2010-09-03 05:41:20 +08:00
|
|
|
/*
|
|
|
|
This file is part of Mitsuba, a physically based rendering system.
|
|
|
|
|
|
|
|
Copyright (c) 2007-2010 by Wenzel Jakob and others.
|
|
|
|
|
|
|
|
Mitsuba is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License Version 3
|
|
|
|
as published by the Free Software Foundation.
|
|
|
|
|
|
|
|
Mitsuba is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2010-08-20 01:49:53 +08:00
|
|
|
#include <xercesc/dom/DOM.hpp>
|
|
|
|
#include <xercesc/dom/DOMDocument.hpp>
|
|
|
|
#include <xercesc/dom/DOMDocumentType.hpp>
|
|
|
|
#include <xercesc/dom/DOMElement.hpp>
|
|
|
|
#include <xercesc/dom/DOMImplementation.hpp>
|
|
|
|
#include <xercesc/dom/DOMImplementationLS.hpp>
|
|
|
|
#include <xercesc/dom/DOMNodeIterator.hpp>
|
|
|
|
#include <xercesc/dom/DOMNodeList.hpp>
|
|
|
|
#include <xercesc/parsers/XercesDOMParser.hpp>
|
|
|
|
#include <xercesc/framework/MemBufInputSource.hpp>
|
|
|
|
#include <xercesc/framework/Wrapper4InputSource.hpp>
|
2010-11-22 16:18:15 +08:00
|
|
|
#include <xercesc/framework/MemBufFormatTarget.hpp>
|
2010-08-20 01:49:53 +08:00
|
|
|
#include <xercesc/util/XMLUni.hpp>
|
2010-09-14 03:19:04 +08:00
|
|
|
#include <mitsuba/core/fresolver.h>
|
2010-11-16 03:51:31 +08:00
|
|
|
#include <mitsuba/core/fstream.h>
|
2010-09-14 03:19:04 +08:00
|
|
|
#include <boost/algorithm/string.hpp>
|
2010-08-20 01:49:53 +08:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <set>
|
|
|
|
|
|
|
|
XERCES_CPP_NAMESPACE_USE
|
|
|
|
|
|
|
|
#include "converter.h"
|
|
|
|
|
|
|
|
class ImporterDOMErrorHandler : public DOMErrorHandler {
|
|
|
|
public:
|
|
|
|
inline ImporterDOMErrorHandler() { }
|
|
|
|
|
|
|
|
bool handleError(const DOMError& domError) {
|
2010-09-21 09:10:49 +08:00
|
|
|
SLog(EWarn, "%s (line %i, char %i): %s",
|
2010-08-20 01:49:53 +08:00
|
|
|
XMLString::transcode(domError.getLocation()->getURI()),
|
|
|
|
domError.getLocation()->getLineNumber(),
|
|
|
|
domError.getLocation()->getColumnNumber(),
|
|
|
|
XMLString::transcode(domError.getMessage()));
|
2010-09-21 09:10:49 +08:00
|
|
|
|
|
|
|
if (domError.getSeverity() != DOMError::DOM_SEVERITY_WARNING)
|
|
|
|
SLog(EError, "Encountered a critical DOM error -- giving up!");
|
|
|
|
|
2010-08-20 01:49:53 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-11-17 08:43:39 +08:00
|
|
|
void createNodeMap(DOMNode *node, std::map<std::string, DOMNode *> &nodes) {
|
2010-08-20 01:49:53 +08:00
|
|
|
if (node) {
|
|
|
|
char *nodeName = XMLString::transcode(node->getNodeName());
|
|
|
|
if (strcmp(nodeName, "ref") == 0) {
|
|
|
|
XMLString::release(&nodeName);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
XMLString::release(&nodeName);
|
|
|
|
if (node->getNodeType() == DOMNode::ELEMENT_NODE && node->hasAttributes()) {
|
|
|
|
DOMNamedNodeMap *attributes = node->getAttributes();
|
2010-11-17 08:43:39 +08:00
|
|
|
XMLCh *idString = XMLString::transcode("id");
|
|
|
|
DOMAttr *attribute = (DOMAttr *) attributes->getNamedItem(idString);
|
|
|
|
XMLString::release(&idString);
|
|
|
|
if (attribute != NULL) {
|
2010-08-20 01:49:53 +08:00
|
|
|
char *value = XMLString::transcode(attribute->getValue());
|
2010-11-17 08:43:39 +08:00
|
|
|
nodes[value] = node;
|
2010-08-20 01:49:53 +08:00
|
|
|
XMLString::release(&value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (DOMNode *child = node->getFirstChild(); child != 0; child=child->getNextSibling())
|
2010-11-17 08:43:39 +08:00
|
|
|
createNodeMap(child, nodes);
|
2010-08-20 01:49:53 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-22 15:29:55 +08:00
|
|
|
void cleanup(DOMNode *node) {
|
|
|
|
DOMNode *child = node->getFirstChild();
|
|
|
|
while (child) {
|
|
|
|
DOMNode *next = child->getNextSibling();
|
|
|
|
if (child->getNodeType() == DOMNode::TEXT_NODE)
|
|
|
|
node->removeChild(child);
|
|
|
|
else
|
|
|
|
cleanup(child);
|
|
|
|
child = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-14 03:19:04 +08:00
|
|
|
void GeometryConverter::convert(const fs::path &inputFile,
|
|
|
|
const fs::path &outputDirectory,
|
|
|
|
const fs::path &sceneName,
|
|
|
|
const fs::path &adjustmentFile) {
|
2010-08-20 01:49:53 +08:00
|
|
|
|
2010-09-10 09:14:48 +08:00
|
|
|
fs::path textureDirectory = "textures";
|
|
|
|
fs::path meshesDirectory = "meshes";
|
|
|
|
fs::path outputFile = sceneName;
|
2010-08-20 01:49:53 +08:00
|
|
|
|
2010-11-16 17:36:49 +08:00
|
|
|
this->m_outputDirectory = outputDirectory;
|
|
|
|
|
2010-09-14 03:19:04 +08:00
|
|
|
if (!outputDirectory.empty()) {
|
|
|
|
textureDirectory = outputDirectory / "textures";
|
|
|
|
meshesDirectory = outputDirectory / "meshes";
|
|
|
|
outputFile = outputDirectory / sceneName;
|
2010-08-20 01:49:53 +08:00
|
|
|
}
|
|
|
|
|
2010-11-16 03:51:31 +08:00
|
|
|
if (m_packGeometry) {
|
|
|
|
m_geometryFileName = outputDirectory / sceneName;
|
|
|
|
m_geometryFileName.replace_extension(".serialized");
|
|
|
|
m_geometryFile = new FileStream(m_geometryFileName, FileStream::ETruncReadWrite);
|
|
|
|
m_geometryFile->setByteOrder(Stream::ELittleEndian);
|
|
|
|
}
|
|
|
|
|
2010-09-14 17:27:35 +08:00
|
|
|
if (!fs::exists(textureDirectory)) {
|
|
|
|
SLog(EInfo, "Creating directory \"%s\" ..", textureDirectory.file_string().c_str());
|
2010-09-10 09:14:48 +08:00
|
|
|
fs::create_directory(textureDirectory);
|
2010-09-14 17:27:35 +08:00
|
|
|
}
|
2010-08-20 01:49:53 +08:00
|
|
|
|
2010-11-16 06:40:37 +08:00
|
|
|
if (!fs::exists(meshesDirectory) && !m_packGeometry) {
|
2010-09-14 17:27:35 +08:00
|
|
|
SLog(EInfo, "Creating directory \"%s\" ..", meshesDirectory.file_string().c_str());
|
2010-09-10 09:14:48 +08:00
|
|
|
fs::create_directory(meshesDirectory);
|
2010-09-14 17:27:35 +08:00
|
|
|
}
|
2010-08-20 01:49:53 +08:00
|
|
|
|
|
|
|
std::ostringstream os;
|
2010-08-27 01:14:25 +08:00
|
|
|
SLog(EInfo, "Beginning conversion ..");
|
2010-09-14 03:19:04 +08:00
|
|
|
|
|
|
|
std::string extension = boost::to_lower_copy(fs::extension(inputFile));
|
|
|
|
|
|
|
|
if (extension == ".dae" || extension == ".zae") {
|
2010-08-20 01:49:53 +08:00
|
|
|
convertCollada(inputFile, os, textureDirectory, meshesDirectory);
|
2010-09-14 03:19:04 +08:00
|
|
|
} else if (extension == ".obj") {
|
2010-08-20 01:49:53 +08:00
|
|
|
convertOBJ(inputFile, os, textureDirectory, meshesDirectory);
|
|
|
|
} else {
|
2010-08-31 02:12:23 +08:00
|
|
|
SLog(EError, "Unknown input format (must end in either .DAE, .ZAE or .OBJ)");
|
2010-08-20 01:49:53 +08:00
|
|
|
}
|
|
|
|
|
2010-09-14 06:51:09 +08:00
|
|
|
if (!adjustmentFile.empty()) {
|
2010-08-20 01:49:53 +08:00
|
|
|
SLog(EInfo, "Applying adjustments ..");
|
|
|
|
static const XMLCh gLS[] = { chLatin_L, chLatin_S, chNull };
|
|
|
|
DOMImplementationLS *impl = DOMImplementationRegistry::getDOMImplementation(gLS);
|
|
|
|
|
|
|
|
DOMLSParser *parser = impl->createLSParser(DOMImplementationLS::MODE_SYNCHRONOUS, 0);
|
|
|
|
DOMConfiguration *conf(parser->getDomConfig());
|
|
|
|
ImporterDOMErrorHandler errorHandler;
|
|
|
|
conf->setParameter(XMLUni::fgDOMErrorHandler, &errorHandler);
|
|
|
|
|
|
|
|
std::string xmlString = os.str();
|
|
|
|
MemBufInputSource* memBufIS = new MemBufInputSource((const XMLByte*) xmlString.c_str(),
|
|
|
|
xmlString.length(), "bufID", false);
|
|
|
|
Wrapper4InputSource *wrapper = new Wrapper4InputSource(memBufIS, false);
|
|
|
|
XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *doc = parser->parse(wrapper);
|
2010-09-14 03:19:04 +08:00
|
|
|
XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *adj = parser->parseURI(adjustmentFile.file_string().c_str());
|
2010-09-21 09:10:49 +08:00
|
|
|
if (adj == NULL)
|
|
|
|
SLog(EError, "Could not parse adjustments file!");
|
2010-09-14 03:19:04 +08:00
|
|
|
|
2010-11-17 08:43:39 +08:00
|
|
|
std::map<std::string, DOMNode *> nodeMap;
|
|
|
|
createNodeMap(doc, nodeMap);
|
2010-08-20 01:49:53 +08:00
|
|
|
|
|
|
|
DOMElement *docRoot = doc->getDocumentElement();
|
|
|
|
DOMElement *adjRoot = adj->getDocumentElement();
|
|
|
|
|
|
|
|
DOMNode *insertBeforeNode = NULL;
|
|
|
|
for (DOMNode *child = docRoot->getFirstChild(); child != 0; child=child->getNextSibling()) {
|
|
|
|
char *name = XMLString::transcode(child->getNodeName());
|
|
|
|
if (strcmp(name, "shape") == 0) {
|
|
|
|
insertBeforeNode = child;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
XMLString::release(&name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (insertBeforeNode == NULL) {
|
|
|
|
/* No shape node found, use the camera node instead */
|
|
|
|
for (DOMNode *child = docRoot->getFirstChild(); child != 0; child=child->getNextSibling()) {
|
|
|
|
char *name = XMLString::transcode(child->getNodeName());
|
|
|
|
if (strcmp(name, "camera") == 0) {
|
|
|
|
insertBeforeNode = child;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
XMLString::release(&name);
|
|
|
|
}
|
|
|
|
SAssertEx(insertBeforeNode != NULL, "Internal error while applying adjustments: cannot find shape/camera node");
|
|
|
|
}
|
|
|
|
|
2010-11-17 08:43:39 +08:00
|
|
|
for (DOMNode *child = adjRoot->getFirstChild(); child != 0; child=child->getNextSibling()) {
|
|
|
|
if (child->getNodeType() == DOMNode::ELEMENT_NODE) {
|
|
|
|
char *nodeName = XMLString::transcode(child->getNodeName());
|
|
|
|
std::string id;
|
|
|
|
if (child->getNodeType() == DOMNode::ELEMENT_NODE && child->hasAttributes()) {
|
|
|
|
DOMNamedNodeMap *attributes = child->getAttributes();
|
|
|
|
XMLCh *idString = XMLString::transcode("id");
|
|
|
|
DOMAttr *attribute = (DOMAttr *) attributes->getNamedItem(idString);
|
|
|
|
XMLString::release(&idString);
|
|
|
|
if (attribute) {
|
|
|
|
char *value = XMLString::transcode(attribute->getValue());
|
|
|
|
id = value;
|
|
|
|
XMLString::release(&value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (id != "" && nodeMap.find(id) != nodeMap.end()) {
|
|
|
|
DOMNode *node = nodeMap[id], *parent = node->getParentNode();
|
|
|
|
if (strcmp(nodeName, "append") == 0) {
|
|
|
|
for (DOMNode *child2 = child->getFirstChild(); child2 != 0; child2=child2->getNextSibling())
|
|
|
|
node->insertBefore(doc->importNode(child2, true), NULL);
|
2010-11-19 23:36:00 +08:00
|
|
|
} else if (strcmp(nodeName, "prepend") == 0) {
|
|
|
|
for (DOMNode *child2 = child->getFirstChild(); child2 != 0; child2=child2->getNextSibling())
|
|
|
|
node->insertBefore(doc->importNode(child2, true), node->getFirstChild());
|
2010-11-18 04:10:14 +08:00
|
|
|
} else if (parent == insertBeforeNode->getParentNode()) {
|
2010-11-17 08:43:39 +08:00
|
|
|
parent->removeChild(node);
|
|
|
|
docRoot->insertBefore(doc->importNode(child, true), insertBeforeNode);
|
2010-11-18 04:10:14 +08:00
|
|
|
} else {
|
|
|
|
parent->replaceChild(doc->importNode(child, true), node);
|
2010-11-17 08:43:39 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
docRoot->insertBefore(doc->importNode(child, true), insertBeforeNode);
|
|
|
|
}
|
|
|
|
XMLString::release(&nodeName);
|
|
|
|
}
|
|
|
|
}
|
2010-11-22 15:29:55 +08:00
|
|
|
cleanup(doc);
|
2010-08-20 01:49:53 +08:00
|
|
|
|
|
|
|
DOMLSSerializer *serializer = impl->createLSSerializer();
|
2010-11-19 23:36:00 +08:00
|
|
|
DOMConfiguration *serConf = serializer->getDomConfig();
|
2010-08-20 01:49:53 +08:00
|
|
|
serConf->setParameter(XMLUni::fgDOMErrorHandler, &errorHandler);
|
2010-11-19 23:36:00 +08:00
|
|
|
if (serConf->canSetParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true))
|
|
|
|
serConf->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true);
|
|
|
|
DOMLSOutput *output = impl->createLSOutput();
|
2010-11-22 16:18:15 +08:00
|
|
|
MemBufFormatTarget *target = new MemBufFormatTarget();
|
2010-08-20 01:49:53 +08:00
|
|
|
output->setByteStream(target);
|
|
|
|
serializer->write(doc, output);
|
2010-11-22 16:18:15 +08:00
|
|
|
const XMLByte *content = target->getRawBuffer();
|
|
|
|
std::ostringstream oss;
|
|
|
|
|
|
|
|
/* Turn leading spaces into tabs */
|
|
|
|
bool newline = true;
|
|
|
|
int numSpaces = 0;
|
|
|
|
for (size_t i=0; i<target->getLen(); ++i) {
|
|
|
|
char data = content[i];
|
|
|
|
switch (data) {
|
|
|
|
case ' ':
|
|
|
|
if (newline)
|
|
|
|
numSpaces++;
|
|
|
|
else
|
|
|
|
oss << data;
|
|
|
|
break;
|
|
|
|
case '\r':
|
|
|
|
case '\n':
|
|
|
|
oss << data;
|
|
|
|
newline = true;
|
|
|
|
numSpaces = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (newline) {
|
|
|
|
for (int i=0; i<numSpaces/2; ++i)
|
|
|
|
oss << '\t';
|
|
|
|
}
|
|
|
|
oss << data;
|
|
|
|
newline = false;
|
|
|
|
numSpaces = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fs::ofstream os(outputFile);
|
|
|
|
os << oss.str();
|
|
|
|
os.close();
|
2010-08-20 01:49:53 +08:00
|
|
|
delete output;
|
|
|
|
delete target;
|
|
|
|
delete wrapper;
|
|
|
|
delete memBufIS;
|
|
|
|
delete serializer;
|
|
|
|
parser->release();
|
|
|
|
} else {
|
2010-09-10 09:14:48 +08:00
|
|
|
fs::ofstream ofile(outputFile);
|
2010-08-20 01:49:53 +08:00
|
|
|
if (ofile.fail())
|
2010-09-10 09:14:48 +08:00
|
|
|
SLog(EError, "Could not write to \"%s\"!", outputFile.file_string().c_str());
|
2010-08-20 01:49:53 +08:00
|
|
|
ofile << os.str();
|
|
|
|
ofile.close();
|
|
|
|
}
|
2010-11-16 03:51:31 +08:00
|
|
|
if (m_geometryFile) {
|
|
|
|
for (size_t i=0; i<m_geometryDict.size(); ++i)
|
|
|
|
m_geometryFile->writeUInt(m_geometryDict[i]);
|
|
|
|
m_geometryFile->writeUInt((uint32_t) m_geometryDict.size());
|
|
|
|
m_geometryFile->close();
|
|
|
|
}
|
|
|
|
|
2010-09-14 03:19:04 +08:00
|
|
|
m_filename = outputFile;
|
2010-08-20 01:49:53 +08:00
|
|
|
}
|
|
|
|
|