Compare commits

..

19 Commits

Author SHA1 Message Date
f77be09171 Merge branch 'master' into temp-ui-cpp 2022-11-26 00:22:49 -06:00
1a3681b83c Cleanup: Move interface_intern.hh
The entire interface directory is now compiled as C++ files!
2022-11-26 00:21:17 -06:00
3c2073d844 Cleanup: Move interface eyedroppers directory to C++ 2022-11-26 00:01:49 -06:00
a1ea6d6496 Cleanup: Move interface_handlers.c to C++ 2022-11-25 23:48:33 -06:00
1836acbeac Cleanup: Move six more interface files to C++ 2022-11-25 23:48:18 -06:00
ae3f443220 Merge branch 'master' into temp-ui-cpp 2022-04-03 12:07:43 -05:00
b9d1b07a45 Cleanup: Move interface.c to C++
Similar to 4537eb0c3b
2022-04-03 12:07:38 -05:00
dd1be8db19 Cleanup: Move interface View2D files to C++ 2022-04-02 21:32:25 -05:00
6450f380ac Cleanup: Move more interface files to C++ 2022-04-02 15:07:36 -05:00
1051c17af3 Cleanup: Move interface_query.c to C++ 2022-04-02 14:07:43 -05:00
87628abaa1 Merge branch 'master' into temp-ui-cpp 2022-04-02 13:54:52 -05:00
462177fd62 Merge branch 'master' into temp-ui-cpp 2021-12-01 22:37:05 -05:00
5d221a2a8a Merge branch 'temp-ui-cpp' of git.blender.org:blender into temp-ui-cpp 2021-11-26 08:08:01 -05:00
fb4b7aaa8f Attempt to resolve build error 2021-11-26 08:07:50 -05:00
6183f63250 Merge branch 'master' into temp-ui-cpp 2021-11-21 11:39:51 -05:00
08228956d9 Cleanup: Move outliner_tools.c to C++ 2021-11-20 17:00:41 -05:00
956be86c09 Merge branch 'master' into temp-ui-cpp 2021-11-20 16:31:11 -05:00
e2f9602be2 Remove designated initializer 2021-11-20 15:59:05 -05:00
24ae9c52d8 Cleanup: Move search menu template to C++
This will allow using improved data structures, and
make other refactors to the search button API cleaner.
2021-11-19 23:51:48 -05:00
331 changed files with 5653 additions and 10284 deletions

View File

