nuclear@0: /* nuclear@0: Open Asset Import Library (assimp) nuclear@0: ---------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the nuclear@0: following conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: nuclear@0: ---------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: nuclear@0: #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER nuclear@0: nuclear@0: #include "OgreImporter.hpp" nuclear@0: #include "TinyFormatter.h" nuclear@0: nuclear@0: using namespace std; nuclear@0: nuclear@0: namespace Assimp nuclear@0: { nuclear@0: namespace Ogre nuclear@0: { nuclear@0: nuclear@0: nuclear@0: void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader) nuclear@0: { nuclear@0: if(Reader->getAttributeValue("usesharedvertices")) nuclear@0: theSubMesh.SharedData=GetAttribute(Reader, "usesharedvertices"); nuclear@0: nuclear@0: XmlRead(Reader); nuclear@0: //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 nuclear@0: //of faces and geometry changed, and not if we have more than one of one nuclear@0: while( Reader->getNodeName()==string("faces") nuclear@0: || Reader->getNodeName()==string("geometry") nuclear@0: || Reader->getNodeName()==string("boneassignments")) nuclear@0: { nuclear@0: if(string(Reader->getNodeName())=="faces")//Read the face list nuclear@0: { nuclear@0: //some info logging: nuclear@0: unsigned int NumFaces=GetAttribute(Reader, "count"); nuclear@0: ostringstream ss; ss <<"Submesh has " << NumFaces << " Faces."; nuclear@0: DefaultLogger::get()->debug(ss.str()); nuclear@0: nuclear@0: while(XmlRead(Reader) && Reader->getNodeName()==string("face")) nuclear@0: { nuclear@0: Face NewFace; nuclear@0: NewFace.VertexIndices[0]=GetAttribute(Reader, "v1"); nuclear@0: NewFace.VertexIndices[1]=GetAttribute(Reader, "v2"); nuclear@0: NewFace.VertexIndices[2]=GetAttribute(Reader, "v3"); nuclear@0: if(Reader->getAttributeValue("v4"))//this should be supported in the future nuclear@0: { nuclear@0: DefaultLogger::get()->warn("Submesh has quads, only traingles are supported!"); nuclear@0: //throw DeadlyImportError("Submesh has quads, only traingles are supported!"); nuclear@0: } nuclear@0: theSubMesh.FaceList.push_back(NewFace); nuclear@0: } nuclear@0: nuclear@0: }//end of faces nuclear@0: else if(string(Reader->getNodeName())=="geometry")//Read the vertexdata nuclear@0: { nuclear@0: //some info logging: nuclear@0: unsigned int NumVertices=GetAttribute(Reader, "vertexcount"); nuclear@0: ostringstream ss; ss<<"VertexCount: " << NumVertices; nuclear@0: DefaultLogger::get()->debug(ss.str()); nuclear@0: nuclear@0: //General Informations about vertices nuclear@0: XmlRead(Reader); nuclear@0: while(Reader->getNodeName()==string("vertexbuffer")) nuclear@0: { nuclear@0: ReadVertexBuffer(theSubMesh, Reader, NumVertices); nuclear@0: } nuclear@0: nuclear@0: //some error checking on the loaded data nuclear@0: if(!theSubMesh.HasPositions) nuclear@0: throw DeadlyImportError("No positions could be loaded!"); nuclear@0: nuclear@0: if(theSubMesh.HasNormals && theSubMesh.Normals.size() != NumVertices) nuclear@0: throw DeadlyImportError("Wrong Number of Normals loaded!"); nuclear@0: nuclear@0: if(theSubMesh.HasTangents && theSubMesh.Tangents.size() != NumVertices) nuclear@0: throw DeadlyImportError("Wrong Number of Tangents loaded!"); nuclear@0: nuclear@0: for(unsigned int i=0; igetNodeName()==string("boneassignments")) nuclear@0: { nuclear@0: ReadBoneWeights(theSubMesh, Reader); nuclear@0: } nuclear@0: } nuclear@0: DefaultLogger::get()->debug((Formatter::format(), nuclear@0: "Positionen: ",theSubMesh.Positions.size(), nuclear@0: " Normale: ",theSubMesh.Normals.size(), nuclear@0: " TexCoords: ",theSubMesh.Uvs.size(), nuclear@0: " Tantents: ",theSubMesh.Tangents.size() nuclear@0: )); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void OgreImporter::ReadVertexBuffer(SubMesh &theSubMesh, XmlReader *Reader, unsigned int NumVertices) nuclear@0: { nuclear@0: DefaultLogger::get()->debug("new Vertex Buffer"); nuclear@0: nuclear@0: bool ReadPositions=false; nuclear@0: bool ReadNormals=false; nuclear@0: bool ReadTangents=false; nuclear@0: unsigned int NumUvs=0; nuclear@0: nuclear@0: //-------------------- check, what we need to read: -------------------------------- nuclear@0: if(Reader->getAttributeValue("positions") && GetAttribute(Reader, "positions")) nuclear@0: { nuclear@0: ReadPositions=theSubMesh.HasPositions=true; nuclear@0: theSubMesh.Positions.reserve(NumVertices); nuclear@0: DefaultLogger::get()->debug("reading positions"); nuclear@0: } nuclear@0: if(Reader->getAttributeValue("normals") && GetAttribute(Reader, "normals")) nuclear@0: { nuclear@0: ReadNormals=theSubMesh.HasNormals=true; nuclear@0: theSubMesh.Normals.reserve(NumVertices); nuclear@0: DefaultLogger::get()->debug("reading normals"); nuclear@0: } nuclear@0: if(Reader->getAttributeValue("tangents") && GetAttribute(Reader, "tangents")) nuclear@0: { nuclear@0: ReadTangents=theSubMesh.HasTangents=true; nuclear@0: theSubMesh.Tangents.reserve(NumVertices); nuclear@0: DefaultLogger::get()->debug("reading tangents"); nuclear@0: } nuclear@0: nuclear@0: if(Reader->getAttributeValue("texture_coords")) nuclear@0: { nuclear@0: NumUvs=GetAttribute(Reader, "texture_coords"); nuclear@0: theSubMesh.Uvs.resize(NumUvs); nuclear@0: for(unsigned int i=0; idebug("reading texture coords"); nuclear@0: } nuclear@0: //___________________________________________________________________ nuclear@0: nuclear@0: nuclear@0: //check if we will load anything nuclear@0: if(!( ReadPositions || ReadNormals || ReadTangents || (NumUvs>0) )) nuclear@0: DefaultLogger::get()->warn("vertexbuffer seams to be empty!"); nuclear@0: nuclear@0: nuclear@0: //read all the vertices: nuclear@0: XmlRead(Reader); nuclear@0: nuclear@0: /*it might happen, that we have more than one attribute per vertex (they are not splitted to different buffers) nuclear@0: so the break condition is a bit tricky */ nuclear@0: while(Reader->getNodeName()==string("vertex") nuclear@0: ||Reader->getNodeName()==string("position") nuclear@0: ||Reader->getNodeName()==string("normal") nuclear@0: ||Reader->getNodeName()==string("tangent") nuclear@0: ||Reader->getNodeName()==string("texcoord") nuclear@0: ||Reader->getNodeName()==string("colour_diffuse")) nuclear@0: { nuclear@0: if(Reader->getNodeName()==string("vertex")) nuclear@0: XmlRead(Reader);//Read an attribute tag nuclear@0: nuclear@0: //Position nuclear@0: if(ReadPositions && Reader->getNodeName()==string("position")) nuclear@0: { nuclear@0: aiVector3D NewPos; nuclear@0: NewPos.x=GetAttribute(Reader, "x"); nuclear@0: NewPos.y=GetAttribute(Reader, "y"); nuclear@0: NewPos.z=GetAttribute(Reader, "z"); nuclear@0: theSubMesh.Positions.push_back(NewPos); nuclear@0: } nuclear@0: nuclear@0: //Normal nuclear@0: else if(ReadNormals && Reader->getNodeName()==string("normal")) nuclear@0: { nuclear@0: aiVector3D NewNormal; nuclear@0: NewNormal.x=GetAttribute(Reader, "x"); nuclear@0: NewNormal.y=GetAttribute(Reader, "y"); nuclear@0: NewNormal.z=GetAttribute(Reader, "z"); nuclear@0: theSubMesh.Normals.push_back(NewNormal); nuclear@0: } nuclear@0: nuclear@0: //Tangent nuclear@0: else if(ReadTangents && Reader->getNodeName()==string("tangent")) nuclear@0: { nuclear@0: aiVector3D NewTangent; nuclear@0: NewTangent.x=GetAttribute(Reader, "x"); nuclear@0: NewTangent.y=GetAttribute(Reader, "y"); nuclear@0: NewTangent.z=GetAttribute(Reader, "z"); nuclear@0: theSubMesh.Tangents.push_back(NewTangent); nuclear@0: } nuclear@0: nuclear@0: //Uv: nuclear@0: else if(NumUvs>0 && Reader->getNodeName()==string("texcoord")) nuclear@0: { nuclear@0: for(unsigned int i=0; igetNodeName()!=string("texcoord")) nuclear@0: { nuclear@0: DefaultLogger::get()->warn(string("Not enough UVs in Vertex: ")+Reader->getNodeName()); nuclear@0: } nuclear@0: aiVector3D NewUv; nuclear@0: NewUv.x=GetAttribute(Reader, "u"); nuclear@0: NewUv.y=GetAttribute(Reader, "v")*(-1)+1;//flip the uv vertikal, blender exports them so! nuclear@0: theSubMesh.Uvs[i].push_back(NewUv); nuclear@0: XmlRead(Reader); nuclear@0: } nuclear@0: continue;//because we already read the next node... nuclear@0: } nuclear@0: nuclear@0: //Color: nuclear@0: //TODO: actually save this data! nuclear@0: else if(Reader->getNodeName()==string("colour_diffuse")) nuclear@0: { nuclear@0: //do nothing, because we not yet support them nuclear@0: } nuclear@0: nuclear@0: //Attribute could not be read nuclear@0: else nuclear@0: { nuclear@0: DefaultLogger::get()->warn(string("Attribute was not read: ")+Reader->getNodeName()); nuclear@0: } nuclear@0: nuclear@0: XmlRead(Reader);//Read the Vertex tag nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void OgreImporter::ReadBoneWeights(SubMesh &theSubMesh, XmlReader *Reader) nuclear@0: { nuclear@0: theSubMesh.Weights.resize(theSubMesh.Positions.size()); nuclear@0: while(XmlRead(Reader) && Reader->getNodeName()==string("vertexboneassignment")) nuclear@0: { nuclear@0: Weight NewWeight; nuclear@0: unsigned int VertexId=GetAttribute(Reader, "vertexindex"); nuclear@0: NewWeight.BoneId=GetAttribute(Reader, "boneindex"); nuclear@0: NewWeight.Value=GetAttribute(Reader, "weight"); nuclear@0: //calculate the number of bones used (this is the highest id +1 becuase bone ids start at 0) nuclear@0: theSubMesh.BonesUsed=max(theSubMesh.BonesUsed, NewWeight.BoneId+1); nuclear@0: nuclear@0: theSubMesh.Weights[VertexId].push_back(NewWeight); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: void OgreImporter::ProcessSubMesh(SubMesh &theSubMesh, SubMesh &theSharedGeometry) nuclear@0: { nuclear@0: //---------------Make all Vertexes unique: (this is required by assimp)----------------------- nuclear@0: vector UniqueFaceList(theSubMesh.FaceList.size()); nuclear@0: unsigned int UniqueVertexCount=theSubMesh.FaceList.size()*3;//*3 because each face consists of 3 vertexes, because we only support triangles^^ nuclear@0: nuclear@0: vector UniquePositions(UniqueVertexCount); nuclear@0: nuclear@0: vector UniqueNormals(UniqueVertexCount); nuclear@0: nuclear@0: vector UniqueTangents(UniqueVertexCount); nuclear@0: nuclear@0: vector< vector > UniqueWeights(UniqueVertexCount); nuclear@0: nuclear@0: vector< vector > UniqueUvs(theSubMesh.Uvs.size()); nuclear@0: for(unsigned int i=0; i0) nuclear@0: { nuclear@0: for(unsigned int j=0; j 0) nuclear@0: { nuclear@0: UniqueWeights[3*i+0]=VertexSource.Weights[Vertex1]; nuclear@0: UniqueWeights[3*i+1]=VertexSource.Weights[Vertex2]; nuclear@0: UniqueWeights[3*i+2]=VertexSource.Weights[Vertex3]; nuclear@0: } nuclear@0: nuclear@0: //The indexvalues a just continuous numbers (0, 1, 2, 3, 4, 5, 6...) nuclear@0: UniqueFaceList[i].VertexIndices[0]=3*i+0; nuclear@0: UniqueFaceList[i].VertexIndices[1]=3*i+1; nuclear@0: UniqueFaceList[i].VertexIndices[2]=3*i+2; nuclear@0: } nuclear@0: //_________________________________________________________________________________________ nuclear@0: nuclear@0: //now we have the unique datas, but want them in the SubMesh, so we swap all the containers: nuclear@0: //if we don't have one of them, we just swap empty containers, so everything is ok nuclear@0: theSubMesh.FaceList.swap(UniqueFaceList); nuclear@0: theSubMesh.Positions.swap(UniquePositions); nuclear@0: theSubMesh.Normals.swap(UniqueNormals); nuclear@0: theSubMesh.Tangents.swap(UniqueTangents); nuclear@0: theSubMesh.Uvs.swap(UniqueUvs); nuclear@0: theSubMesh.Weights.swap(UniqueWeights); nuclear@0: nuclear@0: nuclear@0: nuclear@0: //------------- normalize weights ----------------------------- nuclear@0: //The Blender exporter doesn't care about whether the sum of all boneweights for a single vertex equals 1 or not, nuclear@0: //so we have to make this sure: nuclear@0: for(unsigned int VertexId=0; VertexId1.0f+0.05f) nuclear@0: { nuclear@0: //normalize all weights: nuclear@0: for(unsigned int BoneId=0; BoneId& Bones) const nuclear@0: { nuclear@0: const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene nuclear@0: (void)m_CurrentScene; nuclear@0: nuclear@0: aiMesh* NewAiMesh=new aiMesh(); nuclear@0: nuclear@0: //Positions nuclear@0: NewAiMesh->mVertices=new aiVector3D[theSubMesh.Positions.size()]; nuclear@0: memcpy(NewAiMesh->mVertices, &theSubMesh.Positions[0], theSubMesh.Positions.size()*sizeof(aiVector3D)); nuclear@0: NewAiMesh->mNumVertices=theSubMesh.Positions.size(); nuclear@0: nuclear@0: //Normals nuclear@0: if(theSubMesh.HasNormals) nuclear@0: { nuclear@0: NewAiMesh->mNormals=new aiVector3D[theSubMesh.Normals.size()]; nuclear@0: memcpy(NewAiMesh->mNormals, &theSubMesh.Normals[0], theSubMesh.Normals.size()*sizeof(aiVector3D)); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: //until we have support for bitangents, no tangents will be written nuclear@0: /* nuclear@0: //Tangents nuclear@0: if(theSubMesh.HasTangents) nuclear@0: { nuclear@0: NewAiMesh->mTangents=new aiVector3D[theSubMesh.Tangents.size()]; nuclear@0: memcpy(NewAiMesh->mTangents, &theSubMesh.Tangents[0], theSubMesh.Tangents.size()*sizeof(aiVector3D)); nuclear@0: } nuclear@0: */ nuclear@0: nuclear@0: //Uvs nuclear@0: if(theSubMesh.Uvs.size()>0) nuclear@0: { nuclear@0: for(unsigned int i=0; imNumUVComponents[i]=2; nuclear@0: NewAiMesh->mTextureCoords[i]=new aiVector3D[theSubMesh.Uvs[i].size()]; nuclear@0: memcpy(NewAiMesh->mTextureCoords[i], &(theSubMesh.Uvs[i][0]), theSubMesh.Uvs[i].size()*sizeof(aiVector3D)); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: //---------------------------------------- Bones -------------------------------------------- nuclear@0: nuclear@0: //Copy the weights in in Bone-Vertices Struktur nuclear@0: //(we have them in a Vertex-Bones Structur, this is much easier for making them unique, which is required by assimp nuclear@0: vector< vector > aiWeights(theSubMesh.BonesUsed);//now the outer list are the bones, and the inner vector the vertices nuclear@0: for(unsigned int VertexId=0; VertexId aiBones; nuclear@0: aiBones.reserve(theSubMesh.BonesUsed);//the vector might be smaller, because there might be empty bones (bones that are not attached to any vertex) nuclear@0: nuclear@0: //create all the bones and fill them with informations nuclear@0: for(unsigned int i=0; i0) nuclear@0: { nuclear@0: aiBone* NewBone=new aiBone(); nuclear@0: NewBone->mNumWeights=aiWeights[i].size(); nuclear@0: NewBone->mWeights=new aiVertexWeight[aiWeights[i].size()]; nuclear@0: memcpy(NewBone->mWeights, &(aiWeights[i][0]), sizeof(aiVertexWeight)*aiWeights[i].size()); nuclear@0: NewBone->mName=Bones[i].Name;//The bone list should be sorted after its id's, this was done in LoadSkeleton nuclear@0: NewBone->mOffsetMatrix=Bones[i].BoneToWorldSpace; nuclear@0: nuclear@0: aiBones.push_back(NewBone); nuclear@0: } nuclear@0: } nuclear@0: NewAiMesh->mNumBones=aiBones.size(); nuclear@0: nuclear@0: // mBones must be NULL if mNumBones is non 0 or the validation fails. nuclear@0: if (aiBones.size()) { nuclear@0: NewAiMesh->mBones=new aiBone* [aiBones.size()]; nuclear@0: memcpy(NewAiMesh->mBones, &(aiBones[0]), aiBones.size()*sizeof(aiBone*)); nuclear@0: } nuclear@0: nuclear@0: //______________________________________________________________________________________________________ nuclear@0: nuclear@0: nuclear@0: nuclear@0: //Faces nuclear@0: NewAiMesh->mFaces=new aiFace[theSubMesh.FaceList.size()]; nuclear@0: for(unsigned int i=0; imFaces[i].mNumIndices=3; nuclear@0: NewAiMesh->mFaces[i].mIndices=new unsigned int[3]; nuclear@0: nuclear@0: NewAiMesh->mFaces[i].mIndices[0]=theSubMesh.FaceList[i].VertexIndices[0]; nuclear@0: NewAiMesh->mFaces[i].mIndices[1]=theSubMesh.FaceList[i].VertexIndices[1]; nuclear@0: NewAiMesh->mFaces[i].mIndices[2]=theSubMesh.FaceList[i].VertexIndices[2]; nuclear@0: } nuclear@0: NewAiMesh->mNumFaces=theSubMesh.FaceList.size(); nuclear@0: nuclear@0: //Link the material: nuclear@0: NewAiMesh->mMaterialIndex=theSubMesh.MaterialIndex;//the index is set by the function who called ReadSubMesh nuclear@0: nuclear@0: return NewAiMesh; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: }//namespace Ogre nuclear@0: }//namespace Assimp nuclear@0: nuclear@0: #endif // !! ASSIMP_BUILD_NO_OGRE_IMPORTER