Merge branch 'master' into xr-dev

This commit is contained in:
Peter Kim 2022-05-09 18:10:04 +09:00
commit c5a8372a11
415 changed files with 8029 additions and 2421 deletions

View File

@ -1,6 +1,8 @@
# The warnings below are disabled because they are too pedantic and not worth fixing.
# Some of them will be enabled as part of the Clang-Tidy task, see T78535.
# NOTE: No comments in the list below is allowed. Clang-tidy will ignore items after comments in the lists flag list.
# This is because the comment is not a valid list item and it will stop parsing flags if a list item is a comment.
Checks: >
-*,
readability-*,
@ -14,10 +16,9 @@ Checks: >
-readability-make-member-function-const,
-readability-suspicious-call-argument,
-readability-redundant-member-init,
-readability-misleading-indentation,
-readability-use-anyofallof,
-readability-identifier-length,
-readability-function-cognitive-complexity,
@ -35,6 +36,8 @@ Checks: >
-bugprone-redundant-branch-condition,
-bugprone-suspicious-include,
modernize-*,
-modernize-use-auto,
-modernize-use-trailing-return-type,
@ -42,8 +45,6 @@ Checks: >
-modernize-use-nodiscard,
-modernize-loop-convert,
-modernize-pass-by-value,
# Cannot be enabled yet, because using raw string literals in tests breaks
# the windows compiler currently.
-modernize-raw-string-literal,
-modernize-return-braced-init-list

View File

@ -486,7 +486,7 @@ if((UNIX AND NOT APPLE) OR (CMAKE_GENERATOR MATCHES "^Visual Studio.+"))
endif()
option(WITH_BOOST "Enable features depending on boost" ON)
option(WITH_TBB "Enable features depending on TBB (OpenVDB, OpenImageDenoise, sculpt multithreading)" ON)
option(WITH_TBB "Enable multithreading. TBB is also required for features such as Cycles, OpenVDB and USD" ON)
# TBB malloc is only supported on for windows currently
if(WIN32)

View File

@ -120,7 +120,7 @@ Utilities
Updates git and all submodules but not svn.
* format:
Format source code using clang (uses PATHS if passed in). For example::
Format source code using clang-format & autopep8 (uses PATHS if passed in). For example::
make format PATHS="source/blender/blenlib source/blender/blenkernel"
@ -130,6 +130,7 @@ Environment Variables
* BUILD_DIR: Override default build path.
* PYTHON: Use this for the Python command (used for checking tools).
* NPROCS: Number of processes to use building (auto-detect when omitted).
* AUTOPEP8: Command used for Python code-formatting (used for the format target).
Documentation Targets
Not associated with building Blender.
@ -206,6 +207,27 @@ ifeq ($(OS_NCASE),darwin)
endif
endif
# Set the LIBDIR, an empty string when not found.
LIBDIR:=$(wildcard ../lib/${OS_NCASE}_${CPU})
ifeq (, $(LIBDIR))
LIBDIR:=$(wildcard ../lib/${OS_NCASE}_centos7_${CPU})
endif
ifeq (, $(LIBDIR))
LIBDIR:=$(wildcard ../lib/${OS_NCASE})
endif
# Use the autopep8 module in ../lib/ (which can be executed via Python directly).
# Otherwise the "autopep8" command can be used.
ifndef AUTOPEP8
ifneq (, $(LIBDIR))
AUTOPEP8:=$(wildcard $(LIBDIR)/python/lib/python3.10/site-packages/autopep8.py)
endif
ifeq (, $(AUTOPEP8))
AUTOPEP8:=autopep8
endif
endif
# -----------------------------------------------------------------------------
# additional targets for the build configuration
@ -527,8 +549,8 @@ update_code: .FORCE
@$(PYTHON) ./build_files/utils/make_update.py --no-libraries
format: .FORCE
@PATH="../lib/${OS_NCASE}_${CPU}/llvm/bin/:../lib/${OS_NCASE}_centos7_${CPU}/llvm/bin/:../lib/${OS_NCASE}/llvm/bin/:$(PATH)" \
$(PYTHON) source/tools/utils_maintenance/clang_format_paths.py $(PATHS)
@PATH="${LIBDIR}/llvm/bin/:$(PATH)" $(PYTHON) source/tools/utils_maintenance/clang_format_paths.py $(PATHS)
@$(PYTHON) source/tools/utils_maintenance/autopep8_format_paths.py --autopep8-command="$(AUTOPEP8)" $(PATHS)
# -----------------------------------------------------------------------------

View File

@ -64,6 +64,7 @@ ENDIF()
MARK_AS_ADVANCED(
USD_INCLUDE_DIR
USD_LIBRARY_DIR
USD_LIBRARY
)
UNSET(_usd_SEARCH_DIRS)

View File

@ -74,4 +74,9 @@ ENDIF()
MARK_AS_ADVANCED(
WEBP_INCLUDE_DIR
WEBP_LIBRARY_DIR
# Generated names.
WEBP_WEBPDEMUX_LIBRARY
WEBP_WEBPMUX_LIBRARY
WEBP_WEBP_LIBRARY
)

View File

@ -104,7 +104,7 @@ string(APPEND CMAKE_MODULE_LINKER_FLAGS " /SAFESEH:NO /ignore:4099")
list(APPEND PLATFORM_LINKLIBS
ws2_32 vfw32 winmm kernel32 user32 gdi32 comdlg32 Comctl32 version
advapi32 shfolder shell32 ole32 oleaut32 uuid psapi Dbghelp Shlwapi
pathcch Shcore
pathcch Shcore Dwmapi
)
if(WITH_INPUT_IME)

View File

@ -0,0 +1,47 @@
"""
Image Data
++++++++++
The Image data-block is a shallow wrapper around image or video file(s)
(on disk, as packed data, or generated).
All actual data like the pixel buffer, size, resolution etc. is
cached in an :class:`imbuf.types.ImBuf` image buffer (or several buffers
in some cases, like UDIM textures, multi-views, animations...).
Several properties and functions of the Image data-block are then actually
using/modifying its image buffer, and not the Image data-block itself.
.. warning::
One key limitation is that image buffers are not shared between different
Image data-blocks, and they are not duplicated when copying an image.
So until a modified image buffer is saved on disk, duplicating its Image
data-block will not propagate the underlying buffer changes to the new Image.
This example script generates an Image data-block with a given size,
change its first pixel, rescale it, and duplicates the image.
The duplicated image still has the same size and colors as the original image
at its creation, all editing in the original image's buffer is 'lost' in its copy.
"""
import bpy
image_src = bpy.data.images.new('src', 1024, 102)
print(image_src.size)
print(image_src.pixels[0:4])
image_src.scale(1024, 720)
image_src.pixels[0:4] = (0.5, 0.5, 0.5, 0.5)
image_src.update()
print(image_src.size)
print(image_src.pixels[0:4])
image_dest = image_src.copy()
image_dest.update()
print(image_dest.size)
print(image_dest.pixels[0:4])

View File

@ -29,3 +29,36 @@ def draw():
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')
"""
3D Image
--------
Similar to the 2D Image shader, but works with 3D positions for the image vertices.
To use this example you have to provide an image that should be displayed.
"""
import bpy
import gpu
from gpu_extras.batch import batch_for_shader
IMAGE_NAME = "Untitled"
image = bpy.data.images[IMAGE_NAME]
texture = gpu.texture.from_image(image)
shader = gpu.shader.from_builtin('3D_IMAGE')
batch = batch_for_shader(
shader, 'TRIS',
{
"pos": ((0, 0, 0), (0, 1, 1), (1, 1, 1), (1, 1, 1), (1, 0, 0), (0, 0, 0)),
"texCoord": ((0, 0), (0, 1), (1, 1), (1, 1), (1, 0), (0, 0)),
},
)
def draw():
shader.bind()
shader.uniform_sampler("image", texture)
batch.draw(shader)
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')

View File

