mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-11 14:44:36 +00:00
Updates to various components, added a few new ones.
This commit is contained in:
parent
e0627973fb
commit
dac8d6e1fd
52 changed files with 4566 additions and 1799 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -19,190 +19,188 @@
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
#ifndef COLLISION_COMPONENT_H
|
||||
#define COLLISION_COMPONENT_H
|
||||
|
||||
#ifndef __RESOURCE_H__
|
||||
#include "core/resource.h"
|
||||
#ifndef COMPONENT_H
|
||||
#include "T3D/components/component.h"
|
||||
#endif
|
||||
#ifndef _TSSHAPE_H_
|
||||
#include "ts/tsShape.h"
|
||||
|
||||
#ifndef _CONVEX_H_
|
||||
#include "collision/convex.h"
|
||||
#endif
|
||||
#ifndef _SCENERENDERSTATE_H_
|
||||
#include "scene/sceneRenderState.h"
|
||||
#ifndef _COLLISION_H_
|
||||
#include "collision/collision.h"
|
||||
#endif
|
||||
#ifndef _MBOX_H_
|
||||
#include "math/mBox.h"
|
||||
#ifndef _EARLYOUTPOLYLIST_H_
|
||||
#include "collision/earlyOutPolyList.h"
|
||||
#endif
|
||||
#ifndef ENTITY_H
|
||||
#include "T3D/entity.h"
|
||||
#ifndef _SIM_H_
|
||||
#include "console/sim.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"
|
||||
#ifndef _SCENECONTAINER_H_
|
||||
#include "scene/sceneContainer.h"
|
||||
#endif
|
||||
#ifndef _T3D_PHYSICSCOMMON_H_
|
||||
#include "T3D/physics/physicsCommon.h"
|
||||
#endif
|
||||
#ifndef PHYSICS_COMPONENT_H
|
||||
#include "T3D/components/physics/physicsComponent.h"
|
||||
#endif
|
||||
#ifndef _T3D_PHYSICS_PHYSICSWORLD_H_
|
||||
#include "T3D/physics/physicsWorld.h"
|
||||
#endif
|
||||
|
||||
class TSShapeInstance;
|
||||
class SceneRenderState;
|
||||
class CollisionComponent;
|
||||
class PhysicsBody;
|
||||
class PhysicsWorld;
|
||||
struct CollisionContactInfo
|
||||
{
|
||||
bool contacted, move;
|
||||
SceneObject *contactObject;
|
||||
VectorF idealContactNormal;
|
||||
VectorF contactNormal;
|
||||
Point3F contactPoint;
|
||||
F32 contactTime;
|
||||
S32 contactTimer;
|
||||
BaseMatInstance *contactMaterial;
|
||||
|
||||
class CollisionComponent : public Component,
|
||||
public CollisionInterface,
|
||||
public CastRayInterface
|
||||
Vector<SceneObject*> overlapObjects;
|
||||
|
||||
void clear()
|
||||
{
|
||||
contacted=move=false;
|
||||
contactObject = NULL;
|
||||
contactNormal.set(0,0,0);
|
||||
contactTime = 0.f;
|
||||
contactTimer = 0;
|
||||
idealContactNormal.set(0, 0, 1);
|
||||
contactMaterial = NULL;
|
||||
overlapObjects.clear();
|
||||
}
|
||||
|
||||
CollisionContactInfo() { clear(); }
|
||||
|
||||
};
|
||||
|
||||
class CollisionComponent : public Component
|
||||
{
|
||||
typedef Component Parent;
|
||||
|
||||
public:
|
||||
enum MeshType
|
||||
// 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
|
||||
{
|
||||
None = 0, ///< No mesh
|
||||
Bounds = 1, ///< Bounding box of the shape
|
||||
CollisionMesh = 2, ///< Specifically designated collision meshes
|
||||
VisibleMesh = 3 ///< Rendered mesh polygons
|
||||
CollisionTimeout* next;
|
||||
SceneObject* object;
|
||||
U32 objectNumber;
|
||||
SimTime expireTime;
|
||||
VectorF vector;
|
||||
};
|
||||
|
||||
Signal< void( SceneObject* ) > onCollisionSignal;
|
||||
Signal< void( SceneObject* ) > onContactSignal;
|
||||
|
||||
protected:
|
||||
PhysicsWorld* mPhysicsWorld;
|
||||
PhysicsBody* mPhysicsRep;
|
||||
|
||||
protected:
|
||||
MeshType mCollisionType;
|
||||
MeshType mDecalType;
|
||||
MeshType mLOSType;
|
||||
CollisionTimeout* mTimeoutList;
|
||||
static CollisionTimeout* sFreeTimeoutList;
|
||||
|
||||
Vector<S32> mCollisionDetails;
|
||||
Vector<S32> mLOSDetails;
|
||||
CollisionList mCollisionList;
|
||||
Vector<CollisionComponent*> mCollisionNotifyList;
|
||||
|
||||
StringTableEntry colisionMeshPrefix;
|
||||
CollisionContactInfo mContactInfo;
|
||||
|
||||
RenderComponentInterface* mOwnerRenderInterface;
|
||||
U32 CollisionMoveMask;
|
||||
|
||||
PhysicsComponentInterface* mOwnerPhysicsInterface;
|
||||
bool mBlockColliding;
|
||||
|
||||
//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;
|
||||
bool mCollisionInited;
|
||||
|
||||
enum
|
||||
{
|
||||
ColliderMask = Parent::NextFreeMask,
|
||||
};
|
||||
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:
|
||||
CollisionComponent();
|
||||
virtual ~CollisionComponent();
|
||||
|
||||
DECLARE_CONOBJECT(CollisionComponent);
|
||||
|
||||
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||
//Setup
|
||||
virtual void prepCollision() {};
|
||||
|
||||
virtual void componentAddedToOwner(Component *comp);
|
||||
virtual void componentRemovedFromOwner(Component *comp);
|
||||
virtual void ownerTransformSet(MatrixF *mat);
|
||||
void targetShapeChanged(RenderComponentInterface* instanceInterface);
|
||||
/// 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;
|
||||
|
||||
virtual void onComponentRemove();
|
||||
virtual void onComponentAdd();
|
||||
CollisionList *getCollisionList() { return &mCollisionList; }
|
||||
|
||||
virtual void checkDependencies();
|
||||
void clearCollisionList() { mCollisionList.clear(); }
|
||||
|
||||
static void initPersistFields();
|
||||
void clearCollisionNotifyList() { mCollisionNotifyList.clear(); }
|
||||
|
||||
void inspectPostApply();
|
||||
Collision *getCollision(S32 col);
|
||||
|
||||
virtual void processTick();
|
||||
CollisionContactInfo* getContactInfo() { return &mContactInfo; }
|
||||
|
||||
void prepCollision();
|
||||
enum PublicConstants {
|
||||
CollisionTimeoutValue = 250
|
||||
};
|
||||
|
||||
PhysicsCollision* buildColShapes();
|
||||
bool doesBlockColliding() { return mBlockColliding; }
|
||||
|
||||
void updatePhysics();
|
||||
/// handleCollisionList
|
||||
/// This basically takes in a CollisionList and calls handleCollision for each.
|
||||
void handleCollisionList(CollisionList &collisionList, VectorF velocity);
|
||||
|
||||
virtual bool castRay(const Point3F &start, const Point3F &end, RayInfo* info);
|
||||
/// 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 bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere){ return false; }
|
||||
virtual bool checkCollisions(const F32 travelTime, Point3F *velocity, Point3F start);
|
||||
virtual bool updateCollisions(F32 time, VectorF vector, VectorF velocity);
|
||||
virtual void updateWorkingCollisionSet(const U32 mask);
|
||||
|
||||
virtual PhysicsCollision* getCollisionData();
|
||||
//
|
||||
bool buildConvexOpcode(TSShapeInstance* sI, S32 dl, const Box3F &bounds, Convex *c, Convex *list);
|
||||
bool buildMeshOpcode(TSMesh *mesh, const MatrixF &meshToObjectMat, const Box3F &bounds, Convex *convex, Convex *list);
|
||||
|
||||
//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();
|
||||
bool castRayOpcode(S32 dl, const Point3F & startPos, const Point3F & endPos, RayInfo *info);
|
||||
bool castRayMeshOpcode(TSMesh *mesh, const Point3F &s, const Point3F &e, RayInfo *info, TSMaterialList *materials);
|
||||
|
||||
virtual PhysicsCollision* getCollisionData() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Point3F getCollisionNormal(S32 collisionIndex)
|
||||
virtual PhysicsBody *getPhysicsRep()
|
||||
{
|
||||
if (collisionIndex < 0 || mCollisionList.getCount() < collisionIndex)
|
||||
return Point3F::Zero;
|
||||
|
||||
return mCollisionList[collisionIndex].normal;
|
||||
return mPhysicsRep;
|
||||
}
|
||||
|
||||
F32 getCollisionAngle(S32 collisionIndex, Point3F upVector)
|
||||
{
|
||||
if (collisionIndex < 0 || mCollisionList.getCount() < collisionIndex)
|
||||
return 0.0f;
|
||||
void buildConvex(const Box3F& box, Convex* convex) {}
|
||||
bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere) { return false; }
|
||||
|
||||
return mRadToDeg(mAcos(mDot(mCollisionList[collisionIndex].normal, upVector)));
|
||||
}
|
||||
//
|
||||
Point3F getContactNormal();
|
||||
bool hasContact();
|
||||
S32 getCollisionCount();
|
||||
Point3F getCollisionNormal(S32 collisionIndex);
|
||||
F32 getCollisionAngle(S32 collisionIndex, Point3F upVector);
|
||||
S32 getBestCollision(Point3F upVector);
|
||||
F32 getBestCollisionAngle(VectorF 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);
|
||||
}
|
||||
Signal< void(PhysicsCollision* collision) > onCollisionChanged;
|
||||
};
|
||||
|
||||
typedef CollisionComponent::MeshType CollisionMeshMeshType;
|
||||
DefineEnumType(CollisionMeshMeshType);
|
||||
|
||||
#endif // COLLISION_COMPONENT_H
|
||||
#endif
|
||||
|
|
@ -1,258 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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> sCollisionTimeoutChunker;
|
||||
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 = sCollisionTimeoutChunker.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;
|
||||
}
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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* ) > onCollisionSignal;
|
||||
Signal< void( SceneObject* ) > 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) > 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
|
||||
|
|
@ -480,7 +480,7 @@ void CollisionTrigger::processTick(const Move* move)
|
|||
}
|
||||
}
|
||||
|
||||
if (!mTickCommand.isEmpty())
|
||||
if (!mTickCommand.isEmpty() && mObjects.size() != 0)
|
||||
Con::evaluate(mTickCommand.c_str());
|
||||
|
||||
//if (mObjects.size() != 0)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,122 @@
|
|||
#include "T3D/components/collision/raycastColliderComponent.h"
|
||||
#include "T3D/physics/physicsPlugin.h"
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1(RaycastColliderComponent);
|
||||
|
||||
RaycastColliderComponent::RaycastColliderComponent() :
|
||||
mUseVelocity(false),
|
||||
mOwnerPhysicsComponent(nullptr),
|
||||
mRayDirection(VectorF::Zero),
|
||||
mRayLength(1),
|
||||
mPhysicsWorld(nullptr),
|
||||
mOldPosition(Point3F::Zero),
|
||||
mMask(-1)
|
||||
{
|
||||
}
|
||||
|
||||
RaycastColliderComponent::~RaycastColliderComponent()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool RaycastColliderComponent::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
if (PHYSICSMGR)
|
||||
mPhysicsWorld = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
|
||||
|
||||
return true;
|
||||
}
|
||||
void RaycastColliderComponent::onRemove()
|
||||
{
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
void RaycastColliderComponent::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
void RaycastColliderComponent::onComponentAdd()
|
||||
{
|
||||
PhysicsComponent* physComp = mOwner->getComponent<PhysicsComponent>();
|
||||
|
||||
if (physComp)
|
||||
{
|
||||
mOwnerPhysicsComponent = physComp;
|
||||
}
|
||||
}
|
||||
|
||||
void RaycastColliderComponent::onComponentRemove()
|
||||
{
|
||||
mOwnerPhysicsComponent = nullptr;
|
||||
}
|
||||
|
||||
void RaycastColliderComponent::componentAddedToOwner(Component *comp)
|
||||
{
|
||||
Parent::componentAddedToOwner(comp);
|
||||
|
||||
PhysicsComponent* physComp = dynamic_cast<PhysicsComponent*>(comp);
|
||||
if (physComp)
|
||||
{
|
||||
mOwnerPhysicsComponent = physComp;
|
||||
}
|
||||
}
|
||||
|
||||
void RaycastColliderComponent::componentRemovedFromOwner(Component *comp)
|
||||
{
|
||||
Parent::componentRemovedFromOwner(comp);
|
||||
|
||||
if (mOwnerPhysicsComponent != nullptr && mOwnerPhysicsComponent->getId() == comp->getId())
|
||||
{
|
||||
mOwnerPhysicsComponent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void RaycastColliderComponent::processTick()
|
||||
{
|
||||
Parent::processTick();
|
||||
|
||||
// Raycast the abstract PhysicsWorld if a PhysicsPlugin exists.
|
||||
bool hit = false;
|
||||
|
||||
Point3F start = mOldPosition;
|
||||
Point3F end;
|
||||
|
||||
if (mUseVelocity)
|
||||
{
|
||||
//our end is the new position
|
||||
end = mOwner->getPosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
end = start + (mRayDirection * mRayLength);
|
||||
}
|
||||
|
||||
RayInfo rInfo;
|
||||
|
||||
if (mPhysicsWorld)
|
||||
hit = mPhysicsWorld->castRay(start, end, &rInfo, Point3F::Zero);
|
||||
else
|
||||
hit = mOwner->getContainer()->castRay(start, end, mMask, &rInfo);
|
||||
|
||||
if (hit)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
if (mUseVelocity)
|
||||
mOldPosition = end;
|
||||
}
|
||||
|
||||
void RaycastColliderComponent::interpolateTick(F32 dt)
|
||||
{
|
||||
Parent::interpolateTick(dt);
|
||||
}
|
||||
|
||||
void RaycastColliderComponent::advanceTime(F32 dt)
|
||||
{
|
||||
Parent::advanceTime(dt);
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include "T3D/components/collision/collisionComponent.h"
|
||||
#include "T3D/components/physics/physicsComponent.h"
|
||||
#include "T3D/physics/physicsWorld.h"
|
||||
|
||||
class RaycastColliderComponent : public CollisionComponent
|
||||
{
|
||||
typedef CollisionComponent Parent;
|
||||
|
||||
//If we're velocity based, we need a physics component on our owner to calculate the vel
|
||||
bool mUseVelocity;
|
||||
PhysicsComponent* mOwnerPhysicsComponent;
|
||||
|
||||
//If we're not using velocity, we'll just have a set direction and length we check against
|
||||
VectorF mRayDirection;
|
||||
F32 mRayLength;
|
||||
|
||||
PhysicsWorld *mPhysicsWorld;
|
||||
|
||||
Point3F mOldPosition;
|
||||
|
||||
U32 mMask;
|
||||
|
||||
public:
|
||||
DECLARE_CONOBJECT(RaycastColliderComponent);
|
||||
|
||||
RaycastColliderComponent();
|
||||
~RaycastColliderComponent();
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
static void initPersistFields();
|
||||
|
||||
virtual void onComponentAdd();
|
||||
virtual void onComponentRemove();
|
||||
|
||||
//This is called when a different component is added to our owner entity
|
||||
virtual void componentAddedToOwner(Component *comp);
|
||||
//This is called when a different component is removed from our owner entity
|
||||
virtual void componentRemovedFromOwner(Component *comp);
|
||||
|
||||
virtual void processTick();
|
||||
virtual void interpolateTick(F32 dt);
|
||||
virtual void advanceTime(F32 dt);
|
||||
};
|
||||
|
|
@ -0,0 +1,607 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/shapeCollisionComponent.h"
|
||||
#include "T3D/components/collision/shapeCollisionComponent_ScriptBinding.h"
|
||||
#include "T3D/components/physics/physicsComponent.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(ShapeCollisionComponent,
|
||||
"@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 ShapeCollisionComponentInstance()\n"
|
||||
"{\n"
|
||||
" template = ShapeCollisionComponentTemplate;\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")
|
||||
{ ShapeCollisionComponent::None, "None", "No mesh data." },
|
||||
{ ShapeCollisionComponent::Bounds, "Bounds", "Bounding box of the shape." },
|
||||
{ ShapeCollisionComponent::CollisionMesh, "Collision Mesh", "Specifically desingated \"collision\" meshes." },
|
||||
{ ShapeCollisionComponent::VisibleMesh, "Visible Mesh", "Rendered mesh polygons." },
|
||||
EndImplementEnumType;
|
||||
|
||||
//
|
||||
ShapeCollisionComponent::ShapeCollisionComponent() : CollisionComponent()
|
||||
{
|
||||
mFriendlyName = "Shape Collision Component";
|
||||
|
||||
mFriendlyName = "Shape Collision";
|
||||
mComponentType = "Collision";
|
||||
|
||||
mDescription = getDescriptionText("A stub component class that physics components should inherit from.");
|
||||
|
||||
mOwnerRenderInterface = NULL;
|
||||
mOwnerPhysicsComp = NULL;
|
||||
|
||||
mBlockColliding = true;
|
||||
|
||||
mCollisionType = CollisionMesh;
|
||||
mLOSType = CollisionMesh;
|
||||
mDecalType = CollisionMesh;
|
||||
|
||||
colisionMeshPrefix = StringTable->insert("Collision");
|
||||
|
||||
CollisionMoveMask = (TerrainObjectType | PlayerObjectType |
|
||||
StaticShapeObjectType | VehicleObjectType |
|
||||
VehicleBlockerObjectType | DynamicShapeObjectType | StaticObjectType | EntityObjectType | TriggerObjectType);
|
||||
|
||||
mAnimated = false;
|
||||
|
||||
mCollisionInited = false;
|
||||
}
|
||||
|
||||
ShapeCollisionComponent::~ShapeCollisionComponent()
|
||||
{
|
||||
for (S32 i = 0; i < mFields.size(); ++i)
|
||||
{
|
||||
ComponentField &field = mFields[i];
|
||||
SAFE_DELETE_ARRAY(field.mFieldDescription);
|
||||
}
|
||||
|
||||
SAFE_DELETE_ARRAY(mDescription);
|
||||
}
|
||||
|
||||
IMPLEMENT_CONOBJECT(ShapeCollisionComponent);
|
||||
|
||||
void ShapeCollisionComponent::onComponentAdd()
|
||||
{
|
||||
Parent::onComponentAdd();
|
||||
|
||||
RenderComponentInterface *renderInterface = mOwner->getComponent<RenderComponentInterface>();
|
||||
if (renderInterface)
|
||||
{
|
||||
renderInterface->onShapeInstanceChanged.notify(this, &ShapeCollisionComponent::targetShapeChanged);
|
||||
mOwnerRenderInterface = renderInterface;
|
||||
}
|
||||
|
||||
//physicsInterface
|
||||
PhysicsComponent *physicsComp = mOwner->getComponent<PhysicsComponent>();
|
||||
if (physicsComp)
|
||||
{
|
||||
mOwnerPhysicsComp = physicsComp;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PHYSICSMGR)
|
||||
{
|
||||
mPhysicsRep = PHYSICSMGR->createBody();
|
||||
}
|
||||
}
|
||||
|
||||
prepCollision();
|
||||
}
|
||||
|
||||
void ShapeCollisionComponent::onComponentRemove()
|
||||
{
|
||||
SAFE_DELETE(mPhysicsRep);
|
||||
|
||||
mOwnerPhysicsComp = nullptr;
|
||||
|
||||
mCollisionInited = false;
|
||||
|
||||
Parent::onComponentRemove();
|
||||
}
|
||||
|
||||
void ShapeCollisionComponent::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, &ShapeCollisionComponent::targetShapeChanged);
|
||||
mOwnerRenderInterface = renderInterface;
|
||||
prepCollision();
|
||||
}
|
||||
|
||||
PhysicsComponent *physicsComp = dynamic_cast<PhysicsComponent*>(comp);
|
||||
if (physicsComp)
|
||||
{
|
||||
if (mPhysicsRep)
|
||||
SAFE_DELETE(mPhysicsRep);
|
||||
|
||||
mOwnerPhysicsComp = physicsComp;
|
||||
|
||||
prepCollision();
|
||||
}
|
||||
}
|
||||
|
||||
void ShapeCollisionComponent::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, &ShapeCollisionComponent::targetShapeChanged);
|
||||
mOwnerRenderInterface = NULL;
|
||||
prepCollision();
|
||||
}
|
||||
|
||||
//physicsInterface
|
||||
PhysicsComponent *physicsComp = dynamic_cast<PhysicsComponent*>(comp);
|
||||
if (physicsComp)
|
||||
{
|
||||
mPhysicsRep = PHYSICSMGR->createBody();
|
||||
|
||||
mCollisionInited = false;
|
||||
|
||||
mOwnerPhysicsComp = nullptr;
|
||||
|
||||
prepCollision();
|
||||
}
|
||||
}
|
||||
|
||||
void ShapeCollisionComponent::checkDependencies()
|
||||
{
|
||||
}
|
||||
|
||||
void ShapeCollisionComponent::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addGroup("Collision");
|
||||
|
||||
addField("CollisionType", TypeCollisionMeshMeshType, Offset(mCollisionType, ShapeCollisionComponent),
|
||||
"The type of mesh data to use for collision queries.");
|
||||
|
||||
addField("LineOfSightType", TypeCollisionMeshMeshType, Offset(mLOSType, ShapeCollisionComponent),
|
||||
"The type of mesh data to use for collision queries.");
|
||||
|
||||
addField("DecalType", TypeCollisionMeshMeshType, Offset(mDecalType, ShapeCollisionComponent),
|
||||
"The type of mesh data to use for collision queries.");
|
||||
|
||||
addField("CollisionMeshPrefix", TypeString, Offset(colisionMeshPrefix, ShapeCollisionComponent),
|
||||
"The type of mesh data to use for collision queries.");
|
||||
|
||||
addField("BlockCollisions", TypeBool, Offset(mBlockColliding, ShapeCollisionComponent), "");
|
||||
|
||||
endGroup("Collision");
|
||||
}
|
||||
|
||||
void ShapeCollisionComponent::inspectPostApply()
|
||||
{
|
||||
// Apply any transformations set in the editor
|
||||
Parent::inspectPostApply();
|
||||
|
||||
if (isServerObject())
|
||||
{
|
||||
setMaskBits(ColliderMask);
|
||||
prepCollision();
|
||||
}
|
||||
}
|
||||
|
||||
U32 ShapeCollisionComponent::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 ShapeCollisionComponent::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 ShapeCollisionComponent::ownerTransformSet(MatrixF *mat)
|
||||
{
|
||||
/*bool isSrv = isServerObject();
|
||||
if (mPhysicsRep && mCollisionInited)
|
||||
mPhysicsRep->setTransform(mOwner->getTransform());*/
|
||||
}
|
||||
|
||||
//Setup
|
||||
void ShapeCollisionComponent::targetShapeChanged(RenderComponentInterface* instanceInterface)
|
||||
{
|
||||
prepCollision();
|
||||
}
|
||||
|
||||
void ShapeCollisionComponent::prepCollision()
|
||||
{
|
||||
if (!mOwner)
|
||||
return;
|
||||
|
||||
// Let the client know that the collision was updated
|
||||
setMaskBits(ColliderMask);
|
||||
|
||||
mOwner->disableCollision();
|
||||
|
||||
if (mCollisionType == None)
|
||||
return;
|
||||
|
||||
//Physics API
|
||||
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());
|
||||
|
||||
mCollisionInited = true;
|
||||
}
|
||||
}
|
||||
|
||||
mOwner->enableCollision();
|
||||
|
||||
onCollisionChanged.trigger(colShape);
|
||||
}
|
||||
|
||||
//Update
|
||||
void ShapeCollisionComponent::processTick()
|
||||
{
|
||||
if (!isActive())
|
||||
return;
|
||||
|
||||
//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 ShapeCollisionComponent::updatePhysics()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PhysicsCollision* ShapeCollisionComponent::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("ShapeCollisionComponent::updatePhysics: Cannot use visible mesh collisions with an animated mesh!");
|
||||
}
|
||||
|
||||
return colShape;
|
||||
}
|
||||
|
||||
bool ShapeCollisionComponent::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* ShapeCollisionComponent::buildColShapes()
|
||||
{
|
||||
PROFILE_SCOPE(ShapeCollisionComponent_buildColShapes);
|
||||
|
||||
PhysicsCollision *colShape = NULL;
|
||||
U32 surfaceKey = 0;
|
||||
|
||||
TSShape* shape = mOwnerRenderInterface->getShape();
|
||||
|
||||
if (shape == nullptr)
|
||||
return nullptr;
|
||||
|
||||
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;
|
||||
}
|
||||
137
Engine/source/T3D/components/collision/shapeCollisionComponent.h
Normal file
137
Engine/source/T3D/components/collision/shapeCollisionComponent.h
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
#pragma once
|
||||
#ifndef SHAPE_COLLISION_COMPONENT_H
|
||||
#define SHAPE_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_COMPONENT_H
|
||||
#include "T3D/components/collision/collisionComponent.h"
|
||||
#endif
|
||||
#ifndef RENDER_COMPONENT_INTERFACE_H
|
||||
#include "T3D/components/render/renderComponentInterface.h"
|
||||
#endif
|
||||
|
||||
class TSShapeInstance;
|
||||
class SceneRenderState;
|
||||
class ShapeCollisionComponent;
|
||||
class PhysicsBody;
|
||||
class PhysicsWorld;
|
||||
|
||||
class ShapeCollisionComponent : public CollisionComponent
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
protected:
|
||||
MeshType mCollisionType;
|
||||
MeshType mDecalType;
|
||||
MeshType mLOSType;
|
||||
|
||||
Vector<S32> mCollisionDetails;
|
||||
Vector<S32> mLOSDetails;
|
||||
|
||||
StringTableEntry colisionMeshPrefix;
|
||||
|
||||
RenderComponentInterface* mOwnerRenderInterface;
|
||||
|
||||
PhysicsComponent* mOwnerPhysicsComp;
|
||||
|
||||
//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:
|
||||
ShapeCollisionComponent();
|
||||
virtual ~ShapeCollisionComponent();
|
||||
DECLARE_CONOBJECT(ShapeCollisionComponent);
|
||||
|
||||
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();
|
||||
|
||||
//Setup
|
||||
virtual void prepCollision();
|
||||
|
||||
//Updates
|
||||
virtual void processTick();
|
||||
|
||||
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();
|
||||
|
||||
};
|
||||
|
||||
typedef ShapeCollisionComponent::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/shapeCollisionComponent.h"
|
||||
#include "materials/baseMatInstance.h"
|
||||
|
||||
DefineEngineMethod(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();
|
||||
}
|
||||
|
||||
DefineEngineMethod(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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(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;
|
||||
}
|
||||
|
||||
DefineEngineMethod(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(ShapeCollisionComponent, 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(ShapeCollisionComponent, 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(ShapeCollisionComponent, 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(ShapeCollisionComponent, 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(ShapeCollisionComponent, 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);
|
||||
}
|
||||
216
Engine/source/T3D/components/collision/simpleHitboxComponent.cpp
Normal file
216
Engine/source/T3D/components/collision/simpleHitboxComponent.cpp
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
#include "simpleHitboxComponent.h"
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(SimpleHitboxComponent);
|
||||
|
||||
SimpleHitboxComponent::SimpleHitboxComponent() :
|
||||
// location of head, torso, legs
|
||||
mBoxHeadPercentage(0.85f),
|
||||
mBoxTorsoPercentage(0.55f),
|
||||
mBoxLeftPercentage(0),
|
||||
mBoxRightPercentage(1),
|
||||
mBoxBackPercentage(0),
|
||||
mBoxFrontPercentage(1),
|
||||
mIsProne(false)
|
||||
{
|
||||
}
|
||||
|
||||
SimpleHitboxComponent::~SimpleHitboxComponent()
|
||||
{
|
||||
}
|
||||
|
||||
void SimpleHitboxComponent::initPersistFields()
|
||||
{
|
||||
addGroup("Hitbox");
|
||||
addField("headPercentage", TypeF32, Offset(mBoxHeadPercentage, SimpleHitboxComponent), "");
|
||||
addField("torsoPercentage", TypeF32, Offset(mBoxTorsoPercentage, SimpleHitboxComponent), "");
|
||||
addField("leftPercentage", TypeF32, Offset(mBoxLeftPercentage, SimpleHitboxComponent), "");
|
||||
addField("rightPercentage", TypeF32, Offset(mBoxRightPercentage, SimpleHitboxComponent), "");
|
||||
addField("backPercentage", TypeF32, Offset(mBoxBackPercentage, SimpleHitboxComponent), "");
|
||||
addField("frontPercentage", TypeF32, Offset(mBoxFrontPercentage, SimpleHitboxComponent), "");
|
||||
|
||||
addField("isProne", TypeF32, Offset(mIsProne, SimpleHitboxComponent), "");
|
||||
endGroup("Hitbox");
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
void SimpleHitboxComponent::onComponentAdd()
|
||||
{
|
||||
Parent::onComponentAdd();
|
||||
}
|
||||
|
||||
void SimpleHitboxComponent::componentAddedToOwner(Component *comp)
|
||||
{
|
||||
Parent::componentAddedToOwner(comp);
|
||||
}
|
||||
|
||||
void SimpleHitboxComponent::componentRemovedFromOwner(Component *comp)
|
||||
{
|
||||
Parent::componentRemovedFromOwner(comp);
|
||||
}
|
||||
|
||||
void SimpleHitboxComponent::processTick()
|
||||
{
|
||||
Parent::processTick();
|
||||
}
|
||||
|
||||
void SimpleHitboxComponent::interpolateTick(F32 dt)
|
||||
{
|
||||
Parent::interpolateTick(dt);
|
||||
}
|
||||
|
||||
void SimpleHitboxComponent::advanceTime(F32 dt)
|
||||
{
|
||||
Parent::advanceTime(dt);
|
||||
}
|
||||
|
||||
void SimpleHitboxComponent::getDamageLocation(const Point3F& in_rPos, const char *&out_rpVert, const char *&out_rpQuad)
|
||||
{
|
||||
Point3F newPoint;
|
||||
mOwner->getWorldToObj().mulP(in_rPos, &newPoint);
|
||||
|
||||
Point3F boxSize = mOwner->getObjBox().getExtents();
|
||||
|
||||
F32 boxHeight = boxSize.z;
|
||||
F32 pointHeight = newPoint.z;
|
||||
|
||||
if (mIsProne)
|
||||
pointHeight = newPoint.y; //this assumes we're y-forward
|
||||
|
||||
F32 zTorso = mBoxTorsoPercentage;
|
||||
F32 zHead = mBoxHeadPercentage;
|
||||
|
||||
zTorso *= boxHeight;
|
||||
zHead *= boxHeight;
|
||||
|
||||
if (pointHeight <= zTorso)
|
||||
out_rpVert = "legs";
|
||||
else if (pointHeight <= zHead)
|
||||
out_rpVert = "torso";
|
||||
else
|
||||
out_rpVert = "head";
|
||||
|
||||
if (dStrcmp(out_rpVert, "head") != 0)
|
||||
{
|
||||
if (newPoint.y >= 0.0f)
|
||||
{
|
||||
if (newPoint.x <= 0.0f)
|
||||
out_rpQuad = "front_left";
|
||||
else
|
||||
out_rpQuad = "front_right";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (newPoint.x <= 0.0f)
|
||||
out_rpQuad = "back_left";
|
||||
else
|
||||
out_rpQuad = "back_right";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
F32 backToFront = boxSize.x;
|
||||
F32 leftToRight = boxSize.y;
|
||||
|
||||
F32 backPoint = backToFront * mBoxBackPercentage;
|
||||
F32 frontPoint = backToFront * mBoxFrontPercentage;
|
||||
F32 leftPoint = leftToRight * mBoxLeftPercentage;
|
||||
F32 rightPoint = leftToRight * mBoxRightPercentage;
|
||||
|
||||
S32 index = 0;
|
||||
if (newPoint.y < backPoint)
|
||||
index += 0;
|
||||
else if (newPoint.y >= frontPoint)
|
||||
index += 3;
|
||||
else
|
||||
index += 6;
|
||||
|
||||
if (newPoint.x < leftPoint)
|
||||
index += 0;
|
||||
else if (newPoint.x >= rightPoint)
|
||||
index += 1;
|
||||
else
|
||||
index += 2;
|
||||
|
||||
switch (index)
|
||||
{
|
||||
case 0: out_rpQuad = "left_back"; break;
|
||||
case 1: out_rpQuad = "middle_back"; break;
|
||||
case 2: out_rpQuad = "right_back"; break;
|
||||
case 3: out_rpQuad = "left_middle"; break;
|
||||
case 4: out_rpQuad = "middle_middle"; break;
|
||||
case 5: out_rpQuad = "right_middle"; break;
|
||||
case 6: out_rpQuad = "left_front"; break;
|
||||
case 7: out_rpQuad = "middle_front"; break;
|
||||
case 8: out_rpQuad = "right_front"; break;
|
||||
|
||||
default:
|
||||
AssertFatal(0, "Bad non-tant index");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
DefineEngineMethod(SimpleHitboxComponent, getDamageLocation, const char*, (Point3F pos), ,
|
||||
"@brief Get the named damage location and modifier for a given world position.\n\n"
|
||||
|
||||
"the Player object can simulate different hit locations based on a pre-defined set "
|
||||
"of PlayerData defined percentages. These hit percentages divide up the Player's "
|
||||
"bounding box into different regions. The diagram below demonstrates how the various "
|
||||
"PlayerData properties split up the bounding volume:\n\n"
|
||||
|
||||
"<img src=\"images/player_damageloc.png\">\n\n"
|
||||
|
||||
"While you may pass in any world position and getDamageLocation() will provide a best-fit "
|
||||
"location, you should be aware that this can produce some interesting results. For example, "
|
||||
"any position that is above PlayerData::boxHeadPercentage will be considered a 'head' hit, even "
|
||||
"if the world position is high in the sky. Therefore it may be wise to keep the passed in point "
|
||||
"to somewhere on the surface of, or within, the Player's bounding volume.\n\n"
|
||||
|
||||
"@note This method will not return an accurate location when the player is "
|
||||
"prone or swimming.\n\n"
|
||||
|
||||
"@param pos A world position for which to retrieve a body region on this player.\n"
|
||||
|
||||
"@return a string containing two words (space separated strings), where the "
|
||||
"first is a location and the second is a modifier.\n\n"
|
||||
|
||||
"Posible locations:<ul>"
|
||||
"<li>head</li>"
|
||||
"<li>torso</li>"
|
||||
"<li>legs</li></ul>\n"
|
||||
|
||||
"Head modifiers:<ul>"
|
||||
"<li>left_back</li>"
|
||||
"<li>middle_back</li>"
|
||||
"<li>right_back</li>"
|
||||
"<li>left_middle</li>"
|
||||
"<li>middle_middle</li>"
|
||||
"<li>right_middle</li>"
|
||||
"<li>left_front</li>"
|
||||
"<li>middle_front</li>"
|
||||
"<li>right_front</li></ul>\n"
|
||||
|
||||
"Legs/Torso modifiers:<ul>"
|
||||
"<li>front_left</li>"
|
||||
"<li>front_right</li>"
|
||||
"<li>back_left</li>"
|
||||
"<li>back_right</li></ul>\n"
|
||||
|
||||
"@see PlayerData::boxHeadPercentage\n"
|
||||
"@see PlayerData::boxHeadFrontPercentage\n"
|
||||
"@see PlayerData::boxHeadBackPercentage\n"
|
||||
"@see PlayerData::boxHeadLeftPercentage\n"
|
||||
"@see PlayerData::boxHeadRightPercentage\n"
|
||||
"@see PlayerData::boxTorsoPercentage\n"
|
||||
)
|
||||
{
|
||||
const char *buffer1;
|
||||
const char *buffer2;
|
||||
|
||||
object->getDamageLocation(pos, buffer1, buffer2);
|
||||
|
||||
static const U32 bufSize = 128;
|
||||
char *buff = Con::getReturnBuffer(bufSize);
|
||||
dSprintf(buff, bufSize, "%s %s", buffer1, buffer2);
|
||||
return buff;
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "T3D/components/component.h"
|
||||
|
||||
class SimpleHitboxComponent : public Component
|
||||
{
|
||||
typedef Component Parent;
|
||||
|
||||
// location of head, torso, legs
|
||||
F32 mBoxHeadPercentage;
|
||||
F32 mBoxTorsoPercentage;
|
||||
|
||||
// damage locations
|
||||
F32 mBoxLeftPercentage;
|
||||
F32 mBoxRightPercentage;
|
||||
F32 mBoxBackPercentage;
|
||||
F32 mBoxFrontPercentage;
|
||||
|
||||
// Is our hitbox horizontal, usually due to being prone, swimming, etc
|
||||
bool mIsProne;
|
||||
|
||||
public:
|
||||
SimpleHitboxComponent();
|
||||
~SimpleHitboxComponent();
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
virtual void onComponentAdd();
|
||||
virtual void componentAddedToOwner(Component *comp);
|
||||
virtual void componentRemovedFromOwner(Component *comp);
|
||||
|
||||
virtual void processTick();
|
||||
virtual void interpolateTick(F32 dt);
|
||||
virtual void advanceTime(F32 dt);
|
||||
|
||||
void getDamageLocation(const Point3F& in_rPos, const char *&out_rpVert, const char *&out_rpQuad);
|
||||
|
||||
DECLARE_CONOBJECT(SimpleHitboxComponent);
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue