svn merge -r41655:41715 ^/trunk/blender --- this is the real cycles merge, needs some edits to cycles its self before cycles will build

This commit is contained in:
Campbell Barton 2011-11-10 04:17:43 +00:00
commit 121ff4223d
366 changed files with 72305 additions and 242 deletions

@ -213,6 +213,10 @@ if(UNIX AND NOT APPLE)
endif() endif()
option(WITH_PYTHON_INSTALL "Copy system python into the blender install folder" ON) 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 # disable for now, but plan to support on all platforms eventually
option(WITH_MEM_JEMALLOC "Enable malloc replacement (http://www.canonware.com/jemalloc)" OFF) option(WITH_MEM_JEMALLOC "Enable malloc replacement (http://www.canonware.com/jemalloc)" OFF)
mark_as_advanced(WITH_MEM_JEMALLOC) mark_as_advanced(WITH_MEM_JEMALLOC)
@ -284,6 +288,12 @@ if(WITH_PYTHON_MODULE)
set(WITH_HEADLESS ON) set(WITH_HEADLESS ON)
endif() 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) TEST_SSE_SUPPORT(COMPILER_SSE_FLAG COMPILER_SSE2_FLAG)
# don't store paths to libs for portable distrobution # don't store paths to libs for portable distrobution
@ -492,12 +502,12 @@ if(UNIX AND NOT APPLE)
endif() endif()
if(WITH_BOOST) if(WITH_BOOST)
set(BOOST "/usr" CACHE PATH "Boost Directory")
# uses in build instructions to override include and library variables
if(NOT BOOST_CUSTOM) if(NOT BOOST_CUSTOM)
set(BOOST_ROOT ${BOOST})
set(Boost_USE_MULTITHREADED ON) 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)
mark_as_advanced(Boost_DIR) # why doesnt boost do this?
endif() endif()
set(BOOST_INCLUDE_DIR ${Boost_INCLUDE_DIRS}) set(BOOST_INCLUDE_DIR ${Boost_INCLUDE_DIRS})
@ -507,10 +517,7 @@ if(UNIX AND NOT APPLE)
endif() endif()
if(WITH_OPENIMAGEIO) if(WITH_OPENIMAGEIO)
set(OPENIMAGEIO "/usr" CACHE PATH "OpenImageIO Directory") find_package(OpenImageIO)
set(OPENIMAGEIO_ROOT_DIR ${OPENIMAGEIO})
find_package(OpenImageIO REQUIRED)
set(OPENIMAGEIO_LIBRARIES ${OPENIMAGEIO_LIBRARIES} ${PNG_LIBRARIES} ${JPEG_LIBRARIES} ${TIFF_LIBRARY} ${OPENEXR_LIBRARIES} ${ZLIB_LIBRARIES} ${BOOST_LIBRARIES}) 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 set(OPENIMAGEIO_LIBPATH) # TODO, remove and reference the absolute path everywhere
@ -518,6 +525,8 @@ if(UNIX AND NOT APPLE)
if(NOT OPENIMAGEIO_FOUND) if(NOT OPENIMAGEIO_FOUND)
set(WITH_OPENIMAGEIO OFF) set(WITH_OPENIMAGEIO OFF)
set(WITH_CYCLES OFF)
message(STATUS "OpenImageIO not found, disabling WITH_CYCLES")
endif() endif()
endif() endif()
@ -843,7 +852,7 @@ elseif(WIN32)
else() else()
# keep GCC specific stuff here # keep GCC specific stuff here
if(CMAKE_COMPILER_IS_GNUCC) if(CMAKE_COMPILER_IS_GNUCC)
set(PLATFORM_LINKLIBS "-lshell32 -lshfolder -lgdi32 -lmsvcrt -lwinmm -lmingw32 -lm -lws2_32 -lz -lstdc++ -lole32 -luuid -lwsock32") set(PLATFORM_LINKLIBS "-lshell32 -lshfolder -lgdi32 -lmsvcrt -lwinmm -lmingw32 -lm -lws2_32 -lz -lstdc++ -lole32 -luuid -lwsock32 -lpsapi")
set(PLATFORM_CFLAGS "-pipe -funsigned-char -fno-strict-aliasing") set(PLATFORM_CFLAGS "-pipe -funsigned-char -fno-strict-aliasing")
add_definitions(-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE) add_definitions(-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE)
@ -942,26 +951,26 @@ elseif(WIN32)
if(WITH_BOOST) if(WITH_BOOST)
set(BOOST ${LIBDIR}/boost) set(BOOST ${LIBDIR}/boost)
set(BOOST_INCLUDE_DIR ${BOOST}/include) set(BOOST_INCLUDE_DIR ${BOOST}/include)
set(BOOST_POSTFIX "vc90-mt-s-1_46_1") set(BOOST_POSTFIX "mgw45-mt-s-1_47")
set(BOOST_DEBUG_POSTFIX "vc90-mt-sgd-1_46_1") set(BOOST_DEBUG_POSTFIX "mgw45-mt-sd-1_47")
set(BOOST_LIBRARIES set(BOOST_LIBRARIES
optimized libboost_date_time-${BOOST_POSTFIX} libboost_filesystem-${BOOST_POSTFIX} optimized boost_date_time-${BOOST_POSTFIX} boost_filesystem-${BOOST_POSTFIX}
libboost_regex-${BOOST_POSTFIX} libboost_system-${BOOST_POSTFIX} libboost_thread-${BOOST_POSTFIX} boost_regex-${BOOST_POSTFIX} boost_system-${BOOST_POSTFIX} boost_thread-${BOOST_POSTFIX}
debug libboost_date_time-${BOOST_DEBUG_POSTFIX} libboost_filesystem-${BOOST_DEBUG_POSTFIX} debug boost_date_time-${BOOST_DEBUG_POSTFIX} boost_filesystem-${BOOST_DEBUG_POSTFIX}
libboost_regex-${BOOST_DEBUG_POSTFIX} libboost_system-${BOOST_DEBUG_POSTFIX} libboost_thread-${BOOST_DEBUG_POSTFIX}) boost_regex-${BOOST_DEBUG_POSTFIX} boost_system-${BOOST_DEBUG_POSTFIX} boost_thread-${BOOST_DEBUG_POSTFIX})
set(BOOST_LIBPATH ${BOOST}/lib) set(BOOST_LIBPATH ${BOOST}/lib/gcc)
set(BOOST_DEFINITIONS "-DBOOST_ALL_NO_LIB") set(BOOST_DEFINITIONS "-DBOOST_ALL_NO_LIB -DBOOST_THREAD_USE_LIB ")
endif() endif()
if(WITH_OPENIMAGEIO) if(WITH_OPENIMAGEIO)
set(OPENIMAGEIO ${LIBDIR}/openimageio) set(OPENIMAGEIO ${LIBDIR}/gcc/openimageio)
set(OPENIMAGEIO_INCLUDE_DIRS ${OPENIMAGEIO}/include) set(OPENIMAGEIO_INCLUDE_DIRS ${OPENIMAGEIO}/include)
set(OPENIMAGEIO_LIBRARIES OpenImageIO) set(OPENIMAGEIO_LIBRARIES OpenImageIO)
set(OPENIMAGEIO_LIBPATH ${OPENIMAGEIO}/lib) set(OPENIMAGEIO_LIBPATH ${OPENIMAGEIO}/lib)
set(OPENIMAGEIO_DEFINITIONS) set(OPENIMAGEIO_DEFINITIONS)
endif() endif()
set(PLATFORM_LINKFLAGS "--stack,2097152") set(PLATFORM_LINKFLAGS "-Xlinker --stack=2097152")
endif() endif()
@ -1480,6 +1489,7 @@ if(FIRST_RUN)
info_cfg_option(WITH_FFTW3) info_cfg_option(WITH_FFTW3)
info_cfg_option(WITH_INTERNATIONAL) info_cfg_option(WITH_INTERNATIONAL)
info_cfg_option(WITH_INPUT_NDOF) info_cfg_option(WITH_INPUT_NDOF)
info_cfg_option(WITH_CYCLES)
info_cfg_text("Compiler Options:") info_cfg_text("Compiler Options:")
info_cfg_option(WITH_BUILDINFO) info_cfg_option(WITH_BUILDINFO)

@ -435,12 +435,12 @@ B.init_lib_dict()
Export('env') Export('env')
BuildDir(B.root_build_dir+'/source', 'source', duplicate=0)
SConscript(B.root_build_dir+'/source/SConscript')
BuildDir(B.root_build_dir+'/intern', 'intern', duplicate=0) BuildDir(B.root_build_dir+'/intern', 'intern', duplicate=0)
SConscript(B.root_build_dir+'/intern/SConscript') SConscript(B.root_build_dir+'/intern/SConscript')
BuildDir(B.root_build_dir+'/extern', 'extern', duplicate=0) BuildDir(B.root_build_dir+'/extern', 'extern', duplicate=0)
SConscript(B.root_build_dir+'/extern/SConscript') SConscript(B.root_build_dir+'/extern/SConscript')
BuildDir(B.root_build_dir+'/source', 'source', duplicate=0)
SConscript(B.root_build_dir+'/source/SConscript')
# now that we have read all SConscripts, we know what # now that we have read all SConscripts, we know what
# libraries will be built. Create list of # libraries will be built. Create list of
@ -526,6 +526,50 @@ if env['OURPLATFORM']!='darwin':
if len(source)==0: if len(source)==0:
env.Execute(Mkdir(dir)) env.Execute(Mkdir(dir))
scriptinstall.append(env.Install(dir=dir,source=source)) scriptinstall.append(env.Install(dir=dir,source=source))
if env['WITH_BF_CYCLES']:
# cycles python code
dir=os.path.join(env['BF_INSTALLDIR'], VERSION, 'scripts', 'addons','cycles')
source=os.listdir('intern/cycles/blender/addon')
if '.svn' in source: source.remove('.svn')
if '_svn' in source: source.remove('_svn')
if '__pycache__' in source: source.remove('__pycache__')
source=['intern/cycles/blender/addon/'+s for s in source]
scriptinstall.append(env.Install(dir=dir,source=source))
# cycles kernel code
dir=os.path.join(env['BF_INSTALLDIR'], VERSION, 'scripts', 'addons','cycles', 'kernel')
source=os.listdir('intern/cycles/kernel')
if '.svn' in source: source.remove('.svn')
if '_svn' in source: source.remove('_svn')
if '__pycache__' in source: source.remove('__pycache__')
source.remove('kernel.cpp')
source.remove('CMakeLists.txt')
source.remove('svm')
source.remove('osl')
source=['intern/cycles/kernel/'+s for s in source]
source.append('intern/cycles/util/util_color.h')
source.append('intern/cycles/util/util_math.h')
source.append('intern/cycles/util/util_transform.h')
source.append('intern/cycles/util/util_types.h')
scriptinstall.append(env.Install(dir=dir,source=source))
# svm
dir=os.path.join(env['BF_INSTALLDIR'], VERSION, 'scripts', 'addons','cycles', 'kernel', 'svm')
source=os.listdir('intern/cycles/kernel/svm')
if '.svn' in source: source.remove('.svn')
if '_svn' in source: source.remove('_svn')
if '__pycache__' in source: source.remove('__pycache__')
source=['intern/cycles/kernel/svm/'+s for s in source]
scriptinstall.append(env.Install(dir=dir,source=source))
# licenses
dir=os.path.join(env['BF_INSTALLDIR'], VERSION, 'scripts', 'addons','cycles', 'license')
source=os.listdir('intern/cycles/doc/license')
if '.svn' in source: source.remove('.svn')
if '_svn' in source: source.remove('_svn')
if '__pycache__' in source: source.remove('__pycache__')
source.remove('CMakeLists.txt')
source=['intern/cycles/doc/license/'+s for s in source]
scriptinstall.append(env.Install(dir=dir,source=source))
if env['WITH_BF_INTERNATIONAL']: if env['WITH_BF_INTERNATIONAL']:
internationalpaths=['release' + os.sep + 'datafiles'] internationalpaths=['release' + os.sep + 'datafiles']
@ -696,6 +740,9 @@ if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'win64-vc', 'linuxcross'):
dllsources.append('${LCGDIR}/thumbhandler/lib/BlendThumb.dll') dllsources.append('${LCGDIR}/thumbhandler/lib/BlendThumb.dll')
dllsources.append('${LCGDIR}/thumbhandler/lib/BlendThumb64.dll') dllsources.append('${LCGDIR}/thumbhandler/lib/BlendThumb64.dll')
if env['WITH_BF_OIIO']:
dllsources.append('${LCGDIR}/openimageio/bin/OpenImageIO.dll')
dllsources.append('#source/icons/blender.exe.manifest') dllsources.append('#source/icons/blender.exe.manifest')
windlls = env.Install(dir=env['BF_INSTALLDIR'], source = dllsources) windlls = env.Install(dir=env['BF_INSTALLDIR'], source = dllsources)

@ -34,7 +34,7 @@ BF_FFMPEG_LIB_STATIC = '${BF_FFMPEG_LIBPATH}/libavformat.a ${BF_FFMPEG_LIBPATH}/
# Don't depend on system's libstdc++ # Don't depend on system's libstdc++
WITH_BF_STATICCXX = True WITH_BF_STATICCXX = True
BF_CXX_LIB_STATIC = '/usr/lib/gcc/i486-linux-gnu/4.3.2/libstdc++.a' BF_CXX_LIB_STATIC = '/usr/lib/gcc/i486-linux-gnu/4.3.4/libstdc++.a'
WITH_BF_OPENAL = True WITH_BF_OPENAL = True
WITH_BF_STATICOPENAL = True WITH_BF_STATICOPENAL = True
@ -92,9 +92,24 @@ WITH_BF_STATICFFTW3 = True
# JACK # JACK
WITH_BF_JACK = True WITH_BF_JACK = True
# Cycles
WITH_BF_CYCLES = True
WITH_BF_OIIO = True
WITH_BF_STATICOIIO = True
BF_OIIO = '/opt/oiio'
BF_OIIO_INC = '${BF_OIIO}/include'
BF_OIIO_LIB_STATIC = '${BF_OIIO_LIBPATH}/libOpenImageIO.a ${BF_OPENEXR}/lib/libIlmImf.a'
BF_OIIO_LIBPATH = '${BF_OIIO}/lib'
WITH_BF_BOOST = True
WITH_BF_STATICBOOST = True
BF_BOOST = '/opt/boost'
BF_BOOST_INC = '${BF_BOOST}/include'
BF_BOOST_LIB_STATIC = '${BF_BOOST_LIBPATH}/libboost_filesystem.a ${BF_BOOST_LIBPATH}/libboost_date_time.a ${BF_BOOST_LIBPATH}/libboost_regex.a ${BF_BOOST_LIBPATH}/libboost_system.a ${BF_BOOST_LIBPATH}/libboost_thread.a'
BF_BOOST_LIBPATH = '${BF_BOOST}/lib'
# Compilation and optimization # Compilation and optimization
BF_DEBUG = False BF_DEBUG = False
REL_CFLAGS = [] REL_CCFLAGS = ['-O2', '-msse', '-msse2'] # C & C++
REL_CXXFLAGS = []
REL_CCFLAGS = ['-O2'] # C & C++
PLATFORM_LINKFLAGS = ['-L/home/sources/staticlibs/lib32'] PLATFORM_LINKFLAGS = ['-L/home/sources/staticlibs/lib32']

@ -28,7 +28,7 @@ BF_FFMPEG_LIB_STATIC = '${BF_FFMPEG_LIBPATH}/libavformat.a ${BF_FFMPEG_LIBPATH}/
# Don't depend on system's libstdc++ # Don't depend on system's libstdc++
WITH_BF_STATICCXX = True WITH_BF_STATICCXX = True
BF_CXX_LIB_STATIC = '/usr/lib/gcc/i486-linux-gnu/4.3.2/libstdc++.a' BF_CXX_LIB_STATIC = '/usr/lib/gcc/i486-linux-gnu/4.3.4/libstdc++.a'
WITH_BF_OPENAL = True WITH_BF_OPENAL = True
WITH_BF_STATICOPENAL = True WITH_BF_STATICOPENAL = True
@ -83,9 +83,10 @@ BF_3DMOUSE_LIBPATH = '${BF_3DMOUSE}/lib32'
# JACK # JACK
WITH_BF_JACK = True WITH_BF_JACK = True
# Motion Tracking
WITH_BF_LIBMV = False
# Compilation and optimization # Compilation and optimization
BF_DEBUG = False BF_DEBUG = False
REL_CFLAGS = [] REL_CCFLAGS = ['-O2', '-msse', '-msse2'] # C & C++
REL_CXXFLAGS = []
REL_CCFLAGS = ['-O2'] # C & C++
PLATFORM_LINKFLAGS = ['-L/home/sources/staticlibs/lib32'] PLATFORM_LINKFLAGS = ['-L/home/sources/staticlibs/lib32']

@ -28,7 +28,7 @@ BF_FFMPEG_LIB_STATIC = '${BF_FFMPEG_LIBPATH}/libavformat.a ${BF_FFMPEG_LIBPATH}/
# Don't depend on system's libstdc++ # Don't depend on system's libstdc++
WITH_BF_STATICCXX = True WITH_BF_STATICCXX = True
BF_CXX_LIB_STATIC = '/usr/lib/gcc/x86_64-linux-gnu/4.3.2/libstdc++.a' BF_CXX_LIB_STATIC = '/usr/lib/gcc/x86_64-linux-gnu/4.3.4/libstdc++.a'
WITH_BF_OPENAL = True WITH_BF_OPENAL = True
WITH_BF_STATICOPENAL = True WITH_BF_STATICOPENAL = True
@ -83,9 +83,10 @@ BF_3DMOUSE_LIBPATH = '${BF_3DMOUSE}/lib64'
# JACK # JACK
WITH_BF_JACK = True WITH_BF_JACK = True
# Motion Tracking
WITH_BF_LIBMV = False
# Compilation and optimization # Compilation and optimization
BF_DEBUG = False BF_DEBUG = False
REL_CFLAGS = [] REL_CCFLAGS = ['-O2', '-msse', '-msse2'] # C & C++
REL_CXXFLAGS = []
REL_CCFLAGS = ['-O2'] # C & C++
PLATFORM_LINKFLAGS = ['-L/home/sources/staticlibs/lib64'] PLATFORM_LINKFLAGS = ['-L/home/sources/staticlibs/lib64']

@ -34,7 +34,7 @@ BF_FFMPEG_LIB_STATIC = '${BF_FFMPEG_LIBPATH}/libavformat.a ${BF_FFMPEG_LIBPATH}/
# Don't depend on system's libstdc++ # Don't depend on system's libstdc++
WITH_BF_STATICCXX = True WITH_BF_STATICCXX = True
BF_CXX_LIB_STATIC = '/usr/lib/gcc/x86_64-linux-gnu/4.3.2/libstdc++.a' BF_CXX_LIB_STATIC = '/usr/lib/gcc/x86_64-linux-gnu/4.3.4/libstdc++.a'
WITH_BF_OPENAL = True WITH_BF_OPENAL = True
WITH_BF_STATICOPENAL = True WITH_BF_STATICOPENAL = True
@ -92,9 +92,24 @@ WITH_BF_STATICFFTW3 = True
# JACK # JACK
WITH_BF_JACK = True WITH_BF_JACK = True
# Cycles
WITH_BF_CYCLES = True
WITH_BF_OIIO = True
WITH_BF_STATICOIIO = True
BF_OIIO = '/opt/oiio'
BF_OIIO_INC = '${BF_OIIO}/include'
BF_OIIO_LIB_STATIC = '${BF_OIIO_LIBPATH}/libOpenImageIO.a ${BF_OPENEXR}/lib/libIlmImf.a'
BF_OIIO_LIBPATH = '${BF_OIIO}/lib'
WITH_BF_BOOST = True
WITH_BF_STATICBOOST = True
BF_BOOST = '/opt/boost'
BF_BOOST_INC = '${BF_BOOST}/include'
BF_BOOST_LIB_STATIC = '${BF_BOOST_LIBPATH}/libboost_filesystem.a ${BF_BOOST_LIBPATH}/libboost_date_time.a ${BF_BOOST_LIBPATH}/libboost_regex.a ${BF_BOOST_LIBPATH}/libboost_system.a ${BF_BOOST_LIBPATH}/libboost_thread.a'
BF_BOOST_LIBPATH = '${BF_BOOST}/lib'
# Compilation and optimization # Compilation and optimization
BF_DEBUG = False BF_DEBUG = False
REL_CFLAGS = [] REL_CCFLAGS = ['-O2', '-msse', '-msse2'] # C & C++
REL_CXXFLAGS = []
REL_CCFLAGS = ['-O2'] # C & C++
PLATFORM_LINKFLAGS = ['-L/home/sources/staticlibs/lib64'] PLATFORM_LINKFLAGS = ['-L/home/sources/staticlibs/lib64']

@ -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
)

@ -172,6 +172,12 @@ def cmake_get_src(f):
pass pass
elif new_file.endswith(".def"): elif new_file.endswith(".def"):
pass pass
elif new_file.endswith(".cl"): # opencl
pass
elif new_file.endswith(".cu"): # cuda
pass
elif new_file.endswith(".osl"): # open shading language
pass
else: else:
raise Exception("unknown file type - not c or h %s -> %s" % (f, new_file)) raise Exception("unknown file type - not c or h %s -> %s" % (f, new_file))

@ -39,6 +39,7 @@ from project_info import (SIMPLE_PROJECTFILE,
# is_py, # is_py,
cmake_advanced_info, cmake_advanced_info,
cmake_compiler_defines, cmake_compiler_defines,
project_name_get,
) )
@ -59,7 +60,11 @@ def create_nb_project_main():
includes = list(set(includes) | set(dirname(f) for f in files if is_c_header(f))) includes = list(set(includes) | set(dirname(f) for f in files if is_c_header(f)))
includes.sort() includes.sort()
if 0:
PROJECT_NAME = "Blender" PROJECT_NAME = "Blender"
else:
# be tricky, get the project name from SVN if we can!
PROJECT_NAME = project_name_get(SOURCE_DIR)
# --------------- NB spesific # --------------- NB spesific
defines = [("%s=%s" % cdef) if cdef[1] else cdef[0] for cdef in defines] defines = [("%s=%s" % cdef) if cdef[1] else cdef[0] for cdef in defines]

@ -12,6 +12,7 @@ set(WITH_BUILTIN_GLEW OFF CACHE FORCE BOOL)
set(WITH_BULLET OFF CACHE FORCE BOOL) set(WITH_BULLET OFF CACHE FORCE BOOL)
set(WITH_CODEC_FFMPEG OFF CACHE FORCE BOOL) set(WITH_CODEC_FFMPEG OFF CACHE FORCE BOOL)
set(WITH_CODEC_SNDFILE 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_FFTW3 OFF CACHE FORCE BOOL)
set(WITH_LIBMV OFF CACHE FORCE BOOL) set(WITH_LIBMV OFF CACHE FORCE BOOL)
set(WITH_GAMEENGINE OFF CACHE FORCE BOOL) set(WITH_GAMEENGINE OFF CACHE FORCE BOOL)