@ -1446,28 +1446,10 @@ class CYCLES_WORLD_PT_surface(CyclesButtonsPanel, Panel):
layout.use_property_split = True
world = context.world
view_layer = context.view_layer
if not panel_node_draw(layout, world, 'OUTPUT_WORLD', 'Surface'):
layout.prop(world, "color")
row = layout.row(align=True)
row.use_property_decorate = False
sub = row.column(align=True)
sub.prop_search(
world,
"lightgroup",
view_layer,
"lightgroups",
text="Light Group",
results_are_suggestions=True,
)
sub = row.column(align=True)
sub.active = bool(world.lightgroup) and not any(lg.name == world.lightgroup for lg in view_layer.lightgroups)
sub.operator("scene.view_layer_add_lightgroup", icon='ADD', text="").name = world.lightgroup
class CYCLES_WORLD_PT_volume(CyclesButtonsPanel, Panel):
bl_label = "Volume"
@ -1616,6 +1598,40 @@ class CYCLES_WORLD_PT_settings_volume(CyclesButtonsPanel, Panel):
sub.prop(cworld, "volume_step_size")
class CYCLES_WORLD_PT_settings_light_group(CyclesButtonsPanel, Panel):
bl_label = "Light Group"
bl_parent_id = "CYCLES_WORLD_PT_settings"
bl_context = "world"
@classmethod
def poll(cls, context):
return context.world and CyclesButtonsPanel.poll(context)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
world = context.world
view_layer = context.view_layer
row = layout.row(align=True)
sub = row.column(align=True)
sub.prop_search(
world,
"lightgroup",
view_layer,
"lightgroups",
text="Light Group",
results_are_suggestions=True,
)
sub = row.column(align=True)
sub.active = bool(world.lightgroup) and not any(lg.name == world.lightgroup for lg in view_layer.lightgroups)
sub.operator("scene.view_layer_add_lightgroup", icon='ADD', text="").name = world.lightgroup
class CYCLES_MATERIAL_PT_preview(CyclesButtonsPanel, Panel):
bl_label = "Preview"
bl_context = "material"
@ -2308,6 +2324,7 @@ classes = (
CYCLES_WORLD_PT_settings,
CYCLES_WORLD_PT_settings_surface,
CYCLES_WORLD_PT_settings_volume,
CYCLES_WORLD_PT_settings_light_group,
CYCLES_MATERIAL_PT_preview,
CYCLES_MATERIAL_PT_surface,
CYCLES_MATERIAL_PT_volume,

View File

@ -355,6 +355,18 @@ static ShaderNode *add_node(Scene *scene,
else if (b_node.is_a(&RNA_ShaderNodeCombineHSV)) {
node = graph->create_node<CombineHSVNode>();
}
else if (b_node.is_a(&RNA_ShaderNodeSeparateColor)) {
BL::ShaderNodeSeparateColor b_separate_node(b_node);
SeparateColorNode *separate_node = graph->create_node<SeparateColorNode>();
separate_node->set_color_type((NodeCombSepColorType)b_separate_node.mode());
node = separate_node;
}
else if (b_node.is_a(&RNA_ShaderNodeCombineColor)) {
BL::ShaderNodeCombineColor b_combine_node(b_node);
CombineColorNode *combine_node = graph->create_node<CombineColorNode>();
combine_node->set_color_type((NodeCombSepColorType)b_combine_node.mode());
node = combine_node;
}
else if (b_node.is_a(&RNA_ShaderNodeSeparateXYZ)) {
node = graph->create_node<SeparateXYZNode>();
}

View File

@ -23,6 +23,7 @@
# include "util/md5.h"
# include "util/path.h"
# include "util/progress.h"
# include "util/task.h"
# include "util/time.h"
# undef __KERNEL_CPU__
@ -216,6 +217,25 @@ static OptixResult optixUtilDenoiserInvokeTiled(OptixDenoiser denoiser,
return OPTIX_SUCCESS;
}
# if OPTIX_ABI_VERSION >= 55
static void execute_optix_task(TaskPool &pool, OptixTask task, OptixResult &failure_reason)
{
OptixTask additional_tasks[16];
unsigned int num_additional_tasks = 0;
const OptixResult result = optixTaskExecute(task, additional_tasks, 16, &num_additional_tasks);
if (result == OPTIX_SUCCESS) {
for (unsigned int i = 0; i < num_additional_tasks; ++i) {
pool.push(function_bind(
&execute_optix_task, std::ref(pool), additional_tasks[i], std::ref(failure_reason)));
}
}
else {
failure_reason = result;
}
}
# endif
} // namespace
OptiXDevice::Denoiser::Denoiser(OptiXDevice *device)
@ -453,6 +473,23 @@ bool OptiXDevice::load_kernels(const uint kernel_features)
return false;
}
# if OPTIX_ABI_VERSION >= 55
OptixTask task = nullptr;
OptixResult result = optixModuleCreateFromPTXWithTasks(context,
&module_options,
&pipeline_options,
ptx_data.data(),
ptx_data.size(),
nullptr,
nullptr,
&optix_module,
&task);
if (result == OPTIX_SUCCESS) {
TaskPool pool;
execute_optix_task(pool, task, result);
pool.wait_work();
}
# else
const OptixResult result = optixModuleCreateFromPTX(context,
&module_options,
&pipeline_options,
@ -461,6 +498,7 @@ bool OptiXDevice::load_kernels(const uint kernel_features)
nullptr,
0,
&optix_module);
# endif
if (result != OPTIX_SUCCESS) {
set_error(string_printf("Failed to load OptiX kernel from '%s' (%s)",
ptx_filename.c_str(),

View File

@ -750,6 +750,11 @@ double RenderScheduler::guess_display_update_interval_in_seconds_for_num_samples
int RenderScheduler::calculate_num_samples_per_update() const
{
const double time_per_sample_average = path_trace_time_.get_average();
/* Fall back to 1 sample if we have not recorded a time yet. */
if (time_per_sample_average == 0.0) {
return 1;
}
const double num_samples_in_second = pixel_size_ * pixel_size_ / time_per_sample_average;
const double update_interval_in_seconds = guess_display_update_interval_in_seconds();

View File

@ -145,6 +145,7 @@ set(SRC_KERNEL_SVM_HEADERS
svm/normal.h
svm/ramp.h
svm/ramp_util.h
svm/sepcomb_color.h
svm/sepcomb_hsv.h
svm/sepcomb_vector.h
svm/sky.h
@ -356,16 +357,13 @@ if(WITH_CYCLES_CUDA_BINARIES)
set(CUDA_VERSION "${CUDA_VERSION_MAJOR}${CUDA_VERSION_MINOR}")
# warn for other versions
if((CUDA_VERSION MATCHES "101") OR
(CUDA_VERSION MATCHES "102") OR
(CUDA_VERSION MATCHES "111") OR
(CUDA_VERSION MATCHES "112") OR
(CUDA_VERSION MATCHES "113") OR
(CUDA_VERSION MATCHES "114"))
if((CUDA_VERSION STREQUAL "101") OR
(CUDA_VERSION STREQUAL "102") OR
(CUDA_VERSION_MAJOR STREQUAL "11"))
else()
message(WARNING
"CUDA version ${CUDA_VERSION_MAJOR}.${CUDA_VERSION_MINOR} detected, "
"build may succeed but only CUDA 10.1 to 11.4 are officially supported")
"build may succeed but only CUDA 11, 10.2 and 10.1 have been tested")
endif()
# build for each arch
@ -482,19 +480,19 @@ if(WITH_CYCLES_CUDA_BINARIES)
if(DEFINED CUDA10_NVCC_EXECUTABLE)
set(cuda_nvcc_executable ${CUDA10_NVCC_EXECUTABLE})
set(cuda_toolkit_root_dir ${CUDA10_TOOLKIT_ROOT_DIR})
elseif(${CUDA_VERSION} LESS 110) # Support for sm_30 was removed in CUDA 11
elseif("${CUDA_VERSION}" LESS 110) # Support for sm_30 was removed in CUDA 11
set(cuda_nvcc_executable ${CUDA_NVCC_EXECUTABLE})
set(cuda_toolkit_root_dir ${CUDA_TOOLKIT_ROOT_DIR})
else()
message(STATUS "CUDA binaries for ${arch} require CUDA 10 or earlier, skipped.")
endif()
elseif(${arch} MATCHES ".*_7." AND ${CUDA_VERSION} LESS 100)
elseif(${arch} MATCHES ".*_7." AND "${CUDA_VERSION}" LESS 100)
message(STATUS "CUDA binaries for ${arch} require CUDA 10.0+, skipped.")
elseif(${arch} MATCHES ".*_8.")
if(DEFINED CUDA11_NVCC_EXECUTABLE)
set(cuda_nvcc_executable ${CUDA11_NVCC_EXECUTABLE})
set(cuda_toolkit_root_dir ${CUDA11_TOOLKIT_ROOT_DIR})
elseif(${CUDA_VERSION} GREATER_EQUAL 111) # Support for sm_86 was introduced in CUDA 11
elseif("${CUDA_VERSION}" GREATER_EQUAL 111) # Support for sm_86 was introduced in CUDA 11
set(cuda_nvcc_executable ${CUDA_NVCC_EXECUTABLE})
set(cuda_toolkit_root_dir ${CUDA_TOOLKIT_ROOT_DIR})
else()

View File

@ -387,6 +387,22 @@ ccl_device_forceinline int integrate_surface_volume_only_bounce(IntegratorState
}
#endif
ccl_device_forceinline bool integrate_surface_terminate(IntegratorState state,
const uint32_t path_flag)
{
const float probability = (path_flag & PATH_RAY_TERMINATE_ON_NEXT_SURFACE) ?
0.0f :
INTEGRATOR_STATE(state, path, continuation_probability);
if (probability == 0.0f) {
return true;
}
else if (probability != 1.0f) {
INTEGRATOR_STATE_WRITE(state, path, throughput) /= probability;
}
return false;
}
#if defined(__AO__)
ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg,
IntegratorState state,
@ -478,12 +494,12 @@ ccl_device bool integrate_surface(KernelGlobals kg,
int continue_path_label = 0;
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
/* Skip most work for volume bounding surface. */
#ifdef __VOLUME__
if (!(sd.flag & SD_HAS_ONLY_VOLUME)) {
#endif
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
#ifdef __SUBSURFACE__
/* Can skip shader evaluation for BSSRDF exit point without bump mapping. */
if (!(path_flag & PATH_RAY_SUBSURFACE) || ((sd.flag & SD_HAS_BSSRDF_BUMP)))
@ -509,55 +525,50 @@ ccl_device bool integrate_surface(KernelGlobals kg,
subsurface_shader_data_setup(kg, state, &sd, path_flag);
INTEGRATOR_STATE_WRITE(state, path, flag) &= ~PATH_RAY_SUBSURFACE;
}
else
#endif
shader_prepare_surface_closures(kg, state, &sd, path_flag);
{
/* Filter closures. */
shader_prepare_surface_closures(kg, state, &sd, path_flag);
#ifdef __HOLDOUT__
/* Evaluate holdout. */
if (!integrate_surface_holdout(kg, state, &sd, render_buffer)) {
return false;
}
/* Evaluate holdout. */
if (!integrate_surface_holdout(kg, state, &sd, render_buffer)) {
return false;
}
#endif
#ifdef __EMISSION__
/* Write emission. */
if (sd.flag & SD_EMISSION) {
integrate_surface_emission(kg, state, &sd, render_buffer);
}
/* Write emission. */
if (sd.flag & SD_EMISSION) {
integrate_surface_emission(kg, state, &sd, render_buffer);
}
#endif
/* Perform path termination. Most paths have already been terminated in
* the intersect_closest kernel, this is just for emission and for dividing
* throughput by the probability at the right moment.
*
* Also ensure we don't do it twice for SSS at both the entry and exit point. */
if (integrate_surface_terminate(state, path_flag)) {
return false;
}
/* Write render passes. */
#ifdef __PASSES__
/* Write render passes. */
PROFILING_EVENT(PROFILING_SHADE_SURFACE_PASSES);
kernel_write_data_passes(kg, state, &sd, render_buffer);
PROFILING_EVENT(PROFILING_SHADE_SURFACE_PASSES);
kernel_write_data_passes(kg, state, &sd, render_buffer);
#endif
#ifdef __DENOISING_FEATURES__
kernel_write_denoising_features_surface(kg, state, &sd, render_buffer);
#endif
}
/* Load random number state. */
RNGState rng_state;
path_state_rng_load(state, &rng_state);
/* Perform path termination. Most paths have already been terminated in
* the intersect_closest kernel, this is just for emission and for dividing
* throughput by the probability at the right moment.
*
* Also ensure we don't do it twice for SSS at both the entry and exit point. */
if (!(path_flag & PATH_RAY_SUBSURFACE)) {
const float probability = (path_flag & PATH_RAY_TERMINATE_ON_NEXT_SURFACE) ?
0.0f :
INTEGRATOR_STATE(state, path, continuation_probability);
if (probability == 0.0f) {
return false;
}
else if (probability != 1.0f) {
INTEGRATOR_STATE_WRITE(state, path, throughput) /= probability;
}
}
#ifdef __DENOISING_FEATURES__
kernel_write_denoising_features_surface(kg, state, &sd, render_buffer);
#endif
/* Direct light. */
PROFILING_EVENT(PROFILING_SHADE_SURFACE_DIRECT_LIGHT);
integrate_surface_direct_light<node_feature_mask>(kg, state, &sd, &rng_state);
@ -575,6 +586,10 @@ ccl_device bool integrate_surface(KernelGlobals kg,
#ifdef __VOLUME__
}
else {
if (integrate_surface_terminate(state, path_flag)) {
return false;
}
PROFILING_EVENT(PROFILING_SHADE_SURFACE_INDIRECT_LIGHT);
continue_path_label = integrate_surface_volume_only_bounce(state, &sd);
}

View File

@ -16,6 +16,7 @@ set(SRC_OSL
node_camera.osl
node_checker_texture.osl
node_clamp.osl
node_combine_color.osl
node_combine_rgb.osl
node_combine_hsv.osl
node_combine_xyz.osl
@ -68,6 +69,7 @@ set(SRC_OSL
node_refraction_bsdf.osl
node_rgb_curves.osl
node_rgb_ramp.osl
node_separate_color.osl
node_separate_rgb.osl
node_separate_hsv.osl
node_separate_xyz.osl

View File

@ -148,3 +148,53 @@ color hsv_to_rgb(color hsv)
return rgb;
}
color rgb_to_hsl(color rgb)
{
float cmax, cmin, h, s, l;
cmax = max(rgb[0], max(rgb[1], rgb[2]));
cmin = min(rgb[0], min(rgb[1], rgb[2]));
l = min(1.0, (cmax + cmin) / 2.0);
if (cmax == cmin) {
h = s = 0.0; /* achromatic */
}
else {
float cdelta = cmax - cmin;
s = l > 0.5 ? cdelta / (2.0 - cmax - cmin) : cdelta / (cmax + cmin);
if (cmax == rgb[0]) {
h = (rgb[1] - rgb[2]) / cdelta + (rgb[1] < rgb[2] ? 6.0 : 0.0);
}
else if (cmax == rgb[1]) {
h = (rgb[2] - rgb[0]) / cdelta + 2.0;
}
else {
h = (rgb[0] - rgb[1]) / cdelta + 4.0;
}
}
h /= 6.0;
return color(h, s, l);
}
color hsl_to_rgb(color hsl)
{
float nr, ng, nb, chroma, h, s, l;
h = hsl[0];
s = hsl[1];
l = hsl[2];
nr = abs(h * 6.0 - 3.0) - 1.0;
ng = 2.0 - abs(h * 6.0 - 2.0);
nb = 2.0 - abs(h * 6.0 - 4.0);
nr = clamp(nr, 0.0, 1.0);
nb = clamp(nb, 0.0, 1.0);
ng = clamp(ng, 0.0, 1.0);
chroma = (1.0 - abs(2.0 * l - 1.0)) * s;
return color((nr - 0.5) * chroma + l, (ng - 0.5) * chroma + l, (nb - 0.5) * chroma + l);
}

View File

@ -0,0 +1,16 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
#include "stdcycles.h"
shader node_combine_color(string color_type = "rgb",
float Red = 0.0,
float Green = 0.0,
float Blue = 0.0,
output color Color = 0.8)
{
if (color_type == "rgb" || color_type == "hsv" || color_type == "hsl")
Color = color(color_type, Red, Green, Blue);
else
warning("%s", "Unknown color space!");
}

View File

@ -0,0 +1,26 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
#include "node_color.h"
#include "stdcycles.h"
shader node_separate_color(string color_type = "rgb",
color Color = 0.8,
output float Red = 0.0,
output float Green = 0.0,
output float Blue = 0.0)
{
color col;
if (color_type == "rgb")
col = Color;
else if (color_type == "hsv")
col = rgb_to_hsv(Color);
else if (color_type == "hsl")
col = rgb_to_hsl(Color);
else
warning("%s", "Unknown color space!");
Red = col[0];
Green = col[1];
Blue = col[2];
}

View File

@ -307,4 +307,30 @@ ccl_device_inline float3 svm_brightness_contrast(float3 color, float brightness,
return color;
}
ccl_device float3 svm_combine_color(NodeCombSepColorType type, float3 color)
{
switch (type) {
case NODE_COMBSEP_COLOR_HSV:
return hsv_to_rgb(color);
case NODE_COMBSEP_COLOR_HSL:
return hsl_to_rgb(color);
case NODE_COMBSEP_COLOR_RGB:
default:
return color;
}
}
ccl_device float3 svm_separate_color(NodeCombSepColorType type, float3 color)
{
switch (type) {
case NODE_COMBSEP_COLOR_HSV:
return rgb_to_hsv(color);
case NODE_COMBSEP_COLOR_HSL:
return rgb_to_hsl(color);
case NODE_COMBSEP_COLOR_RGB:
default:
return color;
}
}
CCL_NAMESPACE_END

View File

@ -8,159 +8,188 @@
CCL_NAMESPACE_BEGIN
/* Bump Node */
template<uint node_feature_mask>
ccl_device_noinline void svm_node_set_bump(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *stack,
uint4 node)
{
#ifdef __RAY_DIFFERENTIALS__
/* get normal input */
uint normal_offset, scale_offset, invert, use_object_space;
svm_unpack_node_uchar4(node.y, &normal_offset, &scale_offset, &invert, &use_object_space);
IF_KERNEL_NODES_FEATURE(BUMP)
{
/* get normal input */
uint normal_offset, scale_offset, invert, use_object_space;
svm_unpack_node_uchar4(node.y, &normal_offset, &scale_offset, &invert, &use_object_space);
float3 normal_in = stack_valid(normal_offset) ? stack_load_float3(stack, normal_offset) : sd->N;
float3 normal_in = stack_valid(normal_offset) ? stack_load_float3(stack, normal_offset) :
sd->N;
float3 dPdx = sd->dP.dx;
float3 dPdy = sd->dP.dy;
float3 dPdx = sd->dP.dx;
float3 dPdy = sd->dP.dy;
if (use_object_space) {
object_inverse_normal_transform(kg, sd, &normal_in);
object_inverse_dir_transform(kg, sd, &dPdx);
object_inverse_dir_transform(kg, sd, &dPdy);
if (use_object_space) {
object_inverse_normal_transform(kg, sd, &normal_in);
object_inverse_dir_transform(kg, sd, &dPdx);
object_inverse_dir_transform(kg, sd, &dPdy);
}
/* get surface tangents from normal */
float3 Rx = cross(dPdy, normal_in);
float3 Ry = cross(normal_in, dPdx);
/* get bump values */
uint c_offset, x_offset, y_offset, strength_offset;
svm_unpack_node_uchar4(node.z, &c_offset, &x_offset, &y_offset, &strength_offset);
float h_c = stack_load_float(stack, c_offset);
float h_x = stack_load_float(stack, x_offset);
float h_y = stack_load_float(stack, y_offset);
/* compute surface gradient and determinant */
float det = dot(dPdx, Rx);
float3 surfgrad = (h_x - h_c) * Rx + (h_y - h_c) * Ry;
float absdet = fabsf(det);
float strength = stack_load_float(stack, strength_offset);
float scale = stack_load_float(stack, scale_offset);
if (invert)
scale *= -1.0f;
strength = max(strength, 0.0f);
/* compute and output perturbed normal */
float3 normal_out = safe_normalize(absdet * normal_in - scale * signf(det) * surfgrad);
if (is_zero(normal_out)) {
normal_out = normal_in;
}
else {
normal_out = normalize(strength * normal_out + (1.0f - strength) * normal_in);
}
if (use_object_space) {
object_normal_transform(kg, sd, &normal_out);
}
normal_out = ensure_valid_reflection(sd->Ng, sd->I, normal_out);
stack_store_float3(stack, node.w, normal_out);
}
/* get surface tangents from normal */
float3 Rx = cross(dPdy, normal_in);
float3 Ry = cross(normal_in, dPdx);
/* get bump values */
uint c_offset, x_offset, y_offset, strength_offset;
svm_unpack_node_uchar4(node.z, &c_offset, &x_offset, &y_offset, &strength_offset);
float h_c = stack_load_float(stack, c_offset);
float h_x = stack_load_float(stack, x_offset);
float h_y = stack_load_float(stack, y_offset);
/* compute surface gradient and determinant */
float det = dot(dPdx, Rx);
float3 surfgrad = (h_x - h_c) * Rx + (h_y - h_c) * Ry;
float absdet = fabsf(det);
float strength = stack_load_float(stack, strength_offset);
float scale = stack_load_float(stack, scale_offset);
if (invert)
scale *= -1.0f;
strength = max(strength, 0.0f);
/* compute and output perturbed normal */
float3 normal_out = safe_normalize(absdet * normal_in - scale * signf(det) * surfgrad);
if (is_zero(normal_out)) {
normal_out = normal_in;
else
{
stack_store_float3(stack, node.w, zero_float3());
}
else {
normal_out = normalize(strength * normal_out + (1.0f - strength) * normal_in);
}
if (use_object_space) {
object_normal_transform(kg, sd, &normal_out);
}
normal_out = ensure_valid_reflection(sd->Ng, sd->I, normal_out);
stack_store_float3(stack, node.w, normal_out);
#endif
}
/* Displacement Node */
template<uint node_feature_mask>
ccl_device void svm_node_set_displacement(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *stack,
uint fac_offset)
{
float3 dP = stack_load_float3(stack, fac_offset);
sd->P += dP;
IF_KERNEL_NODES_FEATURE(BUMP)
{
float3 dP = stack_load_float3(stack, fac_offset);
sd->P += dP;
}
}
template<uint node_feature_mask>
ccl_device_noinline void svm_node_displacement(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *stack,
uint4 node)
{
uint height_offset, midlevel_offset, scale_offset, normal_offset;
svm_unpack_node_uchar4(node.y, &height_offset, &midlevel_offset, &scale_offset, &normal_offset);
IF_KERNEL_NODES_FEATURE(BUMP)
{
uint height_offset, midlevel_offset, scale_offset, normal_offset;
svm_unpack_node_uchar4(
node.y, &height_offset, &midlevel_offset, &scale_offset, &normal_offset);
float height = stack_load_float(stack, height_offset);
float midlevel = stack_load_float(stack, midlevel_offset);
float scale = stack_load_float(stack, scale_offset);
float3 normal = stack_valid(normal_offset) ? stack_load_float3(stack, normal_offset) : sd->N;
uint space = node.w;
float height = stack_load_float(stack, height_offset);
float midlevel = stack_load_float(stack, midlevel_offset);
float scale = stack_load_float(stack, scale_offset);
float3 normal = stack_valid(normal_offset) ? stack_load_float3(stack, normal_offset) : sd->N;
uint space = node.w;
float3 dP = normal;
float3 dP = normal;
if (space == NODE_NORMAL_MAP_OBJECT) {
/* Object space. */
object_inverse_normal_transform(kg, sd, &dP);
dP *= (height - midlevel) * scale;
object_dir_transform(kg, sd, &dP);
if (space == NODE_NORMAL_MAP_OBJECT) {
/* Object space. */
object_inverse_normal_transform(kg, sd, &dP);
dP *= (height - midlevel) * scale;
object_dir_transform(kg, sd, &dP);
}
else {
/* World space. */
dP *= (height - midlevel) * scale;
}
stack_store_float3(stack, node.z, dP);
}
else {
/* World space. */
dP *= (height - midlevel) * scale;
else
{
stack_store_float3(stack, node.z, zero_float3());
}
stack_store_float3(stack, node.z, dP);
}
template<uint node_feature_mask>
ccl_device_noinline int svm_node_vector_displacement(
KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset)
{
uint4 data_node = read_node(kg, &offset);
uint space = data_node.x;
uint vector_offset, midlevel_offset, scale_offset, displacement_offset;
svm_unpack_node_uchar4(
node.y, &vector_offset, &midlevel_offset, &scale_offset, &displacement_offset);
float3 vector = stack_load_float3(stack, vector_offset);
float midlevel = stack_load_float(stack, midlevel_offset);
float scale = stack_load_float(stack, scale_offset);
float3 dP = (vector - make_float3(midlevel, midlevel, midlevel)) * scale;
IF_KERNEL_NODES_FEATURE(BUMP)
{
uint space = data_node.x;
if (space == NODE_NORMAL_MAP_TANGENT) {
/* Tangent space. */
float3 normal = sd->N;
object_inverse_normal_transform(kg, sd, &normal);
float3 vector = stack_load_float3(stack, vector_offset);
float midlevel = stack_load_float(stack, midlevel_offset);
float scale = stack_load_float(stack, scale_offset);
float3 dP = (vector - make_float3(midlevel, midlevel, midlevel)) * scale;
const AttributeDescriptor attr = find_attribute(kg, sd, node.z);
float3 tangent;
if (attr.offset != ATTR_STD_NOT_FOUND) {
tangent = primitive_surface_attribute_float3(kg, sd, attr, NULL, NULL);
}
else {
tangent = normalize(sd->dPdu);
if (space == NODE_NORMAL_MAP_TANGENT) {
/* Tangent space. */
float3 normal = sd->N;
object_inverse_normal_transform(kg, sd, &normal);
const AttributeDescriptor attr = find_attribute(kg, sd, node.z);
float3 tangent;
if (attr.offset != ATTR_STD_NOT_FOUND) {
tangent = primitive_surface_attribute_float3(kg, sd, attr, NULL, NULL);
}
else {
tangent = normalize(sd->dPdu);
}
float3 bitangent = normalize(cross(normal, tangent));
const AttributeDescriptor attr_sign = find_attribute(kg, sd, node.w);
if (attr_sign.offset != ATTR_STD_NOT_FOUND) {
float sign = primitive_surface_attribute_float(kg, sd, attr_sign, NULL, NULL);
bitangent *= sign;
}
dP = tangent * dP.x + normal * dP.y + bitangent * dP.z;
}
float3 bitangent = normalize(cross(normal, tangent));
const AttributeDescriptor attr_sign = find_attribute(kg, sd, node.w);
if (attr_sign.offset != ATTR_STD_NOT_FOUND) {
float sign = primitive_surface_attribute_float(kg, sd, attr_sign, NULL, NULL);
bitangent *= sign;
if (space != NODE_NORMAL_MAP_WORLD) {
/* Tangent or object space. */
object_dir_transform(kg, sd, &dP);
}
dP = tangent * dP.x + normal * dP.y + bitangent * dP.z;
stack_store_float3(stack, displacement_offset, dP);
}
else
{
stack_store_float3(stack, displacement_offset, zero_float3());
}
if (space != NODE_NORMAL_MAP_WORLD) {
/* Tangent or object space. */
object_dir_transform(kg, sd, &dP);
}
stack_store_float3(stack, displacement_offset, dP);
return offset;
}

View File

@ -0,0 +1,54 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
#pragma once
CCL_NAMESPACE_BEGIN
ccl_device_noinline void svm_node_combine_color(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *stack,
uint color_type,
uint inputs_stack_offsets,
uint result_stack_offset)
{
uint red_stack_offset, green_stack_offset, blue_stack_offset;
svm_unpack_node_uchar3(
inputs_stack_offsets, &red_stack_offset, &green_stack_offset, &blue_stack_offset);
float r = stack_load_float(stack, red_stack_offset);
float g = stack_load_float(stack, green_stack_offset);
float b = stack_load_float(stack, blue_stack_offset);
/* Combine, and convert back to RGB */
float3 color = svm_combine_color((NodeCombSepColorType)color_type, make_float3(r, g, b));
if (stack_valid(result_stack_offset))
stack_store_float3(stack, result_stack_offset, color);
}
ccl_device_noinline void svm_node_separate_color(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *stack,
uint color_type,
uint input_stack_offset,
uint results_stack_offsets)
{
float3 color = stack_load_float3(stack, input_stack_offset);
/* Convert color space */
color = svm_separate_color((NodeCombSepColorType)color_type, color);
uint red_stack_offset, green_stack_offset, blue_stack_offset;
svm_unpack_node_uchar3(
results_stack_offsets, &red_stack_offset, &green_stack_offset, &blue_stack_offset);
if (stack_valid(red_stack_offset))
stack_store_float(stack, red_stack_offset, color.x);
if (stack_valid(green_stack_offset))
stack_store_float(stack, green_stack_offset, color.y);
if (stack_valid(blue_stack_offset))
stack_store_float(stack, blue_stack_offset, color.z);
}
CCL_NAMESPACE_END

View File

@ -181,6 +181,7 @@ CCL_NAMESPACE_END
#include "kernel/svm/noisetex.h"
#include "kernel/svm/normal.h"
#include "kernel/svm/ramp.h"
#include "kernel/svm/sepcomb_color.h"
#include "kernel/svm/sepcomb_hsv.h"
#include "kernel/svm/sepcomb_vector.h"
#include "kernel/svm/sky.h"
@ -304,22 +305,13 @@ ccl_device void svm_eval_nodes(KernelGlobals kg,
}
break;
case NODE_SET_DISPLACEMENT:
IF_KERNEL_NODES_FEATURE(BUMP)
{
svm_node_set_displacement(kg, sd, stack, node.y);
}
svm_node_set_displacement<node_feature_mask>(kg, sd, stack, node.y);
break;
case NODE_DISPLACEMENT:
IF_KERNEL_NODES_FEATURE(BUMP)
{
svm_node_displacement(kg, sd, stack, node);
}
svm_node_displacement<node_feature_mask>(kg, sd, stack, node);
break;
case NODE_VECTOR_DISPLACEMENT:
IF_KERNEL_NODES_FEATURE(BUMP)
{
offset = svm_node_vector_displacement(kg, sd, stack, node, offset);
}
offset = svm_node_vector_displacement<node_feature_mask>(kg, sd, stack, node, offset);
break;
case NODE_TEX_IMAGE:
offset = svm_node_tex_image(kg, sd, stack, node, offset);
@ -331,10 +323,7 @@ ccl_device void svm_eval_nodes(KernelGlobals kg,
offset = svm_node_tex_noise(kg, sd, stack, node.y, node.z, node.w, offset);
break;
case NODE_SET_BUMP:
IF_KERNEL_NODES_FEATURE(BUMP)
{
svm_node_set_bump(kg, sd, stack, node);
}
svm_node_set_bump<node_feature_mask>(kg, sd, stack, node);
break;
case NODE_ATTR_BUMP_DX:
IF_KERNEL_NODES_FEATURE(BUMP)
@ -520,6 +509,12 @@ ccl_device void svm_eval_nodes(KernelGlobals kg,
case NODE_MIX:
offset = svm_node_mix(kg, sd, stack, node.y, node.z, node.w, offset);
break;
case NODE_SEPARATE_COLOR:
svm_node_separate_color(kg, sd, stack, node.y, node.z, node.w);
break;
case NODE_COMBINE_COLOR:
svm_node_combine_color(kg, sd, stack, node.y, node.z, node.w);
break;
case NODE_SEPARATE_VECTOR:
svm_node_separate_vector(sd, stack, node.y, node.z, node.w);
break;

View File

@ -92,6 +92,8 @@ typedef enum ShaderNodeType {
NODE_NORMAL_MAP,
NODE_INVERT,
NODE_MIX,
NODE_SEPARATE_COLOR,
NODE_COMBINE_COLOR,
NODE_SEPARATE_VECTOR,
NODE_COMBINE_VECTOR,
NODE_SEPARATE_HSV,
@ -487,6 +489,12 @@ typedef enum NodePrincipledHairParametrization {
NODE_PRINCIPLED_HAIR_NUM,
} NodePrincipledHairParametrization;
typedef enum NodeCombSepColorType {
NODE_COMBSEP_COLOR_RGB,
NODE_COMBSEP_COLOR_HSV,
NODE_COMBSEP_COLOR_HSL,
} NodeCombSepColorType;
/* Closure */
typedef enum ClosureType {

View File

@ -618,6 +618,7 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom,
for (size_t k = 0; k < size; k++) {
attr_uchar4[offset + k] = data[k];
}
attr_uchar4.tag_modified();
}
attr_uchar4_offset += size;
}
@ -630,6 +631,7 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom,
for (size_t k = 0; k < size; k++) {
attr_float[offset + k] = data[k];
}
attr_float.tag_modified();
}
attr_float_offset += size;
}
@ -642,6 +644,7 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom,
for (size_t k = 0; k < size; k++) {
attr_float2[offset + k] = data[k];
}
attr_float2.tag_modified();
}
attr_float2_offset += size;
}
@ -654,6 +657,7 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom,
for (size_t k = 0; k < size * 3; k++) {
attr_float4[offset + k] = (&tfm->x)[k];
}
attr_float4.tag_modified();
}
attr_float4_offset += size * 3;
}
@ -666,6 +670,7 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom,
for (size_t k = 0; k < size; k++) {
attr_float4[offset + k] = data[k];
}
attr_float4.tag_modified();
}
attr_float4_offset += size;
}
@ -678,6 +683,7 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom,
for (size_t k = 0; k < size; k++) {
attr_float3[offset + k] = data[k];
}
attr_float3.tag_modified();
}
attr_float3_offset += size;
}

View File

@ -220,7 +220,7 @@ void Object::tag_update(Scene *scene)
}
if (geometry) {
if (tfm_is_modified()) {
if (tfm_is_modified() || motion_is_modified()) {
flag |= ObjectManager::TRANSFORM_MODIFIED;
}
@ -480,7 +480,7 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
kobject.motion_offset = state->motion_offset[ob->index];
/* Decompose transforms for interpolation. */
if (ob->tfm_is_modified() || update_all) {
if (ob->tfm_is_modified() || ob->motion_is_modified() || update_all) {
DecomposedTransform *decomp = state->object_motion + kobject.motion_offset;
transform_motion_decompose(decomp, ob->motion.data(), ob->motion.size());
}

View File

@ -19,7 +19,6 @@
#include "util/color.h"
#include "util/foreach.h"
#include "util/log.h"
#include "util/string.h"
#include "util/transform.h"
#include "kernel/tables.h"
@ -450,12 +449,8 @@ void ImageTextureNode::compile(OSLCompiler &compiler)
const ustring known_colorspace = metadata.colorspace;
if (handle.svm_slot() == -1) {
/* OIIO currently does not support <UVTILE> substitutions natively. Replace with a format they
* understand. */
std::string osl_filename = filename.string();
string_replace(osl_filename, "<UVTILE>", "<U>_<V>");
compiler.parameter_texture(
"filename", ustring(osl_filename), compress_as_srgb ? u_colorspace_raw : known_colorspace);
"filename", filename, compress_as_srgb ? u_colorspace_raw : known_colorspace);
}
else {
compiler.parameter_texture("filename", handle.svm_slot());
@ -5010,6 +5005,63 @@ void MixNode::constant_fold(const ConstantFolder &folder)
}
}
/* Combine Color */
NODE_DEFINE(CombineColorNode)
{
NodeType *type = NodeType::add("combine_color", create, NodeType::SHADER);
static NodeEnum type_enum;
type_enum.insert("rgb", NODE_COMBSEP_COLOR_RGB);
type_enum.insert("hsv", NODE_COMBSEP_COLOR_HSV);
type_enum.insert("hsl", NODE_COMBSEP_COLOR_HSL);
SOCKET_ENUM(color_type, "Type", type_enum, NODE_COMBSEP_COLOR_RGB);
SOCKET_IN_FLOAT(r, "Red", 0.0f);
SOCKET_IN_FLOAT(g, "Green", 0.0f);
SOCKET_IN_FLOAT(b, "Blue", 0.0f);
SOCKET_OUT_COLOR(color, "Color");
return type;
}
CombineColorNode::CombineColorNode() : ShaderNode(get_node_type())
{
}
void CombineColorNode::constant_fold(const ConstantFolder &folder)
{
if (folder.all_inputs_constant()) {
folder.make_constant(svm_combine_color(color_type, make_float3(r, g, b)));
}
}
void CombineColorNode::compile(SVMCompiler &compiler)
{
ShaderInput *red_in = input("Red");
ShaderInput *green_in = input("Green");
ShaderInput *blue_in = input("Blue");
ShaderOutput *color_out = output("Color");
int red_stack_offset = compiler.stack_assign(red_in);
int green_stack_offset = compiler.stack_assign(green_in);
int blue_stack_offset = compiler.stack_assign(blue_in);
int color_stack_offset = compiler.stack_assign(color_out);
compiler.add_node(
NODE_COMBINE_COLOR,
color_type,
compiler.encode_uchar4(red_stack_offset, green_stack_offset, blue_stack_offset),
color_stack_offset);
}
void CombineColorNode::compile(OSLCompiler &compiler)
{
compiler.parameter(this, "color_type");
compiler.add(this, "node_combine_color");
}
/* Combine RGB */
NODE_DEFINE(CombineRGBNode)
@ -5250,6 +5302,70 @@ void BrightContrastNode::compile(OSLCompiler &compiler)
compiler.add(this, "node_brightness");
}
/* Separate Color */
NODE_DEFINE(SeparateColorNode)
{
NodeType *type = NodeType::add("separate_color", create, NodeType::SHADER);
static NodeEnum type_enum;
type_enum.insert("rgb", NODE_COMBSEP_COLOR_RGB);
type_enum.insert("hsv", NODE_COMBSEP_COLOR_HSV);
type_enum.insert("hsl", NODE_COMBSEP_COLOR_HSL);
SOCKET_ENUM(color_type, "Type", type_enum, NODE_COMBSEP_COLOR_RGB);
SOCKET_IN_COLOR(color, "Color", zero_float3());
SOCKET_OUT_FLOAT(r, "Red");
SOCKET_OUT_FLOAT(g, "Green");
SOCKET_OUT_FLOAT(b, "Blue");
return type;
}
SeparateColorNode::SeparateColorNode() : ShaderNode(get_node_type())
{
}
void SeparateColorNode::constant_fold(const ConstantFolder &folder)
{
if (folder.all_inputs_constant()) {
float3 col = svm_separate_color(color_type, color);
for (int channel = 0; channel < 3; channel++) {
if (outputs[channel] == folder.output) {
folder.make_constant(col[channel]);
return;
}
}
}
}
void SeparateColorNode::compile(SVMCompiler &compiler)
{
ShaderInput *color_in = input("Color");
ShaderOutput *red_out = output("Red");
ShaderOutput *green_out = output("Green");
ShaderOutput *blue_out = output("Blue");
int color_stack_offset = compiler.stack_assign(color_in);
int red_stack_offset = compiler.stack_assign(red_out);
int green_stack_offset = compiler.stack_assign(green_out);
int blue_stack_offset = compiler.stack_assign(blue_out);
compiler.add_node(
NODE_SEPARATE_COLOR,
color_type,
color_stack_offset,
compiler.encode_uchar4(red_stack_offset, green_stack_offset, blue_stack_offset));
}
void SeparateColorNode::compile(OSLCompiler &compiler)
{
compiler.parameter(this, "color_type");
compiler.add(this, "node_separate_color");
}
/* Separate RGB */
NODE_DEFINE(SeparateRGBNode)

View File

@ -1101,6 +1101,17 @@ class MixNode : public ShaderNode {
NODE_SOCKET_API(float, fac)
};
class CombineColorNode : public ShaderNode {
public:
SHADER_NODE_CLASS(CombineColorNode)
void constant_fold(const ConstantFolder &folder);
NODE_SOCKET_API(NodeCombSepColorType, color_type)
NODE_SOCKET_API(float, r)
NODE_SOCKET_API(float, g)
NODE_SOCKET_API(float, b)
};
class CombineRGBNode : public ShaderNode {
public:
SHADER_NODE_CLASS(CombineRGBNode)
@ -1150,6 +1161,15 @@ class BrightContrastNode : public ShaderNode {
NODE_SOCKET_API(float, contrast)
};
class SeparateColorNode : public ShaderNode {
public:
SHADER_NODE_CLASS(SeparateColorNode)
void constant_fold(const ConstantFolder &folder);
NODE_SOCKET_API(NodeCombSepColorType, color_type)
NODE_SOCKET_API(float3, color)
};
class SeparateRGBNode : public ShaderNode {
public:
SHADER_NODE_CLASS(SeparateRGBNode)

View File

@ -152,6 +152,56 @@ ccl_device float3 hsv_to_rgb(float3 hsv)
return rgb;
}
ccl_device float3 rgb_to_hsl(float3 rgb)
{
float cmax, cmin, h, s, l;
cmax = fmaxf(rgb.x, fmaxf(rgb.y, rgb.z));
cmin = min(rgb.x, min(rgb.y, rgb.z));
l = min(1.0f, (cmax + cmin) / 2.0f);
if (cmax == cmin) {
h = s = 0.0f; /* achromatic */
}
else {
float cdelta = cmax - cmin;
s = l > 0.5f ? cdelta / (2.0f - cmax - cmin) : cdelta / (cmax + cmin);
if (cmax == rgb.x) {
h = (rgb.y - rgb.z) / cdelta + (rgb.y < rgb.z ? 6.0f : 0.0f);
}
else if (cmax == rgb.y) {
h = (rgb.z - rgb.x) / cdelta + 2.0f;
}
else {
h = (rgb.x - rgb.y) / cdelta + 4.0f;
}
}
h /= 6.0f;
return make_float3(h, s, l);
}
ccl_device float3 hsl_to_rgb(float3 hsl)
{
float nr, ng, nb, chroma, h, s, l;
h = hsl.x;
s = hsl.y;
l = hsl.z;
nr = fabsf(h * 6.0f - 3.0f) - 1.0f;
ng = 2.0f - fabsf(h * 6.0f - 2.0f);
nb = 2.0f - fabsf(h * 6.0f - 4.0f);
nr = clamp(nr, 0.0f, 1.0f);
nb = clamp(nb, 0.0f, 1.0f);
ng = clamp(ng, 0.0f, 1.0f);
chroma = (1.0f - fabsf(2.0f * l - 1.0f)) * s;
return make_float3((nr - 0.5f) * chroma + l, (ng - 0.5f) * chroma + l, (nb - 0.5f) * chroma + l);
}
ccl_device float3 xyY_to_xyz(float x, float y, float Y)
{
float X, Z;

View File

@ -376,6 +376,7 @@ elseif(WIN32)
intern/GHOST_DisplayManagerWin32.cpp
intern/GHOST_DropTargetWin32.cpp
intern/GHOST_SystemWin32.cpp
intern/GHOST_TrackpadWin32.cpp
intern/GHOST_WindowWin32.cpp
intern/GHOST_Wintab.cpp
@ -384,6 +385,7 @@ elseif(WIN32)
intern/GHOST_DropTargetWin32.h
intern/GHOST_SystemWin32.h
intern/GHOST_TaskbarWin32.h
intern/GHOST_TrackpadWin32.h
intern/GHOST_WindowWin32.h
intern/GHOST_Wintab.h
)

View File

@ -8,12 +8,14 @@
#include "GHOST_SystemWin32.h"
#include "GHOST_ContextD3D.h"
#include "GHOST_EventDragnDrop.h"
#include "GHOST_EventTrackpad.h"
#ifndef _WIN32_IE
# define _WIN32_IE 0x0501 /* shipped before XP, so doesn't impose additional requirements */
#endif
#include <commctrl.h>
#include <dwmapi.h>
#include <psapi.h>
#include <shellapi.h>
#include <shellscalingapi.h>
@ -414,6 +416,8 @@ bool GHOST_SystemWin32::processEvents(bool waitForEvent)
hasEventHandled = true;
}
driveTrackpad();
// Process all the events waiting for us
while (::PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) != 0) {
// TranslateMessage doesn't alter the message, and doesn't change our raw keyboard data.
@ -423,6 +427,8 @@ bool GHOST_SystemWin32::processEvents(bool waitForEvent)
hasEventHandled = true;
}
processTrackpad();
/* PeekMessage above is allowed to dispatch messages to the wndproc without us
* noticing, so we need to check the event manager here to see if there are
* events waiting in the queue.
@ -1416,6 +1422,52 @@ bool GHOST_SystemWin32::processNDOF(RAWINPUT const &raw)
}
#endif // WITH_INPUT_NDOF
void GHOST_SystemWin32::driveTrackpad()
{
GHOST_WindowWin32 *active_window = static_cast<GHOST_WindowWin32 *>(
getWindowManager()->getActiveWindow());
if (active_window) {
active_window->updateDirectManipulation();
}
}
void GHOST_SystemWin32::processTrackpad()
{
GHOST_WindowWin32 *active_window = static_cast<GHOST_WindowWin32 *>(
getWindowManager()->getActiveWindow());
if (!active_window) {
return;
}
GHOST_TTrackpadInfo trackpad_info = active_window->getTrackpadInfo();
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
int32_t cursor_x, cursor_y;
system->getCursorPosition(cursor_x, cursor_y);
if (trackpad_info.x != 0 || trackpad_info.y != 0) {
system->pushEvent(new GHOST_EventTrackpad(system->getMilliSeconds(),
active_window,
GHOST_kTrackpadEventScroll,
cursor_x,
cursor_y,
trackpad_info.x,
trackpad_info.y,
trackpad_info.isScrollDirectionInverted));
}
if (trackpad_info.scale != 0) {
system->pushEvent(new GHOST_EventTrackpad(system->getMilliSeconds(),
active_window,
GHOST_kTrackpadEventMagnify,
cursor_x,
cursor_y,
trackpad_info.scale,
0,
false));
}
}
LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
GHOST_Event *event = NULL;
@ -1968,6 +2020,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
suggestedWindowRect->right - suggestedWindowRect->left,
suggestedWindowRect->bottom - suggestedWindowRect->top,
SWP_NOZORDER | SWP_NOACTIVATE);
window->updateDPI();
}
break;
case WM_DISPLAYCHANGE: {
@ -1985,6 +2039,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
::SetFocus(hwnd);
}
break;
case WM_SETTINGCHANGE:
/* Microsoft: "Note that some applications send this message with lParam set to NULL" */
if ((lParam != NULL) && (wcscmp(LPCWSTR(lParam), L"ImmersiveColorSet") == 0)) {
window->ThemeRefresh();
}
break;
////////////////////////////////////////////////////////////////////////
// Window events, ignored
////////////////////////////////////////////////////////////////////////
@ -2056,6 +2116,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
* In GHOST, we let DefWindowProc call the timer callback.
*/
break;
case DM_POINTERHITTEST:
/* The DM_POINTERHITTEST message is sent to a window, when pointer input is first
* detected, in order to determine the most probable input target for Direct
* Manipulation. */
window->onPointerHitTest(wParam);
break;
}
}
else {

View File

@ -406,6 +406,16 @@ class GHOST_SystemWin32 : public GHOST_System {
bool processNDOF(RAWINPUT const &raw);
#endif
/**
* Drives Direct Manipulation update.
*/
void driveTrackpad();
/**
* Creates trackpad events for the active window.
*/
void processTrackpad();
/**
* Returns the local state of the modifier keys (from the message queue).
* \param keys: The state of the keys.

View File

@ -0,0 +1,343 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup GHOST
*/
#include <cmath>
#include "GHOST_Debug.h"
#include "GHOST_TrackpadWin32.h"
GHOST_DirectManipulationHelper::GHOST_DirectManipulationHelper(
HWND hWnd,
Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager,
Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager,
Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport,
Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
directManipulationEventHandler,
DWORD directManipulationViewportHandlerCookie,
bool isScrollDirectionInverted)
: m_hWnd(hWnd),
m_scrollDirectionRegKey(NULL),
m_scrollDirectionChangeEvent(NULL),
m_directManipulationManager(directManipulationManager),
m_directManipulationUpdateManager(directManipulationUpdateManager),
m_directManipulationViewport(directManipulationViewport),
m_directManipulationEventHandler(directManipulationEventHandler),
m_directManipulationViewportHandlerCookie(directManipulationViewportHandlerCookie),
m_isScrollDirectionInverted(isScrollDirectionInverted)
{
}
GHOST_DirectManipulationHelper *GHOST_DirectManipulationHelper::create(HWND hWnd, uint16_t dpi)
{
#define DM_CHECK_RESULT_AND_EXIT_EARLY(hr, failMessage) \
{ \
if (!SUCCEEDED(hr)) { \
GHOST_PRINT(failMessage); \
return nullptr; \
} \
}
Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager;
HRESULT hr = ::CoCreateInstance(CLSID_DirectManipulationManager,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&directManipulationManager));
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "DirectManipulationManager create failed\n");
/* Since we want to use fake viewport, we need to send fake updates to UpdateManager. */
Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager;
hr = directManipulationManager->GetUpdateManager(IID_PPV_ARGS(&directManipulationUpdateManager));
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Get UpdateManager failed\n");
Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport;
hr = directManipulationManager->CreateViewport(
nullptr, hWnd, IID_PPV_ARGS(&directManipulationViewport));
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport create failed\n");
DIRECTMANIPULATION_CONFIGURATION configuration =
DIRECTMANIPULATION_CONFIGURATION_INTERACTION |
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X |
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y |
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_INERTIA |
DIRECTMANIPULATION_CONFIGURATION_SCALING;
hr = directManipulationViewport->ActivateConfiguration(configuration);
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set ActivateConfiguration failed\n");
/* Since we are using fake viewport and only want to use Direct Manipulation for touchpad, we
* need to use MANUALUPDATE option. */
hr = directManipulationViewport->SetViewportOptions(
DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE);
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set ViewportOptions failed\n");
/* We receive Direct Manipulation transform updates in IDirectManipulationViewportEventHandler
* callbacks. */
Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
directManipulationEventHandler =
Microsoft::WRL::Make<GHOST_DirectManipulationViewportEventHandler>(dpi);
DWORD directManipulationViewportHandlerCookie;
directManipulationViewport->AddEventHandler(
hWnd, directManipulationEventHandler.Get(), &directManipulationViewportHandlerCookie);
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport add EventHandler failed\n");
/* Set default rect for viewport before activating. */
RECT rect = {0, 0, 10000, 10000};
hr = directManipulationViewport->SetViewportRect(&rect);
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set rect failed\n");
hr = directManipulationManager->Activate(hWnd);
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "DirectManipulationManager activate failed\n");
hr = directManipulationViewport->Enable();
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport enable failed\n");
directManipulationEventHandler->resetViewport(directManipulationViewport.Get());
bool isScrollDirectionInverted = getScrollDirectionFromReg();
auto instance = new GHOST_DirectManipulationHelper(hWnd,
directManipulationManager,
directManipulationUpdateManager,
directManipulationViewport,
directManipulationEventHandler,
directManipulationViewportHandlerCookie,
isScrollDirectionInverted);
instance->registerScrollDirectionChangeListener();
return instance;
#undef DM_CHECK_RESULT_AND_EXIT_EARLY
}
bool GHOST_DirectManipulationHelper::getScrollDirectionFromReg()
{
DWORD scrollDirectionRegValue, pcbData;
HRESULT hr = HRESULT_FROM_WIN32(
RegGetValueW(HKEY_CURRENT_USER,
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PrecisionTouchPad\\",
L"ScrollDirection",
RRF_RT_REG_DWORD,
NULL,
&scrollDirectionRegValue,
&pcbData));
if (!SUCCEEDED(hr)) {
GHOST_PRINT("Failed to get scroll direction from registry\n");
return false;
}
return scrollDirectionRegValue == 0;
}
void GHOST_DirectManipulationHelper::registerScrollDirectionChangeListener()
{
if (!m_scrollDirectionRegKey) {
HRESULT hr = HRESULT_FROM_WIN32(
RegOpenKeyExW(HKEY_CURRENT_USER,
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PrecisionTouchPad\\",
0,
KEY_NOTIFY,
&m_scrollDirectionRegKey));
if (!SUCCEEDED(hr)) {
GHOST_PRINT("Failed to open scroll direction registry key\n");
return;
}
}
if (!m_scrollDirectionChangeEvent) {
m_scrollDirectionChangeEvent = CreateEventW(NULL, true, false, NULL);
}
else {
ResetEvent(m_scrollDirectionChangeEvent);
}
HRESULT hr = HRESULT_FROM_WIN32(RegNotifyChangeKeyValue(m_scrollDirectionRegKey,
true,
REG_NOTIFY_CHANGE_LAST_SET,
m_scrollDirectionChangeEvent,
true));
if (!SUCCEEDED(hr)) {
GHOST_PRINT("Failed to register scroll direction change listener\n");
return;
}
}
void GHOST_DirectManipulationHelper::onPointerHitTest(UINT32 pointerId)
{
[[maybe_unused]] HRESULT hr = m_directManipulationViewport->SetContact(pointerId);
GHOST_ASSERT(SUCCEEDED(hr), "Viewport set contact failed\n");
if (WaitForSingleObject(m_scrollDirectionChangeEvent, 0) == WAIT_OBJECT_0) {
m_isScrollDirectionInverted = getScrollDirectionFromReg();
registerScrollDirectionChangeListener();
}
}
void GHOST_DirectManipulationHelper::update()
{
if (m_directManipulationEventHandler->dm_status == DIRECTMANIPULATION_RUNNING ||
m_directManipulationEventHandler->dm_status == DIRECTMANIPULATION_INERTIA) {
[[maybe_unused]] HRESULT hr = m_directManipulationUpdateManager->Update(nullptr);
GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationUpdateManager update failed\n");
}
}
void GHOST_DirectManipulationHelper::setDPI(uint16_t dpi)
{
m_directManipulationEventHandler->dpi = dpi;
}
GHOST_TTrackpadInfo GHOST_DirectManipulationHelper::getTrackpadInfo()
{
GHOST_TTrackpadInfo result = m_directManipulationEventHandler->accumulated_values;
result.isScrollDirectionInverted = m_isScrollDirectionInverted;
m_directManipulationEventHandler->accumulated_values = {0, 0, 0};
return result;
}
GHOST_DirectManipulationHelper::~GHOST_DirectManipulationHelper()
{
HRESULT hr;
hr = m_directManipulationViewport->Stop();
GHOST_ASSERT(SUCCEEDED(hr), "Viewport stop failed\n");
hr = m_directManipulationViewport->RemoveEventHandler(m_directManipulationViewportHandlerCookie);
GHOST_ASSERT(SUCCEEDED(hr), "Viewport remove event handler failed\n");
hr = m_directManipulationViewport->Abandon();
GHOST_ASSERT(SUCCEEDED(hr), "Viewport abandon failed\n");
hr = m_directManipulationManager->Deactivate(m_hWnd);
GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationManager deactivate failed\n");
if (m_scrollDirectionChangeEvent) {
CloseHandle(m_scrollDirectionChangeEvent);
m_scrollDirectionChangeEvent = NULL;
}
if (m_scrollDirectionRegKey) {
RegCloseKey(m_scrollDirectionRegKey);
m_scrollDirectionRegKey = NULL;
}
}
GHOST_DirectManipulationViewportEventHandler::GHOST_DirectManipulationViewportEventHandler(
uint16_t dpi)
: accumulated_values({0, 0, 0}), dpi(dpi), dm_status(DIRECTMANIPULATION_BUILDING)
{
}
void GHOST_DirectManipulationViewportEventHandler::resetViewport(
IDirectManipulationViewport *viewport)
{
if (gesture_state != GESTURE_NONE) {
[[maybe_unused]] HRESULT hr = viewport->ZoomToRect(0.0f, 0.0f, 10000.0f, 10000.0f, FALSE);
GHOST_ASSERT(SUCCEEDED(hr), "Viewport reset failed\n");
}
gesture_state = GESTURE_NONE;
last_scale = PINCH_SCALE_FACTOR;
last_x = 0.0f;
last_y = 0.0f;
}
HRESULT GHOST_DirectManipulationViewportEventHandler::OnViewportStatusChanged(
IDirectManipulationViewport *viewport,
DIRECTMANIPULATION_STATUS current,
DIRECTMANIPULATION_STATUS previous)
{
dm_status = current;
if (current == previous) {
return S_OK;
}
if (previous == DIRECTMANIPULATION_ENABLED || current == DIRECTMANIPULATION_READY ||
(previous == DIRECTMANIPULATION_INERTIA && current != DIRECTMANIPULATION_INERTIA)) {
resetViewport(viewport);
}
return S_OK;
}
HRESULT GHOST_DirectManipulationViewportEventHandler::OnViewportUpdated(
IDirectManipulationViewport *viewport)
{
/* Nothing to do here. */
return S_OK;
}
HRESULT GHOST_DirectManipulationViewportEventHandler::OnContentUpdated(
IDirectManipulationViewport *viewport, IDirectManipulationContent *content)
{
float transform[6];
HRESULT hr = content->GetContentTransform(transform, ARRAYSIZE(transform));
GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationContent get transform failed\n");
const float device_scale_factor = dpi / 96.0f;
const float scale = transform[0] * PINCH_SCALE_FACTOR;
const float x = transform[4] / device_scale_factor;
const float y = transform[5] / device_scale_factor;
const float EPS = 3e-5;
/* Ignore repeating or incorrect input. */
if ((fabs(scale - last_scale) <= EPS && fabs(x - last_x) <= EPS && fabs(y - last_y) <= EPS) ||
scale == 0.0f) {
GHOST_PRINT("Ignoring touchpad input\n");
return hr;
}
/* Assume that every gesture is a pan in the beginning.
* If it's a pinch, the gesture will be changed below. */
if (gesture_state == GESTURE_NONE) {
gesture_state = GESTURE_PAN;
}
/* DM doesn't always immediately recognize pinch gestures,
* so allow transition from pan to pinch. */
if (gesture_state == GESTURE_PAN) {
if (fabs(scale - PINCH_SCALE_FACTOR) > EPS) {
gesture_state = GESTURE_PINCH;
}
}
/* This state machine is used here because:
* 1. Pinch and pan gestures must be differentiated and cannot be processed at the same time
* because XY transform values become nonsensical during pinch gesture.
* 2. GHOST requires delta values for events while DM provides transformation matrix of the
* current gesture.
* 3. GHOST events accept integer values while DM values are non-integer.
* Truncated fractional parts are accumulated and accounted for in following updates.
*/
switch (gesture_state) {
case GESTURE_PINCH: {
int32_t dscale = roundf(scale - last_scale);
last_scale += dscale;
accumulated_values.scale += dscale;
break;
}
case GESTURE_PAN: {
int32_t dx = roundf(x - last_x);
int32_t dy = roundf(y - last_y);
last_x += dx;
last_y += dy;
accumulated_values.x += dx;
accumulated_values.y += dy;
break;
}
case GESTURE_NONE:
break;
}
return hr;
}

View File

@ -0,0 +1,138 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup GHOST
* Declaration of GHOST DirectManipulation classes.
*/
#pragma once
#ifndef WIN32
# error WIN32 only!
#endif // WIN32
#include "GHOST_Types.h"
#include <directmanipulation.h>
#include <wrl.h>
#define PINCH_SCALE_FACTOR 125.0f
typedef struct {
int32_t x, y, scale;
bool isScrollDirectionInverted;
} GHOST_TTrackpadInfo;
class GHOST_DirectManipulationHelper;
class GHOST_DirectManipulationViewportEventHandler
: public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>,
Microsoft::WRL::Implements<
Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>,
Microsoft::WRL::FtmBase,
IDirectManipulationViewportEventHandler>> {
public:
GHOST_DirectManipulationViewportEventHandler(uint16_t dpi);
/*
* Resets viewport and tracked touchpad state.
*/
void resetViewport(IDirectManipulationViewport *viewport);
/* DirectManipulation callbacks. */
HRESULT STDMETHODCALLTYPE OnViewportStatusChanged(IDirectManipulationViewport *viewport,
DIRECTMANIPULATION_STATUS current,
DIRECTMANIPULATION_STATUS previous) override;
HRESULT STDMETHODCALLTYPE OnViewportUpdated(IDirectManipulationViewport *viewport) override;
HRESULT STDMETHODCALLTYPE OnContentUpdated(IDirectManipulationViewport *viewport,
IDirectManipulationContent *content) override;
private:
enum { GESTURE_NONE, GESTURE_PAN, GESTURE_PINCH } gesture_state;
int32_t last_x, last_y, last_scale;
GHOST_TTrackpadInfo accumulated_values;
uint16_t dpi;
DIRECTMANIPULATION_STATUS dm_status;
friend class GHOST_DirectManipulationHelper;
};
class GHOST_DirectManipulationHelper {
public:
/*
* Creates a GHOST_DirectManipulationHelper for the provided window.
* \param hWnd: The window receiving DirectManipulation events.
* \param dpi: The current DPI.
* \return Pointer to the new GHOST_DirectManipulationHelper if created, nullptr if there was an
* error.
*/
static GHOST_DirectManipulationHelper *create(HWND hWnd, uint16_t dpi);
~GHOST_DirectManipulationHelper();
/*
* Drives the DirectManipulation context.
* DirectManipulation's intended use is to tie user input into DirectComposition's compositor
* scaling and translating. We are not using DirectComposition and therefore must drive
* DirectManipulation manually.
*/
void update();
/*
* Sets pointer in contact with the DirectManipulation context.
* \param pointerId: ID of the pointer in contact.
*/
void onPointerHitTest(UINT32 pointerId);
/*
* Updates DPI information for touchpad scaling.
* \param dpi: The new DPI.
*/
void setDPI(uint16_t dpi);
/*
* Retrieves trackpad input.
* \return The accumulated trackpad translation and scale since last call.
*/
GHOST_TTrackpadInfo getTrackpadInfo();
private:
GHOST_DirectManipulationHelper(
HWND hWnd,
Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager,
Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager,
Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport,
Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
directManipulationEventHandler,
DWORD directManipulationViewportHandlerCookie,
bool isScrollDirectionInverted);
/*
* Retrieves the scroll direction from the registry.
* \return True if scroll direction is inverted.
*/
static bool getScrollDirectionFromReg();
/*
* Registers listener for registry scroll direction entry changes.
*/
void registerScrollDirectionChangeListener();
HWND m_hWnd;
HKEY m_scrollDirectionRegKey;
HANDLE m_scrollDirectionChangeEvent;
Microsoft::WRL::ComPtr<IDirectManipulationManager> m_directManipulationManager;
Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> m_directManipulationUpdateManager;
Microsoft::WRL::ComPtr<IDirectManipulationViewport> m_directManipulationViewport;
Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
m_directManipulationEventHandler;
DWORD m_directManipulationViewportHandlerCookie;
bool m_isScrollDirectionInverted;
};

View File

@ -16,9 +16,7 @@
#include "GHOST_ContextWGL.h"
#ifdef WIN32_COMPOSITING
# include <Dwmapi.h>
#endif
#include <Dwmapi.h>
#include <assert.h>
#include <math.h>
@ -70,6 +68,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
m_normal_state(GHOST_kWindowStateNormal),
m_user32(::LoadLibrary("user32.dll")),
m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : HWND_DESKTOP),
m_directManipulationHelper(NULL),
m_debug_context(is_debug)
{
DWORD style = parentwindow ?
@ -172,6 +171,8 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
break;
}
ThemeRefresh();
::ShowWindow(m_hWnd, nCmdShow);
#ifdef WIN32_COMPOSITING
@ -204,6 +205,42 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
/* Allow the showing of a progress bar on the taskbar. */
CoCreateInstance(
CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (LPVOID *)&m_Bar);
/* Initialize Direct Manipulation. */
m_directManipulationHelper = GHOST_DirectManipulationHelper::create(m_hWnd, getDPIHint());
}
void GHOST_WindowWin32::updateDirectManipulation()
{
if (!m_directManipulationHelper) {
return;
}
m_directManipulationHelper->update();
}
void GHOST_WindowWin32::onPointerHitTest(WPARAM wParam)
{
/* Only DM_POINTERHITTEST can be the first message of input sequence of touchpad input. */
if (!m_directManipulationHelper) {
return;
}
UINT32 pointerId = GET_POINTERID_WPARAM(wParam);
POINTER_INPUT_TYPE pointerType;
if (GetPointerType(pointerId, &pointerType) && pointerType == PT_TOUCHPAD) {
m_directManipulationHelper->onPointerHitTest(pointerId);
}
}
GHOST_TTrackpadInfo GHOST_WindowWin32::getTrackpadInfo()
{
if (!m_directManipulationHelper) {
return {0, 0, 0};
}
return m_directManipulationHelper->getTrackpadInfo();
}
GHOST_WindowWin32::~GHOST_WindowWin32()
@ -253,6 +290,9 @@ GHOST_WindowWin32::~GHOST_WindowWin32()
::DestroyWindow(m_hWnd);
m_hWnd = 0;
}
delete m_directManipulationHelper;
m_directManipulationHelper = NULL;
}
void GHOST_WindowWin32::adjustWindowRectForClosestMonitor(LPRECT win_rect,
@ -1016,6 +1056,32 @@ GHOST_TabletData GHOST_WindowWin32::getTabletData()
}
}
void GHOST_WindowWin32::ThemeRefresh()
{
DWORD lightMode;
DWORD pcbData = sizeof(lightMode);
if (RegGetValueW(HKEY_CURRENT_USER,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize\\",
L"AppsUseLightTheme",
RRF_RT_REG_DWORD,
NULL,
&lightMode,
&pcbData) == ERROR_SUCCESS) {
BOOL DarkMode = !lightMode;
/* 20 == DWMWA_USE_IMMERSIVE_DARK_MODE in Windows 11 SDK. This value was undocumented for
* Windows 10 versions 2004 and later, supported for Windows 11 Build 22000 and later. */
DwmSetWindowAttribute(this->m_hWnd, 20, &DarkMode, sizeof(DarkMode));
}
}
void GHOST_WindowWin32::updateDPI()
{
if (m_directManipulationHelper) {
m_directManipulationHelper->setDPI(getDPIHint());
}
}
uint16_t GHOST_WindowWin32::getDPIHint()
{
if (m_user32) {

View File

@ -13,6 +13,7 @@
#endif // WIN32
#include "GHOST_TaskbarWin32.h"
#include "GHOST_TrackpadWin32.h"
#include "GHOST_Window.h"
#include "GHOST_Wintab.h"
#ifdef WITH_INPUT_IME
@ -286,6 +287,8 @@ class GHOST_WindowWin32 : public GHOST_Window {
return GHOST_kFailure;
}
void updateDPI();
uint16_t getDPIHint() override;
/** True if the mouse is either over or captured by the window. */
@ -294,6 +297,9 @@ class GHOST_WindowWin32 : public GHOST_Window {
/** True if the window currently resizing. */
bool m_inLiveResize;
/** Called when OS colors change and when the window is created. */
void ThemeRefresh();
#ifdef WITH_INPUT_IME
GHOST_ImeWin32 *getImeInput()
{
@ -305,6 +311,19 @@ class GHOST_WindowWin32 : public GHOST_Window {
void endIME();
#endif /* WITH_INPUT_IME */
/*
* Drive DirectManipulation context.
*/
void updateDirectManipulation();
/*
* Handle DM_POINTERHITTEST events.
* \param wParam: wParam from the event.
*/
void onPointerHitTest(WPARAM wParam);
GHOST_TTrackpadInfo getTrackpadInfo();
private:
/**
* \param type: The type of rendering context create.
@ -388,6 +407,8 @@ class GHOST_WindowWin32 : public GHOST_Window {
HWND m_parentWindowHwnd;
GHOST_DirectManipulationHelper *m_directManipulationHelper;
#ifdef WITH_INPUT_IME
/** Handle input method editors event */
GHOST_ImeWin32 m_imeInput;

Binary file not shown.

View File

@ -16,4 +16,5 @@ __all__ = (
"mesh_utils",
"node_utils",
"view3d_utils",
"id_map_utils",
)

View File

@ -0,0 +1,49 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# <pep8 compliant>
from typing import Dict, Set
import bpy
from bpy.types import ID
__all__ = (
"get_id_reference_map",
"get_all_referenced_ids",
)
def get_id_reference_map() -> Dict[ID, Set[ID]]:
"""Return a dictionary of direct datablock references for every datablock in the blend file."""
inv_map = {}
for key, values in bpy.data.user_map().items():
for value in values:
if value == key:
# So an object is not considered to be referencing itself.
continue
inv_map.setdefault(value, set()).add(key)
return inv_map
def recursive_get_referenced_ids(
ref_map: Dict[ID, Set[ID]], id: ID, referenced_ids: Set, visited: Set
):
"""Recursively populate referenced_ids with IDs referenced by id."""
if id in visited:
# Avoid infinite recursion from circular references.
return
visited.add(id)
for ref in ref_map.get(id, []):
referenced_ids.add(ref)
recursive_get_referenced_ids(
ref_map=ref_map, id=ref, referenced_ids=referenced_ids, visited=visited
)
def get_all_referenced_ids(id: ID, ref_map: Dict[ID, Set[ID]]) -> Set[ID]:
"""Return a set of IDs directly or indirectly referenced by id."""
referenced_ids = set()
recursive_get_referenced_ids(
ref_map=ref_map, id=id, referenced_ids=referenced_ids, visited=set()
)
return referenced_ids

View File

@ -1145,3 +1145,11 @@ class TextureNode(NodeInternal):
@classmethod
def poll(cls, ntree):
return ntree.bl_idname == 'TextureNodeTree'
class GeometryNode(NodeInternal):
__slots__ = ()
@classmethod
def poll(cls, ntree):
return ntree.bl_idname == 'GeometryNodeTree'

View File

@ -1300,6 +1300,8 @@ def km_uv_editor(params):
{"properties": [("data_path", 'tool_settings.use_snap_uv')]}),
("wm.context_menu_enum", {"type": 'TAB', "value": 'PRESS', "shift": True, "ctrl": True},
{"properties": [("data_path", 'tool_settings.snap_uv_element')]}),
("wm.context_toggle", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.show_gizmo')]}),
*_template_items_context_menu("IMAGE_MT_uvs_context_menu", params.context_menu_event),
])
@ -1967,6 +1969,8 @@ def km_image(params):
),
("image.render_border", {"type": 'B', "value": 'PRESS', "ctrl": True}, None),
("image.clear_render_border", {"type": 'B', "value": 'PRESS', "ctrl": True, "alt": True}, None),
("wm.context_toggle", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.show_gizmo')]}),
*_template_items_context_menu("IMAGE_MT_mask_context_menu", params.context_menu_event),
])

View File

@ -769,6 +769,18 @@ def brush_settings(layout, context, brush, popover=False):
elif brush.color_type == 'GRADIENT':
layout.row().prop(brush, "gradient_fill_mode", expand=True)
elif mode == 'SCULPT_CURVES':
if brush.curves_sculpt_tool == 'ADD':
layout.prop(brush.curves_sculpt_settings, "add_amount")
layout.prop(brush.curves_sculpt_settings, "points_per_curve")
layout.prop(brush.curves_sculpt_settings, "curve_length")
layout.prop(brush.curves_sculpt_settings, "interpolate_length")
layout.prop(brush.curves_sculpt_settings, "interpolate_shape")
use_frontface = True
elif brush.curves_sculpt_tool == 'GROW_SHRINK':
layout.prop(brush.curves_sculpt_settings, "scale_uniform")
layout.prop(brush.curves_sculpt_settings, "minimum_length")
def brush_shared_settings(layout, context, brush, popover=False):
""" Draw simple brush settings that are shared between different paint modes. """
@ -827,6 +839,7 @@ def brush_shared_settings(layout, context, brush, popover=False):
if mode == 'SCULPT_CURVES':
size = True
strength = True
direction = brush.curves_sculpt_tool == "GROW_SHRINK"
### Draw settings. ###
ups = context.scene.tool_settings.unified_paint_settings
@ -925,16 +938,6 @@ def brush_settings_advanced(layout, context, brush, popover=False):
col.prop(brush, "use_original_plane", text="Plane")
layout.separator()
elif mode == 'SCULPT_CURVES':
if brush.curves_sculpt_tool == 'ADD':
layout.prop(brush.curves_sculpt_settings, "add_amount")
layout.prop(brush.curves_sculpt_settings, "curve_length")
layout.prop(brush.curves_sculpt_settings, "interpolate_length")
layout.prop(brush.curves_sculpt_settings, "interpolate_shape")
elif brush.curves_sculpt_tool == 'GROW_SHRINK':
layout.prop(brush.curves_sculpt_settings, "scale_uniform")
layout.prop(brush.curves_sculpt_settings, "minimum_length")
# 3D and 2D Texture Paint.
elif mode in {'PAINT_TEXTURE', 'PAINT_2D'}:
capabilities = brush.image_paint_capabilities

View File

@ -840,7 +840,6 @@ class TEXTURE_PT_colors_ramp(TextureButtonsPanel, TextureColorsPoll, Panel):
if is_active:
layout.template_color_ramp(tex, "color_ramp", expand=True)
else:
layout.alignment = 'RIGHT'
layout.label(text="Enable the Color Ramp first")

View File

@ -806,6 +806,13 @@ class IMAGE_HT_header(Header):
layout.separator_spacer()
# Gizmo toggle & popover.
row = layout.row(align=True)
row.prop(sima, "show_gizmo", icon='GIZMO', text="")
sub = row.row(align=True)
sub.active = sima.show_gizmo
sub.popover(panel="IMAGE_PT_gizmo_display", text="")
# Overlay toggle & popover
row = layout.row(align=True)
row.prop(overlay, "show_overlays", icon='OVERLAY', text="")
@ -1453,6 +1460,26 @@ class IMAGE_PT_uv_cursor(Panel):
col.prop(sima, "cursor_location", text="Location")
class IMAGE_PT_gizmo_display(Panel):
bl_space_type = 'IMAGE_EDITOR'
bl_region_type = 'HEADER'
bl_label = "Gizmos"
bl_ui_units_x = 8
def draw(self, context):
layout = self.layout
view = context.space_data
col = layout.column()
col.label(text="Viewport Gizmos")
col.separator()
col.active = view.show_gizmo
colsub = col.column()
colsub.prop(view, "show_gizmo_navigate", text="Navigate")
class IMAGE_PT_overlay(Panel):
bl_space_type = 'IMAGE_EDITOR'
bl_region_type = 'HEADER'
@ -1680,6 +1707,7 @@ classes = (
IMAGE_PT_scope_sample,
IMAGE_PT_uv_cursor,
IMAGE_PT_annotation,
IMAGE_PT_gizmo_display,
IMAGE_PT_overlay,
IMAGE_PT_overlay_guides,
IMAGE_PT_overlay_uv_edit,

View File

@ -2274,6 +2274,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel):
self._draw_items(
context, (
({"property": "use_new_curves_type"}, "T68981"),
({"property": "use_new_curves_tools"}, "T68981"),
({"property": "use_new_point_cloud_type"}, "T75717"),
({"property": "use_full_frame_compositor"}, "T88150"),
({"property": "enable_eevee_next"}, "T93220"),

View File

@ -151,6 +151,11 @@ class VIEW3D_HT_tool_header(Header):
row.popover(panel="VIEW3D_PT_sculpt_symmetry_for_topbar", text="")
elif mode_string == 'PAINT_VERTEX':
row.popover(panel="VIEW3D_PT_tools_vertexpaint_symmetry_for_topbar", text="")
elif mode_string == 'SCULPT_CURVES':
_row, sub = row_for_mirror()
sub.prop(context.object.data, "use_mirror_x", text="X", toggle=True)
sub.prop(context.object.data, "use_mirror_y", text="Y", toggle=True)
sub.prop(context.object.data, "use_mirror_z", text="Z", toggle=True)
# Expand panels from the side-bar as popovers.
popover_kw = {"space_type": 'VIEW_3D', "region_type": 'UI', "category": "Tool"}
@ -511,9 +516,10 @@ class _draw_tool_settings_context_mode:
layout.prop(brush, "curve_preset")
if brush.curves_sculpt_tool == 'ADD':
layout.prop(brush, "use_frontface")
layout.prop(brush, "use_frontface", text="Front Faces Only")
layout.prop(brush, "falloff_shape", expand=True)
layout.prop(brush.curves_sculpt_settings, "add_amount")
layout.prop(brush.curves_sculpt_settings, "points_per_curve")
layout.prop(brush.curves_sculpt_settings, "curve_length")
layout.prop(brush.curves_sculpt_settings, "interpolate_length")
layout.prop(brush.curves_sculpt_settings, "interpolate_shape")
@ -2037,7 +2043,7 @@ class VIEW3D_MT_curve_add(Menu):
bl_idname = "VIEW3D_MT_curve_add"
bl_label = "Curve"
def draw(self, _context):
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
@ -2051,18 +2057,14 @@ class VIEW3D_MT_curve_add(Menu):
layout.operator("curve.primitive_nurbs_circle_add", text="Nurbs Circle", icon='CURVE_NCIRCLE')
layout.operator("curve.primitive_nurbs_path_add", text="Path", icon='CURVE_PATH')
experimental = context.preferences.experimental
if experimental.use_new_curves_type:
layout.separator()
class VIEW3D_MT_curves_add(Menu):
bl_idname = "VIEW3D_MT_curves_add"
bl_label = "Curves"
layout.operator("object.curves_empty_hair_add", text="Empty Hair", icon='CURVES_DATA')
def draw(self, _context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("object.curves_empty_hair_add", text="Empty Hair", icon='CURVES_DATA')
layout.operator("object.curves_random_add", text="Random", icon='CURVES_DATA')
if experimental.use_new_curves_tools:
layout.operator("object.curves_random_add", text="Random", icon='CURVES_DATA')
class VIEW3D_MT_surface_add(Menu):
@ -2217,8 +2219,6 @@ class VIEW3D_MT_add(Menu):
# layout.operator_menu_enum("object.curve_add", "type", text="Curve", icon='OUTLINER_OB_CURVE')
layout.menu("VIEW3D_MT_curve_add", icon='OUTLINER_OB_CURVE')
if context.preferences.experimental.use_new_curves_type:
layout.menu("VIEW3D_MT_curves_add", icon='OUTLINER_OB_CURVES')
# layout.operator_menu_enum("object.surface_add", "type", text="Surface", icon='OUTLINER_OB_SURFACE')
layout.menu("VIEW3D_MT_surface_add", icon='OUTLINER_OB_SURFACE')
layout.menu("VIEW3D_MT_metaball_add", text="Metaball", icon='OUTLINER_OB_META')
@ -3149,7 +3149,9 @@ class VIEW3D_MT_sculpt_curves(Menu):
def draw(self, _context):
layout = self.layout
layout.operator("curves.snap_curves_to_surface")
layout.operator("curves.snap_curves_to_surface", text="Snap to Deformed Surface").attach_mode = 'DEFORM'
layout.operator("curves.snap_curves_to_surface", text="Snap to Nearest Surface").attach_mode = 'NEAREST'
layout.separator()
layout.operator("curves.convert_to_particle_system", text="Convert to Particle System")
@ -5687,7 +5689,7 @@ class VIEW3D_PT_object_type_visibility(Panel):
bl_label = "View Object Types"
bl_ui_units_x = 7
# Allows derived classes to pass view data other than context.space_data.
# Allows derived classes to pass view data other than context.space_data.
# This is used by the official VR add-on, which passes XrSessionSettings
# since VR has a 3D view that only exists for the duration of the VR session.
def draw_ex(self, context, view, show_select):
@ -7656,7 +7658,6 @@ classes = (
VIEW3D_MT_angle_control,
VIEW3D_MT_mesh_add,
VIEW3D_MT_curve_add,
VIEW3D_MT_curves_add,
VIEW3D_MT_surface_add,
VIEW3D_MT_edit_metaball_context_menu,
VIEW3D_MT_metaball_add,

View File

@ -1049,6 +1049,36 @@ class VIEW3D_PT_sculpt_symmetry_for_topbar(Panel):
draw = VIEW3D_PT_sculpt_symmetry.draw
class VIEW3D_PT_curves_sculpt_symmetry(Panel, View3DPaintPanel):
bl_context = ".curves_sculpt" # dot on purpose (access from topbar)
bl_label = "Symmetry"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'CURVES'
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
curves = context.object.data
row = layout.row(align=True, heading="Mirror")
row.prop(curves, "use_mirror_x", text="X", toggle=True)
row.prop(curves, "use_mirror_y", text="Y", toggle=True)
row.prop(curves, "use_mirror_z", text="Z", toggle=True)
class VIEW3D_PT_curves_sculpt_symmetry_for_topbar(Panel):
bl_space_type = 'TOPBAR'
bl_region_type = 'HEADER'
bl_label = "Symmetry"
draw = VIEW3D_PT_curves_sculpt_symmetry.draw
# ********** default tools for weight-paint ****************
@ -1921,7 +1951,7 @@ class VIEW3D_PT_tools_grease_pencil_sculpt_brush_advanced(GreasePencilSculptAdva
return False
tool = brush.gpencil_sculpt_tool
return tool != 'CLONE'
return tool != 'CLONE'
class VIEW3D_PT_tools_grease_pencil_sculpt_brush_popover(GreasePencilSculptAdvancedPanel, View3DPanel, Panel):
@ -1939,7 +1969,7 @@ class VIEW3D_PT_tools_grease_pencil_sculpt_brush_popover(GreasePencilSculptAdvan
return False
tool = brush.gpencil_sculpt_tool
return tool != 'CLONE'
return tool != 'CLONE'
# Grease Pencil weight painting tools
@ -2351,6 +2381,9 @@ classes = (
VIEW3D_PT_sculpt_options,
VIEW3D_PT_sculpt_options_gravity,
VIEW3D_PT_curves_sculpt_symmetry,
VIEW3D_PT_curves_sculpt_symmetry_for_topbar,
VIEW3D_PT_tools_weightpaint_symmetry,
VIEW3D_PT_tools_weightpaint_symmetry_for_topbar,
VIEW3D_PT_tools_weightpaint_options,

View File

@ -419,12 +419,10 @@ shader_node_categories = [
NodeItem("ShaderNodeRGBToBW"),
NodeItem("ShaderNodeShaderToRGB", poll=object_eevee_shader_nodes_poll),
NodeItem("ShaderNodeVectorMath"),
NodeItem("ShaderNodeSeparateRGB"),
NodeItem("ShaderNodeCombineRGB"),
NodeItem("ShaderNodeSeparateColor"),
NodeItem("ShaderNodeCombineColor"),
NodeItem("ShaderNodeSeparateXYZ"),
NodeItem("ShaderNodeCombineXYZ"),
NodeItem("ShaderNodeSeparateHSV"),
NodeItem("ShaderNodeCombineHSV"),
NodeItem("ShaderNodeWavelength"),
NodeItem("ShaderNodeBlackbody"),
]),
@ -483,14 +481,8 @@ compositor_node_categories = [
NodeItem("CompositorNodePremulKey"),
NodeItem("CompositorNodeIDMask"),
NodeItem("CompositorNodeRGBToBW"),
NodeItem("CompositorNodeSepRGBA"),
NodeItem("CompositorNodeCombRGBA"),
NodeItem("CompositorNodeSepHSVA"),
NodeItem("CompositorNodeCombHSVA"),
NodeItem("CompositorNodeSepYUVA"),
NodeItem("CompositorNodeCombYUVA"),
NodeItem("CompositorNodeSepYCCA"),
NodeItem("CompositorNodeCombYCCA"),
NodeItem("CompositorNodeSeparateColor"),
NodeItem("CompositorNodeCombineColor"),
NodeItem("CompositorNodeSeparateXYZ"),
NodeItem("CompositorNodeCombineXYZ"),
NodeItem("CompositorNodeSwitchView"),
@ -576,8 +568,8 @@ texture_node_categories = [
NodeItem("TextureNodeCurveRGB"),
NodeItem("TextureNodeInvert"),
NodeItem("TextureNodeHueSaturation"),
NodeItem("TextureNodeCompose"),
NodeItem("TextureNodeDecompose"),
NodeItem("TextureNodeCombineColor"),
NodeItem("TextureNodeSeparateColor"),
]),
TextureNodeCategory("TEX_PATTERN", "Pattern", items=[
NodeItem("TextureNodeChecker"),
@ -629,8 +621,8 @@ geometry_node_categories = [
NodeItem("ShaderNodeMixRGB"),
NodeItem("ShaderNodeRGBCurve"),
NodeItem("ShaderNodeValToRGB"),
NodeItem("ShaderNodeSeparateRGB"),
NodeItem("ShaderNodeCombineRGB"),
NodeItem("FunctionNodeSeparateColor"),
NodeItem("FunctionNodeCombineColor"),
]),
GeometryNodeCategory("GEO_CURVE", "Curve", items=curve_node_items),
GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[

View File

@ -15,8 +15,9 @@ if(WITH_CLANG_TIDY AND NOT MSVC)
endif()
find_package(ClangTidy REQUIRED)
set(CMAKE_C_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE})
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE})
set(CMAKE_C_CLANG_TIDY
${CLANG_TIDY_EXECUTABLE};--extra-arg=-Wno-error=unknown-warning-option)
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE};--extra-arg=-Wno-error=unknown-warning-option)
endif()
add_subdirectory(blender)

View File

@ -1374,7 +1374,7 @@ bool blf_font_size(FontBLF *font, float size, unsigned int dpi)
font->dpi = dpi;
}
else {
printf("The current font does not support the size, %f and dpi, %u\n", size, dpi);
printf("The current font does not support the size, %f and DPI, %u\n", size, dpi);
return false;
}
}

View File

@ -123,7 +123,7 @@ typedef struct GlyphCacheBLF {
/* font size. */
float size;
/* and dpi. */
/* and DPI. */
unsigned int dpi;
bool bold;
@ -264,7 +264,7 @@ typedef struct FontBLF {
/* the width to wrap the text, see BLF_WORD_WRAP */
int wrap_width;
/* font dpi (default 72). */
/* Font DPI (default 72). */
unsigned int dpi;
/* font size. */
@ -276,7 +276,8 @@ typedef struct FontBLF {
/* font options. */
int flags;
/* List of glyph caches (GlyphCacheBLF) for this font for size, dpi, bold, italic.
/**
* List of glyph caches (#GlyphCacheBLF) for this font for size, DPI, bold, italic.
* Use blf_glyph_cache_acquire(font) and blf_glyph_cache_release(font) to access cache!
*/
ListBase cache;

View File

@ -129,6 +129,36 @@ bool BKE_curvemapping_RGBA_does_something(const struct CurveMapping *cumap);
void BKE_curvemapping_table_F(const struct CurveMapping *cumap, float **array, int *size);
void BKE_curvemapping_table_RGBA(const struct CurveMapping *cumap, float **array, int *size);
/** Get the minimum x value of each curve map table. */
void BKE_curvemapping_get_range_minimums(const struct CurveMapping *curve_mapping,
float minimums[4]);
/** Get the reciprocal of the difference between the maximum and the minimum x value of each curve
* map table. Evaluation parameters can be multiplied by this value to be normalized. If the
* difference is zero, 1^8 is returned. */
void BKE_curvemapping_compute_range_dividers(const struct CurveMapping *curve_mapping,
float dividers[4]);
/** Compute the slopes at the start and end points of each curve map. The slopes are multiplied by
* the range of the curve map to compensate for parameter normalization. If the slope is vertical,
* 1^8 is returned. */
void BKE_curvemapping_compute_slopes(const struct CurveMapping *curve_mapping,
float start_slopes[4],
float end_slopes[4]);
/** Check if the curve map at the index is identity, that is, does nothing. A curve map is said to
* be identity if:
* - The curve mapping uses extrapolation.
* - Its range is 1.
* - The slope at its start point is 1.
* - The slope at its end point is 1.
* - The number of points is 2.
* - The start point is at (0, 0).
* - The end point is at (1, 1).
* Note that this could return false even if the curve map is identity, this happens in the case
* when more than 2 points exist in the curve map but all points are collinear. */
bool BKE_curvemapping_is_map_identity(const struct CurveMapping *curve_mapping, int index);
/**
* Call when you do images etc, needs restore too. also verifies tables.
* non-const (these modify the curve).

View File

@ -48,6 +48,13 @@ struct BasisCache {
* In other words, the index of the first control point that influences this evaluated point.
*/
Vector<int> start_indices;
/**
* The result of #check_valid_size_and_order, to avoid retrieving its inputs later on.
* If this is true, the data above will be invalid, and original data should be copied
* to the evaluated result.
*/
bool invalid = false;
};
} // namespace curves::nurbs
@ -174,11 +181,18 @@ class CurvesGeometry : public ::CurvesGeometry {
/** Update the cached count of curves of each type, necessary after #curve_types_for_write. */
void update_curve_types();
bool has_curve_with_type(const CurveType type) const;
bool has_curve_with_type(CurveType type) const;
/** Return true if all of the curves have the provided type. */
bool is_single_type(CurveType type) const;
/** Return the number of curves with each type. */
const std::array<int, CURVE_TYPES_NUM> &curve_type_counts() const;
/**
* All of the curve indices for curves with a specific type.
*/
IndexMask indices_for_curve_type(CurveType type, Vector<int64_t> &r_indices) const;
IndexMask indices_for_curve_type(CurveType type,
IndexMask selection,
Vector<int64_t> &r_indices) const;
Span<float3> positions() const;
MutableSpan<float3> positions_for_write();
@ -276,11 +290,6 @@ class CurvesGeometry : public ::CurvesGeometry {
bool bounds_min_max(float3 &min, float3 &max) const;
private:
/**
* All of the curve indices for curves with a specific type.
*/
IndexMask indices_for_curve_type(CurveType type, Vector<int64_t> &r_indices) const;
/* --------------------------------------------------------------------
* Evaluation.
*/
@ -412,7 +421,7 @@ namespace curves {
inline int curve_segment_size(const int points_num, const bool cyclic)
{
BLI_assert(points_num > 0);
return cyclic ? points_num : points_num - 1;
return (cyclic && points_num > 1) ? points_num : points_num - 1;
}
inline float2 encode_surface_bary_coord(const float3 &v)
@ -760,7 +769,7 @@ inline IndexRange CurvesGeometry::lengths_range_for_curve(const int curve_index,
BLI_assert(cyclic == this->cyclic()[curve_index]);
const IndexRange points = this->evaluated_points_for_curve(curve_index);
const int start = points.start() + curve_index;
return {start, points.is_empty() ? 0 : curves::curve_segment_size(points.size(), cyclic)};
return {start, curves::curve_segment_size(points.size(), cyclic)};
}
inline Span<float> CurvesGeometry::evaluated_lengths_for_curve(const int curve_index,
@ -775,8 +784,7 @@ inline float CurvesGeometry::evaluated_length_total_for_curve(const int curve_in
const bool cyclic) const
{
const Span<float> lengths = this->evaluated_lengths_for_curve(curve_index, cyclic);
/* Check for curves that have no evaluated segments. */
return lengths.is_empty() ? 0.0f : lengths.last();
return lengths.last();
}
/** \} */

View File

@ -162,4 +162,14 @@ class AnonymousAttributeFieldInput : public GeometryFieldInput {
bool is_equal_to(const fn::FieldNode &other) const override;
};
class CurveLengthFieldInput final : public GeometryFieldInput {
public:
CurveLengthFieldInput();
GVArray get_varray_for_context(const GeometryComponent &component,
AttributeDomain domain,
IndexMask mask) const final;
uint64_t hash() const override;
bool is_equal_to(const fn::FieldNode &other) const override;
};
} // namespace blender::bke

View File

@ -40,7 +40,7 @@ struct ReportList;
struct IDPrincipleProperties *BKE_lib_principleprop_init(struct ID *id);
#if 0
/**
* Shallow or deep copy of a whole princple properties from \a src_id to \a dst_id.
* Shallow or deep copy of a whole principle properties from \a src_id to \a dst_id.
*/
void BKE_lib_principleprop_copy(struct ID *dst_id, const struct ID *src_id, bool do_full_copy);
#endif

View File

@ -57,7 +57,9 @@ enum {
ID_REMAP_FORCE_NEVER_NULL_USAGE = 1 << 3,
/** Do not remap library override pointers. */
ID_REMAP_SKIP_OVERRIDE_LIBRARY = 1 << 5,
/** Don't touch the user count (use for low level actions such as swapping pointers). */
/** Don't touch the special user counts (use when the 'old' remapped ID remains in use):
* - Do not transfer 'fake user' status from old to new ID.
* - Do not clear 'extra user' from old ID. */
ID_REMAP_SKIP_USER_CLEAR = 1 << 6,
/**
* Force internal ID runtime pointers (like `ID.newid`, `ID.orig_id` etc.) to also be processed.

View File

@ -52,7 +52,7 @@ void BKE_object_material_remap_calc(struct Object *ob_dst,
*/
void BKE_object_material_from_eval_data(struct Main *bmain,
struct Object *ob_orig,
struct ID *data_eval);
const struct ID *data_eval);
struct Material *BKE_material_add(struct Main *bmain, const char *name);
struct Material *BKE_gpencil_material_add(struct Main *bmain, const char *name);
void BKE_gpencil_material_attr_init(struct Material *ma);

View File

@ -893,6 +893,14 @@ struct Mesh *BKE_mesh_merge_verts(struct Mesh *mesh,
int tot_vtargetmap,
int merge_mode);
/**
* Account for custom-data such as UV's becoming detached because of of imprecision
* in custom-data interpolation.
* Without running this operation subdivision surface can cause UV's to be disconnected,
* see: T81065.
*/
void BKE_mesh_merge_customdata_for_apply_modifier(struct Mesh *me);
/* Flush flags. */
/**

View File

@ -113,7 +113,7 @@ typedef enum ModifierApplyFlag {
/** Render time. */
MOD_APPLY_RENDER = 1 << 0,
/** Result of evaluation will be cached, so modifier might
* want to cache data for quick updates (used by subsurf) */
* want to cache data for quick updates (used by subdivision-surface) */
MOD_APPLY_USECACHE = 1 << 1,
/** Modifier evaluated for undeformed texture coordinates */
MOD_APPLY_ORCO = 1 << 2,

View File

@ -1090,8 +1090,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define SH_NODE_SQUEEZE 117
//#define SH_NODE_MATERIAL_EXT 118
#define SH_NODE_INVERT 119
#define SH_NODE_SEPRGB 120
#define SH_NODE_COMBRGB 121
#define SH_NODE_SEPRGB_LEGACY 120
#define SH_NODE_COMBRGB_LEGACY 121
#define SH_NODE_HUE_SAT 122
#define SH_NODE_OUTPUT_MATERIAL 124
@ -1147,8 +1147,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define SH_NODE_WAVELENGTH 180
#define SH_NODE_BLACKBODY 181
#define SH_NODE_VECT_TRANSFORM 182
#define SH_NODE_SEPHSV 183
#define SH_NODE_COMBHSV 184
#define SH_NODE_SEPHSV_LEGACY 183
#define SH_NODE_COMBHSV_LEGACY 184
#define SH_NODE_BSDF_HAIR 185
// #define SH_NODE_LAMP 186
#define SH_NODE_UVMAP 187
@ -1175,6 +1175,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define SH_NODE_VECTOR_ROTATE 708
#define SH_NODE_CURVE_FLOAT 709
#define SH_NODE_POINT_INFO 710
#define SH_NODE_COMBINE_COLOR 711
#define SH_NODE_SEPARATE_COLOR 712
/** \} */
@ -1202,8 +1204,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define CMP_NODE_MAP_VALUE 213
#define CMP_NODE_TIME 214
#define CMP_NODE_VECBLUR 215
#define CMP_NODE_SEPRGBA 216
#define CMP_NODE_SEPHSVA 217
#define CMP_NODE_SEPRGBA_LEGACY 216
#define CMP_NODE_SEPHSVA_LEGACY 217
#define CMP_NODE_SETALPHA 218
#define CMP_NODE_HUE_SAT 219
#define CMP_NODE_IMAGE 220
@ -1213,14 +1215,14 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define CMP_NODE_TEXTURE 224
#define CMP_NODE_TRANSLATE 225
#define CMP_NODE_ZCOMBINE 226
#define CMP_NODE_COMBRGBA 227
#define CMP_NODE_COMBRGBA_LEGACY 227
#define CMP_NODE_DILATEERODE 228
#define CMP_NODE_ROTATE 229
#define CMP_NODE_SCALE 230
#define CMP_NODE_SEPYCCA 231
#define CMP_NODE_COMBYCCA 232
#define CMP_NODE_SEPYUVA 233
#define CMP_NODE_COMBYUVA 234
#define CMP_NODE_SEPYCCA_LEGACY 231
#define CMP_NODE_COMBYCCA_LEGACY 232
#define CMP_NODE_SEPYUVA_LEGACY 233
#define CMP_NODE_COMBYUVA_LEGACY 234
#define CMP_NODE_DIFF_MATTE 235
#define CMP_NODE_COLOR_SPILL 236
#define CMP_NODE_CHROMA_MATTE 237
@ -1232,7 +1234,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define CMP_NODE_ID_MASK 243
#define CMP_NODE_DEFOCUS 244
#define CMP_NODE_DISPLACE 245
#define CMP_NODE_COMBHSVA 246
#define CMP_NODE_COMBHSVA_LEGACY 246
#define CMP_NODE_MATH 247
#define CMP_NODE_LUMA_MATTE 248
#define CMP_NODE_BRIGHTCONTRAST 249
@ -1289,6 +1291,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define CMP_NODE_SCENE_TIME 329
#define CMP_NODE_SEPARATE_XYZ 330
#define CMP_NODE_COMBINE_XYZ 331
#define CMP_NODE_COMBINE_COLOR 332
#define CMP_NODE_SEPARATE_COLOR 333
/* channel toggles */
#define CMP_CHAN_RGB 1
@ -1354,11 +1358,13 @@ struct TexResult;
#define TEX_NODE_TRANSLATE 416
#define TEX_NODE_COORD 417
#define TEX_NODE_DISTANCE 418
#define TEX_NODE_COMPOSE 419
#define TEX_NODE_DECOMPOSE 420
#define TEX_NODE_COMPOSE_LEGACY 419
#define TEX_NODE_DECOMPOSE_LEGACY 420
#define TEX_NODE_VALTONOR 421
#define TEX_NODE_SCALE 422
#define TEX_NODE_AT 423
#define TEX_NODE_COMBINE_COLOR 424
#define TEX_NODE_SEPARATE_COLOR 425
/* 501-599 reserved. Use like this: TEX_NODE_PROC + TEX_CLOUDS, etc */
#define TEX_NODE_PROC 500
@ -1511,6 +1517,8 @@ struct TexResult;
#define FN_NODE_REPLACE_STRING 1218
#define FN_NODE_INPUT_BOOL 1219
#define FN_NODE_INPUT_INT 1220
#define FN_NODE_SEPARATE_COLOR 1221
#define FN_NODE_COMBINE_COLOR 1222
/** \} */

View File

@ -733,7 +733,7 @@ enum {
/* paint_vertex.cc */
/**
* Fills the object's active color atribute layer with the fill color.
* Fills the object's active color attribute layer with the fill color.
*
* \param[in] ob: The object.
* \param[in] fill_color: The fill color.

View File

@ -199,6 +199,7 @@ set(SRC
intern/mesh_iterators.c
intern/mesh_mapping.c
intern/mesh_merge.c
intern/mesh_merge_customdata.cc
intern/mesh_mirror.c
intern/mesh_normals.cc
intern/mesh_remap.c
@ -241,8 +242,8 @@ set(SRC
intern/particle_child.c
intern/particle_distribute.c
intern/particle_system.c
intern/pbvh.cc
intern/pbvh.c
intern/pbvh.cc
intern/pbvh_bmesh.c
intern/pbvh_pixels.cc
intern/pointcache.c

View File

@ -320,18 +320,22 @@ bool BKE_where_on_path(const Object *ob,
key_curve_position_weights(frac, w, KEY_BSPLINE);
}
r_vec[0] = /* X */
w[0] * p0->vec[0] + w[1] * p1->vec[0] + w[2] * p2->vec[0] + w[3] * p3->vec[0];
r_vec[1] = /* Y */
w[0] * p0->vec[1] + w[1] * p1->vec[1] + w[2] * p2->vec[1] + w[3] * p3->vec[1];
r_vec[2] = /* Z */
w[0] * p0->vec[2] + w[1] * p1->vec[2] + w[2] * p2->vec[2] + w[3] * p3->vec[2];
if (r_vec) {
r_vec[0] = /* X */
w[0] * p0->vec[0] + w[1] * p1->vec[0] + w[2] * p2->vec[0] + w[3] * p3->vec[0];
r_vec[1] = /* Y */
w[0] * p0->vec[1] + w[1] * p1->vec[1] + w[2] * p2->vec[1] + w[3] * p3->vec[1];
r_vec[2] = /* Z */
w[0] * p0->vec[2] + w[1] * p1->vec[2] + w[2] * p2->vec[2] + w[3] * p3->vec[2];
}
/* Clamp weights to 0-1 as we don't want to extrapolate other values than position. */
clamp_v4(w, 0.0f, 1.0f);
/* Tilt, should not be needed since we have quat still used. */
r_vec[3] = w[0] * p0->tilt + w[1] * p1->tilt + w[2] * p2->tilt + w[3] * p3->tilt;
if (r_vec) {
/* Tilt, should not be needed since we have quat still used. */
r_vec[3] = w[0] * p0->tilt + w[1] * p1->tilt + w[2] * p2->tilt + w[3] * p3->tilt;
}
if (r_quat) {
float totfac, q1[4], q2[4];

View File

@ -1564,7 +1564,7 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context,
}
if (GS(old_id->name) == ID_KE) {
/* Shape Keys are handled as part of their owning obdata (see below). This implies thar
/* Shape Keys are handled as part of their owning obdata (see below). This implies that
* there is no way to know when the old pointer gets invalid, so just clear it immediately.
*/
item->userdata = NULL;

View File

@ -1559,6 +1559,7 @@ void BKE_brush_init_curves_sculpt_settings(Brush *brush)
}
BrushCurvesSculptSettings *settings = brush->curves_sculpt_settings;
settings->add_amount = 1;
settings->points_per_curve = 8;
settings->minimum_length = 0.01f;
settings->curve_length = 0.3f;
}

View File

@ -1158,6 +1158,80 @@ bool BKE_curvemapping_RGBA_does_something(const CurveMapping *cumap)
return false;
}
void BKE_curvemapping_get_range_minimums(const CurveMapping *curve_mapping, float minimums[CM_TOT])
{
for (int i = 0; i < CM_TOT; i++) {
minimums[i] = curve_mapping->cm[i].mintable;
}
}
void BKE_curvemapping_compute_range_dividers(const CurveMapping *curve_mapping,
float dividers[CM_TOT])
{
for (int i = 0; i < CM_TOT; i++) {
const CurveMap *curve_map = &curve_mapping->cm[i];
dividers[i] = 1.0f / max_ff(1e-8f, curve_map->maxtable - curve_map->mintable);
}
}
void BKE_curvemapping_compute_slopes(const CurveMapping *curve_mapping,
float start_slopes[CM_TOT],
float end_slopes[CM_TOT])
{
float range_dividers[CM_TOT];
BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
for (int i = 0; i < CM_TOT; i++) {
const CurveMap *curve_map = &curve_mapping->cm[i];
/* If extrapolation is not enabled, the slopes are horizontal. */
if (!(curve_mapping->flag & CUMA_EXTEND_EXTRAPOLATE)) {
start_slopes[i] = 0.0f;
end_slopes[i] = 0.0f;
continue;
}
if (curve_map->ext_in[0] != 0.0f) {
start_slopes[i] = curve_map->ext_in[1] / (curve_map->ext_in[0] * range_dividers[i]);
}
else {
start_slopes[i] = 1e8f;
}
if (curve_map->ext_out[0] != 0.0f) {
end_slopes[i] = curve_map->ext_out[1] / (curve_map->ext_out[0] * range_dividers[i]);
}
else {
end_slopes[i] = 1e8f;
}
}
}
bool BKE_curvemapping_is_map_identity(const CurveMapping *curve_mapping, int index)
{
if (!(curve_mapping->flag & CUMA_EXTEND_EXTRAPOLATE)) {
return false;
}
const CurveMap *curve_map = &curve_mapping->cm[index];
if (curve_map->maxtable - curve_map->mintable != 1.0f) {
return false;
}
if (curve_map->ext_in[0] != curve_map->ext_in[1]) {
return false;
}
if (curve_map->ext_out[0] != curve_map->ext_out[1]) {
return false;
}
if (curve_map->totpoint != 2) {
return false;
}
if (curve_map->curve[0].x != 0 || curve_map->curve[0].y != 0) {
return false;
}
if (curve_map->curve[1].x != 0 || curve_map->curve[1].y != 0) {
return false;
}
return true;
}
void BKE_curvemapping_init(CurveMapping *cumap)
{
int a;

View File

@ -36,7 +36,7 @@ int calculate_evaluated_size(const int points_num,
const KnotsMode knots_mode)
{
if (!check_valid_size_and_order(points_num, order, cyclic, knots_mode)) {
return 0;
return points_num;
}
return resolution * curve_segment_size(points_num, cyclic);
}
@ -232,8 +232,12 @@ void interpolate_to_evaluated(const BasisCache &basis_cache,
const GSpan src,
GMutableSpan dst)
{
BLI_assert(dst.size() == basis_cache.start_indices.size());
if (basis_cache.invalid) {
dst.copy_from(src);
return;
}
BLI_assert(dst.size() == basis_cache.start_indices.size());
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {

View File

@ -110,7 +110,7 @@ void calculate_normals_minimum(const Span<float3> tangents,
normals.first() = math::normalize(float3(first_tangent.y, -first_tangent.x, 0.0f));
}
/* Forward normal with minimum twist along the entire spline. */
/* Forward normal with minimum twist along the entire curve. */
for (const int i : IndexRange(1, normals.size() - 1)) {
normals[i] = calculate_next_normal(normals[i - 1], tangents[i - 1], tangents[i]);
}
@ -120,7 +120,7 @@ void calculate_normals_minimum(const Span<float3> tangents,
}
/* Compute how much the first normal deviates from the normal that has been forwarded along the
* entire cyclic spline. */
* entire cyclic curve. */
const float3 uncorrected_last_normal = calculate_next_normal(
normals.last(), tangents.last(), tangents.first());
float correction_angle = angle_signed_on_axis_v3v3_v3(

View File

@ -668,12 +668,12 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
Vector<std::byte> eval_buffer;
Curves main_id = {nullptr};
Curves main_id = {{nullptr}};
main_id.geometry = reinterpret_cast<const ::CurvesGeometry &>(main);
CurveComponent main_component;
main_component.replace(&main_id, GeometryOwnershipType::Editable);
Curves profile_id = {nullptr};
Curves profile_id = {{nullptr}};
profile_id.geometry = reinterpret_cast<const ::CurvesGeometry &>(profile);
CurveComponent profile_component;
profile_component.replace(&profile_id, GeometryOwnershipType::Editable);

View File

@ -65,6 +65,8 @@ CurvesGeometry::CurvesGeometry(const int point_size, const int curve_size)
this->update_customdata_pointers();
this->runtime = MEM_new<CurvesGeometryRuntime>(__func__);
/* Fill the type counts with the default so they're in a valid state. */
this->runtime->type_counts[CURVE_TYPE_CATMULL_ROM] = curve_size;
}
/**
@ -250,6 +252,10 @@ void CurvesGeometry::fill_curve_types(const CurveType type)
void CurvesGeometry::fill_curve_types(const IndexMask selection, const CurveType type)
{
if (selection.size() == this->curves_num()) {
this->fill_curve_types(type);
return;
}
/* A potential performance optimization is only counting the changed indices. */
this->curve_types_for_write().fill_indices(selection, type);
this->update_curve_types();
@ -527,19 +533,23 @@ Span<int> CurvesGeometry::evaluated_offsets() const
IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type,
Vector<int64_t> &r_indices) const
{
return this->indices_for_curve_type(type, this->curves_range(), r_indices);
}
VArray<int8_t> types = this->curve_types();
IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type,
const IndexMask selection,
Vector<int64_t> &r_indices) const
{
if (this->curve_type_counts()[type] == this->curves_num()) {
return selection;
}
const VArray<int8_t> types = this->curve_types();
if (types.is_single()) {
if (types.get_internal_single() == type) {
return IndexMask(types.size());
}
return {};
return types.get_internal_single() == type ? IndexMask(this->curves_num()) : IndexMask(0);
}
Span<int8_t> types_span = types.get_internal_span();
return index_mask_ops::find_indices_based_on_predicate(
IndexMask(types.size()), 1024, r_indices, [&](const int index) {
return types_span[index] == type;
});
selection, 1024, r_indices, [&](const int index) { return types_span[index] == type; });
}
void CurvesGeometry::ensure_nurbs_basis_cache() const
@ -577,6 +587,11 @@ void CurvesGeometry::ensure_nurbs_basis_cache() const
const bool is_cyclic = cyclic[curve_index];
const KnotsMode mode = KnotsMode(knots_modes[curve_index]);
if (!curves::nurbs::check_valid_size_and_order(points.size(), order, is_cyclic, mode)) {
basis_caches[curve_index].invalid = true;
continue;
}
const int knots_size = curves::nurbs::knots_size(points.size(), order, is_cyclic);
Array<float> knots(knots_size);
curves::nurbs::calculate_knots(points.size(), mode, order, is_cyclic, knots);
@ -696,9 +711,6 @@ Span<float3> CurvesGeometry::evaluated_tangents() const
threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) {
for (const int curve_index : curves_range) {
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
if (UNLIKELY(evaluated_points.is_empty())) {
continue;
}
curves::poly::calculate_tangents(evaluated_positions.slice(evaluated_points),
cyclic[curve_index],
tangents.slice(evaluated_points));
@ -773,9 +785,6 @@ Span<float3> CurvesGeometry::evaluated_normals() const
for (const int curve_index : curves_range) {
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
if (UNLIKELY(evaluated_points.is_empty())) {
continue;
}
switch (normal_mode[curve_index]) {
case NORMAL_MODE_Z_UP:
curves::poly::calculate_normals_z_up(evaluated_tangents.slice(evaluated_points),
@ -916,9 +925,6 @@ void CurvesGeometry::ensure_evaluated_lengths() const
for (const int curve_index : curves_range) {
const bool cyclic = curves_cyclic[curve_index];
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
if (UNLIKELY(evaluated_points.is_empty())) {
continue;
}
const IndexRange lengths_range = this->lengths_range_for_curve(curve_index, cyclic);
length_parameterize::accumulate_lengths(evaluated_positions.slice(evaluated_points),
cyclic,
@ -1196,6 +1202,8 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves,
}
});
new_curves.update_curve_types();
return new_curves;
}

View File

@ -342,7 +342,7 @@ void BKE_editmesh_loop_tangent_calc(BMEditMesh *em,
/* Calculation */
if (em->tottri != 0) {
TaskPool *task_pool;
task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW);
task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH);
tangent_mask_curr = 0;
/* Calculate tangent layers */

View File

@ -237,6 +237,69 @@ VArray<float3> curve_normals_varray(const CurveComponent &component, const Attri
return nullptr;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Curve Length Field Input
* \{ */
static VArray<float> construct_curve_length_gvarray(const CurveComponent &component,
const AttributeDomain domain)
{
if (!component.has_curves()) {
return {};
}
const Curves &curves_id = *component.get_for_read();
const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
curves.ensure_evaluated_lengths();
VArray<bool> cyclic = curves.cyclic();
VArray<float> lengths = VArray<float>::ForFunc(
curves.curves_num(), [&curves, cyclic = std::move(cyclic)](int64_t index) {
return curves.evaluated_length_total_for_curve(index, cyclic[index]);
});
if (domain == ATTR_DOMAIN_CURVE) {
return lengths;
}
if (domain == ATTR_DOMAIN_POINT) {
return component.attribute_try_adapt_domain<float>(
std::move(lengths), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
}
return {};
}
CurveLengthFieldInput::CurveLengthFieldInput()
: GeometryFieldInput(CPPType::get<float>(), "Spline Length node")
{
category_ = Category::Generated;
}
GVArray CurveLengthFieldInput::get_varray_for_context(const GeometryComponent &component,
const AttributeDomain domain,
IndexMask UNUSED(mask)) const
{
if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
return construct_curve_length_gvarray(curve_component, domain);
}
return {};
}
uint64_t CurveLengthFieldInput::hash() const
{
/* Some random constant hash. */
return 3549623580;
}
bool CurveLengthFieldInput::is_equal_to(const fn::FieldNode &other) const
{
return dynamic_cast<const CurveLengthFieldInput *>(&other) != nullptr;
}
} // namespace blender::bke
/** \} */

View File

@ -2742,35 +2742,75 @@ void BKE_gpencil_update_layer_transforms(const Depsgraph *depsgraph, Object *ob)
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
bool changed = false;
unit_m4(cur_mat);
if (gpl->actframe != NULL) {
if (gpl->parent != NULL) {
Object *ob_parent = DEG_get_evaluated_object(depsgraph, gpl->parent);
/* calculate new matrix */
if (ELEM(gpl->partype, PAROBJECT, PARSKEL)) {
mul_m4_m4m4(cur_mat, ob->imat, ob_parent->obmat);
/* Skip non-visible layers. */
if (gpl->flag & GP_LAYER_HIDE || is_zero_v3(gpl->scale)) {
continue;
}
/* Skip empty layers. */
if (BLI_listbase_is_empty(&gpl->frames)) {
continue;
}
/* Determine frame range to transform. */
bGPDframe *gpf_start = NULL;
bGPDframe *gpf_end = NULL;
/* If onion skinning is activated, consider all frames. */
if (gpl->onion_flag & GP_LAYER_ONIONSKIN) {
gpf_start = gpl->frames.first;
}
/* Otherwise, consider only active frame. */
else {
/* Skip layer if it has no active frame to transform. */
if (gpl->actframe == NULL) {
continue;
}
gpf_start = gpl->actframe;
gpf_end = gpl->actframe->next;
}
if (gpl->parent != NULL) {
Object *ob_parent = DEG_get_evaluated_object(depsgraph, gpl->parent);
/* calculate new matrix */
if (ELEM(gpl->partype, PAROBJECT, PARSKEL)) {
mul_m4_m4m4(cur_mat, ob->imat, ob_parent->obmat);
}
else if (gpl->partype == PARBONE) {
bPoseChannel *pchan = BKE_pose_channel_find_name(ob_parent->pose, gpl->parsubstr);
if (pchan != NULL) {
mul_m4_series(cur_mat, ob->imat, ob_parent->obmat, pchan->pose_mat);
}
else if (gpl->partype == PARBONE) {
bPoseChannel *pchan = BKE_pose_channel_find_name(ob_parent->pose, gpl->parsubstr);
if (pchan != NULL) {
mul_m4_series(cur_mat, ob->imat, ob_parent->obmat, pchan->pose_mat);
}
else {
unit_m4(cur_mat);
}
else {
unit_m4(cur_mat);
}
changed = !equals_m4m4(gpl->inverse, cur_mat);
}
changed = !equals_m4m4(gpl->inverse, cur_mat);
}
/* Calc local layer transform. */
bool transformed = ((!is_zero_v3(gpl->location)) || (!is_zero_v3(gpl->rotation)) ||
(!is_one_v3(gpl->scale)));
if (transformed) {
loc_eul_size_to_mat4(gpl->layer_mat, gpl->location, gpl->rotation, gpl->scale);
}
/* Continue if no transformations are applied to this layer. */
if (!changed && !transformed) {
continue;
}
/* Iterate over frame range. */
for (bGPDframe *gpf = gpf_start; gpf != NULL && gpf != gpf_end; gpf = gpf->next) {
/* Skip frames without a valid onion skinning id (note: active frame has one). */
if (gpf->runtime.onion_id == INT_MAX) {
continue;
}
/* Calc local layer transform. */
bool transformed = ((!is_zero_v3(gpl->location)) || (!is_zero_v3(gpl->rotation)) ||
(!is_one_v3(gpl->scale)));
if (transformed) {
loc_eul_size_to_mat4(gpl->layer_mat, gpl->location, gpl->rotation, gpl->scale);
}
/* only redo if any change */
/* Apply transformations only if needed. */
if (changed || transformed) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpl->actframe->strokes) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
bGPDspoint *pt;
int i;
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {

View File

@ -1123,15 +1123,16 @@ void BKE_object_material_remap_calc(Object *ob_dst, Object *ob_src, short *remap
BLI_ghash_free(gh_mat_map, NULL, NULL);
}
void BKE_object_material_from_eval_data(Main *bmain, Object *ob_orig, ID *data_eval)
void BKE_object_material_from_eval_data(Main *bmain, Object *ob_orig, const ID *data_eval)
{
ID *data_orig = ob_orig->data;
short *orig_totcol = BKE_id_material_len_p(data_orig);
Material ***orig_mat = BKE_id_material_array_p(data_orig);
short *eval_totcol = BKE_id_material_len_p(data_eval);
Material ***eval_mat = BKE_id_material_array_p(data_eval);
/* Can cast away const, because the data is not changed. */
const short *eval_totcol = BKE_id_material_len_p((ID *)data_eval);
Material ***eval_mat = BKE_id_material_array_p((ID *)data_eval);
if (ELEM(NULL, orig_totcol, orig_mat, eval_totcol, eval_mat)) {
return;

View File

@ -407,17 +407,11 @@ static void copy_poly_attributes(Mesh *dest_mesh,
int index_in_orig_me,
Span<short> material_remap)
{
mp->mat_nr = orig_mp->mat_nr;
if (mp->mat_nr >= dest_mesh->totcol) {
mp->mat_nr = 0;
if (material_remap.size() > 0 && material_remap.index_range().contains(orig_mp->mat_nr)) {
mp->mat_nr = material_remap[orig_mp->mat_nr];
}
else {
if (material_remap.size() > 0) {
short mat_nr = material_remap[orig_mp->mat_nr];
if (mat_nr >= 0 && mat_nr < dest_mesh->totcol) {
mp->mat_nr = mat_nr;
}
}
mp->mat_nr = orig_mp->mat_nr;
}
mp->flag = orig_mp->flag;

View File

@ -0,0 +1,137 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include "MEM_guardedalloc.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BLI_math.h"
#include "BLI_task.hh"
#include "BLI_utildefines.h"
#include "BKE_customdata.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BLI_memarena.h"
#include "BLI_strict_flags.h"
using namespace blender;
enum {
CMP_CLOSE = 0,
CMP_EQUAL = 1,
CMP_APART = 2,
};
static int compare_v2_classify(const float uv_a[2], const float uv_b[2])
{
if (uv_a[0] == uv_b[0] && uv_a[1] == uv_b[1]) {
return CMP_EQUAL;
}
/* Note that the ULP value is the primary value used to compare relative values
* as the absolute value doesn't account for float precision at difference scales.
* - For subdivision-surface ULP of 3 is sufficient,
* although this value is extremely small.
* - For bevel the URL of 12 is sufficient to merge UV's that appear to be connected
* with bevel on Suzanne beveled 15% with 6 segments.
*
* These values could be tweaked but should be kept on the small side to prevent
* unintentional joining of intentionally dis-connected UV's.
*
* Before v2.91 the threshold was either (`1e-4` or `0.05 / image_size` for selection picking).
* So picking used a threshold of `1e-4` for a 500x500 image and `1e-5` for a 5000x5000 image.
* Given this value worked reasonably well for a long time, the absolute difference should
* never exceed `1e-4` (#STD_UV_CONNECT_LIMIT which is still used in a few areas). */
const float diff_abs = 1e-12f;
const int diff_ulp = 12;
if (compare_ff_relative(uv_a[0], uv_b[0], diff_abs, diff_ulp) &&
compare_ff_relative(uv_a[1], uv_b[1], diff_abs, diff_ulp)) {
return CMP_CLOSE;
}
return CMP_APART;
}
static void merge_uvs_for_vertex(const Span<int> loops_for_vert, Span<MLoopUV *> mloopuv_layers)
{
if (loops_for_vert.size() <= 1) {
return;
}
/* Manipulate a copy of the loop indices, de-duplicating UV's per layer. */
Vector<int, 32> loops_merge;
loops_merge.reserve(loops_for_vert.size());
for (MLoopUV *mloopuv : mloopuv_layers) {
BLI_assert(loops_merge.is_empty());
loops_merge.extend_unchecked(loops_for_vert);
while (loops_merge.size() > 1) {
uint i_last = (uint)loops_merge.size() - 1;
const float *uv_src = mloopuv[loops_merge[0]].uv;
for (uint i = 1; i <= i_last;) {
float *uv_dst = mloopuv[loops_merge[i]].uv;
switch (compare_v2_classify(uv_src, uv_dst)) {
case CMP_CLOSE: {
uv_dst[0] = uv_src[0];
uv_dst[1] = uv_src[1];
ATTR_FALLTHROUGH;
}
case CMP_EQUAL: {
loops_merge[i] = loops_merge[i_last--];
break;
}
case CMP_APART: {
/* Doesn't match, check the next UV. */
i++;
break;
}
default: {
BLI_assert_unreachable();
}
}
}
/* Finished de-duplicating with the first index, throw it away. */
loops_merge[0] = loops_merge[i_last];
loops_merge.resize(i_last);
}
loops_merge.clear();
}
}
void BKE_mesh_merge_customdata_for_apply_modifier(Mesh *me)
{
if (me->totloop == 0) {
return;
}
const int mloopuv_layers_num = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV);
if (mloopuv_layers_num == 0) {
return;
}
int *vert_map_mem;
struct MeshElemMap *vert_to_loop;
BKE_mesh_vert_loop_map_create(
&vert_to_loop, &vert_map_mem, me->mpoly, me->mloop, me->totvert, me->totpoly, me->totloop);
Vector<MLoopUV *> mloopuv_layers;
mloopuv_layers.reserve(mloopuv_layers_num);
for (int a = 0; a < mloopuv_layers_num; a++) {
MLoopUV *mloopuv = static_cast<MLoopUV *>(CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, a));
mloopuv_layers.append_unchecked(mloopuv);
}
Span<MLoopUV *> mloopuv_layers_as_span = mloopuv_layers.as_span();
threading::parallel_for(IndexRange(me->totvert), 1024, [&](IndexRange range) {
for (const int64_t v_index : range) {
MeshElemMap &loops_for_vert = vert_to_loop[v_index];
Span<int> loops_for_vert_span(loops_for_vert.indices, loops_for_vert.count);
merge_uvs_for_vertex(loops_for_vert_span, mloopuv_layers_as_span);
}
});
MEM_freeN(vert_to_loop);
MEM_freeN(vert_map_mem);
}

View File

@ -455,7 +455,10 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
if (flip_map) {
for (i = 0; i < maxVerts; dvert++, i++) {
/* merged vertices get both groups, others get flipped */
if (do_vtargetmap && (vtargetmap[i] != -1)) {
if (use_correct_order_on_merge && do_vtargetmap && (vtargetmap[i + maxVerts] != -1)) {
BKE_defvert_flip_merged(dvert - maxVerts, flip_map, flip_map_len);
}
else if (!use_correct_order_on_merge && do_vtargetmap && (vtargetmap[i] != -1)) {
BKE_defvert_flip_merged(dvert, flip_map, flip_map_len);
}
else {

View File

@ -620,7 +620,7 @@ void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert,
/* Calculation */
if (looptri_len != 0) {
TaskPool *task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW);
TaskPool *task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH);
tangent_mask_curr = 0;
/* Calculate tangent layers */

View File

@ -4502,6 +4502,8 @@ static void registerCompositNodes()
register_node_type_cmp_premulkey();
register_node_type_cmp_separate_xyz();
register_node_type_cmp_combine_xyz();
register_node_type_cmp_separate_color();
register_node_type_cmp_combine_color();
register_node_type_cmp_diff_matte();
register_node_type_cmp_distance_matte();
@ -4574,6 +4576,8 @@ static void registerShaderNodes()
register_node_type_sh_vect_transform();
register_node_type_sh_squeeze();
register_node_type_sh_invert();
register_node_type_sh_sepcolor();
register_node_type_sh_combcolor();
register_node_type_sh_seprgb();
register_node_type_sh_combrgb();
register_node_type_sh_sephsv();
@ -4660,6 +4664,8 @@ static void registerTextureNodes()
register_node_type_tex_distance();
register_node_type_tex_compose();
register_node_type_tex_decompose();
register_node_type_tex_combine_color();
register_node_type_tex_separate_color();
register_node_type_tex_output();
register_node_type_tex_viewer();
@ -4821,6 +4827,7 @@ static void registerFunctionNodes()
{
register_node_type_fn_align_euler_to_vector();
register_node_type_fn_boolean_math();
register_node_type_fn_combine_color();
register_node_type_fn_compare();
register_node_type_fn_float_to_int();
register_node_type_fn_input_bool();
@ -4832,6 +4839,7 @@ static void registerFunctionNodes()
register_node_type_fn_random_value();
register_node_type_fn_replace_string();
register_node_type_fn_rotate_euler();
register_node_type_fn_separate_color();
register_node_type_fn_slice_string();
register_node_type_fn_string_length();
register_node_type_fn_value_to_string();

View File

@ -2114,7 +2114,7 @@ static const char *get_obdata_defname(int type)
case OB_SPEAKER:
return DATA_("Speaker");
case OB_CURVES:
return DATA_("HairCurves");
return DATA_("Curves");
case OB_POINTCLOUD:
return DATA_("PointCloud");
case OB_VOLUME:

View File

@ -3194,7 +3194,7 @@ void psys_cache_child_paths(ParticleSimulationData *sim,
return;
}
task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW);
task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_HIGH);
totchild = ctx.totchild;
totparent = ctx.totparent;

View File

@ -1313,7 +1313,7 @@ static void distribute_particles_on_dm(ParticleSimulationData *sim, int from)
return;
}
task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW);
task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_HIGH);
totpart = (from == PART_FROM_CHILD ? sim->psys->totchild : sim->psys->totpart);
psys_tasks_create(&ctx, 0, totpart, &tasks, &numtasks);

View File

@ -833,8 +833,8 @@ static void ptcache_rigidbody_interpolate(int index,
memcpy(orn, data + 3, sizeof(float[4]));
}
else {
PTCACHE_DATA_TO(data, BPHYS_DATA_LOCATION, index, pos);
PTCACHE_DATA_TO(data, BPHYS_DATA_ROTATION, index, orn);
PTCACHE_DATA_TO(data, BPHYS_DATA_LOCATION, 0, pos);
PTCACHE_DATA_TO(data, BPHYS_DATA_ROTATION, 0, orn);
}
const float t = (cfra - cfra1) / (cfra2 - cfra1);

View File

@ -274,7 +274,7 @@ static void scene_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
scene_dst->nodetree,
(void *)(&scene_src->id),
&scene_dst->id,
ID_REMAP_SKIP_NEVER_NULL_USAGE);
ID_REMAP_SKIP_NEVER_NULL_USAGE | ID_REMAP_SKIP_USER_CLEAR);
}
if (scene_src->rigidbody_world) {

View File

@ -295,16 +295,16 @@ static void init_functions(OpenSubdiv_Converter *converter)
static void initialize_manifold_index_array(const BLI_bitmap *used_map,
const int num_elements,
int **indices_r,
int **indices_reverse_r,
int *num_manifold_elements_r)
int **r_indices,
int **r_indices_reverse,
int *r_num_manifold_elements)
{
int *indices = NULL;
if (indices_r != NULL) {
if (r_indices != NULL) {
indices = MEM_malloc_arrayN(num_elements, sizeof(int), "manifold indices");
}
int *indices_reverse = NULL;
if (indices_reverse_r != NULL) {
if (r_indices_reverse != NULL) {
indices_reverse = MEM_malloc_arrayN(num_elements, sizeof(int), "manifold indices reverse");
}
int offset = 0;
@ -324,13 +324,13 @@ static void initialize_manifold_index_array(const BLI_bitmap *used_map,
offset++;
}
}
if (indices_r != NULL) {
*indices_r = indices;
if (r_indices != NULL) {
*r_indices = indices;
}
if (indices_reverse_r != NULL) {
*indices_reverse_r = indices_reverse;
if (r_indices_reverse != NULL) {
*r_indices_reverse = indices_reverse;
}
*num_manifold_elements_r = num_elements - offset;
*r_num_manifold_elements = num_elements - offset;
}
static void initialize_manifold_indices(ConverterStorage *storage)

View File

@ -21,12 +21,12 @@
typedef double Vec2[2];
static int point_markers_correspondences_on_both_image(
MovieTrackingPlaneTrack *plane_track, int frame1, int frame2, Vec2 **x1_r, Vec2 **x2_r)
MovieTrackingPlaneTrack *plane_track, int frame1, int frame2, Vec2 **r_x1, Vec2 **r_x2)
{
Vec2 *x1, *x2;
*x1_r = x1 = MEM_mallocN(sizeof(*x1) * plane_track->point_tracksnr, "point correspondences x1");
*x2_r = x2 = MEM_mallocN(sizeof(*x1) * plane_track->point_tracksnr, "point correspondences x2");
*r_x1 = x1 = MEM_mallocN(sizeof(*x1) * plane_track->point_tracksnr, "point correspondences x1");
*r_x2 = x2 = MEM_mallocN(sizeof(*x1) * plane_track->point_tracksnr, "point correspondences x2");
int correspondence_index = 0;
for (int i = 0; i < plane_track->point_tracksnr; i++) {

View File

@ -0,0 +1,192 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <cmath>
#include <cstdint>
#include "BLI_assert.h"
#include "BLI_math_base.h"
#include "BLI_math_matrix.h"
#include "BLI_math_vec_types.hh"
#include "BLI_math_vector.h"
namespace blender {
struct float3x3 {
/* A 3x3 matrix in column major order. */
float values[3][3];
float3x3() = default;
float3x3(const float *matrix)
{
memcpy(values, matrix, sizeof(float) * 3 * 3);
}
float3x3(const float matrix[3][3]) : float3x3(static_cast<const float *>(matrix[0]))
{
}
static float3x3 zero()
{
float3x3 result;
zero_m3(result.values);
return result;
}
static float3x3 identity()
{
float3x3 result;
unit_m3(result.values);
return result;
}
static float3x3 from_translation(const float2 translation)
{
float3x3 result = identity();
result.values[2][0] = translation.x;
result.values[2][1] = translation.y;
return result;
}
static float3x3 from_rotation(float rotation)
{
float3x3 result = zero();
const float cosine = std::cos(rotation);
const float sine = std::sin(rotation);
result.values[0][0] = cosine;
result.values[0][1] = sine;
result.values[1][0] = -sine;
result.values[1][1] = cosine;
result.values[2][2] = 1.0f;
return result;
}
static float3x3 from_translation_rotation_scale(const float2 translation,
float rotation,
const float2 scale)
{
float3x3 result;
const float cosine = std::cos(rotation);
const float sine = std::sin(rotation);
result.values[0][0] = scale.x * cosine;
result.values[0][1] = scale.x * sine;
result.values[0][2] = 0.0f;
result.values[1][0] = scale.y * -sine;
result.values[1][1] = scale.y * cosine;
result.values[1][2] = 0.0f;
result.values[2][0] = translation.x;
result.values[2][1] = translation.y;
result.values[2][2] = 1.0f;
return result;
}
static float3x3 from_normalized_axes(const float2 translation,
const float2 horizontal,
const float2 vertical)
{
BLI_ASSERT_UNIT_V2(horizontal);
BLI_ASSERT_UNIT_V2(vertical);
float3x3 result;
result.values[0][0] = horizontal.x;
result.values[0][1] = horizontal.y;
result.values[0][2] = 0.0f;
result.values[1][0] = vertical.x;
result.values[1][1] = vertical.y;
result.values[1][2] = 0.0f;
result.values[2][0] = translation.x;
result.values[2][1] = translation.y;
result.values[2][2] = 1.0f;
return result;
}
/* Construct a transformation that is pivoted around the given origin point. So for instance,
* from_origin_transformation(from_rotation(M_PI_2), float2(0.0f, 2.0f))
* will construct a transformation representing a 90 degree rotation around the point (0, 2). */
static float3x3 from_origin_transformation(const float3x3 &transformation, const float2 origin)
{
return from_translation(origin) * transformation * from_translation(-origin);
}
operator float *()
{
return &values[0][0];
}
operator const float *() const
{
return &values[0][0];
}
float *operator[](const int64_t index)
{
BLI_assert(index >= 0);
BLI_assert(index < 3);
return &values[index][0];
}
const float *operator[](const int64_t index) const
{
BLI_assert(index >= 0);
BLI_assert(index < 3);
return &values[index][0];
}
using c_style_float3x3 = float[3][3];
c_style_float3x3 &ptr()
{
return values;
}
const c_style_float3x3 &ptr() const
{
return values;
}
friend float3x3 operator*(const float3x3 &a, const float3x3 &b)
{
float3x3 result;
mul_m3_m3m3(result.values, a.values, b.values);
return result;
}
void operator*=(const float3x3 &other)
{
mul_m3_m3_post(values, other.values);
}
friend float2 operator*(const float3x3 &transformation, const float2 &vector)
{
float2 result;
mul_v2_m3v2(result, transformation.values, vector);
return result;
}
friend float2 operator*(const float3x3 &transformation, const float (*vector)[2])
{
return transformation * float2(vector);
}
float3x3 transposed() const
{
float3x3 result;
transpose_m3_m3(result.values, values);
return result;
}
float3x3 inverted() const
{
float3x3 result;
invert_m3_m3(result.values, values);
return result;
}
friend bool operator==(const float3x3 &a, const float3x3 &b)
{
return equals_m3m3(a.values, b.values);
}
};
} // namespace blender

View File

@ -79,8 +79,8 @@ set(SRC
intern/kdtree_3d.c
intern/kdtree_4d.c
intern/lasso_2d.c
intern/listbase.c
intern/length_parameterize.cc
intern/listbase.c
intern/math_base.c
intern/math_base_inline.c
intern/math_base_safe_inline.c
@ -199,6 +199,7 @@ set(SRC
BLI_fileops.hh
BLI_fileops_types.h
BLI_filereader.h
BLI_float3x3.hh
BLI_float4x4.hh
BLI_fnmatch.h
BLI_function_ref.hh
@ -274,8 +275,8 @@ set(SRC
BLI_multi_value_map.hh
BLI_noise.h
BLI_noise.hh
BLI_path_util.h
BLI_parameter_pack_utils.hh
BLI_path_util.h
BLI_polyfill_2d.h
BLI_polyfill_2d_beautify.h
BLI_probing_strategies.hh
@ -431,6 +432,7 @@ if(WITH_GTESTS)
tests/BLI_edgehash_test.cc
tests/BLI_expr_pylike_eval_test.cc
tests/BLI_fileops_test.cc
tests/BLI_float3x3_test.cc
tests/BLI_function_ref_test.cc
tests/BLI_generic_array_test.cc
tests/BLI_generic_span_test.cc

View File

@ -238,7 +238,7 @@ void rgb_to_hsl(float r, float g, float b, float *r_h, float *r_s, float *r_l)
{
const float cmax = max_fff(r, g, b);
const float cmin = min_fff(r, g, b);
float h, s, l = min_ff(1.0, (cmax + cmin) / 2.0f);
float h, s, l = min_ff(1.0f, (cmax + cmin) / 2.0f);
if (cmax == cmin) {
h = s = 0.0f; /* achromatic */

View File

@ -0,0 +1,119 @@
/* SPDX-License-Identifier: Apache-2.0 */
#include "testing/testing.h"
#include "BLI_float3x3.hh"
#include "BLI_math_base.h"
#include "BLI_math_vec_types.hh"
namespace blender::tests {
TEST(float3x3, Identity)
{
float2 point(1.0f, 2.0f);
float3x3 transformation = float3x3::identity();
float2 result = transformation * point;
EXPECT_EQ(result, point);
}
TEST(float3x3, Translation)
{
float2 point(1.0f, 2.0f);
float3x3 transformation = float3x3::from_translation(float2(5.0f, 3.0f));
float2 result = transformation * point;
EXPECT_FLOAT_EQ(result[0], 6.0f);
EXPECT_FLOAT_EQ(result[1], 5.0f);
}
TEST(float3x3, Rotation)
{
float2 point(1.0f, 2.0f);
float3x3 transformation = float3x3::from_rotation(M_PI_2);
float2 result = transformation * point;
EXPECT_FLOAT_EQ(result[0], -2.0f);
EXPECT_FLOAT_EQ(result[1], 1.0f);
}
TEST(float3x3, TranslationRotationScale)
{
float2 point(1.0f, 2.0f);
float3x3 transformation = float3x3::from_translation_rotation_scale(
float2(1.0f, 3.0f), M_PI_2, float2(2.0f, 3.0f));
float2 result = transformation * point;
EXPECT_FLOAT_EQ(result[0], -5.0f);
EXPECT_FLOAT_EQ(result[1], 5.0f);
}
TEST(float3x3, NormalizedAxes)
{
float2 point(1.0f, 2.0f);
/* The horizontal is aligned with (1, 1) and vertical is aligned with (-1, 1), in other words, a
* Pi / 4 rotation. */
float value = std::sqrt(2.0f) / 2.0f;
float3x3 transformation = float3x3::from_normalized_axes(
float2(1.0f, 3.0f), float2(value), float2(-value, value));
float2 result = transformation * point;
float3x3 expected_transformation = float3x3::from_translation_rotation_scale(
float2(1.0f, 3.0f), M_PI_4, float2(1.0f));
float2 expected = expected_transformation * point;
EXPECT_FLOAT_EQ(result[0], expected[0]);
EXPECT_FLOAT_EQ(result[1], expected[1]);
}
TEST(float3x3, PostTransformationMultiplication)
{
float2 point(1.0f, 2.0f);
float3x3 translation = float3x3::from_translation(float2(5.0f, 3.0f));
float3x3 rotation = float3x3::from_rotation(M_PI_2);
float3x3 transformation = translation * rotation;
float2 result = transformation * point;
EXPECT_FLOAT_EQ(result[0], 3.0f);
EXPECT_FLOAT_EQ(result[1], 4.0f);
}
TEST(float3x3, PreTransformationMultiplication)
{
float2 point(1.0f, 2.0f);
float3x3 translation = float3x3::from_translation(float2(5.0f, 3.0f));
float3x3 rotation = float3x3::from_rotation(M_PI_2);
float3x3 transformation = rotation * translation;
float2 result = transformation * point;
EXPECT_FLOAT_EQ(result[0], -5.0f);
EXPECT_FLOAT_EQ(result[1], 6.0f);
}
TEST(float3x3, TransformationMultiplicationAssignment)
{
float2 point(1.0f, 2.0f);
float3x3 transformation = float3x3::from_translation(float2(5.0f, 3.0f));
transformation *= float3x3::from_rotation(M_PI_2);
float2 result = transformation * point;
EXPECT_FLOAT_EQ(result[0], 3.0f);
EXPECT_FLOAT_EQ(result[1], 4.0f);
}
TEST(float3x3, Inverted)
{
float2 point(1.0f, 2.0f);
float3x3 transformation = float3x3::from_translation_rotation_scale(
float2(1.0f, 3.0f), M_PI_4, float2(1.0f));
transformation *= transformation.inverted();
float2 result = transformation * point;
EXPECT_FLOAT_EQ(result[0], 1.0f);
EXPECT_FLOAT_EQ(result[1], 2.0f);
}
TEST(float3x3, Origin)
{
float2 point(1.0f, 2.0f);
float3x3 rotation = float3x3::from_rotation(M_PI_2);
float3x3 transformation = float3x3::from_origin_transformation(rotation, float2(0.0f, 2.0f));
float2 result = transformation * point;
EXPECT_FLOAT_EQ(result[0], 0.0f);
EXPECT_FLOAT_EQ(result[1], 3.0f);
}
} // namespace blender::tests

View File

@ -2068,7 +2068,7 @@ static void direct_link_id_embedded_id(BlendDataReader *reader,
static int direct_link_id_restore_recalc_exceptions(const ID *id_current)
{
/* Exception for armature objects, where the pose has direct points to the
* armature databolock. */
* armature data-block. */
if (GS(id_current->name) == ID_OB && ((Object *)id_current)->pose) {
return ID_RECALC_GEOMETRY;
}

View File

@ -2735,6 +2735,13 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
FOREACH_NODETREE_END;
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type == NTREE_GEOMETRY) {
version_node_input_socket_name(
ntree, GEO_NODE_SUBDIVISION_SURFACE, "Crease", "Edge Crease");
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 302, 13)) {
@ -2772,5 +2779,260 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
*/
{
/* Keep this block, even when empty. */
/* Replace legacy combine/separate color nodes */
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
/* In geometry nodes, replace shader combine/separate color nodes with function nodes */
if (ntree->type == NTREE_GEOMETRY) {
version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "R", "Red");
version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "G", "Green");
version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "B", "Blue");
version_node_output_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "Image", "Color");
version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "R", "Red");
version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "G", "Green");
version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "B", "Blue");
version_node_input_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "Image", "Color");
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
switch (node->type) {
case SH_NODE_COMBRGB_LEGACY: {
node->type = FN_NODE_COMBINE_COLOR;
NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor),
__func__);
storage->mode = NODE_COMBSEP_COLOR_RGB;
strcpy(node->idname, "FunctionNodeCombineColor");
node->storage = storage;
break;
}
case SH_NODE_SEPRGB_LEGACY: {
node->type = FN_NODE_SEPARATE_COLOR;
NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor),
__func__);
storage->mode = NODE_COMBSEP_COLOR_RGB;
strcpy(node->idname, "FunctionNodeSeparateColor");
node->storage = storage;
break;
}
}
}
}
/* In compositing nodes, replace combine/separate RGBA/HSVA/YCbCrA/YCCA nodes with
* combine/separate color */
if (ntree->type == NTREE_COMPOSIT) {
version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "R", "Red");
version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "G", "Green");
version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "B", "Blue");
version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "A", "Alpha");
version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "H", "Red");
version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "S", "Green");
version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "V", "Blue");
version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "A", "Alpha");
version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "Y", "Red");
version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "Cb", "Green");
version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "Cr", "Blue");
version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "A", "Alpha");
version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "Y", "Red");
version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "U", "Green");
version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "V", "Blue");
version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "A", "Alpha");
version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "R", "Red");
version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "G", "Green");
version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "B", "Blue");
version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "A", "Alpha");
version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "H", "Red");
version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "S", "Green");
version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "V", "Blue");
version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "A", "Alpha");
version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "Y", "Red");
version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "Cb", "Green");
version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "Cr", "Blue");
version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "A", "Alpha");
version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "Y", "Red");
version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "U", "Green");
version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "V", "Blue");
version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "A", "Alpha");
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
switch (node->type) {
case CMP_NODE_COMBRGBA_LEGACY: {
node->type = CMP_NODE_COMBINE_COLOR;
NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN(
sizeof(NodeCMPCombSepColor), __func__);
storage->mode = CMP_NODE_COMBSEP_COLOR_RGB;
strcpy(node->idname, "CompositorNodeCombineColor");
node->storage = storage;
break;
}
case CMP_NODE_COMBHSVA_LEGACY: {
node->type = CMP_NODE_COMBINE_COLOR;
NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN(
sizeof(NodeCMPCombSepColor), __func__);
storage->mode = CMP_NODE_COMBSEP_COLOR_HSV;
strcpy(node->idname, "CompositorNodeCombineColor");
node->storage = storage;
break;
}
case CMP_NODE_COMBYCCA_LEGACY: {
node->type = CMP_NODE_COMBINE_COLOR;
NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN(
sizeof(NodeCMPCombSepColor), __func__);
storage->mode = CMP_NODE_COMBSEP_COLOR_YCC;
storage->ycc_mode = node->custom1;
strcpy(node->idname, "CompositorNodeCombineColor");
node->storage = storage;
break;
}
case CMP_NODE_COMBYUVA_LEGACY: {
node->type = CMP_NODE_COMBINE_COLOR;
NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN(
sizeof(NodeCMPCombSepColor), __func__);
storage->mode = CMP_NODE_COMBSEP_COLOR_YUV;
strcpy(node->idname, "CompositorNodeCombineColor");
node->storage = storage;
break;
}
case CMP_NODE_SEPRGBA_LEGACY: {
node->type = CMP_NODE_SEPARATE_COLOR;
NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN(
sizeof(NodeCMPCombSepColor), __func__);
storage->mode = CMP_NODE_COMBSEP_COLOR_RGB;
strcpy(node->idname, "CompositorNodeSeparateColor");
node->storage = storage;
break;
}
case CMP_NODE_SEPHSVA_LEGACY: {
node->type = CMP_NODE_SEPARATE_COLOR;
NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN(
sizeof(NodeCMPCombSepColor), __func__);
storage->mode = CMP_NODE_COMBSEP_COLOR_HSV;
strcpy(node->idname, "CompositorNodeSeparateColor");
node->storage = storage;
break;
}
case CMP_NODE_SEPYCCA_LEGACY: {
node->type = CMP_NODE_SEPARATE_COLOR;
NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN(
sizeof(NodeCMPCombSepColor), __func__);
storage->mode = CMP_NODE_COMBSEP_COLOR_YCC;
storage->ycc_mode = node->custom1;
strcpy(node->idname, "CompositorNodeSeparateColor");
node->storage = storage;
break;
}
case CMP_NODE_SEPYUVA_LEGACY: {
node->type = CMP_NODE_SEPARATE_COLOR;
NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN(
sizeof(NodeCMPCombSepColor), __func__);
storage->mode = CMP_NODE_COMBSEP_COLOR_YUV;
strcpy(node->idname, "CompositorNodeSeparateColor");
node->storage = storage;
break;
}
}
}
}
/* In texture nodes, replace combine/separate RGBA with combine/separate color */
if (ntree->type == NTREE_TEXTURE) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
switch (node->type) {
case TEX_NODE_COMPOSE_LEGACY: {
node->type = TEX_NODE_COMBINE_COLOR;
node->custom1 = NODE_COMBSEP_COLOR_RGB;
strcpy(node->idname, "TextureNodeCombineColor");
break;
}
case TEX_NODE_DECOMPOSE_LEGACY: {
node->type = TEX_NODE_SEPARATE_COLOR;
node->custom1 = NODE_COMBSEP_COLOR_RGB;
strcpy(node->idname, "TextureNodeSeparateColor");
break;
}
}
}
}
/* In shader nodes, replace combine/separate RGB/HSV with combine/separate color */
if (ntree->type == NTREE_SHADER) {
version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "R", "Red");
version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "G", "Green");
version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "B", "Blue");
version_node_output_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "Image", "Color");
version_node_input_socket_name(ntree, SH_NODE_COMBHSV_LEGACY, "H", "Red");
version_node_input_socket_name(ntree, SH_NODE_COMBHSV_LEGACY, "S", "Green");
version_node_input_socket_name(ntree, SH_NODE_COMBHSV_LEGACY, "V", "Blue");
version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "R", "Red");
version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "G", "Green");
version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "B", "Blue");
version_node_input_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "Image", "Color");
version_node_output_socket_name(ntree, SH_NODE_SEPHSV_LEGACY, "H", "Red");
version_node_output_socket_name(ntree, SH_NODE_SEPHSV_LEGACY, "S", "Green");
version_node_output_socket_name(ntree, SH_NODE_SEPHSV_LEGACY, "V", "Blue");
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
switch (node->type) {
case SH_NODE_COMBRGB_LEGACY: {
node->type = SH_NODE_COMBINE_COLOR;
NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor),
__func__);
storage->mode = NODE_COMBSEP_COLOR_RGB;
strcpy(node->idname, "ShaderNodeCombineColor");
node->storage = storage;
break;
}
case SH_NODE_COMBHSV_LEGACY: {
node->type = SH_NODE_COMBINE_COLOR;
NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor),
__func__);
storage->mode = NODE_COMBSEP_COLOR_HSV;
strcpy(node->idname, "ShaderNodeCombineColor");
node->storage = storage;
break;
}
case SH_NODE_SEPRGB_LEGACY: {
node->type = SH_NODE_SEPARATE_COLOR;
NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor),
__func__);
storage->mode = NODE_COMBSEP_COLOR_RGB;
strcpy(node->idname, "ShaderNodeSeparateColor");
node->storage = storage;
break;
}
case SH_NODE_SEPHSV_LEGACY: {
node->type = SH_NODE_SEPARATE_COLOR;
NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor),
__func__);
storage->mode = NODE_COMBSEP_COLOR_HSV;
strcpy(node->idname, "ShaderNodeSeparateColor");
node->storage = storage;
break;
}
}
}
}
}
FOREACH_NODETREE_END;
/* Initialize brush curves sculpt settings. */
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
if (brush->ob_mode != OB_MODE_SCULPT_CURVES) {
continue;
}
if (brush->curves_sculpt_settings->points_per_curve == 0) {
brush->curves_sculpt_settings->points_per_curve = 8;
}
}
}
}

