diff --git a/Engine/source/T3D/gameBase/gameBase.cpp b/Engine/source/T3D/gameBase/gameBase.cpp index 17c050309..3d0e8bd1e 100644 --- a/Engine/source/T3D/gameBase/gameBase.cpp +++ b/Engine/source/T3D/gameBase/gameBase.cpp @@ -404,6 +404,12 @@ void GameBase::processTick(const Move * move) #endif } +void GameBase::interpolateTick(F32 dt) +{ + // PATHSHAPE + updateRenderChangesByParent(); + // PATHSHAPE END +} //---------------------------------------------------------------------------- F32 GameBase::getUpdatePriority(CameraScopeQuery *camInfo, U32 updateMask, S32 updateSkips) @@ -471,14 +477,15 @@ F32 GameBase::getUpdatePriority(CameraScopeQuery *camInfo, U32 updateMask, S32 u // Weight by updateSkips F32 wSkips = updateSkips * 0.5; - // Calculate final priority, should total to about 1.0f + // Calculate final priority, should total to about 1.0f (plus children) // return wFov * sUpFov + wDistance * sUpDistance + wVelocity * sUpVelocity + wSkips * sUpSkips + - wInterest * sUpInterest; + wInterest * sUpInterest + + getNumChildren(); } //---------------------------------------------------------------------------- @@ -757,3 +764,44 @@ DefineEngineMethod( GameBase, applyRadialImpulse, void, ( Point3F origin, F32 ra { object->applyRadialImpulse( origin, radius, magnitude ); } + +// PATHSHAPE +// Console Methods for attach children. can't put them in sceneobject because // +// we want the processafter functions//////////////////////////////////////////// + +DefineEngineMethod(GameBase, attachChild, bool, (GameBase* _subObject), (nullAsType()), "(SceneObject subObject)" + "attach an object to this one, preserving its present transform.") +{ + if (_subObject != nullptr) + { + if (_subObject->getParent() != object){ + Con::errorf("Object is (%d)", _subObject->getId()); + _subObject->clearProcessAfter(); + _subObject->processAfter(object); + return object->attachChild(_subObject); + } + else + return false; + } + else + { + Con::errorf("Couldn't addObject()!"); + return false; + } +} + + +DefineEngineMethod(GameBase, detachChild, bool, (GameBase* _subObject), (nullAsType()), "(SceneObject subObject)" + "attach an object to this one, preserving its present transform.") +{ + if (_subObject != nullptr) + { + _subObject->clearProcessAfter(); + return _subObject->attachToParent(NULL); + } + else + { + return false; + } +}//end +// PATHSHAPE END diff --git a/Engine/source/T3D/gameBase/gameBase.h b/Engine/source/T3D/gameBase/gameBase.h index 2571c1426..56f5e2929 100644 --- a/Engine/source/T3D/gameBase/gameBase.h +++ b/Engine/source/T3D/gameBase/gameBase.h @@ -356,7 +356,7 @@ public: /// @name Network /// @see NetObject, NetConnection /// @{ - + void interpolateTick(F32 dt); F32 getUpdatePriority( CameraScopeQuery *focusObject, U32 updateMask, S32 updateSkips ); U32 packUpdate ( NetConnection *conn, U32 mask, BitStream *stream ); void unpackUpdate( NetConnection *conn, BitStream *stream ); diff --git a/Engine/source/T3D/gameFunctions.cpp b/Engine/source/T3D/gameFunctions.cpp index db2ac5a27..c546bf83b 100644 --- a/Engine/source/T3D/gameFunctions.cpp +++ b/Engine/source/T3D/gameFunctions.cpp @@ -665,6 +665,9 @@ static void RegisterGameFunctions() Con::setIntVariable("$TypeMasks::DebrisObjectType", DebrisObjectType); Con::setIntVariable("$TypeMasks::PhysicalZoneObjectType", PhysicalZoneObjectType); Con::setIntVariable("$TypeMasks::LightObjectType", LightObjectType); +// PATHSHAPE + Con::setIntVariable("$TypeMasks::PathShapeObjectType", PathShapeObjectType); +// PATHSHAPE END Con::addVariable("Ease::InOut", TypeS32, &gEaseInOut, "InOut ease for curve movement.\n" diff --git a/Engine/source/T3D/item.cpp b/Engine/source/T3D/item.cpp index f31e2e8e0..c7ae784fc 100644 --- a/Engine/source/T3D/item.cpp +++ b/Engine/source/T3D/item.cpp @@ -618,6 +618,9 @@ void Item::interpolateTick(F32 dt) mat.setColumn(3,pos); setRenderTransform(mat); mDelta.dt = dt; +// PATHSHAPE + updateRenderChangesByParent(); +// PATHSHAPE END } diff --git a/Engine/source/T3D/objectTypes.h b/Engine/source/T3D/objectTypes.h index 8f1c4d69e..063bec7e5 100644 --- a/Engine/source/T3D/objectTypes.h +++ b/Engine/source/T3D/objectTypes.h @@ -162,6 +162,12 @@ enum SceneObjectTypes #if defined(AFX_CAP_AFXMODEL_TYPE) afxModelObjectType = BIT(26) #endif + + // PATHSHAPE + PathShapeObjectType = BIT( 28 ), + // PATHSHAPE END + + /// @} }; enum SceneObjectTypeMasks : U32 diff --git a/Engine/source/T3D/pathShape.cpp b/Engine/source/T3D/pathShape.cpp new file mode 100644 index 000000000..fbc22967f --- /dev/null +++ b/Engine/source/T3D/pathShape.cpp @@ -0,0 +1,588 @@ +//----------------------------------------------------------------------------- +// Torque Game Engine +// Copyright (C) GarageGames.com, Inc. +// @author Stefan "Beffy" Moises +// this is a modified version of PathCamera that allows to move shapes along paths +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/mMath.h" +#include "math/mathIO.h" +#include "console/simBase.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "core/stream/bitStream.h" +#include "core/dnet.h" +#include "scene/pathManager.h" +#include "T3D/gameFunctions.h" +#include "T3D/gameBase/gameConnection.h" +#include "gui/worldEditor/editor.h" +#include "console/engineAPI.h" +#include "math/mTransform.h" + +#include "T3D/pathShape.h" + + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_DATABLOCK_V1(PathShapeData); + +void PathShapeData::consoleInit() +{ +} + + +bool PathShapeData::preload(bool server, String &errorStr) +{ + + if(!Parent::preload(server, errorStr)) + return false; + + return true; +} + + +void PathShapeData::initPersistFields() +{ + Parent::initPersistFields(); +} + +void PathShapeData::packData(BitStream* stream) +{ + Parent::packData(stream); +} + +void PathShapeData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); +} + + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_NETOBJECT_V1(PathShape); + +PathShape::PathShape() +{ + mNetFlags.set(Ghostable|ScopeAlways); + mTypeMask |= PathShapeObjectType | StaticShapeObjectType; + delta.time = 0; + delta.timeVec = 0; + mDataBlock = NULL; + mState = Forward; + mNodeBase = 0; + mNodeCount = 0; + mPosition = 0; + mTarget = 0; + mTargetSet = false; + + MatrixF mat(1); + mat.setPosition(Point3F(0,0,700)); + Parent::setTransform(mat); + + mLastXform = mat; + for (U32 i = 0; i < 4; i++) + { + mControl[i] = StringTable->insert(""); + } +} + +PathShape::~PathShape() +{ +} + + +//---------------------------------------------------------------------------- + +bool PathShape::onAdd() +{ + if(!Parent::onAdd() && !mDataBlock) + return false; + + mTypeMask |= PathShapeObjectType | StaticShapeObjectType; + // 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; + } + + if (isServerObject()) scriptOnAdd(); + return true; + +} + +void PathShape::onRemove() +{ + scriptOnRemove(); + removeFromScene(); + + unmount(); + Parent::onRemove(); + + if (isGhost()) + for (S32 i = 0; i < MaxSoundThreads; i++) + stopAudio(i); +} + +bool PathShape::onNewDataBlock(GameBaseData* dptr, bool reload) +{ + mDataBlock = dynamic_cast(dptr); + if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload)) + return false; + + scriptOnNewDataBlock(); + return true; +} + +PathShapeData::PathShapeData() +{ + +} + +//---------------------------------------------------------------------------- + +void PathShape::initPersistFields() +{ + + addField( "Path", TYPEID< SimObjectRef >(), Offset( mSimPath, PathShape ), + "@brief Name of a Path to follow." ); + + addField("Controler", TypeString, Offset(mControl, PathShape), 4, "controlers"); + + Parent::initPersistFields(); + +} + +void PathShape::consoleInit() +{ +} + + +//---------------------------------------------------------------------------- + +void PathShape::processTick(const Move* move) +{ + // client and server + Parent::processTick(move); + + // Move to new time + advancePosition(TickMs); + + MatrixF mat; + interpolateMat(mPosition,&mat); + Parent::setTransform(mat); + + updateContainer(); +} + +void PathShape::interpolateTick(F32 dt) +{ + Parent::interpolateTick(dt); + MatrixF mat; + interpolateMat(delta.time + (delta.timeVec * dt),&mat); + Parent::setRenderTransform(mat); +} + +void PathShape::interpolateMat(F32 pos,MatrixF* mat) +{ + CameraSpline::Knot knot; + mSpline.value(pos - mNodeBase,&knot); + knot.mRotation.setMatrix(mat); + mat->setPosition(knot.mPosition); +} + +void PathShape::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 PathShape::setPosition(F32 pos) +{ + mPosition = mClampF(pos,mNodeBase,mNodeBase + mNodeCount - 1); + MatrixF mat; + interpolateMat(mPosition,&mat); + Parent::setTransform(mat); + setMaskBits(PositionMask); +} + +void PathShape::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 PathShape::setState(State s) +{ + mState = s; + setMaskBits(StateMask); +} + +S32 PathShape::getState() +{ + return mState; +} + + +//----------------------------------------------------------------------------- + +void PathShape::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 PathShape::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 = mNodeBase; + setMaskBits(PositionMask); + } +} + +void PathShape::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 > mNodeBase + (NodeWindow - 1)) { + mPosition = mNodeBase + (NodeWindow - 1); + setMaskBits(PositionMask); + } +} + +void PathShape::popFront() +{ + if (mNodeCount < 2) + return; + + // Remove the first node. Node base and position are unaffected. + mNodeCount--; + delete mSpline.remove(mSpline.getKnot(0)); +} + + +//---------------------------------------------------------------------------- + +void PathShape::onNode(S32 node) +{ + if (!isGhost()) + Con::executef(mDataBlock,"onNode",getIdString(), Con::getIntArg(node)); +} + + +//---------------------------------------------------------------------------- + + + +//---------------------------------------------------------------------------- + +U32 PathShape::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 PathShape::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; +} + + +//----------------------------------------------------------------------------- +// Console access methods +//----------------------------------------------------------------------------- +DefineEngineMethod(PathShape, setPosition, void, (F32 position), (0.0f), "Set the current position of the camera along the path.\n" + "@param position Position along the path, from 0.0 (path start) - 1.0 (path end), to place the camera.\n" + "@tsexample\n" + "// Set the camera on a position along its path from 0.0 - 1.0.\n" + "%position = \"0.35\";\n\n" + "// Force the pathCamera to its new position along the path.\n" + "%pathCamera.setPosition(%position);\n" + "@endtsexample\n") +{ + object->setPosition(position); +} + +DefineEngineMethod(PathShape, setTarget, void, (F32 position), (1.0f), "@brief Set the movement target for this camera along its path.\n\n" + "The camera will attempt to move along the path to the given target in the direction provided " + "by setState() (the default is forwards). Once the camera moves past this target it will come " + "to a stop, and the target state will be cleared.\n" + "@param position Target position, between 0.0 (path start) and 1.0 (path end), for the camera to move to along its path.\n" + "@tsexample\n" + "// Set the position target, between 0.0 (path start) and 1.0 (path end), for this camera to move to.\n" + "%position = \"0.50\";\n\n" + "// Inform the pathCamera of the new target position it will move to.\n" + "%pathCamera.setTarget(%position);\n" + "@endtsexample\n") +{ + object->setTarget(position); +} + +DefineEngineMethod(PathShape, setState, void, (const char* newState), ("forward"), "Set the movement state for this path camera.\n" + "@param newState New movement state type for this camera. Forward, Backward or Stop.\n" + "@tsexample\n" + "// Set the state type (forward, backward, stop).\n" + "// In this example, the camera will travel from the first node\n" + "// to the last node (or target if given with setTarget())\n" + "%state = \"forward\";\n\n" + "// Inform the pathCamera to change its movement state to the defined value.\n" + "%pathCamera.setState(%state);\n" + "@endtsexample\n") +{ + if (!dStricmp(newState, "forward")) + object->setState(PathShape::Forward); + else + if (!dStricmp(newState, "backward")) + object->setState(PathShape::Backward); + else + object->setState(PathShape::Stop); +} + +DefineEngineMethod(PathShape, reset, void, (F32 speed), (1.0f), "@brief Clear the camera's path and set the camera's current transform as the start of the new path.\n\n" + "What specifically occurs is a new knot is created from the camera's current transform. Then the current path " + "is cleared and the new knot is pushed onto the path. Any previous target is cleared and the camera's movement " + "state is set to Forward. The camera is now ready for a new path to be defined.\n" + "@param speed Speed for the camera to move along its path after being reset.\n" + "@tsexample\n" + "//Determine the new movement speed of this camera. If not set, the speed will default to 1.0.\n" + "%speed = \"0.50\";\n\n" + "// Inform the path camera to start a new path at" + "// the camera's current position, and set the new " + "// path's speed value.\n" + "%pathCamera.reset(%speed);\n" + "@endtsexample\n") +{ + object->reset(speed); +} + +static CameraSpline::Knot::Type resolveKnotType(const char *arg) +{ + if (dStricmp(arg, "Position Only") == 0) + return CameraSpline::Knot::POSITION_ONLY; + if (dStricmp(arg, "Kink") == 0) + return CameraSpline::Knot::KINK; + return CameraSpline::Knot::NORMAL; +} + +static CameraSpline::Knot::Path resolveKnotPath(const char *arg) +{ + if (!dStricmp(arg, "Linear")) + return CameraSpline::Knot::LINEAR; + return CameraSpline::Knot::SPLINE; +} + +DefineEngineMethod(PathShape, pushBack, void, (TransformF transform, F32 speed, const char* type, const char* path), + (TransformF::Identity, 1.0f, "Normal", "Linear"), + "@brief Adds a new knot to the back of a path camera's path.\n" + "@param transform Transform for the new knot. In the form of \"x y z ax ay az aa\" such as returned by SceneObject::getTransform()\n" + "@param speed Speed setting for this knot.\n" + "@param type Knot type (Normal, Position Only, Kink).\n" + "@param path %Path type (Linear, Spline).\n" + "@tsexample\n" + "// Transform vector for new knot. (Pos_X Pos_Y Pos_Z Rot_X Rot_Y Rot_Z Angle)\n" + "%transform = \"15.0 5.0 5.0 1.4 1.0 0.2 1.0\"\n\n" + "// Speed setting for knot.\n" + "%speed = \"1.0\"\n\n" + "// Knot type. (Normal, Position Only, Kink)\n" + "%type = \"Normal\";\n\n" + "// Path Type. (Linear, Spline)\n" + "%path = \"Linear\";\n\n" + "// Inform the path camera to add a new knot to the back of its path\n" + "%pathCamera.pushBack(%transform,%speed,%type,%path);\n" + "@endtsexample\n") +{ + QuatF rot(transform.getOrientation()); + + object->pushBack(new CameraSpline::Knot(transform.getPosition(), rot, speed, resolveKnotType(type), resolveKnotPath(path))); +} + +DefineEngineMethod(PathShape, pushFront, void, (TransformF transform, F32 speed, const char* type, const char* path), + (1.0f, "Normal", "Linear"), + "@brief Adds a new knot to the front of a path camera's path.\n" + "@param transform Transform for the new knot. In the form of \"x y z ax ay az aa\" such as returned by SceneObject::getTransform()\n" + "@param speed Speed setting for this knot.\n" + "@param type Knot type (Normal, Position Only, Kink).\n" + "@param path %Path type (Linear, Spline).\n" + "@tsexample\n" + "// Transform vector for new knot. (Pos_X,Pos_Y,Pos_Z,Rot_X,Rot_Y,Rot_Z,Angle)\n" + "%transform = \"15.0 5.0 5.0 1.4 1.0 0.2 1.0\"\n\n" + "// Speed setting for knot.\n" + "%speed = \"1.0\";\n\n" + "// Knot type. (Normal, Position Only, Kink)\n" + "%type = \"Normal\";\n\n" + "// Path Type. (Linear, Spline)\n" + "%path = \"Linear\";\n\n" + "// Inform the path camera to add a new knot to the front of its path\n" + "%pathCamera.pushFront(%transform, %speed, %type, %path);\n" + "@endtsexample\n") +{ + QuatF rot(transform.getOrientation()); + + object->pushFront(new CameraSpline::Knot(transform.getPosition(), rot, speed, resolveKnotType(type), resolveKnotPath(path))); +} + +DefineEngineMethod(PathShape, popFront, void, (), , "Removes the knot at the front of the camera's path.\n" + "@tsexample\n" + "// Remove the first knot in the camera's path.\n" + "%pathCamera.popFront();\n" + "@endtsexample\n") +{ + object->popFront(); +} + +DefineEngineMethod(PathShape, getState, S32, (), , "PathShape.getState()") +{ + return object->getState(); +} \ No newline at end of file diff --git a/Engine/source/T3D/pathShape.h b/Engine/source/T3D/pathShape.h new file mode 100644 index 000000000..351d2d19c --- /dev/null +++ b/Engine/source/T3D/pathShape.h @@ -0,0 +1,114 @@ +//----------------------------------------------------------------------------- +// Torque Game Engine +// Copyright (C) GarageGames.com, Inc. +//----------------------------------------------------------------------------- + +#ifndef _PATHSHAPE_H_ +#define _PATHSHAPE_H_ + +#ifndef _STATICSHAPE_H_ +#include "T3D/staticShape.h" +#endif + +#ifndef _CAMERASPLINE_H_ +#include "T3D/cameraSpline.h" +#endif + +#ifndef _SIMPATH_H_ +#include "scene/simPath.h" +#endif +//---------------------------------------------------------------------------- +struct PathShapeData: public StaticShapeData { + typedef StaticShapeData Parent; + + + PathShapeData(); + static void consoleInit(); + + DECLARE_CONOBJECT(PathShapeData); + bool preload(bool server, String &errorStr); + static void initPersistFields(); + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); +}; + + +//---------------------------------------------------------------------------- +class PathShape: public StaticShape +{ +public: + enum State { + Forward, + Backward, + Stop, + StateBits = 3 + }; + +private: + typedef StaticShape Parent; + + enum MaskBits { + WindowMask = Parent::NextFreeMask, + PositionMask = WindowMask << 1, + TargetMask = PositionMask << 1, + StateMask = TargetMask << 1, + NextFreeMask = StateMask << 1 + + }; + + struct StateDelta { + F32 time; + F32 timeVec; + }; + StateDelta delta; + + enum Constants { + NodeWindow = 20 // Maximum number of active nodes + }; + + PathShapeData* 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(PathShape); + + PathShape(); + ~PathShape(); + + StringTableEntry mControl[4]; + + static void initPersistFields(); + static void consoleInit(); + bool onAdd(); + void onRemove(); + bool onNewDataBlock(GameBaseData* dptr, bool reload); + void onNode(S32 node); + + void processTick(const Move*); + void interpolateTick(F32 dt); + + U32 packUpdate(NetConnection *, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *, BitStream *stream); + + 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); + S32 getState(); + SimObjectRef< SimPath::Path > mSimPath; +}; + + +#endif diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 40ea1a3c1..06ec7171a 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -120,7 +120,10 @@ static U32 sCollisionMoveMask = TerrainObjectType | PlayerObjectType | StaticShapeObjectType | VehicleObjectType | - PhysicalZoneObjectType; + PhysicalZoneObjectType | +// PATHSHAPE + PathShapeObjectType; +// PATHSHAPE END static U32 sServerCollisionContactMask = sCollisionMoveMask | ItemObjectType | @@ -2206,6 +2209,9 @@ void Player::processTick(const Move* move) } } } +// PATHSHAPE + if (!isGhost()) updateAttachment(); +// PATHSHAPE END } void Player::interpolateTick(F32 dt) @@ -2239,6 +2245,9 @@ void Player::interpolateTick(F32 dt) updateLookAnimation(dt); mDelta.dt = dt; +// PATHSHAPE + updateRenderChangesByParent(); +// PATHSHAPE END } void Player::advanceTime(F32 dt) @@ -3658,7 +3667,10 @@ void Player::updateDeathOffsets() //---------------------------------------------------------------------------- -static const U32 sPlayerConformMask = StaticShapeObjectType | StaticObjectType | TerrainObjectType; +// PATHSHAPE +static const U32 sPlayerConformMask = StaticShapeObjectType | StaticObjectType | + TerrainObjectType | PathShapeObjectType; +// PATHSHAPE END static void accel(F32& from, F32 to, F32 rate) { @@ -4780,6 +4792,45 @@ bool Player::step(Point3F *pos,F32 *maxStep,F32 time) return false; } +// PATHSHAPE +// This Function does a ray cast down to see if a pathshape object is below +// If so, it will attempt to attach to it. +void Player::updateAttachment(){ + Point3F rot, pos; + RayInfo rInfo; + MatrixF mat = getTransform(); + mat.getColumn(3, &pos); + if (gServerContainer.castRay(Point3F(pos.x, pos.y, pos.z + 0.1f), + Point3F(pos.x, pos.y, pos.z - 1.0f ), + PathShapeObjectType, &rInfo)) + { + if( rInfo.object->getTypeMask() & PathShapeObjectType) //Ramen + { + if (getParent() == NULL) + { // ONLY do this if we are not parented + //Con::printf("I'm on a pathshape object. Going to attempt attachment."); + ShapeBase* col = static_cast(rInfo.object); + if (!isGhost()) + { + this->attachToParent(col); + } + } + } + else + { + //Con::printf("object %i",rInfo.object->getId()); + } + } + else + { + if (getParent() !=NULL) + { + clearProcessAfter(); + attachToParent(NULL); + } + } +} +// PATHSHAPE END //---------------------------------------------------------------------------- inline Point3F createInterpPos(const Point3F& s, const Point3F& e, const F32 t, const F32 d) diff --git a/Engine/source/T3D/player.h b/Engine/source/T3D/player.h index 3e1cb5e50..fed759602 100644 --- a/Engine/source/T3D/player.h +++ b/Engine/source/T3D/player.h @@ -614,6 +614,9 @@ protected: void _handleCollision( const Collision &collision ); virtual bool updatePos(const F32 travelTime = TickSec); + // PATHSHAPE + void updateAttachment(); + // PATHSHAPE END ///Update head animation void updateLookAnimation(F32 dT = 0.f); diff --git a/Engine/source/T3D/staticShape.cpp b/Engine/source/T3D/staticShape.cpp index 9bc7369e1..7e926157c 100644 --- a/Engine/source/T3D/staticShape.cpp +++ b/Engine/source/T3D/staticShape.cpp @@ -246,6 +246,14 @@ void StaticShape::processTick(const Move* move) } } +void StaticShape::interpolateTick(F32 delta) +{ + Parent::interpolateTick(delta); + // PATHSHAPE + updateRenderChangesByParent(); + // PATHSHAPE END +} + void StaticShape::setTransform(const MatrixF& mat) { Parent::setTransform(mat); diff --git a/Engine/source/T3D/staticShape.h b/Engine/source/T3D/staticShape.h index 292260c72..7403b9e3a 100644 --- a/Engine/source/T3D/staticShape.h +++ b/Engine/source/T3D/staticShape.h @@ -84,6 +84,7 @@ public: bool onNewDataBlock(GameBaseData *dptr, bool reload); void processTick(const Move *move); + void interpolateTick(F32 delta); void setTransform(const MatrixF &mat); U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); diff --git a/Engine/source/scene/sceneObject.cpp b/Engine/source/scene/sceneObject.cpp index b2e93a8db..0238d6528 100644 --- a/Engine/source/scene/sceneObject.cpp +++ b/Engine/source/scene/sceneObject.cpp @@ -150,6 +150,13 @@ SceneObject::SceneObject() mSceneObjectLinks = NULL; mObjectFlags.set( RenderEnabledFlag | SelectionEnabledFlag ); +// PATHSHAPE + // init the scenegraph relationships to indicate no parent, no children, and no siblings + mGraph.parent = NULL; + mGraph.nextSibling = NULL; + mGraph.firstChild = NULL; + mGraph.objToParent.identity(); +// PATHSHAPE END mIsScopeAlways = false; mAccuTex = NULL; @@ -331,6 +338,9 @@ void SceneObject::onRemove() plUnlink(); Parent::onRemove(); +// PATHSHAPE + if ( getParent() != NULL) attachToParent( NULL); +// PATHSHAPE END } //----------------------------------------------------------------------------- @@ -403,6 +413,9 @@ void SceneObject::setTransform( const MatrixF& mat ) #endif PROFILE_SCOPE( SceneObject_setTransform ); +// PATHSHAPE + PerformUpdatesForChildren(mat); +// PATHSHAPE END // Update the transforms. @@ -877,6 +890,36 @@ U32 SceneObject::packUpdate( NetConnection* conn, U32 mask, BitStream* stream ) if ( stream->writeFlag( mask & FlagMask ) ) stream->writeRangedU32( (U32)mObjectFlags, 0, getObjectFlagMax() ); + // PATHSHAPE + //Begin attachment + retMask = 0; //retry mask + + if (stream->writeFlag(getParent() != NULL)) { + stream->writeAffineTransform(mGraph.objToParent); + } + if (stream->writeFlag(mask & MountedMask)) + { + // Check to see if we need to write an object ID + if (stream->writeFlag(mGraph.parent)) { + S32 t = conn->getGhostIndex(mGraph.parent); + // Check to see if we can actually ghost this... + if (t == -1) { + // Cant, try again later + retMask |= MountedMask; + stream->writeFlag(false); + } + else { + // Can, write it. + stream->writeFlag(true); + stream->writeRangedU32(U32(t), 0, NetConnection::MaxGhostCount); + stream->writeAffineTransform(mGraph.objToParent); + //Con::errorf("%d: sent mounted on %d", getId(), mGraph.parent->getId()); + } + } + } + // End of Attachment + // PATHSHAPE END + if ( mask & MountedMask ) { if ( mMount.object ) @@ -916,6 +959,44 @@ void SceneObject::unpackUpdate( NetConnection* conn, BitStream* stream ) if ( stream->readFlag() ) mObjectFlags = stream->readRangedU32( 0, getObjectFlagMax() ); + // PATHSHAPE + // begin of attachment + if (stream->readFlag()) + { + MatrixF m; + stream->readAffineTransform(&m); + mGraph.objToParent = m; + } + if (stream->readFlag()) + { + // Check to see if we need to read an object ID + if (stream->readFlag()) + { + // Check to see if we can actually ghost this... + if (stream->readFlag()) + { + GameBase *newParent = static_cast(conn->resolveGhost(stream->readRangedU32(0, NetConnection::MaxGhostCount))); + MatrixF m; + stream->readAffineTransform(&m); + + if (getParent() != newParent) + { + clearProcessAfter(); + processAfter(newParent); + } + + attachToParent(newParent, &m); + //Con::errorf("%d: got mounted on %d", getId(), mParentObject->getId()); + } + } + else + { + attachToParent(NULL); + } + } + // End of attachment + // PATHSHAPE END + // MountedMask if ( stream->readFlag() ) { @@ -1480,6 +1561,9 @@ DefineEngineMethod( SceneObject, setTransform, void, ( TransformF txfm ),, "Set the object's transform (orientation and position)." "@param txfm object transform to set" ) { +// PATHSHAPE + object->PerformUpdatesForChildren(txfm.getMatrix()); +// PATHSHAPE END if ( !txfm.hasRotation() ) object->setPosition( txfm.getPosition() ); else @@ -1553,3 +1637,356 @@ DefineEngineMethod(SceneObject, setForwardVector, void, (VectorF newForward, Vec { object->setForwardVector(newForward, upVector); } + +// PATHSHAPE +// Move RenderTransform by set amount +// no longer used + +void SceneObject::moveRender(const Point3F &delta) +{ + Point3F pos; + + const MatrixF& tmat = getRenderTransform(); + tmat.getColumn(3,&pos); + AngAxisF aa(tmat); + pos += delta; + + MatrixF mat; + aa.setMatrix(&mat); + mat.setColumn(3,pos); + setRenderTransform(mat); +} + +void SceneObject::PerformUpdatesForChildren(MatrixF mat){ + UpdateXformChange(mat); + for (U32 i=0; i < getNumChildren(); i++) { + SceneObject *o = getChild(i); + o->updateChildTransform(); //update the position of the child object + } +} + + + + + +// This function will move the players based on how much it's +// parent have moved +void SceneObject::updateChildTransform(){ + if (getParent() != NULL){ + MatrixF one; + MatrixF two; + MatrixF three; + MatrixF four; + MatrixF mat; + one= getTransform(); + two = getParent()->getTransform(); + one.affineInverse(); + four.mul(two,one); + mat.mul(getParent()->mLastXform,getTransform()); + setTransform(mat); + } +} + +// This function will move the rendered image based on how much it's +// parent have moved since the processtick. +// For some reason the player object must be updated via it's GetRenderTransform seen below, +// Other objects seem to require getTransform() only +void SceneObject::updateRenderChangesByParent(){ + if (getParent() != NULL){ + MatrixF renderXform = getParent()->getRenderTransform(); + MatrixF xform = getParent()->getTransform(); + xform.affineInverse(); + + MatrixF offset; + offset.mul(renderXform, xform); + + MatrixF mat; + + //add the "offset" caused by the parents change, and add it to it's own + // This is needed by objects that update their own render transform thru interpolate tick + // Mostly for stationary objects. + + if (getClassName() == "Player") + mat.mul(offset,getRenderTransform()); + else + mat.mul(offset,getTransform()); + setRenderTransform(mat); + } +} + + + + + +//Ramen - Move Transform by set amount +//written by Anthony Lovell +void SceneObject::move(F32 x, F32 y, F32 z) +{ + Point3F delta; + delta.x = x; + delta.y = y; + delta.z = z; + move(delta); +} +// move by a specified delta in root coordinate space +void SceneObject::move(const Point3F &delta) +{ + Point3F pos; + + const MatrixF& tmat = getTransform(); + tmat.getColumn(3,&pos); + AngAxisF aa(tmat); + + pos += delta; + + MatrixF mat; + aa.setMatrix(&mat); + mat.setColumn(3,pos); + setTransform(mat); +} + + + +//written by Anthony Lovell ---------------------------------------------------------- +U32 +SceneObject::getNumChildren() const +{ + U32 num = 0; + for (SceneObject *cur = mGraph.firstChild; cur; cur = cur->mGraph.nextSibling) + num++; + return num; +} +//written by Anthony Lovell ---------------------------------------------------------- +SceneObject * +SceneObject::getChild(U32 index) const +{ + SceneObject *cur = mGraph.firstChild; + for (U32 i = 0; + cur && i < index; + i++) + cur = cur->mGraph.nextSibling; + return cur; +} + + + + +void SceneObject::UpdateXformChange(const MatrixF &mat){ +// This function gets the difference between the Transform and current Render transform +// Used for Interpolation matching with the child objects who rely on this data. + + MatrixF oldxform = getTransform(); + + oldxform.affineInverse(); + mLastXform.mul(mat,oldxform); + +} + + +//---------------------------------------------------------- +bool +SceneObject::attachChildAt(SceneObject *subObject, MatrixF atThisOffset, S32 node) +{ + AssertFatal(subObject, "attaching a null subObject"); + AssertFatal(!isChildOf(subObject), "cyclic attachChild()"); + bool b = subObject->attachToParent(this, &atThisOffset, node); + if (!b) + return false; + + return true; +} + +//---------------------------------------------------------- +bool +SceneObject::attachChildAt(SceneObject *subObject, Point3F atThisPosition) +{ + AssertFatal(subObject, "attaching a null subObject"); + AssertFatal(!isChildOf(subObject), "cyclic attachChild()"); + bool b = subObject->attachToParent(this); + if (!b) + return false; + + subObject->mGraph.objToParent.setColumn(3, atThisPosition); +// calcTransformFromLocalTransform(); + + return true; +} + +//---------------------------------------------------------- +bool +SceneObject::attachChild(SceneObject *child) +{ + AssertFatal(child, "attaching a null subObject"); + AssertFatal(!isChildOf(child), "cyclic attachChild()"); + + return child->attachToParent(this); +} + + +//---------------------------------------------------------- +/// returns a count of children plus their children, recursively +U32 +SceneObject::getNumProgeny() const +{ + U32 num = 0; + for (SceneObject *cur = mGraph.firstChild; cur; cur = cur->mGraph.nextSibling) { + num += 1 + cur->getNumProgeny(); + } + return num; +} + +DefineEngineMethod(SceneObject, getNumChildren, S32, (),, "returns number of direct child objects") +{ + return object->getNumChildren(); +} + +DefineEngineMethod(SceneObject, getNumProgeny, S32, (),, "returns number of recursively-nested child objects") +{ + return object->getNumProgeny(); +} + +DefineEngineMethod(SceneObject, getChild, S32, (S32 _index), (0), "getChild(S32 index) -- returns child SceneObject at given index") +{ + SceneObject *s = object->getChild(_index); + return s ? s->getId() : 0; +} + +DefineEngineMethod(SceneObject, attachChildAt, bool, (SceneObject* _subObject, MatrixF _offset, S32 _node), (nullAsType(), MatrixF::Identity, 0), "(SceneObject subObject, MatrixF offset, S32 offset)" + "Mount object to this one with the specified offset expressed in our coordinate space.") +{ + if (_subObject != nullptr) + { + return object->attachChildAt(_subObject, _offset, _node); + } + else + { + Con::errorf("Couldn't addObject()!"); + return false; + } +} + +DefineEngineMethod(SceneObject, attachToParent, bool, (const char*_sceneObject), ,"attachToParent(SceneObject)" + "specify a null or non-null parent") +{ + SceneObject * t; + + if(Sim::findObject(_sceneObject, t)) + { + return object->attachToParent(t); + } + else + { + if ((!dStrcmp("0", _sceneObject))|| (!dStrcmp("", _sceneObject))) + return object->attachToParent(NULL); + else + { + Con::errorf("Couldn't setParent()!"); + return false; + } + } +} + +DefineEngineMethod(SceneObject, getParent, S32, (),, "returns ID of parent SceneObject") +{ + SceneObject *p = object->getParent(); + return p ? p->getId() : -1; +} + +DefineEngineMethod(SceneObject, attachChild, bool, (const char*_subObject),, "(SceneObject subObject)" + "attach an object to this one, preserving its present transform.") +{ + SceneObject * t; + MatrixF m; + if(Sim::findObject(_subObject, t)) + return object->attachChild(t); + + Con::errorf("Couldn't addObject()!"); + return false; +} + +bool SceneObject::isChildOf(SceneObject *so) +{ + SceneObject *p = mGraph.parent; + if (p) { + if (p == so) + return true; + else + return p->isChildOf(so); + } else + return false; +} + + + +bool SceneObject::attachToParent(SceneObject *newParent, MatrixF *atThisOffset/* = NULL */, S32 node ) +{ + SceneObject *oldParent = mGraph.parent; + + if (oldParent == newParent) + return true; + + // cycles in the scene hierarchy are forbidden! + // that is: a SceneObject cannot be a child of its progeny + if (newParent && newParent->isChildOf(this)) + return false; + + mGraph.parent = newParent; + + if (oldParent) { + + clearNotify(oldParent); + + // remove this SceneObject from the list of children of oldParent + SceneObject *cur = oldParent->mGraph.firstChild; + if (cur == this) { // if we are the first child, this is easy + oldParent->mGraph.firstChild = mGraph.nextSibling; + } else { + while (cur->mGraph.nextSibling != this) { + cur = cur->mGraph.nextSibling; + // ASSERT cur != NULL; + } + cur->mGraph.nextSibling = mGraph.nextSibling; + } + oldParent->onLostChild(this); + } + + if (newParent) { + + deleteNotify(newParent); // if we are deleted, inform our parent + + // add this SceneObject to the list of children of oldParent + mGraph.nextSibling = newParent->mGraph.firstChild; + newParent->mGraph.firstChild = this; + mGraph.parent = newParent; + + newParent->onNewChild(this); + + if (atThisOffset) + mGraph.objToParent = *atThisOffset; + } else { + mGraph.parent = NULL; + mGraph.nextSibling = NULL; + mGraph.objToParent = mObjToWorld; + } + + onLostParent(oldParent); + onNewParent(newParent); + + setMaskBits(MountedMask); + return true; +} + +DefineEngineMethod(SceneObject, detachChild, bool, (const char*_subObject),, "SceneObject subObject") +{ + SceneObject * t; + if(Sim::findObject(_subObject, t)) { + return t->attachToParent(NULL); + } else + return false; +} + +// subclasses can do something with these if they care to +void SceneObject::onNewParent(SceneObject *newParent) {} +void SceneObject::onLostParent(SceneObject *oldParent){} +void SceneObject::onNewChild(SceneObject *newKid){} +void SceneObject::onLostChild(SceneObject *lostKid){} diff --git a/Engine/source/scene/sceneObject.h b/Engine/source/scene/sceneObject.h index fb2944955..ef44982cf 100644 --- a/Engine/source/scene/sceneObject.h +++ b/Engine/source/scene/sceneObject.h @@ -287,6 +287,10 @@ class SceneObject : public NetObject, private SceneContainer::Link, public Proce /// @name Transform and Collision Members /// @{ + // PATHSHAPE + MatrixF mLastXform; + // PATHSHAPE END + /// Transform from object space to world space. MatrixF mObjToWorld; @@ -819,6 +823,27 @@ class SceneObject : public NetObject, private SceneContainer::Link, public Proce static bool _setAccuEnabled( void *object, const char *index, const char *data ); /// @} +// PATHSHAPE + /// @} + //Anthony's Original Code, still used so i keep it here + /// TGE uses the term "mount" in a quirky, staticky way relating to its limited use to have + /// riders and guns mounted on a vehicle (and similar) + /// I did not alter that code at all (yet) and did not want to keep its terminology for other reasons + /// I decided to support a hierarchy of scene objects and dubbed the operations + /// attaching and removing child SceneObjects + protected: + + // this member struct tracks the relationship to parent and children + // sceneObjects in a hierarchical scene graph whose root is the entire Scene + struct AttachInfo { + SceneObject* firstChild; ///< Objects mounted on this object + SimObjectPtr parent; ///< Object this object is mounted on. + SceneObject* nextSibling; ///< Link to next child object of this object's parent + MatrixF objToParent; ///< this obects transformation in the parent object's space + MatrixF RenderobjToParent; ///< this obects Render Offset transformation to the parent object + } mGraph; +// PATHSHAPE END + // Accumulation Texture // Note: This was placed in SceneObject to both ShapeBase and TSStatic could support it. @@ -841,6 +866,83 @@ class SceneObject : public NetObject, private SceneContainer::Link, public Proce // as opposed to something like a Player that has a built-in camera that requires // special calculations to determine the view transform. virtual bool isCamera() const { return false; } + // AFX CODE BLOCK (is-camera) >> +// PATHSHAPE +// Added for dynamic attaching + void UpdateXformChange(const MatrixF &mat); + /// this is useful for setting NULL parent (making SceneObject a root object) + virtual bool attachToParent(SceneObject *parent, MatrixF *atThisOffset = NULL, S32 node=0); + SceneObject *getParent() { return mGraph.parent; }; + + + /// attach a subobject, but do not alter the subObject's present absolute position or orientation + bool attachChild(SceneObject* subObject); + /// attach a subobject, at the specified offset expressed in our local coordinate space + bool attachChildAt(SceneObject* subObject, MatrixF atThisTransform, S32 node); + + /// attach a subobject, at the specified position expressed in our local coordinate space + bool attachChildAt(SceneObject* subObject, Point3F atThisPosition); + + /// how many child SceneObjects are (directly) attached to this one? + U32 getNumChildren() const; + + /// how many child objects does this SceneObject have when we count them recursively? + U32 getNumProgeny() const; + + /// returns the (direct) child SceneObject at the given index (0 <= index <= getNumChildren() - 1) + SceneObject *getChild(U32 index) const; + + /// is this SceneObject a child (directly or indirectly) of the given object? + bool isChildOf(SceneObject *); + + /// set position in parent SceneObject's coordinate space (or in world space if no parent) + //void setLocalPosition(const Point3F &pos); + + /// move the object in parent SceneObject's coordinate space (or in world space if no parent) + //void localMove(const Point3F &delta); + /// as localMove(const Point3F &delta), with different signature + //void localMove(F32 x, F32 y, F32 z); + + /// move the object in world space, without altering place in scene hierarchy + void move(const Point3F &delta); + + // Does checks for children objects and updates their positions + void PerformUpdatesForChildren(MatrixF mat); + + // Move the RenderTransform + void moveRender(const Point3F &delta); + //Calculate how much to adjust the render transform - Called by the child objects + void updateRenderChangesByParent(); + //Calculate how much to adjust the transform - Called by the parent object + void updateChildTransform(); + /// as move(const Point3F &delta), with different signature + void move(F32 x, F32 y, F32 z); + + /// returns the transform relative to parent SceneObject transform (or world transform if no parent) + //const MatrixF& getLocalTransform() const; + /// returns the position within parent SceneObject space (or world space if no parent) + //Point3F getLocalPosition() const; + + +// virtual void onParentScaleChanged(); +// virtual void onParentTransformChanged(); + + /// Sets the Object -> Parent transform. If no parent SceneObject, this is equivalent to + /// setTransform() + /// + /// @param mat New transform matrix + //virtual void setLocalTransform(const MatrixF & mat); + + + /// Called to let instance specific code happen + virtual void onLostParent(SceneObject *oldParent); + /// Called to let instance specific code happen + virtual void onNewParent(SceneObject *newParent); + /// notification that a direct child object has been attached + virtual void onNewChild(SceneObject *subObject); + /// notification that a direct child object has been detached + virtual void onLostChild(SceneObject *subObject); +// PATHSHAPE END }; #endif // _SCENEOBJECT_H_ diff --git a/Engine/source/scene/simPath.cpp b/Engine/source/scene/simPath.cpp index efa8f05f0..3aee6e1e6 100644 --- a/Engine/source/scene/simPath.cpp +++ b/Engine/source/scene/simPath.cpp @@ -34,6 +34,7 @@ #include "core/stream/bitStream.h" #include "renderInstance/renderPassManager.h" #include "console/engineAPI.h" +#include "T3D/pathShape.h" #include "T3D/Scene.h" @@ -155,6 +156,11 @@ Path::Path() { mPathIndex = NoPathIndex; mIsLooping = true; + mPathSpeed = 1.0f; + mDataBlock = NULL; + mSpawnCount = 1; + mMinDelay = 0; + mMaxDelay = 0; } Path::~Path() @@ -166,6 +172,13 @@ Path::~Path() void Path::initPersistFields() { addField("isLooping", TypeBool, Offset(mIsLooping, Path), "If this is true, the loop is closed, otherwise it is open.\n"); + addField("Speed", TypeF32, Offset(mPathSpeed, Path), "Speed.\n"); + addProtectedField("mPathShape", TYPEID< PathShapeData >(), Offset(mDataBlock, Path), + &setDataBlockProperty, &defaultProtectedGetFn, + "@brief Spawned PathShape.\n\n"); + addField("spawnCount", TypeS32, Offset(mSpawnCount, Path), "Spawn Count.\n"); + addField("minDelay", TypeS32, Offset(mMinDelay, Path), "Spawn Delay (min).\n"); + addField("maxDelay", TypeS32, Offset(mMaxDelay, Path), "Spawn Delay (max).\n"); Parent::initPersistFields(); // @@ -179,9 +192,14 @@ bool Path::onAdd() if(!Parent::onAdd()) return false; + onAdd_callback(getId()); return true; } +IMPLEMENT_CALLBACK(Path, onAdd, void, (SimObjectId ID), (ID), + "Called when this ScriptGroup is added to the system.\n" + "@param ID Unique object ID assigned when created (%this in script).\n" +); void Path::onRemove() { diff --git a/Engine/source/scene/simPath.h b/Engine/source/scene/simPath.h index 9c31ac507..8647d2ed6 100644 --- a/Engine/source/scene/simPath.h +++ b/Engine/source/scene/simPath.h @@ -36,17 +36,21 @@ #include "gfx/gfxPrimitiveBuffer.h" #endif -class BaseMatInstance; +#ifndef _STATICSHAPE_H_ +#include "T3D/staticShape.h" +#endif +class BaseMatInstance; +struct PathShapeData; namespace SimPath { //-------------------------------------------------------------------------- /// A path! -class Path : public SimGroup +class Path : public GameBase { - typedef SimGroup Parent; + typedef GameBase Parent; public: enum : U32 @@ -57,8 +61,12 @@ class Path : public SimGroup private: U32 mPathIndex; + F32 mPathSpeed; bool mIsLooping; - + PathShapeData* mDataBlock; + S32 mSpawnCount; + S32 mMinDelay; + S32 mMaxDelay; protected: bool onAdd(); void onRemove(); @@ -77,6 +85,7 @@ class Path : public SimGroup DECLARE_CONOBJECT(Path); static void initPersistFields(); + DECLARE_CALLBACK(void, onAdd, (SimObjectId ID)); }; //--------------------------------------------------------------------------