1156 lines
44 KiB
C++
1156 lines
44 KiB
C++
/*
|
|
This file is part of Mitsuba, a physically based rendering system.
|
|
|
|
Copyright (c) 2007-2010 by Wenzel Jakob and others.
|
|
|
|
Mitsuba is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License Version 3
|
|
as published by the Free Software Foundation.
|
|
|
|
Mitsuba is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#define BOOST_FILESYSTEM_NO_LIB
|
|
#define BOOST_SYSTEM_NO_LIB
|
|
|
|
#include <mitsuba/mitsuba.h>
|
|
#include <mitsuba/render/trimesh.h>
|
|
#include <mitsuba/core/fresolver.h>
|
|
#include <mitsuba/core/fstream.h>
|
|
#include <dae.h>
|
|
#include <dom/domCOLLADA.h>
|
|
#include <dom/domProfile_COMMON.h>
|
|
#include <boost/filesystem.hpp>
|
|
#include <fstream>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <errno.h>
|
|
|
|
#if defined(__OSX__)
|
|
#include <OpenGL/glu.h>
|
|
#else
|
|
#include <GL/glu.h>
|
|
#endif
|
|
|
|
#include "converter.h"
|
|
|
|
typedef std::map<std::string, std::string> StringMap;
|
|
|
|
enum ESourceType {
|
|
EPosition = 0,
|
|
ENormal = 1,
|
|
EUV = 2,
|
|
EVertexColors = 3,
|
|
ELast
|
|
};
|
|
|
|
struct Vec4 {
|
|
Float x, y, z, w;
|
|
|
|
inline Vec4(Float x=0, Float y=0, Float z=0, Float w=0)
|
|
: x(x), y(y), z(z), w(w) {
|
|
}
|
|
|
|
inline Float operator[](int i) const {
|
|
return (&x)[i];
|
|
}
|
|
|
|
inline Float &operator[](int i) {
|
|
return (&x)[i];
|
|
}
|
|
|
|
inline Point toPoint() const {
|
|
return Point(x, y, z);
|
|
}
|
|
|
|
inline Normal toNormal() const {
|
|
return Normal(x, y, z);
|
|
}
|
|
|
|
inline Point2 toPoint2() const {
|
|
return Point2(x, y);
|
|
}
|
|
};
|
|
|
|
struct VertexData {
|
|
size_t nSources;
|
|
bool hasNormals;
|
|
bool hasUVs;
|
|
GLdouble *glPos;
|
|
std::vector<Vec4 *> data;
|
|
std::vector<int> typeToOffset;
|
|
std::vector<int> typeToOffsetInStream;
|
|
|
|
VertexData() : glPos(NULL) {
|
|
}
|
|
|
|
virtual ~VertexData() {
|
|
for (size_t i=0; i<data.size(); ++i) {
|
|
if (data[i])
|
|
delete[] data[i];
|
|
}
|
|
if (glPos)
|
|
delete[] glPos;
|
|
}
|
|
};
|
|
|
|
/* This code is not thread-safe for now */
|
|
GLUtesselator *tess = NULL;
|
|
std::vector<domUint> tess_data;
|
|
size_t tess_nSources;
|
|
|
|
VertexData *fetchVertexData(Transform transform, std::ostream &os,
|
|
const domInputLocal_Array &vertInputs,
|
|
const domInputLocalOffset_Array &inputs) {
|
|
VertexData *result = new VertexData();
|
|
|
|
result->hasNormals = false;
|
|
result->hasUVs = false;
|
|
result->nSources = inputs.getCount();
|
|
result->data.resize(result->nSources);
|
|
for (size_t i=0; i<result->nSources; ++i)
|
|
result->data[i] = NULL;
|
|
result->typeToOffset.resize(ELast);
|
|
result->typeToOffsetInStream.resize(ELast);
|
|
for (int i=0; i<ELast; ++i)
|
|
result->typeToOffset[i] = result->typeToOffsetInStream[i] = -1;
|
|
int vertInputIndex = 0;
|
|
|
|
for (size_t i=0; i<inputs.getCount(); ++i) {
|
|
int offsetInStream = (int) inputs[i]->getOffset(),
|
|
offset = offsetInStream;
|
|
daeURI &sourceRef = inputs[i]->getSource();
|
|
sourceRef.resolveElement();
|
|
domSource *source = daeSafeCast<domSource>(sourceRef.getElement());
|
|
std::string semantic = inputs[i]->getSemantic();
|
|
|
|
if (semantic == "VERTEX") {
|
|
sourceRef = vertInputs[vertInputIndex]->getSource();
|
|
sourceRef.resolveElement();
|
|
source = daeSafeCast<domSource>(sourceRef.getElement());
|
|
semantic = vertInputs[vertInputIndex]->getSemantic();
|
|
|
|
if (vertInputIndex > 0) {
|
|
offset = result->data.size();
|
|
result->data.push_back(NULL);
|
|
}
|
|
|
|
if (++vertInputIndex < (int) vertInputs.getCount())
|
|
--i;
|
|
}
|
|
|
|
domListOfFloats &floatArray = source->getFloat_array()->getValue();
|
|
domSource::domTechnique_common *techniqueCommon = source->getTechnique_common();
|
|
if (!techniqueCommon)
|
|
SLog(EError, "Data source does not have a <technique_common> tag!");
|
|
domAccessor *accessor = techniqueCommon->getAccessor();
|
|
if (!accessor)
|
|
SLog(EError, "Data source does not have a <accessor> tag!");
|
|
int nParams = (int) accessor->getParam_array().getCount(),
|
|
stride = (int) accessor->getStride(),
|
|
size = (int) accessor->getCount();
|
|
SAssert(nParams <= 4);
|
|
|
|
Vec4 *target = new Vec4[size];
|
|
for (int j=0; j<size; ++j)
|
|
for (int k=0; k<nParams; ++k)
|
|
target[j][k] = (Float) floatArray[j*stride+k];
|
|
|
|
result->data[offset] = target;
|
|
|
|
if (semantic == "POSITION") {
|
|
SAssert(accessor->getStride() == 3);
|
|
SAssert(result->typeToOffset[EPosition] == -1);
|
|
result->typeToOffset[EPosition] = offset;
|
|
result->typeToOffsetInStream[EPosition] = offsetInStream;
|
|
result->glPos = new GLdouble[3*size];
|
|
for (int k=0; k<3*size; ++k)
|
|
result->glPos[k] = floatArray[k];
|
|
} else if (semantic == "NORMAL") {
|
|
SAssert(accessor->getStride() == 3);
|
|
SAssert(result->typeToOffset[ENormal] == -1);
|
|
result->hasNormals = true;
|
|
result->typeToOffset[ENormal] = offset;
|
|
result->typeToOffsetInStream[ENormal] = offsetInStream;
|
|
} else if (semantic == "TEXCOORD") {
|
|
SAssert(accessor->getStride() == 2 || accessor->getStride() == 3);
|
|
if (result->typeToOffset[EUV] == -1) {
|
|
result->hasUVs = true;
|
|
result->typeToOffset[EUV] = offset;
|
|
result->typeToOffsetInStream[EUV] = offsetInStream;
|
|
} else {
|
|
SLog(EWarn, "Found multiple sets of texture coordinates - ignoring!");
|
|
}
|
|
} else if (semantic == "COLOR") {
|
|
SLog(EWarn, "Found per-vertex colors - ignoring. Please bake into a texture "
|
|
"(Lighting/shading -> Batch Bake in Maya)");
|
|
result->typeToOffset[EVertexColors] = offset;
|
|
result->typeToOffsetInStream[EVertexColors] = offsetInStream;
|
|
} else {
|
|
SLog(EError, "Encountered an unknown source semantic: %s", semantic.c_str());
|
|
}
|
|
}
|
|
SAssert(result->typeToOffset[EPosition] != -1);
|
|
|
|
return result;
|
|
}
|
|
|
|
/// For using vertices as keys in an associative structure
|
|
struct vertex_key_order : public
|
|
std::binary_function<Vertex, Vertex, bool> {
|
|
static int compare(const Vertex &v1, const Vertex &v2) {
|
|
if (v1.v.x < v2.v.x) return -1;
|
|
else if (v1.v.x > v2.v.x) return 1;
|
|
if (v1.v.y < v2.v.y) return -1;
|
|
else if (v1.v.y > v2.v.y) return 1;
|
|
if (v1.v.z < v2.v.z) return -1;
|
|
else if (v1.v.z > v2.v.z) return 1;
|
|
if (v1.n.x < v2.n.x) return -1;
|
|
else if (v1.n.x > v2.n.x) return 1;
|
|
if (v1.n.y < v2.n.y) return -1;
|
|
else if (v1.n.y > v2.n.y) return 1;
|
|
if (v1.n.z < v2.n.z) return -1;
|
|
else if (v1.n.z > v2.n.z) return 1;
|
|
if (v1.uv.x < v2.uv.x) return -1;
|
|
else if (v1.uv.x > v2.uv.x) return 1;
|
|
if (v1.uv.y < v2.uv.y) return -1;
|
|
else if (v1.uv.y > v2.uv.y) return 1;
|
|
return 0;
|
|
}
|
|
|
|
bool operator()(const Vertex &v1, const Vertex &v2) const {
|
|
return compare(v1, v2) < 0;
|
|
}
|
|
};
|
|
|
|
struct SimpleTriangle {
|
|
Point p0, p1, p2;
|
|
|
|
inline SimpleTriangle() { }
|
|
inline SimpleTriangle(const Point &p0, const Point &p1, const Point &p2)
|
|
: p0(p0), p1(p1), p2(p2) { }
|
|
};
|
|
|
|
struct triangle_key_order : public std::binary_function<SimpleTriangle, SimpleTriangle, bool> {
|
|
static int compare(const Point &v1, const Point &v2) {
|
|
if (v1.x < v2.x) return -1;
|
|
else if (v1.x > v2.x) return 1;
|
|
if (v1.y < v2.y) return -1;
|
|
else if (v1.y > v2.y) return 1;
|
|
if (v1.z < v2.z) return -1;
|
|
else if (v1.z > v2.z) return 1;
|
|
return 0;
|
|
}
|
|
bool operator()(const SimpleTriangle &t1, const SimpleTriangle &t2) const {
|
|
int result;
|
|
result = compare(t1.p0, t2.p0);
|
|
if (result == -1) return true;
|
|
if (result == 1) return false;
|
|
result = compare(t1.p1, t2.p1);
|
|
if (result == -1) return true;
|
|
if (result == 1) return false;
|
|
result = compare(t1.p2, t2.p2);
|
|
if (result == -1) return true;
|
|
if (result == 1) return false;
|
|
return false;
|
|
}
|
|
};
|
|
|
|
typedef std::map<SimpleTriangle, bool, triangle_key_order> TriangleMap;
|
|
|
|
void writeGeometry(std::string prefixName, std::string id, int geomIndex, std::string matID, Transform transform,
|
|
std::ostream &os, VertexData *vData, TriangleMap &triMap, const fs::path &meshesDirectory) {
|
|
std::vector<Vertex> vertexBuffer;
|
|
std::vector<Triangle> triangles;
|
|
std::map<Vertex, int, vertex_key_order> vertexMap;
|
|
size_t numMerged = 0, triangleIdx = 0, duplicates = 0;
|
|
Triangle triangle;
|
|
if (tess_data.size() == 0)
|
|
return;
|
|
|
|
id += formatString("_%i", geomIndex);
|
|
|
|
for (size_t i=0; i<tess_data.size(); i+=tess_nSources) {
|
|
Vertex vertex;
|
|
domUint posRef = tess_data[i+vData->typeToOffsetInStream[EPosition]];
|
|
vertex.v = vData->data[vData->typeToOffset[EPosition]][posRef].toPoint();
|
|
|
|
if (vData->typeToOffset[ENormal] != -1) {
|
|
domUint normalRef = tess_data[i+vData->typeToOffsetInStream[ENormal]];
|
|
vertex.n = vData->data[vData->typeToOffset[ENormal]][normalRef].toNormal();
|
|
}
|
|
|
|
if (vData->typeToOffset[EUV] != -1) {
|
|
domUint uvRef = tess_data[i+vData->typeToOffsetInStream[EUV]];
|
|
vertex.uv = vData->data[vData->typeToOffset[EUV]][uvRef].toPoint2();
|
|
}
|
|
|
|
int key = -1;
|
|
if (vertexMap.find(vertex) != vertexMap.end()) {
|
|
key = vertexMap[vertex];
|
|
numMerged++;
|
|
} else {
|
|
key = (int) vertexBuffer.size();
|
|
vertexMap[vertex] = (int) key;
|
|
vertexBuffer.push_back(vertex);
|
|
}
|
|
triangle.idx[triangleIdx++] = key;
|
|
if (triangleIdx == 3) {
|
|
Point p0 = vertexBuffer[triangle.idx[0]].v,
|
|
p1 = vertexBuffer[triangle.idx[1]].v,
|
|
p2 = vertexBuffer[triangle.idx[2]].v;
|
|
if (triMap.find(SimpleTriangle(p0, p1, p2)) != triMap.end() ||
|
|
triMap.find(SimpleTriangle(p2, p0, p1)) != triMap.end() ||
|
|
triMap.find(SimpleTriangle(p1, p2, p0)) != triMap.end() ||
|
|
triMap.find(SimpleTriangle(p1, p0, p2)) != triMap.end() ||
|
|
triMap.find(SimpleTriangle(p0, p2, p1)) != triMap.end() ||
|
|
triMap.find(SimpleTriangle(p2, p1, p0)) != triMap.end()) {
|
|
/* This triangle is a duplicate from another one which exists
|
|
in the same geometry group -- we may be dealing with SketchUp,
|
|
which sometimes exports every face TWICE! */
|
|
duplicates++;
|
|
} else {
|
|
triMap[SimpleTriangle(p0, p1, p2)] = true;
|
|
triangles.push_back(triangle);
|
|
}
|
|
triangleIdx = 0;
|
|
}
|
|
}
|
|
|
|
if (duplicates > 0) {
|
|
if (triangles.size() == 0) {
|
|
SLog(EWarn, "%s: Only contains duplicates of already-existing geometry. Ignoring.");
|
|
os << "\t<!-- Ignored shape \"" << prefixName << "/"
|
|
<< id << "\" (mat=\"" << matID << "\"), since it only contains duplicate geometry. -->" << endl << endl;
|
|
return;
|
|
} else {
|
|
SLog(EWarn, "Geometry contains %i duplicate triangles!", duplicates);
|
|
}
|
|
}
|
|
|
|
SAssert(triangleIdx == 0);
|
|
SLog(EInfo, "%s: Converted " SIZE_T_FMT " triangles, " SIZE_T_FMT
|
|
" vertices (merged " SIZE_T_FMT " vertices).", id.c_str(),
|
|
triangles.size(), vertexBuffer.size(), numMerged);
|
|
|
|
ref<TriMesh> mesh = new TriMesh(triangles.size(), vertexBuffer.size());
|
|
std::copy(triangles.begin(), triangles.end(), mesh->getTriangles());
|
|
std::copy(vertexBuffer.begin(), vertexBuffer.end(), mesh->getVertexBuffer());
|
|
mesh->calculateTangentSpaceBasis(vData->typeToOffset[ENormal]!=-1, vData->typeToOffset[EUV]!=-1);
|
|
|
|
std::string filename = id + std::string(".serialized");
|
|
ref<FileStream> stream = new FileStream(meshesDirectory / filename, FileStream::ETruncReadWrite);
|
|
stream->setByteOrder(Stream::ENetworkByteOrder);
|
|
mesh->serialize(stream);
|
|
stream->close();
|
|
|
|
std::ostringstream matrix;
|
|
for (int i=0; i<4; ++i)
|
|
for (int j=0; j<4; ++j)
|
|
matrix << transform.getMatrix()->m[i][j] << " ";
|
|
std::string matrixValues = matrix.str();
|
|
|
|
os << "\t<shape id=\"" << prefixName << "/" << id << "\" type=\"serialized\">" << endl;
|
|
os << "\t\t<string name=\"filename\" value=\"meshes/" << filename << "\"/>" << endl;
|
|
if (!transform.isIdentity()) {
|
|
os << "\t\t<transform name=\"toWorld\">" << endl;
|
|
os << "\t\t\t<matrix value=\"" << matrixValues.substr(0, matrixValues.length()-1) << "\"/>" << endl;
|
|
os << "\t\t</transform>" << endl;
|
|
}
|
|
if (matID != "")
|
|
os << "\t\t<ref name=\"bsdf\" id=\"" << matID << "\"/>" << endl;
|
|
os << "\t</shape>" << endl << endl;
|
|
}
|
|
|
|
void loadGeometry(std::string prefixName, Transform transform, std::ostream &os, domGeometry &geom,
|
|
StringMap &matLookupTable, const fs::path &meshesDir) {
|
|
std::string identifier;
|
|
if (geom.getId() != NULL) {
|
|
identifier = geom.getId();
|
|
} else {
|
|
if (geom.getName() != NULL) {
|
|
identifier = geom.getName();
|
|
} else {
|
|
static int unnamedCtr = 0;
|
|
identifier = formatString("unnamedGeom_%i", unnamedCtr++);
|
|
}
|
|
}
|
|
TriangleMap triMap;
|
|
|
|
SLog(EInfo, "Converting geometry \"%s\" (instantiated by %s)..", identifier.c_str(),
|
|
prefixName == "" ? "/" : prefixName.c_str());
|
|
domMesh *mesh = geom.getMesh().cast();
|
|
if (!mesh)
|
|
SLog(EError, "Invalid geometry type encountered (must be a <mesh>)!");
|
|
|
|
const domInputLocal_Array &vertInputs = mesh->getVertices()->getInput_array();
|
|
|
|
int geomIndex = 0;
|
|
|
|
domTriangles_Array &trianglesArray = mesh->getTriangles_array();
|
|
for (size_t i=0; i<trianglesArray.getCount(); ++i) {
|
|
domTriangles *triangles = trianglesArray[i];
|
|
domInputLocalOffset_Array &inputs = triangles->getInput_array();
|
|
VertexData *data = fetchVertexData(transform, os, vertInputs, inputs);
|
|
domListOfUInts &indices = triangles->getP()->getValue();
|
|
tess_data.clear();
|
|
tess_nSources = data->nSources;
|
|
for (size_t j=0; j<indices.getCount(); ++j)
|
|
tess_data.push_back(indices[j]);
|
|
std::string matID;
|
|
if (triangles->getMaterial() == NULL || matLookupTable.find(triangles->getMaterial()) == matLookupTable.end())
|
|
SLog(EWarn, "Referenced material could not be found, substituting a lambertian BRDF.");
|
|
else
|
|
matID = matLookupTable[triangles->getMaterial()];
|
|
writeGeometry(prefixName, identifier, geomIndex, matID, transform, os, data, triMap, meshesDir);
|
|
delete data;
|
|
++geomIndex;
|
|
}
|
|
|
|
domPolygons_Array &polygonsArray = mesh->getPolygons_array();
|
|
for (size_t i=0; i<polygonsArray.getCount(); ++i) {
|
|
domPolygons *polygons = polygonsArray[i];
|
|
domInputLocalOffset_Array &inputs = polygons->getInput_array();
|
|
VertexData *data = fetchVertexData(transform, os, vertInputs, inputs);
|
|
domP_Array &indexArray = polygons->getP_array();
|
|
int posOffset = data->typeToOffset[EPosition];
|
|
|
|
tess_data.clear();
|
|
tess_nSources = data->nSources;
|
|
for (size_t j=0; j<indexArray.getCount(); ++j) {
|
|
domListOfUInts &indices = indexArray[j]->getValue();
|
|
domUint *temp = new domUint[indices.getCount()];
|
|
for (size_t l = 0; l<indices.getCount(); ++l)
|
|
temp[l] = indices.get(l);
|
|
|
|
gluTessBeginPolygon(tess, NULL);
|
|
gluTessBeginContour(tess);
|
|
|
|
for (size_t k=0; k<indices.getCount(); k+=data->nSources)
|
|
gluTessVertex(tess, &data->glPos[temp[k+posOffset]*3], (GLvoid *) (k+temp));
|
|
|
|
gluTessEndContour(tess);
|
|
gluTessEndPolygon(tess);
|
|
delete[] temp;
|
|
}
|
|
|
|
std::string matID;
|
|
if (polygons->getMaterial() == NULL || matLookupTable.find(polygons->getMaterial()) == matLookupTable.end())
|
|
SLog(EWarn, "Referenced material could not be found, substituting a lambertian BRDF.");
|
|
else
|
|
matID = matLookupTable[polygons->getMaterial()];
|
|
|
|
writeGeometry(prefixName, identifier, geomIndex, matID, transform, os, data, triMap, meshesDir);
|
|
delete data;
|
|
++geomIndex;
|
|
}
|
|
|
|
domPolylist_Array &polylistArray = mesh->getPolylist_array();
|
|
for (size_t i=0; i<polylistArray.getCount(); ++i) {
|
|
domPolylist *polylist = polylistArray[i];
|
|
domInputLocalOffset_Array &inputs = polylist->getInput_array();
|
|
VertexData *data = fetchVertexData(transform, os, vertInputs, inputs);
|
|
domListOfUInts &vcount = polylist->getVcount()->getValue();
|
|
domListOfUInts &indexArray = polylist->getP()->getValue();
|
|
int posOffset = data->typeToOffset[EPosition], indexOffset = 0;
|
|
tess_data.clear();
|
|
tess_nSources = data->nSources;
|
|
|
|
for (size_t j=0; j<vcount.getCount(); ++j) {
|
|
size_t vertexCount = (size_t) vcount.get(j);
|
|
|
|
domUint *temp = new domUint[vertexCount * data->nSources];
|
|
for (size_t l = 0; l<vertexCount * data->nSources; ++l)
|
|
temp[l] = indexArray.get(indexOffset++);
|
|
|
|
gluTessBeginPolygon(tess, NULL);
|
|
gluTessBeginContour(tess);
|
|
|
|
for (size_t k=0; k<vertexCount; k++)
|
|
gluTessVertex(tess, &data->glPos[temp[k*data->nSources+posOffset]*3], (GLvoid *) (temp + k*data->nSources));
|
|
|
|
gluTessEndContour(tess);
|
|
gluTessEndPolygon(tess);
|
|
delete[] temp;
|
|
}
|
|
|
|
std::string matID;
|
|
if (polylist->getMaterial() == NULL || matLookupTable.find(polylist->getMaterial()) == matLookupTable.end())
|
|
SLog(EWarn, "Referenced material could not be found, substituting a lambertian BRDF.");
|
|
else
|
|
matID = matLookupTable[polylist->getMaterial()];
|
|
|
|
writeGeometry(prefixName, identifier, geomIndex, matID, transform, os, data, triMap, meshesDir);
|
|
delete data;
|
|
++geomIndex;
|
|
}
|
|
}
|
|
|
|
void loadMaterialParam(GeometryConverter *cvt, std::ostream &os, const fs::path &name, StringMap &idToTexture,
|
|
domCommon_color_or_texture_type *value, bool handleRefs) {
|
|
if (!value)
|
|
return;
|
|
domCommon_color_or_texture_type_complexType::domColor* color =
|
|
value->getColor().cast();
|
|
domCommon_color_or_texture_type_complexType::domTexture* texture =
|
|
value->getTexture().cast();
|
|
if (color && !handleRefs) {
|
|
domFloat4 &colValue = color->getValue();
|
|
if (cvt->m_srgb)
|
|
os << "\t\t<srgb name=\"" << name << "\" value=\"";
|
|
else
|
|
os << "\t\t<rgb name=\"" << name << "\" value=\"";
|
|
os << colValue.get(0) << " " << colValue.get(1) << " "
|
|
<< colValue.get(2) << "\"/>" << endl;
|
|
} else if (texture && handleRefs) {
|
|
if (idToTexture.find(texture->getTexture()) == idToTexture.end()) {
|
|
SLog(EError, "Could not find referenced texture \"%s\"", texture->getTexture());
|
|
} else {
|
|
os << "\t\t<ref name=\"" << name << "\" id=\""
|
|
<< idToTexture[texture->getTexture()] << "\"/>" << endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
void loadMaterialParam(GeometryConverter *cvt, std::ostream &os, const fs::path &name, StringMap &,
|
|
domCommon_float_or_param_type *value, bool handleRef) {
|
|
if (!value)
|
|
return;
|
|
domCommon_float_or_param_type::domFloat *floatValue = value->getFloat();
|
|
if (!handleRef && floatValue) {
|
|
os << "\t\t<float name=\"" << name << "\" value=\""
|
|
<< floatValue->getValue() << "\"/>" << endl;
|
|
}
|
|
}
|
|
|
|
void loadMaterial(GeometryConverter *cvt, std::ostream &os, domMaterial &mat, StringMap &_idToTexture) {
|
|
std::string identifier;
|
|
if (mat.getId() != NULL) {
|
|
identifier = mat.getId();
|
|
} else {
|
|
if (mat.getName() != NULL) {
|
|
identifier = mat.getName();
|
|
} else {
|
|
static int unnamedCtr = 0;
|
|
identifier = formatString("unnamedMat_%i", unnamedCtr++);
|
|
}
|
|
}
|
|
StringMap idToTexture = _idToTexture;
|
|
|
|
daeURI &effRef = mat.getInstance_effect()->getUrl();
|
|
effRef.resolveElement();
|
|
domEffect *effect = daeSafeCast<domEffect>(effRef.getElement());
|
|
|
|
if (!effect)
|
|
SLog(EError, "Referenced effect not found!");
|
|
|
|
domProfile_COMMON *commonProfile = daeSafeCast<domProfile_COMMON>
|
|
(effect->getDescendant("profile_COMMON"));
|
|
|
|
if (!commonProfile)
|
|
SLog(EError, "Common effect profile not found!");
|
|
|
|
/* The following supports a subset of the curious ColladaFX output
|
|
produced by the Blender COLLADA exporter */
|
|
daeTArray<daeSmartRef<domCommon_newparam_type> > &newParamArray = commonProfile->getNewparam_array();
|
|
for (size_t i=0; i<newParamArray.getCount(); ++i) {
|
|
domCommon_newparam_type_complexType *newParam = newParamArray[i];
|
|
domFx_surface_common *surface = newParam->getSurface();
|
|
domFx_sampler2D_common *sampler2D = newParam->getSampler2D();
|
|
|
|
|
|
if (surface) {
|
|
SAssert(surface->getType() == FX_SURFACE_TYPE_ENUM_2D);
|
|
daeTArray<daeSmartRef<domFx_surface_init_from_common> > &initFromArray
|
|
= surface->getFx_surface_init_common()->getInit_from_array();
|
|
SAssert(initFromArray.getCount() == 1);
|
|
std::string id = initFromArray[0]->getValue().getID();
|
|
if (idToTexture.find(id) == idToTexture.end())
|
|
SLog(EError, "Referenced bitmap '%s' not found!", id.c_str());
|
|
idToTexture[newParam->getSid()] = idToTexture[id];
|
|
}
|
|
|
|
if (sampler2D) {
|
|
std::string id = sampler2D->getSource()->getValue();
|
|
if (idToTexture.find(id) == idToTexture.end())
|
|
SLog(EError, "Referenced surface '%s' not found!", id.c_str());
|
|
idToTexture[newParam->getSid()] = idToTexture[id];
|
|
}
|
|
}
|
|
|
|
domProfile_COMMON::domTechnique *technique = commonProfile->getTechnique();
|
|
|
|
if (!technique)
|
|
SLog(EError, "The technique element is missing!");
|
|
|
|
domProfile_COMMON::domTechnique::domPhong* phong = technique->getPhong();
|
|
domProfile_COMMON::domTechnique::domBlinn* blinn = technique->getBlinn();
|
|
domProfile_COMMON::domTechnique::domLambert* lambert = technique->getLambert();
|
|
domProfile_COMMON::domTechnique::domConstant* constant = technique->getConstant();
|
|
|
|
if (phong) {
|
|
domCommon_color_or_texture_type* diffuse = phong->getDiffuse();
|
|
domCommon_color_or_texture_type* specular = phong->getSpecular();
|
|
domCommon_float_or_param_type* shininess = phong->getShininess();
|
|
bool isDiffuse = false;
|
|
|
|
if (specular->getColor().cast()) {
|
|
domFloat4 &colValue = specular->getColor()->getValue();
|
|
if (colValue.get(0) == colValue.get(1) &&
|
|
colValue.get(1) == colValue.get(2) &&
|
|
colValue.get(2) == 0)
|
|
isDiffuse = true;
|
|
}
|
|
if (isDiffuse) {
|
|
os << "\t<bsdf id=\"" << identifier << "\" type=\"lambertian\">" << endl;
|
|
loadMaterialParam(cvt, os, "reflectance", idToTexture, diffuse, false);
|
|
loadMaterialParam(cvt, os, "reflectance", idToTexture, diffuse, true);
|
|
os << "\t</bsdf>" << endl << endl;
|
|
} else {
|
|
os << "\t<bsdf id=\"" << identifier << "\" type=\"phong\">" << endl;
|
|
loadMaterialParam(cvt, os, "diffuseReflectance", idToTexture, diffuse, false);
|
|
loadMaterialParam(cvt, os, "specularReflectance", idToTexture, specular, false);
|
|
loadMaterialParam(cvt, os, "exponent", idToTexture, shininess, false);
|
|
loadMaterialParam(cvt, os, "diffuseReflectance", idToTexture, diffuse, true);
|
|
loadMaterialParam(cvt, os, "specularReflectance", idToTexture, specular, true);
|
|
loadMaterialParam(cvt, os, "exponent", idToTexture, shininess, true);
|
|
os << "\t</bsdf>" << endl << endl;
|
|
}
|
|
} else if (lambert) {
|
|
domCommon_float_or_param_type* transparency = lambert->getTransparency();
|
|
domCommon_float_or_param_type::domFloat *transparencyValue =
|
|
transparency ? transparency->getFloat() : NULL;
|
|
if (transparencyValue && transparencyValue->getValue() > 0.5) {
|
|
os << "\t<bsdf id=\"" << identifier << "\" type=\"dielectric\"/>" << endl << endl;
|
|
} else {
|
|
domCommon_color_or_texture_type* diffuse = lambert->getDiffuse();
|
|
os << "\t<bsdf id=\"" << identifier << "\" type=\"lambertian\">" << endl;
|
|
loadMaterialParam(cvt, os, "reflectance", idToTexture, diffuse, false);
|
|
loadMaterialParam(cvt, os, "reflectance", idToTexture, diffuse, true);
|
|
os << "\t</bsdf>" << endl << endl;
|
|
}
|
|
} else if (blinn) {
|
|
SLog(EWarn, "\"%s\": Encountered a \"blinn\" COLLADA material, which is currently "
|
|
"unsupported in Mitsuba -- replacing it using a Phong material.", identifier.c_str());
|
|
domCommon_color_or_texture_type* diffuse = blinn->getDiffuse();
|
|
domCommon_color_or_texture_type* specular = blinn->getSpecular();
|
|
domCommon_float_or_param_type* shininess = blinn->getShininess();
|
|
bool isDiffuse = false;
|
|
|
|
if (specular->getColor().cast()) {
|
|
domFloat4 &colValue = specular->getColor()->getValue();
|
|
if (colValue.get(0) == colValue.get(1) &&
|
|
colValue.get(1) == colValue.get(2) &&
|
|
colValue.get(2) == 0)
|
|
isDiffuse = true;
|
|
}
|
|
if (isDiffuse) {
|
|
os << "\t<bsdf id=\"" << identifier << "\" type=\"lambertian\">" << endl;
|
|
loadMaterialParam(cvt, os, "reflectance", idToTexture, diffuse, false);
|
|
loadMaterialParam(cvt, os, "reflectance", idToTexture, diffuse, true);
|
|
os << "\t</bsdf>" << endl << endl;
|
|
} else {
|
|
os << "\t<bsdf id=\"" << identifier << "\" type=\"blinn\">" << endl;
|
|
os << "\t\t<float name=\"specularReflectance\" value=\"1\"/>" << endl;
|
|
os << "\t\t<float name=\"diffuseReflectance\" value=\"1\"/>" << endl;
|
|
loadMaterialParam(cvt, os, "diffuseReflectance", idToTexture, diffuse, false);
|
|
loadMaterialParam(cvt, os, "specularReflectance", idToTexture, specular, false);
|
|
loadMaterialParam(cvt, os, "exponent", idToTexture, shininess, false);
|
|
loadMaterialParam(cvt, os, "diffuseReflectance", idToTexture, diffuse, true);
|
|
loadMaterialParam(cvt, os, "specularReflectance", idToTexture, specular, true);
|
|
loadMaterialParam(cvt, os, "exponent", idToTexture, shininess, true);
|
|
os << "\t</bsdf>" << endl << endl;
|
|
}
|
|
} else if (constant) {
|
|
domCommon_float_or_param_type* transparency = constant->getTransparency();
|
|
domCommon_float_or_param_type::domFloat *transparencyValue =
|
|
transparency ? transparency->getFloat() : NULL;
|
|
if (transparencyValue && transparencyValue->getValue() > 0.5) {
|
|
os << "\t<bsdf id=\"" << identifier << "\" type=\"dielectric\"/>" << endl << endl;
|
|
} else {
|
|
SLog(EWarn, "\"%s\": Encountered a \"constant\" COLLADA material, which is currently "
|
|
"unsupported in Mitsuba -- replacing it using a Lambertian material.", identifier.c_str());
|
|
os << "\t<bsdf id=\"" << identifier << "\" type=\"lambertian\"/>" << endl << endl;
|
|
}
|
|
} else {
|
|
SLog(EError, "Material type not supported! (must be Lambertian/Phong/Blinn/Constant)");
|
|
}
|
|
}
|
|
|
|
void loadLight(Transform transform, std::ostream &os, domLight &light) {
|
|
std::string identifier;
|
|
if (light.getId() != NULL) {
|
|
identifier = light.getId();
|
|
} else {
|
|
if (light.getName() != NULL) {
|
|
identifier = light.getName();
|
|
} else {
|
|
static int unnamedCtr = 0;
|
|
identifier = formatString("unnamedLight_%i", unnamedCtr++);
|
|
}
|
|
}
|
|
|
|
SLog(EInfo, "Converting light \"%s\" ..", identifier.c_str());
|
|
char *end_ptr = NULL;
|
|
|
|
// Lights in Mitsuba point along the positive Z axis (COLLADA: neg. Z)
|
|
transform = transform * Transform::scale(Vector(1, 1, -1));
|
|
|
|
Point pos = transform(Point(0, 0, 0));
|
|
Point target = transform(Point(0, 0, 1));
|
|
|
|
Float intensity = 1;
|
|
const domTechnique_Array &techniques = light.getTechnique_array();
|
|
for (size_t i=0; i<techniques.getCount(); ++i) {
|
|
domTechnique *tech = techniques.get(i);
|
|
|
|
daeElement *intensityElement = tech->getChild("intensity");
|
|
if (intensityElement && intensityElement->hasCharData()) {
|
|
std::string charData = intensityElement->getCharData();
|
|
intensity = (Float) strtod(charData.c_str(), &end_ptr);
|
|
if (*end_ptr != '\0')
|
|
SLog(EError, "Could not parse the light intensity!");
|
|
}
|
|
}
|
|
|
|
domLight::domTechnique_common::domPoint *point = light.getTechnique_common()->getPoint().cast();
|
|
if (point) {
|
|
bool notQuadratic = false;
|
|
if (point->getConstant_attenuation() && point->getConstant_attenuation()->getValue() != 1)
|
|
notQuadratic = true;
|
|
if (point->getLinear_attenuation() && point->getLinear_attenuation()->getValue() != 0)
|
|
notQuadratic = true;
|
|
if (point->getQuadratic_attenuation() && point->getQuadratic_attenuation()->getValue() != 1)
|
|
notQuadratic = true;
|
|
if (notQuadratic)
|
|
SLog(EWarn, "Point light \"%s\" is not a quadratic light! Treating it as one -- expect problems.", identifier.c_str());
|
|
domFloat3 &color = point->getColor()->getValue();
|
|
os << "\t<luminaire id=\"" << identifier << "\" type=\"point\">" << endl;
|
|
os << "\t\t<rgb name=\"intensity\" value=\"" << color[0]*intensity << " " << color[1]*intensity << " " << color[2]*intensity << "\"/>" << endl << endl;
|
|
os << "\t\t<transform name=\"toWorld\">" << endl;
|
|
os << "\t\t\t<translate x=\"" << pos.x << "\" y=\"" << pos.y << "\" z=\"" << pos.z << "\"/>" << endl;
|
|
os << "\t\t</transform>" << endl;
|
|
os << "\t</luminaire>" << endl << endl;
|
|
}
|
|
|
|
domLight::domTechnique_common::domDirectional *directional = light.getTechnique_common()->getDirectional().cast();
|
|
if (directional) {
|
|
domFloat3 &color = directional->getColor()->getValue();
|
|
os << "\t<luminaire id=\"" << identifier << "\" type=\"directional\">" << endl;
|
|
os << "\t\t<rgb name=\"intensity\" value=\"" << color[0]*intensity << " " << color[1]*intensity << " " << color[2]*intensity << "\"/>" << endl << endl;
|
|
os << "\t\t<transform name=\"toWorld\">" << endl;
|
|
os << "\t\t\t<lookAt ox=\"" << pos.x << "\" oy=\"" << pos.y << "\" oz=\"" << pos.z << "\" tx=\"" << target.x << "\" ty=\"" << target.y << "\" tz=\"" << target.z << "\"/>" << endl;
|
|
os << "\t\t</transform>" << endl << endl;
|
|
os << "\t</luminaire>" << endl << endl;
|
|
}
|
|
|
|
domLight::domTechnique_common::domSpot *spot = light.getTechnique_common()->getSpot().cast();
|
|
if (spot) {
|
|
bool notQuadratic = false;
|
|
if (spot->getConstant_attenuation() && spot->getConstant_attenuation()->getValue() != 1)
|
|
notQuadratic = true;
|
|
if (spot->getLinear_attenuation() && spot->getLinear_attenuation()->getValue() != 0)
|
|
notQuadratic = true;
|
|
if (spot->getQuadratic_attenuation() && spot->getQuadratic_attenuation()->getValue() != 1)
|
|
notQuadratic = true;
|
|
if (notQuadratic)
|
|
SLog(EWarn, "Spot light \"%s\" is not a quadratic light! Treating it as one -- expect problems.", identifier.c_str());
|
|
domFloat3 &color = spot->getColor()->getValue();
|
|
Float falloffAngle = 180.0f;
|
|
if (spot->getFalloff_angle())
|
|
falloffAngle = (Float) spot->getFalloff_angle()->getValue();
|
|
os << "\t<luminaire id=\"" << identifier << "\" type=\"spot\">" << endl;
|
|
os << "\t\t<rgb name=\"intensity\" value=\"" << color[0]*intensity << " " << color[1]*intensity << " " << color[2]*intensity << "\"/>" << endl;
|
|
os << "\t\t<float name=\"cutoffAngle\" value=\"" << falloffAngle/2 << "\"/>" << endl << endl;
|
|
os << "\t\t<transform name=\"toWorld\">" << endl;
|
|
os << "\t\t\t<lookAt ox=\"" << pos.x << "\" oy=\"" << pos.y << "\" oz=\"" << pos.z << "\" tx=\"" << target.x << "\" ty=\"" << target.y << "\" tz=\"" << target.z << "\"/>" << endl;
|
|
os << "\t\t</transform>" << endl;
|
|
os << "\t</luminaire>" << endl << endl;
|
|
}
|
|
domLight::domTechnique_common::domAmbient *ambient = light.getTechnique_common()->getAmbient().cast();
|
|
if (ambient) {
|
|
domFloat3 &color = ambient->getColor()->getValue();
|
|
os << "\t<luminaire id=\"" << identifier << "\" type=\"constant\">" << endl;
|
|
os << "\t\t<rgb name=\"intensity\" value=\"" << color[0]*intensity << " " << color[1]*intensity << " " << color[2]*intensity << "\"/>" << endl;
|
|
os << "\t</luminaire>" << endl << endl;
|
|
}
|
|
if (!point && !spot && !ambient && !directional)
|
|
SLog(EWarn, "Encountered an unknown light type!");
|
|
}
|
|
|
|
void loadImage(GeometryConverter *cvt, std::ostream &os, const fs::path &textureDir,
|
|
domImage &image, StringMap &idToTexture, StringMap &fileToId) {
|
|
std::string identifier;
|
|
if (image.getId() != NULL) {
|
|
identifier = image.getId();
|
|
} else {
|
|
if (image.getName() != NULL) {
|
|
identifier = image.getName();
|
|
} else {
|
|
static int unnamedCtr = 0;
|
|
identifier = formatString("unnamedTexture_%i", unnamedCtr++);
|
|
}
|
|
}
|
|
|
|
SLog(EInfo, "Converting texture \"%s\" ..", identifier.c_str());
|
|
|
|
std::string filename = cdom::uriToFilePath(image.getInit_from()->getValue().str());
|
|
if (fileToId.find(filename) != fileToId.end()) {
|
|
idToTexture[identifier] = fileToId[filename];
|
|
return;
|
|
}
|
|
|
|
idToTexture[identifier] = identifier;
|
|
fileToId[filename] = identifier;
|
|
|
|
boost::filesystem::path path = boost::filesystem::path(filename, boost::filesystem::native);
|
|
fs::path targetPath = textureDir / path.leaf();
|
|
fs::path resolved = filename;
|
|
|
|
if (endsWith(filename, ".rgb"))
|
|
SLog(EWarn, "Maya RGB images must be converted to PNG, EXR or JPEG! The 'imgcvt' "
|
|
"utility found in the Maya binary directory can be used to do this.");
|
|
|
|
if (!fs::exists(targetPath)) {
|
|
ref<FileResolver> fRes = Thread::getThread()->getFileResolver();
|
|
if (!fs::exists(resolved)) {
|
|
resolved = fRes->resolve(path.leaf());
|
|
if (!fs::exists(resolved)) {
|
|
SLog(EWarn, "Found neither \"%s\" nor \"%s\"!", filename.c_str(), resolved.file_string().c_str());
|
|
std::string result = cvt->locateResource(filename);
|
|
if (result == "")
|
|
SLog(EError, "Unable to locate a resource -- aborting conversion.");
|
|
resolved = result;
|
|
}
|
|
}
|
|
ref<FileStream> input = new FileStream(resolved, FileStream::EReadOnly);
|
|
ref<FileStream> output = new FileStream(targetPath, FileStream::ETruncReadWrite);
|
|
input->copyTo(output);
|
|
input->close();
|
|
output->close();
|
|
}
|
|
|
|
os << "\t<texture id=\"" << identifier << "\" type=\"ldrtexture\">" << endl;
|
|
os << "\t\t<string name=\"filename\" value=\"textures/" << path.leaf() << "\"/>" << endl;
|
|
os << "\t</texture>" << endl << endl;
|
|
}
|
|
|
|
void loadCamera(GeometryConverter *cvt, Transform transform, std::ostream &os, domCamera &camera) {
|
|
std::string identifier;
|
|
if (camera.getId() != NULL) {
|
|
identifier = camera.getId();
|
|
} else {
|
|
if (camera.getName() != NULL) {
|
|
identifier = camera.getName();
|
|
} else {
|
|
static int unnamedCtr = 0;
|
|
identifier = formatString("unnamedCamera_%i", unnamedCtr++);
|
|
}
|
|
}
|
|
|
|
SLog(EInfo, "Converting camera \"%s\" ..", identifier.c_str());
|
|
Float aspect = 1.0f;
|
|
int xres=768;
|
|
|
|
// Cameras in Mitsuba point along the positive Z axis (COLLADA: neg. Z)
|
|
transform = transform * Transform::scale(Vector(1,1,-1));
|
|
|
|
std::ostringstream matrix;
|
|
for (int i=0; i<4; ++i)
|
|
for (int j=0; j<4; ++j)
|
|
matrix << transform.getMatrix()->m[i][j] << " ";
|
|
std::string matrixValues = matrix.str();
|
|
|
|
domCamera::domOptics::domTechnique_common::domOrthographic* ortho = camera.getOptics()->
|
|
getTechnique_common()->getOrthographic().cast();
|
|
if (ortho) {
|
|
if (ortho->getAspect_ratio().cast() != 0)
|
|
aspect = (Float) ortho->getAspect_ratio()->getValue();
|
|
if (cvt->m_xres != -1) {
|
|
xres = cvt->m_xres;
|
|
aspect = (Float) cvt->m_xres / (Float) cvt->m_yres;
|
|
}
|
|
os << "\t<camera id=\"" << identifier << "\" type=\"orthographic\">" << endl;
|
|
}
|
|
|
|
domCamera::domOptics::domTechnique_common::domPerspective* persp = camera.getOptics()->
|
|
getTechnique_common()->getPerspective().cast();
|
|
if (persp) {
|
|
if (cvt->m_xres != -1) {
|
|
xres = cvt->m_xres;
|
|
aspect = (Float) cvt->m_xres / (Float) cvt->m_yres;
|
|
} else {
|
|
if (persp->getAspect_ratio().cast() != 0) {
|
|
aspect = (Float) persp->getAspect_ratio()->getValue();
|
|
if (std::abs(aspect-0.1) < Epsilon) {
|
|
SLog(EWarn, "Found the suspicious aspect ratio \"0.1\", which is likely due to a bug in Blender 2.5"
|
|
" - setting to 1.0. Please use the \"-r\" parameter to override the resolution.");
|
|
aspect = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
os << "\t<camera id=\"" << identifier << "\" type=\"perspective\">" << endl;
|
|
if (persp->getXfov().cast()) {
|
|
Float xFov = (Float) persp->getXfov()->getValue();
|
|
if (std::abs(xFov-1.0f) < Epsilon && cvt->m_fov == -1) {
|
|
SLog(EWarn, "Found the suspicious field of view value \"1.0\", which is likely due to a bug in Blender 2.5"
|
|
" - setting to 45deg. Please use the \"-f\" parameter to override this.");
|
|
xFov = 45.0f;
|
|
}
|
|
Float yFov = radToDeg(2 * std::atan(std::tan(degToRad(xFov)/2) / aspect));
|
|
if (cvt->m_fov != -1)
|
|
xFov = yFov = cvt->m_fov;
|
|
if (aspect <= 1.0f)
|
|
os << "\t\t<float name=\"fov\" value=\"" << xFov << "\"/>" << endl;
|
|
else
|
|
os << "\t\t<float name=\"fov\" value=\"" << yFov << "\"/>" << endl;
|
|
} else if (persp->getYfov().cast()) {
|
|
Float yFov = (Float) persp->getYfov()->getValue();
|
|
if (std::abs(yFov-1.0) < Epsilon && cvt->m_fov == -1) {
|
|
SLog(EWarn, "Found the suspicious field of view value \"1.0\", which is likely due to a bug in Blender 2.5"
|
|
" - setting to 45deg. Please use the \"-f\" parameter to override this.");
|
|
yFov = 45.0f;
|
|
}
|
|
Float xFov = radToDeg(2 * std::atan(std::tan(degToRad(yFov)/2) * aspect));
|
|
if (cvt->m_fov != -1)
|
|
xFov = yFov = cvt->m_fov;
|
|
if (aspect > 1.0f)
|
|
os << "\t\t<float name=\"fov\" value=\"" << yFov << "\"/>" << endl;
|
|
else
|
|
os << "\t\t<float name=\"fov\" value=\"" << xFov << "\"/>" << endl;
|
|
}
|
|
os << "\t\t<float name=\"nearClip\" value=\"" << persp->getZnear()->getValue() << "\"/>" << endl;
|
|
os << "\t\t<float name=\"farClip\" value=\"" << persp->getZfar()->getValue() << "\"/>" << endl;
|
|
os << "\t\t<boolean name=\"mapSmallerSide\" value=\"" << (cvt->m_mapSmallerSide ? "true" : "false") << "\"/>" << endl;
|
|
}
|
|
|
|
os << endl;
|
|
os << "\t\t<transform name=\"toWorld\">" << endl;
|
|
os << "\t\t\t<matrix value=\"" << matrixValues.substr(0, matrixValues.length()-1) << "\"/>" << endl;
|
|
os << "\t\t</transform>" << endl << endl;
|
|
os << "\t\t<sampler type=\"ldsampler\">" << endl;
|
|
os << "\t\t\t<integer name=\"sampleCount\" value=\"" << cvt->m_samplesPerPixel << "\"/>" << endl;
|
|
os << "\t\t</sampler>" << endl << endl;
|
|
os << "\t\t<film type=\"exrfilm\">" << endl;
|
|
os << "\t\t\t<integer name=\"width\" value=\"" << xres << "\"/>" << endl;
|
|
os << "\t\t\t<integer name=\"height\" value=\"" << (int) (xres/aspect) << "\"/>" << endl;
|
|
os << "\t\t\t<rfilter type=\"gaussian\"/>" << endl;
|
|
os << "\t\t</film>" << endl;
|
|
os << "\t</camera>" << endl << endl;
|
|
}
|
|
|
|
void loadNode(GeometryConverter *cvt, Transform transform, std::ostream &os,
|
|
domNode &node, std::string prefixName, const fs::path &meshesDir) {
|
|
std::string identifier;
|
|
if (node.getId() != NULL) {
|
|
identifier = node.getId();
|
|
} else {
|
|
if (node.getName() != NULL) {
|
|
identifier = node.getName();
|
|
} else {
|
|
static int unnamedCtr = 0;
|
|
identifier = formatString("unnamedNode_%i", unnamedCtr);
|
|
}
|
|
}
|
|
SLog(EInfo, "Converting node \"%s\" ..", identifier.c_str());
|
|
|
|
daeTArray<daeSmartRef<daeElement> > children = node.getChildren();
|
|
/* Parse transformations */
|
|
for (size_t i=0; i<children.getCount(); ++i) {
|
|
daeElement *element = children.get(i);
|
|
if (element->typeID() == domRotate::ID()) {
|
|
/* Skip rotations labeled as "post-rotationY". Maya exports these with some cameras,
|
|
which introduces an incorrect 90 degree rotation unless ignored */
|
|
if (element->hasAttribute("sid") && element->getAttribute("sid") == "post-rotationY")
|
|
continue;
|
|
daeTArray<double> value = daeSafeCast<domRotate>(element)->getValue();
|
|
transform = transform *
|
|
Transform::rotate(Vector((Float) value.get(0), (Float) value.get(1), (Float) value.get(2)), (Float) value.get(3));
|
|
} else if (element->typeID() == domTranslate::ID()) {
|
|
daeTArray<double> value = daeSafeCast<domTranslate>(element)->getValue();
|
|
transform = transform *
|
|
Transform::translate(Vector((Float) value.get(0), (Float) value.get(1), (Float) value.get(2)));
|
|
} else if (element->typeID() == domScale::ID()) {
|
|
daeTArray<double> value = daeSafeCast<domScale>(element)->getValue();
|
|
transform = transform *
|
|
Transform::scale(Vector((Float) value.get(0), (Float) value.get(1), (Float) value.get(2)));
|
|
} else if (element->typeID() == domLookat::ID()) {
|
|
daeTArray<double> value = daeSafeCast<domLookat>(element)->getValue();
|
|
transform = transform *
|
|
Transform::lookAt(
|
|
Point((Float) value.get(0), (Float) value.get(1), (Float) value.get(2)),
|
|
Point((Float) value.get(3), (Float) value.get(4), (Float) value.get(5)),
|
|
Vector((Float) value.get(6), (Float) value.get(7), (Float) value.get(8))
|
|
);
|
|
} else if (element->typeID() == domMatrix::ID()) {
|
|
daeTArray<double> value = daeSafeCast<domMatrix>(element)->getValue();
|
|
ref<Matrix4x4> matrix = new Matrix4x4(
|
|
(Float) value.get(0), (Float) value.get(1), (Float) value.get(2), (Float) value.get(3),
|
|
(Float) value.get(4), (Float) value.get(5), (Float) value.get(6), (Float) value.get(7),
|
|
(Float) value.get(8), (Float) value.get(9), (Float) value.get(10), (Float) value.get(11),
|
|
(Float) value.get(12), (Float) value.get(13), (Float) value.get(14), (Float) value.get(15)
|
|
);
|
|
transform = transform * Transform(matrix);
|
|
}
|
|
}
|
|
|
|
|
|
/* Iterate over all geometry references */
|
|
domInstance_geometry_Array &instanceGeometries = node.getInstance_geometry_array();
|
|
for (size_t i=0; i<instanceGeometries.getCount(); ++i) {
|
|
domInstance_geometry *inst = instanceGeometries[i];
|
|
domGeometry *geom = daeSafeCast<domGeometry>(inst->getUrl().getElement());
|
|
domBind_material *bmat = inst->getBind_material();
|
|
StringMap matLookupTable;
|
|
if (bmat) {
|
|
domBind_material::domTechnique_common *technique = bmat->getTechnique_common();
|
|
if (!technique)
|
|
SLog(EError, "bind_material does not contain a <technique_common> element!");
|
|
domInstance_material_Array &instMaterials = technique->getInstance_material_array();
|
|
|
|
for (size_t i=0; i<instMaterials.getCount(); ++i) {
|
|
domInstance_material *instMat = instMaterials[i];
|
|
daeURI &matRef = instMat->getTarget();
|
|
matRef.resolveElement();
|
|
domMaterial *material = daeSafeCast<domMaterial>(matRef.getElement());
|
|
matLookupTable[instMat->getSymbol()] = material->getId();
|
|
}
|
|
} else {
|
|
SLog(EWarn, "instance_geometry does not contain a <bind_material> element!");
|
|
}
|
|
|
|
if (!geom)
|
|
SLog(EError, "Could not find a referenced geometry object!");
|
|
loadGeometry(prefixName, transform, os, *geom, matLookupTable, meshesDir);
|
|
}
|
|
|
|
/* Iterate over all light references */
|
|
domInstance_light_Array &instanceLights = node.getInstance_light_array();
|
|
for (size_t i=0; i<instanceLights.getCount(); ++i) {
|
|
domInstance_light *inst = instanceLights[i];
|
|
domLight *light = daeSafeCast<domLight>(inst->getUrl().getElement());
|
|
if (!light)
|
|
SLog(EError, "Could not find a referenced light!");
|
|
loadLight(transform, os, *light);
|
|
}
|
|
|
|
/* Iterate over all camera references */
|
|
domInstance_camera_Array &instanceCameras = node.getInstance_camera_array();
|
|
for (size_t i=0; i<instanceCameras.getCount(); ++i) {
|
|
domInstance_camera *inst = instanceCameras[i];
|
|
domCamera *camera = daeSafeCast<domCamera>(inst->getUrl().getElement());
|
|
if (camera == NULL)
|
|
SLog(EError, "Could not find a referenced camera!");
|
|
loadCamera(cvt, transform, os, *camera);
|
|
}
|
|
|
|
/* Recursively iterate through sub-nodes */
|
|
domNode_Array &nodes = node.getNode_array();
|
|
for (size_t i=0; i<nodes.getCount(); ++i)
|
|
loadNode(cvt, transform, os, *nodes[i], prefixName + std::string("/") + identifier, meshesDir);
|
|
|
|
/* Recursively iterate through <instance_node> elements */
|
|
domInstance_node_Array &instanceNodes = node.getInstance_node_array();
|
|
for (size_t i=0; i<instanceNodes.getCount(); ++i) {
|
|
domNode *node = daeSafeCast<domNode>(instanceNodes[i]->getUrl().getElement());
|
|
if (!node)
|
|
SLog(EError, "Could not find a referenced node!");
|
|
loadNode(cvt, transform, os, *node, prefixName + std::string("/") + identifier, meshesDir);
|
|
}
|
|
}
|
|
|
|
#ifndef WIN32
|
|
#define __stdcall
|
|
#endif
|
|
|
|
GLvoid __stdcall tessBegin(GLenum type) {
|
|
SAssert(type == GL_TRIANGLES);
|
|
}
|
|
|
|
GLvoid __stdcall tessVertex(void *data) {
|
|
const domUint *raw = (domUint *) data;
|
|
for (size_t i=0; i<tess_nSources; ++i)
|
|
tess_data.push_back(raw[i]);
|
|
}
|
|
|
|
GLvoid __stdcall tessCombine(GLdouble coords[3], void *vertex_data[4],
|
|
GLfloat weight[4], void **outData) {
|
|
SLog(EWarn, "Detected a self-intersecting polygon!");
|
|
}
|
|
|
|
GLvoid __stdcall tessEnd() {
|
|
}
|
|
|
|
GLvoid __stdcall tessError(GLenum error) {
|
|
SLog(EError, "The GLU tesselator generated an error: %s!", gluErrorString(error));
|
|
}
|
|
|
|
GLvoid __stdcall tessEdgeFlag(GLboolean) {
|
|
}
|
|
|
|
void GeometryConverter::convertCollada(const std::string &inputFile,
|
|
std::ostream &os,
|
|
const fs::path &textureDirectory,
|
|
const fs::path &meshesDirectory) {
|
|
DAE *dae = new DAE();
|
|
SLog(EInfo, "Loading \"%s\" ..", inputFile.c_str());
|
|
if (dae->load(inputFile.c_str()) != DAE_OK)
|
|
SLog(EError, "Could not load \"%s\"!", inputFile.c_str());
|
|
|
|
domCOLLADA *document = dae->getDom(inputFile.c_str());
|
|
domVisual_scene *visualScene = daeSafeCast<domVisual_scene>
|
|
(document->getDescendant("visual_scene"));
|
|
if (!visualScene)
|
|
SLog(EError, "Could not find a visual_scene!");
|
|
|
|
/* Configure the GLU tesselator */
|
|
tess = gluNewTess();
|
|
if (!tess)
|
|
SLog(EError, "Could not allocate a GLU tesselator!");
|
|
|
|
gluTessCallback(tess, GLU_TESS_VERTEX, reinterpret_cast<GLvoid (__stdcall *)()>(&tessVertex));
|
|
gluTessCallback(tess, GLU_TESS_BEGIN, reinterpret_cast<GLvoid (__stdcall *)()>(&tessBegin));
|
|
gluTessCallback(tess, GLU_TESS_END, reinterpret_cast<GLvoid (__stdcall *)()>(&tessEnd));
|
|
gluTessCallback(tess, GLU_TESS_ERROR, reinterpret_cast<GLvoid (__stdcall *)()>(&tessError));
|
|
gluTessCallback(tess, GLU_TESS_COMBINE, reinterpret_cast<GLvoid (__stdcall *)()>(&tessCombine));
|
|
gluTessCallback(tess, GLU_TESS_EDGE_FLAG, reinterpret_cast<GLvoid (__stdcall *)()>(&tessEdgeFlag));
|
|
|
|
domNode_Array &nodes = visualScene->getNode_array();
|
|
os << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << endl << endl;
|
|
os << "<!--" << endl << endl;
|
|
os << "\tAutomatically converted from COLLADA" << endl << endl;
|
|
os << "-->" << endl << endl;
|
|
os << "<scene>" << endl;
|
|
os << "\t<integrator id=\"integrator\" type=\"direct\"/>" << endl << endl;
|
|
|
|
StringMap idToTexture, fileToId;
|
|
domLibrary_images_Array &libraryImages = document->getLibrary_images_array();
|
|
for (size_t i=0; i<libraryImages.getCount(); ++i) {
|
|
domImage_Array &images = libraryImages[i]->getImage_array();
|
|
for (size_t j=0; j<images.getCount(); ++j)
|
|
loadImage(this, os, textureDirectory, *images.get(j), idToTexture, fileToId);
|
|
}
|
|
|
|
domLibrary_materials_Array &libraryMaterials = document->getLibrary_materials_array();
|
|
for (size_t i=0; i<libraryMaterials.getCount(); ++i) {
|
|
domMaterial_Array &materials = libraryMaterials[i]->getMaterial_array();
|
|
for (size_t j=0; j<materials.getCount(); ++j)
|
|
loadMaterial(this, os, *materials.get(j), idToTexture);
|
|
}
|
|
|
|
for (size_t i=0; i<nodes.getCount(); ++i)
|
|
loadNode(this, Transform(), os, *nodes[i], "", meshesDirectory);
|
|
|
|
os << "</scene>" << endl;
|
|
|
|
gluDeleteTess(tess);
|
|
delete dae;
|
|
}
|
|
|