Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'intern/cycles/blender')
-rw-r--r--intern/cycles/blender/CMakeLists.txt39
-rw-r--r--intern/cycles/blender/addon/__init__.py90
-rw-r--r--intern/cycles/blender/addon/engine.py74
-rw-r--r--intern/cycles/blender/addon/enums.py59
-rw-r--r--intern/cycles/blender/addon/presets.py53
-rw-r--r--intern/cycles/blender/addon/properties.py201
-rw-r--r--intern/cycles/blender/addon/ui.py748
-rw-r--r--intern/cycles/blender/addon/xml.py99
-rw-r--r--intern/cycles/blender/blender_camera.cpp290
-rw-r--r--intern/cycles/blender/blender_mesh.cpp322
-rw-r--r--intern/cycles/blender/blender_object.cpp252
-rw-r--r--intern/cycles/blender/blender_python.cpp212
-rw-r--r--intern/cycles/blender/blender_session.cpp316
-rw-r--r--intern/cycles/blender/blender_session.h81
-rw-r--r--intern/cycles/blender/blender_shader.cpp700
-rw-r--r--intern/cycles/blender/blender_sync.cpp303
-rw-r--r--intern/cycles/blender/blender_sync.h119
-rw-r--r--intern/cycles/blender/blender_util.h331
18 files changed, 4289 insertions, 0 deletions
diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt
new file mode 100644
index 00000000000..a4f290f4abd
--- /dev/null
+++ b/intern/cycles/blender/CMakeLists.txt
@@ -0,0 +1,39 @@
+
+set(sources
+ blender_camera.cpp
+ blender_mesh.cpp
+ blender_object.cpp
+ blender_python.cpp
+ blender_session.cpp
+ blender_shader.cpp
+ blender_sync.cpp
+
+ blender_sync.h
+ blender_session.h
+ blender_util.h)
+
+set(addonfiles
+ addon/__init__.py
+ addon/engine.py
+ addon/enums.py
+ addon/presets.py
+ addon/properties.py
+ addon/ui.py
+ addon/xml.py)
+
+include_directories(
+ ../render
+ ../device
+ ../kernel
+ ../kernel/svm
+ ../util
+ ../subd
+ ${BLENDER_INCLUDE_DIRS}
+ ${PYTHON_INCLUDE_DIRS}
+ ${GLEW_INCLUDE_PATH})
+
+blender_add_lib(bf_intern_cycles "${sources}" "" "")
+add_dependencies(bf_intern_cycles bf_rna)
+
+delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${addonfiles}" ${CYCLES_INSTALL_PATH})
+
diff --git a/intern/cycles/blender/addon/__init__.py b/intern/cycles/blender/addon/__init__.py
new file mode 100644
index 00000000000..979e3e872d7
--- /dev/null
+++ b/intern/cycles/blender/addon/__init__.py
@@ -0,0 +1,90 @@
+#
+# Copyright 2011, Blender Foundation.
+#
+# 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.
+#
+
+bl_info = {
+ "name": "Cycles Render Engine",
+ "author": "",
+ "version": (0,0),
+ "blender": (2, 5, 6),
+ "api": 34462,
+ "location": "Info header, render engine menu",
+ "description": "Cycles Render Engine integration.",
+ "warning": "",
+ "wiki_url": "",
+ "tracker_url": "",
+ "category": "Render"}
+
+import bpy
+
+from cycles import ui
+from cycles import properties
+from cycles import xml
+from cycles import engine
+from cycles import presets
+
+class CyclesRender(bpy.types.RenderEngine):
+ bl_idname = 'CYCLES'
+ bl_label = "Cycles"
+ bl_use_shading_nodes = True
+
+ def __init__(self):
+ engine.init()
+ self.session = None
+
+ def __del__(self):
+ engine.free(self)
+
+ # final render
+ def update(self, data, scene):
+ engine.create(self, data, scene)
+ engine.update(self, data, scene)
+
+ def render(self, scene):
+ engine.render(self)
+
+ # preview render
+ # def preview_update(self, context, id):
+ # pass
+ #
+ # def preview_render(self):
+ # pass
+
+ # viewport render
+ def view_update(self, context):
+ if not self.session:
+ engine.create(self, context.blend_data, context.scene,
+ context.region, context.space_data, context.region_data)
+ engine.update(self, context.blend_data, context.scene)
+
+ def view_draw(self, context):
+ engine.draw(self, context.region, context.space_data, context.region_data)
+
+def register():
+ properties.register()
+ ui.register()
+ xml.register()
+ presets.register()
+ bpy.utils.register_module(__name__)
+
+def unregister():
+ xml.unregister()
+ ui.unregister()
+ properties.unregister()
+ presets.unregister()
+ bpy.utils.unregister_module(__name__)
+
diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py
new file mode 100644
index 00000000000..097909ca058
--- /dev/null
+++ b/intern/cycles/blender/addon/engine.py
@@ -0,0 +1,74 @@
+#
+# Copyright 2011, Blender Foundation.
+#
+# 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.
+#
+
+import bpy
+
+def init():
+ import bcycles
+ import os.path
+
+ path = os.path.dirname(__file__)
+ user_path = os.path.dirname(os.path.abspath(bpy.utils.user_resource('CONFIG', '')))
+
+ bcycles.init(path, user_path)
+
+def create(engine, data, scene, region = 0, v3d = 0, rv3d = 0):
+ import bcycles
+
+ data = data.as_pointer()
+ scene = scene.as_pointer()
+ if region:
+ region = region.as_pointer()
+ if v3d:
+ v3d = v3d.as_pointer()
+ if rv3d:
+ rv3d = rv3d.as_pointer()
+
+ engine.session = bcycles.create(engine.as_pointer(), data, scene, region, v3d, rv3d)
+
+def free(engine):
+ if "session" in dir(engine):
+ if engine.session:
+ import bcycles
+ bcycles.free(engine.session)
+ del engine.session
+
+def render(engine):
+ import bcycles
+ bcycles.render(engine.session)
+
+def update(engine, data, scene):
+ import bcycles
+ bcycles.sync(engine.session)
+
+def draw(engine, region, v3d, rv3d):
+ import bcycles
+ v3d = v3d.as_pointer()
+ rv3d = rv3d.as_pointer()
+
+ # draw render image
+ bcycles.draw(engine.session, v3d, rv3d)
+
+def available_devices():
+ import bcycles
+ return bcycles.available_devices()
+
+def with_osl():
+ import bcycles
+ return bcycles.with_osl()
+
diff --git a/intern/cycles/blender/addon/enums.py b/intern/cycles/blender/addon/enums.py
new file mode 100644
index 00000000000..4aef2553050
--- /dev/null
+++ b/intern/cycles/blender/addon/enums.py
@@ -0,0 +1,59 @@
+#
+# Copyright 2011, Blender Foundation.
+#
+# 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.
+#
+
+from cycles import engine
+
+def get_gpu_device():
+ available_devices = engine.available_devices()
+ cuda = 'cuda' in available_devices
+ opencl = 'opencl' in available_devices
+ if cuda and opencl:
+ gpu_string = "GPU"
+ elif cuda and not opencl:
+ gpu_string = "CUDA GPU"
+ else:
+ gpu_string = "OpenCL GPU"
+
+ return gpu_string
+
+devices = (
+("CPU", "CPU", "Processor"),
+("GPU", get_gpu_device(), "Graphics card"))
+
+gpu_type = (
+("CUDA", "CUDA", "NVidia only"),
+("OPENCL", "OpenCL (incomplete)", ""))
+
+shading_systems = (
+("GPU_COMPATIBLE", "GPU Compatible", "Restricted shading system compatible with GPU rendering"),
+("OSL", "Open Shading Language", "Open Shading Language shading system that only runs on the CPU"))
+
+displacement_methods = (
+("BUMP", "Bump", "Bump mapping to simulate the appearance of displacement"),
+("TRUE", "True", "Use true displacement only, requires fine subdivision"),
+("BOTH", "Both", "Combination of displacement and bump mapping"))
+
+bvh_types = (
+("DYNAMIC_BVH", "Dynamic BVH", "Objects can be individually updated, at the cost of slower render time"),
+("STATIC_BVH", "Static BVH", "Any object modification requires a complete BVH rebuild, but renders faster"))
+
+filter_types = (
+("BOX", "Box", "Box filter"),
+("GAUSSIAN", "Gaussian", "Gaussian filter"))
+
+
diff --git a/intern/cycles/blender/addon/presets.py b/intern/cycles/blender/addon/presets.py
new file mode 100644
index 00000000000..e5243b633be
--- /dev/null
+++ b/intern/cycles/blender/addon/presets.py
@@ -0,0 +1,53 @@
+#
+# Copyright 2011, Blender Foundation.
+#
+# 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.
+#
+
+from bl_operators.presets import AddPresetBase
+from bpy.types import Operator
+
+class AddPresetIntegrator(AddPresetBase, Operator):
+ '''Add an Integrator Preset'''
+ bl_idname = "render.cycles_integrator_preset_add"
+ bl_label = "Add Integrator Preset"
+ preset_menu = "CYCLES_MT_integrator_presets"
+
+ preset_defines = [
+ "cycles = bpy.context.scene.cycles"
+ ]
+
+ preset_values = [
+ "cycles.max_bounces",
+ "cycles.min_bounces",
+ "cycles.no_caustics",
+ "cycles.diffuse_bounces",
+ "cycles.glossy_bounces",
+ "cycles.transmission_bounces",
+ "cycles.transparent_min_bounces",
+ "cycles.transparent_max_bounces"
+ ]
+
+ preset_subdir = "cycles/integrator"
+
+def register():
+ pass
+
+def unregister():
+ pass
+
+if __name__ == "__main__":
+ register()
+
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
new file mode 100644
index 00000000000..a58e4dfd154
--- /dev/null
+++ b/intern/cycles/blender/addon/properties.py
@@ -0,0 +1,201 @@
+#
+# Copyright 2011, Blender Foundation.
+#
+# 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.
+#
+
+import bpy
+from bpy.props import *
+
+import math
+
+from cycles import enums
+
+class CyclesRenderSettings(bpy.types.PropertyGroup):
+ @classmethod
+ def register(cls):
+ bpy.types.Scene.cycles = PointerProperty(type=cls, name="Cycles Render Settings", description="Cycles render settings")
+
+ cls.device = EnumProperty(name="Device", description="Device to use for rendering",
+ items=enums.devices, default="CPU")
+
+ cls.gpu_type = EnumProperty(name="GPU Type", description="Processing system to use on the GPU",
+ items=enums.gpu_type, default="CUDA")
+
+ cls.shading_system = EnumProperty(name="Shading System", description="Shading system to use for rendering",
+ items=enums.shading_systems, default="GPU_COMPATIBLE")
+
+ cls.samples = IntProperty(name="Samples", description="Number of samples to render for each pixel",
+ default=10, min=1, max=2147483647)
+ cls.preview_samples = IntProperty(name="Preview Samples", description="Number of samples to render in the viewport, unlimited if 0",
+ default=0, min=0, max=2147483647)
+ cls.preview_pause = BoolProperty(name="Pause Preview", description="Pause all viewport preview renders",
+ default=False)
+
+ cls.no_caustics = BoolProperty(name="No Caustics", description="Leave out caustics, resulting in a darker image with less noise",
+ default=False)
+ cls.blur_caustics = FloatProperty(name="Blur Caustics", description="Blur caustics to reduce noise",
+ default=0.0, min=0.0, max=1.0)
+
+ cls.min_bounces = IntProperty(name="Min Bounces", description="Minimum number of bounces, setting this lower than the maximum enables probalistic path termination (faster but noisier)",
+ default=3, min=0, max=1024)
+ cls.max_bounces = IntProperty(name="Max Bounces", description="Total maximum number of bounces",
+ default=8, min=0, max=1024)
+
+ cls.diffuse_bounces = IntProperty(name="Diffuse Bounces", description="Maximum number of diffuse reflection bounces, bounded by total maximum",
+ default=128, min=0, max=1024)
+ cls.glossy_bounces = IntProperty(name="Glossy Bounces", description="Maximum number of glossy reflection bounces, bounded by total maximum",
+ default=128, min=0, max=1024)
+ cls.transmission_bounces = IntProperty(name="Transmission Bounces", description="Maximum number of transmission bounces, bounded by total maximum",
+ default=128, min=0, max=1024)
+
+ cls.transparent_min_bounces = IntProperty(name="Transparent Min Bounces", description="Minimum number of transparent bounces, setting this lower than the maximum enables probalistic path termination (faster but noisier)",
+ default=8, min=0, max=1024)
+ cls.transparent_max_bounces = IntProperty(name="Transparent Max Bounces", description="Maximum number of transparent bounces",
+ default=8, min=0, max=1024)
+ cls.use_transparent_shadows = BoolProperty(name="Transparent Shadows", description="Use transparency of surfaces for rendering shadows",
+ default=True)
+
+ cls.film_exposure = FloatProperty(name="Exposure", description="Image brightness scale",
+ default=1.0, min=0.0, max=10.0)
+ cls.film_transparent = BoolProperty(name="Transparent", description="World background is transparent",
+ default=False)
+
+ cls.filter_type = EnumProperty(name="Filter Type", description="Pixel filter type",
+ items=enums.filter_types, default="GAUSSIAN")
+ cls.filter_width = FloatProperty(name="Filter Width", description="Pixel filter width",
+ default=1.5, min=0.01, max=10.0)
+
+ cls.seed = IntProperty(name="Seed", description="Seed value for integrator to get different noise patterns",
+ default=0, min=0, max=2147483647)
+
+ cls.debug_tile_size = IntProperty(name="Tile Size", description="",
+ default=1024, min=1, max=4096)
+ cls.debug_min_size = IntProperty(name="Min Size", description="",
+ default=64, min=1, max=4096)
+ cls.debug_reset_timeout = FloatProperty(name="Reset timeout", description="",
+ default=0.1, min=0.01, max=10.0)
+ cls.debug_cancel_timeout = FloatProperty(name="Cancel timeout", description="",
+ default=0.1, min=0.01, max=10.0)
+ cls.debug_text_timeout = FloatProperty(name="Text timeout", description="",
+ default=1.0, min=0.01, max=10.0)
+
+ cls.debug_bvh_type = EnumProperty(name="Viewport BVH Type", description="Choose between faster updates, or faster render",
+ items=enums.bvh_types, default="DYNAMIC_BVH")
+ cls.debug_use_spatial_splits = BoolProperty(name="Use Spatial Splits", description="Use BVH spatial splits: longer builder time, faster render",
+ default=False)
+
+ @classmethod
+ def unregister(cls):
+ del bpy.types.Scene.cycles
+
+class CyclesCameraSettings(bpy.types.PropertyGroup):
+ @classmethod
+ def register(cls):
+ bpy.types.Camera.cycles = PointerProperty(type=cls, name="Cycles Camera Settings", description="Cycles camera settings")
+
+ cls.aperture_size = FloatProperty(name="Aperture Size", description="Radius of the aperture for depth of field",
+ default=0.0, min=0.0, max=10.0)
+ cls.aperture_blades = IntProperty(name="Aperture Blades", description="Number of blades in aperture for polygonal bokeh (need 3 or more)",
+ default=0, min=0, max=100)
+ cls.aperture_rotation = FloatProperty(name="Aperture Rotation", description="Rotation of blades in aperture",
+ default=0, soft_min=-math.pi, soft_max=math.pi, subtype='ANGLE')
+
+ @classmethod
+ def unregister(cls):
+ del bpy.types.Camera.cycles
+
+class CyclesMaterialSettings(bpy.types.PropertyGroup):
+ @classmethod
+ def register(cls):
+ bpy.types.Material.cycles = PointerProperty(type=cls, name="Cycles Material Settings", description="Cycles material settings")
+ cls.sample_as_light = BoolProperty(name="Sample as Light", description="Use direct light sampling, to reduce noise for small or strong emitting materials", default=True)
+ cls.homogeneous_volume = BoolProperty(name="Homogeneous Volume", description="When using volume rendering, assume volume has the same density everywhere, for faster rendering", default=False)
+
+ @classmethod
+ def unregister(cls):
+ del bpy.types.Material.cycles
+
+class CyclesLampSettings(bpy.types.PropertyGroup):
+ @classmethod
+ def register(cls):
+ bpy.types.Lamp.cycles = PointerProperty(type=cls, name="Cycles Lamp Settings", description="Cycles lamp settings")
+ cls.cast_shadow = BoolProperty(name="Cast Shadow", description="Lamp casts shadows", default=True)
+
+ @classmethod
+ def unregister(cls):
+ del bpy.types.Lamp.cycles
+
+class CyclesWorldSettings(bpy.types.PropertyGroup):
+ @classmethod
+ def register(cls):
+ bpy.types.World.cycles = PointerProperty(type=cls, name="Cycles World Settings", description="Cycles world settings")
+
+ @classmethod
+ def unregister(cls):
+ del bpy.types.World.cycles
+
+class CyclesVisibilitySettings(bpy.types.PropertyGroup):
+ @classmethod
+ def register(cls):
+ bpy.types.Object.cycles_visibility = PointerProperty(type=cls, name="Cycles Visibility Settings", description="Cycles visibility settings")
+
+ cls.camera = BoolProperty(name="Camera", description="Object visibility for camera rays", default=True)
+ cls.diffuse = BoolProperty(name="Diffuse", description="Object visibility for diffuse reflection rays", default=True)
+ cls.glossy = BoolProperty(name="Glossy", description="Object visibility for glossy reflection rays", default=True)
+ cls.transmission = BoolProperty(name="Transmission", description="Object visibility for transmission rays", default=True)
+ cls.shadow = BoolProperty(name="Shadow", description="Object visibility for shadow rays", default=True)
+
+ @classmethod
+ def unregister(cls):
+ del bpy.types.Object.cycles_visibility
+
+class CyclesMeshSettings(bpy.types.PropertyGroup):
+ @classmethod
+ def register(cls):
+ bpy.types.Mesh.cycles = PointerProperty(type=cls, name="Cycles Mesh Settings", description="Cycles mesh settings")
+ bpy.types.Curve.cycles = PointerProperty(type=cls, name="Cycles Mesh Settings", description="Cycles mesh settings")
+ bpy.types.MetaBall.cycles = PointerProperty(type=cls, name="Cycles Mesh Settings", description="Cycles mesh settings")
+
+ cls.displacement_method = EnumProperty(name="Displacement Method", description="Method to use for the displacement",
+ items=enums.displacement_methods, default="BUMP")
+ cls.use_subdivision = BoolProperty(name="Use Subdivision", description="Subdivide mesh for rendering",
+ default=False)
+ cls.dicing_rate = FloatProperty(name="Dicing Rate", description="", default=1.0, min=0.001, max=1000.0)
+
+ @classmethod
+ def unregister(cls):
+ del bpy.types.Mesh.cycles
+ del bpy.types.Curve.cycles
+ del bpy.types.MetaBall.cycles
+
+def register():
+ bpy.utils.register_class(CyclesRenderSettings)
+ bpy.utils.register_class(CyclesCameraSettings)
+ bpy.utils.register_class(CyclesMaterialSettings)
+ bpy.utils.register_class(CyclesLampSettings)
+ bpy.utils.register_class(CyclesWorldSettings)
+ bpy.utils.register_class(CyclesVisibilitySettings)
+ bpy.utils.register_class(CyclesMeshSettings)
+
+def unregister():
+ bpy.utils.unregister_class(CyclesRenderSettings)
+ bpy.utils.unregister_class(CyclesCameraSettings)
+ bpy.utils.unregister_class(CyclesMaterialSettings)
+ bpy.utils.unregister_class(CyclesLampSettings)
+ bpy.utils.unregister_class(CyclesWorldSettings)
+ bpy.utils.unregister_class(CyclesMeshSettings)
+ bpy.utils.unregister_class(CyclesVisibilitySettings)
+
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
new file mode 100644
index 00000000000..58852edce88
--- /dev/null
+++ b/intern/cycles/blender/addon/ui.py
@@ -0,0 +1,748 @@
+#
+# Copyright 2011, Blender Foundation.
+#
+# 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.
+#
+
+import bpy
+
+from bpy.types import Panel, Menu
+
+from cycles import enums
+from cycles import engine
+
+class CYCLES_MT_integrator_presets(Menu):
+ bl_label = "Integrator Presets"
+ preset_subdir = "cycles/integrator"
+ preset_operator = "script.execute_preset"
+ COMPAT_ENGINES = {'CYCLES'}
+ draw = Menu.draw_preset
+
+class CyclesButtonsPanel():
+ bl_space_type = "PROPERTIES"
+ bl_region_type = "WINDOW"
+ bl_context = "render"
+
+ @classmethod
+ def poll(cls, context):
+ rd = context.scene.render
+ return rd.engine == 'CYCLES'
+
+class CyclesRender_PT_integrator(CyclesButtonsPanel, Panel):
+ bl_label = "Integrator"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+ cscene = scene.cycles
+
+ row = layout.row(align=True)
+ row.menu("CYCLES_MT_integrator_presets", text=bpy.types.CYCLES_MT_integrator_presets.bl_label)
+ row.operator("render.cycles_integrator_preset_add", text="", icon="ZOOMIN")
+ row.operator("render.cycles_integrator_preset_add", text="", icon="ZOOMOUT").remove_active = True
+
+ split = layout.split()
+
+ col = split.column()
+ sub = col.column(align=True)
+ sub.label(text="Samples:")
+ sub.prop(cscene, "samples", text="Render")
+ sub.prop(cscene, "preview_samples", text="Preview")
+ sub.prop(cscene, "seed")
+
+ sub = col.column(align=True)
+ sub.label("Transparency:")
+ sub.prop(cscene, "transparent_max_bounces", text="Max")
+ sub.prop(cscene, "transparent_min_bounces", text="Min")
+ sub.prop(cscene, "use_transparent_shadows", text="Shadows")
+
+ col = split.column()
+
+ sub = col.column(align=True)
+ sub.label(text="Bounces:")
+ sub.prop(cscene, "max_bounces", text="Max")
+ sub.prop(cscene, "min_bounces", text="Min")
+
+ sub = col.column(align=True)
+ sub.label(text="Light Paths:")
+ sub.prop(cscene, "diffuse_bounces", text="Diffuse")
+ sub.prop(cscene, "glossy_bounces", text="Glossy")
+ sub.prop(cscene, "transmission_bounces", text="Transmission")
+ sub.prop(cscene, "no_caustics")
+
+ #row = col.row()
+ #row.prop(cscene, "blur_caustics")
+ #row.active = not cscene.no_caustics
+
+class CyclesRender_PT_film(CyclesButtonsPanel, Panel):
+ bl_label = "Film"
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+ cscene = scene.cycles
+
+ split = layout.split()
+
+ col = split.column();
+ col.prop(cscene, "film_exposure")
+ col.prop(cscene, "film_transparent")
+
+ col = split.column()
+ sub = col.column(align=True)
+ sub.prop(cscene, "filter_type", text="")
+ if cscene.filter_type != 'BOX':
+ sub.prop(cscene, "filter_width", text="Width")
+
+class CyclesRender_PT_performance(CyclesButtonsPanel, Panel):
+ bl_label = "Performance"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+ rd = scene.render
+ cscene = scene.cycles
+
+ split = layout.split()
+
+ col = split.column(align=True)
+
+ col.label(text="Threads:")
+ col.row().prop(rd, "threads_mode", expand=True)
+ sub = col.column()
+ sub.enabled = rd.threads_mode == 'FIXED'
+ sub.prop(rd, "threads")
+
+ sub = col.column(align=True)
+ sub.label(text="Tiles:")
+ sub.prop(cscene, "debug_tile_size")
+ sub.prop(cscene, "debug_min_size")
+
+ col = split.column()
+
+ sub = col.column(align=True)
+ sub.label(text="Acceleration structure:")
+ sub.prop(cscene, "debug_bvh_type", text="")
+ sub.prop(cscene, "debug_use_spatial_splits")
+
+class CyclesRender_PT_layers(CyclesButtonsPanel, Panel):
+ bl_label = "Layers"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+ rd = scene.render
+
+ # row = layout.row()
+ # row.template_list(rd, "layers", rd.layers, "active_index", rows=2)
+
+ # col = row.column(align=True)
+ # col.operator("scene.render_layer_add", icon='ZOOMIN', text="")
+ # col.operator("scene.render_layer_remove", icon='ZOOMOUT', text="")
+
+ row = layout.row()
+ # rl = rd.layers.active
+ rl = rd.layers[0]
+ row.prop(rl, "name")
+ #row.prop(rd, "use_single_layer", text="", icon_only=True)
+
+ split = layout.split()
+
+ col = split.column()
+ col.prop(scene, "layers", text="Scene")
+
+ col = split.column()
+ col.prop(rl, "layers", text="Layer")
+
+ layout.separator()
+
+ layout.prop(rl, "material_override", text="Material")
+
+class Cycles_PT_post_processing(CyclesButtonsPanel, Panel):
+ bl_label = "Post Processing"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ rd = context.scene.render
+
+ split = layout.split()
+
+ col = split.column()
+ col.prop(rd, "use_compositing")
+ col.prop(rd, "use_sequencer")
+
+ col = split.column()
+ col.prop(rd, "dither_intensity", text="Dither", slider=True)
+
+class CyclesCamera_PT_dof(CyclesButtonsPanel, Panel):
+ bl_label = "Depth of Field"
+ bl_context = "data"
+
+ @classmethod
+ def poll(cls, context):
+ return context.camera and CyclesButtonsPanel.poll(context)
+
+ def draw(self, context):
+ layout = self.layout
+
+ cam = context.camera
+ ccam = cam.cycles
+
+ split = layout.split()
+
+ col = split.column()
+ col.label("Focus:")
+ col.prop(cam, "dof_object", text="")
+
+ sub = col.row()
+ sub.active = cam.dof_object is None
+ sub.prop(cam, "dof_distance", text="Distance")
+
+ col = split.column()
+
+ col.label("Aperture:")
+ col.prop(ccam, "aperture_size", text="Size")
+
+ sub = col.column(align=True)
+ sub.prop(ccam, "aperture_blades", text="Blades")
+ sub.prop(ccam, "aperture_rotation", text="Rotation")
+
+class Cycles_PT_context_material(CyclesButtonsPanel, Panel):
+ bl_label = "Surface"
+ bl_context = "material"
+ bl_options = {'HIDE_HEADER'}
+
+ @classmethod
+ def poll(cls, context):
+ return (context.material or context.object) and CyclesButtonsPanel.poll(context)
+
+ def draw(self, context):
+ layout = self.layout
+
+ mat = context.material
+ ob = context.object
+ slot = context.material_slot
+ space = context.space_data
+
+ if ob:
+ row = layout.row()
+
+ row.template_list(ob, "material_slots", ob, "active_material_index", rows=2)
+
+ col = row.column(align=True)
+ col.operator("object.material_slot_add", icon='ZOOMIN', text="")
+ col.operator("object.material_slot_remove", icon='ZOOMOUT', text="")
+
+ col.menu("MATERIAL_MT_specials", icon='DOWNARROW_HLT', text="")
+
+ if ob.mode == 'EDIT':
+ row = layout.row(align=True)
+ row.operator("object.material_slot_assign", text="Assign")
+ row.operator("object.material_slot_select", text="Select")
+ row.operator("object.material_slot_deselect", text="Deselect")
+
+ split = layout.split(percentage=0.65)
+
+ if ob:
+ split.template_ID(ob, "active_material", new="material.new")
+ row = split.row()
+
+ if slot:
+ row.prop(slot, "link", text="")
+ else:
+ row.label()
+ elif mat:
+ split.template_ID(space, "pin_id")
+ split.separator()
+
+class Cycles_PT_mesh_displacement(CyclesButtonsPanel, Panel):
+ bl_label = "Displacement"
+ bl_context = "data"
+
+ @classmethod
+ def poll(cls, context):
+ return context.mesh or context.curve or context.meta_ball
+
+ def draw(self, context):
+ layout = self.layout
+
+ mesh = context.mesh
+ curve = context.curve
+ mball = context.meta_ball
+
+ if mesh:
+ cdata = mesh.cycles
+ elif curve:
+ cdata = curve.cycles
+ elif mball:
+ cdata = mball.cycles
+
+ layout.prop(cdata, "displacement_method", text="Method")
+ layout.prop(cdata, "use_subdivision");
+ layout.prop(cdata, "dicing_rate");
+
+class CyclesObject_PT_ray_visibility(CyclesButtonsPanel, Panel):
+ bl_label = "Ray Visibility"
+ bl_context = "object"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ ob = context.object
+ return CyclesButtonsPanel.poll(context) and ob and ob.type in ('MESH', 'CURVE', 'CURVE', 'SURFACE', 'FONT', 'META') # todo: 'LAMP'
+
+ def draw(self, context):
+ layout = self.layout
+
+ ob = context.object
+ visibility = ob.cycles_visibility
+
+ split = layout.split()
+
+ col = split.column()
+ col.prop(visibility, "camera")
+ col.prop(visibility, "diffuse")
+ col.prop(visibility, "glossy")
+
+ col = split.column()
+ col.prop(visibility, "transmission")
+ col.prop(visibility, "shadow")
+
+def find_node(material, nodetype):
+ if material and material.node_tree:
+ ntree = material.node_tree
+
+ for node in ntree.nodes:
+ if hasattr(node, 'type') and node.type == nodetype:
+ return node
+
+ return None
+
+def find_node_input(node, name):
+ for input in node.inputs:
+ if input.name == name:
+ return input
+
+ return None
+
+def panel_node_draw(layout, id, output_type, input_name):
+ if not id.node_tree:
+ layout.prop(id, "use_nodes", icon='NODETREE')
+ return
+
+ ntree = id.node_tree
+
+ node = find_node(id, output_type)
+ if not node:
+ layout.label(text="No output node.")
+ else:
+ input = find_node_input(node, input_name)
+ layout.template_node_view(ntree, node, input);
+
+class CyclesLamp_PT_lamp(CyclesButtonsPanel, Panel):
+ bl_label = "Lamp"
+ bl_context = "data"
+
+ @classmethod
+ def poll(cls, context):
+ return context.lamp and CyclesButtonsPanel.poll(context)
+
+ def draw(self, context):
+ layout = self.layout
+
+ lamp = context.lamp
+ clamp = lamp.cycles
+
+ layout.prop(lamp, "type", expand=True)
+
+ split = layout.split()
+ col = split.column(align=True)
+
+ if lamp.type in ('POINT', 'SUN', 'SPOT'):
+ col.prop(lamp, "shadow_soft_size", text="Size")
+ elif lamp.type == 'AREA':
+ col.prop(lamp, "shape", text="")
+ sub = col.column(align=True)
+
+ if lamp.shape == 'SQUARE':
+ sub.prop(lamp, "size")
+ elif lamp.shape == 'RECTANGLE':
+ sub.prop(lamp, "size", text="Size X")
+ sub.prop(lamp, "size_y", text="Size Y")
+
+ col = split.column()
+ col.prop(clamp, "cast_shadow")
+
+ if lamp.type == 'SPOT':
+ layout.label(text="Not supported, interpreted as point lamp.")
+ elif lamp.type == 'HEMI':
+ layout.label(text="Not supported, interpreted as sun lamp.")
+
+class CyclesLamp_PT_nodes(CyclesButtonsPanel, Panel):
+ bl_label = "Nodes"
+ bl_context = "data"
+
+ @classmethod
+ def poll(cls, context):
+ return context.lamp and CyclesButtonsPanel.poll(context)
+
+ def draw(self, context):
+ layout = self.layout
+
+ mat = context.lamp
+ panel_node_draw(layout, mat, 'OUTPUT_LAMP', 'Surface')
+
+class CyclesWorld_PT_surface(CyclesButtonsPanel, Panel):
+ bl_label = "Surface"
+ bl_context = "world"
+
+ @classmethod
+ def poll(cls, context):
+ return context.world and CyclesButtonsPanel.poll(context)
+
+ def draw(self, context):
+ layout = self.layout
+
+ mat = context.world
+ panel_node_draw(layout, mat, 'OUTPUT_WORLD', 'Surface')
+
+class CyclesWorld_PT_volume(CyclesButtonsPanel, Panel):
+ bl_label = "Volume"
+ bl_context = "world"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ world = context.world
+ return world and world.node_tree and CyclesButtonsPanel.poll(context)
+
+ def draw(self, context):
+ layout = self.layout
+ layout.active = False
+
+ world = context.world
+ panel_node_draw(layout, world, 'OUTPUT_WORLD', 'Volume')
+
+class CyclesMaterial_PT_surface(CyclesButtonsPanel, Panel):
+ bl_label = "Surface"
+ bl_context = "material"
+
+ @classmethod
+ def poll(cls, context):
+ return context.material and CyclesButtonsPanel.poll(context)
+
+ def draw(self, context):
+ layout = self.layout
+
+ mat = context.material
+ panel_node_draw(layout, mat, 'OUTPUT_MATERIAL', 'Surface')
+
+class CyclesMaterial_PT_volume(CyclesButtonsPanel, Panel):
+ bl_label = "Volume"
+ bl_context = "material"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ mat = context.material
+ return mat and mat.node_tree and CyclesButtonsPanel.poll(context)
+
+ def draw(self, context):
+ layout = self.layout
+ layout.active = False
+
+ mat = context.material
+ cmat = mat.cycles
+
+ panel_node_draw(layout, mat, 'OUTPUT_MATERIAL', 'Volume')
+
+ layout.prop(cmat, "homogeneous_volume")
+
+class CyclesMaterial_PT_displacement(CyclesButtonsPanel, Panel):
+ bl_label = "Displacement"
+ bl_context = "material"
+
+ @classmethod
+ def poll(cls, context):
+ mat = context.material
+ return mat and mat.node_tree and CyclesButtonsPanel.poll(context)
+
+ def draw(self, context):
+ layout = self.layout
+
+ mat = context.material
+ panel_node_draw(layout, mat, 'OUTPUT_MATERIAL', 'Displacement')
+
+class CyclesMaterial_PT_settings(CyclesButtonsPanel, Panel):
+ bl_label = "Settings"
+ bl_context = "material"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ return context.material and CyclesButtonsPanel.poll(context)
+
+ def draw(self, context):
+ layout = self.layout
+
+ mat = context.material
+ cmat = mat.cycles
+
+ split = layout.split()
+
+ col = split.column()
+ col.prop(mat, "diffuse_color", text="Viewport Color")
+
+ col = split.column()
+ col.prop(cmat, "sample_as_light")
+
+class CyclesTexture_PT_context(CyclesButtonsPanel, Panel):
+ bl_label = ""
+ bl_context = "texture"
+ bl_options = {'HIDE_HEADER'}
+ COMPAT_ENGINES = {'CYCLES'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ tex = context.texture
+ space = context.space_data
+ pin_id = space.pin_id
+ use_pin_id = space.use_pin_id;
+ user = context.texture_user
+ node = context.texture_node
+
+ if not use_pin_id or not isinstance(pin_id, bpy.types.Texture):
+ pin_id = None
+
+ if not pin_id:
+ layout.template_texture_user()
+
+ if user:
+ layout.separator()
+
+ split = layout.split(percentage=0.65)
+ col = split.column()
+
+ if pin_id:
+ col.template_ID(space, "pin_id")
+ elif user:
+ col.template_ID(user, "texture", new="texture.new")
+
+ if tex:
+ row = split.row()
+ row.prop(tex, "use_nodes", icon="NODETREE", text="")
+ row.label()
+
+ if not tex.use_nodes:
+ split = layout.split(percentage=0.2)
+ split.label(text="Type:")
+ split.prop(tex, "type", text="")
+
+class CyclesTexture_PT_nodes(CyclesButtonsPanel, Panel):
+ bl_label = "Nodes"
+ bl_context = "texture"
+
+ @classmethod
+ def poll(cls, context):
+ tex = context.texture
+ return (tex and tex.use_nodes) and CyclesButtonsPanel.poll(context)
+
+ def draw(self, context):
+ layout = self.layout
+
+ tex = context.texture
+ panel_node_draw(layout, tex, 'OUTPUT_TEXTURE', 'Color')
+
+class CyclesTexture_PT_node(CyclesButtonsPanel, Panel):
+ bl_label = "Node"
+ bl_context = "texture"
+
+ @classmethod
+ def poll(cls, context):
+ node = context.texture_node
+ return node and CyclesButtonsPanel.poll(context)
+
+ def draw(self, context):
+ layout = self.layout
+
+ node = context.texture_node
+ ntree = node.id_data
+ layout.template_node_view(ntree, node, None)
+
+class CyclesTexture_PT_mapping(CyclesButtonsPanel, Panel):
+ bl_label = "Mapping"
+ bl_context = "texture"
+
+ @classmethod
+ def poll(cls, context):
+ tex = context.texture
+ node = context.texture_node
+ return (node or (tex and tex.use_nodes)) and CyclesButtonsPanel.poll(context)
+
+ def draw(self, context):
+ layout = self.layout
+
+ tex = context.texture
+ node = context.texture_node
+
+ mapping = node.texture_mapping
+
+ row = layout.row()
+
+ row.column().prop(mapping, "location")
+ row.column().prop(mapping, "rotation")
+ row.column().prop(mapping, "scale")
+
+ layout.label(text="Projection:")
+
+ row = layout.row()
+ row.prop(mapping, "mapping_x", text="")
+ row.prop(mapping, "mapping_y", text="")
+ row.prop(mapping, "mapping_z", text="")
+
+class CyclesTexture_PT_colors(CyclesButtonsPanel, Panel):
+ bl_label = "Color"
+ bl_context = "texture"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ tex = context.texture
+ node = context.texture_node
+ return False
+ #return (node or (tex and tex.use_nodes)) and CyclesButtonsPanel.poll(context)
+
+ def draw(self, context):
+ layout = self.layout
+
+ tex = context.texture
+ node = context.texture_node
+
+ mapping = node.color_mapping
+
+ split = layout.split()
+
+ col = split.column()
+ col.label(text="Blend:")
+ col.prop(mapping, "blend_type", text="")
+ col.prop(mapping, "blend_factor", text="Factor")
+ col.prop(mapping, "blend_color", text="")
+
+ col = split.column()
+ col.label(text="Adjust:")
+ col.prop(mapping, "brightness")
+ col.prop(mapping, "contrast")
+ col.prop(mapping, "saturation")
+
+ layout.separator()
+
+ layout.prop(mapping, "use_color_ramp", text="Ramp")
+ if mapping.use_color_ramp:
+ layout.template_color_ramp(mapping, "color_ramp", expand=True)
+
+def draw_device(self, context):
+ scene = context.scene
+ layout = self.layout
+
+ if scene.render.engine == "CYCLES":
+ cscene = scene.cycles
+
+ available_devices = engine.available_devices()
+ available_cuda = 'cuda' in available_devices
+ available_opencl = 'opencl' in available_devices
+
+ if available_cuda or available_opencl:
+ layout.prop(cscene, "device")
+ if cscene.device == 'GPU' and available_cuda and available_opencl:
+ layout.prop(cscene, "gpu_type")
+ if cscene.device == 'CPU' and engine.with_osl():
+ layout.prop(cscene, "shading_system")
+
+def draw_pause(self, context):
+ layout = self.layout
+ scene = context.scene
+
+ if scene.render.engine == "CYCLES":
+ view = context.space_data
+
+ if view.viewport_shade == "RENDERED":
+ cscene = scene.cycles
+ layout.prop(cscene, "preview_pause", icon="PAUSE", text="")
+
+def get_panels():
+ return [
+ bpy.types.RENDER_PT_render,
+ bpy.types.RENDER_PT_output,
+ bpy.types.RENDER_PT_encoding,
+ bpy.types.RENDER_PT_dimensions,
+ bpy.types.RENDER_PT_stamp,
+ bpy.types.WORLD_PT_context_world,
+ bpy.types.DATA_PT_context_mesh,
+ bpy.types.DATA_PT_context_camera,
+ bpy.types.DATA_PT_context_lamp,
+ bpy.types.DATA_PT_texture_space,
+ bpy.types.DATA_PT_curve_texture_space,
+ bpy.types.DATA_PT_mball_texture_space,
+ bpy.types.DATA_PT_vertex_groups,
+ bpy.types.DATA_PT_shape_keys,
+ bpy.types.DATA_PT_uv_texture,
+ bpy.types.DATA_PT_vertex_colors,
+ bpy.types.DATA_PT_camera,
+ bpy.types.DATA_PT_camera_display,
+ bpy.types.DATA_PT_lens,
+ bpy.types.DATA_PT_custom_props_mesh,
+ bpy.types.DATA_PT_custom_props_camera,
+ bpy.types.DATA_PT_custom_props_lamp,
+ bpy.types.TEXTURE_PT_clouds,
+ bpy.types.TEXTURE_PT_wood,
+ bpy.types.TEXTURE_PT_marble,
+ bpy.types.TEXTURE_PT_magic,
+ bpy.types.TEXTURE_PT_blend,
+ bpy.types.TEXTURE_PT_stucci,
+ bpy.types.TEXTURE_PT_image,
+ bpy.types.TEXTURE_PT_image_sampling,
+ bpy.types.TEXTURE_PT_image_mapping,
+ bpy.types.TEXTURE_PT_musgrave,
+ bpy.types.TEXTURE_PT_voronoi,
+ bpy.types.TEXTURE_PT_distortednoise,
+ bpy.types.TEXTURE_PT_voxeldata,
+ bpy.types.TEXTURE_PT_pointdensity,
+ bpy.types.TEXTURE_PT_pointdensity_turbulence]
+
+def register():
+ bpy.types.RENDER_PT_render.append(draw_device)
+ bpy.types.VIEW3D_HT_header.append(draw_pause)
+
+ for panel in get_panels():
+ panel.COMPAT_ENGINES.add('CYCLES')
+
+def unregister():
+ bpy.types.RENDER_PT_render.remove(draw_device)
+ bpy.types.VIEW3D_HT_header.remove(draw_pause)
+
+ for panel in get_panels():
+ panel.COMPAT_ENGINES.remove('CYCLES')
+
diff --git a/intern/cycles/blender/addon/xml.py b/intern/cycles/blender/addon/xml.py
new file mode 100644
index 00000000000..3713da09235
--- /dev/null
+++ b/intern/cycles/blender/addon/xml.py
@@ -0,0 +1,99 @@
+#
+# Copyright 2011, Blender Foundation.
+#
+# 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.
+#
+
+# XML exporter for generating test files, not intended for end users
+
+import os
+import bpy
+from bpy_extras.io_utils import ExportHelper
+import xml.etree.ElementTree as etree
+import xml.dom.minidom as dom
+
+def strip(root):
+ root.text = None
+ root.tail = None
+
+ for elem in root:
+ strip(elem)
+
+def write(node, fname):
+ strip(node)
+
+ s = etree.tostring(node)
+ s = dom.parseString(s).toprettyxml()
+
+ f = open(fname, "w")
+ f.write(s)
+
+class ExportCyclesXML(bpy.types.Operator, ExportHelper):
+ ''''''
+ bl_idname = "export_mesh.cycles_xml"
+ bl_label = "Export Cycles XML"
+
+ filename_ext = ".xml"
+
+ @classmethod
+ def poll(cls, context):
+ return context.active_object != None
+
+ def execute(self, context):
+ filepath = bpy.path.ensure_ext(self.filepath, ".xml")
+
+ # get mesh
+ scene = context.scene
+ object = context.object
+
+ if not object:
+ raise Exception("No active object")
+
+ mesh = object.to_mesh(scene, True, 'PREVIEW')
+
+ if not mesh:
+ raise Exception("No mesh data in active object")
+
+ # generate mesh node
+ nverts = ""
+ verts = ""
+ P = ""
+
+ for v in mesh.vertices:
+ P += "%f %f %f " % (v.co[0], v.co[1], v.co[2])
+
+ for i, f in enumerate(mesh.faces):
+ nverts += str(len(f.vertices)) + " "
+
+ for v in f.vertices:
+ verts += str(v) + " "
+ verts += " "
+
+ node = etree.Element('mesh', attrib={'nverts': nverts, 'verts': verts, 'P': P})
+
+ # write to file
+ write(node, filepath)
+
+ return {'FINISHED'}
+
+def register():
+ pass
+
+def unregister():
+ pass
+
+if __name__ == "__main__":
+ register()
+
diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp
new file mode 100644
index 00000000000..2a2c2a7c643
--- /dev/null
+++ b/intern/cycles/blender/blender_camera.cpp
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2011, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include "camera.h"
+#include "scene.h"
+
+#include "blender_sync.h"
+#include "blender_util.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Blender Camera Intermediate: we first convert both the offline and 3d view
+ * render camera to this, and from there convert to our native camera format. */
+
+struct BlenderCamera {
+ float nearclip;
+ float farclip;
+
+ bool ortho;
+ float ortho_scale;
+
+ float lens;
+
+ float aperturesize;
+ uint apertureblades;
+ float aperturerotation;
+ float focaldistance;
+
+ float2 shift;
+ float2 offset;
+ float zoom;
+
+ float2 pixelaspect;
+
+ enum { AUTO, HORIZONTAL, VERTICAL } sensor_fit;
+ float sensor_width;
+ float sensor_height;
+
+ Transform matrix;
+};
+
+static void blender_camera_init(BlenderCamera *bcam)
+{
+ memset(bcam, 0, sizeof(BlenderCamera));
+
+ bcam->zoom = 1.0f;
+ bcam->pixelaspect = make_float2(1.0f, 1.0f);
+ bcam->sensor_width = 32.0f;
+ bcam->sensor_height = 18.0f;
+ bcam->sensor_fit = BlenderCamera::AUTO;
+}
+
+static float blender_camera_focal_distance(BL::Object b_ob, BL::Camera b_camera)
+{
+ BL::Object b_dof_object = b_camera.dof_object();
+
+ if(!b_dof_object)
+ return b_camera.dof_distance();
+
+ /* for dof object, return distance along camera direction. this is
+ * compatible with blender, but does it fit our dof model? */
+ Transform obmat = get_transform(b_ob.matrix_world());
+ Transform dofmat = get_transform(b_dof_object.matrix_world());
+
+ float3 cam_p = transform_get_column(&obmat, 3);
+ float3 cam_dir = normalize(transform_get_column(&obmat, 2));
+ float3 dof_p = transform_get_column(&dofmat, 3);
+ float3 proj_p = dot(dof_p, cam_dir) * cam_dir;
+
+ return len(proj_p - cam_p);
+}
+
+static void blender_camera_from_object(BlenderCamera *bcam, BL::Object b_ob)
+{
+ BL::ID b_ob_data = b_ob.data();
+
+ if(b_ob_data.is_a(&RNA_Camera)) {
+ BL::Camera b_camera(b_ob_data);
+ PointerRNA ccamera = RNA_pointer_get(&b_camera.ptr, "cycles");
+
+ bcam->nearclip = b_camera.clip_start();
+ bcam->farclip = b_camera.clip_end();
+
+ bcam->ortho = (b_camera.type() == BL::Camera::type_ORTHO);
+ bcam->ortho_scale = b_camera.ortho_scale();
+
+ bcam->lens = b_camera.lens();
+ bcam->aperturesize = RNA_float_get(&ccamera, "aperture_size");
+ bcam->apertureblades = RNA_int_get(&ccamera, "aperture_blades");
+ bcam->aperturerotation = RNA_float_get(&ccamera, "aperture_rotation");
+ bcam->focaldistance = blender_camera_focal_distance(b_ob, b_camera);
+
+ bcam->shift.x = b_camera.shift_x();
+ bcam->shift.y = b_camera.shift_y();
+
+ bcam->sensor_width = b_camera.sensor_width();
+ bcam->sensor_height = b_camera.sensor_height();
+
+ if(b_camera.sensor_fit() == BL::Camera::sensor_fit_AUTO)
+ bcam->sensor_fit = BlenderCamera::AUTO;
+ else if(b_camera.sensor_fit() == BL::Camera::sensor_fit_HORIZONTAL)
+ bcam->sensor_fit = BlenderCamera::HORIZONTAL;
+ else
+ bcam->sensor_fit = BlenderCamera::VERTICAL;
+ }
+ else {
+ /* from lamp not implemented yet */
+ }
+}
+
+static void blender_camera_sync(Camera *cam, BlenderCamera *bcam, int width, int height)
+{
+ /* copy camera to compare later */
+ Camera prevcam = *cam;
+
+ /* dimensions */
+ float xratio = width*bcam->pixelaspect.x;
+ float yratio = height*bcam->pixelaspect.y;
+
+ /* compute x/y aspect and ratio */
+ float aspectratio, xaspect, yaspect;
+
+ /* sensor fitting */
+ bool horizontal_fit;
+ float sensor_size;
+
+ if(bcam->sensor_fit == BlenderCamera::AUTO) {
+ horizontal_fit = (xratio > yratio);
+ sensor_size = bcam->sensor_width;
+ }
+ else if(bcam->sensor_fit == BlenderCamera::HORIZONTAL) {
+ horizontal_fit = true;
+ sensor_size = bcam->sensor_width;
+ }
+ else {
+ horizontal_fit = false;
+ sensor_size = bcam->sensor_height;
+ }
+
+ if(horizontal_fit) {
+ aspectratio= xratio/yratio;
+ xaspect= aspectratio;
+ yaspect= 1.0f;
+ }
+ else {
+ aspectratio= yratio/xratio;
+ xaspect= 1.0f;
+ yaspect= aspectratio;
+ }
+
+ /* modify aspect for orthographic scale */
+ if(bcam->ortho) {
+ xaspect = xaspect*bcam->ortho_scale/(aspectratio*2.0f);
+ yaspect = yaspect*bcam->ortho_scale/(aspectratio*2.0f);
+ aspectratio = bcam->ortho_scale/2.0f;
+ }
+
+ /* set viewplane */
+ cam->left = -xaspect;
+ cam->right = xaspect;
+ cam->bottom = -yaspect;
+ cam->top = yaspect;
+
+ /* zoom for 3d camera view */
+ cam->left *= bcam->zoom;
+ cam->right *= bcam->zoom;
+ cam->bottom *= bcam->zoom;
+ cam->top *= bcam->zoom;
+
+ /* modify viewplane with camera shift and 3d camera view offset */
+ float dx = 2.0f*(aspectratio*bcam->shift.x + bcam->offset.x*xaspect*2.0f);
+ float dy = 2.0f*(aspectratio*bcam->shift.y + bcam->offset.y*yaspect*2.0f);
+
+ cam->left += dx;
+ cam->right += dx;
+ cam->bottom += dy;
+ cam->top += dy;
+
+ /* clipping distances */
+ cam->nearclip = bcam->nearclip;
+ cam->farclip = bcam->farclip;
+
+ /* orthographic */
+ cam->ortho = bcam->ortho;
+
+ /* perspective */
+ cam->fov = 2.0f*atan((0.5f*sensor_size)/bcam->lens/aspectratio);
+ cam->focaldistance = bcam->focaldistance;
+ cam->aperturesize = bcam->aperturesize;
+ cam->blades = bcam->apertureblades;
+ cam->bladesrotation = bcam->aperturerotation;
+
+ /* transform, note the blender camera points along the negative z-axis */
+ cam->matrix = bcam->matrix * transform_scale(1.0f, 1.0f, -1.0f);
+
+ /* set update flag */
+ if(cam->modified(prevcam))
+ cam->tag_update();
+}
+
+/* Sync Render Camera */
+
+void BlenderSync::sync_camera(int width, int height)
+{
+ BlenderCamera bcam;
+ blender_camera_init(&bcam);
+
+ /* pixel aspect */
+ BL::RenderSettings r = b_scene.render();
+
+ bcam.pixelaspect.x = r.pixel_aspect_x();
+ bcam.pixelaspect.y = r.pixel_aspect_y();
+
+ /* camera object */
+ BL::Object b_ob = b_scene.camera();
+
+ if(b_ob) {
+ blender_camera_from_object(&bcam, b_ob);
+ bcam.matrix = get_transform(b_ob.matrix_world());
+ }
+
+ /* sync */
+ Camera *cam = scene->camera;
+ blender_camera_sync(cam, &bcam, width, height);
+}
+
+/* Sync 3D View Camera */
+
+void BlenderSync::sync_view(BL::SpaceView3D b_v3d, BL::RegionView3D b_rv3d, int width, int height)
+{
+ BlenderCamera bcam;
+ blender_camera_init(&bcam);
+
+ /* 3d view parameters */
+ bcam.nearclip = b_v3d.clip_start();
+ bcam.farclip = b_v3d.clip_end();
+ bcam.lens = b_v3d.lens();
+
+ if(b_rv3d.view_perspective() == BL::RegionView3D::view_perspective_CAMERA) {
+ /* camera view */
+ BL::Object b_ob = b_scene.camera();
+
+ if(b_ob) {
+ blender_camera_from_object(&bcam, b_ob);
+
+ /* magic zoom formula */
+ bcam.zoom = (float)b_rv3d.view_camera_zoom();
+ bcam.zoom = (1.41421f + bcam.zoom/50.0f);
+ bcam.zoom *= bcam.zoom;
+ bcam.zoom = 2.0f/bcam.zoom;
+
+ /* offset */
+ bcam.offset = get_float2(b_rv3d.view_camera_offset());
+ }
+ }
+ else if(b_rv3d.view_perspective() == BL::RegionView3D::view_perspective_ORTHO) {
+ /* orthographic view */
+ bcam.farclip *= 0.5;
+ bcam.nearclip = -bcam.farclip;
+
+ bcam.ortho = true;
+ bcam.ortho_scale = b_rv3d.view_distance();
+ }
+
+ bcam.zoom *= 2.0f;
+
+ /* 3d view transform */
+ bcam.matrix = transform_inverse(get_transform(b_rv3d.view_matrix()));
+
+ /* sync */
+ blender_camera_sync(scene->camera, &bcam, width, height);
+}
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp
new file mode 100644
index 00000000000..3bf83fa533f
--- /dev/null
+++ b/intern/cycles/blender/blender_mesh.cpp
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2011, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include "mesh.h"
+#include "object.h"
+#include "scene.h"
+
+#include "blender_sync.h"
+#include "blender_util.h"
+
+#include "subd_mesh.h"
+#include "subd_patch.h"
+#include "subd_split.h"
+
+#include "util_foreach.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Find/Add */
+
+static bool mesh_need_attribute(Scene *scene, Mesh *mesh, Attribute::Standard std)
+{
+ if(std == Attribute::STD_NONE)
+ return false;
+
+ foreach(uint shader, mesh->used_shaders)
+ if(scene->shaders[shader]->attributes.find(std))
+ return true;
+
+ return false;
+}
+
+static bool mesh_need_attribute(Scene *scene, Mesh *mesh, ustring name)
+{
+ if(name == ustring())
+ return false;
+
+ foreach(uint shader, mesh->used_shaders)
+ if(scene->shaders[shader]->attributes.find(name))
+ return true;
+
+ return false;
+}
+
+static void create_mesh(Scene *scene, Mesh *mesh, BL::Mesh b_mesh, const vector<uint>& used_shaders)
+{
+ /* create vertices */
+ BL::Mesh::vertices_iterator v;
+
+ for(b_mesh.vertices.begin(v); v != b_mesh.vertices.end(); ++v)
+ mesh->verts.push_back(get_float3(v->co()));
+
+ /* create vertex normals */
+ Attribute *attr_N = mesh->attributes.add(Attribute::STD_VERTEX_NORMAL);
+ float3 *N = attr_N->data_float3();
+
+ for(b_mesh.vertices.begin(v); v != b_mesh.vertices.end(); ++v, ++N)
+ *N= get_float3(v->normal());
+
+ /* create faces */
+ BL::Mesh::faces_iterator f;
+ vector<int> nverts;
+
+ for(b_mesh.faces.begin(f); f != b_mesh.faces.end(); ++f) {
+ int4 vi = get_int4(f->vertices_raw());
+ int n= (vi[3] == 0)? 3: 4;
+ int shader = used_shaders[f->material_index()];
+ bool smooth = f->use_smooth();
+
+ mesh->add_triangle(vi[0], vi[1], vi[2], shader, smooth);
+
+ if(n == 4)
+ mesh->add_triangle(vi[0], vi[2], vi[3], shader, smooth);
+
+ nverts.push_back(n);
+ }
+
+ /* create generated coordinates. todo: we should actually get the orco
+ coordinates from modifiers, for now we use texspace loc/size which
+ is available in the api. */
+ if(mesh_need_attribute(scene, mesh, Attribute::STD_GENERATED)) {
+ Attribute *attr = mesh->attributes.add(Attribute::STD_GENERATED);
+ float3 loc = get_float3(b_mesh.texspace_location());
+ float3 size = get_float3(b_mesh.texspace_size());
+
+ if(size.x != 0.0f) size.x = 0.5f/size.x;
+ if(size.y != 0.0f) size.y = 0.5f/size.y;
+ if(size.z != 0.0f) size.z = 0.5f/size.z;
+
+ loc = loc*size - make_float3(0.5f, 0.5f, 0.5f);
+
+ float3 *fdata = attr->data_float3();
+ BL::Mesh::vertices_iterator v;
+ size_t i = 0;
+
+ for(b_mesh.vertices.begin(v); v != b_mesh.vertices.end(); ++v)
+ fdata[i++] = get_float3(v->co())*size - loc;
+ }
+
+ /* create vertex color attributes */
+ {
+ BL::Mesh::vertex_colors_iterator l;
+
+ for(b_mesh.vertex_colors.begin(l); l != b_mesh.vertex_colors.end(); ++l) {
+ if(!mesh_need_attribute(scene, mesh, ustring(l->name().c_str())))
+ continue;
+
+ Attribute *attr = mesh->attributes.add(
+ ustring(l->name().c_str()), TypeDesc::TypeColor, Attribute::CORNER);
+
+ BL::MeshColorLayer::data_iterator c;
+ float3 *fdata = attr->data_float3();
+ size_t i = 0;
+
+ for(l->data.begin(c); c != l->data.end(); ++c, ++i) {
+ fdata[0] = color_srgb_to_scene_linear(get_float3(c->color1()));
+ fdata[1] = color_srgb_to_scene_linear(get_float3(c->color2()));
+ fdata[2] = color_srgb_to_scene_linear(get_float3(c->color3()));
+
+ if(nverts[i] == 4) {
+ fdata[3] = fdata[0];
+ fdata[4] = fdata[2];
+ fdata[5] = color_srgb_to_scene_linear(get_float3(c->color4()));
+ fdata += 6;
+ }
+ else
+ fdata += 3;
+ }
+ }
+ }
+
+ /* create uv layer attributes */
+ {
+ BL::Mesh::uv_textures_iterator l;
+
+ for(b_mesh.uv_textures.begin(l); l != b_mesh.uv_textures.end(); ++l) {
+ Attribute::Standard std = (l->active_render())? Attribute::STD_UV: Attribute::STD_NONE;
+ ustring name = ustring(l->name().c_str());
+
+ if(!(mesh_need_attribute(scene, mesh, name) || mesh_need_attribute(scene, mesh, std)))
+ continue;
+
+ Attribute *attr;
+
+ if(l->active_render())
+ attr = mesh->attributes.add(std, name);
+ else
+ attr = mesh->attributes.add(name, TypeDesc::TypePoint, Attribute::CORNER);
+
+ BL::MeshTextureFaceLayer::data_iterator t;
+ float3 *fdata = attr->data_float3();
+ size_t i = 0;
+
+ for(l->data.begin(t); t != l->data.end(); ++t, ++i) {
+ fdata[0] = get_float3(t->uv1());
+ fdata[1] = get_float3(t->uv2());
+ fdata[2] = get_float3(t->uv3());
+ fdata += 3;
+
+ if(nverts[i] == 4) {
+ fdata[0] = get_float3(t->uv1());
+ fdata[1] = get_float3(t->uv3());
+ fdata[2] = get_float3(t->uv4());
+ fdata += 3;
+ }
+ }
+ }
+ }
+}
+
+static void create_subd_mesh(Mesh *mesh, BL::Mesh b_mesh, PointerRNA *cmesh, const vector<uint>& used_shaders)
+{
+ /* create subd mesh */
+ SubdMesh sdmesh;
+
+ /* create vertices */
+ BL::Mesh::vertices_iterator v;
+
+ for(b_mesh.vertices.begin(v); v != b_mesh.vertices.end(); ++v)
+ sdmesh.add_vert(get_float3(v->co()));
+
+ /* create faces */
+ BL::Mesh::faces_iterator f;
+
+ for(b_mesh.faces.begin(f); f != b_mesh.faces.end(); ++f) {
+ int4 vi = get_int4(f->vertices_raw());
+ int n= (vi[3] == 0)? 3: 4;
+ //int shader = used_shaders[f->material_index()];
+
+ if(n == 4)
+ sdmesh.add_face(vi[0], vi[1], vi[2], vi[3]);
+ /*else
+ sdmesh.add_face(vi[0], vi[1], vi[2]);*/
+ }
+
+ /* finalize subd mesh */
+ sdmesh.link_boundary();
+
+ /* subdivide */
+ DiagSplit dsplit;
+ dsplit.camera = NULL;
+ dsplit.dicing_rate = RNA_float_get(cmesh, "dicing_rate");
+
+ sdmesh.tesselate(&dsplit, false, mesh, used_shaders[0], true);
+}
+
+/* Sync */
+
+Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated)
+{
+ /* test if we can instance or if the object is modified */
+ BL::ID b_ob_data = b_ob.data();
+ BL::ID key = (object_is_modified(b_ob))? b_ob: b_ob_data;
+
+ /* find shader indices */
+ vector<uint> used_shaders;
+
+ BL::Object::material_slots_iterator slot;
+ for(b_ob.material_slots.begin(slot); slot != b_ob.material_slots.end(); ++slot) {
+ if(render_layer.material_override)
+ find_shader(render_layer.material_override, used_shaders);
+ else
+ find_shader(slot->material(), used_shaders);
+ }
+
+ if(used_shaders.size() == 0)
+ used_shaders.push_back(scene->default_surface);
+
+ /* test if we need to sync */
+ Mesh *mesh;
+
+ if(!mesh_map.sync(&mesh, key)) {
+ /* if transform was applied to mesh, need full update */
+ if(object_updated && mesh->transform_applied);
+ /* test if shaders changed, these can be object level so mesh
+ does not get tagged for recalc */
+ else if(mesh->used_shaders != used_shaders);
+ else {
+ /* even if not tagged for recalc, we may need to sync anyway
+ * because the shader needs different mesh attributes */
+ bool attribute_recalc = false;
+
+ foreach(uint shader, mesh->used_shaders)
+ if(scene->shaders[shader]->need_update_attributes)
+ attribute_recalc = true;
+
+ if(!attribute_recalc)
+ return mesh;
+ }
+ }
+
+ /* ensure we only sync instanced meshes once */
+ if(mesh_synced.find(mesh) != mesh_synced.end())
+ return mesh;
+
+ mesh_synced.insert(mesh);
+
+ /* create derived mesh */
+ BL::Mesh b_mesh = object_to_mesh(b_ob, b_scene, true, !preview);
+ PointerRNA cmesh = RNA_pointer_get(&b_ob_data.ptr, "cycles");
+
+ vector<Mesh::Triangle> oldtriangle = mesh->triangles;
+
+
+ mesh->clear();
+ mesh->used_shaders = used_shaders;
+ mesh->name = ustring(b_ob_data.name().c_str());
+
+ if(b_mesh) {
+ if(cmesh.data && RNA_boolean_get(&cmesh, "use_subdivision"))
+ create_subd_mesh(mesh, b_mesh, &cmesh, used_shaders);
+ else
+ create_mesh(scene, mesh, b_mesh, used_shaders);
+
+ /* free derived mesh */
+ object_remove_mesh(b_data, b_mesh);
+ }
+
+ /* displacement method */
+ if(cmesh.data) {
+ int method = RNA_enum_get(&cmesh, "displacement_method");
+
+ if(method == 0)
+ mesh->displacement_method = Mesh::DISPLACE_BUMP;
+ else if(method == 1)
+ mesh->displacement_method = Mesh::DISPLACE_TRUE;
+ else
+ mesh->displacement_method = Mesh::DISPLACE_BOTH;
+ }
+
+ /* tag update */
+ bool rebuild = false;
+
+ if(oldtriangle.size() != mesh->triangles.size())
+ rebuild = true;
+ else if(oldtriangle.size()) {
+ if(memcmp(&oldtriangle[0], &mesh->triangles[0], sizeof(Mesh::Triangle)*oldtriangle.size()) != 0)
+ rebuild = true;
+ }
+
+ mesh->tag_update(scene, rebuild);
+
+ return mesh;
+}
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp
new file mode 100644
index 00000000000..1095a3ee026
--- /dev/null
+++ b/intern/cycles/blender/blender_object.cpp
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2011, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include "light.h"
+#include "mesh.h"
+#include "object.h"
+#include "scene.h"
+
+#include "blender_sync.h"
+#include "blender_util.h"
+
+#include "util_foreach.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Utilities */
+
+bool BlenderSync::object_is_modified(BL::Object b_ob)
+{
+ /* test if we can instance or if the object is modified */
+ if(ccl::object_is_modified(b_ob, b_scene, preview)) {
+ /* modifiers */
+ return true;
+ }
+ else {
+ /* object level material links */
+ BL::Object::material_slots_iterator slot;
+ for(b_ob.material_slots.begin(slot); slot != b_ob.material_slots.end(); ++slot)
+ if(slot->link() == BL::MaterialSlot::link_OBJECT)
+ return true;
+ }
+
+ return false;
+}
+
+bool BlenderSync::object_is_mesh(BL::Object b_ob)
+{
+ BL::ID b_ob_data = b_ob.data();
+
+ return (b_ob_data && (b_ob_data.is_a(&RNA_Mesh) ||
+ b_ob_data.is_a(&RNA_Curve) || b_ob_data.is_a(&RNA_MetaBall)));
+}
+
+bool BlenderSync::object_is_light(BL::Object b_ob)
+{
+ BL::ID b_ob_data = b_ob.data();
+
+ return (b_ob_data && b_ob_data.is_a(&RNA_Lamp));
+}
+
+static uint object_ray_visibility(BL::Object b_ob)
+{
+ PointerRNA cvisibility = RNA_pointer_get(&b_ob.ptr, "cycles_visibility");
+ uint flag = 0;
+
+ flag |= get_boolean(cvisibility, "camera")? PATH_RAY_CAMERA: 0;
+ flag |= get_boolean(cvisibility, "diffuse")? PATH_RAY_DIFFUSE: 0;
+ flag |= get_boolean(cvisibility, "glossy")? PATH_RAY_GLOSSY: 0;
+ flag |= get_boolean(cvisibility, "transmission")? PATH_RAY_TRANSMIT: 0;
+ flag |= get_boolean(cvisibility, "shadow")? PATH_RAY_SHADOW: 0;
+
+ return flag;
+}
+
+/* Light */
+
+void BlenderSync::sync_light(BL::Object b_parent, int b_index, BL::Object b_ob, Transform& tfm)
+{
+ /* test if we need to sync */
+ Light *light;
+ ObjectKey key(b_parent, b_index, b_ob);
+
+ if(!light_map.sync(&light, b_ob, b_parent, key))
+ return;
+
+ BL::Lamp b_lamp(b_ob.data());
+
+ /* type */
+ switch(b_lamp.type()) {
+ case BL::Lamp::type_POINT: {
+ BL::PointLamp b_point_lamp(b_lamp);
+ light->size = b_point_lamp.shadow_soft_size();
+ light->type = LIGHT_POINT;
+ break;
+ }
+ case BL::Lamp::type_SPOT: {
+ BL::SpotLamp b_spot_lamp(b_lamp);
+ light->size = b_spot_lamp.shadow_soft_size();
+ light->type = LIGHT_POINT;
+ break;
+ }
+ case BL::Lamp::type_HEMI: {
+ light->type = LIGHT_DISTANT;
+ light->size = 0.0f;
+ break;
+ }
+ case BL::Lamp::type_SUN: {
+ BL::SunLamp b_sun_lamp(b_lamp);
+ light->size = b_sun_lamp.shadow_soft_size();
+ light->type = LIGHT_DISTANT;
+ break;
+ }
+ case BL::Lamp::type_AREA: {
+ BL::AreaLamp b_area_lamp(b_lamp);
+ light->size = 1.0f;
+ light->axisu = make_float3(tfm.x.x, tfm.y.x, tfm.z.x);
+ light->axisv = make_float3(tfm.x.y, tfm.y.y, tfm.z.y);
+ light->sizeu = b_area_lamp.size();
+ if(b_area_lamp.shape() == BL::AreaLamp::shape_RECTANGLE)
+ light->sizev = b_area_lamp.size_y();
+ else
+ light->sizev = light->sizeu;
+ light->type = LIGHT_AREA;
+ break;
+ }
+ }
+
+ /* location and (inverted!) direction */
+ light->co = make_float3(tfm.x.w, tfm.y.w, tfm.z.w);
+ light->dir = -make_float3(tfm.x.z, tfm.y.z, tfm.z.z);
+
+ /* shader */
+ vector<uint> used_shaders;
+
+ find_shader(b_lamp, used_shaders);
+
+ if(used_shaders.size() == 0)
+ used_shaders.push_back(scene->default_light);
+
+ light->shader = used_shaders[0];
+
+ /* shadow */
+ PointerRNA clamp = RNA_pointer_get(&b_lamp.ptr, "cycles");
+ light->cast_shadow = get_boolean(clamp, "cast_shadow");
+
+ /* tag */
+ light->tag_update(scene);
+}
+
+/* Object */
+
+void BlenderSync::sync_object(BL::Object b_parent, int b_index, BL::Object b_ob, Transform& tfm, uint visibility)
+{
+ /* light is handled separately */
+ if(object_is_light(b_ob)) {
+ sync_light(b_parent, b_index, b_ob, tfm);
+ return;
+ }
+
+ /* only interested in object that we can create meshes from */
+ if(!object_is_mesh(b_ob))
+ return;
+
+ /* test if we need to sync */
+ ObjectKey key(b_parent, b_index, b_ob);
+ Object *object;
+ bool object_updated = false;
+
+ /* object sync */
+ if(object_map.sync(&object, b_ob, b_parent, key)) {
+ object->name = b_ob.name().c_str();
+ object->tfm = tfm;
+
+ object->visibility = object_ray_visibility(b_ob) & visibility;
+ if(b_parent.ptr.data != b_ob.ptr.data)
+ object->visibility &= object_ray_visibility(b_parent);
+
+ object->tag_update(scene);
+ object_updated = true;
+ }
+
+ /* mesh sync */
+ object->mesh = sync_mesh(b_ob, object_updated);
+}
+
+/* Object Loop */
+
+void BlenderSync::sync_objects(BL::SpaceView3D b_v3d)
+{
+ /* layer data */
+ uint scene_layer = render_layer.scene_layer;
+ uint layer = render_layer.layer;
+
+ /* prepare for sync */
+ light_map.pre_sync();
+ mesh_map.pre_sync();
+ object_map.pre_sync();
+ mesh_synced.clear();
+
+ /* object loop */
+ BL::Scene::objects_iterator b_ob;
+
+ for(b_scene.objects.begin(b_ob); b_ob != b_scene.objects.end(); ++b_ob) {
+ bool hide = (b_v3d)? b_ob->hide(): b_ob->hide_render();
+ uint ob_layer = get_layer(b_ob->layers());
+
+ if(!hide && (ob_layer & scene_layer)) {
+ uint visibility = PATH_RAY_ALL;
+
+ if(!(ob_layer & layer))
+ visibility &= ~PATH_RAY_CAMERA;
+
+ if(b_ob->is_duplicator()) {
+ /* dupli objects */
+ object_create_duplilist(*b_ob, b_scene);
+
+ BL::Object::dupli_list_iterator b_dup;
+ int b_index = 0;
+
+ for(b_ob->dupli_list.begin(b_dup); b_dup != b_ob->dupli_list.end(); ++b_dup) {
+ Transform tfm = get_transform(b_dup->matrix());
+ sync_object(*b_ob, b_index, b_dup->object(), tfm, visibility);
+ b_index++;
+ }
+
+ object_free_duplilist(*b_ob);
+ }
+ else {
+ /* object itself */
+ Transform tfm = get_transform(b_ob->matrix_world());
+ sync_object(*b_ob, 0, *b_ob, tfm, visibility);
+ }
+ }
+ }
+
+ /* handle removed data and modified pointers */
+ if(light_map.post_sync())
+ scene->light_manager->tag_update(scene);
+ if(mesh_map.post_sync())
+ scene->mesh_manager->tag_update(scene);
+ if(object_map.post_sync())
+ scene->object_manager->tag_update(scene);
+ mesh_synced.clear();
+}
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp
new file mode 100644
index 00000000000..58049bb8cbe
--- /dev/null
+++ b/intern/cycles/blender/blender_python.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2011, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include <Python.h>
+
+#include "blender_sync.h"
+#include "blender_session.h"
+
+#include "util_opengl.h"
+#include "util_path.h"
+
+CCL_NAMESPACE_BEGIN
+
+static PyObject *init_func(PyObject *self, PyObject *args)
+{
+ const char *path, *user_path;
+
+ if(!PyArg_ParseTuple(args, "ss", &path, &user_path))
+ return NULL;
+
+ path_init(path, user_path);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *create_func(PyObject *self, PyObject *args)
+{
+ PyObject *pyengine, *pydata, *pyscene, *pyregion, *pyv3d, *pyrv3d;
+
+ if(!PyArg_ParseTuple(args, "OOOOOO", &pyengine, &pydata, &pyscene, &pyregion, &pyv3d, &pyrv3d))
+ return NULL;
+
+ /* RNA */
+ PointerRNA engineptr;
+ RNA_pointer_create(NULL, &RNA_RenderEngine, (void*)PyLong_AsVoidPtr(pyengine), &engineptr);
+ BL::RenderEngine engine(engineptr);
+
+ PointerRNA dataptr;
+ RNA_id_pointer_create((ID*)PyLong_AsVoidPtr(pydata), &dataptr);
+ BL::BlendData data(dataptr);
+
+ PointerRNA sceneptr;
+ RNA_id_pointer_create((ID*)PyLong_AsVoidPtr(pyscene), &sceneptr);
+ BL::Scene scene(sceneptr);
+
+ PointerRNA regionptr;
+ RNA_id_pointer_create((ID*)PyLong_AsVoidPtr(pyregion), &regionptr);
+ BL::Region region(regionptr);
+
+ PointerRNA v3dptr;
+ RNA_id_pointer_create((ID*)PyLong_AsVoidPtr(pyv3d), &v3dptr);
+ BL::SpaceView3D v3d(v3dptr);
+
+ PointerRNA rv3dptr;
+ RNA_id_pointer_create((ID*)PyLong_AsVoidPtr(pyrv3d), &rv3dptr);
+ BL::RegionView3D rv3d(rv3dptr);
+
+ /* create session */
+ BlenderSession *session;
+
+ if(rv3d) {
+ /* interactive session */
+ int width = region.width();
+ int height = region.height();
+
+ session = new BlenderSession(engine, data, scene, v3d, rv3d, width, height);
+ }
+ else {
+ /* offline session */
+ session = new BlenderSession(engine, data, scene);
+ }
+
+ return PyLong_FromVoidPtr(session);
+}
+
+static PyObject *free_func(PyObject *self, PyObject *args)
+{
+ PyObject *pysession;
+
+ if(!PyArg_ParseTuple(args, "O", &pysession))
+ return NULL;
+
+ delete (BlenderSession*)PyLong_AsVoidPtr(pysession);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *render_func(PyObject *self, PyObject *args)
+{
+ PyObject *pysession;
+
+ if(!PyArg_ParseTuple(args, "O", &pysession))
+ return NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+
+ BlenderSession *session = (BlenderSession*)PyLong_AsVoidPtr(pysession);
+ session->render();
+
+ Py_END_ALLOW_THREADS
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *draw_func(PyObject *self, PyObject *args)
+{
+ PyObject *pysession, *pyv3d, *pyrv3d;
+
+ if(!PyArg_ParseTuple(args, "OOO", &pysession, &pyv3d, &pyrv3d))
+ return NULL;
+
+ BlenderSession *session = (BlenderSession*)PyLong_AsVoidPtr(pysession);
+
+ if(PyLong_AsVoidPtr(pyrv3d)) {
+ /* 3d view drawing */
+ int viewport[4];
+ glGetIntegerv(GL_VIEWPORT, viewport);
+
+ session->draw(viewport[2], viewport[3]);
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *sync_func(PyObject *self, PyObject *args)
+{
+ PyObject *pysession;
+
+ if(!PyArg_ParseTuple(args, "O", &pysession))
+ return NULL;
+
+ BlenderSession *session = (BlenderSession*)PyLong_AsVoidPtr(pysession);
+ session->synchronize();
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *available_devices_func(PyObject *self, PyObject *args)
+{
+ vector<DeviceType> types = Device::available_types();
+
+ PyObject *ret = PyTuple_New(types.size());
+
+ for(size_t i = 0; i < types.size(); i++) {
+ string name = Device::string_from_type(types[i]);
+ PyTuple_SetItem(ret, i, PyUnicode_FromString(name.c_str()));
+ }
+
+ return ret;
+}
+
+static PyObject *with_osl_func(PyObject *self, PyObject *args)
+{
+#ifdef WITH_OSL
+ PyObject *ret = Py_True;
+#else
+ PyObject *ret = Py_False;
+#endif
+
+ return Py_INCREF(ret), ret;
+}
+
+static PyMethodDef methods[] = {
+ {"init", init_func, METH_VARARGS, ""},
+ {"create", create_func, METH_VARARGS, ""},
+ {"free", free_func, METH_VARARGS, ""},
+ {"render", render_func, METH_VARARGS, ""},
+ {"draw", draw_func, METH_VARARGS, ""},
+ {"sync", sync_func, METH_VARARGS, ""},
+ {"available_devices", available_devices_func, METH_NOARGS, ""},
+ {"with_osl", with_osl_func, METH_NOARGS, ""},
+ {NULL, NULL, 0, NULL},
+};
+
+static struct PyModuleDef module = {
+ PyModuleDef_HEAD_INIT,
+ "bcycles",
+ "Blender cycles render integration",
+ -1,
+ methods,
+ NULL, NULL, NULL, NULL
+};
+
+CCL_NAMESPACE_END
+
+extern "C" PyObject *CYCLES_initPython();
+
+PyObject *CYCLES_initPython()
+{
+ return PyModule_Create(&ccl::module);
+}
+
diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp
new file mode 100644
index 00000000000..4433b1e24f9
--- /dev/null
+++ b/intern/cycles/blender/blender_session.cpp
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2011, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include "background.h"
+#include "buffers.h"
+#include "camera.h"
+#include "device.h"
+#include "integrator.h"
+#include "film.h"
+#include "light.h"
+#include "scene.h"
+#include "session.h"
+#include "shader.h"
+
+#include "util_color.h"
+#include "util_foreach.h"
+#include "util_function.h"
+#include "util_progress.h"
+#include "util_time.h"
+
+#include "blender_sync.h"
+#include "blender_session.h"
+#include "blender_util.h"
+
+CCL_NAMESPACE_BEGIN
+
+BlenderSession::BlenderSession(BL::RenderEngine b_engine_, BL::BlendData b_data_, BL::Scene b_scene_)
+: b_engine(b_engine_), b_data(b_data_), b_scene(b_scene_), b_v3d(PointerRNA_NULL), b_rv3d(PointerRNA_NULL)
+{
+ /* offline render */
+ BL::RenderSettings r = b_scene.render();
+
+ width = (int)(r.resolution_x()*r.resolution_percentage()*0.01f);
+ height = (int)(r.resolution_y()*r.resolution_percentage()*0.01f);
+ background = true;
+ last_redraw_time = 0.0f;
+
+ create_session();
+}
+
+BlenderSession::BlenderSession(BL::RenderEngine b_engine_, BL::BlendData b_data_, BL::Scene b_scene_,
+ BL::SpaceView3D b_v3d_, BL::RegionView3D b_rv3d_, int width_, int height_)
+: b_engine(b_engine_), b_data(b_data_), b_scene(b_scene_), b_v3d(b_v3d_), b_rv3d(b_rv3d_)
+{
+ /* 3d view render */
+ width = width_;
+ height = height_;
+ background = false;
+ last_redraw_time = 0.0f;
+
+ create_session();
+}
+
+BlenderSession::~BlenderSession()
+{
+ free_session();
+}
+
+void BlenderSession::create_session()
+{
+ SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background);
+ SessionParams session_params = BlenderSync::get_session_params(b_scene, background);
+
+ /* reset status/progress */
+ last_status= "";
+ last_progress= -1.0f;
+
+ /* create scene */
+ scene = new Scene(scene_params);
+
+ /* create sync */
+ sync = new BlenderSync(b_data, b_scene, scene, !background);
+ sync->sync_data(b_v3d);
+
+ if(b_rv3d)
+ sync->sync_view(b_v3d, b_rv3d, width, height);
+ else
+ sync->sync_camera(width, height);
+
+ /* create session */
+ session = new Session(session_params);
+ session->scene = scene;
+ session->progress.set_update_callback(function_bind(&BlenderSession::tag_redraw, this));
+ session->progress.set_cancel_callback(function_bind(&BlenderSession::test_cancel, this));
+ session->set_pause(BlenderSync::get_session_pause(b_scene, background));
+
+ /* start rendering */
+ session->reset(width, height, session_params.samples);
+ session->start();
+}
+
+void BlenderSession::free_session()
+{
+ delete sync;
+ delete session;
+}
+
+void BlenderSession::render()
+{
+ session->wait();
+
+ if(session->progress.get_cancel())
+ return;
+
+ /* write result */
+ write_render_result();
+}
+
+void BlenderSession::write_render_result()
+{
+ /* get result */
+ RenderBuffers *buffers = session->buffers;
+ float exposure = scene->film->exposure;
+ double total_time, sample_time;
+ int sample;
+ session->progress.get_sample(sample, total_time, sample_time);
+
+ float4 *pixels = buffers->copy_from_device(exposure, sample);
+
+ if(!pixels)
+ return;
+
+ struct RenderResult *rrp = RE_engine_begin_result((RenderEngine*)b_engine.ptr.data, 0, 0, width, height);
+ PointerRNA rrptr;
+ RNA_pointer_create(NULL, &RNA_RenderResult, rrp, &rrptr);
+ BL::RenderResult rr(rrptr);
+
+ BL::RenderResult::layers_iterator layer;
+ rr.layers.begin(layer);
+ rna_RenderLayer_rect_set(&layer->ptr, (float*)pixels);
+
+ RE_engine_end_result((RenderEngine*)b_engine.ptr.data, rrp);
+
+ delete [] pixels;
+}
+
+void BlenderSession::synchronize()
+{
+ /* on session/scene parameter changes, we recreate session entirely */
+ SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background);
+ SessionParams session_params = BlenderSync::get_session_params(b_scene, background);
+
+ if(session->params.modified(session_params) ||
+ scene->params.modified(scene_params)) {
+ free_session();
+ create_session();
+ return;
+ }
+
+ /* increase samples, but never decrease */
+ session->set_samples(session_params.samples);
+ session->set_pause(BlenderSync::get_session_pause(b_scene, background));
+
+ /* copy recalc flags, outside of mutex so we can decide to do the real
+ synchronization at a later time to not block on running updates */
+ sync->sync_recalc();
+
+ /* try to acquire mutex. if we don't want to or can't, come back later */
+ if(!session->ready_to_reset() || !session->scene->mutex.try_lock()) {
+ tag_update();
+ return;
+ }
+
+ /* data and camera synchronize */
+ sync->sync_data(b_v3d);
+
+ if(b_rv3d)
+ sync->sync_view(b_v3d, b_rv3d, width, height);
+ else
+ sync->sync_camera(width, height);
+
+ /* unlock */
+ session->scene->mutex.unlock();
+
+ /* reset if needed */
+ if(scene->need_reset())
+ session->reset(width, height, session_params.samples);
+}
+
+bool BlenderSession::draw(int w, int h)
+{
+ /* before drawing, we verify camera and viewport size changes, because
+ we do not get update callbacks for those, we must detect them here */
+ if(session->ready_to_reset()) {
+ bool reset = false;
+
+ /* try to acquire mutex. if we can't, come back later */
+ if(!session->scene->mutex.try_lock()) {
+ tag_update();
+ }
+ else {
+ /* update camera from 3d view */
+ bool need_update = scene->camera->need_update;
+
+ sync->sync_view(b_v3d, b_rv3d, w, h);
+
+ if(scene->camera->need_update && !need_update)
+ reset = true;
+
+ session->scene->mutex.unlock();
+ }
+
+ /* if dimensions changed, reset */
+ if(width != w || height != h) {
+ width = w;
+ height = h;
+ reset = true;
+ }
+
+ /* reset if requested */
+ if(reset) {
+ SessionParams session_params = BlenderSync::get_session_params(b_scene, background);
+ session->reset(width, height, session_params.samples);
+ }
+ }
+
+ /* update status and progress for 3d view draw */
+ update_status_progress();
+
+ /* draw */
+ return !session->draw(width, height);
+}
+
+void BlenderSession::get_status(string& status, string& substatus)
+{
+ session->progress.get_status(status, substatus);
+}
+
+void BlenderSession::get_progress(float& progress, double& total_time)
+{
+ double sample_time;
+ int sample;
+
+ session->progress.get_sample(sample, total_time, sample_time);
+ progress = ((float)sample/(float)session->params.samples);
+}
+
+void BlenderSession::update_status_progress()
+{
+ string status, substatus;
+ float progress;
+ double total_time;
+ char time_str[128];
+
+ get_status(status, substatus);
+ get_progress(progress, total_time);
+
+ if(!background) {
+ BLI_timestr(total_time, time_str);
+ status = "Time: " + string(time_str) + " | " + status;
+ }
+
+ if(substatus.size() > 0)
+ status += " | " + substatus;
+
+ if(status != last_status) {
+ RE_engine_update_stats((RenderEngine*)b_engine.ptr.data, "", status.c_str());
+ last_status = status;
+ }
+ if(progress != last_progress) {
+ RE_engine_update_progress((RenderEngine*)b_engine.ptr.data, progress);
+ last_progress = progress;
+ }
+}
+
+void BlenderSession::tag_update()
+{
+ /* tell blender that we want to get another update callback */
+ engine_tag_update((RenderEngine*)b_engine.ptr.data);
+}
+
+void BlenderSession::tag_redraw()
+{
+ if(background) {
+ /* update stats and progress, only for background here because
+ in 3d view we do it in draw for thread safety reasons */
+ update_status_progress();
+
+ /* offline render, redraw if timeout passed */
+ if(time_dt() - last_redraw_time > 1.0f) {
+ write_render_result();
+ engine_tag_redraw((RenderEngine*)b_engine.ptr.data);
+ last_redraw_time = time_dt();
+ }
+ }
+ else {
+ /* tell blender that we want to redraw */
+ engine_tag_redraw((RenderEngine*)b_engine.ptr.data);
+ }
+}
+
+void BlenderSession::test_cancel()
+{
+ /* test if we need to cancel rendering */
+ if(background)
+ if(RE_engine_test_break((RenderEngine*)b_engine.ptr.data))
+ session->progress.set_cancel("Cancelled");
+}
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h
new file mode 100644
index 00000000000..e30b60c3d63
--- /dev/null
+++ b/intern/cycles/blender/blender_session.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2011, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef __BLENDER_SESSION_H__
+#define __BLENDER_SESSION_H__
+
+#include "device.h"
+#include "scene.h"
+#include "session.h"
+
+#include "util_vector.h"
+
+CCL_NAMESPACE_BEGIN
+
+class Scene;
+class Session;
+
+class BlenderSession {
+public:
+ BlenderSession(BL::RenderEngine b_engine, BL::BlendData b_data, BL::Scene b_scene);
+ BlenderSession(BL::RenderEngine b_engine, BL::BlendData b_data, BL::Scene b_scene,
+ BL::SpaceView3D b_v3d, BL::RegionView3D b_rv3d, int width, int height);
+
+ ~BlenderSession();
+
+ /* session */
+ void create_session();
+ void free_session();
+
+ /* offline render */
+ void render();
+ void write_render_result();
+
+ /* interactive updates */
+ void synchronize();
+
+ /* drawing */
+ bool draw(int w, int h);
+ void tag_redraw();
+ void tag_update();
+ void get_status(string& status, string& substatus);
+ void get_progress(float& progress, double& total_time);
+ void test_cancel();
+ void update_status_progress();
+
+ bool background;
+ Session *session;
+ Scene *scene;
+ BlenderSync *sync;
+ double last_redraw_time;
+
+ BL::RenderEngine b_engine;
+ BL::BlendData b_data;
+ BL::Scene b_scene;
+ BL::SpaceView3D b_v3d;
+ BL::RegionView3D b_rv3d;
+
+ string last_status;
+ float last_progress;
+
+ int width, height;
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __BLENDER_SESSION_H__ */
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp
new file mode 100644
index 00000000000..2e558cf3e97
--- /dev/null
+++ b/intern/cycles/blender/blender_shader.cpp
@@ -0,0 +1,700 @@
+/*
+ * Copyright 2011, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include "background.h"
+#include "graph.h"
+#include "light.h"
+#include "nodes.h"
+#include "scene.h"
+#include "shader.h"
+
+#include "blender_sync.h"
+#include "blender_util.h"
+
+#include "util_debug.h"
+
+CCL_NAMESPACE_BEGIN
+
+typedef map<void*, ShaderNode*> PtrNodeMap;
+typedef pair<ShaderNode*, std::string> SocketPair;
+typedef map<void*, SocketPair> PtrSockMap;
+
+/* Find */
+
+void BlenderSync::find_shader(BL::ID id, vector<uint>& used_shaders)
+{
+ Shader *shader = shader_map.find(id);
+
+ for(size_t i = 0; i < scene->shaders.size(); i++) {
+ if(scene->shaders[i] == shader) {
+ used_shaders.push_back(i);
+ break;
+ }
+ }
+}
+
+/* Graph */
+
+static BL::NodeSocket get_node_input(BL::Node *b_group_node, BL::NodeSocket b_in)
+{
+ if(b_group_node) {
+
+ BL::NodeTree b_ntree = BL::NodeGroup(*b_group_node).node_tree();
+ BL::NodeTree::links_iterator b_link;
+
+ for(b_ntree.links.begin(b_link); b_link != b_ntree.links.end(); ++b_link) {
+ if(b_link->to_socket().ptr.data == b_in.ptr.data) {
+ BL::Node::inputs_iterator b_gin;
+
+ for(b_group_node->inputs.begin(b_gin); b_gin != b_group_node->inputs.end(); ++b_gin)
+ if(b_gin->group_socket().ptr.data == b_link->from_socket().ptr.data)
+ return *b_gin;
+
+ }
+ }
+ }
+
+ return b_in;
+}
+
+static BL::NodeSocket get_node_output(BL::Node b_node, const string& name)
+{
+ BL::Node::outputs_iterator b_out;
+
+ for(b_node.outputs.begin(b_out); b_out != b_node.outputs.end(); ++b_out)
+ if(b_out->name() == name)
+ return *b_out;
+
+ assert(0);
+
+ return *b_out;
+}
+
+static float3 get_node_output_rgba(BL::Node b_node, const string& name)
+{
+ BL::NodeSocketRGBA sock(get_node_output(b_node, name));
+ return get_float3(sock.default_value());
+}
+
+static float get_node_output_value(BL::Node b_node, const string& name)
+{
+ BL::NodeSocketFloatNone sock(get_node_output(b_node, name));
+ return sock.default_value();
+}
+
+static void get_tex_mapping(TextureMapping *mapping, BL::TexMapping b_mapping)
+{
+ mapping->translation = get_float3(b_mapping.location());
+ mapping->rotation = get_float3(b_mapping.rotation())*(M_PI/180.0f); /* in degrees! */
+ mapping->scale = get_float3(b_mapping.scale());
+
+ mapping->x_mapping = (TextureMapping::Mapping)b_mapping.mapping_x();
+ mapping->y_mapping = (TextureMapping::Mapping)b_mapping.mapping_y();
+ mapping->z_mapping = (TextureMapping::Mapping)b_mapping.mapping_z();
+}
+
+static ShaderNode *add_node(BL::BlendData b_data, ShaderGraph *graph, BL::Node *b_group_node, BL::ShaderNode b_node)
+{
+ ShaderNode *node = NULL;
+
+ switch(b_node.type()) {
+ /* not supported */
+ case BL::ShaderNode::type_CAMERA: break;
+ case BL::ShaderNode::type_COMBRGB: break;
+ case BL::ShaderNode::type_CURVE_RGB: break;
+ case BL::ShaderNode::type_CURVE_VEC: break;
+ case BL::ShaderNode::type_GEOM: break;
+ case BL::ShaderNode::type_HUE_SAT: break;
+ case BL::ShaderNode::type_INVERT: break;
+ case BL::ShaderNode::type_MATERIAL: break;
+ case BL::ShaderNode::type_MATERIAL_EXT: break;
+ case BL::ShaderNode::type_NORMAL: break;
+ case BL::ShaderNode::type_OUTPUT: break;
+ case BL::ShaderNode::type_SCRIPT: break;
+ case BL::ShaderNode::type_SEPRGB: break;
+ case BL::ShaderNode::type_SQUEEZE: break;
+ case BL::ShaderNode::type_TEXTURE: break;
+ case BL::ShaderNode::type_VALTORGB: break;
+ /* handled outside this function */
+ case BL::ShaderNode::type_GROUP: break;
+ /* existing blender nodes */
+ case BL::ShaderNode::type_RGB: {
+ ColorNode *color = new ColorNode();
+ color->value = get_node_output_rgba(b_node, "Color");
+ node = color;
+ break;
+ }
+ case BL::ShaderNode::type_VALUE: {
+ ValueNode *value = new ValueNode();
+ value->value = get_node_output_value(b_node, "Value");
+ node = value;
+ break;
+ }
+ case BL::ShaderNode::type_MIX_RGB: {
+ BL::ShaderNodeMixRGB b_mix_node(b_node);
+ MixNode *mix = new MixNode();
+ mix->type = MixNode::type_enum[b_mix_node.blend_type()];
+ node = mix;
+ break;
+ }
+ case BL::ShaderNode::type_RGBTOBW: {
+ node = new ConvertNode(SHADER_SOCKET_COLOR, SHADER_SOCKET_FLOAT);
+ break;
+ }
+ case BL::ShaderNode::type_MATH: {
+ BL::ShaderNodeMath b_math_node(b_node);
+ MathNode *math = new MathNode();
+ math->type = MathNode::type_enum[b_math_node.operation()];
+ node = math;
+ break;
+ }
+ case BL::ShaderNode::type_VECT_MATH: {
+ BL::ShaderNodeVectorMath b_vector_math_node(b_node);
+ VectorMathNode *vmath = new VectorMathNode();
+ vmath->type = VectorMathNode::type_enum[b_vector_math_node.operation()];
+ node = vmath;
+ break;
+ }
+ case BL::ShaderNode::type_MAPPING: {
+ BL::ShaderNodeMapping b_mapping_node(b_node);
+ MappingNode *mapping = new MappingNode();
+
+ get_tex_mapping(&mapping->tex_mapping, b_mapping_node.mapping());
+
+ node = mapping;
+ break;
+ }
+
+ /* new nodes */
+ case BL::ShaderNode::type_OUTPUT_MATERIAL:
+ case BL::ShaderNode::type_OUTPUT_WORLD:
+ case BL::ShaderNode::type_OUTPUT_LAMP: {
+ node = graph->output();
+ break;
+ }
+ case BL::ShaderNode::type_FRESNEL: {
+ node = new FresnelNode();
+ break;
+ }
+ case BL::ShaderNode::type_LAYER_WEIGHT: {
+ node = new LayerWeightNode();
+ break;
+ }
+ case BL::ShaderNode::type_ADD_SHADER: {
+ node = new AddClosureNode();
+ break;
+ }
+ case BL::ShaderNode::type_MIX_SHADER: {
+ node = new MixClosureNode();
+ break;
+ }
+ case BL::ShaderNode::type_ATTRIBUTE: {
+ BL::ShaderNodeAttribute b_attr_node(b_node);
+ AttributeNode *attr = new AttributeNode();
+ attr->attribute = b_attr_node.attribute_name();
+ node = attr;
+ break;
+ }
+ case BL::ShaderNode::type_BACKGROUND: {
+ node = new BackgroundNode();
+ break;
+ }
+ case BL::ShaderNode::type_HOLDOUT: {
+ node = new HoldoutNode();
+ break;
+ }
+ case BL::ShaderNode::type_BSDF_ANISOTROPIC: {
+ node = new WardBsdfNode();
+ break;
+ }
+ case BL::ShaderNode::type_BSDF_DIFFUSE: {
+ node = new DiffuseBsdfNode();
+ break;
+ }
+ case BL::ShaderNode::type_BSDF_GLOSSY: {
+ BL::ShaderNodeBsdfGlossy b_glossy_node(b_node);
+ GlossyBsdfNode *glossy = new GlossyBsdfNode();
+
+ switch(b_glossy_node.distribution()) {
+ case BL::ShaderNodeBsdfGlossy::distribution_SHARP:
+ glossy->distribution = ustring("Sharp");
+ break;
+ case BL::ShaderNodeBsdfGlossy::distribution_BECKMANN:
+ glossy->distribution = ustring("Beckmann");
+ break;
+ case BL::ShaderNodeBsdfGlossy::distribution_GGX:
+ glossy->distribution = ustring("GGX");
+ break;
+ }
+ node = glossy;
+ break;
+ }
+ case BL::ShaderNode::type_BSDF_GLASS: {
+ BL::ShaderNodeBsdfGlass b_glass_node(b_node);
+ GlassBsdfNode *glass = new GlassBsdfNode();
+ switch(b_glass_node.distribution()) {
+ case BL::ShaderNodeBsdfGlass::distribution_SHARP:
+ glass->distribution = ustring("Sharp");
+ break;
+ case BL::ShaderNodeBsdfGlass::distribution_BECKMANN:
+ glass->distribution = ustring("Beckmann");
+ break;
+ case BL::ShaderNodeBsdfGlass::distribution_GGX:
+ glass->distribution = ustring("GGX");
+ break;
+ }
+ node = glass;
+ break;
+ }
+ case BL::ShaderNode::type_BSDF_TRANSLUCENT: {
+ node = new TranslucentBsdfNode();
+ break;
+ }
+ case BL::ShaderNode::type_BSDF_TRANSPARENT: {
+ node = new TransparentBsdfNode();
+ break;
+ }
+ case BL::ShaderNode::type_BSDF_VELVET: {
+ node = new VelvetBsdfNode();
+ break;
+ }
+ case BL::ShaderNode::type_EMISSION: {
+ node = new EmissionNode();
+ break;
+ }
+ case BL::ShaderNode::type_VOLUME_ISOTROPIC: {
+ node = new IsotropicVolumeNode();
+ break;
+ }
+ case BL::ShaderNode::type_VOLUME_TRANSPARENT: {
+ node = new TransparentVolumeNode();
+ break;
+ }
+ case BL::ShaderNode::type_GEOMETRY: {
+ node = new GeometryNode();
+ break;
+ }
+ case BL::ShaderNode::type_LIGHT_PATH: {
+ node = new LightPathNode();
+ break;
+ }
+ case BL::ShaderNode::type_TEX_IMAGE: {
+ BL::ShaderNodeTexImage b_image_node(b_node);
+ BL::Image b_image(b_image_node.image());
+ ImageTextureNode *image = new ImageTextureNode();
+ /* todo: handle generated/builtin images */
+ if(b_image)
+ image->filename = blender_absolute_path(b_data, b_image, b_image.filepath());
+ image->color_space = ImageTextureNode::color_space_enum[(int)b_image_node.color_space()];
+ get_tex_mapping(&image->tex_mapping, b_image_node.texture_mapping());
+ node = image;
+ break;
+ }
+ case BL::ShaderNode::type_TEX_ENVIRONMENT: {
+ BL::ShaderNodeTexEnvironment b_env_node(b_node);
+ BL::Image b_image(b_env_node.image());
+ EnvironmentTextureNode *env = new EnvironmentTextureNode();
+ if(b_image)
+ env->filename = blender_absolute_path(b_data, b_image, b_image.filepath());
+ env->color_space = EnvironmentTextureNode::color_space_enum[(int)b_env_node.color_space()];
+ get_tex_mapping(&env->tex_mapping, b_env_node.texture_mapping());
+ node = env;
+ break;
+ }
+ case BL::ShaderNode::type_TEX_GRADIENT: {
+ BL::ShaderNodeTexGradient b_gradient_node(b_node);
+ GradientTextureNode *gradient = new GradientTextureNode();
+ gradient->type = GradientTextureNode::type_enum[(int)b_gradient_node.gradient_type()];
+ get_tex_mapping(&gradient->tex_mapping, b_gradient_node.texture_mapping());
+ node = gradient;
+ break;
+ }
+ case BL::ShaderNode::type_TEX_VORONOI: {
+ BL::ShaderNodeTexVoronoi b_voronoi_node(b_node);
+ VoronoiTextureNode *voronoi = new VoronoiTextureNode();
+ voronoi->coloring = VoronoiTextureNode::coloring_enum[(int)b_voronoi_node.coloring()];
+ get_tex_mapping(&voronoi->tex_mapping, b_voronoi_node.texture_mapping());
+ node = voronoi;
+ break;
+ }
+ case BL::ShaderNode::type_TEX_MAGIC: {
+ BL::ShaderNodeTexMagic b_magic_node(b_node);
+ MagicTextureNode *magic = new MagicTextureNode();
+ magic->depth = b_magic_node.turbulence_depth();
+ get_tex_mapping(&magic->tex_mapping, b_magic_node.texture_mapping());
+ node = magic;
+ break;
+ }
+ case BL::ShaderNode::type_TEX_WAVE: {
+ BL::ShaderNodeTexWave b_wave_node(b_node);
+ WaveTextureNode *wave = new WaveTextureNode();
+ wave->type = WaveTextureNode::type_enum[(int)b_wave_node.wave_type()];
+ get_tex_mapping(&wave->tex_mapping, b_wave_node.texture_mapping());
+ node = wave;
+ break;
+ }
+ case BL::ShaderNode::type_TEX_NOISE: {
+ BL::ShaderNodeTexNoise b_noise_node(b_node);
+ NoiseTextureNode *noise = new NoiseTextureNode();
+ get_tex_mapping(&noise->tex_mapping, b_noise_node.texture_mapping());
+ node = noise;
+ break;
+ }
+ case BL::ShaderNode::type_TEX_MUSGRAVE: {
+ BL::ShaderNodeTexMusgrave b_musgrave_node(b_node);
+ MusgraveTextureNode *musgrave = new MusgraveTextureNode();
+ musgrave->type = MusgraveTextureNode::type_enum[(int)b_musgrave_node.musgrave_type()];
+ get_tex_mapping(&musgrave->tex_mapping, b_musgrave_node.texture_mapping());
+ node = musgrave;
+ break;
+ }
+ case BL::ShaderNode::type_TEX_COORD: {
+ node = new TextureCoordinateNode();;
+ break;
+ }
+ case BL::ShaderNode::type_TEX_SKY: {
+ BL::ShaderNodeTexSky b_sky_node(b_node);
+ SkyTextureNode *sky = new SkyTextureNode();
+ sky->sun_direction = get_float3(b_sky_node.sun_direction());
+ sky->turbidity = b_sky_node.turbidity();
+ get_tex_mapping(&sky->tex_mapping, b_sky_node.texture_mapping());
+ node = sky;
+ break;
+ }
+ }
+
+ if(node && node != graph->output())
+ graph->add(node);
+
+ return node;
+}
+
+static SocketPair node_socket_map_pair(PtrNodeMap& node_map, BL::Node b_node, BL::NodeSocket b_socket)
+{
+ BL::Node::inputs_iterator b_input;
+ BL::Node::outputs_iterator b_output;
+ string name = b_socket.name();
+ bool found = false;
+ int counter = 0, total = 0;
+
+ /* find in inputs */
+ for(b_node.inputs.begin(b_input); b_input != b_node.inputs.end(); ++b_input) {
+ if(b_input->name() == name) {
+ if(!found)
+ counter++;
+ total++;
+ }
+
+ if(b_input->ptr.data == b_socket.ptr.data)
+ found = true;
+ }
+
+ if(!found) {
+ /* find in outputs */
+ found = false;
+ counter = 0;
+ total = 0;
+
+ for(b_node.outputs.begin(b_output); b_output != b_node.outputs.end(); ++b_output) {
+ if(b_output->name() == name) {
+ if(!found)
+ counter++;
+ total++;
+ }
+
+ if(b_output->ptr.data == b_socket.ptr.data)
+ found = true;
+ }
+ }
+
+ /* rename if needed */
+ if(name == "Shader")
+ name = "Closure";
+
+ if(total > 1)
+ name = string_printf("%s%d", name.c_str(), counter);
+
+ return SocketPair(node_map[b_node.ptr.data], name);
+}
+
+static void add_nodes(BL::BlendData b_data, ShaderGraph *graph, BL::ShaderNodeTree b_ntree, BL::Node *b_group_node, PtrSockMap& sockets_map)
+{
+ /* add nodes */
+ BL::ShaderNodeTree::nodes_iterator b_node;
+ PtrNodeMap node_map;
+ map<void*, PtrSockMap> node_groups;
+
+ for(b_ntree.nodes.begin(b_node); b_node != b_ntree.nodes.end(); ++b_node) {
+ if(b_node->is_a(&RNA_NodeGroup)) {
+ BL::NodeGroup b_gnode(*b_node);
+ BL::ShaderNodeTree b_group_ntree(b_gnode.node_tree());
+
+ node_groups[b_node->ptr.data] = PtrSockMap();
+ add_nodes(b_data, graph, b_group_ntree, &b_gnode, node_groups[b_node->ptr.data]);
+ }
+ else {
+ ShaderNode *node = add_node(b_data, graph, b_group_node, BL::ShaderNode(*b_node));
+
+ if(node) {
+ BL::Node::inputs_iterator b_input;
+ BL::Node::outputs_iterator b_output;
+
+ node_map[b_node->ptr.data] = node;
+
+ for(b_node->inputs.begin(b_input); b_input != b_node->inputs.end(); ++b_input) {
+ SocketPair pair = node_socket_map_pair(node_map, *b_node, *b_input);
+ ShaderInput *input = pair.first->input(pair.second.c_str());
+ BL::NodeSocket sock(get_node_input(b_group_node, *b_input));
+
+ assert(input);
+
+ /* copy values for non linked inputs */
+ switch(input->type) {
+ case SHADER_SOCKET_FLOAT: {
+ BL::NodeSocketFloatNone value_sock(sock);
+ input->set(value_sock.default_value());
+ break;
+ }
+ case SHADER_SOCKET_COLOR: {
+ BL::NodeSocketRGBA rgba_sock(sock);
+ input->set(get_float3(rgba_sock.default_value()));
+ break;
+ }
+ case SHADER_SOCKET_NORMAL:
+ case SHADER_SOCKET_POINT:
+ case SHADER_SOCKET_VECTOR: {
+ BL::NodeSocketVectorNone vec_sock(sock);
+ input->set(get_float3(vec_sock.default_value()));
+ break;
+ }
+ case SHADER_SOCKET_CLOSURE:
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* connect nodes */
+ BL::NodeTree::links_iterator b_link;
+
+ for(b_ntree.links.begin(b_link); b_link != b_ntree.links.end(); ++b_link) {
+ /* get blender link data */
+ BL::Node b_from_node = b_link->from_node();
+ BL::Node b_to_node = b_link->to_node();
+
+ BL::NodeSocket b_from_sock = b_link->from_socket();
+ BL::NodeSocket b_to_sock = b_link->to_socket();
+
+ /* if link with group socket, add to map so we can connect it later */
+ if(b_group_node) {
+ if(!b_from_node) {
+ sockets_map[b_from_sock.ptr.data] =
+ node_socket_map_pair(node_map, b_to_node, b_to_sock);
+
+ continue;
+ }
+ else if(!b_to_node) {
+ sockets_map[b_to_sock.ptr.data] =
+ node_socket_map_pair(node_map, b_from_node, b_from_sock);
+
+ continue;
+ }
+ }
+
+ SocketPair from_pair, to_pair;
+
+ /* from sock */
+ if(b_from_node.is_a(&RNA_NodeGroup)) {
+ /* group node */
+ BL::NodeSocket group_sock = b_from_sock.group_socket();
+ from_pair = node_groups[b_from_node.ptr.data][group_sock.ptr.data];
+ }
+ else {
+ /* regular node */
+ from_pair = node_socket_map_pair(node_map, b_from_node, b_from_sock);
+ }
+
+ /* to sock */
+ if(b_to_node.is_a(&RNA_NodeGroup)) {
+ /* group node */
+ BL::NodeSocket group_sock = b_to_sock.group_socket();
+ to_pair = node_groups[b_to_node.ptr.data][group_sock.ptr.data];
+ }
+ else {
+ /* regular node */
+ to_pair = node_socket_map_pair(node_map, b_to_node, b_to_sock);
+ }
+
+ /* in case of groups there may not actually be a node inside the group
+ that the group socket connects to, so from_node or to_node may be NULL */
+ if(from_pair.first && to_pair.first) {
+ ShaderOutput *output = from_pair.first->output(from_pair.second.c_str());
+ ShaderInput *input = to_pair.first->input(to_pair.second.c_str());
+
+ graph->connect(output, input);
+ }
+ }
+}
+
+/* Sync Materials */
+
+void BlenderSync::sync_materials()
+{
+ shader_map.set_default(scene->shaders[scene->default_surface]);
+
+ /* material loop */
+ BL::BlendData::materials_iterator b_mat;
+
+ for(b_data.materials.begin(b_mat); b_mat != b_data.materials.end(); ++b_mat) {
+ Shader *shader;
+
+ /* test if we need to sync */
+ if(shader_map.sync(&shader, *b_mat)) {
+ ShaderGraph *graph = new ShaderGraph();
+
+ shader->name = b_mat->name().c_str();
+
+ /* create nodes */
+ if(b_mat->use_nodes() && b_mat->node_tree()) {
+ PtrSockMap sock_to_node;
+ BL::ShaderNodeTree b_ntree(b_mat->node_tree());
+
+ add_nodes(b_data, graph, b_ntree, NULL, sock_to_node);
+ }
+ else {
+ ShaderNode *closure, *out;
+
+ closure = graph->add(new DiffuseBsdfNode());
+ closure->input("Color")->value = get_float3(b_mat->diffuse_color());
+ out = graph->output();
+
+ graph->connect(closure->output("BSDF"), out->input("Surface"));
+ }
+
+ /* settings */
+ PointerRNA cmat = RNA_pointer_get(&b_mat->ptr, "cycles");
+ shader->sample_as_light = get_boolean(cmat, "sample_as_light");
+ shader->homogeneous_volume = get_boolean(cmat, "homogeneous_volume");
+
+ shader->set_graph(graph);
+ shader->tag_update(scene);
+ }
+ }
+}
+
+/* Sync World */
+
+void BlenderSync::sync_world()
+{
+ Background *background = scene->background;
+ Background prevbackground = *background;
+
+ BL::World b_world = b_scene.world();
+
+ if(world_recalc || b_world.ptr.data != world_map) {
+ Shader *shader = scene->shaders[scene->default_background];
+ ShaderGraph *graph = new ShaderGraph();
+
+ /* create nodes */
+ if(b_world && b_world.use_nodes() && b_world.node_tree()) {
+ PtrSockMap sock_to_node;
+ BL::ShaderNodeTree b_ntree(b_world.node_tree());
+
+ add_nodes(b_data, graph, b_ntree, NULL, sock_to_node);
+ }
+ else if(b_world) {
+ ShaderNode *closure, *out;
+
+ closure = graph->add(new BackgroundNode());
+ closure->input("Color")->value = get_float3(b_world.horizon_color());
+ out = graph->output();
+
+ graph->connect(closure->output("Background"), out->input("Surface"));
+ }
+
+ shader->set_graph(graph);
+ shader->tag_update(scene);
+ }
+
+ PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
+ background->transparent = get_boolean(cscene, "film_transparent");
+
+ if(background->modified(prevbackground))
+ background->tag_update(scene);
+
+ world_map = b_world.ptr.data;
+ world_recalc = false;
+}
+
+/* Sync Lamps */
+
+void BlenderSync::sync_lamps()
+{
+ shader_map.set_default(scene->shaders[scene->default_light]);
+
+ /* lamp loop */
+ BL::BlendData::lamps_iterator b_lamp;
+
+ for(b_data.lamps.begin(b_lamp); b_lamp != b_data.lamps.end(); ++b_lamp) {
+ Shader *shader;
+
+ /* test if we need to sync */
+ if(shader_map.sync(&shader, *b_lamp)) {
+ ShaderGraph *graph = new ShaderGraph();
+
+ /* create nodes */
+ if(b_lamp->use_nodes() && b_lamp->node_tree()) {
+ shader->name = b_lamp->name().c_str();
+
+ PtrSockMap sock_to_node;
+ BL::ShaderNodeTree b_ntree(b_lamp->node_tree());
+
+ add_nodes(b_data, graph, b_ntree, NULL, sock_to_node);
+ }
+ else {
+ ShaderNode *closure, *out;
+
+ closure = graph->add(new EmissionNode());
+ closure->input("Color")->value = get_float3(b_lamp->color());
+ closure->input("Strength")->value.x = b_lamp->energy()*10.0f;
+ out = graph->output();
+
+ graph->connect(closure->output("Emission"), out->input("Surface"));
+ }
+
+ shader->set_graph(graph);
+ shader->tag_update(scene);
+ }
+ }
+}
+
+void BlenderSync::sync_shaders()
+{
+ shader_map.pre_sync();
+
+ sync_world();
+ sync_lamps();
+ sync_materials();
+
+ /* false = don't delete unused shaders, not supported */
+ shader_map.post_sync(false);
+}
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp
new file mode 100644
index 00000000000..36cf29de128
--- /dev/null
+++ b/intern/cycles/blender/blender_sync.cpp
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2011, Blender Foundation.
+ *
+ * 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.
+ */
+
+#include "background.h"
+#include "film.h"
+#include "../render/filter.h"
+#include "graph.h"
+#include "integrator.h"
+#include "light.h"
+#include "mesh.h"
+#include "nodes.h"
+#include "object.h"
+#include "scene.h"
+#include "shader.h"
+
+#include "device.h"
+
+#include "blender_sync.h"
+#include "blender_util.h"
+
+#include "util_debug.h"
+#include "util_foreach.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Constructor */
+
+BlenderSync::BlenderSync(BL::BlendData b_data_, BL::Scene b_scene_, Scene *scene_, bool preview_)
+: b_data(b_data_), b_scene(b_scene_),
+ shader_map(&scene_->shaders),
+ object_map(&scene_->objects),
+ mesh_map(&scene_->meshes),
+ light_map(&scene_->lights),
+ world_map(NULL),
+ world_recalc(false)
+{
+ scene = scene_;
+ preview = preview_;
+}
+
+BlenderSync::~BlenderSync()
+{
+}
+
+/* Sync */
+
+bool BlenderSync::sync_recalc()
+{
+ /* sync recalc flags from blender to cycles. actual update is done separate,
+ so we can do it later on if doing it immediate is not suitable */
+
+ BL::BlendData::materials_iterator b_mat;
+
+ for(b_data.materials.begin(b_mat); b_mat != b_data.materials.end(); ++b_mat)
+ if(b_mat->is_updated())
+ shader_map.set_recalc(*b_mat);
+
+ BL::BlendData::lamps_iterator b_lamp;
+
+ for(b_data.lamps.begin(b_lamp); b_lamp != b_data.lamps.end(); ++b_lamp)
+ if(b_lamp->is_updated())
+ shader_map.set_recalc(*b_lamp);
+
+ BL::BlendData::objects_iterator b_ob;
+
+ for(b_data.objects.begin(b_ob); b_ob != b_data.objects.end(); ++b_ob) {
+ if(b_ob->is_updated()) {
+ object_map.set_recalc(*b_ob);
+ light_map.set_recalc(*b_ob);
+ }
+
+ if(object_is_mesh(*b_ob)) {
+ if(b_ob->is_updated_data() || b_ob->data().is_updated()) {
+ BL::ID key = object_is_modified(*b_ob)? *b_ob: b_ob->data();
+ mesh_map.set_recalc(key);
+ }
+ }
+ else if(object_is_light(*b_ob)) {
+ if(b_ob->is_updated_data() || b_ob->data().is_updated())
+ light_map.set_recalc(*b_ob);
+ }
+ }
+
+ BL::BlendData::meshes_iterator b_mesh;
+
+ for(b_data.meshes.begin(b_mesh); b_mesh != b_data.meshes.end(); ++b_mesh)
+ if(b_mesh->is_updated())
+ mesh_map.set_recalc(*b_mesh);
+
+ BL::BlendData::worlds_iterator b_world;
+
+ for(b_data.worlds.begin(b_world); b_world != b_data.worlds.end(); ++b_world)
+ if(world_map == b_world->ptr.data && b_world->is_updated())
+ world_recalc = true;
+
+ bool recalc =
+ shader_map.has_recalc() ||
+ object_map.has_recalc() ||
+ light_map.has_recalc() ||
+ mesh_map.has_recalc() ||
+ BlendDataObjects_is_updated_get(&b_data.ptr) ||
+ world_recalc;
+
+ return recalc;
+}
+
+void BlenderSync::sync_data(BL::SpaceView3D b_v3d)
+{
+ sync_integrator();
+ sync_film();
+ sync_render_layer(b_v3d);
+ sync_shaders();
+ sync_objects(b_v3d);
+}
+
+/* Integrator */
+
+void BlenderSync::sync_integrator()
+{
+ PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
+
+ Integrator *integrator = scene->integrator;
+ Integrator previntegrator = *integrator;
+
+ integrator->min_bounce = get_int(cscene, "min_bounces");
+ integrator->max_bounce = get_int(cscene, "max_bounces");
+
+ integrator->max_diffuse_bounce = get_int(cscene, "diffuse_bounces");
+ integrator->max_glossy_bounce = get_int(cscene, "glossy_bounces");
+ integrator->max_transmission_bounce = get_int(cscene, "transmission_bounces");
+
+ integrator->transparent_max_bounce = get_int(cscene, "transparent_max_bounces");
+ integrator->transparent_min_bounce = get_int(cscene, "transparent_min_bounces");
+ integrator->transparent_shadows = get_boolean(cscene, "use_transparent_shadows");
+
+ integrator->no_caustics = get_boolean(cscene, "no_caustics");
+ integrator->blur_caustics = get_float(cscene, "blur_caustics");
+
+ integrator->seed = get_int(cscene, "seed");
+
+ if(integrator->modified(previntegrator))
+ integrator->tag_update(scene);
+}
+
+/* Film */
+
+void BlenderSync::sync_film()
+{
+ PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
+
+ Film *film = scene->film;
+ Film prevfilm = *film;
+
+ film->exposure = get_float(cscene, "film_exposure");
+
+ if(film->modified(prevfilm))
+ film->tag_update(scene);
+
+ Filter *filter = scene->filter;
+ Filter prevfilter = *filter;
+
+ filter->filter_type = (FilterType)RNA_enum_get(&cscene, "filter_type");
+ filter->filter_width = (filter->filter_type == FILTER_BOX)? 1.0f: get_float(cscene, "filter_width");
+
+ if(filter->modified(prevfilter))
+ filter->tag_update(scene);
+}
+
+/* Render Layer */
+
+void BlenderSync::sync_render_layer(BL::SpaceView3D b_v3d)
+{
+ if(b_v3d) {
+ render_layer.scene_layer = get_layer(b_v3d.layers());
+ render_layer.layer = render_layer.scene_layer;
+ render_layer.material_override = PointerRNA_NULL;
+ }
+ else {
+ BL::RenderSettings r = b_scene.render();
+ BL::RenderSettings::layers_iterator b_rlay;
+ bool first = true;
+
+ for(r.layers.begin(b_rlay); b_rlay != r.layers.end(); ++b_rlay) {
+ /* single layer for now */
+ if(first) {
+ render_layer.scene_layer = get_layer(b_scene.layers());
+ render_layer.layer = get_layer(b_rlay->layers());
+ render_layer.material_override = b_rlay->material_override();
+
+ first = false;
+ }
+ }
+ }
+}
+
+/* Scene Parameters */
+
+SceneParams BlenderSync::get_scene_params(BL::Scene b_scene, bool background)
+{
+ SceneParams params;
+ PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
+ int shadingsystem = RNA_enum_get(&cscene, "shading_system");
+
+ if(shadingsystem == 0)
+ params.shadingsystem = SceneParams::SVM;
+ else if(shadingsystem == 1)
+ params.shadingsystem = SceneParams::OSL;
+
+ if(background)
+ params.bvh_type = SceneParams::BVH_STATIC;
+ else
+ params.bvh_type = (SceneParams::BVHType)RNA_enum_get(&cscene, "debug_bvh_type");
+
+ params.use_bvh_spatial_split = RNA_boolean_get(&cscene, "debug_use_spatial_splits");
+
+ return params;
+}
+
+/* Session Parameters */
+
+bool BlenderSync::get_session_pause(BL::Scene b_scene, bool background)
+{
+ PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
+ return (background)? false: get_boolean(cscene, "preview_pause");
+}
+
+static bool device_type_available(vector<DeviceType>& types, DeviceType dtype)
+{
+ foreach(DeviceType dt, types)
+ if(dt == dtype)
+ return true;
+
+ return false;
+}
+
+SessionParams BlenderSync::get_session_params(BL::Scene b_scene, bool background)
+{
+ SessionParams params;
+ PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
+
+ /* device type */
+ params.device_type = DEVICE_CPU;
+
+ if(RNA_enum_get(&cscene, "device") != 0) {
+ vector<DeviceType> types = Device::available_types();
+ DeviceType dtype = (RNA_enum_get(&cscene, "gpu_type") == 0)? DEVICE_CUDA: DEVICE_OPENCL;
+
+ if(device_type_available(types, dtype))
+ params.device_type = dtype;
+ else if(device_type_available(types, DEVICE_OPENCL))
+ params.device_type = DEVICE_OPENCL;
+ else if(device_type_available(types, DEVICE_CUDA))
+ params.device_type = DEVICE_CUDA;
+ }
+
+ /* Background */
+ params.background = background;
+
+ /* samples */
+ if(background) {
+ params.samples = get_int(cscene, "samples");
+ }
+ else {
+ params.samples = get_int(cscene, "preview_samples");
+ if(params.samples == 0)
+ params.samples = INT_MAX;
+ }
+
+ /* other parameters */
+ params.threads = b_scene.render().threads();
+ params.tile_size = get_int(cscene, "debug_tile_size");
+ params.min_size = get_int(cscene, "debug_min_size");
+ params.cancel_timeout = get_float(cscene, "debug_cancel_timeout");
+ params.reset_timeout = get_float(cscene, "debug_reset_timeout");
+ params.text_timeout = get_float(cscene, "debug_text_timeout");
+
+ if(background) {
+ params.progressive = true;
+ params.min_size = INT_MAX;
+ }
+ else
+ params.progressive = true;
+
+ return params;
+}
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h
new file mode 100644
index 00000000000..2e7b8ed253d
--- /dev/null
+++ b/intern/cycles/blender/blender_sync.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2011, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef __BLENDER_SYNC_H__
+#define __BLENDER_SYNC_H__
+
+#include "MEM_guardedalloc.h"
+#include "RNA_types.h"
+#include "RNA_access.h"
+#include "RNA_blender_cpp.h"
+
+#include "blender_util.h"
+
+#include "scene.h"
+#include "session.h"
+
+#include "util_map.h"
+#include "util_set.h"
+#include "util_transform.h"
+#include "util_vector.h"
+
+CCL_NAMESPACE_BEGIN
+
+class Background;
+class Camera;
+class Film;
+class Light;
+class Mesh;
+class Object;
+class Scene;
+class Shader;
+class ShaderGraph;
+class ShaderNode;
+
+class BlenderSync {
+public:
+ BlenderSync(BL::BlendData b_data, BL::Scene b_scene, Scene *scene_, bool preview_);
+ ~BlenderSync();
+
+ /* sync */
+ bool sync_recalc();
+ void sync_data(BL::SpaceView3D b_v3d);
+ void sync_camera(int width, int height);
+ void sync_view(BL::SpaceView3D b_v3d, BL::RegionView3D b_rv3d, int width, int height);
+
+ /* get parameters */
+ static SceneParams get_scene_params(BL::Scene b_scene, bool background);
+ static SessionParams get_session_params(BL::Scene b_scene, bool background);
+ static bool get_session_pause(BL::Scene b_scene, bool background);
+
+private:
+ /* sync */
+ void sync_lamps();
+ void sync_materials();
+ void sync_objects(BL::SpaceView3D b_v3d);
+ void sync_film();
+ void sync_integrator();
+ void sync_view();
+ void sync_world();
+ void sync_render_layer(BL::SpaceView3D b_v3d);
+ void sync_shaders();
+
+ void sync_nodes(Shader *shader, BL::ShaderNodeTree b_ntree);
+ Mesh *sync_mesh(BL::Object b_ob, bool object_updated);
+ void sync_object(BL::Object b_parent, int b_index, BL::Object b_object, Transform& tfm, uint visibility);
+ void sync_light(BL::Object b_parent, int b_index, BL::Object b_ob, Transform& tfm);
+
+ /* util */
+ void find_shader(BL::ID id, vector<uint>& used_shaders);
+ bool object_is_modified(BL::Object b_ob);
+ bool object_is_mesh(BL::Object b_ob);
+ bool object_is_light(BL::Object b_ob);
+
+ /* variables */
+ BL::BlendData b_data;
+ BL::Scene b_scene;
+
+ id_map<void*, Shader> shader_map;
+ id_map<ObjectKey, Object> object_map;
+ id_map<void*, Mesh> mesh_map;
+ id_map<ObjectKey, Light> light_map;
+ set<Mesh*> mesh_synced;
+ void *world_map;
+ bool world_recalc;
+
+ Scene *scene;
+ bool preview;
+
+ struct RenderLayerInfo {
+ RenderLayerInfo()
+ : scene_layer(0), layer(0),
+ material_override(PointerRNA_NULL)
+ {}
+
+ uint scene_layer;
+ uint layer;
+ BL::Material material_override;
+ } render_layer;
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __BLENDER_SYNC_H__ */
+
diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h
new file mode 100644
index 00000000000..bcca028dc2a
--- /dev/null
+++ b/intern/cycles/blender/blender_util.h
@@ -0,0 +1,331 @@
+/*
+ * Copyright 2011, Blender Foundation.
+ *
+ * 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.
+ */
+
+#ifndef __BLENDER_UTIL_H__
+#define __BLENDER_UTIL_H__
+
+#include "util_map.h"
+#include "util_path.h"
+#include "util_set.h"
+#include "util_transform.h"
+#include "util_types.h"
+#include "util_vector.h"
+
+/* Hacks to hook into Blender API */
+
+extern "C" {
+
+struct RenderEngine;
+struct RenderResult;
+
+ID *rna_Object_to_mesh(void *_self, void *reports, void *scene, int apply_modifiers, int settings);
+void rna_Main_meshes_remove(void *bmain, void *reports, void *mesh);
+void rna_Object_create_duplilist(void *ob, void *reports, void *sce);
+void rna_Object_free_duplilist(void *ob, void *reports);
+void rna_RenderLayer_rect_set(PointerRNA *ptr, const float *values);
+void rna_RenderPass_rect_set(PointerRNA *ptr, const float *values);
+struct RenderResult *RE_engine_begin_result(struct RenderEngine *engine, int x, int y, int w, int h);
+void RE_engine_update_result(struct RenderEngine *engine, struct RenderResult *result);
+void RE_engine_end_result(struct RenderEngine *engine, struct RenderResult *result);
+int RE_engine_test_break(struct RenderEngine *engine);
+void RE_engine_update_stats(struct RenderEngine *engine, const char *stats, const char *info);
+void RE_engine_update_progress(struct RenderEngine *engine, float progress);
+void engine_tag_redraw(void *engine);
+void engine_tag_update(void *engine);
+int rna_Object_is_modified(void *ob, void *scene, int settings);
+void BLI_timestr(double _time, char *str);
+
+}
+
+CCL_NAMESPACE_BEGIN
+
+static inline BL::Mesh object_to_mesh(BL::Object self, BL::Scene scene, bool apply_modifiers, bool render)
+{
+ ID *data = rna_Object_to_mesh(self.ptr.data, NULL, scene.ptr.data, apply_modifiers, (render)? 2: 1);
+ PointerRNA ptr;
+ RNA_id_pointer_create(data, &ptr);
+ return BL::Mesh(ptr);
+}
+
+static inline void object_remove_mesh(BL::BlendData data, BL::Mesh mesh)
+{
+ rna_Main_meshes_remove(data.ptr.data, NULL, mesh.ptr.data);
+}
+
+static inline void object_create_duplilist(BL::Object self, BL::Scene scene)
+{
+ rna_Object_create_duplilist(self.ptr.data, NULL, scene.ptr.data);
+}
+
+static inline void object_free_duplilist(BL::Object self)
+{
+ rna_Object_free_duplilist(self.ptr.data, NULL);
+}
+
+static inline bool object_is_modified(BL::Object self, BL::Scene scene, bool preview)
+{
+ return rna_Object_is_modified(self.ptr.data, scene.ptr.data, (preview)? (1<<0): (1<<1))? true: false;
+}
+
+/* Utilities */
+
+static inline Transform get_transform(BL::Array<float, 16> array)
+{
+ Transform tfm;
+
+ /* we assume both types to be just 16 floats, and transpose because blender
+ use column major matrix order while we use row major */
+ memcpy(&tfm, &array, sizeof(float)*16);
+ tfm = transform_transpose(tfm);
+
+ return tfm;
+}
+
+static inline float2 get_float2(BL::Array<float, 2> array)
+{
+ return make_float2(array[0], array[1]);
+}
+
+static inline float3 get_float3(BL::Array<float, 2> array)
+{
+ return make_float3(array[0], array[1], 0.0f);
+}
+
+static inline float3 get_float3(BL::Array<float, 3> array)
+{
+ return make_float3(array[0], array[1], array[2]);
+}
+
+static inline float3 get_float3(BL::Array<float, 4> array)
+{
+ return make_float3(array[0], array[1], array[2]);
+}
+
+static inline int4 get_int4(BL::Array<int, 4> array)
+{
+ return make_int4(array[0], array[1], array[2], array[3]);
+}
+
+static inline uint get_layer(BL::Array<int, 20> array)
+{
+ uint layer = 0;
+
+ for(uint i = 0; i < 20; i++)
+ if(array[i])
+ layer |= (1 << i);
+
+ return layer;
+}
+
+/*static inline float3 get_float3(PointerRNA& ptr, const char *name)
+{
+ float3 f;
+ RNA_float_get_array(&ptr, name, &f.x);
+ return f;
+}*/
+
+static inline bool get_boolean(PointerRNA& ptr, const char *name)
+{
+ return RNA_boolean_get(&ptr, name)? true: false;
+}
+
+static inline float get_float(PointerRNA& ptr, const char *name)
+{
+ return RNA_float_get(&ptr, name);
+}
+
+static inline int get_int(PointerRNA& ptr, const char *name)
+{
+ return RNA_int_get(&ptr, name);
+}
+
+static inline int get_enum(PointerRNA& ptr, const char *name)
+{
+ return RNA_enum_get(&ptr, name);
+}
+
+static inline string get_enum_identifier(PointerRNA& ptr, const char *name)
+{
+ PropertyRNA *prop = RNA_struct_find_property(&ptr, name);
+ const char *identifier = "";
+ int value = RNA_property_enum_get(&ptr, prop);
+
+ RNA_property_enum_identifier(NULL, &ptr, prop, value, &identifier);
+
+ return string(identifier);
+}
+
+/* Relative Paths */
+
+static inline string blender_absolute_path(BL::BlendData b_data, BL::ID b_id, const string& path)
+{
+ if(path.size() >= 2 && path[0] == '/' && path[1] == '/') {
+ string dirname = (b_id.library())? b_id.library().filepath(): b_data.filepath();
+ return path_join(path_dirname(dirname), path.substr(2));
+ }
+
+ return path;
+}
+
+/* ID Map
+ *
+ * Utility class to keep in sync with blender data.
+ * Used for objects, meshes, lights and shaders. */
+
+template<typename K, typename T>
+class id_map {
+public:
+ id_map(vector<T*> *scene_data_)
+ {
+ scene_data = scene_data_;
+ }
+
+ T *find(BL::ID id)
+ {
+ return find(id.ptr.id.data);
+ }
+
+ T *find(const K& key)
+ {
+ if(b_map.find(key) != b_map.end()) {
+ T *data = b_map[key];
+ return data;
+ }
+
+ return NULL;
+ }
+
+ void set_recalc(BL::ID id)
+ {
+ b_recalc.insert(id.ptr.data);
+ }
+
+ bool has_recalc()
+ {
+ return !(b_recalc.empty());
+ }
+
+ void pre_sync()
+ {
+ used_set.clear();
+ }
+
+ bool sync(T **r_data, BL::ID id)
+ {
+ return sync(r_data, id, id, id.ptr.id.data);
+ }
+
+ bool sync(T **r_data, BL::ID id, BL::ID parent, const K& key)
+ {
+ T *data = find(key);
+ bool recalc;
+
+ if(!data) {
+ /* add data if it didn't exist yet */
+ data = new T();
+ scene_data->push_back(data);
+ b_map[key] = data;
+ recalc = true;
+ }
+ else {
+ recalc = (b_recalc.find(id.ptr.data) != b_recalc.end());
+ if(parent.ptr.data)
+ recalc = recalc || (b_recalc.find(parent.ptr.data) != b_recalc.end());
+ }
+
+ used(data);
+
+ *r_data = data;
+ return recalc;
+ }
+
+ void used(T *data)
+ {
+ /* tag data as still in use */
+ used_set.insert(data);
+ }
+
+ void set_default(T *data)
+ {
+ b_map[NULL] = data;
+ }
+
+ bool post_sync(bool do_delete = true)
+ {
+ /* remove unused data */
+ vector<T*> new_scene_data;
+ typename vector<T*>::iterator it;
+ bool deleted = false;
+
+ for(it = scene_data->begin(); it != scene_data->end(); it++) {
+ T *data = *it;
+
+ if(do_delete && used_set.find(data) == used_set.end()) {
+ delete data;
+ deleted = true;
+ }
+ else
+ new_scene_data.push_back(data);
+ }
+
+ *scene_data = new_scene_data;
+
+ /* update mapping */
+ map<K, T*> new_map;
+ typedef pair<const K, T*> TMapPair;
+ typename map<K, T*>::iterator jt;
+
+ for(jt = b_map.begin(); jt != b_map.end(); jt++) {
+ TMapPair& pair = *jt;
+
+ if(used_set.find(pair.second) != used_set.end())
+ new_map[pair.first] = pair.second;
+ }
+
+ used_set.clear();
+ b_recalc.clear();
+ b_map = new_map;
+
+ return deleted;
+ }
+
+protected:
+ vector<T*> *scene_data;
+ map<K, T*> b_map;
+ set<T*> used_set;
+ set<void*> b_recalc;
+};
+
+/* Object Key */
+
+struct ObjectKey {
+ void *parent;
+ int index;
+ void *ob;
+
+ ObjectKey(void *parent_, int index_, void *ob_)
+ : parent(parent_), index(index_), ob(ob_) {}
+
+ bool operator<(const ObjectKey& k) const
+ { return (parent < k.parent || (parent == k.parent && (index < k.index || (index == k.index && ob < k.ob)))); }
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __BLENDER_UTIL_H__ */
+