Updates to various components, added a few new ones.

This commit is contained in:
Areloch 2019-02-24 01:50:38 -06:00
parent e0627973fb
commit dac8d6e1fd
52 changed files with 4566 additions and 1799 deletions

View file

@ -74,7 +74,7 @@ AnimationComponent::AnimationComponent() : Component()
mNetworked = true;
mFriendlyName = "Animation(Component)";
mComponentType = "Render";
mComponentType = "Animation";
mDescription = getDescriptionText("Allows a rendered mesh to be animated");

View file

@ -101,7 +101,7 @@ public:
TSShape* getShape();
void targetShapeChanged(RenderComponentInterface* instanceInterface);
virtual void targetShapeChanged(RenderComponentInterface* instanceInterface);
virtual void processTick();
virtual void advanceTime(F32 dt);

View file

@ -92,13 +92,13 @@ void SoundComponent::onComponentAdd()
{
Parent::onComponentAdd();
Con::printf("We were added to an entity! SoundComponent reporting in for owner entity %i", mOwner->getId());
//Con::printf("We were added to an entity! SoundComponent reporting in for owner entity %i", mOwner->getId());
}
//This is called when the component has been removed from an entity
void SoundComponent::onComponentRemove()
{
Con::printf("We were removed from our entity! SoundComponent signing off for owner entity %i", mOwner->getId());
//Con::printf("We were removed from our entity! SoundComponent signing off for owner entity %i", mOwner->getId());
Parent::onComponentRemove();
}
@ -113,14 +113,14 @@ void SoundComponent::componentAddedToOwner(Component *comp)
playAudio(slotNum, mSoundFile[slotNum]);
}
}
Con::printf("Our owner entity has a new component being added! SoundComponent welcomes component %i of type %s", comp->getId(), comp->getClassRep()->getNameSpace());
//Con::printf("Our owner entity has a new component being added! SoundComponent welcomes component %i of type %s", comp->getId(), comp->getClassRep()->getNameSpace());
}
//This is called any time a component is removed from an entity. Every component current owned by the entity is informed of the event.
//This allows cleanup and dependency management.
void SoundComponent::componentRemovedFromOwner(Component *comp)
{
Con::printf("Our owner entity has a removed a component! SoundComponent waves farewell to component %i of type %s", comp->getId(), comp->getClassRep()->getNameSpace());
//Con::printf("Our owner entity has a removed a component! SoundComponent waves farewell to component %i of type %s", comp->getId(), comp->getClassRep()->getNameSpace());
}
//Regular init persist fields function to set up static fields.

File diff suppressed because it is too large Load diff

View file

@ -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

View file

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

View file

@ -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

View file

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

View file

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

View file

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

View file

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

View 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

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

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

View file

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

View file