View File

@ -265,8 +265,20 @@ typedef struct BMFace {
* (the length of #BMFace.l_first circular linked list).
*/
int len;
float no[3]; /* face normal */
short mat_nr; /* material index */
/**
* Face normal, see #BM_face_calc_normal.
*/
float no[3];
/**
* Material index, typically >= 0 and < #Mesh.totcol although this isn't enforced
* Python for e.g. can set this to any positive value since scripts may create
* mesh data first and setup material slots later.
*
* When using to index into a material array it's range should be checked first,
* values exceeding the range should be ignored or treated as zero
* (if a material slot needs to be used - when drawing for e.g.)
*/
short mat_nr;
// short _pad[3];
} BMFace;

View File

@ -259,12 +259,16 @@ set(SRC
# converter nodes
nodes/COM_CombineColorNode.cc
nodes/COM_CombineColorNode.h
nodes/COM_CombineColorNodeLegacy.cc
nodes/COM_CombineColorNodeLegacy.h
nodes/COM_CombineXYZNode.cc
nodes/COM_CombineXYZNode.h
nodes/COM_IDMaskNode.cc
nodes/COM_IDMaskNode.h
nodes/COM_SeparateColorNode.cc
nodes/COM_SeparateColorNode.h
nodes/COM_SeparateColorNodeLegacy.cc
nodes/COM_SeparateColorNodeLegacy.h
nodes/COM_SeparateXYZNode.cc
nodes/COM_SeparateXYZNode.h

View File

@ -29,6 +29,7 @@
#include "COM_ColorSpillNode.h"
#include "COM_ColorToBWNode.h"
#include "COM_CombineColorNode.h"
#include "COM_CombineColorNodeLegacy.h"
#include "COM_CombineXYZNode.h"
#include "COM_CompositorNode.h"
#include "COM_ConvertAlphaNode.h"
@ -82,6 +83,7 @@
#include "COM_ScaleOperation.h"
#include "COM_SceneTimeNode.h"
#include "COM_SeparateColorNode.h"
#include "COM_SeparateColorNodeLegacy.h"
#include "COM_SeparateXYZNode.h"
#include "COM_SetAlphaNode.h"
#include "COM_SetValueOperation.h"
@ -169,28 +171,34 @@ Node *COM_convert_bnode(bNode *b_node)
case CMP_NODE_BRIGHTCONTRAST:
node = new BrightnessNode(b_node);
break;
case CMP_NODE_SEPRGBA:
case CMP_NODE_SEPARATE_COLOR:
node = new SeparateColorNode(b_node);
break;
case CMP_NODE_COMBINE_COLOR:
node = new CombineColorNode(b_node);
break;
case CMP_NODE_SEPRGBA_LEGACY:
node = new SeparateRGBANode(b_node);
break;
case CMP_NODE_COMBRGBA:
case CMP_NODE_COMBRGBA_LEGACY:
node = new CombineRGBANode(b_node);
break;
case CMP_NODE_SEPHSVA:
case CMP_NODE_SEPHSVA_LEGACY:
node = new SeparateHSVANode(b_node);
break;
case CMP_NODE_COMBHSVA:
case CMP_NODE_COMBHSVA_LEGACY:
node = new CombineHSVANode(b_node);
break;
case CMP_NODE_SEPYUVA:
case CMP_NODE_SEPYUVA_LEGACY:
node = new SeparateYUVANode(b_node);
break;
case CMP_NODE_COMBYUVA:
case CMP_NODE_COMBYUVA_LEGACY:
node = new CombineYUVANode(b_node);
break;
case CMP_NODE_SEPYCCA:
case CMP_NODE_SEPYCCA_LEGACY:
node = new SeparateYCCANode(b_node);
break;
case CMP_NODE_COMBYCCA:
case CMP_NODE_COMBYCCA_LEGACY:
node = new CombineYCCANode(b_node);
break;
case CMP_NODE_ALPHAOVER:

View File

@ -12,7 +12,7 @@ CombineColorNode::CombineColorNode(bNode *editor_node) : Node(editor_node)
}
void CombineColorNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
const CompositorContext &UNUSED(context)) const
{
NodeInput *input_rsocket = this->get_input_socket(0);
NodeInput *input_gsocket = this->get_input_socket(1);
@ -40,7 +40,39 @@ void CombineColorNode::convert_to_operations(NodeConverter &converter,
converter.map_input_socket(input_bsocket, operation->get_input_socket(2));
converter.map_input_socket(input_asocket, operation->get_input_socket(3));
NodeOperation *color_conv = get_color_converter(context);
bNode *editor_node = this->get_bnode();
NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)editor_node->storage;
NodeOperation *color_conv = nullptr;
switch (storage->mode) {
case CMP_NODE_COMBSEP_COLOR_RGB: {
/* Pass */
break;
}
case CMP_NODE_COMBSEP_COLOR_HSV: {
color_conv = new ConvertHSVToRGBOperation();
break;
}
case CMP_NODE_COMBSEP_COLOR_HSL: {
color_conv = new ConvertHSLToRGBOperation();
break;
}
case CMP_NODE_COMBSEP_COLOR_YCC: {
ConvertYCCToRGBOperation *operation = new ConvertYCCToRGBOperation();
operation->set_mode(storage->ycc_mode);
color_conv = operation;
break;
}
case CMP_NODE_COMBSEP_COLOR_YUV: {
color_conv = new ConvertYUVToRGBOperation();
break;
}
default: {
BLI_assert_unreachable();
break;
}
}
if (color_conv) {
converter.add_operation(color_conv);
@ -52,27 +84,4 @@ void CombineColorNode::convert_to_operations(NodeConverter &converter,
}
}
NodeOperation *CombineRGBANode::get_color_converter(const CompositorContext & /*context*/) const
{
return nullptr; /* no conversion needed */
}
NodeOperation *CombineHSVANode::get_color_converter(const CompositorContext & /*context*/) const
{
return new ConvertHSVToRGBOperation();
}
NodeOperation *CombineYCCANode::get_color_converter(const CompositorContext & /*context*/) const
{
ConvertYCCToRGBOperation *operation = new ConvertYCCToRGBOperation();
bNode *editor_node = this->get_bnode();
operation->set_mode(editor_node->custom1);
return operation;
}
NodeOperation *CombineYUVANode::get_color_converter(const CompositorContext & /*context*/) const
{
return new ConvertYUVToRGBOperation();
}
} // namespace blender::compositor

