123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985 |
- //-----------------------------------------------------------------------------
- // 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.
- //-----------------------------------------------------------------------------
- #ifndef _COLLADA_UTILS_H_
- #define _COLLADA_UTILS_H_
- #ifdef _MSC_VER
- #pragma warning(disable : 4786) // disable warning about long debug symbol names
- #pragma warning(disable : 4355) // disable "'this' : used in base member initializer list" warnings
- #endif
- #ifndef _MMATRIX_H_
- #include "math/mMatrix.h"
- #endif
- #ifndef _MQUAT_H_
- #include "math/mQuat.h"
- #endif
- #ifndef _TVECTOR_H_
- #include "core/util/tVector.h"
- #endif
- #ifndef _TSSHAPE_LOADER_H_
- #include "ts/loader/tsShapeLoader.h"
- #endif
- #ifndef _OPTIMIZEDPOLYLIST_H_
- #include "collision/optimizedPolyList.h"
- #endif
- #ifndef TINYXML2_INCLUDED
- #include "tinyxml2.h"
- #endif
- #ifndef _CONSOLE_H_
- #include "console/console.h"
- #endif
- #ifndef _TSSHAPEINSTANCE_H_
- #include "ts/tsShapeInstance.h"
- #endif
- #include "platform/tmm_off.h"
- #include "dae.h"
- #include "dae/daeErrorHandler.h"
- #include "dae/domAny.h"
- #include "dom/domProfile_COMMON.h"
- #include "dom/domMaterial.h"
- #include "dom/domGeometry.h"
- #include "dom/domMorph.h"
- #include "dom/domNode.h"
- #include "dom/domCOLLADA.h"
- #include "platform/tmm_on.h"
- #include "core/strings/findMatch.h"
- namespace ColladaUtils
- {
- struct ImportOptions
- {
- enum eLodType
- {
- DetectDTS = 0,
- SingleSize,
- TrailingNumber,
- NumLodTypes
- };
- enum eAnimTimingType
- {
- FrameCount = 0,
- Seconds = 1,
- Milliseconds = 1000
- };
- domUpAxisType upAxis; // Override for the collada <up_axis> element
- F32 unit; // Override for the collada <unit> element
- eLodType lodType; // LOD type option
- S32 singleDetailSize; // Detail size for all meshes in the model
- String matNamePrefix; // Prefix to apply to collada material names
- String alwaysImport; // List of node names (with wildcards) to import, even if in the neverImport list
- String neverImport; // List of node names (with wildcards) to ignore on loading
- String alwaysImportMesh; // List of mesh names (with wildcards) to import, even if in the neverImportMesh list
- String neverImportMesh; // List of mesh names (with wildcards) to ignore on loading
- String neverImportMat; // List of material names (with wildcards) to ignore on loading
- bool ignoreNodeScale; // Ignore <scale> elements in <node>s
- bool adjustCenter; // Translate model so origin is at the center
- bool adjustFloor; // Translate model so origin is at the bottom
- bool forceUpdateMaterials; // Force update of materials.tscript
- bool useDiffuseNames; // Use diffuse texture as the material name
- // Assimp specific preprocess import options
- bool convertLeftHanded; // Convert to left handed coordinate system.
- bool calcTangentSpace; // Calculate tangents and bitangents, if possible.
- bool genUVCoords; // Convert spherical, cylindrical, box and planar mapping to proper UVs.
- bool transformUVCoords; // Preprocess UV transformations (scaling, translation ...)
- bool flipUVCoords; // This step flips all UV coordinates along the y-axis and adjusts material settings
- // and bitangents accordingly.\nAssimp uses TL(0,0):BR(1,1). T3D uses TL(0,1):BR(1,0).
- bool findInstances; // Search for instanced meshes and remove them by references to one master.
- bool limitBoneWeights; // Limit bone weights to 4 per vertex.
- bool joinIdenticalVerts; // Identifies and joins identical vertex data sets within all imported meshes.
- bool reverseWindingOrder; // This step adjusts the output face winding order to be clockwise. The default face winding order is counter clockwise.
- bool invertNormals; // Reverse the normal vector direction for all normals.
- bool removeRedundantMats; // Removes redundant materials.
- eAnimTimingType animTiming; // How to import timing data as frames, seconds or milliseconds
- S32 animFPS; // FPS value to use if timing is set in frames and the animations does not have an fps set
- F32 formatScaleFactor; // Scale factor applied to convert the shape format default unit to meters
- ImportOptions()
- {
- reset();
- }
- void reset()
- {
- upAxis = UPAXISTYPE_COUNT;
- unit = -1.0f;
- lodType = TrailingNumber;
- singleDetailSize = 2;
- matNamePrefix = "";
- alwaysImport = "";
- neverImport = String(Con::getVariable("$TSShapeConstructor::neverImport"));
- alwaysImportMesh = "";
- neverImportMesh = String(Con::getVariable("$TSShapeConstructor::neverImportMesh"));
- neverImportMat = String(Con::getVariable("$TSShapeConstructor::neverImportMat"));
- ignoreNodeScale = false;
- adjustCenter = false;
- adjustFloor = false;
- forceUpdateMaterials = false;
- useDiffuseNames = false;
- convertLeftHanded = false;
- calcTangentSpace = false;
- genUVCoords = false;
- transformUVCoords = false;
- flipUVCoords = true;
- findInstances = false;
- limitBoneWeights = false;
- joinIdenticalVerts = true;
- reverseWindingOrder = true;
- invertNormals = false;
- removeRedundantMats = true;
- animTiming = Seconds;
- animFPS = 30;
- formatScaleFactor = 1.0f;
- }
- };
- ImportOptions& getOptions();
- struct ExportData
- {
- struct detailLevel
- {
- OptimizedPolyList mesh;
- S32 size;
- Map<int, int> materialRefList;
- };
- struct meshLODData
- {
- Vector<detailLevel> meshDetailLevels;
- TSShapeInstance* shapeInst;
- MatrixF meshTransform;
- SceneObject* originatingObject;
- Point3F scale;
- S32 hasDetailLevel(S32 size)
- {
- for (U32 i = 0; i < meshDetailLevels.size(); ++i)
- {
- U32 mdlSize = meshDetailLevels[i].size;
- if (mdlSize == size)
- return i;
- }
- return -1;
- }
- meshLODData() : shapeInst(nullptr), meshTransform(true), originatingObject(nullptr), scale(0)
- {}
- };
- struct colMesh
- {
- OptimizedPolyList mesh;
- String colMeshName;
- };
- Vector<detailLevel> detailLevels;
- Vector<meshLODData> meshData;
- Vector<colMesh> colMeshes;
- Vector<BaseMatInstance*> materials;
- void processData();
- S32 hasDetailLevel(U32 dl)
- {
- for (U32 i = 0; i < detailLevels.size(); i++)
- {
- if (detailLevels[i].size == dl)
- return i;
- }
- return -1;
- }
- S32 hasMaterialInstance(BaseMatInstance* matInst)
- {
- for (U32 i = 0; i < materials.size(); i++)
- {
- if (materials[i] == matInst)
- return i;
- }
- return -1;
- }
- S32 numberOfDetailLevels()
- {
- Vector<S32> detailLevelIdxs;
- for (U32 i = 0; i < meshData.size(); ++i)
- {
- for (U32 d = 0; d < meshData[i].meshDetailLevels.size(); ++d)
- {
- detailLevelIdxs.push_back_unique(meshData[i].meshDetailLevels[d].size);
- }
- }
- return detailLevelIdxs.size();
- }
- static S32 _Sort(const S32 *p1, const S32 *p2)
- {
- S32 e1 = (*p1);
- S32 e2 = (*p2);
- if (e1 > e2)
- return 1;
- else if (e1 < e2)
- return -1;
- return 0;
- }
- S32 getDetailLevelSize(U32 detailIdx)
- {
- Vector<S32> detailLevelIdxs;
- for (U32 i = 0; i < meshData.size(); ++i)
- {
- for (U32 d = 0; d < meshData[i].meshDetailLevels.size(); ++d)
- {
- S32 mdlSize = meshData[i].meshDetailLevels[d].size;
- detailLevelIdxs.push_back_unique(mdlSize);
- }
- }
- if (detailIdx >= detailLevelIdxs.size())
- return -1;
- detailLevelIdxs.sort(&_Sort);
- return detailLevelIdxs[detailIdx];
- }
- };
- void convertTransform(MatrixF& m);
- void collapsePath(std::string& path);
- // Apply the set of Collada conditioners (suited for loading Collada models into Torque)
- void applyConditioners(domCOLLADA* root);
- const domProfile_COMMON* findEffectCommonProfile(const domEffect* effect);
- const domCommon_color_or_texture_type_complexType* findEffectDiffuse(const domEffect* effect);
- const domCommon_color_or_texture_type_complexType* findEffectSpecular(const domEffect* effect);
- const domFx_sampler2D_common_complexType* getTextureSampler(const domEffect* effect, const domCommon_color_or_texture_type_complexType* texture);
- String getSamplerImagePath(const domEffect* effect, const domFx_sampler2D_common_complexType* sampler2D);
- String resolveImagePath(const domImage* image);
- // Collada export helper functions
- Torque::Path findTexture(const Torque::Path& diffuseMap);
- void exportColladaHeader(tinyxml2::XMLElement* rootNode);
- void exportColladaMaterials(tinyxml2::XMLElement* rootNode, const OptimizedPolyList& mesh, Vector<String>& matNames, const Torque::Path& colladaFile);
- void exportColladaTriangles(tinyxml2::XMLElement* meshNode, const OptimizedPolyList& mesh, const String& meshName, const Vector<String>& matNames);
- void exportColladaMesh(tinyxml2::XMLElement* rootNode, const OptimizedPolyList& mesh, const String& meshName, const Vector<String>& matNames);
- void exportColladaScene(tinyxml2::XMLElement* rootNode, const String& meshName, const Vector<String>& matNames);
- void exportColladaMaterials(tinyxml2::XMLElement* rootNode, const ExportData& exportData, const Torque::Path& colladaFile);
- void exportColladaMesh(tinyxml2::XMLElement* rootNode, const ExportData& exportData, const String& meshName);
- void exportColladaCollisionTriangles(tinyxml2::XMLElement* meshNode, const ExportData& exportData, const U32 collisionIdx);
- void exportColladaTriangles(tinyxml2::XMLElement* meshNode, const ExportData& exportData, const U32 detailLevel, const String& meshName);
- void exportColladaScene(tinyxml2::XMLElement* rootNode, const ExportData& exportData, const String& meshName);
- // Export an OptimizedPolyList to a simple Collada file
- void exportToCollada(const Torque::Path& colladaFile, const OptimizedPolyList& mesh, const String& meshName = String::EmptyString);
- void exportToCollada(const Torque::Path& colladaFile, const ExportData& exportData);
- };
- //-----------------------------------------------------------------------------
- // Helper Classes
- //
- // The Collada DOM uses a different class for each XML element, and there is very
- // little class inheritance, even though many elements have the same attributes
- // and children. This makes the DOM a bit ugly to work with, and the following
- // templates attempt to make this situation a bit nicer by providing a common way
- // to access common elements, while retaining the strong typing of the DOM classes.
- //-----------------------------------------------------------------------------
- /// Convert from the Collada transform types to a Torque MatrixF
- template<class T> inline MatrixF vecToMatrixF(const domListOfFloats& vec) { return MatrixF(true); }
- /// Collada <translate>: [x_translate, y_translate, z_translate]
- template<> inline MatrixF vecToMatrixF<domTranslate>(const domListOfFloats& vec)
- {
- MatrixF mat(true);
- mat.setPosition(Point3F(vec[0], vec[1], vec[2]));
- return mat;
- }
- /// Collada <scale>: [x_scale, y_scale, z_scale]
- template<> inline MatrixF vecToMatrixF<domScale>(const domListOfFloats& vec)
- {
- MatrixF mat(true);
- mat.scale(Point3F(vec[0], vec[1], vec[2]));
- return mat;
- }
- /// Collada <rotate>: [rotation_axis, angle_in_degrees]
- template<> inline MatrixF vecToMatrixF<domRotate>(const domListOfFloats& vec)
- {
- AngAxisF aaxis(Point3F(vec[0], vec[1], vec[2]), -(vec[3] * M_PI) / 180.0f);
- MatrixF mat(true);
- aaxis.setMatrix(&mat);
- return mat;
- }
- /// Collada <matrix>: same form as TGE (woohoo!)
- template<> inline MatrixF vecToMatrixF<domMatrix>(const domListOfFloats& vec)
- {
- MatrixF mat;
- for (S32 i = 0; i < 16; i++)
- mat[i] = vec[i];
- return mat;
- }
- /// Collada <skew>: [angle_in_degrees, rotation_axis, translation_axis]
- /// skew transform code adapted from GMANMatrix4 implementation
- template<> inline MatrixF vecToMatrixF<domSkew>(const domListOfFloats& vec)
- {
- F32 angle = -(vec[0] * M_PI) / 180.0f;
- Point3F rotAxis(vec[1], vec[2], vec[3]);
- Point3F transAxis(vec[4], vec[5], vec[6]);
- transAxis.normalize();
- Point3F a1 = transAxis * mDot(rotAxis, transAxis);
- Point3F a2 = rotAxis - a1;
- a2.normalize();
- F32 an1 = mDot(rotAxis, a2);
- F32 an2 = mDot(rotAxis, transAxis);
- F32 rx = an1 * mCos(angle) - an2 * mSin(angle);
- F32 ry = an1 * mSin(angle) + an2 * mCos(angle);
- // Check for rotation parallel to translation
- F32 alpha = (an1 == 0) ? 0 : (ry/rx - an2/an1);
- MatrixF mat(true);
- mat(0,0) = a2.x * transAxis.x * alpha + 1.0;
- mat(1,0) = a2.y * transAxis.x * alpha;
- mat(2,0) = a2.z * transAxis.x * alpha;
- mat(0,1) = a2.x * transAxis.y * alpha;
- mat(1,1) = a2.y * transAxis.y * alpha + 1.0;
- mat(2,1) = a2.z * transAxis.y * alpha;
- mat(0,2) = a2.x * transAxis.z * alpha;
- mat(1,2) = a2.y * transAxis.z * alpha;
- mat(2,2) = a2.z * transAxis.z * alpha + 1.0;
- return mat;
- }
- /// Collada <lookat>: [eye, target, up]
- template<> inline MatrixF vecToMatrixF<domLookat>(const domListOfFloats& vec)
- {
- Point3F eye(vec[0], vec[1], vec[2]);
- Point3F target(vec[3], vec[4], vec[5]);
- Point3F up(vec[6], vec[7], vec[8]);
- Point3F fwd = target - eye;
- fwd.normalizeSafe();
- Point3F right = mCross(fwd, up);
- right.normalizeSafe();
- up = mCross(right, fwd);
- up.normalizeSafe();
- MatrixF mat(true);
- mat.setColumn(0, right);
- mat.setColumn(1, fwd);
- mat.setColumn(2, up);
- mat.setColumn(3, eye);
- return mat;
- }
- //-----------------------------------------------------------------------------
- /// Try to get a name for the element using the following attributes (in order):
- /// name, sid, id, "null"
- template<class T> inline const char* _GetNameOrId(const T* element)
- {
- return element ? (element->getName() ? element->getName() : (element->getId() ? element->getId() : "null")) : "null";
- }
- template<> inline const char* _GetNameOrId(const domInstance_geometry* element)
- {
- return element ? (element->getName() ? element->getName() : (element->getSid() ? element->getSid() : "null")) : "null";
- }
- template<> inline const char* _GetNameOrId(const domInstance_controller* element)
- {
- return element ? (element->getName() ? element->getName() : (element->getSid() ? element->getSid() : "null")) : "null";
- }
- //-----------------------------------------------------------------------------
- // Collada <source>s are extremely flexible, and thus difficult to access in a nice
- // way. This class attempts to provide a clean interface to convert Collada source
- // data to the appropriate Torque data structure without losing any of the flexibility
- // of the underlying Collada DOM.
- //
- // Some of the conversions we need to handle are:
- // - daeString to const char*
- // - daeIDRef to const char*
- // - double to F32
- // - double to Point2F
- // - double to Point3F
- // - double to MatrixF
- //
- // The _SourceReader object is initialized with a list of parameter names that it
- // tries to match to <param> elements in the source accessor to figure out how to
- // pull values out of the 1D source array. Note that no type checking of any kind
- // is done until we actually try to extract values from the source.
- class _SourceReader
- {
- const domSource* source; // the wrapped Collada source
- const domAccessor* accessor; // shortcut to the source accessor
- Vector<U32> offsets; // offset of each of the desired values to pull from the source array
- public:
- _SourceReader() : source(0), accessor(0) {}
- void reset()
- {
- source = 0;
- accessor = 0;
- offsets.clear();
- }
- //------------------------------------------------------
- // Initialize the _SourceReader object
- bool initFromSource(const domSource* src, const char* paramNames[] = 0)
- {
- source = src;
- accessor = source->getTechnique_common()->getAccessor();
- offsets.clear();
- // The source array has groups of values in a 1D stream => need to map the
- // input param names to source params to determine the offset within the
- // group for each desired value
- U32 paramCount = 0;
- while (paramNames && paramNames[paramCount][0]) {
- // lookup the index of the source param that matches the input param
- offsets.push_back(paramCount);
- for (U32 iParam = 0; iParam < accessor->getParam_array().getCount(); iParam++) {
- if (accessor->getParam_array()[iParam]->getName() &&
- dStrEqual(accessor->getParam_array()[iParam]->getName(), paramNames[paramCount])) {
- offsets.last() = iParam;
- break;
- }
- }
- paramCount++;
- }
- // If no input params were specified, just map the source params directly
- if (!offsets.size()) {
- for (S32 iParam = 0; iParam < accessor->getParam_array().getCount(); iParam++)
- offsets.push_back(iParam);
- }
- return true;
- }
- //------------------------------------------------------
- // Shortcut to the size of the array (should be the number of destination objects)
- S32 size() const { return accessor ? accessor->getCount() : 0; }
- // Get the number of elements per group in the source
- S32 stride() const { return accessor ? accessor->getStride() : 0; }
- //------------------------------------------------------
- // Get a pointer to the start of a group of values (index advances by stride)
- //template<class T> T getArrayData(S32 index) const { return 0; }
- const double* getStringArrayData(S32 index) const
- {
- if ((index >= 0) && (index < size())) {
- if (source->getFloat_array())
- return &source->getFloat_array()->getValue()[index*stride()];
- }
- return 0;
- }
- //------------------------------------------------------
- // Read a single value from the source array
- //template<class T> T getValue(S32 index) const { return T; }
- const char* getStringValue(S32 index) const
- {
- if ((index >= 0) && (index < size())) {
- // could be plain strings or IDREFs
- if (source->getName_array())
- return source->getName_array()->getValue()[index*stride()];
- else if (source->getIDREF_array())
- return source->getIDREF_array()->getValue()[index*stride()].getID();
- }
- return "";
- }
- F32 getFloatValue(S32 index) const
- {
- F32 value(0);
- if (const double* data = getStringArrayData(index))
- return data[offsets[0]];
- return value;
- }
- Point2F getPoint2FValue(S32 index) const
- {
- Point2F value(0, 0);
- if (const double* data = getStringArrayData(index))
- value.set(data[offsets[0]], data[offsets[1]]);
- return value;
- }
- Point3F getPoint3FValue(S32 index) const
- {
- Point3F value(1, 0, 0);
- if (const double* data = getStringArrayData(index))
- value.set(data[offsets[0]], data[offsets[1]], data[offsets[2]]);
- return value;
- }
- ColorI getColorIValue(S32 index) const
- {
- ColorI value(255, 255, 255, 255);
- if (const double* data = getStringArrayData(index))
- {
- value.red = data[offsets[0]] * 255.0;
- value.green = data[offsets[1]] * 255.0;
- value.blue = data[offsets[2]] * 255.0;
- if ( stride() == 4 )
- value.alpha = data[offsets[3]] * 255.0;
- }
- return value;
- }
- MatrixF getMatrixFValue(S32 index) const
- {
- MatrixF value(true);
- if (const double* data = getStringArrayData(index)) {
- for (S32 i = 0; i < 16; i++)
- value[i] = data[i];
- }
- return value;
- }
- };
- //-----------------------------------------------------------------------------
- // Collada geometric primitives: Use the BasePrimitive class to access the
- // different primitive types in a nice way.
- class BasePrimitive
- {
- public:
- virtual ~BasePrimitive() { }
- /// Return true if the element is a geometric primitive type
- static bool isPrimitive(const daeElement* element)
- {
- switch (element->getElementType()) {
- case COLLADA_TYPE::TRIANGLES: case COLLADA_TYPE::POLYLIST:
- case COLLADA_TYPE::POLYGONS: case COLLADA_TYPE::TRIFANS:
- case COLLADA_TYPE::TRISTRIPS: case COLLADA_TYPE::CAPSULE:
- case COLLADA_TYPE::CYLINDER: case COLLADA_TYPE::LINES:
- case COLLADA_TYPE::LINESTRIPS: case COLLADA_TYPE::PLANE:
- case COLLADA_TYPE::SPLINE: case COLLADA_TYPE::SPHERE:
- case COLLADA_TYPE::TAPERED_CAPSULE: case COLLADA_TYPE::TAPERED_CYLINDER:
- return true;
- }
- return false;
- }
- /// Return true if the element is a supported primitive type
- static bool isSupportedPrimitive(const daeElement* element)
- {
- switch (element->getElementType()) {
- case COLLADA_TYPE::TRIANGLES:
- case COLLADA_TYPE::TRISTRIPS:
- case COLLADA_TYPE::TRIFANS:
- case COLLADA_TYPE::POLYLIST:
- case COLLADA_TYPE::POLYGONS:
- return true;
- }
- return false;
- }
- /// Construct a child class based on the type of Collada element
- static BasePrimitive* get(const daeElement* element);
- /// Methods to be implemented for each supported Collada geometric element
- virtual const char* getElementName() = 0;
- virtual const char* getMaterial() = 0;
- virtual const domInputLocalOffset_Array& getInputs() = 0;
- virtual S32 getStride() const = 0;
- virtual const domListOfUInts *getTriangleData() = 0;
- };
- /// Template child class for supported Collada primitive elements
- template<class T> class ColladaPrimitive : public BasePrimitive
- {
- T* primitive;
- domListOfUInts *pTriangleData;
- S32 stride;
- public:
- ColladaPrimitive(const daeElement* e) : pTriangleData(0)
- {
- // Cast to geometric primitive element
- primitive = daeSafeCast<T>(const_cast<daeElement*>(e));
- // Determine stride
- stride = 0;
- for (S32 iInput = 0; iInput < getInputs().getCount(); iInput++) {
- if (getInputs()[iInput]->getOffset() >= stride)
- stride = getInputs()[iInput]->getOffset() + 1;
- }
- }
- ~ColladaPrimitive()
- {
- delete pTriangleData;
- }
- /// Most primitives can use these common implementations
- const char* getElementName() { return primitive->getElementName(); }
- const char* getMaterial() { return (FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().neverImportMat, primitive->getMaterial(), false)) ? NULL : primitive->getMaterial(); }
- const domInputLocalOffset_Array& getInputs() { return primitive->getInput_array(); }
- S32 getStride() const { return stride; }
- /// Each supported primitive needs to implement this method (and convert
- /// to triangles if required)
- const domListOfUInts *getTriangleData() { return NULL; }
- };
- //-----------------------------------------------------------------------------
- // <triangles>
- template<> inline const domListOfUInts *ColladaPrimitive<domTriangles>::getTriangleData()
- {
- // Return the <p> integer list directly
- return (primitive->getP() ? &(primitive->getP()->getValue()) : NULL);
- }
- //-----------------------------------------------------------------------------
- // <tristrips>
- template<> inline const domListOfUInts *ColladaPrimitive<domTristrips>::getTriangleData()
- {
- if (!pTriangleData)
- {
- // Convert strips to triangles
- pTriangleData = new domListOfUInts();
- for (S32 iStrip = 0; iStrip < primitive->getCount(); iStrip++) {
- domP* P = primitive->getP_array()[iStrip];
- // Ignore invalid P arrays
- if (!P || !P->getValue().getCount())
- continue;
- domUint* pSrcData = &(P->getValue()[0]);
- size_t numTriangles = (P->getValue().getCount() / stride) - 2;
- // Convert the strip back to a triangle list
- domUint* v0 = pSrcData;
- for (S32 iTri = 0; iTri < numTriangles; iTri++, v0 += stride) {
- if (iTri & 0x1)
- {
- // CW triangle
- pTriangleData->appendArray(stride, v0);
- pTriangleData->appendArray(stride, v0 + 2*stride);
- pTriangleData->appendArray(stride, v0 + stride);
- }
- else
- {
- // CCW triangle
- pTriangleData->appendArray(stride*3, v0);
- }
- }
- }
- }
- return pTriangleData;
- }
- //-----------------------------------------------------------------------------
- // <trifans>
- template<> inline const domListOfUInts *ColladaPrimitive<domTrifans>::getTriangleData()
- {
- if (!pTriangleData)
- {
- // Convert strips to triangles
- pTriangleData = new domListOfUInts();
- for (S32 iStrip = 0; iStrip < primitive->getCount(); iStrip++) {
- domP* P = primitive->getP_array()[iStrip];
- // Ignore invalid P arrays
- if (!P || !P->getValue().getCount())
- continue;
- domUint* pSrcData = &(P->getValue()[0]);
- size_t numTriangles = (P->getValue().getCount() / stride) - 2;
- // Convert the fan back to a triangle list
- domUint* v0 = pSrcData + stride;
- for (S32 iTri = 0; iTri < numTriangles; iTri++, v0 += stride) {
- pTriangleData->appendArray(stride, pSrcData); // shared vertex
- pTriangleData->appendArray(stride, v0); // previous vertex
- pTriangleData->appendArray(stride, v0+stride); // current vertex
- }
- }
- }
- return pTriangleData;
- }
- //-----------------------------------------------------------------------------
- // <polygons>
- template<> inline const domListOfUInts *ColladaPrimitive<domPolygons>::getTriangleData()
- {
- if (!pTriangleData)
- {
- // Convert polygons to triangles
- pTriangleData = new domListOfUInts();
- for (S32 iPoly = 0; iPoly < primitive->getCount(); iPoly++) {
- domP* P = primitive->getP_array()[iPoly];
- // Ignore invalid P arrays
- if (!P || !P->getValue().getCount())
- continue;
- domUint* pSrcData = &(P->getValue()[0]);
- size_t numPoints = P->getValue().getCount() / stride;
- // Use a simple tri-fan (centered at the first point) method of
- // converting the polygon to triangles.
- domUint* v0 = pSrcData;
- pSrcData += stride;
- for (S32 iTri = 0; iTri < numPoints-2; iTri++) {
- pTriangleData->appendArray(stride, v0);
- pTriangleData->appendArray(stride*2, pSrcData);
- pSrcData += stride;
- }
- }
- }
- return pTriangleData;
- }
- //-----------------------------------------------------------------------------
- // <polylist>
- template<> inline const domListOfUInts *ColladaPrimitive<domPolylist>::getTriangleData()
- {
- if (!pTriangleData)
- {
- // Convert polygons to triangles
- pTriangleData = new domListOfUInts();
- // Check that the P element has the right number of values (this
- // has been seen with certain models exported using COLLADAMax)
- const domListOfUInts& vcount = primitive->getVcount()->getValue();
- U32 expectedCount = 0;
- for (S32 iPoly = 0; iPoly < vcount.getCount(); iPoly++)
- expectedCount += vcount[iPoly];
- expectedCount *= stride;
- if (!primitive->getP() || !primitive->getP()->getValue().getCount() ||
- (primitive->getP()->getValue().getCount() != expectedCount) )
- {
- Con::warnf("<polylist> element found with invalid <p> array. This primitive will be ignored.");
- return pTriangleData;
- }
- domUint* pSrcData = &(primitive->getP()->getValue()[0]);
- for (S32 iPoly = 0; iPoly < vcount.getCount(); iPoly++) {
- // Use a simple tri-fan (centered at the first point) method of
- // converting the polygon to triangles.
- domUint* v0 = pSrcData;
- pSrcData += stride;
- for (S32 iTri = 0; iTri < vcount[iPoly]-2; iTri++) {
- pTriangleData->appendArray(stride, v0);
- pTriangleData->appendArray(stride*2, pSrcData);
- pSrcData += stride;
- }
- pSrcData += stride;
- }
- }
- return pTriangleData;
- }
- //-----------------------------------------------------------------------------
- /// Convert a custom parameter string to a particular type
- template<typename T> inline T convert(const char* value) { return value; }
- template<> inline bool convert(const char* value) { return dAtob(value); }
- template<> inline S32 convert(const char* value) { return dAtoi(value); }
- template<> inline F64 convert(const char* value) { return dAtof(value); }
- template<> inline F32 convert(const char* value) { return convert<double>(value); }
- //-----------------------------------------------------------------------------
- /// Collada animation data
- struct AnimChannels : public Vector<struct AnimData*>
- {
- daeElement *element;
- AnimChannels(daeElement* el) : element(el)
- {
- element->setUserData(this);
- }
- ~AnimChannels()
- {
- if (element)
- element->setUserData(0);
- }
- };
- struct AnimData
- {
- bool enabled; ///!< Used to select animation channels for the current clip
- _SourceReader input;
- _SourceReader output;
- _SourceReader inTangent;
- _SourceReader outTangent;
- _SourceReader interpolation;
- U32 targetValueOffset; ///< Offset into the target element (for arrays of values)
- U32 targetValueCount; ///< Number of values animated (from OUTPUT source array)
- /// Get the animation channels for the Collada element (if any)
- static AnimChannels* getAnimChannels(const daeElement* element)
- {
- return element ? (AnimChannels*)const_cast<daeElement*>(element)->getUserData() : 0;
- }
- AnimData() : enabled(false), targetValueOffset(0), targetValueCount(0){ }
- void parseTargetString(const char* target, S32 fullCount, const char* elements[]);
- F32 invertParamCubic(F32 param, F32 x0, F32 x1, F32 x2, F32 x3) const;
- void interpValue(F32 t, U32 offset, double* value) const;
- void interpValue(F32 t, U32 offset, const char** value) const;
- };
- //-----------------------------------------------------------------------------
- // Collada allows any element with an SID or ID attribute to be the target of
- // an animation channel, which is very flexible, but awkward to work with. Some
- // examples of animated values are:
- // - single float
- // - single int
- // - single bool
- // - single string
- // - list of floats (transform elements or morph weights)
- //
- // This class provides a generic way to check if an element is animated, and
- // to get the value of the element at a given time.
- template<class T>
- struct AnimatedElement
- {
- const daeElement* element; ///< The Collada element (can be NULL)
- T defaultVal; ///< Default value (used when element is NULL)
- AnimatedElement(const daeElement* e=0) : element(e) { }
- /// Check if the element has any animations channels
- bool isAnimated() { return (AnimData::getAnimChannels(element) != 0); }
- bool isAnimated(F32 start, F32 end) { return isAnimated(); }
- /// Get the value of the element at the specified time
- T getValue(F32 time)
- {
- // If the element is NULL, just use the default (handy for <extra> profiles which
- // may or may not be present in the document)
- T value(defaultVal);
- if (const domAny* param = daeSafeCast<domAny>(const_cast<daeElement*>(element))) {
- // If the element is not animated, just use its current value
- value = convert<T>(param->getValue());
- // Animate the value
- const AnimChannels* channels = AnimData::getAnimChannels(element);
- if (channels && (time >= 0)) {
- for (S32 iChannel = 0; iChannel < channels->size(); iChannel++) {
- const AnimData* animData = (*channels)[iChannel];
- if (animData->enabled)
- animData->interpValue(time, 0, &value);
- }
- }
- }
- return value;
- }
- };
- template<class T> struct AnimatedElementList : public AnimatedElement<T>
- {
- AnimatedElementList(const daeElement* e=0) : AnimatedElement<T>(e) { }
- // @todo: Disable morph animations for now since they are not supported by T3D
- bool isAnimated() { return false; }
- bool isAnimated(F32 start, F32 end) { return false; }
- // Get the value of the element list at the specified time
- T getValue(F32 time)
- {
- T vec(this->defaultVal);
- if (this->element) {
- // Get a copy of the vector
- vec = *(T*)const_cast<daeElement*>(this->element)->getValuePointer();
- // Animate the vector
- const AnimChannels* channels = AnimData::getAnimChannels(this->element);
- if (channels && (time >= 0)) {
- for (S32 iChannel = 0; iChannel < channels->size(); iChannel++) {
- const AnimData* animData = (*channels)[iChannel];
- if (animData->enabled) {
- for (S32 iValue = 0; iValue < animData->targetValueCount; iValue++)
- animData->interpValue(time, iValue, &vec[animData->targetValueOffset + iValue]);
- }
- }
- }
- }
- return vec;
- }
- };
- // Strongly typed animated values
- typedef AnimatedElement<double> AnimatedFloat;
- typedef AnimatedElement<bool> AnimatedBool;
- typedef AnimatedElement<S32> AnimatedInt;
- typedef AnimatedElement<const char*> AnimatedString;
- typedef AnimatedElementList<domListOfFloats> AnimatedFloatList;
- #endif // _COLLADA_UTILS_H_
|