@ -456,7 +456,7 @@ void Component::checkComponentFieldModified(const char* slotName, const char* ne
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void Component::addComponentField(const char *fieldName, const char *desc, const char *type, const char *defaultValue /* = NULL */, const char *userData /* = NULL */, /*const char* dependency /* = NULL *//*,*/ bool hidden /* = false */)
void Component::addComponentField(const char *fieldName, const char *desc, const char *type, const char *defaultValue /* = NULL */, const char *userData /* = NULL */, /*const char* dependency /* = NULL *//*,*/ bool hidden /* = false */, const char* customLabel /* = ""*/)
{
StringTableEntry stFieldName = StringTable->insert(fieldName);
@ -467,6 +467,12 @@ void Component::addComponentField(const char *fieldName, const char *desc, const
}
ComponentField field;
if (customLabel != "")
field.mFieldLabel = customLabel;
else
field.mFieldLabel = stFieldName;
field.mFieldName = stFieldName;
//find the field type

View file

@ -20,6 +20,8 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#pragma once
#ifndef COMPONENT_H
#define COMPONENT_H
@ -44,6 +46,7 @@ class Namespace;
struct ComponentField
{
StringTableEntry mFieldLabel;
StringTableEntry mFieldName;
StringTableEntry mFieldDescription;
@ -145,7 +148,7 @@ public:
/// @param type The Type of field that this is, example 'Text' or 'Bool'
/// @param defaultValue The Default value of this field
/// @param userData An extra optional field that can be used for user data
void addComponentField(const char *fieldName, const char *desc, const char *type, const char *defaultValue = NULL, const char *userData = NULL, bool hidden = false);
void addComponentField(const char *fieldName, const char *desc, const char *type, const char *defaultValue = NULL, const char *userData = NULL, bool hidden = false, const char* customLabel = "");
/// Returns the number of ComponentField's on this template
inline S32 getComponentFieldCount() { return mFields.size(); };

View file

@ -0,0 +1,149 @@
//-----------------------------------------------------------------------------
// 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/game/controlObjectComponent.h"
//////////////////////////////////////////////////////////////////////////
// Constructor/Destructor
//////////////////////////////////////////////////////////////////////////
ControlObjectComponent::ControlObjectComponent() : Component(),
mOwnerConnection(nullptr),
mOwnerConnectionId(0)
{
mFriendlyName = "Control Object";
mComponentType = "Game";
mDescription = getDescriptionText("Allows owner entity to be controlled by a client.");
}
ControlObjectComponent::~ControlObjectComponent()
{
}
IMPLEMENT_CO_NETOBJECT_V1(ControlObjectComponent);
bool ControlObjectComponent::onAdd()
{
if (!Parent::onAdd())
return false;
return true;
}
void ControlObjectComponent::onRemove()
{
Parent::onRemove();
}
//This is mostly a catch for situations where the behavior is re-added to the object and the like and we may need to force an update to the behavior
void ControlObjectComponent::onComponentAdd()
{
Parent::onComponentAdd();
if (mOwnerConnection && mOwnerConnection->getControlObject() == nullptr)
{
mOwnerConnection->setControlObject(mOwner);
mOwnerConnectionId = mOwnerConnection->getId();
}
}
void ControlObjectComponent::onComponentRemove()
{
Parent::onComponentRemove();
if (mOwnerConnection)
{
mOwnerConnection->setControlObject(nullptr);
mOwnerConnectionId = 0;
}
}
void ControlObjectComponent::initPersistFields()
{
Parent::initPersistFields();
addField("clientOwner", TypeS32, Offset(mOwnerConnectionId, ControlObjectComponent), "Client connection ID");
}
void ControlObjectComponent::setConnectionControlObject(GameConnection* conn)
{
if (conn)
{
if (conn->getControlObject() == nullptr)
{
conn->setControlObject(mOwner);
mOwnerConnectionId = conn->getId();
}
else
{
//Inform the old control object it's no longer in control?
conn->setControlObject(mOwner);
mOwnerConnectionId = conn->getId();
}
}
}
void ControlObjectComponent::onClientConnect(GameConnection* conn)
{
if (conn && conn->getControlObject() == nullptr)
{
conn->setControlObject(mOwner);
mOwnerConnectionId = conn->getId();
}
}
void ControlObjectComponent::onClientDisconnect(GameConnection* conn)
{
if (conn && conn->getControlObject() == mOwner)
{
conn->setControlObject(nullptr);
mOwnerConnectionId = 0;
}
}
DefineEngineMethod(ControlObjectComponent, onClientConnect, void, (GameConnection* conn), (nullAsType<GameConnection*>()),
"Triggers a signal call to all components for a certain function.")
{
if (conn == nullptr)
return;
object->onClientConnect(conn);
}
DefineEngineMethod(ControlObjectComponent, onClientDisconnect, void, (GameConnection* conn), (nullAsType<GameConnection*>()),
"Triggers a signal call to all components for a certain function.")
{
if (conn == nullptr)
return;
object->onClientDisconnect(conn);
}
DefineEngineMethod(ControlObjectComponent, setConnectionControlObject, void, (GameConnection* conn), (nullAsType<GameConnection*>()),
"Triggers a signal call to all components for a certain function.")
{
if (conn == nullptr)
return;
object->setConnectionControlObject(conn);
}

View file

@ -0,0 +1,30 @@
#pragma once
#include "T3D/components/component.h"
#include "T3D/gameBase/gameConnection.h"
class ControlObjectComponent : public Component
{
typedef Component Parent;
GameConnection* mOwnerConnection;
S32 mOwnerConnectionId;
public:
ControlObjectComponent();
~ControlObjectComponent();
DECLARE_CONOBJECT(ControlObjectComponent);
virtual bool onAdd();
virtual void onRemove();
static void initPersistFields();
virtual void onComponentAdd();
virtual void onComponentRemove();
void onClientConnect(GameConnection* conn);
void onClientDisconnect(GameConnection* conn);
void setConnectionControlObject(GameConnection* conn);
};

View file

@ -0,0 +1,359 @@
#include "T3D/components/game/followPathComponent.h"
IMPLEMENT_CO_DATABLOCK_V1(FollowPathComponent);
IMPLEMENT_CALLBACK(FollowPathComponent, onNode, void, (S32 node), (node),
"A script callback that indicates the path camera has arrived at a specific node in its path. Server side only.\n"
"@param Node Unique ID assigned to this node.\n");
FollowPathComponent::FollowPathComponent()
{
delta.time = 0;
delta.timeVec = 0;
//mDataBlock = 0;
mState = Forward;
mNodeBase = 0;
mNodeCount = 0;
mPosition = 0;
mTarget = 0;
mTargetSet = false;
}
FollowPathComponent::~FollowPathComponent()
{
}
bool FollowPathComponent::onAdd()
{
if (!Parent::onAdd())
return false;
/*// Initialize from the current transform.
if (!mNodeCount)
{
QuatF rot(getTransform());
Point3F pos = getPosition();
mSpline.removeAll();
mSpline.push_back(new CameraSpline::Knot(pos, rot, 1,
CameraSpline::Knot::NORMAL, CameraSpline::Knot::SPLINE));
mNodeCount = 1;
}
//
mObjBox.maxExtents = mObjScale;
mObjBox.minExtents = mObjScale;
mObjBox.minExtents.neg();
resetWorldBox();*/
return true;
}
void FollowPathComponent::onRemove()
{
Parent::onRemove();
}
void FollowPathComponent::initPersistFields()
{
Parent::initPersistFields();
}
void FollowPathComponent::componentAddedToOwner(Component *comp)
{
Parent::componentAddedToOwner(comp);
}
void FollowPathComponent::componentRemovedFromOwner(Component *comp)
{
Parent::componentRemovedFromOwner(comp);
}
void FollowPathComponent::processTick()
{
Parent::processTick();
// Move to new time
advancePosition(TickMs);
// Set new position
MatrixF mat;
interpolateMat(mPosition, &mat);
mOwner->setTransform(mat);
mOwner->updateContainer();
}
void FollowPathComponent::interpolateTick(F32 dt)
{
Parent::interpolateTick(dt);
MatrixF mat;
interpolateMat(delta.time + (delta.timeVec * dt), &mat);
mOwner->setRenderTransform(mat);
}
void FollowPathComponent::interpolateMat(F32 pos, MatrixF* mat)
{
/*CameraSpline::Knot knot;
mSpline.value(pos - mNodeBase, &knot);
knot.mRotation.setMatrix(mat);
mat->setPosition(knot.mPosition);*/
}
void FollowPathComponent::advanceTime(F32 dt)
{
Parent::advanceTime(dt);
}
void FollowPathComponent::advancePosition(S32 ms)
{
/*delta.timeVec = mPosition;
// Advance according to current speed
if (mState == Forward) {
mPosition = mSpline.advanceTime(mPosition - mNodeBase, ms);
if (mPosition > F32(mNodeCount - 1))
mPosition = F32(mNodeCount - 1);
mPosition += (F32)mNodeBase;
if (mTargetSet && mPosition >= mTarget) {
mTargetSet = false;
mPosition = mTarget;
mState = Stop;
}
}
else
if (mState == Backward) {
mPosition = mSpline.advanceTime(mPosition - mNodeBase, -ms);
if (mPosition < 0)
mPosition = 0;
mPosition += mNodeBase;
if (mTargetSet && mPosition <= mTarget) {
mTargetSet = false;
mPosition = mTarget;
mState = Stop;
}
}
// Script callbacks
if (int(mPosition) != int(delta.timeVec))
onNode(int(mPosition));
// Set frame interpolation
delta.time = mPosition;
delta.timeVec -= mPosition;*/
}
//----------------------------------------------------------------------------
/*void FollowPathComponent::getCameraTransform(F32* pos, MatrixF* mat)
{
// Overide the ShapeBase method to skip all the first/third person support.
getRenderEyeTransform(mat);
// Apply Camera FX.
mat->mul(gCamFXMgr.getTrans());
}*/
//----------------------------------------------------------------------------
void FollowPathComponent::setPosition(F32 pos)
{
mPosition = mClampF(pos, (F32)mNodeBase, (F32)(mNodeBase + mNodeCount - 1));
MatrixF mat;
interpolateMat(mPosition, &mat);
mOwner->setTransform(mat);
setMaskBits(PositionMask);
}
void FollowPathComponent::setTarget(F32 pos)
{
mTarget = pos;
mTargetSet = true;
if (mTarget > mPosition)
mState = Forward;
else
if (mTarget < mPosition)
mState = Backward;
else {
mTargetSet = false;
mState = Stop;
}
setMaskBits(TargetMask | StateMask);
}
void FollowPathComponent::setState(State s)
{
mState = s;
setMaskBits(StateMask);
}
//-----------------------------------------------------------------------------
void FollowPathComponent::reset(F32 speed)
{
/*CameraSpline::Knot *knot = new CameraSpline::Knot;
mSpline.value(mPosition - mNodeBase, knot);
if (speed)
knot->mSpeed = speed;
mSpline.removeAll();
mSpline.push_back(knot);
mNodeBase = 0;
mNodeCount = 1;
mPosition = 0;
mTargetSet = false;
mState = Forward;
setMaskBits(StateMask | PositionMask | WindowMask | TargetMask);*/
}
/*void FollowPathComponent::pushBack(CameraSpline::Knot *knot)
{
// Make room at the end
if (mNodeCount == NodeWindow) {
delete mSpline.remove(mSpline.getKnot(0));
mNodeBase++;
}
else
mNodeCount++;
// Fill in the new node
mSpline.push_back(knot);
setMaskBits(WindowMask);
// Make sure the position doesn't fall off
if (mPosition < mNodeBase) {
mPosition = (F32)mNodeBase;
setMaskBits(PositionMask);
}
}
void FollowPathComponent::pushFront(CameraSpline::Knot *knot)
{
// Make room at the front
if (mNodeCount == NodeWindow)
delete mSpline.remove(mSpline.getKnot(mNodeCount));
else
mNodeCount++;
mNodeBase--;
// Fill in the new node
mSpline.push_front(knot);
setMaskBits(WindowMask);
// Make sure the position doesn't fall off
if (mPosition > F32(mNodeBase + (NodeWindow - 1)))
{
mPosition = F32(mNodeBase + (NodeWindow - 1));
setMaskBits(PositionMask);
}
}*/
void FollowPathComponent::popFront()
{
/*if (mNodeCount < 2)
return;
// Remove the first node. Node base and position are unaffected.
mNodeCount--;
delete mSpline.remove(mSpline.getKnot(0));
if (mPosition > 0)
mPosition--;*/
}
//----------------------------------------------------------------------------
void FollowPathComponent::onNode(S32 node)
{
//if (!isGhost())
// onNode_callback(node);
}
/*U32 FollowPathComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
{
Parent::packUpdate(con, mask, stream);
if (stream->writeFlag(mask & StateMask))
stream->writeInt(mState, StateBits);
if (stream->writeFlag(mask & PositionMask))
stream->write(mPosition);
if (stream->writeFlag(mask & TargetMask))
if (stream->writeFlag(mTargetSet))
stream->write(mTarget);
if (stream->writeFlag(mask & WindowMask)) {
stream->write(mNodeBase);
stream->write(mNodeCount);
for (S32 i = 0; i < mNodeCount; i++) {
CameraSpline::Knot *knot = mSpline.getKnot(i);
mathWrite(*stream, knot->mPosition);
mathWrite(*stream, knot->mRotation);
stream->write(knot->mSpeed);
stream->writeInt(knot->mType, CameraSpline::Knot::NUM_TYPE_BITS);
stream->writeInt(knot->mPath, CameraSpline::Knot::NUM_PATH_BITS);
}
}
// The rest of the data is part of the control object packet update.
// If we're controlled by this client, we don't need to send it.
if (stream->writeFlag(getControllingClient() == con && !(mask & InitialUpdateMask)))
return 0;
return 0;
}
void FollowPathComponent::unpackUpdate(NetConnection *con, BitStream *stream)
{
Parent::unpackUpdate(con, stream);
// StateMask
if (stream->readFlag())
mState = stream->readInt(StateBits);
// PositionMask
if (stream->readFlag())
{
stream->read(&mPosition);
delta.time = mPosition;
delta.timeVec = 0;
}
// TargetMask
if (stream->readFlag())
{
mTargetSet = stream->readFlag();
if (mTargetSet)
stream->read(&mTarget);
}
// WindowMask
if (stream->readFlag())
{
mSpline.removeAll();
stream->read(&mNodeBase);
stream->read(&mNodeCount);
for (S32 i = 0; i < mNodeCount; i++)
{
CameraSpline::Knot *knot = new CameraSpline::Knot();
mathRead(*stream, &knot->mPosition);
mathRead(*stream, &knot->mRotation);
stream->read(&knot->mSpeed);
knot->mType = (CameraSpline::Knot::Type)stream->readInt(CameraSpline::Knot::NUM_TYPE_BITS);
knot->mPath = (CameraSpline::Knot::Path)stream->readInt(CameraSpline::Knot::NUM_PATH_BITS);
mSpline.push_back(knot);
}
}
// Controlled by the client?
if (stream->readFlag())
return;
}*/

View file

@ -0,0 +1,82 @@
#pragma once
#include "T3D/components/component.h"
class FollowPathComponent : public Component
{
typedef Component Parent;
enum State {
Forward,
Backward,
Stop,
StateBits = 3
};
enum MaskBits {
WindowMask = Parent::NextFreeMask,
PositionMask = Parent::NextFreeMask + 1,
TargetMask = Parent::NextFreeMask + 2,
StateMask = Parent::NextFreeMask + 3,
NextFreeMask = Parent::NextFreeMask << 1
};
struct StateDelta {
F32 time;
F32 timeVec;
};
StateDelta delta;
enum Constants {
NodeWindow = 128 // Maximum number of active nodes
};
//
//PathCameraData* mDataBlock;
//CameraSpline mSpline;
S32 mNodeBase;
S32 mNodeCount;
F32 mPosition;
S32 mState;
F32 mTarget;
bool mTargetSet;
void interpolateMat(F32 pos, MatrixF* mat);
void advancePosition(S32 ms);
public:
DECLARE_CONOBJECT(FollowPathComponent);
FollowPathComponent();
~FollowPathComponent();
virtual bool onAdd();
virtual void onRemove();
static void initPersistFields();
//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);
//
//void onEditorEnable();
//void onEditorDisable();
void onNode(S32 node);
void reset(F32 speed = 1);
//void pushFront(CameraSpline::Knot *knot);
//void pushBack(CameraSpline::Knot *knot);
void popFront();
void setPosition(F32 pos);
void setTarget(F32 pos);
void setState(State s);
DECLARE_CALLBACK(void, onNode, (S32 node));
};

View file

@ -0,0 +1,171 @@
//-----------------------------------------------------------------------------
// 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/game/interactComponent.h"
#include "scene/sceneContainer.h"
#include "T3D/components/game/interactableComponent.h"
//////////////////////////////////////////////////////////////////////////
// Constructor/Destructor
//////////////////////////////////////////////////////////////////////////
InteractComponent::InteractComponent() : Component(),
mUseRaycastInteract(true),
mUseRenderedRaycast(false),
mUseRadiusInteract(true),
mInteractRadius(1.5),
mInteractRayDist(1.5),
mUseNaturalReach(false)
{
mFriendlyName = "Interact";
mComponentType = "Game";
mDescription = getDescriptionText("Allows owner entity interact.");
}
InteractComponent::~InteractComponent()
{
}
IMPLEMENT_CO_NETOBJECT_V1(InteractComponent);
bool InteractComponent::onAdd()
{
if (!Parent::onAdd())
return false;
return true;
}
void InteractComponent::onRemove()
{
Parent::onRemove();
}
//This is mostly a catch for situations where the behavior is re-added to the object and the like and we may need to force an update to the behavior
void InteractComponent::onComponentAdd()
{
Parent::onComponentAdd();
}
void InteractComponent::onComponentRemove()
{
Parent::onComponentRemove();
}
void InteractComponent::initPersistFields()
{
Parent::initPersistFields();
}
void InteractComponent::processTick()
{
if (isClientObject())
return; //this shouldn't happen
//First, if we're doing rays, try that as if we get a hit, we can always assume that's the "correct" option
if (mUseRaycastInteract)
{
mOwner->disableCollision();
Point3F start = mOwner->getPosition();
Point3F end = mOwner->getTransform().getForwardVector() * mInteractRayDist + start;
RayInfo rinfo;
S32 ret = 0;
if (mUseRenderedRaycast)
{
rinfo.generateTexCoord = true;
if (gServerContainer.castRayRendered(mOwner->getPosition(), end, EntityObjectType, &rinfo) == true)
ret = rinfo.object->getId();
}
else
{
if (gServerContainer.castRay(mOwner->getPosition(), end, EntityObjectType, &rinfo) == true)
ret = rinfo.object->getId();
}
mOwner->enableCollision();
Entity* hitEntity = static_cast<Entity*>(rinfo.object);
if (hitEntity)
{
//call on that badboy!
InteractableComponent* iComp = hitEntity->getComponent<InteractableComponent>();
if (iComp)
{
iComp->interact(this, rinfo);
return;
}
}
}
//If not using rays or no hit, then do the radius search if we have it
if (mUseRadiusInteract)
{
gServerContainer.initRadiusSearch(mOwner->getPosition(), mInteractRadius, EntityObjectType);
F32 lastBestDist = 9999;
F32 lastBestWeight = 0;
Entity* bestFitEntity = nullptr;
Entity* e = static_cast<Entity*>(gServerContainer.containerSearchNextObject());
while (e != nullptr)
{
InteractableComponent* iComp = e->getComponent<InteractableComponent>();
if (iComp != nullptr)
{
F32 weight = iComp->getWeight();
VectorF distVec = e->getPosition() - mOwner->getPosition();
F32 dist = distVec.len();
//If the weight is better, always pick it
if (weight > lastBestWeight)
{
lastBestDist = dist;
lastBestWeight = weight;
bestFitEntity = e;
}
//Otherwise, if the weight is matched and the distance is closer, pick that
else if (weight >= lastBestWeight && lastBestDist < dist)
{
lastBestWeight = weight;
bestFitEntity = e;
}
}
e = static_cast<Entity*>(gServerContainer.containerSearchNextObject()); //loop 'round
}
if (bestFitEntity)
{
//call on that badboy!
InteractableComponent* iComp = bestFitEntity->getComponent<InteractableComponent>();
iComp->interact(this);
}
}
}

View file

@ -0,0 +1,34 @@
#pragma once
#include "T3D/components/component.h"
class InteractComponent : public Component
{
typedef Component Parent;
bool mUseRaycastInteract;
bool mUseRenderedRaycast;
bool mUseRadiusInteract;
F32 mInteractRadius;
F32 mInteractRayDist;
//Adjusts the length of the ray based on the idea of further reach if you look down because of crouching
bool mUseNaturalReach;
public:
InteractComponent();
~InteractComponent();
DECLARE_CONOBJECT(InteractComponent);
virtual bool onAdd();
virtual void onRemove();
static void initPersistFields();
virtual void onComponentAdd();
virtual void onComponentRemove();
virtual void processTick();
};

View file

@ -0,0 +1,94 @@
//-----------------------------------------------------------------------------
// 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/game/InteractableComponent.h"
//////////////////////////////////////////////////////////////////////////
// Constructor/Destructor
//////////////////////////////////////////////////////////////////////////
InteractableComponent::InteractableComponent() : Component(),
mInteractableWeight(1)
{
mFriendlyName = "Interactable";
mComponentType = "Game";
mDescription = getDescriptionText("Allows owner entity to be interacted with.");
}
InteractableComponent::~InteractableComponent()
{
}
IMPLEMENT_CO_NETOBJECT_V1(InteractableComponent);
bool InteractableComponent::onAdd()
{
if (!Parent::onAdd())
return false;
return true;
}
void InteractableComponent::onRemove()
{
Parent::onRemove();
}
//This is mostly a catch for situations where the behavior is re-added to the object and the like and we may need to force an update to the behavior
void InteractableComponent::onComponentAdd()
{
Parent::onComponentAdd();
}
void InteractableComponent::onComponentRemove()
{
Parent::onComponentRemove();
}
void InteractableComponent::initPersistFields()
{
Parent::initPersistFields();
addField("interactableWeight", TypeF32, Offset(mInteractableWeight, InteractableComponent), "Controls importance values when using radius mode for interaction");
}
void InteractableComponent::interact(InteractComponent* interactor)
{
if (interactor != nullptr)
{
mOwner->notifyComponents("onInteract", interactor->getIdString());
if(isMethod("onInteract"))
Con::executef(this, "onInteract", interactor);
}
}
void InteractableComponent::interact(InteractComponent* interactor, RayInfo rayInfo)
{
if (interactor != nullptr)
{
mOwner->notifyComponents("onInteract", interactor->getIdString());
if (isMethod("onInteract"))
Con::executef(this, "onInteract", interactor);
}
}

View file

@ -0,0 +1,30 @@
#pragma once
#include "T3D/components/component.h"
#include "T3D/components/game/interactComponent.h"
class InteractableComponent : public Component
{
typedef Component Parent;
//Controls importance values when using radius mode for interaction
F32 mInteractableWeight;
public:
InteractableComponent();
~InteractableComponent();
DECLARE_CONOBJECT(InteractableComponent);
virtual bool onAdd();
virtual void onRemove();
static void initPersistFields();
virtual void onComponentAdd();
virtual void onComponentRemove();
void interact(InteractComponent* interactor);
void interact(InteractComponent* interactor, RayInfo rayInfo);
F32 getWeight() { return mInteractableWeight; }
};

View file

@ -11,7 +11,7 @@
#include "console/engineAPI.h"
#include "sim/netConnection.h"
#include "T3D/gameBase/gameConnection.h"
#include "T3D/components/coreInterfaces.h"
//#include "T3D/components/coreInterfaces.h"
#include "math/mathUtils.h"
#include "collision/concretePolyList.h"
#include "collision/clippedPolyList.h"
@ -94,21 +94,21 @@ void TriggerComponent::onComponentAdd()
{
Parent::onComponentAdd();
CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
CollisionComponent *colComp = mOwner->getComponent<CollisionComponent>();
if(colInt)
if(colComp)
{
colInt->onCollisionSignal.notify(this, &TriggerComponent::potentialEnterObject);
colComp->onCollisionSignal.notify(this, &TriggerComponent::potentialEnterObject);
}
}
void TriggerComponent::onComponentRemove()
{
CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
CollisionComponent *colComp = mOwner->getComponent<CollisionComponent>();
if(colInt)
if(colComp)
{
colInt->onCollisionSignal.remove(this, &TriggerComponent::potentialEnterObject);
colComp->onCollisionSignal.remove(this, &TriggerComponent::potentialEnterObject);
}
Parent::onComponentRemove();
@ -119,11 +119,11 @@ void TriggerComponent::componentAddedToOwner(Component *comp)
if (comp->getId() == getId())
return;
CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
CollisionComponent *colComp = mOwner->getComponent<CollisionComponent>();
if (colInt)
if (colComp)
{
colInt->onCollisionSignal.notify(this, &TriggerComponent::potentialEnterObject);
colComp->onCollisionSignal.notify(this, &TriggerComponent::potentialEnterObject);
}
}
@ -132,11 +132,11 @@ void TriggerComponent::componentRemovedFromOwner(Component *comp)
if (comp->getId() == getId()) //?????????
return;
CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
CollisionComponent *colComp = mOwner->getComponent<CollisionComponent>();
if (colInt)
if (colComp)
{
colInt->onCollisionSignal.remove(this, &TriggerComponent::potentialEnterObject);
colComp->onCollisionSignal.remove(this, &TriggerComponent::potentialEnterObject);
}
}
@ -217,19 +217,19 @@ bool TriggerComponent::testObject(SceneObject* enter)
return false;
//check if the entity has a collision shape
CollisionInterface *cI = enterEntity->getComponent<CollisionInterface>();
if (cI)
CollisionComponent *colComp = enterEntity->getComponent<CollisionComponent>();
if (colComp)
{
cI->buildPolyList(PLC_Collision, &mClippedList, mOwner->getWorldBox(), sphere);
colComp->buildPolyList(PLC_Collision, &mClippedList, mOwner->getWorldBox(), sphere);
if (!mClippedList.isEmpty())
{
//well, it's clipped with, or inside, our bounds
//now to test the clipped list against our own collision mesh
CollisionInterface *myCI = mOwner->getComponent<CollisionInterface>();
CollisionComponent *myColComp = mOwner->getComponent<CollisionComponent>();
//wait, how would we NOT have this?
if (myCI)
if (myColComp)
{
//anywho, build our list and then we'll check intersections
ClippedPolyList myList;
@ -238,7 +238,7 @@ bool TriggerComponent::testObject(SceneObject* enter)
myList.setTransform(&ownerTransform, mOwner->getScale());
myList.setObject(mOwner);
myCI->buildPolyList(PLC_Collision, &myList, enterBox, sphere);
myColComp->buildPolyList(PLC_Collision, &myList, enterBox, sphere);
}
}
}
@ -354,4 +354,4 @@ DefineEngineMethod( TriggerComponent, visualizeFrustums, void,
"@return true if successful, false if failed (objB is not valid)" )
{
object->visualizeFrustums(renderTime);
}
}

View file

@ -13,8 +13,8 @@
#include "T3D/entity.h"
#endif
#ifndef _COLLISION_INTERFACES_H_
#include "T3D/components/collision/collisionInterfaces.h"
#ifndef COLLISION_COMPONENT_H
#include "T3D/components/collision/collisionComponent.h"
#endif
//////////////////////////////////////////////////////////////////////////

View file

@ -20,7 +20,7 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "T3D/components/physics/physicsBehavior.h"
#include "T3D/components/physics/physicsComponent.h"
#include "platform/platform.h"
#include "console/consoleTypes.h"
#include "core/util/safeDelete.h"
@ -37,6 +37,8 @@
#include "T3D/containerQuery.h"
#include "math/mathIO.h"
#include "T3D/physics/physicsPlugin.h"
//////////////////////////////////////////////////////////////////////////
// Constructor/Destructor
//////////////////////////////////////////////////////////////////////////
@ -47,6 +49,11 @@ PhysicsComponent::PhysicsComponent() : Component()
addComponentField("drag", "The drag coefficient that constantly affects the object", "float", "0.7", "");
addComponentField("mass", "The mass of the object", "float", "1", "");
mFriendlyName = "Physics Component";
mComponentType = "Physics";
mDescription = getDescriptionText("A stub component class that physics components should inherit from.");
mStatic = false;
mAtRest = false;
mAtRestCounter = 0;
@ -80,7 +87,7 @@ PhysicsComponent::~PhysicsComponent()
SAFE_DELETE_ARRAY(mDescription);
}
IMPLEMENT_CO_NETOBJECT_V1(PhysicsComponent);
IMPLEMENT_CONOBJECT(PhysicsComponent);
void PhysicsComponent::onComponentAdd()
{
@ -92,6 +99,11 @@ void PhysicsComponent::onComponentAdd()
mDelta.posVec = Point3F(0,0,0);
}
void PhysicsComponent::onComponentRemove()
{
Parent::onComponentRemove();
}
void PhysicsComponent::initPersistFields()
{
Parent::initPersistFields();
@ -101,6 +113,7 @@ void PhysicsComponent::initPersistFields()
addField("isStatic", TypeBool, Offset(mStatic, PhysicsComponent));
}
//Networking
U32 PhysicsComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
{
U32 retMask = Parent::packUpdate(con, mask, stream);
@ -123,7 +136,6 @@ U32 PhysicsComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream
}
return retMask;
}
void PhysicsComponent::unpackUpdate(NetConnection *con, BitStream *stream)
{
Parent::unpackUpdate(con, stream);
@ -146,7 +158,35 @@ void PhysicsComponent::unpackUpdate(NetConnection *con, BitStream *stream)
}
}
//
//Setup
void PhysicsComponent::prepCollision()
{
if (!mOwner)
return;
if (mConvexList != NULL)
mConvexList->nukeList();
mOwner->enableCollision();
_updatePhysics();
}
void PhysicsComponent::_updatePhysics()
{
SAFE_DELETE( mPhysicsRep );
if ( !PHYSICSMGR )
return;
return;
}
void PhysicsComponent::buildConvex(const Box3F& box, Convex* convex)
{
convex = nullptr;
}
//Updates
void PhysicsComponent::interpolateTick(F32 dt)
{
Point3F pos = mDelta.pos + mDelta.posVec * dt;
@ -155,10 +195,19 @@ void PhysicsComponent::interpolateTick(F32 dt)
setRenderPosition(pos,dt);
}
//
void PhysicsComponent::updatePos(const F32 travelTime)
{
return;
}
void PhysicsComponent::updateForces()
{
return;
}
void PhysicsComponent::updateContainer()
{
PROFILE_SCOPE( PhysicsBehaviorInstance_updateContainer );
PROFILE_SCOPE(PhysicsBehaviorInstance_updateContainer);
// Update container drag and buoyancy properties
@ -168,11 +217,11 @@ void PhysicsComponent::updateContainer()
//mGravityMod = 1.0;
//mAppliedForce.set(0,0,0);
ContainerQueryInfo info;
info.box = mOwner->getWorldBox();
info.mass = mMass;
mLastContainerInfo = ContainerQueryInfo();
mLastContainerInfo.box = mOwner->getWorldBox();
mLastContainerInfo.mass = mMass;
mOwner->getContainer()->findObjects(info.box, WaterObjectType|PhysicalZoneObjectType,findRouter,&info);
mOwner->getContainer()->findObjects(mLastContainerInfo.box, WaterObjectType | PhysicalZoneObjectType, findRouter, &mLastContainerInfo);
//mWaterCoverage = info.waterCoverage;
//mLiquidType = info.liquidType;
@ -182,71 +231,37 @@ void PhysicsComponent::updateContainer()
// This value might be useful as a datablock value,
// This is what allows the player to stand in shallow water (below this coverage)
// without jiggling from buoyancy
if (info.waterCoverage >= 0.25f)
{
if (mLastContainerInfo.waterCoverage >= 0.25f)
{
// water viscosity is used as drag for in water.
// ShapeBaseData drag is used for drag outside of water.
// Combine these two components to calculate this ShapeBase object's
// current drag.
mDrag = ( info.waterCoverage * info.waterViscosity ) +
( 1.0f - info.waterCoverage ) * mDrag;
mDrag = (mLastContainerInfo.waterCoverage * mLastContainerInfo.waterViscosity) +
(1.0f - mLastContainerInfo.waterCoverage) * mDrag;
//mBuoyancy = (info.waterDensity / mDataBlock->density) * info.waterCoverage;
}
//mAppliedForce = info.appliedForce;
mGravityMod = info.gravityScale;
mGravityMod = mLastContainerInfo.gravityScale;
}
//
void PhysicsComponent::_updatePhysics()
//Events
void PhysicsComponent::updateVelocity(const F32 dt)
{
/*SAFE_DELETE( mOwner->mPhysicsRep );
if ( !PHYSICSMGR )
return;
if (mDataBlock->simpleServerCollision)
{
// We only need the trigger on the server.
if ( isServerObject() )
{
PhysicsCollision *colShape = PHYSICSMGR->createCollision();
colShape->addBox( mObjBox.getExtents() * 0.5f, MatrixF::Identity );
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() );
}
}
else
{
if ( !mShapeInstance )
return;
PhysicsCollision* colShape = mShapeInstance->getShape()->buildColShape( false, getScale() );
if ( colShape )
{
PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
mPhysicsRep = PHYSICSMGR->createBody();
mPhysicsRep->init( colShape, 0, PhysicsBody::BF_KINEMATIC, this, world );
mPhysicsRep->setTransform( getTransform() );
}
}*/
return;
}
PhysicsBody *PhysicsComponent::getPhysicsRep()
void PhysicsComponent::applyImpulse(const Point3F&, const VectorF& vec)
{
/*if(mOwner)
{
Entity* ac = dynamic_cast<Entity*>(mOwner);
if(ac)
return ac->mPhysicsRep;
}*/
return NULL;
// Items ignore angular velocity
VectorF vel;
vel.x = vec.x / mMass;
vel.y = vec.y / mMass;
vel.z = vec.z / mMass;
setVelocity(mVelocity + vel);
}
//
//Setters
void PhysicsComponent::setTransform(const MatrixF& mat)
{
mOwner->setTransform(mat);
@ -257,8 +272,8 @@ void PhysicsComponent::setTransform(const MatrixF& mat)
mAtRestCounter = 0;
}
if ( getPhysicsRep() )
getPhysicsRep()->setTransform( mOwner->getTransform() );
if (getPhysicsRep())
getPhysicsRep()->setTransform(mOwner->getTransform());
setMaskBits(UpdateMask);
}
@ -272,16 +287,15 @@ void PhysicsComponent::setPosition(const Point3F& pos)
return;
}
else {
mat.setColumn(3,pos);
mat.setColumn(3, pos);
}
mOwner->setTransform(mat);
if ( getPhysicsRep() )
getPhysicsRep()->setTransform( mat );
if (getPhysicsRep())
getPhysicsRep()->setTransform(mat);
}
void PhysicsComponent::setRenderPosition(const Point3F& pos, F32 dt)
{
MatrixF mat = mOwner->getRenderTransform();
@ -291,16 +305,12 @@ void PhysicsComponent::setRenderPosition(const Point3F& pos, F32 dt)
return;
}
else {
mat.setColumn(3,pos);
mat.setColumn(3, pos);
}
mOwner->setRenderTransform(mat);
}
void PhysicsComponent::updateVelocity(const F32 dt)
{
}
void PhysicsComponent::setVelocity(const VectorF& vel)
{
mVelocity = vel;
@ -310,6 +320,18 @@ void PhysicsComponent::setVelocity(const VectorF& vel)
setMaskBits(VelocityMask);
}
//Getters
PhysicsBody *PhysicsComponent::getPhysicsRep()
{
/*if(mOwner)
{
Entity* ac = dynamic_cast<Entity*>(mOwner);
if(ac)
return ac->mPhysicsRep;
}*/
return NULL;
}
void PhysicsComponent::getVelocity(const Point3F& r, Point3F* v)
{
*v = mVelocity;
@ -339,20 +361,6 @@ F32 PhysicsComponent::getZeroImpulse(const Point3F& r,const Point3F& normal)
return 1 / ((1/mMass) + mDot(c, normal));
}
void PhysicsComponent::accumulateForce(F32 dt, Point3F force)
{
mVelocity += force * dt;
}
void PhysicsComponent::applyImpulse(const Point3F&,const VectorF& vec)
{
// Items ignore angular velocity
VectorF vel;
vel.x = vec.x / mMass;
vel.y = vec.y / mMass;
vel.z = vec.z / mMass;
setVelocity(mVelocity + vel);
}
DefineEngineMethod( PhysicsComponent, applyImpulse, bool, ( Point3F pos, VectorF vel ),,
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"

View file

@ -2,9 +2,10 @@
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#pragma once
#ifndef PHYSICS_COMPONENT_H
#define PHYSICS_COMPONENT_H
#ifndef _PHYSICSBEHAVIOR_H_
#define _PHYSICSBEHAVIOR_H_
#include "T3D/components/component.h"
#ifndef __RESOURCE_H__
@ -59,13 +60,17 @@ protected:
VectorF mGravity;
VectorF mVelocity;
F32 mDrag;
F32 mMass;
F32 mMass;
F32 mGravityMod;
S32 csmAtRestTimer;
F32 sAtRestVelocity; // Min speed after collisio
PhysicsBody* mPhysicsRep;
PhysicsWorld* mPhysicsWorld;
Convex* mConvexList;
public:
enum MaskBits {
PositionMask = Parent::NextFreeMask << 0,
@ -79,7 +84,7 @@ public:
{
Move move; ///< Last move from server
F32 dt; ///< Last interpolation time
// Interpolation data
// Interpolation data
Point3F pos;
Point3F posVec;
QuatF rot[2];
@ -91,8 +96,11 @@ public:
};
StateDelta mDelta;
S32 mPredictionCount; ///< Number of ticks to predict
ContainerQueryInfo mLastContainerInfo;
public:
PhysicsComponent();
virtual ~PhysicsComponent();
@ -100,36 +108,49 @@ public:
static void initPersistFields();
virtual void interpolateTick(F32 dt);
virtual void updatePos(const U32 /*mask*/, const F32 dt){}
virtual void _updatePhysics();
virtual PhysicsBody *getPhysicsRep();
//Components
virtual void onComponentAdd();
virtual void onComponentRemove();
//Setup
void prepCollision();
virtual void _updatePhysics();
virtual void buildConvex(const Box3F& box, Convex* convex);
//Update
virtual void interpolateTick(F32 dt);
virtual void updatePos(const F32 dt);
virtual void updateForces();
void updateContainer();
//Physics Collision Conveinence Hooks
virtual bool updateCollision(F32 dt, Rigid& ns, CollisionList &cList) { return false; }
virtual bool resolveContacts(Rigid& ns, CollisionList& cList, F32 dt) { return false; }
virtual bool resolveCollision(const Point3F& p, const Point3F &normal) { return false; }
//Networking
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
virtual void onComponentAdd();
void updateContainer();
//Events
virtual void updateVelocity(const F32 dt);
virtual Point3F getVelocity() { return mVelocity; }
virtual void getOriginVector(const Point3F &p, Point3F* r);
virtual void getVelocity(const Point3F& r, Point3F* v);
virtual void setVelocity(const VectorF& vel);
virtual void setTransform(const MatrixF& mat);
virtual void setPosition(const Point3F& pos);
void setRenderPosition(const Point3F& pos, F32 dt);
virtual void applyImpulse(const Point3F&, const VectorF& vec);
virtual F32 getZeroImpulse(const Point3F& r, const Point3F& normal);
virtual void accumulateForce(F32 dt, Point3F force);
//Rigid Body Collision Conveinence Hooks
virtual bool updateCollision(F32 dt, Rigid& ns, CollisionList &cList) { return false; }
virtual bool resolveContacts(Rigid& ns, CollisionList& cList, F32 dt) { return false; }
//virtual bool resolveCollision(Rigid& ns, CollisionList& cList) { return false; }
virtual bool resolveCollision(const Point3F& p, const Point3F &normal) { return false; }
//Gets
F32 getMass() { return mMass; }
virtual PhysicsBody *getPhysicsRep();
virtual Point3F getVelocity() { return mVelocity; }
virtual void getOriginVector(const Point3F &p, Point3F* r);
virtual void getVelocity(const Point3F& r, Point3F* v);
virtual F32 getZeroImpulse(const Point3F& r, const Point3F& normal);
};
#endif // _COMPONENT_H_

View file

@ -37,7 +37,6 @@
#include "collision/collision.h"
#include "T3D/physics/physicsPlayer.h"
#include "T3D/physics/physicsPlugin.h"
#include "T3D/components/collision/collisionInterfaces.h"
#include "T3D/trigger.h"
#include "T3D/components/collision/collisionTrigger.h"
@ -58,13 +57,8 @@ IMPLEMENT_CALLBACK(PlayerControllerComponent, updateMove, void, (PlayerControlle
//////////////////////////////////////////////////////////////////////////
// Constructor/Destructor
//////////////////////////////////////////////////////////////////////////
PlayerControllerComponent::PlayerControllerComponent() : Component()
PlayerControllerComponent::PlayerControllerComponent() : PhysicsComponent()
{
addComponentField("isStatic", "If enabled, object will not simulate physics", "bool", "0", "");
addComponentField("gravity", "The direction of gravity affecting this object, as a vector", "vector", "0 0 -9", "");
addComponentField("drag", "The drag coefficient that constantly affects the object", "float", "0.7", "");
addComponentField("mass", "The mass of the object", "float", "1", "");
mBuoyancy = 0.f;
mFriction = 0.3f;
mElasticity = 0.4f;
@ -122,7 +116,7 @@ PlayerControllerComponent::PlayerControllerComponent() : Component()
mPhysicsRep = nullptr;
mPhysicsWorld = nullptr;
mOwnerCollisionInterface = nullptr;
mOwnerCollisionComp = nullptr;
mIntegrationCount = 0;
}
@ -160,6 +154,13 @@ void PlayerControllerComponent::onComponentAdd()
{
Parent::onComponentAdd();
CollisionComponent *collisionComp = mOwner->getComponent<CollisionComponent>();
if (collisionComp)
{
collisionComp->onCollisionChanged.notify(this, &PlayerControllerComponent::updatePhysics);
mOwnerCollisionComp = collisionComp;
}
updatePhysics();
}
@ -168,12 +169,11 @@ void PlayerControllerComponent::componentAddedToOwner(Component *comp)
if (comp->getId() == getId())
return;
//test if this is a shape component!
CollisionInterface *collisionInterface = dynamic_cast<CollisionInterface*>(comp);
if (collisionInterface)
CollisionComponent *collisionComp = dynamic_cast<CollisionComponent*>(comp);
if (collisionComp)
{
collisionInterface->onCollisionChanged.notify(this, &PlayerControllerComponent::updatePhysics);
mOwnerCollisionInterface = collisionInterface;
collisionComp->onCollisionChanged.notify(this, &PlayerControllerComponent::updatePhysics);
mOwnerCollisionComp = collisionComp;
updatePhysics();
}
}
@ -183,12 +183,11 @@ void PlayerControllerComponent::componentRemovedFromOwner(Component *comp)
if (comp->getId() == getId()) //?????????
return;
//test if this is a shape component!
CollisionInterface *collisionInterface = dynamic_cast<CollisionInterface*>(comp);
if (collisionInterface)
CollisionComponent *collisionComp = dynamic_cast<CollisionComponent*>(comp);
if (collisionComp)
{
collisionInterface->onCollisionChanged.remove(this, &PlayerControllerComponent::updatePhysics);
mOwnerCollisionInterface = NULL;
collisionComp->onCollisionChanged.notify(this, &PlayerControllerComponent::updatePhysics);
mOwnerCollisionComp = nullptr;
updatePhysics();
}
}
@ -219,7 +218,7 @@ void PlayerControllerComponent::initPersistFields()
Parent::initPersistFields();
addField("inputVelocity", TypePoint3F, Offset(mInputVelocity, PlayerControllerComponent), "");
addField("useDirectMoveInput", TypePoint3F, Offset(mUseDirectMoveInput, PlayerControllerComponent), "");
addField("useDirectMoveInput", TypeBool, Offset(mUseDirectMoveInput, PlayerControllerComponent), "");
}
U32 PlayerControllerComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
@ -579,10 +578,10 @@ void PlayerControllerComponent::updatePos(const F32 travelTime)
haveCollisions = true;
//TODO: clean this up so the phys component doesn't have to tell the col interface to do this
CollisionInterface* colInterface = mOwner->getComponent<CollisionInterface>();
if (colInterface)
CollisionComponent* colComp = mOwner->getComponent<CollisionComponent>();
if (colComp)
{
colInterface->handleCollisionList(collisionList, mVelocity);
colComp->handleCollisionList(collisionList, mVelocity);
}
}
@ -629,6 +628,8 @@ void PlayerControllerComponent::updatePos(const F32 travelTime)
}
}
}
updateContainer();
MatrixF newMat;
newMat.setPosition(newPos);
@ -701,6 +702,9 @@ void PlayerControllerComponent::findContact(bool *run, bool *jump, VectorF *cont
if (mContactInfo.contacted)
mContactInfo.contactNormal = *contactNormal;
mContactInfo.run = *run;
mContactInfo.jump = *jump;
}
void PlayerControllerComponent::applyImpulse(const Point3F &pos, const VectorF &vec)

