From fbd4188b832ca93bf8817708e42f67b8894cb750 Mon Sep 17 00:00:00 2001 From: irrlicht Date: Sun, 9 Sep 2007 06:52:57 +0000 Subject: [PATCH] added loader for the new .irrmesh format. (CIrrMeshFileLoader) git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@931 dfc29bdd-3216-0410-991c-e03cc46cb475 --- changes.txt | 8 +- include/IAttributes.h | 4 +- include/IrrCompileConfig.h | 3 + source/Irrlicht/CIrrMeshFileLoader.cpp | 586 +++++++++++++++++++++++++ source/Irrlicht/CIrrMeshFileLoader.h | 96 ++++ source/Irrlicht/CSceneManager.cpp | 8 +- source/Irrlicht/Irrlicht7.1.vcproj | 6 + 7 files changed, 706 insertions(+), 5 deletions(-) create mode 100644 source/Irrlicht/CIrrMeshFileLoader.cpp create mode 100644 source/Irrlicht/CIrrMeshFileLoader.h diff --git a/changes.txt b/changes.txt index 4296c550..3b31df33 100644 --- a/changes.txt +++ b/changes.txt @@ -1,8 +1,12 @@ Changes in version 1.4 (... 2007) - - Irrlicht is now able to write Meshes out into files again. Use ISceneManager::createMeshWriter() + - Irrlicht now has its own file format for static meshes. It is based on xml and has the + extension .irrmesh. Irrlicht is able to write every IMesh to this file format, and of course to + read it back again. + + - Irrlicht is now able to write Meshes out into files. Use ISceneManager::createMeshWriter() to obtain an interface with which you can write out meshes. Currently, an own .irrmesh - file format is supported as well as COLLADA. + file format is supported as well as the COLLADA file format. - the base class for nearly all Irrlicht classes has been renamed from IUnknown to IReferenceCounted diff --git a/include/IAttributes.h b/include/IAttributes.h index ef9acf60..9567ddee 100644 --- a/include/IAttributes.h +++ b/include/IAttributes.h @@ -154,7 +154,8 @@ public: virtual void clear() = 0; //! Reads attributes from a xml file. - //! \param readCurrentElementOnly: If set to true, reading only works if current element has the name 'attributes'. + //! \param readCurrentElementOnly: If set to true, reading only works if current element has the name 'attributes' or + //! the name specified using elementName. //! \param elementName: The surrounding element name. If it is null, the default one, "attributes" will be taken. //! If set to false, the first appearing list of attributes are read. virtual bool read(irr::io::IXMLReader* reader, bool readCurrentElementOnly=false, const wchar_t* elementName=0) = 0; @@ -163,7 +164,6 @@ public: //! \param writer: The XML writer to write to //! \param writeXMLHeader: Writes a header to the XML file, required if at the beginning of the file //! \param elementName: The surrounding element name. If it is null, the default one, "attributes" will be taken. - //! and you haven't already written one with writer->writeXMLHeader() virtual bool write(io::IXMLWriter* writer, bool writeXMLHeader=false, const wchar_t* elementName=0) = 0; diff --git a/include/IrrCompileConfig.h b/include/IrrCompileConfig.h index fac43d58..823cc980 100644 --- a/include/IrrCompileConfig.h +++ b/include/IrrCompileConfig.h @@ -227,6 +227,9 @@ B3D, MS3D or X meshes */ #define _IRR_COMPILE_WITH_X_LOADER_ #endif +//! Define _IRR_COMPILE_WITH_IRR_MESH_LOADER_ if you want to load Irrlicht Engine .irrmesh files +#define _IRR_COMPILE_WITH_IRR_MESH_LOADER_ + //! Define _IRR_COMPILE_WITH_MD2_LOADER_ if you want to load Quake 2 animated files #define _IRR_COMPILE_WITH_MD2_LOADER_ //! Define _IRR_COMPILE_WITH_MD3_LOADER_ if you want to load Quake 3 animated files diff --git a/source/Irrlicht/CIrrMeshFileLoader.cpp b/source/Irrlicht/CIrrMeshFileLoader.cpp new file mode 100644 index 00000000..e273debb --- /dev/null +++ b/source/Irrlicht/CIrrMeshFileLoader.cpp @@ -0,0 +1,586 @@ +// Copyright (C) 2002-2007 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_IRR_MESH_LOADER_ + +#include "CIrrMeshFileLoader.h" +#include "os.h" +#include "IXMLReader.h" +#include "SAnimatedMesh.h" +#include "fast_atof.h" +#include "IReadFile.h" +#include "IAttributes.h" +#include "IMeshSceneNode.h" +#include "SMeshBufferLightMap.h" + +namespace irr +{ +namespace scene +{ + + +//! Constructor +CIrrMeshFileLoader::CIrrMeshFileLoader(video::IVideoDriver* driver, + scene::ISceneManager* smgr, io::IFileSystem* fs) +: Driver(driver), SceneManager(smgr), FileSystem(fs) +{ + +} + + +//! destructor +CIrrMeshFileLoader::~CIrrMeshFileLoader() +{ +} + + +//! Returns true if the file maybe is able to be loaded by this class. +/** This decision should be based only on the file extension (e.g. ".cob") */ +bool CIrrMeshFileLoader::isALoadableFileExtension(const c8* fileName) +{ + return strstr(fileName, ".xml") || + strstr(fileName, ".irrmesh"); +} + + +//! 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* CIrrMeshFileLoader::createMesh(irr::io::IReadFile* file) +{ + io::IXMLReader* reader = FileSystem->createXMLReader(file); + if (!reader) + return 0; + + // read until mesh section, skip other parts + + const core::stringc meshTagName = "mesh"; + IAnimatedMesh* mesh = 0; + + while(reader->read()) + { + if (reader->getNodeType() == io::EXN_ELEMENT) + { + if (meshTagName == reader->getNodeName()) + { + mesh = readMesh(reader); + break; + } + else + skipSection(reader, true); // unknown section + } + } + + reader->drop(); + + return mesh; +} + + +//! reads a mesh sections and creates a mesh from it +IAnimatedMesh* CIrrMeshFileLoader::readMesh(io::IXMLReader* reader) +{ + SAnimatedMesh* animatedmesh = new SAnimatedMesh(); + SMesh* mesh = new SMesh(); + + animatedmesh->addMesh(mesh); + mesh->drop(); + + core::stringc bbSectionName = "boundingBox"; + core::stringc bufferSectionName = "buffer"; + core::stringc meshSectionName = "mesh"; + + if (!reader->isEmptyElement()) + while(reader->read()) + { + if (reader->getNodeType() == io::EXN_ELEMENT) + { + const wchar_t* nodeName = reader->getNodeName(); + if (bbSectionName == nodeName) + { + // inside a bounding box, ignore it for now because + // we are calculating this anyway ourselves later. + } + else + if (bufferSectionName == nodeName) + { + // we've got a mesh buffer + + IMeshBuffer* buffer = readMeshBuffer(reader); + if (buffer) + { + mesh->addMeshBuffer(buffer); + buffer->drop(); + } + } + else + skipSection(reader, true); // unknown section + + } // end if node type is element + else + if (reader->getNodeType() == io::EXN_ELEMENT_END) + { + if (meshSectionName == reader->getNodeName()) + { + // end of mesh section reached, cancel out + break; + } + } + } // end while reader->read(); + + mesh->recalculateBoundingBox(); + animatedmesh->recalculateBoundingBox(); + + return animatedmesh; +} + + +//! reads a mesh sections and creates a mesh buffer from it +IMeshBuffer* CIrrMeshFileLoader::readMeshBuffer(io::IXMLReader* reader) +{ + IMeshBuffer* buffer = 0; + SMeshBuffer* sbuffer1 = 0; + SMeshBufferLightMap* sbuffer2 = 0; + SMeshBufferTangents* sbuffer3 = 0; + + core::stringc verticesSectionName = "vertices"; + core::stringc bbSectionName = "boundingBox"; + core::stringc materialSectionName = "material"; + core::stringc indicesSectionName = "indices"; + core::stringc bufferSectionName = "buffer"; + + bool insideVertexSection = false; + bool insideIndexSection = false; + + int vertexCount = 0; + int indexCount = 0; + + video::SMaterial material; + + if (!reader->isEmptyElement()) + while(reader->read()) + { + if (reader->getNodeType() == io::EXN_ELEMENT) + { + const wchar_t* nodeName = reader->getNodeName(); + if (bbSectionName == nodeName) + { + // inside a bounding box, ignore it for now because + // we are calculating this anyway ourselves later. + } + else + if (materialSectionName == nodeName) + { + //we've got a material + + io::IAttributes* attributes = FileSystem->createEmptyAttributes(Driver); + attributes->read(reader, true, L"material"); + + Driver->fillMaterialStructureFromAttributes(material, attributes); + attributes->drop(); + } + else + if (verticesSectionName == nodeName) + { + // vertices section + + core::stringc vertexTypeName1 = "standard"; + core::stringc vertexTypeName2 = "2tcoords"; + core::stringc vertexTypeName3 = "tangents"; + + const wchar_t* vertexType = reader->getAttributeValue(L"type"); + vertexCount = reader->getAttributeValueAsInt(L"vertexCount"); + + insideVertexSection = true; + + if (vertexTypeName1 == vertexType) + { + sbuffer1 = new SMeshBuffer(); + sbuffer1->Vertices.reallocate(vertexCount); + sbuffer1->Material = material; + buffer = sbuffer1; + } + else + if (vertexTypeName2 == vertexType) + { + sbuffer2 = new SMeshBufferLightMap(); + sbuffer2->Vertices.reallocate(vertexCount); + sbuffer2->Material = material; + buffer = sbuffer2; + } + else + if (vertexTypeName3 == vertexType) + { + sbuffer3 = new SMeshBufferTangents(); + sbuffer3->Vertices.reallocate(vertexCount); + sbuffer3->Material = material; + buffer = sbuffer3; + } + } + else + if (indicesSectionName == nodeName) + { + // indices section + + indexCount = reader->getAttributeValueAsInt(L"indexCount"); + insideIndexSection = true; + } + + } // end if node type is element + else + if (reader->getNodeType() == io::EXN_TEXT) + { + // read vertex data + if (insideVertexSection) + { + if (sbuffer1) + readMeshBuffer(reader, vertexCount, sbuffer1); + else + if (sbuffer2) + readMeshBuffer(reader, vertexCount, sbuffer2); + else + if (sbuffer3) + readMeshBuffer(reader, vertexCount, sbuffer3); + + insideVertexSection = false; + + } // end reading vertex array + else + if (insideIndexSection) + { + if (sbuffer1) + readIndices(reader, indexCount, sbuffer1->Indices); + else + if (sbuffer2) + readIndices(reader, indexCount, sbuffer2->Indices); + else + if (sbuffer2) + readIndices(reader, indexCount, sbuffer3->Indices); + + insideIndexSection = false; + } + + } // end if node type is text + else + if (reader->getNodeType() == io::EXN_ELEMENT_END) + { + if (bufferSectionName == reader->getNodeName()) + { + // end of buffer section reached, cancel out + break; + } + } + } // end while reader->read(); + + if (buffer) + buffer->recalculateBoundingBox(); + + return buffer; +} + + +//! read indices +void CIrrMeshFileLoader::readIndices(io::IXMLReader* reader, int indexCount, core::array& indices) +{ + indices.reallocate(indexCount); + + core::stringc data = reader->getNodeData(); + const c8* p = &data[0]; + + for (int i=0; igetNodeData(); + const c8* p = &data[0]; + + if (sbuffer) + { + video::S3DVertex vtx; + + for (int i=0; iVertices.push_back(vtx); + } + } +} + + +void CIrrMeshFileLoader::readMeshBuffer(io::IXMLReader* reader, int vertexCount, SMeshBufferLightMap* sbuffer) +{ + core::stringc data = reader->getNodeData(); + const c8* p = &data[0]; + + if (sbuffer) + { + video::S3DVertex2TCoords vtx; + + for (int i=0; iVertices.push_back(vtx); + } + } +} + + +void CIrrMeshFileLoader::readMeshBuffer(io::IXMLReader* reader, int vertexCount, SMeshBufferTangents* sbuffer) +{ + core::stringc data = reader->getNodeData(); + const c8* p = &data[0]; + + if (sbuffer) + { + video::S3DVertexTangents vtx; + + for (int i=0; iVertices.push_back(vtx); + } + } +} + + +//! skips an (unknown) section in the irrmesh document +void CIrrMeshFileLoader::skipSection(io::IXMLReader* reader, bool reportSkipping) +{ +#ifdef _DEBUG + os::Printer::log("irrMesh skipping section", core::stringc(reader->getNodeName()).c_str()); +#endif + + // skip if this element is empty anyway. + if (reader->isEmptyElement()) + return; + + // read until we've reached the last element in this section + u32 tagCounter = 1; + + while(tagCounter && reader->read()) + { + if (reader->getNodeType() == io::EXN_ELEMENT && + !reader->isEmptyElement()) + { + #ifdef _DEBUG + if (reportSkipping) + os::Printer::log("irrMesh unknown element:", core::stringc(reader->getNodeName()).c_str()); + #endif + + ++tagCounter; + } + else + if (reader->getNodeType() == io::EXN_ELEMENT_END) + --tagCounter; + } +} + + +//! parses a float from a char pointer and moves the pointer +//! to the end of the parsed float +inline f32 CIrrMeshFileLoader::readFloat(const c8** p) +{ + f32 ftmp; + *p = core::fast_atof_move(*p, ftmp); + return ftmp; +} + + +//! parses an int from a char pointer and moves the pointer to +//! the end of the parsed float +inline s32 CIrrMeshFileLoader::readInt(const c8** p) +{ + return (s32)readFloat(p); +} + + +//! places pointer to next begin of a token +void CIrrMeshFileLoader::skipCurrentNoneWhiteSpace(const c8** start) +{ + const c8* p = *start; + + while(*p && !(*p==' ' || *p=='\n' || *p=='\r' || *p=='\t')) + ++p; + + // TODO: skip comments + + *start = p; +} + +//! places pointer to next begin of a token +void CIrrMeshFileLoader::findNextNoneWhiteSpace(const c8** start) +{ + const c8* p = *start; + + while(*p && (*p==' ' || *p=='\n' || *p=='\r' || *p=='\t')) + ++p; + + // TODO: skip comments + + *start = p; +} + + +//! reads floats from inside of xml element until end of xml element +void CIrrMeshFileLoader::readFloatsInsideElement(io::IXMLReader* reader, f32* floats, u32 count) +{ + if (reader->isEmptyElement()) + return; + + while(reader->read()) + { + // TODO: check for comments inside the element + // and ignore them. + + if (reader->getNodeType() == io::EXN_TEXT) + { + // parse float data + core::stringc data = reader->getNodeData(); + const c8* p = &data[0]; + + for (u32 i=0; igetNodeType() == io::EXN_ELEMENT_END) + break; // end parsing text + } +} + + + + +} // end namespace scene +} // end namespace irr + +#endif // _IRR_COMPILE_WITH_IRR_MESH_LOADER_ diff --git a/source/Irrlicht/CIrrMeshFileLoader.h b/source/Irrlicht/CIrrMeshFileLoader.h new file mode 100644 index 00000000..374e0335 --- /dev/null +++ b/source/Irrlicht/CIrrMeshFileLoader.h @@ -0,0 +1,96 @@ +// Copyright (C) 2002-2007 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#ifndef __C_IRR_MESH_FILE_LOADER_H_INCLUDED__ +#define __C_IRR_MESH_FILE_LOADER_H_INCLUDED__ + +#include "IMeshLoader.h" +#include "IFileSystem.h" +#include "IVideoDriver.h" +#include "irrString.h" +#include "SMesh.h" +#include "SMeshBuffer.h" +#include "ISceneManager.h" + +namespace irr +{ +namespace scene +{ + + +//! Meshloader capable of loading .irrmesh meshes, the Irrlicht Engine mesh format for static meshes +class CIrrMeshFileLoader : public IMeshLoader +{ +public: + + //! Constructor + CIrrMeshFileLoader(video::IVideoDriver* driver, + scene::ISceneManager* smgr, io::IFileSystem* fs); + + //! destructor + virtual ~CIrrMeshFileLoader(); + + //! returns true if the file maybe is able to be loaded by this class + //! based on the file extension (e.g. ".cob") + virtual bool isALoadableFileExtension(const c8* fileName); + + //! 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. + virtual IAnimatedMesh* createMesh(irr::io::IReadFile* file); + +private: + + //! reads a mesh sections and creates a mesh from it + IAnimatedMesh* readMesh(io::IXMLReader* reader); + + //! reads a mesh sections and creates a mesh buffer from it + IMeshBuffer* readMeshBuffer(io::IXMLReader* reader); + + //! skips an (unknown) section in the irrmesh file + void skipSection(io::IXMLReader* reader, bool reportSkipping); + + //! reads a element and stores it in the material section + void readMaterial(io::IXMLReader* reader); + + //! parses a float from a char pointer and moves the pointer to + //! the end of the parsed float + inline f32 readFloat(const c8** p); + + //! parses an int from a char pointer and moves the pointer to + //! the end of the parsed float + inline s32 readInt(const c8** p); + + //! places pointer to next begin of a token + void findNextNoneWhiteSpace(const c8** p); + + //! places pointer to next begin of a token + void skipCurrentNoneWhiteSpace(const c8** p); + + //! reads floats from inside of xml element until end of xml element + void readFloatsInsideElement(io::IXMLReader* reader, f32* floats, u32 count); + + //! read all 3 types of mesh buffers + void readMeshBuffer(io::IXMLReader* reader, int vertexCount, SMeshBuffer* sbuffer); + void readMeshBuffer(io::IXMLReader* reader, int vertexCount, SMeshBufferLightMap* sbuffer); + void readMeshBuffer(io::IXMLReader* reader, int vertexCount, SMeshBufferTangents* sbuffer); + + //! read indices + void readIndices(io::IXMLReader* reader, int indexCount, core::array& indices); + + + // member variables + + video::IVideoDriver* Driver; + scene::ISceneManager* SceneManager; + io::IFileSystem* FileSystem; +}; + + +} // end namespace scene +} // end namespace irr + +#endif + diff --git a/source/Irrlicht/CSceneManager.cpp b/source/Irrlicht/CSceneManager.cpp index 97d81372..9fbe7d48 100644 --- a/source/Irrlicht/CSceneManager.cpp +++ b/source/Irrlicht/CSceneManager.cpp @@ -18,6 +18,10 @@ #include "CGeometryCreator.h" +#ifdef _IRR_COMPILE_WITH_IRR_MESH_LOADER_ +#include "CIrrMeshFileLoader.h" +#endif + #ifdef _IRR_COMPILE_WITH_BSP_LOADER_ #include "CBSPMeshFileLoader.h" #endif @@ -169,7 +173,9 @@ CSceneManager::CSceneManager(video::IVideoDriver* driver, io::IFileSystem* fs, // add file format loaders - + #ifdef _IRR_COMPILE_WITH_IRR_MESH_LOADER_ + MeshLoaderList.push_back(new CIrrMeshFileLoader(Driver, this, FileSystem)); + #endif #ifdef _IRR_COMPILE_WITH_BSP_LOADER_ MeshLoaderList.push_back(new CBSPMeshFileLoader(FileSystem, Driver, this)); #endif diff --git a/source/Irrlicht/Irrlicht7.1.vcproj b/source/Irrlicht/Irrlicht7.1.vcproj index 86a7e112..49496d2c 100644 --- a/source/Irrlicht/Irrlicht7.1.vcproj +++ b/source/Irrlicht/Irrlicht7.1.vcproj @@ -1276,6 +1276,12 @@ + + + +