Merge branch 'master' into xr-dev
This commit is contained in:
commit
c5a8372a11
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
28
GNUmakefile
28
GNUmakefile
|
@ -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)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
|
|
@ -64,6 +64,7 @@ ENDIF()
|
|||
MARK_AS_ADVANCED(
|
||||
USD_INCLUDE_DIR
|
||||
USD_LIBRARY_DIR
|
||||
USD_LIBRARY
|
||||
)
|
||||
|
||||
UNSET(_usd_SEARCH_DIRS)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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])
|
||||
|
|
@ -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')
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>();
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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!");
|
||||
}
|
|
@ -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];
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
@ -16,4 +16,5 @@ __all__ = (
|
|||
"mesh_utils",
|
||||
"node_utils",
|
||||
"view3d_utils",
|
||||
"id_map_utils",
|
||||
)
|
||||
|
|
|
@ -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
|
|
@ -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'
|
||||
|
|
|
@ -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),
|
||||
])
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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=[
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
|
||||
/**
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>>) {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue