/** * This source file is part of OgreColladaPlugin * an addon for OGRE (Object-oriented Graphics Rendering Engine) * For the latest info, see http://www.ogre3d.org/ * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307, USA, or go to * http://www.gnu.org/copyleft/lesser.txt. * * @author Philipp Hartl * @see README */ #include "OgreColladaGeometry.h" #include "OgreColladaDocument.h" #include "OgreColladaLibrary.h" #include "OgreColladaMaterial.h" #include "OgreColladaScene.h" #include "OgreColladaSyntax.h" #include "OgreColladaUtils.h" #include "OgreSceneManager.h" #include "OgreMovableObject.h" #include "OgreMeshManager.h" #include "OgreSubMesh.h" #include "OgreEntity.h" #include "OgreStringConverter.h" namespace Ogre { //------------------------------------------------------------------------- ColladaGeometry::ColladaGeometry(ColladaDocument *doc, xmlNode *n) : ColladaEntity(doc, n) { mOgreMesh = NULL; mEntityName = ""; mBB = NULL; mBBReset = false; } //------------------------------------------------------------------------- ColladaGeometry::~ColladaGeometry(void) { if (!mSources.empty()) { for (ColladaGeometrySourcePtrVector::iterator i = mSources.begin(); i != mSources.end(); ++i) { (*i)->data.clear(); OGRE_DELETE(*i); } mSources.clear(); } if (!mVertexInputs.empty()) { for (ColladaGeometryInputDataPtrVector::iterator i = mVertexInputs.begin(); i != mVertexInputs.end(); ++i) { // source already freed OGRE_DELETE(*i); } mVertexInputs.clear(); } if (!mPolygons.empty()) { for (ColladaGeometryPolygonPtrVector::iterator i = mPolygons.begin(); i != mPolygons.end(); ++i) { for (ColladaGeometryInputDataPtrVector::iterator j = (*i)->inputs.begin(); j != (*i)->inputs.end(); ++j) { // source already freed OGRE_DELETE(*j); } (*i)->inputs.clear(); (*i)->material = NULL; OGRE_DELETE(*i); } mPolygons.clear(); } if (mOgreMesh != NULL) { for (ColladaGeometryOgreMeshIndexData::iterator it = mOgreMesh->indices.begin(); it != mOgreMesh->indices.end(); ++it) { OGRE_DELETE_ARRAY((*it)->indices); OGRE_DELETE(*it); } mOgreMesh->indices.clear(); OGRE_DELETE_ARRAY(mOgreMesh->vertices); OGRE_DELETE(mOgreMesh); } if (mBBReset) mBB = NULL; else OGRE_DELETE(mBB); } //------------------------------------------------------------------------- void ColladaGeometry::calcBoundingBox(void) { if (!mBB) { // calculate a bounding box ColladaGeometrySource *src = mVertexInputs.at(0)->source; float minX, minY, minZ, maxX, maxY, maxZ; minX = maxX = src->data.at(0); minY = maxY = src->data.at(1); minZ = maxZ = src->data.at(2); for (uint i = 0; i < src->count; i++) { uint index = i * 3; if (minX > src->data.at(index)) minX = src->data.at(index); if (minY > src->data.at(index + 1)) minY = src->data.at(index + 1); if (minZ > src->data.at(index + 2)) minZ = src->data.at(index + 2); if (maxX < src->data.at(index)) maxX = src->data.at(index); if (maxY < src->data.at(index + 1)) maxY = src->data.at(index + 1); if (maxZ < src->data.at(index + 2)) maxZ = src->data.at(index + 2); } mBB = new ColladaBoundingBox(Vector3(minX, minY, minZ), Vector3(maxX, maxY, maxZ)); } } //------------------------------------------------------------------------- void ColladaGeometry::createTriangleList(void) { // walk through all polygons, there must be at least one at index 0 uint polygonCount = static_cast(mPolygons.size()); ColladaGeometryPolygon *polygon = mPolygons.at(0); if (polygon == NULL) return; // for simple life ColladaGeometryInputData *vertexInput = NULL; ColladaGeometryInputData *normalInput = NULL; ColladaGeometryInputData *texcoordInput = NULL; // get all trivial sources through vertex inputs, one index array for all ColladaGeometryInputDataPtrVector::iterator it; for (it = mVertexInputs.begin(); it != mVertexInputs.end(); ++it) { if ((*it)->semantic == ColladaGeometrySpecific::POSITION) vertexInput = *it; if ((*it)->semantic == ColladaGeometrySpecific::NORMAL) normalInput = *it; if ((*it)->semantic == ColladaGeometrySpecific::TEXCOORD) texcoordInput = *it; } // look for detailed normals and texcoords, each source with its own index data for (it = polygon->inputs.begin(); it != polygon->inputs.end(); ++it) { if ((*it)->semantic == ColladaGeometrySpecific::NORMAL) normalInput = *it; if ((*it)->semantic == ColladaGeometrySpecific::TEXCOORD) texcoordInput = *it; } // vertex, normal, texcoord source counts uint vertexcount = static_cast(vertexInput->source->data.size()); uint normalcount = 0; uint texcoordcount = 0; if (normalInput != NULL) normalcount = static_cast(normalInput->source->data.size()); if (texcoordInput != NULL) texcoordcount = static_cast(texcoordInput->source->data.size()); // create new ogre mesh structure mOgreMesh = new ColladaGeometryOgreMesh(); mOgreMesh->drawNormals = ((normalInput != NULL) && (normalcount > 0)); // are there more normals per vertex? bool normalsEx = (mOgreMesh->drawNormals && (normalcount != vertexcount)); mOgreMesh->drawTexCoords = ((texcoordInput != NULL) && (texcoordcount > 0)); // more texcoords per vertex bool texcoordsEx = (mOgreMesh->drawTexCoords && (texcoordcount != vertexcount)); // calc stride size for vertex array uint stridesize = vertexInput->source->stride; if (mOgreMesh->drawNormals) stridesize += normalInput->source->stride; if (mOgreMesh->drawTexCoords) stridesize += texcoordInput->source->stride; // how many triangles are in one polygon? // if there are more than one polygon per geometry, the amount should not change! uint trianglesppolygon = 1; uint i = 0; for (i = polygon->vertexCount; i > 3; i--) trianglesppolygon++; /** simple case, each vertex has at maximum only one normal and/or one texture information * (vertexcount == normalcount == texcoordcount, all data have the same indices) * so we only have to triangulate the polygon primitive */ if (!normalsEx && !texcoordsEx) { // create space for the vertex array mOgreMesh->vertexCount = vertexInput->source->count; mOgreMesh->vertices = new float[mOgreMesh->vertexCount * stridesize]; // (1) fill up vertices with vertex (normal, texcoord) data // the mesh could be created anyway if polygonCount > 0 uint offset = 0; for (i = 0; i < mOgreMesh->vertexCount; i++) { mOgreMesh->vertices[i * stridesize] = vertexInput->source->data[i * 3]; mOgreMesh->vertices[i * stridesize + 1] = vertexInput->source->data[i * 3 + 1]; mOgreMesh->vertices[i * stridesize + 2] = vertexInput->source->data[i * 3 + 2]; mDoc->correctAxis(&mOgreMesh->vertices[i * stridesize]); offset = 3; if (mOgreMesh->drawNormals) { mOgreMesh->vertices[i * stridesize + offset] = normalInput->source->data[i * 3]; mOgreMesh->vertices[i * stridesize + offset + 1] = normalInput->source->data[i * 3 + 1]; mOgreMesh->vertices[i * stridesize + offset + 2] = normalInput->source->data[i * 3 + 2]; mDoc->correctAxis(&mOgreMesh->vertices[i * stridesize + offset]); offset += 3; } if (mOgreMesh->drawTexCoords) { mOgreMesh->vertices[i * stridesize + offset] = texcoordInput->source->data[i * 2]; mOgreMesh->vertices[i * stridesize + offset + 1] = texcoordInput->source->data[i * 2 + 1]; } } // (2) fill up indices array for each polygon primitive for (ColladaGeometryPolygonPtrVector::iterator it = mPolygons.begin(); it != mPolygons.end(); ++it) { polygon = *it; // create enough space for the index array ColladaGeometryIndexData *tmpIndices = new ColladaGeometryIndexData(); tmpIndices->count = trianglesppolygon * polygon->primitiveCount * 3; tmpIndices->indices = new unsigned short[tmpIndices->count]; tmpIndices->material = ((*it)->setMaterial) ? (*it)->material->getId() : ""; // set vertexInput pointer at indices vertexInput = polygon->inputs.at(0); offset = trianglesppolygon * 3; uint index = 0; for (i = 0; i < polygon->primitiveCount; i++) { index = offset * i; // first triangle tmpIndices->indices[index] = vertexInput->indices[i * polygon->vertexCount]; tmpIndices->indices[index + 1] = vertexInput->indices[i * polygon->vertexCount + 1]; tmpIndices->indices[index + 2] = vertexInput->indices[i * polygon->vertexCount + 2]; // are there more than one triangle in one primitive? for (uint j = 1; j < trianglesppolygon; j++) { tmpIndices->indices[index + j * 3] = vertexInput->indices[i * polygon->vertexCount]; tmpIndices->indices[index + j * 3 + 1] = vertexInput->indices[i * polygon->vertexCount + j + 1]; tmpIndices->indices[index + j * 3 + 2] = vertexInput->indices[i * polygon->vertexCount + j + 2]; } } // save index array mOgreMesh->indices.push_back(tmpIndices); } } /** tricky case, each vertex can have more than one normal and/or texture * normals or texcoord arrays with different indices */ else { // space for vertex count mOgreMesh->vertexCount = 0; ColladaGeometryPolygonPtrVector::iterator it; for (it = mPolygons.begin(); it != mPolygons.end(); ++it) { // the vertexCount should not change! mOgreMesh->vertexCount += (*it)->primitiveCount * (*it)->vertexCount; } mOgreMesh->vertices = new float[mOgreMesh->vertexCount * stridesize]; for (it = mPolygons.begin(); it != mPolygons.end(); ++it) { polygon = (*it); // create enough space for the index array ColladaGeometryIndexData *tmpIndices = new ColladaGeometryIndexData(); tmpIndices->count = trianglesppolygon * polygon->primitiveCount * 3; tmpIndices->indices = new unsigned short[tmpIndices->count]; tmpIndices->material = ((*it)->setMaterial) ? (*it)->material->getId() : ""; // get vertex indices intVector *vertexIndices = &polygon->inputs.at(0)->indices; // get normal indices intVector *normalIndices = &normalInput->indices; if (!normalsEx) normalIndices = vertexIndices; // get texture indices intVector *textureIndices = &texcoordInput->indices; if (!texcoordsEx) textureIndices = vertexIndices; // fill up mVertices and mIndices array in one pass for (i = 0; i < polygon->primitiveCount; i++) { // starting index for one primitive uint pindex = i * polygon->vertexCount; // (1) vertices uint j; for (j = 0; j < polygon->vertexCount; j++) { uint vindex = pindex + j; // vertex index // vertex data mOgreMesh->vertices[vindex * stridesize] = vertexInput->source->data[vertexIndices->at(vindex) * 3]; mOgreMesh->vertices[vindex * stridesize + 1] = vertexInput->source->data[vertexIndices->at(vindex) * 3 + 1]; mOgreMesh->vertices[vindex * stridesize + 2] = vertexInput->source->data[vertexIndices->at(vindex) * 3 + 2]; mDoc->correctAxis(&mOgreMesh->vertices[vindex * stridesize]); uint offset = 3; if (mOgreMesh->drawNormals) { mOgreMesh->vertices[vindex * stridesize + offset] = normalInput->source->data[normalIndices->at(vindex) * 3]; mOgreMesh->vertices[vindex * stridesize + offset + 1] = normalInput->source->data[normalIndices->at(vindex) * 3 + 1]; mOgreMesh->vertices[vindex * stridesize + offset + 2] = normalInput->source->data[normalIndices->at(vindex) * 3 + 2]; mDoc->correctAxis(&mOgreMesh->vertices[vindex * stridesize + offset]); offset += 3; } if (mOgreMesh->drawTexCoords) { mOgreMesh->vertices[vindex * stridesize + offset] = texcoordInput->source->data[textureIndices->at(vindex) * 2]; mOgreMesh->vertices[vindex * stridesize + offset + 1] = texcoordInput->source->data[textureIndices->at(vindex) * 2 + 1]; } } // (2) indices uint iindex = i * trianglesppolygon * 3; // indices index // first triangle tmpIndices->indices[iindex] = pindex; tmpIndices->indices[iindex + 1] = pindex + 1; tmpIndices->indices[iindex + 2] = pindex + 2; // are there more than one triangle in one primitive? for (j = 1; j < trianglesppolygon; j++) { tmpIndices->indices[iindex + j * 3] = pindex; tmpIndices->indices[iindex + j * 3 + 1] = pindex + j + 1; tmpIndices->indices[iindex + j * 3 + 2] = pindex + j + 2; } } // save index array mOgreMesh->indices.push_back(tmpIndices); } } // TEST // printVertices(stridesize); // printIndices(); } //------------------------------------------------------------------------- MovableObject *ColladaGeometry::getOgreInstance(void) const { if (mOgreMesh != NULL && !mEntityName.empty()) { // create hw-buffers and push the data there, material will be set too createOgreMesh(); return mDoc->getSceneManager()->createEntity(mEntityName, mId); } return NULL; } //------------------------------------------------------------------------- void ColladaGeometry::createOgreMesh(void) const { //if (MeshManager::getSingleton().getByName(mId) != static_cast(NULL)) return; ResourcePtr p; if (MeshManager::getSingleton().getByName(mId) != p) return; MeshPtr pMesh = MeshManager::getSingleton().createManual(mId, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); pMesh->sharedVertexData = new VertexData(); pMesh->sharedVertexData->vertexCount = mOgreMesh->vertexCount; VertexDeclaration *pVertexDecl = pMesh->sharedVertexData->vertexDeclaration; size_t offset = 0; // position pVertexDecl->addElement(0, offset, VET_FLOAT3, VES_POSITION); offset += VertexElement::getTypeSize(VET_FLOAT3); // normals if (mOgreMesh->drawNormals) { pVertexDecl->addElement(0, offset, VET_FLOAT3, VES_NORMAL); offset += VertexElement::getTypeSize(VET_FLOAT3); } // colour (diffuse, specular) // texture coordinates if (mOgreMesh->drawTexCoords) { pVertexDecl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0); offset += VertexElement::getTypeSize(VET_FLOAT2); } HardwareVertexBufferSharedPtr vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( offset, pMesh->sharedVertexData->vertexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false); // set vertex buffer binding so buffer 0 is bound to our vertex buffer VertexBufferBinding* bind = pMesh->sharedVertexData->vertexBufferBinding; bind->setBinding(0, vbuf); // upload the vertex data to the card vbuf->writeData(0, vbuf->getSizeInBytes(), mOgreMesh->vertices, true); // for each polygon create a new submesh, for multimaterial e.g. // submesh with index buffer for (ColladaGeometryOgreMeshIndexData::iterator it = mOgreMesh->indices.begin(); it != mOgreMesh->indices.end(); ++it) { SubMesh *pSubMesh = pMesh->createSubMesh(); pSubMesh->indexData->indexCount = (*it)->count; pSubMesh->indexData->indexBuffer = HardwareBufferManager::getSingleton().createIndexBuffer( HardwareIndexBuffer::IT_16BIT, pSubMesh->indexData->indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY); HardwareIndexBufferSharedPtr ibuf = pSubMesh->indexData->indexBuffer; // upload the index data ibuf->writeData(0, ibuf->getSizeInBytes(), (*it)->indices, true); if (!(*it)->material.empty()) pSubMesh->setMaterialName((*it)->material); pSubMesh->useSharedVertices = true; pSubMesh->operationType = RenderOperation::OT_TRIANGLE_LIST; } // there should be an AABB in every case, maybe overwritten from the collada scene node import // be careful by AABB if the object is duplicated and translated pMesh->_setBounds(mBB->getAABB()); pMesh->_setBoundingSphereRadius(mBB->getRadius()); pMesh->load(); } //------------------------------------------------------------------------- bool ColladaGeometry::doImport(void) { if (mLoaded) return mStatus; else mLoaded = true; // get node, must occur exactly one time [1,1] xmlNode *mesh = ColladaUtils::getChildByTagName(mNode, CS_ELM_MESH); if (mesh != NULL) mStatus = importMesh(mesh); // get nodes, can occur [0,*] // xmlNodePtrVector extras = ColladaUtils::getChildsByTagName(mNode, CS_ELM_EXTRA); // for (xmlNodePtrVector::iterator i = extras.begin(); i != extras.end(); ++i) importExtra((*i)); if (mStatus) { calcBoundingBox(); // create the vertices and indices by simple triangulation if (mType == POLYGONS || mType == TRIANGLES) createTriangleList(); } return mStatus; } //------------------------------------------------------------------------- void ColladaGeometry::importExtra(xmlNode *extraNode) { // not yet implemented } //------------------------------------------------------------------------- bool ColladaGeometry::importMesh(xmlNode *meshNode) { bool bPrimitives = false; // vertices must occur exactly one time if (!importVerticesNode(meshNode)) return false; // look for polygon primitives bPrimitives = importPolygonsNodes(meshNode, CS_ELM_POLYGONS); if (bPrimitives) { mType = POLYGONS; return true; } // triangles bPrimitives = importPolygonsNodes(meshNode, CS_ELM_TRIANGLES); if (bPrimitives) { mType = TRIANGLES; return true; } return false; } //------------------------------------------------------------------------- bool ColladaGeometry::importPolygonsNodes(xmlNode *meshNode, const String &primitive) { // there can be more than one node, e.g. multi materials xmlNodePtrVector polygonsNodes = ColladaUtils::getChildsByTagName(meshNode, primitive); if (polygonsNodes.empty()) return false; // walk through all for (xmlNodePtrVector::iterator it = polygonsNodes.begin(); it != polygonsNodes.end(); ++it) { xmlNode *polygonsNode = *it; ColladaGeometryPolygon *polygon = new ColladaGeometryPolygon(); // attribute material refers to a node String material = ColladaUtils::getProperty(polygonsNode, CS_ATR_INPUT_MATERIAL); polygon->setMaterial = false; // search for material, get ptr if (!material.empty()) { material.erase(0,1); // remove the # sign polygon->material = mDoc->getLibrary()->getMaterial(material); if (polygon->material != NULL) { // import material entity if (!(polygon->setMaterial = polygon->material->doImport())) { LogManager::getSingleton().logMessage("ColladaGeometry::importPolygonsNodes - loading of assigned material " + material + " failed! " + mId); } } else { LogManager::getSingleton().logMessage("ColladaGeometry::importPolygonsNodes - can't find assigned material with id " + material + "! " + mId); } } // the number of polygon primitives String count = ColladaUtils::getProperty(polygonsNode, CS_ATR_COUNT); polygon->primitiveCount = (!count.empty()) ? static_cast(atoi(count.c_str())) : 0; // walk through , ,

