rev |
line source |
nuclear@0
|
1 /*
|
nuclear@0
|
2 ---------------------------------------------------------------------------
|
nuclear@0
|
3 Open Asset Import Library (assimp)
|
nuclear@0
|
4 ---------------------------------------------------------------------------
|
nuclear@0
|
5
|
nuclear@0
|
6 Copyright (c) 2006-2012, assimp team
|
nuclear@0
|
7
|
nuclear@0
|
8 All rights reserved.
|
nuclear@0
|
9
|
nuclear@0
|
10 Redistribution and use of this software in source and binary forms,
|
nuclear@0
|
11 with or without modification, are permitted provided that the following
|
nuclear@0
|
12 conditions are met:
|
nuclear@0
|
13
|
nuclear@0
|
14 * Redistributions of source code must retain the above
|
nuclear@0
|
15 copyright notice, this list of conditions and the
|
nuclear@0
|
16 following disclaimer.
|
nuclear@0
|
17
|
nuclear@0
|
18 * Redistributions in binary form must reproduce the above
|
nuclear@0
|
19 copyright notice, this list of conditions and the
|
nuclear@0
|
20 following disclaimer in the documentation and/or other
|
nuclear@0
|
21 materials provided with the distribution.
|
nuclear@0
|
22
|
nuclear@0
|
23 * Neither the name of the assimp team, nor the names of its
|
nuclear@0
|
24 contributors may be used to endorse or promote products
|
nuclear@0
|
25 derived from this software without specific prior
|
nuclear@0
|
26 written permission of the assimp team.
|
nuclear@0
|
27
|
nuclear@0
|
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
nuclear@0
|
29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
nuclear@0
|
30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
nuclear@0
|
31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
nuclear@0
|
32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
nuclear@0
|
33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
nuclear@0
|
34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
nuclear@0
|
35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
nuclear@0
|
36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
nuclear@0
|
37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
nuclear@0
|
38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
nuclear@0
|
39 ---------------------------------------------------------------------------
|
nuclear@0
|
40 */
|
nuclear@0
|
41
|
nuclear@0
|
42 /** @file Implementation of the Collada loader */
|
nuclear@0
|
43
|
nuclear@0
|
44 #include "AssimpPCH.h"
|
nuclear@0
|
45 #ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
|
nuclear@0
|
46
|
nuclear@0
|
47 #include "assimp/anim.h"
|
nuclear@0
|
48 #include "ColladaLoader.h"
|
nuclear@0
|
49 #include "ColladaParser.h"
|
nuclear@0
|
50
|
nuclear@0
|
51 #include "fast_atof.h"
|
nuclear@0
|
52 #include "ParsingUtils.h"
|
nuclear@0
|
53 #include "SkeletonMeshBuilder.h"
|
nuclear@0
|
54
|
nuclear@0
|
55 #include "time.h"
|
nuclear@0
|
56
|
nuclear@0
|
57 using namespace Assimp;
|
nuclear@0
|
58
|
nuclear@0
|
59 static const aiImporterDesc desc = {
|
nuclear@0
|
60 "Collada Importer",
|
nuclear@0
|
61 "",
|
nuclear@0
|
62 "",
|
nuclear@0
|
63 "http://collada.org",
|
nuclear@0
|
64 aiImporterFlags_SupportTextFlavour,
|
nuclear@0
|
65 1,
|
nuclear@0
|
66 3,
|
nuclear@0
|
67 1,
|
nuclear@0
|
68 5,
|
nuclear@0
|
69 "dae"
|
nuclear@0
|
70 };
|
nuclear@0
|
71
|
nuclear@0
|
72
|
nuclear@0
|
73 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
74 // Constructor to be privately used by Importer
|
nuclear@0
|
75 ColladaLoader::ColladaLoader()
|
nuclear@0
|
76 : noSkeletonMesh()
|
nuclear@0
|
77 {}
|
nuclear@0
|
78
|
nuclear@0
|
79 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
80 // Destructor, private as well
|
nuclear@0
|
81 ColladaLoader::~ColladaLoader()
|
nuclear@0
|
82 {}
|
nuclear@0
|
83
|
nuclear@0
|
84 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
85 // Returns whether the class can handle the format of the given file.
|
nuclear@0
|
86 bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
|
nuclear@0
|
87 {
|
nuclear@0
|
88 // check file extension
|
nuclear@0
|
89 std::string extension = GetExtension(pFile);
|
nuclear@0
|
90
|
nuclear@0
|
91 if( extension == "dae")
|
nuclear@0
|
92 return true;
|
nuclear@0
|
93
|
nuclear@0
|
94 // XML - too generic, we need to open the file and search for typical keywords
|
nuclear@0
|
95 if( extension == "xml" || !extension.length() || checkSig) {
|
nuclear@0
|
96 /* If CanRead() is called in order to check whether we
|
nuclear@0
|
97 * support a specific file extension in general pIOHandler
|
nuclear@0
|
98 * might be NULL and it's our duty to return true here.
|
nuclear@0
|
99 */
|
nuclear@0
|
100 if (!pIOHandler)return true;
|
nuclear@0
|
101 const char* tokens[] = {"collada"};
|
nuclear@0
|
102 return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
|
nuclear@0
|
103 }
|
nuclear@0
|
104 return false;
|
nuclear@0
|
105 }
|
nuclear@0
|
106
|
nuclear@0
|
107 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
108 void ColladaLoader::SetupProperties(const Importer* pImp)
|
nuclear@0
|
109 {
|
nuclear@0
|
110 noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0;
|
nuclear@0
|
111 }
|
nuclear@0
|
112
|
nuclear@0
|
113
|
nuclear@0
|
114 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
115 // Get file extension list
|
nuclear@0
|
116 const aiImporterDesc* ColladaLoader::GetInfo () const
|
nuclear@0
|
117 {
|
nuclear@0
|
118 return &desc;
|
nuclear@0
|
119 }
|
nuclear@0
|
120
|
nuclear@0
|
121 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
122 // Imports the given file into the given scene structure.
|
nuclear@0
|
123 void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
|
nuclear@0
|
124 {
|
nuclear@0
|
125 mFileName = pFile;
|
nuclear@0
|
126
|
nuclear@0
|
127 // clean all member arrays - just for safety, it should work even if we did not
|
nuclear@0
|
128 mMeshIndexByID.clear();
|
nuclear@0
|
129 mMaterialIndexByName.clear();
|
nuclear@0
|
130 mMeshes.clear();
|
nuclear@0
|
131 newMats.clear();
|
nuclear@0
|
132 mLights.clear();
|
nuclear@0
|
133 mCameras.clear();
|
nuclear@0
|
134 mTextures.clear();
|
nuclear@0
|
135
|
nuclear@0
|
136 // parse the input file
|
nuclear@0
|
137 ColladaParser parser( pIOHandler, pFile);
|
nuclear@0
|
138
|
nuclear@0
|
139 if( !parser.mRootNode)
|
nuclear@0
|
140 throw DeadlyImportError( "Collada: File came out empty. Something is wrong here.");
|
nuclear@0
|
141
|
nuclear@0
|
142 // reserve some storage to avoid unnecessary reallocs
|
nuclear@0
|
143 newMats.reserve(parser.mMaterialLibrary.size()*2);
|
nuclear@0
|
144 mMeshes.reserve(parser.mMeshLibrary.size()*2);
|
nuclear@0
|
145
|
nuclear@0
|
146 mCameras.reserve(parser.mCameraLibrary.size());
|
nuclear@0
|
147 mLights.reserve(parser.mLightLibrary.size());
|
nuclear@0
|
148
|
nuclear@0
|
149 // create the materials first, for the meshes to find
|
nuclear@0
|
150 BuildMaterials( parser, pScene);
|
nuclear@0
|
151
|
nuclear@0
|
152 // build the node hierarchy from it
|
nuclear@0
|
153 pScene->mRootNode = BuildHierarchy( parser, parser.mRootNode);
|
nuclear@0
|
154
|
nuclear@0
|
155 // ... then fill the materials with the now adjusted settings
|
nuclear@0
|
156 FillMaterials(parser, pScene);
|
nuclear@0
|
157
|
nuclear@0
|
158 // Apply unitsize scale calculation
|
nuclear@0
|
159 pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0, 0, 0,
|
nuclear@0
|
160 0, parser.mUnitSize, 0, 0,
|
nuclear@0
|
161 0, 0, parser.mUnitSize, 0,
|
nuclear@0
|
162 0, 0, 0, 1);
|
nuclear@0
|
163
|
nuclear@0
|
164 // Convert to Y_UP, if different orientation
|
nuclear@0
|
165 if( parser.mUpDirection == ColladaParser::UP_X)
|
nuclear@0
|
166 pScene->mRootNode->mTransformation *= aiMatrix4x4(
|
nuclear@0
|
167 0, -1, 0, 0,
|
nuclear@0
|
168 1, 0, 0, 0,
|
nuclear@0
|
169 0, 0, 1, 0,
|
nuclear@0
|
170 0, 0, 0, 1);
|
nuclear@0
|
171 else if( parser.mUpDirection == ColladaParser::UP_Z)
|
nuclear@0
|
172 pScene->mRootNode->mTransformation *= aiMatrix4x4(
|
nuclear@0
|
173 1, 0, 0, 0,
|
nuclear@0
|
174 0, 0, 1, 0,
|
nuclear@0
|
175 0, -1, 0, 0,
|
nuclear@0
|
176 0, 0, 0, 1);
|
nuclear@0
|
177
|
nuclear@0
|
178 // store all meshes
|
nuclear@0
|
179 StoreSceneMeshes( pScene);
|
nuclear@0
|
180
|
nuclear@0
|
181 // store all materials
|
nuclear@0
|
182 StoreSceneMaterials( pScene);
|
nuclear@0
|
183
|
nuclear@0
|
184 // store all lights
|
nuclear@0
|
185 StoreSceneLights( pScene);
|
nuclear@0
|
186
|
nuclear@0
|
187 // store all cameras
|
nuclear@0
|
188 StoreSceneCameras( pScene);
|
nuclear@0
|
189
|
nuclear@0
|
190 // store all animations
|
nuclear@0
|
191 StoreAnimations( pScene, parser);
|
nuclear@0
|
192
|
nuclear@0
|
193
|
nuclear@0
|
194 // If no meshes have been loaded, it's probably just an animated skeleton.
|
nuclear@0
|
195 if (!pScene->mNumMeshes) {
|
nuclear@0
|
196
|
nuclear@0
|
197 if (!noSkeletonMesh) {
|
nuclear@0
|
198 SkeletonMeshBuilder hero(pScene);
|
nuclear@0
|
199 }
|
nuclear@0
|
200 pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
|
nuclear@0
|
201 }
|
nuclear@0
|
202 }
|
nuclear@0
|
203
|
nuclear@0
|
204 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
205 // Recursively constructs a scene node for the given parser node and returns it.
|
nuclear@0
|
206 aiNode* ColladaLoader::BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode)
|
nuclear@0
|
207 {
|
nuclear@0
|
208 // create a node for it
|
nuclear@0
|
209 aiNode* node = new aiNode();
|
nuclear@0
|
210
|
nuclear@0
|
211 // find a name for the new node. It's more complicated than you might think
|
nuclear@0
|
212 node->mName.Set( FindNameForNode( pNode));
|
nuclear@0
|
213
|
nuclear@0
|
214 // calculate the transformation matrix for it
|
nuclear@0
|
215 node->mTransformation = pParser.CalculateResultTransform( pNode->mTransforms);
|
nuclear@0
|
216
|
nuclear@0
|
217 // now resolve node instances
|
nuclear@0
|
218 std::vector<const Collada::Node*> instances;
|
nuclear@0
|
219 ResolveNodeInstances(pParser,pNode,instances);
|
nuclear@0
|
220
|
nuclear@0
|
221 // add children. first the *real* ones
|
nuclear@0
|
222 node->mNumChildren = pNode->mChildren.size()+instances.size();
|
nuclear@0
|
223 node->mChildren = new aiNode*[node->mNumChildren];
|
nuclear@0
|
224
|
nuclear@0
|
225 for( size_t a = 0; a < pNode->mChildren.size(); a++)
|
nuclear@0
|
226 {
|
nuclear@0
|
227 node->mChildren[a] = BuildHierarchy( pParser, pNode->mChildren[a]);
|
nuclear@0
|
228 node->mChildren[a]->mParent = node;
|
nuclear@0
|
229 }
|
nuclear@0
|
230
|
nuclear@0
|
231 // ... and finally the resolved node instances
|
nuclear@0
|
232 for( size_t a = 0; a < instances.size(); a++)
|
nuclear@0
|
233 {
|
nuclear@0
|
234 node->mChildren[pNode->mChildren.size() + a] = BuildHierarchy( pParser, instances[a]);
|
nuclear@0
|
235 node->mChildren[pNode->mChildren.size() + a]->mParent = node;
|
nuclear@0
|
236 }
|
nuclear@0
|
237
|
nuclear@0
|
238 // construct meshes
|
nuclear@0
|
239 BuildMeshesForNode( pParser, pNode, node);
|
nuclear@0
|
240
|
nuclear@0
|
241 // construct cameras
|
nuclear@0
|
242 BuildCamerasForNode(pParser, pNode, node);
|
nuclear@0
|
243
|
nuclear@0
|
244 // construct lights
|
nuclear@0
|
245 BuildLightsForNode(pParser, pNode, node);
|
nuclear@0
|
246 return node;
|
nuclear@0
|
247 }
|
nuclear@0
|
248
|
nuclear@0
|
249 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
250 // Resolve node instances
|
nuclear@0
|
251 void ColladaLoader::ResolveNodeInstances( const ColladaParser& pParser, const Collada::Node* pNode,
|
nuclear@0
|
252 std::vector<const Collada::Node*>& resolved)
|
nuclear@0
|
253 {
|
nuclear@0
|
254 // reserve enough storage
|
nuclear@0
|
255 resolved.reserve(pNode->mNodeInstances.size());
|
nuclear@0
|
256
|
nuclear@0
|
257 // ... and iterate through all nodes to be instanced as children of pNode
|
nuclear@0
|
258 for (std::vector<Collada::NodeInstance>::const_iterator it = pNode->mNodeInstances.begin(),
|
nuclear@0
|
259 end = pNode->mNodeInstances.end(); it != end; ++it)
|
nuclear@0
|
260 {
|
nuclear@0
|
261 // find the corresponding node in the library
|
nuclear@0
|
262 const ColladaParser::NodeLibrary::const_iterator itt = pParser.mNodeLibrary.find((*it).mNode);
|
nuclear@0
|
263 const Collada::Node* nd = itt == pParser.mNodeLibrary.end() ? NULL : (*itt).second;
|
nuclear@0
|
264
|
nuclear@0
|
265 // FIX for http://sourceforge.net/tracker/?func=detail&aid=3054873&group_id=226462&atid=1067632
|
nuclear@0
|
266 // need to check for both name and ID to catch all. To avoid breaking valid files,
|
nuclear@0
|
267 // the workaround is only enabled when the first attempt to resolve the node has failed.
|
nuclear@0
|
268 if (!nd) {
|
nuclear@0
|
269 nd = FindNode(pParser.mRootNode,(*it).mNode);
|
nuclear@0
|
270 }
|
nuclear@0
|
271 if (!nd)
|
nuclear@0
|
272 DefaultLogger::get()->error("Collada: Unable to resolve reference to instanced node " + (*it).mNode);
|
nuclear@0
|
273
|
nuclear@0
|
274 else {
|
nuclear@0
|
275 // attach this node to the list of children
|
nuclear@0
|
276 resolved.push_back(nd);
|
nuclear@0
|
277 }
|
nuclear@0
|
278 }
|
nuclear@0
|
279 }
|
nuclear@0
|
280
|
nuclear@0
|
281 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
282 // Resolve UV channels
|
nuclear@0
|
283 void ColladaLoader::ApplyVertexToEffectSemanticMapping(Collada::Sampler& sampler,
|
nuclear@0
|
284 const Collada::SemanticMappingTable& table)
|
nuclear@0
|
285 {
|
nuclear@0
|
286 std::map<std::string, Collada::InputSemanticMapEntry>::const_iterator it = table.mMap.find(sampler.mUVChannel);
|
nuclear@0
|
287 if (it != table.mMap.end()) {
|
nuclear@0
|
288 if (it->second.mType != Collada::IT_Texcoord)
|
nuclear@0
|
289 DefaultLogger::get()->error("Collada: Unexpected effect input mapping");
|
nuclear@0
|
290
|
nuclear@0
|
291 sampler.mUVId = it->second.mSet;
|
nuclear@0
|
292 }
|
nuclear@0
|
293 }
|
nuclear@0
|
294
|
nuclear@0
|
295 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
296 // Builds lights for the given node and references them
|
nuclear@0
|
297 void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
|
nuclear@0
|
298 {
|
nuclear@0
|
299 BOOST_FOREACH( const Collada::LightInstance& lid, pNode->mLights)
|
nuclear@0
|
300 {
|
nuclear@0
|
301 // find the referred light
|
nuclear@0
|
302 ColladaParser::LightLibrary::const_iterator srcLightIt = pParser.mLightLibrary.find( lid.mLight);
|
nuclear@0
|
303 if( srcLightIt == pParser.mLightLibrary.end())
|
nuclear@0
|
304 {
|
nuclear@0
|
305 DefaultLogger::get()->warn("Collada: Unable to find light for ID \"" + lid.mLight + "\". Skipping.");
|
nuclear@0
|
306 continue;
|
nuclear@0
|
307 }
|
nuclear@0
|
308 const Collada::Light* srcLight = &srcLightIt->second;
|
nuclear@0
|
309 if (srcLight->mType == aiLightSource_AMBIENT) {
|
nuclear@0
|
310 DefaultLogger::get()->error("Collada: Skipping ambient light for the moment");
|
nuclear@0
|
311 continue;
|
nuclear@0
|
312 }
|
nuclear@0
|
313
|
nuclear@0
|
314 // now fill our ai data structure
|
nuclear@0
|
315 aiLight* out = new aiLight();
|
nuclear@0
|
316 out->mName = pTarget->mName;
|
nuclear@0
|
317 out->mType = (aiLightSourceType)srcLight->mType;
|
nuclear@0
|
318
|
nuclear@0
|
319 // collada lights point in -Z by default, rest is specified in node transform
|
nuclear@0
|
320 out->mDirection = aiVector3D(0.f,0.f,-1.f);
|
nuclear@0
|
321
|
nuclear@0
|
322 out->mAttenuationConstant = srcLight->mAttConstant;
|
nuclear@0
|
323 out->mAttenuationLinear = srcLight->mAttLinear;
|
nuclear@0
|
324 out->mAttenuationQuadratic = srcLight->mAttQuadratic;
|
nuclear@0
|
325
|
nuclear@0
|
326 // collada doesn't differenciate between these color types
|
nuclear@0
|
327 out->mColorDiffuse = out->mColorSpecular = out->mColorAmbient = srcLight->mColor*srcLight->mIntensity;
|
nuclear@0
|
328
|
nuclear@0
|
329 // convert falloff angle and falloff exponent in our representation, if given
|
nuclear@0
|
330 if (out->mType == aiLightSource_SPOT) {
|
nuclear@0
|
331
|
nuclear@0
|
332 out->mAngleInnerCone = AI_DEG_TO_RAD( srcLight->mFalloffAngle );
|
nuclear@0
|
333
|
nuclear@0
|
334 // ... some extension magic.
|
nuclear@0
|
335 if (srcLight->mOuterAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET*(1-1e-6f))
|
nuclear@0
|
336 {
|
nuclear@0
|
337 // ... some deprecation magic.
|
nuclear@0
|
338 if (srcLight->mPenumbraAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET*(1-1e-6f))
|
nuclear@0
|
339 {
|
nuclear@0
|
340 // Need to rely on falloff_exponent. I don't know how to interpret it, so I need to guess ....
|
nuclear@0
|
341 // epsilon chosen to be 0.1
|
nuclear@0
|
342 out->mAngleOuterCone = AI_DEG_TO_RAD (acos(pow(0.1f,1.f/srcLight->mFalloffExponent))+
|
nuclear@0
|
343 srcLight->mFalloffAngle);
|
nuclear@0
|
344 }
|
nuclear@0
|
345 else {
|
nuclear@0
|
346 out->mAngleOuterCone = out->mAngleInnerCone + AI_DEG_TO_RAD( srcLight->mPenumbraAngle );
|
nuclear@0
|
347 if (out->mAngleOuterCone < out->mAngleInnerCone)
|
nuclear@0
|
348 std::swap(out->mAngleInnerCone,out->mAngleOuterCone);
|
nuclear@0
|
349 }
|
nuclear@0
|
350 }
|
nuclear@0
|
351 else out->mAngleOuterCone = AI_DEG_TO_RAD( srcLight->mOuterAngle );
|
nuclear@0
|
352 }
|
nuclear@0
|
353
|
nuclear@0
|
354 // add to light list
|
nuclear@0
|
355 mLights.push_back(out);
|
nuclear@0
|
356 }
|
nuclear@0
|
357 }
|
nuclear@0
|
358
|
nuclear@0
|
359 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
360 // Builds cameras for the given node and references them
|
nuclear@0
|
361 void ColladaLoader::BuildCamerasForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
|
nuclear@0
|
362 {
|
nuclear@0
|
363 BOOST_FOREACH( const Collada::CameraInstance& cid, pNode->mCameras)
|
nuclear@0
|
364 {
|
nuclear@0
|
365 // find the referred light
|
nuclear@0
|
366 ColladaParser::CameraLibrary::const_iterator srcCameraIt = pParser.mCameraLibrary.find( cid.mCamera);
|
nuclear@0
|
367 if( srcCameraIt == pParser.mCameraLibrary.end())
|
nuclear@0
|
368 {
|
nuclear@0
|
369 DefaultLogger::get()->warn("Collada: Unable to find camera for ID \"" + cid.mCamera + "\". Skipping.");
|
nuclear@0
|
370 continue;
|
nuclear@0
|
371 }
|
nuclear@0
|
372 const Collada::Camera* srcCamera = &srcCameraIt->second;
|
nuclear@0
|
373
|
nuclear@0
|
374 // orthographic cameras not yet supported in Assimp
|
nuclear@0
|
375 if (srcCamera->mOrtho) {
|
nuclear@0
|
376 DefaultLogger::get()->warn("Collada: Orthographic cameras are not supported.");
|
nuclear@0
|
377 }
|
nuclear@0
|
378
|
nuclear@0
|
379 // now fill our ai data structure
|
nuclear@0
|
380 aiCamera* out = new aiCamera();
|
nuclear@0
|
381 out->mName = pTarget->mName;
|
nuclear@0
|
382
|
nuclear@0
|
383 // collada cameras point in -Z by default, rest is specified in node transform
|
nuclear@0
|
384 out->mLookAt = aiVector3D(0.f,0.f,-1.f);
|
nuclear@0
|
385
|
nuclear@0
|
386 // near/far z is already ok
|
nuclear@0
|
387 out->mClipPlaneFar = srcCamera->mZFar;
|
nuclear@0
|
388 out->mClipPlaneNear = srcCamera->mZNear;
|
nuclear@0
|
389
|
nuclear@0
|
390 // ... but for the rest some values are optional
|
nuclear@0
|
391 // and we need to compute the others in any combination.
|
nuclear@0
|
392 if (srcCamera->mAspect != 10e10f)
|
nuclear@0
|
393 out->mAspect = srcCamera->mAspect;
|
nuclear@0
|
394
|
nuclear@0
|
395 if (srcCamera->mHorFov != 10e10f) {
|
nuclear@0
|
396 out->mHorizontalFOV = srcCamera->mHorFov;
|
nuclear@0
|
397
|
nuclear@0
|
398 if (srcCamera->mVerFov != 10e10f && srcCamera->mAspect == 10e10f) {
|
nuclear@0
|
399 out->mAspect = tan(AI_DEG_TO_RAD(srcCamera->mHorFov)) /
|
nuclear@0
|
400 tan(AI_DEG_TO_RAD(srcCamera->mVerFov));
|
nuclear@0
|
401 }
|
nuclear@0
|
402 }
|
nuclear@0
|
403 else if (srcCamera->mAspect != 10e10f && srcCamera->mVerFov != 10e10f) {
|
nuclear@0
|
404 out->mHorizontalFOV = 2.0f * AI_RAD_TO_DEG(atan(srcCamera->mAspect *
|
nuclear@0
|
405 tan(AI_DEG_TO_RAD(srcCamera->mVerFov) * 0.5f)));
|
nuclear@0
|
406 }
|
nuclear@0
|
407
|
nuclear@0
|
408 // Collada uses degrees, we use radians
|
nuclear@0
|
409 out->mHorizontalFOV = AI_DEG_TO_RAD(out->mHorizontalFOV);
|
nuclear@0
|
410
|
nuclear@0
|
411 // add to camera list
|
nuclear@0
|
412 mCameras.push_back(out);
|
nuclear@0
|
413 }
|
nuclear@0
|
414 }
|
nuclear@0
|
415
|
nuclear@0
|
416 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
417 // Builds meshes for the given node and references them
|
nuclear@0
|
418 void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
|
nuclear@0
|
419 {
|
nuclear@0
|
420 // accumulated mesh references by this node
|
nuclear@0
|
421 std::vector<size_t> newMeshRefs;
|
nuclear@0
|
422 newMeshRefs.reserve(pNode->mMeshes.size());
|
nuclear@0
|
423
|
nuclear@0
|
424 // add a mesh for each subgroup in each collada mesh
|
nuclear@0
|
425 BOOST_FOREACH( const Collada::MeshInstance& mid, pNode->mMeshes)
|
nuclear@0
|
426 {
|
nuclear@0
|
427 const Collada::Mesh* srcMesh = NULL;
|
nuclear@0
|
428 const Collada::Controller* srcController = NULL;
|
nuclear@0
|
429
|
nuclear@0
|
430 // find the referred mesh
|
nuclear@0
|
431 ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find( mid.mMeshOrController);
|
nuclear@0
|
432 if( srcMeshIt == pParser.mMeshLibrary.end())
|
nuclear@0
|
433 {
|
nuclear@0
|
434 // if not found in the mesh-library, it might also be a controller referring to a mesh
|
nuclear@0
|
435 ColladaParser::ControllerLibrary::const_iterator srcContrIt = pParser.mControllerLibrary.find( mid.mMeshOrController);
|
nuclear@0
|
436 if( srcContrIt != pParser.mControllerLibrary.end())
|
nuclear@0
|
437 {
|
nuclear@0
|
438 srcController = &srcContrIt->second;
|
nuclear@0
|
439 srcMeshIt = pParser.mMeshLibrary.find( srcController->mMeshId);
|
nuclear@0
|
440 if( srcMeshIt != pParser.mMeshLibrary.end())
|
nuclear@0
|
441 srcMesh = srcMeshIt->second;
|
nuclear@0
|
442 }
|
nuclear@0
|
443
|
nuclear@0
|
444 if( !srcMesh)
|
nuclear@0
|
445 {
|
nuclear@0
|
446 DefaultLogger::get()->warn( boost::str( boost::format( "Collada: Unable to find geometry for ID \"%s\". Skipping.") % mid.mMeshOrController));
|
nuclear@0
|
447 continue;
|
nuclear@0
|
448 }
|
nuclear@0
|
449 } else
|
nuclear@0
|
450 {
|
nuclear@0
|
451 // ID found in the mesh library -> direct reference to an unskinned mesh
|
nuclear@0
|
452 srcMesh = srcMeshIt->second;
|
nuclear@0
|
453 }
|
nuclear@0
|
454
|
nuclear@0
|
455 // build a mesh for each of its subgroups
|
nuclear@0
|
456 size_t vertexStart = 0, faceStart = 0;
|
nuclear@0
|
457 for( size_t sm = 0; sm < srcMesh->mSubMeshes.size(); ++sm)
|
nuclear@0
|
458 {
|
nuclear@0
|
459 const Collada::SubMesh& submesh = srcMesh->mSubMeshes[sm];
|
nuclear@0
|
460 if( submesh.mNumFaces == 0)
|
nuclear@0
|
461 continue;
|
nuclear@0
|
462
|
nuclear@0
|
463 // find material assigned to this submesh
|
nuclear@0
|
464 std::string meshMaterial;
|
nuclear@0
|
465 std::map<std::string, Collada::SemanticMappingTable >::const_iterator meshMatIt = mid.mMaterials.find( submesh.mMaterial);
|
nuclear@0
|
466
|
nuclear@0
|
467 const Collada::SemanticMappingTable* table = NULL;
|
nuclear@0
|
468 if( meshMatIt != mid.mMaterials.end())
|
nuclear@0
|
469 {
|
nuclear@0
|
470 table = &meshMatIt->second;
|
nuclear@0
|
471 meshMaterial = table->mMatName;
|
nuclear@0
|
472 }
|
nuclear@0
|
473 else
|
nuclear@0
|
474 {
|
nuclear@0
|
475 DefaultLogger::get()->warn( boost::str( boost::format( "Collada: No material specified for subgroup <%s> in geometry <%s>.") % submesh.mMaterial % mid.mMeshOrController));
|
nuclear@0
|
476 if( !mid.mMaterials.empty() )
|
nuclear@0
|
477 meshMaterial = mid.mMaterials.begin()->second.mMatName;
|
nuclear@0
|
478 }
|
nuclear@0
|
479
|
nuclear@0
|
480 // OK ... here the *real* fun starts ... we have the vertex-input-to-effect-semantic-table
|
nuclear@0
|
481 // given. The only mapping stuff which we do actually support is the UV channel.
|
nuclear@0
|
482 std::map<std::string, size_t>::const_iterator matIt = mMaterialIndexByName.find( meshMaterial);
|
nuclear@0
|
483 unsigned int matIdx;
|
nuclear@0
|
484 if( matIt != mMaterialIndexByName.end())
|
nuclear@0
|
485 matIdx = matIt->second;
|
nuclear@0
|
486 else
|
nuclear@0
|
487 matIdx = 0;
|
nuclear@0
|
488
|
nuclear@0
|
489 if (table && !table->mMap.empty() ) {
|
nuclear@0
|
490 std::pair<Collada::Effect*, aiMaterial*>& mat = newMats[matIdx];
|
nuclear@0
|
491
|
nuclear@0
|
492 // Iterate through all texture channels assigned to the effect and
|
nuclear@0
|
493 // check whether we have mapping information for it.
|
nuclear@0
|
494 ApplyVertexToEffectSemanticMapping(mat.first->mTexDiffuse, *table);
|
nuclear@0
|
495 ApplyVertexToEffectSemanticMapping(mat.first->mTexAmbient, *table);
|
nuclear@0
|
496 ApplyVertexToEffectSemanticMapping(mat.first->mTexSpecular, *table);
|
nuclear@0
|
497 ApplyVertexToEffectSemanticMapping(mat.first->mTexEmissive, *table);
|
nuclear@0
|
498 ApplyVertexToEffectSemanticMapping(mat.first->mTexTransparent,*table);
|
nuclear@0
|
499 ApplyVertexToEffectSemanticMapping(mat.first->mTexBump, *table);
|
nuclear@0
|
500 }
|
nuclear@0
|
501
|
nuclear@0
|
502 // built lookup index of the Mesh-Submesh-Material combination
|
nuclear@0
|
503 ColladaMeshIndex index( mid.mMeshOrController, sm, meshMaterial);
|
nuclear@0
|
504
|
nuclear@0
|
505 // if we already have the mesh at the library, just add its index to the node's array
|
nuclear@0
|
506 std::map<ColladaMeshIndex, size_t>::const_iterator dstMeshIt = mMeshIndexByID.find( index);
|
nuclear@0
|
507 if( dstMeshIt != mMeshIndexByID.end()) {
|
nuclear@0
|
508 newMeshRefs.push_back( dstMeshIt->second);
|
nuclear@0
|
509 }
|
nuclear@0
|
510 else
|
nuclear@0
|
511 {
|
nuclear@0
|
512 // else we have to add the mesh to the collection and store its newly assigned index at the node
|
nuclear@0
|
513 aiMesh* dstMesh = CreateMesh( pParser, srcMesh, submesh, srcController, vertexStart, faceStart);
|
nuclear@0
|
514
|
nuclear@0
|
515 // store the mesh, and store its new index in the node
|
nuclear@0
|
516 newMeshRefs.push_back( mMeshes.size());
|
nuclear@0
|
517 mMeshIndexByID[index] = mMeshes.size();
|
nuclear@0
|
518 mMeshes.push_back( dstMesh);
|
nuclear@0
|
519 vertexStart += dstMesh->mNumVertices; faceStart += submesh.mNumFaces;
|
nuclear@0
|
520
|
nuclear@0
|
521 // assign the material index
|
nuclear@0
|
522 dstMesh->mMaterialIndex = matIdx;
|
nuclear@0
|
523 dstMesh->mName = mid.mMeshOrController;
|
nuclear@0
|
524 }
|
nuclear@0
|
525 }
|
nuclear@0
|
526 }
|
nuclear@0
|
527
|
nuclear@0
|
528 // now place all mesh references we gathered in the target node
|
nuclear@0
|
529 pTarget->mNumMeshes = newMeshRefs.size();
|
nuclear@0
|
530 if( newMeshRefs.size())
|
nuclear@0
|
531 {
|
nuclear@0
|
532 pTarget->mMeshes = new unsigned int[pTarget->mNumMeshes];
|
nuclear@0
|
533 std::copy( newMeshRefs.begin(), newMeshRefs.end(), pTarget->mMeshes);
|
nuclear@0
|
534 }
|
nuclear@0
|
535 }
|
nuclear@0
|
536
|
nuclear@0
|
537 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
538 // Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh
|
nuclear@0
|
539 aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh,
|
nuclear@0
|
540 const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace)
|
nuclear@0
|
541 {
|
nuclear@0
|
542 aiMesh* dstMesh = new aiMesh;
|
nuclear@0
|
543
|
nuclear@0
|
544 // count the vertices addressed by its faces
|
nuclear@0
|
545 const size_t numVertices = std::accumulate( pSrcMesh->mFaceSize.begin() + pStartFace,
|
nuclear@0
|
546 pSrcMesh->mFaceSize.begin() + pStartFace + pSubMesh.mNumFaces, 0);
|
nuclear@0
|
547
|
nuclear@0
|
548 // copy positions
|
nuclear@0
|
549 dstMesh->mNumVertices = numVertices;
|
nuclear@0
|
550 dstMesh->mVertices = new aiVector3D[numVertices];
|
nuclear@0
|
551 std::copy( pSrcMesh->mPositions.begin() + pStartVertex, pSrcMesh->mPositions.begin() +
|
nuclear@0
|
552 pStartVertex + numVertices, dstMesh->mVertices);
|
nuclear@0
|
553
|
nuclear@0
|
554 // normals, if given. HACK: (thom) Due to the glorious Collada spec we never
|
nuclear@0
|
555 // know if we have the same number of normals as there are positions. So we
|
nuclear@0
|
556 // also ignore any vertex attribute if it has a different count
|
nuclear@0
|
557 if( pSrcMesh->mNormals.size() >= pStartVertex + numVertices)
|
nuclear@0
|
558 {
|
nuclear@0
|
559 dstMesh->mNormals = new aiVector3D[numVertices];
|
nuclear@0
|
560 std::copy( pSrcMesh->mNormals.begin() + pStartVertex, pSrcMesh->mNormals.begin() +
|
nuclear@0
|
561 pStartVertex + numVertices, dstMesh->mNormals);
|
nuclear@0
|
562 }
|
nuclear@0
|
563
|
nuclear@0
|
564 // tangents, if given.
|
nuclear@0
|
565 if( pSrcMesh->mTangents.size() >= pStartVertex + numVertices)
|
nuclear@0
|
566 {
|
nuclear@0
|
567 dstMesh->mTangents = new aiVector3D[numVertices];
|
nuclear@0
|
568 std::copy( pSrcMesh->mTangents.begin() + pStartVertex, pSrcMesh->mTangents.begin() +
|
nuclear@0
|
569 pStartVertex + numVertices, dstMesh->mTangents);
|
nuclear@0
|
570 }
|
nuclear@0
|
571
|
nuclear@0
|
572 // bitangents, if given.
|
nuclear@0
|
573 if( pSrcMesh->mBitangents.size() >= pStartVertex + numVertices)
|
nuclear@0
|
574 {
|
nuclear@0
|
575 dstMesh->mBitangents = new aiVector3D[numVertices];
|
nuclear@0
|
576 std::copy( pSrcMesh->mBitangents.begin() + pStartVertex, pSrcMesh->mBitangents.begin() +
|
nuclear@0
|
577 pStartVertex + numVertices, dstMesh->mBitangents);
|
nuclear@0
|
578 }
|
nuclear@0
|
579
|
nuclear@0
|
580 // same for texturecoords, as many as we have
|
nuclear@0
|
581 // empty slots are not allowed, need to pack and adjust UV indexes accordingly
|
nuclear@0
|
582 for( size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)
|
nuclear@0
|
583 {
|
nuclear@0
|
584 if( pSrcMesh->mTexCoords[a].size() >= pStartVertex + numVertices)
|
nuclear@0
|
585 {
|
nuclear@0
|
586 dstMesh->mTextureCoords[real] = new aiVector3D[numVertices];
|
nuclear@0
|
587 for( size_t b = 0; b < numVertices; ++b)
|
nuclear@0
|
588 dstMesh->mTextureCoords[real][b] = pSrcMesh->mTexCoords[a][pStartVertex+b];
|
nuclear@0
|
589
|
nuclear@0
|
590 dstMesh->mNumUVComponents[real] = pSrcMesh->mNumUVComponents[a];
|
nuclear@0
|
591 ++real;
|
nuclear@0
|
592 }
|
nuclear@0
|
593 }
|
nuclear@0
|
594
|
nuclear@0
|
595 // same for vertex colors, as many as we have. again the same packing to avoid empty slots
|
nuclear@0
|
596 for( size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++)
|
nuclear@0
|
597 {
|
nuclear@0
|
598 if( pSrcMesh->mColors[a].size() >= pStartVertex + numVertices)
|
nuclear@0
|
599 {
|
nuclear@0
|
600 dstMesh->mColors[real] = new aiColor4D[numVertices];
|
nuclear@0
|
601 std::copy( pSrcMesh->mColors[a].begin() + pStartVertex, pSrcMesh->mColors[a].begin() + pStartVertex + numVertices,dstMesh->mColors[real]);
|
nuclear@0
|
602 ++real;
|
nuclear@0
|
603 }
|
nuclear@0
|
604 }
|
nuclear@0
|
605
|
nuclear@0
|
606 // create faces. Due to the fact that each face uses unique vertices, we can simply count up on each vertex
|
nuclear@0
|
607 size_t vertex = 0;
|
nuclear@0
|
608 dstMesh->mNumFaces = pSubMesh.mNumFaces;
|
nuclear@0
|
609 dstMesh->mFaces = new aiFace[dstMesh->mNumFaces];
|
nuclear@0
|
610 for( size_t a = 0; a < dstMesh->mNumFaces; ++a)
|
nuclear@0
|
611 {
|
nuclear@0
|
612 size_t s = pSrcMesh->mFaceSize[ pStartFace + a];
|
nuclear@0
|
613 aiFace& face = dstMesh->mFaces[a];
|
nuclear@0
|
614 face.mNumIndices = s;
|
nuclear@0
|
615 face.mIndices = new unsigned int[s];
|
nuclear@0
|
616 for( size_t b = 0; b < s; ++b)
|
nuclear@0
|
617 face.mIndices[b] = vertex++;
|
nuclear@0
|
618 }
|
nuclear@0
|
619
|
nuclear@0
|
620 // create bones if given
|
nuclear@0
|
621 if( pSrcController)
|
nuclear@0
|
622 {
|
nuclear@0
|
623 // refuse if the vertex count does not match
|
nuclear@0
|
624 // if( pSrcController->mWeightCounts.size() != dstMesh->mNumVertices)
|
nuclear@0
|
625 // throw DeadlyImportError( "Joint Controller vertex count does not match mesh vertex count");
|
nuclear@0
|
626
|
nuclear@0
|
627 // resolve references - joint names
|
nuclear@0
|
628 const Collada::Accessor& jointNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointNameSource);
|
nuclear@0
|
629 const Collada::Data& jointNames = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointNamesAcc.mSource);
|
nuclear@0
|
630 // joint offset matrices
|
nuclear@0
|
631 const Collada::Accessor& jointMatrixAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointOffsetMatrixSource);
|
nuclear@0
|
632 const Collada::Data& jointMatrices = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointMatrixAcc.mSource);
|
nuclear@0
|
633 // joint vertex_weight name list - should refer to the same list as the joint names above. If not, report and reconsider
|
nuclear@0
|
634 const Collada::Accessor& weightNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputJoints.mAccessor);
|
nuclear@0
|
635 if( &weightNamesAcc != &jointNamesAcc)
|
nuclear@0
|
636 throw DeadlyImportError( "Temporary implementational lazyness. If you read this, please report to the author.");
|
nuclear@0
|
637 // vertex weights
|
nuclear@0
|
638 const Collada::Accessor& weightsAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputWeights.mAccessor);
|
nuclear@0
|
639 const Collada::Data& weights = pParser.ResolveLibraryReference( pParser.mDataLibrary, weightsAcc.mSource);
|
nuclear@0
|
640
|
nuclear@0
|
641 if( !jointNames.mIsStringArray || jointMatrices.mIsStringArray || weights.mIsStringArray)
|
nuclear@0
|
642 throw DeadlyImportError( "Data type mismatch while resolving mesh joints");
|
nuclear@0
|
643 // sanity check: we rely on the vertex weights always coming as pairs of BoneIndex-WeightIndex
|
nuclear@0
|
644 if( pSrcController->mWeightInputJoints.mOffset != 0 || pSrcController->mWeightInputWeights.mOffset != 1)
|
nuclear@0
|
645 throw DeadlyImportError( "Unsupported vertex_weight addressing scheme. ");
|
nuclear@0
|
646
|
nuclear@0
|
647 // create containers to collect the weights for each bone
|
nuclear@0
|
648 size_t numBones = jointNames.mStrings.size();
|
nuclear@0
|
649 std::vector<std::vector<aiVertexWeight> > dstBones( numBones);
|
nuclear@0
|
650
|
nuclear@0
|
651 // build a temporary array of pointers to the start of each vertex's weights
|
nuclear@0
|
652 typedef std::vector< std::pair<size_t, size_t> > IndexPairVector;
|
nuclear@0
|
653 std::vector<IndexPairVector::const_iterator> weightStartPerVertex;
|
nuclear@0
|
654 weightStartPerVertex.resize(pSrcController->mWeightCounts.size(),pSrcController->mWeights.end());
|
nuclear@0
|
655
|
nuclear@0
|
656 IndexPairVector::const_iterator pit = pSrcController->mWeights.begin();
|
nuclear@0
|
657 for( size_t a = 0; a < pSrcController->mWeightCounts.size(); ++a)
|
nuclear@0
|
658 {
|
nuclear@0
|
659 weightStartPerVertex[a] = pit;
|
nuclear@0
|
660 pit += pSrcController->mWeightCounts[a];
|
nuclear@0
|
661 }
|
nuclear@0
|
662
|
nuclear@0
|
663 // now for each vertex put the corresponding vertex weights into each bone's weight collection
|
nuclear@0
|
664 for( size_t a = pStartVertex; a < pStartVertex + numVertices; ++a)
|
nuclear@0
|
665 {
|
nuclear@0
|
666 // which position index was responsible for this vertex? that's also the index by which
|
nuclear@0
|
667 // the controller assigns the vertex weights
|
nuclear@0
|
668 size_t orgIndex = pSrcMesh->mFacePosIndices[a];
|
nuclear@0
|
669 // find the vertex weights for this vertex
|
nuclear@0
|
670 IndexPairVector::const_iterator iit = weightStartPerVertex[orgIndex];
|
nuclear@0
|
671 size_t pairCount = pSrcController->mWeightCounts[orgIndex];
|
nuclear@0
|
672
|
nuclear@0
|
673 for( size_t b = 0; b < pairCount; ++b, ++iit)
|
nuclear@0
|
674 {
|
nuclear@0
|
675 size_t jointIndex = iit->first;
|
nuclear@0
|
676 size_t vertexIndex = iit->second;
|
nuclear@0
|
677
|
nuclear@0
|
678 float weight = ReadFloat( weightsAcc, weights, vertexIndex, 0);
|
nuclear@0
|
679
|
nuclear@0
|
680 // one day I gonna kill that XSI Collada exporter
|
nuclear@0
|
681 if( weight > 0.0f)
|
nuclear@0
|
682 {
|
nuclear@0
|
683 aiVertexWeight w;
|
nuclear@0
|
684 w.mVertexId = a - pStartVertex;
|
nuclear@0
|
685 w.mWeight = weight;
|
nuclear@0
|
686 dstBones[jointIndex].push_back( w);
|
nuclear@0
|
687 }
|
nuclear@0
|
688 }
|
nuclear@0
|
689 }
|
nuclear@0
|
690
|
nuclear@0
|
691 // count the number of bones which influence vertices of the current submesh
|
nuclear@0
|
692 size_t numRemainingBones = 0;
|
nuclear@0
|
693 for( std::vector<std::vector<aiVertexWeight> >::const_iterator it = dstBones.begin(); it != dstBones.end(); ++it)
|
nuclear@0
|
694 if( it->size() > 0)
|
nuclear@0
|
695 numRemainingBones++;
|
nuclear@0
|
696
|
nuclear@0
|
697 // create bone array and copy bone weights one by one
|
nuclear@0
|
698 dstMesh->mNumBones = numRemainingBones;
|
nuclear@0
|
699 dstMesh->mBones = new aiBone*[numRemainingBones];
|
nuclear@0
|
700 size_t boneCount = 0;
|
nuclear@0
|
701 for( size_t a = 0; a < numBones; ++a)
|
nuclear@0
|
702 {
|
nuclear@0
|
703 // omit bones without weights
|
nuclear@0
|
704 if( dstBones[a].size() == 0)
|
nuclear@0
|
705 continue;
|
nuclear@0
|
706
|
nuclear@0
|
707 // create bone with its weights
|
nuclear@0
|
708 aiBone* bone = new aiBone;
|
nuclear@0
|
709 bone->mName = ReadString( jointNamesAcc, jointNames, a);
|
nuclear@0
|
710 bone->mOffsetMatrix.a1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 0);
|
nuclear@0
|
711 bone->mOffsetMatrix.a2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 1);
|
nuclear@0
|
712 bone->mOffsetMatrix.a3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 2);
|
nuclear@0
|
713 bone->mOffsetMatrix.a4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 3);
|
nuclear@0
|
714 bone->mOffsetMatrix.b1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 4);
|
nuclear@0
|
715 bone->mOffsetMatrix.b2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 5);
|
nuclear@0
|
716 bone->mOffsetMatrix.b3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 6);
|
nuclear@0
|
717 bone->mOffsetMatrix.b4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 7);
|
nuclear@0
|
718 bone->mOffsetMatrix.c1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 8);
|
nuclear@0
|
719 bone->mOffsetMatrix.c2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 9);
|
nuclear@0
|
720 bone->mOffsetMatrix.c3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 10);
|
nuclear@0
|
721 bone->mOffsetMatrix.c4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 11);
|
nuclear@0
|
722 bone->mNumWeights = dstBones[a].size();
|
nuclear@0
|
723 bone->mWeights = new aiVertexWeight[bone->mNumWeights];
|
nuclear@0
|
724 std::copy( dstBones[a].begin(), dstBones[a].end(), bone->mWeights);
|
nuclear@0
|
725
|
nuclear@0
|
726 // apply bind shape matrix to offset matrix
|
nuclear@0
|
727 aiMatrix4x4 bindShapeMatrix;
|
nuclear@0
|
728 bindShapeMatrix.a1 = pSrcController->mBindShapeMatrix[0];
|
nuclear@0
|
729 bindShapeMatrix.a2 = pSrcController->mBindShapeMatrix[1];
|
nuclear@0
|
730 bindShapeMatrix.a3 = pSrcController->mBindShapeMatrix[2];
|
nuclear@0
|
731 bindShapeMatrix.a4 = pSrcController->mBindShapeMatrix[3];
|
nuclear@0
|
732 bindShapeMatrix.b1 = pSrcController->mBindShapeMatrix[4];
|
nuclear@0
|
733 bindShapeMatrix.b2 = pSrcController->mBindShapeMatrix[5];
|
nuclear@0
|
734 bindShapeMatrix.b3 = pSrcController->mBindShapeMatrix[6];
|
nuclear@0
|
735 bindShapeMatrix.b4 = pSrcController->mBindShapeMatrix[7];
|
nuclear@0
|
736 bindShapeMatrix.c1 = pSrcController->mBindShapeMatrix[8];
|
nuclear@0
|
737 bindShapeMatrix.c2 = pSrcController->mBindShapeMatrix[9];
|
nuclear@0
|
738 bindShapeMatrix.c3 = pSrcController->mBindShapeMatrix[10];
|
nuclear@0
|
739 bindShapeMatrix.c4 = pSrcController->mBindShapeMatrix[11];
|
nuclear@0
|
740 bindShapeMatrix.d1 = pSrcController->mBindShapeMatrix[12];
|
nuclear@0
|
741 bindShapeMatrix.d2 = pSrcController->mBindShapeMatrix[13];
|
nuclear@0
|
742 bindShapeMatrix.d3 = pSrcController->mBindShapeMatrix[14];
|
nuclear@0
|
743 bindShapeMatrix.d4 = pSrcController->mBindShapeMatrix[15];
|
nuclear@0
|
744 bone->mOffsetMatrix *= bindShapeMatrix;
|
nuclear@0
|
745
|
nuclear@0
|
746 // HACK: (thom) Some exporters address the bone nodes by SID, others address them by ID or even name.
|
nuclear@0
|
747 // Therefore I added a little name replacement here: I search for the bone's node by either name, ID or SID,
|
nuclear@0
|
748 // and replace the bone's name by the node's name so that the user can use the standard
|
nuclear@0
|
749 // find-by-name method to associate nodes with bones.
|
nuclear@0
|
750 const Collada::Node* bnode = FindNode( pParser.mRootNode, bone->mName.data);
|
nuclear@0
|
751 if( !bnode)
|
nuclear@0
|
752 bnode = FindNodeBySID( pParser.mRootNode, bone->mName.data);
|
nuclear@0
|
753
|
nuclear@0
|
754 // assign the name that we would have assigned for the source node
|
nuclear@0
|
755 if( bnode)
|
nuclear@0
|
756 bone->mName.Set( FindNameForNode( bnode));
|
nuclear@0
|
757 else
|
nuclear@0
|
758 DefaultLogger::get()->warn( boost::str( boost::format( "ColladaLoader::CreateMesh(): could not find corresponding node for joint \"%s\".") % bone->mName.data));
|
nuclear@0
|
759
|
nuclear@0
|
760 // and insert bone
|
nuclear@0
|
761 dstMesh->mBones[boneCount++] = bone;
|
nuclear@0
|
762 }
|
nuclear@0
|
763 }
|
nuclear@0
|
764
|
nuclear@0
|
765 return dstMesh;
|
nuclear@0
|
766 }
|
nuclear@0
|
767
|
nuclear@0
|
768 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
769 // Stores all meshes in the given scene
|
nuclear@0
|
770 void ColladaLoader::StoreSceneMeshes( aiScene* pScene)
|
nuclear@0
|
771 {
|
nuclear@0
|
772 pScene->mNumMeshes = mMeshes.size();
|
nuclear@0
|
773 if( mMeshes.size() > 0)
|
nuclear@0
|
774 {
|
nuclear@0
|
775 pScene->mMeshes = new aiMesh*[mMeshes.size()];
|
nuclear@0
|
776 std::copy( mMeshes.begin(), mMeshes.end(), pScene->mMeshes);
|
nuclear@0
|
777 mMeshes.clear();
|
nuclear@0
|
778 }
|
nuclear@0
|
779 }
|
nuclear@0
|
780
|
nuclear@0
|
781 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
782 // Stores all cameras in the given scene
|
nuclear@0
|
783 void ColladaLoader::StoreSceneCameras( aiScene* pScene)
|
nuclear@0
|
784 {
|
nuclear@0
|
785 pScene->mNumCameras = mCameras.size();
|
nuclear@0
|
786 if( mCameras.size() > 0)
|
nuclear@0
|
787 {
|
nuclear@0
|
788 pScene->mCameras = new aiCamera*[mCameras.size()];
|
nuclear@0
|
789 std::copy( mCameras.begin(), mCameras.end(), pScene->mCameras);
|
nuclear@0
|
790 mCameras.clear();
|
nuclear@0
|
791 }
|
nuclear@0
|
792 }
|
nuclear@0
|
793
|
nuclear@0
|
794 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
795 // Stores all lights in the given scene
|
nuclear@0
|
796 void ColladaLoader::StoreSceneLights( aiScene* pScene)
|
nuclear@0
|
797 {
|
nuclear@0
|
798 pScene->mNumLights = mLights.size();
|
nuclear@0
|
799 if( mLights.size() > 0)
|
nuclear@0
|
800 {
|
nuclear@0
|
801 pScene->mLights = new aiLight*[mLights.size()];
|
nuclear@0
|
802 std::copy( mLights.begin(), mLights.end(), pScene->mLights);
|
nuclear@0
|
803 mLights.clear();
|
nuclear@0
|
804 }
|
nuclear@0
|
805 }
|
nuclear@0
|
806
|
nuclear@0
|
807 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
808 // Stores all textures in the given scene
|
nuclear@0
|
809 void ColladaLoader::StoreSceneTextures( aiScene* pScene)
|
nuclear@0
|
810 {
|
nuclear@0
|
811 pScene->mNumTextures = mTextures.size();
|
nuclear@0
|
812 if( mTextures.size() > 0)
|
nuclear@0
|
813 {
|
nuclear@0
|
814 pScene->mTextures = new aiTexture*[mTextures.size()];
|
nuclear@0
|
815 std::copy( mTextures.begin(), mTextures.end(), pScene->mTextures);
|
nuclear@0
|
816 mTextures.clear();
|
nuclear@0
|
817 }
|
nuclear@0
|
818 }
|
nuclear@0
|
819
|
nuclear@0
|
820 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
821 // Stores all materials in the given scene
|
nuclear@0
|
822 void ColladaLoader::StoreSceneMaterials( aiScene* pScene)
|
nuclear@0
|
823 {
|
nuclear@0
|
824 pScene->mNumMaterials = newMats.size();
|
nuclear@0
|
825
|
nuclear@0
|
826 if (newMats.size() > 0) {
|
nuclear@0
|
827 pScene->mMaterials = new aiMaterial*[newMats.size()];
|
nuclear@0
|
828 for (unsigned int i = 0; i < newMats.size();++i)
|
nuclear@0
|
829 pScene->mMaterials[i] = newMats[i].second;
|
nuclear@0
|
830
|
nuclear@0
|
831 newMats.clear();
|
nuclear@0
|
832 }
|
nuclear@0
|
833 }
|
nuclear@0
|
834
|
nuclear@0
|
835 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
836 // Stores all animations
|
nuclear@0
|
837 void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pParser)
|
nuclear@0
|
838 {
|
nuclear@0
|
839 // recursivly collect all animations from the collada scene
|
nuclear@0
|
840 StoreAnimations( pScene, pParser, &pParser.mAnims, "");
|
nuclear@0
|
841
|
nuclear@0
|
842 // catch special case: many animations with the same length, each affecting only a single node.
|
nuclear@0
|
843 // we need to unite all those single-node-anims to a proper combined animation
|
nuclear@0
|
844 for( size_t a = 0; a < mAnims.size(); ++a)
|
nuclear@0
|
845 {
|
nuclear@0
|
846 aiAnimation* templateAnim = mAnims[a];
|
nuclear@0
|
847 if( templateAnim->mNumChannels == 1)
|
nuclear@0
|
848 {
|
nuclear@0
|
849 // search for other single-channel-anims with the same duration
|
nuclear@0
|
850 std::vector<size_t> collectedAnimIndices;
|
nuclear@0
|
851 for( size_t b = a+1; b < mAnims.size(); ++b)
|
nuclear@0
|
852 {
|
nuclear@0
|
853 aiAnimation* other = mAnims[b];
|
nuclear@0
|
854 if( other->mNumChannels == 1 && other->mDuration == templateAnim->mDuration && other->mTicksPerSecond == templateAnim->mTicksPerSecond )
|
nuclear@0
|
855 collectedAnimIndices.push_back( b);
|
nuclear@0
|
856 }
|
nuclear@0
|
857
|
nuclear@0
|
858 // if there are other animations which fit the template anim, combine all channels into a single anim
|
nuclear@0
|
859 if( !collectedAnimIndices.empty() )
|
nuclear@0
|
860 {
|
nuclear@0
|
861 aiAnimation* combinedAnim = new aiAnimation();
|
nuclear@0
|
862 combinedAnim->mName = aiString( std::string( "combinedAnim_") + char( '0' + a));
|
nuclear@0
|
863 combinedAnim->mDuration = templateAnim->mDuration;
|
nuclear@0
|
864 combinedAnim->mTicksPerSecond = templateAnim->mTicksPerSecond;
|
nuclear@0
|
865 combinedAnim->mNumChannels = collectedAnimIndices.size() + 1;
|
nuclear@0
|
866 combinedAnim->mChannels = new aiNodeAnim*[combinedAnim->mNumChannels];
|
nuclear@0
|
867 // add the template anim as first channel by moving its aiNodeAnim to the combined animation
|
nuclear@0
|
868 combinedAnim->mChannels[0] = templateAnim->mChannels[0];
|
nuclear@0
|
869 templateAnim->mChannels[0] = NULL;
|
nuclear@0
|
870 delete templateAnim;
|
nuclear@0
|
871 // combined animation replaces template animation in the anim array
|
nuclear@0
|
872 mAnims[a] = combinedAnim;
|
nuclear@0
|
873
|
nuclear@0
|
874 // move the memory of all other anims to the combined anim and erase them from the source anims
|
nuclear@0
|
875 for( size_t b = 0; b < collectedAnimIndices.size(); ++b)
|
nuclear@0
|
876 {
|
nuclear@0
|
877 aiAnimation* srcAnimation = mAnims[collectedAnimIndices[b]];
|
nuclear@0
|
878 combinedAnim->mChannels[1 + b] = srcAnimation->mChannels[0];
|
nuclear@0
|
879 srcAnimation->mChannels[0] = NULL;
|
nuclear@0
|
880 delete srcAnimation;
|
nuclear@0
|
881 }
|
nuclear@0
|
882
|
nuclear@0
|
883 // in a second go, delete all the single-channel-anims that we've stripped from their channels
|
nuclear@0
|
884 // back to front to preserve indices - you know, removing an element from a vector moves all elements behind the removed one
|
nuclear@0
|
885 while( !collectedAnimIndices.empty() )
|
nuclear@0
|
886 {
|
nuclear@0
|
887 mAnims.erase( mAnims.begin() + collectedAnimIndices.back());
|
nuclear@0
|
888 collectedAnimIndices.pop_back();
|
nuclear@0
|
889 }
|
nuclear@0
|
890 }
|
nuclear@0
|
891 }
|
nuclear@0
|
892 }
|
nuclear@0
|
893
|
nuclear@0
|
894 // now store all anims in the scene
|
nuclear@0
|
895 if( !mAnims.empty())
|
nuclear@0
|
896 {
|
nuclear@0
|
897 pScene->mNumAnimations = mAnims.size();
|
nuclear@0
|
898 pScene->mAnimations = new aiAnimation*[mAnims.size()];
|
nuclear@0
|
899 std::copy( mAnims.begin(), mAnims.end(), pScene->mAnimations);
|
nuclear@0
|
900 }
|
nuclear@0
|
901 }
|
nuclear@0
|
902
|
nuclear@0
|
903 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
904 // Constructs the animations for the given source anim
|
nuclear@0
|
905 void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string pPrefix)
|
nuclear@0
|
906 {
|
nuclear@0
|
907 std::string animName = pPrefix.empty() ? pSrcAnim->mName : pPrefix + "_" + pSrcAnim->mName;
|
nuclear@0
|
908
|
nuclear@0
|
909 // create nested animations, if given
|
nuclear@0
|
910 for( std::vector<Collada::Animation*>::const_iterator it = pSrcAnim->mSubAnims.begin(); it != pSrcAnim->mSubAnims.end(); ++it)
|
nuclear@0
|
911 StoreAnimations( pScene, pParser, *it, animName);
|
nuclear@0
|
912
|
nuclear@0
|
913 // create animation channels, if any
|
nuclear@0
|
914 if( !pSrcAnim->mChannels.empty())
|
nuclear@0
|
915 CreateAnimation( pScene, pParser, pSrcAnim, animName);
|
nuclear@0
|
916 }
|
nuclear@0
|
917
|
nuclear@0
|
918 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
919 // Constructs the animation for the given source anim
|
nuclear@0
|
920 void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pName)
|
nuclear@0
|
921 {
|
nuclear@0
|
922 // collect a list of animatable nodes
|
nuclear@0
|
923 std::vector<const aiNode*> nodes;
|
nuclear@0
|
924 CollectNodes( pScene->mRootNode, nodes);
|
nuclear@0
|
925
|
nuclear@0
|
926 std::vector<aiNodeAnim*> anims;
|
nuclear@0
|
927 for( std::vector<const aiNode*>::const_iterator nit = nodes.begin(); nit != nodes.end(); ++nit)
|
nuclear@0
|
928 {
|
nuclear@0
|
929 // find all the collada anim channels which refer to the current node
|
nuclear@0
|
930 std::vector<Collada::ChannelEntry> entries;
|
nuclear@0
|
931 std::string nodeName = (*nit)->mName.data;
|
nuclear@0
|
932
|
nuclear@0
|
933 // find the collada node corresponding to the aiNode
|
nuclear@0
|
934 const Collada::Node* srcNode = FindNode( pParser.mRootNode, nodeName);
|
nuclear@0
|
935 // ai_assert( srcNode != NULL);
|
nuclear@0
|
936 if( !srcNode)
|
nuclear@0
|
937 continue;
|
nuclear@0
|
938
|
nuclear@0
|
939 // now check all channels if they affect the current node
|
nuclear@0
|
940 for( std::vector<Collada::AnimationChannel>::const_iterator cit = pSrcAnim->mChannels.begin();
|
nuclear@0
|
941 cit != pSrcAnim->mChannels.end(); ++cit)
|
nuclear@0
|
942 {
|
nuclear@0
|
943 const Collada::AnimationChannel& srcChannel = *cit;
|
nuclear@0
|
944 Collada::ChannelEntry entry;
|
nuclear@0
|
945
|
nuclear@0
|
946 // we expect the animation target to be of type "nodeName/transformID.subElement". Ignore all others
|
nuclear@0
|
947 // find the slash that separates the node name - there should be only one
|
nuclear@0
|
948 std::string::size_type slashPos = srcChannel.mTarget.find( '/');
|
nuclear@0
|
949 if( slashPos == std::string::npos)
|
nuclear@0
|
950 continue;
|
nuclear@0
|
951 if( srcChannel.mTarget.find( '/', slashPos+1) != std::string::npos)
|
nuclear@0
|
952 continue;
|
nuclear@0
|
953 std::string targetID = srcChannel.mTarget.substr( 0, slashPos);
|
nuclear@0
|
954 if( targetID != srcNode->mID)
|
nuclear@0
|
955 continue;
|
nuclear@0
|
956
|
nuclear@0
|
957 // find the dot that separates the transformID - there should be only one or zero
|
nuclear@0
|
958 std::string::size_type dotPos = srcChannel.mTarget.find( '.');
|
nuclear@0
|
959 if( dotPos != std::string::npos)
|
nuclear@0
|
960 {
|
nuclear@0
|
961 if( srcChannel.mTarget.find( '.', dotPos+1) != std::string::npos)
|
nuclear@0
|
962 continue;
|
nuclear@0
|
963
|
nuclear@0
|
964 entry.mTransformId = srcChannel.mTarget.substr( slashPos+1, dotPos - slashPos - 1);
|
nuclear@0
|
965
|
nuclear@0
|
966 std::string subElement = srcChannel.mTarget.substr( dotPos+1);
|
nuclear@0
|
967 if( subElement == "ANGLE")
|
nuclear@0
|
968 entry.mSubElement = 3; // last number in an Axis-Angle-Transform is the angle
|
nuclear@0
|
969 else if( subElement == "X")
|
nuclear@0
|
970 entry.mSubElement = 0;
|
nuclear@0
|
971 else if( subElement == "Y")
|
nuclear@0
|
972 entry.mSubElement = 1;
|
nuclear@0
|
973 else if( subElement == "Z")
|
nuclear@0
|
974 entry.mSubElement = 2;
|
nuclear@0
|
975 else
|
nuclear@0
|
976 DefaultLogger::get()->warn( boost::str( boost::format( "Unknown anim subelement <%s>. Ignoring") % subElement));
|
nuclear@0
|
977 } else
|
nuclear@0
|
978 {
|
nuclear@0
|
979 // no subelement following, transformId is remaining string
|
nuclear@0
|
980 entry.mTransformId = srcChannel.mTarget.substr( slashPos+1);
|
nuclear@0
|
981 }
|
nuclear@0
|
982
|
nuclear@0
|
983 // determine which transform step is affected by this channel
|
nuclear@0
|
984 entry.mTransformIndex = SIZE_MAX;
|
nuclear@0
|
985 for( size_t a = 0; a < srcNode->mTransforms.size(); ++a)
|
nuclear@0
|
986 if( srcNode->mTransforms[a].mID == entry.mTransformId)
|
nuclear@0
|
987 entry.mTransformIndex = a;
|
nuclear@0
|
988
|
nuclear@0
|
989 if( entry.mTransformIndex == SIZE_MAX) {
|
nuclear@0
|
990 continue;
|
nuclear@0
|
991 }
|
nuclear@0
|
992
|
nuclear@0
|
993 entry.mChannel = &(*cit);
|
nuclear@0
|
994 entries.push_back( entry);
|
nuclear@0
|
995 }
|
nuclear@0
|
996
|
nuclear@0
|
997 // if there's no channel affecting the current node, we skip it
|
nuclear@0
|
998 if( entries.empty())
|
nuclear@0
|
999 continue;
|
nuclear@0
|
1000
|
nuclear@0
|
1001 // resolve the data pointers for all anim channels. Find the minimum time while we're at it
|
nuclear@0
|
1002 float startTime = 1e20f, endTime = -1e20f;
|
nuclear@0
|
1003 for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
|
nuclear@0
|
1004 {
|
nuclear@0
|
1005 Collada::ChannelEntry& e = *it;
|
nuclear@0
|
1006 e.mTimeAccessor = &pParser.ResolveLibraryReference( pParser.mAccessorLibrary, e.mChannel->mSourceTimes);
|
nuclear@0
|
1007 e.mTimeData = &pParser.ResolveLibraryReference( pParser.mDataLibrary, e.mTimeAccessor->mSource);
|
nuclear@0
|
1008 e.mValueAccessor = &pParser.ResolveLibraryReference( pParser.mAccessorLibrary, e.mChannel->mSourceValues);
|
nuclear@0
|
1009 e.mValueData = &pParser.ResolveLibraryReference( pParser.mDataLibrary, e.mValueAccessor->mSource);
|
nuclear@0
|
1010
|
nuclear@0
|
1011 // time count and value count must match
|
nuclear@0
|
1012 if( e.mTimeAccessor->mCount != e.mValueAccessor->mCount)
|
nuclear@0
|
1013 throw DeadlyImportError( boost::str( boost::format( "Time count / value count mismatch in animation channel \"%s\".") % e.mChannel->mTarget));
|
nuclear@0
|
1014
|
nuclear@0
|
1015 if( e.mTimeAccessor->mCount > 0 )
|
nuclear@0
|
1016 {
|
nuclear@0
|
1017 // find bounding times
|
nuclear@0
|
1018 startTime = std::min( startTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, 0, 0));
|
nuclear@0
|
1019 endTime = std::max( endTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, e.mTimeAccessor->mCount-1, 0));
|
nuclear@0
|
1020 }
|
nuclear@0
|
1021 }
|
nuclear@0
|
1022
|
nuclear@0
|
1023 std::vector<aiMatrix4x4> resultTrafos;
|
nuclear@0
|
1024 if( !entries.empty() && entries.front().mTimeAccessor->mCount > 0 )
|
nuclear@0
|
1025 {
|
nuclear@0
|
1026 // create a local transformation chain of the node's transforms
|
nuclear@0
|
1027 std::vector<Collada::Transform> transforms = srcNode->mTransforms;
|
nuclear@0
|
1028
|
nuclear@0
|
1029 // now for every unique point in time, find or interpolate the key values for that time
|
nuclear@0
|
1030 // and apply them to the transform chain. Then the node's present transformation can be calculated.
|
nuclear@0
|
1031 float time = startTime;
|
nuclear@0
|
1032 while( 1)
|
nuclear@0
|
1033 {
|
nuclear@0
|
1034 for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
|
nuclear@0
|
1035 {
|
nuclear@0
|
1036 Collada::ChannelEntry& e = *it;
|
nuclear@0
|
1037
|
nuclear@0
|
1038 // find the keyframe behind the current point in time
|
nuclear@0
|
1039 size_t pos = 0;
|
nuclear@0
|
1040 float postTime = 0.f;
|
nuclear@0
|
1041 while( 1)
|
nuclear@0
|
1042 {
|
nuclear@0
|
1043 if( pos >= e.mTimeAccessor->mCount)
|
nuclear@0
|
1044 break;
|
nuclear@0
|
1045 postTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0);
|
nuclear@0
|
1046 if( postTime >= time)
|
nuclear@0
|
1047 break;
|
nuclear@0
|
1048 ++pos;
|
nuclear@0
|
1049 }
|
nuclear@0
|
1050
|
nuclear@0
|
1051 pos = std::min( pos, e.mTimeAccessor->mCount-1);
|
nuclear@0
|
1052
|
nuclear@0
|
1053 // read values from there
|
nuclear@0
|
1054 float temp[16];
|
nuclear@0
|
1055 for( size_t c = 0; c < e.mValueAccessor->mSize; ++c)
|
nuclear@0
|
1056 temp[c] = ReadFloat( *e.mValueAccessor, *e.mValueData, pos, c);
|
nuclear@0
|
1057
|
nuclear@0
|
1058 // if not exactly at the key time, interpolate with previous value set
|
nuclear@0
|
1059 if( postTime > time && pos > 0)
|
nuclear@0
|
1060 {
|
nuclear@0
|
1061 float preTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos-1, 0);
|
nuclear@0
|
1062 float factor = (time - postTime) / (preTime - postTime);
|
nuclear@0
|
1063
|
nuclear@0
|
1064 for( size_t c = 0; c < e.mValueAccessor->mSize; ++c)
|
nuclear@0
|
1065 {
|
nuclear@0
|
1066 float v = ReadFloat( *e.mValueAccessor, *e.mValueData, pos-1, c);
|
nuclear@0
|
1067 temp[c] += (v - temp[c]) * factor;
|
nuclear@0
|
1068 }
|
nuclear@0
|
1069 }
|
nuclear@0
|
1070
|
nuclear@0
|
1071 // Apply values to current transformation
|
nuclear@0
|
1072 std::copy( temp, temp + e.mValueAccessor->mSize, transforms[e.mTransformIndex].f + e.mSubElement);
|
nuclear@0
|
1073 }
|
nuclear@0
|
1074
|
nuclear@0
|
1075 // Calculate resulting transformation
|
nuclear@0
|
1076 aiMatrix4x4 mat = pParser.CalculateResultTransform( transforms);
|
nuclear@0
|
1077
|
nuclear@0
|
1078 // out of lazyness: we store the time in matrix.d4
|
nuclear@0
|
1079 mat.d4 = time;
|
nuclear@0
|
1080 resultTrafos.push_back( mat);
|
nuclear@0
|
1081
|
nuclear@0
|
1082 // find next point in time to evaluate. That's the closest frame larger than the current in any channel
|
nuclear@0
|
1083 float nextTime = 1e20f;
|
nuclear@0
|
1084 for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
|
nuclear@0
|
1085 {
|
nuclear@0
|
1086 Collada::ChannelEntry& e = *it;
|
nuclear@0
|
1087
|
nuclear@0
|
1088 // find the next time value larger than the current
|
nuclear@0
|
1089 size_t pos = 0;
|
nuclear@0
|
1090 while( pos < e.mTimeAccessor->mCount)
|
nuclear@0
|
1091 {
|
nuclear@0
|
1092 float t = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0);
|
nuclear@0
|
1093 if( t > time)
|
nuclear@0
|
1094 {
|
nuclear@0
|
1095 nextTime = std::min( nextTime, t);
|
nuclear@0
|
1096 break;
|
nuclear@0
|
1097 }
|
nuclear@0
|
1098 ++pos;
|
nuclear@0
|
1099 }
|
nuclear@0
|
1100 }
|
nuclear@0
|
1101
|
nuclear@0
|
1102 // no more keys on any channel after the current time -> we're done
|
nuclear@0
|
1103 if( nextTime > 1e19)
|
nuclear@0
|
1104 break;
|
nuclear@0
|
1105
|
nuclear@0
|
1106 // else construct next keyframe at this following time point
|
nuclear@0
|
1107 time = nextTime;
|
nuclear@0
|
1108 }
|
nuclear@0
|
1109 }
|
nuclear@0
|
1110
|
nuclear@0
|
1111 // there should be some keyframes, but we aren't that fixated on valid input data
|
nuclear@0
|
1112 // ai_assert( resultTrafos.size() > 0);
|
nuclear@0
|
1113
|
nuclear@0
|
1114 // build an animation channel for the given node out of these trafo keys
|
nuclear@0
|
1115 if( !resultTrafos.empty() )
|
nuclear@0
|
1116 {
|
nuclear@0
|
1117 aiNodeAnim* dstAnim = new aiNodeAnim;
|
nuclear@0
|
1118 dstAnim->mNodeName = nodeName;
|
nuclear@0
|
1119 dstAnim->mNumPositionKeys = resultTrafos.size();
|
nuclear@0
|
1120 dstAnim->mNumRotationKeys= resultTrafos.size();
|
nuclear@0
|
1121 dstAnim->mNumScalingKeys = resultTrafos.size();
|
nuclear@0
|
1122 dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()];
|
nuclear@0
|
1123 dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()];
|
nuclear@0
|
1124 dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()];
|
nuclear@0
|
1125
|
nuclear@0
|
1126 for( size_t a = 0; a < resultTrafos.size(); ++a)
|
nuclear@0
|
1127 {
|
nuclear@0
|
1128 aiMatrix4x4 mat = resultTrafos[a];
|
nuclear@0
|
1129 double time = double( mat.d4); // remember? time is stored in mat.d4
|
nuclear@0
|
1130 mat.d4 = 1.0f;
|
nuclear@0
|
1131
|
nuclear@0
|
1132 dstAnim->mPositionKeys[a].mTime = time;
|
nuclear@0
|
1133 dstAnim->mRotationKeys[a].mTime = time;
|
nuclear@0
|
1134 dstAnim->mScalingKeys[a].mTime = time;
|
nuclear@0
|
1135 mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue);
|
nuclear@0
|
1136 }
|
nuclear@0
|
1137
|
nuclear@0
|
1138 anims.push_back( dstAnim);
|
nuclear@0
|
1139 } else
|
nuclear@0
|
1140 {
|
nuclear@0
|
1141 DefaultLogger::get()->warn( "Collada loader: found empty animation channel, ignored. Please check your exporter.");
|
nuclear@0
|
1142 }
|
nuclear@0
|
1143 }
|
nuclear@0
|
1144
|
nuclear@0
|
1145 if( !anims.empty())
|
nuclear@0
|
1146 {
|
nuclear@0
|
1147 aiAnimation* anim = new aiAnimation;
|
nuclear@0
|
1148 anim->mName.Set( pName);
|
nuclear@0
|
1149 anim->mNumChannels = anims.size();
|
nuclear@0
|
1150 anim->mChannels = new aiNodeAnim*[anims.size()];
|
nuclear@0
|
1151 std::copy( anims.begin(), anims.end(), anim->mChannels);
|
nuclear@0
|
1152 anim->mDuration = 0.0f;
|
nuclear@0
|
1153 for( size_t a = 0; a < anims.size(); ++a)
|
nuclear@0
|
1154 {
|
nuclear@0
|
1155 anim->mDuration = std::max( anim->mDuration, anims[a]->mPositionKeys[anims[a]->mNumPositionKeys-1].mTime);
|
nuclear@0
|
1156 anim->mDuration = std::max( anim->mDuration, anims[a]->mRotationKeys[anims[a]->mNumRotationKeys-1].mTime);
|
nuclear@0
|
1157 anim->mDuration = std::max( anim->mDuration, anims[a]->mScalingKeys[anims[a]->mNumScalingKeys-1].mTime);
|
nuclear@0
|
1158 }
|
nuclear@0
|
1159 anim->mTicksPerSecond = 1;
|
nuclear@0
|
1160 mAnims.push_back( anim);
|
nuclear@0
|
1161 }
|
nuclear@0
|
1162 }
|
nuclear@0
|
1163
|
nuclear@0
|
1164 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1165 // Add a texture to a material structure
|
nuclear@0
|
1166 void ColladaLoader::AddTexture ( aiMaterial& mat, const ColladaParser& pParser,
|
nuclear@0
|
1167 const Collada::Effect& effect,
|
nuclear@0
|
1168 const Collada::Sampler& sampler,
|
nuclear@0
|
1169 aiTextureType type, unsigned int idx)
|
nuclear@0
|
1170 {
|
nuclear@0
|
1171 // first of all, basic file name
|
nuclear@0
|
1172 const aiString name = FindFilenameForEffectTexture( pParser, effect, sampler.mName );
|
nuclear@0
|
1173 mat.AddProperty( &name, _AI_MATKEY_TEXTURE_BASE, type, idx );
|
nuclear@0
|
1174
|
nuclear@0
|
1175 // mapping mode
|
nuclear@0
|
1176 int map = aiTextureMapMode_Clamp;
|
nuclear@0
|
1177 if (sampler.mWrapU)
|
nuclear@0
|
1178 map = aiTextureMapMode_Wrap;
|
nuclear@0
|
1179 if (sampler.mWrapU && sampler.mMirrorU)
|
nuclear@0
|
1180 map = aiTextureMapMode_Mirror;
|
nuclear@0
|
1181
|
nuclear@0
|
1182 mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_U_BASE, type, idx);
|
nuclear@0
|
1183
|
nuclear@0
|
1184 map = aiTextureMapMode_Clamp;
|
nuclear@0
|
1185 if (sampler.mWrapV)
|
nuclear@0
|
1186 map = aiTextureMapMode_Wrap;
|
nuclear@0
|
1187 if (sampler.mWrapV && sampler.mMirrorV)
|
nuclear@0
|
1188 map = aiTextureMapMode_Mirror;
|
nuclear@0
|
1189
|
nuclear@0
|
1190 mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_V_BASE, type, idx);
|
nuclear@0
|
1191
|
nuclear@0
|
1192 // UV transformation
|
nuclear@0
|
1193 mat.AddProperty(&sampler.mTransform, 1,
|
nuclear@0
|
1194 _AI_MATKEY_UVTRANSFORM_BASE, type, idx);
|
nuclear@0
|
1195
|
nuclear@0
|
1196 // Blend mode
|
nuclear@0
|
1197 mat.AddProperty((int*)&sampler.mOp , 1,
|
nuclear@0
|
1198 _AI_MATKEY_TEXBLEND_BASE, type, idx);
|
nuclear@0
|
1199
|
nuclear@0
|
1200 // Blend factor
|
nuclear@0
|
1201 mat.AddProperty((float*)&sampler.mWeighting , 1,
|
nuclear@0
|
1202 _AI_MATKEY_TEXBLEND_BASE, type, idx);
|
nuclear@0
|
1203
|
nuclear@0
|
1204 // UV source index ... if we didn't resolve the mapping, it is actually just
|
nuclear@0
|
1205 // a guess but it works in most cases. We search for the frst occurence of a
|
nuclear@0
|
1206 // number in the channel name. We assume it is the zero-based index into the
|
nuclear@0
|
1207 // UV channel array of all corresponding meshes. It could also be one-based
|
nuclear@0
|
1208 // for some exporters, but we won't care of it unless someone complains about.
|
nuclear@0
|
1209 if (sampler.mUVId != UINT_MAX)
|
nuclear@0
|
1210 map = sampler.mUVId;
|
nuclear@0
|
1211 else {
|
nuclear@0
|
1212 map = -1;
|
nuclear@0
|
1213 for (std::string::const_iterator it = sampler.mUVChannel.begin();it != sampler.mUVChannel.end(); ++it){
|
nuclear@0
|
1214 if (IsNumeric(*it)) {
|
nuclear@0
|
1215 map = strtoul10(&(*it));
|
nuclear@0
|
1216 break;
|
nuclear@0
|
1217 }
|
nuclear@0
|
1218 }
|
nuclear@0
|
1219 if (-1 == map) {
|
nuclear@0
|
1220 DefaultLogger::get()->warn("Collada: unable to determine UV channel for texture");
|
nuclear@0
|
1221 map = 0;
|
nuclear@0
|
1222 }
|
nuclear@0
|
1223 }
|
nuclear@0
|
1224 mat.AddProperty(&map,1,_AI_MATKEY_UVWSRC_BASE,type,idx);
|
nuclear@0
|
1225 }
|
nuclear@0
|
1226
|
nuclear@0
|
1227 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1228 // Fills materials from the collada material definitions
|
nuclear@0
|
1229 void ColladaLoader::FillMaterials( const ColladaParser& pParser, aiScene* /*pScene*/)
|
nuclear@0
|
1230 {
|
nuclear@0
|
1231 for (std::vector<std::pair<Collada::Effect*, aiMaterial*> >::iterator it = newMats.begin(),
|
nuclear@0
|
1232 end = newMats.end(); it != end; ++it)
|
nuclear@0
|
1233 {
|
nuclear@0
|
1234 aiMaterial& mat = (aiMaterial&)*it->second;
|
nuclear@0
|
1235 Collada::Effect& effect = *it->first;
|
nuclear@0
|
1236
|
nuclear@0
|
1237 // resolve shading mode
|
nuclear@0
|
1238 int shadeMode;
|
nuclear@0
|
1239 if (effect.mFaceted) /* fixme */
|
nuclear@0
|
1240 shadeMode = aiShadingMode_Flat;
|
nuclear@0
|
1241 else {
|
nuclear@0
|
1242 switch( effect.mShadeType)
|
nuclear@0
|
1243 {
|
nuclear@0
|
1244 case Collada::Shade_Constant:
|
nuclear@0
|
1245 shadeMode = aiShadingMode_NoShading;
|
nuclear@0
|
1246 break;
|
nuclear@0
|
1247 case Collada::Shade_Lambert:
|
nuclear@0
|
1248 shadeMode = aiShadingMode_Gouraud;
|
nuclear@0
|
1249 break;
|
nuclear@0
|
1250 case Collada::Shade_Blinn:
|
nuclear@0
|
1251 shadeMode = aiShadingMode_Blinn;
|
nuclear@0
|
1252 break;
|
nuclear@0
|
1253 case Collada::Shade_Phong:
|
nuclear@0
|
1254 shadeMode = aiShadingMode_Phong;
|
nuclear@0
|
1255 break;
|
nuclear@0
|
1256
|
nuclear@0
|
1257 default:
|
nuclear@0
|
1258 DefaultLogger::get()->warn("Collada: Unrecognized shading mode, using gouraud shading");
|
nuclear@0
|
1259 shadeMode = aiShadingMode_Gouraud;
|
nuclear@0
|
1260 break;
|
nuclear@0
|
1261 }
|
nuclear@0
|
1262 }
|
nuclear@0
|
1263 mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
|
nuclear@0
|
1264
|
nuclear@0
|
1265 // double-sided?
|
nuclear@0
|
1266 shadeMode = effect.mDoubleSided;
|
nuclear@0
|
1267 mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_TWOSIDED);
|
nuclear@0
|
1268
|
nuclear@0
|
1269 // wireframe?
|
nuclear@0
|
1270 shadeMode = effect.mWireframe;
|
nuclear@0
|
1271 mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_ENABLE_WIREFRAME);
|
nuclear@0
|
1272
|
nuclear@0
|
1273 // add material colors
|
nuclear@0
|
1274 mat.AddProperty( &effect.mAmbient, 1,AI_MATKEY_COLOR_AMBIENT);
|
nuclear@0
|
1275 mat.AddProperty( &effect.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
|
nuclear@0
|
1276 mat.AddProperty( &effect.mSpecular, 1,AI_MATKEY_COLOR_SPECULAR);
|
nuclear@0
|
1277 mat.AddProperty( &effect.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
|
nuclear@0
|
1278 mat.AddProperty( &effect.mTransparent, 1, AI_MATKEY_COLOR_TRANSPARENT);
|
nuclear@0
|
1279 mat.AddProperty( &effect.mReflective, 1, AI_MATKEY_COLOR_REFLECTIVE);
|
nuclear@0
|
1280
|
nuclear@0
|
1281 // scalar properties
|
nuclear@0
|
1282 mat.AddProperty( &effect.mShininess, 1, AI_MATKEY_SHININESS);
|
nuclear@0
|
1283 mat.AddProperty( &effect.mReflectivity, 1, AI_MATKEY_REFLECTIVITY);
|
nuclear@0
|
1284 mat.AddProperty( &effect.mRefractIndex, 1, AI_MATKEY_REFRACTI);
|
nuclear@0
|
1285
|
nuclear@0
|
1286 // transparency, a very hard one. seemingly not all files are following the
|
nuclear@0
|
1287 // specification here .. but we can trick.
|
nuclear@0
|
1288 if (effect.mTransparency >= 0.f && effect.mTransparency < 1.f) {
|
nuclear@0
|
1289 effect.mTransparency = 1.f- effect.mTransparency;
|
nuclear@0
|
1290 mat.AddProperty( &effect.mTransparency, 1, AI_MATKEY_OPACITY );
|
nuclear@0
|
1291 mat.AddProperty( &effect.mTransparent, 1, AI_MATKEY_COLOR_TRANSPARENT );
|
nuclear@0
|
1292 }
|
nuclear@0
|
1293
|
nuclear@0
|
1294 // add textures, if given
|
nuclear@0
|
1295 if( !effect.mTexAmbient.mName.empty())
|
nuclear@0
|
1296 /* It is merely a lightmap */
|
nuclear@0
|
1297 AddTexture( mat, pParser, effect, effect.mTexAmbient, aiTextureType_LIGHTMAP);
|
nuclear@0
|
1298
|
nuclear@0
|
1299 if( !effect.mTexEmissive.mName.empty())
|
nuclear@0
|
1300 AddTexture( mat, pParser, effect, effect.mTexEmissive, aiTextureType_EMISSIVE);
|
nuclear@0
|
1301
|
nuclear@0
|
1302 if( !effect.mTexSpecular.mName.empty())
|
nuclear@0
|
1303 AddTexture( mat, pParser, effect, effect.mTexSpecular, aiTextureType_SPECULAR);
|
nuclear@0
|
1304
|
nuclear@0
|
1305 if( !effect.mTexDiffuse.mName.empty())
|
nuclear@0
|
1306 AddTexture( mat, pParser, effect, effect.mTexDiffuse, aiTextureType_DIFFUSE);
|
nuclear@0
|
1307
|
nuclear@0
|
1308 if( !effect.mTexBump.mName.empty())
|
nuclear@0
|
1309 AddTexture( mat, pParser, effect, effect.mTexBump, aiTextureType_NORMALS);
|
nuclear@0
|
1310
|
nuclear@0
|
1311 if( !effect.mTexTransparent.mName.empty())
|
nuclear@0
|
1312 AddTexture( mat, pParser, effect, effect.mTexTransparent, aiTextureType_OPACITY);
|
nuclear@0
|
1313
|
nuclear@0
|
1314 if( !effect.mTexReflective.mName.empty())
|
nuclear@0
|
1315 AddTexture( mat, pParser, effect, effect.mTexReflective, aiTextureType_REFLECTION);
|
nuclear@0
|
1316 }
|
nuclear@0
|
1317 }
|
nuclear@0
|
1318
|
nuclear@0
|
1319 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1320 // Constructs materials from the collada material definitions
|
nuclear@0
|
1321 void ColladaLoader::BuildMaterials( ColladaParser& pParser, aiScene* /*pScene*/)
|
nuclear@0
|
1322 {
|
nuclear@0
|
1323 newMats.reserve(pParser.mMaterialLibrary.size());
|
nuclear@0
|
1324
|
nuclear@0
|
1325 for( ColladaParser::MaterialLibrary::const_iterator matIt = pParser.mMaterialLibrary.begin(); matIt != pParser.mMaterialLibrary.end(); ++matIt)
|
nuclear@0
|
1326 {
|
nuclear@0
|
1327 const Collada::Material& material = matIt->second;
|
nuclear@0
|
1328 // a material is only a reference to an effect
|
nuclear@0
|
1329 ColladaParser::EffectLibrary::iterator effIt = pParser.mEffectLibrary.find( material.mEffect);
|
nuclear@0
|
1330 if( effIt == pParser.mEffectLibrary.end())
|
nuclear@0
|
1331 continue;
|
nuclear@0
|
1332 Collada::Effect& effect = effIt->second;
|
nuclear@0
|
1333
|
nuclear@0
|
1334 // create material
|
nuclear@0
|
1335 aiMaterial* mat = new aiMaterial;
|
nuclear@0
|
1336 aiString name( matIt->first);
|
nuclear@0
|
1337 mat->AddProperty(&name,AI_MATKEY_NAME);
|
nuclear@0
|
1338
|
nuclear@0
|
1339 // store the material
|
nuclear@0
|
1340 mMaterialIndexByName[matIt->first] = newMats.size();
|
nuclear@0
|
1341 newMats.push_back( std::pair<Collada::Effect*, aiMaterial*>( &effect,mat) );
|
nuclear@0
|
1342 }
|
nuclear@0
|
1343 // ScenePreprocessor generates a default material automatically if none is there.
|
nuclear@0
|
1344 // All further code here in this loader works well without a valid material so
|
nuclear@0
|
1345 // we can safely let it to ScenePreprocessor.
|
nuclear@0
|
1346 #if 0
|
nuclear@0
|
1347 if( newMats.size() == 0)
|
nuclear@0
|
1348 {
|
nuclear@0
|
1349 aiMaterial* mat = new aiMaterial;
|
nuclear@0
|
1350 aiString name( AI_DEFAULT_MATERIAL_NAME );
|
nuclear@0
|
1351 mat->AddProperty( &name, AI_MATKEY_NAME);
|
nuclear@0
|
1352
|
nuclear@0
|
1353 const int shadeMode = aiShadingMode_Phong;
|
nuclear@0
|
1354 mat->AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
|
nuclear@0
|
1355 aiColor4D colAmbient( 0.2f, 0.2f, 0.2f, 1.0f), colDiffuse( 0.8f, 0.8f, 0.8f, 1.0f), colSpecular( 0.5f, 0.5f, 0.5f, 0.5f);
|
nuclear@0
|
1356 mat->AddProperty( &colAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
|
nuclear@0
|
1357 mat->AddProperty( &colDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
|
nuclear@0
|
1358 mat->AddProperty( &colSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
|
nuclear@0
|
1359 const float specExp = 5.0f;
|
nuclear@0
|
1360 mat->AddProperty( &specExp, 1, AI_MATKEY_SHININESS);
|
nuclear@0
|
1361 }
|
nuclear@0
|
1362 #endif
|
nuclear@0
|
1363 }
|
nuclear@0
|
1364
|
nuclear@0
|
1365 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1366 // Resolves the texture name for the given effect texture entry
|
nuclear@0
|
1367 aiString ColladaLoader::FindFilenameForEffectTexture( const ColladaParser& pParser,
|
nuclear@0
|
1368 const Collada::Effect& pEffect, const std::string& pName)
|
nuclear@0
|
1369 {
|
nuclear@0
|
1370 // recurse through the param references until we end up at an image
|
nuclear@0
|
1371 std::string name = pName;
|
nuclear@0
|
1372 while( 1)
|
nuclear@0
|
1373 {
|
nuclear@0
|
1374 // the given string is a param entry. Find it
|
nuclear@0
|
1375 Collada::Effect::ParamLibrary::const_iterator it = pEffect.mParams.find( name);
|
nuclear@0
|
1376 // if not found, we're at the end of the recursion. The resulting string should be the image ID
|
nuclear@0
|
1377 if( it == pEffect.mParams.end())
|
nuclear@0
|
1378 break;
|
nuclear@0
|
1379
|
nuclear@0
|
1380 // else recurse on
|
nuclear@0
|
1381 name = it->second.mReference;
|
nuclear@0
|
1382 }
|
nuclear@0
|
1383
|
nuclear@0
|
1384 // find the image referred by this name in the image library of the scene
|
nuclear@0
|
1385 ColladaParser::ImageLibrary::const_iterator imIt = pParser.mImageLibrary.find( name);
|
nuclear@0
|
1386 if( imIt == pParser.mImageLibrary.end())
|
nuclear@0
|
1387 {
|
nuclear@0
|
1388 throw DeadlyImportError( boost::str( boost::format(
|
nuclear@0
|
1389 "Collada: Unable to resolve effect texture entry \"%s\", ended up at ID \"%s\".") % pName % name));
|
nuclear@0
|
1390 }
|
nuclear@0
|
1391
|
nuclear@0
|
1392 aiString result;
|
nuclear@0
|
1393
|
nuclear@0
|
1394 // if this is an embedded texture image setup an aiTexture for it
|
nuclear@0
|
1395 if (imIt->second.mFileName.empty())
|
nuclear@0
|
1396 {
|
nuclear@0
|
1397 if (imIt->second.mImageData.empty()) {
|
nuclear@0
|
1398 throw DeadlyImportError("Collada: Invalid texture, no data or file reference given");
|
nuclear@0
|
1399 }
|
nuclear@0
|
1400
|
nuclear@0
|
1401 aiTexture* tex = new aiTexture();
|
nuclear@0
|
1402
|
nuclear@0
|
1403 // setup format hint
|
nuclear@0
|
1404 if (imIt->second.mEmbeddedFormat.length() > 3) {
|
nuclear@0
|
1405 DefaultLogger::get()->warn("Collada: texture format hint is too long, truncating to 3 characters");
|
nuclear@0
|
1406 }
|
nuclear@0
|
1407 strncpy(tex->achFormatHint,imIt->second.mEmbeddedFormat.c_str(),3);
|
nuclear@0
|
1408
|
nuclear@0
|
1409 // and copy texture data
|
nuclear@0
|
1410 tex->mHeight = 0;
|
nuclear@0
|
1411 tex->mWidth = imIt->second.mImageData.size();
|
nuclear@0
|
1412 tex->pcData = (aiTexel*)new char[tex->mWidth];
|
nuclear@0
|
1413 memcpy(tex->pcData,&imIt->second.mImageData[0],tex->mWidth);
|
nuclear@0
|
1414
|
nuclear@0
|
1415 // setup texture reference string
|
nuclear@0
|
1416 result.data[0] = '*';
|
nuclear@0
|
1417 result.length = 1 + ASSIMP_itoa10(result.data+1,MAXLEN-1,mTextures.size());
|
nuclear@0
|
1418
|
nuclear@0
|
1419 // and add this texture to the list
|
nuclear@0
|
1420 mTextures.push_back(tex);
|
nuclear@0
|
1421 }
|
nuclear@0
|
1422 else
|
nuclear@0
|
1423 {
|
nuclear@0
|
1424 result.Set( imIt->second.mFileName );
|
nuclear@0
|
1425 ConvertPath(result);
|
nuclear@0
|
1426 }
|
nuclear@0
|
1427 return result;
|
nuclear@0
|
1428 }
|
nuclear@0
|
1429
|
nuclear@0
|
1430 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1431 // Convert a path read from a collada file to the usual representation
|
nuclear@0
|
1432 void ColladaLoader::ConvertPath (aiString& ss)
|
nuclear@0
|
1433 {
|
nuclear@0
|
1434 // TODO: collada spec, p 22. Handle URI correctly.
|
nuclear@0
|
1435 // For the moment we're just stripping the file:// away to make it work.
|
nuclear@0
|
1436 // Windoes doesn't seem to be able to find stuff like
|
nuclear@0
|
1437 // 'file://..\LWO\LWO2\MappingModes\earthSpherical.jpg'
|
nuclear@0
|
1438 if (0 == strncmp(ss.data,"file://",7))
|
nuclear@0
|
1439 {
|
nuclear@0
|
1440 ss.length -= 7;
|
nuclear@0
|
1441 memmove(ss.data,ss.data+7,ss.length);
|
nuclear@0
|
1442 ss.data[ss.length] = '\0';
|
nuclear@0
|
1443 }
|
nuclear@0
|
1444
|
nuclear@0
|
1445 // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes...
|
nuclear@0
|
1446 // I need to filter it without destroying linux paths starting with "/somewhere"
|
nuclear@0
|
1447 if( ss.data[0] == '/' && isalpha( ss.data[1]) && ss.data[2] == ':' )
|
nuclear@0
|
1448 {
|
nuclear@0
|
1449 ss.length--;
|
nuclear@0
|
1450 memmove( ss.data, ss.data+1, ss.length);
|
nuclear@0
|
1451 ss.data[ss.length] = 0;
|
nuclear@0
|
1452 }
|
nuclear@0
|
1453
|
nuclear@0
|
1454 // find and convert all %xy special chars
|
nuclear@0
|
1455 char* out = ss.data;
|
nuclear@0
|
1456 for( const char* it = ss.data; it != ss.data + ss.length; /**/ )
|
nuclear@0
|
1457 {
|
nuclear@0
|
1458 if( *it == '%' && (it + 3) < ss.data + ss.length )
|
nuclear@0
|
1459 {
|
nuclear@0
|
1460 // separate the number to avoid dragging in chars from behind into the parsing
|
nuclear@0
|
1461 char mychar[3] = { it[1], it[2], 0 };
|
nuclear@0
|
1462 size_t nbr = strtoul16( mychar);
|
nuclear@0
|
1463 it += 3;
|
nuclear@0
|
1464 *out++ = (char)(nbr & 0xFF);
|
nuclear@0
|
1465 } else
|
nuclear@0
|
1466 {
|
nuclear@0
|
1467 *out++ = *it++;
|
nuclear@0
|
1468 }
|
nuclear@0
|
1469 }
|
nuclear@0
|
1470
|
nuclear@0
|
1471 // adjust length and terminator of the shortened string
|
nuclear@0
|
1472 *out = 0;
|
nuclear@0
|
1473 ss.length = (ptrdiff_t) (out - ss.data);
|
nuclear@0
|
1474 }
|
nuclear@0
|
1475
|
nuclear@0
|
1476 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1477 // Reads a float value from an accessor and its data array.
|
nuclear@0
|
1478 float ColladaLoader::ReadFloat( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex, size_t pOffset) const
|
nuclear@0
|
1479 {
|
nuclear@0
|
1480 // FIXME: (thom) Test for data type here in every access? For the moment, I leave this to the caller
|
nuclear@0
|
1481 size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset + pOffset;
|
nuclear@0
|
1482 ai_assert( pos < pData.mValues.size());
|
nuclear@0
|
1483 return pData.mValues[pos];
|
nuclear@0
|
1484 }
|
nuclear@0
|
1485
|
nuclear@0
|
1486 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1487 // Reads a string value from an accessor and its data array.
|
nuclear@0
|
1488 const std::string& ColladaLoader::ReadString( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex) const
|
nuclear@0
|
1489 {
|
nuclear@0
|
1490 size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset;
|
nuclear@0
|
1491 ai_assert( pos < pData.mStrings.size());
|
nuclear@0
|
1492 return pData.mStrings[pos];
|
nuclear@0
|
1493 }
|
nuclear@0
|
1494
|
nuclear@0
|
1495 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1496 // Collects all nodes into the given array
|
nuclear@0
|
1497 void ColladaLoader::CollectNodes( const aiNode* pNode, std::vector<const aiNode*>& poNodes) const
|
nuclear@0
|
1498 {
|
nuclear@0
|
1499 poNodes.push_back( pNode);
|
nuclear@0
|
1500
|
nuclear@0
|
1501 for( size_t a = 0; a < pNode->mNumChildren; ++a)
|
nuclear@0
|
1502 CollectNodes( pNode->mChildren[a], poNodes);
|
nuclear@0
|
1503 }
|
nuclear@0
|
1504
|
nuclear@0
|
1505 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1506 // Finds a node in the collada scene by the given name
|
nuclear@0
|
1507 const Collada::Node* ColladaLoader::FindNode( const Collada::Node* pNode, const std::string& pName) const
|
nuclear@0
|
1508 {
|
nuclear@0
|
1509 if( pNode->mName == pName || pNode->mID == pName)
|
nuclear@0
|
1510 return pNode;
|
nuclear@0
|
1511
|
nuclear@0
|
1512 for( size_t a = 0; a < pNode->mChildren.size(); ++a)
|
nuclear@0
|
1513 {
|
nuclear@0
|
1514 const Collada::Node* node = FindNode( pNode->mChildren[a], pName);
|
nuclear@0
|
1515 if( node)
|
nuclear@0
|
1516 return node;
|
nuclear@0
|
1517 }
|
nuclear@0
|
1518
|
nuclear@0
|
1519 return NULL;
|
nuclear@0
|
1520 }
|
nuclear@0
|
1521
|
nuclear@0
|
1522 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1523 // Finds a node in the collada scene by the given SID
|
nuclear@0
|
1524 const Collada::Node* ColladaLoader::FindNodeBySID( const Collada::Node* pNode, const std::string& pSID) const
|
nuclear@0
|
1525 {
|
nuclear@0
|
1526 if( pNode->mSID == pSID)
|
nuclear@0
|
1527 return pNode;
|
nuclear@0
|
1528
|
nuclear@0
|
1529 for( size_t a = 0; a < pNode->mChildren.size(); ++a)
|
nuclear@0
|
1530 {
|
nuclear@0
|
1531 const Collada::Node* node = FindNodeBySID( pNode->mChildren[a], pSID);
|
nuclear@0
|
1532 if( node)
|
nuclear@0
|
1533 return node;
|
nuclear@0
|
1534 }
|
nuclear@0
|
1535
|
nuclear@0
|
1536 return NULL;
|
nuclear@0
|
1537 }
|
nuclear@0
|
1538
|
nuclear@0
|
1539 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1540 // Finds a proper name for a node derived from the collada-node's properties
|
nuclear@0
|
1541 std::string ColladaLoader::FindNameForNode( const Collada::Node* pNode) const
|
nuclear@0
|
1542 {
|
nuclear@0
|
1543 // now setup the name of the node. We take the name if not empty, otherwise the collada ID
|
nuclear@0
|
1544 // FIX: Workaround for XSI calling the instanced visual scene 'untitled' by default.
|
nuclear@0
|
1545 if (!pNode->mName.empty() && pNode->mName != "untitled")
|
nuclear@0
|
1546 return pNode->mName;
|
nuclear@0
|
1547 else if (!pNode->mID.empty())
|
nuclear@0
|
1548 return pNode->mID;
|
nuclear@0
|
1549 else if (!pNode->mSID.empty())
|
nuclear@0
|
1550 return pNode->mSID;
|
nuclear@0
|
1551 else
|
nuclear@0
|
1552 {
|
nuclear@0
|
1553 // No need to worry. Unnamed nodes are no problem at all, except
|
nuclear@0
|
1554 // if cameras or lights need to be assigned to them.
|
nuclear@0
|
1555 return boost::str( boost::format( "$ColladaAutoName$_%d") % clock());
|
nuclear@0
|
1556 }
|
nuclear@0
|
1557 }
|
nuclear@0
|
1558
|
nuclear@0
|
1559 #endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER
|