From 7028cbe09c688437910a25623098762bf0fa592d Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Mon, 28 Mar 2016 22:28:34 +1000 Subject: Move Irrlicht to src/others. --- .../source/Irrlicht/CXMeshFileLoader.cpp | 2423 ++++++++++++++++++++ 1 file changed, 2423 insertions(+) create mode 100644 src/others/irrlicht-1.8.1/source/Irrlicht/CXMeshFileLoader.cpp (limited to 'src/others/irrlicht-1.8.1/source/Irrlicht/CXMeshFileLoader.cpp') diff --git a/src/others/irrlicht-1.8.1/source/Irrlicht/CXMeshFileLoader.cpp b/src/others/irrlicht-1.8.1/source/Irrlicht/CXMeshFileLoader.cpp new file mode 100644 index 0000000..0c4922a --- /dev/null +++ b/src/others/irrlicht-1.8.1/source/Irrlicht/CXMeshFileLoader.cpp @@ -0,0 +1,2423 @@ +// 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() +{ + if (P>=End) + return 0; +#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() +{ + if (P>=End) + return 0; +#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