childs xmlNode *c = NULL; for (c = polygonsNode->children; c != NULL; c = c->next) { if (c->type != XML_ELEMENT_NODE) continue; String tagname = (const char *)c->name; // , ignore it if (tagname == CS_ELM_PARAM) continue; // else if (tagname == CS_ELM_INPUT) { // get semantic attribute String semantic = ColladaUtils::getProperty(c, CS_ATR_INPUT_SEMANTIC); ColladaGeometrySpecific::Semantic tmpsemantic = ColladaGeometrySpecific::getSemantic(semantic); if (tmpsemantic == ColladaGeometrySpecific::VERTEX || tmpsemantic == ColladaGeometrySpecific::NORMAL || tmpsemantic == ColladaGeometrySpecific::TEXCOORD) { // if (tmpsemantic == ColladaGeometryInput::NORMAL) mhasFaceNormals = true; // if (tmpsemantic == ColladaGeometryInput::TEXCOORD) mhasFaceTexCoords = true; String idx = ColladaUtils::getProperty(c, CS_ATR_IDX); String source = ColladaUtils::getProperty(c, CS_ATR_INPUT_SOURCE); ColladaGeometrySource *gsource = NULL; // the source node points at , already imported by importVerticesNode if (tmpsemantic != ColladaGeometrySpecific::VERTEX) { gsource = importSourceNode(ColladaUtils::getChildById(meshNode, source.c_str())); if (gsource == NULL) { LogManager::getSingleton().logMessage("ColladaGeometry::importVerticesNode - unable to find/import with id " + source); continue; } } // make new inputdata and push it on the vector ColladaGeometryInputData *input = new ColladaGeometryInputData(); input->idx = (!idx.empty()) ? static_cast(atoi(idx.c_str())) : polygon->inputs.size(); input->semantic = tmpsemantic; input->source = gsource; // check for duplicate bool idxrepeat = false; for (ColladaGeometryInputDataPtrVector::iterator jt = polygon->inputs.begin(); jt != polygon->inputs.end() && !idxrepeat; ++jt) { if ((*jt)->idx == input->idx) idxrepeat = true; } if (!idxrepeat) polygon->inputs.push_back(input); else OGRE_DELETE(input); // free unneeded geometry input data } // unknwon or unsupported else { LogManager::getSingleton().logMessage("ColladaGeometry::importPolygonsNode - unknown " + semantic + " in " + mId); continue; } } //

first appearance, break, cause we will parse the primitves later else if (tagname == CS_ELM_P) break; // unknwon or unsupported else { LogManager::getSingleton().logMessage("ColladaGeometry::importPolygonsNodes - unknown child: " + tagname); continue; } } // there should be at least one with semantic="VERTEX" if (polygon->inputs.at(0)->semantic != ColladaGeometrySpecific::VERTEX) continue; polygon->inputCount = static_cast(polygon->inputs.size()); uint indexCount = 0; polygon->vertexCount = 0; uint tmppCount = 0; // parse primitives,

nodes, line by line for (; c != NULL; c = c->next) { if (c->type != XML_ELEMENT_NODE) continue; // get all indices (vertices, normals, texcoords) // ATTENTION: holes are currently not supported !! String primitive = ColladaUtils::getContentDirect(c); // split the primitive string StringVector sallindices = StringUtil::split(primitive," "); if (sallindices.empty()) continue; // get the amount of vertex indices of one polygon, quad patches are pleasant indexCount = static_cast(sallindices.size()) / polygon->inputCount; if (indexCount != polygon->vertexCount) { // skip the false primitive line, but be aware of false geometries (e.g. holes)! // perhaps it is better to stop reading immediately? if (polygon->vertexCount > 0) { LogManager::getSingleton().logMessage("ColladaGeometry::importPolygonsNodes - different index count? " + mId); LogManager::getSingleton().logMessage("ColladaGeometry::importPolygonsNodes - primitive: " + primitive); continue; } polygon->vertexCount = indexCount; } // we can parse more than quad patches // but should we do this? currently there is only a simple algortihm which triangulate if (polygon->vertexCount > 4) { LogManager::getSingleton().logMessage("ColladaGeometry::importPolygonsNodes - why are you not using quad patches?"); } intVector iallindices; iallindices.reserve(sallindices.size()); // fill up a temp indices vector with conversions to int for (StringVector::iterator st = sallindices.begin(); st != sallindices.end(); ++st) { iallindices.push_back(atoi((*st).c_str())); } // fill up the corresponding index vectors with indices for (uint offset = 0; offset < static_cast(iallindices.size()); offset += polygon->inputCount) { // in order of inputs for (ColladaGeometryInputDataPtrVector::iterator pt = polygon->inputs.begin(); pt != polygon->inputs.end(); ++pt) { (*pt)->indices.push_back(iallindices[offset + (*pt)->idx]); } } tmppCount++; } // if there was a failure in parsing primitives, the amount is not still the same if (tmppCount < polygon->primitiveCount) polygon->primitiveCount = tmppCount; mPolygons.push_back(polygon); } return true; } //------------------------------------------------------------------------- ColladaGeometrySource *ColladaGeometry::importSourceNode(xmlNode *sourceNode) { if (sourceNode == NULL) return NULL; // get unique identifier String id = ColladaUtils::getProperty(sourceNode, CS_ATR_ID); // check if source is already loaded for (ColladaGeometrySourcePtrVector::iterator itsource = mSources.begin(); itsource != mSources.end(); ++itsource) { if ((*itsource)->id == id) return *itsource; } // get all child nodes xmlNodePtrVector techniques = ColladaUtils::getChildsByTagName(sourceNode, CS_ELM_TECHNIQUE); // currently we are only interested in "COMMON" profile xmlNode *technique = NULL; for (xmlNodePtrVector::iterator it = techniques.begin(); it != techniques.end(); ++it) { String profile = ColladaUtils::getProperty(*it, CS_ATR_PROFILE); if (profile == CS_VAL_TECHNIQUE_PROFILE_COMMON) { technique = *it; break; } } if (technique == NULL) return NULL; // currently ignore , // get information xmlNode *accessor = ColladaUtils::getChildByTagName(technique, CS_ELM_ACCESSOR); if (accessor == NULL) return NULL; // currently ignore , , // get count and stride properties String count = ColladaUtils::getProperty(accessor, CS_ATR_COUNT); String stride = ColladaUtils::getProperty(accessor, CS_ATR_ACCESSOR_STRIDE); // new source record ColladaGeometrySource *source = new ColladaGeometrySource(); source->id = id; source->count = (!count.empty()) ? static_cast(atoi(count.c_str())) : 0; source->stride = (!stride.empty()) ? static_cast(atoi(stride.c_str())) : 0; // get data from xmlNode *data = ColladaUtils::getChildByTagName(sourceNode, CS_ELM_FLOAT_ARRAY); if (data == NULL) return NULL; String content = ColladaUtils::getContentDirect(data); if (content.empty()) return NULL; // split string data by carriage return and line feed StringVector lines = StringUtil::split(content,"\r\n"); if (lines.size() != source->count) source->count = static_cast(lines.size()); // reserve enough space source->data.reserve(source->count * source->stride); // walk through each data per line for (StringVector::iterator jt = lines.begin(); jt != lines.end(); ++jt) { // split each line by a blank StringVector line = StringUtil::split(*jt," "); // for vertices (x, y, z), normals (x, y, z), texcoord (s, t), ... for (StringVector::iterator kt = line.begin(); kt != line.end(); ++kt) { source->data.push_back( StringConverter::parseReal(*kt) ); } } mSources.push_back(source); return source; } //------------------------------------------------------------------------- bool ColladaGeometry::importVerticesNode(xmlNode *meshNode) { // get node of mesh xmlNode *vertices = ColladaUtils::getChildByTagName(meshNode, CS_ELM_VERTICES); if (vertices == NULL) { LogManager::getSingleton().logMessage("ColladaGeometry::importVerticesNode - no node found in " + mId); return false; } // walk through childs for (xmlNode *c = vertices->children; c != NULL; c = c->next) { if (c->type != XML_ELEMENT_NODE) continue; String tagname = (const char *)c->name; // if (tagname == CS_ELM_INPUT) { // get semantic attribute type String semantic = ColladaUtils::getProperty(c, CS_ATR_INPUT_SEMANTIC); ColladaGeometrySpecific::Semantic tmpsemantic = ColladaGeometrySpecific::getSemantic(semantic); if (tmpsemantic == ColladaGeometrySpecific::POSITION || tmpsemantic == ColladaGeometrySpecific::NORMAL || tmpsemantic == ColladaGeometrySpecific::COLOR || tmpsemantic == ColladaGeometrySpecific::TEXCOORD) { // if (tmpsemantic == ColladaGeometryInput::NORMAL) mhasNormals = true; // if (tmpsemantic == ColladaGeometryInput::TEXCOORD) mhasTexCoords = true; // get source string String source = ColladaUtils::getProperty(c, CS_ATR_INPUT_SOURCE); if (source.empty()) continue; // import source ColladaGeometrySource *gsource = importSourceNode(ColladaUtils::getChildById(meshNode, source.c_str())); if (gsource == NULL) { LogManager::getSingleton().logMessage("ColladaGeometry::importVerticesNode - unable to find/import with id " + source); continue; } // make new inputdata and push it on the vector ColladaGeometryInputData *idata = new ColladaGeometryInputData(); idata->semantic = tmpsemantic; idata->source = gsource; mVertexInputs.push_back(idata); } // unknown or unsupported else { LogManager::getSingleton().logMessage("ColladaGeometry::importVerticesNode - unknown " + semantic + " in " + mId); continue; } } // unknown or unsupported else { LogManager::getSingleton().logMessage("ColladaGeometry::importVerticesNode - unknown child: " + tagname); } } // there must be at least one return (mVertexInputs.at(0)->semantic == ColladaGeometrySpecific::POSITION); } //---------------------------------------------------------------------------- void ColladaGeometry::printIndices(void) { LogManager::getSingleton().logMessage(StringConverter::toString(mOgreMesh->indices.size())); for (ColladaGeometryOgreMeshIndexData::iterator it = mOgreMesh->indices.begin(); it != mOgreMesh->indices.end(); ++it) { String result = ""; for (uint i = 0; i < (*it)->count; i++) { if (i % 3 == 0) result += "\n"; result += " " + StringConverter::toString((*it)->indices[i]); } LogManager::getSingleton().logMessage(result + " " + StringConverter::toString((*it)->count) + " " + (*it)->material); } } //---------------------------------------------------------------------------- void ColladaGeometry::printVertices(const unsigned short x) { String result = ""; LogManager::getSingleton().logMessage(StringConverter::toString(mOgreMesh->vertexCount)); for (uint i = 0; i < mOgreMesh->vertexCount * x; i++) { if (i % x == 0) result += "\n"; result += " " + StringConverter::toString(mOgreMesh->vertices[i]); } LogManager::getSingleton().logMessage(result); } //---------------------------------------------------------------------------- void ColladaGeometry::setBoundingBox(ColladaBoundingBox *b) { if (mBB == NULL) return; mBB = b; mBBReset = true; } //---------------------------------------------------------------------------- namespace ColladaGeometrySpecific { Semantic getSemantic(const String &s) { if (s == CS_VAL_INPUT_SEMANTIC_POSITION) return POSITION; else if (s == CS_VAL_INPUT_SEMANTIC_VERTEX) return VERTEX; else if (s == CS_VAL_INPUT_SEMANTIC_NORMAL) return NORMAL; else if (s == CS_VAL_INPUT_SEMANTIC_TEXCOORD) return TEXCOORD; else if (s == CS_VAL_INPUT_SEMANTIC_COLOR) return COLOR; else return UNKNOWN; } } }