diff options
Diffstat (limited to 'intern/cycles/blender')
-rw-r--r-- | intern/cycles/blender/CMakeLists.txt | 72 | ||||
-rw-r--r-- | intern/cycles/blender/addon/__init__.py | 73 | ||||
-rw-r--r-- | intern/cycles/blender/addon/engine.py | 107 | ||||
-rw-r--r-- | intern/cycles/blender/addon/enums.py | 113 | ||||
-rw-r--r-- | intern/cycles/blender/addon/properties.py | 120 | ||||
-rw-r--r-- | intern/cycles/blender/addon/ui.py | 388 | ||||
-rw-r--r-- | intern/cycles/blender/addon/xml.py | 99 | ||||
-rw-r--r-- | intern/cycles/blender/blender_camera.cpp | 249 | ||||
-rw-r--r-- | intern/cycles/blender/blender_mesh.cpp | 300 | ||||
-rw-r--r-- | intern/cycles/blender/blender_object.cpp | 183 | ||||
-rw-r--r-- | intern/cycles/blender/blender_python.cpp | 230 | ||||
-rw-r--r-- | intern/cycles/blender/blender_session.cpp | 280 | ||||
-rw-r--r-- | intern/cycles/blender/blender_session.h | 77 | ||||
-rw-r--r-- | intern/cycles/blender/blender_shader.cpp | 629 | ||||
-rw-r--r-- | intern/cycles/blender/blender_sync.cpp | 210 | ||||
-rw-r--r-- | intern/cycles/blender/blender_sync.h | 105 | ||||
-rw-r--r-- | intern/cycles/blender/blender_util.h | 326 |
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, ®ionptr); + 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__ */ + |