@ -272,7 +272,7 @@ macro(setup_liblinks
target_link_libraries(${target} ${BOOST_LIBRARIES}) target_link_libraries(${target} ${BOOST_LIBRARIES})
endif() endif()
if(WITH_IMAGE_OPENEXR) if(WITH_IMAGE_OPENEXR)
if(WIN32 AND NOT UNIX) if(WIN32 AND NOT UNIX AND NOT CMAKE_COMPILER_IS_GNUCC)
file_list_suffix(OPENEXR_LIBRARIES_DEBUG "${OPENEXR_LIBRARIES}" "_d") file_list_suffix(OPENEXR_LIBRARIES_DEBUG "${OPENEXR_LIBRARIES}" "_d")
target_link_libraries_debug(${target} "${OPENEXR_LIBRARIES_DEBUG}") target_link_libraries_debug(${target} "${OPENEXR_LIBRARIES_DEBUG}")
target_link_libraries_optimized(${target} "${OPENEXR_LIBRARIES}") target_link_libraries_optimized(${target} "${OPENEXR_LIBRARIES}")

@ -282,6 +282,21 @@ BF_PCRE_LIBPATH = '${BF_PCRE}/lib'
#BF_EXPAT_LIB = 'expat' #BF_EXPAT_LIB = 'expat'
#BF_EXPAT_LIBPATH = '/usr/lib' #BF_EXPAT_LIBPATH = '/usr/lib'
#Cycles
WITH_BF_CYCLES = True
WITH_BF_OIIO = True
BF_OIIO = LIBDIR + '/openimageio'
BF_OIIO_INC = BF_OIIO + '/include'
BF_OIIO_LIB = 'OpenImageIO'
BF_OIIO_LIBPATH = BF_OIIO + '/lib'
WITH_BF_BOOST = True
BF_BOOST = LIBDIR + '/boost'
BF_BOOST_INC = BF_BOOST + '/include'
BF_BOOST_LIB = 'boost_date_time-mt boost_filesystem-mt boost_regex-mt boost_system-mt boost_thread-mt'
BF_BOOST_LIBPATH = BF_BOOST + '/lib'
#Ray trace optimization #Ray trace optimization
if MACOSX_ARCHITECTURE == 'x86_64' or MACOSX_ARCHITECTURE == 'i386': if MACOSX_ARCHITECTURE == 'x86_64' or MACOSX_ARCHITECTURE == 'i386':
WITH_BF_RAYOPTIMIZATION = True WITH_BF_RAYOPTIMIZATION = True

@ -178,6 +178,8 @@ BF_JEMALLOC_LIBPATH = '${BF_JEMALLOC}/lib'
BF_JEMALLOC_LIB = 'jemalloc' BF_JEMALLOC_LIB = 'jemalloc'
BF_JEMALLOC_LIB_STATIC = '${BF_JEMALLOC_LIBPATH}/libjemalloc.a' BF_JEMALLOC_LIB_STATIC = '${BF_JEMALLOC_LIBPATH}/libjemalloc.a'
WITH_BF_CYCLES = False
WITH_BF_OPENMP = True WITH_BF_OPENMP = True
#Ray trace optimization #Ray trace optimization

@ -17,7 +17,7 @@ BF_OPENAL_INC = '${BF_OPENAL}/include'
BF_OPENAL_LIB = 'wrap_oal' BF_OPENAL_LIB = 'wrap_oal'
BF_OPENAL_LIBPATH = '${BF_OPENAL}/lib' BF_OPENAL_LIBPATH = '${BF_OPENAL}/lib'
WITH_BF_FFMPEG = False WITH_BF_FFMPEG = True
BF_FFMPEG_LIB = 'avformat-53 avcodec-53 avdevice-53 avutil-51 swscale-2' BF_FFMPEG_LIB = 'avformat-53 avcodec-53 avdevice-53 avutil-51 swscale-2'
BF_FFMPEG_LIBPATH = LIBDIR + '/ffmpeg/lib' BF_FFMPEG_LIBPATH = LIBDIR + '/ffmpeg/lib'
BF_FFMPEG_INC = LIBDIR + '/ffmpeg/include' BF_FFMPEG_INC = LIBDIR + '/ffmpeg/include'
@ -46,11 +46,11 @@ BF_PTHREADS_INC = '${BF_PTHREADS}/include'
BF_PTHREADS_LIB = 'pthreadGC2' BF_PTHREADS_LIB = 'pthreadGC2'
BF_PTHREADS_LIBPATH = '${BF_PTHREADS}/lib' BF_PTHREADS_LIBPATH = '${BF_PTHREADS}/lib'
WITH_BF_OPENEXR = False # TODO, gives linking problems for the moment. WITH_BF_OPENEXR = True
WITH_BF_STATICOPENEXR = False WITH_BF_STATICOPENEXR = False
BF_OPENEXR = LIBDIR + '/gcc/openexr' BF_OPENEXR = LIBDIR + '/gcc/openexr'
BF_OPENEXR_INC = '${BF_OPENEXR}/include ${BF_OPENEXR}/include/OpenEXR' BF_OPENEXR_INC = '${BF_OPENEXR}/include ${BF_OPENEXR}/include/OpenEXR'
BF_OPENEXR_LIB = ' Half IlmImf Iex IlmThread ' BF_OPENEXR_LIB = 'Half IlmImf Imath IlmThread Iex'
BF_OPENEXR_LIBPATH = '${BF_OPENEXR}/lib' BF_OPENEXR_LIBPATH = '${BF_OPENEXR}/lib'
# Warning, this static lib configuration is untested! users of this OS please confirm. # Warning, this static lib configuration is untested! users of this OS please confirm.
BF_OPENEXR_LIB_STATIC = '${BF_OPENEXR}/lib/libHalf.a ${BF_OPENEXR}/lib/libIlmImf.a ${BF_OPENEXR}/lib/libIex.a ${BF_OPENEXR}/lib/libImath.a ${BF_OPENEXR}/lib/libIlmThread.a' BF_OPENEXR_LIB_STATIC = '${BF_OPENEXR}/lib/libHalf.a ${BF_OPENEXR}/lib/libIlmImf.a ${BF_OPENEXR}/lib/libIex.a ${BF_OPENEXR}/lib/libImath.a ${BF_OPENEXR}/lib/libIlmThread.a'
@ -100,8 +100,8 @@ BF_FFTW3_INC = '${BF_FFTW3}/include'
BF_FFTW3_LIB = 'fftw3' BF_FFTW3_LIB = 'fftw3'
BF_FFTW3_LIBPATH = '${BF_FFTW3}/lib' BF_FFTW3_LIBPATH = '${BF_FFTW3}/lib'
WITH_BF_GAMEENGINE = False WITH_BF_GAMEENGINE = True
WITH_BF_PLAYER = False WITH_BF_PLAYER = True
WITH_BF_BULLET = True WITH_BF_BULLET = True
BF_BULLET = '#extern/bullet2/src' BF_BULLET = '#extern/bullet2/src'
@ -140,7 +140,7 @@ BF_OPENGL_LIB_STATIC = [ '${BF_OPENGL}/lib/libGL.a', '${BF_OPENGL}/lib/libGLU.a'
'${BF_OPENGL}/lib/libXmu.a', '${BF_OPENGL}/lib/libXext.a', '${BF_OPENGL}/lib/libXmu.a', '${BF_OPENGL}/lib/libXext.a',
'${BF_OPENGL}/lib/libX11.a', '${BF_OPENGL}/lib/libXi.a' ] '${BF_OPENGL}/lib/libX11.a', '${BF_OPENGL}/lib/libXi.a' ]
WITH_BF_COLLADA = False # TODO, gives linking problems at the moment. WITH_BF_COLLADA = True
BF_COLLADA = '#source/blender/collada' BF_COLLADA = '#source/blender/collada'
BF_COLLADA_INC = '${BF_COLLADA}' BF_COLLADA_INC = '${BF_COLLADA}'
BF_COLLADA_LIB = 'bf_collada' BF_COLLADA_LIB = 'bf_collada'
@ -150,8 +150,23 @@ BF_OPENCOLLADA_INC = '${BF_OPENCOLLADA}/include'
BF_OPENCOLLADA_LIB = 'OpenCOLLADAStreamWriter OpenCOLLADASaxFrameworkLoader OpenCOLLADAFramework OpenCOLLADABaseUtils GeneratedSaxParser UTF MathMLSolver expat pcre buffer ftoa' BF_OPENCOLLADA_LIB = 'OpenCOLLADAStreamWriter OpenCOLLADASaxFrameworkLoader OpenCOLLADAFramework OpenCOLLADABaseUtils GeneratedSaxParser UTF MathMLSolver expat pcre buffer ftoa'
BF_OPENCOLLADA_LIBPATH = '${BF_OPENCOLLADA}/lib' BF_OPENCOLLADA_LIBPATH = '${BF_OPENCOLLADA}/lib'
#Cycles
WITH_BF_CYCLES = True
WITH_BF_OIIO = True
BF_OIIO = LIBDIR + '/gcc/openimageio'
BF_OIIO_INC = '#../lib/windows/gcc/openimageio/include'
BF_OIIO_LIB = 'OpenImageIO'
BF_OIIO_LIBPATH = '#../lib/windows/gcc/openimageio/lib'
WITH_BF_BOOST = True
BF_BOOST = LIBDIR + '/boost'
BF_BOOST_INC = '#../lib/windows/boost/include'
BF_BOOST_LIB = 'boost_date_time-mgw45-mt-s-1_47 boost_filesystem-mgw45-mt-s-1_47 boost_regex-mgw45-mt-s-1_47 boost_system-mgw45-mt-s-1_47 boost_thread-mgw45-mt-s-1_47'
BF_BOOST_LIBPATH = '#../lib/windows/boost/lib/gcc'
#Ray trace optimization #Ray trace optimization
WITH_BF_RAYOPTIMIZATION = False WITH_BF_RAYOPTIMIZATION = True
BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse'] BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse']
## ##
@ -161,7 +176,7 @@ CXX = 'g++'
CCFLAGS = [ '-pipe', '-funsigned-char', '-fno-strict-aliasing' ] CCFLAGS = [ '-pipe', '-funsigned-char', '-fno-strict-aliasing' ]
CXXFLAGS = [] CXXFLAGS = []
CPPFLAGS = ['-DWIN32', '-DFREE_WINDOWS', '-D_LARGEFILE_SOURCE', '-D_FILE_OFFSET_BITS=64', '-D_LARGEFILE64_SOURCE'] CPPFLAGS = ['-DWIN32', '-DFREE_WINDOWS', '-D_LARGEFILE_SOURCE', '-D_FILE_OFFSET_BITS=64', '-D_LARGEFILE64_SOURCE', '-DBOOST_ALL_NO_LIB', '-DBOOST_THREAD_USE_LIB', '-DGLEW_STATIC']
REL_CFLAGS = [] REL_CFLAGS = []
REL_CXXFLAGS = [] REL_CXXFLAGS = []
REL_CCFLAGS = ['-DNDEBUG', '-O2'] REL_CCFLAGS = ['-DNDEBUG', '-O2']
@ -170,9 +185,9 @@ C_WARN = ['-Wno-char-subscripts', '-Wdeclaration-after-statement', '-Wstrict-pro
CC_WARN = [ '-Wall' ] CC_WARN = [ '-Wall' ]
LLIBS = ['-lshell32', '-lshfolder', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz', '-lstdc++','-lole32','-luuid', '-lwsock32'] LLIBS = ['-lshell32', '-lshfolder', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz', '-lstdc++','-lole32','-luuid', '-lwsock32', '-lpsapi']
PLATFORM_LINKFLAGS = ['--stack,2097152'] PLATFORM_LINKFLAGS = ['-Xlinker', '--stack=2097152']
BF_DEBUG = False BF_DEBUG = False
BF_DEBUG_CCFLAGS= ['-g', '-D_DEBUG'] BF_DEBUG_CCFLAGS= ['-g', '-D_DEBUG']

@ -149,19 +149,20 @@ WITH_BF_3DMOUSE = True
WITH_BF_OPENMP = True WITH_BF_OPENMP = True
''' #Cycles
WITH_BF_CYCLES = True
WITH_BF_OIIO = True WITH_BF_OIIO = True
BF_OIIO = LIBDIR + '/openimageio' BF_OIIO = '${LIBDIR}/openimageio'
BF_OIIO_INC = '${BF_OIIO}/include' BF_OIIO_INC = '${BF_OIIO}/include'
BF_OIIO_LIB = 'OpenImageIO' BF_OIIO_LIB = 'OpenImageIO'
BF_OIIO_LIBPATH = '${BF_OIIO}/lib' BF_OIIO_LIBPATH = '${BF_OIIO}/lib'
WITH_BF_BOOST = True WITH_BF_BOOST = True
BF_BOOST = LIBDIR + '/boost' BF_BOOST = '${LIBDIR}/boost'
BF_BOOST_INC = '${BF_BOOST}/include' BF_BOOST_INC = '${BF_BOOST}/include'
BF_BOOST_LIB = 'libboost_date_time-vc90-mt-s-1_46_1 libboost_filesystem-vc90-mt-s-1_46_1 libboost_regex-vc90-mt-s-1_46_1 libboost_system-vc90-mt-s-1_46_1 libboost_thread-vc90-mt-s-1_46_1' BF_BOOST_LIB = 'libboost_date_time-vc90-mt-s-1_47 libboost_filesystem-vc90-mt-s-1_47 libboost_regex-vc90-mt-s-1_47 libboost_system-vc90-mt-s-1_47 libboost_thread-vc90-mt-s-1_47'
BF_BOOST_LIBPATH = '${BF_BOOST}/lib' BF_BOOST_LIBPATH = '${BF_BOOST}/lib'
'''
#Ray trace optimization #Ray trace optimization
WITH_BF_RAYOPTIMIZATION = True WITH_BF_RAYOPTIMIZATION = True

@ -153,19 +153,20 @@ WITH_BF_3DMOUSE = True
WITH_BF_OPENMP = True WITH_BF_OPENMP = True
''' WITH_BF_CYCLES = True
WITH_BF_OIIO = True WITH_BF_OIIO = True
BF_OIIO = LIBDIR + '/openimageio' BF_OIIO = '${LIBDIR}/openimageio'
BF_OIIO_INC = '${BF_OIIO}/include' BF_OIIO_INC = '${BF_OIIO}/include'
BF_OIIO_LIB = 'OpenImageIO' BF_OIIO_LIB = 'OpenImageIO'
BF_OIIO_LIBPATH = '${BF_OIIO}/lib' BF_OIIO_LIBPATH = '${BF_OIIO}/lib'
BF_OIIO_LIBPATH = '${BF_OIIO}/lib'
WITH_BF_BOOST = True WITH_BF_BOOST = True
BF_BOOST = LIBDIR + '/boost' BF_BOOST = '${LIBDIR}/boost'
BF_BOOST_INC = '${BF_BOOST}/include' BF_BOOST_INC = '${BF_BOOST}/include'
BF_BOOST_LIB = 'libboost_date_time-vc90-mt-s-1_45 libboost_filesystem-vc90-mt-s-1_45 libboost_regex-vc90-mt-s-1_45 libboost_system-vc90-mt-s-1_45 libboost_thread-vc90-mt-s-1_45' BF_BOOST_LIB = 'libboost_date_time-vc90-mt-s-1_47 libboost_filesystem-vc90-mt-s-1_47 libboost_regex-vc90-mt-s-1_47 libboost_system-vc90-mt-s-1_47 libboost_thread-vc90-mt-s-1_47'
BF_BOOST_LIBPATH = '${BF_BOOST}/lib' BF_BOOST_LIBPATH = '${BF_BOOST}/lib'
'''
#Ray trace optimization #Ray trace optimization
WITH_BF_RAYOPTIMIZATION = True WITH_BF_RAYOPTIMIZATION = True

@ -195,8 +195,13 @@ def setup_staticlibs(lenv):
if lenv['WITH_BF_OIIO']: if lenv['WITH_BF_OIIO']:
libincs += Split(lenv['BF_OIIO_LIBPATH']) libincs += Split(lenv['BF_OIIO_LIBPATH'])
if lenv['WITH_BF_STATICOIIO']:
statlibs += Split(lenv['BF_OIIO_LIB_STATIC'])
if lenv['WITH_BF_BOOST']: if lenv['WITH_BF_BOOST']:
libincs += Split(lenv['BF_BOOST_LIBPATH']) libincs += Split(lenv['BF_BOOST_LIBPATH'])
if lenv['WITH_BF_STATICBOOST']:
statlibs += Split(lenv['BF_BOOST_LIB_STATIC'])
# setting this last so any overriding of manually libs could be handled # setting this last so any overriding of manually libs could be handled
if lenv['OURPLATFORM'] not in ('win32-vc', 'win32-mingw', 'win64-vc', 'linuxcross'): if lenv['OURPLATFORM'] not in ('win32-vc', 'win32-mingw', 'win64-vc', 'linuxcross'):
@ -216,11 +221,7 @@ def setup_staticlibs(lenv):
return statlibs, libincs return statlibs, libincs
def setup_syslibs(lenv): def setup_syslibs(lenv):
syslibs = [ syslibs = []
lenv['BF_JPEG_LIB'],
lenv['BF_PNG_LIB'],
]
if not lenv['WITH_BF_FREETYPE_STATIC']: if not lenv['WITH_BF_FREETYPE_STATIC']:
syslibs += Split(lenv['BF_FREETYPE_LIB']) syslibs += Split(lenv['BF_FREETYPE_LIB'])
@ -241,6 +242,10 @@ def setup_syslibs(lenv):
syslibs += ['gomp'] syslibs += ['gomp']
if lenv['WITH_BF_ICONV']: if lenv['WITH_BF_ICONV']:
syslibs += Split(lenv['BF_ICONV_LIB']) syslibs += Split(lenv['BF_ICONV_LIB'])
if lenv['WITH_BF_OIIO']:
if not lenv['WITH_BF_STATICOIIO']:
syslibs += Split(lenv['BF_OIIO_LIB'])
if lenv['WITH_BF_OPENEXR'] and not lenv['WITH_BF_STATICOPENEXR']: if lenv['WITH_BF_OPENEXR'] and not lenv['WITH_BF_STATICOPENEXR']:
syslibs += Split(lenv['BF_OPENEXR_LIB']) syslibs += Split(lenv['BF_OPENEXR_LIB'])
if lenv['WITH_BF_TIFF'] and not lenv['WITH_BF_STATICTIFF']: if lenv['WITH_BF_TIFF'] and not lenv['WITH_BF_STATICTIFF']:
@ -280,11 +285,12 @@ def setup_syslibs(lenv):
if not lenv['WITH_BF_STATIC3DMOUSE']: if not lenv['WITH_BF_STATIC3DMOUSE']:
syslibs += Split(lenv['BF_3DMOUSE_LIB']) syslibs += Split(lenv['BF_3DMOUSE_LIB'])
if lenv['WITH_BF_OIIO']: if lenv['WITH_BF_BOOST'] and not lenv['WITH_BF_STATICBOOST']:
syslibs += Split(lenv['BF_OIIO_LIB'])
if lenv['WITH_BF_BOOST']:
syslibs += Split(lenv['BF_BOOST_LIB']) syslibs += Split(lenv['BF_BOOST_LIB'])
syslibs += Split(lenv['BF_JPEG_LIB'])
syslibs += Split(lenv['BF_PNG_LIB'])
syslibs += lenv['LLIBS'] syslibs += lenv['LLIBS']
return syslibs return syslibs
@ -550,10 +556,6 @@ def AppIt(target=None, source=None, env=None):
bldroot = env.Dir('.').abspath bldroot = env.Dir('.').abspath
binary = env['BINARYKIND'] binary = env['BINARYKIND']
if b=='verse':
print bc.OKBLUE+"no bundle for verse"+bc.ENDC
return 0
sourcedir = bldroot + '/source/darwin/%s.app'%binary sourcedir = bldroot + '/source/darwin/%s.app'%binary
sourceinfo = bldroot + "/source/darwin/%s.app/Contents/Info.plist"%binary sourceinfo = bldroot + "/source/darwin/%s.app/Contents/Info.plist"%binary
targetinfo = installdir +'/' + "%s.app/Contents/Info.plist"%binary targetinfo = installdir +'/' + "%s.app/Contents/Info.plist"%binary
@ -582,6 +584,23 @@ def AppIt(target=None, source=None, env=None):
cmd = 'cp -R %s/release/scripts %s/%s.app/Contents/MacOS/%s/'%(bldroot,installdir,binary,VERSION) cmd = 'cp -R %s/release/scripts %s/%s.app/Contents/MacOS/%s/'%(bldroot,installdir,binary,VERSION)
commands.getoutput(cmd) commands.getoutput(cmd)
if env['WITH_BF_CYCLES']:
croot = '%s/intern/cycles' % (bldroot)
cinstalldir = '%s/%s.app/Contents/MacOS/%s/scripts/addons/cycles' % (installdir,binary,VERSION)
cmd = 'mkdir %s' % (cinstalldir)
commands.getoutput(cmd)
cmd = 'mkdir %s/kernel' % (cinstalldir)
commands.getoutput(cmd)
cmd = 'cp -R %s/blender/addon/*.py %s/' % (croot, cinstalldir)
commands.getoutput(cmd)
cmd = 'cp -R %s/doc/license %s/license' % (croot, cinstalldir)
commands.getoutput(cmd)
cmd = 'cp -R %s/kernel/*.h %s/kernel/*.cl %s/kernel/*.cu %s/kernel/' % (croot, croot, croot, cinstalldir)
commands.getoutput(cmd)
cmd = 'cp -R %s/kernel/svm %s/util/util_color.h %s/util/util_math.h %s/util/util_transform.h %s/util/util_types.h %s/kernel/' % (croot, croot, croot, croot, croot, cinstalldir)
commands.getoutput(cmd)
if env['WITH_OSX_STATICPYTHON']: if env['WITH_OSX_STATICPYTHON']:
cmd = 'mkdir %s/%s.app/Contents/MacOS/%s/python/'%(installdir,binary, VERSION) cmd = 'mkdir %s/%s.app/Contents/MacOS/%s/python/'%(installdir,binary, VERSION)
commands.getoutput(cmd) commands.getoutput(cmd)

@ -156,8 +156,9 @@ def validate_arguments(args, bc):
'WITH_BF_JEMALLOC', 'WITH_BF_STATICJEMALLOC', 'BF_JEMALLOC', 'BF_JEMALLOC_INC', 'BF_JEMALLOC_LIBPATH', 'BF_JEMALLOC_LIB', 'BF_JEMALLOC_LIB_STATIC', 'WITH_BF_JEMALLOC', 'WITH_BF_STATICJEMALLOC', 'BF_JEMALLOC', 'BF_JEMALLOC_INC', 'BF_JEMALLOC_LIBPATH', 'BF_JEMALLOC_LIB', 'BF_JEMALLOC_LIB_STATIC',
'BUILDBOT_BRANCH', 'BUILDBOT_BRANCH',
'WITH_BF_3DMOUSE', 'WITH_BF_STATIC3DMOUSE', 'BF_3DMOUSE', 'BF_3DMOUSE_INC', 'BF_3DMOUSE_LIB', 'BF_3DMOUSE_LIBPATH', 'BF_3DMOUSE_LIB_STATIC', 'WITH_BF_3DMOUSE', 'WITH_BF_STATIC3DMOUSE', 'BF_3DMOUSE', 'BF_3DMOUSE_INC', 'BF_3DMOUSE_LIB', 'BF_3DMOUSE_LIBPATH', 'BF_3DMOUSE_LIB_STATIC',
'WITH_BF_OIIO', 'BF_OIIO', 'BF_OIIO_INC', 'BF_OIIO_LIB', 'BF_OIIO_LIBPATH', 'WITH_BF_CYCLES',
'WITH_BF_BOOST', 'BF_BOOST', 'BF_BOOST_INC', 'BF_BOOST_LIB', 'BF_BOOST_LIBPATH' 'WITH_BF_OIIO', 'WITH_BF_STATICOIIO', 'BF_OIIO', 'BF_OIIO_INC', 'BF_OIIO_LIB', 'BF_OIIO_LIB_STATIC', 'BF_OIIO_LIBPATH',
'WITH_BF_BOOST', 'WITH_BF_STATICBOOST', 'BF_BOOST', 'BF_BOOST_INC', 'BF_BOOST_LIB', 'BF_BOOST_LIB_STATIC', 'BF_BOOST_LIBPATH'
] ]
# Have options here that scons expects to be lists # Have options here that scons expects to be lists
@ -239,6 +240,7 @@ def read_opts(env, cfg, args):
localopts = Variables.Variables(cfg, args) localopts = Variables.Variables(cfg, args)
localopts.AddVariables( localopts.AddVariables(
('LCGDIR', 'location of cvs lib dir'), ('LCGDIR', 'location of cvs lib dir'),
('LIBDIR', 'root dir of libs'),
(BoolVariable('WITH_BF_PYTHON', 'Compile with python', True)), (BoolVariable('WITH_BF_PYTHON', 'Compile with python', True)),
(BoolVariable('WITH_BF_PYTHON_SAFETY', 'Internal API error checking to track invalid data to prevent crash on access (at the expense of some effeciency)', False)), (BoolVariable('WITH_BF_PYTHON_SAFETY', 'Internal API error checking to track invalid data to prevent crash on access (at the expense of some effeciency)', False)),
('BF_PYTHON', 'Base path for python', ''), ('BF_PYTHON', 'Base path for python', ''),
@ -536,16 +538,26 @@ def read_opts(env, cfg, args):
(BoolVariable('WITH_BF_CXX_GUARDEDALLOC', 'Enable GuardedAlloc for C++ memory allocation tracking.', False)), (BoolVariable('WITH_BF_CXX_GUARDEDALLOC', 'Enable GuardedAlloc for C++ memory allocation tracking.', False)),
('BUILDBOT_BRANCH', 'Buildbot branch name', ''), ('BUILDBOT_BRANCH', 'Buildbot branch name', ''),
) # end of opts.AddOptions()
localopts.AddVariables(
(BoolVariable('WITH_BF_CYCLES', 'Build with the Cycles engine', True)),
(BoolVariable('WITH_BF_OIIO', 'Build with OpenImageIO', False)), (BoolVariable('WITH_BF_OIIO', 'Build with OpenImageIO', False)),
(BoolVariable('WITH_BF_STATICOIIO', 'Staticly link to OpenImageIO', False)),
('BF_OIIO', 'OIIO root path', ''),
('BF_OIIO_INC', 'OIIO include path', ''), ('BF_OIIO_INC', 'OIIO include path', ''),
('BF_OIIO_LIB', 'OIIO library', ''), ('BF_OIIO_LIB', 'OIIO library', ''),
('BF_OIIO_LIBPATH', 'OIIO library path', ''), ('BF_OIIO_LIBPATH', 'OIIO library path', ''),
('BF_OIIO_LIB_STATIC', 'OIIO static library', ''),
(BoolVariable('WITH_BF_BOOST', 'Build with Boost', False)), (BoolVariable('WITH_BF_BOOST', 'Build with Boost', False)),
(BoolVariable('WITH_BF_STATICBOOST', 'Staticly link to boost', False)),
('BF_BOOST', 'Boost root path', ''),
('BF_BOOST_INC', 'Boost include path', ''), ('BF_BOOST_INC', 'Boost include path', ''),
('BF_BOOST_LIB', 'Boost library', ''), ('BF_BOOST_LIB', 'Boost library', ''),
('BF_BOOST_LIBPATH', 'Boost library path', '') ('BF_BOOST_LIBPATH', 'Boost library path', ''),
('BF_BOOST_LIB_STATIC', 'Boost static library', '')
) # end of opts.AddOptions() ) # end of opts.AddOptions()
return localopts return localopts

@ -57,3 +57,8 @@ endif()
if(WITH_IK_ITASC) if(WITH_IK_ITASC)
add_subdirectory(itasc) add_subdirectory(itasc)
endif() endif()
if(WITH_CYCLES)
add_subdirectory(cycles)
endif()

@ -25,6 +25,9 @@ NEW_CSG='false'
if env['WITH_BF_FLUID']: if env['WITH_BF_FLUID']:
SConscript(['elbeem/SConscript']) SConscript(['elbeem/SConscript'])
if env['WITH_BF_CYCLES']:
SConscript(['cycles/SConscript'])
if NEW_CSG=='false': if NEW_CSG=='false':
SConscript(['bsp/SConscript']) SConscript(['bsp/SConscript'])
else: else:

@ -98,7 +98,6 @@ set(SRC
intern/AUD_IWriter.h intern/AUD_IWriter.h
intern/AUD_JOSResampleFactory.cpp intern/AUD_JOSResampleFactory.cpp
intern/AUD_JOSResampleFactory.h intern/AUD_JOSResampleFactory.h
intern/AUD_JOSResampleReaderCoeff.cpp
intern/AUD_JOSResampleReader.cpp intern/AUD_JOSResampleReader.cpp
intern/AUD_JOSResampleReader.h intern/AUD_JOSResampleReader.h
intern/AUD_LinearResampleFactory.cpp intern/AUD_LinearResampleFactory.cpp

@ -0,0 +1,74 @@
set(CYCLES_INSTALL_PATH "scripts/addons/cycles")
set(WITH_CYCLES_BLENDER ON)
# External Libraries
include(cmake/external_libs.cmake)
# Build Flags
set(GCC_OPTIM_FLAGS "-ffast-math -msse -msse2 -msse3 -mtune=native")
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_OPTIM_FLAGS}")
set(RTTI_DISABLE_FLAGS "-fno-rtti -DBOOST_NO_RTTI -DBOOST_NO_TYPEID")
endif()
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 "${CMAKE_CXX_FLAGS} ${GCC_OPTIM_FLAGS}")
set(RTTI_DISABLE_FLAGS "-fno-rtti -DBOOST_NO_RTTI -DBOOST_NO_TYPEID")
endif()
endif()
if(UNIX AND NOT APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_OPTIM_FLAGS}")
set(RTTI_DISABLE_FLAGS "-fno-rtti -DBOOST_NO_RTTI -DBOOST_NO_TYPEID")
endif()
# 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)

40
intern/cycles/SConscript Normal file

@ -0,0 +1,40 @@
#!/usr/bin/python
from os import path
Import('env')
cycles = env.Clone()
cycles.Depends('../../source/blender/makesrna/intern/RNA_blender_cpp.h', 'makesrna')
sources = cycles.Glob('bvh/*.cpp') + cycles.Glob('device/*.cpp') + cycles.Glob('kernel/*.cpp') + cycles.Glob('render/*.cpp') + cycles.Glob('subd/*.cpp') + cycles.Glob('util/*.cpp') + cycles.Glob('util/*.c') + cycles.Glob('blender/*.cpp')
sources.remove(path.join('util', 'util_view.cpp'))
sources.remove(path.join('render', 'film_response.cpp'))
incs = []
defs = []
ccflags = []
cxxflags = []
defs.append('CCL_NAMESPACE_BEGIN=namespace ccl {')
defs.append('CCL_NAMESPACE_END=}')
defs.append('WITH_OPENCL')
defs.append('WITH_MULTI')
defs.append('WITH_CUDA')
if env['OURPLATFORM'] in ('win32-mingw'):
cxxflags.append('-fno-rtti -ffast-math -msse -msse2 -msse3 -mtune=native'.split())
ccflags.append('-ffast-math -msse -msse2 -msse3 -mtune=native'.split())
defs.append('BOOST_NO_RTTI BOOST_NO_TYPEID'.split())
incs.extend('. bvh render device kernel kernel/osl kernel/svm util subd'.split())
incs.extend('#intern/guardedalloc #source/blender/makesrna #source/blender/makesdna'.split())
incs.extend('#source/blender/blenloader ../../source/blender/makesrna/intern'.split())
incs.extend('#extern/glew/include'.split())
incs.append(cycles['BF_OIIO_INC'])
incs.append(cycles['BF_BOOST_INC'])
incs.append(cycles['BF_PYTHON_INC'])
cycles.BlenderLib('bf_intern_cycles', sources, incs, defs, libtype=['intern'], priority=[0], compileflags=[None], cc_compileflags=ccflags, cxx_compileflags=cxxflags)

@ -0,0 +1,69 @@
set(INC
.
../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()
include_directories(${INC})
if(WITH_CYCLES_TEST)
set(SRC
cycles_test.cpp
cycles_xml.cpp
cycles_xml.h
)
add_executable(cycles_test ${SRC})
target_link_libraries(cycles_test ${LIBRARIES})
if(UNIX AND NOT APPLE)
set_target_properties(cycles_test PROPERTIES INSTALL_RPATH $ORIGIN/lib)
endif()
unset(SRC)
endif()
if(WITH_CYCLES_NETWORK)
set(SRC
cycles_server.cpp
)
add_executable(cycles_server ${SRC})
target_link_libraries(cycles_server ${LIBRARIES})
if(UNIX AND NOT APPLE)
set_target_properties(cycles_server PROPERTIES INSTALL_RPATH $ORIGIN/lib)
endif()
unset(SRC)
endif()

@ -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;
}

@ -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;
}

@ -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

@ -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__ */

@ -0,0 +1,47 @@
set(INC
../render
../device
../kernel
../kernel/svm
../util
../subd
)
set(INC_SYS
${BLENDER_INCLUDE_DIRS}
${PYTHON_INCLUDE_DIRS}
${GLEW_INCLUDE_PATH}
)
set(SRC
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(ADDON_FILES
addon/__init__.py
addon/engine.py
addon/enums.py
addon/presets.py
addon/properties.py
addon/ui.py
addon/xml.py
)
blender_add_lib(bf_intern_cycles "${SRC}" "${INC}" "${INC_SYS}")
add_dependencies(bf_intern_cycles bf_rna)
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${ADDON_FILES}" ${CYCLES_INSTALL_PATH})

@ -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__)

@ -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()

@ -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"))

@ -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()

@ -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)

@ -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')

@ -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()

@ -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

@ -0,0 +1,321 @@
/*
* 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

@ -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

@ -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);
}

@ -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

@ -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__ */

@ -0,0 +1,702 @@
/*
* 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;
float strength = 1.0f;
if(b_lamp->type() == BL::Lamp::type_POINT ||
b_lamp->type() == BL::Lamp::type_SPOT ||
b_lamp->type() == BL::Lamp::type_AREA)
strength = 100.0f;
closure = graph->add(new EmissionNode());
closure->input("Color")->value = get_float3(b_lamp->color());
closure->input("Strength")->value.x = strength;
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

@ -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

@ -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__ */

@ -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__ */

@ -0,0 +1,28 @@
set(INC
.
../kernel
../kernel/svm
../render
../util
../device
)
set(SRC
bvh.cpp
bvh_build.cpp
bvh_node.cpp
bvh_sort.cpp
)
set(SRC_HEADERS
bvh.h
bvh_build.h
bvh_node.h
bvh_params.h
bvh_sort.h
)
include_directories(${INC})
add_library(cycles_bvh ${SRC} ${SRC_HEADERS})

701
intern/cycles/bvh/bvh.cpp Normal file

@ -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

155
intern/cycles/bvh/bvh.h Normal file

@ -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__ */

@ -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 => create leaf. */
if(spec.num <= params.min_leaf_size || level >= BVHParams::MAX_DEPTH)
return create_leaf_node(spec);
/* find split candidates. */
float area = spec.bounds.area();
float leafSAH = area * params.triangle_cost(spec.num);
float nodeSAH = area * params.node_cost(2);
ObjectSplit object = find_object_split(spec, nodeSAH);
SpatialSplit spatial;
if(params.use_spatial_split && level < BVHParams::MAX_SPATIAL_DEPTH) {
BoundBox overlap = object.left_bounds;
overlap.intersect(object.right_bounds);
if(overlap.area() >= spatial_min_overlap)
spatial = find_spatial_split(spec, nodeSAH);
}
/* leaf SAH is the lowest => create leaf. */
float minSAH = min(min(leafSAH, object.sah), spatial.sah);
if(minSAH == leafSAH && spec.num <= params.max_leaf_size)
return create_leaf_node(spec);
/* perform split. */
NodeSpec left, right;
if(params.use_spatial_split && minSAH == spatial.sah)
do_spatial_split(left, right, spec, spatial);
if(!left.num || !right.num)
do_object_split(left, right, spec, object);
/* create inner node. */
progress_num_duplicates += left.num + right.num - spec.num;
float progress_mid = lerp(progress_start, progress_end, (float)right.num / (float)(left.num + right.num));
BVHNode* rightNode = build_node(right, level + 1, progress_start, progress_mid);
if(progress.get_cancel()) {
if(rightNode) rightNode->deleteSubtree();
return NULL;
}
BVHNode* leftNode = build_node(left, level + 1, progress_mid, progress_end);
if(progress.get_cancel()) {
if(leftNode) leftNode->deleteSubtree();
return NULL;
}
return new InnerNode(spec.bounds, leftNode, rightNode);
}
BVHNode *BVHBuild::create_object_leaf_nodes(const Reference *ref, int num)
{
if(num == 0) {
BoundBox bounds;
return new LeafNode(bounds, 0, 0, 0);
}
else if(num == 1) {
prim_index.push_back(ref[0].prim_index);
prim_object.push_back(ref[0].prim_object);
uint visibility = objects[ref[0].prim_object]->visibility;
return new LeafNode(ref[0].bounds, visibility, prim_index.size()-1, prim_index.size());
}
else {
int mid = num/2;
BVHNode *leaf0 = create_object_leaf_nodes(ref, mid);
BVHNode *leaf1 = create_object_leaf_nodes(ref+mid, num-mid);
BoundBox bounds;
bounds.grow(leaf0->m_bounds);
bounds.grow(leaf1->m_bounds);
return new InnerNode(bounds, leaf0, leaf1);
}
}
BVHNode* BVHBuild::create_leaf_node(const NodeSpec& spec)
{
vector<int>& p_index = prim_index;
vector<int>& p_object = prim_object;
BoundBox bounds;
int num = 0;
uint visibility = 0;
for(int i = 0; i < spec.num; i++) {
if(references.back().prim_index != -1) {
p_index.push_back(references.back().prim_index);
p_object.push_back(references.back().prim_object);
bounds.grow(references.back().bounds);
visibility |= objects[references.back().prim_object]->visibility;
references.pop_back();
num++;
}
}
BVHNode *leaf = NULL;
if(num > 0) {
leaf = new LeafNode(bounds, visibility, p_index.size() - num, p_index.size());
if(num == spec.num)
return leaf;
}
/* while there may be multiple triangles in a leaf, for object primitives
* we want them to be the only one, so we */
int ob_num = spec.num - num;
const Reference *ref = (ob_num)? &references.back() - (ob_num - 1): NULL;
BVHNode *oleaf = create_object_leaf_nodes(ref, ob_num);
for(int i = 0; i < ob_num; i++)
references.pop_back();
if(leaf)
return new InnerNode(spec.bounds, leaf, oleaf);
else
return oleaf;
}
/* Object Split */
BVHBuild::ObjectSplit BVHBuild::find_object_split(const NodeSpec& spec, float nodeSAH)
{
ObjectSplit split;
const Reference *ref_ptr = &references[references.size() - spec.num];
for(int dim = 0; dim < 3; dim++) {
/* sort references */
bvh_reference_sort(references.size() - spec.num, references.size(), &references[0], dim);
/* sweep right to left and determine bounds. */
BoundBox right_bounds;
for(int i = spec.num - 1; i > 0; i--) {
right_bounds.grow(ref_ptr[i].bounds);
spatial_right_bounds[i - 1] = right_bounds;
}
/* sweep left to right and select lowest SAH. */
BoundBox left_bounds;
for(int i = 1; i < spec.num; i++) {
left_bounds.grow(ref_ptr[i - 1].bounds);
right_bounds = spatial_right_bounds[i - 1];
float sah = nodeSAH +
left_bounds.area() * params.triangle_cost(i) +
right_bounds.area() * params.triangle_cost(spec.num - i);
if(sah < split.sah) {
split.sah = sah;
split.dim = dim;
split.num_left = i;
split.left_bounds = left_bounds;
split.right_bounds = right_bounds;
}
}
}
return split;
}
void BVHBuild::do_object_split(NodeSpec& left, NodeSpec& right, const NodeSpec& spec, const ObjectSplit& split)
{
/* sort references according to split */
int start = references.size() - spec.num;
int end = references.size(); /* todo: is this right? */
bvh_reference_sort(start, end, &references[0], split.dim);
/* split node specs */
left.num = split.num_left;
left.bounds = split.left_bounds;
right.num = spec.num - split.num_left;
right.bounds = split.right_bounds;
}
/* Spatial Split */
BVHBuild::SpatialSplit BVHBuild::find_spatial_split(const NodeSpec& spec, float nodeSAH)
{
/* initialize bins. */
float3 origin = spec.bounds.min;
float3 binSize = (spec.bounds.max - origin) * (1.0f / (float)BVHParams::NUM_SPATIAL_BINS);
float3 invBinSize = 1.0f / binSize;
for(int dim = 0; dim < 3; dim++) {
for(int i = 0; i < BVHParams::NUM_SPATIAL_BINS; i++) {
SpatialBin& bin = spatial_bins[dim][i];
bin.bounds = BoundBox();
bin.enter = 0;
bin.exit = 0;
}
}
/* chop references into bins. */
for(unsigned int refIdx = references.size() - spec.num; refIdx < references.size(); refIdx++) {
const Reference& ref = references[refIdx];
float3 firstBinf = (ref.bounds.min - origin) * invBinSize;
float3 lastBinf = (ref.bounds.max - origin) * invBinSize;
int3 firstBin = make_int3((int)firstBinf.x, (int)firstBinf.y, (int)firstBinf.z);
int3 lastBin = make_int3((int)lastBinf.x, (int)lastBinf.y, (int)lastBinf.z);
firstBin = clamp(firstBin, 0, BVHParams::NUM_SPATIAL_BINS - 1);
lastBin = clamp(lastBin, firstBin, BVHParams::NUM_SPATIAL_BINS - 1);
for(int dim = 0; dim < 3; dim++) {
Reference currRef = ref;
for(int i = firstBin[dim]; i < lastBin[dim]; i++) {
Reference leftRef, rightRef;
split_reference(leftRef, rightRef, currRef, dim, origin[dim] + binSize[dim] * (float)(i + 1));
spatial_bins[dim][i].bounds.grow(leftRef.bounds);
currRef = rightRef;
}
spatial_bins[dim][lastBin[dim]].bounds.grow(currRef.bounds);
spatial_bins[dim][firstBin[dim]].enter++;
spatial_bins[dim][lastBin[dim]].exit++;
}
}
/* select best split plane. */
SpatialSplit split;
for(int dim = 0; dim < 3; dim++) {
/* sweep right to left and determine bounds. */
BoundBox right_bounds;
for(int i = BVHParams::NUM_SPATIAL_BINS - 1; i > 0; i--) {
right_bounds.grow(spatial_bins[dim][i].bounds);
spatial_right_bounds[i - 1] = right_bounds;
}
/* sweep left to right and select lowest SAH. */
BoundBox left_bounds;
int leftNum = 0;
int rightNum = spec.num;
for(int i = 1; i < BVHParams::NUM_SPATIAL_BINS; i++) {
left_bounds.grow(spatial_bins[dim][i - 1].bounds);
leftNum += spatial_bins[dim][i - 1].enter;
rightNum -= spatial_bins[dim][i - 1].exit;
float sah = nodeSAH +
left_bounds.area() * params.triangle_cost(leftNum) +
spatial_right_bounds[i - 1].area() * params.triangle_cost(rightNum);
if(sah < split.sah) {
split.sah = sah;
split.dim = dim;
split.pos = origin[dim] + binSize[dim] * (float)i;
}
}
}
return split;
}
void BVHBuild::do_spatial_split(NodeSpec& left, NodeSpec& right, const NodeSpec& spec, const SpatialSplit& split)
{
/* Categorize references and compute bounds.
*
* Left-hand side: [left_start, left_end[
* Uncategorized/split: [left_end, right_start[
* Right-hand side: [right_start, refs.size()[ */
vector<Reference>& refs = references;
int left_start = refs.size() - spec.num;
int left_end = left_start;
int right_start = refs.size();
left.bounds = right.bounds = BoundBox();
for(int i = left_end; i < right_start; i++) {
if(refs[i].bounds.max[split.dim] <= split.pos) {
/* entirely on the left-hand side */
left.bounds.grow(refs[i].bounds);
swap(refs[i], refs[left_end++]);
}
else if(refs[i].bounds.min[split.dim] >= split.pos) {
/* entirely on the right-hand side */
right.bounds.grow(refs[i].bounds);
swap(refs[i--], refs[--right_start]);
}
}
/* duplicate or unsplit references intersecting both sides. */
while(left_end < right_start) {
/* split reference. */
Reference lref, rref;
split_reference(lref, rref, refs[left_end], split.dim, split.pos);
/* compute SAH for duplicate/unsplit candidates. */
BoundBox lub = left.bounds; // Unsplit to left: new left-hand bounds.
BoundBox rub = right.bounds; // Unsplit to right: new right-hand bounds.
BoundBox ldb = left.bounds; // Duplicate: new left-hand bounds.
BoundBox rdb = right.bounds; // Duplicate: new right-hand bounds.
lub.grow(refs[left_end].bounds);
rub.grow(refs[left_end].bounds);
ldb.grow(lref.bounds);
rdb.grow(rref.bounds);
float lac = params.triangle_cost(left_end - left_start);
float rac = params.triangle_cost(refs.size() - right_start);
float lbc = params.triangle_cost(left_end - left_start + 1);
float rbc = params.triangle_cost(refs.size() - right_start + 1);
float unsplitLeftSAH = lub.area() * lbc + right.bounds.area() * rac;
float unsplitRightSAH = left.bounds.area() * lac + rub.area() * rbc;
float duplicateSAH = ldb.area() * lbc + rdb.area() * rbc;
float minSAH = min(min(unsplitLeftSAH, unsplitRightSAH), duplicateSAH);
if(minSAH == unsplitLeftSAH) {
/* unsplit to left */
left.bounds = lub;
left_end++;
}
else if(minSAH == unsplitRightSAH) {
/* unsplit to right */
right.bounds = rub;
swap(refs[left_end], refs[--right_start]);
}
else {
/* duplicate */
left.bounds = ldb;
right.bounds = rdb;
refs[left_end++] = lref;
refs.push_back(rref);
}
}
left.num = left_end - left_start;
right.num = refs.size() - right_start;
}
void BVHBuild::split_reference(Reference& left, Reference& right, const Reference& ref, int dim, float pos)
{
/* initialize references. */
left.prim_index = right.prim_index = ref.prim_index;
left.prim_object = right.prim_object = ref.prim_object;
left.bounds = right.bounds = BoundBox();
/* loop over vertices/edges. */
Object *ob = objects[ref.prim_object];
const Mesh *mesh = ob->mesh;
const int *inds = mesh->triangles[ref.prim_index].v;
const float3 *verts = &mesh->verts[0];
const float3* v1 = &verts[inds[2]];
for(int i = 0; i < 3; i++) {
const float3* v0 = v1;
int vindex = inds[i];
v1 = &verts[vindex];
float v0p = (*v0)[dim];
float v1p = (*v1)[dim];
/* insert vertex to the boxes it belongs to. */
if(v0p <= pos)
left.bounds.grow(*v0);
if(v0p >= pos)
right.bounds.grow(*v0);
/* edge intersects the plane => insert intersection to both boxes. */
if((v0p < pos && v1p > pos) || (v0p > pos && v1p < pos)) {
float3 t = lerp(*v0, *v1, clamp((pos - v0p) / (v1p - v0p), 0.0f, 1.0f));
left.bounds.grow(t);
right.bounds.grow(t);
}
}
/* intersect with original bounds. */
left.bounds.max[dim] = pos;
right.bounds.min[dim] = pos;
left.bounds.intersect(ref.bounds);
right.bounds.intersect(ref.bounds);
}
CCL_NAMESPACE_END

