diff --git a/tools/a3dconvert/Android.mk b/tools/a3dconvert/Android.mk new file mode 100644 index 000000000..7bc634e74 --- /dev/null +++ b/tools/a3dconvert/Android.mk @@ -0,0 +1,40 @@ +# Copyright (C) 2011 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) + +# Host executable +include $(CLEAR_VARS) +LOCAL_MODULE := a3dconvert +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS += -DANDROID_RS_SERIALIZE +# Needed for colladaDom +LOCAL_CFLAGS += -DNO_BOOST -DDOM_INCLUDE_TINYXML -DNO_ZAE + +LOCAL_SRC_FILES := \ + a3dconvert.cpp \ + ObjLoader.cpp \ + ColladaConditioner.cpp \ + ColladaGeometry.cpp \ + ColladaLoader.cpp + + +LOCAL_C_INCLUDES += external/collada/include +LOCAL_C_INCLUDES += external/collada/include/1.4 +LOCAL_C_INCLUDES += frameworks/base/libs/rs + +LOCAL_LDLIBS := -lpthread +LOCAL_STATIC_LIBRARIES += libRSserialize libutils libcutils +LOCAL_STATIC_LIBRARIES += colladadom libtinyxml libpcrecpp libpcre +include $(BUILD_HOST_EXECUTABLE) diff --git a/tools/a3dconvert/ColladaConditioner.cpp b/tools/a3dconvert/ColladaConditioner.cpp new file mode 100644 index 000000000..0b0f694f0 --- /dev/null +++ b/tools/a3dconvert/ColladaConditioner.cpp @@ -0,0 +1,203 @@ +/* +* 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 "ColladaConditioner.h" +unsigned int ColladaConditioner::getMaxOffset( domInputLocalOffset_Array &input_array ) { + + unsigned int maxOffset = 0; + for ( unsigned int i = 0; i < input_array.getCount(); i++ ) { + if ( input_array[i]->getOffset() > maxOffset ) { + maxOffset = (unsigned int)input_array[i]->getOffset(); + } + } + return maxOffset; +} + +void ColladaConditioner::createTrianglesFromPolylist( domMesh *thisMesh, domPolylist *thisPolylist ) { + + // Create a new inside the mesh that has the same material as the + domTriangles *thisTriangles = (domTriangles *)thisMesh->createAndPlace("triangles"); + //thisTriangles->setCount( 0 ); + unsigned int triangles = 0; + thisTriangles->setMaterial(thisPolylist->getMaterial()); + domP* p_triangles = (domP*)thisTriangles->createAndPlace("p"); + + // Give the new the same <_dae> and as the old + for(int i=0; i<(int)(thisPolylist->getInput_array().getCount()); i++) { + + thisTriangles->placeElement( thisPolylist->getInput_array()[i]->clone() ); + } + + // Get the number of inputs and primitives for the polygons array + int numberOfInputs = (int)getMaxOffset(thisPolylist->getInput_array()) + 1; + int numberOfPrimitives = (int)(thisPolylist->getVcount()->getValue().getCount()); + + unsigned int offset = 0; + + // Triangulate all the primitives, this generates all the triangles in a single

