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:
authorTon Roosendaal <ton@blender.org>2011-04-27 15:58:34 +0400
committerTon Roosendaal <ton@blender.org>2011-04-27 15:58:34 +0400
commitda376e0237517543aa21740ee2363234ee1c20ae (patch)
tree014a513ed8d0eccc5e54fef42347781e85bae56a /intern/cycles/blender
parent693780074388111e7b9ef1c3825e462f398dc6c4 (diff)
Cycles render engine, initial commit. This is the engine itself, blender modifications and build instructions will follow later.
Cycles uses code from some great open source projects, many thanks them: * BVH building and traversal code from NVidia's "Understanding the Efficiency of Ray Traversal on GPUs": http://code.google.com/p/understanding-the-efficiency-of-ray-traversal-on-gpus/ * Open Shading Language for a large part of the shading system: http://code.google.com/p/openshadinglanguage/ * Blender for procedural textures and a few other nodes. * Approximate Catmull Clark subdivision from NVidia Mesh tools: http://code.google.com/p/nvidia-mesh-tools/ * Sobol direction vectors from: http://web.maths.unsw.edu.au/~fkuo/sobol/ * Film response functions from: http://www.cs.columbia.edu/CAVE/software/softlib/dorf.php
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__ */
+