physicalZone.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  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 "T3D/physicalZone.h"
  23. #include "core/stream/bitStream.h"
  24. #include "collision/boxConvex.h"
  25. #include "collision/clippedPolyList.h"
  26. #include "console/consoleTypes.h"
  27. #include "math/mathIO.h"
  28. #include "scene/sceneRenderState.h"
  29. #include "T3D/trigger.h"
  30. #include "gfx/gfxTransformSaver.h"
  31. #include "renderInstance/renderPassManager.h"
  32. #include "gfx/gfxDrawUtil.h"
  33. #include "console/engineAPI.h"
  34. IMPLEMENT_CO_NETOBJECT_V1(PhysicalZone);
  35. ConsoleDocClass( PhysicalZone,
  36. "@brief Physical Zones are areas that modify the player's gravity and/or velocity and/or applied force.\n\n"
  37. "The datablock properties determine how the physics, velocity and applied forces affect a player who enters this zone.\n"
  38. "@tsexample\n"
  39. "new PhysicalZone(Team1JumpPad) {\n"
  40. "velocityMod = \"1\";"
  41. "gravityMod = \"0\";\n"
  42. "appliedForce = \"0 0 20000\";\n"
  43. "polyhedron = \"0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 -1.0000000 0.0000000 0.0000000 0.0000000 1.0000000\";\n"
  44. "position = \"273.559 -166.371 249.856\";\n"
  45. "rotation = \"0 0 1 13.0216\";\n"
  46. "scale = \"8 4.95 28.31\";\n"
  47. "isRenderEnabled = \"true\";\n"
  48. "canSaveDynamicFields = \"1\";\n"
  49. "enabled = \"1\";\n"
  50. "};\n"
  51. "@endtsexample\n\n"
  52. "@ingroup enviroMisc\n"
  53. );
  54. bool PhysicalZone::smRenderPZones = false;
  55. DefineEngineMethod(PhysicalZone, activate, void, (),, "Activate the physical zone's effects.\n"
  56. "@tsexample\n"
  57. "// Activate effects for a specific physical zone.\n"
  58. "%thisPhysicalZone.activate();\n"
  59. "@endtsexample\n"
  60. "@ingroup Datablocks\n"
  61. )
  62. {
  63. if (object->isClientObject())
  64. return;
  65. object->activate();
  66. }
  67. DefineEngineMethod(PhysicalZone, deactivate, void, (),, "Deactivate the physical zone's effects.\n"
  68. "@tsexample\n"
  69. "// Deactivate effects for a specific physical zone.\n"
  70. "%thisPhysicalZone.deactivate();\n"
  71. "@endtsexample\n"
  72. "@ingroup Datablocks\n"
  73. )
  74. {
  75. if (object->isClientObject())
  76. return;
  77. object->deactivate();
  78. }
  79. //--------------------------------------------------------------------------
  80. //--------------------------------------
  81. //
  82. PhysicalZone::PhysicalZone()
  83. {
  84. mNetFlags.set(Ghostable | ScopeAlways);
  85. mTypeMask |= PhysicalZoneObjectType;
  86. mVelocityMod = 1.0f;
  87. mGravityMod = 1.0f;
  88. mAppliedForce.set(0, 0, 0);
  89. mConvexList = new Convex;
  90. mActive = true;
  91. }
  92. PhysicalZone::~PhysicalZone()
  93. {
  94. delete mConvexList;
  95. mConvexList = NULL;
  96. }
  97. //--------------------------------------------------------------------------
  98. void PhysicalZone::consoleInit()
  99. {
  100. Con::addVariable( "$PhysicalZone::renderZones", TypeBool, &smRenderPZones, "If true, a box will render around the location of all PhysicalZones.\n"
  101. "@ingroup EnviroMisc\n");
  102. }
  103. void PhysicalZone::initPersistFields()
  104. {
  105. addGroup("Misc");
  106. addField("velocityMod", TypeF32, Offset(mVelocityMod, PhysicalZone), "Multiply velocity of objects entering zone by this value every tick.");
  107. addField("gravityMod", TypeF32, Offset(mGravityMod, PhysicalZone), "Gravity in PhysicalZone. Multiplies against standard gravity.");
  108. addField("appliedForce", TypePoint3F, Offset(mAppliedForce, PhysicalZone), "Three-element floating point value representing forces in three axes to apply to objects entering PhysicalZone.");
  109. addField("polyhedron", TypeTriggerPolyhedron, Offset(mPolyhedron, PhysicalZone),
  110. "The polyhedron type is really a quadrilateral and consists of a corner"
  111. "point followed by three vectors representing the edges extending from the corner." );
  112. endGroup("Misc");
  113. Parent::initPersistFields();
  114. }
  115. //--------------------------------------------------------------------------
  116. bool PhysicalZone::onAdd()
  117. {
  118. if(!Parent::onAdd())
  119. return false;
  120. if (mVelocityMod < -40.0f || mVelocityMod > 40.0f) {
  121. Con::errorf("PhysicalZone: velocity mod out of range. [-40, 40]");
  122. mVelocityMod = mVelocityMod < -40.0f ? -40.0f : 40.0f;
  123. }
  124. if (mGravityMod < -40.0f || mGravityMod > 40.0f) {
  125. Con::errorf("PhysicalZone: GravityMod out of range. [-40, 40]");
  126. mGravityMod = mGravityMod < -40.0f ? -40.0f : 40.0f;
  127. }
  128. static const char* coordString[] = { "x", "y", "z" };
  129. F32* p = mAppliedForce;
  130. for (U32 i = 0; i < 3; i++) {
  131. if (p[i] < -40000.0f || p[i] > 40000.0f) {
  132. Con::errorf("PhysicalZone: applied force: %s out of range. [-40000, 40000]", coordString[i]);
  133. p[i] = p[i] < -40000.0f ? -40000.0f : 40000.0f;
  134. }
  135. }
  136. Polyhedron temp = mPolyhedron;
  137. setPolyhedron(temp);
  138. addToScene();
  139. return true;
  140. }
  141. void PhysicalZone::onRemove()
  142. {
  143. mConvexList->nukeList();
  144. removeFromScene();
  145. Parent::onRemove();
  146. }
  147. void PhysicalZone::inspectPostApply()
  148. {
  149. setPolyhedron(mPolyhedron);
  150. Parent::inspectPostApply();
  151. }
  152. //------------------------------------------------------------------------------
  153. void PhysicalZone::setTransform(const MatrixF & mat)
  154. {
  155. Parent::setTransform(mat);
  156. MatrixF base(true);
  157. base.scale(Point3F(1.0/mObjScale.x,
  158. 1.0/mObjScale.y,
  159. 1.0/mObjScale.z));
  160. base.mul(mWorldToObj);
  161. mClippedList.setBaseTransform(base);
  162. if (isServerObject())
  163. setMaskBits(InitialUpdateMask);
  164. }
  165. void PhysicalZone::prepRenderImage( SceneRenderState *state )
  166. {
  167. // only render if selected or render flag is set
  168. if ( !smRenderPZones && !isSelected() )
  169. return;
  170. ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
  171. ri->renderDelegate.bind( this, &PhysicalZone::renderObject );
  172. ri->type = RenderPassManager::RIT_Editor;
  173. ri->defaultKey = 0;
  174. ri->defaultKey2 = 0;
  175. state->getRenderPass()->addInst( ri );
  176. }
  177. void PhysicalZone::renderObject( ObjectRenderInst *ri,
  178. SceneRenderState *state,
  179. BaseMatInstance *overrideMat )
  180. {
  181. if (overrideMat)
  182. return;
  183. GFXStateBlockDesc desc;
  184. desc.setZReadWrite( true, false );
  185. desc.setBlend( true );
  186. desc.setCullMode( GFXCullNone );
  187. GFXTransformSaver saver;
  188. MatrixF mat = getRenderTransform();
  189. mat.scale( getScale() );
  190. GFX->multWorld( mat );
  191. GFXDrawUtil *drawer = GFX->getDrawUtil();
  192. drawer->drawPolyhedron( desc, mPolyhedron, ColorI( 0, 255, 0, 45 ) );
  193. desc.setFillModeWireframe();
  194. drawer->drawPolyhedron( desc, mPolyhedron, ColorF::BLACK );
  195. }
  196. //--------------------------------------------------------------------------
  197. U32 PhysicalZone::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
  198. {
  199. U32 i;
  200. U32 retMask = Parent::packUpdate(con, mask, stream);
  201. if (stream->writeFlag((mask & InitialUpdateMask) != 0)) {
  202. // Note that we don't really care about efficiency here, since this is an
  203. // edit-only ghost...
  204. mathWrite(*stream, mObjToWorld);
  205. mathWrite(*stream, mObjScale);
  206. // Write the polyhedron
  207. stream->write(mPolyhedron.pointList.size());
  208. for (i = 0; i < mPolyhedron.pointList.size(); i++)
  209. mathWrite(*stream, mPolyhedron.pointList[i]);
  210. stream->write(mPolyhedron.planeList.size());
  211. for (i = 0; i < mPolyhedron.planeList.size(); i++)
  212. mathWrite(*stream, mPolyhedron.planeList[i]);
  213. stream->write(mPolyhedron.edgeList.size());
  214. for (i = 0; i < mPolyhedron.edgeList.size(); i++) {
  215. const Polyhedron::Edge& rEdge = mPolyhedron.edgeList[i];
  216. stream->write(rEdge.face[0]);
  217. stream->write(rEdge.face[1]);
  218. stream->write(rEdge.vertex[0]);
  219. stream->write(rEdge.vertex[1]);
  220. }
  221. stream->write(mVelocityMod);
  222. stream->write(mGravityMod);
  223. mathWrite(*stream, mAppliedForce);
  224. stream->writeFlag(mActive);
  225. } else {
  226. stream->writeFlag(mActive);
  227. }
  228. return retMask;
  229. }
  230. void PhysicalZone::unpackUpdate(NetConnection* con, BitStream* stream)
  231. {
  232. Parent::unpackUpdate(con, stream);
  233. if (stream->readFlag()) {
  234. U32 i, size;
  235. MatrixF temp;
  236. Point3F tempScale;
  237. Polyhedron tempPH;
  238. // Transform
  239. mathRead(*stream, &temp);
  240. mathRead(*stream, &tempScale);
  241. // Read the polyhedron
  242. stream->read(&size);
  243. tempPH.pointList.setSize(size);
  244. for (i = 0; i < tempPH.pointList.size(); i++)
  245. mathRead(*stream, &tempPH.pointList[i]);
  246. stream->read(&size);
  247. tempPH.planeList.setSize(size);
  248. for (i = 0; i < tempPH.planeList.size(); i++)
  249. mathRead(*stream, &tempPH.planeList[i]);
  250. stream->read(&size);
  251. tempPH.edgeList.setSize(size);
  252. for (i = 0; i < tempPH.edgeList.size(); i++) {
  253. Polyhedron::Edge& rEdge = tempPH.edgeList[i];
  254. stream->read(&rEdge.face[0]);
  255. stream->read(&rEdge.face[1]);
  256. stream->read(&rEdge.vertex[0]);
  257. stream->read(&rEdge.vertex[1]);
  258. }
  259. stream->read(&mVelocityMod);
  260. stream->read(&mGravityMod);
  261. mathRead(*stream, &mAppliedForce);
  262. setPolyhedron(tempPH);
  263. setScale(tempScale);
  264. setTransform(temp);
  265. mActive = stream->readFlag();
  266. } else {
  267. mActive = stream->readFlag();
  268. }
  269. }
  270. //--------------------------------------------------------------------------
  271. void PhysicalZone::setPolyhedron(const Polyhedron& rPolyhedron)
  272. {
  273. mPolyhedron = rPolyhedron;
  274. if (mPolyhedron.pointList.size() != 0) {
  275. mObjBox.minExtents.set(1e10, 1e10, 1e10);
  276. mObjBox.maxExtents.set(-1e10, -1e10, -1e10);
  277. for (U32 i = 0; i < mPolyhedron.pointList.size(); i++) {
  278. mObjBox.minExtents.setMin(mPolyhedron.pointList[i]);
  279. mObjBox.maxExtents.setMax(mPolyhedron.pointList[i]);
  280. }
  281. } else {
  282. mObjBox.minExtents.set(-0.5, -0.5, -0.5);
  283. mObjBox.maxExtents.set( 0.5, 0.5, 0.5);
  284. }
  285. MatrixF xform = getTransform();
  286. setTransform(xform);
  287. mClippedList.clear();
  288. mClippedList.mPlaneList = mPolyhedron.planeList;
  289. MatrixF base(true);
  290. base.scale(Point3F(1.0/mObjScale.x,
  291. 1.0/mObjScale.y,
  292. 1.0/mObjScale.z));
  293. base.mul(mWorldToObj);
  294. mClippedList.setBaseTransform(base);
  295. }
  296. //--------------------------------------------------------------------------
  297. void PhysicalZone::buildConvex(const Box3F& box, Convex* convex)
  298. {
  299. // These should really come out of a pool
  300. mConvexList->collectGarbage();
  301. Box3F realBox = box;
  302. mWorldToObj.mul(realBox);
  303. realBox.minExtents.convolveInverse(mObjScale);
  304. realBox.maxExtents.convolveInverse(mObjScale);
  305. if (realBox.isOverlapped(getObjBox()) == false)
  306. return;
  307. // Just return a box convex for the entire shape...
  308. Convex* cc = 0;
  309. CollisionWorkingList& wl = convex->getWorkingList();
  310. for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) {
  311. if (itr->mConvex->getType() == BoxConvexType &&
  312. itr->mConvex->getObject() == this) {
  313. cc = itr->mConvex;
  314. break;
  315. }
  316. }
  317. if (cc)
  318. return;
  319. // Create a new convex.
  320. BoxConvex* cp = new BoxConvex;
  321. mConvexList->registerObject(cp);
  322. convex->addToWorkingList(cp);
  323. cp->init(this);
  324. mObjBox.getCenter(&cp->mCenter);
  325. cp->mSize.x = mObjBox.len_x() / 2.0f;
  326. cp->mSize.y = mObjBox.len_y() / 2.0f;
  327. cp->mSize.z = mObjBox.len_z() / 2.0f;
  328. }
  329. bool PhysicalZone::testObject(SceneObject* enter)
  330. {
  331. // TODO: This doesn't look like it's testing against the polyhedron at
  332. // all. And whats the point of building a convex if no collision methods
  333. // are implemented?
  334. if (mPolyhedron.pointList.size() == 0)
  335. return false;
  336. mClippedList.clear();
  337. SphereF sphere;
  338. sphere.center = (mWorldBox.minExtents + mWorldBox.maxExtents) * 0.5;
  339. VectorF bv = mWorldBox.maxExtents - sphere.center;
  340. sphere.radius = bv.len();
  341. enter->buildPolyList(PLC_Collision, &mClippedList, mWorldBox, sphere);
  342. return mClippedList.isEmpty() == false;
  343. }
  344. bool PhysicalZone::testBox( const Box3F &box ) const
  345. {
  346. return mWorldBox.isOverlapped( box );
  347. }
  348. void PhysicalZone::activate()
  349. {
  350. AssertFatal(isServerObject(), "Client objects not allowed in ForceFieldInstance::open()");
  351. if (mActive != true)
  352. setMaskBits(ActiveMask);
  353. mActive = true;
  354. }
  355. void PhysicalZone::deactivate()
  356. {
  357. AssertFatal(isServerObject(), "Client objects not allowed in ForceFieldInstance::close()");
  358. if (mActive != false)
  359. setMaskBits(ActiveMask);
  360. mActive = false;
  361. }