/* Convert an OpenCOLLADA's FloatOrDoubleArray type to a GLTFBufferView Note: the resulting GLTFBufferView is not typed, it's the call responsability to keep track of the type if needed. */ shared_ptr <GLTFBufferView> convertFloatOrDoubleArrayToGLTFBufferView(const COLLADAFW::FloatOrDoubleArray &floatOrDoubleArray) { unsigned char* sourceData = 0; size_t sourceSize = 0; switch (floatOrDoubleArray.getType()) { case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: { const COLLADAFW::FloatArray* array = floatOrDoubleArray.getFloatValues(); sourceData = (unsigned char*)array->getData(); sourceSize = array->getCount() * sizeof(float); } break; case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: { const COLLADAFW::DoubleArray* array = floatOrDoubleArray.getDoubleValues(); sourceData = (unsigned char*)array->getData(); sourceSize = array->getCount() * sizeof(double); } break; default: case COLLADAFW::MeshVertexData::DATA_TYPE_UNKNOWN: //FIXME report error break; } unsigned char* copiedData = (unsigned char*)malloc(sourceSize); memcpy(copiedData, sourceData, sourceSize); shared_ptr <GLTF::GLTFBufferView> bufferView = createBufferViewWithAllocatedBuffer(copiedData, 0, sourceSize, true); return bufferView; }
//FIXME: these 3 functions up there could use some refactoring shared_ptr <GLTFBufferView> convertIntArrayToGLTFBufferView(const COLLADAFW::IntValuesArray &array) { unsigned char* sourceData = (unsigned char*)array.getData(); size_t sourceSize = array.getCount() * sizeof(int); unsigned char* copiedData = (unsigned char*)malloc(sourceSize); memcpy(copiedData, sourceData, sourceSize); shared_ptr <GLTF::GLTFBufferView> bufferView = createBufferViewWithAllocatedBuffer(copiedData, 0, sourceSize, true); return bufferView; }
shared_ptr<GLTF::GLTFPrimitiveRemapInfos> __BuildPrimitiveUniqueIndexes(shared_ptr<GLTF::GLTFPrimitive> primitive, std::vector< shared_ptr<GLTF::GLTFIndices> > allIndices, RemappedMeshIndexesHashmap& remappedMeshIndexesMap, unsigned int* indicesInRemapping, size_t startIndex, unsigned int meshAttributesCount, size_t &endIndex) { unsigned int generatedIndicesCount = 0; size_t allIndicesSize = allIndices.size(); size_t vertexIndicesCount = allIndices[0]->getCount(); size_t sizeOfRemappedIndex = (meshAttributesCount + 1) * sizeof(unsigned int); unsigned int* originalCountAndIndexes = (unsigned int*)calloc( vertexIndicesCount, sizeOfRemappedIndex); //this is useful for debugging. unsigned int *uniqueIndexes = (unsigned int*)calloc( vertexIndicesCount , sizeof(unsigned int)); unsigned int *generatedIndices = (unsigned int*) calloc (vertexIndicesCount , sizeof(unsigned int)); //owned by PrimitiveRemapInfos unsigned int currentIndex = startIndex; for (size_t k = 0 ; k < vertexIndicesCount ; k++) { unsigned int* remappedIndex = &originalCountAndIndexes[k * (meshAttributesCount + 1)]; remappedIndex[0] = meshAttributesCount; for (unsigned int i = 0 ; i < allIndicesSize ; i++) { unsigned int idx = indicesInRemapping[i]; unsigned int* indicesPtr = (unsigned int*)allIndices[i]->getBufferView()->getBufferDataByApplyingOffset(); remappedIndex[1 + idx] = indicesPtr[k]; } unsigned int index; if (remappedMeshIndexesMap.count(remappedIndex) == 0) { index = currentIndex++; generatedIndices[generatedIndicesCount++] = (unsigned int)k; remappedMeshIndexesMap[remappedIndex] = index; } else { index = remappedMeshIndexesMap[remappedIndex]; } uniqueIndexes[k] = index; } endIndex = currentIndex; shared_ptr <GLTF::GLTFPrimitiveRemapInfos> primitiveRemapInfos(new GLTF::GLTFPrimitiveRemapInfos(generatedIndices, generatedIndicesCount, originalCountAndIndexes)); shared_ptr <GLTF::GLTFBufferView> indicesBufferView = createBufferViewWithAllocatedBuffer(uniqueIndexes, 0, vertexIndicesCount * sizeof(unsigned int), true); shared_ptr <GLTF::GLTFIndices> indices = shared_ptr <GLTF::GLTFIndices> (new GLTF::GLTFIndices(indicesBufferView, vertexIndicesCount)); primitive->setIndices(indices); return primitiveRemapInfos; }
static void __HandleIndexList(unsigned int idx, COLLADAFW::IndexList *indexList, Semantic semantic, bool shouldTriangulate, unsigned int count, unsigned int vcount, unsigned int *verticesCountArray, shared_ptr <GLTF::GLTFPrimitive> cvtPrimitive, IndicesVector &primitiveIndicesVector ) { unsigned int triangulatedIndicesCount = 0; bool ownData = false; unsigned int *indices = indexList->getIndices().getData(); if (shouldTriangulate) { indices = createTrianglesFromPolylist(verticesCountArray, indices, vcount, &triangulatedIndicesCount); count = triangulatedIndicesCount; ownData = true; } //Why is OpenCOLLADA doing this ? why adding an offset the indices ?? //We need to offset it backward here. unsigned int initialIndex = indexList->getInitialIndex(); if (initialIndex != 0) { unsigned int *bufferDestination = 0; if (!ownData) { bufferDestination = (unsigned int*)malloc(sizeof(unsigned int) * count); ownData = true; } else { bufferDestination = indices; } for (size_t idx = 0 ; idx < count ; idx++) { bufferDestination[idx] = indices[idx] - initialIndex; } indices = bufferDestination; } shared_ptr <GLTF::GLTFBufferView> uvBuffer = createBufferViewWithAllocatedBuffer(indices, 0, count * sizeof(unsigned int), ownData); //FIXME: Looks like for texcoord indexSet begin at 1, this is out of the sync with the index used in ConvertOpenCOLLADAMeshVertexDataToGLTFMeshAttributes that begins at 0 //for now forced to 0, to be fixed for multi texturing. //unsigned int idx = (unsigned int)indexList->getSetIndex(); shared_ptr <GLTFIndices> jsonIndices(new GLTFIndices(uvBuffer, count)); __AppendIndices(cvtPrimitive, primitiveIndicesVector, jsonIndices, semantic, idx); }
static void __HandleIndexList(unsigned int idx, COLLADAFW::IndexList *indexList, Semantic semantic, bool shouldTriangulate, unsigned int count, unsigned int vcount, unsigned int *verticesCountArray, shared_ptr <GLTF::GLTFPrimitive> cvtPrimitive, IndicesVector &primitiveIndicesVector, shared_ptr<GLTFProfile> profile) { unsigned int triangulatedIndicesCount = 0; bool ownData = false; unsigned int *indices = indexList->getIndices().getData(); if (shouldTriangulate) { indices = createTrianglesFromPolylist(verticesCountArray, indices, vcount, &triangulatedIndicesCount); count = triangulatedIndicesCount; ownData = true; } //Why is OpenCOLLADA doing this ? why adding an offset the indices ?? //We need to offset it backward here. unsigned int initialIndex = (unsigned int)indexList->getInitialIndex(); if (initialIndex != 0) { unsigned int *bufferDestination = 0; if (!ownData) { bufferDestination = (unsigned int*)malloc(sizeof(unsigned int) * count); ownData = true; } else { bufferDestination = indices; } for (size_t idx = 0 ; idx < count ; idx++) { bufferDestination[idx] = indices[idx] - initialIndex; } indices = bufferDestination; } shared_ptr <GLTF::GLTFBufferView> uvBuffer = createBufferViewWithAllocatedBuffer(indices, 0, count * sizeof(unsigned int), ownData); shared_ptr <GLTFAccessor> accessor(new GLTFAccessor(profile, profile->getGLenumForString("UNSIGNED_SHORT"))); accessor->setBufferView(uvBuffer); accessor->setCount(count); __AppendIndices(cvtPrimitive, primitiveIndicesVector, accessor, semantic, idx); }
/* Since GLTF does not have the same granularity as COLLADA, we need in some situations to duplicate a few datas. For instance a target path like position.x is not supported by glTF, just the 3 components (x,y,z) can be animated. This function create a buffer that will allow to handle a target path supported by GLTF. */ static shared_ptr<GLTFBufferView> __CreateBufferViewByReplicatingArrayAndReplacingValueAtIndex(shared_ptr<GLTFBufferView> bufferView, shared_ptr<JSONArray> array, size_t index, std::string type, size_t keyCount) { char *destinationBuffer = 0; char *sourceBuffer = (char*)bufferView->getBufferDataByApplyingOffset(); size_t elementSize = 0; size_t offset = 0; //TODO handle other types if (type == "FLOAT") { elementSize = sizeof(float); offset = (elementSize * array->values().size()); } size_t destinationBufferLength = offset * keyCount; if (elementSize != 0) { //FIXME: should not assume FLOAT here size_t count = array->values().size(); float *values = (float*)malloc(elementSize * count); for (size_t i = 0 ; i < count ; i++) { shared_ptr <JSONNumber> nb = static_pointer_cast<JSONNumber>(array->values()[i]); values[i] = (float)nb->getDouble(); } destinationBuffer = (char*)malloc(destinationBufferLength); for (size_t i = 0 ; i < keyCount ; i++) { memcpy(destinationBuffer + (offset * i), values, offset); memcpy(destinationBuffer + (offset * i) + (index * elementSize) , sourceBuffer + (i * elementSize) , elementSize); } free(values); } else { //TODO:.. printf("WARNING attempt to use __CreateBufferViewByReplicatingArrayAndReplacingValueAtIndex without using floats\n"); } return createBufferViewWithAllocatedBuffer(destinationBuffer, 0, destinationBufferLength, true); }
//FIXME: add suport for interleaved arrays void __RemapSubMesh(SubMeshContext *subMesh, GLTFMesh *sourceMesh) { //remap the subMesh using the original mesh //we walk through all meshAttributes vector <GLTF::Semantic> allSemantics = sourceMesh->allSemantics(); std::map<string, unsigned int> semanticAndSetToIndex; for (unsigned int i = 0 ; i < allSemantics.size() ; i++) { IndexSetToMeshAttributeHashmap& indexSetToMeshAttribute = sourceMesh->getMeshAttributesForSemantic(allSemantics[i]); IndexSetToMeshAttributeHashmap& targetIndexSetToMeshAttribute = subMesh->targetMesh->getMeshAttributesForSemantic(allSemantics[i]); IndexSetToMeshAttributeHashmap::const_iterator meshAttributeIterator; for (meshAttributeIterator = indexSetToMeshAttribute.begin() ; meshAttributeIterator != indexSetToMeshAttribute.end() ; meshAttributeIterator++) { //(*it).first; // the key value (of type Key) //(*it).second; // the mapped value (of type T) shared_ptr <GLTFMeshAttribute> selectedMeshAttribute = (*meshAttributeIterator).second; unsigned int indexSet = (*meshAttributeIterator).first; shared_ptr <GLTFBufferView> referenceBufferView = selectedMeshAttribute->getBufferView(); unsigned int vertexAttributeCount = subMesh->indexToRemappedIndex.size(); //FIXME: this won't work with interleaved unsigned int *targetBufferPtr = (unsigned int*)malloc(selectedMeshAttribute->getVertexAttributeByteLength() * vertexAttributeCount); void *context[2]; context[0] = targetBufferPtr; context[1] = subMesh; selectedMeshAttribute->apply(__RemapMeshAttribute, (void*)context); shared_ptr <GLTFBufferView> remappedBufferView = createBufferViewWithAllocatedBuffer(referenceBufferView->getID(), targetBufferPtr, 0, selectedMeshAttribute->getVertexAttributeByteLength() * vertexAttributeCount, true); shared_ptr <GLTFMeshAttribute> remappedMeshAttribute(new GLTF::GLTFMeshAttribute(selectedMeshAttribute.get())); remappedMeshAttribute->setBufferView(remappedBufferView); remappedMeshAttribute->setCount(vertexAttributeCount); targetIndexSetToMeshAttribute[indexSet] = remappedMeshAttribute; } } }
static shared_ptr <GLTF::GLTFPrimitive> ConvertOpenCOLLADAMeshPrimitive( COLLADAFW::MeshPrimitive *openCOLLADAMeshPrimitive, IndicesVector &primitiveIndicesVector) { shared_ptr <GLTF::GLTFPrimitive> cvtPrimitive(new GLTF::GLTFPrimitive()); // We want to match OpenGL/ES mode , as WebGL spec points to OpenGL/ES spec... // "Symbolic constants GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, and GL_TRIANGLES are accepted." std::string type; bool shouldTriangulate = false; switch(openCOLLADAMeshPrimitive->getPrimitiveType()) { //these 2 requires transforms case COLLADAFW::MeshPrimitive::POLYLIST: case COLLADAFW::MeshPrimitive::POLYGONS: // FIXME: perform conversion, but until not done report error //these mode are supported by WebGL shouldTriangulate = true; //force triangles type = "TRIANGLES"; break; case COLLADAFW::MeshPrimitive::LINES: type = "LINES"; break; case COLLADAFW::MeshPrimitive::LINE_STRIPS: type = "LINE_STRIP"; break; case COLLADAFW::MeshPrimitive::TRIANGLES: type = "TRIANGLES"; break; case COLLADAFW::MeshPrimitive::TRIANGLE_FANS: type = "TRIANGLE_FANS"; break; case COLLADAFW::MeshPrimitive::TRIANGLE_STRIPS: type = "TRIANGLE_STRIPS"; break; case COLLADAFW::MeshPrimitive::POINTS: type = "POINTS"; break; default: break; } cvtPrimitive->setMaterialObjectID((unsigned int)openCOLLADAMeshPrimitive->getMaterialId()); cvtPrimitive->setType(type); //count of indices , it must be the same for all kind of indices size_t count = openCOLLADAMeshPrimitive->getPositionIndices().getCount(); //vertex //IndexList &positionIndexList = openCOLLADAMeshPrimitive->getPositionIndices(); unsigned int *indices = openCOLLADAMeshPrimitive->getPositionIndices().getData(); unsigned int *verticesCountArray = 0; unsigned int vcount = 0; //count of elements in the array containing the count of indices per polygon & polylist. if (shouldTriangulate) { unsigned int triangulatedIndicesCount = 0; //We have to upcast to polygon to retrieve the array of vertexCount //OpenCOLLADA use polylist as polygon. COLLADAFW::Polygons *polygon = (COLLADAFW::Polygons*)openCOLLADAMeshPrimitive; const COLLADAFW::Polygons::VertexCountArray& vertexCountArray = polygon->getGroupedVerticesVertexCountArray(); vcount = (unsigned int)vertexCountArray.getCount(); verticesCountArray = (unsigned int*)malloc(sizeof(unsigned int) * vcount); for (size_t i = 0; i < vcount; i++) { verticesCountArray[i] = polygon->getGroupedVerticesVertexCount(i);; } indices = createTrianglesFromPolylist(verticesCountArray, indices, vcount, &triangulatedIndicesCount); count = triangulatedIndicesCount; } shared_ptr <GLTFBufferView> positionBuffer = createBufferViewWithAllocatedBuffer(indices, 0, count * sizeof(unsigned int), shouldTriangulate ? true : false); shared_ptr <GLTF::GLTFIndices> positionIndices(new GLTF::GLTFIndices(positionBuffer,count)); __AppendIndices(cvtPrimitive, primitiveIndicesVector, positionIndices, POSITION, 0); if (openCOLLADAMeshPrimitive->hasNormalIndices()) { unsigned int triangulatedIndicesCount = 0; indices = openCOLLADAMeshPrimitive->getNormalIndices().getData(); if (shouldTriangulate) { indices = createTrianglesFromPolylist(verticesCountArray, indices, vcount, &triangulatedIndicesCount); count = triangulatedIndicesCount; } shared_ptr <GLTF::GLTFBufferView> normalBuffer = createBufferViewWithAllocatedBuffer(indices, 0, count * sizeof(unsigned int), shouldTriangulate ? true : false); shared_ptr <GLTF::GLTFIndices> normalIndices(new GLTF::GLTFIndices(normalBuffer, count)); __AppendIndices(cvtPrimitive, primitiveIndicesVector, normalIndices, NORMAL, 0); } if (openCOLLADAMeshPrimitive->hasColorIndices()) { COLLADAFW::IndexListArray& colorListArray = openCOLLADAMeshPrimitive->getColorIndicesArray(); for (size_t i = 0 ; i < colorListArray.getCount() ; i++) { COLLADAFW::IndexList* indexList = openCOLLADAMeshPrimitive->getColorIndices(i); __HandleIndexList(i, indexList, GLTF::COLOR, shouldTriangulate, count, vcount, verticesCountArray, cvtPrimitive, primitiveIndicesVector); } } if (openCOLLADAMeshPrimitive->hasUVCoordIndices()) { COLLADAFW::IndexListArray& uvListArray = openCOLLADAMeshPrimitive->getUVCoordIndicesArray(); for (size_t i = 0 ; i < uvListArray.getCount() ; i++) { COLLADAFW::IndexList* indexList = openCOLLADAMeshPrimitive->getUVCoordIndices(i); __HandleIndexList(i, indexList, GLTF::TEXCOORD, shouldTriangulate, count, vcount, verticesCountArray, cvtPrimitive, primitiveIndicesVector); } } if (verticesCountArray) { free(verticesCountArray); } return cvtPrimitive; }
static unsigned int ConvertOpenCOLLADAMeshVertexDataToGLTFMeshAttributes(const COLLADAFW::MeshVertexData &vertexData, GLTF::IndexSetToMeshAttributeHashmap &meshAttributes) { // The following are OpenCOLLADA fmk issues preventing doing a totally generic processing of sources //1. "set"(s) other than texCoord don't have valid input infos //2. not the original id in the source std::string name; size_t length, elementsCount; size_t stride = 0; size_t size = 0; size_t byteOffset = 0; size_t inputLength = 0; size_t setCount = vertexData.getNumInputInfos(); bool unpatchedOpenCOLLADA = (setCount == 0); // reliable heuristic to know if the input have not been set if (unpatchedOpenCOLLADA) setCount = 1; for (size_t indexOfSet = 0 ; indexOfSet < setCount ; indexOfSet++) { if (!unpatchedOpenCOLLADA) { name = vertexData.getName(indexOfSet); size = vertexData.getStride(indexOfSet); inputLength = vertexData.getLength(indexOfSet); } else { // for unpatched version of OpenCOLLADA we need this work-around. name = GLTF::GLTFUtils::generateIDForType("buffer").c_str(); size = 3; //only normal and positions should reach this code inputLength = vertexData.getLength(0); } //name is the id length = inputLength ? inputLength : vertexData.getValuesCount(); elementsCount = length / size; unsigned char *sourceData = 0; size_t sourceSize = 0; GLTF::ComponentType componentType = GLTF::NOT_AN_ELEMENT_TYPE; switch (vertexData.getType()) { case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: { componentType = GLTF::FLOAT; stride = sizeof(float) * size; const COLLADAFW::FloatArray* array = vertexData.getFloatValues(); sourceData = (unsigned char*)array->getData() + byteOffset; sourceSize = length * sizeof(float); byteOffset += sourceSize; //Doh! - OpenCOLLADA store all sets contiguously in the same array } break; case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: { /* sourceType = DOUBLE; const DoubleArray& array = vertexData.getDoubleValues()[indexOfSet]; const size_t count = array.getCount(); sourceData = (void*)array.getData(); sourceSize = count * sizeof(double); */ // Warning if can't make "safe" conversion } break; default: case COLLADAFW::MeshVertexData::DATA_TYPE_UNKNOWN: //FIXME report error break; } // FIXME: the source could be shared, store / retrieve it here shared_ptr <GLTFBufferView> cvtBufferView = createBufferViewWithAllocatedBuffer(name, sourceData, 0, sourceSize, false); shared_ptr <GLTFMeshAttribute> cvtMeshAttribute(new GLTFMeshAttribute()); cvtMeshAttribute->setBufferView(cvtBufferView); cvtMeshAttribute->setComponentsPerAttribute(size); cvtMeshAttribute->setByteStride(stride); cvtMeshAttribute->setComponentType(componentType); cvtMeshAttribute->setCount(elementsCount); meshAttributes[(unsigned int)indexOfSet] = cvtMeshAttribute; } return (unsigned int)setCount; }
static unsigned int __ConvertOpenCOLLADAMeshVertexDataToGLTFAccessors(const COLLADAFW::MeshVertexData &vertexData, GLTFMesh* mesh, GLTF::Semantic semantic, size_t allowedComponentsPerAttribute, shared_ptr<GLTFProfile> profile) { // The following are OpenCOLLADA fmk issues preventing doing a totally generic processing of sources //1. "set"(s) other than texCoord don't have valid input infos //2. not the original id in the source std::string id; size_t length, elementsCount; size_t stride = 0; size_t componentsPerElement = 0; size_t byteOffset = 0; size_t inputLength = 0; size_t setCount = vertexData.getNumInputInfos(); bool unpatchedOpenCOLLADA = (setCount == 0); // reliable heuristic to know if the input have not been set if (unpatchedOpenCOLLADA) setCount = 1; for (size_t indexOfSet = 0 ; indexOfSet < setCount ; indexOfSet++) { bool meshAttributeOwnsBuffer = false; if (!unpatchedOpenCOLLADA) { id = vertexData.getName(indexOfSet); componentsPerElement = vertexData.getStride(indexOfSet); inputLength = vertexData.getLength(indexOfSet); } else { // for unpatched version of OpenCOLLADA we need this work-around. id = GLTF::GLTFUtils::generateIDForType("buffer").c_str(); componentsPerElement = 3; //only normal and positions should reach this code inputLength = vertexData.getLength(0); } length = inputLength ? inputLength : vertexData.getValuesCount(); elementsCount = length / componentsPerElement; unsigned char *sourceData = 0; size_t sourceSize = 0; GLTF::ComponentType componentType = GLTF::NOT_AN_ELEMENT_TYPE; switch (vertexData.getType()) { case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: { componentType = GLTF::FLOAT; stride = sizeof(float) * componentsPerElement; const COLLADAFW::FloatArray* array = vertexData.getFloatValues(); sourceData = (unsigned char*)array->getData() + byteOffset; sourceSize = length * sizeof(float); byteOffset += sourceSize; //Doh! - OpenCOLLADA store all sets contiguously in the same array } break; case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: { //FIXME: handle this /* sourceType = DOUBLE; const DoubleArray& array = vertexData.getDoubleValues()[indexOfSet]; const size_t count = array.getCount(); sourceData = (void*)array.getData(); sourceSize = length * sizeof(double); */ // Warning if can't make "safe" conversion } break; default: case COLLADAFW::MeshVertexData::DATA_TYPE_UNKNOWN: //FIXME report error break; } //FIXME: this is assuming float if (allowedComponentsPerAttribute != componentsPerElement) { sourceSize = elementsCount * sizeof(float) * allowedComponentsPerAttribute; float *adjustedSource = (float*)malloc(sourceSize); float *originalSource = (float*)sourceData; size_t adjustedStride = sizeof(float) * allowedComponentsPerAttribute; if (allowedComponentsPerAttribute < componentsPerElement) { for (size_t i = 0 ; i < elementsCount ; i++) { for (size_t j= 0 ; j < allowedComponentsPerAttribute ; j++) { adjustedSource[(i*allowedComponentsPerAttribute) + j] = originalSource[(i*componentsPerElement) + j]; } } } else { //FIXME: unlikely but should be taken care of } //Free source before replacing it if (meshAttributeOwnsBuffer) { free(sourceData); } componentsPerElement = allowedComponentsPerAttribute; meshAttributeOwnsBuffer = true; sourceData = (unsigned char*)adjustedSource; stride = adjustedStride; } // FIXME: the source could be shared, store / retrieve it here shared_ptr <GLTFBufferView> cvtBufferView = createBufferViewWithAllocatedBuffer(id, sourceData, 0, sourceSize, meshAttributeOwnsBuffer); shared_ptr <GLTFAccessor> cvtMeshAttribute(new GLTFAccessor(profile, profile->getGLTypeForComponentType(componentType, componentsPerElement))); cvtMeshAttribute->setBufferView(cvtBufferView); cvtMeshAttribute->setByteStride(stride); cvtMeshAttribute->setCount(elementsCount); mesh->setMeshAttribute(semantic, indexOfSet, cvtMeshAttribute); } return (unsigned int)setCount; }
bool createMeshesWithMaximumIndicesCountFromMeshIfNeeded(GLTFMesh *sourceMesh, unsigned int maxiumIndicesCount, MeshVector &meshes) { bool splitNeeded = false; //First, check every primitive indices count to figure out if we really need to split anything at all. //TODO: what about making a sanity check, to ensure we don't have points not referenced by any primitive. (I wonder if that's would be considered compliant with the SPEC. need to check. PrimitiveVector primitives = sourceMesh->getPrimitives(); for (size_t i = 0 ; i < primitives.size() ; i++) { if (primitives[i]->getIndices()->getCount() >= maxiumIndicesCount) { splitNeeded = true; break; } } if (!splitNeeded) return false; SubMeshContext *subMesh = NULL; bool stillHavePrimitivesElementsToBeProcessed = false; bool primitiveCompleted = false; int *allNextPrimitiveIndices = (int*)calloc(primitives.size(), sizeof(int)); unsigned int meshIndex = 0; for (size_t i = 0 ; i < primitives.size() ; i++) { if (allNextPrimitiveIndices[i] == -1) continue; if (subMesh == 0) { subMesh = __CreateSubMeshContext(); meshes.push_back(subMesh->targetMesh); std::string meshID = ""; std::string meshName = ""; meshID += sourceMesh->getID(); meshName += sourceMesh->getName(); if (meshIndex) { meshID += "-"+ GLTFUtils::toString(meshIndex); meshName += "-"+ GLTFUtils::toString(meshIndex); } subMesh->targetMesh->setID(meshID); subMesh->targetMesh->setName(meshName); stillHavePrimitivesElementsToBeProcessed = false; meshIndex++; } shared_ptr <GLTFPrimitive> targetPrimitive; //when we are done with a primitive we mark its nextIndice with a -1 targetPrimitive = shared_ptr <GLTFPrimitive> (new GLTFPrimitive((*primitives[i]))); unsigned int nextPrimitiveIndex = (unsigned int)allNextPrimitiveIndices[i]; shared_ptr<GLTFPrimitive> &primitive = primitives[i]; shared_ptr<GLTFIndices> indices = primitive->getIndices(); unsigned int* indicesPtr = (unsigned int*)indices->getBufferView()->getBufferDataByApplyingOffset(); unsigned int* targetIndicesPtr = (unsigned int*)malloc(indices->getBufferView()->getBuffer()->getByteLength()); //sub meshes are built this way [ and it is not optimal yet (*)]: //each primitive is iterated through all its triangles/lines/... //When the indices count in indexToRemappedIndex is >= maxiumIndicesCount then we try the next primitive. /* we could continue walking through a primitive even if the of maximum indices has been reached, because, for instance the next say, triangles could be within the already remapped indices. That said, not doing so should produce meshes that have more chances to have adjacent triangles. Need more experimentation about this. Having 2 modes would ideal. */ //Different iterators type will be needed for these types /* type = "TRIANGLES"; type = "LINES"; type = "LINE_STRIP"; type = "TRIANGLES"; type = "TRIANGLE_FANS"; type = "TRIANGLE_STRIPS"; type = "POINTS"; */ size_t j = 0; unsigned int primitiveCount = 0; unsigned int targetIndicesCount = 0; if (primitive->getType() == "TRIANGLES") { unsigned int indicesPerElementCount = 3; primitiveCount = indices->getCount() / indicesPerElementCount; for (j = nextPrimitiveIndex ; j < primitiveCount ; j++) { unsigned int *indicesPtrAtPrimitiveIndex = indicesPtr + (j * indicesPerElementCount); //will we still have room to store coming indices from this mesh ? //note: this is tied to the policy described above in (*) size_t currentSize = subMesh->indexToRemappedIndex.size(); if ((currentSize + indicesPerElementCount) < maxiumIndicesCount) { __PushAndRemapIndicesInSubMesh(subMesh, indicesPtrAtPrimitiveIndex, indicesPerElementCount); //build the indices for the primitive to be added to the subMesh targetIndicesPtr[targetIndicesCount] = subMesh->indexToRemappedIndex[indicesPtrAtPrimitiveIndex[0]]; targetIndicesPtr[targetIndicesCount + 1] = subMesh->indexToRemappedIndex[indicesPtrAtPrimitiveIndex[1]]; targetIndicesPtr[targetIndicesCount + 2] = subMesh->indexToRemappedIndex[indicesPtrAtPrimitiveIndex[2]]; targetIndicesCount += indicesPerElementCount; nextPrimitiveIndex++; } else { allNextPrimitiveIndices[i] = -1; primitiveCompleted = true; break; } } } allNextPrimitiveIndices[i] = nextPrimitiveIndex; if (targetIndicesCount > 0) { //FIXME: here targetIndices takes too much memory //To avoid this we would need to make a smaller copy. //In our case not sure if that's really a problem since this buffer won't be around for too long, as each buffer is deallocated once the callback from OpenCOLLADA to handle geomery has completed. shared_ptr <GLTFBufferView> targetBufferView = createBufferViewWithAllocatedBuffer(targetIndicesPtr, 0,targetIndicesCount * sizeof(unsigned int), true); shared_ptr <GLTFIndices> indices(new GLTFIndices(targetBufferView, targetIndicesCount)); targetPrimitive->setIndices(indices); subMesh->targetMesh->appendPrimitive(targetPrimitive); } else { if (targetIndicesPtr) free(targetIndicesPtr); } if (j < primitiveCount) stillHavePrimitivesElementsToBeProcessed = true; //did we process the last primitive ? if (primitiveCompleted || (((i + 1) == primitives.size()))) { __RemapSubMesh(subMesh, sourceMesh); if (stillHavePrimitivesElementsToBeProcessed) { //loop again and build new mesh i = -1; delete subMesh; subMesh = 0; } } } free(allNextPrimitiveIndices); return true; }
shared_ptr <GLTFMesh> createUnifiedIndexesMeshFromMesh(GLTFMesh *sourceMesh, std::vector< shared_ptr<IndicesVector> > &vectorOfIndicesVector) { MeshAttributeVector originalMeshAttributes; MeshAttributeVector remappedMeshAttributes; shared_ptr <GLTFMesh> targetMesh(new GLTFMesh(*sourceMesh)); PrimitiveVector sourcePrimitives = sourceMesh->getPrimitives(); PrimitiveVector targetPrimitives = targetMesh->getPrimitives(); size_t startIndex = 0; size_t endIndex = 0; size_t primitiveCount = sourcePrimitives.size(); unsigned int maxVertexAttributes = 0; if (primitiveCount == 0) { // FIXME: report error //return 0; } //in originalMeshAttributes we'll get the flattened list of all the meshAttributes as a vector. //fill semanticAndSetToIndex with key: (semantic, indexSet) value: index in originalMeshAttributes vector. vector <GLTF::Semantic> allSemantics = sourceMesh->allSemantics(); std::map<string, unsigned int> semanticAndSetToIndex; for (unsigned int i = 0 ; i < allSemantics.size() ; i++) { IndexSetToMeshAttributeHashmap& indexSetToMeshAttribute = sourceMesh->getMeshAttributesForSemantic(allSemantics[i]); IndexSetToMeshAttributeHashmap::const_iterator meshAttributeIterator; for (meshAttributeIterator = indexSetToMeshAttribute.begin() ; meshAttributeIterator != indexSetToMeshAttribute.end() ; meshAttributeIterator++) { //(*it).first; // the key value (of type Key) //(*it).second; // the mapped value (of type T) shared_ptr <GLTF::GLTFMeshAttribute> selectedMeshAttribute = (*meshAttributeIterator).second; unsigned int indexSet = (*meshAttributeIterator).first; GLTF::Semantic semantic = allSemantics[i]; std::string semanticIndexSetKey = keyWithSemanticAndSet(semantic, indexSet); unsigned int size = (unsigned int)originalMeshAttributes.size(); semanticAndSetToIndex[semanticIndexSetKey] = size; originalMeshAttributes.push_back(selectedMeshAttribute); } } maxVertexAttributes = (unsigned int)originalMeshAttributes.size(); vector <shared_ptr<GLTF::GLTFPrimitiveRemapInfos> > allPrimitiveRemapInfos; //build a array that maps the meshAttributes that the indices points to with the index of the indice. GLTF::RemappedMeshIndexesHashmap remappedMeshIndexesMap; for (unsigned int i = 0 ; i < primitiveCount ; i++) { shared_ptr<IndicesVector> allIndicesSharedPtr = vectorOfIndicesVector[i]; IndicesVector *allIndices = allIndicesSharedPtr.get(); unsigned int* indicesInRemapping = (unsigned int*)malloc(sizeof(unsigned int) * allIndices->size()); VertexAttributeVector vertexAttributes = sourcePrimitives[i]->getVertexAttributes(); for (unsigned int k = 0 ; k < allIndices->size() ; k++) { GLTF::Semantic semantic = vertexAttributes[k]->getSemantic(); unsigned int indexSet = vertexAttributes[k]->getIndexOfSet(); std::string semanticIndexSetKey = keyWithSemanticAndSet(semantic, indexSet); unsigned int idx = semanticAndSetToIndex[semanticIndexSetKey]; indicesInRemapping[k] = idx; } shared_ptr<GLTF::GLTFPrimitiveRemapInfos> primitiveRemapInfos = __BuildPrimitiveUniqueIndexes(targetPrimitives[i], *allIndices, remappedMeshIndexesMap, indicesInRemapping, startIndex, maxVertexAttributes, endIndex); free(indicesInRemapping); if (primitiveRemapInfos.get()) { startIndex = endIndex; allPrimitiveRemapInfos.push_back(primitiveRemapInfos); } else { // FIXME: report error //return NULL; } } // now we got not only the uniqueIndexes but also the number of different indexes, i.e the number of vertex attributes count // we can allocate the buffer to hold vertex attributes unsigned int vertexCount = endIndex; //just allocate it now, will be filled later unsigned int* remapTableForPositions = (unsigned int*)malloc(sizeof(unsigned int) * vertexCount); targetMesh->setRemapTableForPositions(remapTableForPositions); for (unsigned int i = 0 ; i < allSemantics.size() ; i++) { Semantic semantic = allSemantics[i]; IndexSetToMeshAttributeHashmap& indexSetToMeshAttribute = sourceMesh->getMeshAttributesForSemantic(semantic); IndexSetToMeshAttributeHashmap& destinationIndexSetToMeshAttribute = targetMesh->getMeshAttributesForSemantic(semantic); IndexSetToMeshAttributeHashmap::const_iterator meshAttributeIterator; //FIXME: consider turn this search into a method for mesh for (meshAttributeIterator = indexSetToMeshAttribute.begin() ; meshAttributeIterator != indexSetToMeshAttribute.end() ; meshAttributeIterator++) { //(*it).first; // the key value (of type Key) //(*it).second; // the mapped value (of type T) shared_ptr <GLTF::GLTFMeshAttribute> selectedMeshAttribute = (*meshAttributeIterator).second; size_t sourceSize = vertexCount * selectedMeshAttribute->getVertexAttributeByteLength(); void* sourceData = malloc(sourceSize); shared_ptr <GLTFBufferView> referenceBufferView = selectedMeshAttribute->getBufferView(); shared_ptr <GLTFBufferView> remappedBufferView = createBufferViewWithAllocatedBuffer(referenceBufferView->getID(), sourceData, 0, sourceSize, true); shared_ptr <GLTFMeshAttribute> remappedMeshAttribute(new GLTFMeshAttribute(selectedMeshAttribute.get())); remappedMeshAttribute->setBufferView(remappedBufferView); remappedMeshAttribute->setCount(vertexCount); destinationIndexSetToMeshAttribute[(*meshAttributeIterator).first] = remappedMeshAttribute; remappedMeshAttributes.push_back(remappedMeshAttribute); } } /* if (_allOriginalMeshAttributes.size() != allIndices.size()) { // FIXME: report error return false; } */ for (unsigned int i = 0 ; i < primitiveCount ; i++) { shared_ptr<IndicesVector> allIndicesSharedPtr = vectorOfIndicesVector[i]; IndicesVector *allIndices = allIndicesSharedPtr.get(); unsigned int* indicesInRemapping = (unsigned int*)calloc(sizeof(unsigned int) * (*allIndices).size(), 1); VertexAttributeVector vertexAttributes = sourcePrimitives[i]->getVertexAttributes(); for (unsigned int k = 0 ; k < (*allIndices).size() ; k++) { GLTF::Semantic semantic = vertexAttributes[k]->getSemantic(); unsigned int indexSet = vertexAttributes[k]->getIndexOfSet(); std::string semanticIndexSetKey = keyWithSemanticAndSet(semantic, indexSet); unsigned int idx = semanticAndSetToIndex[semanticIndexSetKey]; indicesInRemapping[k] = idx; } bool status = __RemapPrimitiveVertices(targetPrimitives[i], (*allIndices), originalMeshAttributes , remappedMeshAttributes, indicesInRemapping, allPrimitiveRemapInfos[i], remapTableForPositions); free(indicesInRemapping); if (!status) { // FIXME: report error //return NULL; } } return targetMesh; }
static void __DecomposeMatrices(float *matrices, size_t count, std::vector< shared_ptr <GLTFBufferView> > &TRSBufferViews) { size_t translationBufferSize = sizeof(float) * 3 * count; size_t rotationBufferSize = sizeof(float) * 4 * count; size_t scaleBufferSize = sizeof(float) * 3 * count; float *translationData = (float*)malloc(translationBufferSize); float *rotationData = (float*)malloc(rotationBufferSize); float *scaleData = (float*)malloc(scaleBufferSize); shared_ptr <GLTF::GLTFBufferView> translationBufferView = createBufferViewWithAllocatedBuffer(translationData, 0, translationBufferSize, true); shared_ptr <GLTF::GLTFBufferView> rotationBufferView = createBufferViewWithAllocatedBuffer(rotationData, 0, rotationBufferSize, true); shared_ptr <GLTF::GLTFBufferView> scaleBufferView = createBufferViewWithAllocatedBuffer(scaleData, 0, scaleBufferSize, true); float *previousRotation = 0; for (size_t i = 0 ; i < count ; i++) { float *m = matrices; COLLADABU::Math::Matrix4 mat; mat.setAllElements(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], m[12], m[13], m[14], m[15] ); decomposeMatrix(mat, translationData, rotationData, scaleData); //make sure we export the short path from orientations if (0 != previousRotation) { COLLADABU::Math::Vector3 axis1(previousRotation[0], previousRotation[1], previousRotation[2]); COLLADABU::Math::Vector3 axis2(rotationData[0], rotationData[1], rotationData[2]); COLLADABU::Math::Quaternion key1; COLLADABU::Math::Quaternion key2; key1.fromAngleAxis(previousRotation[3], axis1); key2.fromAngleAxis(rotationData[3], axis2); COLLADABU::Math::Real cosHalfTheta = key1.dot(key2); if (cosHalfTheta < 0) { key2.x = -key2.x; key2.y = -key2.y; key2.z = -key2.z; key2.w = -key2.w; COLLADABU::Math::Real angle; key2.toAngleAxis(angle, axis2); rotationData[3] = (float)angle; rotationData[0] = (float)axis2.x; rotationData[1] = (float)axis2.y; rotationData[2] = (float)axis2.z; key2.fromAngleAxis(rotationData[3], axis2); //FIXME: this needs to be refined, we ensure continuity here, but assume in clockwise order cosHalfTheta = key1.dot(key2); if (cosHalfTheta < 0) { rotationData[3] += (float)(2. * 3.14159265359); key2.fromAngleAxis(rotationData[3], axis2); } } } previousRotation = rotationData; translationData += 3; rotationData += 4; scaleData += 3; matrices += 16; } /* rotationData = (float*)rotationBufferView->getBufferDataByApplyingOffset(); for (size_t i = 0 ; i < count ; i++) { printf("rotation at:%d %f %f %f %f\n", i, rotationData[0],rotationData[1],rotationData[2],rotationData[3]); rotationData += 4; } */ TRSBufferViews.push_back(translationBufferView); TRSBufferViews.push_back(rotationBufferView); TRSBufferViews.push_back(scaleBufferView); }