diff --git a/.hgignore b/.hgignore index b604ac32..c27dbffe 100644 --- a/.hgignore +++ b/.hgignore @@ -6,6 +6,7 @@ Mitsuba.app .*moc_.*\.cc$ .*qrc_.*\.cc$ .*\.dylib$ +\.DS_Store sconf_temp mitsuba mtsgui diff --git a/ChangeLog b/ChangeLog index a512ff7e..34369467 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,30 @@ +2010-08-05 Wenzel Jakob + + * constant.cpp, src/libhw/vpl.cpp: Support for image environment + sources and spot lights in the real-time preview. + +2010-08-03 Wenzel Jakob + + * mitsuba.cpp: Ability to run the 'mitsuba' execubable in arbitrary + locations on OSX while ensuring that it still finds all plugins etc. + + * mtsimport: COLLADA importer support for ambient lights and 'polylist' meshes. + + * scene.cpp: Create a default camera when none is specified in the XML file. + + * constant.cpp, src/libhw/vpl.cpp: Support for rendering constant environment + sources in the real-time preview. + +2010-07-21 Wenzel Jakob + + * glwidget.cpp, mainwindow.cpp: Support for Drag & Drop + + * glwidget.cpp, mainwindow.cpp: The Mitsuba user interface now also + doubles as an EXR viewer / tonemapper. Drag an EXR file onto the + UI or open it using the File menu, and the image opens in a new + tab. Afterwards, it is possible to export the image as a tonemapped + 8-bit PNG image. + 2010-07-19 Wenzel Jakob * include/mitsuba/mitsuba.h: First semi-official external release 0.1.1 diff --git a/SConstruct b/SConstruct index 662169f0..0f3d821e 100644 --- a/SConstruct +++ b/SConstruct @@ -2,6 +2,7 @@ import SCons import sys import glob import os +import fnmatch if not os.path.exists('config.py'): print 'A configuration file must be selected! Have a look at \"README\"' @@ -142,7 +143,7 @@ if not conf.CheckCXXHeader('dae.h'): hasCollada = False print 'COLLADA DOM is missing: not building the COLLADA importer' if not conf.CheckCXXHeader('boost/math/distributions/students_t.hpp'): - print 'Boost is missing (install libboost1.37-dev and libboost-math1.37-dev)!' + print 'Boost is missing (install libboost1.40-dev and libboost-math1.40-dev)!' Exit(1) if sys.platform == 'win32': if not (conf.CheckCHeader(['windows.h', 'GL/gl.h']) and conf.CheckCHeader(['windows.h', 'GL/glu.h']) and conf.CheckCHeader(['windows.h', 'GL/gl.h', 'GL/glext.h'])): @@ -469,6 +470,7 @@ plugins += env.SharedLibrary('plugins/serialized', ['src/shapes/serialized.cpp'] plugins += env.SharedLibrary('plugins/sphere', ['src/shapes/sphere.cpp']) plugins += env.SharedLibrary('plugins/cylinder', ['src/shapes/cylinder.cpp']) plugins += env.SharedLibrary('plugins/hair', ['src/shapes/hair.cpp']) +#plugins += env.SharedLibrary('plugins/group', ['src/shapes/group.cpp']) # Samplers plugins += env.SharedLibrary('plugins/independent', ['src/samplers/independent.cpp']) @@ -671,7 +673,9 @@ elif sys.platform == 'darwin': installTargets += env.Install('Mitsuba.app/Contents/Resources/PreviewSettings.nib', 'tools/darwin/PreviewSettings.nib/keyedobjects.nib') installTargets += env.Install('Mitsuba.app/Contents/Resources', 'tools/darwin/qt.conf') installTargets += env.Install('Mitsuba.app/Contents/Frameworks/BWToolkitFramework.framework/Versions/A', 'tools/darwin/BWToolkitFramework.framework/Versions/A/BWToolkitFramework') - installTargets += env.Install('Mitsuba.app/Contents/Frameworks/BWToolkitFramework.framework', 'tools/darwin/BWToolkitFramework.framework/Versions/A/Resources') + for file in os.listdir('tools/darwin/BWToolkitFramework.framework/Versions/A/Resources'): + if fnmatch.fnmatch(file, '*.pdf') or fnmatch.fnmatch(file, '*.tiff') or fnmatch.fnmatch(file, '*.tif') or fnmatch.fnmatch(file, '*.png') or fnmatch.fnmatch(file, '*.rtf') or fnmatch.fnmatch(file, '*.plist'): + installTargets += env.Install('Mitsuba.app/Contents/Frameworks/BWToolkitFramework.framework/Resources', 'tools/darwin/BWToolkitFramework.framework/Versions/A/Resources/' + file) if dist: if sys.platform == 'win32': diff --git a/include/mitsuba/hw/gputexture.h b/include/mitsuba/hw/gputexture.h index 3f6123e9..608254e6 100644 --- a/include/mitsuba/hw/gputexture.h +++ b/include/mitsuba/hw/gputexture.h @@ -67,8 +67,10 @@ public: enum EWrapType { /// Clamp the coordinates to [0, 1] EClamp = 0, - /// Clamp the coordinates to [2n, 1-2n] (Avoids edge artifacts) + /// Similar to EClamp, but prevents mixing at the edges EClampToEdge, + /// Similar to EClamp + EClampToBorder, /// Modulo 1 operation (default) ERepeat, /// Mirror the coordinates at the edges diff --git a/include/mitsuba/hw/vpl.h b/include/mitsuba/hw/vpl.h index 6cfab593..446155ca 100644 --- a/include/mitsuba/hw/vpl.h +++ b/include/mitsuba/hw/vpl.h @@ -25,12 +25,15 @@ public: void init(); /// Generate the shadow map for a particular VPL - bool setVPL(const VPL &vpl); + void setVPL(const VPL &vpl); /// Prepare for rendering a material with BSDF 'bsdf' illuminated by VPL 'vpl'. void configure(const VPL &vpl, const BSDF *bsdf, const Luminaire *luminaire, const Point &camPos); + /// Draw the background if there is an environment luminaire + void drawBackground(const Transform &clipToWorld, const Point &camPos); + /// Release bound resources void unbind(); @@ -229,6 +232,8 @@ private: std::map m_programs; ProgramAndConfiguration m_current; VPLProgramConfiguration m_targetConfig; + ref m_backgroundProgram; + VPLDependencyNode m_backgroundDependencies; }; MTS_NAMESPACE_END diff --git a/include/mitsuba/render/kdtree.h b/include/mitsuba/render/kdtree.h index 251e90d6..eb5e5d6d 100644 --- a/include/mitsuba/render/kdtree.h +++ b/include/mitsuba/render/kdtree.h @@ -81,7 +81,7 @@ MTS_NAMESPACE_BEGIN */ class MTS_EXPORT_RENDER KDTree : public Object { public: - /// Construct a new, unbuilt kd-tree + /// Construct a new kd-tree in an unbuilt state KDTree(); /// Add geometry to the kd-tree @@ -424,7 +424,7 @@ protected: */ bool rayIntersect(const Ray &ray, Intersection &its, Float mint, Float maxt, bool shadowRay, unsigned int &shapeIndex, unsigned int &primIndex) const; - + /// Recursive tree-building algorithm void buildTree(int nodeIndex, int depth, int badRefines, int numPrims, const AABB &aabb, EdgeEventVec3 &allEvents); diff --git a/include/mitsuba/render/records.h b/include/mitsuba/render/records.h index 74da54d9..5b85d3e6 100644 --- a/include/mitsuba/render/records.h +++ b/include/mitsuba/render/records.h @@ -172,12 +172,17 @@ public: struct MTS_EXPORT_RENDER EmissionRecord { public: + enum ESamplingType { + ENormal, + EPreview + }; + /// Construct a luminaire sampling record that can be used to query a luminaire inline EmissionRecord(const Luminaire *luminaire, const ShapeSamplingRecord &sRec, const Vector &d) - : luminaire(luminaire), sRec(sRec), d(d) { } + : luminaire(luminaire), type(ENormal), sRec(sRec), d(d) { } - inline EmissionRecord() : luminaire(NULL) { } + inline EmissionRecord() : luminaire(NULL), type(ENormal) { } /// Return a string representation std::string toString() const; @@ -185,6 +190,8 @@ public: /// Associated luminaire const Luminaire *luminaire; + ESamplingType type; + /// Data record of the associated shape sample ShapeSamplingRecord sRec; diff --git a/schema/scene.xsd b/schema/scene.xsd index 6e01a9a9..a3c88189 100644 --- a/schema/scene.xsd +++ b/schema/scene.xsd @@ -223,7 +223,7 @@ - + diff --git a/src/collada/main.cpp b/src/collada/main.cpp index 2e86c4ad..1f9ba9ad 100644 --- a/src/collada/main.cpp +++ b/src/collada/main.cpp @@ -9,9 +9,9 @@ * - Arbitrary polygonal meshes * - Lambert and Phong materials (allowed to be textured) * - Cameras - * - Spot and Point lights + * - Spot and Point and Ambient lights * - * When exporting from Maya/FBX, be sure to have it convert all NURBS surfaces into + * When exporting using Maya/FBX, be sure to have it convert all NURBS surfaces into * "Software Render Meshes". Triangulation is not required (the code below does this * automatically for arbitrary polygonal meshes). The Light and camera export options * should be activated, since they are off by default. While modeling the scene, it is @@ -183,7 +183,7 @@ VertexData *fetchVertexData(Transform transform, std::ostream &os, result->offsetToType[offset] = ENormal; result->typeToOffset[ENormal] = offset; } else if (!strcmp(inputs[i]->getSemantic(), "TEXCOORD")) { - SAssert(accessor->getStride() == 2); + SAssert(accessor->getStride() == 2 || accessor->getStride() == 3); if (result->typeToOffset[EUV] == -1) { result->hasUVs = true; result->offsetToType[offset] = EUV; @@ -294,9 +294,11 @@ void writeGeometry(std::string id, int geomIndex, std::string matID, Transform t os << "\t" << endl; os << "\t\t" << endl; - os << "\t\t" << endl; - os << "\t\t\t" << endl; - os << "\t\t" << endl; + if (!transform.isIdentity()) { + os << "\t\t" << endl; + os << "\t\t\t" << endl; + os << "\t\t" << endl; + } os << "\t\t" << endl; os << "\t" << endl << endl; } @@ -357,7 +359,7 @@ void loadGeometry(std::string nodeName, Transform transform, std::ostream &os, d gluTessEndPolygon(tess); delete[] temp; } - + if (polygons->getMaterial() == NULL) SLog(EError, "No material reference specified!"); @@ -368,6 +370,46 @@ void loadGeometry(std::string nodeName, Transform transform, std::ostream &os, d delete data; ++geomIndex; } + + domPolylist_Array &polylistArray = mesh->getPolylist_array(); + for (size_t i=0; igetInput_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; jnSources]; + for (size_t l = 0; lnSources; ++l) + temp[l] = indexArray.get(indexOffset++); + + gluTessBeginPolygon(tess, NULL); + gluTessBeginContour(tess); + + for (size_t k=0; knSources; k+=data->nSources) + gluTessVertex(tess, &data->glPos[temp[k+posOffset]*3], (GLvoid *) (k+temp)); + + gluTessEndContour(tess); + gluTessEndPolygon(tess); + delete[] temp; + } + + if (polylist->getMaterial() == NULL) + SLog(EError, "No material reference specified!"); + + if (matLookupTable.find(polylist->getMaterial()) == matLookupTable.end()) + SLog(EError, "Referenced material could not be found!"); + + writeGeometry(xmlName, geomIndex, matLookupTable[polylist->getMaterial()], transform, os, data); + delete data; + ++geomIndex; + } } void loadMaterialParam(std::ostream &os, const std::string &name, StringMap &idToTexture, @@ -511,9 +553,15 @@ void loadLight(Transform transform, std::ostream &os, domLight &light) { os << "\t\t" << endl; os << "\t" << endl << endl; } - if (!point && !spot) { - SLog(EWarn, "Encountered an unknown light type!"); + domLight::domTechnique_common::domAmbient *ambient = light.getTechnique_common()->getAmbient().cast(); + if (ambient) { + domFloat3 &color = ambient->getColor()->getValue(); + os << "\t" << endl; + os << "\t\t" << endl; + os << "\t" << endl << endl; } + if (!point && !spot && !ambient) + SLog(EWarn, "Encountered an unknown light type!"); } void loadImage(std::ostream &os, domImage &image, StringMap &idToTexture, StringMap &fileToId) { @@ -667,6 +715,8 @@ void loadNode(Transform transform, std::ostream &os, domNode &node) { matLookupTable[instMat->getSymbol()] = material->getId(); } + if (!geom) + SLog(EError, "Could not find a referenced geometry object!"); loadGeometry(node.getName(), transform, os, *geom, matLookupTable); } @@ -675,6 +725,8 @@ void loadNode(Transform transform, std::ostream &os, domNode &node) { for (size_t i=0; i(inst->getUrl().getElement()); + if (!light) + SLog(EError, "Could not find a referenced light!"); loadLight(transform, os, *light); } @@ -683,6 +735,8 @@ void loadNode(Transform transform, std::ostream &os, domNode &node) { for (size_t i=0; i(inst->getUrl().getElement()); + if (camera == NULL) + SLog(EError, "Could not find a referenced camera!"); loadCamera(transform, os, *camera); } diff --git a/src/integrators/vpl/vpl.cpp b/src/integrators/vpl/vpl.cpp index 3d23a9cf..3d312440 100644 --- a/src/integrators/vpl/vpl.cpp +++ b/src/integrators/vpl/vpl.cpp @@ -32,8 +32,11 @@ public: /// Draw the full scene using additive blending and shadow maps void drawShadowedScene(const Scene *scene, const VPL &vpl) { + const ProjectiveCamera *camera = static_cast(scene->getCamera()); Point2 jitter(0.5f - m_random->nextFloat(), 0.5f - m_random->nextFloat()); - m_renderer->setCamera(static_cast(scene->getCamera()), jitter); + Transform projectionTransform = camera->getGLProjectionTransform(jitter); + m_renderer->setCamera(projectionTransform.getMatrix(), camera->getViewTransform().getMatrix()); + Transform clipToWorld = camera->getViewTransform().inverse() * projectionTransform.inverse(); const std::vector meshes = scene->getMeshes(); Point camPos = scene->getCamera()->getPosition(); @@ -45,6 +48,7 @@ public: m_shaderManager->unbind(); } m_renderer->endDrawingMeshes(); + m_shaderManager->drawBackground(clipToWorld, camPos); } void preprocess(const Scene *scene, RenderQueue *queue, const RenderJob *job, @@ -130,8 +134,7 @@ public: m_renderer->setDepthMask(true); m_renderer->setDepthTest(true); m_renderer->setBlendMode(Renderer::EBlendNone); - if (!m_shaderManager->setVPL(vpl)) - continue; + m_shaderManager->setVPL(vpl); m_framebuffer->activateTarget(); m_framebuffer->clear(); diff --git a/src/libcore/bitmap.cpp b/src/libcore/bitmap.cpp index 2169c203..5c4492aa 100644 --- a/src/libcore/bitmap.cpp +++ b/src/libcore/bitmap.cpp @@ -445,6 +445,7 @@ void Bitmap::savePNG(Stream *stream) const { png_destroy_write_struct(&png_ptr, &info_ptr); Log(EError, "Error writing the PNG file"); } + png_set_write_fn(png_ptr, stream, (png_rw_ptr) png_write_data, (png_flush_ptr) png_flush_data); // png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); png_set_compression_level(png_ptr, 5); diff --git a/src/libcore/util.cpp b/src/libcore/util.cpp index 79faccae..37f4f883 100644 --- a/src/libcore/util.cpp +++ b/src/libcore/util.cpp @@ -238,10 +238,12 @@ std::string getFQDN() { hints.ai_protocol = IPPROTO_TCP; int retVal = getaddrinfo(getHostName().c_str(), NULL, &hints, &addrInfo); - if (addrInfo == NULL || retVal != 0) - SLog(EError, "Could not retrieve the computer's fully " + if (addrInfo == NULL || retVal != 0) { + SLog(EWarn, "Could not retrieve the computer's fully " "qualified domain name: could not resolve host address \"%s\"!", getHostName().c_str()); + return getHostName(); + } char fqdn[NI_MAXHOST]; retVal = getnameinfo(addrInfo->ai_addr, sizeof(struct sockaddr_in), @@ -249,12 +251,13 @@ std::string getFQDN() { if (retVal != 0) { freeaddrinfo(addrInfo); #if defined(WIN32) - SLog(EError, "Could not retrieve the computer's fully " + SLog(EWarn, "Could not retrieve the computer's fully " "qualified domain name: error %i!", WSAGetLastError()); #else - SLog(EError, "Could not retrieve the computer's fully " + SLog(EWarn, "Could not retrieve the computer's fully " "qualified domain name: error %i!", gai_strerror(retVal)); #endif + return getHostName(); } freeaddrinfo(addrInfo); diff --git a/src/libhw/glrenderer.cpp b/src/libhw/glrenderer.cpp index 691903eb..e36afc97 100644 --- a/src/libhw/glrenderer.cpp +++ b/src/libhw/glrenderer.cpp @@ -276,6 +276,7 @@ void GLRenderer::drawTriMesh(const TriMesh *mesh) { glEnableClientState(GL_TEXTURE_COORD_ARRAY); glTexCoordPointer(2, dataType, sizeof(Vertex), vertices + sizeof(Float) * 6); + /* Pass 'dpdu' as second set of texture coordinates */ glClientActiveTexture(GL_TEXTURE1); glTexCoordPointer(3, dataType, sizeof(Vertex), @@ -367,14 +368,15 @@ void GLRenderer::blitTexture(const GPUTexture *tex, bool flipVertically, if (flipVertically) std::swap(upperLeft.y, lowerRight.y); + const Float zDepth = -1.0f; // just before the far plane glTexCoord2f(0.0f, 0.0f); - glVertex2f(upperLeft.x, upperLeft.y); + glVertex3f(upperLeft.x, upperLeft.y, zDepth); glTexCoord2f(1.0f, 0.0f); - glVertex2f(lowerRight.x, upperLeft.y); + glVertex3f(lowerRight.x, upperLeft.y, zDepth); glTexCoord2f(1.0f, 1.0f); - glVertex2f(lowerRight.x, lowerRight.y); + glVertex3f(lowerRight.x, lowerRight.y, zDepth); glTexCoord2f(0.0f, 1.0f); - glVertex2f(upperLeft.x, lowerRight.y); + glVertex3f(upperLeft.x, lowerRight.y, zDepth); glEnd(); } else if (tex->getType() == GPUTexture::ETextureCubeMap) { glMatrixMode(GL_PROJECTION); @@ -457,15 +459,16 @@ void GLRenderer::blitQuad(bool flipVertically) { glOrtho(0, scrSize.x, scrSize.y, 0, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); + const Float zDepth = -1.0f; glBegin(GL_QUADS); glTexCoord2f(0.0f, flipVertically ? 1.0f : 0.0f); - glVertex2f(0.0f, 0.0f); + glVertex3f(0.0f, 0.0f, zDepth); glTexCoord2f(1.0f, flipVertically ? 1.0f : 0.0f); - glVertex2f(scrSize.x, 0.0f); + glVertex3f(scrSize.x, 0.0f, zDepth); glTexCoord2f(1.0f, flipVertically ? 0.0f : 1.0f); - glVertex2f(scrSize.x, scrSize.y); + glVertex3f(scrSize.x, scrSize.y, zDepth); glTexCoord2f(0.0f, flipVertically ? 0.0f : 1.0f); - glVertex2f(0.0f, scrSize.y); + glVertex3f(0.0f, scrSize.y, zDepth); glEnd(); } diff --git a/src/libhw/gltexture.cpp b/src/libhw/gltexture.cpp index 118b303c..53b5cea9 100644 --- a/src/libhw/gltexture.cpp +++ b/src/libhw/gltexture.cpp @@ -331,6 +331,7 @@ void GLTexture::configureTexture() { switch (getWrapType()) { case EClamp: wrap = GL_CLAMP; break; case EClampToEdge: wrap = GL_CLAMP_TO_EDGE; break; + case EClampToBorder: wrap = GL_CLAMP_TO_BORDER; break; case ERepeat: wrap = GL_REPEAT; break; case EMirroredRepeat: wrap = GL_MIRRORED_REPEAT_ARB; break; default: Log(EError, "Invalid texture wrap type specified"); return; diff --git a/src/libhw/gputexture.cpp b/src/libhw/gputexture.cpp index 2a0031d4..aae78629 100644 --- a/src/libhw/gputexture.cpp +++ b/src/libhw/gputexture.cpp @@ -132,6 +132,7 @@ static const char *toString(GPUTexture::EWrapType wrap) { switch (wrap) { case GPUTexture::EClamp: return "clamp"; case GPUTexture::EClampToEdge: return "clampToEdge"; + case GPUTexture::EClampToBorder: return "clampToBorder"; case GPUTexture::ERepeat: return "repeat"; case GPUTexture::EMirroredRepeat: return "mirroredRepeat"; default: SLog(EError, "Invalid texture wrap type"); return NULL; diff --git a/src/libhw/vpl.cpp b/src/libhw/vpl.cpp index c73fe2c8..5803f574 100644 --- a/src/libhw/vpl.cpp +++ b/src/libhw/vpl.cpp @@ -106,12 +106,47 @@ void VPLShaderManager::init() { if (shader != NULL && !shader->isComplete()) m_renderer->unregisterShaderForResource(meshes[i]->getBSDF()); } + for (size_t i=0; iregisterShaderForResource(luminaires[i]); + + if (m_scene->hasBackgroundLuminaire() && + m_renderer->getShaderForResource(m_scene->getBackgroundLuminaire()) != NULL) { + Shader *shader = m_renderer->getShaderForResource(m_scene->getBackgroundLuminaire()); + m_backgroundDependencies = VPLDependencyNode(shader); + int id = 0; + std::ostringstream oss; + std::string evalName = m_backgroundDependencies.recursiveGenerateCode(oss, id); + + m_backgroundProgram = m_renderer->createGPUProgram("Background program"); + m_backgroundProgram->setSource(GPUProgram::EVertexProgram, + "uniform mat4 clipToWorld;\n" + "varying vec3 d;\n" + "void main() {\n" + " gl_Position = ftransform();\n" + " vec4 tmp = clipToWorld * (gl_ModelViewProjectionMatrix * gl_Vertex);\n" + " d = tmp.xyz/tmp.w;" + "}\n" + ); + + oss << "varying vec3 d;" << endl + << "uniform vec3 camPos;" << endl + << "void main() {" << endl + << " gl_FragColor.rgb = " << evalName << "_background(normalize(d - camPos));" << endl + << " gl_FragColor.a = 1.0;" << endl + << "}" << endl; + + m_backgroundProgram->setSource(GPUProgram::EFragmentProgram, oss.str()); + m_backgroundProgram->init(); + + id = 0; + m_backgroundDependencies.recursiveResolve(m_backgroundProgram, id); + } + m_initialized = true; } -bool VPLShaderManager::setVPL(const VPL &vpl) { +void VPLShaderManager::setVPL(const VPL &vpl) { Point p = vpl.its.p + vpl.its.shFrame.n * 0.01; Intersection its; @@ -154,8 +189,12 @@ bool VPLShaderManager::setVPL(const VPL &vpl) { nearClip = std::min(nearClip, (Float) 0.001f); farClip = std::min(farClip * 1.5f, m_maxClipDist); - if (farClip < 0 || nearClip >= farClip) - return false; + if (farClip < 0 || nearClip >= farClip) { + /* Unable to find any surface - just default values based on the scene size */ + nearClip = 1e-3 * m_scene->getBSphere().radius; + farClip = 1e3 * m_scene->getBSphere().radius; + m_minDist = 0; + } m_nearClip = nearClip; m_invClipRange = 1/(farClip-nearClip); @@ -164,8 +203,8 @@ bool VPLShaderManager::setVPL(const VPL &vpl) { m_shadowMap->activateTarget(); if (m_singlePass && m_shadowProgram != NULL) { /* "Fancy": render the whole cube map in a single pass using - a geometry program. On anything but brand-new hardware, this - is actually slower. */ + a geometry program. On anything but brand-new hardware, this + is actually slower. */ m_shadowMap->activateSide(-1); m_shadowMap->clear(); @@ -221,8 +260,6 @@ bool VPLShaderManager::setVPL(const VPL &vpl) { m_altShadowProgram->unbind(); } m_shadowMap->releaseTarget(); - - return true; } void VPLShaderManager::configure(const VPL &vpl, const BSDF *bsdf, const Luminaire *luminaire, const Point &camPos) { @@ -319,6 +356,8 @@ void VPLShaderManager::configure(const VPL &vpl, const BSDF *bsdf, const Luminai << " vec3 wi = vec3(dot(S, nCamVec)," << endl << " dot(T, nCamVec)," << endl << " dot(N, nCamVec));" << endl + << " if (wi.z < 0)" << endl + << " discard;" << endl << " vec3 vplWo = -vec3(dot(vplS, nLightVec)," << endl << " dot(vplT, nLightVec)," << endl << " dot(vplN, nLightVec));" << endl @@ -331,7 +370,7 @@ void VPLShaderManager::configure(const VPL &vpl, const BSDF *bsdf, const Luminai oss << " * " << vplEvalName << "_dir(vplWo)" << endl; if (vpl.type == ESurfaceVPL || (vpl.type == ELuminaireVPL && (vpl.luminaire->getType() & Luminaire::EOnSurface))) - oss << " * vplPower * shadow * abs(cosTheta(wo)*cosTheta(vplWo)) / (d*d)"; + oss << " * vplPower * shadow * abs(cosTheta(wo) * cosTheta(vplWo)) / (d*d)"; else oss << " * vplPower * shadow * abs(cosTheta(wo)) / (d*d)"; if (luminaire != NULL) { @@ -343,6 +382,7 @@ void VPLShaderManager::configure(const VPL &vpl, const BSDF *bsdf, const Luminai } oss << " gl_FragColor.a = 1.0;" << endl << "}" << endl; + program->setSource(GPUProgram::EFragmentProgram, oss.str()); program->init(); @@ -389,6 +429,20 @@ void VPLShaderManager::configure(const VPL &vpl, const BSDF *bsdf, const Luminai m_targetConfig.bind(program, config, textureUnitOffset); } +void VPLShaderManager::drawBackground(const Transform &clipToWorld, const Point &camPos) { + if (m_backgroundProgram == NULL) + return; + int textureUnitOffset = 0; + m_backgroundProgram->bind(); + m_backgroundDependencies.recursiveBind(m_backgroundProgram, + m_backgroundDependencies, textureUnitOffset); + m_backgroundProgram->setParameter("clipToWorld", clipToWorld, false); + m_backgroundProgram->setParameter("camPos", camPos, false); + m_renderer->blitQuad(false); + m_backgroundProgram->unbind(); + m_backgroundDependencies.recursiveUnbind(); +} + void VPLShaderManager::unbind() { if (m_current.program && m_current.program->isBound()) { m_targetConfig.unbind(); @@ -407,6 +461,11 @@ void VPLShaderManager::cleanup() { if (m_shadowMap) m_shadowMap->cleanup(); + if (m_backgroundProgram) { + m_backgroundProgram->cleanup(); + m_backgroundProgram = NULL; + } + const std::vector meshes = m_scene->getMeshes(); const std::vector luminaires = m_scene->getLuminaires(); diff --git a/src/librender/camera.cpp b/src/librender/camera.cpp index 8fd8562d..91bb5c88 100644 --- a/src/librender/camera.cpp +++ b/src/librender/camera.cpp @@ -79,14 +79,16 @@ void ProjectiveCamera::configure() { /* Instantiate an EXR film by default */ m_film = static_cast (PluginManager::getInstance()-> createObject(Film::m_theClass, Properties("exrfilm"))); + m_film->configure(); } if (m_sampler == NULL) { /* No sampler has been selected - load an independent filter with 4 samples/pixel by default */ Properties props("independent"); - props.setInteger("samplesPerPixel", 4); + props.setInteger("sampleCount", 4); m_sampler = static_cast (PluginManager::getInstance()-> createObject(Sampler::m_theClass, props)); + m_sampler->configure(); } m_aspect = (Float) m_film->getSize().x / (Float) m_film->getSize().y; } diff --git a/src/librender/kdtree_coherent.cpp b/src/librender/kdtree_coherent.cpp index 89ffc3d7..76592b38 100644 --- a/src/librender/kdtree_coherent.cpp +++ b/src/librender/kdtree_coherent.cpp @@ -132,7 +132,6 @@ void KDTree::rayIntersectPacket(const Ray *rays, Intersection *its) const { const uint32_t shapeIndex = its4.shapeIndex.i[i]; const uint32_t primIndex = its4.primIndex.i[i]; const Shape *shape = m_shapes[shapeIndex]; - it.shape = shape; if (EXPECT_TAKEN(primIndex != KNoTriangleFlag)) { const TriMesh *triMesh = static_cast(m_shapes[shapeIndex]); @@ -166,6 +165,7 @@ void KDTree::rayIntersectPacket(const Ray *rays, Intersection *its) const { it.geoFrame.t = cross(it.shFrame.n, it.shFrame.s); it.wi = it.toLocal(-rayD); it.hasUVPartials = false; + it.shape = shape; } else { /* Non-triangle shape: intersect again to fill in details */ shape->rayIntersect(rays[i], it); diff --git a/src/librender/kdtree_traversal.cpp b/src/librender/kdtree_traversal.cpp index 791efbd2..72d52fb1 100644 --- a/src/librender/kdtree_traversal.cpp +++ b/src/librender/kdtree_traversal.cpp @@ -221,8 +221,6 @@ bool KDTree::rayIntersect(const Ray &ray, Intersection &its) const { unsigned int shapeIndex, primIndex; if (rayIntersect(ray, its, mint, maxt, false, shapeIndex, primIndex)) { const Shape *shape = m_shapes[shapeIndex]; - its.shape = shape; - if (EXPECT_NOT_TAKEN(its.t < mint || its.t > maxt)) // double-check without epsilons return false; @@ -266,6 +264,8 @@ bool KDTree::rayIntersect(const Ray &ray, Intersection &its) const { its.dpdu = v0.dpdu * b.x + v1.dpdu * b.y + v2.dpdu * b.z; its.dpdv = v0.dpdv * b.x + v1.dpdv * b.y + v2.dpdv * b.z; its.geoFrame.n = faceNormal; + its.shape = shape; + coordinateSystem(its.geoFrame.n, its.geoFrame.s, its.geoFrame.t); its.shFrame.n = normalize(v0.n * b.x + v1.n * b.y + v2.n * b.z); its.shFrame.s = normalize(its.dpdu - its.shFrame.n diff --git a/src/librender/preview.cpp b/src/librender/preview.cpp index 9a24bbd2..89279020 100644 --- a/src/librender/preview.cpp +++ b/src/librender/preview.cpp @@ -89,6 +89,7 @@ void PreviewWorker::processIncoherent(const WorkUnit *workUnit, WorkResult *work } else { EmissionRecord eRec(m_vpl.luminaire, ShapeSamplingRecord(m_vpl.its.p, m_vpl.its.shFrame.n), toIts); + eRec.type = EmissionRecord::EPreview; value += m_vpl.P * bsdfVal * m_vpl.luminaire->f(eRec) * ((m_vpl.luminaire->getType() == Luminaire::EOnSurface ? (Float) 1 : dot(m_vpl.its.shFrame.n, toIts)) / (length*length)); @@ -331,6 +332,7 @@ void PreviewWorker::processCoherent(const WorkUnit *workUnit, WorkResult *workRe } else { EmissionRecord eRec(m_vpl.luminaire, ShapeSamplingRecord(m_vpl.its.p, m_vpl.its.shFrame.n), wi); + eRec.type = EmissionRecord::EPreview; vplWeight = m_vpl.luminaire->f(eRec) * m_vpl.P; } } diff --git a/src/librender/scene.cpp b/src/librender/scene.cpp index 222f3b40..d2f1e135 100644 --- a/src/librender/scene.cpp +++ b/src/librender/scene.cpp @@ -176,12 +176,33 @@ void Scene::wakeup(std::map ¶ms) { } void Scene::configure() { - if (m_integrator == NULL) - Log(EError, "No integrator has been specified!"); - if (m_camera == NULL) - Log(EError, "No camera has been specified!"); + if (m_integrator == NULL) { + /* Create a direct integrator by default */ + m_integrator = static_cast (PluginManager::getInstance()-> + createObject(Integrator::m_theClass, Properties("direct"))); + m_integrator->configure(); + } + if (m_camera == NULL) { + Properties props("perspective"); + /* Create a perspective camera with 45deg. FOV, which can see the whole scene */ + AABB aabb; + for (size_t i=0; igetAABB()); + Point center = aabb.getCenter(); + Vector extents = aabb.getExtents(); + Float maxExtents = std::max(extents.x, extents.y); + Float distance = maxExtents/(2.0f * std::tan(45 * .5f * M_PI/180)); + + props.setTransform("toWorld", Transform::translate(Vector(center.x, center.y, aabb.getMinimum().x - distance))); + props.setFloat("fov", 45.0f); + + m_camera = static_cast (PluginManager::getInstance()->createObject(Camera::m_theClass, props)); + m_camera->configure(); + m_sampler = m_camera->getSamplerX(); + } + if (m_media.size() > 1) - Log(EError, "Scenes are currently restricted to at most one medium."); + Log(EError, "Scenes are currently restricted to at most one participating medium."); m_integrator->configureSampler(m_sampler); diff --git a/src/librender/vpl.cpp b/src/librender/vpl.cpp index cac6aaf8..7a2523c4 100644 --- a/src/librender/vpl.cpp +++ b/src/librender/vpl.cpp @@ -8,6 +8,7 @@ size_t generateVPLs(const Scene *scene, size_t offset, size_t count, int maxDept ref sampler = static_cast (PluginManager::getInstance()-> createObject(Sampler::m_theClass, Properties("halton"))); EmissionRecord eRec; + eRec.type = EmissionRecord::EPreview; Ray ray; Intersection its; Spectrum weight, bsdfVal; diff --git a/src/luminaires/constant.cpp b/src/luminaires/constant.cpp index e9c3d33e..e6ce68f4 100644 --- a/src/luminaires/constant.cpp +++ b/src/luminaires/constant.cpp @@ -1,4 +1,5 @@ #include +#include MTS_NAMESPACE_BEGIN @@ -9,7 +10,7 @@ class ConstantLuminaire : public Luminaire { public: ConstantLuminaire(const Properties &props) : Luminaire(props) { m_intensity = props.getSpectrum("intensity", 1.0f); - m_type = EDiffuseDirection | EOnSurface; + m_type = EOnSurface; } ConstantLuminaire(Stream *stream, InstanceManager *manager) @@ -26,7 +27,7 @@ public: void preprocess(const Scene *scene) { /* Get the scene's bounding sphere and slightly enlarge it */ m_bsphere = scene->getBSphere(); - m_bsphere.radius *= 1.01f; + m_bsphere.radius *= 2.0f; m_surfaceArea = m_bsphere.radius * m_bsphere.radius * M_PI; } @@ -87,6 +88,7 @@ public: */ void sampleEmission(EmissionRecord &eRec, const Point2 &sample1, const Point2 &sample2) const { + Assert(eRec.type == EmissionRecord::ENormal); /* Chord model - generate the ray passing through two uniformly distributed points on a sphere containing the scene */ Vector d = squareToSphere(sample1); @@ -109,15 +111,23 @@ public: } void sampleEmissionArea(EmissionRecord &eRec, const Point2 &sample) const { + Float radius = m_bsphere.radius; + if (eRec.type == EmissionRecord::EPreview) { + /* This is more suitable for VPL-based rendering */ + radius *= 10; + } Vector d = squareToSphere(sample); - eRec.sRec.p = m_bsphere.center + d * m_bsphere.radius; + eRec.sRec.p = m_bsphere.center + d * radius; eRec.sRec.n = Normal(-d); - eRec.pdfArea = 1.0f / (4 * M_PI * m_bsphere.radius * m_bsphere.radius); + eRec.pdfArea = 1.0f / (4 * M_PI * radius * radius); eRec.P = m_intensity * M_PI; } Spectrum sampleEmissionDirection(EmissionRecord &eRec, const Point2 &sample) const { - Point p2 = m_bsphere.center + squareToSphere(sample) * m_bsphere.radius; + Float radius = m_bsphere.radius; + if (eRec.type == EmissionRecord::EPreview) + radius *= 10; + Point p2 = m_bsphere.center + squareToSphere(sample) * radius; eRec.d = p2 - eRec.sRec.p; Float length = eRec.d.length(); @@ -132,6 +142,7 @@ public: } void pdfEmission(EmissionRecord &eRec) const { + Assert(eRec.type == EmissionRecord::ENormal); Float dp = dot(eRec.sRec.n, eRec.d); if (dp > 0) eRec.pdfDir = INV_PI * dp; @@ -143,7 +154,7 @@ public: Spectrum f(const EmissionRecord &eRec) const { return Spectrum(INV_PI); } - + Spectrum fArea(const EmissionRecord &eRec) const { return m_intensity * M_PI; } @@ -160,6 +171,8 @@ public: << "]"; return oss.str(); } + + Shader *createShader(Renderer *renderer) const; MTS_DECLARE_CLASS() private: @@ -167,6 +180,46 @@ private: BSphere m_bsphere; }; +// ================ Hardware shader implementation ================ + +class ConstantLuminaireShader : public Shader { +public: + ConstantLuminaireShader(Renderer *renderer, const Spectrum &intensity) + : Shader(renderer, ELuminaireShader), m_emittance(intensity * M_PI) { + } + + void resolve(const GPUProgram *program, const std::string &evalName, std::vector ¶meterIDs) const { + parameterIDs.push_back(program->getParameterID(evalName + "_emittance", false)); + } + + void generateCode(std::ostringstream &oss, const std::string &evalName, + const std::vector &depNames) const { + oss << "uniform vec3 " << evalName << "_emittance;" << endl + << endl + << "vec3 " << evalName << "_dir(vec3 wo) {" << endl + << " return vec3(0.31831);" << endl + << "}" << endl + << endl + << "vec3 " << evalName << "_background(vec3 wo) {" << endl + << " return " << evalName << "_emittance * 0.31831;" << endl + << "}" << endl; + } + + void bind(GPUProgram *program, const std::vector ¶meterIDs, + int &textureUnitOffset) const { + program->setParameter(parameterIDs[0], m_emittance); + } + + MTS_DECLARE_CLASS() +private: + Spectrum m_emittance; +}; + +Shader *ConstantLuminaire::createShader(Renderer *renderer) const { + return new ConstantLuminaireShader(renderer, m_intensity); +} + MTS_IMPLEMENT_CLASS_S(ConstantLuminaire, false, Luminaire) +MTS_IMPLEMENT_CLASS(ConstantLuminaireShader, false, Shader) MTS_EXPORT_PLUGIN(ConstantLuminaire, "Constant background luminaire"); MTS_NAMESPACE_END diff --git a/src/luminaires/envmap.cpp b/src/luminaires/envmap.cpp index 987f3a2f..81e17ab8 100644 --- a/src/luminaires/envmap.cpp +++ b/src/luminaires/envmap.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include MTS_NAMESPACE_BEGIN @@ -161,6 +163,7 @@ public: */ void sampleEmission(EmissionRecord &eRec, const Point2 &sample1, const Point2 &sample2) const { + Assert(eRec.type == EmissionRecord::ENormal); /* Chord model - generate the ray passing through two uniformly distributed points on a sphere containing the scene */ Vector d = squareToSphere(sample1); @@ -183,15 +186,29 @@ public: } void sampleEmissionArea(EmissionRecord &eRec, const Point2 &sample) const { - Vector d = squareToSphere(sample); - eRec.sRec.p = m_bsphere.center + d * m_bsphere.radius; - eRec.sRec.n = Normal(-d); - eRec.pdfArea = 1.0f / (4 * M_PI * m_bsphere.radius * m_bsphere.radius); - eRec.P = Spectrum(M_PI); + if (eRec.type == EmissionRecord::ENormal) { + Vector d = squareToSphere(sample); + eRec.sRec.p = m_bsphere.center + d * m_bsphere.radius; + eRec.sRec.n = Normal(-d); + eRec.pdfArea = 1.0f / (4 * M_PI * m_bsphere.radius * m_bsphere.radius); + eRec.P = Spectrum(M_PI); + } else { + /* Preview mode, which is more suitable for VPL-based rendering: approximate + the infinitely far-away source with set of diffuse point sources */ + const Float radius = m_bsphere.radius * 10; + Vector d = squareToSphere(sample); + eRec.sRec.p = m_bsphere.center + d * radius; + eRec.sRec.n = Normal(-d); + eRec.pdfArea = 1.0f / (4 * M_PI * radius * radius); + eRec.P = Le(d) * M_PI; + } } Spectrum sampleEmissionDirection(EmissionRecord &eRec, const Point2 &sample) const { - Point p2 = m_bsphere.center + squareToSphere(sample) * m_bsphere.radius; + Float radius = m_bsphere.radius; + if (eRec.type == EmissionRecord::EPreview) + radius *= 10; + Point p2 = m_bsphere.center + squareToSphere(sample) * radius; eRec.d = p2 - eRec.sRec.p; Float length = eRec.d.length(); @@ -199,13 +216,17 @@ public: eRec.pdfDir = 1.0f; return Spectrum(0.0f); } - + eRec.d /= length; eRec.pdfDir = INV_PI * dot(eRec.sRec.n, eRec.d); - return Le(-eRec.d) * INV_PI; /// XXX perhaps make this normalized? + if (eRec.type == EmissionRecord::ENormal) + return Le(-eRec.d) * INV_PI; + else + return Spectrum(INV_PI); } void pdfEmission(EmissionRecord &eRec) const { + Assert(eRec.type == EmissionRecord::ENormal); Float dp = dot(eRec.sRec.n, eRec.d); if (dp > 0) eRec.pdfDir = INV_PI * dp; @@ -215,10 +236,14 @@ public: } Spectrum f(const EmissionRecord &eRec) const { - return Le(-eRec.d) * INV_PI; + if (eRec.type == EmissionRecord::ENormal) + return Le(-eRec.d) * INV_PI; + else + return Spectrum(INV_PI); } Spectrum fArea(const EmissionRecord &eRec) const { + Assert(eRec.type == EmissionRecord::ENormal); return M_PI; } @@ -237,6 +262,8 @@ public: return oss.str(); } + Shader *createShader(Renderer *renderer) const; + MTS_DECLARE_CLASS() private: Spectrum m_average; @@ -247,6 +274,80 @@ private: ref m_stream; }; +// ================ Hardware shader implementation ================ + +class EnvMapLuminaireShader : public Shader { +public: + EnvMapLuminaireShader(Renderer *renderer, const std::string &filename, ref bitmap, + Float intensityScale, const Transform &worldToLuminaire) : Shader(renderer, ELuminaireShader) { + m_gpuTexture = renderer->createGPUTexture(filename, bitmap); + m_gpuTexture->setWrapType(GPUTexture::ERepeat); + m_gpuTexture->setMaxAnisotropy(8); + m_gpuTexture->init(); + /* Release the memory on the host side */ + m_gpuTexture->setBitmap(0, NULL); + m_intensityScale = intensityScale; + m_worldToLuminaire = worldToLuminaire; + } + + void cleanup(Renderer *renderer) { + m_gpuTexture->cleanup(); + } + + void resolve(const GPUProgram *program, const std::string &evalName, std::vector ¶meterIDs) const { + parameterIDs.push_back(program->getParameterID(evalName + "_texture", false)); + parameterIDs.push_back(program->getParameterID(evalName + "_intensityScale", false)); + parameterIDs.push_back(program->getParameterID(evalName + "_worldToLuminaire", false)); + } + + void generateCode(std::ostringstream &oss, const std::string &evalName, + const std::vector &depNames) const { + oss << "uniform sampler2D " << evalName << "_texture;" << endl + << "uniform float " << evalName << "_intensityScale;" << endl + << "uniform mat4 " << evalName << "_worldToLuminaire;" << endl + << endl + << "vec3 " << evalName << "_dir(vec3 wo) {" << endl + << " return vec3(0.318309);" << endl + << "}" << endl + << endl + << "vec3 " << evalName << "_background(vec3 wo) {" << endl + << " vec3 d = normalize((" << evalName << "_worldToLuminaire * vec4(wo, 0.0)).xyz);" << endl + << " float u = 0.5 * (1.0 + atan(d.x, -d.z) * 0.318309);" << endl + << " float v = acos(max(-1.0, min(1.0, d.y))) * 0.318309;" << endl + // The following is not very elegant, but necessary to trick GLSL + // into doing correct texture filtering across the u=0 to u=1 seam. + << " if (u < 0.1)" << endl + << " return texture2D(" << evalName << "_texture, vec2(u+1.0, v)).rgb * " << evalName << "_intensityScale;" << endl + << " else" + << " return texture2D(" << evalName << "_texture, vec2(u, v)).rgb * " << evalName << "_intensityScale;" << endl + << "}" << endl; + } + + void bind(GPUProgram *program, const std::vector ¶meterIDs, + int &textureUnitOffset) const { + m_gpuTexture->bind(textureUnitOffset++); + program->setParameter(parameterIDs[0], m_gpuTexture.get()); + program->setParameter(parameterIDs[1], m_intensityScale); + program->setParameter(parameterIDs[2], m_worldToLuminaire); + } + + void unbind() const { + m_gpuTexture->unbind(); + } + + MTS_DECLARE_CLASS() +private: + ref m_gpuTexture; + Float m_intensityScale; + Transform m_worldToLuminaire; +}; + +Shader *EnvMapLuminaire::createShader(Renderer *renderer) const { + return new EnvMapLuminaireShader(renderer, m_filename, m_mipmap->getBitmap(), + m_intensityScale, m_worldToLuminaire); +} + MTS_IMPLEMENT_CLASS_S(EnvMapLuminaire, false, Luminaire) +MTS_IMPLEMENT_CLASS(EnvMapLuminaireShader, false, Shader) MTS_EXPORT_PLUGIN(EnvMapLuminaire, "Environment map luminaire"); MTS_NAMESPACE_END diff --git a/src/luminaires/point.cpp b/src/luminaires/point.cpp index 9dc7e3c7..d6d0c031 100644 --- a/src/luminaires/point.cpp +++ b/src/luminaires/point.cpp @@ -120,10 +120,7 @@ public: void generateCode(std::ostringstream &oss, const std::string &evalName, const std::vector &depNames) const { - oss << "vec3 " << evalName << "_area(vec2 uv) {" << endl - << " return vec3(0.0); /* Won't ever be used */ " << endl - << "}" << endl << endl - << "vec3 " << evalName << "_dir(vec3 wo) {" << endl + oss << "vec3 " << evalName << "_dir(vec3 wo) {" << endl << " return vec3(0.079577);" << endl << "}" << endl; } diff --git a/src/luminaires/spot.cpp b/src/luminaires/spot.cpp index 32feaa2c..d260e623 100644 --- a/src/luminaires/spot.cpp +++ b/src/luminaires/spot.cpp @@ -1,5 +1,6 @@ #include #include +#include MTS_NAMESPACE_BEGIN @@ -27,6 +28,7 @@ public: m_texture = new ConstantTexture( props.getSpectrum("texture", Spectrum(1.0f))); m_uvFactor = std::tan(m_beamWidth/2); + m_invTransitionWidth = 1.0 / (m_cutoffAngle - m_beamWidth); } SpotLuminaire(Stream *stream, InstanceManager *manager) @@ -39,6 +41,7 @@ public: m_cosCutoffAngle = std::cos(m_cutoffAngle); m_position = m_luminaireToWorld(Point(0, 0, 0)); m_uvFactor = std::tan(m_beamWidth/2); + m_invTransitionWidth = 1.0 / (m_cutoffAngle - m_beamWidth); } void serialize(Stream *stream, InstanceManager *manager) const { @@ -65,6 +68,9 @@ public: Spectrum result(throughputOnly ? Spectrum(1.0f) : m_intensity); Vector localDir = m_worldToLuminaire(d); const Float cosTheta = localDir.z; + + if (cosTheta < m_cosCutoffAngle) + return Spectrum(0.0f); if (m_texture->getClass() != ConstantTexture::m_theClass) { Intersection its; @@ -74,17 +80,12 @@ public: result *= m_texture->getValue(its); } - if (cosTheta < m_cosCutoffAngle) - return Spectrum(0.0f); if (cosTheta > m_cosBeamWidth) return result; - return result * ((m_cutoffAngle - std::acos(cosTheta)) - / (m_cutoffAngle - m_beamWidth)); + return result * ((m_cutoffAngle - std::acos(cosTheta)) * m_invTransitionWidth); } Spectrum Le(const LuminaireSamplingRecord &lRec) const { - if (lRec.sRec.p == m_position) - return m_intensity * falloffCurve(lRec.d); return Spectrum(0.0f); } @@ -171,15 +172,100 @@ public: return oss.str(); } + Shader *createShader(Renderer *renderer) const; + MTS_DECLARE_CLASS() private: Spectrum m_intensity; ref m_texture; Float m_beamWidth, m_cutoffAngle, m_uvFactor; - Float m_cosBeamWidth, m_cosCutoffAngle; + Float m_cosBeamWidth, m_cosCutoffAngle, m_invTransitionWidth; Point m_position; }; +// ================ Hardware shader implementation ================ + +class SpotLuminaireShader : public Shader { +public: + SpotLuminaireShader(Renderer *renderer, Transform worldToLuminaire, + Float invTransitionWidth, Float cutoffAngle, Float cosCutoffAngle, + Float cosBeamWidth, Float uvFactor, const Texture *texture) + : Shader(renderer, ELuminaireShader), m_worldToLuminaire(worldToLuminaire), + m_invTransitionWidth(invTransitionWidth), m_cutoffAngle(cutoffAngle), + m_cosCutoffAngle(cosCutoffAngle), m_cosBeamWidth(cosBeamWidth), + m_uvFactor(uvFactor), m_texture(texture) { + m_textureShader = renderer->registerShaderForResource(m_texture.get()); + } + + bool isComplete() const { + return m_textureShader.get() != NULL; + } + + void cleanup(Renderer *renderer) { + renderer->unregisterShaderForResource(m_texture.get()); + } + + void putDependencies(std::vector &deps) { + deps.push_back(m_textureShader.get()); + } + + void generateCode(std::ostringstream &oss, const std::string &evalName, + const std::vector &depNames) const { + oss << "uniform float " << evalName << "_invTransitionWidth;" << endl + << "uniform float " << evalName << "_cutoffAngle;" << endl + << "uniform float " << evalName << "_cosCutoffAngle;" << endl + << "uniform float " << evalName << "_cosBeamWidth;" << endl + << "uniform float " << evalName << "_uvFactor;" << endl + << "uniform mat4 " << evalName << "_worldToLuminaire;" << endl + << "vec3 " << evalName << "_dir(vec3 wo) {" << endl + << " vec3 localDir = (" << evalName << "_worldToLuminaire * vec4(wo, 0)).xyz;" << endl + << " float cosTheta = localDir.z;" << endl + << " if (cosTheta < " << evalName << "_cosCutoffAngle)" << endl + << " return vec3(0.0);" << endl + << " vec2 uv = 0.5 + (localDir.xy / localDir.z * " << evalName << "_uvFactor);" << endl + << " vec3 color = " << depNames[0] << "(uv);" << endl + << " if (cosTheta > " << evalName << "_cosBeamWidth)" << endl + << " return color;" << endl + << " return color * ((" << evalName << "_cutoffAngle - acos(cosTheta))" << endl + << " * " << evalName << "_invTransitionWidth);" << endl + << "}" << endl; + } + + void resolve(const GPUProgram *program, const std::string &evalName, std::vector ¶meterIDs) const { + parameterIDs.push_back(program->getParameterID(evalName + "_worldToLuminaire", false)); + parameterIDs.push_back(program->getParameterID(evalName + "_invTransitionWidth", false)); + parameterIDs.push_back(program->getParameterID(evalName + "_cutoffAngle", false)); + parameterIDs.push_back(program->getParameterID(evalName + "_cosCutoffAngle", false)); + parameterIDs.push_back(program->getParameterID(evalName + "_cosBeamWidth", false)); + parameterIDs.push_back(program->getParameterID(evalName + "_uvFactor", false)); + } + + void bind(GPUProgram *program, const std::vector ¶meterIDs, int &textureUnitOffset) const { + program->setParameter(parameterIDs[0], m_worldToLuminaire); + program->setParameter(parameterIDs[1], m_invTransitionWidth); + program->setParameter(parameterIDs[2], m_cutoffAngle); + program->setParameter(parameterIDs[3], m_cosCutoffAngle); + program->setParameter(parameterIDs[4], m_cosBeamWidth); + program->setParameter(parameterIDs[5], m_uvFactor); + } + + MTS_DECLARE_CLASS() +private: + Transform m_worldToLuminaire; + Float m_invTransitionWidth; + Float m_cutoffAngle, m_cosCutoffAngle; + Float m_cosBeamWidth, m_uvFactor; + ref m_texture; + ref m_textureShader; +}; + +Shader *SpotLuminaire::createShader(Renderer *renderer) const { + return new SpotLuminaireShader(renderer, m_worldToLuminaire, + m_invTransitionWidth, m_cutoffAngle, m_cosCutoffAngle, + m_cosBeamWidth, m_uvFactor, m_texture.get()); +} + +MTS_IMPLEMENT_CLASS(SpotLuminaireShader, false, Shader) MTS_IMPLEMENT_CLASS_S(SpotLuminaire, false, Luminaire) MTS_EXPORT_PLUGIN(SpotLuminaire, "Spot light"); MTS_NAMESPACE_END diff --git a/src/mitsuba/mitsuba.cpp b/src/mitsuba/mitsuba.cpp index 8aa963ad..c1d09bc0 100644 --- a/src/mitsuba/mitsuba.cpp +++ b/src/mitsuba/mitsuba.cpp @@ -358,6 +358,13 @@ int main(int argc, char **argv) { resolver->addPath("/usr/share/mitsuba"); #endif +#if defined(__OSX__) + MTS_AUTORELEASE_BEGIN() + FileResolver *resolver = FileResolver::getInstance(); + resolver->addPath(__ubi_bundlepath()); + MTS_AUTORELEASE_END() +#endif + /* Initialize Xerces-C */ try { XMLPlatformUtils::Initialize(); diff --git a/src/qtgui/glwidget.cpp b/src/qtgui/glwidget.cpp index 719849ae..44e86150 100644 --- a/src/qtgui/glwidget.cpp +++ b/src/qtgui/glwidget.cpp @@ -33,6 +33,7 @@ GLWidget::GLWidget(QWidget *parent) : m_didSetCursor = false; m_ignoreResizeEvents = false; m_ignoreScrollEvents = false; + setAcceptDrops(true); } GLWidget::~GLWidget() { @@ -243,17 +244,16 @@ void GLWidget::initializeGL() { void GLWidget::setScene(SceneContext *context) { if (m_movementTimer->isActive()) m_movementTimer->stop(); - - bool contextSwitch = m_context != NULL && context != NULL; - m_preview->setSceneContext(context, contextSwitch, false); m_context = context; + + if (context && context->scene == NULL) + context = NULL; + m_preview->setSceneContext(context, true, false); m_framebufferChanged = true; m_mouseButtonDown = false; m_leftKeyDown = m_rightKeyDown = m_upKeyDown = m_downKeyDown = false; updateGeometry(); updateScrollBars(); - if (!contextSwitch) - resetPreview(); } void GLWidget::refreshScene() { @@ -280,7 +280,7 @@ void GLWidget::setShadowMapResolution(int res) { } void GLWidget::setPreviewMethod(EPreviewMethod method) { - if (method != m_context->clamping) { + if (method != m_context->previewMethod) { m_context->previewMethod = method; resetPreview(); } @@ -333,7 +333,7 @@ void GLWidget::downloadFramebuffer() { bool createdFramebuffer = false; makeCurrent(); - if (m_framebuffer == NULL || true || + if (m_framebuffer == NULL || m_framebuffer->getBitmap() != m_context->framebuffer) { if (m_framebuffer) m_framebuffer->cleanup(); @@ -376,9 +376,8 @@ void GLWidget::downloadFramebuffer() { QSize GLWidget::sizeHint() const { QSize minimumSize(440, 170); if (m_context) { - Vector2i size = m_context->scene->getCamera()->getFilm()->getSize(); - return QSize(std::max(minimumSize.width(), size.x), - std::max(minimumSize.height(), size.y)); + return QSize(std::max(minimumSize.width(), m_context->framebuffer->getWidth()), + std::max(minimumSize.height(), m_context->framebuffer->getHeight())); } else { return minimumSize; } @@ -391,7 +390,7 @@ void GLWidget::focusOutEvent(QFocusEvent *event) { } void GLWidget::timerImpulse() { - if (!m_context) { + if (!m_context || !m_context->scene) { m_movementTimer->stop(); return; } @@ -429,8 +428,10 @@ void GLWidget::timerImpulse() { } void GLWidget::resetPreview() { + if (!m_context || !m_context->scene) + return; bool motion = m_leftKeyDown || m_rightKeyDown || - m_upKeyDown || m_downKeyDown || m_mouseButtonDown; + m_upKeyDown || m_downKeyDown || m_mouseButtonDown; m_preview->setSceneContext(m_context, false, motion); updateGL(); } @@ -497,7 +498,7 @@ void GLWidget::mouseMoveEvent(QMouseEvent *event) { rel = event->pos() - m_mousePos; m_mousePos = event->pos(); - if (!m_context || !m_mouseButtonDown || rel == QPoint(0,0)) + if (!m_context || !m_context->scene || !m_mouseButtonDown || rel == QPoint(0,0)) return; // if (m_ignoreMouseEvent == rel) { @@ -526,15 +527,14 @@ void GLWidget::mouseMoveEvent(QMouseEvent *event) { Transform trafo = Transform::rotate(Vector(0,1,0), yaw) * Transform::rotate(Vector(1,0,0), pitch) * camera->getViewTransform(); + direction = trafo.inverse()(Vector(0,0,1)); if (camera->getViewTransform().det3x3() < 0) { - direction = trafo.inverse()(Vector(0,0,1)); camera->setInverseViewTransform(Transform::lookAt(p, p+direction, m_context->up)); } else { - direction = trafo.inverse()(Vector(0,0,1)); p.z = -p.z; direction.z = -direction.z; camera->setInverseViewTransform(Transform::scale(Vector(1,1,-1)) * - Transform::lookAt(p, p+direction, -m_context->up)); + Transform::lookAt(p, p+direction, m_context->up)); } didMove = true; } @@ -549,10 +549,10 @@ void GLWidget::mouseMoveEvent(QMouseEvent *event) { } else { p.z = -p.z; direction.z = -direction.z; camera->setInverseViewTransform(Transform::scale(Vector(1,1,-1)) * - Transform::lookAt(p, p+direction, -m_context->up)); + Transform::lookAt(p, p+direction, m_context->up)); } - camera->setFov(std::min(std::max(1.0f, camera->getFov() + fovChange), 100.0f)); + camera->setFov(std::min(std::max((Float) 1.0f, camera->getFov() + fovChange), (Float) 100.0f)); camera->configure(); didMove = true; @@ -638,7 +638,7 @@ void exportPNG(const char *name, GPUTexture *buffer, bool downsample) { if (ch == 3) value = 1; else - value = std::pow(value, 1/2.2f); + value = std::pow(value, (Float) (1/2.2f)); target[(x + y*size.x)*4 + ch] = (uint8_t) std::min(std::max((int) (value* 255), 0), 255); } } @@ -719,7 +719,7 @@ void GLWidget::paintGL() { m_luminanceBuffer[i]->init(); } } - + Float multiplier = 1.0; if (m_context->mode == EPreview) multiplier /= entry.vplSampleOffset; @@ -761,6 +761,10 @@ void GLWidget::paintGL() { Float logLuminance, maxLuminance, unused; m_luminanceBuffer[target]->getPixel(0, 0).toLinearRGB(logLuminance, maxLuminance, unused); logLuminance = std::exp(logLuminance / (size.x*size.y)); + if (std::isnan(logLuminance) || std::isinf(logLuminance)) { + SLog(EWarn, "Could not determine the average log-luminance, since the image contains NaNs/infs/negative values"); + logLuminance = 1; + } buffer->bind(); m_reinhardTonemap->bind(); @@ -897,7 +901,21 @@ void GLWidget::onScroll() { m_context->scrollOffset.y = m_vScroll->value(); updateGL(); } - + +void GLWidget::dragEnterEvent(QDragEnterEvent *event) { + if (event->mimeData()->hasFormat("text/uri-list")) { + event->setDropAction(Qt::CopyAction); + event->acceptProposedAction(); + } +} + +void GLWidget::dropEvent(QDropEvent *event) { + QList urls = event->mimeData()->urls(); + for (int i=0; iacceptProposedAction(); +} + void GLWidget::resumePreview() { m_preview->resume(); } diff --git a/src/qtgui/glwidget.h b/src/qtgui/glwidget.h index 097c37b0..885e6d3f 100644 --- a/src/qtgui/glwidget.h +++ b/src/qtgui/glwidget.h @@ -37,11 +37,13 @@ public: void setScrollBars(QScrollBar *hScroll, QScrollBar *vScroll); inline void ignoreResizeEvents(bool value) { m_ignoreResizeEvents = value; } void updateScrollBars(); + signals: void beginRendering(); void stopRendering(); void quit(); void statusMessage(const QString &status); + void loadFileRequest(const QString &fileName); public slots: void timerImpulse(); @@ -71,6 +73,8 @@ protected: void resetPreview(); void resizeEvent(QResizeEvent *event); void wheelEvent(QWheelEvent *event); + void dragEnterEvent(QDragEnterEvent *event); + void dropEvent(QDropEvent *event); /* Masquerade QGLWidget as a GL device for libhw */ #if defined(WIN32) diff --git a/src/qtgui/mainwindow.cpp b/src/qtgui/mainwindow.cpp index dfc7aeab..7d2695d6 100644 --- a/src/qtgui/mainwindow.cpp +++ b/src/qtgui/mainwindow.cpp @@ -488,14 +488,14 @@ void MainWindow::onProgressMessage(const RenderJob *job, const QString &name, } void MainWindow::on_actionOpen_triggered() { QFileDialog *dialog = new QFileDialog(this, Qt::Sheet); - dialog->setNameFilter(tr("Mitsuba scenes (*.xml)")); + dialog->setNameFilter(tr("Mitsuba scenes (*.xml);;EXR images (*.exr)")); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->setAcceptMode(QFileDialog::AcceptOpen); dialog->setViewMode(QFileDialog::Detail); dialog->setWindowModality(Qt::WindowModal); QSettings settings("mitsuba-renderer.org", "qtgui"); dialog->restoreState(settings.value("fileDialogState").toByteArray()); - connect(dialog, SIGNAL(finished(int)), this, SLOT(onFileDialogClose(int))); + connect(dialog, SIGNAL(finished(int)), this, SLOT(onOpenDialogClose(int))); m_currentChild = dialog; // prevent a tab drawing artifact on Qt/OSX m_activeWindowHack = true; @@ -504,7 +504,7 @@ void MainWindow::on_actionOpen_triggered() { m_activeWindowHack = false; } -void MainWindow::onFileDialogClose(int reason) { +void MainWindow::onOpenDialogClose(int reason) { QSettings settings("mitsuba-renderer.org", "qtgui"); QFileDialog *dialog = static_cast(sender()); m_currentChild = NULL; @@ -534,7 +534,6 @@ void MainWindow::onClearRecent() { } SceneContext *MainWindow::loadScene(const QString &qFileName) { - /* Prepare for parsing scene descriptions */ ref resolver = FileResolver::getInstance(); std::string filename = resolver->resolveAbsolute(qFileName.toStdString()); std::string filePath = resolver->pathFromFile(filename); @@ -644,14 +643,15 @@ void MainWindow::updateUI() { SceneContext *context = hasTab ? m_context[index] : NULL; bool isRendering = hasTab ? context->renderJob != NULL : false; - bool isInactiveScene = hasTab ? context->renderJob == NULL : false; + bool hasScene = hasTab && context->scene != NULL; + bool isInactiveScene = (hasTab && hasScene) ? context->renderJob == NULL : false; ui->actionStop->setEnabled(isRendering); ui->actionRender->setEnabled(isInactiveScene); ui->actionRefresh->setEnabled(isInactiveScene); ui->actionRenderSettings->setEnabled(isInactiveScene); - ui->actionSave->setEnabled(hasTab); - ui->actionSaveAs->setEnabled(hasTab); + ui->actionSave->setEnabled(hasScene); + ui->actionSaveAs->setEnabled(hasScene); ui->actionExportImage->setEnabled(hasTab); ui->actionClose->setEnabled(hasTab); ui->actionDuplicateTab->setEnabled(hasTab); @@ -910,7 +910,6 @@ void MainWindow::on_actionPreviewSettings_triggered() { connect(&d, SIGNAL(reinhardBurnChanged(Float)), ui->glView, SLOT(setReinhardBurn(Float))); connect(&d, SIGNAL(previewMethodChanged(EPreviewMethod)), ui->glView, SLOT(setPreviewMethod(EPreviewMethod))); connect(&d, SIGNAL(toneMappingMethodChanged(EToneMappingMethod)), ui->glView, SLOT(setToneMappingMethod(EToneMappingMethod))); - d.setWindowModality(Qt::NonModal); d.setMaximumSize(d.minimumSize()); d.exec(); QSettings settings("mitsuba-renderer.org", "qtgui"); @@ -1241,6 +1240,7 @@ void MainWindow::on_actionExportImage_triggered() { source += 4; } } + temp->setGamma(ctx->srgb ? -1 : ctx->gamma); temp->save(format, fs); } } @@ -1252,29 +1252,42 @@ void MainWindow::on_actionSave_triggered() { } void MainWindow::on_actionSaveAs_triggered() { + QFileDialog *dialog = new QFileDialog(this, tr("Save as .."), + "", tr("Mitsuba scenes (*.xml)")); + + m_currentChild = dialog; + QSettings settings("mitsuba-renderer.org", "qtgui"); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->setViewMode(QFileDialog::Detail); + dialog->setAcceptMode(QFileDialog::AcceptSave); + dialog->restoreState(settings.value("fileDialogState").toByteArray()); + dialog->setWindowModality(Qt::WindowModal); + connect(dialog, SIGNAL(finished(int)), this, SLOT(onSaveAsDialogClose(int))); + m_currentChild = dialog; + // prevent a tab drawing artifact on Qt/OSX + m_activeWindowHack = true; + dialog->show(); + qApp->processEvents(); + m_activeWindowHack = false; +} + +void MainWindow::onSaveAsDialogClose(int reason) { int currentIndex = ui->tabBar->currentIndex(); SceneContext *context = m_context[currentIndex]; - QFileDialog dialog(this, tr("Save as .."), - "", tr("Mitsuba scenes (*.xml)")); - - m_currentChild = &dialog; QSettings settings("mitsuba-renderer.org", "qtgui"); - dialog.setViewMode(QFileDialog::Detail); - dialog.setAcceptMode(QFileDialog::AcceptSave); - dialog.restoreState(settings.value("fileDialogState").toByteArray()); - - if (dialog.exec() == QDialog::Accepted) { + QFileDialog *dialog = static_cast(sender()); + m_currentChild = NULL; + if (reason == QDialog::Accepted) { m_currentChild = NULL; - QString fileName = dialog.selectedFiles().value(0); - settings.setValue("fileDialogState", dialog.saveState()); + QString fileName = dialog->selectedFiles().value(0); + settings.setValue("fileDialogState", dialog->saveState()); saveScene(this, context, fileName); context->fileName = fileName; context->shortName = QFileInfo(fileName).fileName(); ui->tabBar->setTabText(currentIndex, context->shortName); addRecentFile(fileName); } - m_currentChild = NULL; } void MainWindow::on_actionNavigationControls_triggered() { @@ -1397,7 +1410,10 @@ void MainWindow::onWorkBegin(const RenderJob *job, const RectangularWorkUnit *wu if (isCurrentView) emit updateView(); } - + +void MainWindow::on_glView_loadFileRequest(const QString &string) { + loadFile(string); +} void MainWindow::onWorkEnd(const RenderJob *job, const ImageBlock *block) { int ox = block->getOffset().x, oy = block->getOffset().y, @@ -1501,52 +1517,58 @@ QString ServerConnection::toString() const { } SceneContext::SceneContext(SceneContext *ctx) { - scene = new Scene(ctx->scene); - ref pluginMgr = PluginManager::getInstance(); - ref oldCamera = static_cast(ctx->scene->getCamera()); - ref camera = static_cast - (pluginMgr->createObject(Camera::m_theClass, oldCamera->getProperties())); - ref sampler = static_cast - (pluginMgr->createObject(Sampler::m_theClass, ctx->scene->getSampler()->getProperties())); - ref film = static_cast - (pluginMgr->createObject(Film::m_theClass, oldCamera->getFilm()->getProperties())); - const Integrator *oldIntegrator = ctx->scene->getIntegrator(); - ref currentIntegrator; + if (ctx->scene) { + scene = new Scene(ctx->scene); + ref pluginMgr = PluginManager::getInstance(); + ref oldCamera = static_cast(ctx->scene->getCamera()); + ref camera = static_cast + (pluginMgr->createObject(Camera::m_theClass, oldCamera->getProperties())); + ref sampler = static_cast + (pluginMgr->createObject(Sampler::m_theClass, ctx->scene->getSampler()->getProperties())); + ref film = static_cast + (pluginMgr->createObject(Film::m_theClass, oldCamera->getFilm()->getProperties())); + const Integrator *oldIntegrator = ctx->scene->getIntegrator(); + ref currentIntegrator; - int depth = 0; - std::vector integratorList; - while (oldIntegrator != NULL) { - ref integrator = static_cast (pluginMgr->createObject( - Integrator::m_theClass, oldIntegrator->getProperties())); - if (depth++ == 0) - scene->setIntegrator(integrator); - else - currentIntegrator->addChild("", integrator); - currentIntegrator = integrator; - integratorList.push_back(integrator); - oldIntegrator = oldIntegrator->getSubIntegrator(); + int depth = 0; + std::vector integratorList; + while (oldIntegrator != NULL) { + ref integrator = static_cast (pluginMgr->createObject( + Integrator::m_theClass, oldIntegrator->getProperties())); + if (depth++ == 0) + scene->setIntegrator(integrator); + else + currentIntegrator->addChild("", integrator); + currentIntegrator = integrator; + integratorList.push_back(integrator); + oldIntegrator = oldIntegrator->getSubIntegrator(); + } + + for (int i=(int) integratorList.size()-1; i>=0; --i) + integratorList[i]->configure(); + + ref rfilter = static_cast + (pluginMgr->createObject(ReconstructionFilter::m_theClass, oldCamera->getFilm()-> + getReconstructionFilter()->getProperties())); + + rfilter->configure(); + film->addChild("", rfilter); + film->configure(); + sampler->configure(); + camera->addChild("", sampler); + camera->addChild("", film); + camera->setViewTransform(oldCamera->getViewTransform()); + camera->setFov(oldCamera->getFov()); + camera->configure(); + scene->setCamera(camera); + scene->setSampler(sampler); + scene->configure(); + sceneResID = ctx->sceneResID; + Scheduler::getInstance()->retainResource(sceneResID); + } else { + sceneResID = -1; + renderJob = NULL; } - - for (int i=(int) integratorList.size()-1; i>=0; --i) - integratorList[i]->configure(); - - ref rfilter = static_cast - (pluginMgr->createObject(ReconstructionFilter::m_theClass, oldCamera->getFilm()-> - getReconstructionFilter()->getProperties())); - - rfilter->configure(); - film->addChild("", rfilter); - film->configure(); - sampler->configure(); - camera->addChild("", sampler); - camera->addChild("", film); - camera->setViewTransform(oldCamera->getViewTransform()); - camera->setFov(oldCamera->getFov()); - camera->configure(); - scene->setCamera(camera); - scene->setSampler(sampler); - scene->configure(); - sceneResID = ctx->sceneResID; fileName = ctx->fileName; shortName = ctx->shortName; movementScale = ctx->movementScale; @@ -1554,10 +1576,8 @@ SceneContext::SceneContext(SceneContext *ctx) { renderJob = NULL; cancelled = false; progress = 0.0f; - framebuffer = new Bitmap(ctx->framebuffer->getWidth(), - ctx->framebuffer->getHeight(), 128); - framebuffer->clear(); - mode = EPreview; + framebuffer = ctx->framebuffer->clone(); + mode = ctx->renderJob ? EPreview : ctx->mode; gamma = ctx->gamma; exposure = ctx->exposure; clamping = ctx->clamping; @@ -1571,11 +1591,10 @@ SceneContext::SceneContext(SceneContext *ctx) { scrollOffset = ctx->scrollOffset; reinhardKey = ctx->reinhardKey; reinhardBurn = ctx->reinhardBurn; - Scheduler::getInstance()->retainResource(sceneResID); } SceneContext::~SceneContext() { - if (sceneResID != -1) + if (scene && sceneResID != -1) Scheduler::getInstance()->unregisterResource(sceneResID); if (previewBuffer.buffer) { previewBuffer.buffer->disassociate(); @@ -1586,6 +1605,9 @@ SceneContext::~SceneContext() { } int SceneContext::detectPathLength() const { + if (!scene) + return 2; + const Integrator *integrator = scene->getIntegrator(); int extraDepth = 0; diff --git a/src/qtgui/mainwindow.h b/src/qtgui/mainwindow.h index 9f06adcb..949f94b8 100644 --- a/src/qtgui/mainwindow.h +++ b/src/qtgui/mainwindow.h @@ -109,6 +109,7 @@ private slots: bool on_tabBar_tabCloseRequested(int index); void on_tabBar_tabMoved(int from, int to); void on_tabBar_customContextMenuRequested(const QPoint &pt); + void on_glView_loadFileRequest(const QString &string); void onOpenRecent(); void onClearRecent(); void onWorkBegin(const RenderJob *job, const RectangularWorkUnit *wu, int worker); @@ -125,7 +126,8 @@ private slots: void updateUI(); void updateStatus(); void onPreviewSettingsClose(); - void onFileDialogClose(int reason); + void onOpenDialogClose(int reason); + void onSaveAsDialogClose(int reason); void onRenderSettingsClose(int reason); private: diff --git a/src/qtgui/preview.cpp b/src/qtgui/preview.cpp index 4e3f61a4..b092bf3b 100644 --- a/src/qtgui/preview.cpp +++ b/src/qtgui/preview.cpp @@ -117,18 +117,21 @@ void PreviewThread::setSceneContext(SceneContext *context, bool swapContext, boo m_queueCV->wait(); } - if (swapContext) { + if (swapContext && m_context) { m_context->vpls = m_vpls; m_context->previewBuffer = temp[0]; m_context->previewBuffer.buffer->disassociate(); m_recycleQueue.push_back(PreviewQueueEntry(m_queueEntryIndex++)); + + /* Put back all buffers */ + for (size_t i=1; ipreviewBuffer.vplSampleOffset > 0) { + if (swapContext && context && context->previewBuffer.vplSampleOffset > 0) { /* Resume from a stored state */ m_vplSampleOffset = context->previewBuffer.vplSampleOffset; m_vpls = context->vpls; @@ -155,7 +158,6 @@ void PreviewThread::setSceneContext(SceneContext *context, bool swapContext, boo m_vplSampleOffset = 0; m_vpls.clear(); m_accumBuffer = NULL; - swapContext = false; } if (m_context != context) @@ -323,25 +325,14 @@ void PreviewThread::run() { m_timer->reset(); } - int unsuccessfulAttempts = 0; - while (true) { - if (m_vpls.empty()) - m_vplSampleOffset = generateVPLs(m_context->scene, m_vplSampleOffset, - 1, m_context->pathLength, m_vpls); + if (m_vpls.empty()) + m_vplSampleOffset = generateVPLs(m_context->scene, m_vplSampleOffset, + 1, m_context->pathLength, m_vpls); - VPL vpl = m_vpls.front(); - m_vpls.pop_front(); - - if (rtrtRenderVPL(target, vpl)) { - break; - } else { - if (unsuccessfulAttempts++ > 50) { - Log(EError, "Something is fishy about this scene -- unable to " - " render 50 VPLs in a row, disabling the preview feature."); - } - } - } + VPL vpl = m_vpls.front(); + m_vpls.pop_front(); + rtrtRenderVPL(target, vpl); } else { if (m_shaderManager == NULL || m_shaderManager->getScene() != m_context->scene) { if (m_shaderManager) { @@ -370,24 +361,15 @@ void PreviewThread::run() { m_timer->reset(); } - int unsuccessfulAttempts = 0; - while (true) { - if (m_vpls.empty()) - m_vplSampleOffset = generateVPLs(m_context->scene, m_vplSampleOffset, - 1, m_context->pathLength, m_vpls); + if (m_vpls.empty()) + m_vplSampleOffset = generateVPLs(m_context->scene, m_vplSampleOffset, + 1, m_context->pathLength, m_vpls); - VPL vpl = m_vpls.front(); - m_vpls.pop_front(); + VPL vpl = m_vpls.front(); + m_vpls.pop_front(); - if (oglRenderVPL(target, vpl)) { - break; - } else { - if (unsuccessfulAttempts++ > 50) { - Log(EError, "Something is fishy about this scene -- unable to " - " render 50 VPLs in a row, disabling the preview feature."); - } - } - } + oglRenderVPL(target, vpl); + if (m_useSync) target.sync->init(); } @@ -454,11 +436,10 @@ void PreviewThread::run() { MTS_AUTORELEASE_END() } -bool PreviewThread::oglRenderVPL(PreviewQueueEntry &target, const VPL &vpl) { +void PreviewThread::oglRenderVPL(PreviewQueueEntry &target, const VPL &vpl) { const std::vector meshes = m_context->scene->getMeshes(); - if (!m_shaderManager->setVPL(vpl)) - return false; + m_shaderManager->setVPL(vpl); Point2 jitter(.5f, .5f); if (!m_motion) @@ -467,12 +448,15 @@ bool PreviewThread::oglRenderVPL(PreviewQueueEntry &target, const VPL &vpl) { m_mutex->lock(); const ProjectiveCamera *camera = static_cast (m_context->scene->getCamera()); - m_renderer->setCamera(camera->getGLProjectionTransform(jitter).getMatrix(), - m_camViewTransform.getMatrix()); + Transform projectionTransform = camera->getGLProjectionTransform(jitter); + m_renderer->setCamera(projectionTransform.getMatrix(), m_camViewTransform.getMatrix()); + Transform clipToWorld = m_camViewTransform.inverse() + * Transform::scale(Vector(1, 1, -1)) * projectionTransform.inverse(); + target.vplSampleOffset = m_vplSampleOffset; Point camPos = m_camPos; m_mutex->unlock(); - + m_framebuffer->activateTarget(); m_framebuffer->clear(); m_renderer->beginDrawingMeshes(); @@ -482,8 +466,9 @@ bool PreviewThread::oglRenderVPL(PreviewQueueEntry &target, const VPL &vpl) { m_renderer->drawTriMesh(meshes[j]); m_shaderManager->unbind(); } - m_framebuffer->releaseTarget(); m_renderer->endDrawingMeshes(); + m_shaderManager->drawBackground(clipToWorld, camPos); + m_framebuffer->releaseTarget(); target.buffer->activateTarget(); m_renderer->setDepthMask(false); @@ -521,10 +506,9 @@ bool PreviewThread::oglRenderVPL(PreviewQueueEntry &target, const VPL &vpl) { m_renderer->finish(); } } - return true; } -bool PreviewThread::rtrtRenderVPL(PreviewQueueEntry &target, const VPL &vpl) { +void PreviewThread::rtrtRenderVPL(PreviewQueueEntry &target, const VPL &vpl) { Float nearClip = std::numeric_limits::infinity(), farClip = -std::numeric_limits::infinity(); @@ -549,11 +533,15 @@ bool PreviewThread::rtrtRenderVPL(PreviewQueueEntry &target, const VPL &vpl) { } } - if (nearClip >= farClip) - return false; - Float minDist = nearClip + (farClip - nearClip) * m_context->clamping; + if (nearClip >= farClip) { + /* Unable to find any surface - just default values based on the scene size */ + nearClip = 1e-3 * m_context->scene->getBSphere().radius; + farClip = 1e3 * m_context->scene->getBSphere().radius; + minDist = 0; + } + Point2 jitter(.5f, .5f); if (!m_motion) jitter = Point2(m_random->nextFloat(), m_random->nextFloat()); @@ -574,6 +562,4 @@ bool PreviewThread::rtrtRenderVPL(PreviewQueueEntry &target, const VPL &vpl) { target.vplSampleOffset = m_vplSampleOffset; m_raysPerSecond += m_previewProc->getRayCount(); m_accumBuffer = target.buffer; - - return true; } diff --git a/src/qtgui/preview.h b/src/qtgui/preview.h index 42426bff..abbedc47 100644 --- a/src/qtgui/preview.h +++ b/src/qtgui/preview.h @@ -55,9 +55,9 @@ protected: /// Virtual destructure virtual ~PreviewThread(); /// Render a single VPL using OpenGL - bool oglRenderVPL(PreviewQueueEntry &target, const VPL &vpl); + void oglRenderVPL(PreviewQueueEntry &target, const VPL &vpl); /// Render a single VPL using real-time coherent ray tracing - bool rtrtRenderVPL(PreviewQueueEntry &target, const VPL &vpl); + void rtrtRenderVPL(PreviewQueueEntry &target, const VPL &vpl); private: ref m_session; ref m_device, m_parentDevice; diff --git a/src/qtgui/save.cpp b/src/qtgui/save.cpp index fd56b494..4f6a8aee 100644 --- a/src/qtgui/save.cpp +++ b/src/qtgui/save.cpp @@ -110,12 +110,9 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { QDomElement camera = findUniqueChild(root, "camera"); if (camera.isNull()) { - QMessageBox::critical(parent, parent->tr("Unable to save"), - parent->tr("Unable to save changes: could not find the camera descriptor in " - "%1. If you are using include files, make sure that the camera " - "descriptor is located within the main scene file.").arg(ctx->fileName), - QMessageBox::Ok); - return; + camera = doc.createElement("camera"); + camera.setAttribute("type", "perspective"); + root.insertAfter(camera, QDomNode()); } const PinholeCamera *sceneCamera = static_cast(ctx->scene->getCamera()); @@ -130,6 +127,7 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { QDomElement cameraTransform = findUniqueChild(camera, "transform"); if (cameraTransform.isNull()) { cameraTransform = doc.createElement("transform"); + cameraTransform.setAttribute("name", "toWorld"); camera.insertBefore(cameraTransform, QDomNode()); } @@ -142,7 +140,10 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { Point t, p = sceneCamera->getInverseViewTransform()(Point(0,0,0)); if (sceneCamera->getViewTransform().det3x3() > 0) { - p.z = -p.z; direction.z = -direction.z; u = -u; + p.z = -p.z; direction.z = -direction.z; + QDomElement scale = doc.createElement("scale"); + cameraTransform.insertAfter(scale, lookAt); + scale.setAttribute("z", "-1"); } t = p + direction; @@ -178,12 +179,9 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { QDomElement film = findUniqueChild(camera, "film"); if (film.isNull()) { - QMessageBox::critical(parent, parent->tr("Unable to save"), - parent->tr("Unable to save changes: could not find the film descriptor in " - "%1. If you are using include files, make sure that the film " - "descriptor is located within the main scene file.").arg(ctx->fileName), - QMessageBox::Ok); - return; + film = doc.createElement("film"); + film.setAttribute("type", "exrfilm"); + camera.insertAfter(film, QDomNode()); } QDomElement widthProperty = findProperty(film, "width"); QDomElement heightProperty = findProperty(film, "height"); diff --git a/src/qtgui/sceneloader.cpp b/src/qtgui/sceneloader.cpp index 75471f5b..584bd76d 100644 --- a/src/qtgui/sceneloader.cpp +++ b/src/qtgui/sceneloader.cpp @@ -14,57 +14,13 @@ SceneLoader::~SceneLoader() { void SceneLoader::run() { FileResolver::setInstance(m_resolver); SAXParser* parser = new SAXParser(); + std::string lowerCase = m_filename; + for(size_t i=0; iresolveAbsolute("schema/scene.xsd"); - - /* Check against the 'scene.xsd' XML Schema */ - parser->setDoSchema(true); - parser->setValidationSchemaFullChecking(true); - parser->setValidationScheme(SAXParser::Val_Always); - parser->setExternalNoNamespaceSchemaLocation(schemaPath.c_str()); - - /* Set the SAX handler */ - parser->setDoNamespaces(true); - parser->setDocumentHandler(handler); - parser->setErrorHandler(handler); - - std::string filenameWithoutExtension = m_resolver->resolveDest( - FileResolver::getFilenameWithoutExtension(m_filename)); - - SLog(EInfo, "Parsing scene description from \"%s\" ..", m_filename.c_str()); - parser->parse(m_filename.c_str()); - ref scene = handler->getScene(); - - scene->setSourceFile(m_filename.c_str()); - scene->setDestinationFile(filenameWithoutExtension); - scene->initialize(); - - if (scene->getIntegrator() == NULL) - SLog(EError, "The scene contains no integrator! Aborting.."); - if (scene->getCamera() == NULL) - SLog(EError, "The scene contains no camera! Aborting.."); - if (scene->getCamera()->getFilm() == NULL) - SLog(EError, "The scene contains no film! Aborting.."); - if (scene->getLuminaires().size() == 0) - SLog(EError, "The scene contains no light sources! Aborting.."); - - Vector2i size = scene->getFilm()->getSize(); - Camera *camera = scene->getCamera(); - - m_result->scene = scene; - m_result->sceneResID = Scheduler::getInstance()->registerResource(scene); - m_result->renderJob = NULL; - m_result->movementScale = scene->getBSphere().radius / 2000.0f; - m_result->mode = EPreview; - m_result->framebuffer = new Bitmap(size.x, size.y, 128); - m_result->framebuffer->clear(); - m_result->fileName = QString(m_filename.c_str()); - m_result->shortName = QFileInfo(m_filename.c_str()).fileName(); - m_result->up = camera->getInverseViewTransform()(Vector(0, 1, 0)); - m_result->scrollOffset = Vector2i(0, 0); - QSettings settings("mitsuba-renderer.org", "qtgui"); m_result->srgb = settings.value("preview_sRGB", true).toBool(); m_result->gamma = (Float) settings.value("preview_gamma", 2.2).toDouble(); @@ -73,9 +29,69 @@ void SceneLoader::run() { m_result->exposure = (Float) settings.value("preview_exposure", 0).toDouble(); m_result->shadowMapResolution = settings.value("preview_shadowMapResolution", 256).toInt(); m_result->clamping = (Float) settings.value("preview_clamping", 0.1f).toDouble(); - m_result->pathLength = m_result->detectPathLength(); m_result->previewMethod = (EPreviewMethod) settings.value("preview_previewMethod", EOpenGL).toInt(); m_result->toneMappingMethod = (EToneMappingMethod) settings.value("preview_toneMappingMethod", EGamma).toInt(); + + if (endsWith(lowerCase, ".exr")) { + /* This is an image, not a scene */ + ref fs = new FileStream(m_filename, FileStream::EReadOnly); + ref bitmap = new Bitmap(Bitmap::EEXR, fs); + + m_result->mode = ERender; + m_result->framebuffer = bitmap; + m_result->fileName = QString(m_filename.c_str()); + m_result->shortName = QFileInfo(m_filename.c_str()).fileName(); + m_result->pathLength = 2; + } else { + std::string schemaPath = m_resolver->resolveAbsolute("schema/scene.xsd"); + + /* Check against the 'scene.xsd' XML Schema */ + parser->setDoSchema(true); + parser->setValidationSchemaFullChecking(true); + parser->setValidationScheme(SAXParser::Val_Always); + parser->setExternalNoNamespaceSchemaLocation(schemaPath.c_str()); + + /* Set the SAX handler */ + parser->setDoNamespaces(true); + parser->setDocumentHandler(handler); + parser->setErrorHandler(handler); + + std::string filenameWithoutExtension = m_resolver->resolveDest( + FileResolver::getFilenameWithoutExtension(m_filename)); + + SLog(EInfo, "Parsing scene description from \"%s\" ..", m_filename.c_str()); + parser->parse(m_filename.c_str()); + ref scene = handler->getScene(); + + scene->setSourceFile(m_filename.c_str()); + scene->setDestinationFile(filenameWithoutExtension); + scene->initialize(); + + if (scene->getIntegrator() == NULL) + SLog(EError, "The scene contains no integrator! Aborting.."); + if (scene->getCamera() == NULL) + SLog(EError, "The scene contains no camera! Aborting.."); + if (scene->getCamera()->getFilm() == NULL) + SLog(EError, "The scene contains no film! Aborting.."); + if (scene->getLuminaires().size() == 0) + SLog(EError, "The scene contains no light sources! Aborting.."); + + Vector2i size = scene->getFilm()->getSize(); + Camera *camera = scene->getCamera(); + + m_result->scene = scene; + m_result->sceneResID = Scheduler::getInstance()->registerResource(scene); + m_result->renderJob = NULL; + m_result->movementScale = scene->getBSphere().radius / 2000.0f; + m_result->mode = EPreview; + m_result->framebuffer = new Bitmap(size.x, size.y, 128); + m_result->framebuffer->clear(); + m_result->fileName = QString(m_filename.c_str()); + m_result->shortName = QFileInfo(m_filename.c_str()).fileName(); + m_result->up = camera->getInverseViewTransform()(Vector(0, 1, 0)); + m_result->scrollOffset = Vector2i(0, 0); + m_result->pathLength = m_result->detectPathLength(); + } } catch (const std::exception &e) { m_error = e.what(); delete m_result; diff --git a/src/shapes/cylinder.cpp b/src/shapes/cylinder.cpp index 9e241dc4..91d19846 100644 --- a/src/shapes/cylinder.cpp +++ b/src/shapes/cylinder.cpp @@ -122,6 +122,7 @@ public: its.shFrame = its.geoFrame; its.wi = its.toLocal(-ray.d); its.hasUVPartials = false; + its.shape = this; /* Intersection refinement step */ Vector2 localDir(normalize(Vector2(local.x, local.y))); diff --git a/src/shapes/sphere.cpp b/src/shapes/sphere.cpp index b6b4bda8..a06c0ff5 100644 --- a/src/shapes/sphere.cpp +++ b/src/shapes/sphere.cpp @@ -73,6 +73,7 @@ public: phi += 2*M_PI; its.uv.x = theta / M_PI; its.uv.y = phi / (2*M_PI); + its.shape = this; Float zrad = std::sqrt(local.x*local.x + local.y*local.y); its.geoFrame.n = Normal(normalize(its.p - m_center)); diff --git a/src/textures/ldrtexture.cpp b/src/textures/ldrtexture.cpp index e95b4fd3..7ff5006e 100644 --- a/src/textures/ldrtexture.cpp +++ b/src/textures/ldrtexture.cpp @@ -11,7 +11,7 @@ MTS_NAMESPACE_BEGIN /** - * Gamma-corrected bitmap texture using the JPG file format + * Gamma-corrected bitmap texture using the JPG or PNG file format */ class LDRTexture : public Texture { public: @@ -233,6 +233,7 @@ public: m_gpuTexture->setWrapType(GPUTexture::ERepeat); m_gpuTexture->setMaxAnisotropy(8); m_gpuTexture->init(); + /* Release the memory on the host side */ m_gpuTexture->setBitmap(0, NULL); } diff --git a/src/utils/bluenoise b/src/utils/bluenoise deleted file mode 100755 index 27e8f555..00000000 Binary files a/src/utils/bluenoise and /dev/null differ diff --git a/src/utils/bluenoise.o b/src/utils/bluenoise.o deleted file mode 100644 index 11291d59..00000000 Binary files a/src/utils/bluenoise.o and /dev/null differ diff --git a/src/utils/trackball.o b/src/utils/trackball.o deleted file mode 100644 index 51e235d6..00000000 Binary files a/src/utils/trackball.o and /dev/null differ diff --git a/tools/linux/build-sourcedist.sh b/tools/linux/build-sourcedist.sh index 2e80f8f0..0b9319f5 100755 --- a/tools/linux/build-sourcedist.sh +++ b/tools/linux/build-sourcedist.sh @@ -1,10 +1,9 @@ #!/bin/bash -tar --exclude-vcs -czvf mitsuba-$1.tar.gz --transform "s,^,mitsuba-$1/," ChangeLog README SConstruct \ +tar --exclude-vcs -czvf mitsuba-$1.tar.gz ChangeLog README SConstruct \ config include/ schema/ src/bsdfs/ src/cameras/ src/collada/ src/films/ src/libcore/ src/libhw/ \ src/librender/ src/luminaires/ src/medium/ src/mitsuba/ src/phase/ src/qtgui/ src/rfilters/ src/samplers/ \ src/shapes/ src/subsurface/ src/textures/ src/utils/ src/volume/ src/integrators/direct src/integrators/misc \ src/integrators/path src/integrators/photonmapper src/integrators/vpl tools/boost tools/darwin \ tools/build.sh tools/scons-1.2.0 tools/windows tools/linux/*.sh tools/linux/mitsuba.desktop tools/qt4.py \ - tools/build.bat - + tools/build.bat setpath.sh