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