View file

@ -23,8 +23,8 @@
#ifndef PLAYER_CONTORLLER_COMPONENT_H
#define PLAYER_CONTORLLER_COMPONENT_H
#ifndef PHYSICSBEHAVIOR_H
#include "T3D/components/physics/physicsBehavior.h"
#ifndef PHYSICS_COMPONENT_H
#include "T3D/components/physics/physicsComponent.h"
#endif
#ifndef __RESOURCE_H__
#include "core/resource.h"
@ -53,25 +53,20 @@
#ifndef _T3D_PHYSICS_PHYSICSWORLD_H_
#include "T3D/physics/physicsWorld.h"
#endif
#ifndef PHYSICS_COMPONENT_INTERFACE_H
#include "T3D/components/physics/physicsComponentInterface.h"
#endif
#ifndef COLLISION_INTERFACES_H
#include "T3D/components/collision/collisionInterfaces.h"
#ifndef COLLISION_COMPONENT_H
#include "T3D/components/collision/collisionComponent.h"
#endif
class SceneRenderState;
class PhysicsWorld;
class PhysicsPlayer;
class SimplePhysicsBehaviorInstance;
class CollisionInterface;
//////////////////////////////////////////////////////////////////////////
///
///
//////////////////////////////////////////////////////////////////////////
class PlayerControllerComponent : public Component,
public PhysicsComponentInterface
class PlayerControllerComponent : public PhysicsComponent
{
typedef Component Parent;
@ -101,7 +96,7 @@ class PlayerControllerComponent : public Component,
PhysicsPlayer *mPhysicsRep;
PhysicsWorld *mPhysicsWorld;
CollisionInterface* mOwnerCollisionInterface;
CollisionComponent* mOwnerCollisionComp;
struct ContactInfo
{
@ -113,8 +108,9 @@ class PlayerControllerComponent : public Component,
void clear()
{
contacted = jump = run = false;
contactObject = NULL;
contactNormal.set(1, 1, 1);
contactObject = nullptr;
contactNormal.set(0,0,0);
contactTime = 0;
}
ContactInfo() { clear(); }

View file

@ -39,7 +39,7 @@ bool RigidBodyComponent::smNoSmoothing = false;
//////////////////////////////////////////////////////////////////////////
// Constructor/Destructor
//////////////////////////////////////////////////////////////////////////
RigidBodyComponent::RigidBodyComponent() : Component()
RigidBodyComponent::RigidBodyComponent() : PhysicsComponent()
{
mMass = 20;
mDynamicFriction = 1;
@ -353,11 +353,11 @@ void RigidBodyComponent::findContact()
mPhysicsRep->findContact(&contactObject, contactNormal, &overlapObjects);
if (!overlapObjects.empty())
/*if (!overlapObjects.empty())
{
//fire our signal that the physics sim said collisions happened
onPhysicsCollision.trigger(*contactNormal, overlapObjects);
}
}*/
}
void RigidBodyComponent::_onPhysicsReset(PhysicsResetEvent reset)

View file

@ -32,8 +32,8 @@
#ifndef COLLISION_COMPONENT_H
#include "T3D/components/collision/collisionComponent.h"
#endif
#ifndef PHYSICS_COMPONENT_INTERFACE_H
#include "T3D/components/physics/physicsComponentInterface.h"
#ifndef PHYSICS_COMPONENT_H
#include "T3D/components/physics/physicsComponent.h"
#endif
class PhysicsBody;
@ -42,9 +42,9 @@ class PhysicsBody;
///
///
//////////////////////////////////////////////////////////////////////////
class RigidBodyComponent : public Component, public PhysicsComponentInterface
class RigidBodyComponent : public PhysicsComponent
{
typedef Component Parent;
typedef PhysicsComponent Parent;
enum SimType
{

View file

@ -0,0 +1,391 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "T3D/components/physics/simplePhysicsComponent.h"
#include "T3D/components/collision/collisionComponent.h"
#include "platform/platform.h"
#include "console/consoleTypes.h"
#include "core/util/safeDelete.h"
#include "core/resourceManager.h"
#include "core/stream/fileStream.h"
#include "console/consoleTypes.h"
#include "console/consoleObject.h"
#include "ts/tsShapeInstance.h"
#include "core/stream/bitStream.h"
#include "gfx/gfxTransformSaver.h"
#include "console/engineAPI.h"
#include "lighting/lightQuery.h"
#include "T3D/gameBase/gameConnection.h"
#include "collision/collision.h"
//////////////////////////////////////////////////////////////////////////
// Callbacks
IMPLEMENT_CALLBACK( SimplePhysicsComponent, updateMove, void, ( SimplePhysicsComponent* obj ), ( obj ),
"Called when the player updates it's movement, only called if object is set to callback in script(doUpdateMove).\n"
"@param obj the Player object\n" );
//////////////////////////////////////////////////////////////////////////
// Constructor/Destructor
//////////////////////////////////////////////////////////////////////////
SimplePhysicsComponent::SimplePhysicsComponent() : PhysicsComponent()
{
mBuoyancy = 0.f;
mFriction = 0.3f;
mElasticity = 0.4f;
mMaxVelocity = 3000.f;
mSticky = false;
mDrag = 0.5;
mVelocity = Point3F::Zero;
moveSpeed = Point3F(1, 1, 1);
mFriendlyName = "Simple Physics";
mComponentType = "Physics";
mDescription = getDescriptionText("Simple physics Component that allows gravity and impulses.");
}
SimplePhysicsComponent::~SimplePhysicsComponent()
{
for(S32 i = 0;i < mFields.size();++i)
{
ComponentField &field = mFields[i];
SAFE_DELETE_ARRAY(field.mFieldDescription);
}
SAFE_DELETE_ARRAY(mDescription);
}
IMPLEMENT_CONOBJECT(SimplePhysicsComponent);
//////////////////////////////////////////////////////////////////////////
bool SimplePhysicsComponent::onAdd()
{
if(! Parent::onAdd())
return false;
return true;
}
void SimplePhysicsComponent::onRemove()
{
Parent::onRemove();
}
void SimplePhysicsComponent::initPersistFields()
{
Parent::initPersistFields();
addField( "moveSpeed", TypePoint3F, Offset(moveSpeed, SimplePhysicsComponent), "");
}
U32 SimplePhysicsComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
{
U32 retMask = Parent::packUpdate(con, mask, stream);
return retMask;
}
void SimplePhysicsComponent::unpackUpdate(NetConnection *con, BitStream *stream)
{
Parent::unpackUpdate(con, stream);
}
//
void SimplePhysicsComponent::processTick()
{
Parent::processTick();
if (!isServerObject() || !isActive())
return;
//
//if (mCollisionObject && !--mCollisionTimeout)
// mCollisionObject = 0;
// Warp to catch up to server
if (mDelta.warpCount < mDelta.warpTicks)
{
mDelta.warpCount++;
// Set new pos.
mOwner->getTransform().getColumn(3,&mDelta.pos);
mDelta.pos += mDelta.warpOffset;
//mDelta.rot[0] = mDelta.rot[1];
//mDelta.rot[1].interpolate(mDelta.warpRot[0],mDelta.warpRot[1],F32(mDelta.warpCount)/mDelta.warpTicks);
MatrixF trans;
mDelta.rot[1].setMatrix(&trans);
trans.setPosition(mDelta.pos);
setTransform(trans);
// Pos backstepping
mDelta.posVec.x = -mDelta.warpOffset.x;
mDelta.posVec.y = -mDelta.warpOffset.y;
mDelta.posVec.z = -mDelta.warpOffset.z;
}
else
{
// Save current rigid state interpolation
mDelta.posVec = mOwner->getPosition();
//mDelta.rot[0] = mOwner->getTransform();
updateForces();
updatePos(TickSec);
// Wrap up interpolation info
mDelta.pos = mOwner->getPosition();
mDelta.posVec -= mOwner->getPosition();
//mDelta.rot[1] = mRigid.angPosition;
// Update container database
setTransform(mOwner->getTransform());
setMaskBits(UpdateMask);
updateContainer();
}
}
void SimplePhysicsComponent::interpolateTick(F32 dt)
{
// Client side interpolation
Point3F pos = mDelta.pos + mDelta.posVec * dt;
MatrixF mat = mOwner->getRenderTransform();
mat.setColumn(3,pos);
mOwner->setRenderTransform(mat);
mDelta.dt = dt;
}
void SimplePhysicsComponent::updatePos(const F32 travelTime)
{
mOwner->getTransform().getColumn(3,&mDelta.posVec);
// When mounted to another object, only Z rotation used.
if (mOwner->isMounted()) {
mVelocity = mOwner->getObjectMount()->getVelocity();
setPosition(Point3F(0.0f, 0.0f, 0.0f));
setMaskBits(UpdateMask);
return;
}
Point3F newPos;
if ( mVelocity.isZero() )
newPos = mDelta.posVec;
else
newPos = _move( travelTime );
//}
// Set new position
// If on the client, calc delta for backstepping
if (isClientObject())
{
mDelta.pos = newPos;
mDelta.posVec = mDelta.posVec - mDelta.pos;
mDelta.dt = 1.0f;
}
setPosition( newPos );
setMaskBits( UpdateMask );
updateContainer();
/*if (!isGhost())
{
// Do mission area callbacks on the server as well
checkMissionArea();
}*/
return;
}
Point3F SimplePhysicsComponent::_move( const F32 travelTime )
{
// Try and move to new pos
F32 totalMotion = 0.0f;
Point3F start;
Point3F initialPosition;
mOwner->getTransform().getColumn(3,&start);
initialPosition = start;
VectorF firstNormal(0.0f, 0.0f, 0.0f);
//F32 maxStep = mDataBlock->maxStepHeight;
F32 time = travelTime;
U32 count = 0;
S32 sMoveRetryCount = 5;
CollisionComponent* colComp = mOwner->getComponent<CollisionComponent>();
if(!colComp)
return start + mVelocity * time;
colComp->clearCollisionList();
for (; count < sMoveRetryCount; count++)
{
F32 speed = mVelocity.len();
if (!speed)
break;
Point3F end = start + mVelocity * time;
Point3F distance = end - start;
bool collided = colComp->checkCollisions(time, &mVelocity, start);
if (colComp->getCollisionList()->getCount() != 0 && colComp->getCollisionList()->getTime() < 1.0f)
{
// Set to collision point
F32 velLen = mVelocity.len();
F32 dt = time * getMin(colComp->getCollisionList()->getTime(), 1.0f);
start += mVelocity * dt;
time -= dt;
totalMotion += velLen * dt;
// Back off...
if ( velLen > 0.f )
{
F32 newT = getMin(0.01f / velLen, dt);
start -= mVelocity * newT;
totalMotion -= velLen * newT;
}
// Pick the surface most parallel to the face that was hit.
U32 colCount = colComp->getCollisionList()->getCount();
const Collision *collision = colComp->getCollision(0);
const Collision *cp = collision + 1;
const Collision *ep = collision + colComp->getCollisionList()->getCount();
for (; cp != ep; cp++)
{
U32 colCountLoop = colComp->getCollisionList()->getCount();
//TODO: Move this somewhere else
if(Entity* colEnt = dynamic_cast<Entity*>(collision->object))
{
if(CollisionComponent *collidingEntityColComp = colEnt->getComponent<CollisionComponent>())
{
if(!collidingEntityColComp->doesBlockColliding())
{
continue;
}
}
}
if (cp->faceDot > collision->faceDot)
collision = cp;
}
//check the last/first one just incase
if(Entity* colEnt = dynamic_cast<Entity*>(collision->object))
{
if(CollisionComponent *collidingEntityColComp = colEnt->getComponent<CollisionComponent>())
{
if(!collidingEntityColComp->doesBlockColliding())
{
//if our ideal surface doesn't stop us, just move along
return start + mVelocity * time;
}
}
}
//F32 bd = _doCollisionImpact( collision, wasFalling );
F32 bd = -mDot( mVelocity, collision->normal);
// Subtract out velocity
F32 sNormalElasticity = 0.01f;
VectorF dv = collision->normal * (bd + sNormalElasticity);
mVelocity += dv;
if (count == 0)
{
firstNormal = collision->normal;
}
else
{
if (count == 1)
{
// Re-orient velocity along the crease.
if (mDot(dv,firstNormal) < 0.0f &&
mDot(collision->normal,firstNormal) < 0.0f)
{
VectorF nv;
mCross(collision->normal,firstNormal,&nv);
F32 nvl = nv.len();
if (nvl)
{
if (mDot(nv,mVelocity) < 0.0f)
nvl = -nvl;
nv *= mVelocity.len() / nvl;
mVelocity = nv;
}
}
}
}
}
else
{
totalMotion += (end - start).len();
start = end;
break;
}
}
U32 colCountThree = colComp->getCollisionList()->getCount();
if (colCountThree != 0)
bool derp = true;
if (count == sMoveRetryCount)
{
// Failed to move
start = initialPosition;
mVelocity.set(0.0f, 0.0f, 0.0f);
}
return start;
}
void SimplePhysicsComponent::updateForces()
{
// Acceleration due to gravity
mVelocity += (mGravity * mGravityMod) * TickMs;
F32 len = mVelocity.len();
if (mMaxVelocity > 0 && mAbs(len) > mMaxVelocity)
{
Point3F excess = mVelocity * (1.0 - (mMaxVelocity / len));
excess *= 0.1f;
mVelocity -= excess;
}
// Container buoyancy & drag
if(mOwner->getContainerInfo().waterCoverage > 0.65f)
mVelocity -= mBuoyancy * (mGravity * mGravityMod) * TickMs;
mVelocity -= mVelocity * mDrag * TickMs;
if( mVelocity.isZero() )
mVelocity = Point3F::Zero;
else
setMaskBits(VelocityMask);
}
//
void SimplePhysicsComponent::setVelocity(const VectorF& vel)
{
Parent::setVelocity(vel);
// Clamp against the maximum velocity.
if ( mMaxVelocity > 0 )
{
F32 len = mVelocity.magnitudeSafe();
if ( len > mMaxVelocity )
{
Point3F excess = mVelocity * ( 1.0f - (mMaxVelocity / len ) );
mVelocity -= excess;
}
}
}

View file

@ -0,0 +1,88 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef SIMPLE_PHYSICS_COMPONENT_H
#define SIMPLE_PHYSICS_COMPONENT_H
#ifndef PHYSICS_COMPONENT_H
#include "T3D/components/physics/physicsComponent.h"
#endif
#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 _CONVEX_H_
#include "collision/convex.h"
#endif
#ifndef _BOXCONVEX_H_
#include "collision/boxConvex.h"
#endif
class SceneRenderState;
class PhysicsBody;
//////////////////////////////////////////////////////////////////////////
///
///
//////////////////////////////////////////////////////////////////////////
class SimplePhysicsComponent : public PhysicsComponent
{
typedef PhysicsComponent Parent;
protected:
F32 mBuoyancy;
F32 mFriction;
F32 mElasticity;
F32 mMaxVelocity;
bool mSticky;
U32 mIntegrationCount;
Point3F moveSpeed;
Point3F mStickyCollisionPos;
Point3F mStickyCollisionNormal;
public:
SimplePhysicsComponent();
virtual ~SimplePhysicsComponent();
DECLARE_CONOBJECT(SimplePhysicsComponent);
virtual bool onAdd();
virtual void onRemove();
static void initPersistFields();
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
virtual void processTick();
virtual void interpolateTick(F32 dt);
virtual void updatePos(const F32 dt);
void updateForces();
void updateMove(const Move* move);
Point3F _move(const F32 travelTime);
//virtual void onComponentRemove();
virtual VectorF getVelocity() { return mVelocity; }
virtual void setVelocity(const VectorF& vel);
//
DECLARE_CALLBACK(void, updateMove, (SimplePhysicsComponent* obj));
};
#endif // _COMPONENT_H_

View file

@ -45,21 +45,10 @@
#include "core/strings/findMatch.h"
#include "T3D/components/render/meshComponent_ScriptBinding.h"
ImplementEnumType(BatchingMode,
"Type of mesh data available in a shape.\n"
"@ingroup gameObjects")
{
MeshComponent::Individual, "Individual", "This mesh is rendered indivudally, wthout batching or instancing."
},
{ MeshComponent::StaticBatch, "Static Batching", "Statically batches this mesh together with others to reduce drawcalls." },
//{ MeshComponent::DynamicBatch, "Dynamic Batching", "Dynamical batches this mesh together with others to reduce drawcalls each frame." },
// { MeshComponent::Instanced, "Instanced", "This mesh is rendered as an instance, reducing draw overhead with others that share the same mesh and material." },
EndImplementEnumType;
//////////////////////////////////////////////////////////////////////////
// Constructor/Destructor
//////////////////////////////////////////////////////////////////////////
MeshComponent::MeshComponent() : Component(), mShape(nullptr), mRenderMode(Individual)
MeshComponent::MeshComponent() : Component(), mShape(nullptr)
{
mFriendlyName = "Mesh Component";
mComponentType = "Render";
@ -75,8 +64,6 @@ MeshComponent::MeshComponent() : Component(), mShape(nullptr), mRenderMode(Indiv
mMeshAssetId = StringTable->EmptyString();
mInterfaceData = new MeshRenderSystemInterface();
mRenderMode = Individual;
}
MeshComponent::~MeshComponent()
@ -134,14 +121,9 @@ void MeshComponent::initPersistFields()
{
Parent::initPersistFields();
addGroup("Rendering");
addField("BatchingMode", TypeBatchingMode, Offset(mRenderMode, MeshComponent),
"The mode of batching this shape should be rendered with.");
endGroup("Rendering");
//create a hook to our internal variables
addGroup("Model");
addProtectedField("MeshAsset", TypeShapeAssetPtr, Offset(mShapeAsset, MeshComponent), &_setMesh, &defaultProtectedGetFn,
addProtectedField("MeshAsset", TypeShapeAssetPtr, Offset(mShapeAsset, MeshComponent), &_setMesh, &defaultProtectedGetFn, &writeShape,
"The asset Id used for the mesh.", AbstractClassRep::FieldFlags::FIELD_ComponentInspectors);
endGroup("Model");
}
@ -182,6 +164,8 @@ bool MeshComponent::setMeshAsset(const char* assetName)
return false;
}
mMeshAsset->onShapeChanged.notify(this, &MeshComponent::_shapeAssetUpdated);
mShapeName = mMeshAssetId;
mShapeAsset = mShapeName;
updateShape(); //make sure we force the update to resize the owner bounds
@ -190,6 +174,11 @@ bool MeshComponent::setMeshAsset(const char* assetName)
return true;
}
void MeshComponent::_shapeAssetUpdated(ShapeAsset* asset)
{
updateShape();
}
void MeshComponent::updateShape()
{
if (mInterfaceData == nullptr)
@ -197,7 +186,6 @@ void MeshComponent::updateShape()
//if ((mShapeName && mShapeName[0] != '\0') || (mShapeAsset && mShapeAsset[0] != '\0'))
if ((mShapeName && mShapeName[0] != '\0') || (mMeshAssetId && mMeshAssetId[0] != '\0'))
{
if (mMeshAsset == NULL)
return;
@ -210,7 +198,7 @@ void MeshComponent::updateShape()
setupShape();
//Do this on both the server and client
S32 materialCount = mMeshAsset->getShape()->materialList->getMaterialNameList().size();
S32 materialCount = mMeshAsset->getShape()->materialList->getMaterialNameList().size(); //mMeshAsset->getMaterialCount();
if (isServerObject())
{
@ -234,13 +222,21 @@ void MeshComponent::updateShape()
for (U32 i = 0; i < materialCount; i++)
{
String materialname = mMeshAsset->getShape()->materialList->getMaterialName(i);
if (materialname == String("ShapeBounds"))
continue;
StringTableEntry materialname = StringTable->insert(mMeshAsset->getShape()->materialList->getMaterialName(i).c_str());
dSprintf(matFieldName, 128, "MaterialSlot%d", i);
//Iterate through our assetList to find the compliant entry in our matList
for (U32 m = 0; m < mMeshAsset->getMaterialCount(); m++)
{
AssetPtr<MaterialAsset> matAsset = mMeshAsset->getMaterialAsset(m);
addComponentField(matFieldName, "A material used in the shape file", "Material", materialname, "");
if (matAsset->getMaterialDefinitionName() == materialname)
{
dSprintf(matFieldName, 128, "MaterialSlot%d", i);
addComponentField(matFieldName, "A material used in the shape file", "Material", matAsset->getAssetId(), "");
break;
}
}
}
if (materialCount > 0)
@ -272,27 +268,6 @@ void MeshComponent::updateShape()
mOwner->getSceneManager()->notifyObjectDirty(mOwner);
}
if (isClientObject() && mInterfaceData)
{
if (mRenderMode == StaticBatch)
{
mInterfaceData->mStatic = true;
OptimizedPolyList geom;
MatrixF transform = mInterfaceData->mTransform;
mInterfaceData->mGeometry.setTransform(&transform, mInterfaceData->mScale);
mInterfaceData->mGeometry.setObject(mOwner);
mInterfaceData->mShapeInstance->buildPolyList(&mInterfaceData->mGeometry, 0);
}
else
{
mInterfaceData->mStatic = false;
}
MeshRenderSystem::rebuildBuffers();
}
//finally, notify that our shape was changed
onShapeInstanceChanged.trigger(this);
}
@ -305,6 +280,8 @@ void MeshComponent::setupShape()
void MeshComponent::_onResourceChanged( const Torque::Path &path )
{
/*bool srv = isServerObject();
if (mInterfaceData == nullptr)
return;
@ -316,7 +293,7 @@ void MeshComponent::_onResourceChanged( const Torque::Path &path )
return;
updateShape();
setMaskBits(ShapeMask);
setMaskBits(ShapeMask);*/
}
void MeshComponent::inspectPostApply()
@ -343,8 +320,6 @@ U32 MeshComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
if (stream->writeFlag(mask & ShapeMask))
{
stream->writeString(mShapeName);
stream->writeInt(mRenderMode, 8);
}
if (stream->writeFlag( mask & MaterialMask ))
@ -373,7 +348,6 @@ void MeshComponent::unpackUpdate(NetConnection *con, BitStream *stream)
{
mShapeName = stream->readSTString();
mRenderMode = (RenderMode)stream->readInt(8);
setMeshAsset(mShapeName);
updateShape();
}
@ -460,28 +434,34 @@ void MeshComponent::updateMaterials()
TSMaterialList* pMatList = mInterfaceData->mShapeInstance->getMaterialList();
pMatList->setTextureLookupPath(getShapeResource().getPath().getPath());
bool found = false;
const Vector<String> &materialNames = pMatList->getMaterialNameList();
for ( S32 i = 0; i < materialNames.size(); i++ )
{
if (found)
break;
for(U32 m=0; m < mChangingMaterials.size(); m++)
{
if(mChangingMaterials[m].slot == i)
{
//Fetch the actual material asset
pMatList->renameMaterial( i, mChangingMaterials[m].matAsset->getMaterialDefinitionName());
found = true;
break;
}
}
mChangingMaterials.clear();
}
mChangingMaterials.clear();
// Initialize the material instances
mInterfaceData->mShapeInstance->initMaterialList();
}
MatrixF MeshComponent::getNodeTransform(S32 nodeIdx)
{
if (mInterfaceData != nullptr && mMeshAsset->getShape())
if (mInterfaceData != nullptr && !mMeshAsset.isNull() && mMeshAsset->isAssetValid() && mMeshAsset->getShape())
{
S32 nodeCount = getShape()->nodes.size();
@ -589,7 +569,6 @@ void MeshComponent::onDynamicModified(const char* slotName, const char* newValue
void MeshComponent::changeMaterial(U32 slot, MaterialAsset* newMat)
{
char fieldName[512];
//update our respective field
@ -629,4 +608,4 @@ void MeshComponent::ownerTransformSet(MatrixF *mat)
MatrixF newTransform = *mat;
mInterfaceData->mTransform = newTransform;
}
}
}

View file

@ -101,18 +101,6 @@ protected:
Vector<matMap> mChangingMaterials;
Vector<matMap> mMaterials;
public:
enum RenderMode
{
Individual = 0,
DynamicBatch,
StaticBatch,
Instanced
};
protected:
RenderMode mRenderMode;
public:
StringTableEntry mMeshAssetId;
AssetPtr<ShapeAsset> mMeshAsset;
@ -144,6 +132,8 @@ public:
void updateShape();
void updateMaterials();
void _shapeAssetUpdated(ShapeAsset* asset);
virtual void onComponentRemove();
virtual void onComponentAdd();
@ -153,6 +143,8 @@ public:
static bool _setShape(void *object, const char *index, const char *data);
const char* _getShape(void *object, const char *data);
static bool writeShape(void* obj, StringTableEntry pFieldName) { return static_cast<MeshComponent*>(obj)->mMeshAsset.notNull(); }
bool setMeshAsset(const char* assetName);
virtual TSShape* getShape() { if (mMeshAsset) return mMeshAsset->getShape(); else return NULL; }
@ -186,7 +178,4 @@ public:
}
};
typedef MeshComponent::RenderMode BatchingMode;
DefineEnumType(BatchingMode);
#endif

View file

@ -36,7 +36,7 @@
#include "T3D/components/coreInterfaces.h"
#include "T3D/components/render/renderComponentInterface.h"
#include "T3D/components/collision/collisionInterfaces.h"
#include "T3D/components/collision/collisionComponent.h"
#include "gui/controls/guiTreeViewCtrl.h"
#include "assets/assetManager.h"
@ -1119,8 +1119,8 @@ bool Entity::buildPolyList(PolyListContext context, AbstractPolyList* polyList,
void Entity::buildConvex(const Box3F& box, Convex* convex)
{
Vector<BuildConvexInterface*> updaters = getComponents<BuildConvexInterface>();
for (Vector<BuildConvexInterface*>::iterator it = updaters.begin(); it != updaters.end(); it++)
Vector<CollisionComponent*> colliders = getComponents<CollisionComponent>();
for (Vector<CollisionComponent*>::iterator it = colliders.begin(); it != colliders.end(); it++)
{
(*it)->buildConvex(box, convex);
}
@ -1394,10 +1394,7 @@ void Entity::clearComponents(bool deleteComponents)
{
comp->onComponentRemove(); //in case the behavior needs to do cleanup on the owner
//we only need to delete them on the server side. they'll be cleaned up on the client side
//via the ghosting system for us
if (isServerObject())
comp->deleteObject();
comp->deleteObject();
}
}
}
@ -1911,12 +1908,9 @@ DefineEngineMethod(Entity, getMoveVector, VectorF, (),,
"Get the number of static fields on the object.\n"
"@return The number of static fields defined on the object.")
{
if (object->getControllingClient() != NULL)
{
//fetch our last move
if (object->lastMove.x != 0 || object->lastMove.y != 0 || object->lastMove.z != 0)
return VectorF(object->lastMove.x, object->lastMove.y, object->lastMove.z);
}
//fetch our last move
if (object->lastMove.x != 0 || object->lastMove.y != 0 || object->lastMove.z != 0)
return VectorF(object->lastMove.x, object->lastMove.y, object->lastMove.z);
return VectorF::Zero;
}
@ -1925,12 +1919,9 @@ DefineEngineMethod(Entity, getMoveRotation, VectorF, (), ,
"Get the number of static fields on the object.\n"
"@return The number of static fields defined on the object.")
{
if(object->getControllingClient() != NULL)
{
//fetch our last move
if (object->lastMove.pitch != 0 || object->lastMove.roll != 0 || object->lastMove.yaw != 0)
return VectorF(object->lastMove.pitch, object->lastMove.roll, object->lastMove.yaw);
}
//fetch our last move
if (object->lastMove.pitch != 0 || object->lastMove.roll != 0 || object->lastMove.yaw != 0)
return VectorF(object->lastMove.pitch, object->lastMove.roll, object->lastMove.yaw);
return VectorF::Zero;
}

View file

@ -204,7 +204,7 @@ public:
//Components
virtual bool deferAddingComponents() const { return true; }
void notifyComponents(String signalFunction, String argA, String argB, String argC, String argD, String argE);
void notifyComponents(String signalFunction, String argA, String argB="", String argC = "", String argD = "", String argE = "");
template <class T>
T* getComponent();
@ -283,8 +283,9 @@ public:
MatrixF getWorldToObj() { return mWorldToObj; }
MatrixF getObjToWorld() { return mObjToWorld; }
DECLARE_CONOBJECT(Entity);
StateDelta getNetworkDelta() { return mDelta; }
DECLARE_CONOBJECT(Entity);
};
template <class T>

View file

@ -116,6 +116,8 @@ public:
virtual void findContact(SceneObject **contactObject, VectorF *contactNormal, Vector<SceneObject*> *outOverlapObjects) const;
virtual void moveKinematicTo(const MatrixF &xfm);
virtual bool isValid() { return mActor != nullptr; }
};
#endif // _T3D_PHYSICS_BTBODY_H_

View file

@ -128,6 +128,8 @@ public:
///
virtual void moveKinematicTo(const MatrixF &xfm) = 0;
virtual bool isValid() = 0;
};

