2012-09-19 15:15:01 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// 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"
|
2023-04-23 08:39:54 +00:00
|
|
|
#include "console/script.h"
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
#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()
|
|
|
|
|
{
|
2023-01-28 22:12:05 +00:00
|
|
|
docsURL;
|
2012-09-19 15:15:01 +00:00
|
|
|
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;
|
2020-09-05 00:29:50 +00:00
|
|
|
mTripOnce = false;
|
|
|
|
|
mTrippedBy = 0xFFFFFFFF;
|
2024-10-21 05:08:07 +00:00
|
|
|
mTripIf = "";
|
2022-02-14 07:07:39 +00:00
|
|
|
|
|
|
|
|
//Default up a basic square
|
|
|
|
|
Point3F vecs[3] = { Point3F(1.0, 0.0, 0.0),
|
|
|
|
|
Point3F(0.0, -1.0, 0.0),
|
|
|
|
|
Point3F(0.0, 0.0, 1.0) };
|
|
|
|
|
|
|
|
|
|
mTriggerPolyhedron = Polyhedron(Point3F(-0.5, 0.5, 0.0), vecs);
|
2012-09-19 15:15:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2013-08-04 21:26:01 +00:00
|
|
|
for (S32 i = 0; i < 3; i++)
|
2012-09-19 15:15:01 +00:00
|
|
|
{
|
|
|
|
|
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,,
|
|
|
|
|
"" )
|
2019-08-03 10:39:41 +00:00
|
|
|
|
|
|
|
|
FIELD(mPointList, pointList, 1, "")
|
|
|
|
|
FIELD(mPlaneList, planeList, 1, "")
|
|
|
|
|
FIELD(mEdgeList, edgeList, 1, "")
|
|
|
|
|
|
2012-09-19 15:15:01 +00:00
|
|
|
END_IMPLEMENT_STRUCT;
|
2015-10-13 20:19:36 +00:00
|
|
|
ConsoleType(floatList, TypeTriggerPolyhedron, Polyhedron, "")
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
ConsoleGetType( TypeTriggerPolyhedron )
|
|
|
|
|
{
|
|
|
|
|
U32 i;
|
|
|
|
|
Polyhedron* pPoly = reinterpret_cast<Polyhedron*>(dptr);
|
|
|
|
|
|
|
|
|
|
// First point is corner, need to find the three vectors...`
|
2018-03-14 20:18:00 +00:00
|
|
|
Point3F origin = pPoly->mPointList[0];
|
2012-09-19 15:15:01 +00:00
|
|
|
U32 currVec = 0;
|
|
|
|
|
Point3F vecs[3];
|
2018-03-14 20:18:00 +00:00
|
|
|
for (i = 0; i < pPoly->mEdgeList.size(); i++) {
|
|
|
|
|
const U32 *vertex = pPoly->mEdgeList[i].vertex;
|
2012-09-19 15:15:01 +00:00
|
|
|
if (vertex[0] == 0)
|
2018-03-14 20:18:00 +00:00
|
|
|
vecs[currVec++] = pPoly->mPointList[vertex[1]] - origin;
|
2012-09-19 15:15:01 +00:00
|
|
|
else
|
|
|
|
|
if (vertex[1] == 0)
|
2018-03-14 20:18:00 +00:00
|
|
|
vecs[currVec++] = pPoly->mPointList[vertex[0]] - origin;
|
2012-09-19 15:15:01 +00:00
|
|
|
}
|
|
|
|
|
AssertFatal(currVec == 3, "Internal error: Bad trigger polyhedron");
|
|
|
|
|
|
|
|
|
|
// Build output string.
|
2014-05-15 07:12:43 +00:00
|
|
|
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",
|
2012-09-19 15:15:01 +00:00
|
|
|
origin.x, origin.y, origin.z,
|
|
|
|
|
vecs[0].x, vecs[0].y, vecs[0].z,
|
2014-02-14 05:27:29 +00:00
|
|
|
vecs[2].x, vecs[2].y, vecs[2].z,
|
|
|
|
|
vecs[1].x, vecs[1].y, vecs[1].z);
|
2013-10-28 04:13:14 +00:00
|
|
|
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2018-03-14 20:18:00 +00:00
|
|
|
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];
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
Point3F normal;
|
2018-03-14 20:18:00 +00:00
|
|
|
pPoly->mPlaneList.setSize(6);
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
mCross(vecs[2], vecs[0], &normal);
|
2018-03-14 20:18:00 +00:00
|
|
|
pPoly->mPlaneList[0].set(origin, normal);
|
2012-09-19 15:15:01 +00:00
|
|
|
mCross(vecs[0], vecs[1], &normal);
|
2018-03-14 20:18:00 +00:00
|
|
|
pPoly->mPlaneList[1].set(origin, normal);
|
2012-09-19 15:15:01 +00:00
|
|
|
mCross(vecs[1], vecs[2], &normal);
|
2018-03-14 20:18:00 +00:00
|
|
|
pPoly->mPlaneList[2].set(origin, normal);
|
2012-09-19 15:15:01 +00:00
|
|
|
mCross(vecs[1], vecs[0], &normal);
|
2018-03-14 20:18:00 +00:00
|
|
|
pPoly->mPlaneList[3].set(pPoly->mPointList[7], normal);
|
2012-09-19 15:15:01 +00:00
|
|
|
mCross(vecs[2], vecs[1], &normal);
|
2018-03-14 20:18:00 +00:00
|
|
|
pPoly->mPlaneList[4].set(pPoly->mPointList[7], normal);
|
2012-09-19 15:15:01 +00:00
|
|
|
mCross(vecs[0], vecs[2], &normal);
|
2018-03-14 20:18:00 +00:00
|
|
|
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;
|
2012-09-19 15:15:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
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()
|
|
|
|
|
{
|
2023-01-27 07:13:15 +00:00
|
|
|
docsURL;
|
2012-09-19 15:15:01 +00:00
|
|
|
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");
|
|
|
|
|
|
2020-09-05 00:29:50 +00:00
|
|
|
addField("TripOnce", TypeBool, Offset(mTripOnce, Trigger),"Do we trigger callacks just the once?");
|
2024-10-21 05:08:07 +00:00
|
|
|
addField("tripIf", TypeRealString, Offset(mTripIf, Trigger),"evaluation condition to trip callbacks (true/false)");
|
2024-05-04 14:56:04 +00:00
|
|
|
addField("TrippedBy", TypeGameTypeMasksType, Offset(mTrippedBy, Trigger), "typemask filter");
|
2012-09-19 15:15:01 +00:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-13 17:12:52 +00:00
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void Trigger::testObjects()
|
|
|
|
|
{
|
|
|
|
|
Vector<SceneObject*> foundobjs;
|
2020-08-17 23:06:23 +00:00
|
|
|
foundobjs.clear();
|
|
|
|
|
if (getSceneManager() && getSceneManager()->getContainer() && getSceneManager()->getZoneManager())
|
2020-09-05 00:29:50 +00:00
|
|
|
getSceneManager()->getContainer()->findObjectList(getWorldBox(), mTrippedBy, &foundobjs);
|
2020-08-17 23:06:23 +00:00
|
|
|
else return;
|
|
|
|
|
|
|
|
|
|
for (S32 i = 0; i < foundobjs.size(); i++)
|
2020-06-13 17:12:52 +00:00
|
|
|
{
|
|
|
|
|
GameBase* so = dynamic_cast<GameBase*>(foundobjs[i]);
|
|
|
|
|
if (so)
|
|
|
|
|
potentialEnterObject(so);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-19 15:15:01 +00:00
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
bool Trigger::onAdd()
|
|
|
|
|
{
|
|
|
|
|
if(!Parent::onAdd())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
onAdd_callback( getId() );
|
|
|
|
|
|
|
|
|
|
Polyhedron temp = mTriggerPolyhedron;
|
|
|
|
|
setTriggerPolyhedron(temp);
|
2020-09-05 00:29:50 +00:00
|
|
|
mTripped = false;
|
2012-09-19 15:15:01 +00:00
|
|
|
addToScene();
|
|
|
|
|
|
|
|
|
|
if (isServerObject())
|
|
|
|
|
scriptOnAdd();
|
2020-06-13 17:12:52 +00:00
|
|
|
|
|
|
|
|
testObjects();
|
|
|
|
|
|
2012-09-19 15:15:01 +00:00
|
|
|
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;
|
|
|
|
|
|
2025-04-28 00:49:13 +00:00
|
|
|
scriptOnNewDataBlock(reload);
|
2012-09-19 15:15:01 +00:00
|
|
|
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)
|
2020-06-13 17:12:52 +00:00
|
|
|
mDataBlock->onLeaveTrigger_callback( this, NULL );
|
2012-09-19 15:15:01 +00:00
|
|
|
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);
|
|
|
|
|
|
2023-04-27 03:27:35 +00:00
|
|
|
setMaskBits((U32)TransformMask | (U32)ScaleMask);
|
2012-09-19 15:15:01 +00:00
|
|
|
}
|
2020-06-13 17:12:52 +00:00
|
|
|
|
|
|
|
|
testObjects();
|
2012-09-19 15:15:01 +00:00
|
|
|
}
|
|
|
|
|
|
2020-07-10 20:50:25 +00:00
|
|
|
void Trigger::onUnmount( SceneObject *obj, S32 node )
|
|
|
|
|
{
|
|
|
|
|
Parent::onUnmount( obj, node );
|
|
|
|
|
// Make sure the client get's the final server pos.
|
2023-04-27 03:27:35 +00:00
|
|
|
setMaskBits((U32)TransformMask | (U32)ScaleMask);
|
2020-07-10 20:50:25 +00:00
|
|
|
}
|
|
|
|
|
|
2012-09-19 15:15:01 +00:00
|
|
|
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;
|
|
|
|
|
|
2018-03-14 20:18:00 +00:00
|
|
|
if (mTriggerPolyhedron.mPointList.size() != 0) {
|
2012-09-19 15:15:01 +00:00
|
|
|
mObjBox.minExtents.set(1e10, 1e10, 1e10);
|
|
|
|
|
mObjBox.maxExtents.set(-1e10, -1e10, -1e10);
|
2018-03-14 20:18:00 +00:00
|
|
|
for (U32 i = 0; i < mTriggerPolyhedron.mPointList.size(); i++) {
|
|
|
|
|
mObjBox.minExtents.setMin(mTriggerPolyhedron.mPointList[i]);
|
|
|
|
|
mObjBox.maxExtents.setMax(mTriggerPolyhedron.mPointList[i]);
|
2012-09-19 15:15:01 +00:00
|
|
|
}
|
|
|
|
|
} 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();
|
2018-03-14 20:18:00 +00:00
|
|
|
mClippedList.mPlaneList = mTriggerPolyhedron.mPlaneList;
|
2012-09-19 15:15:01 +00:00
|
|
|
// 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)
|
|
|
|
|
{
|
2018-03-14 20:18:00 +00:00
|
|
|
if (mTriggerPolyhedron.mPointList.size() == 0)
|
2012-09-19 15:15:01 +00:00
|
|
|
return false;
|
|
|
|
|
|
2020-09-05 00:29:50 +00:00
|
|
|
if (!(enter->getTypeMask() & mTrippedBy))
|
|
|
|
|
return false; //not the right type of object
|
|
|
|
|
|
2012-09-19 15:15:01 +00:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-05 00:29:50 +00:00
|
|
|
bool Trigger::testTrippable()
|
|
|
|
|
{
|
|
|
|
|
if ((mTripOnce == true) && (mTripped == true))
|
|
|
|
|
return false; // we've already fired the once
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Trigger::testCondition()
|
|
|
|
|
{
|
2024-10-21 05:08:07 +00:00
|
|
|
if (mTripIf.isEmpty())
|
2020-09-05 00:29:50 +00:00
|
|
|
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);
|
2024-10-21 05:08:07 +00:00
|
|
|
String command = resVar + "=" + mTripIf + ";";
|
2020-09-05 00:29:50 +00:00
|
|
|
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;
|
|
|
|
|
}
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
2020-09-05 00:29:50 +00:00
|
|
|
if(evalCmD(&mEnterCommand))
|
2012-09-19 15:15:01 +00:00
|
|
|
{
|
2021-11-07 20:52:25 +00:00
|
|
|
String command = String("%obj = ") + enter->getIdString() + ";";
|
|
|
|
|
command = command + String("%this = ") + getIdString() + ";" + mEnterCommand;
|
2012-09-19 15:15:01 +00:00
|
|
|
Con::evaluate(command.c_str());
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-05 00:29:50 +00:00
|
|
|
if( mDataBlock && testTrippable() && testCondition())
|
2012-09-19 15:15:01 +00:00
|
|
|
mDataBlock->onEnterTrigger_callback( this, enter );
|
2020-09-05 00:29:50 +00:00
|
|
|
mTripped = true;
|
2012-09-19 15:15:01 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Trigger::processTick(const Move* move)
|
|
|
|
|
{
|
|
|
|
|
Parent::processTick(move);
|
|
|
|
|
|
|
|
|
|
if (!mDataBlock)
|
|
|
|
|
return;
|
|
|
|
|
if (mDataBlock->isClientSide && isServerObject())
|
|
|
|
|
return;
|
|
|
|
|
if (!mDataBlock->isClientSide && isClientObject())
|
|
|
|
|
return;
|
|
|
|
|
|
2020-07-10 20:50:25 +00:00
|
|
|
if (isMounted()) {
|
|
|
|
|
MatrixF mat;
|
|
|
|
|
mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat );
|
|
|
|
|
setTransform(mat);
|
|
|
|
|
setRenderTransform(mat);
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-19 15:15:01 +00:00
|
|
|
//
|
|
|
|
|
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);
|
|
|
|
|
|
2020-09-05 00:29:50 +00:00
|
|
|
if (evalCmD(&mLeaveCommand))
|
2012-09-19 15:15:01 +00:00
|
|
|
{
|
2021-11-07 20:52:25 +00:00
|
|
|
String command = String("%obj = ") + remove->getIdString() + ";";
|
|
|
|
|
command = command + String("%this = ") + getIdString() + ";" + mLeaveCommand;
|
2012-09-19 15:15:01 +00:00
|
|
|
Con::evaluate(command.c_str());
|
|
|
|
|
}
|
2020-09-05 00:29:50 +00:00
|
|
|
if (testTrippable() && testCondition())
|
|
|
|
|
mDataBlock->onLeaveTrigger_callback( this, remove );
|
|
|
|
|
mTripped = true;
|
2012-09-19 15:15:01 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-05 00:29:50 +00:00
|
|
|
if (evalCmD(&mTickCommand))
|
2012-09-19 15:15:01 +00:00
|
|
|
Con::evaluate(mTickCommand.c_str());
|
|
|
|
|
|
2020-09-05 00:29:50 +00:00
|
|
|
if (mObjects.size() != 0 && testTrippable() && testCondition())
|
2012-09-19 15:15:01 +00:00
|
|
|
mDataBlock->onTickTrigger_callback( this );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
mCurrTick += TickMs;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-10 20:50:25 +00:00
|
|
|
void Trigger::interpolateTick(F32 delta)
|
|
|
|
|
{
|
|
|
|
|
if (isMounted()) {
|
|
|
|
|
MatrixF mat;
|
|
|
|
|
mMount.object->getRenderMountTransform( delta, mMount.node, mMount.xfm, &mat );
|
|
|
|
|
setRenderTransform(mat);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-19 15:15:01 +00:00
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
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 ) )
|
|
|
|
|
{
|
2018-03-14 20:18:00 +00:00
|
|
|
stream->write(mTriggerPolyhedron.mPointList.size());
|
|
|
|
|
for (i = 0; i < mTriggerPolyhedron.mPointList.size(); i++)
|
|
|
|
|
mathWrite(*stream, mTriggerPolyhedron.mPointList[i]);
|
2012-09-19 15:15:01 +00:00
|
|
|
|
2018-03-14 20:18:00 +00:00
|
|
|
stream->write(mTriggerPolyhedron.mPlaneList.size());
|
|
|
|
|
for (i = 0; i < mTriggerPolyhedron.mPlaneList.size(); i++)
|
|
|
|
|
mathWrite(*stream, mTriggerPolyhedron.mPlaneList[i]);
|
2012-09-19 15:15:01 +00:00
|
|
|
|
2018-03-14 20:18:00 +00:00
|
|
|
stream->write(mTriggerPolyhedron.mEdgeList.size());
|
|
|
|
|
for (i = 0; i < mTriggerPolyhedron.mEdgeList.size(); i++) {
|
|
|
|
|
const Polyhedron::Edge& rEdge = mTriggerPolyhedron.mEdgeList[i];
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
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);
|
2018-03-14 20:18:00 +00:00
|
|
|
tempPH.mPointList.setSize(size);
|
|
|
|
|
for (i = 0; i < tempPH.mPointList.size(); i++)
|
|
|
|
|
mathRead(*stream, &tempPH.mPointList[i]);
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
stream->read(&size);
|
2018-03-14 20:18:00 +00:00
|
|
|
tempPH.mPlaneList.setSize(size);
|
|
|
|
|
for (i = 0; i < tempPH.mPlaneList.size(); i++)
|
|
|
|
|
mathRead(*stream, &tempPH.mPlaneList[i]);
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
stream->read(&size);
|
2018-03-14 20:18:00 +00:00
|
|
|
tempPH.mEdgeList.setSize(size);
|
|
|
|
|
for (i = 0; i < tempPH.mEdgeList.size(); i++) {
|
|
|
|
|
Polyhedron::Edge& rEdge = tempPH.mEdgeList[i];
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|