From fa1789be6d23fdf238835f65c9391b955c969e43 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 13 Aug 2010 14:53:52 +0200 Subject: [PATCH] portal luminaire, support for adjustments when importing from COLLADA, bugfixes --- SConstruct | 3 +- doc/format.tex | 5 +- include/mitsuba/render/scene.h | 2 +- schema/scene.xsd | 5 +- src/bsdfs/microfacet.cpp | 12 ++ src/collada/converter.cpp | 213 +++++++++++++++++++++++++++++---- src/collada/converter.h | 8 +- src/collada/main.cpp | 58 ++++++++- src/luminaires/portal.cpp | 194 ++++++++++++++++++++++++++++++ src/mitsuba/shandler.cpp | 23 ++++ src/qtgui/main.cpp | 2 +- 11 files changed, 488 insertions(+), 37 deletions(-) create mode 100644 src/luminaires/portal.cpp diff --git a/SConstruct b/SConstruct index 46219eeb..140cff55 100644 --- a/SConstruct +++ b/SConstruct @@ -409,7 +409,7 @@ if hasCollada: if env.has_key('COLLADALIB'): colladaEnv.Append(LIBS=env['COLLADALIB']) colladaConverter = colladaEnv.StaticObject('src/collada/converter.cpp') - colladaEnv.Program('mtsimport', darwinStub + ['src/collada/main.cpp', colladaConverter]) + colladaEnv.Program('mtsimport', darwinStub + ['src/collada/main.cpp', colladaConverter] + resources) if hasQt: qtEnv = mainEnv.Clone() @@ -529,6 +529,7 @@ plugins += env.SharedLibrary('plugins/spot', ['src/luminaires/spot.cpp']) plugins += env.SharedLibrary('plugins/point', ['src/luminaires/point.cpp']) plugins += env.SharedLibrary('plugins/collimated', ['src/luminaires/collimated.cpp']) plugins += env.SharedLibrary('plugins/directional', ['src/luminaires/directional.cpp']) +plugins += env.SharedLibrary('plugins/portal', ['src/luminaires/portal.cpp']) # Integrators plugins += env.SharedLibrary('plugins/direct', ['src/integrators/direct/direct.cpp']) diff --git a/doc/format.tex b/doc/format.tex index 19a1e500..21fffc28 100644 --- a/doc/format.tex +++ b/doc/format.tex @@ -133,10 +133,11 @@ Passing strings is very straightforward: \end{xml} \subsubsection{Color spectra} There are several different ways of passing color spectra to objects, which can be used interchangeably. -The most basic one is to supply linear RGB values as floating-point triplets or hex values +The most basic one is to supply linear RGB or sRGB values as floating-point triplets or hex values \begin{xml} - + + \end{xml} When Mitsuba is compiled with the default settings, it internally uses linear RGB to represent colors, so these values are directly used. The renderer can also be configured to sample the color spectrum using a specified diff --git a/include/mitsuba/render/scene.h b/include/mitsuba/render/scene.h index f2e02750..b1a405de 100644 --- a/include/mitsuba/render/scene.h +++ b/include/mitsuba/render/scene.h @@ -216,7 +216,7 @@ public: * will be stored in eRec. */ void sampleEmissionArea(EmissionRecord &lRec, Point2 &sample) const; - + /** * As above, but handles only the directional part. Must be called *after* * sampleEmissionArea(). The return value is to be understood as a BRDF, diff --git a/schema/scene.xsd b/schema/scene.xsd index a3c88189..33b92dff 100644 --- a/schema/scene.xsd +++ b/schema/scene.xsd @@ -22,6 +22,7 @@ + @@ -40,6 +41,7 @@ + @@ -83,6 +85,7 @@ + @@ -97,7 +100,7 @@ - + diff --git a/src/bsdfs/microfacet.cpp b/src/bsdfs/microfacet.cpp index 7e3fb139..0dae7323 100644 --- a/src/bsdfs/microfacet.cpp +++ b/src/bsdfs/microfacet.cpp @@ -231,6 +231,18 @@ public: return Spectrum(0.0f); } + + void addChild(const std::string &name, ConfigurableObject *child) { + if (child->getClass()->derivesFrom(Texture::m_theClass) && name == "diffuseReflectance") { + m_diffuseReflectance = static_cast(child); + m_usesRayDifferentials |= m_diffuseReflectance->usesRayDifferentials(); + } else if (child->getClass()->derivesFrom(Texture::m_theClass) && name == "specularReflectance") { + m_specularReflectance = static_cast(child); + m_usesRayDifferentials |= m_specularReflectance->usesRayDifferentials(); + } else { + BSDF::addChild(name, child); + } + } void serialize(Stream *stream, InstanceManager *manager) const { BSDF::serialize(stream, manager); diff --git a/src/collada/converter.cpp b/src/collada/converter.cpp index c0926fa1..28f2c6f6 100644 --- a/src/collada/converter.cpp +++ b/src/collada/converter.cpp @@ -1,6 +1,20 @@ #define BOOST_FILESYSTEM_NO_LIB #define BOOST_SYSTEM_NO_LIB +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -19,6 +33,8 @@ #include #endif +XERCES_CPP_NAMESPACE_USE + #include "converter.h" typedef std::map StringMap; @@ -80,7 +96,87 @@ struct VertexData { delete[] glPos; } }; - + +class ImporterDOMErrorHandler : public DOMErrorHandler { +public: + inline ImporterDOMErrorHandler() { } + + bool handleError(const DOMError& domError) { + ELogLevel logLevel; + + if (domError.getSeverity() == DOMError::DOM_SEVERITY_WARNING) + logLevel = EWarn; + else + logLevel = EError; + + SLog(logLevel, "%s (line %i, char %i): %s", + XMLString::transcode(domError.getLocation()->getURI()), + domError.getLocation()->getLineNumber(), + domError.getLocation()->getColumnNumber(), + XMLString::transcode(domError.getMessage())); + return true; + } +}; + +void findRemovals(DOMNode *node, std::set &removals) { + if (node) { + if (node->getNodeType() == DOMNode::ELEMENT_NODE && node->hasAttributes()) { + DOMNamedNodeMap *attributes = node->getAttributes(); + for (size_t i=0; igetLength(); ++i) { + DOMAttr *attribute = (DOMAttr*) attributes->item(i); + char *name = XMLString::transcode(attribute->getName()); + char *value = XMLString::transcode(attribute->getValue()); + + if (strcmp(name, "id") == 0) + removals.insert(value); + + XMLString::release(&name); + XMLString::release(&value); + } + } + for (DOMNode *child = node->getFirstChild(); child != 0; child=child->getNextSibling()) + findRemovals(child, removals); + } +} + +bool cleanupPass(DOMNode *node, const std::set &removals) { + if (node) { + char *nodeName = XMLString::transcode(node->getNodeName()); + if (strcmp(nodeName, "ref") == 0) { + XMLString::release(&nodeName); + return false; + } + + if (node->getNodeType() == DOMNode::ELEMENT_NODE && node->hasAttributes()) { + DOMNamedNodeMap *attributes = node->getAttributes(); + for (size_t i=0; igetLength(); ++i) { + DOMAttr *attribute = (DOMAttr*) attributes->item(i); + char *name = XMLString::transcode(attribute->getName()); + char *value = XMLString::transcode(attribute->getValue()); + + if (strcmp(name, "id") == 0 && removals.find(value) != removals.end()) { + XMLString::release(&name); + XMLString::release(&value); + return true; /* Remove this node */ + } + + XMLString::release(&name); + XMLString::release(&value); + } + XMLString::release(&nodeName); + } + DOMNode *child = node->getFirstChild(); + while (child) { + DOMNode *next = child->getNextSibling(); + bool doRemove = cleanupPass(child, removals); + if (doRemove) + node->removeChild(child); + child = next; + } + } + return false; +} + /* This code is not thread-safe for now */ GLUtesselator *tess = NULL; std::vector tess_data; @@ -383,7 +479,7 @@ void loadGeometry(std::string nodeName, Transform transform, std::ostream &os, d } } -void loadMaterialParam(std::ostream &os, const std::string &name, StringMap &idToTexture, +void loadMaterialParam(ColladaConverter *cvt, std::ostream &os, const std::string &name, StringMap &idToTexture, domCommon_color_or_texture_type *value, bool handleRefs) { if (!value) return; @@ -393,8 +489,11 @@ void loadMaterialParam(std::ostream &os, const std::string &name, StringMap &idT value->getTexture().cast(); if (color && !handleRefs) { domFloat4 &colValue = color->getValue(); - os << "\t\tm_srgb) + os << "\t\t" << endl; } else if (texture && handleRefs) { if (idToTexture.find(texture->getTexture()) == idToTexture.end()) { @@ -406,7 +505,7 @@ void loadMaterialParam(std::ostream &os, const std::string &name, StringMap &idT } } -void loadMaterialParam(std::ostream &os, const std::string &name, StringMap &, +void loadMaterialParam(ColladaConverter *cvt, std::ostream &os, const std::string &name, StringMap &, domCommon_float_or_param_type *value, bool handleRef) { if (!value) return; @@ -417,7 +516,7 @@ void loadMaterialParam(std::ostream &os, const std::string &name, StringMap &, } } -void loadMaterial(std::ostream &os, domMaterial &mat, StringMap &_idToTexture) { +void loadMaterial(ColladaConverter *cvt, std::ostream &os, domMaterial &mat, StringMap &_idToTexture) { SLog(EInfo, "Converting material \"%s\" ..", mat.getName()); StringMap idToTexture = _idToTexture; @@ -485,24 +584,24 @@ void loadMaterial(std::ostream &os, domMaterial &mat, StringMap &_idToTexture) { } if (isDiffuse) { os << "\t" << endl; - loadMaterialParam(os, "reflectance", idToTexture, diffuse, false); - loadMaterialParam(os, "reflectance", idToTexture, diffuse, true); + loadMaterialParam(cvt, os, "reflectance", idToTexture, diffuse, false); + loadMaterialParam(cvt, os, "reflectance", idToTexture, diffuse, true); os << "\t" << endl << endl; } else { os << "\t" << 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); + loadMaterialParam(cvt, os, "diffuseReflectance", idToTexture, diffuse, false); + loadMaterialParam(cvt, os, "specularReflectance", idToTexture, specular, false); + loadMaterialParam(cvt, os, "exponent", idToTexture, shininess, false); + loadMaterialParam(cvt, os, "diffuseReflectance", idToTexture, diffuse, true); + loadMaterialParam(cvt, os, "specularReflectance", idToTexture, specular, true); + loadMaterialParam(cvt, os, "exponent", idToTexture, shininess, true); os << "\t" << endl << endl; } } else if (lambert) { domCommon_color_or_texture_type* diffuse = lambert->getDiffuse(); os << "\t" << endl; - loadMaterialParam(os, "reflectance", idToTexture, diffuse, false); - loadMaterialParam(os, "reflectance", idToTexture, diffuse, true); + loadMaterialParam(cvt, os, "reflectance", idToTexture, diffuse, false); + loadMaterialParam(cvt, os, "reflectance", idToTexture, diffuse, true); os << "\t" << endl << endl; } else { SLog(EError, "Material type not supported! (must be Lambertian/Phong)"); @@ -670,8 +769,9 @@ void loadCamera(Transform transform, std::ostream &os, domCamera &camera) { os << "\t\t" << endl; os << "\t\t\t" << endl; os << "\t\t" << endl << endl; - - os << "\t\t" << endl << endl; + os << "\t\t" << endl; + os << "\t\t\t" << endl; + os << "\t\t" << endl << endl; os << "\t\t" << endl; os << "\t\t\t" << endl; os << "\t\t\t" << endl; @@ -876,8 +976,8 @@ void ColladaConverter::convert(const std::string &inputFile, domNode_Array &nodes = visualScene->getNode_array(); os << "" << endl << endl; - os << "" << endl << endl; os << "" << endl; os << "\t" << endl << endl; @@ -896,7 +996,7 @@ void ColladaConverter::convert(const std::string &inputFile, for (size_t i=0; igetMaterial_array(); for (size_t j=0; jcreateDOMBuilder(DOMImplementationLS::MODE_SYNCHRONOUS, 0); + + ImporterDOMErrorHandler errorHandler; + parser->setErrorHandler(&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); + DOMDocument *doc = parser->parse(*wrapper); + DOMDocument *adj = parser->parseURI(adjustmentFile.c_str()); - std::ofstream ofile(outputFile.c_str()); - if (ofile.fail()) - SLog(EError, "Could not write to \"%s\"!", outputFile.c_str()); - ofile << os.str(); - ofile.close(); + std::set removals; + findRemovals(adj, removals); + cleanupPass(doc, removals); + + 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"); + } + + for (DOMNode *child = adjRoot->getFirstChild(); child != 0; child=child->getNextSibling()) + docRoot->insertBefore(doc->importNode(child, true), insertBeforeNode); + + DOMWriter *serializer = ((DOMImplementationLS*)impl)->createDOMWriter(); + serializer->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint, true); + serializer->setErrorHandler(&errorHandler); + XMLFormatTarget *target = new LocalFileFormatTarget(outputFile.c_str()); + serializer->writeNode(target, *doc); + + delete wrapper; + delete memBufIS; + delete serializer; + parser->release(); + } else { + std::ofstream ofile(outputFile.c_str()); + if (ofile.fail()) + SLog(EError, "Could not write to \"%s\"!", outputFile.c_str()); + ofile << os.str(); + ofile.close(); + } } diff --git a/src/collada/converter.h b/src/collada/converter.h index c53db038..e06e01f9 100644 --- a/src/collada/converter.h +++ b/src/collada/converter.h @@ -4,7 +4,9 @@ using namespace mitsuba; class ColladaConverter { public: - inline ColladaConverter() { } + inline ColladaConverter() { + m_srgb = false; + } void convert(const std::string &inputFile, const std::string &outputDirectory, @@ -12,4 +14,8 @@ public: const std::string &adjustmentFile); virtual std::string locateResource(const std::string &resource) = 0; + + void setSRGB(bool srgb) { m_srgb = srgb; } +public: + bool m_srgb; }; diff --git a/src/collada/main.cpp b/src/collada/main.cpp index bd3ea3e0..3a7ac370 100644 --- a/src/collada/main.cpp +++ b/src/collada/main.cpp @@ -29,10 +29,14 @@ * (e.g. using Lighting/shading -> Batch bake in Maya). */ +#include +#include #include "converter.h" #include #include +XERCES_CPP_NAMESPACE_USE + class ConsoleColladaConverter : public ColladaConverter { public: inline ConsoleColladaConverter() { @@ -43,21 +47,57 @@ public: } }; +void help() { + cout << "COLLADA 1.4 Importer, Version " MTS_VERSION ", Copyright (c) " MTS_YEAR " Wenzel Jakob" << endl + << "Syntax: mtsimport [options] [Adjustment file]" << endl + << "Options/Arguments:" << endl + << " -h Display this help text" << endl << endl + << " -s Assume that colors are in sRGB space." << endl << endl + << "Please see the documentation for more information." << endl; +} + int colladaMain(int argc, char **argv) { - if (argc < 3) { - cout << "Syntax: mtsimport [Adjustment file]" << endl - << "Please see the documentation for more information." << endl; + bool srgb = false; + char optchar; + + optind = 1; + + while ((optchar = getopt(argc, argv, "sh")) != -1) { + switch (optchar) { + case 's': + srgb = true; + break; + case 'h': + default: + help(); + return -1; + } + }; + + if (argc-optind < 2) { + help(); return -1; } ConsoleColladaConverter converter; - converter.convert(argv[1], "", argv[2], argc > 3 ? argv[3] : ""); + converter.setSRGB(srgb); + converter.convert(argv[optind], "", argv[optind+1], argc > optind+2 ? argv[optind+2] : ""); return 0; } int ubi_main(int argc, char **argv) { int retval; + + /* Initialize Xerces-C */ + try { + XMLPlatformUtils::Initialize(); + } catch(const XMLException &toCatch) { + fprintf(stderr, "Error during Xerces initialization: %s", + XMLString::transcode(toCatch.getMessage())); + return -1; + } + /* Initialize the core framework */ Class::staticInitialization(); Statistics::staticInitialization(); @@ -112,10 +152,20 @@ int ubi_main(int argc, char **argv) { } catch (const std::exception &e) { std::cerr << "Caught a critical exeption: " << e.what() << std::endl; retval = -1; + } catch(const XMLException &toCatch) { + SLog(EError, "Caught a Xerces exception: %s", + XMLString::transcode(toCatch.getMessage())); + retval = -1; + } catch(const DOMException &toCatch) { + SLog(EError, "Caught a Xerces exception: %s", + XMLString::transcode(toCatch.getMessage())); + retval = -1; } catch (...) { std::cerr << "Caught a critical exeption of unknown type!" << endl; retval = -1; } + + XMLPlatformUtils::Terminate(); /* Shutdown the core framework */ Spectrum::staticShutdown(); diff --git a/src/luminaires/portal.cpp b/src/luminaires/portal.cpp new file mode 100644 index 00000000..f7ec1c07 --- /dev/null +++ b/src/luminaires/portal.cpp @@ -0,0 +1,194 @@ +#include +#include + +MTS_NAMESPACE_BEGIN + +/** + * Portal luminaire -- can be used to turn a surface into a + * portal that exposes luminaires behind it, such as an + * environment map. This is often necessary to make interior + * lighting work efficiently enough when using algorithms like + * path tracing or photon mapping. + */ +class PortalLuminaire : public Luminaire { +public: + PortalLuminaire(const Properties &props) : Luminaire(props), m_shape(NULL) { + AssertEx(m_luminaireToWorld.isIdentity(), "Error: non-identity transformation found. " + "Portal luminaires inherit their transformation from the associated shape!"); + m_type = EDiffuseDirection | EOnSurface; + m_intersectable = true; + } + + virtual ~PortalLuminaire() { + for (size_t i=0; idecRef(); + } + + PortalLuminaire(Stream *stream, InstanceManager *manager) + : Luminaire(stream, manager) { + m_shape = static_cast(manager->getInstance(stream)); + int luminaireCount = stream->readInt(); + for (int i=0; i(manager->getInstance(stream))); + configure(); + } + + void preprocess(const Scene *scene) { + for (size_t i=0; ipreprocess(scene); + configure(); + } + + void serialize(Stream *stream, InstanceManager *manager) const { + Luminaire::serialize(stream, manager); + + manager->serialize(stream, m_shape); + stream->writeInt((int) m_luminaires.size()); + for (size_t i=0; iserialize(stream, m_luminaires[i]); + } + + void addChild(const std::string &name, ConfigurableObject *child) { + const Class *cClass = child->getClass(); + if (cClass->derivesFrom(Luminaire::m_theClass)) { + Luminaire *luminaire = static_cast(child); + m_luminaires.push_back(luminaire); + luminaire->incRef(); + } else { + Luminaire::addChild(name, child); + } + } + + void setParent(ConfigurableObject *parent) { + ConfigurableObject::setParent(parent); + + if (parent->getClass()->derivesFrom(Shape::m_theClass)) { + m_shape = static_cast(parent); + parent->configure(); + m_surfaceArea = m_shape->getSurfaceArea(); + } else { + Log(EError, "An portal light source must be child of a shape instance"); + } + } + + void configure() { + m_power = Spectrum(0.0f); + if (m_luminaires.size() == 0) + Log(EError, "Portal luminaire must have one or more child luminaires!"); + for (size_t i=0; iconfigure(); + m_power += m_luminaires[i]->getPower(); + } + } + + Spectrum getPower() const { + return m_power; + } + + Spectrum Le(const EmissionRecord &eRec) const { + Spectrum result(0.0f); + + if (dot(eRec.d, eRec.sRec.n) <= 0) + return Spectrum(0.0f); + + for (size_t i=0; iLe(Ray(eRec.sRec.p, -eRec.d)); + + return result; + } + + Spectrum Le(const LuminaireSamplingRecord &lRec) const { + Spectrum result(0.0f); + + if (dot(lRec.d, lRec.sRec.n) <= 0) + return Spectrum(0.0f); + + for (size_t i=0; iLe(Ray(lRec.sRec.p, -lRec.d)); + + return result; + } + + inline void sample(const Point &p, LuminaireSamplingRecord &lRec, + const Point2 &sample) const { + lRec.pdf = m_shape->sampleSolidAngle(lRec.sRec, p, sample); + lRec.d = p - lRec.sRec.p; + + if (EXPECT_TAKEN(lRec.pdf > 0 && dot(lRec.d, lRec.sRec.n) > 0)) { + lRec.d = normalize(lRec.d); + lRec.Le = Le(lRec); + } else { + lRec.pdf = 0; + } + } + + inline void sample(const Intersection &its, LuminaireSamplingRecord &lRec, + const Point2 &sample) const { + PortalLuminaire::sample(its.p, lRec, sample); + } + + inline Float pdf(const Point &p, const LuminaireSamplingRecord &lRec) const { + return m_shape->pdfSolidAngle(lRec.sRec, p); + } + + Float pdf(const Intersection &its, const LuminaireSamplingRecord &lRec) const { + return pdf(its.p, lRec); + } + + void sampleEmission(EmissionRecord &eRec, + const Point2 &sample1, const Point2 &sample2) const { + eRec.pdfArea = m_shape->sampleArea(eRec.sRec, sample1); + Vector wo = squareToHemispherePSA(sample2); + eRec.pdfDir = Frame::cosTheta(wo) * INV_PI; + eRec.d = Frame(eRec.sRec.n).toWorld(wo); + eRec.P = Le(eRec); + } + + void sampleEmissionArea(EmissionRecord &eRec, const Point2 &sample) const { + eRec.pdfArea = m_shape->sampleArea(eRec.sRec, sample); + eRec.P = 1.0f; + } + + Spectrum fArea(const EmissionRecord &eRec) const { + return 1.0f; + } + + Spectrum sampleEmissionDirection(EmissionRecord &eRec, const Point2 &sample) const { + Vector wo = squareToHemispherePSA(sample); + eRec.d = Frame(eRec.sRec.n).toWorld(wo); + eRec.pdfDir = Frame::cosTheta(wo) * INV_PI; + return Le(eRec); + } + + Spectrum f(const EmissionRecord &eRec) const { + Float dp = dot(eRec.sRec.n, eRec.d); + if (dp > 0) + return Le(eRec); + else + return Spectrum(0.0f); + } + + void pdfEmission(EmissionRecord &eRec) const { + Float dp = dot(eRec.sRec.n, eRec.d); + if (dp > 0) + eRec.pdfDir = dp * INV_PI; + else { + eRec.pdfDir = 0; + } + eRec.pdfArea = m_shape->pdfArea(eRec.sRec); + } + + std::string toString() const { + return "PortalLuminaire[]"; + } + + MTS_DECLARE_CLASS() +private: + const Shape *m_shape; + Spectrum m_power; + std::vector m_luminaires; +}; + +MTS_IMPLEMENT_CLASS_S(PortalLuminaire, false, Luminaire) +MTS_EXPORT_PLUGIN(PortalLuminaire, "Portal luminaire"); +MTS_NAMESPACE_END diff --git a/src/mitsuba/shandler.cpp b/src/mitsuba/shandler.cpp index cd72cf86..cc05c350 100644 --- a/src/mitsuba/shandler.cpp +++ b/src/mitsuba/shandler.cpp @@ -236,6 +236,29 @@ void SceneHandler::endElement(const XMLCh* const xmlName) { specValue.fromLinearRGB(value[0], value[1], value[2]); context.parent->properties.setSpectrum(context.attributes["name"], specValue); + } else if (name == "srgb") { + std::string valueStr = context.attributes["value"]; + std::vector tokens = tokenize(valueStr, ", "); + Float value[3]; + if (tokens.size() == 1 && tokens[0].length() == 7 && tokens[0][0] == '#') { + char *end_ptr = NULL; + /* Parse HTML-style hexadecimal colors */ + int encoded = strtol(tokens[0].c_str()+1, &end_ptr, 16); + if (*end_ptr != '\0') + SLog(EError, "Invalid sRGB value specified (in <%s>)", context.attributes["name"].c_str()); + value[0] = ((encoded & 0xFF0000) >> 16) / 255.0f; + value[1] = ((encoded & 0x00FF00) >> 8) / 255.0f; + value[2] = (encoded & 0x0000FF) / 255.0f; + } else { + if (tokens.size() != 3) + SLog(EError, "Invalid RGB value specified"); + for (int i=0; i<3; i++) + value[i] = parseFloat(name, tokens[i]); + } + Spectrum specValue; + specValue.fromSRGB(value[0], value[1], value[2]); + context.parent->properties.setSpectrum(context.attributes["name"], + specValue); } else if (name == "blackbody") { Float temperature = parseFloat(name, context.attributes["temperature"]); BlackBodySpectrum *spec = new BlackBodySpectrum(temperature); diff --git a/src/qtgui/main.cpp b/src/qtgui/main.cpp index 64366da7..f0814ce4 100644 --- a/src/qtgui/main.cpp +++ b/src/qtgui/main.cpp @@ -1,5 +1,5 @@ -#include #include +#include #include #include #if defined(__OSX__)