Compare commits
19 Commits
tracking_t
...
temp-ui-cp
Author | SHA1 | Date | |
---|---|---|---|
f77be09171 | |||
1a3681b83c | |||
3c2073d844 | |||
a1ea6d6496 | |||
1836acbeac | |||
ae3f443220 | |||
b9d1b07a45 | |||
dd1be8db19 | |||
6450f380ac | |||
1051c17af3 | |||
87628abaa1 | |||
462177fd62 | |||
5d221a2a8a | |||
fb4b7aaa8f | |||
6183f63250 | |||
08228956d9 | |||
956be86c09 | |||
e2f9602be2 | |||
24ae9c52d8 |
@@ -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
|
||||
|
@@ -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
|
||||
========
|
||||
|
||||
|
@@ -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,
|
||||
)
|
||||
|
@@ -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, "
|
||||
|
@@ -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)
|
||||
|
@@ -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
@@ -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);
|
||||
};
|
||||
|
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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));
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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__ */
|
||||
|
@@ -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)
|
||||
|
@@ -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,
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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. */
|
||||
|
@@ -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;
|
||||
|
@@ -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. */
|
||||
|
@@ -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. */
|
||||
|
@@ -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,
|
||||
|
@@ -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) ?
|
||||
|
@@ -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
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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
|
@@ -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
|
@@ -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
|
||||
|
@@ -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
|
@@ -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
|
||||
|
@@ -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
|
@@ -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
|
@@ -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;
|
||||
|
@@ -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"
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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,
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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()
|
||||
|
@@ -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 {
|
||||
|
@@ -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. */
|
||||
|
@@ -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;
|
||||
|
Submodule release/datafiles/locale updated: 4a581c54af...ef57e2c2c6
Submodule release/scripts/addons updated: fdfd24de03...bde68da02f
Submodule release/scripts/addons_contrib updated: 96143b1a8b...e6179b3b11
@@ -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))
|
||||
|
@@ -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)),
|
||||
|
@@ -17,7 +17,6 @@ _modules = [
|
||||
"file",
|
||||
"geometry_nodes",
|
||||
"image",
|
||||
"mask",
|
||||
"mesh",
|
||||
"node",
|
||||
"object",
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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,
|
||||
)
|
@@ -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()
|
||||
|
@@ -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"
|
||||
|
||||
|
@@ -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,
|
||||
|
@@ -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():
|
||||
|
@@ -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="")
|
||||
|
@@ -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':
|
||||
|
@@ -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,
|
||||
)
|
||||
|
@@ -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()
|
||||
|
@@ -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
|
@@ -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);
|
||||
};
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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_
|
||||
|
@@ -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 =
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
@@ -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
|
@@ -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
|
||||
|
@@ -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 -------- */
|
||||
|
||||
/**
|
||||
|
@@ -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 {
|
||||
|
@@ -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]);
|
||||
|
||||
|
@@ -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.
|
||||
*/
|
||||
|
@@ -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);
|
||||
|
@@ -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 */
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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.
|
||||
|
@@ -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
|
||||
|
@@ -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
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@@ -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
Reference in New Issue
Block a user