metadata
Wenzel Jakob 2011-04-10 22:10:10 +02:00
commit 57e0b50c38
44 changed files with 1020 additions and 284 deletions

View File

@ -32,11 +32,13 @@ from ..export import (get_instance_materials,
resolution, MtsLaunch, MtsExporter)
from ..properties import (
engine, sampler, integrator, lamp, texture, material, world
engine, sampler, integrator, lamp, texture,
material, mesh, camera, world
);
from ..ui import (
render_panels, lamps, materials, world
render_panels, lamps, materials, mesh,
camera, world
)
from ..ui.textures import (

View File

@ -302,6 +302,8 @@ class MtsExporter:
ltype = lamp.data.type
name = translate_id(lamp.data.name)
mult = lamp.data.mitsuba_lamp.intensity
if lamp.data.mitsuba_lamp.inside_medium:
self.exportMedium(scene.mitsuba_media.media[lamp.data.mitsuba_lamp.lamp_medium])
if ltype == 'POINT':
self.openElement('luminaire', { 'type' : 'point', 'id' : '%s-light' % name })
self.exportWorldTrafo(lamp.matrix_world)
@ -309,9 +311,8 @@ class MtsExporter:
"%f %f %f" % (lamp.data.color.r*mult, lamp.data.color.g*mult,
lamp.data.color.b*mult)})
self.parameter('float', 'samplingWeight', {'value' : '%f' % lamp.data.mitsuba_lamp.samplingWeight})
if lamp.data.mitsuba_lamp.inside_medium:
self.exportMediumReference(scene, lamp, nil, lamp.data.mitsuba_lamp.lamp_medium)
self.element('ref', {'id' : lamp.data.mitsuba_lamp.lamp_medium})
self.closeElement()
elif ltype == 'AREA':
self.element('remove', { 'id' : '%s-light' % name})
@ -360,6 +361,8 @@ class MtsExporter:
self.parameter('float', 'cutoffAngle', {'value' : '%f' % (lamp.data.spot_size * 180 / (math.pi * 2))})
self.parameter('float', 'beamWidth', {'value' : '%f' % (lamp.data.spot_blend * lamp.data.spot_size * 180 / (math.pi * 2))})
self.parameter('float', 'samplingWeight', {'value' : '%f' % lamp.data.mitsuba_lamp.samplingWeight})
if lamp.data.mitsuba_lamp.inside_medium:
self.element('ref', {'id' : lamp.data.mitsuba_lamp.lamp_medium})
self.closeElement()
elif ltype == 'HEMI':
if lamp.data.mitsuba_lamp.envmap_type == 'constant':
@ -378,6 +381,7 @@ class MtsExporter:
def exportIntegrator(self, integrator):
self.openElement('integrator', { 'id' : 'integrator', 'type' : integrator.type})
self.parameter('integer', 'maxDepth', { 'value' : str(integrator.maxdepth)})
self.closeElement()
def exportSampler(self, sampler):
@ -419,6 +423,7 @@ class MtsExporter:
self.exported_materials += [mat.name]
mmat = mat.mitsuba_material
if mmat.type == 'none':
self.element('null', {'id' : '%s-material' % translate_id(mat.name)})
return
params = mmat.get_params()
twosided = False
@ -446,20 +451,28 @@ class MtsExporter:
self.closeElement()
def exportEmission(self, obj):
lamp = obj.data.materials[0].mitsuba_emission
if obj.data.users > 1:
MtsLog("Error: luminaires cannot be instantiated!")
return
mult = lamp.intensity
name = translate_id(obj.data.name) + "-mesh_0"
lamp = obj.data.materials[0].mitsuba_emission
if obj.data.users > 1:
MtsLog("Error: luminaires cannot be instantiated!")
return
mult = lamp.intensity
name = translate_id(obj.data.name) + "-mesh_0"
self.openElement('append', { 'id' : name})
self.openElement('luminaire', { 'id' : '%s-emission' % name, 'type' : 'area'})
self.parameter('float', 'samplingWeight', {'value' : '%f' % lamp.samplingWeight})
self.parameter('rgb', 'intensity', { 'value' : "%f %f %f"
% (lamp.color.r*mult, lamp.color.g*mult, lamp.color.b*mult)})
self.closeElement()
self.closeElement()
def exportNormalMode(self, obj):
mesh = obj.data.mitsuba_mesh
name = translate_id(obj.data.name) + "-mesh_0"
if mesh.normals == 'facenormals':
self.openElement('append', { 'id' : name})
self.openElement('luminaire', { 'id' : '%s-emission' % name, 'type' : 'area'})
self.parameter('float', 'samplingWeight', {'value' : '%f' % lamp.samplingWeight})
self.parameter('rgb', 'intensity', { 'value' : "%f %f %f"
% (lamp.color.r*mult, lamp.color.g*mult, lamp.color.b*mult)})
self.parameter('boolean', 'faceNormals', {'value' : 'true'})
self.closeElement()
self.closeElement()
def exportMediumReference(self, scene, obj, role, mediumName):
if mediumName == "":
return
@ -507,6 +520,7 @@ class MtsExporter:
self.closeElement()
def exportCameraSettings(self, scene, camera):
mcam = camera.data.mitsuba_camera
if scene.mitsuba_integrator.motionblur:
frameTime = 1.0/scene.render.fps
shuttertime = scene.mitsuba_integrator.shuttertime
@ -516,6 +530,11 @@ class MtsExporter:
self.parameter('float', 'shutterOpen', {'value' : str(shutterOpen)})
self.parameter('float', 'shutterClose', {'value' : str(shutterClose)})
self.closeElement()
if mcam.exterior_medium != '':
self.exportMedium(scene.mitsuba_media.media[mcam.exterior_medium])
self.openElement('prepend', {'id' : '%s-camera' % translate_id(camera.name)})
self.element('ref', { 'name' : 'exterior', 'id' : mcam.exterior_medium})
self.closeElement()
def exportMedium(self, medium):
if medium.name in self.exported_media:
@ -559,8 +578,9 @@ class MtsExporter:
self.exportSampler(scene.mitsuba_sampler)
for obj in scene.objects:
if obj.type == 'LAMP':
self.exportLamp(obj, idx)
self.exportLamp(scene, obj, idx)
elif obj.type == 'MESH':
self.exportNormalMode(obj)
for mat in obj.data.materials:
self.exportMaterial(mat)
if len(obj.data.materials) > 0 and obj.data.materials[0] != None:

View File

@ -0,0 +1,55 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
from .. import MitsubaAddon
from extensions_framework import declarative_property_group
from extensions_framework import util as efutil
def MediumParameter(attr, name):
return [
{
'attr': '%s_medium' % attr,
'type': 'string',
'name': '%s_medium' % attr,
'description': '%s; blank means vacuum' % name,
'save_in_preset': True
},
{
'type': 'prop_search',
'attr': attr,
'src': lambda s,c: s.scene.mitsuba_media,
'src_attr': 'media',
'trg': lambda s,c: c.mitsuba_camera,
'trg_attr': '%s_medium' % attr,
'name': name
}
]
@MitsubaAddon.addon_register_class
class mitsuba_camera(declarative_property_group):
ef_attach_to = ['Camera']
controls = [
'exterior'
]
visibility = { }
properties = MediumParameter('exterior', 'Exterior medium')

View File

@ -32,6 +32,7 @@ class mitsuba_integrator(declarative_property_group):
controls = [
'type',
'maxdepth',
['motionblur',
'shuttertime']
]
@ -50,7 +51,8 @@ class mitsuba_integrator(declarative_property_group):
'items': [
('volpath', 'Volumetric path tracer', 'volpath'),
('path', 'Path tracer', 'path'),
('direct', 'Direct Illumination', 'direct')
('direct', 'Direct Illumination', 'direct'),
('ptracer', 'Adjoint Particle Tracer', 'ptracer')
],
'save_in_preset': True
},
@ -71,6 +73,16 @@ class mitsuba_integrator(declarative_property_group):
'min': 0,
'max': 100,
'default': 1
},
{
'type': 'int',
'attr': 'maxdepth',
'name': 'Max. path depth',
'description': 'Maximum path depth to be rendered. 2 corresponds to direct illumination, 3 is 1-bounce indirect illumination, etc.',
'save_in_preset': True,
'min': 2,
'max': 100,
'default': 4
}
]

View File

