Merge "renderscript geometry converter. Initial implementation of the converted from .obj and .dae file to the renderscript file format."

This commit is contained in:
Alex Sakhartchouk
2011-02-23 09:41:15 -08:00
committed by Android (Google) Code Review
12 changed files with 1544 additions and 0 deletions

View File

@@ -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)

View File

@@ -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 <triangles> inside the mesh that has the same material as the <polylist>
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 <triangles> the same <_dae> and <parameters> as the old <polylist>
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 <p> 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 <triangles> inside the mesh that has the same material as the <polygons>
domTriangles *thisTriangles = (domTriangles *)thisMesh->createAndPlace("triangles");
thisTriangles->setCount( 0 );
thisTriangles->setMaterial(thisPolygons->getMaterial());
domP* p_triangles = (domP*)thisTriangles->createAndPlace("p");
// Give the new <triangles> the same <_dae> and <parameters> as the old <polygons>
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 <p> 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 <polygons> 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 <polygons> 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;
}

View File

@@ -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 <dae.h>
#include <dom/domConstants.h>
#include <dom/domCOLLADA.h>
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

View File

@@ -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 <iostream>
#include <sstream>
ColladaGeometry::ColladaGeometry() :
mPositionFloats(NULL), mPositionOffset(-1),
mNormalFloats(NULL), mNormalOffset(-1),
mTangentFloats(NULL), mTangentOffset(-1),
mBinormalFloats(NULL), mBinormalOffset(-1),
mTexture1Floats(NULL), mTexture1Offset(-1),
mMultiIndexOffset(-1),
mPositionsStride(3), mNormalsStride(3),
mTextureCoordsStride(2), mTangentssStride(3), mBinormalsStride(3) {
mConvertedMesh.appendChannel("position", mPositionsStride);
mConvertedMesh.appendChannel("normal", mNormalsStride);
mConvertedMesh.appendChannel("texture0", mTextureCoordsStride);
mConvertedMesh.appendChannel("binormal", mBinormalsStride);
mConvertedMesh.appendChannel("tangent", mTangentssStride);
mPositions = &mConvertedMesh.mChannels[0].mData;
mNormals = &mConvertedMesh.mChannels[1].mData;
mTextureCoords = &mConvertedMesh.mChannels[2].mData;
mBinormals = &mConvertedMesh.mChannels[3].mData;
mTangents = &mConvertedMesh.mChannels[4].mData;
}
bool ColladaGeometry::init(domGeometryRef geometry) {
bool convertSuceeded = true;
const char* geoName = geometry->getName();
if (geoName == NULL) {
geoName = geometry->getId();
}
mConvertedMesh.mName = geoName;
mMesh = geometry->getMesh();
// Iterate over all the index groups and build up a simple resolved tri list and vertex array
const domTriangles_Array &allTriLists = mMesh->getTriangles_array();
int numTriLists = allTriLists.getCount();
mConvertedMesh.mTriangleLists.reserve(numTriLists);
mConvertedMesh.mTriangleListNames.reserve(numTriLists);
for (int i = 0; i < numTriLists; i ++) {
addTriangles(allTriLists[i]);
}
return convertSuceeded;
}
void ColladaGeometry::addTriangles(domTriangles * colladaTriangles) {
int numTriangles = colladaTriangles->getCount();
int triListIndex = mConvertedMesh.mTriangleLists.size();
mConvertedMesh.mTriangleLists.resize(triListIndex + 1);
std::string materialName = colladaTriangles->getMaterial();
if (materialName.size() == 0) {
char buffer[128];
sprintf(buffer, "index%d", triListIndex);
materialName = buffer;
}
mConvertedMesh.mTriangleListNames.push_back(materialName);
// It's a good idea to tell stl how much memory we intend to use
// to limit the number of reallocations
mPositions->reserve(numTriangles * 3);
mNormals->reserve(numTriangles * 3);
mTangents->reserve(numTriangles * 3);
mBinormals->reserve(numTriangles * 3);
mTextureCoords->reserve(numTriangles * 3);
// Stores the pointers to the image data and where in the tri list that data comes from
cacheOffsetsAndDataPointers(colladaTriangles);
// Collapse the multiindex that collada uses
const domListOfUInts &colladaIndexList = colladaTriangles->getP()->getValue();
std::vector<uint32_t> &a3dIndexList = mConvertedMesh.mTriangleLists[triListIndex];
a3dIndexList.resize(numTriangles * 3);
for (int i = 0; i < numTriangles * 3; i ++) {
a3dIndexList[i] = remapIndexAndStoreData(colladaIndexList, i);
}
}
void ColladaGeometry::cacheOffsetsAndDataPointers(domTriangles * colladaTriangles) {
// Define the names of known vertex channels
const char *positionSemantic = "POSITION";
const char *vertexSemantic = "VERTEX";
const char *normalSemantic = "NORMAL";
const char *tangentSemantic = "TANGENT";
const char *binormalSemantic = "BINORMAL";
const char *texture1Semantic = "TEXCOORD";
const domInputLocalOffset_Array &inputs = colladaTriangles->getInput_array();
mMultiIndexOffset = inputs.getCount();
// inputs with offsets
// There are two places collada can put links to our data
// 1 - in the VERTEX, which is its way of saying follow a link to the vertex structure
// then every geometry array you find there is the same size as the position array
// 2 - a direct link to the channel from the primitive list. This tells us that there are
// potentially more or less floats in those channels because there is some vertex re-use
// or divergence in that data channel. For example, highly segmented uv set would produce a
// larger array because for every physical vertex position thre might be 2 or more uv coords
for (uint32_t i = 0; i < inputs.getCount(); i ++) {
int currentOffset = inputs[i]->getOffset();
const char *currentSemantic = inputs[i]->getSemantic();
domSource * source = (domSource*) (domElement*) inputs[i]->getSource().getElement();
if (strcmp(vertexSemantic, currentSemantic) == 0) {
mPositionOffset = currentOffset;
}
else if (strcmp(normalSemantic, currentSemantic) == 0) {
mNormalOffset = currentOffset;
mNormalFloats = &source->getFloat_array()->getValue();
}
else if (strcmp(tangentSemantic, currentSemantic) == 0) {
mTangentOffset = currentOffset;
mTangentFloats = &source->getFloat_array()->getValue();
}
else if (strcmp(binormalSemantic, currentSemantic) == 0) {
mBinormalOffset = currentOffset;
mBinormalFloats = &source->getFloat_array()->getValue();
}
else if (strcmp(texture1Semantic, currentSemantic) == 0) {
mTexture1Offset = currentOffset;
mTexture1Floats = & source->getFloat_array()->getValue();
}
}
// There are multiple ways of getting to data, so follow them all
domVertices * vertices = mMesh->getVertices();
const domInputLocal_Array &verticesInputs = vertices->getInput_array();
for (uint32_t i = 0; i < verticesInputs.getCount(); i ++) {
const char *currentSemantic = verticesInputs[i]->getSemantic();
domSource * source = (domSource*) (domElement*) verticesInputs[i]->getSource().getElement();
if (strcmp(positionSemantic, currentSemantic) == 0) {
mPositionFloats = & source->getFloat_array()->getValue();
// TODO: Querry this from the accessor in the future because
// I supopose it's possible to have 4 floats if we hide something in w
int numberOfFloatsPerPoint = 3;
// We want to cllapse duplicate vertices, otherwise we could just unroll the tri list
mVertexRemap.resize(source->getFloat_array()->getCount()/numberOfFloatsPerPoint);
}
else if (strcmp(normalSemantic, currentSemantic) == 0) {
mNormalFloats = & source->getFloat_array()->getValue();
mNormalOffset = mPositionOffset;
}
else if (strcmp(tangentSemantic, currentSemantic) == 0) {
mTangentFloats = & source->getFloat_array()->getValue();
mTangentOffset = mPositionOffset;
}
else if (strcmp(binormalSemantic, currentSemantic) == 0) {
mBinormalFloats = & source->getFloat_array()->getValue();
mBinormalOffset = mPositionOffset;
}
else if (strcmp(texture1Semantic, currentSemantic) == 0) {
mTexture1Floats = & source->getFloat_array()->getValue();
mTexture1Offset = mPositionOffset;
}
}
}
int ColladaGeometry::remapIndexAndStoreData(const domListOfUInts &colladaIndexList, int indexToRemap) {
domUint positionIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mPositionOffset];
float posX = (*mPositionFloats)[positionIndex * mPositionsStride + 0];
float posY = (*mPositionFloats)[positionIndex * mPositionsStride + 1];
float posZ = (*mPositionFloats)[positionIndex * mPositionsStride + 2];
float normX = 0;
float normY = 0;
float normZ = 0;
if (mNormalOffset != -1) {
domUint normalIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mNormalOffset];
normX = (*mNormalFloats)[normalIndex * mNormalsStride + 0];
normY = (*mNormalFloats)[normalIndex * mNormalsStride + 1];
normZ = (*mNormalFloats)[normalIndex * mNormalsStride + 2];
}
float tanX = 0;
float tanY = 0;
float tanZ = 0;
if (mTangentOffset != -1) {
domUint tangentIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mTangentOffset];
tanX = (*mTangentFloats)[tangentIndex * mTangentssStride + 0];
tanY = (*mTangentFloats)[tangentIndex * mTangentssStride + 1];
tanZ = (*mTangentFloats)[tangentIndex * mTangentssStride + 2];
}
float binormX = 0;
float binormY = 0;
float binormZ = 0;
if (mBinormalOffset != -1) {
domUint binormalIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mNormalOffset];
binormX = (*mBinormalFloats)[binormalIndex * mBinormalsStride + 0];
binormY = (*mBinormalFloats)[binormalIndex * mBinormalsStride + 1];
binormZ = (*mBinormalFloats)[binormalIndex * mBinormalsStride + 2];
}
float texCoordX = 0;
float texCoordY = 0;
if (mTexture1Offset != -1) {
domUint texCoordIndex = colladaIndexList[indexToRemap*mMultiIndexOffset + mTexture1Offset];
texCoordX = (*mTexture1Floats)[texCoordIndex * mTextureCoordsStride + 0];
texCoordY = (*mTexture1Floats)[texCoordIndex * mTextureCoordsStride + 1];
}
std::vector<uint32_t> &ithRemapList = mVertexRemap[positionIndex];
// We may have some potential vertices we can reuse
// loop over all the potential candidates and see if any match our guy
for (uint32_t i = 0; i < ithRemapList.size(); i ++) {
int ithRemap = ithRemapList[i];
// compare existing vertex with the new one
if ((*mPositions)[ithRemap * mPositionsStride + 0] != posX ||
(*mPositions)[ithRemap * mPositionsStride + 1] != posY ||
(*mPositions)[ithRemap * mPositionsStride + 2] != posZ) {
continue;
}
// Now go over normals
if (mNormalOffset != -1) {
if ((*mNormals)[ithRemap * mNormalsStride + 0] != normX ||
(*mNormals)[ithRemap * mNormalsStride + 1] != normY ||
(*mNormals)[ithRemap * mNormalsStride + 2] != normZ) {
continue;
}
}
// Now go over tangents
if (mTangentOffset != -1) {
if ((*mTangents)[ithRemap * mTangentssStride + 0] != tanX ||
(*mTangents)[ithRemap * mTangentssStride + 1] != tanY ||
(*mTangents)[ithRemap * mTangentssStride + 2] != tanZ) {
continue;
}
}
// Now go over binormals
if (mBinormalOffset != -1) {
if ((*mBinormals)[ithRemap * mBinormalsStride + 0] != binormX ||
(*mBinormals)[ithRemap * mBinormalsStride + 1] != binormY ||
(*mBinormals)[ithRemap * mBinormalsStride + 2] != binormZ) {
continue;
}
}
// And texcoords
if (mTexture1Offset != -1) {
if ((*mTextureCoords)[ithRemap * mTextureCoordsStride + 0] != texCoordX ||
(*mTextureCoords)[ithRemap * mTextureCoordsStride + 1] != texCoordY) {
continue;
}
}
// If we got here the new vertex is identical to the one that we already stored
return ithRemap;
}
// We did not encounter this vertex yet, store it and return its index
mPositions->push_back(posX);
mPositions->push_back(posY);
mPositions->push_back(posZ);
if (mNormalOffset != -1) {
mNormals->push_back(normX);
mNormals->push_back(normY);
mNormals->push_back(normZ);
}
if (mTangentOffset != -1) {
mTangents->push_back(tanX);
mTangents->push_back(tanY);
mTangents->push_back(tanZ);
}
if (mBinormalOffset != -1) {
mBinormals->push_back(binormX);
mBinormals->push_back(binormY);
mBinormals->push_back(binormZ);
}
if (mTexture1Offset != -1) {
mTextureCoords->push_back(texCoordX);
mTextureCoords->push_back(texCoordY);
}
// We need to remember this mapping. Since we are storing floats, not vec3's, need to
// divide by position size to get the right index
int currentVertexIndex = (mPositions->size()/mPositionsStride) - 1;
ithRemapList.push_back(currentVertexIndex);
return currentVertexIndex;
}