@ -0,0 +1,152 @@
/*
* 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_BUILD_H__
#define __BVH_BUILD_H__
#include <float.h>
#include "bvh.h"
#include "util_boundbox.h"
#include "util_vector.h"
CCL_NAMESPACE_BEGIN
class BVHParams;
class Mesh;
class Object;
class Progress;
/* BVH Builder */
class BVHBuild
{
public:
struct Reference
{
int prim_index;
int prim_object;
BoundBox bounds;
Reference()
{
}
};
struct NodeSpec
{
int num;
BoundBox bounds;
NodeSpec()
{
num = 0;
}
};
BVHBuild(
const vector<Object*>& objects,
vector<int>& prim_index,
vector<int>& prim_object,
const BVHParams& params,
Progress& progress);
~BVHBuild();
BVHNode *run();
protected:
/* adding references */
void add_reference_mesh(NodeSpec& root, Mesh *mesh, int i);
void add_reference_object(NodeSpec& root, Object *ob, int i);
void add_references(NodeSpec& root);
/* building */
BVHNode *build_node(const NodeSpec& spec, int level, float progress_start, float progress_end);
BVHNode *create_leaf_node(const NodeSpec& spec);
BVHNode *create_object_leaf_nodes(const Reference *ref, int num);
void progress_update(float progress_start, float progress_end);
/* object splits */
struct ObjectSplit
{
float sah;
int dim;
int num_left;
BoundBox left_bounds;
BoundBox right_bounds;
ObjectSplit()
: sah(FLT_MAX), dim(0), num_left(0)
{
}
};
ObjectSplit find_object_split(const NodeSpec& spec, float nodeSAH);
void do_object_split(NodeSpec& left, NodeSpec& right, const NodeSpec& spec, const ObjectSplit& split);
/* spatial splits */
struct SpatialSplit
{
float sah;
int dim;
float pos;
SpatialSplit()
: sah(FLT_MAX), dim(0), pos(0.0f)
{
}
};
struct SpatialBin
{
BoundBox bounds;
int enter;
int exit;
};
SpatialSplit find_spatial_split(const NodeSpec& spec, float nodeSAH);
void do_spatial_split(NodeSpec& left, NodeSpec& right, const NodeSpec& spec, const SpatialSplit& split);
void split_reference(Reference& left, Reference& right, const Reference& ref, int dim, float pos);
/* objects and primitive references */
vector<Object*> objects;
vector<Reference> references;
/* output primitive indexes and objects */
vector<int>& prim_index;
vector<int>& prim_object;
/* build parameters */
BVHParams params;
/* progress reporting */
Progress& progress;
double progress_start_time;
int progress_num_duplicates;
/* spatial splitting */
float spatial_min_overlap;
vector<BoundBox> spatial_right_bounds;
SpatialBin spatial_bins[3][BVHParams::NUM_SPATIAL_BINS];
};
CCL_NAMESPACE_END
#endif /* __BVH_BUILD_H__ */

@ -0,0 +1,101 @@
/*
* 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.h"
#include "bvh_build.h"
#include "bvh_node.h"
#include "util_debug.h"
#include "util_vector.h"
CCL_NAMESPACE_BEGIN
int BVHNode::getSubtreeSize(BVH_STAT stat) const
{
int cnt = 0;
switch(stat)
{
case BVH_STAT_NODE_COUNT:
cnt = 1;
break;
case BVH_STAT_LEAF_COUNT:
cnt = is_leaf() ? 1 : 0;
break;
case BVH_STAT_INNER_COUNT:
cnt = is_leaf() ? 0 : 1;
break;
case BVH_STAT_TRIANGLE_COUNT:
cnt = is_leaf() ? reinterpret_cast<const LeafNode*>(this)->num_triangles() : 0;
break;
case BVH_STAT_CHILDNODE_COUNT:
cnt = num_children();
break;
default:
assert(0); /* unknown mode */
}
if(!is_leaf())
for(int i=0;i<num_children();i++)
cnt += get_child(i)->getSubtreeSize(stat);
return cnt;
}
void BVHNode::deleteSubtree()
{
for(int i=0;i<num_children();i++)
get_child(i)->deleteSubtree();
delete this;
}
float BVHNode::computeSubtreeSAHCost(const BVHParams& p, float probability) const
{
float SAH = probability * p.cost(num_children(), num_triangles());
for(int i=0;i<num_children();i++) {
BVHNode *child = get_child(i);
SAH += child->computeSubtreeSAHCost(p, probability * child->m_bounds.area()/m_bounds.area());
}
return SAH;
}
void InnerNode::print(int depth) const
{
for(int i = 0; i < depth; i++)
printf(" ");
printf("inner node %p\n", (void*)this);
if(children[0])
children[0]->print(depth+1);
if(children[1])
children[1]->print(depth+1);
}
void LeafNode::print(int depth) const
{
for(int i = 0; i < depth; i++)
printf(" ");
printf("leaf node %d to %d\n", m_lo, m_hi);
}
CCL_NAMESPACE_END

@ -0,0 +1,112 @@
/*
* 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_NODE_H__
#define __BVH_NODE_H__
#include "util_boundbox.h"
#include "util_debug.h"
#include "util_types.h"
CCL_NAMESPACE_BEGIN
enum BVH_STAT
{
BVH_STAT_NODE_COUNT,
BVH_STAT_INNER_COUNT,
BVH_STAT_LEAF_COUNT,
BVH_STAT_TRIANGLE_COUNT,
BVH_STAT_CHILDNODE_COUNT
};
class BVHParams;
class BVHNode
{
public:
BVHNode()
{
}
virtual ~BVHNode() {}
virtual bool is_leaf() const = 0;
virtual int num_children() const = 0;
virtual BVHNode *get_child(int i) const = 0;
virtual int num_triangles() const { return 0; }
virtual void print(int depth = 0) const = 0;
float getArea() const { return m_bounds.area(); }
BoundBox m_bounds;
uint m_visibility;
// Subtree functions
int getSubtreeSize(BVH_STAT stat=BVH_STAT_NODE_COUNT) const;
float computeSubtreeSAHCost(const BVHParams& p, float probability = 1.0f) const;
void deleteSubtree();
};
class InnerNode : public BVHNode
{
public:
InnerNode(const BoundBox& bounds, BVHNode* child0, BVHNode* child1)
{
m_bounds = bounds;
m_visibility = child0->m_visibility|child1->m_visibility;
children[0] = child0;
children[1] = child1;
}
bool is_leaf() const { return false; }
int num_children() const { return 2; }
BVHNode *get_child(int i) const{ assert(i>=0 && i<2); return children[i]; }
void print(int depth) const;
BVHNode *children[2];
};
class LeafNode : public BVHNode
{
public:
LeafNode(const BoundBox& bounds, uint visibility, int lo, int hi)
{
m_bounds = bounds;
m_visibility = visibility;
m_lo = lo;
m_hi = hi;
}
LeafNode(const LeafNode& s)
: BVHNode()
{
*this = s;
}
bool is_leaf() const { return true; }
int num_children() const { return 0; }
BVHNode *get_child(int) const { return NULL; }
int num_triangles() const { return m_hi - m_lo; }
void print(int depth) const;
int m_lo;
int m_hi;
};
CCL_NAMESPACE_END
#endif /* __BVH_NODE_H__ */

@ -0,0 +1,86 @@
/*
* 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_PARAMS_H__
#define __BVH_PARAMS_H__
CCL_NAMESPACE_BEGIN
/* BVH Parameters */
class BVHParams
{
public:
/* spatial split area threshold */
bool use_spatial_split;
float spatial_split_alpha;
/* SAH costs */
float sah_node_cost;
float sah_triangle_cost;
/* number of triangles in leaf */
int min_leaf_size;
int max_leaf_size;
/* object or mesh level bvh */
bool top_level;
/* disk cache */
bool use_cache;
/* QBVH */
bool use_qbvh;
/* fixed parameters */
enum {
MAX_DEPTH = 64,
MAX_SPATIAL_DEPTH = 48,
NUM_SPATIAL_BINS = 32
};
BVHParams()
{
use_spatial_split = true;
spatial_split_alpha = 1e-5f;
sah_node_cost = 1.0f;
sah_triangle_cost = 1.0f;
min_leaf_size = 1;
max_leaf_size = 0x7FFFFFF;
top_level = false;
use_cache = false;
use_qbvh = false;
}
/* SAH costs */
float cost(int num_nodes, int num_tris) const
{ return node_cost(num_nodes) + triangle_cost(num_tris); }
float triangle_cost(int n) const
{ return n*sah_triangle_cost; }
float node_cost(int n) const
{ return n*sah_node_cost; }
};
CCL_NAMESPACE_END
#endif /* __BVH_PARAMS_H__ */

@ -0,0 +1,57 @@
/*
* 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_sort.h"
#include "util_algorithm.h"
#include "util_debug.h"
CCL_NAMESPACE_BEGIN
struct BVHReferenceCompare {
public:
int dim;
BVHReferenceCompare(int dim_)
{
dim = dim_;
}
bool operator()(const BVHBuild::Reference& ra, const BVHBuild::Reference& rb)
{
float ca = ra.bounds.min[dim] + ra.bounds.max[dim];
float cb = rb.bounds.min[dim] + rb.bounds.max[dim];
if(ca < cb) return true;
else if(ca > cb) return false;
else if(ra.prim_object < rb.prim_object) return true;
else if(ra.prim_object > rb.prim_object) return false;
else if(ra.prim_index < rb.prim_index) return true;
else if(ra.prim_index > rb.prim_index) return false;
return false;
}
};
void bvh_reference_sort(int start, int end, BVHBuild::Reference *data, int dim)
{
sort(data+start, data+end, BVHReferenceCompare(dim));
}
CCL_NAMESPACE_END

@ -0,0 +1,28 @@
/*
* 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_SORT_H__
#define __BVH_SORT_H__
CCL_NAMESPACE_BEGIN
void bvh_reference_sort(int start, int end, BVHBuild::Reference *data, int dim);
CCL_NAMESPACE_END
#endif /* __BVH_SORT_H__ */

@ -0,0 +1,87 @@
###########################################################################
# GLUT
if(WITH_CYCLES_TEST)
set(GLUT_ROOT_PATH ${CYCLES_GLUT})
find_package(GLUT)
message(STATUS "GLUT_FOUND=${GLUT_FOUND}")
include_directories(${GLUT_INCLUDE_DIR})
endif()
if(WITH_BUILTIN_GLEW)
set(CYCLES_GLEW_LIBRARY extern_glew)
else()
set(CYCLES_GLEW_LIBRARY ${GLEW_LIBRARY})
endif()
###########################################################################
# OpenShadingLanguage
if(WITH_CYCLES_OSL)
set(CYCLES_OSL "" CACHE PATH "Path to OpenShadingLanguage installation")
message(STATUS "CYCLES_OSL = ${CYCLES_OSL}")
find_library(OSL_LIBRARIES NAMES oslexec oslcomp oslquery PATHS ${CYCLES_OSL}/lib ${CYCLES_OSL}/dist)
find_path(OSL_INCLUDES OSL/oslclosure.h PATHS ${CYCLES_OSL}/include ${CYCLES_OSL}/dist)
find_program(OSL_COMPILER NAMES oslc PATHS ${CYCLES_OSL}/bin ${CYCLES_OSL}/dist)
if(OSL_INCLUDES AND OSL_LIBRARIES AND OSL_COMPILER)
set(OSL_FOUND TRUE)
message(STATUS "OSL includes = ${OSL_INCLUDES}")
message(STATUS "OSL library = ${OSL_LIBRARIES}")
message(STATUS "OSL compiler = ${OSL_COMPILER}")
else()
message(STATUS "OSL not found")
endif()
include_directories(${OSL_INCLUDES} ${OSL_INCLUDES}/OSL ${OSL_INCLUDES}/../../../src/liboslexec)
endif()
###########################################################################
# Partio
if(WITH_CYCLES_PARTIO)
set(CYCLES_PARTIO "" CACHE PATH "Path to Partio installation")
message(STATUS "CYCLES_PARTIO = ${CYCLES_PARTIO}")
find_library(PARTIO_LIBRARIES NAMES partio PATHS ${CYCLES_PARTIO}/lib)
find_path(PARTIO_INCLUDES Partio.h ${CYCLES_PARTIO}/include)
find_package(ZLIB)
if(PARTIO_INCLUDES AND PARTIO_LIBRARIES AND ZLIB_LIBRARIES)
list(APPEND PARTIO_LIBRARIES ${ZLIB_LIBRARIES})
set(PARTIO_FOUND TRUE)
message(STATUS "PARTIO includes = ${PARTIO_INCLUDES}")
message(STATUS "PARTIO library = ${PARTIO_LIBRARIES}")
else()
message(STATUS "PARTIO not found")
endif()
include_directories(${PARTIO_INCLUDES})
endif()
###########################################################################
# Blender
if(WITH_CYCLES_BLENDER)
set(BLENDER_INCLUDE_DIRS
${CMAKE_SOURCE_DIR}/intern/guardedalloc
${CMAKE_SOURCE_DIR}/source/blender/makesdna
${CMAKE_SOURCE_DIR}/source/blender/makesrna
${CMAKE_SOURCE_DIR}/source/blender/blenloader
${CMAKE_BINARY_DIR}/source/blender/makesrna/intern)
add_definitions(-DBLENDER_PLUGIN)
endif()

@ -0,0 +1,32 @@
set(INC
.
../kernel
../kernel/svm
../kernel/osl
../util
../render
${OPENGL_INCLUDE_DIR}
${GLEW_INCLUDE_PATH}
)
set(SRC
device.cpp
device_cpu.cpp
device_cuda.cpp
device_multi.cpp
device_network.cpp
device_opencl.cpp
)
set(SRC_HEADERS
device.h
device_intern.h
device_network.h
)
add_definitions(-DGLEW_STATIC)
include_directories(${INC})
add_library(cycles_device ${SRC} ${SRC_HEADERS})

@ -0,0 +1,222 @@
/*
* 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 <stdlib.h>
#include <string.h>
#include "device.h"
#include "device_intern.h"
#include "util_cuda.h"
#include "util_debug.h"
#include "util_math.h"
#include "util_opencl.h"
#include "util_opengl.h"
#include "util_types.h"
#include "util_vector.h"
CCL_NAMESPACE_BEGIN
/* Device Task */
DeviceTask::DeviceTask(Type type_)
: type(type_), x(0), y(0), w(0), h(0), rng_state(0), rgba(0), buffer(0),
sample(0), resolution(0),
displace_input(0), displace_offset(0), displace_x(0), displace_w(0)
{
}
void DeviceTask::split(ThreadQueue<DeviceTask>& tasks, int num)
{
if(type == DISPLACE) {
num = min(displace_w, num);
for(int i = 0; i < num; i++) {
int tx = displace_x + (displace_w/num)*i;
int tw = (i == num-1)? displace_w - i*(displace_w/num): displace_w/num;
DeviceTask task = *this;
task.displace_x = tx;
task.displace_w = tw;
tasks.push(task);
}
}
else {
num = min(h, num);
for(int i = 0; i < num; i++) {
int ty = y + (h/num)*i;
int th = (i == num-1)? h - i*(h/num): h/num;
DeviceTask task = *this;
task.y = ty;
task.h = th;
tasks.push(task);
}
}
}
/* Device */
void Device::pixels_alloc(device_memory& mem)
{
mem_alloc(mem, MEM_READ_WRITE);
}
void Device::pixels_copy_from(device_memory& mem, int y, int w, int h)
{
mem_copy_from(mem, sizeof(uint8_t)*4*y*w, sizeof(uint8_t)*4*w*h);
}
void Device::pixels_free(device_memory& mem)
{
mem_free(mem);
}
void Device::draw_pixels(device_memory& rgba, int y, int w, int h, int width, int height, bool transparent)
{
pixels_copy_from(rgba, y, w, h);
if(transparent) {
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
glPixelZoom((float)width/(float)w, (float)height/(float)h);
glRasterPos2f(0, y);
uint8_t *pixels = (uint8_t*)rgba.data_pointer;
/* for multi devices, this assumes the ineffecient method that we allocate
all pixels on the device even though we only render to a subset */
pixels += 4*y*w;
glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
glRasterPos2f(0.0f, 0.0f);
glPixelZoom(1.0f, 1.0f);
if(transparent)
glDisable(GL_BLEND);
}
Device *Device::create(DeviceType type, bool background, int threads)
{
Device *device;
switch(type) {
case DEVICE_CPU:
device = device_cpu_create(threads);
break;
#ifdef WITH_CUDA
case DEVICE_CUDA:
if(cuLibraryInit())
device = device_cuda_create(background);
else
device = NULL;
break;
#endif
#ifdef WITH_MULTI
case DEVICE_MULTI:
device = device_multi_create(background);
break;
#endif
#ifdef WITH_NETWORK
case DEVICE_NETWORK:
device = device_network_create("127.0.0.1");
break;
#endif
#ifdef WITH_OPENCL
case DEVICE_OPENCL:
if(clLibraryInit())
device = device_opencl_create(background);
else
device = NULL;
break;
#endif
default:
return NULL;
}
return device;
}
DeviceType Device::type_from_string(const char *name)
{
if(strcmp(name, "cpu") == 0)
return DEVICE_CPU;
else if(strcmp(name, "cuda") == 0)
return DEVICE_CUDA;
else if(strcmp(name, "opencl") == 0)
return DEVICE_OPENCL;
else if(strcmp(name, "network") == 0)
return DEVICE_NETWORK;
else if(strcmp(name, "multi") == 0)
return DEVICE_MULTI;
return DEVICE_NONE;
}
string Device::string_from_type(DeviceType type)
{
if(type == DEVICE_CPU)
return "cpu";
else if(type == DEVICE_CUDA)
return "cuda";
else if(type == DEVICE_OPENCL)
return "opencl";
else if(type == DEVICE_NETWORK)
return "network";
else if(type == DEVICE_MULTI)
return "multi";
return "";
}
vector<DeviceType> Device::available_types()
{
vector<DeviceType> types;
types.push_back(DEVICE_CPU);
#ifdef WITH_CUDA
if(cuLibraryInit())
types.push_back(DEVICE_CUDA);
#endif
#ifdef WITH_OPENCL
if(clLibraryInit())
types.push_back(DEVICE_OPENCL);
#endif
#ifdef WITH_NETWORK
types.push_back(DEVICE_NETWORK);
#endif
#ifdef WITH_MULTI
types.push_back(DEVICE_MULTI);
#endif
return types;
}
CCL_NAMESPACE_END

@ -0,0 +1,140 @@
/*
* 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 __DEVICE_H__
#define __DEVICE_H__
#include <stdlib.h>
#include "device_memory.h"
#include "util_string.h"
#include "util_thread.h"
#include "util_types.h"
#include "util_vector.h"
CCL_NAMESPACE_BEGIN
class Progress;
enum DeviceType {
DEVICE_NONE,
DEVICE_CPU,
DEVICE_OPENCL,
DEVICE_CUDA,
DEVICE_NETWORK,
DEVICE_MULTI
};
enum MemoryType {
MEM_READ_ONLY,
MEM_WRITE_ONLY,
MEM_READ_WRITE
};
/* Device Task */
class DeviceTask {
public:
typedef enum { PATH_TRACE, TONEMAP, DISPLACE } Type;
Type type;
int x, y, w, h;
device_ptr rng_state;
device_ptr rgba;
device_ptr buffer;
int sample;
int resolution;
device_ptr displace_input;
device_ptr displace_offset;
int displace_x, displace_w;
DeviceTask(Type type = PATH_TRACE);
void split(ThreadQueue<DeviceTask>& tasks, int num);
};
/* Device */
class Device {
protected:
Device() {}
bool background;
public:
virtual ~Device() {}
virtual bool support_full_kernel() = 0;
/* info */
virtual string description() = 0;
/* regular memory */
virtual void mem_alloc(device_memory& mem, MemoryType type) = 0;
virtual void mem_copy_to(device_memory& mem) = 0;
virtual void mem_copy_from(device_memory& mem,
size_t offset, size_t size) = 0;
virtual void mem_zero(device_memory& mem) = 0;
virtual void mem_free(device_memory& mem) = 0;
/* constant memory */
virtual void const_copy_to(const char *name, void *host, size_t size) = 0;
/* texture memory */
virtual void tex_alloc(const char *name, device_memory& mem,
bool interpolation = false, bool periodic = false) {};
virtual void tex_free(device_memory& mem) {};
/* pixel memory */
virtual void pixels_alloc(device_memory& mem);
virtual void pixels_copy_from(device_memory& mem, int y, int w, int h);
virtual void pixels_free(device_memory& mem);
/* open shading language, only for CPU device */
virtual void *osl_memory() { return NULL; }
/* load/compile kernels, must be called before adding tasks */
virtual bool load_kernels() { return true; }
/* tasks */
virtual void task_add(DeviceTask& task) = 0;
virtual void task_wait() = 0;
virtual void task_cancel() = 0;
/* opengl drawing */
virtual void draw_pixels(device_memory& mem, int y, int w, int h,
int width, int height, bool transparent);
#ifdef WITH_NETWORK
/* networking */
void server_run();
#endif
/* static */
static Device *create(DeviceType type, bool background = true, int threads = 0);
static DeviceType type_from_string(const char *name);
static string string_from_type(DeviceType type);
static vector<DeviceType> available_types();
};
CCL_NAMESPACE_END
#endif /* __DEVICE_H__ */

@ -0,0 +1,224 @@
/*
* 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 <stdlib.h>
#include <string.h>
#include "device.h"
#include "device_intern.h"
#include "kernel.h"
#include "kernel_types.h"
#include "osl_shader.h"
#include "util_debug.h"
#include "util_foreach.h"
#include "util_function.h"
#include "util_opengl.h"
#include "util_progress.h"
#include "util_system.h"
#include "util_thread.h"
CCL_NAMESPACE_BEGIN
class CPUDevice : public Device
{
public:
vector<thread*> threads;
ThreadQueue<DeviceTask> tasks;
KernelGlobals *kg;
CPUDevice(int threads_num)
{
kg = kernel_globals_create();
if(threads_num == 0)
threads_num = system_cpu_thread_count();
threads.resize(threads_num);
for(size_t i = 0; i < threads.size(); i++)
threads[i] = new thread(function_bind(&CPUDevice::thread_run, this, i));
}
~CPUDevice()
{
tasks.stop();
foreach(thread *t, threads) {
t->join();
delete t;
}
kernel_globals_free(kg);
}
bool support_full_kernel()
{
return true;
}
string description()
{
return system_cpu_brand_string();
}
void mem_alloc(device_memory& mem, MemoryType type)
{
mem.device_pointer = mem.data_pointer;
}
void mem_copy_to(device_memory& mem)
{
/* no-op */
}
void mem_copy_from(device_memory& mem, size_t offset, size_t size)
{
/* no-op */
}
void mem_zero(device_memory& mem)
{
memset((void*)mem.device_pointer, 0, mem.memory_size());
}
void mem_free(device_memory& mem)
{
mem.device_pointer = 0;
}
void const_copy_to(const char *name, void *host, size_t size)
{
kernel_const_copy(kg, name, host, size);
}
void tex_alloc(const char *name, device_memory& mem, bool interpolation, bool periodic)
{
kernel_tex_copy(kg, name, mem.data_pointer, mem.data_width, mem.data_height);
mem.device_pointer = mem.data_pointer;
}
void tex_free(device_memory& mem)
{
mem.device_pointer = 0;
}
void *osl_memory()
{
#ifdef WITH_OSL
return kernel_osl_memory(kg);
#else
return NULL;
#endif
}
void thread_run(int t)
{
DeviceTask task;
while(tasks.worker_wait_pop(task)) {
if(task.type == DeviceTask::PATH_TRACE)
thread_path_trace(task);
else if(task.type == DeviceTask::TONEMAP)
thread_tonemap(task);
else if(task.type == DeviceTask::DISPLACE)
thread_displace(task);
tasks.worker_done();
}
}
void thread_path_trace(DeviceTask& task)
{
if(tasks.worker_cancel())
return;
#ifdef WITH_OSL
if(kernel_osl_use(kg))
OSLShader::thread_init(kg);
#endif
for(int y = task.y; y < task.y + task.h; y++) {
for(int x = task.x; x < task.x + task.w; x++)
kernel_cpu_path_trace(kg, (float4*)task.buffer, (unsigned int*)task.rng_state, task.sample, x, y);
if(tasks.worker_cancel())
break;
}
#ifdef WITH_OSL
if(kernel_osl_use(kg))
OSLShader::thread_free(kg);
#endif
}
void thread_tonemap(DeviceTask& task)
{
for(int y = task.y; y < task.y + task.h; y++) {
for(int x = task.x; x < task.x + task.w; x++)
kernel_cpu_tonemap(kg, (uchar4*)task.rgba, (float4*)task.buffer, task.sample, task.resolution, x, y);
}
}
void thread_displace(DeviceTask& task)
{
#ifdef WITH_OSL
if(kernel_osl_use(kg))
OSLShader::thread_init(kg);
#endif
for(int x = task.displace_x; x < task.displace_x + task.displace_w; x++) {
kernel_cpu_displace(kg, (uint4*)task.displace_input, (float3*)task.displace_offset, x);
if(tasks.worker_cancel())
break;
}
#ifdef WITH_OSL
if(kernel_osl_use(kg))
OSLShader::thread_free(kg);
#endif
}
void task_add(DeviceTask& task)
{
/* split task into smaller ones, more than number of threads for uneven
workloads where some parts of the image render slower than others */
task.split(tasks, threads.size()*10);
}
void task_wait()
{
tasks.wait_done();
}
void task_cancel()
{
tasks.cancel();
}
};
Device *device_cpu_create(int threads)
{
return new CPUDevice(threads);
}
CCL_NAMESPACE_END

