| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108 | //-----------------------------------------------------------------------------// 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"// Make GCC happy.  Needs to have seen this before processing the// hash table template.struct VertTuple;namespace DictHash{   inline U32 hash( const VertTuple& data );}#include "ts/collada/colladaExtensions.h"#include "ts/collada/colladaAppMesh.h"#include "ts/collada/colladaAppNode.h"#include "ts/collada/colladaAppMaterial.h"#include "core/util/tDictionary.h"#include "core/stringTable.h"using namespace ColladaUtils;bool ColladaAppMesh::fixedSizeEnabled = false;S32 ColladaAppMesh::fixedSize = 2;//-----------------------------------------------------------------------------// Define a VertTuple dictionary to allow fast tuple lookupsnamespace DictHash{   inline U32 hash(const VertTuple& data)   {      return (U32)data.vertex;   }}typedef Map<VertTuple, S32> VertTupleMap;//-----------------------------------------------------------------------------// Find a source with matching ID. Cannot use the DOM .getElement method since// some lame Collada exporters generate <source>s with non-unique IDs.daeElement* findInputSource(const daeElement* input){   // Try using the DOM .getElement method => the resolved element's parent   // should be the input's grandparent   daeElement* parent = ((daeElement*)input)->getParentElement();   daeElement* grandparent = parent ? parent->getParentElement() : 0;   if (!grandparent)      return NULL;   const domURIFragmentType* uri = 0;   if (input->getElementType() == COLLADA_TYPE::INPUTLOCAL)      uri = &daeSafeCast<domInputLocal>((daeElement*)input)->getSource();   else if (input->getElementType() == COLLADA_TYPE::INPUTLOCALOFFSET)      uri = &daeSafeCast<domInputLocalOffset>((daeElement*)input)->getSource();   if (!uri)      return NULL;   daeElement* element = uri->getElement();   if (element && element->getParentElement() == grandparent)      return element;   else   {      // Probably a non-unique ID => search for the matching element manually      // Skip the leading '#' on source IDs      const char* id = uri->originalStr().c_str();      if (id && (id[0] == '#'))         id++;      for (S32 iChild = 0; iChild < grandparent->getChildren().getCount(); iChild++)      {         element = grandparent->getChildren()[iChild];         if ((element->getElementType() != COLLADA_TYPE::SOURCE) &&             (element->getElementType() != COLLADA_TYPE::VERTICES))             continue;         if (dStrEqual(id, element->getAttribute("id").c_str()))            return element;      }   }   return NULL;}//-----------------------------------------------------------------------------// Collada scatters the data required for geometry all over the place; this class// helps to group it all together.class MeshStreams{public:   // The sources we want to read from the mesh stream. Can be any order, but   // sources of the same type (eg. UVs and UV2s) must be sequential (to allow   // ordering by set index)   enum eSourceType {      Points,      Normals,      Colors,      UVs,      UV2s,      Joints,      Weights,      InvBindMatrices,      NumStreams   };   const char* SourceTypeToSemantic(eSourceType type)   {      switch ( type )      {         case Points:            return "POSITION";         case Normals:           return "NORMAL";         case Colors:            return "COLOR";         case UVs:         case UV2s:              return "TEXCOORD";         case Joints:            return "JOINT";         case Weights:           return "WEIGHT";         case InvBindMatrices:   return "INV_BIND_MATRIX";         default:                return "";      }   }private:   /// Classify a single input   template<class T>   static void selectInput(T input, T sortedInputs[], S32 start, S32 end=-1)   {      if (end == -1)         end = start;      // Get the set for this input      const domInputLocalOffset* localOffset = daeSafeCast<domInputLocalOffset>(input);      domUint newSet = localOffset ? localOffset->getSet() : 0;      // Add the input to the right place in the list (somewhere between start and end)      for (S32 i = start; i <= end; i++) {         const domInputLocalOffset* localOffset = daeSafeCast<domInputLocalOffset>(sortedInputs[i]);         domUint set = localOffset ? localOffset->getSet() : 0xFFFFFFFF;         if (newSet < set) {            for (S32 j = i + 1; j <= end; j++)               sortedInputs[j] = sortedInputs[j-1];            sortedInputs[i] = input;            return;         }      }   }   /// Attempt to initialise a _SourceReader   template<class T>   bool initSourceReader(T input, eSourceType type, _SourceReader& reader, const char* params[])   {      if (!input)         return false;      // Try to get the source element      const domSource* source = 0;      daeElement *element = findInputSource(input);      if (element->getElementType() == COLLADA_TYPE::SOURCE)         source = daeSafeCast<domSource>(element);      else if (element->getElementType() == COLLADA_TYPE::VERTICES) {         const domVertices* vertices = daeSafeCast<domVertices>(element);         // Search for the input with the desired semantic         const char* semantic = SourceTypeToSemantic( type );         for (S32 iInput = 0; iInput < vertices->getInput_array().getCount(); iInput++)         {            domInputLocal* input = vertices->getInput_array().get(iInput);            if (dStrEqual(input->getSemantic(), semantic))            {               source = daeSafeCast<domSource>(findInputSource(input));               break;            }         }      }      if (!source)         return false;      return reader.initFromSource(source, params);   }public:   _SourceReader     points;   _SourceReader     normals;   _SourceReader     colors;   _SourceReader     uvs;   _SourceReader     uv2s;   _SourceReader     joints;   _SourceReader     weights;   _SourceReader     invBindMatrices;   /// Clear the mesh streams   void reset()   {      points.reset();      normals.reset();      colors.reset();      uvs.reset();      uv2s.reset();      joints.reset();      weights.reset();      invBindMatrices.reset();   }   /// Classify a set of inputs by type and set number (needs to be a template   /// because Collada has two forms of input arrays that may be accessed in   /// an identical fashion, but the classes are unrelated. Sigh.   template<class T>   static void classifyInputs(const daeTArray<T>& inputs, T sortedInputs[], U32 *maxOffset=0)   {      if (maxOffset)         *maxOffset = 0;      // Clear output array      for (S32 i = 0; i < NumStreams; i++)         sortedInputs[i] = 0;      // Separate inputs by type, and sort by set (ie. lowest TEXCOORD set becomes UV,      // next TEXCOORD set becomes UV2 etc)      for (S32 iInput = 0; iInput < inputs.getCount(); iInput++) {         const T& input = inputs[iInput];         const daeString semantic = input->getSemantic();         if (dStrEqual(semantic, "VERTEX"))         {            domVertices* vertices = daeSafeCast<domVertices>(findInputSource(input));            // The <vertices> element may contain multiple inputs (eg. POSITION, NORMAL etc)            domInputLocalRef verticesInputs[NumStreams];            classifyInputs(vertices->getInput_array(), verticesInputs);            for (S32 iStream = 0; iStream < NumStreams; iStream++)            {               if (verticesInputs[iStream] != 0)                  sortedInputs[iStream] = input;            }         }         else if (dStrEqual(semantic, "POSITION"))          selectInput(input, sortedInputs, Points);         else if (dStrEqual(semantic, "NORMAL"))            selectInput(input, sortedInputs, Normals);         else if (dStrEqual(semantic, "COLOR"))             selectInput(input, sortedInputs, Colors);         else if (dStrEqual(semantic, "TEXCOORD"))          selectInput(input, sortedInputs, UVs, UV2s);         else if (dStrEqual(semantic, "JOINT"))             selectInput(input, sortedInputs, Joints);         else if (dStrEqual(semantic, "WEIGHT"))            selectInput(input, sortedInputs, Weights);         else if (dStrEqual(semantic, "INV_BIND_MATRIX"))   selectInput(input, sortedInputs, InvBindMatrices);         if (maxOffset)         {            const domInputLocalOffset* localOffset = daeSafeCast<domInputLocalOffset>(input);            domUint offset = localOffset ? localOffset->getOffset() : 0;            if (offset > (*maxOffset))               *maxOffset = offset;         }      }   }   /// Read a set of inputs into the named sources. There may be multiple 'sets'   /// of COLOR or TEXCOORD (uvs) streams, but we are only interested in the   /// first COLOR set (ie. smallest set value), and the first 2 TEXCOORDS sets.   template<class T>   bool readInputs(const daeTArray<T>& inputs)   {      // Sort inputs by type and set to find the ones we are interested in      T sortedInputs[NumStreams];      classifyInputs(inputs, sortedInputs);      // Attempt to initialise the SourceReaders      const char* vertex_params[] = { "X", "Y", "Z", "" };      initSourceReader(sortedInputs[Points], Points, points, vertex_params);      const char* normal_params[] = { "X", "Y", "Z", "" };      initSourceReader(sortedInputs[Normals], Normals, normals, normal_params);      const char* color_params[] = { "R", "G", "B", "A", "" };      initSourceReader(sortedInputs[Colors], Colors, colors, color_params);      const char* uv_params[] = { "S", "T", "" };      const char* uv_params2[] = { "U", "V", "" }; // some files use the nonstandard U,V or X,Y param names      const char* uv_params3[] = { "X", "Y", "" };      if (!initSourceReader(sortedInputs[UVs], UVs, uvs, uv_params))         if (!initSourceReader(sortedInputs[UVs], UVs, uvs, uv_params2))            initSourceReader(sortedInputs[UVs], UVs, uvs, uv_params3);      if (!initSourceReader(sortedInputs[UV2s], UV2s, uv2s, uv_params))         if (!initSourceReader(sortedInputs[UV2s], UV2s, uv2s, uv_params2))            initSourceReader(sortedInputs[UV2s], UV2s, uv2s, uv_params3);      const char* joint_params[] = { "JOINT", "" };      initSourceReader(sortedInputs[Joints], Joints, joints, joint_params);      const char* weight_params[] = { "WEIGHT", "" };      initSourceReader(sortedInputs[Weights], Weights, weights, weight_params);      const char* matrix_params[] = { "TRANSFORM", "" };      initSourceReader(sortedInputs[InvBindMatrices], InvBindMatrices, invBindMatrices, matrix_params);      return true;   }};//------------------------------------------------------------------------------ColladaAppMesh::ColladaAppMesh(const domInstance_geometry* instance, ColladaAppNode* node)   : appNode(node),instanceGeom(instance), instanceCtrl(0),  geomExt(0){   flags = 0;   numFrames = 0;   numMatFrames = 0;}ColladaAppMesh::ColladaAppMesh(const domInstance_controller* instance, ColladaAppNode* node)   : appNode(node),instanceGeom(0), instanceCtrl(instance),  geomExt(0){   flags = 0;   numFrames = 0;   numMatFrames = 0;}const char* ColladaAppMesh::getName(bool allowFixed){   // Some exporters add a 'PIVOT' or unnamed node between the mesh and the   // actual object node. Detect this and return the object node name instead   // of the pivot node.   const char* nodeName = appNode->getName();   if ( dStrEqual(nodeName, "null") || dStrEndsWith(nodeName, "PIVOT") )      nodeName = appNode->getParentName();   // If all geometry is being fixed to the same size, append the size   // to the name   return allowFixed && fixedSizeEnabled ? avar("%s %d", nodeName, fixedSize) : nodeName;}MatrixF ColladaAppMesh::getMeshTransform(F32 time){   return appNode->getNodeTransform(time);}bool ColladaAppMesh::animatesVis(const AppSequence* appSeq){   #define IS_VIS_ANIMATED(node)    \      (dynamic_cast<const ColladaAppNode*>(node)->nodeExt->visibility.isAnimated(appSeq->getStart(), appSeq->getEnd()))   // Check if the node visibility is animated within the sequence interval   return IS_VIS_ANIMATED(appNode) || (appNode->appParent ? IS_VIS_ANIMATED(appNode->appParent) : false);}bool ColladaAppMesh::animatesMatFrame(const AppSequence* appSeq){   // Texture coordinates may be animated in two ways:   // - by animating the MAYA profile texture transform (diffuse texture)   // - by animating the morph weights for morph targets with different UVs   // Check if the MAYA profile texture transform is animated   for (S32 iMat = 0; iMat < appMaterials.size(); iMat++) {      ColladaAppMaterial* appMat = static_cast<ColladaAppMaterial*>(appMaterials[iMat]);      if (appMat->effectExt &&          appMat->effectExt->animatesTextureTransform(appSeq->getStart(), appSeq->getEnd()))         return true;   }   // Check that the morph weights are animated within the sequence interval,    // and that the morph targets have different UVs to the base geometry.   bool animated = false;   bool differentUVs = false;   if (const domMorph* morph = getMorph()) {      for (S32 iInput = 0; iInput < morph->getTargets()->getInput_array().getCount(); iInput++) {         const domInputLocal* input = morph->getTargets()->getInput_array()[iInput];         if (dStrEqual(input->getSemantic(), "MORPH_TARGET")) {            // @todo: Check if morph targets have different UVs to base geometry            differentUVs = false;         }         if (dStrEqual(input->getSemantic(), "MORPH_WEIGHT")) {            const domSource* source = daeSafeCast<domSource>(findInputSource(input));            AnimatedFloatList weights(source ? source->getFloat_array() : 0);            animated = weights.isAnimated(appSeq->getStart(), appSeq->getEnd());         }      }   }   return (animated && differentUVs);}bool ColladaAppMesh::animatesFrame(const AppSequence* appSeq){   // Collada <morph>s ALWAYS contain vert positions, so just need to check if   // the morph weights are animated within the sequence interval   bool animated = false;   if (const domMorph* morph = getMorph()) {      for (S32 iInput = 0; iInput < morph->getTargets()->getInput_array().getCount(); iInput++) {         const domInputLocal* input = morph->getTargets()->getInput_array()[iInput];         if (dStrEqual(input->getSemantic(), "MORPH_WEIGHT")) {            const domSource* source = daeSafeCast<domSource>(findInputSource(input));            AnimatedFloatList weights(source ? source->getFloat_array() : 0);            animated = weights.isAnimated(appSeq->getStart(), appSeq->getEnd());            break;         }      }   }   return animated;}F32 ColladaAppMesh::getVisValue(F32 t){   #define GET_VIS(node)   \      (dynamic_cast<const ColladaAppNode*>(node)->nodeExt->visibility.getValue(t))   // Get the visibility of the mesh's node at time, 't'   return GET_VIS(appNode) * (appNode->appParent ? GET_VIS(appNode->appParent) : 1.0f);}S32 ColladaAppMesh::addMaterial(const char* symbol){   if (!symbol)      return TSDrawPrimitive::NoMaterial;   // Lookup the symbol in the materials already bound to this geometry/controller   // instance   Map<StringTableEntry,U32>::Iterator itr = boundMaterials.find(symbol);   if (itr != boundMaterials.end())      return itr->value;   // Find the Collada material that this symbol maps to   U32 matIndex = TSDrawPrimitive::NoMaterial;   const domBind_material* binds = instanceGeom ? instanceGeom->getBind_material() :                                                  instanceCtrl->getBind_material();   if (binds) {      const domInstance_material_Array& matArray = binds->getTechnique_common()->getInstance_material_array();      for (S32 iBind = 0; iBind < matArray.getCount(); iBind++) {         if (dStrEqual(matArray[iBind]->getSymbol(), symbol)) {            // Find the index of the bound material in the shape global list            const domMaterial* mat = daeSafeCast<domMaterial>(matArray[iBind]->getTarget().getElement());            for (matIndex = 0; matIndex < appMaterials.size(); matIndex++) {               if (static_cast<ColladaAppMaterial*>(appMaterials[matIndex])->mat == mat)                  break;            }            // Check if this material needs to be added to the shape global list            if (matIndex == appMaterials.size()) {               if (mat)                  appMaterials.push_back(new ColladaAppMaterial(mat));               else                  appMaterials.push_back(new ColladaAppMaterial(symbol));            }            break;         }      }   }   else   {      // No Collada material is present for this symbol, so just create an empty one      appMaterials.push_back(new ColladaAppMaterial(symbol));   }   // Add this symbol to the bound list for the mesh   boundMaterials.insert(StringTable->insert(symbol), matIndex);   return matIndex;}void ColladaAppMesh::getPrimitives(const domGeometry* geometry){   // Only do this once   if (primitives.size())      return;   // Read the <geometry> extension   if (!geomExt)      geomExt = new ColladaExtension_geometry(geometry);   // Get the supported primitive elements for this geometry, and warn   // about unsupported elements   Vector<BasePrimitive*> meshPrims;   const daeElementRefArray& contents = geometry->getMesh()->getContents();   for (S32 iElem = 0; iElem < contents.getCount(); iElem++) {      if (BasePrimitive::isPrimitive(contents[iElem])) {         if (BasePrimitive::isSupportedPrimitive(contents[iElem]))            meshPrims.push_back(BasePrimitive::get(contents[iElem]));         else {            daeErrorHandler::get()->handleWarning(avar("Collada <%s> element "               "in %s is not supported.", contents[iElem]->getElementName(),               _GetNameOrId(geometry)));         }      }   }   MeshStreams streams;   VertTupleMap tupleMap;   // Create Torque primitives   for (S32 iPrim = 0; iPrim < meshPrims.size(); iPrim++) {      // Primitive element must have at least 1 triangle      const domListOfUInts* pTriData = meshPrims[iPrim]->getTriangleData();      if (!pTriData)         continue;      U32 numTriangles = pTriData->getCount() / meshPrims[iPrim]->getStride() / 3;      if (!numTriangles)         continue;      // Create TSMesh primitive      primitives.increment();      TSDrawPrimitive& primitive = primitives.last();      primitive.start = indices.size();      primitive.matIndex = (TSDrawPrimitive::Triangles | TSDrawPrimitive::Indexed) |                           addMaterial(meshPrims[iPrim]->getMaterial());      // Get the AppMaterial associated with this primitive      ColladaAppMaterial* appMat = 0;      if (!(primitive.matIndex & TSDrawPrimitive::NoMaterial))         appMat = static_cast<ColladaAppMaterial*>(appMaterials[primitive.matIndex & TSDrawPrimitive::MaterialMask]);      // Force the material to be double-sided if this geometry is double-sided.      if (geomExt->double_sided && appMat && appMat->effectExt)         appMat->effectExt->double_sided = true;      // Pre-allocate triangle indices      primitive.numElements = numTriangles * 3;      indices.setSize(indices.size() + primitive.numElements);      U32* dstIndex = indices.end() - primitive.numElements;      // Determine the offset for each element type in the stream, and also the      // maximum input offset, which will be the number of indices per vertex we      // need to skip.      domInputLocalOffsetRef sortedInputs[MeshStreams::NumStreams];      MeshStreams::classifyInputs(meshPrims[iPrim]->getInputs(), sortedInputs);      S32 offsets[MeshStreams::NumStreams];      for (S32 i = 0; i < MeshStreams::NumStreams; i++)         offsets[i] = sortedInputs[i] ? sortedInputs[i]->getOffset() : -1;      // Loop through indices      const domUint* pSrcData = &(pTriData->get(0));      for (U32 iTri = 0; iTri < numTriangles; iTri++) {         // If the next triangle could cause us to index across a 16-bit         // boundary, split this primitive and clear the tuple map to         // ensure primitives only index verts within a 16-bit range.         if (vertTuples.size() &&            (((vertTuples.size()-1) ^ (vertTuples.size()+2)) & 0x10000))         {            // Pad vertTuples up to the next 16-bit boundary            while (vertTuples.size() & 0xFFFF)               vertTuples.push_back(VertTuple(vertTuples.last()));            // Split the primitive at the current triangle            S32 indicesRemaining = (numTriangles - iTri) * 3;            if (iTri > 0)            {               daeErrorHandler::get()->handleWarning(avar("Splitting primitive "                  "in %s: too many verts for 16-bit indices.", _GetNameOrId(geometry)));               primitives.last().numElements -= indicesRemaining;               primitives.push_back(TSDrawPrimitive(primitives.last()));            }            primitives.last().numElements = indicesRemaining;            primitives.last().start = indices.size() - indicesRemaining;            tupleMap.clear();         }         streams.reset();         streams.readInputs(meshPrims[iPrim]->getInputs());         for (U32 v = 0; v < 3; v++) {            // Collect vert tuples into a single array so we can easily grab            // vertex data later.            VertTuple tuple;            tuple.prim = iPrim;            tuple.vertex = offsets[MeshStreams::Points]  >= 0 ? pSrcData[offsets[MeshStreams::Points]] : -1;            tuple.normal = offsets[MeshStreams::Normals] >= 0 ? pSrcData[offsets[MeshStreams::Normals]] : -1;            tuple.color  = offsets[MeshStreams::Colors]  >= 0 ? pSrcData[offsets[MeshStreams::Colors]] : -1;            tuple.uv     = offsets[MeshStreams::UVs]     >= 0 ? pSrcData[offsets[MeshStreams::UVs]] : -1;            tuple.uv2    = offsets[MeshStreams::UV2s]    >= 0 ? pSrcData[offsets[MeshStreams::UV2s]] : -1;            tuple.dataVertex = tuple.vertex > -1 ? streams.points.getPoint3FValue(tuple.vertex) : Point3F::Max;            tuple.dataNormal = tuple.normal > -1 ? streams.normals.getPoint3FValue(tuple.normal) : Point3F::Max;            tuple.dataColor  = tuple.color > -1  ? streams.colors.getColorIValue(tuple.color) : ColorI(0,0,0);            tuple.dataUV     = tuple.uv > -1     ? streams.uvs.getPoint2FValue(tuple.uv) : Point2F::Max;            tuple.dataUV2    = tuple.uv2 > -1    ? streams.uv2s.getPoint2FValue(tuple.uv2) : Point2F::Max;            VertTupleMap::Iterator itr = tupleMap.find(tuple);            if (itr == tupleMap.end())            {               itr = tupleMap.insert(tuple, vertTuples.size());               vertTuples.push_back(tuple);            }            // Collada uses CCW for front face and Torque uses the opposite, so            // for normal (non-inverted) meshes, the indices are flipped.            if (appNode->invertMeshes)               dstIndex[v] = itr->value;            else               dstIndex[2 - v] = itr->value;            pSrcData += meshPrims[iPrim]->getStride();         }         dstIndex += 3;      }   }   for (S32 iPrim = 0; iPrim < meshPrims.size(); iPrim++)      delete meshPrims[iPrim];}void ColladaAppMesh::getVertexData(const domGeometry* geometry, F32 time, const MatrixF& objOffset,                                   Vector<Point3F>& v_points,                                    Vector<Point3F>& v_norms,                                    Vector<ColorI>& v_colors,                                   Vector<Point2F>& v_uvs,                                   Vector<Point2F>& v_uv2s,                                   bool appendValues){   if (!primitives.size())      return;   MeshStreams streams;   S32 lastPrimitive = -1;   ColladaAppMaterial* appMat = 0;   // Get the supported primitive elements for this geometry   Vector<BasePrimitive*> meshPrims;   const daeElementRefArray& contents = geometry->getMesh()->getContents();   for (S32 iElem = 0; iElem < contents.getCount(); iElem++) {      if (BasePrimitive::isSupportedPrimitive(contents[iElem]))         meshPrims.push_back(BasePrimitive::get(contents[iElem]));   }   // If appending values, pre-allocate the arrays   if (appendValues) {      v_points.setSize(v_points.size() + vertTuples.size());      v_uvs.setSize(v_uvs.size() + vertTuples.size());   }   // Get pointers to arrays   Point3F* points_array = &v_points[v_points.size() - vertTuples.size()];   Point2F* uvs_array = &v_uvs[v_uvs.size() - vertTuples.size()];   Point3F* norms_array = NULL;   ColorI*  colors_array = NULL;   Point2F* uv2s_array = NULL;   for (S32 iVert = 0; iVert < vertTuples.size(); iVert++) {      const VertTuple& tuple = vertTuples[iVert];      // Change primitives?      if (tuple.prim != lastPrimitive) {         if (meshPrims.size() <= tuple.prim) {            daeErrorHandler::get()->handleError(avar("Failed to get vertex data "               "for %s. Primitives do not match base geometry.", geometry->getID()));            break;         }         // Update vertex/normal/UV streams and get the new material index         streams.reset();         streams.readInputs(meshPrims[tuple.prim]->getInputs());         S32 matIndex = addMaterial(meshPrims[tuple.prim]->getMaterial());         if (matIndex != TSDrawPrimitive::NoMaterial)            appMat = static_cast<ColladaAppMaterial*>(appMaterials[matIndex]);         else            appMat = 0;         lastPrimitive = tuple.prim;      }      // If we are NOT appending values, only set the value if it actually exists      // in the mesh data stream.      if (appendValues || ((tuple.vertex >= 0) && (tuple.vertex < streams.points.size()))) {         points_array[iVert] = streams.points.getPoint3FValue(tuple.vertex);         // Flip verts for inverted meshes         if (appNode->invertMeshes)            points_array[iVert].z = -points_array[iVert].z;         objOffset.mulP(points_array[iVert]);      }      if (appendValues || ((tuple.uv >= 0) && (tuple.uv < streams.uvs.size()))) {         uvs_array[iVert] = streams.uvs.getPoint2FValue(tuple.uv);         if (appMat && appMat->effectExt)            appMat->effectExt->applyTextureTransform(uvs_array[iVert], time);         uvs_array[iVert].y = 1.0f - uvs_array[iVert].y;   // Collada texcoords are upside down compared to TGE      }      // The rest is non-required data... if it doesn't exist then don't append it.      if ( (tuple.normal >= 0) && (tuple.normal < streams.normals.size()) ) {         if ( !norms_array && iVert == 0 )         {            v_norms.setSize(v_norms.size() + vertTuples.size());            norms_array = &v_norms[v_norms.size() - vertTuples.size()];         }         if ( norms_array ) {            norms_array[iVert] = streams.normals.getPoint3FValue(tuple.normal);            // Flip normals for inverted meshes            if (appNode->invertMeshes)               norms_array[iVert].z = -norms_array[iVert].z;         }      }      if ( (tuple.color >= 0) && (tuple.color < streams.colors.size()))       {         if ( !colors_array && iVert == 0 )         {            v_colors.setSize(v_colors.size() + vertTuples.size());            colors_array = &v_colors[v_colors.size() - vertTuples.size()];         }         if ( colors_array )            colors_array[iVert] = streams.colors.getColorIValue(tuple.color);      }      if ( (tuple.uv2 >= 0) && (tuple.uv2 < streams.uv2s.size()) )       {         if ( !uv2s_array && iVert == 0 )         {            v_uv2s.setSize(v_uv2s.size() + vertTuples.size());            uv2s_array = &v_uv2s[v_uv2s.size() - vertTuples.size()];         }         if ( uv2s_array )         {            uv2s_array[iVert] = streams.uv2s.getPoint2FValue(tuple.uv2);            if (appMat && appMat->effectExt)               appMat->effectExt->applyTextureTransform(uv2s_array[iVert], time);            uv2s_array[iVert].y = 1.0f - uv2s_array[iVert].y;   // Collada texcoords are upside down compared to TGE         }      }   }   for (S32 iPrim = 0; iPrim < meshPrims.size(); iPrim++)      delete meshPrims[iPrim];}void ColladaAppMesh::getMorphVertexData(const domMorph* morph, F32 time, const MatrixF& objOffset,                                        Vector<Point3F>& v_points,                                         Vector<Point3F>& v_norms,                                         Vector<ColorI>& v_colors,                                         Vector<Point2F>& v_uvs,                                        Vector<Point2F>& v_uv2s){   // @todo: Could the base geometry (or any target geometry) also be a morph?   // Get the target geometries and weights (could be animated)   Vector<const domGeometry*> targetGeoms;   domListOfFloats targetWeights;   for (S32 iInput = 0; iInput < morph->getTargets()->getInput_array().getCount(); iInput++) {      const domInputLocal* input = morph->getTargets()->getInput_array()[iInput];      const domSource* source = daeSafeCast<domSource>(findInputSource(input));      if (dStrEqual(input->getSemantic(), "MORPH_TARGET")) {         //  Get the morph targets         _SourceReader srcTargets;         srcTargets.initFromSource(source);         for (S32 iTarget = 0; iTarget < srcTargets.size(); iTarget++) {            // Lookup the element and add to the targets list            daeIDRef idref(srcTargets.getStringValue(iTarget));            idref.setContainer(morph->getDocument()->getDomRoot());            targetGeoms.push_back(daeSafeCast<domGeometry>(idref.getElement()));         }      }      else if (dStrEqual(input->getSemantic(), "MORPH_WEIGHT")) {         //  Get the (possibly animated) morph weight         targetWeights = AnimatedFloatList(source->getFloat_array()).getValue(time);      }   }   // Check that we have a weight for each target   if (targetGeoms.size() != targetWeights.getCount())   {      domController* ctrl = daeSafeCast<domController>(const_cast<domMorph*>(morph)->getParent());      Con::warnf("Mismatched morph targets and weights in %s.", _GetNameOrId(ctrl));      // Set unused targets to zero weighting (unused weights are ignored)      while (targetGeoms.size() > targetWeights.getCount())         targetWeights.append(0.0f);   }   // Get the base geometry and vertex data   const domGeometry* baseGeometry = daeSafeCast<domGeometry>(morph->getSource().getElement());   if (!baseGeometry)      return;   getPrimitives(baseGeometry);   getVertexData(baseGeometry, time, objOffset, v_points, v_norms, v_colors, v_uvs, v_uv2s, true);   // Get pointers to the arrays of base geometry data   Point3F* points_array = &v_points[v_points.size() - vertTuples.size()];   Point3F* norms_array = &v_norms[v_norms.size() - vertTuples.size()];   Point2F* uvs_array = &v_uvs[v_uvs.size() - vertTuples.size()];   ColorI* colors_array = v_colors.size() ? &v_colors[v_colors.size() - vertTuples.size()] : 0;   Point2F* uv2s_array = v_uv2s.size() ? &v_uv2s[v_uv2s.size() - vertTuples.size()] : 0;   // Normalize base vertex data?   if (morph->getMethod() == MORPHMETHODTYPE_NORMALIZED) {      F32 weightSum = 0.0f;      for (S32 iWeight = 0; iWeight < targetWeights.getCount(); iWeight++) {         weightSum += targetWeights[iWeight];      }      // Result = Base*(1.0-w1-w2 ... -wN) + w1*Target1 + w2*Target2 ... + wN*TargetN      weightSum = mClampF(1.0f - weightSum, 0.0f, 1.0f);      for (S32 iVert = 0; iVert < vertTuples.size(); iVert++) {         points_array[iVert] *= weightSum;         norms_array[iVert] *= weightSum;         uvs_array[iVert] *= weightSum;      }      if (uv2s_array) {         for (S32 iVert = 0; iVert < vertTuples.size(); iVert++)            uv2s_array[iVert] *= weightSum;      }   }   // Interpolate using the target geometry and weights   for (S32 iTarget = 0; iTarget < targetGeoms.size(); iTarget++) {      // Ignore empty weights      if (targetWeights[iTarget] == 0.0f)         continue;      // Get target geometry data into temporary arrays      Vector<Point3F> targetPoints;      Vector<Point3F> targetNorms;      Vector<Point2F> targetUvs;      Vector<ColorI>  targetColors;      Vector<Point2F> targetUv2s;      // Copy base geometry into target geometry (will be used if target does      // not define normals or uvs)      targetPoints.set(points_array, vertTuples.size());      targetNorms.set(norms_array, vertTuples.size());      targetUvs.set(uvs_array, vertTuples.size());      if (colors_array)         targetColors.set(colors_array, vertTuples.size());      if (uv2s_array)         targetUv2s.set(uv2s_array, vertTuples.size());      getVertexData(targetGeoms[iTarget], time, objOffset, targetPoints, targetNorms, targetColors, targetUvs, targetUv2s, false);      // Combine with base geometry      for (S32 iVert = 0; iVert < vertTuples.size(); iVert++) {         points_array[iVert] += targetPoints[iVert] * targetWeights[iTarget];         norms_array[iVert] += targetNorms[iVert] * targetWeights[iTarget];         uvs_array[iVert] += targetUvs[iVert] * targetWeights[iTarget];      }      if (uv2s_array) {         for (S32 iVert = 0; iVert < vertTuples.size(); iVert++)            uv2s_array[iVert] += targetUv2s[iVert] * targetWeights[iTarget];      }      if (colors_array) {         for (S32 iVert = 0; iVert < vertTuples.size(); iVert++)         {            LinearColorF tCol = colors_array[iVert];            tCol += LinearColorF(targetColors[iVert]) * (F32)targetWeights[iTarget];            colors_array[iVert] = tCol.toColorI();         }      }   }}void ColladaAppMesh::lockMesh(F32 t, const MatrixF& objOffset){   // Find the geometry element for this mesh. Could be one of 3 things:   // 1) a simple static mesh (Collada <geometry> element)   // 2) a simple morph (some combination of static meshes)   // 3) a skin (skin geometry could also be a morph!)   daeElement* geometry = 0;   if (instanceGeom) {      // Simple, static mesh      geometry = instanceGeom->getUrl().getElement();   }   else if (instanceCtrl) {      const domController* ctrl = daeSafeCast<domController>(instanceCtrl->getUrl().getElement());      if (!ctrl) {         daeErrorHandler::get()->handleWarning(avar("Failed to find <controller> "            "element for %s", getName()));         return;      }      else if (ctrl->getMorph()) {         // Morph controller         geometry = ctrl->getMorph();      }      else {         // Skinned mesh: source geometry could be static geometry or a morph controller.         geometry = ctrl->getSkin()->getSource().getElement();         if (geometry && geometry->getElementType() == COLLADA_TYPE::CONTROLLER)            geometry = daeSafeCast<domController>(geometry)->getMorph();      }   }   if (!geometry) {      daeErrorHandler::get()->handleWarning(avar("Failed to find source geometry "         "for %s", getName()));      return;   }   // Now get the vertex data at the specified time   if (geometry->getElementType() == COLLADA_TYPE::GEOMETRY) {      getPrimitives(daeSafeCast<domGeometry>(geometry));      getVertexData(daeSafeCast<domGeometry>(geometry), t, objOffset, points, normals, colors, uvs, uv2s, true);   }   else if (geometry->getElementType() == COLLADA_TYPE::MORPH) {      getMorphVertexData(daeSafeCast<domMorph>(geometry), t, objOffset, points, normals, colors, uvs, uv2s);   }   else {      daeErrorHandler::get()->handleWarning(avar("Unsupported geometry type "         "'<%s>' for %s", geometry->getElementName(), getName()));   }}void ColladaAppMesh::lookupSkinData(){   // Only lookup skin data once   if (!isSkin() || weight.size())      return;   // Get the skin and vertex weight data   const domSkin* skin = daeSafeCast<domController>(instanceCtrl->getUrl().getElement())->getSkin();   const domSkin::domVertex_weights& weightIndices = *(skin->getVertex_weights());   const domListOfInts& weights_v = weightIndices.getV()->getValue();   const domListOfUInts& weights_vcount = weightIndices.getVcount()->getValue();   MeshStreams streams;   streams.readInputs(skin->getJoints()->getInput_array());   streams.readInputs(weightIndices.getInput_array());   MatrixF invObjOffset(objectOffset);   invObjOffset.inverse();   // Get the bind shape matrix   MatrixF bindShapeMatrix(true);   if (skin->getBind_shape_matrix())      bindShapeMatrix = vecToMatrixF<domMatrix>(skin->getBind_shape_matrix()->getValue());   bindShapeMatrix.mul(invObjOffset);   // Determine the offset into the vindices array for each vertex (since each   // vertex may have multiple [bone, weight] pairs in the array)   Vector<U32> vindicesOffset;   const domInt* vindices = (domInt*)weights_v.getRaw(0);   for (S32 iWeight = 0; iWeight < weights_vcount.getCount(); iWeight++) {      // Store the offset into the vindices array for this vertex      vindicesOffset.push_back(vindices - (domInt*)weights_v.getRaw(0));      vindices += (weights_vcount[iWeight]*2); // 2 indices [bone, weight] per vert   }   // Set vertex weights   bool tooManyWeightsWarning = false;   for (S32 iVert = 0; iVert < vertsPerFrame; iVert++) {      const domUint* vcount = (domUint*)weights_vcount.getRaw(0);      const domInt* vindices = (domInt*)weights_v.getRaw(0);      vindices += vindicesOffset[vertTuples[iVert].vertex];      S32 nonZeroWeightCount = 0;      for (S32 iWeight = 0; iWeight < vcount[vertTuples[iVert].vertex]; iWeight++) {         S32 bIndex = vindices[iWeight*2];         F32 bWeight = streams.weights.getFloatValue( vindices[iWeight*2 + 1] );         // Ignore empty weights         if ( bIndex < 0 || bWeight == 0 )            continue;         // Limit the number of weights per bone (keep the N largest influences)         if ( nonZeroWeightCount >= TSSkinMesh::BatchData::maxBonePerVert )         {            if (vcount[vertTuples[iVert].vertex] > TSSkinMesh::BatchData::maxBonePerVert)            {               if (!tooManyWeightsWarning)               {                  tooManyWeightsWarning = true;                  daeErrorHandler::get()->handleWarning(avar("At least one vertex has "                     "too many bone weights. Limiting to the largest %d influences.",                     TSSkinMesh::BatchData::maxBonePerVert));               }            }            // Too many weights => find and replace the smallest one            S32 minIndex = weight.size() - TSSkinMesh::BatchData::maxBonePerVert;            F32 minWeight = weight[minIndex];            for (S32 i = minIndex + 1; i < weight.size(); i++)            {               if (weight[i] < minWeight)               {                  minWeight = weight[i];                  minIndex = i;               }            }            boneIndex[minIndex] = bIndex;            weight[minIndex] = bWeight;         }         else         {            vertexIndex.push_back( iVert );            boneIndex.push_back( bIndex );            weight.push_back( bWeight );            nonZeroWeightCount++;         }      }   }   // Normalize vertex weights (force weights for each vert to sum to 1)   S32 iWeight = 0;   while (iWeight < weight.size()) {      // Find the last weight with the same vertex number, and sum all weights for      // that vertex      F32 invTotalWeight = 0;      S32 iLast;      for (iLast = iWeight; iLast < weight.size(); iLast++) {         if (vertexIndex[iLast] != vertexIndex[iWeight])            break;         invTotalWeight += weight[iLast];      }      // Then normalize the vertex weights      invTotalWeight = 1.0f / invTotalWeight;      for (; iWeight < iLast; iWeight++)         weight[iWeight] *= invTotalWeight;   }   // Add dummy AppNodes to allow Collada joints to be mapped to 3space nodes   bones.setSize(streams.joints.size());   initialTransforms.setSize(streams.joints.size());   for (S32 iJoint = 0; iJoint < streams.joints.size(); iJoint++)   {      const char* jointName = streams.joints.getStringValue(iJoint);      // Lookup the joint element      const domNode* joint = 0;      if (instanceCtrl->getSkeleton_array().getCount()) {         // Search for the node using the <skeleton> as the base element         for (S32 iSkel = 0; iSkel < instanceCtrl->getSkeleton_array().getCount(); iSkel++) {            xsAnyURI skeleton = instanceCtrl->getSkeleton_array()[iSkel]->getValue();            daeSIDResolver resolver(skeleton.getElement(), jointName);            joint = daeSafeCast<domNode>(resolver.getElement());            if (joint)               break;         }      }      else {         // Search for the node from the root level         daeSIDResolver resolver(skin->getDocument()->getDomRoot(), jointName);         joint = daeSafeCast<domNode>(resolver.getElement());      }      if (!joint) {         daeErrorHandler::get()->handleWarning(avar("Failed to find bone '%s', "            "defaulting to instance_controller parent node '%s'", jointName, appNode->getName()));         joint = appNode->getDomNode();      }      bones[iJoint] = new ColladaAppNode(joint);      initialTransforms[iJoint] = objectOffset;      // Bone scaling is generally ignored during import, since 3space only      // stores default node transform and rotation. Compensate for this by      // removing the scaling from the inverse bind transform as well      MatrixF invBind = streams.invBindMatrices.getMatrixFValue(iJoint);      if (!ColladaUtils::getOptions().ignoreNodeScale)      {         Point3F invScale = invBind.getScale();         invScale.x = invScale.x ? (1.0f / invScale.x) : 0;         invScale.y = invScale.y ? (1.0f / invScale.y) : 0;         invScale.z = invScale.z ? (1.0f / invScale.z) : 0;         initialTransforms[iJoint].scale(invScale);      }      // Inverted node coordinate spaces (negative scale factor) are corrected      // in ColladaAppNode::getNodeTransform, so need to apply the same operation      // here to match      if (m_matF_determinant(invBind) < 0.0f)         initialTransforms[iJoint].scale(Point3F(1, 1, -1));      initialTransforms[iJoint].mul(invBind);      initialTransforms[iJoint].mul(bindShapeMatrix);   }}
 |