123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166 |
- //-----------------------------------------------------------------------------
- // 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 "console/consoleTypes.h"
- #include "core/resourceManager.h"
- #include "ts/tsShape.h"
- #include "ts/tsShapeInstance.h"
- #include "ts/tsLastDetail.h"
- #include "ts/tsMaterialList.h"
- #include "core/stream/fileStream.h"
- #include "core/volume.h"
- //-----------------------------------------------------------------------------
- S32 TSShape::addName(const String& name)
- {
- // Check for empty names
- if (name.isEmpty())
- return -1;
- // Return the index of the new name (add if it is unique)
- S32 index = findName(name);
- if (index >= 0)
- return index;
- names.push_back(StringTable->insert(name));
- return names.size()-1;
- }
- void TSShape::updateSmallestVisibleDL()
- {
- // Update smallest visible detail
- mSmallestVisibleDL = -1;
- mSmallestVisibleSize = F32_MAX;
- F32 maxSize = 0.0f;
- for (S32 i = 0; i < details.size(); i++)
- {
- maxSize = getMax( maxSize, details[i].size );
- if ((details[i].size >= 0) && (details[i].size < mSmallestVisibleSize))
- {
- mSmallestVisibleDL = i;
- mSmallestVisibleSize = details[i].size;
- }
- }
- // Initialize the detail level lod lookup table.
- mDetailLevelLookup.setSize( (U32)( maxSize * 2.0f ) + 2 );
- for ( U32 l=0; l < mDetailLevelLookup.size(); l++ )
- {
- F32 pixelSize = (F32)l;
- S32 dl = -1;
- for ( U32 d=0; d < details.size(); d++ )
- {
- // Break when we get to hidden detail
- // levels like collision shapes.
- if ( details[d].size < 0 )
- break;
- if ( pixelSize > details[d].size )
- {
- dl = d;
- break;
- }
- if ( d + 1 >= details.size() || details[d+1].size < 0 )
- {
- // We've run out of details and haven't found anything?
- // Let's just grab this one.
- dl = d;
- break;
- }
- }
- // Calculate the intra detail level.
- F32 intraDL = 0;
- if ( dl > -1 )
- {
- F32 curSize = details[dl].size;
- F32 nextSize = dl == 0 ? 2.0f * curSize : details[dl - 1].size;
- intraDL = mClampF( nextSize - curSize > 0.01f ? (pixelSize - curSize) / (nextSize - curSize) : 1.0f, 0, 1 );
- }
- mDetailLevelLookup[l].set( dl, intraDL );
- }
- // Test for using the legacy screen error
- // lod method here instead of runtime.
- //
- // See setDetailFromDistance().
- //
- mUseDetailFromScreenError = mSmallestVisibleDL >= 0 &&
- details.first().maxError >= 0;
- }
- S32 TSShape::addDetail(const String& dname, S32 size, S32 subShapeNum)
- {
- S32 nameIndex = addName(avar("%s%d", dname.c_str(), size));
- // Check if this detail size has already been added
- S32 index;
- for (index = 0; index < details.size(); index++)
- {
- if ((details[index].size == size) &&
- (details[index].subShapeNum == subShapeNum) &&
- (details[index].nameIndex == nameIndex))
- return index;
- if (details[index].size < size)
- break;
- }
- // Create a new detail level at the right index, so array
- // remains sorted by detail size (from largest to smallest)
- details.insert(index);
- TSShape::Detail &detail = details[index];
- // Clear the detail to ensure no garbage values
- // are left in any vars we don't set.
- dMemset( &detail, 0, sizeof( Detail ) );
- // Setup the detail.
- detail.nameIndex = nameIndex;
- detail.size = size;
- detail.subShapeNum = subShapeNum;
- detail.objectDetailNum = 0;
- detail.averageError = -1;
- detail.maxError = -1;
- detail.polyCount = 0;
- // Resize alpha vectors
- alphaIn.increment();
- alphaOut.increment();
- // Fixup objectDetailNum in other detail levels
- for (S32 i = index+1; i < details.size(); i++)
- {
- if ((details[i].subShapeNum >= 0) &&
- ((subShapeNum == -1) || (details[i].subShapeNum == subShapeNum)))
- details[i].objectDetailNum++;
- }
- // Update smallest visible detail
- updateSmallestVisibleDL();
- return index;
- }
- S32 TSShape::addImposter(const String& cachePath, S32 size, S32 numEquatorSteps,
- S32 numPolarSteps, S32 dl, S32 dim, bool includePoles, F32 polarAngle)
- {
- // Check if the desired size is already in use
- bool isNewDetail = false;
- S32 detIndex = findDetailBySize( size );
- if ( detIndex >= 0 )
- {
- // Size is in use. If the detail is already an imposter, we can just change
- // the settings, otherwise quit
- if ( details[detIndex].subShapeNum >= 0 )
- {
- Con::errorf( "TSShape::addImposter: A non-billboard detail already "
- "exists at size %d", size );
- return -1;
- }
- }
- else
- {
- // Size is not in use. If an imposter already exists, change its size, otherwise
- // create a new detail
- for ( detIndex = 0; detIndex < details.size(); ++detIndex )
- {
- if ( details[detIndex].subShapeNum < 0 )
- {
- // Change the imposter detail size
- setDetailSize( details[detIndex].size, size );
- break;
- }
- }
- if ( detIndex == details.size() )
- {
- isNewDetail = true;
- detIndex = addDetail( "bbDetail", size, -1 );
- }
- }
- // Now set the billboard properties.
- Detail &detail = details[detIndex];
- // In prior to DTS version 26 we would pack the autobillboard
- // into this single 32bit value. This was prone to overflows
- // of parameters caused random bugs.
- //
- // Set the old autobillboard properties var to zero.
- detail.objectDetailNum = 0;
-
- // We now use the new vars.
- detail.bbEquatorSteps = numEquatorSteps;
- detail.bbPolarSteps = numPolarSteps;
- detail.bbPolarAngle = polarAngle;
- detail.bbDetailLevel = dl;
- detail.bbDimension = dim;
- detail.bbIncludePoles = includePoles;
- // Rebuild billboard details or force an update of the modified detail
- if ( isNewDetail )
- {
- // Add NULL meshes for this detail
- for ( S32 iObj = 0; iObj < objects.size(); ++iObj )
- {
- if ( detIndex < objects[iObj].numMeshes )
- {
- objects[iObj].numMeshes++;
- meshes.insert( objects[iObj].startMeshIndex + detIndex, NULL );
- for (S32 j = iObj + 1; j < objects.size(); ++j )
- objects[j].startMeshIndex++;
- }
- }
- // Could be dedicated server.
- if ( GFXDevice::devicePresent() )
- setupBillboardDetails( cachePath );
- while ( detailCollisionAccelerators.size() < details.size() )
- detailCollisionAccelerators.push_back( NULL );
- }
- else
- {
- if ( billboardDetails.size() && GFXDevice::devicePresent() )
- {
- delete billboardDetails[detIndex];
- billboardDetails[detIndex] = new TSLastDetail(
- this,
- cachePath,
- detail.bbEquatorSteps,
- detail.bbPolarSteps,
- detail.bbPolarAngle,
- detail.bbIncludePoles,
- detail.bbDetailLevel,
- detail.bbDimension );
- billboardDetails[detIndex]->update( true );
- }
- }
- return detIndex;
- }
- bool TSShape::removeImposter()
- {
- // Find the imposter detail level
- S32 detIndex;
- for ( detIndex = 0; detIndex < details.size(); ++detIndex )
- {
- if ( details[detIndex].subShapeNum < 0 )
- break;
- }
- if ( detIndex == details.size() )
- {
- Con::errorf( "TSShape::removeImposter: No imposter detail level found in shape" );
- return false;
- }
- // Remove the detail level
- details.erase( detIndex );
- if ( detIndex < billboardDetails.size() )
- {
- // Delete old textures
- TSLastDetail* bb = billboardDetails[detIndex];
- bb->deleteImposterCacheTextures();
- delete billboardDetails[detIndex];
- }
- billboardDetails.clear();
- detailCollisionAccelerators.erase( detIndex );
- // Remove the (NULL) meshes from each object
- for ( S32 iObj = 0; iObj < objects.size(); ++iObj )
- {
- if ( detIndex < objects[iObj].numMeshes )
- {
- objects[iObj].numMeshes--;
- meshes.erase( objects[iObj].startMeshIndex + detIndex );
- for (S32 j = iObj + 1; j < objects.size(); ++j )
- objects[j].startMeshIndex--;
- }
- }
- // Update smallest visible size
- updateSmallestVisibleDL();
- return true;
- }
- //-----------------------------------------------------------------------------
- /// Get the index of the element in the group with a given name
- template<class T> S32 findByName(Vector<T>& group, S32 nameIndex)
- {
- for (S32 i = 0; i < group.size(); i++)
- if (group[i].nameIndex == nameIndex)
- return i;
- return -1;
- }
- /// Adjust the nameIndex for elements in the group
- template<class T> void adjustForNameRemoval(Vector<T>& group, S32 nameIndex)
- {
- for (S32 i = 0; i < group.size(); i++)
- if (group[i].nameIndex > nameIndex)
- group[i].nameIndex--;
- }
- bool TSShape::removeName(const String& name)
- {
- // Check if the name is still in use
- S32 nameIndex = findName(name);
- if ((findByName(nodes, nameIndex) >= 0) ||
- (findByName(objects, nameIndex) >= 0) ||
- (findByName(sequences, nameIndex) >= 0) ||
- (findByName(details, nameIndex) >= 0))
- return false;
- // Remove the name, then update nameIndex for affected elements
- names.erase(nameIndex);
- adjustForNameRemoval(nodes, nameIndex);
- adjustForNameRemoval(objects, nameIndex);
- adjustForNameRemoval(sequences, nameIndex);
- adjustForNameRemoval(details, nameIndex);
- return true;
- }
- //-----------------------------------------------------------------------------
- template<class T> bool doRename(TSShape* shape, Vector<T>& group, const String& oldName, const String& newName)
- {
- // Find the element in the group with the oldName
- S32 index = findByName(group, shape->findName(oldName));
- if (index < 0)
- {
- Con::errorf("TSShape::rename: Could not find '%s'", oldName.c_str());
- return false;
- }
- // Ignore trivial renames
- if (oldName.equal(newName, String::NoCase))
- return true;
- // Check that this name is not already in use
- if (findByName(group, shape->findName(newName)) >= 0)
- {
- Con::errorf("TSShape::rename: '%s' is already in use", newName.c_str());
- return false;
- }
- // Do the rename (the old name will be removed if it is no longer in use)
- group[index].nameIndex = shape->addName(newName);
- shape->removeName(oldName);
- return true;
- }
- bool TSShape::renameNode(const String& oldName, const String& newName)
- {
- return doRename(this, nodes, oldName, newName);
- }
- bool TSShape::renameObject(const String& oldName, const String& newName)
- {
- return doRename(this, objects, oldName, newName);
- }
- bool TSShape::renameDetail(const String& oldName, const String& newName)
- {
- return doRename(this, details, oldName, newName);
- }
- bool TSShape::renameSequence(const String& oldName, const String& newName)
- {
- return doRename(this, sequences, oldName, newName);
- }
- //-----------------------------------------------------------------------------
- bool TSShape::addNode(const String& name, const String& parentName, const Point3F& pos, const QuatF& rot)
- {
- // Check that adding this node would not exceed the maximum count
- if (nodes.size() >= MAX_TS_SET_SIZE)
- {
- Con::errorf("TSShape::addNode: Cannot add node, shape already has maximum (%d) nodes", MAX_TS_SET_SIZE);
- return false;
- }
- // Check that there is not already a node with this name
- if (findNode(name) >= 0)
- {
- Con::errorf("TSShape::addNode: %s already exists!", name.c_str());
- return false;
- }
- // Find the parent node (OK for name to be empty => node is at root level)
- S32 parentIndex = -1;
- if (dStrcmp(parentName, ""))
- {
- parentIndex = findNode(parentName);
- if (parentIndex < 0)
- {
- Con::errorf("TSShape::addNode: Could not find parent node '%s'", parentName.c_str());
- return false;
- }
- }
- // Need to make everything editable since node indexes etc will change
- makeEditable();
- // Insert node at the end of the subshape
- S32 subShapeIndex = (parentIndex >= 0) ? getSubShapeForNode(parentIndex) : 0;
- S32 nodeIndex = subShapeNumNodes[subShapeIndex];
- // Adjust subshape node indices
- subShapeNumNodes[subShapeIndex]++;
- for (S32 i = subShapeIndex + 1; i < subShapeFirstNode.size(); i++)
- subShapeFirstNode[i]++;
- // Update animation sequences
- for (S32 iSeq = 0; iSeq < sequences.size(); iSeq++)
- {
- // Update animation matters arrays (new node is not animated)
- TSShape::Sequence& seq = sequences[iSeq];
- seq.translationMatters.insert(nodeIndex, false);
- seq.rotationMatters.insert(nodeIndex, false);
- seq.scaleMatters.insert(nodeIndex, false);
- }
- // Insert the new node
- TSShape::Node node;
- node.nameIndex = addName(name);
- node.parentIndex = parentIndex;
- node.firstChild = -1;
- node.firstObject = -1;
- node.nextSibling = -1;
- nodes.insert(nodeIndex, node);
- // Insert node default translation and rotation
- Quat16 rot16;
- rot16.set(rot);
- defaultTranslations.insert(nodeIndex, pos);
- defaultRotations.insert(nodeIndex, rot16);
- // Fixup node indices
- for (S32 i = 0; i < nodes.size(); i++)
- {
- if (nodes[i].parentIndex >= nodeIndex)
- nodes[i].parentIndex++;
- }
- for (S32 i = 0; i < objects.size(); i++)
- {
- if (objects[i].nodeIndex >= nodeIndex)
- objects[i].nodeIndex++;
- }
- for (S32 i = 0; i < meshes.size(); i++)
- {
- if (meshes[i] && (meshes[i]->getMeshType() == TSMesh::SkinMeshType))
- {
- TSSkinMesh* skin = dynamic_cast<TSSkinMesh*>(meshes[i]);
- for (S32 j = 0; j < skin->batchData.nodeIndex.size(); j++)
- {
- if (skin->batchData.nodeIndex[j] >= nodeIndex)
- skin->batchData.nodeIndex[j]++;
- }
- }
- }
- initObjects();
- return true;
- }
- /// Erase animation keyframes (translation, rotation etc)
- template<class T> S32 eraseStates(Vector<T>& vec, const TSIntegerSet& matters, S32 base, S32 numKeyframes, S32 index=-1)
- {
- S32 dest, count;
- if (index == -1)
- {
- // Erase for all nodes/objects
- dest = base;
- count = numKeyframes * matters.count();
- }
- else
- {
- // Erase for the indexed node/object only
- dest = base + matters.count(index)*numKeyframes;
- count = numKeyframes;
- }
- // Erase the values
- if (count)
- {
- if ((dest + count) < vec.size())
- dCopyArray(&vec[dest], &vec[dest + count], vec.size() - (dest + count));
- vec.decrement(count);
- }
- return count;
- }
- bool TSShape::removeNode(const String& name)
- {
- // Find the node to be removed
- S32 nodeIndex = findNode(name);
- if (nodeIndex < 0)
- {
- Con::errorf("TSShape::removeNode: Could not find node '%s'", name.c_str());
- return false;
- }
- S32 nodeParentIndex = nodes[nodeIndex].parentIndex;
- // Warn if there are objects attached to this node
- Vector<S32> nodeObjects;
- getNodeObjects(nodeIndex, nodeObjects);
- if (nodeObjects.size())
- {
- Con::warnf("TSShape::removeNode: Node '%s' has %d objects attached, these "
- "will be reassigned to the node's parent ('%s')", name.c_str(), nodeObjects.size(),
- ((nodeParentIndex >= 0) ? getName(nodes[nodeParentIndex].nameIndex).c_str() : "null"));
- }
- // Need to make everything editable since node indexes etc will change
- makeEditable();
- // Update animation sequences
- for (S32 iSeq = 0; iSeq < sequences.size(); iSeq++)
- {
- TSShape::Sequence& seq = sequences[iSeq];
- // Remove animated node transforms
- if (seq.translationMatters.test(nodeIndex))
- eraseStates(nodeTranslations, seq.translationMatters, seq.baseTranslation, seq.numKeyframes, nodeIndex);
- if (seq.rotationMatters.test(nodeIndex))
- eraseStates(nodeRotations, seq.rotationMatters, seq.baseRotation, seq.numKeyframes, nodeIndex);
- if (seq.scaleMatters.test(nodeIndex))
- {
- if (seq.flags & TSShape::ArbitraryScale)
- {
- eraseStates(nodeArbitraryScaleRots, seq.scaleMatters, seq.baseScale, seq.numKeyframes, nodeIndex);
- eraseStates(nodeArbitraryScaleFactors, seq.scaleMatters, seq.baseScale, seq.numKeyframes, nodeIndex);
- }
- else if (seq.flags & TSShape::AlignedScale)
- eraseStates(nodeAlignedScales, seq.scaleMatters, seq.baseScale, seq.numKeyframes, nodeIndex);
- else
- eraseStates(nodeUniformScales, seq.scaleMatters, seq.baseScale, seq.numKeyframes, nodeIndex);
- }
- seq.translationMatters.erase(nodeIndex);
- seq.rotationMatters.erase(nodeIndex);
- seq.scaleMatters.erase(nodeIndex);
- }
- // Remove the node
- nodes.erase(nodeIndex);
- defaultTranslations.erase(nodeIndex);
- defaultRotations.erase(nodeIndex);
- // Adjust subshape node indices
- S32 subShapeIndex = getSubShapeForNode(nodeIndex);
- subShapeNumNodes[subShapeIndex]--;
- for (S32 i = subShapeIndex + 1; i < subShapeFirstNode.size(); i++)
- subShapeFirstNode[i]--;
- // Fixup node parent indices
- for (S32 i = 0; i < nodes.size(); i++)
- {
- if (nodes[i].parentIndex == nodeIndex)
- nodes[i].parentIndex = -1;
- else if (nodes[i].parentIndex > nodeIndex)
- nodes[i].parentIndex--;
- }
- if (nodeParentIndex > nodeIndex)
- nodeParentIndex--;
- // Fixup object node indices, and re-assign attached objects to node's parent
- for (S32 i = 0; i < objects.size(); i++)
- {
- if (objects[i].nodeIndex == nodeIndex)
- objects[i].nodeIndex = nodeParentIndex;
- if (objects[i].nodeIndex > nodeIndex)
- objects[i].nodeIndex--;
- }
- // Fixup skin weight node indices, and re-assign weights for deleted node to its parent
- for (S32 i = 0; i < meshes.size(); i++)
- {
- if (meshes[i] && (meshes[i]->getMeshType() == TSMesh::SkinMeshType))
- {
- TSSkinMesh* skin = dynamic_cast<TSSkinMesh*>(meshes[i]);
- for (S32 j = 0; j < skin->batchData.nodeIndex.size(); j++)
- {
- if (skin->batchData.nodeIndex[j] == nodeIndex)
- skin->batchData.nodeIndex[j] = nodeParentIndex;
- if (skin->batchData.nodeIndex[j] > nodeIndex)
- skin->batchData.nodeIndex[j]--;
- }
- }
- }
- // Remove the sequence name if it is no longer in use
- removeName(name);
- initObjects();
- return true;
- }
- //-----------------------------------------------------------------------------
- bool TSShape::setNodeTransform(const String& name, const Point3F& pos, const QuatF& rot)
- {
- // Find the node to be transformed
- S32 nodeIndex = findNode(name);
- if (nodeIndex < 0)
- {
- Con::errorf("TSShape::setNodeTransform: Could not find node '%s'", name.c_str());
- return false;
- }
- // Update initial node position and rotation
- defaultTranslations[nodeIndex] = pos;
- defaultRotations[nodeIndex].set(rot);
- return true;
- }
- //-----------------------------------------------------------------------------
- S32 TSShape::addObject(const String& objName, S32 subShapeIndex)
- {
- S32 objIndex = subShapeNumObjects[subShapeIndex];
- // Add object to subshape
- subShapeNumObjects[subShapeIndex]++;
- for (S32 i = subShapeIndex + 1; i < subShapeFirstObject.size(); i++)
- subShapeFirstObject[i]++;
- TSShape::Object obj;
- obj.nameIndex = addName(objName);
- obj.nodeIndex = 0;
- obj.numMeshes = 0;
- obj.startMeshIndex = (objIndex == 0) ? 0 : objects[objIndex-1].startMeshIndex + objects[objIndex-1].numMeshes;
- obj.firstDecal = 0;
- obj.nextSibling = 0;
- objects.insert(objIndex, obj);
- // Add default object state
- TSShape::ObjectState state;
- state.frameIndex = 0;
- state.matFrameIndex = 0;
- state.vis = 1.0f;
- objectStates.insert(objIndex, state);
- // Fixup sequences
- for (S32 i = 0; i < sequences.size(); i++)
- sequences[i].baseObjectState++;
- return objIndex;
- }
- void TSShape::addMeshToObject(S32 objIndex, S32 meshIndex, TSMesh* mesh)
- {
- TSShape::Object& obj = objects[objIndex];
- // Pad with NULLs if required
- S32 oldNumMeshes = obj.numMeshes;
- if (mesh)
- {
- for (S32 i = obj.numMeshes; i < meshIndex; i++)
- {
- meshes.insert(obj.startMeshIndex + i, NULL);
- obj.numMeshes++;
- }
- }
- // Insert the new mesh
- meshes.insert(obj.startMeshIndex + meshIndex, mesh);
- obj.numMeshes++;
- // Skinned meshes are not attached to any node
- if (mesh && (mesh->getMeshType() == TSMesh::SkinMeshType))
- obj.nodeIndex = -1;
- // Fixup mesh indices for other objects
- for (S32 i = 0; i < objects.size(); i++)
- {
- if ((i != objIndex) && (objects[i].startMeshIndex >= obj.startMeshIndex))
- objects[i].startMeshIndex += (obj.numMeshes - oldNumMeshes);
- }
- }
- void TSShape::removeMeshFromObject(S32 objIndex, S32 meshIndex)
- {
- TSShape::Object& obj = objects[objIndex];
- // Remove the mesh, but do not destroy it (this must be done by the caller)
- meshes[obj.startMeshIndex + meshIndex] = NULL;
- // Check if there are any objects remaining that have a valid mesh at this
- // detail size
- bool removeDetail = true;
- for (S32 i = 0; i < objects.size(); i++)
- {
- if ((meshIndex < objects[i].numMeshes) && meshes[objects[i].startMeshIndex + meshIndex])
- {
- removeDetail = false;
- break;
- }
- }
- // Remove detail level if possible
- if (removeDetail)
- {
- for (S32 i = 0; i < objects.size(); i++)
- {
- if (meshIndex < objects[i].numMeshes)
- {
- U32 idxToRemove = objects[i].startMeshIndex + meshIndex;
- meshes.erase(idxToRemove);
- objects[i].numMeshes--;
- // Clear invalid parent
- for (U32 k = 0; k < meshes.size(); k++)
- {
- if (meshes[k] == NULL)
- continue;
- if (meshes[k]->parentMesh == idxToRemove)
- {
- meshes[k]->parentMesh = -1;
- }
- else if (meshes[k]->parentMesh > idxToRemove)
- {
- meshes[k]->parentMesh--;
- }
- }
- for (S32 j = 0; j < objects.size(); j++)
- {
- if (objects[j].startMeshIndex > objects[i].startMeshIndex)
- objects[j].startMeshIndex--;
- }
- }
- }
- Vector<S32> validDetails;
- getSubShapeDetails(getSubShapeForObject(objIndex), validDetails);
- for (S32 i = 0; i < validDetails.size(); i++)
- {
- TSShape::Detail& detail = details[validDetails[i]];
- if (detail.objectDetailNum > meshIndex)
- detail.objectDetailNum--;
- }
- details.erase(validDetails[meshIndex]);
- }
- // Remove trailing NULL meshes from the object
- S32 oldNumMeshes = obj.numMeshes;
- while (obj.numMeshes && !meshes[obj.startMeshIndex + obj.numMeshes - 1])
- {
- U32 idxToRemove = obj.startMeshIndex + obj.numMeshes - 1;
- meshes.erase(idxToRemove);
- // Clear invalid parent
- for (U32 k = 0; k < meshes.size(); k++)
- {
- if (meshes[k] == NULL)
- continue;
- if (meshes[k]->parentMesh == idxToRemove)
- {
- meshes[k]->parentMesh = -1;
- }
- else if (meshes[k]->parentMesh > idxToRemove)
- {
- meshes[k]->parentMesh--;
- }
- }
- obj.numMeshes--;
- }
- // Fixup mesh indices for other objects
- for (S32 i = 0; i < objects.size(); i++)
- {
- if (objects[i].startMeshIndex > obj.startMeshIndex)
- objects[i].startMeshIndex -= (oldNumMeshes - obj.numMeshes);
- }
- }
- bool TSShape::setObjectNode(const String& objName, const String& nodeName)
- {
- // Find the object and node
- S32 objIndex = findObject(objName);
- if (objIndex < 0)
- {
- Con::errorf("TSShape::setObjectNode: Could not find object '%s'", objName.c_str());
- return false;
- }
- S32 nodeIndex;
- if (nodeName.isEmpty())
- nodeIndex = -1;
- else
- {
- nodeIndex = findNode(nodeName);
- if (nodeIndex < 0)
- {
- Con::errorf("TSShape::setObjectNode: Could not find node '%s'", nodeName.c_str());
- return false;
- }
- }
- objects[objIndex].nodeIndex = nodeIndex;
- return true;
- }
- bool TSShape::removeObject(const String& name)
- {
- // Find the object
- S32 objIndex = findObject(name);
- if (objIndex < 0)
- {
- Con::errorf("TSShape::removeObject: Could not find object '%s'", name.c_str());
- return false;
- }
- // Need to make everything editable since node indexes etc will change
- makeEditable();
- // Destroy all meshes in the object
- TSShape::Object& obj = objects[objIndex];
- while ( obj.numMeshes )
- {
- destructInPlace(meshes[obj.startMeshIndex + obj.numMeshes - 1]);
- removeMeshFromObject(objIndex, obj.numMeshes - 1);
- }
- // Remove the object from the shape
- objects.erase(objIndex);
- S32 subShapeIndex = getSubShapeForObject(objIndex);
- subShapeNumObjects[subShapeIndex]--;
- for (S32 i = subShapeIndex + 1; i < subShapeFirstObject.size(); i++)
- subShapeFirstObject[i]--;
- // Remove the object from all sequences
- for (S32 i = 0; i < sequences.size(); i++)
- {
- TSShape::Sequence& seq = sequences[i];
- TSIntegerSet objMatters(seq.frameMatters);
- objMatters.overlap(seq.matFrameMatters);
- objMatters.overlap(seq.visMatters);
- if (objMatters.test(objIndex))
- eraseStates(objectStates, objMatters, seq.baseObjectState, seq.numKeyframes, objIndex);
- seq.frameMatters.erase(objIndex);
- seq.matFrameMatters.erase(objIndex);
- seq.visMatters.erase(objIndex);
- }
- // Remove the object name if it is no longer in use
- removeName(name);
- // Update smallest visible detail
- updateSmallestVisibleDL();
- initObjects();
- return true;
- }
- //-----------------------------------------------------------------------------
- // Helper to copy a TSMesh ready for adding to this TSShape (with the right vertex format etc)
- TSMesh* TSShape::copyMesh( const TSMesh* srcMesh ) const
- {
- TSMesh * mesh = 0;
- if ( srcMesh && ( srcMesh->getMeshType() == TSMesh::SkinMeshType ) )
- {
- TSSkinMesh* skin = new TSSkinMesh;
- // Copy skin elements
- const TSSkinMesh *srcSkin = dynamic_cast<const TSSkinMesh*>(srcMesh);
- skin->weight = srcSkin->weight;
- skin->vertexIndex = srcSkin->vertexIndex;
- skin->boneIndex = srcSkin->boneIndex;
- skin->batchData.nodeIndex = srcSkin->batchData.nodeIndex;
- skin->batchData.initialTransforms = srcSkin->batchData.initialTransforms;
- skin->batchData.initialVerts = srcSkin->batchData.initialVerts;
- skin->batchData.initialNorms = srcSkin->batchData.initialNorms;
- mesh = static_cast<TSMesh*>(skin);
- }
- else
- {
- mesh = new TSMesh;
- }
- if ( !srcMesh )
- return mesh; // return an empty mesh
- // Copy mesh elements
- mesh->indices = srcMesh->indices;
- mesh->primitives = srcMesh->primitives;
- mesh->numFrames = srcMesh->numFrames;
- mesh->numMatFrames = srcMesh->numMatFrames;
- mesh->vertsPerFrame = srcMesh->vertsPerFrame;
- mesh->setFlags(srcMesh->getFlags());
- mesh->mNumVerts = srcMesh->mNumVerts;
- // Copy vertex data in an *unpacked* form
- mesh->copySourceVertexDataFrom(srcMesh);
- mesh->createTangents(mesh->verts, mesh->norms);
- mesh->encodedNorms.set(NULL, 0);
- mesh->computeBounds();
- return mesh;
- }
- bool TSShape::addMesh(TSMesh* mesh, const String& meshName)
- {
- // Ensure mesh is in editable state
- mesh->makeEditable();
- // Need to make everything editable since node indexes etc will change
- makeEditable();
- // Determine the object name and detail size from the mesh name
- S32 detailSize = 999;
- String objName(String::GetTrailingNumber(meshName, detailSize));
- // Find the destination object (create one if it does not exist)
- S32 objIndex = findObject(objName);
- if (objIndex < 0)
- objIndex = addObject(objName, 0);
- AssertFatal(objIndex >= 0 && objIndex < objects.size(), "Invalid object index!");
- // Determine the subshape this object belongs to
- S32 subShapeIndex = getSubShapeForObject(objIndex);
- AssertFatal(subShapeIndex < subShapeFirstObject.size(), "Could not find subshape for object!");
- // Get the existing detail levels for the subshape
- Vector<S32> validDetails;
- getSubShapeDetails(subShapeIndex, validDetails);
- // Determine where to add the new mesh, and whether this is a new detail
- S32 detIndex;
- bool newDetail = true;
- for (detIndex = 0; detIndex < validDetails.size(); detIndex++)
- {
- const TSShape::Detail& det = details[validDetails[detIndex]];
- if (detailSize >= det.size)
- {
- newDetail = (det.size != detailSize);
- break;
- }
- }
- // Insert the new detail level if required
- if (newDetail)
- {
- // Determine a name for the detail level
- const char* detailName;
- if (dStrStartsWith(objName, "Col"))
- detailName = "collision";
- else if (dStrStartsWith(objName, "loscol"))
- detailName = "los";
- else
- detailName = "detail";
- S32 index = addDetail(detailName, detailSize, subShapeIndex);
- details[index].objectDetailNum = detIndex;
- }
- // Adding a new mesh or detail level is a bit tricky, since each
- // object potentially stores a different number of meshes, including
- // NULL meshes for higher detail levels where required.
- // For example, the following table shows 3 objects. Note how NULLs
- // must be inserted for detail levels higher than the first valid
- // mesh, but details after the the last valid mesh are left empty.
- //
- // Detail | Object1 | Object2 | Object3
- // ---------+-----------+-----------+---------
- // 128 | 128 | NULL | NULL
- // 64 | | NULL | 64
- // 32 | | 32 | NULL
- // 2 | | | 2
- // Add meshes as required for each object
- for (S32 i = 0; i < subShapeNumObjects[subShapeIndex]; i++)
- {
- S32 index = subShapeFirstObject[subShapeIndex] + i;
- const TSShape::Object& obj = objects[index];
- if (index == objIndex)
- {
- // The target object: replace the existing mesh (if any) or add a new one
- // if required.
- if (!newDetail && (detIndex < obj.numMeshes))
- {
- if ( meshes[obj.startMeshIndex + detIndex] )
- destructInPlace(meshes[obj.startMeshIndex + detIndex]);
- meshes[obj.startMeshIndex + detIndex] = mesh;
- }
- else
- addMeshToObject(index, detIndex, mesh);
- }
- else
- {
- // Other objects: add a NULL mesh only if inserting before a valid mesh
- if (newDetail && (detIndex < obj.numMeshes))
- addMeshToObject(index, detIndex, NULL);
- }
- }
- initObjects();
- return true;
- }
- bool TSShape::addMesh(TSShape* srcShape, const String& srcMeshName, const String& meshName)
- {
- // Find the mesh in the source shape
- TSMesh* srcMesh = srcShape->findMesh(srcMeshName);
- if (!srcMesh)
- {
- Con::errorf("TSShape::addMesh: Could not find mesh '%s' in shape", srcMeshName.c_str());
- return false;
- }
- // Copy the source mesh
- TSMesh *mesh = copyMesh( srcMesh );
- if (srcMesh->getMeshType() == TSMesh::SkinMeshType)
- {
- TSSkinMesh *srcSkin = dynamic_cast<TSSkinMesh*>(srcMesh);
- // Check that the source skin is compatible with our skeleton
- Vector<S32> nodeMap(srcShape->nodes.size());
- for (S32 i = 0; i < srcShape->nodes.size(); i++)
- nodeMap.push_back( findNode( srcShape->getName(srcShape->nodes[i].nameIndex) ) );
- for (S32 i = 0; i < srcSkin->boneIndex.size(); i++)
- {
- S32 srcNode = srcSkin->boneIndex[i];
- if (nodeMap[srcNode] == -1)
- {
- const char* name = srcShape->getName(srcShape->nodes[srcNode].nameIndex).c_str();
- Con::errorf("TSShape::addMesh: Skin is weighted to node (%s) that "
- "does not exist in this shape", name);
- return false;
- }
- }
- TSSkinMesh *skin = dynamic_cast<TSSkinMesh*>(mesh);
- // Remap node indices
- skin->batchData.nodeIndex = srcSkin->batchData.nodeIndex;
- for (S32 i = 0; i < skin->batchData.nodeIndex.size(); i++)
- skin->batchData.nodeIndex[i] = nodeMap[skin->batchData.nodeIndex[i]];
- }
- // Add the copied mesh to the shape
- if (!addMesh(mesh, meshName))
- {
- delete mesh;
- return false;
- }
- // Copy materials used by the source mesh (only if from a different shape)
- if (srcShape != this)
- {
- for (S32 i = 0; i < mesh->primitives.size(); i++)
- {
- if (!(mesh->primitives[i].matIndex & TSDrawPrimitive::NoMaterial))
- {
- S32 drawType = (mesh->primitives[i].matIndex & (~TSDrawPrimitive::MaterialMask));
- S32 srcMatIndex = mesh->primitives[i].matIndex & TSDrawPrimitive::MaterialMask;
- const String& matName = srcShape->materialList->getMaterialName(srcMatIndex);
- // Add the material if it does not already exist
- S32 destMatIndex = materialList->getMaterialNameList().find_next(matName);
- if (destMatIndex < 0)
- {
- destMatIndex = materialList->size();
- materialList->push_back(matName, srcShape->materialList->getFlags(srcMatIndex));
- }
- mesh->primitives[i].matIndex = drawType | destMatIndex;
- }
- }
- }
- return true;
- }
- bool TSShape::setMeshSize(const String& meshName, S32 size)
- {
- S32 objIndex, meshIndex;
- if (!findMeshIndex(meshName, objIndex, meshIndex) ||
- !meshes[objects[objIndex].startMeshIndex + meshIndex])
- {
- Con::errorf("TSShape::setMeshSize: Could not find mesh '%s'", meshName.c_str());
- return false;
- }
- // Need to make everything editable since node indexes etc will change
- makeEditable();
- // Remove the mesh from the object, but don't destroy it
- TSShape::Object& obj = objects[objIndex];
- TSMesh* mesh = meshes[obj.startMeshIndex + meshIndex];
- removeMeshFromObject(objIndex, meshIndex);
- // Add the mesh back at the new position
- addMesh(mesh, avar("%s %d", getName(obj.nameIndex).c_str(), size));
- // Update smallest visible detail
- updateSmallestVisibleDL();
- initObjects();
- return true;
- }
- bool TSShape::removeMesh(const String& meshName)
- {
- S32 objIndex, meshIndex;
- if (!findMeshIndex(meshName, objIndex, meshIndex) ||
- !meshes[objects[objIndex].startMeshIndex + meshIndex])
- {
- Con::errorf("TSShape::removeMesh: Could not find mesh '%s'", meshName.c_str());
- return false;
- }
- // Need to make everything editable since node indexes etc will change
- makeEditable();
- // Destroy and remove the mesh
- TSShape::Object& obj = objects[objIndex];
- destructInPlace(meshes[obj.startMeshIndex + meshIndex]);
- removeMeshFromObject(objIndex, meshIndex);
- // Remove the object if there are no meshes left
- if (!obj.numMeshes)
- removeObject(getName(obj.nameIndex));
- // Update smallest visible detail
- updateSmallestVisibleDL();
- initObjects();
- return true;
- }
- //-----------------------------------------------------------------------------
- // Helper function for dealing with some of the Vectors used in a TSShape. 'meshes'
- // for example contains a TSMesh* per-object, per-detail-level, with NULLs for
- // undefined details for each object. Trailing NULLs are not added to the Vector,
- // so you end up with a different number of pointers for each object, depending
- // on which detail levels it defines. This makes it tricky to move meshes around
- // since you have to know how many elements belong to each object.
- // To simplify things, this function pads the Vector up to a certain length (so
- // all objects can appear to have the same number of meshes), the moves a single
- // element to a new index, then trims trailing NULLs again.
- template<class T>
- static void _PadMoveAndTrim(Vector<T*>& vec, S32 offset, S32 count,
- S32 padLength, S32 oldIndex, S32 newIndex)
- {
- // Pad the array with NULLs
- for ( S32 i = count; i < padLength; ++i )
- vec.insert( offset + count, NULL );
- // Move the element from the old to the new index
- T* tmp = vec[offset + oldIndex];
- vec.erase( offset + oldIndex );
- vec.insert( offset + newIndex, tmp );
- // Trim trailing NULLs from the vector
- for ( S32 i = padLength - 1; i >= 0; --i )
- {
- if ( vec[offset + i] )
- break;
- else
- vec.erase( offset + i );
- }
- }
- S32 TSShape::setDetailSize(S32 oldSize, S32 newSize)
- {
- S32 oldIndex = findDetailBySize( oldSize );
- if ( oldIndex < 0 )
- {
- Con::errorf( "TSShape::setDetailSize: Cannot find detail with size %d", oldSize );
- return -1;
- }
- // Remove this detail from the list
- TSShape::Detail tmpDetail = details[oldIndex];
- tmpDetail.size = newSize;
- details.erase(oldIndex);
- // Determine the new position for the detail (details are sorted by size)
- S32 newIndex = 0;
- for ( newIndex = 0; newIndex < details.size(); ++newIndex )
- {
- if ( newSize > details[newIndex].size )
- break;
- }
- // Add the detail at its new position
- details.insert( newIndex, tmpDetail );
- // Rename the detail so its trailing size value is correct
- {
- S32 tmp;
- String oldName( getName( tmpDetail.nameIndex ) );
- String newName( String::GetTrailingNumber( oldName, tmp ) );
- newName += String::ToString( "%d", newSize );
- renameDetail(oldName, newName);
- }
- if ( newIndex != oldIndex )
- {
- // Fixup details
- for ( S32 iDet = 0; iDet < details.size(); iDet++ )
- {
- if ( details[iDet].subShapeNum < 0 )
- {
- if ( details[iDet].bbDetailLevel == oldIndex )
- details[iDet].bbDetailLevel = newIndex;
- }
- else
- {
- details[iDet].objectDetailNum = iDet;
- }
- }
- // Fixup Billboard details
- _PadMoveAndTrim( billboardDetails, 0, billboardDetails.size(),
- details.size(), oldIndex, newIndex );
- // Now move the mesh for each object in the subshape (adding and removing
- // NULLs as appropriate)
- for ( S32 iObj = 0; iObj < objects.size(); ++iObj )
- {
- TSShape::Object& obj = objects[iObj];
- S32 oldMeshCount = meshes.size();
- _PadMoveAndTrim( meshes, obj.startMeshIndex, obj.numMeshes,
- details.size(), oldIndex, newIndex );
- obj.numMeshes += ( meshes.size() - oldMeshCount );
- // Fixup startMeshIndex for remaining objects
- for ( S32 j = iObj + 1; j < objects.size(); ++j )
- objects[j].startMeshIndex += ( meshes.size() - oldMeshCount );
- }
- }
- // Update smallest visible detail
- updateSmallestVisibleDL();
- // Nothing major, just reint object lists
- initObjects();
- return newIndex;
- }
- bool TSShape::removeDetail( S32 size )
- {
- S32 dl = findDetailBySize( size );
- if ( ( dl < 0 ) || ( dl >= details.size() ) )
- {
- Con::errorf( "TSShape::removeDetail: Invalid detail index (%d)", dl );
- return false;
- }
- // Need to make everything editable since node indexes etc will change
- makeEditable();
- // Destroy and remove each mesh in the detail level
- for ( S32 objIndex = objects.size()-1; objIndex >= 0; objIndex-- )
- {
- TSShape::Object& obj = objects[objIndex];
- if ( dl < obj.numMeshes )
- {
- if ( meshes[obj.startMeshIndex + dl] )
- destructInPlace( meshes[obj.startMeshIndex + dl] );
- removeMeshFromObject(objIndex, dl);
- // Remove the object if there are no meshes left
- if (!obj.numMeshes)
- removeObject( getName( obj.nameIndex ) );
- }
- }
- // Destroy billboard detail level
- if ( dl < billboardDetails.size() )
- {
- if ( billboardDetails[dl] )
- {
- // Delete old textures
- billboardDetails[dl]->deleteImposterCacheTextures();
- delete billboardDetails[dl];
- }
-
- billboardDetails.erase( dl );
- }
- // Update smallest visible detail
- updateSmallestVisibleDL();
- initObjects();
- return true;
- }
- //-----------------------------------------------------------------------------
- bool TSShape::addSequence(const Torque::Path& path, const String& fromSeq,
- const String& name, S32 startFrame, S32 endFrame,
- bool padRotKeys, bool padTransKeys)
- {
- String oldName(fromSeq);
- if (path.getExtension().equal("dsq", String::NoCase))
- {
- S32 oldSeqCount = sequences.size();
- // DSQ source file
- char filenameBuf[1024];
- Con::expandScriptFilename(filenameBuf, sizeof(filenameBuf), path.getFullPath().c_str());
- FileStream *f;
- if((f = FileStream::createAndOpen( filenameBuf, Torque::FS::File::Read )) == NULL)
- {
- Con::errorf("TSShape::addSequence: Could not load DSQ file '%s'", filenameBuf);
- return false;
- }
- if (!importSequences(f, filenameBuf) || (f->getStatus() != Stream::Ok))
- {
- delete f;
- Con::errorf("TSShape::addSequence: Load sequence file '%s' failed", filenameBuf);
- return false;
- }
- delete f;
- // Rename the new sequence if required (avoid rename if name is not
- // unique (this will be fixed up later, and we don't need 2 errors about it!)
- if (oldName.isEmpty())
- oldName = getName(sequences.last().nameIndex);
- if (!oldName.equal(name))
- {
- if (findSequence(name) == -1)
- {
- // Use a dummy intermediate name since we might be renaming from an
- // existing name (and we want to rename the right sequence!)
- sequences.last().nameIndex = addName("__dummy__");
- renameSequence("__dummy__", name);
- }
- }
- // Check that sequences have unique names
- bool lastSequenceRejected = false;
- for (S32 i = sequences.size()-1; i >= oldSeqCount; i--)
- {
- S32 nameIndex = (i == sequences.size()-1) ? findName(name) : sequences[i].nameIndex;
- S32 seqIndex = findSequence(nameIndex);
- if ((seqIndex != -1) && (seqIndex != i))
- {
- Con::errorf("TSShape::addSequence: Failed to add sequence '%s' "
- "(name already exists)", getName(nameIndex).c_str());
- sequences[i].nameIndex = addName("__dummy__");
- removeSequence("__dummy__");
- if (i == sequences.size())
- lastSequenceRejected = true;
- }
- }
- // @todo:Need to remove keyframes if start!=0 and end!=-1
- TSShape::Sequence& seq = sequences.last();
- // Store information about how this sequence was created
- seq.sourceData.from = String::ToString("%s\t%s", filenameBuf, name.c_str());
- seq.sourceData.total = seq.numKeyframes;
- seq.sourceData.start = ((startFrame < 0) || (startFrame >= seq.numKeyframes)) ? 0 : startFrame;
- seq.sourceData.end = ((endFrame < 0) || (endFrame >= seq.numKeyframes)) ? seq.numKeyframes-1 : endFrame;
- return (sequences.size() != oldSeqCount);
- }
- /* Check that sequence to be added does not already exist */
- if (findSequence(name) != -1)
- {
- Con::errorf("TSShape::addSequence: Cannot add sequence '%s' (name already exists)", name.c_str());
- return false;
- }
- Resource<TSShape> hSrcShape;
- TSShape* srcShape = this; // Assume we are copying an existing sequence
- if (path.getExtension().equal("dts", String::NoCase) ||
- path.getExtension().equal("dae", String::NoCase))
- {
- // DTS or DAE source file
- char filenameBuf[1024];
- Con::expandScriptFilename(filenameBuf, sizeof(filenameBuf), path.getFullPath().c_str());
- hSrcShape = ResourceManager::get().load(filenameBuf);
- if (!bool(hSrcShape))
- {
- Con::errorf("TSShape::addSequence: Could not load source shape '%s'", path.getFullPath().c_str());
- return false;
- }
- srcShape = const_cast<TSShape*>((const TSShape*)hSrcShape);
- if (!srcShape->sequences.size())
- {
- Con::errorf("TSShape::addSequence: Source shape '%s' does not contain any sequences", path.getFullPath().c_str());
- return false;
- }
- // If no sequence name is specified, just use the first one
- if (oldName.isEmpty())
- oldName = srcShape->getName(srcShape->sequences[0].nameIndex);
- }
- else
- {
- // Source is an existing sequence
- oldName = path.getFullPath();
- }
- // Find the sequence
- S32 seqIndex = srcShape->findSequence(oldName);
- if (seqIndex < 0)
- {
- Con::errorf("TSShape::addSequence: Could not find sequence named '%s'", oldName.c_str());
- return false;
- }
- // Check keyframe range
- const TSShape::Sequence* srcSeq = &srcShape->sequences[seqIndex];
- if ((startFrame < 0) || (startFrame >= srcSeq->numKeyframes))
- {
- Con::warnf("TSShape::addSequence: Start keyframe (%d) out of range (0-%d) for sequence '%s'",
- startFrame, srcSeq->numKeyframes-1, oldName.c_str());
- startFrame = 0;
- }
- if (endFrame < 0)
- endFrame = srcSeq->numKeyframes - 1;
- else if (endFrame >= srcSeq->numKeyframes)
- {
- Con::warnf("TSShape::addSequence: End keyframe (%d) out of range (0-%d) for sequence '%s'",
- endFrame, srcSeq->numKeyframes-1, oldName.c_str());
- endFrame = srcSeq->numKeyframes - 1;
- }
- // Create array to map source nodes to our nodes
- Vector<S32> nodeMap(srcShape->nodes.size());
- for (S32 i = 0; i < srcShape->nodes.size(); i++)
- nodeMap.push_back(findNode(srcShape->getName(srcShape->nodes[i].nameIndex)));
- // Create array to map source objects to our objects
- Vector<S32> objectMap(srcShape->objects.size());
- for (S32 i = 0; i < srcShape->objects.size(); i++)
- objectMap.push_back(findObject(srcShape->getName(srcShape->objects[i].nameIndex)));
- // Copy the source sequence (need to do it this ugly way instead of just
- // using push_back since srcSeq pointer may change if copying a sequence
- // from inside the shape itself
- sequences.increment();
- TSShape::Sequence& seq = sequences.last();
- srcSeq = &srcShape->sequences[seqIndex]; // update pointer as it may have changed!
- seq = *srcSeq;
- seq.nameIndex = addName(name);
- seq.numKeyframes = endFrame - startFrame + 1;
- if (seq.duration > 0)
- seq.duration *= ((F32)seq.numKeyframes / srcSeq->numKeyframes);
- // Add object states
- // Note: only visibility animation is supported
- seq.frameMatters.clearAll();
- seq.matFrameMatters.clearAll();
- seq.visMatters.clearAll();
- for (S32 i = 0; i < objectMap.size(); i++)
- {
- if (objectMap[i] < 0)
- continue;
- if (srcSeq->visMatters.test(i))
- {
- // Check if visibility is animated within the frames to be copied
- const F32 defaultVis = srcShape->objectStates[i].vis;
- S32 objNum = srcSeq->visMatters.count(i);
- for (S32 iFrame = startFrame; iFrame <= endFrame; iFrame++)
- {
- if (srcShape->getObjectState(*srcSeq, iFrame, objNum).vis != defaultVis)
- {
- seq.visMatters.set(objectMap[i]);
- break;
- }
- }
- }
- }
- TSIntegerSet srcObjectStateSet(srcSeq->frameMatters);
- srcObjectStateSet.overlap(srcSeq->matFrameMatters);
- srcObjectStateSet.overlap(srcSeq->visMatters);
- TSIntegerSet objectStateSet(seq.frameMatters);
- objectStateSet.overlap(seq.matFrameMatters);
- objectStateSet.overlap(seq.visMatters);
- seq.baseObjectState = objectStates.size();
- objectStates.increment(objectStateSet.count()*seq.numKeyframes);
- for (S32 i = 0; i < objectMap.size(); i++)
- {
- if (objectMap[i] < 0)
- continue;
- // Note: only visibility animation is supported
- if (objectStateSet.test(objectMap[i]))
- {
- S32 src = srcSeq->baseObjectState + srcSeq->numKeyframes * srcObjectStateSet.count(i) + startFrame;
- S32 dest = seq.baseObjectState + seq.numKeyframes * objectStateSet.count(objectMap[i]);
- dCopyArray(&objectStates[dest], &srcShape->objectStates[src], seq.numKeyframes);
- }
- }
- // Add ground frames
- F32 ratio = (F32)seq.numKeyframes / srcSeq->numKeyframes;
- S32 groundBase = srcSeq->firstGroundFrame + startFrame*ratio;
- seq.numGroundFrames *= ratio;
- seq.firstGroundFrame = groundTranslations.size();
- groundTranslations.reserve(groundTranslations.size() + seq.numGroundFrames);
- groundRotations.reserve(groundRotations.size() + seq.numGroundFrames);
- for (S32 i = 0; i < seq.numGroundFrames; i++)
- {
- groundTranslations.push_back(srcShape->groundTranslations[groundBase + i]);
- groundRotations.push_back(srcShape->groundRotations[groundBase + i]);
- }
- // Add triggers
- seq.numTriggers = 0;
- seq.firstTrigger = triggers.size();
- F32 seqStartPos = (F32)startFrame / seq.numKeyframes;
- F32 seqEndPos = (F32)endFrame / seq.numKeyframes;
- for (S32 i = 0; i < srcSeq->numTriggers; i++)
- {
- const TSShape::Trigger& srcTrig = srcShape->triggers[srcSeq->firstTrigger + i];
- if ((srcTrig.pos >= seqStartPos) && (srcTrig.pos <= seqEndPos))
- {
- triggers.push_back(srcTrig);
- triggers.last().pos -= seqStartPos;
- seq.numTriggers++;
- }
- }
- // Fixup node matters arrays
- seq.translationMatters.clearAll();
- seq.rotationMatters.clearAll();
- seq.scaleMatters.clearAll();
- for (S32 i = 0; i < nodeMap.size(); i++)
- {
- if (nodeMap[i] < 0)
- continue;
- if (srcSeq->translationMatters.test(i))
- {
- // Check if node position is animated within the frames to be copied
- const Point3F& defaultTrans = srcShape->defaultTranslations[i];
- S32 tranNum = srcSeq->translationMatters.count(i);
- for (S32 iFrame = startFrame; iFrame <= endFrame; iFrame++)
- {
- if (srcShape->getTranslation(*srcSeq, iFrame, tranNum) != defaultTrans)
- {
- seq.translationMatters.set(nodeMap[i]);
- break;
- }
- }
- }
- if (srcSeq->rotationMatters.test(i))
- {
- // Check if node rotation is animated within the frames to be copied
- const QuatF defaultRot = srcShape->defaultRotations[i].getQuatF();
- S32 rotNum = srcSeq->rotationMatters.count(i);
- for (S32 iFrame = startFrame; iFrame <= endFrame; iFrame++)
- {
- QuatF temp;
- if (srcShape->getRotation(*srcSeq, iFrame, rotNum, &temp) != defaultRot)
- {
- seq.rotationMatters.set(nodeMap[i]);
- break;
- }
- }
- }
- if (srcSeq->scaleMatters.test(i))
- {
- S32 scaleNum = srcSeq->scaleMatters.count(i);
- // Check if node scale is animated within the frames to be copied
- if (srcSeq->animatesArbitraryScale())
- {
- TSScale defaultScale;
- defaultScale.identity();
- for (S32 iFrame = startFrame; iFrame <= endFrame; iFrame++)
- {
- TSScale temp;
- if (!(srcShape->getArbitraryScale(*srcSeq, iFrame, scaleNum, &temp) == defaultScale))
- {
- seq.scaleMatters.set(nodeMap[i]);
- break;
- }
- }
- }
- else if (srcSeq->animatesAlignedScale())
- {
- const Point3F defaultScale(Point3F::One);
- for (S32 iFrame = startFrame; iFrame <= endFrame; iFrame++)
- {
- if (srcShape->getAlignedScale(*srcSeq, iFrame, scaleNum) != defaultScale)
- {
- seq.scaleMatters.set(nodeMap[i]);
- break;
- }
- }
- }
- else if (srcSeq->animatesUniformScale())
- {
- const F32 defaultScale = 1.0f;
- for (S32 iFrame = startFrame; iFrame <= endFrame; iFrame++)
- {
- if (srcShape->getUniformScale(*srcSeq, iFrame, scaleNum) != defaultScale)
- {
- seq.scaleMatters.set(nodeMap[i]);
- break;
- }
- }
- }
- }
- }
- // Resize the node transform arrays
- seq.baseTranslation = nodeTranslations.size();
- nodeTranslations.increment(seq.translationMatters.count()*seq.numKeyframes);
- seq.baseRotation = nodeRotations.size();
- nodeRotations.increment(seq.rotationMatters.count()*seq.numKeyframes);
- if (seq.flags & TSShape::ArbitraryScale)
- {
- S32 scaleCount = seq.scaleMatters.count();
- seq.baseScale = nodeArbitraryScaleRots.size();
- nodeArbitraryScaleRots.increment(scaleCount*seq.numKeyframes);
- nodeArbitraryScaleFactors.increment(scaleCount*seq.numKeyframes);
- }
- else if (seq.flags & TSShape::AlignedScale)
- {
- seq.baseScale = nodeAlignedScales.size();
- nodeAlignedScales.increment(seq.scaleMatters.count()*seq.numKeyframes);
- }
- else
- {
- seq.baseScale = nodeUniformScales.size();
- nodeUniformScales.increment(seq.scaleMatters.count()*seq.numKeyframes);
- }
- // Add node transforms (remap from source node indices to our node indices). As
- // well as copying animated node translations and rotations, also handle when the
- // default translation and rotation are different between the source and
- // destination shapes.
- for (S32 i = 0; i < nodeMap.size(); i++)
- {
- if (nodeMap[i] < 0)
- continue;
- if (seq.translationMatters.test(nodeMap[i]))
- {
- S32 src = srcSeq->baseTranslation + srcSeq->numKeyframes * srcSeq->translationMatters.count(i) + startFrame;
- S32 dest = seq.baseTranslation + seq.numKeyframes * seq.translationMatters.count(nodeMap[i]);
- dCopyArray(&nodeTranslations[dest], &srcShape->nodeTranslations[src], seq.numKeyframes);
- }
- else if (padTransKeys && (defaultTranslations[nodeMap[i]] != srcShape->defaultTranslations[i]))
- {
- seq.translationMatters.set(nodeMap[i]);
- S32 dest = seq.baseTranslation + seq.numKeyframes * seq.translationMatters.count(nodeMap[i]);
- for (S32 j = 0; j < seq.numKeyframes; j++)
- nodeTranslations.insert(dest, srcShape->defaultTranslations[i]);
- }
- if (seq.rotationMatters.test(nodeMap[i]))
- {
- S32 src = srcSeq->baseRotation + srcSeq->numKeyframes * srcSeq->rotationMatters.count(i) + startFrame;
- S32 dest = seq.baseRotation + seq.numKeyframes * seq.rotationMatters.count(nodeMap[i]);
- dCopyArray(&nodeRotations[dest], &srcShape->nodeRotations[src], seq.numKeyframes);
- }
- else if (padRotKeys && (defaultRotations[nodeMap[i]] != srcShape->defaultRotations[i]))
- {
- seq.rotationMatters.set(nodeMap[i]);
- S32 dest = seq.baseRotation + seq.numKeyframes * seq.rotationMatters.count(nodeMap[i]);
- for (S32 j = 0; j < seq.numKeyframes; j++)
- nodeRotations.insert(dest, srcShape->defaultRotations[i]);
- }
- if (seq.scaleMatters.test(nodeMap[i]))
- {
- S32 src = srcSeq->baseScale + srcSeq->numKeyframes * srcSeq->scaleMatters.count(i)+ startFrame;
- S32 dest = seq.baseScale + seq.numKeyframes * seq.scaleMatters.count(nodeMap[i]);
- if (seq.flags & TSShape::ArbitraryScale)
- {
- dCopyArray(&nodeArbitraryScaleRots[dest], &srcShape->nodeArbitraryScaleRots[src], seq.numKeyframes);
- dCopyArray(&nodeArbitraryScaleFactors[dest], &srcShape->nodeArbitraryScaleFactors[src], seq.numKeyframes);
- }
- else if (seq.flags & TSShape::AlignedScale)
- dCopyArray(&nodeAlignedScales[dest], &srcShape->nodeAlignedScales[src], seq.numKeyframes);
- else
- dCopyArray(&nodeUniformScales[dest], &srcShape->nodeUniformScales[src], seq.numKeyframes);
- }
- }
- // Set shape flags (only the most significant scale type)
- U32 curVal = mFlags & AnyScale;
- mFlags &= ~(AnyScale);
- mFlags |= getMax(curVal, seq.flags & AnyScale); // take the larger value (can only convert upwards)
- // Set sequence flags
- seq.dirtyFlags = 0;
- if (seq.rotationMatters.testAll() || seq.translationMatters.testAll() || seq.scaleMatters.testAll())
- seq.dirtyFlags |= TSShapeInstance::TransformDirty;
- if (seq.visMatters.testAll())
- seq.dirtyFlags |= TSShapeInstance::VisDirty;
- if (seq.frameMatters.testAll())
- seq.dirtyFlags |= TSShapeInstance::FrameDirty;
- if (seq.matFrameMatters.testAll())
- seq.dirtyFlags |= TSShapeInstance::MatFrameDirty;
- // Store information about how this sequence was created
- seq.sourceData.from = String::ToString("%s\t%s", path.getFullPath().c_str(), oldName.c_str());
- seq.sourceData.total = srcSeq->numKeyframes;
- seq.sourceData.start = startFrame;
- seq.sourceData.end = endFrame;
- return true;
- }
- bool TSShape::removeSequence(const String& name)
- {
- // Find the sequence to be removed
- S32 seqIndex = findSequence(name);
- if (seqIndex < 0)
- {
- Con::errorf("TSShape::removeSequence: Could not find sequence '%s'", name.c_str());
- return false;
- }
- TSShape::Sequence& seq = sequences[seqIndex];
- // Remove the node transforms for this sequence
- S32 transCount = eraseStates(nodeTranslations, seq.translationMatters, seq.baseTranslation, seq.numKeyframes);
- S32 rotCount = eraseStates(nodeRotations, seq.rotationMatters, seq.baseRotation, seq.numKeyframes);
- S32 scaleCount = 0;
- if (seq.flags & TSShape::ArbitraryScale)
- {
- scaleCount = eraseStates(nodeArbitraryScaleRots, seq.scaleMatters, seq.baseScale, seq.numKeyframes);
- eraseStates(nodeArbitraryScaleFactors, seq.scaleMatters, seq.baseScale, seq.numKeyframes);
- }
- else if (seq.flags & TSShape::AlignedScale)
- scaleCount = eraseStates(nodeAlignedScales, seq.scaleMatters, seq.baseScale, seq.numKeyframes);
- else
- scaleCount = eraseStates(nodeUniformScales, seq.scaleMatters, seq.baseScale, seq.numKeyframes);
- // Remove the object states for this sequence
- TSIntegerSet objMatters(seq.frameMatters);
- objMatters.overlap(seq.matFrameMatters);
- objMatters.overlap(seq.visMatters);
- S32 objCount = eraseStates(objectStates, objMatters, seq.baseObjectState, seq.numKeyframes);
- // Remove groundframes and triggers
- TSIntegerSet dummy;
- eraseStates(groundTranslations, dummy, seq.firstGroundFrame, seq.numGroundFrames, 0);
- eraseStates(groundRotations, dummy, seq.firstGroundFrame, seq.numGroundFrames, 0);
- eraseStates(triggers, dummy, seq.firstTrigger, seq.numTriggers, 0);
- // Fixup the base indices of the other sequences
- for (S32 i = seqIndex + 1; i < sequences.size(); i++)
- {
- sequences[i].baseTranslation -= transCount;
- sequences[i].baseRotation -= rotCount;
- sequences[i].baseScale -= scaleCount;
- sequences[i].baseObjectState -= objCount;
- sequences[i].firstGroundFrame -= seq.numGroundFrames;
- sequences[i].firstTrigger -= seq.numTriggers;
- }
- // Remove the sequence itself
- sequences.erase(seqIndex);
- // Remove the sequence name if it is no longer in use
- removeName(name);
- return true;
- }
- //-----------------------------------------------------------------------------
- bool TSShape::addTrigger(const String& seqName, S32 keyframe, S32 state)
- {
- // Find the sequence
- S32 seqIndex = findSequence(seqName);
- if (seqIndex < 0)
- {
- Con::errorf("TSShape::addTrigger: Could not find sequence '%s'", seqName.c_str());
- return false;
- }
- TSShape::Sequence& seq = sequences[seqIndex];
- if (keyframe >= seq.numKeyframes)
- {
- Con::errorf("TSShape::addTrigger: Keyframe out of range (0-%d for sequence '%s')",
- seq.numKeyframes-1, seqName.c_str());
- return false;
- }
- // Encode the trigger state
- if (state < 0)
- state = 1 << (-state-1);
- else if (state > 0)
- state = (1 << (state-1)) | TSShape::Trigger::StateOn;
- // Fixup seq.firstTrigger if this sequence does not have any triggers yet
- if (seq.numTriggers == 0)
- {
- seq.firstTrigger = 0;
- for (S32 i = 0; i < seqIndex; i++)
- seq.firstTrigger += sequences[i].numTriggers;
- }
- // Find where to insert the trigger (sorted by keyframe)
- S32 trigIndex;
- for (trigIndex = seq.firstTrigger; trigIndex < (seq.firstTrigger + seq.numTriggers); trigIndex++)
- {
- const TSShape::Trigger& trig = triggers[trigIndex];
- if ((S32)(trig.pos * seq.numKeyframes) > keyframe)
- break;
- }
- // Create the new trigger
- TSShape::Trigger trig;
- trig.pos = (F32)keyframe / getMax(1, seq.numKeyframes-1);
- trig.state = state;
- triggers.insert(trigIndex, trig);
- seq.numTriggers++;
- // set invert for other triggers if needed
- if ((trig.state & TSShape::Trigger::StateOn) == 0)
- {
- U32 offTrigger = (trig.state & TSShape::Trigger::StateMask);
- for (S32 i = 0; i < seq.numTriggers; i++)
- {
- if (triggers[seq.firstTrigger + i].state & offTrigger)
- triggers[seq.firstTrigger + i].state |= TSShape::Trigger::InvertOnReverse;
- }
- }
- // fixup firstTrigger index for other sequences
- for (S32 i = seqIndex + 1; i < sequences.size(); i++)
- {
- if (sequences[i].numTriggers > 0)
- sequences[i].firstTrigger++;
- }
- // set MakePath flag so triggers will be animated
- seq.flags |= TSShape::MakePath;
- return true;
- }
- bool TSShape::removeTrigger(const String& seqName, S32 keyframe, S32 state)
- {
- // Find the sequence
- S32 seqIndex = findSequence(seqName);
- if (seqIndex < 0)
- {
- Con::errorf("TSShape::removeTrigger: Could not find sequence '%s'", seqName.c_str());
- return false;
- }
- TSShape::Sequence& seq = sequences[seqIndex];
- if (keyframe >= seq.numKeyframes)
- {
- Con::errorf("TSShape::removeTrigger: Keyframe out of range (0-%d for sequence '%s')",
- seq.numKeyframes-1, seqName.c_str());
- return false;
- }
- // Encode the trigger state
- if (state < 0)
- state = 1 << (-state-1);
- else if (state > 0)
- state = (1 << (state-1)) | TSShape::Trigger::StateOn;
- // Find and remove the trigger
- for (S32 trigIndex = seq.firstTrigger; trigIndex < (seq.firstTrigger + seq.numTriggers); trigIndex++)
- {
- TSShape::Trigger& trig = triggers[trigIndex];
- S32 cmpFrame = (S32)(trig.pos * (seq.numKeyframes-1) + 0.5f);
- S32 cmpState = trig.state & (~TSShape::Trigger::InvertOnReverse);
- if ((cmpFrame == keyframe) && (cmpState == state))
- {
- triggers.erase(trigIndex);
- seq.numTriggers--;
- // Fix up firstTrigger for other sequences
- for (S32 i = seqIndex + 1; i < sequences.size(); i++)
- {
- if (sequences[i].numTriggers > 0)
- sequences[i].firstTrigger--;
- }
- // Clear MakePath flag if no more triggers
- if ( seq.numTriggers == 0 )
- seq.flags &= (~TSShape::MakePath);
- return true;
- }
- }
- Con::errorf("TSShape::removeTrigger: Could not find trigger (%d, %d) for sequence '%s'",
- keyframe, state, seqName.c_str());
- return false;
- }
- void TSShape::getNodeKeyframe(S32 nodeIndex, const TSShape::Sequence& seq, S32 keyframe, MatrixF* mat) const
- {
- // Get the node rotation and translation
- QuatF rot;
- if (seq.rotationMatters.test(nodeIndex))
- {
- S32 index = seq.rotationMatters.count(nodeIndex) * seq.numKeyframes + keyframe;
- nodeRotations[seq.baseRotation + index].getQuatF(&rot);
- }
- else
- defaultRotations[nodeIndex].getQuatF(&rot);
- Point3F trans;
- if (seq.translationMatters.test(nodeIndex))
- {
- S32 index = seq.translationMatters.count(nodeIndex) * seq.numKeyframes + keyframe;
- trans = nodeTranslations[seq.baseTranslation + index];
- }
- else
- trans = defaultTranslations[nodeIndex];
- // Set the keyframe matrix
- rot.setMatrix(mat);
- mat->setPosition(trans);
- }
- bool TSShape::setSequenceBlend(const String& seqName, bool blend, const String& blendRefSeqName, S32 blendRefFrame)
- {
- // Find the target sequence
- S32 seqIndex = findSequence(seqName);
- if (seqIndex < 0)
- {
- Con::errorf("TSShape::setSequenceBlend: Could not find sequence named '%s'", seqName.c_str());
- return false;
- }
- TSShape::Sequence& seq = sequences[seqIndex];
- // Ignore if blend flag is already correct
- if (seq.isBlend() == blend)
- return true;
- // Find the sequence containing the reference frame
- S32 blendRefSeqIndex = findSequence(blendRefSeqName);
- if (blendRefSeqIndex < 0)
- {
- Con::errorf("TSShape::setSequenceBlend: Could not find reference sequence named '%s'", blendRefSeqName.c_str());
- return false;
- }
- TSShape::Sequence& blendRefSeq = sequences[blendRefSeqIndex];
- if ((blendRefFrame < 0) || (blendRefFrame >= blendRefSeq.numKeyframes))
- {
- Con::errorf("TSShape::setSequenceBlend: Reference frame out of range (0-%d)", blendRefSeq.numKeyframes-1);
- return false;
- }
- // Set the new flag
- if (blend)
- seq.flags |= TSShape::Blend;
- else
- seq.flags &= (~TSShape::Blend);
- // For each animated node in the target sequence, need to add or subtract the
- // reference keyframe from each frame
- TSIntegerSet nodeMatters(seq.rotationMatters);
- nodeMatters.overlap(seq.translationMatters);
- S32 end = nodeMatters.end();
- for (S32 nodeIndex = nodeMatters.start(); nodeIndex < end; nodeMatters.next(nodeIndex))
- {
- MatrixF refMat;
- getNodeKeyframe(nodeIndex, blendRefSeq, blendRefFrame, &refMat);
- // Add or subtract the reference?
- if (blend)
- refMat.inverse();
- bool updateRot(false), updateTrans(false);
- S32 rotOffset(0), transOffset(0);
- if (seq.rotationMatters.test(nodeIndex))
- {
- updateRot = true;
- rotOffset = seq.baseRotation + seq.rotationMatters.count(nodeIndex) * seq.numKeyframes;
- }
- if (seq.translationMatters.test(nodeIndex))
- {
- updateTrans = true;
- transOffset = seq.baseTranslation + seq.translationMatters.count(nodeIndex) * seq.numKeyframes;
- }
- for (S32 frame = 0; frame < seq.numKeyframes; frame++)
- {
- MatrixF oldMat;
- getNodeKeyframe(nodeIndex, seq, frame, &oldMat);
- MatrixF newMat;
- newMat.mul(refMat, oldMat);
- if (updateRot)
- nodeRotations[rotOffset + frame].set(QuatF(newMat));
- if (updateTrans)
- nodeTranslations[transOffset + frame] = newMat.getPosition();
- }
- }
- // Update sequence blend information
- seq.sourceData.blendSeq = blendRefSeqName;
- seq.sourceData.blendFrame = blendRefFrame;
- return true;
- }
- bool TSShape::setSequenceGroundSpeed(const String& seqName, const Point3F& trans, const Point3F& rot)
- {
- // Find the sequence
- S32 seqIndex = findSequence(seqName);
- if (seqIndex < 0)
- {
- Con::errorf("setSequenceGroundSpeed: Could not find sequence named '%s'", seqName.c_str());
- return false;
- }
- TSShape::Sequence& seq = sequences[seqIndex];
- // Determine how many ground-frames to generate (FPS=10, at least 1 frame)
- const F32 groundFrameRate = 10.0f;
- S32 numFrames = getMax(1, (S32)(seq.duration * groundFrameRate));
- // Allocate space for the frames (add/delete frames as required)
- S32 frameAdjust = numFrames - seq.numGroundFrames;
- for (S32 i = 0; i < mAbs(frameAdjust); i++)
- {
- if (frameAdjust > 0)
- {
- groundTranslations.insert(seq.firstGroundFrame);
- groundRotations.insert(seq.firstGroundFrame);
- }
- else
- {
- groundTranslations.erase(seq.firstGroundFrame);
- groundRotations.erase(seq.firstGroundFrame);
- }
- }
- // Fixup ground frame indices
- seq.numGroundFrames += frameAdjust;
- for (S32 i = seqIndex + 1; i < sequences.size(); i++)
- sequences[i].firstGroundFrame += frameAdjust;
- // Generate the ground-frames
- Point3F adjTrans = trans;
- Point3F adjRot = rot;
- if (seq.numGroundFrames > 0)
- {
- adjTrans /= seq.numGroundFrames;
- adjRot /= seq.numGroundFrames;
- }
- QuatF rotSpeed(adjRot);
- QuatF groundRot(rotSpeed);
- for (S32 i = 0; i < seq.numGroundFrames; i++)
- {
- groundTranslations[seq.firstGroundFrame + i] = adjTrans * (i + 1);
- groundRotations[seq.firstGroundFrame + i].set(groundRot);
- groundRot *= rotSpeed;
- }
- // set MakePath flag so ground frames will be animated
- seq.flags |= TSShape::MakePath;
- return true;
- }
- void TSShape::makeEditable()
- {
- mNeedReinit = true;
- if (mShapeVertexData.base == NULL)
- return;
- for (U32 i = 0; i < meshes.size(); i++)
- {
- if (meshes[i])
- {
- meshes[i]->makeEditable();
- }
- }
- mShapeVertexData.set(NULL, 0);
- }
- bool TSShape::needsReinit()
- {
- return mVertexSize == 0 || mShapeVertexData.base == NULL || mNeedReinit;
- }
|