development
Revision | 67a81ea84d34ca5b25c234bc28401d2fcc9cc57c (tree) |
---|---|
Zeit | 2011-02-24 02:41:15 |
Autor | Alex Sakhartchouk <alexst@goog...> |
Commiter | Android (Google) Code Review |
Merge "renderscript geometry converter. Initial implementation of the converted from .obj and .dae file to the renderscript file format."
@@ -0,0 +1,40 @@ | ||
1 | +# Copyright (C) 2011 The Android Open Source Project | |
2 | +# | |
3 | +# Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | +# you may not use this file except in compliance with the License. | |
5 | +# You may obtain a copy of the License at | |
6 | +# | |
7 | +# http://www.apache.org/licenses/LICENSE-2.0 | |
8 | +# | |
9 | +# Unless required by applicable law or agreed to in writing, software | |
10 | +# distributed under the License is distributed on an "AS IS" BASIS, | |
11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | +# See the License for the specific language governing permissions and | |
13 | +# limitations under the License. | |
14 | + | |
15 | +LOCAL_PATH := $(call my-dir) | |
16 | + | |
17 | +# Host executable | |
18 | +include $(CLEAR_VARS) | |
19 | +LOCAL_MODULE := a3dconvert | |
20 | +LOCAL_MODULE_TAGS := optional | |
21 | +LOCAL_CFLAGS += -DANDROID_RS_SERIALIZE | |
22 | +# Needed for colladaDom | |
23 | +LOCAL_CFLAGS += -DNO_BOOST -DDOM_INCLUDE_TINYXML -DNO_ZAE | |
24 | + | |
25 | +LOCAL_SRC_FILES := \ | |
26 | + a3dconvert.cpp \ | |
27 | + ObjLoader.cpp \ | |
28 | + ColladaConditioner.cpp \ | |
29 | + ColladaGeometry.cpp \ | |
30 | + ColladaLoader.cpp | |
31 | + | |
32 | + | |
33 | +LOCAL_C_INCLUDES += external/collada/include | |
34 | +LOCAL_C_INCLUDES += external/collada/include/1.4 | |
35 | +LOCAL_C_INCLUDES += frameworks/base/libs/rs | |
36 | + | |
37 | +LOCAL_LDLIBS := -lpthread | |
38 | +LOCAL_STATIC_LIBRARIES += libRSserialize libutils libcutils | |
39 | +LOCAL_STATIC_LIBRARIES += colladadom libtinyxml libpcrecpp libpcre | |
40 | +include $(BUILD_HOST_EXECUTABLE) |
@@ -0,0 +1,203 @@ | ||
1 | +/* | |
2 | +* Copyright 2006 Sony Computer Entertainment Inc. | |
3 | +* | |
4 | +* Licensed under the MIT Open Source License, for details please see license.txt or the website | |
5 | +* http://www.opensource.org/licenses/mit-license.php | |
6 | +* | |
7 | +*/ | |
8 | + | |
9 | +#include "ColladaConditioner.h" | |
10 | +unsigned int ColladaConditioner::getMaxOffset( domInputLocalOffset_Array &input_array ) { | |
11 | + | |
12 | + unsigned int maxOffset = 0; | |
13 | + for ( unsigned int i = 0; i < input_array.getCount(); i++ ) { | |
14 | + if ( input_array[i]->getOffset() > maxOffset ) { | |
15 | + maxOffset = (unsigned int)input_array[i]->getOffset(); | |
16 | + } | |
17 | + } | |
18 | + return maxOffset; | |
19 | +} | |
20 | + | |
21 | +void ColladaConditioner::createTrianglesFromPolylist( domMesh *thisMesh, domPolylist *thisPolylist ) { | |
22 | + | |
23 | + // Create a new <triangles> inside the mesh that has the same material as the <polylist> | |
24 | + domTriangles *thisTriangles = (domTriangles *)thisMesh->createAndPlace("triangles"); | |
25 | + //thisTriangles->setCount( 0 ); | |
26 | + unsigned int triangles = 0; | |
27 | + thisTriangles->setMaterial(thisPolylist->getMaterial()); | |
28 | + domP* p_triangles = (domP*)thisTriangles->createAndPlace("p"); | |
29 | + | |
30 | + // Give the new <triangles> the same <_dae> and <parameters> as the old <polylist> | |
31 | + for(int i=0; i<(int)(thisPolylist->getInput_array().getCount()); i++) { | |
32 | + | |
33 | + thisTriangles->placeElement( thisPolylist->getInput_array()[i]->clone() ); | |
34 | + } | |
35 | + | |
36 | + // Get the number of inputs and primitives for the polygons array | |
37 | + int numberOfInputs = (int)getMaxOffset(thisPolylist->getInput_array()) + 1; | |
38 | + int numberOfPrimitives = (int)(thisPolylist->getVcount()->getValue().getCount()); | |
39 | + | |
40 | + unsigned int offset = 0; | |
41 | + | |
42 | + // Triangulate all the primitives, this generates all the triangles in a single <p> element | |
43 | + for(int j = 0; j < numberOfPrimitives; j++) { | |
44 | + | |
45 | + int triangleCount = (int)thisPolylist->getVcount()->getValue()[j] -2; | |
46 | + // Write out the primitives as triangles, just fan using the first element as the base | |
47 | + int idx = numberOfInputs; | |
48 | + for(int k = 0; k < triangleCount; k++) { | |
49 | + // First vertex | |
50 | + for(int l = 0; l < numberOfInputs; l++) { | |
51 | + | |
52 | + p_triangles->getValue().append(thisPolylist->getP()->getValue()[offset + l]); | |
53 | + } | |
54 | + // Second vertex | |
55 | + for(int l = 0; l < numberOfInputs; l++) { | |
56 | + | |
57 | + p_triangles->getValue().append(thisPolylist->getP()->getValue()[offset + idx + l]); | |
58 | + } | |
59 | + // Third vertex | |
60 | + idx += numberOfInputs; | |
61 | + for(int l = 0; l < numberOfInputs; l++) { | |
62 | + | |
63 | + p_triangles->getValue().append(thisPolylist->getP()->getValue()[offset + idx + l]); | |
64 | + } | |
65 | + triangles++; | |
66 | + } | |
67 | + offset += (unsigned int)thisPolylist->getVcount()->getValue()[j] * numberOfInputs; | |
68 | + } | |
69 | + thisTriangles->setCount( triangles ); | |
70 | + | |
71 | +} | |
72 | + | |
73 | +void ColladaConditioner::createTrianglesFromPolygons( domMesh *thisMesh, domPolygons *thisPolygons ) { | |
74 | + | |
75 | + // Create a new <triangles> inside the mesh that has the same material as the <polygons> | |
76 | + domTriangles *thisTriangles = (domTriangles *)thisMesh->createAndPlace("triangles"); | |
77 | + thisTriangles->setCount( 0 ); | |
78 | + thisTriangles->setMaterial(thisPolygons->getMaterial()); | |
79 | + domP* p_triangles = (domP*)thisTriangles->createAndPlace("p"); | |
80 | + | |
81 | + // Give the new <triangles> the same <_dae> and <parameters> as the old <polygons> | |
82 | + for(int i=0; i<(int)(thisPolygons->getInput_array().getCount()); i++) { | |
83 | + | |
84 | + thisTriangles->placeElement( thisPolygons->getInput_array()[i]->clone() ); | |
85 | + } | |
86 | + | |
87 | + // Get the number of inputs and primitives for the polygons array | |
88 | + int numberOfInputs = (int)getMaxOffset(thisPolygons->getInput_array()) +1; | |
89 | + int numberOfPrimitives = (int)(thisPolygons->getP_array().getCount()); | |
90 | + | |
91 | + // Triangulate all the primitives, this generates all the triangles in a single <p> element | |
92 | + for(int j = 0; j < numberOfPrimitives; j++) { | |
93 | + | |
94 | + // Check the polygons for consistancy (some exported files have had the wrong number of indices) | |
95 | + domP * thisPrimitive = thisPolygons->getP_array()[j]; | |
96 | + int elementCount = (int)(thisPrimitive->getValue().getCount()); | |
97 | + // Skip the invalid primitive | |
98 | + if((elementCount % numberOfInputs) != 0) { | |
99 | + continue; | |
100 | + } else { | |
101 | + int triangleCount = (elementCount/numberOfInputs)-2; | |
102 | + // Write out the primitives as triangles, just fan using the first element as the base | |
103 | + int idx = numberOfInputs; | |
104 | + for(int k = 0; k < triangleCount; k++) { | |
105 | + // First vertex | |
106 | + for(int l = 0; l < numberOfInputs; l++) { | |
107 | + | |
108 | + p_triangles->getValue().append(thisPrimitive->getValue()[l]); | |
109 | + } | |
110 | + // Second vertex | |
111 | + for(int l = 0; l < numberOfInputs; l++) { | |
112 | + | |
113 | + p_triangles->getValue().append(thisPrimitive->getValue()[idx + l]); | |
114 | + } | |
115 | + // Third vertex | |
116 | + idx += numberOfInputs; | |
117 | + for(int l = 0; l < numberOfInputs; l++) { | |
118 | + | |
119 | + p_triangles->getValue().append(thisPrimitive->getValue()[idx + l]); | |
120 | + } | |
121 | + thisTriangles->setCount(thisTriangles->getCount()+1); | |
122 | + } | |
123 | + } | |
124 | + } | |
125 | + | |
126 | +} | |
127 | + | |
128 | + | |
129 | +bool ColladaConditioner::triangulate(DAE *dae) { | |
130 | + | |
131 | + int error = 0; | |
132 | + | |
133 | + // How many geometry elements are there? | |
134 | + int geometryElementCount = (int)(dae->getDatabase()->getElementCount(NULL, "geometry" )); | |
135 | + | |
136 | + for(int currentGeometry = 0; currentGeometry < geometryElementCount; currentGeometry++) { | |
137 | + | |
138 | + // Find the next geometry element | |
139 | + domGeometry *thisGeometry; | |
140 | + // error = _dae->getDatabase()->getElement((daeElement**)&thisGeometry,currentGeometry, NULL, "geometry"); | |
141 | + daeElement * element = 0; | |
142 | + error = dae->getDatabase()->getElement(&element,currentGeometry, NULL, "geometry"); | |
143 | + thisGeometry = (domGeometry *) element; | |
144 | + | |
145 | + // Get the mesh out of the geometry | |
146 | + domMesh *thisMesh = thisGeometry->getMesh(); | |
147 | + | |
148 | + if (thisMesh == NULL){ | |
149 | + continue; | |
150 | + } | |
151 | + | |
152 | + // Loop over all the polygon elements | |
153 | + for(int currentPolygons = 0; currentPolygons < (int)(thisMesh->getPolygons_array().getCount()); currentPolygons++) { | |
154 | + | |
155 | + // Get the polygons out of the mesh | |
156 | + // Always get index 0 because every pass through this loop deletes the <polygons> element as it finishes with it | |
157 | + domPolygons *thisPolygons = thisMesh->getPolygons_array()[currentPolygons]; | |
158 | + createTrianglesFromPolygons( thisMesh, thisPolygons ); | |
159 | + } | |
160 | + while (thisMesh->getPolygons_array().getCount() > 0) { | |
161 | + | |
162 | + domPolygons *thisPolygons = thisMesh->getPolygons_array().get(0); | |
163 | + // Remove the polygons from the mesh | |
164 | + thisMesh->removeChildElement(thisPolygons); | |
165 | + } | |
166 | + int polylistElementCount = (int)(thisMesh->getPolylist_array().getCount()); | |
167 | + for(int currentPolylist = 0; currentPolylist < polylistElementCount; currentPolylist++) { | |
168 | + | |
169 | + // Get the polylist out of the mesh | |
170 | + // Always get index 0 because every pass through this loop deletes the <polygons> element as it finishes with it | |
171 | + domPolylist *thisPolylist = thisMesh->getPolylist_array()[currentPolylist]; | |
172 | + createTrianglesFromPolylist( thisMesh, thisPolylist ); | |
173 | + } | |
174 | + while (thisMesh->getPolylist_array().getCount() > 0) { | |
175 | + | |
176 | + domPolylist *thisPolylist = thisMesh->getPolylist_array().get(0); | |
177 | + // Remove the polylist from the mesh | |
178 | + thisMesh->removeChildElement(thisPolylist); | |
179 | + } | |
180 | + } | |
181 | + return (error == 0); | |
182 | +} | |
183 | + | |
184 | +bool ColladaConditioner::triangulate(const char *inputFile) { | |
185 | + | |
186 | + DAE dae; | |
187 | + bool convertSuceeded = true; | |
188 | + domCOLLADA* root = dae.open(inputFile); | |
189 | + | |
190 | + if (!root) { | |
191 | + printf("Failed to read file %s.\n", inputFile); | |
192 | + return false; | |
193 | + } | |
194 | + | |
195 | + convertSuceeded = triangulate(&dae); | |
196 | + | |
197 | + dae.writeAll(); | |
198 | + if(!convertSuceeded) { | |
199 | + printf("Encountered errors\n"); | |
200 | + } | |
201 | + | |
202 | + return convertSuceeded; | |
203 | +} | |
\ No newline at end of file |
@@ -0,0 +1,28 @@ | ||
1 | +/* | |
2 | +* Copyright 2006 Sony Computer Entertainment Inc. | |
3 | +* | |
4 | +* Licensed under the MIT Open Source License, for details please see license.txt or the website | |
5 | +* http://www.opensource.org/licenses/mit-license.php | |
6 | +* | |
7 | +*/ | |
8 | + | |
9 | +#ifndef COLLADA_CONDITIONER | |
10 | +#define COLLADA_CONDITIONER | |
11 | + | |
12 | +#include <dae.h> | |
13 | +#include <dom/domConstants.h> | |
14 | +#include <dom/domCOLLADA.h> | |
15 | + | |
16 | +class ColladaConditioner { | |
17 | + | |
18 | +private: | |
19 | + unsigned int getMaxOffset( domInputLocalOffset_Array &input_array ); | |
20 | + void createTrianglesFromPolylist( domMesh *thisMesh, domPolylist *thisPolylist ); | |
21 | + void createTrianglesFromPolygons( domMesh *thisMesh, domPolygons *thisPolygons ); | |
22 | + | |
23 | +public: | |
24 | + bool triangulate(DAE *dae); | |
25 | + bool triangulate(const char *inputFile); | |
26 | +}; | |
27 | + | |
28 | +#endif //COLLADA_CONDITIONER |
@@ -0,0 +1,319 @@ | ||
1 | +/* | |
2 | +* Copyright 2006 Sony Computer Entertainment Inc. | |
3 | +* | |
4 | +* Licensed under the MIT Open Source License, for details please see license.txt or the website | |
5 | +* http://www.opensource.org/licenses/mit-license.php | |
6 | +* | |
7 | +*/ | |
8 | + | |
9 | +#include "ColladaGeometry.h" | |
10 | +#include <iostream> | |
11 | +#include <sstream> | |
12 | + | |
13 | +ColladaGeometry::ColladaGeometry() : | |
14 | + mPositionFloats(NULL), mPositionOffset(-1), | |
15 | + mNormalFloats(NULL), mNormalOffset(-1), | |
16 | + mTangentFloats(NULL), mTangentOffset(-1), | |
17 | + mBinormalFloats(NULL), mBinormalOffset(-1), | |
18 | + mTexture1Floats(NULL), mTexture1Offset(-1), | |
19 | + mMultiIndexOffset(-1), | |
20 | + mPositionsStride(3), mNormalsStride(3), | |
21 | + mTextureCoordsStride(2), mTangentssStride(3), mBinormalsStride(3) { | |
22 | + | |
23 | + mConvertedMesh.appendChannel("position", mPositionsStride); | |
24 | + mConvertedMesh.appendChannel("normal", mNormalsStride); | |
25 | + mConvertedMesh.appendChannel("texture0", mTextureCoordsStride); | |
26 | + mConvertedMesh.appendChannel("binormal", mBinormalsStride); | |
27 | + mConvertedMesh.appendChannel("tangent", mTangentssStride); | |
28 | + | |
29 | + mPositions = &mConvertedMesh.mChannels[0].mData; | |
30 | + mNormals = &mConvertedMesh.mChannels[1].mData; | |
31 | + mTextureCoords = &mConvertedMesh.mChannels[2].mData; | |
32 | + mBinormals = &mConvertedMesh.mChannels[3].mData; | |
33 | + mTangents = &mConvertedMesh.mChannels[4].mData; | |
34 | +} | |
35 | + | |
36 | +bool ColladaGeometry::init(domGeometryRef geometry) { | |
37 | + | |
38 | + bool convertSuceeded = true; | |
39 | + | |
40 | + const char* geoName = geometry->getName(); | |
41 | + if (geoName == NULL) { | |
42 | + geoName = geometry->getId(); | |
43 | + } | |
44 | + mConvertedMesh.mName = geoName; | |
45 | + mMesh = geometry->getMesh(); | |
46 | + | |
47 | + // Iterate over all the index groups and build up a simple resolved tri list and vertex array | |
48 | + const domTriangles_Array &allTriLists = mMesh->getTriangles_array(); | |
49 | + int numTriLists = allTriLists.getCount(); | |
50 | + mConvertedMesh.mTriangleLists.reserve(numTriLists); | |
51 | + mConvertedMesh.mTriangleListNames.reserve(numTriLists); | |
52 | + for (int i = 0; i < numTriLists; i ++) { | |
53 | + addTriangles(allTriLists[i]); | |
54 | + } | |
55 | + | |
56 | + return convertSuceeded; | |
57 | +} | |
58 | + | |
59 | +void ColladaGeometry::addTriangles(domTriangles * colladaTriangles) { | |
60 | + | |
61 | + int numTriangles = colladaTriangles->getCount(); | |
62 | + int triListIndex = mConvertedMesh.mTriangleLists.size(); | |
63 | + mConvertedMesh.mTriangleLists.resize(triListIndex + 1); | |
64 | + std::string materialName = colladaTriangles->getMaterial(); | |
65 | + if (materialName.size() == 0) { | |
66 | + char buffer[128]; | |
67 | + sprintf(buffer, "index%d", triListIndex); | |
68 | + materialName = buffer; | |
69 | + } | |
70 | + mConvertedMesh.mTriangleListNames.push_back(materialName); | |
71 | + | |
72 | + // It's a good idea to tell stl how much memory we intend to use | |
73 | + // to limit the number of reallocations | |
74 | + mPositions->reserve(numTriangles * 3); | |
75 | + mNormals->reserve(numTriangles * 3); | |
76 | + mTangents->reserve(numTriangles * 3); | |
77 | + mBinormals->reserve(numTriangles * 3); | |
78 | + mTextureCoords->reserve(numTriangles * 3); | |
79 | + | |
80 | + // Stores the pointers to the image data and where in the tri list that data comes from | |
81 | + cacheOffsetsAndDataPointers(colladaTriangles); | |
82 | + | |
83 | + // Collapse the multiindex that collada uses | |
84 | + const domListOfUInts &colladaIndexList = colladaTriangles->getP()->getValue(); | |
85 | + std::vector<uint32_t> &a3dIndexList = mConvertedMesh.mTriangleLists[triListIndex]; | |
86 | + a3dIndexList.resize(numTriangles * 3); | |
87 | + for (int i = 0; i < numTriangles * 3; i ++) { | |
88 | + | |
89 | + a3dIndexList[i] = remapIndexAndStoreData(colladaIndexList, i); | |
90 | + } | |
91 | + | |
92 | +} | |
93 | + | |
94 | +void ColladaGeometry::cacheOffsetsAndDataPointers(domTriangles * colladaTriangles) { | |
95 | + // Define the names of known vertex channels | |
96 | + const char *positionSemantic = "POSITION"; | |
97 | + const char *vertexSemantic = "VERTEX"; | |
98 | + const char *normalSemantic = "NORMAL"; | |
99 | + const char *tangentSemantic = "TANGENT"; | |
100 | + const char *binormalSemantic = "BINORMAL"; | |
101 | + const char *texture1Semantic = "TEXCOORD"; | |
102 | + | |
103 | + const domInputLocalOffset_Array &inputs = colladaTriangles->getInput_array(); | |
104 | + mMultiIndexOffset = inputs.getCount(); | |
105 | + | |
106 | + // inputs with offsets | |
107 | + // There are two places collada can put links to our data | |
108 | + // 1 - in the VERTEX, which is its way of saying follow a link to the vertex structure | |
109 | + // then every geometry array you find there is the same size as the position array | |
110 | + // 2 - a direct link to the channel from the primitive list. This tells us that there are | |
111 | + // potentially more or less floats in those channels because there is some vertex re-use | |
112 | + // or divergence in that data channel. For example, highly segmented uv set would produce a | |
113 | + // larger array because for every physical vertex position thre might be 2 or more uv coords | |
114 | + for (uint32_t i = 0; i < inputs.getCount(); i ++) { | |
115 | + | |
116 | + int currentOffset = inputs[i]->getOffset(); | |
117 | + const char *currentSemantic = inputs[i]->getSemantic(); | |
118 | + | |
119 | + domSource * source = (domSource*) (domElement*) inputs[i]->getSource().getElement(); | |
120 | + if (strcmp(vertexSemantic, currentSemantic) == 0) { | |
121 | + mPositionOffset = currentOffset; | |
122 | + } | |
123 | + else if (strcmp(normalSemantic, currentSemantic) == 0) { | |
124 | + mNormalOffset = currentOffset; | |
125 | + mNormalFloats = &source->getFloat_array()->getValue(); | |
126 | + } | |
127 | + else if (strcmp(tangentSemantic, currentSemantic) == 0) { | |
128 | + mTangentOffset = currentOffset; | |
129 | + mTangentFloats = &source->getFloat_array()->getValue(); | |
130 | + } | |
131 | + else if (strcmp(binormalSemantic, currentSemantic) == 0) { | |
132 | + mBinormalOffset = currentOffset; | |
133 | + mBinormalFloats = &source->getFloat_array()->getValue(); | |
134 | + } | |
135 | + else if (strcmp(texture1Semantic, currentSemantic) == 0) { | |
136 | + mTexture1Offset = currentOffset; | |
137 | + mTexture1Floats = & source->getFloat_array()->getValue(); | |
138 | + } | |
139 | + } | |
140 | + | |
141 | + // There are multiple ways of getting to data, so follow them all | |
142 | + domVertices * vertices = mMesh->getVertices(); | |
143 | + const domInputLocal_Array &verticesInputs = vertices->getInput_array(); | |
144 | + for (uint32_t i = 0; i < verticesInputs.getCount(); i ++) { | |
145 | + | |
146 | + const char *currentSemantic = verticesInputs[i]->getSemantic(); | |
147 | + | |
148 | + domSource * source = (domSource*) (domElement*) verticesInputs[i]->getSource().getElement(); | |
149 | + if (strcmp(positionSemantic, currentSemantic) == 0) { | |
150 | + mPositionFloats = & source->getFloat_array()->getValue(); | |
151 | + // TODO: Querry this from the accessor in the future because | |
152 | + // I supopose it's possible to have 4 floats if we hide something in w | |
153 | + int numberOfFloatsPerPoint = 3; | |
154 | + // We want to cllapse duplicate vertices, otherwise we could just unroll the tri list | |
155 | + mVertexRemap.resize(source->getFloat_array()->getCount()/numberOfFloatsPerPoint); | |
156 | + } | |
157 | + else if (strcmp(normalSemantic, currentSemantic) == 0) { | |
158 | + mNormalFloats = & source->getFloat_array()->getValue(); | |
159 | + mNormalOffset = mPositionOffset; | |
160 | + } | |
161 | + else if (strcmp(tangentSemantic, currentSemantic) == 0) { | |
162 | + mTangentFloats = & source->getFloat_array()->getValue(); | |
163 | + mTangentOffset = mPositionOffset; | |
164 | + } | |
165 | + else if (strcmp(binormalSemantic, currentSemantic) == 0) { | |
166 | + mBinormalFloats = & source->getFloat_array()->getValue(); | |
167 | + mBinormalOffset = mPositionOffset; | |
168 | + } | |
169 | + else if (strcmp(texture1Semantic, currentSemantic) == 0) { | |
170 | + mTexture1Floats = & source->getFloat_array()->getValue(); | |
171 | + mTexture1Offset = mPositionOffset; | |
172 | + } | |
173 | + } | |
174 | +} | |
175 | + | |
176 | +int ColladaGeometry::remapIndexAndStoreData(const domListOfUInts &colladaIndexList, int indexToRemap) { | |
177 | + | |
178 | + domUint positionIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mPositionOffset]; | |
179 | + | |
180 | + float posX = (*mPositionFloats)[positionIndex * mPositionsStride + 0]; | |
181 | + float posY = (*mPositionFloats)[positionIndex * mPositionsStride + 1]; | |
182 | + float posZ = (*mPositionFloats)[positionIndex * mPositionsStride + 2]; | |
183 | + | |
184 | + float normX = 0; | |
185 | + float normY = 0; | |
186 | + float normZ = 0; | |
187 | + | |
188 | + if (mNormalOffset != -1) { | |
189 | + domUint normalIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mNormalOffset]; | |
190 | + normX = (*mNormalFloats)[normalIndex * mNormalsStride + 0]; | |
191 | + normY = (*mNormalFloats)[normalIndex * mNormalsStride + 1]; | |
192 | + normZ = (*mNormalFloats)[normalIndex * mNormalsStride + 2]; | |
193 | + } | |
194 | + | |
195 | + float tanX = 0; | |
196 | + float tanY = 0; | |
197 | + float tanZ = 0; | |
198 | + | |
199 | + if (mTangentOffset != -1) { | |
200 | + domUint tangentIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mTangentOffset]; | |
201 | + tanX = (*mTangentFloats)[tangentIndex * mTangentssStride + 0]; | |
202 | + tanY = (*mTangentFloats)[tangentIndex * mTangentssStride + 1]; | |
203 | + tanZ = (*mTangentFloats)[tangentIndex * mTangentssStride + 2]; | |
204 | + } | |
205 | + | |
206 | + float binormX = 0; | |
207 | + float binormY = 0; | |
208 | + float binormZ = 0; | |
209 | + | |
210 | + if (mBinormalOffset != -1) { | |
211 | + domUint binormalIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mNormalOffset]; | |
212 | + binormX = (*mBinormalFloats)[binormalIndex * mBinormalsStride + 0]; | |
213 | + binormY = (*mBinormalFloats)[binormalIndex * mBinormalsStride + 1]; | |
214 | + binormZ = (*mBinormalFloats)[binormalIndex * mBinormalsStride + 2]; | |
215 | + } | |
216 | + | |
217 | + float texCoordX = 0; | |
218 | + float texCoordY = 0; | |
219 | + | |
220 | + if (mTexture1Offset != -1) { | |
221 | + domUint texCoordIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mTexture1Offset]; | |
222 | + texCoordX = (*mTexture1Floats)[texCoordIndex * mTextureCoordsStride + 0]; | |
223 | + texCoordY = (*mTexture1Floats)[texCoordIndex * mTextureCoordsStride + 1]; | |
224 | + } | |
225 | + | |
226 | + std::vector<uint32_t> &ithRemapList = mVertexRemap[positionIndex]; | |
227 | + // We may have some potential vertices we can reuse | |
228 | + // loop over all the potential candidates and see if any match our guy | |
229 | + for (uint32_t i = 0; i < ithRemapList.size(); i ++) { | |
230 | + | |
231 | + int ithRemap = ithRemapList[i]; | |
232 | + // compare existing vertex with the new one | |
233 | + if ((*mPositions)[ithRemap * mPositionsStride + 0] != posX || | |
234 | + (*mPositions)[ithRemap * mPositionsStride + 1] != posY || | |
235 | + (*mPositions)[ithRemap * mPositionsStride + 2] != posZ) { | |
236 | + continue; | |
237 | + } | |
238 | + | |
239 | + // Now go over normals | |
240 | + if (mNormalOffset != -1) { | |
241 | + if ((*mNormals)[ithRemap * mNormalsStride + 0] != normX || | |
242 | + (*mNormals)[ithRemap * mNormalsStride + 1] != normY || | |
243 | + (*mNormals)[ithRemap * mNormalsStride + 2] != normZ) { | |
244 | + continue; | |
245 | + } | |
246 | + } | |
247 | + | |
248 | + // Now go over tangents | |
249 | + if (mTangentOffset != -1) { | |
250 | + if ((*mTangents)[ithRemap * mTangentssStride + 0] != tanX || | |
251 | + (*mTangents)[ithRemap * mTangentssStride + 1] != tanY || | |
252 | + (*mTangents)[ithRemap * mTangentssStride + 2] != tanZ) { | |
253 | + continue; | |
254 | + } | |
255 | + } | |
256 | + | |
257 | + // Now go over binormals | |
258 | + if (mBinormalOffset != -1) { | |
259 | + if ((*mBinormals)[ithRemap * mBinormalsStride + 0] != binormX || | |
260 | + (*mBinormals)[ithRemap * mBinormalsStride + 1] != binormY || | |
261 | + (*mBinormals)[ithRemap * mBinormalsStride + 2] != binormZ) { | |
262 | + continue; | |
263 | + } | |
264 | + } | |
265 | + | |
266 | + // And texcoords | |
267 | + if (mTexture1Offset != -1) { | |
268 | + if ((*mTextureCoords)[ithRemap * mTextureCoordsStride + 0] != texCoordX || | |
269 | + (*mTextureCoords)[ithRemap * mTextureCoordsStride + 1] != texCoordY) { | |
270 | + continue; | |
271 | + } | |
272 | + } | |
273 | + | |
274 | + // If we got here the new vertex is identical to the one that we already stored | |
275 | + return ithRemap; | |
276 | + } | |
277 | + | |
278 | + // We did not encounter this vertex yet, store it and return its index | |
279 | + mPositions->push_back(posX); | |
280 | + mPositions->push_back(posY); | |
281 | + mPositions->push_back(posZ); | |
282 | + | |
283 | + if (mNormalOffset != -1) { | |
284 | + mNormals->push_back(normX); | |
285 | + mNormals->push_back(normY); | |
286 | + mNormals->push_back(normZ); | |
287 | + } | |
288 | + | |
289 | + if (mTangentOffset != -1) { | |
290 | + mTangents->push_back(tanX); | |
291 | + mTangents->push_back(tanY); | |
292 | + mTangents->push_back(tanZ); | |
293 | + } | |
294 | + | |
295 | + if (mBinormalOffset != -1) { | |
296 | + mBinormals->push_back(binormX); | |
297 | + mBinormals->push_back(binormY); | |
298 | + mBinormals->push_back(binormZ); | |
299 | + } | |
300 | + | |
301 | + if (mTexture1Offset != -1) { | |
302 | + mTextureCoords->push_back(texCoordX); | |
303 | + mTextureCoords->push_back(texCoordY); | |
304 | + } | |
305 | + | |
306 | + // We need to remember this mapping. Since we are storing floats, not vec3's, need to | |
307 | + // divide by position size to get the right index | |
308 | + int currentVertexIndex = (mPositions->size()/mPositionsStride) - 1; | |
309 | + ithRemapList.push_back(currentVertexIndex); | |
310 | + | |
311 | + return currentVertexIndex; | |
312 | +} | |
313 | + | |
314 | + | |
315 | + | |
316 | + | |
317 | + | |
318 | + | |
319 | + |
@@ -0,0 +1,84 @@ | ||
1 | +/* | |
2 | +* Copyright 2006 Sony Computer Entertainment Inc. | |
3 | +* | |
4 | +* Licensed under the MIT Open Source License, for details please see license.txt or the website | |
5 | +* http://www.opensource.org/licenses/mit-license.php | |
6 | +* | |
7 | +*/ | |
8 | + | |
9 | +#ifndef _COLLADA_GEOMETRY_H_ | |
10 | +#define _COLLADA_GEOMETRY_H_ | |
11 | + | |
12 | +#include <dae.h> | |
13 | +#include <dom/domCOLLADA.h> | |
14 | +#include <vector> | |
15 | +#include <string> | |
16 | + | |
17 | +#include "rsContext.h" | |
18 | +#include "rsMesh.h" | |
19 | +#include "SimpleMesh.h" | |
20 | + | |
21 | +using namespace android; | |
22 | +using namespace android::renderscript; | |
23 | + | |
24 | + | |
25 | +class ColladaGeometry { | |
26 | +public: | |
27 | + ColladaGeometry(); | |
28 | + bool init(domGeometryRef geometry); | |
29 | + | |
30 | + Mesh *getMesh(Context *rsc) { | |
31 | + return mConvertedMesh.getMesh(rsc); | |
32 | + } | |
33 | + | |
34 | +private: | |
35 | + | |
36 | + //Store some collada stuff | |
37 | + domMesh *mMesh; | |
38 | + | |
39 | + // Cache the pointers to the collada version of the data | |
40 | + // This contains raw vertex data that is not necessarily the same size for all | |
41 | + // Offset refers to the way collada packs each triangle's index to position / normal / etc. | |
42 | + domListOfFloats *mPositionFloats; | |
43 | + int mPositionOffset; | |
44 | + domListOfFloats *mNormalFloats; | |
45 | + int mNormalOffset; | |
46 | + domListOfFloats *mTangentFloats; | |
47 | + int mTangentOffset; | |
48 | + domListOfFloats *mBinormalFloats; | |
49 | + int mBinormalOffset; | |
50 | + domListOfFloats *mTexture1Floats; | |
51 | + int mTexture1Offset; | |
52 | + | |
53 | + // In the list of triangles, collada uses multiple indecies per triangle to point to the correct | |
54 | + // index in all the different arrays. We need to know the total number of these guys so we can | |
55 | + // just to the next triangle to process | |
56 | + int mMultiIndexOffset; | |
57 | + | |
58 | + // All these vectors would contain the same number of "points" | |
59 | + // index*stride would properly get to the uv, normal etc. | |
60 | + // collada, like maya and many others keep point array, normal array etc | |
61 | + // different size in the cases the same vertex produces divergent normals for different faces | |
62 | + std::vector<float> *mPositions; | |
63 | + unsigned int mPositionsStride; | |
64 | + std::vector<float> *mNormals; | |
65 | + unsigned int mNormalsStride; | |
66 | + std::vector<float> *mTextureCoords; | |
67 | + unsigned int mTextureCoordsStride; | |
68 | + std::vector<float> *mTangents; | |
69 | + unsigned int mTangentssStride; | |
70 | + std::vector<float> *mBinormals; | |
71 | + unsigned int mBinormalsStride; | |
72 | + | |
73 | + SimpleMesh mConvertedMesh; | |
74 | + | |
75 | + // This vector is used to remap a position index into a list of all divergent vertices | |
76 | + std::vector<std::vector<unsigned int> > mVertexRemap; | |
77 | + | |
78 | + void addTriangles(domTriangles * colladaTriangles); | |
79 | + void cacheOffsetsAndDataPointers(domTriangles * colladaTriangles); | |
80 | + int remapIndexAndStoreData(const domListOfUInts &colladaIndexList, int indexToRemap); | |
81 | + | |
82 | +}; | |
83 | + | |
84 | +#endif //COLLADA_TO_A3D_GEOMETRY |
@@ -0,0 +1,120 @@ | ||
1 | +/* | |
2 | + * Copyright (C) 2011 The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +#include "ColladaLoader.h" | |
18 | +#include "ColladaConditioner.h" | |
19 | +#include "ColladaGeometry.h" | |
20 | +#include "rsContext.h" | |
21 | +#include "rsFileA3D.h" | |
22 | + | |
23 | +#include <dae.h> | |
24 | +#include <dom/domCOLLADA.h> | |
25 | + | |
26 | +ColladaLoader::ColladaLoader() { | |
27 | + | |
28 | +} | |
29 | + | |
30 | +ColladaLoader::~ColladaLoader() { | |
31 | + clearGeometry(); | |
32 | +} | |
33 | + | |
34 | +void ColladaLoader::clearGeometry() { | |
35 | + for (uint32_t i = 0; i < mGeometries.size(); i++) { | |
36 | + delete mGeometries[i]; | |
37 | + } | |
38 | + mGeometries.clear(); | |
39 | +} | |
40 | + | |
41 | +bool ColladaLoader::init(const char *colladaFile) { | |
42 | + DAE dae; | |
43 | + | |
44 | + clearGeometry(); | |
45 | + | |
46 | + bool convertSuceeded = true; | |
47 | + | |
48 | + domCOLLADA* root = dae.open(colladaFile); | |
49 | + if (!root) { | |
50 | + fprintf(stderr, "Failed to read file %s.\n", colladaFile); | |
51 | + return false; | |
52 | + } | |
53 | + | |
54 | + // We only want to deal with triangulated meshes since rendering complex polygons is not feasible | |
55 | + ColladaConditioner conditioner; | |
56 | + conditioner.triangulate(&dae); | |
57 | + | |
58 | + domLibrary_geometries *allGeometry = daeSafeCast<domLibrary_geometries>(root->getDescendant("library_geometries")); | |
59 | + | |
60 | + if (allGeometry) { | |
61 | + convertSuceeded = convertAllGeometry(allGeometry) && convertSuceeded; | |
62 | + } | |
63 | + | |
64 | + return convertSuceeded; | |
65 | +} | |
66 | + | |
67 | +bool ColladaLoader::convertToA3D(const char *a3dFile) { | |
68 | + if (mGeometries.size() == 0) { | |
69 | + return false; | |
70 | + } | |
71 | + // Now write all this stuff out | |
72 | + Context rsc; | |
73 | + FileA3D file(&rsc); | |
74 | + | |
75 | + for (uint32_t i = 0; i < mGeometries.size(); i++) { | |
76 | + Mesh *exportedMesh = mGeometries[i]->getMesh(&rsc); | |
77 | + file.appendToFile(exportedMesh); | |
78 | + delete exportedMesh; | |
79 | + } | |
80 | + | |
81 | + file.writeFile(a3dFile); | |
82 | + return true; | |
83 | +} | |
84 | + | |
85 | +bool ColladaLoader::convertAllGeometry(domLibrary_geometries *allGeometry) { | |
86 | + | |
87 | + bool convertSuceeded = true; | |
88 | + domGeometry_Array &geo_array = allGeometry->getGeometry_array(); | |
89 | + for (size_t i = 0; i < geo_array.getCount(); i++) { | |
90 | + domGeometry *geometry = geo_array[i]; | |
91 | + const char *geometryName = geometry->getName(); | |
92 | + if (geometryName == NULL) { | |
93 | + geometryName = geometry->getId(); | |
94 | + } | |
95 | + | |
96 | + domMeshRef mesh = geometry->getMesh(); | |
97 | + if (mesh != NULL) { | |
98 | + printf("Converting geometry: %s\n", geometryName); | |
99 | + convertSuceeded = convertGeometry(geometry) && convertSuceeded; | |
100 | + } else { | |
101 | + printf("Skipping geometry: %s, unsupported type\n", geometryName); | |
102 | + } | |
103 | + | |
104 | + } | |
105 | + | |
106 | + return convertSuceeded; | |
107 | +} | |
108 | + | |
109 | +bool ColladaLoader::convertGeometry(domGeometry *geometry) { | |
110 | + bool convertSuceeded = true; | |
111 | + | |
112 | + domMeshRef mesh = geometry->getMesh(); | |
113 | + | |
114 | + ColladaGeometry *convertedGeo = new ColladaGeometry(); | |
115 | + convertedGeo->init(geometry); | |
116 | + | |
117 | + mGeometries.push_back(convertedGeo); | |
118 | + | |
119 | + return convertSuceeded; | |
120 | +} |
@@ -0,0 +1,43 @@ | ||
1 | +/* | |
2 | + * Copyright (C) 2011 The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +#ifndef _COLLADA_LOADER_H_ | |
18 | +#define _COLLADA_LOADER_H_ | |
19 | + | |
20 | +#include <vector> | |
21 | + | |
22 | +class domLibrary_geometries; | |
23 | +class domGeometry; | |
24 | +class ColladaGeometry; | |
25 | + | |
26 | +class ColladaLoader { | |
27 | +public: | |
28 | + ColladaLoader(); | |
29 | + ~ColladaLoader(); | |
30 | + | |
31 | + bool init(const char *colladaFile); | |
32 | + bool convertToA3D(const char *a3dFile); | |
33 | + | |
34 | +private: | |
35 | + void clearGeometry(); | |
36 | + std::vector<ColladaGeometry*> mGeometries; | |
37 | + | |
38 | + bool convertAllGeometry(domLibrary_geometries *allGeometry); | |
39 | + bool convertGeometry(domGeometry *geometry); | |
40 | + | |
41 | +}; | |
42 | + | |
43 | +#endif | |
\ No newline at end of file |
@@ -0,0 +1,351 @@ | ||
1 | +/* | |
2 | + * Copyright (C) 2011 The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +#include "ObjLoader.h" | |
18 | +#include <rsFileA3D.h> | |
19 | +#include <sstream> | |
20 | + | |
21 | +ObjLoader::ObjLoader() : | |
22 | + mPositionsStride(3), mNormalsStride(3), mTextureCoordsStride(2) { | |
23 | + | |
24 | +} | |
25 | + | |
26 | +bool isWhitespace(char c) { | |
27 | + const char whiteSpace[] = { ' ', '\n', '\t', '\f', '\r' }; | |
28 | + const uint32_t numWhiteSpaceChars = 5; | |
29 | + for (uint32_t i = 0; i < numWhiteSpaceChars; i ++) { | |
30 | + if (whiteSpace[i] == c) { | |
31 | + return true; | |
32 | + } | |
33 | + } | |
34 | + return false; | |
35 | +} | |
36 | + | |
37 | +void eatWhitespace(std::istream &is) { | |
38 | + while(is.good() && isWhitespace(is.peek())) { | |
39 | + is.get(); | |
40 | + } | |
41 | +} | |
42 | + | |
43 | +bool getToken(std::istream &is, std::string &token) { | |
44 | + eatWhitespace(is); | |
45 | + token.clear(); | |
46 | + char c; | |
47 | + while(is.good() && !isWhitespace(is.peek())) { | |
48 | + c = is.get(); | |
49 | + if (is.good()){ | |
50 | + token += c; | |
51 | + } | |
52 | + } | |
53 | + return token.size() > 0; | |
54 | +} | |
55 | + | |
56 | +void appendDataFromStream(std::vector<float> &dataVec, uint32_t numFloats, std::istream &is) { | |
57 | + std::string token; | |
58 | + for (uint32_t i = 0; i < numFloats; i ++){ | |
59 | + bool valid = getToken(is, token); | |
60 | + if (valid) { | |
61 | + dataVec.push_back((float)atof(token.c_str())); | |
62 | + } else { | |
63 | + fprintf(stderr, "Encountered error reading geometry data"); | |
64 | + dataVec.push_back(0.0f); | |
65 | + } | |
66 | + } | |
67 | +} | |
68 | + | |
69 | +bool checkNegativeIndex(int idx) { | |
70 | + if(idx < 0) { | |
71 | + fprintf(stderr, "Negative indices are not supported. Skipping face\n"); | |
72 | + return false; | |
73 | + } | |
74 | + return true; | |
75 | +} | |
76 | + | |
77 | +void ObjLoader::parseRawFaces(){ | |
78 | + // We need at least a triangle | |
79 | + if (mRawFaces.size() < 3) { | |
80 | + return; | |
81 | + } | |
82 | + | |
83 | + const char slash = '/'; | |
84 | + mParsedFaces.resize(mRawFaces.size()); | |
85 | + for (uint32_t i = 0; i < mRawFaces.size(); i ++) { | |
86 | + size_t firstSeparator = mRawFaces[i].find_first_of(slash); | |
87 | + size_t nextSeparator = mRawFaces[i].find_last_of(slash); | |
88 | + | |
89 | + // Use the string as a temp buffer to parse the index | |
90 | + // Insert 0 instead of the slash to avoid substrings | |
91 | + if (firstSeparator != std::string::npos) { | |
92 | + mRawFaces[i][firstSeparator] = 0; | |
93 | + } | |
94 | + // Simple case, only one index | |
95 | + int32_t vIdx = atoi(mRawFaces[i].c_str()); | |
96 | + // We do not support negative indices | |
97 | + if (!checkNegativeIndex(vIdx)) { | |
98 | + return; | |
99 | + } | |
100 | + // obj indices things beginning 1 | |
101 | + mParsedFaces[i].vertIdx = (uint32_t)vIdx - 1; | |
102 | + | |
103 | + if (nextSeparator != std::string::npos && nextSeparator != firstSeparator) { | |
104 | + mRawFaces[i][nextSeparator] = 0; | |
105 | + uint32_t nIdx = atoi(mRawFaces[i].c_str() + nextSeparator + 1); | |
106 | + if (!checkNegativeIndex(nIdx)) { | |
107 | + return; | |
108 | + } | |
109 | + // obj indexes things beginning 1 | |
110 | + mParsedFaces[i].normIdx = (uint32_t)nIdx - 1; | |
111 | + } | |
112 | + | |
113 | + // second case is where we have vertex and texture indices | |
114 | + if (nextSeparator != std::string::npos && | |
115 | + (nextSeparator > firstSeparator + 1 || nextSeparator == firstSeparator)) { | |
116 | + uint32_t tIdx = atoi(mRawFaces[i].c_str() + firstSeparator + 1); | |
117 | + if (!checkNegativeIndex(tIdx)) { | |
118 | + return; | |
119 | + } | |
120 | + // obj indexes things beginning 1 | |
121 | + mParsedFaces[i].texIdx = (uint32_t)tIdx - 1; | |
122 | + } | |
123 | + } | |
124 | + | |
125 | + // Make sure a face list exists before we go adding to it | |
126 | + if (mMeshes.back().mUnfilteredFaces.size() == 0) { | |
127 | + mMeshes.back().appendUnfilteredFaces(mLastMtl); | |
128 | + } | |
129 | + | |
130 | + // Now we have our parsed face, that we need to triangulate as necessary | |
131 | + // Treat more complex polygons as fans. | |
132 | + // This approach will only work only for convex polygons | |
133 | + // but concave polygons need to be addressed elsewhere anyway | |
134 | + for (uint32_t next = 1; next < mParsedFaces.size() - 1; next ++) { | |
135 | + // push it to our current mesh | |
136 | + mMeshes.back().mUnfilteredFaces.back().push_back(mParsedFaces[0]); | |
137 | + mMeshes.back().mUnfilteredFaces.back().push_back(mParsedFaces[next]); | |
138 | + mMeshes.back().mUnfilteredFaces.back().push_back(mParsedFaces[next + 1]); | |
139 | + } | |
140 | +} | |
141 | + | |
142 | +void ObjLoader::checkNewMeshCreation(std::string &newGroup) { | |
143 | + // start a new mesh if we have some faces | |
144 | + // accumulated on the current mesh. | |
145 | + // It's possible to have multiple group statements | |
146 | + // but we only care to actually start a new mesh | |
147 | + // once we can have something we can draw on the previous one | |
148 | + if (mMeshes.back().mUnfilteredFaces.size()) { | |
149 | + mMeshes.push_back(ObjMesh()); | |
150 | + } | |
151 | + | |
152 | + mMeshes.back().mName = newGroup; | |
153 | + printf("Converting vertex group: %s\n", newGroup.c_str()); | |
154 | +} | |
155 | + | |
156 | +void ObjLoader::handleObjLine(char *line) { | |
157 | + const char* vtxToken = "v"; | |
158 | + const char* normToken = "vn"; | |
159 | + const char* texToken = "vt"; | |
160 | + const char* groupToken = "g"; | |
161 | + const char* mtlToken = "usemtl"; | |
162 | + const char* faceToken = "f"; | |
163 | + | |
164 | + std::istringstream lineStream(line, std::istringstream::in); | |
165 | + | |
166 | + std::string token; | |
167 | + bool valid = getToken(lineStream, token); | |
168 | + if (!valid) { | |
169 | + return; | |
170 | + } | |
171 | + | |
172 | + if (token == vtxToken) { | |
173 | + appendDataFromStream(mObjPositions, 3, lineStream); | |
174 | + } else if (token == normToken) { | |
175 | + appendDataFromStream(mObjNormals, 3, lineStream); | |
176 | + } else if (token == texToken) { | |
177 | + appendDataFromStream(mObjTextureCoords, 2, lineStream); | |
178 | + } else if (token == groupToken) { | |
179 | + valid = getToken(lineStream, token); | |
180 | + checkNewMeshCreation(token); | |
181 | + } else if (token == faceToken) { | |
182 | + mRawFaces.clear(); | |
183 | + while(getToken(lineStream, token)) { | |
184 | + mRawFaces.push_back(token); | |
185 | + } | |
186 | + parseRawFaces(); | |
187 | + } | |
188 | + // Ignore materials for now | |
189 | + else if (token == mtlToken) { | |
190 | + valid = getToken(lineStream, token); | |
191 | + mLastMtl = token; | |
192 | + | |
193 | + mMeshes.back().appendUnfilteredFaces(token); | |
194 | + } | |
195 | +} | |
196 | + | |
197 | +bool ObjLoader::init(const char *fileName) { | |
198 | + | |
199 | + std::ifstream ifs(fileName , std::ifstream::in); | |
200 | + if (!ifs.good()) { | |
201 | + fprintf(stderr, "Failed to read file %s.\n", fileName); | |
202 | + return false; | |
203 | + } | |
204 | + | |
205 | + mMeshes.clear(); | |
206 | + | |
207 | + const uint32_t maxBufferSize = 2048; | |
208 | + char *buffer = new char[maxBufferSize]; | |
209 | + | |
210 | + mMeshes.push_back(ObjMesh()); | |
211 | + | |
212 | + std::string token; | |
213 | + bool isDone = false; | |
214 | + while(!isDone) { | |
215 | + ifs.getline(buffer, maxBufferSize); | |
216 | + if (ifs.good() && ifs.gcount() > 0) { | |
217 | + handleObjLine(buffer); | |
218 | + } else { | |
219 | + isDone = true; | |
220 | + } | |
221 | + } | |
222 | + | |
223 | + ifs.close(); | |
224 | + delete buffer; | |
225 | + | |
226 | + reIndexGeometry(); | |
227 | + | |
228 | + return true; | |
229 | +} | |
230 | + | |
231 | +bool ObjLoader::convertToA3D(const char *a3dFile) { | |
232 | + if (!getNumMeshes()) { | |
233 | + return false; | |
234 | + } | |
235 | + // Now write all this stuff out | |
236 | + Context rsc; | |
237 | + FileA3D file(&rsc); | |
238 | + | |
239 | + for (uint32_t i = 0; i < getNumMeshes(); i ++) { | |
240 | + Mesh *exportedMesh = getMesh(&rsc, i); | |
241 | + file.appendToFile(exportedMesh); | |
242 | + delete exportedMesh; | |
243 | + } | |
244 | + | |
245 | + file.writeFile(a3dFile); | |
246 | + return true; | |
247 | +} | |
248 | + | |
249 | +void ObjLoader::reIndexGeometry() { | |
250 | + // We want to know where each vertex lands | |
251 | + mVertexRemap.resize(mObjPositions.size() / mPositionsStride); | |
252 | + | |
253 | + for (uint32_t m = 0; m < mMeshes.size(); m ++) { | |
254 | + // clear the remap vector of old data | |
255 | + for (uint32_t r = 0; r < mVertexRemap.size(); r ++) { | |
256 | + mVertexRemap[r].clear(); | |
257 | + } | |
258 | + | |
259 | + for (uint32_t i = 0; i < mMeshes[m].mUnfilteredFaces.size(); i ++) { | |
260 | + mMeshes[m].mTriangleLists[i].reserve(mMeshes[m].mUnfilteredFaces[i].size() * 2); | |
261 | + for (uint32_t fI = 0; fI < mMeshes[m].mUnfilteredFaces[i].size(); fI ++) { | |
262 | + uint32_t newIndex = reIndexGeometryPrim(mMeshes[m], mMeshes[m].mUnfilteredFaces[i][fI]); | |
263 | + mMeshes[m].mTriangleLists[i].push_back(newIndex); | |
264 | + } | |
265 | + } | |
266 | + } | |
267 | +} | |
268 | + | |
269 | +uint32_t ObjLoader::reIndexGeometryPrim(ObjMesh &mesh, PrimitiveVtx &prim) { | |
270 | + | |
271 | + std::vector<float> &mPositions = mesh.mChannels[0].mData; | |
272 | + std::vector<float> &mNormals = mesh.mChannels[1].mData; | |
273 | + std::vector<float> &mTextureCoords = mesh.mChannels[2].mData; | |
274 | + | |
275 | + float posX = mObjPositions[prim.vertIdx * mPositionsStride + 0]; | |
276 | + float posY = mObjPositions[prim.vertIdx * mPositionsStride + 1]; | |
277 | + float posZ = mObjPositions[prim.vertIdx * mPositionsStride + 2]; | |
278 | + | |
279 | + float normX = 0.0f; | |
280 | + float normY = 0.0f; | |
281 | + float normZ = 0.0f; | |
282 | + if (prim.normIdx != MAX_INDEX) { | |
283 | + normX = mObjNormals[prim.normIdx * mNormalsStride + 0]; | |
284 | + normY = mObjNormals[prim.normIdx * mNormalsStride + 1]; | |
285 | + normZ = mObjNormals[prim.normIdx * mNormalsStride + 2]; | |
286 | + } | |
287 | + | |
288 | + float texCoordX = 0.0f; | |
289 | + float texCoordY = 0.0f; | |
290 | + if (prim.texIdx != MAX_INDEX) { | |
291 | + texCoordX = mObjTextureCoords[prim.texIdx * mTextureCoordsStride + 0]; | |
292 | + texCoordY = mObjTextureCoords[prim.texIdx * mTextureCoordsStride + 1]; | |
293 | + } | |
294 | + | |
295 | + std::vector<unsigned int> &ithRemapList = mVertexRemap[prim.vertIdx]; | |
296 | + // We may have some potential vertices we can reuse | |
297 | + // loop over all the potential candidates and see if any match our guy | |
298 | + for (unsigned int i = 0; i < ithRemapList.size(); i ++) { | |
299 | + | |
300 | + int ithRemap = ithRemapList[i]; | |
301 | + // compare existing vertex with the new one | |
302 | + if (mPositions[ithRemap * mPositionsStride + 0] != posX || | |
303 | + mPositions[ithRemap * mPositionsStride + 1] != posY || | |
304 | + mPositions[ithRemap * mPositionsStride + 2] != posZ) { | |
305 | + continue; | |
306 | + } | |
307 | + | |
308 | + // Now go over normals | |
309 | + if (prim.normIdx != MAX_INDEX) { | |
310 | + if (mNormals[ithRemap * mNormalsStride + 0] != normX || | |
311 | + mNormals[ithRemap * mNormalsStride + 1] != normY || | |
312 | + mNormals[ithRemap * mNormalsStride + 2] != normZ) { | |
313 | + continue; | |
314 | + } | |
315 | + } | |
316 | + | |
317 | + // And texcoords | |
318 | + if (prim.texIdx != MAX_INDEX) { | |
319 | + if (mTextureCoords[ithRemap * mTextureCoordsStride + 0] != texCoordX || | |
320 | + mTextureCoords[ithRemap * mTextureCoordsStride + 1] != texCoordY) { | |
321 | + continue; | |
322 | + } | |
323 | + } | |
324 | + | |
325 | + // If we got here the new vertex is identical to the one that we already stored | |
326 | + return ithRemap; | |
327 | + } | |
328 | + | |
329 | + // We did not encounter this vertex yet, store it and return its index | |
330 | + mPositions.push_back(posX); | |
331 | + mPositions.push_back(posY); | |
332 | + mPositions.push_back(posZ); | |
333 | + | |
334 | + if (prim.normIdx != MAX_INDEX) { | |
335 | + mNormals.push_back(normX); | |
336 | + mNormals.push_back(normY); | |
337 | + mNormals.push_back(normZ); | |
338 | + } | |
339 | + | |
340 | + if (prim.texIdx != MAX_INDEX) { | |
341 | + mTextureCoords.push_back(texCoordX); | |
342 | + mTextureCoords.push_back(texCoordY); | |
343 | + } | |
344 | + | |
345 | + // We need to remember this mapping. Since we are storing floats, not vec3's, need to | |
346 | + // divide by position size to get the right index | |
347 | + int currentVertexIndex = (mPositions.size()/mPositionsStride) - 1; | |
348 | + ithRemapList.push_back(currentVertexIndex); | |
349 | + | |
350 | + return currentVertexIndex; | |
351 | +} |
@@ -0,0 +1,107 @@ | ||
1 | +/* | |
2 | + * Copyright (C) 2011 The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +#ifndef _OBJ_LOADER_H_ | |
18 | +#define _OBJ_LOADER_H_ | |
19 | + | |
20 | +#include <vector> | |
21 | +#include <string> | |
22 | +#include <iostream> | |
23 | +#include <fstream> | |
24 | + | |
25 | +#include "SimpleMesh.h" | |
26 | +#include <rsContext.h> | |
27 | + | |
28 | +using namespace android; | |
29 | +using namespace android::renderscript; | |
30 | + | |
31 | +#define MAX_INDEX 0xffffffff | |
32 | + | |
33 | +class ObjLoader { | |
34 | +public: | |
35 | + ObjLoader(); | |
36 | + bool init(const char *objFile); | |
37 | + bool convertToA3D(const char *a3dFile); | |
38 | +private: | |
39 | + | |
40 | + Mesh *getMesh(Context *rsc, uint32_t meshIndex) { | |
41 | + return mMeshes[meshIndex].getMesh(rsc); | |
42 | + } | |
43 | + uint32_t getNumMeshes() const { | |
44 | + return mMeshes.size(); | |
45 | + } | |
46 | + | |
47 | + // .obj has a global list of vertex data | |
48 | + std::vector<float> mObjPositions; | |
49 | + std::vector<float> mObjNormals; | |
50 | + std::vector<float> mObjTextureCoords; | |
51 | + | |
52 | + struct PrimitiveVtx { | |
53 | + uint32_t vertIdx; | |
54 | + uint32_t normIdx; | |
55 | + uint32_t texIdx; | |
56 | + | |
57 | + PrimitiveVtx() : vertIdx(MAX_INDEX), | |
58 | + normIdx(MAX_INDEX), | |
59 | + texIdx(MAX_INDEX){ | |
60 | + } | |
61 | + }; | |
62 | + | |
63 | + // Scratch buffer for faces | |
64 | + std::vector<std::string> mRawFaces; | |
65 | + std::vector<PrimitiveVtx> mParsedFaces; | |
66 | + std::string mLastMtl; | |
67 | + | |
68 | + // Groups are used to separate multiple meshes within the same .obj file | |
69 | + class ObjMesh : public SimpleMesh { | |
70 | + public: | |
71 | + | |
72 | + std::vector<std::vector<PrimitiveVtx> > mUnfilteredFaces; | |
73 | + | |
74 | + void appendUnfilteredFaces(std::string name) { | |
75 | + appendFaceList(name); | |
76 | + mUnfilteredFaces.push_back(std::vector<PrimitiveVtx>()); | |
77 | + // Reserve some space for index data | |
78 | + static const uint32_t numReserveIndecies = 128; | |
79 | + mUnfilteredFaces.back().reserve(numReserveIndecies); | |
80 | + } | |
81 | + | |
82 | + ObjMesh() { | |
83 | + appendChannel("position", 3); | |
84 | + appendChannel("normal", 3); | |
85 | + appendChannel("texture0", 2); | |
86 | + } | |
87 | + }; | |
88 | + | |
89 | + std::vector<ObjMesh> mMeshes; | |
90 | + void checkNewMeshCreation(std::string &newGroup); | |
91 | + | |
92 | + void parseRawFaces(); | |
93 | + void handleObjLine(char *line); | |
94 | + | |
95 | + void reIndexGeometry(); | |
96 | + uint32_t reIndexGeometryPrim(ObjMesh &mesh, PrimitiveVtx &prim); | |
97 | + | |
98 | + unsigned int mPositionsStride; | |
99 | + unsigned int mNormalsStride; | |
100 | + unsigned int mTextureCoordsStride; | |
101 | + | |
102 | + // This vector is used to remap a position index into a list | |
103 | + // of all divergent vertices | |
104 | + std::vector<std::vector<unsigned int> > mVertexRemap; | |
105 | +}; | |
106 | + | |
107 | +#endif //_OBJ_LOADER_H_ |
@@ -0,0 +1,154 @@ | ||
1 | +/* | |
2 | + * Copyright (C) 2011 The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +#ifndef _SIMPLE_MESH_H_ | |
18 | +#define _SIMPLE_MESH_H_ | |
19 | + | |
20 | +#include <rsContext.h> | |
21 | +#include <rsMesh.h> | |
22 | +using namespace android; | |
23 | +using namespace android::renderscript; | |
24 | + | |
25 | +class SimpleMesh { | |
26 | +public: | |
27 | + struct Channel { | |
28 | + std::vector<float> mData; | |
29 | + std::string mName; | |
30 | + uint32_t mStride; | |
31 | + }; | |
32 | + | |
33 | + // Vertex channels (position, normal) | |
34 | + // This assumes all the data array are the same size | |
35 | + std::vector<Channel> mChannels; | |
36 | + | |
37 | + // Triangle list index data | |
38 | + std::vector<std::vector<uint32_t> > mTriangleLists; | |
39 | + // Names of all the triangle lists | |
40 | + std::vector<std::string> mTriangleListNames; | |
41 | + // Name of the entire object | |
42 | + std::string mName; | |
43 | + | |
44 | + // Adds another index set to the mesh | |
45 | + void appendFaceList(std::string name) { | |
46 | + mTriangleListNames.push_back(name); | |
47 | + mTriangleLists.push_back(std::vector<uint32_t>()); | |
48 | + } | |
49 | + | |
50 | + // Adds another data channel (position, normal, etc.) | |
51 | + void appendChannel(std::string name, uint32_t stride) { | |
52 | + mChannels.push_back(Channel()); | |
53 | + static const uint32_t reserveVtx = 128; | |
54 | + mChannels.back().mData.reserve(reserveVtx*stride); | |
55 | + mChannels.back().mName = name; | |
56 | + mChannels.back().mStride = stride; | |
57 | + } | |
58 | + | |
59 | + SimpleMesh() { | |
60 | + // reserve some data in the vectors | |
61 | + // simply letting it grow by itself tends to waste a lot of time on | |
62 | + // rallocations / copies when dealing with geometry data | |
63 | + static const uint32_t reserveFaces = 8; | |
64 | + static const uint32_t reserveChannels = 8; | |
65 | + mTriangleLists.reserve(reserveFaces); | |
66 | + mTriangleListNames.reserve(reserveFaces); | |
67 | + mChannels.reserve(reserveChannels); | |
68 | + } | |
69 | + | |
70 | + // Generates a renderscript mesh that could be used for a3d serialization | |
71 | + Mesh *getMesh(Context *rsc) { | |
72 | + if (mChannels.size() == 0) { | |
73 | + return NULL; | |
74 | + } | |
75 | + | |
76 | + // Generate the element that describes our channel layout | |
77 | + rsc->mStateElement.elementBuilderBegin(); | |
78 | + for (uint32_t c = 0; c < mChannels.size(); c ++) { | |
79 | + // Skip empty channels | |
80 | + if (mChannels[c].mData.size() == 0) { | |
81 | + continue; | |
82 | + } | |
83 | + const Element *subElem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, mChannels[c].mStride); | |
84 | + rsc->mStateElement.elementBuilderAdd(subElem, mChannels[c].mName.c_str(), 1); | |
85 | + } | |
86 | + const Element *vertexDataElem = rsc->mStateElement.elementBuilderCreate(rsc); | |
87 | + | |
88 | + uint32_t numVerts = mChannels[0].mData.size()/mChannels[0].mStride; | |
89 | + Type *vertexDataType = Type::getType(rsc, vertexDataElem, numVerts, 0, 0, false, false); | |
90 | + vertexDataType->compute(); | |
91 | + | |
92 | + Allocation *vertexAlloc = new Allocation(rsc, vertexDataType, RS_ALLOCATION_USAGE_SCRIPT); | |
93 | + | |
94 | + uint32_t vertexSize = vertexDataElem->getSizeBytes()/sizeof(float); | |
95 | + // Fill this allocation with some data | |
96 | + float *dataPtr = (float*)vertexAlloc->getPtr(); | |
97 | + for (uint32_t i = 0; i < numVerts; i ++) { | |
98 | + // Find the pointer to the current vertex's data | |
99 | + uint32_t vertexPos = i*vertexSize; | |
100 | + float *vertexPtr = dataPtr + vertexPos; | |
101 | + | |
102 | + for (uint32_t c = 0; c < mChannels.size(); c ++) { | |
103 | + // Skip empty channels | |
104 | + if (mChannels[c].mData.size() == 0) { | |
105 | + continue; | |
106 | + } | |
107 | + for (uint32_t cStride = 0; cStride < mChannels[c].mStride; cStride ++) { | |
108 | + *(vertexPtr++) = mChannels[c].mData[i * mChannels[c].mStride + cStride]; | |
109 | + } | |
110 | + } | |
111 | + } | |
112 | + | |
113 | + // Now lets write index data | |
114 | + const Element *indexElem = Element::create(rsc, RS_TYPE_UNSIGNED_16, RS_KIND_USER, false, 1); | |
115 | + | |
116 | + Mesh *mesh = new Mesh(rsc); | |
117 | + mesh->setName(mName.c_str()); | |
118 | + mesh->mVertexBufferCount = 1; | |
119 | + mesh->mVertexBuffers = new ObjectBaseRef<Allocation>[1]; | |
120 | + mesh->mVertexBuffers[0].set(vertexAlloc); | |
121 | + | |
122 | + mesh->mPrimitivesCount = mTriangleLists.size(); | |
123 | + mesh->mPrimitives = new Mesh::Primitive_t *[mesh->mPrimitivesCount]; | |
124 | + | |
125 | + // load all primitives | |
126 | + for (uint32_t pCount = 0; pCount < mesh->mPrimitivesCount; pCount ++) { | |
127 | + Mesh::Primitive_t *prim = new Mesh::Primitive_t; | |
128 | + mesh->mPrimitives[pCount] = prim; | |
129 | + | |
130 | + uint32_t numIndicies = mTriangleLists[pCount].size(); | |
131 | + Type *indexType = Type::getType(rsc, indexElem, numIndicies, 0, 0, false, false ); | |
132 | + | |
133 | + indexType->compute(); | |
134 | + | |
135 | + Allocation *indexAlloc = new Allocation(rsc, indexType, RS_ALLOCATION_USAGE_SCRIPT); | |
136 | + uint16_t *indexPtr = (uint16_t*)indexAlloc->getPtr(); | |
137 | + const std::vector<uint32_t> &indexList = mTriangleLists[pCount]; | |
138 | + uint32_t numTries = numIndicies / 3; | |
139 | + | |
140 | + for (uint32_t i = 0; i < numTries; i ++) { | |
141 | + indexPtr[i * 3 + 0] = (uint16_t)indexList[i * 3 + 0]; | |
142 | + indexPtr[i * 3 + 1] = (uint16_t)indexList[i * 3 + 1]; | |
143 | + indexPtr[i * 3 + 2] = (uint16_t)indexList[i * 3 + 2]; | |
144 | + } | |
145 | + indexAlloc->setName(mTriangleListNames[pCount].c_str()); | |
146 | + prim->mIndexBuffer.set(indexAlloc); | |
147 | + prim->mPrimitive = RS_PRIMITIVE_TRIANGLE; | |
148 | + } | |
149 | + | |
150 | + return mesh; | |
151 | + } | |
152 | +}; | |
153 | + | |
154 | +#endif |
@@ -0,0 +1,70 @@ | ||
1 | +/* | |
2 | + * Copyright (C) 2011 The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +#include <iostream> | |
18 | +#include <vector> | |
19 | + | |
20 | +#include "ColladaLoader.h" | |
21 | +#include "ObjLoader.h" | |
22 | + | |
23 | +int main (int argc, char * const argv[]) { | |
24 | + const char *objExt = ".obj"; | |
25 | + const char *daeExt = ".dae"; | |
26 | + | |
27 | + if(argc != 3) { | |
28 | + printf("-----------------------------------------------------------------\n"); | |
29 | + printf("Usage:\n"); | |
30 | + printf("a3dconvert input_file a3d_output_file\n"); | |
31 | + printf("Currently .obj and .dae (collada) input files are accepted\n"); | |
32 | + printf("-----------------------------------------------------------------\n"); | |
33 | + return 1; | |
34 | + } | |
35 | + | |
36 | + bool isSuccessful = false; | |
37 | + | |
38 | + std::string filename = argv[1]; | |
39 | + size_t dotPos = filename.find_last_of('.'); | |
40 | + if (dotPos == std::string::npos) { | |
41 | + printf("Invalid input. Currently .obj and .dae (collada) input files are accepted\n"); | |
42 | + return 1; | |
43 | + } | |
44 | + | |
45 | + std::string ext = filename.substr(dotPos); | |
46 | + if (ext == daeExt) { | |
47 | + ColladaLoader converter; | |
48 | + isSuccessful = converter.init(argv[1]); | |
49 | + if (isSuccessful) { | |
50 | + isSuccessful = converter.convertToA3D(argv[2]); | |
51 | + } | |
52 | + } else if (ext == objExt) { | |
53 | + ObjLoader objConv; | |
54 | + isSuccessful = objConv.init(argv[1]); | |
55 | + if (isSuccessful) { | |
56 | + isSuccessful = objConv.convertToA3D(argv[2]); | |
57 | + } | |
58 | + } else { | |
59 | + printf("Invalid input. Currently .obj and .dae (collada) input files are accepted\n"); | |
60 | + return 1; | |
61 | + } | |
62 | + | |
63 | + if(isSuccessful) { | |
64 | + printf("---All done---\n"); | |
65 | + } else { | |
66 | + printf("---Encountered errors, conversion failed---\n"); | |
67 | + } | |
68 | + | |
69 | + return isSuccessful ? 0 : 1; | |
70 | +} |
@@ -0,0 +1,25 @@ | ||
1 | +Parts of this code come from colladaDom with the license below. The rest is AOSP. | |
2 | + | |
3 | + | |
4 | + | |
5 | +The MIT License | |
6 | + | |
7 | +Copyright 2006 Sony Computer Entertainment Inc. | |
8 | + | |
9 | +Permission is hereby granted, free of charge, to any person obtaining a copy | |
10 | +of this software and associated documentation files (the "Software"), to deal | |
11 | +in the Software without restriction, including without limitation the rights | |
12 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
13 | +copies of the Software, and to permit persons to whom the Software is | |
14 | +furnished to do so, subject to the following conditions: | |
15 | + | |
16 | +The above copyright notice and this permission notice shall be included in | |
17 | +all copies or substantial portions of the Software. | |
18 | + | |
19 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
20 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
21 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
22 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
23 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
24 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
25 | +THE SOFTWARE. |