1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179 |
- //-----------------------------------------------------------------------------
- // 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;
- }
- groundTranslations.push_back(srcShape->groundTranslations[offset]);
- 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;
- }
- groundRotations.push_back(srcShape->groundRotations[offset2]);
- }
- // 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;
- }
|