/* * Copyright 2006 Sony Computer Entertainment Inc. * * Licensed under the MIT Open Source License, for details please see license.txt or the website * http://www.opensource.org/licenses/mit-license.php * */ #include "ColladaGeometry.h" #include <iostream> #include <sstream> ColladaGeometry::ColladaGeometry() : mPositionFloats(NULL), mPositionOffset(-1), mNormalFloats(NULL), mNormalOffset(-1), mTangentFloats(NULL), mTangentOffset(-1), mBinormalFloats(NULL), mBinormalOffset(-1), mTexture1Floats(NULL), mTexture1Offset(-1), mMultiIndexOffset(-1), mPositionsStride(3), mNormalsStride(3), mTextureCoordsStride(2), mTangentssStride(3), mBinormalsStride(3) { mConvertedMesh.appendChannel("position", mPositionsStride); mConvertedMesh.appendChannel("normal", mNormalsStride); mConvertedMesh.appendChannel("texture0", mTextureCoordsStride); mConvertedMesh.appendChannel("binormal", mBinormalsStride); mConvertedMesh.appendChannel("tangent", mTangentssStride); mPositions = &mConvertedMesh.mChannels[0].mData; mNormals = &mConvertedMesh.mChannels[1].mData; mTextureCoords = &mConvertedMesh.mChannels[2].mData; mBinormals = &mConvertedMesh.mChannels[3].mData; mTangents = &mConvertedMesh.mChannels[4].mData; } bool ColladaGeometry::init(domGeometryRef geometry) { bool convertSuceeded = true; const char* geoName = geometry->getName(); if (geoName == NULL) { geoName = geometry->getId(); } mConvertedMesh.mName = geoName; mMesh = geometry->getMesh(); // Iterate over all the index groups and build up a simple resolved tri list and vertex array const domTriangles_Array &allTriLists = mMesh->getTriangles_array(); int numTriLists = allTriLists.getCount(); mConvertedMesh.mTriangleLists.reserve(numTriLists); mConvertedMesh.mTriangleListNames.reserve(numTriLists); for (int i = 0; i < numTriLists; i ++) { addTriangles(allTriLists[i]); } return convertSuceeded; } void ColladaGeometry::addTriangles(domTriangles * colladaTriangles) { int numTriangles = colladaTriangles->getCount(); int triListIndex = mConvertedMesh.mTriangleLists.size(); mConvertedMesh.mTriangleLists.resize(triListIndex + 1); std::string materialName = colladaTriangles->getMaterial(); if (materialName.size() == 0) { char buffer[128]; sprintf(buffer, "index%d", triListIndex); materialName = buffer; } mConvertedMesh.mTriangleListNames.push_back(materialName); // It's a good idea to tell stl how much memory we intend to use // to limit the number of reallocations mPositions->reserve(numTriangles * 3); mNormals->reserve(numTriangles * 3); mTangents->reserve(numTriangles * 3); mBinormals->reserve(numTriangles * 3); mTextureCoords->reserve(numTriangles * 3); // Stores the pointers to the image data and where in the tri list that data comes from cacheOffsetsAndDataPointers(colladaTriangles); // Collapse the multiindex that collada uses const domListOfUInts &colladaIndexList = colladaTriangles->getP()->getValue(); std::vector<uint32_t> &a3dIndexList = mConvertedMesh.mTriangleLists[triListIndex]; a3dIndexList.resize(numTriangles * 3); for (int i = 0; i < numTriangles * 3; i ++) { a3dIndexList[i] = remapIndexAndStoreData(colladaIndexList, i); } } void ColladaGeometry::cacheOffsetsAndDataPointers(domTriangles * colladaTriangles) { // Define the names of known vertex channels const char *positionSemantic = "POSITION"; const char *vertexSemantic = "VERTEX"; const char *normalSemantic = "NORMAL"; const char *tangentSemantic = "TANGENT"; const char *binormalSemantic = "BINORMAL"; const char *texture1Semantic = "TEXCOORD"; const domInputLocalOffset_Array &inputs = colladaTriangles->getInput_array(); mMultiIndexOffset = inputs.getCount(); // inputs with offsets // There are two places collada can put links to our data // 1 - in the VERTEX, which is its way of saying follow a link to the vertex structure // then every geometry array you find there is the same size as the position array // 2 - a direct link to the channel from the primitive list. This tells us that there are // potentially more or less floats in those channels because there is some vertex re-use // or divergence in that data channel. For example, highly segmented uv set would produce a // larger array because for every physical vertex position thre might be 2 or more uv coords for (uint32_t i = 0; i < inputs.getCount(); i ++) { int currentOffset = inputs[i]->getOffset(); const char *currentSemantic = inputs[i]->getSemantic(); domSource * source = (domSource*) (domElement*) inputs[i]->getSource().getElement(); if (strcmp(vertexSemantic, currentSemantic) == 0) { mPositionOffset = currentOffset; } else if (strcmp(normalSemantic, currentSemantic) == 0) { mNormalOffset = currentOffset; mNormalFloats = &source->getFloat_array()->getValue(); } else if (strcmp(tangentSemantic, currentSemantic) == 0) { mTangentOffset = currentOffset; mTangentFloats = &source->getFloat_array()->getValue(); } else if (strcmp(binormalSemantic, currentSemantic) == 0) { mBinormalOffset = currentOffset; mBinormalFloats = &source->getFloat_array()->getValue(); } else if (strcmp(texture1Semantic, currentSemantic) == 0) { mTexture1Offset = currentOffset; mTexture1Floats = & source->getFloat_array()->getValue(); } } // There are multiple ways of getting to data, so follow them all domVertices * vertices = mMesh->getVertices(); const domInputLocal_Array &verticesInputs = vertices->getInput_array(); for (uint32_t i = 0; i < verticesInputs.getCount(); i ++) { const char *currentSemantic = verticesInputs[i]->getSemantic(); domSource * source = (domSource*) (domElement*) verticesInputs[i]->getSource().getElement(); if (strcmp(positionSemantic, currentSemantic) == 0) { mPositionFloats = & source->getFloat_array()->getValue(); // TODO: Querry this from the accessor in the future because // I supopose it's possible to have 4 floats if we hide something in w int numberOfFloatsPerPoint = 3; // We want to cllapse duplicate vertices, otherwise we could just unroll the tri list mVertexRemap.resize(source->getFloat_array()->getCount()/numberOfFloatsPerPoint); } else if (strcmp(normalSemantic, currentSemantic) == 0) { mNormalFloats = & source->getFloat_array()->getValue(); mNormalOffset = mPositionOffset; } else if (strcmp(tangentSemantic, currentSemantic) == 0) { mTangentFloats = & source->getFloat_array()->getValue(); mTangentOffset = mPositionOffset; } else if (strcmp(binormalSemantic, currentSemantic) == 0) { mBinormalFloats = & source->getFloat_array()->getValue(); mBinormalOffset = mPositionOffset; } else if (strcmp(texture1Semantic, currentSemantic) == 0) { mTexture1Floats = & source->getFloat_array()->getValue(); mTexture1Offset = mPositionOffset; } } } int ColladaGeometry::remapIndexAndStoreData(const domListOfUInts &colladaIndexList, int indexToRemap) { domUint positionIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mPositionOffset]; float posX = (*mPositionFloats)[positionIndex * mPositionsStride + 0]; float posY = (*mPositionFloats)[positionIndex * mPositionsStride + 1]; float posZ = (*mPositionFloats)[positionIndex * mPositionsStride + 2]; float normX = 0; float normY = 0; float normZ = 0; if (mNormalOffset != -1) { domUint normalIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mNormalOffset]; normX = (*mNormalFloats)[normalIndex * mNormalsStride + 0]; normY = (*mNormalFloats)[normalIndex * mNormalsStride + 1]; normZ = (*mNormalFloats)[normalIndex * mNormalsStride + 2]; } float tanX = 0; float tanY = 0; float tanZ = 0; if (mTangentOffset != -1) { domUint tangentIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mTangentOffset]; tanX = (*mTangentFloats)[tangentIndex * mTangentssStride + 0]; tanY = (*mTangentFloats)[tangentIndex * mTangentssStride + 1]; tanZ = (*mTangentFloats)[tangentIndex * mTangentssStride + 2]; } float binormX = 0; float binormY = 0; float binormZ = 0; if (mBinormalOffset != -1) { domUint binormalIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mNormalOffset]; binormX = (*mBinormalFloats)[binormalIndex * mBinormalsStride + 0]; binormY = (*mBinormalFloats)[binormalIndex * mBinormalsStride + 1]; binormZ = (*mBinormalFloats)[binormalIndex * mBinormalsStride + 2]; } float texCoordX = 0; float texCoordY = 0; if (mTexture1Offset != -1) { domUint texCoordIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mTexture1Offset]; texCoordX = (*mTexture1Floats)[texCoordIndex * mTextureCoordsStride + 0]; texCoordY = (*mTexture1Floats)[texCoordIndex * mTextureCoordsStride + 1]; } std::vector<uint32_t> &ithRemapList = mVertexRemap[positionIndex]; // We may have some potential vertices we can reuse // loop over all the potential candidates and see if any match our guy for (uint32_t i = 0; i < ithRemapList.size(); i ++) { int ithRemap = ithRemapList[i]; // compare existing vertex with the new one if ((*mPositions)[ithRemap * mPositionsStride + 0] != posX || (*mPositions)[ithRemap * mPositionsStride + 1] != posY || (*mPositions)[ithRemap * mPositionsStride + 2] != posZ) { continue; } // Now go over normals if (mNormalOffset != -1) { if ((*mNormals)[ithRemap * mNormalsStride + 0] != normX || (*mNormals)[ithRemap * mNormalsStride + 1] != normY || (*mNormals)[ithRemap * mNormalsStride + 2] != normZ) { continue; } } // Now go over tangents if (mTangentOffset != -1) { if ((*mTangents)[ithRemap * mTangentssStride + 0] != tanX || (*mTangents)[ithRemap * mTangentssStride + 1] != tanY || (*mTangents)[ithRemap * mTangentssStride + 2] != tanZ) { continue; } } // Now go over binormals if (mBinormalOffset != -1) { if ((*mBinormals)[ithRemap * mBinormalsStride + 0] != binormX || (*mBinormals)[ithRemap * mBinormalsStride + 1] != binormY || (*mBinormals)[ithRemap * mBinormalsStride + 2] != binormZ) { continue; } } // And texcoords if (mTexture1Offset != -1) { if ((*mTextureCoords)[ithRemap * mTextureCoordsStride + 0] != texCoordX || (*mTextureCoords)[ithRemap * mTextureCoordsStride + 1] != texCoordY) { continue; } } // If we got here the new vertex is identical to the one that we already stored return ithRemap; } // We did not encounter this vertex yet, store it and return its index mPositions->push_back(posX); mPositions->push_back(posY); mPositions->push_back(posZ); if (mNormalOffset != -1) { mNormals->push_back(normX); mNormals->push_back(normY); mNormals->push_back(normZ); } if (mTangentOffset != -1) { mTangents->push_back(tanX); mTangents->push_back(tanY); mTangents->push_back(tanZ); } if (mBinormalOffset != -1) { mBinormals->push_back(binormX); mBinormals->push_back(binormY); mBinormals->push_back(binormZ); } if (mTexture1Offset != -1) { mTextureCoords->push_back(texCoordX); mTextureCoords->push_back(texCoordY); } // We need to remember this mapping. Since we are storing floats, not vec3's, need to // divide by position size to get the right index int currentVertexIndex = (mPositions->size()/mPositionsStride) - 1; ithRemapList.push_back(currentVertexIndex); return currentVertexIndex; }