Mesh: Replace auto smooth with node group #108014
|
@ -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
|
||||
========
|
||||
|
||||
|
|
|
@ -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, "
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) ?
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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 -------- */
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>)),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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++);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue