1683 lines
61 KiB
C++
1683 lines
61 KiB
C++
/*
|
|
This file is part of Mitsuba, a physically based rendering system.
|
|
|
|
Copyright (c) 2007-2012 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/>.
|
|
*/
|
|
|
|
#include <mitsuba/mitsuba.h>
|
|
#include <mitsuba/render/trimesh.h>
|
|
#include <mitsuba/core/fresolver.h>
|
|
#include <mitsuba/core/fstream.h>
|
|
#include <mitsuba/core/timer.h>
|
|
#include <mitsuba/core/version.h>
|
|
#include <mitsuba/core/track.h>
|
|
#include <dom/domCOLLADA.h>
|
|
#include <dae.h>
|
|
#include <dae/daeErrorHandler.h>
|
|
#include <dom/domProfile_COMMON.h>
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <set>
|
|
|
|
#if defined(__OSX__)
|
|
#include <OpenGL/glu.h>
|
|
#else
|
|
#if defined(WIN32)
|
|
# include <windows.h>
|
|
#endif
|
|
#include <GL/glu.h>
|
|
#endif
|
|
|
|
#ifndef WIN32
|
|
#define __stdcall
|
|
#endif
|
|
|
|
#include "converter.h"
|
|
|
|
typedef std::map<std::string, std::string> StringMap;
|
|
typedef std::map<std::string, int> RefCountMap;
|
|
typedef std::multimap<std::string, AbstractAnimationTrack *> AnimationMap;
|
|
|
|
struct ColladaContext {
|
|
GeometryConverter *cvt;
|
|
RefCountMap refCountMap;
|
|
AnimationMap animations;
|
|
std::set<std::string> serializedGeometry;
|
|
fs::path texturesDirectory;
|
|
fs::path meshesDirectory;
|
|
StringMap idToTexture, fileToId;
|
|
std::ostream &os;
|
|
int trackIndex;
|
|
|
|
inline ColladaContext(std::ostream &os) : os(os) { }
|
|
};
|
|
|
|
enum ESourceType {
|
|
EPosition = 0,
|
|
ENormal = 1,
|
|
EUV = 2,
|
|
EVertexColor = 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 void operator+=(const Vec4 &v) {
|
|
x += v.x; y += v.y; z += v.z; w += v.w;
|
|
}
|
|
|
|
inline Vec4 operator*(Float f) const {
|
|
return Vec4(x * f, y * f, z * f, w * f);
|
|
}
|
|
|
|
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 Vector toVector() const {
|
|
return Vector(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;
|
|
std::vector<size_t> typeToCount;
|
|
|
|
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;
|
|
std::vector<domUint *> tess_cleanup;
|
|
VertexData *tess_vdata = NULL;
|
|
size_t tess_nSources;
|
|
|
|
VertexData *fetchVertexData(Transform transform,
|
|
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->typeToCount.resize(ELast);
|
|
result->typeToOffsetInStream.resize(ELast);
|
|
for (int i=0; i<ELast; ++i) {
|
|
result->typeToOffset[i] = result->typeToOffsetInStream[i] = -1;
|
|
result->typeToCount[i] = 0;
|
|
}
|
|
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 = (int) 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!");
|
|
unsigned int nParams = (unsigned int) accessor->getParam_array().getCount(),
|
|
stride = (unsigned int) accessor->getStride();
|
|
size_t size = (size_t) accessor->getCount();
|
|
SAssert(nParams <= 4);
|
|
|
|
Vec4 *target = new Vec4[size];
|
|
for (size_t j=0; j<size; ++j)
|
|
for (unsigned 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->typeToCount[EPosition] = size;
|
|
result->typeToOffsetInStream[EPosition] = offsetInStream;
|
|
result->glPos = new GLdouble[3*size];
|
|
for (size_t 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;
|
|
result->typeToCount[ENormal] = size;
|
|
} 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;
|
|
result->typeToCount[EUV] = size;
|
|
} else {
|
|
SLog(EWarn, "Found multiple sets of texture coordinates - ignoring!");
|
|
}
|
|
} else if (semantic == "COLOR") {
|
|
SAssert(accessor->getStride() == 3 || accessor->getStride() == 4);
|
|
SAssert(result->typeToOffset[EVertexColor] == -1);
|
|
result->hasNormals = true;
|
|
result->typeToOffset[EVertexColor] = offset;
|
|
result->typeToOffsetInStream[EVertexColor] = offsetInStream;
|
|
result->typeToCount[EVertexColor] = size;
|
|
} else {
|
|
SLog(EError, "Encountered an unknown source semantic: %s", semantic.c_str());
|
|
}
|
|
}
|
|
SAssert(result->typeToOffset[EPosition] != -1);
|
|
|
|
return result;
|
|
}
|
|
|
|
struct Vertex {
|
|
Point p;
|
|
Normal n;
|
|
Point2 uv;
|
|
Vector col;
|
|
};
|
|
|
|
/// 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.p.x < v2.p.x) return -1;
|
|
else if (v1.p.x > v2.p.x) return 1;
|
|
if (v1.p.y < v2.p.y) return -1;
|
|
else if (v1.p.y > v2.p.y) return 1;
|
|
if (v1.p.z < v2.p.z) return -1;
|
|
else if (v1.p.z > v2.p.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;
|
|
if (v1.col.x < v2.col.x) return -1;
|
|
else if (v1.col.x > v2.col.x) return 1;
|
|
if (v1.col.y < v2.col.y) return -1;
|
|
else if (v1.col.y > v2.col.y) return 1;
|
|
if (v1.col.z < v2.col.z) return -1;
|
|
else if (v1.col.z > v2.col.z) 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;
|
|
}
|
|
};
|
|
|
|
class CustomErrorHandler : public daeErrorHandler {
|
|
public:
|
|
void handleError(daeString msg) {
|
|
SLog(EWarn, "Critical COLLADA error: %s", msg);
|
|
}
|
|
|
|
void handleWarning(daeString msg) {
|
|
SLog(EWarn, "COLLADA warning: %s", msg);
|
|
}
|
|
};
|
|
|
|
typedef std::map<SimpleTriangle, bool, triangle_key_order> TriangleMap;
|
|
|
|
static inline Float fromSRGBComponent(Float value) {
|
|
if (value <= (Float) 0.04045)
|
|
return value / (Float) 12.92;
|
|
return std::pow((value + (Float) 0.055)
|
|
/ (Float) (1.0 + 0.055), (Float) 2.4);
|
|
}
|
|
|
|
void writeGeometry(ColladaContext &ctx, const std::string &prefixName, std::string id,
|
|
int geomIndex, std::string matID, Transform transform, VertexData *vData,
|
|
TriangleMap &triMap, bool exportShapeGroup) {
|
|
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;
|
|
|
|
for (size_t i=0; i<tess_cleanup.size(); ++i)
|
|
delete[] tess_cleanup[i];
|
|
tess_cleanup.clear();
|
|
|
|
for (size_t i=0; i<tess_data.size(); i+=tess_nSources) {
|
|
Vertex vertex;
|
|
domUint posRef = tess_data[i+vData->typeToOffsetInStream[EPosition]];
|
|
vertex.p = 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();
|
|
} else {
|
|
vertex.n = Normal(0.0f);
|
|
}
|
|
|
|
if (vData->typeToOffset[EVertexColor] != -1) {
|
|
domUint colorRef = tess_data[i+vData->typeToOffsetInStream[EVertexColor]];
|
|
vertex.col = vData->data[vData->typeToOffset[EVertexColor]][colorRef].toVector();
|
|
} else {
|
|
vertex.col = Normal(0.0f);
|
|
}
|
|
|
|
if (vData->typeToOffset[EUV] != -1) {
|
|
domUint uvRef = tess_data[i+vData->typeToOffsetInStream[EUV]];
|
|
vertex.uv = vData->data[vData->typeToOffset[EUV]][uvRef].toPoint2();
|
|
vertex.uv.y = 1-vertex.uv.y; // Invert the V coordinate
|
|
} else {
|
|
vertex.uv = Point2(0.0f);
|
|
}
|
|
|
|
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]].p,
|
|
p1 = vertexBuffer[triangle.idx[1]].p,
|
|
p2 = vertexBuffer[triangle.idx[2]].p;
|
|
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/%s\": Only contains duplicates (%i triangles) of already-existing geometry. Ignoring.",
|
|
prefixName.c_str(), id.c_str(), duplicates);
|
|
ctx.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(EDebug, "\"%s/%s\": Converted " SIZE_T_FMT " triangles, " SIZE_T_FMT
|
|
" vertices (merged " SIZE_T_FMT " vertices).", prefixName.c_str(), id.c_str(),
|
|
triangles.size(), vertexBuffer.size(), numMerged);
|
|
|
|
ref<TriMesh> mesh = new TriMesh(prefixName + "/" + id,
|
|
triangles.size(), vertexBuffer.size(),
|
|
vData->typeToOffset[ENormal] != -1,
|
|
vData->typeToOffset[EUV] != -1,
|
|
vData->typeToOffset[EVertexColor] != -1);
|
|
|
|
std::copy(triangles.begin(), triangles.end(), mesh->getTriangles());
|
|
|
|
Point *target_positions = mesh->getVertexPositions();
|
|
Normal *target_normals = mesh->getVertexNormals();
|
|
Point2 *target_texcoords = mesh->getVertexTexcoords();
|
|
Color3 *target_colors = mesh->getVertexColors();
|
|
|
|
for (size_t i=0; i<vertexBuffer.size(); ++i) {
|
|
*target_positions++ = vertexBuffer[i].p;
|
|
if (target_normals)
|
|
*target_normals ++ = vertexBuffer[i].n;
|
|
if (target_texcoords)
|
|
*target_texcoords++ = vertexBuffer[i].uv;
|
|
if (target_colors) {
|
|
Float r = vertexBuffer[i].col.x;
|
|
Float g = vertexBuffer[i].col.y;
|
|
Float b = vertexBuffer[i].col.z;
|
|
if (!ctx.cvt->m_srgb)
|
|
*target_colors++ = Color3(r, g, b);
|
|
else
|
|
*target_colors++ = Color3(
|
|
fromSRGBComponent(r),
|
|
fromSRGBComponent(g),
|
|
fromSRGBComponent(b)
|
|
);
|
|
}
|
|
}
|
|
|
|
id += formatString("_%i", geomIndex);
|
|
std::string filename;
|
|
|
|
if (!ctx.cvt->m_geometryFile) {
|
|
filename = id + std::string(".serialized");
|
|
ref<FileStream> stream = new FileStream(ctx.meshesDirectory / filename, FileStream::ETruncReadWrite);
|
|
stream->setByteOrder(Stream::ELittleEndian);
|
|
mesh->serialize(stream);
|
|
stream->close();
|
|
filename = "meshes/" + filename;
|
|
} else {
|
|
ctx.cvt->m_geometryDict.push_back((uint32_t) ctx.cvt->m_geometryFile->getPos());
|
|
mesh->serialize(ctx.cvt->m_geometryFile);
|
|
filename = ctx.cvt->m_geometryFileName.filename().string();
|
|
}
|
|
|
|
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();
|
|
|
|
if (!exportShapeGroup) {
|
|
ctx.os << "\t<shape id=\"" << id << "\" type=\"serialized\">" << endl;
|
|
ctx.os << "\t\t<string name=\"filename\" value=\"" << filename << "\"/>" << endl;
|
|
if (ctx.cvt->m_geometryFile)
|
|
ctx.os << "\t\t<integer name=\"shapeIndex\" value=\"" << (ctx.cvt->m_geometryDict.size() - 1) << "\"/>" << endl;
|
|
if (!transform.isIdentity()) {
|
|
ctx.os << "\t\t<transform name=\"toWorld\">" << endl;
|
|
ctx.os << "\t\t\t<matrix value=\"" << matrixValues.substr(0, matrixValues.length()-1) << "\"/>" << endl;
|
|
ctx.os << "\t\t</transform>" << endl;
|
|
}
|
|
if (matID != "")
|
|
ctx.os << "\t\t<ref name=\"bsdf\" id=\"" << matID << "\"/>" << endl;
|
|
ctx.os << "\t</shape>" << endl << endl;
|
|
} else {
|
|
ctx.os << "\t\t<shape type=\"serialized\">" << endl;
|
|
ctx.os << "\t\t\t<string name=\"filename\" value=\"" << filename << "\"/>" << endl;
|
|
if (ctx.cvt->m_geometryFile)
|
|
ctx.os << "\t\t\t<integer name=\"shapeIndex\" value=\"" << (ctx.cvt->m_geometryDict.size() - 1)<< "\"/>" << endl;
|
|
if (matID != "")
|
|
ctx.os << "\t\t\t<ref name=\"bsdf\" id=\"" << matID << "\"/>" << endl;
|
|
ctx.os << "\t\t</shape>" << endl << endl;
|
|
}
|
|
}
|
|
|
|
void exportAnimation(ColladaContext &ctx, const fs::path &path, const std::string &name) {
|
|
AnimationMap::iterator start = ctx.animations.lower_bound(name);
|
|
AnimationMap::iterator end = ctx.animations.upper_bound(name);
|
|
|
|
ref<AnimatedTransform> trafo = new AnimatedTransform();
|
|
|
|
for (AnimationMap::iterator it = start; it != end; ++it) {
|
|
AbstractAnimationTrack *track = it->second;
|
|
int type = track->getType();
|
|
if (type == FloatTrack::ERotationX
|
|
|| type == FloatTrack::ERotationY
|
|
|| type == FloatTrack::ERotationZ)
|
|
continue;
|
|
trafo->addTrack(track);
|
|
}
|
|
SLog(EDebug, "Writing animation track \"%s\"", path.filename().string().c_str());
|
|
ref<FileStream> fs = new FileStream(path, FileStream::ETruncReadWrite);
|
|
trafo->serialize(fs);
|
|
}
|
|
|
|
void loadGeometry(ColladaContext &ctx, const std::string &instanceName,
|
|
std::string prefixName, Transform transform, domGeometry &geom,
|
|
StringMap &matLookupTable) {
|
|
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(EDebug, "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>)!");
|
|
|
|
bool exportShapeGroup = false;
|
|
if (ctx.serializedGeometry.find(identifier) != ctx.serializedGeometry.end()) {
|
|
if (ctx.animations.find(instanceName) != ctx.animations.end()) {
|
|
ctx.os << "\t<shape id=\"" << prefixName << "/" << identifier << "\" type=\"animatedinstance\">" << endl;
|
|
std::string filename = formatString("animation_%i.serialized", ctx.trackIndex++);
|
|
ctx.os << "\t\t<string name=\"filename\" value=\"" << filename << "\"/>" << endl;
|
|
exportAnimation(ctx, ctx.cvt->m_outputDirectory / filename, instanceName);
|
|
} else {
|
|
ctx.os << "\t<shape id=\"" << prefixName << "/" << identifier << "\" type=\"instance\">" << endl;
|
|
}
|
|
if (!transform.isIdentity()) {
|
|
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();
|
|
ctx.os << "\t\t<transform name=\"toWorld\">" << endl;
|
|
ctx.os << "\t\t\t<matrix value=\"" << matrixValues.substr(0, matrixValues.length()-1) << "\"/>" << endl;
|
|
ctx.os << "\t\t</transform>" << endl;
|
|
}
|
|
ctx.os << "\t\t<ref id=\"" << identifier << "\"/>" << endl;
|
|
ctx.os << "\t</shape>" << endl << endl;
|
|
return;
|
|
} else if (ctx.refCountMap[identifier] > 1 || ctx.refCountMap[instanceName]) {
|
|
ctx.os << "\t<shape id=\"" << identifier << "\" type=\"shapegroup\">" << endl;
|
|
exportShapeGroup = true;
|
|
}
|
|
ctx.serializedGeometry.insert(identifier);
|
|
|
|
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, 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 diffuse BRDF.");
|
|
else
|
|
matID = matLookupTable[triangles->getMaterial()];
|
|
writeGeometry(ctx, prefixName, identifier, geomIndex, matID, transform, data, triMap, exportShapeGroup);
|
|
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, 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()];
|
|
bool invalid = false;
|
|
|
|
for (int m=0; m<ELast; ++m) {
|
|
int offset = data->typeToOffsetInStream[m];
|
|
if (offset == -1)
|
|
continue;
|
|
size_t count = data->typeToCount[m];
|
|
for (size_t l = 0; l < indices.getCount(); l+=data->nSources) {
|
|
domUint idx = indices.get(l+offset);
|
|
if (idx >= count) {
|
|
SLog(EWarn, "Encountered the invalid polygon index %i "
|
|
"(must be in [0, " SIZE_T_FMT "]) -- ignoring polygon!", (domInt) idx, count-1);
|
|
invalid = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (invalid)
|
|
continue;
|
|
|
|
for (size_t l = 0; l<indices.getCount(); ++l)
|
|
temp[l] = indices.get(l);
|
|
|
|
tess_vdata = data;
|
|
gluTessBeginPolygon(tess, NULL);
|
|
gluTessBeginContour(tess);
|
|
|
|
for (size_t k=0; k<indices.getCount(); k+=data->nSources)
|
|
gluTessVertex(tess, &data->glPos[3*temp[k+posOffset]], (GLvoid *) (temp+k));
|
|
|
|
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 diffuse BRDF.");
|
|
else
|
|
matID = matLookupTable[polygons->getMaterial()];
|
|
|
|
writeGeometry(ctx, prefixName, identifier, geomIndex, matID, transform, data, triMap, exportShapeGroup);
|
|
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, 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];
|
|
|
|
bool invalid = false;
|
|
|
|
for (int m=0; m<ELast; ++m) {
|
|
int offset = data->typeToOffsetInStream[m];
|
|
if (offset == -1)
|
|
continue;
|
|
size_t count = data->typeToCount[m];
|
|
for (size_t l = 0; l < vertexCount; ++l) {
|
|
domUint idx = indexArray.get(l*data->nSources+offset);
|
|
if (idx >= count) {
|
|
SLog(EWarn, "Encountered the invalid polygon index %i "
|
|
"(must be in [0, " SIZE_T_FMT "]) -- ignoring polygon!", (domInt) idx, count-1);
|
|
invalid = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (invalid)
|
|
continue;
|
|
|
|
for (size_t l = 0; l<vertexCount * data->nSources; ++l)
|
|
temp[l] = indexArray.get(indexOffset++);
|
|
|
|
tess_vdata = data;
|
|
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 \"%s\" could not be found, substituting a diffuse BRDF.", polylist->getMaterial());
|
|
else
|
|
matID = matLookupTable[polylist->getMaterial()];
|
|
|
|
writeGeometry(ctx, prefixName, identifier, geomIndex, matID, transform, data, triMap, exportShapeGroup);
|
|
delete data;
|
|
++geomIndex;
|
|
}
|
|
if (exportShapeGroup) {
|
|
ctx.os << "\t</shape>" << endl << endl;
|
|
if (ctx.animations.find(instanceName) != ctx.animations.end()) {
|
|
ctx.os << "\t<shape id=\"" << prefixName << "/" << identifier << "\" type=\"animatedinstance\">" << endl;
|
|
std::string filename = formatString("animation_%i.serialized", ctx.trackIndex++);
|
|
ctx.os << "\t\t<string name=\"filename\" value=\"" << filename << "\"/>" << endl;
|
|
exportAnimation(ctx, ctx.cvt->m_outputDirectory / filename, instanceName);
|
|
} else {
|
|
ctx.os << "\t<shape id=\"" << prefixName << "/" << identifier << "\" type=\"instance\">" << endl;
|
|
if (!transform.isIdentity()) {
|
|
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();
|
|
ctx.os << "\t\t<transform name=\"toWorld\">" << endl;
|
|
ctx.os << "\t\t\t<matrix value=\"" << matrixValues.substr(0, matrixValues.length()-1) << "\"/>" << endl;
|
|
ctx.os << "\t\t</transform>" << endl;
|
|
}
|
|
}
|
|
ctx.os << "\t\t<ref id=\"" << identifier << "\"/>" << endl;
|
|
ctx.os << "\t</shape>" << endl << endl;
|
|
}
|
|
}
|
|
|
|
void loadMaterialParam(ColladaContext &ctx, const std::string &name,
|
|
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 (ctx.cvt->m_srgb)
|
|
ctx.os << "\t\t<srgb name=\"" << name << "\" value=\"";
|
|
else
|
|
ctx.os << "\t\t<rgb name=\"" << name << "\" value=\"";
|
|
ctx.os << colValue.get(0) << " " << colValue.get(1) << " "
|
|
<< colValue.get(2) << "\"/>" << endl;
|
|
} else if (texture && handleRefs) {
|
|
if (ctx.idToTexture.find(texture->getTexture()) == ctx.idToTexture.end()) {
|
|
SLog(EError, "Could not find referenced texture \"%s\"", texture->getTexture());
|
|
} else {
|
|
ctx.os << "\t\t<ref name=\"" << name << "\" id=\""
|
|
<< ctx.idToTexture[texture->getTexture()] << "\"/>" << endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
void loadMaterialParam(ColladaContext &ctx, const std::string &name,
|
|
domCommon_float_or_param_type *value, bool handleRef) {
|
|
if (!value)
|
|
return;
|
|
domCommon_float_or_param_type::domFloat *floatValue = value->getFloat();
|
|
if (!handleRef && floatValue) {
|
|
ctx.os << "\t\t<float name=\"" << name << "\" value=\""
|
|
<< floatValue->getValue() << "\"/>" << endl;
|
|
}
|
|
}
|
|
|
|
void loadMaterial(ColladaContext &ctx, domMaterial &mat) {
|
|
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++);
|
|
}
|
|
}
|
|
|
|
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 (ctx.idToTexture.find(id) == ctx.idToTexture.end())
|
|
SLog(EError, "Referenced bitmap '%s' not found!", id.c_str());
|
|
ctx.idToTexture[newParam->getSid()] = ctx.idToTexture[id];
|
|
}
|
|
|
|
if (sampler2D) {
|
|
std::string id = sampler2D->getSource()->getValue();
|
|
if (ctx.idToTexture.find(id) == ctx.idToTexture.end())
|
|
SLog(EError, "Referenced surface '%s' not found!", id.c_str());
|
|
ctx.idToTexture[newParam->getSid()] = ctx.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 && 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) {
|
|
ctx.os << "\t<bsdf id=\"" << identifier << "\" type=\"diffuse\">" << endl;
|
|
loadMaterialParam(ctx, "reflectance", diffuse, false);
|
|
loadMaterialParam(ctx, "reflectance", diffuse, true);
|
|
ctx.os << "\t</bsdf>" << endl << endl;
|
|
} else {
|
|
ctx.os << "\t<bsdf id=\"" << identifier << "\" type=\"phong\">" << endl;
|
|
loadMaterialParam(ctx, "diffuseReflectance", diffuse, false);
|
|
loadMaterialParam(ctx, "specularReflectance", specular, false);
|
|
loadMaterialParam(ctx, "exponent", shininess, false);
|
|
loadMaterialParam(ctx, "diffuseReflectance", diffuse, true);
|
|
loadMaterialParam(ctx, "specularReflectance", specular, true);
|
|
loadMaterialParam(ctx, "exponent", shininess, true);
|
|
ctx.os << "\t</bsdf>" << endl << endl;
|
|
}
|
|
} else if (lambert) {
|
|
domCommon_color_or_texture_type* diffuse = lambert->getDiffuse();
|
|
ctx.os << "\t<bsdf id=\"" << identifier << "\" type=\"diffuse\">" << endl;
|
|
loadMaterialParam(ctx, "reflectance", diffuse, false);
|
|
loadMaterialParam(ctx, "reflectance", diffuse, true);
|
|
ctx.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 && 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) {
|
|
ctx.os << "\t<bsdf id=\"" << identifier << "\" type=\"diffuse\">" << endl;
|
|
loadMaterialParam(ctx, "reflectance", diffuse, false);
|
|
loadMaterialParam(ctx, "reflectance", diffuse, true);
|
|
ctx.os << "\t</bsdf>" << endl << endl;
|
|
} else {
|
|
ctx.os << "\t<bsdf id=\"" << identifier << "\" type=\"blinn\">" << endl;
|
|
ctx.os << "\t\t<float name=\"specularReflectance\" value=\"1\"/>" << endl;
|
|
ctx.os << "\t\t<float name=\"diffuseReflectance\" value=\"1\"/>" << endl;
|
|
loadMaterialParam(ctx, "diffuseReflectance", diffuse, false);
|
|
loadMaterialParam(ctx, "specularReflectance", specular, false);
|
|
loadMaterialParam(ctx, "exponent", shininess, false);
|
|
loadMaterialParam(ctx, "diffuseReflectance", diffuse, true);
|
|
loadMaterialParam(ctx, "specularReflectance", specular, true);
|
|
loadMaterialParam(ctx, "exponent", shininess, true);
|
|
ctx.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) {
|
|
ctx.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());
|
|
ctx.os << "\t<bsdf id=\"" << identifier << "\" type=\"diffuse\"/>" << endl << endl;
|
|
}
|
|
} else {
|
|
SLog(EError, "Material type not supported! (must be Lambertian/Phong/Blinn/Constant)");
|
|
}
|
|
}
|
|
|
|
void loadLight(ColladaContext &ctx, Transform transform, 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(EDebug, "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();
|
|
ctx.os << "\t<emitter id=\"" << identifier << "\" type=\"point\">" << endl;
|
|
ctx.os << "\t\t<rgb name=\"intensity\" value=\"" << color[0]*intensity << " " << color[1]*intensity << " " << color[2]*intensity << "\"/>" << endl << endl;
|
|
ctx.os << "\t\t<transform name=\"toWorld\">" << endl;
|
|
ctx.os << "\t\t\t<translate x=\"" << pos.x << "\" y=\"" << pos.y << "\" z=\"" << pos.z << "\"/>" << endl;
|
|
ctx.os << "\t\t</transform>" << endl;
|
|
ctx.os << "\t</emitter>" << endl << endl;
|
|
}
|
|
|
|
domLight::domTechnique_common::domDirectional *directional = light.getTechnique_common()->getDirectional().cast();
|
|
if (directional) {
|
|
domFloat3 &color = directional->getColor()->getValue();
|
|
ctx.os << "\t<emitter id=\"" << identifier << "\" type=\"directional\">" << endl;
|
|
ctx.os << "\t\t<rgb name=\"irradiance\" value=\"" << color[0]*intensity << " " << color[1]*intensity << " " << color[2]*intensity << "\"/>" << endl << endl;
|
|
ctx.os << "\t\t<transform name=\"toWorld\">" << endl;
|
|
ctx.os << "\t\t\t<lookat origin=\"" << pos.x << ", " << pos.y << ", " << pos.z << "\" target=\"" << target.x << ", " << target.y << ", " << target.z << "\"/>" << endl;
|
|
ctx.os << "\t\t</transform>" << endl << endl;
|
|
ctx.os << "\t</emitter>" << 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();
|
|
ctx.os << "\t<emitter id=\"" << identifier << "\" type=\"spot\">" << endl;
|
|
ctx.os << "\t\t<rgb name=\"intensity\" value=\"" << color[0]*intensity << " " << color[1]*intensity << " " << color[2]*intensity << "\"/>" << endl;
|
|
ctx.os << "\t\t<float name=\"cutoffAngle\" value=\"" << falloffAngle/2 << "\"/>" << endl << endl;
|
|
ctx.os << "\t\t<transform name=\"toWorld\">" << endl;
|
|
ctx.os << "\t\t\t<lookat origin=\"" << pos.x << ", " << pos.y << ", " << pos.z << "\" target=\"" << target.x << ", " << target.y << ", " << target.z << "\"/>" << endl;
|
|
ctx.os << "\t\t</transform>" << endl;
|
|
ctx.os << "\t</emitter>" << endl << endl;
|
|
}
|
|
domLight::domTechnique_common::domAmbient *ambient = light.getTechnique_common()->getAmbient().cast();
|
|
if (ambient) {
|
|
domFloat3 &color = ambient->getColor()->getValue();
|
|
ctx.os << "\t<emitter id=\"" << identifier << "\" type=\"constant\">" << endl;
|
|
ctx.os << "\t\t<rgb name=\"radiance\" value=\"" << color[0]*intensity << " " << color[1]*intensity << " " << color[2]*intensity << "\"/>" << endl;
|
|
ctx.os << "\t</emitter>" << endl << endl;
|
|
}
|
|
if (!point && !spot && !ambient && !directional)
|
|
SLog(EWarn, "Encountered an unknown light type!");
|
|
}
|
|
|
|
void loadImage(ColladaContext &ctx, domImage &image) {
|
|
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(EDebug, "Converting texture \"%s\" ..", identifier.c_str());
|
|
|
|
std::string uri = image.getInit_from()->getValue().str();
|
|
std::string filename = cdom::uriToFilePath(uri);
|
|
if (filename.empty()) /* When uriToFilePath fails, try to use the path as is */
|
|
filename = uri;
|
|
|
|
/* Prevent Linux/OSX fs::path handling issues for DAE files created on Windows */
|
|
for (size_t i=0; i<filename.length(); ++i) {
|
|
if (filename[i] == '\\')
|
|
filename[i] = '/';
|
|
}
|
|
if (ctx.fileToId.find(filename) != ctx.fileToId.end()) {
|
|
ctx.idToTexture[identifier] = ctx.fileToId[filename];
|
|
return;
|
|
}
|
|
|
|
ctx.idToTexture[identifier] = identifier;
|
|
ctx.fileToId[filename] = identifier;
|
|
|
|
fs::path path = fs::path(filename);
|
|
fs::path targetPath = ctx.texturesDirectory / path.filename();
|
|
|
|
std::string extension = boost::to_lower_copy(path.extension().string());
|
|
if (extension == ".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)) {
|
|
if (!fs::exists(path)) {
|
|
ref<FileResolver> fRes = Thread::getThread()->getFileResolver();
|
|
path = fRes->resolve(path.filename());
|
|
|
|
if (!fs::exists(path)) {
|
|
SLog(EWarn, "Found neither \"%s\" nor \"%s\"!", filename.c_str(), path.string().c_str());
|
|
path = ctx.cvt->locateResource(path.filename());
|
|
targetPath = targetPath.parent_path() / path.filename();
|
|
if (path.empty())
|
|
SLog(EError, "Unable to locate a resource -- aborting conversion.");
|
|
else
|
|
fRes->appendPath(path.parent_path());
|
|
}
|
|
}
|
|
if (fs::absolute(path) != fs::absolute(targetPath)) {
|
|
ref<FileStream> input = new FileStream(path, FileStream::EReadOnly);
|
|
ref<FileStream> output = new FileStream(targetPath, FileStream::ETruncReadWrite);
|
|
input->copyTo(output);
|
|
input->close();
|
|
output->close();
|
|
}
|
|
}
|
|
|
|
ctx.os << "\t<texture id=\"" << identifier << "\" type=\"bitmap\">" << endl;
|
|
ctx.os << "\t\t<string name=\"filename\" value=\"textures/" << targetPath.filename().string() << "\"/>" << endl;
|
|
ctx.os << "\t</texture>" << endl << endl;
|
|
}
|
|
|
|
void loadCamera(ColladaContext &ctx, Transform transform, 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(EDebug, "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 (ctx.cvt->m_xres != -1) {
|
|
xres = ctx.cvt->m_xres;
|
|
aspect = (Float) ctx.cvt->m_xres / (Float) ctx.cvt->m_yres;
|
|
}
|
|
ctx.os << "\t<sensor id=\"" << identifier << "\" type=\"orthographic\">" << endl;
|
|
}
|
|
|
|
domCamera::domOptics::domTechnique_common::domPerspective* persp = camera.getOptics()->
|
|
getTechnique_common()->getPerspective().cast();
|
|
if (persp) {
|
|
if (ctx.cvt->m_xres != -1) {
|
|
xres = ctx.cvt->m_xres;
|
|
aspect = (Float) ctx.cvt->m_xres / (Float) ctx.cvt->m_yres;
|
|
} else {
|
|
if (persp->getAspect_ratio().cast() != 0)
|
|
aspect = (Float) persp->getAspect_ratio()->getValue();
|
|
}
|
|
ctx.os << "\t<sensor id=\"" << identifier << "\" type=\"perspective\">" << endl;
|
|
if (persp->getXfov().cast()) {
|
|
Float xFov = (Float) persp->getXfov()->getValue();
|
|
Float yFov = radToDeg(2 * std::atan(std::tan(degToRad(xFov)/2) / aspect));
|
|
if (aspect <= 1.0f)
|
|
ctx.os << "\t\t<float name=\"fov\" value=\"" << xFov << "\"/>" << endl;
|
|
else
|
|
ctx.os << "\t\t<float name=\"fov\" value=\"" << yFov << "\"/>" << endl;
|
|
} else if (persp->getYfov().cast()) {
|
|
Float yFov = (Float) persp->getYfov()->getValue();
|
|
Float xFov = radToDeg(2 * std::atan(std::tan(degToRad(yFov)/2) * aspect));
|
|
if (aspect > 1.0f)
|
|
ctx.os << "\t\t<float name=\"fov\" value=\"" << yFov << "\"/>" << endl;
|
|
else
|
|
ctx.os << "\t\t<float name=\"fov\" value=\"" << xFov << "\"/>" << endl;
|
|
}
|
|
ctx.os << "\t\t<float name=\"nearClip\" value=\"" << persp->getZnear()->getValue() << "\"/>" << endl;
|
|
ctx.os << "\t\t<float name=\"farClip\" value=\"" << persp->getZfar()->getValue() << "\"/>" << endl;
|
|
ctx.os << "\t\t<string name=\"fovAxis\" value=\"" << (ctx.cvt->m_mapSmallerSide ? "smaller" : "larger") << "\"/>" << endl;
|
|
}
|
|
|
|
ctx.os << endl;
|
|
ctx.os << "\t\t<transform name=\"toWorld\">" << endl;
|
|
ctx.os << "\t\t\t<matrix value=\"" << matrixValues.substr(0, matrixValues.length()-1) << "\"/>" << endl;
|
|
ctx.os << "\t\t</transform>" << endl << endl;
|
|
ctx.os << "\t\t<sampler id=\"" << identifier << "_sampler\" type=\"ldsampler\">" << endl;
|
|
ctx.os << "\t\t\t<integer name=\"sampleCount\" value=\"4\"/>" << endl;
|
|
ctx.os << "\t\t</sampler>" << endl << endl;
|
|
ctx.os << "\t\t<film id=\"" << identifier << "_film\" type=\"" << ctx.cvt->m_filmType << "\">" << endl;
|
|
ctx.os << "\t\t\t<integer name=\"width\" value=\"" << xres << "\"/>" << endl;
|
|
ctx.os << "\t\t\t<integer name=\"height\" value=\"" << (int) (xres/aspect) << "\"/>" << endl;
|
|
ctx.os << "\t\t\t<rfilter type=\"gaussian\"/>" << endl;
|
|
ctx.os << "\t\t</film>" << endl;
|
|
ctx.os << "\t</sensor>" << endl << endl;
|
|
}
|
|
|
|
void loadNode(ColladaContext &ctx, Transform transform, domNode &node, std::string prefixName) {
|
|
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);
|
|
}
|
|
}
|
|
prefixName = prefixName + std::string("/") + identifier;
|
|
SLog(EDebug, "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 and 3ds max export 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();
|
|
Vector axis((Float) value.get(0), (Float) value.get(1), (Float) value.get(2));
|
|
Float angle = (Float) value.get(3);
|
|
if (angle != 0) {
|
|
if (axis.isZero()) {
|
|
SLog(EWarn, "Encountered a rotation around a zero vector -- ignoring!");
|
|
} else {
|
|
transform = transform *
|
|
Transform::rotate(axis, (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();
|
|
Matrix4x4 matrix(
|
|
(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(ctx, identifier, prefixName, transform, *geom, matLookupTable);
|
|
}
|
|
|
|
/* 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(ctx, transform, *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(ctx, transform, *camera);
|
|
}
|
|
|
|
/* Recursively iterate through sub-nodes */
|
|
domNode_Array &nodes = node.getNode_array();
|
|
for (size_t i=0; i<nodes.getCount(); ++i)
|
|
loadNode(ctx, transform, *nodes[i], prefixName);
|
|
|
|
/* 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(ctx, transform, *node, prefixName + std::string("/") + identifier);
|
|
}
|
|
}
|
|
|
|
void computeRefCounts(ColladaContext &ctx, domNode &node) {
|
|
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());
|
|
if (geom->getId() != NULL) {
|
|
if (ctx.refCountMap.find(geom->getId()) == ctx.refCountMap.end())
|
|
ctx.refCountMap[geom->getId()] = 1;
|
|
else
|
|
ctx.refCountMap[geom->getId()]++;
|
|
}
|
|
}
|
|
|
|
/* Recursively iterate through sub-nodes */
|
|
domNode_Array &nodes = node.getNode_array();
|
|
for (size_t i=0; i<nodes.getCount(); ++i)
|
|
computeRefCounts(ctx, *nodes[i]);
|
|
|
|
/* 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());
|
|
computeRefCounts(ctx, *node);
|
|
}
|
|
}
|
|
|
|
void loadAnimation(ColladaContext &ctx, domAnimation &anim) {
|
|
std::string identifier;
|
|
if (anim.getId() != NULL) {
|
|
identifier = anim.getId();
|
|
} else {
|
|
if (anim.getName() != NULL) {
|
|
identifier = anim.getName();
|
|
} else {
|
|
static int unnamedCtr = 0;
|
|
identifier = formatString("unnamedAnimation_%i", unnamedCtr);
|
|
}
|
|
}
|
|
SLog(EDebug, "Loading animation \"%s\" ..", identifier.c_str());
|
|
|
|
domChannel_Array &channels = anim.getChannel_array();
|
|
for (size_t i=0; i<channels.getCount(); ++i) {
|
|
domChannel *channel = channels[i];
|
|
std::vector<std::string> target = tokenize(channel->getTarget(), "./");
|
|
SAssert(target.size() >= 2);
|
|
if (ctx.refCountMap.find(target[0]) == ctx.refCountMap.end())
|
|
ctx.refCountMap[target[0]] = 1;
|
|
else
|
|
ctx.refCountMap[target[0]]++;
|
|
|
|
daeURI &sourceRef = channel->getSource();
|
|
sourceRef.resolveElement();
|
|
domSampler *sampler = daeSafeCast<domSampler>(sourceRef.getElement());
|
|
if (!sampler)
|
|
SLog(EError, "Referenced animation sampler not found!");
|
|
const domInputLocal_Array &inputs = sampler->getInput_array();
|
|
AbstractAnimationTrack *track = NULL;
|
|
AbstractAnimationTrack::EType trackType = AbstractAnimationTrack::EInvalid;
|
|
boost::to_lower(target[1]);
|
|
if (target.size() > 2)
|
|
boost::to_lower(target[2]);
|
|
if (target[1] == "location" || target[1] == "translate") {
|
|
if (target.size() == 2) {
|
|
trackType = VectorTrack::ETranslationXYZ;
|
|
} else if (target[2] == "x") {
|
|
trackType = FloatTrack::ETranslationX;
|
|
} else if (target[2] == "y") {
|
|
trackType = FloatTrack::ETranslationY;
|
|
} else if (target[2] == "z") {
|
|
trackType = FloatTrack::ETranslationZ;
|
|
}
|
|
} else if (target[1] == "scale") {
|
|
if (target.size() == 2) {
|
|
trackType = VectorTrack::EScaleXYZ;
|
|
} else if (target[2] == "x") {
|
|
trackType = FloatTrack::EScaleX;
|
|
} else if (target[2] == "y") {
|
|
trackType = FloatTrack::EScaleY;
|
|
} else if (target[2] == "z") {
|
|
trackType = FloatTrack::EScaleZ;
|
|
}
|
|
} else if ((target[1] == "rotationx" || target[1] == "rotatex") && target.size() == 3 && target[2] == "angle") {
|
|
trackType = FloatTrack::ERotationX;
|
|
} else if ((target[1] == "rotationy" || target[1] == "rotatey") && target.size() == 3 && target[2] == "angle") {
|
|
trackType = FloatTrack::ERotationY;
|
|
} else if ((target[1] == "rotationz" || target[1] == "rotatez") && target.size() == 3 && target[2] == "angle") {
|
|
trackType = FloatTrack::ERotationZ;
|
|
}
|
|
|
|
if (trackType == FloatTrack::EInvalid) {
|
|
SLog(EWarn, "Skipping unsupported animation track of type %s.%s",
|
|
target[1].c_str(), target.size() > 2 ? target[2].c_str() : "");
|
|
continue;
|
|
}
|
|
|
|
for (size_t j=0; j<inputs.getCount(); ++j) {
|
|
sourceRef = inputs[j]->getSource();
|
|
sourceRef.resolveElement();
|
|
domSource *source = daeSafeCast<domSource>(sourceRef.getElement());
|
|
if (!source)
|
|
SLog(EError, "Referenced animation source not found!");
|
|
std::string semantic = inputs[j]->getSemantic();
|
|
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!");
|
|
unsigned int stride = (unsigned int) accessor->getStride();
|
|
size_t size = (size_t) accessor->getCount();
|
|
|
|
if (!track) {
|
|
if (trackType == VectorTrack::EScaleXYZ || trackType == VectorTrack::ETranslationXYZ)
|
|
track = new VectorTrack(trackType, size);
|
|
else
|
|
track = new FloatTrack(trackType, size);
|
|
track->incRef();
|
|
} else {
|
|
SAssert(track->getSize() == size);
|
|
}
|
|
|
|
if (semantic == "INPUT") {
|
|
domListOfFloats &floatArray = source->getFloat_array()->getValue();
|
|
SAssert(stride == 1);
|
|
for (size_t i=0; i<size; ++i)
|
|
track->setTime(i, (Float) floatArray[i]);
|
|
} else if (semantic == "OUTPUT") {
|
|
domListOfFloats &floatArray = source->getFloat_array()->getValue();
|
|
if (trackType == VectorTrack::ETranslationXYZ || trackType == VectorTrack::EScaleXYZ) {
|
|
SAssert(stride == 3);
|
|
for (size_t i=0; i<size; ++i)
|
|
((VectorTrack *) track)->setValue(i,
|
|
Vector((Float) floatArray[i*3+0], (Float) floatArray[i*3+1],
|
|
(Float) floatArray[i*3+2]));
|
|
} else {
|
|
SAssert(stride == 1);
|
|
for (size_t i=0; i<size; ++i)
|
|
((FloatTrack *) track)->setValue(i, (Float) floatArray[i]);
|
|
}
|
|
} else if (semantic == "INTERPOLATION") {
|
|
/// Ignored for now
|
|
} else {
|
|
SLog(EWarn, "Encountered an unsupported semantic: \"%s\"", semantic.c_str());
|
|
}
|
|
}
|
|
|
|
if (track)
|
|
ctx.animations.insert(AnimationMap::value_type(target[0], track));
|
|
}
|
|
}
|
|
|
|
void mergeRotations(ColladaContext &ctx) {
|
|
for (AnimationMap::iterator it = ctx.animations.begin();
|
|
it != ctx.animations.end();) {
|
|
std::string key = it->first;
|
|
AnimationMap::iterator start = ctx.animations.lower_bound(key);
|
|
AnimationMap::iterator end = ctx.animations.upper_bound(key);
|
|
FloatTrack *tracks[] = { NULL, NULL, NULL };
|
|
|
|
for (AnimationMap::iterator it2 = start; it2 != end; ++it2) {
|
|
if (it2->second->getType() == FloatTrack::ERotationX)
|
|
tracks[0] = (FloatTrack *) it2->second;
|
|
else if (it2->second->getType() == FloatTrack::ERotationY)
|
|
tracks[1] = (FloatTrack *) it2->second;
|
|
else if (it2->second->getType() == FloatTrack::ERotationZ)
|
|
tracks[2] = (FloatTrack *) it2->second;
|
|
}
|
|
|
|
if (!tracks[0] && !tracks[1] && !tracks[2]) {
|
|
it = end;
|
|
continue;
|
|
}
|
|
|
|
SLog(EDebug, "Converting rotation track of \"%s\" to quaternions ..",
|
|
key.c_str());
|
|
|
|
std::set<Float> times;
|
|
for (size_t i=0; i<3; ++i)
|
|
for (size_t j=0; j<(tracks[i] ? tracks[i]->getSize() : (size_t) 0); ++j)
|
|
times.insert(tracks[i]->getTime(j));
|
|
|
|
QuatTrack *newTrack = new QuatTrack(QuatTrack::ERotationQuat, times.size());
|
|
size_t idx = 0;
|
|
|
|
for (std::set<Float>::iterator it2 = times.begin();
|
|
it2 != times.end(); ++it2) {
|
|
Float time = *it2, rot[3];
|
|
for (int i=0; i<3; ++i)
|
|
rot[i] = tracks[i] ? (tracks[i]->eval(time) * (M_PI/180)) : (Float) 0;
|
|
|
|
newTrack->setTime(idx, time);
|
|
newTrack->setValue(idx, Quaternion::fromEulerAngles(
|
|
Quaternion::EEulerXYZ, rot[0], rot[1], rot[2]));
|
|
idx++;
|
|
}
|
|
|
|
newTrack->incRef();
|
|
ctx.animations.insert(AnimationMap::value_type(key, newTrack));
|
|
|
|
it = ctx.animations.upper_bound(key);
|
|
}
|
|
}
|
|
|
|
|
|
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) {
|
|
domUint *result = new domUint[tess_nSources], size = 0;
|
|
tess_cleanup.push_back(result);
|
|
|
|
for (size_t i=0; i<tess_nSources; ++i) {
|
|
int offset = 0;
|
|
bool found = false;
|
|
for (int j=0; j<ELast; ++j) {
|
|
if (tess_vdata->typeToOffsetInStream[j] == (int) i) {
|
|
offset = tess_vdata->typeToOffset[j];
|
|
size = tess_vdata->typeToCount[j];
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
SAssert(found);
|
|
|
|
// this will be very slow -- let's hope that it happens rarely
|
|
Vec4 *oldVec = tess_vdata->data[offset];
|
|
Vec4 *newVec = new Vec4[(size_t) size + 1];
|
|
memcpy(newVec, oldVec, (size_t) size * sizeof(Vec4));
|
|
|
|
newVec[size] = Vec4(0.0f);
|
|
for (int j=0; j<4; ++j) {
|
|
void *ptr = vertex_data[j];
|
|
if (!ptr)
|
|
continue;
|
|
domUint idx = ((domUint *) ptr)[i];
|
|
newVec[size] += newVec[idx] * weight[j];
|
|
}
|
|
tess_vdata->data[offset] = newVec;
|
|
result[i] = size;
|
|
|
|
delete oldVec;
|
|
}
|
|
*outData = result;
|
|
}
|
|
|
|
GLvoid __stdcall tessEnd() { }
|
|
GLvoid __stdcall tessEdgeFlag(GLboolean) { }
|
|
GLvoid __stdcall tessError(GLenum error) {
|
|
SLog(EError, "The GLU tesselator generated an error: %s!", gluErrorString(error));
|
|
}
|
|
|
|
void GeometryConverter::convertCollada(const fs::path &inputFile,
|
|
std::ostream &os,
|
|
const fs::path &texturesDirectory,
|
|
const fs::path &meshesDirectory) {
|
|
CustomErrorHandler errorHandler;
|
|
daeErrorHandler::setErrorHandler(&errorHandler);
|
|
SLog(EInfo, "Loading \"%s\" ..", inputFile.filename().string().c_str());
|
|
#if COLLADA_DOM_SUPPORT141
|
|
DAE *dae = new DAE(NULL, NULL, "1.4.1");
|
|
domCOLLADA *document = dae->open141(inputFile.string());
|
|
if (document == NULL)
|
|
SLog(EError, "Could not load \"%s\"!", inputFile.string().c_str());
|
|
#else
|
|
DAE *dae = new DAE();
|
|
if (dae->load(inputFile.string().c_str()) != DAE_OK)
|
|
SLog(EError, "Could not load \"%s\"!", inputFile.string().c_str());
|
|
domCOLLADA *document = dae->getDom(inputFile.string().c_str());
|
|
#endif
|
|
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 version=\"" << MTS_VERSION << "\">" << endl;
|
|
os << "\t<integrator id=\"integrator\" type=\"direct\"/>" << endl << endl;
|
|
|
|
ColladaContext ctx(os);
|
|
ctx.meshesDirectory = meshesDirectory;
|
|
ctx.texturesDirectory = texturesDirectory;
|
|
ctx.cvt = this;
|
|
ctx.trackIndex = 0;
|
|
|
|
ref<Timer> timer = new Timer();
|
|
|
|
if (m_importMaterials) {
|
|
SLog(EInfo, "Importing materials ..");
|
|
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(ctx, *images.get(j));
|
|
}
|
|
|
|
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(ctx, *materials.get(j));
|
|
}
|
|
}
|
|
|
|
if (m_importAnimations) {
|
|
SLog(EInfo, "Importing animations ..");
|
|
domLibrary_animations_Array &libraryAnimations = document->getLibrary_animations_array();
|
|
for (size_t i=0; i<libraryAnimations.getCount(); ++i) {
|
|
domAnimation_Array &animations = libraryAnimations[i]->getAnimation_array();
|
|
for (size_t j=0; j<animations.getCount(); ++j)
|
|
loadAnimation(ctx, *animations[j]);
|
|
}
|
|
mergeRotations(ctx);
|
|
}
|
|
|
|
SLog(EInfo, "Importing scene ..");
|
|
for (size_t i=0; i<nodes.getCount(); ++i)
|
|
computeRefCounts(ctx, *nodes[i]);
|
|
|
|
for (size_t i=0; i<nodes.getCount(); ++i)
|
|
loadNode(ctx, Transform(), *nodes[i], "");
|
|
|
|
for (AnimationMap::iterator it = ctx.animations.begin();
|
|
it != ctx.animations.end(); ++it)
|
|
it->second->decRef();
|
|
|
|
SLog(EInfo, "Done, took %s", timeString(timer->getMilliseconds()/1000.0f).c_str());
|
|
|
|
os << "</scene>" << endl;
|
|
|
|
gluDeleteTess(tess);
|
|
daeErrorHandler::setErrorHandler(NULL);
|
|
delete dae;
|
|
}
|
|
|