123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546 |
- //-----------------------------------------------------------------------------
- // 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 "scene/simPath.h"
- #include "gfx/gfxDevice.h"
- #include "gfx/gfxVertexBuffer.h"
- #include "gfx/gfxPrimitiveBuffer.h"
- #include "gfx/gfxTransformSaver.h"
- #include "console/consoleTypes.h"
- #include "scene/pathManager.h"
- #include "scene/sceneRenderState.h"
- #include "math/mathIO.h"
- #include "core/stream/bitStream.h"
- #include "renderInstance/renderPassManager.h"
- #include "console/engineAPI.h"
- #include "T3D/pathShape.h"
- #include "T3D/Scene.h"
- extern bool gEditingMission;
- //--------------------------------------------------------------------------
- //-------------------------------------- Console functions and cmp funcs
- //
- DefineEngineFunction(pathOnMissionLoadDone, void, (),,
- "@brief Load all Path information from the mission.\n\n"
- "This function is usually called from the loadMissionStage2() server-side function "
- "after the mission file has loaded. Internally it places all Paths into the server's "
- "PathManager. From this point the Paths are ready for transmission to the clients.\n\n"
- "@tsexample\n"
- "// Inform the engine to load all Path information from the mission.\n"
- "pathOnMissionLoadDone();\n\n"
- "@endtsexample\n"
- "@see NetConnection::transmitPaths()\n"
- "@see NetConnection::clearPaths()\n"
- "@see Path\n"
- "@ingroup Networking")
- {
- // Need to load subobjects for all loaded interiors...
- Scene* scene = Scene::getRootScene();
- AssertFatal(scene != NULL, "Error, mission done loading and no scene?");
- U32 currStart = 0;
- U32 currEnd = 1;
- Vector<SimGroup*> groups;
- groups.push_back(scene);
- while (true) {
- for (U32 i = currStart; i < currEnd; i++) {
- for (SimGroup::iterator itr = groups[i]->begin(); itr != groups[i]->end(); itr++) {
- if (dynamic_cast<SimGroup*>(*itr) != NULL)
- groups.push_back(static_cast<SimGroup*>(*itr));
- }
- }
- if (groups.size() == currEnd) {
- break;
- } else {
- currStart = currEnd;
- currEnd = groups.size();
- }
- }
- for (U32 i = 0; i < groups.size(); i++) {
- SimPath::Path* pPath = dynamic_cast<SimPath::Path*>(groups[i]);
- if (pPath)
- pPath->updatePath();
- }
- }
- S32 FN_CDECL cmpPathObject(const void* p1, const void* p2)
- {
- SimObject* o1 = *((SimObject**)p1);
- SimObject* o2 = *((SimObject**)p2);
- Marker* m1 = dynamic_cast<Marker*>(o1);
- Marker* m2 = dynamic_cast<Marker*>(o2);
- if (m1 == NULL && m2 == NULL)
- return 0;
- else if (m1 != NULL && m2 == NULL)
- return 1;
- else if (m1 == NULL && m2 != NULL)
- return -1;
- else {
- // Both markers...
- return S32(m1->mSeqNum) - S32(m2->mSeqNum);
- }
- }
- ConsoleDocClass(SimPath::Path,
- "@brief A spline along which various objects can move along. The spline object acts like a container for Marker objects, which make\n"
- "up the joints, or knots, along the path. Paths can be assigned a speed, can be looping or non-looping. Each of a path's markers can be\n"
- "one of three primary movement types: \"normal\", \"Position Only\", or \"Kink\". \n"
- "@tsexample\n"
- "new path()\n"
- " {\n"
- " isLooping = \"1\";\n"
- "\n"
- " new Marker()\n"
- " {\n"
- " seqNum = \"0\";\n"
- " type = \"Normal\";\n"
- " msToNext = \"1000\";\n"
- " smoothingType = \"Spline\";\n"
- " position = \"-0.054708 -35.0612 234.802\";\n"
- " rotation = \"1 0 0 0\";\n"
- " };\n"
- "\n"
- " };\n"
- "@endtsexample\n"
- "@see Marker\n"
- "@see NetConnection::transmitPaths()\n"
- "@see NetConnection::clearPaths()\n"
- "@see Path\n"
- "@ingroup enviroMisc\n"
- );
- namespace SimPath
- {
- //--------------------------------------------------------------------------
- //-------------------------------------- Implementation
- //
- IMPLEMENT_CONOBJECT(Path);
- Path::Path()
- {
- mPathIndex = NoPathIndex;
- mIsLooping = true;
- mPathSpeed = 1.0f;
- mDataBlock = NULL;
- mSpawnCount = 1;
- mMinDelay = 0;
- mMaxDelay = 0;
- }
- 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();
- //
- }
- //--------------------------------------------------------------------------
- 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()
- {
- //
- Parent::onRemove();
- }
- //--------------------------------------------------------------------------
- /// Sort the markers objects into sequence order
- void Path::sortMarkers()
- {
- dQsort(mObjectList.address(), mObjectList.size(), sizeof(SimObject*), cmpPathObject);
- }
- void Path::updatePath()
- {
- // If we need to, allocate a path index from the manager
- if (mPathIndex == NoPathIndex)
- mPathIndex = gServerPathManager->allocatePathId();
- sortMarkers();
- Vector<Point3F> positions;
- Vector<QuatF> rotations;
- Vector<U32> times;
- Vector<U32> smoothingTypes;
- for (iterator itr = begin(); itr != end(); itr++)
- {
- Marker* pMarker = dynamic_cast<Marker*>(*itr);
- if (pMarker != NULL)
- {
- Point3F pos;
- pMarker->getTransform().getColumn(3, &pos);
- positions.push_back(pos);
- QuatF rot;
- rot.set(pMarker->getTransform());
- rotations.push_back(rot);
- times.push_back(pMarker->mMSToNext);
- smoothingTypes.push_back(pMarker->mSmoothingType);
- }
- }
- gServerPathManager->updatePath(mPathIndex, positions, rotations, times, smoothingTypes, mIsLooping);
- }
- void Path::addObject(SimObject* obj)
- {
- Parent::addObject(obj);
- if (mPathIndex != NoPathIndex) {
- // If we're already finished, and this object is a marker, then we need to
- // update our path information...
- if (dynamic_cast<Marker*>(obj) != NULL)
- updatePath();
- }
- }
- void Path::removeObject(SimObject* obj)
- {
- bool recalc = dynamic_cast<Marker*>(obj) != NULL;
- Parent::removeObject(obj);
- if (mPathIndex != NoPathIndex && recalc == true)
- updatePath();
- }
- DefineEngineMethod( Path, getPathId, S32, (),,
- "@brief Returns the PathID (not the object ID) of this path.\n\n"
- "@return PathID (not the object ID) of this path.\n"
- "@tsexample\n"
- "// Acquire the PathID of this path object.\n"
- "%pathID = %thisPath.getPathId();\n\n"
- "@endtsexample\n\n"
- )
- {
- Path *path = static_cast<Path *>(object);
- return path->getPathIndex();
- }
- } // Namespace
- //--------------------------------------------------------------------------
- //--------------------------------------------------------------------------
- GFXStateBlockRef Marker::smStateBlock;
- GFXVertexBufferHandle<GFXVertexPCT> Marker::smVertexBuffer;
- GFXPrimitiveBufferHandle Marker::smPrimitiveBuffer;
- static Point3F wedgePoints[4] = {
- Point3F(-1, -1, 0),
- Point3F( 0, 1, 0),
- Point3F( 1, -1, 0),
- Point3F( 0,-.75, .5),
- };
- void Marker::initGFXResources()
- {
- if(smVertexBuffer != NULL)
- return;
-
- GFXStateBlockDesc d;
- d.cullDefined = true;
- d.cullMode = GFXCullNone;
-
- smStateBlock = GFX->createStateBlock(d);
-
- smVertexBuffer.set(GFX, 4, GFXBufferTypeStatic);
- GFXVertexPCT* verts = smVertexBuffer.lock();
- verts[0].point = wedgePoints[0] * 1.25f;
- verts[1].point = wedgePoints[1] * 1.25f;
- verts[2].point = wedgePoints[2] * 1.25f;
- verts[3].point = wedgePoints[3] * 1.25f;
- verts[1].color = GFXVertexColor(ColorI(255, 0, 0, 255));
- verts[0].color = verts[2].color = verts[3].color = GFXVertexColor(ColorI(0, 0, 255, 255));
- smVertexBuffer.unlock();
-
- smPrimitiveBuffer.set(GFX, 24, 12, GFXBufferTypeStatic);
- U16* prims;
- smPrimitiveBuffer.lock(&prims);
- prims[0] = 0;
- prims[1] = 3;
- prims[2] = 3;
- prims[3] = 1;
- prims[4] = 1;
- prims[5] = 0;
-
- prims[6] = 3;
- prims[7] = 1;
- prims[8] = 1;
- prims[9] = 2;
- prims[10] = 2;
- prims[11] = 3;
-
- prims[12] = 0;
- prims[13] = 3;
- prims[14] = 3;
- prims[15] = 2;
- prims[16] = 2;
- prims[17] = 0;
-
- prims[18] = 0;
- prims[19] = 2;
- prims[20] = 2;
- prims[21] = 1;
- prims[22] = 1;
- prims[23] = 0;
- smPrimitiveBuffer.unlock();
- }
- IMPLEMENT_CO_NETOBJECT_V1(Marker);
- ConsoleDocClass( Marker,
- "@brief A single joint, or knot, along a path. Should be stored inside a Path container object. A path markers can be\n"
- "one of three primary movement types: \"normal\", \"Position Only\", or \"Kink\". \n"
- "@tsexample\n"
- "new path()\n"
- " {\n"
- " isLooping = \"1\";\n"
- "\n"
- " new Marker()\n"
- " {\n"
- " seqNum = \"0\";\n"
- " type = \"Normal\";\n"
- " msToNext = \"1000\";\n"
- " smoothingType = \"Spline\";\n"
- " position = \"-0.054708 -35.0612 234.802\";\n"
- " rotation = \"1 0 0 0\";\n"
- " };\n"
- "\n"
- " };\n"
- "@endtsexample\n"
- "@see Path\n"
- "@ingroup enviroMisc\n"
- );
- Marker::Marker()
- {
- // Not ghostable unless we're editing...
- mNetFlags.clear(Ghostable);
- mTypeMask |= MarkerObjectType;
- mHitCommand = String::EmptyString;
- mSeqNum = 0;
- mMSToNext = 1000;
- mSmoothingType = SmoothingTypeSpline;
- mKnotType = KnotTypeNormal;
- }
- Marker::~Marker()
- {
- //
- }
- //--------------------------------------------------------------------------
- ImplementEnumType( MarkerSmoothingType,
- "The type of smoothing this marker will have for pathed objects.\n"
- "@ingroup enviroMisc\n\n")
- { Marker::SmoothingTypeSpline , "Spline", "Marker will cause the movements of the pathed object to be smooth.\n" },
- { Marker::SmoothingTypeLinear , "Linear", "Marker will have no smoothing effect.\n" },
- //{ Marker::SmoothingTypeAccelerate , "Accelerate" },
- EndImplementEnumType;
- ImplementEnumType( MarkerKnotType,
- "The type of knot that this marker will be.\n"
- "@ingroup enviroMisc\n\n")
- { Marker::KnotTypeNormal , "Normal", "Knot will have a smooth camera translation/rotation effect.\n" },
- { Marker::KnotTypePositionOnly, "Position Only", "Will do the same for translations, leaving rotation un-touched.\n" },
- { Marker::KnotTypeKink, "Kink", "The rotation will take effect immediately for an abrupt rotation change.\n" },
- EndImplementEnumType;
- void Marker::initPersistFields()
- {
- addGroup( "Misc" );
- addField("seqNum", TypeS32, Offset(mSeqNum, Marker), "Marker position in sequence of markers on this path.\n");
- addField("hitCommand", TypeCommand, Offset(mHitCommand, Marker), "The command to execute when a path follower reaches this marker.");
- addField("type", TYPEID< KnotType >(), Offset(mKnotType, Marker), "Type of this marker/knot. A \"normal\" knot will have a smooth camera translation/rotation effect.\n\"Position Only\" will do the same for translations, leaving rotation un-touched.\nLastly, a \"Kink\" means the rotation will take effect immediately for an abrupt rotation change.\n");
- addField("msToNext", TypeS32, Offset(mMSToNext, Marker), "Milliseconds to next marker in sequence.\n");
- addField("smoothingType", TYPEID< SmoothingType >(), Offset(mSmoothingType, Marker), "Path smoothing at this marker/knot. \"Linear\" means no smoothing, while \"Spline\" means to smooth.\n");
- endGroup("Misc");
- Parent::initPersistFields();
- }
- //--------------------------------------------------------------------------
- bool Marker::onAdd()
- {
- if(!Parent::onAdd())
- return false;
- mObjBox = Box3F(Point3F(-1.25, -1.25, -1.25), Point3F(1.25, 1.25, 1.25));
- resetWorldBox();
- if(gEditingMission)
- onEditorEnable();
- return true;
- }
- void Marker::onRemove()
- {
- if(gEditingMission)
- onEditorDisable();
- Parent::onRemove();
- smVertexBuffer = NULL;
- smPrimitiveBuffer = NULL;
- }
- void Marker::onGroupAdd()
- {
- mSeqNum = getGroup()->size() - 1;
- }
- /// Enable scoping so we can see this thing on the client.
- void Marker::onEditorEnable()
- {
- mNetFlags.set(Ghostable);
- setScopeAlways();
- addToScene();
- }
- /// Disable scoping so we can see this thing on the client
- void Marker::onEditorDisable()
- {
- removeFromScene();
- mNetFlags.clear(Ghostable);
- clearScopeAlways();
- }
- /// Tell our parent that this Path has been modified
- void Marker::inspectPostApply()
- {
- SimPath::Path *path = dynamic_cast<SimPath::Path*>(getGroup());
- if (path)
- path->updatePath();
- }
- //--------------------------------------------------------------------------
- void Marker::prepRenderImage( SceneRenderState* state )
- {
- // This should be sufficient for most objects that don't manage zones, and
- // don't need to return a specialized RenderImage...
- ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
- ri->renderDelegate.bind( this, &Marker::renderObject );
- ri->type = RenderPassManager::RIT_Editor;
- state->getRenderPass()->addInst(ri);
- }
- void Marker::renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat)
- {
- initGFXResources();
-
- for(U32 i = 0; i < GFX->getNumSamplers(); i++)
- GFX->setTexture(i, NULL);
- GFXTransformSaver saver;
- MatrixF mat = getRenderTransform();
- mat.scale(mObjScale);
- GFX->multWorld(mat);
-
- GFX->setStateBlock(smStateBlock);
- GFX->setVertexBuffer(smVertexBuffer);
- GFX->setPrimitiveBuffer(smPrimitiveBuffer);
- GFX->setupGenericShaders();
- GFX->drawIndexedPrimitive(GFXLineList, 0, 0, 4, 0, 12);
- }
- //--------------------------------------------------------------------------
- U32 Marker::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
- {
- U32 retMask = Parent::packUpdate(con, mask, stream);
- // Note that we don't really care about efficiency here, since this is an
- // edit-only ghost...
- stream->writeAffineTransform(mObjToWorld);
- return retMask;
- }
- void Marker::unpackUpdate(NetConnection* con, BitStream* stream)
- {
- Parent::unpackUpdate(con, stream);
- // Transform
- MatrixF otow;
- stream->readAffineTransform(&otow);
- setTransform(otow);
- }
|