View file

@ -6,9 +6,6 @@
#include "materials/materialManager.h"
#include "materials/baseMatInstance.h"
Vector<MeshRenderSystem::BufferMaterials> MeshRenderSystem::mBufferMaterials(0);
Vector<MeshRenderSystem::BufferSet> MeshRenderSystem::mStaticBuffers(0);
void MeshRenderSystem::render(SceneManager *sceneManager, SceneRenderState* state)
{
if (sceneManager == nullptr || state == nullptr)
@ -24,9 +21,6 @@ void MeshRenderSystem::render(SceneManager *sceneManager, SceneRenderState* stat
if (!MeshRenderSystemInterface::all[i]->mIsClient)
continue;
if (MeshRenderSystemInterface::all[i]->mStatic)
continue;
//First, do frustum culling
if (viewFrustum.isCulled(MeshRenderSystemInterface::all[i]->mBounds))
continue;
@ -52,90 +46,6 @@ void MeshRenderSystem::render(SceneManager *sceneManager, SceneRenderState* stat
//if we've made it this far, call down to the render function to actually display our stuff
renderInterface(i, state);
}
//Static Batch rendering
if ( /*!mMaterialInst ||*/ !state)
return;
BaseMatInstance *matInst = MATMGR->getWarningMatInstance();
// Get a handy pointer to our RenderPassmanager
RenderPassManager *renderPass = state->getRenderPass();
for (U32 i = 0; i < mStaticBuffers.size(); i++)
{
for (U32 b = 0; b < mStaticBuffers[i].buffers.size(); b++)
{
if (mStaticBuffers[i].buffers[b].vertData.empty())
continue;
MeshRenderInst *ri = renderPass->allocInst<MeshRenderInst>();
// Set our RenderInst as a standard mesh render
ri->type = RenderPassManager::RIT_Mesh;
//If our material has transparency set on this will redirect it to proper render bin
if (matInst->getMaterial()->isTranslucent())
{
ri->type = RenderPassManager::RIT_Translucent;
ri->translucentSort = true;
}
// Calculate our sorting point
if (state)
{
// Calculate our sort point manually.
const Box3F& rBox = Box3F(1000);// getRenderWorldBox();
ri->sortDistSq = rBox.getSqDistanceToPoint(state->getCameraPosition());
}
else
ri->sortDistSq = 0.0f;
// Set up our transforms
MatrixF objectToWorld = MatrixF::Identity;//getRenderTransform();
//objectToWorld.scale(getScale());
ri->objectToWorld = renderPass->allocUniqueXform(objectToWorld);
ri->worldToCamera = renderPass->allocSharedXform(RenderPassManager::View);
ri->projection = renderPass->allocSharedXform(RenderPassManager::Projection);
// If our material needs lights then fill the RIs
// light vector with the best lights.
/*if (matInst->isForwardLit())
{
LightQuery query;
query.init(getWorldSphere());
query.getLights(ri->lights, 8);
}*/
// Make sure we have an up-to-date backbuffer in case
// our Material would like to make use of it
// NOTICE: SFXBB is removed and refraction is disabled!
//ri->backBuffTex = GFX->getSfxBackBuffer();
// Set our Material
ri->matInst = matInst;
// Set up our vertex buffer and primitive buffer
ri->vertBuff = &mStaticBuffers[i].buffers[b].vertexBuffer;
ri->primBuff = &mStaticBuffers[i].buffers[b].primitiveBuffer;
ri->prim = renderPass->allocPrim();
ri->prim->type = GFXTriangleList;
ri->prim->minIndex = 0;
ri->prim->startIndex = 0;
ri->prim->numPrimitives = mStaticBuffers[i].buffers[b].primData.size();
ri->prim->startVertex = 0;
ri->prim->numVertices = mStaticBuffers[i].buffers[b].vertData.size();
// We sort by the material then vertex buffer
ri->defaultKey = matInst->getStateHint();
ri->defaultKey2 = (uintptr_t)ri->vertBuff;
// Submit our RenderInst to the RenderPassManager
state->getRenderPass()->addInst(ri);
}
}
}
void MeshRenderSystem::renderInterface(U32 interfaceIndex, SceneRenderState* state)
@ -181,196 +91,4 @@ void MeshRenderSystem::renderInterface(U32 interfaceIndex, SceneRenderState* sta
GFX->setWorldMatrix(mat);
interface->mShapeInstance->render(rdata);
}
void MeshRenderSystem::rebuildBuffers()
{
U32 BUFFER_SIZE = 65000;
Vector<U32> tempIndices;
tempIndices.reserve(4);
Box3F newBounds = Box3F::Zero;
mStaticBuffers.clear();
for (U32 i = 0; i < MeshRenderSystemInterface::all.size(); i++)
{
if (!MeshRenderSystemInterface::all[i]->mIsEnabled)
continue;
if (!MeshRenderSystemInterface::all[i]->mIsClient || !MeshRenderSystemInterface::all[i]->mStatic)
continue;
//TODO: Properly re-implement StaticElements to container owner interfaces and buffer sets
for (U32 j = 0; j < MeshRenderSystemInterface::all[i]->mGeometry.mPolyList.size(); j++)
{
const OptimizedPolyList::Poly& poly = MeshRenderSystemInterface::all[i]->mGeometry.mPolyList[j];
if (poly.vertexCount < 3)
continue;
tempIndices.setSize(poly.vertexCount);
dMemset(tempIndices.address(), 0, poly.vertexCount);
if (poly.type == OptimizedPolyList::TriangleStrip ||
poly.type == OptimizedPolyList::TriangleFan)
{
tempIndices[0] = 0;
U32 idx = 1;
for (U32 k = 1; k < poly.vertexCount; k += 2)
tempIndices[idx++] = k;
for (U32 k = ((poly.vertexCount - 1) & (~0x1)); k > 0; k -= 2)
tempIndices[idx++] = k;
}
else if (poly.type == OptimizedPolyList::TriangleList)
{
for (U32 k = 0; k < poly.vertexCount; k++)
tempIndices[k] = k;
}
else
continue;
//got our data, now insert it into the correct buffer!
S32 bufferId = findBufferSetByMaterial(poly.material);
if (bufferId == -1)
{
//add a new buffer set if we didn't get a match!
BufferSet newSet;
newSet.surfaceMaterialId = poly.material;
mStaticBuffers.push_back(newSet);
bufferId = mStaticBuffers.size() - 1;
}
//see if this would push us over our buffer size limit, if it is, make a new buffer for this set
if (mStaticBuffers[bufferId].buffers.last().vertData.size() + 3 > BUFFER_SIZE
|| mStaticBuffers[bufferId].buffers.last().primData.size() + 1 > BUFFER_SIZE)
{
//yep, we'll overstep with this, so spool up a new buffer in this set
BufferSet::Buffers newBuffer = BufferSet::Buffers();
mStaticBuffers[bufferId].buffers.push_back(newBuffer);
}
const U32& firstIdx = MeshRenderSystemInterface::all[i]->mGeometry.mIndexList[poly.vertexStart];
const OptimizedPolyList::VertIndex& firstVertIdx = MeshRenderSystemInterface::all[i]->mGeometry.mVertexList[firstIdx];
//Vector<Point3F> geomPoints = MeshRenderSystemInterface::all[i]->mGeometry.mPoints;
//Vector<Point3F> geomNormals = MeshRenderSystemInterface::all[i]->mGeometry.mNormals;
//Vector<Point2F> geoUVs = MeshRenderSystemInterface::all[i]->mGeometry.mUV0s;
for (U32 k = 1; k < poly.vertexCount - 1; k++)
{
const U32& secondIdx = MeshRenderSystemInterface::all[i]->mGeometry.mIndexList[poly.vertexStart + tempIndices[k]];
const U32& thirdIdx = MeshRenderSystemInterface::all[i]->mGeometry.mIndexList[poly.vertexStart + tempIndices[k + 1]];
const OptimizedPolyList::VertIndex& secondVertIdx = MeshRenderSystemInterface::all[i]->mGeometry.mVertexList[secondIdx];
const OptimizedPolyList::VertIndex& thirdVertIdx = MeshRenderSystemInterface::all[i]->mGeometry.mVertexList[thirdIdx];
Point3F points[3];
points[0] = MeshRenderSystemInterface::all[i]->mGeometry.mPoints[firstVertIdx.vertIdx];
points[1] = MeshRenderSystemInterface::all[i]->mGeometry.mPoints[secondVertIdx.vertIdx];
points[2] = MeshRenderSystemInterface::all[i]->mGeometry.mPoints[thirdVertIdx.vertIdx];
Point3F normals[3];
normals[0] = MeshRenderSystemInterface::all[i]->mGeometry.mNormals[firstVertIdx.normalIdx];
normals[1] = MeshRenderSystemInterface::all[i]->mGeometry.mNormals[secondVertIdx.normalIdx];
normals[2] = MeshRenderSystemInterface::all[i]->mGeometry.mNormals[thirdVertIdx.normalIdx];
Point3F tangents[3];
tangents[0] = mCross(points[1] - points[0], normals[0]);
tangents[1] = mCross(points[2] - points[1], normals[1]);
tangents[2] = mCross(points[0] - points[2], normals[2]);
Point2F uvs[3];
uvs[0] = MeshRenderSystemInterface::all[i]->mGeometry.mUV0s[firstVertIdx.uv0Idx];
uvs[1] = MeshRenderSystemInterface::all[i]->mGeometry.mUV0s[secondVertIdx.uv0Idx];
uvs[2] = MeshRenderSystemInterface::all[i]->mGeometry.mUV0s[thirdVertIdx.uv0Idx];
mStaticBuffers[bufferId].vertCount += 3;
mStaticBuffers[bufferId].primCount += 1;
for (U32 v = 0; v < 3; ++v)
{
//Build the vert and store it to the buffers!
GFXVertexPNTT bufVert;
bufVert.point = points[v];
bufVert.normal = normals[v];
bufVert.tangent = tangents[v];
bufVert.texCoord = uvs[v];
newBounds.extend(points[v]);
mStaticBuffers[bufferId].buffers.last().vertData.push_back(bufVert);
U32 vertPrimId = mStaticBuffers[bufferId].buffers.last().vertData.size() - 1;
mStaticBuffers[bufferId].buffers.last().primData.push_back(vertPrimId);
mStaticBuffers[bufferId].center += points[v];
}
}
}
}
//Now, iterate through the organized data and turn them into renderable buffers
for (U32 i = 0; i < mStaticBuffers.size(); i++)
{
for (U32 b = 0; b < mStaticBuffers[i].buffers.size(); b++)
{
BufferSet::Buffers& buffers = mStaticBuffers[i].buffers[b];
//if there's no data to be had in this buffer, just skip it
if (buffers.vertData.empty())
continue;
buffers.vertexBuffer.set(GFX, buffers.vertData.size(), GFXBufferTypeStatic);
GFXVertexPNTT *pVert = buffers.vertexBuffer.lock();
for (U32 v = 0; v < buffers.vertData.size(); v++)
{
pVert->normal = buffers.vertData[v].normal;
pVert->tangent = buffers.vertData[v].tangent;
//pVert->color = buffers.vertData[v].color;
pVert->point = buffers.vertData[v].point;
pVert->texCoord = buffers.vertData[v].texCoord;
pVert++;
}
buffers.vertexBuffer.unlock();
// Allocate PB
buffers.primitiveBuffer.set(GFX, buffers.primData.size(), buffers.primData.size() / 3, GFXBufferTypeStatic);
U16 *pIndex;
buffers.primitiveBuffer.lock(&pIndex);
for (U16 primDataIDx = 0; primDataIDx < buffers.primData.size(); primDataIDx++)
{
*pIndex = primDataIDx;
pIndex++;
}
buffers.primitiveBuffer.unlock();
}
mStaticBuffers[i].center /= mStaticBuffers[i].vertCount;
}
//mObjBox = newBounds;
//resetWorldBox();
}
U32 MeshRenderSystem::findBufferSetByMaterial(U32 matId)
{
for (U32 i = 0; i < mStaticBuffers.size(); i++)
{
if (mStaticBuffers[i].surfaceMaterialId == matId)
return i;
}
return -1;
}
}