View File

@@ -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 <dae.h>
#include <dom/domCOLLADA.h>
#include <vector>
#include <string>
#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<float> *mPositions;
unsigned int mPositionsStride;
std::vector<float> *mNormals;
unsigned int mNormalsStride;
std::vector<float> *mTextureCoords;
unsigned int mTextureCoordsStride;
std::vector<float> *mTangents;
unsigned int mTangentssStride;
std::vector<float> *mBinormals;
unsigned int mBinormalsStride;
SimpleMesh mConvertedMesh;
// This vector is used to remap a position index into a list of all divergent vertices
std::vector<std::vector<unsigned int> > mVertexRemap;
void addTriangles(domTriangles * colladaTriangles);
void cacheOffsetsAndDataPointers(domTriangles * colladaTriangles);
int remapIndexAndStoreData(const domListOfUInts &colladaIndexList, int indexToRemap);
};
#endif //COLLADA_TO_A3D_GEOMETRY

View File

@@ -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 <dae.h>
#include <dom/domCOLLADA.h>
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<domLibrary_geometries>(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;
}

View File

@@ -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 <vector>
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<ColladaGeometry*> mGeometries;
bool convertAllGeometry(domLibrary_geometries *allGeometry);
bool convertGeometry(domGeometry *geometry);
};
#endif