View File

@ -12,45 +12,6 @@ class CombineColorNode : public Node {
CombineColorNode(bNode *editor_node);
void convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const override;
protected:
virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0;
};
class CombineRGBANode : public CombineColorNode {
public:
CombineRGBANode(bNode *editor_node) : CombineColorNode(editor_node)
{
}
NodeOperation *get_color_converter(const CompositorContext &context) const override;
};
class CombineHSVANode : public CombineColorNode {
public:
CombineHSVANode(bNode *editor_node) : CombineColorNode(editor_node)
{
}
NodeOperation *get_color_converter(const CompositorContext &context) const override;
};
class CombineYCCANode : public CombineColorNode {
public:
CombineYCCANode(bNode *editor_node) : CombineColorNode(editor_node)
{
}
NodeOperation *get_color_converter(const CompositorContext &context) const override;
};
class CombineYUVANode : public CombineColorNode {
public:
CombineYUVANode(bNode *editor_node) : CombineColorNode(editor_node)
{
}
NodeOperation *get_color_converter(const CompositorContext &context) const override;
};
} // namespace blender::compositor

View File

@ -0,0 +1,78 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2011 Blender Foundation. */
#include "COM_CombineColorNodeLegacy.h"
#include "COM_ConvertOperation.h"
namespace blender::compositor {
CombineColorNodeLegacy::CombineColorNodeLegacy(bNode *editor_node) : Node(editor_node)
{
}
void CombineColorNodeLegacy::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
NodeInput *input_rsocket = this->get_input_socket(0);
NodeInput *input_gsocket = this->get_input_socket(1);
NodeInput *input_bsocket = this->get_input_socket(2);
NodeInput *input_asocket = this->get_input_socket(3);
NodeOutput *output_socket = this->get_output_socket(0);
CombineChannelsOperation *operation = new CombineChannelsOperation();
if (input_rsocket->is_linked()) {
operation->set_canvas_input_index(0);
}
else if (input_gsocket->is_linked()) {
operation->set_canvas_input_index(1);
}
else if (input_bsocket->is_linked()) {
operation->set_canvas_input_index(2);
}
else {
operation->set_canvas_input_index(3);
}
converter.add_operation(operation);
converter.map_input_socket(input_rsocket, operation->get_input_socket(0));
converter.map_input_socket(input_gsocket, operation->get_input_socket(1));
converter.map_input_socket(input_bsocket, operation->get_input_socket(2));
converter.map_input_socket(input_asocket, operation->get_input_socket(3));
NodeOperation *color_conv = get_color_converter(context);
if (color_conv) {
converter.add_operation(color_conv);
converter.add_link(operation->get_output_socket(), color_conv->get_input_socket(0));
converter.map_output_socket(output_socket, color_conv->get_output_socket());
}
else {
converter.map_output_socket(output_socket, operation->get_output_socket());
}
}
NodeOperation *CombineRGBANode::get_color_converter(const CompositorContext & /*context*/) const
{
return nullptr; /* no conversion needed */
}
NodeOperation *CombineHSVANode::get_color_converter(const CompositorContext & /*context*/) const
{
return new ConvertHSVToRGBOperation();
}
NodeOperation *CombineYCCANode::get_color_converter(const CompositorContext & /*context*/) const
{
ConvertYCCToRGBOperation *operation = new ConvertYCCToRGBOperation();
bNode *editor_node = this->get_bnode();
operation->set_mode(editor_node->custom1);
return operation;
}
NodeOperation *CombineYUVANode::get_color_converter(const CompositorContext & /*context*/) const
{
return new ConvertYUVToRGBOperation();
}
} // namespace blender::compositor

