forked from blender/blender
realize-depth #5
@ -1949,7 +1949,7 @@ if html_theme == "furo":
|
||||
"sidebar/scroll-start.html",
|
||||
"sidebar/navigation.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)
|
||||
endif()
|
||||
|
||||
if(WITH_COMPOSITOR_CPU)
|
||||
add_subdirectory(smaa_areatex)
|
||||
endif()
|
||||
|
||||
if(WITH_VULKAN_BACKEND)
|
||||
add_subdirectory(vulkan_memory_allocator)
|
||||
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);
|
||||
|
||||
void compile_thread_func(int thread_index);
|
||||
void compile_thread_func();
|
||||
|
||||
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) {
|
||||
|
||||
@ -309,7 +309,7 @@ void ShaderCache::load_kernel(DeviceKernel device_kernel,
|
||||
|
||||
metal_printf("Spawning %d Cycles kernel compilation threads\n", max_mtlcompiler_threads);
|
||||
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;
|
||||
} VolumeShaderCoefficients;
|
||||
|
||||
typedef struct EquiangularCoefficients {
|
||||
float3 P;
|
||||
float2 t_range;
|
||||
} EquiangularCoefficients;
|
||||
|
||||
/* Evaluate shader to get extinction coefficient at P. */
|
||||
ccl_device_inline bool shadow_volume_shader_sample(KernelGlobals kg,
|
||||
IntegratorShadowState state,
|
||||
@ -264,18 +269,18 @@ ccl_device void volume_shadow_heterogeneous(KernelGlobals kg,
|
||||
# define VOLUME_SAMPLE_PDF_CUTOFF 1e-8f
|
||||
|
||||
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,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const float tmin = ray->tmin;
|
||||
const float tmax = ray->tmax;
|
||||
const float delta = dot((light_P - ray->P), ray->D);
|
||||
const float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta);
|
||||
const float delta = dot((coeffs.P - ray->P), ray->D);
|
||||
const float D = safe_sqrtf(len_squared(coeffs.P - ray->P) - delta * delta);
|
||||
if (UNLIKELY(D == 0.0f)) {
|
||||
*pdf = 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_b = atan2f(tmax - delta, D);
|
||||
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,
|
||||
const float3 light_P,
|
||||
ccl_private const EquiangularCoefficients &coeffs,
|
||||
const float sample_t)
|
||||
{
|
||||
const float delta = dot((light_P - ray->P), ray->D);
|
||||
const float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta);
|
||||
const float delta = dot((coeffs.P - ray->P), ray->D);
|
||||
const float D = safe_sqrtf(len_squared(coeffs.P - ray->P) - delta * delta);
|
||||
if (UNLIKELY(D == 0.0f)) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
const float tmin = ray->tmin;
|
||||
const float tmax = ray->tmax;
|
||||
const float tmin = coeffs.t_range.x;
|
||||
const float tmax = coeffs.t_range.y;
|
||||
const float t_ = sample_t - delta;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 */
|
||||
|
||||
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_private const ShaderData *sd,
|
||||
ccl_private const Ray *ray,
|
||||
const float3 equiangular_light_P,
|
||||
ccl_private const EquiangularCoefficients &equiangular_coeffs,
|
||||
ccl_private const VolumeShaderCoefficients &ccl_restrict coeff,
|
||||
const Spectrum transmittance,
|
||||
ccl_private VolumeIntegrateState &ccl_restrict vstate,
|
||||
@ -474,7 +502,7 @@ ccl_device_forceinline void volume_integrate_step_scattering(
|
||||
|
||||
/* Multiple importance sampling. */
|
||||
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,
|
||||
equiangular_pdf);
|
||||
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,
|
||||
const float object_step_size,
|
||||
const VolumeSampleMethod direct_sample_method,
|
||||
const float3 equiangular_light_P,
|
||||
ccl_private const EquiangularCoefficients &equiangular_coeffs,
|
||||
ccl_private VolumeIntegrateResult &result)
|
||||
{
|
||||
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. */
|
||||
if (vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR) {
|
||||
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__
|
||||
result.direct_sample_method = vstate.direct_sample_method;
|
||||
@ -614,7 +642,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous(
|
||||
|
||||
/* Scattering and absorption. */
|
||||
volume_integrate_step_scattering(
|
||||
sd, ray, equiangular_light_P, coeff, transmittance, vstate, result);
|
||||
sd, ray, equiangular_coeffs, coeff, transmittance, vstate, result);
|
||||
}
|
||||
else {
|
||||
/* 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 ShaderData *ccl_restrict sd,
|
||||
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. */
|
||||
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 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,
|
||||
rand_light,
|
||||
sd->time,
|
||||
@ -708,9 +736,10 @@ ccl_device_forceinline bool integrate_volume_equiangular_sample_light(
|
||||
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
|
||||
@ -725,41 +754,26 @@ ccl_device_forceinline void integrate_volume_direct_light(
|
||||
# ifdef __PATH_GUIDING__
|
||||
ccl_private const Spectrum unlit_throughput,
|
||||
# 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);
|
||||
|
||||
if (!kernel_data.integrator.use_direct_light) {
|
||||
if (!kernel_data.integrator.use_direct_light || ls.emitter_id == EMITTER_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
/* Sample position on the same light again, now from the shading point where we scattered. */
|
||||
{
|
||||
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
|
||||
const uint bounce = INTEGRATOR_STATE(state, path, bounce);
|
||||
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,
|
||||
rng_state,
|
||||
rand_light,
|
||||
sd->time,
|
||||
P,
|
||||
zero_float3(),
|
||||
light_link_receiver_nee(kg, sd),
|
||||
SD_BSDF_HAS_TRANSMISSION,
|
||||
bounce,
|
||||
path_flag,
|
||||
&ls))
|
||||
if (!light_sample<false>(
|
||||
kg, rand_light, sd->time, P, N, object_receiver, shader_flags, bounce, path_flag, &ls))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -877,6 +891,7 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
|
||||
KernelGlobals kg,
|
||||
IntegratorState state,
|
||||
ccl_private ShaderData *sd,
|
||||
ccl_private const Ray *ray,
|
||||
ccl_private const RNGState *rng_state,
|
||||
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, D) = normalize(phase_wo);
|
||||
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;
|
||||
# ifdef __RAY_DIFFERENTIALS__
|
||||
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 */
|
||||
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(
|
||||
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. */
|
||||
/* 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);
|
||||
float3 equiangular_P = zero_float3();
|
||||
const bool have_equiangular_sample = need_light_sample &&
|
||||
integrate_volume_equiangular_sample_light(
|
||||
kg, state, ray, &sd, &rng_state, &equiangular_P);
|
||||
|
||||
EquiangularCoefficients equiangular_coeffs = {zero_float3(), make_float2(ray->tmin, ray->tmax)};
|
||||
|
||||
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) ?
|
||||
volume_stack_sample_method(kg, state) :
|
||||
@ -1023,7 +1044,7 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
|
||||
render_buffer,
|
||||
step_size,
|
||||
direct_sample_method,
|
||||
equiangular_P,
|
||||
equiangular_coeffs,
|
||||
result);
|
||||
|
||||
/* 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__
|
||||
unlit_throughput,
|
||||
# endif
|
||||
result.direct_throughput);
|
||||
result.direct_throughput,
|
||||
ls);
|
||||
}
|
||||
|
||||
/* Indirect light.
|
||||
@ -1130,7 +1152,7 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
|
||||
# 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;
|
||||
}
|
||||
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, dP, 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)
|
||||
|
||||
/*************************** Intersection result ******************************/
|
||||
|
@ -233,6 +233,11 @@ ccl_device bool area_light_spread_clamp_light(const float3 P,
|
||||
return true;
|
||||
}
|
||||
|
||||
ccl_device_forceinline bool area_light_is_ellipse(const ccl_global KernelAreaLight *light)
|
||||
{
|
||||
return light->invarea < 0.0f;
|
||||
}
|
||||
|
||||
/* Common API. */
|
||||
/* Compute `eval_fac` and `pdf`. Also sample a new position on the light if `sample_coord`. */
|
||||
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;
|
||||
|
||||
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. */
|
||||
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. */
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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>
|
||||
ccl_device_forceinline bool area_light_tree_parameters(const ccl_global KernelLight *klight,
|
||||
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)) +
|
||||
fabsf(dot(N, extentv)) >
|
||||
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
|
||||
|
@ -12,9 +12,9 @@ CCL_NAMESPACE_BEGIN
|
||||
|
||||
typedef struct LightSample {
|
||||
float3 P; /* position on light, or direction for distant light */
|
||||
float3 Ng; /* normal on light */
|
||||
float3 D; /* direction from shading point to light */
|
||||
packed_float3 Ng; /* normal on 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 pdf; /* pdf for selecting light and point on light */
|
||||
float pdf_selection; /* pdf for selecting light */
|
||||
@ -25,6 +25,7 @@ typedef struct LightSample {
|
||||
int lamp; /* lamp id */
|
||||
int group; /* lightgroup */
|
||||
LightType type; /* type of light */
|
||||
int emitter_id; /* index in the emitter array */
|
||||
} LightSample;
|
||||
|
||||
/* Utilities */
|
||||
|
@ -41,36 +41,14 @@ ccl_device int light_distribution_sample(KernelGlobals kg, const float rand)
|
||||
return index;
|
||||
}
|
||||
|
||||
template<bool in_volume_segment>
|
||||
ccl_device_noinline bool light_distribution_sample(KernelGlobals kg,
|
||||
const float3 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,
|
||||
const float rand,
|
||||
ccl_private LightSample *ls)
|
||||
{
|
||||
/* Sample light index from distribution. */
|
||||
/* The first two dimensions of the Sobol sequence have better stratification. */
|
||||
const int index = light_distribution_sample(kg, rand.z);
|
||||
const float pdf_selection = kernel_data.integrator.distribution_pdf_lights;
|
||||
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);
|
||||
ls->emitter_id = light_distribution_sample(kg, rand);
|
||||
ls->pdf_selection = kernel_data.integrator.distribution_pdf_lights;
|
||||
return true;
|
||||
}
|
||||
|
||||
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>
|
||||
ccl_device_noinline bool light_sample(KernelGlobals kg,
|
||||
const float2 rand,
|
||||
const float3 rand_light,
|
||||
const float time,
|
||||
const float3 P,
|
||||
const float3 N,
|
||||
@ -185,33 +185,31 @@ ccl_device_noinline bool light_sample(KernelGlobals kg,
|
||||
const int shader_flags,
|
||||
const int bounce,
|
||||
const uint32_t path_flag,
|
||||
const int emitter_index,
|
||||
const int object_id,
|
||||
const float pdf_selection,
|
||||
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;
|
||||
MeshLight mesh_light;
|
||||
#ifdef __LIGHT_TREE__
|
||||
if (kernel_data.integrator.use_light_tree) {
|
||||
ccl_global const KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters,
|
||||
emitter_index);
|
||||
ls->emitter_id);
|
||||
prim = kemitter->light.id;
|
||||
mesh_light.shader_flag = kemitter->mesh_light.shader_flag;
|
||||
mesh_light.object_id = object_id;
|
||||
mesh_light.object_id = ls->object;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
ccl_global const KernelLightDistribution *kdistribution = &kernel_data_fetch(
|
||||
light_distribution, emitter_index);
|
||||
light_distribution, ls->emitter_id);
|
||||
prim = kdistribution->prim;
|
||||
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) {
|
||||
/* Mesh light. */
|
||||
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,
|
||||
ccl_private LightSample *ls)
|
||||
{
|
||||
const int shader_flags = SD_BSDF_HAS_TRANSMISSION;
|
||||
|
||||
#ifdef __LIGHT_TREE__
|
||||
if (kernel_data.integrator.use_light_tree) {
|
||||
return light_tree_sample<true>(
|
||||
kg, rand, time, P, D, t, object_receiver, SD_BSDF_HAS_TRANSMISSION, bounce, path_flag, ls);
|
||||
if (!light_tree_sample<true>(kg, rand.z, P, D, t, object_receiver, shader_flags, ls)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
return light_distribution_sample<true>(
|
||||
kg, rand, time, P, D, object_receiver, SD_BSDF_HAS_TRANSMISSION, bounce, path_flag, ls);
|
||||
if (!light_distribution_sample(kg, rand.z, 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,
|
||||
@ -354,17 +362,24 @@ ccl_device bool light_sample_from_position(KernelGlobals kg,
|
||||
const uint32_t path_flag,
|
||||
ccl_private LightSample *ls)
|
||||
{
|
||||
/* Randomly select a light. */
|
||||
#ifdef __LIGHT_TREE__
|
||||
if (kernel_data.integrator.use_light_tree) {
|
||||
return light_tree_sample<false>(
|
||||
kg, rand, time, P, N, 0.0f, object_receiver, shader_flags, bounce, path_flag, ls);
|
||||
if (!light_tree_sample<false>(kg, rand.z, P, N, 0.0f, object_receiver, shader_flags, ls)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
return light_distribution_sample<false>(
|
||||
kg, rand, time, P, N, object_receiver, shader_flags, bounce, path_flag, ls);
|
||||
if (!light_distribution_sample(kg, rand.z, 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
|
||||
@ -415,13 +430,15 @@ ccl_device_inline float light_sample_mis_weight_forward_surface(KernelGlobals kg
|
||||
#ifdef __LIGHT_TREE__
|
||||
if (kernel_data.integrator.use_light_tree) {
|
||||
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);
|
||||
|
||||
uint lookup_offset = kernel_data_fetch(object_lookup_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);
|
||||
|
||||
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
|
||||
#endif
|
||||
@ -445,9 +462,11 @@ ccl_device_inline float light_sample_mis_weight_forward_lamp(KernelGlobals kg,
|
||||
#ifdef __LIGHT_TREE__
|
||||
if (kernel_data.integrator.use_light_tree) {
|
||||
const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n);
|
||||
const float dt = INTEGRATOR_STATE(state, ray, previous_dt);
|
||||
pdf *= light_tree_pdf(kg,
|
||||
P,
|
||||
N,
|
||||
dt,
|
||||
path_flag,
|
||||
0,
|
||||
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__
|
||||
if (kernel_data.integrator.use_light_tree) {
|
||||
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);
|
||||
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
|
||||
#endif
|
||||
|
@ -265,6 +265,24 @@ ccl_device_inline bool spot_light_sample_from_intersection(
|
||||
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>
|
||||
ccl_device_forceinline bool spot_light_tree_parameters(const ccl_global KernelLight *klight,
|
||||
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_max_incidence_angle = 1.0f;
|
||||
|
||||
/* When sampling the light tree for the second time in `shade_volume.h` and when query the pdf in
|
||||
* `sample.h`. */
|
||||
const bool in_volume = is_zero(N_or_D);
|
||||
if (!in_volume_segment && !in_volume) {
|
||||
if (!in_volume_segment) {
|
||||
const float3 N = N_or_D;
|
||||
const float cos_theta_i = has_transmission ? fabsf(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 /
|
||||
(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) {
|
||||
min_importance = max_importance;
|
||||
min_importance = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -270,10 +267,10 @@ ccl_device bool compute_emitter_centroid_and_dir(KernelGlobals kg,
|
||||
/* Arbitrary centroid and direction. */
|
||||
centroid = 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:
|
||||
dir = centroid;
|
||||
return !in_volume_segment;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -323,12 +320,13 @@ ccl_device void light_tree_node_importance(KernelGlobals kg,
|
||||
float cos_theta_u;
|
||||
float distance;
|
||||
if (knode->type == LIGHT_TREE_DISTANT) {
|
||||
if (in_volume_segment) {
|
||||
return;
|
||||
}
|
||||
point_to_centroid = -bcone.axis;
|
||||
cos_theta_u = fast_cosf(bcone.theta_o + bcone.theta_e);
|
||||
distance = 1.0f;
|
||||
if (t == FLT_MAX) {
|
||||
/* In world volume, distant light has no contribution. */
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
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. */
|
||||
distance = len(centroid - closest_point);
|
||||
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);
|
||||
}
|
||||
else {
|
||||
@ -697,17 +698,16 @@ ccl_device int light_tree_root_node_index(KernelGlobals kg, const int object_rec
|
||||
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>
|
||||
ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
|
||||
const float3 rand,
|
||||
const float time,
|
||||
const float rand,
|
||||
const float3 P,
|
||||
float3 N_or_D,
|
||||
float t,
|
||||
const int object_receiver,
|
||||
const int shader_flags,
|
||||
const int bounce,
|
||||
const uint32_t path_flag,
|
||||
ccl_private LightSample *ls)
|
||||
{
|
||||
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_selection = 1.0f;
|
||||
int selected_emitter = -1;
|
||||
int object_emitter = 0;
|
||||
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.z;
|
||||
float rand_selection = rand;
|
||||
|
||||
float3 local_P = P;
|
||||
|
||||
@ -743,7 +741,7 @@ ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@ -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_selection *= pdf_leaf;
|
||||
ls->emitter_id = selected_emitter;
|
||||
ls->pdf_selection = pdf_selection * pdf_leaf;
|
||||
|
||||
return light_sample<in_volume_segment>(kg,
|
||||
float3_to_float2(rand),
|
||||
time,
|
||||
P,
|
||||
N_or_D,
|
||||
object_receiver,
|
||||
shader_flags,
|
||||
bounce,
|
||||
path_flag,
|
||||
selected_emitter,
|
||||
object_emitter,
|
||||
pdf_selection,
|
||||
ls);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
float3 P,
|
||||
float3 N,
|
||||
const float dt,
|
||||
const int path_flag,
|
||||
const int object_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,
|
||||
index_emitter);
|
||||
int root_index;
|
||||
int subtree_root_index;
|
||||
uint bit_trail, target_emitter;
|
||||
|
||||
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);
|
||||
ccl_global const KernelLightTreeEmitter *kmesh = &kernel_data_fetch(light_tree_emitters,
|
||||
target_emitter);
|
||||
root_index = kmesh->mesh.node_id;
|
||||
ccl_global const KernelLightTreeNode *kroot = &kernel_data_fetch(light_tree_nodes, root_index);
|
||||
subtree_root_index = kmesh->mesh.node_id;
|
||||
ccl_global const KernelLightTreeNode *kroot = &kernel_data_fetch(light_tree_nodes,
|
||||
subtree_root_index);
|
||||
bit_trail = kroot->bit_trail;
|
||||
|
||||
if (kroot->type == LIGHT_TREE_INSTANCE) {
|
||||
root_index = kroot->instance.reference;
|
||||
subtree_root_index = kroot->instance.reference;
|
||||
}
|
||||
}
|
||||
else {
|
||||
root_index = 0;
|
||||
subtree_root_index = -1;
|
||||
bit_trail = kemitter->bit_trail;
|
||||
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++) {
|
||||
const int emitter = knode->leaf.first_emitter + i;
|
||||
float max_importance, min_importance;
|
||||
light_tree_emitter_importance<false>(
|
||||
kg, P, N, 0, has_transmission, emitter, max_importance, min_importance);
|
||||
light_tree_emitter_importance<in_volume_segment>(
|
||||
kg, P, N, dt, has_transmission, emitter, max_importance, min_importance);
|
||||
num_has_importance += (max_importance > 0);
|
||||
if (emitter == target_emitter) {
|
||||
target_max_importance = max_importance;
|
||||
@ -856,13 +846,13 @@ ccl_device float light_tree_pdf(KernelGlobals kg,
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (root_index) {
|
||||
if (subtree_root_index != -1) {
|
||||
/* Arrived at the mesh light. Continue with the subtree. */
|
||||
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;
|
||||
root_index = 0;
|
||||
node_index = subtree_root_index;
|
||||
subtree_root_index = -1;
|
||||
target_emitter = index_emitter;
|
||||
bit_trail = kemitter->bit_trail;
|
||||
continue;
|
||||
@ -877,8 +867,8 @@ ccl_device float light_tree_pdf(KernelGlobals kg,
|
||||
const int right_index = knode->inner.right_child;
|
||||
|
||||
float left_prob;
|
||||
if (!get_left_probability<false>(
|
||||
kg, P, N, 0, has_transmission, left_index, right_index, left_prob))
|
||||
if (!get_left_probability<in_volume_segment>(
|
||||
kg, P, N, dt, has_transmission, left_index, right_index, left_prob))
|
||||
{
|
||||
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
|
||||
|
@ -269,6 +269,26 @@ ccl_device_forceinline bool triangle_light_sample(KernelGlobals kg,
|
||||
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>
|
||||
ccl_device_forceinline bool triangle_light_tree_parameters(
|
||||
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 in_volume = is_zero(N);
|
||||
|
||||
return (front_facing && shape_above_surface) || in_volume;
|
||||
return front_facing && shape_above_surface;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
@ -45,6 +45,7 @@ CCL_NAMESPACE_BEGIN
|
||||
#define OBJECT_NONE (~0)
|
||||
#define PRIM_NONE (~0)
|
||||
#define LAMP_NONE (~0)
|
||||
#define EMITTER_NONE (~0)
|
||||
#define ID_NONE (0.0f)
|
||||
#define PASS_UNUSED (~0)
|
||||
#define LIGHTGROUP_NONE (~0)
|
||||
@ -1376,6 +1377,8 @@ typedef struct KernelSpotLight {
|
||||
int is_sphere;
|
||||
/* For non-uniform object scaling, the actual spread might be different. */
|
||||
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;
|
||||
|
||||
/* 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. */
|
||||
klights[light_index].spot.cos_half_larger_spread = inversesqrtf(
|
||||
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;
|
||||
|
@ -1030,6 +1030,46 @@ ccl_device_inline uint32_t reverse_integer_bits(uint32_t x)
|
||||
#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
|
||||
|
||||
#endif /* __UTIL_MATH_H__ */
|
||||
|
@ -302,6 +302,140 @@ ccl_device bool ray_quad_intersect(float3 ray_P,
|
||||
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
|
||||
|
||||
#endif /* __UTIL_MATH_INTERSECT_H__ */
|
||||
|
@ -161,6 +161,17 @@ ccl_device_inline Transform make_transform(float a,
|
||||
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)
|
||||
{
|
||||
float cx = cosf(euler.x);
|
||||
|
@ -1886,6 +1886,30 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
|
||||
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.
|
||||
* While postponing until after the buffer drawing is context is set
|
||||
* 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.
|
||||
sub = layout.column()
|
||||
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_unused", text="Remove Unused Collections")
|
||||
|
||||
|
@ -2187,6 +2187,7 @@ class VIEW3D_MT_paint_grease_pencil(Menu):
|
||||
layout.separator()
|
||||
|
||||
layout.menu("VIEW3D_MT_edit_greasepencil_showhide")
|
||||
layout.menu("VIEW3D_MT_edit_greasepencil_cleanup")
|
||||
|
||||
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
|
||||
|
||||
|
||||
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):
|
||||
bl_label = "Grease Pencil"
|
||||
|
||||
@ -5828,7 +5838,7 @@ class VIEW3D_MT_edit_greasepencil(Menu):
|
||||
|
||||
layout.menu("VIEW3D_MT_edit_greasepencil_showhide")
|
||||
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()
|
||||
|
||||
@ -7526,14 +7536,14 @@ class VIEW3D_PT_snapping(Panel):
|
||||
layout = self.layout
|
||||
col = layout.column()
|
||||
|
||||
col.label(text="Snap With")
|
||||
col.label(text="Snap Base")
|
||||
row = col.row(align=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.label(text="Snap Individual Elements To")
|
||||
col.label(text="Snap Target for Individual Elements")
|
||||
col.prop(tool_settings, "snap_elements_individual", expand=True)
|
||||
|
||||
col.separator()
|
||||
@ -8988,6 +8998,7 @@ classes = (
|
||||
VIEW3D_MT_edit_gpencil_delete,
|
||||
VIEW3D_MT_edit_gpencil_showhide,
|
||||
VIEW3D_MT_edit_greasepencil_showhide,
|
||||
VIEW3D_MT_edit_greasepencil_cleanup,
|
||||
VIEW3D_MT_weight_gpencil,
|
||||
VIEW3D_MT_gpencil_animation,
|
||||
VIEW3D_MT_gpencil_simplify,
|
||||
|
@ -253,54 +253,6 @@ static AttributeIDRef attribute_id_from_custom_data_layer(const CustomDataLayer
|
||||
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,
|
||||
const eCustomDataType data_type,
|
||||
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
|
||||
{
|
||||
if (stored_as_named_attribute_) {
|
||||
return CustomData_get_named_layer_index(&custom_data, stored_type_, name_) != -1;
|
||||
}
|
||||
return CustomData_has_layer(&custom_data, stored_type_);
|
||||
return CustomData_get_named_layer_index(&custom_data, data_type_, name_) != -1;
|
||||
}
|
||||
|
||||
GAttributeReader BuiltinCustomDataLayerProvider::try_get_for_read(const void *owner) const
|
||||
@ -416,13 +365,7 @@ GAttributeReader BuiltinCustomDataLayerProvider::try_get_for_read(const void *ow
|
||||
return {};
|
||||
}
|
||||
|
||||
int index;
|
||||
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_);
|
||||
}
|
||||
const int index = CustomData_get_named_layer_index(custom_data, data_type_, name_);
|
||||
if (index == -1) {
|
||||
return {};
|
||||
}
|
||||
@ -452,13 +395,7 @@ GAttributeWriter BuiltinCustomDataLayerProvider::try_get_for_write(void *owner)
|
||||
return {};
|
||||
}
|
||||
|
||||
void *data = nullptr;
|
||||
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);
|
||||
}
|
||||
void *data = CustomData_get_layer_named_for_write(custom_data, data_type_, name_, element_num);
|
||||
if (data == nullptr) {
|
||||
return {};
|
||||
}
|
||||
@ -475,57 +412,42 @@ bool BuiltinCustomDataLayerProvider::try_delete(void *owner) const
|
||||
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) {
|
||||
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 false;
|
||||
}
|
||||
|
||||
bool BuiltinCustomDataLayerProvider::try_create(void *owner,
|
||||
const AttributeInit &initializer) const
|
||||
{
|
||||
if (createable_ != Creatable) {
|
||||
return false;
|
||||
}
|
||||
CustomData *custom_data = custom_data_access_.get_custom_data(owner);
|
||||
if (custom_data == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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_)) {
|
||||
/* Exists already. */
|
||||
return false;
|
||||
}
|
||||
return add_custom_data_layer_from_attribute_init(
|
||||
name_, *custom_data, stored_type_, element_num, initializer);
|
||||
if (add_custom_data_layer_from_attribute_init(
|
||||
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;
|
||||
}
|
||||
|
||||
if (CustomData_get_layer(custom_data, stored_type_) != nullptr) {
|
||||
/* Exists already. */
|
||||
return false;
|
||||
}
|
||||
return add_builtin_type_custom_data_layer_from_init(
|
||||
*custom_data, stored_type_, element_num, initializer);
|
||||
}
|
||||
|
||||
bool BuiltinCustomDataLayerProvider::exists(const void *owner) const
|
||||
@ -534,10 +456,7 @@ bool BuiltinCustomDataLayerProvider::exists(const void *owner) const
|
||||
if (custom_data == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (stored_as_named_attribute_) {
|
||||
return CustomData_has_layer_named(custom_data, stored_type_, name_);
|
||||
}
|
||||
return CustomData_get_layer(custom_data, stored_type_) != nullptr;
|
||||
return CustomData_has_layer_named(custom_data, data_type_, name_);
|
||||
}
|
||||
|
||||
GAttributeReader CustomDataAttributeProvider::try_get_for_read(
|
||||
|
@ -31,14 +31,10 @@ struct CustomDataAccessInfo {
|
||||
* 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
|
||||
* 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 {
|
||||
public:
|
||||
/* Some utility enums to avoid hard to read booleans in function calls. */
|
||||
enum CreatableEnum {
|
||||
Creatable,
|
||||
NonCreatable,
|
||||
};
|
||||
enum DeletableEnum {
|
||||
Deletable,
|
||||
NonDeletable,
|
||||
@ -48,7 +44,6 @@ class BuiltinAttributeProvider {
|
||||
const std::string name_;
|
||||
const AttrDomain domain_;
|
||||
const eCustomDataType data_type_;
|
||||
const CreatableEnum createable_;
|
||||
const DeletableEnum deletable_;
|
||||
const AttributeValidator validator_;
|
||||
|
||||
@ -56,13 +51,11 @@ class BuiltinAttributeProvider {
|
||||
BuiltinAttributeProvider(std::string name,
|
||||
const AttrDomain domain,
|
||||
const eCustomDataType data_type,
|
||||
const CreatableEnum createable,
|
||||
const DeletableEnum deletable,
|
||||
AttributeValidator validator = {})
|
||||
: name_(std::move(name)),
|
||||
domain_(domain),
|
||||
data_type_(data_type),
|
||||
createable_(createable),
|
||||
deletable_(deletable),
|
||||
validator_(validator)
|
||||
{
|
||||
@ -174,27 +167,21 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
|
||||
*/
|
||||
class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
|
||||
using UpdateOnChange = void (*)(void *owner);
|
||||
const eCustomDataType stored_type_;
|
||||
const CustomDataAccessInfo custom_data_access_;
|
||||
const UpdateOnChange update_on_change_;
|
||||
bool stored_as_named_attribute_;
|
||||
|
||||
public:
|
||||
BuiltinCustomDataLayerProvider(std::string attribute_name,
|
||||
const AttrDomain domain,
|
||||
const eCustomDataType attribute_type,
|
||||
const eCustomDataType stored_type,
|
||||
const CreatableEnum creatable,
|
||||
const eCustomDataType data_type,
|
||||
const DeletableEnum deletable,
|
||||
const CustomDataAccessInfo custom_data_access,
|
||||
const UpdateOnChange update_on_write,
|
||||
const UpdateOnChange update_on_change,
|
||||
const AttributeValidator validator = {})
|
||||
: BuiltinAttributeProvider(
|
||||
std::move(attribute_name), domain, attribute_type, creatable, deletable, validator),
|
||||
stored_type_(stored_type),
|
||||
std::move(attribute_name), domain, data_type, deletable, validator),
|
||||
custom_data_access_(custom_data_access),
|
||||
update_on_change_(update_on_write),
|
||||
stored_as_named_attribute_(data_type_ == stored_type_)
|
||||
update_on_change_(update_on_change)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -972,9 +972,14 @@ static std::shared_ptr<DictionaryValue> serialize_geometry_set(const GeometrySet
|
||||
|
||||
auto io_references = io_instances->append_array("references");
|
||||
for (const InstanceReference &reference : instances.references()) {
|
||||
BLI_assert(reference.type() == InstanceReference::Type::GeometrySet);
|
||||
io_references->append(
|
||||
serialize_geometry_set(reference.geometry_set(), blob_writer, blob_sharing));
|
||||
if (reference.type() == InstanceReference::Type::GeometrySet) {
|
||||
const GeometrySet &geometry = reference.geometry_set();
|
||||
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(
|
||||
|
@ -58,6 +58,8 @@ CurvesGeometry::CurvesGeometry() : CurvesGeometry(0, 0) {}
|
||||
|
||||
CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num)
|
||||
{
|
||||
this->runtime = MEM_new<CurvesGeometryRuntime>(__func__);
|
||||
|
||||
this->point_num = point_num;
|
||||
this->curve_num = curve_num;
|
||||
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>(
|
||||
"position", AttrDomain::Point, AttributeInitConstruct());
|
||||
|
||||
this->runtime = MEM_new<CurvesGeometryRuntime>(__func__);
|
||||
|
||||
if (curve_num > 0) {
|
||||
this->curve_offsets = static_cast<int *>(
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
@ -476,8 +476,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
static BuiltinCustomDataLayerProvider position("position",
|
||||
AttrDomain::Point,
|
||||
CD_PROP_FLOAT3,
|
||||
CD_PROP_FLOAT3,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
point_access,
|
||||
tag_component_positions_changed);
|
||||
@ -485,8 +483,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
static BuiltinCustomDataLayerProvider radius("radius",
|
||||
AttrDomain::Point,
|
||||
CD_PROP_FLOAT,
|
||||
CD_PROP_FLOAT,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
point_access,
|
||||
tag_component_radii_changed);
|
||||
@ -494,8 +490,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
static BuiltinCustomDataLayerProvider id("id",
|
||||
AttrDomain::Point,
|
||||
CD_PROP_INT32,
|
||||
CD_PROP_INT32,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
point_access,
|
||||
nullptr);
|
||||
@ -503,8 +497,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
static BuiltinCustomDataLayerProvider tilt("tilt",
|
||||
AttrDomain::Point,
|
||||
CD_PROP_FLOAT,
|
||||
CD_PROP_FLOAT,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
point_access,
|
||||
tag_component_normals_changed);
|
||||
@ -512,8 +504,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
static BuiltinCustomDataLayerProvider handle_right("handle_right",
|
||||
AttrDomain::Point,
|
||||
CD_PROP_FLOAT3,
|
||||
CD_PROP_FLOAT3,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
point_access,
|
||||
tag_component_positions_changed);
|
||||
@ -521,8 +511,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
static BuiltinCustomDataLayerProvider handle_left("handle_left",
|
||||
AttrDomain::Point,
|
||||
CD_PROP_FLOAT3,
|
||||
CD_PROP_FLOAT3,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
point_access,
|
||||
tag_component_positions_changed);
|
||||
@ -536,8 +524,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
static BuiltinCustomDataLayerProvider handle_type_right("handle_type_right",
|
||||
AttrDomain::Point,
|
||||
CD_PROP_INT8,
|
||||
CD_PROP_INT8,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
point_access,
|
||||
tag_component_topology_changed,
|
||||
@ -546,8 +532,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
static BuiltinCustomDataLayerProvider handle_type_left("handle_type_left",
|
||||
AttrDomain::Point,
|
||||
CD_PROP_INT8,
|
||||
CD_PROP_INT8,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
point_access,
|
||||
tag_component_topology_changed,
|
||||
@ -556,21 +540,17 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
static BuiltinCustomDataLayerProvider nurbs_weight("nurbs_weight",
|
||||
AttrDomain::Point,
|
||||
CD_PROP_FLOAT,
|
||||
CD_PROP_FLOAT,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
point_access,
|
||||
tag_component_positions_changed);
|
||||
|
||||
static const auto nurbs_order_clamp = mf::build::SI1_SO<int8_t, int8_t>(
|
||||
"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());
|
||||
static BuiltinCustomDataLayerProvider nurbs_order("nurbs_order",
|
||||
AttrDomain::Curve,
|
||||
CD_PROP_INT8,
|
||||
CD_PROP_INT8,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
curve_access,
|
||||
tag_component_topology_changed,
|
||||
@ -585,8 +565,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
static BuiltinCustomDataLayerProvider normal_mode("normal_mode",
|
||||
AttrDomain::Curve,
|
||||
CD_PROP_INT8,
|
||||
CD_PROP_INT8,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
curve_access,
|
||||
tag_component_normals_changed,
|
||||
@ -595,8 +573,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
static BuiltinCustomDataLayerProvider custom_normal("custom_normal",
|
||||
AttrDomain::Point,
|
||||
CD_PROP_FLOAT3,
|
||||
CD_PROP_FLOAT3,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
point_access,
|
||||
tag_component_normals_changed);
|
||||
@ -610,8 +586,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
static BuiltinCustomDataLayerProvider nurbs_knots_mode("knots_mode",
|
||||
AttrDomain::Curve,
|
||||
CD_PROP_INT8,
|
||||
CD_PROP_INT8,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
curve_access,
|
||||
tag_component_topology_changed,
|
||||
@ -626,8 +600,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
static BuiltinCustomDataLayerProvider curve_type("curve_type",
|
||||
AttrDomain::Curve,
|
||||
CD_PROP_INT8,
|
||||
CD_PROP_INT8,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
curve_access,
|
||||
tag_component_curve_types_changed,
|
||||
@ -640,8 +612,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
static BuiltinCustomDataLayerProvider resolution("resolution",
|
||||
AttrDomain::Curve,
|
||||
CD_PROP_INT32,
|
||||
CD_PROP_INT32,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
curve_access,
|
||||
tag_component_topology_changed,
|
||||
@ -650,8 +620,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
|
||||
static BuiltinCustomDataLayerProvider cyclic("cyclic",
|
||||
AttrDomain::Curve,
|
||||
CD_PROP_BOOL,
|
||||
CD_PROP_BOOL,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
curve_access,
|
||||
tag_component_topology_changed);
|
||||
|
@ -133,8 +133,6 @@ static ComponentAttributeProviders create_attribute_providers_for_instances()
|
||||
static BuiltinCustomDataLayerProvider id("id",
|
||||
AttrDomain::Instance,
|
||||
CD_PROP_INT32,
|
||||
CD_PROP_INT32,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
instance_custom_data_access,
|
||||
nullptr);
|
||||
@ -142,8 +140,6 @@ static ComponentAttributeProviders create_attribute_providers_for_instances()
|
||||
static BuiltinCustomDataLayerProvider instance_transform("instance_transform",
|
||||
AttrDomain::Instance,
|
||||
CD_PROP_FLOAT4X4,
|
||||
CD_PROP_FLOAT4X4,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
instance_custom_data_access,
|
||||
nullptr);
|
||||
@ -152,8 +148,6 @@ static ComponentAttributeProviders create_attribute_providers_for_instances()
|
||||
static BuiltinCustomDataLayerProvider reference_index(".reference_index",
|
||||
AttrDomain::Instance,
|
||||
CD_PROP_INT32,
|
||||
CD_PROP_INT32,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
instance_custom_data_access,
|
||||
tag_component_reference_index_changed);
|
||||
|
@ -1010,8 +1010,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
static BuiltinCustomDataLayerProvider position("position",
|
||||
AttrDomain::Point,
|
||||
CD_PROP_FLOAT3,
|
||||
CD_PROP_FLOAT3,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
point_access,
|
||||
tag_component_positions_changed);
|
||||
@ -1019,8 +1017,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
static BuiltinCustomDataLayerProvider id("id",
|
||||
AttrDomain::Point,
|
||||
CD_PROP_INT32,
|
||||
CD_PROP_INT32,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
point_access,
|
||||
nullptr);
|
||||
@ -1035,8 +1031,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
static BuiltinCustomDataLayerProvider material_index("material_index",
|
||||
AttrDomain::Face,
|
||||
CD_PROP_INT32,
|
||||
CD_PROP_INT32,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
face_access,
|
||||
nullptr,
|
||||
@ -1049,8 +1043,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
static BuiltinCustomDataLayerProvider edge_verts(".edge_verts",
|
||||
AttrDomain::Edge,
|
||||
CD_PROP_INT32_2D,
|
||||
CD_PROP_INT32_2D,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
edge_access,
|
||||
nullptr,
|
||||
@ -1065,8 +1057,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
static BuiltinCustomDataLayerProvider corner_vert(".corner_vert",
|
||||
AttrDomain::Corner,
|
||||
CD_PROP_INT32,
|
||||
CD_PROP_INT32,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
corner_access,
|
||||
nullptr,
|
||||
@ -1074,8 +1064,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
static BuiltinCustomDataLayerProvider corner_edge(".corner_edge",
|
||||
AttrDomain::Corner,
|
||||
CD_PROP_INT32,
|
||||
CD_PROP_INT32,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
corner_access,
|
||||
nullptr,
|
||||
@ -1084,8 +1072,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
static BuiltinCustomDataLayerProvider sharp_face("sharp_face",
|
||||
AttrDomain::Face,
|
||||
CD_PROP_BOOL,
|
||||
CD_PROP_BOOL,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
face_access,
|
||||
tag_component_sharpness_changed);
|
||||
@ -1093,8 +1079,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
|
||||
static BuiltinCustomDataLayerProvider sharp_edge("sharp_edge",
|
||||
AttrDomain::Edge,
|
||||
CD_PROP_BOOL,
|
||||
CD_PROP_BOOL,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
edge_access,
|
||||
tag_component_sharpness_changed);
|
||||
|
@ -147,24 +147,18 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
|
||||
static BuiltinCustomDataLayerProvider position("position",
|
||||
AttrDomain::Point,
|
||||
CD_PROP_FLOAT3,
|
||||
CD_PROP_FLOAT3,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
point_access,
|
||||
tag_component_positions_changed);
|
||||
static BuiltinCustomDataLayerProvider radius("radius",
|
||||
AttrDomain::Point,
|
||||
CD_PROP_FLOAT,
|
||||
CD_PROP_FLOAT,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
point_access,
|
||||
tag_component_radius_changed);
|
||||
static BuiltinCustomDataLayerProvider id("id",
|
||||
AttrDomain::Point,
|
||||
CD_PROP_INT32,
|
||||
CD_PROP_INT32,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
point_access,
|
||||
nullptr);
|
||||
|
@ -62,11 +62,11 @@ static void pointcloud_init_data(ID *id)
|
||||
|
||||
MEMCPY_STRUCT_AFTER(pointcloud, DNA_struct_default_get(PointCloud), id);
|
||||
|
||||
pointcloud->runtime = new blender::bke::PointCloudRuntime();
|
||||
|
||||
CustomData_reset(&pointcloud->pdata);
|
||||
pointcloud->attributes_for_write().add<float3>(
|
||||
"position", blender::bke::AttrDomain::Point, blender::bke::AttributeInitConstruct());
|
||||
|
||||
pointcloud->runtime = new blender::bke::PointCloudRuntime();
|
||||
}
|
||||
|
||||
static void pointcloud_copy_data(Main * /*bmain*/,
|
||||
|
@ -584,20 +584,6 @@ if(WITH_COMPOSITOR_CPU)
|
||||
${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)
|
||||
add_definitions(-DWITH_OPENIMAGEDENOISE)
|
||||
add_definitions(-DOIDN_STATIC_LIB)
|
||||
|
@ -7,37 +7,41 @@
|
||||
|
||||
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,
|
||||
const CompositorContext & /*context*/) const
|
||||
{
|
||||
const bNode *node = this->get_bnode();
|
||||
const NodeAntiAliasingData *data = (const NodeAntiAliasingData *)node->storage;
|
||||
|
||||
/* Edge Detection (First Pass) */
|
||||
SMAAEdgeDetectionOperation *operation1 = nullptr;
|
||||
SMAAOperation *operation = new SMAAOperation();
|
||||
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();
|
||||
operation1->set_threshold(data->threshold);
|
||||
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());
|
||||
converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0));
|
||||
converter.map_output_socket(get_output_socket(0), operation->get_output_socket());
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
@ -18,28 +18,13 @@ void CornerPinNode::convert_to_operations(NodeConverter &converter,
|
||||
PlaneCornerPinMaskOperation *plane_mask_operation = new PlaneCornerPinMaskOperation();
|
||||
converter.add_operation(plane_mask_operation);
|
||||
|
||||
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
|
||||
converter.add_operation(smaa_edge_detection);
|
||||
SMAAOperation *smaa_operation = new SMAAOperation();
|
||||
converter.add_operation(smaa_operation);
|
||||
|
||||
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 =
|
||||
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());
|
||||
converter.map_output_socket(this->get_output_socket(1), smaa_operation->get_output_socket());
|
||||
|
||||
PlaneCornerPinWarpImageOperation *warp_image_operation = new PlaneCornerPinWarpImageOperation();
|
||||
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_link(warp_image_operation->get_output_socket(),
|
||||
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));
|
||||
converter.map_output_socket(this->get_output_socket(0),
|
||||
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));
|
||||
|
||||
if (editor_node->custom3 < 2.0f) {
|
||||
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
|
||||
converter.add_operation(smaa_edge_detection);
|
||||
|
||||
converter.add_link(operation->get_output_socket(), 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(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());
|
||||
SMAAOperation *smaa_operation = new SMAAOperation();
|
||||
converter.add_operation(smaa_operation);
|
||||
converter.add_link(operation->get_output_socket(), smaa_operation->get_input_socket(0));
|
||||
converter.map_output_socket(get_output_socket(0), smaa_operation->get_output_socket());
|
||||
}
|
||||
else {
|
||||
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));
|
||||
}
|
||||
else {
|
||||
SMAAEdgeDetectionOperation *operation1 = nullptr;
|
||||
|
||||
operation1 = new SMAAEdgeDetectionOperation();
|
||||
converter.add_operation(operation1);
|
||||
|
||||
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());
|
||||
SMAAOperation *smaa_operation = new SMAAOperation();
|
||||
converter.add_operation(smaa_operation);
|
||||
converter.add_link(operation->get_output_socket(0), smaa_operation->get_input_socket(0));
|
||||
converter.map_output_socket(get_output_socket(0), smaa_operation->get_output_socket());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,28 +35,13 @@ void PlaneTrackDeformNode::convert_to_operations(NodeConverter &converter,
|
||||
}
|
||||
converter.add_operation(plane_mask_operation);
|
||||
|
||||
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
|
||||
converter.add_operation(smaa_edge_detection);
|
||||
SMAAOperation *smaa_operation = new SMAAOperation();
|
||||
converter.add_operation(smaa_operation);
|
||||
|
||||
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 =
|
||||
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());
|
||||
converter.map_output_socket(this->get_output_socket(1), smaa_operation->get_output_socket());
|
||||
|
||||
PlaneTrackWarpImageOperation *warp_image_operation = new PlaneTrackWarpImageOperation();
|
||||
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_link(warp_image_operation->get_output_socket(),
|
||||
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));
|
||||
converter.map_output_socket(this->get_output_socket(0),
|
||||
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));
|
||||
|
||||
/* Step 2 anti alias mask bit of an expensive operation, but does the trick. */
|
||||
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
|
||||
converter.add_operation(smaa_edge_detection);
|
||||
SMAAOperation *smaa_operation = new SMAAOperation();
|
||||
converter.add_operation(smaa_operation);
|
||||
|
||||
converter.add_link(maskoperation->get_output_socket(),
|
||||
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));
|
||||
converter.add_link(maskoperation->get_output_socket(), smaa_operation->get_input_socket(0));
|
||||
|
||||
/* use mask to blend between the input colors. */
|
||||
ZCombineMaskOperation *zcombineoperation = this->get_bnode()->custom1 ?
|
||||
@ -80,7 +65,7 @@ void ZCombineNode::convert_to_operations(NodeConverter &converter,
|
||||
new ZCombineMaskOperation();
|
||||
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));
|
||||
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));
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,87 +4,36 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
#include "COM_NodeOperation.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Edge Detection (First Pass) */
|
||||
|
||||
class SMAAEdgeDetectionOperation : public MultiThreadedOperation {
|
||||
class SMAAOperation : public NodeOperation {
|
||||
protected:
|
||||
float threshold_;
|
||||
float contrast_limit_;
|
||||
float threshold_ = 0.1f;
|
||||
float local_contrast_adaptation_factor_ = 2.0f;
|
||||
int corner_rounding_ = 25;
|
||||
|
||||
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 update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
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,
|
||||
void update_memory_buffer(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs) override;
|
||||
};
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include "COM_ConstantOperation.h"
|
||||
#include "COM_MultiThreadedOperation.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
class TranslateOperation : public MultiThreadedOperation {
|
||||
@ -21,6 +23,8 @@ class TranslateOperation : public MultiThreadedOperation {
|
||||
bool is_delta_set_;
|
||||
bool is_relative_;
|
||||
|
||||
std::mutex mutex_;
|
||||
|
||||
protected:
|
||||
MemoryBufferExtend x_extend_mode_;
|
||||
MemoryBufferExtend y_extend_mode_;
|
||||
@ -50,6 +54,11 @@ class TranslateOperation : public MultiThreadedOperation {
|
||||
inline void ensure_delta()
|
||||
{
|
||||
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_y_ = get_input_operation(Y_INPUT_INDEX)->get_constant_value_default(0.0f);
|
||||
if (get_is_relative()) {
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "IMB_colormanagement.hh"
|
||||
#include "IMB_imbuf.hh"
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
||||
@ -56,74 +57,6 @@ bool operator==(const CachedImageKey &a, const CachedImageKey &b)
|
||||
* 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. */
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
Image *image,
|
||||
ImageUser *image_user,
|
||||
@ -227,34 +210,12 @@ CachedImage::CachedImage(Context &context,
|
||||
context, image, image_user, pass_name);
|
||||
|
||||
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);
|
||||
texture_ = IMB_create_gpu_texture("Image Texture", image_buffer, true, is_premultiplied);
|
||||
ImBuf *linear_image_buffer = compute_linear_buffer(image_buffer);
|
||||
|
||||
texture_ = IMB_create_gpu_texture("Image Texture", linear_image_buffer, true, true);
|
||||
GPU_texture_update_mipmap_chain(texture_);
|
||||
|
||||
const eGPUTextureFormat original_format = GPU_texture_format(texture_);
|
||||
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");
|
||||
}
|
||||
|
||||
IMB_freeImBuf(linear_image_buffer);
|
||||
BKE_image_release_ibuf(image, image_buffer, nullptr);
|
||||
}
|
||||
|
||||
|
@ -3674,11 +3674,11 @@ size_t ANIM_animdata_filter(bAnimContext *ac,
|
||||
void *data,
|
||||
eAnimCont_Types datatype)
|
||||
{
|
||||
size_t items = 0;
|
||||
if (!data || !anim_data) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* only filter data if there's somewhere to put it */
|
||||
if (data && anim_data) {
|
||||
/* firstly filter the data */
|
||||
size_t items = 0;
|
||||
switch (datatype) {
|
||||
/* Action-Editing Modes */
|
||||
case ANIMCONT_ACTION: /* 'Action Editor' */
|
||||
@ -3803,9 +3803,7 @@ size_t ANIM_animdata_filter(bAnimContext *ac,
|
||||
if (filter_mode & ANIMFILTER_NODUPLIS) {
|
||||
items = animdata_filter_remove_duplis(anim_data);
|
||||
}
|
||||
}
|
||||
|
||||
/* return the number of items in the list */
|
||||
return items;
|
||||
}
|
||||
|
||||
|
@ -582,7 +582,7 @@ static void ASSET_OT_catalog_delete(wmOperatorType *ot)
|
||||
static asset_system::AssetCatalogService *get_catalog_service(bContext *C)
|
||||
{
|
||||
const SpaceFile *sfile = CTX_wm_space_file(C);
|
||||
if (!sfile) {
|
||||
if (!sfile || ED_fileselect_is_file_browser(sfile)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -1406,7 +1406,7 @@ static void GREASE_PENCIL_OT_clean_loose(wmOperatorType *ot)
|
||||
|
||||
ot->invoke = WM_operator_props_popup_confirm;
|
||||
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;
|
||||
|
||||
|
@ -53,8 +53,8 @@ static int cachefile_open_invoke(bContext *C, wmOperator *op, const wmEvent * /*
|
||||
char filepath[FILE_MAX];
|
||||
Main *bmain = CTX_data_main(C);
|
||||
|
||||
STRNCPY(filepath, BKE_main_blendfile_path(bmain));
|
||||
BLI_path_extension_replace(filepath, sizeof(filepath), ".abc");
|
||||
/* Default to the same directory as the blend file. */
|
||||
BLI_path_split_dir_part(BKE_main_blendfile_path(bmain), filepath, sizeof(filepath));
|
||||
RNA_string_set(op->ptr, "filepath", filepath);
|
||||
}
|
||||
|
||||
@ -119,7 +119,7 @@ void CACHEFILE_OT_open(wmOperatorType *ot)
|
||||
ot->cancel = open_cancel;
|
||||
|
||||
WM_operator_properties_filesel(ot,
|
||||
FILE_TYPE_ALEMBIC | FILE_TYPE_FOLDER,
|
||||
FILE_TYPE_ALEMBIC | FILE_TYPE_USD | FILE_TYPE_FOLDER,
|
||||
FILE_BLENDER,
|
||||
FILE_OPENFILE,
|
||||
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];
|
||||
Main *bmain = CTX_data_main(C);
|
||||
|
||||
STRNCPY(filepath, BKE_main_blendfile_path(bmain));
|
||||
BLI_path_extension_replace(filepath, sizeof(filepath), ".abc");
|
||||
/* Default to the same directory as the blend file. */
|
||||
BLI_path_split_dir_part(BKE_main_blendfile_path(bmain), filepath, sizeof(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;
|
||||
|
||||
WM_operator_properties_filesel(ot,
|
||||
FILE_TYPE_ALEMBIC | FILE_TYPE_FOLDER,
|
||||
FILE_TYPE_ALEMBIC | FILE_TYPE_USD | FILE_TYPE_FOLDER,
|
||||
FILE_BLENDER,
|
||||
FILE_OPENFILE,
|
||||
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;
|
||||
}
|
||||
|
@ -1613,7 +1613,6 @@ static int shade_smooth_exec(bContext *C, wmOperator *op)
|
||||
const float angle = RNA_float_get(op->ptr, "angle");
|
||||
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);
|
||||
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. */
|
||||
Base *base = nullptr;
|
||||
Object *obedit = nullptr;
|
||||
{
|
||||
if (object_index != -1) {
|
||||
Vector<Base *> bases = BKE_view_layer_array_from_bases_in_edit_mode(
|
||||
scene, view_layer, CTX_wm_view3d(C));
|
||||
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", "");
|
||||
|
||||
/* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid previous ambiguity of
|
||||
* "target" (now, "source" is geometry to be moved and "target" is geometry to which moved
|
||||
* geometry is snapped). Use "Source snap point" and "Point on source that will snap to
|
||||
* target" for name and description, respectively. */
|
||||
prop = RNA_def_enum(ot->srna, "snap_target", rna_enum_snap_source_items, 0, "Snap With", "");
|
||||
/* TODO(@gfxcoder): Rename `snap_target` to `snap_base` to avoid previous ambiguity of
|
||||
* "target" (now, "base" or "source" is geometry to be moved and "target" is geometry to
|
||||
* which moved geometry is snapped). */
|
||||
prop = RNA_def_enum(ot->srna,
|
||||
"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);
|
||||
|
||||
/* Target selection. */
|
||||
|
@ -13,6 +13,6 @@ bke::GeometrySet join_geometries(Span<bke::GeometrySet> geometries,
|
||||
const bke::AnonymousAttributePropagationInfo &propagation_info);
|
||||
|
||||
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
|
||||
|
@ -62,16 +62,8 @@ bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
|
||||
const RealizeInstancesOptions &options);
|
||||
|
||||
/**
|
||||
* Join all instances into a single geometry component for each geometry type. For example, all
|
||||
* mesh instances (including the already realized mesh) are joined into a single mesh. The output
|
||||
* 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.
|
||||
* Same #realize_instances but will realize only the instances chosen by
|
||||
* varied_depth_option to there chosen depth.
|
||||
*/
|
||||
bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
|
||||
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,
|
||||
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,
|
||||
ignored_attributes);
|
||||
@ -129,7 +129,7 @@ static void join_instances(const Span<const GeometryComponent *> src_components,
|
||||
|
||||
r_result.replace_instances(dst_instances.release());
|
||||
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*/,
|
||||
|
@ -298,7 +298,7 @@ struct InstanceContext {
|
||||
curves(gather_info.curves.attributes.size()),
|
||||
instances(gather_info.instances_attriubutes.size())
|
||||
{
|
||||
//empty
|
||||
// empty
|
||||
}
|
||||
};
|
||||
|
||||
@ -823,12 +823,11 @@ static bool attribute_foreach(const bke::GeometrySet &geometry_set,
|
||||
return is_relevant;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Based on bke::GeometrySet::gather_attributes_for_propagation.
|
||||
* Specialized for Specialized attribute_foreach to get:
|
||||
* current_depth, depth_target, instance_depth and selection.
|
||||
*/
|
||||
*/
|
||||
void static gather_attributes_for_propagation(
|
||||
bke::GeometrySet re_geometry_set,
|
||||
const Span<bke::GeometryComponent::Type> component_types,
|
||||
@ -869,7 +868,8 @@ void static gather_attributes_for_propagation(
|
||||
|
||||
AttrDomain domain = meta_data.domain;
|
||||
if (dst_component_type != bke::GeometryComponent::Type::Instance &&
|
||||
domain == AttrDomain::Instance) {
|
||||
domain == AttrDomain::Instance)
|
||||
{
|
||||
domain = AttrDomain::Point;
|
||||
}
|
||||
|
||||
@ -1014,7 +1014,7 @@ static void execute_instances_tasks(
|
||||
}
|
||||
|
||||
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;
|
||||
all_instances.depths = VArray<int>::ForSingle(VariedDepthOption::MAX_DEPTH,
|
||||
geometry_set.get_instances()->instances_num());
|
||||
IndexMaskMemory memory;
|
||||
all_instances.selection = IndexMask::from_bools(
|
||||
VArray<bool>::ForSingle(true, geometry_set.get_instances()->instances_num()), memory);
|
||||
all_instances.selection = IndexMask(geometry_set.get_instances()->instances_num());
|
||||
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();
|
||||
InstanceContext attribute_fallbacks(gather_info);
|
||||
|
||||
gather_realize_tasks_recursive(gather_info,
|
||||
0,
|
||||
VariedDepthOption::MAX_DEPTH,
|
||||
geometry_set,
|
||||
transform,
|
||||
attribute_fallbacks);
|
||||
gather_realize_tasks_recursive(
|
||||
gather_info, 0, VariedDepthOption::MAX_DEPTH, geometry_set, transform, attribute_fallbacks);
|
||||
|
||||
bke::GeometrySet new_geometry_set;
|
||||
execute_instances_tasks(gather_info.instances.instances_components_to_merge,
|
||||
|
@ -527,6 +527,8 @@ void gpu::MTLTexture::update_sub(
|
||||
}
|
||||
}
|
||||
|
||||
const bool is_compressed = (format_flag_ & GPU_FORMAT_COMPRESSED);
|
||||
|
||||
@autoreleasepool {
|
||||
/* Determine totalsize of INPUT Data. */
|
||||
int num_channels = to_component_len(format_);
|
||||
@ -593,10 +595,12 @@ void gpu::MTLTexture::update_sub(
|
||||
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;
|
||||
if (expected_dst_bytes_per_pixel != input_bytes_per_pixel ||
|
||||
num_channels != destination_num_channels)
|
||||
if (!is_compressed && (expected_dst_bytes_per_pixel != input_bytes_per_pixel ||
|
||||
num_channels != destination_num_channels))
|
||||
{
|
||||
can_use_direct_blit = false;
|
||||
}
|
||||
@ -620,7 +624,7 @@ void gpu::MTLTexture::update_sub(
|
||||
|
||||
/* Safety Checks. */
|
||||
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 &&
|
||||
"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] :
|
||||
ctx->pipeline_state.unpack_row_length);
|
||||
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);
|
||||
for (int array_index = 0; array_index < max_array_index; array_index++) {
|
||||
|
||||
@ -827,6 +837,13 @@ void gpu::MTLTexture::update_sub(
|
||||
extent[0] :
|
||||
ctx->pipeline_state.unpack_row_length);
|
||||
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;
|
||||
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()
|
||||
{
|
||||
/* 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. */
|
||||
MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
|
||||
BLI_assert(ctx);
|
||||
|
@ -160,13 +160,17 @@ MTLPixelFormat gpu_texture_format_to_metal(eGPUTextureFormat tex_format)
|
||||
return MTLPixelFormatR8Snorm;
|
||||
/* Special formats, texture only. */
|
||||
case GPU_SRGB8_A8_DXT1:
|
||||
return MTLPixelFormatBC1_RGBA_sRGB;
|
||||
case GPU_SRGB8_A8_DXT3:
|
||||
return MTLPixelFormatBC2_RGBA_sRGB;
|
||||
case GPU_SRGB8_A8_DXT5:
|
||||
return MTLPixelFormatBC3_RGBA_sRGB;
|
||||
case GPU_RGBA8_DXT1:
|
||||
return MTLPixelFormatBC1_RGBA;
|
||||
case GPU_RGBA8_DXT3:
|
||||
return MTLPixelFormatBC2_RGBA;
|
||||
case GPU_RGBA8_DXT5:
|
||||
BLI_assert_msg(false, "Compressed texture not implemented yet!\n");
|
||||
return MTLPixelFormatRGBA8Unorm;
|
||||
return MTLPixelFormatBC3_RGBA;
|
||||
case GPU_SRGB8:
|
||||
/* 24-Bit pixel format are not supported. Emulate using a padded type with alpha. */
|
||||
return MTLPixelFormatRGBA8Unorm_sRGB;
|
||||
@ -247,6 +251,14 @@ size_t get_mtl_format_bytesize(MTLPixelFormat tex_format)
|
||||
return 4;
|
||||
case MTLPixelFormatDepth16Unorm:
|
||||
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:
|
||||
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 MTLPixelFormatRGB10A2Uint:
|
||||
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;
|
||||
|
||||
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_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.
|
||||
* 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_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
|
||||
* 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,
|
||||
};
|
||||
|
||||
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.
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
* Returns true of allocation succeeded, false otherwise. */
|
||||
template<class BufferType>
|
||||
@ -249,11 +270,7 @@ void IMB_freeImBuf(ImBuf *ibuf)
|
||||
IMB_free_gpu_textures(ibuf);
|
||||
IMB_metadata_free(ibuf->metadata);
|
||||
colormanage_cache_free(ibuf);
|
||||
|
||||
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);
|
||||
}
|
||||
imb_free_dds_buffer(ibuf->dds_data);
|
||||
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(
|
||||
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.data = (uchar *)malloc(ibuf->dds_data.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. */
|
||||
FlipDXTCImage(ibuf);
|
||||
|
@ -7703,6 +7703,8 @@ static void rna_def_modifier_nodes_bake(BlenderRNA *brna)
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
RNA_define_lib_overridable(true);
|
||||
|
||||
srna = RNA_def_struct(brna, "NodesModifierBake", nullptr);
|
||||
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_collection_sdna(prop, nullptr, "data_blocks", "data_blocks_num");
|
||||
RNA_def_property_srna(prop, "NodesModifierBakeDataBlocks");
|
||||
|
||||
RNA_define_lib_overridable(false);
|
||||
}
|
||||
|
||||
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);
|
||||
RNA_def_property_flag(prop, PROP_ENUM_FLAG);
|
||||
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 */
|
||||
|
||||
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")
|
||||
.default_value(true)
|
||||
.hide_value()
|
||||
.supports_field()
|
||||
.field_on_all()
|
||||
.description("Which top-level instances to realize");
|
||||
b.add_input<decl::Bool>("Realize All")
|
||||
.default_value(true)
|
||||
.supports_field()
|
||||
.field_on_all()
|
||||
.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");
|
||||
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);
|
||||
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");
|
||||
|
||||
static auto depth_override = mf::build::SI2_SO<int, bool, int>(
|
||||
"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());
|
||||
|
||||
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>(
|
||||
"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());
|
||||
|
||||
Field<int> depth_field_overrided(
|
||||
FieldOperation::Create(depth_override, {depth_field, realize_all_filed}));
|
||||
Field<bool> selection_field_overrided(
|
||||
FieldOperation::Create(selection_override, {depth_field_overrided, selection_field}));
|
||||
Field<bool> selection_field_overrided(FieldOperation::Create(
|
||||
selection_override, {depth_field_overrided, std::move(selection_field)}));
|
||||
|
||||
const bke::Instances &instances = *geometry_set.get_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(
|
||||
{GeometryComponent::Type::Curve, GeometryComponent::Type::GreasePencil});
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -241,7 +241,7 @@ static PyObject *pygpu_vertbuf__tp_new(PyTypeObject * /*type*/, PyObject *args,
|
||||
PY_ARG_PARSER_HEAD_COMPAT()
|
||||
"O!" /* `format` */
|
||||
"I" /* `len` */
|
||||
":blender::gpu::VertBuf.__new__",
|
||||
":GPUVertBuf.__new__",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
@ -307,7 +307,7 @@ static PyObject *pygpu_vertbuf_attr_fill(BPyGPUVertBuf *self, PyObject *args, Py
|
||||
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;
|
||||
}
|
||||
|
||||
@ -340,7 +340,7 @@ static void pygpu_vertbuf__tp_dealloc(BPyGPUVertBuf *self)
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_vertbuf__tp_doc,
|
||||
".. class:: blender::gpu::VertBuf(format, len)\n"
|
||||
".. class:: GPUVertBuf(format, len)\n"
|
||||
"\n"
|
||||
" Contains a VBO.\n"
|
||||
"\n"
|
||||
@ -350,7 +350,7 @@ PyDoc_STRVAR(
|
||||
" :type len: int\n");
|
||||
PyTypeObject BPyGPUVertBuf_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "blender::gpu::VertBuf",
|
||||
/*tp_name*/ "GPUVertBuf",
|
||||
/*tp_basicsize*/ sizeof(BPyGPUVertBuf),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*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. */
|
||||
WM_system_gpu_context_activate(engine->system_gpu_context);
|
||||
if (engine->blender_gpu_context) {
|
||||
GPU_context_active_set(engine->blender_gpu_context);
|
||||
GPU_render_begin();
|
||||
GPU_context_active_set(engine->blender_gpu_context);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1330,8 +1330,8 @@ void RE_engine_gpu_context_disable(RenderEngine *engine)
|
||||
else {
|
||||
if (engine->system_gpu_context) {
|
||||
if (engine->blender_gpu_context) {
|
||||
GPU_render_end();
|
||||
GPU_context_active_set(nullptr);
|
||||
GPU_render_end();
|
||||
}
|
||||
WM_system_gpu_context_release(engine->system_gpu_context);
|
||||
/* Restore DRW state context if previously active. */
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 5038ad7165fd1a77e61e0d2d6efdadd6ea7c0dfb
|
||||
Subproject commit bf5c70830540b215a3b1df21f28e0e80ead230f7
|
Loading…
Reference in New Issue
Block a user