From 34d05ff16f7d899434dc3d08a93e8135952c273f Mon Sep 17 00:00:00 2001 From: Areloch Date: Sun, 24 Feb 2019 01:50:38 -0600 Subject: [PATCH] Updates to various components, added a few new ones. --- .../animation/animationComponent.cpp | 2 +- .../components/animation/animationComponent.h | 2 +- .../T3D/components/audio/SoundComponent.cpp | 8 +- .../collision/collisionComponent.cpp | 1194 ++++++++++------- .../components/collision/collisionComponent.h | 250 ++-- .../collision/collisionInterfaces.cpp | 258 ---- .../collision/collisionInterfaces.h | 167 --- .../components/collision/collisionTrigger.cpp | 2 +- .../collision/raycastColliderComponent.cpp | 122 ++ .../collision/raycastColliderComponent.h | 46 + .../collision/shapeCollisionComponent.cpp | 607 +++++++++ .../collision/shapeCollisionComponent.h | 137 ++ .../shapeCollisionComponent_ScriptBinding.h | 172 +++ .../collision/simpleHitboxComponent.cpp | 216 +++ .../collision/simpleHitboxComponent.h | 39 + Engine/source/T3D/components/component.cpp | 8 +- Engine/source/T3D/components/component.h | 5 +- .../game/controlObjectComponent.cpp | 149 ++ .../components/game/controlObjectComponent.h | 30 + .../components/game/followPathComponent.cpp | 359 +++++ .../T3D/components/game/followPathComponent.h | 82 ++ .../T3D/components/game/interactComponent.cpp | 171 +++ .../T3D/components/game/interactComponent.h | 34 + .../components/game/interactableComponent.cpp | 94 ++ .../components/game/interactableComponent.h | 30 + .../T3D/components/game/triggerComponent.cpp | 40 +- .../T3D/components/game/triggerComponent.h | 4 +- ...ysicsBehavior.cpp => physicsComponent.cpp} | 180 +-- .../{physicsBehavior.h => physicsComponent.h} | 65 +- .../physics/playerControllerComponent.cpp | 48 +- .../physics/playerControllerComponent.h | 22 +- .../components/physics/rigidBodyComponent.cpp | 6 +- .../components/physics/rigidBodyComponent.h | 8 +- .../physics/simplePhysicsComponent.cpp | 391 ++++++ .../physics/simplePhysicsComponent.h | 88 ++ .../T3D/components/render/meshComponent.cpp | 93 +- .../T3D/components/render/meshComponent.h | 19 +- Engine/source/T3D/entity.cpp | 29 +- Engine/source/T3D/entity.h | 5 +- Engine/source/T3D/physics/bullet/btBody.h | 2 + Engine/source/T3D/physics/physicsBody.h | 2 + .../T3D/systems/render/meshRenderSystem.cpp | 284 +--- .../T3D/systems/render/meshRenderSystem.h | 147 +- .../core/components/Core_Components.module | 5 + .../components/game/controlObject.asset.taml | 5 +- .../components/game/controlObject.cs | 89 -- .../componentEditor/gui/TypeMaskFieldGui.gui | 107 ++ .../interface/materialFieldType.cs | 276 ++++ .../interface/stateMachineField.cs | 215 +++ .../interface/typeMaskFieldType.cs | 40 + .../game/tools/componentEditor/main.cs | 7 + .../scripts/componentEditor.ed.cs | 4 +- 52 files changed, 4566 insertions(+), 1799 deletions(-) delete mode 100644 Engine/source/T3D/components/collision/collisionInterfaces.cpp delete mode 100644 Engine/source/T3D/components/collision/collisionInterfaces.h create mode 100644 Engine/source/T3D/components/collision/raycastColliderComponent.cpp create mode 100644 Engine/source/T3D/components/collision/raycastColliderComponent.h create mode 100644 Engine/source/T3D/components/collision/shapeCollisionComponent.cpp create mode 100644 Engine/source/T3D/components/collision/shapeCollisionComponent.h create mode 100644 Engine/source/T3D/components/collision/shapeCollisionComponent_ScriptBinding.h create mode 100644 Engine/source/T3D/components/collision/simpleHitboxComponent.cpp create mode 100644 Engine/source/T3D/components/collision/simpleHitboxComponent.h create mode 100644 Engine/source/T3D/components/game/controlObjectComponent.cpp create mode 100644 Engine/source/T3D/components/game/controlObjectComponent.h create mode 100644 Engine/source/T3D/components/game/followPathComponent.cpp create mode 100644 Engine/source/T3D/components/game/followPathComponent.h create mode 100644 Engine/source/T3D/components/game/interactComponent.cpp create mode 100644 Engine/source/T3D/components/game/interactComponent.h create mode 100644 Engine/source/T3D/components/game/interactableComponent.cpp create mode 100644 Engine/source/T3D/components/game/interactableComponent.h rename Engine/source/T3D/components/physics/{physicsBehavior.cpp => physicsComponent.cpp} (81%) rename Engine/source/T3D/components/physics/{physicsBehavior.h => physicsComponent.h} (84%) create mode 100644 Engine/source/T3D/components/physics/simplePhysicsComponent.cpp create mode 100644 Engine/source/T3D/components/physics/simplePhysicsComponent.h delete mode 100644 Templates/BaseGame/game/core/components/components/game/controlObject.cs create mode 100644 Templates/BaseGame/game/tools/componentEditor/gui/TypeMaskFieldGui.gui create mode 100644 Templates/BaseGame/game/tools/componentEditor/interface/materialFieldType.cs create mode 100644 Templates/BaseGame/game/tools/componentEditor/interface/stateMachineField.cs create mode 100644 Templates/BaseGame/game/tools/componentEditor/interface/typeMaskFieldType.cs diff --git a/Engine/source/T3D/components/animation/animationComponent.cpp b/Engine/source/T3D/components/animation/animationComponent.cpp index 59ff1fbf8..4081d626b 100644 --- a/Engine/source/T3D/components/animation/animationComponent.cpp +++ b/Engine/source/T3D/components/animation/animationComponent.cpp @@ -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"); diff --git a/Engine/source/T3D/components/animation/animationComponent.h b/Engine/source/T3D/components/animation/animationComponent.h index f1c01f40c..14702aee9 100644 --- a/Engine/source/T3D/components/animation/animationComponent.h +++ b/Engine/source/T3D/components/animation/animationComponent.h @@ -101,7 +101,7 @@ public: TSShape* getShape(); - void targetShapeChanged(RenderComponentInterface* instanceInterface); + virtual void targetShapeChanged(RenderComponentInterface* instanceInterface); virtual void processTick(); virtual void advanceTime(F32 dt); diff --git a/Engine/source/T3D/components/audio/SoundComponent.cpp b/Engine/source/T3D/components/audio/SoundComponent.cpp index 9552c2d67..9e688e34a 100644 --- a/Engine/source/T3D/components/audio/SoundComponent.cpp +++ b/Engine/source/T3D/components/audio/SoundComponent.cpp @@ -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. diff --git a/Engine/source/T3D/components/collision/collisionComponent.cpp b/Engine/source/T3D/components/collision/collisionComponent.cpp index 5eee24a9e..f7099085f 100644 --- a/Engine/source/T3D/components/collision/collisionComponent.cpp +++ b/Engine/source/T3D/components/collision/collisionComponent.cpp @@ -21,123 +21,33 @@ //----------------------------------------------------------------------------- #include "T3D/components/collision/collisionComponent.h" -#include "T3D/components/collision/collisionComponent_ScriptBinding.h" -#include "T3D/components/physics/physicsBehavior.h" -#include "console/consoleTypes.h" -#include "core/util/safeDelete.h" -#include "core/resourceManager.h" -#include "console/consoleTypes.h" -#include "console/consoleObject.h" -#include "core/stream/bitStream.h" -#include "scene/sceneRenderState.h" -#include "gfx/gfxTransformSaver.h" -#include "gfx/gfxDrawUtil.h" +#include "scene/sceneObject.h" +#include "T3D/entity.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 "materials/baseMatInstance.h" +#include "collision/extrudedPolyList.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 "collision/clippedPolyList.h" -#include "math/mathUtils.h" -#include "materials/baseMatInstance.h" -#include "collision/vertexPolyList.h" +static F32 sTractionDistance = 0.04f; -extern bool gEditingMission; +IMPLEMENT_CONOBJECT(CollisionComponent); -static bool sRenderColliders = false; - -//Docs -ConsoleDocClass(CollisionComponent, - "@brief The Box Collider component uses a box or rectangular convex shape for collisions.\n\n" - - "Colliders are individualized components that are similarly based off the CollisionInterface core.\n" - "They are basically the entire functionality of how Torque handles collisions compacted into a single component.\n" - "A collider will both collide against and be collided with, other entities.\n" - "Individual colliders will offer different shapes. This box collider will generate a box/rectangle convex, \n" - "while the mesh collider will take the owner Entity's rendered shape and do polysoup collision on it, etc.\n\n" - - "The general flow of operations for how collisions happen is thus:\n" - " -When the component is added(or updated) prepCollision() is called.\n" - " This will set up our initial convex shape for usage later.\n\n" - - " -When we update via processTick(), we first test if our entity owner is mobile.\n" - " If our owner isn't mobile(as in, they have no components that provide it a velocity to move)\n" - " then we skip doing our active collision checks. Collisions are checked by the things moving, as\n" - " opposed to being reactionary. If we're moving, we call updateWorkingCollisionSet().\n" - " updateWorkingCollisionSet() estimates our bounding space for our current ticket based on our position and velocity.\n" - " If our bounding space has changed since the last tick, we proceed to call updateWorkingList() on our convex.\n" - " This notifies any object in the bounding space that they may be collided with, so they will call buildConvex().\n" - " buildConvex() will set up our ConvexList with our collision convex info.\n\n" - - " -When the component that is actually causing our movement, such as SimplePhysicsBehavior, updates, it will check collisions.\n" - " It will call checkCollisions() on us. checkCollisions() will first build a bounding shape for our convex, and test\n" - " if we can early out because we won't hit anything based on our starting point, velocity, and tick time.\n" - " If we don't early out, we proceed to call updateCollisions(). This builds an ExtrudePolyList, which is then extruded\n" - " based on our velocity. We then test our extruded polies on our working list of objects we build\n" - " up earlier via updateWorkingCollisionSet. Any collisions that happen here will be added to our mCollisionList.\n" - " Finally, we call handleCollisionList() on our collisionList, which then queues out the colliison notice\n" - " to the object(s) we collided with so they can do callbacks and the like. We also report back on if we did collide\n" - " to the physics component via our bool return in checkCollisions() so it can make the physics react accordingly.\n\n" - - "One interesting point to note is the usage of mBlockColliding.\n" - "This is set so that it dictates the return on checkCollisions(). If set to false, it will ensure checkCollisions()\n" - "will return false, regardless if we actually collided. This is useful, because even if checkCollisions() returns false,\n" - "we still handle the collisions so the callbacks happen. This enables us to apply a collider to an object that doesn't block\n" - "objects, but does have callbacks, so it can act as a trigger, allowing for arbitrarily shaped triggers, as any collider can\n" - "act as a trigger volume(including MeshCollider).\n\n" - - "@tsexample\n" - "new CollisionComponentInstance()\n" - "{\n" - " template = CollisionComponentTemplate;\n" - " colliderSize = \"1 1 2\";\n" - " blockColldingObject = \"1\";\n" - "};\n" - "@endtsexample\n" - - "@see SimplePhysicsBehavior\n" - "@ingroup Collision\n" - "@ingroup Components\n" - ); -//Docs - -///////////////////////////////////////////////////////////////////////// -ImplementEnumType(CollisionMeshMeshType, - "Type of mesh data available in a shape.\n" - "@ingroup gameObjects") -{ CollisionComponent::None, "None", "No mesh data." }, -{ CollisionComponent::Bounds, "Bounds", "Bounding box of the shape." }, -{ CollisionComponent::CollisionMesh, "Collision Mesh", "Specifically desingated \"collision\" meshes." }, -{ CollisionComponent::VisibleMesh, "Visible Mesh", "Rendered mesh polygons." }, -EndImplementEnumType; - -// CollisionComponent::CollisionComponent() : Component() { - mFriendlyName = "Collision(Component)"; + mFriendlyName = "Collision Component"; - mOwnerRenderInterface = NULL; - mOwnerPhysicsInterface = NULL; + mComponentType = "Collision"; + + mDescription = getDescriptionText("A stub component class that collision components should inherit from."); mBlockColliding = true; - mCollisionType = CollisionMesh; - mLOSType = CollisionMesh; - mDecalType = CollisionMesh; - - colisionMeshPrefix = StringTable->insert("Collision"); - CollisionMoveMask = (TerrainObjectType | PlayerObjectType | StaticShapeObjectType | VehicleObjectType | VehicleBlockerObjectType | DynamicShapeObjectType | StaticObjectType | EntityObjectType | TriggerObjectType); @@ -146,8 +56,6 @@ CollisionComponent::CollisionComponent() : Component() mPhysicsWorld = nullptr; mTimeoutList = nullptr; - - mAnimated = false; } CollisionComponent::~CollisionComponent() @@ -159,423 +67,807 @@ CollisionComponent::~CollisionComponent() } SAFE_DELETE_ARRAY(mDescription); -} -IMPLEMENT_CO_NETOBJECT_V1(CollisionComponent); - -void CollisionComponent::onComponentAdd() -{ - Parent::onComponentAdd(); - - RenderComponentInterface *renderInterface = mOwner->getComponent(); - if (renderInterface) - { - renderInterface->onShapeInstanceChanged.notify(this, &CollisionComponent::targetShapeChanged); - mOwnerRenderInterface = renderInterface; - } - - //physicsInterface - PhysicsComponentInterface *physicsInterface = mOwner->getComponent(); - if (!physicsInterface) - { - mPhysicsRep = PHYSICSMGR->createBody(); - } - - prepCollision(); -} - -void CollisionComponent::onComponentRemove() -{ SAFE_DELETE(mPhysicsRep); - - Parent::onComponentRemove(); } -void CollisionComponent::componentAddedToOwner(Component *comp) +bool CollisionComponent::checkCollisions(const F32 travelTime, Point3F *velocity, Point3F start) { - if (comp->getId() == getId()) - return; - - //test if this is a shape component! - RenderComponentInterface *renderInterface = dynamic_cast(comp); - if (renderInterface) - { - renderInterface->onShapeInstanceChanged.notify(this, &CollisionComponent::targetShapeChanged); - mOwnerRenderInterface = renderInterface; - prepCollision(); - } - - PhysicsComponentInterface *physicsInterface = dynamic_cast(comp); - if (physicsInterface) - { - if (mPhysicsRep) - SAFE_DELETE(mPhysicsRep); - - prepCollision(); - } + return false; } -void CollisionComponent::componentRemovedFromOwner(Component *comp) +bool CollisionComponent::updateCollisions(F32 time, VectorF vector, VectorF velocity) { - if (comp->getId() == getId()) //????????? - return; - - //test if this is a shape component! - RenderComponentInterface *renderInterface = dynamic_cast(comp); - if (renderInterface) - { - renderInterface->onShapeInstanceChanged.remove(this, &CollisionComponent::targetShapeChanged); - mOwnerRenderInterface = NULL; - prepCollision(); - } - - //physicsInterface - PhysicsComponentInterface *physicsInterface = dynamic_cast(comp); - if (physicsInterface) - { - mPhysicsRep = PHYSICSMGR->createBody(); - - prepCollision(); - } + return false; } -void CollisionComponent::checkDependencies() +void CollisionComponent::updateWorkingCollisionSet(const U32 mask) { } -void CollisionComponent::initPersistFields() +void CollisionComponent::handleCollisionList( CollisionList &collisionList, VectorF velocity ) { - Parent::initPersistFields(); + Collision bestCol; - addGroup("Collision"); + mCollisionList = collisionList; - addField("CollisionType", TypeCollisionMeshMeshType, Offset(mCollisionType, CollisionComponent), - "The type of mesh data to use for collision queries."); - - addField("LineOfSightType", TypeCollisionMeshMeshType, Offset(mLOSType, CollisionComponent), - "The type of mesh data to use for collision queries."); - - addField("DecalType", TypeCollisionMeshMeshType, Offset(mDecalType, CollisionComponent), - "The type of mesh data to use for collision queries."); - - addField("CollisionMeshPrefix", TypeString, Offset(colisionMeshPrefix, CollisionComponent), - "The type of mesh data to use for collision queries."); - - addField("BlockCollisions", TypeBool, Offset(mBlockColliding, CollisionComponent), ""); - - endGroup("Collision"); -} - -void CollisionComponent::inspectPostApply() -{ - // Apply any transformations set in the editor - Parent::inspectPostApply(); - - if (isServerObject()) + for (U32 i=0; i < collisionList.getCount(); ++i) { - setMaskBits(ColliderMask); - prepCollision(); - } -} + Collision& colCheck = collisionList[i]; -U32 CollisionComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream) -{ - U32 retMask = Parent::packUpdate(con, mask, stream); - - if (stream->writeFlag(mask & (ColliderMask | InitialUpdateMask))) - { - stream->write((U32)mCollisionType); - stream->writeString(colisionMeshPrefix); - } - - return retMask; -} - -void CollisionComponent::unpackUpdate(NetConnection *con, BitStream *stream) -{ - Parent::unpackUpdate(con, stream); - - if (stream->readFlag()) // UpdateMask - { - U32 collisionType = CollisionMesh; - - stream->read(&collisionType); - - // Handle it if we have changed CollisionType's - if ((MeshType)collisionType != mCollisionType) + if (colCheck.object) { - mCollisionType = (MeshType)collisionType; + 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(colCheck.object); - prepCollision(); - } + Component *comp = dynamic_cast(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(colCheck.object); + if (ent) + { + CollisionComponent *colObjectInterface = ent->getComponent(); + if (colObjectInterface) + { + //convert us to our component + Component *thisComp = dynamic_cast(this); + if (thisComp) + { + colObjectInterface->onCollisionSignal.trigger(thisComp->getOwner()); - char readBuffer[1024]; + //TODO: properly do this + Collision oppositeCol = colCheck; + oppositeCol.object = thisComp->getOwner(); - stream->readString(readBuffer); - colisionMeshPrefix = StringTable->insert(readBuffer); - } -} - -void CollisionComponent::ownerTransformSet(MatrixF *mat) -{ - if (mPhysicsRep) - mPhysicsRep->setTransform(mOwner->getTransform()); -} - -void CollisionComponent::targetShapeChanged(RenderComponentInterface* instanceInterface) -{ - prepCollision(); -} - -void CollisionComponent::prepCollision() -{ - if (!mOwner) - return; - - // Let the client know that the collision was updated - setMaskBits(ColliderMask); - - mOwner->disableCollision(); - - if ((!PHYSICSMGR || mCollisionType == None) || - (mOwnerRenderInterface == NULL && (mCollisionType == CollisionMesh || mCollisionType == VisibleMesh))) - return; - - PhysicsCollision *colShape = NULL; - - if (mCollisionType == Bounds) - { - MatrixF offset(true); - - if (mOwnerRenderInterface && mOwnerRenderInterface->getShape()) - offset.setPosition(mOwnerRenderInterface->getShape()->center); - - colShape = PHYSICSMGR->createCollision(); - colShape->addBox(mOwner->getObjBox().getExtents() * 0.5f * mOwner->getScale(), offset); - } - else if (mCollisionType == CollisionMesh || (mCollisionType == VisibleMesh /*&& !mOwner->getComponent()*/)) - { - colShape = buildColShapes(); - } - - if (colShape) - { - mPhysicsWorld = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client"); - - if (mPhysicsRep) - { - if (mBlockColliding) - mPhysicsRep->init(colShape, 0, 0, mOwner, mPhysicsWorld); + colObjectInterface->handleCollision(oppositeCol, velocity); + } + } + } + } else - mPhysicsRep->init(colShape, 0, PhysicsBody::BF_TRIGGER, mOwner, mPhysicsWorld); + { + handleCollision(colCheck, velocity); + } + } + } +} - mPhysicsRep->setTransform(mOwner->getTransform()); +void CollisionComponent::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(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 CollisionComponent::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(this); + if (thisComp) + { + mCollisionNotifyList[i]->onCollisionSignal.trigger(thisComp->getOwner()); } } - mOwner->enableCollision(); - - onCollisionChanged.trigger(colShape); + mCollisionNotifyList.clear(); } -void CollisionComponent::processTick() +Chunker sCollisionTimeoutChunker; +CollisionComponent::CollisionTimeout* CollisionComponent::sFreeTimeoutList = 0; + +void CollisionComponent::queueCollision( SceneObject *obj, const VectorF &vec) { - if (!isActive()) - return; + // Add object to list of collisions. + SimTime time = Sim::getCurrentTime(); + S32 num = obj->getId(); - //ProcessTick is where our collision testing begins! - - //callback if we have a persisting contact - if (mContactInfo.contactObject) + CollisionTimeout** adr = &mTimeoutList; + CollisionTimeout* ptr = mTimeoutList; + while (ptr) { - if (mContactInfo.contactTimer > 0) + if (ptr->objectNumber == num) { - if (isMethod("updateContact")) - Con::executef(this, "updateContact"); + 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; + } + } - if (mOwner->isMethod("updateContact")) - Con::executef(mOwner, "updateContact"); + // 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 CollisionComponent::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; } - ++mContactInfo.contactTimer; - } - else if (mContactInfo.contactTimer != 0) - mContactInfo.clear(); -} - -void CollisionComponent::updatePhysics() -{ - -} - -PhysicsCollision* CollisionComponent::getCollisionData() -{ - if ((!PHYSICSMGR || mCollisionType == None) || mOwnerRenderInterface == NULL) - return NULL; - - PhysicsCollision *colShape = NULL; - if (mCollisionType == Bounds) - { - MatrixF offset(true); - offset.setPosition(mOwnerRenderInterface->getShape()->center); - colShape = PHYSICSMGR->createCollision(); - colShape->addBox(mOwner->getObjBox().getExtents() * 0.5f * mOwner->getScale(), offset); - } - else if (mCollisionType == CollisionMesh || (mCollisionType == VisibleMesh/* && !mOwner->getComponent()*/)) - { - colShape = buildColShapes(); - //colShape = mOwnerShapeInstance->getShape()->buildColShape(mCollisionType == VisibleMesh, mOwner->getScale()); - } - /*else if (mCollisionType == VisibleMesh && !mOwner->getComponent()) - { - //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()*/) - { - Con::printf("CollisionComponent::updatePhysics: Cannot use visible mesh collisions with an animated mesh!"); - } - - return colShape; -} - -bool CollisionComponent::castRay(const Point3F &start, const Point3F &end, RayInfo* info) -{ - if (!mCollisionType == None) - { - if (mPhysicsWorld) + if (eaPolyList.isEmpty()) { - return mPhysicsWorld->castRay(start, end, info, Point3F::Zero); + return true; } } return false; } -PhysicsCollision* CollisionComponent::buildColShapes() + +Collision* CollisionComponent::getCollision(S32 col) +{ + if(col < mCollisionList.getCount() && col >= 0) + return &mCollisionList[col]; + else + return NULL; +} + +Point3F CollisionComponent::getContactNormal() +{ + return mContactInfo.contactNormal; +} + +bool CollisionComponent::hasContact() { - PROFILE_SCOPE(CollisionComponent_buildColShapes); + if (mContactInfo.contactObject) + return true; + else + return false; +} - PhysicsCollision *colShape = NULL; - U32 surfaceKey = 0; +S32 CollisionComponent::getCollisionCount() +{ + return mCollisionList.getCount(); +} - TSShape* shape = mOwnerRenderInterface->getShape(); +Point3F CollisionComponent::getCollisionNormal(S32 collisionIndex) +{ + if (collisionIndex < 0 || mCollisionList.getCount() < collisionIndex) + return Point3F::Zero; - if (mCollisionType == VisibleMesh) + return mCollisionList[collisionIndex].normal; +} + +F32 CollisionComponent::getCollisionAngle(S32 collisionIndex, Point3F upVector) +{ + if (collisionIndex < 0 || mCollisionList.getCount() < collisionIndex) + return 0.0f; + + return mRadToDeg(mAcos(mDot(mCollisionList[collisionIndex].normal, upVector))); +} + +S32 CollisionComponent::getBestCollision(Point3F upVector) +{ + S32 bestCollision = -1; + + F32 bestAngle = 360.f; + S32 count = mCollisionList.getCount(); + for (U32 i = 0; i < count; ++i) { - // Here we build triangle collision meshes from the - // visible detail levels. + F32 angle = mRadToDeg(mAcos(mDot(mCollisionList[i].normal, upVector))); - // 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++) + if (angle < bestAngle) { - const TSShape::Object &object = shape->objects[o]; - if (detail.objectDetailNum >= object.numMeshes) + bestCollision = i; + bestAngle = angle; + } + } + + return bestCollision; +} + +F32 CollisionComponent::getBestCollisionAngle(VectorF upVector) +{ + S32 bestCol = getBestCollision(upVector); + + if (bestCol == -1) + return 0; + + return getCollisionAngle(bestCol, upVector); +} + +// +bool CollisionComponent::buildConvexOpcode(TSShapeInstance* sI, S32 dl, const Box3F &bounds, Convex *c, Convex *list) +{ + AssertFatal(dl >= 0 && dl < sI->getShape()->details.size(), "TSShapeInstance::buildConvexOpcode"); + + TSShape* shape = sI->getShape(); + + const MatrixF &objMat = mOwner->getObjToWorld(); + const Point3F &objScale = mOwner->getScale(); + + // get subshape and object detail + const TSDetail * detail = &shape->details[dl]; + S32 ss = detail->subShapeNum; + S32 od = detail->objectDetailNum; + + // nothing emitted yet... + bool emitted = false; + + S32 start = shape->subShapeFirstObject[ss]; + S32 end = shape->subShapeNumObjects[ss] + start; + if (startmMeshObjects[start].getTransform(); + mat.mul(initialMat, scaleMat); + mat.mul(*previousMat); + + // Update our bounding box... + Box3F localBox = bounds; + MatrixF otherMat = mat; + otherMat.inverse(); + otherMat.mul(localBox); + + // run through objects and collide + for (S32 i = start; imMeshObjects[i]; + + if (od >= meshInstance->object->numMeshes) continue; - // No mesh or no verts.... nothing to do. - TSMesh *mesh = shape->meshes[object.startMeshIndex + detail.objectDetailNum]; - if (!mesh || mesh->mNumVerts == 0) + if (&meshInstance->getTransform() != previousMat) + { + // different node from before, set up for this node + previousMat = &meshInstance->getTransform(); + + if (previousMat != NULL) + { + mat.mul(initialMat, scaleMat); + mat.mul(*previousMat); + + // Update our bounding box... + otherMat = mat; + otherMat.inverse(); + localBox = bounds; + otherMat.mul(localBox); + } + } + + // collide... note we pass the original mech transform + // here so that the convex data returned is in mesh space. + TSMesh * mesh = meshInstance->getMesh(od); + if (mesh && !meshInstance->forceHidden && meshInstance->visible > 0.01f && localBox.isOverlapped(mesh->getBounds())) + emitted |= buildMeshOpcode(mesh, *previousMat, localBox, c, list); + else + emitted |= false; + } + } + + return emitted; +} + +bool CollisionComponent::buildMeshOpcode(TSMesh *mesh, const MatrixF &meshToObjectMat, + const Box3F &nodeBox, Convex *convex, Convex *list) +{ + /*PROFILE_SCOPE(MeshCollider_buildConvexOpcode); + + // This is small... there is no win for preallocating it. + Opcode::AABBCollider opCollider; + opCollider.SetPrimitiveTests(true); + + // This isn't really needed within the AABBCollider as + // we don't use temporal coherance... use a static to + // remove the allocation overhead. + static Opcode::AABBCache opCache; + + IceMaths::AABB opBox; + opBox.SetMinMax(Point(nodeBox.minExtents.x, nodeBox.minExtents.y, nodeBox.minExtents.z), + Point(nodeBox.maxExtents.x, nodeBox.maxExtents.y, nodeBox.maxExtents.z)); + Opcode::CollisionAABB opCBox(opBox); + + if (!opCollider.Collide(opCache, opCBox, *mesh->mOptTree)) + return false; + + U32 cnt = opCollider.GetNbTouchedPrimitives(); + const udword *idx = opCollider.GetTouchedPrimitives(); + + Opcode::VertexPointers vp; + for (S32 i = 0; i < cnt; i++) + { + // First, check our active convexes for a potential match (and clean things + // up, too.) + const U32 curIdx = idx[i]; + + // See if the square already exists as part of the working set. + bool gotMatch = false; + CollisionWorkingList& wl = convex->getWorkingList(); + for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) + { + if (itr->mConvex->getType() != TSPolysoupConvexType) continue; - // Gather the mesh triangles. - polyList.clear(); - mesh->buildPolyList(0, &polyList, surfaceKey, NULL); + const MeshColliderPolysoupConvex *chunkc = static_cast(itr->mConvex); - // Create the collision shape if we haven't already. - if (!colShape) - colShape = PHYSICSMGR->createCollision(); + if (chunkc->getObject() != mOwner) + continue; - // Get the object space mesh transform. - MatrixF localXfm; - shape->getNodeWorldTransform(object.nodeIndex, &localXfm); + if (chunkc->mesh != mesh) + continue; - colShape->addTriangleMesh(polyList.mVertexList.address(), - polyList.mVertexList.size(), - polyList.mIndexList.address(), - polyList.mIndexList.size() / 3, - localXfm); + if (chunkc->idx != curIdx) + continue; + + // A match! Don't need to add it. + gotMatch = true; + break; } - // Return what we built... if anything. - return colShape; + if (gotMatch) + continue; + + // Get the triangle... + mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, idx[i]); + + Point3F a(vp.Vertex[0]->x, vp.Vertex[0]->y, vp.Vertex[0]->z); + Point3F b(vp.Vertex[1]->x, vp.Vertex[1]->y, vp.Vertex[1]->z); + Point3F c(vp.Vertex[2]->x, vp.Vertex[2]->y, vp.Vertex[2]->z); + + // Transform the result into object space! + meshToObjectMat.mulP(a); + meshToObjectMat.mulP(b); + meshToObjectMat.mulP(c); + + //If we're not doing debug rendering on the client, then set up our convex list as normal + PlaneF p(c, b, a); + Point3F peak = ((a + b + c) / 3.0f) - (p * 0.15f); + + // Set up the convex... + MeshColliderPolysoupConvex *cp = new MeshColliderPolysoupConvex(); + + list->registerObject(cp); + convex->addToWorkingList(cp); + + cp->mesh = mesh; + cp->idx = curIdx; + cp->mObject = mOwner; + + cp->normal = p; + cp->verts[0] = a; + cp->verts[1] = b; + cp->verts[2] = c; + cp->verts[3] = peak; + + // Update the bounding box. + Box3F &bounds = cp->box; + bounds.minExtents.set(F32_MAX, F32_MAX, F32_MAX); + bounds.maxExtents.set(-F32_MAX, -F32_MAX, -F32_MAX); + + bounds.minExtents.setMin(a); + bounds.minExtents.setMin(b); + bounds.minExtents.setMin(c); + bounds.minExtents.setMin(peak); + + bounds.maxExtents.setMax(a); + bounds.maxExtents.setMax(b); + bounds.maxExtents.setMax(c); + bounds.maxExtents.setMax(peak); } - else if (mCollisionType == CollisionMesh) + + return true;*/ + return false; +} + +bool CollisionComponent::castRayOpcode(S32 dl, const Point3F & startPos, const Point3F & endPos, RayInfo *info) +{ + // if dl==-1, nothing to do + //if (dl == -1 || !getShapeInstance()) + return false; + + /*TSShape *shape = getShapeInstance()->getShape(); + + AssertFatal(dl >= 0 && dl < shape->details.size(), "TSShapeInstance::castRayOpcode"); + + info->t = 100.f; + + // get subshape and object detail + const TSDetail * detail = &shape->details[dl]; + S32 ss = detail->subShapeNum; + if (ss < 0) + return false; + + S32 od = detail->objectDetailNum; + + // nothing emitted yet... + bool emitted = false; + + const MatrixF* saveMat = NULL; + S32 start = shape->subShapeFirstObject[ss]; + S32 end = shape->subShapeNumObjects[ss] + start; + if (startmMeshObjects[start].getTransform(); + mat = *previousMat; + mat.inverse(); + Point3F localStart, localEnd; + mat.mulP(startPos, &localStart); + mat.mulP(endPos, &localEnd); - // Scan out the collision hulls... - // - // TODO: We need to support LOS collision for physics. - // - for (U32 i = 0; i < shape->details.size(); i++) + // run through objects and collide + for (S32 i = start; idetails[i]; - const String &name = shape->names[detail.nameIndex]; + TSShapeInstance::MeshObjectInstance * meshInstance = &getShapeInstance()->mMeshObjects[i]; - // Is this a valid collision detail. - if (!dStrStartsWith(name, colisionMeshPrefix) || detail.subShapeNum < 0) + if (od >= meshInstance->object->numMeshes) 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++) + if (&meshInstance->getTransform() != previousMat) { - const TSShape::Object &object = shape->objects[o]; + // different node from before, set up for this node + previousMat = &meshInstance->getTransform(); - if (object.numMeshes <= detail.objectDetailNum) - continue; + if (previousMat != NULL) + { + mat = *previousMat; + mat.inverse(); + mat.mulP(startPos, &localStart); + mat.mulP(endPos, &localEnd); + } + } - // 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 + // collide... + TSMesh * mesh = meshInstance->getMesh(od); + if (mesh && !meshInstance->forceHidden && meshInstance->visible > 0.01f) + { + if (castRayMeshOpcode(mesh, localStart, localEnd, info, getShapeInstance()->mMaterialList)) + { + saveMat = previousMat; + emitted = true; + } + } + } } - return colShape; + if (emitted) + { + saveMat->mulV(info->normal); + info->point = endPos - startPos; + info->point *= info->t; + info->point += startPos; + } + + return emitted;*/ +} + +static Point3F texGenAxis[18] = +{ + Point3F(0,0,1), Point3F(1,0,0), Point3F(0,-1,0), + Point3F(0,0,-1), Point3F(1,0,0), Point3F(0,1,0), + Point3F(1,0,0), Point3F(0,1,0), Point3F(0,0,1), + Point3F(-1,0,0), Point3F(0,1,0), Point3F(0,0,-1), + Point3F(0,1,0), Point3F(1,0,0), Point3F(0,0,1), + Point3F(0,-1,0), Point3F(-1,0,0), Point3F(0,0,-1) +}; + +bool CollisionComponent::castRayMeshOpcode(TSMesh *mesh, const Point3F &s, const Point3F &e, RayInfo *info, TSMaterialList *materials) +{ + Opcode::RayCollider ray; + Opcode::CollisionFaces cfs; + + IceMaths::Point dir(e.x - s.x, e.y - s.y, e.z - s.z); + const F32 rayLen = dir.Magnitude(); + IceMaths::Ray vec(Point(s.x, s.y, s.z), dir.Normalize()); + + ray.SetDestination(&cfs); + ray.SetFirstContact(false); + ray.SetClosestHit(true); + ray.SetPrimitiveTests(true); + ray.SetCulling(true); + ray.SetMaxDist(rayLen); + + AssertFatal(ray.ValidateSettings() == NULL, "invalid ray settings"); + + // Do collision. + bool safety = ray.Collide(vec, *mesh->mOptTree); + AssertFatal(safety, "CollisionComponent::castRayOpcode - no good ray collide!"); + + // If no hit, just skip out. + if (cfs.GetNbFaces() == 0) + return false; + + // Got a hit! + AssertFatal(cfs.GetNbFaces() == 1, "bad"); + const Opcode::CollisionFace &face = cfs.GetFaces()[0]; + + // If the cast was successful let's check if the t value is less than what we had + // and toggle the collision boolean + // Stupid t... i prefer coffee + const F32 t = face.mDistance / rayLen; + + if (t < 0.0f || t > 1.0f) + return false; + + if (t <= info->t) + { + info->t = t; + + // Calculate the normal. + Opcode::VertexPointers vp; + mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, face.mFaceID); + + if (materials && vp.MatIdx >= 0 && vp.MatIdx < materials->size()) + info->material = materials->getMaterialInst(vp.MatIdx); + + // Get the two edges. + IceMaths::Point baseVert = *vp.Vertex[0]; + IceMaths::Point a = *vp.Vertex[1] - baseVert; + IceMaths::Point b = *vp.Vertex[2] - baseVert; + + IceMaths::Point n; + n.Cross(a, b); + n.Normalize(); + + info->normal.set(n.x, n.y, n.z); + + // generate UV coordinate across mesh based on + // matching normals, this isn't done by default and is + // primarily of interest in matching a collision point to + // either a GUI control coordinate or finding a hit pixel in texture space + if (info->generateTexCoord) + { + baseVert = *vp.Vertex[0]; + a = *vp.Vertex[1]; + b = *vp.Vertex[2]; + + Point3F facePoint = (1.0f - face.mU - face.mV) * Point3F(baseVert.x, baseVert.y, baseVert.z) + + face.mU * Point3F(a.x, a.y, a.z) + face.mV * Point3F(b.x, b.y, b.z); + + U32 faces[1024]; + U32 numFaces = 0; + for (U32 i = 0; i < mesh->mOptTree->GetMeshInterface()->GetNbTriangles(); i++) + { + if (i == face.mFaceID) + { + faces[numFaces++] = i; + } + else + { + IceMaths::Point n2; + + mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, i); + + baseVert = *vp.Vertex[0]; + a = *vp.Vertex[1] - baseVert; + b = *vp.Vertex[2] - baseVert; + n2.Cross(a, b); + n2.Normalize(); + + F32 eps = .01f; + if (mFabs(n.x - n2.x) < eps && mFabs(n.y - n2.y) < eps && mFabs(n.z - n2.z) < eps) + { + faces[numFaces++] = i; + } + } + + if (numFaces == 1024) + { + // too many faces in this collision mesh for UV generation + return true; + } + + } + + Point3F min(F32_MAX, F32_MAX, F32_MAX); + Point3F max(-F32_MAX, -F32_MAX, -F32_MAX); + + for (U32 i = 0; i < numFaces; i++) + { + mesh->mOptTree->GetMeshInterface()->GetTriangle(vp, faces[i]); + + for (U32 j = 0; j < 3; j++) + { + a = *vp.Vertex[j]; + + if (a.x < min.x) + min.x = a.x; + if (a.y < min.y) + min.y = a.y; + if (a.z < min.z) + min.z = a.z; + + if (a.x > max.x) + max.x = a.x; + if (a.y > max.y) + max.y = a.y; + if (a.z > max.z) + max.z = a.z; + + } + + } + + // slerp + Point3F s = ((max - min) - (facePoint - min)) / (max - min); + + // compute axis + S32 bestAxis = 0; + F32 best = 0.f; + + for (U32 i = 0; i < 6; i++) + { + F32 dot = mDot(info->normal, texGenAxis[i * 3]); + if (dot > best) + { + best = dot; + bestAxis = i; + } + } + + Point3F xv = texGenAxis[bestAxis * 3 + 1]; + Point3F yv = texGenAxis[bestAxis * 3 + 2]; + + S32 sv, tv; + + if (xv.x) + sv = 0; + else if (xv.y) + sv = 1; + else + sv = 2; + + if (yv.x) + tv = 0; + else if (yv.y) + tv = 1; + else + tv = 2; + + // handle coord translation + if (bestAxis == 2 || bestAxis == 3) + { + S32 x = sv; + sv = tv; + tv = x; + + if (yv.z < 0) + s[sv] = 1.f - s[sv]; + } + + if (bestAxis < 2) + { + if (yv.y < 0) + s[sv] = 1.f - s[sv]; + } + + if (bestAxis > 3) + { + s[sv] = 1.f - s[sv]; + if (yv.z > 0) + s[tv] = 1.f - s[tv]; + + } + + // done! + info->texCoord.set(s[sv], s[tv]); + + } + + return true; + } + + return false; } \ No newline at end of file diff --git a/Engine/source/T3D/components/collision/collisionComponent.h b/Engine/source/T3D/components/collision/collisionComponent.h index 2d1be3098..efcd62418 100644 --- a/Engine/source/T3D/components/collision/collisionComponent.h +++ b/Engine/source/T3D/components/collision/collisionComponent.h @@ -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 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 mCollisionDetails; - Vector mLOSDetails; + CollisionList mCollisionList; + Vector 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 \ No newline at end of file diff --git a/Engine/source/T3D/components/collision/collisionInterfaces.cpp b/Engine/source/T3D/components/collision/collisionInterfaces.cpp deleted file mode 100644 index c45dbde48..000000000 --- a/Engine/source/T3D/components/collision/collisionInterfaces.cpp +++ /dev/null @@ -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(colCheck.object); - - Component *comp = dynamic_cast(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(colCheck.object); - if (ent) - { - CollisionInterface *colObjectInterface = ent->getComponent(); - if (colObjectInterface) - { - //convert us to our component - Component *thisComp = dynamic_cast(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(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(this); - if (thisComp) - { - mCollisionNotifyList[i]->onCollisionSignal.trigger(thisComp->getOwner()); - } - } - - mCollisionNotifyList.clear(); -} - -Chunker 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; -} \ No newline at end of file diff --git a/Engine/source/T3D/components/collision/collisionInterfaces.h b/Engine/source/T3D/components/collision/collisionInterfaces.h deleted file mode 100644 index 6592d6015..000000000 --- a/Engine/source/T3D/components/collision/collisionInterfaces.h +++ /dev/null @@ -1,167 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#ifndef COLLISION_INTERFACES_H -#define COLLISION_INTERFACES_H - -#ifndef _CONVEX_H_ -#include "collision/convex.h" -#endif -#ifndef _COLLISION_H_ -#include "collision/collision.h" -#endif -#ifndef _EARLYOUTPOLYLIST_H_ -#include "collision/earlyOutPolyList.h" -#endif -#ifndef _SIM_H_ -#include "console/sim.h" -#endif -#ifndef _SCENECONTAINER_H_ -#include "scene/sceneContainer.h" -#endif -#ifndef _T3D_PHYSICSCOMMON_H_ -#include "T3D/physics/physicsCommon.h" -#endif - -struct ContactInfo -{ - bool contacted, move; - SceneObject *contactObject; - VectorF idealContactNormal; - VectorF contactNormal; - Point3F contactPoint; - F32 contactTime; - S32 contactTimer; - BaseMatInstance *contactMaterial; - - void clear() - { - contacted=move=false; - contactObject = NULL; - contactNormal.set(0,0,0); - contactTime = 0.f; - contactTimer = 0; - idealContactNormal.set(0, 0, 1); - contactMaterial = NULL; - } - - ContactInfo() { clear(); } - -}; - -class CollisionInterface// : public Interface -{ -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 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 -{ -public: - virtual void buildConvex(const Box3F& box, Convex* convex)=0; -}; - -class BuildPolyListInterface// : public Interface -{ -public: - virtual bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere) = 0; -}; - -#endif \ No newline at end of file diff --git a/Engine/source/T3D/components/collision/collisionTrigger.cpp b/Engine/source/T3D/components/collision/collisionTrigger.cpp index efac30b55..844e4f569 100644 --- a/Engine/source/T3D/components/collision/collisionTrigger.cpp +++ b/Engine/source/T3D/components/collision/collisionTrigger.cpp @@ -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) diff --git a/Engine/source/T3D/components/collision/raycastColliderComponent.cpp b/Engine/source/T3D/components/collision/raycastColliderComponent.cpp new file mode 100644 index 000000000..93975301e --- /dev/null +++ b/Engine/source/T3D/components/collision/raycastColliderComponent.cpp @@ -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(); + + if (physComp) + { + mOwnerPhysicsComponent = physComp; + } +} + +void RaycastColliderComponent::onComponentRemove() +{ + mOwnerPhysicsComponent = nullptr; +} + +void RaycastColliderComponent::componentAddedToOwner(Component *comp) +{ + Parent::componentAddedToOwner(comp); + + PhysicsComponent* physComp = dynamic_cast(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); +} \ No newline at end of file diff --git a/Engine/source/T3D/components/collision/raycastColliderComponent.h b/Engine/source/T3D/components/collision/raycastColliderComponent.h new file mode 100644 index 000000000..a6cd5c5d8 --- /dev/null +++ b/Engine/source/T3D/components/collision/raycastColliderComponent.h @@ -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); +}; \ No newline at end of file diff --git a/Engine/source/T3D/components/collision/shapeCollisionComponent.cpp b/Engine/source/T3D/components/collision/shapeCollisionComponent.cpp new file mode 100644 index 000000000..c18ec8df0 --- /dev/null +++ b/Engine/source/T3D/components/collision/shapeCollisionComponent.cpp @@ -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(); + if (renderInterface) + { + renderInterface->onShapeInstanceChanged.notify(this, &ShapeCollisionComponent::targetShapeChanged); + mOwnerRenderInterface = renderInterface; + } + + //physicsInterface + PhysicsComponent *physicsComp = mOwner->getComponent(); + 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(comp); + if (renderInterface) + { + renderInterface->onShapeInstanceChanged.notify(this, &ShapeCollisionComponent::targetShapeChanged); + mOwnerRenderInterface = renderInterface; + prepCollision(); + } + + PhysicsComponent *physicsComp = dynamic_cast(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(comp); + if (renderInterface) + { + renderInterface->onShapeInstanceChanged.remove(this, &ShapeCollisionComponent::targetShapeChanged); + mOwnerRenderInterface = NULL; + prepCollision(); + } + + //physicsInterface + PhysicsComponent *physicsComp = dynamic_cast(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()*/)) + { + 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()*/)) + { + colShape = buildColShapes(); + //colShape = mOwnerShapeInstance->getShape()->buildColShape(mCollisionType == VisibleMesh, mOwner->getScale()); + } + /*else if (mCollisionType == VisibleMesh && !mOwner->getComponent()) + { + //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()*/) + { + 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; +} \ No newline at end of file diff --git a/Engine/source/T3D/components/collision/shapeCollisionComponent.h b/Engine/source/T3D/components/collision/shapeCollisionComponent.h new file mode 100644 index 000000000..6debe3ef7 --- /dev/null +++ b/Engine/source/T3D/components/collision/shapeCollisionComponent.h @@ -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 mCollisionDetails; + Vector 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 diff --git a/Engine/source/T3D/components/collision/shapeCollisionComponent_ScriptBinding.h b/Engine/source/T3D/components/collision/shapeCollisionComponent_ScriptBinding.h new file mode 100644 index 000000000..1f72e1382 --- /dev/null +++ b/Engine/source/T3D/components/collision/shapeCollisionComponent_ScriptBinding.h @@ -0,0 +1,172 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/engineAPI.h" +#include "T3D/components/collision/shapeCollisionComponent.h" +#include "materials/baseMatInstance.h" + +DefineEngineMethod(CollisionComponent, getNumberOfContacts, S32, (), , + "Gets the number of contacts this collider has hit.\n" + "@return The number of static fields defined on the object.") +{ + return object->getCollisionList()->getCount(); +} + +DefineEngineMethod(CollisionComponent, getBestContact, S32, (), , + "Gets the number of contacts this collider has hit.\n" + "@return The number of static fields defined on the object.") +{ + return 0; +} + +DefineEngineMethod(CollisionComponent, getContactNormal, Point3F, (), , + "Gets the number of contacts this collider has hit.\n" + "@return The number of static fields defined on the object.") +{ + if (object->getContactInfo()) + { + if (object->getContactInfo()->contactObject) + { + return object->getContactInfo()->contactNormal; + } + } + + return Point3F::Zero; +} + +DefineEngineMethod(CollisionComponent, getContactMaterial, S32, (), , + "Gets the number of contacts this collider has hit.\n" + "@return The number of static fields defined on the object.") +{ + if (object->getContactInfo()) + { + if (object->getContactInfo()->contactObject) + { + if (object->getContactInfo()->contactMaterial != NULL) + return object->getContactInfo()->contactMaterial->getMaterial()->getId(); + } + } + + return 0; +} + +DefineEngineMethod(CollisionComponent, getContactObject, S32, (), , + "Gets the number of contacts this collider has hit.\n" + "@return The number of static fields defined on the object.") +{ + if (object->getContactInfo()) + { + return object->getContactInfo()->contactObject != NULL ? object->getContactInfo()->contactObject->getId() : 0; + } + + return 0; +} + +DefineEngineMethod(CollisionComponent, getContactPoint, Point3F, (), , + "Gets the number of contacts this collider has hit.\n" + "@return The number of static fields defined on the object.") +{ + if (object->getContactInfo()) + { + if (object->getContactInfo()->contactObject) + { + return object->getContactInfo()->contactPoint; + } + } + + return Point3F::Zero; +} + +DefineEngineMethod(CollisionComponent, getContactTime, S32, (), , + "Gets the number of contacts this collider has hit.\n" + "@return The number of static fields defined on the object.") +{ + if (object->getContactInfo()) + { + if (object->getContactInfo()->contactObject) + { + return object->getContactInfo()->contactTimer; + } + } + + return 0; +} + +DefineEngineMethod(ShapeCollisionComponent, hasContact, bool, (), , + "@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n" + + "@param pos impulse world position\n" + "@param vel impulse velocity (impulse force F = m * v)\n" + "@return Always true\n" + + "@note Not all objects that derrive from GameBase have this defined.\n") +{ + return object->hasContact(); +} + +DefineEngineMethod(ShapeCollisionComponent, getCollisionCount, S32, (), , + "@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n" + + "@param pos impulse world position\n" + "@param vel impulse velocity (impulse force F = m * v)\n" + "@return Always true\n" + + "@note Not all objects that derrive from GameBase have this defined.\n") +{ + return object->getCollisionCount(); +} + +DefineEngineMethod(ShapeCollisionComponent, getCollisionNormal, Point3F, (S32 collisionIndex), , + "@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n" + + "@param pos impulse world position\n" + "@param vel impulse velocity (impulse force F = m * v)\n" + "@return Always true\n" + + "@note Not all objects that derrive from GameBase have this defined.\n") +{ + return object->getCollisionNormal(collisionIndex); +} + +DefineEngineMethod(ShapeCollisionComponent, getCollisionAngle, F32, (S32 collisionIndex, VectorF upVector), , + "@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n" + + "@param pos impulse world position\n" + "@param vel impulse velocity (impulse force F = m * v)\n" + "@return Always true\n" + + "@note Not all objects that derrive from GameBase have this defined.\n") +{ + return object->getCollisionAngle(collisionIndex, upVector); +} + +DefineEngineMethod(ShapeCollisionComponent, getBestCollisionAngle, F32, (VectorF upVector), , + "@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n" + + "@param pos impulse world position\n" + "@param vel impulse velocity (impulse force F = m * v)\n" + "@return Always true\n" + + "@note Not all objects that derrive from GameBase have this defined.\n") +{ + return object->getBestCollisionAngle(upVector); +} \ No newline at end of file diff --git a/Engine/source/T3D/components/collision/simpleHitboxComponent.cpp b/Engine/source/T3D/components/collision/simpleHitboxComponent.cpp new file mode 100644 index 000000000..2c8a07ccb --- /dev/null +++ b/Engine/source/T3D/components/collision/simpleHitboxComponent.cpp @@ -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" + + "\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:
    " + "
  • head
  • " + "
  • torso
  • " + "
  • legs