@ -0,0 +1,804 @@
/*
* 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 <stdlib.h>
#include <string.h>
#include "device.h"
#include "device_intern.h"
#include "util_cuda.h"
#include "util_debug.h"
#include "util_map.h"
#include "util_opengl.h"
#include "util_path.h"
#include "util_system.h"
#include "util_types.h"
#include "util_time.h"
CCL_NAMESPACE_BEGIN
class CUDADevice : public Device
{
public:
CUdevice cuDevice;
CUcontext cuContext;
CUmodule cuModule;
map<device_ptr, bool> tex_interp_map;
int cuDevId;
struct PixelMem {
GLuint cuPBO;
CUgraphicsResource cuPBOresource;
GLuint cuTexId;
int w, h;
};
map<device_ptr, PixelMem> pixel_mem_map;
CUdeviceptr cuda_device_ptr(device_ptr mem)
{
return (CUdeviceptr)mem;
}
const char *cuda_error_string(CUresult result)
{
switch(result) {
case CUDA_SUCCESS: return "No errors";
case CUDA_ERROR_INVALID_VALUE: return "Invalid value";
case CUDA_ERROR_OUT_OF_MEMORY: return "Out of memory";
case CUDA_ERROR_NOT_INITIALIZED: return "Driver not initialized";
case CUDA_ERROR_DEINITIALIZED: return "Driver deinitialized";
case CUDA_ERROR_NO_DEVICE: return "No CUDA-capable device available";
case CUDA_ERROR_INVALID_DEVICE: return "Invalid device";
case CUDA_ERROR_INVALID_IMAGE: return "Invalid kernel image";
case CUDA_ERROR_INVALID_CONTEXT: return "Invalid context";
case CUDA_ERROR_CONTEXT_ALREADY_CURRENT: return "Context already current";
case CUDA_ERROR_MAP_FAILED: return "Map failed";
case CUDA_ERROR_UNMAP_FAILED: return "Unmap failed";
case CUDA_ERROR_ARRAY_IS_MAPPED: return "Array is mapped";
case CUDA_ERROR_ALREADY_MAPPED: return "Already mapped";
case CUDA_ERROR_NO_BINARY_FOR_GPU: return "No binary for GPU";
case CUDA_ERROR_ALREADY_ACQUIRED: return "Already acquired";
case CUDA_ERROR_NOT_MAPPED: return "Not mapped";
case CUDA_ERROR_NOT_MAPPED_AS_ARRAY: return "Mapped resource not available for access as an array";
case CUDA_ERROR_NOT_MAPPED_AS_POINTER: return "Mapped resource not available for access as a pointer";
case CUDA_ERROR_ECC_UNCORRECTABLE: return "Uncorrectable ECC error detected";
case CUDA_ERROR_UNSUPPORTED_LIMIT: return "CUlimit not supported by device";
case CUDA_ERROR_INVALID_SOURCE: return "Invalid source";
case CUDA_ERROR_FILE_NOT_FOUND: return "File not found";
case CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND: return "Link to a shared object failed to resolve";
case CUDA_ERROR_SHARED_OBJECT_INIT_FAILED: return "Shared object initialization failed";
case CUDA_ERROR_INVALID_HANDLE: return "Invalid handle";
case CUDA_ERROR_NOT_FOUND: return "Not found";
case CUDA_ERROR_NOT_READY: return "CUDA not ready";
case CUDA_ERROR_LAUNCH_FAILED: return "Launch failed";
case CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES: return "Launch exceeded resources";
case CUDA_ERROR_LAUNCH_TIMEOUT: return "Launch exceeded timeout";
case CUDA_ERROR_LAUNCH_INCOMPATIBLE_TEXTURING: return "Launch with incompatible texturing";
case CUDA_ERROR_UNKNOWN: return "Unknown error";
default: return "Unknown CUDA error value";
}
}
static int cuda_align_up(int& offset, int alignment)
{
return (offset + alignment - 1) & ~(alignment - 1);
}
#ifdef NDEBUG
#define cuda_abort()
#else
#define cuda_abort() abort()
#endif
#define cuda_assert(stmt) \
{ \
CUresult result = stmt; \
\
if(result != CUDA_SUCCESS) { \
fprintf(stderr, "CUDA error: %s in %s\n", cuda_error_string(result), #stmt); \
cuda_abort(); \
} \
}
bool cuda_error(CUresult result)
{
if(result == CUDA_SUCCESS)
return false;
fprintf(stderr, "CUDA error: %s\n", cuda_error_string(result));
return true;
}
void cuda_push_context()
{
cuda_assert(cuCtxSetCurrent(cuContext))
}
void cuda_pop_context()
{
cuda_assert(cuCtxSetCurrent(NULL));
}
CUDADevice(bool background_)
{
background = background_;
cuDevId = 0;
cuDevice = 0;
cuContext = 0;
/* intialize */
if(cuda_error(cuInit(0)))
return;
/* setup device and context */
if(cuda_error(cuDeviceGet(&cuDevice, cuDevId)))
return;
CUresult result;
if(background)
result = cuCtxCreate(&cuContext, 0, cuDevice);
else
result = cuGLCtxCreate(&cuContext, 0, cuDevice);
if(cuda_error(result))
return;
cuda_pop_context();
}
~CUDADevice()
{
cuda_push_context();
cuda_assert(cuCtxDetach(cuContext))
}
bool support_full_kernel()
{
int major, minor;
cuDeviceComputeCapability(&major, &minor, cuDevId);
return (major >= 2);
}
string description()
{
/* print device information */
char deviceName[100];
cuda_push_context();
cuDeviceGetName(deviceName, 256, cuDevId);
cuda_pop_context();
return string("CUDA ") + deviceName;
}
string compile_kernel()
{
/* compute cubin name */
int major, minor;
cuDeviceComputeCapability(&major, &minor, cuDevId);
/* attempt to use kernel provided with blender */
string cubin = path_get(string_printf("lib/kernel_sm_%d%d.cubin", major, minor));
if(path_exists(cubin))
return cubin;
/* not found, try to use locally compiled kernel */
string kernel_path = path_get("kernel");
string md5 = path_files_md5_hash(kernel_path);
cubin = string_printf("cycles_kernel_sm%d%d_%s.cubin", major, minor, md5.c_str());;
cubin = path_user_get(path_join("cache", cubin));
/* if exists already, use it */
if(path_exists(cubin))
return cubin;
/* if not, find CUDA compiler */
string nvcc = cuCompilerPath();
if(nvcc == "") {
fprintf(stderr, "CUDA nvcc compiler not found. Install CUDA toolkit in default location.\n");
return "";
}
/* compile */
string kernel = path_join(kernel_path, "kernel.cu");
string include = kernel_path;
const int machine = system_cpu_bits();
const int maxreg = 24;
double starttime = time_dt();
printf("Compiling CUDA kernel ...\n");
path_create_directories(cubin);
string command = string_printf("\"%s\" -arch=sm_%d%d -m%d --cubin \"%s\" --use_fast_math "
"-o \"%s\" --ptxas-options=\"-v\" --maxrregcount=%d --opencc-options -OPT:Olimit=0 -I\"%s\" -DNVCC",
nvcc.c_str(), major, minor, machine, kernel.c_str(), cubin.c_str(), maxreg, include.c_str());
if(system(command.c_str()) == -1) {
fprintf(stderr, "Failed to execute compilation command.\n");
return "";
}
/* verify if compilation succeeded */
if(!path_exists(cubin)) {
fprintf(stderr, "CUDA kernel compilation failed.\n");
return "";
}
printf("Kernel compilation finished in %.2lfs.\n", time_dt() - starttime);
return cubin;
}
bool load_kernels()
{
/* check if cuda init succeeded */
if(cuContext == 0)
return false;
/* get kernel */
string cubin = compile_kernel();
if(cubin == "")
return false;
/* open module */
cuda_push_context();
CUresult result = cuModuleLoad(&cuModule, cubin.c_str());
if(cuda_error(result))
fprintf(stderr, "Failed loading CUDA kernel %s.\n", cubin.c_str());
cuda_pop_context();
return (result == CUDA_SUCCESS);
}
void mem_alloc(device_memory& mem, MemoryType type)
{
cuda_push_context();
CUdeviceptr device_pointer;
cuda_assert(cuMemAlloc(&device_pointer, mem.memory_size()))
mem.device_pointer = (device_ptr)device_pointer;
cuda_pop_context();
}
void mem_copy_to(device_memory& mem)
{
cuda_push_context();
cuda_assert(cuMemcpyHtoD(cuda_device_ptr(mem.device_pointer), (void*)mem.data_pointer, mem.memory_size()))
cuda_pop_context();
}
void mem_copy_from(device_memory& mem, size_t offset, size_t size)
{
/* todo: offset is ignored */
cuda_push_context();
cuda_assert(cuMemcpyDtoH((uchar*)mem.data_pointer + offset,
(CUdeviceptr)((uchar*)mem.device_pointer + offset), size))
cuda_pop_context();
}
void mem_zero(device_memory& mem)
{
memset((void*)mem.data_pointer, 0, mem.memory_size());
cuda_push_context();
cuda_assert(cuMemsetD8(cuda_device_ptr(mem.device_pointer), 0, mem.memory_size()))
cuda_pop_context();
}
void mem_free(device_memory& mem)
{
if(mem.device_pointer) {
cuda_push_context();
cuda_assert(cuMemFree(cuda_device_ptr(mem.device_pointer)))
cuda_pop_context();
mem.device_pointer = 0;
}
}
void const_copy_to(const char *name, void *host, size_t size)
{
CUdeviceptr mem;
size_t bytes;
cuda_push_context();
cuda_assert(cuModuleGetGlobal(&mem, &bytes, cuModule, name))
//assert(bytes == size);
cuda_assert(cuMemcpyHtoD(mem, host, size))
cuda_pop_context();
}
void tex_alloc(const char *name, device_memory& mem, bool interpolation, bool periodic)
{
/* determine format */
CUarray_format_enum format;
size_t dsize = datatype_size(mem.data_type);
size_t size = mem.memory_size();
switch(mem.data_type) {
case TYPE_UCHAR: format = CU_AD_FORMAT_UNSIGNED_INT8; break;
case TYPE_UINT: format = CU_AD_FORMAT_UNSIGNED_INT32; break;
case TYPE_INT: format = CU_AD_FORMAT_SIGNED_INT32; break;
case TYPE_FLOAT: format = CU_AD_FORMAT_FLOAT; break;
default: assert(0); return;
}
CUtexref texref;
cuda_push_context();
cuda_assert(cuModuleGetTexRef(&texref, cuModule, name))
if(interpolation) {
CUarray handle;
CUDA_ARRAY_DESCRIPTOR desc;
desc.Width = mem.data_width;
desc.Height = mem.data_height;
desc.Format = format;
desc.NumChannels = mem.data_elements;
cuda_assert(cuArrayCreate(&handle, &desc))
if(mem.data_height > 1) {
CUDA_MEMCPY2D param;
memset(&param, 0, sizeof(param));
param.dstMemoryType = CU_MEMORYTYPE_ARRAY;
param.dstArray = handle;
param.srcMemoryType = CU_MEMORYTYPE_HOST;
param.srcHost = (void*)mem.data_pointer;
param.srcPitch = mem.data_width*dsize*mem.data_elements;
param.WidthInBytes = param.srcPitch;
param.Height = mem.data_height;
cuda_assert(cuMemcpy2D(&param))
}
else
cuda_assert(cuMemcpyHtoA(handle, 0, (void*)mem.data_pointer, size))
cuda_assert(cuTexRefSetArray(texref, handle, CU_TRSA_OVERRIDE_FORMAT))
cuda_assert(cuTexRefSetFilterMode(texref, CU_TR_FILTER_MODE_LINEAR))
cuda_assert(cuTexRefSetFlags(texref, CU_TRSF_NORMALIZED_COORDINATES))
mem.device_pointer = (device_ptr)handle;
}
else {
cuda_pop_context();
mem_alloc(mem, MEM_READ_ONLY);
mem_copy_to(mem);
cuda_push_context();
cuda_assert(cuTexRefSetAddress(NULL, texref, cuda_device_ptr(mem.device_pointer), size))
cuda_assert(cuTexRefSetFilterMode(texref, CU_TR_FILTER_MODE_POINT))
cuda_assert(cuTexRefSetFlags(texref, CU_TRSF_READ_AS_INTEGER))
}
if(periodic) {
cuda_assert(cuTexRefSetAddressMode(texref, 0, CU_TR_ADDRESS_MODE_WRAP))
cuda_assert(cuTexRefSetAddressMode(texref, 1, CU_TR_ADDRESS_MODE_WRAP))
}
else {
cuda_assert(cuTexRefSetAddressMode(texref, 0, CU_TR_ADDRESS_MODE_CLAMP))
cuda_assert(cuTexRefSetAddressMode(texref, 1, CU_TR_ADDRESS_MODE_CLAMP))
}
cuda_assert(cuTexRefSetFormat(texref, format, mem.data_elements))
cuda_pop_context();
tex_interp_map[mem.device_pointer] = interpolation;
}
void tex_free(device_memory& mem)
{
if(mem.device_pointer) {
if(tex_interp_map[mem.device_pointer]) {
cuda_push_context();
cuArrayDestroy((CUarray)mem.device_pointer);
cuda_pop_context();
tex_interp_map.erase(tex_interp_map.find(mem.device_pointer));
mem.device_pointer = 0;
}
else {
tex_interp_map.erase(tex_interp_map.find(mem.device_pointer));
mem_free(mem);
}
}
}
void path_trace(DeviceTask& task)
{
cuda_push_context();
CUfunction cuPathTrace;
CUdeviceptr d_buffer = cuda_device_ptr(task.buffer);
CUdeviceptr d_rng_state = cuda_device_ptr(task.rng_state);
/* get kernel function */
cuda_assert(cuModuleGetFunction(&cuPathTrace, cuModule, "kernel_cuda_path_trace"))
/* pass in parameters */
int offset = 0;
cuda_assert(cuParamSetv(cuPathTrace, offset, &d_buffer, sizeof(d_buffer)))
offset += sizeof(d_buffer);
cuda_assert(cuParamSetv(cuPathTrace, offset, &d_rng_state, sizeof(d_rng_state)))
offset += sizeof(d_rng_state);
int sample = task.sample;
offset = cuda_align_up(offset, __alignof(sample));
cuda_assert(cuParamSeti(cuPathTrace, offset, task.sample))
offset += sizeof(task.sample);
cuda_assert(cuParamSeti(cuPathTrace, offset, task.x))
offset += sizeof(task.x);
cuda_assert(cuParamSeti(cuPathTrace, offset, task.y))
offset += sizeof(task.y);
cuda_assert(cuParamSeti(cuPathTrace, offset, task.w))
offset += sizeof(task.w);
cuda_assert(cuParamSeti(cuPathTrace, offset, task.h))
offset += sizeof(task.h);
cuda_assert(cuParamSetSize(cuPathTrace, offset))
/* launch kernel: todo find optimal size, cache config for fermi */
#ifndef __APPLE__
int xthreads = 16;
int ythreads = 16;
#else
int xthreads = 8;
int ythreads = 8;
#endif
int xblocks = (task.w + xthreads - 1)/xthreads;
int yblocks = (task.h + ythreads - 1)/ythreads;
cuda_assert(cuFuncSetCacheConfig(cuPathTrace, CU_FUNC_CACHE_PREFER_L1))
cuda_assert(cuFuncSetBlockShape(cuPathTrace, xthreads, ythreads, 1))
cuda_assert(cuLaunchGrid(cuPathTrace, xblocks, yblocks))
cuda_pop_context();
}
void tonemap(DeviceTask& task)
{
cuda_push_context();
CUfunction cuFilmConvert;
CUdeviceptr d_rgba = map_pixels(task.rgba);
CUdeviceptr d_buffer = cuda_device_ptr(task.buffer);
/* get kernel function */
cuda_assert(cuModuleGetFunction(&cuFilmConvert, cuModule, "kernel_cuda_tonemap"))
/* pass in parameters */
int offset = 0;
cuda_assert(cuParamSetv(cuFilmConvert, offset, &d_rgba, sizeof(d_rgba)))
offset += sizeof(d_rgba);
cuda_assert(cuParamSetv(cuFilmConvert, offset, &d_buffer, sizeof(d_buffer)))
offset += sizeof(d_buffer);
int sample = task.sample;
offset = cuda_align_up(offset, __alignof(sample));
cuda_assert(cuParamSeti(cuFilmConvert, offset, task.sample))
offset += sizeof(task.sample);
cuda_assert(cuParamSeti(cuFilmConvert, offset, task.resolution))
offset += sizeof(task.resolution);
cuda_assert(cuParamSeti(cuFilmConvert, offset, task.x))
offset += sizeof(task.x);
cuda_assert(cuParamSeti(cuFilmConvert, offset, task.y))
offset += sizeof(task.y);
cuda_assert(cuParamSeti(cuFilmConvert, offset, task.w))
offset += sizeof(task.w);
cuda_assert(cuParamSeti(cuFilmConvert, offset, task.h))
offset += sizeof(task.h);
cuda_assert(cuParamSetSize(cuFilmConvert, offset))
/* launch kernel: todo find optimal size, cache config for fermi */
#ifndef __APPLE__
int xthreads = 16;
int ythreads = 16;
#else
int xthreads = 8;
int ythreads = 8;
#endif
int xblocks = (task.w + xthreads - 1)/xthreads;
int yblocks = (task.h + ythreads - 1)/ythreads;
cuda_assert(cuFuncSetCacheConfig(cuFilmConvert, CU_FUNC_CACHE_PREFER_L1))
cuda_assert(cuFuncSetBlockShape(cuFilmConvert, xthreads, ythreads, 1))
cuda_assert(cuLaunchGrid(cuFilmConvert, xblocks, yblocks))
unmap_pixels(task.rgba);
cuda_pop_context();
}
void displace(DeviceTask& task)
{
cuda_push_context();
CUfunction cuDisplace;
CUdeviceptr d_input = cuda_device_ptr(task.displace_input);
CUdeviceptr d_offset = cuda_device_ptr(task.displace_offset);
/* get kernel function */
cuda_assert(cuModuleGetFunction(&cuDisplace, cuModule, "kernel_cuda_displace"))
/* pass in parameters */
int offset = 0;
cuda_assert(cuParamSetv(cuDisplace, offset, &d_input, sizeof(d_input)))
offset += sizeof(d_input);
cuda_assert(cuParamSetv(cuDisplace, offset, &d_offset, sizeof(d_offset)))
offset += sizeof(d_offset);
int displace_x = task.displace_x;
offset = cuda_align_up(offset, __alignof(displace_x));
cuda_assert(cuParamSeti(cuDisplace, offset, task.displace_x))
offset += sizeof(task.displace_x);
cuda_assert(cuParamSetSize(cuDisplace, offset))
/* launch kernel: todo find optimal size, cache config for fermi */
#ifndef __APPLE__
int xthreads = 16;
#else
int xthreads = 8;
#endif
int xblocks = (task.displace_w + xthreads - 1)/xthreads;
cuda_assert(cuFuncSetCacheConfig(cuDisplace, CU_FUNC_CACHE_PREFER_L1))
cuda_assert(cuFuncSetBlockShape(cuDisplace, xthreads, 1, 1))
cuda_assert(cuLaunchGrid(cuDisplace, xblocks, 1))
cuda_pop_context();
}
CUdeviceptr map_pixels(device_ptr mem)
{
if(!background) {
PixelMem pmem = pixel_mem_map[mem];
CUdeviceptr buffer;
size_t bytes;
cuda_assert(cuGraphicsMapResources(1, &pmem.cuPBOresource, 0))
cuda_assert(cuGraphicsResourceGetMappedPointer(&buffer, &bytes, pmem.cuPBOresource))
return buffer;
}
return cuda_device_ptr(mem);
}
void unmap_pixels(device_ptr mem)
{
if(!background) {
PixelMem pmem = pixel_mem_map[mem];
cuda_assert(cuGraphicsUnmapResources(1, &pmem.cuPBOresource, 0))
}
}
void pixels_alloc(device_memory& mem)
{
if(!background) {
PixelMem pmem;
pmem.w = mem.data_width;
pmem.h = mem.data_height;
cuda_push_context();
glGenBuffers(1, &pmem.cuPBO);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pmem.cuPBO);
glBufferData(GL_PIXEL_UNPACK_BUFFER, pmem.w*pmem.h*sizeof(GLfloat)*3, NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glGenTextures(1, &pmem.cuTexId);
glBindTexture(GL_TEXTURE_2D, pmem.cuTexId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pmem.w, pmem.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
cuda_assert(cuGraphicsGLRegisterBuffer(&pmem.cuPBOresource, pmem.cuPBO, CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE))
cuda_pop_context();
mem.device_pointer = pmem.cuTexId;
pixel_mem_map[mem.device_pointer] = pmem;
return;
}
Device::pixels_alloc(mem);
}
void pixels_copy_from(device_memory& mem, int y, int w, int h)
{
if(!background) {
PixelMem pmem = pixel_mem_map[mem.device_pointer];
cuda_push_context();
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pmem.cuPBO);
uchar *pixels = (uchar*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_ONLY);
size_t offset = sizeof(uchar)*4*y*w;
memcpy((uchar*)mem.data_pointer + offset, pixels + offset, sizeof(uchar)*4*w*h);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
cuda_pop_context();
return;
}
Device::pixels_copy_from(mem, y, w, h);
}
void pixels_free(device_memory& mem)
{
if(mem.device_pointer) {
if(!background) {
PixelMem pmem = pixel_mem_map[mem.device_pointer];
cuda_push_context();
cuda_assert(cuGraphicsUnregisterResource(pmem.cuPBOresource))
glDeleteBuffers(1, &pmem.cuPBO);
glDeleteTextures(1, &pmem.cuTexId);
cuda_pop_context();
pixel_mem_map.erase(pixel_mem_map.find(mem.device_pointer));
mem.device_pointer = 0;
return;
}
Device::pixels_free(mem);
}
}
void draw_pixels(device_memory& mem, int y, int w, int h, int width, int height, bool transparent)
{
if(!background) {
PixelMem pmem = pixel_mem_map[mem.device_pointer];
cuda_push_context();
/* for multi devices, this assumes the ineffecient method that we allocate
all pixels on the device even though we only render to a subset */
size_t offset = sizeof(uint8_t)*4*y*w;
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pmem.cuPBO);
glBindTexture(GL_TEXTURE_2D, pmem.cuTexId);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void*)offset);
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
glEnable(GL_TEXTURE_2D);
if(transparent) {
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
glColor3f(1.0f, 1.0f, 1.0f);
glPushMatrix();
glTranslatef(0.0f, (float)y, 0.0f);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex2f(0.0f, 0.0f);
glTexCoord2f((float)w/(float)pmem.w, 0.0f);
glVertex2f((float)width, 0.0f);
glTexCoord2f((float)w/(float)pmem.w, (float)h/(float)pmem.h);
glVertex2f((float)width, (float)height);
glTexCoord2f(0.0f, (float)h/(float)pmem.h);
glVertex2f(0.0f, (float)height);
glEnd();
glPopMatrix();
if(transparent)
glDisable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
cuda_pop_context();
return;
}
Device::draw_pixels(mem, y, w, h, width, height, transparent);
}
void task_add(DeviceTask& task)
{
if(task.type == DeviceTask::TONEMAP)
tonemap(task);
else if(task.type == DeviceTask::PATH_TRACE)
path_trace(task);
else if(task.type == DeviceTask::DISPLACE)
displace(task);
}
void task_wait()
{
cuda_push_context();
cuda_assert(cuCtxSynchronize())
cuda_pop_context();
}
void task_cancel()
{
}
};
Device *device_cuda_create(bool background)
{
return new CUDADevice(background);
}
CCL_NAMESPACE_END

@ -0,0 +1,35 @@
/*
* 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 __DEVICE_INTERN_H__
#define __DEVICE_INTERN_H__
CCL_NAMESPACE_BEGIN
class Device;
Device *device_cpu_create(int threads);
Device *device_opencl_create(bool background);
Device *device_cuda_create(bool background);
Device *device_network_create(const char *address);
Device *device_multi_create(bool background);
CCL_NAMESPACE_END
#endif /* __DEVICE_INTERN_H__ */

@ -0,0 +1,244 @@
/*
* 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 __DEVICE_MEMORY_H__
#define __DEVICE_MEMORY_H__
/* Device Memory
*
* This file defines data types that can be used in device memory arrays, and
* a device_vector<T> type to store such arrays.
*
* device_vector<T> contains an STL vector, metadata about the data type,
* dimensions, elements, and a device pointer. For the CPU device this is just
* a pointer to the STL vector data, as no copying needs to take place. For
* other devices this is a pointer to device memory, where we will copy memory
* to and from. */
#include "util_debug.h"
#include "util_types.h"
#include "util_vector.h"
CCL_NAMESPACE_BEGIN
/* Supported Data Types */
enum DataType {
TYPE_UCHAR,
TYPE_UINT,
TYPE_INT,
TYPE_FLOAT
};
static inline size_t datatype_size(DataType datatype)
{
switch(datatype) {
case TYPE_UCHAR: return sizeof(uchar);
case TYPE_FLOAT: return sizeof(float);
case TYPE_UINT: return sizeof(uint);
case TYPE_INT: return sizeof(int);
default: return 0;
}
}
/* Traits for data types */
template<typename T> struct device_type_traits {
static const DataType data_type = TYPE_UCHAR;
static const int num_elements = 0;
};
template<> struct device_type_traits<uchar> {
static const DataType data_type = TYPE_UCHAR;
static const int num_elements = 1;
};
template<> struct device_type_traits<uchar2> {
static const DataType data_type = TYPE_UCHAR;
static const int num_elements = 2;
};
template<> struct device_type_traits<uchar3> {
static const DataType data_type = TYPE_UCHAR;
static const int num_elements = 3;
};
template<> struct device_type_traits<uchar4> {
static const DataType data_type = TYPE_UCHAR;
static const int num_elements = 4;
};
template<> struct device_type_traits<uint> {
static const DataType data_type = TYPE_UINT;
static const int num_elements = 1;
};
template<> struct device_type_traits<uint2> {
static const DataType data_type = TYPE_UINT;
static const int num_elements = 2;
};
template<> struct device_type_traits<uint3> {
static const DataType data_type = TYPE_UINT;
static const int num_elements = 3;
};
template<> struct device_type_traits<uint4> {
static const DataType data_type = TYPE_UINT;
static const int num_elements = 4;
};
template<> struct device_type_traits<int> {
static const DataType data_type = TYPE_INT;
static const int num_elements = 1;
};
template<> struct device_type_traits<int2> {
static const DataType data_type = TYPE_INT;
static const int num_elements = 2;
};
template<> struct device_type_traits<int3> {
static const DataType data_type = TYPE_INT;
static const int num_elements = 3;
};
template<> struct device_type_traits<int4> {
static const DataType data_type = TYPE_INT;
static const int num_elements = 4;
};
template<> struct device_type_traits<float> {
static const DataType data_type = TYPE_FLOAT;
static const int num_elements = 1;
};
template<> struct device_type_traits<float2> {
static const DataType data_type = TYPE_FLOAT;
static const int num_elements = 2;
};
template<> struct device_type_traits<float3> {
static const DataType data_type = TYPE_FLOAT;
static const int num_elements = 3;
};
template<> struct device_type_traits<float4> {
static const DataType data_type = TYPE_FLOAT;
static const int num_elements = 4;
};
/* Device Memory */
class device_memory
{
public:
size_t memory_size() { return data_size*data_elements*datatype_size(data_type); }
/* data information */
DataType data_type;
int data_elements;
device_ptr data_pointer;
size_t data_size;
size_t data_width;
size_t data_height;
/* device pointer */
device_ptr device_pointer;
protected:
device_memory() {}
virtual ~device_memory() { assert(!device_pointer); }
/* no copying */
device_memory(const device_memory&);
device_memory& operator = (const device_memory&);
};
/* Device Vector */
template<typename T> class device_vector : public device_memory
{
public:
device_vector()
{
data_type = device_type_traits<T>::data_type;
data_elements = device_type_traits<T>::num_elements;
data_pointer = 0;
data_size = 0;
data_width = 0;
data_height = 0;
assert(data_elements > 0);
device_pointer = 0;
}
virtual ~device_vector() {}
/* vector functions */
T *resize(size_t width, size_t height = 0)
{
data_size = (height == 0)? width: width*height;
data.resize(data_size);
data_pointer = (device_ptr)&data[0];
data_width = width;
data_height = height;
return &data[0];
}
T *copy(T *ptr, size_t width, size_t height = 0)
{
T *mem = resize(width, height);
memcpy(mem, ptr, memory_size());
return mem;
}
void reference(T *ptr, size_t width, size_t height = 0)
{
data.clear();
data_size = (height == 0)? width: width*height;
data_pointer = (device_ptr)ptr;
data_width = width;
data_height = height;
}
void clear()
{
data.clear();
data_pointer = 0;
data_width = 0;
data_height = 0;
data_size = 0;
}
size_t size()
{
return data.size();
}
private:
array<T> data;
bool referenced;
};
CCL_NAMESPACE_END
#endif /* __DEVICE_MEMORY_H__ */

@ -0,0 +1,336 @@
/*
* 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 <stdlib.h>
#include <sstream>
#include "device.h"
#include "device_intern.h"
#include "device_network.h"
#include "util_foreach.h"
#include "util_list.h"
#include "util_map.h"
#include "util_time.h"
CCL_NAMESPACE_BEGIN
class MultiDevice : public Device
{
public:
struct SubDevice {
SubDevice(Device *device_)
: device(device_) {}
Device *device;
map<device_ptr, device_ptr> ptr_map;
};
list<SubDevice> devices;
device_ptr unique_ptr;
MultiDevice(bool background_)
: unique_ptr(1)
{
Device *device;
/* add CPU device */
device = Device::create(DEVICE_CPU, background);
devices.push_back(SubDevice(device));
#ifdef WITH_CUDA
/* try to add GPU device */
device = Device::create(DEVICE_CUDA, background);
if(device) {
devices.push_back(SubDevice(device));
}
else
#endif
{
#ifdef WITH_OPENCL
device = Device::create(DEVICE_OPENCL, background);
if(device)
devices.push_back(SubDevice(device));
#endif
}
#ifdef WITH_NETWORK
/* try to add network devices */
ServerDiscovery discovery(true);
time_sleep(1.0);
list<string> servers = discovery.get_server_list();
foreach(string& server, servers) {
device = device_network_create(server.c_str());
if(device)
devices.push_back(SubDevice(device));
}
#endif
}
~MultiDevice()
{
foreach(SubDevice& sub, devices)
delete sub.device;
}
bool support_full_kernel()
{
foreach(SubDevice& sub, devices) {
if(!sub.device->support_full_kernel())
return false;
}
return true;
}
string description()
{
/* create map to find duplicate descriptions */
map<string, int> dupli_map;
map<string, int>::iterator dt;
foreach(SubDevice& sub, devices) {
string key = sub.device->description();
if(dupli_map.find(key) == dupli_map.end())
dupli_map[key] = 1;
else
dupli_map[key]++;
}
/* generate string */
stringstream desc;
bool first = true;
for(dt = dupli_map.begin(); dt != dupli_map.end(); dt++) {
if(!first) desc << ", ";
first = false;
if(dt->second > 1)
desc << dt->second << "x " << dt->first;
else
desc << dt->first;
}
return desc.str();
}
bool load_kernels()
{
foreach(SubDevice& sub, devices)
if(!sub.device->load_kernels())
return false;
return true;
}
void mem_alloc(device_memory& mem, MemoryType type)
{
foreach(SubDevice& sub, devices) {
mem.device_pointer = 0;
sub.device->mem_alloc(mem, type);
sub.ptr_map[unique_ptr] = mem.device_pointer;
}
mem.device_pointer = unique_ptr++;
}
void mem_copy_to(device_memory& mem)
{
device_ptr tmp = mem.device_pointer;
foreach(SubDevice& sub, devices) {
mem.device_pointer = sub.ptr_map[tmp];
sub.device->mem_copy_to(mem);
}
mem.device_pointer = tmp;
}
void mem_copy_from(device_memory& mem, size_t offset, size_t size)
{
device_ptr tmp = mem.device_pointer;
/* todo: how does this work? */
foreach(SubDevice& sub, devices) {
mem.device_pointer = sub.ptr_map[tmp];
sub.device->mem_copy_from(mem, offset, size);
break;
}
mem.device_pointer = tmp;
}
void mem_zero(device_memory& mem)
{
device_ptr tmp = mem.device_pointer;
foreach(SubDevice& sub, devices) {
mem.device_pointer = sub.ptr_map[tmp];
sub.device->mem_zero(mem);
}
mem.device_pointer = tmp;
}
void mem_free(device_memory& mem)
{
device_ptr tmp = mem.device_pointer;
foreach(SubDevice& sub, devices) {
mem.device_pointer = sub.ptr_map[tmp];
sub.device->mem_free(mem);
sub.ptr_map.erase(sub.ptr_map.find(tmp));
}
mem.device_pointer = 0;
}
void const_copy_to(const char *name, void *host, size_t size)
{
foreach(SubDevice& sub, devices)
sub.device->const_copy_to(name, host, size);
}
void tex_alloc(const char *name, device_memory& mem, bool interpolation, bool periodic)
{
foreach(SubDevice& sub, devices) {
mem.device_pointer = 0;
sub.device->tex_alloc(name, mem, interpolation, periodic);
sub.ptr_map[unique_ptr] = mem.device_pointer;
}
mem.device_pointer = unique_ptr++;
}
void tex_free(device_memory& mem)
{
device_ptr tmp = mem.device_pointer;
foreach(SubDevice& sub, devices) {
mem.device_pointer = sub.ptr_map[tmp];
sub.device->tex_free(mem);
sub.ptr_map.erase(sub.ptr_map.find(tmp));
}
mem.device_pointer = 0;
}
void pixels_alloc(device_memory& mem)
{
foreach(SubDevice& sub, devices) {
mem.device_pointer = 0;
sub.device->pixels_alloc(mem);
sub.ptr_map[unique_ptr] = mem.device_pointer;
}
mem.device_pointer = unique_ptr++;
}
void pixels_free(device_memory& mem)
{
device_ptr tmp = mem.device_pointer;
foreach(SubDevice& sub, devices) {
mem.device_pointer = sub.ptr_map[tmp];
sub.device->pixels_free(mem);
sub.ptr_map.erase(sub.ptr_map.find(tmp));
}
mem.device_pointer = 0;
}
void pixels_copy_from(device_memory& mem, int y, int w, int h)
{
device_ptr tmp = mem.device_pointer;
int i = 0, sub_h = h/devices.size();
foreach(SubDevice& sub, devices) {
int sy = y + i*sub_h;
int sh = (i == (int)devices.size() - 1)? h - sub_h*i: sub_h;
mem.device_pointer = sub.ptr_map[tmp];
sub.device->pixels_copy_from(mem, sy, w, sh);
i++;
}
mem.device_pointer = tmp;
}
void draw_pixels(device_memory& rgba, int y, int w, int h, int width, int height, bool transparent)
{
device_ptr tmp = rgba.device_pointer;
int i = 0, sub_h = h/devices.size();
int sub_height = height/devices.size();
foreach(SubDevice& sub, devices) {
int sy = y + i*sub_h;
int sh = (i == (int)devices.size() - 1)? h - sub_h*i: sub_h;
int sheight = (i == (int)devices.size() - 1)? height - sub_height*i: sub_height;
/* adjust math for w/width */
rgba.device_pointer = sub.ptr_map[tmp];
sub.device->draw_pixels(rgba, sy, w, sh, width, sheight, transparent);
i++;
}
rgba.device_pointer = tmp;
}
void task_add(DeviceTask& task)
{
ThreadQueue<DeviceTask> tasks;
task.split(tasks, devices.size());
foreach(SubDevice& sub, devices) {
DeviceTask subtask;
if(tasks.worker_wait_pop(subtask)) {
if(task.buffer) subtask.buffer = sub.ptr_map[task.buffer];
if(task.rng_state) subtask.rng_state = sub.ptr_map[task.rng_state];
if(task.rgba) subtask.rgba = sub.ptr_map[task.rgba];
if(task.displace_input) subtask.displace_input = sub.ptr_map[task.displace_input];
if(task.displace_offset) subtask.displace_offset = sub.ptr_map[task.displace_offset];
sub.device->task_add(subtask);
}
}
}
void task_wait()
{
foreach(SubDevice& sub, devices)
sub.device->task_wait();
}
void task_cancel()
{
foreach(SubDevice& sub, devices)
sub.device->task_cancel();
}
};
Device *device_multi_create(bool background)
{
return new MultiDevice(background);
}
CCL_NAMESPACE_END