View file

@ -38,12 +38,7 @@ public:
Vector<matMap> mChangingMaterials;
Vector<matMap> mMaterials;
//Static geometry stuff
bool mStatic;
OptimizedPolyList mGeometry;
MeshRenderSystemInterface() : SystemInterface(), mShapeInstance(nullptr), mTransform(MatrixF::Identity), mScale(Point3F::One), mIsClient(false), mStatic(false)
MeshRenderSystemInterface() : SystemInterface(), mShapeInstance(nullptr), mTransform(MatrixF::Identity), mScale(Point3F::One), mIsClient(false)
{
mBounds = Box3F(1);
mSphere = SphereF();
@ -58,150 +53,10 @@ public:
class MeshRenderSystem
{
protected:
/*struct StaticBatchElement
{
SimObject* owner;
OptimizedPolyList geometry;
String batchName;
};
static Vector<StaticBatchElement> mStaticElements;*/
//We retain the pushed geometry data for rendering here. It's static(unless forced to change through editing or whatnot)
//so rendering the batches is real fast
struct BufferMaterials
{
// The name of the Material we will use for rendering
String mMaterialName;
// The actual Material instance
BaseMatInstance* mMaterialInst;
BufferMaterials()
{
mMaterialName = "";
mMaterialInst = NULL;
}
};
static Vector<BufferMaterials> mBufferMaterials;
struct BufferSet
{
U32 surfaceMaterialId;
U32 vertCount;
U32 primCount;
Point3F center;
struct Buffers
{
U32 vertStart;
U32 primStart;
U32 vertCount;
U32 primCount;
Vector<GFXVertexPNTT> vertData;
Vector<U32> primData;
GFXVertexBufferHandle< GFXVertexPNTT > vertexBuffer;
GFXPrimitiveBufferHandle primitiveBuffer;
Buffers()
{
vertStart = 0;
primStart = 0;
vertCount = 0;
primCount = 0;
vertexBuffer = NULL;
primitiveBuffer = NULL;
}
};
Vector<Buffers> buffers;
BufferSet()
{
Buffers newBuffer;
buffers.push_back(newBuffer);
surfaceMaterialId = 0;
vertCount = 0;
primCount = 0;
center = Point3F::Zero;
}
};
static Vector<BufferSet> mStaticBuffers;
public:
/*virtual void prepRenderImage(SceneRenderState *state);
bool setMeshAsset(const char* assetName);
virtual TSShape* getShape() { if (mMeshAsset) return mMeshAsset->getShape(); else return NULL; }
virtual TSShapeInstance* getShapeInstance() { return mShapeInstance; }
Resource<TSShape> getShapeResource() { return mMeshAsset->getShapeResource(); }
void _onResourceChanged(const Torque::Path &path);
virtual bool castRayRendered(const Point3F &start, const Point3F &end, RayInfo *info);
void mountObjectToNode(SceneObject* objB, String node, MatrixF txfm);
virtual void onDynamicModified(const char* slotName, const char* newValue);
void changeMaterial(U32 slot, MaterialAsset* newMat);
bool setMatInstField(U32 slot, const char* field, const char* value);
virtual void onInspect();
virtual void onEndInspect();
virtual Vector<MatrixF> getNodeTransforms()
{
Vector<MatrixF> bob;
return bob;
}
virtual void setNodeTransforms(Vector<MatrixF> transforms)
{
return;
}*/
/*MeshRenderSystem()
{
}
virtual ~MeshRenderSystem()
{
smInterfaceList.clear();
}
static MeshComponentInterface* GetNewInterface()
{
smInterfaceList.increment();
return &smInterfaceList.last();
}
static void RemoveInterface(T* q)
{
smInterfaceList.erase(q);
}*/
//Core render function, which does all the real work
static void render(SceneManager *sceneManager, SceneRenderState* state);
//Render our particular interface's data
static void renderInterface(U32 interfaceIndex, SceneRenderState* state);
//Static Batch rendering
static void rebuildBuffers();
static U32 findBufferSetByMaterial(U32 matId);
};

View file

@ -11,4 +11,9 @@
canSaveDynamicFields="true"
Extension="asset.taml"
Recurse="true" />
<AutoloadAssets
canSave="true"
canSaveDynamicFields="true"
AssetType="ComponentAsset"
Recurse="true" />
</ModuleDefinition>

View file

@ -3,8 +3,7 @@
canSaveDynamicFields="true"
AssetName="ControlObjectComponentAsset"
componentName="ControlObjectComponent"
componentClass="Component"
componentClass="ControlObjectComponent"
friendlyName="Control Object"
componentType="Game"
description="Allows the component owner to be controlled by a client."
scriptFile="core/components/components/game/controlObject.cs" />
description="Allows the component owner to be controlled by a client." />

View file

@ -1,89 +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.
//-----------------------------------------------------------------------------
//registerComponent("ControlObjectComponent", "Component", "Control Object", "Game", false, "Allows the behavior owner to operate as a camera.");
function ControlObjectComponent::onAdd(%this)
{
%this.addComponentField(clientOwner, "The shape to use for rendering", "int", "1", "");
%clientID = %this.getClientID();
if(%clientID && !isObject(%clientID.getControlObject()))
%clientID.setControlObject(%this.owner);
}
function ControlObjectComponent::onRemove(%this)
{
%clientID = %this.getClientID();
if(%clientID)
%clientID.setControlObject(0);
}
function ControlObjectComponent::onClientConnect(%this, %client)
{
if(%this.isControlClient(%client) && !isObject(%client.getControlObject()))
%client.setControlObject(%this.owner);
}
function ControlObjectComponent::onClientDisconnect(%this, %client)
{
if(%this.isControlClient(%client))
%client.setControlObject(0);
}
function ControlObjectComponent::getClientID(%this)
{
return ClientGroup.getObject(%this.clientOwner-1);
}
function ControlObjectComponent::isControlClient(%this, %client)
{
%clientID = ClientGroup.getObject(%this.clientOwner-1);
if(%client.getID() == %clientID)
return true;
else
return false;
}
function ControlObjectComponent::onInspectorUpdate(%this, %field)
{
%clientID = %this.getClientID();
if(%clientID && !isObject(%clientID.getControlObject()))
%clientID.setControlObject(%this.owner);
}
function switchControlObject(%client, %newControlEntity)
{
if(!isObject(%client) || !isObject(%newControlEntity))
return error("SwitchControlObject: No client or target controller!");
%control = %newControlEntity.getComponent(ControlObjectComponent);
if(!isObject(%control))
return error("SwitchControlObject: Target controller has no conrol object behavior!");
%client.setControlObject(%newControlEntity);
}

View file

@ -0,0 +1,107 @@
//--- OBJECT WRITE BEGIN ---
%guiContent = new GuiControl(TypeMaskFieldGui) {
position = "0 0";
extent = "1024 768";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "ToolsGuiDefaultNonModalProfile";
visible = "1";
active = "1";
tooltipProfile = "GuiToolTipProfile";
hovertime = "1000";
isContainer = "1";
canSave = "1";
canSaveDynamicFields = "1";
new GuiWindowCtrl(TypeMaskFieldWindow) {
text = "Select Type Masks";
resizeWidth = "0";
resizeHeight = "1";
canMove = "1";
canClose = "1";
canMinimize = "0";
canMaximize = "0";
canCollapse = "0";
closeCommand = "Canvas.popDialog(TypeMaskFieldGui);";
edgeSnap = "1";
docking = "None";
margin = "4 4 4 4";
padding = "0 0 0 0";
anchorTop = "0";
anchorBottom = "0";
anchorLeft = "0";
anchorRight = "0";
position = "430 176";
extent = "161 250";
minExtent = "161 86";
horizSizing = "windowRelative";
vertSizing = "windowRelative";
profile = "ToolsGuiWindowProfile";
visible = "1";
active = "1";
tooltipProfile = "GuiToolTipProfile";
hovertime = "1000";
isContainer = "1";
canSave = "1";
canSaveDynamicFields = "0";
new GuiScrollCtrl() {
willFirstRespond = "1";
hScrollBar = "alwaysOff";
vScrollBar = "dynamic";
lockHorizScroll = "0";
lockVertScroll = "0";
constantThumbHeight = "0";
childMargin = "2 0";
mouseWheelScrollSpeed = "-1";
docking = "Client";
margin = "0 0 0 0";
padding = "0 0 0 0";
anchorTop = "0";
anchorBottom = "0";
anchorLeft = "1";
anchorRight = "0";
position = "1 9";
extent = "159 238";
minExtent = "8 2";
horizSizing = "width";
vertSizing = "height";
profile = "ToolsGuiScrollProfile";
visible = "1";
active = "1";
tooltipProfile = "ToolsGuiToolTipProfile";
hovertime = "1000";
isContainer = "1";
canSave = "1";
canSaveDynamicFields = "0";
new GuiStackControl() {
stackingType = "Vertical";
horizStacking = "Left to Right";
vertStacking = "Top to Bottom";
padding = "-2";
dynamicSize = "1";
dynamicNonStackExtent = "0";
dynamicPos = "0";
changeChildSizeToFit = "1";
changeChildPosition = "1";
position = "3 1";
extent = "153 18";
minExtent = "16 16";
horizSizing = "width";
vertSizing = "bottom";
profile = "ToolsGuiDefaultProfile";
visible = "1";
active = "1";
tooltipProfile = "ToolsGuiToolTipProfile";
hovertime = "1000";
isContainer = "1";
internalName = "TypeMaskList";
canSave = "1";
canSaveDynamicFields = "0";
};
};
};
};
//--- OBJECT WRITE END ---

View file

@ -0,0 +1,276 @@
function GuiInspectorComponentGroup::buildMaterialField(%this, %component, %fieldName)
{
%extent = 200;
%currentMaterial = %component.getFieldValue(%fieldName);
//if we don't have a new material set on this slot, just use the default
if(%currentMaterial $= "" || %currentMaterial == 0)
%currentMaterial = %material;
%container = new GuiControl() {
canSaveDynamicFields = "0";
Profile = "EditorContainerProfile";
HorizSizing = "right";
VertSizing = "bottom";
Position = "0 0";
Extent = "300 110";
MinExtent = "8 2";
canSave = "0";
Visible = "1";
hovertime = "100";
tooltip = %tooltip;
tooltipProfile = "EditorToolTipProfile";
};
%labelControl = new GuiTextCtrl() {
canSaveDynamicFields = "0";
Profile = "EditorFontHLBold";
HorizSizing = "right";
VertSizing = "bottom";
Position = "16 3";
Extent = "100 18";
MinExtent = "8 2";
canSave = "0";
Visible = "1";
hovertime = "100";
tooltip = %tooltip;
tooltipProfile = "EditorToolTipProfile";
text = %fieldName;
maxLength = "1024";
};
//
%imageContainer = new GuiControl(){
profile = "ToolsGuiDefaultProfile";
Position = "20 25";
Extent = "74 87";
HorizSizing = "right";
VertSizing = "bottom";
isContainer = "1";
new GuiTextCtrl(){
position = "7 71";
profile = "ToolsGuiTextCenterProfile";
extent = "64 16";
text = %matName;
};
};
%previewButton = new GuiBitmapButtonCtrl(){
internalName = %matName;
HorizSizing = "right";
VertSizing = "bottom";
profile = "ToolsGuiButtonProfile";
position = "7 4";
extent = "64 64";
buttonType = "PushButton";
bitmap = "";
Command = "";
text = "Loading...";
useStates = false;
tooltip = "Change material";
new GuiBitmapButtonCtrl(){
HorizSizing = "right";
VertSizing = "bottom";
profile = "ToolsGuiButtonProfile";
position = "0 0";
extent = "64 64";
Variable = "";
buttonType = "toggleButton";
bitmap = "tools/materialEditor/gui/cubemapBtnBorder";
groupNum = "0";
text = "";
tooltip = "Change material";
};
};
%previewBorder = new GuiButtonCtrl(){
className = "materialFieldBtn";
internalName = %matName@"Border";
HorizSizing = "right";
VertSizing = "bottom";
profile = "ToolsGuiThumbHighlightButtonProfile";
position = "3 0";
extent = "72 88";
Variable = "";
buttonType = "toggleButton";
tooltip = %matName;
groupNum = "0";
text = "";
Object = %component;
targetField = %fieldName;
};
%previewBorder.Command = %previewBorder @ ".getMaterialName();";
%imageContainer.add(%previewButton);
%imageContainer.add(%previewBorder);
%container.add(%imageContainer);
//
%mapToLabel = new GuiTextCtrl() {
canSaveDynamicFields = "0";
Profile = "EditorFontHLBold";
HorizSizing = "right";
VertSizing = "bottom";
Position = "100 26";
Extent = %extent SPC "18";
MinExtent = "8 2";
canSave = "0";
Visible = "1";
hovertime = "100";
tooltip = %tooltip;
tooltipProfile = "EditorToolTipProfile";
text = "Mapped to:" SPC %material.mapTo;
maxLength = "1024";
};
%editControl = new GuiTextEditCtrl() {
class = "BehaviorEdTextField";
internalName = %accessor @ "File";
canSaveDynamicFields = "0";
Profile = "EditorTextEdit";
HorizSizing = "right";
VertSizing = "bottom";
Position = "100 50";
Extent = %extent SPC "22";
MinExtent = "8 2";
canSave = "0";
Visible = "1";
hovertime = "100";
tooltip = %tooltip;
tooltipProfile = "EditorToolTipProfile";
maxLength = "1024";
historySize = "0";
password = "0";
text = %currentMaterial;
tabComplete = "0";
sinkAllKeyEvents = "0";
password = "0";
passwordMask = "*";
precision = %precision;
accessor = %accessor;
isProperty = true;
undoLabel = %fieldName;
object = %this.object;
useWords = false;
};
%resetButton = new GuiButtonCtrl() {
canSaveDynamicFields = "0";
className = "materialFieldBtn";
Profile = "GuiButtonProfile";
HorizSizing = "right";
VertSizing = "bottom";
Position = "100 75";
Extent = (%extent * 0.3)-3 SPC "22";
MinExtent = "8 2";
canSave = "0";
Visible = "1";
hovertime = "100";
tooltip = "Reset to default material";
tooltipProfile = "EditorToolTipProfile";
text = "Reset";
pathField = %editControl;
};
%editMatButton = new GuiButtonCtrl() {
canSaveDynamicFields = "0";
className = "materialFieldBtn";
Profile = "GuiButtonProfile";
HorizSizing = "right";
VertSizing = "bottom";
Position = %resetButton.position.x + (%extent * 0.3) + 6 SPC "75";
Extent = (%extent * 0.6)-3 SPC "22";
MinExtent = "8 2";
canSave = "0";
Visible = "1";
hovertime = "100";
tooltip = "Edit in Material Editor";
tooltipProfile = "EditorToolTipProfile";
text = "Open in Editor";
pathField = %editControl;
};
%container.add(%mapToLabel);
%container.add(%labelControl);
%container.add(%editControl);
%container.add(%resetButton);
%container.add(%editMatButton);
//load the material
%matName = "";
// CustomMaterials are not available for selection
/*if ( !isObject( %currentMaterial ) || %currentMaterial.isMemberOfClass( "CustomMaterial" ) )
return;*/
%assetDef = AssetDatabase.acquireAsset(%currentMaterial);
%materialDef = %assetDef.materialDefinitionName;
if( %materialDef.isMemberOfClass("TerrainMaterial") )
{
%matName = %currentMaterial.getInternalName();
if( %materialDef.diffuseMap $= "")
%previewImage = "core/art/warnmat";
else
%previewImage = %materialDef.diffuseMap;
}
else if( %materialDef.toneMap[0] $= "" && %materialDef.diffuseMap[0] $= "" && !isObject(%materialDef.cubemap) )
{
%matName = %materialDef.name;
%previewImage = "core/art/warnmat";
}
else
{
%matName = %materialDef.name;
if( %materialDef.toneMap[0] !$= "" )
%previewImage = %materialDef.toneMap[0];
else if( %materialDef.diffuseMap[0] !$= "" )
%previewImage = %materialDef.diffuseMap[0];
else if( %materialDef.cubemap.cubeFace[0] !$= "" )
%previewImage = %materialDef.cubemap.cubeFace[0];
//%previewImage = MaterialEditorGui.searchForTexture( %material, %previewImage );
// were going to use a couple of string commands in order to properly
// find out what the img src path is
// **NEW** this needs to be updated with the above, but has some timing issues
%materialDiffuse = %previewImage;
%materialPath = %materialDef.getFilename();
if( strchr( %materialDiffuse, "/") $= "" )
{
%k = 0;
while( strpos( %materialPath, "/", %k ) != -1 )
{
%foo = strpos( %materialPath, "/", %k );
%k = %foo + 1;
}
%foobar = getSubStr( %materialPath , %k , 99 );
%previewImage = strreplace( %materialPath, %foobar, %previewImage );
}
else
%previewImage = strreplace( %materialPath, %materialPath, %previewImage );
}
%previewButton.setBitmap(%previewImage);
%previewButton.setText("");
%this.stack.add(%container);
}
function materialFieldBtn::onClick(%this)
{
AssetBrowser.showDialog("MaterialAsset", "", %this.Object, %this.targetField, "");
}
function materialFieldBtn::setMaterial(%this, %matAssetName)
{
}

View file

@ -0,0 +1,215 @@
function BehaviorFieldStack::createStateMachineEditor(%this, %behavior, %fieldIndex)
{
%fieldInfo = %behavior.template.getBehaviorField(%fieldIndex);
%name = getField(%fieldInfo, 0);
%button = new GuiButtonCtrl()
{
class = EditStateMachineBtn;
HorizSizing = "right";
VertSizing = "bottom";
Position = "0 2";
Extent = (%this.extent.x - 8) SPC 13;
text = "Edit States";
tooltip = "Open window to edit the state machine";
behavior = %behavior;
};
%this.add(%button);
}
function EditStateMachineBtn::onClick(%this)
{
Canvas.pushDialog(StateMachineEditor);
StateMachineEditor.behavior = %this.behavior;
StateMachineEditor.open();
}
function StateMachineEditor::open(%this)
{
//check our behavior and see if we have any existing state/field info to work with
//if we do, load those up first
for(%i = 0; %i < %this.behavior.stateMachine.count(); %i++)
{
%stateName = %this.behavior.stateMachine.getKey(%i);
%this.addState(%stateName);
}
}
function StateMachineEditor::addState(%this, %stateName)
{
if(%stateName $= "")
%stateName = "New State";
%state = new GuiControl() {
position = "0 0";
extent = "285 50";
horizSizing = "horizResizeWidth";
vertSizing = "vertResizeTop";
isContainer = "1";
new GuiTextEditCtrl() {
position = "0 0";
extent = "100 15";
text = %stateName;
};
new GuiButtonCtrl() {
//buttonMargin = "4 4";
text = "Remove State";
position = "184 0";
extent = "100 15";
//profile = "GuiButtonProfile";
command = "ScriptEditorGui.save();";
};
new GuiSeparatorCtrl() {
position = "0 15";
extent = %this.extent.x SPC "10";
type = "horizontal";
};
new GuiStackControl(%stateName@"StateStack")
{
//Profile = "EditorContainerProfile";
HorizSizing = "right";
VertSizing = "bottom";
Position = "0 25";
Extent = "285 20";
padding = 4;
new GuiButtonCtrl() {
text = "Add field";
position = "3 0";
extent = "280 20";
horizSizing = "left";
vertSizing = "top";
command = "StateMachineEditor.addField("@%stateName@");";
};
};
};
%this-->Stack.add(%state);
//%this-->stateStackScroll.computeSizes();
}
function StateMachineEditor::addField(%this, %stateName)
{
%index = %this.behavior.stateMachine.count();
%field = new GuiControl() {
position = "0 0";
extent = "285 20";
horizSizing = "width";
vertSizing = "height";
isContainer = "1";
fieldValueCtrl = "";
fieldID = %index++;
};
%fieldList = new GuiPopUpMenuCtrlEx()
{
class = "stateMachineFieldList";
Profile = "GuiPopupMenuProfile";
HorizSizing = "width";
VertSizing = "bottom";
position = "0 1";
Extent = "120 18";
behavior = %this.behavior;
};
%field.add(%fieldList);
%fieldList.refresh();
(%stateName@"StateStack").addToStack(%field);
%this-->Stack.updateStack();
%this-->stateStackScroll.computeSizes();
%this.behavior.addStateField(%stateName, "", "");
return %field;
}
//==============================================================================
function stateMachineFieldList::refresh(%this)
{
%this.clear();
// Find all the types.
%count = getWordCount(%this.behavior.stateFields);
%index = 0;
for (%j = 0; %j < %count; %j++)
{
%item = getWord(%this.behavior.stateFields, %j);
%this.add(%item, %index);
%this.fieldType[%index] = %item;
%index++;
}
}
function stateMachineFieldList::onSelect(%this)
{
//if(%this.getParent().fieldValueCtrl $= "")
%this.fieldType = %this.fieldType[%this.getSelected()];
if(%this.fieldType $= "transitionOnAnimEnd" || %this.fieldType $= "transitionOnAnimTrigger"
|| %this.fieldType $= "transitionOnTimeout")
{
%fieldCtrl = new GuiPopUpMenuCtrlEx()
{
class = "stateMachineFieldList";
Profile = "GuiPopupMenuProfile";
HorizSizing = "width";
VertSizing = "bottom";
position = "124 1";
Extent = "120 18";
};
}
else if(%this.fieldType $= "animation")
{
%fieldCtrl = new GuiPopUpMenuCtrlEx()
{
class = "stateMachineFieldList";
Profile = "GuiPopupMenuProfile";
HorizSizing = "width";
VertSizing = "bottom";
position = "124 1";
Extent = "120 18";
};
%index = 0;
%animBhvr = %this.behavior.owner.getBehavior("AnimationController");
for(%i = 0; %i < %animBhvr.getAnimationCount(); %i++)
{
%item = %animBhvr.getAnimationName(%i);
%fieldCtrl.add(%item, %index);
%fieldCtrl.fieldValue[%index] = %item;
%index++;
}
}
else
{
%fieldCtrl = new GuiTextEditCtrl() {
position = "124 1";
extent = "120 10";
text = "";
};
}
//get the state machine entry
%index = %this.getParent().fieldID;
%oldValue = %this.behavior.stateMachine.getValue(%index);
%this.behavior.stateMachine.setValue(%fieldType SPC %oldValue.y);
%this.getParent().add(%fieldCtrl);
}
//==============================================================================
//Now for the unique field types
/*function stateMachineFieldList::refresh(%this)
{
}*/

View file

@ -0,0 +1,40 @@
function GuiInspectorComponentGroup::buildTypeMaskField(%this, %component, %fieldName)
{
%extent = 200;
%container = new GuiControl() {
canSaveDynamicFields = "0";
Profile = "EditorContainerProfile";
HorizSizing = "right";
VertSizing = "bottom";
Position = "0 0";
Extent = "300 110";
MinExtent = "8 2";
canSave = "0";
Visible = "1";
hovertime = "100";
tooltip = %tooltip;
tooltipProfile = "EditorToolTipProfile";
};
%labelControl = new GuiTextCtrl() {
canSaveDynamicFields = "0";
Profile = "EditorFontHLBold";
HorizSizing = "right";
VertSizing = "bottom";
Position = "16 3";
Extent = "100 18";
MinExtent = "8 2";
canSave = "0";
Visible = "1";
hovertime = "100";
tooltip = %tooltip;
tooltipProfile = "EditorToolTipProfile";
text = %fieldName;
maxLength = "1024";
};
%container.add(%container);
%this.stack.add(%container);
}

View file

@ -22,7 +22,14 @@
//Scripts
exec("./scripts/componentEditor.ed.cs");
exec("./scripts/stateMachineEditor.ed.cs");
exec("./scripts/superToolTipDlg.ed.cs");
//gui
exec("./gui/superToolTipDlg.ed.gui");
exec("./gui/stateMachineDlg.ed.gui");
//field types
exec("./interface/materialFieldType.cs");
exec("./interface/typeMaskFieldType.cs");
exec("./interface/stateMachineField.cs");

View file

@ -154,13 +154,13 @@ function QuickEditComponentList::onHotTrackItem( %this, %itemID )
SuperTooltipDlg.setTitle(%componentObj.friendlyName);
SuperTooltipDlg.addParam("", %componentObj.description @ "\n");
/*%fieldCount = %componentObj.getComponentFieldCount();
%fieldCount = %componentObj.getComponentFieldCount();
for (%i = 0; %i < %fieldCount; %i++)
{
%name = getField(%componentObj.getComponentField(%i), 0);
SuperTooltipDlg.addParam(%name, %description @ "\n");
}*/
}
%position = %this.getGlobalPosition();
SuperTooltipDlg.processTooltip( %position,0,1 );
%this.opened = true;