12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183 |
- //-----------------------------------------------------------------------------
- // 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 (String::compare(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]->mParentMesh == idxToRemove)
- {
- meshes[k]->mParentMesh = -1;
- }
- else if (meshes[k]->mParentMesh > idxToRemove)
- {
- meshes[k]->mParentMesh--;
- }
- }
- 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]->mParentMesh == idxToRemove)
- {
- meshes[k]->mParentMesh = -1;
- }
- else if (meshes[k]->mParentMesh > idxToRemove)
- {
- meshes[k]->mParentMesh--;
- }
- }
- 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->mIndices = srcMesh->mIndices;
- mesh->mPrimitives = srcMesh->mPrimitives;
- 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->mVerts, mesh->mNorms);
- mesh->mEncodedNorms.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->mPrimitives.size(); i++)
- {
- if (!(mesh->mPrimitives[i].matIndex & TSDrawPrimitive::NoMaterial))
- {
- S32 drawType = (mesh->mPrimitives[i].matIndex & (~TSDrawPrimitive::MaterialMask));
- S32 srcMatIndex = mesh->mPrimitives[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->mPrimitives[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 (%s): Could not find sequence named '%s'", path.getFullPath().c_str(), 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 (%s): Start keyframe (%d) out of range (0-%d) for sequence '%s'",
- path.getFullPath().c_str(), 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 (%s): End keyframe (%d) out of range (0-%d) for sequence '%s'",
- path.getFullPath().c_str(), 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(mMin(groundTranslations.size() + seq.numGroundFrames, srcShape->groundTranslations.size()));
- groundRotations.reserve(mMin(groundRotations.size() + seq.numGroundFrames, srcShape->groundRotations.size()));
- for (S32 i = 0; i < seq.numGroundFrames; i++)
- {
- S32 offset = groundBase + i;
- if (offset >= srcShape->groundTranslations.size())
- {
- Con::errorf("%s groundTranslations out of bounds! [%i/%i] ", path.getFullPath().c_str(), groundBase + i, srcShape->groundTranslations.size());
- offset = srcShape->groundTranslations.size() - 1;
- }
- const Point3F pointValueToCopy = srcShape->groundTranslations[offset];
- groundTranslations.push_back(pointValueToCopy);
- S32 offset2 = groundBase + i;
- if (offset2 >= srcShape->groundRotations.size())
- {
- Con::errorf("%s groundRotations out of bounds! [%i/%i] ", path.getFullPath().c_str(), groundBase + i, srcShape->groundRotations.size());
- offset2 = srcShape->groundRotations.size() - 1;
- }
- const Quat16 quatValueToCopy = srcShape->groundRotations[offset2];
- groundRotations.push_back(quatValueToCopy);
- }
- // 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;
- }
|