@@ -53,18 +53,7 @@ This package provides Blender as a Python module for use in studio pipelines, we
[System requirements](https://www.blender.org/download/requirements/) are the same as Blender.
Each Blender release supports one Python version, and the package is only compatible with that version.
## Source Code
* [Releases](https://download.blender.org/source/)
* Repository: [git.blender.org/blender.git](https://git.blender.org/gitweb/gitweb.cgi/blender.git)
## Credits
Created by the [Blender developer community](https://www.blender.org/about/credits/).
Thanks to Tyler Alden Gubala for maintaining the original version of this package."""
Each Blender release supports one Python version, and the package is only compatible with that version."""
# ------------------------------------------------------------------------------
# Generic Functions

View File

@@ -870,26 +870,6 @@ an issue but, due to internal implementation details, currently are:
thus breaking any current iteration over ``Collection.all_objects``.
.. rubric:: Do not:
.. code-block:: python
# `all_objects` is an iterator. Using it directly while performing operations on its members that will update
# the memory accessed by the `all_objects` iterator will lead to invalid memory accesses and crashes.
for object in bpy.data.collections["Collection"].all_objects:
object.hide_viewport = True
.. rubric:: Do:
.. code-block:: python
# `all_objects[:]` is an independent list generated from the iterator. As long as no objects are deleted,
# its content will remain valid even if the data accessed by the `all_objects` iterator is modified.
for object in bpy.data.collections["Collection"].all_objects[:]:
object.hide_viewport = True
sys.exit
========

View File

@@ -1294,7 +1294,6 @@ def pycontext2sphinx(basepath):
type_descr = prop.get_type_description(
class_fmt=":class:`bpy.types.%s`",
mathutils_fmt=":class:`mathutils.%s`",
collection_id=_BPY_PROP_COLLECTION_ID,
enum_descr_override=enum_descr_override,
)
@@ -1447,7 +1446,6 @@ def pyrna2sphinx(basepath):
identifier = " %s" % prop.identifier
kwargs["class_fmt"] = ":class:`%s`"
kwargs["mathutils_fmt"] = ":class:`mathutils.%s`"
kwargs["collection_id"] = _BPY_PROP_COLLECTION_ID
@@ -1567,7 +1565,6 @@ def pyrna2sphinx(basepath):
type_descr = prop.get_type_description(
class_fmt=":class:`%s`",
mathutils_fmt=":class:`mathutils.%s`",
collection_id=_BPY_PROP_COLLECTION_ID,
enum_descr_override=enum_descr_override,
)
@@ -1634,7 +1631,6 @@ def pyrna2sphinx(basepath):
type_descr = prop.get_type_description(
as_ret=True, class_fmt=":class:`%s`",
mathutils_fmt=":class:`mathutils.%s`",
collection_id=_BPY_PROP_COLLECTION_ID,
enum_descr_override=enum_descr_override,
)

View File

@@ -86,14 +86,6 @@ enum_sampling_pattern = (
('PROGRESSIVE_MULTI_JITTER', "Progressive Multi-Jitter", "Use Progressive Multi-Jitter random sampling pattern", 1),
)
enum_emission_sampling = (
('NONE', 'None', "Do not use this surface as a light for sampling", 0),
('AUTO', 'Auto', "Automatically determine if the surface should be treated as a light for sampling, based on estimated emission intensity", 1),
('FRONT', 'Front', "Treat only front side of the surface as a light, usually for closed meshes whose interior is not visible", 2),
('BACK', 'Back', "Treat only back side of the surface as a light for sampling", 3),
('FRONT_BACK', 'Front and Back', "Treat surface as a light for sampling, emitting from both the front and back side", 4),
)
enum_volume_sampling = (
('DISTANCE',
"Distance",
@@ -1051,13 +1043,13 @@ class CyclesCameraSettings(bpy.types.PropertyGroup):
class CyclesMaterialSettings(bpy.types.PropertyGroup):
emission_sampling: EnumProperty(
name="Emission Sampling",
description="Sampling strategy for emissive surfaces",
items=enum_emission_sampling,
default="AUTO",
sample_as_light: BoolProperty(
name="Multiple Importance Sample",
description="Use multiple importance sampling for this material, "
"disabling may reduce overall noise for large "
"objects that emit little light compared to other light sources",
default=True,
)
use_transparent_shadow: BoolProperty(
name="Transparent Shadows",
description="Use transparent shadows for this material if it contains a Transparent BSDF, "

View File

@@ -1832,9 +1832,9 @@ class CYCLES_MATERIAL_PT_settings_surface(CyclesButtonsPanel, Panel):
cmat = mat.cycles
col = layout.column()
col.prop(cmat, "displacement_method", text="Displacement")
col.prop(cmat, "emission_sampling")
col.prop(cmat, "sample_as_light", text="Multiple Importance")
col.prop(cmat, "use_transparent_shadow")
col.prop(cmat, "displacement_method", text="Displacement")
def draw(self, context):
self.draw_shared(self, context.material)

View File

@@ -99,7 +99,7 @@ def do_versions(self):
library_versions.setdefault(library.version, []).append(library)
# Do versioning per library, since they might have different versions.
max_need_versioning = (3, 5, 2)
max_need_versioning = (3, 0, 25)
for version, libraries in library_versions.items():
if version > max_need_versioning:
continue
@@ -297,8 +297,3 @@ def do_versions(self):
cmat = mat.cycles
if not cmat.is_property_set("displacement_method"):
cmat.displacement_method = 'DISPLACEMENT'
if version <= (3, 5, 3):
cmat = mat.cycles
if not cmat.get("sample_as_light", True):
cmat.emission_sampling = 'NONE'

File diff suppressed because it is too large Load Diff

View File

@@ -15,10 +15,6 @@
#include "util/unique_ptr.h"
#include "util/vector.h"
typedef struct GPUContext GPUContext;
typedef struct GPUFence GPUFence;
typedef struct GPUShader GPUShader;
CCL_NAMESPACE_BEGIN
/* Base class of shader used for display driver rendering. */
@@ -33,7 +29,7 @@ class BlenderDisplayShader {
BlenderDisplayShader() = default;
virtual ~BlenderDisplayShader() = default;
virtual GPUShader *bind(int width, int height) = 0;
virtual void bind(int width, int height) = 0;
virtual void unbind() = 0;
/* Get attribute location for position and texture coordinate respectively.
@@ -44,7 +40,7 @@ class BlenderDisplayShader {
protected:
/* Get program of this display shader.
* NOTE: The shader needs to be bound to have access to this. */
virtual GPUShader *get_shader_program() = 0;
virtual uint get_shader_program() = 0;
/* Cached values of various OpenGL resources. */
int position_attribute_location_ = -1;
@@ -55,16 +51,16 @@ class BlenderDisplayShader {
* display space shader. */
class BlenderFallbackDisplayShader : public BlenderDisplayShader {
public:
virtual GPUShader *bind(int width, int height) override;
virtual void bind(int width, int height) override;
virtual void unbind() override;
protected:
virtual GPUShader *get_shader_program() override;
virtual uint get_shader_program() override;
void create_shader_if_needed();
void destroy_shader();
GPUShader *shader_program_ = 0;
uint shader_program_ = 0;
int image_texture_location_ = -1;
int fullscreen_location_ = -1;
@@ -77,17 +73,17 @@ class BlenderDisplaySpaceShader : public BlenderDisplayShader {
public:
BlenderDisplaySpaceShader(BL::RenderEngine &b_engine, BL::Scene &b_scene);
virtual GPUShader *bind(int width, int height) override;
virtual void bind(int width, int height) override;
virtual void unbind() override;
protected:
virtual GPUShader *get_shader_program() override;
virtual uint get_shader_program() override;
BL::RenderEngine b_engine_;
BL::Scene &b_scene_;
/* Cached values of various OpenGL resources. */
GPUShader *shader_program_ = nullptr;
uint shader_program_ = 0;
};
/* Display driver implementation which is specific for Blender viewport integration. */
@@ -126,9 +122,6 @@ class BlenderDisplayDriver : public DisplayDriver {
void gpu_context_lock();
void gpu_context_unlock();
/* Create GPU resources used by the dispaly driver. */
bool gpu_resources_create();
/* Destroy all GPU resources which are being used by this object. */
void gpu_resources_destroy();
@@ -144,8 +137,8 @@ class BlenderDisplayDriver : public DisplayDriver {
struct Tiles;
unique_ptr<Tiles> tiles_;
GPUFence *gpu_render_sync_ = nullptr;
GPUFence *gpu_upload_sync_ = nullptr;
void *gl_render_sync_ = nullptr;
void *gl_upload_sync_ = nullptr;
float2 zoom_ = make_float2(1.0f, 1.0f);
};

View File

@@ -1085,11 +1085,11 @@ static void create_subd_mesh(Scene *scene,
const int edges_num = b_mesh.edges.length();
if (edges_num != 0 && b_mesh.edge_creases.length() > 0) {
BL::MeshEdgeCreaseLayer creases = b_mesh.edge_creases[0];
size_t num_creases = 0;
const float *creases = static_cast<float *>(b_mesh.edge_creases[0].ptr.data);
for (int i = 0; i < edges_num; i++) {
if (creases.data[i].value() != 0.0f) {
if (creases[i] != 0.0f) {
num_creases++;
}
}
@@ -1098,18 +1098,17 @@ static void create_subd_mesh(Scene *scene,
const MEdge *edges = static_cast<MEdge *>(b_mesh.edges[0].ptr.data);
for (int i = 0; i < edges_num; i++) {
const float crease = creases.data[i].value();
if (crease != 0.0f) {
if (creases[i] != 0.0f) {
const MEdge &b_edge = edges[i];
mesh->add_edge_crease(b_edge.v1, b_edge.v2, crease);
mesh->add_edge_crease(b_edge.v1, b_edge.v2, creases[i]);
}
}
}
for (BL::MeshVertexCreaseLayer &c : b_mesh.vertex_creases) {
for (int i = 0; i < c.data.length(); ++i) {
if (c.data[i].value() != 0.0f) {
mesh->add_vertex_crease(i, c.data[i].value());
for (BL::MeshVertexCreaseLayer &c : b_mesh.vertex_creases) {
for (int i = 0; i < c.data.length(); ++i) {
if (c.data[i].value() != 0.0f) {
mesh->add_vertex_crease(i, c.data[i].value());
}
}
}
}

View File

@@ -61,12 +61,6 @@ static DisplacementMethod get_displacement_method(PointerRNA &ptr)
ptr, "displacement_method", DISPLACE_NUM_METHODS, DISPLACE_BUMP);
}
static EmissionSampling get_emission_sampling(PointerRNA &ptr)
{
return (EmissionSampling)get_enum(
ptr, "emission_sampling", EMISSION_SAMPLING_NUM, EMISSION_SAMPLING_AUTO);
}
static int validate_enum_value(int value, int num_values, int default_value)
{
if (value >= num_values) {
@@ -1565,7 +1559,7 @@ void BlenderSync::sync_materials(BL::Depsgraph &b_depsgraph, bool update_all)
/* settings */
PointerRNA cmat = RNA_pointer_get(&b_mat.ptr, "cycles");
shader->set_emission_sampling_method(get_emission_sampling(cmat));
shader->set_use_mis(get_boolean(cmat, "sample_as_light"));
shader->set_use_transparent_shadow(get_boolean(cmat, "use_transparent_shadow"));
shader->set_heterogeneous_volume(!get_boolean(cmat, "homogeneous_volume"));
shader->set_volume_sampling_method(get_volume_sampling(cmat));

View File

@@ -1,6 +1,584 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2011-2022 Blender Foundation
###########################################################################
# Helper macros
###########################################################################
macro(_set_default variable value)
if(NOT ${variable})
set(${variable} ${value})
endif()
endmacro()
###########################################################################
# Precompiled libraries detection
#
# Use precompiled libraries from Blender repository
###########################################################################
if(CYCLES_STANDALONE_REPOSITORY)
if(APPLE)
if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "x86_64")
set(_cycles_lib_dir "${CMAKE_SOURCE_DIR}/../lib/darwin")
else()
set(_cycles_lib_dir "${CMAKE_SOURCE_DIR}/../lib/darwin_arm64")
endif()
# Always use system zlib
find_package(ZLIB REQUIRED)
elseif(WIN32)
if(CMAKE_CL_64)
set(_cycles_lib_dir "${CMAKE_SOURCE_DIR}/../lib/win64_vc15")
else()
message(FATAL_ERROR "Unsupported Visual Studio Version")
endif()
else()
# Path to a locally compiled libraries.
set(LIBDIR_NAME ${CMAKE_SYSTEM_NAME}_${CMAKE_SYSTEM_PROCESSOR})
string(TOLOWER ${LIBDIR_NAME} LIBDIR_NAME)
set(LIBDIR_NATIVE_ABI ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_NAME})
# Path to precompiled libraries with known CentOS 7 ABI.
set(LIBDIR_CENTOS7_ABI ${CMAKE_SOURCE_DIR}/../lib/linux_centos7_x86_64)
# Choose the best suitable libraries.
if(EXISTS ${LIBDIR_NATIVE_ABI})
set(_cycles_lib_dir ${LIBDIR_NATIVE_ABI})
elseif(EXISTS ${LIBDIR_CENTOS7_ABI})
set(_cycles_lib_dir ${LIBDIR_CENTOS7_ABI})
set(WITH_CXX11_ABI OFF)
if(CMAKE_COMPILER_IS_GNUCC AND
CMAKE_C_COMPILER_VERSION VERSION_LESS 9.3)
message(FATAL_ERROR "GCC version must be at least 9.3 for precompiled libraries, found ${CMAKE_C_COMPILER_VERSION}")
endif()
endif()
if(DEFINED _cycles_lib_dir)
message(STATUS "Using precompiled libraries at ${_cycles_lib_dir}")
endif()
# Avoid namespace pollustion.
unset(LIBDIR_NATIVE_ABI)
unset(LIBDIR_CENTOS7_ABI)
endif()
if(EXISTS ${_cycles_lib_dir})
_set_default(ALEMBIC_ROOT_DIR "${_cycles_lib_dir}/alembic")
_set_default(BOOST_ROOT "${_cycles_lib_dir}/boost")
_set_default(BLOSC_ROOT_DIR "${_cycles_lib_dir}/blosc")
_set_default(EMBREE_ROOT_DIR "${_cycles_lib_dir}/embree")
_set_default(EPOXY_ROOT_DIR "${_cycles_lib_dir}/epoxy")
_set_default(IMATH_ROOT_DIR "${_cycles_lib_dir}/imath")
_set_default(GLEW_ROOT_DIR "${_cycles_lib_dir}/glew")
_set_default(JPEG_ROOT "${_cycles_lib_dir}/jpeg")
_set_default(LLVM_ROOT_DIR "${_cycles_lib_dir}/llvm")
_set_default(CLANG_ROOT_DIR "${_cycles_lib_dir}/llvm")
_set_default(NANOVDB_ROOT_DIR "${_cycles_lib_dir}/openvdb")
_set_default(OPENCOLORIO_ROOT_DIR "${_cycles_lib_dir}/opencolorio")
_set_default(OPENEXR_ROOT_DIR "${_cycles_lib_dir}/openexr")
_set_default(OPENIMAGEDENOISE_ROOT_DIR "${_cycles_lib_dir}/openimagedenoise")
_set_default(OPENIMAGEIO_ROOT_DIR "${_cycles_lib_dir}/openimageio")
_set_default(OPENJPEG_ROOT_DIR "${_cycles_lib_dir}/openjpeg")
_set_default(OPENSUBDIV_ROOT_DIR "${_cycles_lib_dir}/opensubdiv")
_set_default(OPENVDB_ROOT_DIR "${_cycles_lib_dir}/openvdb")
_set_default(OSL_ROOT_DIR "${_cycles_lib_dir}/osl")
_set_default(PNG_ROOT "${_cycles_lib_dir}/png")
_set_default(PUGIXML_ROOT_DIR "${_cycles_lib_dir}/pugixml")
_set_default(SDL2_ROOT_DIR "${_cycles_lib_dir}/sdl")
_set_default(TBB_ROOT_DIR "${_cycles_lib_dir}/tbb")
_set_default(TIFF_ROOT "${_cycles_lib_dir}/tiff")
_set_default(USD_ROOT_DIR "${_cycles_lib_dir}/usd")
_set_default(WEBP_ROOT_DIR "${_cycles_lib_dir}/webp")
_set_default(ZLIB_ROOT "${_cycles_lib_dir}/zlib")
if(WIN32)
set(LEVEL_ZERO_ROOT_DIR ${_cycles_lib_dir}/level_zero)
else()
set(LEVEL_ZERO_ROOT_DIR ${_cycles_lib_dir}/level-zero)
endif()
_set_default(SYCL_ROOT_DIR "${_cycles_lib_dir}/dpcpp")
# Ignore system libraries
set(CMAKE_IGNORE_PATH "${CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES};${CMAKE_SYSTEM_INCLUDE_PATH};${CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES};${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}")
else()
unset(_cycles_lib_dir)
endif()
endif()
###########################################################################
# Zlib
###########################################################################
if(CYCLES_STANDALONE_REPOSITORY)
if(MSVC AND EXISTS ${_cycles_lib_dir})
set(ZLIB_INCLUDE_DIRS ${_cycles_lib_dir}/zlib/include)
set(ZLIB_LIBRARIES ${_cycles_lib_dir}/zlib/lib/libz_st.lib)
set(ZLIB_INCLUDE_DIR ${_cycles_lib_dir}/zlib/include)
set(ZLIB_LIBRARY ${_cycles_lib_dir}/zlib/lib/libz_st.lib)
set(ZLIB_DIR ${_cycles_lib_dir}/zlib)
set(ZLIB_FOUND ON)
elseif(NOT APPLE)
find_package(ZLIB REQUIRED)
endif()
endif()
###########################################################################
# PThreads
###########################################################################
if(CYCLES_STANDALONE_REPOSITORY)
if(MSVC AND EXISTS ${_cycles_lib_dir})
set(PTHREADS_LIBRARIES "${_cycles_lib_dir}/pthreads/lib/pthreadVC3.lib")
include_directories("${_cycles_lib_dir}/pthreads/include")
else()
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
find_package(Threads REQUIRED)
set(PTHREADS_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})
endif()
endif()
###########################################################################
# OpenImageIO and image libraries
###########################################################################
if(CYCLES_STANDALONE_REPOSITORY)
if(MSVC AND EXISTS ${_cycles_lib_dir})
add_definitions(
# OIIO changed the name of this define in newer versions
# we define both, so it would work with both old and new
# versions.
-DOIIO_STATIC_BUILD
-DOIIO_STATIC_DEFINE
)
set(OPENIMAGEIO_INCLUDE_DIR ${OPENIMAGEIO_ROOT_DIR}/include)
set(OPENIMAGEIO_INCLUDE_DIRS ${OPENIMAGEIO_INCLUDE_DIR} ${OPENIMAGEIO_INCLUDE_DIR}/OpenImageIO)
# Special exceptions for libraries which needs explicit debug version
set(OPENIMAGEIO_LIBRARIES
optimized ${OPENIMAGEIO_ROOT_DIR}/lib/OpenImageIO.lib
optimized ${OPENIMAGEIO_ROOT_DIR}/lib/OpenImageIO_Util.lib
debug ${OPENIMAGEIO_ROOT_DIR}/lib/OpenImageIO_d.lib
debug ${OPENIMAGEIO_ROOT_DIR}/lib/OpenImageIO_Util_d.lib
)
set(PUGIXML_INCLUDE_DIR ${PUGIXML_ROOT_DIR}/include)
set(PUGIXML_LIBRARIES
optimized ${PUGIXML_ROOT_DIR}/lib/pugixml.lib
debug ${PUGIXML_ROOT_DIR}/lib/pugixml_d.lib
)
else()
find_package(OpenImageIO REQUIRED)
if(OPENIMAGEIO_PUGIXML_FOUND)
set(PUGIXML_INCLUDE_DIR "${OPENIMAGEIO_INCLUDE_DIR}/OpenImageIO")
set(PUGIXML_LIBRARIES "")
else()
find_package(PugiXML REQUIRED)
endif()
endif()
# Dependencies
if(MSVC AND EXISTS ${_cycles_lib_dir})
set(OPENJPEG_INCLUDE_DIR ${OPENJPEG}/include/openjpeg-2.3)
set(OPENJPEG_LIBRARIES ${_cycles_lib_dir}/openjpeg/lib/openjp2${CMAKE_STATIC_LIBRARY_SUFFIX})
else()
find_package(OpenJPEG REQUIRED)
endif()
find_package(JPEG REQUIRED)
find_package(TIFF REQUIRED)
find_package(WebP)
if(EXISTS ${_cycles_lib_dir})
set(PNG_NAMES png16 libpng16 png libpng)
endif()
find_package(PNG REQUIRED)
endif()
###########################################################################
# OpenEXR
###########################################################################
if(CYCLES_STANDALONE_REPOSITORY)
if(MSVC AND EXISTS ${_cycles_lib_dir})
set(OPENEXR_INCLUDE_DIR ${OPENEXR_ROOT_DIR}/include)
set(OPENEXR_INCLUDE_DIRS ${OPENEXR_INCLUDE_DIR} ${OPENEXR_ROOT_DIR}/include/OpenEXR ${IMATH_ROOT_DIR}/include ${IMATH_ROOT_DIR}/include/Imath)
set(OPENEXR_LIBRARIES
optimized ${OPENEXR_ROOT_DIR}/lib/OpenEXR_s.lib
optimized ${OPENEXR_ROOT_DIR}/lib/OpenEXRCore_s.lib
optimized ${OPENEXR_ROOT_DIR}/lib/Iex_s.lib
optimized ${IMATH_ROOT_DIR}/lib/Imath_s.lib
optimized ${OPENEXR_ROOT_DIR}/lib/IlmThread_s.lib
debug ${OPENEXR_ROOT_DIR}/lib/OpenEXR_s_d.lib
debug ${OPENEXR_ROOT_DIR}/lib/OpenEXRCore_s_d.lib
debug ${OPENEXR_ROOT_DIR}/lib/Iex_s_d.lib
debug ${IMATH_ROOT_DIR}/lib/Imath_s_d.lib
debug ${OPENEXR_ROOT_DIR}/lib/IlmThread_s_d.lib
)
else()
find_package(OpenEXR REQUIRED)
endif()
endif()
###########################################################################
# OpenShadingLanguage & LLVM
###########################################################################
if(CYCLES_STANDALONE_REPOSITORY AND WITH_CYCLES_OSL)
if(EXISTS ${_cycles_lib_dir})
set(LLVM_STATIC ON)
endif()
if(MSVC AND EXISTS ${_cycles_lib_dir})
# TODO(sergey): On Windows llvm-config doesn't give proper results for the
# library names, use hardcoded libraries for now.
file(GLOB _llvm_libs_release ${LLVM_ROOT_DIR}/lib/*.lib)
file(GLOB _llvm_libs_debug ${LLVM_ROOT_DIR}/debug/lib/*.lib)
set(_llvm_libs)
foreach(_llvm_lib_path ${_llvm_libs_release})
get_filename_component(_llvm_lib_name ${_llvm_lib_path} ABSOLUTE)
list(APPEND _llvm_libs optimized ${_llvm_lib_name})
endforeach()
foreach(_llvm_lib_path ${_llvm_libs_debug})
get_filename_component(_llvm_lib_name ${_llvm_lib_path} ABSOLUTE)
list(APPEND _llvm_libs debug ${_llvm_lib_name})
endforeach()
set(LLVM_LIBRARY ${_llvm_libs})
unset(_llvm_lib_name)
unset(_llvm_lib_path)
unset(_llvm_libs)
unset(_llvm_libs_debug)
unset(_llvm_libs_release)
set(OSL_INCLUDE_DIR ${OSL_ROOT_DIR}/include)
set(OSL_LIBRARIES
optimized ${OSL_ROOT_DIR}/lib/oslcomp.lib
optimized ${OSL_ROOT_DIR}/lib/oslexec.lib
optimized ${OSL_ROOT_DIR}/lib/oslquery.lib
debug ${OSL_ROOT_DIR}/lib/oslcomp_d.lib
debug ${OSL_ROOT_DIR}/lib/oslexec_d.lib
debug ${OSL_ROOT_DIR}/lib/oslquery_d.lib
${PUGIXML_LIBRARIES}
)
find_program(OSL_COMPILER NAMES oslc PATHS ${OSL_ROOT_DIR}/bin)
else()
find_package(OSL REQUIRED)
find_package(LLVM REQUIRED)
find_package(Clang REQUIRED)
endif()
endif()
###########################################################################
# OpenPGL
###########################################################################
if(CYCLES_STANDALONE_REPOSITORY AND WITH_CYCLES_PATH_GUIDING)
if(NOT openpgl_DIR AND EXISTS ${_cycles_lib_dir})
set(openpgl_DIR ${_cycles_lib_dir}/openpgl/lib/cmake/openpgl)
endif()
find_package(openpgl QUIET)
if(openpgl_FOUND)
if(WIN32)
get_target_property(OPENPGL_LIBRARIES_RELEASE openpgl::openpgl LOCATION_RELEASE)
get_target_property(OPENPGL_LIBRARIES_DEBUG openpgl::openpgl LOCATION_DEBUG)
set(OPENPGL_LIBRARIES optimized ${OPENPGL_LIBRARIES_RELEASE} debug ${OPENPGL_LIBRARIES_DEBUG})
else()
get_target_property(OPENPGL_LIBRARIES openpgl::openpgl LOCATION)
endif()
get_target_property(OPENPGL_INCLUDE_DIR openpgl::openpgl INTERFACE_INCLUDE_DIRECTORIES)
else()
set_and_warn_library_found("OpenPGL" openpgl_FOUND WITH_CYCLES_PATH_GUIDING)
endif()
endif()
###########################################################################
# OpenColorIO
###########################################################################
if(CYCLES_STANDALONE_REPOSITORY AND WITH_CYCLES_OPENCOLORIO)
set(WITH_OPENCOLORIO ON)
if(NOT USD_OVERRIDE_OPENCOLORIO)
if(MSVC AND EXISTS ${_cycles_lib_dir})
set(OPENCOLORIO_INCLUDE_DIRS ${OPENCOLORIO_ROOT_DIR}/include)
set(OPENCOLORIO_LIBRARIES
optimized ${OPENCOLORIO_ROOT_DIR}/lib/OpenColorIO.lib
optimized ${OPENCOLORIO_ROOT_DIR}/lib/libyaml-cpp.lib
optimized ${OPENCOLORIO_ROOT_DIR}/lib/libexpatMD.lib
optimized ${OPENCOLORIO_ROOT_DIR}/lib/pystring.lib
debug ${OPENCOLORIO_ROOT_DIR}/lib/OpencolorIO_d.lib
debug ${OPENCOLORIO_ROOT_DIR}/lib/libyaml-cpp_d.lib
debug ${OPENCOLORIO_ROOT_DIR}/lib/libexpatdMD.lib
debug ${OPENCOLORIO_ROOT_DIR}/lib/pystring_d.lib
)
else()
find_package(OpenColorIO REQUIRED)
endif()
endif()
endif()
###########################################################################
# Boost
###########################################################################
if(CYCLES_STANDALONE_REPOSITORY)
if(EXISTS ${_cycles_lib_dir})
if(MSVC)
set(Boost_USE_STATIC_RUNTIME OFF)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_LIBS ON)
else()
set(BOOST_LIBRARYDIR ${_cycles_lib_dir}/boost/lib)
set(Boost_NO_BOOST_CMAKE ON)
set(Boost_NO_SYSTEM_PATHS ON)
endif()
endif()
if(MSVC AND EXISTS ${_cycles_lib_dir})
set(BOOST_INCLUDE_DIR ${BOOST_ROOT}/include)
set(BOOST_VERSION_HEADER ${BOOST_INCLUDE_DIR}/boost/version.hpp)
if(EXISTS ${BOOST_VERSION_HEADER})
file(STRINGS "${BOOST_VERSION_HEADER}" BOOST_LIB_VERSION REGEX "#define BOOST_LIB_VERSION ")
if(BOOST_LIB_VERSION MATCHES "#define BOOST_LIB_VERSION \"([0-9_]+)\"")
set(BOOST_VERSION "${CMAKE_MATCH_1}")
endif()
endif()
if(NOT BOOST_VERSION)
message(FATAL_ERROR "Unable to determine Boost version")
endif()
set(BOOST_POSTFIX "vc142-mt-x64-${BOOST_VERSION}.lib")
set(BOOST_DEBUG_POSTFIX "vc142-mt-gd-x64-${BOOST_VERSION}.lib")
set(BOOST_LIBRARIES
optimized ${BOOST_ROOT}/lib/libboost_date_time-${BOOST_POSTFIX}
optimized ${BOOST_ROOT}/lib/libboost_iostreams-${BOOST_POSTFIX}
optimized ${BOOST_ROOT}/lib/libboost_filesystem-${BOOST_POSTFIX}
optimized ${BOOST_ROOT}/lib/libboost_regex-${BOOST_POSTFIX}
optimized ${BOOST_ROOT}/lib/libboost_system-${BOOST_POSTFIX}
optimized ${BOOST_ROOT}/lib/libboost_thread-${BOOST_POSTFIX}
optimized ${BOOST_ROOT}/lib/libboost_chrono-${BOOST_POSTFIX}
debug ${BOOST_ROOT}/lib/libboost_date_time-${BOOST_DEBUG_POSTFIX}
debug ${BOOST_ROOT}/lib/libboost_iostreams-${BOOST_DEBUG_POSTFIX}
debug ${BOOST_ROOT}/lib/libboost_filesystem-${BOOST_DEBUG_POSTFIX}
debug ${BOOST_ROOT}/lib/libboost_regex-${BOOST_DEBUG_POSTFIX}
debug ${BOOST_ROOT}/lib/libboost_system-${BOOST_DEBUG_POSTFIX}
debug ${BOOST_ROOT}/lib/libboost_thread-${BOOST_DEBUG_POSTFIX}
debug ${BOOST_ROOT}/lib/libboost_chrono-${BOOST_DEBUG_POSTFIX}
)
if(WITH_CYCLES_OSL)
set(BOOST_LIBRARIES ${BOOST_LIBRARIES}
optimized ${BOOST_ROOT}/lib/libboost_wave-${BOOST_POSTFIX}
debug ${BOOST_ROOT}/lib/libboost_wave-${BOOST_DEBUG_POSTFIX})
endif()
else()
set(__boost_packages iostreams filesystem regex system thread date_time)
if(WITH_CYCLES_OSL)
list(APPEND __boost_packages wave)
endif()
find_package(Boost 1.48 COMPONENTS ${__boost_packages} REQUIRED)
if(NOT Boost_FOUND)
# Try to find non-multithreaded if -mt not found, this flag
# doesn't matter for us, it has nothing to do with thread
# safety, but keep it to not disturb build setups.
set(Boost_USE_MULTITHREADED OFF)
find_package(Boost 1.48 COMPONENTS ${__boost_packages})
endif()
unset(__boost_packages)
set(BOOST_INCLUDE_DIR ${Boost_INCLUDE_DIRS})
set(BOOST_LIBRARIES ${Boost_LIBRARIES})
set(BOOST_LIBPATH ${Boost_LIBRARY_DIRS})
endif()
set(BOOST_DEFINITIONS "-DBOOST_ALL_NO_LIB ${BOOST_DEFINITIONS}")
endif()
###########################################################################
# Embree
###########################################################################
if(CYCLES_STANDALONE_REPOSITORY AND WITH_CYCLES_EMBREE)
if(MSVC AND EXISTS ${_cycles_lib_dir})
set(EMBREE_INCLUDE_DIRS ${EMBREE_ROOT_DIR}/include)
set(EMBREE_LIBRARIES
optimized ${EMBREE_ROOT_DIR}/lib/embree3.lib
optimized ${EMBREE_ROOT_DIR}/lib/embree_avx2.lib
optimized ${EMBREE_ROOT_DIR}/lib/embree_avx.lib
optimized ${EMBREE_ROOT_DIR}/lib/embree_sse42.lib
optimized ${EMBREE_ROOT_DIR}/lib/lexers.lib
optimized ${EMBREE_ROOT_DIR}/lib/math.lib
optimized ${EMBREE_ROOT_DIR}/lib/simd.lib
optimized ${EMBREE_ROOT_DIR}/lib/tasking.lib
optimized ${EMBREE_ROOT_DIR}/lib/sys.lib
debug ${EMBREE_ROOT_DIR}/lib/embree3_d.lib
debug ${EMBREE_ROOT_DIR}/lib/embree_avx2_d.lib
debug ${EMBREE_ROOT_DIR}/lib/embree_avx_d.lib
debug ${EMBREE_ROOT_DIR}/lib/embree_sse42_d.lib
debug ${EMBREE_ROOT_DIR}/lib/lexers_d.lib
debug ${EMBREE_ROOT_DIR}/lib/math_d.lib
debug ${EMBREE_ROOT_DIR}/lib/simd_d.lib
debug ${EMBREE_ROOT_DIR}/lib/sys_d.lib
debug ${EMBREE_ROOT_DIR}/lib/tasking_d.lib
)
else()
find_package(Embree 3.8.0 REQUIRED)
endif()
endif()
###########################################################################
# Logging
###########################################################################
if(CYCLES_STANDALONE_REPOSITORY AND WITH_CYCLES_LOGGING)
find_package(Glog REQUIRED)
find_package(Gflags REQUIRED)
endif()
###########################################################################
# OpenSubdiv
###########################################################################
if(CYCLES_STANDALONE_REPOSITORY AND WITH_CYCLES_OPENSUBDIV)
set(WITH_OPENSUBDIV ON)
if(NOT USD_OVERRIDE_OPENSUBDIV)
if(MSVC AND EXISTS ${_cycles_lib_dir})
set(OPENSUBDIV_INCLUDE_DIRS ${OPENSUBDIV_ROOT_DIR}/include)
set(OPENSUBDIV_LIBRARIES
optimized ${OPENSUBDIV_ROOT_DIR}/lib/osdCPU.lib
optimized ${OPENSUBDIV_ROOT_DIR}/lib/osdGPU.lib
debug ${OPENSUBDIV_ROOT_DIR}/lib/osdCPU_d.lib
debug ${OPENSUBDIV_ROOT_DIR}/lib/osdGPU_d.lib
)
else()
find_package(OpenSubdiv REQUIRED)
endif()
endif()
endif()
###########################################################################
# OpenVDB
###########################################################################
if(CYCLES_STANDALONE_REPOSITORY AND WITH_CYCLES_OPENVDB)
set(WITH_OPENVDB ON)
set(OPENVDB_DEFINITIONS -DNOMINMAX -D_USE_MATH_DEFINES)
if(NOT USD_OVERRIDE_OPENVDB)
find_package(OpenVDB REQUIRED)
if(MSVC AND EXISTS ${_cycles_lib_dir})
set(BLOSC_LIBRARY
optimized ${BLOSC_ROOT_DIR}/lib/libblosc.lib
debug ${BLOSC_ROOT_DIR}/lib/libblosc_d.lib
)
else()
find_package(Blosc REQUIRED)
endif()
endif()
endif()
###########################################################################
# NanoVDB
###########################################################################
if(CYCLES_STANDALONE_REPOSITORY AND WITH_CYCLES_NANOVDB)
set(WITH_NANOVDB ON)
if(MSVC AND EXISTS ${_cycles_lib_dir})
set(NANOVDB_INCLUDE_DIR ${NANOVDB_ROOT_DIR}/include)
set(NANOVDB_INCLUDE_DIRS ${NANOVDB_INCLUDE_DIR})
else()
find_package(NanoVDB REQUIRED)
endif()
endif()
###########################################################################
# OpenImageDenoise
###########################################################################
if(CYCLES_STANDALONE_REPOSITORY AND WITH_CYCLES_OPENIMAGEDENOISE)
set(WITH_OPENIMAGEDENOISE ON)
if(MSVC AND EXISTS ${_cycles_lib_dir})
set(OPENIMAGEDENOISE_INCLUDE_DIRS ${OPENIMAGEDENOISE_ROOT_DIR}/include)
set(OPENIMAGEDENOISE_LIBRARIES
optimized ${OPENIMAGEDENOISE_ROOT_DIR}/lib/OpenImageDenoise.lib
optimized ${OPENIMAGEDENOISE_ROOT_DIR}/lib/common.lib
optimized ${OPENIMAGEDENOISE_ROOT_DIR}/lib/dnnl.lib
debug ${OPENIMAGEDENOISE_ROOT_DIR}/lib/OpenImageDenoise_d.lib
debug ${OPENIMAGEDENOISE_ROOT_DIR}/lib/common_d.lib
debug ${OPENIMAGEDENOISE_ROOT_DIR}/lib/dnnl_d.lib
)
else()
find_package(OpenImageDenoise REQUIRED)
endif()
endif()
###########################################################################
# TBB
###########################################################################
if(CYCLES_STANDALONE_REPOSITORY)
if(NOT USD_OVERRIDE_TBB)
if(MSVC AND EXISTS ${_cycles_lib_dir})
set(TBB_INCLUDE_DIRS ${TBB_ROOT_DIR}/include)
set(TBB_LIBRARIES
optimized ${TBB_ROOT_DIR}/lib/tbb.lib
debug ${TBB_ROOT_DIR}/lib/tbb_debug.lib
)
else()
find_package(TBB REQUIRED)
endif()
endif()
endif()
###########################################################################
# Epoxy
###########################################################################
if(CYCLES_STANDALONE_REPOSITORY)
if((WITH_CYCLES_STANDALONE AND WITH_CYCLES_STANDALONE_GUI) OR
WITH_CYCLES_HYDRA_RENDER_DELEGATE)
if(MSVC AND EXISTS ${_cycles_lib_dir})
set(Epoxy_LIBRARIES "${_cycles_lib_dir}/epoxy/lib/epoxy.lib")
set(Epoxy_INCLUDE_DIRS "${_cycles_lib_dir}/epoxy/include")
else()
find_package(Epoxy REQUIRED)
endif()
endif()
endif()
###########################################################################
# Alembic
###########################################################################
if(WITH_CYCLES_ALEMBIC)
if(CYCLES_STANDALONE_REPOSITORY)
if(MSVC AND EXISTS ${_cycles_lib_dir})
set(ALEMBIC_INCLUDE_DIRS ${_cycles_lib_dir}/alembic/include)
set(ALEMBIC_LIBRARIES
optimized ${_cycles_lib_dir}/alembic/lib/Alembic.lib
debug ${_cycles_lib_dir}/alembic/lib/Alembic_d.lib)
else()
find_package(Alembic REQUIRED)
endif()
set(WITH_ALEMBIC ON)
endif()
endif()
###########################################################################
# System Libraries
###########################################################################
# Detect system libraries again
if(EXISTS ${_cycles_lib_dir})
unset(CMAKE_IGNORE_PATH)
unset(_cycles_lib_dir)
endif()
###########################################################################
# SDL
###########################################################################
@@ -109,3 +687,5 @@ if(WITH_CYCLES_DEVICE_ONEAPI)
set(WITH_CYCLES_DEVICE_ONEAPI OFF)
endif()
endif()
unset(_cycles_lib_dir)

View File

@@ -38,7 +38,7 @@ class CUDADeviceGraphicsInterop : public DeviceGraphicsInterop {
CUDADevice *device_ = nullptr;
/* OpenGL PBO which is currently registered as the destination for the CUDA buffer. */
int64_t opengl_pbo_id_ = 0;
uint opengl_pbo_id_ = 0;
/* Buffer area in pixels of the corresponding PBO. */
int64_t buffer_area_ = 0;

View File

@@ -36,7 +36,7 @@ class HIPDeviceGraphicsInterop : public DeviceGraphicsInterop {
HIPDevice *device_ = nullptr;
/* OpenGL PBO which is currently registered as the destination for the HIP buffer. */
int64_t opengl_pbo_id_ = 0;
uint opengl_pbo_id_ = 0;
/* Buffer area in pixels of the corresponding PBO. */
int64_t buffer_area_ = 0;

View File

@@ -285,16 +285,10 @@ set(SRC_KERNEL_INTEGRATOR_HEADERS
)
set(SRC_KERNEL_LIGHT_HEADERS
light/area.h
light/light.h
light/background.h
light/common.h
light/distant.h
light/distribution.h
light/light.h
light/point.h
light/sample.h
light/spot.h
light/triangle.h
)
set(SRC_KERNEL_SAMPLE_HEADERS
@@ -472,7 +466,6 @@ if(WITH_CYCLES_CUDA_BINARIES)
if(WITH_CYCLES_DEBUG)
set(cuda_flags ${cuda_flags} -D WITH_CYCLES_DEBUG)
set(cuda_flags ${cuda_flags} --ptxas-options="-v")
endif()
set(_cuda_nvcc_args
@@ -480,6 +473,7 @@ if(WITH_CYCLES_CUDA_BINARIES)
${CUDA_NVCC_FLAGS}
--${format}
${CMAKE_CURRENT_SOURCE_DIR}${cuda_kernel_src}
--ptxas-options="-v"
${cuda_flags})
if(WITH_COMPILER_CCACHE AND CCACHE_PROGRAM)

View File

@@ -69,7 +69,7 @@ ccl_device int bsdf_diffuse_sample(ccl_private const ShaderClosure *sc,
ccl_device int bsdf_translucent_setup(ccl_private DiffuseBsdf *bsdf)
{
bsdf->type = CLOSURE_BSDF_TRANSLUCENT_ID;
return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_HAS_TRANSMISSION;
return SD_BSDF | SD_BSDF_HAS_EVAL;
}
ccl_device Spectrum bsdf_translucent_eval(ccl_private const ShaderClosure *sc,

View File

@@ -34,7 +34,7 @@ ccl_device int bsdf_hair_transmission_setup(ccl_private HairBsdf *bsdf)
bsdf->type = CLOSURE_BSDF_HAIR_TRANSMISSION_ID;
bsdf->roughness1 = clamp(bsdf->roughness1, 0.001f, 1.0f);
bsdf->roughness2 = clamp(bsdf->roughness2, 0.001f, 1.0f);
return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_HAS_TRANSMISSION;
return SD_BSDF | SD_BSDF_HAS_EVAL;
}
ccl_device Spectrum bsdf_hair_reflection_eval(ccl_private const ShaderClosure *sc,

View File

@@ -196,7 +196,7 @@ ccl_device int bsdf_principled_hair_setup(ccl_private ShaderData *sd,
bsdf->extra->geom = make_float4(Y.x, Y.y, Y.z, h);
return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG | SD_BSDF_HAS_TRANSMISSION;
return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG;
}
#endif /* __HAIR__ */

View File

@@ -346,7 +346,7 @@ ccl_device int bsdf_microfacet_ggx_refraction_setup(ccl_private MicrofacetBsdf *
bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_HAS_TRANSMISSION;
return SD_BSDF | SD_BSDF_HAS_EVAL;
}
ccl_device void bsdf_microfacet_ggx_blur(ccl_private ShaderClosure *sc, float roughness)
@@ -776,7 +776,7 @@ ccl_device int bsdf_microfacet_beckmann_refraction_setup(ccl_private MicrofacetB
bsdf->alpha_y = bsdf->alpha_x;
bsdf->type = CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID;
return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_HAS_TRANSMISSION;
return SD_BSDF | SD_BSDF_HAS_EVAL;
}
ccl_device void bsdf_microfacet_beckmann_blur(ccl_private ShaderClosure *sc, float roughness)

View File

@@ -559,7 +559,7 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_setup(ccl_private MicrofacetBsdf
bsdf->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID;
return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG | SD_BSDF_HAS_TRANSMISSION;
return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG;
}
ccl_device int bsdf_microfacet_multi_ggx_glass_fresnel_setup(ccl_private MicrofacetBsdf *bsdf,

View File

@@ -23,19 +23,24 @@ KERNEL_STRUCT_MEMBER(background, int, volume_shader)
KERNEL_STRUCT_MEMBER(background, float, volume_step_size)
KERNEL_STRUCT_MEMBER(background, int, transparent)
KERNEL_STRUCT_MEMBER(background, float, transparent_roughness_squared_threshold)
/* Portal sampling. */
KERNEL_STRUCT_MEMBER(background, float, portal_weight)
KERNEL_STRUCT_MEMBER(background, int, num_portals)
KERNEL_STRUCT_MEMBER(background, int, portal_offset)
/* Sun sampling. */
KERNEL_STRUCT_MEMBER(background, float, sun_weight)
/* Importance map sampling. */
KERNEL_STRUCT_MEMBER(background, float, map_weight)
KERNEL_STRUCT_MEMBER(background, float, portal_weight)
KERNEL_STRUCT_MEMBER(background, int, map_res_x)
KERNEL_STRUCT_MEMBER(background, int, map_res_y)
/* Multiple importance sampling. */
KERNEL_STRUCT_MEMBER(background, int, use_mis)
/* Lightgroup. */
KERNEL_STRUCT_MEMBER(background, int, lightgroup)
/* Light Index. */
KERNEL_STRUCT_MEMBER(background, int, light_index)
/* Padding. */
KERNEL_STRUCT_MEMBER(background, int, pad1)
KERNEL_STRUCT_MEMBER(background, int, pad2)
KERNEL_STRUCT_MEMBER(background, int, pad3)
KERNEL_STRUCT_END(KernelBackground)
/* BVH: own BVH2 if no native device acceleration struct used. */
@@ -142,17 +147,10 @@ KERNEL_STRUCT_END(KernelFilm)
KERNEL_STRUCT_BEGIN(KernelIntegrator, integrator)
/* Emission. */
KERNEL_STRUCT_MEMBER(integrator, int, use_direct_light)
KERNEL_STRUCT_MEMBER(integrator, int, use_light_mis)
KERNEL_STRUCT_MEMBER(integrator, int, num_lights)
KERNEL_STRUCT_MEMBER(integrator, int, num_distant_lights)
KERNEL_STRUCT_MEMBER(integrator, int, num_background_lights)
/* Portal sampling. */
KERNEL_STRUCT_MEMBER(integrator, int, num_portals)
KERNEL_STRUCT_MEMBER(integrator, int, portal_offset)
/* Flat light distribution. */
KERNEL_STRUCT_MEMBER(integrator, int, num_distribution)
KERNEL_STRUCT_MEMBER(integrator, float, distribution_pdf_triangles)
KERNEL_STRUCT_MEMBER(integrator, float, distribution_pdf_lights)
KERNEL_STRUCT_MEMBER(integrator, int, num_all_lights)
KERNEL_STRUCT_MEMBER(integrator, float, pdf_triangles)
KERNEL_STRUCT_MEMBER(integrator, float, pdf_lights)
KERNEL_STRUCT_MEMBER(integrator, float, light_inv_rr_threshold)
/* Bounces. */
KERNEL_STRUCT_MEMBER(integrator, int, min_bounce)
@@ -179,6 +177,8 @@ KERNEL_STRUCT_MEMBER(integrator, int, seed)
/* Clamp. */
KERNEL_STRUCT_MEMBER(integrator, float, sample_clamp_direct)
KERNEL_STRUCT_MEMBER(integrator, float, sample_clamp_indirect)
/* MIS. */
KERNEL_STRUCT_MEMBER(integrator, int, use_lamp_mis)
/* Caustics. */
KERNEL_STRUCT_MEMBER(integrator, int, use_caustics)
/* Sampling pattern. */
@@ -195,6 +195,7 @@ KERNEL_STRUCT_MEMBER(integrator, int, has_shadow_catcher)
KERNEL_STRUCT_MEMBER(integrator, int, filter_closures)
/* MIS debugging. */
KERNEL_STRUCT_MEMBER(integrator, int, direct_light_sampling_type)
/* Path Guiding */
KERNEL_STRUCT_MEMBER(integrator, float, surface_guiding_probability)
KERNEL_STRUCT_MEMBER(integrator, float, volume_guiding_probability)

View File

@@ -314,7 +314,11 @@ ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
int kernel_index);
ccl_gpu_kernel_lambda_pass.kernel_index = kernel_index;
gpu_parallel_active_index_array(num_states, indices, num_indices, ccl_gpu_kernel_lambda_pass);
gpu_parallel_active_index_array(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE,
num_states,
indices,
num_indices,
ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
@@ -329,7 +333,11 @@ ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
int kernel_index);
ccl_gpu_kernel_lambda_pass.kernel_index = kernel_index;
gpu_parallel_active_index_array(num_states, indices, num_indices, ccl_gpu_kernel_lambda_pass);
gpu_parallel_active_index_array(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE,
num_states,
indices,
num_indices,
ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
@@ -341,7 +349,11 @@ ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
{
ccl_gpu_kernel_lambda(INTEGRATOR_STATE(state, path, queued_kernel) != 0);
gpu_parallel_active_index_array(num_states, indices, num_indices, ccl_gpu_kernel_lambda_pass);
gpu_parallel_active_index_array(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE,
num_states,
indices,
num_indices,
ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
@@ -354,8 +366,11 @@ ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
{
ccl_gpu_kernel_lambda(INTEGRATOR_STATE(state, path, queued_kernel) == 0);
gpu_parallel_active_index_array(
num_states, indices + indices_offset, num_indices, ccl_gpu_kernel_lambda_pass);
gpu_parallel_active_index_array(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE,
num_states,
indices + indices_offset,
num_indices,
ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
@@ -368,8 +383,11 @@ ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
{
ccl_gpu_kernel_lambda(INTEGRATOR_STATE(state, shadow_path, queued_kernel) == 0);
gpu_parallel_active_index_array(
num_states, indices + indices_offset, num_indices, ccl_gpu_kernel_lambda_pass);
gpu_parallel_active_index_array(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE,
num_states,
indices + indices_offset,
num_indices,
ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
@@ -413,7 +431,11 @@ ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
int num_active_paths);
ccl_gpu_kernel_lambda_pass.num_active_paths = num_active_paths;
gpu_parallel_active_index_array(num_states, indices, num_indices, ccl_gpu_kernel_lambda_pass);
gpu_parallel_active_index_array(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE,
num_states,
indices,
num_indices,
ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
@@ -447,7 +469,11 @@ ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
int num_active_paths);
ccl_gpu_kernel_lambda_pass.num_active_paths = num_active_paths;
gpu_parallel_active_index_array(num_states, indices, num_indices, ccl_gpu_kernel_lambda_pass);
gpu_parallel_active_index_array(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE,
num_states,
indices,
num_indices,
ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix

View File

@@ -56,7 +56,7 @@ void gpu_parallel_active_index_array_impl(const uint num_states,
const uint is_active = (state_index < num_states) ? is_active_op(state_index) : 0;
#else /* !__KERNEL__ONEAPI__ */
# ifndef __KERNEL_METAL__
template<typename IsActiveOp>
template<uint blocksize, typename IsActiveOp>
__device__
# endif
void
@@ -79,10 +79,6 @@ __device__
{
extern ccl_gpu_shared int warp_offset[];
# ifndef __KERNEL_METAL__
const uint blocksize = ccl_gpu_block_dim_x;
# endif
const uint thread_index = ccl_gpu_thread_idx_x;
const uint thread_warp = thread_index % ccl_gpu_warp_size;
@@ -153,7 +149,7 @@ __device__
#ifdef __KERNEL_METAL__
# define gpu_parallel_active_index_array(num_states, indices, num_indices, is_active_op) \
# define gpu_parallel_active_index_array(dummy, num_states, indices, num_indices, is_active_op) \
const uint is_active = (ccl_gpu_global_id_x() < num_states) ? \
is_active_op(ccl_gpu_global_id_x()) : \
0; \
@@ -171,13 +167,15 @@ __device__
simdgroup_offset)
#elif defined(__KERNEL_ONEAPI__)
# define gpu_parallel_active_index_array(num_states, indices, num_indices, is_active_op) \
# define gpu_parallel_active_index_array( \
blocksize, num_states, indices, num_indices, is_active_op) \
gpu_parallel_active_index_array_impl(num_states, indices, num_indices, is_active_op)
#else
# define gpu_parallel_active_index_array(num_states, indices, num_indices, is_active_op) \
gpu_parallel_active_index_array_impl(num_states, indices, num_indices, is_active_op)
# define gpu_parallel_active_index_array( \
blocksize, num_states, indices, num_indices, is_active_op) \
gpu_parallel_active_index_array_impl<blocksize>(num_states, indices, num_indices, is_active_op)
#endif

View File

@@ -157,47 +157,4 @@ ccl_device_inline void film_write_data_passes(KernelGlobals kg,
#endif
}
ccl_device_inline void film_write_data_passes_background(
KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer)
{
#ifdef __PASSES__
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
if (!(path_flag & PATH_RAY_TRANSPARENT_BACKGROUND)) {
return;
}
/* Don't write data passes for paths that were split off for shadow catchers
* to avoid double-counting. */
if (path_flag & PATH_RAY_SHADOW_CATCHER_PASS) {
return;
}
const int flag = kernel_data.film.pass_flag;
if (!(flag & PASS_ANY)) {
return;
}
if (!(path_flag & PATH_RAY_SINGLE_PASS_DONE)) {
ccl_global float *buffer = film_pass_pixel_render_buffer(kg, state, render_buffer);
if (INTEGRATOR_STATE(state, path, sample) == 0) {
if (flag & PASSMASK(DEPTH)) {
film_overwrite_pass_float(buffer + kernel_data.film.pass_depth, 0.0f);
}
if (flag & PASSMASK(OBJECT_ID)) {
film_overwrite_pass_float(buffer + kernel_data.film.pass_object_id, 0.0f);
}
if (flag & PASSMASK(MATERIAL_ID)) {
film_overwrite_pass_float(buffer + kernel_data.film.pass_material_id, 0.0f);
}
if (flag & PASSMASK(POSITION)) {
film_overwrite_pass_float3(buffer + kernel_data.film.pass_position, zero_float3());
}
}
}
#endif
}
CCL_NAMESPACE_END

View File

@@ -11,10 +11,10 @@
#include "kernel/integrator/path_state.h"
#include "kernel/integrator/shadow_catcher.h"
#include "kernel/geom/geom.h"
#include "kernel/light/light.h"
#include "kernel/geom/geom.h"
#include "kernel/bvh/bvh.h"
CCL_NAMESPACE_BEGIN
@@ -387,7 +387,7 @@ ccl_device void integrator_intersect_closest(KernelGlobals kg,
#endif /* __MNEE__ */
/* Light intersection for MIS. */
if (kernel_data.integrator.use_light_mis) {
if (kernel_data.integrator.use_lamp_mis) {
/* NOTE: if we make lights visible to camera rays, we'll need to initialize
* these in the path_state_init. */
const int last_type = INTEGRATOR_STATE(state, isect, type);

View File

@@ -108,6 +108,48 @@ ccl_device_inline float mat22_inverse(const float4 m, ccl_private float4 &m_inve
return det;
}
/* Update light sample */
ccl_device_forceinline void mnee_update_light_sample(KernelGlobals kg,
const float3 P,
ccl_private LightSample *ls)
{
/* correct light sample position/direction and pdf
* NOTE: preserve pdf in area measure */
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->lamp);
if (ls->type == LIGHT_POINT || ls->type == LIGHT_SPOT) {
ls->D = normalize_len(ls->P - P, &ls->t);
ls->Ng = -ls->D;
float2 uv = map_to_sphere(ls->Ng);
ls->u = uv.x;
ls->v = uv.y;
float invarea = klight->spot.invarea;
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
ls->pdf = invarea;
if (ls->type == LIGHT_SPOT) {
/* spot light attenuation */
float3 dir = make_float3(klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]);
ls->eval_fac *= spot_light_attenuation(
dir, klight->spot.spot_angle, klight->spot.spot_smooth, ls->Ng);
}
}
else if (ls->type == LIGHT_AREA) {
float invarea = fabsf(klight->area.invarea);
ls->D = normalize_len(ls->P - P, &ls->t);
ls->pdf = invarea;
if (klight->area.tan_spread > 0.f) {
ls->eval_fac = 0.25f * invarea;
ls->eval_fac *= light_spread_attenuation(
ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread);
}
}
ls->pdf *= kernel_data.integrator.pdf_lights;
}
/* Manifold vertex setup from ray and intersection data */
ccl_device_forceinline void mnee_setup_manifold_vertex(KernelGlobals kg,
ccl_private ManifoldVertex *vtx,
@@ -777,7 +819,7 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg,
/* Update light sample with new position / direct.ion
* and keep pdf in vertex area measure */
light_sample_update_position(kg, ls, vertices[vertex_count - 1].p);
mnee_update_light_sample(kg, vertices[vertex_count - 1].p, ls);
/* Save state path bounce info in case a light path node is used in the refractive interface or
* light shader graph. */

View File

@@ -91,10 +91,7 @@ ccl_device_inline void path_state_init_integrator(KernelGlobals kg,
#endif
}
ccl_device_inline void path_state_next(KernelGlobals kg,
IntegratorState state,
const int label,
const int shader_flag)
ccl_device_inline void path_state_next(KernelGlobals kg, IntegratorState state, int label)
{
uint32_t flag = INTEGRATOR_STATE(state, path, flag);
@@ -123,12 +120,12 @@ ccl_device_inline void path_state_next(KernelGlobals kg,
flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
}
flag &= ~(PATH_RAY_ALL_VISIBILITY | PATH_RAY_MIS_SKIP | PATH_RAY_MIS_HAD_TRANSMISSION);
flag &= ~(PATH_RAY_ALL_VISIBILITY | PATH_RAY_MIS_SKIP);
#ifdef __VOLUME__
if (label & LABEL_VOLUME_SCATTER) {
/* volume scatter */
flag |= PATH_RAY_VOLUME_SCATTER | PATH_RAY_MIS_HAD_TRANSMISSION;
flag |= PATH_RAY_VOLUME_SCATTER;
flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND;
if (!(flag & PATH_RAY_ANY_PASS)) {
flag |= PATH_RAY_VOLUME_PASS;
@@ -191,11 +188,6 @@ ccl_device_inline void path_state_next(KernelGlobals kg,
flag |= PATH_RAY_GLOSSY | PATH_RAY_SINGULAR | PATH_RAY_MIS_SKIP;
}
/* Flag for consistent MIS weights with light tree. */
if (shader_flag & SD_BSDF_HAS_TRANSMISSION) {
flag |= PATH_RAY_MIS_HAD_TRANSMISSION;
}
/* Render pass categories. */
if (!(flag & PATH_RAY_ANY_PASS) && !(flag & PATH_RAY_TRANSPARENT_BACKGROUND)) {
flag |= PATH_RAY_SURFACE_PASS;

View File

@@ -3,7 +3,6 @@
#pragma once
#include "kernel/film/data_passes.h"
#include "kernel/film/light_passes.h"
#include "kernel/integrator/guiding.h"
@@ -69,9 +68,9 @@ ccl_device_inline void integrate_background(KernelGlobals kg,
bool eval_background = true;
float transparent = 0.0f;
int path_flag = INTEGRATOR_STATE(state, path, flag);
const bool is_transparent_background_ray = kernel_data.background.transparent &&
(path_flag & PATH_RAY_TRANSPARENT_BACKGROUND);
(INTEGRATOR_STATE(state, path, flag) &
PATH_RAY_TRANSPARENT_BACKGROUND);
if (is_transparent_background_ray) {
transparent = average(INTEGRATOR_STATE(state, path, throughput));
@@ -86,7 +85,7 @@ ccl_device_inline void integrate_background(KernelGlobals kg,
#ifdef __MNEE__
if (INTEGRATOR_STATE(state, path, mnee) & PATH_MNEE_CULL_LIGHT_CONNECTION) {
if (kernel_data.background.use_mis) {
for (int lamp = 0; lamp < kernel_data.integrator.num_lights; lamp++) {
for (int lamp = 0; lamp < kernel_data.integrator.num_all_lights; lamp++) {
/* This path should have been resolved with mnee, it will
* generate a firefly for small lights since it is improbable. */
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, lamp);
@@ -116,7 +115,14 @@ ccl_device_inline void integrate_background(KernelGlobals kg,
/* Check if background light exists or if we should skip pdf. */
if (!(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_MIS_SKIP) &&
kernel_data.background.use_mis) {
mis_weight = light_sample_mis_weight_forward_background(kg, state, path_flag);
const float3 ray_P = INTEGRATOR_STATE(state, ray, P);
const float3 ray_D = INTEGRATOR_STATE(state, ray, D);
const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf);
/* multiple importance sampling, get background light pdf for ray
* direction, and compute weight with respect to BSDF pdf */
const float pdf = background_light_pdf(kg, ray_P, ray_D);
mis_weight = light_sample_mis_weight_forward(kg, mis_ray_pdf, pdf);
}
guiding_record_background(kg, state, L, mis_weight);
@@ -125,7 +131,6 @@ ccl_device_inline void integrate_background(KernelGlobals kg,
/* Write to render buffer. */
film_write_background(kg, state, L, transparent, is_transparent_background_ray, render_buffer);
film_write_data_passes_background(kg, state, render_buffer);
}
ccl_device_inline void integrate_distant_lights(KernelGlobals kg,
@@ -135,8 +140,8 @@ ccl_device_inline void integrate_distant_lights(KernelGlobals kg,
const float3 ray_D = INTEGRATOR_STATE(state, ray, D);
const float ray_time = INTEGRATOR_STATE(state, ray, time);
LightSample ls ccl_optional_struct_init;
for (int lamp = 0; lamp < kernel_data.integrator.num_lights; lamp++) {
if (distant_light_sample_from_intersection(kg, ray_D, lamp, &ls)) {
for (int lamp = 0; lamp < kernel_data.integrator.num_all_lights; lamp++) {
if (light_sample_from_distant_ray(kg, ray_D, lamp, &ls)) {
/* Use visibility flag to skip lights. */
#ifdef __PASSES__
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
@@ -175,7 +180,10 @@ ccl_device_inline void integrate_distant_lights(KernelGlobals kg,
/* MIS weighting. */
float mis_weight = 1.0f;
if (!(path_flag & PATH_RAY_MIS_SKIP)) {
mis_weight = light_sample_mis_weight_forward_distant(kg, state, path_flag, &ls);
/* multiple importance sampling, get regular light pdf,
* and compute weight with respect to BSDF pdf */
const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf);
mis_weight = light_sample_mis_weight_forward(kg, mis_ray_pdf, ls.pdf);
}
/* Write to render buffer. */

View File

@@ -61,7 +61,10 @@ ccl_device_inline void integrate_light(KernelGlobals kg,
/* MIS weighting. */
float mis_weight = 1.0f;
if (!(path_flag & PATH_RAY_MIS_SKIP)) {
mis_weight = light_sample_mis_weight_forward_lamp(kg, state, path_flag, &ls, ray_P);
/* multiple importance sampling, get regular light pdf,
* and compute weight with respect to BSDF pdf */
const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf);
mis_weight = light_sample_mis_weight_forward(kg, mis_ray_pdf, ls.pdf);
}
/* Write to render buffer. */

View File

@@ -15,6 +15,7 @@
#include "kernel/integrator/surface_shader.h"
#include "kernel/integrator/volume_stack.h"
#include "kernel/light/light.h"
#include "kernel/light/sample.h"
CCL_NAMESPACE_BEGIN
@@ -112,16 +113,20 @@ ccl_device_forceinline void integrate_surface_emission(KernelGlobals kg,
Spectrum L = surface_shader_emission(sd);
float mis_weight = 1.0f;
const bool has_mis = !(path_flag & PATH_RAY_MIS_SKIP) &&
(sd->flag & ((sd->flag & SD_BACKFACING) ? SD_MIS_BACK : SD_MIS_FRONT));
#ifdef __HAIR__
if (has_mis && (sd->type & PRIMITIVE_TRIANGLE))
if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS) &&
(sd->type & PRIMITIVE_TRIANGLE))
#else
if (has_mis)
if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS))
#endif
{
mis_weight = light_sample_mis_weight_forward_surface(kg, state, path_flag, sd);
const float bsdf_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf);
const float t = sd->ray_length;
/* Multiple importance sampling, get triangle light pdf,
* and compute weight with respect to BSDF pdf. */
float pdf = triangle_light_pdf(kg, sd, t);
mis_weight = light_sample_mis_weight_forward(kg, bsdf_pdf, pdf);
}
guiding_record_surface_emission(kg, state, L, mis_weight);
@@ -149,17 +154,8 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
const uint bounce = INTEGRATOR_STATE(state, path, bounce);
const float2 rand_light = path_state_rng_2D(kg, rng_state, PRNG_LIGHT);
if (!light_sample_from_position(kg,
rng_state,
rand_light.x,
rand_light.y,
sd->time,
sd->P,
sd->N,
sd->flag,
bounce,
path_flag,
&ls)) {
if (!light_distribution_sample_from_position(
kg, rand_light.x, rand_light.y, sd->time, sd->P, bounce, path_flag, &ls)) {
return;
}
}
@@ -449,7 +445,7 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
unguided_bsdf_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf));
}
path_state_next(kg, state, label, sd->flag);
path_state_next(kg, state, label);
guiding_record_surface_bounce(kg,
state,

View File

@@ -690,7 +690,6 @@ ccl_device_forceinline void volume_integrate_heterogeneous(
ccl_device_forceinline bool integrate_volume_sample_light(
KernelGlobals kg,
IntegratorState state,
ccl_private const Ray *ccl_restrict ray,
ccl_private const ShaderData *ccl_restrict sd,
ccl_private const RNGState *ccl_restrict rng_state,
ccl_private LightSample *ccl_restrict ls)
@@ -705,16 +704,8 @@ ccl_device_forceinline bool integrate_volume_sample_light(
const uint bounce = INTEGRATOR_STATE(state, path, bounce);
const float2 rand_light = path_state_rng_2D(kg, rng_state, PRNG_LIGHT);
if (!light_sample_from_volume_segment(kg,
rand_light.x,
rand_light.y,
sd->time,
sd->P,
ray->D,
ray->tmax - ray->tmin,
bounce,
path_flag,
ls)) {
if (!light_distribution_sample_from_volume_segment(
kg, rand_light.x, rand_light.y, sd->time, sd->P, bounce, path_flag, ls)) {
return false;
}
@@ -746,32 +737,18 @@ ccl_device_forceinline void integrate_volume_direct_light(
return;
}
/* Sample position on the same light again, now from the shading point where we scattered.
/* Sample position on the same light again, now from the shading
* point where we scattered.
*
* Note that this means we sample the light tree twice when equiangular sampling is used.
* We could consider sampling the light tree just once and use the same light position again.
*
* This would make the PDFs for MIS weights more complicated due to having to account for
* both distance/equiangular and direct/indirect light sampling, but could be more accurate.
* Additionally we could end up behind the light or outside a spot light cone, which might
* waste a sample. Though on the other hand it would be possible to prevent that with
* equiangular sampling restricted to a smaller sub-segment where the light has influence. */
* TODO: decorrelate random numbers and use light_sample_new_position to
* avoid resampling the CDF. */
{
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
const uint bounce = INTEGRATOR_STATE(state, path, bounce);
const float2 rand_light = path_state_rng_2D(kg, rng_state, PRNG_LIGHT);
if (!light_sample_from_position(kg,
rng_state,
rand_light.x,
rand_light.y,
sd->time,
P,
zero_float3(),
SD_BSDF_HAS_TRANSMISSION,
bounce,
path_flag,
ls)) {
if (!light_distribution_sample_from_position(
kg, rand_light.x, rand_light.y, sd->time, P, bounce, path_flag, ls)) {
return;
}
}
@@ -984,7 +961,7 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf(
unguided_phase_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf));
path_state_next(kg, state, label, sd->flag);
path_state_next(kg, state, label);
return true;
}
@@ -1010,7 +987,7 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
const bool need_light_sample = !(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_TERMINATE);
const bool have_equiangular_sample = need_light_sample &&
integrate_volume_sample_light(
kg, state, ray, &sd, &rng_state, &ls) &&
kg, state, &sd, &rng_state, &ls) &&
(ls.t != FLT_MAX);
VolumeSampleMethod direct_sample_method = (have_equiangular_sample) ?

View File

@@ -1,324 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
#pragma once
#include "kernel/light/common.h"
CCL_NAMESPACE_BEGIN
/* Importance sampling.
*
* An Area-Preserving Parametrization for Spherical Rectangles.
* Carlos Urena et al.
*
* NOTE: light_p is modified when sample_coord is true. */
ccl_device_inline float area_light_rect_sample(float3 P,
ccl_private float3 *light_p,
float3 extentu,
float3 extentv,
float randu,
float randv,
bool sample_coord)
{
/* In our name system we're using P for the center, which is o in the paper. */
float3 corner = *light_p - extentu * 0.5f - extentv * 0.5f;
float extentu_len, extentv_len;
/* Compute local reference system R. */
float3 x = normalize_len(extentu, &extentu_len);
float3 y = normalize_len(extentv, &extentv_len);
float3 z = cross(x, y);
/* Compute rectangle coords in local reference system. */
float3 dir = corner - P;
float z0 = dot(dir, z);
/* Flip 'z' to make it point against Q. */
if (z0 > 0.0f) {
z *= -1.0f;
z0 *= -1.0f;
}
float x0 = dot(dir, x);
float y0 = dot(dir, y);
float x1 = x0 + extentu_len;
float y1 = y0 + extentv_len;
/* Compute internal angles (gamma_i). */
float4 diff = make_float4(x0, y1, x1, y0) - make_float4(x1, y0, x0, y1);
float4 nz = make_float4(y0, x1, y1, x0) * diff;
nz = nz / sqrt(z0 * z0 * diff * diff + nz * nz);
float g0 = safe_acosf(-nz.x * nz.y);
float g1 = safe_acosf(-nz.y * nz.z);
float g2 = safe_acosf(-nz.z * nz.w);
float g3 = safe_acosf(-nz.w * nz.x);
/* Compute predefined constants. */
float b0 = nz.x;
float b1 = nz.z;
float b0sq = b0 * b0;
float k = M_2PI_F - g2 - g3;
/* Compute solid angle from internal angles. */
float S = g0 + g1 - k;
if (sample_coord) {
/* Compute cu. */
float au = randu * S + k;
float fu = (cosf(au) * b0 - b1) / sinf(au);
float cu = 1.0f / sqrtf(fu * fu + b0sq) * (fu > 0.0f ? 1.0f : -1.0f);
cu = clamp(cu, -1.0f, 1.0f);
/* Compute xu. */
float xu = -(cu * z0) / max(sqrtf(1.0f - cu * cu), 1e-7f);
xu = clamp(xu, x0, x1);
/* Compute yv. */
float z0sq = z0 * z0;
float y0sq = y0 * y0;
float y1sq = y1 * y1;
float d = sqrtf(xu * xu + z0sq);
float h0 = y0 / sqrtf(d * d + y0sq);
float h1 = y1 / sqrtf(d * d + y1sq);
float hv = h0 + randv * (h1 - h0), hv2 = hv * hv;
float yv = (hv2 < 1.0f - 1e-6f) ? (hv * d) / sqrtf(1.0f - hv2) : y1;
/* Transform (xu, yv, z0) to world coords. */
*light_p = P + xu * x + yv * y + z0 * z;
}
/* return pdf */
if (S != 0.0f)
return 1.0f / S;
else
return 0.0f;
}
/* Light spread. */
ccl_device float area_light_spread_attenuation(const float3 D,
const float3 lightNg,
const float tan_spread,
const float normalize_spread)
{
/* Model a soft-box grid, computing the ratio of light not hidden by the
* slats of the grid at a given angle. (see D10594). */
const float cos_a = -dot(D, lightNg);
const float sin_a = safe_sqrtf(1.0f - sqr(cos_a));
const float tan_a = sin_a / cos_a;
return max((1.0f - (tan_spread * tan_a)) * normalize_spread, 0.0f);
}
/* Compute subset of area light that actually has an influence on the shading point, to
* reduce noise with low spread. */
ccl_device bool area_light_spread_clamp_area_light(const float3 P,
const float3 lightNg,
ccl_private float3 *lightP,
ccl_private float3 *extentu,
ccl_private float3 *extentv,
const float tan_spread)
{
/* Closest point in area light plane and distance to that plane. */
const float3 closest_P = P - dot(lightNg, P - *lightP) * lightNg;
const float t = len(closest_P - P);
/* Radius of circle on area light that actually affects the shading point. */
const float radius = t / tan_spread;
/* TODO: would be faster to store as normalized vector + length, also in area_light_rect_sample.
*/
float len_u, len_v;
const float3 u = normalize_len(*extentu, &len_u);
const float3 v = normalize_len(*extentv, &len_v);
/* Local uv coordinates of closest point. */
const float closest_u = dot(u, closest_P - *lightP);
const float closest_v = dot(v, closest_P - *lightP);
/* Compute rectangle encompassing the circle that affects the shading point,
* clamped to the bounds of the area light. */
const float min_u = max(closest_u - radius, -len_u * 0.5f);
const float max_u = min(closest_u + radius, len_u * 0.5f);
const float min_v = max(closest_v - radius, -len_v * 0.5f);
const float max_v = min(closest_v + radius, len_v * 0.5f);
/* Skip if rectangle is empty. */
if (min_u >= max_u || min_v >= max_v) {
return false;
}
/* Compute new area light center position and axes from rectangle in local
* uv coordinates. */
const float new_center_u = 0.5f * (min_u + max_u);
const float new_center_v = 0.5f * (min_v + max_v);
const float new_len_u = max_u - min_u;
const float new_len_v = max_v - min_v;
*lightP = *lightP + new_center_u * u + new_center_v * v;
*extentu = u * new_len_u;
*extentv = v * new_len_v;
return true;
}
/* Common API. */
template<bool in_volume_segment>
ccl_device_inline bool area_light_sample(const ccl_global KernelLight *klight,
const float randu,
const float randv,
const float3 P,
ccl_private LightSample *ls)
{
ls->P = klight->co;
float3 extentu = klight->area.extentu;
float3 extentv = klight->area.extentv;
float3 Ng = klight->area.dir;
float invarea = fabsf(klight->area.invarea);
bool is_round = (klight->area.invarea < 0.0f);
if (!in_volume_segment) {
if (dot(ls->P - P, Ng) > 0.0f) {
return false;
}
}
float3 inplane;
if (is_round || in_volume_segment) {
inplane = ellipse_sample(extentu * 0.5f, extentv * 0.5f, randu, randv);
ls->P += inplane;
ls->pdf = invarea;
}
else {
inplane = ls->P;
float3 sample_extentu = extentu;
float3 sample_extentv = extentv;
if (!in_volume_segment && klight->area.tan_spread > 0.0f) {
if (!area_light_spread_clamp_area_light(
P, Ng, &ls->P, &sample_extentu, &sample_extentv, klight->area.tan_spread)) {
return false;
}
}
ls->pdf = area_light_rect_sample(
P, &ls->P, sample_extentu, sample_extentv, randu, randv, true);
inplane = ls->P - inplane;
}
const float light_u = dot(inplane, extentu) * (1.0f / dot(extentu, extentu));
const float light_v = dot(inplane, extentv) * (1.0f / dot(extentv, extentv));
/* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */
ls->u = light_v + 0.5f;
ls->v = -light_u - light_v;
ls->Ng = Ng;
ls->D = normalize_len(ls->P - P, &ls->t);
ls->eval_fac = 0.25f * invarea;
if (klight->area.tan_spread > 0.0f) {
/* Area Light spread angle attenuation */
ls->eval_fac *= area_light_spread_attenuation(
ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread);
}
if (is_round) {
ls->pdf *= lamp_light_pdf(Ng, -ls->D, ls->t);
}
return true;
}
ccl_device_forceinline void area_light_update_position(const ccl_global KernelLight *klight,
ccl_private LightSample *ls,
const float3 P)
{
const float invarea = fabsf(klight->area.invarea);
ls->D = normalize_len(ls->P - P, &ls->t);
ls->pdf = invarea;
if (klight->area.tan_spread > 0.f) {
ls->eval_fac = 0.25f * invarea;
ls->eval_fac *= area_light_spread_attenuation(
ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread);
}
}
ccl_device_inline bool area_light_intersect(const ccl_global KernelLight *klight,
const ccl_private Ray *ccl_restrict ray,
ccl_private float *t,
ccl_private float *u,
ccl_private float *v)
{
/* Area light. */
const float invarea = fabsf(klight->area.invarea);
const bool is_round = (klight->area.invarea < 0.0f);
if (invarea == 0.0f) {
return false;
}
const float3 extentu = klight->area.extentu;
const float3 extentv = klight->area.extentv;
const float3 Ng = klight->area.dir;
/* One sided. */
if (dot(ray->D, Ng) >= 0.0f) {
return false;
}
const float3 light_P = klight->co;
float3 P;
return ray_quad_intersect(
ray->P, ray->D, ray->tmin, ray->tmax, light_P, extentu, extentv, Ng, &P, t, u, v, is_round);
}
ccl_device_inline bool area_light_sample_from_intersection(
const ccl_global KernelLight *klight,
ccl_private const Intersection *ccl_restrict isect,
const float3 ray_P,
const float3 ray_D,
ccl_private LightSample *ccl_restrict ls)
{
/* area light */
float invarea = fabsf(klight->area.invarea);
float3 extentu = klight->area.extentu;
float3 extentv = klight->area.extentv;
float3 Ng = klight->area.dir;
float3 light_P = klight->co;
ls->u = isect->u;
ls->v = isect->v;
ls->D = ray_D;
ls->Ng = Ng;
const bool is_round = (klight->area.invarea < 0.0f);
if (is_round) {
ls->pdf = invarea * lamp_light_pdf(Ng, -ray_D, ls->t);
}
else {
float3 sample_extentu = extentu;
float3 sample_extentv = extentv;
if (klight->area.tan_spread > 0.0f) {
if (!area_light_spread_clamp_area_light(
ray_P, Ng, &light_P, &sample_extentu, &sample_extentv, klight->area.tan_spread)) {
return false;
}
}
ls->pdf = area_light_rect_sample(ray_P, &light_P, sample_extentu, sample_extentv, 0, 0, false);
}
ls->eval_fac = 0.25f * invarea;
if (klight->area.tan_spread > 0.0f) {
/* Area Light spread angle attenuation */
ls->eval_fac *= area_light_spread_attenuation(
ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread);
if (ls->eval_fac == 0.0f) {
return false;
}
}
return true;
}
CCL_NAMESPACE_END

View File

@@ -3,7 +3,6 @@
#pragma once
#include "kernel/light/area.h"
#include "kernel/light/common.h"
CCL_NAMESPACE_BEGIN
@@ -131,11 +130,11 @@ ccl_device float background_map_pdf(KernelGlobals kg, float3 direction)
ccl_device_inline bool background_portal_data_fetch_and_check_side(
KernelGlobals kg, float3 P, int index, ccl_private float3 *lightpos, ccl_private float3 *dir)
{
int portal = kernel_data.integrator.portal_offset + index;
int portal = kernel_data.background.portal_offset + index;
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, portal);
*lightpos = klight->co;
*dir = klight->area.dir;
*lightpos = make_float3(klight->co[0], klight->co[1], klight->co[2]);
*dir = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]);
/* Check whether portal is on the right side. */
if (dot(*dir, P - *lightpos) > 1e-4f)
@@ -150,7 +149,7 @@ ccl_device_inline float background_portal_pdf(
float portal_pdf = 0.0f;
int num_possible = 0;
for (int p = 0; p < kernel_data.integrator.num_portals; p++) {
for (int p = 0; p < kernel_data.background.num_portals; p++) {
if (p == ignore_portal)
continue;
@@ -164,10 +163,12 @@ ccl_device_inline float background_portal_pdf(
}
num_possible++;
int portal = kernel_data.integrator.portal_offset + p;
int portal = kernel_data.background.portal_offset + p;
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, portal);
float3 extentu = klight->area.extentu;
float3 extentv = klight->area.extentv;
float3 axisu = make_float3(
klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]);
float3 axisv = make_float3(
klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]);
bool is_round = (klight->area.invarea < 0.0f);
if (!ray_quad_intersect(P,
@@ -175,8 +176,8 @@ ccl_device_inline float background_portal_pdf(
1e-4f,
FLT_MAX,
lightpos,
extentu,
extentv,
axisu,
axisv,
dir,
NULL,
NULL,
@@ -188,10 +189,10 @@ ccl_device_inline float background_portal_pdf(
if (is_round) {
float t;
float3 D = normalize_len(lightpos - P, &t);
portal_pdf += fabsf(klight->area.invarea) * lamp_light_pdf(dir, -D, t);
portal_pdf += fabsf(klight->area.invarea) * lamp_light_pdf(kg, dir, -D, t);
}
else {
portal_pdf += area_light_rect_sample(P, &lightpos, extentu, extentv, 0.0f, 0.0f, false);
portal_pdf += rect_light_sample(P, &lightpos, axisu, axisv, 0.0f, 0.0f, false);
}
}
@@ -206,7 +207,7 @@ ccl_device_inline float background_portal_pdf(
ccl_device int background_num_possible_portals(KernelGlobals kg, float3 P)
{
int num_possible_portals = 0;
for (int p = 0; p < kernel_data.integrator.num_portals; p++) {
for (int p = 0; p < kernel_data.background.num_portals; p++) {
float3 lightpos, dir;
if (background_portal_data_fetch_and_check_side(kg, P, p, &lightpos, &dir))
num_possible_portals++;
@@ -230,7 +231,7 @@ ccl_device float3 background_portal_sample(KernelGlobals kg,
/* TODO(sergey): Some smarter way of finding portal to sample
* is welcome.
*/
for (int p = 0; p < kernel_data.integrator.num_portals; p++) {
for (int p = 0; p < kernel_data.background.num_portals; p++) {
/* Search for the sampled portal. */
float3 lightpos, dir;
if (!background_portal_data_fetch_and_check_side(kg, P, p, &lightpos, &dir))
@@ -238,21 +239,23 @@ ccl_device float3 background_portal_sample(KernelGlobals kg,
if (portal == 0) {
/* p is the portal to be sampled. */
int portal = kernel_data.integrator.portal_offset + p;
int portal = kernel_data.background.portal_offset + p;
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, portal);
float3 extentu = klight->area.extentu;
float3 extentv = klight->area.extentv;
float3 axisu = make_float3(
klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]);
float3 axisv = make_float3(
klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]);
bool is_round = (klight->area.invarea < 0.0f);
float3 D;
if (is_round) {
lightpos += ellipse_sample(extentu * 0.5f, extentv * 0.5f, randu, randv);
lightpos += ellipse_sample(axisu * 0.5f, axisv * 0.5f, randu, randv);
float t;
D = normalize_len(lightpos - P, &t);
*pdf = fabsf(klight->area.invarea) * lamp_light_pdf(dir, -D, t);
*pdf = fabsf(klight->area.invarea) * lamp_light_pdf(kg, dir, -D, t);
}
else {
*pdf = area_light_rect_sample(P, &lightpos, extentu, extentv, randu, randv, true);
*pdf = rect_light_sample(P, &lightpos, axisu, axisv, randu, randv, true);
D = normalize(lightpos - P);
}
@@ -411,7 +414,7 @@ ccl_device float background_light_pdf(KernelGlobals kg, float3 P, float3 directi
float pdf_fac = (portal_method_pdf + sun_method_pdf + map_method_pdf);
if (pdf_fac == 0.0f) {
/* Use uniform as a fallback if we can't use any strategy. */
return 1.0f / M_4PI_F;
return kernel_data.integrator.pdf_lights / M_4PI_F;
}
pdf_fac = 1.0f / pdf_fac;
@@ -427,6 +430,7 @@ ccl_device float background_light_pdf(KernelGlobals kg, float3 P, float3 directi
pdf += background_map_pdf(kg, direction) * map_method_pdf;
}
return pdf;
return pdf * kernel_data.integrator.pdf_lights;
}
CCL_NAMESPACE_END

View File

@@ -7,26 +7,92 @@
CCL_NAMESPACE_BEGIN
/* Light Sample Result */
/* Area light sampling */
typedef struct LightSample {
float3 P; /* position on light, or direction for distant light */
float3 Ng; /* normal on light */
float3 D; /* direction from shading point to light */
float t; /* distance to light (FLT_MAX for distant light) */
float u, v; /* parametric coordinate on primitive */
float pdf; /* pdf for selecting light and point on light */
float pdf_selection; /* pdf for selecting light */
float eval_fac; /* intensity multiplier */
int object; /* object id for triangle/curve lights */
int prim; /* primitive id for triangle/curve lights */
int shader; /* shader id */
int lamp; /* lamp id */
int group; /* lightgroup */
LightType type; /* type of light */
} LightSample;
/* Uses the following paper:
*
* Carlos Urena et al.
* An Area-Preserving Parametrization for Spherical Rectangles.
*
* https://www.solidangle.com/research/egsr2013_spherical_rectangle.pdf
*
* NOTE: light_p is modified when sample_coord is true.
*/
ccl_device_inline float rect_light_sample(float3 P,
ccl_private float3 *light_p,
float3 axisu,
float3 axisv,
float randu,
float randv,
bool sample_coord)
{
/* In our name system we're using P for the center,
* which is o in the paper.
*/
/* Utilities */
float3 corner = *light_p - axisu * 0.5f - axisv * 0.5f;
float axisu_len, axisv_len;
/* Compute local reference system R. */
float3 x = normalize_len(axisu, &axisu_len);
float3 y = normalize_len(axisv, &axisv_len);
float3 z = cross(x, y);
/* Compute rectangle coords in local reference system. */
float3 dir = corner - P;
float z0 = dot(dir, z);
/* Flip 'z' to make it point against Q. */
if (z0 > 0.0f) {
z *= -1.0f;
z0 *= -1.0f;
}
float x0 = dot(dir, x);
float y0 = dot(dir, y);
float x1 = x0 + axisu_len;
float y1 = y0 + axisv_len;
/* Compute internal angles (gamma_i). */
float4 diff = make_float4(x0, y1, x1, y0) - make_float4(x1, y0, x0, y1);
float4 nz = make_float4(y0, x1, y1, x0) * diff;
nz = nz / sqrt(z0 * z0 * diff * diff + nz * nz);
float g0 = safe_acosf(-nz.x * nz.y);
float g1 = safe_acosf(-nz.y * nz.z);
float g2 = safe_acosf(-nz.z * nz.w);
float g3 = safe_acosf(-nz.w * nz.x);
/* Compute predefined constants. */
float b0 = nz.x;
float b1 = nz.z;
float b0sq = b0 * b0;
float k = M_2PI_F - g2 - g3;
/* Compute solid angle from internal angles. */
float S = g0 + g1 - k;
if (sample_coord) {
/* Compute cu. */
float au = randu * S + k;
float fu = (cosf(au) * b0 - b1) / sinf(au);
float cu = 1.0f / sqrtf(fu * fu + b0sq) * (fu > 0.0f ? 1.0f : -1.0f);
cu = clamp(cu, -1.0f, 1.0f);
/* Compute xu. */
float xu = -(cu * z0) / max(sqrtf(1.0f - cu * cu), 1e-7f);
xu = clamp(xu, x0, x1);
/* Compute yv. */
float z0sq = z0 * z0;
float y0sq = y0 * y0;
float y1sq = y1 * y1;
float d = sqrtf(xu * xu + z0sq);
float h0 = y0 / sqrtf(d * d + y0sq);
float h1 = y1 / sqrtf(d * d + y1sq);
float hv = h0 + randv * (h1 - h0), hv2 = hv * hv;
float yv = (hv2 < 1.0f - 1e-6f) ? (hv * d) / sqrtf(1.0f - hv2) : y1;
/* Transform (xu, yv, z0) to world coords. */
*light_p = P + xu * x + yv * y + z0 * z;
}
/* return pdf */
if (S != 0.0f)
return 1.0f / S;
else
return 0.0f;
}
ccl_device_inline float3 ellipse_sample(float3 ru, float3 rv, float randu, float randv)
{
@@ -43,7 +109,99 @@ ccl_device float3 disk_light_sample(float3 v, float randu, float randv)
return ellipse_sample(ru, rv, randu, randv);
}
ccl_device float lamp_light_pdf(const float3 Ng, const float3 I, float t)
ccl_device float3 distant_light_sample(float3 D, float radius, float randu, float randv)
{
return normalize(D + disk_light_sample(D, randu, randv) * radius);
}
ccl_device float3
sphere_light_sample(float3 P, float3 center, float radius, float randu, float randv)
{
return disk_light_sample(normalize(P - center), randu, randv) * radius;
}
ccl_device float spot_light_attenuation(float3 dir, float spot_angle, float spot_smooth, float3 N)
{
float attenuation = dot(dir, N);
if (attenuation <= spot_angle) {
attenuation = 0.0f;
}
else {
float t = attenuation - spot_angle;
if (t < spot_smooth && spot_smooth != 0.0f)
attenuation *= smoothstepf(t / spot_smooth);
}
return attenuation;
}
ccl_device float light_spread_attenuation(const float3 D,
const float3 lightNg,
const float tan_spread,
const float normalize_spread)
{
/* Model a soft-box grid, computing the ratio of light not hidden by the
* slats of the grid at a given angle. (see D10594). */
const float cos_a = -dot(D, lightNg);
const float sin_a = safe_sqrtf(1.0f - sqr(cos_a));
const float tan_a = sin_a / cos_a;
return max((1.0f - (tan_spread * tan_a)) * normalize_spread, 0.0f);
}
/* Compute subset of area light that actually has an influence on the shading point, to
* reduce noise with low spread. */
ccl_device bool light_spread_clamp_area_light(const float3 P,
const float3 lightNg,
ccl_private float3 *lightP,
ccl_private float3 *axisu,
ccl_private float3 *axisv,
const float tan_spread)
{
/* Closest point in area light plane and distance to that plane. */
const float3 closest_P = P - dot(lightNg, P - *lightP) * lightNg;
const float t = len(closest_P - P);
/* Radius of circle on area light that actually affects the shading point. */
const float radius = t / tan_spread;
/* TODO: would be faster to store as normalized vector + length, also in rect_light_sample. */
float len_u, len_v;
const float3 u = normalize_len(*axisu, &len_u);
const float3 v = normalize_len(*axisv, &len_v);
/* Local uv coordinates of closest point. */
const float closest_u = dot(u, closest_P - *lightP);
const float closest_v = dot(v, closest_P - *lightP);
/* Compute rectangle encompassing the circle that affects the shading point,
* clamped to the bounds of the area light. */
const float min_u = max(closest_u - radius, -len_u * 0.5f);
const float max_u = min(closest_u + radius, len_u * 0.5f);
const float min_v = max(closest_v - radius, -len_v * 0.5f);
const float max_v = min(closest_v + radius, len_v * 0.5f);
/* Skip if rectangle is empty. */
if (min_u >= max_u || min_v >= max_v) {
return false;
}
/* Compute new area light center position and axes from rectangle in local
* uv coordinates. */
const float new_center_u = 0.5f * (min_u + max_u);
const float new_center_v = 0.5f * (min_v + max_v);
const float new_len_u = max_u - min_u;
const float new_len_v = max_v - min_v;
*lightP = *lightP + new_center_u * u + new_center_v * v;
*axisu = u * new_len_u;
*axisv = v * new_len_v;
return true;
}
ccl_device float lamp_light_pdf(KernelGlobals kg, const float3 Ng, const float3 I, float t)
{
float cos_pi = dot(Ng, I);

View File

@@ -1,110 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
#pragma once
#include "kernel/geom/geom.h"
#include "kernel/light/common.h"
CCL_NAMESPACE_BEGIN
ccl_device_inline bool distant_light_sample(const ccl_global KernelLight *klight,
const float randu,
const float randv,
ccl_private LightSample *ls)
{
/* distant light */
float3 lightD = klight->co;
float3 D = lightD;
float radius = klight->distant.radius;
float invarea = klight->distant.invarea;
if (radius > 0.0f) {
D = normalize(D + disk_light_sample(D, randu, randv) * radius);
}
ls->P = D;
ls->Ng = D;
ls->D = -D;
ls->t = FLT_MAX;
float costheta = dot(lightD, D);
ls->pdf = invarea / (costheta * costheta * costheta);
ls->eval_fac = ls->pdf;
return true;
}
ccl_device bool distant_light_sample_from_intersection(KernelGlobals kg,
const float3 ray_D,
const int lamp,
ccl_private LightSample *ccl_restrict ls)
{
ccl_global const KernelLight *klight = &kernel_data_fetch(lights, lamp);
const int shader = klight->shader_id;
const float radius = klight->distant.radius;
const LightType type = (LightType)klight->type;
if (type != LIGHT_DISTANT) {
return false;
}
if (!(shader & SHADER_USE_MIS)) {
return false;
}
if (radius == 0.0f) {
return false;
}
/* a distant light is infinitely far away, but equivalent to a disk
* shaped light exactly 1 unit away from the current shading point.
*
* radius t^2/cos(theta)
* <----------> t = sqrt(1^2 + tan(theta)^2)
* tan(th) area = radius*radius*pi
* <----->
* \ | (1 + tan(theta)^2)/cos(theta)
* \ | (1 + tan(acos(cos(theta)))^2)/cos(theta)
* t \th| 1 simplifies to
* \-| 1/(cos(theta)^3)
* \| magic!
* P
*/
float3 lightD = klight->co;
float costheta = dot(-lightD, ray_D);
float cosangle = klight->distant.cosangle;
/* Workaround to prevent a hang in the classroom scene with AMD HIP drivers 22.10,
* Remove when a compiler fix is available. */
#ifdef __HIP__
ls->shader = klight->shader_id;
#endif
if (costheta < cosangle)
return false;
ls->type = type;
#ifndef __HIP__
ls->shader = klight->shader_id;
#endif
ls->object = PRIM_NONE;
ls->prim = PRIM_NONE;
ls->lamp = lamp;
/* todo: missing texture coordinates */
ls->u = 0.0f;
ls->v = 0.0f;
ls->t = FLT_MAX;
ls->P = -ray_D;
ls->Ng = -ray_D;
ls->D = ray_D;
ls->group = lamp_lightgroup(kg, lamp);
/* compute pdf */
float invarea = klight->distant.invarea;
ls->pdf = invarea / (costheta * costheta * costheta);
ls->eval_fac = ls->pdf;
return true;
}
CCL_NAMESPACE_END

View File

@@ -1,103 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
#pragma once
#include "kernel/light/light.h"
#include "kernel/light/triangle.h"
CCL_NAMESPACE_BEGIN
/* Simple CDF based sampling over all lights in the scene, without taking into
* account shading position or normal. */
ccl_device int light_distribution_sample(KernelGlobals kg, ccl_private float *randu)
{
/* 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
* arbitrary shaders. */
int first = 0;
int len = kernel_data.integrator.num_distribution + 1;
float r = *randu;
do {
int half_len = len >> 1;
int middle = first + half_len;
if (r < kernel_data_fetch(light_distribution, middle).totarea) {
len = half_len;
}
else {
first = middle + 1;
len = len - half_len - 1;
}
} while (len > 0);
/* Clamping should not be needed but float rounding errors seem to
* make this fail on rare occasions. */
int index = clamp(first - 1, 0, kernel_data.integrator.num_distribution - 1);
/* Rescale to reuse random number. this helps the 2D samples within
* each area light be stratified as well. */
float distr_min = kernel_data_fetch(light_distribution, index).totarea;
float distr_max = kernel_data_fetch(light_distribution, index + 1).totarea;
*randu = (r - distr_min) / (distr_max - distr_min);
return index;
}
template<bool in_volume_segment>
ccl_device_noinline bool light_distribution_sample(KernelGlobals kg,
float randu,
const float randv,
const float time,
const float3 P,
const int bounce,
const uint32_t path_flag,
ccl_private LightSample *ls)
{
/* Sample light index from distribution. */
const int index = light_distribution_sample(kg, &randu);
ccl_global const KernelLightDistribution *kdistribution = &kernel_data_fetch(light_distribution,
index);
const int prim = kdistribution->prim;
if (prim >= 0) {
/* Mesh light. */
const int object = kdistribution->mesh_light.object_id;
/* Exclude synthetic meshes from shadow catcher pass. */
if ((path_flag & PATH_RAY_SHADOW_CATCHER_PASS) &&
!(kernel_data_fetch(object_flag, object) & SD_OBJECT_SHADOW_CATCHER)) {
return false;
}
const int shader_flag = kdistribution->mesh_light.shader_flag;
triangle_light_sample<in_volume_segment>(kg, prim, object, randu, randv, time, ls, P);
ls->shader |= shader_flag;
return (ls->pdf > 0.0f);
}
const int lamp = -prim - 1;
if (UNLIKELY(light_select_reached_max_bounces(kg, lamp, bounce))) {
return false;
}
if (!light_sample<in_volume_segment>(kg, lamp, randu, randv, P, path_flag, ls)) {
return false;
}
ls->pdf_selection = kernel_data.integrator.distribution_pdf_lights;
ls->pdf *= ls->pdf_selection;
return true;
}
ccl_device_inline float light_distribution_pdf_lamp(KernelGlobals kg)
{
return kernel_data.integrator.distribution_pdf_lights;
}
CCL_NAMESPACE_END

View File

@@ -3,18 +3,31 @@
#pragma once
#include "kernel/light/area.h"
#include "kernel/geom/geom.h"
#include "kernel/light/background.h"
#include "kernel/light/distant.h"
#include "kernel/light/point.h"
#include "kernel/light/spot.h"
#include "kernel/light/triangle.h"
#include "kernel/sample/mapping.h"
CCL_NAMESPACE_BEGIN
/* Sample point on an individual light. */
/* Light Sample result */
typedef struct LightSample {
float3 P; /* position on light, or direction for distant light */
float3 Ng; /* normal on light */
float3 D; /* direction from shading point to light */
float t; /* distance to light (FLT_MAX for distant light) */
float u, v; /* parametric coordinate on primitive */
float pdf; /* light sampling probability density function */
float eval_fac; /* intensity multiplier */
int object; /* object id for triangle/curve lights */
int prim; /* primitive id for triangle/curve lights */
int shader; /* shader id */
int lamp; /* lamp id */
int group; /* lightgroup */
LightType type; /* type of light */
} LightSample;
/* Regular Light */
template<bool in_volume_segment>
ccl_device_inline bool light_sample(KernelGlobals kg,
@@ -50,15 +63,28 @@ ccl_device_inline bool light_sample(KernelGlobals kg,
ls->Ng = zero_float3();
ls->D = zero_float3();
ls->pdf = 1.0f;
ls->eval_fac = 0.0f;
ls->t = FLT_MAX;
return true;
}
if (type == LIGHT_DISTANT) {
if (!distant_light_sample(klight, randu, randv, ls)) {
return false;
}
/* distant light */
float3 lightD = make_float3(klight->co[0], klight->co[1], klight->co[2]);
float3 D = lightD;
float radius = klight->distant.radius;
float invarea = klight->distant.invarea;
if (radius > 0.0f)
D = distant_light_sample(D, radius, randu, randv);
ls->P = D;
ls->Ng = D;
ls->D = -D;
ls->t = FLT_MAX;
float costheta = dot(lightD, D);
ls->pdf = invarea / (costheta * costheta * costheta);
ls->eval_fac = ls->pdf;
}
else if (type == LIGHT_BACKGROUND) {
/* infinite area light (e.g. light dome or env light) */
@@ -70,28 +96,139 @@ ccl_device_inline bool light_sample(KernelGlobals kg,
ls->t = FLT_MAX;
ls->eval_fac = 1.0f;
}
else if (type == LIGHT_SPOT) {
if (!spot_light_sample<in_volume_segment>(klight, randu, randv, P, ls)) {
return false;
}
}
else if (type == LIGHT_POINT) {
if (!point_light_sample<in_volume_segment>(klight, randu, randv, P, ls)) {
return false;
}
}
else {
/* area light */
if (!area_light_sample<in_volume_segment>(klight, randu, randv, P, ls)) {
return false;
ls->P = make_float3(klight->co[0], klight->co[1], klight->co[2]);
if (type == LIGHT_SPOT) {
const float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]);
const float radius = klight->spot.radius;
const float3 dir = make_float3(
klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]);
/* disk oriented normal */
const float3 lightN = normalize(P - center);
ls->P = center;
if (radius > 0.0f)
/* disk light */
ls->P += disk_light_sample(lightN, randu, randv) * radius;
const float invarea = klight->spot.invarea;
ls->pdf = invarea;
ls->D = normalize_len(ls->P - P, &ls->t);
/* we set the light normal to the outgoing direction to support texturing */
ls->Ng = -ls->D;
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
/* spot light attenuation */
ls->eval_fac *= spot_light_attenuation(
dir, klight->spot.spot_angle, klight->spot.spot_smooth, -ls->D);
if (!in_volume_segment && ls->eval_fac == 0.0f) {
return false;
}
float2 uv = map_to_sphere(ls->Ng);
ls->u = uv.x;
ls->v = uv.y;
ls->pdf *= lamp_light_pdf(kg, lightN, -ls->D, ls->t);
}
else if (type == LIGHT_POINT) {
float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]);
float radius = klight->spot.radius;
/* disk oriented normal */
const float3 lightN = normalize(P - center);
ls->P = center;
if (radius > 0.0f) {
ls->P += disk_light_sample(lightN, randu, randv) * radius;
}
ls->pdf = klight->spot.invarea;
ls->D = normalize_len(ls->P - P, &ls->t);
/* we set the light normal to the outgoing direction to support texturing */
ls->Ng = -ls->D;
ls->eval_fac = M_1_PI_F * 0.25f * klight->spot.invarea;
if (!in_volume_segment && ls->eval_fac == 0.0f) {
return false;
}
float2 uv = map_to_sphere(ls->Ng);
ls->u = uv.x;
ls->v = uv.y;
ls->pdf *= lamp_light_pdf(kg, lightN, -ls->D, ls->t);
}
else {
/* area light */
float3 axisu = make_float3(
klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]);
float3 axisv = make_float3(
klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]);
float3 Ng = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]);
float invarea = fabsf(klight->area.invarea);
bool is_round = (klight->area.invarea < 0.0f);
if (!in_volume_segment) {
if (dot(ls->P - P, Ng) > 0.0f) {
return false;
}
}
float3 inplane;
if (is_round || in_volume_segment) {
inplane = ellipse_sample(axisu * 0.5f, axisv * 0.5f, randu, randv);
ls->P += inplane;
ls->pdf = invarea;
}
else {
inplane = ls->P;
float3 sample_axisu = axisu;
float3 sample_axisv = axisv;
if (!in_volume_segment && klight->area.tan_spread > 0.0f) {
if (!light_spread_clamp_area_light(
P, Ng, &ls->P, &sample_axisu, &sample_axisv, klight->area.tan_spread)) {
return false;
}
}
ls->pdf = rect_light_sample(P, &ls->P, sample_axisu, sample_axisv, randu, randv, true);
inplane = ls->P - inplane;
}
const float light_u = dot(inplane, axisu) * (1.0f / dot(axisu, axisu));
const float light_v = dot(inplane, axisv) * (1.0f / dot(axisv, axisv));
/* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */
ls->u = light_v + 0.5f;
ls->v = -light_u - light_v;
ls->Ng = Ng;
ls->D = normalize_len(ls->P - P, &ls->t);
ls->eval_fac = 0.25f * invarea;
if (klight->area.tan_spread > 0.0f) {
/* Area Light spread angle attenuation */
ls->eval_fac *= light_spread_attenuation(
ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread);
}
if (is_round) {
ls->pdf *= lamp_light_pdf(kg, Ng, -ls->D, ls->t);
}
}
}
ls->pdf *= kernel_data.integrator.pdf_lights;
return in_volume_segment || (ls->pdf > 0.0f);
}
/* Intersect ray with individual light. */
ccl_device bool lights_intersect(KernelGlobals kg,
IntegratorState state,
ccl_private const Ray *ccl_restrict ray,
@@ -101,7 +238,7 @@ ccl_device bool lights_intersect(KernelGlobals kg,
const int last_type,
const uint32_t path_flag)
{
for (int lamp = 0; lamp < kernel_data.integrator.num_lights; lamp++) {
for (int lamp = 0; lamp < kernel_data.integrator.num_all_lights; lamp++) {
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, lamp);
if (path_flag & PATH_RAY_CAMERA) {
@@ -134,17 +271,76 @@ ccl_device bool lights_intersect(KernelGlobals kg,
float t = 0.0f, u = 0.0f, v = 0.0f;
if (type == LIGHT_SPOT) {
if (!spot_light_intersect(klight, ray, &t)) {
/* Spot/Disk light. */
const float3 lightP = make_float3(klight->co[0], klight->co[1], klight->co[2]);
const float radius = klight->spot.radius;
if (radius == 0.0f) {
continue;
}
/* disk oriented normal */
const float3 lightN = normalize(ray->P - lightP);
/* One sided. */
if (dot(ray->D, lightN) >= 0.0f) {
continue;
}
float3 P;
if (!ray_disk_intersect(
ray->P, ray->D, ray->tmin, ray->tmax, lightP, lightN, radius, &P, &t)) {
continue;
}
}
else if (type == LIGHT_POINT) {
if (!point_light_intersect(klight, ray, &t)) {
/* Sphere light (aka, aligned disk light). */
const float3 lightP = make_float3(klight->co[0], klight->co[1], klight->co[2]);
const float radius = klight->spot.radius;
if (radius == 0.0f) {
continue;
}
/* disk oriented normal */
const float3 lightN = normalize(ray->P - lightP);
float3 P;
if (!ray_disk_intersect(
ray->P, ray->D, ray->tmin, ray->tmax, lightP, lightN, radius, &P, &t)) {
continue;
}
}
else if (type == LIGHT_AREA) {
if (!area_light_intersect(klight, ray, &t, &u, &v)) {
/* Area light. */
const float invarea = fabsf(klight->area.invarea);
const bool is_round = (klight->area.invarea < 0.0f);
if (invarea == 0.0f) {
continue;
}
const float3 axisu = make_float3(
klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]);
const float3 axisv = make_float3(
klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]);
const float3 Ng = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]);
/* One sided. */
if (dot(ray->D, Ng) >= 0.0f) {
continue;
}
const float3 light_P = make_float3(klight->co[0], klight->co[1], klight->co[2]);
float3 P;
if (!ray_quad_intersect(ray->P,
ray->D,
ray->tmin,
ray->tmax,
light_P,
axisu,
axisv,
Ng,
&P,
&t,
&u,
&v,
is_round)) {
continue;
}
}
@@ -166,7 +362,78 @@ ccl_device bool lights_intersect(KernelGlobals kg,
return isect->prim != PRIM_NONE;
}
/* Setup light sample from intersection. */
ccl_device bool light_sample_from_distant_ray(KernelGlobals kg,
const float3 ray_D,
const int lamp,
ccl_private LightSample *ccl_restrict ls)
{
ccl_global const KernelLight *klight = &kernel_data_fetch(lights, lamp);
const int shader = klight->shader_id;
const float radius = klight->distant.radius;
const LightType type = (LightType)klight->type;
if (type != LIGHT_DISTANT) {
return false;
}
if (!(shader & SHADER_USE_MIS)) {
return false;
}
if (radius == 0.0f) {
return false;
}
/* a distant light is infinitely far away, but equivalent to a disk
* shaped light exactly 1 unit away from the current shading point.
*
* radius t^2/cos(theta)
* <----------> t = sqrt(1^2 + tan(theta)^2)
* tan(th) area = radius*radius*pi
* <----->
* \ | (1 + tan(theta)^2)/cos(theta)
* \ | (1 + tan(acos(cos(theta)))^2)/cos(theta)
* t \th| 1 simplifies to
* \-| 1/(cos(theta)^3)
* \| magic!
* P
*/
float3 lightD = make_float3(klight->co[0], klight->co[1], klight->co[2]);
float costheta = dot(-lightD, ray_D);
float cosangle = klight->distant.cosangle;
/* Workaround to prevent a hang in the classroom scene with AMD HIP drivers 22.10,
* Remove when a compiler fix is available. */
#ifdef __HIP__
ls->shader = klight->shader_id;
#endif
if (costheta < cosangle)
return false;
ls->type = type;
#ifndef __HIP__
ls->shader = klight->shader_id;
#endif
ls->object = PRIM_NONE;
ls->prim = PRIM_NONE;
ls->lamp = lamp;
/* todo: missing texture coordinates */
ls->u = 0.0f;
ls->v = 0.0f;
ls->t = FLT_MAX;
ls->P = -ray_D;
ls->Ng = -ray_D;
ls->D = ray_D;
ls->group = lamp_lightgroup(kg, lamp);
/* compute pdf */
float invarea = klight->distant.invarea;
ls->pdf = invarea / (costheta * costheta * costheta);
ls->eval_fac = ls->pdf;
ls->pdf *= kernel_data.integrator.pdf_lights;
return true;
}
ccl_device bool light_sample_from_intersection(KernelGlobals kg,
ccl_private const Intersection *ccl_restrict isect,
@@ -189,18 +456,102 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg,
ls->group = lamp_lightgroup(kg, lamp);
if (type == LIGHT_SPOT) {
if (!spot_light_sample_from_intersection(klight, isect, ray_P, ray_D, ls)) {
const float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]);
const float3 dir = make_float3(klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]);
/* the normal of the oriented disk */
const float3 lightN = normalize(ray_P - center);
/* We set the light normal to the outgoing direction to support texturing. */
ls->Ng = -ls->D;
float invarea = klight->spot.invarea;
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
ls->pdf = invarea;
/* spot light attenuation */
ls->eval_fac *= spot_light_attenuation(
dir, klight->spot.spot_angle, klight->spot.spot_smooth, -ls->D);
if (ls->eval_fac == 0.0f) {
return false;
}
float2 uv = map_to_sphere(ls->Ng);
ls->u = uv.x;
ls->v = uv.y;
/* compute pdf */
if (ls->t != FLT_MAX)
ls->pdf *= lamp_light_pdf(kg, lightN, -ls->D, ls->t);
else
ls->pdf = 0.f;
}
else if (type == LIGHT_POINT) {
if (!point_light_sample_from_intersection(klight, isect, ray_P, ray_D, ls)) {
const float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]);
const float3 lighN = normalize(ray_P - center);
/* We set the light normal to the outgoing direction to support texturing. */
ls->Ng = -ls->D;
float invarea = klight->spot.invarea;
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
ls->pdf = invarea;
if (ls->eval_fac == 0.0f) {
return false;
}
float2 uv = map_to_sphere(ls->Ng);
ls->u = uv.x;
ls->v = uv.y;
/* compute pdf */
if (ls->t != FLT_MAX)
ls->pdf *= lamp_light_pdf(kg, lighN, -ls->D, ls->t);
else
ls->pdf = 0.f;
}
else if (type == LIGHT_AREA) {
if (!area_light_sample_from_intersection(klight, isect, ray_P, ray_D, ls)) {
return false;
/* area light */
float invarea = fabsf(klight->area.invarea);
float3 axisu = make_float3(
klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]);
float3 axisv = make_float3(
klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]);
float3 Ng = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]);
float3 light_P = make_float3(klight->co[0], klight->co[1], klight->co[2]);
ls->u = isect->u;
ls->v = isect->v;
ls->D = ray_D;
ls->Ng = Ng;
const bool is_round = (klight->area.invarea < 0.0f);
if (is_round) {
ls->pdf = invarea * lamp_light_pdf(kg, Ng, -ray_D, ls->t);
}
else {
float3 sample_axisu = axisu;
float3 sample_axisv = axisv;
if (klight->area.tan_spread > 0.0f) {
if (!light_spread_clamp_area_light(
ray_P, Ng, &light_P, &sample_axisu, &sample_axisv, klight->area.tan_spread)) {
return false;
}
}
ls->pdf = rect_light_sample(ray_P, &light_P, sample_axisu, sample_axisv, 0, 0, false);
}
ls->eval_fac = 0.25f * invarea;
if (klight->area.tan_spread > 0.0f) {
/* Area Light spread angle attenuation */
ls->eval_fac *= light_spread_attenuation(
ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread);
if (ls->eval_fac == 0.0f) {
return false;
}
}
}
else {
@@ -208,33 +559,411 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg,
return false;
}
ls->pdf *= kernel_data.integrator.pdf_lights;
return true;
}
/* Update light sample for changed new position, for MNEE. */
/* Triangle Light */
ccl_device_forceinline void light_update_position(KernelGlobals kg,
ccl_private LightSample *ls,
const float3 P)
/* returns true if the triangle is has motion blur or an instancing transform applied */
ccl_device_inline bool triangle_world_space_vertices(
KernelGlobals kg, int object, int prim, float time, float3 V[3])
{
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->lamp);
bool has_motion = false;
const int object_flag = kernel_data_fetch(object_flag, object);
if (ls->type == LIGHT_POINT) {
point_light_update_position(klight, ls, P);
if (object_flag & SD_OBJECT_HAS_VERTEX_MOTION && time >= 0.0f) {
motion_triangle_vertices(kg, object, prim, time, V);
has_motion = true;
}
else if (ls->type == LIGHT_SPOT) {
spot_light_update_position(klight, ls, P);
else {
triangle_vertices(kg, prim, V);
}
else if (ls->type == LIGHT_AREA) {
area_light_update_position(klight, ls, P);
if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
#ifdef __OBJECT_MOTION__
float object_time = (time >= 0.0f) ? time : 0.5f;
Transform tfm = object_fetch_transform_motion_test(kg, object, object_time, NULL);
#else
Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
#endif
V[0] = transform_point(&tfm, V[0]);
V[1] = transform_point(&tfm, V[1]);
V[2] = transform_point(&tfm, V[2]);
has_motion = true;
}
return has_motion;
}
ccl_device_inline float triangle_light_pdf_area(KernelGlobals kg,
const float3 Ng,
const float3 I,
float t)
{
float pdf = kernel_data.integrator.pdf_triangles;
float cos_pi = fabsf(dot(Ng, I));
if (cos_pi == 0.0f)
return 0.0f;
return t * t * pdf / cos_pi;
}
ccl_device_forceinline float triangle_light_pdf(KernelGlobals kg,
ccl_private const ShaderData *sd,
float t)
{
/* A naive heuristic to decide between costly solid angle sampling
* and simple area sampling, comparing the distance to the triangle plane
* to the length of the edges of the triangle. */
float3 V[3];
bool has_motion = triangle_world_space_vertices(kg, sd->object, sd->prim, sd->time, V);
const float3 e0 = V[1] - V[0];
const float3 e1 = V[2] - V[0];
const float3 e2 = V[2] - V[1];
const float longest_edge_squared = max(len_squared(e0), max(len_squared(e1), len_squared(e2)));
const float3 N = cross(e0, e1);
const float distance_to_plane = fabsf(dot(N, sd->I * t)) / dot(N, N);
if (longest_edge_squared > distance_to_plane * distance_to_plane) {
/* sd contains the point on the light source
* calculate Px, the point that we're shading */
const float3 Px = sd->P + sd->I * t;
const float3 v0_p = V[0] - Px;
const float3 v1_p = V[1] - Px;
const float3 v2_p = V[2] - Px;
const float3 u01 = safe_normalize(cross(v0_p, v1_p));
const float3 u02 = safe_normalize(cross(v0_p, v2_p));
const float3 u12 = safe_normalize(cross(v1_p, v2_p));
const float alpha = fast_acosf(dot(u02, u01));
const float beta = fast_acosf(-dot(u01, u12));
const float gamma = fast_acosf(dot(u02, u12));
const float solid_angle = alpha + beta + gamma - M_PI_F;
/* pdf_triangles is calculated over triangle area, but we're not sampling over its area */
if (UNLIKELY(solid_angle == 0.0f)) {
return 0.0f;
}
else {
float area = 1.0f;
if (has_motion) {
/* get the center frame vertices, this is what the PDF was calculated from */
triangle_world_space_vertices(kg, sd->object, sd->prim, -1.0f, V);
area = triangle_area(V[0], V[1], V[2]);
}
else {
area = 0.5f * len(N);
}
const float pdf = area * kernel_data.integrator.pdf_triangles;
return pdf / solid_angle;
}
}
else {
float pdf = triangle_light_pdf_area(kg, sd->Ng, sd->I, t);
if (has_motion) {
const float area = 0.5f * len(N);
if (UNLIKELY(area == 0.0f)) {
return 0.0f;
}
/* scale the PDF.
* area = the area the sample was taken from
* area_pre = the are from which pdf_triangles was calculated from */
triangle_world_space_vertices(kg, sd->object, sd->prim, -1.0f, V);
const float area_pre = triangle_area(V[0], V[1], V[2]);
pdf = pdf * area_pre / area;
}
return pdf;
}
}
/* Light info. */
template<bool in_volume_segment>
ccl_device_forceinline void triangle_light_sample(KernelGlobals kg,
int prim,
int object,
float randu,
float randv,
float time,
ccl_private LightSample *ls,
const float3 P)
{
/* A naive heuristic to decide between costly solid angle sampling
* and simple area sampling, comparing the distance to the triangle plane
* to the length of the edges of the triangle. */
float3 V[3];
bool has_motion = triangle_world_space_vertices(kg, object, prim, time, V);
const float3 e0 = V[1] - V[0];
const float3 e1 = V[2] - V[0];
const float3 e2 = V[2] - V[1];
const float longest_edge_squared = max(len_squared(e0), max(len_squared(e1), len_squared(e2)));
const float3 N0 = cross(e0, e1);
float Nl = 0.0f;
ls->Ng = safe_normalize_len(N0, &Nl);
float area = 0.5f * Nl;
/* flip normal if necessary */
const int object_flag = kernel_data_fetch(object_flag, object);
if (object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) {
ls->Ng = -ls->Ng;
}
ls->eval_fac = 1.0f;
ls->shader = kernel_data_fetch(tri_shader, prim);
ls->object = object;
ls->prim = prim;
ls->lamp = LAMP_NONE;
ls->shader |= SHADER_USE_MIS;
ls->type = LIGHT_TRIANGLE;
ls->group = object_lightgroup(kg, object);
float distance_to_plane = fabsf(dot(N0, V[0] - P) / dot(N0, N0));
if (!in_volume_segment && (longest_edge_squared > distance_to_plane * distance_to_plane)) {
/* see James Arvo, "Stratified Sampling of Spherical Triangles"
* http://www.graphics.cornell.edu/pubs/1995/Arv95c.pdf */
/* project the triangle to the unit sphere
* and calculate its edges and angles */
const float3 v0_p = V[0] - P;
const float3 v1_p = V[1] - P;
const float3 v2_p = V[2] - P;
const float3 u01 = safe_normalize(cross(v0_p, v1_p));
const float3 u02 = safe_normalize(cross(v0_p, v2_p));
const float3 u12 = safe_normalize(cross(v1_p, v2_p));
const float3 A = safe_normalize(v0_p);
const float3 B = safe_normalize(v1_p);
const float3 C = safe_normalize(v2_p);
const float cos_alpha = dot(u02, u01);
const float cos_beta = -dot(u01, u12);
const float cos_gamma = dot(u02, u12);
/* calculate dihedral angles */
const float alpha = fast_acosf(cos_alpha);
const float beta = fast_acosf(cos_beta);
const float gamma = fast_acosf(cos_gamma);
/* the area of the unit spherical triangle = solid angle */
const float solid_angle = alpha + beta + gamma - M_PI_F;
/* precompute a few things
* these could be re-used to take several samples
* as they are independent of randu/randv */
const float cos_c = dot(A, B);
const float sin_alpha = fast_sinf(alpha);
const float product = sin_alpha * cos_c;
/* Select a random sub-area of the spherical triangle
* and calculate the third vertex C_ of that new triangle */
const float phi = randu * solid_angle - alpha;
float s, t;
fast_sincosf(phi, &s, &t);
const float u = t - cos_alpha;
const float v = s + product;
const float3 U = safe_normalize(C - dot(C, A) * A);
float q = 1.0f;
const float det = ((v * s + u * t) * sin_alpha);
if (det != 0.0f) {
q = ((v * t - u * s) * cos_alpha - v) / det;
}
const float temp = max(1.0f - q * q, 0.0f);
const float3 C_ = safe_normalize(q * A + sqrtf(temp) * U);
/* Finally, select a random point along the edge of the new triangle
* That point on the spherical triangle is the sampled ray direction */
const float z = 1.0f - randv * (1.0f - dot(C_, B));
ls->D = z * B + safe_sqrtf(1.0f - z * z) * safe_normalize(C_ - dot(C_, B) * B);
/* calculate intersection with the planar triangle */
if (!ray_triangle_intersect(
P, ls->D, 0.0f, FLT_MAX, V[0], V[1], V[2], &ls->u, &ls->v, &ls->t)) {
ls->pdf = 0.0f;
return;
}
ls->P = P + ls->D * ls->t;
/* pdf_triangles is calculated over triangle area, but we're sampling over solid angle */
if (UNLIKELY(solid_angle == 0.0f)) {
ls->pdf = 0.0f;
return;
}
else {
if (has_motion) {
/* get the center frame vertices, this is what the PDF was calculated from */
triangle_world_space_vertices(kg, object, prim, -1.0f, V);
area = triangle_area(V[0], V[1], V[2]);
}
const float pdf = area * kernel_data.integrator.pdf_triangles;
ls->pdf = pdf / solid_angle;
}
}
else {
/* compute random point in triangle. From Eric Heitz's "A Low-Distortion Map Between Triangle
* and Square" */
float u = randu;
float v = randv;
if (v > u) {
u *= 0.5f;
v -= u;
}
else {
v *= 0.5f;
u -= v;
}
const float t = 1.0f - u - v;
ls->P = u * V[0] + v * V[1] + t * V[2];
/* compute incoming direction, distance and pdf */
ls->D = normalize_len(ls->P - P, &ls->t);
ls->pdf = triangle_light_pdf_area(kg, ls->Ng, -ls->D, ls->t);
if (has_motion && area != 0.0f) {
/* scale the PDF.
* area = the area the sample was taken from
* area_pre = the are from which pdf_triangles was calculated from */
triangle_world_space_vertices(kg, object, prim, -1.0f, V);
const float area_pre = triangle_area(V[0], V[1], V[2]);
ls->pdf = ls->pdf * area_pre / area;
}
ls->u = u;
ls->v = v;
}
}
/* Light Distribution */
ccl_device int light_distribution_sample(KernelGlobals kg, ccl_private float *randu)
{
/* 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
* arbitrary shaders. */
int first = 0;
int len = kernel_data.integrator.num_distribution + 1;
float r = *randu;
do {
int half_len = len >> 1;
int middle = first + half_len;
if (r < kernel_data_fetch(light_distribution, middle).totarea) {
len = half_len;
}
else {
first = middle + 1;
len = len - half_len - 1;
}
} while (len > 0);
/* Clamping should not be needed but float rounding errors seem to
* make this fail on rare occasions. */
int index = clamp(first - 1, 0, kernel_data.integrator.num_distribution - 1);
/* Rescale to reuse random number. this helps the 2D samples within
* each area light be stratified as well. */
float distr_min = kernel_data_fetch(light_distribution, index).totarea;
float distr_max = kernel_data_fetch(light_distribution, index + 1).totarea;
*randu = (r - distr_min) / (distr_max - distr_min);
return index;
}
/* Generic Light */
ccl_device_inline bool light_select_reached_max_bounces(KernelGlobals kg, int index, int bounce)
{
return (bounce > kernel_data_fetch(lights, index).max_bounces);
}
template<bool in_volume_segment>
ccl_device_noinline bool light_distribution_sample(KernelGlobals kg,
float randu,
const float randv,
const float time,
const float3 P,
const int bounce,
const uint32_t path_flag,
ccl_private LightSample *ls)
{
/* Sample light index from distribution. */
const int index = light_distribution_sample(kg, &randu);
ccl_global const KernelLightDistribution *kdistribution = &kernel_data_fetch(light_distribution,
index);
const int prim = kdistribution->prim;
if (prim >= 0) {
/* Mesh light. */
const int object = kdistribution->mesh_light.object_id;
/* Exclude synthetic meshes from shadow catcher pass. */
if ((path_flag & PATH_RAY_SHADOW_CATCHER_PASS) &&
!(kernel_data_fetch(object_flag, object) & SD_OBJECT_SHADOW_CATCHER)) {
return false;
}
const int shader_flag = kdistribution->mesh_light.shader_flag;
triangle_light_sample<in_volume_segment>(kg, prim, object, randu, randv, time, ls, P);
ls->shader |= shader_flag;
return (ls->pdf > 0.0f);
}
const int lamp = -prim - 1;
if (UNLIKELY(light_select_reached_max_bounces(kg, lamp, bounce))) {
return false;
}
return light_sample<in_volume_segment>(kg, lamp, randu, randv, P, path_flag, ls);
}
ccl_device_inline bool light_distribution_sample_from_volume_segment(KernelGlobals kg,
float randu,
const float randv,
const float time,
const float3 P,
const int bounce,
const uint32_t path_flag,
ccl_private LightSample *ls)
{
return light_distribution_sample<true>(kg, randu, randv, time, P, bounce, path_flag, ls);
}
ccl_device_inline bool light_distribution_sample_from_position(KernelGlobals kg,
float randu,
const float randv,
const float time,
const float3 P,
const int bounce,
const uint32_t path_flag,
ccl_private LightSample *ls)
{
return light_distribution_sample<false>(kg, randu, randv, time, P, bounce, path_flag, ls);
}
ccl_device_inline bool light_distribution_sample_new_position(KernelGlobals kg,
const float randu,
const float randv,
const float time,
const float3 P,
ccl_private LightSample *ls)
{
/* Sample a new position on the same light, for volume sampling. */
if (ls->type == LIGHT_TRIANGLE) {
triangle_light_sample<false>(kg, ls->prim, ls->object, randu, randv, time, ls, P);
return (ls->pdf > 0.0f);
}
else {
return light_sample<false>(kg, ls->lamp, randu, randv, P, 0, ls);
}
}
CCL_NAMESPACE_END

View File

@@ -1,111 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
#pragma once
#include "kernel/light/common.h"
CCL_NAMESPACE_BEGIN
template<bool in_volume_segment>
ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight,
const float randu,
const float randv,
const float3 P,
ccl_private LightSample *ls)
{
float3 center = klight->co;
float radius = klight->spot.radius;
/* disk oriented normal */
const float3 lightN = normalize(P - center);
ls->P = center;
if (radius > 0.0f) {
ls->P += disk_light_sample(lightN, randu, randv) * radius;
}
ls->pdf = klight->spot.invarea;
ls->D = normalize_len(ls->P - P, &ls->t);
/* we set the light normal to the outgoing direction to support texturing */
ls->Ng = -ls->D;
ls->eval_fac = M_1_PI_F * 0.25f * klight->spot.invarea;
if (!in_volume_segment && ls->eval_fac == 0.0f) {
return false;
}
float2 uv = map_to_sphere(ls->Ng);
ls->u = uv.x;
ls->v = uv.y;
ls->pdf *= lamp_light_pdf(lightN, -ls->D, ls->t);
return true;
}
ccl_device_forceinline void point_light_update_position(const ccl_global KernelLight *klight,
ccl_private LightSample *ls,
const float3 P)
{
ls->D = normalize_len(ls->P - P, &ls->t);
ls->Ng = -ls->D;
float2 uv = map_to_sphere(ls->Ng);
ls->u = uv.x;
ls->v = uv.y;
float invarea = klight->spot.invarea;
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
ls->pdf = invarea;
}
ccl_device_inline bool point_light_intersect(const ccl_global KernelLight *klight,
const ccl_private Ray *ccl_restrict ray,
ccl_private float *t)
{
/* Sphere light (aka, aligned disk light). */
const float3 lightP = klight->co;
const float radius = klight->spot.radius;
if (radius == 0.0f) {
return false;
}
/* disk oriented normal */
const float3 lightN = normalize(ray->P - lightP);
float3 P;
return ray_disk_intersect(ray->P, ray->D, ray->tmin, ray->tmax, lightP, lightN, radius, &P, t);
}
ccl_device_inline bool point_light_sample_from_intersection(
const ccl_global KernelLight *klight,
ccl_private const Intersection *ccl_restrict isect,
const float3 ray_P,
const float3 ray_D,
ccl_private LightSample *ccl_restrict ls)
{
const float3 lighN = normalize(ray_P - klight->co);
/* We set the light normal to the outgoing direction to support texturing. */
ls->Ng = -ls->D;
float invarea = klight->spot.invarea;
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
ls->pdf = invarea;
if (ls->eval_fac == 0.0f) {
return false;
}
float2 uv = map_to_sphere(ls->Ng);
ls->u = uv.x;
ls->v = uv.y;
/* compute pdf */
if (ls->t != FLT_MAX) {
ls->pdf *= lamp_light_pdf(lighN, -ls->D, ls->t);
}
else {
ls->pdf = 0.f;
}
return true;
}
CCL_NAMESPACE_END

View File

@@ -6,7 +6,6 @@
#include "kernel/integrator/path_state.h"
#include "kernel/integrator/surface_shader.h"
#include "kernel/light/distribution.h"
#include "kernel/light/light.h"
#include "kernel/sample/mapping.h"
@@ -278,8 +277,6 @@ ccl_device_inline void light_sample_to_volume_shadow_ray(
shadow_ray_setup(sd, ls, P, ray, false);
}
/* Multiple importance sampling weights. */
ccl_device_inline float light_sample_mis_weight_forward(KernelGlobals kg,
const float forward_pdf,
const float nee_pdf)
@@ -312,138 +309,4 @@ ccl_device_inline float light_sample_mis_weight_nee(KernelGlobals kg,
return power_heuristic(nee_pdf, forward_pdf);
}
/* Next event estimation sampling.
*
* Sample a position on a light in the scene, from a position on a surface or
* from a volume segment. */
ccl_device_inline bool light_sample_from_volume_segment(KernelGlobals kg,
float randu,
const float randv,
const float time,
const float3 P,
const float3 D,
const float t,
const int bounce,
const uint32_t path_flag,
ccl_private LightSample *ls)
{
return light_distribution_sample<true>(kg, randu, randv, time, P, bounce, path_flag, ls);
}
ccl_device bool light_sample_from_position(KernelGlobals kg,
ccl_private const RNGState *rng_state,
const float randu,
const float randv,
const float time,
const float3 P,
const float3 N,
const int shader_flags,
const int bounce,
const uint32_t path_flag,
ccl_private LightSample *ls)
{
return light_distribution_sample<false>(kg, randu, randv, time, P, bounce, path_flag, ls);
}
ccl_device_inline bool light_sample_new_position(KernelGlobals kg,
const float randu,
const float randv,
const float time,
const float3 P,
ccl_private LightSample *ls)
{
/* Sample a new position on the same light, for volume sampling. */
if (ls->type == LIGHT_TRIANGLE) {
if (!triangle_light_sample<false>(kg, ls->prim, ls->object, randu, randv, time, ls, P)) {
return false;
}
return true;
}
else {
if (!light_sample<false>(kg, ls->lamp, randu, randv, P, 0, ls)) {
return false;
}
ls->pdf *= ls->pdf_selection;
return true;
}
}
ccl_device_forceinline void light_sample_update_position(KernelGlobals kg,
ccl_private LightSample *ls,
const float3 P)
{
/* Update light sample for new shading point position, while keeping
* position on the light fixed. */
/* NOTE : preserve pdf in area measure. */
light_update_position(kg, ls, P);
/* Re-apply already computed selection pdf. */
ls->pdf *= ls->pdf_selection;
}
/* Forward sampling.
*
* Multiple importance sampling weights for hitting surface, light or background
* through indirect light ray.
*
* The BSDF or phase pdf from the previous bounce was stored in mis_ray_pdf and
* is used for balancing with the light sampling pdf. */
ccl_device_inline float light_sample_mis_weight_forward_surface(KernelGlobals kg,
IntegratorState state,
const uint32_t path_flag,
const ccl_private ShaderData *sd)
{
const float bsdf_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf);
const float t = sd->ray_length;
float pdf = triangle_light_pdf(kg, sd, t);
/* Light selection pdf. */
/* Handled in triangle_light_pdf for effeciency. */
return light_sample_mis_weight_forward(kg, bsdf_pdf, pdf);
}
ccl_device_inline float light_sample_mis_weight_forward_lamp(KernelGlobals kg,
IntegratorState state,
const uint32_t path_flag,
const ccl_private LightSample *ls,
const float3 P)
{
const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf);
float pdf = ls->pdf;
/* Light selection pdf. */
pdf *= light_distribution_pdf_lamp(kg);
return light_sample_mis_weight_forward(kg, mis_ray_pdf, pdf);
}
ccl_device_inline float light_sample_mis_weight_forward_distant(KernelGlobals kg,
IntegratorState state,
const uint32_t path_flag,
const ccl_private LightSample *ls)
{
const float3 ray_P = INTEGRATOR_STATE(state, ray, P);
return light_sample_mis_weight_forward_lamp(kg, state, path_flag, ls, ray_P);
}
ccl_device_inline float light_sample_mis_weight_forward_background(KernelGlobals kg,
IntegratorState state,
const uint32_t path_flag)
{
const float3 ray_P = INTEGRATOR_STATE(state, ray, P);
const float3 ray_D = INTEGRATOR_STATE(state, ray, D);
const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf);
float pdf = background_light_pdf(kg, ray_P, ray_D);
/* Light selection pdf. */
pdf *= light_distribution_pdf_lamp(kg);
return light_sample_mis_weight_forward(kg, mis_ray_pdf, pdf);
}
CCL_NAMESPACE_END

View File

@@ -1,153 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
#pragma once
#include "kernel/light/common.h"
CCL_NAMESPACE_BEGIN
ccl_device float spot_light_attenuation(float3 dir,
float cos_half_spot_angle,
float spot_smooth,
float3 N)
{
float attenuation = dot(dir, N);
if (attenuation <= cos_half_spot_angle) {
attenuation = 0.0f;
}
else {
float t = attenuation - cos_half_spot_angle;
if (t < spot_smooth && spot_smooth != 0.0f)
attenuation *= smoothstepf(t / spot_smooth);
}
return attenuation;
}
template<bool in_volume_segment>
ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight,
const float randu,
const float randv,
const float3 P,
ccl_private LightSample *ls)
{
ls->P = klight->co;
const float3 center = klight->co;
const float radius = klight->spot.radius;
/* disk oriented normal */
const float3 lightN = normalize(P - center);
ls->P = center;
if (radius > 0.0f) {
/* disk light */
ls->P += disk_light_sample(lightN, randu, randv) * radius;
}
const float invarea = klight->spot.invarea;
ls->pdf = invarea;
ls->D = normalize_len(ls->P - P, &ls->t);
/* we set the light normal to the outgoing direction to support texturing */
ls->Ng = -ls->D;
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
/* spot light attenuation */
ls->eval_fac *= spot_light_attenuation(
klight->spot.dir, klight->spot.cos_half_spot_angle, klight->spot.spot_smooth, -ls->D);
if (!in_volume_segment && ls->eval_fac == 0.0f) {
return false;
}
float2 uv = map_to_sphere(ls->Ng);
ls->u = uv.x;
ls->v = uv.y;
ls->pdf *= lamp_light_pdf(lightN, -ls->D, ls->t);
return true;
}
ccl_device_forceinline void spot_light_update_position(const ccl_global KernelLight *klight,
ccl_private LightSample *ls,
const float3 P)
{
ls->D = normalize_len(ls->P - P, &ls->t);
ls->Ng = -ls->D;
float2 uv = map_to_sphere(ls->Ng);
ls->u = uv.x;
ls->v = uv.y;
float invarea = klight->spot.invarea;
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
ls->pdf = invarea;
/* spot light attenuation */
ls->eval_fac *= spot_light_attenuation(
klight->spot.dir, klight->spot.cos_half_spot_angle, klight->spot.spot_smooth, ls->Ng);
}
ccl_device_inline bool spot_light_intersect(const ccl_global KernelLight *klight,
const ccl_private Ray *ccl_restrict ray,
ccl_private float *t)
{
/* Spot/Disk light. */
const float3 lightP = klight->co;
const float radius = klight->spot.radius;
if (radius == 0.0f) {
return false;
}
/* disk oriented normal */
const float3 lightN = normalize(ray->P - lightP);
/* One sided. */
if (dot(ray->D, lightN) >= 0.0f) {
return false;
}
float3 P;
return ray_disk_intersect(ray->P, ray->D, ray->tmin, ray->tmax, lightP, lightN, radius, &P, t);
}
ccl_device_inline bool spot_light_sample_from_intersection(
const ccl_global KernelLight *klight,
ccl_private const Intersection *ccl_restrict isect,
const float3 ray_P,
const float3 ray_D,
ccl_private LightSample *ccl_restrict ls)
{
/* the normal of the oriented disk */
const float3 lightN = normalize(ray_P - klight->co);
/* We set the light normal to the outgoing direction to support texturing. */
ls->Ng = -ls->D;
float invarea = klight->spot.invarea;
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
ls->pdf = invarea;
/* spot light attenuation */
ls->eval_fac *= spot_light_attenuation(
klight->spot.dir, klight->spot.cos_half_spot_angle, klight->spot.spot_smooth, -ls->D);
if (ls->eval_fac == 0.0f) {
return false;
}
float2 uv = map_to_sphere(ls->Ng);
ls->u = uv.x;
ls->v = uv.y;
/* compute pdf */
if (ls->t != FLT_MAX) {
ls->pdf *= lamp_light_pdf(lightN, -ls->D, ls->t);
}
else {
ls->pdf = 0.f;
}
return true;
}
CCL_NAMESPACE_END

View File

@@ -1,281 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
#pragma once
#include "kernel/geom/geom.h"
CCL_NAMESPACE_BEGIN
/* returns true if the triangle is has motion blur or an instancing transform applied */
ccl_device_inline bool triangle_world_space_vertices(
KernelGlobals kg, int object, int prim, float time, float3 V[3])
{
bool has_motion = false;
const int object_flag = kernel_data_fetch(object_flag, object);
if (object_flag & SD_OBJECT_HAS_VERTEX_MOTION && time >= 0.0f) {
motion_triangle_vertices(kg, object, prim, time, V);
has_motion = true;
}
else {
triangle_vertices(kg, prim, V);
}
if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
#ifdef __OBJECT_MOTION__
float object_time = (time >= 0.0f) ? time : 0.5f;
Transform tfm = object_fetch_transform_motion_test(kg, object, object_time, NULL);
#else
Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
#endif
V[0] = transform_point(&tfm, V[0]);
V[1] = transform_point(&tfm, V[1]);
V[2] = transform_point(&tfm, V[2]);
has_motion = true;
}
return has_motion;
}
ccl_device_inline float triangle_light_pdf_area_sampling(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 / cos_pi;
}
ccl_device_forceinline float triangle_light_pdf(KernelGlobals kg,
ccl_private const ShaderData *sd,
float t)
{
/* A naive heuristic to decide between costly solid angle sampling
* and simple area sampling, comparing the distance to the triangle plane
* to the length of the edges of the triangle. */
float3 V[3];
bool has_motion = triangle_world_space_vertices(kg, sd->object, sd->prim, sd->time, V);
const float3 e0 = V[1] - V[0];
const float3 e1 = V[2] - V[0];
const float3 e2 = V[2] - V[1];
const float longest_edge_squared = max(len_squared(e0), max(len_squared(e1), len_squared(e2)));
const float3 N = cross(e0, e1);
const float distance_to_plane = fabsf(dot(N, sd->I * t)) / dot(N, N);
const float area = 0.5f * len(N);
float pdf;
if (longest_edge_squared > distance_to_plane * distance_to_plane) {
/* sd contains the point on the light source
* calculate Px, the point that we're shading */
const float3 Px = sd->P + sd->I * t;
const float3 v0_p = V[0] - Px;
const float3 v1_p = V[1] - Px;
const float3 v2_p = V[2] - Px;
const float3 u01 = safe_normalize(cross(v0_p, v1_p));
const float3 u02 = safe_normalize(cross(v0_p, v2_p));
const float3 u12 = safe_normalize(cross(v1_p, v2_p));
const float alpha = fast_acosf(dot(u02, u01));
const float beta = fast_acosf(-dot(u01, u12));
const float gamma = fast_acosf(dot(u02, u12));
const float solid_angle = alpha + beta + gamma - M_PI_F;
/* distribution_pdf_triangles is calculated over triangle area, but we're not sampling over
* its area */
if (UNLIKELY(solid_angle == 0.0f)) {
return 0.0f;
}
else {
pdf = 1.0f / solid_angle;
}
}
else {
if (UNLIKELY(area == 0.0f)) {
return 0.0f;
}
pdf = triangle_light_pdf_area_sampling(sd->Ng, sd->I, t) / area;
}
/* Belongs in distribution.h but can reuse computations here. */
float distribution_area = area;
if (has_motion && area != 0.0f) {
/* For motion blur need area of triangle at fixed time as used in the CDF. */
triangle_world_space_vertices(kg, sd->object, sd->prim, -1.0f, V);
distribution_area = triangle_area(V[0], V[1], V[2]);
}
pdf *= distribution_area * kernel_data.integrator.distribution_pdf_triangles;
return pdf;
}
template<bool in_volume_segment>
ccl_device_forceinline bool triangle_light_sample(KernelGlobals kg,
int prim,
int object,
float randu,
float randv,
float time,
ccl_private LightSample *ls,
const float3 P)
{
/* A naive heuristic to decide between costly solid angle sampling
* and simple area sampling, comparing the distance to the triangle plane
* to the length of the edges of the triangle. */
float3 V[3];
bool has_motion = triangle_world_space_vertices(kg, object, prim, time, V);
const float3 e0 = V[1] - V[0];
const float3 e1 = V[2] - V[0];
const float3 e2 = V[2] - V[1];
const float longest_edge_squared = max(len_squared(e0), max(len_squared(e1), len_squared(e2)));
const float3 N0 = cross(e0, e1);
float Nl = 0.0f;
ls->Ng = safe_normalize_len(N0, &Nl);
const float area = 0.5f * Nl;
/* flip normal if necessary */
const int object_flag = kernel_data_fetch(object_flag, object);
if (object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) {
ls->Ng = -ls->Ng;
}
ls->eval_fac = 1.0f;
ls->shader = kernel_data_fetch(tri_shader, prim);
ls->object = object;
ls->prim = prim;
ls->lamp = LAMP_NONE;
ls->shader |= SHADER_USE_MIS;
ls->type = LIGHT_TRIANGLE;
ls->group = object_lightgroup(kg, object);
float distance_to_plane = fabsf(dot(N0, V[0] - P) / dot(N0, N0));
if (!in_volume_segment && (longest_edge_squared > distance_to_plane * distance_to_plane)) {
/* see James Arvo, "Stratified Sampling of Spherical Triangles"
* http://www.graphics.cornell.edu/pubs/1995/Arv95c.pdf */
/* project the triangle to the unit sphere
* and calculate its edges and angles */
const float3 v0_p = V[0] - P;
const float3 v1_p = V[1] - P;
const float3 v2_p = V[2] - P;
const float3 u01 = safe_normalize(cross(v0_p, v1_p));
const float3 u02 = safe_normalize(cross(v0_p, v2_p));
const float3 u12 = safe_normalize(cross(v1_p, v2_p));
const float3 A = safe_normalize(v0_p);
const float3 B = safe_normalize(v1_p);
const float3 C = safe_normalize(v2_p);
const float cos_alpha = dot(u02, u01);
const float cos_beta = -dot(u01, u12);
const float cos_gamma = dot(u02, u12);
/* calculate dihedral angles */
const float alpha = fast_acosf(cos_alpha);
const float beta = fast_acosf(cos_beta);
const float gamma = fast_acosf(cos_gamma);
/* the area of the unit spherical triangle = solid angle */
const float solid_angle = alpha + beta + gamma - M_PI_F;
/* precompute a few things
* these could be re-used to take several samples
* as they are independent of randu/randv */
const float cos_c = dot(A, B);
const float sin_alpha = fast_sinf(alpha);
const float product = sin_alpha * cos_c;
/* Select a random sub-area of the spherical triangle
* and calculate the third vertex C_ of that new triangle */
const float phi = randu * solid_angle - alpha;
float s, t;
fast_sincosf(phi, &s, &t);
const float u = t - cos_alpha;
const float v = s + product;
const float3 U = safe_normalize(C - dot(C, A) * A);
float q = 1.0f;
const float det = ((v * s + u * t) * sin_alpha);
if (det != 0.0f) {
q = ((v * t - u * s) * cos_alpha - v) / det;
}
const float temp = max(1.0f - q * q, 0.0f);
const float3 C_ = safe_normalize(q * A + sqrtf(temp) * U);
/* Finally, select a random point along the edge of the new triangle
* That point on the spherical triangle is the sampled ray direction */
const float z = 1.0f - randv * (1.0f - dot(C_, B));
ls->D = z * B + safe_sqrtf(1.0f - z * z) * safe_normalize(C_ - dot(C_, B) * B);
/* calculate intersection with the planar triangle */
if (!ray_triangle_intersect(
P, ls->D, 0.0f, FLT_MAX, V[0], V[1], V[2], &ls->u, &ls->v, &ls->t)) {
ls->pdf = 0.0f;
return false;
}
ls->P = P + ls->D * ls->t;
/* distribution_pdf_triangles is calculated over triangle area, but we're sampling over solid
* angle */
if (UNLIKELY(solid_angle == 0.0f)) {
ls->pdf = 0.0f;
return false;
}
else {
ls->pdf = 1.0f / solid_angle;
}
}
else {
if (UNLIKELY(area == 0.0f)) {
return 0.0f;
}
/* compute random point in triangle. From Eric Heitz's "A Low-Distortion Map Between Triangle
* and Square" */
float u = randu;
float v = randv;
if (v > u) {
u *= 0.5f;
v -= u;
}
else {
v *= 0.5f;
u -= v;
}
const float t = 1.0f - u - v;
ls->P = u * V[0] + v * V[1] + t * V[2];
/* compute incoming direction, distance and pdf */
ls->D = normalize_len(ls->P - P, &ls->t);
ls->pdf = triangle_light_pdf_area_sampling(ls->Ng, -ls->D, ls->t) / area;
ls->u = u;
ls->v = v;
}
/* Belongs in distribution.h but can reuse computations here. */
float distribution_area = area;
if (has_motion && area != 0.0f) {
/* For motion blur need area of triangle at fixed time as used in the CDF. */
triangle_world_space_vertices(kg, object, prim, -1.0f, V);
distribution_area = triangle_area(V[0], V[1], V[2]);
}
ls->pdf_selection = distribution_area * kernel_data.integrator.distribution_pdf_triangles;
ls->pdf *= ls->pdf_selection;
return (ls->pdf > 0.0f);
}
CCL_NAMESPACE_END

View File

@@ -209,26 +209,21 @@ enum PathRayFlag : uint32_t {
PATH_RAY_SHADOW_TRANSPARENT = (1U << 9U),
PATH_RAY_SHADOW = (PATH_RAY_SHADOW_OPAQUE | PATH_RAY_SHADOW_TRANSPARENT),
/* Special flag to tag unaligned BVH nodes.
* Only set and used in BVH nodes to distinguish how to interpret bounding box information stored
* in the node (either it should be intersected as AABB or as OBBU). */
PATH_RAY_NODE_UNALIGNED = (1U << 10U),
/* Subset of flags used for ray visibility for intersection.
*
* NOTE: SHADOW_CATCHER macros below assume there are no more than
* 16 visibility bits. */
PATH_RAY_ALL_VISIBILITY = ((1U << 10U) - 1U),
/* Special flag to tag unaligned BVH nodes.
* Only set and used in BVH nodes to distinguish how to interpret bounding box information stored
* in the node (either it should be intersected as AABB or as OBBU).
* So this can overlap with path flags. */
PATH_RAY_NODE_UNALIGNED = (1U << 10U),
PATH_RAY_ALL_VISIBILITY = ((1U << 11U) - 1U),
/* --------------------------------------------------------------------
* Path flags.
*/
/* Surface had transmission component at previous bounce. Used for light tree
* traversal and culling to be consistent with MIS pdf at the next bounce. */
PATH_RAY_MIS_HAD_TRANSMISSION = (1U << 10U),
/* Don't apply multiple importance sampling weights to emission from
* lamp or surface hits, because they were not direct light sampled. */
PATH_RAY_MIS_SKIP = (1U << 11U),
@@ -467,16 +462,6 @@ typedef enum ShaderFlag {
SHADER_EXCLUDE_ANY)
} ShaderFlag;
enum EmissionSampling {
EMISSION_SAMPLING_NONE = 0,
EMISSION_SAMPLING_AUTO = 1,
EMISSION_SAMPLING_FRONT = 2,
EMISSION_SAMPLING_BACK = 3,
EMISSION_SAMPLING_FRONT_BACK = 4,
EMISSION_SAMPLING_NUM
};
/* Light Type */
typedef enum LightType {
@@ -790,16 +775,14 @@ enum ShaderDataFlag {
SD_TRANSPARENT = (1 << 9),
/* BSDF requires LCG for evaluation. */
SD_BSDF_NEEDS_LCG = (1 << 10),
/* BSDF has a transmissive component. */
SD_BSDF_HAS_TRANSMISSION = (1 << 11),
SD_CLOSURE_FLAGS = (SD_EMISSION | SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSSRDF | SD_HOLDOUT |
SD_EXTINCTION | SD_SCATTER | SD_BSDF_NEEDS_LCG | SD_BSDF_HAS_TRANSMISSION),
SD_EXTINCTION | SD_SCATTER | SD_BSDF_NEEDS_LCG),
/* Shader flags. */
/* Use front side for direct light sampling. */
SD_MIS_FRONT = (1 << 16),
/* direct light sample */
SD_USE_MIS = (1 << 16),
/* Has transparent shadow. */
SD_HAS_TRANSPARENT_SHADOW = (1 << 17),
/* Has volume shader. */
@@ -828,14 +811,12 @@ enum ShaderDataFlag {
SD_HAS_EMISSION = (1 << 29),
/* Shader has raytracing */
SD_HAS_RAYTRACE = (1 << 30),
/* Use back side for direct light sampling. */
SD_MIS_BACK = (1 << 31),
SD_SHADER_FLAGS = (SD_MIS_FRONT | SD_HAS_TRANSPARENT_SHADOW | SD_HAS_VOLUME |
SD_HAS_ONLY_VOLUME | SD_HETEROGENEOUS_VOLUME | SD_HAS_BSSRDF_BUMP |
SD_VOLUME_EQUIANGULAR | SD_VOLUME_MIS | SD_VOLUME_CUBIC | SD_HAS_BUMP |
SD_HAS_DISPLACEMENT | SD_HAS_CONSTANT_EMISSION | SD_NEED_VOLUME_ATTRIBUTES |
SD_HAS_EMISSION | SD_HAS_RAYTRACE | SD_MIS_BACK)
SD_SHADER_FLAGS = (SD_USE_MIS | SD_HAS_TRANSPARENT_SHADOW | SD_HAS_VOLUME | SD_HAS_ONLY_VOLUME |
SD_HETEROGENEOUS_VOLUME | SD_HAS_BSSRDF_BUMP | SD_VOLUME_EQUIANGULAR |
SD_VOLUME_MIS | SD_VOLUME_CUBIC | SD_HAS_BUMP | SD_HAS_DISPLACEMENT |
SD_HAS_CONSTANT_EMISSION | SD_NEED_VOLUME_ATTRIBUTES | SD_HAS_EMISSION |
SD_HAS_RAYTRACE)
};
/* Object flags. */
@@ -1287,20 +1268,20 @@ static_assert_align(KernelCurveSegment, 8);
typedef struct KernelSpotLight {
float radius;
float invarea;
float cos_half_spot_angle;
float spot_angle;
float spot_smooth;
packed_float3 dir;
float dir[3];
float pad;
} KernelSpotLight;
/* PointLight is SpotLight with only radius and invarea being used. */
typedef struct KernelAreaLight {
packed_float3 extentu;
float axisu[3];
float invarea;
packed_float3 extentv;
float axisv[3];
float tan_spread;
packed_float3 dir;
float dir[3];
float normalize_spread;
} KernelAreaLight;
@@ -1313,7 +1294,7 @@ typedef struct KernelDistantLight {
typedef struct KernelLight {
int type;
packed_float3 co;
float co[3];
int shader_id;
float max_bounces;
float random;

View File

@@ -4,7 +4,6 @@
#include "scene/background.h"
#include "device/device.h"
#include "scene/integrator.h"
#include "scene/light.h"
#include "scene/scene.h"
#include "scene/shader.h"
#include "scene/shader_graph.h"

View File

@@ -271,7 +271,7 @@ void Geometry::tag_update(Scene *scene, bool rebuild)
else {
foreach (Node *node, used_shaders) {
Shader *shader = static_cast<Shader *>(node);
if (shader->emission_sampling != EMISSION_SAMPLING_NONE) {
if (shader->has_surface_emission) {
scene->light_manager->tag_update(scene, LightManager::EMISSIVE_MESH_MODIFIED);
break;
}

View File

@@ -162,9 +162,7 @@ bool Light::has_contribution(Scene *scene)
if (light_type == LIGHT_BACKGROUND) {
return true;
}
const Shader *effective_shader = (shader) ? shader : scene->default_light;
return !is_zero(effective_shader->emission_estimate);
return (shader) ? shader->has_surface_emission : scene->default_light->has_surface_emission;
}
/* Light Manager */
@@ -258,27 +256,37 @@ bool LightManager::object_usable_as_light(Object *object)
*/
foreach (Node *node, geom->get_used_shaders()) {
Shader *shader = static_cast<Shader *>(node);
if (shader->emission_sampling != EMISSION_SAMPLING_NONE) {
if (shader->get_use_mis() && shader->has_surface_emission) {
return true;
}
}
return false;
}
void LightManager::device_update_distribution(Device *device,
void LightManager::device_update_distribution(Device *,
DeviceScene *dscene,
Scene *scene,
Progress &progress)
{
KernelIntegrator *kintegrator = &dscene->data.integrator;
KernelFilm *kfilm = &dscene->data.film;
/* Update CDF over lights. */
progress.set_status("Updating Lights", "Computing distribution");
/* Counts emissive triangles in the scene. */
/* count */
size_t num_lights = 0;
size_t num_portals = 0;
size_t num_background_lights = 0;
size_t num_triangles = 0;
bool background_mis = false;
foreach (Light *light, scene->lights) {
if (light->is_enabled) {
num_lights++;
}
if (light->is_portal) {
num_portals++;
}
}
foreach (Object *object, scene->objects) {
if (progress.get_cancel())
return;
@@ -287,36 +295,29 @@ void LightManager::device_update_distribution(Device *device,
continue;
}
/* Count emissive triangles. */
/* Count triangles. */
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
size_t mesh_num_triangles = mesh->num_triangles();
for (size_t i = 0; i < mesh_num_triangles; i++) {
int shader_index = mesh->get_shader()[i];
Shader *shader = (shader_index < mesh->get_used_shaders().size()) ?
static_cast<Shader *>(mesh->get_used_shaders()[shader_index]) :
scene->default_surface;
if (shader->emission_sampling != EMISSION_SAMPLING_NONE) {
if (shader->get_use_mis() && shader->has_surface_emission) {
num_triangles++;
}
}
}
const size_t num_lights = kintegrator->num_lights;
const size_t num_background_lights = kintegrator->num_background_lights;
const size_t num_distribution = num_triangles + num_lights;
/* Distribution size. */
kintegrator->num_distribution = num_distribution;
size_t num_distribution = num_triangles + num_lights;
VLOG_INFO << "Total " << num_distribution << " of light distribution primitives.";
/* Emission area. */
/* emission area */
KernelLightDistribution *distribution = dscene->light_distribution.alloc(num_distribution + 1);
float totarea = 0.0f;
/* Triangles. */
/* triangles */
size_t offset = 0;
int j = 0;
@@ -361,7 +362,7 @@ void LightManager::device_update_distribution(Device *device,
static_cast<Shader *>(mesh->get_used_shaders()[shader_index]) :
scene->default_surface;
if (shader->emission_sampling != EMISSION_SAMPLING_NONE) {
if (shader->get_use_mis() && shader->has_surface_emission) {
distribution[offset].totarea = totarea;
distribution[offset].prim = i + mesh->prim_offset;
distribution[offset].mesh_light.shader_flag = shader_flag;
@@ -389,9 +390,9 @@ void LightManager::device_update_distribution(Device *device,
j++;
}
const float trianglearea = totarea;
/* Lights. */
float trianglearea = totarea;
/* point lights */
bool use_lamp_mis = false;
int light_index = 0;
if (num_lights > 0) {
@@ -406,6 +407,20 @@ void LightManager::device_update_distribution(Device *device,
distribution[offset].lamp.size = light->size;
totarea += lightarea;
if (light->light_type == LIGHT_DISTANT) {
use_lamp_mis |= (light->angle > 0.0f && light->use_mis);
}
else if (light->light_type == LIGHT_POINT || light->light_type == LIGHT_SPOT) {
use_lamp_mis |= (light->size > 0.0f && light->use_mis);
}
else if (light->light_type == LIGHT_AREA) {
use_lamp_mis |= light->use_mis;
}
else if (light->light_type == LIGHT_BACKGROUND) {
num_background_lights++;
background_mis |= light->use_mis;
}
light_index++;
offset++;
}
@@ -426,42 +441,82 @@ void LightManager::device_update_distribution(Device *device,
if (progress.get_cancel())
return;
/* Update integrator state. */
/* update device */
KernelIntegrator *kintegrator = &dscene->data.integrator;
KernelBackground *kbackground = &dscene->data.background;
KernelFilm *kfilm = &dscene->data.film;
kintegrator->use_direct_light = (totarea > 0.0f);
/* Precompute pdfs for distribution sampling.
* Sample one, with 0.5 probability of light or triangle. */
kintegrator->distribution_pdf_triangles = 0.0f;
kintegrator->distribution_pdf_lights = 0.0f;
if (kintegrator->use_direct_light) {
/* number of emissives */
kintegrator->num_distribution = num_distribution;
if (trianglearea > 0.0f) {
kintegrator->distribution_pdf_triangles = 1.0f / trianglearea;
if (num_lights) {
kintegrator->distribution_pdf_triangles *= 0.5f;
}
}
/* precompute pdfs */
kintegrator->pdf_triangles = 0.0f;
kintegrator->pdf_lights = 0.0f;
/* sample one, with 0.5 probability of light or triangle */
kintegrator->num_all_lights = num_lights;
if (num_lights) {
kintegrator->distribution_pdf_lights = 1.0f / num_lights;
if (trianglearea > 0.0f) {
kintegrator->distribution_pdf_lights *= 0.5f;
kintegrator->pdf_triangles = 1.0f / trianglearea;
if (num_lights)
kintegrator->pdf_triangles *= 0.5f;
}
if (num_lights) {
kintegrator->pdf_lights = 1.0f / num_lights;
if (trianglearea > 0.0f)
kintegrator->pdf_lights *= 0.5f;
}
kintegrator->use_lamp_mis = use_lamp_mis;
/* bit of an ugly hack to compensate for emitting triangles influencing
* amount of samples we get for this pass */
kfilm->pass_shadow_scale = 1.0f;
if (kintegrator->pdf_triangles != 0.0f)
kfilm->pass_shadow_scale /= 0.5f;
if (num_background_lights < num_lights)
kfilm->pass_shadow_scale /= (float)(num_lights - num_background_lights) / (float)num_lights;
/* CDF */
dscene->light_distribution.copy_to_device();
/* Portals */
if (num_portals > 0) {
kbackground->portal_offset = light_index;
kbackground->num_portals = num_portals;
kbackground->portal_weight = 1.0f;
}
else {
kbackground->num_portals = 0;
kbackground->portal_offset = 0;
kbackground->portal_weight = 0.0f;
}
/* Map */
kbackground->map_weight = background_mis ? 1.0f : 0.0f;
}
else {
dscene->light_distribution.free();
/* bit of an ugly hack to compensate for emitting triangles influencing
* amount of samples we get for this pass */
kfilm->pass_shadow_scale = 1.0f;
kintegrator->num_distribution = 0;
kintegrator->num_all_lights = 0;
kintegrator->pdf_triangles = 0.0f;
kintegrator->pdf_lights = 0.0f;
kintegrator->use_lamp_mis = false;
if (kintegrator->distribution_pdf_triangles != 0.0f) {
kfilm->pass_shadow_scale /= 0.5f;
kbackground->num_portals = 0;
kbackground->portal_offset = 0;
kbackground->portal_weight = 0.0f;
kbackground->sun_weight = 0.0f;
kbackground->map_weight = 0.0f;
kfilm->pass_shadow_scale = 1.0f;
}
if (num_background_lights < num_lights) {
kfilm->pass_shadow_scale /= (float)(num_lights - num_background_lights) / (float)num_lights;
}
/* Copy distribution to device. */
dscene->light_distribution.copy_to_device();
}
static void background_cdf(
@@ -509,34 +564,31 @@ void LightManager::device_update_background(Device *device,
Scene *scene,
Progress &progress)
{
KernelIntegrator *kintegrator = &dscene->data.integrator;
KernelBackground *kbackground = &dscene->data.background;
Light *background_light = NULL;
bool background_mis = false;
/* find background light */
foreach (Light *light, scene->lights) {
if (light->light_type == LIGHT_BACKGROUND && light->is_enabled) {
if (light->light_type == LIGHT_BACKGROUND) {
background_light = light;
background_mis |= light->use_mis;
break;
}
}
kbackground->portal_weight = kintegrator->num_portals > 0 ? 1.0f : 0.0f;
kbackground->map_weight = background_mis ? 1.0f : 0.0f;
kbackground->sun_weight = 0.0f;
/* no background light found, signal renderer to skip sampling */
if (!background_light || !background_light->is_enabled) {
kbackground->map_res_x = 0;
kbackground->map_res_y = 0;
kbackground->map_weight = 0.0f;
kbackground->sun_weight = 0.0f;
kbackground->use_mis = (kbackground->portal_weight > 0.0f);
return;
}
progress.set_status("Updating Lights", "Importance map");
assert(dscene->data.integrator.use_direct_light);
int2 environment_res = make_int2(0, 0);
Shader *shader = scene->background->get_shader(scene);
int num_suns = 0;
@@ -580,7 +632,6 @@ void LightManager::device_update_background(Device *device,
kbackground->sun = make_float4(
sun_direction.x, sun_direction.y, sun_direction.z, half_angle);
/* empirical value */
kbackground->sun_weight = 4.0f;
environment_res.x = max(environment_res.x, 512);
environment_res.y = max(environment_res.y, 256);
@@ -663,83 +714,27 @@ void LightManager::device_update_background(Device *device,
dscene->light_background_conditional_cdf.copy_to_device();
}
void LightManager::device_update_lights(Device *, DeviceScene *dscene, Scene *scene)
void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *scene)
{
/* Counts lights in the scene. */
size_t num_lights = 0;
size_t num_portals = 0;
size_t num_background_lights = 0;
size_t num_distant_lights = 0;
bool use_light_mis = false;
int num_scene_lights = scene->lights.size();
int num_lights = 0;
foreach (Light *light, scene->lights) {
if (light->is_enabled) {
if (light->is_enabled || light->is_portal) {
num_lights++;
if (light->light_type == LIGHT_DISTANT) {
num_distant_lights++;
}
else if (light->light_type == LIGHT_POINT || light->light_type == LIGHT_SPOT) {
use_light_mis |= (light->size > 0.0f && light->use_mis);
}
else if (light->light_type == LIGHT_AREA) {
use_light_mis |= light->use_mis;
}
else if (light->light_type == LIGHT_BACKGROUND) {
num_distant_lights++;
num_background_lights++;
}
}
if (light->is_portal) {
num_portals++;
}
}
/* Update integrator settings. */
KernelIntegrator *kintegrator = &dscene->data.integrator;
kintegrator->num_lights = num_lights;
kintegrator->num_distant_lights = num_distant_lights;
kintegrator->num_background_lights = num_background_lights;
kintegrator->use_light_mis = use_light_mis;
KernelLight *klights = dscene->lights.alloc(num_lights);
kintegrator->num_portals = num_portals;
kintegrator->portal_offset = num_lights;
/* Create KernelLight for every portal and enabled light in the scene. */
KernelLight *klights = dscene->lights.alloc(num_lights + num_portals);
if (num_lights == 0) {
VLOG_WORK << "No effective light, ignoring points update.";
return;
}
int light_index = 0;
int portal_index = num_lights;
foreach (Light *light, scene->lights) {
/* Consider moving portals update to their own function
* keeping this one more manageable. */
if (light->is_portal) {
assert(light->light_type == LIGHT_AREA);
float3 extentu = light->axisu * (light->sizeu * light->size);
float3 extentv = light->axisv * (light->sizev * light->size);
float area = len(extentu) * len(extentv);
if (light->round) {
area *= -M_PI_4_F;
}
float invarea = (area != 0.0f) ? 1.0f / area : 1.0f;
float3 dir = light->dir;
dir = safe_normalize(dir);
klights[portal_index].co = light->co;
klights[portal_index].area.extentu = extentu;
klights[portal_index].area.extentv = extentv;
klights[portal_index].area.invarea = invarea;
klights[portal_index].area.dir = dir;
klights[portal_index].tfm = light->tfm;
klights[portal_index].itfm = transform_inverse(light->tfm);
portal_index++;
continue;
}
if (!light->is_enabled) {
continue;
}
@@ -786,7 +781,10 @@ void LightManager::device_update_lights(Device *, DeviceScene *dscene, Scene *sc
if (light->use_mis && radius > 0.0f)
shader_id |= SHADER_USE_MIS;
klights[light_index].co = co;
klights[light_index].co[0] = co.x;
klights[light_index].co[1] = co.y;
klights[light_index].co[2] = co.z;
klights[light_index].spot.radius = radius;
klights[light_index].spot.invarea = invarea;
}
@@ -805,7 +803,10 @@ void LightManager::device_update_lights(Device *, DeviceScene *dscene, Scene *sc
if (light->use_mis && area > 0.0f)
shader_id |= SHADER_USE_MIS;
klights[light_index].co = dir;
klights[light_index].co[0] = dir.x;
klights[light_index].co[1] = dir.y;
klights[light_index].co[2] = dir.z;
klights[light_index].distant.invarea = invarea;
klights[light_index].distant.radius = radius;
klights[light_index].distant.cosangle = cosangle;
@@ -813,8 +814,6 @@ void LightManager::device_update_lights(Device *, DeviceScene *dscene, Scene *sc
else if (light->light_type == LIGHT_BACKGROUND) {
uint visibility = scene->background->get_visibility();
dscene->data.background.light_index = light_index;
shader_id &= ~SHADER_AREA_LIGHT;
shader_id |= SHADER_USE_MIS;
@@ -832,9 +831,9 @@ void LightManager::device_update_lights(Device *, DeviceScene *dscene, Scene *sc
}
}
else if (light->light_type == LIGHT_AREA) {
float3 extentu = light->axisu * (light->sizeu * light->size);
float3 extentv = light->axisv * (light->sizev * light->size);
float area = len(extentu) * len(extentv);
float3 axisu = light->axisu * (light->sizeu * light->size);
float3 axisv = light->axisv * (light->sizev * light->size);
float area = len(axisu) * len(axisv);
if (light->round) {
area *= -M_PI_4_F;
}
@@ -855,11 +854,20 @@ void LightManager::device_update_lights(Device *, DeviceScene *dscene, Scene *sc
if (light->use_mis && area != 0.0f)
shader_id |= SHADER_USE_MIS;
klights[light_index].co = co;
klights[light_index].area.extentu = extentu;
klights[light_index].area.extentv = extentv;
klights[light_index].co[0] = co.x;
klights[light_index].co[1] = co.y;
klights[light_index].co[2] = co.z;
klights[light_index].area.axisu[0] = axisu.x;
klights[light_index].area.axisu[1] = axisu.y;
klights[light_index].area.axisu[2] = axisu.z;
klights[light_index].area.axisv[0] = axisv.x;
klights[light_index].area.axisv[1] = axisv.y;
klights[light_index].area.axisv[2] = axisv.z;
klights[light_index].area.invarea = invarea;
klights[light_index].area.dir = dir;
klights[light_index].area.dir[0] = dir.x;
klights[light_index].area.dir[1] = dir.y;
klights[light_index].area.dir[2] = dir.z;
klights[light_index].area.tan_spread = tan_spread;
klights[light_index].area.normalize_spread = normalize_spread;
}
@@ -868,8 +876,8 @@ void LightManager::device_update_lights(Device *, DeviceScene *dscene, Scene *sc
float radius = light->size;
float invarea = (radius > 0.0f) ? 1.0f / (M_PI_F * radius * radius) : 1.0f;
float cos_half_spot_angle = cosf(light->spot_angle * 0.5f);
float spot_smooth = (1.0f - cos_half_spot_angle) * light->spot_smooth;
float spot_angle = cosf(light->spot_angle * 0.5f);
float spot_smooth = (1.0f - spot_angle) * light->spot_smooth;
float3 dir = light->dir;
dir = safe_normalize(dir);
@@ -877,12 +885,17 @@ void LightManager::device_update_lights(Device *, DeviceScene *dscene, Scene *sc
if (light->use_mis && radius > 0.0f)
shader_id |= SHADER_USE_MIS;
klights[light_index].co = co;
klights[light_index].co[0] = co.x;
klights[light_index].co[1] = co.y;
klights[light_index].co[2] = co.z;
klights[light_index].spot.radius = radius;
klights[light_index].spot.invarea = invarea;
klights[light_index].spot.cos_half_spot_angle = cos_half_spot_angle;
klights[light_index].spot.spot_angle = spot_angle;
klights[light_index].spot.spot_smooth = spot_smooth;
klights[light_index].spot.dir = dir;
klights[light_index].spot.dir[0] = dir.x;
klights[light_index].spot.dir[1] = dir.y;
klights[light_index].spot.dir[2] = dir.z;
}
klights[light_index].shader_id = shader_id;
@@ -905,7 +918,49 @@ void LightManager::device_update_lights(Device *, DeviceScene *dscene, Scene *sc
light_index++;
}
VLOG_INFO << "Number of lights sent to the device: " << num_lights;
/* TODO(sergey): Consider moving portals update to their own function
* keeping this one more manageable.
*/
foreach (Light *light, scene->lights) {
if (!light->is_portal)
continue;
assert(light->light_type == LIGHT_AREA);
float3 co = light->co;
float3 axisu = light->axisu * (light->sizeu * light->size);
float3 axisv = light->axisv * (light->sizev * light->size);
float area = len(axisu) * len(axisv);
if (light->round) {
area *= -M_PI_4_F;
}
float invarea = (area != 0.0f) ? 1.0f / area : 1.0f;
float3 dir = light->dir;
dir = safe_normalize(dir);
klights[light_index].co[0] = co.x;
klights[light_index].co[1] = co.y;
klights[light_index].co[2] = co.z;
klights[light_index].area.axisu[0] = axisu.x;
klights[light_index].area.axisu[1] = axisu.y;
klights[light_index].area.axisu[2] = axisu.z;
klights[light_index].area.axisv[0] = axisv.x;
klights[light_index].area.axisv[1] = axisv.y;
klights[light_index].area.axisv[2] = axisv.z;
klights[light_index].area.invarea = invarea;
klights[light_index].area.dir[0] = dir.x;
klights[light_index].area.dir[1] = dir.y;
klights[light_index].area.dir[2] = dir.z;
klights[light_index].tfm = light->tfm;
klights[light_index].itfm = transform_inverse(light->tfm);
light_index++;
}
VLOG_INFO << "Number of lights sent to the device: " << light_index;
VLOG_INFO << "Number of lights without contribution: " << num_scene_lights - light_index;
dscene->lights.copy_to_device();
}
@@ -931,7 +986,11 @@ void LightManager::device_update(Device *device,
device_free(device, dscene, need_update_background);
device_update_lights(device, dscene, scene);
device_update_points(device, dscene, scene);
if (progress.get_cancel())
return;
device_update_distribution(device, dscene, scene, progress);
if (progress.get_cancel())
return;
@@ -941,10 +1000,6 @@ void LightManager::device_update(Device *device,
return;
}
device_update_distribution(device, dscene, scene, progress);
if (progress.get_cancel())
return;
device_update_ies(dscene);
if (progress.get_cancel())
return;

View File

@@ -127,12 +127,11 @@ class LightManager {
*/
void test_enabled_lights(Scene *scene);
void device_update_lights(Device *device, DeviceScene *dscene, Scene *scene);
void device_update_points(Device *device, DeviceScene *dscene, Scene *scene);
void device_update_distribution(Device *device,
DeviceScene *dscene,
Scene *scene,
Progress &progress);
void device_update_tree(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
void device_update_background(Device *device,
DeviceScene *dscene,
Scene *scene,

View File

@@ -231,7 +231,7 @@ void Object::tag_update(Scene *scene)
foreach (Node *node, geometry->get_used_shaders()) {
Shader *shader = static_cast<Shader *>(node);
if (shader->emission_sampling != EMISSION_SAMPLING_NONE)
if (shader->get_use_mis() && shader->has_surface_emission)
scene->light_manager->tag_update(scene, LightManager::EMISSIVE_MESH_MODIFIED);
}
}

View File

@@ -137,7 +137,7 @@ void OSLShaderManager::device_update_specific(Device *device,
compiler.compile(og, shader);
});
if (shader->emission_sampling != EMISSION_SAMPLING_NONE)
if (shader->get_use_mis() && shader->has_surface_emission)
scene->light_manager->tag_update(scene, LightManager::SHADER_COMPILED);
}
@@ -819,11 +819,8 @@ void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath)
if (current_type == SHADER_TYPE_SURFACE) {
if (info) {
if (info->has_surface_emission && node->special_type == SHADER_SPECIAL_TYPE_OSL) {
/* Will be used by Shader::estimate_emission. */
OSLNode *oslnode = static_cast<OSLNode *>(node);
oslnode->has_emission = true;
}
if (info->has_surface_emission)
current_shader->has_surface_emission = true;
if (info->has_surface_transparent)
current_shader->has_surface_transparent = true;
if (info->has_surface_bssrdf) {
@@ -1123,6 +1120,8 @@ void OSLCompiler::generate_nodes(const ShaderNodeSet &nodes)
done.insert(node);
if (current_type == SHADER_TYPE_SURFACE) {
if (node->has_surface_emission())
current_shader->has_surface_emission = true;
if (node->has_surface_transparent())
current_shader->has_surface_transparent = true;
if (node->get_feature() & KERNEL_FEATURE_NODE_RAYTRACE)
@@ -1214,6 +1213,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader)
current_shader = shader;
shader->has_surface = false;
shader->has_surface_emission = false;
shader->has_surface_transparent = false;
shader->has_surface_bssrdf = false;
shader->has_bump = has_bump;
@@ -1256,9 +1256,6 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader)
}
else
shader->osl_displacement_ref = OSL::ShaderGroupRef();
/* Estimate emission for MIS. */
shader->estimate_emission();
}
/* push state to array for lookup */

View File

@@ -147,17 +147,7 @@ NODE_DEFINE(Shader)
{
NodeType *type = NodeType::add("shader", create);
static NodeEnum emission_sampling_method_enum;
emission_sampling_method_enum.insert("none", EMISSION_SAMPLING_NONE);
emission_sampling_method_enum.insert("auto", EMISSION_SAMPLING_AUTO);
emission_sampling_method_enum.insert("front", EMISSION_SAMPLING_FRONT);
emission_sampling_method_enum.insert("back", EMISSION_SAMPLING_BACK);
emission_sampling_method_enum.insert("front_back", EMISSION_SAMPLING_FRONT_BACK);
SOCKET_ENUM(emission_sampling_method,
"Emission Sampling Method",
emission_sampling_method_enum,
EMISSION_SAMPLING_AUTO);
SOCKET_BOOLEAN(use_mis, "Use MIS", true);
SOCKET_BOOLEAN(use_transparent_shadow, "Use Transparent Shadow", true);
SOCKET_BOOLEAN(heterogeneous_volume, "Heterogeneous Volume", true);
@@ -199,6 +189,7 @@ Shader::Shader() : Node(get_node_type())
has_surface = false;
has_surface_transparent = false;
has_surface_emission = false;
has_surface_raytrace = false;
has_surface_bssrdf = false;
has_volume = false;
@@ -212,10 +203,6 @@ Shader::Shader() : Node(get_node_type())
has_volume_connected = false;
prev_volume_step_rate = 0.0f;
emission_estimate = zero_float3();
emission_sampling = EMISSION_SAMPLING_NONE;
emission_is_constant = true;
displacement_method = DISPLACE_BUMP;
id = -1;
@@ -230,141 +217,50 @@ Shader::~Shader()
delete graph;
}
static float3 output_estimate_emission(ShaderOutput *output, bool &is_constant)
{
/* Only supports a few nodes for now, not arbitrary shader graphs. */
ShaderNode *node = (output) ? output->parent : nullptr;
if (node == nullptr) {
return zero_float3();
}
else if (node->type == EmissionNode::get_node_type() ||
node->type == BackgroundNode::get_node_type()) {
/* Emission and Background node. */
ShaderInput *color_in = node->input("Color");
ShaderInput *strength_in = node->input("Strength");
float3 estimate = one_float3();
if (color_in->link) {
is_constant = false;
}
else {
estimate *= node->get_float3(color_in->socket_type);
}
if (strength_in->link) {
is_constant = false;
estimate *= output_estimate_emission(strength_in->link, is_constant);
}
else {
estimate *= node->get_float(strength_in->socket_type);
}
return estimate;
}
else if (node->type == LightFalloffNode::get_node_type()) {
/* Light Falloff node. */
ShaderInput *strength_in = node->input("Strength");
is_constant = false;
return (strength_in->link) ? output_estimate_emission(strength_in->link, is_constant) :
make_float3(node->get_float(strength_in->socket_type));
}
else if (node->type == AddClosureNode::get_node_type()) {
/* Add Closure. */
ShaderInput *closure1_in = node->input("Closure1");
ShaderInput *closure2_in = node->input("Closure2");
const float3 estimate1 = (closure1_in->link) ?
output_estimate_emission(closure1_in->link, is_constant) :
zero_float3();
const float3 estimate2 = (closure2_in->link) ?
output_estimate_emission(closure2_in->link, is_constant) :
zero_float3();
return estimate1 + estimate2;
}
else if (node->type == MixClosureNode::get_node_type()) {
/* Mix Closure. */
ShaderInput *fac_in = node->input("Fac");
ShaderInput *closure1_in = node->input("Closure1");
ShaderInput *closure2_in = node->input("Closure2");
const float3 estimate1 = (closure1_in->link) ?
output_estimate_emission(closure1_in->link, is_constant) :
zero_float3();
const float3 estimate2 = (closure2_in->link) ?
output_estimate_emission(closure2_in->link, is_constant) :
zero_float3();
if (fac_in->link) {
is_constant = false;
return estimate1 + estimate2;
}
else {
const float fac = node->get_float(fac_in->socket_type);
return (1.0f - fac) * estimate1 + fac * estimate2;
}
}
else {
/* Other nodes, potentially OSL nodes with arbitrary code for which all we can
* determine is if it has emission or not. */
const bool has_emission = node->has_surface_emission();
float3 estimate;
if (output->type() == SocketType::CLOSURE) {
if (has_emission) {
estimate = one_float3();
is_constant = false;
}
else {
estimate = zero_float3();
}
foreach (const ShaderInput *in, node->inputs) {
if (in->type() == SocketType::CLOSURE && in->link) {
estimate += output_estimate_emission(in->link, is_constant);
}
}
}
else {
estimate = one_float3();
is_constant = false;
}
return estimate;
}
}
void Shader::estimate_emission()
bool Shader::is_constant_emission(float3 *emission)
{
/* If the shader has AOVs, they need to be evaluated, so we can't skip the shader. */
emission_is_constant = true;
foreach (ShaderNode *node, graph->nodes) {
if (node->special_type == SHADER_SPECIAL_TYPE_OUTPUT_AOV) {
emission_is_constant = false;
return false;
}
}
ShaderInput *surf = graph->output()->input("Surface");
emission_estimate = output_estimate_emission(surf->link, emission_is_constant);
if (is_zero(emission_estimate)) {
emission_sampling = EMISSION_SAMPLING_NONE;
if (surf->link == NULL) {
return false;
}
else if (emission_sampling_method == EMISSION_SAMPLING_AUTO) {
/* Automatically disable MIS when emission is low, to avoid weakly emitting
* using a lot of memory in the light tree and potentially wasting samples
* where indirect light samples are sufficient.
* Possible optimization: estimate front and back emission separately. */
emission_sampling = (reduce_max(emission_estimate) > 0.5f) ? EMISSION_SAMPLING_FRONT_BACK :
EMISSION_SAMPLING_NONE;
if (surf->link->parent->type == EmissionNode::get_node_type()) {
EmissionNode *node = (EmissionNode *)surf->link->parent;
assert(node->input("Color"));
assert(node->input("Strength"));
if (node->input("Color")->link || node->input("Strength")->link) {
return false;
}
*emission = node->get_color() * node->get_strength();
}
else if (surf->link->parent->type == BackgroundNode::get_node_type()) {
BackgroundNode *node = (BackgroundNode *)surf->link->parent;
assert(node->input("Color"));
assert(node->input("Strength"));
if (node->input("Color")->link || node->input("Strength")->link) {
return false;
}
*emission = node->get_color() * node->get_strength();
}
else {
emission_sampling = emission_sampling_method;
return false;
}
return true;
}
void Shader::set_graph(ShaderGraph *graph_)
@@ -409,7 +305,7 @@ void Shader::tag_update(Scene *scene)
/* if the shader previously was emissive, update light distribution,
* if the new shader is emissive, a light manager update tag will be
* done in the shader manager device update. */
if (emission_sampling != EMISSION_SAMPLING_NONE)
if (use_mis && has_surface_emission)
scene->light_manager->tag_update(scene, LightManager::SHADER_MODIFIED);
/* Special handle of background MIS light for now: for some reason it
@@ -595,17 +491,9 @@ void ShaderManager::device_update_common(Device * /*device*/,
foreach (Shader *shader, scene->shaders) {
uint flag = 0;
if (shader->emission_sampling == EMISSION_SAMPLING_FRONT) {
flag |= SD_MIS_FRONT;
}
else if (shader->emission_sampling == EMISSION_SAMPLING_BACK) {
flag |= SD_MIS_BACK;
}
else if (shader->emission_sampling == EMISSION_SAMPLING_FRONT_BACK) {
flag |= SD_MIS_FRONT | SD_MIS_BACK;
}
if (!is_zero(shader->emission_estimate))
if (shader->get_use_mis())
flag |= SD_USE_MIS;
if (shader->has_surface_emission)
flag |= SD_HAS_EMISSION;
if (shader->has_surface_transparent && shader->get_use_transparent_shadow())
flag |= SD_HAS_TRANSPARENT_SHADOW;
@@ -643,7 +531,8 @@ void ShaderManager::device_update_common(Device * /*device*/,
flag |= SD_HAS_DISPLACEMENT;
/* constant emission check */
if (shader->emission_is_constant)
float3 constant_emission = zero_float3();
if (shader->is_constant_emission(&constant_emission))
flag |= SD_HAS_CONSTANT_EMISSION;
uint32_t cryptomatte_id = util_murmur_hash3(shader->name.c_str(), shader->name.length(), 0);
@@ -651,9 +540,9 @@ void ShaderManager::device_update_common(Device * /*device*/,
/* regular shader */
kshader->flags = flag;
kshader->pass_id = shader->get_pass_id();
kshader->constant_emission[0] = shader->emission_estimate.x;
kshader->constant_emission[1] = shader->emission_estimate.y;
kshader->constant_emission[2] = shader->emission_estimate.z;
kshader->constant_emission[0] = constant_emission.x;
kshader->constant_emission[1] = constant_emission.y;
kshader->constant_emission[2] = constant_emission.z;
kshader->cryptomatte_id = util_hash_to_float(cryptomatte_id);
kshader++;
@@ -738,8 +627,8 @@ void ShaderManager::add_default(Scene *scene)
shader->set_graph(graph);
scene->default_volume = shader;
shader->tag_update(scene);
/* No default reference for the volume to avoid compiling volume kernels if there are no
* actual volumes in the scene */
/* No default reference for the volume to avoid compiling volume kernels if there are no actual
* volumes in the scene */
}
/* default light */

View File

@@ -34,7 +34,6 @@ struct float3;
enum ShadingSystem { SHADINGSYSTEM_OSL, SHADINGSYSTEM_SVM };
/* Keep those in sync with the python-defined enum. */
enum VolumeSampling {
VOLUME_SAMPLING_DISTANCE = 0,
VOLUME_SAMPLING_EQUIANGULAR = 1,
@@ -74,7 +73,7 @@ class Shader : public Node {
NODE_SOCKET_API(int, pass_id)
/* sampling */
NODE_SOCKET_API(EmissionSampling, emission_sampling_method)
NODE_SOCKET_API(bool, use_mis)
NODE_SOCKET_API(bool, use_transparent_shadow)
NODE_SOCKET_API(bool, heterogeneous_volume)
NODE_SOCKET_API(VolumeSampling, volume_sampling_method)
@@ -102,6 +101,7 @@ class Shader : public Node {
/* information about shader after compiling */
bool has_surface;
bool has_surface_emission;
bool has_surface_transparent;
bool has_surface_raytrace;
bool has_volume;
@@ -114,10 +114,6 @@ class Shader : public Node {
bool has_volume_attribute_dependency;
bool has_integrator_dependency;
float3 emission_estimate;
EmissionSampling emission_sampling;
bool emission_is_constant;
/* requested mesh attributes */
AttributeRequestSet attributes;
@@ -135,12 +131,11 @@ class Shader : public Node {
Shader();
~Shader();
/* Estimate emission of this shader based on the shader graph. This works only in very simple
* cases. But it helps improve light importance sampling in common cases.
*
* If the emission is fully constant, returns true, so that shader evaluation can be skipped
* entirely for a light. */
void estimate_emission();
/* Checks whether the shader consists of just a emission node with fixed inputs that's connected
* directly to the output.
* If yes, it sets the content of emission to the constant value (color * strength), which is
* then used for speeding up light evaluation. */
bool is_constant_emission(float3 *emission);
void set_graph(ShaderGraph *graph);
void tag_update(Scene *scene);

View File

@@ -74,15 +74,15 @@ class ShaderInput {
{
}
ustring name() const
ustring name()
{
return socket_type.ui_name;
}
int flags() const
int flags()
{
return socket_type.flags;
}
SocketType::Type type() const
SocketType::Type type()
{
return socket_type.type;
}
@@ -119,11 +119,11 @@ class ShaderOutput {
{
}
ustring name() const
ustring name()
{
return socket_type.ui_name;
}
SocketType::Type type() const
SocketType::Type type()
{
return socket_type.type;
}

View File

@@ -7211,7 +7211,6 @@ void SetNormalNode::compile(OSLCompiler &compiler)
OSLNode::OSLNode() : ShaderNode(new NodeType(NodeType::SHADER))
{
special_type = SHADER_SPECIAL_TYPE_OSL;
has_emission = false;
}
OSLNode::~OSLNode()

View File

@@ -1530,11 +1530,6 @@ class OSLNode final : public ShaderNode {
SHADER_NODE_NO_CLONE_CLASS(OSLNode)
bool has_surface_emission()
{
return has_emission;
}
/* Ideally we could better detect this, but we can't query this now. */
bool has_spatial_varying()
{
@@ -1556,7 +1551,6 @@ class OSLNode final : public ShaderNode {
string filepath;
string bytecode_hash;
bool has_emission;
};
class NormalMapNode : public ShaderNode {

View File

@@ -109,7 +109,7 @@ void SVMShaderManager::device_update_specific(Device *device,
Shader *shader = scene->shaders[i];
shader->clear_modified();
if (shader->emission_sampling != EMISSION_SAMPLING_NONE) {
if (shader->get_use_mis() && shader->has_surface_emission) {
scene->light_manager->tag_update(scene, LightManager::SHADER_COMPILED);
}
@@ -516,6 +516,8 @@ void SVMCompiler::generate_closure_node(ShaderNode *node, CompilerState *state)
mix_weight_offset = SVM_STACK_INVALID;
if (current_type == SHADER_TYPE_SURFACE) {
if (node->has_surface_emission())
current_shader->has_surface_emission = true;
if (node->has_surface_transparent())
current_shader->has_surface_transparent = true;
if (node->has_surface_bssrdf()) {
@@ -871,6 +873,7 @@ void SVMCompiler::compile(Shader *shader, array<int4> &svm_nodes, int index, Sum
current_shader = shader;
shader->has_surface = false;
shader->has_surface_emission = false;
shader->has_surface_transparent = false;
shader->has_surface_raytrace = false;
shader->has_surface_bssrdf = false;
@@ -925,9 +928,6 @@ void SVMCompiler::compile(Shader *shader, array<int4> &svm_nodes, int index, Sum
summary->peak_stack_usage = max_stack_use;
summary->num_svm_nodes = svm_nodes.size() - start_num_svm_nodes;
}
/* Estimate emission for MIS. */
shader->estimate_emission();
}
/* Compiler summary implementation. */

View File

@@ -85,7 +85,7 @@ class DisplayDriver {
int buffer_height = 0;
/* OpenGL pixel buffer object. */
int64_t opengl_pbo_id = 0;
int opengl_pbo_id = 0;
/* Clear the entire buffer before doing partial write to it. */
bool need_clear = false;

View File

@@ -248,7 +248,6 @@ class InfoPropertyRNA:
"collection_type",
"type",
"fixed_type",
"subtype",
"is_argument_optional",
"is_enum_flag",
"is_required",
@@ -273,7 +272,6 @@ class InfoPropertyRNA:
self.array_length = getattr(rna_prop, "array_length", 0)
self.array_dimensions = getattr(rna_prop, "array_dimensions", ())[:]
self.collection_type = GetInfoStructRNA(rna_prop.srna)
self.subtype = getattr(rna_prop, "subtype", "")
self.is_required = rna_prop.is_required
self.is_readonly = rna_prop.is_readonly
self.is_never_none = rna_prop.is_never_none
@@ -360,7 +358,6 @@ class InfoPropertyRNA:
as_ret=False,
as_arg=False,
class_fmt="%s",
mathutils_fmt="%s",
collection_id="Collection",
enum_descr_override=None,
):
@@ -374,31 +371,11 @@ class InfoPropertyRNA:
type_str += self.type
if self.array_length:
if self.array_dimensions[1] != 0:
dimension_str = " of %s items" % (
type_str += " multi-dimensional array of %s items" % (
" * ".join(str(d) for d in self.array_dimensions if d != 0)
)
type_str += " multi-dimensional array" + dimension_str
else:
dimension_str = " of %d items" % (self.array_length)
type_str += " array" + dimension_str
# Describe mathutils types; logic mirrors pyrna_math_object_from_array
if self.type == "float":
if self.subtype == "MATRIX":
if self.array_length in {9, 16}:
type_str = (mathutils_fmt % "Matrix") + dimension_str
elif self.subtype in {"COLOR", "COLOR_GAMMA"}:
if self.array_length == 3:
type_str = (mathutils_fmt % "Color") + dimension_str
elif self.subtype in {"EULER", "QUATERNION"}:
if self.array_length == 3:
type_str = (mathutils_fmt % "Euler") + " rotation" + dimension_str
elif self.array_length == 4:
type_str = (mathutils_fmt % "Quaternion") + " rotation" + dimension_str
elif self.subtype in {"COORDINATES", "TRANSLATION", "DIRECTION", "VELOCITY",
"ACCELERATION", "XYZ", "XYZ_LENGTH"}:
if 2 <= self.array_length <= 4:
type_str = (mathutils_fmt % "Vector") + dimension_str
type_str += " array of %d items" % (self.array_length)
if self.type in {"float", "int"}:
type_str += " in [%s, %s]" % (range_str(self.min), range_str(self.max))

View File

@@ -939,6 +939,8 @@ def km_mask_editing(params):
op_menu("MASK_MT_add", {"type": 'A', "value": 'PRESS', "shift": True}),
*_template_items_proportional_editing(
params, connected=False, toggle_data_path='tool_settings.use_proportional_edit_mask'),
("mask.add_vertex_slide", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None),
("mask.add_feather_vertex_slide", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True}, None),
("mask.delete", {"type": 'X', "value": 'PRESS'}, None),
("mask.delete", {"type": 'DEL', "value": 'PRESS'}, None),
("mask.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True},
@@ -960,6 +962,8 @@ def km_mask_editing(params):
*_template_items_hide_reveal_actions("mask.hide_view_set", "mask.hide_view_clear"),
("clip.select", {"type": params.select_mouse, "value": 'PRESS', "ctrl": True}, None),
("mask.cyclic_toggle", {"type": 'C', "value": 'PRESS', "alt": True}, None),
("mask.slide_point", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("mask.slide_spline_curvature", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("mask.handle_type_set", {"type": 'V', "value": 'PRESS'}, None),
("mask.normals_make_consistent",
{"type": 'N', "value": 'PRESS', "ctrl" if params.legacy else "shift": True}, None),
@@ -3185,6 +3189,10 @@ def km_clip_editor(params):
("clip.frame_jump", {"type": 'RIGHT_ARROW', "value": 'PRESS', "shift": True, "alt": True, "repeat": True},
{"properties": [("position", 'PATHSTART')]}),
("clip.change_frame", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("clip.select", {"type": params.select_mouse, "value": 'PRESS'},
{"properties": [("deselect_all", not params.legacy)]}),
("clip.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True},
{"properties": [("extend", True)]}),
*_template_items_select_actions(params, "clip.select_all"),
("clip.select_box", {"type": 'B', "value": 'PRESS'}, None),
("clip.select_circle", {"type": 'C', "value": 'PRESS'}, None),
@@ -3193,8 +3201,10 @@ def km_clip_editor(params):
{"properties": [("mode", 'ADD')]}),
("clip.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True, "alt": True},
{"properties": [("mode", 'SUB')]}),
("clip.add_marker_slide", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None),
("clip.delete_marker", {"type": 'X', "value": 'PRESS', "shift": True}, None),
("clip.delete_marker", {"type": 'DEL', "value": 'PRESS', "shift": True}, None),
("clip.slide_marker", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("clip.disable_markers", {"type": 'D', "value": 'PRESS', "shift": True},
{"properties": [("action", 'TOGGLE')]}),
("clip.delete_track", {"type": 'X', "value": 'PRESS'}, None),
@@ -3204,6 +3214,7 @@ def km_clip_editor(params):
("clip.lock_tracks", {"type": 'L', "value": 'PRESS', "alt": True},
{"properties": [("action", 'UNLOCK')]}),
*_template_items_hide_reveal_actions("clip.hide_tracks", "clip.hide_tracks_clear"),
("clip.slide_plane_marker", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None),
("clip.keyframe_insert", {"type": 'I', "value": 'PRESS'}, None),
("clip.keyframe_delete", {"type": 'I', "value": 'PRESS', "alt": True}, None),
("clip.join_tracks", {"type": 'J', "value": 'PRESS', "ctrl": True}, None),
@@ -3927,7 +3938,7 @@ def km_grease_pencil_stroke_sculpt_mode(params):
items.extend([
# Selection
*_grease_pencil_selection(params, use_select_mouse=(params.use_fallback_tool_select_mouse == False)),
*_grease_pencil_selection(params, use_select_mouse=False),
# Brush strength
("wm.radial_control", {"type": 'F', "value": 'PRESS', "shift": True},
@@ -4218,7 +4229,7 @@ def km_grease_pencil_stroke_vertex_mode(params):
items.extend([
# Selection
*_grease_pencil_selection(params, use_select_mouse=(params.use_fallback_tool_select_mouse == False)),
*_grease_pencil_selection(params, use_select_mouse=False),
# Brush strength
("wm.radial_control", {"type": 'F', "value": 'PRESS', "shift": True},
{"properties": [
@@ -6437,72 +6448,6 @@ def km_generic_tool_annotate_eraser(params):
)
def km_generic_tool_mask_select(params):
return (
"Mask Editing: Tweak",
{"space_type": 'EMPTY', "region_type": 'WINDOW'},
{"items": [
("mask.select", {"type": params.select_mouse, "value": 'PRESS'},
{"properties": [("extend", False), ("deselect_all", not params.legacy)]}),
]},
)
def km_generic_tool_mask_select_box(params):
return (
"Mask Editing: Select Box",
{"space_type": 'EMPTY', "region_type": 'WINDOW'},
{"items": _template_items_tool_select_actions_simple(
"mask.select_box", type=params.tool_mouse, value='CLICK_DRAG',
)},
)
def km_generic_tool_mask_select_lasso(params):
return (
"Mask Editing: Select Lasso",
{"space_type": 'EMPTY', "region_type": 'WINDOW'},
{"items": _template_items_tool_select_actions_simple(
"mask.select_lasso", type=params.tool_mouse, value='PRESS',
)},
)
def km_generic_tool_mask_select_circle(params):
return (
"Mask Editing: Select Circle",
{"space_type": 'EMPTY', "region_type": 'WINDOW'},
{"items": _template_items_tool_select_actions_simple(
"mask.select_circle", type=params.tool_mouse, value='PRESS',
properties=[("wait_for_input", False)],
)},
)
def km_generic_tool_mask_add_vertex(params):
return (
"Mask Editing: Add Vertex and Slide",
{"space_type": 'EMPTY', "region_type": 'WINDOW'},
{"items": [
("mask.draw_mask", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("mask.add_feather_vertex_slide", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None),
]},
)
def km_generic_tool_mask_add_feather_vertex(params):
return (
"Mask Editing: Add Feather Vertex and Slide",
{"space_type": 'EMPTY', "region_type": 'WINDOW'},
{"items": [
("mask.slide_point", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("mask.draw_mask", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}, None),
("mask.add_feather_vertex_slide", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None),
("mask.slide_spline_curvature", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
]},
)
def km_image_editor_tool_generic_sample(params):
return (
"Image Editor Tool: Sample",
@@ -6652,111 +6597,6 @@ def km_image_editor_tool_uv_scale(params):
)
# ------------------------------------------------------------------------------
# Tool System (Clip Editor)
def _template_items_clip_tool_mouse_selection(params, use_release=False):
return [
("clip.select", {"type": params.select_mouse, "value": 'RELEASE' if use_release else 'PRESS'},
{"properties": [
("extend", False),
("deselect_all", True),
("activate_selected", params.select_mouse == 'LEFTMOUSE')]}
),
("clip.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True},
{"properties": [("extend", True)]}),
]
def _template_items_clip_tool_tweak_selection(params):
items = [
*_template_items_clip_tool_mouse_selection(params),
]
if params.select_mouse == 'RIGHTMOUSE':
items.append(
("clip.select", {"type": 'LEFTMOUSE', "value": 'PRESS'},
{"properties": [
("extend", False),
("deselect_all", True),
("activate_selected", True),
]
}
)
)
return items
def _template_items_clip_tool_tweak(params):
return [
("clip.change_frame", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
*_template_items_clip_tool_tweak_selection(params),
("clip.slide_marker", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("clip.slide_plane_marker", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
]
def km_clip_editor_tool_select(params):
return [
"Clip Editor: Tweak",
{"space_type": 'CLIP_EDITOR', "region_type": 'WINDOW'},
{"items": _template_items_clip_tool_tweak(params)},
]
def km_clip_editor_tool_select_box(params):
return (
"Clip Editor: Select Box",
{"space_type": 'CLIP_EDITOR', "region_type": 'WINDOW'},
{"items": [
*_template_items_clip_tool_mouse_selection(params, params.select_mouse == 'LEFTMOUSE'),
*_template_items_tool_select_actions_simple(
"clip.select_box", type=params.tool_mouse, value='CLICK_DRAG'),
]
},
)
def km_clip_editor_tool_select_lasso(params):
return (
"Clip Editor: Select Lasso",
{"space_type": 'CLIP_EDITOR', "region_type": 'WINDOW'},
{"items": [
*_template_items_clip_tool_mouse_selection(params, params.select_mouse == 'LEFTMOUSE'),
*_template_items_tool_select_actions_simple(
"clip.select_lasso", type=params.tool_mouse, value='PRESS'),
]
},
)
def km_clip_editor_tool_select_circle(params):
return (
"Clip Editor: Select Circle",
{"space_type": 'CLIP_EDITOR', "region_type": 'WINDOW'},
{"items": [
*_template_items_clip_tool_mouse_selection(params, params.select_mouse == 'LEFTMOUSE'),
*_template_items_tool_select_actions_simple(
"clip.select_circle", type=params.tool_mouse, value='PRESS',
properties=[("wait_for_input", False)]),
]
},
)
def km_clip_editor_tool_add_marker_tweak(params):
return (
"Clip Editor: Add Marker and Tweak",
{"space_type": 'CLIP_EDITOR', "region_type": 'WINDOW'},
{"items": [
*_template_items_clip_tool_tweak(params),
("clip.add_marker_slide", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
]
},
)
# ------------------------------------------------------------------------------
# Tool System (Node Editor)
@@ -8236,13 +8076,6 @@ def generate_keymaps(params=None):
km_generic_tool_annotate_polygon(params),
km_generic_tool_annotate_eraser(params),
km_generic_tool_mask_select(params),
km_generic_tool_mask_select_box(params),
km_generic_tool_mask_select_lasso(params),
km_generic_tool_mask_select_circle(params),
km_generic_tool_mask_add_vertex(params),
km_generic_tool_mask_add_feather_vertex(params),
km_image_editor_tool_generic_sample(params),
km_image_editor_tool_uv_cursor(params),
*(km_image_editor_tool_uv_select(params, fallback=fallback) for fallback in (False, True)),
@@ -8253,11 +8086,6 @@ def generate_keymaps(params=None):
km_image_editor_tool_uv_sculpt_stroke(params),
km_image_editor_tool_uv_move(params),
km_image_editor_tool_uv_rotate(params),
km_clip_editor_tool_select(params),
km_clip_editor_tool_select_box(params),
km_clip_editor_tool_select_lasso(params),
km_clip_editor_tool_select_circle(params),
km_clip_editor_tool_add_marker_tweak(params),
km_image_editor_tool_uv_scale(params),
*(km_node_editor_tool_select(params, fallback=fallback) for fallback in (False, True)),
*(km_node_editor_tool_select_box(params, fallback=fallback) for fallback in (False, True)),

View File

@@ -17,7 +17,6 @@ _modules = [
"file",
"geometry_nodes",
"image",
"mask",
"mesh",
"node",
"object",

View File

@@ -252,14 +252,9 @@ class NLA_OT_bake(Operator):
do_pose = 'POSE' in self.bake_types
do_object = 'OBJECT' in self.bake_types
if do_pose and self.only_selected:
pose_bones = context.selected_pose_bones or []
armatures = {pose_bone.id_data for pose_bone in pose_bones}
objects = list(armatures)
else:
objects = context.selected_editable_objects
if do_pose and not do_object:
objects = [obj for obj in objects if obj.pose is not None]
objects = context.selected_editable_objects
if do_pose and not do_object:
objects = [obj for obj in objects if obj.pose is not None]
object_action_pairs = (
[(obj, getattr(obj.animation_data, "action", None)) for obj in objects]
@@ -287,12 +282,8 @@ class NLA_OT_bake(Operator):
def invoke(self, context, _event):
scene = context.scene
if scene.use_preview_range:
self.frame_start = scene.frame_preview_start
self.frame_end = scene.frame_preview_end
else:
self.frame_start = scene.frame_start
self.frame_end = scene.frame_end
self.frame_start = scene.frame_start
self.frame_end = scene.frame_end
self.bake_types = {'POSE'} if context.mode == 'POSE' else {'OBJECT'}
wm = context.window_manager

View File

@@ -97,12 +97,13 @@ class ASSET_OT_open_containing_blend_file(Operator):
def execute(self, context):
asset_file_handle = context.asset_file_handle
asset_library_ref = context.asset_library_ref
if asset_file_handle.local_id:
self.report({'WARNING'}, "This asset is stored in the current blend file")
return {'CANCELLED'}
asset_lib_path = bpy.types.AssetHandle.get_full_library_path(asset_file_handle)
asset_lib_path = bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library_ref)
self.open_in_new_blender(asset_lib_path)
wm = context.window_manager

View File

@@ -1,56 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# <pep8-80 compliant>
import bpy
from bpy.types import Operator
from bpy.props import EnumProperty
class MASK_OT_draw_mask(Operator):
"""Smart code to draw a mask"""
bl_label = "Draw a mask"
bl_idname = "mask.draw_mask"
@classmethod
def poll(cls, context):
if context.space_data.type == 'CLIP_EDITOR':
clip = context.space_data.clip
return clip
else:
return True
type: EnumProperty(
name="Type",
items=(
('AUTO', "Auto", ""),
('VECTOR', "Vector", ""),
('ALIGNED', "Aligned Single", ""),
('ALIGNED_DOUBLESIDE', "Aligned", ""),
('FREE', "Free", ""),
),
)
def execute(self, context):
return bpy.ops.mask.add_vertex_slide(
MASK_OT_add_vertex={
"type": self.type,
}
)
def invoke(self, context, _event):
bpy.ops.mask.add_vertex_slide(
'INVOKE_REGION_WIN',
MASK_OT_add_vertex={
"type": self.type,
}
)
# ignore return from operators above because they are 'RUNNING_MODAL',
# and cause this one not to be freed. T24671.
return {'FINISHED'}
classes = (
MASK_OT_draw_mask,
)

View File

@@ -372,10 +372,19 @@ class AnnotationDataPanel:
layout = self.layout
layout.use_property_decorate = False
is_clip_editor = context.space_data.type == 'CLIP_EDITOR'
# Grease Pencil owner.
gpd_owner = context.annotation_data_owner
gpd = context.annotation_data
# Owner selector.
if is_clip_editor:
col = layout.column()
col.label(text="Data Source:")
row = col.row()
row.prop(context.space_data, "annotation_source", expand=True)
# Only allow adding annotation ID if its owner exist
if context.annotation_data_owner is None:
row = layout.row()

View File

@@ -247,6 +247,64 @@ class MASK_PT_display:
row.prop(space_data, "blend_factor", text="Blending Factor")
class MASK_PT_transforms:
# subclasses must define...
# ~ bl_space_type = 'CLIP_EDITOR'
# ~ bl_region_type = 'TOOLS'
bl_label = "Transforms"
bl_category = "Mask"
@classmethod
def poll(cls, context):
space_data = context.space_data
return space_data.mask and space_data.mode == 'MASK'
def draw(self, _context):
layout = self.layout
col = layout.column(align=True)
col.label(text="Transform:")
col.operator("transform.translate")
col.operator("transform.rotate")
col.operator("transform.resize", text="Scale")
col.operator("transform.transform", text="Scale Feather").mode = 'MASK_SHRINKFATTEN'
class MASK_PT_tools:
bl_label = "Mask Tools"
bl_category = "Mask"
@classmethod
def poll(cls, context):
space_data = context.space_data
return space_data.mask and space_data.mode == 'MASK'
def draw(self, _context):
layout = self.layout
col = layout.column(align=True)
col.label(text="Spline:")
col.operator("mask.delete")
col.operator("mask.cyclic_toggle")
col.operator("mask.switch_direction")
col.operator("mask.handle_type_set").type = 'VECTOR'
col.operator("mask.feather_weight_clear")
col = layout.column(align=True)
col.label(text="Parenting:")
row = col.row(align=True)
row.operator("mask.parent_set", text="Parent")
row.operator("mask.parent_clear", text="Clear")
col = layout.column(align=True)
col.label(text="Animation:")
row = col.row(align=True)
row.operator("mask.shape_key_insert", text="Insert Key")
row.operator("mask.shape_key_clear", text="Clear Key")
col.operator("mask.shape_key_feather_reset", text="Reset Feather Animation")
col.operator("mask.shape_key_rekey", text="Re-Key Shape Points")
class MASK_MT_mask(Menu):
bl_label = "Mask"

View File

@@ -8,12 +8,10 @@ from bpy.app.translations import (
)
from bl_ui.utils import PresetPanel
from bl_ui.properties_grease_pencil_common import (
AnnotationDrawingToolsPanel,
AnnotationDataPanel,
)
from bl_ui.space_toolsystem_common import (
ToolActivePanelHelper,
)
class CLIP_UL_tracking_objects(UIList):
def draw_item(self, _context, layout, _data, item, _icon,
@@ -112,98 +110,22 @@ class CLIP_PT_clip_display(Panel):
col.prop(clip, "display_aspect", text="Display Aspect Ratio")
class CLIP_PT_active_tool(ToolActivePanelHelper, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'UI'
bl_category = "Tool"
class CLIP_HT_tool_header(Header):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'TOOL_HEADER'
def draw(self, context):
layout = self.layout
sc = context.space_data
if sc.view == 'CLIP':
self.draw_tool_settings(context)
layout.separator_spacer()
CLIP_HT_header.draw_xform_template(layout, context)
layout.separator_spacer()
self.draw_mode_settings(context)
def draw_tool_settings(self, context):
layout = self.layout
# Active Tool
# -----------
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
tool = ToolSelectPanelHelper.draw_active_tool_header(context, layout)
tool_mode = context.mode if tool is None else tool.mode
if tool_mode == 'TRACKING':
clip = context.space_data.clip
if clip:
layout.separator()
CLIP_PT_tracking_settings_presets.draw_menu(self.layout)
def draw_mode_settings(self, context):
layout = self.layout
# Active Tool
# -----------
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
tool = ToolSelectPanelHelper.tool_active_from_context(context)
tool_mode = context.mode if tool is None else tool.mode
clip = context.space_data.clip
if clip:
if tool_mode == 'TRACKING':
settings = clip.tracking.settings
layout.prop(settings, "speed", text="")
class CLIP_HT_header(Header):
bl_space_type = 'CLIP_EDITOR'
@staticmethod
def draw_xform_template(layout, context):
sc = context.space_data
tool_settings = context.tool_settings
layout.prop(sc, "pivot_point", text="", icon_only=True)
row = layout.row(align=True)
row.prop(tool_settings, "use_proportional_edit_mask", text="", icon_only=True)
sub = row.row(align=True)
sub.active = tool_settings.use_proportional_edit_mask
sub.prop(tool_settings, "proportional_edit_falloff", text="", icon_only=True)
def _draw_tracking(self, context):
layout = self.layout
sc = context.space_data
clip = sc.clip
show_region_tool_header = sc.show_region_tool_header
CLIP_MT_tracking_editor_menus.draw_collapsible(context, layout)
layout.separator_spacer()
if not show_region_tool_header:
if sc.view == 'CLIP':
CLIP_HT_header.draw_xform_template(layout, context)
row = layout.row()
if sc.view == 'CLIP':
row.template_ID(sc, "clip", open="clip.open")
row = layout.row(align=True)
row.popover(panel='CLIP_PT_solve_panel')
row.operator("clip.solve_camera", text="", icon='PLAY')
else:
row = layout.row(align=True)
props = row.operator("clip.refine_markers", text="", icon='TRACKING_REFINE_BACKWARDS')
@@ -244,15 +166,14 @@ class CLIP_HT_header(Header):
if sc.view == 'CLIP':
r = active_object.reconstruction
if r.is_valid and sc.view == 'CLIP':
layout.label(text=iface_("Solve error: %.2f px") %
(r.average_error),
translate=False)
object_icon = 'CAMERA_DATA' if active_object.is_camera else 'OBJECT_DATA'
row = layout.row()
row.popover(panel='CLIP_PT_objects', text=active_object.name, icon=object_icon)
row.prop(sc, "pivot_point", text="", icon_only=True)
row = layout.row(align=True)
icon = 'LOCKED' if sc.lock_selection else 'UNLOCKED'
row.operator("clip.lock_selection_toggle", icon=icon, text="", depress=sc.lock_selection)
@@ -288,6 +209,7 @@ class CLIP_HT_header(Header):
def _draw_masking(self, context):
layout = self.layout
tool_settings = context.tool_settings
sc = context.space_data
clip = sc.clip
@@ -295,20 +217,24 @@ class CLIP_HT_header(Header):
layout.separator_spacer()
if not sc.show_region_tool_header:
CLIP_HT_header.draw_xform_template(layout, context)
row = layout.row()
row.template_ID(sc, "clip", open="clip.open")
layout.separator_spacer()
if clip:
layout.prop(sc, "pivot_point", text="", icon_only=True)
row = layout.row(align=True)
row.prop(tool_settings, "use_proportional_edit_mask", text="", icon_only=True)
sub = row.row(align=True)
sub.active = tool_settings.use_proportional_edit_mask
sub.prop(tool_settings, "proportional_edit_falloff", text="", icon_only=True)
row = layout.row()
row.template_ID(sc, "mask", new="mask.new")
row.popover(panel='CLIP_PT_mask_display')
row = layout.row()
row.popover(panel='CLIP_PT_objects')
row = layout.row(align=True)
icon = 'LOCKED' if sc.lock_selection else 'UNLOCKED'
row.operator("clip.lock_selection_toggle", icon=icon, text="", depress=sc.lock_selection)
@@ -410,10 +336,10 @@ class CLIP_PT_reconstruction_panel:
class CLIP_PT_tools_clip(Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'UI'
bl_region_type = 'TOOLS'
bl_label = "Clip"
bl_translation_context = bpy.app.translations.contexts.id_movieclip
bl_category = "Footage"
bl_category = "Track"
@classmethod
def poll(cls, context):
@@ -587,19 +513,11 @@ class CLIP_PT_tools_plane_tracking(CLIP_PT_tracking_panel, Panel):
layout.operator("clip.create_plane_track")
class CLIP_PT_solve_panel(Panel):
class CLIP_PT_tools_solve(CLIP_PT_tracking_panel, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'HEADER'
bl_region_type = 'TOOLS'
bl_label = "Solve"
def draw(self, _context):
pass
class CLIP_PT_tools_solve(Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'HEADER'
bl_label = "Solve"
bl_parent_id = 'CLIP_PT_solve_panel'
bl_category = "Solve"
def draw(self, context):
layout = self.layout
@@ -610,7 +528,7 @@ class CLIP_PT_tools_solve(Panel):
tracking = clip.tracking
settings = tracking.settings
tracking_object = tracking.objects.active
camera = tracking.camera
camera = clip.tracking.camera
col = layout.column()
col.prop(settings, "use_tripod_solver", text="Tripod")
@@ -638,21 +556,17 @@ class CLIP_PT_tools_solve(Panel):
col = layout.column(align=True)
col.scale_y = 2.0
col.operator("clip.solve_camera",
text="Solve Camera Motion" if tracking_object.is_camera
else "Solve Object Motion")
class CLIP_PT_cleanup(CLIP_PT_tracking_panel, Panel):
class CLIP_PT_tools_cleanup(CLIP_PT_tracking_panel, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'UI'
bl_region_type = 'TOOLS'
bl_label = "Clean Up"
bl_category = "Reconstruction"
@classmethod
def poll(cls, context):
if CLIP_PT_clip_view_panel.poll(context):
sc = context.space_data
return sc.mode == 'TRACKING' and sc.clip
return False
bl_options = {'DEFAULT_CLOSED'}
bl_category = "Solve"
def draw(self, context):
layout = self.layout
@@ -663,25 +577,20 @@ class CLIP_PT_cleanup(CLIP_PT_tracking_panel, Panel):
settings = clip.tracking.settings
col = layout.column()
col.prop(settings, "clean_frames", text="Frames")
col.prop(settings, "clean_error", text="Error")
col.prop(settings, "clean_action", text="Type")
col.separator()
col.operator("clip.clean_tracks")
col.operator("clip.filter_tracks")
class CLIP_PT_geometry(CLIP_PT_tracking_panel, Panel):
class CLIP_PT_tools_geometry(CLIP_PT_tracking_panel, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'UI'
bl_region_type = 'TOOLS'
bl_label = "Geometry"
bl_options = {'DEFAULT_CLOSED'}
bl_category = "Reconstruction"
@classmethod
def poll(cls, context):
if CLIP_PT_clip_view_panel.poll(context):
sc = context.space_data
return sc.mode == 'TRACKING' and sc.clip
return False
bl_category = "Solve"
def draw(self, _context):
layout = self.layout
@@ -690,20 +599,11 @@ class CLIP_PT_geometry(CLIP_PT_tracking_panel, Panel):
layout.operator("clip.track_to_empty")
class CLIP_PT_orientation(CLIP_PT_reconstruction_panel, Panel):
class CLIP_PT_tools_orientation(CLIP_PT_tracking_panel, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'UI'
bl_region_type = 'TOOLS'
bl_label = "Orientation"
bl_category = "Reconstruction"
@classmethod
def poll(cls, context):
if CLIP_PT_clip_view_panel.poll(context):
sc = context.space_data
return sc.mode == 'TRACKING' and sc.clip
return False
bl_category = "Solve"
def draw(self, context):
layout = self.layout
@@ -711,9 +611,7 @@ class CLIP_PT_orientation(CLIP_PT_reconstruction_panel, Panel):
layout.use_property_decorate = False
sc = context.space_data
clip = sc.clip
tracking_object = clip.tracking.objects.active
settings = clip.tracking.settings
settings = sc.clip.tracking.settings
col = layout.column(align=True)
@@ -732,29 +630,60 @@ class CLIP_PT_orientation(CLIP_PT_reconstruction_panel, Panel):
col = layout.column()
row = col.row(align=True)
if tracking_object.is_camera:
row.operator("clip.set_scale")
col.prop(settings, "distance")
else:
row.operator("clip.set_solution_scale", text="Set Scale")
col.prop(settings, "object_distance")
row.operator("clip.set_scale")
row.operator("clip.apply_solution_scale", text="Apply Scale")
col.prop(settings, "distance")
class CLIP_PT_tools_object(CLIP_PT_reconstruction_panel, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'TOOLS'
bl_label = "Object"
bl_category = "Solve"
@classmethod
def poll(cls, context):
sc = context.space_data
if CLIP_PT_reconstruction_panel.poll(context) and sc.mode == 'TRACKING':
clip = sc.clip
tracking_object = clip.tracking.objects.active
return not tracking_object.is_camera
return False
def draw(self, context):
layout = self.layout
sc = context.space_data
clip = sc.clip
tracking_object = clip.tracking.objects.active
settings = sc.clip.tracking.settings
col = layout.column()
col.prop(tracking_object, "scale")
col.separator()
col.operator("clip.set_solution_scale", text="Set Scale")
col.prop(settings, "object_distance")
class CLIP_PT_objects(CLIP_PT_clip_view_panel, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'HEADER'
bl_region_type = 'UI'
bl_category = "Track"
bl_label = "Objects"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
sc = context.space_data
tracking = sc.clip.tracking
tracking_object = sc.clip.tracking.objects.active
row = layout.row()
row.template_list("CLIP_UL_tracking_objects", "", tracking, "objects",
@@ -765,10 +694,6 @@ class CLIP_PT_objects(CLIP_PT_clip_view_panel, Panel):
sub.operator("clip.tracking_object_new", icon='ADD', text="")
sub.operator("clip.tracking_object_remove", icon='REMOVE', text="")
if not tracking_object.is_camera:
row = layout.row()
row.prop(tracking_object, "scale")
class CLIP_PT_track(CLIP_PT_tracking_panel, Panel):
bl_space_type = 'CLIP_EDITOR'
@@ -850,20 +775,13 @@ class CLIP_PT_plane_track(CLIP_PT_tracking_panel, Panel):
clip = context.space_data.clip
active_track = clip.tracking.plane_tracks.active
row = layout.row()
col = row.column()
col.separator()
col.operator("clip.create_plane_track")
row = layout.row()
if not active_track:
row.active = False
row.label(text="No active plane track")
layout.active = False
layout.label(text="No active plane track")
return
layout.prop(active_track, "name")
layout.prop(active_track, "use_auto_keying", text="")
layout.prop(active_track, "use_auto_keying")
row = layout.row()
row.template_ID(
active_track, "image", new="image.new", open="image.open")
@@ -902,34 +820,6 @@ class CLIP_PT_track_settings(CLIP_PT_tracking_panel, Panel):
col.prop(active, "use_normalization")
class CLIP_PT_track_settings_tool(CLIP_PT_tracking_panel, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'HEADER'
bl_label = "Extras"
@classmethod
def poll(cls, context):
clip = context.space_data.clip
return clip
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
clip = context.space_data.clip
settings = clip.tracking.settings
col = layout.column(align=True)
col.prop(settings, "default_correlation_min")
col.prop(settings, "default_margin")
col = layout.column()
col.prop(settings, "default_frames_limit")
col.prop(settings, "use_default_mask")
class CLIP_PT_track_settings_extras(CLIP_PT_tracking_panel, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'UI'
@@ -958,8 +848,9 @@ class CLIP_PT_track_settings_extras(CLIP_PT_tracking_panel, Panel):
col.prop(active, "margin")
col = layout.column()
col.prop(active, "frames_limit")
col.prop(active, "use_mask")
col.prop(active, "frames_limit")
col.prop(settings, "speed")
class CLIP_PT_tracking_camera(Panel):
@@ -1000,6 +891,7 @@ class CLIP_PT_tracking_lens(Panel):
bl_category = "Track"
bl_label = "Lens"
bl_parent_id = 'CLIP_PT_tracking_camera'
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
@@ -1273,35 +1165,45 @@ from bl_ui.properties_mask_common import (
MASK_PT_spline,
MASK_PT_point,
MASK_PT_display,
MASK_PT_transforms,
MASK_PT_tools
)
class CLIP_PT_mask_layers(MASK_PT_layers, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'UI'
bl_category = 'Mask'
class CLIP_PT_mask_display(MASK_PT_display, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'HEADER'
bl_category = "Mask"
class CLIP_PT_active_mask_spline(MASK_PT_spline, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'UI'
bl_category = 'Mask'
bl_category = "Mask"
class CLIP_PT_active_mask_point(MASK_PT_point, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'UI'
bl_category = 'Mask'
bl_category = "Mask"
class CLIP_PT_mask(MASK_PT_mask, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'UI'
bl_category = 'Mask'
bl_category = "Mask"
class CLIP_PT_tools_mask_transforms(MASK_PT_transforms, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'TOOLS'
bl_category = "Mask"
class CLIP_PT_tools_mask_tools(MASK_PT_tools, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'TOOLS'
bl_category = "Mask"
class CLIP_PT_mask_display(MASK_PT_display, Panel):
@@ -1332,12 +1234,12 @@ class CLIP_PT_footage(CLIP_PT_clip_view_panel, Panel):
col.template_movieclip_information(sc, "clip", sc.clip_user)
class CLIP_PT_scenesetup(Panel):
class CLIP_PT_tools_scenesetup(Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'UI'
bl_region_type = 'TOOLS'
bl_label = "Scene Setup"
bl_translation_context = bpy.app.translations.contexts.id_movieclip
bl_category = "Reconstruction"
bl_category = "Solve"
@classmethod
def poll(cls, context):
@@ -1364,6 +1266,12 @@ class CLIP_PT_annotation(AnnotationDataPanel, CLIP_PT_clip_view_panel, Panel):
# But, this should only be visible in "clip" view
# Grease Pencil drawing tools
class CLIP_PT_tools_grease_pencil_draw(AnnotationDrawingToolsPanel, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'TOOLS'
class CLIP_MT_view_zoom(Menu):
bl_label = "Fractional Zoom"
@@ -1394,7 +1302,6 @@ class CLIP_MT_view(Menu):
if sc.view == 'CLIP':
layout.prop(sc, "show_region_ui")
layout.prop(sc, "show_region_toolbar")
layout.prop(sc, "show_region_tool_header")
layout.prop(sc, "show_region_hud")
layout.separator()
@@ -1449,6 +1356,11 @@ class CLIP_MT_clip(Menu):
layout.operator("clip.reload")
layout.menu("CLIP_MT_proxy")
layout.separator()
layout.operator("clip.set_viewport_background")
layout.operator("clip.setup_tracking_scene")
class CLIP_MT_proxy(Menu):
bl_label = "Proxy"
@@ -1567,6 +1479,9 @@ class CLIP_MT_track(Menu):
def draw(self, context):
layout = self.layout
clip = context.space_data.clip
tracking_object = clip.tracking.objects.active
layout.menu("CLIP_MT_track_transform")
layout.menu("CLIP_MT_track_motion")
layout.menu("CLIP_MT_track_clear")
@@ -1576,6 +1491,7 @@ class CLIP_MT_track(Menu):
layout.operator("clip.add_marker_move", text="Add Marker")
layout.operator("clip.detect_features")
layout.operator("clip.create_plane_track")
layout.separator()
layout.operator("clip.new_image_from_plane_marker")
@@ -1583,6 +1499,13 @@ class CLIP_MT_track(Menu):
layout.separator()
layout.operator(
"clip.solve_camera",
text=(
"Solve Camera Motion" if tracking_object.is_camera else
"Solve Object Motion"
),
)
layout.separator()
@@ -1617,10 +1540,8 @@ class CLIP_MT_track(Menu):
class CLIP_MT_reconstruction(Menu):
bl_label = "Reconstruction"
def draw(self, context):
def draw(self, _context):
layout = self.layout
clip = context.space_data.clip
tracking_object = clip.tracking.objects.active
layout.operator("clip.set_origin")
layout.operator("clip.set_plane", text="Set Floor").plane = 'FLOOR'
@@ -1629,24 +1550,6 @@ class CLIP_MT_reconstruction(Menu):
layout.operator("clip.set_axis", text="Set X Axis").axis = 'X'
layout.operator("clip.set_axis", text="Set Y Axis").axis = 'Y'
layout.separator()
layout.operator(
"clip.solve_camera",
text=(
"Solve Camera Motion" if tracking_object.is_camera else
"Solve Object Motion"
),
)
layout.operator("clip.create_plane_track")
layout.separator()
layout.operator("clip.set_viewport_background")
layout.operator("clip.setup_tracking_scene")
layout.separator()
layout.operator("clip.set_scale")
layout.operator("clip.apply_solution_scale")
@@ -1707,6 +1610,10 @@ class CLIP_MT_tracking_context_menu(Menu):
layout.separator()
layout.operator("clip.track_copy_color")
layout.separator()
layout.operator("clip.copy_tracks", icon='COPYDOWN')
layout.operator("clip.paste_tracks", icon='PASTEDOWN')
@@ -1733,22 +1640,10 @@ class CLIP_MT_tracking_context_menu(Menu):
layout.operator("clip.join_tracks")
layout.operator("clip.average_tracks")
layout.separator()
layout.operator("clip.filter_tracks")
layout.operator("clip.clean_tracks")
layout.separator()
layout.operator("clip.delete_track")
layout.separator()
layout.operator("clip.track_copy_color")
layout.separator()
layout.operator("clip.create_plane_track")
elif mode == 'MASK':
from .properties_mask_common import draw_mask_context_menu
draw_mask_context_menu(layout, context)
@@ -2032,31 +1927,32 @@ class CLIP_PT_gizmo_display(Panel):
classes = (
CLIP_UL_tracking_objects,
CLIP_HT_header,
CLIP_HT_tool_header,
CLIP_PT_display,
CLIP_PT_clip_display,
CLIP_PT_marker_display,
CLIP_MT_tracking_editor_menus,
CLIP_MT_masking_editor_menus,
CLIP_PT_active_tool,
CLIP_PT_track,
CLIP_PT_solve_panel,
CLIP_PT_tools_clip,
CLIP_PT_tools_marker,
CLIP_PT_tracking_settings,
CLIP_PT_tracking_settings_extras,
CLIP_PT_tools_tracking,
CLIP_PT_tools_plane_tracking,
CLIP_PT_tools_solve,
CLIP_PT_tools_cleanup,
CLIP_PT_tools_geometry,
CLIP_PT_tools_orientation,
CLIP_PT_tools_object,
CLIP_PT_objects,
CLIP_PT_plane_track,
CLIP_PT_track_settings_tool,
CLIP_PT_track_settings,
CLIP_PT_track_settings_extras,
CLIP_PT_orientation,
CLIP_PT_scenesetup,
CLIP_PT_geometry,
CLIP_PT_cleanup,
CLIP_PT_tracking_camera,
CLIP_PT_tracking_lens,
CLIP_PT_marker,
CLIP_PT_proxy,
CLIP_PT_footage,
CLIP_PT_tools_clip,
CLIP_PT_stabilization,
CLIP_PT_2d_cursor,
CLIP_PT_mask,
@@ -2064,10 +1960,11 @@ classes = (
CLIP_PT_mask_display,
CLIP_PT_active_mask_spline,
CLIP_PT_active_mask_point,
CLIP_PT_tools_mask_transforms,
CLIP_PT_tools_mask_tools,
CLIP_PT_tools_scenesetup,
CLIP_PT_annotation,
CLIP_PT_camera_presets,
CLIP_PT_track_color_presets,
CLIP_PT_tracking_settings_presets,
CLIP_PT_tools_grease_pencil_draw,
CLIP_MT_view_zoom,
CLIP_MT_view,
CLIP_MT_clip,
@@ -2085,6 +1982,9 @@ classes = (
CLIP_MT_select_grouped,
CLIP_MT_tracking_context_menu,
CLIP_MT_plane_track_image_context_menu,
CLIP_PT_camera_presets,
CLIP_PT_track_color_presets,
CLIP_PT_tracking_settings_presets,
CLIP_MT_stabilize_2d_context_menu,
CLIP_MT_stabilize_2d_rotation_context_menu,
CLIP_MT_pivot_pie,

View File

@@ -862,7 +862,8 @@ def asset_path_str_get(_self):
if asset_file_handle.local_id:
return "Current File"
return bpy.types.AssetHandle.get_full_library_path(asset_file_handle)
asset_library_ref = bpy.context.asset_library_ref
return bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library_ref)
def register_props():

View File

@@ -737,16 +737,6 @@ class IMAGE_HT_header(Header):
if show_uvedit or show_maskedit:
layout.prop(sima, "pivot_point", icon_only=True)
if show_maskedit:
# Proportional Editing
tool_settings = context.tool_settings
row = layout.row(align=True)
row = layout.row(align=True)
row.prop(tool_settings, "use_proportional_edit_mask", text="", icon_only=True)
sub = row.row(align=True)
sub.active = tool_settings.use_proportional_edit_mask
sub.prop(tool_settings, "proportional_edit_falloff", text="", icon_only=True)
if show_uvedit:
tool_settings = context.tool_settings
@@ -782,7 +772,6 @@ class IMAGE_HT_header(Header):
panel="IMAGE_PT_proportional_edit",
)
def draw(self, context):
layout = self.layout
@@ -829,18 +818,15 @@ class IMAGE_HT_header(Header):
layout.template_ID(sima, "image", new="image.new", open="image.open")
if show_maskedit:
row = layout.row()
row.template_ID(sima, "mask", new="mask.new")
if not show_render:
layout.prop(sima, "use_image_pin", text="", emboss=False)
layout.separator_spacer()
# Masking
if show_maskedit:
row = layout.row()
row.template_ID(sima, "mask", new="mask.new")
row.popover(panel='IMAGE_PT_mask_display')
# Gizmo toggle & popover.
row = layout.row(align=True)
row.prop(sima, "show_gizmo", icon='GIZMO', text="")

View File

@@ -462,17 +462,6 @@ class ToolSelectPanelHelper:
if tool is not None:
tool.refresh_from_context()
return tool
elif space_type == 'CLIP_EDITOR':
space_data = context.space_data
if mode is None:
if space_data is None:
mode = 'TRACKING'
else:
mode = space_data.mode
tool = context.workspace.tools.from_space_clip_mode(mode, create=create)
if tool is not None:
tool.refresh_from_context()
return tool
elif space_type == 'NODE_EDITOR':
space_data = context.space_data
tool = context.workspace.tools.from_space_node(create=create)
@@ -755,10 +744,6 @@ class ToolSelectPanelHelper:
if space_data is None:
space_data = context.space_data
return space_type, space_data.mode
elif space_type == 'CLIP_EDITOR':
if space_data is None:
space_data = context.space_data
return space_type, space_data.mode
elif space_type == 'NODE_EDITOR':
return space_type, None
elif space_type == 'SEQUENCE_EDITOR':

View File

@@ -175,9 +175,6 @@ class _defs_annotate:
elif space_type in {'IMAGE_EDITOR', 'NODE_EDITOR', 'SEQUENCE_EDITOR', 'CLIP_EDITOR'}:
row = layout.row(align=True)
row.prop(tool_settings, "annotation_stroke_placement_view2d", text="Placement")
if (space_type == 'CLIP_EDITOR') and (tool_settings.annotation_stroke_placement_view2d == 'IMAGE'):
row = layout.row(align=True)
row.prop(context.space_data, "annotation_source", text="Data Source", expand=True)
if tool.idname == "builtin.annotate_line":
props = tool.operator_properties("gpencil.annotate")
@@ -2076,7 +2073,6 @@ class _defs_gpencil_paint:
def draw_settings(_context, layout, tool):
props = tool.operator_properties("gpencil.interpolate")
layout.prop(props, "layers")
layout.prop(props, "exclude_breakdowns")
layout.prop(props, "flip")
layout.prop(props, "smooth_factor")
layout.prop(props, "smooth_steps")
@@ -2260,7 +2256,6 @@ class _defs_gpencil_edit:
props = tool.operator_properties("gpencil.interpolate")
layout.prop(props, "layers")
layout.prop(props, "interpolate_selected_only")
layout.prop(props, "exclude_breakdowns")
layout.prop(props, "flip")
layout.prop(props, "smooth_factor")
layout.prop(props, "smooth_steps")
@@ -2447,219 +2442,6 @@ class _defs_gpencil_vertex:
)
class _defs_clip_select:
@ToolDef.from_fn
def select():
return dict(
idname="builtin.select",
label="Tweak",
icon="ops.generic.select",
widget=None,
keymap="Clip Editor: Tweak",
)
@ToolDef.from_fn
def box():
def draw_settings(_context, layout, tool):
props = tool.operator_properties("clip.select_box")
row = layout.row()
row.use_property_split = False
row.prop(props, "mode", text="", expand=True, icon_only=True)
return dict(
idname="builtin.select_box",
label="Select Box",
icon="ops.generic.select_box",
widget=None,
keymap="Clip Editor: Select Box",
draw_settings=draw_settings,
)
@ToolDef.from_fn
def lasso():
def draw_settings(_context, layout, tool):
props = tool.operator_properties("clip.select_lasso")
row = layout.row()
row.use_property_split = False
row.prop(props, "mode", text="", expand=True, icon_only=True)
return dict(
idname="builtin.select_lasso",
label="Select Lasso",
icon="ops.generic.select_lasso",
widget=None,
keymap="Clip Editor: Select Lasso",
draw_settings=draw_settings,
)
@ToolDef.from_fn
def circle():
def draw_settings(_context, layout, tool):
props = tool.operator_properties("clip.select_circle")
row = layout.row()
row.use_property_split = False
row.prop(props, "mode", text="", expand=True, icon_only=True)
layout.prop(props, "radius")
def draw_cursor(_context, tool, xy):
from gpu_extras.presets import draw_circle_2d
props = tool.operator_properties("clip.select_circle")
radius = props.radius
draw_circle_2d(xy, (1.0,) * 4, radius, segments=32)
return dict(
idname="builtin.select_circle",
label="Select Circle",
icon="ops.generic.select_circle",
widget=None,
keymap="Clip Editor: Select Circle",
draw_settings=draw_settings,
draw_cursor=draw_cursor,
)
class _defs_clip_tracking_tools:
@ToolDef.from_fn
def add_marker_tweak():
def draw_settings(_context, layout, tool):
clip = _context.space_data.clip
if clip:
settings = clip.tracking.settings
# layout.use_property_split = True
row = layout.row(align=True)
row.prop(settings, "default_pattern_size", text="")
row.prop(settings, "default_search_size", text="")
row = layout.row()
row.ui_units_x = 5
row.prop(settings, "default_motion_model", text="")
row = layout.row()
row.ui_units_x = 5
row.prop(settings, "default_pattern_match", text="")
row = layout.row()
row.prop(settings, "use_default_brute")
row.prop(settings, "use_default_normalization")
row = layout.row(align=True)
row.prop(settings, "use_default_red_channel", text="R", toggle=True)
row.prop(settings, "use_default_green_channel", text="G", toggle=True)
row.prop(settings, "use_default_blue_channel", text="B", toggle=True)
row = layout.row()
row.prop(settings, "default_weight")
row.popover(panel="CLIP_PT_track_settings_tool")
return dict(
idname="builtin.add_marker_tweak",
label="Add Marker and Tweak",
icon="ops.generic.select",
cursor='CROSSHAIR',
widget=None,
draw_settings=draw_settings,
keymap="Clip Editor: Add Marker and Tweak"
)
class _defs_mask_select:
@ToolDef.from_fn
def select():
return dict(
idname="builtin.select",
label="Tweak",
icon="ops.generic.select",
widget=None,
keymap="Mask Editing: Select Box",
)
@ToolDef.from_fn
def box():
def draw_settings(_context, layout, tool):
props = tool.operator_properties("mask.select_box")
row = layout.row()
row.use_property_split = False
row.prop(props, "mode", text="", expand=True, icon_only=True)
return dict(
idname="builtin.select_box",
label="Select Box",
icon="ops.generic.select_box",
widget=None,
keymap="Mask Editing: Select Box",
draw_settings=draw_settings,
)
@ToolDef.from_fn
def lasso():
def draw_settings(_context, layout, tool):
props = tool.operator_properties("mask.select_lasso")
row = layout.row()
row.use_property_split = False
row.prop(props, "mode", text="", expand=True, icon_only=True)
return dict(
idname="builtin.select_lasso",
label="Select Lasso",
icon="ops.generic.select_lasso",
widget=None,
keymap="Mask Editing: Select Lasso",
draw_settings=draw_settings,
)
@ToolDef.from_fn
def circle():
def draw_settings(_context, layout, tool):
props = tool.operator_properties("mask.select_circle")
row = layout.row()
row.use_property_split = False
row.prop(props, "mode", text="", expand=True, icon_only=True)
layout.prop(props, "radius")
def draw_cursor(_context, tool, xy):
from gpu_extras.presets import draw_circle_2d
props = tool.operator_properties("mask.select_circle")
radius = props.radius
draw_circle_2d(xy, (1.0,) * 4, radius, segments=32)
return dict(
idname="builtin.select_circle",
label="Select Circle",
icon="ops.generic.select_circle",
widget=None,
keymap="Mask Editing: Select Circle",
draw_settings=draw_settings,
draw_cursor=draw_cursor,
)
class _defs_mask_tools:
@ToolDef.from_fn
def add_vertex_slide():
def draw_settings(_context, layout, tool):
tool_settings = _context.tool_settings
props = tool.operator_properties("mask.draw_mask")
row = layout.row()
row.label(text="Mask Settings")
row.prop(props, "type")
return dict(
idname="builtin.draw_mask",
label="Draw a Mask",
icon="ops.curve.extrude_move",
widget=None,
draw_settings=draw_settings,
keymap="Mask Editing: Add Vertex and Slide"
)
@ToolDef.from_fn
def add_feather_vertex_slide():
def draw_settings(_context, layout, tool):
row = layout.row()
row.label(text="Slide / Feather")
return dict(
idname="builtin.add_feather_vertex_slide",
label="Slide Mask / Add a Feather Vertex",
icon="ops.curve.radius",
widget=None,
keymap="Mask Editing: Add Feather Vertex and Slide"
)
class _defs_node_select:
@ToolDef.from_fn
@@ -2903,7 +2685,7 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel):
_defs_image_uv_transform.transform,
)
_tools_select_uv = (
_tools_select = (
(
_defs_image_uv_select.select,
_defs_image_uv_select.box,
@@ -2912,15 +2694,6 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel):
),
)
_tools_select_mask = (
(
_defs_mask_select.select,
_defs_mask_select.box,
_defs_mask_select.lasso,
_defs_mask_select.circle,
),
)
_tools_annotate = (
(
_defs_annotate.scribble,
@@ -2942,7 +2715,7 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel):
*_tools_annotate,
],
'UV': [
*_tools_select_uv,
*_tools_select,
_defs_image_generic.cursor,
None,
*_tools_transform,
@@ -2958,12 +2731,7 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel):
),
],
'MASK': [
_defs_mask_tools.add_vertex_slide,
_defs_mask_tools.add_feather_vertex_slide,
None,
*_tools_select_mask,
None,
*_tools_annotate,
],
'PAINT': [
_defs_texture_paint.generate_from_brushes,
@@ -2973,86 +2741,6 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel):
}
class CLIP_PT_tools_active(ToolSelectPanelHelper, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'TOOLS'
bl_label = "Tools" # not visible
bl_options = {'HIDE_HEADER'}
# Satisfy the 'ToolSelectPanelHelper' API.
keymap_prefix = "Clip Editor Tool:"
# Default group to use as a fallback.
tool_fallback_id = "builtin.select_box"
@classmethod
def tools_from_context(cls, context, mode=None):
if mode is None:
if context.space_data is None:
mode = 'TRACKING'
else:
mode = context.space_data.mode
for tools in (cls._tools[None], cls._tools.get(mode, ())):
for item in tools:
if not (type(item) is ToolDef) and callable(item):
yield from item(context)
else:
yield item
@classmethod
def tools_all(cls):
yield from cls._tools.items()
_tools_clip_select = (
(
_defs_clip_select.select,
_defs_clip_select.box,
_defs_clip_select.lasso,
_defs_clip_select.circle,
),
)
_tools_mask_select = (
(
_defs_mask_select.select,
_defs_mask_select.box,
_defs_mask_select.lasso,
_defs_mask_select.circle,
),
)
_tools_annotate = (
(
_defs_annotate.scribble,
_defs_annotate.line,
_defs_annotate.poly,
_defs_annotate.eraser,
),
)
_tools = {
None: [
None,
],
'TRACKING': [
_defs_clip_tracking_tools.add_marker_tweak,
None,
*_tools_clip_select,
None,
*_tools_annotate,
None,
],
'MASK': [
_defs_mask_tools.add_vertex_slide,
_defs_mask_tools.add_feather_vertex_slide,
None,
*_tools_mask_select,
None,
*_tools_annotate,
None
]
}
class NODE_PT_tools_active(ToolSelectPanelHelper, Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'TOOLS'
@@ -3574,7 +3262,6 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel):
classes = (
IMAGE_PT_tools_active,
NODE_PT_tools_active,
CLIP_PT_tools_active,
VIEW3D_PT_tools_active,
SEQUENCER_PT_tools_active,
)

View File

@@ -7177,9 +7177,6 @@ class VIEW3D_PT_overlay_gpencil_options(Panel):
# Handles for Curve Edit
layout.prop(overlay, "display_handle", text="Handles")
if context.object.mode == 'SCULPT_GPENCIL':
layout.prop(overlay, "vertex_opacity", text="Vertex Opacity", slider=True)
if context.object.mode in {'PAINT_GPENCIL', 'VERTEX_GPENCIL'}:
layout.label(text="Vertex Paint")
row = layout.row()

View File

@@ -1,30 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup asset_system
*
* \brief Information to uniquely identify and locate an asset.
*
* https://wiki.blender.org/wiki/Source/Architecture/Asset_System/Back_End#Asset_Identifier
*/
#pragma once
#include <memory>
#include <string>
namespace blender::asset_system {
class AssetIdentifier {
std::shared_ptr<std::string> library_root_path_;
std::string relative_asset_path_;
public:
AssetIdentifier(std::shared_ptr<std::string> library_root_path, std::string relative_asset_path);
AssetIdentifier(AssetIdentifier &&) = default;
AssetIdentifier(const AssetIdentifier &) = default;
std::string full_path() const;
};
} // namespace blender::asset_system

View File

@@ -24,7 +24,6 @@ struct Main;
namespace blender::asset_system {
class AssetIdentifier;
class AssetRepresentation;
class AssetStorage;
@@ -35,11 +34,7 @@ class AssetStorage;
* to also include asset indexes and more.
*/
class AssetLibrary {
/** If this is an asset library on disk, the top-level directory path. Normalized using
* #normalize_directory_path(). Shared pointer so assets can safely point to it, and don't have
* to hold a copy (which is the size of `std::string` + the allocated buffer, if no short string
* optimization is used). With thousands of assets this might make a reasonable difference. */
std::shared_ptr<std::string> root_path_;
bCallbackFuncStore on_save_callback_store_{};
/** Storage for assets (better said their representations) that are considered to be part of this
* library. Assets are not automatically loaded into this when loading an asset library. Assets
@@ -56,8 +51,6 @@ class AssetLibrary {
*/
std::unique_ptr<AssetStorage> asset_storage_;
bCallbackFuncStore on_save_callback_store_{};
public:
/* Controlled by #ED_asset_catalogs_set_save_catalogs_when_file_is_saved,
* for managing the "Save Catalog Changes" in the quit-confirmation dialog box. */
@@ -66,13 +59,10 @@ class AssetLibrary {
std::unique_ptr<AssetCatalogService> catalog_service;
public:
/**
* \param root_path: If this is an asset library on disk, the top-level directory path.
*/
AssetLibrary(StringRef root_path = "");
AssetLibrary();
~AssetLibrary();
void load_catalogs();
void load_catalogs(StringRefNull library_root_directory);
/** Load catalogs that have changed on disk. */
void refresh();
@@ -82,16 +72,10 @@ class AssetLibrary {
* representation is not needed anymore, it must be freed using #remove_asset(), or there will be
* leaking that's only cleared when the library storage is destructed (typically on exit or
* loading a different file).
*
* \param relative_asset_path: The path of the asset relative to the asset library root. With
* this the asset must be uniquely identifiable within the asset
* library.
*/
AssetRepresentation &add_external_asset(StringRef relative_asset_path,
StringRef name,
std::unique_ptr<AssetMetaData> metadata);
AssetRepresentation &add_external_asset(StringRef name, std::unique_ptr<AssetMetaData> metadata);
/** See #AssetLibrary::add_external_asset(). */
AssetRepresentation &add_local_id_asset(StringRef relative_asset_path, ID &id);
AssetRepresentation &add_local_id_asset(ID &id);
/** Remove an asset from the library that was added using #add_external_asset() or
* #add_local_id_asset(). Can usually be expected to be constant time complexity (worst case may
* differ).
@@ -121,14 +105,6 @@ class AssetLibrary {
void on_blend_save_post(Main *bmain, PointerRNA **pointers, int num_pointers);
/**
* Create an asset identifier from the root path of this asset library and the given relative
* asset path (relative to the asset library root directory).
*/
AssetIdentifier asset_identifier_from_library(StringRef relative_asset_path);
StringRefNull root_path() const;
private:
std::optional<int> find_asset_index(const AssetRepresentation &asset);
};

View File

@@ -16,23 +16,21 @@
#include "BLI_string_ref.hh"
#include "AS_asset_identifier.hh"
struct AssetMetaData;
struct ID;
namespace blender::asset_system {
class AssetRepresentation {
AssetIdentifier identifier_;
/** Indicate if this is a local or external asset, and as such, which of the union members below
* should be used. */
const bool is_local_id_ = false;
struct ExternalAsset {
std::string name;
std::unique_ptr<AssetMetaData> metadata_ = nullptr;
};
/** Indicate if this is a local or external asset, and as such, which of the union members below
* should be used. */
const bool is_local_id_ = false;
union {
ExternalAsset external_asset_;
ID *local_asset_id_ = nullptr; /* Non-owning. */
@@ -42,12 +40,10 @@ class AssetRepresentation {
public:
/** Constructs an asset representation for an external ID. The asset will not be editable. */
AssetRepresentation(AssetIdentifier &&identifier,
StringRef name,
std::unique_ptr<AssetMetaData> metadata);
explicit AssetRepresentation(StringRef name, std::unique_ptr<AssetMetaData> metadata);
/** Constructs an asset representation for an ID stored in the current file. This makes the asset
* local and fully editable. */
AssetRepresentation(AssetIdentifier &&identifier, ID &id);
explicit AssetRepresentation(ID &id);
AssetRepresentation(AssetRepresentation &&other);
/* Non-copyable type. */
AssetRepresentation(const AssetRepresentation &other) = delete;
@@ -59,8 +55,6 @@ class AssetRepresentation {
/* Non-copyable type. */
AssetRepresentation &operator=(const AssetRepresentation &other) = delete;
const AssetIdentifier &get_identifier() const;
StringRefNull get_name() const;
AssetMetaData &get_metadata() const;
/** Returns if this asset is stored inside this current file, and as such fully editable. */
@@ -68,8 +62,3 @@ class AssetRepresentation {
};
} // namespace blender::asset_system
/* C-Handle */
struct AssetRepresentation;
const std::string AS_asset_representation_full_path_get(const ::AssetRepresentation *asset);

View File

@@ -17,22 +17,18 @@ set(SRC
intern/asset_catalog.cc
intern/asset_catalog_path.cc
intern/asset_catalog_tree.cc
intern/asset_identifier.cc
intern/asset_library.cc
intern/asset_library_service.cc
intern/asset_representation.cc
intern/asset_storage.cc
intern/utils.cc
AS_asset_catalog.hh
AS_asset_catalog_path.hh
AS_asset_catalog_tree.hh
AS_asset_identifier.hh
AS_asset_library.hh
AS_asset_representation.hh
intern/asset_library_service.hh
intern/asset_storage.hh
intern/utils.hh
AS_asset_library.h
AS_asset_representation.h

View File

@@ -1,27 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup asset_system
*/
#include "BLI_path_util.h"
#include <iostream>
#include "AS_asset_identifier.hh"
namespace blender::asset_system {
AssetIdentifier::AssetIdentifier(std::shared_ptr<std::string> library_root_path,
std::string relative_asset_path)
: library_root_path_(library_root_path), relative_asset_path_(relative_asset_path)
{
}
std::string AssetIdentifier::full_path() const
{
char path[FILE_MAX];
BLI_path_join(path, sizeof(path), library_root_path_->c_str(), relative_asset_path_.c_str());
return path;
}
} // namespace blender::asset_system

View File

@@ -7,7 +7,6 @@
#include <memory>
#include "AS_asset_catalog_tree.hh"
#include "AS_asset_identifier.hh"
#include "AS_asset_library.h"
#include "AS_asset_library.hh"
#include "AS_asset_representation.hh"
@@ -23,7 +22,6 @@
#include "asset_library_service.hh"
#include "asset_storage.hh"
#include "utils.hh"
using namespace blender;
using namespace blender::asset_system;
@@ -122,9 +120,8 @@ void AS_asset_library_remap_ids(const IDRemapper *mappings)
namespace blender::asset_system {
AssetLibrary::AssetLibrary(StringRef root_path)
: root_path_(std::make_shared<std::string>(utils::normalize_directory_path(root_path))),
asset_storage_(std::make_unique<AssetStorage>()),
AssetLibrary::AssetLibrary()
: asset_storage_(std::make_unique<AssetStorage>()),
catalog_service(std::make_unique<AssetCatalogService>())
{
}
@@ -136,9 +133,9 @@ AssetLibrary::~AssetLibrary()
}
}
void AssetLibrary::load_catalogs()
void AssetLibrary::load_catalogs(StringRefNull library_root_directory)
{
auto catalog_service = std::make_unique<AssetCatalogService>(root_path());
auto catalog_service = std::make_unique<AssetCatalogService>(library_root_directory);
catalog_service->load_from_disk();
this->catalog_service = std::move(catalog_service);
}
@@ -148,18 +145,15 @@ void AssetLibrary::refresh()
this->catalog_service->reload_catalogs();
}
AssetRepresentation &AssetLibrary::add_external_asset(StringRef relative_asset_path,
StringRef name,
AssetRepresentation &AssetLibrary::add_external_asset(StringRef name,
std::unique_ptr<AssetMetaData> metadata)
{
AssetIdentifier identifier = asset_identifier_from_library(relative_asset_path);
return asset_storage_->add_external_asset(std::move(identifier), name, std::move(metadata));
return asset_storage_->add_external_asset(name, std::move(metadata));
}
AssetRepresentation &AssetLibrary::add_local_id_asset(StringRef relative_asset_path, ID &id)
AssetRepresentation &AssetLibrary::add_local_id_asset(ID &id)
{
AssetIdentifier identifier = asset_identifier_from_library(relative_asset_path);
return asset_storage_->add_local_id_asset(std::move(identifier), id);
return asset_storage_->add_local_id_asset(id);
}
bool AssetLibrary::remove_asset(AssetRepresentation &asset)
@@ -215,11 +209,6 @@ void AssetLibrary::on_blend_save_post(struct Main *main,
}
}
AssetIdentifier AssetLibrary::asset_identifier_from_library(StringRef relative_asset_path)
{
return AssetIdentifier(root_path_, relative_asset_path);
}
void AssetLibrary::refresh_catalog_simplename(struct AssetMetaData *asset_data)
{
if (BLI_uuid_is_nil(asset_data->catalog_id)) {
@@ -235,11 +224,6 @@ void AssetLibrary::refresh_catalog_simplename(struct AssetMetaData *asset_data)
STRNCPY(asset_data->catalog_simple_name, catalog->simple_name.c_str());
}
StringRefNull AssetLibrary::root_path() const
{
return *root_path_;
}
Vector<AssetLibraryReference> all_valid_asset_library_refs()
{
Vector<AssetLibraryReference> result;

View File

@@ -4,6 +4,9 @@
* \ingroup asset_system
*/
#include "asset_library_service.hh"
#include "AS_asset_library.hh"
#include "BKE_blender.h"
#include "BKE_preferences.h"
@@ -16,10 +19,6 @@
#include "CLG_log.h"
#include "AS_asset_library.hh"
#include "asset_library_service.hh"
#include "utils.hh"
/* When enabled, use a pre file load handler (#BKE_CB_EVT_LOAD_PRE) callback to destroy the asset
* library service. Without this an explicit call from the file loading code is needed to do this,
* which is not as nice.
@@ -81,30 +80,41 @@ AssetLibrary *AssetLibraryService::get_asset_library(
return nullptr;
}
AssetLibrary *AssetLibraryService::get_asset_library_on_disk(StringRefNull root_path)
namespace {
std::string normalize_directory_path(StringRefNull directory)
{
BLI_assert_msg(!root_path.is_empty(),
char dir_normalized[PATH_MAX];
STRNCPY(dir_normalized, directory.c_str());
BLI_path_normalize_dir(nullptr, dir_normalized, sizeof(dir_normalized));
return std::string(dir_normalized);
}
} // namespace
AssetLibrary *AssetLibraryService::get_asset_library_on_disk(StringRefNull top_level_directory)
{
BLI_assert_msg(!top_level_directory.is_empty(),
"top level directory must be given for on-disk asset library");
std::string normalized_root_path = utils::normalize_directory_path(root_path);
std::string top_dir_trailing_slash = normalize_directory_path(top_level_directory);
std::unique_ptr<AssetLibrary> *lib_uptr_ptr = on_disk_libraries_.lookup_ptr(
normalized_root_path);
top_dir_trailing_slash);
if (lib_uptr_ptr != nullptr) {
CLOG_INFO(&LOG, 2, "get \"%s\" (cached)", normalized_root_path.c_str());
CLOG_INFO(&LOG, 2, "get \"%s\" (cached)", top_dir_trailing_slash.c_str());
AssetLibrary *lib = lib_uptr_ptr->get();
lib->refresh();
return lib;
}
std::unique_ptr lib_uptr = std::make_unique<AssetLibrary>(normalized_root_path);
std::unique_ptr lib_uptr = std::make_unique<AssetLibrary>();
AssetLibrary *lib = lib_uptr.get();
lib->on_blend_save_handler_register();
lib->load_catalogs();
lib->load_catalogs(top_dir_trailing_slash);
on_disk_libraries_.add_new(normalized_root_path, std::move(lib_uptr));
CLOG_INFO(&LOG, 2, "get \"%s\" (loaded)", normalized_root_path.c_str());
on_disk_libraries_.add_new(top_dir_trailing_slash, std::move(lib_uptr));
CLOG_INFO(&LOG, 2, "get \"%s\" (loaded)", top_dir_trailing_slash.c_str());
return lib;
}

View File

@@ -33,8 +33,7 @@ namespace blender::asset_system {
class AssetLibraryService {
static std::unique_ptr<AssetLibraryService> instance_;
/* Mapping absolute path of the library's root path (normalize with #normalize_directory_path()!)
* the AssetLibrary instance. */
/* Mapping absolute path of the library's top-level directory to the AssetLibrary instance. */
Map<std::string, std::unique_ptr<AssetLibrary>> on_disk_libraries_;
/** Library without a known path, i.e. the "Current File" library if the file isn't saved yet. If
* the file was saved, a valid path for the library can be determined and #on_disk_libraries_

View File

@@ -9,7 +9,6 @@
#include "DNA_ID.h"
#include "DNA_asset_types.h"
#include "AS_asset_identifier.hh"
#include "AS_asset_representation.h"
#include "AS_asset_representation.hh"
@@ -17,17 +16,14 @@
namespace blender::asset_system {
AssetRepresentation::AssetRepresentation(AssetIdentifier &&identifier,
StringRef name,
std::unique_ptr<AssetMetaData> metadata)
: identifier_(identifier), is_local_id_(false), external_asset_()
AssetRepresentation::AssetRepresentation(StringRef name, std::unique_ptr<AssetMetaData> metadata)
: is_local_id_(false), external_asset_()
{
external_asset_.name = name;
external_asset_.metadata_ = std::move(metadata);
}
AssetRepresentation::AssetRepresentation(AssetIdentifier &&identifier, ID &id)
: identifier_(identifier), is_local_id_(true), local_asset_id_(&id)
AssetRepresentation::AssetRepresentation(ID &id) : is_local_id_(true), local_asset_id_(&id)
{
if (!id.asset_data) {
throw std::invalid_argument("Passed ID is not an asset");
@@ -35,7 +31,7 @@ AssetRepresentation::AssetRepresentation(AssetIdentifier &&identifier, ID &id)
}
AssetRepresentation::AssetRepresentation(AssetRepresentation &&other)
: identifier_(std::move(other.identifier_)), is_local_id_(other.is_local_id_)
: is_local_id_(other.is_local_id_)
{
if (is_local_id_) {
local_asset_id_ = other.local_asset_id_;
@@ -53,11 +49,6 @@ AssetRepresentation::~AssetRepresentation()
}
}
const AssetIdentifier &AssetRepresentation::get_identifier() const
{
return identifier_;
}
StringRefNull AssetRepresentation::get_name() const
{
if (is_local_id_) {
@@ -79,20 +70,12 @@ bool AssetRepresentation::is_local_id() const
} // namespace blender::asset_system
using namespace blender;
const std::string AS_asset_representation_full_path_get(const AssetRepresentation *asset_handle)
{
const asset_system::AssetRepresentation *asset =
reinterpret_cast<const asset_system::AssetRepresentation *>(asset_handle);
const asset_system::AssetIdentifier &identifier = asset->get_identifier();
return identifier.full_path();
}
/* ---------------------------------------------------------------------- */
/** \name C-API
* \{ */
using namespace blender;
const char *AS_asset_representation_name_get(const AssetRepresentation *asset_handle)
{
const asset_system::AssetRepresentation *asset =

View File

@@ -15,18 +15,16 @@
namespace blender::asset_system {
AssetRepresentation &AssetStorage::add_local_id_asset(AssetIdentifier &&identifier, ID &id)
AssetRepresentation &AssetStorage::add_local_id_asset(ID &id)
{
return *local_id_assets_.lookup_key_or_add(
std::make_unique<AssetRepresentation>(std::move(identifier), id));
return *local_id_assets_.lookup_key_or_add(std::make_unique<AssetRepresentation>(id));
}
AssetRepresentation &AssetStorage::add_external_asset(AssetIdentifier &&identifier,
StringRef name,
AssetRepresentation &AssetStorage::add_external_asset(StringRef name,
std::unique_ptr<AssetMetaData> metadata)
{
return *external_assets_.lookup_key_or_add(
std::make_unique<AssetRepresentation>(std::move(identifier), name, std::move(metadata)));
std::make_unique<AssetRepresentation>(name, std::move(metadata)));
}
bool AssetStorage::remove_asset(AssetRepresentation &asset)

View File

@@ -19,7 +19,6 @@ struct IDRemapper;
namespace blender::asset_system {
class AssetIdentifier;
class AssetRepresentation;
class AssetStorage {
@@ -33,11 +32,9 @@ class AssetStorage {
public:
/** See #AssetLibrary::add_external_asset(). */
AssetRepresentation &add_external_asset(AssetIdentifier &&identifier,
StringRef name,
std::unique_ptr<AssetMetaData> metadata);
AssetRepresentation &add_external_asset(StringRef name, std::unique_ptr<AssetMetaData> metadata);
/** See #AssetLibrary::add_external_asset(). */
AssetRepresentation &add_local_id_asset(AssetIdentifier &&identifier, ID &id);
AssetRepresentation &add_local_id_asset(ID &id);
/** See #AssetLibrary::remove_asset(). */
bool remove_asset(AssetRepresentation &asset);

View File

@@ -1,30 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup asset_system
*/
#include "BLI_fileops.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "utils.hh"
namespace blender::asset_system::utils {
std::string normalize_directory_path(StringRef directory)
{
if (directory.is_empty()) {
return "";
}
char dir_normalized[PATH_MAX];
BLI_strncpy(dir_normalized,
directory.data(),
/* + 1 for null terminator. */
std::min(directory.size() + 1, int64_t(sizeof(dir_normalized))));
BLI_path_normalize_dir(nullptr, dir_normalized, sizeof(dir_normalized));
return std::string(dir_normalized);
}
} // namespace blender::asset_system::utils

View File

@@ -1,19 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup asset_system
*/
#pragma once
#include "BLI_string_ref.hh"
namespace blender::asset_system::utils {
/**
* Returns a normalized directory path with a trailing slash, and a maximum length of #PATH_MAX.
* Slashes are not converted to native format (they probably should be though?).
*/
std::string normalize_directory_path(StringRef directory);
} // namespace blender::asset_system::utils

View File

@@ -25,7 +25,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 3
#define BLENDER_FILE_SUBVERSION 2
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file

View File

@@ -483,18 +483,6 @@ bool BKE_fcurve_delete_keys_selected(struct FCurve *fcu);
*/
void BKE_fcurve_delete_keys_all(struct FCurve *fcu);
/**
* Called during transform/snapping to make sure selected keyframes replace
* any other keyframes which may reside on that frame (that is not selected).
*
* \param sel_flag: The flag (bezt.f1/2/3) value to use to determine selection. Usually `SELECT`,
* but may want to use a different one at times (if caller does not operate on
* selection).
*/
void BKE_fcurve_merge_duplicate_keys(struct FCurve *fcu,
const int sel_flag,
const bool use_handle);
/* -------- Curve Sanity -------- */
/**

View File

@@ -38,53 +38,44 @@ enum {
* Indicates whether this is direct (i.e. by local data) or indirect (i.e. by linked data) usage.
*/
IDWALK_CB_INDIRECT_USAGE = (1 << 2),
/**
* Indicates that this is a direct weak link usage, i.e. if the user is a local ID, and is using
* (pointing to) a linked ID, that usage does not make the linked ID directly linked.
*
* E.g. usages of linked collections or objects by ViewLayerCollections or Bases in scenes.
*
* See also #LIB_INDIRECT_WEAK_LINK in DNA_ID.h
*/
IDWALK_CB_DIRECT_WEAK_LINK = (1 << 3),
/**
* That ID is used as mere sub-data by its owner (only case currently: those root nodetrees in
* materials etc., and the Scene's master collections).
* This means callback shall not *do* anything, only use this as informative data if it needs it.
*/
IDWALK_CB_EMBEDDED = (1 << 4),
IDWALK_CB_EMBEDDED = (1 << 3),
/**
* That ID is not really used by its owner, it's just an internal hint/helper.
* This marks the 'from' pointers issue, like Key->from.
* How to handle that kind of cases totally depends on what caller code is doing... */
IDWALK_CB_LOOPBACK = (1 << 5),
IDWALK_CB_LOOPBACK = (1 << 4),
/** That ID is used as library override's reference by its owner. */
IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE = (1 << 6),
IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE = (1 << 5),
/** That ID pointer is not overridable. */
IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE = (1 << 7),
IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE = (1 << 6),
/**
* Indicates that this is an internal runtime ID pointer, like e.g. `ID.newid` or `ID.original`.
* \note Those should be ignored in most cases, and won't be processed/generated anyway unless
* `IDWALK_DO_INTERNAL_RUNTIME_POINTERS` option is enabled.
*/
IDWALK_CB_INTERNAL = (1 << 8),
IDWALK_CB_INTERNAL = (1 << 7),
/**
* This ID usage is fully refcounted.
* Callback is responsible to deal accordingly with #ID.us if needed.
*/
IDWALK_CB_USER = (1 << 9),
IDWALK_CB_USER = (1 << 8),
/**
* This ID usage is not refcounted, but at least one user should be generated by it (to avoid
* e.g. losing the used ID on save/reload).
* Callback is responsible to deal accordingly with #ID.us if needed.
*/
IDWALK_CB_USER_ONE = (1 << 10),
IDWALK_CB_USER_ONE = (1 << 9),
};
enum {

View File

@@ -72,11 +72,6 @@ void BKE_mesh_tag_coords_changed_uniformly(struct Mesh *mesh);
void BKE_mesh_tag_topology_changed(struct Mesh *mesh);
/**
* Call when new edges and vertices have been created but positions and faces haven't changed.
*/
void BKE_mesh_tag_edges_split(struct Mesh *mesh);
/* *** mesh.c *** */
struct BMesh *BKE_mesh_to_bmesh_ex(const struct Mesh *me,
@@ -132,8 +127,7 @@ int BKE_mesh_edge_other_vert(const struct MEdge *e, int v);
/**
* Sets each output array element to the edge index if it is a real edge, or -1.
*/
void BKE_mesh_looptri_get_real_edges(const struct MEdge *edges,
const struct MLoop *loops,
void BKE_mesh_looptri_get_real_edges(const struct Mesh *mesh,
const struct MLoopTri *looptri,
int r_edges[3]);

View File

@@ -22,7 +22,7 @@ namespace blender::meshintersect {
* It is allowed for the pointers to be null, meaning the transformation is the identity.
* \param material_remaps: An array of maps from material slot numbers in the corresponding mesh
* to the material slot in the first mesh. It is OK for material_remaps or any of its constituent
* arrays to be empty. A -1 value means that the original index should be used with no mapping.
* arrays to be empty.
* \param r_intersecting_edges: Array to store indices of edges on the resulting mesh in. These
* 'new' edges are the result of the intersections.
*/

View File

@@ -351,7 +351,6 @@ namespace blender::bke::mesh_topology {
Array<int> build_loop_to_poly_map(Span<MPoly> polys, int loops_num);
Array<Vector<int>> build_vert_to_edge_map(Span<MEdge> edges, int verts_num);
Array<Vector<int>> build_vert_to_poly_map(Span<MPoly> polys, Span<MLoop> loops, int verts_num);
Array<Vector<int>> build_vert_to_loop_map(Span<MLoop> loops, int verts_num);
Array<Vector<int>> build_edge_to_loop_map(Span<MLoop> loops, int edges_num);
Vector<Vector<int>> build_edge_to_loop_map_resizable(Span<MLoop> loops, int edges_num);

View File

@@ -156,8 +156,15 @@ void old_mdisps_bilinear(float out[3], float (*disps)[3], int st, float u, float
/**
* Find per-corner coordinate with given per-face UV coord.
*/
int mdisp_rot_face_to_crn(
struct MPoly *mpoly, int face_side, float u, float v, float *x, float *y);
int mdisp_rot_face_to_crn(struct MVert *mvert,
struct MPoly *mpoly,
struct MLoop *mloop,
const struct MLoopTri *lt,
int face_side,
float u,
float v,
float *x,
float *y);
/* Reshaping, define in multires_reshape.c */

View File

@@ -405,6 +405,8 @@ typedef struct bNodeTreeType {
int ui_icon;
/* callbacks */
void (*free_cache)(struct bNodeTree *ntree);
void (*free_node_cache)(struct bNodeTree *ntree, struct bNode *node);
/* Iteration over all node classes. */
void (*foreach_nodeclass)(struct Scene *scene, void *calldata, bNodeClassCallback func);
/* Check visibility in the node editor */
@@ -519,6 +521,8 @@ void ntreeUpdateAllUsers(struct Main *main, struct ID *id);
*/
void ntreeSetOutput(struct bNodeTree *ntree);
void ntreeFreeCache(struct bNodeTree *ntree);
void ntreeNodeFlagSet(const bNodeTree *ntree, int flag, bool enable);
/**
* Returns localized tree for execution in threads.
@@ -1385,7 +1389,7 @@ struct TexResult;
* \{ */
#define GEO_NODE_TRIANGULATE 1000
#define GEO_NODE_TRANSFORM_GEOMETRY 1002
#define GEO_NODE_TRANSFORM 1002
#define GEO_NODE_MESH_BOOLEAN 1003
#define GEO_NODE_OBJECT_INFO 1007
#define GEO_NODE_JOIN_GEOMETRY 1010
@@ -1564,7 +1568,6 @@ struct TexResult;
void BKE_node_system_init(void);
void BKE_node_system_exit(void);
extern bNodeTreeType NodeTreeTypeUndefined;
extern struct bNodeType NodeTypeUndefined;
extern struct bNodeSocketType NodeSocketTypeUndefined;

View File

@@ -46,6 +46,11 @@ class bNodeTreeRuntime : NonCopyable, NonMovable {
*/
uint8_t runtime_flag = 0;
/** Flag to prevent re-entrant update calls. */
short is_updating = 0;
/** Generic temporary flag for recursion check (DFS/BFS). */
short done = 0;
/** Execution data.
*
* XXX It would be preferable to completely move this data out of the underlying node tree,
@@ -133,6 +138,9 @@ class bNodeSocketRuntime : NonCopyable, NonMovable {
/* Runtime-only cache of the number of input links, for multi-input sockets. */
short total_inputs = 0;
/** Cached data from execution. */
void *cache = nullptr;
/** Only valid when #topology_cache_is_dirty is false. */
Vector<bNodeLink *> directly_linked_links;
Vector<bNodeSocket *> directly_linked_sockets;

View File

@@ -99,15 +99,6 @@ typedef struct {
float (*color)[4];
} PBVHColorBufferNode;
typedef struct PBVHPixels {
/**
* Storage for texture painting on PBVH level.
*
* Contains #blender::bke::pbvh::pixels::PBVHData
*/
void *data;
} PBVHPixels;
typedef struct PBVHPixelsNode {
/**
* Contains triangle/pixel data used during texture painting.

View File

@@ -18,46 +18,8 @@
namespace blender::bke::pbvh::pixels {
/**
* Data shared between pixels that belong to the same triangle.
*
* Data is stored as a list of structs, grouped by usage to improve performance (improves CPU
* cache prefetching).
*/
struct PaintGeometryPrimitives {
/** Data accessed by the inner loop of the painting brush. */
Vector<int3> vert_indices;
public:
void append(const int3 vert_indices)
{
this->vert_indices.append(vert_indices);
}
const int3 &get_vert_indices(const int index) const
{
return vert_indices[index];
}
void clear()
{
vert_indices.clear();
}
int64_t size() const
{
return vert_indices.size();
}
int64_t mem_size() const
{
return size() * sizeof(int3);
}
};
struct UVPrimitivePaintInput {
/** Corresponding index into PaintGeometryPrimitives */
int64_t geometry_primitive_index;
struct TrianglePaintInput {
int3 vert_indices;
/**
* Delta barycentric coordinates between 2 neighboring UV's in the U direction.
*
@@ -71,27 +33,34 @@ struct UVPrimitivePaintInput {
* delta_barycentric_coord_u is initialized in a later stage as it requires image tile
* dimensions.
*/
UVPrimitivePaintInput(int64_t geometry_primitive_index)
: geometry_primitive_index(geometry_primitive_index), delta_barycentric_coord_u(0.0f, 0.0f)
TrianglePaintInput(const int3 vert_indices)
: vert_indices(vert_indices), delta_barycentric_coord_u(0.0f, 0.0f)
{
}
};
struct PaintUVPrimitives {
/**
* Data shared between pixels that belong to the same triangle.
*
* Data is stored as a list of structs, grouped by usage to improve performance (improves CPU
* cache prefetching).
*/
struct Triangles {
/** Data accessed by the inner loop of the painting brush. */
Vector<UVPrimitivePaintInput> paint_input;
Vector<TrianglePaintInput> paint_input;
void append(int64_t geometry_primitive_index)
public:
void append(const int3 vert_indices)
{
this->paint_input.append(UVPrimitivePaintInput(geometry_primitive_index));
this->paint_input.append(TrianglePaintInput(vert_indices));
}
UVPrimitivePaintInput &last()
TrianglePaintInput &get_paint_input(const int index)
{
return paint_input.last();
return paint_input[index];
}
const UVPrimitivePaintInput &get_paint_input(uint64_t index) const
const TrianglePaintInput &get_paint_input(const int index) const
{
return paint_input[index];
}
@@ -108,7 +77,7 @@ struct PaintUVPrimitives {
int64_t mem_size() const
{
return size() * sizeof(UVPrimitivePaintInput);
return paint_input.size() * sizeof(TrianglePaintInput);
}
};
@@ -123,7 +92,7 @@ struct PackedPixelRow {
/** Number of sequential pixels encoded in this package. */
ushort num_pixels;
/** Reference to the pbvh triangle index. */
ushort uv_primitive_index;
ushort triangle_index;
};
/**
@@ -179,7 +148,7 @@ struct NodeData {
Vector<UDIMTilePixels> tiles;
Vector<UDIMTileUndo> undo_regions;
PaintUVPrimitives uv_primitives;
Triangles triangles;
NodeData()
{
@@ -232,7 +201,7 @@ struct NodeData {
void clear_data()
{
tiles.clear();
uv_primitives.clear();
triangles.clear();
}
static void free_func(void *instance)
@@ -242,18 +211,7 @@ struct NodeData {
}
};
struct PBVHData {
/* Per UVPRimitive contains the paint data. */
PaintGeometryPrimitives geom_primitives;
void clear_data()
{
geom_primitives.clear();
}
};
NodeData &BKE_pbvh_pixels_node_data_get(PBVHNode &node);
void BKE_pbvh_pixels_mark_image_dirty(PBVHNode &node, Image &image, ImageUser &image_user);
PBVHData &BKE_pbvh_pixels_data_get(PBVH &pbvh);
} // namespace blender::bke::pbvh::pixels

View File

@@ -1,746 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <fstream>
#include <optional>
#include "BLI_array.hh"
#include "BLI_edgehash.h"
#include "BLI_float3x3.hh"
#include "BLI_map.hh"
#include "BLI_math.h"
#include "BLI_math_vec_types.hh"
#include "BLI_rect.h"
#include "BLI_vector.hh"
#include "BLI_vector_list.hh"
#include "DNA_meshdata_types.h"
namespace blender::bke::uv_islands {
struct MeshEdge;
struct MeshPrimitive;
struct UVBorder;
struct UVEdge;
struct UVIslands;
struct UVIslandsMask;
struct UVPrimitive;
struct UVPrimitiveEdge;
struct UVVertex;
struct MeshVertex {
int64_t v;
Vector<MeshEdge *> edges;
};
struct MeshUVVert {
MeshVertex *vertex;
float2 uv;
int64_t loop;
};
struct MeshEdge {
MeshVertex *vert1;
MeshVertex *vert2;
Vector<MeshPrimitive *> primitives;
};
/** Represents a triangle in 3d space (MLoopTri) */
struct MeshPrimitive {
int64_t index;
int64_t poly;
Vector<MeshEdge *, 3> edges;
Vector<MeshUVVert, 3> vertices;
/**
* UV island this primitive belongs to. This is used to speed up the initial uv island
* extraction, but should not be used when extending uv islands.
*/
int64_t uv_island_id;
MeshUVVert *get_other_uv_vertex(const MeshVertex *v1, const MeshVertex *v2)
{
BLI_assert(vertices[0].vertex == v1 || vertices[1].vertex == v1 || vertices[2].vertex == v1);
BLI_assert(vertices[0].vertex == v2 || vertices[1].vertex == v2 || vertices[2].vertex == v2);
for (MeshUVVert &uv_vertex : vertices) {
if (uv_vertex.vertex != v1 && uv_vertex.vertex != v2) {
return &uv_vertex;
}
}
return nullptr;
}
rctf uv_bounds() const;
bool has_shared_uv_edge(const MeshPrimitive *other) const
{
int shared_uv_verts = 0;
for (const MeshUVVert &vert : vertices) {
for (const MeshUVVert &other_vert : other->vertices) {
if (vert.uv == other_vert.uv) {
shared_uv_verts += 1;
}
}
}
return shared_uv_verts >= 2;
}
};
/**
* MeshData contains input geometry data converted in a list of primitives, edges and vertices for
* quick access for both local space and uv space.
*/
struct MeshData {
public:
const MLoopTri *looptri;
const int64_t looptri_len;
const int64_t vert_len;
const MLoop *mloop;
const MLoopUV *mloopuv;
public:
Vector<MeshPrimitive> primitives;
Vector<MeshEdge> edges;
Vector<MeshVertex> vertices;
/** Total number of uv islands detected. */
int64_t uv_island_len;
explicit MeshData(const MLoopTri *looptri,
const int64_t looptri_len,
const int64_t vert_len,
const MLoop *mloop,
const MLoopUV *mloopuv)
: looptri(looptri),
looptri_len(looptri_len),
vert_len(vert_len),
mloop(mloop),
mloopuv(mloopuv)
{
init_vertices();
init_primitives();
init_edges();
init_primitive_uv_island_ids();
}
void init_vertices()
{
vertices.reserve(vert_len);
for (int64_t i = 0; i < vert_len; i++) {
MeshVertex vert;
vert.v = i;
vertices.append(vert);
}
}
void init_primitives()
{
primitives.reserve(looptri_len);
for (int64_t i = 0; i < looptri_len; i++) {
const MLoopTri &tri = looptri[i];
MeshPrimitive primitive;
primitive.index = i;
primitive.poly = tri.poly;
for (int j = 0; j < 3; j++) {
MeshUVVert uv_vert;
uv_vert.loop = tri.tri[j];
uv_vert.vertex = &vertices[mloop[uv_vert.loop].v];
uv_vert.uv = mloopuv[uv_vert.loop].uv;
primitive.vertices.append(uv_vert);
}
primitives.append(primitive);
}
}
void init_edges()
{
edges.reserve(looptri_len * 2);
EdgeHash *eh = BLI_edgehash_new_ex(__func__, looptri_len * 3);
for (int64_t i = 0; i < looptri_len; i++) {
const MLoopTri &tri = looptri[i];
MeshPrimitive &primitive = primitives[i];
for (int j = 0; j < 3; j++) {
int v1 = mloop[tri.tri[j]].v;
int v2 = mloop[tri.tri[(j + 1) % 3]].v;
/* TODO: Use lookup_ptr to be able to store edge 0. */
void *v = BLI_edgehash_lookup(eh, v1, v2);
int64_t edge_index;
if (v == nullptr) {
edge_index = edges.size();
BLI_edgehash_insert(eh, v1, v2, POINTER_FROM_INT(edge_index + 1));
MeshEdge edge;
edge.vert1 = &vertices[v1];
edge.vert2 = &vertices[v2];
edges.append(edge);
MeshEdge *edge_ptr = &edges.last();
vertices[v1].edges.append(edge_ptr);
vertices[v2].edges.append(edge_ptr);
}
else {
edge_index = POINTER_AS_INT(v) - 1;
}
MeshEdge *edge = &edges[edge_index];
edge->primitives.append(&primitive);
primitive.edges.append(edge);
}
}
BLI_edgehash_free(eh, nullptr);
}
static const int64_t INVALID_UV_ISLAND_ID = -1;
/**
* NOTE: doesn't support weird topology where unconnected mesh primitives share the same uv
* island. For a accurate implementation we should use implement an uv_prim_lookup.
*/
static void extract_uv_neighbors(Vector<MeshPrimitive *> &prims_to_add, MeshPrimitive *primitive)
{
for (MeshEdge *edge : primitive->edges) {
for (MeshPrimitive *other_primitive : edge->primitives) {
if (primitive == other_primitive) {
continue;
}
if (other_primitive->uv_island_id != MeshData::INVALID_UV_ISLAND_ID) {
continue;
}
if (primitive->has_shared_uv_edge(other_primitive)) {
prims_to_add.append(other_primitive);
}
}
}
}
void init_primitive_uv_island_ids()
{
for (MeshPrimitive &primitive : primitives) {
primitive.uv_island_id = INVALID_UV_ISLAND_ID;
}
int64_t uv_island_id = 0;
Vector<MeshPrimitive *> prims_to_add;
for (MeshPrimitive &primitive : primitives) {
/* Early exit when uv island id is already extracted during uv neighbor extractions. */
if (primitive.uv_island_id != INVALID_UV_ISLAND_ID) {
continue;
}
prims_to_add.append(&primitive);
while (!prims_to_add.is_empty()) {
MeshPrimitive *primitive = prims_to_add.pop_last();
primitive->uv_island_id = uv_island_id;
extract_uv_neighbors(prims_to_add, primitive);
}
uv_island_id++;
}
uv_island_len = uv_island_id;
}
};
struct UVVertex {
MeshVertex *vertex;
/* Position in uv space. */
float2 uv;
/* uv edges that share this UVVertex. */
Vector<UVEdge *> uv_edges;
struct {
bool is_border : 1;
bool is_extended : 1;
} flags;
explicit UVVertex()
{
flags.is_border = false;
flags.is_extended = false;
}
explicit UVVertex(const MeshUVVert &vert) : vertex(vert.vertex), uv(vert.uv)
{
flags.is_border = false;
flags.is_extended = false;
}
};
struct UVEdge {
std::array<UVVertex *, 2> vertices;
Vector<UVPrimitive *, 2> uv_primitives;
bool has_shared_edge(const MeshUVVert &v1, const MeshUVVert &v2) const
{
return (vertices[0]->uv == v1.uv && vertices[1]->uv == v2.uv) ||
(vertices[0]->uv == v2.uv && vertices[1]->uv == v1.uv);
}
bool has_shared_edge(const UVVertex &v1, const UVVertex &v2) const
{
return (vertices[0]->uv == v1.uv && vertices[1]->uv == v2.uv) ||
(vertices[0]->uv == v2.uv && vertices[1]->uv == v1.uv);
}
bool has_shared_edge(const UVEdge &other) const
{
return has_shared_edge(*other.vertices[0], *other.vertices[1]);
}
bool has_same_vertices(const MeshVertex &vert1, const MeshVertex &vert2) const
{
return (vertices[0]->vertex == &vert1 && vertices[1]->vertex == &vert2) ||
(vertices[0]->vertex == &vert2 && vertices[1]->vertex == &vert1);
}
bool has_same_uv_vertices(const UVEdge &other) const
{
return has_shared_edge(other) &&
has_same_vertices(*other.vertices[0]->vertex, *other.vertices[1]->vertex);
;
}
bool has_same_vertices(const MeshEdge &edge) const
{
return has_same_vertices(*edge.vert1, *edge.vert2);
}
bool is_border_edge() const
{
return uv_primitives.size() == 1;
}
void append_to_uv_vertices()
{
for (UVVertex *vertex : vertices) {
vertex->uv_edges.append_non_duplicates(this);
}
}
UVVertex *get_other_uv_vertex(const MeshVertex *vertex)
{
if (vertices[0]->vertex == vertex) {
return vertices[1];
}
return vertices[0];
}
};
struct UVPrimitive {
/**
* Index of the primitive in the original mesh.
*/
MeshPrimitive *primitive;
Vector<UVEdge *, 3> edges;
explicit UVPrimitive(MeshPrimitive *primitive) : primitive(primitive)
{
}
void append_to_uv_edges()
{
for (UVEdge *uv_edge : edges) {
uv_edge->uv_primitives.append_non_duplicates(this);
}
}
void append_to_uv_vertices()
{
for (UVEdge *uv_edge : edges) {
uv_edge->append_to_uv_vertices();
}
}
Vector<std::pair<UVEdge *, UVEdge *>> shared_edges(UVPrimitive &other)
{
Vector<std::pair<UVEdge *, UVEdge *>> result;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (edges[i]->has_shared_edge(*other.edges[j])) {
result.append(std::pair<UVEdge *, UVEdge *>(edges[i], other.edges[j]));
}
}
}
return result;
}
bool has_shared_edge(const UVPrimitive &other) const
{
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (edges[i]->has_shared_edge(*other.edges[j])) {
return true;
}
}
}
return false;
}
bool has_shared_edge(const MeshPrimitive &primitive) const
{
for (const UVEdge *uv_edge : edges) {
const MeshUVVert *v1 = &primitive.vertices.last();
for (int i = 0; i < primitive.vertices.size(); i++) {
const MeshUVVert *v2 = &primitive.vertices[i];
if (uv_edge->has_shared_edge(*v1, *v2)) {
return true;
}
v1 = v2;
}
}
return false;
}
/**
* Get the UVVertex in the order that the verts are ordered in the MeshPrimitive.
*/
const UVVertex *get_uv_vertex(const uint8_t mesh_vert_index) const
{
const MeshVertex *mesh_vertex = primitive->vertices[mesh_vert_index].vertex;
for (const UVEdge *uv_edge : edges) {
for (const UVVertex *uv_vert : uv_edge->vertices) {
if (uv_vert->vertex == mesh_vertex) {
return uv_vert;
}
}
}
BLI_assert_unreachable();
return nullptr;
}
/**
* Get the UVEdge that share the given uv coordinates.
* Will assert when no UVEdge found.
*/
UVEdge *get_uv_edge(const float2 uv1, const float2 uv2) const
{
for (UVEdge *uv_edge : edges) {
const float2 &e1 = uv_edge->vertices[0]->uv;
const float2 &e2 = uv_edge->vertices[1]->uv;
if ((e1 == uv1 && e2 == uv2) || (e1 == uv2 && e2 == uv1)) {
return uv_edge;
}
}
BLI_assert_unreachable();
return nullptr;
}
UVEdge *get_uv_edge(const MeshVertex *v1, const MeshVertex *v2) const
{
for (UVEdge *uv_edge : edges) {
const MeshVertex *e1 = uv_edge->vertices[0]->vertex;
const MeshVertex *e2 = uv_edge->vertices[1]->vertex;
if ((e1 == v1 && e2 == v2) || (e1 == v2 && e2 == v1)) {
return uv_edge;
}
}
BLI_assert_unreachable();
return nullptr;
}
const bool contains_uv_vertex(const UVVertex *uv_vertex) const
{
for (UVEdge *edge : edges) {
if (std::find(edge->vertices.begin(), edge->vertices.end(), uv_vertex) !=
edge->vertices.end()) {
return true;
}
}
return false;
}
const UVVertex *get_other_uv_vertex(const UVVertex *v1, const UVVertex *v2) const
{
BLI_assert(contains_uv_vertex(v1));
BLI_assert(contains_uv_vertex(v2));
for (const UVEdge *edge : edges) {
for (const UVVertex *uv_vertex : edge->vertices) {
if (uv_vertex != v1 && uv_vertex != v2) {
return uv_vertex;
}
}
}
BLI_assert_unreachable();
return nullptr;
}
UVBorder extract_border() const;
};
struct UVBorderEdge {
UVEdge *edge;
bool tag = false;
UVPrimitive *uv_primitive;
/* Should the vertices of the edge be evaluated in reverse order. */
bool reverse_order = false;
int64_t index = -1;
int64_t prev_index = -1;
int64_t next_index = -1;
int64_t border_index = -1;
explicit UVBorderEdge(UVEdge *edge, UVPrimitive *uv_primitive)
: edge(edge), uv_primitive(uv_primitive)
{
}
UVVertex *get_uv_vertex(int index)
{
int actual_index = reverse_order ? 1 - index : index;
return edge->vertices[actual_index];
}
const UVVertex *get_uv_vertex(int index) const
{
int actual_index = reverse_order ? 1 - index : index;
return edge->vertices[actual_index];
}
/**
* Get the uv vertex from the primitive that is not part of the edge.
*/
const UVVertex *get_other_uv_vertex() const
{
return uv_primitive->get_other_uv_vertex(edge->vertices[0], edge->vertices[1]);
}
float length() const
{
return len_v2v2(edge->vertices[0]->uv, edge->vertices[1]->uv);
}
};
struct UVBorderCorner {
UVBorderEdge *first;
UVBorderEdge *second;
float angle;
UVBorderCorner(UVBorderEdge *first, UVBorderEdge *second, float angle)
: first(first), second(second), angle(angle)
{
}
/**
* Calculate a uv coordinate between the edges of the corner.
*
* 'min_uv_distance' is the minimum distance between the corner and the
* resulting uv coordinate. The distance is in uv space.
*/
float2 uv(float factor, float min_uv_distance);
};
struct UVBorder {
/** Ordered list of UV Verts of the border of this island. */
// TODO: support multiple rings + order (CW, CCW)
Vector<UVBorderEdge> edges;
/**
* Check if the border is counter clock wise from its island.
*/
bool is_ccw() const;
/**
* Flip the order of the verts, changing the order between CW and CCW.
*/
void flip();
/**
* Calculate the outside angle of the given vert.
*/
float outside_angle(const UVBorderEdge &vert) const;
void update_indexes(uint64_t border_index);
static std::optional<UVBorder> extract_from_edges(Vector<UVBorderEdge> &edges);
/** Remove edge from the border. updates the indexes. */
void remove(int64_t index)
{
/* Could read the border_index from any border edge as they are consistent. */
uint64_t border_index = edges[0].border_index;
edges.remove(index);
update_indexes(border_index);
}
};
struct UVIsland {
VectorList<UVVertex> uv_vertices;
VectorList<UVEdge> uv_edges;
VectorList<UVPrimitive> uv_primitives;
/**
* List of borders of this island. There can be multiple borders per island as a border could
* be completely encapsulated by another one.
*/
Vector<UVBorder> borders;
/**
* Key is mesh vert index, Value is list of UVVertices that refer to the mesh vertex with that
* index. Map is used internally to quickly lookup similar UVVertices.
*/
Map<int64_t, Vector<UVVertex *>> uv_vertex_lookup;
UVVertex *lookup(const UVVertex &vertex)
{
int64_t vert_index = vertex.vertex->v;
Vector<UVVertex *> &vertices = uv_vertex_lookup.lookup_or_add_default(vert_index);
for (UVVertex *v : vertices) {
if (v->uv == vertex.uv) {
return v;
}
}
return nullptr;
}
UVVertex *lookup_or_create(const UVVertex &vertex)
{
UVVertex *found_vertex = lookup(vertex);
if (found_vertex != nullptr) {
return found_vertex;
}
uv_vertices.append(vertex);
UVVertex *result = &uv_vertices.last();
result->uv_edges.clear();
/* v is already a key. Ensured by UVIsland::lookup in this method. */
uv_vertex_lookup.lookup(vertex.vertex->v).append(result);
return result;
}
UVEdge *lookup(const UVEdge &edge)
{
UVVertex *found_vertex = lookup(*edge.vertices[0]);
if (found_vertex == nullptr) {
return nullptr;
}
for (UVEdge *e : found_vertex->uv_edges) {
UVVertex *other_vertex = e->get_other_uv_vertex(found_vertex->vertex);
if (other_vertex->vertex == edge.vertices[1]->vertex &&
other_vertex->uv == edge.vertices[1]->uv) {
return e;
}
}
return nullptr;
}
UVEdge *lookup_or_create(const UVEdge &edge)
{
UVEdge *found_edge = lookup(edge);
if (found_edge != nullptr) {
return found_edge;
}
uv_edges.append(edge);
UVEdge *result = &uv_edges.last();
result->uv_primitives.clear();
return result;
}
/** Initialize the border attribute. */
void extract_borders();
/** Iterative extend border to fit the mask. */
void extend_border(const UVIslandsMask &mask, const short island_index);
private:
void append(const UVPrimitive &primitive)
{
uv_primitives.append(primitive);
UVPrimitive *new_prim_ptr = &uv_primitives.last();
for (int i = 0; i < 3; i++) {
UVEdge *other_edge = primitive.edges[i];
UVEdge uv_edge_template;
uv_edge_template.vertices[0] = lookup_or_create(*other_edge->vertices[0]);
uv_edge_template.vertices[1] = lookup_or_create(*other_edge->vertices[1]);
new_prim_ptr->edges[i] = lookup_or_create(uv_edge_template);
new_prim_ptr->edges[i]->append_to_uv_vertices();
new_prim_ptr->edges[i]->uv_primitives.append(new_prim_ptr);
}
}
public:
bool has_shared_edge(const UVPrimitive &primitive) const
{
for (const VectorList<UVPrimitive>::UsedVector &prims : uv_primitives) {
for (const UVPrimitive &prim : prims) {
if (prim.has_shared_edge(primitive)) {
return true;
}
}
}
return false;
}
bool has_shared_edge(const MeshPrimitive &primitive) const
{
for (const VectorList<UVPrimitive>::UsedVector &primitives : uv_primitives) {
for (const UVPrimitive &prim : primitives) {
if (prim.has_shared_edge(primitive)) {
return true;
}
}
}
return false;
}
const void extend_border(const UVPrimitive &primitive)
{
for (const VectorList<UVPrimitive>::UsedVector &primitives : uv_primitives) {
for (const UVPrimitive &prim : primitives) {
if (prim.has_shared_edge(primitive)) {
append(primitive);
}
}
}
}
};
struct UVIslands {
Vector<UVIsland> islands;
explicit UVIslands(MeshData &mesh_data);
void extract_borders();
void extend_borders(const UVIslandsMask &islands_mask);
};
/** Mask to find the index of the UVIsland for a given UV coordinate. */
struct UVIslandsMask {
/** Mask for each udim tile. */
struct Tile {
float2 udim_offset;
ushort2 tile_resolution;
ushort2 mask_resolution;
Array<uint16_t> mask;
Tile(float2 udim_offset, ushort2 tile_resolution);
bool is_masked(const uint16_t island_index, const float2 uv) const;
bool contains(const float2 uv) const;
float get_pixel_size_in_uv_space() const;
};
Vector<Tile> tiles;
void add_tile(float2 udim_offset, ushort2 resolution);
/**
* Find a tile containing the given uv coordinate.
*/
const Tile *find_tile(const float2 uv) const;
/**
* Is the given uv coordinate part of the given island_index mask.
*
* true - part of the island mask.
* false - not part of the island mask.
*/
bool is_masked(const uint16_t island_index, const float2 uv) const;
/**
* Add the given UVIslands to the mask. Tiles should be added beforehand using the 'add_tile'
* method.
*/
void add(const UVIslands &islands);
void dilate(int max_iterations);
};
} // namespace blender::bke::uv_islands

View File

@@ -250,7 +250,6 @@ set(SRC
intern/pbvh.cc
intern/pbvh_bmesh.c
intern/pbvh_pixels.cc
intern/pbvh_uv_islands.cc
intern/pointcache.c
intern/pointcloud.cc
intern/preferences.c

View File

@@ -1122,8 +1122,7 @@ static bool collection_object_remove(Main *bmain,
id_us_min(&ob->id);
}
collection_tag_update_parent_recursive(
bmain, collection, ID_RECALC_COPY_ON_WRITE | ID_RECALC_GEOMETRY);
collection_tag_update_parent_recursive(bmain, collection, ID_RECALC_COPY_ON_WRITE);
return true;
}

View File

@@ -1709,133 +1709,6 @@ void BKE_fcurve_delete_keys_all(FCurve *fcu)
fcurve_bezt_free(fcu);
}
/* Time + Average value */
typedef struct tRetainedKeyframe {
struct tRetainedKeyframe *next, *prev;
float frame; /* frame to cluster around */
float val; /* average value */
size_t tot_count; /* number of keyframes that have been averaged */
size_t del_count; /* number of keyframes of this sort that have been deleted so far */
} tRetainedKeyframe;
void BKE_fcurve_merge_duplicate_keys(FCurve *fcu, const int sel_flag, const bool use_handle)
{
/* NOTE: We assume that all keys are sorted */
ListBase retained_keys = {NULL, NULL};
const bool can_average_points = ((fcu->flag & (FCURVE_INT_VALUES | FCURVE_DISCRETE_VALUES)) ==
0);
/* sanity checks */
if ((fcu->totvert == 0) || (fcu->bezt == NULL)) {
return;
}
/* 1) Identify selected keyframes, and average the values on those
* in case there are collisions due to multiple keys getting scaled
* to all end up on the same frame
*/
for (int i = 0; i < fcu->totvert; i++) {
BezTriple *bezt = &fcu->bezt[i];
if (BEZT_ISSEL_ANY(bezt)) {
bool found = false;
/* If there's another selected frame here, merge it */
for (tRetainedKeyframe *rk = retained_keys.last; rk; rk = rk->prev) {
if (IS_EQT(rk->frame, bezt->vec[1][0], BEZT_BINARYSEARCH_THRESH)) {
rk->val += bezt->vec[1][1];
rk->tot_count++;
found = true;
break;
}
if (rk->frame < bezt->vec[1][0]) {
/* Terminate early if have passed the supposed insertion point? */
break;
}
}
/* If nothing found yet, create a new one */
if (found == false) {
tRetainedKeyframe *rk = MEM_callocN(sizeof(tRetainedKeyframe), "tRetainedKeyframe");
rk->frame = bezt->vec[1][0];
rk->val = bezt->vec[1][1];
rk->tot_count = 1;
BLI_addtail(&retained_keys, rk);
}
}
}
if (BLI_listbase_is_empty(&retained_keys)) {
/* This may happen if none of the points were selected... */
if (G.debug & G_DEBUG) {
printf("%s: nothing to do for FCurve %p (rna_path = '%s')\n", __func__, fcu, fcu->rna_path);
}
return;
}
/* Compute the average values for each retained keyframe */
LISTBASE_FOREACH (tRetainedKeyframe *, rk, &retained_keys) {
rk->val = rk->val / (float)rk->tot_count;
}
/* 2) Delete all keyframes duplicating the "retained keys" found above
* - Most of these will be unselected keyframes
* - Some will be selected keyframes though. For those, we only keep the last one
* (or else everything is gone), and replace its value with the averaged value.
*/
for (int i = fcu->totvert - 1; i >= 0; i--) {
BezTriple *bezt = &fcu->bezt[i];
/* Is this keyframe a candidate for deletion? */
/* TODO: Replace loop with an O(1) lookup instead */
for (tRetainedKeyframe *rk = retained_keys.last; rk; rk = rk->prev) {
if (IS_EQT(bezt->vec[1][0], rk->frame, BEZT_BINARYSEARCH_THRESH)) {
/* Selected keys are treated with greater care than unselected ones... */
if (BEZT_ISSEL_ANY(bezt)) {
/* - If this is the last selected key left (based on rk->del_count) ==> UPDATE IT
* (or else we wouldn't have any keyframe left here)
* - Otherwise, there are still other selected keyframes on this frame
* to be merged down still ==> DELETE IT
*/
if (rk->del_count == rk->tot_count - 1) {
/* Update keyframe... */
if (can_average_points) {
/* TODO: update handles too? */
bezt->vec[1][1] = rk->val;
}
}
else {
/* Delete Keyframe */
BKE_fcurve_delete_key(fcu, i);
}
/* Update count of how many we've deleted
* - It should only matter that we're doing this for all but the last one
*/
rk->del_count++;
}
else {
/* Always delete - Unselected keys don't matter */
BKE_fcurve_delete_key(fcu, i);
}
/* Stop the RK search... we've found our match now */
break;
}
}
}
/* 3) Recalculate handles */
testhandles_fcurve(fcu, sel_flag, use_handle);
/* cleanup */
BLI_freelistN(&retained_keys);
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@@ -1490,13 +1490,13 @@ int BKE_mesh_edge_other_vert(const MEdge *e, int v)
return -1;
}
void BKE_mesh_looptri_get_real_edges(const MEdge *edges,
const MLoop *loops,
const MLoopTri *tri,
int r_edges[3])
void BKE_mesh_looptri_get_real_edges(const Mesh *mesh, const MLoopTri *looptri, int r_edges[3])
{
const Span<MEdge> edges = mesh->edges();
const Span<MLoop> loops = mesh->loops();
for (int i = 2, i_next = 0; i_next < 3; i = i_next++) {
const MLoop *l1 = &loops[tri->tri[i]], *l2 = &loops[tri->tri[i_next]];
const MLoop *l1 = &loops[looptri->tri[i]], *l2 = &loops[looptri->tri[i_next]];
const MEdge *e = &edges[l1->e];
bool is_real = (l1->v == e->v1 && l2->v == e->v2) || (l1->v == e->v2 && l2->v == e->v1);

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