@ -0,0 +1,391 @@
/*
* 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 "device.h"
#include "device_intern.h"
#include "device_network.h"
#include "util_foreach.h"
CCL_NAMESPACE_BEGIN
#ifdef WITH_NETWORK
class NetworkDevice : public Device
{
public:
boost::asio::io_service io_service;
tcp::socket socket;
NetworkDevice(const char *address)
: socket(io_service)
{
stringstream portstr;
portstr << SERVER_PORT;
tcp::resolver resolver(io_service);
tcp::resolver::query query(address, portstr.str());
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
boost::system::error_code error = boost::asio::error::host_not_found;
while(error && endpoint_iterator != end)
{
socket.close();
socket.connect(*endpoint_iterator++, error);
}
if(error)
throw boost::system::system_error(error);
}
~NetworkDevice()
{
}
bool support_full_kernel()
{
return false;
}
string description()
{
RPCSend snd(socket, "description");
snd.write();
RPCReceive rcv(socket);
string desc_string;
*rcv.archive & desc_string;
return desc_string + " (remote)";
}
void mem_alloc(device_memory& mem, MemoryType type)
{
#if 0
RPCSend snd(socket, "mem_alloc");
snd.archive & size & type;
snd.write();
RPCReceive rcv(socket);
device_ptr mem;
*rcv.archive & mem;
return mem;
#endif
}
void mem_copy_to(device_memory& mem)
{
#if 0
RPCSend snd(socket, "mem_copy_to");
snd.archive & mem & size;
snd.write();
snd.write_buffer(host, size);
#endif
}
void mem_copy_from(device_memory& mem, size_t offset, size_t size)
{
#if 0
RPCSend snd(socket, "mem_copy_from");
snd.archive & mem & offset & size;
snd.write();
RPCReceive rcv(socket);
rcv.read_buffer(host, size);
#endif
}
void mem_zero(device_memory& mem)
{
#if 0
RPCSend snd(socket, "mem_zero");
snd.archive & mem & size;
snd.write();
#endif
}
void mem_free(device_memory& mem)
{
#if 0
if(mem) {
RPCSend snd(socket, "mem_free");
snd.archive & mem;
snd.write();
}
#endif
}
void const_copy_to(const char *name, void *host, size_t size)
{
RPCSend snd(socket, "const_copy_to");
string name_string(name);
snd.archive & name_string & size;
snd.write();
snd.write_buffer(host, size);
}
void tex_alloc(const char *name, device_memory& mem, bool interpolation, bool periodic)
{
#if 0
RPCSend snd(socket, "tex_alloc");
string name_string(name);
snd.archive & name_string & width & height & datatype & components & interpolation;
snd.write();
size_t size = width*height*components*datatype_size(datatype);
snd.write_buffer(host, size);
RPCReceive rcv(socket);
device_ptr mem;
*rcv.archive & mem;
return mem;
#endif
}
void tex_free(device_memory& mem)
{
#if 0
if(mem) {
RPCSend snd(socket, "tex_free");
snd.archive & mem;
snd.write();
}
#endif
}
void path_trace(int x, int y, int w, int h, device_ptr buffer, device_ptr rng_state, int sample)
{
#if 0
RPCSend snd(socket, "path_trace");
snd.archive & x & y & w & h & buffer & rng_state & sample;
snd.write();
#endif
}
void tonemap(int x, int y, int w, int h, device_ptr rgba, device_ptr buffer, int sample, int resolution)
{
#if 0
RPCSend snd(socket, "tonemap");
snd.archive & x & y & w & h & rgba & buffer & sample & resolution;
snd.write();
#endif
}
void task_add(DeviceTask& task)
{
if(task.type == DeviceTask::TONEMAP)
tonemap(task.x, task.y, task.w, task.h, task.rgba, task.buffer, task.sample, task.resolution);
else if(task.type == DeviceTask::PATH_TRACE)
path_trace(task.x, task.y, task.w, task.h, task.buffer, task.rng_state, task.sample);
}
void task_wait()
{
}
void task_cancel()
{
}
};
Device *device_network_create(const char *address)
{
return new NetworkDevice(address);
}
void Device::server_run()
{
try
{
/* starts thread that responds to discovery requests */
ServerDiscovery discovery;
for(;;)
{
/* accept connection */
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), SERVER_PORT));
tcp::socket socket(io_service);
acceptor.accept(socket);
/* receive remote function calls */
for(;;) {
RPCReceive rcv(socket);
if(rcv.name == "description") {
string desc = description();
RPCSend snd(socket);
snd.archive & desc;
snd.write();
}
else if(rcv.name == "mem_alloc") {
#if 0
MemoryType type;
size_t size;
device_ptr mem;
*rcv.archive & size & type;
mem = mem_alloc(size, type);
RPCSend snd(socket);
snd.archive & mem;
snd.write();
#endif
}
else if(rcv.name == "mem_copy_to") {
#if 0
device_ptr mem;
size_t size;
*rcv.archive & mem & size;
vector<char> host_vector(size);
rcv.read_buffer(&host_vector[0], size);
mem_copy_to(mem, &host_vector[0], size);
#endif
}
else if(rcv.name == "mem_copy_from") {
#if 0
device_ptr mem;
size_t offset, size;
*rcv.archive & mem & offset & size;
vector<char> host_vector(size);
mem_copy_from(&host_vector[0], mem, offset, size);
RPCSend snd(socket);
snd.write();
snd.write_buffer(&host_vector[0], size);
#endif
}
else if(rcv.name == "mem_zero") {
#if 0
device_ptr mem;
size_t size;
*rcv.archive & mem & size;
mem_zero(mem, size);
#endif
}
else if(rcv.name == "mem_free") {
#if 0
device_ptr mem;
*rcv.archive & mem;
mem_free(mem);
#endif
}
else if(rcv.name == "const_copy_to") {
string name_string;
size_t size;
*rcv.archive & name_string & size;
vector<char> host_vector(size);
rcv.read_buffer(&host_vector[0], size);
const_copy_to(name_string.c_str(), &host_vector[0], size);
}
else if(rcv.name == "tex_alloc") {
#if 0
string name_string;
DataType datatype;
device_ptr mem;
size_t width, height;
int components;
bool interpolation;
*rcv.archive & name_string & width & height & datatype & components & interpolation;
size_t size = width*height*components*datatype_size(datatype);
vector<char> host_vector(size);
rcv.read_buffer(&host_vector[0], size);
mem = tex_alloc(name_string.c_str(), &host_vector[0], width, height, datatype, components, interpolation);
RPCSend snd(socket);
snd.archive & mem;
snd.write();
#endif
}
else if(rcv.name == "tex_free") {
#if 0
device_ptr mem;
*rcv.archive & mem;
tex_free(mem);
#endif
}
else if(rcv.name == "path_trace") {
#if 0
device_ptr buffer, rng_state;
int x, y, w, h;
int sample;
*rcv.archive & x & y & w & h & buffer & rng_state & sample;
path_trace(x, y, w, h, buffer, rng_state, sample);
#endif
}
else if(rcv.name == "tonemap") {
#if 0
device_ptr rgba, buffer;
int x, y, w, h;
int sample, resolution;
*rcv.archive & x & y & w & h & rgba & buffer & sample & resolution;
tonemap(x, y, w, h, rgba, buffer, sample, resolution);
#endif
}
}
}
}
catch(exception& e)
{
cerr << "Network server exception: " << e.what() << endl;
}
}
#endif
CCL_NAMESPACE_END

@ -0,0 +1,308 @@
/*
* 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 __DEVICE_NETWORK_H__
#define __DEVICE_NETWORK_H__
#ifdef WITH_NETWORK
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/thread.hpp>
#include <iostream>
#include "util_foreach.h"
#include "util_list.h"
#include "util_string.h"
CCL_NAMESPACE_BEGIN
using std::cout;
using std::cerr;
using std::endl;
using std::hex;
using std::setw;
using std::exception;
using boost::asio::ip::tcp;
static const int SERVER_PORT = 5120;
static const int DISCOVER_PORT = 5121;
static const string DISCOVER_REQUEST_MSG = "REQUEST_RENDER_SERVER_IP";
static const string DISCOVER_REPLY_MSG = "REPLY_RENDER_SERVER_IP";
typedef struct RPCSend {
RPCSend(tcp::socket& socket_, const string& name_ = "")
: name(name_), socket(socket_), archive(archive_stream)
{
archive & name_;
}
void write()
{
boost::system::error_code error;
/* get string from stream */
string archive_str = archive_stream.str();
/* first send fixed size header with size of following data */
ostringstream header_stream;
header_stream << setw(8) << hex << archive_str.size();
string header_str = header_stream.str();
boost::asio::write(socket,
boost::asio::buffer(header_str),
boost::asio::transfer_all(), error);
if(error.value())
cout << "Network send error: " << error.message() << "\n";
/* then send actual data */
boost::asio::write(socket,
boost::asio::buffer(archive_str),
boost::asio::transfer_all(), error);
if(error.value())
cout << "Network send error: " << error.message() << "\n";
}
void write_buffer(void *buffer, size_t size)
{
boost::system::error_code error;
boost::asio::write(socket,
boost::asio::buffer(buffer, size),
boost::asio::transfer_all(), error);
if(error.value())
cout << "Network send error: " << error.message() << "\n";
}
string name;
tcp::socket& socket;
ostringstream archive_stream;
boost::archive::text_oarchive archive;
} RPCSend;
typedef struct RPCReceive {
RPCReceive(tcp::socket& socket_)
: socket(socket_), archive_stream(NULL), archive(NULL)
{
/* read head with fixed size */
vector<char> header(8);
size_t len = boost::asio::read(socket, boost::asio::buffer(header));
/* verify if we got something */
if(len == header.size()) {
/* decode header */
string header_str(&header[0], header.size());
istringstream header_stream(header_str);
size_t data_size;
if((header_stream >> hex >> data_size)) {
vector<char> data(data_size);
size_t len = boost::asio::read(socket, boost::asio::buffer(data));
if(len == data_size) {
archive_str = (data.size())? string(&data[0], data.size()): string("");
/*istringstream archive_stream(archive_str);
boost::archive::text_iarchive archive(archive_stream);*/
archive_stream = new istringstream(archive_str);
archive = new boost::archive::text_iarchive(*archive_stream);
*archive & name;
}
else
cout << "Network receive error: data size doens't match header\n";
}
else
cout << "Network receive error: can't decode data size from header\n";
}
else
cout << "Network receive error: invalid header size\n";
}
~RPCReceive()
{
delete archive;
delete archive_stream;
}
void read_buffer(void *buffer, size_t size)
{
size_t len = boost::asio::read(socket, boost::asio::buffer(buffer, size));
if(len != size)
cout << "Network receive error: buffer size doesn't match expected size\n";
}
string name;
tcp::socket& socket;
string archive_str;
istringstream *archive_stream;
boost::archive::text_iarchive *archive;
} RPCReceive;
class ServerDiscovery {
public:
ServerDiscovery(bool discover = false)
: listen_socket(io_service), collect_servers(false)
{
/* setup listen socket */
listen_endpoint.address(boost::asio::ip::address_v4::any());
listen_endpoint.port(DISCOVER_PORT);
listen_socket.open(listen_endpoint.protocol());
boost::asio::socket_base::reuse_address option(true);
listen_socket.set_option(option);
listen_socket.bind(listen_endpoint);
/* setup receive callback */
async_receive();
/* start server discovery */
if(discover) {
collect_servers = true;
servers.clear();
broadcast_message(DISCOVER_REQUEST_MSG);
}
/* start thread */
work = new boost::asio::io_service::work(io_service);
thread = new boost::thread(boost::bind(&boost::asio::io_service::run, &io_service));
}
~ServerDiscovery()
{
io_service.stop();
thread->join();
delete thread;
delete work;
}
list<string> get_server_list()
{
list<string> result;
mutex.lock();
result = servers;
mutex.unlock();
return result;
}
private:
void handle_receive_from(const boost::system::error_code& error, size_t size)
{
if(error) {
cout << "Server discovery receive error: " << error.message() << "\n";
return;
}
if(size > 0) {
string msg = string(receive_buffer, size);
/* handle incoming message */
if(collect_servers) {
if(msg == DISCOVER_REPLY_MSG) {
string address = receive_endpoint.address().to_string();
mutex.lock();
/* add address if it's not already in the list */
bool found = false;
foreach(string& server, servers)
if(server == address)
found = true;
if(!found)
servers.push_back(address);
mutex.unlock();
}
}
else {
/* reply to request */
if(msg == DISCOVER_REQUEST_MSG)
broadcast_message(DISCOVER_REPLY_MSG);
}
}
async_receive();
}
void async_receive()
{
listen_socket.async_receive_from(
boost::asio::buffer(receive_buffer), receive_endpoint,
boost::bind(&ServerDiscovery::handle_receive_from, this,
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
void broadcast_message(const string& msg)
{
/* setup broadcast socket */
boost::asio::ip::udp::socket socket(io_service);
socket.open(boost::asio::ip::udp::v4());
boost::asio::socket_base::broadcast option(true);
socket.set_option(option);
boost::asio::ip::udp::endpoint broadcast_endpoint(
boost::asio::ip::address::from_string("255.255.255.255"), DISCOVER_PORT);
/* broadcast message */
socket.send_to(boost::asio::buffer(msg), broadcast_endpoint);
}
/* network service and socket */
boost::asio::io_service io_service;
boost::asio::ip::udp::endpoint listen_endpoint;
boost::asio::ip::udp::socket listen_socket;
/* threading */
boost::thread *thread;
boost::asio::io_service::work *work;
boost::mutex mutex;
/* buffer and endpoint for receiving messages */
char receive_buffer[256];
boost::asio::ip::udp::endpoint receive_endpoint;
/* collection of server addresses in list */
bool collect_servers;
list<string> servers;
};
CCL_NAMESPACE_END
#endif
#endif /* __DEVICE_NETWORK_H__ */

@ -0,0 +1,648 @@
/*
* 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.
*/
#ifdef WITH_OPENCL
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "device.h"
#include "device_intern.h"
#include "util_map.h"
#include "util_math.h"
#include "util_md5.h"
#include "util_opencl.h"
#include "util_opengl.h"
#include "util_path.h"
#include "util_time.h"
CCL_NAMESPACE_BEGIN
#define CL_MEM_PTR(p) ((cl_mem)(unsigned long)(p))
class OpenCLDevice : public Device
{
public:
cl_context cxContext;
cl_command_queue cqCommandQueue;
cl_platform_id cpPlatform;
cl_device_id cdDevice;
cl_program cpProgram;
cl_kernel ckPathTraceKernel;
cl_kernel ckFilmConvertKernel;
cl_int ciErr;
map<string, device_vector<uchar>*> const_mem_map;
map<string, device_memory*> mem_map;
device_ptr null_mem;
bool device_initialized;
const char *opencl_error_string(cl_int err)
{
switch (err) {
case CL_SUCCESS: return "Success!";
case CL_DEVICE_NOT_FOUND: return "Device not found.";
case CL_DEVICE_NOT_AVAILABLE: return "Device not available";
case CL_COMPILER_NOT_AVAILABLE: return "Compiler not available";
case CL_MEM_OBJECT_ALLOCATION_FAILURE: return "Memory object allocation failure";
case CL_OUT_OF_RESOURCES: return "Out of resources";
case CL_OUT_OF_HOST_MEMORY: return "Out of host memory";
case CL_PROFILING_INFO_NOT_AVAILABLE: return "Profiling information not available";
case CL_MEM_COPY_OVERLAP: return "Memory copy overlap";
case CL_IMAGE_FORMAT_MISMATCH: return "Image format mismatch";
case CL_IMAGE_FORMAT_NOT_SUPPORTED: return "Image format not supported";
case CL_BUILD_PROGRAM_FAILURE: return "Program build failure";
case CL_MAP_FAILURE: return "Map failure";
case CL_INVALID_VALUE: return "Invalid value";
case CL_INVALID_DEVICE_TYPE: return "Invalid device type";
case CL_INVALID_PLATFORM: return "Invalid platform";
case CL_INVALID_DEVICE: return "Invalid device";
case CL_INVALID_CONTEXT: return "Invalid context";
case CL_INVALID_QUEUE_PROPERTIES: return "Invalid queue properties";
case CL_INVALID_COMMAND_QUEUE: return "Invalid command queue";
case CL_INVALID_HOST_PTR: return "Invalid host pointer";
case CL_INVALID_MEM_OBJECT: return "Invalid memory object";
case CL_INVALID_IMAGE_FORMAT_DESCRIPTOR: return "Invalid image format descriptor";
case CL_INVALID_IMAGE_SIZE: return "Invalid image size";
case CL_INVALID_SAMPLER: return "Invalid sampler";
case CL_INVALID_BINARY: return "Invalid binary";
case CL_INVALID_BUILD_OPTIONS: return "Invalid build options";
case CL_INVALID_PROGRAM: return "Invalid program";
case CL_INVALID_PROGRAM_EXECUTABLE: return "Invalid program executable";
case CL_INVALID_KERNEL_NAME: return "Invalid kernel name";
case CL_INVALID_KERNEL_DEFINITION: return "Invalid kernel definition";
case CL_INVALID_KERNEL: return "Invalid kernel";
case CL_INVALID_ARG_INDEX: return "Invalid argument index";
case CL_INVALID_ARG_VALUE: return "Invalid argument value";
case CL_INVALID_ARG_SIZE: return "Invalid argument size";
case CL_INVALID_KERNEL_ARGS: return "Invalid kernel arguments";
case CL_INVALID_WORK_DIMENSION: return "Invalid work dimension";
case CL_INVALID_WORK_GROUP_SIZE: return "Invalid work group size";
case CL_INVALID_WORK_ITEM_SIZE: return "Invalid work item size";
case CL_INVALID_GLOBAL_OFFSET: return "Invalid global offset";
case CL_INVALID_EVENT_WAIT_LIST: return "Invalid event wait list";
case CL_INVALID_EVENT: return "Invalid event";
case CL_INVALID_OPERATION: return "Invalid operation";
case CL_INVALID_GL_OBJECT: return "Invalid OpenGL object";
case CL_INVALID_BUFFER_SIZE: return "Invalid buffer size";
case CL_INVALID_MIP_LEVEL: return "Invalid mip-map level";
default: return "Unknown";
}
}
bool opencl_error(cl_int err)
{
if(err != CL_SUCCESS) {
fprintf(stderr, "OpenCL error (%d): %s\n", err, opencl_error_string(err));
return true;
}
return false;
}
void opencl_assert(cl_int err)
{
if(err != CL_SUCCESS) {
fprintf(stderr, "OpenCL error (%d): %s\n", err, opencl_error_string(err));
#ifndef NDEBUG
abort();
#endif
}
}
OpenCLDevice(bool background_)
{
background = background_;
cpPlatform = NULL;
cxContext = NULL;
cqCommandQueue = NULL;
cpProgram = NULL;
ckPathTraceKernel = NULL;
ckFilmConvertKernel = NULL;
null_mem = 0;
device_initialized = false;
vector<cl_platform_id> platform_ids;
cl_uint num_platforms;
/* setup device */
ciErr = clGetPlatformIDs(0, NULL, &num_platforms);
if(opencl_error(ciErr))
return;
if(num_platforms == 0) {
fprintf(stderr, "OpenCL: no platforms found.\n");
return;
}
platform_ids.resize(num_platforms);
ciErr = clGetPlatformIDs(num_platforms, &platform_ids[0], NULL);
if(opencl_error(ciErr))
return;
cpPlatform = platform_ids[0]; /* todo: pick specified platform && device */
ciErr = clGetDeviceIDs(cpPlatform, CL_DEVICE_TYPE_GPU|CL_DEVICE_TYPE_ACCELERATOR, 1, &cdDevice, NULL);
if(opencl_error(ciErr))
return;
cxContext = clCreateContext(0, 1, &cdDevice, NULL, NULL, &ciErr);
if(opencl_error(ciErr))
return;
cqCommandQueue = clCreateCommandQueue(cxContext, cdDevice, 0, &ciErr);
if(opencl_error(ciErr))
return;
null_mem = (device_ptr)clCreateBuffer(cxContext, CL_MEM_READ_ONLY, 1, NULL, &ciErr);
device_initialized = true;
}
bool opencl_version_check()
{
char version[256];
int major, minor, req_major = 1, req_minor = 1;
clGetPlatformInfo(cpPlatform, CL_PLATFORM_VERSION, sizeof(version), &version, NULL);
if(sscanf(version, "OpenCL %d.%d", &major, &minor) < 2) {
fprintf(stderr, "OpenCL: failed to parse platform version string (%s).", version);
return false;
}
if(!((major == req_major && minor >= req_minor) || (major > req_major))) {
fprintf(stderr, "OpenCL: platform version 1.1 or later required, found %d.%d\n", major, minor);
return false;
}
clGetDeviceInfo(cdDevice, CL_DEVICE_OPENCL_C_VERSION, sizeof(version), &version, NULL);
if(sscanf(version, "OpenCL C %d.%d", &major, &minor) < 2) {
fprintf(stderr, "OpenCL: failed to parse OpenCL C version string (%s).", version);
return false;
}
if(!((major == req_major && minor >= req_minor) || (major > req_major))) {
fprintf(stderr, "OpenCL: C version 1.1 or later required, found %d.%d\n", major, minor);
return false;
}
/* we don't check CL_DEVICE_VERSION since for e.g. nvidia sm 1.3 cards this is
1.0 even if the language features are there, just limited shared memory */
return true;
}
bool load_binary(const string& kernel_path, const string& clbin)
{
/* read binary into memory */
vector<uint8_t> binary;
if(!path_read_binary(clbin, binary)) {
fprintf(stderr, "OpenCL failed to read cached binary %s.\n", clbin.c_str());
return false;
}
/* create program */
cl_int status;
size_t size = binary.size();
const uint8_t *bytes = &binary[0];
cpProgram = clCreateProgramWithBinary(cxContext, 1, &cdDevice,
&size, &bytes, &status, &ciErr);
if(opencl_error(status) || opencl_error(ciErr)) {
fprintf(stderr, "OpenCL failed create program from cached binary %s.\n", clbin.c_str());
return false;
}
if(!build_kernel(kernel_path))
return false;
return true;
}
bool save_binary(const string& clbin)
{
size_t size = 0;
clGetProgramInfo(cpProgram, CL_PROGRAM_BINARY_SIZES, sizeof(size_t), &size, NULL);
if(!size)
return false;
vector<uint8_t> binary(size);
uint8_t *bytes = &binary[0];
clGetProgramInfo(cpProgram, CL_PROGRAM_BINARIES, sizeof(uint8_t*), &bytes, NULL);
if(!path_write_binary(clbin, binary)) {
fprintf(stderr, "OpenCL failed to write cached binary %s.\n", clbin.c_str());
return false;
}
return true;
}
bool build_kernel(const string& kernel_path)
{
string build_options = "";
build_options += "-I " + kernel_path + ""; /* todo: escape path */
build_options += " -cl-fast-relaxed-math ";
ciErr = clBuildProgram(cpProgram, 0, NULL, build_options.c_str(), NULL, NULL);
if(ciErr != CL_SUCCESS) {
/* show build errors */
char *build_log;
size_t ret_val_size;
clGetProgramBuildInfo(cpProgram, cdDevice, CL_PROGRAM_BUILD_LOG, 0, NULL, &ret_val_size);
build_log = new char[ret_val_size+1];
clGetProgramBuildInfo(cpProgram, cdDevice, CL_PROGRAM_BUILD_LOG, ret_val_size, build_log, NULL);
build_log[ret_val_size] = '\0';
fprintf(stderr, "OpenCL build failed:\n %s\n", build_log);
delete[] build_log;
return false;
}
return true;
}
bool compile_kernel(const string& kernel_path, const string& kernel_md5)
{
/* we compile kernels consisting of many files. unfortunately opencl
kernel caches do not seem to recognize changes in included files.
so we force recompile on changes by adding the md5 hash of all files */
string source = "#include \"kernel.cl\" // " + kernel_md5 + "\n";
size_t source_len = source.size();
const char *source_str = source.c_str();
cpProgram = clCreateProgramWithSource(cxContext, 1, &source_str, &source_len, &ciErr);
if(opencl_error(ciErr))
return false;
double starttime = time_dt();
printf("Compiling OpenCL kernel ...\n");
if(!build_kernel(kernel_path))
return false;
printf("Kernel compilation finished in %.2lfs.\n", time_dt() - starttime);
return true;
}
string device_md5_hash()
{
MD5Hash md5;
char version[256], driver[256], name[256], vendor[256];
clGetPlatformInfo(cpPlatform, CL_PLATFORM_VENDOR, sizeof(vendor), &vendor, NULL);
clGetDeviceInfo(cdDevice, CL_DEVICE_VERSION, sizeof(version), &version, NULL);
clGetDeviceInfo(cdDevice, CL_DEVICE_NAME, sizeof(name), &name, NULL);
clGetDeviceInfo(cdDevice, CL_DRIVER_VERSION, sizeof(driver), &driver, NULL);
md5.append((uint8_t*)vendor, strlen(vendor));
md5.append((uint8_t*)version, strlen(version));
md5.append((uint8_t*)name, strlen(name));
md5.append((uint8_t*)driver, strlen(driver));
return md5.get_hex();
}
bool load_kernels()
{
/* verify if device was initialized */
if(!device_initialized) {
fprintf(stderr, "OpenCL: failed to initialize device.\n");
return false;
}
/* verify we have right opencl version */
if(!opencl_version_check())
return false;
/* md5 hash to detect changes */
string kernel_path = path_get("kernel");
string kernel_md5 = path_files_md5_hash(kernel_path);
string device_md5 = device_md5_hash();
/* try to use cache binary */
string clbin = string_printf("cycles_kernel_%s_%s.clbin", device_md5.c_str(), kernel_md5.c_str());;
clbin = path_user_get(path_join("cache", clbin));
if(path_exists(clbin)) {
/* if exists already, try use it */
if(!load_binary(kernel_path, clbin))
return false;
}
else {
/* compile kernel */
if(!compile_kernel(kernel_path, kernel_md5))
return false;
/* save binary for reuse */
save_binary(clbin);
}
/* find kernels */
ckPathTraceKernel = clCreateKernel(cpProgram, "kernel_ocl_path_trace", &ciErr);
if(opencl_error(ciErr))
return false;
ckFilmConvertKernel = clCreateKernel(cpProgram, "kernel_ocl_tonemap", &ciErr);
if(opencl_error(ciErr))
return false;
return true;
}
~OpenCLDevice()
{
if(null_mem)
clReleaseMemObject(CL_MEM_PTR(null_mem));
map<string, device_vector<uchar>*>::iterator mt;
for(mt = const_mem_map.begin(); mt != const_mem_map.end(); mt++) {
mem_free(*(mt->second));
delete mt->second;
}
if(ckPathTraceKernel)
clReleaseKernel(ckPathTraceKernel);
if(ckFilmConvertKernel)
clReleaseKernel(ckFilmConvertKernel);
if(cpProgram)
clReleaseProgram(cpProgram);
if(cqCommandQueue)
clReleaseCommandQueue(cqCommandQueue);
if(cxContext)
clReleaseContext(cxContext);
}
bool support_full_kernel()
{
return false;
}
string description()
{
char name[1024];
clGetDeviceInfo(cdDevice, CL_DEVICE_NAME, sizeof(name), &name, NULL);
return string("OpenCL ") + name;
}
void mem_alloc(device_memory& mem, MemoryType type)
{
size_t size = mem.memory_size();
if(type == MEM_READ_ONLY)
mem.device_pointer = (device_ptr)clCreateBuffer(cxContext, CL_MEM_READ_ONLY, size, NULL, &ciErr);
else if(type == MEM_WRITE_ONLY)
mem.device_pointer = (device_ptr)clCreateBuffer(cxContext, CL_MEM_WRITE_ONLY, size, NULL, &ciErr);
else
mem.device_pointer = (device_ptr)clCreateBuffer(cxContext, CL_MEM_READ_WRITE, size, NULL, &ciErr);
opencl_assert(ciErr);
}
void mem_copy_to(device_memory& mem)
{
/* this is blocking */
size_t size = mem.memory_size();
ciErr = clEnqueueWriteBuffer(cqCommandQueue, CL_MEM_PTR(mem.device_pointer), CL_TRUE, 0, size, (void*)mem.data_pointer, 0, NULL, NULL);
opencl_assert(ciErr);
}
void mem_copy_from(device_memory& mem, size_t offset, size_t size)
{
ciErr = clEnqueueReadBuffer(cqCommandQueue, CL_MEM_PTR(mem.device_pointer), CL_TRUE, offset, size, (uchar*)mem.data_pointer + offset, 0, NULL, NULL);
opencl_assert(ciErr);
}
void mem_zero(device_memory& mem)
{
if(mem.device_pointer) {
memset((void*)mem.data_pointer, 0, mem.memory_size());
mem_copy_to(mem);
}
}
void mem_free(device_memory& mem)
{
if(mem.device_pointer) {
ciErr = clReleaseMemObject(CL_MEM_PTR(mem.device_pointer));
mem.device_pointer = 0;
opencl_assert(ciErr);
}
}
void const_copy_to(const char *name, void *host, size_t size)
{
if(const_mem_map.find(name) == const_mem_map.end()) {
device_vector<uchar> *data = new device_vector<uchar>();
data->copy((uchar*)host, size);
mem_alloc(*data, MEM_READ_ONLY);
const_mem_map[name] = data;
}
else {
device_vector<uchar> *data = const_mem_map[name];
data->copy((uchar*)host, size);
}
mem_copy_to(*const_mem_map[name]);
}
void tex_alloc(const char *name, device_memory& mem, bool interpolation, bool periodic)
{
mem_alloc(mem, MEM_READ_ONLY);
mem_copy_to(mem);
mem_map[name] = &mem;
}
void tex_free(device_memory& mem)
{
if(mem.data_pointer)
mem_free(mem);
}
size_t global_size_round_up(int group_size, int global_size)
{
int r = global_size % group_size;
return global_size + ((r == 0)? 0: group_size - r);
}
void path_trace(DeviceTask& task)
{
/* cast arguments to cl types */
cl_mem d_data = CL_MEM_PTR(const_mem_map["__data"]->device_pointer);
cl_mem d_buffer = CL_MEM_PTR(task.buffer);
cl_mem d_rng_state = CL_MEM_PTR(task.rng_state);
cl_int d_x = task.x;
cl_int d_y = task.y;
cl_int d_w = task.w;
cl_int d_h = task.h;
cl_int d_sample = task.sample;
/* sample arguments */
int narg = 0;
ciErr = 0;
ciErr |= clSetKernelArg(ckPathTraceKernel, narg++, sizeof(d_data), (void*)&d_data);
ciErr |= clSetKernelArg(ckPathTraceKernel, narg++, sizeof(d_buffer), (void*)&d_buffer);
ciErr |= clSetKernelArg(ckPathTraceKernel, narg++, sizeof(d_rng_state), (void*)&d_rng_state);
#define KERNEL_TEX(type, ttype, name) \
ciErr |= set_kernel_arg_mem(ckPathTraceKernel, &narg, #name);
#include "kernel_textures.h"
ciErr |= clSetKernelArg(ckPathTraceKernel, narg++, sizeof(d_sample), (void*)&d_sample);
ciErr |= clSetKernelArg(ckPathTraceKernel, narg++, sizeof(d_x), (void*)&d_x);
ciErr |= clSetKernelArg(ckPathTraceKernel, narg++, sizeof(d_y), (void*)&d_y);
ciErr |= clSetKernelArg(ckPathTraceKernel, narg++, sizeof(d_w), (void*)&d_w);
ciErr |= clSetKernelArg(ckPathTraceKernel, narg++, sizeof(d_h), (void*)&d_h);
opencl_assert(ciErr);
size_t workgroup_size;
clGetKernelWorkGroupInfo(ckPathTraceKernel, cdDevice,
CL_KERNEL_WORK_GROUP_SIZE, sizeof(size_t), &workgroup_size, NULL);
workgroup_size = max(sqrt((double)workgroup_size), 1.0);
size_t local_size[2] = {workgroup_size, workgroup_size};
size_t global_size[2] = {global_size_round_up(local_size[0], d_w), global_size_round_up(local_size[1], d_h)};
/* run kernel */
ciErr = clEnqueueNDRangeKernel(cqCommandQueue, ckPathTraceKernel, 2, NULL, global_size, local_size, 0, NULL, NULL);
opencl_assert(ciErr);
opencl_assert(clFinish(cqCommandQueue));
}
cl_int set_kernel_arg_mem(cl_kernel kernel, int *narg, const char *name)
{
cl_mem ptr;
cl_int size, err = 0;
if(mem_map.find(name) != mem_map.end()) {
device_memory *mem = mem_map[name];
ptr = CL_MEM_PTR(mem->device_pointer);
size = mem->data_width;
}
else {
/* work around NULL not working, even though the spec says otherwise */
ptr = CL_MEM_PTR(null_mem);
size = 1;
}
err |= clSetKernelArg(kernel, (*narg)++, sizeof(ptr), (void*)&ptr);
opencl_assert(err);
err |= clSetKernelArg(kernel, (*narg)++, sizeof(size), (void*)&size);
opencl_assert(err);
return err;
}
void tonemap(DeviceTask& task)
{
/* cast arguments to cl types */
cl_mem d_data = CL_MEM_PTR(const_mem_map["__data"]->device_pointer);
cl_mem d_rgba = CL_MEM_PTR(task.rgba);
cl_mem d_buffer = CL_MEM_PTR(task.buffer);
cl_int d_x = task.x;
cl_int d_y = task.y;
cl_int d_w = task.w;
cl_int d_h = task.h;
cl_int d_sample = task.sample;
cl_int d_resolution = task.resolution;
/* sample arguments */
int narg = 0;
ciErr = 0;
ciErr |= clSetKernelArg(ckFilmConvertKernel, narg++, sizeof(d_data), (void*)&d_data);
ciErr |= clSetKernelArg(ckFilmConvertKernel, narg++, sizeof(d_rgba), (void*)&d_rgba);
ciErr |= clSetKernelArg(ckFilmConvertKernel, narg++, sizeof(d_buffer), (void*)&d_buffer);
#define KERNEL_TEX(type, ttype, name) \
ciErr |= set_kernel_arg_mem(ckFilmConvertKernel, &narg, #name);
#include "kernel_textures.h"
ciErr |= clSetKernelArg(ckFilmConvertKernel, narg++, sizeof(d_sample), (void*)&d_sample);
ciErr |= clSetKernelArg(ckFilmConvertKernel, narg++, sizeof(d_resolution), (void*)&d_resolution);
ciErr |= clSetKernelArg(ckFilmConvertKernel, narg++, sizeof(d_x), (void*)&d_x);
ciErr |= clSetKernelArg(ckFilmConvertKernel, narg++, sizeof(d_y), (void*)&d_y);
ciErr |= clSetKernelArg(ckFilmConvertKernel, narg++, sizeof(d_w), (void*)&d_w);
ciErr |= clSetKernelArg(ckFilmConvertKernel, narg++, sizeof(d_h), (void*)&d_h);
opencl_assert(ciErr);
size_t workgroup_size;
clGetKernelWorkGroupInfo(ckFilmConvertKernel, cdDevice,
CL_KERNEL_WORK_GROUP_SIZE, sizeof(size_t), &workgroup_size, NULL);
workgroup_size = max(sqrt((double)workgroup_size), 1.0);
size_t local_size[2] = {workgroup_size, workgroup_size};
size_t global_size[2] = {global_size_round_up(local_size[0], d_w), global_size_round_up(local_size[1], d_h)};
/* run kernel */
ciErr = clEnqueueNDRangeKernel(cqCommandQueue, ckFilmConvertKernel, 2, NULL, global_size, local_size, 0, NULL, NULL);
opencl_assert(ciErr);
opencl_assert(clFinish(cqCommandQueue));
}
void task_add(DeviceTask& task)
{
if(task.type == DeviceTask::TONEMAP)
tonemap(task);
else if(task.type == DeviceTask::PATH_TRACE)
path_trace(task);
}
void task_wait()
{
}
void task_cancel()
{
}
};
Device *device_opencl_create(bool background)
{
return new OpenCLDevice(background);
}
CCL_NAMESPACE_END
#endif /* WITH_OPENCL */

@ -0,0 +1,2 @@
add_subdirectory(license)

@ -0,0 +1,203 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

@ -0,0 +1,17 @@
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.
Contributor(s): Alfredo de Greef, Blender Foundation

@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

@ -0,0 +1,13 @@
set(LICENSES
Apache_2.0.txt
Blender.txt
GPL.txt
ILM.txt
NVidia.txt
OSL.txt
Sobol.txt
readme.txt
)
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${LICENSES}" ${CYCLES_INSTALL_PATH}/license)

@ -0,0 +1,342 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

@ -0,0 +1,29 @@
Copyright (c) 2002, Industrial Light & Magic, a division of Lucas
Digital Ltd. LLC. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Industrial Light & Magic nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,36 @@
Copyright 2006, NVIDIA Corporation Ignacio Castano <icastano@nvidia.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Copyright 2009-2010 NVIDIA Corporation
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.

@ -0,0 +1,28 @@
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,29 @@
Copyright (c) 2008, Frances Y. Kuo and Stephen Joe
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of the copyright holders nor the names of the
University of New South Wales and the University of Waikato
and its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,12 @@
This program uses code from various sources. These are the licenses:
* New code is licensed under the GPL license v2 or later.
* BVH building and traversal code is licensed under Apache License v2.
* Approximate Catmull Clark subdivision code is licensed under the MIT license.
* Open Shading Language code on GPU is licensed under the Modified BSD license.
* Sobol direction vectors are licensed under the Modified BSD license.
* Matrix code adapted from OpenEXR under the Modified BSD license.
* Procedural texture functions from Blender are licensed under GPL v2 or later.
* Boost and OpenCL dynamic loading under Boost License.

@ -0,0 +1,117 @@
set(INC
.
../util
osl
svm
)
set(SRC
kernel.cpp
kernel.cl
kernel.cu
)
set(SRC_HEADERS
kernel.h
kernel_bvh.h
kernel_camera.h
kernel_compat_cpu.h
kernel_compat_cuda.h
kernel_compat_opencl.h
kernel_differential.h
kernel_displace.h
kernel_emission.h
kernel_film.h
kernel_globals.h
kernel_light.h
kernel_math.h
kernel_mbvh.h
kernel_montecarlo.h
kernel_object.h
kernel_path.h
kernel_qbvh.h
kernel_random.h
kernel_shader.h
kernel_textures.h
kernel_triangle.h
kernel_types.h
)
set(SRC_SVM_HEADERS
svm/bsdf.h
svm/bsdf_ashikhmin_velvet.h
svm/bsdf_diffuse.h
svm/bsdf_microfacet.h
svm/bsdf_reflection.h
svm/bsdf_refraction.h
svm/bsdf_transparent.h
svm/bsdf_ward.h
svm/bsdf_westin.h
svm/emissive.h
svm/svm.h
svm/svm_attribute.h
svm/svm_bsdf.h
svm/svm_closure.h
svm/svm_convert.h
svm/svm_displace.h
svm/svm_fresnel.h
svm/svm_geometry.h
svm/svm_gradient.h
svm/svm_image.h
svm/svm_light_path.h
svm/svm_magic.h
svm/svm_mapping.h
svm/svm_math.h
svm/svm_mix.h
svm/svm_musgrave.h
svm/svm_noise.h
svm/svm_noisetex.h
svm/svm_sky.h
svm/svm_tex_coord.h
svm/svm_texture.h
svm/svm_types.h
svm/svm_value.h
svm/svm_voronoi.h
svm/svm_wave.h
svm/volume.h
)
set(SRC_UTIL_HEADERS
../util/util_color.h
../util/util_math.h
../util/util_transform.h
../util/util_types.h
)
# OSL module
if(WITH_CYCLES_OSL)
add_subdirectory(osl)
endif()
# CPU module
include_directories(${INC})
add_library(cycles_kernel ${SRC} ${SRC_HEADERS} ${SRC_SVM_HEADERS})
if(WITH_CYCLES_CUDA)
add_dependencies(cycles_kernel cycles_kernel_cuda)
endif()
# OPENCL kernel
#set(KERNEL_PREPROCESSED ${CMAKE_CURRENT_BINARY_DIR}/kernel_preprocessed.cl)
#add_custom_command(
# OUTPUT ${KERNEL_PREPROCESSED}
# COMMAND gcc -x c++ -E ${CMAKE_CURRENT_SOURCE_DIR}/kernel.cl -I ${CMAKE_CURRENT_SOURCE_DIR}/../util/ -DCCL_NAMESPACE_BEGIN= -DCCL_NAMESPACE_END= -DWITH_OPENCL -o ${KERNEL_PREPROCESSED}
# DEPENDS ${SRC_KERNEL} ${SRC_UTIL_HEADERS})
#add_custom_target(cycles_kernel_preprocess ALL DEPENDS ${KERNEL_PREPROCESSED})
#delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${KERNEL_PREPROCESSED}" ${CYCLES_INSTALL_PATH}/kernel)
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "kernel.cl" ${CYCLES_INSTALL_PATH}/kernel)
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "kernel.cu" ${CYCLES_INSTALL_PATH}/kernel)
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_HEADERS}" ${CYCLES_INSTALL_PATH}/kernel)
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_SVM_HEADERS}" ${CYCLES_INSTALL_PATH}/kernel/svm)
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_UTIL_HEADERS}" ${CYCLES_INSTALL_PATH}/kernel)

