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.txt72
-rw-r--r--intern/cycles/blender/addon/__init__.py73
-rw-r--r--intern/cycles/blender/addon/engine.py107
-rw-r--r--intern/cycles/blender/addon/enums.py113
-rw-r--r--intern/cycles/blender/addon/properties.py120
-rw-r--r--intern/cycles/blender/addon/ui.py388
-rw-r--r--intern/cycles/blender/addon/xml.py99
-rw-r--r--intern/cycles/blender/blender_camera.cpp249
-rw-r--r--intern/cycles/blender/blender_mesh.cpp300
-rw-r--r--intern/cycles/blender/blender_object.cpp183
-rw-r--r--intern/cycles/blender/blender_python.cpp230
-rw-r--r--intern/cycles/blender/blender_session.cpp280
-rw-r--r--intern/cycles/blender/blender_session.h77
-rw-r--r--intern/cycles/blender/blender_shader.cpp629
-rw-r--r--intern/cycles/blender/blender_sync.cpp210
-rw-r--r--intern/cycles/blender/blender_sync.h105
-rw-r--r--intern/cycles/blender/blender_util.h326
17 files changed, 3561 insertions, 0 deletions
diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt
new file mode 100644
index 00000000000..a75f7295e31
--- /dev/null
+++ b/intern/cycles/blender/CMakeLists.txt
@@ -0,0 +1,72 @@
+
+SET(sources
+ blender_camera.cpp
+ blender_mesh.cpp
+ blender_object.cpp
+ blender_python.cpp
+ blender_session.cpp
+ blender_shader.cpp
+ blender_sync.cpp)
+
+SET(headers
+ blender_sync.h
+ blender_session.h
+ blender_util.h)
+
+SET(addonfiles
+ addon/__init__.py
+ addon/engine.py
+ addon/enums.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})
+
+SET(LIBRARIES
+ render
+ bvh
+ device
+ kernel
+ util
+ subd
+ ${Boost_LIBRARIES}
+ ${OPENGL_LIBRARIES}
+ ${OPENIMAGEIO_LIBRARY}
+ ${PYTHON_LIBRARIES}
+ ${GLUT_LIBRARIES}
+ ${GLEW_LIBRARIES}
+ ${BLENDER_LIBRARIES})
+
+IF(WITH_OSL)
+ LIST(APPEND LIBRARIES kernel_osl ${OSL_LIBRARIES})
+ENDIF(WITH_OSL)
+
+IF(WITH_PARTIO)
+ LIST(APPEND LIBRARIES ${PARTIO_LIBRARIES})
+ENDIF(WITH_PARTIO)
+
+IF(WITH_OPENCL)
+ LIST(APPEND LIBRARIES ${OPENCL_LIBRARIES})
+ENDIF(WITH_OPENCL)
+
+SET(CMAKE_MODULE_LINKER_FLAGS ${PYTHON_MODULE_FLAGS})
+
+ADD_LIBRARY(cycles_blender MODULE ${sources} ${headers})
+TARGET_LINK_LIBRARIES(cycles_blender ${LIBRARIES})
+
+INSTALL(FILES ${addonfiles} DESTINATION ${INSTALL_PATH}/cycles)
+INSTALL(TARGETS cycles_blender LIBRARY DESTINATION ${INSTALL_PATH}/cycles)
+
+IF(UNIX AND NOT APPLE)
+ SET_TARGET_PROPERTIES(cycles_blender PROPERTIES INSTALL_RPATH $ORIGIN/lib)
+ENDIF()
+
diff --git a/intern/cycles/blender/addon/__init__.py b/intern/cycles/blender/addon/__init__.py
new file mode 100644
index 00000000000..0a2e5cee142
--- /dev/null
+++ b/intern/cycles/blender/addon/__init__.py
@@ -0,0 +1,73 @@
+#
+# 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
+
+class CyclesRender(bpy.types.RenderEngine):
+ bl_idname = 'CYCLES'
+ bl_label = "Cycles"
+
+ def __init__(self):
+ engine.init()
+ self.session = None
+
+ def __del__(self):
+ engine.free(self)
+
+ def render(self, scene):
+ engine.create(self, scene, True)
+ engine.render(self, scene)
+
+ def draw(self, scene):
+ if not self.session:
+ engine.create(self, scene, False)
+ engine.draw(self, scene)
+
+ def update(self, scene):
+ engine.update(self, scene)
+
+def register():
+ properties.register()
+ ui.register()
+ xml.register()
+ bpy.utils.register_module(__name__)
+
+def unregister():
+ xml.unregister()
+ ui.unregister()
+ properties.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..fb98068766f
--- /dev/null
+++ b/intern/cycles/blender/addon/engine.py
@@ -0,0 +1,107 @@
+#
+# 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():
+ from cycles import libcycles_blender as lib
+ import os.path
+ lib.init(os.path.dirname(__file__))
+
+def create(engine, scene, offline):
+ from cycles import libcycles_blender as lib
+ data = bpy.data.as_pointer()
+ scene = scene.as_pointer()
+
+ if not offline and bpy.context.area.type == 'VIEW_3D':
+ region = bpy.context.region.as_pointer()
+ v3d = bpy.context.space_data.as_pointer()
+ rv3d = bpy.context.region_data.as_pointer()
+ else:
+ region = 0
+ v3d = 0
+ rv3d = 0
+
+ engine.session = lib.create(engine.as_pointer(), data, scene, region, v3d, rv3d)
+
+def free(engine):
+ if "session" in dir(engine):
+ if engine.session:
+ from cycles import libcycles_blender as lib
+ lib.free(engine.session)
+ del engine.session
+
+def render(engine, scene):
+ from cycles import libcycles_blender as lib
+ lib.render(engine.session)
+
+def update(engine, scene):
+ from cycles import libcycles_blender as lib
+ lib.sync(engine.session)
+
+def draw(engine, scene):
+ from cycles import libcycles_blender as lib
+ v3d = bpy.context.space_data.as_pointer()
+ rv3d = bpy.context.region_data.as_pointer()
+ region = bpy.context.region
+
+ # draw render image
+ status, substatus = lib.draw(engine.session, v3d, rv3d)
+
+ # draw text over image
+ if status != "":
+ import blf
+ import bgl
+
+ fontid = 0 # todo, find out how to set this
+ dim = blf.dimensions(fontid, status)
+ dim_sub = blf.dimensions(fontid, substatus)
+
+ padding = 5
+
+ x = (region.width - max(dim[0], dim_sub[0]))*0.5 - padding
+ y = (region.height - (dim[1] + dim_sub[1] + padding))*0.5 - padding
+
+ bgl.glColor4f(0.0, 0.0, 0.0, 0.5)
+ bgl.glEnable(bgl.GL_BLEND)
+ bgl.glBlendFunc(bgl.GL_SRC_ALPHA, bgl.GL_ONE_MINUS_SRC_ALPHA)
+ bgl.glRectf(x, y, x+max(dim[0], dim_sub[0])+padding+padding, y+dim[1]+dim_sub[1]+padding+padding+2)
+ bgl.glDisable(bgl.GL_BLEND)
+
+ x = (region.width - dim[0])*0.5
+ y = (region.height - (dim[1] + dim_sub[1] + padding))*0.5 + dim_sub[1] + padding
+
+ bgl.glColor3f(0.8, 0.8, 0.8)
+ blf.position(fontid, x, y, 0)
+ blf.draw(fontid, status)
+
+ x = (region.width - dim_sub[0])*0.5
+ y = (region.height - (dim[1] + dim_sub[1] + padding))*0.5
+
+ bgl.glColor3f(0.6, 0.6, 0.6)
+ blf.position(fontid, x, y, 0)
+ blf.draw(fontid, substatus)
+
+def available_devices():
+ from cycles import libcycles_blender as lib
+ return lib.available_devices()
+
+def with_osl():
+ from cycles import libcycles_blender as lib
+ return lib.with_osl()
+
diff --git a/intern/cycles/blender/addon/enums.py b/intern/cycles/blender/addon/enums.py
new file mode 100644
index 00000000000..fd12fa6d5a5
--- /dev/null
+++ b/intern/cycles/blender/addon/enums.py
@@ -0,0 +1,113 @@
+#
+# 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.
+#
+
+devices = (
+("CPU", "CPU", "Processor"),
+("GPU", "GPU", "Graphics card (NVidia only)"))
+
+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"))
+
+response_curves = (
+("None", "None", ""),
+("", "Agfa", ""),
+("Agfacolor Futura 100", "Futura 100", ""),
+("Agfacolor Futura 200", "Futura 200", ""),
+("Agfacolor Futura 400", "Futura 400", ""),
+("Agfacolor Futura II 100", "Futura II 100", ""),
+("Agfacolor Futura II 200", "Futura II 200", ""),
+("Agfacolor Futura II 400", "Futura II 400", ""),
+("Agfacolor HDC 100 plus", "HDC 100 plus", ""),
+("Agfacolor HDC 400 plus", "HDC 400 plus", ""),
+("Agfacolor HDC 200 plus", "HDC 200 plus", ""),
+("Agfacolor Optima II 100", "Optima II 100", ""),
+("Agfacolor Optima II 200", "Optima II 200", ""),
+("Agfacolor Ultra 050", "Ultra 050", ""),
+("", "Agfa", ""),
+("Agfacolor Vista 100", "Vista 100", ""),
+("Agfacolor Vista 200", "Vista 200", ""),
+("Agfacolor Vista 400", "Vista 400", ""),
+("Agfacolor Vista 800", "Vista 800", ""),
+("Agfachrome CT Precisa 100", "CT Precisa 100", ""),
+("Agfachrome CT Precisa 200", "CT Precisa 200", ""),
+("Agfachrome RSX2 050", "Agfachrome RSX2 050", ""),
+("Agfachrome RSX2 100", "Agfachrome RSX2 100", ""),
+("Agfachrome RSX2 200", "Agfachrome RSX2 200", ""),
+("Advantix 100", "Advantix 100", ""),
+("Advantix 200", "Advantix 200", ""),
+("Advantix 400", "Advantix 400", ""),
+("", "Kodak", ""),
+("Gold 100", "Gold 100", ""),
+("Gold 200", "Gold 200", ""),
+("Max Zoom 800", "Max Zoom 800", ""),
+("Portra 100T", "Portra 100T", ""),
+("Portra 160NC", "Portra 160NC", ""),
+("Portra 160VC", "Portra 160VC", ""),
+("Portra 800", "Portra 800", ""),
+("Portra 400VC", "Portra 400VC", ""),
+("Portra 400NC", "Portra 400NC", ""),
+("", "Kodak", ""),
+("Ektachrome 100 plus", "Ektachrome 100 plus", ""),
+("Ektachrome 320T", "Ektachrome 320T", ""),
+("Ektachrome 400X", "Ektachrome 400X", ""),
+("Ektachrome 64", "Ektachrome 64", ""),
+("Ektachrome 64T", "Ektachrome 64T", ""),
+("Ektachrome E100S", "Ektachrome E100S", ""),
+("Ektachrome 100", "Ektachrome 100", ""),
+("Kodachrome 200", "Kodachrome 200", ""),
+("Kodachrome 25", "Kodachrome 25", ""),
+("Kodachrome 64", "Kodachrome 64", ""),
+#("DSCS 3151", "DSCS 3151", ""),
+#("DSCS 3152", "DSCS 3152", ""),
+#("DSCS 3153", "DSCS 3153", ""),
+#("DSCS 3154", "DSCS 3154", ""),
+#("DSCS 3155", "DSCS 3155", ""),
+#("DSCS 3156", "DSCS 3156", ""),
+#("KAI-0311", "KAI-0311", ""),
+#("KAF-2001", "KAF-2001", ""),
+#("KAF-3000", "KAF-3000", ""),
+#("KAI-0372", "KAI-0372", ""),
+#("KAI-1010", "KAI-1010", ""),
+("", "Fujifilm", ""),
+("F-125", "F-125", ""),
+("F-250", "F-250", ""),
+("F-400", "F-400", ""),
+("FCI", "FCI", ""),
+("FP2900Z", "FP2900Z", ""),
+("", "Eastman", ""),
+("Double X Neg 12min", "Double X Neg 12min", ""),
+("Double X Neg 6min", "Double X Neg 6min", ""),
+("Double X Neg 5min", "Double X Neg 5min", ""),
+("Double X Neg 4min", "Double X Neg 4min", ""),
+("", "Canon", ""),
+("Optura 981111", "Optura 981111", ""),
+("Optura 981113", "Optura 981113", ""),
+("Optura 981114", "Optura 981114", ""),
+("Optura 981111.SLRR", "Optura 981111.SLRR", "")
+)
+
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
new file mode 100644
index 00000000000..3ec0587d89c
--- /dev/null
+++ b/intern/cycles/blender/addon/properties.py
@@ -0,0 +1,120 @@
+#
+# 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 *
+
+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.shading_system = EnumProperty(name="Shading System", description="Shading system to use for rendering",
+ items=enums.shading_systems, default="GPU_COMPATIBLE")
+
+ cls.passes = IntProperty(name="Passes", description="Number of passes to render",
+ default=10, min=1, max=2147483647)
+ cls.min_bounces = IntProperty(name="Min Bounces", description="Minimum number of bounces",
+ default=3, min=0, max=1024)
+ cls.max_bounces = IntProperty(name="Max Bounces", description="Maximum number of bounces",
+ default=8, min=0, max=1024)
+ 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.exposure = FloatProperty(name="Exposure", description="Image brightness scale",
+ default=1.0, min=0.0, max=10.0)
+ cls.response_curve = EnumProperty(name="Response Curve", description="Measured camera film response",
+ items=enums.response_curves, default="Advantix 400")
+
+ 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="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.lens_radius = FloatProperty(name="Lens radius", description="Lens radius for depth of field",
+ default=0.0, min=0.0, max=10.0)
+
+ @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")
+
+ @classmethod
+ def unregister(cls):
+ del bpy.types.Material.cycles
+
+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
+
+def register():
+ bpy.utils.register_class(CyclesRenderSettings)
+ bpy.utils.register_class(CyclesCameraSettings)
+ bpy.utils.register_class(CyclesMaterialSettings)
+ 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(CyclesMeshSettings)
+
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
new file mode 100644
index 00000000000..2c4f24db2c5
--- /dev/null
+++ b/intern/cycles/blender/addon/ui.py
@@ -0,0 +1,388 @@
+#
+# 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 cycles import enums
+from cycles import engine
+
+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, bpy.types.Panel):
+ bl_label = "Integrator"
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+ cycles = scene.cycles
+
+ split = layout.split()
+
+ col = split.column()
+ col.prop(cycles, "passes")
+ col.prop(cycles, "no_caustics")
+
+ col = split.column()
+ col = col.column(align=True)
+ col.prop(cycles, "max_bounces")
+ col.prop(cycles, "min_bounces")
+
+ #row = col.row()
+ #row.prop(cycles, "blur_caustics")
+ #row.active = not cycles.no_caustics
+
+class CyclesRender_PT_film(CyclesButtonsPanel, bpy.types.Panel):
+ bl_label = "Film"
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+ cycles = scene.cycles
+
+ split = layout.split()
+
+ split.prop(cycles, "exposure")
+ split.prop(cycles, "response_curve", text="")
+
+class CyclesRender_PT_debug(CyclesButtonsPanel, bpy.types.Panel):
+ bl_label = "Debug"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+ cycles = scene.cycles
+
+ split = layout.split()
+
+ col = split.column()
+
+ sub = col.column(align=True)
+ sub.prop(cycles, "debug_bvh_type", text="")
+ sub.prop(cycles, "debug_use_spatial_splits")
+
+ sub = col.column(align=True)
+ sub.prop(cycles, "debug_tile_size")
+ sub.prop(cycles, "debug_min_size")
+
+ col = split.column(align=True)
+ col.prop(cycles, "debug_cancel_timeout")
+ col.prop(cycles, "debug_reset_timeout")
+ col.prop(cycles, "debug_text_timeout")
+
+class Cycles_PT_post_processing(CyclesButtonsPanel, bpy.types.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 Cycles_PT_camera(CyclesButtonsPanel, bpy.types.Panel):
+ bl_label = "Cycles"
+ bl_context = "data"
+
+ @classmethod
+ def poll(cls, context):
+ return context.camera
+
+ def draw(self, context):
+ layout = self.layout
+
+ camera = context.camera
+ cycles = camera.cycles
+
+ layout.prop(cycles, "lens_radius")
+
+class Cycles_PT_context_material(CyclesButtonsPanel, bpy.types.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, bpy.types.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:
+ cycles = mesh.cycles
+ elif curve:
+ cycles = curve.cycles
+ elif mball:
+ cycles = mball.cycles
+
+ layout.prop(cycles, "displacement_method", text="Method")
+ layout.prop(cycles, "use_subdivision");
+ layout.prop(cycles, "dicing_rate");
+
+def find_node(material, nodetype):
+ if material and material.node_tree:
+ ntree = material.node_tree
+
+ for node in ntree.nodes:
+ if type(node) is not bpy.types.NodeGroup 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")
+ return
+
+ ntree = id.node_tree
+
+ node = find_node(id, output_type)
+ if not node:
+ layout.label(text="No output node.")
+
+ input = find_node_input(node, input_name)
+ layout.template_node_view(id, ntree, node, input);
+
+class CyclesLamp_PT_lamp(CyclesButtonsPanel, bpy.types.Panel):
+ bl_label = "Surface"
+ 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, bpy.types.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, bpy.types.Panel):
+ bl_label = "Volume"
+ bl_context = "world"
+
+ @classmethod
+ def poll(cls, context):
+ return context.world and CyclesButtonsPanel.poll(context)
+
+ def draw(self, context):
+ layout = self.layout
+ layout.active = False
+
+ mat = context.world
+ panel_node_draw(layout, mat, 'OUTPUT_WORLD', 'Volume')
+
+class CyclesMaterial_PT_surface(CyclesButtonsPanel, bpy.types.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, bpy.types.Panel):
+ bl_label = "Volume"
+ bl_context = "material"
+
+ @classmethod
+ def poll(cls, context):
+ return context.material and CyclesButtonsPanel.poll(context)
+
+ def draw(self, context):
+ layout = self.layout
+ layout.active = False
+
+ mat = context.material
+ panel_node_draw(layout, mat, 'OUTPUT_MATERIAL', 'Volume')
+
+class CyclesMaterial_PT_displacement(CyclesButtonsPanel, bpy.types.Panel):
+ bl_label = "Displacement"
+ 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', 'Displacement')
+
+class CyclesMaterial_PT_settings(CyclesButtonsPanel, bpy.types.Panel):
+ bl_label = "Settings"
+ bl_context = "material"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ # return context.material and CyclesButtonsPanel.poll(context)
+ return False
+
+ def draw(self, context):
+ layout = self.layout
+
+ mat = context.material
+
+ row = layout.row()
+ row.label(text="Light Group:")
+ row.prop(mat, "light_group", text="")
+
+def draw_device(self, context):
+ scene = context.scene
+ layout = self.layout
+
+ if scene.render.engine == "CYCLES":
+ cycles = scene.cycles
+
+ if 'cuda' in engine.available_devices():
+ layout.prop(cycles, "device")
+ if cycles.device == 'CPU' and engine.with_osl():
+ layout.prop(cycles, "shading_system")
+
+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_vertex_groups,
+ bpy.types.DATA_PT_shape_keys,
+ bpy.types.DATA_PT_uv_texture,
+ bpy.types.DATA_PT_vertex_colors,
+ bpy.types.DATA_PT_custom_props_mesh,
+ bpy.types.DATA_PT_context_camera,
+ bpy.types.DATA_PT_camera,
+ bpy.types.DATA_PT_camera_display,
+ bpy.types.DATA_PT_custom_props_camera,
+ bpy.types.DATA_PT_context_lamp,
+ bpy.types.DATA_PT_custom_props_lamp,
+ bpy.types.TEXTURE_PT_context_texture]
+
+def register():
+ bpy.types.RENDER_PT_render.append(draw_device)
+
+ for panel in get_panels():
+ panel.COMPAT_ENGINES.add('CYCLES')
+
+def unregister():
+ bpy.types.RENDER_PT_render.remove(draw_device)
+
+ 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..4d1b43d4d95
--- /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
+import io_utils
+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, io_utils.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.create_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..cc4cfda8793
--- /dev/null
+++ b/intern/cycles/blender/blender_camera.cpp
@@ -0,0 +1,249 @@
+/*
+ * 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 lensradius;
+ float focaldistance;
+
+ float2 shift;
+ float2 offset;
+ float zoom;
+
+ float2 pixelaspect;
+
+ 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);
+}
+
+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->lensradius = RNA_float_get(&ccamera, "lens_radius");
+ bcam->focaldistance = blender_camera_focal_distance(b_ob, b_camera);
+
+ bcam->shift.x = b_camera.shift_x();
+ bcam->shift.y = b_camera.shift_y();
+ }
+ 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;
+
+ if(xratio > yratio) {
+ 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(16.0f/bcam->lens/aspectratio);
+ cam->focaldistance = bcam->focaldistance;
+ cam->lensradius = bcam->lensradius;
+
+ /* 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 = 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..e18a67bed8f
--- /dev/null
+++ b/intern/cycles/blender/blender_mesh.cpp
@@ -0,0 +1,300 @@
+/*
+ * 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(v = b_mesh.vertices.begin(); v != b_mesh.vertices.end(); ++v)
+ mesh->verts.push_back(get_float3(v->co()));
+
+ /* create faces */
+ BL::Mesh::faces_iterator f;
+ vector<int> nverts;
+
+ for(f = b_mesh.faces.begin(); 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(v = b_mesh.vertices.begin(); 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(l = b_mesh.vertex_colors.begin(); l != b_mesh.vertex_colors.end(); ++l) {
+ if(!mesh_need_attribute(scene, mesh, ustring(l->name())))
+ continue;
+
+ Attribute *attr = mesh->attributes.add(
+ ustring(l->name()), TypeDesc::TypeColor, Attribute::CORNER);
+
+ BL::MeshColorLayer::data_iterator c;
+ float3 *fdata = attr->data_float3();
+ size_t i = 0;
+
+ for(c = l->data.begin(); c != l->data.end(); ++c, ++i) {
+ fdata[0] = get_float3(c->color1());
+ fdata[1] = get_float3(c->color2());
+ fdata[2] = get_float3(c->color3());
+ fdata += 3;
+
+ if(nverts[i] == 4) {
+ fdata[0] = get_float3(c->color1());
+ fdata[1] = get_float3(c->color3());
+ fdata[2] = get_float3(c->color4());
+ fdata += 3;
+ }
+ }
+ }
+ }
+
+ /* create uv layer attributes */
+ {
+ BL::Mesh::uv_textures_iterator l;
+
+ for(l = b_mesh.uv_textures.begin(); l != b_mesh.uv_textures.end(); ++l) {
+ Attribute::Standard std = (l->active_render())? Attribute::STD_UV: Attribute::STD_NONE;
+ ustring name = ustring(l->name());
+
+ 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(t = l->data.begin(); 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(v = b_mesh.vertices.begin(); v != b_mesh.vertices.end(); ++v)
+ sdmesh.add_vert(get_float3(v->co()));
+
+ /* create faces */
+ BL::Mesh::faces_iterator f;
+
+ for(f = b_mesh.faces.begin(); 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(slot = b_ob.material_slots.begin(); slot != b_ob.material_slots.end(); ++slot)
+ 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;
+ }
+ }
+
+ /* create derived mesh */
+ BL::Mesh b_mesh = object_to_mesh(b_ob, b_scene, true, !preview);
+ /* todo: this will crash on non-mesh types! */
+ 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());
+
+ 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(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..51fd743956d
--- /dev/null
+++ b/intern/cycles/blender/blender_object.cpp
@@ -0,0 +1,183 @@
+/*
+ * 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(slot = b_ob.material_slots.begin(); 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));
+}
+
+/* Light */
+
+void BlenderSync::sync_light(BL::Object b_ob, Transform& tfm)
+{
+ /* test if we need to sync */
+ Light *light;
+
+ /* todo: account for instancing */
+ if(!light_map.sync(&light, b_ob))
+ return;
+
+ /* location */
+ light->co = make_float3(tfm.x.w, tfm.y.w, tfm.z.w);
+
+ /* shader */
+ BL::Lamp b_lamp(b_ob.data());
+ 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];
+
+ /* tag */
+ light->tag_update(scene);
+}
+
+/* Object */
+
+void BlenderSync::sync_object(BL::Object b_parent, int b_index, BL::Object b_ob, Transform& tfm)
+{
+ /* light is handled separately */
+ if(object_is_light(b_ob)) {
+ sync_light(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, key)) {
+ object->name = b_ob.name();
+ object->tfm = tfm;
+ 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 layer;
+
+ if(b_v3d)
+ layer = get_layer(b_v3d.layers());
+ else
+ layer = get_layer(b_scene.layers());
+
+ /* prepare for sync */
+ light_map.pre_sync();
+ mesh_map.pre_sync();
+ object_map.pre_sync();
+
+ /* object loop */
+ BL::Scene::objects_iterator b_ob;
+
+ for(b_ob = b_scene.objects.begin(); b_ob != b_scene.objects.end(); ++b_ob) {
+ bool hide = (b_v3d)? b_ob->hide(): b_ob->hide_render();
+
+ if(!hide && get_layer(b_ob->layers()) & layer) {
+ 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_dup = b_ob->dupli_list.begin(); 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);
+ 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);
+ }
+ }
+ }
+
+ /* 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);
+}
+
+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..7c5bdf0e519
--- /dev/null
+++ b/intern/cycles/blender/blender_python.cpp
@@ -0,0 +1,230 @@
+/*
+ * 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;
+
+ if(!PyArg_ParseTuple(args, "s", &path))
+ return NULL;
+
+ path_init(path);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *create_func(PyObject *self, PyObject *args)
+{
+ Py_ssize_t pyengine, pydata, pyscene, pyregion, pyv3d, pyrv3d;
+
+ if(!PyArg_ParseTuple(args, "nnnnnn", &pyengine, &pydata, &pyscene, &pyregion, &pyv3d, &pyrv3d))
+ return NULL;
+
+ /* RNA */
+ PointerRNA engineptr;
+ RNA_pointer_create(NULL, &RNA_RenderEngine, (void*)pyengine, &engineptr);
+ BL::RenderEngine engine(engineptr);
+
+ PointerRNA dataptr;
+ RNA_id_pointer_create((ID*)pydata, &dataptr);
+ BL::BlendData data(dataptr);
+
+ PointerRNA sceneptr;
+ RNA_id_pointer_create((ID*)pyscene, &sceneptr);
+ BL::Scene scene(sceneptr);
+
+ PointerRNA regionptr;
+ RNA_id_pointer_create((ID*)pyregion, &regionptr);
+ BL::Region region(regionptr);
+
+ PointerRNA v3dptr;
+ RNA_id_pointer_create((ID*)pyv3d, &v3dptr);
+ BL::SpaceView3D v3d(v3dptr);
+
+ PointerRNA rv3dptr;
+ RNA_id_pointer_create((ID*)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)
+{
+ Py_ssize_t pysession;
+
+ if(!PyArg_ParseTuple(args, "n", &pysession))
+ return NULL;
+
+ delete (BlenderSession*)pysession;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *render_func(PyObject *self, PyObject *args)
+{
+ Py_ssize_t pysession;
+
+ if(!PyArg_ParseTuple(args, "n", &pysession))
+ return NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+
+ BlenderSession *session = (BlenderSession*)pysession;
+ session->render();
+
+ Py_END_ALLOW_THREADS
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *draw_func(PyObject *self, PyObject *args)
+{
+ Py_ssize_t pysession, pyv3d, pyrv3d;
+
+ if(!PyArg_ParseTuple(args, "nnn", &pysession, &pyv3d, &pyrv3d))
+ return NULL;
+
+ BlenderSession *session = (BlenderSession*)pysession;
+
+ bool draw_text = false;
+
+ if(pyrv3d) {
+ /* 3d view drawing */
+ int viewport[4];
+ glGetIntegerv(GL_VIEWPORT, viewport);
+
+ draw_text = session->draw(viewport[2], viewport[3]);
+ }
+ else {
+ /* image editor drawing */
+ draw_text = session->draw();
+ }
+
+ /* draw */
+ PyObject *ret = PyTuple_New(2);
+
+ if(!draw_text) {
+ PyTuple_SetItem(ret, 0, PyUnicode_FromString(""));
+ PyTuple_SetItem(ret, 1, PyUnicode_FromString(""));
+ }
+ else {
+ string status, substatus;
+
+ session->get_status(status, substatus);
+
+ PyTuple_SetItem(ret, 0, PyUnicode_FromString(status.c_str()));
+ PyTuple_SetItem(ret, 1, PyUnicode_FromString(substatus.c_str()));
+ }
+
+ return ret;
+}
+
+static PyObject *sync_func(PyObject *self, PyObject *args)
+{
+ Py_ssize_t pysession;
+
+ if(!PyArg_ParseTuple(args, "n", &pysession))
+ return NULL;
+
+ BlenderSession *session = (BlenderSession*)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},
+};
+
+static struct PyModuleDef module = {
+ PyModuleDef_HEAD_INIT,
+ "libcycles_blender",
+ "Blender RNA to render exporter",
+ -1,
+ methods
+};
+
+CCL_NAMESPACE_END
+
+PyMODINIT_FUNC PyInit_libcycles_blender()
+{
+ 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..8e8bba4e5b6
--- /dev/null
+++ b/intern/cycles/blender/blender_session.cpp
@@ -0,0 +1,280 @@
+/*
+ * 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 "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);
+ SessionParams session_params = BlenderSync::get_session_params(b_scene, background);
+
+ /* 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));
+
+ /* start rendering */
+ session->reset(width, height);
+ 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 */
+ DisplayBuffer *display = session->display;
+ Device *device = session->device;
+
+ if(!display->rgba.device_pointer)
+ return;
+
+ /* todo: get float buffer */
+ device->pixels_copy_from(display->rgba, 0, width, height);
+ uchar4 *rgba = (uchar4*)display->rgba.data_pointer;
+
+ vector<float4> buffer(width*height);
+ float fac = 1.0f/255.0f;
+
+ /* normalize */
+ for(int i = width*height - 1; i >= 0; i--) {
+ uchar4 f = rgba[i];
+ float r = color_srgb_to_scene_linear(f.x*fac);
+ float g = color_srgb_to_scene_linear(f.y*fac);
+ float b = color_srgb_to_scene_linear(f.z*fac);
+ buffer[i] = make_float4(r, g, b, 1.0f);
+ }
+
+ 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);
+
+ rna_RenderLayer_rect_set(&rr.layers.begin()->ptr, (float*)&buffer[0]);
+
+ RE_engine_end_result((RenderEngine*)b_engine.ptr.data, rrp);
+}
+
+void BlenderSession::synchronize()
+{
+ /* on session/scene parameter changes, we recreate session entirely */
+ SceneParams scene_params = BlenderSync::get_scene_params(b_scene);
+ 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;
+ }
+
+ /* 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);
+
+ /* reset if needed */
+ if(scene->need_reset())
+ session->reset(width, height);
+
+ /* unlock */
+ session->scene->mutex.unlock();
+}
+
+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)
+ session->reset(width, height);
+ }
+
+ /* draw */
+ return !session->draw(width, height);
+}
+
+bool BlenderSession::draw()
+{
+ return !session->draw(width, height);
+}
+
+void BlenderSession::get_status(string& status, string& substatus)
+{
+ session->progress.get_status(status, substatus);
+}
+
+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) {
+ /* offline render, set stats and redraw if timeout passed */
+ string status, substatus;
+ get_status(status, substatus);
+
+ if(substatus.size() > 0)
+ status += " | " + substatus;
+
+ RE_engine_update_stats((RenderEngine*)b_engine.ptr.data, "", status.c_str());
+
+ 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..8bc5d3dfab9
--- /dev/null
+++ b/intern/cycles/blender/blender_session.h
@@ -0,0 +1,77 @@
+/*
+ * 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();
+ bool draw(int w, int h);
+ void tag_redraw();
+ void tag_update();
+ void get_status(string& status, string& substatus);
+ void test_cancel();
+
+ 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;
+
+ 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..c6dbfc630ab
--- /dev/null
+++ b/intern/cycles/blender/blender_shader.cpp
@@ -0,0 +1,629 @@
+/*
+ * 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::Node b_node, const string& name)
+{
+ BL::Node::inputs_iterator b_in;
+
+ for(b_in = b_node.inputs.begin(); b_in != b_node.inputs.end(); ++b_in) {
+ if(b_in->name() == name) {
+ if(b_group_node) {
+
+ BL::NodeTree b_ntree = BL::NodeGroup(*b_group_node).node_tree();
+ BL::NodeTree::links_iterator b_link;
+
+ for(b_link = b_ntree.links.begin(); 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_gin = b_group_node->inputs.begin(); 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;
+ }
+ }
+
+ assert(0);
+
+ return *b_in;
+}
+
+static BL::NodeSocket get_node_output(BL::Node b_node, const string& name)
+{
+ BL::Node::outputs_iterator b_out;
+
+ for(b_out = b_node.outputs.begin(); 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::RGBANodeSocket 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::ValueNodeSocket sock(get_node_output(b_node, name));
+ return sock.default_value()[0];
+}
+
+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();
+ mapping->translation = get_float3(b_mapping_node.location());
+ mapping->rotation = get_float3(b_mapping_node.rotation());
+ mapping->scale = get_float3(b_mapping_node.scale());
+ 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_ADD_CLOSURE: {
+ node = new AddClosureNode();
+ break;
+ }
+ case BL::ShaderNode::type_MIX_CLOSURE: {
+ node = new MixClosureNode();
+ break;
+ }
+ case BL::ShaderNode::type_ATTRIBUTE: {
+ AttributeNode *attr = new AttributeNode();
+ attr->attribute = "";
+ node = attr;
+ break;
+ }
+ case BL::ShaderNode::type_BACKGROUND: {
+ node = new BackgroundNode();
+ 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_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());
+ node = image;
+ break;
+ }
+ case BL::ShaderNode::type_TEX_ENVIRONMENT: {
+ BL::ShaderNodeTexEnvironment b_environment_node(b_node);
+ BL::Image b_image(b_environment_node.image());
+ EnvironmentTextureNode *env = new EnvironmentTextureNode();
+ if(b_image)
+ env->filename = blender_absolute_path(b_data, b_image, b_image.filepath());
+ node = env;
+ break;
+ }
+ case BL::ShaderNode::type_TEX_NOISE: {
+ node = new NoiseTextureNode();
+ break;
+ }
+ case BL::ShaderNode::type_TEX_BLEND: {
+ BL::ShaderNodeTexBlend b_blend_node(b_node);
+ BlendTextureNode *blend = new BlendTextureNode();
+ blend->progression = BlendTextureNode::progression_enum[(int)b_blend_node.progression()];
+ blend->axis = BlendTextureNode::axis_enum[(int)b_blend_node.axis()];
+ node = blend;
+ break;
+ }
+ case BL::ShaderNode::type_TEX_VORONOI: {
+ BL::ShaderNodeTexVoronoi b_voronoi_node(b_node);
+ VoronoiTextureNode *voronoi = new VoronoiTextureNode();
+ voronoi->distance_metric = VoronoiTextureNode::distance_metric_enum[(int)b_voronoi_node.distance_metric()];
+ voronoi->coloring = VoronoiTextureNode::coloring_enum[(int)b_voronoi_node.coloring()];
+ 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();
+ node = magic;
+ break;
+ }
+ case BL::ShaderNode::type_TEX_MARBLE: {
+ BL::ShaderNodeTexMarble b_marble_node(b_node);
+ MarbleTextureNode *marble = new MarbleTextureNode();
+ marble->depth = b_marble_node.turbulence_depth();
+ marble->basis = MarbleTextureNode::basis_enum[(int)b_marble_node.noise_basis()];
+ marble->type = MarbleTextureNode::type_enum[(int)b_marble_node.marble_type()];
+ marble->wave = MarbleTextureNode::wave_enum[(int)b_marble_node.wave_type()];
+ marble->hard = b_marble_node.noise_type() == BL::ShaderNodeTexMarble::noise_type_HARD;
+ node = marble;
+ break;
+ }
+ case BL::ShaderNode::type_TEX_CLOUDS: {
+ BL::ShaderNodeTexClouds b_clouds_node(b_node);
+ CloudsTextureNode *clouds = new CloudsTextureNode();
+ clouds->depth = b_clouds_node.turbulence_depth();
+ clouds->basis = CloudsTextureNode::basis_enum[(int)b_clouds_node.noise_basis()];
+ clouds->hard = b_clouds_node.noise_type() == BL::ShaderNodeTexClouds::noise_type_HARD;
+ node = clouds;
+ break;
+ }
+ case BL::ShaderNode::type_TEX_WOOD: {
+ BL::ShaderNodeTexWood b_wood_node(b_node);
+ WoodTextureNode *wood = new WoodTextureNode();
+ wood->type = WoodTextureNode::type_enum[(int)b_wood_node.wood_type()];
+ wood->basis = WoodTextureNode::basis_enum[(int)b_wood_node.noise_basis()];
+ wood->hard = b_wood_node.noise_type() == BL::ShaderNodeTexWood::noise_type_HARD;
+ wood->wave = WoodTextureNode::wave_enum[(int)b_wood_node.wave_type()];
+ node = wood;
+ 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()];
+ musgrave->basis = MusgraveTextureNode::basis_enum[(int)b_musgrave_node.noise_basis()];
+ node = musgrave;
+ break;
+ }
+ case BL::ShaderNode::type_TEX_STUCCI: {
+ BL::ShaderNodeTexStucci b_stucci_node(b_node);
+ StucciTextureNode *stucci = new StucciTextureNode();
+ stucci->type = StucciTextureNode::type_enum[(int)b_stucci_node.stucci_type()];
+ stucci->basis = StucciTextureNode::basis_enum[(int)b_stucci_node.noise_basis()];
+ stucci->hard = b_stucci_node.noise_type() == BL::ShaderNodeTexStucci::noise_type_HARD;
+ node = stucci;
+ break;
+ }
+ case BL::ShaderNode::type_TEX_DISTORTED_NOISE: {
+ BL::ShaderNodeTexDistortedNoise b_distnoise_node(b_node);
+ DistortedNoiseTextureNode *distnoise = new DistortedNoiseTextureNode();
+ distnoise->basis = DistortedNoiseTextureNode::basis_enum[(int)b_distnoise_node.noise_basis()];
+ distnoise->distortion_basis = DistortedNoiseTextureNode::basis_enum[(int)b_distnoise_node.noise_distortion()];
+ node = distnoise;
+ 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();
+ node = sky;
+ break;
+ }
+ }
+
+ if(node != graph->output())
+ graph->add(node);
+
+ return node;
+}
+
+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_node = b_ntree.nodes.begin(); 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_input = b_node->inputs.begin(); b_input != b_node->inputs.end(); ++b_input) {
+ ShaderInput *input = node->input(b_input->name().c_str());
+ BL::NodeSocket sock(get_node_input(b_group_node, *b_node, b_input->name()));
+
+ assert(input);
+
+ /* copy values for non linked inputs */
+ switch(input->type) {
+ case SHADER_SOCKET_FLOAT: {
+ BL::ValueNodeSocket value_sock(sock);
+ input->set(value_sock.default_value()[0]);
+ break;
+ }
+ case SHADER_SOCKET_COLOR: {
+ BL::RGBANodeSocket 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::VectorNodeSocket 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_link = b_ntree.links.begin(); 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] =
+ SocketPair(node_map[b_to_node.ptr.data], b_to_sock.name());
+
+ continue;
+ }
+ else if(!b_to_node) {
+ sockets_map[b_to_sock.ptr.data] =
+ SocketPair(node_map[b_from_node.ptr.data], b_from_sock.name());
+
+ continue;
+ }
+ }
+
+ ShaderNode *from_node, *to_node;
+ string from_name, to_name;
+
+ /* from sock */
+ if(b_from_node.is_a(&RNA_NodeGroup)) {
+ /* group node */
+ BL::NodeSocket group_sock = b_from_sock.group_socket();
+ SocketPair& pair = node_groups[b_from_node.ptr.data][group_sock.ptr.data];
+
+ from_node = pair.first;
+ from_name = pair.second;
+ }
+ else {
+ /* regular node */
+ from_node = node_map[b_from_node.ptr.data];
+ from_name = b_from_sock.name();
+ }
+
+ /* to sock */
+ if(b_to_node.is_a(&RNA_NodeGroup)) {
+ /* group node */
+ BL::NodeSocket group_sock = b_to_sock.group_socket();
+ SocketPair& pair = node_groups[b_to_node.ptr.data][group_sock.ptr.data];
+
+ to_node = pair.first;
+ to_name = pair.second;
+ }
+ else {
+ /* regular node */
+ to_node = node_map[b_to_node.ptr.data];
+ to_name = b_to_sock.name();
+ }
+
+ graph->connect(from_node->output(from_name.c_str()), to_node->input(to_name.c_str()));
+ }
+}
+
+/* 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_mat = b_data.materials.begin(); 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();
+
+ /* create nodes */
+ if(b_mat && b_mat->node_tree()) {
+ shader->name = b_mat->name();
+
+ PtrSockMap sock_to_node;
+ BL::ShaderNodeTree b_ntree(b_mat->node_tree());
+
+ add_nodes(b_data, graph, b_ntree, NULL, sock_to_node);
+ }
+
+ 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.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);
+ }
+
+ shader->set_graph(graph);
+ shader->tag_update(scene);
+ }
+
+ 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_lamp = b_data.lamps.begin(); 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 && b_lamp->node_tree()) {
+ shader->name = b_lamp->name();
+
+ PtrSockMap sock_to_node;
+ BL::ShaderNodeTree b_ntree(b_lamp->node_tree());
+
+ add_nodes(b_data, graph, b_ntree, NULL, sock_to_node);
+ }
+
+ 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..1bf85f13fca
--- /dev/null
+++ b/intern/cycles/blender/blender_sync.cpp
@@ -0,0 +1,210 @@
+/*
+ * 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 "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()
+{
+ BL::BlendData::materials_iterator b_mat;
+
+ for(b_mat = b_data.materials.begin(); b_mat != b_data.materials.end(); ++b_mat)
+ if(b_mat->recalc())
+ shader_map.set_recalc(*b_mat);
+
+ BL::BlendData::lamps_iterator b_lamp;
+
+ for(b_lamp = b_data.lamps.begin(); b_lamp != b_data.lamps.end(); ++b_lamp)
+ if(b_lamp->recalc())
+ shader_map.set_recalc(*b_lamp);
+
+ BL::BlendData::objects_iterator b_ob;
+
+ for(b_ob = b_data.objects.begin(); b_ob != b_data.objects.end(); ++b_ob) {
+ if(b_ob->recalc()) {
+ object_map.set_recalc(*b_ob);
+ light_map.set_recalc(*b_ob);
+ }
+ if(object_is_mesh(*b_ob)) {
+ if(b_ob->recalc_data() || b_ob->data().recalc()) {
+ BL::ID key = object_is_modified(*b_ob)? *b_ob: b_ob->data();
+ mesh_map.set_recalc(key);
+ }
+ }
+ }
+
+ BL::BlendData::meshes_iterator b_mesh;
+
+ for(b_mesh = b_data.meshes.begin(); b_mesh != b_data.meshes.end(); ++b_mesh)
+ if(b_mesh->recalc())
+ mesh_map.set_recalc(*b_mesh);
+
+ BL::BlendData::worlds_iterator b_world;
+
+ for(b_world = b_data.worlds.begin(); b_world != b_data.worlds.end(); ++b_world)
+ if(world_map == b_world->ptr.data && b_world->recalc())
+ world_recalc = true;
+
+ bool recalc =
+ shader_map.has_recalc() ||
+ object_map.has_recalc() ||
+ light_map.has_recalc() ||
+ mesh_map.has_recalc() ||
+ world_recalc;
+
+ return recalc;
+}
+
+void BlenderSync::sync_data(BL::SpaceView3D b_v3d)
+{
+ sync_integrator();
+ sync_film();
+ 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->minbounce = get_int(cscene, "min_bounces");
+ integrator->maxbounce = get_int(cscene, "max_bounces");
+ integrator->no_caustics = get_boolean(cscene, "no_caustics");
+ integrator->blur_caustics = get_float(cscene, "blur_caustics");
+
+ 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, "exposure");
+ film->response = get_enum_identifier(cscene, "response_curve");
+
+ if(film->modified(prevfilm))
+ film->tag_update(scene);
+}
+
+/* Scene Parameters */
+
+SceneParams BlenderSync::get_scene_params(BL::Scene b_scene)
+{
+ 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;
+
+ 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 */
+
+SessionParams BlenderSync::get_session_params(BL::Scene b_scene, bool background)
+{
+ SessionParams params;
+ PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
+
+ /* device type */
+ DeviceType dtype = (RNA_enum_get(&cscene, "device") == 1)? DEVICE_CUDA: DEVICE_CPU;
+
+ params.device_type = DEVICE_CPU;
+ vector<DeviceType> types = Device::available_types();
+
+ foreach(DeviceType dt, types)
+ if(dt == dtype)
+ params.device_type = dtype;
+
+ /* other parameters */
+ params.background = background;
+ params.passes = (background)? get_int(cscene, "passes"): INT_MAX;
+ 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..f1fce37bfaf
--- /dev/null
+++ b/intern/cycles/blender/blender_sync.h
@@ -0,0 +1,105 @@
+/*
+ * 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);
+ static SessionParams get_session_params(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_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);
+ void sync_light(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<void*, Light> light_map;
+ void *world_map;
+ bool world_recalc;
+
+ Scene *scene;
+ bool preview;
+};
+
+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..9da1c1dfe05
--- /dev/null
+++ b/intern/cycles/blender/blender_util.h
@@ -0,0 +1,326 @@
+/*
+ * 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 engine_tag_redraw(void *engine);
+void engine_tag_update(void *engine);
+int rna_Object_is_modified(void *ob, void *scene, int settings);
+
+}
+
+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);
+ 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));
+}
+
+/* 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);
+}
+
+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.ptr.id.data);
+ }
+
+ bool sync(T **r_data, BL::ID id, 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());
+
+ 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__ */
+