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
master
irrlicht 2007-09-09 06:52:57 +00:00
parent c02aca0029
commit fbd4188b83
7 changed files with 706 additions and 5 deletions

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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<u16>& indices)
{
indices.reallocate(indexCount);
core::stringc data = reader->getNodeData();
const c8* p = &data[0];
for (int i=0; i<indexCount && *p; ++i)
{
findNextNoneWhiteSpace(&p);
indices.push_back((u16)readInt(&p));
}
}
void CIrrMeshFileLoader::readMeshBuffer(io::IXMLReader* reader, int vertexCount, SMeshBuffer* sbuffer)
{
core::stringc data = reader->getNodeData();
const c8* p = &data[0];
if (sbuffer)
{
video::S3DVertex vtx;
for (int i=0; i<vertexCount && *p; ++i)
{
// position
findNextNoneWhiteSpace(&p);
vtx.Pos.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Pos.Y = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Pos.Z = readFloat(&p);
// normal
findNextNoneWhiteSpace(&p);
vtx.Normal.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Normal.Y = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Normal.Z = readFloat(&p);
// color
findNextNoneWhiteSpace(&p);
sscanf(p, "%08x", &vtx.Color);
skipCurrentNoneWhiteSpace(&p);
// tcoord1
findNextNoneWhiteSpace(&p);
vtx.TCoords.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.TCoords.Y = readFloat(&p);
sbuffer->Vertices.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; i<vertexCount && *p; ++i)
{
// position
findNextNoneWhiteSpace(&p);
vtx.Pos.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Pos.Y = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Pos.Z = readFloat(&p);
// normal
findNextNoneWhiteSpace(&p);
vtx.Normal.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Normal.Y = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Normal.Z = readFloat(&p);
// color
findNextNoneWhiteSpace(&p);
sscanf(p, "%08x", &vtx.Color);
skipCurrentNoneWhiteSpace(&p);
// tcoord1
findNextNoneWhiteSpace(&p);
vtx.TCoords.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.TCoords.Y = readFloat(&p);
// tcoord2
findNextNoneWhiteSpace(&p);
vtx.TCoords2.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.TCoords2.Y = readFloat(&p);
sbuffer->Vertices.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; i<vertexCount && *p; ++i)
{
// position
findNextNoneWhiteSpace(&p);
vtx.Pos.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Pos.Y = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Pos.Z = readFloat(&p);
// normal
findNextNoneWhiteSpace(&p);
vtx.Normal.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Normal.Y = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Normal.Z = readFloat(&p);
// color
findNextNoneWhiteSpace(&p);
sscanf(p, "%08x", &vtx.Color);
skipCurrentNoneWhiteSpace(&p);
// tcoord1
findNextNoneWhiteSpace(&p);
vtx.TCoords.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.TCoords.Y = readFloat(&p);
// tangent
findNextNoneWhiteSpace(&p);
vtx.Tangent.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Tangent.Y = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Tangent.Z = readFloat(&p);
// binormal
findNextNoneWhiteSpace(&p);
vtx.Binormal.X = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Binormal.Y = readFloat(&p);
findNextNoneWhiteSpace(&p);
vtx.Binormal.Z = readFloat(&p);
sbuffer->Vertices.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; i<count; ++i)
{
findNextNoneWhiteSpace(&p);
if (*p)
floats[i] = readFloat(&p);
else
floats[i] = 0.0f;
}
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
break; // end parsing text
}
}
} // end namespace scene
} // end namespace irr
#endif // _IRR_COMPILE_WITH_IRR_MESH_LOADER_

View File

@ -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 <material> 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<u16>& indices);
// member variables
video::IVideoDriver* Driver;
scene::ISceneManager* SceneManager;
io::IFileSystem* FileSystem;
};
} // end namespace scene
} // end namespace irr
#endif

View File

@ -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

View File

@ -1276,6 +1276,12 @@
<File
RelativePath=".\CDMFLoader.h">
</File>
<File
RelativePath=".\CIrrMeshFileLoader.cpp">
</File>
<File
RelativePath=".\CIrrMeshFileLoader.h">
</File>
<File
RelativePath=".\CLMTSMeshFileLoader.cpp">
</File>