Alex Sakhartchouk | 17bd28b | 2011-02-11 17:51:44 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2006 Sony Computer Entertainment Inc. |
| 3 | * |
| 4 | * Licensed under the MIT Open Source License, for details please see license.txt or the website |
| 5 | * http://www.opensource.org/licenses/mit-license.php |
| 6 | * |
| 7 | */ |
| 8 | |
| 9 | #include "ColladaGeometry.h" |
| 10 | #include <iostream> |
| 11 | #include <sstream> |
| 12 | |
| 13 | ColladaGeometry::ColladaGeometry() : |
| 14 | mPositionFloats(NULL), mPositionOffset(-1), |
| 15 | mNormalFloats(NULL), mNormalOffset(-1), |
| 16 | mTangentFloats(NULL), mTangentOffset(-1), |
| 17 | mBinormalFloats(NULL), mBinormalOffset(-1), |
| 18 | mTexture1Floats(NULL), mTexture1Offset(-1), |
| 19 | mMultiIndexOffset(-1), |
| 20 | mPositionsStride(3), mNormalsStride(3), |
| 21 | mTextureCoordsStride(2), mTangentssStride(3), mBinormalsStride(3) { |
| 22 | |
| 23 | mConvertedMesh.appendChannel("position", mPositionsStride); |
| 24 | mConvertedMesh.appendChannel("normal", mNormalsStride); |
| 25 | mConvertedMesh.appendChannel("texture0", mTextureCoordsStride); |
| 26 | mConvertedMesh.appendChannel("binormal", mBinormalsStride); |
| 27 | mConvertedMesh.appendChannel("tangent", mTangentssStride); |
| 28 | |
| 29 | mPositions = &mConvertedMesh.mChannels[0].mData; |
| 30 | mNormals = &mConvertedMesh.mChannels[1].mData; |
| 31 | mTextureCoords = &mConvertedMesh.mChannels[2].mData; |
| 32 | mBinormals = &mConvertedMesh.mChannels[3].mData; |
| 33 | mTangents = &mConvertedMesh.mChannels[4].mData; |
| 34 | } |
| 35 | |
| 36 | bool ColladaGeometry::init(domGeometryRef geometry) { |
| 37 | |
| 38 | bool convertSuceeded = true; |
| 39 | |
| 40 | const char* geoName = geometry->getName(); |
| 41 | if (geoName == NULL) { |
| 42 | geoName = geometry->getId(); |
| 43 | } |
| 44 | mConvertedMesh.mName = geoName; |
| 45 | mMesh = geometry->getMesh(); |
| 46 | |
| 47 | // Iterate over all the index groups and build up a simple resolved tri list and vertex array |
| 48 | const domTriangles_Array &allTriLists = mMesh->getTriangles_array(); |
| 49 | int numTriLists = allTriLists.getCount(); |
| 50 | mConvertedMesh.mTriangleLists.reserve(numTriLists); |
| 51 | mConvertedMesh.mTriangleListNames.reserve(numTriLists); |
| 52 | for (int i = 0; i < numTriLists; i ++) { |
| 53 | addTriangles(allTriLists[i]); |
| 54 | } |
| 55 | |
| 56 | return convertSuceeded; |
| 57 | } |
| 58 | |
| 59 | void ColladaGeometry::addTriangles(domTriangles * colladaTriangles) { |
| 60 | |
| 61 | int numTriangles = colladaTriangles->getCount(); |
| 62 | int triListIndex = mConvertedMesh.mTriangleLists.size(); |
| 63 | mConvertedMesh.mTriangleLists.resize(triListIndex + 1); |
| 64 | std::string materialName = colladaTriangles->getMaterial(); |
| 65 | if (materialName.size() == 0) { |
| 66 | char buffer[128]; |
| 67 | sprintf(buffer, "index%d", triListIndex); |
| 68 | materialName = buffer; |
| 69 | } |
| 70 | mConvertedMesh.mTriangleListNames.push_back(materialName); |
| 71 | |
| 72 | // It's a good idea to tell stl how much memory we intend to use |
| 73 | // to limit the number of reallocations |
| 74 | mPositions->reserve(numTriangles * 3); |
| 75 | mNormals->reserve(numTriangles * 3); |
| 76 | mTangents->reserve(numTriangles * 3); |
| 77 | mBinormals->reserve(numTriangles * 3); |
| 78 | mTextureCoords->reserve(numTriangles * 3); |
| 79 | |
| 80 | // Stores the pointers to the image data and where in the tri list that data comes from |
| 81 | cacheOffsetsAndDataPointers(colladaTriangles); |
| 82 | |
| 83 | // Collapse the multiindex that collada uses |
| 84 | const domListOfUInts &colladaIndexList = colladaTriangles->getP()->getValue(); |
| 85 | std::vector<uint32_t> &a3dIndexList = mConvertedMesh.mTriangleLists[triListIndex]; |
| 86 | a3dIndexList.resize(numTriangles * 3); |
| 87 | for (int i = 0; i < numTriangles * 3; i ++) { |
| 88 | |
| 89 | a3dIndexList[i] = remapIndexAndStoreData(colladaIndexList, i); |
| 90 | } |
| 91 | |
| 92 | } |
| 93 | |
| 94 | void ColladaGeometry::cacheOffsetsAndDataPointers(domTriangles * colladaTriangles) { |
| 95 | // Define the names of known vertex channels |
| 96 | const char *positionSemantic = "POSITION"; |
| 97 | const char *vertexSemantic = "VERTEX"; |
| 98 | const char *normalSemantic = "NORMAL"; |
| 99 | const char *tangentSemantic = "TANGENT"; |
| 100 | const char *binormalSemantic = "BINORMAL"; |
| 101 | const char *texture1Semantic = "TEXCOORD"; |
| 102 | |
| 103 | const domInputLocalOffset_Array &inputs = colladaTriangles->getInput_array(); |
| 104 | mMultiIndexOffset = inputs.getCount(); |
| 105 | |
| 106 | // inputs with offsets |
| 107 | // There are two places collada can put links to our data |
| 108 | // 1 - in the VERTEX, which is its way of saying follow a link to the vertex structure |
| 109 | // then every geometry array you find there is the same size as the position array |
| 110 | // 2 - a direct link to the channel from the primitive list. This tells us that there are |
| 111 | // potentially more or less floats in those channels because there is some vertex re-use |
| 112 | // or divergence in that data channel. For example, highly segmented uv set would produce a |
| 113 | // larger array because for every physical vertex position thre might be 2 or more uv coords |
| 114 | for (uint32_t i = 0; i < inputs.getCount(); i ++) { |
| 115 | |
| 116 | int currentOffset = inputs[i]->getOffset(); |
| 117 | const char *currentSemantic = inputs[i]->getSemantic(); |
| 118 | |
| 119 | domSource * source = (domSource*) (domElement*) inputs[i]->getSource().getElement(); |
| 120 | if (strcmp(vertexSemantic, currentSemantic) == 0) { |
| 121 | mPositionOffset = currentOffset; |
| 122 | } |
| 123 | else if (strcmp(normalSemantic, currentSemantic) == 0) { |
| 124 | mNormalOffset = currentOffset; |
| 125 | mNormalFloats = &source->getFloat_array()->getValue(); |
| 126 | } |
| 127 | else if (strcmp(tangentSemantic, currentSemantic) == 0) { |
| 128 | mTangentOffset = currentOffset; |
| 129 | mTangentFloats = &source->getFloat_array()->getValue(); |
| 130 | } |
| 131 | else if (strcmp(binormalSemantic, currentSemantic) == 0) { |
| 132 | mBinormalOffset = currentOffset; |
| 133 | mBinormalFloats = &source->getFloat_array()->getValue(); |
| 134 | } |
| 135 | else if (strcmp(texture1Semantic, currentSemantic) == 0) { |
| 136 | mTexture1Offset = currentOffset; |
| 137 | mTexture1Floats = & source->getFloat_array()->getValue(); |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | // There are multiple ways of getting to data, so follow them all |
| 142 | domVertices * vertices = mMesh->getVertices(); |
| 143 | const domInputLocal_Array &verticesInputs = vertices->getInput_array(); |
| 144 | for (uint32_t i = 0; i < verticesInputs.getCount(); i ++) { |
| 145 | |
| 146 | const char *currentSemantic = verticesInputs[i]->getSemantic(); |
| 147 | |
| 148 | domSource * source = (domSource*) (domElement*) verticesInputs[i]->getSource().getElement(); |
| 149 | if (strcmp(positionSemantic, currentSemantic) == 0) { |
| 150 | mPositionFloats = & source->getFloat_array()->getValue(); |
| 151 | // TODO: Querry this from the accessor in the future because |
| 152 | // I supopose it's possible to have 4 floats if we hide something in w |
| 153 | int numberOfFloatsPerPoint = 3; |
| 154 | // We want to cllapse duplicate vertices, otherwise we could just unroll the tri list |
| 155 | mVertexRemap.resize(source->getFloat_array()->getCount()/numberOfFloatsPerPoint); |
| 156 | } |
| 157 | else if (strcmp(normalSemantic, currentSemantic) == 0) { |
| 158 | mNormalFloats = & source->getFloat_array()->getValue(); |
| 159 | mNormalOffset = mPositionOffset; |
| 160 | } |
| 161 | else if (strcmp(tangentSemantic, currentSemantic) == 0) { |
| 162 | mTangentFloats = & source->getFloat_array()->getValue(); |
| 163 | mTangentOffset = mPositionOffset; |
| 164 | } |
| 165 | else if (strcmp(binormalSemantic, currentSemantic) == 0) { |
| 166 | mBinormalFloats = & source->getFloat_array()->getValue(); |
| 167 | mBinormalOffset = mPositionOffset; |
| 168 | } |
| 169 | else if (strcmp(texture1Semantic, currentSemantic) == 0) { |
| 170 | mTexture1Floats = & source->getFloat_array()->getValue(); |
| 171 | mTexture1Offset = mPositionOffset; |
| 172 | } |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | int ColladaGeometry::remapIndexAndStoreData(const domListOfUInts &colladaIndexList, int indexToRemap) { |
| 177 | |
| 178 | domUint positionIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mPositionOffset]; |
| 179 | |
| 180 | float posX = (*mPositionFloats)[positionIndex * mPositionsStride + 0]; |
| 181 | float posY = (*mPositionFloats)[positionIndex * mPositionsStride + 1]; |
| 182 | float posZ = (*mPositionFloats)[positionIndex * mPositionsStride + 2]; |
| 183 | |
| 184 | float normX = 0; |
| 185 | float normY = 0; |
| 186 | float normZ = 0; |
| 187 | |
| 188 | if (mNormalOffset != -1) { |
| 189 | domUint normalIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mNormalOffset]; |
| 190 | normX = (*mNormalFloats)[normalIndex * mNormalsStride + 0]; |
| 191 | normY = (*mNormalFloats)[normalIndex * mNormalsStride + 1]; |
| 192 | normZ = (*mNormalFloats)[normalIndex * mNormalsStride + 2]; |
| 193 | } |
| 194 | |
| 195 | float tanX = 0; |
| 196 | float tanY = 0; |
| 197 | float tanZ = 0; |
| 198 | |
| 199 | if (mTangentOffset != -1) { |
| 200 | domUint tangentIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mTangentOffset]; |
| 201 | tanX = (*mTangentFloats)[tangentIndex * mTangentssStride + 0]; |
| 202 | tanY = (*mTangentFloats)[tangentIndex * mTangentssStride + 1]; |
| 203 | tanZ = (*mTangentFloats)[tangentIndex * mTangentssStride + 2]; |
| 204 | } |
| 205 | |
| 206 | float binormX = 0; |
| 207 | float binormY = 0; |
| 208 | float binormZ = 0; |
| 209 | |
| 210 | if (mBinormalOffset != -1) { |
| 211 | domUint binormalIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mNormalOffset]; |
| 212 | binormX = (*mBinormalFloats)[binormalIndex * mBinormalsStride + 0]; |
| 213 | binormY = (*mBinormalFloats)[binormalIndex * mBinormalsStride + 1]; |
| 214 | binormZ = (*mBinormalFloats)[binormalIndex * mBinormalsStride + 2]; |
| 215 | } |
| 216 | |
| 217 | float texCoordX = 0; |
| 218 | float texCoordY = 0; |
| 219 | |
| 220 | if (mTexture1Offset != -1) { |
| 221 | domUint texCoordIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mTexture1Offset]; |
| 222 | texCoordX = (*mTexture1Floats)[texCoordIndex * mTextureCoordsStride + 0]; |
| 223 | texCoordY = (*mTexture1Floats)[texCoordIndex * mTextureCoordsStride + 1]; |
| 224 | } |
| 225 | |
| 226 | std::vector<uint32_t> &ithRemapList = mVertexRemap[positionIndex]; |
| 227 | // We may have some potential vertices we can reuse |
| 228 | // loop over all the potential candidates and see if any match our guy |
| 229 | for (uint32_t i = 0; i < ithRemapList.size(); i ++) { |
| 230 | |
| 231 | int ithRemap = ithRemapList[i]; |
| 232 | // compare existing vertex with the new one |
| 233 | if ((*mPositions)[ithRemap * mPositionsStride + 0] != posX || |
| 234 | (*mPositions)[ithRemap * mPositionsStride + 1] != posY || |
| 235 | (*mPositions)[ithRemap * mPositionsStride + 2] != posZ) { |
| 236 | continue; |
| 237 | } |
| 238 | |
| 239 | // Now go over normals |
| 240 | if (mNormalOffset != -1) { |
| 241 | if ((*mNormals)[ithRemap * mNormalsStride + 0] != normX || |
| 242 | (*mNormals)[ithRemap * mNormalsStride + 1] != normY || |
| 243 | (*mNormals)[ithRemap * mNormalsStride + 2] != normZ) { |
| 244 | continue; |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | // Now go over tangents |
| 249 | if (mTangentOffset != -1) { |
| 250 | if ((*mTangents)[ithRemap * mTangentssStride + 0] != tanX || |
| 251 | (*mTangents)[ithRemap * mTangentssStride + 1] != tanY || |
| 252 | (*mTangents)[ithRemap * mTangentssStride + 2] != tanZ) { |
| 253 | continue; |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | // Now go over binormals |
| 258 | if (mBinormalOffset != -1) { |
| 259 | if ((*mBinormals)[ithRemap * mBinormalsStride + 0] != binormX || |
| 260 | (*mBinormals)[ithRemap * mBinormalsStride + 1] != binormY || |
| 261 | (*mBinormals)[ithRemap * mBinormalsStride + 2] != binormZ) { |
| 262 | continue; |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | // And texcoords |
| 267 | if (mTexture1Offset != -1) { |
| 268 | if ((*mTextureCoords)[ithRemap * mTextureCoordsStride + 0] != texCoordX || |
| 269 | (*mTextureCoords)[ithRemap * mTextureCoordsStride + 1] != texCoordY) { |
| 270 | continue; |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | // If we got here the new vertex is identical to the one that we already stored |
| 275 | return ithRemap; |
| 276 | } |
| 277 | |
| 278 | // We did not encounter this vertex yet, store it and return its index |
| 279 | mPositions->push_back(posX); |
| 280 | mPositions->push_back(posY); |
| 281 | mPositions->push_back(posZ); |
| 282 | |
| 283 | if (mNormalOffset != -1) { |
| 284 | mNormals->push_back(normX); |
| 285 | mNormals->push_back(normY); |
| 286 | mNormals->push_back(normZ); |
| 287 | } |
| 288 | |
| 289 | if (mTangentOffset != -1) { |
| 290 | mTangents->push_back(tanX); |
| 291 | mTangents->push_back(tanY); |
| 292 | mTangents->push_back(tanZ); |
| 293 | } |
| 294 | |
| 295 | if (mBinormalOffset != -1) { |
| 296 | mBinormals->push_back(binormX); |
| 297 | mBinormals->push_back(binormY); |
| 298 | mBinormals->push_back(binormZ); |
| 299 | } |
| 300 | |
| 301 | if (mTexture1Offset != -1) { |
| 302 | mTextureCoords->push_back(texCoordX); |
| 303 | mTextureCoords->push_back(texCoordY); |
| 304 | } |
| 305 | |
| 306 | // We need to remember this mapping. Since we are storing floats, not vec3's, need to |
| 307 | // divide by position size to get the right index |
| 308 | int currentVertexIndex = (mPositions->size()/mPositionsStride) - 1; |
| 309 | ithRemapList.push_back(currentVertexIndex); |
| 310 | |
| 311 | return currentVertexIndex; |
| 312 | } |
| 313 | |
| 314 | |
| 315 | |
| 316 | |
| 317 | |
| 318 | |
| 319 | |