@ -0,0 +1,94 @@
/*
* 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.
*/
/* OpenCL kernel entry points - unfinished */
#include "kernel_compat_opencl.h"
#include "kernel_math.h"
#include "kernel_types.h"
#include "kernel_globals.h"
#include "kernel_film.h"
#include "kernel_path.h"
//#include "kernel_displace.h"
__kernel void kernel_ocl_path_trace(
__constant KernelData *data,
__global float4 *buffer,
__global uint *rng_state,
#define KERNEL_TEX(type, ttype, name) \
__global type *name, \
int name##_width,
#include "kernel_textures.h"
int sample,
int sx, int sy, int sw, int sh)
{
KernelGlobals kglobals, *kg = &kglobals;
kg->data = data;
#define KERNEL_TEX(type, ttype, name) \
kg->name = name; \
kg->name##_width = name##_width;
#include "kernel_textures.h"
int x = sx + get_global_id(0);
int y = sy + get_global_id(1);
if(x < sx + sw && y < sy + sh)
kernel_path_trace(kg, buffer, rng_state, sample, x, y);
}
__kernel void kernel_ocl_tonemap(
__constant KernelData *data,
__global uchar4 *rgba,
__global float4 *buffer,
#define KERNEL_TEX(type, ttype, name) \
__global type *name, \
int name##_width,
#include "kernel_textures.h"
int sample, int resolution,
int sx, int sy, int sw, int sh)
{
KernelGlobals kglobals, *kg = &kglobals;
kg->data = data;
#define KERNEL_TEX(type, ttype, name) \
kg->name = name; \
kg->name##_width = name##_width;
#include "kernel_textures.h"
int x = sx + get_global_id(0);
int y = sy + get_global_id(1);
if(x < sx + sw && y < sy + sh)
kernel_film_tonemap(kg, rgba, buffer, sample, resolution, x, y);
}
/*__kernel void kernel_ocl_displace(__global uint4 *input, __global float3 *offset, int sx)
{
int x = sx + get_global_id(0);
kernel_displace(input, offset, x);
}*/

@ -0,0 +1,227 @@
/*
* 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.
*/
/* CPU kernel entry points */
#include "kernel.h"
#include "kernel_compat_cpu.h"
#include "kernel_math.h"
#include "kernel_types.h"
#include "kernel_globals.h"
#include "kernel_film.h"
#include "kernel_path.h"
#include "kernel_displace.h"
CCL_NAMESPACE_BEGIN
/* Globals */
KernelGlobals *kernel_globals_create()
{
KernelGlobals *kg = new KernelGlobals();
#ifdef WITH_OSL
kg->osl.use = false;
#endif
return kg;
}
void kernel_globals_free(KernelGlobals *kg)
{
delete kg;
}
/* OSL */
#ifdef WITH_OSL
void *kernel_osl_memory(KernelGlobals *kg)
{
return (void*)&kg->osl;
}
bool kernel_osl_use(KernelGlobals *kg)
{
return kg->osl.use;
}
#endif
/* Memory Copy */
void kernel_const_copy(KernelGlobals *kg, const char *name, void *host, size_t size)
{
if(strcmp(name, "__data") == 0)
memcpy(&kg->__data, host, size);
else
assert(0);
}
void kernel_tex_copy(KernelGlobals *kg, const char *name, device_ptr mem, size_t width, size_t height)
{
if(0) {
}
#define KERNEL_TEX(type, ttype, tname) \
else if(strcmp(name, #tname) == 0) { \
kg->tname.data = (type*)mem; \
kg->tname.width = width; \
}
#define KERNEL_IMAGE_TEX(type, ttype, tname)
#include "kernel_textures.h"
else if(strstr(name, "__tex_image")) {
texture_image_uchar4 *tex = NULL;
int id = atoi(name + strlen("__tex_image_"));
switch(id) {
case 0: tex = &kg->__tex_image_000; break;
case 1: tex = &kg->__tex_image_001; break;
case 2: tex = &kg->__tex_image_002; break;
case 3: tex = &kg->__tex_image_003; break;
case 4: tex = &kg->__tex_image_004; break;
case 5: tex = &kg->__tex_image_005; break;
case 6: tex = &kg->__tex_image_006; break;
case 7: tex = &kg->__tex_image_007; break;
case 8: tex = &kg->__tex_image_008; break;
case 9: tex = &kg->__tex_image_009; break;
case 10: tex = &kg->__tex_image_010; break;
case 11: tex = &kg->__tex_image_011; break;
case 12: tex = &kg->__tex_image_012; break;
case 13: tex = &kg->__tex_image_013; break;
case 14: tex = &kg->__tex_image_014; break;
case 15: tex = &kg->__tex_image_015; break;
case 16: tex = &kg->__tex_image_016; break;
case 17: tex = &kg->__tex_image_017; break;
case 18: tex = &kg->__tex_image_018; break;
case 19: tex = &kg->__tex_image_019; break;
case 20: tex = &kg->__tex_image_020; break;
case 21: tex = &kg->__tex_image_021; break;
case 22: tex = &kg->__tex_image_022; break;
case 23: tex = &kg->__tex_image_023; break;
case 24: tex = &kg->__tex_image_024; break;
case 25: tex = &kg->__tex_image_025; break;
case 26: tex = &kg->__tex_image_026; break;
case 27: tex = &kg->__tex_image_027; break;
case 28: tex = &kg->__tex_image_028; break;
case 29: tex = &kg->__tex_image_029; break;
case 30: tex = &kg->__tex_image_030; break;
case 31: tex = &kg->__tex_image_031; break;
case 32: tex = &kg->__tex_image_032; break;
case 33: tex = &kg->__tex_image_033; break;
case 34: tex = &kg->__tex_image_034; break;
case 35: tex = &kg->__tex_image_035; break;
case 36: tex = &kg->__tex_image_036; break;
case 37: tex = &kg->__tex_image_037; break;
case 38: tex = &kg->__tex_image_038; break;
case 39: tex = &kg->__tex_image_039; break;
case 40: tex = &kg->__tex_image_040; break;
case 41: tex = &kg->__tex_image_041; break;
case 42: tex = &kg->__tex_image_042; break;
case 43: tex = &kg->__tex_image_043; break;
case 44: tex = &kg->__tex_image_044; break;
case 45: tex = &kg->__tex_image_045; break;
case 46: tex = &kg->__tex_image_046; break;
case 47: tex = &kg->__tex_image_047; break;
case 48: tex = &kg->__tex_image_048; break;
case 49: tex = &kg->__tex_image_049; break;
case 50: tex = &kg->__tex_image_050; break;
case 51: tex = &kg->__tex_image_051; break;
case 52: tex = &kg->__tex_image_052; break;
case 53: tex = &kg->__tex_image_053; break;
case 54: tex = &kg->__tex_image_054; break;
case 55: tex = &kg->__tex_image_055; break;
case 56: tex = &kg->__tex_image_056; break;
case 57: tex = &kg->__tex_image_057; break;
case 58: tex = &kg->__tex_image_058; break;
case 59: tex = &kg->__tex_image_059; break;
case 60: tex = &kg->__tex_image_060; break;
case 61: tex = &kg->__tex_image_061; break;
case 62: tex = &kg->__tex_image_062; break;
case 63: tex = &kg->__tex_image_063; break;
case 64: tex = &kg->__tex_image_064; break;
case 65: tex = &kg->__tex_image_065; break;
case 66: tex = &kg->__tex_image_066; break;
case 67: tex = &kg->__tex_image_067; break;
case 68: tex = &kg->__tex_image_068; break;
case 69: tex = &kg->__tex_image_069; break;
case 70: tex = &kg->__tex_image_070; break;
case 71: tex = &kg->__tex_image_071; break;
case 72: tex = &kg->__tex_image_072; break;
case 73: tex = &kg->__tex_image_073; break;
case 74: tex = &kg->__tex_image_074; break;
case 75: tex = &kg->__tex_image_075; break;
case 76: tex = &kg->__tex_image_076; break;
case 77: tex = &kg->__tex_image_077; break;
case 78: tex = &kg->__tex_image_078; break;
case 79: tex = &kg->__tex_image_079; break;
case 80: tex = &kg->__tex_image_080; break;
case 81: tex = &kg->__tex_image_081; break;
case 82: tex = &kg->__tex_image_082; break;
case 83: tex = &kg->__tex_image_083; break;
case 84: tex = &kg->__tex_image_084; break;
case 85: tex = &kg->__tex_image_085; break;
case 86: tex = &kg->__tex_image_086; break;
case 87: tex = &kg->__tex_image_087; break;
case 88: tex = &kg->__tex_image_088; break;
case 89: tex = &kg->__tex_image_089; break;
case 90: tex = &kg->__tex_image_090; break;
case 91: tex = &kg->__tex_image_091; break;
case 92: tex = &kg->__tex_image_092; break;
case 93: tex = &kg->__tex_image_093; break;
case 94: tex = &kg->__tex_image_094; break;
case 95: tex = &kg->__tex_image_095; break;
case 96: tex = &kg->__tex_image_096; break;
case 97: tex = &kg->__tex_image_097; break;
case 98: tex = &kg->__tex_image_098; break;
case 99: tex = &kg->__tex_image_099; break;
default: break;
}
if(tex) {
tex->data = (uchar4*)mem;
tex->width = width;
tex->height = height;
}
}
else
assert(0);
}
/* Path Tracing */
void kernel_cpu_path_trace(KernelGlobals *kg, float4 *buffer, unsigned int *rng_state, int sample, int x, int y)
{
kernel_path_trace(kg, buffer, rng_state, sample, x, y);
}
/* Tonemapping */
void kernel_cpu_tonemap(KernelGlobals *kg, uchar4 *rgba, float4 *buffer, int sample, int resolution, int x, int y)
{
kernel_film_tonemap(kg, rgba, buffer, sample, resolution, x, y);
}
/* Displacement */
void kernel_cpu_displace(KernelGlobals *kg, uint4 *input, float3 *offset, int i)
{
kernel_displace(kg, input, offset, i);
}
CCL_NAMESPACE_END

@ -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.
*/
/* CUDA kernel entry points */
#include "kernel_compat_cuda.h"
#include "kernel_math.h"
#include "kernel_types.h"
#include "kernel_globals.h"
#include "kernel_film.h"
#include "kernel_path.h"
#include "kernel_displace.h"
extern "C" __global__ void kernel_cuda_path_trace(float4 *buffer, uint *rng_state, int sample, int sx, int sy, int sw, int sh)
{
int x = sx + blockDim.x*blockIdx.x + threadIdx.x;
int y = sy + blockDim.y*blockIdx.y + threadIdx.y;
if(x < sx + sw && y < sy + sh)
kernel_path_trace(NULL, buffer, rng_state, sample, x, y);
}
extern "C" __global__ void kernel_cuda_tonemap(uchar4 *rgba, float4 *buffer, int sample, int resolution, int sx, int sy, int sw, int sh)
{
int x = sx + blockDim.x*blockIdx.x + threadIdx.x;
int y = sy + blockDim.y*blockIdx.y + threadIdx.y;
if(x < sx + sw && y < sy + sh)
kernel_film_tonemap(NULL, rgba, buffer, sample, resolution, x, y);
}
extern "C" __global__ void kernel_cuda_displace(uint4 *input, float3 *offset, int sx)
{
int x = sx + blockDim.x*blockIdx.x + threadIdx.x;
kernel_displace(NULL, input, offset, x);
}

@ -0,0 +1,47 @@
/*
* 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 __KERNEL_H__
#define __KERNEL_H__
/* CPU Kernel Interfae */
#include "util_types.h"
CCL_NAMESPACE_BEGIN
struct KernelGlobals;
KernelGlobals *kernel_globals_create();
void kernel_globals_free(KernelGlobals *kg);
void *kernel_osl_memory(KernelGlobals *kg);
bool kernel_osl_use(KernelGlobals *kg);
void kernel_const_copy(KernelGlobals *kg, const char *name, void *host, size_t size);
void kernel_tex_copy(KernelGlobals *kg, const char *name, device_ptr mem, size_t width, size_t height);
void kernel_cpu_path_trace(KernelGlobals *kg, float4 *buffer, unsigned int *rng_state, int sample, int x, int y);
void kernel_cpu_tonemap(KernelGlobals *kg, uchar4 *rgba, float4 *buffer, int sample, int resolution, int x, int y);
void kernel_cpu_displace(KernelGlobals *kg, uint4 *input, float3 *offset, int i);
CCL_NAMESPACE_END
#endif /* __KERNEL_H__ */

@ -0,0 +1,375 @@
/*
* 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.
*/
CCL_NAMESPACE_BEGIN
/*
* "Persistent while-while kernel" used in:
*
* "Understanding the Efficiency of Ray Traversal on GPUs",
* Timo Aila and Samuli Laine,
* Proc. High-Performance Graphics 2009
*/
/* bottom-most stack entry, indicating the end of traversal */
#define ENTRYPOINT_SENTINEL 0x76543210
/* 64 object BVH + 64 mesh BVH + 64 object node splitting */
#define BVH_STACK_SIZE 192
#define BVH_NODE_SIZE 4
#define TRI_NODE_SIZE 3
__device_inline float3 bvh_inverse_direction(float3 dir)
{
/* avoid divide by zero (ooeps = exp2f(-80.0f)) */
float ooeps = 0.00000000000000000000000082718061255302767487140869206996285356581211090087890625f;
float3 idir;
idir.x = 1.0f/((fabsf(dir.x) > ooeps)? dir.x: copysignf(ooeps, dir.x));
idir.y = 1.0f/((fabsf(dir.y) > ooeps)? dir.y: copysignf(ooeps, dir.y));
idir.z = 1.0f/((fabsf(dir.z) > ooeps)? dir.z: copysignf(ooeps, dir.z));
return idir;
}
__device_inline void bvh_instance_push(KernelGlobals *kg, int object, const Ray *ray, float3 *P, float3 *idir, float *t, const float tmax)
{
Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
*P = transform(&tfm, ray->P);
float3 dir = transform_direction(&tfm, ray->D);
float len;
dir = normalize_len(dir, &len);
*idir = bvh_inverse_direction(dir);
if(*t != FLT_MAX)
*t *= len;
}
__device_inline void bvh_instance_pop(KernelGlobals *kg, int object, const Ray *ray, float3 *P, float3 *idir, float *t, const float tmax)
{
Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
if(*t != FLT_MAX)
*t *= len(transform_direction(&tfm, 1.0f/(*idir)));
*P = ray->P;
*idir = bvh_inverse_direction(ray->D);
}
/* intersect two bounding boxes */
__device_inline void bvh_node_intersect(KernelGlobals *kg,
bool *traverseChild0, bool *traverseChild1,
bool *closestChild1, int *nodeAddr0, int *nodeAddr1,
float3 P, float3 idir, float t, uint visibility, int nodeAddr)
{
/* fetch node data */
float4 n0xy = kernel_tex_fetch(__bvh_nodes, nodeAddr*BVH_NODE_SIZE+0);
float4 n1xy = kernel_tex_fetch(__bvh_nodes, nodeAddr*BVH_NODE_SIZE+1);
float4 nz = kernel_tex_fetch(__bvh_nodes, nodeAddr*BVH_NODE_SIZE+2);
float4 cnodes = kernel_tex_fetch(__bvh_nodes, nodeAddr*BVH_NODE_SIZE+3);
/* intersect ray against child nodes */
float3 ood = P * idir;
float c0lox = n0xy.x * idir.x - ood.x;
float c0hix = n0xy.y * idir.x - ood.x;
float c0loy = n0xy.z * idir.y - ood.y;
float c0hiy = n0xy.w * idir.y - ood.y;
float c0loz = nz.x * idir.z - ood.z;
float c0hiz = nz.y * idir.z - ood.z;
float c1loz = nz.z * idir.z - ood.z;
float c1hiz = nz.w * idir.z - ood.z;
float c0min_x = min(c0lox, c0hix);
float c0min_y = min(c0loy, c0hiy);
float c0min_z = min(c0loz, c0hiz);
float c0min = max4(c0min_x, c0min_y, c0min_z, 0.0f);
float c0max = min4(max(c0lox, c0hix), max(c0loy, c0hiy), max(c0loz, c0hiz), t);
float c1lox = n1xy.x * idir.x - ood.x;
float c1hix = n1xy.y * idir.x - ood.x;
float c1loy = n1xy.z * idir.y - ood.y;
float c1hiy = n1xy.w * idir.y - ood.y;
float c1min = max4(min(c1lox, c1hix), min(c1loy, c1hiy), min(c1loz, c1hiz), 0.0f);
float c1max = min4(max(c1lox, c1hix), max(c1loy, c1hiy), max(c1loz, c1hiz), t);
/* decide which nodes to traverse next */
#ifdef __VISIBILITY_FLAG__
/* this visibility test gives a 5% performance hit, how to solve? */
*traverseChild0 = (c0max >= c0min) && (__float_as_int(cnodes.z) & visibility);
*traverseChild1 = (c1max >= c1min) && (__float_as_int(cnodes.w) & visibility);
#else
*traverseChild0 = (c0max >= c0min);
*traverseChild1 = (c1max >= c1min);
#endif
*nodeAddr0 = __float_as_int(cnodes.x);
*nodeAddr1 = __float_as_int(cnodes.y);
*closestChild1 = (c1min < c0min);
}
/* Sven Woop's algorithm */
__device_inline void bvh_triangle_intersect(KernelGlobals *kg, Intersection *isect,
float3 P, float3 idir, uint visibility, int object, int triAddr)
{
/* compute and check intersection t-value */
float4 v00 = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+0);
float4 v11 = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+1);
float3 dir = 1.0f/idir;
float Oz = v00.w - P.x*v00.x - P.y*v00.y - P.z*v00.z;
float invDz = 1.0f/(dir.x*v00.x + dir.y*v00.y + dir.z*v00.z);
float t = Oz * invDz;
if(t > 0.0f && t < isect->t) {
/* compute and check barycentric u */
float Ox = v11.w + P.x*v11.x + P.y*v11.y + P.z*v11.z;
float Dx = dir.x*v11.x + dir.y*v11.y + dir.z*v11.z;
float u = Ox + t*Dx;
if(u >= 0.0f) {
/* compute and check barycentric v */
float4 v22 = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+2);
float Oy = v22.w + P.x*v22.x + P.y*v22.y + P.z*v22.z;
float Dy = dir.x*v22.x + dir.y*v22.y + dir.z*v22.z;
float v = Oy + t*Dy;
if(v >= 0.0f && u + v <= 1.0f) {
#ifdef __VISIBILITY_FLAG__
/* visibility flag test. we do it here under the assumption
that most triangles are culled by node flags */
if(kernel_tex_fetch(__prim_visibility, triAddr) & visibility)
#endif
{
/* record intersection */
isect->prim = triAddr;
isect->object = object;
isect->u = u;
isect->v = v;
isect->t = t;
}
}
}
}
}
__device_inline bool scene_intersect(KernelGlobals *kg, const Ray *ray, const uint visibility, Intersection *isect)
{
/* traversal stack in CUDA thread-local memory */
int traversalStack[BVH_STACK_SIZE];
traversalStack[0] = ENTRYPOINT_SENTINEL;
/* traversal variables in registers */
int stackPtr = 0;
int nodeAddr = kernel_data.bvh.root;
/* ray parameters in registers */
const float tmax = ray->t;
float3 P = ray->P;
float3 idir = bvh_inverse_direction(ray->D);
int object = ~0;
isect->t = tmax;
isect->object = ~0;
isect->prim = ~0;
isect->u = 0.0f;
isect->v = 0.0f;
/* traversal loop */
do {
do
{
/* traverse internal nodes */
while(nodeAddr >= 0 && nodeAddr != ENTRYPOINT_SENTINEL)
{
bool traverseChild0, traverseChild1, closestChild1;
int nodeAddrChild1;
bvh_node_intersect(kg, &traverseChild0, &traverseChild1,
&closestChild1, &nodeAddr, &nodeAddrChild1,
P, idir, isect->t, visibility, nodeAddr);
if(traverseChild0 != traverseChild1) {
/* one child was intersected */
if(traverseChild1) {
nodeAddr = nodeAddrChild1;
}
}
else {
if(!traverseChild0) {
/* neither child was intersected */
nodeAddr = traversalStack[stackPtr];
--stackPtr;
}
else {
/* both children were intersected, push the farther one */
if(closestChild1) {
int tmp = nodeAddr;
nodeAddr = nodeAddrChild1;
nodeAddrChild1 = tmp;
}
++stackPtr;
traversalStack[stackPtr] = nodeAddrChild1;
}
}
}
/* if node is leaf, fetch triangle list */
if(nodeAddr < 0) {
float4 leaf = kernel_tex_fetch(__bvh_nodes, (-nodeAddr-1)*BVH_NODE_SIZE+(BVH_NODE_SIZE-1));
int primAddr = __float_as_int(leaf.x);
#ifdef __INSTANCING__
if(primAddr >= 0) {
#endif
int primAddr2 = __float_as_int(leaf.y);
/* pop */
nodeAddr = traversalStack[stackPtr];
--stackPtr;
/* triangle intersection */
while(primAddr < primAddr2) {
/* intersect ray against triangle */
bvh_triangle_intersect(kg, isect, P, idir, visibility, object, primAddr);
/* shadow ray early termination */
if(visibility == PATH_RAY_SHADOW_OPAQUE && isect->prim != ~0)
return true;
primAddr++;
}
#ifdef __INSTANCING__
}
else {
/* instance push */
object = kernel_tex_fetch(__prim_object, -primAddr-1);
bvh_instance_push(kg, object, ray, &P, &idir, &isect->t, tmax);
++stackPtr;
traversalStack[stackPtr] = ENTRYPOINT_SENTINEL;
nodeAddr = kernel_tex_fetch(__object_node, object);
}
#endif
}
} while(nodeAddr != ENTRYPOINT_SENTINEL);
#ifdef __INSTANCING__
if(stackPtr >= 0) {
kernel_assert(object != ~0);
/* instance pop */
bvh_instance_pop(kg, object, ray, &P, &idir, &isect->t, tmax);
object = ~0;
nodeAddr = traversalStack[stackPtr];
--stackPtr;
}
#endif
} while(nodeAddr != ENTRYPOINT_SENTINEL);
return (isect->prim != ~0);
}
__device_inline float3 ray_offset(float3 P, float3 Ng)
{
#ifdef __INTERSECTION_REFINE__
const float epsilon_f = 1e-5f;
const int epsilon_i = 32;
float3 res;
/* x component */
if(fabsf(P.x) < epsilon_f) {
res.x = P.x + Ng.x*epsilon_f;
}
else {
uint ix = __float_as_uint(P.x);
ix += ((ix ^ __float_as_uint(Ng.x)) >> 31)? -epsilon_i: epsilon_i;
res.x = __uint_as_float(ix);
}
/* y component */
if(fabsf(P.y) < epsilon_f) {
res.y = P.y + Ng.y*epsilon_f;
}
else {
uint iy = __float_as_uint(P.y);
iy += ((iy ^ __float_as_uint(Ng.y)) >> 31)? -epsilon_i: epsilon_i;
res.y = __uint_as_float(iy);
}
/* z component */
if(fabsf(P.z) < epsilon_f) {
res.z = P.z + Ng.z*epsilon_f;
}
else {
uint iz = __float_as_uint(P.z);
iz += ((iz ^ __float_as_uint(Ng.z)) >> 31)? -epsilon_i: epsilon_i;
res.z = __uint_as_float(iz);
}
return res;
#else
const float epsilon_f = 1e-4f;
return P + epsilon_f*Ng;
#endif
}
__device_inline float3 bvh_triangle_refine(KernelGlobals *kg, const Intersection *isect, const Ray *ray)
{
float3 P = ray->P;
float3 D = ray->D;
float t = isect->t;
#ifdef __INTERSECTION_REFINE__
if(isect->object != ~0) {
Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_INVERSE_TRANSFORM);
P = transform(&tfm, P);
D = transform_direction(&tfm, D*t);
D = normalize_len(D, &t);
}
P = P + D*t;
float4 v00 = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+0);
float Oz = v00.w - P.x*v00.x - P.y*v00.y - P.z*v00.z;
float invDz = 1.0f/(D.x*v00.x + D.y*v00.y + D.z*v00.z);
float rt = Oz * invDz;
P = P + D*rt;
if(isect->object != ~0) {
Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_TRANSFORM);
P = transform(&tfm, P);
}
return P;
#else
return P + D*t;
#endif
}
CCL_NAMESPACE_END

