Cycles Merge
authorBrecht Van Lommel <brechtvanlommel@pandora.be>
Tue, 8 Nov 2011 17:00:40 +0000 (17:00 +0000)
committerBrecht Van Lommel <brechtvanlommel@pandora.be>
Tue, 8 Nov 2011 17:00:40 +0000 (17:00 +0000)
Documentation:
http://wiki.blender.org/index.php/Dev:2.6/Source/Render/Cycles
http://wiki.blender.org/index.php/Doc:2.6/Manual/Render/Cycles

Cycles is available is an extra render engine from the top header. It's not
feature complete, consider this as a first preview release. Known bugs:
http://wiki.blender.org/index.php/Dev:2.6/Source/Render/Cycles/KnownIssues

Building currently only works with CMake, SCons support is being worked on
and should be available soon.

Also missing still is precompiled OpenImageIO and Boost for Linux, these will
be added later in lib/linux*, if you do not have these installed on your
system, Cycles will simply not be available.

311 files changed:
CMakeLists.txt
build_files/cmake/Modules/FindOpenImageIO.cmake [new file with mode: 0644]
build_files/cmake/config/blender_lite.cmake
intern/CMakeLists.txt
intern/cycles/CMakeLists.txt [new file with mode: 0644]
intern/cycles/app/CMakeLists.txt [new file with mode: 0644]
intern/cycles/app/cycles_server.cpp [new file with mode: 0644]
intern/cycles/app/cycles_test.cpp [new file with mode: 0644]
intern/cycles/app/cycles_xml.cpp [new file with mode: 0644]
intern/cycles/app/cycles_xml.h [new file with mode: 0644]
intern/cycles/blender/CMakeLists.txt [new file with mode: 0644]
intern/cycles/blender/addon/__init__.py [new file with mode: 0644]
intern/cycles/blender/addon/engine.py [new file with mode: 0644]
intern/cycles/blender/addon/enums.py [new file with mode: 0644]
intern/cycles/blender/addon/presets.py [new file with mode: 0644]
intern/cycles/blender/addon/properties.py [new file with mode: 0644]
intern/cycles/blender/addon/ui.py [new file with mode: 0644]
intern/cycles/blender/addon/xml.py [new file with mode: 0644]
intern/cycles/blender/blender_camera.cpp [new file with mode: 0644]
intern/cycles/blender/blender_mesh.cpp [new file with mode: 0644]
intern/cycles/blender/blender_object.cpp [new file with mode: 0644]
intern/cycles/blender/blender_python.cpp [new file with mode: 0644]
intern/cycles/blender/blender_session.cpp [new file with mode: 0644]
intern/cycles/blender/blender_session.h [new file with mode: 0644]
intern/cycles/blender/blender_shader.cpp [new file with mode: 0644]
intern/cycles/blender/blender_sync.cpp [new file with mode: 0644]
intern/cycles/blender/blender_sync.h [new file with mode: 0644]
intern/cycles/blender/blender_util.h [new file with mode: 0644]
intern/cycles/bvh/CMakeLists.txt [new file with mode: 0644]
intern/cycles/bvh/bvh.cpp [new file with mode: 0644]
intern/cycles/bvh/bvh.h [new file with mode: 0644]
intern/cycles/bvh/bvh_build.cpp [new file with mode: 0644]
intern/cycles/bvh/bvh_build.h [new file with mode: 0644]
intern/cycles/bvh/bvh_node.cpp [new file with mode: 0644]
intern/cycles/bvh/bvh_node.h [new file with mode: 0644]
intern/cycles/bvh/bvh_params.h [new file with mode: 0644]
intern/cycles/bvh/bvh_sort.cpp [new file with mode: 0644]
intern/cycles/bvh/bvh_sort.h [new file with mode: 0644]
intern/cycles/cmake/external_libs.cmake [new file with mode: 0644]
intern/cycles/device/CMakeLists.txt [new file with mode: 0644]
intern/cycles/device/device.cpp [new file with mode: 0644]
intern/cycles/device/device.h [new file with mode: 0644]
intern/cycles/device/device_cpu.cpp [new file with mode: 0644]
intern/cycles/device/device_cuda.cpp [new file with mode: 0644]
intern/cycles/device/device_intern.h [new file with mode: 0644]
intern/cycles/device/device_memory.h [new file with mode: 0644]
intern/cycles/device/device_multi.cpp [new file with mode: 0644]
intern/cycles/device/device_network.cpp [new file with mode: 0644]
intern/cycles/device/device_network.h [new file with mode: 0644]
intern/cycles/device/device_opencl.cpp [new file with mode: 0644]
intern/cycles/doc/CMakeLists.txt [new file with mode: 0644]
intern/cycles/doc/license/Apache_2.0.txt [new file with mode: 0644]
intern/cycles/doc/license/Blender.txt [new file with mode: 0644]
intern/cycles/doc/license/Boost.txt [new file with mode: 0644]
intern/cycles/doc/license/CMakeLists.txt [new file with mode: 0644]
intern/cycles/doc/license/GPL.txt [new file with mode: 0644]
intern/cycles/doc/license/ILM.txt [new file with mode: 0644]
intern/cycles/doc/license/NVidia.txt [new file with mode: 0644]
intern/cycles/doc/license/OSL.txt [new file with mode: 0644]
intern/cycles/doc/license/Sobol.txt [new file with mode: 0644]
intern/cycles/doc/license/readme.txt [new file with mode: 0644]
intern/cycles/kernel/CMakeLists.txt [new file with mode: 0644]
intern/cycles/kernel/kernel.cl [new file with mode: 0644]
intern/cycles/kernel/kernel.cpp [new file with mode: 0644]
intern/cycles/kernel/kernel.cu [new file with mode: 0644]
intern/cycles/kernel/kernel.h [new file with mode: 0644]
intern/cycles/kernel/kernel_bvh.h [new file with mode: 0644]
intern/cycles/kernel/kernel_camera.h [new file with mode: 0644]
intern/cycles/kernel/kernel_compat_cpu.h [new file with mode: 0644]
intern/cycles/kernel/kernel_compat_cuda.h [new file with mode: 0644]
intern/cycles/kernel/kernel_compat_opencl.h [new file with mode: 0644]
intern/cycles/kernel/kernel_differential.h [new file with mode: 0644]
intern/cycles/kernel/kernel_displace.h [new file with mode: 0644]
intern/cycles/kernel/kernel_emission.h [new file with mode: 0644]
intern/cycles/kernel/kernel_film.h [new file with mode: 0644]
intern/cycles/kernel/kernel_globals.h [new file with mode: 0644]
intern/cycles/kernel/kernel_light.h [new file with mode: 0644]
intern/cycles/kernel/kernel_math.h [new file with mode: 0644]
intern/cycles/kernel/kernel_mbvh.h [new file with mode: 0644]
intern/cycles/kernel/kernel_montecarlo.h [new file with mode: 0644]
intern/cycles/kernel/kernel_object.h [new file with mode: 0644]
intern/cycles/kernel/kernel_path.h [new file with mode: 0644]
intern/cycles/kernel/kernel_qbvh.h [new file with mode: 0644]
intern/cycles/kernel/kernel_random.h [new file with mode: 0644]
intern/cycles/kernel/kernel_shader.h [new file with mode: 0644]
intern/cycles/kernel/kernel_textures.h [new file with mode: 0644]
intern/cycles/kernel/kernel_triangle.h [new file with mode: 0644]
intern/cycles/kernel/kernel_types.h [new file with mode: 0644]
intern/cycles/kernel/osl/CMakeLists.txt [new file with mode: 0644]
intern/cycles/kernel/osl/background.cpp [new file with mode: 0644]
intern/cycles/kernel/osl/bsdf_ashikhmin_velvet.cpp [new file with mode: 0644]
intern/cycles/kernel/osl/bsdf_diffuse.cpp [new file with mode: 0644]
intern/cycles/kernel/osl/bsdf_microfacet.cpp [new file with mode: 0644]
intern/cycles/kernel/osl/bsdf_reflection.cpp [new file with mode: 0644]
intern/cycles/kernel/osl/bsdf_refraction.cpp [new file with mode: 0644]
intern/cycles/kernel/osl/bsdf_transparent.cpp [new file with mode: 0644]
intern/cycles/kernel/osl/bsdf_ward.cpp [new file with mode: 0644]
intern/cycles/kernel/osl/bsdf_westin.cpp [new file with mode: 0644]
intern/cycles/kernel/osl/bssrdf.cpp [new file with mode: 0644]
intern/cycles/kernel/osl/debug.cpp [new file with mode: 0644]
intern/cycles/kernel/osl/emissive.cpp [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/CMakeLists.txt [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_add_closure.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_attribute.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_background.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_blend_texture.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_blend_weight.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_bump.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_clouds_texture.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_color.h [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_convert_from_color.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_convert_from_float.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_convert_from_normal.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_convert_from_point.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_convert_from_vector.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_diffuse_bsdf.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_distorted_noise_texture.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_emission.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_environment_texture.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_fresnel.h [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_fresnel.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_geometry.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_glass_bsdf.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_glossy_bsdf.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_holdout.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_image_texture.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_light_path.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_magic_texture.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_mapping.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_marble_texture.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_math.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_mix.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_mix_closure.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_musgrave_texture.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_noise_texture.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_output_displacement.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_output_surface.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_output_volume.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_sky_texture.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_stucci_texture.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_texture.h [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_texture_coordinate.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_translucent_bsdf.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_transparent_bsdf.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_value.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_vector_math.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_velvet_bsdf.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_voronoi_texture.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_ward_bsdf.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/node_wood_texture.osl [new file with mode: 0644]
intern/cycles/kernel/osl/nodes/stdosl.h [new file with mode: 0644]
intern/cycles/kernel/osl/osl_closures.cpp [new file with mode: 0644]
intern/cycles/kernel/osl/osl_closures.h [new file with mode: 0644]
intern/cycles/kernel/osl/osl_globals.h [new file with mode: 0644]
intern/cycles/kernel/osl/osl_services.cpp [new file with mode: 0644]
intern/cycles/kernel/osl/osl_services.h [new file with mode: 0644]
intern/cycles/kernel/osl/osl_shader.cpp [new file with mode: 0644]
intern/cycles/kernel/osl/osl_shader.h [new file with mode: 0644]
intern/cycles/kernel/osl/vol_subsurface.cpp [new file with mode: 0644]
intern/cycles/kernel/svm/bsdf.h [new file with mode: 0644]
intern/cycles/kernel/svm/bsdf_ashikhmin_velvet.h [new file with mode: 0644]
intern/cycles/kernel/svm/bsdf_diffuse.h [new file with mode: 0644]
intern/cycles/kernel/svm/bsdf_microfacet.h [new file with mode: 0644]
intern/cycles/kernel/svm/bsdf_reflection.h [new file with mode: 0644]
intern/cycles/kernel/svm/bsdf_refraction.h [new file with mode: 0644]
intern/cycles/kernel/svm/bsdf_transparent.h [new file with mode: 0644]
intern/cycles/kernel/svm/bsdf_ward.h [new file with mode: 0644]
intern/cycles/kernel/svm/bsdf_westin.h [new file with mode: 0644]
intern/cycles/kernel/svm/emissive.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_attribute.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_bsdf.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_closure.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_convert.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_displace.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_fresnel.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_geometry.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_gradient.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_image.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_light_path.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_magic.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_mapping.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_math.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_mix.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_musgrave.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_noise.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_noisetex.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_sky.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_tex_coord.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_texture.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_types.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_value.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_voronoi.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_wave.h [new file with mode: 0644]
intern/cycles/kernel/svm/volume.h [new file with mode: 0644]
intern/cycles/render/CMakeLists.txt [new file with mode: 0644]
intern/cycles/render/attribute.cpp [new file with mode: 0644]
intern/cycles/render/attribute.h [new file with mode: 0644]
intern/cycles/render/background.cpp [new file with mode: 0644]
intern/cycles/render/background.h [new file with mode: 0644]
intern/cycles/render/buffers.cpp [new file with mode: 0644]
intern/cycles/render/buffers.h [new file with mode: 0644]
intern/cycles/render/camera.cpp [new file with mode: 0644]
intern/cycles/render/camera.h [new file with mode: 0644]
intern/cycles/render/film.cpp [new file with mode: 0644]
intern/cycles/render/film.h [new file with mode: 0644]
intern/cycles/render/filter.cpp [new file with mode: 0644]
intern/cycles/render/filter.h [new file with mode: 0644]
intern/cycles/render/graph.cpp [new file with mode: 0644]
intern/cycles/render/graph.h [new file with mode: 0644]
intern/cycles/render/image.cpp [new file with mode: 0644]
intern/cycles/render/image.h [new file with mode: 0644]
intern/cycles/render/integrator.cpp [new file with mode: 0644]
intern/cycles/render/integrator.h [new file with mode: 0644]
intern/cycles/render/light.cpp [new file with mode: 0644]
intern/cycles/render/light.h [new file with mode: 0644]
intern/cycles/render/mesh.cpp [new file with mode: 0644]
intern/cycles/render/mesh.h [new file with mode: 0644]
intern/cycles/render/mesh_displace.cpp [new file with mode: 0644]
intern/cycles/render/nodes.cpp [new file with mode: 0644]
intern/cycles/render/nodes.h [new file with mode: 0644]
intern/cycles/render/object.cpp [new file with mode: 0644]
intern/cycles/render/object.h [new file with mode: 0644]
intern/cycles/render/osl.cpp [new file with mode: 0644]
intern/cycles/render/osl.h [new file with mode: 0644]
intern/cycles/render/scene.cpp [new file with mode: 0644]
intern/cycles/render/scene.h [new file with mode: 0644]
intern/cycles/render/session.cpp [new file with mode: 0644]
intern/cycles/render/session.h [new file with mode: 0644]
intern/cycles/render/shader.cpp [new file with mode: 0644]
intern/cycles/render/shader.h [new file with mode: 0644]
intern/cycles/render/sobol.cpp [new file with mode: 0644]
intern/cycles/render/sobol.h [new file with mode: 0644]
intern/cycles/render/svm.cpp [new file with mode: 0644]
intern/cycles/render/svm.h [new file with mode: 0644]
intern/cycles/render/tile.cpp [new file with mode: 0644]
intern/cycles/render/tile.h [new file with mode: 0644]
intern/cycles/subd/CMakeLists.txt [new file with mode: 0644]
intern/cycles/subd/subd_build.cpp [new file with mode: 0644]
intern/cycles/subd/subd_build.h [new file with mode: 0644]
intern/cycles/subd/subd_dice.cpp [new file with mode: 0644]
intern/cycles/subd/subd_dice.h [new file with mode: 0644]
intern/cycles/subd/subd_edge.h [new file with mode: 0644]
intern/cycles/subd/subd_face.h [new file with mode: 0644]
intern/cycles/subd/subd_mesh.cpp [new file with mode: 0644]
intern/cycles/subd/subd_mesh.h [new file with mode: 0644]
intern/cycles/subd/subd_patch.cpp [new file with mode: 0644]
intern/cycles/subd/subd_patch.h [new file with mode: 0644]
intern/cycles/subd/subd_ring.cpp [new file with mode: 0644]
intern/cycles/subd/subd_ring.h [new file with mode: 0644]
intern/cycles/subd/subd_split.cpp [new file with mode: 0644]
intern/cycles/subd/subd_split.h [new file with mode: 0644]
intern/cycles/subd/subd_stencil.cpp [new file with mode: 0644]
intern/cycles/subd/subd_stencil.h [new file with mode: 0644]
intern/cycles/subd/subd_vert.h [new file with mode: 0644]
intern/cycles/util/CMakeLists.txt [new file with mode: 0644]
intern/cycles/util/util_algorithm.h [new file with mode: 0644]
intern/cycles/util/util_args.h [new file with mode: 0644]
intern/cycles/util/util_boundbox.h [new file with mode: 0644]
intern/cycles/util/util_cache.cpp [new file with mode: 0644]
intern/cycles/util/util_cache.h [new file with mode: 0644]
intern/cycles/util/util_color.h [new file with mode: 0644]
intern/cycles/util/util_cuda.cpp [new file with mode: 0644]
intern/cycles/util/util_cuda.h [new file with mode: 0644]
intern/cycles/util/util_debug.h [new file with mode: 0644]
intern/cycles/util/util_dynlib.cpp [new file with mode: 0644]
intern/cycles/util/util_dynlib.h [new file with mode: 0644]
intern/cycles/util/util_foreach.h [new file with mode: 0644]
intern/cycles/util/util_function.h [new file with mode: 0644]
intern/cycles/util/util_hash.h [new file with mode: 0644]
intern/cycles/util/util_image.h [new file with mode: 0644]
intern/cycles/util/util_list.h [new file with mode: 0644]
intern/cycles/util/util_map.h [new file with mode: 0644]
intern/cycles/util/util_math.h [new file with mode: 0644]
intern/cycles/util/util_md5.cpp [new file with mode: 0644]
intern/cycles/util/util_md5.h [new file with mode: 0644]
intern/cycles/util/util_memarena.cpp [new file with mode: 0644]
intern/cycles/util/util_memarena.h [new file with mode: 0644]
intern/cycles/util/util_opencl.c [new file with mode: 0755]
intern/cycles/util/util_opencl.h [new file with mode: 0755]
intern/cycles/util/util_opengl.h [new file with mode: 0644]
intern/cycles/util/util_param.h [new file with mode: 0644]
intern/cycles/util/util_path.cpp [new file with mode: 0644]
intern/cycles/util/util_path.h [new file with mode: 0644]
intern/cycles/util/util_progress.h [new file with mode: 0644]
intern/cycles/util/util_set.h [new file with mode: 0644]
intern/cycles/util/util_string.cpp [new file with mode: 0644]
intern/cycles/util/util_string.h [new file with mode: 0644]
intern/cycles/util/util_system.cpp [new file with mode: 0644]
intern/cycles/util/util_system.h [new file with mode: 0644]
intern/cycles/util/util_thread.h [new file with mode: 0644]
intern/cycles/util/util_time.cpp [new file with mode: 0644]
intern/cycles/util/util_time.h [new file with mode: 0644]
intern/cycles/util/util_transform.cpp [new file with mode: 0644]
intern/cycles/util/util_transform.h [new file with mode: 0644]
intern/cycles/util/util_types.h [new file with mode: 0644]
intern/cycles/util/util_vector.h [new file with mode: 0644]
intern/cycles/util/util_view.cpp [new file with mode: 0644]
intern/cycles/util/util_view.h [new file with mode: 0644]
intern/cycles/util/util_xml.h [new file with mode: 0644]
release/scripts/presets/cycles/integrator/direct_light.py [new file with mode: 0644]
release/scripts/presets/cycles/integrator/full_global_illumination.py [new file with mode: 0644]
release/scripts/presets/cycles/integrator/limited_global_illumination.py [new file with mode: 0644]
source/blender/blenkernel/BKE_blender.h
source/blender/editors/interface/resources.c
source/blender/makesrna/intern/rna_nodetree.c
source/blender/makesrna/intern/rna_object_api.c
source/blender/makesrna/intern/rna_render.c
source/blender/python/intern/CMakeLists.txt
source/blender/python/intern/bpy_interface.c
source/creator/CMakeLists.txt

index 4ed7eab8b4ebca09938b24d0dfb57aa02cad32f4..17ea5cf249dd28aae75ae98b4b5a25f1b45b6a84 100644 (file)
@@ -213,6 +213,10 @@ if(UNIX AND NOT APPLE)
 endif()
 option(WITH_PYTHON_INSTALL       "Copy system python into the blender install folder" ON)
 
+# Cycles
+option(WITH_CYCLES                     "Enable Cycles Render Engine" ON)
+OPTION(WITH_CYCLES_TEST                "Build cycles test application" OFF)
+
 # disable for now, but plan to support on all platforms eventually
 option(WITH_MEM_JEMALLOC   "Enable malloc replacement (http://www.canonware.com/jemalloc)" OFF)
 mark_as_advanced(WITH_MEM_JEMALLOC)
@@ -284,6 +288,12 @@ if(WITH_PYTHON_MODULE)
        set(WITH_HEADLESS ON)
 endif()
 
+# auto enable openimageio and boost for cycles
+if(WITH_CYCLES)
+       set(WITH_OPENIMAGEIO ON)
+       set(WITH_BOOST ON)
+endif()
+
 TEST_SSE_SUPPORT(COMPILER_SSE_FLAG COMPILER_SSE2_FLAG)
 
 # don't store paths to libs for portable distrobution
@@ -497,7 +507,7 @@ if(UNIX AND NOT APPLE)
                if(NOT BOOST_CUSTOM)
                        set(BOOST_ROOT ${BOOST})
                        set(Boost_USE_MULTITHREADED ON)
-                       find_package(Boost 1.34 REQUIRED COMPONENTS filesystem regex system thread)
+                       find_package(Boost 1.34 COMPONENTS filesystem regex system thread)
                endif()
 
                set(BOOST_INCLUDE_DIR ${Boost_INCLUDE_DIRS})
@@ -510,7 +520,7 @@ if(UNIX AND NOT APPLE)
                set(OPENIMAGEIO "/usr" CACHE PATH "OpenImageIO Directory")
 
                set(OPENIMAGEIO_ROOT_DIR ${OPENIMAGEIO})
-               find_package(OpenImageIO REQUIRED)
+               find_package(OpenImageIO)
 
                set(OPENIMAGEIO_LIBRARIES ${OPENIMAGEIO_LIBRARIES} ${PNG_LIBRARIES} ${JPEG_LIBRARIES} ${TIFF_LIBRARY} ${OPENEXR_LIBRARIES} ${ZLIB_LIBRARIES} ${BOOST_LIBRARIES})
                set(OPENIMAGEIO_LIBPATH)  # TODO, remove and reference the absolute path everywhere
@@ -518,6 +528,8 @@ if(UNIX AND NOT APPLE)
 
                if(NOT OPENIMAGEIO_FOUND)
                        set(WITH_OPENIMAGEIO OFF)
+                       set(WITH_CYCLES OFF)
+                       message(STATUS "OpenImageIO not found, disabling WITH_CYCLES")
                endif()
        endif()
 
@@ -1480,6 +1492,7 @@ if(FIRST_RUN)
        info_cfg_option(WITH_FFTW3)
        info_cfg_option(WITH_INTERNATIONAL)
        info_cfg_option(WITH_INPUT_NDOF)
+       info_cfg_option(WITH_CYCLES)
 
        info_cfg_text("Compiler Options:")
        info_cfg_option(WITH_BUILDINFO)
diff --git a/build_files/cmake/Modules/FindOpenImageIO.cmake b/build_files/cmake/Modules/FindOpenImageIO.cmake
new file mode 100644 (file)
index 0000000..7512b93
--- /dev/null
@@ -0,0 +1,70 @@
+# - Find OpenImageIO library
+# Find the native OpenImageIO includes and library
+# This module defines
+#  OPENIMAGEIO_INCLUDE_DIRS, where to find openimageio.h, Set when
+#                            OPENIMAGEIO_INCLUDE_DIR is found.
+#  OPENIMAGEIO_LIBRARIES, libraries to link against to use OpenImageIO.
+#  OPENIMAGEIO_ROOT_DIR, The base directory to search for OpenImageIO.
+#                        This can also be an environment variable.
+#  OPENIMAGEIO_FOUND, If false, do not try to use OpenImageIO.
+#
+# also defined, but not for general use are
+#  OPENIMAGEIO_LIBRARY, where to find the OpenImageIO library.
+
+#=============================================================================
+# Copyright 2011 Blender Foundation.
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+
+# If OPENIMAGEIO_ROOT_DIR was defined in the environment, use it.
+IF(NOT OPENIMAGEIO_ROOT_DIR AND NOT $ENV{OPENIMAGEIO_ROOT_DIR} STREQUAL "")
+  SET(OPENIMAGEIO_ROOT_DIR $ENV{OPENIMAGEIO_ROOT_DIR})
+ENDIF()
+
+SET(_openimageio_SEARCH_DIRS
+  ${OPENIMAGEIO_ROOT_DIR}
+  /usr/local
+  /sw # Fink
+  /opt/local # DarwinPorts
+  /opt/csw # Blastwave
+)
+
+FIND_PATH(OPENIMAGEIO_INCLUDE_DIR
+  NAMES
+    OpenImageIO/imageio.h
+  HINTS
+    ${_openimageio_SEARCH_DIRS}
+  PATH_SUFFIXES
+    include
+)
+
+FIND_LIBRARY(OPENIMAGEIO_LIBRARY
+  NAMES
+    OpenImageIO
+  HINTS
+    ${_openimageio_SEARCH_DIRS}
+  PATH_SUFFIXES
+    lib64 lib
+  )
+
+# handle the QUIETLY and REQUIRED arguments and set OPENIMAGEIO_FOUND to TRUE if 
+# all listed variables are TRUE
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(OpenImageIO DEFAULT_MSG
+    OPENIMAGEIO_LIBRARY OPENIMAGEIO_INCLUDE_DIR)
+
+IF(OPENIMAGEIO_FOUND)
+  SET(OPENIMAGEIO_LIBRARIES ${OPENIMAGEIO_LIBRARY})
+  SET(OPENIMAGEIO_INCLUDE_DIRS ${OPENIMAGEIO_INCLUDE_DIR})
+ENDIF(OPENIMAGEIO_FOUND)
+
+MARK_AS_ADVANCED(
+  OPENIMAGEIO_INCLUDE_DIR
+  OPENIMAGEIO_LIBRARY
+)
index 0da28a943ebc3ed30fe71b16c0c274dbc7e89527..4f04960d5e347837a62756a621edc462c7b3f9fe 100644 (file)
@@ -12,6 +12,7 @@ set(WITH_BUILTIN_GLEW        OFF CACHE FORCE BOOL)
 set(WITH_BULLET              OFF CACHE FORCE BOOL)
 set(WITH_CODEC_FFMPEG        OFF CACHE FORCE BOOL)
 set(WITH_CODEC_SNDFILE       OFF CACHE FORCE BOOL)
+set(WITH_CYCLES              OFF CACHE FORCE BOOL)
 set(WITH_FFTW3               OFF CACHE FORCE BOOL)
 set(WITH_LIBMV               OFF CACHE FORCE BOOL)
 set(WITH_GAMEENGINE          OFF CACHE FORCE BOOL)
index 7a663ec9335dbdbf3d0b17fd093dc4cfcbe6fd57..0f7672595d75058c99bcdbae220a06b2ef144b6b 100644 (file)
@@ -57,3 +57,8 @@ endif()
 if(WITH_IK_ITASC)
        add_subdirectory(itasc)
 endif()
+
+if(WITH_CYCLES)
+       add_subdirectory(cycles)
+endif()
+
diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt
new file mode 100644 (file)
index 0000000..8bb99e4
--- /dev/null
@@ -0,0 +1,76 @@
+
+set(CYCLES_INSTALL_PATH "scripts/addons/cycles")
+SET(WITH_CYCLES_BLENDER ON)
+
+# External Libraries
+
+include(cmake/external_libs.cmake)
+
+# Build Flags
+
+set(GCC_WARNING_FLAGS "-Wall -Wextra -Wno-unused-parameter -Wno-long-long")
+set(GCC_OPTIM_FLAGS "-ffast-math -msse -msse2 -msse3 -mtune=native")
+
+if(APPLE)
+       set(CMAKE_CXX_FLAGS "${GCC_WARNING_FLAGS} ${GCC_OPTIM_FLAGS}")
+       set(RTTI_DISABLE_FLAGS "-fno-rtti -DBOOST_NO_RTTI -DBOOST_NO_TYPEID")
+endif(APPLE)
+
+if(WIN32)
+       if(MSVC)
+               set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Ox /Ot /arch:SSE2 -D_CRT_SECURE_NO_WARNINGS /EHsc /fp:fast")
+               set(RTTI_DISABLE_FLAGS "/GR- -DBOOST_NO_RTTI -DBOOST_NO_TYPEID")
+       elseif(CMAKE_COMPILER_IS_GNUCC)
+               set(CMAKE_CXX_FLAGS "${GCC_WARNING_FLAGS} ${GCC_OPTIM_FLAGS}")
+               set(RTTI_DISABLE_FLAGS "-fno-rtti -DBOOST_NO_RTTI -DBOOST_NO_TYPEID")
+       endif()
+endif(WIN32)
+
+if(UNIX AND NOT APPLE)
+       set(CMAKE_CXX_FLAGS "${GCC_WARNING_FLAGS} ${GCC_OPTIM_FLAGS}")
+       set(RTTI_DISABLE_FLAGS "-fno-rtti -DBOOST_NO_RTTI -DBOOST_NO_TYPEID")
+endif(UNIX AND NOT APPLE)
+
+# Definitions and Includes
+
+add_definitions(${BOOST_DEFINITIONS} ${OPENIMAGEIO_DEFINITIONS})
+
+add_definitions(-DCCL_NAMESPACE_BEGIN=namespace\ ccl\ {)
+add_definitions(-DCCL_NAMESPACE_END=})
+
+if(WITH_CYCLES_NETWORK)
+  add_definitions(-DWITH_NETWORK)
+endif()
+
+if(WITH_CYCLES_OSL)
+  add_definitions(-DWITH_OSL)
+endif()
+
+if(WITH_CYCLES_PARTIO)
+  add_definitions(-DWITH_PARTIO)
+endif()
+
+add_definitions(-DWITH_OPENCL)
+add_definitions(-DWITH_CUDA)
+add_definitions(-DWITH_MULTI)
+
+include_directories(
+       ${BOOST_INCLUDE_DIR}
+       ${OPENIMAGEIO_INCLUDE_DIRS}
+       ${OPENIMAGEIO_INCLUDE_DIRS}/OpenImageIO)
+
+# Subdirectories
+
+if(WITH_CYCLES_BLENDER)
+       add_subdirectory(blender)
+endif(WITH_CYCLES_BLENDER)
+
+add_subdirectory(app)
+add_subdirectory(bvh)
+add_subdirectory(device)
+add_subdirectory(doc)
+add_subdirectory(kernel)
+add_subdirectory(render)
+add_subdirectory(subd)
+add_subdirectory(util)
+
diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ef6a80f
--- /dev/null
@@ -0,0 +1,55 @@
+
+include_directories(
+       .
+       ../device
+       ../kernel
+       ../kernel/svm
+       ../bvh
+       ../util
+       ../render
+       ../subd)
+
+set(LIBRARIES
+       cycles_device
+       cycles_kernel
+       cycles_render
+       cycles_bvh
+       cycles_subd
+       cycles_util
+       ${BOOST_LIBRARIES}
+       ${OPENGL_LIBRARIES}
+       ${CYCLES_GLEW_LIBRARY}
+       ${OPENIMAGEIO_LIBRARIES})
+
+link_directories(${OPENIMAGEIO_LIBPATH} ${BOOST_LIBPATH})
+
+if(WITH_CYCLES_TEST)
+       list(APPEND LIBRARIES ${GLUT_LIBRARIES})
+endif()
+
+if(WITH_CYCLES_OSL)
+       list(APPEND LIBRARIES cycles_kernel_osl ${OSL_LIBRARIES})
+endif()
+
+if(WITH_CYCLES_PARTIO)
+       list(APPEND LIBRARIES ${PARTIO_LIBRARIES})
+endif()
+
+if(WITH_CYCLES_TEST)
+       add_executable(cycles_test cycles_test.cpp cycles_xml.cpp cycles_xml.h)
+       target_link_libraries(cycles_test ${LIBRARIES})
+
+       if(UNIX AND NOT APPLE)
+               set_target_properties(cycles_test PROPERTIES INSTALL_RPATH $ORIGIN/lib)
+       endif()
+endif()
+
+if(WITH_CYCLES_NETWORK)
+       add_executable(cycles_server cycles_server.cpp)
+       target_link_libraries(cycles_server ${LIBRARIES})
+
+       if(UNIX AND NOT APPLE)
+               set_target_properties(cycles_server PROPERTIES INSTALL_RPATH $ORIGIN/lib)
+       endif()
+endif()
+
diff --git a/intern/cycles/app/cycles_server.cpp b/intern/cycles/app/cycles_server.cpp
new file mode 100644 (file)
index 0000000..bcf4d3e
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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 <stdio.h>
+
+#include "device.h"
+
+#include "util_args.h"
+#include "util_foreach.h"
+#include "util_path.h"
+#include "util_string.h"
+
+using namespace ccl;
+
+int main(int argc, const char **argv)
+{
+       path_init();
+
+       /* device types */
+       string devices = "";
+       string devicename = "cpu";
+
+       vector<DeviceType> types = Device::available_types();
+
+       foreach(DeviceType type, types) {
+               if(devices != "")
+                       devices += ", ";
+
+               devices += Device::string_from_type(type);
+       }
+
+       /* parse options */
+       ArgParse ap;
+
+       ap.options ("Usage: cycles_server [options]",
+               "--device %s", &devicename, ("Devices to use: " + devices).c_str(),
+               NULL);
+
+       if(ap.parse(argc, argv) < 0) {
+               fprintf(stderr, "%s\n", ap.error_message().c_str());
+               ap.usage();
+               exit(EXIT_FAILURE);
+       }
+
+       DeviceType dtype = Device::type_from_string(devicename.c_str());
+
+       while(1) {
+               Device *device = Device::create(dtype);
+               printf("Cycles Server with device: %s\n", device->description().c_str());
+               device->server_run();
+               delete device;
+       }
+
+       return 0;
+}
+
diff --git a/intern/cycles/app/cycles_test.cpp b/intern/cycles/app/cycles_test.cpp
new file mode 100644 (file)
index 0000000..27e53de
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * 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 <stdio.h>
+
+#include "buffers.h"
+#include "camera.h"
+#include "device.h"
+#include "scene.h"
+#include "session.h"
+
+#include "util_args.h"
+#include "util_foreach.h"
+#include "util_function.h"
+#include "util_path.h"
+#include "util_progress.h"
+#include "util_string.h"
+#include "util_time.h"
+#include "util_view.h"
+
+#include "cycles_xml.h"
+
+CCL_NAMESPACE_BEGIN
+
+struct Options {
+       Session *session;
+       Scene *scene;
+       string filepath;
+       int width, height;
+       SceneParams scene_params;
+       SessionParams session_params;
+       bool quiet;
+} options;
+
+static void session_print(const string& str)
+{
+       /* print with carriage return to overwrite previous */
+       printf("\r%s", str.c_str());
+
+       /* add spaces to overwrite longer previous print */
+       static int maxlen = 0;
+       int len = str.size();
+       maxlen = max(len, maxlen);
+
+       for(int i = len; i < maxlen; i++)
+               printf(" ");
+
+       /* flush because we don't write an end of line */
+       fflush(stdout);
+}
+
+static void session_print_status()
+{
+       int sample;
+       double total_time, sample_time;
+       string status, substatus;
+
+       /* get status */
+       options.session->progress.get_sample(sample, total_time, sample_time);
+       options.session->progress.get_status(status, substatus);
+
+       if(substatus != "")
+               status += ": " + substatus;
+
+       /* print status */
+       status = string_printf("Sample %d   %s", sample, status.c_str());
+       session_print(status);
+}
+
+static void session_init()
+{
+       options.session = new Session(options.session_params);
+       options.session->reset(options.width, options.height, options.session_params.samples);
+       options.session->scene = options.scene;
+       
+       if(options.session_params.background && !options.quiet)
+               options.session->progress.set_update_callback(function_bind(&session_print_status));
+       else
+               options.session->progress.set_update_callback(function_bind(&view_redraw));
+
+       options.session->start();
+
+       options.scene = NULL;
+}
+
+static void scene_init()
+{
+       options.scene = new Scene(options.scene_params);
+       xml_read_file(options.scene, options.filepath.c_str());
+       options.width = options.scene->camera->width;
+       options.height = options.scene->camera->height;
+}
+
+static void session_exit()
+{
+       if(options.session) {
+               delete options.session;
+               options.session = NULL;
+       }
+       if(options.scene) {
+               delete options.scene;
+               options.scene = NULL;
+       }
+
+       if(options.session_params.background && !options.quiet) {
+               session_print("Finished Rendering.");
+               printf("\n");
+       }
+}
+
+static void display_info(Progress& progress)
+{
+       static double latency = 0.0;
+       static double last = 0;
+       double elapsed = time_dt();
+       string str;
+
+       latency = (elapsed - last);
+       last = elapsed;
+
+       int sample;
+       double total_time, sample_time;
+       string status, substatus;
+
+       progress.get_sample(sample, total_time, sample_time);
+       progress.get_status(status, substatus);
+
+       if(substatus != "")
+               status += ": " + substatus;
+
+       str = string_printf("latency: %.4f        sample: %d        total: %.4f        average: %.4f        %s",
+               latency, sample, total_time, sample_time, status.c_str());
+
+       view_display_info(str.c_str());
+}
+
+static void display()
+{
+       options.session->draw(options.width, options.height);
+
+       display_info(options.session->progress);
+}
+
+static void resize(int width, int height)
+{
+       options.width= width;
+       options.height= height;
+
+       if(options.session)
+               options.session->reset(options.width, options.height, options.session_params.samples);
+}
+
+void keyboard(unsigned char key)
+{
+       if(key == 'r')
+               options.session->reset(options.width, options.height, options.session_params.samples);
+       else if(key == 27) // escape
+               options.session->progress.set_cancel("Cancelled");
+}
+
+static int files_parse(int argc, const char *argv[])
+{
+       if(argc > 0)
+               options.filepath = argv[0];
+
+       return 0;
+}
+
+static void options_parse(int argc, const char **argv)
+{
+       options.width= 1024;
+       options.height= 512;
+       options.filepath = "";
+       options.session = NULL;
+       options.quiet = false;
+
+       /* devices */
+       string devices = "";
+       string devicename = "cpu";
+
+       vector<DeviceType> types = Device::available_types();
+
+       foreach(DeviceType type, types) {
+               if(devices != "")
+                       devices += ", ";
+
+               devices += Device::string_from_type(type);
+       }
+
+       /* shading system */
+       string ssname = "svm";
+       string shadingsystems = "Shading system to use: svm";
+
+#ifdef WITH_OSL
+       shadingsystems += ", osl"; 
+#endif
+
+       /* parse options */
+       ArgParse ap;
+       bool help = false;
+
+       ap.options ("Usage: cycles_test [options] file.xml",
+               "%*", files_parse, "",
+               "--device %s", &devicename, ("Devices to use: " + devices).c_str(),
+               "--shadingsys %s", &ssname, "Shading system to use: svm, osl",
+               "--background", &options.session_params.background, "Render in background, without user interface",
+               "--quiet", &options.quiet, "In background mode, don't print progress messages",
+               "--samples %d", &options.session_params.samples, "Number of samples to render",
+               "--output %s", &options.session_params.output_path, "File path to write output image",
+               "--threads %d", &options.session_params.threads, "CPU Rendering Threads",
+               "--help", &help, "Print help message",
+               NULL);
+       
+       if(ap.parse(argc, argv) < 0) {
+               fprintf(stderr, "%s\n", ap.error_message().c_str());
+               ap.usage();
+               exit(EXIT_FAILURE);
+       }
+       else if(help || options.filepath == "") {
+               ap.usage();
+               exit(EXIT_SUCCESS);
+       }
+
+       options.session_params.device_type = Device::type_from_string(devicename.c_str());
+
+       if(ssname == "osl")
+               options.scene_params.shadingsystem = SceneParams::OSL;
+       else if(ssname == "svm")
+               options.scene_params.shadingsystem = SceneParams::SVM;
+
+       /* handle invalid configurations */
+       bool type_available = false;
+
+       foreach(DeviceType dtype, types)
+               if(options.session_params.device_type == dtype)
+                       type_available = true;
+
+       if(options.session_params.device_type == DEVICE_NONE || !type_available) {
+               fprintf(stderr, "Unknown device: %s\n", devicename.c_str());
+               exit(EXIT_FAILURE);
+       }
+#ifdef WITH_OSL
+       else if(!(ssname == "osl" || ssname == "svm")) {
+#else
+       else if(!(ssname == "svm")) {
+#endif
+               fprintf(stderr, "Unknown shading system: %s\n", ssname.c_str());
+               exit(EXIT_FAILURE);
+       }
+       else if(options.scene_params.shadingsystem == SceneParams::OSL && options.session_params.device_type != DEVICE_CPU) {
+               fprintf(stderr, "OSL shading system only works with CPU device\n");
+               exit(EXIT_FAILURE);
+       }
+       else if(options.session_params.samples < 0) {
+               fprintf(stderr, "Invalid number of samples: %d\n", options.session_params.samples);
+               exit(EXIT_FAILURE);
+       }
+       else if(options.filepath == "") {
+               fprintf(stderr, "No file path specified\n");
+               exit(EXIT_FAILURE);
+       }
+
+       /* load scene */
+       scene_init();
+}
+
+CCL_NAMESPACE_END
+
+using namespace ccl;
+
+int main(int argc, const char **argv)
+{
+       path_init("../build/bin/2.59/scripts/addons/cycles/");
+
+       options_parse(argc, argv);
+
+       if(options.session_params.background) {
+               session_init();
+               options.session->wait();
+               session_exit();
+       }
+       else {
+               string title = "Cycles: " + path_filename(options.filepath);
+
+               /* init/exit are callback so they run while GL is initialized */
+               view_main_loop(title.c_str(), options.width, options.height,
+                       session_init, session_exit, resize, display, keyboard);
+       }
+
+       return 0;
+}
+
diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp
new file mode 100644 (file)
index 0000000..5f9e1d7
--- /dev/null
@@ -0,0 +1,942 @@
+/*
+ * 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 <stdio.h>
+
+#include <sstream>
+#include <algorithm>
+#include <iterator>
+
+#include "camera.h"
+#include "film.h"
+#include "graph.h"
+#include "integrator.h"
+#include "light.h"
+#include "mesh.h"
+#include "nodes.h"
+#include "object.h"
+#include "shader.h"
+#include "scene.h"
+
+#include "subd_mesh.h"
+#include "subd_patch.h"
+#include "subd_split.h"
+
+#include "util_debug.h"
+#include "util_foreach.h"
+#include "util_path.h"
+#include "util_transform.h"
+#include "util_xml.h"
+
+#include "cycles_xml.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* XML reading state */
+
+struct XMLReadState {
+       Scene *scene;           /* scene pointer */
+       Transform tfm;          /* current transform state */
+       bool smooth;            /* smooth normal state */
+       int shader;                     /* current shader */
+       string base;            /* base path to current file*/
+       float dicing_rate;      /* current dicing rate */
+       Mesh::DisplacementMethod displacement_method;
+};
+
+/* Attribute Reading */
+
+static bool xml_read_bool(bool *value, pugi::xml_node node, const char *name)
+{
+       pugi::xml_attribute attr = node.attribute(name);
+
+       if(attr) {
+               *value = (string_iequals(attr.value(), "true")) || (atoi(attr.value()) != 0);
+               return true;
+       }
+
+       return false;
+}
+
+static bool xml_read_int(int *value, pugi::xml_node node, const char *name)
+{
+       pugi::xml_attribute attr = node.attribute(name);
+
+       if(attr) {
+               *value = atoi(attr.value());
+               return true;
+       }
+
+       return false;
+}
+
+static bool xml_read_int_array(vector<int>& value, pugi::xml_node node, const char *name)
+{
+       pugi::xml_attribute attr = node.attribute(name);
+
+       if(attr) {
+               vector<string> tokens;
+               string_split(tokens, attr.value());
+
+               foreach(const string& token, tokens)
+                       value.push_back(atoi(token.c_str()));
+
+               return true;
+       }
+
+       return false;
+}
+
+static bool xml_read_float(float *value, pugi::xml_node node, const char *name)
+{
+       pugi::xml_attribute attr = node.attribute(name);
+
+       if(attr) {
+               *value = atof(attr.value());
+               return true;
+       }
+
+       return false;
+}
+
+static bool xml_read_float_array(vector<float>& value, pugi::xml_node node, const char *name)
+{
+       pugi::xml_attribute attr = node.attribute(name);
+
+       if(attr) {
+               vector<string> tokens;
+               string_split(tokens, attr.value());
+
+               foreach(const string& token, tokens)
+                       value.push_back(atof(token.c_str()));
+
+               return true;
+       }
+
+       return false;
+}
+
+static bool xml_read_float3(float3 *value, pugi::xml_node node, const char *name)
+{
+       vector<float> array;
+
+       if(xml_read_float_array(array, node, name) && array.size() == 3) {
+               *value = make_float3(array[0], array[1], array[2]);
+               return true;
+       }
+
+       return false;
+}
+
+static bool xml_read_float3_array(vector<float3>& value, pugi::xml_node node, const char *name)
+{
+       vector<float> array;
+
+       if(xml_read_float_array(array, node, name)) {
+               for(size_t i = 0; i < array.size(); i += 3)
+                       value.push_back(make_float3(array[i+0], array[i+1], array[i+2]));
+
+               return true;
+       }
+
+       return false;
+}
+
+static bool xml_read_float4(float4 *value, pugi::xml_node node, const char *name)
+{
+       vector<float> array;
+
+       if(xml_read_float_array(array, node, name) && array.size() == 4) {
+               *value = make_float4(array[0], array[1], array[2], array[3]);
+               return true;
+       }
+
+       return false;
+}
+
+static bool xml_read_string(string *str, pugi::xml_node node, const char *name)
+{
+       pugi::xml_attribute attr = node.attribute(name);
+
+       if(attr) {
+               *str = attr.value();
+               return true;
+       }
+
+       return false;
+}
+
+static bool xml_read_ustring(ustring *str, pugi::xml_node node, const char *name)
+{
+       pugi::xml_attribute attr = node.attribute(name);
+
+       if(attr) {
+               *str = ustring(attr.value());
+               return true;
+       }
+
+       return false;
+}
+
+static bool xml_equal_string(pugi::xml_node node, const char *name, const char *value)
+{
+       pugi::xml_attribute attr = node.attribute(name);
+
+       if(attr)
+               return string_iequals(attr.value(), value);
+       
+       return false;
+}
+
+static bool xml_read_enum(ustring *str, ShaderEnum& enm, pugi::xml_node node, const char *name)
+{
+       pugi::xml_attribute attr = node.attribute(name);
+
+       if(attr) {
+               ustring ustr(attr.value());
+
+               if(enm.exists(ustr)) {
+                       *str = ustr;
+                       return true;
+               }
+               else
+                       fprintf(stderr, "Unknown value \"%s\" for attribute \"%s\".\n", ustr.c_str(), name);
+       }
+
+       return false;
+}
+
+/* Film */
+
+static void xml_read_film(const XMLReadState& state, pugi::xml_node node)
+{
+       Camera *cam = state.scene->camera;
+
+       xml_read_int(&cam->width, node, "width");
+       xml_read_int(&cam->height, node, "height");
+
+       float aspect = (float)cam->width/(float)cam->height;
+
+       if(cam->width >= cam->height) {
+               cam->left = -aspect;
+               cam->right = aspect;
+               cam->bottom = -1.0f;
+               cam->top = 1.0f;
+       }
+       else {
+               cam->left = -1.0f;
+               cam->right = 1.0f;
+               cam->bottom = -1.0f/aspect;
+               cam->top = 1.0f/aspect;
+       }
+
+       cam->need_update = true;
+       cam->update();
+}
+
+/* Integrator */
+
+static void xml_read_integrator(const XMLReadState& state, pugi::xml_node node)
+{
+       Integrator *integrator = state.scene->integrator;
+
+       xml_read_int(&integrator->min_bounce, node, "min_bounce");
+       xml_read_int(&integrator->max_bounce, node, "max_bounce");
+       xml_read_bool(&integrator->no_caustics, node, "no_caustics");
+       xml_read_float(&integrator->blur_caustics, node, "blur_caustics");
+}
+
+/* Camera */
+
+static void xml_read_camera(const XMLReadState& state, pugi::xml_node node)
+{
+       Camera *cam = state.scene->camera;
+
+       if(xml_read_float(&cam->fov, node, "fov"))
+               cam->fov *= M_PI/180.0f;
+
+       xml_read_float(&cam->nearclip, node, "nearclip");
+       xml_read_float(&cam->farclip, node, "farclip");
+       xml_read_float(&cam->aperturesize, node, "aperturesize"); // 0.5*focallength/fstop
+       xml_read_float(&cam->focaldistance, node, "focaldistance");
+       xml_read_float(&cam->shutteropen, node, "shutteropen");
+       xml_read_float(&cam->shutterclose, node, "shutterclose");
+
+       if(xml_equal_string(node, "type", "orthographic"))
+               cam->ortho = true;
+       else if(xml_equal_string(node, "type", "perspective"))
+               cam->ortho = false;
+
+       cam->matrix = state.tfm;
+
+       cam->need_update = true;
+       cam->update();
+}
+
+/* Shader */
+
+static string xml_socket_name(const char *name)
+{
+       string sname = name;
+       size_t i;
+
+       while((i = sname.find(" ")) != string::npos)
+               sname.replace(i, 1, "");
+       
+       return sname;
+}
+
+static void xml_read_shader_graph(const XMLReadState& state, Shader *shader, pugi::xml_node graph_node)
+{
+       ShaderGraph *graph = new ShaderGraph();
+
+       map<string, ShaderNode*> nodemap;
+
+       nodemap["output"] = graph->output();
+
+       for(pugi::xml_node node = graph_node.first_child(); node; node = node.next_sibling()) {
+               ShaderNode *snode = NULL;
+
+               if(string_iequals(node.name(), "image_texture")) {
+                       ImageTextureNode *img = new ImageTextureNode();
+
+                       xml_read_string(&img->filename, node, "src");
+                       img->filename = path_join(state.base, img->filename);
+
+                       snode = img;
+               }
+               else if(string_iequals(node.name(), "environment_texture")) {
+                       EnvironmentTextureNode *env = new EnvironmentTextureNode();
+
+                       xml_read_string(&env->filename, node, "src");
+                       env->filename = path_join(state.base, env->filename);
+
+                       snode = env;
+               }
+               else if(string_iequals(node.name(), "sky_texture")) {
+                       SkyTextureNode *sky = new SkyTextureNode();
+
+                       xml_read_float3(&sky->sun_direction, node, "sun_direction");
+                       xml_read_float(&sky->turbidity, node, "turbidity");
+                       
+                       snode = sky;
+               }
+               else if(string_iequals(node.name(), "noise_texture")) {
+                       snode = new NoiseTextureNode();
+               }
+               else if(string_iequals(node.name(), "blend_texture")) {
+                       BlendTextureNode *blend = new BlendTextureNode();
+                       xml_read_enum(&blend->progression, BlendTextureNode::progression_enum, node, "progression");
+                       xml_read_enum(&blend->axis, BlendTextureNode::axis_enum, node, "axis");
+                       snode = blend;
+               }
+               else if(string_iequals(node.name(), "clouds_texture")) {
+                       CloudsTextureNode *clouds = new CloudsTextureNode();
+                       xml_read_bool(&clouds->hard, node, "hard");
+                       xml_read_int(&clouds->depth, node, "depth");
+                       xml_read_enum(&clouds->basis, CloudsTextureNode::basis_enum, node, "basis");
+                       snode = clouds;
+               }
+               else if(string_iequals(node.name(), "voronoi_texture")) {
+                       VoronoiTextureNode *voronoi = new VoronoiTextureNode();
+                       xml_read_enum(&voronoi->distance_metric, VoronoiTextureNode::distance_metric_enum, node, "distance_metric");
+                       xml_read_enum(&voronoi->coloring, VoronoiTextureNode::coloring_enum, node, "coloring");
+                       snode = voronoi;
+               }
+               else if(string_iequals(node.name(), "musgrave_texture")) {
+                       MusgraveTextureNode *musgrave = new MusgraveTextureNode();
+                       xml_read_enum(&musgrave->type, MusgraveTextureNode::type_enum, node, "type");
+                       xml_read_enum(&musgrave->basis, MusgraveTextureNode::basis_enum, node, "basis");
+                       snode = musgrave;
+               }
+               else if(string_iequals(node.name(), "marble_texture")) {
+                       MarbleTextureNode *marble = new MarbleTextureNode();
+                       xml_read_enum(&marble->type, MarbleTextureNode::type_enum, node, "type");
+                       xml_read_enum(&marble->wave, MarbleTextureNode::wave_enum, node, "wave");
+                       xml_read_enum(&marble->basis, MarbleTextureNode::basis_enum, node, "basis");
+                       xml_read_bool(&marble->hard, node, "hard");
+                       xml_read_int(&marble->depth, node, "depth");
+                       snode = marble;
+               }
+               else if(string_iequals(node.name(), "magic_texture")) {
+                       MagicTextureNode *magic = new MagicTextureNode();
+                       xml_read_int(&magic->depth, node, "depth");
+                       snode = magic;
+               }
+               else if(string_iequals(node.name(), "stucci_texture")) {
+                       StucciTextureNode *stucci = new StucciTextureNode();
+                       xml_read_enum(&stucci->type, StucciTextureNode::type_enum, node, "type");
+                       xml_read_enum(&stucci->basis, StucciTextureNode::basis_enum, node, "basis");
+                       xml_read_bool(&stucci->hard, node, "hard");
+                       snode = stucci;
+               }
+               else if(string_iequals(node.name(), "distorted_noise_texture")) {
+                       DistortedNoiseTextureNode *dist = new DistortedNoiseTextureNode();
+                       xml_read_enum(&dist->basis, DistortedNoiseTextureNode::basis_enum, node, "basis");
+                       xml_read_enum(&dist->distortion_basis, DistortedNoiseTextureNode::basis_enum, node, "distortion_basis");
+                       snode = dist;
+               }
+               else if(string_iequals(node.name(), "wood_texture")) {
+                       WoodTextureNode *wood = new WoodTextureNode();
+                       xml_read_enum(&wood->type, WoodTextureNode::type_enum, node, "type");
+                       xml_read_enum(&wood->wave, WoodTextureNode::wave_enum, node, "wave");
+                       xml_read_enum(&wood->basis, WoodTextureNode::basis_enum, node, "basis");
+                       xml_read_bool(&wood->hard, node, "hard");
+                       snode = wood;
+               }
+               else if(string_iequals(node.name(), "mapping")) {
+                       snode = new MappingNode();
+               }
+               else if(string_iequals(node.name(), "ward_bsdf")) {
+                       snode = new WardBsdfNode();
+               }
+               else if(string_iequals(node.name(), "diffuse_bsdf")) {
+                       snode = new DiffuseBsdfNode();
+               }
+               else if(string_iequals(node.name(), "translucent_bsdf")) {
+                       snode = new TranslucentBsdfNode();
+               }
+               else if(string_iequals(node.name(), "transparent_bsdf")) {
+                       snode = new TransparentBsdfNode();
+               }
+               else if(string_iequals(node.name(), "velvet_bsdf")) {
+                       snode = new VelvetBsdfNode();
+               }
+               else if(string_iequals(node.name(), "glossy_bsdf")) {
+                       GlossyBsdfNode *glossy = new GlossyBsdfNode();
+                       xml_read_enum(&glossy->distribution, GlossyBsdfNode::distribution_enum, node, "distribution");
+                       snode = glossy;
+               }
+               else if(string_iequals(node.name(), "glass_bsdf")) {
+                       GlassBsdfNode *diel = new GlassBsdfNode();
+                       xml_read_enum(&diel->distribution, GlassBsdfNode::distribution_enum, node, "distribution");
+                       snode = diel;
+               }
+               else if(string_iequals(node.name(), "emission")) {
+                       EmissionNode *emission = new EmissionNode();
+                       xml_read_bool(&emission->total_power, node, "total_power");
+                       snode = emission;
+               }
+               else if(string_iequals(node.name(), "background")) {
+                       snode = new BackgroundNode();
+               }
+               else if(string_iequals(node.name(), "transparent_volume")) {
+                       snode = new TransparentVolumeNode();
+               }
+               else if(string_iequals(node.name(), "isotropic_volume")) {
+                       snode = new IsotropicVolumeNode();
+               }
+               else if(string_iequals(node.name(), "geometry")) {
+                       snode = new GeometryNode();
+               }
+               else if(string_iequals(node.name(), "texture_coordinate")) {
+                       snode = new TextureCoordinateNode();
+               }
+               else if(string_iequals(node.name(), "lightPath")) {
+                       snode = new LightPathNode();
+               }
+               else if(string_iequals(node.name(), "value")) {
+                       ValueNode *value = new ValueNode();
+                       xml_read_float(&value->value, node, "value");
+                       snode = value;
+               }
+               else if(string_iequals(node.name(), "color")) {
+                       ColorNode *color = new ColorNode();
+                       xml_read_float3(&color->value, node, "value");
+                       snode = color;
+               }
+               else if(string_iequals(node.name(), "mix_closure")) {
+                       snode = new MixClosureNode();
+               }
+               else if(string_iequals(node.name(), "add_closure")) {
+                       snode = new AddClosureNode();
+               }
+               else if(string_iequals(node.name(), "mix")) {
+                       MixNode *mix = new MixNode();
+                       xml_read_enum(&mix->type, MixNode::type_enum, node, "type");
+                       snode = mix;
+               }
+               else if(string_iequals(node.name(), "attribute")) {
+                       AttributeNode *attr = new AttributeNode();
+                       xml_read_ustring(&attr->attribute, node, "attribute");
+                       snode = attr;
+               }
+               else if(string_iequals(node.name(), "fresnel")) {
+                       snode = new FresnelNode();
+               }
+               else if(string_iequals(node.name(), "math")) {
+                       MathNode *math = new MathNode();
+                       xml_read_enum(&math->type, MathNode::type_enum, node, "type");
+                       snode = math;
+               }
+               else if(string_iequals(node.name(), "vector_math")) {
+                       VectorMathNode *vmath = new VectorMathNode();
+                       xml_read_enum(&vmath->type, VectorMathNode::type_enum, node, "type");
+                       snode = vmath;
+               }
+               else if(string_iequals(node.name(), "connect")) {
+                       /* connect nodes */
+                       vector<string> from_tokens, to_tokens;
+
+                       string_split(from_tokens, node.attribute("from").value());
+                       string_split(to_tokens, node.attribute("to").value());
+
+                       if(from_tokens.size() == 2 && to_tokens.size() == 2) {
+                               /* find nodes and sockets */
+                               ShaderOutput *output = NULL;
+                               ShaderInput *input = NULL;
+
+                               if(nodemap.find(from_tokens[0]) != nodemap.end()) {
+                                       ShaderNode *fromnode = nodemap[from_tokens[0]];
+
+                                       foreach(ShaderOutput *out, fromnode->outputs)
+                                               if(string_iequals(xml_socket_name(out->name), from_tokens[1]))
+                                                       output = out;
+
+                                       if(!output)
+                                               fprintf(stderr, "Unknown output socket name \"%s\" on \"%s\".\n", from_tokens[1].c_str(), from_tokens[0].c_str());
+                               }
+                               else
+                                       fprintf(stderr, "Unknown shader node name \"%s\".\n", from_tokens[0].c_str());
+
+                               if(nodemap.find(to_tokens[0]) != nodemap.end()) {
+                                       ShaderNode *tonode = nodemap[to_tokens[0]];
+
+                                       foreach(ShaderInput *in, tonode->inputs)
+                                               if(string_iequals(xml_socket_name(in->name), to_tokens[1]))
+                                                       input = in;
+
+                                       if(!input)
+                                               fprintf(stderr, "Unknown input socket name \"%s\" on \"%s\".\n", to_tokens[1].c_str(), to_tokens[0].c_str());
+                               }
+                               else
+                                       fprintf(stderr, "Unknown shader node name \"%s\".\n", to_tokens[0].c_str());
+
+                               /* connect */
+                               if(output && input)
+                                       graph->connect(output, input);
+                       }
+                       else
+                               fprintf(stderr, "Invalid from or to value for connect node.\n");
+               }
+               else
+                       fprintf(stderr, "Unknown shader node \"%s\".\n", node.name());
+
+               if(snode) {
+                       /* add to graph */
+                       graph->add(snode);
+
+                       /* add to map for name lookups */
+                       string name = "";
+                       xml_read_string(&name, node, "name");
+
+                       nodemap[name] = snode;
+
+                       /* read input values */
+                       for(pugi::xml_attribute attr = node.first_attribute(); attr; attr = attr.next_attribute()) {
+                               foreach(ShaderInput *in, snode->inputs) {
+                                       if(string_iequals(in->name, attr.name())) {
+                                               switch(in->type) {
+                                                       case SHADER_SOCKET_FLOAT:
+                                                               xml_read_float(&in->value.x, node, attr.name());
+                                                               break;
+                                                       case SHADER_SOCKET_COLOR:
+                                                       case SHADER_SOCKET_VECTOR:
+                                                       case SHADER_SOCKET_POINT:
+                                                       case SHADER_SOCKET_NORMAL:
+                                                               xml_read_float3(&in->value, node, attr.name());
+                                                               break;
+                                                       default:
+                                                               break;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       shader->set_graph(graph);
+       shader->tag_update(state.scene);
+}
+
+static void xml_read_shader(const XMLReadState& state, pugi::xml_node node)
+{
+       Shader *shader = new Shader();
+       xml_read_string(&shader->name, node, "name");
+       xml_read_shader_graph(state, shader, node);
+       state.scene->shaders.push_back(shader);
+}
+
+/* Background */
+
+static void xml_read_background(const XMLReadState& state, pugi::xml_node node)
+{
+       Shader *shader = state.scene->shaders[state.scene->default_background];
+
+       xml_read_shader_graph(state, shader, node);
+}
+
+/* Mesh */
+
+static Mesh *xml_add_mesh(Scene *scene, const Transform& tfm)
+{
+       /* create mesh */
+       Mesh *mesh = new Mesh();
+       scene->meshes.push_back(mesh);
+
+       /* create object*/
+       Object *object = new Object();
+       object->mesh = mesh;
+       object->tfm = tfm;
+       scene->objects.push_back(object);
+
+       return mesh;
+}
+
+static void xml_read_mesh(const XMLReadState& state, pugi::xml_node node)
+{
+       /* add mesh */
+       Mesh *mesh = xml_add_mesh(state.scene, state.tfm);
+       mesh->used_shaders.push_back(state.shader);
+
+       /* read state */
+       int shader = state.shader;
+       bool smooth = state.smooth;
+
+       mesh->displacement_method = state.displacement_method;
+
+       /* read vertices and polygons, RIB style */
+       vector<float3> P;
+       vector<int> verts, nverts;
+
+       xml_read_float3_array(P, node, "P");
+       xml_read_int_array(verts, node, "verts");
+       xml_read_int_array(nverts, node, "nverts");
+
+       if(xml_equal_string(node, "subdivision", "catmull-clark")) {
+               /* create subd mesh */
+               SubdMesh sdmesh;
+
+               /* create subd vertices */
+               for(size_t i = 0; i < P.size(); i++)
+                       sdmesh.add_vert(P[i]);
+
+               /* create subd faces */
+               int index_offset = 0;
+
+               for(size_t i = 0; i < nverts.size(); i++) {
+                       if(nverts[i] == 4) {
+                               int v0 = verts[index_offset + 0];
+                               int v1 = verts[index_offset + 1];
+                               int v2 = verts[index_offset + 2];
+                               int v3 = verts[index_offset + 3];
+
+                               sdmesh.add_face(v0, v1, v2, v3);
+                       }
+                       else {
+                               for(int j = 0; j < nverts[i]-2; j++) {
+                                       int v0 = verts[index_offset];
+                                       int v1 = verts[index_offset + j + 1];
+                                       int v2 = verts[index_offset + j + 2];;
+
+                                       sdmesh.add_face(v0, v1, v2);
+                               }
+                       }
+
+                       index_offset += nverts[i];
+               }
+
+               /* finalize subd mesh */
+               sdmesh.link_boundary();
+
+               /* subdivide */
+               DiagSplit dsplit;
+               //dsplit.camera = state.scene->camera;
+               //dsplit.dicing_rate = 5.0f;
+               dsplit.dicing_rate = state.dicing_rate;
+               xml_read_float(&dsplit.dicing_rate, node, "dicing_rate");
+               sdmesh.tesselate(&dsplit, false, mesh, shader, smooth);
+       }
+       else {
+               /* create vertices */
+               mesh->verts = P;
+
+               /* create triangles */
+               int index_offset = 0;
+
+               for(size_t i = 0; i < nverts.size(); i++) {
+                       for(int j = 0; j < nverts[i]-2; j++) {
+                               int v0 = verts[index_offset];
+                               int v1 = verts[index_offset + j + 1];
+                               int v2 = verts[index_offset + j + 2];
+
+                               assert(v0 < (int)P.size());
+                               assert(v1 < (int)P.size());
+                               assert(v2 < (int)P.size());
+
+                               mesh->add_triangle(v0, v1, v2, shader, smooth);
+                       }
+
+                       index_offset += nverts[i];
+               }
+       }
+
+       /* temporary for test compatibility */
+       mesh->attributes.remove(Attribute::STD_VERTEX_NORMAL);
+}
+
+/* Patch */
+
+static void xml_read_patch(const XMLReadState& state, pugi::xml_node node)
+{
+       /* read patch */
+       Patch *patch = NULL;
+
+       vector<float3> P;
+       xml_read_float3_array(P, node, "P");
+
+       if(xml_equal_string(node, "type", "bilinear")) {
+               /* bilinear patch */
+               if(P.size() == 4) {
+                       LinearQuadPatch *bpatch = new LinearQuadPatch();
+
+                       for(int i = 0; i < 4; i++)
+                               P[i] = transform(&state.tfm, P[i]);
+                       memcpy(bpatch->hull, &P[0], sizeof(bpatch->hull));
+
+                       patch = bpatch;
+               }
+               else
+                       fprintf(stderr, "Invalid number of control points for bilinear patch.\n");
+       }
+       else if(xml_equal_string(node, "type", "bicubic")) {
+               /* bicubic patch */
+               if(P.size() == 16) {
+                       BicubicPatch *bpatch = new BicubicPatch();
+
+                       for(int i = 0; i < 16; i++)
+                               P[i] = transform(&state.tfm, P[i]);
+                       memcpy(bpatch->hull, &P[0], sizeof(bpatch->hull));
+
+                       patch = bpatch;
+               }
+               else
+                       fprintf(stderr, "Invalid number of control points for bicubic patch.\n");
+       }
+       else
+               fprintf(stderr, "Unknown patch type.\n");
+
+       if(patch) {
+               /* add mesh */
+               Mesh *mesh = xml_add_mesh(state.scene, transform_identity());
+
+               mesh->used_shaders.push_back(state.shader);
+
+               /* split */
+               DiagSplit dsplit;
+               //dsplit.camera = state.scene->camera;
+               //dsplit.dicing_rate = 5.0f;
+               dsplit.dicing_rate = state.dicing_rate;
+               xml_read_float(&dsplit.dicing_rate, node, "dicing_rate");
+               dsplit.split_quad(mesh, patch, state.shader, state.smooth);
+
+               delete patch;
+
+               /* temporary for test compatibility */
+               mesh->attributes.remove(Attribute::STD_VERTEX_NORMAL);
+       }
+}
+
+/* Light */
+
+static void xml_read_light(const XMLReadState& state, pugi::xml_node node)
+{
+       Light *light = new Light();
+       light->shader = state.shader;
+       xml_read_float3(&light->co, node, "P");
+       light->co = transform(&state.tfm, light->co);
+
+       state.scene->lights.push_back(light);
+}
+
+/* Transform */
+
+static void xml_read_transform(pugi::xml_node node, Transform& tfm)
+{
+       if(node.attribute("matrix")) {
+               vector<float> matrix;
+               if(xml_read_float_array(matrix, node, "matrix") && matrix.size() == 16)
+                       tfm = tfm * transform_transpose((*(Transform*)&matrix[0]));
+       }
+
+       if(node.attribute("translate")) {
+               float3 translate = make_float3(0.0f, 0.0f, 0.0f);
+               xml_read_float3(&translate, node, "translate");
+               tfm = tfm * transform_translate(translate);
+       }
+
+       if(node.attribute("rotate")) {
+               float4 rotate = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+               xml_read_float4(&rotate, node, "rotate");
+               tfm = tfm * transform_rotate(rotate.x*M_PI/180.0f, make_float3(rotate.y, rotate.z, rotate.w));
+       }
+
+       if(node.attribute("scale")) {
+               float3 scale = make_float3(0.0f, 0.0f, 0.0f);
+               xml_read_float3(&scale, node, "scale");
+               tfm = tfm * transform_scale(scale);
+       }
+}
+
+/* State */
+
+static void xml_read_state(XMLReadState& state, pugi::xml_node node)
+{
+       /* read shader */
+       string shadername;
+
+       if(xml_read_string(&shadername, node, "shader")) {
+               int i = 0;
+               bool found = false;
+
+               foreach(Shader *shader, state.scene->shaders) {
+                       if(shader->name == shadername) {
+                               state.shader = i;
+                               found = true;
+                               break;
+                       }
+
+                       i++;
+               }
+
+               if(!found)
+                       fprintf(stderr, "Unknown shader \"%s\".\n", shadername.c_str());
+       }
+
+       xml_read_float(&state.dicing_rate, node, "dicing_rate");
+
+       /* read smooth/flat */
+       if(xml_equal_string(node, "interpolation", "smooth"))
+               state.smooth = true;
+       else if(xml_equal_string(node, "interpolation", "flat"))
+               state.smooth = false;
+
+       /* read displacement method */
+       if(xml_equal_string(node, "displacement_method", "true"))
+               state.displacement_method = Mesh::DISPLACE_TRUE;
+       else if(xml_equal_string(node, "displacement_method", "bump"))
+               state.displacement_method = Mesh::DISPLACE_BUMP;
+       else if(xml_equal_string(node, "displacement_method", "both"))
+               state.displacement_method = Mesh::DISPLACE_BOTH;
+}
+
+/* Scene */
+
+static void xml_read_include(const XMLReadState& state, const string& src);
+
+static void xml_read_scene(const XMLReadState& state, pugi::xml_node scene_node)
+{
+       for(pugi::xml_node node = scene_node.first_child(); node; node = node.next_sibling()) {
+               if(string_iequals(node.name(), "film")) {
+                       xml_read_film(state, node);
+               }
+               else if(string_iequals(node.name(), "integrator")) {
+                       xml_read_integrator(state, node);
+               }
+               else if(string_iequals(node.name(), "camera")) {
+                       xml_read_camera(state, node);
+               }
+               else if(string_iequals(node.name(), "shader")) {
+                       xml_read_shader(state, node);
+               }
+               else if(string_iequals(node.name(), "background")) {
+                       xml_read_background(state, node);
+               }
+               else if(string_iequals(node.name(), "mesh")) {
+                       xml_read_mesh(state, node);
+               }
+               else if(string_iequals(node.name(), "patch")) {
+                       xml_read_patch(state, node);
+               }
+               else if(string_iequals(node.name(), "light")) {
+                       xml_read_light(state, node);
+               }
+               else if(string_iequals(node.name(), "transform")) {
+                       XMLReadState substate = state;
+
+                       xml_read_transform(node, substate.tfm);
+                       xml_read_scene(substate, node);
+               }
+               else if(string_iequals(node.name(), "state")) {
+                       XMLReadState substate = state;
+
+                       xml_read_state(substate, node);
+                       xml_read_scene(substate, node);
+               }
+               else if(string_iequals(node.name(), "include")) {
+                       string src;
+
+                       if(xml_read_string(&src, node, "src"))
+                               xml_read_include(state, src);
+               }
+               else
+                       fprintf(stderr, "Unknown node \"%s\".\n", node.name());
+       }
+}
+
+/* Include */
+
+static void xml_read_include(const XMLReadState& state, const string& src)
+{
+       /* open XML document */
+       pugi::xml_document doc;
+       pugi::xml_parse_result parse_result;
+
+       string path = path_join(state.base, src);
+       parse_result = doc.load_file(path.c_str());
+
+       if(parse_result) {
+               XMLReadState substate = state;
+               substate.base = path_dirname(path);
+
+               xml_read_scene(substate, doc);
+       }
+       else
+               fprintf(stderr, "%s read error: %s\n", src.c_str(), parse_result.description());
+}
+
+/* File */
+
+void xml_read_file(Scene *scene, const char *filepath)
+{
+       XMLReadState state;
+
+       state.scene = scene;
+       state.tfm = transform_identity();
+       state.shader = scene->default_surface;
+       state.smooth = false;
+       state.dicing_rate = 0.1f;
+       state.base = path_dirname(filepath);
+
+       xml_read_include(state, path_filename(filepath));
+
+       scene->params.bvh_type = SceneParams::BVH_STATIC;
+}
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/app/cycles_xml.h b/intern/cycles/app/cycles_xml.h
new file mode 100644 (file)
index 0000000..90ec529
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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 __CYCLES_XML__
+#define __CYCLES_XML__
+
+CCL_NAMESPACE_BEGIN
+
+class Scene;
+
+void xml_read_file(Scene *scene, const char *filepath);
+
+CCL_NAMESPACE_END
+
+#endif /* __CYCLES_XML__ */
+
diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a4f290f
--- /dev/null
@@ -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 (file)
index 0000000..979e3e8
--- /dev/null
@@ -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 (file)
index 0000000..097909c
--- /dev/null
@@ -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 (file)
index 0000000..4aef255
--- /dev/null
@@ -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 (file)
index 0000000..e5243b6
--- /dev/null
@@ -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 (file)
index 0000000..5a56240
--- /dev/null
@@ -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=10, 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 (file)
index 0000000..dfbfb76
--- /dev/null
@@ -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 False # 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 False #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 (file)
index 0000000..3713da0
--- /dev/null
@@ -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 (file)
index 0000000..2a2c2a7
--- /dev/null
@@ -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 (file)
index 0000000..3bf83fa
--- /dev/null
@@ -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 (file)
index 0000000..1095a3e
--- /dev/null
@@ -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 (file)
index 0000000..58049bb
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2011, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <Python.h>
+
+#include "blender_sync.h"
+#include "blender_session.h"
+
+#include "util_opengl.h"
+#include "util_path.h"
+
+CCL_NAMESPACE_BEGIN
+
+static PyObject *init_func(PyObject *self, PyObject *args)
+{
+       const char *path, *user_path;
+
+       if(!PyArg_ParseTuple(args, "ss", &path, &user_path))
+               return NULL;
+       
+       path_init(path, user_path);
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+static PyObject *create_func(PyObject *self, PyObject *args)
+{
+       PyObject *pyengine, *pydata, *pyscene, *pyregion, *pyv3d, *pyrv3d;
+
+       if(!PyArg_ParseTuple(args, "OOOOOO", &pyengine, &pydata, &pyscene, &pyregion, &pyv3d, &pyrv3d))
+               return NULL;
+
+       /* RNA */
+       PointerRNA engineptr;
+       RNA_pointer_create(NULL, &RNA_RenderEngine, (void*)PyLong_AsVoidPtr(pyengine), &engineptr);
+       BL::RenderEngine engine(engineptr);
+
+       PointerRNA dataptr;
+       RNA_id_pointer_create((ID*)PyLong_AsVoidPtr(pydata), &dataptr);
+       BL::BlendData data(dataptr);
+
+       PointerRNA sceneptr;
+       RNA_id_pointer_create((ID*)PyLong_AsVoidPtr(pyscene), &sceneptr);
+       BL::Scene scene(sceneptr);
+
+       PointerRNA regionptr;
+       RNA_id_pointer_create((ID*)PyLong_AsVoidPtr(pyregion), &regionptr);
+       BL::Region region(regionptr);
+
+       PointerRNA v3dptr;
+       RNA_id_pointer_create((ID*)PyLong_AsVoidPtr(pyv3d), &v3dptr);
+       BL::SpaceView3D v3d(v3dptr);
+
+       PointerRNA rv3dptr;
+       RNA_id_pointer_create((ID*)PyLong_AsVoidPtr(pyrv3d), &rv3dptr);
+       BL::RegionView3D rv3d(rv3dptr);
+
+       /* create session */
+       BlenderSession *session;
+
+       if(rv3d) {
+               /* interactive session */
+               int width = region.width();
+               int height = region.height();
+
+               session = new BlenderSession(engine, data, scene, v3d, rv3d, width, height);
+       }
+       else {
+               /* offline session */
+               session = new BlenderSession(engine, data, scene);
+       }
+       
+       return PyLong_FromVoidPtr(session);
+}
+
+static PyObject *free_func(PyObject *self, PyObject *args)
+{
+       PyObject *pysession;
+
+       if(!PyArg_ParseTuple(args, "O", &pysession))
+               return NULL;
+
+       delete (BlenderSession*)PyLong_AsVoidPtr(pysession);
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+static PyObject *render_func(PyObject *self, PyObject *args)
+{
+       PyObject *pysession;
+
+       if(!PyArg_ParseTuple(args, "O", &pysession))
+               return NULL;
+       
+       Py_BEGIN_ALLOW_THREADS
+
+       BlenderSession *session = (BlenderSession*)PyLong_AsVoidPtr(pysession);
+       session->render();
+
+       Py_END_ALLOW_THREADS
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+static PyObject *draw_func(PyObject *self, PyObject *args)
+{
+       PyObject *pysession, *pyv3d, *pyrv3d;
+
+       if(!PyArg_ParseTuple(args, "OOO", &pysession, &pyv3d, &pyrv3d))
+               return NULL;
+       
+       BlenderSession *session = (BlenderSession*)PyLong_AsVoidPtr(pysession);
+
+       if(PyLong_AsVoidPtr(pyrv3d)) {
+               /* 3d view drawing */
+               int viewport[4];
+               glGetIntegerv(GL_VIEWPORT, viewport);
+
+               session->draw(viewport[2], viewport[3]);
+       }
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+static PyObject *sync_func(PyObject *self, PyObject *args)
+{
+       PyObject *pysession;
+
+       if(!PyArg_ParseTuple(args, "O", &pysession))
+               return NULL;
+
+       BlenderSession *session = (BlenderSession*)PyLong_AsVoidPtr(pysession);
+       session->synchronize();
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+static PyObject *available_devices_func(PyObject *self, PyObject *args)
+{
+       vector<DeviceType> types = Device::available_types();
+
+       PyObject *ret = PyTuple_New(types.size());
+
+       for(size_t i = 0; i < types.size(); i++) {
+               string name = Device::string_from_type(types[i]);
+               PyTuple_SetItem(ret, i, PyUnicode_FromString(name.c_str()));
+       }
+
+       return ret;
+}
+
+static PyObject *with_osl_func(PyObject *self, PyObject *args)
+{
+#ifdef WITH_OSL
+       PyObject *ret = Py_True;
+#else
+       PyObject *ret = Py_False;
+#endif
+
+       return Py_INCREF(ret), ret;
+}
+
+static PyMethodDef methods[] = {
+       {"init", init_func, METH_VARARGS, ""},
+       {"create", create_func, METH_VARARGS, ""},
+       {"free", free_func, METH_VARARGS, ""},
+       {"render", render_func, METH_VARARGS, ""},
+       {"draw", draw_func, METH_VARARGS, ""},
+       {"sync", sync_func, METH_VARARGS, ""},
+       {"available_devices", available_devices_func, METH_NOARGS, ""},
+       {"with_osl", with_osl_func, METH_NOARGS, ""},
+       {NULL, NULL, 0, NULL},
+};
+
+static struct PyModuleDef module = {
+       PyModuleDef_HEAD_INIT,
+       "bcycles",
+       "Blender cycles render integration",
+       -1,
+       methods,
+       NULL, NULL, NULL, NULL
+};
+
+CCL_NAMESPACE_END
+
+extern "C" PyObject *CYCLES_initPython();
+
+PyObject *CYCLES_initPython()
+{
+       return PyModule_Create(&ccl::module);
+}
+
diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp
new file mode 100644 (file)
index 0000000..4433b1e
--- /dev/null
@@ -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 (file)
index 0000000..e30b60c
--- /dev/null
@@ -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 (file)
index 0000000..f1b032b
--- /dev/null
@@ -0,0 +1,696 @@
+/*
+ * 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_GEOMETRY: 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_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_NEW_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 (file)
index 0000000..36cf29d
--- /dev/null
@@ -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 (file)
index 0000000..2e7b8ed
--- /dev/null
@@ -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 (file)
index 0000000..c5cceff
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * 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
+   todo: clean this up ... */
+
+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__ */
+
diff --git a/intern/cycles/bvh/CMakeLists.txt b/intern/cycles/bvh/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b35c20b
--- /dev/null
@@ -0,0 +1,18 @@
+
+include_directories(. ../kernel ../kernel/svm ../render ../util ../device)
+
+set(sources
+       bvh.cpp
+       bvh_build.cpp
+       bvh_node.cpp
+       bvh_sort.cpp)
+
+set(headers
+       bvh.h
+       bvh_build.h
+       bvh_node.h
+       bvh_params.h
+       bvh_sort.h)
+
+add_library(cycles_bvh ${sources} ${headers})
+
diff --git a/intern/cycles/bvh/bvh.cpp b/intern/cycles/bvh/bvh.cpp
new file mode 100644 (file)
index 0000000..cd3ad70
--- /dev/null
@@ -0,0 +1,701 @@
+/*
+ * Adapted from code copyright 2009-2010 NVIDIA Corporation
+ * Modifications Copyright 2011, Blender Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mesh.h"
+#include "object.h"
+#include "scene.h"
+
+#include "bvh.h"
+#include "bvh_build.h"
+#include "bvh_node.h"
+#include "bvh_params.h"
+
+#include "util_cache.h"
+#include "util_debug.h"
+#include "util_foreach.h"
+#include "util_map.h"
+#include "util_progress.h"
+#include "util_types.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Pack Utility */
+
+struct BVHStackEntry
+{
+       const BVHNode *node;
+       int idx;
+
+       BVHStackEntry(const BVHNode* n = 0, int i = 0)
+       : node(n), idx(i)
+       {
+       }
+
+       int encodeIdx() const
+       {
+               return (node->is_leaf())? ~idx: idx;
+       }
+};
+
+/* BVH */
+
+BVH::BVH(const BVHParams& params_, const vector<Object*>& objects_)
+: params(params_), objects(objects_)
+{
+}
+
+BVH *BVH::create(const BVHParams& params, const vector<Object*>& objects)
+{
+       if(params.use_qbvh)
+               return new QBVH(params, objects);
+       else
+               return new RegularBVH(params, objects);
+}
+
+/* Cache */
+
+bool BVH::cache_read(CacheData& key)
+{
+       key.add(&params, sizeof(params));
+
+       foreach(Object *ob, objects) {
+               key.add(ob->mesh->verts);
+               key.add(ob->mesh->triangles);
+       }
+
+       CacheData value;
+
+       if(Cache::global.lookup(key, value)) {
+               value.read(pack.root_index);
+
+               value.read(pack.nodes);
+               value.read(pack.object_node);
+               value.read(pack.tri_woop);
+               value.read(pack.prim_visibility);
+               value.read(pack.prim_index);
+               value.read(pack.prim_object);
+               value.read(pack.is_leaf);
+
+               return true;
+       }
+
+       return false;
+}
+
+void BVH::cache_write(CacheData& key)
+{
+       CacheData value;
+
+       value.add(pack.root_index);
+
+       value.add(pack.nodes);
+       value.add(pack.object_node);
+       value.add(pack.tri_woop);
+       value.add(pack.prim_visibility);
+       value.add(pack.prim_index);
+       value.add(pack.prim_object);
+       value.add(pack.is_leaf);
+
+       Cache::global.insert(key, value);
+}
+
+/* Building */
+
+void BVH::build(Progress& progress)
+{
+       progress.set_substatus("Building BVH");
+
+       /* cache read */
+       CacheData key("bvh");
+
+       if(params.use_cache) {
+               progress.set_substatus("Looking in BVH cache");
+
+               if(cache_read(key))
+                       return;
+       }
+
+       /* build nodes */
+       vector<int> prim_index;
+       vector<int> prim_object;
+
+       BVHBuild bvh_build(objects, prim_index, prim_object, params, progress);
+       BVHNode *root = bvh_build.run();
+
+       if(progress.get_cancel()) {
+               if(root) root->deleteSubtree();
+               return;
+       }
+
+       /* todo: get rid of this copy */
+       pack.prim_index = prim_index;
+       pack.prim_object = prim_object;
+
+       /* compute SAH */
+       if(!params.top_level)
+               pack.SAH = root->computeSubtreeSAHCost(params);
+
+       if(progress.get_cancel()) {
+               root->deleteSubtree();
+               return;
+       }
+
+       /* pack triangles */
+       progress.set_substatus("Packing BVH triangles");
+       pack_triangles();
+
+       if(progress.get_cancel()) {
+               root->deleteSubtree();
+               return;
+       }
+
+       /* pack nodes */
+       progress.set_substatus("Packing BVH nodes");
+       array<int> tmp_prim_object = pack.prim_object;
+       pack_nodes(tmp_prim_object, root);
+       
+       /* free build nodes */
+       root->deleteSubtree();
+
+       if(progress.get_cancel()) return;
+
+       /* cache write */
+       if(params.use_cache) {
+               progress.set_substatus("Writing BVH cache");
+               cache_write(key);
+       }
+}
+
+/* Refitting */
+
+void BVH::refit(Progress& progress)
+{
+       progress.set_substatus("Packing BVH triangles");
+       pack_triangles();
+
+       if(progress.get_cancel()) return;
+
+       progress.set_substatus("Refitting BVH nodes");
+       refit_nodes();
+}
+
+/* Triangles */
+
+void BVH::pack_triangle(int idx, float4 woop[3])
+{
+       /* create Woop triangle */
+       int tob = pack.prim_object[idx];
+       const Mesh *mesh = objects[tob]->mesh;
+       int tidx = pack.prim_index[idx];
+       const int *vidx = mesh->triangles[tidx].v;
+       const float3* vpos = &mesh->verts[0];
+       float3 v0 = vpos[vidx[0]];
+       float3 v1 = vpos[vidx[1]];
+       float3 v2 = vpos[vidx[2]];
+
+       float3 r0 = v0 - v2;
+       float3 r1 = v1 - v2;
+       float3 r2 = cross(r0, r1);
+
+       if(dot(r0, r0) == 0.0f || dot(r1, r1) == 0.0f || dot(r2, r2) == 0.0f) {
+               /* degenerate */
+               woop[0] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+               woop[1] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+               woop[2] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+       }
+       else {
+               Transform t = make_transform(
+                       r0.x, r1.x, r2.x, v2.x,
+                       r0.y, r1.y, r2.y, v2.y,
+                       r0.z, r1.z, r2.z, v2.z,
+                       0.0f, 0.0f, 0.0f, 1.0f);
+
+               t = transform_inverse(t);
+
+               woop[0] = make_float4(t.z.x, t.z.y, t.z.z, -t.z.w);
+               woop[1] = make_float4(t.x.x, t.x.y, t.x.z, t.x.w);
+               woop[2] = make_float4(t.y.x, t.y.y, t.y.z, t.y.w);
+       }
+}
+
+void BVH::pack_triangles()
+{
+       int nsize = TRI_NODE_SIZE;
+       size_t tidx_size = pack.prim_index.size();
+
+       pack.tri_woop.clear();
+       pack.tri_woop.resize(tidx_size * nsize);
+       pack.prim_visibility.clear();
+       pack.prim_visibility.resize(tidx_size);
+
+       for(unsigned int i = 0; i < tidx_size; i++) {
+               if(pack.prim_index[i] != -1) {
+                       float4 woop[3];
+
+                       pack_triangle(i, woop);
+                       memcpy(&pack.tri_woop[i * nsize], woop, sizeof(float4)*3);
+
+                       int tob = pack.prim_object[i];
+                       Object *ob = objects[tob];
+                       pack.prim_visibility[i] = ob->visibility;
+               }
+       }
+}
+
+/* Pack Instances */
+
+void BVH::pack_instances(size_t nodes_size)
+{
+       /* The BVH's for instances are built separately, but for traversal all
+          BVH's are stored in global arrays. This function merges them into the
+          top level BVH, adjusting indexes and offsets where appropriate. */
+       bool use_qbvh = params.use_qbvh;
+       size_t nsize = (use_qbvh)? BVH_QNODE_SIZE: BVH_NODE_SIZE;
+
+       /* adjust primitive index to point to the triangle in the global array, for
+          meshes with transform applied and already in the top level BVH */
+       for(size_t i = 0; i < pack.prim_index.size(); i++)
+               if(pack.prim_index[i] != -1)
+                       pack.prim_index[i] += objects[pack.prim_object[i]]->mesh->tri_offset;
+
+       /* track offsets of instanced BVH data in global array */
+       size_t tri_offset = pack.prim_index.size();
+       size_t nodes_offset = nodes_size;
+
+       /* clear array that gives the node indexes for instanced objects */
+       pack.object_node.clear();
+
+       /* reserve */
+       size_t prim_index_size = pack.prim_index.size();
+       size_t tri_woop_size = pack.tri_woop.size();
+
+       size_t pack_prim_index_offset = prim_index_size;
+       size_t pack_tri_woop_offset = tri_woop_size;
+       size_t pack_nodes_offset = nodes_size;
+       size_t object_offset = 0;
+
+       map<Mesh*, int> mesh_map;
+
+       foreach(Object *ob, objects) {
+               Mesh *mesh = ob->mesh;
+               BVH *bvh = mesh->bvh;
+
+               if(!mesh->transform_applied) {
+                       if(mesh_map.find(mesh) == mesh_map.end()) {
+                               prim_index_size += bvh->pack.prim_index.size();
+                               tri_woop_size += bvh->pack.tri_woop.size();
+                               nodes_size += bvh->pack.nodes.size()*nsize;
+
+                               mesh_map[mesh] = 1;
+                       }
+               }
+       }
+
+       mesh_map.clear();
+
+       pack.prim_index.resize(prim_index_size);
+       pack.prim_object.resize(prim_index_size);
+       pack.prim_visibility.resize(prim_index_size);
+       pack.tri_woop.resize(tri_woop_size);
+       pack.nodes.resize(nodes_size);
+       pack.object_node.resize(objects.size());
+
+       int *pack_prim_index = (pack.prim_index.size())? &pack.prim_index[0]: NULL;
+       int *pack_prim_object = (pack.prim_object.size())? &pack.prim_object[0]: NULL;
+       uint *pack_prim_visibility = (pack.prim_visibility.size())? &pack.prim_visibility[0]: NULL;
+       float4 *pack_tri_woop = (pack.tri_woop.size())? &pack.tri_woop[0]: NULL;
+       int4 *pack_nodes = (pack.nodes.size())? &pack.nodes[0]: NULL;
+
+       /* merge */
+       foreach(Object *ob, objects) {
+               Mesh *mesh = ob->mesh;
+
+               /* if mesh transform is applied, that means it's already in the top
+                  level BVH, and we don't need to merge it in */
+               if(mesh->transform_applied) {
+                       pack.object_node[object_offset++] = 0;
+                       continue;
+               }
+
+               /* if mesh already added once, don't add it again, but used set
+                  node offset for this object */
+               map<Mesh*, int>::iterator it = mesh_map.find(mesh);
+
+               if(mesh_map.find(mesh) != mesh_map.end()) {
+                       int noffset = it->second;
+                       pack.object_node[object_offset++] = noffset;
+                       continue;
+               }
+
+               BVH *bvh = mesh->bvh;
+
+               int noffset = nodes_offset/nsize;
+               int mesh_tri_offset = mesh->tri_offset;
+
+               /* fill in node indexes for instances */
+               if(bvh->pack.is_leaf[0])
+                       pack.object_node[object_offset++] = -noffset-1;
+               else
+                       pack.object_node[object_offset++] = noffset;
+
+               mesh_map[mesh] = pack.object_node[object_offset-1];
+
+               /* merge primitive and object indexes */
+               if(bvh->pack.prim_index.size()) {
+                       size_t bvh_prim_index_size = bvh->pack.prim_index.size();
+                       int *bvh_prim_index = &bvh->pack.prim_index[0];
+                       uint *bvh_prim_visibility = &bvh->pack.prim_visibility[0];
+
+                       for(size_t i = 0; i < bvh_prim_index_size; i++) {
+                               pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + mesh_tri_offset;
+                               pack_prim_visibility[pack_prim_index_offset] = bvh_prim_visibility[i];
+                               pack_prim_object[pack_prim_index_offset] = 0;  // unused for instances
+                               pack_prim_index_offset++;
+                       }
+               }
+
+               /* merge triangle intersection data */
+               if(bvh->pack.tri_woop.size()) {
+                       memcpy(pack_tri_woop+pack_tri_woop_offset, &bvh->pack.tri_woop[0],
+                               bvh->pack.tri_woop.size()*sizeof(float4));
+                       pack_tri_woop_offset += bvh->pack.tri_woop.size();
+               }
+
+               /* merge nodes */
+               if( bvh->pack.nodes.size()) {
+                       size_t nsize_bbox = (use_qbvh)? nsize-2: nsize-1;
+                       int4 *bvh_nodes = &bvh->pack.nodes[0];
+                       size_t bvh_nodes_size = bvh->pack.nodes.size(); 
+                       int *bvh_is_leaf = &bvh->pack.is_leaf[0];
+
+                       for(size_t i = 0, j = 0; i < bvh_nodes_size; i+=nsize, j++) {
+                               memcpy(pack_nodes + pack_nodes_offset, bvh_nodes + i, nsize_bbox*sizeof(int4));
+
+                               /* modify offsets into arrays */
+                               int4 data = bvh_nodes[i + nsize_bbox];
+
+                               if(bvh_is_leaf[j]) {
+                                       data.x += tri_offset;
+                                       data.y += tri_offset;
+                               }
+                               else {
+                                       data.x += (data.x < 0)? -noffset: noffset;
+                                       data.y += (data.y < 0)? -noffset: noffset;
+
+                                       if(use_qbvh) {
+                                               data.z += (data.z < 0)? -noffset: noffset;
+                                               data.w += (data.w < 0)? -noffset: noffset;
+                                       }
+                               }
+
+                               pack_nodes[pack_nodes_offset + nsize_bbox] = data;
+
+                               if(use_qbvh)
+                                       pack_nodes[pack_nodes_offset + nsize_bbox+1] = bvh_nodes[i + nsize_bbox+1];
+
+                               pack_nodes_offset += nsize;
+                       }
+               }
+
+               nodes_offset += bvh->pack.nodes.size();
+               tri_offset += bvh->pack.prim_index.size();
+       }
+}
+
+/* Regular BVH */
+
+RegularBVH::RegularBVH(const BVHParams& params_, const vector<Object*>& objects_)
+: BVH(params_, objects_)
+{
+}
+
+void RegularBVH::pack_leaf(const BVHStackEntry& e, const LeafNode *leaf)
+{
+       if(leaf->num_triangles() == 1 && pack.prim_index[leaf->m_lo] == -1)
+               /* object */
+               pack_node(e.idx, leaf->m_bounds, leaf->m_bounds, ~(leaf->m_lo), 0, leaf->m_visibility, leaf->m_visibility);
+       else
+               /* triangle */
+               pack_node(e.idx, leaf->m_bounds, leaf->m_bounds, leaf->m_lo, leaf->m_hi, leaf->m_visibility, leaf->m_visibility);
+}
+
+void RegularBVH::pack_inner(const BVHStackEntry& e, const BVHStackEntry& e0, const BVHStackEntry& e1)
+{
+       pack_node(e.idx, e0.node->m_bounds, e1.node->m_bounds, e0.encodeIdx(), e1.encodeIdx(), e0.node->m_visibility, e1.node->m_visibility);
+}
+
+void RegularBVH::pack_node(int idx, const BoundBox& b0, const BoundBox& b1, int c0, int c1, uint visibility0, uint visibility1)
+{
+       int4 data[BVH_NODE_SIZE] =
+       {
+               make_int4(__float_as_int(b0.min.x), __float_as_int(b0.max.x), __float_as_int(b0.min.y), __float_as_int(b0.max.y)),
+               make_int4(__float_as_int(b1.min.x), __float_as_int(b1.max.x), __float_as_int(b1.min.y), __float_as_int(b1.max.y)),
+               make_int4(__float_as_int(b0.min.z), __float_as_int(b0.max.z), __float_as_int(b1.min.z), __float_as_int(b1.max.z)),
+               make_int4(c0, c1, visibility0, visibility1)
+       };
+
+       memcpy(&pack.nodes[idx * BVH_NODE_SIZE], data, sizeof(int4)*BVH_NODE_SIZE);
+}
+
+void RegularBVH::pack_nodes(const array<int>& prims, const BVHNode *root)
+{
+       size_t node_size = root->getSubtreeSize(BVH_STAT_NODE_COUNT);
+
+       /* resize arrays */
+       pack.nodes.clear();
+       pack.is_leaf.clear();
+       pack.is_leaf.resize(node_size);
+
+       /* for top level BVH, first merge existing BVH's so we know the offsets */
+       if(params.top_level)
+               pack_instances(node_size*BVH_NODE_SIZE);
+       else
+               pack.nodes.resize(node_size*BVH_NODE_SIZE);
+
+       int nextNodeIdx = 0;
+
+       vector<BVHStackEntry> stack;
+       stack.push_back(BVHStackEntry(root, nextNodeIdx++));
+
+       while(stack.size()) {
+               BVHStackEntry e = stack.back();
+               stack.pop_back();
+
+               pack.is_leaf[e.idx] = e.node->is_leaf();
+
+               if(e.node->is_leaf()) {
+                       /* leaf node */
+                       const LeafNode* leaf = reinterpret_cast<const LeafNode*>(e.node);
+                       pack_leaf(e, leaf);
+               }
+               else {
+                       /* innner node */
+                       stack.push_back(BVHStackEntry(e.node->get_child(0), nextNodeIdx++));
+                       stack.push_back(BVHStackEntry(e.node->get_child(1), nextNodeIdx++));
+
+                       pack_inner(e, stack[stack.size()-2], stack[stack.size()-1]);
+               }
+       }
+
+       /* root index to start traversal at, to handle case of single leaf node */
+       pack.root_index = (pack.is_leaf[0])? -1: 0;
+}
+
+void RegularBVH::refit_nodes()
+{
+       assert(!params.top_level);
+
+       BoundBox bbox;
+       uint visibility = 0;
+       refit_node(0, (pack.is_leaf[0])? true: false, bbox, visibility);
+}
+
+void RegularBVH::refit_node(int idx, bool leaf, BoundBox& bbox, uint& visibility)
+{
+       int4 *data = &pack.nodes[idx*4];
+
+       int c0 = data[3].x;
+       int c1 = data[3].y;
+
+       if(leaf) {
+               /* refit leaf node */
+               for(int tri = c0; tri < c1; tri++) {
+                       int tidx = pack.prim_index[tri];
+                       int tob = pack.prim_object[tri];
+                       Object *ob = objects[tob];
+
+                       if(tidx == -1) {
+                               /* object instance */
+                               bbox.grow(ob->bounds);
+                       }
+                       else {
+                               /* triangles */
+                               const Mesh *mesh = ob->mesh;
+                               int tri_offset = (params.top_level)? mesh->tri_offset: 0;
+                               const int *vidx = mesh->triangles[tidx - tri_offset].v;
+                               const float3 *vpos = &mesh->verts[0];
+
+                               bbox.grow(vpos[vidx[0]]);
+                               bbox.grow(vpos[vidx[1]]);
+                               bbox.grow(vpos[vidx[2]]);
+                       }
+
+                       visibility |= ob->visibility;
+               }
+
+               pack_node(idx, bbox, bbox, c0, c1, visibility, visibility);
+       }
+       else {
+               /* refit inner node, set bbox from children */
+               BoundBox bbox0, bbox1;
+               uint visibility0 = 0, visibility1 = 0;
+
+               refit_node((c0 < 0)? -c0-1: c0, (c0 < 0), bbox0, visibility0);
+               refit_node((c1 < 0)? -c1-1: c1, (c1 < 0), bbox1, visibility1);
+
+               pack_node(idx, bbox0, bbox1, c0, c1, visibility0, visibility1);
+
+               bbox.grow(bbox0);
+               bbox.grow(bbox1);
+               visibility = visibility0|visibility1;
+       }
+}
+
+/* QBVH */
+
+QBVH::QBVH(const BVHParams& params_, const vector<Object*>& objects_)
+: BVH(params_, objects_)
+{
+       params.use_qbvh = true;
+
+       /* todo: use visibility */
+}
+
+void QBVH::pack_leaf(const BVHStackEntry& e, const LeafNode *leaf)
+{
+       float4 data[BVH_QNODE_SIZE];
+
+       memset(data, 0, sizeof(data));
+
+       if(leaf->num_triangles() == 1 && pack.prim_index[leaf->m_lo] == -1) {
+               /* object */
+               data[6].x = __int_as_float(~(leaf->m_lo));
+               data[6].y = __int_as_float(0);
+       }
+       else {
+               /* triangle */
+               data[6].x = __int_as_float(leaf->m_lo);
+               data[6].y = __int_as_float(leaf->m_hi);
+       }
+
+       memcpy(&pack.nodes[e.idx * BVH_QNODE_SIZE], data, sizeof(float4)*BVH_QNODE_SIZE);
+}
+
+void QBVH::pack_inner(const BVHStackEntry& e, const BVHStackEntry *en, int num)
+{
+       float4 data[BVH_QNODE_SIZE];
+
+       for(int i = 0; i < num; i++) {
+               float3 bb_min = en[i].node->m_bounds.min;
+               float3 bb_max = en[i].node->m_bounds.max;
+
+               data[0][i] = bb_min.x;
+               data[1][i] = bb_max.x;
+               data[2][i] = bb_min.y;
+               data[3][i] = bb_max.y;
+               data[4][i] = bb_min.z;
+               data[5][i] = bb_max.z;
+
+               data[6][i] = __int_as_float(en[i].encodeIdx());
+               data[7][i] = 0.0f;
+       }
+
+       for(int i = num; i < 4; i++) {
+               data[0][i] = 0.0f;
+               data[1][i] = 0.0f;
+               data[2][i] = 0.0f;
+
+               data[3][i] = 0.0f;
+               data[4][i] = 0.0f;
+               data[5][i] = 0.0f;
+
+               data[6][i] = __int_as_float(0);
+               data[7][i] = 0.0f;
+       }
+
+       memcpy(&pack.nodes[e.idx * BVH_QNODE_SIZE], data, sizeof(float4)*BVH_QNODE_SIZE);
+}
+
+/* Quad SIMD Nodes */
+
+void QBVH::pack_nodes(const array<int>& prims, const BVHNode *root)
+{
+       size_t node_size = root->getSubtreeSize(BVH_STAT_NODE_COUNT);
+
+       /* resize arrays */
+       pack.nodes.clear();
+       pack.is_leaf.clear();
+       pack.is_leaf.resize(node_size);
+
+       /* for top level BVH, first merge existing BVH's so we know the offsets */
+       if(params.top_level)
+               pack_instances(node_size*BVH_QNODE_SIZE);
+       else
+               pack.nodes.resize(node_size*BVH_QNODE_SIZE);
+
+       int nextNodeIdx = 0;
+
+       vector<BVHStackEntry> stack;
+       stack.push_back(BVHStackEntry(root, nextNodeIdx++));
+
+       while(stack.size()) {
+               BVHStackEntry e = stack.back();
+               stack.pop_back();
+
+               pack.is_leaf[e.idx] = e.node->is_leaf();
+
+               if(e.node->is_leaf()) {
+                       /* leaf node */
+                       const LeafNode* leaf = reinterpret_cast<const LeafNode*>(e.node);
+                       pack_leaf(e, leaf);
+               }
+               else {
+                       /* inner node */
+                       const BVHNode *node = e.node;
+                       const BVHNode *node0 = node->get_child(0);
+                       const BVHNode *node1 = node->get_child(1);
+
+                       /* collect nodes */
+                       const BVHNode *nodes[4];
+                       int numnodes = 0;
+
+                       if(node0->is_leaf()) {
+                               nodes[numnodes++] = node0;
+                       }
+                       else {
+                               nodes[numnodes++] = node0->get_child(0);
+                               nodes[numnodes++] = node0->get_child(1);
+                       }
+
+                       if(node1->is_leaf()) {
+                               nodes[numnodes++] = node1;
+                       }
+                       else {
+                               nodes[numnodes++] = node1->get_child(0);
+                               nodes[numnodes++] = node1->get_child(1);
+                       }
+
+                       /* push entries on the stack */
+                       for(int i = 0; i < numnodes; i++)
+                               stack.push_back(BVHStackEntry(nodes[i], nextNodeIdx++));
+
+                       /* set node */
+                       pack_inner(e, &stack[stack.size()-numnodes], numnodes);
+               }
+       }
+
+       /* root index to start traversal at, to handle case of single leaf node */
+       pack.root_index = (pack.is_leaf[0])? -1: 0;
+}
+
+void QBVH::refit_nodes()
+{
+       assert(0); /* todo */
+}
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/bvh/bvh.h b/intern/cycles/bvh/bvh.h
new file mode 100644 (file)
index 0000000..e502af7
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Adapted from code copyright 2009-2010 NVIDIA Corporation
+ * Modifications Copyright 2011, Blender Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BVH_H__
+#define __BVH_H__
+
+#include "bvh_params.h"
+
+#include "util_types.h"
+#include "util_vector.h"
+
+CCL_NAMESPACE_BEGIN
+
+class BVHNode;
+struct BVHStackEntry;
+class BVHParams;
+class BoundBox;
+class CacheData;
+class LeafNode;
+class Object;
+class Progress;
+
+#define BVH_NODE_SIZE  4
+#define BVH_QNODE_SIZE 8
+#define BVH_ALIGN              4096
+#define TRI_NODE_SIZE  3
+
+/* Packed BVH
+ *
+ * BVH stored as it will be used for traversal on the rendering device. */
+
+struct PackedBVH {
+       /* BVH nodes storage, one node is 4x int4, and contains two bounding boxes,
+          and child, triangle or object indexes dependening on the node type */
+       array<int4> nodes; 
+       /* object index to BVH node index mapping for instances */
+       array<int> object_node; 
+       /* precomputed triangle intersection data, one triangle is 4x float4 */
+       array<float4> tri_woop; 
+       /* visibility visibilitys for primitives */
+       array<uint> prim_visibility;
+       /* mapping from BVH primitive index to true primitive index, as primitives
+          may be duplicated due to spatial splits. -1 for instances. */
+       array<int> prim_index;
+       /* mapping from BVH primitive index, to the object id of that primitive. */
+       array<int> prim_object;
+       /* quick array to lookup if a node is a leaf, not used for traversal, only
+          for instance BVH merging  */
+       array<int> is_leaf;
+
+       /* index of the root node. */
+       int root_index;
+
+       /* surface area heuristic, for building top level BVH */
+       float SAH;
+
+       PackedBVH()
+       {
+               root_index = 0;
+               SAH = 0.0f;
+       }
+};
+
+/* BVH */
+
+class BVH
+{
+public:
+       PackedBVH pack;
+       BVHParams params;
+       vector<Object*> objects;
+
+       static BVH *create(const BVHParams& params, const vector<Object*>& objects);
+       virtual ~BVH() {}
+
+       void build(Progress& progress);
+       void refit(Progress& progress);
+
+protected:
+       BVH(const BVHParams& params, const vector<Object*>& objects);
+
+       /* cache */
+       bool cache_read(CacheData& key);
+       void cache_write(CacheData& key);
+
+       /* triangles */
+       void pack_triangles();
+       void pack_triangle(int idx, float4 woop[3]);
+
+       /* merge instance BVH's */
+       void pack_instances(size_t nodes_size);
+
+       /* for subclasses to implement */
+       virtual void pack_nodes(const array<int>& prims, const BVHNode *root) = 0;
+       virtual void refit_nodes() = 0;
+};
+
+/* Regular BVH
+ *
+ * Typical BVH with each node having two children. */
+
+class RegularBVH : public BVH {
+protected:
+       /* constructor */
+       friend class BVH;
+       RegularBVH(const BVHParams& params, const vector<Object*>& objects);
+
+       /* pack */
+       void pack_nodes(const array<int>& prims, const BVHNode *root);
+       void pack_leaf(const BVHStackEntry& e, const LeafNode *leaf);
+       void pack_inner(const BVHStackEntry& e, const BVHStackEntry& e0, const BVHStackEntry& e1);
+       void pack_node(int idx, const BoundBox& b0, const BoundBox& b1, int c0, int c1, uint visibility0, uint visibility1);
+
+       /* refit */
+       void refit_nodes();
+       void refit_node(int idx, bool leaf, BoundBox& bbox, uint& visibility);
+};
+
+/* QBVH
+ *
+ * Quad BVH, with each node having four children, to use with SIMD instructions. */
+
+class QBVH : public BVH {
+protected:
+       /* constructor */
+       friend class BVH;
+       QBVH(const BVHParams& params, const vector<Object*>& objects);
+
+       /* pack */
+       void pack_nodes(const array<int>& prims, const BVHNode *root);
+       void pack_leaf(const BVHStackEntry& e, const LeafNode *leaf);
+       void pack_inner(const BVHStackEntry& e, const BVHStackEntry *en, int num);
+
+       /* refit */
+       void refit_nodes();
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __BVH_H__ */
+
diff --git a/intern/cycles/bvh/bvh_build.cpp b/intern/cycles/bvh/bvh_build.cpp
new file mode 100644 (file)
index 0000000..67cff3f
--- /dev/null
@@ -0,0 +1,549 @@
+/*
+ * Adapted from code copyright 2009-2010 NVIDIA Corporation
+ * Modifications Copyright 2011, Blender Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "bvh_build.h"
+#include "bvh_node.h"
+#include "bvh_params.h"
+#include "bvh_sort.h"
+
+#include "mesh.h"
+#include "object.h"
+#include "scene.h"
+
+#include "util_algorithm.h"
+#include "util_foreach.h"
+#include "util_progress.h"
+#include "util_time.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Constructor / Destructor */
+
+BVHBuild::BVHBuild(const vector<Object*>& objects_,
+       vector<int>& prim_index_, vector<int>& prim_object_,
+       const BVHParams& params_, Progress& progress_)
+: objects(objects_),
+  prim_index(prim_index_),
+  prim_object(prim_object_),
+  params(params_),
+  progress(progress_),
+  progress_start_time(0.0)
+{
+       spatial_min_overlap = 0.0f;
+       progress_num_duplicates = 0;
+}
+
+BVHBuild::~BVHBuild()
+{
+}
+
+/* Adding References */
+
+void BVHBuild::add_reference_mesh(NodeSpec& root, Mesh *mesh, int i)
+{
+       for(uint j = 0; j < mesh->triangles.size(); j++) {
+               Mesh::Triangle t = mesh->triangles[j];
+               Reference ref;
+
+               ref.prim_index = j;
+               ref.prim_object = i;
+
+               for(int k = 0; k < 3; k++) {
+                       float3 pt = mesh->verts[t.v[k]];
+                       ref.bounds.grow(pt);
+               }
+
+               references.push_back(ref);
+               root.bounds.grow(ref.bounds);
+       }
+}
+
+void BVHBuild::add_reference_object(NodeSpec& root, Object *ob, int i)
+{
+       Reference ref;
+
+       ref.prim_index = -1;
+       ref.prim_object = i;
+       ref.bounds = ob->bounds;
+
+       references.push_back(ref);
+       root.bounds.grow(ref.bounds);
+}
+
+void BVHBuild::add_references(NodeSpec& root)
+{
+       /* init root spec */
+       root.num = 0;
+       root.bounds = BoundBox();
+
+       /* add objects */
+       int i = 0;
+
+       foreach(Object *ob, objects) {
+               if(params.top_level) {
+                       if(ob->mesh->transform_applied)
+                               add_reference_mesh(root, ob->mesh, i);
+                       else
+                               add_reference_object(root, ob, i);
+               }
+               else
+                       add_reference_mesh(root, ob->mesh, i);
+
+               i++;
+
+               if(progress.get_cancel()) return;
+       }
+
+       /* happens mostly on empty meshes */
+       if(!root.bounds.valid())
+               root.bounds.grow(make_float3(0.0f, 0.0f, 0.0f));
+
+       root.num = references.size();
+}
+
+/* Build */
+
+BVHNode* BVHBuild::run()
+{
+       NodeSpec root;
+
+       /* add references */
+       add_references(root);
+
+       if(progress.get_cancel()) return NULL;
+
+       /* init spatial splits */
+       if(params.top_level) /* todo: get rid of this */
+               params.use_spatial_split = false;
+
+       spatial_min_overlap = root.bounds.area() * params.spatial_split_alpha;
+       spatial_right_bounds.clear();
+       spatial_right_bounds.resize(max(root.num, (int)BVHParams::NUM_SPATIAL_BINS) - 1);
+
+       /* init progress updates */
+       progress_num_duplicates = 0;
+       progress_start_time = time_dt();
+
+       /* build recursively */
+       return build_node(root, 0, 0.0f, 1.0f);
+}
+
+void BVHBuild::progress_update(float progress_start, float progress_end)
+{
+       if(time_dt() - progress_start_time < 0.25f)
+               return;
+
+       float duplicates = (float)progress_num_duplicates/(float)references.size();
+       string msg = string_printf("Building BVH %.0f%%, duplicates %.0f%%",
+               progress_start*100.0f, duplicates*100.0f);
+
+       progress.set_substatus(msg);
+       progress_start_time = time_dt();
+}
+
+BVHNode* BVHBuild::build_node(const NodeSpec& spec, int level, float progress_start, float progress_end)
+{
+       /* progress update */
+       progress_update(progress_start, progress_end);
+       if(progress.get_cancel()) return NULL;
+
+       /* small enough or too deep =&g