\n" + + "Head modifiers:
    " + "
  • left_back
  • " + "
  • middle_back
  • " + "
  • right_back
  • " + "
  • left_middle
  • " + "
  • middle_middle
  • " + "
  • right_middle
  • " + "
  • left_front
  • " + "
  • middle_front
  • " + "
  • right_front
\n" + + "Legs/Torso modifiers:
    " + "
  • front_left
  • " + "
  • front_right
  • " + "
  • back_left
  • " + "
  • back_right
\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; +} \ No newline at end of file diff --git a/Engine/source/T3D/components/collision/simpleHitboxComponent.h b/Engine/source/T3D/components/collision/simpleHitboxComponent.h new file mode 100644 index 000000000..cce23348f --- /dev/null +++ b/Engine/source/T3D/components/collision/simpleHitboxComponent.h @@ -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); +}; diff --git a/Engine/source/T3D/components/component.cpp b/Engine/source/T3D/components/component.cpp index 92c9dda44..a4b8af249 100644 --- a/Engine/source/T3D/components/component.cpp +++ b/Engine/source/T3D/components/component.cpp @@ -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 diff --git a/Engine/source/T3D/components/component.h b/Engine/source/T3D/components/component.h index b585a30b0..801412896 100644 --- a/Engine/source/T3D/components/component.h +++ b/Engine/source/T3D/components/component.h @@ -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(); }; diff --git a/Engine/source/T3D/components/game/controlObjectComponent.cpp b/Engine/source/T3D/components/game/controlObjectComponent.cpp new file mode 100644 index 000000000..3e25c1c24 --- /dev/null +++ b/Engine/source/T3D/components/game/controlObjectComponent.cpp @@ -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()), +"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()), + "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()), + "Triggers a signal call to all components for a certain function.") +{ + if (conn == nullptr) + return; + + object->setConnectionControlObject(conn); +} diff --git a/Engine/source/T3D/components/game/controlObjectComponent.h b/Engine/source/T3D/components/game/controlObjectComponent.h new file mode 100644 index 000000000..1cbec97de --- /dev/null +++ b/Engine/source/T3D/components/game/controlObjectComponent.h @@ -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); +}; \ No newline at end of file diff --git a/Engine/source/T3D/components/game/followPathComponent.cpp b/Engine/source/T3D/components/game/followPathComponent.cpp new file mode 100644 index 000000000..3d81d5d2c --- /dev/null +++ b/Engine/source/T3D/components/game/followPathComponent.cpp @@ -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; + +}*/ \ No newline at end of file diff --git a/Engine/source/T3D/components/game/followPathComponent.h b/Engine/source/T3D/components/game/followPathComponent.h new file mode 100644 index 000000000..f0ac812dc --- /dev/null +++ b/Engine/source/T3D/components/game/followPathComponent.h @@ -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)); +}; \ No newline at end of file diff --git a/Engine/source/T3D/components/game/interactComponent.cpp b/Engine/source/T3D/components/game/interactComponent.cpp new file mode 100644 index 000000000..8f02ea413 --- /dev/null +++ b/Engine/source/T3D/components/game/interactComponent.cpp @@ -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(rinfo.object); + + if (hitEntity) + { + //call on that badboy! + InteractableComponent* iComp = hitEntity->getComponent(); + + 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(gServerContainer.containerSearchNextObject()); + + while (e != nullptr) + { + InteractableComponent* iComp = e->getComponent(); + + 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(gServerContainer.containerSearchNextObject()); //loop 'round + } + + if (bestFitEntity) + { + //call on that badboy! + InteractableComponent* iComp = bestFitEntity->getComponent(); + + iComp->interact(this); + } + } +} \ No newline at end of file diff --git a/Engine/source/T3D/components/game/interactComponent.h b/Engine/source/T3D/components/game/interactComponent.h new file mode 100644 index 000000000..33b82832b --- /dev/null +++ b/Engine/source/T3D/components/game/interactComponent.h @@ -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(); +}; \ No newline at end of file diff --git a/Engine/source/T3D/components/game/interactableComponent.cpp b/Engine/source/T3D/components/game/interactableComponent.cpp new file mode 100644 index 000000000..bff08b722 --- /dev/null +++ b/Engine/source/T3D/components/game/interactableComponent.cpp @@ -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); + } +} \ No newline at end of file diff --git a/Engine/source/T3D/components/game/interactableComponent.h b/Engine/source/T3D/components/game/interactableComponent.h new file mode 100644 index 000000000..20fce4310 --- /dev/null +++ b/Engine/source/T3D/components/game/interactableComponent.h @@ -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; } +}; \ No newline at end of file diff --git a/Engine/source/T3D/components/game/triggerComponent.cpp b/Engine/source/T3D/components/game/triggerComponent.cpp index 873c1307d..17a96a882 100644 --- a/Engine/source/T3D/components/game/triggerComponent.cpp +++ b/Engine/source/T3D/components/game/triggerComponent.cpp @@ -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(); + CollisionComponent *colComp = mOwner->getComponent(); - if(colInt) + if(colComp) { - colInt->onCollisionSignal.notify(this, &TriggerComponent::potentialEnterObject); + colComp->onCollisionSignal.notify(this, &TriggerComponent::potentialEnterObject); } } void TriggerComponent::onComponentRemove() { - CollisionInterface *colInt = mOwner->getComponent(); + CollisionComponent *colComp = mOwner->getComponent(); - 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(); + CollisionComponent *colComp = mOwner->getComponent(); - 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(); + CollisionComponent *colComp = mOwner->getComponent(); - 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(); - if (cI) + CollisionComponent *colComp = enterEntity->getComponent(); + 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(); + CollisionComponent *myColComp = mOwner->getComponent(); //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); -} \ No newline at end of file +} diff --git a/Engine/source/T3D/components/game/triggerComponent.h b/Engine/source/T3D/components/game/triggerComponent.h index bac45b62a..4826044b0 100644 --- a/Engine/source/T3D/components/game/triggerComponent.h +++ b/Engine/source/T3D/components/game/triggerComponent.h @@ -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 ////////////////////////////////////////////////////////////////////////// diff --git a/Engine/source/T3D/components/physics/physicsBehavior.cpp b/Engine/source/T3D/components/physics/physicsComponent.cpp similarity index 81% rename from Engine/source/T3D/components/physics/physicsBehavior.cpp rename to Engine/source/T3D/components/physics/physicsComponent.cpp index f281192c0..c9ba26519 100644 --- a/Engine/source/T3D/components/physics/physicsBehavior.cpp +++ b/Engine/source/T3D/components/physics/physicsComponent.cpp @@ -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(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(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" diff --git a/Engine/source/T3D/components/physics/physicsBehavior.h b/Engine/source/T3D/components/physics/physicsComponent.h similarity index 84% rename from Engine/source/T3D/components/physics/physicsBehavior.h rename to Engine/source/T3D/components/physics/physicsComponent.h index bc09f3108..12cad9ea4 100644 --- a/Engine/source/T3D/components/physics/physicsBehavior.h +++ b/Engine/source/T3D/components/physics/physicsComponent.h @@ -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_ diff --git a/Engine/source/T3D/components/physics/playerControllerComponent.cpp b/Engine/source/T3D/components/physics/playerControllerComponent.cpp index d321f7904..1738dc521 100644 --- a/Engine/source/T3D/components/physics/playerControllerComponent.cpp +++ b/Engine/source/T3D/components/physics/playerControllerComponent.cpp @@ -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(); + 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(comp); - if (collisionInterface) + CollisionComponent *collisionComp = dynamic_cast(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(comp); - if (collisionInterface) + CollisionComponent *collisionComp = dynamic_cast(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(); - if (colInterface) + CollisionComponent* colComp = mOwner->getComponent(); + 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) diff --git a/Engine/source/T3D/components/physics/playerControllerComponent.h b/Engine/source/T3D/components/physics/playerControllerComponent.h index 49d4c175c..1e2baaca3 100644 --- a/Engine/source/T3D/components/physics/playerControllerComponent.h +++ b/Engine/source/T3D/components/physics/playerControllerComponent.h @@ -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(); } diff --git a/Engine/source/T3D/components/physics/rigidBodyComponent.cpp b/Engine/source/T3D/components/physics/rigidBodyComponent.cpp index 200e0d65e..ef4f76864 100644 --- a/Engine/source/T3D/components/physics/rigidBodyComponent.cpp +++ b/Engine/source/T3D/components/physics/rigidBodyComponent.cpp @@ -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) diff --git a/Engine/source/T3D/components/physics/rigidBodyComponent.h b/Engine/source/T3D/components/physics/rigidBodyComponent.h index 4bbe1f927..746f3b944 100644 --- a/Engine/source/T3D/components/physics/rigidBodyComponent.h +++ b/Engine/source/T3D/components/physics/rigidBodyComponent.h @@ -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 { diff --git a/Engine/source/T3D/components/physics/simplePhysicsComponent.cpp b/Engine/source/T3D/components/physics/simplePhysicsComponent.cpp new file mode 100644 index 000000000..c5236586a --- /dev/null +++ b/Engine/source/T3D/components/physics/simplePhysicsComponent.cpp @@ -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(); + + 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(collision->object)) + { + if(CollisionComponent *collidingEntityColComp = colEnt->getComponent()) + { + if(!collidingEntityColComp->doesBlockColliding()) + { + continue; + } + } + } + + if (cp->faceDot > collision->faceDot) + collision = cp; + } + + //check the last/first one just incase + if(Entity* colEnt = dynamic_cast(collision->object)) + { + if(CollisionComponent *collidingEntityColComp = colEnt->getComponent()) + { + 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; + } + } +} \ No newline at end of file diff --git a/Engine/source/T3D/components/physics/simplePhysicsComponent.h b/Engine/source/T3D/components/physics/simplePhysicsComponent.h new file mode 100644 index 000000000..794fc2638 --- /dev/null +++ b/Engine/source/T3D/components/physics/simplePhysicsComponent.h @@ -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_ diff --git a/Engine/source/T3D/components/render/meshComponent.cpp b/Engine/source/T3D/components/render/meshComponent.cpp index ad7ce0a5c..f4123c708 100644 --- a/Engine/source/T3D/components/render/meshComponent.cpp +++ b/Engine/source/T3D/components/render/meshComponent.cpp @@ -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 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 &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; } -} \ No newline at end of file +} diff --git a/Engine/source/T3D/components/render/meshComponent.h b/Engine/source/T3D/components/render/meshComponent.h index 587038d63..8c23b238c 100644 --- a/Engine/source/T3D/components/render/meshComponent.h +++ b/Engine/source/T3D/components/render/meshComponent.h @@ -101,18 +101,6 @@ protected: Vector mChangingMaterials; Vector mMaterials; -public: - enum RenderMode - { - Individual = 0, - DynamicBatch, - StaticBatch, - Instanced - }; - -protected: - RenderMode mRenderMode; - public: StringTableEntry mMeshAssetId; AssetPtr 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(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 diff --git a/Engine/source/T3D/entity.cpp b/Engine/source/T3D/entity.cpp index bb8f7becf..8a3edd7e5 100644 --- a/Engine/source/T3D/entity.cpp +++ b/Engine/source/T3D/entity.cpp @@ -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 updaters = getComponents(); - for (Vector::iterator it = updaters.begin(); it != updaters.end(); it++) + Vector colliders = getComponents(); + for (Vector::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; } diff --git a/Engine/source/T3D/entity.h b/Engine/source/T3D/entity.h index b7e325ce5..e25145957 100644 --- a/Engine/source/T3D/entity.h +++ b/Engine/source/T3D/entity.h @@ -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 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 diff --git a/Engine/source/T3D/physics/bullet/btBody.h b/Engine/source/T3D/physics/bullet/btBody.h index 84b7dddc0..e8f37649b 100644 --- a/Engine/source/T3D/physics/bullet/btBody.h +++ b/Engine/source/T3D/physics/bullet/btBody.h @@ -116,6 +116,8 @@ public: virtual void findContact(SceneObject **contactObject, VectorF *contactNormal, Vector *outOverlapObjects) const; virtual void moveKinematicTo(const MatrixF &xfm); + virtual bool isValid() { return mActor != nullptr; } + }; #endif // _T3D_PHYSICS_BTBODY_H_ diff --git a/Engine/source/T3D/physics/physicsBody.h b/Engine/source/T3D/physics/physicsBody.h index 28cf95d45..39fd86ef8 100644 --- a/Engine/source/T3D/physics/physicsBody.h +++ b/Engine/source/T3D/physics/physicsBody.h @@ -128,6 +128,8 @@ public: /// virtual void moveKinematicTo(const MatrixF &xfm) = 0; + virtual bool isValid() = 0; + }; diff --git a/Engine/source/T3D/systems/render/meshRenderSystem.cpp b/Engine/source/T3D/systems/render/meshRenderSystem.cpp index 6faf2a74c..e828c29a1 100644 --- a/Engine/source/T3D/systems/render/meshRenderSystem.cpp +++ b/Engine/source/T3D/systems/render/meshRenderSystem.cpp @@ -6,9 +6,6 @@ #include "materials/materialManager.h" #include "materials/baseMatInstance.h" -Vector MeshRenderSystem::mBufferMaterials(0); -Vector 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(); - - // 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 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 geomPoints = MeshRenderSystemInterface::all[i]->mGeometry.mPoints; - //Vector geomNormals = MeshRenderSystemInterface::all[i]->mGeometry.mNormals; - //Vector 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; -} +} \ No newline at end of file diff --git a/Engine/source/T3D/systems/render/meshRenderSystem.h b/Engine/source/T3D/systems/render/meshRenderSystem.h index 4bc2269db..978f9c0e7 100644 --- a/Engine/source/T3D/systems/render/meshRenderSystem.h +++ b/Engine/source/T3D/systems/render/meshRenderSystem.h @@ -38,12 +38,7 @@ public: Vector mChangingMaterials; Vector 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 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 mBufferMaterials; - - struct BufferSet - { - U32 surfaceMaterialId; - - U32 vertCount; - U32 primCount; - - Point3F center; - - struct Buffers - { - U32 vertStart; - U32 primStart; - U32 vertCount; - U32 primCount; - - Vector vertData; - Vector primData; - - GFXVertexBufferHandle< GFXVertexPNTT > vertexBuffer; - GFXPrimitiveBufferHandle primitiveBuffer; - - Buffers() - { - vertStart = 0; - primStart = 0; - vertCount = 0; - primCount = 0; - - vertexBuffer = NULL; - primitiveBuffer = NULL; - } - }; - - Vector buffers; - - BufferSet() - { - Buffers newBuffer; - buffers.push_back(newBuffer); - - surfaceMaterialId = 0; - - vertCount = 0; - primCount = 0; - - center = Point3F::Zero; - } - }; - - static Vector 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 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 getNodeTransforms() - { - Vector bob; - return bob; - } - - virtual void setNodeTransforms(Vector 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); }; \ No newline at end of file diff --git a/Templates/BaseGame/game/core/components/Core_Components.module b/Templates/BaseGame/game/core/components/Core_Components.module index 2c98b8091..473e1303c 100644 --- a/Templates/BaseGame/game/core/components/Core_Components.module +++ b/Templates/BaseGame/game/core/components/Core_Components.module @@ -11,4 +11,9 @@ canSaveDynamicFields="true" Extension="asset.taml" Recurse="true" /> + \ No newline at end of file diff --git a/Templates/BaseGame/game/core/components/components/game/controlObject.asset.taml b/Templates/BaseGame/game/core/components/components/game/controlObject.asset.taml index 7efa2d3a2..bb21efb67 100644 --- a/Templates/BaseGame/game/core/components/components/game/controlObject.asset.taml +++ b/Templates/BaseGame/game/core/components/components/game/controlObject.asset.taml @@ -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." /> diff --git a/Templates/BaseGame/game/core/components/components/game/controlObject.cs b/Templates/BaseGame/game/core/components/components/game/controlObject.cs deleted file mode 100644 index 7f477ecca..000000000 --- a/Templates/BaseGame/game/core/components/components/game/controlObject.cs +++ /dev/null @@ -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); -} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/componentEditor/gui/TypeMaskFieldGui.gui b/Templates/BaseGame/game/tools/componentEditor/gui/TypeMaskFieldGui.gui new file mode 100644 index 000000000..a18277419 --- /dev/null +++ b/Templates/BaseGame/game/tools/componentEditor/gui/TypeMaskFieldGui.gui @@ -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 --- diff --git a/Templates/BaseGame/game/tools/componentEditor/interface/materialFieldType.cs b/Templates/BaseGame/game/tools/componentEditor/interface/materialFieldType.cs new file mode 100644 index 000000000..65c338dd7 --- /dev/null +++ b/Templates/BaseGame/game/tools/componentEditor/interface/materialFieldType.cs @@ -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) +{ + +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/componentEditor/interface/stateMachineField.cs b/Templates/BaseGame/game/tools/componentEditor/interface/stateMachineField.cs new file mode 100644 index 000000000..b6b31380f --- /dev/null +++ b/Templates/BaseGame/game/tools/componentEditor/interface/stateMachineField.cs @@ -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) +{ + +}*/ \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/componentEditor/interface/typeMaskFieldType.cs b/Templates/BaseGame/game/tools/componentEditor/interface/typeMaskFieldType.cs new file mode 100644 index 000000000..83cb55a6f --- /dev/null +++ b/Templates/BaseGame/game/tools/componentEditor/interface/typeMaskFieldType.cs @@ -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); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/componentEditor/main.cs b/Templates/BaseGame/game/tools/componentEditor/main.cs index 56d74830a..643e74c0f 100644 --- a/Templates/BaseGame/game/tools/componentEditor/main.cs +++ b/Templates/BaseGame/game/tools/componentEditor/main.cs @@ -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"); \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/componentEditor/scripts/componentEditor.ed.cs b/Templates/BaseGame/game/tools/componentEditor/scripts/componentEditor.ed.cs index a2a62b08a..94013ed36 100644 --- a/Templates/BaseGame/game/tools/componentEditor/scripts/componentEditor.ed.cs +++ b/Templates/BaseGame/game/tools/componentEditor/scripts/componentEditor.ed.cs @@ -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;