@ -0,0 +1,144 @@
/*
* 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.
*/
CCL_NAMESPACE_BEGIN
/* Perspective Camera */
__device float2 camera_sample_aperture(KernelGlobals *kg, float u, float v)
{
float blades = kernel_data.cam.blades;
if(blades == 0.0f) {
/* sample disk */
return concentric_sample_disk(u, v);
}
else {
/* sample polygon */
float rotation = kernel_data.cam.bladesrotation;
return regular_polygon_sample(blades, rotation, u, v);
}
}
__device void camera_sample_perspective(KernelGlobals *kg, float raster_x, float raster_y, float lens_u, float lens_v, Ray *ray)
{
/* create ray form raster position */
Transform rastertocamera = kernel_data.cam.rastertocamera;
float3 Pcamera = transform(&rastertocamera, make_float3(raster_x, raster_y, 0.0f));
ray->P = make_float3(0.0f, 0.0f, 0.0f);
ray->D = Pcamera;
/* modify ray for depth of field */
float aperturesize = kernel_data.cam.aperturesize;
if(aperturesize > 0.0f) {
/* sample point on aperture */
float2 lensuv = camera_sample_aperture(kg, lens_u, lens_v)*aperturesize;
/* compute point on plane of focus */
float ft = kernel_data.cam.focaldistance/ray->D.z;
float3 Pfocus = ray->P + ray->D*ft;
/* update ray for effect of lens */
ray->P = make_float3(lensuv.x, lensuv.y, 0.0f);
ray->D = normalize(Pfocus - ray->P);
}
/* transform ray from camera to world */
Transform cameratoworld = kernel_data.cam.cameratoworld;
ray->P = transform(&cameratoworld, ray->P);
ray->D = transform_direction(&cameratoworld, ray->D);
ray->D = normalize(ray->D);
#ifdef __RAY_DIFFERENTIALS__
/* ray differential */
float3 Ddiff = transform_direction(&cameratoworld, Pcamera);
ray->dP.dx = make_float3(0.0f, 0.0f, 0.0f);
ray->dP.dy = make_float3(0.0f, 0.0f, 0.0f);
ray->dD.dx = normalize(Ddiff + kernel_data.cam.dx) - normalize(Ddiff);
ray->dD.dy = normalize(Ddiff + kernel_data.cam.dy) - normalize(Ddiff);
#endif
#ifdef __CAMERA_CLIPPING__
/* clipping */
ray->P += kernel_data.cam.nearclip*ray->D;
ray->t = kernel_data.cam.cliplength;
#else
ray->t = FLT_MAX;
#endif
}
/* Orthographic Camera */
__device void camera_sample_orthographic(KernelGlobals *kg, float raster_x, float raster_y, Ray *ray)
{
/* create ray form raster position */
Transform rastertocamera = kernel_data.cam.rastertocamera;
float3 Pcamera = transform(&rastertocamera, make_float3(raster_x, raster_y, 0.0f));
ray->P = Pcamera;
ray->D = make_float3(0.0f, 0.0f, 1.0f);
/* transform ray from camera to world */
Transform cameratoworld = kernel_data.cam.cameratoworld;
ray->P = transform(&cameratoworld, ray->P);
ray->D = transform_direction(&cameratoworld, ray->D);
ray->D = normalize(ray->D);
#ifdef __RAY_DIFFERENTIALS__
/* ray differential */
ray->dP.dx = kernel_data.cam.dx;
ray->dP.dy = kernel_data.cam.dy;
ray->dD.dx = make_float3(0.0f, 0.0f, 0.0f);
ray->dD.dy = make_float3(0.0f, 0.0f, 0.0f);
#endif
#ifdef __CAMERA_CLIPPING__
/* clipping */
ray->t = kernel_data.cam.cliplength;
#else
ray->t = FLT_MAX;
#endif
}
/* Common */
__device void camera_sample(KernelGlobals *kg, int x, int y, float filter_u, float filter_v, float lens_u, float lens_v, Ray *ray)
{
/* pixel filter */
float raster_x = x + kernel_tex_interp(__filter_table, filter_u);
float raster_y = y + kernel_tex_interp(__filter_table, filter_v);
/* motion blur */
//ray->time = lerp(time_t, kernel_data.cam.shutter_open, kernel_data.cam.shutter_close);
/* sample */
if(kernel_data.cam.ortho)
camera_sample_orthographic(kg, raster_x, raster_y, ray);
else
camera_sample_perspective(kg, raster_x, raster_y, lens_u, lens_v, ray);
}
CCL_NAMESPACE_END

@ -0,0 +1,162 @@
/*
* 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 __KERNEL_COMPAT_CPU_H__
#define __KERNEL_COMPAT_CPU_H__
#define __KERNEL_CPU__
#include "util_debug.h"
#include "util_math.h"
#include "util_types.h"
CCL_NAMESPACE_BEGIN
/* Assertions inside the kernel only work for the CPU device, so we wrap it in
a macro which is empty for other devices */
#define kernel_assert(cond) assert(cond)
/* Texture types to be compatible with CUDA textures. These are really just
simple arrays and after inlining fetch hopefully revert to being a simple
pointer lookup. */
template<typename T> struct texture {
T fetch(int index)
{
kernel_assert(index >= 0 && index < width);
return data[index];
}
__m128 fetch_m128(int index)
{
kernel_assert(index >= 0 && index < width);
return ((__m128*)data)[index];
}
__m128i fetch_m128i(int index)
{
kernel_assert(index >= 0 && index < width);
return ((__m128i*)data)[index];
}
float interp(float x)
{
x = clamp(x, 0.0f, 1.0f)*width;
int index = min((int)x, width-1);
int nindex = min(index+1, width-1);
float t = x - index;
return (1.0f - t)*data[index] + t*data[nindex];
}
T *data;
int width;
};
template<typename T> struct texture_image {
float4 read(float4 r)
{
return r;
}
float4 read(uchar4 r)
{
float f = 1.0f/255.0f;
return make_float4(r.x*f, r.y*f, r.z*f, r.w*f);
}
int wrap_periodic(int x, int width)
{
x %= width;
if(x < 0)
x += width;
return x;
}
int wrap_clamp(int x, int width)
{
return clamp(x, 0, width-1);
}
float frac(float x, int *ix)
{
int i = (int)x - ((x < 0.0f)? 1: 0);
*ix = i;
return x - (float)i;
}
float4 interp(float x, float y, bool periodic = true)
{
if(!data)
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
int ix, iy, nix, niy;
float tx = frac(x*width, &ix);
float ty = frac(y*height, &iy);
if(periodic) {
ix = wrap_periodic(ix, width);
iy = wrap_periodic(iy, height);
nix = wrap_periodic(ix+1, width);
niy = wrap_periodic(iy+1, height);
}
else {
ix = wrap_clamp(ix, width);
iy = wrap_clamp(iy, height);
nix = wrap_clamp(ix+1, width);
niy = wrap_clamp(iy+1, height);
}
float4 r = (1.0f - ty)*(1.0f - tx)*read(data[ix + iy*width]);
r += (1.0f - ty)*tx*read(data[nix + iy*width]);
r += ty*(1.0f - tx)*read(data[ix + niy*width]);
r += ty*tx*read(data[nix + niy*width]);
return r;
}
T *data;
int width, height;
};
typedef texture<float4> texture_float4;
typedef texture<float> texture_float;
typedef texture<uint> texture_uint;
typedef texture<int> texture_int;
typedef texture<uint4> texture_uint4;
typedef texture_image<float4> texture_image_float4;
typedef texture_image<uchar4> texture_image_uchar4;
/* Macros to handle different memory storage on different devices */
#define kernel_tex_fetch(tex, index) (kg->tex.fetch(index))
#define kernel_tex_fetch_m128(tex, index) (kg->tex.fetch_m128(index))
#define kernel_tex_fetch_m128i(tex, index) (kg->tex.fetch_m128i(index))
#define kernel_tex_interp(tex, t) (kg->tex.interp(t))
#define kernel_tex_image_interp(tex, x, y) (kg->tex.interp(x, y))
#define kernel_data (kg->__data)
CCL_NAMESPACE_END
#endif /* __KERNEL_COMPAT_CPU_H__ */

@ -0,0 +1,64 @@
/*
* 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 __KERNEL_COMPAT_CUDA_H__
#define __KERNEL_COMPAT_CUDA_H__
#define __KERNEL_GPU__
#define __KERNEL_CUDA__
#define CCL_NAMESPACE_BEGIN
#define CCL_NAMESPACE_END
#include <cuda.h>
#include <float.h>
#include "util_types.h"
/* Qualifier wrappers for different names on different devices */
#define __device __device__ __inline__
#define __device_inline __device__ __inline__
#define __device_noinline __device__ __noinline__
#define __global
#define __shared __shared__
#define __constant
/* No assert supported for CUDA */
#define kernel_assert(cond)
/* Textures */
typedef texture<float4, 1> texture_float4;
typedef texture<float, 1> texture_float;
typedef texture<uint, 1> texture_uint;
typedef texture<int, 1> texture_int;
typedef texture<uint4, 1> texture_uint4;
typedef texture<float4, 2> texture_image_float4;
typedef texture<uchar4, 2, cudaReadModeNormalizedFloat> texture_image_uchar4;
/* Macros to handle different memory storage on different devices */
#define kernel_tex_fetch(t, index) tex1Dfetch(t, index)
#define kernel_tex_interp(t, x) tex1D(t, x)
#define kernel_tex_image_interp(t, x, y) tex2D(t, x, y)
#define kernel_data __data
#endif /* __KERNEL_COMPAT_CUDA_H__ */

@ -0,0 +1,112 @@
/*
* 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 __KERNEL_COMPAT_OPENCL_H__
#define __KERNEL_COMPAT_OPENCL_H__
#define __KERNEL_GPU__
#define __KERNEL_OPENCL__
/* no namespaces in opencl */
#define CCL_NAMESPACE_BEGIN
#define CCL_NAMESPACE_END
#define WITH_OPENCL
/* in opencl all functions are device functions, so leave this empty */
#define __device
#define __device_inline
#define __device_noinline
/* no assert in opencl */
#define kernel_assert(cond)
/* manual implementation of interpolated 1D lookup */
__device float kernel_tex_interp_(__global float *data, int width, float x)
{
x = clamp(x, 0.0f, 1.0f)*width;
int index = min((int)x, width-1);
int nindex = min(index+1, width-1);
float t = x - index;
return (1.0f - t)*data[index] + t*data[nindex];
}
/* make_type definitions with opencl style element initializers */
#ifdef make_float2
#undef make_float2
#endif
#ifdef make_float3
#undef make_float3
#endif
#ifdef make_float4
#undef make_float4
#endif
#ifdef make_int2
#undef make_int2
#endif
#ifdef make_int3
#undef make_int3
#endif
#ifdef make_int4
#undef make_int4
#endif
#define make_float2(x, y) ((float2)(x, y))
#define make_float3(x, y, z) ((float3)(x, y, z))
#define make_float4(x, y, z, w) ((float4)(x, y, z, w))
#define make_int2(x, y) ((int2)(x, y))
#define make_int3(x, y, z) ((int3)(x, y, z))
#define make_int4(x, y, z, w) ((int4)(x, y, z, w))
/* math functions */
#define __uint_as_float(x) as_float(x)
#define __float_as_uint(x) as_uint(x)
#define __int_as_float(x) as_float(x)
#define __float_as_int(x) as_int(x)
#define sqrtf(x) sqrt(((float)x))
#define cosf(x) cos(((float)x))
#define sinf(x) sin(((float)x))
#define powf(x, y) pow(((float)x), ((float)y))
#define fabsf(x) fabs(((float)x))
#define copysignf(x, y) copysign(((float)x), ((float)y))
#define cosf(x) cos(((float)x))
#define asinf(x) asin(((float)x))
#define acosf(x) acos(((float)x))
#define atanf(x) atan(((float)x))
#define tanf(x) tan(((float)x))
#define logf(x) log(((float)x))
#define floorf(x) floor(((float)x))
#define expf(x) exp(((float)x))
#define hypotf(x, y) hypot(((float)x), ((float)y))
#define atan2f(x, y) atan2(((float)x), ((float)y))
#define fmaxf(x, y) fmax(((float)x), ((float)y))
#define fminf(x, y) fmin(((float)x), ((float)y))
/* data lookup defines */
#define kernel_data (*kg->data)
#define kernel_tex_interp(t, x) kernel_tex_interp_(kg->t, kg->t##_width, x)
#define kernel_tex_fetch(t, index) kg->t[index]
/* define NULL */
#define NULL 0
#include "util_types.h"
#endif /* __KERNEL_COMPAT_OPENCL_H__ */

@ -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.
*/
CCL_NAMESPACE_BEGIN
/* See "Tracing Ray Differentials", Homan Igehy, 1999. */
__device void differential_transfer(differential3 *dP_, const differential3 dP, float3 D, const differential3 dD, float3 Ng, float t)
{
/* ray differential transfer through homogenous medium, to
* compute dPdx/dy at a shading point from the incoming ray */
float3 tmp = D/dot(D, Ng);
float3 tmpx = dP.dx + t*dD.dx;
float3 tmpy = dP.dy + t*dD.dy;
dP_->dx = tmpx - dot(tmpx, Ng)*tmp;
dP_->dy = tmpy - dot(tmpy, Ng)*tmp;
}
__device void differential_incoming(differential3 *dI, const differential3 dD)
{
/* compute dIdx/dy at a shading point, we just need to negate the
* differential of the ray direction */
dI->dx = -dD.dx;
dI->dy = -dD.dy;
}
__device void differential_dudv(differential *du, differential *dv, float3 dPdu, float3 dPdv, differential3 dP, float3 Ng)
{
/* now we have dPdx/dy from the ray differential transfer, and dPdu/dv
* from the primitive, we can compute dudx/dy and dvdx/dy. these are
* mainly used for differentials of arbitrary mesh attributes. */
/* find most stable axis to project to 2D */
float xn= fabsf(Ng.x);
float yn= fabsf(Ng.y);
float zn= fabsf(Ng.z);
if(zn < xn || zn < yn) {
if(yn < xn || yn < zn) {
dPdu.x = dPdu.y;
dPdv.x = dPdv.y;
dP.dx.x = dP.dx.y;
dP.dy.x = dP.dy.y;
}
dPdu.y = dPdu.z;
dPdv.y = dPdv.z;
dP.dx.y = dP.dx.z;
dP.dy.y = dP.dy.z;
}
/* using Cramer's rule, we solve for dudx and dvdx in a 2x2 linear system,
* and the same for dudy and dvdy. the denominator is the same for both
* solutions, so we compute it only once.
*
* dP.dx = dPdu * dudx + dPdv * dvdx;
* dP.dy = dPdu * dudy + dPdv * dvdy; */
float det = (dPdu.x*dPdv.y - dPdv.x*dPdu.y);
if(det != 0.0f)
det = 1.0f/det;
du->dx = (dP.dx.x*dPdv.y - dP.dx.y*dPdv.x)*det;
dv->dx = (dP.dx.y*dPdu.x - dP.dx.x*dPdu.y)*det;
du->dy = (dP.dy.x*dPdv.y - dP.dy.y*dPdv.x)*det;
dv->dy = (dP.dy.y*dPdu.x - dP.dy.x*dPdu.y)*det;
}
CCL_NAMESPACE_END

@ -0,0 +1,35 @@
/*
* 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.
*/
CCL_NAMESPACE_BEGIN
__device void kernel_displace(KernelGlobals *kg, uint4 *input, float3 *offset, int i)
{
/* setup shader data */
ShaderData sd;
uint4 in = input[i];
shader_setup_from_displace(kg, &sd, in.x, in.y, __int_as_float(in.z), __int_as_float(in.w));
/* evaluate */
float3 P = sd.P;
shader_eval_displacement(kg, &sd);
offset[i] = sd.P - P;
}
CCL_NAMESPACE_END

@ -0,0 +1,139 @@
/*
* 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.
*/
CCL_NAMESPACE_BEGIN
/* Direction Emission */
__device float3 direct_emissive_eval(KernelGlobals *kg, float rando,
LightSample *ls, float u, float v, float3 I)
{
/* setup shading at emitter */
ShaderData sd;
shader_setup_from_sample(kg, &sd, ls->P, ls->Ng, I, ls->shader, ls->object, ls->prim, u, v);
ls->Ng = sd.Ng;
/* no path flag, we're evaluating this for all closures. that's weak but
we'd have to do multiple evaluations otherwise */
shader_eval_surface(kg, &sd, rando, 0);
float3 eval;
/* evaluate emissive closure */
if(sd.flag & SD_EMISSION)
eval = shader_emissive_eval(kg, &sd);
else
eval = make_float3(0.0f, 0.0f, 0.0f);
shader_release(kg, &sd);
return eval;
}
__device bool direct_emission(KernelGlobals *kg, ShaderData *sd, int lindex,
float randt, float rando, float randu, float randv, Ray *ray, float3 *eval)
{
LightSample ls;
#ifdef __MULTI_LIGHT__
if(lindex != -1) {
/* sample position on a specified light */
light_select(kg, lindex, randu, randv, sd->P, &ls);
}
else
#endif
{
/* sample a light and position on int */
light_sample(kg, randt, randu, randv, sd->P, &ls);
}
/* compute pdf */
float pdf = light_sample_pdf(kg, &ls, -ls.D, ls.t);
/* evaluate closure */
*eval = direct_emissive_eval(kg, rando, &ls, randu, randv, -ls.D);
if(is_zero(*eval) || pdf == 0.0f)
return false;
/* todo: use visbility flag to skip lights */
/* evaluate BSDF at shading point */
float bsdf_pdf;
float3 bsdf_eval = shader_bsdf_eval(kg, sd, ls.D, &bsdf_pdf);
*eval *= bsdf_eval/pdf;
if(is_zero(*eval))
return false;
if(ls.prim != ~0) {
/* multiple importance sampling */
float mis_weight = power_heuristic(pdf, bsdf_pdf);
*eval *= mis_weight;
}
/* todo: clean up these weights */
else if(ls.shader & SHADER_AREA_LIGHT)
*eval *= 0.25f; /* area lamp */
else if(ls.t != FLT_MAX)
*eval *= 0.25f*M_1_PI_F; /* point lamp */
if(ls.shader & SHADER_CAST_SHADOW) {
/* setup ray */
ray->P = ray_offset(sd->P, sd->Ng);
if(ls.t == FLT_MAX) {
/* distant light */
ray->D = ls.D;
ray->t = ls.t;
}
else {
/* other lights, avoid self-intersection */
ray->D = ray_offset(ls.P, ls.Ng) - ray->P;
ray->D = normalize_len(ray->D, &ray->t);
}
}
else {
/* signal to not cast shadow ray */
ray->t = 0.0f;
}
return true;
}
/* Indirect Emission */
__device float3 indirect_emission(KernelGlobals *kg, ShaderData *sd, float t, int path_flag, float bsdf_pdf)
{
/* evaluate emissive closure */
float3 L = shader_emissive_eval(kg, sd);
if(!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_SAMPLE_AS_LIGHT)) {
/* multiple importance sampling */
float pdf = triangle_light_pdf(kg, sd->Ng, sd->I, t);
float mis_weight = power_heuristic(bsdf_pdf, pdf);
return L*mis_weight;
}
return L;
}
CCL_NAMESPACE_END

@ -0,0 +1,64 @@
/*
* 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.
*/
CCL_NAMESPACE_BEGIN
__device float4 film_map(KernelGlobals *kg, float4 irradiance, int sample)
{
float scale = 1.0f/(float)(sample+1);
float exposure = kernel_data.film.exposure;
float4 result = irradiance*scale;
/* conversion to srgb */
result.x = color_scene_linear_to_srgb(result.x*exposure);
result.y = color_scene_linear_to_srgb(result.y*exposure);
result.z = color_scene_linear_to_srgb(result.z*exposure);
/* clamp since alpha might be > 1.0 due to russian roulette */
result.w = clamp(result.w, 0.0f, 1.0f);
return result;
}
__device uchar4 film_float_to_byte(float4 color)
{
uchar4 result;
/* simple float to byte conversion */
result.x = (uchar)clamp(color.x*255.0f, 0.0f, 255.0f);
result.y = (uchar)clamp(color.y*255.0f, 0.0f, 255.0f);
result.z = (uchar)clamp(color.z*255.0f, 0.0f, 255.0f);
result.w = (uchar)clamp(color.w*255.0f, 0.0f, 255.0f);
return result;
}
__device void kernel_film_tonemap(KernelGlobals *kg, __global uchar4 *rgba, __global float4 *buffer, int sample, int resolution, int x, int y)
{
int w = kernel_data.cam.width;
int index = x + y*w;
float4 irradiance = buffer[index];
float4 float_result = film_map(kg, irradiance, sample);
uchar4 byte_result = film_float_to_byte(float_result);
rgba[index] = byte_result;
}
CCL_NAMESPACE_END

@ -0,0 +1,88 @@
/*
* 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.
*/
/* Constant Globals */
#ifdef __KERNEL_CPU__
#ifdef __OSL__
#include "osl_globals.h"
#endif
#endif
CCL_NAMESPACE_BEGIN
/* On the CPU, we pass along the struct KernelGlobals to nearly everywhere in
the kernel, to access constant data. These are all stored as "textures", but
these are really just standard arrays. We can't use actually globals because
multiple renders may be running inside the same process. */
#ifdef __KERNEL_CPU__
typedef struct KernelGlobals {
#define KERNEL_TEX(type, ttype, name) ttype name;
#define KERNEL_IMAGE_TEX(type, ttype, name) ttype name;
#include "kernel_textures.h"
KernelData __data;
#ifdef __OSL__
/* On the CPU, we also have the OSL globals here. Most data structures are shared
with SVM, the difference is in the shaders and object/mesh attributes. */
OSLGlobals osl;
#endif
} KernelGLobals;
#endif
/* For CUDA, constant memory textures must be globals, so we can't put them
into a struct. As a result we don't actually use this struct and use actual
globals and simply pass along a NULL pointer everywhere, which we hope gets
optimized out. */
#ifdef __KERNEL_CUDA__
__constant__ KernelData __data;
typedef struct KernelGlobals {} KernelGlobals;
#define KERNEL_TEX(type, ttype, name) ttype name;
#define KERNEL_IMAGE_TEX(type, ttype, name) ttype name;
#include "kernel_textures.h"
#endif
/* OpenCL */
#ifdef __KERNEL_OPENCL__
typedef struct KernelGlobals {
__constant KernelData *data;
#define KERNEL_TEX(type, ttype, name) \
__global type *name; \
int name##_width;
#include "kernel_textures.h"
} KernelGlobals;
#endif
CCL_NAMESPACE_END

@ -0,0 +1,241 @@
/*
* 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.
*/
CCL_NAMESPACE_BEGIN
typedef struct LightSample {
float3 P;
float3 D;
float3 Ng;
float t;
int object;
int prim;
int shader;
} LightSample;
/* Regular Light */
__device float3 disk_light_sample(float3 v, float randu, float randv)
{
float3 ru, rv;
make_orthonormals(v, &ru, &rv);
to_unit_disk(&randu, &randv);
return ru*randu + rv*randv;
}
__device float3 distant_light_sample(float3 D, float size, float randu, float randv)
{
return normalize(D + disk_light_sample(D, randu, randv)*size);
}
__device float3 sphere_light_sample(float3 P, float3 center, float size, float randu, float randv)
{
return disk_light_sample(normalize(P - center), randu, randv)*size;
}
__device float3 area_light_sample(float3 axisu, float3 axisv, float randu, float randv)
{
randu = randu - 0.5f;
randv = randv - 0.5f;
return axisu*randu + axisv*randv;
}
__device void regular_light_sample(KernelGlobals *kg, int point,
float randu, float randv, float3 P, LightSample *ls)
{
float4 data0 = kernel_tex_fetch(__light_data, point*LIGHT_SIZE + 0);
float4 data1 = kernel_tex_fetch(__light_data, point*LIGHT_SIZE + 1);
LightType type = (LightType)__float_as_int(data0.x);
if(type == LIGHT_DISTANT) {
/* distant light */
float3 D = make_float3(data0.y, data0.z, data0.w);
float size = data1.y;
if(size > 0.0f)
D = distant_light_sample(D, size, randu, randv);
ls->P = D;
ls->Ng = D;
ls->D = -D;
ls->t = FLT_MAX;
}
else {
ls->P = make_float3(data0.y, data0.z, data0.w);
if(type == LIGHT_POINT) {
float size = data1.y;
/* sphere light */
if(size > 0.0f)
ls->P += sphere_light_sample(P, ls->P, size, randu, randv);
ls->Ng = normalize(P - ls->P);
}
else {
/* area light */
float4 data2 = kernel_tex_fetch(__light_data, point*LIGHT_SIZE + 2);
float4 data3 = kernel_tex_fetch(__light_data, point*LIGHT_SIZE + 3);
float3 axisu = make_float3(data1.y, data1.z, data2.w);
float3 axisv = make_float3(data2.y, data2.z, data2.w);
float3 D = make_float3(data3.y, data3.z, data3.w);
ls->P += area_light_sample(axisu, axisv, randu, randv);
ls->Ng = D;
}
ls->t = 0.0f;
}
ls->shader = __float_as_int(data1.x);
ls->object = ~0;
ls->prim = ~0;
}
__device float regular_light_pdf(KernelGlobals *kg,
const float3 Ng, const float3 I, float t)
{
float pdf = kernel_data.integrator.pdf_lights;
if(t == FLT_MAX)
return pdf;
float cos_pi = dot(Ng, I);
if(cos_pi <= 0.0f)
return 0.0f;
return t*t*pdf/cos_pi;
}
/* Triangle Light */
__device void triangle_light_sample(KernelGlobals *kg, int prim, int object,
float randu, float randv, LightSample *ls)
{
/* triangle, so get position, normal, shader */
ls->P = triangle_sample_MT(kg, prim, randu, randv);
ls->Ng = triangle_normal_MT(kg, prim, &ls->shader);
ls->object = object;
ls->prim = prim;
ls->t = 0.0f;
#ifdef __INSTANCING__
/* instance transform */
if(ls->object >= 0) {
object_position_transform(kg, ls->object, &ls->P);
object_normal_transform(kg, ls->object, &ls->Ng);
}
#endif
}
__device float triangle_light_pdf(KernelGlobals *kg,
const float3 Ng, const float3 I, float t)
{
float cos_pi = fabsf(dot(Ng, I));
if(cos_pi == 0.0f)
return 0.0f;
return (t*t*kernel_data.integrator.pdf_triangles)/cos_pi;
}
/* Light Distribution */
__device int light_distribution_sample(KernelGlobals *kg, float randt)
{
/* this is basically std::upper_bound as used by pbrt, to find a point light or
triangle to emit from, proportional to area. a good improvement would be to
also sample proportional to power, though it's not so well defined with
OSL shaders. */
int first = 0;
int len = kernel_data.integrator.num_distribution + 1;
while(len > 0) {
int half_len = len >> 1;
int middle = first + half_len;
if(randt < kernel_tex_fetch(__light_distribution, middle).x) {
len = half_len;
}
else {
first = middle + 1;
len = len - half_len - 1;
}
}
first = max(0, first-1);
kernel_assert(first >= 0 && first < kernel_data.integrator.num_distribution);
return first;
}
/* Generic Light */
__device void light_sample(KernelGlobals *kg, float randt, float randu, float randv, float3 P, LightSample *ls)
{
/* sample index */
int index = light_distribution_sample(kg, randt);
/* fetch light data */
float4 l = kernel_tex_fetch(__light_distribution, index);
int prim = __float_as_int(l.y);
if(prim >= 0) {
int object = __float_as_int(l.w);
triangle_light_sample(kg, prim, object, randu, randv, ls);
}
else {
int point = -prim-1;
regular_light_sample(kg, point, randu, randv, P, ls);
}
/* compute incoming direction and distance */
if(ls->t != FLT_MAX)
ls->D = normalize_len(ls->P - P, &ls->t);
}
__device float light_sample_pdf(KernelGlobals *kg, LightSample *ls, float3 I, float t)
{
float pdf;
if(ls->prim != ~0)
pdf = triangle_light_pdf(kg, ls->Ng, I, t);
else
pdf = regular_light_pdf(kg, ls->Ng, I, t);
return pdf;
}
__device void light_select(KernelGlobals *kg, int index, float randu, float randv, float3 P, LightSample *ls)
{
regular_light_sample(kg, index, randu, randv, P, ls);
}
__device float light_select_pdf(KernelGlobals *kg, LightSample *ls, float3 I, float t)
{
return regular_light_pdf(kg, ls->Ng, I, t);
}
CCL_NAMESPACE_END

@ -0,0 +1,27 @@
/*
* 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 __KERNEL_MATH_H__
#define __KERNEL_MATH_H__
#include "util_color.h"
#include "util_math.h"
#include "util_transform.h"
#endif /* __KERNEL_MATH_H__ */

