merge
commit
57e0b50c38
|
@ -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 (
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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')
|
|
@ -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
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
]
|
||||
|
|
@ -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)
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ¶ms, NamedObjectMap *objects = NULL,
|
||||
bool isIncludedFile = false);
|
||||
SceneHandler(const SAXParser *parser, const ParameterMap ¶ms,
|
||||
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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,10 +25,15 @@
|
|||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
SceneHandler::SceneHandler(const ParameterMap ¶ms, 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 ¶ms, 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();
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue