mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-04-29 08:15:44 +00:00
Other renames to ensure linux case-sensitivity compliance and casing format consistency.
This commit is contained in:
parent
93e767f0c5
commit
f5e86a83b5
33 changed files with 0 additions and 0 deletions
582
Engine/source/T3D/components/collision/collisionComponent.cpp
Normal file
582
Engine/source/T3D/components/collision/collisionComponent.cpp
Normal file
|
|
@ -0,0 +1,582 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "T3D/components/collision/collisioncomponent.h"
|
||||
#include "T3D/components/collision/collisionComponent_ScriptBinding.h"
|
||||
#include "T3D/components/physics/physicsBehavior.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
#include "core/resourceManager.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "console/consoleObject.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "gfx/gfxTransformSaver.h"
|
||||
#include "gfx/gfxDrawUtil.h"
|
||||
#include "console/engineAPI.h"
|
||||
#include "T3D/physics/physicsPlugin.h"
|
||||
#include "T3D/physics/physicsBody.h"
|
||||
#include "T3D/physics/physicsCollision.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "collision/extrudedPolyList.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "gfx/sim/debugDraw.h"
|
||||
#include "collision/concretePolyList.h"
|
||||
|
||||
#include "T3D/trigger.h"
|
||||
#include "opcode/Opcode.h"
|
||||
#include "opcode/Ice/IceAABB.h"
|
||||
#include "opcode/Ice/IcePoint.h"
|
||||
#include "opcode/OPC_AABBTree.h"
|
||||
#include "opcode/OPC_AABBCollider.h"
|
||||
|
||||
#include "math/mathUtils.h"
|
||||
#include "materials/baseMatInstance.h"
|
||||
#include "collision/vertexPolyList.h"
|
||||
|
||||
extern bool gEditingMission;
|
||||
|
||||
static bool sRenderColliders = false;
|
||||
|
||||
//Docs
|
||||
ConsoleDocClass(CollisionComponent,
|
||||
"@brief The Box Collider component uses a box or rectangular convex shape for collisions.\n\n"
|
||||
|
||||
"Colliders are individualized components that are similarly based off the CollisionInterface core.\n"
|
||||
"They are basically the entire functionality of how Torque handles collisions compacted into a single component.\n"
|
||||
"A collider will both collide against and be collided with, other entities.\n"
|
||||
"Individual colliders will offer different shapes. This box collider will generate a box/rectangle convex, \n"
|
||||
"while the mesh collider will take the owner Entity's rendered shape and do polysoup collision on it, etc.\n\n"
|
||||
|
||||
"The general flow of operations for how collisions happen is thus:\n"
|
||||
" -When the component is added(or updated) prepCollision() is called.\n"
|
||||
" This will set up our initial convex shape for usage later.\n\n"
|
||||
|
||||
" -When we update via processTick(), we first test if our entity owner is mobile.\n"
|
||||
" If our owner isn't mobile(as in, they have no components that provide it a velocity to move)\n"
|
||||
" then we skip doing our active collision checks. Collisions are checked by the things moving, as\n"
|
||||
" opposed to being reactionary. If we're moving, we call updateWorkingCollisionSet().\n"
|
||||
" updateWorkingCollisionSet() estimates our bounding space for our current ticket based on our position and velocity.\n"
|
||||
" If our bounding space has changed since the last tick, we proceed to call updateWorkingList() on our convex.\n"
|
||||
" This notifies any object in the bounding space that they may be collided with, so they will call buildConvex().\n"
|
||||
" buildConvex() will set up our ConvexList with our collision convex info.\n\n"
|
||||
|
||||
" -When the component that is actually causing our movement, such as SimplePhysicsBehavior, updates, it will check collisions.\n"
|
||||
" It will call checkCollisions() on us. checkCollisions() will first build a bounding shape for our convex, and test\n"
|
||||
" if we can early out because we won't hit anything based on our starting point, velocity, and tick time.\n"
|
||||
" If we don't early out, we proceed to call updateCollisions(). This builds an ExtrudePolyList, which is then extruded\n"
|
||||
" based on our velocity. We then test our extruded polies on our working list of objects we build\n"
|
||||
" up earlier via updateWorkingCollisionSet. Any collisions that happen here will be added to our mCollisionList.\n"
|
||||
" Finally, we call handleCollisionList() on our collisionList, which then queues out the colliison notice\n"
|
||||
" to the object(s) we collided with so they can do callbacks and the like. We also report back on if we did collide\n"
|
||||
" to the physics component via our bool return in checkCollisions() so it can make the physics react accordingly.\n\n"
|
||||
|
||||
"One interesting point to note is the usage of mBlockColliding.\n"
|
||||
"This is set so that it dictates the return on checkCollisions(). If set to false, it will ensure checkCollisions()\n"
|
||||
"will return false, regardless if we actually collided. This is useful, because even if checkCollisions() returns false,\n"
|
||||
"we still handle the collisions so the callbacks happen. This enables us to apply a collider to an object that doesn't block\n"
|
||||
"objects, but does have callbacks, so it can act as a trigger, allowing for arbitrarily shaped triggers, as any collider can\n"
|
||||
"act as a trigger volume(including MeshCollider).\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"new CollisionComponentInstance()\n"
|
||||
"{\n"
|
||||
" template = CollisionComponentTemplate;\n"
|
||||
" colliderSize = \"1 1 2\";\n"
|
||||
" blockColldingObject = \"1\";\n"
|
||||
"};\n"
|
||||
"@endtsexample\n"
|
||||
|
||||
"@see SimplePhysicsBehavior\n"
|
||||
"@ingroup Collision\n"
|
||||
"@ingroup Components\n"
|
||||
);
|
||||
//Docs
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
ImplementEnumType(CollisionMeshMeshType,
|
||||
"Type of mesh data available in a shape.\n"
|
||||
"@ingroup gameObjects")
|
||||
{ CollisionComponent::None, "None", "No mesh data." },
|
||||
{ CollisionComponent::Bounds, "Bounds", "Bounding box of the shape." },
|
||||
{ CollisionComponent::CollisionMesh, "Collision Mesh", "Specifically desingated \"collision\" meshes." },
|
||||
{ CollisionComponent::VisibleMesh, "Visible Mesh", "Rendered mesh polygons." },
|
||||
EndImplementEnumType;
|
||||
|
||||
//
|
||||
CollisionComponent::CollisionComponent() : Component()
|
||||
{
|
||||
mNetFlags.set(Ghostable | ScopeAlways);
|
||||
|
||||
mFriendlyName = "Collision(Component)";
|
||||
|
||||
mOwnerRenderInterface = NULL;
|
||||
mOwnerPhysicsInterface = NULL;
|
||||
|
||||
mBlockColliding = true;
|
||||
|
||||
mCollisionType = CollisionMesh;
|
||||
mLOSType = CollisionMesh;
|
||||
mDecalType = CollisionMesh;
|
||||
|
||||
colisionMeshPrefix = StringTable->insert("Collision");
|
||||
|
||||
CollisionMoveMask = (TerrainObjectType | PlayerObjectType |
|
||||
StaticShapeObjectType | VehicleObjectType |
|
||||
VehicleBlockerObjectType | DynamicShapeObjectType | StaticObjectType | EntityObjectType | TriggerObjectType);
|
||||
|
||||
mPhysicsRep = NULL;
|
||||
mPhysicsWorld = NULL;
|
||||
|
||||
mTimeoutList = NULL;
|
||||
}
|
||||
|
||||
CollisionComponent::~CollisionComponent()
|
||||
{
|
||||
for (S32 i = 0; i < mFields.size(); ++i)
|
||||
{
|
||||
ComponentField &field = mFields[i];
|
||||
SAFE_DELETE_ARRAY(field.mFieldDescription);
|
||||
}
|
||||
|
||||
SAFE_DELETE_ARRAY(mDescription);
|
||||
}
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(CollisionComponent);
|
||||
|
||||
void CollisionComponent::onComponentAdd()
|
||||
{
|
||||
Parent::onComponentAdd();
|
||||
|
||||
RenderComponentInterface *renderInterface = mOwner->getComponent<RenderComponentInterface>();
|
||||
if (renderInterface)
|
||||
{
|
||||
renderInterface->onShapeInstanceChanged.notify(this, &CollisionComponent::targetShapeChanged);
|
||||
mOwnerRenderInterface = renderInterface;
|
||||
}
|
||||
|
||||
//physicsInterface
|
||||
PhysicsComponentInterface *physicsInterface = mOwner->getComponent<PhysicsComponentInterface>();
|
||||
if (!physicsInterface)
|
||||
{
|
||||
mPhysicsRep = PHYSICSMGR->createBody();
|
||||
}
|
||||
|
||||
prepCollision();
|
||||
}
|
||||
|
||||
void CollisionComponent::onComponentRemove()
|
||||
{
|
||||
SAFE_DELETE(mPhysicsRep);
|
||||
|
||||
Parent::onComponentRemove();
|
||||
}
|
||||
|
||||
void CollisionComponent::componentAddedToOwner(Component *comp)
|
||||
{
|
||||
if (comp->getId() == getId())
|
||||
return;
|
||||
|
||||
//test if this is a shape component!
|
||||
RenderComponentInterface *renderInterface = dynamic_cast<RenderComponentInterface*>(comp);
|
||||
if (renderInterface)
|
||||
{
|
||||
renderInterface->onShapeInstanceChanged.notify(this, &CollisionComponent::targetShapeChanged);
|
||||
mOwnerRenderInterface = renderInterface;
|
||||
prepCollision();
|
||||
}
|
||||
|
||||
PhysicsComponentInterface *physicsInterface = dynamic_cast<PhysicsComponentInterface*>(comp);
|
||||
if (physicsInterface)
|
||||
{
|
||||
if (mPhysicsRep)
|
||||
SAFE_DELETE(mPhysicsRep);
|
||||
|
||||
prepCollision();
|
||||
}
|
||||
}
|
||||
|
||||
void CollisionComponent::componentRemovedFromOwner(Component *comp)
|
||||
{
|
||||
if (comp->getId() == getId()) //?????????
|
||||
return;
|
||||
|
||||
//test if this is a shape component!
|
||||
RenderComponentInterface *renderInterface = dynamic_cast<RenderComponentInterface*>(comp);
|
||||
if (renderInterface)
|
||||
{
|
||||
renderInterface->onShapeInstanceChanged.remove(this, &CollisionComponent::targetShapeChanged);
|
||||
mOwnerRenderInterface = NULL;
|
||||
prepCollision();
|
||||
}
|
||||
|
||||
//physicsInterface
|
||||
PhysicsComponentInterface *physicsInterface = dynamic_cast<PhysicsComponentInterface*>(comp);
|
||||
if (physicsInterface)
|
||||
{
|
||||
mPhysicsRep = PHYSICSMGR->createBody();
|
||||
|
||||
prepCollision();
|
||||
}
|
||||
}
|
||||
|
||||
void CollisionComponent::checkDependencies()
|
||||
{
|
||||
}
|
||||
|
||||
void CollisionComponent::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addGroup("Collision");
|
||||
|
||||
addField("CollisionType", TypeCollisionMeshMeshType, Offset(mCollisionType, CollisionComponent),
|
||||
"The type of mesh data to use for collision queries.");
|
||||
|
||||
addField("LineOfSightType", TypeCollisionMeshMeshType, Offset(mLOSType, CollisionComponent),
|
||||
"The type of mesh data to use for collision queries.");
|
||||
|
||||
addField("DecalType", TypeCollisionMeshMeshType, Offset(mDecalType, CollisionComponent),
|
||||
"The type of mesh data to use for collision queries.");
|
||||
|
||||
addField("CollisionMeshPrefix", TypeString, Offset(colisionMeshPrefix, CollisionComponent),
|
||||
"The type of mesh data to use for collision queries.");
|
||||
|
||||
addField("BlockCollisions", TypeBool, Offset(mBlockColliding, CollisionComponent), "");
|
||||
|
||||
endGroup("Collision");
|
||||
}
|
||||
|
||||
void CollisionComponent::inspectPostApply()
|
||||
{
|
||||
// Apply any transformations set in the editor
|
||||
Parent::inspectPostApply();
|
||||
|
||||
if (isServerObject())
|
||||
{
|
||||
setMaskBits(ColliderMask);
|
||||
prepCollision();
|
||||
}
|
||||
}
|
||||
|
||||
U32 CollisionComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||
|
||||
if (stream->writeFlag(mask & (ColliderMask | InitialUpdateMask)))
|
||||
{
|
||||
stream->write((U32)mCollisionType);
|
||||
stream->writeString(colisionMeshPrefix);
|
||||
}
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void CollisionComponent::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||
{
|
||||
Parent::unpackUpdate(con, stream);
|
||||
|
||||
if (stream->readFlag()) // UpdateMask
|
||||
{
|
||||
U32 collisionType = CollisionMesh;
|
||||
|
||||
stream->read(&collisionType);
|
||||
|
||||
// Handle it if we have changed CollisionType's
|
||||
if ((MeshType)collisionType != mCollisionType)
|
||||
{
|
||||
mCollisionType = (MeshType)collisionType;
|
||||
|
||||
prepCollision();
|
||||
}
|
||||
|
||||
char readBuffer[1024];
|
||||
|
||||
stream->readString(readBuffer);
|
||||
colisionMeshPrefix = StringTable->insert(readBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void CollisionComponent::ownerTransformSet(MatrixF *mat)
|
||||
{
|
||||
if (mPhysicsRep)
|
||||
mPhysicsRep->setTransform(mOwner->getTransform());
|
||||
}
|
||||
|
||||
void CollisionComponent::targetShapeChanged(RenderComponentInterface* instanceInterface)
|
||||
{
|
||||
prepCollision();
|
||||
}
|
||||
|
||||
void CollisionComponent::prepCollision()
|
||||
{
|
||||
if (!mOwner)
|
||||
return;
|
||||
|
||||
// Let the client know that the collision was updated
|
||||
setMaskBits(ColliderMask);
|
||||
|
||||
mOwner->disableCollision();
|
||||
|
||||
if ((!PHYSICSMGR || mCollisionType == None) ||
|
||||
(mOwnerRenderInterface == NULL && (mCollisionType == CollisionMesh || mCollisionType == VisibleMesh)))
|
||||
return;
|
||||
|
||||
PhysicsCollision *colShape = NULL;
|
||||
|
||||
if (mCollisionType == Bounds)
|
||||
{
|
||||
MatrixF offset(true);
|
||||
|
||||
if (mOwnerRenderInterface && mOwnerRenderInterface->getShape())
|
||||
offset.setPosition(mOwnerRenderInterface->getShape()->center);
|
||||
|
||||
colShape = PHYSICSMGR->createCollision();
|
||||
colShape->addBox(mOwner->getObjBox().getExtents() * 0.5f * mOwner->getScale(), offset);
|
||||
}
|
||||
else if (mCollisionType == CollisionMesh || (mCollisionType == VisibleMesh /*&& !mOwner->getComponent<AnimatedMesh>()*/))
|
||||
{
|
||||
colShape = buildColShapes();
|
||||
}
|
||||
|
||||
if (colShape)
|
||||
{
|
||||
mPhysicsWorld = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
|
||||
|
||||
if (mPhysicsRep)
|
||||
{
|
||||
if (mBlockColliding)
|
||||
mPhysicsRep->init(colShape, 0, 0, mOwner, mPhysicsWorld);
|
||||
else
|
||||
mPhysicsRep->init(colShape, 0, PhysicsBody::BF_TRIGGER, mOwner, mPhysicsWorld);
|
||||
|
||||
mPhysicsRep->setTransform(mOwner->getTransform());
|
||||
}
|
||||
}
|
||||
|
||||
mOwner->enableCollision();
|
||||
|
||||
onCollisionChanged.trigger(colShape);
|
||||
}
|
||||
|
||||
void CollisionComponent::processTick()
|
||||
{
|
||||
if (!isActive())
|
||||
return;
|
||||
|
||||
//ProcessTick is where our collision testing begins!
|
||||
|
||||
//callback if we have a persisting contact
|
||||
if (mContactInfo.contactObject)
|
||||
{
|
||||
if (mContactInfo.contactTimer > 0)
|
||||
{
|
||||
if (isMethod("updateContact"))
|
||||
Con::executef(this, "updateContact");
|
||||
|
||||
if (mOwner->isMethod("updateContact"))
|
||||
Con::executef(mOwner, "updateContact");
|
||||
}
|
||||
|
||||
++mContactInfo.contactTimer;
|
||||
}
|
||||
else if (mContactInfo.contactTimer != 0)
|
||||
mContactInfo.clear();
|
||||
}
|
||||
|
||||
void CollisionComponent::updatePhysics()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PhysicsCollision* CollisionComponent::getCollisionData()
|
||||
{
|
||||
if ((!PHYSICSMGR || mCollisionType == None) || mOwnerRenderInterface == NULL)
|
||||
return NULL;
|
||||
|
||||
PhysicsCollision *colShape = NULL;
|
||||
if (mCollisionType == Bounds)
|
||||
{
|
||||
MatrixF offset(true);
|
||||
offset.setPosition(mOwnerRenderInterface->getShape()->center);
|
||||
colShape = PHYSICSMGR->createCollision();
|
||||
colShape->addBox(mOwner->getObjBox().getExtents() * 0.5f * mOwner->getScale(), offset);
|
||||
}
|
||||
else if (mCollisionType == CollisionMesh || (mCollisionType == VisibleMesh/* && !mOwner->getComponent<AnimatedMesh>()*/))
|
||||
{
|
||||
colShape = buildColShapes();
|
||||
//colShape = mOwnerShapeInstance->getShape()->buildColShape(mCollisionType == VisibleMesh, mOwner->getScale());
|
||||
}
|
||||
/*else if (mCollisionType == VisibleMesh && !mOwner->getComponent<AnimatedMesh>())
|
||||
{
|
||||
//We don't have support for visible mesh collisions with animated meshes currently in the physics abstraction layer
|
||||
//so we don't generate anything if we're set to use a visible mesh but have an animated mesh component.
|
||||
colShape = mOwnerShapeInstance->getShape()->buildColShape(mCollisionType == VisibleMesh, mOwner->getScale());
|
||||
}*/
|
||||
else if (mCollisionType == VisibleMesh/* && mOwner->getComponent<AnimatedMesh>()*/)
|
||||
{
|
||||
Con::printf("CollisionComponent::updatePhysics: Cannot use visible mesh collisions with an animated mesh!");
|
||||
}
|
||||
|
||||
return colShape;
|
||||
}
|
||||
|
||||
bool CollisionComponent::castRay(const Point3F &start, const Point3F &end, RayInfo* info)
|
||||
{
|
||||
if (!mCollisionType == None)
|
||||
{
|
||||
if (mPhysicsWorld)
|
||||
{
|
||||
return mPhysicsWorld->castRay(start, end, info, Point3F::Zero);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
PhysicsCollision* CollisionComponent::buildColShapes()
|
||||
{
|
||||
PROFILE_SCOPE(CollisionComponent_buildColShapes);
|
||||
|
||||
PhysicsCollision *colShape = NULL;
|
||||
U32 surfaceKey = 0;
|
||||
|
||||
TSShape* shape = mOwnerRenderInterface->getShape();
|
||||
|
||||
if (mCollisionType == VisibleMesh)
|
||||
{
|
||||
// Here we build triangle collision meshes from the
|
||||
// visible detail levels.
|
||||
|
||||
// A negative subshape on the detail means we don't have geometry.
|
||||
const TSShape::Detail &detail = shape->details[0];
|
||||
if (detail.subShapeNum < 0)
|
||||
return NULL;
|
||||
|
||||
// We don't try to optimize the triangles we're given
|
||||
// and assume the art was created properly for collision.
|
||||
ConcretePolyList polyList;
|
||||
polyList.setTransform(&MatrixF::Identity, mOwner->getScale());
|
||||
|
||||
// Create the collision meshes.
|
||||
S32 start = shape->subShapeFirstObject[detail.subShapeNum];
|
||||
S32 end = start + shape->subShapeNumObjects[detail.subShapeNum];
|
||||
for (S32 o = start; o < end; o++)
|
||||
{
|
||||
const TSShape::Object &object = shape->objects[o];
|
||||
if (detail.objectDetailNum >= object.numMeshes)
|
||||
continue;
|
||||
|
||||
// No mesh or no verts.... nothing to do.
|
||||
TSMesh *mesh = shape->meshes[object.startMeshIndex + detail.objectDetailNum];
|
||||
if (!mesh || mesh->mNumVerts == 0)
|
||||
continue;
|
||||
|
||||
// Gather the mesh triangles.
|
||||
polyList.clear();
|
||||
mesh->buildPolyList(0, &polyList, surfaceKey, NULL);
|
||||
|
||||
// Create the collision shape if we haven't already.
|
||||
if (!colShape)
|
||||
colShape = PHYSICSMGR->createCollision();
|
||||
|
||||
// Get the object space mesh transform.
|
||||
MatrixF localXfm;
|
||||
shape->getNodeWorldTransform(object.nodeIndex, &localXfm);
|
||||
|
||||
colShape->addTriangleMesh(polyList.mVertexList.address(),
|
||||
polyList.mVertexList.size(),
|
||||
polyList.mIndexList.address(),
|
||||
polyList.mIndexList.size() / 3,
|
||||
localXfm);
|
||||
}
|
||||
|
||||
// Return what we built... if anything.
|
||||
return colShape;
|
||||
}
|
||||
else if (mCollisionType == CollisionMesh)
|
||||
{
|
||||
|
||||
// Scan out the collision hulls...
|
||||
//
|
||||
// TODO: We need to support LOS collision for physics.
|
||||
//
|
||||
for (U32 i = 0; i < shape->details.size(); i++)
|
||||
{
|
||||
const TSShape::Detail &detail = shape->details[i];
|
||||
const String &name = shape->names[detail.nameIndex];
|
||||
|
||||
// Is this a valid collision detail.
|
||||
if (!dStrStartsWith(name, colisionMeshPrefix) || detail.subShapeNum < 0)
|
||||
continue;
|
||||
|
||||
// Now go thru the meshes for this detail.
|
||||
S32 start = shape->subShapeFirstObject[detail.subShapeNum];
|
||||
S32 end = start + shape->subShapeNumObjects[detail.subShapeNum];
|
||||
if (start >= end)
|
||||
continue;
|
||||
|
||||
for (S32 o = start; o < end; o++)
|
||||
{
|
||||
const TSShape::Object &object = shape->objects[o];
|
||||
const String &meshName = shape->names[object.nameIndex];
|
||||
|
||||
if (object.numMeshes <= detail.objectDetailNum)
|
||||
continue;
|
||||
|
||||
// No mesh, a flat bounds, or no verts.... nothing to do.
|
||||
TSMesh *mesh = shape->meshes[object.startMeshIndex + detail.objectDetailNum];
|
||||
if (!mesh || mesh->getBounds().isEmpty() || mesh->mNumVerts == 0)
|
||||
continue;
|
||||
|
||||
// We need the default mesh transform.
|
||||
MatrixF localXfm;
|
||||
shape->getNodeWorldTransform(object.nodeIndex, &localXfm);
|
||||
|
||||
// We have some sort of collision shape... so allocate it.
|
||||
if (!colShape)
|
||||
colShape = PHYSICSMGR->createCollision();
|
||||
|
||||
// Any other mesh name we assume as a generic convex hull.
|
||||
//
|
||||
// Collect the verts using the vertex polylist which will
|
||||
// filter out duplicates. This is importaint as the convex
|
||||
// generators can sometimes fail with duplicate verts.
|
||||
//
|
||||
VertexPolyList polyList;
|
||||
MatrixF meshMat(localXfm);
|
||||
|
||||
Point3F t = meshMat.getPosition();
|
||||
t.convolve(mOwner->getScale());
|
||||
meshMat.setPosition(t);
|
||||
|
||||
polyList.setTransform(&MatrixF::Identity, mOwner->getScale());
|
||||
mesh->buildPolyList(0, &polyList, surfaceKey, NULL);
|
||||
colShape->addConvex(polyList.getVertexList().address(),
|
||||
polyList.getVertexList().size(),
|
||||
meshMat);
|
||||
} // objects
|
||||
} // details
|
||||
}
|
||||
|
||||
return colShape;
|
||||
}
|
||||
208
Engine/source/T3D/components/collision/collisionComponent.h
Normal file
208
Engine/source/T3D/components/collision/collisionComponent.h
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef COLLISION_COMPONENT_H
|
||||
#define COLLISION_COMPONENT_H
|
||||
|
||||
#ifndef __RESOURCE_H__
|
||||
#include "core/resource.h"
|
||||
#endif
|
||||
#ifndef _TSSHAPE_H_
|
||||
#include "ts/tsShape.h"
|
||||
#endif
|
||||
#ifndef _SCENERENDERSTATE_H_
|
||||
#include "scene/sceneRenderState.h"
|
||||
#endif
|
||||
#ifndef _MBOX_H_
|
||||
#include "math/mBox.h"
|
||||
#endif
|
||||
#ifndef ENTITY_H
|
||||
#include "T3D/entity.h"
|
||||
#endif
|
||||
#ifndef CORE_INTERFACES_H
|
||||
#include "T3D/components/coreInterfaces.h"
|
||||
#endif
|
||||
#ifndef COLLISION_INTERFACES_H
|
||||
#include "T3D/components/collision/collisionInterfaces.h"
|
||||
#endif
|
||||
#ifndef RENDER_COMPONENT_INTERFACE_H
|
||||
#include "T3D/components/render/renderComponentInterface.h"
|
||||
#endif
|
||||
#ifndef PHYSICS_COMPONENT_INTERFACE_H
|
||||
#include "T3D/components/physics/physicsComponentInterface.h"
|
||||
#endif
|
||||
#ifndef _T3D_PHYSICSCOMMON_H_
|
||||
#include "T3D/physics/physicsCommon.h"
|
||||
#endif
|
||||
#ifndef _T3D_PHYSICS_PHYSICSWORLD_H_
|
||||
#include "T3D/physics/physicsWorld.h"
|
||||
#endif
|
||||
|
||||
class TSShapeInstance;
|
||||
class SceneRenderState;
|
||||
class CollisionComponent;
|
||||
class PhysicsBody;
|
||||
class PhysicsWorld;
|
||||
|
||||
class CollisionComponent : public Component,
|
||||
public CollisionInterface,
|
||||
public CastRayInterface
|
||||
{
|
||||
typedef Component Parent;
|
||||
public:
|
||||
enum MeshType
|
||||
{
|
||||
None = 0, ///< No mesh
|
||||
Bounds = 1, ///< Bounding box of the shape
|
||||
CollisionMesh = 2, ///< Specifically designated collision meshes
|
||||
VisibleMesh = 3 ///< Rendered mesh polygons
|
||||
};
|
||||
|
||||
PhysicsWorld* mPhysicsWorld;
|
||||
PhysicsBody* mPhysicsRep;
|
||||
|
||||
protected:
|
||||
MeshType mCollisionType;
|
||||
MeshType mDecalType;
|
||||
MeshType mLOSType;
|
||||
|
||||
Vector<S32> mCollisionDetails;
|
||||
Vector<S32> mLOSDetails;
|
||||
|
||||
StringTableEntry colisionMeshPrefix;
|
||||
|
||||
RenderComponentInterface* mOwnerRenderInterface;
|
||||
|
||||
PhysicsComponentInterface* mOwnerPhysicsInterface;
|
||||
|
||||
//only really relevent for the collision mesh type
|
||||
//if we note an animation component is added, we flag as being animated.
|
||||
//This way, if we're using collision meshes, we can set it up to update their transforms
|
||||
//as needed
|
||||
bool mAnimated;
|
||||
|
||||
enum
|
||||
{
|
||||
ColliderMask = Parent::NextFreeMask,
|
||||
};
|
||||
|
||||
public:
|
||||
CollisionComponent();
|
||||
virtual ~CollisionComponent();
|
||||
DECLARE_CONOBJECT(CollisionComponent);
|
||||
|
||||
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||
|
||||
virtual void componentAddedToOwner(Component *comp);
|
||||
virtual void componentRemovedFromOwner(Component *comp);
|
||||
virtual void ownerTransformSet(MatrixF *mat);
|
||||
void targetShapeChanged(RenderComponentInterface* instanceInterface);
|
||||
|
||||
virtual void onComponentRemove();
|
||||
virtual void onComponentAdd();
|
||||
|
||||
virtual void checkDependencies();
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
void inspectPostApply();
|
||||
|
||||
virtual void processTick();
|
||||
|
||||
void prepCollision();
|
||||
|
||||
PhysicsCollision* buildColShapes();
|
||||
|
||||
void updatePhysics();
|
||||
|
||||
virtual bool castRay(const Point3F &start, const Point3F &end, RayInfo* info);
|
||||
|
||||
virtual bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere){ return false; }
|
||||
|
||||
virtual PhysicsCollision* getCollisionData();
|
||||
|
||||
//Utility functions, mostly for script
|
||||
Point3F getContactNormal() { return mContactInfo.contactNormal; }
|
||||
bool hasContact()
|
||||
{
|
||||
if (mContactInfo.contactObject)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
S32 getCollisionCount()
|
||||
{
|
||||
return mCollisionList.getCount();
|
||||
}
|
||||
|
||||
Point3F getCollisionNormal(S32 collisionIndex)
|
||||
{
|
||||
if (collisionIndex < 0 || mCollisionList.getCount() < collisionIndex)
|
||||
return Point3F::Zero;
|
||||
|
||||
return mCollisionList[collisionIndex].normal;
|
||||
}
|
||||
|
||||
F32 getCollisionAngle(S32 collisionIndex, Point3F upVector)
|
||||
{
|
||||
if (collisionIndex < 0 || mCollisionList.getCount() < collisionIndex)
|
||||
return 0.0f;
|
||||
|
||||
return mRadToDeg(mAcos(mDot(mCollisionList[collisionIndex].normal, upVector)));
|
||||
}
|
||||
|
||||
S32 getBestCollision(Point3F upVector)
|
||||
{
|
||||
S32 bestCollision = -1;
|
||||
|
||||
F32 bestAngle = 360.f;
|
||||
S32 count = mCollisionList.getCount();
|
||||
for (U32 i = 0; i < count; ++i)
|
||||
{
|
||||
F32 angle = mRadToDeg(mAcos(mDot(mCollisionList[i].normal, upVector)));
|
||||
|
||||
if (angle < bestAngle)
|
||||
{
|
||||
bestCollision = i;
|
||||
bestAngle = angle;
|
||||
}
|
||||
}
|
||||
|
||||
return bestCollision;
|
||||
}
|
||||
|
||||
F32 getBestCollisionAngle(VectorF upVector)
|
||||
{
|
||||
S32 bestCol = getBestCollision(upVector);
|
||||
|
||||
if (bestCol == -1)
|
||||
return 0;
|
||||
|
||||
return getCollisionAngle(bestCol, upVector);
|
||||
}
|
||||
};
|
||||
|
||||
typedef CollisionComponent::MeshType CollisionMeshMeshType;
|
||||
DefineEnumType(CollisionMeshMeshType);
|
||||
|
||||
#endif // COLLISION_COMPONENT_H
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "console/engineAPI.h"
|
||||
#include "T3D/components/collision/collisioncomponent.h"
|
||||
#include "materials/baseMatInstance.h"
|
||||
|
||||
DefineConsoleMethod(CollisionComponent, getNumberOfContacts, S32, (), ,
|
||||
"Gets the number of contacts this collider has hit.\n"
|
||||
"@return The number of static fields defined on the object.")
|
||||
{
|
||||
return object->getCollisionList()->getCount();
|
||||
}
|
||||
|
||||
DefineConsoleMethod(CollisionComponent, getBestContact, S32, (), ,
|
||||
"Gets the number of contacts this collider has hit.\n"
|
||||
"@return The number of static fields defined on the object.")
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DefineConsoleMethod(CollisionComponent, getContactNormal, Point3F, (), ,
|
||||
"Gets the number of contacts this collider has hit.\n"
|
||||
"@return The number of static fields defined on the object.")
|
||||
{
|
||||
if (object->getContactInfo())
|
||||
{
|
||||
if (object->getContactInfo()->contactObject)
|
||||
{
|
||||
return object->getContactInfo()->contactNormal;
|
||||
}
|
||||
}
|
||||
|
||||
return Point3F::Zero;
|
||||
}
|
||||
|
||||
DefineConsoleMethod(CollisionComponent, getContactMaterial, S32, (), ,
|
||||
"Gets the number of contacts this collider has hit.\n"
|
||||
"@return The number of static fields defined on the object.")
|
||||
{
|
||||
if (object->getContactInfo())
|
||||
{
|
||||
if (object->getContactInfo()->contactObject)
|
||||
{
|
||||
if (object->getContactInfo()->contactMaterial != NULL)
|
||||
return object->getContactInfo()->contactMaterial->getMaterial()->getId();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DefineConsoleMethod(CollisionComponent, getContactObject, S32, (), ,
|
||||
"Gets the number of contacts this collider has hit.\n"
|
||||
"@return The number of static fields defined on the object.")
|
||||
{
|
||||
if (object->getContactInfo())
|
||||
{
|
||||
return object->getContactInfo()->contactObject != NULL ? object->getContactInfo()->contactObject->getId() : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DefineConsoleMethod(CollisionComponent, getContactPoint, Point3F, (), ,
|
||||
"Gets the number of contacts this collider has hit.\n"
|
||||
"@return The number of static fields defined on the object.")
|
||||
{
|
||||
if (object->getContactInfo())
|
||||
{
|
||||
if (object->getContactInfo()->contactObject)
|
||||
{
|
||||
return object->getContactInfo()->contactPoint;
|
||||
}
|
||||
}
|
||||
|
||||
return Point3F::Zero;
|
||||
}
|
||||
|
||||
DefineConsoleMethod(CollisionComponent, getContactTime, S32, (), ,
|
||||
"Gets the number of contacts this collider has hit.\n"
|
||||
"@return The number of static fields defined on the object.")
|
||||
{
|
||||
if (object->getContactInfo())
|
||||
{
|
||||
if (object->getContactInfo()->contactObject)
|
||||
{
|
||||
return object->getContactInfo()->contactTimer;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DefineEngineMethod(CollisionComponent, hasContact, bool, (), ,
|
||||
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||
|
||||
"@param pos impulse world position\n"
|
||||
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||
"@return Always true\n"
|
||||
|
||||
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||
{
|
||||
return object->hasContact();
|
||||
}
|
||||
|
||||
DefineEngineMethod(CollisionComponent, getCollisionCount, S32, (), ,
|
||||
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||
|
||||
"@param pos impulse world position\n"
|
||||
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||
"@return Always true\n"
|
||||
|
||||
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||
{
|
||||
return object->getCollisionCount();
|
||||
}
|
||||
|
||||
DefineEngineMethod(CollisionComponent, getCollisionNormal, Point3F, (S32 collisionIndex), ,
|
||||
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||
|
||||
"@param pos impulse world position\n"
|
||||
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||
"@return Always true\n"
|
||||
|
||||
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||
{
|
||||
return object->getCollisionNormal(collisionIndex);
|
||||
}
|
||||
|
||||
DefineEngineMethod(CollisionComponent, getCollisionAngle, F32, (S32 collisionIndex, VectorF upVector), ,
|
||||
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||
|
||||
"@param pos impulse world position\n"
|
||||
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||
"@return Always true\n"
|
||||
|
||||
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||
{
|
||||
return object->getCollisionAngle(collisionIndex, upVector);
|
||||
}
|
||||
|
||||
DefineEngineMethod(CollisionComponent, getBestCollisionAngle, F32, (VectorF upVector), ,
|
||||
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||
|
||||
"@param pos impulse world position\n"
|
||||
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||
"@return Always true\n"
|
||||
|
||||
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||
{
|
||||
return object->getBestCollisionAngle(upVector);
|
||||
}
|
||||
258
Engine/source/T3D/components/collision/collisionInterfaces.cpp
Normal file
258
Engine/source/T3D/components/collision/collisionInterfaces.cpp
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "T3D/components/collision/collisionInterfaces.h"
|
||||
#include "scene/sceneObject.h"
|
||||
#include "T3D/entity.h"
|
||||
#include "console/engineAPI.h"
|
||||
#include "T3D/trigger.h"
|
||||
#include "materials/baseMatInstance.h"
|
||||
|
||||
void CollisionInterface::handleCollisionList( CollisionList &collisionList, VectorF velocity )
|
||||
{
|
||||
Collision bestCol;
|
||||
|
||||
mCollisionList = collisionList;
|
||||
|
||||
for (U32 i=0; i < collisionList.getCount(); ++i)
|
||||
{
|
||||
Collision& colCheck = collisionList[i];
|
||||
|
||||
if (colCheck.object)
|
||||
{
|
||||
if (colCheck.object->getTypeMask() & PlayerObjectType)
|
||||
{
|
||||
handleCollision( colCheck, velocity );
|
||||
}
|
||||
else if (colCheck.object->getTypeMask() & TriggerObjectType)
|
||||
{
|
||||
// We've hit it's bounding box, that's close enough for triggers
|
||||
Trigger* pTrigger = static_cast<Trigger*>(colCheck.object);
|
||||
|
||||
Component *comp = dynamic_cast<Component*>(this);
|
||||
pTrigger->potentialEnterObject(comp->getOwner());
|
||||
}
|
||||
else if (colCheck.object->getTypeMask() & DynamicShapeObjectType)
|
||||
{
|
||||
Con::printf("HIT A GENERICALLY DYNAMIC OBJECT");
|
||||
handleCollision(colCheck, velocity);
|
||||
}
|
||||
else if(colCheck.object->getTypeMask() & EntityObjectType)
|
||||
{
|
||||
Entity* ent = dynamic_cast<Entity*>(colCheck.object);
|
||||
if (ent)
|
||||
{
|
||||
CollisionInterface *colObjectInterface = ent->getComponent<CollisionInterface>();
|
||||
if (colObjectInterface)
|
||||
{
|
||||
//convert us to our component
|
||||
Component *thisComp = dynamic_cast<Component*>(this);
|
||||
if (thisComp)
|
||||
{
|
||||
colObjectInterface->onCollisionSignal.trigger(thisComp->getOwner());
|
||||
|
||||
//TODO: properly do this
|
||||
Collision oppositeCol = colCheck;
|
||||
oppositeCol.object = thisComp->getOwner();
|
||||
|
||||
colObjectInterface->handleCollision(oppositeCol, velocity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
handleCollision(colCheck, velocity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CollisionInterface::handleCollision( Collision &col, VectorF velocity )
|
||||
{
|
||||
if (col.object && (mContactInfo.contactObject == NULL ||
|
||||
col.object->getId() != mContactInfo.contactObject->getId()))
|
||||
{
|
||||
queueCollision(col.object, velocity - col.object->getVelocity());
|
||||
|
||||
//do the callbacks to script for this collision
|
||||
Component *comp = dynamic_cast<Component*>(this);
|
||||
if (comp->isMethod("onCollision"))
|
||||
{
|
||||
S32 matId = col.material != NULL ? col.material->getMaterial()->getId() : 0;
|
||||
Con::executef(comp, "onCollision", col.object, col.normal, col.point, matId, velocity);
|
||||
}
|
||||
|
||||
if (comp->getOwner()->isMethod("onCollisionEvent"))
|
||||
{
|
||||
S32 matId = col.material != NULL ? col.material->getMaterial()->getId() : 0;
|
||||
Con::executef(comp->getOwner(), "onCollisionEvent", col.object, col.normal, col.point, matId, velocity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CollisionInterface::handleCollisionNotifyList()
|
||||
{
|
||||
//special handling for any collision components we should notify that a collision happened.
|
||||
for (U32 i = 0; i < mCollisionNotifyList.size(); ++i)
|
||||
{
|
||||
//convert us to our component
|
||||
Component *thisComp = dynamic_cast<Component*>(this);
|
||||
if (thisComp)
|
||||
{
|
||||
mCollisionNotifyList[i]->onCollisionSignal.trigger(thisComp->getOwner());
|
||||
}
|
||||
}
|
||||
|
||||
mCollisionNotifyList.clear();
|
||||
}
|
||||
|
||||
Chunker<CollisionInterface::CollisionTimeout> sTimeoutChunker;
|
||||
CollisionInterface::CollisionTimeout* CollisionInterface::sFreeTimeoutList = 0;
|
||||
|
||||
void CollisionInterface::queueCollision( SceneObject *obj, const VectorF &vec)
|
||||
{
|
||||
// Add object to list of collisions.
|
||||
SimTime time = Sim::getCurrentTime();
|
||||
S32 num = obj->getId();
|
||||
|
||||
CollisionTimeout** adr = &mTimeoutList;
|
||||
CollisionTimeout* ptr = mTimeoutList;
|
||||
while (ptr)
|
||||
{
|
||||
if (ptr->objectNumber == num)
|
||||
{
|
||||
if (ptr->expireTime < time)
|
||||
{
|
||||
ptr->expireTime = time + CollisionTimeoutValue;
|
||||
ptr->object = obj;
|
||||
ptr->vector = vec;
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Recover expired entries
|
||||
if (ptr->expireTime < time)
|
||||
{
|
||||
CollisionTimeout* cur = ptr;
|
||||
*adr = ptr->next;
|
||||
ptr = ptr->next;
|
||||
cur->next = sFreeTimeoutList;
|
||||
sFreeTimeoutList = cur;
|
||||
}
|
||||
else
|
||||
{
|
||||
adr = &ptr->next;
|
||||
ptr = ptr->next;
|
||||
}
|
||||
}
|
||||
|
||||
// New entry for the object
|
||||
if (sFreeTimeoutList != NULL)
|
||||
{
|
||||
ptr = sFreeTimeoutList;
|
||||
sFreeTimeoutList = ptr->next;
|
||||
ptr->next = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = sTimeoutChunker.alloc();
|
||||
}
|
||||
|
||||
ptr->object = obj;
|
||||
ptr->objectNumber = obj->getId();
|
||||
ptr->vector = vec;
|
||||
ptr->expireTime = time + CollisionTimeoutValue;
|
||||
ptr->next = mTimeoutList;
|
||||
|
||||
mTimeoutList = ptr;
|
||||
}
|
||||
|
||||
bool CollisionInterface::checkEarlyOut(Point3F start, VectorF velocity, F32 time, Box3F objectBox, Point3F objectScale,
|
||||
Box3F collisionBox, U32 collisionMask, CollisionWorkingList &colWorkingList)
|
||||
{
|
||||
Point3F end = start + velocity * time;
|
||||
Point3F distance = end - start;
|
||||
|
||||
Box3F scaledBox = objectBox;
|
||||
scaledBox.minExtents.convolve(objectScale);
|
||||
scaledBox.maxExtents.convolve(objectScale);
|
||||
|
||||
if (mFabs(distance.x) < objectBox.len_x() &&
|
||||
mFabs(distance.y) < objectBox.len_y() &&
|
||||
mFabs(distance.z) < objectBox.len_z())
|
||||
{
|
||||
// We can potentially early out of this. If there are no polys in the clipped polylist at our
|
||||
// end position, then we can bail, and just set start = end;
|
||||
Box3F wBox = scaledBox;
|
||||
wBox.minExtents += end;
|
||||
wBox.maxExtents += end;
|
||||
|
||||
static EarlyOutPolyList eaPolyList;
|
||||
eaPolyList.clear();
|
||||
eaPolyList.mNormal.set(0.0f, 0.0f, 0.0f);
|
||||
eaPolyList.mPlaneList.clear();
|
||||
eaPolyList.mPlaneList.setSize(6);
|
||||
eaPolyList.mPlaneList[0].set(wBox.minExtents,VectorF(-1.0f, 0.0f, 0.0f));
|
||||
eaPolyList.mPlaneList[1].set(wBox.maxExtents,VectorF(0.0f, 1.0f, 0.0f));
|
||||
eaPolyList.mPlaneList[2].set(wBox.maxExtents,VectorF(1.0f, 0.0f, 0.0f));
|
||||
eaPolyList.mPlaneList[3].set(wBox.minExtents,VectorF(0.0f, -1.0f, 0.0f));
|
||||
eaPolyList.mPlaneList[4].set(wBox.minExtents,VectorF(0.0f, 0.0f, -1.0f));
|
||||
eaPolyList.mPlaneList[5].set(wBox.maxExtents,VectorF(0.0f, 0.0f, 1.0f));
|
||||
|
||||
// Build list from convex states here...
|
||||
CollisionWorkingList& rList = colWorkingList;
|
||||
CollisionWorkingList* pList = rList.wLink.mNext;
|
||||
while (pList != &rList)
|
||||
{
|
||||
Convex* pConvex = pList->mConvex;
|
||||
|
||||
if (pConvex->getObject()->getTypeMask() & collisionMask)
|
||||
{
|
||||
Box3F convexBox = pConvex->getBoundingBox();
|
||||
|
||||
if (wBox.isOverlapped(convexBox))
|
||||
{
|
||||
// No need to separate out the physical zones here, we want those
|
||||
// to cause a fallthrough as well...
|
||||
pConvex->getPolyList(&eaPolyList);
|
||||
}
|
||||
}
|
||||
pList = pList->wLink.mNext;
|
||||
}
|
||||
|
||||
if (eaPolyList.isEmpty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Collision* CollisionInterface::getCollision(S32 col)
|
||||
{
|
||||
if(col < mCollisionList.getCount() && col >= 0)
|
||||
return &mCollisionList[col];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
167
Engine/source/T3D/components/collision/collisionInterfaces.h
Normal file
167
Engine/source/T3D/components/collision/collisionInterfaces.h
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef COLLISION_INTERFACES_H
|
||||
#define COLLISION_INTERFACES_H
|
||||
|
||||
#ifndef _CONVEX_H_
|
||||
#include "collision/convex.h"
|
||||
#endif
|
||||
#ifndef _COLLISION_H_
|
||||
#include "collision/collision.h"
|
||||
#endif
|
||||
#ifndef _EARLYOUTPOLYLIST_H_
|
||||
#include "collision/earlyOutPolyList.h"
|
||||
#endif
|
||||
#ifndef _SIM_H_
|
||||
#include "console/sim.h"
|
||||
#endif
|
||||
#ifndef _SCENECONTAINER_H_
|
||||
#include "scene/sceneContainer.h"
|
||||
#endif
|
||||
#ifndef _T3D_PHYSICSCOMMON_H_
|
||||
#include "T3D/physics/physicsCommon.h"
|
||||
#endif
|
||||
|
||||
struct ContactInfo
|
||||
{
|
||||
bool contacted, move;
|
||||
SceneObject *contactObject;
|
||||
VectorF idealContactNormal;
|
||||
VectorF contactNormal;
|
||||
Point3F contactPoint;
|
||||
F32 contactTime;
|
||||
S32 contactTimer;
|
||||
BaseMatInstance *contactMaterial;
|
||||
|
||||
void clear()
|
||||
{
|
||||
contacted=move=false;
|
||||
contactObject = NULL;
|
||||
contactNormal.set(0,0,0);
|
||||
contactTime = 0.f;
|
||||
contactTimer = 0;
|
||||
idealContactNormal.set(0, 0, 1);
|
||||
contactMaterial = NULL;
|
||||
}
|
||||
|
||||
ContactInfo() { clear(); }
|
||||
|
||||
};
|
||||
|
||||
class CollisionInterface// : public Interface<CollisionInterface>
|
||||
{
|
||||
public:
|
||||
// CollisionTimeout
|
||||
// This struct lets us track our collisions and estimate when they've have timed out and we'll need to act on it.
|
||||
struct CollisionTimeout
|
||||
{
|
||||
CollisionTimeout* next;
|
||||
SceneObject* object;
|
||||
U32 objectNumber;
|
||||
SimTime expireTime;
|
||||
VectorF vector;
|
||||
};
|
||||
|
||||
Signal< void( SceneObject* ) > CollisionInterface::onCollisionSignal;
|
||||
Signal< void( SceneObject* ) > CollisionInterface::onContactSignal;
|
||||
|
||||
protected:
|
||||
CollisionTimeout* mTimeoutList;
|
||||
static CollisionTimeout* sFreeTimeoutList;
|
||||
|
||||
CollisionList mCollisionList;
|
||||
Vector<CollisionInterface*> mCollisionNotifyList;
|
||||
|
||||
ContactInfo mContactInfo;
|
||||
|
||||
Box3F mWorkingQueryBox;
|
||||
|
||||
U32 CollisionMoveMask;
|
||||
|
||||
Convex *mConvexList;
|
||||
|
||||
bool mBlockColliding;
|
||||
|
||||
void handleCollisionNotifyList();
|
||||
|
||||
void queueCollision( SceneObject *obj, const VectorF &vec);
|
||||
|
||||
/// checkEarlyOut
|
||||
/// This function lets you trying and early out of any expensive collision checks by using simple extruded poly boxes representing our objects
|
||||
/// If it returns true, we know we won't hit with the given parameters and can successfully early out. If it returns false, our test case collided
|
||||
/// and we should do the full collision sim.
|
||||
bool checkEarlyOut(Point3F start, VectorF velocity, F32 time, Box3F objectBox, Point3F objectScale,
|
||||
Box3F collisionBox, U32 collisionMask, CollisionWorkingList &colWorkingList);
|
||||
|
||||
public:
|
||||
/// checkCollisions
|
||||
// This is our main function for checking if a collision is happening based on the start point, velocity and time
|
||||
// We do the bulk of the collision checking in here
|
||||
//virtual bool checkCollisions( const F32 travelTime, Point3F *velocity, Point3F start )=0;
|
||||
|
||||
CollisionList *getCollisionList() { return &mCollisionList; }
|
||||
|
||||
void clearCollisionList() { mCollisionList.clear(); }
|
||||
|
||||
void clearCollisionNotifyList() { mCollisionNotifyList.clear(); }
|
||||
|
||||
Collision *getCollision(S32 col);
|
||||
|
||||
ContactInfo* getContactInfo() { return &mContactInfo; }
|
||||
|
||||
Convex *getConvexList() { return mConvexList; }
|
||||
|
||||
virtual bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere) = 0;
|
||||
|
||||
enum PublicConstants {
|
||||
CollisionTimeoutValue = 250
|
||||
};
|
||||
|
||||
bool doesBlockColliding() { return mBlockColliding; }
|
||||
|
||||
/// handleCollisionList
|
||||
/// This basically takes in a CollisionList and calls handleCollision for each.
|
||||
void handleCollisionList(CollisionList &collisionList, VectorF velocity);
|
||||
|
||||
/// handleCollision
|
||||
/// This will take a collision and queue the collision info for the object so that in knows about the collision.
|
||||
void handleCollision(Collision &col, VectorF velocity);
|
||||
|
||||
virtual PhysicsCollision* getCollisionData() = 0;
|
||||
|
||||
Signal< void(PhysicsCollision* collision) > CollisionInterface::onCollisionChanged;
|
||||
};
|
||||
|
||||
class BuildConvexInterface //: public Interface<CollisionInterface>
|
||||
{
|
||||
public:
|
||||
virtual void buildConvex(const Box3F& box, Convex* convex)=0;
|
||||
};
|
||||
|
||||
class BuildPolyListInterface// : public Interface<CollisionInterface>
|
||||
{
|
||||
public:
|
||||
virtual bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
619
Engine/source/T3D/components/collision/collisionTrigger.cpp
Normal file
619
Engine/source/T3D/components/collision/collisionTrigger.cpp
Normal file
|
|
@ -0,0 +1,619 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/components/collision/collisionTrigger.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 CollisionTrigger::smRenderCollisionTriggers = false;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(CollisionTrigger);
|
||||
|
||||
ConsoleDocClass(CollisionTrigger,
|
||||
"@brief A CollisionTrigger is a volume of space that initiates script callbacks "
|
||||
"when objects pass through the CollisionTrigger.\n\n"
|
||||
|
||||
"CollisionTriggerData provides the callbacks for the CollisionTrigger when an object enters, stays inside "
|
||||
"or leaves the CollisionTrigger's volume.\n\n"
|
||||
|
||||
"@see CollisionTriggerData\n"
|
||||
"@ingroup gameObjects\n"
|
||||
);
|
||||
|
||||
IMPLEMENT_CALLBACK(CollisionTrigger, onAdd, void, (U32 objectId), (objectId),
|
||||
"@brief Called when the CollisionTrigger is being created.\n\n"
|
||||
"@param objectId the object id of the CollisionTrigger being created\n");
|
||||
|
||||
IMPLEMENT_CALLBACK(CollisionTrigger, onRemove, void, (U32 objectId), (objectId),
|
||||
"@brief Called just before the CollisionTrigger is deleted.\n\n"
|
||||
"@param objectId the object id of the CollisionTrigger being deleted\n");
|
||||
|
||||
CollisionTrigger::CollisionTrigger()
|
||||
{
|
||||
// Don't ghost by default.
|
||||
mNetFlags.set(Ghostable | ScopeAlways);
|
||||
|
||||
mTypeMask |= TriggerObjectType;
|
||||
|
||||
mObjScale.set(1, 1, 1);
|
||||
mObjToWorld.identity();
|
||||
mWorldToObj.identity();
|
||||
|
||||
mLastThink = 0;
|
||||
mCurrTick = 0;
|
||||
|
||||
mConvexList = new Convex;
|
||||
|
||||
mPhysicsRep = NULL;
|
||||
}
|
||||
|
||||
CollisionTrigger::~CollisionTrigger()
|
||||
{
|
||||
delete mConvexList;
|
||||
mConvexList = NULL;
|
||||
SAFE_DELETE(mPhysicsRep);
|
||||
}
|
||||
|
||||
bool CollisionTrigger::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;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CollisionTrigger::consoleInit()
|
||||
{
|
||||
Con::addVariable("$CollisionTrigger::renderCollisionTriggers", TypeBool, &smRenderCollisionTriggers,
|
||||
"@brief Forces all CollisionTrigger's to render.\n\n"
|
||||
"Used by the Tools and debug render modes.\n"
|
||||
"@ingroup gameObjects");
|
||||
}
|
||||
|
||||
void CollisionTrigger::initPersistFields()
|
||||
{
|
||||
addField("polyhedron", TypeTriggerPolyhedron, Offset(mCollisionTriggerPolyhedron, CollisionTrigger),
|
||||
"@brief Defines a non-rectangular area for the CollisionTrigger.\n\n"
|
||||
"Rather than the standard rectangular bounds, this optional parameter defines a quadrilateral "
|
||||
"CollisionTrigger area. The quadrilateral is defined as a corner point followed by three vectors "
|
||||
"representing the edges extending from the corner.\n");
|
||||
|
||||
addProtectedField("enterCommand", TypeCommand, Offset(mEnterCommand, CollisionTrigger), &setEnterCmd, &defaultProtectedGetFn,
|
||||
"The command to execute when an object enters this CollisionTrigger. Object id stored in %%obj. Maximum 1023 characters.");
|
||||
addProtectedField("leaveCommand", TypeCommand, Offset(mLeaveCommand, CollisionTrigger), &setLeaveCmd, &defaultProtectedGetFn,
|
||||
"The command to execute when an object leaves this CollisionTrigger. Object id stored in %%obj. Maximum 1023 characters.");
|
||||
addProtectedField("tickCommand", TypeCommand, Offset(mTickCommand, CollisionTrigger), &setTickCmd, &defaultProtectedGetFn,
|
||||
"The command to execute while an object is inside this CollisionTrigger. Maximum 1023 characters.");
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
bool CollisionTrigger::setEnterCmd(void *object, const char *index, const char *data)
|
||||
{
|
||||
static_cast<CollisionTrigger*>(object)->setMaskBits(EnterCmdMask);
|
||||
return true; // to update the actual field
|
||||
}
|
||||
|
||||
bool CollisionTrigger::setLeaveCmd(void *object, const char *index, const char *data)
|
||||
{
|
||||
static_cast<CollisionTrigger*>(object)->setMaskBits(LeaveCmdMask);
|
||||
return true; // to update the actual field
|
||||
}
|
||||
|
||||
bool CollisionTrigger::setTickCmd(void *object, const char *index, const char *data)
|
||||
{
|
||||
static_cast<CollisionTrigger*>(object)->setMaskBits(TickCmdMask);
|
||||
return true; // to update the actual field
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
bool CollisionTrigger::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
onAdd_callback(getId());
|
||||
|
||||
Polyhedron temp = mCollisionTriggerPolyhedron;
|
||||
setTriggerPolyhedron(temp);
|
||||
|
||||
addToScene();
|
||||
|
||||
if (isServerObject())
|
||||
scriptOnAdd();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CollisionTrigger::onRemove()
|
||||
{
|
||||
onRemove_callback(getId());
|
||||
|
||||
mConvexList->nukeList();
|
||||
|
||||
removeFromScene();
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
bool CollisionTrigger::onNewDataBlock(GameBaseData *dptr, bool reload)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void CollisionTrigger::onDeleteNotify(SimObject *obj)
|
||||
{
|
||||
GameBase* pScene = dynamic_cast<GameBase*>(obj);
|
||||
|
||||
if (pScene != NULL)
|
||||
{
|
||||
for (U32 i = 0; i < mObjects.size(); i++)
|
||||
{
|
||||
if (pScene == mObjects[i])
|
||||
{
|
||||
mObjects.erase(i);
|
||||
//onLeaveCollisionTrigger_callback(this, pScene);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Parent::onDeleteNotify(obj);
|
||||
}
|
||||
|
||||
void CollisionTrigger::inspectPostApply()
|
||||
{
|
||||
setTriggerPolyhedron(mCollisionTriggerPolyhedron);
|
||||
setMaskBits(PolyMask);
|
||||
Parent::inspectPostApply();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void CollisionTrigger::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 CollisionTrigger::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);
|
||||
}
|
||||
}
|
||||
|
||||
void CollisionTrigger::prepRenderImage(SceneRenderState *state)
|
||||
{
|
||||
// only render if selected or render flag is set
|
||||
if (!smRenderCollisionTriggers && !isSelected())
|
||||
return;
|
||||
|
||||
ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
|
||||
ri->renderDelegate.bind(this, &CollisionTrigger::renderObject);
|
||||
ri->type = RenderPassManager::RIT_Editor;
|
||||
ri->translucentSort = true;
|
||||
ri->defaultKey = 1;
|
||||
state->getRenderPass()->addInst(ri);
|
||||
}
|
||||
|
||||
void CollisionTrigger::renderObject(ObjectRenderInst *ri,
|
||||
SceneRenderState *state,
|
||||
BaseMatInstance *overrideMat)
|
||||
{
|
||||
if (overrideMat)
|
||||
return;
|
||||
|
||||
GFXStateBlockDesc desc;
|
||||
desc.setZReadWrite(true, false);
|
||||
desc.setBlend(true);
|
||||
|
||||
// CollisionTrigger 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, mCollisionTriggerPolyhedron, ColorI(255, 192, 0, 45));
|
||||
|
||||
// Render wireframe.
|
||||
|
||||
desc.setFillModeWireframe();
|
||||
drawer->drawPolyhedron(desc, mCollisionTriggerPolyhedron, ColorI::BLACK);
|
||||
}
|
||||
|
||||
void CollisionTrigger::setTriggerPolyhedron(const Polyhedron& rPolyhedron)
|
||||
{
|
||||
mCollisionTriggerPolyhedron = rPolyhedron;
|
||||
|
||||
if (mCollisionTriggerPolyhedron.pointList.size() != 0) {
|
||||
mObjBox.minExtents.set(1e10, 1e10, 1e10);
|
||||
mObjBox.maxExtents.set(-1e10, -1e10, -1e10);
|
||||
for (U32 i = 0; i < mCollisionTriggerPolyhedron.pointList.size(); i++) {
|
||||
mObjBox.minExtents.setMin(mCollisionTriggerPolyhedron.pointList[i]);
|
||||
mObjBox.maxExtents.setMax(mCollisionTriggerPolyhedron.pointList[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 = mCollisionTriggerPolyhedron.planeList;
|
||||
// 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( mCollisionTriggerPolyhedron.pointList.address(), mCollisionTriggerPolyhedron.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 CollisionTrigger::testObject(GameBase* enter)
|
||||
{
|
||||
if (mCollisionTriggerPolyhedron.pointList.size() == 0)
|
||||
return false;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
void CollisionTrigger::potentialEnterObject(GameBase* enter)
|
||||
{
|
||||
for (U32 i = 0; i < mObjects.size(); i++) {
|
||||
if (mObjects[i] == enter)
|
||||
return;
|
||||
}
|
||||
|
||||
if (testObject(enter) == true) {
|
||||
mObjects.push_back(enter);
|
||||
deleteNotify(enter);
|
||||
|
||||
if (!mEnterCommand.isEmpty())
|
||||
{
|
||||
String command = String("%obj = ") + enter->getIdString() + ";" + mEnterCommand;
|
||||
Con::evaluate(command.c_str());
|
||||
}
|
||||
|
||||
//onEnterCollisionTrigger_callback(this, enter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CollisionTrigger::processTick(const Move* move)
|
||||
{
|
||||
Parent::processTick(move);
|
||||
|
||||
//
|
||||
if (mObjects.size() == 0)
|
||||
return;
|
||||
|
||||
if (mLastThink + 100 < 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 (!mLeaveCommand.isEmpty())
|
||||
{
|
||||
String command = String("%obj = ") + remove->getIdString() + ";" + mLeaveCommand;
|
||||
Con::evaluate(command.c_str());
|
||||
}
|
||||
|
||||
//onLeaveCollisionTrigger_callback(this, remove);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mTickCommand.isEmpty())
|
||||
Con::evaluate(mTickCommand.c_str());
|
||||
|
||||
//if (mObjects.size() != 0)
|
||||
// onTickCollisionTrigger_callback(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
mCurrTick += TickMs;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
U32 CollisionTrigger::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(mCollisionTriggerPolyhedron.pointList.size());
|
||||
for (i = 0; i < mCollisionTriggerPolyhedron.pointList.size(); i++)
|
||||
mathWrite(*stream, mCollisionTriggerPolyhedron.pointList[i]);
|
||||
|
||||
stream->write(mCollisionTriggerPolyhedron.planeList.size());
|
||||
for (i = 0; i < mCollisionTriggerPolyhedron.planeList.size(); i++)
|
||||
mathWrite(*stream, mCollisionTriggerPolyhedron.planeList[i]);
|
||||
|
||||
stream->write(mCollisionTriggerPolyhedron.edgeList.size());
|
||||
for (i = 0; i < mCollisionTriggerPolyhedron.edgeList.size(); i++) {
|
||||
const Polyhedron::Edge& rEdge = mCollisionTriggerPolyhedron.edgeList[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 CollisionTrigger::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.pointList.setSize(size);
|
||||
for (i = 0; i < tempPH.pointList.size(); i++)
|
||||
mathRead(*stream, &tempPH.pointList[i]);
|
||||
|
||||
stream->read(&size);
|
||||
tempPH.planeList.setSize(size);
|
||||
for (i = 0; i < tempPH.planeList.size(); i++)
|
||||
mathRead(*stream, &tempPH.planeList[i]);
|
||||
|
||||
stream->read(&size);
|
||||
tempPH.edgeList.setSize(size);
|
||||
for (i = 0; i < tempPH.edgeList.size(); i++) {
|
||||
Polyhedron::Edge& rEdge = tempPH.edgeList[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( CollisionTrigger, getNumObjects, S32, 2, 2, "")
|
||||
DefineEngineMethod(CollisionTrigger, getNumObjects, S32, (), ,
|
||||
"@brief Get the number of objects that are within the CollisionTrigger's bounds.\n\n"
|
||||
"@see getObject()\n")
|
||||
{
|
||||
return object->getNumCollisionTriggeringObjects();
|
||||
}
|
||||
|
||||
//ConsoleMethod( CollisionTrigger, getObject, S32, 3, 3, "(int idx)")
|
||||
DefineEngineMethod(CollisionTrigger, getObject, S32, (S32 index), ,
|
||||
"@brief Retrieve the requested object that is within the CollisionTrigger'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->getNumCollisionTriggeringObjects() || index < 0)
|
||||
return -1;
|
||||
else
|
||||
return object->getObject(U32(index))->getId();
|
||||
}
|
||||
145
Engine/source/T3D/components/collision/collisionTrigger.h
Normal file
145
Engine/source/T3D/components/collision/collisionTrigger.h
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _H_CollisionTrigger
|
||||
#define _H_CollisionTrigger
|
||||
|
||||
#ifndef _GAMEBASE_H_
|
||||
#include "T3D/gameBase/gameBase.h"
|
||||
#endif
|
||||
#ifndef _MBOX_H_
|
||||
#include "math/mBox.h"
|
||||
#endif
|
||||
#ifndef _EARLYOUTPOLYLIST_H_
|
||||
#include "collision/earlyOutPolyList.h"
|
||||
#endif
|
||||
#ifndef _MPOLYHEDRON_H_
|
||||
#include "math/mPolyhedron.h"
|
||||
#endif
|
||||
#ifndef _TRIGGER_H_
|
||||
#include "T3D/trigger.h"
|
||||
#endif
|
||||
|
||||
class Convex;
|
||||
class PhysicsBody;
|
||||
class TriggerPolyhedronType;
|
||||
|
||||
class CollisionTrigger : public GameBase
|
||||
{
|
||||
typedef GameBase Parent;
|
||||
|
||||
/// CollisionTrigger polyhedron with *outward* facing normals and CCW ordered
|
||||
/// vertices.
|
||||
Polyhedron mCollisionTriggerPolyhedron;
|
||||
|
||||
EarlyOutPolyList mClippedList;
|
||||
Vector<GameBase*> mObjects;
|
||||
|
||||
PhysicsBody *mPhysicsRep;
|
||||
|
||||
U32 mLastThink;
|
||||
U32 mCurrTick;
|
||||
Convex *mConvexList;
|
||||
|
||||
String mEnterCommand;
|
||||
String mLeaveCommand;
|
||||
String mTickCommand;
|
||||
|
||||
enum CollisionTriggerUpdateBits
|
||||
{
|
||||
TransformMask = Parent::NextFreeMask << 0,
|
||||
PolyMask = Parent::NextFreeMask << 1,
|
||||
EnterCmdMask = Parent::NextFreeMask << 2,
|
||||
LeaveCmdMask = Parent::NextFreeMask << 3,
|
||||
TickCmdMask = Parent::NextFreeMask << 4,
|
||||
NextFreeMask = Parent::NextFreeMask << 5,
|
||||
};
|
||||
|
||||
static const U32 CMD_SIZE = 1024;
|
||||
|
||||
protected:
|
||||
|
||||
static bool smRenderCollisionTriggers;
|
||||
bool testObject(GameBase* enter);
|
||||
void processTick(const Move *move);
|
||||
|
||||
void buildConvex(const Box3F& box, Convex* convex);
|
||||
|
||||
static bool setEnterCmd(void *object, const char *index, const char *data);
|
||||
static bool setLeaveCmd(void *object, const char *index, const char *data);
|
||||
static bool setTickCmd(void *object, const char *index, const char *data);
|
||||
|
||||
public:
|
||||
CollisionTrigger();
|
||||
~CollisionTrigger();
|
||||
|
||||
// SimObject
|
||||
DECLARE_CONOBJECT(CollisionTrigger);
|
||||
|
||||
DECLARE_CALLBACK(void, onAdd, (U32 objectId));
|
||||
DECLARE_CALLBACK(void, onRemove, (U32 objectId));
|
||||
|
||||
static void consoleInit();
|
||||
static void initPersistFields();
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
void onDeleteNotify(SimObject*);
|
||||
void inspectPostApply();
|
||||
|
||||
// NetObject
|
||||
U32 packUpdate(NetConnection *conn, U32 mask, BitStream* stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream* stream);
|
||||
|
||||
// SceneObject
|
||||
void setTransform(const MatrixF &mat);
|
||||
void prepRenderImage(SceneRenderState* state);
|
||||
|
||||
// GameBase
|
||||
bool onNewDataBlock(GameBaseData *dptr, bool reload);
|
||||
|
||||
// CollisionTrigger
|
||||
void setTriggerPolyhedron(const Polyhedron&);
|
||||
|
||||
void potentialEnterObject(GameBase*);
|
||||
U32 getNumCollisionTriggeringObjects() const;
|
||||
GameBase* getObject(const U32);
|
||||
const Vector<GameBase*>& getObjects() const { return mObjects; }
|
||||
|
||||
void renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat);
|
||||
|
||||
bool castRay(const Point3F &start, const Point3F &end, RayInfo* info);
|
||||
};
|
||||
|
||||
inline U32 CollisionTrigger::getNumCollisionTriggeringObjects() const
|
||||
{
|
||||
return mObjects.size();
|
||||
}
|
||||
|
||||
inline GameBase* CollisionTrigger::getObject(const U32 index)
|
||||
{
|
||||
AssertFatal(index < getNumCollisionTriggeringObjects(), "Error, out of range object index");
|
||||
|
||||
return mObjects[index];
|
||||
}
|
||||
|
||||
#endif // _H_CollisionTrigger
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue