123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "platform/platform.h"
- #include "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 "app/game.h"
- #include "T3D/gameBase/gameConnection.h"
- #include "T3D/fx/cameraFXMgr.h"
- #include "console/engineAPI.h"
- #include "math/mTransform.h"
- #include "T3D/pathCamera.h"
- //----------------------------------------------------------------------------
- IMPLEMENT_CO_DATABLOCK_V1(PathCameraData);
- ConsoleDocClass( PathCameraData,
- "@brief General interface to control a PathCamera object from the script level.\n"
- "@see PathCamera\n"
- "@tsexample\n"
- "datablock PathCameraData(LoopingCam)\n"
- " {\n"
- " mode = \"\";\n"
- " };\n"
- "@endtsexample\n"
- "@ingroup PathCameras\n"
- "@ingroup Datablocks\n"
- );
- void PathCameraData::consoleInit()
- {
- }
- void PathCameraData::initPersistFields()
- {
- docsURL;
- Parent::initPersistFields();
- }
- void PathCameraData::packData(BitStream* stream)
- {
- Parent::packData(stream);
- }
- void PathCameraData::unpackData(BitStream* stream)
- {
- Parent::unpackData(stream);
- }
- //----------------------------------------------------------------------------
- IMPLEMENT_CO_NETOBJECT_V1(PathCamera);
- ConsoleDocClass( PathCamera,
- "@brief A camera that moves along a path. The camera can then be made to travel along this path forwards or backwards.\n\n"
- "A camera's path is made up of knots, which define a position, rotation and speed for the camera. Traversal from one knot to "
- "another may be either linear or using a Catmull-Rom spline. If the knot is part of a spline, then it may be a normal knot "
- "or defined as a kink. Kinked knots are a hard transition on the spline rather than a smooth one. A knot may also be defined "
- "as a position only. In this case the knot is treated as a normal knot but is ignored when determining how to smoothly rotate "
- "the camera while it is travelling along the path (the algorithm moves on to the next knot in the path for determining rotation).\n\n"
- "The datablock field for a PathCamera is a previously created PathCameraData, which acts as the interface between the script and the engine "
- "for this PathCamera object.\n\n"
- "@see PathCameraData\n"
- "@tsexample\n"
- "%newPathCamera = new PathCamera()\n"
- "{\n"
- " dataBlock = LoopingCam;\n"
- " position = \"0 0 300 1 0 0 0\";\n"
- "};\n"
- "@endtsexample\n"
- "@ingroup PathCameras\n"
- );
- IMPLEMENT_CALLBACK( PathCamera, 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");
- PathCamera::PathCamera()
- {
- mNetFlags.clear(Ghostable);
- mTypeMask |= CameraObjectType;
- delta.time = 0;
- delta.timeVec = 0;
- mDataBlock = 0;
- mState = Forward;
- mNodeBase = 0;
- mNodeCount = 0;
- mPosition = 0;
- mTarget = 0;
- mTargetSet = false;
- MatrixF mat(1);
- mat.setPosition(Point3F(0,0,700));
- Parent::setTransform(mat);
- }
- PathCamera::~PathCamera()
- {
- }
- //----------------------------------------------------------------------------
- bool PathCamera::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();
- if (mShapeInstance)
- {
- mNetFlags.set(Ghostable);
- setScopeAlways();
- }
- addToScene();
- return true;
- }
- void PathCamera::onRemove()
- {
- removeFromScene();
- Parent::onRemove();
- }
- bool PathCamera::onNewDataBlock( GameBaseData *dptr, bool reload )
- {
- mDataBlock = dynamic_cast< PathCameraData* >( dptr );
- if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
- return false;
- scriptOnNewDataBlock();
- return true;
- }
- //----------------------------------------------------------------------------
- void PathCamera::onEditorEnable()
- {
- mNetFlags.set(Ghostable);
- }
- void PathCamera::onEditorDisable()
- {
- mNetFlags.clear(Ghostable);
- }
- //----------------------------------------------------------------------------
- void PathCamera::initPersistFields()
- {
- docsURL;
- Parent::initPersistFields();
- }
- void PathCamera::consoleInit()
- {
- }
- //----------------------------------------------------------------------------
- void PathCamera::processTick(const Move* move)
- {
- // client and server
- Parent::processTick(move);
- // Move to new time
- advancePosition(TickMs);
- // Set new position
- MatrixF mat;
- interpolateMat(mPosition,&mat);
- Parent::setTransform(mat);
- updateContainer();
- }
- void PathCamera::interpolateTick(F32 dt)
- {
- Parent::interpolateTick(dt);
- MatrixF mat;
- interpolateMat(delta.time + (delta.timeVec * dt),&mat);
- Parent::setRenderTransform(mat);
- }
- void PathCamera::interpolateMat(F32 pos,MatrixF* mat)
- {
- CameraSpline::Knot knot;
- mSpline.value(pos - mNodeBase,&knot);
- knot.mRotation.setMatrix(mat);
- mat->setPosition(knot.mPosition);
- }
- void PathCamera::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 PathCamera::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 PathCamera::setPosition(F32 pos)
- {
- mPosition = mClampF(pos, (F32)mNodeBase, (F32)(mNodeBase + mNodeCount - 1));
- MatrixF mat;
- interpolateMat(mPosition,&mat);
- Parent::setTransform(mat);
- setMaskBits(PositionMask);
- }
- void PathCamera::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 PathCamera::setState(State s)
- {
- mState = s;
- setMaskBits(StateMask);
- }
- //-----------------------------------------------------------------------------
- void PathCamera::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 PathCamera::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 PathCamera::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 PathCamera::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 PathCamera::onNode(S32 node)
- {
- if (!isGhost())
- onNode_callback(node);
-
- }
- U32 PathCamera::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 PathCamera::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(PathCamera, 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(PathCamera, 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(PathCamera, 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(PathCamera::Forward);
- else
- if (!dStricmp(newState,"backward"))
- object->setState(PathCamera::Backward);
- else
- object->setState(PathCamera::Stop);
- }
- DefineEngineMethod(PathCamera, 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(PathCamera, pushBack, void, (TransformF transform, F32 speed, const char* type, const char* path),
- (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(PathCamera, 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(PathCamera, 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();
- }
|