@ -42,7 +42,48 @@ def dict_merge(*args):
vis.update(deepcopy(vis_dict))
return vis
mat_names = {
'lambertian' : 'Lambertian',
'phong' : 'Phong',
'ward' : 'Anisotropic Ward',
'mirror' : 'Ideal mirror',
'dielectric' : 'Ideal dielectric',
'roughmetal' : 'Rough metal',
'roughglass' : 'Rough glass',
'microfacet' : 'Microfacet',
'composite' : 'Composite material',
'difftrans' : 'Diffuse transmitter',
'none' : 'Passthrough material'
}
@MitsubaAddon.addon_register_class
class MATERIAL_OT_set_mitsuba_type(bpy.types.Operator):
bl_idname = 'material.set_mitsuba_type'
bl_label = 'Set material type'
mat_name = bpy.props.StringProperty()
@classmethod
def poll(cls, context):
return context.material and \
context.material.mitsuba_material
def execute(self, context):
context.material.mitsuba_material.set_type(self.properties.mat_name)
return {'FINISHED'}
@MitsubaAddon.addon_register_class
class MATERIAL_MT_mitsuba_type(bpy.types.Menu):
bl_label = 'Material Type'
def draw(self, context):
sl = self.layout
from operator import itemgetter
result = sorted(mat_names.items(), key=itemgetter(1))
for item in result:
op = sl.operator('MATERIAL_OT_set_mitsuba_type', text = item[1])
op.mat_name = item[0]
@MitsubaAddon.addon_register_class
class mitsuba_material(declarative_property_group):
'''
@ -54,7 +95,6 @@ class mitsuba_material(declarative_property_group):
ef_attach_to = ['Material']
controls = [
'type',
'twosided',
'is_medium_transition',
'interior',
@ -62,7 +102,8 @@ class mitsuba_material(declarative_property_group):
]
visibility = {
'twosided' : { 'type' : O(['lambertian', 'phong', 'ward', 'mirror', 'roughmetal', 'microfacet', 'composite'])},
'twosided' : { 'type' : O(['lambertian', 'phong', 'ward',
'mirror', 'roughmetal', 'microfacet', 'composite'])},
'exterior' : { 'is_medium_transition' : True },
'interior' : { 'is_medium_transition' : True }
}
@ -70,24 +111,17 @@ class mitsuba_material(declarative_property_group):
properties = [
# Material Type Select
{
'type': 'enum',
'attr': 'type_label',
'name': 'Mitsuba material type',
'type': 'string',
'default': 'Lambertian',
'save_in_preset': True
},
{
'type': 'string',
'attr': 'type',
'name': 'Material Type',
'description': 'Mitsuba material type',
'name': 'Type',
'default': 'lambertian',
'items': [
('none', 'None (passthrough)', 'Passthrough material. This is useful for creating participating media with index-matched boundaries'),
('difftrans', 'Diffuse transmitter', 'Material with an ideally diffuse transmittance'),
('microfacet', 'Microfacet', 'Microfacet material (like the rough glass material, but without transmittance)'),
('composite', 'Composite material', 'Allows creating mixtures of different materials'),
('roughglass', 'Rough glass', 'Rough dielectric material (e.g. sand-blasted glass)'),
('roughmetal', 'Rough metal', 'Rough conductor (e.g. sand-blasted metal)'),
('dielectric', 'Ideal dielectric', 'Ideal dielectric material (e.g. glass)'),
('mirror', 'Ideal mirror', 'Ideal mirror material'),
('ward', 'Anisotropic Ward', 'Anisotropic Ward BRDF'),
('phong', 'Phong', 'Modified Phong BRDF'),
('lambertian', 'Lambertian', 'Lambertian (i.e. ideally diffuse) material')
],
'save_in_preset': True
},
{
@ -109,6 +143,9 @@ class mitsuba_material(declarative_property_group):
] + MediumParameter('interior', 'Interior') \
+ MediumParameter('exterior', 'Exterior')
def set_type(self, mat_type):
self.type = mat_type
self.type_label = mat_names[mat_type]
def get_params(self):
sub_type = getattr(self, 'mitsuba_mat_%s' % self.type)

View File

@ -0,0 +1,50 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
from .. import MitsubaAddon
from extensions_framework import declarative_property_group
from extensions_framework import util as efutil
@MitsubaAddon.addon_register_class
class mitsuba_mesh(declarative_property_group):
ef_attach_to = ['Mesh', 'SurfaceCurve', 'TextCurve', 'Curve']
controls = [
'normals'
]
visibility = {
}
properties = [
{
'type': 'enum',
'attr': 'normals',
'name': 'Normal mode',
'description': 'Specifies how Mitsuba obtains normal information',
'items' : [
('dihedralangle','Smooth vertex normals (using angle constraint)', 'dihedralangle'),
('vertexnormals','Smooth vertex normals (using connectivity)', 'vertexnormals'),
('facenormals','Flat face normals', 'facenormals'),
('default','Use normals from Blender', 'default')
],
'default': 'default'
}
]

View File

@ -0,0 +1,41 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
import bpy, bl_ui
from .. import MitsubaAddon
from extensions_framework.ui import property_group_renderer
class world_panel(bl_ui.properties_data_camera.CameraButtonsPanel, property_group_renderer):
COMPAT_ENGINES = { MitsubaAddon.BL_IDNAME }
@MitsubaAddon.addon_register_class
class cameraes(world_panel):
'''
Camera Settings
'''
bl_label = 'Mitsuba Camera Options'
display_property_groups = [
( ('camera',), 'mitsuba_camera' )
]
def draw(self, context):
super().draw(context)

View File

@ -42,5 +42,7 @@ class main(mitsuba_material_base, bpy.types.Panel):
row.operator("mitsuba.convert_all_materials", icon='WORLD_DATA')
row = self.layout.row(align=True)
row.operator("mitsuba.convert_material", icon='MATERIAL_DATA')
row = self.layout.row(align=True)
row.menu('MATERIAL_MT_mitsuba_type', text=context.material.mitsuba_material.type_label)
super().draw(context)

View File

@ -0,0 +1,41 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
import bpy, bl_ui
from .. import MitsubaAddon
from extensions_framework.ui import property_group_renderer
class world_panel(bl_ui.properties_data_mesh.MeshButtonsPanel, property_group_renderer):
COMPAT_ENGINES = { MitsubaAddon.BL_IDNAME }
@MitsubaAddon.addon_register_class
class meshes(world_panel):
'''
Mesh Settings
'''
bl_label = 'Mitsuba Mesh Options'
display_property_groups = [
( ('mesh',), 'mitsuba_mesh' )
]
def draw(self, context):
super().draw(context)

View File

@ -76,7 +76,11 @@ protected:
public:
static Class *m_theClass; ///< Pointer to the object's class descriptor
private:
#ifndef WIN32
volatile mutable int m_refCount;
#else
volatile mutable LONG m_refCount;
#endif
};
inline int Object::getRefCount() const {

View File

@ -182,6 +182,21 @@ struct RayDifferential : public Ray {
#endif
hasDifferentials = false;
}
/// Return a string representation of this ray
inline std::string toString() const {
std::ostringstream oss;
oss << "RayDifferential[" << endl
<< " orig = " << o.toString() << "," << endl
<< " dest = " << d.toString() << "," << endl
<< " mint = " << mint << "," << endl
<< " maxt = " << maxt << "," << endl
<< " time = " << time << "," << endl
<< " rx = " << indent(rx.toString()) << "," << endl
<< " ry = " << indent(ry.toString()) << endl
<< "]" << endl;
return oss.str();
}
};
#if defined(MTS_SSE)

View File

@ -186,6 +186,13 @@ public:
}
#endif
/// Reset the stored counter values
inline void reset() {
for (int i=0; i<NUM_COUNTERS; ++i) {
m_value[i].value = m_base[i].value = 0;
}
}
/// Sorting by name (for the statistics)
bool operator<(const StatsCounter &v) const;
private:

View File

@ -78,9 +78,9 @@ public:
for (int k=0; k<3; ++k)
sum += m_transform.m[i][k] * m_transform.m[j][k];
if (i == j && std::abs(sum-1) > Epsilon)
if (i == j && std::abs(sum-1) > 1e-3f)
return true;
else if (i != j && std::abs(sum) > Epsilon)
else if (i != j && std::abs(sum) > 1e-3f)
return true;
}
}

View File

@ -104,29 +104,30 @@ extern MTS_EXPORT_CORE std::string formatString(const char *pFmt, ...);
/// Base-2 logarithm
extern MTS_EXPORT_CORE Float log2(Float value);
/// Friendly modulo function (always positive)
extern MTS_EXPORT_CORE int modulo(int a, int b);
/// Integer floor function
inline int floorToInt(Float value) {
return (int) std::floor(value);
}
/// Base-2 logarithm (32-bit integer version)
extern MTS_EXPORT_CORE int log2i(uint32_t value);
/// Base-2 logarithm (64-bit integer version)
extern MTS_EXPORT_CORE int log2i(uint64_t value);
/// Friendly modulo function (always positive)
extern MTS_EXPORT_CORE int modulo(int a, int b);
/// Check if an integer is a power of two (32 bit version)
inline bool isPowerOfTwo(uint32_t i) {
return (i & (i-1)) == 0;
}
/// Check if an integer is a power of two (unsigned 32 bit version)
inline bool isPowerOfTwo(uint32_t i) { return (i & (i-1)) == 0; }
/// Check if an integer is a power of two (signed 32 bit version)
inline bool isPowerOfTwo(int32_t i) {
return i > 0 && (i & (i-1)) == 0;
}
inline bool isPowerOfTwo(int32_t i) { return i > 0 && (i & (i-1)) == 0; }
/// Check if an integer is a power of two (64 bit version)
inline bool isPowerOfTwo(uint64_t i) {
return (i & (i-1)) == 0;
}
inline bool isPowerOfTwo(uint64_t i) { return (i & (i-1)) == 0; }
/// Check if an integer is a power of two (signed 64 bit version)
inline bool isPowerOfTwo(int64_t i) { return i > 0 && (i & (i-1)) == 0; }
/// Round an integer to the next power of two
extern MTS_EXPORT_CORE uint32_t roundToPowerOfTwo(uint32_t i);

View File

@ -36,6 +36,9 @@ public:
/// Shut the session down
void shutdown();
/// Process all events and call event callbacks
void processEvents();
/**
* \brief Process all events and call event callbacks.
*

View File

@ -162,6 +162,9 @@ public:
/// Is this luminaire intersectable (e.g. can it be encountered by a tracing a ray)?
inline bool isIntersectable() const { return m_intersectable; }
/// Specify the medium that surrounds the luminaire
inline void setMedium(Medium *medium) { m_medium = medium; }
/// Return a pointer to the medium that surrounds the luminaire
inline Medium *getMedium() { return m_medium.get(); }
@ -345,6 +348,9 @@ public:
/// Optional pre-process step before rendering starts
virtual void preprocess(const Scene *scene);
/// Add a child (e.g. a medium reference) to this luminaire
void addChild(const std::string &name, ConfigurableObject *child);
//! @}
// =============================================================

View File

@ -29,6 +29,7 @@ MTS_NAMESPACE_BEGIN
* The implementations in this class are based on PBRT
*/
class MTS_EXPORT_RENDER Noise {
public:
/**
* \brief Evaluate the Perlin noise function at \a p.
*/

View File

@ -54,6 +54,7 @@ inline Spectrum Intersection::LoSub(const Scene *scene, const Vector &d) const {
inline const BSDF *Intersection::getBSDF(const RayDifferential &ray) {
const BSDF *bsdf = shape->getBSDF();
if (bsdf && bsdf->usesRayDifferentials() && !hasUVPartials)
computePartials(ray);
return bsdf;

View File

@ -26,6 +26,10 @@
#include <stack>
#include <map>
XERCES_CPP_NAMESPACE_BEGIN
class SAXParser;
XERCES_CPP_NAMESPACE_END
XERCES_CPP_NAMESPACE_USE
MTS_NAMESPACE_BEGIN
@ -37,8 +41,8 @@ public:
typedef std::map<std::string, ConfigurableObject *> NamedObjectMap;
typedef std::map<std::string, std::string> ParameterMap;
SceneHandler(const ParameterMap &params, NamedObjectMap *objects = NULL,
bool isIncludedFile = false);
SceneHandler(const SAXParser *parser, const ParameterMap &params,
NamedObjectMap *objects = NULL, bool isIncludedFile = false);
virtual ~SceneHandler();
// -----------------------------------------------------------------------
@ -69,6 +73,8 @@ protected:
XMLString::release(&value);
return result;
}
Float parseFloat(const std::string &name, const std::string &str,
Float defVal = -1) const;
void clear();
@ -84,6 +90,7 @@ private:
std::vector<std::pair<std::string, ConfigurableObject *> > children;
};
const SAXParser *m_parser;
ref<Scene> m_scene;
ParameterMap m_params;
NamedObjectMap *m_namedObjects;

View File

@ -248,8 +248,8 @@ public:
* No details about the intersection are returned, hence the
* function is only useful for visibility queries. For most
* shapes, this will simply call forward the call to \ref
* rayIntersect. When the shape actually contains a nested kd-tree,
* some optimizations are possible.
* rayIntersect. When the shape actually contains a nested
* kd-tree, some optimizations are possible.
*/
virtual bool rayIntersect(const Ray &ray, Float mint, Float maxt) const;
@ -316,7 +316,7 @@ public:
// =============================================================
/// Does the shape act as an occluder?
inline bool isOccluder() const { return m_bsdf.get() != NULL; }
inline bool isOccluder() const { return m_occluder; }
/// Does the surface of this shape mark a medium transition?
inline bool isMediumTransition() const { return m_interiorMedium.get() || m_exteriorMedium.get(); }
/// Return the medium that lies on the interior of this shape (\c NULL == vacuum)
@ -351,7 +351,7 @@ public:
/// Return the shape's BSDF
inline BSDF *getBSDF() { return m_bsdf.get(); }
/// Set the BSDF of this shape
inline void setBSDF(BSDF *bsdf) { m_bsdf = bsdf; }
inline void setBSDF(BSDF *bsdf) { m_bsdf = bsdf; m_occluder = (bsdf != NULL); }
/// Called once after parsing
virtual void configure();
@ -378,7 +378,9 @@ protected:
ref<BSDF> m_bsdf;
ref<Subsurface> m_subsurface;
ref<Luminaire> m_luminaire;
ref<Medium> m_interiorMedium, m_exteriorMedium;
ref<Medium> m_interiorMedium;
ref<Medium> m_exteriorMedium;
bool m_occluder;
};
inline ShapeSamplingRecord::ShapeSamplingRecord(const Intersection &its)

View File

@ -311,11 +311,9 @@ protected:
static_cast<const TriMesh *>(m_shapes[shapeIdx]);
const Triangle &tri = mesh->getTriangles()[idx];
Float tempU, tempV, tempT;
if (tri.rayIntersect(mesh->getVertexPositions(), ray,
tempU, tempV, tempT)) {
if (tempT >= mint && tempT <= maxt)
return mesh->isOccluder();
}
if (mesh->isOccluder() &&
tri.rayIntersect(mesh->getVertexPositions(), ray, tempU, tempV, tempT))
return tempT >= mint && tempT <= maxt;
return false;
} else {
const Shape *shape = m_shapes[shapeIdx];
@ -331,7 +329,7 @@ protected:
return shape->isOccluder() &&
ta.rayIntersect(ray, mint, maxt, tempU, tempV, tempT);
} else {
return shape->isOccluder() &&
return shape->isOccluder() &&
shape->rayIntersect(ray, mint, maxt);
}
#endif

View File

@ -13,6 +13,7 @@
<xsd:element name="medium" type="medium"/>
<xsd:element name="phase" type="phase"/>
<xsd:element name="include" type="include"/>
<xsd:element name="null" type="null"/>
<!-- Usual attributes -->
<xsd:element name="integer" type="integer"/>
@ -70,6 +71,11 @@
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
<!-- NULL -->
<xsd:complexType name="null">
<xsd:attribute name="id" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- CAMERA Element -->
<xsd:complexType name="camera">
<xsd:complexContent>
@ -105,6 +111,8 @@
<xsd:group ref="objectGroup"/>
<xsd:element name="texture" type="texture"/>
<xsd:element name="luminaire" type="luminaire"/>
<xsd:element name="medium" type="medium"/>
<xsd:element name="ref" type="reference"/>
</xsd:choice>
</xsd:extension>
</xsd:complexContent>

View File

@ -116,7 +116,9 @@ public:
std::string toString() const {
std::ostringstream oss;
oss << "Lambertian[reflectance=" << m_reflectance->toString() << "]";
oss << "Microfacet[" << endl
<< " reflectance = " << indent(m_reflectance->toString()) << endl
<< "]";
return oss.str();
}

View File

@ -8,6 +8,8 @@
MTS_NAMESPACE_BEGIN
MemoryMappedFile::MemoryMappedFile(const fs::path &filename) : m_filename(filename) {
if (!fs::exists(filename))
Log(EError, "The file \"%s\" does not exist!", filename.file_string().c_str());
m_size = (size_t) fs::file_size(filename);
Log(ETrace, "Mapping \"%s\" into memory (%s)..",
filename.filename().c_str(), memString(m_size).c_str());

View File

@ -363,8 +363,8 @@ void ConfigurableObject::serialize(Stream *stream, InstanceManager *manager) con
}
void ConfigurableObject::addChild(const std::string &name, ConfigurableObject *child) {
SLog(EError, "ConfigurableObject::addChild(\"%s\") not implemented in \"%s\"",
name.c_str(), toString().c_str());
SLog(EError, "ConfigurableObject::addChild(\"%s\", %s) not implemented in \"%s\"",
name.c_str(), child->toString().c_str(), toString().c_str());
}
void NetworkedObject::serialize(Stream *stream, InstanceManager *manager) const {

View File

@ -12,7 +12,7 @@ void Intersection::computePartials(const RayDifferential &ray) {
return;
hasUVPartials = true;
if (!ray.hasDifferentials) {
if (!ray.hasDifferentials || (dpdu.isZero() && dpdv.isZero())) {
dudx = dvdx = dudy = dvdy = 0.0f;
return;
}
@ -83,12 +83,11 @@ std::string Intersection::toString() const {
<< " t = " << t << "," << std::endl
<< " geoFrame = " << indent(geoFrame.toString()) << "," << std::endl
<< " shFrame = " << indent(shFrame.toString()) << "," << std::endl
<< " uv = " << uv.toString() << "," << std::endl;
if (hasUVPartials) {
oss << " dpdu = " << dpdu.toString() << "," << std::endl
<< " dpdv = " << dpdv.toString() << "," << std::endl;
}
oss << " time = " << time << "," << std::endl
<< " uv = " << uv.toString() << "," << std::endl
<< " hasUVPartials = " << hasUVPartials << "," << std::endl
<< " dpdu = " << dpdu.toString() << "," << std::endl
<< " dpdv = " << dpdv.toString() << "," << std::endl
<< " time = " << time << "," << std::endl
<< " shape = " << indent(((Object *)shape)->toString()) << std::endl
<< "]";
return oss.str();

View File

@ -53,6 +53,16 @@ Luminaire::Luminaire(Stream *stream, InstanceManager *manager)
Luminaire::~Luminaire() {
}
void Luminaire::addChild(const std::string &name, ConfigurableObject *child) {
const Class *cClass = child->getClass();
if (cClass->derivesFrom(MTS_CLASS(Medium))) {
Assert(m_medium == NULL);
m_medium = static_cast<Medium *>(child);
} else {
ConfigurableObject::addChild(name, child);
}
}
void Luminaire::serialize(Stream *stream, InstanceManager *manager) const {
ConfigurableObject::serialize(stream, manager);
manager->serialize(stream, m_medium.get());

View File

@ -2,64 +2,81 @@
MTS_NAMESPACE_BEGIN
// Perlin Noise Data
#define GRAD_PERLIN 1
// Based on Ken Perlin's improved noise reference implementation
#define NOISE_PERM_SIZE 256
static int NoisePerm[2 * NOISE_PERM_SIZE] = {
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96,
53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142,
// Remainder of the noise permutation table
8, 99, 37, 240, 21, 10, 23,
190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33,
88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166,
77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244,
102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196,
135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123,
5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42,
223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9,
129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228,
251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107,
49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254,
138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180,
151, 160, 137, 91, 90, 15,
131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23,
190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33,
88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166,
77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244,
102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196,
135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123,
5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42,
223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9,
129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228,
251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107,
49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254,
138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140,
36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120,
234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33,
88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71,
134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133,
230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63,
161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130,
116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250,
124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47,
16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154,
163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98,
108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34,
242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14,
239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121,
50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243,
141, 128, 195, 78, 66, 215, 61, 156, 180, 151, 160, 137, 91, 90, 15, 131, 13,
201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240,
21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219,
203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136,
171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83,
111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102,
143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18,
169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3,
64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212,
207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119,
248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129,
22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218,
246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81,
51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184,
84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222,
114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180
};
inline Float grad(int x, int y, int z, Float dx, Float dy, Float dz) {
int h = NoisePerm[NoisePerm[NoisePerm[x]+y]+z];
h &= 15;
Float u = h<8 || h==12 || h==13 ? dx : dy;
Float v = h<4 || h==12 || h==13 ? dy : dz;
return ((h&1) ? -u : u) + ((h&2) ? -v : v);
inline static Float grad(int x, int y, int z, Float dx, Float dy, Float dz) {
int h = NoisePerm[NoisePerm[NoisePerm[x]+y]+z];
h &= 15;
#if defined(GRAD_PERLIN)
/* Based on Ken Perlin's improved Noise reference implementation */
Float u = h<8 ? dx : dy;
Float v = h<4 ? dy : h==12 || h==14 ? dx : dz;
#elif defined(GRAD_PBRT)
/* PBRT's implementation uses the hashes somewhat
differently. Possibly, this is just a typo */
Float u = h<8 || h==12 || h==13 ? dx : dy;
Float v = h<4 || h==12 || h==13 ? dy : dz;
#endif
return ((h&1) ? -u : u) + ((h&2) ? -v : v);
}
inline Float noiseWeight(Float t) {
Float t3 = t*t*t;
Float t4 = t3*t;
return 6.f*t4*t - 15.f*t4 + 10.f*t3;
inline static Float noiseWeight(Float t) {
Float t3 = t*t*t, t4 = t3*t, t5 = t4*t;
return 6.0f*t5 - 15.0f*t4 + 10.0f*t3;
}
Float Noise::perlinNoise(const Point &p) {
Float x = p.x, y = p.y, z = p.z;
// Compute noise cell coordinates and offsets
int ix = (int) x, iy = (int) y, iz = (int) z;
Float dx = x - ix, dy = y - iy, dz = z - iz;
int ix = floorToInt(p.x),
iy = floorToInt(p.y),
iz = floorToInt(p.z);
Float dx = p.x - ix,
dy = p.y - iy,
dz = p.z - iz;
// Compute gradient weights
ix &= (NOISE_PERM_SIZE-1);
iy &= (NOISE_PERM_SIZE-1);
iz &= (NOISE_PERM_SIZE-1);
Float w000 = grad(ix, iy, iz, dx, dy, dz);
Float w000 = grad(ix, iy, iz, dx, dy, dz);
Float w100 = grad(ix+1, iy, iz, dx-1, dy, dz);
Float w010 = grad(ix, iy+1, iz, dx, dy-1, dz);
Float w110 = grad(ix+1, iy+1, iz, dx-1, dy-1, dz);
@ -69,18 +86,22 @@ Float Noise::perlinNoise(const Point &p) {
Float w111 = grad(ix+1, iy+1, iz+1, dx-1, dy-1, dz-1);
// Compute trilinear interpolation of weights
Float wx = noiseWeight(dx), wy = noiseWeight(dy), wz = noiseWeight(dz);
Float x00 = lerp(wx, w000, w100);
Float x10 = lerp(wx, w010, w110);
Float x01 = lerp(wx, w001, w101);
Float x11 = lerp(wx, w011, w111);
Float y0 = lerp(wy, x00, x10);
Float y1 = lerp(wy, x01, x11);
Float wx = noiseWeight(dx),
wy = noiseWeight(dy),
wz = noiseWeight(dz);
Float x00 = lerp(wx, w000, w100),
x10 = lerp(wx, w010, w110),
x01 = lerp(wx, w001, w101),
x11 = lerp(wx, w011, w111),
y0 = lerp(wy, x00, x10),
y1 = lerp(wy, x01, x11);
return lerp(wz, y0, y1);
}
Float Noise::fbm(const Point &p, const Vector &dpdx, const Vector &dpdy,
Float omega, int maxOctaves) {
Float Noise::fbm(const Point &p, const Vector &dpdx,
const Vector &dpdy, Float omega, int maxOctaves) {
// Compute number of octaves for antialiased FBm
Float s2 = std::max(dpdx.lengthSquared(), dpdy.lengthSquared());
Float foctaves = std::min((Float) maxOctaves, 1.f - .5f * log2(s2));
@ -94,12 +115,13 @@ Float Noise::fbm(const Point &p, const Vector &dpdx, const Vector &dpdy,
o *= omega;
}
Float partialOctave = foctaves - octaves;
sum += o * smoothStep(.3f, .7f, partialOctave) * perlinNoise(lambda * p);
sum += o * smoothStep(.3f, .7f, partialOctave)
* perlinNoise(lambda * p);
return sum;
}
Float Noise::turbulence(const Point &p, const Vector &dpdx, const Vector &dpdy,
Float omega, int maxOctaves) {
Float Noise::turbulence(const Point &p, const Vector &dpdx,
const Vector &dpdy, Float omega, int maxOctaves) {
// Compute number of octaves for antialiased FBm
Float s2 = std::max(dpdx.lengthSquared(), dpdy.lengthSquared());
Float foctaves = std::min((Float) maxOctaves, 1.f - .5f * log2(s2));
@ -113,8 +135,8 @@ Float Noise::turbulence(const Point &p, const Vector &dpdx, const Vector &dpdy,
o *= omega;
}
Float partialOctave = foctaves - octaves;
sum += o * smoothStep(.3f, .7f, partialOctave) *
std::abs(perlinNoise(lambda * p));
sum += o * smoothStep(.3f, .7f, partialOctave)
* std::abs(perlinNoise(lambda * p));
return sum;
}

View File

@ -25,10 +25,15 @@
MTS_NAMESPACE_BEGIN
SceneHandler::SceneHandler(const ParameterMap &params, NamedObjectMap *namedObjects,
bool isIncludedFile) : m_params(params), m_namedObjects(namedObjects),
m_isIncludedFile(isIncludedFile) {
m_pluginManager = PluginManager::getInstance();
#define XMLLog(level, fmt, ...) Thread::getThread()->getLogger()->log(\
level, NULL, __FILE__, __LINE__, "Near file offset %i: " fmt, \
(int) m_parser->getSrcOffset(), ## __VA_ARGS__)
SceneHandler::SceneHandler(const SAXParser *parser,
const ParameterMap &params, NamedObjectMap *namedObjects,
bool isIncludedFile) : m_parser(parser), m_params(params),
m_namedObjects(namedObjects), m_isIncludedFile(isIncludedFile) {
m_pluginManager = PluginManager::getInstance();
if (m_isIncludedFile) {
SAssert(namedObjects != NULL);
@ -51,8 +56,9 @@ SceneHandler::~SceneHandler() {
void SceneHandler::clear() {
if (!m_isIncludedFile) {
for (NamedObjectMap::iterator it = m_namedObjects->begin();
it != m_namedObjects->end(); ++it)
(*it).second->decRef();
it != m_namedObjects->end(); ++it)
if (it->second)
it->second->decRef();
m_namedObjects->clear();
}
}
@ -73,16 +79,17 @@ void SceneHandler::characters(const XMLCh* const name,
const unsigned int length) {
}
static Float parseFloat(const std::string &name, const std::string &str, Float defVal = -1) {
Float SceneHandler::parseFloat(const std::string &name,
const std::string &str, Float defVal) const {
char *end_ptr = NULL;
if (str == "") {
if (defVal == -1)
SLog(EError, "Missing floating point value (in <%s>)", name.c_str());
XMLLog(EError, "Missing floating point value (in <%s>)", name.c_str());
return defVal;
}
Float result = (Float) std::strtod(str.c_str(), &end_ptr);
if (*end_ptr != '\0')
SLog(EError, "Invalid floating point value specified (in <%s>)", name.c_str());
XMLLog(EError, "Invalid floating point value specified (in <%s>)", name.c_str());
return result;
}
@ -99,14 +106,14 @@ void SceneHandler::startElement(const XMLCh* const xmlName,
for (std::map<std::string, std::string>::const_iterator it = m_params.begin();
it != m_params.end(); ++it) {
std::string::size_type pos = 0;
std::string searchString = "$" + (*it).first;
std::string searchString = "$" + it->first;
while ((pos = attrValue.find(searchString, pos)) != std::string::npos) {
attrValue.replace(pos, searchString.size(), (*it).second);
attrValue.replace(pos, searchString.size(), it->second);
++pos;
}
}
if (attrValue.find('$') != attrValue.npos)
SLog(EError, "The scene referenced an undefined parameter: \"%s\"", attrValue.c_str());
XMLLog(EError, "The scene referenced an undefined parameter: \"%s\"", attrValue.c_str());
}
context.attributes[transcode(xmlAttributes.getName(i))] = attrValue;
@ -169,10 +176,12 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
} else if (name == "rfilter") {
object = static_cast<ReconstructionFilter *> (m_pluginManager->createObject(
MTS_CLASS(ReconstructionFilter), context.properties));
} else if (name == "null") {
object = NULL;
} else if (name == "ref") {
std::string id = context.attributes["id"];
if (m_namedObjects->find(id) == m_namedObjects->end())
SLog(EError, "Referenced object '%s' not found!", id.c_str());
XMLLog(EError, "Referenced object '%s' not found!", id.c_str());
object = (*m_namedObjects)[id];
/* Construct properties */
} else if (name == "integer") {
@ -183,7 +192,7 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
int64_t i = strtoll(context.attributes["value"].c_str(), &end_ptr, 10);
#endif
if (*end_ptr != '\0')
SLog(EError, "Invalid integer value specified (in <%s>)",
XMLLog(EError, "Invalid integer value specified (in <%s>)",
context.attributes["name"].c_str());
context.parent->properties.setLong(context.attributes["name"], i);
} else if (name == "float") {
@ -239,7 +248,7 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
Float x=0, y=0, z=0;
if (hasXYZ && hasValue) {
SLog(EError, "<scale>: provided both xyz and value arguments!");
XMLLog(EError, "<scale>: provided both xyz and value arguments!");
} else if (hasXYZ) {
x = parseFloat(name, context.attributes["x"], 1);
y = parseFloat(name, context.attributes["y"], 1);
@ -247,7 +256,7 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
} else if (hasValue) {
x = y = z = parseFloat(name, context.attributes["value"]);
} else {
SLog(EError, "<scale>: provided neither xyz nor value arguments!");
XMLLog(EError, "<scale>: provided neither xyz nor value arguments!");
}
m_transform = Transform::scale(Vector(x, y, z)) * m_transform;
@ -255,7 +264,7 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
std::vector<std::string> tokens = tokenize(
context.attributes["value"], ", ");
if (tokens.size() != 16)
SLog(EError, "Invalid matrix specified");
XMLLog(EError, "Invalid matrix specified");
int index = 0;
Matrix4x4 mtx;
@ -279,7 +288,7 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
/* Parse HTML-style hexadecimal colors */
int encoded = strtol(tokens[0].c_str()+1, &end_ptr, 16);
if (*end_ptr != '\0')
SLog(EError, "Invalid rgb value specified (in <%s>)", context.attributes["name"].c_str());
XMLLog(EError, "Invalid rgb value specified (in <%s>)", context.attributes["name"].c_str());
value[0] = ((encoded & 0xFF0000) >> 16) / 255.0f;
value[1] = ((encoded & 0x00FF00) >> 8) / 255.0f;
value[2] = (encoded & 0x0000FF) / 255.0f;
@ -290,7 +299,7 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
value[i] = parseFloat(name, tokens[i]);
} else {
value[0] = value[1] = value[2] = 0; // avoid warning
SLog(EError, "Invalid RGB value specified");
XMLLog(EError, "Invalid RGB value specified");
}
Spectrum specValue;
specValue.fromLinearRGB(value[0], value[1], value[2]);
@ -305,7 +314,7 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
/* Parse HTML-style hexadecimal colors */
int encoded = strtol(tokens[0].c_str()+1, &end_ptr, 16);
if (*end_ptr != '\0')
SLog(EError, "Invalid sRGB value specified (in <%s>)", context.attributes["name"].c_str());
XMLLog(EError, "Invalid sRGB value specified (in <%s>)", context.attributes["name"].c_str());
value[0] = ((encoded & 0xFF0000) >> 16) / 255.0f;
value[1] = ((encoded & 0x00FF00) >> 8) / 255.0f;
value[2] = (encoded & 0x0000FF) / 255.0f;
@ -316,7 +325,7 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
value[i] = parseFloat(name, tokens[i]);
} else {
value[0] = value[1] = value[2] = 0; // avoid warning
SLog(EError, "Invalid sRGB value specified");
XMLLog(EError, "Invalid sRGB value specified");
}
Spectrum specValue;
specValue.fromSRGB(value[0], value[1], value[2]);
@ -343,7 +352,7 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
for (size_t i=0; i<tokens.size(); i++) {
std::vector<std::string> tokens2 = tokenize(tokens[i], ":");
if (tokens2.size() != 2)
SLog(EError, "Invalid spectrum->value mapping specified");
XMLLog(EError, "Invalid spectrum->value mapping specified");
Float wavelength = parseFloat(name, tokens2[0]);
Float value = parseFloat(name, tokens2[1]);
interp.appendSample(wavelength, value);
@ -354,7 +363,7 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
discrete);
} else {
if (tokens.size() != SPECTRUM_SAMPLES)
SLog(EError, "Invalid spectrum value specified (incorrect length)");
XMLLog(EError, "Invalid spectrum value specified (incorrect length)");
for (int i=0; i<SPECTRUM_SAMPLES; i++)
value[i] = parseFloat(name, tokens[i]);
context.parent->properties.setSpectrum(context.attributes["name"],
@ -377,57 +386,63 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
parser->setExternalNoNamespaceSchemaLocation(schemaPath.file_string().c_str());
/* Set the handler and start parsing */
SceneHandler *handler = new SceneHandler(m_params, m_namedObjects, true);
SceneHandler *handler = new SceneHandler(parser, m_params, m_namedObjects, true);
parser->setDoNamespaces(true);
parser->setDocumentHandler(handler);
parser->setErrorHandler(handler);
parser->setCalculateSrcOfs(true);
fs::path path = resolver->resolve(context.attributes["filename"]);
SLog(EInfo, "Parsing included file \"%s\" ..", path.filename().c_str());
XMLLog(EInfo, "Parsing included file \"%s\" ..", path.filename().c_str());
parser->parse(path.file_string().c_str());
object = handler->getScene();
delete parser;
delete handler;
} else {
SLog(EError, "Unhandled tag \"%s\" encountered!", name.c_str());
XMLLog(EError, "Unhandled tag \"%s\" encountered!", name.c_str());
}
if (object != NULL) {
if (object != NULL || name == "null") {
std::string id = context.attributes["id"];
std::string nodeName = context.attributes["name"];
if (id != "" && name != "ref") {
if (m_namedObjects->find(id) != m_namedObjects->end())
SLog(EError, "Duplicate ID '%s' used in scene description!", id.c_str());
XMLLog(EError, "Duplicate ID '%s' used in scene description!", id.c_str());
(*m_namedObjects)[id] = object;
object->incRef();
if (object)
object->incRef();
}
/* If the object has a parent, add it to the parent's children list */
if (context.parent != NULL) {
object->incRef();
context.parent->children.push_back(
std::pair<std::string, ConfigurableObject *>(nodeName, object));
}
if (object) {
/* If the object has a parent, add it to the parent's children list */
if (context.parent != NULL) {
object->incRef();
context.parent->children.push_back(
std::pair<std::string, ConfigurableObject *>(nodeName, object));
}
/* If the object has children, append them */
for (std::vector<std::pair<std::string, ConfigurableObject *> >
::iterator it = context.children.begin();
it != context.children.end(); ++it) {
object->addChild((*it).first, (*it).second);
(*it).second->setParent(object);
(*it).second->decRef();
}
/* If the object has children, append them */
for (std::vector<std::pair<std::string, ConfigurableObject *> >
::iterator it = context.children.begin();
it != context.children.end(); ++it) {
if (it->second != NULL) {
object->addChild(it->first, it->second);
it->second->setParent(object);
it->second->decRef();
}
}
/* Don't configure a scene object if it is from an included file */
if (name != "include" && (!m_isIncludedFile || !object->getClass()->derivesFrom(MTS_CLASS(Scene))))
object->configure();
/* Don't configure a scene object if it is from an included file */
if (name != "include" && (!m_isIncludedFile || !object->getClass()->derivesFrom(MTS_CLASS(Scene))))
object->configure();
}
}
/* Warn about unqueried properties */
std::vector<std::string> unq = context.properties.getUnqueried();
for (unsigned int i=0; i<unq.size(); ++i)
SLog(EWarn, "Unqueried attribute \"%s\" in element \"%s\"", unq[i].c_str(), name.c_str());
XMLLog(EWarn, "Unqueried attribute \"%s\" in element \"%s\"", unq[i].c_str(), name.c_str());
m_context.pop();
}

View File

@ -27,7 +27,7 @@
MTS_NAMESPACE_BEGIN
Shape::Shape(const Properties &props)
: ConfigurableObject(props) { }
: ConfigurableObject(props), m_occluder(false) { }
Shape::Shape(Stream *stream, InstanceManager *manager)
: ConfigurableObject(stream, manager) {
@ -36,10 +36,10 @@ Shape::Shape(Stream *stream, InstanceManager *manager)
m_luminaire = static_cast<Luminaire *>(manager->getInstance(stream));
m_interiorMedium = static_cast<Medium *>(manager->getInstance(stream));
m_exteriorMedium = static_cast<Medium *>(manager->getInstance(stream));
m_occluder = stream->readBool();
}
Shape::~Shape() {
}
Shape::~Shape() { }
void Shape::configure() { }
@ -68,11 +68,10 @@ Float Shape::sampleSolidAngle(ShapeSamplingRecord &sRec,
Float pdfArea = sampleArea(sRec, sample);
Vector lumToPoint = from - sRec.p;
Float distSquared = lumToPoint.lengthSquared(), dp = dot(lumToPoint, sRec.n);
if (dp > 0) {
if (dp > 0)
return pdfArea * distSquared * std::sqrt(distSquared) / dp;
} else {
else
return 0.0f;
}
}
Float Shape::pdfSolidAngle(const ShapeSamplingRecord &sRec, const Point &from) const {
@ -87,9 +86,12 @@ void Shape::addChild(const std::string &name, ConfigurableObject *child) {
const Class *cClass = child->getClass();
if (cClass->derivesFrom(MTS_CLASS(BSDF))) {
m_bsdf = static_cast<BSDF *>(child);
m_occluder = true;
} else if (cClass->derivesFrom(MTS_CLASS(Luminaire))) {
Assert(m_luminaire == NULL);
m_luminaire = static_cast<Luminaire *>(child);
if (m_luminaire && m_exteriorMedium)
m_luminaire->setMedium(m_exteriorMedium);
} else if (cClass->derivesFrom(MTS_CLASS(Subsurface))) {
Assert(m_subsurface == NULL);
m_subsurface = static_cast<Subsurface *>(child);
@ -100,12 +102,14 @@ void Shape::addChild(const std::string &name, ConfigurableObject *child) {
} else if (name == "exterior") {
Assert(m_exteriorMedium == NULL);
m_exteriorMedium = static_cast<Medium *>(child);
if (m_luminaire)
m_luminaire->setMedium(m_exteriorMedium);
} else {
Log(EError, "Shape: Invalid medium child (must be named "
"'interiorMedium' or 'exteriorMedium')!");
}
} else {
Log(EError, "Shape: Invalid child node!");
ConfigurableObject::addChild(name, child);
}
}
@ -120,6 +124,7 @@ void Shape::serialize(Stream *stream, InstanceManager *manager) const {
manager->serialize(stream, m_luminaire.get());
manager->serialize(stream, m_interiorMedium.get());
manager->serialize(stream, m_exteriorMedium.get());
stream->writeBool(m_occluder);
}
bool Shape::rayIntersect(const Ray &ray, Float mint,

View File

@ -558,6 +558,11 @@ bool TriMesh::computeTangentSpaceBasis() {
if (m_tangents)
Log(EError, "Tangent space vectors have already been generated!");
if (!m_normals) {
Log(EWarn, "Vertex normals are required to compute a tangent space basis!");
return false;
}
m_tangents = new TangentSpace[m_vertexCount];
memset(m_tangents, 0, sizeof(TangentSpace));
@ -573,7 +578,7 @@ bool TriMesh::computeTangentSpaceBasis() {
}
sharers[i] = 0;
}
for (size_t i=0; i<m_triangleCount; i++) {
uint32_t idx0 = m_triangles[i].idx[0],
idx1 = m_triangles[i].idx[1],

View File

@ -36,9 +36,10 @@ ref<Scene> Utility::loadScene(const std::string &filename,
parser->setValidationSchemaFullChecking(true);
parser->setValidationScheme(SAXParser::Val_Always);
parser->setExternalNoNamespaceSchemaLocation(schemaPath.file_string().c_str());
parser->setCalculateSrcOfs(true);
std::map<std::string, std::string> parameters;
SceneHandler *handler = new SceneHandler(params);
SceneHandler *handler = new SceneHandler(parser, params);
parser->setDoNamespaces(true);
parser->setDocumentHandler(handler);
parser->setErrorHandler(handler);

View File

@ -137,7 +137,7 @@ public:
Spectrum sampleEmissionDirection(EmissionRecord &eRec, const Point2 &sample) const {
m_luminaireToWorld(squareToCone(m_cosCutoffAngle, sample), eRec.d);
eRec.pdfDir = squareToConePdf(m_cosCutoffAngle);
return Spectrum(falloffCurve(eRec.d, true));
return falloffCurve(eRec.d, true);
}
void pdfEmission(EmissionRecord &eRec, bool delta) const {

View File

@ -18,14 +18,30 @@
#include <mitsuba/render/scene.h>
#include <mitsuba/render/volume.h>
#include <mitsuba/core/statistics.h>
MTS_NAMESPACE_BEGIN
/**
* Allow to stop integrating densities when the resulting segment
* has a throughput of less than 'Epsilon'
* \brief When the following line is uncommented, the medium implementation
* stops integrating densities when it is determined that the segment has a
* throughput of less than 'Epsilon' (see \c mitsuba/core/constants.h)
*/
#define EARLY_EXIT 1
#define HETVOL_EARLY_EXIT 1
/// Generate a few statistics related to the implementation?
//#define HETVOL_STATISTICS 1
#if defined(HETVOL_STATISTICS)
static StatsCounter avgNewtonIterations("Heterogeneous volume",
"Avg. # of Newton-Bisection iterations", EAverage);
static StatsCounter avgRayMarchingStepsTransmission("Heterogeneous volume",
"Avg. # of ray marching steps (transmission)", EAverage);
static StatsCounter avgRayMarchingStepsSampling("Heterogeneous volume",
"Avg. # of ray marching steps (sampling)", EAverage);
static StatsCounter earlyExits("Heterogeneous volume",
"Number of early exits", EPercentage);
#endif
/**
* Flexible heterogeneous medium implementation, which acquires its data from
@ -145,26 +161,39 @@ public:
mint = std::max(mint, ray.mint);
maxt = std::min(maxt, ray.maxt);
Float length = maxt-mint;
Float length = maxt-mint, maxComp = 0;
if (length <= 0)
Point p = ray(mint), pLast = ray(maxt);
for (int i=0; i<3; ++i) {
maxComp = std::max(maxComp, std::abs(p[i]));
maxComp = std::max(maxComp, std::abs(pLast[i]));
}
/* Ignore degenerate path segments */
if (length < 1e-6f * maxComp)
return 0.0f;
/* Compute a suitable step size */
uint32_t nSteps = (uint32_t) std::ceil(length / m_stepSize);
nSteps += nSteps % 2;
const Float stepSize = length/nSteps;
const Vector increment = ray.d * stepSize;
#if defined(HETVOL_STATISTICS)
avgRayMarchingStepsTransmission.incrementBase();
earlyExits.incrementBase();
#endif
/* Perform lookups at the first and last node */
Point p = ray(mint);
Float integratedDensity = m_densities->lookupFloat(p)
+ m_densities->lookupFloat(ray(maxt));
#ifdef EARLY_EXIT
const Float stopAfterDensity = -std::log(Epsilon);
const Float stopValue = stopAfterDensity*3.0f/(stepSize
* m_densityMultiplier);
#endif
+ m_densities->lookupFloat(pLast);
#if defined(HETVOL_EARLY_EXIT)
const Float stopAfterDensity = -std::log(Epsilon);
const Float stopValue = stopAfterDensity*3.0f/(stepSize
* m_densityMultiplier);
#endif
p += increment;
@ -172,16 +201,27 @@ public:
for (uint32_t i=1; i<nSteps; ++i) {
integratedDensity += m * m_densities->lookupFloat(p);
m = 6 - m;
#if defined(HETVOL_STATISTICS)
++avgRayMarchingStepsTransmission;
#endif
#ifdef EARLY_EXIT
if (integratedDensity > stopValue) // Stop early
return std::numeric_limits<Float>::infinity();
#endif
#if defined(HETVOL_EARLY_EXIT)
if (integratedDensity > stopValue) {
// Reached the threshold -- stop early
#if defined(HETVOL_STATISTICS)
++earlyExits;
#endif
return std::numeric_limits<Float>::infinity();
}
#endif
Point next = p + increment;
if (p == next) {
Log(EWarn, "integrateDensity(): unable to make forward progress -- "
"round-off error issues? The step size was %f", stepSize);
"round-off error issues? The step size was %e, mint=%f, "
"maxt=%f, nSteps=%i, ray=%s", stepSize, mint, maxt, nSteps,
ray.toString().c_str());
break;
}
p = next;
@ -245,11 +285,17 @@ public:
return false;
mint = std::max(mint, ray.mint);
maxt = std::min(maxt, ray.maxt);
Float length = maxt - mint;
Point p = ray(mint);
Float length = maxt - mint, maxComp = 0;
Point p = ray(mint), pLast = ray(maxt);
if (length <= 0)
return false;
for (int i=0; i<3; ++i) {
maxComp = std::max(maxComp, std::abs(p[i]));
maxComp = std::max(maxComp, std::abs(pLast[i]));
}
/* Ignore degenerate path segments */
if (length < 1e-6f * maxComp)
return 0.0f;
/* Compute a suitable step size */
uint32_t nSteps = (uint32_t) std::ceil(length / m_stepSize);
@ -266,12 +312,18 @@ public:
else
densityAtMinT = 0.0f;
#if defined(HETVOL_STATISTICS)
avgRayMarchingStepsSampling.incrementBase();
#endif
for (uint32_t i=0; i<nSteps; ++i) {
Float node2 = m_densities->lookupFloat(p + halfStep),
node3 = m_densities->lookupFloat(p + fullStep),
newDensity = integratedDensity + multiplier *
(node1+node2*4+node3);
#if defined(HETVOL_STATISTICS)
++avgRayMarchingStepsSampling;
#endif
if (newDensity >= desiredDensity) {
/* The integrated density of the last segment exceeds the desired
amount -- now use the Simpson quadrature expression and
@ -286,7 +338,13 @@ public:
temp = m_densityMultiplier / stepSizeSqr;
int it = 1;
#if defined(HETVOL_STATISTICS)
avgNewtonIterations.incrementBase();
#endif
while (true) {
#if defined(HETVOL_STATISTICS)
++avgNewtonIterations;
#endif
/* Lagrange polynomial from the Simpson quadrature */
Float dfx = temp * (node1 * stepSizeSqr
- (3*node1 - 4*node2 + node3)*stepSize*x
@ -316,7 +374,7 @@ public:
return true;
} else if (++it > 30) {
Log(EWarn, "invertDensityIntegral(): stuck in Newton-Bisection -- "
"round-off error issues? The step size was %f, fx=%f, dfx=%f, "
"round-off error issues? The step size was %e, fx=%f, dfx=%f, "
"a=%f, b=%f", stepSize, fx, dfx, a, b);
return false;
}
@ -331,7 +389,7 @@ public:
Point next = p + fullStep;
if (p == next) {
Log(EWarn, "invertDensityIntegral(): unable to make forward progress -- "
"round-off error issues? The step size was %f", stepSize);
"round-off error issues? The step size was %e", stepSize);
break;
}
integratedDensity = newDensity;

View File

@ -311,9 +311,10 @@ int ubi_main(int argc, char **argv) {
parser->setValidationSchemaFullChecking(true);
parser->setValidationScheme(SAXParser::Val_Always);
parser->setExternalNoNamespaceSchemaLocation(schemaPath.file_string().c_str());
parser->setCalculateSrcOfs(true);
/* Set the handler */
SceneHandler *handler = new SceneHandler(parameters);
SceneHandler *handler = new SceneHandler(parser, parameters);
parser->setDoNamespaces(true);
parser->setDocumentHandler(handler);
parser->setErrorHandler(handler);
@ -348,7 +349,7 @@ int ubi_main(int argc, char **argv) {
scene->setSourceFile(filename);
scene->setDestinationFile(destFile.length() > 0 ?
fs::path(destFile) : baseName);
fs::path(destFile) : (filePath / baseName));
scene->setBlockSize(blockSize);
if (scene->destinationExists() && skipExisting)

View File

@ -39,7 +39,8 @@ void SceneLoader::run() {
for(size_t i=0; i<m_filename.size();++i)
lowerCase[i] = std::tolower(m_filename[i]);
SceneHandler *handler = new SceneHandler(SceneHandler::ParameterMap());
SceneHandler *handler = new SceneHandler(parser,
SceneHandler::ParameterMap());
m_result = new SceneContext();
try {
QSettings settings("mitsuba-renderer.org", "qtgui");
@ -73,6 +74,7 @@ void SceneLoader::run() {
parser->setValidationSchemaFullChecking(true);
parser->setValidationScheme(SAXParser::Val_Always);
parser->setExternalNoNamespaceSchemaLocation(schemaPath.file_string().c_str());
parser->setCalculateSrcOfs(true);
/* Set the SAX handler */
parser->setDoNamespaces(true);
@ -81,6 +83,7 @@ void SceneLoader::run() {
fs::path
filename = m_filename,
filePath = fs::complete(filename).parent_path(),
baseName = fs::basename(filename);
SLog(EInfo, "Parsing scene description from \"%s\" ..", m_filename.c_str());
@ -88,7 +91,7 @@ void SceneLoader::run() {
ref<Scene> scene = handler->getScene();
scene->setSourceFile(m_filename);
scene->setDestinationFile(baseName.file_string());
scene->setDestinationFile(filePath / baseName);
scene->initialize();
if (scene->getIntegrator() == NULL)

View File

@ -32,6 +32,7 @@ public:
Log(EInfo, "Loading animation track from \"%s\"", m_name.c_str());
ref<FileStream> fs = new FileStream(path, FileStream::EReadOnly);
m_occluder = true;
m_transform = new AnimatedTransform(fs);
}
@ -39,6 +40,7 @@ public:
: Shape(stream, manager) {
m_shapeGroup = static_cast<ShapeGroup *>(manager->getInstance(stream));
m_transform = new AnimatedTransform(stream);
m_occluder = true;
configure();
}

View File

@ -23,6 +23,7 @@ MTS_NAMESPACE_BEGIN
Instance::Instance(const Properties &props) : Shape(props) {
m_objectToWorld = props.getTransform("toWorld", Transform());
m_worldToObject = m_objectToWorld.inverse();
m_occluder = true;
}
Instance::Instance(Stream *stream, InstanceManager *manager)
@ -30,6 +31,7 @@ Instance::Instance(Stream *stream, InstanceManager *manager)
m_shapeGroup = static_cast<ShapeGroup *>(manager->getInstance(stream));
m_objectToWorld = Transform(stream);
m_worldToObject = m_objectToWorld.inverse();
m_occluder = true;
}
void Instance::serialize(Stream *stream, InstanceManager *manager) const {

View File

@ -74,10 +74,16 @@ public:
for (size_t i=0; i<m_vertexCount; ++i)
m_positions[i] = objectToWorld(m_positions[i]);
if (m_normals) {
for (size_t i=0; i<m_vertexCount; ++i)
for (size_t i=0; i<m_vertexCount; ++i)
m_normals[i] = objectToWorld(m_normals[i]);
}
}
if (objectToWorld.det3x3() < 0) {
for (size_t i=0; i<m_triangleCount; ++i) {
Triangle &t = m_triangles[i];
std::swap(t.idx[0], t.idx[1]);
}
}
}
SerializedMesh(Stream *stream, InstanceManager *manager) : TriMesh(stream, manager) {

View File

@ -253,7 +253,10 @@ public:
std::string toString() const {
std::ostringstream oss;
oss << "LDRTexture[filename=\"" << m_filename << "\", gamma=" << m_gamma << "]";
oss << "LDRTexture[" << endl
<< " filename = \"" << m_filename << "\"," << endl
<< " gamma = " << m_gamma << endl
<< "]";
return oss.str();
}

View File

@ -3,5 +3,6 @@ Import('env', 'plugins')
plugins += env.SharedLibrary('#plugins/constvolume', ['constvolume.cpp'])
plugins += env.SharedLibrary('#plugins/gridvolume', ['gridvolume.cpp'])
plugins += env.SharedLibrary('#plugins/hgridvolume', ['hgridvolume.cpp'])
plugins += env.SharedLibrary('#plugins/volcache', ['volcache.cpp'])
Export('plugins')

View File

@ -85,7 +85,7 @@ public:
m_data = new float[nEntries];
stream->readSingleArray(m_data, nEntries);
} else {
std::string filename = stream->readString();
fs::path filename = stream->readString();
loadFromFile(filename);
}
configure();
@ -130,10 +130,10 @@ public:
m_aabb.expandBy(m_volumeToWorld(m_dataAABB.getCorner(i)));
}
void loadFromFile(const std::string &filename) {
void loadFromFile(const fs::path &filename) {
m_filename = filename;
fs::path resolved = Thread::getThread()->getFileResolver()->resolve(filename);
m_mmap = new MemoryMappedFile(filename);
m_mmap = new MemoryMappedFile(resolved);
ref<MemoryStream> stream = new MemoryStream(m_mmap->getData(), m_mmap->getSize());
stream->setByteOrder(Stream::ELittleEndian);
@ -162,7 +162,7 @@ public:
m_dataAABB = AABB(Point(xmin, ymin, zmin), Point(xmax, ymax, zmax));
}
Log(EDebug, "Mapped \"%s\" into memory: %ix%ix%i (%i channels), %i KiB, %s", filename.c_str(),
Log(EDebug, "Mapped \"%s\" into memory: %ix%ix%i (%i channels), %i KiB, %s", resolved.filename().c_str(),
m_res.x, m_res.y, m_res.z, m_channels, memString(m_mmap->getSize()).c_str(),
m_dataAABB.toString().c_str());
m_data = ((float *) m_mmap->getData()) + 12;
@ -200,20 +200,17 @@ public:
Float lookupFloat(const Point &_p) const {
const Point p = m_worldToGrid.transformAffine(_p);
if (p.x < 0 || p.y < 0 || p.z < 0)
return 0.0f;
const int x1 = (int) p.x, y1 = (int) p.y, z1 = (int) p.z,
x2 = x1+1, y2 = y1+1, z2 = z1+1;
const int x1 = floorToInt(p.x),
y1 = floorToInt(p.y),
z1 = floorToInt(p.z),
x2 = x1+1, y2 = y1+1, z2 = z1+1;
if (x1 < 0 || y1 < 0 || z1 < 0 || x2 >= m_res.x ||
y2 >= m_res.y || z2 >= m_res.z) {
/* Do an integer bounds test (may seem redundant - this is
to avoid a segfault, should a NaN/Inf ever find its way here..) */
if (x1 < 0 || y1 < 0 || z1 < 0 || x2 >= m_res.x ||
y2 >= m_res.y || z2 >= m_res.z)
return 0;
}
const Float fx = p.x-x1, fy = p.y-y1, fz = p.z-z1,
_fx = 1.0f - fx, _fy = 1.0f - fy, _fz = 1.0f-fz;
const Float fx = p.x - x1, fy = p.y - y1, fz = p.z - z1,
_fx = 1.0f - fx, _fy = 1.0f - fy, _fz = 1.0f - fz;
const Float
d000 = m_data[(z1*m_res.y + y1)*m_res.x + x1],
@ -234,24 +231,19 @@ public:
Spectrum lookupSpectrum(const Point &_p) const {
const Point p = m_worldToGrid.transformAffine(_p);
if (p.x < 0 || p.y < 0 || p.z < 0)
const int x1 = floorToInt(p.x),
y1 = floorToInt(p.y),
z1 = floorToInt(p.z),
x2 = x1+1, y2 = y1+1, z2 = z1+1;
if (x1 < 0 || y1 < 0 || z1 < 0 || x2 >= m_res.x ||
y2 >= m_res.y || z2 >= m_res.z)
return Spectrum(0.0f);
const int x1 = (int) p.x, y1 = (int) p.y, z1 = (int) p.z,
x2 = x1+1, y2 = y1+1, z2 = z1+1;
if (x1 < 0 || y1 < 0 || z1 < 0 || x2 >= m_res.x ||
y2 >= m_res.y || z2 >= m_res.z) {
/* Do an integer bounds test (may seem redundant - this is
to avoid a segfault, should a NaN/Inf ever find its way here..) */
return Spectrum(0.0f);
}
const Float fx = p.x-x1, fy = p.y-y1, fz = p.z-z1,
_fx = 1.0f - fx, _fy = 1.0f - fy, _fz = 1.0f-fz;
const Float fx = p.x - x1, fy = p.y - y1, fz = p.z - z1,
_fx = 1.0f - fx, _fy = 1.0f - fy, _fz = 1.0f - fz;
const float3 *spectrumData = (float3 *) m_data;
const float3
&d000 = spectrumData[(z1*m_res.y + y1)*m_res.x + x1],
&d001 = spectrumData[(z1*m_res.y + y1)*m_res.x + x2],
@ -270,45 +262,23 @@ public:
Vector lookupVector(const Point &_p) const {
const Point p = m_worldToGrid.transformAffine(_p);
if (p.x < 0 || p.y < 0 || p.z < 0)
return Vector(0.0f);
const int x1 = floorToInt(p.x),
y1 = floorToInt(p.y),
z1 = floorToInt(p.z),
x2 = x1+1, y2 = y1+1, z2 = z1+1;
const int x1 = (int) p.x, y1 = (int) p.y, z1 = (int) p.z,
x2 = x1+1, y2 = y1+1, z2 = z1+1;
if (x1 < 0 || y1 < 0 || z1 < 0 || x2 >= m_res.x ||
y2 >= m_res.y || z2 >= m_res.z) {
/* Do an integer bounds test (may seem redundant - this is
to avoid a segfault, should a NaN/Inf ever find its way here..) */
if (x1 < 0 || y1 < 0 || z1 < 0 || x2 >= m_res.x ||
y2 >= m_res.y || z2 >= m_res.z)
return Vector(0.0f);
}
const Float fx = p.x-x1, fy = p.y-y1, fz = p.z-z1;
const float3 *vectorData = (float3 *) m_data;
#if 1
/* Nearest neighbor */
return m_volumeToWorld(vectorData[
(((fz < .5) ? z1 : z2) * m_res.y +
((fy < .5) ? y1 : y2)) * m_res.x +
((fx < .5) ? x1 : x2)].toVector());
#else
Float _fx = 1.0f - fx, _fy = 1.0f - fy, _fz = 1.0f-fz;
const float3
&d000 = vectorData[(z1*m_res.y + y1)*m_res.x + x1],
&d001 = vectorData[(z1*m_res.y + y1)*m_res.x + x2],
&d010 = vectorData[(z1*m_res.y + y2)*m_res.x + x1],
&d011 = vectorData[(z1*m_res.y + y2)*m_res.x + x2],
&d100 = vectorData[(z2*m_res.y + y1)*m_res.x + x1],
&d101 = vectorData[(z2*m_res.y + y1)*m_res.x + x2],
&d110 = vectorData[(z2*m_res.y + y2)*m_res.x + x1],
&d111 = vectorData[(z2*m_res.y + y2)*m_res.x + x2];
return m_volumeToWorld((((d000*_fx + d001*fx)*_fy +
(d010*_fx + d011*fx)*fy)*_fz +
((d100*_fx + d101*fx)*_fy +
(d110*_fx + d111*fx)*fy)*fz).toVector());
#endif
(((fz < .5) ? z1 : z2) * m_res.y +
((fy < .5) ? y1 : y2)) * m_res.x +
((fx < .5) ? x1 : x2)].toVector());
}
bool supportsFloatLookups() const {

View File

@ -138,7 +138,9 @@ public:
Float lookupFloat(const Point &_p) const {
const Point p = m_worldToGrid.transformAffine(_p);
int x = (int) p.x, y = (int) p.y, z = (int) p.z;
const int x = floorToInt(p.x),
y = floorToInt(p.y),
z = floorToInt(p.z);
if (x < 0 || x >= m_res.x ||
y < 0 || y >= m_res.y ||
z < 0 || z >= m_res.z)
@ -153,7 +155,9 @@ public:
Spectrum lookupSpectrum(const Point &_p) const {
const Point p = m_worldToGrid.transformAffine(_p);
int x = (int) p.x, y = (int) p.y, z = (int) p.z;
const int x = floorToInt(p.x),
y = floorToInt(p.y),
z = floorToInt(p.z);
if (x < 0 || x >= m_res.x ||
y < 0 || y >= m_res.y ||
z < 0 || z >= m_res.z)
@ -168,11 +172,13 @@ public:
Vector lookupVector(const Point &_p) const {
const Point p = m_worldToGrid.transformAffine(_p);
int x = (int) p.x, y = (int) p.y, z = (int) p.z;
const int x = floorToInt(p.x),
y = floorToInt(p.y),
z = floorToInt(p.z);
if (x < 0 || x >= m_res.x ||
y < 0 || y >= m_res.y ||
z < 0 || z >= m_res.z)
return Vector();
return Vector(0.0f);
VolumeDataSource *block = m_blocks[((z * m_res.y) + y) * m_res.x + x];
if (block == NULL)

310
src/volume/volcache.cpp Normal file
View File

@ -0,0 +1,310 @@
/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2010 by Wenzel Jakob and others.
Mitsuba is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License Version 3
as published by the Free Software Foundation.
Mitsuba is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mitsuba/render/volume.h>
#include <mitsuba/core/properties.h>
#include <mitsuba/core/lrucache.h>
#include <mitsuba/core/statistics.h>
#include <mitsuba/core/sched.h>
#include <fstream>
MTS_NAMESPACE_BEGIN
static StatsCounter statsHitRate("Volume cache", "Cache hit rate", EPercentage);
static StatsCounter statsCreate("Volume cache", "Block creations");
static StatsCounter statsDestruct("Volume cache", "Block destructions");
static StatsCounter statsEmpty("Volume cache", "Empty blocks", EPercentage);
/* Lexicographic ordering for Vector3i */
struct Vector3iKeyOrder : public std::binary_function<Vector3i, Vector3i, bool> {
inline bool operator()(const Vector3i &v1, const Vector3i &v2) const {
if (v1.x < v2.x) return true;
else if (v1.x > v2.x) return false;
if (v1.y < v2.y) return true;
else if (v1.y > v2.y) return false;
if (v1.z < v2.z) return true;
else if (v1.z > v2.z) return false;
return false;
}
};
/**
* This class sits in between the renderer and another data source, for which
* it caches all data lookups using a LRU scheme. This is useful if the nested
* volume data source is expensive to evaluate.
*/
class CachingDataSource : public VolumeDataSource {
public:
typedef LRUCache<Vector3i, Vector3iKeyOrder, float *> BlockCache;
CachingDataSource(const Properties &props)
: VolumeDataSource(props) {
/// Size of an individual block (must be a power of 2)
m_blockSize = props.getInteger("blockSize", 4);
if (!isPowerOfTwo(m_blockSize))
Log(EError, "Block size must be a power of two!");
/* Width of an individual voxel. Will use the step size of the
nested medium by default */
m_voxelWidth = props.getFloat("voxelWidth", -1);
/* Permissible memory usage in MiB. Default: 1GiB */
m_memoryLimit = (size_t) props.getLong("memoryLimit", 32) * 1024 * 1024;
m_stepSizeMultiplier = (Float) props.getFloat("stepSizeMultiplier", 1.0f);
m_volumeToWorld = props.getTransform("toWorld", Transform());
}
CachingDataSource(Stream *stream, InstanceManager *manager)
: VolumeDataSource(stream, manager) {
m_nested = static_cast<VolumeDataSource *>(manager->getInstance(stream));
configure();
}
virtual ~CachingDataSource() {
}
void serialize(Stream *stream, InstanceManager *manager) const {
VolumeDataSource::serialize(stream, manager);
manager->serialize(stream, m_nested.get());
}
void configure() {
if (m_nested == NULL)
Log(EError, "A nested volume data source is needed!");
m_aabb = m_nested->getAABB();
if (!m_aabb.isValid())
Log(EError, "Nested axis-aligned bounding box was invalid!");
if (m_voxelWidth == -1)
m_voxelWidth = m_nested->getStepSize();
size_t memoryLimitPerCore = m_memoryLimit
/ std::max((size_t) 1, Scheduler::getInstance()->getLocalWorkerCount());
Vector totalCells = m_aabb.getExtents() / m_voxelWidth;
for (int i=0; i<3; ++i)
m_cellCount[i] = (int) std::ceil(totalCells[i]);
if (m_nested->supportsFloatLookups())
m_channels = 1;
else if (m_nested->supportsVectorLookups())
m_channels = 1;
else if (m_nested->supportsSpectrumLookups())
m_channels = SPECTRUM_SAMPLES;
else
Log(EError, "Nested volume offers no access methods!");
m_blockRes = m_blockSize+1;
int blockMemoryUsage = (int) std::pow((Float) m_blockRes, 3) * m_channels * sizeof(float);
m_blocksPerCore = memoryLimitPerCore / blockMemoryUsage;
m_worldToVolume = m_volumeToWorld.inverse();
m_worldToGrid = Transform::scale(Vector(1/m_voxelWidth))
* Transform::translate(-Vector(m_aabb.min)) * m_worldToVolume;
m_voxelMask = m_blockSize-1;
m_blockMask = ~(m_blockSize-1);
m_blockShift = log2i((uint32_t) m_blockSize);
Log(EInfo, "Volume cache configuration");
Log(EInfo, " Block size in voxels = %i", m_blockSize);
Log(EInfo, " Voxel width = %f", m_voxelWidth);
Log(EInfo, " Memory usage of one block = %s", memString(blockMemoryUsage).c_str());
Log(EInfo, " Memory limit = %s", memString(m_memoryLimit).c_str());
Log(EInfo, " Memory limit per core = %s", memString(memoryLimitPerCore).c_str());
Log(EInfo, " Max. blocks per core = %i", m_blocksPerCore);
Log(EInfo, " Effective resolution = %s", totalCells.toString().c_str());
Log(EInfo, " Effective storage = %s", memString(
totalCells[0]*totalCells[1]*totalCells[2]*sizeof(float)*m_channels).c_str());
}
Float lookupFloat(const Point &_p) const {
const Point p = m_worldToGrid.transformAffine(_p);
int x = (int) p.x, y = (int) p.y, z = (int) p.z;
if (EXPECT_NOT_TAKEN(
x < 0 || x >= m_cellCount.x ||
y < 0 || y >= m_cellCount.y ||
z < 0 || z >= m_cellCount.z))
return 0.0f;
BlockCache *cache = m_cache.get();
if (EXPECT_NOT_TAKEN(cache == NULL)) {
cache = new BlockCache(m_blocksPerCore,
boost::bind(&CachingDataSource::renderBlock, this, _1),
boost::bind(&CachingDataSource::destroyBlock, this, _1));
m_cache.set(cache);
}
#if defined(VOLCACHE_DEBUG)
if (cache->isFull()) {
/* For debugging: when the cache is full, dump locations
of all cache records into an OBJ file and exit */
std::vector<Vector3i> keys;
cache->get_keys(std::back_inserter(keys));
std::ofstream os("keys.obj");
os << "o Keys" << endl;
for (size_t i=0; i<keys.size(); i++) {
Vector3i key = keys[i];
key = key * m_blockSize + Vector3i(m_blockSize/2);
Point p(key.x * m_voxelWidth + m_aabb.min.x,
key.y * m_voxelWidth + m_aabb.min.y,
key.z * m_voxelWidth + m_aabb.min.z);
os << "v " << p.x << " " << p.y << " " << p.z << endl;
}
/// Need to generate some fake geometry so that blender will import the points
for (size_t i=3; i<=keys.size(); i++)
os << "f " << i << " " << i-1 << " " << i-2 << endl;
os.close();
_exit(-1);
}
#endif
bool hit = false;
float *blockData = cache->get(Vector3i(
(x & m_blockMask) >> m_blockShift,
(y & m_blockMask) >> m_blockShift,
(z & m_blockMask) >> m_blockShift), hit);
statsHitRate.incrementBase();
if (hit)
++statsHitRate;
if (blockData == NULL)
return 0.0f;
const int x1 = x & m_voxelMask, y1 = y & m_voxelMask, z1 = z & m_voxelMask,
x2 = x1 + 1, y2 = y1 + 1, z2 = z1 + 1;
const Float fx = p.x - x, fy = p.y - y, fz = p.z - z,
_fx = 1.0f - fx, _fy = 1.0f - fy, _fz = 1.0f - fz;
const float
&d000 = blockData[(z1*m_blockRes + y1)*m_blockRes + x1],
&d001 = blockData[(z1*m_blockRes + y1)*m_blockRes + x2],
&d010 = blockData[(z1*m_blockRes + y2)*m_blockRes + x1],
&d011 = blockData[(z1*m_blockRes + y2)*m_blockRes + x2],
&d100 = blockData[(z2*m_blockRes + y1)*m_blockRes + x1],
&d101 = blockData[(z2*m_blockRes + y1)*m_blockRes + x2],
&d110 = blockData[(z2*m_blockRes + y2)*m_blockRes + x1],
&d111 = blockData[(z2*m_blockRes + y2)*m_blockRes + x2];
float result = ((d000*_fx + d001*fx)*_fy +
(d010*_fx + d011*fx)*fy)*_fz +
((d100*_fx + d101*fx)*_fy +
(d110*_fx + d111*fx)*fy)*fz;
return result;
}
Spectrum lookupSpectrum(const Point &_p) const {
return Spectrum(0.0f);
}
Vector lookupVector(const Point &_p) const {
return Vector(0.0f);
}
bool supportsFloatLookups() const {
return m_nested->supportsFloatLookups();
}
bool supportsSpectrumLookups() const {
return m_nested->supportsSpectrumLookups();
}
bool supportsVectorLookups() const {
return m_nested->supportsVectorLookups();
}
Float getStepSize() const {
return m_voxelWidth * m_stepSizeMultiplier;
}
void addChild(const std::string &name, ConfigurableObject *child) {
if (child->getClass()->derivesFrom(VolumeDataSource::m_theClass)) {
Assert(m_nested == NULL);
m_nested = static_cast<VolumeDataSource *>(child);
} else {
VolumeDataSource::addChild(name, child);
}
}
float *renderBlock(const Vector3i &blockIdx) const {
float *result = new float[m_blockRes*m_blockRes*m_blockRes];
Point offset = m_aabb.min + Vector(
blockIdx.x * m_blockSize * m_voxelWidth,
blockIdx.y * m_blockSize * m_voxelWidth,
blockIdx.z * m_blockSize * m_voxelWidth);
int idx = 0;
bool nonempty = false;
for (int z = 0; z<m_blockRes; ++z) {
for (int y = 0; y<m_blockRes; ++y) {
for (int x = 0; x<m_blockRes; ++x) {
Point p = offset + Vector(x, y, z) * m_voxelWidth;
float value = (float) m_nested->lookupFloat(p);
result[idx++] = value;
nonempty |= (value != 0);
}
}
}
++statsCreate;
statsEmpty.incrementBase();
if (nonempty) {
return result;
} else {
++statsEmpty;
delete[] result;
return NULL;
}
}
void destroyBlock(float *ptr) const {
++statsDestruct;
delete[] ptr;
}
MTS_DECLARE_CLASS()
protected:
ref<VolumeDataSource> m_nested;
Transform m_volumeToWorld;
Transform m_worldToVolume;
Transform m_worldToGrid;
Float m_voxelWidth;
Float m_stepSizeMultiplier;
size_t m_memoryLimit;
size_t m_blocksPerCore;
int m_channels;
int m_blockSize, m_blockRes;
int m_blockMask, m_voxelMask, m_blockShift;
Vector3i m_cellCount;
mutable ThreadLocal<BlockCache> m_cache;
};
MTS_IMPLEMENT_CLASS_S(CachingDataSource, false, VolumeDataSource);
MTS_EXPORT_PLUGIN(CachingDataSource, "Caching data source");
MTS_NAMESPACE_END