@ -0,0 +1,394 @@
/*
* 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.
*/
CCL_NAMESPACE_BEGIN
#define MBVH_OBJECT_SENTINEL 0x76543210
#define MBVH_NODE_SIZE 8
#define MBVH_STACK_SIZE 1024
#define MBVH_RAY_STACK_SIZE 10000
typedef struct MBVHTask {
int node;
int index;
int num;
int object;
} MBVHTask;
typedef struct MVBHRay {
float3 P;
float u;
float3 idir;
float v;
float t;
int index;
int object;
float3 origP;
float3 origD;
float tmax;
} MBVHRay;
__device float3 mbvh_inverse_direction(float3 dir)
{
// Avoid divide by zero (ooeps = exp2f(-80.0f))
float ooeps = 0.00000000000000000000000082718061255302767487140869206996285356581211090087890625f;
float3 idir;
idir.x = 1.0f / (fabsf(dir.x) > ooeps ? dir.x : copysignf(ooeps, dir.x));
idir.y = 1.0f / (fabsf(dir.y) > ooeps ? dir.y : copysignf(ooeps, dir.y));
idir.z = 1.0f / (fabsf(dir.z) > ooeps ? dir.z : copysignf(ooeps, dir.z));
return idir;
}
__device void mbvh_instance_push(KernelGlobals *kg, int object, MBVHRay *ray)
{
Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
ray->P = transform(&tfm, ray->origP);
float3 dir = ray->origD;
if(ray->t != ray->tmax) dir *= ray->t;
dir = transform_direction(&tfm, dir);
ray->idir = mbvh_inverse_direction(normalize(dir));
if(ray->t != ray->tmax) ray->t = len(dir);
}
__device void mbvh_instance_pop(KernelGlobals *kg, int object, MBVHRay *ray)
{
Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
if(ray->t != ray->tmax)
ray->t = len(transform_direction(&tfm, (1.0f/(ray->idir)) * (ray->t)));
ray->P = ray->origP;
ray->idir = mbvh_inverse_direction(ray->origD);
}
/* Sven Woop's algorithm */
__device void mbvh_triangle_intersect(KernelGlobals *kg, MBVHRay *ray, int object, int triAddr)
{
float3 P = ray->P;
float3 idir = ray->idir;
/* compute and check intersection t-value */
float4 v00 = kernel_tex_fetch(__tri_woop, triAddr*MBVH_NODE_SIZE+0);
float4 v11 = kernel_tex_fetch(__tri_woop, triAddr*MBVH_NODE_SIZE+1);
float3 dir = 1.0f/idir;
float Oz = v00.w - P.x*v00.x - P.y*v00.y - P.z*v00.z;
float invDz = 1.0f/(dir.x*v00.x + dir.y*v00.y + dir.z*v00.z);
float t = Oz * invDz;
if(t > 0.0f && t < ray->t) {
/* compute and check barycentric u */
float Ox = v11.w + P.x*v11.x + P.y*v11.y + P.z*v11.z;
float Dx = dir.x*v11.x + dir.y*v11.y + dir.z*v11.z;
float u = Ox + t*Dx;
if(u >= 0.0f) {
/* compute and check barycentric v */
float4 v22 = kernel_tex_fetch(__tri_woop, triAddr*MBVH_NODE_SIZE+2);
float Oy = v22.w + P.x*v22.x + P.y*v22.y + P.z*v22.z;
float Dy = dir.x*v22.x + dir.y*v22.y + dir.z*v22.z;
float v = Oy + t*Dy;
if(v >= 0.0f && u + v <= 1.0f) {
/* record intersection */
ray->index = triAddr;
ray->object = object;
ray->u = u;
ray->v = v;
ray->t = t;
}
}
}
}
__device void mbvh_node_intersect(KernelGlobals *kg, __m128 *traverseChild,
__m128 *tHit, float3 P, float3 idir, float t, int nodeAddr)
{
/* X axis */
const __m128 bminx = kernel_tex_fetch_m128(__bvh_nodes, nodeAddr*MBVH_NODE_SIZE+0);
const __m128 t0x = _mm_mul_ps(_mm_sub_ps(bminx, _mm_set_ps1(P.x)), _mm_set_ps1(idir.x));
const __m128 bmaxx = kernel_tex_fetch_m128(__bvh_nodes, nodeAddr*MBVH_NODE_SIZE+1);
const __m128 t1x = _mm_mul_ps(_mm_sub_ps(bmaxx, _mm_set_ps1(P.x)), _mm_set_ps1(idir.x));
__m128 tmin = _mm_max_ps(_mm_min_ps(t0x, t1x), _mm_setzero_ps());
__m128 tmax = _mm_min_ps(_mm_max_ps(t0x, t1x), _mm_set_ps1(t));
/* Y axis */
const __m128 bminy = kernel_tex_fetch_m128(__bvh_nodes, nodeAddr*MBVH_NODE_SIZE+2);
const __m128 t0y = _mm_mul_ps(_mm_sub_ps(bminy, _mm_set_ps1(P.y)), _mm_set_ps1(idir.y));
const __m128 bmaxy = kernel_tex_fetch_m128(__bvh_nodes, nodeAddr*MBVH_NODE_SIZE+3);
const __m128 t1y = _mm_mul_ps(_mm_sub_ps(bmaxy, _mm_set_ps1(P.y)), _mm_set_ps1(idir.y));
tmin = _mm_max_ps(_mm_min_ps(t0y, t1y), tmin);
tmax = _mm_min_ps(_mm_max_ps(t0y, t1y), tmax);
/* Z axis */
const __m128 bminz = kernel_tex_fetch_m128(__bvh_nodes, nodeAddr*MBVH_NODE_SIZE+4);
const __m128 t0z = _mm_mul_ps(_mm_sub_ps(bminz, _mm_set_ps1(P.z)), _mm_set_ps1(idir.z));
const __m128 bmaxz = kernel_tex_fetch_m128(__bvh_nodes, nodeAddr*MBVH_NODE_SIZE+5);
const __m128 t1z = _mm_mul_ps(_mm_sub_ps(bmaxz, _mm_set_ps1(P.z)), _mm_set_ps1(idir.z));
tmin = _mm_max_ps(_mm_min_ps(t0z, t1z), tmin);
tmax = _mm_min_ps(_mm_max_ps(t0z, t1z), tmax);
/* compare and get mask */
*traverseChild = _mm_cmple_ps(tmin, tmax);
/* get distance XXX probably wrong */
*tHit = tmin;
}
static void mbvh_sort_by_length(int id[4], float len[4])
{
for(int i = 1; i < 4; i++) {
int j = i - 1;
while(j >= 0 && len[j] > len[j+1]) {
swap(len[j], len[j+1]);
swap(id[j], id[j+1]);
j--;
}
}
}
__device void scene_intersect(KernelGlobals *kg, MBVHRay *rays, int numrays)
{
/* traversal stacks */
MBVHTask task_stack[MBVH_STACK_SIZE];
int active_ray_stacks[4][MBVH_RAY_STACK_SIZE];
int num_task, num_active[4] = {0, 0, 0, 0};
__m128i one_mm = _mm_set1_epi32(1);
/* push root node task on stack */
task_stack[0].node = kernel_data.bvh.root;
task_stack[0].index = 0;
task_stack[0].num = numrays;
task_stack[0].object = ~0;
num_task = 1;
/* push all rays in first SIMD lane */
for(int i = 0; i < numrays; i++)
active_ray_stacks[0][i] = i;
num_active[0] = numrays;
while(num_task >= 1) {
/* pop task */
MBVHTask task = task_stack[--num_task];
if(task.node == MBVH_OBJECT_SENTINEL) {
/* instance pop */
/* pop rays from stack */
num_active[task.index] -= task.num;
int ray_offset = num_active[task.index];
/* transform rays */
for(int i = 0; i < task.num; i++) {
MBVHRay *ray = &rays[active_ray_stacks[task.index][ray_offset + i]];
mbvh_instance_pop(kg, task.object, ray);
}
}
else if(task.node >= 0) {
/* inner node? */
/* pop rays from stack*/
num_active[task.index] -= task.num;
int ray_offset = num_active[task.index];
/* initialze simd values */
__m128i num_active_mm = _mm_load_si128((__m128i*)num_active);
__m128 len_mm = _mm_set_ps1(0.0f);
for(int i = 0; i < task.num; i++) {
int rayid = active_ray_stacks[task.index][ray_offset + i];
MVBHRay *ray = rays + rayid;
/* intersect 4 QBVH node children */
__m128 result;
__m128 thit;
mbvh_node_intersect(kg, &result, &thit, ray->P, ray->idir, ray->t, task.node);
/* update length for sorting */
len_mm = _mm_add_ps(len_mm, _mm_and_ps(thit, result));
/* push rays on stack */
for(int j = 0; j < 4; j++)
active_ray_stacks[j][num_active[j]] = rayid;
/* update num active */
__m128i resulti = _mm_and_si128(*((__m128i*)&result), one_mm);
num_active_mm = _mm_add_epi32(resulti, num_active_mm);
_mm_store_si128((__m128i*)num_active, num_active_mm);
}
if(num_active[0] || num_active[1] || num_active[2] || num_active[3]) {
/* load child node addresses */
float4 cnodes = kernel_tex_fetch(__bvh_nodes, task.node);
int child[4] = {
__float_as_int(cnodes.x),
__float_as_int(cnodes.y),
__float_as_int(cnodes.z),
__float_as_int(cnodes.w)};
/* sort nodes by average intersection distance */
int ids[4] = {0, 1, 2, 3};
float len[4];
_mm_store_ps(len, len_mm);
mbvh_sort_by_length(ids, len);
/* push new tasks on stack */
for(int j = 0; j < 4; j++) {
if(num_active[j]) {
int id = ids[j];
task_stack[num_task].node = child[id];
task_stack[num_task].index = id;
task_stack[num_task].num = num_active[id];
task_stack[num_task].object = task.object;
num_task++;
}
}
}
}
else {
/* fetch leaf node data */
float4 leaf = kernel_tex_fetch(__bvh_nodes, (-task.node-1)*MBVH_NODE_SIZE+(MBVH_NODE_SIZE-2));
int triAddr = __float_as_int(leaf.x);
int triAddr2 = __float_as_int(leaf.y);
/* pop rays from stack*/
num_active[task.index] -= task.num;
int ray_offset = num_active[task.index];
/* triangles */
if(triAddr >= 0) {
int i, numq = (task.num >> 2) << 2;
/* SIMD ray leaf intersection */
for(i = 0; i < numq; i += 4) {
MBVHRay *ray4[4] = {
&rays[active_ray_stacks[task.index][ray_offset + i + 0]],
&rays[active_ray_stacks[task.index][ray_offset + i + 1]],
&rays[active_ray_stacks[task.index][ray_offset + i + 2]],
&rays[active_ray_stacks[task.index][ray_offset + i + 3]]};
/* load SoA */
while(triAddr < triAddr2) {
mbvh_triangle_intersect(ray4[0], task.object, task.node);
mbvh_triangle_intersect(ray4[1], task.object, task.node);
mbvh_triangle_intersect(ray4[2], task.object, task.node);
mbvh_triangle_intersect(ray4[3], task.object, task.node);
triAddr++;
/* some shadow ray optim could be done by setting t=0 */
}
/* store AoS */
}
/* mono ray leaf intersection */
for(; i < task.num; i++) {
MBVHRay *ray = &rays[active_ray_stacks[task.index][ray_offset + i]];
while(triAddr < triAddr2) {
mbvh_triangle_intersect(kg, ray, task.object, task.node);
triAddr++;
}
}
}
else {
/* instance push */
int object = -triAddr-1;
int node = triAddr;
/* push instance pop task */
task_stack[num_task].node = MBVH_OBJECT_SENTINEL;
task_stack[num_task].index = task.index;
task_stack[num_task].num = task.num;
task_stack[num_task].object = object;
num_task++;
num_active[task.index] += task.num;
/* push node task */
task_stack[num_task].node = node;
task_stack[num_task].index = task.index;
task_stack[num_task].num = task.num;
task_stack[num_task].object = object;
num_task++;
for(int i = 0; i < task.num; i++) {
int rayid = active_ray_stacks[task.index][ray_offset + i];
/* push on stack for last task */
active_ray_stacks[task.index][num_active[task.index]] = rayid;
num_active[task.index]++;
/* transform ray */
MBVHRay *ray = &rays[rayid];
mbvh_instance_push(kg, object, ray);
}
}
}
}
}
__device void mbvh_set_ray(MBVHRay *rays, int i, Ray *ray, float tmax)
{
MBVHRay *mray = &rays[i];
/* ray parameters in registers */
mray->P = ray->P;
mray->idir = mbvh_inverse_direction(ray->D);
mray->t = tmax;
}
__device bool mbvh_get_intersection(MVBHRay *rays, int i, Intersection *isect, float tmax)
{
MBVHRay *mray = &rays[i];
if(mray->t == tmax)
return false;
isect->t = mray->t;
isect->u = mray->u;
isect->v = mray->v;
isect->index = mray->index;
isect->object = mray->object;
return true;
}
__device bool mbvh_get_shadow(MBVHRay *rays, int i, float tmax)
{
return (rays[i].t == tmax);
}
CCL_NAMESPACE_END

@ -0,0 +1,209 @@
/*
* Parts adapted from Open Shading Language with this license:
*
* Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
* All Rights Reserved.
*
* Modifications Copyright 2011, Blender Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Sony Pictures Imageworks nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __KERNEL_MONTECARLO_CL__
#define __KERNEL_MONTECARLO_CL__
CCL_NAMESPACE_BEGIN
/// Given values x and y on [0,1], convert them in place to values on
/// [-1,1] uniformly distributed over a unit sphere. This code is
/// derived from Peter Shirley, "Realistic Ray Tracing", p. 103.
__device void to_unit_disk(float *x, float *y)
{
float r, phi;
float a = 2.0f * (*x) - 1.0f;
float b = 2.0f * (*y) - 1.0f;
if(a > -b) {
if(a > b) {
r = a;
phi = M_PI_4_F *(b/a);
} else {
r = b;
phi = M_PI_4_F *(2.0f - a/b);
}
} else {
if(a < b) {
r = -a;
phi = M_PI_4_F *(4.0f + b/a);
} else {
r = -b;
if(b != 0.0f)
phi = M_PI_4_F *(6.0f - a/b);
else
phi = 0.0f;
}
}
*x = r * cosf(phi);
*y = r * sinf(phi);
}
__device void make_orthonormals_tangent(const float3 N, const float3 T, float3 *a, float3 *b)
{
*b = cross(N, T);
*a = cross(*b, N);
}
__device_inline void sample_cos_hemisphere(const float3 N,
float randu, float randv, float3 *omega_in, float *pdf)
{
// Default closure BSDF implementation: uniformly sample
// cosine-weighted hemisphere above the point.
to_unit_disk(&randu, &randv);
float costheta = sqrtf(max(1.0f - randu * randu - randv * randv, 0.0f));
float3 T, B;
make_orthonormals(N, &T, &B);
*omega_in = randu * T + randv * B + costheta * N;
*pdf = costheta *M_1_PI_F;
}
__device_inline void sample_uniform_hemisphere(const float3 N,
float randu, float randv,
float3 *omega_in, float *pdf)
{
float z = randu;
float r = sqrtf(max(0.f, 1.f - z*z));
float phi = 2.f * M_PI_F * randv;
float x = r * cosf(phi);
float y = r * sinf(phi);
float3 T, B;
make_orthonormals (N, &T, &B);
*omega_in = x * T + y * B + z * N;
*pdf = 0.5f * M_1_PI_F;
}
__device float3 sample_uniform_sphere(float u1, float u2)
{
float z = 1.0f - 2.0f*u1;
float r = sqrtf(fmaxf(0.0f, 1.0f - z*z));
float phi = 2.0f*M_PI_F*u2;
float x = r*cosf(phi);
float y = r*sinf(phi);
return make_float3(x, y, z);
}
__device float power_heuristic(float a, float b)
{
return (a*a)/(a*a + b*b);
}
__device float2 concentric_sample_disk(float u1, float u2)
{
float r, theta;
// Map uniform random numbers to $[-1,1]^2$
float sx = 2 * u1 - 1;
float sy = 2 * u2 - 1;
// Map square to $(r,\theta)$
// Handle degeneracy at the origin
if(sx == 0.0f && sy == 0.0f) {
return make_float2(0.0f, 0.0f);
}
if(sx >= -sy) {
if(sx > sy) {
// Handle first region of disk
r = sx;
if(sy > 0.0f) theta = sy/r;
else theta = 8.0f + sy/r;
}
else {
// Handle second region of disk
r = sy;
theta = 2.0f - sx/r;
}
}
else {
if(sx <= sy) {
// Handle third region of disk
r = -sx;
theta = 4.0f - sy/r;
}
else {
// Handle fourth region of disk
r = -sy;
theta = 6.0f + sx/r;
}
}
theta *= M_PI_4_F;
return make_float2(r * cosf(theta), r * sinf(theta));
}
__device float2 regular_polygon_sample(float corners, float rotation, float u, float v)
{
/* sample corner number and reuse u */
float corner = floorf(u*corners);
u = u*corners - corner;
/* uniform sampled triangle weights */
u = sqrtf(u);
v = v*u;
u = 1.0f - u;
/* point in triangle */
float angle = M_PI_F/corners;
float2 p = make_float2((u + v)*cosf(angle), (u - v)*sinf(angle));
/* rotate */
rotation += corner*2.0f*angle;
float cr = cosf(rotation);
float sr = sinf(rotation);
return make_float2(cr*p.x - sr*p.y, sr*p.x + cr*p.y);
}
/* Spherical coordinates <-> Cartesion direction */
__device float2 direction_to_spherical(float3 dir)
{
float theta = acosf(dir.z);
float phi = atan2f(dir.x, dir.y);
return make_float2(theta, phi);
}
__device float3 spherical_to_direction(float theta, float phi)
{
return make_float3(
sinf(theta)*cosf(phi),
sinf(theta)*sinf(phi),
cosf(theta));
}
CCL_NAMESPACE_END
#endif /* __KERNEL_MONTECARLO_CL__ */

@ -0,0 +1,68 @@
/*
* 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.
*/
CCL_NAMESPACE_BEGIN
enum ObjectTransform {
OBJECT_TRANSFORM = 0,
OBJECT_INVERSE_TRANSFORM = 4,
OBJECT_NORMAL_TRANSFORM = 8,
OBJECT_PROPERTIES = 12
};
__device_inline Transform object_fetch_transform(KernelGlobals *kg, int object, enum ObjectTransform type)
{
Transform tfm;
int offset = object*OBJECT_SIZE + (int)type;
tfm.x = kernel_tex_fetch(__objects, offset + 0);
tfm.y = kernel_tex_fetch(__objects, offset + 1);
tfm.z = kernel_tex_fetch(__objects, offset + 2);
tfm.w = kernel_tex_fetch(__objects, offset + 3);
return tfm;
}
__device_inline void object_position_transform(KernelGlobals *kg, int object, float3 *P)
{
Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
*P = transform(&tfm, *P);
}
__device_inline void object_normal_transform(KernelGlobals *kg, int object, float3 *N)
{
Transform tfm = object_fetch_transform(kg, object, OBJECT_NORMAL_TRANSFORM);
*N = normalize(transform_direction(&tfm, *N));
}
__device_inline void object_dir_transform(KernelGlobals *kg, int object, float3 *D)
{
Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
*D = transform_direction(&tfm, *D);
}
__device_inline float object_surface_area(KernelGlobals *kg, int object)
{
int offset = object*OBJECT_SIZE + OBJECT_PROPERTIES;
float4 f = kernel_tex_fetch(__objects, offset);
return f.x;
}
CCL_NAMESPACE_END

@ -0,0 +1,421 @@
/*
* 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 "kernel_differential.h"
#include "kernel_montecarlo.h"
#include "kernel_triangle.h"
#include "kernel_object.h"
#ifdef __QBVH__
#include "kernel_qbvh.h"
#else
#include "kernel_bvh.h"
#endif
#include "kernel_camera.h"
#include "kernel_shader.h"
#include "kernel_light.h"
#include "kernel_emission.h"
#include "kernel_random.h"
CCL_NAMESPACE_BEGIN
#ifdef __MODIFY_TP__
__device float3 path_terminate_modified_throughput(KernelGlobals *kg, __global float3 *buffer, int x, int y, int sample)
{
/* modify throughput to influence path termination probability, to avoid
darker regions receiving fewer samples than lighter regions. also RGB
are weighted differently. proper validation still remains to be done. */
const float3 weights = make_float3(1.0f, 1.33f, 0.66f);
const float3 one = make_float3(1.0f, 1.0f, 1.0f);
const int minsample = 5;
const float minL = 0.1f;
if(sample >= minsample) {
float3 L = buffer[x + y*kernel_data.cam.width];
float3 Lmin = make_float3(minL, minL, minL);
float correct = (float)(sample+1)/(float)sample;
L = film_map(L*correct, sample);
return weights/clamp(L, Lmin, one);
}
return weights;
}
#endif
typedef struct PathState {
uint flag;
int bounce;
int diffuse_bounce;
int glossy_bounce;
int transmission_bounce;
int transparent_bounce;
} PathState;
__device_inline void path_state_init(PathState *state)
{
state->flag = PATH_RAY_CAMERA|PATH_RAY_SINGULAR|PATH_RAY_MIS_SKIP;
state->bounce = 0;
state->diffuse_bounce = 0;
state->glossy_bounce = 0;
state->transmission_bounce = 0;
state->transparent_bounce = 0;
}
__device_inline void path_state_next(KernelGlobals *kg, PathState *state, int label)
{
/* ray through transparent keeps same flags from previous ray and is
not counted as a regular bounce, transparent has separate max */
if(label & LABEL_TRANSPARENT) {
state->flag |= PATH_RAY_TRANSPARENT;
state->transparent_bounce++;
if(!kernel_data.integrator.transparent_shadows)
state->flag |= PATH_RAY_MIS_SKIP;
return;
}
state->bounce++;
/* reflection/transmission */
if(label & LABEL_REFLECT) {
state->flag |= PATH_RAY_REFLECT;
state->flag &= ~(PATH_RAY_TRANSMIT|PATH_RAY_CAMERA|PATH_RAY_TRANSPARENT);
if(label & LABEL_DIFFUSE)
state->diffuse_bounce++;
else
state->glossy_bounce++;
}
else {
kernel_assert(label & LABEL_TRANSMIT);
state->flag |= PATH_RAY_TRANSMIT;
state->flag &= ~(PATH_RAY_REFLECT|PATH_RAY_CAMERA|PATH_RAY_TRANSPARENT);
state->transmission_bounce++;
}
/* diffuse/glossy/singular */
if(label & LABEL_DIFFUSE) {
state->flag |= PATH_RAY_DIFFUSE;
state->flag &= ~(PATH_RAY_GLOSSY|PATH_RAY_SINGULAR|PATH_RAY_MIS_SKIP);
}
else if(label & LABEL_GLOSSY) {
state->flag |= PATH_RAY_GLOSSY;
state->flag &= ~(PATH_RAY_DIFFUSE|PATH_RAY_SINGULAR|PATH_RAY_MIS_SKIP);
}
else {
kernel_assert(label & LABEL_SINGULAR);
state->flag |= PATH_RAY_GLOSSY|PATH_RAY_SINGULAR|PATH_RAY_MIS_SKIP;
state->flag &= ~PATH_RAY_DIFFUSE;
}
}
__device_inline uint path_state_ray_visibility(PathState *state)
{
uint flag = state->flag;
/* for visibility, diffuse/glossy are for reflection only */
if(flag & PATH_RAY_TRANSMIT)
flag &= ~(PATH_RAY_DIFFUSE|PATH_RAY_GLOSSY);
return flag;
}
__device_inline float path_state_terminate_probability(KernelGlobals *kg, PathState *state, const float3 throughput)
{
if(state->flag & PATH_RAY_TRANSPARENT) {
/* transparent rays treated separately */
if(state->transparent_bounce >= kernel_data.integrator.transparent_max_bounce)
return 0.0f;
else if(state->transparent_bounce <= kernel_data.integrator.transparent_min_bounce)
return 1.0f;
}
else {
/* other rays */
if((state->bounce >= kernel_data.integrator.max_bounce) ||
(state->diffuse_bounce >= kernel_data.integrator.max_diffuse_bounce) ||
(state->glossy_bounce >= kernel_data.integrator.max_glossy_bounce) ||
(state->transmission_bounce >= kernel_data.integrator.max_transmission_bounce))
return 0.0f;
else if(state->bounce <= kernel_data.integrator.min_bounce)
return 1.0f;
}
/* probalistic termination */
return average(throughput);
}
__device_inline bool shadow_blocked(KernelGlobals *kg, PathState *state, Ray *ray, Intersection *isect, float3 *light_L)
{
if(ray->t == 0.0f)
return false;
bool result = scene_intersect(kg, ray, PATH_RAY_SHADOW_OPAQUE, isect);
#ifdef __TRANSPARENT_SHADOWS__
if(result && kernel_data.integrator.transparent_shadows) {
/* transparent shadows work in such a way to try to minimize overhead
in cases where we don't need them. after a regular shadow ray is
cast we check if the hit primitive was potentially transparent, and
only in that case start marching. this gives on extra ray cast for
the cases were we do want transparency.
also note that for this to work correct, multi close sampling must
be used, since we don't pass a random number to shader_eval_surface */
if(shader_transparent_shadow(kg, isect)) {
float3 throughput = make_float3(1.0f, 1.0f, 1.0f);
float3 Pend = ray->P + ray->D*ray->t;
int bounce = state->transparent_bounce;
for(;;) {
if(bounce >= kernel_data.integrator.transparent_max_bounce) {
return true;
}
else if(bounce >= kernel_data.integrator.transparent_min_bounce) {
/* todo: get random number somewhere for probabilistic terminate */
#if 0
float probability = average(throughput);
float terminate = 0.0f;
if(terminate >= probability)
return true;
throughput /= probability;
#endif
}
if(!scene_intersect(kg, ray, PATH_RAY_SHADOW_TRANSPARENT, isect)) {
*light_L *= throughput;
return false;
}
if(!shader_transparent_shadow(kg, isect))
return true;
ShaderData sd;
shader_setup_from_ray(kg, &sd, isect, ray);
shader_eval_surface(kg, &sd, 0.0f, PATH_RAY_SHADOW);
throughput *= shader_bsdf_transparency(kg, &sd);
ray->P = ray_offset(sd.P, -sd.Ng);
if(ray->t != FLT_MAX)
ray->D = normalize_len(Pend - ray->P, &ray->t);
bounce++;
}
}
}
#endif
return result;
}
__device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample, Ray ray, float3 throughput)
{
/* initialize */
float3 L = make_float3(0.0f, 0.0f, 0.0f);
float Ltransparent = 0.0f;
#ifdef __EMISSION__
float ray_pdf = 0.0f;
#endif
PathState state;
int rng_offset = PRNG_BASE_NUM;
path_state_init(&state);
/* path iteration */
for(;; rng_offset += PRNG_BOUNCE_NUM) {
/* intersect scene */
Intersection isect;
uint visibility = path_state_ray_visibility(&state);
if(!scene_intersect(kg, &ray, visibility, &isect)) {
/* eval background shader if nothing hit */
if(kernel_data.background.transparent && (state.flag & PATH_RAY_CAMERA)) {
Ltransparent += average(throughput);
}
else {
#ifdef __BACKGROUND__
ShaderData sd;
shader_setup_from_background(kg, &sd, &ray);
L += throughput*shader_eval_background(kg, &sd, state.flag);
shader_release(kg, &sd);
#else
L += throughput*make_float3(0.8f, 0.8f, 0.8f);
#endif
}
break;
}
/* setup shading */
ShaderData sd;
shader_setup_from_ray(kg, &sd, &isect, &ray);
float rbsdf = path_rng(kg, rng, sample, rng_offset + PRNG_BSDF);
shader_eval_surface(kg, &sd, rbsdf, state.flag);
#ifdef __HOLDOUT__
if((sd.flag & SD_HOLDOUT) && (state.flag & PATH_RAY_CAMERA)) {
float3 holdout_weight = shader_holdout_eval(kg, &sd);
if(kernel_data.background.transparent)
Ltransparent += average(holdout_weight*throughput);
}
#endif
#ifdef __EMISSION__
/* emission */
if(sd.flag & SD_EMISSION)
L += throughput*indirect_emission(kg, &sd, isect.t, state.flag, ray_pdf);
#endif
/* path termination. this is a strange place to put the termination, it's
mainly due to the mixed in MIS that we use. gives too many unneeded
shader evaluations, only need emission if we are going to terminate */
float probability = path_state_terminate_probability(kg, &state, throughput);
float terminate = path_rng(kg, rng, sample, rng_offset + PRNG_TERMINATE);
if(terminate >= probability)
break;
throughput /= probability;
#ifdef __EMISSION__
if(kernel_data.integrator.use_direct_light) {
/* sample illumination from lights to find path contribution */
if(sd.flag & SD_BSDF_HAS_EVAL) {
float light_t = path_rng(kg, rng, sample, rng_offset + PRNG_LIGHT);
float light_o = path_rng(kg, rng, sample, rng_offset + PRNG_LIGHT_F);
float light_u = path_rng(kg, rng, sample, rng_offset + PRNG_LIGHT_U);
float light_v = path_rng(kg, rng, sample, rng_offset + PRNG_LIGHT_V);
Ray light_ray;
float3 light_L;
#ifdef __MULTI_LIGHT__
/* index -1 means randomly sample from distribution */
int i = (kernel_data.integrator.num_distribution)? -1: 0;
for(; i < kernel_data.integrator.num_all_lights; i++) {
#else
const int i = -1;
#endif
if(direct_emission(kg, &sd, i, light_t, light_o, light_u, light_v, &light_ray, &light_L)) {
/* trace shadow ray */
if(!shadow_blocked(kg, &state, &light_ray, &isect, &light_L))
L += throughput*light_L;
}
#ifdef __MULTI_LIGHT__
}
#endif
}
}
#endif
/* no BSDF? we can stop here */
if(!(sd.flag & SD_BSDF))
break;
/* sample BSDF */
float bsdf_pdf;
float3 bsdf_eval;
float3 bsdf_omega_in;
differential3 bsdf_domega_in;
float bsdf_u = path_rng(kg, rng, sample, rng_offset + PRNG_BSDF_U);
float bsdf_v = path_rng(kg, rng, sample, rng_offset + PRNG_BSDF_V);
int label;
label = shader_bsdf_sample(kg, &sd, bsdf_u, bsdf_v, &bsdf_eval,
&bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
shader_release(kg, &sd);
if(bsdf_pdf == 0.0f || is_zero(bsdf_eval))
break;
/* modify throughput */
throughput *= bsdf_eval/bsdf_pdf;
/* set labels */
#ifdef __EMISSION__
ray_pdf = bsdf_pdf;
#endif
/* update path state */
path_state_next(kg, &state, label);
/* setup ray */
ray.P = ray_offset(sd.P, (label & LABEL_TRANSMIT)? -sd.Ng: sd.Ng);
ray.D = bsdf_omega_in;
ray.t = FLT_MAX;
#ifdef __RAY_DIFFERENTIALS__
ray.dP = sd.dP;
ray.dD = bsdf_domega_in;
#endif
}
return make_float4(L.x, L.y, L.z, 1.0f - Ltransparent);
}
__device void kernel_path_trace(KernelGlobals *kg, __global float4 *buffer, __global uint *rng_state, int sample, int x, int y)
{
/* initialize random numbers */
RNG rng;
float filter_u;
float filter_v;
path_rng_init(kg, rng_state, sample, &rng, x, y, &filter_u, &filter_v);
/* sample camera ray */
Ray ray;
float lens_u = path_rng(kg, &rng, sample, PRNG_LENS_U);
float lens_v = path_rng(kg, &rng, sample, PRNG_LENS_V);
camera_sample(kg, x, y, filter_u, filter_v, lens_u, lens_v, &ray);
/* integrate */
#ifdef __MODIFY_TP__
float3 throughput = path_terminate_modified_throughput(kg, buffer, x, y, sample);
float4 L = kernel_path_integrate(kg, &rng, sample, ray, throughput)/throughput;
#else
float3 throughput = make_float3(1.0f, 1.0f, 1.0f);
float4 L = kernel_path_integrate(kg, &rng, sample, ray, throughput);
#endif
/* accumulate result in output buffer */
int index = x + y*kernel_data.cam.width;
if(sample == 0)
buffer[index] = L;
else
buffer[index] += L;
path_rng_end(kg, rng_state, rng, x, y);
}
CCL_NAMESPACE_END

Some files were not shown because too many files have changed in this diff Show More