783 lines
26 KiB
C++
783 lines
26 KiB
C++
/*
|
|
This file is part of Mitsuba, a physically based rendering system.
|
|
|
|
Copyright (c) 2007-2011 by Wenzel Jakob and others.
|
|
|
|
Mitsuba is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License Version 3
|
|
as published by the Free Software Foundation.
|
|
|
|
Mitsuba is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <mitsuba/render/scene.h>
|
|
#include <mitsuba/render/renderjob.h>
|
|
#include <mitsuba/core/plugin.h>
|
|
|
|
#define DEFAULT_BLOCKSIZE 32
|
|
|
|
MTS_NAMESPACE_BEGIN
|
|
|
|
Scene::Scene()
|
|
: NetworkedObject(Properties()), m_blockSize(DEFAULT_BLOCKSIZE) {
|
|
m_kdtree = new ShapeKDTree();
|
|
m_testType = ENone;
|
|
m_testThresh = 0.0f;
|
|
m_importanceSampleLuminaires = true;
|
|
}
|
|
|
|
Scene::Scene(const Properties &props)
|
|
: NetworkedObject(props), m_blockSize(DEFAULT_BLOCKSIZE) {
|
|
m_kdtree = new ShapeKDTree();
|
|
/* When test case mode is active (Mitsuba is started with the -t parameter),
|
|
this specifies the type of test performed. Mitsuba will expect a reference
|
|
solution file of the name <tt><sceneName>.ref</tt>. When set to
|
|
<tt>t-test</tt>, a two-sided t-test on equality to the reference will be
|
|
performed at the (1 - <tt>testThresh</tt>) level (99% by default).
|
|
When set to <tt>relerr</tt>, the test will fail if the relative error
|
|
exceeds <tt>testThresh</tt> (default: 0.01%).
|
|
*/
|
|
std::string testType = props.getString("testType", "none");
|
|
if (testType == "none")
|
|
m_testType = ENone;
|
|
else if (testType == "t-test")
|
|
m_testType = ETTest;
|
|
else if (testType == "relerr")
|
|
m_testType = ERelativeError;
|
|
else
|
|
Log(EError, "Unknown test mode \"%s\" specified (must be \"t-test\" or \"relerr\")",
|
|
testType.c_str());
|
|
/* Error threshold for use with <tt>testType</tt> */
|
|
m_testThresh = props.getFloat("testThresh", 0.01f);
|
|
/* By default, luminaire sampling chooses a luminaire with a probability
|
|
dependent on the emitted power. Setting this parameter to false switches
|
|
to uniform sampling. */
|
|
m_importanceSampleLuminaires = props.getBoolean("importanceSampleLuminaires", true);
|
|
/* kd-tree construction: Enable primitive clipping? Generally leads to a
|
|
significant improvement of the resulting tree. */
|
|
if (props.hasProperty("kdClip"))
|
|
m_kdtree->setClip(props.getBoolean("kdClip"));
|
|
/* kd-tree construction: Relative cost of a triangle intersection operation
|
|
in the surface area heuristic. */
|
|
if (props.hasProperty("kdIntersectionCost"))
|
|
m_kdtree->setQueryCost(props.getFloat("kdIntersectionCost"));
|
|
/* kd-tree construction: Relative cost of a kd-tree traversal operation
|
|
in the surface area heuristic. */
|
|
if (props.hasProperty("kdTraversalCost"))
|
|
m_kdtree->setTraversalCost(props.getFloat("kdTraversalCost"));
|
|
/* kd-tree construction: Bonus factor for cutting away regions of empty space */
|
|
if (props.hasProperty("kdEmptySpaceBonus"))
|
|
m_kdtree->setEmptySpaceBonus(props.getFloat("kdEmptySpaceBonus"));
|
|
/* kd-tree construction: A kd-tree node containing this many or fewer
|
|
primitives will not be split */
|
|
if (props.hasProperty("kdStopPrims"))
|
|
m_kdtree->setStopPrims(props.getInteger("kdStopPrims"));
|
|
/* kd-tree construction: Maximum tree depth */
|
|
if (props.hasProperty("kdMaxDepth"))
|
|
m_kdtree->setMaxDepth(props.getInteger("kdMaxDepth"));
|
|
/* kd-tree construction: Specify the number of primitives, at which the
|
|
builder will switch from (approximate) Min-Max binning to the accurate
|
|
O(n log n) SAH-based optimization method. */
|
|
if (props.hasProperty("kdExactPrimitiveThreshold"))
|
|
m_kdtree->setExactPrimitiveThreshold(props.getInteger("kdExactPrimitiveThreshold"));
|
|
/* kd-tree construction: use multiple processors? */
|
|
if (props.hasProperty("kdParallelBuild"))
|
|
m_kdtree->setParallelBuild(props.getBoolean("kdParallelBuild"));
|
|
/* kd-tree construction: specify whether or not bad splits can be "retracted". */
|
|
if (props.hasProperty("kdRetract"))
|
|
m_kdtree->setRetract(props.getBoolean("kdRetract"));
|
|
/* kd-tree construction: Set the number of bad refines allowed to happen
|
|
in succession before a leaf node will be created.*/
|
|
if (props.hasProperty("kdMaxBadRefines"))
|
|
m_kdtree->setMaxBadRefines(props.getInteger("kdMaxBadRefines"));
|
|
}
|
|
|
|
Scene::Scene(Scene *scene) : NetworkedObject(Properties()) {
|
|
m_kdtree = scene->m_kdtree;
|
|
m_testType = scene->m_testType;
|
|
m_testThresh = scene->m_testThresh;
|
|
m_blockSize = scene->m_blockSize;
|
|
m_aabb = scene->m_aabb;
|
|
m_bsphere = scene->m_bsphere;
|
|
m_backgroundLuminaire = scene->m_backgroundLuminaire;
|
|
m_camera = scene->m_camera;
|
|
m_integrator = scene->m_integrator;
|
|
m_sourceFile = scene->m_sourceFile;
|
|
m_destinationFile = scene->m_destinationFile;
|
|
m_luminairePDF = scene->m_luminairePDF;
|
|
m_importanceSampleLuminaires = scene->m_importanceSampleLuminaires;
|
|
m_shapes = scene->m_shapes;
|
|
for (size_t i=0; i<m_shapes.size(); ++i)
|
|
m_shapes[i]->incRef();
|
|
m_meshes = scene->m_meshes;
|
|
for (size_t i=0; i<m_meshes.size(); ++i)
|
|
m_meshes[i]->incRef();
|
|
m_luminaires = scene->m_luminaires;
|
|
for (size_t i=0; i<m_luminaires.size(); ++i)
|
|
m_luminaires[i]->incRef();
|
|
m_media = scene->m_media;
|
|
for (std::set<Medium *>::iterator it = m_media.begin();
|
|
it != m_media.end(); ++it)
|
|
(*it)->incRef();
|
|
m_ssIntegrators = scene->m_ssIntegrators;
|
|
for (size_t i=0; i<m_ssIntegrators.size(); ++i)
|
|
m_ssIntegrators[i]->incRef();
|
|
m_objects = scene->m_objects;
|
|
for (size_t i=0; i<m_objects.size(); ++i)
|
|
m_objects[i]->incRef();
|
|
m_netObjects = scene->m_netObjects;
|
|
for (size_t i=0; i<m_netObjects.size(); ++i)
|
|
m_netObjects[i]->incRef();
|
|
}
|
|
|
|
|
|
Scene::Scene(Stream *stream, InstanceManager *manager)
|
|
: NetworkedObject(stream, manager) {
|
|
m_kdtree = new ShapeKDTree();
|
|
m_kdtree->setQueryCost(stream->readFloat());
|
|
m_kdtree->setTraversalCost(stream->readFloat());
|
|
m_kdtree->setEmptySpaceBonus(stream->readFloat());
|
|
m_kdtree->setStopPrims(stream->readInt());
|
|
m_kdtree->setClip(stream->readBool());
|
|
m_kdtree->setMaxDepth(stream->readUInt());
|
|
m_kdtree->setExactPrimitiveThreshold(stream->readUInt());
|
|
m_kdtree->setParallelBuild(stream->readBool());
|
|
m_kdtree->setRetract(stream->readBool());
|
|
m_kdtree->setMaxBadRefines(stream->readUInt());
|
|
m_importanceSampleLuminaires = stream->readBool();
|
|
m_testType = (ETestType) stream->readInt();
|
|
m_testThresh = stream->readFloat();
|
|
m_blockSize = stream->readInt();
|
|
m_aabb = AABB(stream);
|
|
m_bsphere = BSphere(stream);
|
|
m_backgroundLuminaire = static_cast<Luminaire *>(manager->getInstance(stream));
|
|
size_t count = stream->readSize();
|
|
for (size_t i=0; i<count; ++i) {
|
|
Shape *shape = static_cast<Shape *>(manager->getInstance(stream));
|
|
shape->incRef();
|
|
m_shapes.push_back(shape);
|
|
}
|
|
count = stream->readSize();
|
|
for (size_t i=0; i<count; ++i) {
|
|
TriMesh *trimesh = static_cast<TriMesh *>(manager->getInstance(stream));
|
|
trimesh->incRef();
|
|
m_meshes.push_back(trimesh);
|
|
}
|
|
count = stream->readSize();
|
|
for (size_t i=0; i<count; ++i) {
|
|
Luminaire *luminaire = static_cast<Luminaire *>(manager->getInstance(stream));
|
|
luminaire->incRef();
|
|
m_luminaires.push_back(luminaire);
|
|
}
|
|
count = stream->readSize();
|
|
for (size_t i=0; i<count; ++i) {
|
|
Medium *medium = static_cast<Medium *>(manager->getInstance(stream));
|
|
medium->incRef();
|
|
m_media.insert(medium);
|
|
}
|
|
count = stream->readSize();
|
|
for (size_t i=0; i<count; ++i) {
|
|
Subsurface *ssIntegrator = static_cast<Subsurface *>(manager->getInstance(stream));
|
|
ssIntegrator->incRef();
|
|
m_ssIntegrators.push_back(ssIntegrator);
|
|
}
|
|
count = stream->readSize();
|
|
for (size_t i=0; i<count; ++i) {
|
|
ConfigurableObject *obj = static_cast<ConfigurableObject *>(manager->getInstance(stream));
|
|
obj->incRef();
|
|
m_objects.push_back(obj);
|
|
}
|
|
count = stream->readSize();
|
|
for (size_t i=0; i<count; ++i) {
|
|
NetworkedObject *obj = static_cast<NetworkedObject *>(manager->getInstance(stream));
|
|
m_netObjects.push_back(obj); // Do not increase the ref. count
|
|
}
|
|
initialize();
|
|
}
|
|
|
|
Scene::~Scene() {
|
|
for (size_t i=0; i<m_shapes.size(); i++)
|
|
m_shapes[i]->decRef();
|
|
for (size_t i=0; i<m_meshes.size(); i++)
|
|
m_meshes[i]->decRef();
|
|
for (size_t i=0; i<m_objects.size(); i++)
|
|
m_objects[i]->decRef();
|
|
for (size_t i=0; i<m_ssIntegrators.size(); i++)
|
|
m_ssIntegrators[i]->decRef();
|
|
for (size_t i=0; i<m_luminaires.size(); i++)
|
|
m_luminaires[i]->decRef();
|
|
for (std::set<Medium *>::iterator it = m_media.begin();
|
|
it != m_media.end(); ++it)
|
|
(*it)->decRef();
|
|
}
|
|
|
|
void Scene::bindUsedResources(ParallelProcess *proc) const {
|
|
for (size_t i=0; i<m_netObjects.size(); ++i)
|
|
m_netObjects[i]->bindUsedResources(proc);
|
|
}
|
|
|
|
void Scene::wakeup(std::map<std::string, SerializableObject *> ¶ms) {
|
|
for (size_t i=0; i<m_netObjects.size(); ++i)
|
|
m_netObjects[i]->wakeup(params);
|
|
}
|
|
|
|
void Scene::configure() {
|
|
if (m_integrator == NULL) {
|
|
/* Create a direct integrator by default */
|
|
m_integrator = static_cast<Integrator *> (PluginManager::getInstance()->
|
|
createObject(MTS_CLASS(Integrator), 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; i<m_shapes.size(); ++i)
|
|
aabb.expandBy(m_shapes[i]->getAABB());
|
|
if (aabb.isValid()) {
|
|
Log(EInfo, "No camera found! Adding a default camera.");
|
|
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.min.z - distance)));
|
|
props.setFloat("fov", 45.0f);
|
|
|
|
m_camera = static_cast<Camera *> (PluginManager::getInstance()->
|
|
createObject(MTS_CLASS(Camera), props));
|
|
m_camera->configure();
|
|
m_sampler = m_camera->getSampler();
|
|
} else {
|
|
m_camera = static_cast<Camera *> (PluginManager::getInstance()->
|
|
createObject(MTS_CLASS(Camera), props));
|
|
m_camera->configure();
|
|
m_sampler = m_camera->getSampler();
|
|
}
|
|
}
|
|
|
|
m_integrator->configureSampler(m_sampler);
|
|
|
|
/**
|
|
* make it possible to serialize the integrator by
|
|
* itself without all the extra baggage
|
|
*/
|
|
m_integrator->setParent(NULL);
|
|
}
|
|
|
|
void Scene::initialize() {
|
|
if (!m_kdtree->isBuilt()) {
|
|
/* Expand all geometry */
|
|
std::vector<Shape *> tempShapes;
|
|
tempShapes.reserve(m_shapes.size());
|
|
m_shapes.swap(tempShapes);
|
|
for (size_t i=0; i<tempShapes.size(); ++i) {
|
|
addShape(tempShapes[i]);
|
|
tempShapes[i]->decRef();
|
|
}
|
|
|
|
/* Build the kd-tree */
|
|
m_kdtree->build();
|
|
|
|
m_aabb = m_kdtree->getAABB();
|
|
m_bsphere = m_kdtree->getBSphere();
|
|
}
|
|
|
|
if (!m_luminairePDF.isReady()) {
|
|
if (m_luminaires.size() == 0) {
|
|
Log(EWarn, "No luminaires found -- adding a sky luminaire");
|
|
Properties skyProps("sunsky");
|
|
skyProps.setFloat("skyScale", 0.1f);
|
|
skyProps.setFloat("sunScale", 0.1f);
|
|
skyProps.setBoolean("extend", true);
|
|
ref<Luminaire> luminaire = static_cast<Luminaire *>(
|
|
PluginManager::getInstance()->createObject(MTS_CLASS(Luminaire), skyProps));
|
|
addChild(luminaire);
|
|
luminaire->configure();
|
|
}
|
|
|
|
/* Calculate a discrete PDF to importance sample luminaires */
|
|
for (std::vector<Luminaire *>::iterator it = m_luminaires.begin();
|
|
it != m_luminaires.end(); ++it) {
|
|
(*it)->preprocess(this);
|
|
/* Add with a probability proportional to the luminaire's power */
|
|
if (m_importanceSampleLuminaires)
|
|
m_luminairePDF.put((*it)->getSamplingWeight());
|
|
else
|
|
m_luminairePDF.put(1.0f);
|
|
}
|
|
m_luminairePDF.build();
|
|
} else {
|
|
for (std::vector<Luminaire *>::iterator it = m_luminaires.begin();
|
|
it != m_luminaires.end(); ++it)
|
|
(*it)->preprocess(this);
|
|
}
|
|
}
|
|
|
|
bool Scene::preprocess(RenderQueue *queue, const RenderJob *job,
|
|
int sceneResID, int cameraResID, int samplerResID) {
|
|
initialize();
|
|
|
|
/* Pre-process step for the main scene integrator */
|
|
if (!m_integrator->preprocess(this, queue, job,
|
|
sceneResID, cameraResID, samplerResID))
|
|
return false;
|
|
|
|
/* Pre-process step for all sub-surface integrators */
|
|
for (std::vector<Subsurface *>::iterator it = m_ssIntegrators.begin();
|
|
it != m_ssIntegrators.end(); ++it)
|
|
if (!(*it)->preprocess(this, queue, job,
|
|
sceneResID, cameraResID, samplerResID))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool Scene::render(RenderQueue *queue, const RenderJob *job,
|
|
int sceneResID, int cameraResID, int samplerResID) {
|
|
m_camera->getFilm()->clear();
|
|
return m_integrator->render(this, queue, job, sceneResID,
|
|
cameraResID, samplerResID);
|
|
}
|
|
|
|
void Scene::cancel() {
|
|
for (std::vector<Subsurface *>::iterator it = m_ssIntegrators.begin();
|
|
it != m_ssIntegrators.end(); ++it)
|
|
(*it)->cancel();
|
|
m_integrator->cancel();
|
|
}
|
|
|
|
void Scene::flush() {
|
|
m_camera->getFilm()->develop(m_destinationFile);
|
|
}
|
|
|
|
void Scene::postprocess(RenderQueue *queue, const RenderJob *job,
|
|
int sceneResID, int cameraResID, int samplerResID) {
|
|
m_integrator->postprocess(this, queue, job, sceneResID,
|
|
cameraResID, samplerResID);
|
|
m_camera->getFilm()->develop(m_destinationFile);
|
|
}
|
|
|
|
Float Scene::pdfLuminaire(const Point &p,
|
|
const LuminaireSamplingRecord &lRec, bool delta) const {
|
|
const Luminaire *luminaire = lRec.luminaire;
|
|
Float luminance;
|
|
|
|
if (m_importanceSampleLuminaires)
|
|
luminance = luminaire->getSamplingWeight();
|
|
else
|
|
luminance = 1.0f;
|
|
|
|
/* Calculate the probability of importance sampling this luminaire */
|
|
const Float fraction = luminance / m_luminairePDF.getOriginalSum();
|
|
return luminaire->pdf(p, lRec, delta) * fraction;
|
|
}
|
|
|
|
bool Scene::sampleLuminaire(const Point &p, Float time,
|
|
LuminaireSamplingRecord &lRec, const Point2 &s,
|
|
bool testVisibility) const {
|
|
Point2 sample(s);
|
|
Float lumPdf;
|
|
size_t index = m_luminairePDF.sampleReuse(sample.x, lumPdf);
|
|
const Luminaire *luminaire = m_luminaires[index];
|
|
luminaire->sample(p, lRec, sample);
|
|
|
|
if (lRec.pdf != 0) {
|
|
if (testVisibility) {
|
|
Vector dir = lRec.sRec.p - p;
|
|
Float length = dir.length();
|
|
Ray ray(p, dir/length, Epsilon, length*(1-ShadowEpsilon), time);
|
|
if (m_kdtree->rayIntersect(ray))
|
|
return false;
|
|
}
|
|
lRec.pdf *= lumPdf;
|
|
lRec.value /= lRec.pdf;
|
|
lRec.luminaire = luminaire;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Spectrum Scene::getTransmittance(const Point &p1, const Point &p2,
|
|
Float time, const Medium *medium, Sampler *sampler) const {
|
|
if (m_media.size() == 0) {
|
|
Vector dir = p2-p1;
|
|
Float length = dir.length();
|
|
Ray ray(p1, dir/length, Epsilon, length*(1-ShadowEpsilon), time);
|
|
return Spectrum(m_kdtree->rayIntersect(ray) ? 0.0f : 1.0f);
|
|
} else {
|
|
Vector d = p2 - p1;
|
|
Float remaining = d.length();
|
|
d /= remaining;
|
|
remaining *= 1-Epsilon;
|
|
|
|
const Shape *shape;
|
|
Ray ray(p1, d, time);
|
|
Spectrum transmittance(1.0f);
|
|
int iterations = 0;
|
|
|
|
while (remaining > 0) {
|
|
Normal n;
|
|
Float t;
|
|
|
|
bool surface = rayIntersect(ray, t, shape, n);
|
|
|
|
if (medium)
|
|
transmittance *= medium->getTransmittance(
|
|
Ray(ray, 0, std::min(t, remaining)), sampler);
|
|
|
|
if (!surface)
|
|
break;
|
|
|
|
ray.o = ray(t);
|
|
remaining -= t;
|
|
|
|
if (remaining > 0) {
|
|
if (shape->isOccluder())
|
|
return Spectrum(0.0f);
|
|
else if (shape->isMediumTransition())
|
|
medium = dot(n, d) > 0 ? shape->getExteriorMedium()
|
|
: shape->getInteriorMedium();
|
|
if (++iterations > 100) { /// Just a precaution..
|
|
Log(EWarn, "getTransmittance(): round-off error issues?");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return transmittance;
|
|
}
|
|
}
|
|
|
|
bool Scene::attenuatedRayIntersect(const Ray &_ray, const Medium *medium,
|
|
Intersection &its, bool &indexMatchedMediumTransition,
|
|
Spectrum &transmittance, Sampler *sampler) const {
|
|
Ray ray(_ray);
|
|
transmittance = Spectrum(1.0f);
|
|
int iterations = 0;
|
|
|
|
while (true) {
|
|
bool surface = m_kdtree->rayIntersect(ray, its);
|
|
|
|
if (medium)
|
|
transmittance *= medium->getTransmittance(Ray(ray, 0, its.t), sampler);
|
|
|
|
if (!surface)
|
|
return false;
|
|
else if (its.shape->isOccluder())
|
|
return true;
|
|
else if (its.shape->isMediumTransition()) {
|
|
medium = dot(its.geoFrame.n, ray.d) > 0 ?
|
|
its.shape->getExteriorMedium()
|
|
: its.shape->getInteriorMedium();
|
|
indexMatchedMediumTransition = true;
|
|
}
|
|
|
|
ray.o = ray(its.t);
|
|
ray.mint = Epsilon;
|
|
|
|
if (++iterations > 100) { /// Just a precaution..
|
|
Log(EWarn, "attenuatedRayIntersect(): round-off error issues?");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Scene::sampleAttenuatedLuminaire(const Point &p, Float time,
|
|
const Medium *medium, LuminaireSamplingRecord &lRec,
|
|
const Point2 &s, Sampler *sampler) const {
|
|
Point2 sample(s);
|
|
Float lumPdf;
|
|
const Luminaire *luminaire = m_luminaires[
|
|
m_luminairePDF.sampleReuse(sample.x, lumPdf)];
|
|
luminaire->sample(p, lRec, sample);
|
|
|
|
if (lRec.pdf != 0) {
|
|
lRec.value *= getTransmittance(p, lRec.sRec.p, time, medium, sampler);
|
|
if (lRec.value.isZero())
|
|
return false;
|
|
lRec.pdf *= lumPdf;
|
|
lRec.value /= lRec.pdf;
|
|
lRec.luminaire = luminaire;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Scene::sampleAttenuatedLuminaire(const Intersection &its,
|
|
const Medium *medium, LuminaireSamplingRecord &lRec,
|
|
const Point2 &s, Sampler *sampler) const {
|
|
Point2 sample(s);
|
|
Float lumPdf;
|
|
const Luminaire *luminaire = m_luminaires[
|
|
m_luminairePDF.sampleReuse(sample.x, lumPdf)];
|
|
luminaire->sample(its.p, lRec, sample);
|
|
|
|
if (lRec.pdf != 0) {
|
|
if (its.isMediumTransition())
|
|
medium = its.getTargetMedium(lRec.sRec.p - its.p);
|
|
lRec.value *= getTransmittance(its.p, lRec.sRec.p, its.time, medium, sampler);
|
|
if (lRec.value.isZero())
|
|
return false;
|
|
lRec.pdf *= lumPdf;
|
|
lRec.value /= lRec.pdf;
|
|
lRec.luminaire = luminaire;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void Scene::sampleEmission(EmissionRecord &eRec, Point2 &sample1, Point2 &sample2) const {
|
|
Float lumPdf;
|
|
size_t index = m_luminairePDF.sampleReuse(sample1.x, lumPdf);
|
|
const Luminaire *luminaire = m_luminaires[index];
|
|
luminaire->sampleEmission(eRec, sample1, sample2);
|
|
eRec.pdfArea *= lumPdf;
|
|
eRec.luminaire = luminaire;
|
|
Float cosTheta = (eRec.luminaire->getType() & Luminaire::EOnSurface)
|
|
? absDot(eRec.sRec.n, eRec.d) : 1;
|
|
eRec.value *= cosTheta / (eRec.pdfArea * eRec.pdfDir);
|
|
}
|
|
|
|
void Scene::sampleEmissionArea(EmissionRecord &eRec, Point2 &sample) const {
|
|
Float lumPdf;
|
|
size_t index = m_luminairePDF.sampleReuse(sample.x, lumPdf);
|
|
const Luminaire *luminaire = m_luminaires[index];
|
|
luminaire->sampleEmissionArea(eRec, sample);
|
|
eRec.pdfArea *= lumPdf;
|
|
eRec.luminaire = luminaire;
|
|
}
|
|
|
|
void Scene::pdfEmission(EmissionRecord &eRec, bool delta) const {
|
|
const Luminaire *luminaire = eRec.luminaire;
|
|
Float luminance;
|
|
if (m_importanceSampleLuminaires)
|
|
luminance = luminaire->getSamplingWeight();
|
|
else
|
|
luminance = 1.0f;
|
|
/* Calculate the probability of importance sampling this luminaire */
|
|
const Float fraction = luminance / m_luminairePDF.getOriginalSum();
|
|
|
|
luminaire->pdfEmission(eRec, delta);
|
|
eRec.pdfArea *= fraction;
|
|
}
|
|
|
|
void Scene::addChild(const std::string &name, ConfigurableObject *child) {
|
|
const Class *cClass = child->getClass();
|
|
|
|
if (cClass->derivesFrom(MTS_CLASS(NetworkedObject)) &&
|
|
!cClass->derivesFrom(MTS_CLASS(Integrator)))
|
|
m_netObjects.push_back(static_cast<NetworkedObject *>(child));
|
|
if (cClass->derivesFrom(MTS_CLASS(Camera))) {
|
|
AssertEx(m_camera == NULL, "There can only be one camera per scene");
|
|
m_camera = static_cast<Camera *>(child);
|
|
m_sampler = m_camera->getSampler();
|
|
} else if (cClass->derivesFrom(MTS_CLASS(Integrator))) {
|
|
AssertEx(m_integrator == NULL, "There can only be one integrator per scene");
|
|
m_integrator = static_cast<Integrator *>(child);
|
|
} else if (cClass->derivesFrom(MTS_CLASS(Texture))
|
|
|| cClass->derivesFrom(MTS_CLASS(BSDF))
|
|
|| cClass->derivesFrom(MTS_CLASS(PhaseFunction))) {
|
|
ConfigurableObject *obj= static_cast<ConfigurableObject *>(child);
|
|
obj->incRef();
|
|
m_objects.push_back(obj);
|
|
} else if (cClass->derivesFrom(MTS_CLASS(Medium))) {
|
|
Medium *medium = static_cast<Medium *>(child);
|
|
if (m_media.find(medium) == m_media.end()) {
|
|
medium->incRef();
|
|
m_media.insert(medium);
|
|
}
|
|
} else if (cClass->derivesFrom(MTS_CLASS(Luminaire))) {
|
|
Luminaire *luminaire = static_cast<Luminaire *>(child);
|
|
|
|
if (luminaire->isCompound()) {
|
|
int index = 0;
|
|
do {
|
|
ref<Luminaire> element = luminaire->getElement(index++);
|
|
if (element == NULL)
|
|
break;
|
|
addChild(name, element);
|
|
} while (true);
|
|
return;
|
|
}
|
|
|
|
luminaire->incRef();
|
|
m_luminaires.push_back(luminaire);
|
|
if (luminaire->isBackgroundLuminaire()) {
|
|
AssertEx(m_backgroundLuminaire.get() == NULL,
|
|
"The scene may only contain one background luminaire");
|
|
m_backgroundLuminaire = luminaire;
|
|
}
|
|
} else if (cClass->derivesFrom(MTS_CLASS(Shape))) {
|
|
ref<Shape> shape = static_cast<Shape *>(child);
|
|
shape->incRef();
|
|
m_shapes.push_back(shape);
|
|
} else if (cClass->derivesFrom(MTS_CLASS(Scene))) {
|
|
ref<Scene> scene = static_cast<Scene *>(child);
|
|
/* A scene from somewhere else has been included.
|
|
Add all of its contents */
|
|
for (size_t i=0; i<scene->getLuminaires().size(); ++i) {
|
|
Luminaire *lum = scene->getLuminaires()[i];
|
|
lum->setParent(this);
|
|
addChild("luminaire", lum);
|
|
}
|
|
for (size_t i=0; i<scene->getShapes().size(); ++i) {
|
|
Shape *shape = scene->getShapes()[i];
|
|
shape->setParent(this);
|
|
addChild("shape", shape);
|
|
}
|
|
for (size_t i=0; i<scene->getReferencedObjects().size(); ++i) {
|
|
ConfigurableObject *obj = scene->getReferencedObjects()[i];
|
|
obj->setParent(this);
|
|
addChild("object", obj);
|
|
}
|
|
for (std::set<Medium *>::iterator it = scene->getMedia().begin();
|
|
it != scene->getMedia().end(); ++it) {
|
|
Medium *medium = *it;
|
|
medium->setParent(this);
|
|
addChild("medium", medium);
|
|
}
|
|
if (scene->getIntegrator() != NULL) {
|
|
scene->getIntegrator()->setParent(this);
|
|
addChild("integrator", scene->getIntegrator());
|
|
}
|
|
if (scene->getCamera() != NULL) {
|
|
scene->getCamera()->setParent(this);
|
|
addChild("integrator", scene->getCamera());
|
|
}
|
|
} else {
|
|
Log(EError, "Scene: Invalid child node! (\"%s\")",
|
|
cClass->getName().c_str());
|
|
}
|
|
}
|
|
|
|
void Scene::addShape(Shape *shape) {
|
|
if (shape->isCompound()) {
|
|
int index = 0;
|
|
do {
|
|
ref<Shape> element = shape->getElement(index++);
|
|
if (element == NULL)
|
|
break;
|
|
addShape(element);
|
|
} while (true);
|
|
} else {
|
|
if (shape->isLuminaire()) {
|
|
if (std::find(m_luminaires.begin(), m_luminaires.end(),
|
|
shape->getLuminaire()) == m_luminaires.end()) {
|
|
m_luminaires.push_back(shape->getLuminaire());
|
|
shape->getLuminaire()->incRef();
|
|
}
|
|
}
|
|
if (shape->hasSubsurface()) {
|
|
m_netObjects.push_back(static_cast<NetworkedObject *>(shape->getSubsurface()));
|
|
if (std::find(m_ssIntegrators.begin(), m_ssIntegrators.end(),
|
|
shape->getSubsurface()) == m_ssIntegrators.end()) {
|
|
m_ssIntegrators.push_back(shape->getSubsurface());
|
|
shape->getSubsurface()->incRef();
|
|
}
|
|
}
|
|
|
|
Medium *iMedium = shape->getInteriorMedium(),
|
|
*eMedium = shape->getExteriorMedium();
|
|
|
|
if (eMedium != NULL && m_media.find(eMedium) == m_media.end()) {
|
|
m_media.insert(eMedium);
|
|
eMedium->incRef();
|
|
}
|
|
|
|
if (iMedium != NULL && m_media.find(iMedium) == m_media.end()) {
|
|
m_media.insert(iMedium);
|
|
iMedium->incRef();
|
|
}
|
|
|
|
if (shape->getClass()->derivesFrom(MTS_CLASS(TriMesh))) {
|
|
if (std::find(m_meshes.begin(), m_meshes.end(), shape)
|
|
== m_meshes.end()) {
|
|
m_meshes.push_back(static_cast<TriMesh *>(shape));
|
|
shape->incRef();
|
|
}
|
|
}
|
|
|
|
shape->incRef();
|
|
m_kdtree->addShape(shape);
|
|
m_shapes.push_back(shape);
|
|
}
|
|
}
|
|
|
|
void Scene::serialize(Stream *stream, InstanceManager *manager) const {
|
|
ConfigurableObject::serialize(stream, manager);
|
|
|
|
stream->writeFloat(m_kdtree->getQueryCost());
|
|
stream->writeFloat(m_kdtree->getTraversalCost());
|
|
stream->writeFloat(m_kdtree->getEmptySpaceBonus());
|
|
stream->writeInt(m_kdtree->getStopPrims());
|
|
stream->writeBool(m_kdtree->getClip());
|
|
stream->writeUInt(m_kdtree->getMaxDepth());
|
|
stream->writeUInt(m_kdtree->getExactPrimitiveThreshold());
|
|
stream->writeBool(m_kdtree->getParallelBuild());
|
|
stream->writeBool(m_kdtree->getRetract());
|
|
stream->writeUInt(m_kdtree->getMaxBadRefines());
|
|
stream->writeBool(m_importanceSampleLuminaires);
|
|
stream->writeInt(m_testType);
|
|
stream->writeFloat(m_testThresh);
|
|
stream->writeInt(m_blockSize);
|
|
m_aabb.serialize(stream);
|
|
m_bsphere.serialize(stream);
|
|
manager->serialize(stream, m_backgroundLuminaire.get());
|
|
stream->writeSize(m_shapes.size());
|
|
for (size_t i=0; i<m_shapes.size(); ++i)
|
|
manager->serialize(stream, m_shapes[i]);
|
|
stream->writeSize(m_meshes.size());
|
|
for (size_t i=0; i<m_meshes.size(); ++i)
|
|
manager->serialize(stream, m_meshes[i]);
|
|
stream->writeSize(m_luminaires.size());
|
|
for (size_t i=0; i<m_luminaires.size(); ++i)
|
|
manager->serialize(stream, m_luminaires[i]);
|
|
stream->writeSize(m_media.size());
|
|
for (std::set<Medium *>::const_iterator it = m_media.begin();
|
|
it != m_media.end(); ++it)
|
|
manager->serialize(stream, *it);
|
|
stream->writeSize(m_ssIntegrators.size());
|
|
for (size_t i=0; i<m_ssIntegrators.size(); ++i)
|
|
manager->serialize(stream, m_ssIntegrators[i]);
|
|
stream->writeSize(m_objects.size());
|
|
for (size_t i=0; i<m_objects.size(); ++i)
|
|
manager->serialize(stream, m_objects[i]);
|
|
stream->writeSize(m_netObjects.size());
|
|
for (size_t i=0; i<m_netObjects.size(); ++i)
|
|
manager->serialize(stream, m_netObjects[i]);
|
|
}
|
|
|
|
std::string Scene::toString() const {
|
|
std::ostringstream oss;
|
|
|
|
oss << "Scene[" << endl
|
|
<< " testType = " << ((m_testType == ETTest) ? "t-test" : "relerr") << ", " << endl
|
|
<< " testThresh = " << m_testThresh << ", " << endl
|
|
<< " importanceSampleLuminaires = " << (int) m_importanceSampleLuminaires << ", " << endl
|
|
<< " camera = " << indent(m_camera.toString()) << "," << endl
|
|
<< " sampler = " << indent(m_sampler.toString()) << "," << endl
|
|
<< " integrator = " << indent(m_integrator.toString()) << "," << endl
|
|
<< " kdtree = " << indent(m_kdtree.toString()) << "," << endl
|
|
<< " backgroundLuminaire = " << indent(m_backgroundLuminaire.toString()) << "," << endl
|
|
<< " meshes = " << indent(containerToString(m_meshes.begin(), m_meshes.end())) << "," << endl
|
|
<< " shapes = " << indent(containerToString(m_shapes.begin(), m_shapes.end())) << "," << endl
|
|
<< " luminaires = " << indent(containerToString(m_luminaires.begin(), m_luminaires.end())) << "," << endl
|
|
<< " media = " << indent(containerToString(m_media.begin(), m_media.end())) << "," << endl
|
|
<< " ssIntegrators = " << indent(containerToString(m_ssIntegrators.begin(), m_ssIntegrators.end())) << "," << endl
|
|
<< " objects = " << indent(containerToString(m_objects.begin(), m_objects.end())) << endl;
|
|
oss << "]";
|
|
return oss.str();
|
|
}
|
|
|
|
MTS_IMPLEMENT_CLASS_S(Scene, false, ConfigurableObject)
|
|
MTS_NAMESPACE_END
|