element + for(int j = 0; j < numberOfPrimitives; j++) { + + int triangleCount = (int)thisPolylist->getVcount()->getValue()[j] -2; + // Write out the primitives as triangles, just fan using the first element as the base + int idx = numberOfInputs; + for(int k = 0; k < triangleCount; k++) { + // First vertex + for(int l = 0; l < numberOfInputs; l++) { + + p_triangles->getValue().append(thisPolylist->getP()->getValue()[offset + l]); + } + // Second vertex + for(int l = 0; l < numberOfInputs; l++) { + + p_triangles->getValue().append(thisPolylist->getP()->getValue()[offset + idx + l]); + } + // Third vertex + idx += numberOfInputs; + for(int l = 0; l < numberOfInputs; l++) { + + p_triangles->getValue().append(thisPolylist->getP()->getValue()[offset + idx + l]); + } + triangles++; + } + offset += (unsigned int)thisPolylist->getVcount()->getValue()[j] * numberOfInputs; + } + thisTriangles->setCount( triangles ); + +} + +void ColladaConditioner::createTrianglesFromPolygons( domMesh *thisMesh, domPolygons *thisPolygons ) { + + // Create a new inside the mesh that has the same material as the + domTriangles *thisTriangles = (domTriangles *)thisMesh->createAndPlace("triangles"); + thisTriangles->setCount( 0 ); + thisTriangles->setMaterial(thisPolygons->getMaterial()); + domP* p_triangles = (domP*)thisTriangles->createAndPlace("p"); + + // Give the new the same <_dae> and as the old + for(int i=0; i<(int)(thisPolygons->getInput_array().getCount()); i++) { + + thisTriangles->placeElement( thisPolygons->getInput_array()[i]->clone() ); + } + + // Get the number of inputs and primitives for the polygons array + int numberOfInputs = (int)getMaxOffset(thisPolygons->getInput_array()) +1; + int numberOfPrimitives = (int)(thisPolygons->getP_array().getCount()); + + // Triangulate all the primitives, this generates all the triangles in a single

element + for(int j = 0; j < numberOfPrimitives; j++) { + + // Check the polygons for consistancy (some exported files have had the wrong number of indices) + domP * thisPrimitive = thisPolygons->getP_array()[j]; + int elementCount = (int)(thisPrimitive->getValue().getCount()); + // Skip the invalid primitive + if((elementCount % numberOfInputs) != 0) { + continue; + } else { + int triangleCount = (elementCount/numberOfInputs)-2; + // Write out the primitives as triangles, just fan using the first element as the base + int idx = numberOfInputs; + for(int k = 0; k < triangleCount; k++) { + // First vertex + for(int l = 0; l < numberOfInputs; l++) { + + p_triangles->getValue().append(thisPrimitive->getValue()[l]); + } + // Second vertex + for(int l = 0; l < numberOfInputs; l++) { + + p_triangles->getValue().append(thisPrimitive->getValue()[idx + l]); + } + // Third vertex + idx += numberOfInputs; + for(int l = 0; l < numberOfInputs; l++) { + + p_triangles->getValue().append(thisPrimitive->getValue()[idx + l]); + } + thisTriangles->setCount(thisTriangles->getCount()+1); + } + } + } + +} + + +bool ColladaConditioner::triangulate(DAE *dae) { + + int error = 0; + + // How many geometry elements are there? + int geometryElementCount = (int)(dae->getDatabase()->getElementCount(NULL, "geometry" )); + + for(int currentGeometry = 0; currentGeometry < geometryElementCount; currentGeometry++) { + + // Find the next geometry element + domGeometry *thisGeometry; + // error = _dae->getDatabase()->getElement((daeElement**)&thisGeometry,currentGeometry, NULL, "geometry"); + daeElement * element = 0; + error = dae->getDatabase()->getElement(&element,currentGeometry, NULL, "geometry"); + thisGeometry = (domGeometry *) element; + + // Get the mesh out of the geometry + domMesh *thisMesh = thisGeometry->getMesh(); + + if (thisMesh == NULL){ + continue; + } + + // Loop over all the polygon elements + for(int currentPolygons = 0; currentPolygons < (int)(thisMesh->getPolygons_array().getCount()); currentPolygons++) { + + // Get the polygons out of the mesh + // Always get index 0 because every pass through this loop deletes the element as it finishes with it + domPolygons *thisPolygons = thisMesh->getPolygons_array()[currentPolygons]; + createTrianglesFromPolygons( thisMesh, thisPolygons ); + } + while (thisMesh->getPolygons_array().getCount() > 0) { + + domPolygons *thisPolygons = thisMesh->getPolygons_array().get(0); + // Remove the polygons from the mesh + thisMesh->removeChildElement(thisPolygons); + } + int polylistElementCount = (int)(thisMesh->getPolylist_array().getCount()); + for(int currentPolylist = 0; currentPolylist < polylistElementCount; currentPolylist++) { + + // Get the polylist out of the mesh + // Always get index 0 because every pass through this loop deletes the element as it finishes with it + domPolylist *thisPolylist = thisMesh->getPolylist_array()[currentPolylist]; + createTrianglesFromPolylist( thisMesh, thisPolylist ); + } + while (thisMesh->getPolylist_array().getCount() > 0) { + + domPolylist *thisPolylist = thisMesh->getPolylist_array().get(0); + // Remove the polylist from the mesh + thisMesh->removeChildElement(thisPolylist); + } + } + return (error == 0); +} + +bool ColladaConditioner::triangulate(const char *inputFile) { + + DAE dae; + bool convertSuceeded = true; + domCOLLADA* root = dae.open(inputFile); + + if (!root) { + printf("Failed to read file %s.\n", inputFile); + return false; + } + + convertSuceeded = triangulate(&dae); + + dae.writeAll(); + if(!convertSuceeded) { + printf("Encountered errors\n"); + } + + return convertSuceeded; +} \ No newline at end of file diff --git a/tools/a3dconvert/ColladaConditioner.h b/tools/a3dconvert/ColladaConditioner.h new file mode 100644 index 000000000..470b7b6c7 --- /dev/null +++ b/tools/a3dconvert/ColladaConditioner.h @@ -0,0 +1,28 @@ +/* +* 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 +* +*/ + +#ifndef COLLADA_CONDITIONER +#define COLLADA_CONDITIONER + +#include +#include +#include + +class ColladaConditioner { + +private: + unsigned int getMaxOffset( domInputLocalOffset_Array &input_array ); + void createTrianglesFromPolylist( domMesh *thisMesh, domPolylist *thisPolylist ); + void createTrianglesFromPolygons( domMesh *thisMesh, domPolygons *thisPolygons ); + +public: + bool triangulate(DAE *dae); + bool triangulate(const char *inputFile); +}; + +#endif //COLLADA_CONDITIONER diff --git a/tools/a3dconvert/ColladaGeometry.cpp b/tools/a3dconvert/ColladaGeometry.cpp new file mode 100644 index 000000000..1aba4f158 --- /dev/null +++ b/tools/a3dconvert/ColladaGeometry.cpp @@ -0,0 +1,319 @@ +/* +* 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 +#include + +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 &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 &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; +} + + + + + + + diff --git a/tools/a3dconvert/ColladaGeometry.h b/tools/a3dconvert/ColladaGeometry.h new file mode 100644 index 000000000..577499742 --- /dev/null +++ b/tools/a3dconvert/ColladaGeometry.h @@ -0,0 +1,84 @@ +/* +* 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 +* +*/ + +#ifndef _COLLADA_GEOMETRY_H_ +#define _COLLADA_GEOMETRY_H_ + +#include +#include +#include +#include + +#include "rsContext.h" +#include "rsMesh.h" +#include "SimpleMesh.h" + +using namespace android; +using namespace android::renderscript; + + +class ColladaGeometry { +public: + ColladaGeometry(); + bool init(domGeometryRef geometry); + + Mesh *getMesh(Context *rsc) { + return mConvertedMesh.getMesh(rsc); + } + +private: + + //Store some collada stuff + domMesh *mMesh; + + // Cache the pointers to the collada version of the data + // This contains raw vertex data that is not necessarily the same size for all + // Offset refers to the way collada packs each triangle's index to position / normal / etc. + domListOfFloats *mPositionFloats; + int mPositionOffset; + domListOfFloats *mNormalFloats; + int mNormalOffset; + domListOfFloats *mTangentFloats; + int mTangentOffset; + domListOfFloats *mBinormalFloats; + int mBinormalOffset; + domListOfFloats *mTexture1Floats; + int mTexture1Offset; + + // In the list of triangles, collada uses multiple indecies per triangle to point to the correct + // index in all the different arrays. We need to know the total number of these guys so we can + // just to the next triangle to process + int mMultiIndexOffset; + + // All these vectors would contain the same number of "points" + // index*stride would properly get to the uv, normal etc. + // collada, like maya and many others keep point array, normal array etc + // different size in the cases the same vertex produces divergent normals for different faces + std::vector *mPositions; + unsigned int mPositionsStride; + std::vector *mNormals; + unsigned int mNormalsStride; + std::vector *mTextureCoords; + unsigned int mTextureCoordsStride; + std::vector *mTangents; + unsigned int mTangentssStride; + std::vector *mBinormals; + unsigned int mBinormalsStride; + + SimpleMesh mConvertedMesh; + + // This vector is used to remap a position index into a list of all divergent vertices + std::vector > mVertexRemap; + + void addTriangles(domTriangles * colladaTriangles); + void cacheOffsetsAndDataPointers(domTriangles * colladaTriangles); + int remapIndexAndStoreData(const domListOfUInts &colladaIndexList, int indexToRemap); + +}; + +#endif //COLLADA_TO_A3D_GEOMETRY diff --git a/tools/a3dconvert/ColladaLoader.cpp b/tools/a3dconvert/ColladaLoader.cpp new file mode 100644 index 000000000..8a747481f --- /dev/null +++ b/tools/a3dconvert/ColladaLoader.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ColladaLoader.h" +#include "ColladaConditioner.h" +#include "ColladaGeometry.h" +#include "rsContext.h" +#include "rsFileA3D.h" + +#include +#include + +ColladaLoader::ColladaLoader() { + +} + +ColladaLoader::~ColladaLoader() { + clearGeometry(); +} + +void ColladaLoader::clearGeometry() { + for (uint32_t i = 0; i < mGeometries.size(); i++) { + delete mGeometries[i]; + } + mGeometries.clear(); +} + +bool ColladaLoader::init(const char *colladaFile) { + DAE dae; + + clearGeometry(); + + bool convertSuceeded = true; + + domCOLLADA* root = dae.open(colladaFile); + if (!root) { + fprintf(stderr, "Failed to read file %s.\n", colladaFile); + return false; + } + + // We only want to deal with triangulated meshes since rendering complex polygons is not feasible + ColladaConditioner conditioner; + conditioner.triangulate(&dae); + + domLibrary_geometries *allGeometry = daeSafeCast(root->getDescendant("library_geometries")); + + if (allGeometry) { + convertSuceeded = convertAllGeometry(allGeometry) && convertSuceeded; + } + + return convertSuceeded; +} + +bool ColladaLoader::convertToA3D(const char *a3dFile) { + if (mGeometries.size() == 0) { + return false; + } + // Now write all this stuff out + Context rsc; + FileA3D file(&rsc); + + for (uint32_t i = 0; i < mGeometries.size(); i++) { + Mesh *exportedMesh = mGeometries[i]->getMesh(&rsc); + file.appendToFile(exportedMesh); + delete exportedMesh; + } + + file.writeFile(a3dFile); + return true; +} + +bool ColladaLoader::convertAllGeometry(domLibrary_geometries *allGeometry) { + + bool convertSuceeded = true; + domGeometry_Array &geo_array = allGeometry->getGeometry_array(); + for (size_t i = 0; i < geo_array.getCount(); i++) { + domGeometry *geometry = geo_array[i]; + const char *geometryName = geometry->getName(); + if (geometryName == NULL) { + geometryName = geometry->getId(); + } + + domMeshRef mesh = geometry->getMesh(); + if (mesh != NULL) { + printf("Converting geometry: %s\n", geometryName); + convertSuceeded = convertGeometry(geometry) && convertSuceeded; + } else { + printf("Skipping geometry: %s, unsupported type\n", geometryName); + } + + } + + return convertSuceeded; +} + +bool ColladaLoader::convertGeometry(domGeometry *geometry) { + bool convertSuceeded = true; + + domMeshRef mesh = geometry->getMesh(); + + ColladaGeometry *convertedGeo = new ColladaGeometry(); + convertedGeo->init(geometry); + + mGeometries.push_back(convertedGeo); + + return convertSuceeded; +} diff --git a/tools/a3dconvert/ColladaLoader.h b/tools/a3dconvert/ColladaLoader.h new file mode 100644 index 000000000..aa66e7d75 --- /dev/null +++ b/tools/a3dconvert/ColladaLoader.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _COLLADA_LOADER_H_ +#define _COLLADA_LOADER_H_ + +#include + +class domLibrary_geometries; +class domGeometry; +class ColladaGeometry; + +class ColladaLoader { +public: + ColladaLoader(); + ~ColladaLoader(); + + bool init(const char *colladaFile); + bool convertToA3D(const char *a3dFile); + +private: + void clearGeometry(); + std::vector mGeometries; + + bool convertAllGeometry(domLibrary_geometries *allGeometry); + bool convertGeometry(domGeometry *geometry); + +}; + +#endif \ No newline at end of file diff --git a/tools/a3dconvert/ObjLoader.cpp b/tools/a3dconvert/ObjLoader.cpp new file mode 100644 index 000000000..4465f4f1d --- /dev/null +++ b/tools/a3dconvert/ObjLoader.cpp @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ObjLoader.h" +#include +#include + +ObjLoader::ObjLoader() : + mPositionsStride(3), mNormalsStride(3), mTextureCoordsStride(2) { + +} + +bool isWhitespace(char c) { + const char whiteSpace[] = { ' ', '\n', '\t', '\f', '\r' }; + const uint32_t numWhiteSpaceChars = 5; + for (uint32_t i = 0; i < numWhiteSpaceChars; i ++) { + if (whiteSpace[i] == c) { + return true; + } + } + return false; +} + +void eatWhitespace(std::istream &is) { + while(is.good() && isWhitespace(is.peek())) { + is.get(); + } +} + +bool getToken(std::istream &is, std::string &token) { + eatWhitespace(is); + token.clear(); + char c; + while(is.good() && !isWhitespace(is.peek())) { + c = is.get(); + if (is.good()){ + token += c; + } + } + return token.size() > 0; +} + +void appendDataFromStream(std::vector &dataVec, uint32_t numFloats, std::istream &is) { + std::string token; + for (uint32_t i = 0; i < numFloats; i ++){ + bool valid = getToken(is, token); + if (valid) { + dataVec.push_back((float)atof(token.c_str())); + } else { + fprintf(stderr, "Encountered error reading geometry data"); + dataVec.push_back(0.0f); + } + } +} + +bool checkNegativeIndex(int idx) { + if(idx < 0) { + fprintf(stderr, "Negative indices are not supported. Skipping face\n"); + return false; + } + return true; +} + +void ObjLoader::parseRawFaces(){ + // We need at least a triangle + if (mRawFaces.size() < 3) { + return; + } + + const char slash = '/'; + mParsedFaces.resize(mRawFaces.size()); + for (uint32_t i = 0; i < mRawFaces.size(); i ++) { + size_t firstSeparator = mRawFaces[i].find_first_of(slash); + size_t nextSeparator = mRawFaces[i].find_last_of(slash); + + // Use the string as a temp buffer to parse the index + // Insert 0 instead of the slash to avoid substrings + if (firstSeparator != std::string::npos) { + mRawFaces[i][firstSeparator] = 0; + } + // Simple case, only one index + int32_t vIdx = atoi(mRawFaces[i].c_str()); + // We do not support negative indices + if (!checkNegativeIndex(vIdx)) { + return; + } + // obj indices things beginning 1 + mParsedFaces[i].vertIdx = (uint32_t)vIdx - 1; + + if (nextSeparator != std::string::npos && nextSeparator != firstSeparator) { + mRawFaces[i][nextSeparator] = 0; + uint32_t nIdx = atoi(mRawFaces[i].c_str() + nextSeparator + 1); + if (!checkNegativeIndex(nIdx)) { + return; + } + // obj indexes things beginning 1 + mParsedFaces[i].normIdx = (uint32_t)nIdx - 1; + } + + // second case is where we have vertex and texture indices + if (nextSeparator != std::string::npos && + (nextSeparator > firstSeparator + 1 || nextSeparator == firstSeparator)) { + uint32_t tIdx = atoi(mRawFaces[i].c_str() + firstSeparator + 1); + if (!checkNegativeIndex(tIdx)) { + return; + } + // obj indexes things beginning 1 + mParsedFaces[i].texIdx = (uint32_t)tIdx - 1; + } + } + + // Make sure a face list exists before we go adding to it + if (mMeshes.back().mUnfilteredFaces.size() == 0) { + mMeshes.back().appendUnfilteredFaces(mLastMtl); + } + + // Now we have our parsed face, that we need to triangulate as necessary + // Treat more complex polygons as fans. + // This approach will only work only for convex polygons + // but concave polygons need to be addressed elsewhere anyway + for (uint32_t next = 1; next < mParsedFaces.size() - 1; next ++) { + // push it to our current mesh + mMeshes.back().mUnfilteredFaces.back().push_back(mParsedFaces[0]); + mMeshes.back().mUnfilteredFaces.back().push_back(mParsedFaces[next]); + mMeshes.back().mUnfilteredFaces.back().push_back(mParsedFaces[next + 1]); + } +} + +void ObjLoader::checkNewMeshCreation(std::string &newGroup) { + // start a new mesh if we have some faces + // accumulated on the current mesh. + // It's possible to have multiple group statements + // but we only care to actually start a new mesh + // once we can have something we can draw on the previous one + if (mMeshes.back().mUnfilteredFaces.size()) { + mMeshes.push_back(ObjMesh()); + } + + mMeshes.back().mName = newGroup; + printf("Converting vertex group: %s\n", newGroup.c_str()); +} + +void ObjLoader::handleObjLine(char *line) { + const char* vtxToken = "v"; + const char* normToken = "vn"; + const char* texToken = "vt"; + const char* groupToken = "g"; + const char* mtlToken = "usemtl"; + const char* faceToken = "f"; + + std::istringstream lineStream(line, std::istringstream::in); + + std::string token; + bool valid = getToken(lineStream, token); + if (!valid) { + return; + } + + if (token == vtxToken) { + appendDataFromStream(mObjPositions, 3, lineStream); + } else if (token == normToken) { + appendDataFromStream(mObjNormals, 3, lineStream); + } else if (token == texToken) { + appendDataFromStream(mObjTextureCoords, 2, lineStream); + } else if (token == groupToken) { + valid = getToken(lineStream, token); + checkNewMeshCreation(token); + } else if (token == faceToken) { + mRawFaces.clear(); + while(getToken(lineStream, token)) { + mRawFaces.push_back(token); + } + parseRawFaces(); + } + // Ignore materials for now + else if (token == mtlToken) { + valid = getToken(lineStream, token); + mLastMtl = token; + + mMeshes.back().appendUnfilteredFaces(token); + } +} + +bool ObjLoader::init(const char *fileName) { + + std::ifstream ifs(fileName , std::ifstream::in); + if (!ifs.good()) { + fprintf(stderr, "Failed to read file %s.\n", fileName); + return false; + } + + mMeshes.clear(); + + const uint32_t maxBufferSize = 2048; + char *buffer = new char[maxBufferSize]; + + mMeshes.push_back(ObjMesh()); + + std::string token; + bool isDone = false; + while(!isDone) { + ifs.getline(buffer, maxBufferSize); + if (ifs.good() && ifs.gcount() > 0) { + handleObjLine(buffer); + } else { + isDone = true; + } + } + + ifs.close(); + delete buffer; + + reIndexGeometry(); + + return true; +} + +bool ObjLoader::convertToA3D(const char *a3dFile) { + if (!getNumMeshes()) { + return false; + } + // Now write all this stuff out + Context rsc; + FileA3D file(&rsc); + + for (uint32_t i = 0; i < getNumMeshes(); i ++) { + Mesh *exportedMesh = getMesh(&rsc, i); + file.appendToFile(exportedMesh); + delete exportedMesh; + } + + file.writeFile(a3dFile); + return true; +} + +void ObjLoader::reIndexGeometry() { + // We want to know where each vertex lands + mVertexRemap.resize(mObjPositions.size() / mPositionsStride); + + for (uint32_t m = 0; m < mMeshes.size(); m ++) { + // clear the remap vector of old data + for (uint32_t r = 0; r < mVertexRemap.size(); r ++) { + mVertexRemap[r].clear(); + } + + for (uint32_t i = 0; i < mMeshes[m].mUnfilteredFaces.size(); i ++) { + mMeshes[m].mTriangleLists[i].reserve(mMeshes[m].mUnfilteredFaces[i].size() * 2); + for (uint32_t fI = 0; fI < mMeshes[m].mUnfilteredFaces[i].size(); fI ++) { + uint32_t newIndex = reIndexGeometryPrim(mMeshes[m], mMeshes[m].mUnfilteredFaces[i][fI]); + mMeshes[m].mTriangleLists[i].push_back(newIndex); + } + } + } +} + +uint32_t ObjLoader::reIndexGeometryPrim(ObjMesh &mesh, PrimitiveVtx &prim) { + + std::vector &mPositions = mesh.mChannels[0].mData; + std::vector &mNormals = mesh.mChannels[1].mData; + std::vector &mTextureCoords = mesh.mChannels[2].mData; + + float posX = mObjPositions[prim.vertIdx * mPositionsStride + 0]; + float posY = mObjPositions[prim.vertIdx * mPositionsStride + 1]; + float posZ = mObjPositions[prim.vertIdx * mPositionsStride + 2]; + + float normX = 0.0f; + float normY = 0.0f; + float normZ = 0.0f; + if (prim.normIdx != MAX_INDEX) { + normX = mObjNormals[prim.normIdx * mNormalsStride + 0]; + normY = mObjNormals[prim.normIdx * mNormalsStride + 1]; + normZ = mObjNormals[prim.normIdx * mNormalsStride + 2]; + } + + float texCoordX = 0.0f; + float texCoordY = 0.0f; + if (prim.texIdx != MAX_INDEX) { + texCoordX = mObjTextureCoords[prim.texIdx * mTextureCoordsStride + 0]; + texCoordY = mObjTextureCoords[prim.texIdx * mTextureCoordsStride + 1]; + } + + std::vector &ithRemapList = mVertexRemap[prim.vertIdx]; + // We may have some potential vertices we can reuse + // loop over all the potential candidates and see if any match our guy + for (unsigned int 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 (prim.normIdx != MAX_INDEX) { + if (mNormals[ithRemap * mNormalsStride + 0] != normX || + mNormals[ithRemap * mNormalsStride + 1] != normY || + mNormals[ithRemap * mNormalsStride + 2] != normZ) { + continue; + } + } + + // And texcoords + if (prim.texIdx != MAX_INDEX) { + 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 (prim.normIdx != MAX_INDEX) { + mNormals.push_back(normX); + mNormals.push_back(normY); + mNormals.push_back(normZ); + } + + if (prim.texIdx != MAX_INDEX) { + 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; +} diff --git a/tools/a3dconvert/ObjLoader.h b/tools/a3dconvert/ObjLoader.h new file mode 100644 index 000000000..210b35faa --- /dev/null +++ b/tools/a3dconvert/ObjLoader.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _OBJ_LOADER_H_ +#define _OBJ_LOADER_H_ + +#include +#include +#include +#include + +#include "SimpleMesh.h" +#include + +using namespace android; +using namespace android::renderscript; + +#define MAX_INDEX 0xffffffff + +class ObjLoader { +public: + ObjLoader(); + bool init(const char *objFile); + bool convertToA3D(const char *a3dFile); +private: + + Mesh *getMesh(Context *rsc, uint32_t meshIndex) { + return mMeshes[meshIndex].getMesh(rsc); + } + uint32_t getNumMeshes() const { + return mMeshes.size(); + } + + // .obj has a global list of vertex data + std::vector mObjPositions; + std::vector mObjNormals; + std::vector mObjTextureCoords; + + struct PrimitiveVtx { + uint32_t vertIdx; + uint32_t normIdx; + uint32_t texIdx; + + PrimitiveVtx() : vertIdx(MAX_INDEX), + normIdx(MAX_INDEX), + texIdx(MAX_INDEX){ + } + }; + + // Scratch buffer for faces + std::vector mRawFaces; + std::vector mParsedFaces; + std::string mLastMtl; + + // Groups are used to separate multiple meshes within the same .obj file + class ObjMesh : public SimpleMesh { + public: + + std::vector > mUnfilteredFaces; + + void appendUnfilteredFaces(std::string name) { + appendFaceList(name); + mUnfilteredFaces.push_back(std::vector()); + // Reserve some space for index data + static const uint32_t numReserveIndecies = 128; + mUnfilteredFaces.back().reserve(numReserveIndecies); + } + + ObjMesh() { + appendChannel("position", 3); + appendChannel("normal", 3); + appendChannel("texture0", 2); + } + }; + + std::vector mMeshes; + void checkNewMeshCreation(std::string &newGroup); + + void parseRawFaces(); + void handleObjLine(char *line); + + void reIndexGeometry(); + uint32_t reIndexGeometryPrim(ObjMesh &mesh, PrimitiveVtx &prim); + + unsigned int mPositionsStride; + unsigned int mNormalsStride; + unsigned int mTextureCoordsStride; + + // This vector is used to remap a position index into a list + // of all divergent vertices + std::vector > mVertexRemap; +}; + +#endif //_OBJ_LOADER_H_ diff --git a/tools/a3dconvert/SimpleMesh.h b/tools/a3dconvert/SimpleMesh.h new file mode 100644 index 000000000..57e1a7c2f --- /dev/null +++ b/tools/a3dconvert/SimpleMesh.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _SIMPLE_MESH_H_ +#define _SIMPLE_MESH_H_ + +#include +#include +using namespace android; +using namespace android::renderscript; + +class SimpleMesh { +public: + struct Channel { + std::vector mData; + std::string mName; + uint32_t mStride; + }; + + // Vertex channels (position, normal) + // This assumes all the data array are the same size + std::vector mChannels; + + // Triangle list index data + std::vector > mTriangleLists; + // Names of all the triangle lists + std::vector mTriangleListNames; + // Name of the entire object + std::string mName; + + // Adds another index set to the mesh + void appendFaceList(std::string name) { + mTriangleListNames.push_back(name); + mTriangleLists.push_back(std::vector()); + } + + // Adds another data channel (position, normal, etc.) + void appendChannel(std::string name, uint32_t stride) { + mChannels.push_back(Channel()); + static const uint32_t reserveVtx = 128; + mChannels.back().mData.reserve(reserveVtx*stride); + mChannels.back().mName = name; + mChannels.back().mStride = stride; + } + + SimpleMesh() { + // reserve some data in the vectors + // simply letting it grow by itself tends to waste a lot of time on + // rallocations / copies when dealing with geometry data + static const uint32_t reserveFaces = 8; + static const uint32_t reserveChannels = 8; + mTriangleLists.reserve(reserveFaces); + mTriangleListNames.reserve(reserveFaces); + mChannels.reserve(reserveChannels); + } + + // Generates a renderscript mesh that could be used for a3d serialization + Mesh *getMesh(Context *rsc) { + if (mChannels.size() == 0) { + return NULL; + } + + // Generate the element that describes our channel layout + rsc->mStateElement.elementBuilderBegin(); + for (uint32_t c = 0; c < mChannels.size(); c ++) { + // Skip empty channels + if (mChannels[c].mData.size() == 0) { + continue; + } + const Element *subElem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, mChannels[c].mStride); + rsc->mStateElement.elementBuilderAdd(subElem, mChannels[c].mName.c_str(), 1); + } + const Element *vertexDataElem = rsc->mStateElement.elementBuilderCreate(rsc); + + uint32_t numVerts = mChannels[0].mData.size()/mChannels[0].mStride; + Type *vertexDataType = Type::getType(rsc, vertexDataElem, numVerts, 0, 0, false, false); + vertexDataType->compute(); + + Allocation *vertexAlloc = new Allocation(rsc, vertexDataType, RS_ALLOCATION_USAGE_SCRIPT); + + uint32_t vertexSize = vertexDataElem->getSizeBytes()/sizeof(float); + // Fill this allocation with some data + float *dataPtr = (float*)vertexAlloc->getPtr(); + for (uint32_t i = 0; i < numVerts; i ++) { + // Find the pointer to the current vertex's data + uint32_t vertexPos = i*vertexSize; + float *vertexPtr = dataPtr + vertexPos; + + for (uint32_t c = 0; c < mChannels.size(); c ++) { + // Skip empty channels + if (mChannels[c].mData.size() == 0) { + continue; + } + for (uint32_t cStride = 0; cStride < mChannels[c].mStride; cStride ++) { + *(vertexPtr++) = mChannels[c].mData[i * mChannels[c].mStride + cStride]; + } + } + } + + // Now lets write index data + const Element *indexElem = Element::create(rsc, RS_TYPE_UNSIGNED_16, RS_KIND_USER, false, 1); + + Mesh *mesh = new Mesh(rsc); + mesh->setName(mName.c_str()); + mesh->mVertexBufferCount = 1; + mesh->mVertexBuffers = new ObjectBaseRef[1]; + mesh->mVertexBuffers[0].set(vertexAlloc); + + mesh->mPrimitivesCount = mTriangleLists.size(); + mesh->mPrimitives = new Mesh::Primitive_t *[mesh->mPrimitivesCount]; + + // load all primitives + for (uint32_t pCount = 0; pCount < mesh->mPrimitivesCount; pCount ++) { + Mesh::Primitive_t *prim = new Mesh::Primitive_t; + mesh->mPrimitives[pCount] = prim; + + uint32_t numIndicies = mTriangleLists[pCount].size(); + Type *indexType = Type::getType(rsc, indexElem, numIndicies, 0, 0, false, false ); + + indexType->compute(); + + Allocation *indexAlloc = new Allocation(rsc, indexType, RS_ALLOCATION_USAGE_SCRIPT); + uint16_t *indexPtr = (uint16_t*)indexAlloc->getPtr(); + const std::vector &indexList = mTriangleLists[pCount]; + uint32_t numTries = numIndicies / 3; + + for (uint32_t i = 0; i < numTries; i ++) { + indexPtr[i * 3 + 0] = (uint16_t)indexList[i * 3 + 0]; + indexPtr[i * 3 + 1] = (uint16_t)indexList[i * 3 + 1]; + indexPtr[i * 3 + 2] = (uint16_t)indexList[i * 3 + 2]; + } + indexAlloc->setName(mTriangleListNames[pCount].c_str()); + prim->mIndexBuffer.set(indexAlloc); + prim->mPrimitive = RS_PRIMITIVE_TRIANGLE; + } + + return mesh; + } +}; + +#endif diff --git a/tools/a3dconvert/a3dconvert.cpp b/tools/a3dconvert/a3dconvert.cpp new file mode 100644 index 000000000..33f5733fd --- /dev/null +++ b/tools/a3dconvert/a3dconvert.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "ColladaLoader.h" +#include "ObjLoader.h" + +int main (int argc, char * const argv[]) { + const char *objExt = ".obj"; + const char *daeExt = ".dae"; + + if(argc != 3) { + printf("-----------------------------------------------------------------\n"); + printf("Usage:\n"); + printf("a3dconvert input_file a3d_output_file\n"); + printf("Currently .obj and .dae (collada) input files are accepted\n"); + printf("-----------------------------------------------------------------\n"); + return 1; + } + + bool isSuccessful = false; + + std::string filename = argv[1]; + size_t dotPos = filename.find_last_of('.'); + if (dotPos == std::string::npos) { + printf("Invalid input. Currently .obj and .dae (collada) input files are accepted\n"); + return 1; + } + + std::string ext = filename.substr(dotPos); + if (ext == daeExt) { + ColladaLoader converter; + isSuccessful = converter.init(argv[1]); + if (isSuccessful) { + isSuccessful = converter.convertToA3D(argv[2]); + } + } else if (ext == objExt) { + ObjLoader objConv; + isSuccessful = objConv.init(argv[1]); + if (isSuccessful) { + isSuccessful = objConv.convertToA3D(argv[2]); + } + } else { + printf("Invalid input. Currently .obj and .dae (collada) input files are accepted\n"); + return 1; + } + + if(isSuccessful) { + printf("---All done---\n"); + } else { + printf("---Encountered errors, conversion failed---\n"); + } + + return isSuccessful ? 0 : 1; +} diff --git a/tools/a3dconvert/license.txt b/tools/a3dconvert/license.txt new file mode 100755 index 000000000..354667a84 --- /dev/null +++ b/tools/a3dconvert/license.txt @@ -0,0 +1,25 @@ +Parts of this code come from colladaDom with the license below. The rest is AOSP. + + + +The MIT License + +Copyright 2006 Sony Computer Entertainment Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.