2
0

simPath.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "scene/simPath.h"
  24. #include "gfx/gfxDevice.h"
  25. #include "gfx/gfxVertexBuffer.h"
  26. #include "gfx/gfxPrimitiveBuffer.h"
  27. #include "gfx/gfxTransformSaver.h"
  28. #include "console/consoleTypes.h"
  29. #include "scene/pathManager.h"
  30. #include "scene/sceneRenderState.h"
  31. #include "math/mathIO.h"
  32. #include "core/stream/bitStream.h"
  33. #include "renderInstance/renderPassManager.h"
  34. #include "console/engineAPI.h"
  35. #include "T3D/pathShape.h"
  36. #include "T3D/Scene.h"
  37. extern bool gEditingMission;
  38. //--------------------------------------------------------------------------
  39. //-------------------------------------- Console functions and cmp funcs
  40. //
  41. DefineEngineFunction(pathOnMissionLoadDone, void, (),,
  42. "@brief Load all Path information from the mission.\n\n"
  43. "This function is usually called from the loadMissionStage2() server-side function "
  44. "after the mission file has loaded. Internally it places all Paths into the server's "
  45. "PathManager. From this point the Paths are ready for transmission to the clients.\n\n"
  46. "@tsexample\n"
  47. "// Inform the engine to load all Path information from the mission.\n"
  48. "pathOnMissionLoadDone();\n\n"
  49. "@endtsexample\n"
  50. "@see NetConnection::transmitPaths()\n"
  51. "@see NetConnection::clearPaths()\n"
  52. "@see Path\n"
  53. "@ingroup Networking")
  54. {
  55. // Need to load subobjects for all loaded interiors...
  56. Scene* scene = Scene::getRootScene();
  57. AssertFatal(scene != NULL, "Error, mission done loading and no scene?");
  58. U32 currStart = 0;
  59. U32 currEnd = 1;
  60. Vector<SimGroup*> groups;
  61. groups.push_back(scene);
  62. while (true) {
  63. for (U32 i = currStart; i < currEnd; i++) {
  64. for (SimGroup::iterator itr = groups[i]->begin(); itr != groups[i]->end(); itr++) {
  65. if (dynamic_cast<SimGroup*>(*itr) != NULL)
  66. groups.push_back(static_cast<SimGroup*>(*itr));
  67. }
  68. }
  69. if (groups.size() == currEnd) {
  70. break;
  71. } else {
  72. currStart = currEnd;
  73. currEnd = groups.size();
  74. }
  75. }
  76. for (U32 i = 0; i < groups.size(); i++) {
  77. SimPath::Path* pPath = dynamic_cast<SimPath::Path*>(groups[i]);
  78. if (pPath)
  79. pPath->updatePath();
  80. }
  81. }
  82. S32 FN_CDECL cmpPathObject(const void* p1, const void* p2)
  83. {
  84. SimObject* o1 = *((SimObject**)p1);
  85. SimObject* o2 = *((SimObject**)p2);
  86. Marker* m1 = dynamic_cast<Marker*>(o1);
  87. Marker* m2 = dynamic_cast<Marker*>(o2);
  88. if (m1 == NULL && m2 == NULL)
  89. return 0;
  90. else if (m1 != NULL && m2 == NULL)
  91. return 1;
  92. else if (m1 == NULL && m2 != NULL)
  93. return -1;
  94. else {
  95. // Both markers...
  96. return S32(m1->mSeqNum) - S32(m2->mSeqNum);
  97. }
  98. }
  99. ConsoleDocClass(SimPath::Path,
  100. "@brief A spline along which various objects can move along. The spline object acts like a container for Marker objects, which make\n"
  101. "up the joints, or knots, along the path. Paths can be assigned a speed, can be looping or non-looping. Each of a path's markers can be\n"
  102. "one of three primary movement types: \"normal\", \"Position Only\", or \"Kink\". \n"
  103. "@tsexample\n"
  104. "new path()\n"
  105. " {\n"
  106. " isLooping = \"1\";\n"
  107. "\n"
  108. " new Marker()\n"
  109. " {\n"
  110. " seqNum = \"0\";\n"
  111. " type = \"Normal\";\n"
  112. " msToNext = \"1000\";\n"
  113. " smoothingType = \"Spline\";\n"
  114. " position = \"-0.054708 -35.0612 234.802\";\n"
  115. " rotation = \"1 0 0 0\";\n"
  116. " };\n"
  117. "\n"
  118. " };\n"
  119. "@endtsexample\n"
  120. "@see Marker\n"
  121. "@see NetConnection::transmitPaths()\n"
  122. "@see NetConnection::clearPaths()\n"
  123. "@see Path\n"
  124. "@ingroup enviroMisc\n"
  125. );
  126. namespace SimPath
  127. {
  128. //--------------------------------------------------------------------------
  129. //-------------------------------------- Implementation
  130. //
  131. IMPLEMENT_CONOBJECT(Path);
  132. Path::Path()
  133. {
  134. mPathIndex = NoPathIndex;
  135. mIsLooping = true;
  136. mPathSpeed = 1.0f;
  137. mDataBlock = NULL;
  138. mSpawnCount = 1;
  139. mMinDelay = 0;
  140. mMaxDelay = 0;
  141. }
  142. Path::~Path()
  143. {
  144. //
  145. }
  146. //--------------------------------------------------------------------------
  147. void Path::initPersistFields()
  148. {
  149. addField("isLooping", TypeBool, Offset(mIsLooping, Path), "If this is true, the loop is closed, otherwise it is open.\n");
  150. addField("Speed", TypeF32, Offset(mPathSpeed, Path), "Speed.\n");
  151. addProtectedField("mPathShape", TYPEID< PathShapeData >(), Offset(mDataBlock, Path),
  152. &setDataBlockProperty, &defaultProtectedGetFn,
  153. "@brief Spawned PathShape.\n\n");
  154. addField("spawnCount", TypeS32, Offset(mSpawnCount, Path), "Spawn Count.\n");
  155. addField("minDelay", TypeS32, Offset(mMinDelay, Path), "Spawn Delay (min).\n");
  156. addField("maxDelay", TypeS32, Offset(mMaxDelay, Path), "Spawn Delay (max).\n");
  157. Parent::initPersistFields();
  158. //
  159. }
  160. //--------------------------------------------------------------------------
  161. bool Path::onAdd()
  162. {
  163. if(!Parent::onAdd())
  164. return false;
  165. onAdd_callback(getId());
  166. return true;
  167. }
  168. IMPLEMENT_CALLBACK(Path, onAdd, void, (SimObjectId ID), (ID),
  169. "Called when this ScriptGroup is added to the system.\n"
  170. "@param ID Unique object ID assigned when created (%this in script).\n"
  171. );
  172. void Path::onRemove()
  173. {
  174. //
  175. Parent::onRemove();
  176. }
  177. //--------------------------------------------------------------------------
  178. /// Sort the markers objects into sequence order
  179. void Path::sortMarkers()
  180. {
  181. dQsort(mObjectList.address(), mObjectList.size(), sizeof(SimObject*), cmpPathObject);
  182. }
  183. void Path::updatePath()
  184. {
  185. // If we need to, allocate a path index from the manager
  186. if (mPathIndex == NoPathIndex)
  187. mPathIndex = gServerPathManager->allocatePathId();
  188. sortMarkers();
  189. Vector<Point3F> positions;
  190. Vector<QuatF> rotations;
  191. Vector<U32> times;
  192. Vector<U32> smoothingTypes;
  193. for (iterator itr = begin(); itr != end(); itr++)
  194. {
  195. Marker* pMarker = dynamic_cast<Marker*>(*itr);
  196. if (pMarker != NULL)
  197. {
  198. Point3F pos;
  199. pMarker->getTransform().getColumn(3, &pos);
  200. positions.push_back(pos);
  201. QuatF rot;
  202. rot.set(pMarker->getTransform());
  203. rotations.push_back(rot);
  204. times.push_back(pMarker->mMSToNext);
  205. smoothingTypes.push_back(pMarker->mSmoothingType);
  206. }
  207. }
  208. gServerPathManager->updatePath(mPathIndex, positions, rotations, times, smoothingTypes, mIsLooping);
  209. }
  210. void Path::addObject(SimObject* obj)
  211. {
  212. Parent::addObject(obj);
  213. if (mPathIndex != NoPathIndex) {
  214. // If we're already finished, and this object is a marker, then we need to
  215. // update our path information...
  216. if (dynamic_cast<Marker*>(obj) != NULL)
  217. updatePath();
  218. }
  219. }
  220. void Path::removeObject(SimObject* obj)
  221. {
  222. bool recalc = dynamic_cast<Marker*>(obj) != NULL;
  223. Parent::removeObject(obj);
  224. if (mPathIndex != NoPathIndex && recalc == true)
  225. updatePath();
  226. }
  227. DefineEngineMethod( Path, getPathId, S32, (),,
  228. "@brief Returns the PathID (not the object ID) of this path.\n\n"
  229. "@return PathID (not the object ID) of this path.\n"
  230. "@tsexample\n"
  231. "// Acquire the PathID of this path object.\n"
  232. "%pathID = %thisPath.getPathId();\n\n"
  233. "@endtsexample\n\n"
  234. )
  235. {
  236. Path *path = static_cast<Path *>(object);
  237. return path->getPathIndex();
  238. }
  239. } // Namespace
  240. //--------------------------------------------------------------------------
  241. //--------------------------------------------------------------------------
  242. GFXStateBlockRef Marker::smStateBlock;
  243. GFXVertexBufferHandle<GFXVertexPCT> Marker::smVertexBuffer;
  244. GFXPrimitiveBufferHandle Marker::smPrimitiveBuffer;
  245. static Point3F wedgePoints[4] = {
  246. Point3F(-1, -1, 0),
  247. Point3F( 0, 1, 0),
  248. Point3F( 1, -1, 0),
  249. Point3F( 0,-.75, .5),
  250. };
  251. void Marker::initGFXResources()
  252. {
  253. if(smVertexBuffer != NULL)
  254. return;
  255. GFXStateBlockDesc d;
  256. d.cullDefined = true;
  257. d.cullMode = GFXCullNone;
  258. smStateBlock = GFX->createStateBlock(d);
  259. smVertexBuffer.set(GFX, 4, GFXBufferTypeStatic);
  260. GFXVertexPCT* verts = smVertexBuffer.lock();
  261. verts[0].point = wedgePoints[0] * 1.25f;
  262. verts[1].point = wedgePoints[1] * 1.25f;
  263. verts[2].point = wedgePoints[2] * 1.25f;
  264. verts[3].point = wedgePoints[3] * 1.25f;
  265. verts[1].color = GFXVertexColor(ColorI(255, 0, 0, 255));
  266. verts[0].color = verts[2].color = verts[3].color = GFXVertexColor(ColorI(0, 0, 255, 255));
  267. smVertexBuffer.unlock();
  268. smPrimitiveBuffer.set(GFX, 24, 12, GFXBufferTypeStatic);
  269. U16* prims;
  270. smPrimitiveBuffer.lock(&prims);
  271. prims[0] = 0;
  272. prims[1] = 3;
  273. prims[2] = 3;
  274. prims[3] = 1;
  275. prims[4] = 1;
  276. prims[5] = 0;
  277. prims[6] = 3;
  278. prims[7] = 1;
  279. prims[8] = 1;
  280. prims[9] = 2;
  281. prims[10] = 2;
  282. prims[11] = 3;
  283. prims[12] = 0;
  284. prims[13] = 3;
  285. prims[14] = 3;
  286. prims[15] = 2;
  287. prims[16] = 2;
  288. prims[17] = 0;
  289. prims[18] = 0;
  290. prims[19] = 2;
  291. prims[20] = 2;
  292. prims[21] = 1;
  293. prims[22] = 1;
  294. prims[23] = 0;
  295. smPrimitiveBuffer.unlock();
  296. }
  297. IMPLEMENT_CO_NETOBJECT_V1(Marker);
  298. ConsoleDocClass( Marker,
  299. "@brief A single joint, or knot, along a path. Should be stored inside a Path container object. A path markers can be\n"
  300. "one of three primary movement types: \"normal\", \"Position Only\", or \"Kink\". \n"
  301. "@tsexample\n"
  302. "new path()\n"
  303. " {\n"
  304. " isLooping = \"1\";\n"
  305. "\n"
  306. " new Marker()\n"
  307. " {\n"
  308. " seqNum = \"0\";\n"
  309. " type = \"Normal\";\n"
  310. " msToNext = \"1000\";\n"
  311. " smoothingType = \"Spline\";\n"
  312. " position = \"-0.054708 -35.0612 234.802\";\n"
  313. " rotation = \"1 0 0 0\";\n"
  314. " };\n"
  315. "\n"
  316. " };\n"
  317. "@endtsexample\n"
  318. "@see Path\n"
  319. "@ingroup enviroMisc\n"
  320. );
  321. Marker::Marker()
  322. {
  323. // Not ghostable unless we're editing...
  324. mNetFlags.clear(Ghostable);
  325. mTypeMask |= MarkerObjectType;
  326. mHitCommand = String::EmptyString;
  327. mSeqNum = 0;
  328. mMSToNext = 1000;
  329. mSmoothingType = SmoothingTypeSpline;
  330. mKnotType = KnotTypeNormal;
  331. }
  332. Marker::~Marker()
  333. {
  334. //
  335. }
  336. //--------------------------------------------------------------------------
  337. ImplementEnumType( MarkerSmoothingType,
  338. "The type of smoothing this marker will have for pathed objects.\n"
  339. "@ingroup enviroMisc\n\n")
  340. { Marker::SmoothingTypeSpline , "Spline", "Marker will cause the movements of the pathed object to be smooth.\n" },
  341. { Marker::SmoothingTypeLinear , "Linear", "Marker will have no smoothing effect.\n" },
  342. //{ Marker::SmoothingTypeAccelerate , "Accelerate" },
  343. EndImplementEnumType;
  344. ImplementEnumType( MarkerKnotType,
  345. "The type of knot that this marker will be.\n"
  346. "@ingroup enviroMisc\n\n")
  347. { Marker::KnotTypeNormal , "Normal", "Knot will have a smooth camera translation/rotation effect.\n" },
  348. { Marker::KnotTypePositionOnly, "Position Only", "Will do the same for translations, leaving rotation un-touched.\n" },
  349. { Marker::KnotTypeKink, "Kink", "The rotation will take effect immediately for an abrupt rotation change.\n" },
  350. EndImplementEnumType;
  351. void Marker::initPersistFields()
  352. {
  353. addGroup( "Misc" );
  354. addField("seqNum", TypeS32, Offset(mSeqNum, Marker), "Marker position in sequence of markers on this path.\n");
  355. addField("hitCommand", TypeCommand, Offset(mHitCommand, Marker), "The command to execute when a path follower reaches this marker.");
  356. addField("type", TYPEID< KnotType >(), Offset(mKnotType, Marker), "Type of this marker/knot. A \"normal\" knot will have a smooth camera translation/rotation effect.\n\"Position Only\" will do the same for translations, leaving rotation un-touched.\nLastly, a \"Kink\" means the rotation will take effect immediately for an abrupt rotation change.\n");
  357. addField("msToNext", TypeS32, Offset(mMSToNext, Marker), "Milliseconds to next marker in sequence.\n");
  358. addField("smoothingType", TYPEID< SmoothingType >(), Offset(mSmoothingType, Marker), "Path smoothing at this marker/knot. \"Linear\" means no smoothing, while \"Spline\" means to smooth.\n");
  359. endGroup("Misc");
  360. Parent::initPersistFields();
  361. }
  362. //--------------------------------------------------------------------------
  363. bool Marker::onAdd()
  364. {
  365. if(!Parent::onAdd())
  366. return false;
  367. mObjBox = Box3F(Point3F(-1.25, -1.25, -1.25), Point3F(1.25, 1.25, 1.25));
  368. resetWorldBox();
  369. if(gEditingMission)
  370. onEditorEnable();
  371. return true;
  372. }
  373. void Marker::onRemove()
  374. {
  375. if(gEditingMission)
  376. onEditorDisable();
  377. Parent::onRemove();
  378. smVertexBuffer = NULL;
  379. smPrimitiveBuffer = NULL;
  380. }
  381. void Marker::onGroupAdd()
  382. {
  383. mSeqNum = getGroup()->size() - 1;
  384. }
  385. /// Enable scoping so we can see this thing on the client.
  386. void Marker::onEditorEnable()
  387. {
  388. mNetFlags.set(Ghostable);
  389. setScopeAlways();
  390. addToScene();
  391. }
  392. /// Disable scoping so we can see this thing on the client
  393. void Marker::onEditorDisable()
  394. {
  395. removeFromScene();
  396. mNetFlags.clear(Ghostable);
  397. clearScopeAlways();
  398. }
  399. /// Tell our parent that this Path has been modified
  400. void Marker::inspectPostApply()
  401. {
  402. SimPath::Path *path = dynamic_cast<SimPath::Path*>(getGroup());
  403. if (path)
  404. path->updatePath();
  405. }
  406. //--------------------------------------------------------------------------
  407. void Marker::prepRenderImage( SceneRenderState* state )
  408. {
  409. // This should be sufficient for most objects that don't manage zones, and
  410. // don't need to return a specialized RenderImage...
  411. ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
  412. ri->renderDelegate.bind( this, &Marker::renderObject );
  413. ri->type = RenderPassManager::RIT_Editor;
  414. state->getRenderPass()->addInst(ri);
  415. }
  416. void Marker::renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat)
  417. {
  418. initGFXResources();
  419. for(U32 i = 0; i < GFX->getNumSamplers(); i++)
  420. GFX->setTexture(i, NULL);
  421. GFXTransformSaver saver;
  422. MatrixF mat = getRenderTransform();
  423. mat.scale(mObjScale);
  424. GFX->multWorld(mat);
  425. GFX->setStateBlock(smStateBlock);
  426. GFX->setVertexBuffer(smVertexBuffer);
  427. GFX->setPrimitiveBuffer(smPrimitiveBuffer);
  428. GFX->setupGenericShaders();
  429. GFX->drawIndexedPrimitive(GFXLineList, 0, 0, 4, 0, 12);
  430. }
  431. //--------------------------------------------------------------------------
  432. U32 Marker::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
  433. {
  434. U32 retMask = Parent::packUpdate(con, mask, stream);
  435. // Note that we don't really care about efficiency here, since this is an
  436. // edit-only ghost...
  437. stream->writeAffineTransform(mObjToWorld);
  438. return retMask;
  439. }
  440. void Marker::unpackUpdate(NetConnection* con, BitStream* stream)
  441. {
  442. Parent::unpackUpdate(con, stream);
  443. // Transform
  444. MatrixF otow;
  445. stream->readAffineTransform(&otow);
  446. setTransform(otow);
  447. }