Mesh: Replace auto smooth with node group #108014

Merged
Hans Goudey merged 149 commits from HooglyBoogly/blender:refactor-mesh-corner-normals-lazy into main 2023-10-20 16:54:20 +02:00
164 changed files with 3948 additions and 2725 deletions
Showing only changes of commit 30502bddc0 - Show all commits

View File

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

View File

@ -86,6 +86,14 @@ 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",
@ -1043,13 +1051,13 @@ class CyclesCameraSettings(bpy.types.PropertyGroup):
class CyclesMaterialSettings(bpy.types.PropertyGroup):
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,
emission_sampling: EnumProperty(
name="Emission Sampling",
description="Sampling strategy for emissive surfaces",
items=enum_emission_sampling,
default="AUTO",
)
use_transparent_shadow: BoolProperty(
name="Transparent Shadows",
description="Use transparent shadows for this material if it contains a Transparent BSDF, "

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -61,6 +61,12 @@ 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) {
@ -1559,7 +1565,7 @@ void BlenderSync::sync_materials(BL::Depsgraph &b_depsgraph, bool update_all)
/* settings */
PointerRNA cmat = RNA_pointer_get(&b_mat.ptr, "cycles");
shader->set_use_mis(get_boolean(cmat, "sample_as_light"));
shader->set_emission_sampling_method(get_emission_sampling(cmat));
shader->set_use_transparent_shadow(get_boolean(cmat, "use_transparent_shadow"));
shader->set_heterogeneous_volume(!get_boolean(cmat, "homogeneous_volume"));
shader->set_volume_sampling_method(get_volume_sampling(cmat));

View File

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

View File

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

View File

@ -285,10 +285,16 @@ set(SRC_KERNEL_INTEGRATOR_HEADERS
)
set(SRC_KERNEL_LIGHT_HEADERS
light/light.h
light/area.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
@ -466,6 +472,7 @@ 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
@ -473,7 +480,6 @@ if(WITH_CYCLES_CUDA_BINARIES)
${CUDA_NVCC_FLAGS}
--${format}
${CMAKE_CURRENT_SOURCE_DIR}${cuda_kernel_src}
--ptxas-options="-v"
${cuda_flags})
if(WITH_COMPILER_CCACHE AND CCACHE_PROGRAM)

View File

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

View File

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

View File

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

View File

@ -346,7 +346,7 @@ ccl_device int bsdf_microfacet_ggx_refraction_setup(ccl_private MicrofacetBsdf *
bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
return SD_BSDF | SD_BSDF_HAS_EVAL;
return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_HAS_TRANSMISSION;
}
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;
return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_HAS_TRANSMISSION;
}
ccl_device void bsdf_microfacet_beckmann_blur(ccl_private ShaderClosure *sc, float roughness)

View File

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

View File

@ -23,24 +23,19 @@ 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)
/* Padding. */
KERNEL_STRUCT_MEMBER(background, int, pad1)
KERNEL_STRUCT_MEMBER(background, int, pad2)
KERNEL_STRUCT_MEMBER(background, int, pad3)
/* Light Index. */
KERNEL_STRUCT_MEMBER(background, int, light_index)
KERNEL_STRUCT_END(KernelBackground)
/* BVH: own BVH2 if no native device acceleration struct used. */
@ -147,10 +142,17 @@ 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, int, num_all_lights)
KERNEL_STRUCT_MEMBER(integrator, float, pdf_triangles)
KERNEL_STRUCT_MEMBER(integrator, float, pdf_lights)
KERNEL_STRUCT_MEMBER(integrator, float, distribution_pdf_triangles)
KERNEL_STRUCT_MEMBER(integrator, float, distribution_pdf_lights)
KERNEL_STRUCT_MEMBER(integrator, float, light_inv_rr_threshold)
/* Bounces. */
KERNEL_STRUCT_MEMBER(integrator, int, min_bounce)
@ -177,8 +179,6 @@ 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,7 +195,6 @@ KERNEL_STRUCT_MEMBER(integrator, int, has_shadow_catcher)
KERNEL_STRUCT_MEMBER(integrator, int, filter_closures)
/* MIS debugging. */
KERNEL_STRUCT_MEMBER(integrator, int, direct_light_sampling_type)
/* Path Guiding */
KERNEL_STRUCT_MEMBER(integrator, float, surface_guiding_probability)
KERNEL_STRUCT_MEMBER(integrator, float, volume_guiding_probability)

View File

@ -314,11 +314,7 @@ 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(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE,
num_states,
indices,
num_indices,
ccl_gpu_kernel_lambda_pass);
gpu_parallel_active_index_array(num_states, indices, num_indices, ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
@ -333,11 +329,7 @@ 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(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE,
num_states,
indices,
num_indices,
ccl_gpu_kernel_lambda_pass);
gpu_parallel_active_index_array(num_states, indices, num_indices, ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
@ -349,11 +341,7 @@ 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(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE,
num_states,
indices,
num_indices,
ccl_gpu_kernel_lambda_pass);
gpu_parallel_active_index_array(num_states, indices, num_indices, ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
@ -366,11 +354,8 @@ 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(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE,
num_states,
indices + indices_offset,
num_indices,
ccl_gpu_kernel_lambda_pass);
gpu_parallel_active_index_array(
num_states, indices + indices_offset, num_indices, ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
@ -383,11 +368,8 @@ 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(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE,
num_states,
indices + indices_offset,
num_indices,
ccl_gpu_kernel_lambda_pass);
gpu_parallel_active_index_array(
num_states, indices + indices_offset, num_indices, ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
@ -431,11 +413,7 @@ 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(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE,
num_states,
indices,
num_indices,
ccl_gpu_kernel_lambda_pass);
gpu_parallel_active_index_array(num_states, indices, num_indices, ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
@ -469,11 +447,7 @@ 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(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE,
num_states,
indices,
num_indices,
ccl_gpu_kernel_lambda_pass);
gpu_parallel_active_index_array(num_states, indices, num_indices, ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix

View File

@ -56,7 +56,7 @@ void gpu_parallel_active_index_array_impl(const uint num_states,
const uint is_active = (state_index < num_states) ? is_active_op(state_index) : 0;
#else /* !__KERNEL__ONEAPI__ */
# ifndef __KERNEL_METAL__
template<uint blocksize, typename IsActiveOp>
template<typename IsActiveOp>
__device__
# endif
void
@ -79,6 +79,10 @@ __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;
@ -149,7 +153,7 @@ __device__
#ifdef __KERNEL_METAL__
# define gpu_parallel_active_index_array(dummy, num_states, indices, num_indices, is_active_op) \
# define gpu_parallel_active_index_array(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; \
@ -167,15 +171,13 @@ __device__
simdgroup_offset)
#elif defined(__KERNEL_ONEAPI__)
# define gpu_parallel_active_index_array( \
blocksize, num_states, indices, num_indices, is_active_op) \
# 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)
#else
# 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)
# 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)
#endif

View File

@ -11,10 +11,10 @@
#include "kernel/integrator/path_state.h"
#include "kernel/integrator/shadow_catcher.h"
#include "kernel/light/light.h"
#include "kernel/geom/geom.h"
#include "kernel/light/light.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_lamp_mis) {
if (kernel_data.integrator.use_light_mis) {
/* NOTE: if we make lights visible to camera rays, we'll need to initialize
* these in the path_state_init. */
const int last_type = INTEGRATOR_STATE(state, isect, type);

View File

@ -108,48 +108,6 @@ 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,
@ -819,7 +777,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 */
mnee_update_light_sample(kg, vertices[vertex_count - 1].p, ls);
light_sample_update_position(kg, ls, vertices[vertex_count - 1].p);
/* Save state path bounce info in case a light path node is used in the refractive interface or
* light shader graph. */

View File

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

View File

@ -69,9 +69,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 &&
(INTEGRATOR_STATE(state, path, flag) &
PATH_RAY_TRANSPARENT_BACKGROUND);
(path_flag & PATH_RAY_TRANSPARENT_BACKGROUND);
if (is_transparent_background_ray) {
transparent = average(INTEGRATOR_STATE(state, path, throughput));
@ -86,7 +86,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_all_lights; lamp++) {
for (int lamp = 0; lamp < kernel_data.integrator.num_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,14 +116,7 @@ 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) {
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);
mis_weight = light_sample_mis_weight_forward_background(kg, state, path_flag);
}
guiding_record_background(kg, state, L, mis_weight);
@ -142,8 +135,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_all_lights; lamp++) {
if (light_sample_from_distant_ray(kg, ray_D, lamp, &ls)) {
for (int lamp = 0; lamp < kernel_data.integrator.num_lights; lamp++) {
if (distant_light_sample_from_intersection(kg, ray_D, lamp, &ls)) {
/* Use visibility flag to skip lights. */
#ifdef __PASSES__
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
@ -182,10 +175,7 @@ ccl_device_inline void integrate_distant_lights(KernelGlobals kg,
/* MIS weighting. */
float mis_weight = 1.0f;
if (!(path_flag & PATH_RAY_MIS_SKIP)) {
/* 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);
mis_weight = light_sample_mis_weight_forward_distant(kg, state, path_flag, &ls);
}
/* Write to render buffer. */

View File

@ -61,10 +61,7 @@ ccl_device_inline void integrate_light(KernelGlobals kg,
/* MIS weighting. */
float mis_weight = 1.0f;
if (!(path_flag & PATH_RAY_MIS_SKIP)) {
/* 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);
mis_weight = light_sample_mis_weight_forward_lamp(kg, state, path_flag, &ls, ray_P);
}
/* Write to render buffer. */

View File

@ -15,7 +15,6 @@
#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
@ -113,20 +112,16 @@ 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 (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS) &&
(sd->type & PRIMITIVE_TRIANGLE))
if (has_mis && (sd->type & PRIMITIVE_TRIANGLE))
#else
if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS))
if (has_mis)
#endif
{
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);
mis_weight = light_sample_mis_weight_forward_surface(kg, state, path_flag, sd);
}
guiding_record_surface_emission(kg, state, L, mis_weight);
@ -154,8 +149,17 @@ 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_distribution_sample_from_position(
kg, rand_light.x, rand_light.y, sd->time, sd->P, bounce, path_flag, &ls)) {
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)) {
return;
}
}
@ -445,7 +449,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);
path_state_next(kg, state, label, sd->flag);
guiding_record_surface_bounce(kg,
state,

View File

@ -690,6 +690,7 @@ 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)
@ -704,8 +705,16 @@ 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_distribution_sample_from_volume_segment(
kg, rand_light.x, rand_light.y, sd->time, sd->P, bounce, path_flag, ls)) {
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)) {
return false;
}
@ -737,18 +746,32 @@ 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.
*
* TODO: decorrelate random numbers and use light_sample_new_position to
* avoid resampling the CDF. */
* 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. */
{
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_distribution_sample_from_position(
kg, rand_light.x, rand_light.y, sd->time, P, bounce, path_flag, ls)) {
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)) {
return;
}
}
@ -961,7 +984,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);
path_state_next(kg, state, label, sd->flag);
return true;
}
@ -987,7 +1010,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, &sd, &rng_state, &ls) &&
kg, state, ray, &sd, &rng_state, &ls) &&
(ls.t != FLT_MAX);
VolumeSampleMethod direct_sample_method = (have_equiangular_sample) ?

View File

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

View File

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

View File

