vrshoot

view libs/assimp/OgreMesh.cpp @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +0200
parents
children
line source
1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
5 Copyright (c) 2006-2012, assimp team
6 All rights reserved.
8 Redistribution and use of this software in source and binary forms,
9 with or without modification, are permitted provided that the
10 following conditions are met:
12 * Redistributions of source code must retain the above
13 copyright notice, this list of conditions and the
14 following disclaimer.
16 * Redistributions in binary form must reproduce the above
17 copyright notice, this list of conditions and the
18 following disclaimer in the documentation and/or other
19 materials provided with the distribution.
21 * Neither the name of the assimp team, nor the names of its
22 contributors may be used to endorse or promote products
23 derived from this software without specific prior
24 written permission of the assimp team.
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 ----------------------------------------------------------------------
39 */
41 #include "AssimpPCH.h"
43 #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
45 #include "OgreImporter.hpp"
46 #include "TinyFormatter.h"
48 using namespace std;
50 namespace Assimp
51 {
52 namespace Ogre
53 {
56 void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader)
57 {
58 if(Reader->getAttributeValue("usesharedvertices"))
59 theSubMesh.SharedData=GetAttribute<bool>(Reader, "usesharedvertices");
61 XmlRead(Reader);
62 //TODO: maybe we have alsways just 1 faces and 1 geometry and always in this order. this loop will only work correct, when the order
63 //of faces and geometry changed, and not if we have more than one of one
64 while( Reader->getNodeName()==string("faces")
65 || Reader->getNodeName()==string("geometry")
66 || Reader->getNodeName()==string("boneassignments"))
67 {
68 if(string(Reader->getNodeName())=="faces")//Read the face list
69 {
70 //some info logging:
71 unsigned int NumFaces=GetAttribute<int>(Reader, "count");
72 ostringstream ss; ss <<"Submesh has " << NumFaces << " Faces.";
73 DefaultLogger::get()->debug(ss.str());
75 while(XmlRead(Reader) && Reader->getNodeName()==string("face"))
76 {
77 Face NewFace;
78 NewFace.VertexIndices[0]=GetAttribute<int>(Reader, "v1");
79 NewFace.VertexIndices[1]=GetAttribute<int>(Reader, "v2");
80 NewFace.VertexIndices[2]=GetAttribute<int>(Reader, "v3");
81 if(Reader->getAttributeValue("v4"))//this should be supported in the future
82 {
83 DefaultLogger::get()->warn("Submesh has quads, only traingles are supported!");
84 //throw DeadlyImportError("Submesh has quads, only traingles are supported!");
85 }
86 theSubMesh.FaceList.push_back(NewFace);
87 }
89 }//end of faces
90 else if(string(Reader->getNodeName())=="geometry")//Read the vertexdata
91 {
92 //some info logging:
93 unsigned int NumVertices=GetAttribute<int>(Reader, "vertexcount");
94 ostringstream ss; ss<<"VertexCount: " << NumVertices;
95 DefaultLogger::get()->debug(ss.str());
97 //General Informations about vertices
98 XmlRead(Reader);
99 while(Reader->getNodeName()==string("vertexbuffer"))
100 {
101 ReadVertexBuffer(theSubMesh, Reader, NumVertices);
102 }
104 //some error checking on the loaded data
105 if(!theSubMesh.HasPositions)
106 throw DeadlyImportError("No positions could be loaded!");
108 if(theSubMesh.HasNormals && theSubMesh.Normals.size() != NumVertices)
109 throw DeadlyImportError("Wrong Number of Normals loaded!");
111 if(theSubMesh.HasTangents && theSubMesh.Tangents.size() != NumVertices)
112 throw DeadlyImportError("Wrong Number of Tangents loaded!");
114 for(unsigned int i=0; i<theSubMesh.Uvs.size(); ++i)
115 {
116 if(theSubMesh.Uvs[i].size() != NumVertices)
117 throw DeadlyImportError("Wrong Number of Uvs loaded!");
118 }
120 }//end of "geometry
123 else if(Reader->getNodeName()==string("boneassignments"))
124 {
125 ReadBoneWeights(theSubMesh, Reader);
126 }
127 }
128 DefaultLogger::get()->debug((Formatter::format(),
129 "Positionen: ",theSubMesh.Positions.size(),
130 " Normale: ",theSubMesh.Normals.size(),
131 " TexCoords: ",theSubMesh.Uvs.size(),
132 " Tantents: ",theSubMesh.Tangents.size()
133 ));
134 }
137 void OgreImporter::ReadVertexBuffer(SubMesh &theSubMesh, XmlReader *Reader, unsigned int NumVertices)
138 {
139 DefaultLogger::get()->debug("new Vertex Buffer");
141 bool ReadPositions=false;
142 bool ReadNormals=false;
143 bool ReadTangents=false;
144 unsigned int NumUvs=0;
146 //-------------------- check, what we need to read: --------------------------------
147 if(Reader->getAttributeValue("positions") && GetAttribute<bool>(Reader, "positions"))
148 {
149 ReadPositions=theSubMesh.HasPositions=true;
150 theSubMesh.Positions.reserve(NumVertices);
151 DefaultLogger::get()->debug("reading positions");
152 }
153 if(Reader->getAttributeValue("normals") && GetAttribute<bool>(Reader, "normals"))
154 {
155 ReadNormals=theSubMesh.HasNormals=true;
156 theSubMesh.Normals.reserve(NumVertices);
157 DefaultLogger::get()->debug("reading normals");
158 }
159 if(Reader->getAttributeValue("tangents") && GetAttribute<bool>(Reader, "tangents"))
160 {
161 ReadTangents=theSubMesh.HasTangents=true;
162 theSubMesh.Tangents.reserve(NumVertices);
163 DefaultLogger::get()->debug("reading tangents");
164 }
166 if(Reader->getAttributeValue("texture_coords"))
167 {
168 NumUvs=GetAttribute<unsigned int>(Reader, "texture_coords");
169 theSubMesh.Uvs.resize(NumUvs);
170 for(unsigned int i=0; i<theSubMesh.Uvs.size(); ++i) theSubMesh.Uvs[i].reserve(NumVertices);
171 DefaultLogger::get()->debug("reading texture coords");
172 }
173 //___________________________________________________________________
176 //check if we will load anything
177 if(!( ReadPositions || ReadNormals || ReadTangents || (NumUvs>0) ))
178 DefaultLogger::get()->warn("vertexbuffer seams to be empty!");
181 //read all the vertices:
182 XmlRead(Reader);
184 /*it might happen, that we have more than one attribute per vertex (they are not splitted to different buffers)
185 so the break condition is a bit tricky */
186 while(Reader->getNodeName()==string("vertex")
187 ||Reader->getNodeName()==string("position")
188 ||Reader->getNodeName()==string("normal")
189 ||Reader->getNodeName()==string("tangent")
190 ||Reader->getNodeName()==string("texcoord")
191 ||Reader->getNodeName()==string("colour_diffuse"))
192 {
193 if(Reader->getNodeName()==string("vertex"))
194 XmlRead(Reader);//Read an attribute tag
196 //Position
197 if(ReadPositions && Reader->getNodeName()==string("position"))
198 {
199 aiVector3D NewPos;
200 NewPos.x=GetAttribute<float>(Reader, "x");
201 NewPos.y=GetAttribute<float>(Reader, "y");
202 NewPos.z=GetAttribute<float>(Reader, "z");
203 theSubMesh.Positions.push_back(NewPos);
204 }
206 //Normal
207 else if(ReadNormals && Reader->getNodeName()==string("normal"))
208 {
209 aiVector3D NewNormal;
210 NewNormal.x=GetAttribute<float>(Reader, "x");
211 NewNormal.y=GetAttribute<float>(Reader, "y");
212 NewNormal.z=GetAttribute<float>(Reader, "z");
213 theSubMesh.Normals.push_back(NewNormal);
214 }
216 //Tangent
217 else if(ReadTangents && Reader->getNodeName()==string("tangent"))
218 {
219 aiVector3D NewTangent;
220 NewTangent.x=GetAttribute<float>(Reader, "x");
221 NewTangent.y=GetAttribute<float>(Reader, "y");
222 NewTangent.z=GetAttribute<float>(Reader, "z");
223 theSubMesh.Tangents.push_back(NewTangent);
224 }
226 //Uv:
227 else if(NumUvs>0 && Reader->getNodeName()==string("texcoord"))
228 {
229 for(unsigned int i=0; i<NumUvs; ++i)
230 {
231 if(Reader->getNodeName()!=string("texcoord"))
232 {
233 DefaultLogger::get()->warn(string("Not enough UVs in Vertex: ")+Reader->getNodeName());
234 }
235 aiVector3D NewUv;
236 NewUv.x=GetAttribute<float>(Reader, "u");
237 NewUv.y=GetAttribute<float>(Reader, "v")*(-1)+1;//flip the uv vertikal, blender exports them so!
238 theSubMesh.Uvs[i].push_back(NewUv);
239 XmlRead(Reader);
240 }
241 continue;//because we already read the next node...
242 }
244 //Color:
245 //TODO: actually save this data!
246 else if(Reader->getNodeName()==string("colour_diffuse"))
247 {
248 //do nothing, because we not yet support them
249 }
251 //Attribute could not be read
252 else
253 {
254 DefaultLogger::get()->warn(string("Attribute was not read: ")+Reader->getNodeName());
255 }
257 XmlRead(Reader);//Read the Vertex tag
258 }
259 }
262 void OgreImporter::ReadBoneWeights(SubMesh &theSubMesh, XmlReader *Reader)
263 {
264 theSubMesh.Weights.resize(theSubMesh.Positions.size());
265 while(XmlRead(Reader) && Reader->getNodeName()==string("vertexboneassignment"))
266 {
267 Weight NewWeight;
268 unsigned int VertexId=GetAttribute<int>(Reader, "vertexindex");
269 NewWeight.BoneId=GetAttribute<int>(Reader, "boneindex");
270 NewWeight.Value=GetAttribute<float>(Reader, "weight");
271 //calculate the number of bones used (this is the highest id +1 becuase bone ids start at 0)
272 theSubMesh.BonesUsed=max(theSubMesh.BonesUsed, NewWeight.BoneId+1);
274 theSubMesh.Weights[VertexId].push_back(NewWeight);
275 }
276 }
280 void OgreImporter::ProcessSubMesh(SubMesh &theSubMesh, SubMesh &theSharedGeometry)
281 {
282 //---------------Make all Vertexes unique: (this is required by assimp)-----------------------
283 vector<Face> UniqueFaceList(theSubMesh.FaceList.size());
284 unsigned int UniqueVertexCount=theSubMesh.FaceList.size()*3;//*3 because each face consists of 3 vertexes, because we only support triangles^^
286 vector<aiVector3D> UniquePositions(UniqueVertexCount);
288 vector<aiVector3D> UniqueNormals(UniqueVertexCount);
290 vector<aiVector3D> UniqueTangents(UniqueVertexCount);
292 vector< vector<Weight> > UniqueWeights(UniqueVertexCount);
294 vector< vector<aiVector3D> > UniqueUvs(theSubMesh.Uvs.size());
295 for(unsigned int i=0; i<UniqueUvs.size(); ++i) UniqueUvs[i].resize(UniqueVertexCount);
299 //Support for shared data:
300 /*We can use this loop to copy vertex informations from the shared data pool. In order to do so
301 we just use a reference to a submodel instead of our submodel itself*/
303 SubMesh& VertexSource= theSubMesh.SharedData ? theSharedGeometry : theSubMesh;
304 if(theSubMesh.SharedData)//copy vertexinformations to our mesh:
305 {
306 theSubMesh.HasPositions=theSharedGeometry.HasPositions;
307 theSubMesh.HasNormals=theSharedGeometry.HasNormals;
308 theSubMesh.HasTangents=theSharedGeometry.HasTangents;
310 theSubMesh.BonesUsed=theSharedGeometry.BonesUsed;
312 UniqueUvs.resize(theSharedGeometry.Uvs.size());
313 for(unsigned int i=0; i<UniqueUvs.size(); ++i) UniqueUvs[i].resize(UniqueVertexCount);
314 }
316 for(unsigned int i=0; i<theSubMesh.FaceList.size(); ++i)
317 {
318 //We precalculate the index vlaues her, because we need them in all vertex attributes
319 unsigned int Vertex1=theSubMesh.FaceList[i].VertexIndices[0];
320 unsigned int Vertex2=theSubMesh.FaceList[i].VertexIndices[1];
321 unsigned int Vertex3=theSubMesh.FaceList[i].VertexIndices[2];
323 UniquePositions[3*i+0]=VertexSource.Positions[Vertex1];
324 UniquePositions[3*i+1]=VertexSource.Positions[Vertex2];
325 UniquePositions[3*i+2]=VertexSource.Positions[Vertex3];
327 if(VertexSource.HasNormals)
328 {
329 UniqueNormals[3*i+0]=VertexSource.Normals[Vertex1];
330 UniqueNormals[3*i+1]=VertexSource.Normals[Vertex2];
331 UniqueNormals[3*i+2]=VertexSource.Normals[Vertex3];
332 }
334 if(VertexSource.HasTangents)
335 {
336 UniqueTangents[3*i+0]=VertexSource.Tangents[Vertex1];
337 UniqueTangents[3*i+1]=VertexSource.Tangents[Vertex2];
338 UniqueTangents[3*i+2]=VertexSource.Tangents[Vertex3];
339 }
341 if(UniqueUvs.size()>0)
342 {
343 for(unsigned int j=0; j<UniqueUvs.size(); ++j)
344 {
345 UniqueUvs[j][3*i+0]=VertexSource.Uvs[j][Vertex1];
346 UniqueUvs[j][3*i+1]=VertexSource.Uvs[j][Vertex2];
347 UniqueUvs[j][3*i+2]=VertexSource.Uvs[j][Vertex3];
348 }
349 }
351 if(VertexSource.Weights.size() > 0)
352 {
353 UniqueWeights[3*i+0]=VertexSource.Weights[Vertex1];
354 UniqueWeights[3*i+1]=VertexSource.Weights[Vertex2];
355 UniqueWeights[3*i+2]=VertexSource.Weights[Vertex3];
356 }
358 //The indexvalues a just continuous numbers (0, 1, 2, 3, 4, 5, 6...)
359 UniqueFaceList[i].VertexIndices[0]=3*i+0;
360 UniqueFaceList[i].VertexIndices[1]=3*i+1;
361 UniqueFaceList[i].VertexIndices[2]=3*i+2;
362 }
363 //_________________________________________________________________________________________
365 //now we have the unique datas, but want them in the SubMesh, so we swap all the containers:
366 //if we don't have one of them, we just swap empty containers, so everything is ok
367 theSubMesh.FaceList.swap(UniqueFaceList);
368 theSubMesh.Positions.swap(UniquePositions);
369 theSubMesh.Normals.swap(UniqueNormals);
370 theSubMesh.Tangents.swap(UniqueTangents);
371 theSubMesh.Uvs.swap(UniqueUvs);
372 theSubMesh.Weights.swap(UniqueWeights);
376 //------------- normalize weights -----------------------------
377 //The Blender exporter doesn't care about whether the sum of all boneweights for a single vertex equals 1 or not,
378 //so we have to make this sure:
379 for(unsigned int VertexId=0; VertexId<theSubMesh.Weights.size(); ++VertexId)//iterate over all vertices
380 {
381 float WeightSum=0.0f;
382 for(unsigned int BoneId=0; BoneId<theSubMesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
383 {
384 WeightSum+=theSubMesh.Weights[VertexId][BoneId].Value;
385 }
387 //check if the sum is too far away from 1
388 if(WeightSum<1.0f-0.05f || WeightSum>1.0f+0.05f)
389 {
390 //normalize all weights:
391 for(unsigned int BoneId=0; BoneId<theSubMesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
392 {
393 theSubMesh.Weights[VertexId][BoneId].Value/=WeightSum;
394 }
395 }
396 }
397 //_________________________________________________________
398 }
403 aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& theSubMesh, const vector<Bone>& Bones) const
404 {
405 const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene
406 (void)m_CurrentScene;
408 aiMesh* NewAiMesh=new aiMesh();
410 //Positions
411 NewAiMesh->mVertices=new aiVector3D[theSubMesh.Positions.size()];
412 memcpy(NewAiMesh->mVertices, &theSubMesh.Positions[0], theSubMesh.Positions.size()*sizeof(aiVector3D));
413 NewAiMesh->mNumVertices=theSubMesh.Positions.size();
415 //Normals
416 if(theSubMesh.HasNormals)
417 {
418 NewAiMesh->mNormals=new aiVector3D[theSubMesh.Normals.size()];
419 memcpy(NewAiMesh->mNormals, &theSubMesh.Normals[0], theSubMesh.Normals.size()*sizeof(aiVector3D));
420 }
423 //until we have support for bitangents, no tangents will be written
424 /*
425 //Tangents
426 if(theSubMesh.HasTangents)
427 {
428 NewAiMesh->mTangents=new aiVector3D[theSubMesh.Tangents.size()];
429 memcpy(NewAiMesh->mTangents, &theSubMesh.Tangents[0], theSubMesh.Tangents.size()*sizeof(aiVector3D));
430 }
431 */
433 //Uvs
434 if(theSubMesh.Uvs.size()>0)
435 {
436 for(unsigned int i=0; i<theSubMesh.Uvs.size(); ++i)
437 {
438 NewAiMesh->mNumUVComponents[i]=2;
439 NewAiMesh->mTextureCoords[i]=new aiVector3D[theSubMesh.Uvs[i].size()];
440 memcpy(NewAiMesh->mTextureCoords[i], &(theSubMesh.Uvs[i][0]), theSubMesh.Uvs[i].size()*sizeof(aiVector3D));
441 }
442 }
445 //---------------------------------------- Bones --------------------------------------------
447 //Copy the weights in in Bone-Vertices Struktur
448 //(we have them in a Vertex-Bones Structur, this is much easier for making them unique, which is required by assimp
449 vector< vector<aiVertexWeight> > aiWeights(theSubMesh.BonesUsed);//now the outer list are the bones, and the inner vector the vertices
450 for(unsigned int VertexId=0; VertexId<theSubMesh.Weights.size(); ++VertexId)//iterate over all vertices
451 {
452 for(unsigned int BoneId=0; BoneId<theSubMesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
453 {
454 aiVertexWeight NewWeight;
455 NewWeight.mVertexId=VertexId;//the current Vertex, we can't use the Id form the submehs weights, because they are bone id's
456 NewWeight.mWeight=theSubMesh.Weights[VertexId][BoneId].Value;
457 aiWeights[theSubMesh.Weights[VertexId][BoneId].BoneId].push_back(NewWeight);
458 }
459 }
463 vector<aiBone*> aiBones;
464 aiBones.reserve(theSubMesh.BonesUsed);//the vector might be smaller, because there might be empty bones (bones that are not attached to any vertex)
466 //create all the bones and fill them with informations
467 for(unsigned int i=0; i<theSubMesh.BonesUsed; ++i)
468 {
469 if(aiWeights[i].size()>0)
470 {
471 aiBone* NewBone=new aiBone();
472 NewBone->mNumWeights=aiWeights[i].size();
473 NewBone->mWeights=new aiVertexWeight[aiWeights[i].size()];
474 memcpy(NewBone->mWeights, &(aiWeights[i][0]), sizeof(aiVertexWeight)*aiWeights[i].size());
475 NewBone->mName=Bones[i].Name;//The bone list should be sorted after its id's, this was done in LoadSkeleton
476 NewBone->mOffsetMatrix=Bones[i].BoneToWorldSpace;
478 aiBones.push_back(NewBone);
479 }
480 }
481 NewAiMesh->mNumBones=aiBones.size();
483 // mBones must be NULL if mNumBones is non 0 or the validation fails.
484 if (aiBones.size()) {
485 NewAiMesh->mBones=new aiBone* [aiBones.size()];
486 memcpy(NewAiMesh->mBones, &(aiBones[0]), aiBones.size()*sizeof(aiBone*));
487 }
489 //______________________________________________________________________________________________________
493 //Faces
494 NewAiMesh->mFaces=new aiFace[theSubMesh.FaceList.size()];
495 for(unsigned int i=0; i<theSubMesh.FaceList.size(); ++i)
496 {
497 NewAiMesh->mFaces[i].mNumIndices=3;
498 NewAiMesh->mFaces[i].mIndices=new unsigned int[3];
500 NewAiMesh->mFaces[i].mIndices[0]=theSubMesh.FaceList[i].VertexIndices[0];
501 NewAiMesh->mFaces[i].mIndices[1]=theSubMesh.FaceList[i].VertexIndices[1];
502 NewAiMesh->mFaces[i].mIndices[2]=theSubMesh.FaceList[i].VertexIndices[2];
503 }
504 NewAiMesh->mNumFaces=theSubMesh.FaceList.size();
506 //Link the material:
507 NewAiMesh->mMaterialIndex=theSubMesh.MaterialIndex;//the index is set by the function who called ReadSubMesh
509 return NewAiMesh;
510 }
513 }//namespace Ogre
514 }//namespace Assimp
516 #endif // !! ASSIMP_BUILD_NO_OGRE_IMPORTER