forked from blender/blender
realize-depth #5
@ -1949,7 +1949,7 @@ if html_theme == "furo":
|
|||||||
"sidebar/scroll-start.html",
|
"sidebar/scroll-start.html",
|
||||||
"sidebar/navigation.html",
|
"sidebar/navigation.html",
|
||||||
"sidebar/scroll-end.html",
|
"sidebar/scroll-end.html",
|
||||||
# "sidebar/variant-selector.html",
|
"sidebar/variant-selector.html",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
|
4
extern/CMakeLists.txt
vendored
4
extern/CMakeLists.txt
vendored
@ -104,10 +104,6 @@ if(WITH_MOD_FLUID)
|
|||||||
add_subdirectory(mantaflow)
|
add_subdirectory(mantaflow)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WITH_COMPOSITOR_CPU)
|
|
||||||
add_subdirectory(smaa_areatex)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WITH_VULKAN_BACKEND)
|
if(WITH_VULKAN_BACKEND)
|
||||||
add_subdirectory(vulkan_memory_allocator)
|
add_subdirectory(vulkan_memory_allocator)
|
||||||
endif()
|
endif()
|
||||||
|
5
extern/smaa_areatex/CMakeLists.txt
vendored
5
extern/smaa_areatex/CMakeLists.txt
vendored
@ -1,5 +0,0 @@
|
|||||||
# SPDX-FileCopyrightText: 2017 Blender Foundation
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
add_executable(smaa_areatex smaa_areatex.cpp)
|
|
5
extern/smaa_areatex/README.blender
vendored
5
extern/smaa_areatex/README.blender
vendored
@ -1,5 +0,0 @@
|
|||||||
Project: smaa-cpp
|
|
||||||
URL: https://github.com/iRi-E/smaa-cpp
|
|
||||||
License: MIT
|
|
||||||
Upstream version: 0.4.0
|
|
||||||
Local modifications:
|
|
1210
extern/smaa_areatex/smaa_areatex.cpp
vendored
1210
extern/smaa_areatex/smaa_areatex.cpp
vendored
File diff suppressed because it is too large
Load Diff
@ -106,7 +106,7 @@ struct ShaderCache {
|
|||||||
|
|
||||||
friend ShaderCache *get_shader_cache(id<MTLDevice> mtlDevice);
|
friend ShaderCache *get_shader_cache(id<MTLDevice> mtlDevice);
|
||||||
|
|
||||||
void compile_thread_func(int thread_index);
|
void compile_thread_func();
|
||||||
|
|
||||||
using PipelineCollection = std::vector<unique_ptr<MetalKernelPipeline>>;
|
using PipelineCollection = std::vector<unique_ptr<MetalKernelPipeline>>;
|
||||||
|
|
||||||
@ -174,7 +174,7 @@ void ShaderCache::wait_for_all()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShaderCache::compile_thread_func(int /*thread_index*/)
|
void ShaderCache::compile_thread_func()
|
||||||
{
|
{
|
||||||
while (running) {
|
while (running) {
|
||||||
|
|
||||||
@ -309,7 +309,7 @@ void ShaderCache::load_kernel(DeviceKernel device_kernel,
|
|||||||
|
|
||||||
metal_printf("Spawning %d Cycles kernel compilation threads\n", max_mtlcompiler_threads);
|
metal_printf("Spawning %d Cycles kernel compilation threads\n", max_mtlcompiler_threads);
|
||||||
for (int i = 0; i < max_mtlcompiler_threads; i++) {
|
for (int i = 0; i < max_mtlcompiler_threads; i++) {
|
||||||
compile_threads.push_back(std::thread([&] { compile_thread_func(i); }));
|
compile_threads.push_back(std::thread([this] { this->compile_thread_func(); }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,11 @@ typedef struct VolumeShaderCoefficients {
|
|||||||
Spectrum emission;
|
Spectrum emission;
|
||||||
} VolumeShaderCoefficients;
|
} VolumeShaderCoefficients;
|
||||||
|
|
||||||
|
typedef struct EquiangularCoefficients {
|
||||||
|
float3 P;
|
||||||
|
float2 t_range;
|
||||||
|
} EquiangularCoefficients;
|
||||||
|
|
||||||
/* Evaluate shader to get extinction coefficient at P. */
|
/* Evaluate shader to get extinction coefficient at P. */
|
||||||
ccl_device_inline bool shadow_volume_shader_sample(KernelGlobals kg,
|
ccl_device_inline bool shadow_volume_shader_sample(KernelGlobals kg,
|
||||||
IntegratorShadowState state,
|
IntegratorShadowState state,
|
||||||
@ -264,18 +269,18 @@ ccl_device void volume_shadow_heterogeneous(KernelGlobals kg,
|
|||||||
# define VOLUME_SAMPLE_PDF_CUTOFF 1e-8f
|
# define VOLUME_SAMPLE_PDF_CUTOFF 1e-8f
|
||||||
|
|
||||||
ccl_device float volume_equiangular_sample(ccl_private const Ray *ccl_restrict ray,
|
ccl_device float volume_equiangular_sample(ccl_private const Ray *ccl_restrict ray,
|
||||||
const float3 light_P,
|
ccl_private const EquiangularCoefficients &coeffs,
|
||||||
const float xi,
|
const float xi,
|
||||||
ccl_private float *pdf)
|
ccl_private float *pdf)
|
||||||
{
|
{
|
||||||
const float tmin = ray->tmin;
|
const float delta = dot((coeffs.P - ray->P), ray->D);
|
||||||
const float tmax = ray->tmax;
|
const float D = safe_sqrtf(len_squared(coeffs.P - ray->P) - delta * delta);
|
||||||
const float delta = dot((light_P - ray->P), ray->D);
|
|
||||||
const float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta);
|
|
||||||
if (UNLIKELY(D == 0.0f)) {
|
if (UNLIKELY(D == 0.0f)) {
|
||||||
*pdf = 0.0f;
|
*pdf = 0.0f;
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
const float tmin = coeffs.t_range.x;
|
||||||
|
const float tmax = coeffs.t_range.y;
|
||||||
const float theta_a = atan2f(tmin - delta, D);
|
const float theta_a = atan2f(tmin - delta, D);
|
||||||
const float theta_b = atan2f(tmax - delta, D);
|
const float theta_b = atan2f(tmax - delta, D);
|
||||||
const float t_ = D * tanf((xi * theta_b) + (1 - xi) * theta_a);
|
const float t_ = D * tanf((xi * theta_b) + (1 - xi) * theta_a);
|
||||||
@ -289,17 +294,17 @@ ccl_device float volume_equiangular_sample(ccl_private const Ray *ccl_restrict r
|
|||||||
}
|
}
|
||||||
|
|
||||||
ccl_device float volume_equiangular_pdf(ccl_private const Ray *ccl_restrict ray,
|
ccl_device float volume_equiangular_pdf(ccl_private const Ray *ccl_restrict ray,
|
||||||
const float3 light_P,
|
ccl_private const EquiangularCoefficients &coeffs,
|
||||||
const float sample_t)
|
const float sample_t)
|
||||||
{
|
{
|
||||||
const float delta = dot((light_P - ray->P), ray->D);
|
const float delta = dot((coeffs.P - ray->P), ray->D);
|
||||||
const float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta);
|
const float D = safe_sqrtf(len_squared(coeffs.P - ray->P) - delta * delta);
|
||||||
if (UNLIKELY(D == 0.0f)) {
|
if (UNLIKELY(D == 0.0f)) {
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float tmin = ray->tmin;
|
const float tmin = coeffs.t_range.x;
|
||||||
const float tmax = ray->tmax;
|
const float tmax = coeffs.t_range.y;
|
||||||
const float t_ = sample_t - delta;
|
const float t_ = sample_t - delta;
|
||||||
|
|
||||||
const float theta_a = atan2f(tmin - delta, D);
|
const float theta_a = atan2f(tmin - delta, D);
|
||||||
@ -313,6 +318,29 @@ ccl_device float volume_equiangular_pdf(ccl_private const Ray *ccl_restrict ray,
|
|||||||
return pdf;
|
return pdf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ccl_device_inline bool volume_equiangular_valid_ray_segment(KernelGlobals kg,
|
||||||
|
const float3 ray_P,
|
||||||
|
const float3 ray_D,
|
||||||
|
ccl_private float2 *t_range,
|
||||||
|
const ccl_private LightSample *ls)
|
||||||
|
{
|
||||||
|
if (ls->type == LIGHT_SPOT) {
|
||||||
|
ccl_global const KernelLight *klight = &kernel_data_fetch(lights, ls->lamp);
|
||||||
|
return spot_light_valid_ray_segment(klight, ray_P, ray_D, t_range);
|
||||||
|
}
|
||||||
|
if (ls->type == LIGHT_AREA) {
|
||||||
|
ccl_global const KernelLight *klight = &kernel_data_fetch(lights, ls->lamp);
|
||||||
|
return area_light_valid_ray_segment(&klight->area, ray_P - klight->co, ray_D, t_range);
|
||||||
|
}
|
||||||
|
if (ls->type == LIGHT_TRIANGLE) {
|
||||||
|
return triangle_light_valid_ray_segment(kg, ray_P - ls->P, ray_D, t_range, ls);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Point light, the whole range of the ray is visible. */
|
||||||
|
kernel_assert(ls->type == LIGHT_POINT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* Distance sampling */
|
/* Distance sampling */
|
||||||
|
|
||||||
ccl_device float volume_distance_sample(float max_t,
|
ccl_device float volume_distance_sample(float max_t,
|
||||||
@ -403,7 +431,7 @@ typedef struct VolumeIntegrateState {
|
|||||||
ccl_device_forceinline void volume_integrate_step_scattering(
|
ccl_device_forceinline void volume_integrate_step_scattering(
|
||||||
ccl_private const ShaderData *sd,
|
ccl_private const ShaderData *sd,
|
||||||
ccl_private const Ray *ray,
|
ccl_private const Ray *ray,
|
||||||
const float3 equiangular_light_P,
|
ccl_private const EquiangularCoefficients &equiangular_coeffs,
|
||||||
ccl_private const VolumeShaderCoefficients &ccl_restrict coeff,
|
ccl_private const VolumeShaderCoefficients &ccl_restrict coeff,
|
||||||
const Spectrum transmittance,
|
const Spectrum transmittance,
|
||||||
ccl_private VolumeIntegrateState &ccl_restrict vstate,
|
ccl_private VolumeIntegrateState &ccl_restrict vstate,
|
||||||
@ -474,7 +502,7 @@ ccl_device_forceinline void volume_integrate_step_scattering(
|
|||||||
|
|
||||||
/* Multiple importance sampling. */
|
/* Multiple importance sampling. */
|
||||||
if (vstate.use_mis) {
|
if (vstate.use_mis) {
|
||||||
const float equiangular_pdf = volume_equiangular_pdf(ray, equiangular_light_P, new_t);
|
const float equiangular_pdf = volume_equiangular_pdf(ray, equiangular_coeffs, new_t);
|
||||||
const float mis_weight = power_heuristic(vstate.distance_pdf * distance_pdf,
|
const float mis_weight = power_heuristic(vstate.distance_pdf * distance_pdf,
|
||||||
equiangular_pdf);
|
equiangular_pdf);
|
||||||
result.direct_throughput *= 2.0f * mis_weight;
|
result.direct_throughput *= 2.0f * mis_weight;
|
||||||
@ -509,7 +537,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous(
|
|||||||
ccl_global float *ccl_restrict render_buffer,
|
ccl_global float *ccl_restrict render_buffer,
|
||||||
const float object_step_size,
|
const float object_step_size,
|
||||||
const VolumeSampleMethod direct_sample_method,
|
const VolumeSampleMethod direct_sample_method,
|
||||||
const float3 equiangular_light_P,
|
ccl_private const EquiangularCoefficients &equiangular_coeffs,
|
||||||
ccl_private VolumeIntegrateResult &result)
|
ccl_private VolumeIntegrateResult &result)
|
||||||
{
|
{
|
||||||
PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_INTEGRATE);
|
PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_INTEGRATE);
|
||||||
@ -560,7 +588,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous(
|
|||||||
/* Equiangular sampling: compute distance and PDF in advance. */
|
/* Equiangular sampling: compute distance and PDF in advance. */
|
||||||
if (vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR) {
|
if (vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR) {
|
||||||
result.direct_t = volume_equiangular_sample(
|
result.direct_t = volume_equiangular_sample(
|
||||||
ray, equiangular_light_P, vstate.rscatter, &vstate.equiangular_pdf);
|
ray, equiangular_coeffs, vstate.rscatter, &vstate.equiangular_pdf);
|
||||||
}
|
}
|
||||||
# ifdef __PATH_GUIDING__
|
# ifdef __PATH_GUIDING__
|
||||||
result.direct_sample_method = vstate.direct_sample_method;
|
result.direct_sample_method = vstate.direct_sample_method;
|
||||||
@ -614,7 +642,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous(
|
|||||||
|
|
||||||
/* Scattering and absorption. */
|
/* Scattering and absorption. */
|
||||||
volume_integrate_step_scattering(
|
volume_integrate_step_scattering(
|
||||||
sd, ray, equiangular_light_P, coeff, transmittance, vstate, result);
|
sd, ray, equiangular_coeffs, coeff, transmittance, vstate, result);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* Absorption only. */
|
/* Absorption only. */
|
||||||
@ -673,7 +701,8 @@ ccl_device_forceinline bool integrate_volume_equiangular_sample_light(
|
|||||||
ccl_private const Ray *ccl_restrict ray,
|
ccl_private const Ray *ccl_restrict ray,
|
||||||
ccl_private const ShaderData *ccl_restrict sd,
|
ccl_private const ShaderData *ccl_restrict sd,
|
||||||
ccl_private const RNGState *ccl_restrict rng_state,
|
ccl_private const RNGState *ccl_restrict rng_state,
|
||||||
ccl_private float3 *ccl_restrict P)
|
ccl_private EquiangularCoefficients *ccl_restrict equiangular_coeffs,
|
||||||
|
ccl_private LightSample &ccl_restrict ls)
|
||||||
{
|
{
|
||||||
/* Test if there is a light or BSDF that needs direct light. */
|
/* Test if there is a light or BSDF that needs direct light. */
|
||||||
if (!kernel_data.integrator.use_direct_light) {
|
if (!kernel_data.integrator.use_direct_light) {
|
||||||
@ -685,7 +714,6 @@ ccl_device_forceinline bool integrate_volume_equiangular_sample_light(
|
|||||||
const uint bounce = INTEGRATOR_STATE(state, path, bounce);
|
const uint bounce = INTEGRATOR_STATE(state, path, bounce);
|
||||||
const float3 rand_light = path_state_rng_3D(kg, rng_state, PRNG_LIGHT);
|
const float3 rand_light = path_state_rng_3D(kg, rng_state, PRNG_LIGHT);
|
||||||
|
|
||||||
LightSample ls ccl_optional_struct_init;
|
|
||||||
if (!light_sample_from_volume_segment(kg,
|
if (!light_sample_from_volume_segment(kg,
|
||||||
rand_light,
|
rand_light,
|
||||||
sd->time,
|
sd->time,
|
||||||
@ -708,9 +736,10 @@ ccl_device_forceinline bool integrate_volume_equiangular_sample_light(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
*P = ls.P;
|
equiangular_coeffs->P = ls.P;
|
||||||
|
|
||||||
return true;
|
return volume_equiangular_valid_ray_segment(
|
||||||
|
kg, ray->P, ray->D, &equiangular_coeffs->t_range, &ls);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Path tracing: sample point on light and evaluate light shader, then
|
/* Path tracing: sample point on light and evaluate light shader, then
|
||||||
@ -725,41 +754,26 @@ ccl_device_forceinline void integrate_volume_direct_light(
|
|||||||
# ifdef __PATH_GUIDING__
|
# ifdef __PATH_GUIDING__
|
||||||
ccl_private const Spectrum unlit_throughput,
|
ccl_private const Spectrum unlit_throughput,
|
||||||
# endif
|
# endif
|
||||||
ccl_private const Spectrum throughput)
|
ccl_private const Spectrum throughput,
|
||||||
|
ccl_private LightSample &ccl_restrict ls)
|
||||||
{
|
{
|
||||||
PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_DIRECT_LIGHT);
|
PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_DIRECT_LIGHT);
|
||||||
|
|
||||||
if (!kernel_data.integrator.use_direct_light) {
|
if (!kernel_data.integrator.use_direct_light || ls.emitter_id == EMITTER_NONE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sample position on the same light again, now from the shading point where we scattered.
|
/* Sample position on the same light again, now from the shading point where we scattered. */
|
||||||
*
|
|
||||||
* Note that this means we sample the light tree twice when equiangular sampling is used.
|
|
||||||
* We could consider sampling the light tree just once and use the same light position again.
|
|
||||||
*
|
|
||||||
* This would make the PDFs for MIS weights more complicated due to having to account for
|
|
||||||
* both distance/equiangular and direct/indirect light sampling, but could be more accurate.
|
|
||||||
* Additionally we could end up behind the light or outside a spot light cone, which might
|
|
||||||
* waste a sample. Though on the other hand it would be possible to prevent that with
|
|
||||||
* equiangular sampling restricted to a smaller sub-segment where the light has influence. */
|
|
||||||
LightSample ls ccl_optional_struct_init;
|
|
||||||
{
|
{
|
||||||
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
|
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
|
||||||
const uint bounce = INTEGRATOR_STATE(state, path, bounce);
|
const uint bounce = INTEGRATOR_STATE(state, path, bounce);
|
||||||
const float3 rand_light = path_state_rng_3D(kg, rng_state, PRNG_LIGHT);
|
const float3 rand_light = path_state_rng_3D(kg, rng_state, PRNG_LIGHT);
|
||||||
|
const float3 N = zero_float3();
|
||||||
|
const int object_receiver = light_link_receiver_nee(kg, sd);
|
||||||
|
const int shader_flags = SD_BSDF_HAS_TRANSMISSION;
|
||||||
|
|
||||||
if (!light_sample_from_position(kg,
|
if (!light_sample<false>(
|
||||||
rng_state,
|
kg, rand_light, sd->time, P, N, object_receiver, shader_flags, bounce, path_flag, &ls))
|
||||||
rand_light,
|
|
||||||
sd->time,
|
|
||||||
P,
|
|
||||||
zero_float3(),
|
|
||||||
light_link_receiver_nee(kg, sd),
|
|
||||||
SD_BSDF_HAS_TRANSMISSION,
|
|
||||||
bounce,
|
|
||||||
path_flag,
|
|
||||||
&ls))
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -877,6 +891,7 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
|
|||||||
KernelGlobals kg,
|
KernelGlobals kg,
|
||||||
IntegratorState state,
|
IntegratorState state,
|
||||||
ccl_private ShaderData *sd,
|
ccl_private ShaderData *sd,
|
||||||
|
ccl_private const Ray *ray,
|
||||||
ccl_private const RNGState *rng_state,
|
ccl_private const RNGState *rng_state,
|
||||||
ccl_private const ShaderVolumePhases *phases)
|
ccl_private const ShaderVolumePhases *phases)
|
||||||
{
|
{
|
||||||
@ -929,6 +944,7 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
|
|||||||
INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P;
|
INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P;
|
||||||
INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(phase_wo);
|
INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(phase_wo);
|
||||||
INTEGRATOR_STATE_WRITE(state, ray, tmin) = 0.0f;
|
INTEGRATOR_STATE_WRITE(state, ray, tmin) = 0.0f;
|
||||||
|
INTEGRATOR_STATE_WRITE(state, ray, previous_dt) = ray->tmax - ray->tmin;
|
||||||
INTEGRATOR_STATE_WRITE(state, ray, tmax) = FLT_MAX;
|
INTEGRATOR_STATE_WRITE(state, ray, tmax) = FLT_MAX;
|
||||||
# ifdef __RAY_DIFFERENTIALS__
|
# ifdef __RAY_DIFFERENTIALS__
|
||||||
INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP);
|
INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP);
|
||||||
@ -957,7 +973,8 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
|
|||||||
|
|
||||||
/* Update path state */
|
/* Update path state */
|
||||||
INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = phase_pdf;
|
INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = phase_pdf;
|
||||||
INTEGRATOR_STATE_WRITE(state, path, mis_origin_n) = zero_float3();
|
const float3 previous_P = ray->P + ray->D * ray->tmin;
|
||||||
|
INTEGRATOR_STATE_WRITE(state, path, mis_origin_n) = sd->P - previous_P;
|
||||||
INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf(
|
INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf(
|
||||||
unguided_phase_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf));
|
unguided_phase_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf));
|
||||||
|
|
||||||
@ -989,11 +1006,15 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
|
|||||||
|
|
||||||
/* Sample light ahead of volume stepping, for equiangular sampling. */
|
/* Sample light ahead of volume stepping, for equiangular sampling. */
|
||||||
/* TODO: distant lights are ignored now, but could instead use even distribution. */
|
/* TODO: distant lights are ignored now, but could instead use even distribution. */
|
||||||
|
LightSample ls ccl_optional_struct_init;
|
||||||
|
ls.emitter_id = EMITTER_NONE;
|
||||||
const bool need_light_sample = !(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_TERMINATE);
|
const bool need_light_sample = !(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_TERMINATE);
|
||||||
float3 equiangular_P = zero_float3();
|
|
||||||
const bool have_equiangular_sample = need_light_sample &&
|
EquiangularCoefficients equiangular_coeffs = {zero_float3(), make_float2(ray->tmin, ray->tmax)};
|
||||||
integrate_volume_equiangular_sample_light(
|
|
||||||
kg, state, ray, &sd, &rng_state, &equiangular_P);
|
const bool have_equiangular_sample =
|
||||||
|
need_light_sample && integrate_volume_equiangular_sample_light(
|
||||||
|
kg, state, ray, &sd, &rng_state, &equiangular_coeffs, ls);
|
||||||
|
|
||||||
VolumeSampleMethod direct_sample_method = (have_equiangular_sample) ?
|
VolumeSampleMethod direct_sample_method = (have_equiangular_sample) ?
|
||||||
volume_stack_sample_method(kg, state) :
|
volume_stack_sample_method(kg, state) :
|
||||||
@ -1023,7 +1044,7 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
|
|||||||
render_buffer,
|
render_buffer,
|
||||||
step_size,
|
step_size,
|
||||||
direct_sample_method,
|
direct_sample_method,
|
||||||
equiangular_P,
|
equiangular_coeffs,
|
||||||
result);
|
result);
|
||||||
|
|
||||||
/* Perform path termination. The intersect_closest will have already marked this path
|
/* Perform path termination. The intersect_closest will have already marked this path
|
||||||
@ -1091,7 +1112,8 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
|
|||||||
# ifdef __PATH_GUIDING__
|
# ifdef __PATH_GUIDING__
|
||||||
unlit_throughput,
|
unlit_throughput,
|
||||||
# endif
|
# endif
|
||||||
result.direct_throughput);
|
result.direct_throughput,
|
||||||
|
ls);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Indirect light.
|
/* Indirect light.
|
||||||
@ -1130,7 +1152,7 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
|
|||||||
# endif
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
if (integrate_volume_phase_scatter(kg, state, &sd, &rng_state, &result.indirect_phases)) {
|
if (integrate_volume_phase_scatter(kg, state, &sd, ray, &rng_state, &result.indirect_phases)) {
|
||||||
return VOLUME_PATH_SCATTERED;
|
return VOLUME_PATH_SCATTERED;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -75,6 +75,9 @@ KERNEL_STRUCT_MEMBER(ray, float, tmax, KERNEL_FEATURE_PATH_TRACING)
|
|||||||
KERNEL_STRUCT_MEMBER(ray, float, time, KERNEL_FEATURE_PATH_TRACING)
|
KERNEL_STRUCT_MEMBER(ray, float, time, KERNEL_FEATURE_PATH_TRACING)
|
||||||
KERNEL_STRUCT_MEMBER(ray, float, dP, KERNEL_FEATURE_PATH_TRACING)
|
KERNEL_STRUCT_MEMBER(ray, float, dP, KERNEL_FEATURE_PATH_TRACING)
|
||||||
KERNEL_STRUCT_MEMBER(ray, float, dD, KERNEL_FEATURE_PATH_TRACING)
|
KERNEL_STRUCT_MEMBER(ray, float, dD, KERNEL_FEATURE_PATH_TRACING)
|
||||||
|
#ifdef __LIGHT_TREE__
|
||||||
|
KERNEL_STRUCT_MEMBER(ray, float, previous_dt, KERNEL_FEATURE_PATH_TRACING)
|
||||||
|
#endif
|
||||||
KERNEL_STRUCT_END(ray)
|
KERNEL_STRUCT_END(ray)
|
||||||
|
|
||||||
/*************************** Intersection result ******************************/
|
/*************************** Intersection result ******************************/
|
||||||
|
@ -233,6 +233,11 @@ ccl_device bool area_light_spread_clamp_light(const float3 P,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ccl_device_forceinline bool area_light_is_ellipse(const ccl_global KernelAreaLight *light)
|
||||||
|
{
|
||||||
|
return light->invarea < 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
/* Common API. */
|
/* Common API. */
|
||||||
/* Compute `eval_fac` and `pdf`. Also sample a new position on the light if `sample_coord`. */
|
/* Compute `eval_fac` and `pdf`. Also sample a new position on the light if `sample_coord`. */
|
||||||
template<bool in_volume_segment>
|
template<bool in_volume_segment>
|
||||||
@ -338,7 +343,7 @@ ccl_device_inline bool area_light_sample(const ccl_global KernelLight *klight,
|
|||||||
const float light_v = dot(inplane, klight->area.axis_v) / klight->area.len_v;
|
const float light_v = dot(inplane, klight->area.axis_v) / klight->area.len_v;
|
||||||
|
|
||||||
if (!in_volume_segment) {
|
if (!in_volume_segment) {
|
||||||
const bool is_ellipse = (klight->area.invarea < 0.0f);
|
const bool is_ellipse = area_light_is_ellipse(&klight->area);
|
||||||
|
|
||||||
/* Sampled point lies outside of the area light. */
|
/* Sampled point lies outside of the area light. */
|
||||||
if (is_ellipse && (sqr(light_u) + sqr(light_v) > 0.25f)) {
|
if (is_ellipse && (sqr(light_u) + sqr(light_v) > 0.25f)) {
|
||||||
@ -380,7 +385,7 @@ ccl_device_inline bool area_light_intersect(const ccl_global KernelLight *klight
|
|||||||
{
|
{
|
||||||
/* Area light. */
|
/* Area light. */
|
||||||
const float invarea = fabsf(klight->area.invarea);
|
const float invarea = fabsf(klight->area.invarea);
|
||||||
const bool is_ellipse = (klight->area.invarea < 0.0f);
|
const bool is_ellipse = area_light_is_ellipse(&klight->area);
|
||||||
if (invarea == 0.0f) {
|
if (invarea == 0.0f) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -428,6 +433,55 @@ ccl_device_inline bool area_light_sample_from_intersection(
|
|||||||
return area_light_eval<false>(klight, ray_P, &light_P, ls, zero_float2(), false);
|
return area_light_eval<false>(klight, ray_P, &light_P, ls, zero_float2(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns the maximal distance between the light center and the boundary. */
|
||||||
|
ccl_device_forceinline float area_light_max_extent(const ccl_global KernelAreaLight *light)
|
||||||
|
{
|
||||||
|
return 0.5f * (area_light_is_ellipse(light) ? fmaxf(light->len_u, light->len_v) :
|
||||||
|
len(make_float2(light->len_u, light->len_v)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the ray segment lit by the area light. */
|
||||||
|
ccl_device_inline bool area_light_valid_ray_segment(const ccl_global KernelAreaLight *light,
|
||||||
|
float3 P,
|
||||||
|
float3 D,
|
||||||
|
ccl_private float2 *t_range)
|
||||||
|
{
|
||||||
|
bool valid;
|
||||||
|
const float tan_half_spread = light->tan_half_spread;
|
||||||
|
float3 axis = light->dir;
|
||||||
|
|
||||||
|
const bool angle_almost_zero = (tan_half_spread < 1e-5f);
|
||||||
|
if (angle_almost_zero) {
|
||||||
|
/* Map to local coordinate of the light. Do not use `itfm` in `KernelLight` as there might be
|
||||||
|
* additional scaling in the light size. */
|
||||||
|
const Transform tfm = make_transform(light->axis_u, light->axis_v, axis);
|
||||||
|
P = transform_point(&tfm, P);
|
||||||
|
D = transform_direction(&tfm, D);
|
||||||
|
axis = make_float3(0.0f, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
const float half_len_u = 0.5f * light->len_u;
|
||||||
|
const float half_len_v = 0.5f * light->len_v;
|
||||||
|
if (area_light_is_ellipse(light)) {
|
||||||
|
valid = ray_infinite_cylinder_intersect(P, D, half_len_u, half_len_v, t_range);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const float3 bbox_min = make_float3(-half_len_u, -half_len_v, 0.0f);
|
||||||
|
const float3 bbox_max = make_float3(half_len_u, half_len_v, FLT_MAX);
|
||||||
|
valid = ray_aabb_intersect(bbox_min, bbox_max, P, D, t_range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Conservative estimation with the smallest possible cone covering the whole spread. */
|
||||||
|
const float3 apex_to_point = P + area_light_max_extent(light) / tan_half_spread * axis;
|
||||||
|
const float cos_angle_sq = 1.0f / (1.0f + sqr(tan_half_spread));
|
||||||
|
|
||||||
|
valid = ray_cone_intersect(axis, apex_to_point, D, cos_angle_sq, t_range);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Limit the range to the positive side of the area light. */
|
||||||
|
return valid && ray_plane_intersect(axis, P, D, t_range);
|
||||||
|
}
|
||||||
|
|
||||||
template<bool in_volume_segment>
|
template<bool in_volume_segment>
|
||||||
ccl_device_forceinline bool area_light_tree_parameters(const ccl_global KernelLight *klight,
|
ccl_device_forceinline bool area_light_tree_parameters(const ccl_global KernelLight *klight,
|
||||||
const float3 centroid,
|
const float3 centroid,
|
||||||
@ -464,9 +518,8 @@ ccl_device_forceinline bool area_light_tree_parameters(const ccl_global KernelLi
|
|||||||
const bool shape_above_surface = dot(N, centroid - P) + fabsf(dot(N, extentu)) +
|
const bool shape_above_surface = dot(N, centroid - P) + fabsf(dot(N, extentu)) +
|
||||||
fabsf(dot(N, extentv)) >
|
fabsf(dot(N, extentv)) >
|
||||||
0;
|
0;
|
||||||
const bool in_volume = is_zero(N);
|
|
||||||
|
|
||||||
return (front_facing && shape_above_surface) || in_volume;
|
return front_facing && shape_above_surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
CCL_NAMESPACE_END
|
CCL_NAMESPACE_END
|
||||||
|
@ -12,9 +12,9 @@ CCL_NAMESPACE_BEGIN
|
|||||||
|
|
||||||
typedef struct LightSample {
|
typedef struct LightSample {
|
||||||
float3 P; /* position on light, or direction for distant light */
|
float3 P; /* position on light, or direction for distant light */
|
||||||
float3 Ng; /* normal on light */
|
packed_float3 Ng; /* normal on light */
|
||||||
float3 D; /* direction from shading point to light */
|
|
||||||
float t; /* distance to light (FLT_MAX for distant light) */
|
float t; /* distance to light (FLT_MAX for distant light) */
|
||||||
|
float3 D; /* direction from shading point to light */
|
||||||
float u, v; /* parametric coordinate on primitive */
|
float u, v; /* parametric coordinate on primitive */
|
||||||
float pdf; /* pdf for selecting light and point on light */
|
float pdf; /* pdf for selecting light and point on light */
|
||||||
float pdf_selection; /* pdf for selecting light */
|
float pdf_selection; /* pdf for selecting light */
|
||||||
@ -25,6 +25,7 @@ typedef struct LightSample {
|
|||||||
int lamp; /* lamp id */
|
int lamp; /* lamp id */
|
||||||
int group; /* lightgroup */
|
int group; /* lightgroup */
|
||||||
LightType type; /* type of light */
|
LightType type; /* type of light */
|
||||||
|
int emitter_id; /* index in the emitter array */
|
||||||
} LightSample;
|
} LightSample;
|
||||||
|
|
||||||
/* Utilities */
|
/* Utilities */
|
||||||
|
@ -41,36 +41,14 @@ ccl_device int light_distribution_sample(KernelGlobals kg, const float rand)
|
|||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<bool in_volume_segment>
|
|
||||||
ccl_device_noinline bool light_distribution_sample(KernelGlobals kg,
|
ccl_device_noinline bool light_distribution_sample(KernelGlobals kg,
|
||||||
const float3 rand,
|
const float rand,
|
||||||
const float time,
|
|
||||||
const float3 P,
|
|
||||||
const float3 N,
|
|
||||||
const int object_receiver,
|
|
||||||
const int shader_flags,
|
|
||||||
const int bounce,
|
|
||||||
const uint32_t path_flag,
|
|
||||||
ccl_private LightSample *ls)
|
ccl_private LightSample *ls)
|
||||||
{
|
{
|
||||||
/* Sample light index from distribution. */
|
/* Sample light index from distribution. */
|
||||||
/* The first two dimensions of the Sobol sequence have better stratification. */
|
ls->emitter_id = light_distribution_sample(kg, rand);
|
||||||
const int index = light_distribution_sample(kg, rand.z);
|
ls->pdf_selection = kernel_data.integrator.distribution_pdf_lights;
|
||||||
const float pdf_selection = kernel_data.integrator.distribution_pdf_lights;
|
return true;
|
||||||
const float2 rand_uv = float3_to_float2(rand);
|
|
||||||
return light_sample<in_volume_segment>(kg,
|
|
||||||
rand_uv,
|
|
||||||
time,
|
|
||||||
P,
|
|
||||||
N,
|
|
||||||
object_receiver,
|
|
||||||
shader_flags,
|
|
||||||
bounce,
|
|
||||||
path_flag,
|
|
||||||
index,
|
|
||||||
0,
|
|
||||||
pdf_selection,
|
|
||||||
ls);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ccl_device_inline float light_distribution_pdf_lamp(KernelGlobals kg)
|
ccl_device_inline float light_distribution_pdf_lamp(KernelGlobals kg)
|
||||||
|
@ -177,7 +177,7 @@ ccl_device_inline bool light_sample(KernelGlobals kg,
|
|||||||
|
|
||||||
template<bool in_volume_segment>
|
template<bool in_volume_segment>
|
||||||
ccl_device_noinline bool light_sample(KernelGlobals kg,
|
ccl_device_noinline bool light_sample(KernelGlobals kg,
|
||||||
const float2 rand,
|
const float3 rand_light,
|
||||||
const float time,
|
const float time,
|
||||||
const float3 P,
|
const float3 P,
|
||||||
const float3 N,
|
const float3 N,
|
||||||
@ -185,33 +185,31 @@ ccl_device_noinline bool light_sample(KernelGlobals kg,
|
|||||||
const int shader_flags,
|
const int shader_flags,
|
||||||
const int bounce,
|
const int bounce,
|
||||||
const uint32_t path_flag,
|
const uint32_t path_flag,
|
||||||
const int emitter_index,
|
|
||||||
const int object_id,
|
|
||||||
const float pdf_selection,
|
|
||||||
ccl_private LightSample *ls)
|
ccl_private LightSample *ls)
|
||||||
{
|
{
|
||||||
|
/* The first two dimensions of the Sobol sequence have better stratification, use them to sample
|
||||||
|
* position on the light. */
|
||||||
|
const float2 rand = float3_to_float2(rand_light);
|
||||||
|
|
||||||
int prim;
|
int prim;
|
||||||
MeshLight mesh_light;
|
MeshLight mesh_light;
|
||||||
#ifdef __LIGHT_TREE__
|
#ifdef __LIGHT_TREE__
|
||||||
if (kernel_data.integrator.use_light_tree) {
|
if (kernel_data.integrator.use_light_tree) {
|
||||||
ccl_global const KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters,
|
ccl_global const KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters,
|
||||||
emitter_index);
|
ls->emitter_id);
|
||||||
prim = kemitter->light.id;
|
prim = kemitter->light.id;
|
||||||
mesh_light.shader_flag = kemitter->mesh_light.shader_flag;
|
mesh_light.shader_flag = kemitter->mesh_light.shader_flag;
|
||||||
mesh_light.object_id = object_id;
|
mesh_light.object_id = ls->object;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
ccl_global const KernelLightDistribution *kdistribution = &kernel_data_fetch(
|
ccl_global const KernelLightDistribution *kdistribution = &kernel_data_fetch(
|
||||||
light_distribution, emitter_index);
|
light_distribution, ls->emitter_id);
|
||||||
prim = kdistribution->prim;
|
prim = kdistribution->prim;
|
||||||
mesh_light = kdistribution->mesh_light;
|
mesh_light = kdistribution->mesh_light;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* A different value would be assigned in `triangle_light_sample()` if `!use_light_tree`. */
|
|
||||||
ls->pdf_selection = pdf_selection;
|
|
||||||
|
|
||||||
if (prim >= 0) {
|
if (prim >= 0) {
|
||||||
/* Mesh light. */
|
/* Mesh light. */
|
||||||
const int object = mesh_light.object_id;
|
const int object = mesh_light.object_id;
|
||||||
|
@ -329,17 +329,25 @@ ccl_device_inline bool light_sample_from_volume_segment(KernelGlobals kg,
|
|||||||
const uint32_t path_flag,
|
const uint32_t path_flag,
|
||||||
ccl_private LightSample *ls)
|
ccl_private LightSample *ls)
|
||||||
{
|
{
|
||||||
|
const int shader_flags = SD_BSDF_HAS_TRANSMISSION;
|
||||||
|
|
||||||
#ifdef __LIGHT_TREE__
|
#ifdef __LIGHT_TREE__
|
||||||
if (kernel_data.integrator.use_light_tree) {
|
if (kernel_data.integrator.use_light_tree) {
|
||||||
return light_tree_sample<true>(
|
if (!light_tree_sample<true>(kg, rand.z, P, D, t, object_receiver, shader_flags, ls)) {
|
||||||
kg, rand, time, P, D, t, object_receiver, SD_BSDF_HAS_TRANSMISSION, bounce, path_flag, ls);
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
return light_distribution_sample<true>(
|
if (!light_distribution_sample(kg, rand.z, ls)) {
|
||||||
kg, rand, time, P, D, object_receiver, SD_BSDF_HAS_TRANSMISSION, bounce, path_flag, ls);
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Sample position on the selected light. */
|
||||||
|
return light_sample<true>(
|
||||||
|
kg, rand, time, P, D, object_receiver, shader_flags, bounce, path_flag, ls);
|
||||||
}
|
}
|
||||||
|
|
||||||
ccl_device bool light_sample_from_position(KernelGlobals kg,
|
ccl_device bool light_sample_from_position(KernelGlobals kg,
|
||||||
@ -354,17 +362,24 @@ ccl_device bool light_sample_from_position(KernelGlobals kg,
|
|||||||
const uint32_t path_flag,
|
const uint32_t path_flag,
|
||||||
ccl_private LightSample *ls)
|
ccl_private LightSample *ls)
|
||||||
{
|
{
|
||||||
|
/* Randomly select a light. */
|
||||||
#ifdef __LIGHT_TREE__
|
#ifdef __LIGHT_TREE__
|
||||||
if (kernel_data.integrator.use_light_tree) {
|
if (kernel_data.integrator.use_light_tree) {
|
||||||
return light_tree_sample<false>(
|
if (!light_tree_sample<false>(kg, rand.z, P, N, 0.0f, object_receiver, shader_flags, ls)) {
|
||||||
kg, rand, time, P, N, 0.0f, object_receiver, shader_flags, bounce, path_flag, ls);
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
return light_distribution_sample<false>(
|
if (!light_distribution_sample(kg, rand.z, ls)) {
|
||||||
kg, rand, time, P, N, object_receiver, shader_flags, bounce, path_flag, ls);
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Sample position on the selected light. */
|
||||||
|
return light_sample<false>(
|
||||||
|
kg, rand, time, P, N, object_receiver, shader_flags, bounce, path_flag, ls);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update light sample with new shading point position for MNEE. The position on the light is fixed
|
/* Update light sample with new shading point position for MNEE. The position on the light is fixed
|
||||||
@ -415,13 +430,15 @@ ccl_device_inline float light_sample_mis_weight_forward_surface(KernelGlobals kg
|
|||||||
#ifdef __LIGHT_TREE__
|
#ifdef __LIGHT_TREE__
|
||||||
if (kernel_data.integrator.use_light_tree) {
|
if (kernel_data.integrator.use_light_tree) {
|
||||||
float3 ray_P = INTEGRATOR_STATE(state, ray, P);
|
float3 ray_P = INTEGRATOR_STATE(state, ray, P);
|
||||||
|
const float dt = INTEGRATOR_STATE(state, ray, previous_dt);
|
||||||
const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n);
|
const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n);
|
||||||
|
|
||||||
uint lookup_offset = kernel_data_fetch(object_lookup_offset, sd->object);
|
uint lookup_offset = kernel_data_fetch(object_lookup_offset, sd->object);
|
||||||
uint prim_offset = kernel_data_fetch(object_prim_offset, sd->object);
|
uint prim_offset = kernel_data_fetch(object_prim_offset, sd->object);
|
||||||
uint triangle = kernel_data_fetch(triangle_to_tree, sd->prim - prim_offset + lookup_offset);
|
uint triangle = kernel_data_fetch(triangle_to_tree, sd->prim - prim_offset + lookup_offset);
|
||||||
|
|
||||||
pdf *= light_tree_pdf(
|
pdf *= light_tree_pdf(
|
||||||
kg, ray_P, N, path_flag, sd->object, triangle, light_link_receiver_forward(kg, state));
|
kg, ray_P, N, dt, path_flag, sd->object, triangle, light_link_receiver_forward(kg, state));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
@ -445,9 +462,11 @@ ccl_device_inline float light_sample_mis_weight_forward_lamp(KernelGlobals kg,
|
|||||||
#ifdef __LIGHT_TREE__
|
#ifdef __LIGHT_TREE__
|
||||||
if (kernel_data.integrator.use_light_tree) {
|
if (kernel_data.integrator.use_light_tree) {
|
||||||
const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n);
|
const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n);
|
||||||
|
const float dt = INTEGRATOR_STATE(state, ray, previous_dt);
|
||||||
pdf *= light_tree_pdf(kg,
|
pdf *= light_tree_pdf(kg,
|
||||||
P,
|
P,
|
||||||
N,
|
N,
|
||||||
|
dt,
|
||||||
path_flag,
|
path_flag,
|
||||||
0,
|
0,
|
||||||
kernel_data_fetch(light_to_tree, ls->lamp),
|
kernel_data_fetch(light_to_tree, ls->lamp),
|
||||||
@ -485,9 +504,10 @@ ccl_device_inline float light_sample_mis_weight_forward_background(KernelGlobals
|
|||||||
#ifdef __LIGHT_TREE__
|
#ifdef __LIGHT_TREE__
|
||||||
if (kernel_data.integrator.use_light_tree) {
|
if (kernel_data.integrator.use_light_tree) {
|
||||||
const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n);
|
const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n);
|
||||||
|
const float dt = INTEGRATOR_STATE(state, ray, previous_dt);
|
||||||
uint light = kernel_data_fetch(light_to_tree, kernel_data.background.light_index);
|
uint light = kernel_data_fetch(light_to_tree, kernel_data.background.light_index);
|
||||||
pdf *= light_tree_pdf(
|
pdf *= light_tree_pdf(
|
||||||
kg, ray_P, N, path_flag, 0, light, light_link_receiver_forward(kg, state));
|
kg, ray_P, N, dt, path_flag, 0, light, light_link_receiver_forward(kg, state));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
|
@ -265,6 +265,24 @@ ccl_device_inline bool spot_light_sample_from_intersection(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Find the ray segment lit by the spot light. */
|
||||||
|
ccl_device_inline bool spot_light_valid_ray_segment(const ccl_global KernelLight *klight,
|
||||||
|
const float3 P,
|
||||||
|
const float3 D,
|
||||||
|
ccl_private float2 *t_range)
|
||||||
|
{
|
||||||
|
/* Convert to local space of the spot light. */
|
||||||
|
const Transform itfm = klight->itfm;
|
||||||
|
float3 local_P = P + klight->spot.dir * klight->spot.ray_segment_dp;
|
||||||
|
local_P = transform_point(&itfm, local_P);
|
||||||
|
const float3 local_D = transform_direction(&itfm, D);
|
||||||
|
const float3 axis = make_float3(0.0f, 0.0f, -1.0f);
|
||||||
|
|
||||||
|
/* Intersect the ray with the smallest enclosing cone of the light spread. */
|
||||||
|
return ray_cone_intersect(
|
||||||
|
axis, local_P, local_D, sqr(klight->spot.cos_half_spot_angle), t_range);
|
||||||
|
}
|
||||||
|
|
||||||
template<bool in_volume_segment>
|
template<bool in_volume_segment>
|
||||||
ccl_device_forceinline bool spot_light_tree_parameters(const ccl_global KernelLight *klight,
|
ccl_device_forceinline bool spot_light_tree_parameters(const ccl_global KernelLight *klight,
|
||||||
const float3 centroid,
|
const float3 centroid,
|
||||||
|
@ -148,10 +148,7 @@ ccl_device void light_tree_importance(const float3 N_or_D,
|
|||||||
float cos_min_incidence_angle = 1.0f;
|
float cos_min_incidence_angle = 1.0f;
|
||||||
float cos_max_incidence_angle = 1.0f;
|
float cos_max_incidence_angle = 1.0f;
|
||||||
|
|
||||||
/* When sampling the light tree for the second time in `shade_volume.h` and when query the pdf in
|
if (!in_volume_segment) {
|
||||||
* `sample.h`. */
|
|
||||||
const bool in_volume = is_zero(N_or_D);
|
|
||||||
if (!in_volume_segment && !in_volume) {
|
|
||||||
const float3 N = N_or_D;
|
const float3 N = N_or_D;
|
||||||
const float cos_theta_i = has_transmission ? fabsf(dot(point_to_centroid, N)) :
|
const float cos_theta_i = has_transmission ? fabsf(dot(point_to_centroid, N)) :
|
||||||
dot(point_to_centroid, N);
|
dot(point_to_centroid, N);
|
||||||
@ -221,9 +218,9 @@ ccl_device void light_tree_importance(const float3 N_or_D,
|
|||||||
max_importance = fabsf(f_a * cos_min_incidence_angle * energy * cos_min_outgoing_angle /
|
max_importance = fabsf(f_a * cos_min_incidence_angle * energy * cos_min_outgoing_angle /
|
||||||
(in_volume_segment ? min_distance : sqr(min_distance)));
|
(in_volume_segment ? min_distance : sqr(min_distance)));
|
||||||
|
|
||||||
/* TODO: also min importance for volume? */
|
/* TODO: compute proper min importance for volume. */
|
||||||
if (in_volume_segment) {
|
if (in_volume_segment) {
|
||||||
min_importance = max_importance;
|
min_importance = 0.0f;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,10 +267,10 @@ ccl_device bool compute_emitter_centroid_and_dir(KernelGlobals kg,
|
|||||||
/* Arbitrary centroid and direction. */
|
/* Arbitrary centroid and direction. */
|
||||||
centroid = make_float3(0.0f, 0.0f, 1.0f);
|
centroid = make_float3(0.0f, 0.0f, 1.0f);
|
||||||
dir = make_float3(0.0f, 0.0f, -1.0f);
|
dir = make_float3(0.0f, 0.0f, -1.0f);
|
||||||
return !in_volume_segment;
|
break;
|
||||||
case LIGHT_DISTANT:
|
case LIGHT_DISTANT:
|
||||||
dir = centroid;
|
dir = centroid;
|
||||||
return !in_volume_segment;
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -323,12 +320,13 @@ ccl_device void light_tree_node_importance(KernelGlobals kg,
|
|||||||
float cos_theta_u;
|
float cos_theta_u;
|
||||||
float distance;
|
float distance;
|
||||||
if (knode->type == LIGHT_TREE_DISTANT) {
|
if (knode->type == LIGHT_TREE_DISTANT) {
|
||||||
if (in_volume_segment) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
point_to_centroid = -bcone.axis;
|
point_to_centroid = -bcone.axis;
|
||||||
cos_theta_u = fast_cosf(bcone.theta_o + bcone.theta_e);
|
cos_theta_u = fast_cosf(bcone.theta_o + bcone.theta_e);
|
||||||
distance = 1.0f;
|
distance = 1.0f;
|
||||||
|
if (t == FLT_MAX) {
|
||||||
|
/* In world volume, distant light has no contribution. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const float3 centroid = 0.5f * (bbox.min + bbox.max);
|
const float3 centroid = 0.5f * (bbox.min + bbox.max);
|
||||||
@ -339,6 +337,9 @@ ccl_device void light_tree_node_importance(KernelGlobals kg,
|
|||||||
/* Minimal distance of the ray to the cluster. */
|
/* Minimal distance of the ray to the cluster. */
|
||||||
distance = len(centroid - closest_point);
|
distance = len(centroid - closest_point);
|
||||||
point_to_centroid = -compute_v(centroid, P, D, bcone.axis, t);
|
point_to_centroid = -compute_v(centroid, P, D, bcone.axis, t);
|
||||||
|
/* FIXME(weizhen): it is not clear from which point the `cos_theta_u` should be computed in
|
||||||
|
* volume segment. We could use `closest_point` as a conservative measure, but then
|
||||||
|
* `point_to_centroid` should also use `closest_point`. */
|
||||||
cos_theta_u = light_tree_cos_bounding_box_angle(bbox, closest_point, point_to_centroid);
|
cos_theta_u = light_tree_cos_bounding_box_angle(bbox, closest_point, point_to_centroid);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -697,17 +698,16 @@ ccl_device int light_tree_root_node_index(KernelGlobals kg, const int object_rec
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Pick a random light from the light tree from a given shading point P, write to the picked light
|
||||||
|
* index and the probability of picking the light. */
|
||||||
template<bool in_volume_segment>
|
template<bool in_volume_segment>
|
||||||
ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
|
ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
|
||||||
const float3 rand,
|
const float rand,
|
||||||
const float time,
|
|
||||||
const float3 P,
|
const float3 P,
|
||||||
float3 N_or_D,
|
float3 N_or_D,
|
||||||
float t,
|
float t,
|
||||||
const int object_receiver,
|
const int object_receiver,
|
||||||
const int shader_flags,
|
const int shader_flags,
|
||||||
const int bounce,
|
|
||||||
const uint32_t path_flag,
|
|
||||||
ccl_private LightSample *ls)
|
ccl_private LightSample *ls)
|
||||||
{
|
{
|
||||||
if (!kernel_data.integrator.use_direct_light) {
|
if (!kernel_data.integrator.use_direct_light) {
|
||||||
@ -718,10 +718,8 @@ ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
|
|||||||
float pdf_leaf = 1.0f;
|
float pdf_leaf = 1.0f;
|
||||||
float pdf_selection = 1.0f;
|
float pdf_selection = 1.0f;
|
||||||
int selected_emitter = -1;
|
int selected_emitter = -1;
|
||||||
int object_emitter = 0;
|
|
||||||
int node_index = light_tree_root_node_index(kg, object_receiver);
|
int node_index = light_tree_root_node_index(kg, object_receiver);
|
||||||
/* The first two dimensions of the Sobol sequence have better stratification. */
|
float rand_selection = rand;
|
||||||
float rand_selection = rand.z;
|
|
||||||
|
|
||||||
float3 local_P = P;
|
float3 local_P = P;
|
||||||
|
|
||||||
@ -743,7 +741,7 @@ ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Continue with the picked mesh light. */
|
/* Continue with the picked mesh light. */
|
||||||
object_emitter = kernel_data_fetch(light_tree_emitters, selected_emitter).mesh.object_id;
|
ls->object = kernel_data_fetch(light_tree_emitters, selected_emitter).mesh.object_id;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -766,27 +764,18 @@ ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
|
|||||||
pdf_leaf *= (node_index == left_index) ? left_prob : (1.0f - left_prob);
|
pdf_leaf *= (node_index == left_index) ? left_prob : (1.0f - left_prob);
|
||||||
}
|
}
|
||||||
|
|
||||||
pdf_selection *= pdf_leaf;
|
ls->emitter_id = selected_emitter;
|
||||||
|
ls->pdf_selection = pdf_selection * pdf_leaf;
|
||||||
|
|
||||||
return light_sample<in_volume_segment>(kg,
|
return true;
|
||||||
float3_to_float2(rand),
|
|
||||||
time,
|
|
||||||
P,
|
|
||||||
N_or_D,
|
|
||||||
object_receiver,
|
|
||||||
shader_flags,
|
|
||||||
bounce,
|
|
||||||
path_flag,
|
|
||||||
selected_emitter,
|
|
||||||
object_emitter,
|
|
||||||
pdf_selection,
|
|
||||||
ls);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We need to be able to find the probability of selecting a given light for MIS. */
|
/* We need to be able to find the probability of selecting a given light for MIS. */
|
||||||
|
template<bool in_volume_segment>
|
||||||
ccl_device float light_tree_pdf(KernelGlobals kg,
|
ccl_device float light_tree_pdf(KernelGlobals kg,
|
||||||
float3 P,
|
float3 P,
|
||||||
float3 N,
|
float3 N,
|
||||||
|
const float dt,
|
||||||
const int path_flag,
|
const int path_flag,
|
||||||
const int object_emitter,
|
const int object_emitter,
|
||||||
const uint index_emitter,
|
const uint index_emitter,
|
||||||
@ -796,7 +785,7 @@ ccl_device float light_tree_pdf(KernelGlobals kg,
|
|||||||
|
|
||||||
ccl_global const KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters,
|
ccl_global const KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters,
|
||||||
index_emitter);
|
index_emitter);
|
||||||
int root_index;
|
int subtree_root_index;
|
||||||
uint bit_trail, target_emitter;
|
uint bit_trail, target_emitter;
|
||||||
|
|
||||||
if (is_triangle(kemitter)) {
|
if (is_triangle(kemitter)) {
|
||||||
@ -805,16 +794,17 @@ ccl_device float light_tree_pdf(KernelGlobals kg,
|
|||||||
target_emitter = kernel_data_fetch(object_to_tree, object_emitter);
|
target_emitter = kernel_data_fetch(object_to_tree, object_emitter);
|
||||||
ccl_global const KernelLightTreeEmitter *kmesh = &kernel_data_fetch(light_tree_emitters,
|
ccl_global const KernelLightTreeEmitter *kmesh = &kernel_data_fetch(light_tree_emitters,
|
||||||
target_emitter);
|
target_emitter);
|
||||||
root_index = kmesh->mesh.node_id;
|
subtree_root_index = kmesh->mesh.node_id;
|
||||||
ccl_global const KernelLightTreeNode *kroot = &kernel_data_fetch(light_tree_nodes, root_index);
|
ccl_global const KernelLightTreeNode *kroot = &kernel_data_fetch(light_tree_nodes,
|
||||||
|
subtree_root_index);
|
||||||
bit_trail = kroot->bit_trail;
|
bit_trail = kroot->bit_trail;
|
||||||
|
|
||||||
if (kroot->type == LIGHT_TREE_INSTANCE) {
|
if (kroot->type == LIGHT_TREE_INSTANCE) {
|
||||||
root_index = kroot->instance.reference;
|
subtree_root_index = kroot->instance.reference;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
root_index = 0;
|
subtree_root_index = -1;
|
||||||
bit_trail = kemitter->bit_trail;
|
bit_trail = kemitter->bit_trail;
|
||||||
target_emitter = index_emitter;
|
target_emitter = index_emitter;
|
||||||
}
|
}
|
||||||
@ -836,8 +826,8 @@ ccl_device float light_tree_pdf(KernelGlobals kg,
|
|||||||
for (int i = 0; i < knode->num_emitters; i++) {
|
for (int i = 0; i < knode->num_emitters; i++) {
|
||||||
const int emitter = knode->leaf.first_emitter + i;
|
const int emitter = knode->leaf.first_emitter + i;
|
||||||
float max_importance, min_importance;
|
float max_importance, min_importance;
|
||||||
light_tree_emitter_importance<false>(
|
light_tree_emitter_importance<in_volume_segment>(
|
||||||
kg, P, N, 0, has_transmission, emitter, max_importance, min_importance);
|
kg, P, N, dt, has_transmission, emitter, max_importance, min_importance);
|
||||||
num_has_importance += (max_importance > 0);
|
num_has_importance += (max_importance > 0);
|
||||||
if (emitter == target_emitter) {
|
if (emitter == target_emitter) {
|
||||||
target_max_importance = max_importance;
|
target_max_importance = max_importance;
|
||||||
@ -856,13 +846,13 @@ ccl_device float light_tree_pdf(KernelGlobals kg,
|
|||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root_index) {
|
if (subtree_root_index != -1) {
|
||||||
/* Arrived at the mesh light. Continue with the subtree. */
|
/* Arrived at the mesh light. Continue with the subtree. */
|
||||||
float unused;
|
float unused;
|
||||||
light_tree_to_local_space<false>(kg, object_emitter, P, N, unused);
|
light_tree_to_local_space<in_volume_segment>(kg, object_emitter, P, N, unused);
|
||||||
|
|
||||||
node_index = root_index;
|
node_index = subtree_root_index;
|
||||||
root_index = 0;
|
subtree_root_index = -1;
|
||||||
target_emitter = index_emitter;
|
target_emitter = index_emitter;
|
||||||
bit_trail = kemitter->bit_trail;
|
bit_trail = kemitter->bit_trail;
|
||||||
continue;
|
continue;
|
||||||
@ -877,8 +867,8 @@ ccl_device float light_tree_pdf(KernelGlobals kg,
|
|||||||
const int right_index = knode->inner.right_child;
|
const int right_index = knode->inner.right_child;
|
||||||
|
|
||||||
float left_prob;
|
float left_prob;
|
||||||
if (!get_left_probability<false>(
|
if (!get_left_probability<in_volume_segment>(
|
||||||
kg, P, N, 0, has_transmission, left_index, right_index, left_prob))
|
kg, P, N, dt, has_transmission, left_index, right_index, left_prob))
|
||||||
{
|
{
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
@ -896,4 +886,27 @@ ccl_device float light_tree_pdf(KernelGlobals kg,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If the function is called in volume, retrieve the previous point in volume segment, and compute
|
||||||
|
* pdf from there. Otherwise compute from the current shading point. */
|
||||||
|
ccl_device_inline float light_tree_pdf(KernelGlobals kg,
|
||||||
|
float3 P,
|
||||||
|
float3 N,
|
||||||
|
const float dt,
|
||||||
|
const int path_flag,
|
||||||
|
const int emitter_object,
|
||||||
|
const uint emitter_id,
|
||||||
|
const int object_receiver)
|
||||||
|
{
|
||||||
|
if (path_flag & PATH_RAY_VOLUME_SCATTER) {
|
||||||
|
const float3 D_times_t = N;
|
||||||
|
const float3 D = normalize(D_times_t);
|
||||||
|
P = P - D_times_t;
|
||||||
|
return light_tree_pdf<true>(
|
||||||
|
kg, P, D, dt, path_flag, emitter_object, emitter_id, object_receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
return light_tree_pdf<false>(
|
||||||
|
kg, P, N, 0.0f, path_flag, emitter_object, emitter_id, object_receiver);
|
||||||
|
}
|
||||||
|
|
||||||
CCL_NAMESPACE_END
|
CCL_NAMESPACE_END
|
||||||
|
@ -269,6 +269,26 @@ ccl_device_forceinline bool triangle_light_sample(KernelGlobals kg,
|
|||||||
return (ls->pdf > 0.0f);
|
return (ls->pdf > 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Find the ray segment lit by the triangle light. */
|
||||||
|
ccl_device_inline bool triangle_light_valid_ray_segment(KernelGlobals kg,
|
||||||
|
const float3 P,
|
||||||
|
const float3 D,
|
||||||
|
ccl_private float2 *t_range,
|
||||||
|
const ccl_private LightSample *ls)
|
||||||
|
{
|
||||||
|
const int shader_flag = kernel_data_fetch(shaders, ls->shader & SHADER_MASK).flags;
|
||||||
|
const int SD_MIS_BOTH = SD_MIS_BACK | SD_MIS_FRONT;
|
||||||
|
if ((shader_flag & SD_MIS_BOTH) == SD_MIS_BOTH) {
|
||||||
|
/* Both sides are sampled, the complete ray segment is visible. */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only one side is sampled, intersect the ray and the triangle light plane to find the visible
|
||||||
|
* ray segment. Flip normal if Emission Sampling is set to back. */
|
||||||
|
const float3 N = ls->Ng;
|
||||||
|
return ray_plane_intersect((shader_flag & SD_MIS_BACK) ? -N : N, P, D, t_range);
|
||||||
|
}
|
||||||
|
|
||||||
template<bool in_volume_segment>
|
template<bool in_volume_segment>
|
||||||
ccl_device_forceinline bool triangle_light_tree_parameters(
|
ccl_device_forceinline bool triangle_light_tree_parameters(
|
||||||
KernelGlobals kg,
|
KernelGlobals kg,
|
||||||
@ -307,9 +327,8 @@ ccl_device_forceinline bool triangle_light_tree_parameters(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const bool front_facing = bcone.theta_o != 0.0f || dot(bcone.axis, point_to_centroid) < 0;
|
const bool front_facing = bcone.theta_o != 0.0f || dot(bcone.axis, point_to_centroid) < 0;
|
||||||
const bool in_volume = is_zero(N);
|
|
||||||
|
|
||||||
return (front_facing && shape_above_surface) || in_volume;
|
return front_facing && shape_above_surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
CCL_NAMESPACE_END
|
CCL_NAMESPACE_END
|
||||||
|
@ -45,6 +45,7 @@ CCL_NAMESPACE_BEGIN
|
|||||||
#define OBJECT_NONE (~0)
|
#define OBJECT_NONE (~0)
|
||||||
#define PRIM_NONE (~0)
|
#define PRIM_NONE (~0)
|
||||||
#define LAMP_NONE (~0)
|
#define LAMP_NONE (~0)
|
||||||
|
#define EMITTER_NONE (~0)
|
||||||
#define ID_NONE (0.0f)
|
#define ID_NONE (0.0f)
|
||||||
#define PASS_UNUSED (~0)
|
#define PASS_UNUSED (~0)
|
||||||
#define LIGHTGROUP_NONE (~0)
|
#define LIGHTGROUP_NONE (~0)
|
||||||
@ -1376,6 +1377,8 @@ typedef struct KernelSpotLight {
|
|||||||
int is_sphere;
|
int is_sphere;
|
||||||
/* For non-uniform object scaling, the actual spread might be different. */
|
/* For non-uniform object scaling, the actual spread might be different. */
|
||||||
float cos_half_larger_spread;
|
float cos_half_larger_spread;
|
||||||
|
/* Distance from the apex of the smallest enclosing cone of the light spread to light center. */
|
||||||
|
float ray_segment_dp;
|
||||||
} KernelSpotLight;
|
} KernelSpotLight;
|
||||||
|
|
||||||
/* PointLight is SpotLight with only radius and invarea being used. */
|
/* PointLight is SpotLight with only radius and invarea being used. */
|
||||||
|
@ -1362,6 +1362,9 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce
|
|||||||
/* Choose the angle which spans a larger cone. */
|
/* Choose the angle which spans a larger cone. */
|
||||||
klights[light_index].spot.cos_half_larger_spread = inversesqrtf(
|
klights[light_index].spot.cos_half_larger_spread = inversesqrtf(
|
||||||
1.0f + tan_sq * fmaxf(len_u_sq, len_v_sq) / len_w_sq);
|
1.0f + tan_sq * fmaxf(len_u_sq, len_v_sq) / len_w_sq);
|
||||||
|
/* radius / sin(half_angle_small) */
|
||||||
|
klights[light_index].spot.ray_segment_dp =
|
||||||
|
light->size * sqrtf(1.0f + len_w_sq / (tan_sq * fminf(len_u_sq, len_v_sq)));
|
||||||
}
|
}
|
||||||
|
|
||||||
klights[light_index].shader_id = shader_id;
|
klights[light_index].shader_id = shader_id;
|
||||||
|
@ -1030,6 +1030,46 @@ ccl_device_inline uint32_t reverse_integer_bits(uint32_t x)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check if intervals (first->x, first->y) and (second.x, second.y) intersect, and replace the
|
||||||
|
* first interval with their intersection. */
|
||||||
|
ccl_device_inline bool intervals_intersect(ccl_private float2 *first, const float2 second)
|
||||||
|
{
|
||||||
|
first->x = fmaxf(first->x, second.x);
|
||||||
|
first->y = fminf(first->y, second.y);
|
||||||
|
|
||||||
|
return first->x < first->y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Solve quadratic equation a*x^2 + b*x + c = 0, adapted from Mitsuba 3
|
||||||
|
* The solution is ordered so that x1 <= x2.
|
||||||
|
* Returns true if at least one solution is found. */
|
||||||
|
ccl_device_inline bool solve_quadratic(
|
||||||
|
const float a, const float b, const float c, ccl_private float &x1, ccl_private float &x2)
|
||||||
|
{
|
||||||
|
/* If the equation is linear, the solution is -c/b, but b has to be non-zero. */
|
||||||
|
const bool valid_linear = (a == 0.0f) && (b != 0.0f);
|
||||||
|
x1 = x2 = -c / b;
|
||||||
|
|
||||||
|
const float discriminant = sqr(b) - 4.0f * a * c;
|
||||||
|
/* Allow slightly negative discriminant in case of numerical precision issues. */
|
||||||
|
const bool valid_quadratic = (a != 0.0f) && (discriminant > -1e-5f);
|
||||||
|
|
||||||
|
if (valid_quadratic) {
|
||||||
|
/* Numerically stable version of (-b ± sqrt(discriminant)) / (2 * a), avoiding catastrophic
|
||||||
|
* cancellation when `b` is very close to `sqrt(discriminant)`, by finding the solution of
|
||||||
|
* greater magnitude which does not suffer from loss of precision, then using the identity
|
||||||
|
* x1 * x2 = c / a. */
|
||||||
|
const float temp = -0.5f * (b + copysignf(safe_sqrtf(discriminant), b));
|
||||||
|
const float r1 = temp / a;
|
||||||
|
const float r2 = c / temp;
|
||||||
|
|
||||||
|
x1 = fminf(r1, r2);
|
||||||
|
x2 = fmaxf(r1, r2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (valid_linear || valid_quadratic);
|
||||||
|
}
|
||||||
|
|
||||||
CCL_NAMESPACE_END
|
CCL_NAMESPACE_END
|
||||||
|
|
||||||
#endif /* __UTIL_MATH_H__ */
|
#endif /* __UTIL_MATH_H__ */
|
||||||
|
@ -302,6 +302,140 @@ ccl_device bool ray_quad_intersect(float3 ray_P,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Find the ray segment that lies in the same side as the normal `N` of the plane.
|
||||||
|
* `P` is the vector pointing from any point on the plane to the ray origin. */
|
||||||
|
ccl_device bool ray_plane_intersect(const float3 N,
|
||||||
|
const float3 P,
|
||||||
|
const float3 ray_D,
|
||||||
|
ccl_private float2 *t_range)
|
||||||
|
{
|
||||||
|
const float DN = dot(ray_D, N);
|
||||||
|
|
||||||
|
/* Distance from P to the plane. */
|
||||||
|
const float t = -dot(P, N) / DN;
|
||||||
|
|
||||||
|
/* Limit the range to the positive side. */
|
||||||
|
if (DN > 0.0f) {
|
||||||
|
t_range->x = fmaxf(t_range->x, t);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
t_range->y = fminf(t_range->y, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return t_range->x < t_range->y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the ray segment inside an axis-aligned bounding box. */
|
||||||
|
ccl_device bool ray_aabb_intersect(const float3 bbox_min,
|
||||||
|
const float3 bbox_max,
|
||||||
|
const float3 ray_P,
|
||||||
|
const float3 ray_D,
|
||||||
|
ccl_private float2 *t_range)
|
||||||
|
{
|
||||||
|
const float3 inv_ray_D = rcp(ray_D);
|
||||||
|
|
||||||
|
/* Absolute distances to lower and upper box coordinates; */
|
||||||
|
const float3 t_lower = (bbox_min - ray_P) * inv_ray_D;
|
||||||
|
const float3 t_upper = (bbox_max - ray_P) * inv_ray_D;
|
||||||
|
|
||||||
|
/* The four t-intervals (for x-/y-/z-slabs, and ray p(t)). */
|
||||||
|
const float4 tmins = float3_to_float4(min(t_lower, t_upper), t_range->x);
|
||||||
|
const float4 tmaxes = float3_to_float4(max(t_lower, t_upper), t_range->y);
|
||||||
|
|
||||||
|
/* Max of mins and min of maxes. */
|
||||||
|
const float tmin = reduce_max(tmins);
|
||||||
|
const float tmax = reduce_min(tmaxes);
|
||||||
|
|
||||||
|
*t_range = make_float2(tmin, tmax);
|
||||||
|
|
||||||
|
return tmin < tmax;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the segment of a ray defined by P + D * t that lies inside a cylinder defined by
|
||||||
|
* (x / len_u)^2 + (y / len_v)^2 = 1. */
|
||||||
|
ccl_device_inline bool ray_infinite_cylinder_intersect(const float3 P,
|
||||||
|
const float3 D,
|
||||||
|
const float len_u,
|
||||||
|
const float len_v,
|
||||||
|
ccl_private float2 *t_range)
|
||||||
|
{
|
||||||
|
/* Convert to a 2D problem. */
|
||||||
|
const float2 inv_len = 1.0f / make_float2(len_u, len_v);
|
||||||
|
float2 P_proj = float3_to_float2(P) * inv_len;
|
||||||
|
const float2 D_proj = float3_to_float2(D) * inv_len;
|
||||||
|
|
||||||
|
/* Solve quadratic equation a*t^2 + 2b*t + c = 0. */
|
||||||
|
const float a = dot(D_proj, D_proj);
|
||||||
|
float b = dot(P_proj, D_proj);
|
||||||
|
|
||||||
|
/* Move ray origin closer to the cylinder to prevent precision issue when the ray is far away. */
|
||||||
|
const float t_mid = -b / a;
|
||||||
|
P_proj += D_proj * t_mid;
|
||||||
|
|
||||||
|
/* Recompute b from the shifted origin. */
|
||||||
|
b = dot(P_proj, D_proj);
|
||||||
|
const float c = dot(P_proj, P_proj) - 1.0f;
|
||||||
|
|
||||||
|
float tmin, tmax;
|
||||||
|
const bool valid = solve_quadratic(a, 2.0f * b, c, tmin, tmax);
|
||||||
|
|
||||||
|
return valid && intervals_intersect(t_range, make_float2(tmin, tmax) + t_mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* *
|
||||||
|
* Find the ray segment inside a single-sided cone.
|
||||||
|
*
|
||||||
|
* \param axis: a unit-length direction around which the cone has a circular symmetry
|
||||||
|
* \param P: the vector pointing from the cone apex to the ray origin
|
||||||
|
* \param D: the direction of the ray, does not need to have unit-length
|
||||||
|
* \param cos_angle_sq: `sqr(cos(half_aperture_of_the_cone))`
|
||||||
|
* \param t_range: the lower and upper bounds between which the ray lies inside the cone
|
||||||
|
* \return whether the intersection exists and is in the provided range
|
||||||
|
*
|
||||||
|
* See https://www.geometrictools.com/Documentation/IntersectionLineCone.pdf for illustration
|
||||||
|
*/
|
||||||
|
ccl_device_inline bool ray_cone_intersect(const float3 axis,
|
||||||
|
const float3 P,
|
||||||
|
float3 D,
|
||||||
|
const float cos_angle_sq,
|
||||||
|
ccl_private float2 *t_range)
|
||||||
|
{
|
||||||
|
if (cos_angle_sq < 1e-4f) {
|
||||||
|
/* The cone is nearly a plane. */
|
||||||
|
return ray_plane_intersect(axis, P, D, t_range);
|
||||||
|
}
|
||||||
|
|
||||||
|
const float inv_len = inversesqrtf(len_squared(D));
|
||||||
|
D *= inv_len;
|
||||||
|
|
||||||
|
const float AD = dot(axis, D);
|
||||||
|
const float AP = dot(axis, P);
|
||||||
|
|
||||||
|
const float a = sqr(AD) - cos_angle_sq;
|
||||||
|
const float b = 2.0f * (AD * AP - cos_angle_sq * dot(D, P));
|
||||||
|
const float c = sqr(AP) - cos_angle_sq * dot(P, P);
|
||||||
|
|
||||||
|
float tmin = 0.0f, tmax = FLT_MAX;
|
||||||
|
bool valid = solve_quadratic(a, b, c, tmin, tmax);
|
||||||
|
|
||||||
|
/* Check if the intersections are in the same hemisphere as the cone. */
|
||||||
|
const bool tmin_valid = AP + tmin * AD > 0.0f;
|
||||||
|
const bool tmax_valid = AP + tmax * AD > 0.0f;
|
||||||
|
|
||||||
|
valid &= (tmin_valid || tmax_valid);
|
||||||
|
|
||||||
|
if (!tmax_valid) {
|
||||||
|
tmax = tmin;
|
||||||
|
tmin = 0.0f;
|
||||||
|
}
|
||||||
|
else if (!tmin_valid) {
|
||||||
|
tmin = tmax;
|
||||||
|
tmax = FLT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid && intervals_intersect(t_range, make_float2(tmin, tmax) * inv_len);
|
||||||
|
}
|
||||||
|
|
||||||
CCL_NAMESPACE_END
|
CCL_NAMESPACE_END
|
||||||
|
|
||||||
#endif /* __UTIL_MATH_INTERSECT_H__ */
|
#endif /* __UTIL_MATH_INTERSECT_H__ */
|
||||||
|
@ -161,6 +161,17 @@ ccl_device_inline Transform make_transform(float a,
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ccl_device_inline Transform make_transform(const float3 x, const float3 y, const float3 z)
|
||||||
|
{
|
||||||
|
Transform t;
|
||||||
|
|
||||||
|
t.x = float3_to_float4(x, 0.0f);
|
||||||
|
t.y = float3_to_float4(y, 0.0f);
|
||||||
|
t.z = float3_to_float4(z, 0.0f);
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
ccl_device_inline Transform euler_to_transform(const float3 euler)
|
ccl_device_inline Transform euler_to_transform(const float3 euler)
|
||||||
{
|
{
|
||||||
float cx = cosf(euler.x);
|
float cx = cosf(euler.x);
|
||||||
|
@ -1886,6 +1886,30 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
|
|||||||
gwl_window_state_set(window_, state);
|
gwl_window_state_set(window_, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* NOTE(@ideasman42): Round trips are important before committing.
|
||||||
|
* This is needed because setting the state is likely to resize the window
|
||||||
|
* (in the case of maximized & full-screen), "normal" windows may still be resized when
|
||||||
|
* they are too large or with tiling window-managers.
|
||||||
|
*
|
||||||
|
* The additional updates allow for the actual size to be configured by the window manager
|
||||||
|
* which is read back before committing the surface. This avoids displaying the buffer
|
||||||
|
* before it's resized (avoiding flickering).
|
||||||
|
*
|
||||||
|
* Without the round-trip here:
|
||||||
|
* - The window will be created and this function will return using the requested buffer size,
|
||||||
|
* instead of the window size which ends up being used (causing a visible flicker).
|
||||||
|
* This has the down side that Blender's internal window state has the outdated size
|
||||||
|
* which then gets immediately resized, causing a noticeable glitch.
|
||||||
|
* - The window decorations will be displayed at the wrong size before refreshing
|
||||||
|
* at the new size.
|
||||||
|
* - On GNOME-Shell 46 shows the previous buffer-size under some conditions, see #119871.
|
||||||
|
* - 2x updates are needed for RIVER & HYPRLAND.
|
||||||
|
*/
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
wl_display_flush(system->wl_display_get());
|
||||||
|
wl_display_dispatch(system->wl_display_get());
|
||||||
|
}
|
||||||
|
|
||||||
/* Commit after setting the buffer.
|
/* Commit after setting the buffer.
|
||||||
* While postponing until after the buffer drawing is context is set
|
* While postponing until after the buffer drawing is context is set
|
||||||
* isn't essential, it reduces flickering. */
|
* isn't essential, it reduces flickering. */
|
||||||
|
@ -158,7 +158,7 @@ class ARMATURE_MT_collection_tree_context_menu(Menu):
|
|||||||
# editable or not. That means this menu has to do the disabling for it.
|
# editable or not. That means this menu has to do the disabling for it.
|
||||||
sub = layout.column()
|
sub = layout.column()
|
||||||
sub.enabled = not active_bcoll_is_locked
|
sub.enabled = not active_bcoll_is_locked
|
||||||
sub.operator("armature.collection_add", text="Add Child Collection")
|
sub.operator("armature.collection_add", text="Add Bone Collection")
|
||||||
sub.operator("armature.collection_remove")
|
sub.operator("armature.collection_remove")
|
||||||
sub.operator("armature.collection_remove_unused", text="Remove Unused Collections")
|
sub.operator("armature.collection_remove_unused", text="Remove Unused Collections")
|
||||||
|
|
||||||
|
@ -2187,6 +2187,7 @@ class VIEW3D_MT_paint_grease_pencil(Menu):
|
|||||||
layout.separator()
|
layout.separator()
|
||||||
|
|
||||||
layout.menu("VIEW3D_MT_edit_greasepencil_showhide")
|
layout.menu("VIEW3D_MT_edit_greasepencil_showhide")
|
||||||
|
layout.menu("VIEW3D_MT_edit_greasepencil_cleanup")
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
|
|
||||||
@ -5803,6 +5804,15 @@ class VIEW3D_MT_edit_greasepencil_showhide(Menu):
|
|||||||
layout.operator("grease_pencil.layer_hide", text="Hide Inactive Layers").unselected = True
|
layout.operator("grease_pencil.layer_hide", text="Hide Inactive Layers").unselected = True
|
||||||
|
|
||||||
|
|
||||||
|
class VIEW3D_MT_edit_greasepencil_cleanup(Menu):
|
||||||
|
bl_label = "Cleanup"
|
||||||
|
|
||||||
|
def draw(self, _context):
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
layout.operator("grease_pencil.clean_loose")
|
||||||
|
|
||||||
|
|
||||||
class VIEW3D_MT_edit_greasepencil(Menu):
|
class VIEW3D_MT_edit_greasepencil(Menu):
|
||||||
bl_label = "Grease Pencil"
|
bl_label = "Grease Pencil"
|
||||||
|
|
||||||
@ -5828,7 +5838,7 @@ class VIEW3D_MT_edit_greasepencil(Menu):
|
|||||||
|
|
||||||
layout.menu("VIEW3D_MT_edit_greasepencil_showhide")
|
layout.menu("VIEW3D_MT_edit_greasepencil_showhide")
|
||||||
layout.operator_menu_enum("grease_pencil.separate", "mode", text="Separate")
|
layout.operator_menu_enum("grease_pencil.separate", "mode", text="Separate")
|
||||||
layout.operator("grease_pencil.clean_loose")
|
layout.menu("VIEW3D_MT_edit_greasepencil_cleanup")
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
|
|
||||||
@ -7526,14 +7536,14 @@ class VIEW3D_PT_snapping(Panel):
|
|||||||
layout = self.layout
|
layout = self.layout
|
||||||
col = layout.column()
|
col = layout.column()
|
||||||
|
|
||||||
col.label(text="Snap With")
|
col.label(text="Snap Base")
|
||||||
row = col.row(align=True)
|
row = col.row(align=True)
|
||||||
row.prop(tool_settings, "snap_target", expand=True)
|
row.prop(tool_settings, "snap_target", expand=True)
|
||||||
|
|
||||||
col.label(text="Snap To")
|
col.label(text="Snap Target")
|
||||||
col.prop(tool_settings, "snap_elements_base", expand=True)
|
col.prop(tool_settings, "snap_elements_base", expand=True)
|
||||||
|
|
||||||
col.label(text="Snap Individual Elements To")
|
col.label(text="Snap Target for Individual Elements")
|
||||||
col.prop(tool_settings, "snap_elements_individual", expand=True)
|
col.prop(tool_settings, "snap_elements_individual", expand=True)
|
||||||
|
|
||||||
col.separator()
|
col.separator()
|
||||||
@ -8988,6 +8998,7 @@ classes = (
|
|||||||
VIEW3D_MT_edit_gpencil_delete,
|
VIEW3D_MT_edit_gpencil_delete,
|
||||||
VIEW3D_MT_edit_gpencil_showhide,
|
VIEW3D_MT_edit_gpencil_showhide,
|
||||||
VIEW3D_MT_edit_greasepencil_showhide,
|
VIEW3D_MT_edit_greasepencil_showhide,
|
||||||
|
VIEW3D_MT_edit_greasepencil_cleanup,
|
||||||
VIEW3D_MT_weight_gpencil,
|
VIEW3D_MT_weight_gpencil,
|
||||||
VIEW3D_MT_gpencil_animation,
|
VIEW3D_MT_gpencil_animation,
|
||||||
VIEW3D_MT_gpencil_simplify,
|
VIEW3D_MT_gpencil_simplify,
|
||||||
|
@ -253,54 +253,6 @@ static AttributeIDRef attribute_id_from_custom_data_layer(const CustomDataLayer
|
|||||||
return layer.name;
|
return layer.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data,
|
|
||||||
const eCustomDataType data_type,
|
|
||||||
const int domain_num,
|
|
||||||
const AttributeInit &initializer)
|
|
||||||
{
|
|
||||||
switch (initializer.type) {
|
|
||||||
case AttributeInit::Type::Construct: {
|
|
||||||
void *data = CustomData_add_layer(&custom_data, data_type, CD_CONSTRUCT, domain_num);
|
|
||||||
return data != nullptr;
|
|
||||||
}
|
|
||||||
case AttributeInit::Type::DefaultValue: {
|
|
||||||
void *data = CustomData_add_layer(&custom_data, data_type, CD_SET_DEFAULT, domain_num);
|
|
||||||
return data != nullptr;
|
|
||||||
}
|
|
||||||
case AttributeInit::Type::VArray: {
|
|
||||||
void *data = CustomData_add_layer(&custom_data, data_type, CD_CONSTRUCT, domain_num);
|
|
||||||
if (data == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const GVArray &varray = static_cast<const AttributeInitVArray &>(initializer).varray;
|
|
||||||
varray.materialize_to_uninitialized(varray.index_range(), data);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case AttributeInit::Type::MoveArray: {
|
|
||||||
void *src_data = static_cast<const AttributeInitMoveArray &>(initializer).data;
|
|
||||||
const void *stored_data = CustomData_add_layer_with_data(
|
|
||||||
&custom_data, data_type, src_data, domain_num, nullptr);
|
|
||||||
if (stored_data == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (stored_data != src_data) {
|
|
||||||
MEM_freeN(src_data);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case AttributeInit::Type::Shared: {
|
|
||||||
const AttributeInitShared &init = static_cast<const AttributeInitShared &>(initializer);
|
|
||||||
const void *stored_data = CustomData_add_layer_with_data(
|
|
||||||
&custom_data, data_type, const_cast<void *>(init.data), domain_num, init.sharing_info);
|
|
||||||
return stored_data != nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BLI_assert_unreachable();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *add_generic_custom_data_layer(CustomData &custom_data,
|
static void *add_generic_custom_data_layer(CustomData &custom_data,
|
||||||
const eCustomDataType data_type,
|
const eCustomDataType data_type,
|
||||||
const eCDAllocType alloctype,
|
const eCDAllocType alloctype,
|
||||||
@ -393,10 +345,7 @@ static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer,
|
|||||||
|
|
||||||
bool BuiltinCustomDataLayerProvider::layer_exists(const CustomData &custom_data) const
|
bool BuiltinCustomDataLayerProvider::layer_exists(const CustomData &custom_data) const
|
||||||
{
|
{
|
||||||
if (stored_as_named_attribute_) {
|
return CustomData_get_named_layer_index(&custom_data, data_type_, name_) != -1;
|
||||||
return CustomData_get_named_layer_index(&custom_data, stored_type_, name_) != -1;
|
|
||||||
}
|
|
||||||
return CustomData_has_layer(&custom_data, stored_type_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GAttributeReader BuiltinCustomDataLayerProvider::try_get_for_read(const void *owner) const
|
GAttributeReader BuiltinCustomDataLayerProvider::try_get_for_read(const void *owner) const
|
||||||
@ -416,13 +365,7 @@ GAttributeReader BuiltinCustomDataLayerProvider::try_get_for_read(const void *ow
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
int index;
|
const int index = CustomData_get_named_layer_index(custom_data, data_type_, name_);
|
||||||
if (stored_as_named_attribute_) {
|
|
||||||
index = CustomData_get_named_layer_index(custom_data, stored_type_, name_);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
index = CustomData_get_layer_index(custom_data, stored_type_);
|
|
||||||
}
|
|
||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -452,13 +395,7 @@ GAttributeWriter BuiltinCustomDataLayerProvider::try_get_for_write(void *owner)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void *data = nullptr;
|
void *data = CustomData_get_layer_named_for_write(custom_data, data_type_, name_, element_num);
|
||||||
if (stored_as_named_attribute_) {
|
|
||||||
data = CustomData_get_layer_named_for_write(custom_data, stored_type_, name_, element_num);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
data = CustomData_get_layer_for_write(custom_data, stored_type_, element_num);
|
|
||||||
}
|
|
||||||
if (data == nullptr) {
|
if (data == nullptr) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -475,57 +412,42 @@ bool BuiltinCustomDataLayerProvider::try_delete(void *owner) const
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto update = [&]() {
|
const int element_num = custom_data_access_.get_element_num(owner);
|
||||||
|
if (CustomData_free_layer_named(custom_data, name_, element_num)) {
|
||||||
if (update_on_change_ != nullptr) {
|
if (update_on_change_ != nullptr) {
|
||||||
update_on_change_(owner);
|
update_on_change_(owner);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const int element_num = custom_data_access_.get_element_num(owner);
|
|
||||||
if (stored_as_named_attribute_) {
|
|
||||||
if (CustomData_free_layer_named(custom_data, name_, element_num)) {
|
|
||||||
update();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int layer_index = CustomData_get_layer_index(custom_data, stored_type_);
|
|
||||||
if (CustomData_free_layer(custom_data, stored_type_, element_num, layer_index)) {
|
|
||||||
update();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BuiltinCustomDataLayerProvider::try_create(void *owner,
|
bool BuiltinCustomDataLayerProvider::try_create(void *owner,
|
||||||
const AttributeInit &initializer) const
|
const AttributeInit &initializer) const
|
||||||
{
|
{
|
||||||
if (createable_ != Creatable) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
CustomData *custom_data = custom_data_access_.get_custom_data(owner);
|
CustomData *custom_data = custom_data_access_.get_custom_data(owner);
|
||||||
if (custom_data == nullptr) {
|
if (custom_data == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int element_num = custom_data_access_.get_element_num(owner);
|
const int element_num = custom_data_access_.get_element_num(owner);
|
||||||
if (stored_as_named_attribute_) {
|
if (CustomData_has_layer_named(custom_data, data_type_, name_)) {
|
||||||
if (CustomData_has_layer_named(custom_data, data_type_, name_)) {
|
|
||||||
/* Exists already. */
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return add_custom_data_layer_from_attribute_init(
|
|
||||||
name_, *custom_data, stored_type_, element_num, initializer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CustomData_get_layer(custom_data, stored_type_) != nullptr) {
|
|
||||||
/* Exists already. */
|
/* Exists already. */
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return add_builtin_type_custom_data_layer_from_init(
|
if (add_custom_data_layer_from_attribute_init(
|
||||||
*custom_data, stored_type_, element_num, initializer);
|
name_, *custom_data, data_type_, element_num, initializer))
|
||||||
|
{
|
||||||
|
if (initializer.type != AttributeInit::Type::Construct) {
|
||||||
|
/* Avoid calling update function when values are not initialized. In that case
|
||||||
|
* values must be set elsewhere anyway, which will cause a separate update tag. */
|
||||||
|
if (update_on_change_ != nullptr) {
|
||||||
|
update_on_change_(owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BuiltinCustomDataLayerProvider::exists(const void *owner) const
|
bool BuiltinCustomDataLayerProvider::exists(const void *owner) const
|
||||||
@ -534,10 +456,7 @@ bool BuiltinCustomDataLayerProvider::exists(const void *owner) const
|
|||||||
if (custom_data == nullptr) {
|
if (custom_data == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (stored_as_named_attribute_) {
|
return CustomData_has_layer_named(custom_data, data_type_, name_);
|
||||||
return CustomData_has_layer_named(custom_data, stored_type_, name_);
|
|
||||||
}
|
|
||||||
return CustomData_get_layer(custom_data, stored_type_) != nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GAttributeReader CustomDataAttributeProvider::try_get_for_read(
|
GAttributeReader CustomDataAttributeProvider::try_get_for_read(
|
||||||
|
@ -31,14 +31,10 @@ struct CustomDataAccessInfo {
|
|||||||
* A #BuiltinAttributeProvider is responsible for exactly one attribute on a geometry component.
|
* A #BuiltinAttributeProvider is responsible for exactly one attribute on a geometry component.
|
||||||
* The attribute is identified by its name and has a fixed domain and type. Builtin attributes do
|
* The attribute is identified by its name and has a fixed domain and type. Builtin attributes do
|
||||||
* not follow the same loose rules as other attributes, because they are mapped to internal
|
* not follow the same loose rules as other attributes, because they are mapped to internal
|
||||||
* "legacy" data structures. For example, some builtin attributes cannot be deleted. */
|
* "legacy" data structures. For example, some builtin attributes cannot be deleted.
|
||||||
|
*/
|
||||||
class BuiltinAttributeProvider {
|
class BuiltinAttributeProvider {
|
||||||
public:
|
public:
|
||||||
/* Some utility enums to avoid hard to read booleans in function calls. */
|
|
||||||
enum CreatableEnum {
|
|
||||||
Creatable,
|
|
||||||
NonCreatable,
|
|
||||||
};
|
|
||||||
enum DeletableEnum {
|
enum DeletableEnum {
|
||||||
Deletable,
|
Deletable,
|
||||||
NonDeletable,
|
NonDeletable,
|
||||||
@ -48,7 +44,6 @@ class BuiltinAttributeProvider {
|
|||||||
const std::string name_;
|
const std::string name_;
|
||||||
const AttrDomain domain_;
|
const AttrDomain domain_;
|
||||||
const eCustomDataType data_type_;
|
const eCustomDataType data_type_;
|
||||||
const CreatableEnum createable_;
|
|
||||||
const DeletableEnum deletable_;
|
const DeletableEnum deletable_;
|
||||||
const AttributeValidator validator_;
|
const AttributeValidator validator_;
|
||||||
|
|
||||||
@ -56,13 +51,11 @@ class BuiltinAttributeProvider {
|
|||||||
BuiltinAttributeProvider(std::string name,
|
BuiltinAttributeProvider(std::string name,
|
||||||
const AttrDomain domain,
|
const AttrDomain domain,
|
||||||
const eCustomDataType data_type,
|
const eCustomDataType data_type,
|
||||||
const CreatableEnum createable,
|
|
||||||
const DeletableEnum deletable,
|
const DeletableEnum deletable,
|
||||||
AttributeValidator validator = {})
|
AttributeValidator validator = {})
|
||||||
: name_(std::move(name)),
|
: name_(std::move(name)),
|
||||||
domain_(domain),
|
domain_(domain),
|
||||||
data_type_(data_type),
|
data_type_(data_type),
|
||||||
createable_(createable),
|
|
||||||
deletable_(deletable),
|
deletable_(deletable),
|
||||||
validator_(validator)
|
validator_(validator)
|
||||||
{
|
{
|
||||||
@ -174,27 +167,21 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
|
|||||||
*/
|
*/
|
||||||
class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
|
class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
|
||||||
using UpdateOnChange = void (*)(void *owner);
|
using UpdateOnChange = void (*)(void *owner);
|
||||||
const eCustomDataType stored_type_;
|
|
||||||
const CustomDataAccessInfo custom_data_access_;
|
const CustomDataAccessInfo custom_data_access_;
|
||||||
const UpdateOnChange update_on_change_;
|
const UpdateOnChange update_on_change_;
|
||||||
bool stored_as_named_attribute_;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BuiltinCustomDataLayerProvider(std::string attribute_name,
|
BuiltinCustomDataLayerProvider(std::string attribute_name,
|
||||||
const AttrDomain domain,
|
const AttrDomain domain,
|
||||||
const eCustomDataType attribute_type,
|
const eCustomDataType data_type,
|
||||||
const eCustomDataType stored_type,
|
|
||||||
const CreatableEnum creatable,
|
|
||||||
const DeletableEnum deletable,
|
const DeletableEnum deletable,
|
||||||
const CustomDataAccessInfo custom_data_access,
|
const CustomDataAccessInfo custom_data_access,
|
||||||
const UpdateOnChange update_on_write,
|
const UpdateOnChange update_on_change,
|
||||||
const AttributeValidator validator = {})
|
const AttributeValidator validator = {})
|
||||||
: BuiltinAttributeProvider(
|
: BuiltinAttributeProvider(
|
||||||
std::move(attribute_name), domain, attribute_type, creatable, deletable, validator),
|
std::move(attribute_name), domain, data_type, deletable, validator),
|
||||||
stored_type_(stored_type),
|
|
||||||
custom_data_access_(custom_data_access),
|
custom_data_access_(custom_data_access),
|
||||||
update_on_change_(update_on_write),
|
update_on_change_(update_on_change)
|
||||||
stored_as_named_attribute_(data_type_ == stored_type_)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -972,9 +972,14 @@ static std::shared_ptr<DictionaryValue> serialize_geometry_set(const GeometrySet
|
|||||||
|
|
||||||
auto io_references = io_instances->append_array("references");
|
auto io_references = io_instances->append_array("references");
|
||||||
for (const InstanceReference &reference : instances.references()) {
|
for (const InstanceReference &reference : instances.references()) {
|
||||||
BLI_assert(reference.type() == InstanceReference::Type::GeometrySet);
|
if (reference.type() == InstanceReference::Type::GeometrySet) {
|
||||||
io_references->append(
|
const GeometrySet &geometry = reference.geometry_set();
|
||||||
serialize_geometry_set(reference.geometry_set(), blob_writer, blob_sharing));
|
io_references->append(serialize_geometry_set(geometry, blob_writer, blob_sharing));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* TODO: Support serializing object and collection references. */
|
||||||
|
io_references->append(serialize_geometry_set({}, blob_writer, blob_sharing));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto io_attributes = serialize_attributes(
|
auto io_attributes = serialize_attributes(
|
||||||
|
@ -58,6 +58,8 @@ CurvesGeometry::CurvesGeometry() : CurvesGeometry(0, 0) {}
|
|||||||
|
|
||||||
CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num)
|
CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num)
|
||||||
{
|
{
|
||||||
|
this->runtime = MEM_new<CurvesGeometryRuntime>(__func__);
|
||||||
|
|
||||||
this->point_num = point_num;
|
this->point_num = point_num;
|
||||||
this->curve_num = curve_num;
|
this->curve_num = curve_num;
|
||||||
CustomData_reset(&this->point_data);
|
CustomData_reset(&this->point_data);
|
||||||
@ -67,8 +69,6 @@ CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num)
|
|||||||
this->attributes_for_write().add<float3>(
|
this->attributes_for_write().add<float3>(
|
||||||
"position", AttrDomain::Point, AttributeInitConstruct());
|
"position", AttrDomain::Point, AttributeInitConstruct());
|
||||||
|
|
||||||
this->runtime = MEM_new<CurvesGeometryRuntime>(__func__);
|
|
||||||
|
|
||||||
if (curve_num > 0) {
|
if (curve_num > 0) {
|
||||||
this->curve_offsets = static_cast<int *>(
|
this->curve_offsets = static_cast<int *>(
|
||||||
MEM_malloc_arrayN(this->curve_num + 1, sizeof(int), __func__));
|
MEM_malloc_arrayN(this->curve_num + 1, sizeof(int), __func__));
|
||||||
|
@ -91,7 +91,7 @@ void BKE_editmesh_looptris_calc_with_partial_ex(BMEditMesh *em,
|
|||||||
const BMeshCalcTessellation_Params *params)
|
const BMeshCalcTessellation_Params *params)
|
||||||
{
|
{
|
||||||
BLI_assert(em->looptris.size() == poly_to_tri_count(em->bm->totface, em->bm->totloop));
|
BLI_assert(em->looptris.size() == poly_to_tri_count(em->bm->totface, em->bm->totloop));
|
||||||
BLI_assert(!em->looptris.is_empty());
|
BLI_assert(!(em->bm->totface && em->looptris.is_empty()));
|
||||||
|
|
||||||
BM_mesh_calc_tessellation_with_partial_ex(em->bm, em->looptris, bmpinfo, params);
|
BM_mesh_calc_tessellation_with_partial_ex(em->bm, em->looptris, bmpinfo, params);
|
||||||
}
|
}
|
||||||
|
@ -476,8 +476,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
|||||||
static BuiltinCustomDataLayerProvider position("position",
|
static BuiltinCustomDataLayerProvider position("position",
|
||||||
AttrDomain::Point,
|
AttrDomain::Point,
|
||||||
CD_PROP_FLOAT3,
|
CD_PROP_FLOAT3,
|
||||||
CD_PROP_FLOAT3,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::NonDeletable,
|
BuiltinAttributeProvider::NonDeletable,
|
||||||
point_access,
|
point_access,
|
||||||
tag_component_positions_changed);
|
tag_component_positions_changed);
|
||||||
@ -485,8 +483,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
|||||||
static BuiltinCustomDataLayerProvider radius("radius",
|
static BuiltinCustomDataLayerProvider radius("radius",
|
||||||
AttrDomain::Point,
|
AttrDomain::Point,
|
||||||
CD_PROP_FLOAT,
|
CD_PROP_FLOAT,
|
||||||
CD_PROP_FLOAT,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
point_access,
|
point_access,
|
||||||
tag_component_radii_changed);
|
tag_component_radii_changed);
|
||||||
@ -494,8 +490,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
|||||||
static BuiltinCustomDataLayerProvider id("id",
|
static BuiltinCustomDataLayerProvider id("id",
|
||||||
AttrDomain::Point,
|
AttrDomain::Point,
|
||||||
CD_PROP_INT32,
|
CD_PROP_INT32,
|
||||||
CD_PROP_INT32,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
point_access,
|
point_access,
|
||||||
nullptr);
|
nullptr);
|
||||||
@ -503,8 +497,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
|||||||
static BuiltinCustomDataLayerProvider tilt("tilt",
|
static BuiltinCustomDataLayerProvider tilt("tilt",
|
||||||
AttrDomain::Point,
|
AttrDomain::Point,
|
||||||
CD_PROP_FLOAT,
|
CD_PROP_FLOAT,
|
||||||
CD_PROP_FLOAT,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
point_access,
|
point_access,
|
||||||
tag_component_normals_changed);
|
tag_component_normals_changed);
|
||||||
@ -512,8 +504,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
|||||||
static BuiltinCustomDataLayerProvider handle_right("handle_right",
|
static BuiltinCustomDataLayerProvider handle_right("handle_right",
|
||||||
AttrDomain::Point,
|
AttrDomain::Point,
|
||||||
CD_PROP_FLOAT3,
|
CD_PROP_FLOAT3,
|
||||||
CD_PROP_FLOAT3,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
point_access,
|
point_access,
|
||||||
tag_component_positions_changed);
|
tag_component_positions_changed);
|
||||||
@ -521,8 +511,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
|||||||
static BuiltinCustomDataLayerProvider handle_left("handle_left",
|
static BuiltinCustomDataLayerProvider handle_left("handle_left",
|
||||||
AttrDomain::Point,
|
AttrDomain::Point,
|
||||||
CD_PROP_FLOAT3,
|
CD_PROP_FLOAT3,
|
||||||
CD_PROP_FLOAT3,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
point_access,
|
point_access,
|
||||||
tag_component_positions_changed);
|
tag_component_positions_changed);
|
||||||
@ -536,8 +524,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
|||||||
static BuiltinCustomDataLayerProvider handle_type_right("handle_type_right",
|
static BuiltinCustomDataLayerProvider handle_type_right("handle_type_right",
|
||||||
AttrDomain::Point,
|
AttrDomain::Point,
|
||||||
CD_PROP_INT8,
|
CD_PROP_INT8,
|
||||||
CD_PROP_INT8,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
point_access,
|
point_access,
|
||||||
tag_component_topology_changed,
|
tag_component_topology_changed,
|
||||||
@ -546,8 +532,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
|||||||
static BuiltinCustomDataLayerProvider handle_type_left("handle_type_left",
|
static BuiltinCustomDataLayerProvider handle_type_left("handle_type_left",
|
||||||
AttrDomain::Point,
|
AttrDomain::Point,
|
||||||
CD_PROP_INT8,
|
CD_PROP_INT8,
|
||||||
CD_PROP_INT8,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
point_access,
|
point_access,
|
||||||
tag_component_topology_changed,
|
tag_component_topology_changed,
|
||||||
@ -556,21 +540,17 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
|||||||
static BuiltinCustomDataLayerProvider nurbs_weight("nurbs_weight",
|
static BuiltinCustomDataLayerProvider nurbs_weight("nurbs_weight",
|
||||||
AttrDomain::Point,
|
AttrDomain::Point,
|
||||||
CD_PROP_FLOAT,
|
CD_PROP_FLOAT,
|
||||||
CD_PROP_FLOAT,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
point_access,
|
point_access,
|
||||||
tag_component_positions_changed);
|
tag_component_positions_changed);
|
||||||
|
|
||||||
static const auto nurbs_order_clamp = mf::build::SI1_SO<int8_t, int8_t>(
|
static const auto nurbs_order_clamp = mf::build::SI1_SO<int8_t, int8_t>(
|
||||||
"NURBS Order Validate",
|
"NURBS Order Validate",
|
||||||
[](int8_t value) { return std::max<int8_t>(value, 0); },
|
[](int8_t value) { return std::max<int8_t>(value, 1); },
|
||||||
mf::build::exec_presets::AllSpanOrSingle());
|
mf::build::exec_presets::AllSpanOrSingle());
|
||||||
static BuiltinCustomDataLayerProvider nurbs_order("nurbs_order",
|
static BuiltinCustomDataLayerProvider nurbs_order("nurbs_order",
|
||||||
AttrDomain::Curve,
|
AttrDomain::Curve,
|
||||||
CD_PROP_INT8,
|
CD_PROP_INT8,
|
||||||
CD_PROP_INT8,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
curve_access,
|
curve_access,
|
||||||
tag_component_topology_changed,
|
tag_component_topology_changed,
|
||||||
@ -585,8 +565,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
|||||||
static BuiltinCustomDataLayerProvider normal_mode("normal_mode",
|
static BuiltinCustomDataLayerProvider normal_mode("normal_mode",
|
||||||
AttrDomain::Curve,
|
AttrDomain::Curve,
|
||||||
CD_PROP_INT8,
|
CD_PROP_INT8,
|
||||||
CD_PROP_INT8,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
curve_access,
|
curve_access,
|
||||||
tag_component_normals_changed,
|
tag_component_normals_changed,
|
||||||
@ -595,8 +573,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
|||||||
static BuiltinCustomDataLayerProvider custom_normal("custom_normal",
|
static BuiltinCustomDataLayerProvider custom_normal("custom_normal",
|
||||||
AttrDomain::Point,
|
AttrDomain::Point,
|
||||||
CD_PROP_FLOAT3,
|
CD_PROP_FLOAT3,
|
||||||
CD_PROP_FLOAT3,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
point_access,
|
point_access,
|
||||||
tag_component_normals_changed);
|
tag_component_normals_changed);
|
||||||
@ -610,8 +586,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
|||||||
static BuiltinCustomDataLayerProvider nurbs_knots_mode("knots_mode",
|
static BuiltinCustomDataLayerProvider nurbs_knots_mode("knots_mode",
|
||||||
AttrDomain::Curve,
|
AttrDomain::Curve,
|
||||||
CD_PROP_INT8,
|
CD_PROP_INT8,
|
||||||
CD_PROP_INT8,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
curve_access,
|
curve_access,
|
||||||
tag_component_topology_changed,
|
tag_component_topology_changed,
|
||||||
@ -626,8 +600,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
|||||||
static BuiltinCustomDataLayerProvider curve_type("curve_type",
|
static BuiltinCustomDataLayerProvider curve_type("curve_type",
|
||||||
AttrDomain::Curve,
|
AttrDomain::Curve,
|
||||||
CD_PROP_INT8,
|
CD_PROP_INT8,
|
||||||
CD_PROP_INT8,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
curve_access,
|
curve_access,
|
||||||
tag_component_curve_types_changed,
|
tag_component_curve_types_changed,
|
||||||
@ -640,8 +612,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
|||||||
static BuiltinCustomDataLayerProvider resolution("resolution",
|
static BuiltinCustomDataLayerProvider resolution("resolution",
|
||||||
AttrDomain::Curve,
|
AttrDomain::Curve,
|
||||||
CD_PROP_INT32,
|
CD_PROP_INT32,
|
||||||
CD_PROP_INT32,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
curve_access,
|
curve_access,
|
||||||
tag_component_topology_changed,
|
tag_component_topology_changed,
|
||||||
@ -650,8 +620,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
|||||||
static BuiltinCustomDataLayerProvider cyclic("cyclic",
|
static BuiltinCustomDataLayerProvider cyclic("cyclic",
|
||||||
AttrDomain::Curve,
|
AttrDomain::Curve,
|
||||||
CD_PROP_BOOL,
|
CD_PROP_BOOL,
|
||||||
CD_PROP_BOOL,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
curve_access,
|
curve_access,
|
||||||
tag_component_topology_changed);
|
tag_component_topology_changed);
|
||||||
|
@ -133,8 +133,6 @@ static ComponentAttributeProviders create_attribute_providers_for_instances()
|
|||||||
static BuiltinCustomDataLayerProvider id("id",
|
static BuiltinCustomDataLayerProvider id("id",
|
||||||
AttrDomain::Instance,
|
AttrDomain::Instance,
|
||||||
CD_PROP_INT32,
|
CD_PROP_INT32,
|
||||||
CD_PROP_INT32,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
instance_custom_data_access,
|
instance_custom_data_access,
|
||||||
nullptr);
|
nullptr);
|
||||||
@ -142,8 +140,6 @@ static ComponentAttributeProviders create_attribute_providers_for_instances()
|
|||||||
static BuiltinCustomDataLayerProvider instance_transform("instance_transform",
|
static BuiltinCustomDataLayerProvider instance_transform("instance_transform",
|
||||||
AttrDomain::Instance,
|
AttrDomain::Instance,
|
||||||
CD_PROP_FLOAT4X4,
|
CD_PROP_FLOAT4X4,
|
||||||
CD_PROP_FLOAT4X4,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::NonDeletable,
|
BuiltinAttributeProvider::NonDeletable,
|
||||||
instance_custom_data_access,
|
instance_custom_data_access,
|
||||||
nullptr);
|
nullptr);
|
||||||
@ -152,8 +148,6 @@ static ComponentAttributeProviders create_attribute_providers_for_instances()
|
|||||||
static BuiltinCustomDataLayerProvider reference_index(".reference_index",
|
static BuiltinCustomDataLayerProvider reference_index(".reference_index",
|
||||||
AttrDomain::Instance,
|
AttrDomain::Instance,
|
||||||
CD_PROP_INT32,
|
CD_PROP_INT32,
|
||||||
CD_PROP_INT32,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::NonDeletable,
|
BuiltinAttributeProvider::NonDeletable,
|
||||||
instance_custom_data_access,
|
instance_custom_data_access,
|
||||||
tag_component_reference_index_changed);
|
tag_component_reference_index_changed);
|
||||||
|
@ -1010,8 +1010,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
|||||||
static BuiltinCustomDataLayerProvider position("position",
|
static BuiltinCustomDataLayerProvider position("position",
|
||||||
AttrDomain::Point,
|
AttrDomain::Point,
|
||||||
CD_PROP_FLOAT3,
|
CD_PROP_FLOAT3,
|
||||||
CD_PROP_FLOAT3,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::NonDeletable,
|
BuiltinAttributeProvider::NonDeletable,
|
||||||
point_access,
|
point_access,
|
||||||
tag_component_positions_changed);
|
tag_component_positions_changed);
|
||||||
@ -1019,8 +1017,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
|||||||
static BuiltinCustomDataLayerProvider id("id",
|
static BuiltinCustomDataLayerProvider id("id",
|
||||||
AttrDomain::Point,
|
AttrDomain::Point,
|
||||||
CD_PROP_INT32,
|
CD_PROP_INT32,
|
||||||
CD_PROP_INT32,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
point_access,
|
point_access,
|
||||||
nullptr);
|
nullptr);
|
||||||
@ -1035,8 +1031,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
|||||||
static BuiltinCustomDataLayerProvider material_index("material_index",
|
static BuiltinCustomDataLayerProvider material_index("material_index",
|
||||||
AttrDomain::Face,
|
AttrDomain::Face,
|
||||||
CD_PROP_INT32,
|
CD_PROP_INT32,
|
||||||
CD_PROP_INT32,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
face_access,
|
face_access,
|
||||||
nullptr,
|
nullptr,
|
||||||
@ -1049,8 +1043,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
|||||||
static BuiltinCustomDataLayerProvider edge_verts(".edge_verts",
|
static BuiltinCustomDataLayerProvider edge_verts(".edge_verts",
|
||||||
AttrDomain::Edge,
|
AttrDomain::Edge,
|
||||||
CD_PROP_INT32_2D,
|
CD_PROP_INT32_2D,
|
||||||
CD_PROP_INT32_2D,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::NonDeletable,
|
BuiltinAttributeProvider::NonDeletable,
|
||||||
edge_access,
|
edge_access,
|
||||||
nullptr,
|
nullptr,
|
||||||
@ -1065,8 +1057,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
|||||||
static BuiltinCustomDataLayerProvider corner_vert(".corner_vert",
|
static BuiltinCustomDataLayerProvider corner_vert(".corner_vert",
|
||||||
AttrDomain::Corner,
|
AttrDomain::Corner,
|
||||||
CD_PROP_INT32,
|
CD_PROP_INT32,
|
||||||
CD_PROP_INT32,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::NonDeletable,
|
BuiltinAttributeProvider::NonDeletable,
|
||||||
corner_access,
|
corner_access,
|
||||||
nullptr,
|
nullptr,
|
||||||
@ -1074,8 +1064,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
|||||||
static BuiltinCustomDataLayerProvider corner_edge(".corner_edge",
|
static BuiltinCustomDataLayerProvider corner_edge(".corner_edge",
|
||||||
AttrDomain::Corner,
|
AttrDomain::Corner,
|
||||||
CD_PROP_INT32,
|
CD_PROP_INT32,
|
||||||
CD_PROP_INT32,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::NonDeletable,
|
BuiltinAttributeProvider::NonDeletable,
|
||||||
corner_access,
|
corner_access,
|
||||||
nullptr,
|
nullptr,
|
||||||
@ -1084,8 +1072,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
|||||||
static BuiltinCustomDataLayerProvider sharp_face("sharp_face",
|
static BuiltinCustomDataLayerProvider sharp_face("sharp_face",
|
||||||
AttrDomain::Face,
|
AttrDomain::Face,
|
||||||
CD_PROP_BOOL,
|
CD_PROP_BOOL,
|
||||||
CD_PROP_BOOL,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
face_access,
|
face_access,
|
||||||
tag_component_sharpness_changed);
|
tag_component_sharpness_changed);
|
||||||
@ -1093,8 +1079,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
|||||||
static BuiltinCustomDataLayerProvider sharp_edge("sharp_edge",
|
static BuiltinCustomDataLayerProvider sharp_edge("sharp_edge",
|
||||||
AttrDomain::Edge,
|
AttrDomain::Edge,
|
||||||
CD_PROP_BOOL,
|
CD_PROP_BOOL,
|
||||||
CD_PROP_BOOL,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
edge_access,
|
edge_access,
|
||||||
tag_component_sharpness_changed);
|
tag_component_sharpness_changed);
|
||||||
|
@ -147,24 +147,18 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
|
|||||||
static BuiltinCustomDataLayerProvider position("position",
|
static BuiltinCustomDataLayerProvider position("position",
|
||||||
AttrDomain::Point,
|
AttrDomain::Point,
|
||||||
CD_PROP_FLOAT3,
|
CD_PROP_FLOAT3,
|
||||||
CD_PROP_FLOAT3,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::NonDeletable,
|
BuiltinAttributeProvider::NonDeletable,
|
||||||
point_access,
|
point_access,
|
||||||
tag_component_positions_changed);
|
tag_component_positions_changed);
|
||||||
static BuiltinCustomDataLayerProvider radius("radius",
|
static BuiltinCustomDataLayerProvider radius("radius",
|
||||||
AttrDomain::Point,
|
AttrDomain::Point,
|
||||||
CD_PROP_FLOAT,
|
CD_PROP_FLOAT,
|
||||||
CD_PROP_FLOAT,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
point_access,
|
point_access,
|
||||||
tag_component_radius_changed);
|
tag_component_radius_changed);
|
||||||
static BuiltinCustomDataLayerProvider id("id",
|
static BuiltinCustomDataLayerProvider id("id",
|
||||||
AttrDomain::Point,
|
AttrDomain::Point,
|
||||||
CD_PROP_INT32,
|
CD_PROP_INT32,
|
||||||
CD_PROP_INT32,
|
|
||||||
BuiltinAttributeProvider::Creatable,
|
|
||||||
BuiltinAttributeProvider::Deletable,
|
BuiltinAttributeProvider::Deletable,
|
||||||
point_access,
|
point_access,
|
||||||
nullptr);
|
nullptr);
|
||||||
|
@ -62,11 +62,11 @@ static void pointcloud_init_data(ID *id)
|
|||||||
|
|
||||||
MEMCPY_STRUCT_AFTER(pointcloud, DNA_struct_default_get(PointCloud), id);
|
MEMCPY_STRUCT_AFTER(pointcloud, DNA_struct_default_get(PointCloud), id);
|
||||||
|
|
||||||
|
pointcloud->runtime = new blender::bke::PointCloudRuntime();
|
||||||
|
|
||||||
CustomData_reset(&pointcloud->pdata);
|
CustomData_reset(&pointcloud->pdata);
|
||||||
pointcloud->attributes_for_write().add<float3>(
|
pointcloud->attributes_for_write().add<float3>(
|
||||||
"position", blender::bke::AttrDomain::Point, blender::bke::AttributeInitConstruct());
|
"position", blender::bke::AttrDomain::Point, blender::bke::AttributeInitConstruct());
|
||||||
|
|
||||||
pointcloud->runtime = new blender::bke::PointCloudRuntime();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pointcloud_copy_data(Main * /*bmain*/,
|
static void pointcloud_copy_data(Main * /*bmain*/,
|
||||||
|
@ -584,20 +584,6 @@ if(WITH_COMPOSITOR_CPU)
|
|||||||
${CMAKE_CURRENT_BINARY_DIR}/operations
|
${CMAKE_CURRENT_BINARY_DIR}/operations
|
||||||
)
|
)
|
||||||
|
|
||||||
set(GENSRC_DIR ${CMAKE_CURRENT_BINARY_DIR}/operations)
|
|
||||||
set(GENSRC ${GENSRC_DIR}/COM_SMAAAreaTexture.h)
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT ${GENSRC}
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR}
|
|
||||||
COMMAND "$<TARGET_FILE:smaa_areatex>" ${GENSRC}
|
|
||||||
DEPENDS smaa_areatex
|
|
||||||
)
|
|
||||||
list(APPEND SRC
|
|
||||||
${GENSRC}
|
|
||||||
)
|
|
||||||
unset(GENSRC)
|
|
||||||
unset(GENSRC_DIR)
|
|
||||||
|
|
||||||
if(WITH_OPENIMAGEDENOISE)
|
if(WITH_OPENIMAGEDENOISE)
|
||||||
add_definitions(-DWITH_OPENIMAGEDENOISE)
|
add_definitions(-DWITH_OPENIMAGEDENOISE)
|
||||||
add_definitions(-DOIDN_STATIC_LIB)
|
add_definitions(-DOIDN_STATIC_LIB)
|
||||||
|
@ -7,37 +7,41 @@
|
|||||||
|
|
||||||
namespace blender::compositor {
|
namespace blender::compositor {
|
||||||
|
|
||||||
|
/* Blender encodes the threshold in the [0, 1] range, while the SMAA algorithm expects it in
|
||||||
|
* the [0, 0.5] range. */
|
||||||
|
static float get_threshold(const NodeAntiAliasingData *data)
|
||||||
|
{
|
||||||
|
return data->threshold / 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Blender encodes the local contrast adaptation factor in the [0, 1] range, while the SMAA
|
||||||
|
* algorithm expects it in the [0, 10] range. */
|
||||||
|
static float get_local_contrast_adaptation_factor(const NodeAntiAliasingData *data)
|
||||||
|
{
|
||||||
|
return data->contrast_limit * 10.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Blender encodes the corner rounding factor in the float [0, 1] range, while the SMAA algorithm
|
||||||
|
* expects it in the integer [0, 100] range. */
|
||||||
|
static int get_corner_rounding(const NodeAntiAliasingData *data)
|
||||||
|
{
|
||||||
|
return int(data->corner_rounding * 100.0f);
|
||||||
|
}
|
||||||
|
|
||||||
void AntiAliasingNode::convert_to_operations(NodeConverter &converter,
|
void AntiAliasingNode::convert_to_operations(NodeConverter &converter,
|
||||||
const CompositorContext & /*context*/) const
|
const CompositorContext & /*context*/) const
|
||||||
{
|
{
|
||||||
const bNode *node = this->get_bnode();
|
const bNode *node = this->get_bnode();
|
||||||
const NodeAntiAliasingData *data = (const NodeAntiAliasingData *)node->storage;
|
const NodeAntiAliasingData *data = (const NodeAntiAliasingData *)node->storage;
|
||||||
|
|
||||||
/* Edge Detection (First Pass) */
|
SMAAOperation *operation = new SMAAOperation();
|
||||||
SMAAEdgeDetectionOperation *operation1 = nullptr;
|
operation->set_threshold(get_threshold(data));
|
||||||
|
operation->set_local_contrast_adaptation_factor(get_local_contrast_adaptation_factor(data));
|
||||||
|
operation->set_corner_rounding(get_corner_rounding(data));
|
||||||
|
converter.add_operation(operation);
|
||||||
|
|
||||||
operation1 = new SMAAEdgeDetectionOperation();
|
converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0));
|
||||||
operation1->set_threshold(data->threshold);
|
converter.map_output_socket(get_output_socket(0), operation->get_output_socket());
|
||||||
operation1->set_local_contrast_adaptation_factor(data->contrast_limit);
|
|
||||||
converter.add_operation(operation1);
|
|
||||||
|
|
||||||
converter.map_input_socket(get_input_socket(0), operation1->get_input_socket(0));
|
|
||||||
|
|
||||||
/* Blending Weight Calculation Pixel Shader (Second Pass) */
|
|
||||||
SMAABlendingWeightCalculationOperation *operation2 =
|
|
||||||
new SMAABlendingWeightCalculationOperation();
|
|
||||||
operation2->set_corner_rounding(data->corner_rounding);
|
|
||||||
converter.add_operation(operation2);
|
|
||||||
|
|
||||||
converter.add_link(operation1->get_output_socket(), operation2->get_input_socket(0));
|
|
||||||
|
|
||||||
/* Neighborhood Blending Pixel Shader (Third Pass) */
|
|
||||||
SMAANeighborhoodBlendingOperation *operation3 = new SMAANeighborhoodBlendingOperation();
|
|
||||||
converter.add_operation(operation3);
|
|
||||||
|
|
||||||
converter.map_input_socket(get_input_socket(0), operation3->get_input_socket(0));
|
|
||||||
converter.add_link(operation2->get_output_socket(), operation3->get_input_socket(1));
|
|
||||||
converter.map_output_socket(get_output_socket(0), operation3->get_output_socket());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace blender::compositor
|
} // namespace blender::compositor
|
||||||
|
@ -18,28 +18,13 @@ void CornerPinNode::convert_to_operations(NodeConverter &converter,
|
|||||||
PlaneCornerPinMaskOperation *plane_mask_operation = new PlaneCornerPinMaskOperation();
|
PlaneCornerPinMaskOperation *plane_mask_operation = new PlaneCornerPinMaskOperation();
|
||||||
converter.add_operation(plane_mask_operation);
|
converter.add_operation(plane_mask_operation);
|
||||||
|
|
||||||
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
|
SMAAOperation *smaa_operation = new SMAAOperation();
|
||||||
converter.add_operation(smaa_edge_detection);
|
converter.add_operation(smaa_operation);
|
||||||
|
|
||||||
converter.add_link(plane_mask_operation->get_output_socket(),
|
converter.add_link(plane_mask_operation->get_output_socket(),
|
||||||
smaa_edge_detection->get_input_socket(0));
|
smaa_operation->get_input_socket(0));
|
||||||
|
|
||||||
SMAABlendingWeightCalculationOperation *smaa_blending_weights =
|
converter.map_output_socket(this->get_output_socket(1), smaa_operation->get_output_socket());
|
||||||
new SMAABlendingWeightCalculationOperation();
|
|
||||||
converter.add_operation(smaa_blending_weights);
|
|
||||||
|
|
||||||
converter.add_link(smaa_edge_detection->get_output_socket(),
|
|
||||||
smaa_blending_weights->get_input_socket(0));
|
|
||||||
|
|
||||||
SMAANeighborhoodBlendingOperation *smaa_neighborhood = new SMAANeighborhoodBlendingOperation();
|
|
||||||
converter.add_operation(smaa_neighborhood);
|
|
||||||
|
|
||||||
converter.add_link(plane_mask_operation->get_output_socket(),
|
|
||||||
smaa_neighborhood->get_input_socket(0));
|
|
||||||
converter.add_link(smaa_blending_weights->get_output_socket(),
|
|
||||||
smaa_neighborhood->get_input_socket(1));
|
|
||||||
|
|
||||||
converter.map_output_socket(this->get_output_socket(1), smaa_neighborhood->get_output_socket());
|
|
||||||
|
|
||||||
PlaneCornerPinWarpImageOperation *warp_image_operation = new PlaneCornerPinWarpImageOperation();
|
PlaneCornerPinWarpImageOperation *warp_image_operation = new PlaneCornerPinWarpImageOperation();
|
||||||
converter.add_operation(warp_image_operation);
|
converter.add_operation(warp_image_operation);
|
||||||
@ -62,7 +47,7 @@ void CornerPinNode::convert_to_operations(NodeConverter &converter,
|
|||||||
converter.add_operation(set_alpha_operation);
|
converter.add_operation(set_alpha_operation);
|
||||||
converter.add_link(warp_image_operation->get_output_socket(),
|
converter.add_link(warp_image_operation->get_output_socket(),
|
||||||
set_alpha_operation->get_input_socket(0));
|
set_alpha_operation->get_input_socket(0));
|
||||||
converter.add_link(smaa_neighborhood->get_output_socket(),
|
converter.add_link(smaa_operation->get_output_socket(),
|
||||||
set_alpha_operation->get_input_socket(1));
|
set_alpha_operation->get_input_socket(1));
|
||||||
converter.map_output_socket(this->get_output_socket(0),
|
converter.map_output_socket(this->get_output_socket(0),
|
||||||
set_alpha_operation->get_output_socket());
|
set_alpha_operation->get_output_socket());
|
||||||
|
@ -37,26 +37,10 @@ void DilateErodeNode::convert_to_operations(NodeConverter &converter,
|
|||||||
converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0));
|
converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0));
|
||||||
|
|
||||||
if (editor_node->custom3 < 2.0f) {
|
if (editor_node->custom3 < 2.0f) {
|
||||||
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
|
SMAAOperation *smaa_operation = new SMAAOperation();
|
||||||
converter.add_operation(smaa_edge_detection);
|
converter.add_operation(smaa_operation);
|
||||||
|
converter.add_link(operation->get_output_socket(), smaa_operation->get_input_socket(0));
|
||||||
converter.add_link(operation->get_output_socket(), smaa_edge_detection->get_input_socket(0));
|
converter.map_output_socket(get_output_socket(0), smaa_operation->get_output_socket());
|
||||||
|
|
||||||
SMAABlendingWeightCalculationOperation *smaa_blending_weights =
|
|
||||||
new SMAABlendingWeightCalculationOperation();
|
|
||||||
converter.add_operation(smaa_blending_weights);
|
|
||||||
|
|
||||||
converter.add_link(smaa_edge_detection->get_output_socket(),
|
|
||||||
smaa_blending_weights->get_input_socket(0));
|
|
||||||
|
|
||||||
SMAANeighborhoodBlendingOperation *smaa_neighborhood =
|
|
||||||
new SMAANeighborhoodBlendingOperation();
|
|
||||||
converter.add_operation(smaa_neighborhood);
|
|
||||||
|
|
||||||
converter.add_link(operation->get_output_socket(), smaa_neighborhood->get_input_socket(0));
|
|
||||||
converter.add_link(smaa_blending_weights->get_output_socket(),
|
|
||||||
smaa_neighborhood->get_input_socket(1));
|
|
||||||
converter.map_output_socket(get_output_socket(0), smaa_neighborhood->get_output_socket());
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0));
|
converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0));
|
||||||
|
@ -27,27 +27,10 @@ void IDMaskNode::convert_to_operations(NodeConverter &converter,
|
|||||||
converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0));
|
converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SMAAEdgeDetectionOperation *operation1 = nullptr;
|
SMAAOperation *smaa_operation = new SMAAOperation();
|
||||||
|
converter.add_operation(smaa_operation);
|
||||||
operation1 = new SMAAEdgeDetectionOperation();
|
converter.add_link(operation->get_output_socket(0), smaa_operation->get_input_socket(0));
|
||||||
converter.add_operation(operation1);
|
converter.map_output_socket(get_output_socket(0), smaa_operation->get_output_socket());
|
||||||
|
|
||||||
converter.add_link(operation->get_output_socket(0), operation1->get_input_socket(0));
|
|
||||||
|
|
||||||
/* Blending Weight Calculation Pixel Shader (Second Pass). */
|
|
||||||
SMAABlendingWeightCalculationOperation *operation2 =
|
|
||||||
new SMAABlendingWeightCalculationOperation();
|
|
||||||
converter.add_operation(operation2);
|
|
||||||
|
|
||||||
converter.add_link(operation1->get_output_socket(), operation2->get_input_socket(0));
|
|
||||||
|
|
||||||
/* Neighborhood Blending Pixel Shader (Third Pass). */
|
|
||||||
SMAANeighborhoodBlendingOperation *operation3 = new SMAANeighborhoodBlendingOperation();
|
|
||||||
converter.add_operation(operation3);
|
|
||||||
|
|
||||||
converter.add_link(operation->get_output_socket(0), operation3->get_input_socket(0));
|
|
||||||
converter.add_link(operation2->get_output_socket(), operation3->get_input_socket(1));
|
|
||||||
converter.map_output_socket(get_output_socket(0), operation3->get_output_socket());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,28 +35,13 @@ void PlaneTrackDeformNode::convert_to_operations(NodeConverter &converter,
|
|||||||
}
|
}
|
||||||
converter.add_operation(plane_mask_operation);
|
converter.add_operation(plane_mask_operation);
|
||||||
|
|
||||||
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
|
SMAAOperation *smaa_operation = new SMAAOperation();
|
||||||
converter.add_operation(smaa_edge_detection);
|
converter.add_operation(smaa_operation);
|
||||||
|
|
||||||
converter.add_link(plane_mask_operation->get_output_socket(),
|
converter.add_link(plane_mask_operation->get_output_socket(),
|
||||||
smaa_edge_detection->get_input_socket(0));
|
smaa_operation->get_input_socket(0));
|
||||||
|
|
||||||
SMAABlendingWeightCalculationOperation *smaa_blending_weights =
|
converter.map_output_socket(this->get_output_socket(1), smaa_operation->get_output_socket());
|
||||||
new SMAABlendingWeightCalculationOperation();
|
|
||||||
converter.add_operation(smaa_blending_weights);
|
|
||||||
|
|
||||||
converter.add_link(smaa_edge_detection->get_output_socket(),
|
|
||||||
smaa_blending_weights->get_input_socket(0));
|
|
||||||
|
|
||||||
SMAANeighborhoodBlendingOperation *smaa_neighborhood = new SMAANeighborhoodBlendingOperation();
|
|
||||||
converter.add_operation(smaa_neighborhood);
|
|
||||||
|
|
||||||
converter.add_link(plane_mask_operation->get_output_socket(),
|
|
||||||
smaa_neighborhood->get_input_socket(0));
|
|
||||||
converter.add_link(smaa_blending_weights->get_output_socket(),
|
|
||||||
smaa_neighborhood->get_input_socket(1));
|
|
||||||
|
|
||||||
converter.map_output_socket(this->get_output_socket(1), smaa_neighborhood->get_output_socket());
|
|
||||||
|
|
||||||
PlaneTrackWarpImageOperation *warp_image_operation = new PlaneTrackWarpImageOperation();
|
PlaneTrackWarpImageOperation *warp_image_operation = new PlaneTrackWarpImageOperation();
|
||||||
warp_image_operation->set_movie_clip(clip);
|
warp_image_operation->set_movie_clip(clip);
|
||||||
@ -75,7 +60,7 @@ void PlaneTrackDeformNode::convert_to_operations(NodeConverter &converter,
|
|||||||
converter.add_operation(set_alpha_operation);
|
converter.add_operation(set_alpha_operation);
|
||||||
converter.add_link(warp_image_operation->get_output_socket(),
|
converter.add_link(warp_image_operation->get_output_socket(),
|
||||||
set_alpha_operation->get_input_socket(0));
|
set_alpha_operation->get_input_socket(0));
|
||||||
converter.add_link(smaa_neighborhood->get_output_socket(),
|
converter.add_link(smaa_operation->get_output_socket(),
|
||||||
set_alpha_operation->get_input_socket(1));
|
set_alpha_operation->get_input_socket(1));
|
||||||
converter.map_output_socket(this->get_output_socket(0),
|
converter.map_output_socket(this->get_output_socket(0),
|
||||||
set_alpha_operation->get_output_socket());
|
set_alpha_operation->get_output_socket());
|
||||||
|
@ -54,25 +54,10 @@ void ZCombineNode::convert_to_operations(NodeConverter &converter,
|
|||||||
converter.map_input_socket(get_input_socket(3), maskoperation->get_input_socket(1));
|
converter.map_input_socket(get_input_socket(3), maskoperation->get_input_socket(1));
|
||||||
|
|
||||||
/* Step 2 anti alias mask bit of an expensive operation, but does the trick. */
|
/* Step 2 anti alias mask bit of an expensive operation, but does the trick. */
|
||||||
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
|
SMAAOperation *smaa_operation = new SMAAOperation();
|
||||||
converter.add_operation(smaa_edge_detection);
|
converter.add_operation(smaa_operation);
|
||||||
|
|
||||||
converter.add_link(maskoperation->get_output_socket(),
|
converter.add_link(maskoperation->get_output_socket(), smaa_operation->get_input_socket(0));
|
||||||
smaa_edge_detection->get_input_socket(0));
|
|
||||||
|
|
||||||
SMAABlendingWeightCalculationOperation *smaa_blending_weights =
|
|
||||||
new SMAABlendingWeightCalculationOperation();
|
|
||||||
converter.add_operation(smaa_blending_weights);
|
|
||||||
|
|
||||||
converter.add_link(smaa_edge_detection->get_output_socket(),
|
|
||||||
smaa_blending_weights->get_input_socket(0));
|
|
||||||
|
|
||||||
SMAANeighborhoodBlendingOperation *smaa_neighborhood = new SMAANeighborhoodBlendingOperation();
|
|
||||||
converter.add_operation(smaa_neighborhood);
|
|
||||||
|
|
||||||
converter.add_link(maskoperation->get_output_socket(), smaa_neighborhood->get_input_socket(0));
|
|
||||||
converter.add_link(smaa_blending_weights->get_output_socket(),
|
|
||||||
smaa_neighborhood->get_input_socket(1));
|
|
||||||
|
|
||||||
/* use mask to blend between the input colors. */
|
/* use mask to blend between the input colors. */
|
||||||
ZCombineMaskOperation *zcombineoperation = this->get_bnode()->custom1 ?
|
ZCombineMaskOperation *zcombineoperation = this->get_bnode()->custom1 ?
|
||||||
@ -80,7 +65,7 @@ void ZCombineNode::convert_to_operations(NodeConverter &converter,
|
|||||||
new ZCombineMaskOperation();
|
new ZCombineMaskOperation();
|
||||||
converter.add_operation(zcombineoperation);
|
converter.add_operation(zcombineoperation);
|
||||||
|
|
||||||
converter.add_link(smaa_neighborhood->get_output_socket(),
|
converter.add_link(smaa_operation->get_output_socket(),
|
||||||
zcombineoperation->get_input_socket(0));
|
zcombineoperation->get_input_socket(0));
|
||||||
converter.map_input_socket(get_input_socket(0), zcombineoperation->get_input_socket(1));
|
converter.map_input_socket(get_input_socket(0), zcombineoperation->get_input_socket(1));
|
||||||
converter.map_input_socket(get_input_socket(2), zcombineoperation->get_input_socket(2));
|
converter.map_input_socket(get_input_socket(2), zcombineoperation->get_input_socket(2));
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -4,89 +4,38 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "COM_MultiThreadedOperation.h"
|
#include "COM_NodeOperation.h"
|
||||||
|
|
||||||
namespace blender::compositor {
|
namespace blender::compositor {
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------------*/
|
class SMAAOperation : public NodeOperation {
|
||||||
/* Edge Detection (First Pass) */
|
|
||||||
|
|
||||||
class SMAAEdgeDetectionOperation : public MultiThreadedOperation {
|
|
||||||
protected:
|
protected:
|
||||||
float threshold_;
|
float threshold_ = 0.1f;
|
||||||
float contrast_limit_;
|
float local_contrast_adaptation_factor_ = 2.0f;
|
||||||
|
int corner_rounding_ = 25;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SMAAEdgeDetectionOperation();
|
SMAAOperation();
|
||||||
|
|
||||||
void set_threshold(float threshold);
|
void set_threshold(float threshold)
|
||||||
|
{
|
||||||
|
threshold_ = threshold;
|
||||||
|
}
|
||||||
|
|
||||||
void set_local_contrast_adaptation_factor(float factor);
|
void set_local_contrast_adaptation_factor(float factor)
|
||||||
|
{
|
||||||
|
local_contrast_adaptation_factor_ = factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_corner_rounding(int corner_rounding)
|
||||||
|
{
|
||||||
|
corner_rounding_ = corner_rounding;
|
||||||
|
}
|
||||||
|
|
||||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
||||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
void update_memory_buffer(MemoryBuffer *output,
|
||||||
const rcti &area,
|
const rcti &area,
|
||||||
Span<MemoryBuffer *> inputs) override;
|
Span<MemoryBuffer *> inputs) override;
|
||||||
};
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------------*/
|
|
||||||
/* Blending Weight Calculation (Second Pass) */
|
|
||||||
|
|
||||||
class SMAABlendingWeightCalculationOperation : public MultiThreadedOperation {
|
|
||||||
private:
|
|
||||||
std::function<void(int x, int y, float *out)> sample_image_fn_;
|
|
||||||
int corner_rounding_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
SMAABlendingWeightCalculationOperation();
|
|
||||||
|
|
||||||
void set_corner_rounding(float rounding);
|
|
||||||
|
|
||||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
|
||||||
void update_memory_buffer_started(MemoryBuffer *output,
|
|
||||||
const rcti &area,
|
|
||||||
Span<MemoryBuffer *> inputs) override;
|
|
||||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
|
||||||
const rcti &area,
|
|
||||||
Span<MemoryBuffer *> inputs) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
/* Diagonal Search Functions */
|
|
||||||
/**
|
|
||||||
* These functions allows to perform diagonal pattern searches.
|
|
||||||
*/
|
|
||||||
int search_diag1(int x, int y, int dir, bool *r_found);
|
|
||||||
int search_diag2(int x, int y, int dir, bool *r_found);
|
|
||||||
/**
|
|
||||||
* This searches for diagonal patterns and returns the corresponding weights.
|
|
||||||
*/
|
|
||||||
void calculate_diag_weights(int x, int y, const float edges[2], float weights[2]);
|
|
||||||
bool is_vertical_search_unneeded(int x, int y);
|
|
||||||
|
|
||||||
/* Horizontal/Vertical Search Functions */
|
|
||||||
int search_xleft(int x, int y);
|
|
||||||
int search_xright(int x, int y);
|
|
||||||
int search_yup(int x, int y);
|
|
||||||
int search_ydown(int x, int y);
|
|
||||||
|
|
||||||
/* Corner Detection Functions */
|
|
||||||
void detect_horizontal_corner_pattern(
|
|
||||||
float weights[2], int left, int right, int y, int d1, int d2);
|
|
||||||
void detect_vertical_corner_pattern(
|
|
||||||
float weights[2], int x, int top, int bottom, int d1, int d2);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------------*/
|
|
||||||
/* Neighborhood Blending (Third Pass) */
|
|
||||||
|
|
||||||
class SMAANeighborhoodBlendingOperation : public MultiThreadedOperation {
|
|
||||||
public:
|
|
||||||
SMAANeighborhoodBlendingOperation();
|
|
||||||
|
|
||||||
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
|
|
||||||
void update_memory_buffer_partial(MemoryBuffer *output,
|
|
||||||
const rcti &area,
|
|
||||||
Span<MemoryBuffer *> inputs) override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace blender::compositor
|
} // namespace blender::compositor
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
#include "COM_ConstantOperation.h"
|
#include "COM_ConstantOperation.h"
|
||||||
#include "COM_MultiThreadedOperation.h"
|
#include "COM_MultiThreadedOperation.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace blender::compositor {
|
namespace blender::compositor {
|
||||||
|
|
||||||
class TranslateOperation : public MultiThreadedOperation {
|
class TranslateOperation : public MultiThreadedOperation {
|
||||||
@ -21,6 +23,8 @@ class TranslateOperation : public MultiThreadedOperation {
|
|||||||
bool is_delta_set_;
|
bool is_delta_set_;
|
||||||
bool is_relative_;
|
bool is_relative_;
|
||||||
|
|
||||||
|
std::mutex mutex_;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
MemoryBufferExtend x_extend_mode_;
|
MemoryBufferExtend x_extend_mode_;
|
||||||
MemoryBufferExtend y_extend_mode_;
|
MemoryBufferExtend y_extend_mode_;
|
||||||
@ -50,6 +54,11 @@ class TranslateOperation : public MultiThreadedOperation {
|
|||||||
inline void ensure_delta()
|
inline void ensure_delta()
|
||||||
{
|
{
|
||||||
if (!is_delta_set_) {
|
if (!is_delta_set_) {
|
||||||
|
std::unique_lock lock(mutex_);
|
||||||
|
if (is_delta_set_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
delta_x_ = get_input_operation(X_INPUT_INDEX)->get_constant_value_default(0.0f);
|
delta_x_ = get_input_operation(X_INPUT_INDEX)->get_constant_value_default(0.0f);
|
||||||
delta_y_ = get_input_operation(Y_INPUT_INDEX)->get_constant_value_default(0.0f);
|
delta_y_ = get_input_operation(Y_INPUT_INDEX)->get_constant_value_default(0.0f);
|
||||||
if (get_is_relative()) {
|
if (get_is_relative()) {
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "GPU_shader.hh"
|
#include "GPU_shader.hh"
|
||||||
#include "GPU_texture.hh"
|
#include "GPU_texture.hh"
|
||||||
|
|
||||||
|
#include "IMB_colormanagement.hh"
|
||||||
#include "IMB_imbuf.hh"
|
#include "IMB_imbuf.hh"
|
||||||
#include "IMB_imbuf_types.hh"
|
#include "IMB_imbuf_types.hh"
|
||||||
|
|
||||||
@ -56,74 +57,6 @@ bool operator==(const CachedImageKey &a, const CachedImageKey &b)
|
|||||||
* Cached Image.
|
* Cached Image.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Returns a new texture of the given format and precision preprocessed using the given shader. The
|
|
||||||
* input texture is freed. */
|
|
||||||
static GPUTexture *preprocess_texture(Context &context,
|
|
||||||
GPUTexture *input_texture,
|
|
||||||
eGPUTextureFormat target_format,
|
|
||||||
ResultPrecision precision,
|
|
||||||
const char *shader_name)
|
|
||||||
{
|
|
||||||
const int2 size = int2(GPU_texture_width(input_texture), GPU_texture_height(input_texture));
|
|
||||||
|
|
||||||
GPUTexture *preprocessed_texture = GPU_texture_create_2d(
|
|
||||||
"Cached Image", size.x, size.y, 1, target_format, GPU_TEXTURE_USAGE_GENERAL, nullptr);
|
|
||||||
|
|
||||||
GPUShader *shader = context.get_shader(shader_name, precision);
|
|
||||||
GPU_shader_bind(shader);
|
|
||||||
|
|
||||||
const int input_unit = GPU_shader_get_sampler_binding(shader, "input_tx");
|
|
||||||
GPU_texture_bind(input_texture, input_unit);
|
|
||||||
|
|
||||||
const int image_unit = GPU_shader_get_sampler_binding(shader, "output_img");
|
|
||||||
GPU_texture_image_bind(preprocessed_texture, image_unit);
|
|
||||||
|
|
||||||
compute_dispatch_threads_at_least(shader, size);
|
|
||||||
|
|
||||||
GPU_shader_unbind();
|
|
||||||
GPU_texture_unbind(input_texture);
|
|
||||||
GPU_texture_image_unbind(preprocessed_texture);
|
|
||||||
GPU_texture_free(input_texture);
|
|
||||||
|
|
||||||
return preprocessed_texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compositor images are expected to be always pre-multiplied, so identify if the GPU texture
|
|
||||||
* returned by the IMB module is straight and needs to be pre-multiplied. An exception is when
|
|
||||||
* the image has an alpha mode of channel packed or alpha ignore, in which case, we always ignore
|
|
||||||
* pre-multiplication. */
|
|
||||||
static bool should_premultiply_alpha(Image *image, ImBuf *image_buffer)
|
|
||||||
{
|
|
||||||
if (ELEM(image->alpha_mode, IMA_ALPHA_CHANNEL_PACKED, IMA_ALPHA_IGNORE)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !BKE_image_has_gpu_texture_premultiplied_alpha(image, image_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get a suitable texture format supported by the compositor given the format of the texture
|
|
||||||
* returned by the IMB module. See imb_gpu_get_format for the formats that needs to be handled. */
|
|
||||||
static eGPUTextureFormat get_compatible_texture_format(eGPUTextureFormat original_format)
|
|
||||||
{
|
|
||||||
switch (original_format) {
|
|
||||||
case GPU_R16F:
|
|
||||||
case GPU_R32F:
|
|
||||||
case GPU_RGBA16F:
|
|
||||||
case GPU_RGBA32F:
|
|
||||||
return original_format;
|
|
||||||
case GPU_R8:
|
|
||||||
return GPU_R16F;
|
|
||||||
case GPU_RGBA8:
|
|
||||||
case GPU_SRGB8_A8:
|
|
||||||
return GPU_RGBA16F;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
BLI_assert_unreachable();
|
|
||||||
return original_format;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the selected render layer selected assuming the image is a multilayer image. */
|
/* Get the selected render layer selected assuming the image is a multilayer image. */
|
||||||
static RenderLayer *get_render_layer(Image *image, ImageUser &image_user)
|
static RenderLayer *get_render_layer(Image *image, ImageUser &image_user)
|
||||||
{
|
{
|
||||||
@ -205,6 +138,56 @@ static ImageUser compute_image_user_for_pass(Context &context,
|
|||||||
return image_user_for_pass;
|
return image_user_for_pass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The image buffer might be stored as an sRGB 8-bit image, while the compositor expects linear
|
||||||
|
* float images, so compute a linear float buffer for the image buffer. This will also do linear
|
||||||
|
* space conversion and alpha pre-multiplication as needed. We could store those images in sRGB GPU
|
||||||
|
* textures and let the GPU do the linear space conversion, but the issues is that we don't control
|
||||||
|
* how the GPU does the conversion and so we get tiny differences across CPU and GPU compositing,
|
||||||
|
* and potentially even across GPUs/Drivers. Further, if alpha pre-multiplication is needed, we
|
||||||
|
* would need to do it ourself, which means alpha pre-multiplication will happen before linear
|
||||||
|
* space conversion, which would produce yet another difference. So we just do everything on the
|
||||||
|
* CPU, since this is already a cached resource.
|
||||||
|
*
|
||||||
|
* To avoid conflicts with other threads, create a new image buffer and assign all the necessary
|
||||||
|
* information to it, with IB_DO_NOT_TAKE_OWNERSHIP for buffers since a deep copy is not needed.
|
||||||
|
*
|
||||||
|
* The caller should free the returned image buffer. */
|
||||||
|
static ImBuf *compute_linear_buffer(ImBuf *image_buffer)
|
||||||
|
{
|
||||||
|
/* Do not pass the flags to the allocation function to avoid buffer allocation, but assign them
|
||||||
|
* after to retain important information like precision and alpha mode. */
|
||||||
|
ImBuf *linear_image_buffer = IMB_allocImBuf(
|
||||||
|
image_buffer->x, image_buffer->y, image_buffer->planes, 0);
|
||||||
|
linear_image_buffer->flags = image_buffer->flags;
|
||||||
|
|
||||||
|
/* Assign the float buffer if it exists, as well as its number of channels. */
|
||||||
|
IMB_assign_float_buffer(
|
||||||
|
linear_image_buffer, image_buffer->float_buffer, IB_DO_NOT_TAKE_OWNERSHIP);
|
||||||
|
linear_image_buffer->channels = image_buffer->channels;
|
||||||
|
|
||||||
|
/* If no float buffer exists, assign it then compute a float buffer from it. This is the main
|
||||||
|
* call of this function. */
|
||||||
|
if (!linear_image_buffer->float_buffer.data) {
|
||||||
|
IMB_assign_byte_buffer(
|
||||||
|
linear_image_buffer, image_buffer->byte_buffer, IB_DO_NOT_TAKE_OWNERSHIP);
|
||||||
|
IMB_float_from_rect(linear_image_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the image buffer contained compressed data, assign them as well, but only if the color
|
||||||
|
* space of the buffer is linear or data, since we need linear data and can't preprocess the
|
||||||
|
* compressed buffer. If not, we fallback to the float buffer already assigned, which is
|
||||||
|
* guaranteed to exist as a fallback for compressed textures. */
|
||||||
|
const bool is_suitable_compressed_color_space =
|
||||||
|
IMB_colormanagement_space_is_data(image_buffer->byte_buffer.colorspace) ||
|
||||||
|
IMB_colormanagement_space_is_scene_linear(image_buffer->byte_buffer.colorspace);
|
||||||
|
if (image_buffer->ftype == IMB_FTYPE_DDS && is_suitable_compressed_color_space) {
|
||||||
|
linear_image_buffer->ftype = IMB_FTYPE_DDS;
|
||||||
|
IMB_assign_dds_data(linear_image_buffer, image_buffer->dds_data, IB_DO_NOT_TAKE_OWNERSHIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
return linear_image_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
CachedImage::CachedImage(Context &context,
|
CachedImage::CachedImage(Context &context,
|
||||||
Image *image,
|
Image *image,
|
||||||
ImageUser *image_user,
|
ImageUser *image_user,
|
||||||
@ -227,34 +210,12 @@ CachedImage::CachedImage(Context &context,
|
|||||||
context, image, image_user, pass_name);
|
context, image, image_user, pass_name);
|
||||||
|
|
||||||
ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &image_user_for_pass, nullptr);
|
ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &image_user_for_pass, nullptr);
|
||||||
const bool is_premultiplied = BKE_image_has_gpu_texture_premultiplied_alpha(image, image_buffer);
|
ImBuf *linear_image_buffer = compute_linear_buffer(image_buffer);
|
||||||
texture_ = IMB_create_gpu_texture("Image Texture", image_buffer, true, is_premultiplied);
|
|
||||||
|
texture_ = IMB_create_gpu_texture("Image Texture", linear_image_buffer, true, true);
|
||||||
GPU_texture_update_mipmap_chain(texture_);
|
GPU_texture_update_mipmap_chain(texture_);
|
||||||
|
|
||||||
const eGPUTextureFormat original_format = GPU_texture_format(texture_);
|
IMB_freeImBuf(linear_image_buffer);
|
||||||
const eGPUTextureFormat target_format = get_compatible_texture_format(original_format);
|
|
||||||
const ResultType result_type = Result::type(target_format);
|
|
||||||
const ResultPrecision precision = Result::precision(target_format);
|
|
||||||
|
|
||||||
/* The GPU image returned by the IMB module can be in a format not supported by the compositor,
|
|
||||||
* or it might need pre-multiplication, so preprocess them first. */
|
|
||||||
if (result_type == ResultType::Color && should_premultiply_alpha(image, image_buffer)) {
|
|
||||||
texture_ = preprocess_texture(
|
|
||||||
context, texture_, target_format, precision, "compositor_premultiply_alpha");
|
|
||||||
}
|
|
||||||
else if (original_format != target_format) {
|
|
||||||
const char *conversion_shader_name = result_type == ResultType::Float ?
|
|
||||||
"compositor_convert_float_to_float" :
|
|
||||||
"compositor_convert_color_to_color";
|
|
||||||
texture_ = preprocess_texture(
|
|
||||||
context, texture_, target_format, precision, conversion_shader_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the alpha to 1 using swizzling if alpha is ignored. */
|
|
||||||
if (result_type == ResultType::Color && image->alpha_mode == IMA_ALPHA_IGNORE) {
|
|
||||||
GPU_texture_swizzle_set(texture_, "rgb1");
|
|
||||||
}
|
|
||||||
|
|
||||||
BKE_image_release_ibuf(image, image_buffer, nullptr);
|
BKE_image_release_ibuf(image, image_buffer, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3674,138 +3674,136 @@ size_t ANIM_animdata_filter(bAnimContext *ac,
|
|||||||
void *data,
|
void *data,
|
||||||
eAnimCont_Types datatype)
|
eAnimCont_Types datatype)
|
||||||
{
|
{
|
||||||
|
if (!data || !anim_data) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
size_t items = 0;
|
size_t items = 0;
|
||||||
|
switch (datatype) {
|
||||||
|
/* Action-Editing Modes */
|
||||||
|
case ANIMCONT_ACTION: /* 'Action Editor' */
|
||||||
|
{
|
||||||
|
Object *obact = ac->obact;
|
||||||
|
SpaceAction *saction = (SpaceAction *)ac->sl;
|
||||||
|
bDopeSheet *ads = (saction) ? &saction->ads : nullptr;
|
||||||
|
|
||||||
/* only filter data if there's somewhere to put it */
|
/* specially check for AnimData filter, see #36687. */
|
||||||
if (data && anim_data) {
|
if (UNLIKELY(filter_mode & ANIMFILTER_ANIMDATA)) {
|
||||||
/* firstly filter the data */
|
/* all channels here are within the same AnimData block, hence this special case */
|
||||||
switch (datatype) {
|
if (LIKELY(obact->adt)) {
|
||||||
/* Action-Editing Modes */
|
ANIMCHANNEL_NEW_CHANNEL(obact->adt, ANIMTYPE_ANIMDATA, (ID *)obact, nullptr);
|
||||||
case ANIMCONT_ACTION: /* 'Action Editor' */
|
|
||||||
{
|
|
||||||
Object *obact = ac->obact;
|
|
||||||
SpaceAction *saction = (SpaceAction *)ac->sl;
|
|
||||||
bDopeSheet *ads = (saction) ? &saction->ads : nullptr;
|
|
||||||
|
|
||||||
/* specially check for AnimData filter, see #36687. */
|
|
||||||
if (UNLIKELY(filter_mode & ANIMFILTER_ANIMDATA)) {
|
|
||||||
/* all channels here are within the same AnimData block, hence this special case */
|
|
||||||
if (LIKELY(obact->adt)) {
|
|
||||||
ANIMCHANNEL_NEW_CHANNEL(obact->adt, ANIMTYPE_ANIMDATA, (ID *)obact, nullptr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
/* The check for the DopeSheet summary is included here
|
|
||||||
* since the summary works here too. */
|
|
||||||
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
|
|
||||||
items += animfilter_action(
|
|
||||||
ac, anim_data, ads, static_cast<bAction *>(data), filter_mode, (ID *)obact);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case ANIMCONT_SHAPEKEY: /* 'ShapeKey Editor' */
|
else {
|
||||||
{
|
/* The check for the DopeSheet summary is included here
|
||||||
Key *key = (Key *)data;
|
* since the summary works here too. */
|
||||||
|
|
||||||
/* specially check for AnimData filter, see #36687. */
|
|
||||||
if (UNLIKELY(filter_mode & ANIMFILTER_ANIMDATA)) {
|
|
||||||
/* all channels here are within the same AnimData block, hence this special case */
|
|
||||||
if (LIKELY(key->adt)) {
|
|
||||||
ANIMCHANNEL_NEW_CHANNEL(key->adt, ANIMTYPE_ANIMDATA, (ID *)key, nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* The check for the DopeSheet summary is included here
|
|
||||||
* since the summary works here too. */
|
|
||||||
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
|
|
||||||
items = animdata_filter_shapekey(ac, anim_data, key, filter_mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Modes for Specialty Data Types (i.e. not keyframes) */
|
|
||||||
case ANIMCONT_GPENCIL: {
|
|
||||||
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
|
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
|
||||||
if (U.experimental.use_grease_pencil_version3) {
|
items += animfilter_action(
|
||||||
items = animdata_filter_grease_pencil(ac, anim_data, filter_mode);
|
ac, anim_data, ads, static_cast<bAction *>(data), filter_mode, (ID *)obact);
|
||||||
}
|
|
||||||
else {
|
|
||||||
items = animdata_filter_gpencil_legacy(ac, anim_data, data, filter_mode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case ANIMCONT_MASK: {
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ANIMCONT_SHAPEKEY: /* 'ShapeKey Editor' */
|
||||||
|
{
|
||||||
|
Key *key = (Key *)data;
|
||||||
|
|
||||||
|
/* specially check for AnimData filter, see #36687. */
|
||||||
|
if (UNLIKELY(filter_mode & ANIMFILTER_ANIMDATA)) {
|
||||||
|
/* all channels here are within the same AnimData block, hence this special case */
|
||||||
|
if (LIKELY(key->adt)) {
|
||||||
|
ANIMCHANNEL_NEW_CHANNEL(key->adt, ANIMTYPE_ANIMDATA, (ID *)key, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* The check for the DopeSheet summary is included here
|
||||||
|
* since the summary works here too. */
|
||||||
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
|
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
|
||||||
items = animdata_filter_mask(ac->bmain, anim_data, data, filter_mode);
|
items = animdata_filter_shapekey(ac, anim_data, key, filter_mode);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* DopeSheet Based Modes */
|
break;
|
||||||
case ANIMCONT_DOPESHEET: /* 'DopeSheet Editor' */
|
|
||||||
{
|
|
||||||
/* the DopeSheet editor is the primary place where the DopeSheet summaries are useful */
|
|
||||||
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
|
|
||||||
items += animdata_filter_dopesheet(
|
|
||||||
ac, anim_data, static_cast<bDopeSheet *>(data), filter_mode);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ANIMCONT_FCURVES: /* Graph Editor -> F-Curves/Animation Editing */
|
|
||||||
case ANIMCONT_DRIVERS: /* Graph Editor -> Drivers Editing */
|
|
||||||
case ANIMCONT_NLA: /* NLA Editor */
|
|
||||||
{
|
|
||||||
/* all of these editors use the basic DopeSheet data for filtering options,
|
|
||||||
* but don't have all the same features */
|
|
||||||
items = animdata_filter_dopesheet(
|
|
||||||
ac, anim_data, static_cast<bDopeSheet *>(data), filter_mode);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Timeline Mode - Basically the same as dopesheet,
|
|
||||||
* except we only have the summary for now */
|
|
||||||
case ANIMCONT_TIMELINE: {
|
|
||||||
/* the DopeSheet editor is the primary place where the DopeSheet summaries are useful */
|
|
||||||
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
|
|
||||||
items += animdata_filter_dopesheet(
|
|
||||||
ac, anim_data, static_cast<bDopeSheet *>(data), filter_mode);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Special/Internal Use */
|
|
||||||
case ANIMCONT_CHANNEL: /* animation channel */
|
|
||||||
{
|
|
||||||
bDopeSheet *ads = ac->ads;
|
|
||||||
|
|
||||||
/* based on the channel type, filter relevant data for this */
|
|
||||||
items = animdata_filter_animchan(
|
|
||||||
ac, anim_data, ads, static_cast<bAnimListElem *>(data), filter_mode);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* unhandled */
|
|
||||||
default: {
|
|
||||||
printf("ANIM_animdata_filter() - Invalid datatype argument %i\n", datatype);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* remove any 'weedy' entries */
|
/* Modes for Specialty Data Types (i.e. not keyframes) */
|
||||||
items = animdata_filter_remove_invalid(anim_data);
|
case ANIMCONT_GPENCIL: {
|
||||||
|
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
|
||||||
|
if (U.experimental.use_grease_pencil_version3) {
|
||||||
|
items = animdata_filter_grease_pencil(ac, anim_data, filter_mode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
items = animdata_filter_gpencil_legacy(ac, anim_data, data, filter_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ANIMCONT_MASK: {
|
||||||
|
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
|
||||||
|
items = animdata_filter_mask(ac->bmain, anim_data, data, filter_mode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* remove duplicates (if required) */
|
/* DopeSheet Based Modes */
|
||||||
if (filter_mode & ANIMFILTER_NODUPLIS) {
|
case ANIMCONT_DOPESHEET: /* 'DopeSheet Editor' */
|
||||||
items = animdata_filter_remove_duplis(anim_data);
|
{
|
||||||
|
/* the DopeSheet editor is the primary place where the DopeSheet summaries are useful */
|
||||||
|
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
|
||||||
|
items += animdata_filter_dopesheet(
|
||||||
|
ac, anim_data, static_cast<bDopeSheet *>(data), filter_mode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ANIMCONT_FCURVES: /* Graph Editor -> F-Curves/Animation Editing */
|
||||||
|
case ANIMCONT_DRIVERS: /* Graph Editor -> Drivers Editing */
|
||||||
|
case ANIMCONT_NLA: /* NLA Editor */
|
||||||
|
{
|
||||||
|
/* all of these editors use the basic DopeSheet data for filtering options,
|
||||||
|
* but don't have all the same features */
|
||||||
|
items = animdata_filter_dopesheet(
|
||||||
|
ac, anim_data, static_cast<bDopeSheet *>(data), filter_mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Timeline Mode - Basically the same as dopesheet,
|
||||||
|
* except we only have the summary for now */
|
||||||
|
case ANIMCONT_TIMELINE: {
|
||||||
|
/* the DopeSheet editor is the primary place where the DopeSheet summaries are useful */
|
||||||
|
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
|
||||||
|
items += animdata_filter_dopesheet(
|
||||||
|
ac, anim_data, static_cast<bDopeSheet *>(data), filter_mode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Special/Internal Use */
|
||||||
|
case ANIMCONT_CHANNEL: /* animation channel */
|
||||||
|
{
|
||||||
|
bDopeSheet *ads = ac->ads;
|
||||||
|
|
||||||
|
/* based on the channel type, filter relevant data for this */
|
||||||
|
items = animdata_filter_animchan(
|
||||||
|
ac, anim_data, ads, static_cast<bAnimListElem *>(data), filter_mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* unhandled */
|
||||||
|
default: {
|
||||||
|
printf("ANIM_animdata_filter() - Invalid datatype argument %i\n", datatype);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* return the number of items in the list */
|
/* remove any 'weedy' entries */
|
||||||
|
items = animdata_filter_remove_invalid(anim_data);
|
||||||
|
|
||||||
|
/* remove duplicates (if required) */
|
||||||
|
if (filter_mode & ANIMFILTER_NODUPLIS) {
|
||||||
|
items = animdata_filter_remove_duplis(anim_data);
|
||||||
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,7 +582,7 @@ static void ASSET_OT_catalog_delete(wmOperatorType *ot)
|
|||||||
static asset_system::AssetCatalogService *get_catalog_service(bContext *C)
|
static asset_system::AssetCatalogService *get_catalog_service(bContext *C)
|
||||||
{
|
{
|
||||||
const SpaceFile *sfile = CTX_wm_space_file(C);
|
const SpaceFile *sfile = CTX_wm_space_file(C);
|
||||||
if (!sfile) {
|
if (!sfile || ED_fileselect_is_file_browser(sfile)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1406,7 +1406,7 @@ static void GREASE_PENCIL_OT_clean_loose(wmOperatorType *ot)
|
|||||||
|
|
||||||
ot->invoke = WM_operator_props_popup_confirm;
|
ot->invoke = WM_operator_props_popup_confirm;
|
||||||
ot->exec = grease_pencil_clean_loose_exec;
|
ot->exec = grease_pencil_clean_loose_exec;
|
||||||
ot->poll = editable_grease_pencil_poll;
|
ot->poll = active_grease_pencil_layer_poll;
|
||||||
|
|
||||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||||
|
|
||||||
|
@ -53,8 +53,8 @@ static int cachefile_open_invoke(bContext *C, wmOperator *op, const wmEvent * /*
|
|||||||
char filepath[FILE_MAX];
|
char filepath[FILE_MAX];
|
||||||
Main *bmain = CTX_data_main(C);
|
Main *bmain = CTX_data_main(C);
|
||||||
|
|
||||||
STRNCPY(filepath, BKE_main_blendfile_path(bmain));
|
/* Default to the same directory as the blend file. */
|
||||||
BLI_path_extension_replace(filepath, sizeof(filepath), ".abc");
|
BLI_path_split_dir_part(BKE_main_blendfile_path(bmain), filepath, sizeof(filepath));
|
||||||
RNA_string_set(op->ptr, "filepath", filepath);
|
RNA_string_set(op->ptr, "filepath", filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ void CACHEFILE_OT_open(wmOperatorType *ot)
|
|||||||
ot->cancel = open_cancel;
|
ot->cancel = open_cancel;
|
||||||
|
|
||||||
WM_operator_properties_filesel(ot,
|
WM_operator_properties_filesel(ot,
|
||||||
FILE_TYPE_ALEMBIC | FILE_TYPE_FOLDER,
|
FILE_TYPE_ALEMBIC | FILE_TYPE_USD | FILE_TYPE_FOLDER,
|
||||||
FILE_BLENDER,
|
FILE_BLENDER,
|
||||||
FILE_OPENFILE,
|
FILE_OPENFILE,
|
||||||
WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH,
|
WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH,
|
||||||
@ -163,8 +163,8 @@ static int cachefile_layer_open_invoke(bContext *C, wmOperator *op, const wmEven
|
|||||||
char filepath[FILE_MAX];
|
char filepath[FILE_MAX];
|
||||||
Main *bmain = CTX_data_main(C);
|
Main *bmain = CTX_data_main(C);
|
||||||
|
|
||||||
STRNCPY(filepath, BKE_main_blendfile_path(bmain));
|
/* Default to the same directory as the blend file. */
|
||||||
BLI_path_extension_replace(filepath, sizeof(filepath), ".abc");
|
BLI_path_split_dir_part(BKE_main_blendfile_path(bmain), filepath, sizeof(filepath));
|
||||||
RNA_string_set(op->ptr, "filepath", filepath);
|
RNA_string_set(op->ptr, "filepath", filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ void CACHEFILE_OT_layer_add(wmOperatorType *ot)
|
|||||||
ot->exec = cachefile_layer_add_exec;
|
ot->exec = cachefile_layer_add_exec;
|
||||||
|
|
||||||
WM_operator_properties_filesel(ot,
|
WM_operator_properties_filesel(ot,
|
||||||
FILE_TYPE_ALEMBIC | FILE_TYPE_FOLDER,
|
FILE_TYPE_ALEMBIC | FILE_TYPE_USD | FILE_TYPE_FOLDER,
|
||||||
FILE_BLENDER,
|
FILE_BLENDER,
|
||||||
FILE_OPENFILE,
|
FILE_OPENFILE,
|
||||||
WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH,
|
WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH,
|
||||||
|
@ -3584,7 +3584,10 @@ static int edbm_remove_doubles_exec(bContext *C, wmOperator *op)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BKE_reportf(op->reports, RPT_INFO, "Removed %d vertice(s)", count_multi);
|
BKE_reportf(op->reports,
|
||||||
|
RPT_INFO,
|
||||||
|
count_multi == 1 ? "Removed %d vertex" : "Removed %d vertices",
|
||||||
|
count_multi);
|
||||||
|
|
||||||
return OPERATOR_FINISHED;
|
return OPERATOR_FINISHED;
|
||||||
}
|
}
|
||||||
|
@ -1613,7 +1613,6 @@ static int shade_smooth_exec(bContext *C, wmOperator *op)
|
|||||||
const float angle = RNA_float_get(op->ptr, "angle");
|
const float angle = RNA_float_get(op->ptr, "angle");
|
||||||
bke::mesh_sharp_edges_set_from_angle(mesh, angle, keep_sharp_edges);
|
bke::mesh_sharp_edges_set_from_angle(mesh, angle, keep_sharp_edges);
|
||||||
}
|
}
|
||||||
mesh.tag_sharpness_changed();
|
|
||||||
BKE_mesh_batch_cache_dirty_tag(static_cast<Mesh *>(ob->data), BKE_MESH_BATCH_DIRTY_ALL);
|
BKE_mesh_batch_cache_dirty_tag(static_cast<Mesh *>(ob->data), BKE_MESH_BATCH_DIRTY_ALL);
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
@ -506,7 +506,7 @@ void ED_view3d_gizmo_mesh_preselect_get_active(const bContext *C,
|
|||||||
/* weak, allocate an array just to access the index. */
|
/* weak, allocate an array just to access the index. */
|
||||||
Base *base = nullptr;
|
Base *base = nullptr;
|
||||||
Object *obedit = nullptr;
|
Object *obedit = nullptr;
|
||||||
{
|
if (object_index != -1) {
|
||||||
Vector<Base *> bases = BKE_view_layer_array_from_bases_in_edit_mode(
|
Vector<Base *> bases = BKE_view_layer_array_from_bases_in_edit_mode(
|
||||||
scene, view_layer, CTX_wm_view3d(C));
|
scene, view_layer, CTX_wm_view3d(C));
|
||||||
if (object_index < bases.size()) {
|
if (object_index < bases.size()) {
|
||||||
|
@ -705,11 +705,15 @@ void Transform_Properties(wmOperatorType *ot, int flags)
|
|||||||
|
|
||||||
RNA_def_boolean(ot->srna, "use_snap_project", false, "Project Individual Elements", "");
|
RNA_def_boolean(ot->srna, "use_snap_project", false, "Project Individual Elements", "");
|
||||||
|
|
||||||
/* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid previous ambiguity of
|
/* TODO(@gfxcoder): Rename `snap_target` to `snap_base` to avoid previous ambiguity of
|
||||||
* "target" (now, "source" is geometry to be moved and "target" is geometry to which moved
|
* "target" (now, "base" or "source" is geometry to be moved and "target" is geometry to
|
||||||
* geometry is snapped). Use "Source snap point" and "Point on source that will snap to
|
* which moved geometry is snapped). */
|
||||||
* target" for name and description, respectively. */
|
prop = RNA_def_enum(ot->srna,
|
||||||
prop = RNA_def_enum(ot->srna, "snap_target", rna_enum_snap_source_items, 0, "Snap With", "");
|
"snap_target",
|
||||||
|
rna_enum_snap_source_items,
|
||||||
|
0,
|
||||||
|
"Snap Base",
|
||||||
|
"Point on source that will snap to target");
|
||||||
RNA_def_property_flag(prop, PROP_HIDDEN);
|
RNA_def_property_flag(prop, PROP_HIDDEN);
|
||||||
|
|
||||||
/* Target selection. */
|
/* Target selection. */
|
||||||
|
@ -13,6 +13,6 @@ bke::GeometrySet join_geometries(Span<bke::GeometrySet> geometries,
|
|||||||
const bke::AnonymousAttributePropagationInfo &propagation_info);
|
const bke::AnonymousAttributePropagationInfo &propagation_info);
|
||||||
|
|
||||||
void join_attributes(Span<const bke::GeometryComponent *> src_components,
|
void join_attributes(Span<const bke::GeometryComponent *> src_components,
|
||||||
bke::GeometryComponent &r_result,
|
Span<StringRef> ignored_attributes,
|
||||||
Span<StringRef> ignored_attributes = {});
|
bke::GeometryComponent &r_result);
|
||||||
} // namespace blender::geometry
|
} // namespace blender::geometry
|
||||||
|
@ -62,16 +62,8 @@ bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
|
|||||||
const RealizeInstancesOptions &options);
|
const RealizeInstancesOptions &options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Join all instances into a single geometry component for each geometry type. For example, all
|
* Same #realize_instances but will realize only the instances chosen by
|
||||||
* mesh instances (including the already realized mesh) are joined into a single mesh. The output
|
* varied_depth_option to there chosen depth.
|
||||||
* geometry set does not contain any instances. If the input did not contain any instances, it is
|
|
||||||
* returned directly.
|
|
||||||
*
|
|
||||||
* The `id` attribute has special handling. If there is an id attribute on any component, the
|
|
||||||
* output will contain an `id` attribute as well. The output id is generated by mixing/hashing ids
|
|
||||||
* of instances and of the instanced geometry data.
|
|
||||||
*
|
|
||||||
* Will realize only the instances chosen by varied_depth_option to there chosen depth.
|
|
||||||
*/
|
*/
|
||||||
bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
|
bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
|
||||||
const RealizeInstancesOptions &options,
|
const RealizeInstancesOptions &options,
|
||||||
|
@ -74,8 +74,8 @@ static void fill_new_attribute(const Span<const GeometryComponent *> src_compone
|
|||||||
}
|
}
|
||||||
|
|
||||||
void join_attributes(const Span<const GeometryComponent *> src_components,
|
void join_attributes(const Span<const GeometryComponent *> src_components,
|
||||||
GeometryComponent &r_result,
|
const Span<StringRef> ignored_attributes,
|
||||||
const Span<StringRef> ignored_attributes)
|
GeometryComponent &r_result)
|
||||||
{
|
{
|
||||||
const Map<AttributeIDRef, AttributeMetaData> info = get_final_attribute_info(src_components,
|
const Map<AttributeIDRef, AttributeMetaData> info = get_final_attribute_info(src_components,
|
||||||
ignored_attributes);
|
ignored_attributes);
|
||||||
@ -129,7 +129,7 @@ static void join_instances(const Span<const GeometryComponent *> src_components,
|
|||||||
|
|
||||||
r_result.replace_instances(dst_instances.release());
|
r_result.replace_instances(dst_instances.release());
|
||||||
auto &dst_component = r_result.get_component_for_write<bke::InstancesComponent>();
|
auto &dst_component = r_result.get_component_for_write<bke::InstancesComponent>();
|
||||||
join_attributes(src_components, dst_component, {".reference_index"});
|
join_attributes(src_components, {".reference_index"}, dst_component);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void join_volumes(const Span<const GeometryComponent *> /*src_components*/,
|
static void join_volumes(const Span<const GeometryComponent *> /*src_components*/,
|
||||||
|
@ -298,7 +298,7 @@ struct InstanceContext {
|
|||||||
curves(gather_info.curves.attributes.size()),
|
curves(gather_info.curves.attributes.size()),
|
||||||
instances(gather_info.instances_attriubutes.size())
|
instances(gather_info.instances_attriubutes.size())
|
||||||
{
|
{
|
||||||
//empty
|
// empty
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -753,7 +753,7 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info,
|
|||||||
* is an instance, the condition is true only when the depth is exactly 0. Additionally, the
|
* is an instance, the condition is true only when the depth is exactly 0. Additionally, the
|
||||||
* function extends its operation to instances if any of their nested children meet the first
|
* function extends its operation to instances if any of their nested children meet the first
|
||||||
* condition.
|
* condition.
|
||||||
*
|
*
|
||||||
* Based on bke::GeometrySet::attribute_foreach
|
* Based on bke::GeometrySet::attribute_foreach
|
||||||
*/
|
*/
|
||||||
static bool attribute_foreach(const bke::GeometrySet &geometry_set,
|
static bool attribute_foreach(const bke::GeometrySet &geometry_set,
|
||||||
@ -823,12 +823,11 @@ static bool attribute_foreach(const bke::GeometrySet &geometry_set,
|
|||||||
return is_relevant;
|
return is_relevant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Based on bke::GeometrySet::gather_attributes_for_propagation.
|
* Based on bke::GeometrySet::gather_attributes_for_propagation.
|
||||||
* Specialized for Specialized attribute_foreach to get:
|
* Specialized for Specialized attribute_foreach to get:
|
||||||
* current_depth, depth_target, instance_depth and selection.
|
* current_depth, depth_target, instance_depth and selection.
|
||||||
*/
|
*/
|
||||||
void static gather_attributes_for_propagation(
|
void static gather_attributes_for_propagation(
|
||||||
bke::GeometrySet re_geometry_set,
|
bke::GeometrySet re_geometry_set,
|
||||||
const Span<bke::GeometryComponent::Type> component_types,
|
const Span<bke::GeometryComponent::Type> component_types,
|
||||||
@ -869,7 +868,8 @@ void static gather_attributes_for_propagation(
|
|||||||
|
|
||||||
AttrDomain domain = meta_data.domain;
|
AttrDomain domain = meta_data.domain;
|
||||||
if (dst_component_type != bke::GeometryComponent::Type::Instance &&
|
if (dst_component_type != bke::GeometryComponent::Type::Instance &&
|
||||||
domain == AttrDomain::Instance) {
|
domain == AttrDomain::Instance)
|
||||||
|
{
|
||||||
domain = AttrDomain::Point;
|
domain = AttrDomain::Point;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -983,7 +983,7 @@ static void execute_instances_tasks(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const void *attribute_ptr;
|
const void *attribute_ptr;
|
||||||
if (attribute_fallback_array[attribute_index] != nullptr) {
|
if (attribute_fallback_array[attribute_index] != nullptr) {
|
||||||
attribute_ptr = attribute_fallback_array[attribute_index];
|
attribute_ptr = attribute_fallback_array[attribute_index];
|
||||||
}
|
}
|
||||||
@ -1014,7 +1014,7 @@ static void execute_instances_tasks(
|
|||||||
}
|
}
|
||||||
|
|
||||||
join_attributes(
|
join_attributes(
|
||||||
for_join_attributes, dst_component, {"position", ".reference_index", "instance_transform"});
|
for_join_attributes, {"position", ".reference_index", "instance_transform"}, dst_component);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
@ -1990,9 +1990,7 @@ bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
|
|||||||
VariedDepthOption all_instances;
|
VariedDepthOption all_instances;
|
||||||
all_instances.depths = VArray<int>::ForSingle(VariedDepthOption::MAX_DEPTH,
|
all_instances.depths = VArray<int>::ForSingle(VariedDepthOption::MAX_DEPTH,
|
||||||
geometry_set.get_instances()->instances_num());
|
geometry_set.get_instances()->instances_num());
|
||||||
IndexMaskMemory memory;
|
all_instances.selection = IndexMask(geometry_set.get_instances()->instances_num());
|
||||||
all_instances.selection = IndexMask::from_bools(
|
|
||||||
VArray<bool>::ForSingle(true, geometry_set.get_instances()->instances_num()), memory);
|
|
||||||
return realize_instances(geometry_set, options, all_instances);
|
return realize_instances(geometry_set, options, all_instances);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2049,12 +2047,8 @@ bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
|
|||||||
const float4x4 transform = float4x4::identity();
|
const float4x4 transform = float4x4::identity();
|
||||||
InstanceContext attribute_fallbacks(gather_info);
|
InstanceContext attribute_fallbacks(gather_info);
|
||||||
|
|
||||||
gather_realize_tasks_recursive(gather_info,
|
gather_realize_tasks_recursive(
|
||||||
0,
|
gather_info, 0, VariedDepthOption::MAX_DEPTH, geometry_set, transform, attribute_fallbacks);
|
||||||
VariedDepthOption::MAX_DEPTH,
|
|
||||||
geometry_set,
|
|
||||||
transform,
|
|
||||||
attribute_fallbacks);
|
|
||||||
|
|
||||||
bke::GeometrySet new_geometry_set;
|
bke::GeometrySet new_geometry_set;
|
||||||
execute_instances_tasks(gather_info.instances.instances_components_to_merge,
|
execute_instances_tasks(gather_info.instances.instances_components_to_merge,
|
||||||
@ -2062,28 +2056,28 @@ bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
|
|||||||
all_instance_attributes,
|
all_instance_attributes,
|
||||||
gather_info.instances.attribute_fallback,
|
gather_info.instances.attribute_fallback,
|
||||||
new_geometry_set);
|
new_geometry_set);
|
||||||
|
|
||||||
const int64_t total_points_num = get_final_points_num(gather_info.r_tasks);
|
const int64_t total_points_num = get_final_points_num(gather_info.r_tasks);
|
||||||
/* This doesn't have to be exact at all, it's just a rough estimate ot make decisions about
|
/* This doesn't have to be exact at all, it's just a rough estimate ot make decisions about
|
||||||
* multi-threading (overhead). */
|
* multi-threading (overhead). */
|
||||||
const int64_t approximate_used_bytes_num = total_points_num * 32;
|
const int64_t approximate_used_bytes_num = total_points_num * 32;
|
||||||
threading::memory_bandwidth_bound_task(approximate_used_bytes_num, [&]() {
|
threading::memory_bandwidth_bound_task(approximate_used_bytes_num, [&]() {
|
||||||
execute_realize_pointcloud_tasks(options.keep_original_ids,
|
execute_realize_pointcloud_tasks(options.keep_original_ids,
|
||||||
all_pointclouds_info,
|
all_pointclouds_info,
|
||||||
gather_info.r_tasks.pointcloud_tasks,
|
gather_info.r_tasks.pointcloud_tasks,
|
||||||
all_pointclouds_info.attributes,
|
all_pointclouds_info.attributes,
|
||||||
new_geometry_set);
|
new_geometry_set);
|
||||||
execute_realize_mesh_tasks(options.keep_original_ids,
|
execute_realize_mesh_tasks(options.keep_original_ids,
|
||||||
all_meshes_info,
|
all_meshes_info,
|
||||||
gather_info.r_tasks.mesh_tasks,
|
gather_info.r_tasks.mesh_tasks,
|
||||||
all_meshes_info.attributes,
|
all_meshes_info.attributes,
|
||||||
all_meshes_info.materials,
|
all_meshes_info.materials,
|
||||||
new_geometry_set);
|
new_geometry_set);
|
||||||
execute_realize_curve_tasks(options.keep_original_ids,
|
execute_realize_curve_tasks(options.keep_original_ids,
|
||||||
all_curves_info,
|
all_curves_info,
|
||||||
gather_info.r_tasks.curve_tasks,
|
gather_info.r_tasks.curve_tasks,
|
||||||
all_curves_info.attributes,
|
all_curves_info.attributes,
|
||||||
new_geometry_set);
|
new_geometry_set);
|
||||||
});
|
});
|
||||||
if (gather_info.r_tasks.first_volume) {
|
if (gather_info.r_tasks.first_volume) {
|
||||||
new_geometry_set.add(*gather_info.r_tasks.first_volume);
|
new_geometry_set.add(*gather_info.r_tasks.first_volume);
|
||||||
|
@ -527,6 +527,8 @@ void gpu::MTLTexture::update_sub(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool is_compressed = (format_flag_ & GPU_FORMAT_COMPRESSED);
|
||||||
|
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
/* Determine totalsize of INPUT Data. */
|
/* Determine totalsize of INPUT Data. */
|
||||||
int num_channels = to_component_len(format_);
|
int num_channels = to_component_len(format_);
|
||||||
@ -593,10 +595,12 @@ void gpu::MTLTexture::update_sub(
|
|||||||
false /* Not a clear. */
|
false /* Not a clear. */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Determine whether we can do direct BLIT or not. */
|
/* Determine whether we can do direct BLIT or not. For compressed textures,
|
||||||
|
* always assume a direct blit (input data pretends to be float, but it is
|
||||||
|
* not). */
|
||||||
bool can_use_direct_blit = true;
|
bool can_use_direct_blit = true;
|
||||||
if (expected_dst_bytes_per_pixel != input_bytes_per_pixel ||
|
if (!is_compressed && (expected_dst_bytes_per_pixel != input_bytes_per_pixel ||
|
||||||
num_channels != destination_num_channels)
|
num_channels != destination_num_channels))
|
||||||
{
|
{
|
||||||
can_use_direct_blit = false;
|
can_use_direct_blit = false;
|
||||||
}
|
}
|
||||||
@ -620,7 +624,7 @@ void gpu::MTLTexture::update_sub(
|
|||||||
|
|
||||||
/* Safety Checks. */
|
/* Safety Checks. */
|
||||||
if (type == GPU_DATA_UINT_24_8 || type == GPU_DATA_10_11_11_REV ||
|
if (type == GPU_DATA_UINT_24_8 || type == GPU_DATA_10_11_11_REV ||
|
||||||
type == GPU_DATA_2_10_10_10_REV)
|
type == GPU_DATA_2_10_10_10_REV || is_compressed)
|
||||||
{
|
{
|
||||||
BLI_assert(can_use_direct_blit &&
|
BLI_assert(can_use_direct_blit &&
|
||||||
"Special input data type must be a 1-1 mapping with destination texture as it "
|
"Special input data type must be a 1-1 mapping with destination texture as it "
|
||||||
@ -755,6 +759,12 @@ void gpu::MTLTexture::update_sub(
|
|||||||
extent[0] :
|
extent[0] :
|
||||||
ctx->pipeline_state.unpack_row_length);
|
ctx->pipeline_state.unpack_row_length);
|
||||||
size_t bytes_per_image = bytes_per_row;
|
size_t bytes_per_image = bytes_per_row;
|
||||||
|
if (is_compressed) {
|
||||||
|
size_t block_size = to_block_size(format_);
|
||||||
|
size_t blocks_x = divide_ceil_u(extent[0], 4);
|
||||||
|
bytes_per_row = blocks_x * block_size;
|
||||||
|
bytes_per_image = bytes_per_row;
|
||||||
|
}
|
||||||
int max_array_index = ((type_ == GPU_TEXTURE_1D_ARRAY) ? extent[1] : 1);
|
int max_array_index = ((type_ == GPU_TEXTURE_1D_ARRAY) ? extent[1] : 1);
|
||||||
for (int array_index = 0; array_index < max_array_index; array_index++) {
|
for (int array_index = 0; array_index < max_array_index; array_index++) {
|
||||||
|
|
||||||
@ -827,6 +837,13 @@ void gpu::MTLTexture::update_sub(
|
|||||||
extent[0] :
|
extent[0] :
|
||||||
ctx->pipeline_state.unpack_row_length);
|
ctx->pipeline_state.unpack_row_length);
|
||||||
size_t bytes_per_image = bytes_per_row * extent[1];
|
size_t bytes_per_image = bytes_per_row * extent[1];
|
||||||
|
if (is_compressed) {
|
||||||
|
size_t block_size = to_block_size(format_);
|
||||||
|
size_t blocks_x = divide_ceil_u(extent[0], 4);
|
||||||
|
size_t blocks_y = divide_ceil_u(extent[1], 4);
|
||||||
|
bytes_per_row = blocks_x * block_size;
|
||||||
|
bytes_per_image = bytes_per_row * blocks_y;
|
||||||
|
}
|
||||||
|
|
||||||
size_t texture_array_relative_offset = 0;
|
size_t texture_array_relative_offset = 0;
|
||||||
int base_slice = (type_ == GPU_TEXTURE_2D_ARRAY) ? offset[2] : 0;
|
int base_slice = (type_ == GPU_TEXTURE_2D_ARRAY) ? offset[2] : 0;
|
||||||
@ -1218,6 +1235,12 @@ void gpu::MTLTexture::ensure_mipmaps(int miplvl)
|
|||||||
|
|
||||||
void gpu::MTLTexture::generate_mipmap()
|
void gpu::MTLTexture::generate_mipmap()
|
||||||
{
|
{
|
||||||
|
/* Compressed textures allow users to provide their own custom mipmaps. And
|
||||||
|
* we can't generate them at runtime anyway. */
|
||||||
|
if (format_flag_ & GPU_FORMAT_COMPRESSED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Fetch Active Context. */
|
/* Fetch Active Context. */
|
||||||
MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
|
MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
|
||||||
BLI_assert(ctx);
|
BLI_assert(ctx);
|
||||||
|
@ -160,13 +160,17 @@ MTLPixelFormat gpu_texture_format_to_metal(eGPUTextureFormat tex_format)
|
|||||||
return MTLPixelFormatR8Snorm;
|
return MTLPixelFormatR8Snorm;
|
||||||
/* Special formats, texture only. */
|
/* Special formats, texture only. */
|
||||||
case GPU_SRGB8_A8_DXT1:
|
case GPU_SRGB8_A8_DXT1:
|
||||||
|
return MTLPixelFormatBC1_RGBA_sRGB;
|
||||||
case GPU_SRGB8_A8_DXT3:
|
case GPU_SRGB8_A8_DXT3:
|
||||||
|
return MTLPixelFormatBC2_RGBA_sRGB;
|
||||||
case GPU_SRGB8_A8_DXT5:
|
case GPU_SRGB8_A8_DXT5:
|
||||||
|
return MTLPixelFormatBC3_RGBA_sRGB;
|
||||||
case GPU_RGBA8_DXT1:
|
case GPU_RGBA8_DXT1:
|
||||||
|
return MTLPixelFormatBC1_RGBA;
|
||||||
case GPU_RGBA8_DXT3:
|
case GPU_RGBA8_DXT3:
|
||||||
|
return MTLPixelFormatBC2_RGBA;
|
||||||
case GPU_RGBA8_DXT5:
|
case GPU_RGBA8_DXT5:
|
||||||
BLI_assert_msg(false, "Compressed texture not implemented yet!\n");
|
return MTLPixelFormatBC3_RGBA;
|
||||||
return MTLPixelFormatRGBA8Unorm;
|
|
||||||
case GPU_SRGB8:
|
case GPU_SRGB8:
|
||||||
/* 24-Bit pixel format are not supported. Emulate using a padded type with alpha. */
|
/* 24-Bit pixel format are not supported. Emulate using a padded type with alpha. */
|
||||||
return MTLPixelFormatRGBA8Unorm_sRGB;
|
return MTLPixelFormatRGBA8Unorm_sRGB;
|
||||||
@ -247,6 +251,14 @@ size_t get_mtl_format_bytesize(MTLPixelFormat tex_format)
|
|||||||
return 4;
|
return 4;
|
||||||
case MTLPixelFormatDepth16Unorm:
|
case MTLPixelFormatDepth16Unorm:
|
||||||
return 2;
|
return 2;
|
||||||
|
case MTLPixelFormatBC1_RGBA:
|
||||||
|
case MTLPixelFormatBC1_RGBA_sRGB:
|
||||||
|
return 1; /* Note: not quite correct (BC1 is 0.5 bpp). */
|
||||||
|
case MTLPixelFormatBC2_RGBA:
|
||||||
|
case MTLPixelFormatBC2_RGBA_sRGB:
|
||||||
|
case MTLPixelFormatBC3_RGBA:
|
||||||
|
case MTLPixelFormatBC3_RGBA_sRGB:
|
||||||
|
return 1;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
BLI_assert_msg(false, "Unrecognised GPU pixel format!\n");
|
BLI_assert_msg(false, "Unrecognised GPU pixel format!\n");
|
||||||
@ -272,6 +284,12 @@ int get_mtl_format_num_components(MTLPixelFormat tex_format)
|
|||||||
case MTLPixelFormatRGBA8Unorm_sRGB:
|
case MTLPixelFormatRGBA8Unorm_sRGB:
|
||||||
case MTLPixelFormatRGB10A2Uint:
|
case MTLPixelFormatRGB10A2Uint:
|
||||||
case MTLPixelFormatRGB10A2Unorm:
|
case MTLPixelFormatRGB10A2Unorm:
|
||||||
|
case MTLPixelFormatBC1_RGBA_sRGB:
|
||||||
|
case MTLPixelFormatBC2_RGBA_sRGB:
|
||||||
|
case MTLPixelFormatBC3_RGBA_sRGB:
|
||||||
|
case MTLPixelFormatBC1_RGBA:
|
||||||
|
case MTLPixelFormatBC2_RGBA:
|
||||||
|
case MTLPixelFormatBC3_RGBA:
|
||||||
return 4;
|
return 4;
|
||||||
|
|
||||||
case MTLPixelFormatRG11B10Float:
|
case MTLPixelFormatRG11B10Float:
|
||||||
|
@ -118,6 +118,21 @@ ImBuf *IMB_allocFromBuffer(const uint8_t *byte_buffer,
|
|||||||
void IMB_assign_byte_buffer(ImBuf *ibuf, uint8_t *buffer_data, ImBufOwnership ownership);
|
void IMB_assign_byte_buffer(ImBuf *ibuf, uint8_t *buffer_data, ImBufOwnership ownership);
|
||||||
void IMB_assign_float_buffer(ImBuf *ibuf, float *buffer_data, ImBufOwnership ownership);
|
void IMB_assign_float_buffer(ImBuf *ibuf, float *buffer_data, ImBufOwnership ownership);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign the content and the color space of the corresponding buffer the data from the given
|
||||||
|
* buffer.
|
||||||
|
*
|
||||||
|
* \note Does not modify the topology (width, height, number of channels)
|
||||||
|
* or the mipmaps in any way.
|
||||||
|
*
|
||||||
|
* \note The ownership of the data in the source buffer is ignored.
|
||||||
|
*/
|
||||||
|
void IMB_assign_byte_buffer(ImBuf *ibuf, const ImBufByteBuffer &buffer, ImBufOwnership ownership);
|
||||||
|
void IMB_assign_float_buffer(ImBuf *ibuf,
|
||||||
|
const ImBufFloatBuffer &buffer,
|
||||||
|
ImBufOwnership ownership);
|
||||||
|
void IMB_assign_dds_data(ImBuf *ibuf, const DDSData &data, ImBufOwnership ownership);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make corresponding buffers available for modification.
|
* Make corresponding buffers available for modification.
|
||||||
* Is achieved by ensuring that the given ImBuf is the only owner of the underlying buffer data.
|
* Is achieved by ensuring that the given ImBuf is the only owner of the underlying buffer data.
|
||||||
|
@ -34,17 +34,6 @@ struct IDProperty;
|
|||||||
#define IMB_MIPMAP_LEVELS 20
|
#define IMB_MIPMAP_LEVELS 20
|
||||||
#define IMB_FILEPATH_SIZE 1024
|
#define IMB_FILEPATH_SIZE 1024
|
||||||
|
|
||||||
struct DDSData {
|
|
||||||
/** DDS fourcc info */
|
|
||||||
unsigned int fourcc;
|
|
||||||
/** The number of mipmaps in the dds file */
|
|
||||||
unsigned int nummipmaps;
|
|
||||||
/** The compressed image data */
|
|
||||||
unsigned char *data;
|
|
||||||
/** The size of the compressed data */
|
|
||||||
unsigned int size;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \ingroup imbuf
|
* \ingroup imbuf
|
||||||
* This is the abstraction of an image. ImBuf is the basic type used for all imbuf operations.
|
* This is the abstraction of an image. ImBuf is the basic type used for all imbuf operations.
|
||||||
@ -143,6 +132,19 @@ enum ImBufOwnership {
|
|||||||
IB_TAKE_OWNERSHIP = 1,
|
IB_TAKE_OWNERSHIP = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DDSData {
|
||||||
|
/** DDS fourcc info */
|
||||||
|
unsigned int fourcc;
|
||||||
|
/** The number of mipmaps in the dds file */
|
||||||
|
unsigned int nummipmaps;
|
||||||
|
/** The compressed image data */
|
||||||
|
unsigned char *data;
|
||||||
|
/** The size of the compressed data */
|
||||||
|
unsigned int size;
|
||||||
|
/** Who owns the data buffer. */
|
||||||
|
ImBufOwnership ownership;
|
||||||
|
};
|
||||||
|
|
||||||
/* Different storage specialization.
|
/* Different storage specialization.
|
||||||
*
|
*
|
||||||
* NOTE: Avoid direct assignments and allocations, use the buffer utilities from the IMB_imbuf.hh
|
* NOTE: Avoid direct assignments and allocations, use the buffer utilities from the IMB_imbuf.hh
|
||||||
|
@ -84,6 +84,27 @@ template<class BufferType> static void imb_free_buffer(BufferType &buffer)
|
|||||||
buffer.ownership = IB_DO_NOT_TAKE_OWNERSHIP;
|
buffer.ownership = IB_DO_NOT_TAKE_OWNERSHIP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Free the specified DDS buffer storage, freeing memory when needed and restoring the state of the
|
||||||
|
* buffer to its defaults. */
|
||||||
|
static void imb_free_dds_buffer(DDSData &dds_data)
|
||||||
|
{
|
||||||
|
if (dds_data.data) {
|
||||||
|
switch (dds_data.ownership) {
|
||||||
|
case IB_DO_NOT_TAKE_OWNERSHIP:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IB_TAKE_OWNERSHIP:
|
||||||
|
/* dds_data.data is allocated by DirectDrawSurface::readData(), so don't use MEM_freeN! */
|
||||||
|
free(dds_data.data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset buffer to defaults. */
|
||||||
|
dds_data.data = nullptr;
|
||||||
|
dds_data.ownership = IB_DO_NOT_TAKE_OWNERSHIP;
|
||||||
|
}
|
||||||
|
|
||||||
/* Allocate pixel storage of the given buffer. The buffer owns the allocated memory.
|
/* Allocate pixel storage of the given buffer. The buffer owns the allocated memory.
|
||||||
* Returns true of allocation succeeded, false otherwise. */
|
* Returns true of allocation succeeded, false otherwise. */
|
||||||
template<class BufferType>
|
template<class BufferType>
|
||||||
@ -249,11 +270,7 @@ void IMB_freeImBuf(ImBuf *ibuf)
|
|||||||
IMB_free_gpu_textures(ibuf);
|
IMB_free_gpu_textures(ibuf);
|
||||||
IMB_metadata_free(ibuf->metadata);
|
IMB_metadata_free(ibuf->metadata);
|
||||||
colormanage_cache_free(ibuf);
|
colormanage_cache_free(ibuf);
|
||||||
|
imb_free_dds_buffer(ibuf->dds_data);
|
||||||
if (ibuf->dds_data.data != nullptr) {
|
|
||||||
/* dds_data.data is allocated by DirectDrawSurface::readData(), so don't use MEM_freeN! */
|
|
||||||
free(ibuf->dds_data.data);
|
|
||||||
}
|
|
||||||
MEM_freeN(ibuf);
|
MEM_freeN(ibuf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -472,6 +489,32 @@ void IMB_assign_float_buffer(ImBuf *ibuf, float *buffer_data, const ImBufOwnersh
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IMB_assign_byte_buffer(ImBuf *ibuf,
|
||||||
|
const ImBufByteBuffer &buffer,
|
||||||
|
const ImBufOwnership ownership)
|
||||||
|
{
|
||||||
|
IMB_assign_byte_buffer(ibuf, buffer.data, ownership);
|
||||||
|
ibuf->byte_buffer.colorspace = buffer.colorspace;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IMB_assign_float_buffer(ImBuf *ibuf,
|
||||||
|
const ImBufFloatBuffer &buffer,
|
||||||
|
const ImBufOwnership ownership)
|
||||||
|
{
|
||||||
|
IMB_assign_float_buffer(ibuf, buffer.data, ownership);
|
||||||
|
ibuf->float_buffer.colorspace = buffer.colorspace;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IMB_assign_dds_data(ImBuf *ibuf, const DDSData &data, const ImBufOwnership ownership)
|
||||||
|
{
|
||||||
|
BLI_assert(ibuf->ftype == IMB_FTYPE_DDS);
|
||||||
|
|
||||||
|
imb_free_dds_buffer(ibuf->dds_data);
|
||||||
|
|
||||||
|
ibuf->dds_data = data;
|
||||||
|
ibuf->dds_data.ownership = ownership;
|
||||||
|
}
|
||||||
|
|
||||||
ImBuf *IMB_allocFromBufferOwn(
|
ImBuf *IMB_allocFromBufferOwn(
|
||||||
uint8_t *byte_buffer, float *float_buffer, uint w, uint h, uint channels)
|
uint8_t *byte_buffer, float *float_buffer, uint w, uint h, uint channels)
|
||||||
{
|
{
|
||||||
|
@ -330,6 +330,7 @@ static void LoadDXTCImage(ImBuf *ibuf, Filesystem::IOMemReader &mem_reader)
|
|||||||
ibuf->dds_data.size = mem_reader.size() - dds_header_size;
|
ibuf->dds_data.size = mem_reader.size() - dds_header_size;
|
||||||
ibuf->dds_data.data = (uchar *)malloc(ibuf->dds_data.size);
|
ibuf->dds_data.data = (uchar *)malloc(ibuf->dds_data.size);
|
||||||
mem_reader.pread(ibuf->dds_data.data, ibuf->dds_data.size, dds_header_size);
|
mem_reader.pread(ibuf->dds_data.data, ibuf->dds_data.size, dds_header_size);
|
||||||
|
ibuf->dds_data.ownership = IB_TAKE_OWNERSHIP;
|
||||||
|
|
||||||
/* Flip compressed image data to match OpenGL convention. */
|
/* Flip compressed image data to match OpenGL convention. */
|
||||||
FlipDXTCImage(ibuf);
|
FlipDXTCImage(ibuf);
|
||||||
|
@ -7703,6 +7703,8 @@ static void rna_def_modifier_nodes_bake(BlenderRNA *brna)
|
|||||||
StructRNA *srna;
|
StructRNA *srna;
|
||||||
PropertyRNA *prop;
|
PropertyRNA *prop;
|
||||||
|
|
||||||
|
RNA_define_lib_overridable(true);
|
||||||
|
|
||||||
srna = RNA_def_struct(brna, "NodesModifierBake", nullptr);
|
srna = RNA_def_struct(brna, "NodesModifierBake", nullptr);
|
||||||
RNA_def_struct_ui_text(srna, "Nodes Modifier Bake", "");
|
RNA_def_struct_ui_text(srna, "Nodes Modifier Bake", "");
|
||||||
|
|
||||||
@ -7758,6 +7760,8 @@ static void rna_def_modifier_nodes_bake(BlenderRNA *brna)
|
|||||||
RNA_def_property_struct_type(prop, "NodesModifierDataBlock");
|
RNA_def_property_struct_type(prop, "NodesModifierDataBlock");
|
||||||
RNA_def_property_collection_sdna(prop, nullptr, "data_blocks", "data_blocks_num");
|
RNA_def_property_collection_sdna(prop, nullptr, "data_blocks", "data_blocks_num");
|
||||||
RNA_def_property_srna(prop, "NodesModifierBakeDataBlocks");
|
RNA_def_property_srna(prop, "NodesModifierBakeDataBlocks");
|
||||||
|
|
||||||
|
RNA_define_lib_overridable(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rna_def_modifier_nodes_bakes(BlenderRNA *brna)
|
static void rna_def_modifier_nodes_bakes(BlenderRNA *brna)
|
||||||
|
@ -3574,7 +3574,7 @@ static void rna_def_tool_settings(BlenderRNA *brna)
|
|||||||
prop, "rna_ToolSettings_snap_mode_get", "rna_ToolSettings_snap_mode_set", nullptr);
|
prop, "rna_ToolSettings_snap_mode_get", "rna_ToolSettings_snap_mode_set", nullptr);
|
||||||
RNA_def_property_flag(prop, PROP_ENUM_FLAG);
|
RNA_def_property_flag(prop, PROP_ENUM_FLAG);
|
||||||
RNA_def_property_ui_text(
|
RNA_def_property_ui_text(
|
||||||
prop, "Snap Element", "Type of element for the \"Snap With\" to snap to");
|
prop, "Snap Element", "Type of element for the \"Snap Base\" to snap to");
|
||||||
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); /* header redraw */
|
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); /* header redraw */
|
||||||
|
|
||||||
prop = RNA_def_property(srna, "snap_elements_individual", PROP_ENUM, PROP_NONE);
|
prop = RNA_def_property(srna, "snap_elements_individual", PROP_ENUM, PROP_NONE);
|
||||||
|
@ -18,13 +18,13 @@ static void node_declare(NodeDeclarationBuilder &b)
|
|||||||
b.add_input<decl::Bool>("Selection")
|
b.add_input<decl::Bool>("Selection")
|
||||||
.default_value(true)
|
.default_value(true)
|
||||||
.hide_value()
|
.hide_value()
|
||||||
.supports_field()
|
.field_on_all()
|
||||||
.description("Which top-level instances to realize");
|
.description("Which top-level instances to realize");
|
||||||
b.add_input<decl::Bool>("Realize All")
|
b.add_input<decl::Bool>("Realize All")
|
||||||
.default_value(true)
|
.default_value(true)
|
||||||
.supports_field()
|
.field_on_all()
|
||||||
.description("Determine wether to realize nested instances completly");
|
.description("Determine wether to realize nested instances completly");
|
||||||
b.add_input<decl::Int>("Depth").default_value(0).min(0).supports_field().description(
|
b.add_input<decl::Int>("Depth").default_value(0).min(0).field_on_all().description(
|
||||||
"Number of levels of nested instances to realize for each top-level instance");
|
"Number of levels of nested instances to realize for each top-level instance");
|
||||||
b.add_output<decl::Geometry>("Geometry").propagate_all();
|
b.add_output<decl::Geometry>("Geometry").propagate_all();
|
||||||
}
|
}
|
||||||
@ -38,24 +38,29 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||||||
}
|
}
|
||||||
|
|
||||||
GeometryComponentEditData::remember_deformed_positions_if_necessary(geometry_set);
|
GeometryComponentEditData::remember_deformed_positions_if_necessary(geometry_set);
|
||||||
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
|
|
||||||
Field<bool> realize_all_filed = params.extract_input<Field<bool>>("Realize All");
|
Field<bool> realize_all_field = params.extract_input<Field<bool>>("Realize All");
|
||||||
Field<int> depth_field = params.extract_input<Field<int>>("Depth");
|
Field<int> depth_field = params.extract_input<Field<int>>("Depth");
|
||||||
|
|
||||||
static auto depth_override = mf::build::SI2_SO<int, bool, int>(
|
static auto depth_override = mf::build::SI2_SO<int, bool, int>(
|
||||||
"depth_override",
|
"depth_override",
|
||||||
[](int value, bool realize) { return realize ? -1 : std::max(value, 0); },
|
[](int depth, bool realize_all_field) {
|
||||||
|
return realize_all_field ? geometry::VariedDepthOption::MAX_DEPTH : std::max(depth, 0);
|
||||||
|
},
|
||||||
mf::build::exec_presets::AllSpanOrSingle());
|
mf::build::exec_presets::AllSpanOrSingle());
|
||||||
|
|
||||||
|
Field<int> depth_field_overrided(FieldOperation::Create(
|
||||||
|
depth_override, {std::move(depth_field), std::move(realize_all_field)}));
|
||||||
|
|
||||||
|
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
|
||||||
|
|
||||||
static auto selection_override = mf::build::SI2_SO<int, bool, bool>(
|
static auto selection_override = mf::build::SI2_SO<int, bool, bool>(
|
||||||
"selection_override",
|
"selection_override",
|
||||||
[](int value, bool selection) { return value == 0 ? false : selection; },
|
[](int depth_override, bool selection) { return depth_override == 0 ? false : selection; },
|
||||||
mf::build::exec_presets::AllSpanOrSingle());
|
mf::build::exec_presets::AllSpanOrSingle());
|
||||||
|
|
||||||
Field<int> depth_field_overrided(
|
Field<bool> selection_field_overrided(FieldOperation::Create(
|
||||||
FieldOperation::Create(depth_override, {depth_field, realize_all_filed}));
|
selection_override, {depth_field_overrided, std::move(selection_field)}));
|
||||||
Field<bool> selection_field_overrided(
|
|
||||||
FieldOperation::Create(selection_override, {depth_field_overrided, selection_field}));
|
|
||||||
|
|
||||||
const bke::Instances &instances = *geometry_set.get_instances();
|
const bke::Instances &instances = *geometry_set.get_instances();
|
||||||
const bke::InstancesFieldContext field_context(instances);
|
const bke::InstancesFieldContext field_context(instances);
|
||||||
|
@ -21,7 +21,10 @@ static void node_declare(NodeDeclarationBuilder &b)
|
|||||||
b.add_input<decl::Geometry>("Curve").supported_type(
|
b.add_input<decl::Geometry>("Curve").supported_type(
|
||||||
{GeometryComponent::Type::Curve, GeometryComponent::Type::GreasePencil});
|
{GeometryComponent::Type::Curve, GeometryComponent::Type::GreasePencil});
|
||||||
b.add_input<decl::Bool>("Selection").default_value(true).hide_value().field_on_all();
|
b.add_input<decl::Bool>("Selection").default_value(true).hide_value().field_on_all();
|
||||||
b.add_input<decl::Vector>("Normal").default_value({0.0f, 0.0f, 1.0f}).field_on_all();
|
b.add_input<decl::Vector>("Normal")
|
||||||
|
.default_value({0.0f, 0.0f, 1.0f})
|
||||||
|
.subtype(PROP_XYZ)
|
||||||
|
.field_on_all();
|
||||||
b.add_output<decl::Geometry>("Curve").propagate_all();
|
b.add_output<decl::Geometry>("Curve").propagate_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +241,7 @@ static PyObject *pygpu_vertbuf__tp_new(PyTypeObject * /*type*/, PyObject *args,
|
|||||||
PY_ARG_PARSER_HEAD_COMPAT()
|
PY_ARG_PARSER_HEAD_COMPAT()
|
||||||
"O!" /* `format` */
|
"O!" /* `format` */
|
||||||
"I" /* `len` */
|
"I" /* `len` */
|
||||||
":blender::gpu::VertBuf.__new__",
|
":GPUVertBuf.__new__",
|
||||||
_keywords,
|
_keywords,
|
||||||
nullptr,
|
nullptr,
|
||||||
};
|
};
|
||||||
@ -307,7 +307,7 @@ static PyObject *pygpu_vertbuf_attr_fill(BPyGPUVertBuf *self, PyObject *args, Py
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pygpu_vertbuf_fill(self->buf, id, data, "blender::gpu::VertBuf.attr_fill")) {
|
if (!pygpu_vertbuf_fill(self->buf, id, data, "GPUVertBuf.attr_fill")) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,7 +340,7 @@ static void pygpu_vertbuf__tp_dealloc(BPyGPUVertBuf *self)
|
|||||||
PyDoc_STRVAR(
|
PyDoc_STRVAR(
|
||||||
/* Wrap. */
|
/* Wrap. */
|
||||||
pygpu_vertbuf__tp_doc,
|
pygpu_vertbuf__tp_doc,
|
||||||
".. class:: blender::gpu::VertBuf(format, len)\n"
|
".. class:: GPUVertBuf(format, len)\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Contains a VBO.\n"
|
" Contains a VBO.\n"
|
||||||
"\n"
|
"\n"
|
||||||
@ -350,7 +350,7 @@ PyDoc_STRVAR(
|
|||||||
" :type len: int\n");
|
" :type len: int\n");
|
||||||
PyTypeObject BPyGPUVertBuf_Type = {
|
PyTypeObject BPyGPUVertBuf_Type = {
|
||||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||||
/*tp_name*/ "blender::gpu::VertBuf",
|
/*tp_name*/ "GPUVertBuf",
|
||||||
/*tp_basicsize*/ sizeof(BPyGPUVertBuf),
|
/*tp_basicsize*/ sizeof(BPyGPUVertBuf),
|
||||||
/*tp_itemsize*/ 0,
|
/*tp_itemsize*/ 0,
|
||||||
/*tp_dealloc*/ (destructor)pygpu_vertbuf__tp_dealloc,
|
/*tp_dealloc*/ (destructor)pygpu_vertbuf__tp_dealloc,
|
||||||
|
@ -1314,8 +1314,8 @@ bool RE_engine_gpu_context_enable(RenderEngine *engine)
|
|||||||
/* Activate RenderEngine System and Blender GPU Context. */
|
/* Activate RenderEngine System and Blender GPU Context. */
|
||||||
WM_system_gpu_context_activate(engine->system_gpu_context);
|
WM_system_gpu_context_activate(engine->system_gpu_context);
|
||||||
if (engine->blender_gpu_context) {
|
if (engine->blender_gpu_context) {
|
||||||
GPU_context_active_set(engine->blender_gpu_context);
|
|
||||||
GPU_render_begin();
|
GPU_render_begin();
|
||||||
|
GPU_context_active_set(engine->blender_gpu_context);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1330,8 +1330,8 @@ void RE_engine_gpu_context_disable(RenderEngine *engine)
|
|||||||
else {
|
else {
|
||||||
if (engine->system_gpu_context) {
|
if (engine->system_gpu_context) {
|
||||||
if (engine->blender_gpu_context) {
|
if (engine->blender_gpu_context) {
|
||||||
GPU_render_end();
|
|
||||||
GPU_context_active_set(nullptr);
|
GPU_context_active_set(nullptr);
|
||||||
|
GPU_render_end();
|
||||||
}
|
}
|
||||||
WM_system_gpu_context_release(engine->system_gpu_context);
|
WM_system_gpu_context_release(engine->system_gpu_context);
|
||||||
/* Restore DRW state context if previously active. */
|
/* Restore DRW state context if previously active. */
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 5038ad7165fd1a77e61e0d2d6efdadd6ea7c0dfb
|
Subproject commit bf5c70830540b215a3b1df21f28e0e80ead230f7
|
Loading…
Reference in New Issue
Block a user