@ -7,92 +7,26 @@
CCL_NAMESPACE_BEGIN
/* Area light sampling */
/* Light Sample Result */
/* 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.
*/
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;
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;
}
/* Utilities */
ccl_device_inline float3 ellipse_sample(float3 ru, float3 rv, float randu, float randv)
{
@ -109,99 +43,7 @@ ccl_device float3 disk_light_sample(float3 v, float randu, float randv)
return ellipse_sample(ru, rv, randu, randv);
}
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)
ccl_device float lamp_light_pdf(const float3 Ng, const float3 I, float t)
{
float cos_pi = dot(Ng, I);

View File

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

View File

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

View File

@ -3,31 +3,18 @@
#pragma once
#include "kernel/geom/geom.h"
#include "kernel/light/area.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
/* 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 */
/* Sample point on an individual light. */
template<bool in_volume_segment>
ccl_device_inline bool light_sample(KernelGlobals kg,
@ -63,28 +50,15 @@ 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) {
/* 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;
if (!distant_light_sample(klight, randu, randv, ls)) {
return false;
}
}
else if (type == LIGHT_BACKGROUND) {
/* infinite area light (e.g. light dome or env light) */
@ -96,139 +70,28 @@ 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 {
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);
}
/* area light */
if (!area_light_sample<in_volume_segment>(klight, randu, randv, P, ls)) {
return false;
}
}
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,
@ -238,7 +101,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_all_lights; lamp++) {
for (int lamp = 0; lamp < kernel_data.integrator.num_lights; lamp++) {
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, lamp);
if (path_flag & PATH_RAY_CAMERA) {
@ -271,76 +134,17 @@ ccl_device bool lights_intersect(KernelGlobals kg,
float t = 0.0f, u = 0.0f, v = 0.0f;
if (type == LIGHT_SPOT) {
/* 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)) {
if (!spot_light_intersect(klight, ray, &t)) {
continue;
}
}
else if (type == LIGHT_POINT) {
/* 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)) {
if (!point_light_intersect(klight, ray, &t)) {
continue;
}
}
else if (type == LIGHT_AREA) {
/* 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)) {
if (!area_light_intersect(klight, ray, &t, &u, &v)) {
continue;
}
}
@ -362,78 +166,7 @@ ccl_device bool lights_intersect(KernelGlobals kg,
return isect->prim != PRIM_NONE;
}
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;
}
/* Setup light sample from intersection. */
ccl_device bool light_sample_from_intersection(KernelGlobals kg,
ccl_private const Intersection *ccl_restrict isect,
@ -456,102 +189,18 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg,
ls->group = lamp_lightgroup(kg, lamp);
if (type == LIGHT_SPOT) {
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) {
if (!spot_light_sample_from_intersection(klight, isect, ray_P, ray_D, ls)) {
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) {
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) {
if (!point_light_sample_from_intersection(klight, isect, ray_P, ray_D, ls)) {
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) {
/* 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;
}
if (!area_light_sample_from_intersection(klight, isect, ray_P, ray_D, ls)) {
return false;
}
}
else {
@ -559,411 +208,33 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg,
return false;
}
ls->pdf *= kernel_data.integrator.pdf_lights;
return true;
}
/* Triangle Light */
/* Update light sample for changed new position, for MNEE. */
/* 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(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;
}
}
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_device_forceinline void light_update_position(KernelGlobals kg,
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. */
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->lamp);
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;
if (ls->type == LIGHT_POINT) {
point_light_update_position(klight, ls, P);
}
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 if (ls->type == LIGHT_SPOT) {
spot_light_update_position(klight, ls, P);
}
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;
else if (ls->type == LIGHT_AREA) {
area_light_update_position(klight, ls, P);
}
}
/* 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 */
/* Light info. */
ccl_device_inline bool light_select_reached_max_bounces(KernelGlobals kg, int index, int bounce)
{
return (bounce > kernel_data_fetch(lights, index).max_bounces);
}
template<bool in_volume_segment>
ccl_device_noinline bool light_distribution_sample(KernelGlobals kg,
float randu,
const float randv,
const float time,
const float3 P,
const int bounce,
const uint32_t path_flag,
ccl_private LightSample *ls)
{
/* Sample light index from distribution. */
const int index = light_distribution_sample(kg, &randu);
ccl_global const KernelLightDistribution *kdistribution = &kernel_data_fetch(light_distribution,
index);
const int prim = kdistribution->prim;
if (prim >= 0) {
/* Mesh light. */
const int object = kdistribution->mesh_light.object_id;
/* Exclude synthetic meshes from shadow catcher pass. */
if ((path_flag & PATH_RAY_SHADOW_CATCHER_PASS) &&
!(kernel_data_fetch(object_flag, object) & SD_OBJECT_SHADOW_CATCHER)) {
return false;
}
const int shader_flag = kdistribution->mesh_light.shader_flag;
triangle_light_sample<in_volume_segment>(kg, prim, object, randu, randv, time, ls, P);
ls->shader |= shader_flag;
return (ls->pdf > 0.0f);
}
const int lamp = -prim - 1;
if (UNLIKELY(light_select_reached_max_bounces(kg, lamp, bounce))) {
return false;
}
return light_sample<in_volume_segment>(kg, lamp, randu, randv, P, path_flag, ls);
}
ccl_device_inline bool light_distribution_sample_from_volume_segment(KernelGlobals kg,
float randu,
const float randv,
const float time,
const float3 P,
const int bounce,
const uint32_t path_flag,
ccl_private LightSample *ls)
{
return light_distribution_sample<true>(kg, randu, randv, time, P, bounce, path_flag, ls);
}
ccl_device_inline bool light_distribution_sample_from_position(KernelGlobals kg,
float randu,
const float randv,
const float time,
const float3 P,
const int bounce,
const uint32_t path_flag,
ccl_private LightSample *ls)
{
return light_distribution_sample<false>(kg, randu, randv, time, P, bounce, path_flag, ls);
}
ccl_device_inline bool light_distribution_sample_new_position(KernelGlobals kg,
const float randu,
const float randv,
const float time,
const float3 P,
ccl_private LightSample *ls)
{
/* Sample a new position on the same light, for volume sampling. */
if (ls->type == LIGHT_TRIANGLE) {
triangle_light_sample<false>(kg, ls->prim, ls->object, randu, randv, time, ls, P);
return (ls->pdf > 0.0f);
}
else {
return light_sample<false>(kg, ls->lamp, randu, randv, P, 0, ls);
}
}
CCL_NAMESPACE_END

View File

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

View File

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

View File

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

View File

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

View File

@ -209,21 +209,26 @@ 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 << 11U) - 1U),
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 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),
@ -462,6 +467,16 @@ 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 {
@ -775,14 +790,16 @@ 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_EXTINCTION | SD_SCATTER | SD_BSDF_NEEDS_LCG | SD_BSDF_HAS_TRANSMISSION),
/* Shader flags. */
/* direct light sample */
SD_USE_MIS = (1 << 16),
/* Use front side for direct light sampling. */
SD_MIS_FRONT = (1 << 16),
/* Has transparent shadow. */
SD_HAS_TRANSPARENT_SHADOW = (1 << 17),
/* Has volume shader. */
@ -811,12 +828,14 @@ 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_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)
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)
};
/* Object flags. */
@ -1268,20 +1287,20 @@ static_assert_align(KernelCurveSegment, 8);
typedef struct KernelSpotLight {
float radius;
float invarea;
float spot_angle;
float cos_half_spot_angle;
float spot_smooth;
float dir[3];
packed_float3 dir;
float pad;
} KernelSpotLight;
/* PointLight is SpotLight with only radius and invarea being used. */
typedef struct KernelAreaLight {
float axisu[3];
packed_float3 extentu;
float invarea;
float axisv[3];
packed_float3 extentv;
float tan_spread;
float dir[3];
packed_float3 dir;
float normalize_spread;
} KernelAreaLight;
@ -1294,7 +1313,7 @@ typedef struct KernelDistantLight {
typedef struct KernelLight {
int type;
float co[3];
packed_float3 co;
int shader_id;
float max_bounces;
float random;

View File

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

View File

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

View File

@ -162,7 +162,9 @@ bool Light::has_contribution(Scene *scene)
if (light_type == LIGHT_BACKGROUND) {
return true;
}
return (shader) ? shader->has_surface_emission : scene->default_light->has_surface_emission;
const Shader *effective_shader = (shader) ? shader : scene->default_light;
return !is_zero(effective_shader->emission_estimate);
}
/* Light Manager */
@ -256,37 +258,27 @@ bool LightManager::object_usable_as_light(Object *object)
*/
foreach (Node *node, geom->get_used_shaders()) {
Shader *shader = static_cast<Shader *>(node);
if (shader->get_use_mis() && shader->has_surface_emission) {
if (shader->emission_sampling != EMISSION_SAMPLING_NONE) {
return true;
}
}
return false;
}
void LightManager::device_update_distribution(Device *,
void LightManager::device_update_distribution(Device *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");
/* count */
size_t num_lights = 0;
size_t num_portals = 0;
size_t num_background_lights = 0;
/* Counts emissive triangles in the scene. */
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;
@ -295,29 +287,36 @@ void LightManager::device_update_distribution(Device *,
continue;
}
/* Count triangles. */
/* Count emissive 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->get_use_mis() && shader->has_surface_emission) {
if (shader->emission_sampling != EMISSION_SAMPLING_NONE) {
num_triangles++;
}
}
}
size_t num_distribution = num_triangles + num_lights;
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;
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;
@ -362,7 +361,7 @@ void LightManager::device_update_distribution(Device *,
static_cast<Shader *>(mesh->get_used_shaders()[shader_index]) :
scene->default_surface;
if (shader->get_use_mis() && shader->has_surface_emission) {
if (shader->emission_sampling != EMISSION_SAMPLING_NONE) {
distribution[offset].totarea = totarea;
distribution[offset].prim = i + mesh->prim_offset;
distribution[offset].mesh_light.shader_flag = shader_flag;
@ -390,9 +389,9 @@ void LightManager::device_update_distribution(Device *,
j++;
}
float trianglearea = totarea;
/* point lights */
bool use_lamp_mis = false;
const float trianglearea = totarea;
/* Lights. */
int light_index = 0;
if (num_lights > 0) {
@ -407,20 +406,6 @@ void LightManager::device_update_distribution(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++;
}
@ -441,82 +426,42 @@ void LightManager::device_update_distribution(Device *,
if (progress.get_cancel())
return;
/* update device */
KernelIntegrator *kintegrator = &dscene->data.integrator;
KernelBackground *kbackground = &dscene->data.background;
KernelFilm *kfilm = &dscene->data.film;
/* Update integrator state. */
kintegrator->use_direct_light = (totarea > 0.0f);
if (kintegrator->use_direct_light) {
/* number of emissives */
kintegrator->num_distribution = num_distribution;
/* 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 (trianglearea > 0.0f) {
kintegrator->pdf_triangles = 1.0f / trianglearea;
if (num_lights)
kintegrator->pdf_triangles *= 0.5f;
}
/* 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 (trianglearea > 0.0f) {
kintegrator->distribution_pdf_triangles = 1.0f / trianglearea;
if (num_lights) {
kintegrator->pdf_lights = 1.0f / num_lights;
if (trianglearea > 0.0f)
kintegrator->pdf_lights *= 0.5f;
kintegrator->distribution_pdf_triangles *= 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();
kintegrator->num_distribution = 0;
kintegrator->num_all_lights = 0;
kintegrator->pdf_triangles = 0.0f;
kintegrator->pdf_lights = 0.0f;
kintegrator->use_lamp_mis = false;
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_lights) {
kintegrator->distribution_pdf_lights = 1.0f / num_lights;
if (trianglearea > 0.0f) {
kintegrator->distribution_pdf_lights *= 0.5f;
}
}
/* 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->distribution_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;
}
/* Copy distribution to device. */
dscene->light_distribution.copy_to_device();
}
static void background_cdf(
@ -564,31 +509,34 @@ 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) {
if (light->light_type == LIGHT_BACKGROUND && light->is_enabled) {
background_light = light;
break;
background_mis |= light->use_mis;
}
}
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;
@ -632,6 +580,7 @@ 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);
@ -714,27 +663,83 @@ void LightManager::device_update_background(Device *device,
dscene->light_background_conditional_cdf.copy_to_device();
}
void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *scene)
void LightManager::device_update_lights(Device *, DeviceScene *dscene, Scene *scene)
{
int num_scene_lights = scene->lights.size();
/* 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_lights = 0;
foreach (Light *light, scene->lights) {
if (light->is_enabled || light->is_portal) {
if (light->is_enabled) {
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++;
}
}
KernelLight *klights = dscene->lights.alloc(num_lights);
/* 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;
if (num_lights == 0) {
VLOG_WORK << "No effective light, ignoring points update.";
return;
}
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);
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;
}
@ -781,10 +786,7 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc
if (light->use_mis && radius > 0.0f)
shader_id |= SHADER_USE_MIS;
klights[light_index].co[0] = co.x;
klights[light_index].co[1] = co.y;
klights[light_index].co[2] = co.z;
klights[light_index].co = co;
klights[light_index].spot.radius = radius;
klights[light_index].spot.invarea = invarea;
}
@ -803,10 +805,7 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc
if (light->use_mis && area > 0.0f)
shader_id |= SHADER_USE_MIS;
klights[light_index].co[0] = dir.x;
klights[light_index].co[1] = dir.y;
klights[light_index].co[2] = dir.z;
klights[light_index].co = dir;
klights[light_index].distant.invarea = invarea;
klights[light_index].distant.radius = radius;
klights[light_index].distant.cosangle = cosangle;
@ -814,6 +813,8 @@ void LightManager::device_update_points(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;
@ -831,9 +832,9 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc
}
}
else if (light->light_type == LIGHT_AREA) {
float3 axisu = light->axisu * (light->sizeu * light->size);
float3 axisv = light->axisv * (light->sizev * light->size);
float area = len(axisu) * len(axisv);
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;
}
@ -854,20 +855,11 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc
if (light->use_mis && area != 0.0f)
shader_id |= SHADER_USE_MIS;
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].co = co;
klights[light_index].area.extentu = extentu;
klights[light_index].area.extentv = extentv;
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].area.dir = dir;
klights[light_index].area.tan_spread = tan_spread;
klights[light_index].area.normalize_spread = normalize_spread;
}
@ -876,8 +868,8 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc
float radius = light->size;
float invarea = (radius > 0.0f) ? 1.0f / (M_PI_F * radius * radius) : 1.0f;
float spot_angle = cosf(light->spot_angle * 0.5f);
float spot_smooth = (1.0f - spot_angle) * light->spot_smooth;
float cos_half_spot_angle = cosf(light->spot_angle * 0.5f);
float spot_smooth = (1.0f - cos_half_spot_angle) * light->spot_smooth;
float3 dir = light->dir;
dir = safe_normalize(dir);
@ -885,17 +877,12 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc
if (light->use_mis && radius > 0.0f)
shader_id |= SHADER_USE_MIS;
klights[light_index].co[0] = co.x;
klights[light_index].co[1] = co.y;
klights[light_index].co[2] = co.z;
klights[light_index].co = co;
klights[light_index].spot.radius = radius;
klights[light_index].spot.invarea = invarea;
klights[light_index].spot.spot_angle = spot_angle;
klights[light_index].spot.cos_half_spot_angle = cos_half_spot_angle;
klights[light_index].spot.spot_smooth = spot_smooth;
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].spot.dir = dir;
}
klights[light_index].shader_id = shader_id;
@ -918,49 +905,7 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc
light_index++;
}
/* 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;
VLOG_INFO << "Number of lights sent to the device: " << num_lights;
dscene->lights.copy_to_device();
}
@ -986,11 +931,7 @@ void LightManager::device_update(Device *device,
device_free(device, dscene, need_update_background);
device_update_points(device, dscene, scene);
if (progress.get_cancel())
return;
device_update_distribution(device, dscene, scene, progress);
device_update_lights(device, dscene, scene);
if (progress.get_cancel())
return;
@ -1000,6 +941,10 @@ void LightManager::device_update(Device *device,
return;
}
device_update_distribution(device, dscene, scene, progress);
if (progress.get_cancel())
return;
device_update_ies(dscene);
if (progress.get_cancel())
return;

View File

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

View File

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

View File

@ -137,7 +137,7 @@ void OSLShaderManager::device_update_specific(Device *device,
compiler.compile(og, shader);
});
if (shader->get_use_mis() && shader->has_surface_emission)
if (shader->emission_sampling != EMISSION_SAMPLING_NONE)
scene->light_manager->tag_update(scene, LightManager::SHADER_COMPILED);
}
@ -819,8 +819,11 @@ void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath)
if (current_type == SHADER_TYPE_SURFACE) {
if (info) {
if (info->has_surface_emission)
current_shader->has_surface_emission = true;
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_transparent)
current_shader->has_surface_transparent = true;
if (info->has_surface_bssrdf) {
@ -1120,8 +1123,6 @@ 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)
@ -1213,7 +1214,6 @@ 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,6 +1256,9 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader)
}
else
shader->osl_displacement_ref = OSL::ShaderGroupRef();
/* Estimate emission for MIS. */
shader->estimate_emission();
}
/* push state to array for lookup */

View File

@ -147,7 +147,17 @@ NODE_DEFINE(Shader)
{
NodeType *type = NodeType::add("shader", create);
SOCKET_BOOLEAN(use_mis, "Use MIS", true);
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_transparent_shadow, "Use Transparent Shadow", true);
SOCKET_BOOLEAN(heterogeneous_volume, "Heterogeneous Volume", true);
@ -189,7 +199,6 @@ 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;
@ -203,6 +212,10 @@ 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;
@ -217,50 +230,141 @@ Shader::~Shader()
delete graph;
}
bool Shader::is_constant_emission(float3 *emission)
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()
{
/* 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) {
return false;
emission_is_constant = false;
}
}
ShaderInput *surf = graph->output()->input("Surface");
emission_estimate = output_estimate_emission(surf->link, emission_is_constant);
if (surf->link == NULL) {
return false;
if (is_zero(emission_estimate)) {
emission_sampling = 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 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;
}
else {
return false;
emission_sampling = emission_sampling_method;
}
return true;
}
void Shader::set_graph(ShaderGraph *graph_)
@ -305,7 +409,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 (use_mis && has_surface_emission)
if (emission_sampling != EMISSION_SAMPLING_NONE)
scene->light_manager->tag_update(scene, LightManager::SHADER_MODIFIED);
/* Special handle of background MIS light for now: for some reason it
@ -491,9 +595,17 @@ void ShaderManager::device_update_common(Device * /*device*/,
foreach (Shader *shader, scene->shaders) {
uint flag = 0;
if (shader->get_use_mis())
flag |= SD_USE_MIS;
if (shader->has_surface_emission)
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))
flag |= SD_HAS_EMISSION;
if (shader->has_surface_transparent && shader->get_use_transparent_shadow())
flag |= SD_HAS_TRANSPARENT_SHADOW;
@ -531,8 +643,7 @@ void ShaderManager::device_update_common(Device * /*device*/,
flag |= SD_HAS_DISPLACEMENT;
/* constant emission check */
float3 constant_emission = zero_float3();
if (shader->is_constant_emission(&constant_emission))
if (shader->emission_is_constant)
flag |= SD_HAS_CONSTANT_EMISSION;
uint32_t cryptomatte_id = util_murmur_hash3(shader->name.c_str(), shader->name.length(), 0);
@ -540,9 +651,9 @@ void ShaderManager::device_update_common(Device * /*device*/,
/* regular shader */
kshader->flags = flag;
kshader->pass_id = shader->get_pass_id();
kshader->constant_emission[0] = constant_emission.x;
kshader->constant_emission[1] = constant_emission.y;
kshader->constant_emission[2] = constant_emission.z;
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->cryptomatte_id = util_hash_to_float(cryptomatte_id);
kshader++;
@ -627,8 +738,8 @@ void ShaderManager::add_default(Scene *scene)
shader->set_graph(graph);
scene->default_volume = shader;
shader->tag_update(scene);
/* No default reference for the volume to avoid compiling volume kernels if there are no actual
* volumes in the scene */
/* No default reference for the volume to avoid compiling volume kernels if there are no
* actual volumes in the scene */
}
/* default light */

View File

@ -34,6 +34,7 @@ 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,
@ -73,7 +74,7 @@ class Shader : public Node {
NODE_SOCKET_API(int, pass_id)
/* sampling */
NODE_SOCKET_API(bool, use_mis)
NODE_SOCKET_API(EmissionSampling, emission_sampling_method)
NODE_SOCKET_API(bool, use_transparent_shadow)
NODE_SOCKET_API(bool, heterogeneous_volume)
NODE_SOCKET_API(VolumeSampling, volume_sampling_method)
@ -101,7 +102,6 @@ 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,6 +114,10 @@ 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;
@ -131,11 +135,12 @@ class Shader : public Node {
Shader();
~Shader();
/* 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);
/* 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();
void set_graph(ShaderGraph *graph);
void tag_update(Scene *scene);

View File

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

View File

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

View File

@ -1530,6 +1530,11 @@ 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()
{
@ -1551,6 +1556,7 @@ class OSLNode final : public ShaderNode {
string filepath;
string bytecode_hash;
bool has_emission;
};
class NormalMapNode : public ShaderNode {

View File

@ -109,7 +109,7 @@ void SVMShaderManager::device_update_specific(Device *device,
Shader *shader = scene->shaders[i];
shader->clear_modified();
if (shader->get_use_mis() && shader->has_surface_emission) {
if (shader->emission_sampling != EMISSION_SAMPLING_NONE) {
scene->light_manager->tag_update(scene, LightManager::SHADER_COMPILED);
}
@ -516,8 +516,6 @@ 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()) {
@ -873,7 +871,6 @@ 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;
@ -928,6 +925,9 @@ void SVMCompiler::compile(Shader *shader, array<int4> &svm_nodes, int index, Sum
summary->peak_stack_usage = max_stack_use;
summary->num_svm_nodes = svm_nodes.size() - start_num_svm_nodes;
}
/* Estimate emission for MIS. */
shader->estimate_emission();
}
/* Compiler summary implementation. */

View File

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

@ -1 +1 @@
Subproject commit ef57e2c2c65933a68811d58b40ed62b775e9b4b0
Subproject commit 4a581c54af9b92cb670d750951b9382160f10f3e

@ -1 +1 @@
Subproject commit bde68da02fde93968dc11b52d42060ac3b81ed37
Subproject commit fdfd24de034d4bba4fb67731d0aae81dc4940239

@ -1 +1 @@
Subproject commit e6179b3b112298e131bbd0faf648bf0d392b6cdd
Subproject commit 96143b1a8b037ea3c81f065f557025db9fe1ace3

View File

@ -252,9 +252,14 @@ class NLA_OT_bake(Operator):
do_pose = 'POSE' in self.bake_types
do_object = 'OBJECT' in self.bake_types
objects = context.selected_editable_objects
if do_pose and not do_object:
objects = [obj for obj in objects if obj.pose is not None]
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]
object_action_pairs = (
[(obj, getattr(obj.animation_data, "action", None)) for obj in objects]

View File

@ -97,13 +97,12 @@ 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_library_ref)
asset_lib_path = bpy.types.AssetHandle.get_full_library_path(asset_file_handle)
self.open_in_new_blender(asset_lib_path)
wm = context.window_manager

View File

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

View File

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

View File

@ -24,6 +24,7 @@ struct Main;
namespace blender::asset_system {
class AssetIdentifier;
class AssetRepresentation;
class AssetStorage;
@ -34,7 +35,11 @@ class AssetStorage;
* to also include asset indexes and more.
*/
class AssetLibrary {
bCallbackFuncStore on_save_callback_store_{};
/** 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_;
/** 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
@ -51,6 +56,8 @@ 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. */
@ -59,10 +66,13 @@ class AssetLibrary {
std::unique_ptr<AssetCatalogService> catalog_service;
public:
AssetLibrary();
/**
* \param root_path: If this is an asset library on disk, the top-level directory path.
*/
AssetLibrary(StringRef root_path = "");
~AssetLibrary();
void load_catalogs(StringRefNull library_root_directory);
void load_catalogs();
/** Load catalogs that have changed on disk. */
void refresh();
@ -72,10 +82,16 @@ 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 name, std::unique_ptr<AssetMetaData> metadata);
AssetRepresentation &add_external_asset(StringRef relative_asset_path,
StringRef name,
std::unique_ptr<AssetMetaData> metadata);
/** See #AssetLibrary::add_external_asset(). */
AssetRepresentation &add_local_id_asset(ID &id);
AssetRepresentation &add_local_id_asset(StringRef relative_asset_path, 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).
@ -105,6 +121,14 @@ class AssetLibrary {
void on_blend_save_post(Main *bmain, PointerRNA **pointers, int num_pointers);
/**
* Create an asset identifier from the root path of this asset library and the given relative
* asset path (relative to the asset library root directory).
*/
AssetIdentifier asset_identifier_from_library(StringRef relative_asset_path);
StringRefNull root_path() const;
private:
std::optional<int> find_asset_index(const AssetRepresentation &asset);
};

View File

@ -16,21 +16,23 @@
#include "BLI_string_ref.hh"
#include "AS_asset_identifier.hh"
struct AssetMetaData;
struct ID;
namespace blender::asset_system {
class AssetRepresentation {
struct ExternalAsset {
std::string name;
std::unique_ptr<AssetMetaData> metadata_ = nullptr;
};
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;
};
union {
ExternalAsset external_asset_;
ID *local_asset_id_ = nullptr; /* Non-owning. */
@ -40,10 +42,12 @@ class AssetRepresentation {
public:
/** Constructs an asset representation for an external ID. The asset will not be editable. */
explicit AssetRepresentation(StringRef name, std::unique_ptr<AssetMetaData> metadata);
AssetRepresentation(AssetIdentifier &&identifier,
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. */
explicit AssetRepresentation(ID &id);
AssetRepresentation(AssetIdentifier &&identifier, ID &id);
AssetRepresentation(AssetRepresentation &&other);
/* Non-copyable type. */
AssetRepresentation(const AssetRepresentation &other) = delete;
@ -55,6 +59,8 @@ 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. */
@ -62,3 +68,8 @@ class AssetRepresentation {
};
} // namespace blender::asset_system
/* C-Handle */
struct AssetRepresentation;
const std::string AS_asset_representation_full_path_get(const ::AssetRepresentation *asset);

View File

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

View File

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

View File

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

View File

@ -4,9 +4,6 @@
* \ingroup asset_system
*/
#include "asset_library_service.hh"
#include "AS_asset_library.hh"
#include "BKE_blender.h"
#include "BKE_preferences.h"
@ -19,6 +16,10 @@
#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.
@ -80,41 +81,30 @@ AssetLibrary *AssetLibraryService::get_asset_library(
return nullptr;
}
namespace {
std::string normalize_directory_path(StringRefNull directory)
AssetLibrary *AssetLibraryService::get_asset_library_on_disk(StringRefNull root_path)
{
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(),
BLI_assert_msg(!root_path.is_empty(),
"top level directory must be given for on-disk asset library");
std::string top_dir_trailing_slash = normalize_directory_path(top_level_directory);
std::string normalized_root_path = utils::normalize_directory_path(root_path);
std::unique_ptr<AssetLibrary> *lib_uptr_ptr = on_disk_libraries_.lookup_ptr(
top_dir_trailing_slash);
normalized_root_path);
if (lib_uptr_ptr != nullptr) {
CLOG_INFO(&LOG, 2, "get \"%s\" (cached)", top_dir_trailing_slash.c_str());
CLOG_INFO(&LOG, 2, "get \"%s\" (cached)", normalized_root_path.c_str());
AssetLibrary *lib = lib_uptr_ptr->get();
lib->refresh();
return lib;
}
std::unique_ptr lib_uptr = std::make_unique<AssetLibrary>();
std::unique_ptr lib_uptr = std::make_unique<AssetLibrary>(normalized_root_path);
AssetLibrary *lib = lib_uptr.get();
lib->on_blend_save_handler_register();
lib->load_catalogs(top_dir_trailing_slash);
lib->load_catalogs();
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());
on_disk_libraries_.add_new(normalized_root_path, std::move(lib_uptr));
CLOG_INFO(&LOG, 2, "get \"%s\" (loaded)", normalized_root_path.c_str());
return lib;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -38,44 +38,53 @@ 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 << 3),
IDWALK_CB_EMBEDDED = (1 << 4),
/**
* 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 << 4),
IDWALK_CB_LOOPBACK = (1 << 5),
/** That ID is used as library override's reference by its owner. */
IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE = (1 << 5),
IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE = (1 << 6),
/** That ID pointer is not overridable. */
IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE = (1 << 6),
IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE = (1 << 7),
/**
* 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 << 7),
IDWALK_CB_INTERNAL = (1 << 8),
/**
* This ID usage is fully refcounted.
* Callback is responsible to deal accordingly with #ID.us if needed.
*/
IDWALK_CB_USER = (1 << 8),
IDWALK_CB_USER = (1 << 9),
/**
* 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 << 9),
IDWALK_CB_USER_ONE = (1 << 10),
};
enum {

View File

@ -405,8 +405,6 @@ 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 */
@ -521,8 +519,6 @@ 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.

View File

@ -46,11 +46,6 @@ 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,
@ -138,9 +133,6 @@ class bNodeSocketRuntime : NonCopyable, NonMovable {
/* Runtime-only cache of the number of input links, for multi-input sockets. */
short total_inputs = 0;
/** Cached data from execution. */
void *cache = nullptr;
/** Only valid when #topology_cache_is_dirty is false. */
Vector<bNodeLink *> directly_linked_links;
Vector<bNodeSocket *> directly_linked_sockets;

View File

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

View File

@ -876,7 +876,7 @@ static void lib_link_node_sockets(BlendLibReader *reader, Library *lib, ListBase
}
}
void ntreeBlendReadLib(struct BlendLibReader *reader, struct bNodeTree *ntree)
void ntreeBlendReadLib(BlendLibReader *reader, bNodeTree *ntree)
{
Library *lib = ntree->id.lib;
@ -1002,7 +1002,7 @@ static void ntree_blend_read_expand(BlendExpander *expander, ID *id)
namespace blender::bke {
static void node_tree_asset_pre_save(void *asset_ptr, struct AssetMetaData *asset_data)
static void node_tree_asset_pre_save(void *asset_ptr, AssetMetaData *asset_data)
{
bNodeTree &node_tree = *static_cast<bNodeTree *>(asset_ptr);
@ -1085,7 +1085,7 @@ static void node_add_sockets_from_type(bNodeTree *ntree, bNode *node, bNodeType
* The #bNodeType may not be registered at creation time of the node,
* so this can be delayed until the node type gets registered.
*/
static void node_init(const struct bContext *C, bNodeTree *ntree, bNode *node)
static void node_init(const bContext *C, bNodeTree *ntree, bNode *node)
{
bNodeType *ntype = node->typeinfo;
if (ntype == &NodeTypeUndefined) {
@ -1126,7 +1126,6 @@ static void node_init(const struct bContext *C, bNodeTree *ntree, bNode *node)
id_us_plus(node->id);
}
/* extra init callback */
if (ntype->initfunc_api) {
PointerRNA ptr;
RNA_pointer_create((ID *)ntree, &RNA_Node, node, &ptr);
@ -1154,7 +1153,7 @@ static void ntree_set_typeinfo(bNodeTree *ntree, bNodeTreeType *typeinfo)
BKE_ntree_update_tag_all(ntree);
}
static void node_set_typeinfo(const struct bContext *C,
static void node_set_typeinfo(const bContext *C,
bNodeTree *ntree,
bNode *node,
bNodeType *typeinfo)
@ -1206,7 +1205,7 @@ static void node_socket_set_typeinfo(bNodeTree *ntree,
/* Set specific typeinfo pointers in all node trees on register/unregister */
static void update_typeinfo(Main *bmain,
const struct bContext *C,
const bContext *C,
bNodeTreeType *treetype,
bNodeType *nodetype,
bNodeSocketType *socktype,
@ -1255,7 +1254,7 @@ static void update_typeinfo(Main *bmain,
FOREACH_NODETREE_END;
}
void ntreeSetTypes(const struct bContext *C, bNodeTree *ntree)
void ntreeSetTypes(const bContext *C, bNodeTree *ntree)
{
ntree_set_typeinfo(ntree, ntreeTypeFind(ntree->idname));
@ -1303,7 +1302,6 @@ void ntreeTypeAdd(bNodeTreeType *nt)
update_typeinfo(G_MAIN, nullptr, nt, nullptr, nullptr, false);
}
/* callback for hash value free function */
static void ntree_free_type(void *treetype_v)
{
bNodeTreeType *treetype = (bNodeTreeType *)treetype_v;
@ -1341,7 +1339,6 @@ bNodeType *nodeTypeFind(const char *idname)
return nullptr;
}
/* callback for hash value free function */
static void node_free_type(void *nodetype_v)
{
bNodeType *nodetype = (bNodeType *)nodetype_v;
@ -1409,7 +1406,6 @@ bNodeSocketType *nodeSocketTypeFind(const char *idname)
return nullptr;
}
/* callback for hash value free function */
static void node_free_socket_type(void *socktype_v)
{
bNodeSocketType *socktype = (bNodeSocketType *)socktype_v;
@ -1451,9 +1447,7 @@ const char *nodeSocketTypeLabel(const bNodeSocketType *stype)
return stype->label[0] != '\0' ? stype->label : RNA_struct_ui_name(stype->ext_socket.srna);
}
struct bNodeSocket *nodeFindSocket(const bNode *node,
eNodeSocketInOut in_out,
const char *identifier)
bNodeSocket *nodeFindSocket(const bNode *node, eNodeSocketInOut in_out, const char *identifier)
{
const ListBase *sockets = (in_out == SOCK_IN) ? &node->inputs : &node->outputs;
LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
@ -1491,7 +1485,6 @@ bNodeSocket *node_find_enabled_output_socket(bNode &node, StringRef name)
} // namespace blender::bke
/* find unique socket identifier */
static bool unique_identifier_check(void *arg, const char *identifier)
{
const ListBase *lb = (const ListBase *)arg;
@ -1703,7 +1696,7 @@ bNodeSocket *nodeAddSocket(bNodeTree *ntree,
return sock;
}
bool nodeIsStaticSocketType(const struct bNodeSocketType *stype)
bool nodeIsStaticSocketType(const bNodeSocketType *stype)
{
/*
* Cannot rely on type==SOCK_CUSTOM here, because type is 0 by default
@ -1943,10 +1936,7 @@ void nodeRemoveSocket(bNodeTree *ntree, bNode *node, bNodeSocket *sock)
nodeRemoveSocketEx(ntree, node, sock, true);
}
void nodeRemoveSocketEx(struct bNodeTree *ntree,
struct bNode *node,
struct bNodeSocket *sock,
bool do_id_user)
void nodeRemoveSocketEx(bNodeTree *ntree, bNode *node, bNodeSocket *sock, bool do_id_user)
{
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
if (link->fromsock == sock || link->tosock == sock) {
@ -2180,15 +2170,13 @@ bool nodeIsDanglingReroute(const bNodeTree *ntree, const bNode *node)
}
}
/* ************** Add stuff ********** */
void nodeUniqueName(bNodeTree *ntree, bNode *node)
{
BLI_uniquename(
&ntree->nodes, node, DATA_("Node"), '.', offsetof(bNode, name), sizeof(node->name));
}
bNode *nodeAddNode(const struct bContext *C, bNodeTree *ntree, const char *idname)
bNode *nodeAddNode(const bContext *C, bNodeTree *ntree, const char *idname)
{
bNode *node = MEM_cnew<bNode>("new node");
node->runtime = MEM_new<bNodeRuntime>(__func__);
@ -2206,7 +2194,7 @@ bNode *nodeAddNode(const struct bContext *C, bNodeTree *ntree, const char *idnam
return node;
}
bNode *nodeAddStaticNode(const struct bContext *C, bNodeTree *ntree, int type)
bNode *nodeAddStaticNode(const bContext *C, bNodeTree *ntree, int type)
{
const char *idname = nullptr;
@ -2246,9 +2234,6 @@ static void node_socket_copy(bNodeSocket *sock_dst, const bNodeSocket *sock_src,
MEM_dupallocN(sock_src->default_attribute_name));
sock_dst->stack_index = 0;
/* XXX some compositor nodes (e.g. image, render layers) still store
* some persistent buffer data here, need to clear this to avoid dangling pointers. */
sock_dst->runtime->cache = nullptr;
}
namespace blender::bke {
@ -2354,12 +2339,10 @@ static int node_count_links(const bNodeTree *ntree, const bNodeSocket *socket)
bNodeLink *nodeAddLink(
bNodeTree *ntree, bNode *fromnode, bNodeSocket *fromsock, bNode *tonode, bNodeSocket *tosock)
{
bNodeLink *link = nullptr;
/* Test valid input. */
BLI_assert(fromnode);
BLI_assert(tonode);
bNodeLink *link = nullptr;
if (fromsock->in_out == SOCK_OUT && tosock->in_out == SOCK_IN) {
link = MEM_cnew<bNodeLink>("link");
if (ntree) {
@ -2888,8 +2871,6 @@ void BKE_node_preview_merge_tree(bNodeTree *to_ntree, bNodeTree *from_ntree, boo
}
}
/* ************** Free stuff ********** */
void nodeUnlinkNode(bNodeTree *ntree, bNode *node)
{
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
@ -2939,10 +2920,6 @@ static void node_free_node(bNodeTree *ntree, bNode *node)
if (ntree) {
BLI_remlink(&ntree->nodes, node);
if (ntree->typeinfo->free_node_cache) {
ntree->typeinfo->free_node_cache(ntree, node);
}
/* texture node has bad habit of keeping exec data around */
if (ntree->type == NTREE_TEXTURE && ntree->runtime->execdata) {
ntreeTexEndExecTree(ntree->runtime->execdata);
@ -3123,17 +3100,6 @@ void ntreeFreeLocalTree(bNodeTree *ntree)
}
}
void ntreeFreeCache(bNodeTree *ntree)
{
if (ntree == nullptr) {
return;
}
if (ntree->typeinfo->free_cache) {
ntree->typeinfo->free_cache(ntree);
}
}
void ntreeSetOutput(bNodeTree *ntree)
{
/* find the active outputs, might become tree type dependent handler */
@ -3375,19 +3341,19 @@ bNodeSocket *ntreeInsertSocketInterface(bNodeTree *ntree,
return iosock;
}
struct bNodeSocket *ntreeAddSocketInterfaceFromSocket(bNodeTree *ntree,
bNode *from_node,
bNodeSocket *from_sock)
bNodeSocket *ntreeAddSocketInterfaceFromSocket(bNodeTree *ntree,
bNode *from_node,
bNodeSocket *from_sock)
{
return ntreeAddSocketInterfaceFromSocketWithName(
ntree, from_node, from_sock, from_sock->idname, from_sock->name);
}
struct bNodeSocket *ntreeAddSocketInterfaceFromSocketWithName(bNodeTree *ntree,
bNode *from_node,
bNodeSocket *from_sock,
const char *idname,
const char *name)
bNodeSocket *ntreeAddSocketInterfaceFromSocketWithName(bNodeTree *ntree,
bNode *from_node,
bNodeSocket *from_sock,
const char *idname,
const char *name)
{
bNodeSocket *iosock = ntreeAddSocketInterface(
ntree, static_cast<eNodeSocketInOut>(from_sock->in_out), idname, DATA_(name));
@ -3399,10 +3365,10 @@ struct bNodeSocket *ntreeAddSocketInterfaceFromSocketWithName(bNodeTree *ntree,
return iosock;
}
struct bNodeSocket *ntreeInsertSocketInterfaceFromSocket(bNodeTree *ntree,
bNodeSocket *next_sock,
bNode *from_node,
bNodeSocket *from_sock)
bNodeSocket *ntreeInsertSocketInterfaceFromSocket(bNodeTree *ntree,
bNodeSocket *next_sock,
bNode *from_node,
bNodeSocket *from_sock)
{
bNodeSocket *iosock = ntreeInsertSocketInterface(
ntree,
@ -4028,7 +3994,6 @@ static bool node_poll_default(bNodeType * /*ntype*/,
return true;
}
/* use the basic poll function */
static bool node_poll_instance_default(bNode *node, bNodeTree *ntree, const char **disabled_hint)
{
return node->typeinfo->poll(node->typeinfo, ntree, disabled_hint);
@ -4115,9 +4080,9 @@ static void unique_socket_template_identifier(bNodeSocketTemplate *list,
sizeof(ntemp->identifier));
}
void node_type_socket_templates(struct bNodeType *ntype,
struct bNodeSocketTemplate *inputs,
struct bNodeSocketTemplate *outputs)
void node_type_socket_templates(bNodeType *ntype,
bNodeSocketTemplate *inputs,
bNodeSocketTemplate *outputs)
{
ntype->inputs = inputs;
ntype->outputs = outputs;
@ -4147,7 +4112,7 @@ void node_type_socket_templates(struct bNodeType *ntype,
}
}
void node_type_size(struct bNodeType *ntype, int width, int minwidth, int maxwidth)
void node_type_size(bNodeType *ntype, int width, int minwidth, int maxwidth)
{
ntype->width = width;
ntype->minwidth = minwidth;
@ -4159,7 +4124,7 @@ void node_type_size(struct bNodeType *ntype, int width, int minwidth, int maxwid
}
}
void node_type_size_preset(struct bNodeType *ntype, eNodeSizePreset size)
void node_type_size_preset(bNodeType *ntype, eNodeSizePreset size)
{
switch (size) {
case NODE_SIZE_DEFAULT:
@ -4179,10 +4144,10 @@ void node_type_size_preset(struct bNodeType *ntype, eNodeSizePreset size)
void node_type_storage(bNodeType *ntype,
const char *storagename,
void (*freefunc)(struct bNode *node),
void (*copyfunc)(struct bNodeTree *dest_ntree,
struct bNode *dest_node,
const struct bNode *src_node))
void (*freefunc)(bNode *node),
void (*copyfunc)(bNodeTree *dest_ntree,
bNode *dest_node,
const bNode *src_node))
{
if (storagename) {
BLI_strncpy(ntype->storagename, storagename, sizeof(ntype->storagename));
@ -4248,7 +4213,7 @@ void BKE_node_system_exit()
/* -------------------------------------------------------------------- */
/* NodeTree Iterator Helpers (FOREACH_NODETREE_BEGIN) */
void BKE_node_tree_iter_init(struct NodeTreeIterStore *ntreeiter, struct Main *bmain)
void BKE_node_tree_iter_init(NodeTreeIterStore *ntreeiter, Main *bmain)
{
ntreeiter->ngroup = (bNodeTree *)bmain->nodetrees.first;
ntreeiter->scene = (Scene *)bmain->scenes.first;
@ -4259,9 +4224,7 @@ void BKE_node_tree_iter_init(struct NodeTreeIterStore *ntreeiter, struct Main *b
ntreeiter->linestyle = (FreestyleLineStyle *)bmain->linestyles.first;
ntreeiter->simulation = (Simulation *)bmain->simulations.first;
}
bool BKE_node_tree_iter_step(struct NodeTreeIterStore *ntreeiter,
bNodeTree **r_nodetree,
struct ID **r_id)
bool BKE_node_tree_iter_step(NodeTreeIterStore *ntreeiter, bNodeTree **r_nodetree, ID **r_id)
{
if (ntreeiter->ngroup) {
*r_nodetree = (bNodeTree *)ntreeiter->ngroup;
@ -4310,9 +4273,6 @@ bool BKE_node_tree_iter_step(struct NodeTreeIterStore *ntreeiter,
return true;
}
/* -------------------------------------------------------------------- */
/* NodeTree kernel functions */
void BKE_nodetree_remove_layer_n(bNodeTree *ntree, Scene *scene, const int layer_index)
{
BLI_assert(layer_index != -1);

View File

@ -39,6 +39,11 @@
#define LEAF_LIMIT 10000
/* Uncomment to test if triangles of the same face are
* properly clustered into single nodes.
*/
//#define TEST_PBVH_FACE_SPLIT
/* Uncomment to test that faces are only assigned to one PBVHNode */
//#define VALIDATE_UNIQUE_NODE_FACES
@ -164,24 +169,65 @@ static bool grid_materials_match(const DMFlagMat *f1, const DMFlagMat *f2)
/* Adapted from BLI_kdopbvh.c */
/* Returns the index of the first element on the right of the partition */
static int partition_indices(int *prim_indices, int lo, int hi, int axis, float mid, BBC *prim_bbc)
static int partition_indices_faces(int *prim_indices,
int *prim_scratch,
int lo,
int hi,
int axis,
float mid,
BBC *prim_bbc,
const MLoopTri *looptri,
const MPoly *mpoly)
{
int i = lo, j = hi;
for (;;) {
for (; prim_bbc[prim_indices[i]].bcentroid[axis] < mid; i++) {
/* pass */
}
for (; mid < prim_bbc[prim_indices[j]].bcentroid[axis]; j--) {
/* pass */
}
if (!(i < j)) {
return i;
}
SWAP(int, prim_indices[i], prim_indices[j]);
i++;
for (int i = lo; i < hi; i++) {
prim_scratch[i - lo] = prim_indices[i];
}
int lo2 = lo, hi2 = hi - 1;
int i1 = lo, i2 = 0;
while (i1 < hi) {
int poly = looptri[prim_scratch[i2]].poly;
bool side = prim_bbc[prim_scratch[i2]].bcentroid[axis] >= mid;
while (i1 < hi && looptri[prim_scratch[i2]].poly == poly) {
prim_indices[side ? hi2-- : lo2++] = prim_scratch[i2];
i1++;
i2++;
}
}
return lo2;
}
static int partition_indices_grids(int *prim_indices,
int *prim_scratch,
int lo,
int hi,
int axis,
float mid,
BBC *prim_bbc,
SubdivCCG *subdiv_ccg)
{
for (int i = lo; i < hi; i++) {
prim_scratch[i - lo] = prim_indices[i];
}
int lo2 = lo, hi2 = hi - 1;
int i1 = lo, i2 = 0;
while (i1 < hi) {
int poly = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, prim_scratch[i2]);
bool side = prim_bbc[prim_scratch[i2]].bcentroid[axis] >= mid;
while (i1 < hi && BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, prim_scratch[i2]) == poly) {
prim_indices[side ? hi2-- : lo2++] = prim_scratch[i2];
i1++;
i2++;
}
}
return lo2;
}
/* Returns the index of the first element on the right of the partition */
@ -424,55 +470,53 @@ static bool leaf_needs_material_split(PBVH *pbvh, int offset, int count)
return false;
}
static int adjust_partition_faces(PBVH *pbvh, int offset, int mid, int count)
#ifdef TEST_PBVH_FACE_SPLIT
static void test_face_boundaries(PBVH *pbvh)
{
int poly = pbvh->looptri[pbvh->prim_indices[mid]].poly;
/* Scan backwards. */
while (mid > offset + 2) { /* First node should have at least 1 primitive */
if (pbvh->looptri[pbvh->prim_indices[mid - 1]].poly != poly) {
return mid;
}
mid--;
int faces_num = BKE_pbvh_num_faces(pbvh);
int *node_map = MEM_calloc_arrayN(faces_num, sizeof(int), __func__);
for (int i = 0; i < faces_num; i++) {
node_map[i] = -1;
}
/* If that didn't work try scanning forward. */
while (mid < pbvh->totprim + count) {
if (pbvh->looptri[pbvh->prim_indices[mid]].poly != poly) {
break;
for (int i = 0; i < pbvh->totnode; i++) {
PBVHNode *node = pbvh->nodes + i;
if (!(node->flag & PBVH_Leaf)) {
continue;
}
mid++;
switch (BKE_pbvh_type(pbvh)) {
case PBVH_FACES: {
for (int j = 0; j < node->totprim; j++) {
int poly = pbvh->looptri[node->prim_indices[j]].poly;
if (node_map[poly] >= 0 && node_map[poly] != i) {
int old_i = node_map[poly];
int prim_i = node->prim_indices - pbvh->prim_indices + j;
printf("PBVH split error; poly: %d, prim_i: %d, node1: %d, node2: %d, totprim: %d\n",
poly,
prim_i,
old_i,
i,
node->totprim);
}
node_map[poly] = i;
}
break;
}
case PBVH_GRIDS:
break;
case PBVH_BMESH:
break;
}
}
return mid;
}
static int adjust_partition_grids(PBVH *pbvh, int offset, int mid, int count)
{
int poly = BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, pbvh->prim_indices[mid]);
/* Scan backwards. */
while (mid > offset + 2) { /* First node should have at least 1 primitive */
if (BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, pbvh->prim_indices[mid - 1]) != poly) {
return mid;
}
mid--;
}
/* If that didn't work try scanning forward. */
while (mid < pbvh->totprim + count) {
if (BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, pbvh->prim_indices[mid]) != poly) {
break;
}
mid++;
}
return mid;
MEM_SAFE_FREE(node_map);
}
#endif
/* Recursively build a node in the tree
*
@ -485,16 +529,32 @@ static int adjust_partition_grids(PBVH *pbvh, int offset, int mid, int count)
* offset and start indicate a range in the array of primitive indices
*/
static void build_sub(PBVH *pbvh, int node_index, BB *cb, BBC *prim_bbc, int offset, int count)
static void build_sub(PBVH *pbvh,
int node_index,
BB *cb,
BBC *prim_bbc,
int offset,
int count,
int *prim_scratch,
int depth)
{
int end;
BB cb_backing;
if (!prim_scratch) {
prim_scratch = MEM_malloc_arrayN(pbvh->totprim, sizeof(int), __func__);
}
/* Decide whether this is a leaf or not */
const bool below_leaf_limit = count <= pbvh->leaf_limit;
const bool below_leaf_limit = count <= pbvh->leaf_limit || depth == STACK_FIXED_DEPTH - 1;
if (below_leaf_limit) {
if (!leaf_needs_material_split(pbvh, offset, count)) {
build_leaf(pbvh, node_index, prim_bbc, offset, count);
if (node_index == 0) {
MEM_SAFE_FREE(prim_scratch);
}
return;
}
}
@ -518,33 +578,54 @@ static void build_sub(PBVH *pbvh, int node_index, BB *cb, BBC *prim_bbc, int off
const int axis = BB_widest_axis(cb);
/* Partition primitives along that axis */
end = partition_indices(pbvh->prim_indices,
offset,
offset + count - 1,
axis,
(cb->bmax[axis] + cb->bmin[axis]) * 0.5f,
prim_bbc);
if (pbvh->header.type == PBVH_FACES) {
end = partition_indices_faces(pbvh->prim_indices,
prim_scratch,
offset,
offset + count,
axis,
(cb->bmax[axis] + cb->bmin[axis]) * 0.5f,
prim_bbc,
pbvh->looptri,
pbvh->mpoly);
}
else {
end = partition_indices_grids(pbvh->prim_indices,
prim_scratch,
offset,
offset + count,
axis,
(cb->bmax[axis] + cb->bmin[axis]) * 0.5f,
prim_bbc,
pbvh->subdiv_ccg);
}
}
else {
/* Partition primitives by material */
end = partition_indices_material(pbvh, offset, offset + count - 1);
}
if (pbvh->header.type == PBVH_FACES) {
end = adjust_partition_faces(pbvh, offset, end, count);
}
else {
end = adjust_partition_grids(pbvh, offset, end, count);
}
/* Build children */
build_sub(pbvh, pbvh->nodes[node_index].children_offset, NULL, prim_bbc, offset, end - offset);
build_sub(pbvh,
pbvh->nodes[node_index].children_offset,
NULL,
prim_bbc,
offset,
end - offset,
prim_scratch,
depth + 1);
build_sub(pbvh,
pbvh->nodes[node_index].children_offset + 1,
NULL,
prim_bbc,
end,
offset + count - end);
offset + count - end,
prim_scratch,
depth + 1);
if (node_index == 0) {
MEM_SAFE_FREE(prim_scratch);
}
}
static void pbvh_build(PBVH *pbvh, BB *cb, BBC *prim_bbc, int totprim)
@ -569,7 +650,7 @@ static void pbvh_build(PBVH *pbvh, BB *cb, BBC *prim_bbc, int totprim)
}
pbvh->totnode = 1;
build_sub(pbvh, 0, cb, prim_bbc, 0, totprim);
build_sub(pbvh, 0, cb, prim_bbc, 0, totprim, NULL, 0);
}
static void pbvh_draw_args_init(PBVH *pbvh, PBVH_GPU_Args *args, PBVHNode *node)
@ -742,7 +823,16 @@ void BKE_pbvh_build_mesh(PBVH *pbvh,
pbvh->hide_vert = (bool *)CustomData_get_layer_named(&mesh->vdata, CD_PROP_BOOL, ".hide_vert");
pbvh->vert_bitmap = MEM_calloc_arrayN(totvert, sizeof(bool), "bvh->vert_bitmap");
pbvh->totvert = totvert;
#ifdef TEST_PBVH_FACE_SPLIT
/* Use lower limit to increase probability of
* edge cases.
*/
pbvh->leaf_limit = 100;
#else
pbvh->leaf_limit = LEAF_LIMIT;
#endif
pbvh->vdata = vdata;
pbvh->ldata = ldata;
pbvh->pdata = pdata;
@ -772,34 +862,12 @@ void BKE_pbvh_build_mesh(PBVH *pbvh,
BB_expand(&cb, bbc->bcentroid);
}
/* Ensure all primitives belonging to the same base face
* have the same bounds. This is needed to prevent them
* from being swapped away from each other inside the partition
* array.
*/
for (int i = 0; i < looptri_num; i++) {
const MLoopTri *lt = &looptri[i];
int poly = lt->poly;
BBC *bbc = prim_bbc + i;
int j = i + 1;
while (j < looptri_num && looptri[j].poly == poly) {
BBC *bbc2 = prim_bbc + j;
BB_expand((BB *)bbc, bbc2->bmin);
BB_expand((BB *)bbc, bbc2->bmax);
j++;
}
j = i + 1;
while (j < looptri_num && looptri[j].poly == poly) {
prim_bbc[j] = prim_bbc[i];
j++;
}
}
if (looptri_num) {
pbvh_build(pbvh, &cb, prim_bbc, looptri_num);
#ifdef TEST_PBVH_FACE_SPLIT
test_face_boundaries(pbvh);
#endif
}
MEM_freeN(prim_bbc);
@ -882,34 +950,12 @@ void BKE_pbvh_build_grids(PBVH *pbvh,
BB_expand(&cb, bbc->bcentroid);
}
/* Ensure all primitives belonging to the same base face
* have the same bounds. This is needed to prevent them
* from being swapped away from each other inside the partition
* array.
*/
for (int i = 0; i < totgrid; i++) {
int poly = BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, i);
BBC *bbc = prim_bbc + i;
int j = i + 1;
while (j < totgrid && BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, j) == poly) {
BBC *bbc2 = prim_bbc + j;
BB_expand((BB *)bbc, bbc2->bmin);
BB_expand((BB *)bbc, bbc2->bmax);
j++;
}
j = i + 1;
while (j < totgrid && BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, j) == poly) {
prim_bbc[j] = prim_bbc[i];
j++;
}
}
if (totgrid) {
pbvh_build(pbvh, &cb, prim_bbc, totgrid);
#ifdef TEST_PBVH_FACE_SPLIT
test_face_boundaries(pbvh);
#endif
}
MEM_freeN(prim_bbc);

View File

@ -761,7 +761,8 @@ static void scene_foreach_layer_collection(LibraryForeachIDData *data, ListBase
(lc->collection->id.flag & LIB_EMBEDDED_DATA) != 0) ?
IDWALK_CB_EMBEDDED :
IDWALK_CB_NOP;
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, lc->collection, cb_flag);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(
data, lc->collection, cb_flag | IDWALK_CB_DIRECT_WEAK_LINK);
scene_foreach_layer_collection(data, &lc->layer_collections);
}
}
@ -834,8 +835,11 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data)
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, view_layer->mat_override, IDWALK_CB_USER);
BKE_view_layer_synced_ensure(scene, view_layer);
LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
BKE_LIB_FOREACHID_PROCESS_IDSUPER(
data, base->object, IDWALK_CB_NOP | IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data,
base->object,
IDWALK_CB_NOP |
IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE |
IDWALK_CB_DIRECT_WEAK_LINK);
}
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(

View File

@ -275,7 +275,7 @@ static void tracking_object_copy(MovieTrackingObject *tracking_object_dst,
}
if (tracking_object_src->active_plane_track) {
tracking_object_dst->active_plane_track = BLI_ghash_lookup(
ctx.old_to_new_track_map, tracking_object_src->active_plane_track);
ctx.old_to_new_plane_track_map, tracking_object_src->active_plane_track);
BLI_assert(tracking_object_dst->active_plane_track != NULL);
}

View File

@ -33,14 +33,14 @@
*
* There are three main ways to provide a hash table implementation with a custom hash function.
*
* - When you want to provide a default hash function for your own custom type: Add a `hash`
* - When you want to provide a default hash function for your own custom type: Add a `hash()`
* member function to it. The function should return `uint64_t` and take no arguments. This
* method will be called by the default implementation of #DefaultHash. It will automatically be
* used by hash table implementations.
*
* - When you want to provide a default hash function for a type that you cannot modify: Add a new
* specialization to the #DefaultHash struct. This can be done by writing code like below in
* either global or BLI namespace.
* either global or `blender` namespace.
*
* template<> struct blender::DefaultHash<TheType> {
* uint64_t operator()(const TheType &value) const {

View File

@ -102,6 +102,41 @@ template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T fract(cons
return a - std::floor(a);
}
template<typename T> inline T cos(const T &a)
{
return std::cos(a);
}
template<typename T> inline T sin(const T &a)
{
return std::sin(a);
}
template<typename T> inline T tan(const T &a)
{
return std::tan(a);
}
template<typename T> inline T acos(const T &a)
{
return std::acos(a);
}
template<typename T> inline T asin(const T &a)
{
return std::asin(a);
}
template<typename T> inline T atan(const T &a)
{
return std::atan(a);
}
template<typename T> inline T atan2(const T &a, const T &b)
{
return std::atan2(a, b);
}
template<typename T,
typename FactorT,
BLI_ENABLE_IF((std::is_arithmetic_v<T>)),

View File

@ -102,6 +102,7 @@
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_lib_override.h"
#include "BKE_lib_query.h"
#include "BKE_main.h"
#include "BKE_node.h"
#include "BKE_packedFile.h"
@ -1083,6 +1084,31 @@ static void write_thumb(WriteData *wd, const BlendThumbnail *thumb)
/** \name File Writing (Private)
* \{ */
/* Helper callback for checking linked IDs used by given ID (assumed local), to ensure directly
* linked data is tagged accordingly. */
static int write_id_direct_linked_data_process_cb(LibraryIDLinkCallbackData *cb_data)
{
ID *id_self = cb_data->id_self;
ID *id = *cb_data->id_pointer;
const int cb_flag = cb_data->cb_flag;
if (id == nullptr || !ID_IS_LINKED(id)) {
return IDWALK_RET_NOP;
}
BLI_assert(!ID_IS_LINKED(id_self));
BLI_assert((cb_flag & IDWALK_CB_INDIRECT_USAGE) == 0);
UNUSED_VARS_NDEBUG(id_self);
if (cb_flag & IDWALK_CB_DIRECT_WEAK_LINK) {
id_lib_indirect_weak_link(id);
}
else {
id_lib_extern(id);
}
return IDWALK_RET_NOP;
}
/* if MemFile * there's filesave to memory */
static bool write_file_handle(Main *mainvar,
WriteWrap *ww,
@ -1097,11 +1123,24 @@ static bool write_file_handle(Main *mainvar,
char buf[16];
WriteData *wd;
blo_split_main(&mainlist, mainvar);
wd = mywrite_begin(ww, compare, current);
BlendWriter writer = {wd};
/* Clear 'directly linked' flag for all linked data, these are not necessarily valid/up-to-date
* info, they will be re-generated while write code is processing local IDs below. */
if (!wd->use_memfile) {
ID *id_iter;
FOREACH_MAIN_ID_BEGIN (mainvar, id_iter) {
if (ID_IS_LINKED(id_iter)) {
id_iter->tag |= LIB_TAG_INDIRECT;
id_iter->tag &= ~LIB_TAG_EXTERN;
}
}
FOREACH_MAIN_ID_END;
}
blo_split_main(&mainlist, mainvar);
BLI_snprintf(buf,
sizeof(buf),
"BLENDER%c%c%.3d",
@ -1169,6 +1208,12 @@ static bool write_file_handle(Main *mainvar,
const bool do_override = !ELEM(override_storage, nullptr, bmain) &&
ID_IS_OVERRIDE_LIBRARY_REAL(id);
/* If not writing undo data, properly set directly linked IDs as `LIB_TAG_EXTERN`. */
if (!wd->use_memfile) {
BKE_library_foreach_ID_link(
bmain, id, write_id_direct_linked_data_process_cb, nullptr, IDWALK_READONLY);
}
if (do_override) {
BKE_lib_override_library_operations_store_start(bmain, override_storage, id);
}

View File

@ -1408,6 +1408,7 @@ static void drw_subdiv_compute_dispatch(const DRWSubdivCache *cache,
}
void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache,
GPUVertBuf *flags_buffer,
GPUVertBuf *pos_nor,
GPUVertBuf *orco)
{
@ -1460,6 +1461,10 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache,
GPU_vertbuf_bind_as_ssbo(patch_arrays_buffer, binding_point++);
GPU_vertbuf_bind_as_ssbo(patch_index_buffer, binding_point++);
GPU_vertbuf_bind_as_ssbo(patch_param_buffer, binding_point++);
if (flags_buffer) {
GPU_vertbuf_bind_as_ssbo(flags_buffer, binding_point);
}
binding_point++;
GPU_vertbuf_bind_as_ssbo(pos_nor, binding_point++);
if (orco) {
GPU_vertbuf_bind_as_ssbo(src_extra_buffer, binding_point++);

View File

@ -242,6 +242,7 @@ void draw_subdiv_finalize_custom_normals(const DRWSubdivCache *cache,
GPUVertBuf *pos_nor);
void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache,
GPUVertBuf *flags_buffer,
struct GPUVertBuf *pos_nor,
struct GPUVertBuf *orco);

View File

@ -236,7 +236,7 @@ static void extract_edituv_stretch_angle_init_subdiv(const DRWSubdivCache *subdi
draw_subdiv_get_pos_nor_format(),
subdiv_cache->num_subdiv_loops + loose_geom.loop_len);
draw_subdiv_extract_pos_nor(subdiv_cache, pos_nor, nullptr);
draw_subdiv_extract_pos_nor(subdiv_cache, nullptr, pos_nor, nullptr);
}
/* UVs are stored contiguously so we need to compute the offset in the UVs buffer for the active

View File

@ -200,8 +200,26 @@ static GPUVertFormat *get_custom_normals_format()
return &format;
}
static void extract_vertex_flags(const MeshRenderData *mr, char *flags)
{
for (int i = 0; i < mr->vert_len; i++) {
char *flag = &flags[i];
const bool vert_hidden = mr->hide_vert && mr->hide_vert[i];
/* Flag for paint mode overlay. */
if (vert_hidden || ((mr->v_origindex) && (mr->v_origindex[i] == ORIGINDEX_NONE))) {
*flag = -1;
}
else if (mr->select_vert && mr->select_vert[i]) {
*flag = 1;
}
else {
*flag = 0;
}
}
}
static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache,
const MeshRenderData * /*mr*/,
const MeshRenderData *mr,
MeshBatchCache *cache,
void *buffer,
void * /*data*/)
@ -217,6 +235,17 @@ static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache,
return;
}
GPUVertBuf *flags_buffer = GPU_vertbuf_calloc();
static GPUVertFormat flag_format = {0};
if (flag_format.attr_len == 0) {
GPU_vertformat_attr_add(&flag_format, "flag", GPU_COMP_I32, 1, GPU_FETCH_INT);
}
GPU_vertbuf_init_with_format(flags_buffer, &flag_format);
GPU_vertbuf_data_alloc(flags_buffer, divide_ceil_u(mr->vert_len, 4));
char *flags = static_cast<char *>(GPU_vertbuf_get_data(flags_buffer));
extract_vertex_flags(mr, flags);
GPU_vertbuf_tag_dirty(flags_buffer);
GPUVertBuf *orco_vbo = cache->final.buff.vbo.orco;
if (orco_vbo) {
@ -231,7 +260,7 @@ static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache,
GPU_vertbuf_init_build_on_device(orco_vbo, &format, subdiv_cache->num_subdiv_loops);
}
draw_subdiv_extract_pos_nor(subdiv_cache, vbo, orco_vbo);
draw_subdiv_extract_pos_nor(subdiv_cache, flags_buffer, vbo, orco_vbo);
if (subdiv_cache->use_custom_loop_normals) {
Mesh *coarse_mesh = subdiv_cache->mesh;
@ -277,6 +306,8 @@ static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache,
GPU_vertbuf_discard(vertex_normals);
GPU_vertbuf_discard(subdiv_loop_subdiv_vert_index);
}
GPU_vertbuf_discard(flags_buffer);
}
static void extract_pos_nor_loose_geom_subdiv(const DRWSubdivCache *subdiv_cache,

View File

@ -87,16 +87,30 @@ layout(std430, binding = 11) readonly buffer extraCoarseFaceData
uint extra_coarse_face_data[];
};
#else
layout(std430, binding = 8) writeonly buffer outputVertexData
layout(std430, binding = 8) readonly buffer inputFlagsBuffer
{
int flags_buffer[]; /*char*/
};
float get_flag(int vertex)
{
int char4 = flags_buffer[vertex / 4];
int flag = (char4 >> ((vertex % 4) * 8)) & 0xFF;
if (flag >= 128) {
flag = -128 + (flag - 128);
}
return float(flag);
}
layout(std430, binding = 9) writeonly buffer outputVertexData
{
PosNorLoop output_verts[];
};
# if defined(ORCO_EVALUATION)
layout(std430, binding = 9) buffer src_extra_buffer
layout(std430, binding = 10) buffer src_extra_buffer
{
float srcExtraVertexBuffer[];
};
layout(std430, binding = 10) writeonly buffer outputOrcoData
layout(std430, binding = 11) writeonly buffer outputOrcoData
{
vec4 output_orcos[];
};
@ -463,6 +477,9 @@ void main()
if (origindex == -1) {
flag = -1.0;
}
else {
flag = get_flag(origindex);
}
PosNorLoop vertex_data;
set_vertex_pos(vertex_data, pos);

View File

@ -249,16 +249,15 @@ static void poselib_tempload_exit(PoseBlendData *pbd)
static bAction *poselib_blend_init_get_action(bContext *C, wmOperator *op)
{
bool asset_handle_valid;
const AssetLibraryReference *asset_library_ref = CTX_wm_asset_library_ref(C);
const AssetHandle asset_handle = CTX_wm_asset_handle(C, &asset_handle_valid);
/* Poll callback should check. */
BLI_assert((asset_library_ref != NULL) && asset_handle_valid);
BLI_assert(asset_handle_valid);
PoseBlendData *pbd = op->customdata;
pbd->temp_id_consumer = ED_asset_temp_id_consumer_create(&asset_handle);
return (bAction *)ED_asset_temp_id_consumer_ensure_local_id(
pbd->temp_id_consumer, C, asset_library_ref, ID_AC, CTX_data_main(C), op->reports);
pbd->temp_id_consumer, ID_AC, CTX_data_main(C), op->reports);
}
static bAction *flip_pose(bContext *C, Object *ob, bAction *action)
@ -508,11 +507,9 @@ static bool poselib_asset_in_context(bContext *C)
{
bool asset_handle_valid;
/* Check whether the context provides the asset data needed to add a pose. */
const AssetLibraryReference *asset_library_ref = CTX_wm_asset_library_ref(C);
AssetHandle asset_handle = CTX_wm_asset_handle(C, &asset_handle_valid);
const AssetHandle asset_handle = CTX_wm_asset_handle(C, &asset_handle_valid);
return (asset_library_ref != NULL) && asset_handle_valid &&
(ED_asset_handle_get_id_type(&asset_handle) == ID_AC);
return asset_handle_valid && (ED_asset_handle_get_id_type(&asset_handle) == ID_AC);
}
/* Poll callback for operators that require existing PoseLib data (with poses) to work. */

View File

@ -27,9 +27,7 @@ struct AssetMetaData *ED_asset_handle_get_metadata(const struct AssetHandle *ass
struct ID *ED_asset_handle_get_local_id(const struct AssetHandle *asset);
ID_Type ED_asset_handle_get_id_type(const struct AssetHandle *asset);
int ED_asset_handle_get_preview_icon_id(const struct AssetHandle *asset);
void ED_asset_handle_get_full_library_path(const struct bContext *C,
const struct AssetLibraryReference *asset_library_ref,
const struct AssetHandle *asset,
void ED_asset_handle_get_full_library_path(const struct AssetHandle *asset,
char r_full_lib_path[]);
#ifdef __cplusplus
@ -41,9 +39,7 @@ void ED_asset_handle_get_full_library_path(const struct bContext *C,
namespace blender::ed::asset {
/** If the ID already exists in the database, return it, otherwise add it. */
ID *get_local_id_from_asset_or_append_and_reuse(Main &bmain,
const AssetLibraryReference &library_ref,
AssetHandle asset);
ID *get_local_id_from_asset_or_append_and_reuse(Main &bmain, AssetHandle asset);
} // namespace blender::ed::asset

View File

@ -51,7 +51,6 @@ void ED_assetlist_storage_id_remap(struct ID *id_old, struct ID *id_new);
void ED_assetlist_storage_exit(void);
struct ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle);
const char *ED_assetlist_library_path(const struct AssetLibraryReference *library_reference);
/**
* \return True if the region needs a UI redraw.

View File

@ -15,10 +15,6 @@ struct AssetLibraryReference;
struct FileDirEntry;
struct bContext;
std::string ED_assetlist_asset_filepath_get(const bContext *C,
const AssetLibraryReference &library_reference,
const AssetHandle &asset_handle);
/* Can return false to stop iterating. */
using AssetListIterFn = blender::FunctionRef<bool(AssetHandle)>;
void ED_assetlist_iterate(const AssetLibraryReference &library_reference, AssetListIterFn fn);

View File

@ -27,13 +27,10 @@ struct bContext;
AssetTempIDConsumer *ED_asset_temp_id_consumer_create(const struct AssetHandle *handle);
void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer);
struct ID *ED_asset_temp_id_consumer_ensure_local_id(
AssetTempIDConsumer *consumer,
const struct bContext *C,
const struct AssetLibraryReference *asset_library_ref,
ID_Type id_type,
struct Main *bmain,
struct ReportList *reports);
struct ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer,
ID_Type id_type,
struct Main *bmain,
struct ReportList *reports);
#ifdef __cplusplus
}

View File

@ -6,7 +6,9 @@
#include <string>
#include "AS_asset_identifier.hh"
#include "AS_asset_representation.h"
#include "AS_asset_representation.hh"
#include "DNA_space_types.h"
@ -42,14 +44,12 @@ int ED_asset_handle_get_preview_icon_id(const AssetHandle *asset)
return asset->file_data->preview_icon_id;
}
void ED_asset_handle_get_full_library_path(const bContext *C,
const AssetLibraryReference *asset_library_ref,
const AssetHandle *asset,
void ED_asset_handle_get_full_library_path(const AssetHandle *asset_handle,
char r_full_lib_path[FILE_MAX_LIBEXTRA])
{
*r_full_lib_path = '\0';
std::string asset_path = ED_assetlist_asset_filepath_get(C, *asset_library_ref, *asset);
std::string asset_path = AS_asset_representation_full_path_get(asset_handle->file_data->asset);
if (asset_path.empty()) {
return;
}
@ -59,16 +59,14 @@ void ED_asset_handle_get_full_library_path(const bContext *C,
namespace blender::ed::asset {
ID *get_local_id_from_asset_or_append_and_reuse(Main &bmain,
const AssetLibraryReference &library_ref,
const AssetHandle asset)
ID *get_local_id_from_asset_or_append_and_reuse(Main &bmain, const AssetHandle asset)
{
if (ID *local_id = ED_asset_handle_get_local_id(&asset)) {
return local_id;
}
char blend_path[FILE_MAX_LIBEXTRA];
ED_asset_handle_get_full_library_path(nullptr, &library_ref, &asset, blend_path);
ED_asset_handle_get_full_library_path(&asset, blend_path);
const char *id_name = ED_asset_handle_get_name(&asset);
return WM_file_append_datablock(&bmain,

View File

@ -22,8 +22,6 @@
#include "BKE_preferences.h"
#include "ED_fileselect.h"
#include "WM_api.h"
/* XXX uses private header of file-space. */
@ -119,7 +117,6 @@ class AssetList : NonCopyable {
int size() const;
void tagMainDataDirty() const;
void remapID(ID *id_old, ID *id_new) const;
StringRef filepath() const;
};
AssetList::AssetList(eFileSelectType filesel_type, const AssetLibraryReference &asset_library_ref)
@ -296,11 +293,6 @@ void AssetList::remapID(ID * /*id_old*/, ID * /*id_new*/) const
tagMainDataDirty();
}
StringRef AssetList::filepath() const
{
return filelist_dir(filelist_);
}
/** \} */
/* -------------------------------------------------------------------- */
@ -465,46 +457,6 @@ void ED_assetlist_iterate(const AssetLibraryReference &library_reference, AssetL
}
}
/* TODO hack to use the File Browser path, so we can keep all the import logic handled by the asset
* API. Get rid of this once the File Browser is integrated better with the asset list. */
static const char *assetlist_library_path_from_sfile_get_hack(const bContext *C)
{
SpaceFile *sfile = CTX_wm_space_file(C);
if (!sfile || !ED_fileselect_is_asset_browser(sfile)) {
return nullptr;
}
FileAssetSelectParams *asset_select_params = ED_fileselect_get_asset_params(sfile);
if (!asset_select_params) {
return nullptr;
}
return filelist_dir(sfile->files);
}
std::string ED_assetlist_asset_filepath_get(const bContext *C,
const AssetLibraryReference &library_reference,
const AssetHandle &asset_handle)
{
if (ED_asset_handle_get_local_id(&asset_handle) ||
!ED_asset_handle_get_metadata(&asset_handle)) {
return {};
}
const char *library_path = ED_assetlist_library_path(&library_reference);
if (!library_path && C) {
library_path = assetlist_library_path_from_sfile_get_hack(C);
}
if (!library_path) {
return {};
}
const char *asset_relpath = asset_handle.file_data->relpath;
char path[FILE_MAX_LIBEXTRA];
BLI_path_join(path, sizeof(path), library_path, asset_relpath);
return path;
}
ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle)
{
ImBuf *imbuf = filelist_file_getimage(asset_handle->file_data);
@ -515,15 +467,6 @@ ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle)
return filelist_geticon_image_ex(asset_handle->file_data);
}
const char *ED_assetlist_library_path(const AssetLibraryReference *library_reference)
{
AssetList *list = AssetListStorage::lookup_list(*library_reference);
if (list) {
return list->filepath().data();
}
return nullptr;
}
bool ED_assetlist_listen(const AssetLibraryReference *library_reference,
const wmNotifier *notifier)
{

View File

@ -44,15 +44,11 @@ class AssetTemporaryIDConsumer : NonCopyable, NonMovable {
return ED_asset_handle_get_local_id(&handle_);
}
ID *import_id(const bContext *C,
const AssetLibraryReference &asset_library_ref,
ID_Type id_type,
Main &bmain,
ReportList &reports)
ID *import_id(ID_Type id_type, Main &bmain, ReportList &reports)
{
const char *asset_name = ED_asset_handle_get_name(&handle_);
char blend_file_path[FILE_MAX_LIBEXTRA];
ED_asset_handle_get_full_library_path(C, &asset_library_ref, &handle_, blend_file_path);
ED_asset_handle_get_full_library_path(&handle_, blend_file_path);
temp_lib_context_ = BLO_library_temp_load_id(
&bmain, blend_file_path, id_type, asset_name, &reports);
@ -84,13 +80,11 @@ void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer)
}
ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer_,
const bContext *C,
const AssetLibraryReference *asset_library_ref,
ID_Type id_type,
Main *bmain,
ReportList *reports)
{
if (!(consumer_ && asset_library_ref && bmain && reports)) {
if (!(consumer_ && bmain && reports)) {
return nullptr;
}
AssetTemporaryIDConsumer *consumer = reinterpret_cast<AssetTemporaryIDConsumer *>(consumer_);
@ -98,5 +92,5 @@ ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer_,
if (ID *local_id = consumer->get_local_id()) {
return local_id;
}
return consumer->import_id(C, *asset_library_ref, id_type, *bmain, *reports);
return consumer->import_id(id_type, *bmain, *reports);
}

View File

@ -1664,11 +1664,7 @@ static bool gpencil_check_cursor_region(bContext *C, const int mval_i[2])
ScrArea *area = CTX_wm_area(C);
Object *ob = CTX_data_active_object(C);
if ((ob == NULL) || !ELEM(ob->mode,
OB_MODE_PAINT_GPENCIL,
OB_MODE_SCULPT_GPENCIL,
OB_MODE_WEIGHT_GPENCIL,
OB_MODE_VERTEX_GPENCIL)) {
if ((ob == NULL) || ((ob->mode & OB_MODE_ALL_PAINT_GPENCIL) == 0)) {
return false;
}

View File

@ -293,7 +293,10 @@ void ED_object_vpaintmode_exit(struct bContext *C);
void ED_object_wpaintmode_exit_ex(struct Object *ob);
void ED_object_wpaintmode_exit(struct bContext *C);
void ED_object_texture_paint_mode_enter_ex(struct Main *bmain, struct Scene *scene, Object *ob);
void ED_object_texture_paint_mode_enter_ex(struct Main *bmain,
struct Scene *scene,
struct Depsgraph *depsgraph,
Object *ob);
void ED_object_texture_paint_mode_enter(struct bContext *C);
void ED_object_texture_paint_mode_exit_ex(struct Main *bmain, struct Scene *scene, Object *ob);

View File

@ -2124,10 +2124,7 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id)
}
/* reset the icon */
if ((ob != NULL) &&
(ob->mode & (OB_MODE_PAINT_GPENCIL | OB_MODE_VERTEX_GPENCIL | OB_MODE_SCULPT_GPENCIL |
OB_MODE_WEIGHT_GPENCIL)) &&
(br->gpencil_settings != NULL)) {
if ((ob != NULL) && (ob->mode & OB_MODE_ALL_PAINT_GPENCIL) && (br->gpencil_settings != NULL)) {
switch (br->gpencil_settings->icon_id) {
case GP_BRUSH_ICON_PENCIL:
br->id.icon_id = ICON_GPBRUSH_PENCIL;

View File

@ -36,9 +36,7 @@ struct AssetViewListData {
bool show_names;
};
static void asset_view_item_but_drag_set(uiBut *but,
AssetViewListData *list_data,
AssetHandle *asset_handle)
static void asset_view_item_but_drag_set(uiBut *but, AssetHandle *asset_handle)
{
ID *id = ED_asset_handle_get_local_id(asset_handle);
if (id != nullptr) {
@ -49,8 +47,7 @@ static void asset_view_item_but_drag_set(uiBut *but,
char blend_path[FILE_MAX_LIBEXTRA];
/* Context can be null here, it's only needed for a File Browser specific hack that should go
* away before too long. */
ED_asset_handle_get_full_library_path(
nullptr, &list_data->asset_library_ref, asset_handle, blend_path);
ED_asset_handle_get_full_library_path(asset_handle, blend_path);
if (blend_path[0]) {
ImBuf *imbuf = ED_assetlist_asset_image_get(asset_handle);
@ -107,7 +104,7 @@ static void asset_view_draw_item(uiList *ui_list,
/* NOLINTNEXTLINE: bugprone-suspicious-enum-usage */
UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
if (!ui_list->dyn_data->custom_drag_optype) {
asset_view_item_but_drag_set(but, list_data, asset_handle);
asset_view_item_but_drag_set(but, asset_handle);
}
}

View File

@ -137,8 +137,7 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode)
}
break;
case OB_GPENCIL:
if (mode & (OB_MODE_EDIT_GPENCIL | OB_MODE_PAINT_GPENCIL | OB_MODE_SCULPT_GPENCIL |
OB_MODE_WEIGHT_GPENCIL | OB_MODE_VERTEX_GPENCIL)) {
if (mode & (OB_MODE_EDIT_GPENCIL | OB_MODE_ALL_PAINT_GPENCIL)) {
return true;
}
break;

View File

@ -15,6 +15,7 @@
#include "DNA_brush_types.h"
#include "DNA_color_types.h"
#include "DNA_customdata_types.h"
#include "DNA_mesh_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"

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