View File

@ -0,0 +1,56 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2011 Blender Foundation. */
#pragma once
#include "COM_Node.h"
namespace blender::compositor {
class CombineColorNodeLegacy : public Node {
public:
CombineColorNodeLegacy(bNode *editor_node);
void convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const override;
protected:
virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0;
};
class CombineRGBANode : public CombineColorNodeLegacy {
public:
CombineRGBANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node)
{
}
NodeOperation *get_color_converter(const CompositorContext &context) const override;
};
class CombineHSVANode : public CombineColorNodeLegacy {
public:
CombineHSVANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node)
{
}
NodeOperation *get_color_converter(const CompositorContext &context) const override;
};
class CombineYCCANode : public CombineColorNodeLegacy {
public:
CombineYCCANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node)
{
}
NodeOperation *get_color_converter(const CompositorContext &context) const override;
};
class CombineYUVANode : public CombineColorNodeLegacy {
public:
CombineYUVANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node)
{
}
NodeOperation *get_color_converter(const CompositorContext &context) const override;
};
} // namespace blender::compositor

View File

@ -12,7 +12,7 @@ SeparateColorNode::SeparateColorNode(bNode *editor_node) : Node(editor_node)
}
void SeparateColorNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
const CompositorContext &UNUSED(context)) const
{
NodeInput *image_socket = this->get_input_socket(0);
NodeOutput *output_rsocket = this->get_output_socket(0);
@ -20,7 +20,39 @@ void SeparateColorNode::convert_to_operations(NodeConverter &converter,
NodeOutput *output_bsocket = this->get_output_socket(2);
NodeOutput *output_asocket = this->get_output_socket(3);
NodeOperation *color_conv = get_color_converter(context);
bNode *editor_node = this->get_bnode();
NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)editor_node->storage;
NodeOperation *color_conv = nullptr;
switch (storage->mode) {
case CMP_NODE_COMBSEP_COLOR_RGB: {
/* Pass */
break;
}
case CMP_NODE_COMBSEP_COLOR_HSV: {
color_conv = new ConvertRGBToHSVOperation();
break;
}
case CMP_NODE_COMBSEP_COLOR_HSL: {
color_conv = new ConvertRGBToHSLOperation();
break;
}
case CMP_NODE_COMBSEP_COLOR_YCC: {
ConvertRGBToYCCOperation *operation = new ConvertRGBToYCCOperation();
operation->set_mode(storage->ycc_mode);
color_conv = operation;
break;
}
case CMP_NODE_COMBSEP_COLOR_YUV: {
color_conv = new ConvertRGBToYUVOperation();
break;
}
default: {
BLI_assert_unreachable();
break;
}
}
if (color_conv) {
converter.add_operation(color_conv);
@ -84,27 +116,4 @@ void SeparateColorNode::convert_to_operations(NodeConverter &converter,
}
}
NodeOperation *SeparateRGBANode::get_color_converter(const CompositorContext & /*context*/) const
{
return nullptr; /* no conversion needed */
}
NodeOperation *SeparateHSVANode::get_color_converter(const CompositorContext & /*context*/) const
{
return new ConvertRGBToHSVOperation();
}
NodeOperation *SeparateYCCANode::get_color_converter(const CompositorContext & /*context*/) const
{
ConvertRGBToYCCOperation *operation = new ConvertRGBToYCCOperation();
bNode *editor_node = this->get_bnode();
operation->set_mode(editor_node->custom1);
return operation;
}
NodeOperation *SeparateYUVANode::get_color_converter(const CompositorContext & /*context*/) const
{
return new ConvertRGBToYUVOperation();
}
} // namespace blender::compositor

View File

@ -12,45 +12,6 @@ class SeparateColorNode : public Node {
SeparateColorNode(bNode *editor_node);
void convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const override;
protected:
virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0;
};
class SeparateRGBANode : public SeparateColorNode {
public:
SeparateRGBANode(bNode *editor_node) : SeparateColorNode(editor_node)
{
}
NodeOperation *get_color_converter(const CompositorContext &context) const override;
};
class SeparateHSVANode : public SeparateColorNode {
public:
SeparateHSVANode(bNode *editor_node) : SeparateColorNode(editor_node)
{
}
NodeOperation *get_color_converter(const CompositorContext &context) const override;
};
class SeparateYCCANode : public SeparateColorNode {
public:
SeparateYCCANode(bNode *editor_node) : SeparateColorNode(editor_node)
{
}
NodeOperation *get_color_converter(const CompositorContext &context) const override;
};
class SeparateYUVANode : public SeparateColorNode {
public:
SeparateYUVANode(bNode *editor_node) : SeparateColorNode(editor_node)
{
}
NodeOperation *get_color_converter(const CompositorContext &context) const override;
};
} // namespace blender::compositor

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