/*
* 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;
}