Other renames to ensure linux case-sensitivity compliance and casing format consistency.

This commit is contained in:
Areloch 2016-05-26 13:56:19 -05:00
parent 93e767f0c5
commit f5e86a83b5
33 changed files with 0 additions and 0 deletions

View 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;
}

View 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

View file

@ -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);
}

View 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;
}

View 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

View 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();
}

View 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