View File

@@ -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 <rsFileA3D.h>
#include <sstream>
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<float> &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<float> &mPositions = mesh.mChannels[0].mData;
std::vector<float> &mNormals = mesh.mChannels[1].mData;
std::vector<float> &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<unsigned int> &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;
}

View File

@@ -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 <vector>
#include <string>
#include <iostream>
#include <fstream>
#include "SimpleMesh.h"
#include <rsContext.h>
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<float> mObjPositions;
std::vector<float> mObjNormals;
std::vector<float> 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<std::string> mRawFaces;
std::vector<PrimitiveVtx> mParsedFaces;
std::string mLastMtl;
// Groups are used to separate multiple meshes within the same .obj file
class ObjMesh : public SimpleMesh {
public:
std::vector<std::vector<PrimitiveVtx> > mUnfilteredFaces;
void appendUnfilteredFaces(std::string name) {
appendFaceList(name);
mUnfilteredFaces.push_back(std::vector<PrimitiveVtx>());
// 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<ObjMesh> 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<std::vector<unsigned int> > mVertexRemap;
};
#endif //_OBJ_LOADER_H_

View File

@@ -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 <rsContext.h>
#include <rsMesh.h>
using namespace android;
using namespace android::renderscript;
class SimpleMesh {
public:
struct Channel {
std::vector<float> mData;
std::string mName;
uint32_t mStride;
};
// Vertex channels (position, normal)
// This assumes all the data array are the same size
std::vector<Channel> mChannels;
// Triangle list index data
std::vector<std::vector<uint32_t> > mTriangleLists;
// Names of all the triangle lists
std::vector<std::string> 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<uint32_t>());
}
// 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<Allocation>[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<uint32_t> &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

View File

@@ -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 <iostream>
#include <vector>
#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;
}

25
tools/a3dconvert/license.txt Executable file
View File

@@ -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.