irrlicht/source/Irrlicht/CColladaFileLoader.cpp

2908 lines
80 KiB
C++

// Copyright (C) 2002-2009 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_COLLADA_LOADER_
#include "CColladaFileLoader.h"
#include "os.h"
#include "IXMLReader.h"
#include "IDummyTransformationSceneNode.h"
#include "SAnimatedMesh.h"
#include "fast_atof.h"
#include "quaternion.h"
#include "ILightSceneNode.h"
#include "ICameraSceneNode.h"
#include "IMeshManipulator.h"
#include "IReadFile.h"
#include "IAttributes.h"
#include "IMeshCache.h"
#include "IMeshSceneNode.h"
#include "SMeshBufferLightMap.h"
#include "irrMap.h"
#ifdef _DEBUG
#define COLLADA_READER_DEBUG
#endif
namespace irr
{
namespace scene
{
namespace
{
// currently supported COLLADA tag names
const core::stringc colladaSectionName = "COLLADA";
const core::stringc librarySectionName = "library";
const core::stringc libraryNodesSectionName = "library_nodes";
const core::stringc libraryGeometriesSectionName = "library_geometries";
const core::stringc libraryMaterialsSectionName = "library_materials";
const core::stringc libraryImagesSectionName = "library_images";
const core::stringc libraryVisualScenesSectionName = "library_visual_scenes";
const core::stringc libraryCamerasSectionName = "library_cameras";
const core::stringc libraryLightsSectionName = "library_lights";
const core::stringc libraryEffectsSectionName = "library_effects";
const core::stringc assetSectionName = "asset";
const core::stringc sceneSectionName = "scene";
const core::stringc visualSceneSectionName = "visual_scene";
const core::stringc lightPrefabName = "light";
const core::stringc cameraPrefabName = "camera";
const core::stringc materialSectionName = "material";
const core::stringc geometrySectionName = "geometry";
const core::stringc imageSectionName = "image";
const core::stringc textureSectionName = "texture";
const core::stringc effectSectionName = "effect";
const core::stringc pointSectionName = "point";
const core::stringc directionalSectionName ="directional";
const core::stringc spotSectionName = "spot";
const core::stringc ambientSectionName = "ambient";
const core::stringc meshSectionName = "mesh";
const core::stringc sourceSectionName = "source";
const core::stringc arraySectionName = "array";
const core::stringc floatArraySectionName ="float_array";
const core::stringc intArraySectionName = "int_array";
const core::stringc techniqueCommonSectionName = "technique_common";
const core::stringc accessorSectionName = "accessor";
const core::stringc verticesSectionName = "vertices";
const core::stringc inputTagName = "input";
const core::stringc polylistSectionName = "polylist";
const core::stringc trianglesSectionName = "triangles";
const core::stringc polygonsSectionName = "polygons";
const core::stringc primitivesName = "p";
const core::stringc vcountName = "vcount";
const core::stringc upAxisNodeName = "up_axis";
const core::stringc nodeSectionName = "node";
const core::stringc lookatNodeName = "lookat";
const core::stringc matrixNodeName = "matrix";
const core::stringc perspectiveNodeName = "perspective";
const core::stringc rotateNodeName = "rotate";
const core::stringc scaleNodeName = "scale";
const core::stringc translateNodeName = "translate";
const core::stringc skewNodeName = "skew";
const core::stringc bboxNodeName = "boundingbox";
const core::stringc minNodeName = "min";
const core::stringc maxNodeName = "max";
const core::stringc instanceName = "instance";
const core::stringc instanceGeometryName = "instance_geometry";
const core::stringc instanceSceneName = "instance_visual_scene";
const core::stringc instanceEffectName = "instance_effect";
const core::stringc instanceMaterialName = "instance_material";
const core::stringc instanceLightName = "instance_light";
const core::stringc instanceNodeName = "instance_node";
const core::stringc bindMaterialName = "bind_material";
const core::stringc extraNodeName = "extra";
const core::stringc techniqueNodeName = "technique";
const core::stringc colorNodeName = "color";
const core::stringc floatNodeName = "float";
const core::stringc float2NodeName = "float2";
const core::stringc float3NodeName = "float3";
const core::stringc newParamName = "newparam";
const core::stringc paramTagName = "param";
const core::stringc initFromName = "init_from";
const core::stringc dataName = "data";
const core::stringc wrapsName = "wrap_s";
const core::stringc wraptName = "wrap_t";
const core::stringc minfilterName = "minfilter";
const core::stringc magfilterName = "magfilter";
const core::stringc mipfilterName = "mipfilter";
const core::stringc textureNodeName = "texture";
const core::stringc doubleSidedNodeName = "double_sided";
const core::stringc constantAttenuationNodeName = "constant_attenuation";
const core::stringc linearAttenuationNodeName = "linear_attenuation";
const core::stringc quadraticAttenuationNodeName = "quadratic_attenuation";
const core::stringc falloffAngleNodeName = "falloff_angle";
const core::stringc falloffExponentNodeName = "falloff_exponent";
const core::stringc profileCOMMONSectionName = "profile_COMMON";
const core::stringc profileCOMMONAttributeName = "COMMON";
const char* const inputSemanticNames[] = {"POSITION", "VERTEX", "NORMAL", "TEXCOORD",
"UV", "TANGENT", "IMAGE", "TEXTURE", 0};
}
//! following class is for holding and creating instances of library
//! objects, named prefabs in this loader.
class CPrefab : public IColladaPrefab
{
public:
CPrefab(const core::stringc& id) : Id(id)
{
}
//! creates an instance of this prefab
virtual scene::ISceneNode* addInstance(scene::ISceneNode* parent,
scene::ISceneManager* mgr)
{
// empty implementation
return 0;
}
//! returns id of this prefab
virtual const core::stringc& getId()
{
return Id;
}
protected:
core::stringc Id;
};
//! prefab for a light scene node
class CLightPrefab : public CPrefab
{
public:
CLightPrefab(const core::stringc& id) : CPrefab(id)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA: loaded light prefab", Id.c_str());
#endif
}
video::SLight LightData; // publically accessible
//! creates an instance of this prefab
virtual scene::ISceneNode* addInstance(scene::ISceneNode* parent,
scene::ISceneManager* mgr)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA: Constructing light instance", Id.c_str());
#endif
scene::ILightSceneNode* l = mgr->addLightSceneNode(parent);
if (l)
{
l->setLightData ( LightData );
l->setName(getId());
}
return l;
}
};
//! prefab for a mesh scene node
class CGeometryPrefab : public CPrefab
{
public:
CGeometryPrefab(const core::stringc& id) : CPrefab(id)
{
}
scene::IMesh* Mesh;
//! creates an instance of this prefab
virtual scene::ISceneNode* addInstance(scene::ISceneNode* parent,
scene::ISceneManager* mgr)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA: Constructing mesh instance", Id.c_str());
#endif
scene::ISceneNode* m = mgr->addMeshSceneNode(Mesh, parent);
if (m)
{
m->setName(getId());
// m->setMaterialFlag(video::EMF_BACK_FACE_CULLING, false);
// m->setDebugDataVisible(scene::EDS_FULL);
}
return m;
}
};
//! prefab for a camera scene node
class CCameraPrefab : public CPrefab
{
public:
CCameraPrefab(const core::stringc& id)
: CPrefab(id), YFov(core::PI / 2.5f), ZNear(1.0f), ZFar(3000.0f)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA: loaded camera prefab", Id.c_str());
#endif
}
// publicly accessible data
f32 YFov;
f32 ZNear;
f32 ZFar;
//! creates an instance of this prefab
virtual scene::ISceneNode* addInstance(scene::ISceneNode* parent,
scene::ISceneManager* mgr)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA: Constructing camera instance", Id.c_str());
#endif
scene::ICameraSceneNode* c = mgr->addCameraSceneNode(parent);
if (c)
{
c->setFOV(YFov);
c->setNearValue(ZNear);
c->setFarValue(ZFar);
c->setName(getId());
}
return c;
}
};
//! prefab for a container scene node
//! Collects other prefabs and instantiates them upon instantiation
//! Uses a dummy scene node to return the childs as one scene node
class CScenePrefab : public CPrefab
{
public:
CScenePrefab(const core::stringc& id) : CPrefab(id)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA: loaded scene prefab", Id.c_str());
#endif
}
//! creates an instance of this prefab
virtual scene::ISceneNode* addInstance(scene::ISceneNode* parent,
scene::ISceneManager* mgr)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA: Constructing scene instance", Id.c_str());
#endif
if (Childs.size()==0)
return 0;
scene::IDummyTransformationSceneNode* s = mgr->addDummyTransformationSceneNode(parent);
if (s)
{
s->setName(getId());
s->getRelativeTransformationMatrix() = Transformation;
core::stringc t;
for (u32 i=0; i<16; ++i)
{
t+=core::stringc(Transformation[i]);
t+=" ";
}
os::Printer::log("COLLADA: Transformation", t.c_str());
for (u32 i=0; i<Childs.size(); ++i)
Childs[i]->addInstance(s, mgr);
}
return s;
}
core::array<IColladaPrefab*> Childs;
core::matrix4 Transformation;
};
//! Constructor
CColladaFileLoader::CColladaFileLoader(scene::ISceneManager* smgr,
io::IFileSystem* fs)
: SceneManager(smgr), FileSystem(fs), DummyMesh(0),
FirstLoadedMesh(0), LoadedMeshCount(0), CreateInstances(false)
{
#ifdef _DEBUG
setDebugName("CColladaFileLoader");
#endif
}
//! destructor
CColladaFileLoader::~CColladaFileLoader()
{
if (DummyMesh)
DummyMesh->drop();
if (FirstLoadedMesh)
FirstLoadedMesh->drop();
}
//! 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 CColladaFileLoader::isALoadableFileExtension(const io::path& filename) const
{
return core::hasFileExtension ( filename, "xml", "dae" );
}
//! 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* CColladaFileLoader::createMesh(io::IReadFile* file)
{
io::IXMLReaderUTF8* reader = FileSystem->createXMLReaderUTF8(file);
if (!reader)
return 0;
CurrentlyLoadingMesh = file->getFileName();
CreateInstances = SceneManager->getParameters()->getAttributeAsBool(
scene::COLLADA_CREATE_SCENE_INSTANCES);
Version = 0;
FlipAxis = false;
// read until COLLADA section, skip other parts
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (colladaSectionName == reader->getNodeName())
readColladaSection(reader);
else
skipSection(reader, true); // unknown section
}
}
reader->drop();
if (!Version)
return 0;
// because this loader loads and creates a complete scene instead of
// a single mesh, return an empty dummy mesh to make the scene manager
// know that everything went well.
if (!DummyMesh)
DummyMesh = new SAnimatedMesh();
scene::IAnimatedMesh* returnMesh = DummyMesh;
if (Version < 10400)
instantiateNode(SceneManager->getRootSceneNode());
// add the first loaded mesh into the mesh cache too, if more than one
// meshes have been loaded from the file
if (LoadedMeshCount>1 && FirstLoadedMesh)
{
os::Printer::log("Added COLLADA mesh", FirstLoadedMeshName.c_str());
SceneManager->getMeshCache()->addMesh(FirstLoadedMeshName.c_str(), FirstLoadedMesh);
}
// clean up temporary loaded data
clearData();
returnMesh->grab(); // store until this loader is destroyed
DummyMesh->drop();
DummyMesh = 0;
if (FirstLoadedMesh)
FirstLoadedMesh->drop();
FirstLoadedMesh = 0;
LoadedMeshCount = 0;
return returnMesh;
}
//! skips an (unknown) section in the collada document
void CColladaFileLoader::skipSection(io::IXMLReaderUTF8* reader, bool reportSkipping)
{
#ifndef COLLADA_READER_DEBUG
if (reportSkipping) // always report in COLLADA_READER_DEBUG mode
#endif
os::Printer::log("COLLADA skipping section", core::stringc(reader->getNodeName()).c_str());
// 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 COLLADA_READER_DEBUG
if (reportSkipping)
os::Printer::log("Skipping COLLADA unknown element", core::stringc(reader->getNodeName()).c_str());
#endif // COLLADA_READER_DEBUG
++tagCounter;
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
--tagCounter;
}
}
//! reads the <COLLADA> section and its content
void CColladaFileLoader::readColladaSection(io::IXMLReaderUTF8* reader)
{
if (reader->isEmptyElement())
return;
// todo: patch level needs to be handled
const f32 version = core::fast_atof(core::stringc(reader->getAttributeValue("version")).c_str());
Version = core::floor32(version)*10000+core::round32(core::fract(version)*1000.0f);
// Version 1.4 can be checked for by if (Version >= 10400)
while(reader->read())
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (assetSectionName == reader->getNodeName())
readAssetSection(reader);
else
if (librarySectionName == reader->getNodeName())
readLibrarySection(reader);
else
if (libraryNodesSectionName == reader->getNodeName())
readLibrarySection(reader);
else
if (libraryGeometriesSectionName == reader->getNodeName())
readLibrarySection(reader);
else
if (libraryMaterialsSectionName == reader->getNodeName())
readLibrarySection(reader);
else
if (libraryEffectsSectionName == reader->getNodeName())
readLibrarySection(reader);
else
if (libraryImagesSectionName == reader->getNodeName())
readLibrarySection(reader);
else
if (libraryCamerasSectionName == reader->getNodeName())
readLibrarySection(reader);
else
if (libraryLightsSectionName == reader->getNodeName())
readLibrarySection(reader);
else
if (libraryVisualScenesSectionName == reader->getNodeName())
readVisualSceneLibrary(reader);
else
if (assetSectionName == reader->getNodeName())
readAssetSection(reader);
else
if (sceneSectionName == reader->getNodeName())
readSceneSection(reader);
else
{
os::Printer::log("COLLADA loader warning: Wrong tag usage found", reader->getNodeName(), ELL_WARNING);
skipSection(reader, true); // unknown section
}
}
}
//! reads a <library> section and its content
void CColladaFileLoader::readLibrarySection(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading library");
#endif
if (reader->isEmptyElement())
return;
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
// animation section tbd
if (cameraPrefabName == reader->getNodeName())
readCameraPrefab(reader);
else
// code section tbd
// controller section tbd
if (geometrySectionName == reader->getNodeName())
readGeometry(reader);
else
if (imageSectionName == reader->getNodeName())
readImage(reader);
else
if (lightPrefabName == reader->getNodeName())
readLightPrefab(reader);
else
if (materialSectionName == reader->getNodeName())
readMaterial(reader);
else
if (nodeSectionName == reader->getNodeName())
{
CScenePrefab p("");
readNodeSection(reader, SceneManager->getRootSceneNode(), &p);
}
else
if (effectSectionName == reader->getNodeName())
readEffect(reader);
else
// program section tbd
if (textureSectionName == reader->getNodeName())
readTexture(reader);
else
skipSection(reader, true); // unknown section, not all allowed supported yet
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (librarySectionName == reader->getNodeName())
break; // end reading.
if (libraryNodesSectionName == reader->getNodeName())
break; // end reading.
if (libraryGeometriesSectionName == reader->getNodeName())
break; // end reading.
if (libraryMaterialsSectionName == reader->getNodeName())
break; // end reading.
if (libraryEffectsSectionName == reader->getNodeName())
break; // end reading.
if (libraryImagesSectionName == reader->getNodeName())
break; // end reading.
if (libraryLightsSectionName == reader->getNodeName())
break; // end reading.
if (libraryCamerasSectionName == reader->getNodeName())
break; // end reading.
}
}
}
//! reads a <visual_scene> element and stores it as a prefab
void CColladaFileLoader::readVisualSceneLibrary(io::IXMLReaderUTF8* reader)
{
CScenePrefab* p = 0;
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (visualSceneSectionName == reader->getNodeName())
p = new CScenePrefab(readId(reader));
else
if (p && nodeSectionName == reader->getNodeName()) // as a child of visual_scene
readNodeSection(reader, SceneManager->getRootSceneNode(), p);
else
if (assetSectionName == reader->getNodeName())
readAssetSection(reader);
else
if (extraNodeName == reader->getNodeName())
skipSection(reader, false); // ignore all other sections
else
{
os::Printer::log("COLLADA loader warning: Wrong tag usage found", reader->getNodeName(), ELL_WARNING);
skipSection(reader, true); // ignore all other sections
}
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (libraryVisualScenesSectionName == reader->getNodeName())
return;
else
if ((visualSceneSectionName == reader->getNodeName()) && p)
{
Prefabs.push_back(p);
p = 0;
}
}
}
}
//! reads a <scene> section and its content
void CColladaFileLoader::readSceneSection(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading scene");
#endif
if (reader->isEmptyElement())
return;
// read the scene
core::matrix4 transform; // transformation of this node
core::aabbox3df bbox;
scene::IDummyTransformationSceneNode* node = 0;
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (lookatNodeName == reader->getNodeName())
transform *= readLookAtNode(reader);
else
if (matrixNodeName == reader->getNodeName())
transform *= readMatrixNode(reader);
else
if (perspectiveNodeName == reader->getNodeName())
transform *= readPerspectiveNode(reader);
else
if (rotateNodeName == reader->getNodeName())
transform *= readRotateNode(reader);
else
if (scaleNodeName == reader->getNodeName())
transform *= readScaleNode(reader);
else
if (skewNodeName == reader->getNodeName())
transform *= readSkewNode(reader);
else
if (translateNodeName == reader->getNodeName())
transform *= readTranslateNode(reader);
else
if (bboxNodeName == reader->getNodeName())
readBboxNode(reader, bbox);
else
if (nodeSectionName == reader->getNodeName())
{
// create dummy node if there is none yet.
if (!node)
node = SceneManager->addDummyTransformationSceneNode(SceneManager->getRootSceneNode());
readNodeSection(reader, node);
}
else
if ((instanceSceneName == reader->getNodeName()))
readInstanceNode(reader, SceneManager->getRootSceneNode(), 0, 0,instanceSceneName);
else
if (extraNodeName == reader->getNodeName())
skipSection(reader, false);
else
{
os::Printer::log("COLLADA loader warning: Wrong tag usage found", reader->getNodeName(), ELL_WARNING);
skipSection(reader, true); // ignore all other sections
}
}
else
if ((reader->getNodeType() == io::EXN_ELEMENT_END) &&
(sceneSectionName == reader->getNodeName()))
return;
}
if (node)
node->getRelativeTransformationMatrix() = transform;
}
//! reads a <asset> section and its content
void CColladaFileLoader::readAssetSection(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading asset");
#endif
if (reader->isEmptyElement())
return;
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (upAxisNodeName == reader->getNodeName())
{
reader->read();
FlipAxis = (core::stringc("Z_UP") == reader->getNodeData());
}
}
else
if ((reader->getNodeType() == io::EXN_ELEMENT_END) &&
(assetSectionName == reader->getNodeName()))
return;
}
}
//! reads a <node> section and its content
void CColladaFileLoader::readNodeSection(io::IXMLReaderUTF8* reader, scene::ISceneNode* parent, CScenePrefab* p)
{
if (reader->isEmptyElement())
{
return;
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading empty node");
#endif
}
core::stringc name = readId(reader);
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading node", name);
#endif
core::matrix4 transform; // transformation of this node
core::aabbox3df bbox;
scene::ISceneNode* node = 0; // instance
CScenePrefab* nodeprefab = 0; // prefab for library_nodes usage
if (p)
{
nodeprefab = new CScenePrefab(readId(reader));
p->Childs.push_back(nodeprefab);
Prefabs.push_back(nodeprefab); // in order to delete them later on
}
// read the node
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (assetSectionName == reader->getNodeName())
readAssetSection(reader);
else
if (lookatNodeName == reader->getNodeName())
transform *= readLookAtNode(reader);
else
if (matrixNodeName == reader->getNodeName())
transform *= readMatrixNode(reader);
else
if (perspectiveNodeName == reader->getNodeName())
transform *= readPerspectiveNode(reader);
else
if (rotateNodeName == reader->getNodeName())
transform *= readRotateNode(reader);
else
if (scaleNodeName == reader->getNodeName())
transform *= readScaleNode(reader);
else
if (skewNodeName == reader->getNodeName())
transform *= readSkewNode(reader);
else
if (translateNodeName == reader->getNodeName())
transform *= readTranslateNode(reader);
else
if (bboxNodeName == reader->getNodeName())
readBboxNode(reader, bbox);
else
if ((instanceName == reader->getNodeName()) ||
(instanceNodeName == reader->getNodeName()) ||
(instanceGeometryName == reader->getNodeName()) ||
(instanceLightName == reader->getNodeName()))
{
scene::ISceneNode* newnode = 0;
readInstanceNode(reader, parent, &newnode, nodeprefab, reader->getNodeName());
if (node && newnode)
{
// move children from dummy to new node
ISceneNodeList::ConstIterator it = node->getChildren().begin();
for (; it != node->getChildren().end(); it = node->getChildren().begin())
(*it)->setParent(newnode);
// remove previous dummy node
node->remove();
node = newnode;
}
}
else
if (nodeSectionName == reader->getNodeName())
{
// create dummy node if there is none yet.
if (CreateInstances && !node)
{
scene::IDummyTransformationSceneNode* dummy =
SceneManager->addDummyTransformationSceneNode(parent);
dummy->getRelativeTransformationMatrix() = transform;
node = dummy;
}
else
node = parent;
// read and add child
readNodeSection(reader, node, nodeprefab);
}
else
if (extraNodeName == reader->getNodeName())
skipSection(reader, false);
else
skipSection(reader, true); // ignore all other sections
} // end if node
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (nodeSectionName == reader->getNodeName())
break;
}
}
if (nodeprefab)
nodeprefab->Transformation = transform;
else
if (node)
{
// set transformation correctly into node.
node->setPosition(transform.getTranslation());
node->setRotation(transform.getRotationDegrees());
node->setScale(transform.getScale());
node->updateAbsolutePosition();
node->setName(name);
}
}
//! reads a <lookat> element and its content and creates a matrix from it
core::matrix4 CColladaFileLoader::readLookAtNode(io::IXMLReaderUTF8* reader)
{
core::matrix4 mat;
if (reader->isEmptyElement())
return mat;
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading look at node");
#endif
f32 floats[9];
readFloatsInsideElement(reader, floats, 9);
mat.buildCameraLookAtMatrixLH(
core::vector3df(floats[0], floats[1], floats[2]),
core::vector3df(floats[3], floats[4], floats[5]),
core::vector3df(floats[6], floats[7], floats[8]));
return mat;
}
//! reads a <skew> element and its content and creates a matrix from it
core::matrix4 CColladaFileLoader::readSkewNode(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading skew node");
#endif
core::matrix4 mat;
if (reader->isEmptyElement())
return mat;
f32 floats[7]; // angle rotation-axis translation-axis
readFloatsInsideElement(reader, floats, 7);
// build skew matrix from these 7 floats
core::quaternion q;
q.fromAngleAxis(floats[0]*core::DEGTORAD, core::vector3df(floats[1], floats[2], floats[3]));
mat = q.getMatrix();
if (floats[4]==1.f) // along x-axis
{
mat[4]=0.f;
mat[6]=0.f;
mat[8]=0.f;
mat[9]=0.f;
}
else
if (floats[5]==1.f) // along y-axis
{
mat[1]=0.f;
mat[2]=0.f;
mat[8]=0.f;
mat[9]=0.f;
}
else
if (floats[6]==1.f) // along z-axis
{
mat[1]=0.f;
mat[2]=0.f;
mat[4]=0.f;
mat[6]=0.f;
}
return mat;
}
//! reads a <boundingbox> element and its content and stores it in bbox
void CColladaFileLoader::readBboxNode(io::IXMLReaderUTF8* reader,
core::aabbox3df& bbox)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading boundingbox node");
#endif
bbox.reset(core::aabbox3df());
if (reader->isEmptyElement())
return;
f32 floats[3];
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (minNodeName == reader->getNodeName())
{
readFloatsInsideElement(reader, floats, 3);
bbox.MinEdge.set(floats[0], floats[1], floats[2]);
}
else
if (maxNodeName == reader->getNodeName())
{
readFloatsInsideElement(reader, floats, 3);
bbox.MaxEdge.set(floats[0], floats[1], floats[2]);
}
else
skipSection(reader, true); // ignore all other sections
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (bboxNodeName == reader->getNodeName())
break;
}
}
}
//! reads a <matrix> element and its content and creates a matrix from it
core::matrix4 CColladaFileLoader::readMatrixNode(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading matrix node");
#endif
core::matrix4 mat;
if (reader->isEmptyElement())
return mat;
readFloatsInsideElement(reader, mat.pointer(), 16);
// put translation into the correct place
if (FlipAxis)
{
core::matrix4 mat2(mat, core::matrix4::EM4CONST_TRANSPOSED);
mat2[1]=mat[8];
mat2[2]=mat[4];
mat2[4]=mat[2];
mat2[5]=mat[10];
mat2[6]=mat[6];
mat2[8]=mat[1];
mat2[9]=mat[9];
mat2[10]=mat[5];
mat2[12]=mat[3];
mat2[13]=mat[11];
mat2[14]=mat[7];
return mat2;
}
else
return core::matrix4(mat, core::matrix4::EM4CONST_TRANSPOSED);
}
//! reads a <perspective> element and its content and creates a matrix from it
core::matrix4 CColladaFileLoader::readPerspectiveNode(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading perspective node");
#endif
core::matrix4 mat;
if (reader->isEmptyElement())
return mat;
f32 floats[1];
readFloatsInsideElement(reader, floats, 1);
// TODO: build perspecitve matrix from this float
os::Printer::log("COLLADA loader warning: <perspective> not implemented yet.", ELL_WARNING);
return mat;
}
//! reads a <rotate> element and its content and creates a matrix from it
core::matrix4 CColladaFileLoader::readRotateNode(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading rotate node");
#endif
core::matrix4 mat;
if (reader->isEmptyElement())
return mat;
f32 floats[4];
readFloatsInsideElement(reader, floats, 4);
if (!core::iszero(floats[3]))
{
core::quaternion q;
if (FlipAxis)
q.fromAngleAxis(floats[3]*core::DEGTORAD, core::vector3df(floats[0], floats[2], floats[1]));
else
q.fromAngleAxis(floats[3]*core::DEGTORAD, core::vector3df(floats[0], floats[1], floats[2]));
return q.getMatrix();
}
else
return core::IdentityMatrix;
}
//! reads a <scale> element and its content and creates a matrix from it
core::matrix4 CColladaFileLoader::readScaleNode(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading scale node");
#endif
core::matrix4 mat;
if (reader->isEmptyElement())
return mat;
f32 floats[3];
readFloatsInsideElement(reader, floats, 3);
if (FlipAxis)
mat.setScale(core::vector3df(floats[0], floats[2], floats[1]));
else
mat.setScale(core::vector3df(floats[0], floats[1], floats[2]));
return mat;
}
//! reads a <translate> element and its content and creates a matrix from it
core::matrix4 CColladaFileLoader::readTranslateNode(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading translate node");
#endif
core::matrix4 mat;
if (reader->isEmptyElement())
return mat;
f32 floats[3];
readFloatsInsideElement(reader, floats, 3);
if (FlipAxis)
mat.setTranslation(core::vector3df(floats[0], floats[2], floats[1]));
else
mat.setTranslation(core::vector3df(floats[0], floats[1], floats[2]));
return mat;
}
//! reads any kind of <instance*> node
void CColladaFileLoader::readInstanceNode(io::IXMLReaderUTF8* reader,
scene::ISceneNode* parent, scene::ISceneNode** outNode,
CScenePrefab* p, const core::stringc& type)
{
// find prefab of the specified id
core::stringc url = reader->getAttributeValue("url");
uriToId(url);
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading instance", url);
#endif
if (!reader->isEmptyElement())
{
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (bindMaterialName == reader->getNodeName())
readBindMaterialSection(reader,url);
else
if (extraNodeName == reader->getNodeName())
skipSection(reader, false);
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
break;
}
}
instantiateNode(parent, outNode, p, url, type);
}
void CColladaFileLoader::instantiateNode(scene::ISceneNode* parent,
scene::ISceneNode** outNode, CScenePrefab* p, const core::stringc& url,
const core::stringc& type)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA instantiate node");
#endif
for (u32 i=0; i<Prefabs.size(); ++i)
{
if (url == "" || url == Prefabs[i]->getId())
{
if (p)
p->Childs.push_back(Prefabs[i]);
else
if (CreateInstances)
{
scene::ISceneNode * newNode
= Prefabs[i]->addInstance(parent, SceneManager);
if (outNode)
{
*outNode = newNode;
if (*outNode)
(*outNode)->setName(url);
}
}
return;
}
}
if (p)
{
if (instanceGeometryName==type)
{
Prefabs.push_back(new CGeometryPrefab(url));
p->Childs.push_back(Prefabs.getLast());
}
}
}
//! reads a <camera> element and stores it as prefab
void CColladaFileLoader::readCameraPrefab(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading camera prefab");
#endif
CCameraPrefab* prefab = new CCameraPrefab(readId(reader));
if (!reader->isEmptyElement())
{
// read techniques optics and imager (the latter is completely ignored, though)
readColladaParameters(reader, cameraPrefabName);
SColladaParam* p;
// XFOV not yet supported
p = getColladaParameter(ECPN_YFOV);
if (p && p->Type == ECPT_FLOAT)
prefab->YFov = p->Floats[0];
p = getColladaParameter(ECPN_ZNEAR);
if (p && p->Type == ECPT_FLOAT)
prefab->ZNear = p->Floats[0];
p = getColladaParameter(ECPN_ZFAR);
if (p && p->Type == ECPT_FLOAT)
prefab->ZFar = p->Floats[0];
// orthographic camera uses LEFT, RIGHT, TOP, and BOTTOM
}
Prefabs.push_back(prefab);
}
//! reads a <image> element and stores it in the image section
void CColladaFileLoader::readImage(io::IXMLReaderUTF8* reader)
{
// add image to list of loaded images.
Images.push_back(SColladaImage());
SColladaImage& image=Images.getLast();
image.Id = readId(reader);
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading image", core::stringc(image.Id));
#endif
image.Dimension.Height = (u32)reader->getAttributeValueAsInt("height");
image.Dimension.Width = (u32)reader->getAttributeValueAsInt("width");
if (Version >= 10400) // start with 1.4
{
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (assetSectionName == reader->getNodeName())
skipSection(reader, false);
else
if (initFromName == reader->getNodeName())
{
reader->read();
image.Source = reader->getNodeData();
image.Source.trim();
image.SourceIsFilename=true;
}
else
if (dataName == reader->getNodeName())
{
reader->read();
image.Source = reader->getNodeData();
image.Source.trim();
image.SourceIsFilename=false;
}
else
if (extraNodeName == reader->getNodeName())
skipSection(reader, false);
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (initFromName == reader->getNodeName())
return;
}
}
}
else
{
image.Source = reader->getAttributeValue("source");
image.Source.trim();
image.SourceIsFilename=false;
}
}
//! reads a <texture> element and stores it in the texture section
void CColladaFileLoader::readTexture(io::IXMLReaderUTF8* reader)
{
// add texture to list of loaded textures.
Textures.push_back(SColladaTexture());
SColladaTexture& texture=Textures.getLast();
texture.Id = readId(reader);
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading texture", core::stringc(texture.Id));
#endif
if (!reader->isEmptyElement())
{
readColladaInputs(reader, textureSectionName);
SColladaInput* input = getColladaInput(ECIS_IMAGE);
if (input)
{
const core::stringc imageName = input->Source;
texture.Texture = getTextureFromImage(imageName);
}
}
}
//! reads a <material> element and stores it in the material section
void CColladaFileLoader::readMaterial(io::IXMLReaderUTF8* reader)
{
// add material to list of loaded materials.
Materials.push_back(SColladaMaterial());
SColladaMaterial& material = Materials.getLast();
material.Id = readId(reader);
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading material", core::stringc(material.Id));
#endif
if (Version >= 10400)
{
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT &&
instanceEffectName == reader->getNodeName())
{
material.InstanceEffectId = reader->getAttributeValue("url");
uriToId(material.InstanceEffectId);
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END &&
materialSectionName == reader->getNodeName())
{
break;
}
} // end while reader->read();
}
else
{
if (!reader->isEmptyElement())
{
readColladaInputs(reader, materialSectionName);
SColladaInput* input = getColladaInput(ECIS_TEXTURE);
if (input)
{
core::stringc textureName = input->Source;
uriToId(textureName);
for (u32 i=0; i<Textures.size(); ++i)
if (textureName == Textures[i].Id)
{
material.Mat.setTexture(0, Textures[i].Texture);
break;
}
}
//does not work because the wrong start node is chosen due to reading of inputs before
#if 0
readColladaParameters(reader, materialSectionName);
SColladaParam* p;
p = getColladaParameter(ECPN_AMBIENT);
if (p && p->Type == ECPT_FLOAT3)
material.Mat.AmbientColor = video::SColorf(p->Floats[0],p->Floats[1],p->Floats[2]).toSColor();
p = getColladaParameter(ECPN_DIFFUSE);
if (p && p->Type == ECPT_FLOAT3)
material.Mat.DiffuseColor = video::SColorf(p->Floats[0],p->Floats[1],p->Floats[2]).toSColor();
p = getColladaParameter(ECPN_SPECULAR);
if (p && p->Type == ECPT_FLOAT3)
material.Mat.DiffuseColor = video::SColorf(p->Floats[0],p->Floats[1],p->Floats[2]).toSColor();
p = getColladaParameter(ECPN_SHININESS);
if (p && p->Type == ECPT_FLOAT)
material.Mat.Shininess = p->Floats[0];
#endif
}
}
}
void CColladaFileLoader::readEffect(io::IXMLReaderUTF8* reader, SColladaEffect * effect)
{
static const core::stringc constantNode("constant");
static const core::stringc lambertNode("lambert");
static const core::stringc phongNode("phong");
static const core::stringc blinnNode("blinn");
static const core::stringc emissionNode("emission");
static const core::stringc ambientNode("ambient");
static const core::stringc diffuseNode("diffuse");
static const core::stringc specularNode("specular");
static const core::stringc shininessNode("shininess");
static const core::stringc reflectiveNode("reflective");
static const core::stringc reflectivityNode("reflectivity");
static const core::stringc transparentNode("transparent");
static const core::stringc transparencyNode("transparency");
static const core::stringc indexOfRefractionNode("index_of_refraction");
if (!effect)
{
Effects.push_back(SColladaEffect());
effect = &Effects.getLast();
effect->Id = readId(reader);
effect->Transparency = 1.f;
effect->Mat.Lighting=true;
effect->Mat.NormalizeNormals=true;
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading effect", core::stringc(effect->Id));
#endif
}
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
// first come the tags we descend, but ignore the top-levels
if (!reader->isEmptyElement() && ((profileCOMMONSectionName == reader->getNodeName()) ||
(techniqueNodeName == reader->getNodeName())))
readEffect(reader,effect);
else
if (newParamName == reader->getNodeName())
readParameter(reader);
else
// these are the actual materials inside technique
if (constantNode == reader->getNodeName() ||
lambertNode == reader->getNodeName() ||
phongNode == reader->getNodeName() ||
blinnNode == reader->getNodeName())
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading effect part", reader->getNodeName());
#endif
effect->Mat.setFlag(irr::video::EMF_GOURAUD_SHADING,
phongNode == reader->getNodeName() ||
blinnNode == reader->getNodeName());
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
const core::stringc node = reader->getNodeName();
if (emissionNode == node || ambientNode == node ||
diffuseNode == node || specularNode == node ||
reflectiveNode == node || transparentNode == node )
{
// color or texture types
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT &&
colorNodeName == reader->getNodeName())
{
const video::SColorf colorf = readColorNode(reader);
const video::SColor color = colorf.toSColor();
if (emissionNode == node)
effect->Mat.EmissiveColor = color;
else
if (ambientNode == node)
effect->Mat.AmbientColor = color;
else
if (diffuseNode == node)
effect->Mat.DiffuseColor = color;
else
if (specularNode == node)
effect->Mat.SpecularColor = color;
else
if (transparentNode == node)
effect->Transparency = colorf.getAlpha();
}
else
if (reader->getNodeType() == io::EXN_ELEMENT &&
textureNodeName == reader->getNodeName())
{
effect->Textures.push_back(reader->getAttributeValue("texture"));
break;
}
else
if (reader->getNodeType() == io::EXN_ELEMENT)
skipSection(reader, false);
else
if (reader->getNodeType() == io::EXN_ELEMENT_END &&
node == reader->getNodeName())
break;
}
}
else
if (shininessNode == node || reflectivityNode == node ||
transparencyNode == node || indexOfRefractionNode == node )
{
// float or param types
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT &&
floatNodeName == reader->getNodeName())
{
f32 f = readFloatNode(reader);
if (shininessNode == node)
effect->Mat.Shininess = f;
else
if (transparencyNode == node)
effect->Transparency *= f;
}
else
if (reader->getNodeType() == io::EXN_ELEMENT)
skipSection(reader, false);
else
if (reader->getNodeType() == io::EXN_ELEMENT_END &&
node == reader->getNodeName())
break;
}
}
else
skipSection(reader, true); // ignore all other nodes
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END && (
constantNode == reader->getNodeName() ||
lambertNode == reader->getNodeName() ||
phongNode == reader->getNodeName() ||
blinnNode == reader->getNodeName()
))
break;
}
}
else
if (!reader->isEmptyElement() && (extraNodeName == reader->getNodeName()))
readEffect(reader,effect);
else
if (doubleSidedNodeName == reader->getNodeName())
{
// read the GoogleEarth extra flag for double sided polys
s32 doubleSided = 0;
readIntsInsideElement(reader,&doubleSided,1);
if (doubleSided)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("Setting double sided flag for effect.");
#endif
effect->Mat.setFlag(irr::video::EMF_BACK_FACE_CULLING,false);
}
}
else
skipSection(reader, true); // ignore all other sections
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (effectSectionName == reader->getNodeName())
break;
else
if (profileCOMMONSectionName == reader->getNodeName())
break;
else
if (techniqueNodeName == reader->getNodeName())
break;
else
if (extraNodeName == reader->getNodeName())
break;
}
}
if (effect->Mat.AmbientColor == video::SColor(0) &&
effect->Mat.DiffuseColor != video::SColor(0))
effect->Mat.AmbientColor = effect->Mat.DiffuseColor;
if (effect->Mat.DiffuseColor == video::SColor(0) &&
effect->Mat.AmbientColor != video::SColor(0))
effect->Mat.DiffuseColor = effect->Mat.AmbientColor;
if ((effect->Transparency != 0.0f) && (effect->Transparency != 1.0f))
{
effect->Mat.MaterialType = irr::video::EMT_TRANSPARENT_VERTEX_ALPHA;
effect->Mat.ZWriteEnable = false;
}
effect->Mat.setFlag(video::EMF_TEXTURE_WRAP, !Parameters.getAttributeAsBool("wrap_s"));
effect->Mat.setFlag(video::EMF_BILINEAR_FILTER, Parameters.getAttributeAsBool("bilinear"));
effect->Mat.setFlag(video::EMF_TRILINEAR_FILTER, Parameters.getAttributeAsBool("trilinear"));
effect->Mat.setFlag(video::EMF_ANISOTROPIC_FILTER, Parameters.getAttributeAsBool("anisotropic"));
}
const SColladaMaterial* CColladaFileLoader::findMaterial(const core::stringc& materialName)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA find material", materialName);
#endif
// do a quick lookup in the materials
SColladaMaterial matToFind;
matToFind.Id = materialName;
s32 mat = Materials.binary_search(matToFind);
if (mat == -1)
return 0;
// instantiate the material effect if needed
if (Materials[mat].InstanceEffectId.size() != 0)
{
// do a quick lookup in the effects
SColladaEffect effectToFind;
effectToFind.Id = Materials[mat].InstanceEffectId;
s32 effect = Effects.binary_search(effectToFind);
if (effect != -1)
{
// found the effect, instantiate by copying into the material
Materials[mat].Mat = Effects[effect].Mat;
Materials[mat].Mat.setTexture(0, getTextureFromImage(Effects[effect].Textures[0]));
Materials[mat].Transparency = Effects[effect].Transparency;
// and indicate the material is instantiated by removing the effect ref
Materials[mat].InstanceEffectId = "";
}
else
return 0;
}
return &Materials[mat];
}
void CColladaFileLoader::readBindMaterialSection(io::IXMLReaderUTF8* reader, const core::stringc & id)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading bind material");
#endif
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (instanceMaterialName == reader->getNodeName())
{
// the symbol to retarget, and the target material
core::stringc meshbufferReference = reader->getAttributeValue("symbol");
if (meshbufferReference.size()==0)
continue;
core::stringc target = reader->getAttributeValue("target");
uriToId(target);
if (target.size()==0)
continue;
const SColladaMaterial * material = findMaterial(target);
if (!material)
continue;
// bind any pending materials for this node
meshbufferReference = id+"/"+meshbufferReference;
#ifdef COLLADA_READER_DEBUG
os::Printer::log((core::stringc("Material binding: ")+meshbufferReference+" "+target).c_str());
#endif
if (MaterialsToBind.find(meshbufferReference))
{
core::array<irr::scene::IMeshBuffer*> & toBind
= MeshesToBind[MaterialsToBind[meshbufferReference]];
#ifdef COLLADA_READER_DEBUG
os::Printer::log("Material binding now ",material->Id.c_str());
os::Printer::log("#meshbuffers",core::stringc(toBind.size()).c_str());
#endif
SMesh tmpmesh;
for (u32 i = 0; i < toBind.size(); ++i)
{
toBind[i]->getMaterial() = material->Mat;
tmpmesh.addMeshBuffer(toBind[i]);
if ((material->Transparency!=0.0f) && (material->Transparency!=1.0f))
{
toBind[i]->getMaterial().MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
toBind[i]->getMaterial().ZWriteEnable = false;
}
}
SceneManager->getMeshManipulator()->setVertexColors(&tmpmesh,material->Mat.DiffuseColor);
if ((material->Transparency!=0.0f) && (material->Transparency!=1.0f))
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA found transparency material", core::stringc(material->Transparency).c_str());
#endif
SceneManager->getMeshManipulator()->setVertexColorAlpha(&tmpmesh, core::floor32(material->Transparency*255.0f));
}
}
}
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END &&
bindMaterialName == reader->getNodeName())
break;
}
}
//! reads a <geometry> element and stores it as mesh if possible
void CColladaFileLoader::readGeometry(io::IXMLReaderUTF8* reader)
{
core::stringc id = readId(reader);
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading geometry", id);
#endif
SAnimatedMesh* amesh = new SAnimatedMesh();
scene::SMesh* mesh = new SMesh();
amesh->addMesh(mesh);
core::array<SSource> sources;
bool okToReadArray = false;
// handles geometry node and the mesh childs in this loop
// read sources with arrays and accessor for each mesh
if (!reader->isEmptyElement())
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
const char* nodeName = reader->getNodeName();
if (meshSectionName == nodeName)
{
// inside a mesh section. Don't have to do anything here.
}
else
if (sourceSectionName == nodeName)
{
// create a new source
sources.push_back(SSource());
sources.getLast().Id = readId(reader);
#ifdef COLLADA_READER_DEBUG
os::Printer::log("Reading source", sources.getLast().Id.c_str());
#endif
}
else
if (arraySectionName == nodeName || floatArraySectionName == nodeName || intArraySectionName == nodeName)
{
// create a new array and read it.
if (!sources.empty())
{
sources.getLast().Array.Name = readId(reader);
int count = reader->getAttributeValueAsInt("count");
sources.getLast().Array.Data.set_used(count); // pre allocate
// check if type of array is ok
const char* type = reader->getAttributeValue("type");
okToReadArray = (type && (!strcmp("float", type) || !strcmp("int", type))) || floatArraySectionName == nodeName || intArraySectionName == nodeName;
#ifdef COLLADA_READER_DEBUG
os::Printer::log("Read array", sources.getLast().Array.Name.c_str());
#endif
}
#ifdef COLLADA_READER_DEBUG
else
os::Printer::log("Warning, array outside source found",
readId(reader).c_str());
#endif
}
else
if (accessorSectionName == nodeName) // child of source (below a technique tag)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("Reading accessor");
#endif
SAccessor accessor;
accessor.Count = reader->getAttributeValueAsInt("count");
accessor.Offset = reader->getAttributeValueAsInt("offset");
accessor.Stride = reader->getAttributeValueAsInt("stride");
if (accessor.Stride == 0)
accessor.Stride = 1;
// the accessor contains some information on how to access (boi!) the array,
// the info is stored in collada style parameters, so just read them.
readColladaParameters(reader, accessorSectionName);
if (!sources.empty())
{
sources.getLast().Accessors.push_back(accessor);
sources.getLast().Accessors.getLast().Parameters = ColladaParameters;
}
}
else
if (verticesSectionName == nodeName)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("Reading vertices");
#endif
// read vertex input position source
readColladaInputs(reader, verticesSectionName);
}
else
// lines and linestrips missing
if (polygonsSectionName == nodeName ||
polylistSectionName == nodeName ||
trianglesSectionName == nodeName)
{
// read polygons section
readPolygonSection(reader, sources, mesh, id);
}
else
// trifans, and tristrips missing
if (doubleSidedNodeName == reader->getNodeName())
{
// read the extra flag for double sided polys
s32 doubleSided = 0;
readIntsInsideElement(reader,&doubleSided,1);
if (doubleSided)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("Setting double sided flag for mesh.");
#endif
amesh->setMaterialFlag(irr::video::EMF_BACK_FACE_CULLING,false);
}
}
else
// techniqueCommon or 'technique profile=common' must not be skipped
if ((techniqueCommonSectionName != nodeName) // Collada 1.2/1.3
&& (techniqueNodeName != nodeName) // Collada 1.4+
&& (extraNodeName != nodeName))
{
os::Printer::log("COLLADA loader warning: Wrong tag usage found in geometry", reader->getNodeName(), ELL_WARNING);
skipSection(reader, true); // ignore all other sections
}
} // end if node type is element
else
if (reader->getNodeType() == io::EXN_TEXT)
{
// read array data
if (okToReadArray && !sources.empty())
{
core::array<f32>& a = sources.getLast().Array.Data;
core::stringc data = reader->getNodeData();
data.trim();
const c8* p = &data[0];
for (u32 i=0; i<a.size(); ++i)
{
findNextNoneWhiteSpace(&p);
if (*p)
a[i] = readFloat(&p);
else
a[i] = 0.0f;
}
} // end reading array
okToReadArray = false;
} // end if node type is text
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (geometrySectionName == reader->getNodeName())
{
// end of geometry section reached, cancel out
break;
}
}
} // end while reader->read();
// add mesh as geometry
mesh->recalculateBoundingBox();
amesh->recalculateBoundingBox();
// create virtual file name
io::path filename = CurrentlyLoadingMesh;
filename += '#';
filename += id;
// add to scene manager
if (LoadedMeshCount)
{
SceneManager->getMeshCache()->addMesh(filename.c_str(), amesh);
os::Printer::log("Added COLLADA mesh", filename.c_str());
}
else
{
FirstLoadedMeshName = filename;
FirstLoadedMesh = amesh;
FirstLoadedMesh->grab();
}
++LoadedMeshCount;
mesh->drop();
amesh->drop();
// create geometry prefab
u32 i;
for (i=0; i<Prefabs.size(); ++i)
{
if (Prefabs[i]->getId()==id)
{
((CGeometryPrefab*)Prefabs[i])->Mesh=mesh;
break;
}
}
if (i==Prefabs.size())
{
CGeometryPrefab* prefab = new CGeometryPrefab(id);
prefab->Mesh = mesh;
Prefabs.push_back(prefab);
}
// store as dummy mesh if no instances will be created
if (!CreateInstances && !DummyMesh)
{
DummyMesh = amesh;
DummyMesh->grab();
}
}
struct SPolygon
{
core::array<s32> Indices;
};
//! reads a polygons section and creates a mesh from it
void CColladaFileLoader::readPolygonSection(io::IXMLReaderUTF8* reader,
core::array<SSource>& sources, scene::SMesh* mesh,
const core::stringc& geometryId)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading polygon section");
#endif
core::stringc materialName = reader->getAttributeValue("material");
core::stringc polygonType = reader->getNodeName();
const int polygonCount = reader->getAttributeValueAsInt("count"); // Not useful because it only determines the number of primitives, which have arbitrary vertices in case of polygon
core::array<SPolygon> polygons;
if (polygonType == polygonsSectionName)
polygons.reallocate(polygonCount);
core::array<int> vCounts;
bool parsePolygonOK = false;
bool parseVcountOK = false;
u32 inputSemanticCount = 0;
bool unresolvedInput=false;
u32 maxOffset = 0;
core::array<SColladaInput> localInputs;
// read all <input> and primitives
if (!reader->isEmptyElement())
while(reader->read())
{
const char* nodeName = reader->getNodeName();
if (reader->getNodeType() == io::EXN_ELEMENT)
{
// polygon node may contain params
if (inputTagName == nodeName)
{
// read input tag
readColladaInput(reader, localInputs);
// resolve input source
SColladaInput& inp = localInputs.getLast();
// get input source array id, if it is a vertex input, take
// the <vertex><input>-source attribute.
if (inp.Semantic == ECIS_VERTEX)
{
inp.Source = Inputs[0].Source;
for (u32 i=1; i<Inputs.size(); ++i)
{
localInputs.push_back(Inputs[i]);
uriToId(localInputs.getLast().Source);
maxOffset = core::max_(maxOffset,localInputs.getLast().Offset);
++inputSemanticCount;
}
}
uriToId(inp.Source);
maxOffset = core::max_(maxOffset,inp.Offset);
++inputSemanticCount;
}
else
if (primitivesName == nodeName)
{
parsePolygonOK = true;
polygons.push_back(SPolygon());
}
else
if (vcountName == nodeName)
{
parseVcountOK = true;
} // end is polygon node
} // end is element node
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (primitivesName == nodeName)
parsePolygonOK = false; // end parsing a polygon
else
if (vcountName == nodeName)
parseVcountOK = false; // end parsing vcounts
else
if (polygonType == nodeName)
break; // cancel out and create mesh
} // end is element end
else
if (reader->getNodeType() == io::EXN_TEXT)
{
if (parseVcountOK)
{
core::stringc data = reader->getNodeData();
data.trim();
const c8* p = &data[0];
while(*p)
{
findNextNoneWhiteSpace(&p);
if (*p)
vCounts.push_back(readInt(&p));
}
parseVcountOK = false;
}
else
if (parsePolygonOK && polygons.size())
{
core::stringc data = reader->getNodeData();
data.trim();
const c8* p = &data[0];
SPolygon& poly = polygons.getLast();
if (polygonType == polygonsSectionName)
poly.Indices.reallocate((maxOffset+1)*3);
else
poly.Indices.reallocate(polygonCount*(maxOffset+1)*3);
if (vCounts.empty())
{
while(*p)
{
findNextNoneWhiteSpace(&p);
poly.Indices.push_back(readInt(&p));
}
}
else
{
for (u32 i = 0; i < vCounts.size(); i++)
{
const int polyVCount = vCounts[i];
core::array<int> polyCorners;
for (u32 j = 0; j < polyVCount * inputSemanticCount; j++)
{
if (!*p)
break;
findNextNoneWhiteSpace(&p);
polyCorners.push_back(readInt(&p));
}
while (polyCorners.size() >= 3 * inputSemanticCount)
{
// add one triangle's worth of indices
for (u32 k = 0; k < inputSemanticCount * 3; ++k)
{
poly.Indices.push_back(polyCorners[k]);
}
// remove one corner from our poly
polyCorners.erase(inputSemanticCount,inputSemanticCount);
}
polyCorners.clear();
}
vCounts.clear();
}
parsePolygonOK = false;
}
}
} // end while reader->read()
// find source array (we'll ignore accessors for this implementation)
for (u32 i=0; i<localInputs.size(); ++i)
{
SColladaInput& inp = localInputs[i];
u32 s;
for (s=0; s<sources.size(); ++s)
{
if (sources[s].Id == inp.Source)
{
// slot found
inp.Data = sources[s].Array.Data.pointer();
inp.Stride = sources[s].Accessors[0].Stride;
break;
}
}
if (s == sources.size())
{
os::Printer::log("COLLADA Warning, polygon input source not found",
inp.Source.c_str());
inp.Semantic=ECIS_COUNT; // for unknown
unresolvedInput=true;
}
else
{
#ifdef COLLADA_READER_DEBUG
// print slot
core::stringc tmp = "Added slot ";
tmp += inputSemanticNames[inp.Semantic];
tmp += " sourceArray:";
tmp += inp.Source;
os::Printer::log(tmp.c_str());
#endif
}
}
if ((inputSemanticCount == 0) || !polygons.size())
return; // cancel if there are no polygons anyway.
// analyze content of Inputs to create a fitting mesh buffer
u32 u;
u32 textureCoordSetCount = 0;
bool normalSlotCount = false;
u32 secondTexCoordSetIndex = 0xFFFFFFFF;
for (u=0; u<Inputs.size(); ++u)
{
if (Inputs[u].Semantic == ECIS_TEXCOORD || Inputs[u].Semantic == ECIS_UV )
{
++textureCoordSetCount;
if (textureCoordSetCount==2)
secondTexCoordSetIndex = u;
}
else
if (Inputs[u].Semantic == ECIS_NORMAL)
normalSlotCount=true;
}
// if there is more than one texture coordinate set, create a lightmap mesh buffer,
// otherwise use a standard mesh buffer
scene::IMeshBuffer* buffer = 0;
++maxOffset; // +1 to jump to the next value
if ( textureCoordSetCount < 2 )
{
// standard mesh buffer
scene::SMeshBuffer* mbuffer = new SMeshBuffer();
buffer = mbuffer;
core::map<video::S3DVertex, int> vertMap;
for (u32 i=0; i<polygons.size(); ++i)
{
core::array<u16> indices;
const u32 vertexCount = polygons[i].Indices.size() / maxOffset;
mbuffer->Vertices.reallocate(mbuffer->Vertices.size()+vertexCount);
// for all index/semantic groups
for (u32 v=0; v<polygons[i].Indices.size(); v+=maxOffset)
{
video::S3DVertex vtx;
vtx.Color.set(255,255,255,255);
// for all input semantics
for (u32 k=0; k<localInputs.size(); ++k)
{
if (!localInputs[k].Data)
continue;
// build vertex from input semantics.
const u32 idx = localInputs[k].Stride*polygons[i].Indices[v+localInputs[k].Offset];
switch(localInputs[k].Semantic)
{
case ECIS_POSITION:
case ECIS_VERTEX:
vtx.Pos.X = localInputs[k].Data[idx+0];
if (FlipAxis)
{
vtx.Pos.Z = localInputs[k].Data[idx+1];
vtx.Pos.Y = localInputs[k].Data[idx+2];
}
else
{
vtx.Pos.Y = localInputs[k].Data[idx+1];
vtx.Pos.Z = localInputs[k].Data[idx+2];
}
break;
case ECIS_NORMAL:
vtx.Normal.X = localInputs[k].Data[idx+0];
if (FlipAxis)
{
vtx.Normal.Z = localInputs[k].Data[idx+1];
vtx.Normal.Y = localInputs[k].Data[idx+2];
}
else
{
vtx.Normal.Y = localInputs[k].Data[idx+1];
vtx.Normal.Z = localInputs[k].Data[idx+2];
}
break;
case ECIS_TEXCOORD:
case ECIS_UV:
vtx.TCoords.X = localInputs[k].Data[idx+0];
vtx.TCoords.Y = 1-localInputs[k].Data[idx+1];
break;
case ECIS_TANGENT:
break;
default:
break;
}
}
//first, try to find this vertex in the mesh
core::map<video::S3DVertex, int>::Node* n = vertMap.find(vtx);
if (n)
{
indices.push_back(n->getValue());
}
else
{
indices.push_back(mbuffer->getVertexCount());
mbuffer->Vertices.push_back(vtx);
vertMap.insert(vtx, mbuffer->getVertexCount()-1);
}
} // end for all vertices
if (polygonsSectionName == polygonType &&
indices.size() > 3)
{
// need to tesselate for polygons of 4 or more vertices
// for now we naively turn interpret it as a triangle fan
// as full tesselation is problematic
if (FlipAxis)
{
for (u32 ind = indices.size()-3; ind>0 ; --ind)
{
mbuffer->Indices.push_back(indices[0]);
mbuffer->Indices.push_back(indices[ind+2]);
mbuffer->Indices.push_back(indices[ind+1]);
}
}
else
{
for (u32 ind = 0; i+2 < indices.size(); ++ind)
{
mbuffer->Indices.push_back(indices[0]);
mbuffer->Indices.push_back(indices[ind+1]);
mbuffer->Indices.push_back(indices[ind+2]);
}
}
}
else
{
// it's just triangles
for (u32 ind = 0; ind < indices.size(); ind+=3)
{
if (FlipAxis)
{
mbuffer->Indices.push_back(indices[ind+2]);
mbuffer->Indices.push_back(indices[ind+1]);
mbuffer->Indices.push_back(indices[ind+0]);
}
else
{
mbuffer->Indices.push_back(indices[ind+0]);
mbuffer->Indices.push_back(indices[ind+1]);
mbuffer->Indices.push_back(indices[ind+2]);
}
}
}
} // end for all polygons
}
else
{
// lightmap mesh buffer
scene::SMeshBufferLightMap* mbuffer = new SMeshBufferLightMap();
buffer = mbuffer;
for (u32 i=0; i<polygons.size(); ++i)
{
const u32 vertexCount = polygons[i].Indices.size() / maxOffset;
mbuffer->Vertices.reallocate(mbuffer->Vertices.size()+vertexCount);
// for all vertices in array
for (u32 v=0; v<polygons[i].Indices.size(); v+=maxOffset)
{
video::S3DVertex2TCoords vtx;
vtx.Color.set(100,255,255,255);
// for all input semantics
for (u32 k=0; k<Inputs.size(); ++k)
{
// build vertex from input semantics.
const u32 idx = localInputs[k].Stride*polygons[i].Indices[v+Inputs[k].Offset];
switch(localInputs[k].Semantic)
{
case ECIS_POSITION:
case ECIS_VERTEX:
vtx.Pos.X = localInputs[k].Data[idx+0];
if (FlipAxis)
{
vtx.Pos.Z = localInputs[k].Data[idx+1];
vtx.Pos.Y = localInputs[k].Data[idx+2];
}
else
{
vtx.Pos.Y = localInputs[k].Data[idx+1];
vtx.Pos.Z = localInputs[k].Data[idx+2];
}
break;
case ECIS_NORMAL:
vtx.Normal.X = localInputs[k].Data[idx+0];
if (FlipAxis)
{
vtx.Normal.Z = localInputs[k].Data[idx+1];
vtx.Normal.Y = localInputs[k].Data[idx+2];
}
else
{
vtx.Normal.Y = localInputs[k].Data[idx+1];
vtx.Normal.Z = localInputs[k].Data[idx+2];
}
break;
case ECIS_TEXCOORD:
case ECIS_UV:
if (k==secondTexCoordSetIndex)
{
vtx.TCoords2.X = localInputs[k].Data[idx+0];
vtx.TCoords2.Y = 1-localInputs[k].Data[idx+1];
}
else
{
vtx.TCoords.X = localInputs[k].Data[idx+0];
vtx.TCoords.Y = 1-localInputs[k].Data[idx+1];
}
break;
case ECIS_TANGENT:
break;
default:
break;
}
}
mbuffer->Vertices.push_back(vtx);
} // end for all vertices
// add vertex indices
const u32 oldVertexCount = mbuffer->Vertices.size() - vertexCount;
for (u32 face=0; face<vertexCount-2; ++face)
{
mbuffer->Indices.push_back(oldVertexCount + 0);
mbuffer->Indices.push_back(oldVertexCount + 1 + face);
mbuffer->Indices.push_back(oldVertexCount + 2 + face);
}
} // end for all polygons
}
const SColladaMaterial* m = findMaterial(materialName);
if (m)
{
buffer->getMaterial() = m->Mat;
SMesh tmpmesh;
tmpmesh.addMeshBuffer(buffer);
SceneManager->getMeshManipulator()->setVertexColors(&tmpmesh,m->Mat.DiffuseColor);
if (m->Transparency != 1.0f)
SceneManager->getMeshManipulator()->setVertexColorAlpha(&tmpmesh,core::floor32(m->Transparency*255.0f));
}
// add future bind reference for the material
core::stringc meshbufferReference = geometryId+"/"+materialName;
if (!MaterialsToBind.find(meshbufferReference))
{
MaterialsToBind[meshbufferReference] = MeshesToBind.size();
MeshesToBind.push_back(core::array<irr::scene::IMeshBuffer*>());
}
MeshesToBind[MaterialsToBind[meshbufferReference]].push_back(buffer);
// calculate normals if there is no slot for it
if (!normalSlotCount)
SceneManager->getMeshManipulator()->recalculateNormals(buffer, true);
// recalculate bounding box
buffer->recalculateBoundingBox();
// add mesh buffer
mesh->addMeshBuffer(buffer);
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA added meshbuffer", core::stringc(buffer->getVertexCount())+" vertices, "+core::stringc(buffer->getIndexCount())+" indices.");
#endif
buffer->drop();
}
//! reads a <light> element and stores it as prefab
void CColladaFileLoader::readLightPrefab(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading light prefab");
#endif
CLightPrefab* prefab = new CLightPrefab(readId(reader));
if (!reader->isEmptyElement())
{
if (Version >= 10400) // start with 1.4
{
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (pointSectionName == reader->getNodeName())
prefab->LightData.Type=video::ELT_POINT;
else
if (directionalSectionName == reader->getNodeName())
prefab->LightData.Type=video::ELT_DIRECTIONAL;
else
if (spotSectionName == reader->getNodeName())
prefab->LightData.Type=video::ELT_SPOT;
else
if (ambientSectionName == reader->getNodeName())
prefab->LightData.Type=video::ELT_POINT; // TODO: This needs some change
else
if (colorNodeName == reader->getNodeName())
prefab->LightData.DiffuseColor=readColorNode(reader);
else
if (constantAttenuationNodeName == reader->getNodeName())
readFloatsInsideElement(reader,&prefab->LightData.Attenuation.X,1);
else
if (linearAttenuationNodeName == reader->getNodeName())
readFloatsInsideElement(reader,&prefab->LightData.Attenuation.Y,1);
else
if (quadraticAttenuationNodeName == reader->getNodeName())
readFloatsInsideElement(reader,&prefab->LightData.Attenuation.Z,1);
else
if (falloffAngleNodeName == reader->getNodeName())
{
readFloatsInsideElement(reader,&prefab->LightData.OuterCone,1);
prefab->LightData.OuterCone *= core::DEGTORAD;
}
else
if (falloffExponentNodeName == reader->getNodeName())
readFloatsInsideElement(reader,&prefab->LightData.Falloff,1);
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if ((pointSectionName == reader->getNodeName()) ||
(directionalSectionName == reader->getNodeName()) ||
(spotSectionName == reader->getNodeName()) ||
(ambientSectionName == reader->getNodeName()))
break;
}
}
}
else
{
readColladaParameters(reader, lightPrefabName);
SColladaParam* p = getColladaParameter(ECPN_COLOR);
if (p && p->Type == ECPT_FLOAT3)
prefab->LightData.DiffuseColor.set(p->Floats[0], p->Floats[1], p->Floats[2]);
}
}
Prefabs.push_back(prefab);
}
//! returns a collada parameter or none if not found
SColladaParam* CColladaFileLoader::getColladaParameter(ECOLLADA_PARAM_NAME name)
{
for (u32 i=0; i<ColladaParameters.size(); ++i)
if (ColladaParameters[i].Name == name)
return &ColladaParameters[i];
return 0;
}
//! returns a collada input or none if not found
SColladaInput* CColladaFileLoader::getColladaInput(ECOLLADA_INPUT_SEMANTIC input)
{
for (u32 i=0; i<Inputs.size(); ++i)
if (Inputs[i].Semantic == input)
return &Inputs[i];
return 0;
}
//! reads a collada input tag and adds it to the input parameter
void CColladaFileLoader::readColladaInput(io::IXMLReaderUTF8* reader, core::array<SColladaInput>& inputs)
{
// parse param
SColladaInput p;
// get type
core::stringc semanticName = reader->getAttributeValue("semantic");
for (u32 i=0; inputSemanticNames[i]; ++i)
{
if (semanticName == inputSemanticNames[i])
{
p.Semantic = (ECOLLADA_INPUT_SEMANTIC)i;
break;
}
}
// get source
p.Source = reader->getAttributeValue("source");
if (reader->getAttributeValue("offset")) // Collada 1.4+
p.Offset = (u32)reader->getAttributeValueAsInt("offset");
else // Collada 1.2/1.3
p.Offset = (u32)reader->getAttributeValueAsInt("idx");
p.Set = (u32)reader->getAttributeValueAsInt("set");
// add input
inputs.push_back(p);
}
//! parses all collada inputs inside an element and stores them in Inputs
void CColladaFileLoader::readColladaInputs(io::IXMLReaderUTF8* reader, const core::stringc& parentName)
{
Inputs.clear();
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT &&
inputTagName == reader->getNodeName())
{
readColladaInput(reader, Inputs);
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (parentName == reader->getNodeName())
return; // end of parent reached
}
} // end while reader->read();
}
//! parses all collada parameters inside an element and stores them in Parameters
void CColladaFileLoader::readColladaParameters(io::IXMLReaderUTF8* reader,
const core::stringc& parentName)
{
ColladaParameters.clear();
const char* const paramNames[] = {"COLOR", "AMBIENT", "DIFFUSE",
"SPECULAR", "SHININESS", "YFOV", "ZNEAR", "ZFAR", 0};
const char* const typeNames[] = {"float", "float2", "float3", 0};
while(reader->read())
{
const char* nodeName = reader->getNodeName();
if (reader->getNodeType() == io::EXN_ELEMENT &&
paramTagName == nodeName)
{
// parse param
SColladaParam p;
// get type
u32 i;
core::stringc typeName = reader->getAttributeValue("type");
for (i=0; typeNames[i]; ++i)
if (typeName == typeNames[i])
{
p.Type = (ECOLLADA_PARAM_TYPE)i;
break;
}
// get name
core::stringc nameName = reader->getAttributeValue("name");
for (i=0; typeNames[i]; ++i)
if (nameName == paramNames[i])
{
p.Name = (ECOLLADA_PARAM_NAME)i;
break;
}
// read parameter data inside parameter tags
switch(p.Type)
{
case ECPT_FLOAT:
case ECPT_FLOAT2:
case ECPT_FLOAT3:
case ECPT_FLOAT4:
readFloatsInsideElement(reader, p.Floats, p.Type - ECPT_FLOAT + 1);
break;
// TODO: other types of data (ints, bools or whatever)
default:
break;
}
// add param
ColladaParameters.push_back(p);
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (parentName == reader->getNodeName())
return; // end of parent reached
}
} // end while reader->read();
}
//! parses a float from a char pointer and moves the pointer
//! to the end of the parsed float
inline f32 CColladaFileLoader::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 CColladaFileLoader::readInt(const c8** p)
{
return (s32)readFloat(p);
}
//! places pointer to next begin of a token
void CColladaFileLoader::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 CColladaFileLoader::readFloatsInsideElement(io::IXMLReaderUTF8* 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();
data.trim();
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
}
}
//! reads ints from inside of xml element until end of xml element
void CColladaFileLoader::readIntsInsideElement(io::IXMLReaderUTF8* reader, s32* ints, 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();
data.trim();
const c8* p = &data[0];
for (u32 i=0; i<count; ++i)
{
findNextNoneWhiteSpace(&p);
if (*p)
ints[i] = readInt(&p);
else
ints[i] = 0;
}
}
else
if (reader->getNodeType() == io::EXN_ELEMENT_END)
break; // end parsing text
}
}
video::SColorf CColladaFileLoader::readColorNode(io::IXMLReaderUTF8* reader)
{
if (reader->getNodeType() == io::EXN_ELEMENT &&
colorNodeName == reader->getNodeName())
{
f32 color[4];
readFloatsInsideElement(reader,color,4);
return video::SColorf(color[0], color[1], color[2], color[3]);
}
return video::SColorf();
}
f32 CColladaFileLoader::readFloatNode(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading <float>");
#endif
f32 result = 0.0f;
if (reader->getNodeType() == io::EXN_ELEMENT &&
floatNodeName == reader->getNodeName())
{
readFloatsInsideElement(reader,&result,1);
}
return result;
}
//! clears all loaded data
void CColladaFileLoader::clearData()
{
// delete all prefabs
for (u32 i=0; i<Prefabs.size(); ++i)
Prefabs[i]->drop();
Prefabs.clear();
// clear all parameters
ColladaParameters.clear();
// clear all materials
Images.clear();
// clear all materials
Textures.clear();
// clear all materials
Materials.clear();
// clear all inputs
Inputs.clear();
// clear all effects
Effects.clear();
// clear all the materials to bind
MaterialsToBind.clear();
MeshesToBind.clear();
}
//! changes the XML URI into an internal id
void CColladaFileLoader::uriToId(core::stringc& str)
{
// currently, we only remove the # from the begin if there
// because we simply don't support referencing other files.
if (!str.size())
return;
if (str[0] == '#')
str.erase(0);
}
//! read Collada Id, uses id or name if id is missing
core::stringc CColladaFileLoader::readId(io::IXMLReaderUTF8* reader)
{
core::stringc id = reader->getAttributeValue("id");
if (id.size()==0)
id = reader->getAttributeValue("name");
return id;
}
//! create an Irrlicht texture from the reference
video::ITexture* CColladaFileLoader::getTextureFromImage(core::stringc uri)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA searching texture", uri);
#endif
video::IVideoDriver* driver = SceneManager->getVideoDriver();
for (;;)
{
uriToId(uri);
for (u32 i=0; i<Images.size(); ++i)
{
if (uri == Images[i].Id)
{
if (Images[i].Source.size() && Images[i].SourceIsFilename)
{
if (FileSystem->existFile(Images[i].Source))
return driver->getTexture(Images[i].Source);
return driver->getTexture((FileSystem->getFileDir(CurrentlyLoadingMesh)+"/"+Images[i].Source));
}
else
if (Images[i].Source.size())
{
//const u32 size = Images[i].Dimension.getArea();
const u32 size = Images[i].Dimension.Width * Images[i].Dimension.Height;;
u32* data = new u32[size]; // we assume RGBA
u32* ptrdest = data;
const c8* ptrsrc = Images[i].Source.c_str();
for (u32 j=0; j<size; ++j)
{
sscanf(ptrsrc, "%x", ptrdest);
++ptrdest;
ptrsrc += 4;
}
video::IImage* img = driver->createImageFromData(video::ECF_A8R8G8B8, Images[i].Dimension, data, true, true);
video::ITexture* tex = driver->addTexture((CurrentlyLoadingMesh+"#"+Images[i].Id).c_str(), img);
img->drop();
return tex;
}
break;
}
}
if (Parameters.getAttributeType(uri.c_str())==io::EAT_STRING)
{
uri = Parameters.getAttributeAsString(uri.c_str());
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA now searching texture", uri.c_str());
#endif
}
else
break;
}
return 0;
}
//! read a parameter and value
void CColladaFileLoader::readParameter(io::IXMLReaderUTF8* reader)
{
#ifdef COLLADA_READER_DEBUG
os::Printer::log("COLLADA reading parameter");
#endif
const core::stringc name = reader->getAttributeValue("sid");
if (!reader->isEmptyElement())
{
while(reader->read())
{
if (reader->getNodeType() == io::EXN_ELEMENT)
{
if (floatNodeName == reader->getNodeName())
{
const f32 f = readFloatNode(reader);
Parameters.addFloat(name.c_str(), f);
}
else
if (float2NodeName == reader->getNodeName())
{
f32 f[2];
readFloatsInsideElement(reader, f, 2);
// Parameters.addVector2d(name.c_str(), core::vector2df(f[0],f[1]));
}
else
if (float3NodeName == reader->getNodeName())
{
f32 f[3];
readFloatsInsideElement(reader, f, 3);
Parameters.addVector3d(name.c_str(), core::vector3df(f[0],f[1],f[2]));
}
else
if ((initFromName == reader->getNodeName()) ||
(sourceSectionName == reader->getNodeName()))
{
reader->read();
Parameters.addString(name.c_str(), reader->getNodeData());
}
else
if (wrapsName == reader->getNodeName())
{
reader->read();
const core::stringc val = reader->getNodeData();
if (val == "WRAP")
Parameters.addBool("wrap_s", true);
}
else
if (wraptName == reader->getNodeName())
{
reader->read();
const core::stringc val = reader->getNodeData();
if (val == "WRAP")
Parameters.addBool("wrap_t", true);
}
else
if (minfilterName == reader->getNodeName())
{
reader->read();
const core::stringc val = reader->getNodeData();
if (val == "LINEAR_MIPMAP_LINEAR")
Parameters.addBool("trilinear", true);
else
if (val == "LINEAR_MIPMAP_NEAREST")
Parameters.addBool("bilinear", true);
}
else
if (magfilterName == reader->getNodeName())
{
reader->read();
const core::stringc val = reader->getNodeData();
if (val != "LINEAR")
{
Parameters.addBool("bilinear", false);
Parameters.addBool("trilinear", false);
}
}
else
if (mipfilterName == reader->getNodeName())
{
Parameters.addBool("anisotropic", true);
}
}
else
if(reader->getNodeType() == io::EXN_ELEMENT_END)
{
if (newParamName == reader->getNodeName())
break;
}
}
}
}
} // end namespace scene
} // end namespace irr
#endif // _IRR_COMPILE_WITH_COLLADA_LOADER_