simPath.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  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. extern bool gEditingMission;
  36. //--------------------------------------------------------------------------
  37. //-------------------------------------- Console functions and cmp funcs
  38. //
  39. DefineEngineFunction(pathOnMissionLoadDone, void, (),,
  40. "@brief Load all Path information from the mission.\n\n"
  41. "This function is usually called from the loadMissionStage2() server-side function "
  42. "after the mission file has loaded. Internally it places all Paths into the server's "
  43. "PathManager. From this point the Paths are ready for transmission to the clients.\n\n"
  44. "@tsexample\n"
  45. "// Inform the engine to load all Path information from the mission.\n"
  46. "pathOnMissionLoadDone();\n\n"
  47. "@endtsexample\n"
  48. "@see NetConnection::transmitPaths()\n"
  49. "@see NetConnection::clearPaths()\n"
  50. "@see Path\n"
  51. "@ingroup Networking")
  52. {
  53. // Need to load subobjects for all loaded interiors...
  54. SimGroup* pMissionGroup = dynamic_cast<SimGroup*>(Sim::findObject("MissionGroup"));
  55. AssertFatal(pMissionGroup != NULL, "Error, mission done loading and no mission group?");
  56. U32 currStart = 0;
  57. U32 currEnd = 1;
  58. Vector<SimGroup*> groups;
  59. groups.push_back(pMissionGroup);
  60. while (true) {
  61. for (U32 i = currStart; i < currEnd; i++) {
  62. for (SimGroup::iterator itr = groups[i]->begin(); itr != groups[i]->end(); itr++) {
  63. if (dynamic_cast<SimGroup*>(*itr) != NULL)
  64. groups.push_back(static_cast<SimGroup*>(*itr));
  65. }
  66. }
  67. if (groups.size() == currEnd) {
  68. break;
  69. } else {
  70. currStart = currEnd;
  71. currEnd = groups.size();
  72. }
  73. }
  74. for (U32 i = 0; i < groups.size(); i++) {
  75. SimPath::Path* pPath = dynamic_cast<SimPath::Path*>(groups[i]);
  76. if (pPath)
  77. pPath->updatePath();
  78. }
  79. }
  80. S32 FN_CDECL cmpPathObject(const void* p1, const void* p2)
  81. {
  82. SimObject* o1 = *((SimObject**)p1);
  83. SimObject* o2 = *((SimObject**)p2);
  84. Marker* m1 = dynamic_cast<Marker*>(o1);
  85. Marker* m2 = dynamic_cast<Marker*>(o2);
  86. if (m1 == NULL && m2 == NULL)
  87. return 0;
  88. else if (m1 != NULL && m2 == NULL)
  89. return 1;
  90. else if (m1 == NULL && m2 != NULL)
  91. return -1;
  92. else {
  93. // Both markers...
  94. return S32(m1->mSeqNum) - S32(m2->mSeqNum);
  95. }
  96. }
  97. ConsoleDocClass(SimPath::Path,
  98. "@brief A spline along which various objects can move along. The spline object acts like a container for Marker objects, which make\n"
  99. "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"
  100. "one of three primary movement types: \"normal\", \"Position Only\", or \"Kink\". \n"
  101. "@tsexample\n"
  102. "new path()\n"
  103. " {\n"
  104. " isLooping = \"1\";\n"
  105. "\n"
  106. " new Marker()\n"
  107. " {\n"
  108. " seqNum = \"0\";\n"
  109. " type = \"Normal\";\n"
  110. " msToNext = \"1000\";\n"
  111. " smoothingType = \"Spline\";\n"
  112. " position = \"-0.054708 -35.0612 234.802\";\n"
  113. " rotation = \"1 0 0 0\";\n"
  114. " };\n"
  115. "\n"
  116. " };\n"
  117. "@endtsexample\n"
  118. "@see Marker\n"
  119. "@see NetConnection::transmitPaths()\n"
  120. "@see NetConnection::clearPaths()\n"
  121. "@see Path\n"
  122. "@ingroup enviroMisc\n"
  123. );
  124. namespace SimPath
  125. {
  126. //--------------------------------------------------------------------------
  127. //-------------------------------------- Implementation
  128. //
  129. IMPLEMENT_CONOBJECT(Path);
  130. Path::Path()
  131. {
  132. mPathIndex = NoPathIndex;
  133. mIsLooping = true;
  134. }
  135. Path::~Path()
  136. {
  137. //
  138. }
  139. //--------------------------------------------------------------------------
  140. void Path::initPersistFields()
  141. {
  142. addField("isLooping", TypeBool, Offset(mIsLooping, Path), "If this is true, the loop is closed, otherwise it is open.\n");
  143. Parent::initPersistFields();
  144. //
  145. }
  146. //--------------------------------------------------------------------------
  147. bool Path::onAdd()
  148. {
  149. if(!Parent::onAdd())
  150. return false;
  151. return true;
  152. }
  153. void Path::onRemove()
  154. {
  155. //
  156. Parent::onRemove();
  157. }
  158. //--------------------------------------------------------------------------
  159. /// Sort the markers objects into sequence order
  160. void Path::sortMarkers()
  161. {
  162. dQsort(objectList.address(), objectList.size(), sizeof(SimObject*), cmpPathObject);
  163. }
  164. void Path::updatePath()
  165. {
  166. // If we need to, allocate a path index from the manager
  167. if (mPathIndex == NoPathIndex)
  168. mPathIndex = gServerPathManager->allocatePathId();
  169. sortMarkers();
  170. Vector<Point3F> positions;
  171. Vector<QuatF> rotations;
  172. Vector<U32> times;
  173. Vector<U32> smoothingTypes;
  174. for (iterator itr = begin(); itr != end(); itr++)
  175. {
  176. Marker* pMarker = dynamic_cast<Marker*>(*itr);
  177. if (pMarker != NULL)
  178. {
  179. Point3F pos;
  180. pMarker->getTransform().getColumn(3, &pos);
  181. positions.push_back(pos);
  182. QuatF rot;
  183. rot.set(pMarker->getTransform());
  184. rotations.push_back(rot);
  185. times.push_back(pMarker->mMSToNext);
  186. smoothingTypes.push_back(pMarker->mSmoothingType);
  187. }
  188. }
  189. // DMMTODO: Looping paths.
  190. gServerPathManager->updatePath(mPathIndex, positions, rotations, times, smoothingTypes);
  191. }
  192. void Path::addObject(SimObject* obj)
  193. {
  194. Parent::addObject(obj);
  195. if (mPathIndex != NoPathIndex) {
  196. // If we're already finished, and this object is a marker, then we need to
  197. // update our path information...
  198. if (dynamic_cast<Marker*>(obj) != NULL)
  199. updatePath();
  200. }
  201. }
  202. void Path::removeObject(SimObject* obj)
  203. {
  204. bool recalc = dynamic_cast<Marker*>(obj) != NULL;
  205. Parent::removeObject(obj);
  206. if (mPathIndex != NoPathIndex && recalc == true)
  207. updatePath();
  208. }
  209. DefineEngineMethod( Path, getPathId, S32, (),,
  210. "@brief Returns the PathID (not the object ID) of this path.\n\n"
  211. "@return PathID (not the object ID) of this path.\n"
  212. "@tsexample\n"
  213. "// Acquire the PathID of this path object.\n"
  214. "%pathID = %thisPath.getPathId();\n\n"
  215. "@endtsexample\n\n"
  216. )
  217. {
  218. Path *path = static_cast<Path *>(object);
  219. return path->getPathIndex();
  220. }
  221. } // Namespace
  222. //--------------------------------------------------------------------------
  223. //--------------------------------------------------------------------------
  224. GFXStateBlockRef Marker::smStateBlock;
  225. GFXVertexBufferHandle<GFXVertexPC> Marker::smVertexBuffer;
  226. GFXPrimitiveBufferHandle Marker::smPrimitiveBuffer;
  227. static Point3F wedgePoints[4] = {
  228. Point3F(-1, -1, 0),
  229. Point3F( 0, 1, 0),
  230. Point3F( 1, -1, 0),
  231. Point3F( 0,-.75, .5),
  232. };
  233. void Marker::initGFXResources()
  234. {
  235. if(smVertexBuffer != NULL)
  236. return;
  237. GFXStateBlockDesc d;
  238. d.cullDefined = true;
  239. d.cullMode = GFXCullNone;
  240. smStateBlock = GFX->createStateBlock(d);
  241. smVertexBuffer.set(GFX, 4, GFXBufferTypeStatic);
  242. GFXVertexPC* verts = smVertexBuffer.lock();
  243. verts[0].point = wedgePoints[0] * 0.25f;
  244. verts[1].point = wedgePoints[1] * 0.25f;
  245. verts[2].point = wedgePoints[2] * 0.25f;
  246. verts[3].point = wedgePoints[3] * 0.25f;
  247. verts[0].color = verts[1].color = verts[2].color = verts[3].color = GFXVertexColor(ColorI(0, 255, 0, 255));
  248. smVertexBuffer.unlock();
  249. smPrimitiveBuffer.set(GFX, 24, 12, GFXBufferTypeStatic);
  250. U16* prims;
  251. smPrimitiveBuffer.lock(&prims);
  252. prims[0] = 0;
  253. prims[1] = 3;
  254. prims[2] = 3;
  255. prims[3] = 1;
  256. prims[4] = 1;
  257. prims[5] = 0;
  258. prims[6] = 3;
  259. prims[7] = 1;
  260. prims[8] = 1;
  261. prims[9] = 2;
  262. prims[10] = 2;
  263. prims[11] = 3;
  264. prims[12] = 0;
  265. prims[13] = 3;
  266. prims[14] = 3;
  267. prims[15] = 2;
  268. prims[16] = 2;
  269. prims[17] = 0;
  270. prims[18] = 0;
  271. prims[19] = 2;
  272. prims[20] = 2;
  273. prims[21] = 1;
  274. prims[22] = 1;
  275. prims[23] = 0;
  276. smPrimitiveBuffer.unlock();
  277. }
  278. IMPLEMENT_CO_NETOBJECT_V1(Marker);
  279. ConsoleDocClass( Marker,
  280. "@brief A single joint, or knot, along a path. Should be stored inside a Path container object. A path markers can be\n"
  281. "one of three primary movement types: \"normal\", \"Position Only\", or \"Kink\". \n"
  282. "@tsexample\n"
  283. "new path()\n"
  284. " {\n"
  285. " isLooping = \"1\";\n"
  286. "\n"
  287. " new Marker()\n"
  288. " {\n"
  289. " seqNum = \"0\";\n"
  290. " type = \"Normal\";\n"
  291. " msToNext = \"1000\";\n"
  292. " smoothingType = \"Spline\";\n"
  293. " position = \"-0.054708 -35.0612 234.802\";\n"
  294. " rotation = \"1 0 0 0\";\n"
  295. " };\n"
  296. "\n"
  297. " };\n"
  298. "@endtsexample\n"
  299. "@see Path\n"
  300. "@ingroup enviroMisc\n"
  301. );
  302. Marker::Marker()
  303. {
  304. // Not ghostable unless we're editing...
  305. mNetFlags.clear(Ghostable);
  306. mTypeMask |= MarkerObjectType;
  307. mSeqNum = 0;
  308. mMSToNext = 1000;
  309. mSmoothingType = SmoothingTypeSpline;
  310. mKnotType = KnotTypeNormal;
  311. }
  312. Marker::~Marker()
  313. {
  314. //
  315. }
  316. //--------------------------------------------------------------------------
  317. ImplementEnumType( MarkerSmoothingType,
  318. "The type of smoothing this marker will have for pathed objects.\n"
  319. "@ingroup enviroMisc\n\n")
  320. { Marker::SmoothingTypeSpline , "Spline", "Marker will cause the movements of the pathed object to be smooth.\n" },
  321. { Marker::SmoothingTypeLinear , "Linear", "Marker will have no smoothing effect.\n" },
  322. //{ Marker::SmoothingTypeAccelerate , "Accelerate" },
  323. EndImplementEnumType;
  324. ImplementEnumType( MarkerKnotType,
  325. "The type of knot that this marker will be.\n"
  326. "@ingroup enviroMisc\n\n")
  327. { Marker::KnotTypeNormal , "Normal", "Knot will have a smooth camera translation/rotation effect.\n" },
  328. { Marker::KnotTypePositionOnly, "Position Only", "Will do the same for translations, leaving rotation un-touched.\n" },
  329. { Marker::KnotTypeKink, "Kink", "The rotation will take effect immediately for an abrupt rotation change.\n" },
  330. EndImplementEnumType;
  331. void Marker::initPersistFields()
  332. {
  333. addGroup( "Misc" );
  334. addField("seqNum", TypeS32, Offset(mSeqNum, Marker), "Marker position in sequence of markers on this path.\n");
  335. 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");
  336. addField("msToNext", TypeS32, Offset(mMSToNext, Marker), "Milliseconds to next marker in sequence.\n");
  337. addField("smoothingType", TYPEID< SmoothingType >(), Offset(mSmoothingType, Marker), "Path smoothing at this marker/knot. \"Linear\"�means no smoothing, while \"Spline\" means to smooth.\n");
  338. endGroup("Misc");
  339. Parent::initPersistFields();
  340. }
  341. //--------------------------------------------------------------------------
  342. bool Marker::onAdd()
  343. {
  344. if(!Parent::onAdd())
  345. return false;
  346. mObjBox = Box3F(Point3F(-.25, -.25, -.25), Point3F(.25, .25, .25));
  347. resetWorldBox();
  348. if(gEditingMission)
  349. onEditorEnable();
  350. return true;
  351. }
  352. void Marker::onRemove()
  353. {
  354. if(gEditingMission)
  355. onEditorDisable();
  356. Parent::onRemove();
  357. smVertexBuffer = NULL;
  358. smPrimitiveBuffer = NULL;
  359. }
  360. void Marker::onGroupAdd()
  361. {
  362. mSeqNum = getGroup()->size() - 1;
  363. }
  364. /// Enable scoping so we can see this thing on the client.
  365. void Marker::onEditorEnable()
  366. {
  367. mNetFlags.set(Ghostable);
  368. setScopeAlways();
  369. addToScene();
  370. }
  371. /// Disable scoping so we can see this thing on the client
  372. void Marker::onEditorDisable()
  373. {
  374. removeFromScene();
  375. mNetFlags.clear(Ghostable);
  376. clearScopeAlways();
  377. }
  378. /// Tell our parent that this Path has been modified
  379. void Marker::inspectPostApply()
  380. {
  381. SimPath::Path *path = dynamic_cast<SimPath::Path*>(getGroup());
  382. if (path)
  383. path->updatePath();
  384. }
  385. //--------------------------------------------------------------------------
  386. void Marker::prepRenderImage( SceneRenderState* state )
  387. {
  388. // This should be sufficient for most objects that don't manage zones, and
  389. // don't need to return a specialized RenderImage...
  390. ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
  391. ri->renderDelegate.bind( this, &Marker::renderObject );
  392. ri->type = RenderPassManager::RIT_Editor;
  393. state->getRenderPass()->addInst(ri);
  394. }
  395. void Marker::renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat)
  396. {
  397. initGFXResources();
  398. for(U32 i = 0; i < GFX->getNumSamplers(); i++)
  399. GFX->setTexture(i, NULL);
  400. GFXTransformSaver saver;
  401. MatrixF mat = getRenderTransform();
  402. mat.scale(mObjScale);
  403. GFX->multWorld(mat);
  404. GFX->setStateBlock(smStateBlock);
  405. GFX->setVertexBuffer(smVertexBuffer);
  406. GFX->setPrimitiveBuffer(smPrimitiveBuffer);
  407. GFX->setupGenericShaders();
  408. GFX->drawIndexedPrimitive(GFXLineList, 0, 0, 4, 0, 12);
  409. }
  410. //--------------------------------------------------------------------------
  411. U32 Marker::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
  412. {
  413. U32 retMask = Parent::packUpdate(con, mask, stream);
  414. // Note that we don't really care about efficiency here, since this is an
  415. // edit-only ghost...
  416. stream->writeAffineTransform(mObjToWorld);
  417. return retMask;
  418. }
  419. void Marker::unpackUpdate(NetConnection* con, BitStream* stream)
  420. {
  421. Parent::unpackUpdate(con, stream);
  422. // Transform
  423. MatrixF otow;
  424. stream->readAffineTransform(&otow);
  425. setTransform(otow);
  426. }