From 959831f4ef5a3e797f576c3de08cd65032c997ad Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Sun, 13 Jan 2013 18:54:10 +1000 Subject: Remove damned ancient DOS line endings from Irrlicht. Hopefully I did not go overboard. --- .../source/Irrlicht/CXMeshFileLoader.cpp | 4838 ++++++++++---------- 1 file changed, 2419 insertions(+), 2419 deletions(-) (limited to 'libraries/irrlicht-1.8/source/Irrlicht/CXMeshFileLoader.cpp') diff --git a/libraries/irrlicht-1.8/source/Irrlicht/CXMeshFileLoader.cpp b/libraries/irrlicht-1.8/source/Irrlicht/CXMeshFileLoader.cpp index 595e911..1c67434 100644 --- a/libraries/irrlicht-1.8/source/Irrlicht/CXMeshFileLoader.cpp +++ b/libraries/irrlicht-1.8/source/Irrlicht/CXMeshFileLoader.cpp @@ -1,2419 +1,2419 @@ -// Copyright (C) 2002-2012 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h - -#include "IrrCompileConfig.h" - -#ifdef _IRR_COMPILE_WITH_X_LOADER_ - -#include "CXMeshFileLoader.h" -#include "os.h" - -#include "fast_atof.h" -#include "coreutil.h" -#include "ISceneManager.h" -#include "IVideoDriver.h" -#include "IFileSystem.h" -#include "IReadFile.h" - -#ifdef _DEBUG -#define _XREADER_DEBUG -#endif -//#define BETTER_MESHBUFFER_SPLITTING_FOR_X - -namespace irr -{ -namespace scene -{ - -//! Constructor -CXMeshFileLoader::CXMeshFileLoader(scene::ISceneManager* smgr, io::IFileSystem* fs) -: SceneManager(smgr), FileSystem(fs), AllJoints(0), AnimatedMesh(0), - Buffer(0), P(0), End(0), BinaryNumCount(0), Line(0), - CurFrame(0), MajorVersion(0), MinorVersion(0), BinaryFormat(false), FloatSize(0) -{ - #ifdef _DEBUG - setDebugName("CXMeshFileLoader"); - #endif -} - - -//! returns true if the file maybe is able to be loaded by this class -//! based on the file extension (e.g. ".bsp") -bool CXMeshFileLoader::isALoadableFileExtension(const io::path& filename) const -{ - return core::hasFileExtension ( filename, "x" ); -} - - -//! creates/loads an animated mesh from the file. -//! \return Pointer to the created mesh. Returns 0 if loading failed. -//! If you no longer need the mesh, you should call IAnimatedMesh::drop(). -//! See IReferenceCounted::drop() for more information. -IAnimatedMesh* CXMeshFileLoader::createMesh(io::IReadFile* f) -{ - if (!f) - return 0; - -#ifdef _XREADER_DEBUG - u32 time = os::Timer::getRealTime(); -#endif - - AnimatedMesh = new CSkinnedMesh(); - - if (load(f)) - { - AnimatedMesh->finalize(); - } - else - { - AnimatedMesh->drop(); - AnimatedMesh = 0; - } -#ifdef _XREADER_DEBUG - time = os::Timer::getRealTime() - time; - core::stringc tmpString = "Time to load "; - tmpString += BinaryFormat ? "binary" : "ascii"; - tmpString += " X file: "; - tmpString += time; - tmpString += "ms"; - os::Printer::log(tmpString.c_str()); -#endif - //Clear up - - MajorVersion=0; - MinorVersion=0; - BinaryFormat=0; - BinaryNumCount=0; - FloatSize=0; - P=0; - End=0; - CurFrame=0; - TemplateMaterials.clear(); - - delete [] Buffer; - Buffer = 0; - - for (u32 i=0; iMaterials.size()) - { - mesh->Materials.push_back(video::SMaterial()); - mesh->Materials[0].DiffuseColor.set(0xff777777); - mesh->Materials[0].Shininess=0.f; - mesh->Materials[0].SpecularColor.set(0xff777777); - mesh->Materials[0].EmissiveColor.set(0xff000000); - } - - u32 i; - - mesh->Buffers.reallocate(mesh->Materials.size()); -#ifndef BETTER_MESHBUFFER_SPLITTING_FOR_X - const u32 bufferOffset = AnimatedMesh->getMeshBufferCount(); -#endif - for (i=0; iMaterials.size(); ++i) - { - mesh->Buffers.push_back( AnimatedMesh->addMeshBuffer() ); - mesh->Buffers.getLast()->Material = mesh->Materials[i]; - - if (!mesh->HasSkinning) - { - //Set up rigid animation - if (mesh->AttachedJointID!=-1) - { - AnimatedMesh->getAllJoints()[mesh->AttachedJointID]->AttachedMeshes.push_back( AnimatedMesh->getMeshBuffers().size()-1 ); - } - } - } - - if (!mesh->FaceMaterialIndices.size()) - { - mesh->FaceMaterialIndices.set_used(mesh->Indices.size() / 3); - for (i=0; iFaceMaterialIndices.size(); ++i) - mesh->FaceMaterialIndices[i]=0; - } - - if (!mesh->HasVertexColors) - { - for (u32 j=0;jFaceMaterialIndices.size();++j) - { - for (u32 id=j*3+0;id<=j*3+2;++id) - { - mesh->Vertices[ mesh->Indices[id] ].Color = mesh->Buffers[mesh->FaceMaterialIndices[j]]->Material.DiffuseColor; - } - } - } - - #ifdef BETTER_MESHBUFFER_SPLITTING_FOR_X - { - //the same vertex can be used in many different meshbuffers, but it's slow to work out - - core::array< core::array< u32 > > verticesLinkIndex; - verticesLinkIndex.reallocate(mesh->Vertices.size()); - core::array< core::array< u16 > > verticesLinkBuffer; - verticesLinkBuffer.reallocate(mesh->Vertices.size()); - - for (i=0;iVertices.size();++i) - { - verticesLinkIndex.push_back( core::array< u32 >() ); - verticesLinkBuffer.push_back( core::array< u16 >() ); - } - - for (i=0;iFaceMaterialIndices.size();++i) - { - for (u32 id=i*3+0;id<=i*3+2;++id) - { - core::array< u16 > &Array=verticesLinkBuffer[ mesh->Indices[id] ]; - bool found=false; - - for (u32 j=0; j < Array.size(); ++j) - { - if (Array[j]==mesh->FaceMaterialIndices[i]) - { - found=true; - break; - } - } - - if (!found) - Array.push_back( mesh->FaceMaterialIndices[i] ); - } - } - - for (i=0;iVertices.size();++i) - { - core::array< u16 > &Array = verticesLinkBuffer[i]; - verticesLinkIndex[i].reallocate(Array.size()); - for (u32 j=0; j < Array.size(); ++j) - { - scene::SSkinMeshBuffer *buffer = mesh->Buffers[ Array[j] ]; - verticesLinkIndex[i].push_back( buffer->Vertices_Standard.size() ); - buffer->Vertices_Standard.push_back( mesh->Vertices[i] ); - } - } - - for (i=0;iFaceMaterialIndices.size();++i) - { - scene::SSkinMeshBuffer *buffer=mesh->Buffers[ mesh->FaceMaterialIndices[i] ]; - - for (u32 id=i*3+0;id<=i*3+2;++id) - { - core::array< u16 > &Array=verticesLinkBuffer[ mesh->Indices[id] ]; - - for (u32 j=0;j< Array.size() ;++j) - { - if ( Array[j]== mesh->FaceMaterialIndices[i] ) - buffer->Indices.push_back( verticesLinkIndex[ mesh->Indices[id] ][j] ); - } - } - } - - for (u32 j=0;jWeightJoint.size();++j) - { - ISkinnedMesh::SJoint* joint = AnimatedMesh->getAllJoints()[mesh->WeightJoint[j]]; - ISkinnedMesh::SWeight& weight = joint->Weights[mesh->WeightNum[j]]; - - u32 id = weight.vertex_id; - - if (id>=verticesLinkIndex.size()) - { - os::Printer::log("X loader: Weight id out of range", ELL_WARNING); - id=0; - weight.strength=0.f; - } - - if (verticesLinkBuffer[id].size()==1) - { - weight.vertex_id=verticesLinkIndex[id][0]; - weight.buffer_id=verticesLinkBuffer[id][0]; - } - else if (verticesLinkBuffer[id].size() != 0) - { - for (u32 k=1; k < verticesLinkBuffer[id].size(); ++k) - { - ISkinnedMesh::SWeight* WeightClone = AnimatedMesh->addWeight(joint); - WeightClone->strength = weight.strength; - WeightClone->vertex_id = verticesLinkIndex[id][k]; - WeightClone->buffer_id = verticesLinkBuffer[id][k]; - } - } - } - } - #else - { - core::array< u32 > verticesLinkIndex; - core::array< s16 > verticesLinkBuffer; - verticesLinkBuffer.set_used(mesh->Vertices.size()); - - // init with 0 - for (i=0;iVertices.size();++i) - { - // watch out for vertices which are not part of the mesh - // they will keep the -1 and can lead to out-of-bounds access - verticesLinkBuffer[i]=-1; - } - - bool warned = false; - // store meshbuffer number per vertex - for (i=0;iFaceMaterialIndices.size();++i) - { - for (u32 id=i*3+0;id<=i*3+2;++id) - { - if ((verticesLinkBuffer[mesh->Indices[id]] != -1) && (verticesLinkBuffer[mesh->Indices[id]] != (s16)mesh->FaceMaterialIndices[i])) - { - if (!warned) - { - os::Printer::log("X loader", "Duplicated vertex, animation might be corrupted.", ELL_WARNING); - warned=true; - } - const u32 tmp = mesh->Vertices.size(); - mesh->Vertices.push_back(mesh->Vertices[ mesh->Indices[id] ]); - mesh->Indices[id] = tmp; - verticesLinkBuffer.set_used(mesh->Vertices.size()); - } - verticesLinkBuffer[ mesh->Indices[id] ] = mesh->FaceMaterialIndices[i]; - } - } - - if (mesh->FaceMaterialIndices.size() != 0) - { - // store vertices in buffers and remember relation in verticesLinkIndex - u32* vCountArray = new u32[mesh->Buffers.size()]; - memset(vCountArray, 0, mesh->Buffers.size()*sizeof(u32)); - // count vertices in each buffer and reallocate - for (i=0; iVertices.size(); ++i) - { - if (verticesLinkBuffer[i] != -1) - ++vCountArray[verticesLinkBuffer[i]]; - } - if (mesh->TCoords2.size()) - { - for (i=0; i!=mesh->Buffers.size(); ++i) - { - mesh->Buffers[i]->Vertices_2TCoords.reallocate(vCountArray[i]); - mesh->Buffers[i]->VertexType=video::EVT_2TCOORDS; - } - } - else - { - for (i=0; i!=mesh->Buffers.size(); ++i) - mesh->Buffers[i]->Vertices_Standard.reallocate(vCountArray[i]); - } - - verticesLinkIndex.set_used(mesh->Vertices.size()); - // actually store vertices - for (i=0; iVertices.size(); ++i) - { - // if a vertex is missing for some reason, just skip it - if (verticesLinkBuffer[i]==-1) - continue; - scene::SSkinMeshBuffer *buffer = mesh->Buffers[ verticesLinkBuffer[i] ]; - - if (mesh->TCoords2.size()) - { - verticesLinkIndex[i] = buffer->Vertices_2TCoords.size(); - buffer->Vertices_2TCoords.push_back( mesh->Vertices[i] ); - // We have a problem with correct tcoord2 handling here - // crash fixed for now by checking the values - buffer->Vertices_2TCoords.getLast().TCoords2=(iTCoords2.size())?mesh->TCoords2[i]:mesh->Vertices[i].TCoords; - } - else - { - verticesLinkIndex[i] = buffer->Vertices_Standard.size(); - buffer->Vertices_Standard.push_back( mesh->Vertices[i] ); - } - } - - // count indices per buffer and reallocate - memset(vCountArray, 0, mesh->Buffers.size()*sizeof(u32)); - for (i=0; iFaceMaterialIndices.size(); ++i) - ++vCountArray[ mesh->FaceMaterialIndices[i] ]; - for (i=0; i!=mesh->Buffers.size(); ++i) - mesh->Buffers[i]->Indices.reallocate(vCountArray[i]); - delete [] vCountArray; - // create indices per buffer - for (i=0; iFaceMaterialIndices.size(); ++i) - { - scene::SSkinMeshBuffer *buffer = mesh->Buffers[ mesh->FaceMaterialIndices[i] ]; - for (u32 id=i*3+0; id!=i*3+3; ++id) - { - buffer->Indices.push_back( verticesLinkIndex[ mesh->Indices[id] ] ); - } - } - } - - for (u32 j=0; jWeightJoint.size(); ++j) - { - ISkinnedMesh::SWeight& weight = (AnimatedMesh->getAllJoints()[mesh->WeightJoint[j]]->Weights[mesh->WeightNum[j]]); - - u32 id = weight.vertex_id; - - if (id>=verticesLinkIndex.size()) - { - os::Printer::log("X loader: Weight id out of range", ELL_WARNING); - id=0; - weight.strength=0.f; - } - - weight.vertex_id=verticesLinkIndex[id]; - weight.buffer_id=verticesLinkBuffer[id] + bufferOffset; - } - } - #endif - - } - - return true; -} - - -//! Reads file into memory -bool CXMeshFileLoader::readFileIntoMemory(io::IReadFile* file) -{ - const long size = file->getSize(); - if (size < 12) - { - os::Printer::log("X File is too small.", ELL_WARNING); - return false; - } - - Buffer = new c8[size]; - - //! read all into memory - if (file->read(Buffer, size) != size) - { - os::Printer::log("Could not read from x file.", ELL_WARNING); - return false; - } - - Line = 1; - End = Buffer + size; - - //! check header "xof " - if (strncmp(Buffer, "xof ", 4)!=0) - { - os::Printer::log("Not an x file, wrong header.", ELL_WARNING); - return false; - } - - //! read minor and major version, e.g. 0302 or 0303 - c8 tmp[3]; - tmp[0] = Buffer[4]; - tmp[1] = Buffer[5]; - tmp[2] = 0x0; - MajorVersion = core::strtoul10(tmp); - - tmp[0] = Buffer[6]; - tmp[1] = Buffer[7]; - MinorVersion = core::strtoul10(tmp); - - //! read format - if (strncmp(&Buffer[8], "txt ", 4) ==0) - BinaryFormat = false; - else if (strncmp(&Buffer[8], "bin ", 4) ==0) - BinaryFormat = true; - else - { - os::Printer::log("Only uncompressed x files currently supported.", ELL_WARNING); - return false; - } - BinaryNumCount=0; - - //! read float size - if (strncmp(&Buffer[12], "0032", 4) ==0) - FloatSize = 4; - else if (strncmp(&Buffer[12], "0064", 4) ==0) - FloatSize = 8; - else - { - os::Printer::log("Float size not supported.", ELL_WARNING); - return false; - } - - P = &Buffer[16]; - - readUntilEndOfLine(); - FilePath = FileSystem->getFileDir(file->getFileName()) + "/"; - - return true; -} - - -//! Parses the file -bool CXMeshFileLoader::parseFile() -{ - while(parseDataObject()) - { - // loop - } - - return true; -} - - -//! Parses the next Data object in the file -bool CXMeshFileLoader::parseDataObject() -{ - core::stringc objectName = getNextToken(); - - if (objectName.size() == 0) - return false; - - // parse specific object -#ifdef _XREADER_DEBUG - os::Printer::log("debug DataObject:", objectName.c_str(), ELL_DEBUG); -#endif - - if (objectName == "template") - return parseDataObjectTemplate(); - else - if (objectName == "Frame") - { - return parseDataObjectFrame( 0 ); - } - else - if (objectName == "Mesh") - { - // some meshes have no frames at all - //CurFrame = AnimatedMesh->addJoint(0); - - SXMesh *mesh=new SXMesh; - - //mesh->Buffer=AnimatedMesh->addMeshBuffer(); - Meshes.push_back(mesh); - - return parseDataObjectMesh(*mesh); - } - else - if (objectName == "AnimationSet") - { - return parseDataObjectAnimationSet(); - } - else - if (objectName == "Material") - { - // template materials now available thanks to joeWright - TemplateMaterials.push_back(SXTemplateMaterial()); - TemplateMaterials.getLast().Name = getNextToken(); - return parseDataObjectMaterial(TemplateMaterials.getLast().Material); - } - else - if (objectName == "}") - { - os::Printer::log("} found in dataObject", ELL_WARNING); - return true; - } - - os::Printer::log("Unknown data object in animation of .x file", objectName.c_str(), ELL_WARNING); - - return parseUnknownDataObject(); -} - - -bool CXMeshFileLoader::parseDataObjectTemplate() -{ -#ifdef _XREADER_DEBUG - os::Printer::log("CXFileReader: Reading template", ELL_DEBUG); -#endif - - // parse a template data object. Currently not stored. - core::stringc name; - - if (!readHeadOfDataObject(&name)) - { - os::Printer::log("Left delimiter in template data object missing.", - name.c_str(), ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - - // read GUID - getNextToken(); - - // read and ignore data members - while(true) - { - core::stringc s = getNextToken(); - - if (s == "}") - break; - - if (s.size() == 0) - return false; - } - - return true; -} - - -bool CXMeshFileLoader::parseDataObjectFrame(CSkinnedMesh::SJoint *Parent) -{ -#ifdef _XREADER_DEBUG - os::Printer::log("CXFileReader: Reading frame", ELL_DEBUG); -#endif - - // A coordinate frame, or "frame of reference." The Frame template - // is open and can contain any object. The Direct3D extensions (D3DX) - // mesh-loading functions recognize Mesh, FrameTransformMatrix, and - // Frame template instances as child objects when loading a Frame - // instance. - - u32 JointID=0; - - core::stringc name; - - if (!readHeadOfDataObject(&name)) - { - os::Printer::log("No opening brace in Frame found in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - - CSkinnedMesh::SJoint *joint=0; - - if (name.size()) - { - for (u32 n=0; n < AnimatedMesh->getAllJoints().size(); ++n) - { - if (AnimatedMesh->getAllJoints()[n]->Name==name) - { - joint=AnimatedMesh->getAllJoints()[n]; - JointID=n; - break; - } - } - } - - if (!joint) - { -#ifdef _XREADER_DEBUG - os::Printer::log("creating joint ", name.c_str(), ELL_DEBUG); -#endif - joint=AnimatedMesh->addJoint(Parent); - joint->Name=name; - JointID=AnimatedMesh->getAllJoints().size()-1; - } - else - { -#ifdef _XREADER_DEBUG - os::Printer::log("using joint ", name.c_str(), ELL_DEBUG); -#endif - if (Parent) - Parent->Children.push_back(joint); - } - - // Now inside a frame. - // read tokens until closing brace is reached. - - while(true) - { - core::stringc objectName = getNextToken(); - -#ifdef _XREADER_DEBUG - os::Printer::log("debug DataObject in frame:", objectName.c_str(), ELL_DEBUG); -#endif - - if (objectName.size() == 0) - { - os::Printer::log("Unexpected ending found in Frame in x file.", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - else - if (objectName == "}") - { - break; // frame finished - } - else - if (objectName == "Frame") - { - - if (!parseDataObjectFrame(joint)) - return false; - } - else - if (objectName == "FrameTransformMatrix") - { - if (!parseDataObjectTransformationMatrix(joint->LocalMatrix)) - return false; - - //joint->LocalAnimatedMatrix - //joint->LocalAnimatedMatrix.makeInverse(); - //joint->LocalMatrix=tmp*joint->LocalAnimatedMatrix; - } - else - if (objectName == "Mesh") - { - /* - frame.Meshes.push_back(SXMesh()); - if (!parseDataObjectMesh(frame.Meshes.getLast())) - return false; - */ - SXMesh *mesh=new SXMesh; - - mesh->AttachedJointID=JointID; - - Meshes.push_back(mesh); - - if (!parseDataObjectMesh(*mesh)) - return false; - } - else - { - os::Printer::log("Unknown data object in frame in x file", objectName.c_str(), ELL_WARNING); - if (!parseUnknownDataObject()) - return false; - } - } - - return true; -} - - -bool CXMeshFileLoader::parseDataObjectTransformationMatrix(core::matrix4 &mat) -{ -#ifdef _XREADER_DEBUG - os::Printer::log("CXFileReader: Reading Transformation Matrix", ELL_DEBUG); -#endif - - if (!readHeadOfDataObject()) - { - os::Printer::log("No opening brace in Transformation Matrix found in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - - readMatrix(mat); - - if (!checkForOneFollowingSemicolons()) - { - os::Printer::log("No finishing semicolon in Transformation Matrix found in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - } - - if (!checkForClosingBrace()) - { - os::Printer::log("No closing brace in Transformation Matrix found in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - - return true; -} - - -bool CXMeshFileLoader::parseDataObjectMesh(SXMesh &mesh) -{ - core::stringc name; - - if (!readHeadOfDataObject(&name)) - { -#ifdef _XREADER_DEBUG - os::Printer::log("CXFileReader: Reading mesh", ELL_DEBUG); -#endif - os::Printer::log("No opening brace in Mesh found in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - -#ifdef _XREADER_DEBUG - os::Printer::log("CXFileReader: Reading mesh", name.c_str(), ELL_DEBUG); -#endif - - // read vertex count - const u32 nVertices = readInt(); - - // read vertices - mesh.Vertices.set_used(nVertices); - for (u32 n=0; n polygonfaces; - u32 currentIndex = 0; - - for (u32 k=0; k>8)&0xf)*sizeof(core::vector2df); - for (u32 j=0; jgetAllJoints().size(); ++n) - { - if (AnimatedMesh->getAllJoints()[n]->Name==TransformNodeName) - { - joint=AnimatedMesh->getAllJoints()[n]; - break; - } - } - - if (!joint) - { -#ifdef _XREADER_DEBUG - os::Printer::log("creating joint for skinning ", TransformNodeName.c_str(), ELL_DEBUG); -#endif - n = AnimatedMesh->getAllJoints().size(); - joint=AnimatedMesh->addJoint(0); - joint->Name=TransformNodeName; - } - - // read vertex weights - const u32 nWeights = readInt(); - - // read vertex indices - u32 i; - - const u32 jointStart = joint->Weights.size(); - joint->Weights.reallocate(jointStart+nWeights); - - mesh.WeightJoint.reallocate( mesh.WeightJoint.size() + nWeights ); - mesh.WeightNum.reallocate( mesh.WeightNum.size() + nWeights ); - - for (i=0; iWeights.size()); - - CSkinnedMesh::SWeight *weight=AnimatedMesh->addWeight(joint); - - weight->buffer_id=0; - weight->vertex_id=readInt(); - } - - // read vertex weights - - for (i=jointStart; iWeights[i].strength = readFloat(); - - // read matrix offset - - // transforms the mesh vertices to the space of the bone - // When concatenated to the bone's transform, this provides the - // world space coordinates of the mesh as affected by the bone - core::matrix4& MatrixOffset = joint->GlobalInversedMatrix; - - readMatrix(MatrixOffset); - - if (!checkForOneFollowingSemicolons()) - { - os::Printer::log("No finishing semicolon in Skin Weights found in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - } - - if (!checkForClosingBrace()) - { - os::Printer::log("No closing brace in Skin Weights found in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - - return true; -} - - -bool CXMeshFileLoader::parseDataObjectSkinMeshHeader(SXMesh& mesh) -{ -#ifdef _XREADER_DEBUG - os::Printer::log("CXFileReader: Reading skin mesh header", ELL_DEBUG); -#endif - - if (!readHeadOfDataObject()) - { - os::Printer::log("No opening brace in Skin Mesh header found in .x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - - mesh.MaxSkinWeightsPerVertex = readInt(); - mesh.MaxSkinWeightsPerFace = readInt(); - mesh.BoneCount = readInt(); - - if (!BinaryFormat) - getNextToken(); // skip semicolon - - if (!checkForClosingBrace()) - { - os::Printer::log("No closing brace in skin mesh header in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - - return true; -} - - -bool CXMeshFileLoader::parseDataObjectMeshNormals(SXMesh &mesh) -{ -#ifdef _XREADER_DEBUG - os::Printer::log("CXFileReader: reading mesh normals", ELL_DEBUG); -#endif - - if (!readHeadOfDataObject()) - { - os::Printer::log("No opening brace in Mesh Normals found in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - - // read count - const u32 nNormals = readInt(); - core::array normals; - normals.set_used(nNormals); - - // read normals - for (u32 i=0; i normalIndices; - normalIndices.set_used(mesh.Indices.size()); - - // read face normal indices - const u32 nFNormals = readInt(); - - u32 normalidx = 0; - core::array polygonfaces; - for (u32 k=0; k=mesh.Vertices.size()) - { - os::Printer::log("index value in parseDataObjectMeshVertexColors out of bounds", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - readRGBA(mesh.Vertices[Index].Color); - checkForOneFollowingSemicolons(); - } - - if (!checkForOneFollowingSemicolons()) - { - os::Printer::log("No finishing semicolon in Mesh Vertex Colors Array found in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - } - - if (!checkForClosingBrace()) - { - os::Printer::log("No closing brace in Mesh Texture Coordinates Array found in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - - return true; -} - - -bool CXMeshFileLoader::parseDataObjectMeshMaterialList(SXMesh &mesh) -{ -#ifdef _XREADER_DEBUG - os::Printer::log("CXFileReader: Reading mesh material list", ELL_DEBUG); -#endif - - if (!readHeadOfDataObject()) - { - os::Printer::log("No opening brace in Mesh Material List found in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - - // read material count - mesh.Materials.reallocate(readInt()); - - // read non triangulated face material index count - const u32 nFaceIndices = readInt(); - - // There seems to be a compact representation of "all faces the same material" - // being represented as 1;1;0;; which means 1 material, 1 face with first material - // all the other faces have to obey then, so check is disabled - //if (nFaceIndices != mesh.IndexCountPerFace.size()) - // os::Printer::log("Index count per face not equal to face material index count in x file.", ELL_WARNING); - - // read non triangulated face indices and create triangulated ones - mesh.FaceMaterialIndices.set_used( mesh.Indices.size() / 3); - u32 triangulatedindex = 0; - u32 ind = 0; - for (u32 tfi=0; tfiexistFile(TextureFileName)) - material.setTexture(textureLayer, SceneManager->getVideoDriver()->getTexture(TextureFileName)); - // mesh path - else - { - TextureFileName=FilePath + FileSystem->getFileBasename(TextureFileName); - if (FileSystem->existFile(TextureFileName)) - material.setTexture(textureLayer, SceneManager->getVideoDriver()->getTexture(TextureFileName)); - // working directory - else - material.setTexture(textureLayer, SceneManager->getVideoDriver()->getTexture(FileSystem->getFileBasename(TextureFileName))); - } - ++textureLayer; - if (textureLayer==2) - material.MaterialType=video::EMT_LIGHTMAP; - } - else - if (objectName.equals_ignore_case("NormalmapFilename")) - { - // some exporters write "NormalmapFileName" instead. - core::stringc TextureFileName; - if (!parseDataObjectTextureFilename(TextureFileName)) - return false; - - // original name - if (FileSystem->existFile(TextureFileName)) - material.setTexture(1, SceneManager->getVideoDriver()->getTexture(TextureFileName)); - // mesh path - else - { - TextureFileName=FilePath + FileSystem->getFileBasename(TextureFileName); - if (FileSystem->existFile(TextureFileName)) - material.setTexture(1, SceneManager->getVideoDriver()->getTexture(TextureFileName)); - // working directory - else - material.setTexture(1, SceneManager->getVideoDriver()->getTexture(FileSystem->getFileBasename(TextureFileName))); - } - if (textureLayer==1) - ++textureLayer; - } - else - { - os::Printer::log("Unknown data object in material in .x file", objectName.c_str(), ELL_WARNING); - if (!parseUnknownDataObject()) - return false; - } - } - - return true; -} - - -bool CXMeshFileLoader::parseDataObjectAnimationSet() -{ -#ifdef _XREADER_DEBUG - os::Printer::log("CXFileReader: Reading animation set", ELL_DEBUG); -#endif - - core::stringc AnimationName; - - if (!readHeadOfDataObject(&AnimationName)) - { - os::Printer::log("No opening brace in Animation Set found in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - os::Printer::log("Reading animationset ", AnimationName, ELL_DEBUG); - - while(true) - { - core::stringc objectName = getNextToken(); - - if (objectName.size() == 0) - { - os::Printer::log("Unexpected ending found in Animation set in x file.", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - else - if (objectName == "}") - { - break; // animation set finished - } - else - if (objectName == "Animation") - { - if (!parseDataObjectAnimation()) - return false; - } - else - { - os::Printer::log("Unknown data object in animation set in x file", objectName.c_str(), ELL_WARNING); - if (!parseUnknownDataObject()) - return false; - } - } - return true; -} - - -bool CXMeshFileLoader::parseDataObjectAnimation() -{ -#ifdef _XREADER_DEBUG - os::Printer::log("CXFileReader: reading animation", ELL_DEBUG); -#endif - - if (!readHeadOfDataObject()) - { - os::Printer::log("No opening brace in Animation found in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - - //anim.closed = true; - //anim.linearPositionQuality = true; - CSkinnedMesh::SJoint animationDump; - - core::stringc FrameName; - - while(true) - { - core::stringc objectName = getNextToken(); - - if (objectName.size() == 0) - { - os::Printer::log("Unexpected ending found in Animation in x file.", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - else - if (objectName == "}") - { - break; // animation finished - } - else - if (objectName == "AnimationKey") - { - if (!parseDataObjectAnimationKey(&animationDump)) - return false; - } - else - if (objectName == "AnimationOptions") - { - //TODO: parse options. - if (!parseUnknownDataObject()) - return false; - } - else - if (objectName == "{") - { - // read frame name - FrameName = getNextToken(); - - if (!checkForClosingBrace()) - { - os::Printer::log("Unexpected ending found in Animation in x file.", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - } - else - { - os::Printer::log("Unknown data object in animation in x file", objectName.c_str(), ELL_WARNING); - if (!parseUnknownDataObject()) - return false; - } - } - - if (FrameName.size() != 0) - { -#ifdef _XREADER_DEBUG - os::Printer::log("frame name", FrameName.c_str(), ELL_DEBUG); -#endif - CSkinnedMesh::SJoint *joint=0; - - u32 n; - for (n=0; n < AnimatedMesh->getAllJoints().size(); ++n) - { - if (AnimatedMesh->getAllJoints()[n]->Name==FrameName) - { - joint=AnimatedMesh->getAllJoints()[n]; - break; - } - } - - if (!joint) - { -#ifdef _XREADER_DEBUG - os::Printer::log("creating joint for animation ", FrameName.c_str(), ELL_DEBUG); -#endif - joint=AnimatedMesh->addJoint(0); - joint->Name=FrameName; - } - - joint->PositionKeys.reallocate(joint->PositionKeys.size()+animationDump.PositionKeys.size()); - for (n=0; nPositionKeys.push_back(animationDump.PositionKeys[n]); - } - - joint->ScaleKeys.reallocate(joint->ScaleKeys.size()+animationDump.ScaleKeys.size()); - for (n=0; nScaleKeys.push_back(animationDump.ScaleKeys[n]); - } - - joint->RotationKeys.reallocate(joint->RotationKeys.size()+animationDump.RotationKeys.size()); - for (n=0; nRotationKeys.push_back(animationDump.RotationKeys[n]); - } - } - else - os::Printer::log("joint name was never given", ELL_WARNING); - - return true; -} - - -bool CXMeshFileLoader::parseDataObjectAnimationKey(ISkinnedMesh::SJoint *joint) -{ -#ifdef _XREADER_DEBUG - os::Printer::log("CXFileReader: reading animation key", ELL_DEBUG); -#endif - - if (!readHeadOfDataObject()) - { - os::Printer::log("No opening brace in Animation Key found in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - - // read key type - - const u32 keyType = readInt(); - - if (keyType > 4) - { - os::Printer::log("Unknown key type found in Animation Key in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - - // read number of keys - const u32 numberOfKeys = readInt(); - - // eat the semicolon after the "0". if there are keys present, readInt() - // does this for us. If there aren't, we need to do it explicitly - if (numberOfKeys == 0) - checkForOneFollowingSemicolons(); - - for (u32 i=0; iaddRotationKey(joint); - key->frame=time; - key->rotation.set(X,Y,Z,W); - } - break; - case 1: //scale - case 2: //position - { - // read vectors - - // read count - if (readInt() != 3) - { - os::Printer::log("Expected 3 numbers in animation key in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - - core::vector3df vector; - readVector3(vector); - - if (!checkForTwoFollowingSemicolons()) - { - os::Printer::log("No finishing semicolon after vector animation key in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - } - - if (keyType==2) - { - ISkinnedMesh::SPositionKey *key=AnimatedMesh->addPositionKey(joint); - key->frame=time; - key->position=vector; - } - else - { - ISkinnedMesh::SScaleKey *key=AnimatedMesh->addScaleKey(joint); - key->frame=time; - key->scale=vector; - } - } - break; - case 3: - case 4: - { - // read matrix - - // read count - if (readInt() != 16) - { - os::Printer::log("Expected 16 numbers in animation key in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - - // read matrix - core::matrix4 mat(core::matrix4::EM4CONST_NOTHING); - readMatrix(mat); - - //mat=joint->LocalMatrix*mat; - - if (!checkForOneFollowingSemicolons()) - { - os::Printer::log("No finishing semicolon after matrix animation key in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - } - - //core::vector3df rotation = mat.getRotationDegrees(); - - ISkinnedMesh::SRotationKey *keyR=AnimatedMesh->addRotationKey(joint); - keyR->frame=time; - - // IRR_TEST_BROKEN_QUATERNION_USE: TODO - switched from mat to mat.getTransposed() for downward compatibility. - // Not tested so far if this was correct or wrong before quaternion fix! - keyR->rotation= core::quaternion(mat.getTransposed()); - - ISkinnedMesh::SPositionKey *keyP=AnimatedMesh->addPositionKey(joint); - keyP->frame=time; - keyP->position=mat.getTranslation(); - -/* - core::vector3df scale=mat.getScale(); - - if (scale.X==0) - scale.X=1; - if (scale.Y==0) - scale.Y=1; - if (scale.Z==0) - scale.Z=1; - ISkinnedMesh::SScaleKey *keyS=AnimatedMesh->addScaleKey(joint); - keyS->frame=time; - keyS->scale=scale; -*/ - } - break; - } // end switch - } - - if (!checkForOneFollowingSemicolons()) - --P; - - if (!checkForClosingBrace()) - { - os::Printer::log("No closing brace in animation key in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - - return true; -} - - -bool CXMeshFileLoader::parseDataObjectTextureFilename(core::stringc& texturename) -{ -#ifdef _XREADER_DEBUG - os::Printer::log("CXFileReader: reading texture filename", ELL_DEBUG); -#endif - - if (!readHeadOfDataObject()) - { - os::Printer::log("No opening brace in Texture filename found in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - - if (!getNextTokenAsString(texturename)) - { - os::Printer::log("Unknown syntax while reading texture filename string in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - - if (!checkForClosingBrace()) - { - os::Printer::log("No closing brace in Texture filename found in x file", ELL_WARNING); - os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); - return false; - } - - return true; -} - - -bool CXMeshFileLoader::parseUnknownDataObject() -{ - // find opening delimiter - while(true) - { - core::stringc t = getNextToken(); - - if (t.size() == 0) - return false; - - if (t == "{") - break; - } - - u32 counter = 1; - - // parse until closing delimiter - - while(counter) - { - core::stringc t = getNextToken(); - - if (t.size() == 0) - return false; - - if (t == "{") - ++counter; - else - if (t == "}") - --counter; - } - - return true; -} - - -//! checks for closing curly brace, returns false if not there -bool CXMeshFileLoader::checkForClosingBrace() -{ - return (getNextToken() == "}"); -} - - -//! checks for one following semicolon, returns false if not there -bool CXMeshFileLoader::checkForOneFollowingSemicolons() -{ - if (BinaryFormat) - return true; - - if (getNextToken() == ";") - return true; - else - { - --P; - return false; - } -} - - -//! checks for two following semicolons, returns false if they are not there -bool CXMeshFileLoader::checkForTwoFollowingSemicolons() -{ - if (BinaryFormat) - return true; - - for (u32 k=0; k<2; ++k) - { - if (getNextToken() != ";") - { - --P; - return false; - } - } - - return true; -} - - -//! reads header of dataobject including the opening brace. -//! returns false if error happened, and writes name of object -//! if there is one -bool CXMeshFileLoader::readHeadOfDataObject(core::stringc* outname) -{ - core::stringc nameOrBrace = getNextToken(); - if (nameOrBrace != "{") - { - if (outname) - (*outname) = nameOrBrace; - - if (getNextToken() != "{") - return false; - } - - return true; -} - - -//! returns next parseable token. Returns empty string if no token there -core::stringc CXMeshFileLoader::getNextToken() -{ - core::stringc s; - - // process binary-formatted file - if (BinaryFormat) - { - // in binary mode it will only return NAME and STRING token - // and (correctly) skip over other tokens. - - s16 tok = readBinWord(); - u32 len; - - // standalone tokens - switch (tok) { - case 1: - // name token - len = readBinDWord(); - s = core::stringc(P, len); - P += len; - return s; - case 2: - // string token - len = readBinDWord(); - s = core::stringc(P, len); - P += (len + 2); - return s; - case 3: - // integer token - P += 4; - return ""; - case 5: - // GUID token - P += 16; - return ""; - case 6: - len = readBinDWord(); - P += (len * 4); - return ""; - case 7: - len = readBinDWord(); - P += (len * FloatSize); - return ""; - case 0x0a: - return "{"; - case 0x0b: - return "}"; - case 0x0c: - return "("; - case 0x0d: - return ")"; - case 0x0e: - return "["; - case 0x0f: - return "]"; - case 0x10: - return "<"; - case 0x11: - return ">"; - case 0x12: - return "."; - case 0x13: - return ","; - case 0x14: - return ";"; - case 0x1f: - return "template"; - case 0x28: - return "WORD"; - case 0x29: - return "DWORD"; - case 0x2a: - return "FLOAT"; - case 0x2b: - return "DOUBLE"; - case 0x2c: - return "CHAR"; - case 0x2d: - return "UCHAR"; - case 0x2e: - return "SWORD"; - case 0x2f: - return "SDWORD"; - case 0x30: - return "void"; - case 0x31: - return "string"; - case 0x32: - return "unicode"; - case 0x33: - return "cstring"; - case 0x34: - return "array"; - } - } - // process text-formatted file - else - { - findNextNoneWhiteSpace(); - - if (P >= End) - return s; - - while((P < End) && !core::isspace(P[0])) - { - // either keep token delimiters when already holding a token, or return if first valid char - if (P[0]==';' || P[0]=='}' || P[0]=='{' || P[0]==',') - { - if (!s.size()) - { - s.append(P[0]); - ++P; - } - break; // stop for delimiter - } - s.append(P[0]); - ++P; - } - } - return s; -} - - -//! places pointer to next begin of a token, which must be a number, -// and ignores comments -void CXMeshFileLoader::findNextNoneWhiteSpaceNumber() -{ - if (BinaryFormat) - return; - - while((P < End) && (P[0] != '-') && (P[0] != '.') && - !( core::isdigit(P[0]))) - { - // check if this is a comment - if ((P[0] == '/' && P[1] == '/') || P[0] == '#') - readUntilEndOfLine(); - else - ++P; - } -} - - -// places pointer to next begin of a token, and ignores comments -void CXMeshFileLoader::findNextNoneWhiteSpace() -{ - if (BinaryFormat) - return; - - while(true) - { - while((P < End) && core::isspace(P[0])) - { - if (*P=='\n') - ++Line; - ++P; - } - - if (P >= End) - return; - - // check if this is a comment - if ((P[0] == '/' && P[1] == '/') || - P[0] == '#') - readUntilEndOfLine(); - else - break; - } -} - - -//! reads a x file style string -bool CXMeshFileLoader::getNextTokenAsString(core::stringc& out) -{ - if (BinaryFormat) - { - out=getNextToken(); - return true; - } - findNextNoneWhiteSpace(); - - if (P >= End) - return false; - - if (P[0] != '"') - return false; - ++P; - - while(P < End && P[0]!='"') - { - out.append(P[0]); - ++P; - } - - if ( P[1] != ';' || P[0] != '"') - return false; - P+=2; - - return true; -} - - -void CXMeshFileLoader::readUntilEndOfLine() -{ - if (BinaryFormat) - return; - - while(P < End) - { - if (P[0] == '\n' || P[0] == '\r') - { - ++P; - ++Line; - return; - } - - ++P; - } -} - - -u16 CXMeshFileLoader::readBinWord() -{ -#ifdef __BIG_ENDIAN__ - const u16 tmp = os::Byteswap::byteswap(*(u16 *)P); -#else - const u16 tmp = *(u16 *)P; -#endif - P += 2; - return tmp; -} - - -u32 CXMeshFileLoader::readBinDWord() -{ -#ifdef __BIG_ENDIAN__ - const u32 tmp = os::Byteswap::byteswap(*(u32 *)P); -#else - const u32 tmp = *(u32 *)P; -#endif - P += 4; - return tmp; -} - - -u32 CXMeshFileLoader::readInt() -{ - if (BinaryFormat) - { - if (!BinaryNumCount) - { - const u16 tmp = readBinWord(); // 0x06 or 0x03 - if (tmp == 0x06) - BinaryNumCount = readBinDWord(); - else - BinaryNumCount = 1; // single int - } - --BinaryNumCount; - return readBinDWord(); - } - else - { - findNextNoneWhiteSpaceNumber(); - return core::strtoul10(P, &P); - } -} - - -f32 CXMeshFileLoader::readFloat() -{ - if (BinaryFormat) - { - if (!BinaryNumCount) - { - const u16 tmp = readBinWord(); // 0x07 or 0x42 - if (tmp == 0x07) - BinaryNumCount = readBinDWord(); - else - BinaryNumCount = 1; // single int - } - --BinaryNumCount; - if (FloatSize == 8) - { -#ifdef __BIG_ENDIAN__ - //TODO: Check if data is properly converted here - f32 ctmp[2]; - ctmp[1] = os::Byteswap::byteswap(*(f32*)P); - ctmp[0] = os::Byteswap::byteswap(*(f32*)P+4); - const f32 tmp = (f32)(*(f64*)(void*)ctmp); -#else - const f32 tmp = (f32)(*(f64 *)P); -#endif - P += 8; - return tmp; - } - else - { -#ifdef __BIG_ENDIAN__ - const f32 tmp = os::Byteswap::byteswap(*(f32 *)P); -#else - const f32 tmp = *(f32 *)P; -#endif - P += 4; - return tmp; - } - } - findNextNoneWhiteSpaceNumber(); - f32 ftmp; - P = core::fast_atof_move(P, ftmp); - return ftmp; -} - - -// read 2-dimensional vector. Stops at semicolon after second value for text file format -bool CXMeshFileLoader::readVector2(core::vector2df& vec) -{ - vec.X = readFloat(); - vec.Y = readFloat(); - return true; -} - - -// read 3-dimensional vector. Stops at semicolon after third value for text file format -bool CXMeshFileLoader::readVector3(core::vector3df& vec) -{ - vec.X = readFloat(); - vec.Y = readFloat(); - vec.Z = readFloat(); - return true; -} - - -// read color without alpha value. Stops after second semicolon after blue value -bool CXMeshFileLoader::readRGB(video::SColor& color) -{ - video::SColorf tmpColor; - tmpColor.r = readFloat(); - tmpColor.g = readFloat(); - tmpColor.b = readFloat(); - color = tmpColor.toSColor(); - return checkForOneFollowingSemicolons(); -} - - -// read color with alpha value. Stops after second semicolon after blue value -bool CXMeshFileLoader::readRGBA(video::SColor& color) -{ - video::SColorf tmpColor; - tmpColor.r = readFloat(); - tmpColor.g = readFloat(); - tmpColor.b = readFloat(); - tmpColor.a = readFloat(); - color = tmpColor.toSColor(); - return checkForOneFollowingSemicolons(); -} - - -// read matrix from list of floats -bool CXMeshFileLoader::readMatrix(core::matrix4& mat) -{ - for (u32 i=0; i<16; ++i) - mat[i] = readFloat(); - return checkForOneFollowingSemicolons(); -} - - -} // end namespace scene -} // end namespace irr - -#endif // _IRR_COMPILE_WITH_X_LOADER_ - +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#include "IrrCompileConfig.h" + +#ifdef _IRR_COMPILE_WITH_X_LOADER_ + +#include "CXMeshFileLoader.h" +#include "os.h" + +#include "fast_atof.h" +#include "coreutil.h" +#include "ISceneManager.h" +#include "IVideoDriver.h" +#include "IFileSystem.h" +#include "IReadFile.h" + +#ifdef _DEBUG +#define _XREADER_DEBUG +#endif +//#define BETTER_MESHBUFFER_SPLITTING_FOR_X + +namespace irr +{ +namespace scene +{ + +//! Constructor +CXMeshFileLoader::CXMeshFileLoader(scene::ISceneManager* smgr, io::IFileSystem* fs) +: SceneManager(smgr), FileSystem(fs), AllJoints(0), AnimatedMesh(0), + Buffer(0), P(0), End(0), BinaryNumCount(0), Line(0), + CurFrame(0), MajorVersion(0), MinorVersion(0), BinaryFormat(false), FloatSize(0) +{ + #ifdef _DEBUG + setDebugName("CXMeshFileLoader"); + #endif +} + + +//! returns true if the file maybe is able to be loaded by this class +//! based on the file extension (e.g. ".bsp") +bool CXMeshFileLoader::isALoadableFileExtension(const io::path& filename) const +{ + return core::hasFileExtension ( filename, "x" ); +} + + +//! creates/loads an animated mesh from the file. +//! \return Pointer to the created mesh. Returns 0 if loading failed. +//! If you no longer need the mesh, you should call IAnimatedMesh::drop(). +//! See IReferenceCounted::drop() for more information. +IAnimatedMesh* CXMeshFileLoader::createMesh(io::IReadFile* f) +{ + if (!f) + return 0; + +#ifdef _XREADER_DEBUG + u32 time = os::Timer::getRealTime(); +#endif + + AnimatedMesh = new CSkinnedMesh(); + + if (load(f)) + { + AnimatedMesh->finalize(); + } + else + { + AnimatedMesh->drop(); + AnimatedMesh = 0; + } +#ifdef _XREADER_DEBUG + time = os::Timer::getRealTime() - time; + core::stringc tmpString = "Time to load "; + tmpString += BinaryFormat ? "binary" : "ascii"; + tmpString += " X file: "; + tmpString += time; + tmpString += "ms"; + os::Printer::log(tmpString.c_str()); +#endif + //Clear up + + MajorVersion=0; + MinorVersion=0; + BinaryFormat=0; + BinaryNumCount=0; + FloatSize=0; + P=0; + End=0; + CurFrame=0; + TemplateMaterials.clear(); + + delete [] Buffer; + Buffer = 0; + + for (u32 i=0; iMaterials.size()) + { + mesh->Materials.push_back(video::SMaterial()); + mesh->Materials[0].DiffuseColor.set(0xff777777); + mesh->Materials[0].Shininess=0.f; + mesh->Materials[0].SpecularColor.set(0xff777777); + mesh->Materials[0].EmissiveColor.set(0xff000000); + } + + u32 i; + + mesh->Buffers.reallocate(mesh->Materials.size()); +#ifndef BETTER_MESHBUFFER_SPLITTING_FOR_X + const u32 bufferOffset = AnimatedMesh->getMeshBufferCount(); +#endif + for (i=0; iMaterials.size(); ++i) + { + mesh->Buffers.push_back( AnimatedMesh->addMeshBuffer() ); + mesh->Buffers.getLast()->Material = mesh->Materials[i]; + + if (!mesh->HasSkinning) + { + //Set up rigid animation + if (mesh->AttachedJointID!=-1) + { + AnimatedMesh->getAllJoints()[mesh->AttachedJointID]->AttachedMeshes.push_back( AnimatedMesh->getMeshBuffers().size()-1 ); + } + } + } + + if (!mesh->FaceMaterialIndices.size()) + { + mesh->FaceMaterialIndices.set_used(mesh->Indices.size() / 3); + for (i=0; iFaceMaterialIndices.size(); ++i) + mesh->FaceMaterialIndices[i]=0; + } + + if (!mesh->HasVertexColors) + { + for (u32 j=0;jFaceMaterialIndices.size();++j) + { + for (u32 id=j*3+0;id<=j*3+2;++id) + { + mesh->Vertices[ mesh->Indices[id] ].Color = mesh->Buffers[mesh->FaceMaterialIndices[j]]->Material.DiffuseColor; + } + } + } + + #ifdef BETTER_MESHBUFFER_SPLITTING_FOR_X + { + //the same vertex can be used in many different meshbuffers, but it's slow to work out + + core::array< core::array< u32 > > verticesLinkIndex; + verticesLinkIndex.reallocate(mesh->Vertices.size()); + core::array< core::array< u16 > > verticesLinkBuffer; + verticesLinkBuffer.reallocate(mesh->Vertices.size()); + + for (i=0;iVertices.size();++i) + { + verticesLinkIndex.push_back( core::array< u32 >() ); + verticesLinkBuffer.push_back( core::array< u16 >() ); + } + + for (i=0;iFaceMaterialIndices.size();++i) + { + for (u32 id=i*3+0;id<=i*3+2;++id) + { + core::array< u16 > &Array=verticesLinkBuffer[ mesh->Indices[id] ]; + bool found=false; + + for (u32 j=0; j < Array.size(); ++j) + { + if (Array[j]==mesh->FaceMaterialIndices[i]) + { + found=true; + break; + } + } + + if (!found) + Array.push_back( mesh->FaceMaterialIndices[i] ); + } + } + + for (i=0;iVertices.size();++i) + { + core::array< u16 > &Array = verticesLinkBuffer[i]; + verticesLinkIndex[i].reallocate(Array.size()); + for (u32 j=0; j < Array.size(); ++j) + { + scene::SSkinMeshBuffer *buffer = mesh->Buffers[ Array[j] ]; + verticesLinkIndex[i].push_back( buffer->Vertices_Standard.size() ); + buffer->Vertices_Standard.push_back( mesh->Vertices[i] ); + } + } + + for (i=0;iFaceMaterialIndices.size();++i) + { + scene::SSkinMeshBuffer *buffer=mesh->Buffers[ mesh->FaceMaterialIndices[i] ]; + + for (u32 id=i*3+0;id<=i*3+2;++id) + { + core::array< u16 > &Array=verticesLinkBuffer[ mesh->Indices[id] ]; + + for (u32 j=0;j< Array.size() ;++j) + { + if ( Array[j]== mesh->FaceMaterialIndices[i] ) + buffer->Indices.push_back( verticesLinkIndex[ mesh->Indices[id] ][j] ); + } + } + } + + for (u32 j=0;jWeightJoint.size();++j) + { + ISkinnedMesh::SJoint* joint = AnimatedMesh->getAllJoints()[mesh->WeightJoint[j]]; + ISkinnedMesh::SWeight& weight = joint->Weights[mesh->WeightNum[j]]; + + u32 id = weight.vertex_id; + + if (id>=verticesLinkIndex.size()) + { + os::Printer::log("X loader: Weight id out of range", ELL_WARNING); + id=0; + weight.strength=0.f; + } + + if (verticesLinkBuffer[id].size()==1) + { + weight.vertex_id=verticesLinkIndex[id][0]; + weight.buffer_id=verticesLinkBuffer[id][0]; + } + else if (verticesLinkBuffer[id].size() != 0) + { + for (u32 k=1; k < verticesLinkBuffer[id].size(); ++k) + { + ISkinnedMesh::SWeight* WeightClone = AnimatedMesh->addWeight(joint); + WeightClone->strength = weight.strength; + WeightClone->vertex_id = verticesLinkIndex[id][k]; + WeightClone->buffer_id = verticesLinkBuffer[id][k]; + } + } + } + } + #else + { + core::array< u32 > verticesLinkIndex; + core::array< s16 > verticesLinkBuffer; + verticesLinkBuffer.set_used(mesh->Vertices.size()); + + // init with 0 + for (i=0;iVertices.size();++i) + { + // watch out for vertices which are not part of the mesh + // they will keep the -1 and can lead to out-of-bounds access + verticesLinkBuffer[i]=-1; + } + + bool warned = false; + // store meshbuffer number per vertex + for (i=0;iFaceMaterialIndices.size();++i) + { + for (u32 id=i*3+0;id<=i*3+2;++id) + { + if ((verticesLinkBuffer[mesh->Indices[id]] != -1) && (verticesLinkBuffer[mesh->Indices[id]] != (s16)mesh->FaceMaterialIndices[i])) + { + if (!warned) + { + os::Printer::log("X loader", "Duplicated vertex, animation might be corrupted.", ELL_WARNING); + warned=true; + } + const u32 tmp = mesh->Vertices.size(); + mesh->Vertices.push_back(mesh->Vertices[ mesh->Indices[id] ]); + mesh->Indices[id] = tmp; + verticesLinkBuffer.set_used(mesh->Vertices.size()); + } + verticesLinkBuffer[ mesh->Indices[id] ] = mesh->FaceMaterialIndices[i]; + } + } + + if (mesh->FaceMaterialIndices.size() != 0) + { + // store vertices in buffers and remember relation in verticesLinkIndex + u32* vCountArray = new u32[mesh->Buffers.size()]; + memset(vCountArray, 0, mesh->Buffers.size()*sizeof(u32)); + // count vertices in each buffer and reallocate + for (i=0; iVertices.size(); ++i) + { + if (verticesLinkBuffer[i] != -1) + ++vCountArray[verticesLinkBuffer[i]]; + } + if (mesh->TCoords2.size()) + { + for (i=0; i!=mesh->Buffers.size(); ++i) + { + mesh->Buffers[i]->Vertices_2TCoords.reallocate(vCountArray[i]); + mesh->Buffers[i]->VertexType=video::EVT_2TCOORDS; + } + } + else + { + for (i=0; i!=mesh->Buffers.size(); ++i) + mesh->Buffers[i]->Vertices_Standard.reallocate(vCountArray[i]); + } + + verticesLinkIndex.set_used(mesh->Vertices.size()); + // actually store vertices + for (i=0; iVertices.size(); ++i) + { + // if a vertex is missing for some reason, just skip it + if (verticesLinkBuffer[i]==-1) + continue; + scene::SSkinMeshBuffer *buffer = mesh->Buffers[ verticesLinkBuffer[i] ]; + + if (mesh->TCoords2.size()) + { + verticesLinkIndex[i] = buffer->Vertices_2TCoords.size(); + buffer->Vertices_2TCoords.push_back( mesh->Vertices[i] ); + // We have a problem with correct tcoord2 handling here + // crash fixed for now by checking the values + buffer->Vertices_2TCoords.getLast().TCoords2=(iTCoords2.size())?mesh->TCoords2[i]:mesh->Vertices[i].TCoords; + } + else + { + verticesLinkIndex[i] = buffer->Vertices_Standard.size(); + buffer->Vertices_Standard.push_back( mesh->Vertices[i] ); + } + } + + // count indices per buffer and reallocate + memset(vCountArray, 0, mesh->Buffers.size()*sizeof(u32)); + for (i=0; iFaceMaterialIndices.size(); ++i) + ++vCountArray[ mesh->FaceMaterialIndices[i] ]; + for (i=0; i!=mesh->Buffers.size(); ++i) + mesh->Buffers[i]->Indices.reallocate(vCountArray[i]); + delete [] vCountArray; + // create indices per buffer + for (i=0; iFaceMaterialIndices.size(); ++i) + { + scene::SSkinMeshBuffer *buffer = mesh->Buffers[ mesh->FaceMaterialIndices[i] ]; + for (u32 id=i*3+0; id!=i*3+3; ++id) + { + buffer->Indices.push_back( verticesLinkIndex[ mesh->Indices[id] ] ); + } + } + } + + for (u32 j=0; jWeightJoint.size(); ++j) + { + ISkinnedMesh::SWeight& weight = (AnimatedMesh->getAllJoints()[mesh->WeightJoint[j]]->Weights[mesh->WeightNum[j]]); + + u32 id = weight.vertex_id; + + if (id>=verticesLinkIndex.size()) + { + os::Printer::log("X loader: Weight id out of range", ELL_WARNING); + id=0; + weight.strength=0.f; + } + + weight.vertex_id=verticesLinkIndex[id]; + weight.buffer_id=verticesLinkBuffer[id] + bufferOffset; + } + } + #endif + + } + + return true; +} + + +//! Reads file into memory +bool CXMeshFileLoader::readFileIntoMemory(io::IReadFile* file) +{ + const long size = file->getSize(); + if (size < 12) + { + os::Printer::log("X File is too small.", ELL_WARNING); + return false; + } + + Buffer = new c8[size]; + + //! read all into memory + if (file->read(Buffer, size) != size) + { + os::Printer::log("Could not read from x file.", ELL_WARNING); + return false; + } + + Line = 1; + End = Buffer + size; + + //! check header "xof " + if (strncmp(Buffer, "xof ", 4)!=0) + { + os::Printer::log("Not an x file, wrong header.", ELL_WARNING); + return false; + } + + //! read minor and major version, e.g. 0302 or 0303 + c8 tmp[3]; + tmp[0] = Buffer[4]; + tmp[1] = Buffer[5]; + tmp[2] = 0x0; + MajorVersion = core::strtoul10(tmp); + + tmp[0] = Buffer[6]; + tmp[1] = Buffer[7]; + MinorVersion = core::strtoul10(tmp); + + //! read format + if (strncmp(&Buffer[8], "txt ", 4) ==0) + BinaryFormat = false; + else if (strncmp(&Buffer[8], "bin ", 4) ==0) + BinaryFormat = true; + else + { + os::Printer::log("Only uncompressed x files currently supported.", ELL_WARNING); + return false; + } + BinaryNumCount=0; + + //! read float size + if (strncmp(&Buffer[12], "0032", 4) ==0) + FloatSize = 4; + else if (strncmp(&Buffer[12], "0064", 4) ==0) + FloatSize = 8; + else + { + os::Printer::log("Float size not supported.", ELL_WARNING); + return false; + } + + P = &Buffer[16]; + + readUntilEndOfLine(); + FilePath = FileSystem->getFileDir(file->getFileName()) + "/"; + + return true; +} + + +//! Parses the file +bool CXMeshFileLoader::parseFile() +{ + while(parseDataObject()) + { + // loop + } + + return true; +} + + +//! Parses the next Data object in the file +bool CXMeshFileLoader::parseDataObject() +{ + core::stringc objectName = getNextToken(); + + if (objectName.size() == 0) + return false; + + // parse specific object +#ifdef _XREADER_DEBUG + os::Printer::log("debug DataObject:", objectName.c_str(), ELL_DEBUG); +#endif + + if (objectName == "template") + return parseDataObjectTemplate(); + else + if (objectName == "Frame") + { + return parseDataObjectFrame( 0 ); + } + else + if (objectName == "Mesh") + { + // some meshes have no frames at all + //CurFrame = AnimatedMesh->addJoint(0); + + SXMesh *mesh=new SXMesh; + + //mesh->Buffer=AnimatedMesh->addMeshBuffer(); + Meshes.push_back(mesh); + + return parseDataObjectMesh(*mesh); + } + else + if (objectName == "AnimationSet") + { + return parseDataObjectAnimationSet(); + } + else + if (objectName == "Material") + { + // template materials now available thanks to joeWright + TemplateMaterials.push_back(SXTemplateMaterial()); + TemplateMaterials.getLast().Name = getNextToken(); + return parseDataObjectMaterial(TemplateMaterials.getLast().Material); + } + else + if (objectName == "}") + { + os::Printer::log("} found in dataObject", ELL_WARNING); + return true; + } + + os::Printer::log("Unknown data object in animation of .x file", objectName.c_str(), ELL_WARNING); + + return parseUnknownDataObject(); +} + + +bool CXMeshFileLoader::parseDataObjectTemplate() +{ +#ifdef _XREADER_DEBUG + os::Printer::log("CXFileReader: Reading template", ELL_DEBUG); +#endif + + // parse a template data object. Currently not stored. + core::stringc name; + + if (!readHeadOfDataObject(&name)) + { + os::Printer::log("Left delimiter in template data object missing.", + name.c_str(), ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + + // read GUID + getNextToken(); + + // read and ignore data members + while(true) + { + core::stringc s = getNextToken(); + + if (s == "}") + break; + + if (s.size() == 0) + return false; + } + + return true; +} + + +bool CXMeshFileLoader::parseDataObjectFrame(CSkinnedMesh::SJoint *Parent) +{ +#ifdef _XREADER_DEBUG + os::Printer::log("CXFileReader: Reading frame", ELL_DEBUG); +#endif + + // A coordinate frame, or "frame of reference." The Frame template + // is open and can contain any object. The Direct3D extensions (D3DX) + // mesh-loading functions recognize Mesh, FrameTransformMatrix, and + // Frame template instances as child objects when loading a Frame + // instance. + + u32 JointID=0; + + core::stringc name; + + if (!readHeadOfDataObject(&name)) + { + os::Printer::log("No opening brace in Frame found in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + + CSkinnedMesh::SJoint *joint=0; + + if (name.size()) + { + for (u32 n=0; n < AnimatedMesh->getAllJoints().size(); ++n) + { + if (AnimatedMesh->getAllJoints()[n]->Name==name) + { + joint=AnimatedMesh->getAllJoints()[n]; + JointID=n; + break; + } + } + } + + if (!joint) + { +#ifdef _XREADER_DEBUG + os::Printer::log("creating joint ", name.c_str(), ELL_DEBUG); +#endif + joint=AnimatedMesh->addJoint(Parent); + joint->Name=name; + JointID=AnimatedMesh->getAllJoints().size()-1; + } + else + { +#ifdef _XREADER_DEBUG + os::Printer::log("using joint ", name.c_str(), ELL_DEBUG); +#endif + if (Parent) + Parent->Children.push_back(joint); + } + + // Now inside a frame. + // read tokens until closing brace is reached. + + while(true) + { + core::stringc objectName = getNextToken(); + +#ifdef _XREADER_DEBUG + os::Printer::log("debug DataObject in frame:", objectName.c_str(), ELL_DEBUG); +#endif + + if (objectName.size() == 0) + { + os::Printer::log("Unexpected ending found in Frame in x file.", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + else + if (objectName == "}") + { + break; // frame finished + } + else + if (objectName == "Frame") + { + + if (!parseDataObjectFrame(joint)) + return false; + } + else + if (objectName == "FrameTransformMatrix") + { + if (!parseDataObjectTransformationMatrix(joint->LocalMatrix)) + return false; + + //joint->LocalAnimatedMatrix + //joint->LocalAnimatedMatrix.makeInverse(); + //joint->LocalMatrix=tmp*joint->LocalAnimatedMatrix; + } + else + if (objectName == "Mesh") + { + /* + frame.Meshes.push_back(SXMesh()); + if (!parseDataObjectMesh(frame.Meshes.getLast())) + return false; + */ + SXMesh *mesh=new SXMesh; + + mesh->AttachedJointID=JointID; + + Meshes.push_back(mesh); + + if (!parseDataObjectMesh(*mesh)) + return false; + } + else + { + os::Printer::log("Unknown data object in frame in x file", objectName.c_str(), ELL_WARNING); + if (!parseUnknownDataObject()) + return false; + } + } + + return true; +} + + +bool CXMeshFileLoader::parseDataObjectTransformationMatrix(core::matrix4 &mat) +{ +#ifdef _XREADER_DEBUG + os::Printer::log("CXFileReader: Reading Transformation Matrix", ELL_DEBUG); +#endif + + if (!readHeadOfDataObject()) + { + os::Printer::log("No opening brace in Transformation Matrix found in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + + readMatrix(mat); + + if (!checkForOneFollowingSemicolons()) + { + os::Printer::log("No finishing semicolon in Transformation Matrix found in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + } + + if (!checkForClosingBrace()) + { + os::Printer::log("No closing brace in Transformation Matrix found in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + + return true; +} + + +bool CXMeshFileLoader::parseDataObjectMesh(SXMesh &mesh) +{ + core::stringc name; + + if (!readHeadOfDataObject(&name)) + { +#ifdef _XREADER_DEBUG + os::Printer::log("CXFileReader: Reading mesh", ELL_DEBUG); +#endif + os::Printer::log("No opening brace in Mesh found in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + +#ifdef _XREADER_DEBUG + os::Printer::log("CXFileReader: Reading mesh", name.c_str(), ELL_DEBUG); +#endif + + // read vertex count + const u32 nVertices = readInt(); + + // read vertices + mesh.Vertices.set_used(nVertices); + for (u32 n=0; n polygonfaces; + u32 currentIndex = 0; + + for (u32 k=0; k>8)&0xf)*sizeof(core::vector2df); + for (u32 j=0; jgetAllJoints().size(); ++n) + { + if (AnimatedMesh->getAllJoints()[n]->Name==TransformNodeName) + { + joint=AnimatedMesh->getAllJoints()[n]; + break; + } + } + + if (!joint) + { +#ifdef _XREADER_DEBUG + os::Printer::log("creating joint for skinning ", TransformNodeName.c_str(), ELL_DEBUG); +#endif + n = AnimatedMesh->getAllJoints().size(); + joint=AnimatedMesh->addJoint(0); + joint->Name=TransformNodeName; + } + + // read vertex weights + const u32 nWeights = readInt(); + + // read vertex indices + u32 i; + + const u32 jointStart = joint->Weights.size(); + joint->Weights.reallocate(jointStart+nWeights); + + mesh.WeightJoint.reallocate( mesh.WeightJoint.size() + nWeights ); + mesh.WeightNum.reallocate( mesh.WeightNum.size() + nWeights ); + + for (i=0; iWeights.size()); + + CSkinnedMesh::SWeight *weight=AnimatedMesh->addWeight(joint); + + weight->buffer_id=0; + weight->vertex_id=readInt(); + } + + // read vertex weights + + for (i=jointStart; iWeights[i].strength = readFloat(); + + // read matrix offset + + // transforms the mesh vertices to the space of the bone + // When concatenated to the bone's transform, this provides the + // world space coordinates of the mesh as affected by the bone + core::matrix4& MatrixOffset = joint->GlobalInversedMatrix; + + readMatrix(MatrixOffset); + + if (!checkForOneFollowingSemicolons()) + { + os::Printer::log("No finishing semicolon in Skin Weights found in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + } + + if (!checkForClosingBrace()) + { + os::Printer::log("No closing brace in Skin Weights found in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + + return true; +} + + +bool CXMeshFileLoader::parseDataObjectSkinMeshHeader(SXMesh& mesh) +{ +#ifdef _XREADER_DEBUG + os::Printer::log("CXFileReader: Reading skin mesh header", ELL_DEBUG); +#endif + + if (!readHeadOfDataObject()) + { + os::Printer::log("No opening brace in Skin Mesh header found in .x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + + mesh.MaxSkinWeightsPerVertex = readInt(); + mesh.MaxSkinWeightsPerFace = readInt(); + mesh.BoneCount = readInt(); + + if (!BinaryFormat) + getNextToken(); // skip semicolon + + if (!checkForClosingBrace()) + { + os::Printer::log("No closing brace in skin mesh header in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + + return true; +} + + +bool CXMeshFileLoader::parseDataObjectMeshNormals(SXMesh &mesh) +{ +#ifdef _XREADER_DEBUG + os::Printer::log("CXFileReader: reading mesh normals", ELL_DEBUG); +#endif + + if (!readHeadOfDataObject()) + { + os::Printer::log("No opening brace in Mesh Normals found in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + + // read count + const u32 nNormals = readInt(); + core::array normals; + normals.set_used(nNormals); + + // read normals + for (u32 i=0; i normalIndices; + normalIndices.set_used(mesh.Indices.size()); + + // read face normal indices + const u32 nFNormals = readInt(); + + u32 normalidx = 0; + core::array polygonfaces; + for (u32 k=0; k=mesh.Vertices.size()) + { + os::Printer::log("index value in parseDataObjectMeshVertexColors out of bounds", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + readRGBA(mesh.Vertices[Index].Color); + checkForOneFollowingSemicolons(); + } + + if (!checkForOneFollowingSemicolons()) + { + os::Printer::log("No finishing semicolon in Mesh Vertex Colors Array found in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + } + + if (!checkForClosingBrace()) + { + os::Printer::log("No closing brace in Mesh Texture Coordinates Array found in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + + return true; +} + + +bool CXMeshFileLoader::parseDataObjectMeshMaterialList(SXMesh &mesh) +{ +#ifdef _XREADER_DEBUG + os::Printer::log("CXFileReader: Reading mesh material list", ELL_DEBUG); +#endif + + if (!readHeadOfDataObject()) + { + os::Printer::log("No opening brace in Mesh Material List found in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + + // read material count + mesh.Materials.reallocate(readInt()); + + // read non triangulated face material index count + const u32 nFaceIndices = readInt(); + + // There seems to be a compact representation of "all faces the same material" + // being represented as 1;1;0;; which means 1 material, 1 face with first material + // all the other faces have to obey then, so check is disabled + //if (nFaceIndices != mesh.IndexCountPerFace.size()) + // os::Printer::log("Index count per face not equal to face material index count in x file.", ELL_WARNING); + + // read non triangulated face indices and create triangulated ones + mesh.FaceMaterialIndices.set_used( mesh.Indices.size() / 3); + u32 triangulatedindex = 0; + u32 ind = 0; + for (u32 tfi=0; tfiexistFile(TextureFileName)) + material.setTexture(textureLayer, SceneManager->getVideoDriver()->getTexture(TextureFileName)); + // mesh path + else + { + TextureFileName=FilePath + FileSystem->getFileBasename(TextureFileName); + if (FileSystem->existFile(TextureFileName)) + material.setTexture(textureLayer, SceneManager->getVideoDriver()->getTexture(TextureFileName)); + // working directory + else + material.setTexture(textureLayer, SceneManager->getVideoDriver()->getTexture(FileSystem->getFileBasename(TextureFileName))); + } + ++textureLayer; + if (textureLayer==2) + material.MaterialType=video::EMT_LIGHTMAP; + } + else + if (objectName.equals_ignore_case("NormalmapFilename")) + { + // some exporters write "NormalmapFileName" instead. + core::stringc TextureFileName; + if (!parseDataObjectTextureFilename(TextureFileName)) + return false; + + // original name + if (FileSystem->existFile(TextureFileName)) + material.setTexture(1, SceneManager->getVideoDriver()->getTexture(TextureFileName)); + // mesh path + else + { + TextureFileName=FilePath + FileSystem->getFileBasename(TextureFileName); + if (FileSystem->existFile(TextureFileName)) + material.setTexture(1, SceneManager->getVideoDriver()->getTexture(TextureFileName)); + // working directory + else + material.setTexture(1, SceneManager->getVideoDriver()->getTexture(FileSystem->getFileBasename(TextureFileName))); + } + if (textureLayer==1) + ++textureLayer; + } + else + { + os::Printer::log("Unknown data object in material in .x file", objectName.c_str(), ELL_WARNING); + if (!parseUnknownDataObject()) + return false; + } + } + + return true; +} + + +bool CXMeshFileLoader::parseDataObjectAnimationSet() +{ +#ifdef _XREADER_DEBUG + os::Printer::log("CXFileReader: Reading animation set", ELL_DEBUG); +#endif + + core::stringc AnimationName; + + if (!readHeadOfDataObject(&AnimationName)) + { + os::Printer::log("No opening brace in Animation Set found in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + os::Printer::log("Reading animationset ", AnimationName, ELL_DEBUG); + + while(true) + { + core::stringc objectName = getNextToken(); + + if (objectName.size() == 0) + { + os::Printer::log("Unexpected ending found in Animation set in x file.", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + else + if (objectName == "}") + { + break; // animation set finished + } + else + if (objectName == "Animation") + { + if (!parseDataObjectAnimation()) + return false; + } + else + { + os::Printer::log("Unknown data object in animation set in x file", objectName.c_str(), ELL_WARNING); + if (!parseUnknownDataObject()) + return false; + } + } + return true; +} + + +bool CXMeshFileLoader::parseDataObjectAnimation() +{ +#ifdef _XREADER_DEBUG + os::Printer::log("CXFileReader: reading animation", ELL_DEBUG); +#endif + + if (!readHeadOfDataObject()) + { + os::Printer::log("No opening brace in Animation found in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + + //anim.closed = true; + //anim.linearPositionQuality = true; + CSkinnedMesh::SJoint animationDump; + + core::stringc FrameName; + + while(true) + { + core::stringc objectName = getNextToken(); + + if (objectName.size() == 0) + { + os::Printer::log("Unexpected ending found in Animation in x file.", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + else + if (objectName == "}") + { + break; // animation finished + } + else + if (objectName == "AnimationKey") + { + if (!parseDataObjectAnimationKey(&animationDump)) + return false; + } + else + if (objectName == "AnimationOptions") + { + //TODO: parse options. + if (!parseUnknownDataObject()) + return false; + } + else + if (objectName == "{") + { + // read frame name + FrameName = getNextToken(); + + if (!checkForClosingBrace()) + { + os::Printer::log("Unexpected ending found in Animation in x file.", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + } + else + { + os::Printer::log("Unknown data object in animation in x file", objectName.c_str(), ELL_WARNING); + if (!parseUnknownDataObject()) + return false; + } + } + + if (FrameName.size() != 0) + { +#ifdef _XREADER_DEBUG + os::Printer::log("frame name", FrameName.c_str(), ELL_DEBUG); +#endif + CSkinnedMesh::SJoint *joint=0; + + u32 n; + for (n=0; n < AnimatedMesh->getAllJoints().size(); ++n) + { + if (AnimatedMesh->getAllJoints()[n]->Name==FrameName) + { + joint=AnimatedMesh->getAllJoints()[n]; + break; + } + } + + if (!joint) + { +#ifdef _XREADER_DEBUG + os::Printer::log("creating joint for animation ", FrameName.c_str(), ELL_DEBUG); +#endif + joint=AnimatedMesh->addJoint(0); + joint->Name=FrameName; + } + + joint->PositionKeys.reallocate(joint->PositionKeys.size()+animationDump.PositionKeys.size()); + for (n=0; nPositionKeys.push_back(animationDump.PositionKeys[n]); + } + + joint->ScaleKeys.reallocate(joint->ScaleKeys.size()+animationDump.ScaleKeys.size()); + for (n=0; nScaleKeys.push_back(animationDump.ScaleKeys[n]); + } + + joint->RotationKeys.reallocate(joint->RotationKeys.size()+animationDump.RotationKeys.size()); + for (n=0; nRotationKeys.push_back(animationDump.RotationKeys[n]); + } + } + else + os::Printer::log("joint name was never given", ELL_WARNING); + + return true; +} + + +bool CXMeshFileLoader::parseDataObjectAnimationKey(ISkinnedMesh::SJoint *joint) +{ +#ifdef _XREADER_DEBUG + os::Printer::log("CXFileReader: reading animation key", ELL_DEBUG); +#endif + + if (!readHeadOfDataObject()) + { + os::Printer::log("No opening brace in Animation Key found in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + + // read key type + + const u32 keyType = readInt(); + + if (keyType > 4) + { + os::Printer::log("Unknown key type found in Animation Key in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + + // read number of keys + const u32 numberOfKeys = readInt(); + + // eat the semicolon after the "0". if there are keys present, readInt() + // does this for us. If there aren't, we need to do it explicitly + if (numberOfKeys == 0) + checkForOneFollowingSemicolons(); + + for (u32 i=0; iaddRotationKey(joint); + key->frame=time; + key->rotation.set(X,Y,Z,W); + } + break; + case 1: //scale + case 2: //position + { + // read vectors + + // read count + if (readInt() != 3) + { + os::Printer::log("Expected 3 numbers in animation key in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + + core::vector3df vector; + readVector3(vector); + + if (!checkForTwoFollowingSemicolons()) + { + os::Printer::log("No finishing semicolon after vector animation key in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + } + + if (keyType==2) + { + ISkinnedMesh::SPositionKey *key=AnimatedMesh->addPositionKey(joint); + key->frame=time; + key->position=vector; + } + else + { + ISkinnedMesh::SScaleKey *key=AnimatedMesh->addScaleKey(joint); + key->frame=time; + key->scale=vector; + } + } + break; + case 3: + case 4: + { + // read matrix + + // read count + if (readInt() != 16) + { + os::Printer::log("Expected 16 numbers in animation key in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + + // read matrix + core::matrix4 mat(core::matrix4::EM4CONST_NOTHING); + readMatrix(mat); + + //mat=joint->LocalMatrix*mat; + + if (!checkForOneFollowingSemicolons()) + { + os::Printer::log("No finishing semicolon after matrix animation key in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + } + + //core::vector3df rotation = mat.getRotationDegrees(); + + ISkinnedMesh::SRotationKey *keyR=AnimatedMesh->addRotationKey(joint); + keyR->frame=time; + + // IRR_TEST_BROKEN_QUATERNION_USE: TODO - switched from mat to mat.getTransposed() for downward compatibility. + // Not tested so far if this was correct or wrong before quaternion fix! + keyR->rotation= core::quaternion(mat.getTransposed()); + + ISkinnedMesh::SPositionKey *keyP=AnimatedMesh->addPositionKey(joint); + keyP->frame=time; + keyP->position=mat.getTranslation(); + +/* + core::vector3df scale=mat.getScale(); + + if (scale.X==0) + scale.X=1; + if (scale.Y==0) + scale.Y=1; + if (scale.Z==0) + scale.Z=1; + ISkinnedMesh::SScaleKey *keyS=AnimatedMesh->addScaleKey(joint); + keyS->frame=time; + keyS->scale=scale; +*/ + } + break; + } // end switch + } + + if (!checkForOneFollowingSemicolons()) + --P; + + if (!checkForClosingBrace()) + { + os::Printer::log("No closing brace in animation key in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + + return true; +} + + +bool CXMeshFileLoader::parseDataObjectTextureFilename(core::stringc& texturename) +{ +#ifdef _XREADER_DEBUG + os::Printer::log("CXFileReader: reading texture filename", ELL_DEBUG); +#endif + + if (!readHeadOfDataObject()) + { + os::Printer::log("No opening brace in Texture filename found in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + + if (!getNextTokenAsString(texturename)) + { + os::Printer::log("Unknown syntax while reading texture filename string in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + + if (!checkForClosingBrace()) + { + os::Printer::log("No closing brace in Texture filename found in x file", ELL_WARNING); + os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); + return false; + } + + return true; +} + + +bool CXMeshFileLoader::parseUnknownDataObject() +{ + // find opening delimiter + while(true) + { + core::stringc t = getNextToken(); + + if (t.size() == 0) + return false; + + if (t == "{") + break; + } + + u32 counter = 1; + + // parse until closing delimiter + + while(counter) + { + core::stringc t = getNextToken(); + + if (t.size() == 0) + return false; + + if (t == "{") + ++counter; + else + if (t == "}") + --counter; + } + + return true; +} + + +//! checks for closing curly brace, returns false if not there +bool CXMeshFileLoader::checkForClosingBrace() +{ + return (getNextToken() == "}"); +} + + +//! checks for one following semicolon, returns false if not there +bool CXMeshFileLoader::checkForOneFollowingSemicolons() +{ + if (BinaryFormat) + return true; + + if (getNextToken() == ";") + return true; + else + { + --P; + return false; + } +} + + +//! checks for two following semicolons, returns false if they are not there +bool CXMeshFileLoader::checkForTwoFollowingSemicolons() +{ + if (BinaryFormat) + return true; + + for (u32 k=0; k<2; ++k) + { + if (getNextToken() != ";") + { + --P; + return false; + } + } + + return true; +} + + +//! reads header of dataobject including the opening brace. +//! returns false if error happened, and writes name of object +//! if there is one +bool CXMeshFileLoader::readHeadOfDataObject(core::stringc* outname) +{ + core::stringc nameOrBrace = getNextToken(); + if (nameOrBrace != "{") + { + if (outname) + (*outname) = nameOrBrace; + + if (getNextToken() != "{") + return false; + } + + return true; +} + + +//! returns next parseable token. Returns empty string if no token there +core::stringc CXMeshFileLoader::getNextToken() +{ + core::stringc s; + + // process binary-formatted file + if (BinaryFormat) + { + // in binary mode it will only return NAME and STRING token + // and (correctly) skip over other tokens. + + s16 tok = readBinWord(); + u32 len; + + // standalone tokens + switch (tok) { + case 1: + // name token + len = readBinDWord(); + s = core::stringc(P, len); + P += len; + return s; + case 2: + // string token + len = readBinDWord(); + s = core::stringc(P, len); + P += (len + 2); + return s; + case 3: + // integer token + P += 4; + return ""; + case 5: + // GUID token + P += 16; + return ""; + case 6: + len = readBinDWord(); + P += (len * 4); + return ""; + case 7: + len = readBinDWord(); + P += (len * FloatSize); + return ""; + case 0x0a: + return "{"; + case 0x0b: + return "}"; + case 0x0c: + return "("; + case 0x0d: + return ")"; + case 0x0e: + return "["; + case 0x0f: + return "]"; + case 0x10: + return "<"; + case 0x11: + return ">"; + case 0x12: + return "."; + case 0x13: + return ","; + case 0x14: + return ";"; + case 0x1f: + return "template"; + case 0x28: + return "WORD"; + case 0x29: + return "DWORD"; + case 0x2a: + return "FLOAT"; + case 0x2b: + return "DOUBLE"; + case 0x2c: + return "CHAR"; + case 0x2d: + return "UCHAR"; + case 0x2e: + return "SWORD"; + case 0x2f: + return "SDWORD"; + case 0x30: + return "void"; + case 0x31: + return "string"; + case 0x32: + return "unicode"; + case 0x33: + return "cstring"; + case 0x34: + return "array"; + } + } + // process text-formatted file + else + { + findNextNoneWhiteSpace(); + + if (P >= End) + return s; + + while((P < End) && !core::isspace(P[0])) + { + // either keep token delimiters when already holding a token, or return if first valid char + if (P[0]==';' || P[0]=='}' || P[0]=='{' || P[0]==',') + { + if (!s.size()) + { + s.append(P[0]); + ++P; + } + break; // stop for delimiter + } + s.append(P[0]); + ++P; + } + } + return s; +} + + +//! places pointer to next begin of a token, which must be a number, +// and ignores comments +void CXMeshFileLoader::findNextNoneWhiteSpaceNumber() +{ + if (BinaryFormat) + return; + + while((P < End) && (P[0] != '-') && (P[0] != '.') && + !( core::isdigit(P[0]))) + { + // check if this is a comment + if ((P[0] == '/' && P[1] == '/') || P[0] == '#') + readUntilEndOfLine(); + else + ++P; + } +} + + +// places pointer to next begin of a token, and ignores comments +void CXMeshFileLoader::findNextNoneWhiteSpace() +{ + if (BinaryFormat) + return; + + while(true) + { + while((P < End) && core::isspace(P[0])) + { + if (*P=='\n') + ++Line; + ++P; + } + + if (P >= End) + return; + + // check if this is a comment + if ((P[0] == '/' && P[1] == '/') || + P[0] == '#') + readUntilEndOfLine(); + else + break; + } +} + + +//! reads a x file style string +bool CXMeshFileLoader::getNextTokenAsString(core::stringc& out) +{ + if (BinaryFormat) + { + out=getNextToken(); + return true; + } + findNextNoneWhiteSpace(); + + if (P >= End) + return false; + + if (P[0] != '"') + return false; + ++P; + + while(P < End && P[0]!='"') + { + out.append(P[0]); + ++P; + } + + if ( P[1] != ';' || P[0] != '"') + return false; + P+=2; + + return true; +} + + +void CXMeshFileLoader::readUntilEndOfLine() +{ + if (BinaryFormat) + return; + + while(P < End) + { + if (P[0] == '\n' || P[0] == '\r') + { + ++P; + ++Line; + return; + } + + ++P; + } +} + + +u16 CXMeshFileLoader::readBinWord() +{ +#ifdef __BIG_ENDIAN__ + const u16 tmp = os::Byteswap::byteswap(*(u16 *)P); +#else + const u16 tmp = *(u16 *)P; +#endif + P += 2; + return tmp; +} + + +u32 CXMeshFileLoader::readBinDWord() +{ +#ifdef __BIG_ENDIAN__ + const u32 tmp = os::Byteswap::byteswap(*(u32 *)P); +#else + const u32 tmp = *(u32 *)P; +#endif + P += 4; + return tmp; +} + + +u32 CXMeshFileLoader::readInt() +{ + if (BinaryFormat) + { + if (!BinaryNumCount) + { + const u16 tmp = readBinWord(); // 0x06 or 0x03 + if (tmp == 0x06) + BinaryNumCount = readBinDWord(); + else + BinaryNumCount = 1; // single int + } + --BinaryNumCount; + return readBinDWord(); + } + else + { + findNextNoneWhiteSpaceNumber(); + return core::strtoul10(P, &P); + } +} + + +f32 CXMeshFileLoader::readFloat() +{ + if (BinaryFormat) + { + if (!BinaryNumCount) + { + const u16 tmp = readBinWord(); // 0x07 or 0x42 + if (tmp == 0x07) + BinaryNumCount = readBinDWord(); + else + BinaryNumCount = 1; // single int + } + --BinaryNumCount; + if (FloatSize == 8) + { +#ifdef __BIG_ENDIAN__ + //TODO: Check if data is properly converted here + f32 ctmp[2]; + ctmp[1] = os::Byteswap::byteswap(*(f32*)P); + ctmp[0] = os::Byteswap::byteswap(*(f32*)P+4); + const f32 tmp = (f32)(*(f64*)(void*)ctmp); +#else + const f32 tmp = (f32)(*(f64 *)P); +#endif + P += 8; + return tmp; + } + else + { +#ifdef __BIG_ENDIAN__ + const f32 tmp = os::Byteswap::byteswap(*(f32 *)P); +#else + const f32 tmp = *(f32 *)P; +#endif + P += 4; + return tmp; + } + } + findNextNoneWhiteSpaceNumber(); + f32 ftmp; + P = core::fast_atof_move(P, ftmp); + return ftmp; +} + + +// read 2-dimensional vector. Stops at semicolon after second value for text file format +bool CXMeshFileLoader::readVector2(core::vector2df& vec) +{ + vec.X = readFloat(); + vec.Y = readFloat(); + return true; +} + + +// read 3-dimensional vector. Stops at semicolon after third value for text file format +bool CXMeshFileLoader::readVector3(core::vector3df& vec) +{ + vec.X = readFloat(); + vec.Y = readFloat(); + vec.Z = readFloat(); + return true; +} + + +// read color without alpha value. Stops after second semicolon after blue value +bool CXMeshFileLoader::readRGB(video::SColor& color) +{ + video::SColorf tmpColor; + tmpColor.r = readFloat(); + tmpColor.g = readFloat(); + tmpColor.b = readFloat(); + color = tmpColor.toSColor(); + return checkForOneFollowingSemicolons(); +} + + +// read color with alpha value. Stops after second semicolon after blue value +bool CXMeshFileLoader::readRGBA(video::SColor& color) +{ + video::SColorf tmpColor; + tmpColor.r = readFloat(); + tmpColor.g = readFloat(); + tmpColor.b = readFloat(); + tmpColor.a = readFloat(); + color = tmpColor.toSColor(); + return checkForOneFollowingSemicolons(); +} + + +// read matrix from list of floats +bool CXMeshFileLoader::readMatrix(core::matrix4& mat) +{ + for (u32 i=0; i<16; ++i) + mat[i] = readFloat(); + return checkForOneFollowingSemicolons(); +} + + +} // end namespace scene +} // end namespace irr + +#endif // _IRR_COMPILE_WITH_X_LOADER_ + -- cgit v1.1