1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
4 
5 Copyright (c) 2006-2021, assimp team
6 
7 All rights reserved.
8 
9 Redistribution and use of this software in source and binary forms,
10 with or without modification, are permitted provided that the
11 following conditions are met:
12 
13 * Redistributions of source code must retain the above
14 copyright notice, this list of conditions and the
15 following disclaimer.
16 
17 * Redistributions in binary form must reproduce the above
18 copyright notice, this list of conditions and the
19 following disclaimer in the documentation and/or other
20 materials provided with the distribution.
21 
22 * Neither the name of the assimp team, nor the names of its
23 contributors may be used to endorse or promote products
24 derived from this software without specific prior
25 written permission of the assimp team.
26 
27 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 
39 ----------------------------------------------------------------------
40 */
41 
42 /** Helper structures for the Collada loader */
43 
44 #ifndef AI_COLLADAHELPER_H_INC
45 #define AI_COLLADAHELPER_H_INC
46 
47 #include <assimp/light.h>
48 #include <assimp/material.h>
49 #include <assimp/mesh.h>
50 
51 #include <cstdint>
52 #include <map>
53 #include <set>
54 #include <vector>
55 
56 struct aiMaterial;
57 
58 namespace Assimp {
59 namespace Collada {
60 
61 /// Collada file versions which evolved during the years ...
62 enum FormatVersion {
63     FV_1_5_n,
64     FV_1_4_n,
65     FV_1_3_n
66 };
67 
68 /// Transformation types that can be applied to a node
69 enum TransformType {
70     TF_LOOKAT,
71     TF_ROTATE,
72     TF_TRANSLATE,
73     TF_SCALE,
74     TF_SKEW,
75     TF_MATRIX
76 };
77 
78 /// Different types of input data to a vertex or face
79 enum InputType {
80     IT_Invalid,
81     IT_Vertex, // special type for per-index data referring to the <vertices> element carrying the per-vertex data.
82     IT_Position,
83     IT_Normal,
84     IT_Texcoord,
85     IT_Color,
86     IT_Tangent,
87     IT_Bitangent
88 };
89 
90 /// Supported controller types
91 enum ControllerType {
92     Skin,
93     Morph
94 };
95 
96 /// Supported morph methods
97 enum MorphMethod {
98     Normalized,
99     Relative
100 };
101 
102 /// Common metadata keys as <Collada, Assimp>
103 using MetaKeyPair = std::pair<std::string, std::string>;
104 using MetaKeyPairVector = std::vector<MetaKeyPair>;
105 
106 /// Collada as lower_case (native)
107 const MetaKeyPairVector &GetColladaAssimpMetaKeys();
108 
109 // Collada as CamelCase (used by Assimp for consistency)
110 const MetaKeyPairVector &GetColladaAssimpMetaKeysCamelCase();
111 
112 /// Convert underscore_separated to CamelCase "authoring_tool" becomes "AuthoringTool"
113 void ToCamelCase(std::string &text);
114 
115 /// Contains all data for one of the different transformation types
116 struct Transform {
117     std::string mID; ///< SID of the transform step, by which anim channels address their target node
118     TransformType mType;
119     ai_real f[16]; ///< Interpretation of data depends on the type of the transformation
120 };
121 
122 /// A collada camera.
123 struct Camera {
CameraCamera124     Camera() :
125             mOrtho(false),
126             mHorFov(10e10f),
127             mVerFov(10e10f),
128             mAspect(10e10f),
129             mZNear(0.1f),
130             mZFar(1000.f) {}
131 
132     /// Name of camera
133     std::string mName;
134 
135     /// True if it is an orthographic camera
136     bool mOrtho;
137 
138     /// Horizontal field of view in degrees
139     ai_real mHorFov;
140 
141     /// Vertical field of view in degrees
142     ai_real mVerFov;
143 
144     /// Screen aspect
145     ai_real mAspect;
146 
147     /// Near& far z
148     ai_real mZNear, mZFar;
149 };
150 
151 #define ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET 1e9f
152 
153 /** A collada light source. */
154 struct Light {
LightLight155     Light() :
156             mType(aiLightSource_UNDEFINED),
157             mAttConstant(1.f),
158             mAttLinear(0.f),
159             mAttQuadratic(0.f),
160             mFalloffAngle(180.f),
161             mFalloffExponent(0.f),
162             mPenumbraAngle(ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET),
163             mOuterAngle(ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET),
164             mIntensity(1.f) {}
165 
166     /// Type of the light source aiLightSourceType + ambient
167     unsigned int mType;
168 
169     /// Color of the light
170     aiColor3D mColor;
171 
172     /// Light attenuation
173     ai_real mAttConstant, mAttLinear, mAttQuadratic;
174 
175     /// Spot light falloff
176     ai_real mFalloffAngle;
177     ai_real mFalloffExponent;
178 
179     // -----------------------------------------------------
180     // FCOLLADA extension from here
181 
182     /// ... related stuff from maja and max extensions
183     ai_real mPenumbraAngle;
184     ai_real mOuterAngle;
185 
186     /// Common light intensity
187     ai_real mIntensity;
188 };
189 
190 /** Short vertex index description */
191 struct InputSemanticMapEntry {
InputSemanticMapEntryInputSemanticMapEntry192     InputSemanticMapEntry() :
193             mSet(0),
194             mType(IT_Invalid) {}
195 
196     /// Index of set, optional
197     unsigned int mSet;
198 
199     /// Type of referenced vertex input
200     InputType mType;
201 };
202 
203 /// Table to map from effect to vertex input semantics
204 struct SemanticMappingTable {
205     /// Name of material
206     std::string mMatName;
207 
208     /// List of semantic map commands, grouped by effect semantic name
209     using InputSemanticMap = std::map<std::string, InputSemanticMapEntry>;
210     InputSemanticMap mMap;
211 
212     /// For std::find
213     bool operator==(const std::string &s) const {
214         return s == mMatName;
215     }
216 };
217 
218 /// A reference to a mesh inside a node, including materials assigned to the various subgroups.
219 /// The ID refers to either a mesh or a controller which specifies the mesh
220 struct MeshInstance {
221     ///< ID of the mesh or controller to be instanced
222     std::string mMeshOrController;
223 
224     ///< Map of materials by the subgroup ID they're applied to
225     std::map<std::string, SemanticMappingTable> mMaterials;
226 };
227 
228 /// A reference to a camera inside a node
229 struct CameraInstance {
230     ///< ID of the camera
231     std::string mCamera;
232 };
233 
234 /// A reference to a light inside a node
235 struct LightInstance {
236     ///< ID of the camera
237     std::string mLight;
238 };
239 
240 /// A reference to a node inside a node
241 struct NodeInstance {
242     ///< ID of the node
243     std::string mNode;
244 };
245 
246 /// A node in a scene hierarchy
247 struct Node {
248     std::string mName;
249     std::string mID;
250     std::string mSID;
251     Node *mParent;
252     std::vector<Node *> mChildren;
253 
254     /// Operations in order to calculate the resulting transformation to parent.
255     std::vector<Transform> mTransforms;
256 
257     /// Meshes at this node
258     std::vector<MeshInstance> mMeshes;
259 
260     /// Lights at this node
261     std::vector<LightInstance> mLights;
262 
263     /// Cameras at this node
264     std::vector<CameraInstance> mCameras;
265 
266     /// Node instances at this node
267     std::vector<NodeInstance> mNodeInstances;
268 
269     /// Root-nodes: Name of primary camera, if any
270     std::string mPrimaryCamera;
271 
272     /// Constructor. Begin with a zero parent
NodeNode273     Node() :
274             mParent(nullptr) {
275         // empty
276     }
277 
278     /// Destructor: delete all children subsequently
~NodeNode279     ~Node() {
280         for (std::vector<Node *>::iterator it = mChildren.begin(); it != mChildren.end(); ++it) {
281             delete *it;
282         }
283     }
284 };
285 
286 /// Data source array: either floats or strings
287 struct Data {
288     bool mIsStringArray;
289     std::vector<ai_real> mValues;
290     std::vector<std::string> mStrings;
291 };
292 
293 /// Accessor to a data array
294 struct Accessor {
295     size_t mCount; // in number of objects
296     size_t mSize; // size of an object, in elements (floats or strings, mostly 1)
297     size_t mOffset; // in number of values
298     size_t mStride; // Stride in number of values
299     std::vector<std::string> mParams; // names of the data streams in the accessors. Empty string tells to ignore.
300     size_t mSubOffset[4]; // Sub-offset inside the object for the common 4 elements. For a vector, that's XYZ, for a color RGBA and so on.
301             // For example, SubOffset[0] denotes which of the values inside the object is the vector X component.
302     std::string mSource; // URL of the source array
303     mutable const Data *mData; // Pointer to the source array, if resolved. nullptr else
304 
AccessorAccessor305     Accessor() {
306         mCount = 0;
307         mSize = 0;
308         mOffset = 0;
309         mStride = 0;
310         mData = nullptr;
311         mSubOffset[0] = mSubOffset[1] = mSubOffset[2] = mSubOffset[3] = 0;
312     }
313 };
314 
315 /// A single face in a mesh
316 struct Face {
317     std::vector<size_t> mIndices;
318 };
319 
320 /// An input channel for mesh data, referring to a single accessor
321 struct InputChannel {
322     InputType mType; // Type of the data
323     size_t mIndex; // Optional index, if multiple sets of the same data type are given
324     size_t mOffset; // Index offset in the indices array of per-face indices. Don't ask, can't explain that any better.
325     std::string mAccessor; // ID of the accessor where to read the actual values from.
326     mutable const Accessor *mResolved; // Pointer to the accessor, if resolved. nullptr else
327 
InputChannelInputChannel328     InputChannel() {
329         mType = IT_Invalid;
330         mIndex = 0;
331         mOffset = 0;
332         mResolved = nullptr;
333     }
334 };
335 
336 /// Subset of a mesh with a certain material
337 struct SubMesh {
338     std::string mMaterial; ///< subgroup identifier
339     size_t mNumFaces; ///< number of faces in this sub-mesh
340 };
341 
342 /// Contains data for a single mesh
343 struct Mesh {
MeshMesh344     Mesh(const std::string &id) :
345             mId(id) {
346         for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
347             mNumUVComponents[i] = 2;
348         }
349     }
350 
351     const std::string mId;
352     std::string mName;
353 
354     // just to check if there's some sophisticated addressing involved...
355     // which we don't support, and therefore should warn about.
356     std::string mVertexID;
357 
358     // Vertex data addressed by vertex indices
359     std::vector<InputChannel> mPerVertexData;
360 
361     // actual mesh data, assembled on encounter of a <p> element. Verbose format, not indexed
362     std::vector<aiVector3D> mPositions;
363     std::vector<aiVector3D> mNormals;
364     std::vector<aiVector3D> mTangents;
365     std::vector<aiVector3D> mBitangents;
366     std::vector<aiVector3D> mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
367     std::vector<aiColor4D> mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
368 
369     unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS];
370 
371     // Faces. Stored are only the number of vertices for each face.
372     // 1 == point, 2 == line, 3 == triangle, 4+ == poly
373     std::vector<size_t> mFaceSize;
374 
375     // Position indices for all faces in the sequence given in mFaceSize -
376     // necessary for bone weight assignment
377     std::vector<size_t> mFacePosIndices;
378 
379     // Sub-meshes in this mesh, each with a given material
380     std::vector<SubMesh> mSubMeshes;
381 };
382 
383 /// Which type of primitives the ReadPrimitives() function is going to read
384 enum PrimitiveType {
385     Prim_Invalid,
386     Prim_Lines,
387     Prim_LineStrip,
388     Prim_Triangles,
389     Prim_TriStrips,
390     Prim_TriFans,
391     Prim_Polylist,
392     Prim_Polygon
393 };
394 
395 /// A skeleton controller to deform a mesh with the use of joints
396 struct Controller {
397     // controller type
398     ControllerType mType;
399 
400     // Morphing method if type is Morph
401     MorphMethod mMethod;
402 
403     // the URL of the mesh deformed by the controller.
404     std::string mMeshId;
405 
406     // accessor URL of the joint names
407     std::string mJointNameSource;
408 
409     ///< The bind shape matrix, as array of floats. I'm not sure what this matrix actually describes, but it can't be ignored in all cases
410     ai_real mBindShapeMatrix[16];
411 
412     // accessor URL of the joint inverse bind matrices
413     std::string mJointOffsetMatrixSource;
414 
415     // input channel: joint names.
416     InputChannel mWeightInputJoints;
417     // input channel: joint weights
418     InputChannel mWeightInputWeights;
419 
420     // Number of weights per vertex.
421     std::vector<size_t> mWeightCounts;
422 
423     // JointIndex-WeightIndex pairs for all vertices
424     std::vector<std::pair<size_t, size_t>> mWeights;
425 
426     std::string mMorphTarget;
427     std::string mMorphWeight;
428 };
429 
430 /// A collada material. Pretty much the only member is a reference to an effect.
431 struct Material {
432     std::string mName;
433     std::string mEffect;
434 };
435 
436 /// Type of the effect param
437 enum ParamType {
438     Param_Sampler,
439     Param_Surface
440 };
441 
442 /// A param for an effect. Might be of several types, but they all just refer to each other, so I summarize them
443 struct EffectParam {
444     ParamType mType;
445     std::string mReference; // to which other thing the param is referring to.
446 };
447 
448 /// Shading type supported by the standard effect spec of Collada
449 enum ShadeType {
450     Shade_Invalid,
451     Shade_Constant,
452     Shade_Lambert,
453     Shade_Phong,
454     Shade_Blinn
455 };
456 
457 /// Represents a texture sampler in collada
458 struct Sampler {
SamplerSampler459     Sampler() :
460             mWrapU(true),
461             mWrapV(true),
462             mMirrorU(),
463             mMirrorV(),
464             mOp(aiTextureOp_Multiply),
465             mUVId(UINT_MAX),
466             mWeighting(1.f),
467             mMixWithPrevious(1.f) {}
468 
469     /// Name of image reference
470     std::string mName;
471 
472     /// Wrap U?
473     bool mWrapU;
474 
475     /// Wrap V?
476     bool mWrapV;
477 
478     /// Mirror U?
479     bool mMirrorU;
480 
481     /// Mirror V?
482     bool mMirrorV;
483 
484     /// Blend mode
485     aiTextureOp mOp;
486 
487     /// UV transformation
488     aiUVTransform mTransform;
489 
490     /// Name of source UV channel
491     std::string mUVChannel;
492 
493     /// Resolved UV channel index or UINT_MAX if not known
494     unsigned int mUVId;
495 
496     // OKINO/MAX3D extensions from here
497     // -------------------------------------------------------
498 
499     /// Weighting factor
500     ai_real mWeighting;
501 
502     /// Mixing factor from OKINO
503     ai_real mMixWithPrevious;
504 };
505 
506 /// A collada effect. Can contain about anything according to the Collada spec,
507 /// but we limit our version to a reasonable subset.
508 struct Effect {
509     /// Shading mode
510     ShadeType mShadeType;
511 
512     /// Colors
513     aiColor4D mEmissive, mAmbient, mDiffuse, mSpecular,
514             mTransparent, mReflective;
515 
516     /// Textures
517     Sampler mTexEmissive, mTexAmbient, mTexDiffuse, mTexSpecular,
518             mTexTransparent, mTexBump, mTexReflective;
519 
520     /// Scalar factory
521     ai_real mShininess, mRefractIndex, mReflectivity;
522     ai_real mTransparency;
523     bool mHasTransparency;
524     bool mRGBTransparency;
525     bool mInvertTransparency;
526 
527     /// local params referring to each other by their SID
528     using ParamLibrary = std::map<std::string, Collada::EffectParam>;
529     ParamLibrary mParams;
530 
531     // MAX3D extensions
532     // ---------------------------------------------------------
533     // Double-sided?
534     bool mDoubleSided, mWireframe, mFaceted;
535 
EffectEffect536     Effect() :
537             mShadeType(Shade_Phong),
538             mEmissive(0, 0, 0, 1),
539             mAmbient(0.1f, 0.1f, 0.1f, 1),
540             mDiffuse(0.6f, 0.6f, 0.6f, 1),
541             mSpecular(0.4f, 0.4f, 0.4f, 1),
542             mTransparent(0, 0, 0, 1),
543             mShininess(10.0f),
544             mRefractIndex(1.f),
545             mReflectivity(0.f),
546             mTransparency(1.f),
547             mHasTransparency(false),
548             mRGBTransparency(false),
549             mInvertTransparency(false),
550             mDoubleSided(false),
551             mWireframe(false),
552             mFaceted(false) {
553     }
554 };
555 
556 /// An image, meaning texture
557 struct Image {
558     std::string mFileName;
559 
560     /// Embedded image data
561     std::vector<uint8_t> mImageData;
562 
563     /// File format hint of embedded image data
564     std::string mEmbeddedFormat;
565 };
566 
567 /// An animation channel.
568 struct AnimationChannel {
569     /// URL of the data to animate. Could be about anything, but we support only the
570     /// "NodeID/TransformID.SubElement" notation
571     std::string mTarget;
572 
573     /// Source URL of the time values. Collada calls them "input". Meh.
574     std::string mSourceTimes;
575     /// Source URL of the value values. Collada calls them "output".
576     std::string mSourceValues;
577     /// Source URL of the IN_TANGENT semantic values.
578     std::string mInTanValues;
579     /// Source URL of the OUT_TANGENT semantic values.
580     std::string mOutTanValues;
581     /// Source URL of the INTERPOLATION semantic values.
582     std::string mInterpolationValues;
583 };
584 
585 /// An animation. Container for 0-x animation channels or 0-x animations
586 struct Animation {
587     /// Anim name
588     std::string mName;
589 
590     /// the animation channels, if any
591     std::vector<AnimationChannel> mChannels;
592 
593     /// the sub-animations, if any
594     std::vector<Animation *> mSubAnims;
595 
596     /// Destructor
~AnimationAnimation597     ~Animation() {
598         for (std::vector<Animation *>::iterator it = mSubAnims.begin(); it != mSubAnims.end(); ++it) {
599             delete *it;
600         }
601     }
602 
603     /// Collect all channels in the animation hierarchy into a single channel list.
CollectChannelsRecursivelyAnimation604     void CollectChannelsRecursively(std::vector<AnimationChannel> &channels) {
605         channels.insert(channels.end(), mChannels.begin(), mChannels.end());
606 
607         for (std::vector<Animation *>::iterator it = mSubAnims.begin(); it != mSubAnims.end(); ++it) {
608             Animation *pAnim = (*it);
609             pAnim->CollectChannelsRecursively(channels);
610         }
611     }
612 
613     /// Combine all single-channel animations' channel into the same (parent) animation channel list.
CombineSingleChannelAnimationsAnimation614     void CombineSingleChannelAnimations() {
615         CombineSingleChannelAnimationsRecursively(this);
616     }
617 
CombineSingleChannelAnimationsRecursivelyAnimation618     void CombineSingleChannelAnimationsRecursively(Animation *pParent) {
619         std::set<std::string> childrenTargets;
620         bool childrenAnimationsHaveDifferentChannels = true;
621 
622         for (std::vector<Animation *>::iterator it = pParent->mSubAnims.begin(); it != pParent->mSubAnims.end();) {
623             Animation *anim = *it;
624             CombineSingleChannelAnimationsRecursively(anim);
625 
626             if (childrenAnimationsHaveDifferentChannels && anim->mChannels.size() == 1 &&
627                     childrenTargets.find(anim->mChannels[0].mTarget) == childrenTargets.end()) {
628                 childrenTargets.insert(anim->mChannels[0].mTarget);
629             } else {
630                 childrenAnimationsHaveDifferentChannels = false;
631             }
632 
633             ++it;
634         }
635 
636         // We only want to combine animations if they have different channels
637         if (childrenAnimationsHaveDifferentChannels) {
638             for (std::vector<Animation *>::iterator it = pParent->mSubAnims.begin(); it != pParent->mSubAnims.end();) {
639                 Animation *anim = *it;
640 
641                 pParent->mChannels.push_back(anim->mChannels[0]);
642 
643                 it = pParent->mSubAnims.erase(it);
644 
645                 delete anim;
646                 continue;
647             }
648         }
649     }
650 };
651 
652 /// Description of a collada animation channel which has been determined to affect the current node
653 struct ChannelEntry {
654     const Collada::AnimationChannel *mChannel; ///< the source channel
655     std::string mTargetId;
656     std::string mTransformId; // the ID of the transformation step of the node which is influenced
657     size_t mTransformIndex; // Index into the node's transform chain to apply the channel to
658     size_t mSubElement; // starting index inside the transform data
659 
660     // resolved data references
661     const Collada::Accessor *mTimeAccessor; ///> Collada accessor to the time values
662     const Collada::Data *mTimeData; ///> Source data array for the time values
663     const Collada::Accessor *mValueAccessor; ///> Collada accessor to the key value values
664     const Collada::Data *mValueData; ///> Source datat array for the key value values
665 
ChannelEntryChannelEntry666     ChannelEntry() :
667             mChannel(),
668             mTransformIndex(),
669             mSubElement(),
670             mTimeAccessor(),
671             mTimeData(),
672             mValueAccessor(),
673             mValueData() {}
674 };
675 
676 } // end of namespace Collada
677 } // end of namespace Assimp
678 
679 #endif // AI_COLLADAHELPER_H_INC
680