123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "platform/platform.h"
- #include "T3D/trigger.h"
- #include "scene/sceneRenderState.h"
- #include "console/consoleTypes.h"
- #include "console/engineAPI.h"
- #include "collision/boxConvex.h"
- #include "core/stream/bitStream.h"
- #include "math/mathIO.h"
- #include "gfx/gfxTransformSaver.h"
- #include "renderInstance/renderPassManager.h"
- #include "gfx/gfxDrawUtil.h"
- #include "T3D/physics/physicsPlugin.h"
- #include "T3D/physics/physicsBody.h"
- #include "T3D/physics/physicsCollision.h"
- bool Trigger::smRenderTriggers = false;
- //-----------------------------------------------------------------------------
- //----------------------------------------------------------------------------
- IMPLEMENT_CO_DATABLOCK_V1(TriggerData);
- ConsoleDocClass( TriggerData,
- "@brief Defines shared properties for Trigger objects.\n\n"
- "The primary focus of the TriggerData datablock is the callbacks it provides when an object is "
- "within or leaves the Trigger bounds.\n"
- "@see Trigger.\n"
- "@ingroup gameObjects\n"
- "@ingroup Datablocks\n"
- );
- IMPLEMENT_CALLBACK( TriggerData, onEnterTrigger, void, ( Trigger* trigger, GameBase* obj ), ( trigger, obj ),
- "@brief Called when an object enters the volume of the Trigger instance using this TriggerData.\n\n"
- "@param trigger the Trigger instance whose volume the object entered\n"
- "@param obj the object that entered the volume of the Trigger instance\n" );
- IMPLEMENT_CALLBACK( TriggerData, onTickTrigger, void, ( Trigger* trigger ), ( trigger ),
- "@brief Called every tickPeriodMS number of milliseconds (as specified in the TriggerData) whenever "
- "one or more objects are inside the volume of the trigger.\n\n"
- "The Trigger has methods to retrieve the objects that are within the Trigger's bounds if you "
- "want to do something with them in this callback.\n"
- "@param trigger the Trigger instance whose volume the object is inside\n"
-
- "@see tickPeriodMS\n"
- "@see Trigger::getNumObjects()\n"
- "@see Trigger::getObject()\n");
- IMPLEMENT_CALLBACK( TriggerData, onLeaveTrigger, void, ( Trigger* trigger, GameBase* obj ), ( trigger, obj ),
- "@brief Called when an object leaves the volume of the Trigger instance using this TriggerData.\n\n"
- "@param trigger the Trigger instance whose volume the object left\n"
- "@param obj the object that left the volume of the Trigger instance\n" );
- TriggerData::TriggerData()
- {
- tickPeriodMS = 100;
- isClientSide = false;
- }
- bool TriggerData::onAdd()
- {
- if (!Parent::onAdd())
- return false;
- return true;
- }
- void TriggerData::initPersistFields()
- {
- addGroup("Callbacks");
- addField( "tickPeriodMS", TypeS32, Offset( tickPeriodMS, TriggerData ),
- "@brief Time in milliseconds between calls to onTickTrigger() while at least one object is within a Trigger's bounds.\n\n"
- "@see onTickTrigger()\n");
- addField( "clientSide", TypeBool, Offset( isClientSide, TriggerData ),
- "Forces Trigger callbacks to only be called on clients.");
- endGroup("Callbacks");
- Parent::initPersistFields();
- }
- //--------------------------------------------------------------------------
- void TriggerData::packData(BitStream* stream)
- {
- Parent::packData(stream);
- stream->write(tickPeriodMS);
- stream->write(isClientSide);
- }
- void TriggerData::unpackData(BitStream* stream)
- {
- Parent::unpackData(stream);
- stream->read(&tickPeriodMS);
- stream->read(&isClientSide);
- }
- //--------------------------------------------------------------------------
- IMPLEMENT_CO_NETOBJECT_V1(Trigger);
- ConsoleDocClass( Trigger,
- "@brief A Trigger is a volume of space that initiates script callbacks "
- "when objects pass through the Trigger.\n\n"
- "TriggerData provides the callbacks for the Trigger when an object enters, stays inside "
- "or leaves the Trigger's volume.\n\n"
- "@see TriggerData\n"
- "@ingroup gameObjects\n"
- );
- IMPLEMENT_CALLBACK( Trigger, onAdd, void, ( U32 objectId ), ( objectId ),
- "@brief Called when the Trigger is being created.\n\n"
- "@param objectId the object id of the Trigger being created\n" );
- IMPLEMENT_CALLBACK( Trigger, onRemove, void, ( U32 objectId ), ( objectId ),
- "@brief Called just before the Trigger is deleted.\n\n"
- "@param objectId the object id of the Trigger being deleted\n" );
- Trigger::Trigger()
- {
- // Don't ghost by default.
- mNetFlags.set(Ghostable | ScopeAlways);
- mTypeMask |= TriggerObjectType;
- mObjScale.set(1, 1, 1);
- mObjToWorld.identity();
- mWorldToObj.identity();
- mDataBlock = NULL;
- mLastThink = 0;
- mCurrTick = 0;
- mConvexList = new Convex;
- mPhysicsRep = NULL;
- mTripOnce = false;
- mTrippedBy = 0xFFFFFFFF;
- mTripCondition = "";
- }
- Trigger::~Trigger()
- {
- delete mConvexList;
- mConvexList = NULL;
- SAFE_DELETE( mPhysicsRep );
- }
- bool Trigger::castRay(const Point3F &start, const Point3F &end, RayInfo* info)
- {
- // Collide against bounding box
- F32 st,et,fst = 0,fet = 1;
- F32 *bmin = &mObjBox.minExtents.x;
- F32 *bmax = &mObjBox.maxExtents.x;
- F32 const *si = &start.x;
- F32 const *ei = &end.x;
- for (S32 i = 0; i < 3; i++)
- {
- if (*si < *ei)
- {
- if (*si > *bmax || *ei < *bmin)
- return false;
- F32 di = *ei - *si;
- st = (*si < *bmin)? (*bmin - *si) / di: 0;
- et = (*ei > *bmax)? (*bmax - *si) / di: 1;
- }
- else
- {
- if (*ei > *bmax || *si < *bmin)
- return false;
- F32 di = *ei - *si;
- st = (*si > *bmax)? (*bmax - *si) / di: 0;
- et = (*ei < *bmin)? (*bmin - *si) / di: 1;
- }
- if (st > fst) fst = st;
- if (et < fet) fet = et;
- if (fet < fst)
- return false;
- bmin++; bmax++;
- si++; ei++;
- }
- info->normal = start - end;
- info->normal.normalizeSafe();
- getTransform().mulV( info->normal );
- info->t = fst;
- info->object = this;
- info->point.interpolate(start,end,fst);
- info->material = 0;
- return true;
- }
- //--------------------------------------------------------------------------
- /* Console polyhedron data type exporter
- The polyhedron type is really a quadrilateral and consists of a corner
- point follow by three vectors representing the edges extending from the
- corner.
- */
- DECLARE_STRUCT( Polyhedron );
- IMPLEMENT_STRUCT( Polyhedron, Polyhedron,,
- "" )
- FIELD(mPointList, pointList, 1, "")
- FIELD(mPlaneList, planeList, 1, "")
- FIELD(mEdgeList, edgeList, 1, "")
- END_IMPLEMENT_STRUCT;
- ConsoleType(floatList, TypeTriggerPolyhedron, Polyhedron, "")
- ConsoleGetType( TypeTriggerPolyhedron )
- {
- U32 i;
- Polyhedron* pPoly = reinterpret_cast<Polyhedron*>(dptr);
- // First point is corner, need to find the three vectors...`
- Point3F origin = pPoly->mPointList[0];
- U32 currVec = 0;
- Point3F vecs[3];
- for (i = 0; i < pPoly->mEdgeList.size(); i++) {
- const U32 *vertex = pPoly->mEdgeList[i].vertex;
- if (vertex[0] == 0)
- vecs[currVec++] = pPoly->mPointList[vertex[1]] - origin;
- else
- if (vertex[1] == 0)
- vecs[currVec++] = pPoly->mPointList[vertex[0]] - origin;
- }
- AssertFatal(currVec == 3, "Internal error: Bad trigger polyhedron");
- // Build output string.
- static const U32 bufSize = 1024;
- char* retBuf = Con::getReturnBuffer(bufSize);
- dSprintf(retBuf, bufSize, "%7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f",
- origin.x, origin.y, origin.z,
- vecs[0].x, vecs[0].y, vecs[0].z,
- vecs[2].x, vecs[2].y, vecs[2].z,
- vecs[1].x, vecs[1].y, vecs[1].z);
-
- return retBuf;
- }
- /* Console polyhedron data type loader
- The polyhedron type is really a quadrilateral and consists of an corner
- point follow by three vectors representing the edges extending from the
- corner.
- */
- ConsoleSetType( TypeTriggerPolyhedron )
- {
- if (argc != 1) {
- Con::printf("(TypeTriggerPolyhedron) multiple args not supported for polyhedra");
- return;
- }
- Point3F origin;
- Point3F vecs[3];
- U32 numArgs = dSscanf(argv[0], "%g %g %g %g %g %g %g %g %g %g %g %g",
- &origin.x, &origin.y, &origin.z,
- &vecs[0].x, &vecs[0].y, &vecs[0].z,
- &vecs[1].x, &vecs[1].y, &vecs[1].z,
- &vecs[2].x, &vecs[2].y, &vecs[2].z);
- if (numArgs != 12) {
- Con::printf("Bad polyhedron!");
- return;
- }
- Polyhedron* pPoly = reinterpret_cast<Polyhedron*>(dptr);
- // This setup goes against conventions for Polyhedrons in that it a) sets up
- // edges with CCW instead of CW order for face[0] and that it b) lets plane
- // normals face outwards rather than inwards.
- pPoly->mPointList.setSize(8);
- pPoly->mPointList[0] = origin;
- pPoly->mPointList[1] = origin + vecs[0];
- pPoly->mPointList[2] = origin + vecs[1];
- pPoly->mPointList[3] = origin + vecs[2];
- pPoly->mPointList[4] = origin + vecs[0] + vecs[1];
- pPoly->mPointList[5] = origin + vecs[0] + vecs[2];
- pPoly->mPointList[6] = origin + vecs[1] + vecs[2];
- pPoly->mPointList[7] = origin + vecs[0] + vecs[1] + vecs[2];
- Point3F normal;
- pPoly->mPlaneList.setSize(6);
- mCross(vecs[2], vecs[0], &normal);
- pPoly->mPlaneList[0].set(origin, normal);
- mCross(vecs[0], vecs[1], &normal);
- pPoly->mPlaneList[1].set(origin, normal);
- mCross(vecs[1], vecs[2], &normal);
- pPoly->mPlaneList[2].set(origin, normal);
- mCross(vecs[1], vecs[0], &normal);
- pPoly->mPlaneList[3].set(pPoly->mPointList[7], normal);
- mCross(vecs[2], vecs[1], &normal);
- pPoly->mPlaneList[4].set(pPoly->mPointList[7], normal);
- mCross(vecs[0], vecs[2], &normal);
- pPoly->mPlaneList[5].set(pPoly->mPointList[7], normal);
- pPoly->mEdgeList.setSize(12);
- pPoly->mEdgeList[0].vertex[0] = 0; pPoly->mEdgeList[0].vertex[1] = 1; pPoly->mEdgeList[0].face[0] = 0; pPoly->mEdgeList[0].face[1] = 1;
- pPoly->mEdgeList[1].vertex[0] = 1; pPoly->mEdgeList[1].vertex[1] = 5; pPoly->mEdgeList[1].face[0] = 0; pPoly->mEdgeList[1].face[1] = 4;
- pPoly->mEdgeList[2].vertex[0] = 5; pPoly->mEdgeList[2].vertex[1] = 3; pPoly->mEdgeList[2].face[0] = 0; pPoly->mEdgeList[2].face[1] = 3;
- pPoly->mEdgeList[3].vertex[0] = 3; pPoly->mEdgeList[3].vertex[1] = 0; pPoly->mEdgeList[3].face[0] = 0; pPoly->mEdgeList[3].face[1] = 2;
- pPoly->mEdgeList[4].vertex[0] = 3; pPoly->mEdgeList[4].vertex[1] = 6; pPoly->mEdgeList[4].face[0] = 3; pPoly->mEdgeList[4].face[1] = 2;
- pPoly->mEdgeList[5].vertex[0] = 6; pPoly->mEdgeList[5].vertex[1] = 2; pPoly->mEdgeList[5].face[0] = 2; pPoly->mEdgeList[5].face[1] = 5;
- pPoly->mEdgeList[6].vertex[0] = 2; pPoly->mEdgeList[6].vertex[1] = 0; pPoly->mEdgeList[6].face[0] = 2; pPoly->mEdgeList[6].face[1] = 1;
- pPoly->mEdgeList[7].vertex[0] = 1; pPoly->mEdgeList[7].vertex[1] = 4; pPoly->mEdgeList[7].face[0] = 4; pPoly->mEdgeList[7].face[1] = 1;
- pPoly->mEdgeList[8].vertex[0] = 4; pPoly->mEdgeList[8].vertex[1] = 2; pPoly->mEdgeList[8].face[0] = 1; pPoly->mEdgeList[8].face[1] = 5;
- pPoly->mEdgeList[9].vertex[0] = 4; pPoly->mEdgeList[9].vertex[1] = 7; pPoly->mEdgeList[9].face[0] = 4; pPoly->mEdgeList[9].face[1] = 5;
- pPoly->mEdgeList[10].vertex[0] = 5; pPoly->mEdgeList[10].vertex[1] = 7; pPoly->mEdgeList[10].face[0] = 3; pPoly->mEdgeList[10].face[1] = 4;
- pPoly->mEdgeList[11].vertex[0] = 7; pPoly->mEdgeList[11].vertex[1] = 6; pPoly->mEdgeList[11].face[0] = 3; pPoly->mEdgeList[11].face[1] = 5;
- }
- //-----------------------------------------------------------------------------
- void Trigger::consoleInit()
- {
- Con::addVariable( "$Trigger::renderTriggers", TypeBool, &smRenderTriggers,
- "@brief Forces all Trigger's to render.\n\n"
- "Used by the Tools and debug render modes.\n"
- "@ingroup gameObjects" );
- }
- void Trigger::initPersistFields()
- {
- addField("polyhedron", TypeTriggerPolyhedron, Offset(mTriggerPolyhedron, Trigger),
- "@brief Defines a non-rectangular area for the trigger.\n\n"
- "Rather than the standard rectangular bounds, this optional parameter defines a quadrilateral "
- "trigger area. The quadrilateral is defined as a corner point followed by three vectors "
- "representing the edges extending from the corner.\n");
- addField("TripOnce", TypeBool, Offset(mTripOnce, Trigger),"Do we trigger callacks just the once?");
- addField("TripCondition", TypeRealString, Offset(mTripCondition, Trigger),"evaluation condition to trip callbacks (true/false)");
- addField("TrippedBy", TypeS32, Offset(mTrippedBy, Trigger), "typemask filter");
- addProtectedField("enterCommand", TypeCommand, Offset(mEnterCommand, Trigger), &setEnterCmd, &defaultProtectedGetFn,
- "The command to execute when an object enters this trigger. Object id stored in %%obj. Maximum 1023 characters." );
- addProtectedField("leaveCommand", TypeCommand, Offset(mLeaveCommand, Trigger), &setLeaveCmd, &defaultProtectedGetFn,
- "The command to execute when an object leaves this trigger. Object id stored in %%obj. Maximum 1023 characters." );
- addProtectedField("tickCommand", TypeCommand, Offset(mTickCommand, Trigger), &setTickCmd, &defaultProtectedGetFn,
- "The command to execute while an object is inside this trigger. Maximum 1023 characters." );
- Parent::initPersistFields();
- }
- bool Trigger::setEnterCmd( void *object, const char *index, const char *data )
- {
- static_cast<Trigger*>(object)->setMaskBits(EnterCmdMask);
- return true; // to update the actual field
- }
- bool Trigger::setLeaveCmd(void *object, const char *index, const char *data)
- {
- static_cast<Trigger*>(object)->setMaskBits(LeaveCmdMask);
- return true; // to update the actual field
- }
- bool Trigger::setTickCmd(void *object, const char *index, const char *data)
- {
- static_cast<Trigger*>(object)->setMaskBits(TickCmdMask);
- return true; // to update the actual field
- }
- //------------------------------------------------------------------------------
- void Trigger::testObjects()
- {
- Vector<SceneObject*> foundobjs;
- foundobjs.clear();
- if (getSceneManager() && getSceneManager()->getContainer() && getSceneManager()->getZoneManager())
- getSceneManager()->getContainer()->findObjectList(getWorldBox(), mTrippedBy, &foundobjs);
- else return;
- for (S32 i = 0; i < foundobjs.size(); i++)
- {
- GameBase* so = dynamic_cast<GameBase*>(foundobjs[i]);
- if (so)
- potentialEnterObject(so);
- }
- }
- //--------------------------------------------------------------------------
- bool Trigger::onAdd()
- {
- if(!Parent::onAdd())
- return false;
- onAdd_callback( getId() );
- Polyhedron temp = mTriggerPolyhedron;
- setTriggerPolyhedron(temp);
- mTripped = false;
- addToScene();
- if (isServerObject())
- scriptOnAdd();
- testObjects();
- return true;
- }
- void Trigger::onRemove()
- {
- onRemove_callback( getId() );
- mConvexList->nukeList();
- removeFromScene();
- Parent::onRemove();
- }
- bool Trigger::onNewDataBlock( GameBaseData *dptr, bool reload )
- {
- mDataBlock = dynamic_cast<TriggerData*>( dptr );
- if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
- return false;
- scriptOnNewDataBlock();
- return true;
- }
- void Trigger::onDeleteNotify( SimObject *obj )
- {
- GameBase* pScene = dynamic_cast<GameBase*>( obj );
- if ( pScene != NULL && mDataBlock != NULL )
- {
- for ( U32 i = 0; i < mObjects.size(); i++ )
- {
- if ( pScene == mObjects[i] )
- {
- mObjects.erase(i);
- if (mDataBlock)
- mDataBlock->onLeaveTrigger_callback( this, NULL );
- break;
- }
- }
- }
- Parent::onDeleteNotify( obj );
- }
- void Trigger::inspectPostApply()
- {
- setTriggerPolyhedron(mTriggerPolyhedron);
- setMaskBits(PolyMask);
- Parent::inspectPostApply();
- }
- //--------------------------------------------------------------------------
- void Trigger::buildConvex(const Box3F& box, Convex* convex)
- {
- // These should really come out of a pool
- mConvexList->collectGarbage();
- Box3F realBox = box;
- mWorldToObj.mul(realBox);
- realBox.minExtents.convolveInverse(mObjScale);
- realBox.maxExtents.convolveInverse(mObjScale);
- if (realBox.isOverlapped(getObjBox()) == false)
- return;
- // Just return a box convex for the entire shape...
- Convex* cc = 0;
- CollisionWorkingList& wl = convex->getWorkingList();
- for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) {
- if (itr->mConvex->getType() == BoxConvexType &&
- itr->mConvex->getObject() == this) {
- cc = itr->mConvex;
- break;
- }
- }
- if (cc)
- return;
- // Create a new convex.
- BoxConvex* cp = new BoxConvex;
- mConvexList->registerObject(cp);
- convex->addToWorkingList(cp);
- cp->init(this);
- mObjBox.getCenter(&cp->mCenter);
- cp->mSize.x = mObjBox.len_x() / 2.0f;
- cp->mSize.y = mObjBox.len_y() / 2.0f;
- cp->mSize.z = mObjBox.len_z() / 2.0f;
- }
- //------------------------------------------------------------------------------
- void Trigger::setTransform(const MatrixF & mat)
- {
- Parent::setTransform(mat);
- if ( mPhysicsRep )
- mPhysicsRep->setTransform( mat );
- if (isServerObject()) {
- MatrixF base(true);
- base.scale(Point3F(1.0/mObjScale.x,
- 1.0/mObjScale.y,
- 1.0/mObjScale.z));
- base.mul(mWorldToObj);
- mClippedList.setBaseTransform(base);
- setMaskBits(TransformMask | ScaleMask);
- }
- testObjects();
- }
- void Trigger::onUnmount( SceneObject *obj, S32 node )
- {
- Parent::onUnmount( obj, node );
- // Make sure the client get's the final server pos.
- setMaskBits(TransformMask | ScaleMask);
- }
- void Trigger::prepRenderImage( SceneRenderState *state )
- {
- // only render if selected or render flag is set
- if ( !smRenderTriggers && !isSelected() )
- return;
- ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
- ri->renderDelegate.bind( this, &Trigger::renderObject );
- ri->type = RenderPassManager::RIT_Editor;
- ri->translucentSort = true;
- ri->defaultKey = 1;
- state->getRenderPass()->addInst( ri );
- }
- void Trigger::renderObject( ObjectRenderInst *ri,
- SceneRenderState *state,
- BaseMatInstance *overrideMat )
- {
- if(overrideMat)
- return;
- GFXStateBlockDesc desc;
- desc.setZReadWrite( true, false );
- desc.setBlend( true );
- // Trigger polyhedrons are set up with outward facing normals and CCW ordering
- // so can't enable backface culling.
- desc.setCullMode( GFXCullNone );
- GFXTransformSaver saver;
- MatrixF mat = getRenderTransform();
- mat.scale( getScale() );
- GFX->multWorld( mat );
- GFXDrawUtil *drawer = GFX->getDrawUtil();
-
- drawer->drawPolyhedron( desc, mTriggerPolyhedron, ColorI( 255, 192, 0, 45 ) );
- // Render wireframe.
- desc.setFillModeWireframe();
- drawer->drawPolyhedron( desc, mTriggerPolyhedron, ColorI::BLACK );
- }
- void Trigger::setTriggerPolyhedron(const Polyhedron& rPolyhedron)
- {
- mTriggerPolyhedron = rPolyhedron;
- if (mTriggerPolyhedron.mPointList.size() != 0) {
- mObjBox.minExtents.set(1e10, 1e10, 1e10);
- mObjBox.maxExtents.set(-1e10, -1e10, -1e10);
- for (U32 i = 0; i < mTriggerPolyhedron.mPointList.size(); i++) {
- mObjBox.minExtents.setMin(mTriggerPolyhedron.mPointList[i]);
- mObjBox.maxExtents.setMax(mTriggerPolyhedron.mPointList[i]);
- }
- } else {
- mObjBox.minExtents.set(-0.5, -0.5, -0.5);
- mObjBox.maxExtents.set( 0.5, 0.5, 0.5);
- }
- MatrixF xform = getTransform();
- setTransform(xform);
- mClippedList.clear();
- mClippedList.mPlaneList = mTriggerPolyhedron.mPlaneList;
- // for (U32 i = 0; i < mClippedList.mPlaneList.size(); i++)
- // mClippedList.mPlaneList[i].neg();
- MatrixF base(true);
- base.scale(Point3F(1.0/mObjScale.x,
- 1.0/mObjScale.y,
- 1.0/mObjScale.z));
- base.mul(mWorldToObj);
- mClippedList.setBaseTransform(base);
- SAFE_DELETE( mPhysicsRep );
- if ( PHYSICSMGR )
- {
- PhysicsCollision *colShape = PHYSICSMGR->createCollision();
- MatrixF colMat( true );
- colMat.displace( Point3F( 0, 0, mObjBox.getExtents().z * 0.5f * mObjScale.z ) );
-
- colShape->addBox( mObjBox.getExtents() * 0.5f * mObjScale, colMat );
- //MatrixF colMat( true );
- //colMat.scale( mObjScale );
- //colShape->addConvex( mTriggerPolyhedron.pointList.address(), mTriggerPolyhedron.pointList.size(), colMat );
- PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
- mPhysicsRep = PHYSICSMGR->createBody();
- mPhysicsRep->init( colShape, 0, PhysicsBody::BF_TRIGGER | PhysicsBody::BF_KINEMATIC, this, world );
- mPhysicsRep->setTransform( getTransform() );
- }
- }
- //--------------------------------------------------------------------------
- bool Trigger::testObject(GameBase* enter)
- {
- if (mTriggerPolyhedron.mPointList.size() == 0)
- return false;
- if (!(enter->getTypeMask() & mTrippedBy))
- return false; //not the right type of object
-
- mClippedList.clear();
- SphereF sphere;
- sphere.center = (mWorldBox.minExtents + mWorldBox.maxExtents) * 0.5;
- VectorF bv = mWorldBox.maxExtents - sphere.center;
- sphere.radius = bv.len();
- enter->buildPolyList(PLC_Collision, &mClippedList, mWorldBox, sphere);
- return mClippedList.isEmpty() == false;
- }
- bool Trigger::testTrippable()
- {
- if ((mTripOnce == true) && (mTripped == true))
- return false; // we've already fired the once
- return true;
- }
- bool Trigger::testCondition()
- {
- if (mTripCondition.isEmpty())
- return true; //we've got no tests to run so just do it
- //test the mapper plugged in condition line
- String resVar = getIdString() + String(".result");
- Con::setBoolVariable(resVar.c_str(), false);
- String command = resVar + "=" + mTripCondition + ";";
- Con::evaluatef(command.c_str());
- if (Con::getBoolVariable(resVar.c_str()) == 1)
- {
- return true;
- }
- return false;
- }
- bool Trigger::evalCmD(String* cmd)
- {
- if (!testTrippable()) return false;
- if (cmd && cmd->isNotEmpty())//do we have a callback?
- {
- return testCondition();
- }
- return false;
- }
- void Trigger::potentialEnterObject(GameBase* enter)
- {
- if( (!mDataBlock || mDataBlock->isClientSide) && isServerObject() )
- return;
- if( (mDataBlock && !mDataBlock->isClientSide) && isGhost() )
- return;
- for (U32 i = 0; i < mObjects.size(); i++) {
- if (mObjects[i] == enter)
- return;
- }
- if (testObject(enter) == true) {
- mObjects.push_back(enter);
- deleteNotify(enter);
- if(evalCmD(&mEnterCommand))
- {
- String command = String("%obj = ") + enter->getIdString() + ";" + mEnterCommand;
- Con::evaluate(command.c_str());
- }
- if( mDataBlock && testTrippable() && testCondition())
- mDataBlock->onEnterTrigger_callback( this, enter );
- mTripped = true;
- }
- }
- void Trigger::processTick(const Move* move)
- {
- Parent::processTick(move);
- if (!mDataBlock)
- return;
- if (mDataBlock->isClientSide && isServerObject())
- return;
- if (!mDataBlock->isClientSide && isClientObject())
- return;
- if (isMounted()) {
- MatrixF mat;
- mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat );
- setTransform(mat);
- setRenderTransform(mat);
- }
- //
- if (mObjects.size() == 0)
- return;
- if (mLastThink + mDataBlock->tickPeriodMS < mCurrTick)
- {
- mCurrTick = 0;
- mLastThink = 0;
- for (S32 i = S32(mObjects.size() - 1); i >= 0; i--)
- {
- if (testObject(mObjects[i]) == false)
- {
- GameBase* remove = mObjects[i];
- mObjects.erase(i);
- clearNotify(remove);
-
- if (evalCmD(&mLeaveCommand))
- {
- String command = String("%obj = ") + remove->getIdString() + ";" + mLeaveCommand;
- Con::evaluate(command.c_str());
- }
- if (testTrippable() && testCondition())
- mDataBlock->onLeaveTrigger_callback( this, remove );
- mTripped = true;
- }
- }
- if (evalCmD(&mTickCommand))
- Con::evaluate(mTickCommand.c_str());
- if (mObjects.size() != 0 && testTrippable() && testCondition())
- mDataBlock->onTickTrigger_callback( this );
- }
- else
- {
- mCurrTick += TickMs;
- }
- }
- void Trigger::interpolateTick(F32 delta)
- {
- if (isMounted()) {
- MatrixF mat;
- mMount.object->getRenderMountTransform( delta, mMount.node, mMount.xfm, &mat );
- setRenderTransform(mat);
- }
- }
- //--------------------------------------------------------------------------
- U32 Trigger::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
- {
- U32 i;
- U32 retMask = Parent::packUpdate(con, mask, stream);
- if( stream->writeFlag( mask & TransformMask ) )
- {
- stream->writeAffineTransform(mObjToWorld);
- }
- // Write the polyhedron
- if( stream->writeFlag( mask & PolyMask ) )
- {
- stream->write(mTriggerPolyhedron.mPointList.size());
- for (i = 0; i < mTriggerPolyhedron.mPointList.size(); i++)
- mathWrite(*stream, mTriggerPolyhedron.mPointList[i]);
- stream->write(mTriggerPolyhedron.mPlaneList.size());
- for (i = 0; i < mTriggerPolyhedron.mPlaneList.size(); i++)
- mathWrite(*stream, mTriggerPolyhedron.mPlaneList[i]);
- stream->write(mTriggerPolyhedron.mEdgeList.size());
- for (i = 0; i < mTriggerPolyhedron.mEdgeList.size(); i++) {
- const Polyhedron::Edge& rEdge = mTriggerPolyhedron.mEdgeList[i];
- stream->write(rEdge.face[0]);
- stream->write(rEdge.face[1]);
- stream->write(rEdge.vertex[0]);
- stream->write(rEdge.vertex[1]);
- }
- }
- if( stream->writeFlag( mask & EnterCmdMask ) )
- stream->writeLongString(CMD_SIZE-1, mEnterCommand.c_str());
- if( stream->writeFlag( mask & LeaveCmdMask ) )
- stream->writeLongString(CMD_SIZE-1, mLeaveCommand.c_str());
- if( stream->writeFlag( mask & TickCmdMask ) )
- stream->writeLongString(CMD_SIZE-1, mTickCommand.c_str());
- return retMask;
- }
- void Trigger::unpackUpdate(NetConnection* con, BitStream* stream)
- {
- Parent::unpackUpdate(con, stream);
- U32 i, size;
- // Transform
- if( stream->readFlag() )
- {
- MatrixF temp;
- stream->readAffineTransform(&temp);
- setTransform(temp);
- }
- // Read the polyhedron
- if( stream->readFlag() )
- {
- Polyhedron tempPH;
- stream->read(&size);
- tempPH.mPointList.setSize(size);
- for (i = 0; i < tempPH.mPointList.size(); i++)
- mathRead(*stream, &tempPH.mPointList[i]);
- stream->read(&size);
- tempPH.mPlaneList.setSize(size);
- for (i = 0; i < tempPH.mPlaneList.size(); i++)
- mathRead(*stream, &tempPH.mPlaneList[i]);
- stream->read(&size);
- tempPH.mEdgeList.setSize(size);
- for (i = 0; i < tempPH.mEdgeList.size(); i++) {
- Polyhedron::Edge& rEdge = tempPH.mEdgeList[i];
- stream->read(&rEdge.face[0]);
- stream->read(&rEdge.face[1]);
- stream->read(&rEdge.vertex[0]);
- stream->read(&rEdge.vertex[1]);
- }
- setTriggerPolyhedron(tempPH);
- }
- if( stream->readFlag() )
- {
- char buf[CMD_SIZE];
- stream->readLongString(CMD_SIZE-1, buf);
- mEnterCommand = buf;
- }
- if( stream->readFlag() )
- {
- char buf[CMD_SIZE];
- stream->readLongString(CMD_SIZE-1, buf);
- mLeaveCommand = buf;
- }
- if( stream->readFlag() )
- {
- char buf[CMD_SIZE];
- stream->readLongString(CMD_SIZE-1, buf);
- mTickCommand = buf;
- }
- }
- //ConsoleMethod( Trigger, getNumObjects, S32, 2, 2, "")
- DefineEngineMethod( Trigger, getNumObjects, S32, (),,
- "@brief Get the number of objects that are within the Trigger's bounds.\n\n"
- "@see getObject()\n")
- {
- return object->getNumTriggeringObjects();
- }
- //ConsoleMethod( Trigger, getObject, S32, 3, 3, "(int idx)")
- DefineEngineMethod( Trigger, getObject, S32, ( S32 index ),,
- "@brief Retrieve the requested object that is within the Trigger's bounds.\n\n"
- "@param index Index of the object to get (range is 0 to getNumObjects()-1)\n"
- "@returns The SimObjectID of the object, or -1 if the requested index is invalid.\n"
- "@see getNumObjects()\n")
- {
- if (index >= object->getNumTriggeringObjects() || index < 0)
- return -1;
- else
- return object->getObject(U32(index))->getId();
- }
|