simPath.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  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. docsURL;
  150. addField("isLooping", TypeBool, Offset(mIsLooping, Path), "If this is true, the loop is closed, otherwise it is open.\n");
  151. addField("Speed", TypeF32, Offset(mPathSpeed, Path), "Speed.\n");
  152. addProtectedField("mPathShape", TYPEID< PathShapeData >(), Offset(mDataBlock, Path),
  153. &setDataBlockProperty, &defaultProtectedGetFn,
  154. "@brief Spawned PathShape.\n\n");
  155. addField("spawnCount", TypeS32, Offset(mSpawnCount, Path), "Spawn Count.\n");
  156. addField("minDelay", TypeS32, Offset(mMinDelay, Path), "Spawn Delay (min).\n");
  157. addField("maxDelay", TypeS32, Offset(mMaxDelay, Path), "Spawn Delay (max).\n");
  158. Parent::initPersistFields();
  159. //
  160. }
  161. //--------------------------------------------------------------------------
  162. bool Path::onAdd()
  163. {
  164. if(!Parent::onAdd())
  165. return false;
  166. onAdd_callback(getId());
  167. return true;
  168. }
  169. IMPLEMENT_CALLBACK(Path, onAdd, void, (SimObjectId ID), (ID),
  170. "Called when this ScriptGroup is added to the system.\n"
  171. "@param ID Unique object ID assigned when created (%this in script).\n"
  172. );
  173. void Path::onRemove()
  174. {
  175. //
  176. Parent::onRemove();
  177. }
  178. //--------------------------------------------------------------------------
  179. /// Sort the markers objects into sequence order
  180. void Path::sortMarkers()
  181. {
  182. dQsort(mObjectList.address(), mObjectList.size(), sizeof(SimObject*), cmpPathObject);
  183. }
  184. void Path::updatePath()
  185. {
  186. // If we need to, allocate a path index from the manager
  187. if (mPathIndex == NoPathIndex)
  188. mPathIndex = gServerPathManager->allocatePathId();
  189. sortMarkers();
  190. Vector<Point3F> positions;
  191. Vector<QuatF> rotations;
  192. Vector<U32> times;
  193. Vector<U32> smoothingTypes;
  194. for (iterator itr = begin(); itr != end(); itr++)
  195. {
  196. Marker* pMarker = dynamic_cast<Marker*>(*itr);
  197. if (pMarker != NULL)
  198. {
  199. Point3F pos;
  200. pMarker->getTransform().getColumn(3, &pos);
  201. positions.push_back(pos);
  202. QuatF rot;
  203. rot.set(pMarker->getTransform());
  204. rotations.push_back(rot);
  205. times.push_back(pMarker->mMSToNext);
  206. smoothingTypes.push_back(pMarker->mSmoothingType);
  207. }
  208. }
  209. gServerPathManager->updatePath(mPathIndex, positions, rotations, times, smoothingTypes, mIsLooping);
  210. }
  211. void Path::addObject(SimObject* obj)
  212. {
  213. Parent::addObject(obj);
  214. if (mPathIndex != NoPathIndex) {
  215. // If we're already finished, and this object is a marker, then we need to
  216. // update our path information...
  217. if (dynamic_cast<Marker*>(obj) != NULL)
  218. updatePath();
  219. }
  220. }
  221. void Path::removeObject(SimObject* obj)
  222. {
  223. bool recalc = dynamic_cast<Marker*>(obj) != NULL;
  224. Parent::removeObject(obj);
  225. if (mPathIndex != NoPathIndex && recalc == true)
  226. updatePath();
  227. }
  228. DefineEngineMethod( Path, getPathId, S32, (),,
  229. "@brief Returns the PathID (not the object ID) of this path.\n\n"
  230. "@return PathID (not the object ID) of this path.\n"
  231. "@tsexample\n"
  232. "// Acquire the PathID of this path object.\n"
  233. "%pathID = %thisPath.getPathId();\n\n"
  234. "@endtsexample\n\n"
  235. )
  236. {
  237. Path *path = static_cast<Path *>(object);
  238. return path->getPathIndex();
  239. }
  240. } // Namespace
  241. //--------------------------------------------------------------------------
  242. //--------------------------------------------------------------------------
  243. GFXStateBlockRef Marker::smStateBlock;
  244. GFXVertexBufferHandle<GFXVertexPCT> Marker::smVertexBuffer;
  245. GFXPrimitiveBufferHandle Marker::smPrimitiveBuffer;
  246. static Point3F wedgePoints[4] = {
  247. Point3F(-1, -1, 0),
  248. Point3F( 0, 1, 0),
  249. Point3F( 1, -1, 0),
  250. Point3F( 0,-.75, .5),
  251. };
  252. void Marker::initGFXResources()
  253. {
  254. if(smVertexBuffer != NULL)
  255. return;
  256. GFXStateBlockDesc d;
  257. d.cullDefined = true;
  258. d.cullMode = GFXCullNone;
  259. smStateBlock = GFX->createStateBlock(d);
  260. smVertexBuffer.set(GFX, 4, GFXBufferTypeStatic);
  261. GFXVertexPCT* verts = smVertexBuffer.lock();
  262. verts[0].point = wedgePoints[0] * 1.25f;
  263. verts[1].point = wedgePoints[1] * 1.25f;
  264. verts[2].point = wedgePoints[2] * 1.25f;
  265. verts[3].point = wedgePoints[3] * 1.25f;
  266. verts[1].color = GFXVertexColor(ColorI(255, 0, 0, 255));
  267. verts[0].color = verts[2].color = verts[3].color = GFXVertexColor(ColorI(0, 0, 255, 255));
  268. smVertexBuffer.unlock();
  269. smPrimitiveBuffer.set(GFX, 24, 12, GFXBufferTypeStatic);
  270. U16* prims;
  271. smPrimitiveBuffer.lock(&prims);
  272. prims[0] = 0;
  273. prims[1] = 3;
  274. prims[2] = 3;
  275. prims[3] = 1;
  276. prims[4] = 1;
  277. prims[5] = 0;
  278. prims[6] = 3;
  279. prims[7] = 1;
  280. prims[8] = 1;
  281. prims[9] = 2;
  282. prims[10] = 2;
  283. prims[11] = 3;
  284. prims[12] = 0;
  285. prims[13] = 3;
  286. prims[14] = 3;
  287. prims[15] = 2;
  288. prims[16] = 2;
  289. prims[17] = 0;
  290. prims[18] = 0;
  291. prims[19] = 2;
  292. prims[20] = 2;
  293. prims[21] = 1;
  294. prims[22] = 1;
  295. prims[23] = 0;
  296. smPrimitiveBuffer.unlock();
  297. }
  298. IMPLEMENT_CO_NETOBJECT_V1(Marker);
  299. ConsoleDocClass( Marker,
  300. "@brief A single joint, or knot, along a path. Should be stored inside a Path container object. A path markers can be\n"
  301. "one of three primary movement types: \"normal\", \"Position Only\", or \"Kink\". \n"
  302. "@tsexample\n"
  303. "new path()\n"
  304. " {\n"
  305. " isLooping = \"1\";\n"
  306. "\n"
  307. " new Marker()\n"
  308. " {\n"
  309. " seqNum = \"0\";\n"
  310. " type = \"Normal\";\n"
  311. " msToNext = \"1000\";\n"
  312. " smoothingType = \"Spline\";\n"
  313. " position = \"-0.054708 -35.0612 234.802\";\n"
  314. " rotation = \"1 0 0 0\";\n"
  315. " };\n"
  316. "\n"
  317. " };\n"
  318. "@endtsexample\n"
  319. "@see Path\n"
  320. "@ingroup enviroMisc\n"
  321. );
  322. Marker::Marker()
  323. {
  324. // Not ghostable unless we're editing...
  325. mNetFlags.clear(Ghostable);
  326. mTypeMask |= MarkerObjectType;
  327. mHitCommand = String::EmptyString;
  328. mSeqNum = 0;
  329. mMSToNext = 1000;
  330. mSmoothingType = SmoothingTypeSpline;
  331. mKnotType = KnotTypeNormal;
  332. }
  333. Marker::~Marker()
  334. {
  335. //
  336. }
  337. //--------------------------------------------------------------------------
  338. ImplementEnumType( MarkerSmoothingType,
  339. "The type of smoothing this marker will have for pathed objects.\n"
  340. "@ingroup enviroMisc\n\n")
  341. { Marker::SmoothingTypeSpline , "Spline", "Marker will cause the movements of the pathed object to be smooth.\n" },
  342. { Marker::SmoothingTypeLinear , "Linear", "Marker will have no smoothing effect.\n" },
  343. //{ Marker::SmoothingTypeAccelerate , "Accelerate" },
  344. EndImplementEnumType;
  345. ImplementEnumType( MarkerKnotType,
  346. "The type of knot that this marker will be.\n"
  347. "@ingroup enviroMisc\n\n")
  348. { Marker::KnotTypeNormal , "Normal", "Knot will have a smooth camera translation/rotation effect.\n" },
  349. { Marker::KnotTypePositionOnly, "Position Only", "Will do the same for translations, leaving rotation un-touched.\n" },
  350. { Marker::KnotTypeKink, "Kink", "The rotation will take effect immediately for an abrupt rotation change.\n" },
  351. EndImplementEnumType;
  352. void Marker::initPersistFields()
  353. {
  354. docsURL;
  355. addGroup( "Misc" );
  356. addField("seqNum", TypeS32, Offset(mSeqNum, Marker), "Marker position in sequence of markers on this path.\n");
  357. addField("hitCommand", TypeCommand, Offset(mHitCommand, Marker), "The command to execute when a path follower reaches this marker.");
  358. 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");
  359. addField("msToNext", TypeS32, Offset(mMSToNext, Marker), "Milliseconds to next marker in sequence.\n");
  360. addField("smoothingType", TYPEID< SmoothingType >(), Offset(mSmoothingType, Marker), "Path smoothing at this marker/knot. \"Linear\" means no smoothing, while \"Spline\" means to smooth.\n");
  361. endGroup("Misc");
  362. Parent::initPersistFields();
  363. }
  364. //--------------------------------------------------------------------------
  365. bool Marker::onAdd()
  366. {
  367. if(!Parent::onAdd())
  368. return false;
  369. mObjBox = Box3F(Point3F(-1.25, -1.25, -1.25), Point3F(1.25, 1.25, 1.25));
  370. resetWorldBox();
  371. if(gEditingMission)
  372. onEditorEnable();
  373. return true;
  374. }
  375. void Marker::onRemove()
  376. {
  377. if(gEditingMission)
  378. onEditorDisable();
  379. Parent::onRemove();
  380. smVertexBuffer = NULL;
  381. smPrimitiveBuffer = NULL;
  382. }
  383. void Marker::onGroupAdd()
  384. {
  385. mSeqNum = getGroup()->size() - 1;
  386. }
  387. /// Enable scoping so we can see this thing on the client.
  388. void Marker::onEditorEnable()
  389. {
  390. mNetFlags.set(Ghostable);
  391. setScopeAlways();
  392. addToScene();
  393. }
  394. /// Disable scoping so we can see this thing on the client
  395. void Marker::onEditorDisable()
  396. {
  397. removeFromScene();
  398. mNetFlags.clear(Ghostable);
  399. clearScopeAlways();
  400. }
  401. /// Tell our parent that this Path has been modified
  402. void Marker::inspectPostApply()
  403. {
  404. SimPath::Path *path = dynamic_cast<SimPath::Path*>(getGroup());
  405. if (path)
  406. path->updatePath();
  407. }
  408. //--------------------------------------------------------------------------
  409. void Marker::prepRenderImage( SceneRenderState* state )
  410. {
  411. // This should be sufficient for most objects that don't manage zones, and
  412. // don't need to return a specialized RenderImage...
  413. ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
  414. ri->renderDelegate.bind( this, &Marker::renderObject );
  415. ri->type = RenderPassManager::RIT_Editor;
  416. state->getRenderPass()->addInst(ri);
  417. }
  418. void Marker::renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat)
  419. {
  420. initGFXResources();
  421. for(U32 i = 0; i < GFX->getNumSamplers(); i++)
  422. GFX->setTexture(i, NULL);
  423. GFXTransformSaver saver;
  424. MatrixF mat = getRenderTransform();
  425. mat.scale(mObjScale);
  426. GFX->multWorld(mat);
  427. GFX->setStateBlock(smStateBlock);
  428. GFX->setVertexBuffer(smVertexBuffer);
  429. GFX->setPrimitiveBuffer(smPrimitiveBuffer);
  430. GFX->setupGenericShaders();
  431. GFX->drawIndexedPrimitive(GFXLineList, 0, 0, 4, 0, 12);
  432. }
  433. //--------------------------------------------------------------------------
  434. U32 Marker::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
  435. {
  436. U32 retMask = Parent::packUpdate(con, mask, stream);
  437. // Note that we don't really care about efficiency here, since this is an
  438. // edit-only ghost...
  439. stream->writeAffineTransform(mObjToWorld);
  440. return retMask;
  441. }
  442. void Marker::unpackUpdate(NetConnection* con, BitStream* stream)
  443. {
  444. Parent::unpackUpdate(con, stream);
  445. // Transform
  446. MatrixF otow;
  447. stream->readAffineTransform(&otow);
  448. setTransform(otow);
  449. }