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 ASELoader.cpp
|
nuclear@0
|
43 * @brief Implementation of the ASE importer class
|
nuclear@0
|
44 */
|
nuclear@0
|
45
|
nuclear@0
|
46 #include "AssimpPCH.h"
|
nuclear@0
|
47 #include "assimp/config.h"
|
nuclear@0
|
48 #ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
|
nuclear@0
|
49
|
nuclear@0
|
50 // internal headers
|
nuclear@0
|
51 #include "ASELoader.h"
|
nuclear@0
|
52 #include "StringComparison.h"
|
nuclear@0
|
53 #include "SkeletonMeshBuilder.h"
|
nuclear@0
|
54 #include "TargetAnimation.h"
|
nuclear@0
|
55
|
nuclear@0
|
56 // utilities
|
nuclear@0
|
57 #include "fast_atof.h"
|
nuclear@0
|
58
|
nuclear@0
|
59 using namespace Assimp;
|
nuclear@0
|
60 using namespace Assimp::ASE;
|
nuclear@0
|
61
|
nuclear@0
|
62 static const aiImporterDesc desc = {
|
nuclear@0
|
63 "ASE Importer",
|
nuclear@0
|
64 "",
|
nuclear@0
|
65 "",
|
nuclear@0
|
66 "Similar to 3DS but text-encoded",
|
nuclear@0
|
67 aiImporterFlags_SupportTextFlavour,
|
nuclear@0
|
68 0,
|
nuclear@0
|
69 0,
|
nuclear@0
|
70 0,
|
nuclear@0
|
71 0,
|
nuclear@0
|
72 "ase ask"
|
nuclear@0
|
73 };
|
nuclear@0
|
74
|
nuclear@0
|
75 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
76 // Constructor to be privately used by Importer
|
nuclear@0
|
77 ASEImporter::ASEImporter()
|
nuclear@0
|
78 : noSkeletonMesh()
|
nuclear@0
|
79 {}
|
nuclear@0
|
80
|
nuclear@0
|
81 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
82 // Destructor, private as well
|
nuclear@0
|
83 ASEImporter::~ASEImporter()
|
nuclear@0
|
84 {}
|
nuclear@0
|
85
|
nuclear@0
|
86 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
87 // Returns whether the class can handle the format of the given file.
|
nuclear@0
|
88 bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const
|
nuclear@0
|
89 {
|
nuclear@0
|
90 // check file extension
|
nuclear@0
|
91 const std::string extension = GetExtension(pFile);
|
nuclear@0
|
92
|
nuclear@0
|
93 if( extension == "ase" || extension == "ask")
|
nuclear@0
|
94 return true;
|
nuclear@0
|
95
|
nuclear@0
|
96 if ((!extension.length() || cs) && pIOHandler) {
|
nuclear@0
|
97 const char* tokens[] = {"*3dsmax_asciiexport"};
|
nuclear@0
|
98 return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
|
nuclear@0
|
99 }
|
nuclear@0
|
100 return false;
|
nuclear@0
|
101 }
|
nuclear@0
|
102
|
nuclear@0
|
103 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
104 // Loader meta information
|
nuclear@0
|
105 const aiImporterDesc* ASEImporter::GetInfo () const
|
nuclear@0
|
106 {
|
nuclear@0
|
107 return &desc;
|
nuclear@0
|
108 }
|
nuclear@0
|
109
|
nuclear@0
|
110 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
111 // Setup configuration options
|
nuclear@0
|
112 void ASEImporter::SetupProperties(const Importer* pImp)
|
nuclear@0
|
113 {
|
nuclear@0
|
114 configRecomputeNormals = (pImp->GetPropertyInteger(
|
nuclear@0
|
115 AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS,1) ? true : false);
|
nuclear@0
|
116
|
nuclear@0
|
117 noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0;
|
nuclear@0
|
118 }
|
nuclear@0
|
119
|
nuclear@0
|
120 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
121 // Imports the given file into the given scene structure.
|
nuclear@0
|
122 void ASEImporter::InternReadFile( const std::string& pFile,
|
nuclear@0
|
123 aiScene* pScene, IOSystem* pIOHandler)
|
nuclear@0
|
124 {
|
nuclear@0
|
125 boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
|
nuclear@0
|
126
|
nuclear@0
|
127 // Check whether we can read from the file
|
nuclear@0
|
128 if( file.get() == NULL) {
|
nuclear@0
|
129 throw DeadlyImportError( "Failed to open ASE file " + pFile + ".");
|
nuclear@0
|
130 }
|
nuclear@0
|
131
|
nuclear@0
|
132 // Allocate storage and copy the contents of the file to a memory buffer
|
nuclear@0
|
133 std::vector<char> mBuffer2;
|
nuclear@0
|
134 TextFileToBuffer(file.get(),mBuffer2);
|
nuclear@0
|
135
|
nuclear@0
|
136 this->mBuffer = &mBuffer2[0];
|
nuclear@0
|
137 this->pcScene = pScene;
|
nuclear@0
|
138
|
nuclear@0
|
139 // ------------------------------------------------------------------
|
nuclear@0
|
140 // Guess the file format by looking at the extension
|
nuclear@0
|
141 // ASC is considered to be the older format 110,
|
nuclear@0
|
142 // ASE is the actual version 200 (that is currently written by max)
|
nuclear@0
|
143 // ------------------------------------------------------------------
|
nuclear@0
|
144 unsigned int defaultFormat;
|
nuclear@0
|
145 std::string::size_type s = pFile.length()-1;
|
nuclear@0
|
146 switch (pFile.c_str()[s]) {
|
nuclear@0
|
147
|
nuclear@0
|
148 case 'C':
|
nuclear@0
|
149 case 'c':
|
nuclear@0
|
150 defaultFormat = AI_ASE_OLD_FILE_FORMAT;
|
nuclear@0
|
151 break;
|
nuclear@0
|
152 default:
|
nuclear@0
|
153 defaultFormat = AI_ASE_NEW_FILE_FORMAT;
|
nuclear@0
|
154 };
|
nuclear@0
|
155
|
nuclear@0
|
156 // Construct an ASE parser and parse the file
|
nuclear@0
|
157 ASE::Parser parser(mBuffer,defaultFormat);
|
nuclear@0
|
158 mParser = &parser;
|
nuclear@0
|
159 mParser->Parse();
|
nuclear@0
|
160
|
nuclear@0
|
161 //------------------------------------------------------------------
|
nuclear@0
|
162 // Check whether we god at least one mesh. If we did - generate
|
nuclear@0
|
163 // materials and copy meshes.
|
nuclear@0
|
164 // ------------------------------------------------------------------
|
nuclear@0
|
165 if ( !mParser->m_vMeshes.empty()) {
|
nuclear@0
|
166
|
nuclear@0
|
167 // If absolutely no material has been loaded from the file
|
nuclear@0
|
168 // we need to generate a default material
|
nuclear@0
|
169 GenerateDefaultMaterial();
|
nuclear@0
|
170
|
nuclear@0
|
171 // process all meshes
|
nuclear@0
|
172 bool tookNormals = false;
|
nuclear@0
|
173 std::vector<aiMesh*> avOutMeshes;
|
nuclear@0
|
174 avOutMeshes.reserve(mParser->m_vMeshes.size()*2);
|
nuclear@0
|
175 for (std::vector<ASE::Mesh>::iterator i = mParser->m_vMeshes.begin();i != mParser->m_vMeshes.end();++i) {
|
nuclear@0
|
176 if ((*i).bSkip) {
|
nuclear@0
|
177 continue;
|
nuclear@0
|
178 }
|
nuclear@0
|
179 BuildUniqueRepresentation(*i);
|
nuclear@0
|
180
|
nuclear@0
|
181 // Need to generate proper vertex normals if necessary
|
nuclear@0
|
182 if(GenerateNormals(*i)) {
|
nuclear@0
|
183 tookNormals = true;
|
nuclear@0
|
184 }
|
nuclear@0
|
185
|
nuclear@0
|
186 // Convert all meshes to aiMesh objects
|
nuclear@0
|
187 ConvertMeshes(*i,avOutMeshes);
|
nuclear@0
|
188 }
|
nuclear@0
|
189 if (tookNormals) {
|
nuclear@0
|
190 DefaultLogger::get()->debug("ASE: Taking normals from the file. Use "
|
nuclear@0
|
191 "the AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS setting if you "
|
nuclear@0
|
192 "experience problems");
|
nuclear@0
|
193 }
|
nuclear@0
|
194
|
nuclear@0
|
195 // Now build the output mesh list. Remove dummies
|
nuclear@0
|
196 pScene->mNumMeshes = (unsigned int)avOutMeshes.size();
|
nuclear@0
|
197 aiMesh** pp = pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
|
nuclear@0
|
198 for (std::vector<aiMesh*>::const_iterator i = avOutMeshes.begin();i != avOutMeshes.end();++i) {
|
nuclear@0
|
199 if (!(*i)->mNumFaces) {
|
nuclear@0
|
200 continue;
|
nuclear@0
|
201 }
|
nuclear@0
|
202 *pp++ = *i;
|
nuclear@0
|
203 }
|
nuclear@0
|
204 pScene->mNumMeshes = (unsigned int)(pp - pScene->mMeshes);
|
nuclear@0
|
205
|
nuclear@0
|
206 // Build final material indices (remove submaterials and setup
|
nuclear@0
|
207 // the final list)
|
nuclear@0
|
208 BuildMaterialIndices();
|
nuclear@0
|
209 }
|
nuclear@0
|
210
|
nuclear@0
|
211 // ------------------------------------------------------------------
|
nuclear@0
|
212 // Copy all scene graph nodes - lights, cameras, dummies and meshes
|
nuclear@0
|
213 // into one huge list.
|
nuclear@0
|
214 //------------------------------------------------------------------
|
nuclear@0
|
215 std::vector<BaseNode*> nodes;
|
nuclear@0
|
216 nodes.reserve(mParser->m_vMeshes.size() +mParser->m_vLights.size()
|
nuclear@0
|
217 + mParser->m_vCameras.size() + mParser->m_vDummies.size());
|
nuclear@0
|
218
|
nuclear@0
|
219 // Lights
|
nuclear@0
|
220 for (std::vector<ASE::Light>::iterator it = mParser->m_vLights.begin(),
|
nuclear@0
|
221 end = mParser->m_vLights.end();it != end; ++it)nodes.push_back(&(*it));
|
nuclear@0
|
222 // Cameras
|
nuclear@0
|
223 for (std::vector<ASE::Camera>::iterator it = mParser->m_vCameras.begin(),
|
nuclear@0
|
224 end = mParser->m_vCameras.end();it != end; ++it)nodes.push_back(&(*it));
|
nuclear@0
|
225 // Meshes
|
nuclear@0
|
226 for (std::vector<ASE::Mesh>::iterator it = mParser->m_vMeshes.begin(),
|
nuclear@0
|
227 end = mParser->m_vMeshes.end();it != end; ++it)nodes.push_back(&(*it));
|
nuclear@0
|
228 // Dummies
|
nuclear@0
|
229 for (std::vector<ASE::Dummy>::iterator it = mParser->m_vDummies.begin(),
|
nuclear@0
|
230 end = mParser->m_vDummies.end();it != end; ++it)nodes.push_back(&(*it));
|
nuclear@0
|
231
|
nuclear@0
|
232 // build the final node graph
|
nuclear@0
|
233 BuildNodes(nodes);
|
nuclear@0
|
234
|
nuclear@0
|
235 // build output animations
|
nuclear@0
|
236 BuildAnimations(nodes);
|
nuclear@0
|
237
|
nuclear@0
|
238 // build output cameras
|
nuclear@0
|
239 BuildCameras();
|
nuclear@0
|
240
|
nuclear@0
|
241 // build output lights
|
nuclear@0
|
242 BuildLights();
|
nuclear@0
|
243
|
nuclear@0
|
244 // ------------------------------------------------------------------
|
nuclear@0
|
245 // If we have no meshes use the SkeletonMeshBuilder helper class
|
nuclear@0
|
246 // to build a mesh for the animation skeleton
|
nuclear@0
|
247 // FIXME: very strange results
|
nuclear@0
|
248 // ------------------------------------------------------------------
|
nuclear@0
|
249 if (!pScene->mNumMeshes) {
|
nuclear@0
|
250 pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
|
nuclear@0
|
251 if (!noSkeletonMesh) {
|
nuclear@0
|
252 SkeletonMeshBuilder skeleton(pScene);
|
nuclear@0
|
253 }
|
nuclear@0
|
254 }
|
nuclear@0
|
255 }
|
nuclear@0
|
256 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
257 void ASEImporter::GenerateDefaultMaterial()
|
nuclear@0
|
258 {
|
nuclear@0
|
259 ai_assert(NULL != mParser);
|
nuclear@0
|
260
|
nuclear@0
|
261 bool bHas = false;
|
nuclear@0
|
262 for (std::vector<ASE::Mesh>::iterator i = mParser->m_vMeshes.begin();i != mParser->m_vMeshes.end();++i) {
|
nuclear@0
|
263 if ((*i).bSkip)continue;
|
nuclear@0
|
264 if (ASE::Face::DEFAULT_MATINDEX == (*i).iMaterialIndex) {
|
nuclear@0
|
265 (*i).iMaterialIndex = (unsigned int)mParser->m_vMaterials.size();
|
nuclear@0
|
266 bHas = true;
|
nuclear@0
|
267 }
|
nuclear@0
|
268 }
|
nuclear@0
|
269 if (bHas || mParser->m_vMaterials.empty()) {
|
nuclear@0
|
270 // add a simple material without submaterials to the parser's list
|
nuclear@0
|
271 mParser->m_vMaterials.push_back ( ASE::Material() );
|
nuclear@0
|
272 ASE::Material& mat = mParser->m_vMaterials.back();
|
nuclear@0
|
273
|
nuclear@0
|
274 mat.mDiffuse = aiColor3D(0.6f,0.6f,0.6f);
|
nuclear@0
|
275 mat.mSpecular = aiColor3D(1.0f,1.0f,1.0f);
|
nuclear@0
|
276 mat.mAmbient = aiColor3D(0.05f,0.05f,0.05f);
|
nuclear@0
|
277 mat.mShading = Discreet3DS::Gouraud;
|
nuclear@0
|
278 mat.mName = AI_DEFAULT_MATERIAL_NAME;
|
nuclear@0
|
279 }
|
nuclear@0
|
280 }
|
nuclear@0
|
281
|
nuclear@0
|
282 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
283 void ASEImporter::BuildAnimations(const std::vector<BaseNode*>& nodes)
|
nuclear@0
|
284 {
|
nuclear@0
|
285 // check whether we have at least one mesh which has animations
|
nuclear@0
|
286 std::vector<ASE::BaseNode*>::const_iterator i = nodes.begin();
|
nuclear@0
|
287 unsigned int iNum = 0;
|
nuclear@0
|
288 for (;i != nodes.end();++i) {
|
nuclear@0
|
289
|
nuclear@0
|
290 // TODO: Implement Bezier & TCB support
|
nuclear@0
|
291 if ((*i)->mAnim.mPositionType != ASE::Animation::TRACK) {
|
nuclear@0
|
292 DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. "
|
nuclear@0
|
293 "This is not supported.");
|
nuclear@0
|
294 }
|
nuclear@0
|
295 if ((*i)->mAnim.mRotationType != ASE::Animation::TRACK) {
|
nuclear@0
|
296 DefaultLogger::get()->warn("ASE: Rotation controller uses Bezier/TCB keys. "
|
nuclear@0
|
297 "This is not supported.");
|
nuclear@0
|
298 }
|
nuclear@0
|
299 if ((*i)->mAnim.mScalingType != ASE::Animation::TRACK) {
|
nuclear@0
|
300 DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. "
|
nuclear@0
|
301 "This is not supported.");
|
nuclear@0
|
302 }
|
nuclear@0
|
303
|
nuclear@0
|
304 // We compare against 1 here - firstly one key is not
|
nuclear@0
|
305 // really an animation and secondly MAX writes dummies
|
nuclear@0
|
306 // that represent the node transformation.
|
nuclear@0
|
307 if ((*i)->mAnim.akeyPositions.size()>1 || (*i)->mAnim.akeyRotations.size()>1 || (*i)->mAnim.akeyScaling.size()>1){
|
nuclear@0
|
308 ++iNum;
|
nuclear@0
|
309 }
|
nuclear@0
|
310 if ((*i)->mTargetAnim.akeyPositions.size() > 1 && is_not_qnan( (*i)->mTargetPosition.x )) {
|
nuclear@0
|
311 ++iNum;
|
nuclear@0
|
312 }
|
nuclear@0
|
313 }
|
nuclear@0
|
314 if (iNum) {
|
nuclear@0
|
315 // Generate a new animation channel and setup everything for it
|
nuclear@0
|
316 pcScene->mNumAnimations = 1;
|
nuclear@0
|
317 pcScene->mAnimations = new aiAnimation*[1];
|
nuclear@0
|
318 aiAnimation* pcAnim = pcScene->mAnimations[0] = new aiAnimation();
|
nuclear@0
|
319 pcAnim->mNumChannels = iNum;
|
nuclear@0
|
320 pcAnim->mChannels = new aiNodeAnim*[iNum];
|
nuclear@0
|
321 pcAnim->mTicksPerSecond = mParser->iFrameSpeed * mParser->iTicksPerFrame;
|
nuclear@0
|
322
|
nuclear@0
|
323 iNum = 0;
|
nuclear@0
|
324
|
nuclear@0
|
325 // Now iterate through all meshes and collect all data we can find
|
nuclear@0
|
326 for (i = nodes.begin();i != nodes.end();++i) {
|
nuclear@0
|
327
|
nuclear@0
|
328 ASE::BaseNode* me = *i;
|
nuclear@0
|
329 if ( me->mTargetAnim.akeyPositions.size() > 1 && is_not_qnan( me->mTargetPosition.x )) {
|
nuclear@0
|
330 // Generate an extra channel for the camera/light target.
|
nuclear@0
|
331 // BuildNodes() does also generate an extra node, named
|
nuclear@0
|
332 // <baseName>.Target.
|
nuclear@0
|
333 aiNodeAnim* nd = pcAnim->mChannels[iNum++] = new aiNodeAnim();
|
nuclear@0
|
334 nd->mNodeName.Set(me->mName + ".Target");
|
nuclear@0
|
335
|
nuclear@0
|
336 // If there is no input position channel we will need
|
nuclear@0
|
337 // to supply the default position from the node's
|
nuclear@0
|
338 // local transformation matrix.
|
nuclear@0
|
339 /*TargetAnimationHelper helper;
|
nuclear@0
|
340 if (me->mAnim.akeyPositions.empty())
|
nuclear@0
|
341 {
|
nuclear@0
|
342 aiMatrix4x4& mat = (*i)->mTransform;
|
nuclear@0
|
343 helper.SetFixedMainAnimationChannel(aiVector3D(
|
nuclear@0
|
344 mat.a4, mat.b4, mat.c4));
|
nuclear@0
|
345 }
|
nuclear@0
|
346 else helper.SetMainAnimationChannel (&me->mAnim.akeyPositions);
|
nuclear@0
|
347 helper.SetTargetAnimationChannel (&me->mTargetAnim.akeyPositions);
|
nuclear@0
|
348
|
nuclear@0
|
349 helper.Process(&me->mTargetAnim.akeyPositions);*/
|
nuclear@0
|
350
|
nuclear@0
|
351 // Allocate the key array and fill it
|
nuclear@0
|
352 nd->mNumPositionKeys = (unsigned int) me->mTargetAnim.akeyPositions.size();
|
nuclear@0
|
353 nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys];
|
nuclear@0
|
354
|
nuclear@0
|
355 ::memcpy(nd->mPositionKeys,&me->mTargetAnim.akeyPositions[0],
|
nuclear@0
|
356 nd->mNumPositionKeys * sizeof(aiVectorKey));
|
nuclear@0
|
357 }
|
nuclear@0
|
358
|
nuclear@0
|
359 if (me->mAnim.akeyPositions.size() > 1 || me->mAnim.akeyRotations.size() > 1 || me->mAnim.akeyScaling.size() > 1) {
|
nuclear@0
|
360 // Begin a new node animation channel for this node
|
nuclear@0
|
361 aiNodeAnim* nd = pcAnim->mChannels[iNum++] = new aiNodeAnim();
|
nuclear@0
|
362 nd->mNodeName.Set(me->mName);
|
nuclear@0
|
363
|
nuclear@0
|
364 // copy position keys
|
nuclear@0
|
365 if (me->mAnim.akeyPositions.size() > 1 )
|
nuclear@0
|
366 {
|
nuclear@0
|
367 // Allocate the key array and fill it
|
nuclear@0
|
368 nd->mNumPositionKeys = (unsigned int) me->mAnim.akeyPositions.size();
|
nuclear@0
|
369 nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys];
|
nuclear@0
|
370
|
nuclear@0
|
371 ::memcpy(nd->mPositionKeys,&me->mAnim.akeyPositions[0],
|
nuclear@0
|
372 nd->mNumPositionKeys * sizeof(aiVectorKey));
|
nuclear@0
|
373 }
|
nuclear@0
|
374 // copy rotation keys
|
nuclear@0
|
375 if (me->mAnim.akeyRotations.size() > 1 ) {
|
nuclear@0
|
376 // Allocate the key array and fill it
|
nuclear@0
|
377 nd->mNumRotationKeys = (unsigned int) me->mAnim.akeyRotations.size();
|
nuclear@0
|
378 nd->mRotationKeys = new aiQuatKey[nd->mNumRotationKeys];
|
nuclear@0
|
379
|
nuclear@0
|
380 // --------------------------------------------------------------------
|
nuclear@0
|
381 // Rotation keys are offsets to the previous keys.
|
nuclear@0
|
382 // We have the quaternion representations of all
|
nuclear@0
|
383 // of them, so we just need to concatenate all
|
nuclear@0
|
384 // (unit-length) quaternions to get the absolute
|
nuclear@0
|
385 // rotations.
|
nuclear@0
|
386 // Rotation keys are ABSOLUTE for older files
|
nuclear@0
|
387 // --------------------------------------------------------------------
|
nuclear@0
|
388
|
nuclear@0
|
389 aiQuaternion cur;
|
nuclear@0
|
390 for (unsigned int a = 0; a < nd->mNumRotationKeys;++a) {
|
nuclear@0
|
391 aiQuatKey q = me->mAnim.akeyRotations[a];
|
nuclear@0
|
392
|
nuclear@0
|
393 if (mParser->iFileFormat > 110) {
|
nuclear@0
|
394 cur = (a ? cur*q.mValue : q.mValue);
|
nuclear@0
|
395 q.mValue = cur.Normalize();
|
nuclear@0
|
396 }
|
nuclear@0
|
397 nd->mRotationKeys[a] = q;
|
nuclear@0
|
398
|
nuclear@0
|
399 // need this to get to Assimp quaternion conventions
|
nuclear@0
|
400 nd->mRotationKeys[a].mValue.w *= -1.f;
|
nuclear@0
|
401 }
|
nuclear@0
|
402 }
|
nuclear@0
|
403 // copy scaling keys
|
nuclear@0
|
404 if (me->mAnim.akeyScaling.size() > 1 ) {
|
nuclear@0
|
405 // Allocate the key array and fill it
|
nuclear@0
|
406 nd->mNumScalingKeys = (unsigned int) me->mAnim.akeyScaling.size();
|
nuclear@0
|
407 nd->mScalingKeys = new aiVectorKey[nd->mNumScalingKeys];
|
nuclear@0
|
408
|
nuclear@0
|
409 ::memcpy(nd->mScalingKeys,&me->mAnim.akeyScaling[0],
|
nuclear@0
|
410 nd->mNumScalingKeys * sizeof(aiVectorKey));
|
nuclear@0
|
411 }
|
nuclear@0
|
412 }
|
nuclear@0
|
413 }
|
nuclear@0
|
414 }
|
nuclear@0
|
415 }
|
nuclear@0
|
416
|
nuclear@0
|
417 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
418 // Build output cameras
|
nuclear@0
|
419 void ASEImporter::BuildCameras()
|
nuclear@0
|
420 {
|
nuclear@0
|
421 if (!mParser->m_vCameras.empty()) {
|
nuclear@0
|
422 pcScene->mNumCameras = (unsigned int)mParser->m_vCameras.size();
|
nuclear@0
|
423 pcScene->mCameras = new aiCamera*[pcScene->mNumCameras];
|
nuclear@0
|
424
|
nuclear@0
|
425 for (unsigned int i = 0; i < pcScene->mNumCameras;++i) {
|
nuclear@0
|
426 aiCamera* out = pcScene->mCameras[i] = new aiCamera();
|
nuclear@0
|
427 ASE::Camera& in = mParser->m_vCameras[i];
|
nuclear@0
|
428
|
nuclear@0
|
429 // copy members
|
nuclear@0
|
430 out->mClipPlaneFar = in.mFar;
|
nuclear@0
|
431 out->mClipPlaneNear = (in.mNear ? in.mNear : 0.1f);
|
nuclear@0
|
432 out->mHorizontalFOV = in.mFOV;
|
nuclear@0
|
433
|
nuclear@0
|
434 out->mName.Set(in.mName);
|
nuclear@0
|
435 }
|
nuclear@0
|
436 }
|
nuclear@0
|
437 }
|
nuclear@0
|
438
|
nuclear@0
|
439 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
440 // Build output lights
|
nuclear@0
|
441 void ASEImporter::BuildLights()
|
nuclear@0
|
442 {
|
nuclear@0
|
443 if (!mParser->m_vLights.empty()) {
|
nuclear@0
|
444 pcScene->mNumLights = (unsigned int)mParser->m_vLights.size();
|
nuclear@0
|
445 pcScene->mLights = new aiLight*[pcScene->mNumLights];
|
nuclear@0
|
446
|
nuclear@0
|
447 for (unsigned int i = 0; i < pcScene->mNumLights;++i) {
|
nuclear@0
|
448 aiLight* out = pcScene->mLights[i] = new aiLight();
|
nuclear@0
|
449 ASE::Light& in = mParser->m_vLights[i];
|
nuclear@0
|
450
|
nuclear@0
|
451 // The direction is encoded in the transformation matrix of the node.
|
nuclear@0
|
452 // In 3DS MAX the light source points into negative Z direction if
|
nuclear@0
|
453 // the node transformation is the identity.
|
nuclear@0
|
454 out->mDirection = aiVector3D(0.f,0.f,-1.f);
|
nuclear@0
|
455
|
nuclear@0
|
456 out->mName.Set(in.mName);
|
nuclear@0
|
457 switch (in.mLightType)
|
nuclear@0
|
458 {
|
nuclear@0
|
459 case ASE::Light::TARGET:
|
nuclear@0
|
460 out->mType = aiLightSource_SPOT;
|
nuclear@0
|
461 out->mAngleInnerCone = AI_DEG_TO_RAD(in.mAngle);
|
nuclear@0
|
462 out->mAngleOuterCone = (in.mFalloff ? AI_DEG_TO_RAD(in.mFalloff) : out->mAngleInnerCone);
|
nuclear@0
|
463 break;
|
nuclear@0
|
464
|
nuclear@0
|
465 case ASE::Light::DIRECTIONAL:
|
nuclear@0
|
466 out->mType = aiLightSource_DIRECTIONAL;
|
nuclear@0
|
467 break;
|
nuclear@0
|
468
|
nuclear@0
|
469 default:
|
nuclear@0
|
470 //case ASE::Light::OMNI:
|
nuclear@0
|
471 out->mType = aiLightSource_POINT;
|
nuclear@0
|
472 break;
|
nuclear@0
|
473 };
|
nuclear@0
|
474 out->mColorDiffuse = out->mColorSpecular = in.mColor * in.mIntensity;
|
nuclear@0
|
475 }
|
nuclear@0
|
476 }
|
nuclear@0
|
477 }
|
nuclear@0
|
478
|
nuclear@0
|
479 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
480 void ASEImporter::AddNodes(const std::vector<BaseNode*>& nodes,
|
nuclear@0
|
481 aiNode* pcParent,const char* szName)
|
nuclear@0
|
482 {
|
nuclear@0
|
483 aiMatrix4x4 m;
|
nuclear@0
|
484 AddNodes(nodes,pcParent,szName,m);
|
nuclear@0
|
485 }
|
nuclear@0
|
486
|
nuclear@0
|
487 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
488 // Add meshes to a given node
|
nuclear@0
|
489 void ASEImporter::AddMeshes(const ASE::BaseNode* snode,aiNode* node)
|
nuclear@0
|
490 {
|
nuclear@0
|
491 for (unsigned int i = 0; i < pcScene->mNumMeshes;++i) {
|
nuclear@0
|
492 // Get the name of the mesh (the mesh instance has been temporarily stored in the third vertex color)
|
nuclear@0
|
493 const aiMesh* pcMesh = pcScene->mMeshes[i];
|
nuclear@0
|
494 const ASE::Mesh* mesh = (const ASE::Mesh*)pcMesh->mColors[2];
|
nuclear@0
|
495
|
nuclear@0
|
496 if (mesh == snode) {
|
nuclear@0
|
497 ++node->mNumMeshes;
|
nuclear@0
|
498 }
|
nuclear@0
|
499 }
|
nuclear@0
|
500
|
nuclear@0
|
501 if(node->mNumMeshes) {
|
nuclear@0
|
502 node->mMeshes = new unsigned int[node->mNumMeshes];
|
nuclear@0
|
503 for (unsigned int i = 0, p = 0; i < pcScene->mNumMeshes;++i) {
|
nuclear@0
|
504
|
nuclear@0
|
505 const aiMesh* pcMesh = pcScene->mMeshes[i];
|
nuclear@0
|
506 const ASE::Mesh* mesh = (const ASE::Mesh*)pcMesh->mColors[2];
|
nuclear@0
|
507 if (mesh == snode) {
|
nuclear@0
|
508 node->mMeshes[p++] = i;
|
nuclear@0
|
509
|
nuclear@0
|
510 // Transform all vertices of the mesh back into their local space ->
|
nuclear@0
|
511 // at the moment they are pretransformed
|
nuclear@0
|
512 aiMatrix4x4 m = mesh->mTransform;
|
nuclear@0
|
513 m.Inverse();
|
nuclear@0
|
514
|
nuclear@0
|
515 aiVector3D* pvCurPtr = pcMesh->mVertices;
|
nuclear@0
|
516 const aiVector3D* pvEndPtr = pvCurPtr + pcMesh->mNumVertices;
|
nuclear@0
|
517 while (pvCurPtr != pvEndPtr) {
|
nuclear@0
|
518 *pvCurPtr = m * (*pvCurPtr);
|
nuclear@0
|
519 pvCurPtr++;
|
nuclear@0
|
520 }
|
nuclear@0
|
521
|
nuclear@0
|
522 // Do the same for the normal vectors, if we have them.
|
nuclear@0
|
523 // As always, inverse transpose.
|
nuclear@0
|
524 if (pcMesh->mNormals) {
|
nuclear@0
|
525 aiMatrix3x3 m3 = aiMatrix3x3( mesh->mTransform );
|
nuclear@0
|
526 m3.Transpose();
|
nuclear@0
|
527
|
nuclear@0
|
528 pvCurPtr = pcMesh->mNormals;
|
nuclear@0
|
529 pvEndPtr = pvCurPtr + pcMesh->mNumVertices;
|
nuclear@0
|
530 while (pvCurPtr != pvEndPtr) {
|
nuclear@0
|
531 *pvCurPtr = m3 * (*pvCurPtr);
|
nuclear@0
|
532 pvCurPtr++;
|
nuclear@0
|
533 }
|
nuclear@0
|
534 }
|
nuclear@0
|
535 }
|
nuclear@0
|
536 }
|
nuclear@0
|
537 }
|
nuclear@0
|
538 }
|
nuclear@0
|
539
|
nuclear@0
|
540 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
541 // Add child nodes to a given parent node
|
nuclear@0
|
542 void ASEImporter::AddNodes (const std::vector<BaseNode*>& nodes,
|
nuclear@0
|
543 aiNode* pcParent, const char* szName,
|
nuclear@0
|
544 const aiMatrix4x4& mat)
|
nuclear@0
|
545 {
|
nuclear@0
|
546 const size_t len = szName ? ::strlen(szName) : 0;
|
nuclear@0
|
547 ai_assert(4 <= AI_MAX_NUMBER_OF_COLOR_SETS);
|
nuclear@0
|
548
|
nuclear@0
|
549 // Receives child nodes for the pcParent node
|
nuclear@0
|
550 std::vector<aiNode*> apcNodes;
|
nuclear@0
|
551
|
nuclear@0
|
552 // Now iterate through all nodes in the scene and search for one
|
nuclear@0
|
553 // which has *us* as parent.
|
nuclear@0
|
554 for (std::vector<BaseNode*>::const_iterator it = nodes.begin(), end = nodes.end(); it != end; ++it) {
|
nuclear@0
|
555 const BaseNode* snode = *it;
|
nuclear@0
|
556 if (szName) {
|
nuclear@0
|
557 if (len != snode->mParent.length() || ::strcmp(szName,snode->mParent.c_str()))
|
nuclear@0
|
558 continue;
|
nuclear@0
|
559 }
|
nuclear@0
|
560 else if (snode->mParent.length())
|
nuclear@0
|
561 continue;
|
nuclear@0
|
562
|
nuclear@0
|
563 (*it)->mProcessed = true;
|
nuclear@0
|
564
|
nuclear@0
|
565 // Allocate a new node and add it to the output data structure
|
nuclear@0
|
566 apcNodes.push_back(new aiNode());
|
nuclear@0
|
567 aiNode* node = apcNodes.back();
|
nuclear@0
|
568
|
nuclear@0
|
569 node->mName.Set((snode->mName.length() ? snode->mName.c_str() : "Unnamed_Node"));
|
nuclear@0
|
570 node->mParent = pcParent;
|
nuclear@0
|
571
|
nuclear@0
|
572 // Setup the transformation matrix of the node
|
nuclear@0
|
573 aiMatrix4x4 mParentAdjust = mat;
|
nuclear@0
|
574 mParentAdjust.Inverse();
|
nuclear@0
|
575 node->mTransformation = mParentAdjust*snode->mTransform;
|
nuclear@0
|
576
|
nuclear@0
|
577 // Add sub nodes - prevent stack overflow due to recursive parenting
|
nuclear@0
|
578 if (node->mName != node->mParent->mName) {
|
nuclear@0
|
579 AddNodes(nodes,node,node->mName.data,snode->mTransform);
|
nuclear@0
|
580 }
|
nuclear@0
|
581
|
nuclear@0
|
582 // Further processing depends on the type of the node
|
nuclear@0
|
583 if (snode->mType == ASE::BaseNode::Mesh) {
|
nuclear@0
|
584 // If the type of this node is "Mesh" we need to search
|
nuclear@0
|
585 // the list of output meshes in the data structure for
|
nuclear@0
|
586 // all those that belonged to this node once. This is
|
nuclear@0
|
587 // slightly inconvinient here and a better solution should
|
nuclear@0
|
588 // be used when this code is refactored next.
|
nuclear@0
|
589 AddMeshes(snode,node);
|
nuclear@0
|
590 }
|
nuclear@0
|
591 else if (is_not_qnan( snode->mTargetPosition.x )) {
|
nuclear@0
|
592 // If this is a target camera or light we generate a small
|
nuclear@0
|
593 // child node which marks the position of the camera
|
nuclear@0
|
594 // target (the direction information is contained in *this*
|
nuclear@0
|
595 // node's animation track but the exact target position
|
nuclear@0
|
596 // would be lost otherwise)
|
nuclear@0
|
597 if (!node->mNumChildren) {
|
nuclear@0
|
598 node->mChildren = new aiNode*[1];
|
nuclear@0
|
599 }
|
nuclear@0
|
600
|
nuclear@0
|
601 aiNode* nd = new aiNode();
|
nuclear@0
|
602
|
nuclear@0
|
603 nd->mName.Set ( snode->mName + ".Target" );
|
nuclear@0
|
604
|
nuclear@0
|
605 nd->mTransformation.a4 = snode->mTargetPosition.x - snode->mTransform.a4;
|
nuclear@0
|
606 nd->mTransformation.b4 = snode->mTargetPosition.y - snode->mTransform.b4;
|
nuclear@0
|
607 nd->mTransformation.c4 = snode->mTargetPosition.z - snode->mTransform.c4;
|
nuclear@0
|
608
|
nuclear@0
|
609 nd->mParent = node;
|
nuclear@0
|
610
|
nuclear@0
|
611 // The .Target node is always the first child node
|
nuclear@0
|
612 for (unsigned int m = 0; m < node->mNumChildren;++m)
|
nuclear@0
|
613 node->mChildren[m+1] = node->mChildren[m];
|
nuclear@0
|
614
|
nuclear@0
|
615 node->mChildren[0] = nd;
|
nuclear@0
|
616 node->mNumChildren++;
|
nuclear@0
|
617
|
nuclear@0
|
618 // What we did is so great, it is at least worth a debug message
|
nuclear@0
|
619 DefaultLogger::get()->debug("ASE: Generating separate target node ("+snode->mName+")");
|
nuclear@0
|
620 }
|
nuclear@0
|
621 }
|
nuclear@0
|
622
|
nuclear@0
|
623 // Allocate enough space for the child nodes
|
nuclear@0
|
624 // We allocate one slot more in case this is a target camera/light
|
nuclear@0
|
625 pcParent->mNumChildren = (unsigned int)apcNodes.size();
|
nuclear@0
|
626 if (pcParent->mNumChildren) {
|
nuclear@0
|
627 pcParent->mChildren = new aiNode*[apcNodes.size()+1 /* PLUS ONE !!! */];
|
nuclear@0
|
628
|
nuclear@0
|
629 // now build all nodes for our nice new children
|
nuclear@0
|
630 for (unsigned int p = 0; p < apcNodes.size();++p)
|
nuclear@0
|
631 pcParent->mChildren[p] = apcNodes[p];
|
nuclear@0
|
632 }
|
nuclear@0
|
633 return;
|
nuclear@0
|
634 }
|
nuclear@0
|
635
|
nuclear@0
|
636 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
637 // Build the output node graph
|
nuclear@0
|
638 void ASEImporter::BuildNodes(std::vector<BaseNode*>& nodes) {
|
nuclear@0
|
639 ai_assert(NULL != pcScene);
|
nuclear@0
|
640
|
nuclear@0
|
641 // allocate the one and only root node
|
nuclear@0
|
642 aiNode* root = pcScene->mRootNode = new aiNode();
|
nuclear@0
|
643 root->mName.Set("<ASERoot>");
|
nuclear@0
|
644
|
nuclear@0
|
645 // Setup the coordinate system transformation
|
nuclear@0
|
646 pcScene->mRootNode->mNumChildren = 1;
|
nuclear@0
|
647 pcScene->mRootNode->mChildren = new aiNode*[1];
|
nuclear@0
|
648 aiNode* ch = pcScene->mRootNode->mChildren[0] = new aiNode();
|
nuclear@0
|
649 ch->mParent = root;
|
nuclear@0
|
650
|
nuclear@0
|
651 // Change the transformation matrix of all nodes
|
nuclear@0
|
652 for (std::vector<BaseNode*>::iterator it = nodes.begin(), end = nodes.end();it != end; ++it) {
|
nuclear@0
|
653 aiMatrix4x4& m = (*it)->mTransform;
|
nuclear@0
|
654 m.Transpose(); // row-order vs column-order
|
nuclear@0
|
655 }
|
nuclear@0
|
656
|
nuclear@0
|
657 // add all nodes
|
nuclear@0
|
658 AddNodes(nodes,ch,NULL);
|
nuclear@0
|
659
|
nuclear@0
|
660 // now iterate through al nodes and find those that have not yet
|
nuclear@0
|
661 // been added to the nodegraph (= their parent could not be recognized)
|
nuclear@0
|
662 std::vector<const BaseNode*> aiList;
|
nuclear@0
|
663 for (std::vector<BaseNode*>::iterator it = nodes.begin(), end = nodes.end();it != end; ++it) {
|
nuclear@0
|
664 if ((*it)->mProcessed) {
|
nuclear@0
|
665 continue;
|
nuclear@0
|
666 }
|
nuclear@0
|
667
|
nuclear@0
|
668 // check whether our parent is known
|
nuclear@0
|
669 bool bKnowParent = false;
|
nuclear@0
|
670
|
nuclear@0
|
671 // search the list another time, starting *here* and try to find out whether
|
nuclear@0
|
672 // there is a node that references *us* as a parent
|
nuclear@0
|
673 for (std::vector<BaseNode*>::const_iterator it2 = nodes.begin();it2 != end; ++it2) {
|
nuclear@0
|
674 if (it2 == it) {
|
nuclear@0
|
675 continue;
|
nuclear@0
|
676 }
|
nuclear@0
|
677
|
nuclear@0
|
678 if ((*it2)->mName == (*it)->mParent) {
|
nuclear@0
|
679 bKnowParent = true;
|
nuclear@0
|
680 break;
|
nuclear@0
|
681 }
|
nuclear@0
|
682 }
|
nuclear@0
|
683 if (!bKnowParent) {
|
nuclear@0
|
684 aiList.push_back(*it);
|
nuclear@0
|
685 }
|
nuclear@0
|
686 }
|
nuclear@0
|
687
|
nuclear@0
|
688 // Are there ane orphaned nodes?
|
nuclear@0
|
689 if (!aiList.empty()) {
|
nuclear@0
|
690 std::vector<aiNode*> apcNodes;
|
nuclear@0
|
691 apcNodes.reserve(aiList.size() + pcScene->mRootNode->mNumChildren);
|
nuclear@0
|
692
|
nuclear@0
|
693 for (unsigned int i = 0; i < pcScene->mRootNode->mNumChildren;++i)
|
nuclear@0
|
694 apcNodes.push_back(pcScene->mRootNode->mChildren[i]);
|
nuclear@0
|
695
|
nuclear@0
|
696 delete[] pcScene->mRootNode->mChildren;
|
nuclear@0
|
697 for (std::vector<const BaseNode*>::/*const_*/iterator i = aiList.begin();i != aiList.end();++i) {
|
nuclear@0
|
698 const ASE::BaseNode* src = *i;
|
nuclear@0
|
699
|
nuclear@0
|
700 // The parent is not known, so we can assume that we must add
|
nuclear@0
|
701 // this node to the root node of the whole scene
|
nuclear@0
|
702 aiNode* pcNode = new aiNode();
|
nuclear@0
|
703 pcNode->mParent = pcScene->mRootNode;
|
nuclear@0
|
704 pcNode->mName.Set(src->mName);
|
nuclear@0
|
705 AddMeshes(src,pcNode);
|
nuclear@0
|
706 AddNodes(nodes,pcNode,pcNode->mName.data);
|
nuclear@0
|
707 apcNodes.push_back(pcNode);
|
nuclear@0
|
708 }
|
nuclear@0
|
709
|
nuclear@0
|
710 // Regenerate our output array
|
nuclear@0
|
711 pcScene->mRootNode->mChildren = new aiNode*[apcNodes.size()];
|
nuclear@0
|
712 for (unsigned int i = 0; i < apcNodes.size();++i)
|
nuclear@0
|
713 pcScene->mRootNode->mChildren[i] = apcNodes[i];
|
nuclear@0
|
714
|
nuclear@0
|
715 pcScene->mRootNode->mNumChildren = (unsigned int)apcNodes.size();
|
nuclear@0
|
716 }
|
nuclear@0
|
717
|
nuclear@0
|
718 // Reset the third color set to NULL - we used this field to store a temporary pointer
|
nuclear@0
|
719 for (unsigned int i = 0; i < pcScene->mNumMeshes;++i)
|
nuclear@0
|
720 pcScene->mMeshes[i]->mColors[2] = NULL;
|
nuclear@0
|
721
|
nuclear@0
|
722 // The root node should not have at least one child or the file is valid
|
nuclear@0
|
723 if (!pcScene->mRootNode->mNumChildren) {
|
nuclear@0
|
724 throw DeadlyImportError("ASE: No nodes loaded. The file is either empty or corrupt");
|
nuclear@0
|
725 }
|
nuclear@0
|
726
|
nuclear@0
|
727 // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
|
nuclear@0
|
728 pcScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
|
nuclear@0
|
729 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
|
nuclear@0
|
730 }
|
nuclear@0
|
731
|
nuclear@0
|
732 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
733 // Convert the imported data to the internal verbose representation
|
nuclear@0
|
734 void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh) {
|
nuclear@0
|
735 // allocate output storage
|
nuclear@0
|
736 std::vector<aiVector3D> mPositions;
|
nuclear@0
|
737 std::vector<aiVector3D> amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
|
nuclear@0
|
738 std::vector<aiColor4D> mVertexColors;
|
nuclear@0
|
739 std::vector<aiVector3D> mNormals;
|
nuclear@0
|
740 std::vector<BoneVertex> mBoneVertices;
|
nuclear@0
|
741
|
nuclear@0
|
742 unsigned int iSize = (unsigned int)mesh.mFaces.size() * 3;
|
nuclear@0
|
743 mPositions.resize(iSize);
|
nuclear@0
|
744
|
nuclear@0
|
745 // optional texture coordinates
|
nuclear@0
|
746 for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) {
|
nuclear@0
|
747 if (!mesh.amTexCoords[i].empty()) {
|
nuclear@0
|
748 amTexCoords[i].resize(iSize);
|
nuclear@0
|
749 }
|
nuclear@0
|
750 }
|
nuclear@0
|
751 // optional vertex colors
|
nuclear@0
|
752 if (!mesh.mVertexColors.empty()) {
|
nuclear@0
|
753 mVertexColors.resize(iSize);
|
nuclear@0
|
754 }
|
nuclear@0
|
755
|
nuclear@0
|
756 // optional vertex normals (vertex normals can simply be copied)
|
nuclear@0
|
757 if (!mesh.mNormals.empty()) {
|
nuclear@0
|
758 mNormals.resize(iSize);
|
nuclear@0
|
759 }
|
nuclear@0
|
760 // bone vertices. There is no need to change the bone list
|
nuclear@0
|
761 if (!mesh.mBoneVertices.empty()) {
|
nuclear@0
|
762 mBoneVertices.resize(iSize);
|
nuclear@0
|
763 }
|
nuclear@0
|
764
|
nuclear@0
|
765 // iterate through all faces in the mesh
|
nuclear@0
|
766 unsigned int iCurrent = 0, fi = 0;
|
nuclear@0
|
767 for (std::vector<ASE::Face>::iterator i = mesh.mFaces.begin();i != mesh.mFaces.end();++i,++fi) {
|
nuclear@0
|
768 for (unsigned int n = 0; n < 3;++n,++iCurrent)
|
nuclear@0
|
769 {
|
nuclear@0
|
770 mPositions[iCurrent] = mesh.mPositions[(*i).mIndices[n]];
|
nuclear@0
|
771
|
nuclear@0
|
772 // add texture coordinates
|
nuclear@0
|
773 for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) {
|
nuclear@0
|
774 if (mesh.amTexCoords[c].empty())break;
|
nuclear@0
|
775 amTexCoords[c][iCurrent] = mesh.amTexCoords[c][(*i).amUVIndices[c][n]];
|
nuclear@0
|
776 }
|
nuclear@0
|
777 // add vertex colors
|
nuclear@0
|
778 if (!mesh.mVertexColors.empty()) {
|
nuclear@0
|
779 mVertexColors[iCurrent] = mesh.mVertexColors[(*i).mColorIndices[n]];
|
nuclear@0
|
780 }
|
nuclear@0
|
781 // add normal vectors
|
nuclear@0
|
782 if (!mesh.mNormals.empty()) {
|
nuclear@0
|
783 mNormals[iCurrent] = mesh.mNormals[fi*3+n];
|
nuclear@0
|
784 mNormals[iCurrent].Normalize();
|
nuclear@0
|
785 }
|
nuclear@0
|
786
|
nuclear@0
|
787 // handle bone vertices
|
nuclear@0
|
788 if ((*i).mIndices[n] < mesh.mBoneVertices.size()) {
|
nuclear@0
|
789 // (sometimes this will cause bone verts to be duplicated
|
nuclear@0
|
790 // however, I' quite sure Schrompf' JoinVerticesStep
|
nuclear@0
|
791 // will fix that again ...)
|
nuclear@0
|
792 mBoneVertices[iCurrent] = mesh.mBoneVertices[(*i).mIndices[n]];
|
nuclear@0
|
793 }
|
nuclear@0
|
794 (*i).mIndices[n] = iCurrent;
|
nuclear@0
|
795 }
|
nuclear@0
|
796 }
|
nuclear@0
|
797
|
nuclear@0
|
798 // replace the old arrays
|
nuclear@0
|
799 mesh.mNormals = mNormals;
|
nuclear@0
|
800 mesh.mPositions = mPositions;
|
nuclear@0
|
801 mesh.mVertexColors = mVertexColors;
|
nuclear@0
|
802
|
nuclear@0
|
803 for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c)
|
nuclear@0
|
804 mesh.amTexCoords[c] = amTexCoords[c];
|
nuclear@0
|
805 }
|
nuclear@0
|
806
|
nuclear@0
|
807 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
808 // Copy a texture from the ASE structs to the output material
|
nuclear@0
|
809 void CopyASETexture(aiMaterial& mat, ASE::Texture& texture, aiTextureType type)
|
nuclear@0
|
810 {
|
nuclear@0
|
811 // Setup the texture name
|
nuclear@0
|
812 aiString tex;
|
nuclear@0
|
813 tex.Set( texture.mMapName);
|
nuclear@0
|
814 mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0));
|
nuclear@0
|
815
|
nuclear@0
|
816 // Setup the texture blend factor
|
nuclear@0
|
817 if (is_not_qnan(texture.mTextureBlend))
|
nuclear@0
|
818 mat.AddProperty<float>( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0));
|
nuclear@0
|
819
|
nuclear@0
|
820 // Setup texture UV transformations
|
nuclear@0
|
821 mat.AddProperty<float>(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0));
|
nuclear@0
|
822 }
|
nuclear@0
|
823
|
nuclear@0
|
824 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
825 // Convert from ASE material to output material
|
nuclear@0
|
826 void ASEImporter::ConvertMaterial(ASE::Material& mat)
|
nuclear@0
|
827 {
|
nuclear@0
|
828 // LARGE TODO: Much code her is copied from 3DS ... join them maybe?
|
nuclear@0
|
829
|
nuclear@0
|
830 // Allocate the output material
|
nuclear@0
|
831 mat.pcInstance = new aiMaterial();
|
nuclear@0
|
832
|
nuclear@0
|
833 // At first add the base ambient color of the
|
nuclear@0
|
834 // scene to the material
|
nuclear@0
|
835 mat.mAmbient.r += mParser->m_clrAmbient.r;
|
nuclear@0
|
836 mat.mAmbient.g += mParser->m_clrAmbient.g;
|
nuclear@0
|
837 mat.mAmbient.b += mParser->m_clrAmbient.b;
|
nuclear@0
|
838
|
nuclear@0
|
839 aiString name;
|
nuclear@0
|
840 name.Set( mat.mName);
|
nuclear@0
|
841 mat.pcInstance->AddProperty( &name, AI_MATKEY_NAME);
|
nuclear@0
|
842
|
nuclear@0
|
843 // material colors
|
nuclear@0
|
844 mat.pcInstance->AddProperty( &mat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
|
nuclear@0
|
845 mat.pcInstance->AddProperty( &mat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
|
nuclear@0
|
846 mat.pcInstance->AddProperty( &mat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
|
nuclear@0
|
847 mat.pcInstance->AddProperty( &mat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
|
nuclear@0
|
848
|
nuclear@0
|
849 // shininess
|
nuclear@0
|
850 if (0.0f != mat.mSpecularExponent && 0.0f != mat.mShininessStrength)
|
nuclear@0
|
851 {
|
nuclear@0
|
852 mat.pcInstance->AddProperty( &mat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
|
nuclear@0
|
853 mat.pcInstance->AddProperty( &mat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH);
|
nuclear@0
|
854 }
|
nuclear@0
|
855 // If there is no shininess, we can disable phong lighting
|
nuclear@0
|
856 else if (D3DS::Discreet3DS::Metal == mat.mShading ||
|
nuclear@0
|
857 D3DS::Discreet3DS::Phong == mat.mShading ||
|
nuclear@0
|
858 D3DS::Discreet3DS::Blinn == mat.mShading)
|
nuclear@0
|
859 {
|
nuclear@0
|
860 mat.mShading = D3DS::Discreet3DS::Gouraud;
|
nuclear@0
|
861 }
|
nuclear@0
|
862
|
nuclear@0
|
863 // opacity
|
nuclear@0
|
864 mat.pcInstance->AddProperty<float>( &mat.mTransparency,1,AI_MATKEY_OPACITY);
|
nuclear@0
|
865
|
nuclear@0
|
866 // Two sided rendering?
|
nuclear@0
|
867 if (mat.mTwoSided)
|
nuclear@0
|
868 {
|
nuclear@0
|
869 int i = 1;
|
nuclear@0
|
870 mat.pcInstance->AddProperty<int>(&i,1,AI_MATKEY_TWOSIDED);
|
nuclear@0
|
871 }
|
nuclear@0
|
872
|
nuclear@0
|
873 // shading mode
|
nuclear@0
|
874 aiShadingMode eShading = aiShadingMode_NoShading;
|
nuclear@0
|
875 switch (mat.mShading)
|
nuclear@0
|
876 {
|
nuclear@0
|
877 case D3DS::Discreet3DS::Flat:
|
nuclear@0
|
878 eShading = aiShadingMode_Flat; break;
|
nuclear@0
|
879 case D3DS::Discreet3DS::Phong :
|
nuclear@0
|
880 eShading = aiShadingMode_Phong; break;
|
nuclear@0
|
881 case D3DS::Discreet3DS::Blinn :
|
nuclear@0
|
882 eShading = aiShadingMode_Blinn; break;
|
nuclear@0
|
883
|
nuclear@0
|
884 // I don't know what "Wire" shading should be,
|
nuclear@0
|
885 // assume it is simple lambertian diffuse (L dot N) shading
|
nuclear@0
|
886 case D3DS::Discreet3DS::Wire:
|
nuclear@0
|
887 {
|
nuclear@0
|
888 // set the wireframe flag
|
nuclear@0
|
889 unsigned int iWire = 1;
|
nuclear@0
|
890 mat.pcInstance->AddProperty<int>( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME);
|
nuclear@0
|
891 }
|
nuclear@0
|
892 case D3DS::Discreet3DS::Gouraud:
|
nuclear@0
|
893 eShading = aiShadingMode_Gouraud; break;
|
nuclear@0
|
894 case D3DS::Discreet3DS::Metal :
|
nuclear@0
|
895 eShading = aiShadingMode_CookTorrance; break;
|
nuclear@0
|
896 }
|
nuclear@0
|
897 mat.pcInstance->AddProperty<int>( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL);
|
nuclear@0
|
898
|
nuclear@0
|
899 // DIFFUSE texture
|
nuclear@0
|
900 if( mat.sTexDiffuse.mMapName.length() > 0)
|
nuclear@0
|
901 CopyASETexture(*mat.pcInstance,mat.sTexDiffuse, aiTextureType_DIFFUSE);
|
nuclear@0
|
902
|
nuclear@0
|
903 // SPECULAR texture
|
nuclear@0
|
904 if( mat.sTexSpecular.mMapName.length() > 0)
|
nuclear@0
|
905 CopyASETexture(*mat.pcInstance,mat.sTexSpecular, aiTextureType_SPECULAR);
|
nuclear@0
|
906
|
nuclear@0
|
907 // AMBIENT texture
|
nuclear@0
|
908 if( mat.sTexAmbient.mMapName.length() > 0)
|
nuclear@0
|
909 CopyASETexture(*mat.pcInstance,mat.sTexAmbient, aiTextureType_AMBIENT);
|
nuclear@0
|
910
|
nuclear@0
|
911 // OPACITY texture
|
nuclear@0
|
912 if( mat.sTexOpacity.mMapName.length() > 0)
|
nuclear@0
|
913 CopyASETexture(*mat.pcInstance,mat.sTexOpacity, aiTextureType_OPACITY);
|
nuclear@0
|
914
|
nuclear@0
|
915 // EMISSIVE texture
|
nuclear@0
|
916 if( mat.sTexEmissive.mMapName.length() > 0)
|
nuclear@0
|
917 CopyASETexture(*mat.pcInstance,mat.sTexEmissive, aiTextureType_EMISSIVE);
|
nuclear@0
|
918
|
nuclear@0
|
919 // BUMP texture
|
nuclear@0
|
920 if( mat.sTexBump.mMapName.length() > 0)
|
nuclear@0
|
921 CopyASETexture(*mat.pcInstance,mat.sTexBump, aiTextureType_HEIGHT);
|
nuclear@0
|
922
|
nuclear@0
|
923 // SHININESS texture
|
nuclear@0
|
924 if( mat.sTexShininess.mMapName.length() > 0)
|
nuclear@0
|
925 CopyASETexture(*mat.pcInstance,mat.sTexShininess, aiTextureType_SHININESS);
|
nuclear@0
|
926
|
nuclear@0
|
927 // store the name of the material itself, too
|
nuclear@0
|
928 if( mat.mName.length() > 0) {
|
nuclear@0
|
929 aiString tex;tex.Set( mat.mName);
|
nuclear@0
|
930 mat.pcInstance->AddProperty( &tex, AI_MATKEY_NAME);
|
nuclear@0
|
931 }
|
nuclear@0
|
932 return;
|
nuclear@0
|
933 }
|
nuclear@0
|
934
|
nuclear@0
|
935 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
936 // Build output meshes
|
nuclear@0
|
937 void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, std::vector<aiMesh*>& avOutMeshes)
|
nuclear@0
|
938 {
|
nuclear@0
|
939 // validate the material index of the mesh
|
nuclear@0
|
940 if (mesh.iMaterialIndex >= mParser->m_vMaterials.size()) {
|
nuclear@0
|
941 mesh.iMaterialIndex = (unsigned int)mParser->m_vMaterials.size()-1;
|
nuclear@0
|
942 DefaultLogger::get()->warn("Material index is out of range");
|
nuclear@0
|
943 }
|
nuclear@0
|
944
|
nuclear@0
|
945 // If the material the mesh is assigned to is consisting of submeshes, split it
|
nuclear@0
|
946 if (!mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials.empty()) {
|
nuclear@0
|
947 std::vector<ASE::Material> vSubMaterials = mParser->
|
nuclear@0
|
948 m_vMaterials[mesh.iMaterialIndex].avSubMaterials;
|
nuclear@0
|
949
|
nuclear@0
|
950 std::vector<unsigned int>* aiSplit = new std::vector<unsigned int>[vSubMaterials.size()];
|
nuclear@0
|
951
|
nuclear@0
|
952 // build a list of all faces per submaterial
|
nuclear@0
|
953 for (unsigned int i = 0; i < mesh.mFaces.size();++i) {
|
nuclear@0
|
954 // check range
|
nuclear@0
|
955 if (mesh.mFaces[i].iMaterial >= vSubMaterials.size()) {
|
nuclear@0
|
956 DefaultLogger::get()->warn("Submaterial index is out of range");
|
nuclear@0
|
957
|
nuclear@0
|
958 // use the last material instead
|
nuclear@0
|
959 aiSplit[vSubMaterials.size()-1].push_back(i);
|
nuclear@0
|
960 }
|
nuclear@0
|
961 else aiSplit[mesh.mFaces[i].iMaterial].push_back(i);
|
nuclear@0
|
962 }
|
nuclear@0
|
963
|
nuclear@0
|
964 // now generate submeshes
|
nuclear@0
|
965 for (unsigned int p = 0; p < vSubMaterials.size();++p) {
|
nuclear@0
|
966 if (!aiSplit[p].empty()) {
|
nuclear@0
|
967
|
nuclear@0
|
968 aiMesh* p_pcOut = new aiMesh();
|
nuclear@0
|
969 p_pcOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
|
nuclear@0
|
970
|
nuclear@0
|
971 // let the sub material index
|
nuclear@0
|
972 p_pcOut->mMaterialIndex = p;
|
nuclear@0
|
973
|
nuclear@0
|
974 // we will need this material
|
nuclear@0
|
975 mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials[p].bNeed = true;
|
nuclear@0
|
976
|
nuclear@0
|
977 // store the real index here ... color channel 3
|
nuclear@0
|
978 p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex;
|
nuclear@0
|
979
|
nuclear@0
|
980 // store a pointer to the mesh in color channel 2
|
nuclear@0
|
981 p_pcOut->mColors[2] = (aiColor4D*) &mesh;
|
nuclear@0
|
982 avOutMeshes.push_back(p_pcOut);
|
nuclear@0
|
983
|
nuclear@0
|
984 // convert vertices
|
nuclear@0
|
985 p_pcOut->mNumVertices = (unsigned int)aiSplit[p].size()*3;
|
nuclear@0
|
986 p_pcOut->mNumFaces = (unsigned int)aiSplit[p].size();
|
nuclear@0
|
987
|
nuclear@0
|
988 // receive output vertex weights
|
nuclear@0
|
989 std::vector<std::pair<unsigned int, float> > *avOutputBones = NULL;
|
nuclear@0
|
990 if (!mesh.mBones.empty()) {
|
nuclear@0
|
991 avOutputBones = new std::vector<std::pair<unsigned int, float> >[mesh.mBones.size()];
|
nuclear@0
|
992 }
|
nuclear@0
|
993
|
nuclear@0
|
994 // allocate enough storage for faces
|
nuclear@0
|
995 p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces];
|
nuclear@0
|
996
|
nuclear@0
|
997 unsigned int iBase = 0,iIndex;
|
nuclear@0
|
998 if (p_pcOut->mNumVertices) {
|
nuclear@0
|
999 p_pcOut->mVertices = new aiVector3D[p_pcOut->mNumVertices];
|
nuclear@0
|
1000 p_pcOut->mNormals = new aiVector3D[p_pcOut->mNumVertices];
|
nuclear@0
|
1001 for (unsigned int q = 0; q < aiSplit[p].size();++q) {
|
nuclear@0
|
1002
|
nuclear@0
|
1003 iIndex = aiSplit[p][q];
|
nuclear@0
|
1004
|
nuclear@0
|
1005 p_pcOut->mFaces[q].mIndices = new unsigned int[3];
|
nuclear@0
|
1006 p_pcOut->mFaces[q].mNumIndices = 3;
|
nuclear@0
|
1007
|
nuclear@0
|
1008 for (unsigned int t = 0; t < 3;++t, ++iBase) {
|
nuclear@0
|
1009 const uint32_t iIndex2 = mesh.mFaces[iIndex].mIndices[t];
|
nuclear@0
|
1010
|
nuclear@0
|
1011 p_pcOut->mVertices[iBase] = mesh.mPositions [iIndex2];
|
nuclear@0
|
1012 p_pcOut->mNormals [iBase] = mesh.mNormals [iIndex2];
|
nuclear@0
|
1013
|
nuclear@0
|
1014 // convert bones, if existing
|
nuclear@0
|
1015 if (!mesh.mBones.empty()) {
|
nuclear@0
|
1016 // check whether there is a vertex weight for this vertex index
|
nuclear@0
|
1017 if (iIndex2 < mesh.mBoneVertices.size()) {
|
nuclear@0
|
1018
|
nuclear@0
|
1019 for (std::vector<std::pair<int,float> >::const_iterator
|
nuclear@0
|
1020 blubb = mesh.mBoneVertices[iIndex2].mBoneWeights.begin();
|
nuclear@0
|
1021 blubb != mesh.mBoneVertices[iIndex2].mBoneWeights.end();++blubb) {
|
nuclear@0
|
1022
|
nuclear@0
|
1023 // NOTE: illegal cases have already been filtered out
|
nuclear@0
|
1024 avOutputBones[(*blubb).first].push_back(std::pair<unsigned int, float>(
|
nuclear@0
|
1025 iBase,(*blubb).second));
|
nuclear@0
|
1026 }
|
nuclear@0
|
1027 }
|
nuclear@0
|
1028 }
|
nuclear@0
|
1029 p_pcOut->mFaces[q].mIndices[t] = iBase;
|
nuclear@0
|
1030 }
|
nuclear@0
|
1031 }
|
nuclear@0
|
1032 }
|
nuclear@0
|
1033 // convert texture coordinates (up to AI_MAX_NUMBER_OF_TEXTURECOORDS sets supported)
|
nuclear@0
|
1034 for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) {
|
nuclear@0
|
1035 if (!mesh.amTexCoords[c].empty())
|
nuclear@0
|
1036 {
|
nuclear@0
|
1037 p_pcOut->mTextureCoords[c] = new aiVector3D[p_pcOut->mNumVertices];
|
nuclear@0
|
1038 iBase = 0;
|
nuclear@0
|
1039 for (unsigned int q = 0; q < aiSplit[p].size();++q) {
|
nuclear@0
|
1040 iIndex = aiSplit[p][q];
|
nuclear@0
|
1041 for (unsigned int t = 0; t < 3;++t) {
|
nuclear@0
|
1042 p_pcOut->mTextureCoords[c][iBase++] = mesh.amTexCoords[c][mesh.mFaces[iIndex].mIndices[t]];
|
nuclear@0
|
1043 }
|
nuclear@0
|
1044 }
|
nuclear@0
|
1045 // Setup the number of valid vertex components
|
nuclear@0
|
1046 p_pcOut->mNumUVComponents[c] = mesh.mNumUVComponents[c];
|
nuclear@0
|
1047 }
|
nuclear@0
|
1048 }
|
nuclear@0
|
1049
|
nuclear@0
|
1050 // Convert vertex colors (only one set supported)
|
nuclear@0
|
1051 if (!mesh.mVertexColors.empty()){
|
nuclear@0
|
1052 p_pcOut->mColors[0] = new aiColor4D[p_pcOut->mNumVertices];
|
nuclear@0
|
1053 iBase = 0;
|
nuclear@0
|
1054 for (unsigned int q = 0; q < aiSplit[p].size();++q) {
|
nuclear@0
|
1055 iIndex = aiSplit[p][q];
|
nuclear@0
|
1056 for (unsigned int t = 0; t < 3;++t) {
|
nuclear@0
|
1057 p_pcOut->mColors[0][iBase++] = mesh.mVertexColors[mesh.mFaces[iIndex].mIndices[t]];
|
nuclear@0
|
1058 }
|
nuclear@0
|
1059 }
|
nuclear@0
|
1060 }
|
nuclear@0
|
1061 // Copy bones
|
nuclear@0
|
1062 if (!mesh.mBones.empty()) {
|
nuclear@0
|
1063 p_pcOut->mNumBones = 0;
|
nuclear@0
|
1064 for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock)
|
nuclear@0
|
1065 if (!avOutputBones[mrspock].empty())p_pcOut->mNumBones++;
|
nuclear@0
|
1066
|
nuclear@0
|
1067 p_pcOut->mBones = new aiBone* [ p_pcOut->mNumBones ];
|
nuclear@0
|
1068 aiBone** pcBone = p_pcOut->mBones;
|
nuclear@0
|
1069 for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock)
|
nuclear@0
|
1070 {
|
nuclear@0
|
1071 if (!avOutputBones[mrspock].empty()) {
|
nuclear@0
|
1072 // we will need this bone. add it to the output mesh and
|
nuclear@0
|
1073 // add all per-vertex weights
|
nuclear@0
|
1074 aiBone* pc = *pcBone = new aiBone();
|
nuclear@0
|
1075 pc->mName.Set(mesh.mBones[mrspock].mName);
|
nuclear@0
|
1076
|
nuclear@0
|
1077 pc->mNumWeights = (unsigned int)avOutputBones[mrspock].size();
|
nuclear@0
|
1078 pc->mWeights = new aiVertexWeight[pc->mNumWeights];
|
nuclear@0
|
1079
|
nuclear@0
|
1080 for (unsigned int captainkirk = 0; captainkirk < pc->mNumWeights;++captainkirk)
|
nuclear@0
|
1081 {
|
nuclear@0
|
1082 const std::pair<unsigned int,float>& ref = avOutputBones[mrspock][captainkirk];
|
nuclear@0
|
1083 pc->mWeights[captainkirk].mVertexId = ref.first;
|
nuclear@0
|
1084 pc->mWeights[captainkirk].mWeight = ref.second;
|
nuclear@0
|
1085 }
|
nuclear@0
|
1086 ++pcBone;
|
nuclear@0
|
1087 }
|
nuclear@0
|
1088 }
|
nuclear@0
|
1089 // delete allocated storage
|
nuclear@0
|
1090 delete[] avOutputBones;
|
nuclear@0
|
1091 }
|
nuclear@0
|
1092 }
|
nuclear@0
|
1093 }
|
nuclear@0
|
1094 // delete storage
|
nuclear@0
|
1095 delete[] aiSplit;
|
nuclear@0
|
1096 }
|
nuclear@0
|
1097 else
|
nuclear@0
|
1098 {
|
nuclear@0
|
1099 // Otherwise we can simply copy the data to one output mesh
|
nuclear@0
|
1100 // This codepath needs less memory and uses fast memcpy()s
|
nuclear@0
|
1101 // to do the actual copying. So I think it is worth the
|
nuclear@0
|
1102 // effort here.
|
nuclear@0
|
1103
|
nuclear@0
|
1104 aiMesh* p_pcOut = new aiMesh();
|
nuclear@0
|
1105 p_pcOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
|
nuclear@0
|
1106
|
nuclear@0
|
1107 // set an empty sub material index
|
nuclear@0
|
1108 p_pcOut->mMaterialIndex = ASE::Face::DEFAULT_MATINDEX;
|
nuclear@0
|
1109 mParser->m_vMaterials[mesh.iMaterialIndex].bNeed = true;
|
nuclear@0
|
1110
|
nuclear@0
|
1111 // store the real index here ... in color channel 3
|
nuclear@0
|
1112 p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex;
|
nuclear@0
|
1113
|
nuclear@0
|
1114 // store a pointer to the mesh in color channel 2
|
nuclear@0
|
1115 p_pcOut->mColors[2] = (aiColor4D*) &mesh;
|
nuclear@0
|
1116 avOutMeshes.push_back(p_pcOut);
|
nuclear@0
|
1117
|
nuclear@0
|
1118 // If the mesh hasn't faces or vertices, there are two cases
|
nuclear@0
|
1119 // possible: 1. the model is invalid. 2. This is a dummy
|
nuclear@0
|
1120 // helper object which we are going to remove later ...
|
nuclear@0
|
1121 if (mesh.mFaces.empty() || mesh.mPositions.empty()) {
|
nuclear@0
|
1122 return;
|
nuclear@0
|
1123 }
|
nuclear@0
|
1124
|
nuclear@0
|
1125 // convert vertices
|
nuclear@0
|
1126 p_pcOut->mNumVertices = (unsigned int)mesh.mPositions.size();
|
nuclear@0
|
1127 p_pcOut->mNumFaces = (unsigned int)mesh.mFaces.size();
|
nuclear@0
|
1128
|
nuclear@0
|
1129 // allocate enough storage for faces
|
nuclear@0
|
1130 p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces];
|
nuclear@0
|
1131
|
nuclear@0
|
1132 // copy vertices
|
nuclear@0
|
1133 p_pcOut->mVertices = new aiVector3D[mesh.mPositions.size()];
|
nuclear@0
|
1134 memcpy(p_pcOut->mVertices,&mesh.mPositions[0],
|
nuclear@0
|
1135 mesh.mPositions.size() * sizeof(aiVector3D));
|
nuclear@0
|
1136
|
nuclear@0
|
1137 // copy normals
|
nuclear@0
|
1138 p_pcOut->mNormals = new aiVector3D[mesh.mNormals.size()];
|
nuclear@0
|
1139 memcpy(p_pcOut->mNormals,&mesh.mNormals[0],
|
nuclear@0
|
1140 mesh.mNormals.size() * sizeof(aiVector3D));
|
nuclear@0
|
1141
|
nuclear@0
|
1142 // copy texture coordinates
|
nuclear@0
|
1143 for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) {
|
nuclear@0
|
1144 if (!mesh.amTexCoords[c].empty()) {
|
nuclear@0
|
1145 p_pcOut->mTextureCoords[c] = new aiVector3D[mesh.amTexCoords[c].size()];
|
nuclear@0
|
1146 memcpy(p_pcOut->mTextureCoords[c],&mesh.amTexCoords[c][0],
|
nuclear@0
|
1147 mesh.amTexCoords[c].size() * sizeof(aiVector3D));
|
nuclear@0
|
1148
|
nuclear@0
|
1149 // setup the number of valid vertex components
|
nuclear@0
|
1150 p_pcOut->mNumUVComponents[c] = mesh.mNumUVComponents[c];
|
nuclear@0
|
1151 }
|
nuclear@0
|
1152 }
|
nuclear@0
|
1153
|
nuclear@0
|
1154 // copy vertex colors
|
nuclear@0
|
1155 if (!mesh.mVertexColors.empty()) {
|
nuclear@0
|
1156 p_pcOut->mColors[0] = new aiColor4D[mesh.mVertexColors.size()];
|
nuclear@0
|
1157 memcpy(p_pcOut->mColors[0],&mesh.mVertexColors[0],
|
nuclear@0
|
1158 mesh.mVertexColors.size() * sizeof(aiColor4D));
|
nuclear@0
|
1159 }
|
nuclear@0
|
1160
|
nuclear@0
|
1161 // copy faces
|
nuclear@0
|
1162 for (unsigned int iFace = 0; iFace < p_pcOut->mNumFaces;++iFace) {
|
nuclear@0
|
1163 p_pcOut->mFaces[iFace].mNumIndices = 3;
|
nuclear@0
|
1164 p_pcOut->mFaces[iFace].mIndices = new unsigned int[3];
|
nuclear@0
|
1165
|
nuclear@0
|
1166 // copy indices
|
nuclear@0
|
1167 p_pcOut->mFaces[iFace].mIndices[0] = mesh.mFaces[iFace].mIndices[0];
|
nuclear@0
|
1168 p_pcOut->mFaces[iFace].mIndices[1] = mesh.mFaces[iFace].mIndices[1];
|
nuclear@0
|
1169 p_pcOut->mFaces[iFace].mIndices[2] = mesh.mFaces[iFace].mIndices[2];
|
nuclear@0
|
1170 }
|
nuclear@0
|
1171
|
nuclear@0
|
1172 // copy vertex bones
|
nuclear@0
|
1173 if (!mesh.mBones.empty() && !mesh.mBoneVertices.empty()) {
|
nuclear@0
|
1174 std::vector<std::vector<aiVertexWeight> > avBonesOut( mesh.mBones.size() );
|
nuclear@0
|
1175
|
nuclear@0
|
1176 // find all vertex weights for this bone
|
nuclear@0
|
1177 unsigned int quak = 0;
|
nuclear@0
|
1178 for (std::vector<BoneVertex>::const_iterator harrypotter = mesh.mBoneVertices.begin();
|
nuclear@0
|
1179 harrypotter != mesh.mBoneVertices.end();++harrypotter,++quak) {
|
nuclear@0
|
1180
|
nuclear@0
|
1181 for (std::vector<std::pair<int,float> >::const_iterator
|
nuclear@0
|
1182 ronaldweasley = (*harrypotter).mBoneWeights.begin();
|
nuclear@0
|
1183 ronaldweasley != (*harrypotter).mBoneWeights.end();++ronaldweasley)
|
nuclear@0
|
1184 {
|
nuclear@0
|
1185 aiVertexWeight weight;
|
nuclear@0
|
1186 weight.mVertexId = quak;
|
nuclear@0
|
1187 weight.mWeight = (*ronaldweasley).second;
|
nuclear@0
|
1188 avBonesOut[(*ronaldweasley).first].push_back(weight);
|
nuclear@0
|
1189 }
|
nuclear@0
|
1190 }
|
nuclear@0
|
1191
|
nuclear@0
|
1192 // now build a final bone list
|
nuclear@0
|
1193 p_pcOut->mNumBones = 0;
|
nuclear@0
|
1194 for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy)
|
nuclear@0
|
1195 if (!avBonesOut[jfkennedy].empty())p_pcOut->mNumBones++;
|
nuclear@0
|
1196
|
nuclear@0
|
1197 p_pcOut->mBones = new aiBone*[p_pcOut->mNumBones];
|
nuclear@0
|
1198 aiBone** pcBone = p_pcOut->mBones;
|
nuclear@0
|
1199 for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy) {
|
nuclear@0
|
1200 if (!avBonesOut[jfkennedy].empty()) {
|
nuclear@0
|
1201 aiBone* pc = *pcBone = new aiBone();
|
nuclear@0
|
1202 pc->mName.Set(mesh.mBones[jfkennedy].mName);
|
nuclear@0
|
1203 pc->mNumWeights = (unsigned int)avBonesOut[jfkennedy].size();
|
nuclear@0
|
1204 pc->mWeights = new aiVertexWeight[pc->mNumWeights];
|
nuclear@0
|
1205 ::memcpy(pc->mWeights,&avBonesOut[jfkennedy][0],
|
nuclear@0
|
1206 sizeof(aiVertexWeight) * pc->mNumWeights);
|
nuclear@0
|
1207 ++pcBone;
|
nuclear@0
|
1208 }
|
nuclear@0
|
1209 }
|
nuclear@0
|
1210 }
|
nuclear@0
|
1211 }
|
nuclear@0
|
1212 }
|
nuclear@0
|
1213
|
nuclear@0
|
1214 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1215 // Setup proper material indices and build output materials
|
nuclear@0
|
1216 void ASEImporter::BuildMaterialIndices()
|
nuclear@0
|
1217 {
|
nuclear@0
|
1218 ai_assert(NULL != pcScene);
|
nuclear@0
|
1219
|
nuclear@0
|
1220 // iterate through all materials and check whether we need them
|
nuclear@0
|
1221 for (unsigned int iMat = 0; iMat < mParser->m_vMaterials.size();++iMat)
|
nuclear@0
|
1222 {
|
nuclear@0
|
1223 ASE::Material& mat = mParser->m_vMaterials[iMat];
|
nuclear@0
|
1224 if (mat.bNeed) {
|
nuclear@0
|
1225 // Convert it to the aiMaterial layout
|
nuclear@0
|
1226 ConvertMaterial(mat);
|
nuclear@0
|
1227 ++pcScene->mNumMaterials;
|
nuclear@0
|
1228 }
|
nuclear@0
|
1229 for (unsigned int iSubMat = 0; iSubMat < mat.avSubMaterials.size();++iSubMat)
|
nuclear@0
|
1230 {
|
nuclear@0
|
1231 ASE::Material& submat = mat.avSubMaterials[iSubMat];
|
nuclear@0
|
1232 if (submat.bNeed) {
|
nuclear@0
|
1233 // Convert it to the aiMaterial layout
|
nuclear@0
|
1234 ConvertMaterial(submat);
|
nuclear@0
|
1235 ++pcScene->mNumMaterials;
|
nuclear@0
|
1236 }
|
nuclear@0
|
1237 }
|
nuclear@0
|
1238 }
|
nuclear@0
|
1239
|
nuclear@0
|
1240 // allocate the output material array
|
nuclear@0
|
1241 pcScene->mMaterials = new aiMaterial*[pcScene->mNumMaterials];
|
nuclear@0
|
1242 D3DS::Material** pcIntMaterials = new D3DS::Material*[pcScene->mNumMaterials];
|
nuclear@0
|
1243
|
nuclear@0
|
1244 unsigned int iNum = 0;
|
nuclear@0
|
1245 for (unsigned int iMat = 0; iMat < mParser->m_vMaterials.size();++iMat) {
|
nuclear@0
|
1246 ASE::Material& mat = mParser->m_vMaterials[iMat];
|
nuclear@0
|
1247 if (mat.bNeed)
|
nuclear@0
|
1248 {
|
nuclear@0
|
1249 ai_assert(NULL != mat.pcInstance);
|
nuclear@0
|
1250 pcScene->mMaterials[iNum] = mat.pcInstance;
|
nuclear@0
|
1251
|
nuclear@0
|
1252 // Store the internal material, too
|
nuclear@0
|
1253 pcIntMaterials[iNum] = &mat;
|
nuclear@0
|
1254
|
nuclear@0
|
1255 // Iterate through all meshes and search for one which is using
|
nuclear@0
|
1256 // this top-level material index
|
nuclear@0
|
1257 for (unsigned int iMesh = 0; iMesh < pcScene->mNumMeshes;++iMesh)
|
nuclear@0
|
1258 {
|
nuclear@0
|
1259 aiMesh* mesh = pcScene->mMeshes[iMesh];
|
nuclear@0
|
1260 if (ASE::Face::DEFAULT_MATINDEX == mesh->mMaterialIndex &&
|
nuclear@0
|
1261 iMat == (uintptr_t)mesh->mColors[3])
|
nuclear@0
|
1262 {
|
nuclear@0
|
1263 mesh->mMaterialIndex = iNum;
|
nuclear@0
|
1264 mesh->mColors[3] = NULL;
|
nuclear@0
|
1265 }
|
nuclear@0
|
1266 }
|
nuclear@0
|
1267 iNum++;
|
nuclear@0
|
1268 }
|
nuclear@0
|
1269 for (unsigned int iSubMat = 0; iSubMat < mat.avSubMaterials.size();++iSubMat) {
|
nuclear@0
|
1270 ASE::Material& submat = mat.avSubMaterials[iSubMat];
|
nuclear@0
|
1271 if (submat.bNeed) {
|
nuclear@0
|
1272 ai_assert(NULL != submat.pcInstance);
|
nuclear@0
|
1273 pcScene->mMaterials[iNum] = submat.pcInstance;
|
nuclear@0
|
1274
|
nuclear@0
|
1275 // Store the internal material, too
|
nuclear@0
|
1276 pcIntMaterials[iNum] = &submat;
|
nuclear@0
|
1277
|
nuclear@0
|
1278 // Iterate through all meshes and search for one which is using
|
nuclear@0
|
1279 // this sub-level material index
|
nuclear@0
|
1280 for (unsigned int iMesh = 0; iMesh < pcScene->mNumMeshes;++iMesh) {
|
nuclear@0
|
1281 aiMesh* mesh = pcScene->mMeshes[iMesh];
|
nuclear@0
|
1282
|
nuclear@0
|
1283 if (iSubMat == mesh->mMaterialIndex && iMat == (uintptr_t)mesh->mColors[3]) {
|
nuclear@0
|
1284 mesh->mMaterialIndex = iNum;
|
nuclear@0
|
1285 mesh->mColors[3] = NULL;
|
nuclear@0
|
1286 }
|
nuclear@0
|
1287 }
|
nuclear@0
|
1288 iNum++;
|
nuclear@0
|
1289 }
|
nuclear@0
|
1290 }
|
nuclear@0
|
1291 }
|
nuclear@0
|
1292
|
nuclear@0
|
1293 // Dekete our temporary array
|
nuclear@0
|
1294 delete[] pcIntMaterials;
|
nuclear@0
|
1295 }
|
nuclear@0
|
1296
|
nuclear@0
|
1297 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1298 // Generate normal vectors basing on smoothing groups
|
nuclear@0
|
1299 bool ASEImporter::GenerateNormals(ASE::Mesh& mesh) {
|
nuclear@0
|
1300
|
nuclear@0
|
1301 if (!mesh.mNormals.empty() && !configRecomputeNormals)
|
nuclear@0
|
1302 {
|
nuclear@0
|
1303 // Check whether there are only uninitialized normals. If there are
|
nuclear@0
|
1304 // some, skip all normals from the file and compute them on our own
|
nuclear@0
|
1305 for (std::vector<aiVector3D>::const_iterator qq = mesh.mNormals.begin();qq != mesh.mNormals.end();++qq) {
|
nuclear@0
|
1306 if ((*qq).x || (*qq).y || (*qq).z)
|
nuclear@0
|
1307 {
|
nuclear@0
|
1308 return true;
|
nuclear@0
|
1309 }
|
nuclear@0
|
1310 }
|
nuclear@0
|
1311 }
|
nuclear@0
|
1312 // The array is reused.
|
nuclear@0
|
1313 ComputeNormalsWithSmoothingsGroups<ASE::Face>(mesh);
|
nuclear@0
|
1314 return false;
|
nuclear@0
|
1315 }
|
nuclear@0
|
1316
|
nuclear@0
|
1317 #endif // !! ASSIMP_BUILD_NO_BASE_IMPORTER
|