better COLLADA import from Blender, improvements to the import dialog

metadata
Wenzel Jakob 2010-08-12 20:01:08 +02:00
parent 845ca566ec
commit 0953ceaba3
9 changed files with 1035 additions and 886 deletions

View File

@ -408,7 +408,8 @@ if hasCollada:
colladaEnv.Append(LIBPATH=env['COLLADALIBDIR'])
if env.has_key('COLLADALIB'):
colladaEnv.Append(LIBS=env['COLLADALIB'])
colladaEnv.Program('mtsimport', darwinStub + ['src/collada/main.cpp'])
colladaConverter = colladaEnv.StaticObject('src/collada/converter.cpp')
colladaEnv.Program('mtsimport', darwinStub + ['src/collada/main.cpp', colladaConverter])
if hasQt:
qtEnv = mainEnv.Clone()

904
src/collada/converter.cpp Normal file
View File

@ -0,0 +1,904 @@
#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 <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<ESourceType> offsetToType;
std::vector<int> typeToOffset;
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(inputs.getCount());
for (size_t i=0; i<inputs.getCount(); ++i)
result->data[i] = NULL;
result->offsetToType.resize(inputs.getCount());
result->typeToOffset.resize(ELast);
for (int i=0; i<ELast; ++i)
result->typeToOffset[i] = -1;
for (size_t i=0; i<inputs.getCount(); ++i) {
int offset = (int) inputs[i]->getOffset();
daeURI &sourceRef = inputs[i]->getSource();
sourceRef.resolveElement();
domSource *source = daeSafeCast<domSource>(sourceRef.getElement());
if (!strcmp(inputs[i]->getSemantic(), "VERTEX")) {
SAssert(vertInputs.getCount() == 1);
sourceRef = vertInputs[0]->getSource();
sourceRef.resolveElement();
source = daeSafeCast<domSource>(sourceRef.getElement());
}
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 (!strcmp(inputs[i]->getSemantic(), "VERTEX")) {
SAssert(accessor->getStride() == 3);
SAssert(result->typeToOffset[EPosition] == -1);
result->offsetToType[offset] = EPosition;
result->typeToOffset[EPosition] = offset;
result->glPos = new GLdouble[3*size];
for (int k=0; k<3*size; ++k)
result->glPos[k] = floatArray[k];
} else if (!strcmp(inputs[i]->getSemantic(), "NORMAL")) {
SAssert(accessor->getStride() == 3);
SAssert(result->typeToOffset[ENormal] == -1);
result->hasNormals = true;
result->offsetToType[offset] = ENormal;
result->typeToOffset[ENormal] = offset;
} else if (!strcmp(inputs[i]->getSemantic(), "TEXCOORD")) {
SAssert(accessor->getStride() == 2 || accessor->getStride() == 3);
if (result->typeToOffset[EUV] == -1) {
result->hasUVs = true;
result->offsetToType[offset] = EUV;
result->typeToOffset[EUV] = offset;
} else {
SLog(EWarn, "Found multiple texture coordinate records - ignoring!");
}
} else if (!strcmp(inputs[i]->getSemantic(), "COLOR")) {
SLog(EWarn, "Found per-vertex colors - ignoring. Please bake into a texture (Lighting/shading -> Batch Bake in Maya)");
result->offsetToType[offset] = EVertexColors;
result->typeToOffset[EVertexColors] = offset;
} else {
SLog(EError, "Encountered an unknown source semantic: %s",
inputs[i]->getSemantic());
}
}
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> {
public:
bool operator()(const Vertex &v1, const Vertex &v2) const {
if (v1.v.x < v2.v.x) return true;
else if (v1.v.x > v2.v.x) return false;
if (v1.v.y < v2.v.y) return true;
else if (v1.v.y > v2.v.y) return false;
if (v1.v.z < v2.v.z) return true;
else if (v1.v.z > v2.v.z) return false;
if (v1.n.x < v2.n.x) return true;
else if (v1.n.x > v2.n.x) return false;
if (v1.n.y < v2.n.y) return true;
else if (v1.n.y > v2.n.y) return false;
if (v1.n.z < v2.n.z) return true;
else if (v1.n.z > v2.n.z) return false;
if (v1.uv.x < v2.uv.x) return true;
else if (v1.uv.x > v2.uv.x) return false;
if (v1.uv.y < v2.uv.y) return true;
else if (v1.uv.y > v2.uv.y) return false;
return false;
}
};
void writeGeometry(std::string id, int geomIndex, std::string matID, Transform transform, std::ostream &os, VertexData *vData) {
std::vector<Vertex> vertexBuffer;
std::vector<Triangle> triangles;
std::map<Vertex, int, vertex_key_order> vertexMap;
size_t numMerged = 0, triangleIdx = 0;
Triangle triangle;
id += formatString("_%i", geomIndex);
for (size_t i=0; i<tess_data.size(); i+=tess_nSources) {
Vertex vertex;
domUint posRef = tess_data[i+vData->typeToOffset[EPosition]];
vertex.v = vData->data[vData->typeToOffset[EPosition]][posRef].toPoint();
if (vData->typeToOffset[ENormal] != -1) {
domUint normalRef = tess_data[i+vData->typeToOffset[ENormal]];
vertex.n = vData->data[vData->typeToOffset[ENormal]][normalRef].toNormal();
}
if (vData->typeToOffset[EUV] != -1) {
domUint uvRef = tess_data[i+vData->typeToOffset[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) {
triangles.push_back(triangle);
triangleIdx = 0;
}
}
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 = formatString("meshes/%s.serialized", id.c_str());
ref<FileStream> stream = new FileStream(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=\"" << id << "\" type=\"serialized\">" << endl;
os << "\t\t<string name=\"filename\" value=\"" << filename.c_str() << "\"/>" << 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 nodeName, Transform transform, std::ostream &os, domGeometry &geom,
StringMap &matLookupTable) {
SLog(EInfo, "Converting geometry \"%s\" (instantiated by %s)..", geom.getName(), nodeName.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();
std::string xmlName = nodeName + std::string("_") + geom.getId();
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(xmlName, geomIndex, matID, transform, os, data);
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(xmlName, geomIndex, matID, transform, os, data);
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 = vcount.get(i);
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*data->nSources; k+=data->nSources)
gluTessVertex(tess, &data->glPos[temp[k+posOffset]*3], (GLvoid *) (k+temp));
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 = polylist->getMaterial();
writeGeometry(xmlName, geomIndex, matID, transform, os, data);
delete data;
++geomIndex;
}
}
void loadMaterialParam(std::ostream &os, const std::string &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();
os << "\t\t<rgb name=\"" << name << "\" value=\""
<< 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(std::ostream &os, const std::string &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(std::ostream &os, domMaterial &mat, StringMap &_idToTexture) {
SLog(EInfo, "Converting material \"%s\" ..", mat.getName());
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::domLambert* lambert = technique->getLambert();
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=\"" << mat.getId() << "\" type=\"lambertian\">" << endl;
loadMaterialParam(os, "reflectance", idToTexture, diffuse, false);
loadMaterialParam(os, "reflectance", idToTexture, diffuse, true);
os << "\t</bsdf>" << endl << endl;
} else {
os << "\t<bsdf id=\"" << mat.getId() << "\" type=\"phong\">" << endl;
loadMaterialParam(os, "diffuseReflectance", idToTexture, diffuse, false);
loadMaterialParam(os, "specularReflectance", idToTexture, specular, false);
loadMaterialParam(os, "exponent", idToTexture, shininess, false);
loadMaterialParam(os, "diffuseReflectance", idToTexture, diffuse, true);
loadMaterialParam(os, "specularReflectance", idToTexture, specular, true);
loadMaterialParam(os, "exponent", idToTexture, shininess, true);
os << "\t</bsdf>" << endl << endl;
}
} else if (lambert) {
domCommon_color_or_texture_type* diffuse = lambert->getDiffuse();
os << "\t<bsdf id=\"" << mat.getId() << "\" type=\"lambertian\">" << endl;
loadMaterialParam(os, "reflectance", idToTexture, diffuse, false);
loadMaterialParam(os, "reflectance", idToTexture, diffuse, true);
os << "\t</bsdf>" << endl << endl;
} else {
SLog(EError, "Material type not supported! (must be Lambertian/Phong)");
}
}
void loadLight(Transform transform, std::ostream &os, domLight &light) {
SLog(EInfo, "Converting light \"%s\" ..", light.getName());
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));
Vector 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) {
domFloat3 &color = point->getColor()->getValue();
os << "\t<luminaire id=\"" << light.getId() << "\" 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::domSpot *spot = light.getTechnique_common()->getSpot().cast();
if (spot) {
domFloat3 &color = spot->getColor()->getValue();
Float falloffAngle = 180.0f;
if (spot->getFalloff_angle())
falloffAngle = (Float) spot->getFalloff_angle()->getValue();
os << "\t<luminaire id=\"" << light.getId() << "\" 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=\"" << light.getId() << "\" 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)
SLog(EWarn, "Encountered an unknown light type!");
}
void loadImage(ColladaConverter *cvt, std::ostream &os, domImage &image, StringMap &idToTexture, StringMap &fileToId) {
SLog(EInfo, "Converting texture \"%s\" ..", image.getName());
std::string filename = cdom::uriToFilePath(image.getInit_from()->getValue().str());
if (fileToId.find(filename) != fileToId.end()) {
idToTexture[image.getId()] = fileToId[filename];
return;
}
idToTexture[image.getId()] = image.getId();
fileToId[filename] = image.getId();
boost::filesystem::path path = boost::filesystem::path(filename, boost::filesystem::native);
std::string leaf = path.leaf();
if (endsWith(leaf, ".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 (!FileStream::exists(filename)) {
if (!FileStream::exists(leaf)) {
SLog(EWarn, "Found neither \"%s\" nor \"%s\"!", filename.c_str(), leaf.c_str());
filename = cvt->locateResource(filename);
if (filename == "")
SLog(EError, "Unable to locate a resource -- aborting conversion.");
} else {
filename = leaf;
}
}
ref<FileStream> input = new FileStream(filename, FileStream::EReadOnly);
ref<FileStream> output = new FileStream(formatString("textures/%s", leaf.c_str()), FileStream::ETruncReadWrite);
input->copyTo(output);
os << "\t<texture id=\"" << image.getId() << "\" type=\"ldrtexture\">" << endl;
os << "\t\t<string name=\"filename\" value=\"textures/" << leaf << "\"/>" << endl;
os << "\t</texture>" << endl << endl;
}
void loadCamera(Transform transform, std::ostream &os, domCamera &camera) {
SLog(EInfo, "Converting camera \"%s\" ..", camera.getName());
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();
os << "\t<camera id=\"" << camera.getId() << "\" type=\"orthographic\">" << endl;
}
domCamera::domOptics::domTechnique_common::domPerspective* persp = camera.getOptics()->
getTechnique_common()->getPerspective().cast();
if (persp) {
if (persp->getAspect_ratio().cast() != 0)
aspect = (Float) persp->getAspect_ratio()->getValue();
os << "\t<camera id=\"" << camera.getId() << "\" type=\"perspective\">" << endl;
if (persp->getXfov().cast()) {
Float yFov = radToDeg(2 * std::atan(std::tan(degToRad((Float) persp->getXfov()->getValue())/2) / aspect));
if (aspect <= 1.0f)
os << "\t\t<float name=\"fov\" value=\"" << persp->getXfov()->getValue() << "\"/>" << endl;
else
os << "\t\t<float name=\"fov\" value=\"" << yFov << "\"/>" << endl;
os << "\t\t<float name=\"nearClip\" value=\"" << persp->getZnear()->getValue() << "\"/>" << endl;
os << "\t\t<float name=\"farClip\" value=\"" << persp->getZfar()->getValue() << "\"/>" << endl;
} else if (persp->getYfov().cast()) {
Float xFov = radToDeg(2 * std::atan(std::tan(degToRad((Float) persp->getYfov()->getValue())/2) * aspect));
if (aspect > 1.0f)
os << "\t\t<float name=\"fov\" value=\"" << persp->getYfov()->getValue() << "\"/>" << 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 << 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=\"stratified\"/>" << 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(Transform transform, std::ostream &os, domNode &node) {
SLog(EInfo, "Converting node \"%s\" ..", node.getName());
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))
);
}
}
/* 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(node.getName(), transform, os, *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(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(transform, os, *camera);
}
/* Recursively iterate through sub-nodes */
domNode_Array &nodes = node.getNode_array();
for (size_t i=0; i<nodes.getCount(); ++i)
loadNode(transform, os, *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());
if (!node)
SLog(EError, "Could not find a referenced node!");
loadNode(transform, os, *node);
}
}
#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 ColladaConverter::convert(const std::string &inputFile,
const std::string &outputDirectory,
const std::string &sceneName,
const std::string &adjustmentFile) {
#if defined(__LINUX__)
std::string path = std::string("file://") +
FileResolver::getInstance()->resolveAbsolute(inputFile);
#else
std::string path = inputFile;
#endif
DAE *dae = new DAE();
SLog(EInfo, "Loading \"%s\" ..", path.c_str());
if (dae->load(path.c_str()) != DAE_OK)
SLog(EError, "Could not load \"%s\"!", path.c_str());
std::ostringstream os;
domCOLLADA *document = dae->getDom(path.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!");
std::string textureDirectory = "textures";
std::string meshesDirectory = "meshes";
std::string outputFile = sceneName;
#ifndef WIN32
if (outputDirectory != "") {
textureDirectory = outputDirectory + "/textures";
meshesDirectory = outputDirectory + "/meshes";
outputFile = outputDirectory + std::string("/") + sceneName;
}
int status = mkdir(textureDirectory.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (status != 0 && errno != EEXIST)
SLog(EError, "Could not create the directory \"textures\"");
status = mkdir(meshesDirectory.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (status != 0 && errno != EEXIST)
SLog(EError, "Could not create the directory \"meshes\"");
#else
if (outputDirectory != "") {
textureDirectory = textureDirectory + "\\textures";
meshesDirectory = meshesDirectory + "\\meshes";
outputFile = outputDirectory + std::string("\\") + sceneName;
}
int status = CreateDirectory(textureDirectory.c_str(), NULL);
if (status == 0 && GetLastError() != ERROR_ALREADY_EXISTS)
SLog(EError, "Could not create the directory \"textures\"");
status = CreateDirectory(meshesDirectory.c_str(), NULL);
if (status == 0 && GetLastError() != ERROR_ALREADY_EXISTS)
SLog(EError, "Could not create the directory \"meshes\"");
#endif
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;
os << "\tAutomatically converted from COLLADA" << endl;
os << "-->" << endl << endl;
os << "<scene>" << endl;
os << "\t<integrator type=\"direct\"/>" << endl << endl;
SLog(EInfo, "Converting to \"%s\" ..", outputFile.c_str());
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, *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(os, *materials.get(j), idToTexture);
}
for (size_t i=0; i<nodes.getCount(); ++i)
loadNode(Transform(), os, *nodes[i]);
os << "</scene>" << endl;
gluDeleteTess(tess);
delete dae;
std::ofstream ofile(outputFile.c_str());
if (ofile.fail())
SLog(EError, "Could not write to \"%s\"!", outputFile.c_str());
ofile << os.str();
ofile.close();
}

15
src/collada/converter.h Normal file
View File

@ -0,0 +1,15 @@
#include <mitsuba/mitsuba.h>
using namespace mitsuba;
class ColladaConverter {
public:
inline ColladaConverter() { }
void convert(const std::string &inputFile,
const std::string &outputDirectory,
const std::string &sceneName,
const std::string &adjustmentFile);
virtual std::string locateResource(const std::string &resource) = 0;
};

View File

@ -29,855 +29,30 @@
* (e.g. using Lighting/shading -> Batch bake in Maya).
*/
#define BOOST_FILESYSTEM_NO_LIB
#define BOOST_SYSTEM_NO_LIB
#include <mitsuba/mitsuba.h>
#include <mitsuba/render/trimesh.h>
#include "converter.h"
#include <mitsuba/hw/glrenderer.h>
#include <mitsuba/core/fresolver.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
using namespace mitsuba;
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<ESourceType> offsetToType;
std::vector<int> typeToOffset;
VertexData() : glPos(NULL) {
}
virtual ~VertexData() {
for (size_t i=0; i<data.size(); ++i) {
if (data[i])
delete[] data[i];
}
if (glPos)
delete[] glPos;
}
};
/* Globals */
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(inputs.getCount());
for (size_t i=0; i<inputs.getCount(); ++i)
result->data[i] = NULL;
result->offsetToType.resize(inputs.getCount());
result->typeToOffset.resize(ELast);
for (int i=0; i<ELast; ++i)
result->typeToOffset[i] = -1;
for (size_t i=0; i<inputs.getCount(); ++i) {
int offset = (int) inputs[i]->getOffset();
daeURI &sourceRef = inputs[i]->getSource();
sourceRef.resolveElement();
domSource *source = daeSafeCast<domSource>(sourceRef.getElement());
if (!strcmp(inputs[i]->getSemantic(), "VERTEX")) {
SAssert(vertInputs.getCount() == 1);
sourceRef = vertInputs[0]->getSource();
sourceRef.resolveElement();
source = daeSafeCast<domSource>(sourceRef.getElement());
}
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 (!strcmp(inputs[i]->getSemantic(), "VERTEX")) {
SAssert(accessor->getStride() == 3);
SAssert(result->typeToOffset[EPosition] == -1);
result->offsetToType[offset] = EPosition;
result->typeToOffset[EPosition] = offset;
result->glPos = new GLdouble[3*size];
for (int k=0; k<3*size; ++k)
result->glPos[k] = floatArray[k];
} else if (!strcmp(inputs[i]->getSemantic(), "NORMAL")) {
SAssert(accessor->getStride() == 3);
SAssert(result->typeToOffset[ENormal] == -1);
result->hasNormals = true;
result->offsetToType[offset] = ENormal;
result->typeToOffset[ENormal] = offset;
} else if (!strcmp(inputs[i]->getSemantic(), "TEXCOORD")) {
SAssert(accessor->getStride() == 2 || accessor->getStride() == 3);
if (result->typeToOffset[EUV] == -1) {
result->hasUVs = true;
result->offsetToType[offset] = EUV;
result->typeToOffset[EUV] = offset;
} else {
SLog(EWarn, "Found multiple texture coordinate records - ignoring!");
}
} else if (!strcmp(inputs[i]->getSemantic(), "COLOR")) {
SLog(EWarn, "Found per-vertex colors - ignoring. Please bake into a texture (Lighting/shading -> Batch Bake in Maya)");
result->offsetToType[offset] = EVertexColors;
result->typeToOffset[EVertexColors] = offset;
} else {
SLog(EError, "Encountered an unknown source semantic: %s",
inputs[i]->getSemantic());
}
}
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> {
class ConsoleColladaConverter : public ColladaConverter {
public:
bool operator()(const Vertex &v1, const Vertex &v2) const {
if (v1.v.x < v2.v.x) return true;
else if (v1.v.x > v2.v.x) return false;
if (v1.v.y < v2.v.y) return true;
else if (v1.v.y > v2.v.y) return false;
if (v1.v.z < v2.v.z) return true;
else if (v1.v.z > v2.v.z) return false;
if (v1.n.x < v2.n.x) return true;
else if (v1.n.x > v2.n.x) return false;
if (v1.n.y < v2.n.y) return true;
else if (v1.n.y > v2.n.y) return false;
if (v1.n.z < v2.n.z) return true;
else if (v1.n.z > v2.n.z) return false;
if (v1.uv.x < v2.uv.x) return true;
else if (v1.uv.x > v2.uv.x) return false;
if (v1.uv.y < v2.uv.y) return true;
else if (v1.uv.y > v2.uv.y) return false;
return false;
inline ConsoleColladaConverter() {
}
std::string locateResource(const std::string &resource) {
return "";
}
};
void writeGeometry(std::string id, int geomIndex, std::string matID, Transform transform, std::ostream &os, VertexData *vData) {
std::vector<Vertex> vertexBuffer;
std::vector<Triangle> triangles;
std::map<Vertex, int, vertex_key_order> vertexMap;
size_t numMerged = 0, triangleIdx = 0;
Triangle triangle;
id += formatString("_%i", geomIndex);
for (size_t i=0; i<tess_data.size(); i+=tess_nSources) {
Vertex vertex;
domUint posRef = tess_data[i+vData->typeToOffset[EPosition]];
vertex.v = vData->data[vData->typeToOffset[EPosition]][posRef].toPoint();
if (vData->typeToOffset[ENormal] != -1) {
domUint normalRef = tess_data[i+vData->typeToOffset[ENormal]];
vertex.n = vData->data[vData->typeToOffset[ENormal]][normalRef].toNormal();
}
if (vData->typeToOffset[EUV] != -1) {
domUint uvRef = tess_data[i+vData->typeToOffset[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) {
triangles.push_back(triangle);
triangleIdx = 0;
}
}
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 = formatString("meshes/%s.serialized", id.c_str());
ref<FileStream> stream = new FileStream(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=\"" << id << "\" type=\"serialized\">" << endl;
os << "\t\t<string name=\"filename\" value=\"" << filename.c_str() << "\"/>" << 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 nodeName, Transform transform, std::ostream &os, domGeometry &geom,
StringMap &matLookupTable) {
SLog(EInfo, "Converting geometry \"%s\" (instantiated by %s)..", geom.getName(), nodeName.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();
std::string xmlName = nodeName + std::string("_") + geom.getId();
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(xmlName, geomIndex, matID, transform, os, data);
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(xmlName, geomIndex, matID, transform, os, data);
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 = vcount.get(i);
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*data->nSources; k+=data->nSources)
gluTessVertex(tess, &data->glPos[temp[k+posOffset]*3], (GLvoid *) (k+temp));
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 = polylist->getMaterial();
writeGeometry(xmlName, geomIndex, matID, transform, os, data);
delete data;
++geomIndex;
}
}
void loadMaterialParam(std::ostream &os, const std::string &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();
os << "\t\t<rgb name=\"" << name << "\" value=\""
<< colValue.get(0) << " " << colValue.get(1) << " "
<< colValue.get(2) << "\"/>" << endl;
} else if (texture && handleRefs) {
os << "\t\t<ref name=\"" << name << "\" id=\""
<< idToTexture[texture->getTexture()] << "\"/>" << endl;
}
}
void loadMaterialParam(std::ostream &os, const std::string &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(std::ostream &os, domMaterial &mat, StringMap &idToTexture) {
SLog(EInfo, "Converting material \"%s\" ..", mat.getName());
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!");
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::domLambert* lambert = technique->getLambert();
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=\"" << mat.getId() << "\" type=\"lambertian\">" << endl;
loadMaterialParam(os, "reflectance", idToTexture, diffuse, false);
loadMaterialParam(os, "reflectance", idToTexture, diffuse, true);
os << "\t</bsdf>" << endl << endl;
} else {
os << "\t<bsdf id=\"" << mat.getId() << "\" type=\"phong\">" << endl;
loadMaterialParam(os, "diffuseReflectance", idToTexture, diffuse, false);
loadMaterialParam(os, "specularReflectance", idToTexture, specular, false);
loadMaterialParam(os, "exponent", idToTexture, shininess, false);
loadMaterialParam(os, "diffuseReflectance", idToTexture, diffuse, true);
loadMaterialParam(os, "specularReflectance", idToTexture, specular, true);
loadMaterialParam(os, "exponent", idToTexture, shininess, true);
os << "\t</bsdf>" << endl << endl;
}
} else if (lambert) {
domCommon_color_or_texture_type* diffuse = lambert->getDiffuse();
os << "\t<bsdf id=\"" << mat.getId() << "\" type=\"lambertian\">" << endl;
loadMaterialParam(os, "reflectance", idToTexture, diffuse, false);
loadMaterialParam(os, "reflectance", idToTexture, diffuse, true);
os << "\t</bsdf>" << endl << endl;
} else {
SLog(EError, "Material type not supported! (must be Lambertian/Phong)");
}
}
void loadLight(Transform transform, std::ostream &os, domLight &light) {
SLog(EInfo, "Converting light \"%s\" ..", light.getName());
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));
Vector 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) {
domFloat3 &color = point->getColor()->getValue();
os << "\t<luminaire id=\"" << light.getId() << "\" 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::domSpot *spot = light.getTechnique_common()->getSpot().cast();
if (spot) {
domFloat3 &color = spot->getColor()->getValue();
Float falloffAngle = 180.0f;
if (spot->getFalloff_angle())
falloffAngle = (Float) spot->getFalloff_angle()->getValue();
os << "\t<luminaire id=\"" << light.getId() << "\" 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=\"" << light.getId() << "\" 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)
SLog(EWarn, "Encountered an unknown light type!");
}
void loadImage(std::ostream &os, domImage &image, StringMap &idToTexture, StringMap &fileToId) {
SLog(EInfo, "Converting texture \"%s\" ..", image.getName());
std::string filename = cdom::uriToFilePath(image.getInit_from()->getValue().str());
if (fileToId.find(filename) != fileToId.end()) {
idToTexture[image.getId()] = fileToId[filename];
return;
}
idToTexture[image.getId()] = image.getId();
fileToId[filename] = image.getId();
boost::filesystem::path path = boost::filesystem::path(filename, boost::filesystem::native);
std::string leaf = path.leaf();
if (endsWith(leaf, ".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 (!FileStream::exists(filename)) {
if (!FileStream::exists(leaf))
SLog(EError, "Found neither \"%s\" nor \"%s\"!", filename.c_str(), leaf.c_str());
filename = leaf;
}
ref<FileStream> input = new FileStream(filename, FileStream::EReadOnly);
ref<FileStream> output = new FileStream(formatString("textures/%s", leaf.c_str()), FileStream::ETruncReadWrite);
input->copyTo(output);
os << "\t<texture id=\"" << image.getId() << "\" type=\"ldrtexture\">" << endl;
os << "\t\t<string name=\"filename\" value=\"textures/" << leaf << "\"/>" << endl;
os << "\t</texture>" << endl << endl;
}
void loadCamera(Transform transform, std::ostream &os, domCamera &camera) {
SLog(EInfo, "Converting camera \"%s\" ..", camera.getName());
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();
os << "\t<camera id=\"" << camera.getId() << "\" type=\"orthographic\">" << endl;
}
domCamera::domOptics::domTechnique_common::domPerspective* persp = camera.getOptics()->
getTechnique_common()->getPerspective().cast();
if (persp) {
if (persp->getAspect_ratio().cast() != 0)
aspect = (Float) persp->getAspect_ratio()->getValue();
os << "\t<camera id=\"" << camera.getId() << "\" type=\"perspective\">" << endl;
if (persp->getXfov().cast()) {
Float yFov = radToDeg(2 * std::atan(std::tan(degToRad((Float) persp->getXfov()->getValue())/2) / aspect));
if (aspect <= 1.0f)
os << "\t\t<float name=\"fov\" value=\"" << persp->getXfov()->getValue() << "\"/>" << endl;
else
os << "\t\t<float name=\"fov\" value=\"" << yFov << "\"/>" << endl;
os << "\t\t<float name=\"nearClip\" value=\"" << persp->getZnear()->getValue() << "\"/>" << endl;
os << "\t\t<float name=\"farClip\" value=\"" << persp->getZfar()->getValue() << "\"/>" << endl;
} else if (persp->getYfov().cast()) {
Float xFov = radToDeg(2 * std::atan(std::tan(degToRad((Float) persp->getYfov()->getValue())/2) * aspect));
if (aspect > 1.0f)
os << "\t\t<float name=\"fov\" value=\"" << persp->getYfov()->getValue() << "\"/>" << 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 << 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=\"stratified\"/>" << 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(Transform transform, std::ostream &os, domNode &node) {
SLog(EInfo, "Converting node \"%s\" ..", node.getName());
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))
);
}
}
/* 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(node.getName(), transform, os, *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(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(transform, os, *camera);
}
/* Recursively iterate through sub-nodes */
domNode_Array &nodes = node.getNode_array();
for (size_t i=0; i<nodes.getCount(); ++i)
loadNode(transform, os, *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());
if (!node)
SLog(EError, "Could not find a referenced node!");
loadNode(transform, os, *node);
}
}
#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) {
}
int colladaMain(int argc, char **argv) {
if (argc < 3) {
cout << "Syntax: mtsimport <DAE source file URL> <XML destination file>" << endl;
cout << "Syntax: mtsimport <DAE source file URL> <XML destination file> [Adjustment file]" << endl
<< "Please see the documentation for more information." << endl;
return -1;
}
#if defined(__LINUX__)
std::string path = std::string("file://") +
FileResolver::getInstance()->resolveAbsolute(argv[1]);
#else
std::string path = argv[1];
#endif
ConsoleColladaConverter converter;
converter.convert(argv[1], "", argv[2], argc > 3 ? argv[3] : "");
DAE *dae = new DAE();
SLog(EInfo, "Loading \"%s\" ..", path.c_str());
if (dae->load(path.c_str()) != DAE_OK)
SLog(EError, "Could not load \"%s\"!", argv[1]);
std::ofstream os(argv[2]);
if (os.fail())
SLog(EError, "Could not write to \"%s\"!", argv[2]);
domCOLLADA *document = dae->getDom(path.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!");
#ifndef WIN32
int status = mkdir("textures", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (status != 0 && errno != EEXIST)
SLog(EError, "Could not create the directory \"textures\"");
status = mkdir("meshes", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (status != 0 && errno != EEXIST)
SLog(EError, "Could not create the directory \"meshes\"");
#else
int status = CreateDirectory("textures", NULL);
if (status == 0 && GetLastError() != ERROR_ALREADY_EXISTS)
SLog(EError, "Could not create the directory \"textures\"");
status = CreateDirectory("meshes", NULL);
if (status == 0 && GetLastError() != ERROR_ALREADY_EXISTS)
SLog(EError, "Could not create the directory \"meshes\"");
#endif
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;
os << "\tAutomatically converted from COLLADA" << endl;
os << "-->" << endl << endl;
os << "<scene>" << endl;
os << "\t<integrator type=\"direct\"/>" << endl << endl;
SLog(EInfo, "Converting to \"%s\" ..", argv[2]);
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(os, *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(os, *materials.get(j), idToTexture);
}
for (size_t i=0; i<nodes.getCount(); ++i)
loadNode(Transform(), os, *nodes[i]);
os << "</scene>" << endl;
gluDeleteTess(tess);
delete dae;
return 0;
}
@ -892,23 +67,26 @@ int ubi_main(int argc, char **argv) {
Thread::getThread()->getLogger()->setLogLevel(EInfo);
#ifdef WIN32
FileResolver *resolver = FileResolver::getInstance();
#if defined(WIN32)
char lpFilename[1024];
if (GetModuleFileNameA(NULL,
lpFilename, sizeof(lpFilename))) {
FileResolver *resolver = FileResolver::getInstance();
resolver->addPathFromFile(lpFilename);
} else {
SLog(EWarn, "Could not determine the executable path");
}
#else
#elif defined(__LINUX__)
char exePath[PATH_MAX];
if (getcwd(exePath, PATH_MAX)) {
FileResolver *resolver = FileResolver::getInstance();
resolver->addPathFromFile(exePath);
} else {
SLog(EWarn, "Could not determine the executable path");
}
#else
MTS_AUTORELEASE_BEGIN()
resolver->addPath(__ubi_bundlepath());
MTS_AUTORELEASE_END()
#endif
try {

View File

@ -14,7 +14,7 @@ public:
/* Number of luminaire samples for direct illumination */
m_directSamples = props.getInteger("directSamples", 1);
/* Number of BSDF samples when intersecting a glossy material */
m_glossySamples = props.getInteger("glossySamples", 1);
m_glossySamples = props.getInteger("glossySamples", 32);
/* Depth to start using russian roulette when tracing photons */
m_rrDepth = props.getInteger("rrDepth", 10);
/* Depth cutoff when tracing photons */

View File

@ -6,6 +6,8 @@ ImportDialog::ImportDialog(QWidget *parent) :
QDialog(parent, Qt::Sheet),
ui(new Ui::ImportDialog) {
ui->setupUi(this);
connect(ui->sceneEdit, SIGNAL(textChanged(const QString &)),
this, SLOT(refresh()));
}
ImportDialog::~ImportDialog() {
@ -22,3 +24,64 @@ void ImportDialog::changeEvent(QEvent *e) {
break;
}
}
void ImportDialog::on_inputBrowse_clicked(bool checked) {
QFileDialog dialog(this);
dialog.setNameFilter(tr("COLLADA scenes (*.dae)"));
dialog.setAcceptMode(QFileDialog::AcceptOpen);
dialog.setViewMode(QFileDialog::Detail);
dialog.setWindowModality(Qt::ApplicationModal);
if (dialog.exec()) {
QString fname = dialog.selectedFiles()[0];
ui->inputEdit->setText(fname);
QFileInfo info(fname);
ui->directoryEdit->setText(info.absoluteDir().absolutePath());
ui->sceneEdit->setText(info.completeBaseName() + ".xml");
refresh();
}
}
void ImportDialog::on_directoryBrowse_clicked(bool checked) {
QFileDialog dialog(this);
dialog.setAcceptMode(QFileDialog::AcceptOpen);
dialog.setFileMode(QFileDialog::DirectoryOnly);
dialog.setWindowModality(Qt::ApplicationModal);
if (dialog.exec()) {
QString fname = dialog.selectedFiles()[0];
ui->directoryEdit->setText(fname);
refresh();
}
}
void ImportDialog::on_adjustmentBrowse_clicked(bool checked) {
QFileDialog dialog(this);
dialog.setNameFilter(tr("Import adjustment files (*.xml)"));
dialog.setAcceptMode(QFileDialog::AcceptOpen);
dialog.setViewMode(QFileDialog::Detail);
dialog.setWindowModality(Qt::ApplicationModal);
if (dialog.exec()) {
QString fname = dialog.selectedFiles()[0];
ui->adjustmentEdit->setText(fname);
refresh();
}
}
void ImportDialog::refresh() {
bool hasInput = ui->inputEdit->text() != "";
bool hasOutput = ui->sceneEdit->text().endsWith(".xml");
ui->directoryBrowse->setEnabled(hasInput);
ui->directoryEdit->setEnabled(hasInput);
ui->adjustmentBrowse->setEnabled(hasInput);
ui->adjustmentEdit->setEnabled(hasInput);
ui->sceneEdit->setEnabled(hasInput);
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(hasInput && hasOutput);
}
void ImportDialog::accept() {
QDialog::accept();
QString sourceFile = ui->inputEdit->text();
QString targetScene = ui->sceneEdit->text();
QString directory = ui->directoryEdit->text();
}

View File

@ -12,6 +12,13 @@ class ImportDialog : public QDialog {
public:
ImportDialog(QWidget *parent);
~ImportDialog();
public slots:
void accept();
protected slots:
void on_inputBrowse_clicked(bool checked);
void on_directoryBrowse_clicked(bool checked);
void on_adjustmentBrowse_clicked(bool checked);
void refresh();
protected:
void changeEvent(QEvent *e);
private:

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>358</width>
<width>357</width>
<height>250</height>
</rect>
</property>
@ -67,14 +67,14 @@
</spacer>
</item>
<item row="1" column="4">
<widget class="QLineEdit" name="lineEdit">
<widget class="QLineEdit" name="inputEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="5">
<widget class="QPushButton" name="pushButton">
<widget class="QPushButton" name="inputBrowse">
<property name="maximumSize">
<size>
<width>30</width>
@ -94,9 +94,9 @@
</widget>
</item>
<item row="2" column="4">
<widget class="QLineEdit" name="lineEdit_2">
<widget class="QLineEdit" name="directoryEdit">
<property name="enabled">
<bool>true</bool>
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
@ -104,9 +104,9 @@
</widget>
</item>
<item row="2" column="5">
<widget class="QPushButton" name="pushButton_2">
<widget class="QPushButton" name="directoryBrowse">
<property name="enabled">
<bool>true</bool>
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
@ -128,42 +128,20 @@
<item row="3" column="1">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Target scene :</string>
<string>Target scene file :</string>
</property>
</widget>
</item>
<item row="3" column="4">
<widget class="QLineEdit" name="lineEdit_3">
<widget class="QLineEdit" name="sceneEdit">
<property name="enabled">
<bool>true</bool>
<bool>false</bool>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="5">
<widget class="QPushButton" name="pushButton_4">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>30</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="5">
<widget class="QLabel" name="label_5">
<property name="font">
@ -179,15 +157,18 @@
</item>
<item row="5" column="1" colspan="2">
<widget class="QLabel" name="label_6">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Adjustment file :</string>
</property>
</widget>
</item>
<item row="5" column="4">
<widget class="QLineEdit" name="lineEdit_4">
<widget class="QLineEdit" name="adjustmentEdit">
<property name="enabled">
<bool>true</bool>
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
@ -195,9 +176,9 @@
</widget>
</item>
<item row="5" column="5">
<widget class="QPushButton" name="pushButton_3">
<widget class="QPushButton" name="adjustmentBrowse">
<property name="enabled">
<bool>true</bool>
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">

View File

@ -326,7 +326,7 @@
encoding to reduce the storage footprint of photons.
</descr>
<param name="directSamples" readableName="Direct samples" type="integer" default="1">Number of luminaire samples for direct illumination</param>
<param name="glossySamples" readableName="Glossy samples" type="integer" default="1">Number of glossy samples for direct illumination</param>
<param name="glossySamples" readableName="Glossy samples" type="integer" default="32">Number of glossy samples for direct illumination</param>
<param name="maxDepth" readableName="Maximum depth" type="integer" default="40">Depth cutoff when tracing photons</param>
<param name="maxSpecularDepth" readableName="Max. specular bounces" type="integer" default="6">Depth cutoff when recursively tracing specular materials</param>
<param name="granularity" readableName="Work unit granularity" importance="1" type="integer" default="1000">Granularity of photon tracing work units (in shot particles)</param>