Vulkan: Clearing Storage Buffers #105487

Merged
Jeroen Bakker merged 94 commits from Jeroen-Bakker/blender:vulkan-storage-buffer-clear into main 2023-03-17 13:48:50 +01:00
118 changed files with 1988 additions and 1245 deletions
Showing only changes of commit 1ec53f799a - Show all commits

View File

@ -2,4 +2,4 @@ ${CommitTitle}
${CommitBody}
Pull Request #${PullRequestIndex}
Pull Request: https://projects.blender.org/blender/blender/pulls/${PullRequestIndex}

View File

@ -1,3 +1,3 @@
${PullRequestTitle}
Pull Request #${PullRequestIndex}
Pull Request: https://projects.blender.org/blender/blender/pulls/${PullRequestIndex}

7
.gitignore vendored
View File

@ -65,3 +65,10 @@ waveletNoiseTile.bin
/release/datafiles/locale/
/release/scripts/addons_contrib/
/source/tools/
# Build files for VS and VS Code.
/build/
/out/
CMakeSettings.json
CMakePresets.json
CMakeUserPresets.json

View File

@ -1865,7 +1865,7 @@ def pyrna2sphinx(basepath):
else:
url_base = API_BASEURL
fw(" :file: `%s\\:%d <%s/%s$%d>`_\n\n" %
fw(" :file:`%s\\:%d <%s/%s#L%d>`_\n\n" %
(location[0], location[1], url_base, location[0], location[1]))
file.close()

View File

@ -1673,7 +1673,7 @@ class CyclesPreferences(bpy.types.AddonPreferences):
driver_version = "470"
col.label(text=iface_("Requires NVIDIA GPU with compute capability %s") % compute_capability,
icon='BLANK1', translate=False)
col.label(text="and NVIDIA driver version %s or newer" % driver_version,
col.label(text=iface_("and NVIDIA driver version %s or newer") % driver_version,
icon='BLANK1', translate=False)
elif device_type == 'HIP':
if True:
@ -1719,7 +1719,8 @@ class CyclesPreferences(bpy.types.AddonPreferences):
.replace('(TM)', unicodedata.lookup('TRADE MARK SIGN'))
.replace('(tm)', unicodedata.lookup('TRADE MARK SIGN'))
.replace('(R)', unicodedata.lookup('REGISTERED SIGN'))
.replace('(C)', unicodedata.lookup('COPYRIGHT SIGN'))
.replace('(C)', unicodedata.lookup('COPYRIGHT SIGN')),
translate=False
)
def draw_impl(self, layout, context):

View File

@ -161,25 +161,12 @@ ShaderCache::~ShaderCache()
running = false;
cond_var.notify_all();
int num_incomplete = int(incomplete_requests);
if (num_incomplete) {
/* Shutting down the app with incomplete shader compilation requests. Give 1 second's grace for
* clean shutdown. */
metal_printf("ShaderCache busy (incomplete_requests = %d)...\n", num_incomplete);
std::this_thread::sleep_for(std::chrono::seconds(1));
num_incomplete = int(incomplete_requests);
}
if (num_incomplete && !MetalDeviceKernels::is_benchmark_warmup()) {
metal_printf("ShaderCache still busy (incomplete_requests = %d). Terminating...\n",
num_incomplete);
std::terminate();
}
metal_printf("ShaderCache idle. Shutting down.\n");
metal_printf("Waiting for ShaderCache threads... (incomplete_requests = %d)\n",
int(incomplete_requests));
for (auto &thread : compile_threads) {
thread.join();
}
metal_printf("ShaderCache shut down.\n");
}
void ShaderCache::wait_for_all()
@ -675,7 +662,9 @@ void MetalKernelPipeline::compile()
}
}
__block bool creating_new_archive = false;
bool creating_new_archive = false;
bool recreate_archive = false;
if (@available(macOS 11.0, *)) {
if (use_binary_archive) {
if (!archive) {
@ -684,51 +673,101 @@ void MetalKernelPipeline::compile()
archive = [mtlDevice newBinaryArchiveWithDescriptor:archiveDesc error:nil];
creating_new_archive = true;
}
computePipelineStateDescriptor.binaryArchives = [NSArray arrayWithObjects:archive, nil];
pipelineOptions = MTLPipelineOptionFailOnBinaryArchiveMiss;
else {
pipelineOptions = MTLPipelineOptionFailOnBinaryArchiveMiss;
computePipelineStateDescriptor.binaryArchives = [NSArray arrayWithObjects:archive, nil];
}
}
}
/* Lambda to do the actual pipeline compilation. */
auto do_compilation = [&]() {
__block bool compilation_finished = false;
__block string error_str;
if (archive && path_exists(metalbin_path)) {
/* Use the blocking variant of newComputePipelineStateWithDescriptor if an archive exists on
* disk. It should load almost instantaneously, and will fail gracefully when loading a
* corrupt archive (unlike the async variant). */
NSError *error = nil;
pipeline = [mtlDevice newComputePipelineStateWithDescriptor:computePipelineStateDescriptor
options:pipelineOptions
reflection:nullptr
error:&error];
const char *err = error ? [[error localizedDescription] UTF8String] : nullptr;
error_str = err ? err : "nil";
}
else {
/* Use the async variant of newComputePipelineStateWithDescriptor if no archive exists on
* disk. This allows us responds to app shutdown. */
[mtlDevice
newComputePipelineStateWithDescriptor:computePipelineStateDescriptor
options:pipelineOptions
completionHandler:^(id<MTLComputePipelineState> computePipelineState,
MTLComputePipelineReflection *reflection,
NSError *error) {
pipeline = computePipelineState;
/* Retain the pipeline so we can use it safely past the completion
* handler. */
if (pipeline) {
[pipeline retain];
}
const char *err = error ?
[[error localizedDescription] UTF8String] :
nullptr;
error_str = err ? err : "nil";
compilation_finished = true;
}];
/* Immediately wait for either the compilation to finish or for app shutdown. */
while (ShaderCache::running && !compilation_finished) {
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
}
if (creating_new_archive && pipeline && ShaderCache::running) {
/* Add pipeline into the new archive. It should be instantaneous following
* newComputePipelineStateWithDescriptor. */
NSError *error;
computePipelineStateDescriptor.binaryArchives = [NSArray arrayWithObjects:archive, nil];
if (![archive addComputePipelineFunctionsWithDescriptor:computePipelineStateDescriptor
error:&error]) {
NSString *errStr = [error localizedDescription];
metal_printf("Failed to add PSO to archive:\n%s\n", errStr ? [errStr UTF8String] : "nil");
}
}
else if (!pipeline) {
metal_printf(
"newComputePipelineStateWithDescriptor failed for \"%s\"%s. "
"Error:\n%s\n",
device_kernel_as_string((DeviceKernel)device_kernel),
(archive && !recreate_archive) ? " Archive may be incomplete or corrupt - attempting "
"recreation.." :
"",
error_str.c_str());
}
};
double starttime = time_dt();
/* Block on load to ensure we continue with a valid kernel function */
if (creating_new_archive) {
starttime = time_dt();
NSError *error;
if (![archive addComputePipelineFunctionsWithDescriptor:computePipelineStateDescriptor
error:&error]) {
NSString *errStr = [error localizedDescription];
metal_printf("Failed to add PSO to archive:\n%s\n", errStr ? [errStr UTF8String] : "nil");
}
}
do_compilation();
pipeline = [mtlDevice newComputePipelineStateWithDescriptor:computePipelineStateDescriptor
options:pipelineOptions
reflection:nullptr
error:&error];
bool recreate_archive = false;
/* An archive might have a corrupt entry and fail to materialize the pipeline. This shouldn't
* happen, but if it does we recreate it. */
if (pipeline == nil && archive) {
NSString *errStr = [error localizedDescription];
metal_printf(
"Failed to create compute pipeline state \"%s\" from archive - attempting recreation... "
"(error: %s)\n",
device_kernel_as_string((DeviceKernel)device_kernel),
errStr ? [errStr UTF8String] : "nil");
pipeline = [mtlDevice newComputePipelineStateWithDescriptor:computePipelineStateDescriptor
options:MTLPipelineOptionNone
reflection:nullptr
error:&error];
recreate_archive = true;
pipelineOptions = MTLPipelineOptionNone;
path_remove(metalbin_path);
do_compilation();
}
double duration = time_dt() - starttime;
if (pipeline == nil) {
NSString *errStr = [error localizedDescription];
error_str = string_printf("Failed to create compute pipeline state \"%s\", error: \n",
device_kernel_as_string((DeviceKernel)device_kernel));
error_str += (errStr ? [errStr UTF8String] : "nil");
metal_printf("%16s | %2d | %-55s | %7.2fs | FAILED!\n",
kernel_type_as_string(pso_type),
device_kernel,
@ -748,7 +787,8 @@ void MetalKernelPipeline::compile()
if (creating_new_archive || recreate_archive) {
if (![archive serializeToURL:[NSURL fileURLWithPath:@(metalbin_path.c_str())]
error:&error]) {
metal_printf("Failed to save binary archive, error:\n%s\n",
metal_printf("Failed to save binary archive to %s, error:\n%s\n",
metalbin_path.c_str(),
[[error localizedDescription] UTF8String]);
}
else {

View File

@ -860,7 +860,7 @@ if(WITH_CYCLES_DEVICE_ONEAPI)
add_custom_command(
OUTPUT ${cycles_kernel_oneapi_lib} ${cycles_kernel_oneapi_linker_lib}
COMMAND ${CMAKE_COMMAND} -E env
"LIB=${sycl_compiler_root}/../lib" # for compiler to find sycl.lib
"LIB=${sycl_compiler_root}/../lib\;${sycl_compiler_root}/../compiler/lib/intel64_win" # for compiler to find sycl.lib and in case of icpx, libircmt.lib
"PATH=${OCLOC_INSTALL_DIR}\;${sycl_compiler_root}"
${SYCL_COMPILER}
"$<$<CONFIG:Release>:${sycl_compiler_flags_Release}>"

View File

@ -13,6 +13,7 @@
#ifndef __FFMPEG_COMPAT_H__
#define __FFMPEG_COMPAT_H__
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
/* Check if our ffmpeg is new enough, avoids user complaints.

View File

@ -103,6 +103,9 @@ bool win32_chk(bool result, const char *file, int line, const char *text)
_ftprintf(
stderr, "%s:%d: [%s] -> Win32 Error# (%lu): %s", file, line, text, ulong(error), msg);
# else
(void)file;
(void)line;
(void)text;
_ftprintf(stderr, "Win32 Error# (%lu): %s", ulong(error), msg);
# endif

View File

@ -78,7 +78,7 @@ ULONG __stdcall GHOST_DropTargetWin32::Release(void)
* Implementation of IDropTarget::DragEnter
*/
HRESULT __stdcall GHOST_DropTargetWin32::DragEnter(IDataObject *p_data_object,
DWORD grf_key_state,
DWORD /*grf_key_state*/,
POINTL pt,
DWORD *pdw_effect)
{
@ -95,7 +95,7 @@ HRESULT __stdcall GHOST_DropTargetWin32::DragEnter(IDataObject *p_data_object,
/*
* Implementation of IDropTarget::DragOver
*/
HRESULT __stdcall GHOST_DropTargetWin32::DragOver(DWORD grf_key_state,
HRESULT __stdcall GHOST_DropTargetWin32::DragOver(DWORD /*grf_key_state*/,
POINTL pt,
DWORD *pdw_effect)
{
@ -128,7 +128,7 @@ HRESULT __stdcall GHOST_DropTargetWin32::DragLeave(void)
* the implementation of IDropTarget::DragOver
*/
HRESULT __stdcall GHOST_DropTargetWin32::Drop(IDataObject *p_data_object,
DWORD grf_key_state,
DWORD /*grf_key_state*/,
POINTL pt,
DWORD *pdw_effect)
{

View File

@ -140,7 +140,7 @@ void GHOST_ImeWin32::SetImeWindowStyle(
::DefWindowProc(window_handle, message, wparam, lparam);
}
void GHOST_ImeWin32::DestroyImeWindow(HWND window_handle)
void GHOST_ImeWin32::DestroyImeWindow(HWND /*window_handle*/)
{
/* Destroy the system caret if we have created for this IME input context. */
if (system_caret_) {
@ -149,7 +149,7 @@ void GHOST_ImeWin32::DestroyImeWindow(HWND window_handle)
}
}
void GHOST_ImeWin32::MoveImeWindow(HWND window_handle, HIMC imm_context)
void GHOST_ImeWin32::MoveImeWindow(HWND /*window_handle*/, HIMC imm_context)
{
int x = caret_rect_.m_l;
int y = caret_rect_.m_t;
@ -228,7 +228,7 @@ void GHOST_ImeWin32::CheckFirst(HWND window_handle)
}
}
void GHOST_ImeWin32::ResetComposition(HWND window_handle)
void GHOST_ImeWin32::ResetComposition(HWND /*window_handle*/)
{
/* Currently, just reset the composition status. */
is_composing_ = false;

View File

@ -217,7 +217,7 @@ GHOST_IWindow *GHOST_SystemWin32::createWindow(const char *title,
uint32_t height,
GHOST_TWindowState state,
GHOST_GLSettings glSettings,
const bool exclusive,
const bool /*exclusive*/,
const bool is_dialog,
const GHOST_IWindow *parentWindow)
{
@ -568,7 +568,7 @@ GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw, bool *r_key_down)
* This function was added in response to bug #25715.
* This is going to be a long list #42426.
*/
GHOST_TKey GHOST_SystemWin32::processSpecialKey(short vKey, short scanCode) const
GHOST_TKey GHOST_SystemWin32::processSpecialKey(short vKey, short /*scanCode*/) const
{
GHOST_TKey key = GHOST_kKeyUnknown;
if (vKey == 0xFF) {
@ -1148,7 +1148,9 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
GHOST_TABLET_DATA_NONE);
}
void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam)
void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window,
WPARAM wParam,
LPARAM /*lParam*/)
{
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
@ -2213,7 +2215,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, uint msg, WPARAM wParam,
return lResult;
}
char *GHOST_SystemWin32::getClipboard(bool selection) const
char *GHOST_SystemWin32::getClipboard(bool /*selection*/) const
{
if (IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(NULL)) {
wchar_t *buffer;

View File

@ -265,14 +265,14 @@ HRESULT GHOST_DirectManipulationViewportEventHandler::OnViewportStatusChanged(
}
HRESULT GHOST_DirectManipulationViewportEventHandler::OnViewportUpdated(
IDirectManipulationViewport *viewport)
IDirectManipulationViewport * /*viewport*/)
{
/* Nothing to do here. */
return S_OK;
}
HRESULT GHOST_DirectManipulationViewportEventHandler::OnContentUpdated(
IDirectManipulationViewport *viewport, IDirectManipulationContent *content)
IDirectManipulationViewport * /*viewport*/, IDirectManipulationContent *content)
{
float transform[6];
HRESULT hr = content->GetContentTransform(transform, ARRAYSIZE(transform));

View File

@ -898,7 +898,7 @@ GHOST_TSuccess GHOST_WindowWin32::hasCursorShape(GHOST_TStandardCursor cursorSha
}
GHOST_TSuccess GHOST_WindowWin32::getPointerInfo(
std::vector<GHOST_PointerInfoWin32> &outPointerInfo, WPARAM wParam, LPARAM lParam)
std::vector<GHOST_PointerInfoWin32> &outPointerInfo, WPARAM wParam, LPARAM /*lParam*/)
{
int32_t pointerId = GET_POINTERID_WPARAM(wParam);
int32_t isPrimary = IS_POINTER_PRIMARY_WPARAM(wParam);
@ -1109,8 +1109,13 @@ static uint16_t uns16ReverseBits(uint16_t shrt)
}
#endif
GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(
uint8_t *bitmap, uint8_t *mask, int sizeX, int sizeY, int hotX, int hotY, bool canInvertColor)
GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(uint8_t *bitmap,
uint8_t *mask,
int sizeX,
int sizeY,
int hotX,
int hotY,
bool /*canInvertColor*/)
{
uint32_t andData[32];
uint32_t xorData[32];
@ -1175,7 +1180,7 @@ GHOST_TSuccess GHOST_WindowWin32::endProgressBar()
}
#ifdef WITH_INPUT_IME
void GHOST_WindowWin32::beginIME(int32_t x, int32_t y, int32_t w, int32_t h, bool completed)
void GHOST_WindowWin32::beginIME(int32_t x, int32_t y, int32_t /*w*/, int32_t h, bool completed)
{
m_imeInput.BeginIME(m_hWnd, GHOST_Rect(x, y - h, x, y), completed);
}

View File

@ -501,9 +501,7 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
Recursively get strings, needed in case we have "Blah" + "Blah", passed as an argument in that case it won't
evaluate to a string. However, break on some kind of stopper nodes, like e.g. Subscript.
"""
# New in py 3.8: all constants are of type 'ast.Constant'.
# 'ast.Str' will have to be removed when we officially switch to this version.
if type(node) in {ast.Str, getattr(ast, "Constant", None)}:
if type(node) == ast.Constant:
eval_str = ast.literal_eval(node)
if eval_str and type(eval_str) == str:
yield (is_split, eval_str, (node,))
@ -692,6 +690,15 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
else:
continue
# Skip function if it's marked as not translatable.
do_translate = True
for kw in node.keywords:
if kw.arg == "translate" and not kw.value.value:
do_translate = False
break
if not do_translate:
continue
func_args = func_translate_args.get(func_id, {})
# First try to get i18n contexts, for every possible msgid id.

View File

@ -226,4 +226,5 @@ _km_hierarchy = [
('Transform Modal Map', 'EMPTY', 'WINDOW', []),
('Eyedropper Modal Map', 'EMPTY', 'WINDOW', []),
('Eyedropper ColorRamp PointSampling Map', 'EMPTY', 'WINDOW', []),
('Mesh Filter Modal Map', 'EMPTY', 'WINDOW', []),
]

View File

@ -6318,6 +6318,25 @@ def km_sculpt_expand_modal(_params):
])
return keymap
def km_sculpt_mesh_filter_modal_map(_params):
items = []
keymap = (
"Mesh Filter Modal Map",
{"space_type": 'EMPTY', "region_type": 'WINDOW', "modal": True},
{"items": items},
)
items.extend([
("CONFIRM", {"type": 'LEFTMOUSE', "value": 'PRESS', "any": True}, None),
("CONFIRM", {"type": 'LEFTMOUSE', "value": 'RELEASE', "any": True}, None),
("CONFIRM", {"type": 'RET', "value": 'RELEASE', "any": True}, None),
("CONFIRM", {"type": 'NUMPAD_ENTER', "value": 'RELEASE', "any": True}, None),
("CANCEL", {"type": 'ESC', "value": 'PRESS', "any": True}, None),
("CANCEL", {"type": 'RIGHTMOUSE', "value": 'PRESS', "any": True}, None),
])
return keymap
def km_curve_pen_modal_map(_params):
items = []
@ -8126,6 +8145,7 @@ def generate_keymaps(params=None):
km_view3d_dolly_modal(params),
km_paint_stroke_modal(params),
km_sculpt_expand_modal(params),
km_sculpt_mesh_filter_modal_map(params),
km_curve_pen_modal_map(params),
km_node_link_modal_map(params),

View File

@ -54,6 +54,9 @@ class SCENE_OT_freestyle_fill_range_by_selection(Operator):
# Find the reference object
if m.type == 'DISTANCE_FROM_CAMERA':
ref = scene.camera
if ref is None:
self.report({'ERROR'}, "No active camera in the scene")
return {'CANCELLED'}
matrix_to_camera = ref.matrix_world.inverted()
elif m.type == 'DISTANCE_FROM_OBJECT':
if m.target is None:

View File

@ -2,7 +2,10 @@
import bpy
from bpy.types import Menu
from bl_ui import node_add_menu
from bpy.app.translations import pgettext_iface as iface_
from bpy.app.translations import (
pgettext_iface as iface_,
contexts as i18n_contexts,
)
class NODE_MT_geometry_node_GEO_ATTRIBUTE(Menu):
@ -238,6 +241,7 @@ class NODE_MT_geometry_node_GEO_INPUT(Menu):
class NODE_MT_geometry_node_GEO_INPUT_CONSTANT(Menu):
bl_idname = "NODE_MT_geometry_node_GEO_INPUT_CONSTANT"
bl_label = "Constant"
bl_translation_context = i18n_contexts.id_nodetree
def draw(self, _context):
layout = self.layout

View File

@ -745,6 +745,8 @@ class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel):
row.operator("asset.open_containing_blend_file", text="", icon='TOOL_SETTINGS')
layout.prop(asset_file_handle.asset_data, "description")
layout.prop(asset_file_handle.asset_data, "license")
layout.prop(asset_file_handle.asset_data, "copyright")
layout.prop(asset_file_handle.asset_data, "author")

View File

@ -79,6 +79,7 @@ class TEXT_HT_footer(Header):
text=iface_("Text: External")
if text.library
else iface_("Text: Internal"),
translate=False
)

View File

@ -2832,7 +2832,9 @@ class VIEW3D_MT_object_parent(Menu):
layout.separator()
layout.operator_context = 'EXEC_REGION_WIN'
layout.operator("object.parent_no_inverse_set")
layout.operator("object.parent_no_inverse_set").keep_transform = False
props = layout.operator("object.parent_no_inverse_set", text="Make Parent without Inverse (Keep Transform)")
props.keep_transform = True
layout.operator_context = operator_context_default
layout.separator()
@ -3219,17 +3221,35 @@ class VIEW3D_MT_sculpt(Menu):
def draw(self, _context):
layout = self.layout
layout.operator("transform.translate")
layout.operator("transform.rotate")
layout.operator("transform.resize", text="Scale")
props = layout.operator("sculpt.mesh_filter", text="Sphere")
props.type = 'SPHERE'
layout.separator()
props = layout.operator("paint.hide_show", text="Box Hide")
props.action = 'HIDE'
props = layout.operator("paint.hide_show", text="Box Show")
props.action = 'SHOW'
layout.separator()
props = layout.operator("sculpt.face_set_change_visibility", text="Toggle Visibility")
props.mode = 'TOGGLE'
props = layout.operator("sculpt.face_set_change_visibility", text="Hide Active Face Set")
props.mode = 'HIDE_ACTIVE'
props = layout.operator("paint.hide_show", text="Show All")
props.action = 'SHOW'
props.area = 'ALL'
props = layout.operator("paint.hide_show", text="Box Show")
props.action = 'SHOW'
props.area = 'INSIDE'
props = layout.operator("paint.hide_show", text="Box Hide")
props.action = 'HIDE'
props.area = 'INSIDE'
props = layout.operator("sculpt.face_set_change_visibility", text="Invert Visible")
props.mode = 'INVERT'
props = layout.operator("paint.hide_show", text="Hide Masked")
props.action = 'HIDE'
@ -3237,10 +3257,55 @@ class VIEW3D_MT_sculpt(Menu):
layout.separator()
props = layout.operator("sculpt.trim_box_gesture", text="Box Trim")
props.trim_mode = 'DIFFERENCE'
layout.operator("sculpt.trim_lasso_gesture", text="Lasso Trim")
props.trim_mode = 'DIFFERENCE'
props = layout.operator("sculpt.trim_box_gesture", text="Box Add")
props.trim_mode = 'JOIN'
layout.operator("sculpt.trim_lasso_gesture", text="Lasso Add")
props.trim_mode = 'JOIN'
layout.operator("sculpt.project_line_gesture", text="Line Project")
layout.separator()
# Fair Positions
props = layout.operator("sculpt.face_set_edit", text="Fair Positions")
props.mode = 'FAIR_POSITIONS'
# Fair Tangency
props = layout.operator("sculpt.face_set_edit", text="Fair Tangency")
props.mode = 'FAIR_TANGENCY'
layout.separator()
sculpt_filters_types = [
('SMOOTH', "Smooth"),
('SURFACE_SMOOTH', "Surface Smooth"),
('INFLATE', "Inflate"),
('RELAX', "Relax Topology"),
('RELAX_FACE_SETS', "Relax Face Sets"),
('SHARPEN', "Sharpen"),
('ENHANCE_DETAILS', "Enhance Details"),
('ERASE_DISCPLACEMENT', "Erase Multires Displacement"),
('RANDOM', "Randomize")
]
for filter_type, ui_name in sculpt_filters_types:
props = layout.operator("sculpt.mesh_filter", text=ui_name)
props.type = filter_type
layout.separator()
layout.menu("VIEW3D_MT_sculpt_set_pivot", text="Set Pivot")
layout.separator()
# Rebuild BVH
layout.operator("sculpt.optimize")
layout.separator()
@ -6376,6 +6441,9 @@ class VIEW3D_PT_overlay_object(Panel):
sub = split.column(align=True)
sub.prop(overlay, "show_extras", text="Extras")
subsub = sub.column()
subsub.active = overlay.show_extras
subsub.prop(overlay, "show_light_colors")
sub.prop(overlay, "show_relationship_lines")
sub.prop(overlay, "show_outline_selected")

View File

@ -44,6 +44,8 @@ AssetMetaData::~AssetMetaData()
}
MEM_SAFE_FREE(author);
MEM_SAFE_FREE(description);
MEM_SAFE_FREE(copyright);
MEM_SAFE_FREE(license);
BLI_freelistN(&tags);
}
@ -161,13 +163,19 @@ void BKE_asset_metadata_write(BlendWriter *writer, AssetMetaData *asset_data)
if (asset_data->properties) {
IDP_BlendWrite(writer, asset_data->properties);
}
if (asset_data->author) {
BLO_write_string(writer, asset_data->author);
}
if (asset_data->description) {
BLO_write_string(writer, asset_data->description);
}
if (asset_data->copyright) {
BLO_write_string(writer, asset_data->copyright);
}
if (asset_data->license) {
BLO_write_string(writer, asset_data->license);
}
LISTBASE_FOREACH (AssetTag *, tag, &asset_data->tags) {
BLO_write_struct(writer, AssetTag, tag);
}
@ -185,6 +193,8 @@ void BKE_asset_metadata_read(BlendDataReader *reader, AssetMetaData *asset_data)
BLO_read_data_address(reader, &asset_data->author);
BLO_read_data_address(reader, &asset_data->description);
BLO_read_data_address(reader, &asset_data->copyright);
BLO_read_data_address(reader, &asset_data->license);
BLO_read_list(reader, &asset_data->tags);
BLI_assert(BLI_listbase_count(&asset_data->tags) == asset_data->tot_tags);
}

View File

@ -272,7 +272,7 @@ static bool library_foreach_ID_link(Main *bmain,
}
if (bmain != NULL && bmain->relations != NULL && (flag & IDWALK_READONLY) &&
(flag & IDWALK_DO_INTERNAL_RUNTIME_POINTERS) == 0 &&
(flag & (IDWALK_DO_INTERNAL_RUNTIME_POINTERS | IDWALK_DO_LIBRARY_POINTER)) == 0 &&
(((bmain->relations->flag & MAINIDRELATIONS_INCLUDE_UI) == 0) ==
((data.flag & IDWALK_INCLUDE_UI) == 0))) {
/* Note that this is minor optimization, even in worst cases (like id being an object with

View File

@ -342,7 +342,7 @@ MaskLayer *BKE_mask_layer_new(Mask *mask, const char *name)
BLI_strncpy(masklay->name, name, sizeof(masklay->name));
}
else {
strcpy(masklay->name, "MaskLayer");
strcpy(masklay->name, DATA_("MaskLayer"));
}
BLI_addtail(&mask->masklayers, masklay);

View File

@ -48,9 +48,9 @@ static int last_studiolight_id = 0;
*/
#define STUDIOLIGHT_LOAD_CACHED_FILES
static const char *STUDIOLIGHT_LIGHTS_FOLDER = "studiolights/studio/";
static const char *STUDIOLIGHT_WORLD_FOLDER = "studiolights/world/";
static const char *STUDIOLIGHT_MATCAP_FOLDER = "studiolights/matcap/";
static const char *STUDIOLIGHT_LIGHTS_FOLDER = "studiolights" SEP_STR "studio" SEP_STR;
static const char *STUDIOLIGHT_WORLD_FOLDER = "studiolights" SEP_STR "world" SEP_STR;
static const char *STUDIOLIGHT_MATCAP_FOLDER = "studiolights" SEP_STR "matcap" SEP_STR;
static const char *STUDIOLIGHT_WORLD_DEFAULT = "forest.exr";
static const char *STUDIOLIGHT_MATCAP_DEFAULT = "basic_1.exr";

View File

@ -16,27 +16,27 @@
namespace blender {
template<typename T> class ListBaseWrapper {
template<typename LB, typename T> class ListBaseWrapperTemplate {
private:
ListBase *listbase_;
LB *listbase_;
public:
ListBaseWrapper(ListBase *listbase) : listbase_(listbase)
ListBaseWrapperTemplate(LB *listbase) : listbase_(listbase)
{
BLI_assert(listbase);
}
ListBaseWrapper(ListBase &listbase) : ListBaseWrapper(&listbase)
ListBaseWrapperTemplate(LB &listbase) : ListBaseWrapperTemplate(&listbase)
{
}
class Iterator {
private:
ListBase *listbase_;
LB *listbase_;
T *current_;
public:
Iterator(ListBase *listbase, T *current) : listbase_(listbase), current_(current)
Iterator(LB *listbase, T *current) : listbase_(listbase), current_(current)
{
}
@ -96,4 +96,7 @@ template<typename T> class ListBaseWrapper {
}
};
template<typename T> using ListBaseWrapper = ListBaseWrapperTemplate<ListBase, T>;
template<typename T> using ConstListBaseWrapper = ListBaseWrapperTemplate<const ListBase, const T>;
} /* namespace blender */

View File

@ -272,6 +272,7 @@ void BLI_filelist_entry_mode_to_string(const struct stat *st,
const char *types[8] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"};
#ifdef WIN32
UNUSED_VARS(st);
BLI_strncpy(r_mode1, types[0], sizeof(*r_mode1) * FILELIST_DIRENTRY_MODE_LEN);
BLI_strncpy(r_mode2, types[0], sizeof(*r_mode2) * FILELIST_DIRENTRY_MODE_LEN);
BLI_strncpy(r_mode3, types[0], sizeof(*r_mode3) * FILELIST_DIRENTRY_MODE_LEN);
@ -315,6 +316,7 @@ void BLI_filelist_entry_owner_to_string(const struct stat *st,
char r_owner[FILELIST_DIRENTRY_OWNER_LEN])
{
#ifdef WIN32
UNUSED_VARS(st);
strcpy(r_owner, "unknown");
#else
struct passwd *pwuser = getpwuid(st->st_uid);

View File

@ -76,6 +76,7 @@
#include "readfile.h"
#include "SEQ_channels.h"
#include "SEQ_effects.h"
#include "SEQ_iterator.h"
#include "SEQ_retiming.h"
#include "SEQ_sequencer.h"
@ -1637,6 +1638,16 @@ static bool version_fix_delete_flag(Sequence *seq, void * /*user_data*/)
return true;
}
static bool version_set_seq_single_frame_content(Sequence *seq, void * /*user_data*/)
{
if ((seq->len == 1) &&
(seq->type == SEQ_TYPE_IMAGE ||
((seq->type & SEQ_TYPE_EFFECT) && SEQ_effect_get_num_inputs(seq->type) == 0))) {
seq->flag |= SEQ_SINGLE_FRAME_CONTENT;
}
return true;
}
/* Those `version_liboverride_rnacollections_*` functions mimic the old, pre-3.0 code to find
* anchor and source items in the given list of modifiers, constraints etc., using only the
* `subitem_local` data of the override property operation.
@ -4027,6 +4038,14 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
/* Use `SEQ_SINGLE_FRAME_CONTENT` flag instead of weird function to check if strip has multiple
* frames. */
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
Editing *ed = SEQ_editing_get(scene);
if (ed != nullptr) {
SEQ_for_each_callback(&ed->seqbase, version_set_seq_single_frame_content, nullptr);
}
}
/* Keep this block, even when empty. */
}
}

View File

@ -130,6 +130,8 @@ const char *BLT_translate_do_new_dataname(const char *msgctxt, const char *msgid
#define BLT_I18NCONTEXT_VIRTUAL_REALITY "Virtual reality"
#define BLT_I18NCONTEXT_CONSTRAINT "Constraint"
#define BLT_I18NCONTEXT_COLOR "Color"
#define BLT_I18NCONTEXT_AMOUNT "Amount"
#define BLT_I18NCONTEXT_UNIT "Unit"
/* Helper for bpy.app.i18n object... */
typedef struct {
@ -198,6 +200,8 @@ typedef struct {
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_VIRTUAL_REALITY, "virtual_reality"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_CONSTRAINT, "constraint"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_COLOR, "color"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_AMOUNT, "amount"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_UNIT, "unit"), \
{ \
NULL, NULL, NULL \
} \

View File

@ -261,6 +261,8 @@ static bool bm_face_split_edgenet_find_loop_walk(BMVert *v_init,
const uint edge_order_len,
BMEdge *e_pair[2])
{
UNUSED_VARS_NDEBUG(edge_order_len);
/* fast-path for the common case (avoid push-pop).
* Also avoids tagging as visited since we know we
* can't reach these verts some other way */

View File

@ -76,13 +76,25 @@ void RealizeOnDomainOperation::execute()
GPUShader *RealizeOnDomainOperation::get_realization_shader()
{
switch (get_result().type()) {
case ResultType::Color:
return shader_manager().get("compositor_realize_on_domain_color");
case ResultType::Vector:
return shader_manager().get("compositor_realize_on_domain_vector");
case ResultType::Float:
return shader_manager().get("compositor_realize_on_domain_float");
if (get_input().get_realization_options().interpolation == Interpolation::Bicubic) {
switch (get_result().type()) {
case ResultType::Color:
return shader_manager().get("compositor_realize_on_domain_bicubic_color");
case ResultType::Vector:
return shader_manager().get("compositor_realize_on_domain_bicubic_vector");
case ResultType::Float:
return shader_manager().get("compositor_realize_on_domain_bicubic_float");
}
}
else {
switch (get_result().type()) {
case ResultType::Color:
return shader_manager().get("compositor_realize_on_domain_color");
case ResultType::Vector:
return shader_manager().get("compositor_realize_on_domain_vector");
case ResultType::Float:
return shader_manager().get("compositor_realize_on_domain_float");
}
}
BLI_assert_unreachable();

View File

@ -1,3 +1,4 @@
#pragma BLENDER_REQUIRE(gpu_shader_bicubic_sampler_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
void main()
@ -25,5 +26,5 @@ void main()
* the sampler's expected [0, 1] range. */
vec2 normalized_coordinates = (coordinates - offset) / vec2(input_size);
imageStore(domain_img, texel, texture(input_tx, normalized_coordinates));
imageStore(domain_img, texel, SAMPLER_FUNCTION(input_tx, normalized_coordinates));
}

View File

@ -8,17 +8,40 @@ GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_shared)
.sampler(0, ImageType::FLOAT_2D, "input_tx")
.compute_source("compositor_realize_on_domain.glsl");
GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_color)
GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_standard_shared)
.additional_info("compositor_realize_on_domain_shared")
.define("SAMPLER_FUNCTION", "texture");
GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_bicubic_shared)
.additional_info("compositor_realize_on_domain_shared")
.define("SAMPLER_FUNCTION", "texture_bicubic");
GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_color)
.additional_info("compositor_realize_on_domain_standard_shared")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "domain_img")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_vector)
.additional_info("compositor_realize_on_domain_shared")
.additional_info("compositor_realize_on_domain_standard_shared")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "domain_img")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_float)
.additional_info("compositor_realize_on_domain_shared")
.additional_info("compositor_realize_on_domain_standard_shared")
.image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "domain_img")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_bicubic_color)
.additional_info("compositor_realize_on_domain_bicubic_shared")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "domain_img")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_bicubic_vector)
.additional_info("compositor_realize_on_domain_bicubic_shared")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "domain_img")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_bicubic_float)
.additional_info("compositor_realize_on_domain_bicubic_shared")
.image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "domain_img")
.do_static_compilation(true);

View File

@ -467,6 +467,7 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_sampling_lib.glsl
engines/eevee_next/shaders/eevee_shadow_debug_frag.glsl
engines/eevee_next/shaders/eevee_shadow_lib.glsl
engines/eevee_next/shaders/eevee_shadow_clipmap_clear_comp.glsl
engines/eevee_next/shaders/eevee_shadow_page_allocate_comp.glsl
engines/eevee_next/shaders/eevee_shadow_page_clear_comp.glsl
engines/eevee_next/shaders/eevee_shadow_page_defrag_comp.glsl

View File

@ -54,6 +54,7 @@
#define SHADOW_PAGE_PER_ROW 64
#define SHADOW_ATLAS_SLOT 5
#define SHADOW_BOUNDS_GROUP_SIZE 64
#define SHADOW_CLIPMAP_GROUP_SIZE 64
#define SHADOW_VIEW_MAX 64 /* Must match DRW_VIEW_MAX. */
/* Ray-tracing. */

View File

@ -233,7 +233,7 @@ void MotionBlurModule::render(View &view, GPUTexture **input_tx, GPUTexture **ou
tiles_tx_.acquire(tiles_extent, GPU_RGBA16F);
GPU_storagebuf_clear_to_zero(tile_indirection_buf_);
tile_indirection_buf_.clear_to_zero();
inst_.manager->submit(motion_blur_ps_, view);

View File

@ -142,6 +142,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
return "eevee_light_culling_tile";
case LIGHT_CULLING_ZBIN:
return "eevee_light_culling_zbin";
case SHADOW_CLIPMAP_CLEAR:
return "eevee_shadow_clipmap_clear";
case SHADOW_DEBUG:
return "eevee_shadow_debug";
case SHADOW_PAGE_ALLOCATE:

View File

@ -62,6 +62,7 @@ enum eShaderType {
MOTION_BLUR_TILE_FLATTEN_RENDER,
MOTION_BLUR_TILE_FLATTEN_VIEWPORT,
SHADOW_CLIPMAP_CLEAR,
SHADOW_DEBUG,
SHADOW_PAGE_ALLOCATE,
SHADOW_PAGE_CLEAR,

View File

@ -831,20 +831,8 @@ void ShadowModule::end_sync()
/* Clear tiles to not reference any page. */
tilemap_pool.tiles_data.clear_to_zero();
/* Clear tile-map clip buffer. */
union {
ShadowTileMapClip clip;
int4 i;
} u;
u.clip.clip_near_stored = 0.0f;
u.clip.clip_far_stored = 0.0f;
u.clip.clip_near = int(0xFF7FFFFFu ^ 0x7FFFFFFFu); /* floatBitsToOrderedInt(-FLT_MAX) */
u.clip.clip_far = 0x7F7FFFFF; /* floatBitsToOrderedInt(FLT_MAX) */
GPU_storagebuf_clear_int(tilemap_pool.tilemaps_clip, u.i, 4);
/* Clear cached page buffer. */
int2 data = {-1, -1};
GPU_storagebuf_clear_int(pages_cached_data_, data, 2);
GPU_storagebuf_clear(pages_cached_data_, -1);
/* Reset info to match new state. */
pages_infos_data_.page_free_count = shadow_page_len_;
@ -863,6 +851,16 @@ void ShadowModule::end_sync()
PassSimple &pass = tilemap_setup_ps_;
pass.init();
{
/** Clear tile-map clip buffer. */
PassSimple::Sub &sub = pass.sub("ClearClipmap");
sub.shader_set(inst_.shaders.static_shader_get(SHADOW_CLIPMAP_CLEAR));
sub.bind_ssbo("tilemaps_clip_buf", tilemap_pool.tilemaps_clip);
sub.dispatch(int3(
divide_ceil_u(tilemap_pool.tilemaps_clip.size(), SHADOW_CLIPMAP_GROUP_SIZE), 1, 1));
sub.barrier(GPU_BARRIER_SHADER_STORAGE);
}
{
/** Compute near/far clip distances for directional shadows based on casters bounds. */
PassSimple::Sub &sub = pass.sub("DirectionalBounds");

View File

@ -0,0 +1,11 @@
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
void main()
{
int index = int(gl_GlobalInvocationID.x);
tilemaps_clip_buf[index].clip_near_stored = 0;
tilemaps_clip_buf[index].clip_far_stored = 0;
tilemaps_clip_buf[index].clip_near = floatBitsToOrderedInt(-FLT_MAX);
tilemaps_clip_buf[index].clip_far = floatBitsToOrderedInt(FLT_MAX);
}

View File

@ -8,6 +8,13 @@
/** \name Shadow pipeline
* \{ */
GPU_SHADER_CREATE_INFO(eevee_shadow_clipmap_clear)
.do_static_compilation(true)
.local_group_size(SHADOW_CLIPMAP_GROUP_SIZE)
.storage_buf(0, Qualifier::WRITE, "ShadowTileMapClip", "tilemaps_clip_buf[]")
.additional_info("eevee_shared")
.compute_source("eevee_shadow_clipmap_clear_comp.glsl");
GPU_SHADER_CREATE_INFO(eevee_shadow_tilemap_bounds)
.do_static_compilation(true)
.local_group_size(SHADOW_BOUNDS_GROUP_SIZE)

View File

@ -18,6 +18,7 @@
#include "DRW_render.h"
#include "BLI_listbase_wrapper.hh"
#include "BLI_math.h"
#include "BLI_utildefines.h"
@ -46,6 +47,8 @@
#define PT_DEFAULT_RAD 0.05f /* radius of the point batch. */
using namespace blender;
struct ArmatureDrawContext {
/* Current armature object */
Object *ob;
@ -2355,7 +2358,7 @@ static void draw_armature_pose(ArmatureDrawContext *ctx)
const Object *obact_orig = DEG_get_original_object(draw_ctx->obact);
const ListBase *defbase = BKE_object_defgroup_list(obact_orig);
LISTBASE_FOREACH (const bDeformGroup *, dg, defbase) {
for (const bDeformGroup *dg : ConstListBaseWrapper<bDeformGroup>(defbase)) {
if (dg->flag & DG_LOCK_WEIGHT) {
pchan = BKE_pose_channel_find_name(ob->pose, dg->name);
@ -2437,7 +2440,7 @@ static void draw_armature_pose(ArmatureDrawContext *ctx)
draw_bone_box(ctx, nullptr, pchan, arm, boneflag, constflag, select_id);
}
}
else {
else if (arm->drawtype == ARM_OCTA) {
draw_bone_update_disp_matrix_default(nullptr, pchan);
if (!is_pose_select || pchan_culling_test_octohedral(view, ob, pchan)) {
draw_bone_octahedral(ctx, nullptr, pchan, arm, boneflag, constflag, select_id);

View File

@ -123,6 +123,9 @@ void OVERLAY_extra_cache_init(OVERLAY_Data *vedata)
cb->field_tube_limit = BUF_INSTANCE(grp_sub, format, DRW_cache_field_tube_limit_get());
cb->field_vortex = BUF_INSTANCE(grp_sub, format, DRW_cache_field_vortex_get());
cb->field_wind = BUF_INSTANCE(grp_sub, format, DRW_cache_field_wind_get());
cb->light_icon_inner = BUF_INSTANCE(grp_sub, format, DRW_cache_light_icon_inner_lines_get());
cb->light_icon_outer = BUF_INSTANCE(grp_sub, format, DRW_cache_light_icon_outer_lines_get());
cb->light_icon_sun_rays = BUF_INSTANCE(grp_sub, format, DRW_cache_light_icon_sun_rays_get());
cb->light_area[0] = BUF_INSTANCE(grp_sub, format, DRW_cache_light_area_disk_lines_get());
cb->light_area[1] = BUF_INSTANCE(grp_sub, format, DRW_cache_light_area_square_lines_get());
cb->light_point = BUF_INSTANCE(grp_sub, format, DRW_cache_light_point_lines_get());
@ -605,8 +608,9 @@ void OVERLAY_light_cache_populate(OVERLAY_Data *vedata, Object *ob)
Light *la = static_cast<Light *>(ob->data);
float *color_p;
DRW_object_wire_theme_get(ob, view_layer, &color_p);
/* Remove the alpha. */
float color[4] = {UNPACK3(color_p), 1.0f};
float theme_color[4] = {UNPACK3(color_p), 1.0f};
/* Pack render data into object matrix. */
union {
float mat[4][4];
@ -636,12 +640,25 @@ void OVERLAY_light_cache_populate(OVERLAY_Data *vedata, Object *ob)
DRW_buffer_add_entry(cb->groundline, instdata.pos);
float light_color[4] = {1.0f};
const bool show_light_colors = vedata->stl->pd->overlay.flag & V3D_OVERLAY_SHOW_LIGHT_COLORS;
if (show_light_colors) {
copy_v3_v3(light_color, &la->r);
}
/* Draw the outer ring of the light icon and the sun rays in `light_color`, if required. */
DRW_buffer_add_entry(
cb->light_icon_outer, show_light_colors ? light_color : theme_color, &instdata);
DRW_buffer_add_entry(cb->light_icon_inner, theme_color, &instdata);
if (la->type == LA_LOCAL) {
instdata.area_size_x = instdata.area_size_y = la->radius;
DRW_buffer_add_entry(cb->light_point, color, &instdata);
DRW_buffer_add_entry(cb->light_point, theme_color, &instdata);
}
else if (la->type == LA_SUN) {
DRW_buffer_add_entry(cb->light_sun, color, &instdata);
DRW_buffer_add_entry(cb->light_sun, theme_color, &instdata);
DRW_buffer_add_entry(
cb->light_icon_sun_rays, show_light_colors ? light_color : theme_color, &instdata);
}
else if (la->type == LA_SPOT) {
/* Previous implementation was using the clip-end distance as cone size.
@ -664,8 +681,8 @@ void OVERLAY_light_cache_populate(OVERLAY_Data *vedata, Object *ob)
instdata.spot_blend = sqrtf((a2 - a2 * c2) / (c2 - a2 * c2));
instdata.spot_cosine = a;
/* HACK: We pack the area size in alpha color. This is decoded by the shader. */
color[3] = -max_ff(la->radius, FLT_MIN);
DRW_buffer_add_entry(cb->light_spot, color, &instdata);
theme_color[3] = -max_ff(la->radius, FLT_MIN);
DRW_buffer_add_entry(cb->light_spot, theme_color, &instdata);
if ((la->mode & LA_SHOW_CONE) && !DRW_state_is_select()) {
const float color_inside[4] = {0.0f, 0.0f, 0.0f, 0.5f};
@ -679,7 +696,7 @@ void OVERLAY_light_cache_populate(OVERLAY_Data *vedata, Object *ob)
int sqr = ELEM(la->area_shape, LA_AREA_SQUARE, LA_AREA_RECT);
instdata.area_size_x = la->area_size;
instdata.area_size_y = uniform_scale ? la->area_size : la->area_sizey;
DRW_buffer_add_entry(cb->light_area[sqr], color, &instdata);
DRW_buffer_add_entry(cb->light_area[sqr], theme_color, &instdata);
}
}

View File

@ -178,6 +178,9 @@ typedef struct OVERLAY_ExtraCallBuffers {
DRWCallBuffer *groundline;
DRWCallBuffer *light_icon_inner;
DRWCallBuffer *light_icon_outer;
DRWCallBuffer *light_icon_sun_rays;
DRWCallBuffer *light_point;
DRWCallBuffer *light_sun;
DRWCallBuffer *light_spot;

View File

@ -220,14 +220,14 @@ void ShadowPass::ShadowView::compute_visibility(ObjectBoundsBuf &bounds,
uint words_len = (view_len_ == 1) ? divide_ceil_u(resource_len, 32) :
resource_len * word_per_draw;
words_len = ceil_to_multiple_u(max_ii(1, words_len), 4);
uint32_t data = 0xFFFFFFFFu;
const uint32_t data = 0xFFFFFFFFu;
if (current_pass_type_ == ShadowPass::PASS) {
/* TODO(fclem): Resize to nearest pow2 to reduce fragmentation. */
pass_visibility_buf_.resize(words_len);
GPU_storagebuf_clear_uint(pass_visibility_buf_, &data, 1);
GPU_storagebuf_clear(pass_visibility_buf_, data);
fail_visibility_buf_.resize(words_len);
GPU_storagebuf_clear_uint(fail_visibility_buf_, &data, 1);
GPU_storagebuf_clear(fail_visibility_buf_, data);
}
else if (current_pass_type_ == ShadowPass::FAIL) {
/* Already computed in the ShadowPass::PASS */
@ -236,7 +236,7 @@ void ShadowPass::ShadowView::compute_visibility(ObjectBoundsBuf &bounds,
}
else {
visibility_buf_.resize(words_len);
GPU_storagebuf_clear_uint(visibility_buf_, &data, 1);
GPU_storagebuf_clear(visibility_buf_, data);
}
if (do_visibility_) {

View File

@ -116,6 +116,9 @@ static struct DRWShapeCache {
GPUBatch *drw_field_cone_limit;
GPUBatch *drw_field_sphere_limit;
GPUBatch *drw_ground_line;
GPUBatch *drw_light_icon_inner_lines;
GPUBatch *drw_light_icon_outer_lines;
GPUBatch *drw_light_icon_sun_rays;
GPUBatch *drw_light_point_lines;
GPUBatch *drw_light_sun_lines;
GPUBatch *drw_light_spot_lines;
@ -1461,21 +1464,90 @@ GPUBatch *DRW_cache_groundline_get(void)
return SHC.drw_ground_line;
}
GPUBatch *DRW_cache_light_point_lines_get(void)
GPUBatch *DRW_cache_light_icon_inner_lines_get(void)
{
if (!SHC.drw_light_point_lines) {
if (!SHC.drw_light_icon_inner_lines) {
GPUVertFormat format = extra_vert_format();
int v_len = 2 * (DIAMOND_NSEGMENTS + INNER_NSEGMENTS + OUTER_NSEGMENTS + CIRCLE_NSEGMENTS);
int v_len = 2 * (DIAMOND_NSEGMENTS + INNER_NSEGMENTS);
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
GPU_vertbuf_data_alloc(vbo, v_len);
const float r = 9.0f;
int v = 0;
/* Light Icon */
circle_verts(vbo, &v, DIAMOND_NSEGMENTS, r * 0.3f, 0.0f, VCLASS_SCREENSPACE);
circle_dashed_verts(vbo, &v, INNER_NSEGMENTS, r * 1.0f, 0.0f, VCLASS_SCREENSPACE);
SHC.drw_light_icon_inner_lines = GPU_batch_create_ex(
GPU_PRIM_LINES, vbo, NULL, GPU_BATCH_OWNS_VBO);
}
return SHC.drw_light_icon_inner_lines;
}
GPUBatch *DRW_cache_light_icon_outer_lines_get(void)
{
if (!SHC.drw_light_icon_outer_lines) {
GPUVertFormat format = extra_vert_format();
int v_len = 2 * OUTER_NSEGMENTS;
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
GPU_vertbuf_data_alloc(vbo, v_len);
const float r = 9.0f;
int v = 0;
circle_dashed_verts(vbo, &v, OUTER_NSEGMENTS, r * 1.33f, 0.0f, VCLASS_SCREENSPACE);
SHC.drw_light_icon_outer_lines = GPU_batch_create_ex(
GPU_PRIM_LINES, vbo, NULL, GPU_BATCH_OWNS_VBO);
}
return SHC.drw_light_icon_outer_lines;
}
GPUBatch *DRW_cache_light_icon_sun_rays_get(void)
{
if (!SHC.drw_light_icon_sun_rays) {
GPUVertFormat format = extra_vert_format();
const int num_rays = 8;
int v_len = 4 * num_rays;
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
GPU_vertbuf_data_alloc(vbo, v_len);
const float r = 9.0f;
int v = 0;
/* Sun Rays */
for (int a = 0; a < num_rays; a++) {
float angle = (2.0f * M_PI * a) / (float)num_rays;
float s = sinf(angle) * r;
float c = cosf(angle) * r;
GPU_vertbuf_vert_set(vbo, v++, &(Vert){{s * 1.6f, c * 1.6f, 0.0f}, VCLASS_SCREENSPACE});
GPU_vertbuf_vert_set(vbo, v++, &(Vert){{s * 1.9f, c * 1.9f, 0.0f}, VCLASS_SCREENSPACE});
GPU_vertbuf_vert_set(vbo, v++, &(Vert){{s * 2.2f, c * 2.2f, 0.0f}, VCLASS_SCREENSPACE});
GPU_vertbuf_vert_set(vbo, v++, &(Vert){{s * 2.5f, c * 2.5f, 0.0f}, VCLASS_SCREENSPACE});
}
SHC.drw_light_icon_sun_rays = GPU_batch_create_ex(
GPU_PRIM_LINES, vbo, NULL, GPU_BATCH_OWNS_VBO);
}
return SHC.drw_light_icon_sun_rays;
}
GPUBatch *DRW_cache_light_point_lines_get(void)
{
if (!SHC.drw_light_point_lines) {
GPUVertFormat format = extra_vert_format();
int v_len = 2 * CIRCLE_NSEGMENTS;
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
GPU_vertbuf_data_alloc(vbo, v_len);
int v = 0;
/* Light area */
int flag = VCLASS_SCREENALIGNED | VCLASS_LIGHT_AREA_SHAPE;
circle_verts(vbo, &v, CIRCLE_NSEGMENTS, 1.0f, 0.0f, flag);
@ -1490,26 +1562,12 @@ GPUBatch *DRW_cache_light_sun_lines_get(void)
if (!SHC.drw_light_sun_lines) {
GPUVertFormat format = extra_vert_format();
int v_len = 2 * (DIAMOND_NSEGMENTS + INNER_NSEGMENTS + OUTER_NSEGMENTS + 8 * 2 + 1);
int v_len = 2;
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
GPU_vertbuf_data_alloc(vbo, v_len);
const float r = 9.0f;
int v = 0;
/* Light Icon */
circle_verts(vbo, &v, DIAMOND_NSEGMENTS, r * 0.3f, 0.0f, VCLASS_SCREENSPACE);
circle_dashed_verts(vbo, &v, INNER_NSEGMENTS, r * 1.0f, 0.0f, VCLASS_SCREENSPACE);
circle_dashed_verts(vbo, &v, OUTER_NSEGMENTS, r * 1.33f, 0.0f, VCLASS_SCREENSPACE);
/* Sun Rays */
for (int a = 0; a < 8; a++) {
float angle = (2.0f * M_PI * a) / 8.0f;
float s = sinf(angle) * r;
float c = cosf(angle) * r;
GPU_vertbuf_vert_set(vbo, v++, &(Vert){{s * 1.6f, c * 1.6f, 0.0f}, VCLASS_SCREENSPACE});
GPU_vertbuf_vert_set(vbo, v++, &(Vert){{s * 1.9f, c * 1.9f, 0.0f}, VCLASS_SCREENSPACE});
GPU_vertbuf_vert_set(vbo, v++, &(Vert){{s * 2.2f, c * 2.2f, 0.0f}, VCLASS_SCREENSPACE});
GPU_vertbuf_vert_set(vbo, v++, &(Vert){{s * 2.5f, c * 2.5f, 0.0f}, VCLASS_SCREENSPACE});
}
/* Direction Line */
GPU_vertbuf_vert_set(vbo, v++, &(Vert){{0.0, 0.0, 0.0}, 0});
GPU_vertbuf_vert_set(vbo, v++, &(Vert){{0.0, 0.0, -20.0}, 0}); /* Good default. */
@ -1524,17 +1582,12 @@ GPUBatch *DRW_cache_light_spot_lines_get(void)
if (!SHC.drw_light_spot_lines) {
GPUVertFormat format = extra_vert_format();
int v_len = 2 * (DIAMOND_NSEGMENTS * 3 + INNER_NSEGMENTS + OUTER_NSEGMENTS +
CIRCLE_NSEGMENTS * 4 + 1);
int v_len = 2 * (DIAMOND_NSEGMENTS * 2 + CIRCLE_NSEGMENTS * 4 + 1);
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
GPU_vertbuf_data_alloc(vbo, v_len);
const float r = 9.0f;
int v = 0;
/* Light Icon */
circle_verts(vbo, &v, DIAMOND_NSEGMENTS, r * 0.3f, 0.0f, VCLASS_SCREENSPACE);
circle_dashed_verts(vbo, &v, INNER_NSEGMENTS, r * 1.0f, 0.0f, VCLASS_SCREENSPACE);
circle_dashed_verts(vbo, &v, OUTER_NSEGMENTS, r * 1.33f, 0.0f, VCLASS_SCREENSPACE);
/* Light area */
int flag = VCLASS_SCREENALIGNED | VCLASS_LIGHT_AREA_SHAPE;
circle_verts(vbo, &v, CIRCLE_NSEGMENTS, 1.0f, 0.0f, flag);
@ -1597,17 +1650,12 @@ GPUBatch *DRW_cache_light_area_disk_lines_get(void)
if (!SHC.drw_light_area_disk_lines) {
GPUVertFormat format = extra_vert_format();
int v_len = 2 *
(DIAMOND_NSEGMENTS * 3 + INNER_NSEGMENTS + OUTER_NSEGMENTS + CIRCLE_NSEGMENTS + 1);
int v_len = 2 * (DIAMOND_NSEGMENTS * 2 + CIRCLE_NSEGMENTS + 1);
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
GPU_vertbuf_data_alloc(vbo, v_len);
const float r = 9.0f;
int v = 0;
/* Light Icon */
circle_verts(vbo, &v, DIAMOND_NSEGMENTS, r * 0.3f, 0.0f, VCLASS_SCREENSPACE);
circle_dashed_verts(vbo, &v, INNER_NSEGMENTS, r * 1.0f, 0.0f, VCLASS_SCREENSPACE);
circle_dashed_verts(vbo, &v, OUTER_NSEGMENTS, r * 1.33f, 0.0f, VCLASS_SCREENSPACE);
/* Light area */
circle_verts(vbo, &v, CIRCLE_NSEGMENTS, 0.5f, 0.0f, VCLASS_LIGHT_AREA_SHAPE);
/* Direction Line */
@ -1630,15 +1678,11 @@ GPUBatch *DRW_cache_light_area_square_lines_get(void)
GPUVertFormat format = extra_vert_format();
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
int v_len = 2 * (DIAMOND_NSEGMENTS * 3 + INNER_NSEGMENTS + OUTER_NSEGMENTS + 4 + 1);
int v_len = 2 * (DIAMOND_NSEGMENTS * 2 + 4 + 1);
GPU_vertbuf_data_alloc(vbo, v_len);
const float r = 9.0f;
int v = 0;
/* Light Icon */
circle_verts(vbo, &v, DIAMOND_NSEGMENTS, r * 0.3f, 0.0f, VCLASS_SCREENSPACE);
circle_dashed_verts(vbo, &v, INNER_NSEGMENTS, r * 1.0f, 0.0f, VCLASS_SCREENSPACE);
circle_dashed_verts(vbo, &v, OUTER_NSEGMENTS, r * 1.33f, 0.0f, VCLASS_SCREENSPACE);
/* Light area */
int flag = VCLASS_LIGHT_AREA_SHAPE;
for (int a = 0; a < 4; a++) {

View File

@ -106,6 +106,9 @@ struct GPUBatch *DRW_cache_field_sphere_limit_get(void);
/* Lights */
struct GPUBatch *DRW_cache_light_icon_inner_lines_get(void);
struct GPUBatch *DRW_cache_light_icon_outer_lines_get(void);
struct GPUBatch *DRW_cache_light_icon_sun_rays_get(void);
struct GPUBatch *DRW_cache_light_point_lines_get(void);
struct GPUBatch *DRW_cache_light_sun_lines_get(void);
struct GPUBatch *DRW_cache_light_spot_lines_get(void);

View File

@ -558,21 +558,21 @@ MeshRenderData *mesh_render_data_create(Object *object,
mr->p_origindex = static_cast<const int *>(CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX));
mr->material_indices = static_cast<const int *>(
CustomData_get_layer_named(&me->pdata, CD_PROP_INT32, "material_index"));
CustomData_get_layer_named(&mr->me->pdata, CD_PROP_INT32, "material_index"));
mr->hide_vert = static_cast<const bool *>(
CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_vert"));
CustomData_get_layer_named(&mr->me->vdata, CD_PROP_BOOL, ".hide_vert"));
mr->hide_edge = static_cast<const bool *>(
CustomData_get_layer_named(&me->edata, CD_PROP_BOOL, ".hide_edge"));
CustomData_get_layer_named(&mr->me->edata, CD_PROP_BOOL, ".hide_edge"));
mr->hide_poly = static_cast<const bool *>(
CustomData_get_layer_named(&me->pdata, CD_PROP_BOOL, ".hide_poly"));
CustomData_get_layer_named(&mr->me->pdata, CD_PROP_BOOL, ".hide_poly"));
mr->select_vert = static_cast<const bool *>(
CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".select_vert"));
CustomData_get_layer_named(&mr->me->vdata, CD_PROP_BOOL, ".select_vert"));
mr->select_edge = static_cast<const bool *>(
CustomData_get_layer_named(&me->edata, CD_PROP_BOOL, ".select_edge"));
CustomData_get_layer_named(&mr->me->edata, CD_PROP_BOOL, ".select_edge"));
mr->select_poly = static_cast<const bool *>(
CustomData_get_layer_named(&me->pdata, CD_PROP_BOOL, ".select_poly"));
CustomData_get_layer_named(&mr->me->pdata, CD_PROP_BOOL, ".select_poly"));
}
else {
/* #BMesh */
@ -585,7 +585,7 @@ MeshRenderData *mesh_render_data_create(Object *object,
mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len);
}
retrieve_active_attribute_names(*mr, *object, *me);
retrieve_active_attribute_names(*mr, *object, *mr->me);
return mr;
}

View File

@ -280,8 +280,8 @@ void View::compute_visibility(ObjectBoundsBuf &bounds, uint resource_len, bool d
/* TODO(fclem): Resize to nearest pow2 to reduce fragmentation. */
visibility_buf_.resize(words_len);
uint32_t data = 0xFFFFFFFFu;
GPU_storagebuf_clear_uint(visibility_buf_, &data, 1);
const uint32_t data = 0xFFFFFFFFu;
GPU_storagebuf_clear(visibility_buf_, data);
if (do_visibility_) {
GPUShader *shader = DRW_shader_draw_visibility_compute_get();

View File

@ -3754,16 +3754,6 @@ static void add_region_padding(bContext *C, bAnimContext *ac, rctf *bounds)
BLI_rctf_pad_y(bounds, ac->region->winy, pad_bottom, pad_top);
}
static ARegion *get_window_region(bAnimContext *ac)
{
LISTBASE_FOREACH (ARegion *, region, &ac->area->regionbase) {
if (region->regiontype == RGN_TYPE_WINDOW) {
return region;
}
}
return NULL;
}
static int graphkeys_view_selected_channels_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
@ -3772,8 +3762,7 @@ static int graphkeys_view_selected_channels_exec(bContext *C, wmOperator *op)
if (ANIM_animdata_get_context(C, &ac) == 0) {
return OPERATOR_CANCELLED;
}
ARegion *window_region = get_window_region(&ac);
ARegion *window_region = BKE_area_find_region_type(ac.area, RGN_TYPE_WINDOW);
if (!window_region) {
return OPERATOR_CANCELLED;
@ -3869,7 +3858,7 @@ static int graphkeys_channel_view_pick_invoke(bContext *C, wmOperator *op, const
return OPERATOR_CANCELLED;
}
ARegion *window_region = get_window_region(&ac);
ARegion *window_region = BKE_area_find_region_type(ac.area, RGN_TYPE_WINDOW);
if (!window_region) {
return OPERATOR_CANCELLED;

View File

@ -56,13 +56,16 @@ using namespace blender::bke::idprop;
* "catalog_name": "<catalog_name>",
* "description": "<description>",
* "author": "<author>",
* "copyright": "<copyright>",
* "license": "<license>",
* "tags": ["<tag>"],
* "properties": [..]
* }]
* }
* \endcode
*
* NOTE: entries, author, description, tags and properties are optional attributes.
* NOTE: entries, author, description, copyright, license, tags and properties are optional
* attributes.
*
* NOTE: File browser uses name and idcode separate. Inside the index they are joined together like
* #ID.name.
@ -75,6 +78,8 @@ constexpr StringRef ATTRIBUTE_ENTRIES_CATALOG_ID("catalog_id");
constexpr StringRef ATTRIBUTE_ENTRIES_CATALOG_NAME("catalog_name");
constexpr StringRef ATTRIBUTE_ENTRIES_DESCRIPTION("description");
constexpr StringRef ATTRIBUTE_ENTRIES_AUTHOR("author");
constexpr StringRef ATTRIBUTE_ENTRIES_COPYRIGHT("copyright");
constexpr StringRef ATTRIBUTE_ENTRIES_LICENSE("license");
constexpr StringRef ATTRIBUTE_ENTRIES_TAGS("tags");
constexpr StringRef ATTRIBUTE_ENTRIES_PROPERTIES("properties");
@ -178,6 +183,26 @@ struct AssetEntryReader {
return lookup.lookup(ATTRIBUTE_ENTRIES_AUTHOR)->as_string_value()->value();
}
bool has_copyright() const
{
return lookup.contains(ATTRIBUTE_ENTRIES_COPYRIGHT);
}
StringRefNull get_copyright() const
{
return lookup.lookup(ATTRIBUTE_ENTRIES_COPYRIGHT)->as_string_value()->value();
}
bool has_license() const
{
return lookup.contains(ATTRIBUTE_ENTRIES_LICENSE);
}
StringRefNull get_license() const
{
return lookup.lookup(ATTRIBUTE_ENTRIES_LICENSE)->as_string_value()->value();
}
StringRefNull get_catalog_name() const
{
return lookup.lookup(ATTRIBUTE_ENTRIES_CATALOG_NAME)->as_string_value()->value();
@ -267,6 +292,16 @@ struct AssetEntryWriter {
attributes.append_as(std::pair(ATTRIBUTE_ENTRIES_AUTHOR, new StringValue(author)));
}
void add_copyright(const StringRefNull copyright)
{
attributes.append_as(std::pair(ATTRIBUTE_ENTRIES_COPYRIGHT, new StringValue(copyright)));
}
void add_license(const StringRefNull license)
{
attributes.append_as(std::pair(ATTRIBUTE_ENTRIES_LICENSE, new StringValue(license)));
}
void add_tags(const ListBase /* AssetTag */ *asset_tags)
{
ArrayValue *tags = new ArrayValue();
@ -305,6 +340,12 @@ static void init_value_from_file_indexer_entry(AssetEntryWriter &result,
if (asset_data.author != nullptr) {
result.add_author(asset_data.author);
}
if (asset_data.copyright != nullptr) {
result.add_copyright(asset_data.copyright);
}
if (asset_data.license != nullptr) {
result.add_license(asset_data.license);
}
if (!BLI_listbase_is_empty(&asset_data.tags)) {
result.add_tags(&asset_data.tags);
@ -372,6 +413,18 @@ static void init_indexer_entry_from_value(FileIndexerEntry &indexer_entry,
BLI_strncpy(author_c_str, author.c_str(), author.size() + 1);
asset_data->author = author_c_str;
}
if (entry.has_copyright()) {
const StringRefNull copyright = entry.get_copyright();
char *copyright_c_str = static_cast<char *>(MEM_mallocN(copyright.size() + 1, __func__));
BLI_strncpy(copyright_c_str, copyright.c_str(), copyright.size() + 1);
asset_data->copyright = copyright_c_str;
}
if (entry.has_license()) {
const StringRefNull license = entry.get_license();
char *license_c_str = static_cast<char *>(MEM_mallocN(license.size() + 1, __func__));
BLI_strncpy(license_c_str, license.c_str(), license.size() + 1);
asset_data->license = license_c_str;
}
const StringRefNull catalog_name = entry.get_catalog_name();
BLI_strncpy(asset_data->catalog_simple_name,

View File

@ -167,7 +167,7 @@ bool has_anything_selected(const VArray<bool> &varray, const IndexRange range_to
bool has_anything_selected(const bke::CurvesGeometry &curves)
{
const VArray<bool> selection = curves.attributes().lookup<bool>(".selection");
return !selection || contains(selection, curves.curves_range(), true);
return !selection || contains(selection, selection.index_range(), true);
}
bool has_anything_selected(const GSpan selection)
@ -400,9 +400,9 @@ void select_random(bke::CurvesGeometry &curves,
selection.finish();
}
static void apply_selection_operation_at_index(GMutableSpan selection,
const int index,
const eSelectOp sel_op)
void apply_selection_operation_at_index(GMutableSpan selection,
const int index,
const eSelectOp sel_op)
{
if (selection.type().is<bool>()) {
MutableSpan<bool> selection_typed = selection.typed<bool>();
@ -440,14 +440,15 @@ static void apply_selection_operation_at_index(GMutableSpan selection,
}
}
static bool find_closest_point_to_screen_co(const Depsgraph &depsgraph,
const ARegion *region,
const RegionView3D *rv3d,
const Object &object,
const bke::CurvesGeometry &curves,
float2 mouse_pos,
float radius,
FindClosestData &closest_data)
static std::optional<FindClosestData> find_closest_point_to_screen_co(
const Depsgraph &depsgraph,
const ARegion *region,
const RegionView3D *rv3d,
const Object &object,
const bke::CurvesGeometry &curves,
float2 mouse_pos,
float radius,
const FindClosestData &initial_closest)
{
float4x4 projection;
ED_view3d_ob_project_mat_get(rv3d, &object, projection.ptr());
@ -456,10 +457,10 @@ static bool find_closest_point_to_screen_co(const Depsgraph &depsgraph,
bke::crazyspace::get_evaluated_curves_deformation(depsgraph, object);
const float radius_sq = pow2f(radius);
closest_data = threading::parallel_reduce(
const FindClosestData new_closest_data = threading::parallel_reduce(
curves.points_range(),
1024,
closest_data,
initial_closest,
[&](const IndexRange point_range, const FindClosestData &init) {
FindClosestData best_match = init;
for (const int point_i : point_range) {
@ -490,21 +491,23 @@ static bool find_closest_point_to_screen_co(const Depsgraph &depsgraph,
}
return b;
});
if (closest_data.index >= 0) {
return true;
if (new_closest_data.distance < initial_closest.distance) {
return new_closest_data;
}
return false;
return {};
}
static bool find_closest_curve_to_screen_co(const Depsgraph &depsgraph,
const ARegion *region,
const RegionView3D *rv3d,
const Object &object,
const bke::CurvesGeometry &curves,
float2 mouse_pos,
float radius,
FindClosestData &closest_data)
static std::optional<FindClosestData> find_closest_curve_to_screen_co(
const Depsgraph &depsgraph,
const ARegion *region,
const RegionView3D *rv3d,
const Object &object,
const bke::CurvesGeometry &curves,
float2 mouse_pos,
float radius,
const FindClosestData &initial_closest)
{
float4x4 projection;
ED_view3d_ob_project_mat_get(rv3d, &object, projection.ptr());
@ -515,10 +518,10 @@ static bool find_closest_curve_to_screen_co(const Depsgraph &depsgraph,
const float radius_sq = pow2f(radius);
const OffsetIndices points_by_curve = curves.points_by_curve();
closest_data = threading::parallel_reduce(
const FindClosestData new_closest_data = threading::parallel_reduce(
curves.curves_range(),
256,
closest_data,
initial_closest,
[&](const IndexRange curves_range, const FindClosestData &init) {
FindClosestData best_match = init;
for (const int curve_i : curves_range) {
@ -578,64 +581,44 @@ static bool find_closest_curve_to_screen_co(const Depsgraph &depsgraph,
return b;
});
if (closest_data.index >= 0) {
return true;
if (new_closest_data.distance < initial_closest.distance) {
return new_closest_data;
}
return false;
return {};
}
bool select_pick(const ViewContext &vc,
const Object &object,
bke::CurvesGeometry &curves,
const eAttrDomain selection_domain,
const SelectPick_Params &params,
const int2 coord,
FindClosestData initial)
std::optional<FindClosestData> closest_elem_find_screen_space(
const ViewContext &vc,
const Object &object,
bke::CurvesGeometry &curves,
const eAttrDomain domain,
const int2 coord,
const FindClosestData &initial_closest)
{
FindClosestData closest = initial;
bool found = false;
if (selection_domain == ATTR_DOMAIN_POINT) {
found = find_closest_point_to_screen_co(*vc.depsgraph,
vc.region,
vc.rv3d,
object,
curves,
float2(coord),
ED_view3d_select_dist_px(),
closest);
switch (domain) {
case ATTR_DOMAIN_POINT:
return find_closest_point_to_screen_co(*vc.depsgraph,
vc.region,
vc.rv3d,
object,
curves,
float2(coord),
ED_view3d_select_dist_px(),
initial_closest);
case ATTR_DOMAIN_CURVE:
return find_closest_curve_to_screen_co(*vc.depsgraph,
vc.region,
vc.rv3d,
object,
curves,
float2(coord),
ED_view3d_select_dist_px(),
initial_closest);
default:
BLI_assert_unreachable();
return {};
}
else if (selection_domain == ATTR_DOMAIN_CURVE) {
found = find_closest_curve_to_screen_co(*vc.depsgraph,
vc.region,
vc.rv3d,
object,
curves,
float2(coord),
ED_view3d_select_dist_px(),
closest);
}
bool deselected = false;
if (params.sel_op == SEL_OP_SET) {
bke::GSpanAttributeWriter selection = ensure_selection_attribute(
curves, selection_domain, CD_PROP_BOOL);
if (found || ((params.deselect_all && has_anything_selected(selection.span)))) {
fill_selection_false(selection.span);
deselected = true;
}
selection.finish();
}
if (found) {
bke::GSpanAttributeWriter selection = ensure_selection_attribute(
curves, selection_domain, CD_PROP_BOOL);
apply_selection_operation_at_index(selection.span, closest.index, params.sel_op);
selection.finish();
}
return deselected || found;
}
bool select_box(const ViewContext &vc,

View File

@ -580,12 +580,19 @@ static void cage2d_draw_circle_wire(const float color[3],
immUnbindProgram();
}
static void cage2d_draw_rect_handles(const rctf *r,
const float margin[2],
const float color[3],
const int transform_flag,
bool solid)
static void cage2d_draw_rect_corner_handles(const rctf *r,
const int highlighted,
const float margin[2],
const float color[3],
const int transform_flag,
bool solid)
{
/* Only draw corner handles when hovering over the corners. */
if (highlighted < ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y ||
highlighted > ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MAX_Y) {
return;
}
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
void (*circle_fn)(uint, float, float, float, float, int) = (solid) ?
imm_draw_circle_fill_aspect_2d :
@ -615,6 +622,38 @@ static void cage2d_draw_rect_handles(const rctf *r,
immUnbindProgram();
}
static void cage2d_draw_rect_edge_handles(const rctf *r,
const int highlighted,
const float size[2],
const float margin[2],
const float color[3],
bool solid)
{
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3fv(color);
switch (highlighted) {
case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X:
case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X: {
const float rad[2] = {0.2f * margin[0], 0.4f * size[1]};
imm_draw_point_aspect_2d(pos, r->xmin, 0.f, rad[0], rad[1], solid);
imm_draw_point_aspect_2d(pos, r->xmax, 0.f, rad[0], rad[1], solid);
break;
}
case ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y:
case ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y: {
const float rad[2] = {0.4f * size[0], 0.2f * margin[1]};
imm_draw_point_aspect_2d(pos, 0.f, r->ymin, rad[0], rad[1], solid);
imm_draw_point_aspect_2d(pos, 0.f, r->ymax, rad[0], rad[1], solid);
break;
}
}
immUnbindProgram();
}
/** \} */
static void gizmo_cage2d_draw_intern(wmGizmo *gz,
@ -665,15 +704,15 @@ static void gizmo_cage2d_draw_intern(wmGizmo *gz,
else {
if (transform_flag & ED_GIZMO_CAGE_XFORM_FLAG_SCALE) {
int scale_parts[] = {
ED_GIZMO_CAGE2D_PART_SCALE_MIN_X,
ED_GIZMO_CAGE2D_PART_SCALE_MAX_X,
ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y,
ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y,
ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y,
ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MAX_Y,
ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MIN_Y,
ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MAX_Y,
ED_GIZMO_CAGE2D_PART_SCALE_MIN_X,
ED_GIZMO_CAGE2D_PART_SCALE_MAX_X,
ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y,
ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y,
};
for (int i = 0; i < ARRAY_SIZE(scale_parts); i++) {
GPU_select_load_id(select_id | scale_parts[i]);
@ -754,9 +793,15 @@ static void gizmo_cage2d_draw_intern(wmGizmo *gz,
cage2d_draw_rect_wire(&r, margin, black, transform_flag, draw_options, outline_line_width);
cage2d_draw_rect_wire(&r, margin, color, transform_flag, draw_options, gz->line_width);
/* Corner gizmos. */
cage2d_draw_rect_handles(&r, margin, black, transform_flag, false);
cage2d_draw_rect_handles(&r, margin, color, transform_flag, true);
/* Edge handles. */
cage2d_draw_rect_edge_handles(&r, gz->highlight_part, size_real, margin, color, true);
cage2d_draw_rect_edge_handles(&r, gz->highlight_part, size_real, margin, black, false);
/* Corner handles. */
cage2d_draw_rect_corner_handles(
&r, gz->highlight_part, margin, color, transform_flag, true);
cage2d_draw_rect_corner_handles(
&r, gz->highlight_part, margin, black, transform_flag, false);
}
else if (draw_style == ED_GIZMO_CAGE2D_STYLE_CIRCLE) {
cage2d_draw_circle_wire(black, size_real, margin, outline_line_width);

View File

@ -252,6 +252,7 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain,
float start_point[2], last[2];
start_point[0] = c[n - 1][2].x;
start_point[1] = c[n - 1][2].y;
zero_v2(last);
for (int32_t i = 0; i < n; i++) {
switch (tag[i]) {

View File

@ -128,6 +128,9 @@ bke::GSpanAttributeWriter ensure_selection_attribute(bke::CurvesGeometry &curves
eAttrDomain selection_domain,
eCustomDataType create_type);
/** Apply a change to a single curve or point. Avoid using this when affecting many elements. */
void apply_selection_operation_at_index(GMutableSpan selection, int index, eSelectOp sel_op);
/**
* (De)select all the curves.
*
@ -167,7 +170,7 @@ void select_random(bke::CurvesGeometry &curves,
float probability);
/**
* Helper struct for `select_pick`.
* Helper struct for `closest_elem_find_screen_space`.
*/
struct FindClosestData {
int index = -1;
@ -175,15 +178,16 @@ struct FindClosestData {
};
/**
* Select point or curve at a (screen-space) point.
* Find the closest screen-space point or curve in projected region-space.
*
* \return A new point or curve closer than the \a initial input, if one exists.
*/
bool select_pick(const ViewContext &vc,
const Object &object,
bke::CurvesGeometry &curves,
eAttrDomain selection_domain,
const SelectPick_Params &params,
int2 coord,
FindClosestData initial = {});
std::optional<FindClosestData> closest_elem_find_screen_space(const ViewContext &vc,
const Object &object,
bke::CurvesGeometry &curves,
eAttrDomain domain,
int2 coord,
const FindClosestData &initial);
/**
* Select points or curves in a (screen-space) rectangle.

View File

@ -18,6 +18,7 @@ struct ViewContext;
struct bContext;
struct rcti;
struct wmOperator;
struct wmKeyConfig;
/* sculpt.cc */
@ -28,6 +29,7 @@ bool ED_sculpt_mask_box_select(struct bContext *C,
const struct rcti *rect,
bool select);
void ED_keymap_sculpt(wmKeyConfig *keyconf);
/* sculpt_transform.cc */
void ED_sculpt_update_modal_transform(struct bContext *C, struct Object *ob);

View File

@ -26,7 +26,6 @@ struct Object;
struct Scene;
struct SpaceImage;
struct ToolSettings;
struct UVPackIsland_Params;
struct View2D;
struct ViewLayer;
struct bContext;
@ -356,26 +355,6 @@ bool uv_coords_isect_udim(const struct Image *image,
const int udim_grid[2],
const float coords[2]);
/**
* Pack UV islands from multiple objects.
*
* \param scene: Scene containing the objects to be packed.
* \param objects: Array of Objects to pack.
* \param objects_len: Length of `objects` array.
* \param bmesh_override: BMesh array aligned with `objects`.
* Optional, when non-null this overrides object's BMesh.
* This is needed to perform UV packing on objects that aren't in edit-mode.
* \param udim_params: Parameters to specify UDIM target and UDIM source image.
* \param params: Parameters and options to pass to the packing engine.
*
*/
void ED_uvedit_pack_islands_multi(const struct Scene *scene,
Object **objects,
uint objects_len,
struct BMesh **bmesh_override,
const struct UVMapUDIM_Params *closest_udim,
const struct UVPackIsland_Params *params);
#ifdef __cplusplus
}
#endif

View File

@ -3877,7 +3877,7 @@ static void ui_but_update_ex(uiBut *but, const bool validate)
case UI_BTYPE_KEY_EVENT: {
const char *str;
if (but->flag & UI_SELECT) {
str = "Press a key";
str = IFACE_("Press a key");
}
else {
UI_GET_BUT_VALUE_INIT(but, value);
@ -3910,7 +3910,7 @@ static void ui_but_update_ex(uiBut *but, const bool validate)
(void)str; /* UNUSED */
}
else {
BLI_strncpy(but->drawstr, "Press a key", UI_MAX_DRAW_STR);
BLI_strncpy(but->drawstr, IFACE_("Press a key"), UI_MAX_DRAW_STR);
}
}
else {

View File

@ -5912,7 +5912,8 @@ static bool ui_layout_has_panel_label(const uiLayout *layout, const PanelType *p
LISTBASE_FOREACH (uiItem *, subitem, &layout->items) {
if (subitem->type == ITEM_BUTTON) {
uiButtonItem *bitem = (uiButtonItem *)subitem;
if (!(bitem->but->flag & UI_HIDDEN) && STREQ(bitem->but->str, pt->label)) {
if (!(bitem->but->flag & UI_HIDDEN) &&
STREQ(bitem->but->str, CTX_IFACE_(pt->translation_context, pt->label))) {
return true;
}
}

View File

@ -64,7 +64,7 @@ static int wm_ply_export_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "No filename given");
return OPERATOR_CANCELLED;
}
struct PLYExportParams export_params;
struct PLYExportParams export_params = {"\0"};
export_params.file_base_for_tests[0] = '\0';
RNA_string_get(op->ptr, "filepath", export_params.filepath);
export_params.blen_filepath = CTX_data_main(C)->filepath;

View File

@ -982,7 +982,7 @@ void SCULPT_OT_face_sets_change_visibility(wmOperatorType *ot)
ot->invoke = sculpt_face_sets_change_visibility_invoke;
ot->poll = SCULPT_mode_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR;
RNA_def_enum(ot->srna,
"mode",

View File

@ -5,6 +5,8 @@
* \ingroup edsculpt
*/
#include "DNA_modifier_types.h"
#include "DNA_windowmanager_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_hash.h"
@ -13,10 +15,13 @@
#include "BLI_math_vector_types.hh"
#include "BLI_task.h"
#include "BLT_translation.h"
#include "DNA_meshdata_types.h"
#include "BKE_brush.h"
#include "BKE_context.h"
#include "BKE_modifier.h"
#include "BKE_paint.h"
#include "BKE_pbvh.h"
@ -26,6 +31,7 @@
#include "WM_types.h"
#include "ED_screen.h"
#include "ED_util.h"
#include "ED_view3d.h"
#include "paint_intern.h"
@ -705,6 +711,55 @@ static void mesh_filter_surface_smooth_displace_task_cb(void *__restrict userdat
BKE_pbvh_vertex_iter_end;
}
enum {
FILTER_MESH_MODAL_CANCEL = 1,
FILTER_MESH_MODAL_CONFIRM,
};
wmKeyMap *filter_mesh_modal_keymap(wmKeyConfig *keyconf)
{
static const EnumPropertyItem modal_items[] = {
{FILTER_MESH_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
{FILTER_MESH_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
{0, nullptr, 0, nullptr, nullptr},
};
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Mesh Filter Modal Map");
/* This function is called for each space-type, only needs to add map once. */
if (keymap && keymap->modal_items) {
return nullptr;
}
keymap = WM_modalkeymap_ensure(keyconf, "Mesh Filter Modal Map", modal_items);
WM_modalkeymap_assign(keymap, "SCULPT_OT_mesh_filter");
return keymap;
}
static void sculpt_mesh_update_status_bar(bContext *C, wmOperator *op)
{
char header[UI_MAX_DRAW_STR];
char buf[UI_MAX_DRAW_STR];
int available_len = sizeof(buf);
char *p = buf;
#define WM_MODALKEY(_id) \
WM_modalkeymap_operator_items_to_string_buf( \
op->type, (_id), true, UI_MAX_SHORTCUT_STR, &available_len, &p)
BLI_snprintf(header,
sizeof(header),
TIP_("%s: Confirm, %s: Cancel"),
WM_MODALKEY(FILTER_MESH_MODAL_CONFIRM),
WM_MODALKEY(FILTER_MESH_MODAL_CANCEL));
#undef WM_MODALKEY
ED_workspace_status_text(C, TIP_(header));
}
static void sculpt_mesh_filter_apply(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
@ -790,17 +845,30 @@ static void sculpt_mesh_filter_apply_with_history(bContext *C, wmOperator *op)
RNA_float_set(op->ptr, "strength", initial_strength);
}
static void sculpt_mesh_filter_end(bContext *C, wmOperator * /*op*/)
static void sculpt_mesh_filter_end(bContext *C)
{
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
SCULPT_filter_cache_free(ss);
SCULPT_undo_push_end(ob);
SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS);
}
static void UNUSED_FUNCTION(sculpt_mesh_filter_cancel)(bContext *C, wmOperator * /*op*/)
static int sculpt_mesh_filter_confirm(SculptSession *ss,
wmOperator *op,
const eSculptMeshFilterType filter_type)
{
float initial_strength = ss->filter_cache->start_filter_strength;
/* Don't update strength property if we're storing an event history. */
if (sculpt_mesh_filter_is_continuous(filter_type)) {
RNA_float_set(op->ptr, "strength", initial_strength);
}
return OPERATOR_FINISHED;
}
static void sculpt_mesh_filter_cancel(bContext *C, wmOperator * /*op*/)
{
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
@ -831,6 +899,7 @@ static void UNUSED_FUNCTION(sculpt_mesh_filter_cancel)(bContext *C, wmOperator *
BKE_pbvh_node_mark_update(node);
}
MEM_SAFE_FREE(nodes);
BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB);
}
@ -841,16 +910,29 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *
SculptSession *ss = ob->sculpt;
const eSculptMeshFilterType filter_type = eSculptMeshFilterType(RNA_enum_get(op->ptr, "type"));
if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
float initial_strength = ss->filter_cache->start_filter_strength;
sculpt_mesh_filter_end(C, op);
WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EW_SCROLL);
sculpt_mesh_update_status_bar(C, op);
/* Don't update strength property if we're storing an event history. */
if (sculpt_mesh_filter_is_continuous(filter_type)) {
RNA_float_set(op->ptr, "strength", initial_strength);
if (event->type == EVT_MODAL_MAP) {
int ret = OPERATOR_FINISHED;
switch (event->val) {
case FILTER_MESH_MODAL_CANCEL:
sculpt_mesh_filter_cancel(C, op);
SCULPT_undo_push_end_ex(ob, true);
ret = OPERATOR_CANCELLED;
break;
case FILTER_MESH_MODAL_CONFIRM:
ret = sculpt_mesh_filter_confirm(ss, op, filter_type);
SCULPT_undo_push_end_ex(ob, false);
break;
}
return OPERATOR_FINISHED;
sculpt_mesh_filter_end(C);
ED_workspace_status_text(C, nullptr); /* Clear status bar */
WM_cursor_modal_restore(CTX_wm_window(C));
return ret;
}
if (event->type != MOUSEMOVE) {
@ -892,6 +974,41 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *
return OPERATOR_RUNNING_MODAL;
}
static void sculpt_filter_specific_init(const eSculptMeshFilterType filter_type,
wmOperator *op,
SculptSession *ss)
{
switch (filter_type) {
case MESH_FILTER_SURFACE_SMOOTH: {
const float shape_preservation = RNA_float_get(op->ptr, "surface_smooth_shape_preservation");
const float current_vertex_displacement = RNA_float_get(op->ptr,
"surface_smooth_current_vertex");
mesh_filter_surface_smooth_init(ss, shape_preservation, current_vertex_displacement);
break;
}
case MESH_FILTER_SHARPEN: {
const float smooth_ratio = RNA_float_get(op->ptr, "sharpen_smooth_ratio");
const float intensify_detail_strength = RNA_float_get(op->ptr,
"sharpen_intensify_detail_strength");
const int curvature_smooth_iterations = RNA_int_get(op->ptr,
"sharpen_curvature_smooth_iterations");
mesh_filter_sharpen_init(
ss, smooth_ratio, intensify_detail_strength, curvature_smooth_iterations);
break;
}
case MESH_FILTER_ENHANCE_DETAILS: {
mesh_filter_enhance_details_init_directions(ss);
break;
}
case MESH_FILTER_ERASE_DISPLACEMENT: {
mesh_filter_init_limit_surface_co(ss);
break;
}
default:
break;
}
}
/* Returns OPERATOR_PASS_THROUGH on success. */
static int sculpt_mesh_filter_start(bContext *C, wmOperator *op)
{
@ -946,35 +1063,7 @@ static int sculpt_mesh_filter_start(bContext *C, wmOperator *op)
filter_cache->active_face_set = SCULPT_FACE_SET_NONE;
filter_cache->automasking = SCULPT_automasking_cache_init(sd, nullptr, ob);
switch (filter_type) {
case MESH_FILTER_SURFACE_SMOOTH: {
const float shape_preservation = RNA_float_get(op->ptr, "surface_smooth_shape_preservation");
const float current_vertex_displacement = RNA_float_get(op->ptr,
"surface_smooth_current_vertex");
mesh_filter_surface_smooth_init(ss, shape_preservation, current_vertex_displacement);
break;
}
case MESH_FILTER_SHARPEN: {
const float smooth_ratio = RNA_float_get(op->ptr, "sharpen_smooth_ratio");
const float intensify_detail_strength = RNA_float_get(op->ptr,
"sharpen_intensify_detail_strength");
const int curvature_smooth_iterations = RNA_int_get(op->ptr,
"sharpen_curvature_smooth_iterations");
mesh_filter_sharpen_init(
ss, smooth_ratio, intensify_detail_strength, curvature_smooth_iterations);
break;
}
case MESH_FILTER_ENHANCE_DETAILS: {
mesh_filter_enhance_details_init_directions(ss);
break;
}
case MESH_FILTER_ERASE_DISPLACEMENT: {
mesh_filter_init_limit_surface_co(ss);
break;
}
default:
break;
}
sculpt_filter_specific_init(filter_type, op, ss);
ss->filter_cache->enabled_axis[0] = deform_axis & MESH_FILTER_DEFORM_X;
ss->filter_cache->enabled_axis[1] = deform_axis & MESH_FILTER_DEFORM_Y;
@ -1021,7 +1110,7 @@ static int sculpt_mesh_filter_exec(bContext *C, wmOperator *op)
ss->filter_cache->no_orig_co = true;
}
sculpt_mesh_filter_end(C, op);
sculpt_mesh_filter_end(C);
return OPERATOR_FINISHED;
}
@ -1087,7 +1176,12 @@ void SCULPT_OT_mesh_filter(wmOperatorType *ot)
ot->exec = sculpt_mesh_filter_exec;
ot->ui = sculpt_mesh_ui_exec;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* Doesn't seem to actually be called?
Check `sculpt_mesh_filter_modal` to see where it's really called. */
ot->cancel = sculpt_mesh_filter_cancel;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR_X | OPTYPE_BLOCKING |
OPTYPE_DEPENDS_ON_CURSOR;
/* RNA. */
SCULPT_mesh_filter_properties(ot);

View File

@ -1683,6 +1683,7 @@ void SCULPT_OT_set_pivot_position(wmOperatorType *ot);
/* Mesh Filter. */
void SCULPT_OT_mesh_filter(wmOperatorType *ot);
struct wmKeyMap *filter_mesh_modal_keymap(struct wmKeyConfig *keyconf);
/* Cloth Filter. */

View File

@ -1427,3 +1427,8 @@ void ED_operatortypes_sculpt(void)
WM_operatortype_append(SCULPT_OT_mask_from_cavity);
WM_operatortype_append(SCULPT_OT_reveal_all);
}
void ED_keymap_sculpt(wmKeyConfig *keyconf)
{
filter_mesh_modal_keymap(keyconf);
}

View File

@ -193,6 +193,7 @@ void ED_spacetypes_keymap(wmKeyConfig *keyconf)
ED_keymap_paint(keyconf);
ED_keymap_mask(keyconf);
ED_keymap_marker(keyconf);
ED_keymap_sculpt(keyconf);
ED_keymap_view2d(keyconf);
ED_keymap_ui(keyconf);

View File

@ -11,6 +11,7 @@
#include "DNA_screen_types.h"
#include "BKE_context.h"
#include "BKE_screen.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
@ -41,18 +42,6 @@
/* Own include. */
#include "sequencer_intern.h"
static ARegion *timeline_region_get(const ScrArea *area)
{
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
if (region->regiontype == RGN_TYPE_WINDOW) {
return region;
}
}
BLI_assert_unreachable();
return NULL;
}
static float draw_offset_get(const View2D *timeline_region_v2d)
{
return timeline_region_v2d->cur.ymin;
@ -335,7 +324,8 @@ void channel_draw_context_init(const bContext *C,
r_context->ed = SEQ_editing_get(r_context->scene);
r_context->seqbase = SEQ_active_seqbase_get(r_context->ed);
r_context->channels = SEQ_channels_displayed_get(r_context->ed);
r_context->timeline_region = timeline_region_get(CTX_wm_area(C));
r_context->timeline_region = BKE_area_find_region_type(r_context->area, RGN_TYPE_WINDOW);
BLI_assert(r_context->timeline_region != NULL);
r_context->timeline_region_v2d = &r_context->timeline_region->v2d;
r_context->channel_height = channel_height_pixelspace_get(r_context->timeline_region_v2d);

View File

@ -1851,6 +1851,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op)
seq_new->start = start_ofs;
seq_new->type = SEQ_TYPE_IMAGE;
seq_new->len = 1;
seq->flag |= SEQ_SINGLE_FRAME_CONTENT;
seq_new->endofs = 1 - step;
/* New strip. */
@ -2918,6 +2919,13 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op)
RNA_END;
}
if (len == 1) {
seq->flag |= SEQ_SINGLE_FRAME_CONTENT;
}
else {
seq->flag &= ~SEQ_SINGLE_FRAME_CONTENT;
}
/* Reset these else we won't see all the images. */
seq->anim_startofs = seq->anim_endofs = 0;

View File

@ -54,10 +54,14 @@
using blender::MutableSpan;
#define REMOVE_GIZMO_HEIGHT 14.0f * U.dpi_fac /* Pixels from bottom of strip. */
#define RETIME_HANDLE_TRIANGLE_SIZE 14.0f * U.dpi_fac /* Size in pixels. */
#define RETIME_HANDLE_MOUSEOVER_THRESHOLD 16.0f * U.dpi_fac /* Size in pixels. */
#define RETIME_BUTTON_SIZE 0.6f /* Factor based on icon size. */
/** Pixels from bottom of strip. */
#define REMOVE_GIZMO_HEIGHT (14.0f * U.dpi_fac)
/** Size in pixels. */
#define RETIME_HANDLE_TRIANGLE_SIZE (14.0f * U.dpi_fac)
/** Size in pixels. */
#define RETIME_HANDLE_MOUSEOVER_THRESHOLD (16.0f * U.dpi_fac)
/** Factor based on icon size. */
#define RETIME_BUTTON_SIZE 0.6f
static float strip_y_rescale(const Sequence *seq, const float y_value)
{

View File

@ -2810,8 +2810,10 @@ void TEXT_OT_scroll(wmOperatorType *ot)
ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY | OPTYPE_INTERNAL;
/* properties */
RNA_def_int(
PropertyRNA *prop;
prop = RNA_def_int(
ot->srna, "lines", 1, INT_MIN, INT_MAX, "Lines", "Number of lines to scroll", -100, 100);
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_TEXT);
}
/** \} */
@ -2915,8 +2917,10 @@ void TEXT_OT_scroll_bar(wmOperatorType *ot)
ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
/* properties */
RNA_def_int(
PropertyRNA *prop;
prop = RNA_def_int(
ot->srna, "lines", 1, INT_MIN, INT_MAX, "Lines", "Number of lines to scroll", -100, 100);
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_TEXT);
}
/** \} */

View File

@ -31,6 +31,7 @@
#include "BLI_math_bits.h"
#include "BLI_rect.h"
#include "BLI_string.h"
#include "BLI_task.hh"
#include "BLI_utildefines.h"
#include "BLI_vector.hh"
@ -2985,8 +2986,13 @@ static bool ed_wpaint_vertex_select_pick(bContext *C,
return changed || found;
}
struct ClosestCurveDataBlock {
Curves *curves_id = nullptr;
blender::ed::curves::FindClosestData elem = {};
};
/**
* Cursor selection for the Curves object.
* Cursor selection for all Curves objects in edit mode.
*
* \returns true if the selection changed.
*/
@ -2999,35 +3005,81 @@ static bool ed_curves_select_pick(bContext &C, const int mval[2], const SelectPi
ED_view3d_viewcontext_init(&C, &vc, depsgraph);
uint bases_len;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
Base **bases_ptr = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
vc.scene, vc.view_layer, vc.v3d, &bases_len);
Span<Base *> bases(bases_ptr, bases_len);
bool changed = false;
ed::curves::FindClosestData closest_data;
for (uint base_index = 0; base_index < bases_len; base_index++) {
Base *base = bases[base_index];
Object *curves_ob = base->object;
Curves &curves_id = *static_cast<Curves *>(curves_ob->data);
bke::CurvesGeometry &curves = curves_id.geometry.wrap();
Curves &active_curves_id = *static_cast<Curves *>(vc.obedit->data);
const eAttrDomain selection_domain = eAttrDomain(active_curves_id.selection_domain);
if (ed::curves::select_pick(vc,
*curves_ob,
curves,
eAttrDomain(curves_id.selection_domain),
params,
mval,
closest_data)) {
changed = true;
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
* generic attribute for now. */
DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(&C, NC_GEOM | ND_DATA, &curves_id);
}
const ClosestCurveDataBlock closest = threading::parallel_reduce(
bases.index_range(),
1L,
ClosestCurveDataBlock(),
[&](const IndexRange range, const ClosestCurveDataBlock &init) {
ClosestCurveDataBlock new_closest = init;
for (Base *base : bases.slice(range)) {
Object &curves_ob = *base->object;
Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
std::optional<ed::curves::FindClosestData> new_closest_elem =
ed::curves::closest_elem_find_screen_space(vc,
curves_ob,
curves_id.geometry.wrap(),
selection_domain,
mval,
new_closest.elem);
if (new_closest_elem) {
new_closest.elem = *new_closest_elem;
new_closest.curves_id = &curves_id;
}
}
return new_closest;
},
[](const ClosestCurveDataBlock &a, const ClosestCurveDataBlock &b) {
return (a.elem.distance < b.elem.distance) ? a : b;
});
std::atomic<bool> deselected = false;
if (params.deselect_all || params.sel_op == SEL_OP_SET) {
threading::parallel_for(bases.index_range(), 1L, [&](const IndexRange range) {
for (Base *base : bases.slice(range)) {
Curves &curves_id = *static_cast<Curves *>(base->object->data);
bke::CurvesGeometry &curves = curves_id.geometry.wrap();
if (ed::curves::has_anything_selected(curves)) {
bke::GSpanAttributeWriter selection = ed::curves::ensure_selection_attribute(
curves, selection_domain, CD_PROP_BOOL);
ed::curves::fill_selection_false(selection.span);
selection.finish();
deselected = true;
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
* generic attribute for now. */
DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(&C, NC_GEOM | ND_DATA, &curves_id);
}
}
});
}
MEM_freeN(bases);
if (!closest.curves_id) {
MEM_freeN(bases_ptr);
return deselected;
}
return changed;
bke::GSpanAttributeWriter selection = ed::curves::ensure_selection_attribute(
closest.curves_id->geometry.wrap(), selection_domain, CD_PROP_BOOL);
ed::curves::apply_selection_operation_at_index(
selection.span, closest.elem.index, params.sel_op);
selection.finish();
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
* generic attribute for now. */
DEG_id_tag_update(&closest.curves_id->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(&C, NC_GEOM | ND_DATA, closest.curves_id);
MEM_freeN(bases_ptr);
return true;
}
static int view3d_select_exec(bContext *C, wmOperator *op)
@ -3081,7 +3133,8 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
if (obedit && enumerate) {
/* Enumerate makes no sense in edit-mode unless also explicitly picking objects or bones.
* Pass the event through so the event may be handled by loop-select for e.g. see: #100204. */
* Pass the event through so the event may be handled by loop-select for e.g. see: #100204.
*/
if (obedit->type != OB_ARMATURE) {
return OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED;
}

View File

@ -170,7 +170,6 @@ static void createTransTrackingCurvesData(bContext *C, TransInfo *t)
static void createTransTrackingCurves(bContext *C, TransInfo *t)
{
ARegion *region = CTX_wm_region(C);
SpaceClip *sc = CTX_wm_space_clip(C);
MovieClip *clip = ED_space_clip_get_clip(sc);
int width, height;
@ -190,7 +189,7 @@ static void createTransTrackingCurves(bContext *C, TransInfo *t)
}
/* transformation was called from graph editor */
BLI_assert(region->regiontype == RGN_TYPE_PREVIEW);
BLI_assert(CTX_wm_region(C)->regiontype == RGN_TYPE_PREVIEW);
createTransTrackingCurvesData(C, t);
}

View File

@ -11,223 +11,14 @@
* This API uses #BMesh data structures and doesn't have limitations for manifold meshes.
*/
#include "MEM_guardedalloc.h"
#include "DNA_meshdata_types.h"
#include "DNA_scene_types.h"
#include "DNA_space_types.h"
#include "BLI_boxpack_2d.h"
#include "BLI_convexhull_2d.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_rect.h"
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
#include "BKE_image.h"
#include "DEG_depsgraph.h"
#include "BLI_math.h"
#include "DNA_image_types.h"
#include "ED_uvedit.h" /* Own include. */
#include "GEO_uv_pack.hh"
#include "WM_api.h"
#include "WM_types.h"
#include "bmesh.h"
static void mul_v2_m2_add_v2v2(float r[2],
const float mat[2][2],
const float a[2],
const float b[2])
{
/* Compute `r = mat * (a + b)` with high precision. */
const double x = double(a[0]) + double(b[0]);
const double y = double(a[1]) + double(b[1]);
r[0] = float(mat[0][0] * x + mat[1][0] * y);
r[1] = float(mat[0][1] * x + mat[1][1] * y);
}
static void island_uv_transform(FaceIsland *island,
const float matrix[2][2], /* Scale and rotation. */
const float pre_translate[2] /* (pre) Translation. */
)
{
/* Use a pre-transform to compute `A * (x+b)`
*
* \note Ordinarily, we'd use a post_transform like `A * x + b`
* In general, post-transforms are easier to work with when using homogenous co-ordinates.
*
* When UV mapping into the unit square, post-transforms can lose precision on small islands.
* Instead we're using a pre-transform to maintain precision.
*
* To convert post-transform to pre-transform, use `A * x + b == A * (x + c), c = A^-1 * b`
*/
const int cd_loop_uv_offset = island->offsets.uv;
const int faces_len = island->faces_len;
for (int i = 0; i < faces_len; i++) {
BMFace *f = island->faces[i];
BMLoop *l;
BMIter iter;
BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) {
float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
mul_v2_m2_add_v2v2(luv, matrix, luv, pre_translate);
}
}
}
/* -------------------------------------------------------------------- */
/** \name UV Face Array Utilities
* \{ */
static void bm_face_array_calc_bounds(BMFace **faces,
const int faces_len,
const int cd_loop_uv_offset,
rctf *r_bounds_rect)
{
BLI_assert(cd_loop_uv_offset >= 0);
float bounds_min[2], bounds_max[2];
INIT_MINMAX2(bounds_min, bounds_max);
for (int i = 0; i < faces_len; i++) {
BMFace *f = faces[i];
BM_face_uv_minmax(f, bounds_min, bounds_max, cd_loop_uv_offset);
}
r_bounds_rect->xmin = bounds_min[0];
r_bounds_rect->ymin = bounds_min[1];
r_bounds_rect->xmax = bounds_max[0];
r_bounds_rect->ymax = bounds_max[1];
}
/**
* Return an array of un-ordered UV coordinates,
* without duplicating coordinates for loops that share a vertex.
*/
static float (*bm_face_array_calc_unique_uv_coords(
BMFace **faces, int faces_len, const int cd_loop_uv_offset, int *r_coords_len))[2]
{
BLI_assert(cd_loop_uv_offset >= 0);
int coords_len_alloc = 0;
for (int i = 0; i < faces_len; i++) {
BMFace *f = faces[i];
BMLoop *l_iter, *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
BM_elem_flag_enable(l_iter, BM_ELEM_TAG);
} while ((l_iter = l_iter->next) != l_first);
coords_len_alloc += f->len;
}
float(*coords)[2] = static_cast<float(*)[2]>(
MEM_mallocN(sizeof(*coords) * coords_len_alloc, __func__));
int coords_len = 0;
for (int i = 0; i < faces_len; i++) {
BMFace *f = faces[i];
BMLoop *l_iter, *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
/* Already walked over, continue. */
continue;
}
BM_elem_flag_disable(l_iter, BM_ELEM_TAG);
const float *luv = BM_ELEM_CD_GET_FLOAT_P(l_iter, cd_loop_uv_offset);
copy_v2_v2(coords[coords_len++], luv);
/* Un tag all connected so we don't add them twice.
* Note that we will tag other loops not part of `faces` but this is harmless,
* since we're only turning off a tag. */
BMVert *v_pivot = l_iter->v;
BMEdge *e_first = v_pivot->e;
const BMEdge *e = e_first;
do {
if (e->l != nullptr) {
const BMLoop *l_radial = e->l;
do {
if (l_radial->v == l_iter->v) {
if (BM_elem_flag_test(l_radial, BM_ELEM_TAG)) {
const float *luv_radial = BM_ELEM_CD_GET_FLOAT_P(l_radial, cd_loop_uv_offset);
if (equals_v2v2(luv, luv_radial)) {
/* Don't add this UV when met in another face in `faces`. */
BM_elem_flag_disable(l_iter, BM_ELEM_TAG);
}
}
}
} while ((l_radial = l_radial->radial_next) != e->l);
}
} while ((e = BM_DISK_EDGE_NEXT(e, v_pivot)) != e_first);
} while ((l_iter = l_iter->next) != l_first);
}
*r_coords_len = coords_len;
return coords;
}
static void face_island_uv_rotate_fit_aabb(FaceIsland *island)
{
BMFace **faces = island->faces;
const int faces_len = island->faces_len;
const float aspect_y = island->aspect_y;
const int cd_loop_uv_offset = island->offsets.uv;
/* Calculate unique coordinates since calculating a convex hull can be an expensive operation. */
int coords_len;
float(*coords)[2] = bm_face_array_calc_unique_uv_coords(
faces, faces_len, cd_loop_uv_offset, &coords_len);
/* Correct aspect ratio. */
if (aspect_y != 1.0f) {
for (int i = 0; i < coords_len; i++) {
coords[i][1] /= aspect_y;
}
}
float angle = BLI_convexhull_aabb_fit_points_2d(coords, coords_len);
/* Rotate coords by `angle` before computing bounding box. */
if (angle != 0.0f) {
float matrix[2][2];
angle_to_mat2(matrix, angle);
matrix[0][1] *= aspect_y;
matrix[1][1] *= aspect_y;
for (int i = 0; i < coords_len; i++) {
mul_m2_v2(matrix, coords[i]);
}
}
/* Compute new AABB. */
float bounds_min[2], bounds_max[2];
INIT_MINMAX2(bounds_min, bounds_max);
for (int i = 0; i < coords_len; i++) {
minmax_v2v2_v2(bounds_min, bounds_max, coords[i]);
}
float size[2];
sub_v2_v2v2(size, bounds_max, bounds_min);
if (size[1] < size[0]) {
angle += DEG2RADF(90.0f);
}
MEM_freeN(coords);
/* Apply rotation back to BMesh. */
if (angle != 0.0f) {
float matrix[2][2];
float pre_translate[2] = {0, 0};
angle_to_mat2(matrix, angle);
matrix[1][0] *= 1.0f / aspect_y;
/* matrix[1][1] *= aspect_y / aspect_y; */
matrix[0][1] *= aspect_y;
island_uv_transform(island, matrix, pre_translate);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name UDIM packing helper functions
* \{ */
@ -261,58 +52,6 @@ bool uv_coords_isect_udim(const Image *image, const int udim_grid[2], const floa
return false;
}
/**
* Calculates distance to nearest UDIM image tile in UV space and its UDIM tile number.
*/
static float uv_nearest_image_tile_distance(const Image *image,
const float coords[2],
float nearest_tile_co[2])
{
BKE_image_find_nearest_tile_with_offset(image, coords, nearest_tile_co);
/* Add 0.5 to get tile center coordinates. */
float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]};
add_v2_fl(nearest_tile_center_co, 0.5f);
return len_squared_v2v2(coords, nearest_tile_center_co);
}
/**
* Calculates distance to nearest UDIM grid tile in UV space and its UDIM tile number.
*/
static float uv_nearest_grid_tile_distance(const int udim_grid[2],
const float coords[2],
float nearest_tile_co[2])
{
const float coords_floor[2] = {floorf(coords[0]), floorf(coords[1])};
if (coords[0] > udim_grid[0]) {
nearest_tile_co[0] = udim_grid[0] - 1;
}
else if (coords[0] < 0) {
nearest_tile_co[0] = 0;
}
else {
nearest_tile_co[0] = coords_floor[0];
}
if (coords[1] > udim_grid[1]) {
nearest_tile_co[1] = udim_grid[1] - 1;
}
else if (coords[1] < 0) {
nearest_tile_co[1] = 0;
}
else {
nearest_tile_co[1] = coords_floor[1];
}
/* Add 0.5 to get tile center coordinates. */
float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]};
add_v2_fl(nearest_tile_center_co, 0.5f);
return len_squared_v2v2(coords, nearest_tile_center_co);
}
/** \} */
/* -------------------------------------------------------------------- */
@ -436,211 +175,3 @@ int bm_mesh_calc_uv_islands(const Scene *scene,
}
/** \} */
static bool island_has_pins(const Scene *scene,
FaceIsland *island,
const UVPackIsland_Params *params)
{
const bool pin_unselected = params->pin_unselected;
const bool only_selected_faces = params->only_selected_faces;
BMLoop *l;
BMIter iter;
const int pin_offset = island->offsets.pin;
for (int i = 0; i < island->faces_len; i++) {
BMFace *efa = island->faces[i];
if (pin_unselected && only_selected_faces && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
return true;
}
BM_ITER_ELEM (l, &iter, island->faces[i], BM_LOOPS_OF_FACE) {
if (BM_ELEM_CD_GET_BOOL(l, pin_offset)) {
return true;
}
if (pin_unselected && !uvedit_uv_select_test(scene, l, island->offsets)) {
return true;
}
}
}
return false;
}
/* -------------------------------------------------------------------- */
/** \name Public UV Island Packing
*
* \note This behavior loosely follows #geometry::uv_parametrizer_pack.
* \{ */
void ED_uvedit_pack_islands_multi(const Scene *scene,
Object **objects,
const uint objects_len,
BMesh **bmesh_override,
const UVMapUDIM_Params *closest_udim,
const UVPackIsland_Params *params)
{
blender::Vector<FaceIsland *> island_vector;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMesh *bm = nullptr;
if (bmesh_override) {
/* Note: obedit is still required for aspect ratio and ID_RECALC_GEOMETRY. */
bm = bmesh_override[ob_index];
}
else {
BMEditMesh *em = BKE_editmesh_from_object(obedit);
bm = em->bm;
}
BLI_assert(bm);
const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
if (offsets.uv == -1) {
continue;
}
const float aspect_y = params->correct_aspect ? ED_uvedit_get_aspect_y(obedit) : 1.0f;
bool only_selected_faces = params->only_selected_faces;
bool only_selected_uvs = params->only_selected_uvs;
if (params->ignore_pinned && params->pin_unselected) {
only_selected_faces = false;
only_selected_uvs = false;
}
ListBase island_list = {nullptr};
bm_mesh_calc_uv_islands(scene,
bm,
&island_list,
only_selected_faces,
only_selected_uvs,
params->use_seams,
aspect_y,
offsets);
/* Remove from linked list and append to blender::Vector. */
LISTBASE_FOREACH_MUTABLE (struct FaceIsland *, island, &island_list) {
BLI_remlink(&island_list, island);
if (params->ignore_pinned && island_has_pins(scene, island, params)) {
MEM_freeN(island->faces);
MEM_freeN(island);
continue;
}
island_vector.append(island);
}
}
if (island_vector.size() == 0) {
return;
}
/* Coordinates of bounding box containing all selected UVs. */
float selection_min_co[2], selection_max_co[2];
INIT_MINMAX2(selection_min_co, selection_max_co);
for (int index = 0; index < island_vector.size(); index++) {
FaceIsland *island = island_vector[index];
if (closest_udim) {
/* Only calculate selection bounding box if using closest_udim. */
for (int i = 0; i < island->faces_len; i++) {
BMFace *f = island->faces[i];
BM_face_uv_minmax(f, selection_min_co, selection_max_co, island->offsets.uv);
}
}
if (params->rotate) {
face_island_uv_rotate_fit_aabb(island);
}
bm_face_array_calc_bounds(
island->faces, island->faces_len, island->offsets.uv, &island->bounds_rect);
}
/* Center of bounding box containing all selected UVs. */
float selection_center[2];
if (closest_udim) {
selection_center[0] = (selection_min_co[0] + selection_max_co[0]) / 2.0f;
selection_center[1] = (selection_min_co[1] + selection_max_co[1]) / 2.0f;
}
float scale[2] = {1.0f, 1.0f};
blender::Vector<blender::geometry::PackIsland *> pack_island_vector;
for (int i = 0; i < island_vector.size(); i++) {
FaceIsland *face_island = island_vector[i];
blender::geometry::PackIsland *pack_island = new blender::geometry::PackIsland();
pack_island->bounds_rect = face_island->bounds_rect;
pack_island_vector.append(pack_island);
}
BoxPack *box_array = pack_islands(pack_island_vector, *params, scale);
float base_offset[2] = {0.0f, 0.0f};
copy_v2_v2(base_offset, params->udim_base_offset);
if (closest_udim) {
const Image *image = closest_udim->image;
const int *udim_grid = closest_udim->grid_shape;
/* Check if selection lies on a valid UDIM grid tile. */
bool is_valid_udim = uv_coords_isect_udim(image, udim_grid, selection_center);
if (is_valid_udim) {
base_offset[0] = floorf(selection_center[0]);
base_offset[1] = floorf(selection_center[1]);
}
/* If selection doesn't lie on any UDIM then find the closest UDIM grid or image tile. */
else {
float nearest_image_tile_co[2] = {FLT_MAX, FLT_MAX};
float nearest_image_tile_dist = FLT_MAX, nearest_grid_tile_dist = FLT_MAX;
if (image) {
nearest_image_tile_dist = uv_nearest_image_tile_distance(
image, selection_center, nearest_image_tile_co);
}
float nearest_grid_tile_co[2] = {0.0f, 0.0f};
nearest_grid_tile_dist = uv_nearest_grid_tile_distance(
udim_grid, selection_center, nearest_grid_tile_co);
base_offset[0] = (nearest_image_tile_dist < nearest_grid_tile_dist) ?
nearest_image_tile_co[0] :
nearest_grid_tile_co[0];
base_offset[1] = (nearest_image_tile_dist < nearest_grid_tile_dist) ?
nearest_image_tile_co[1] :
nearest_grid_tile_co[1];
}
}
float matrix[2][2];
float matrix_inverse[2][2];
float pre_translate[2];
for (int i = 0; i < island_vector.size(); i++) {
FaceIsland *island = island_vector[box_array[i].index];
matrix[0][0] = scale[0];
matrix[0][1] = 0.0f;
matrix[1][0] = 0.0f;
matrix[1][1] = scale[1];
invert_m2_m2(matrix_inverse, matrix);
/* Add base_offset, post transform. */
mul_v2_m2v2(pre_translate, matrix_inverse, base_offset);
/* Translate to box_array from bounds_rect. */
pre_translate[0] += box_array[i].x - island->bounds_rect.xmin;
pre_translate[1] += box_array[i].y - island->bounds_rect.ymin;
island_uv_transform(island, matrix, pre_translate);
}
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data);
}
for (FaceIsland *island : island_vector) {
MEM_freeN(island->faces);
MEM_freeN(island);
}
for (int i = 0; i < pack_island_vector.size(); i++) {
blender::geometry::PackIsland *pack_island = pack_island_vector[i];
pack_island_vector[i] = nullptr;
delete pack_island;
}
MEM_freeN(box_array);
}
/** \} */

View File

@ -19,6 +19,7 @@
#include "DNA_scene_types.h"
#include "BLI_array.hh"
#include "BLI_convexhull_2d.h"
#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
@ -1016,6 +1017,454 @@ void UV_OT_minimize_stretch(wmOperatorType *ot)
/** \} */
/** Compute `r = mat * (a + b)` with high precision. */
static void mul_v2_m2_add_v2v2(float r[2],
const float mat[2][2],
const float a[2],
const float b[2])
{
const double x = double(a[0]) + double(b[0]);
const double y = double(a[1]) + double(b[1]);
r[0] = float(mat[0][0] * x + mat[1][0] * y);
r[1] = float(mat[0][1] * x + mat[1][1] * y);
}
static void island_uv_transform(FaceIsland *island,
const float matrix[2][2], /* Scale and rotation. */
const float pre_translate[2] /* (pre) Translation. */
)
{
/* Use a pre-transform to compute `A * (x+b)`
*
* \note Ordinarily, we'd use a post_transform like `A * x + b`
* In general, post-transforms are easier to work with when using homogenous co-ordinates.
*
* When UV mapping into the unit square, post-transforms can lose precision on small islands.
* Instead we're using a pre-transform to maintain precision.
*
* To convert post-transform to pre-transform, use `A * x + b == A * (x + c), c = A^-1 * b`
*/
const int cd_loop_uv_offset = island->offsets.uv;
const int faces_len = island->faces_len;
for (int i = 0; i < faces_len; i++) {
BMFace *f = island->faces[i];
BMLoop *l;
BMIter iter;
BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) {
float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
mul_v2_m2_add_v2v2(luv, matrix, luv, pre_translate);
}
}
}
static void bm_face_array_calc_bounds(BMFace **faces,
const int faces_len,
const int cd_loop_uv_offset,
rctf *r_bounds_rect)
{
BLI_assert(cd_loop_uv_offset >= 0);
float bounds_min[2], bounds_max[2];
INIT_MINMAX2(bounds_min, bounds_max);
for (int i = 0; i < faces_len; i++) {
BMFace *f = faces[i];
BM_face_uv_minmax(f, bounds_min, bounds_max, cd_loop_uv_offset);
}
r_bounds_rect->xmin = bounds_min[0];
r_bounds_rect->ymin = bounds_min[1];
r_bounds_rect->xmax = bounds_max[0];
r_bounds_rect->ymax = bounds_max[1];
}
/**
* Return an array of un-ordered UV coordinates,
* without duplicating coordinates for loops that share a vertex.
*/
static float (*bm_face_array_calc_unique_uv_coords(
BMFace **faces, int faces_len, const int cd_loop_uv_offset, int *r_coords_len))[2]
{
BLI_assert(cd_loop_uv_offset >= 0);
int coords_len_alloc = 0;
for (int i = 0; i < faces_len; i++) {
BMFace *f = faces[i];
BMLoop *l_iter, *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
BM_elem_flag_enable(l_iter, BM_ELEM_TAG);
} while ((l_iter = l_iter->next) != l_first);
coords_len_alloc += f->len;
}
float(*coords)[2] = static_cast<float(*)[2]>(
MEM_mallocN(sizeof(*coords) * coords_len_alloc, __func__));
int coords_len = 0;
for (int i = 0; i < faces_len; i++) {
BMFace *f = faces[i];
BMLoop *l_iter, *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
/* Already walked over, continue. */
continue;
}
BM_elem_flag_disable(l_iter, BM_ELEM_TAG);
const float *luv = BM_ELEM_CD_GET_FLOAT_P(l_iter, cd_loop_uv_offset);
copy_v2_v2(coords[coords_len++], luv);
/* Un tag all connected so we don't add them twice.
* Note that we will tag other loops not part of `faces` but this is harmless,
* since we're only turning off a tag. */
BMVert *v_pivot = l_iter->v;
BMEdge *e_first = v_pivot->e;
const BMEdge *e = e_first;
do {
if (e->l != nullptr) {
const BMLoop *l_radial = e->l;
do {
if (l_radial->v == l_iter->v) {
if (BM_elem_flag_test(l_radial, BM_ELEM_TAG)) {
const float *luv_radial = BM_ELEM_CD_GET_FLOAT_P(l_radial, cd_loop_uv_offset);
if (equals_v2v2(luv, luv_radial)) {
/* Don't add this UV when met in another face in `faces`. */
BM_elem_flag_disable(l_iter, BM_ELEM_TAG);
}
}
}
} while ((l_radial = l_radial->radial_next) != e->l);
}
} while ((e = BM_DISK_EDGE_NEXT(e, v_pivot)) != e_first);
} while ((l_iter = l_iter->next) != l_first);
}
*r_coords_len = coords_len;
return coords;
}
static void face_island_uv_rotate_fit_aabb(FaceIsland *island)
{
BMFace **faces = island->faces;
const int faces_len = island->faces_len;
const float aspect_y = island->aspect_y;
const int cd_loop_uv_offset = island->offsets.uv;
/* Calculate unique coordinates since calculating a convex hull can be an expensive operation. */
int coords_len;
float(*coords)[2] = bm_face_array_calc_unique_uv_coords(
faces, faces_len, cd_loop_uv_offset, &coords_len);
/* Correct aspect ratio. */
if (aspect_y != 1.0f) {
for (int i = 0; i < coords_len; i++) {
coords[i][1] /= aspect_y;
}
}
float angle = BLI_convexhull_aabb_fit_points_2d(coords, coords_len);
/* Rotate coords by `angle` before computing bounding box. */
if (angle != 0.0f) {
float matrix[2][2];
angle_to_mat2(matrix, angle);
matrix[0][1] *= aspect_y;
matrix[1][1] *= aspect_y;
for (int i = 0; i < coords_len; i++) {
mul_m2_v2(matrix, coords[i]);
}
}
/* Compute new AABB. */
float bounds_min[2], bounds_max[2];
INIT_MINMAX2(bounds_min, bounds_max);
for (int i = 0; i < coords_len; i++) {
minmax_v2v2_v2(bounds_min, bounds_max, coords[i]);
}
float size[2];
sub_v2_v2v2(size, bounds_max, bounds_min);
if (size[1] < size[0]) {
angle += DEG2RADF(90.0f);
}
MEM_freeN(coords);
/* Apply rotation back to BMesh. */
if (angle != 0.0f) {
float matrix[2][2];
float pre_translate[2] = {0, 0};
angle_to_mat2(matrix, angle);
matrix[1][0] *= 1.0f / aspect_y;
/* matrix[1][1] *= aspect_y / aspect_y; */
matrix[0][1] *= aspect_y;
island_uv_transform(island, matrix, pre_translate);
}
}
/**
* Calculates distance to nearest UDIM image tile in UV space and its UDIM tile number.
*/
static float uv_nearest_image_tile_distance(const Image *image,
const float coords[2],
float nearest_tile_co[2])
{
BKE_image_find_nearest_tile_with_offset(image, coords, nearest_tile_co);
/* Add 0.5 to get tile center coordinates. */
float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]};
add_v2_fl(nearest_tile_center_co, 0.5f);
return len_squared_v2v2(coords, nearest_tile_center_co);
}
/**
* Calculates distance to nearest UDIM grid tile in UV space and its UDIM tile number.
*/
static float uv_nearest_grid_tile_distance(const int udim_grid[2],
const float coords[2],
float nearest_tile_co[2])
{
const float coords_floor[2] = {floorf(coords[0]), floorf(coords[1])};
if (coords[0] > udim_grid[0]) {
nearest_tile_co[0] = udim_grid[0] - 1;
}
else if (coords[0] < 0) {
nearest_tile_co[0] = 0;
}
else {
nearest_tile_co[0] = coords_floor[0];
}
if (coords[1] > udim_grid[1]) {
nearest_tile_co[1] = udim_grid[1] - 1;
}
else if (coords[1] < 0) {
nearest_tile_co[1] = 0;
}
else {
nearest_tile_co[1] = coords_floor[1];
}
/* Add 0.5 to get tile center coordinates. */
float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]};
add_v2_fl(nearest_tile_center_co, 0.5f);
return len_squared_v2v2(coords, nearest_tile_center_co);
}
static bool island_has_pins(const Scene *scene,
FaceIsland *island,
const UVPackIsland_Params *params)
{
const bool pin_unselected = params->pin_unselected;
const bool only_selected_faces = params->only_selected_faces;
BMLoop *l;
BMIter iter;
const int pin_offset = island->offsets.pin;
for (int i = 0; i < island->faces_len; i++) {
BMFace *efa = island->faces[i];
if (pin_unselected && only_selected_faces && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
return true;
}
BM_ITER_ELEM (l, &iter, island->faces[i], BM_LOOPS_OF_FACE) {
if (BM_ELEM_CD_GET_BOOL(l, pin_offset)) {
return true;
}
if (pin_unselected && !uvedit_uv_select_test(scene, l, island->offsets)) {
return true;
}
}
}
return false;
}
/**
* Pack UV islands from multiple objects.
*
* \param scene: Scene containing the objects to be packed.
* \param objects: Array of Objects to pack.
* \param objects_len: Length of `objects` array.
* \param bmesh_override: BMesh array aligned with `objects`.
* Optional, when non-null this overrides object's BMesh.
* This is needed to perform UV packing on objects that aren't in edit-mode.
* \param udim_params: Parameters to specify UDIM target and UDIM source image.
* \param params: Parameters and options to pass to the packing engine.
*/
static void uvedit_pack_islands_multi(const Scene *scene,
Object **objects,
const int objects_len,
BMesh **bmesh_override,
const UVMapUDIM_Params *closest_udim,
const UVPackIsland_Params *params)
{
blender::Vector<FaceIsland *> island_vector;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMesh *bm = nullptr;
if (bmesh_override) {
/* Note: obedit is still required for aspect ratio and ID_RECALC_GEOMETRY. */
bm = bmesh_override[ob_index];
}
else {
BMEditMesh *em = BKE_editmesh_from_object(obedit);
bm = em->bm;
}
BLI_assert(bm);
const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
if (offsets.uv == -1) {
continue;
}
const float aspect_y = params->correct_aspect ? ED_uvedit_get_aspect_y(obedit) : 1.0f;
bool only_selected_faces = params->only_selected_faces;
bool only_selected_uvs = params->only_selected_uvs;
if (params->ignore_pinned && params->pin_unselected) {
only_selected_faces = false;
only_selected_uvs = false;
}
ListBase island_list = {nullptr};
bm_mesh_calc_uv_islands(scene,
bm,
&island_list,
only_selected_faces,
only_selected_uvs,
params->use_seams,
aspect_y,
offsets);
/* Remove from linked list and append to blender::Vector. */
LISTBASE_FOREACH_MUTABLE (struct FaceIsland *, island, &island_list) {
BLI_remlink(&island_list, island);
if (params->ignore_pinned && island_has_pins(scene, island, params)) {
MEM_freeN(island->faces);
MEM_freeN(island);
continue;
}
island_vector.append(island);
}
}
if (island_vector.size() == 0) {
return;
}
/* Coordinates of bounding box containing all selected UVs. */
float selection_min_co[2], selection_max_co[2];
INIT_MINMAX2(selection_min_co, selection_max_co);
for (int index = 0; index < island_vector.size(); index++) {
FaceIsland *island = island_vector[index];
if (closest_udim) {
/* Only calculate selection bounding box if using closest_udim. */
for (int i = 0; i < island->faces_len; i++) {
BMFace *f = island->faces[i];
BM_face_uv_minmax(f, selection_min_co, selection_max_co, island->offsets.uv);
}
}
if (params->rotate) {
face_island_uv_rotate_fit_aabb(island);
}
bm_face_array_calc_bounds(
island->faces, island->faces_len, island->offsets.uv, &island->bounds_rect);
}
/* Center of bounding box containing all selected UVs. */
float selection_center[2];
if (closest_udim) {
selection_center[0] = (selection_min_co[0] + selection_max_co[0]) / 2.0f;
selection_center[1] = (selection_min_co[1] + selection_max_co[1]) / 2.0f;
}
float scale[2] = {1.0f, 1.0f};
blender::Vector<blender::geometry::PackIsland *> pack_island_vector;
for (int i = 0; i < island_vector.size(); i++) {
FaceIsland *face_island = island_vector[i];
blender::geometry::PackIsland *pack_island = new blender::geometry::PackIsland();
pack_island->bounds_rect = face_island->bounds_rect;
pack_island_vector.append(pack_island);
}
BoxPack *box_array = pack_islands(pack_island_vector, *params, scale);
float base_offset[2] = {0.0f, 0.0f};
copy_v2_v2(base_offset, params->udim_base_offset);
if (closest_udim) {
const Image *image = closest_udim->image;
const int *udim_grid = closest_udim->grid_shape;
/* Check if selection lies on a valid UDIM grid tile. */
bool is_valid_udim = uv_coords_isect_udim(image, udim_grid, selection_center);
if (is_valid_udim) {
base_offset[0] = floorf(selection_center[0]);
base_offset[1] = floorf(selection_center[1]);
}
/* If selection doesn't lie on any UDIM then find the closest UDIM grid or image tile. */
else {
float nearest_image_tile_co[2] = {FLT_MAX, FLT_MAX};
float nearest_image_tile_dist = FLT_MAX, nearest_grid_tile_dist = FLT_MAX;
if (image) {
nearest_image_tile_dist = uv_nearest_image_tile_distance(
image, selection_center, nearest_image_tile_co);
}
float nearest_grid_tile_co[2] = {0.0f, 0.0f};
nearest_grid_tile_dist = uv_nearest_grid_tile_distance(
udim_grid, selection_center, nearest_grid_tile_co);
base_offset[0] = (nearest_image_tile_dist < nearest_grid_tile_dist) ?
nearest_image_tile_co[0] :
nearest_grid_tile_co[0];
base_offset[1] = (nearest_image_tile_dist < nearest_grid_tile_dist) ?
nearest_image_tile_co[1] :
nearest_grid_tile_co[1];
}
}
float matrix[2][2];
float matrix_inverse[2][2];
float pre_translate[2];
for (int i = 0; i < island_vector.size(); i++) {
FaceIsland *island = island_vector[box_array[i].index];
matrix[0][0] = scale[0];
matrix[0][1] = 0.0f;
matrix[1][0] = 0.0f;
matrix[1][1] = scale[1];
invert_m2_m2(matrix_inverse, matrix);
/* Add base_offset, post transform. */
mul_v2_m2v2(pre_translate, matrix_inverse, base_offset);
/* Translate to box_array from bounds_rect. */
pre_translate[0] += box_array[i].x - island->bounds_rect.xmin;
pre_translate[1] += box_array[i].y - island->bounds_rect.ymin;
island_uv_transform(island, matrix, pre_translate);
}
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data);
}
for (FaceIsland *island : island_vector) {
MEM_freeN(island->faces);
MEM_freeN(island);
}
for (int i = 0; i < pack_island_vector.size(); i++) {
blender::geometry::PackIsland *pack_island = pack_island_vector[i];
pack_island_vector[i] = nullptr;
delete pack_island;
}
MEM_freeN(box_array);
}
/* -------------------------------------------------------------------- */
/** \name Pack UV Islands Operator
* \{ */
@ -1083,7 +1532,7 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
closest_udim->grid_shape[1] = sima->tile_grid_shape[1];
}
ED_uvedit_pack_islands_multi(
uvedit_pack_islands_multi(
scene, objects, objects_len, nullptr, closest_udim, &pack_island_params);
MEM_freeN(objects);
@ -1789,7 +2238,8 @@ static void uv_map_clip_correct(const Scene *scene,
static void uvedit_unwrap(const Scene *scene,
Object *obedit,
const UnwrapOptions *options,
UnwrapResultInfo *result_info)
int *r_count_changed,
int *r_count_failed)
{
BMEditMesh *em = BKE_editmesh_from_object(obedit);
if (!CustomData_has_layer(&em->bm->ldata, CD_PROP_FLOAT2)) {
@ -1801,20 +2251,15 @@ static void uvedit_unwrap(const Scene *scene,
ParamHandle *handle;
if (use_subsurf) {
handle = construct_param_handle_subsurfed(
scene, obedit, em, options, result_info ? &result_info->count_failed : nullptr);
handle = construct_param_handle_subsurfed(scene, obedit, em, options, r_count_failed);
}
else {
handle = construct_param_handle(
scene, obedit, em->bm, options, result_info ? &result_info->count_failed : nullptr);
handle = construct_param_handle(scene, obedit, em->bm, options, r_count_failed);
}
blender::geometry::uv_parametrizer_lscm_begin(
handle, false, scene->toolsettings->unwrapper == 0);
blender::geometry::uv_parametrizer_lscm_solve(
handle,
result_info ? &result_info->count_changed : nullptr,
result_info ? &result_info->count_failed : nullptr);
blender::geometry::uv_parametrizer_lscm_solve(handle, r_count_changed, r_count_failed);
blender::geometry::uv_parametrizer_lscm_end(handle);
blender::geometry::uv_parametrizer_average(handle, true, false, false);
@ -1828,11 +2273,12 @@ static void uvedit_unwrap_multi(const Scene *scene,
Object **objects,
const int objects_len,
const UnwrapOptions *options,
UnwrapResultInfo *result_info)
int *r_count_changed = nullptr,
int *r_count_failed = nullptr)
{
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
uvedit_unwrap(scene, obedit, options, result_info);
uvedit_unwrap(scene, obedit, options, r_count_changed, r_count_failed);
DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data);
}
@ -1861,8 +2307,7 @@ void ED_uvedit_live_unwrap(const Scene *scene, Object **objects, int objects_len
pack_island_params.margin_method = ED_UVPACK_MARGIN_SCALED;
pack_island_params.margin = scene->toolsettings->uvcalc_margin;
ED_uvedit_pack_islands_multi(
scene, objects, objects_len, nullptr, nullptr, &pack_island_params);
uvedit_pack_islands_multi(scene, objects, objects_len, nullptr, nullptr, &pack_island_params);
}
}
@ -1995,7 +2440,12 @@ static int unwrap_exec(bContext *C, wmOperator *op)
UnwrapResultInfo result_info{};
result_info.count_changed = 0;
result_info.count_failed = 0;
uvedit_unwrap_multi(scene, objects, objects_len, &options, &result_info);
uvedit_unwrap_multi(scene,
objects,
objects_len,
&options,
&result_info.count_changed,
&result_info.count_failed);
UVPackIsland_Params pack_island_params{};
pack_island_params.rotate = true;
@ -2009,7 +2459,7 @@ static int unwrap_exec(bContext *C, wmOperator *op)
RNA_enum_get(op->ptr, "margin_method"));
pack_island_params.margin = RNA_float_get(op->ptr, "margin");
ED_uvedit_pack_islands_multi(scene, objects, objects_len, nullptr, nullptr, &pack_island_params);
uvedit_pack_islands_multi(scene, objects, objects_len, nullptr, nullptr, &pack_island_params);
MEM_freeN(objects);
@ -2389,10 +2839,10 @@ static int smart_project_exec(bContext *C, wmOperator *op)
params.margin_method = eUVPackIsland_MarginMethod(RNA_enum_get(op->ptr, "margin_method"));
params.margin = RNA_float_get(op->ptr, "island_margin");
ED_uvedit_pack_islands_multi(
uvedit_pack_islands_multi(
scene, objects_changed, object_changed_len, nullptr, nullptr, &params);
/* #ED_uvedit_pack_islands_multi only supports `per_face_aspect = false`. */
/* #uvedit_pack_islands_multi only supports `per_face_aspect = false`. */
const bool per_face_aspect = false;
uv_map_clip_correct(
scene, objects_changed, object_changed_len, op, per_face_aspect, only_selected_uvs);
@ -3379,7 +3829,7 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob)
params.margin_method = ED_UVPACK_MARGIN_SCALED;
params.margin = 0.001f;
ED_uvedit_pack_islands_multi(scene, &ob, 1, &bm, nullptr, &params);
uvedit_pack_islands_multi(scene, &ob, 1, &bm, nullptr, &params);
/* Write back from BMesh to Mesh. */
BMeshToMeshParams bm_to_me_params{};

View File

@ -407,6 +407,9 @@ inline void execute_element_fn_as_multi_function(const ElementFn element_fn,
std::forward<decltype(args)>(args)...);
});
}
else {
UNUSED_VARS(exec_preset);
}
/* If devirtualized execution was disabled or not possible, use a fallback method which is
* slower but always works. */

View File

@ -11,9 +11,6 @@
* \ingroup geo
*/
/** Workaround to forward-declare C type in C++ header. */
extern "C" {
enum eUVPackIsland_MarginMethod {
ED_UVPACK_MARGIN_SCALED = 0, /* Use scale of existing UVs to multiply margin. */
ED_UVPACK_MARGIN_ADD, /* Just add the margin, ignoring any UV scale. */
@ -43,7 +40,6 @@ struct UVPackIsland_Params {
/** Additional translation for bottom left corner. */
float udim_base_offset[2];
};
}
namespace blender::geometry {

View File

@ -49,9 +49,9 @@ void uv_parametrizer_face_add(ParamHandle *handle,
void uv_parametrizer_edge_set_seam(ParamHandle *handle, ParamKey *vkeys);
void uv_parametrizer_construct_end(ParamHandle *handle,
bool fill,
bool fill_holes,
bool topology_from_uvs,
int *count_fail);
int *r_count_failed = nullptr);
void uv_parametrizer_delete(ParamHandle *handle);
/** \} */

View File

@ -146,12 +146,22 @@ void solve_length_and_collision_constraints(const OffsetIndices<int> points_by_c
float slide_direction_length_cu;
const float3 normalized_slide_direction_cu = math::normalize_and_get_length(
slide_direction_cu, slide_direction_length_cu);
const float slide_normal_length_sq_cu = math::length_squared(slide_normal_cu);
/* Use pythagorian theorem to determine how far to slide. */
const float slide_distance_cu = std::sqrt(pow2f(goal_segment_length_cu) -
math::length_squared(slide_normal_cu)) -
slide_direction_length_cu;
positions_cu[point_i] = plane_pos_cu + normalized_slide_direction_cu * slide_distance_cu;
if (pow2f(goal_segment_length_cu) > slide_normal_length_sq_cu) {
/* Use pythagorian theorem to determine how far to slide. */
const float slide_distance_cu = std::sqrt(pow2f(goal_segment_length_cu) -
slide_normal_length_sq_cu) -
slide_direction_length_cu;
positions_cu[point_i] = plane_pos_cu +
normalized_slide_direction_cu * slide_distance_cu;
}
else {
/* Minimum distance is larger than allowed segment length.
* The unilateral collision constraint is satisfied by just clamping segment length. */
positions_cu[point_i] = prev_pos_cu + math::normalize(old_pos_su - prev_pos_cu) *
goal_segment_length_cu;
}
}
if (used_iterations == max_collisions) {
revert_curve = true;

View File

@ -6,11 +6,14 @@
#include "GEO_uv_pack.hh"
#include "BLI_array.hh"
#include "BLI_boxpack_2d.h"
#include "BLI_convexhull_2d.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_math_matrix.hh"
#include "BLI_rect.h"
#include "BLI_vector.hh"
#include "DNA_meshdata_types.h"
#include "DNA_scene_types.h"
@ -20,21 +23,157 @@
namespace blender::geometry {
static float pack_islands_scale_margin(const Span<PackIsland *> &island_vector,
/* Compact representation for AABB packers. */
class UVAABBIsland {
public:
float2 uv_diagonal;
float2 uv_placement;
int64_t index;
};
/**
* Pack AABB islands using the "Alpaca" strategy, with no rotation.
*
* Each box is packed into an "L" shaped region, gradually filling up space.
* "Alpaca" is a pun, as it's pronounced the same as "L-Packer" in English.
*
* In theory, alpaca_turbo should be the fastest non-trivial packer, hence the "turbo" suffix.
*
* Technically, the algorithm here is only O(n), In practice, to get reasonable results, the input
* must be pre-sorted, which costs an additional O(nlogn) time complexity.
*/
static void pack_islands_alpaca_turbo(const Span<UVAABBIsland *> islands,
float *r_max_u,
float *r_max_v)
{
/* Exclude an initial AABB near the origin. */
float next_u1 = *r_max_u;
float next_v1 = *r_max_v;
bool zigzag = next_u1 < next_v1; /* Horizontal or Vertical strip? */
float u0 = zigzag ? next_u1 : 0.0f;
float v0 = zigzag ? 0.0f : next_v1;
/* Visit every island in order. */
for (UVAABBIsland *island : islands) {
float dsm_u = island->uv_diagonal.x;
float dsm_v = island->uv_diagonal.y;
bool restart = false;
if (zigzag) {
restart = (next_v1 < v0 + dsm_v);
}
else {
restart = (next_u1 < u0 + dsm_u);
}
if (restart) {
/* We're at the end of a strip. Restart from U axis or V axis. */
zigzag = next_u1 < next_v1;
u0 = zigzag ? next_u1 : 0.0f;
v0 = zigzag ? 0.0f : next_v1;
}
/* Place the island. */
island->uv_placement.x = u0;
island->uv_placement.y = v0;
if (zigzag) {
/* Move upwards. */
v0 += dsm_v;
next_u1 = max_ff(next_u1, u0 + dsm_u);
next_v1 = max_ff(next_v1, v0);
}
else {
/* Move sideways. */
u0 += dsm_u;
next_v1 = max_ff(next_v1, v0 + dsm_v);
next_u1 = max_ff(next_u1, u0);
}
}
/* Write back total pack AABB. */
*r_max_u = next_u1;
*r_max_v = next_v1;
}
static float pack_islands_scale_margin(const Span<PackIsland *> islands,
BoxPack *box_array,
const float scale,
const float margin)
{
for (const int64_t index : island_vector.index_range()) {
PackIsland *island = island_vector[index];
BoxPack *box = &box_array[index];
box->index = int(index);
box->w = BLI_rctf_size_x(&island->bounds_rect) * scale + 2 * margin;
box->h = BLI_rctf_size_y(&island->bounds_rect) * scale + 2 * margin;
/* #BLI_box_pack_2d produces layouts with high packing efficiency, but has O(n^3)
* time complexity, causing poor performance if there are lots of islands. See: #102843.
* #pack_islands_alpaca_turbo is designed to be the fastest packing method, O(nlogn),
* but has poor packing efficiency if the AABBs have a spread of sizes and aspect ratios.
* Here, we merge the best properties of both packers into one combined packer.
* The free tuning parameter, `alpaca_cutoff` will determine how many islands are packed
* using each method.
* The current strategy is:
* - Sort islands in size order.
* - Call #BLI_box_pack_2d on the first `alpaca_cutoff` islands.
* - Call #pack_islands_alpaca_turbo on the remaining islands.
* - Combine results.
*/
/* First, copy information from our input into the AABB structure. */
Array<UVAABBIsland *> aabbs(islands.size());
for (const int64_t i : islands.index_range()) {
PackIsland *pack_island = islands[i];
UVAABBIsland *aabb = new UVAABBIsland();
aabb->index = i;
aabb->uv_diagonal.x = BLI_rctf_size_x(&pack_island->bounds_rect) * scale + 2 * margin;
aabb->uv_diagonal.y = BLI_rctf_size_y(&pack_island->bounds_rect) * scale + 2 * margin;
aabbs[i] = aabb;
}
float max_u, max_v;
BLI_box_pack_2d(box_array, int(island_vector.size()), &max_u, &max_v);
return max_ff(max_u, max_v);
/* Sort from "biggest" to "smallest". */
std::stable_sort(aabbs.begin(), aabbs.end(), [](const UVAABBIsland *a, const UVAABBIsland *b) {
/* Just choose the AABB with larger rectangular area. */
return b->uv_diagonal.x * b->uv_diagonal.y < a->uv_diagonal.x * a->uv_diagonal.y;
});
/* Partition island_vector, largest will go to box_pack, the rest alpaca_turbo.
* See discussion above for details. */
const int64_t alpaca_cutoff = int64_t(1024); /* TODO: Tune constant. */
int64_t max_box_pack = std::min(alpaca_cutoff, islands.size());
/* Prepare for box_pack_2d. */
for (const int64_t i : islands.index_range()) {
UVAABBIsland *aabb = aabbs[i];
BoxPack *box = &box_array[i];
box->index = int(aabb->index);
box->w = aabb->uv_diagonal.x;
box->h = aabb->uv_diagonal.y;
}
/* Call box_pack_2d (slow for large N.) */
float max_u = 0.0f;
float max_v = 0.0f;
BLI_box_pack_2d(box_array, int(max_box_pack), &max_u, &max_v);
/* At this stage, `max_u` and `max_v` contain the box_pack UVs. */
/* Call Alpaca. */
pack_islands_alpaca_turbo(aabbs.as_span().drop_front(max_box_pack), &max_u, &max_v);
/* Write back Alpaca UVs. */
for (int64_t index = max_box_pack; index < aabbs.size(); index++) {
UVAABBIsland *aabb = aabbs[index];
BoxPack *box = &box_array[index];
box->x = aabb->uv_placement.x;
box->y = aabb->uv_placement.y;
}
/* Memory management. */
for (int64_t i : aabbs.index_range()) {
UVAABBIsland *aabb = aabbs[i];
aabbs[i] = nullptr;
delete aabb;
}
return std::max(max_u, max_v);
}
static float pack_islands_margin_fraction(const Span<PackIsland *> &island_vector,
@ -102,7 +241,7 @@ static float pack_islands_margin_fraction(const Span<PackIsland *> &island_vecto
}
}
scale = max_ff(scale, min_scale_roundoff);
scale = std::max(scale, min_scale_roundoff);
/* Evaluate our `f`. */
scale_last = scale;

View File

@ -3957,49 +3957,48 @@ void uv_parametrizer_edge_set_seam(ParamHandle *phandle, ParamKey *vkeys)
}
void uv_parametrizer_construct_end(ParamHandle *phandle,
bool fill,
bool fill_holes,
bool topology_from_uvs,
int *count_fail)
int *r_count_failed)
{
PChart *chart = phandle->construction_chart;
int i, j;
PEdge *outer;
param_assert(phandle->state == PHANDLE_STATE_ALLOCATED);
phandle->ncharts = p_connect_pairs(phandle, topology_from_uvs);
phandle->charts = p_split_charts(phandle, chart, phandle->ncharts);
phandle->charts = p_split_charts(phandle, phandle->construction_chart, phandle->ncharts);
MEM_freeN(phandle->construction_chart);
phandle->construction_chart = nullptr;
phash_delete(phandle->hash_verts);
phandle->hash_verts = nullptr;
phash_delete(phandle->hash_edges);
phandle->hash_edges = nullptr;
phash_delete(phandle->hash_faces);
phandle->hash_verts = phandle->hash_edges = phandle->hash_faces = nullptr;
phandle->hash_faces = nullptr;
for (i = j = 0; i < phandle->ncharts; i++) {
PVert *v;
chart = phandle->charts[i];
PChart *chart = phandle->charts[i];
PEdge *outer;
p_chart_boundaries(chart, &outer);
if (!topology_from_uvs && chart->nboundaries == 0) {
MEM_freeN(chart);
if (count_fail != nullptr) {
*count_fail += 1;
if (r_count_failed) {
*r_count_failed += 1;
}
continue;
}
phandle->charts[j] = chart;
j++;
phandle->charts[j++] = chart;
if (fill && (chart->nboundaries > 1)) {
if (fill_holes && chart->nboundaries > 1) {
p_chart_fill_boundaries(phandle, chart, outer);
}
for (v = chart->verts; v; v = v->nextlink) {
for (PVert *v = chart->verts; v; v = v->nextlink) {
p_vert_load_pin_select_uvs(phandle, v);
}
}

View File

@ -410,6 +410,7 @@ set(GLSL_SRC
shaders/gpu_shader_codegen_lib.glsl
shaders/common/gpu_shader_bicubic_sampler_lib.glsl
shaders/common/gpu_shader_common_color_ramp.glsl
shaders/common/gpu_shader_common_color_utils.glsl
shaders/common/gpu_shader_common_curves.glsl

View File

@ -42,20 +42,10 @@ void GPU_storagebuf_unbind_all(void);
void GPU_storagebuf_clear_to_zero(GPUStorageBuf *ssbo);
/**
* Clear the content of the buffer using the given #clear_data. #clear_data will be used as a
* repeatable pattern.
*
* NOTE: clear_data_len must be in range 1 to 4 (inclusive).
* Clear the content of the buffer using the given #clear_value. #clear_value will be used as a
* repeatable pattern of 32bits.
*/
void GPU_storagebuf_clear_uint(GPUStorageBuf *ssbo, uint32_t *clear_data, int clear_data_len);
/**
* Clear the content of the buffer using the given #clear_data. #clear_data will be used as a
* repeatable pattern.
*
* NOTE: clear_data_len must be in range 1 to 4 (inclusive).
*/
void GPU_storagebuf_clear_int(GPUStorageBuf *ssbo, int32_t *clear_data, int clear_data_len);
void GPU_storagebuf_clear(GPUStorageBuf *ssbo, uint32_t clear_value);
/**
* Read back content of the buffer to CPU for inspection.

View File

@ -91,20 +91,12 @@ void GPU_storagebuf_unbind_all()
void GPU_storagebuf_clear_to_zero(GPUStorageBuf *ssbo)
{
uint32_t data = 0u;
GPU_storagebuf_clear_uint(ssbo, &data, 1);
GPU_storagebuf_clear(ssbo, 0);
}
void GPU_storagebuf_clear_int(GPUStorageBuf *ssbo, int32_t *clear_data, int clear_data_len)
void GPU_storagebuf_clear(GPUStorageBuf *ssbo, uint32_t clear_value)
{
uint32_t *clear_data_uint = static_cast<uint32_t *>(static_cast<void *>(clear_data));
GPU_storagebuf_clear_uint(ssbo, clear_data_uint, clear_data_len);
}
void GPU_storagebuf_clear_uint(GPUStorageBuf *ssbo, uint32_t *clear_data, int clear_data_len)
{
BLI_assert(clear_data_len >= 1 && clear_data_len <= 4);
unwrap(ssbo)->clear(blender::Span<uint32_t>(clear_data, clear_data_len));
unwrap(ssbo)->clear(clear_value);
}
void GPU_storagebuf_copy_sub_from_vertbuf(

View File

@ -43,7 +43,7 @@ class StorageBuf {
virtual void update(const void *data) = 0;
virtual void bind(int slot) = 0;
virtual void unbind() = 0;
virtual void clear(Span<uint32_t> data) = 0;
virtual void clear(uint32_t clear_value) = 0;
virtual void copy_sub(VertBuf *src, uint dst_offset, uint src_offset, uint copy_size) = 0;
virtual void read(void *data) = 0;
};

View File

@ -62,11 +62,24 @@ template<> uint denormalize<uint>(float val)
return uint(float(DEPTH_SCALE_FACTOR) * val);
}
/* Float to other type case. */
template<typename T> T convert_type(float type)
{
return T(type);
}
/* Uint to other types. */
template<typename T> T convert_type(uint type)
{
return T(type);
}
/* Int to other types. */
template<typename T> T convert_type(int type)
{
return T(type);
}
template<> uchar convert_type<uchar>(float val)
{
return uchar(val * float(0xFF));
@ -141,8 +154,8 @@ kernel void compute_texture_read(constant TextureReadParams &params [[buffer(0)]
uint xx = position[0];
uint yy = position[1];
uint zz = position[2];
int index = (zz * (params.extent[0] * params.extent[1]) + yy * params.extnt[0] + xx) *
COMPONENT_COUNT_INPUT;
int index = (zz * (params.extent[0] * params.extent[1]) + yy * params.extent[0] + xx) *
COMPONENT_COUNT_OUTPUT;
read_colour = read_tex.read(uint3(params.offset[0], params.offset[1], params.offset[2]) +
uint3(xx, yy, zz));
@ -163,7 +176,7 @@ kernel void compute_texture_read(constant TextureReadParams &params [[buffer(0)]
uint yy = position[1];
uint layer = position[2];
int index = (layer * (params.extent[0] * params.extent[1]) + yy * params.extent[0] + xx) *
COMPONENT_COUNT_INPUT;
COMPONENT_COUNT_OUTPUT;
/* Read data */
# if IS_DEPTH_FORMAT == 1

View File

@ -606,17 +606,6 @@ void MTLFrameBuffer::update_attachments(bool update_viewport)
if (!dirty_attachments_) {
return;
}
/* Cache viewport and scissor (If we have existing attachments). */
int t_viewport[4], t_scissor[4];
update_viewport = update_viewport &&
(this->get_attachment_count() > 0 && this->has_depth_attachment() &&
this->has_stencil_attachment());
if (update_viewport) {
this->viewport_get(t_viewport);
this->scissor_get(t_scissor);
}
/* Clear current attachments state. */
this->remove_all_attachments();
@ -738,22 +727,25 @@ void MTLFrameBuffer::update_attachments(bool update_viewport)
}
}
/* Check whether the first attachment is SRGB. */
/* Extract attachment size and determine if framebuffer is SRGB. */
if (first_attachment != GPU_FB_MAX_ATTACHMENT) {
srgb_ = (first_attachment_mtl.texture->format_get() == GPU_SRGB8_A8);
}
/* Reset viewport and Scissor (If viewport is smaller or equal to the framebuffer size). */
if (update_viewport && t_viewport[2] <= width_ && t_viewport[3] <= height_) {
this->viewport_set(t_viewport);
this->scissor_set(t_viewport);
/* Ensure size is correctly assigned. */
GPUAttachment &attach = attachments_[first_attachment];
int size[3];
GPU_texture_get_mipmap_size(attach.tex, attach.mip, size);
this->size_set(size[0], size[1]);
srgb_ = (GPU_texture_format(attach.tex) == GPU_SRGB8_A8);
}
else {
this->viewport_reset();
this->scissor_reset();
/* Empty frame-buffer. */
width_ = 0;
height_ = 0;
}
/* Reset viewport and Scissor. */
this->viewport_reset();
this->scissor_reset();
/* We have now updated our internal structures. */
dirty_attachments_ = false;
}

View File

@ -117,39 +117,20 @@ void GLStorageBuf::unbind()
slot_ = 0;
}
void GLStorageBuf::clear(Span<uint32_t> data)
void GLStorageBuf::clear(uint32_t clear_value)
{
if (ssbo_id_ == 0) {
this->init();
}
GLenum internal_format = GL_R32UI;
GLenum format = GL_RED_INTEGER;
if (data.size() == 1) {
internal_format = GL_R32UI;
format = GL_RED_INTEGER;
}
else if (data.size() == 2) {
internal_format = GL_RG32UI;
format = GL_RG_INTEGER;
}
else if (data.size() == 3) {
internal_format = GL_RGB32UI;
format = GL_RGB_INTEGER;
}
else if (data.size() == 4) {
internal_format = GL_RGBA32UI;
format = GL_RGBA_INTEGER;
}
if (GLContext::direct_state_access_support) {
glClearNamedBufferData(ssbo_id_, internal_format, format, GL_UNSIGNED_INT, data.data());
glClearNamedBufferData(ssbo_id_, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, &clear_value);
}
else {
/* WATCH(@fclem): This should be ok since we only use clear outside of drawing functions. */
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo_id_);
glClearBufferData(
GL_SHADER_STORAGE_BUFFER, internal_format, format, GL_UNSIGNED_INT, data.data());
GL_SHADER_STORAGE_BUFFER, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, &clear_value);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
}
}

View File

@ -33,7 +33,7 @@ class GLStorageBuf : public StorageBuf {
void update(const void *data) override;
void bind(int slot) override;
void unbind() override;
void clear(Span<uint32_t> data) override;
void clear(uint32_t clear_value) override;
void copy_sub(VertBuf *src, uint dst_offset, uint src_offset, uint copy_size) override;
void read(void *data) override;

View File

@ -0,0 +1,68 @@
/** \param f: Offset from texel center in pixel space. */
void cubic_bspline_coefficients(vec2 f, out vec2 w0, out vec2 w1, out vec2 w2, out vec2 w3)
{
vec2 f2 = f * f;
vec2 f3 = f2 * f;
/* Optimized formulae for cubic B-Spline coefficients. */
w3 = f3 / 6.0;
w0 = -w3 + f2 * 0.5 - f * 0.5 + 1.0 / 6.0;
w1 = f3 * 0.5 - f2 * 1.0 + 2.0 / 3.0;
w2 = 1.0 - w0 - w1 - w3;
}
/* Samples the given 2D sampler at the given coordinates using Bicubic interpolation. This function
* uses an optimized algorithm which assumes a linearly filtered sampler, so the caller needs to
* take that into account when setting up the sampler. */
vec4 texture_bicubic(sampler2D sampler_2d, vec2 coordinates)
{
vec2 texture_size = vec2(textureSize(sampler_2d, 0).xy);
coordinates.xy *= texture_size;
vec2 w0, w1, w2, w3;
vec2 texel_center = floor(coordinates.xy - 0.5) + 0.5;
cubic_bspline_coefficients(coordinates.xy - texel_center, w0, w1, w2, w3);
#if 1 /* Optimized version using 4 filtered taps. */
vec2 s0 = w0 + w1;
vec2 s1 = w2 + w3;
vec2 f0 = w1 / (w0 + w1);
vec2 f1 = w3 / (w2 + w3);
vec4 sampling_coordinates;
sampling_coordinates.xy = texel_center - 1.0 + f0;
sampling_coordinates.zw = texel_center + 1.0 + f1;
sampling_coordinates /= texture_size.xyxy;
vec4 sampled_color = textureLod(sampler_2d, sampling_coordinates.xy, 0.0) * s0.x * s0.y;
sampled_color += textureLod(sampler_2d, sampling_coordinates.zy, 0.0) * s1.x * s0.y;
sampled_color += textureLod(sampler_2d, sampling_coordinates.xw, 0.0) * s0.x * s1.y;
sampled_color += textureLod(sampler_2d, sampling_coordinates.zw, 0.0) * s1.x * s1.y;
return sampled_color;
#else /* Reference brute-force 16 taps. */
vec4 color = texelFetch(sampler_2d, ivec2(texel_center + vec2(-1.0, -1.0)), 0) * w0.x * w0.y;
color += texelFetch(sampler_2d, ivec2(texel_center + vec2(0.0, -1.0)), 0) * w1.x * w0.y;
color += texelFetch(sampler_2d, ivec2(texel_center + vec2(1.0, -1.0)), 0) * w2.x * w0.y;
color += texelFetch(sampler_2d, ivec2(texel_center + vec2(2.0, -1.0)), 0) * w3.x * w0.y;
color += texelFetch(sampler_2d, ivec2(texel_center + vec2(-1.0, 0.0)), 0) * w0.x * w1.y;
color += texelFetch(sampler_2d, ivec2(texel_center + vec2(0.0, 0.0)), 0) * w1.x * w1.y;
color += texelFetch(sampler_2d, ivec2(texel_center + vec2(1.0, 0.0)), 0) * w2.x * w1.y;
color += texelFetch(sampler_2d, ivec2(texel_center + vec2(2.0, 0.0)), 0) * w3.x * w1.y;
color += texelFetch(sampler_2d, ivec2(texel_center + vec2(-1.0, 1.0)), 0) * w0.x * w2.y;
color += texelFetch(sampler_2d, ivec2(texel_center + vec2(0.0, 1.0)), 0) * w1.x * w2.y;
color += texelFetch(sampler_2d, ivec2(texel_center + vec2(1.0, 1.0)), 0) * w2.x * w2.y;
color += texelFetch(sampler_2d, ivec2(texel_center + vec2(2.0, 1.0)), 0) * w3.x * w2.y;
color += texelFetch(sampler_2d, ivec2(texel_center + vec2(-1.0, 2.0)), 0) * w0.x * w3.y;
color += texelFetch(sampler_2d, ivec2(texel_center + vec2(0.0, 2.0)), 0) * w1.x * w3.y;
color += texelFetch(sampler_2d, ivec2(texel_center + vec2(1.0, 2.0)), 0) * w2.x * w3.y;
color += texelFetch(sampler_2d, ivec2(texel_center + vec2(2.0, 2.0)), 0) * w3.x * w3.y;
return color;
#endif
}

View File

@ -1,3 +1,5 @@
#pragma BLENDER_REQUIRE(gpu_shader_bicubic_sampler_lib.glsl)
void point_texco_remap_square(vec3 vin, out vec3 vout)
{
vout = vin * 2.0 - 1.0;
@ -54,68 +56,9 @@ void node_tex_image_linear(vec3 co, sampler2D ima, out vec4 color, out float alp
alpha = color.a;
}
/** \param f: Signed distance to texel center. */
void cubic_bspline_coefs(vec2 f, out vec2 w0, out vec2 w1, out vec2 w2, out vec2 w3)
{
vec2 f2 = f * f;
vec2 f3 = f2 * f;
/* Bspline coefs (optimized) */
w3 = f3 / 6.0;
w0 = -w3 + f2 * 0.5 - f * 0.5 + 1.0 / 6.0;
w1 = f3 * 0.5 - f2 * 1.0 + 2.0 / 3.0;
w2 = 1.0 - w0 - w1 - w3;
}
void node_tex_image_cubic(vec3 co, sampler2D ima, out vec4 color, out float alpha)
{
vec2 tex_size = vec2(textureSize(ima, 0).xy);
co.xy *= tex_size;
/* texel center */
vec2 tc = floor(co.xy - 0.5) + 0.5;
vec2 w0, w1, w2, w3;
cubic_bspline_coefs(co.xy - tc, w0, w1, w2, w3);
#if 1 /* Optimized version using 4 filtered tap. */
vec2 s0 = w0 + w1;
vec2 s1 = w2 + w3;
vec2 f0 = w1 / (w0 + w1);
vec2 f1 = w3 / (w2 + w3);
vec4 final_co;
final_co.xy = tc - 1.0 + f0;
final_co.zw = tc + 1.0 + f1;
final_co /= tex_size.xyxy;
color = safe_color(textureLod(ima, final_co.xy, 0.0)) * s0.x * s0.y;
color += safe_color(textureLod(ima, final_co.zy, 0.0)) * s1.x * s0.y;
color += safe_color(textureLod(ima, final_co.xw, 0.0)) * s0.x * s1.y;
color += safe_color(textureLod(ima, final_co.zw, 0.0)) * s1.x * s1.y;
#else /* Reference bruteforce 16 tap. */
color = texelFetch(ima, ivec2(tc + vec2(-1.0, -1.0)), 0) * w0.x * w0.y;
color += texelFetch(ima, ivec2(tc + vec2(0.0, -1.0)), 0) * w1.x * w0.y;
color += texelFetch(ima, ivec2(tc + vec2(1.0, -1.0)), 0) * w2.x * w0.y;
color += texelFetch(ima, ivec2(tc + vec2(2.0, -1.0)), 0) * w3.x * w0.y;
color += texelFetch(ima, ivec2(tc + vec2(-1.0, 0.0)), 0) * w0.x * w1.y;
color += texelFetch(ima, ivec2(tc + vec2(0.0, 0.0)), 0) * w1.x * w1.y;
color += texelFetch(ima, ivec2(tc + vec2(1.0, 0.0)), 0) * w2.x * w1.y;
color += texelFetch(ima, ivec2(tc + vec2(2.0, 0.0)), 0) * w3.x * w1.y;
color += texelFetch(ima, ivec2(tc + vec2(-1.0, 1.0)), 0) * w0.x * w2.y;
color += texelFetch(ima, ivec2(tc + vec2(0.0, 1.0)), 0) * w1.x * w2.y;
color += texelFetch(ima, ivec2(tc + vec2(1.0, 1.0)), 0) * w2.x * w2.y;
color += texelFetch(ima, ivec2(tc + vec2(2.0, 1.0)), 0) * w3.x * w2.y;
color += texelFetch(ima, ivec2(tc + vec2(-1.0, 2.0)), 0) * w0.x * w3.y;
color += texelFetch(ima, ivec2(tc + vec2(0.0, 2.0)), 0) * w1.x * w3.y;
color += texelFetch(ima, ivec2(tc + vec2(1.0, 2.0)), 0) * w2.x * w3.y;
color += texelFetch(ima, ivec2(tc + vec2(2.0, 2.0)), 0) * w3.x * w3.y;
#endif
color = safe_color(texture_bicubic(ima, co.xy));
alpha = color.a;
}
@ -263,7 +206,7 @@ void node_tex_tile_cubic(
/* texel center */
vec2 tc = floor(co.xy - 0.5) + 0.5;
vec2 w0, w1, w2, w3;
cubic_bspline_coefs(co.xy - tc, w0, w1, w2, w3);
cubic_bspline_coefficients(co.xy - tc, w0, w1, w2, w3);
vec2 s0 = w0 + w1;
vec2 s1 = w2 + w3;

View File

@ -71,59 +71,29 @@ static void test_storage_buffer_clear_zero()
GPU_storagebuf_free(ssbo);
}
GPU_TEST(storage_buffer_clear_zero);
template<int DataLen> static void storage_buffer_clear_int_uniform()
static void test_storage_buffer_clear()
{
GPUStorageBuf *ssbo = GPU_storagebuf_create_ex(
SIZE_IN_BYTES, nullptr, GPU_USAGE_STATIC, __func__);
EXPECT_NE(ssbo, nullptr);
/* Read back data from SSBO. */
int4 clear_data = {-1, -1, -1, -1};
GPU_storagebuf_clear_int(ssbo, clear_data, DataLen);
GPU_storagebuf_clear(ssbo, 157255);
/* Check if data is the same. */
/* Read back data from SSBO. */
Vector<int32_t> read_data;
read_data.resize(SIZE, 0);
GPU_storagebuf_read(ssbo, read_data.data());
/* Check if datatest_ is the same. */
for (int i : IndexRange(SIZE)) {
EXPECT_EQ(clear_data[i % DataLen], read_data[i]);
EXPECT_EQ(157255, read_data[i]);
}
GPU_storagebuf_free(ssbo);
}
static void test_storage_buffer_clear_int_uniform()
{
storage_buffer_clear_int_uniform<1>();
storage_buffer_clear_int_uniform<2>();
storage_buffer_clear_int_uniform<3>();
storage_buffer_clear_int_uniform<4>();
}
GPU_TEST(storage_buffer_clear_int_uniform);
static void test_storage_buffer_clear_non_uniform()
{
GPUStorageBuf *ssbo = GPU_storagebuf_create_ex(
SIZE_IN_BYTES, nullptr, GPU_USAGE_STATIC, __func__);
EXPECT_NE(ssbo, nullptr);
/* Read back data from SSBO. */
int4 clear_data = {-1, -2, -3, -4};
GPU_storagebuf_clear_int(ssbo, clear_data, 4);
/* Check if data is the same. */
Vector<int32_t> read_data;
read_data.resize(SIZE, 0);
GPU_storagebuf_read(ssbo, read_data.data());
for (int i : IndexRange(SIZE)) {
EXPECT_EQ(clear_data[i % 4], read_data[i]);
}
GPU_storagebuf_free(ssbo);
}
GPU_TEST(storage_buffer_clear_non_uniform);
GPU_TEST(storage_buffer_clear);
} // namespace blender::gpu::tests

View File

@ -86,30 +86,10 @@ bool VKBuffer::update(VKContext &context, const void *data)
return result;
}
static bool is_uniform(Span<uint32_t> data)
{
BLI_assert(!data.is_empty());
uint32_t expected_value = data[0];
for (uint32_t value : data.drop_front(1)) {
if (value != expected_value) {
return false;
}
}
return true;
}
void VKBuffer::clear(VKContext &context, Span<uint32_t> data)
void VKBuffer::clear(VKContext &context, uint32_t clear_value)
{
VKCommandBuffer &command_buffer = context.command_buffer_get();
if (is_uniform(data)) {
command_buffer.fill(*this, *data.begin());
return;
}
/* TODO: Use a compute shader to clear the buffer. This is performance wise not recommended, and
* should be avoided. There are some cases where we don't have a choice. Especially when using
* compute shaders.*/
BLI_assert_unreachable();
command_buffer.fill(*this, clear_value);
}
bool VKBuffer::map(VKContext &context, void **r_mapped_memory) const

View File

@ -34,7 +34,7 @@ class VKBuffer {
GPUUsageType usage,
VkBufferUsageFlagBits buffer_usage);
bool update(VKContext &context, const void *data);
void clear(VKContext &context, Span<uint32_t> data);
void clear(VKContext &context, uint32_t clear_value);
bool free(VKContext &context);
bool map(VKContext &context, void **r_mapped_memory) const;
void unmap(VKContext &context) const;

View File

@ -47,13 +47,13 @@ void VKStorageBuffer::unbind()
{
}
void VKStorageBuffer::clear(Span<uint32_t> data)
void VKStorageBuffer::clear(uint32_t clear_value)
{
VKContext &context = *VKContext::get();
if (!buffer_.is_allocated()) {
allocate(context);
}
buffer_.clear(context, data);
buffer_.clear(context, clear_value);
}
void VKStorageBuffer::copy_sub(VertBuf * /*src*/,

View File

@ -30,7 +30,7 @@ class VKStorageBuffer : public StorageBuf {
void update(const void *data) override;
void bind(int slot) override;
void unbind() override;
void clear(Span<uint32_t> data) override;
void clear(uint32_t clear_value) override;
void copy_sub(VertBuf *src, uint dst_offset, uint src_offset, uint copy_size) override;
void read(void *data) override;

View File

@ -50,7 +50,7 @@ class PlyExportTest : public BlendfileLoadingBaseTest {
std::string get_temp_ply_filename(const std::string &filename)
{
return std::string(BKE_tempdir_session()) + "/" + filename;
return std::string(BKE_tempdir_session()) + SEP_STR + filename;
}
};
@ -125,7 +125,7 @@ static std::vector<char> read_temp_file_in_vectorchar(const std::string &file_pa
TEST_F(PlyExportTest, WriteHeaderAscii)
{
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params;
PLYExportParams _params = {};
_params.ascii_format = true;
_params.export_normals = false;
_params.vertex_colors = PLY_VERTEX_COLOR_NONE;
@ -163,7 +163,7 @@ TEST_F(PlyExportTest, WriteHeaderAscii)
TEST_F(PlyExportTest, WriteHeaderBinary)
{
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params;
PLYExportParams _params = {};
_params.ascii_format = false;
_params.export_normals = false;
_params.vertex_colors = PLY_VERTEX_COLOR_NONE;
@ -201,7 +201,7 @@ TEST_F(PlyExportTest, WriteHeaderBinary)
TEST_F(PlyExportTest, WriteVerticesAscii)
{
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params;
PLYExportParams _params = {};
_params.ascii_format = true;
_params.export_normals = false;
_params.vertex_colors = PLY_VERTEX_COLOR_NONE;
@ -233,7 +233,7 @@ TEST_F(PlyExportTest, WriteVerticesAscii)
TEST_F(PlyExportTest, WriteVerticesBinary)
{
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params;
PLYExportParams _params = {};
_params.ascii_format = false;
_params.export_normals = false;
_params.vertex_colors = PLY_VERTEX_COLOR_NONE;
@ -275,7 +275,7 @@ TEST_F(PlyExportTest, WriteVerticesBinary)
TEST_F(PlyExportTest, WriteFacesAscii)
{
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params;
PLYExportParams _params = {};
_params.ascii_format = true;
_params.export_normals = false;
_params.vertex_colors = PLY_VERTEX_COLOR_NONE;
@ -305,7 +305,7 @@ TEST_F(PlyExportTest, WriteFacesAscii)
TEST_F(PlyExportTest, WriteFacesBinary)
{
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params;
PLYExportParams _params = {};
_params.ascii_format = false;
_params.export_normals = false;
_params.vertex_colors = PLY_VERTEX_COLOR_NONE;
@ -348,7 +348,7 @@ TEST_F(PlyExportTest, WriteFacesBinary)
TEST_F(PlyExportTest, WriteVertexNormalsAscii)
{
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params;
PLYExportParams _params = {};
_params.ascii_format = true;
_params.export_normals = true;
_params.vertex_colors = PLY_VERTEX_COLOR_NONE;
@ -380,7 +380,7 @@ TEST_F(PlyExportTest, WriteVertexNormalsAscii)
TEST_F(PlyExportTest, WriteVertexNormalsBinary)
{
std::string filePath = get_temp_ply_filename(temp_file_path);
PLYExportParams _params;
PLYExportParams _params = {};
_params.ascii_format = false;
_params.export_normals = true;
_params.vertex_colors = PLY_VERTEX_COLOR_NONE;
@ -450,34 +450,34 @@ class ply_exporter_ply_data_test : public PlyExportTest {
TEST_F(ply_exporter_ply_data_test, CubeLoadPLYDataVertices)
{
PLYExportParams params;
PlyData plyData = load_ply_data_from_blendfile("io_tests/blend_geometry/cube_all_data.blend",
params);
PLYExportParams params = {};
PlyData plyData = load_ply_data_from_blendfile(
"io_tests" SEP_STR "blend_geometry" SEP_STR "cube_all_data.blend", params);
EXPECT_EQ(plyData.vertices.size(), 8);
}
TEST_F(ply_exporter_ply_data_test, CubeLoadPLYDataUV)
{
PLYExportParams params;
PLYExportParams params = {};
params.export_uv = true;
PlyData plyData = load_ply_data_from_blendfile("io_tests/blend_geometry/cube_all_data.blend",
params);
PlyData plyData = load_ply_data_from_blendfile(
"io_tests" SEP_STR "blend_geometry" SEP_STR "cube_all_data.blend", params);
EXPECT_EQ(plyData.uv_coordinates.size(), 8);
}
TEST_F(ply_exporter_ply_data_test, SuzanneLoadPLYDataUV)
{
PLYExportParams params;
PLYExportParams params = {};
params.export_uv = true;
PlyData plyData = load_ply_data_from_blendfile("io_tests/blend_geometry/suzanne_all_data.blend",
params);
EXPECT_EQ(plyData.uv_coordinates.size(), 541);
PlyData plyData = load_ply_data_from_blendfile(
"io_tests" SEP_STR "blend_geometry" SEP_STR "suzanne_all_data.blend", params);
EXPECT_EQ(plyData.uv_coordinates.size(), 542);
}
TEST_F(ply_exporter_ply_data_test, CubeLoadPLYDataUVDisabled)
{
PLYExportParams params;
PLYExportParams params = {};
params.export_uv = false;
PlyData plyData = load_ply_data_from_blendfile("io_tests/blend_geometry/cube_all_data.blend",
params);
PlyData plyData = load_ply_data_from_blendfile(
"io_tests" SEP_STR "blend_geometry" SEP_STR "cube_all_data.blend", params);
EXPECT_EQ(plyData.uv_coordinates.size(), 0);
}

View File

@ -50,7 +50,7 @@ class obj_exporter_test : public BlendfileLoadingBaseTest {
}
};
const std::string all_objects_file = "io_tests/blend_scene/all_objects.blend";
const std::string all_objects_file = "io_tests" SEP_STR "blend_scene" SEP_STR "all_objects.blend";
TEST_F(obj_exporter_test, filter_objects_curves_as_mesh)
{
@ -93,8 +93,8 @@ TEST_F(obj_exporter_test, filter_objects_selected)
TEST(obj_exporter_utils, append_negative_frame_to_filename)
{
const char path_original[FILE_MAX] = "/my_file.obj";
const char path_truth[FILE_MAX] = "/my_file-123.obj";
const char path_original[FILE_MAX] = SEP_STR "my_file.obj";
const char path_truth[FILE_MAX] = SEP_STR "my_file-123.obj";
const int frame = -123;
char path_with_frame[FILE_MAX] = {0};
const bool ok = append_frame_to_filename(path_original, frame, path_with_frame);
@ -104,8 +104,8 @@ TEST(obj_exporter_utils, append_negative_frame_to_filename)
TEST(obj_exporter_utils, append_positive_frame_to_filename)
{
const char path_original[FILE_MAX] = "/my_file.obj";
const char path_truth[FILE_MAX] = "/my_file123.obj";
const char path_original[FILE_MAX] = SEP_STR "my_file.obj";
const char path_truth[FILE_MAX] = SEP_STR "my_file123.obj";
const int frame = 123;
char path_with_frame[FILE_MAX] = {0};
const bool ok = append_frame_to_filename(path_original, frame, path_with_frame);
@ -148,7 +148,7 @@ TEST(obj_exporter_writer, header)
{
/* Because testing doesn't fully initialize Blender, we need the following. */
BKE_tempdir_init(nullptr);
std::string out_file_path = blender::tests::flags_test_release_dir() + "/" + temp_file_path;
std::string out_file_path = blender::tests::flags_test_release_dir() + SEP_STR + temp_file_path;
{
OBJExportParamsDefault _export;
std::unique_ptr<OBJWriter> writer = init_writer(_export.params, out_file_path);
@ -166,7 +166,7 @@ TEST(obj_exporter_writer, header)
TEST(obj_exporter_writer, mtllib)
{
std::string out_file_path = blender::tests::flags_test_release_dir() + "/" + temp_file_path;
std::string out_file_path = blender::tests::flags_test_release_dir() + SEP_STR + temp_file_path;
{
OBJExportParamsDefault _export;
std::unique_ptr<OBJWriter> writer = init_writer(_export.params, out_file_path);
@ -263,7 +263,7 @@ class obj_exporter_regression_test : public obj_exporter_test {
std::string out_file_path = tempdir + BLI_path_basename(golden_obj.c_str());
strncpy(params.filepath, out_file_path.c_str(), FILE_MAX - 1);
params.blen_filepath = bfile->main->filepath;
std::string golden_file_path = blender::tests::flags_test_asset_dir() + "/" + golden_obj;
std::string golden_file_path = blender::tests::flags_test_asset_dir() + SEP_STR + golden_obj;
BLI_split_dir_part(golden_file_path.c_str(), params.file_base_for_tests, PATH_MAX);
export_frame(depsgraph, params, out_file_path.c_str());
std::string output_str = read_temp_file_in_string(out_file_path);
@ -280,7 +280,8 @@ class obj_exporter_regression_test : public obj_exporter_test {
if (!golden_mtl.empty()) {
std::string out_mtl_file_path = tempdir + BLI_path_basename(golden_mtl.c_str());
std::string output_mtl_str = read_temp_file_in_string(out_mtl_file_path);
std::string golden_mtl_file_path = blender::tests::flags_test_asset_dir() + "/" + golden_mtl;
std::string golden_mtl_file_path = blender::tests::flags_test_asset_dir() + SEP_STR +
golden_mtl;
std::string golden_mtl_str = read_temp_file_in_string(golden_mtl_file_path);
are_equal = strings_equal_after_first_lines(output_mtl_str, golden_mtl_str);
if (save_failing_test_output && !are_equal) {
@ -297,9 +298,9 @@ class obj_exporter_regression_test : public obj_exporter_test {
TEST_F(obj_exporter_regression_test, all_tris)
{
OBJExportParamsDefault _export;
compare_obj_export_to_golden("io_tests/blend_geometry/all_tris.blend",
"io_tests/obj/all_tris.obj",
"io_tests/obj/all_tris.mtl",
compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "all_tris.blend",
"io_tests" SEP_STR "obj" SEP_STR "all_tris.obj",
"io_tests" SEP_STR "obj" SEP_STR "all_tris.mtl",
_export.params);
}
@ -308,8 +309,10 @@ TEST_F(obj_exporter_regression_test, all_quads)
OBJExportParamsDefault _export;
_export.params.global_scale = 2.0f;
_export.params.export_materials = false;
compare_obj_export_to_golden(
"io_tests/blend_geometry/all_quads.blend", "io_tests/obj/all_quads.obj", "", _export.params);
compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "all_quads.blend",
"io_tests" SEP_STR "obj" SEP_STR "all_quads.obj",
"",
_export.params);
}
TEST_F(obj_exporter_regression_test, fgons)
@ -318,8 +321,10 @@ TEST_F(obj_exporter_regression_test, fgons)
_export.params.forward_axis = IO_AXIS_Y;
_export.params.up_axis = IO_AXIS_Z;
_export.params.export_materials = false;
compare_obj_export_to_golden(
"io_tests/blend_geometry/fgons.blend", "io_tests/obj/fgons.obj", "", _export.params);
compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "fgons.blend",
"io_tests" SEP_STR "obj" SEP_STR "fgons.obj",
"",
_export.params);
}
TEST_F(obj_exporter_regression_test, edges)
@ -328,8 +333,10 @@ TEST_F(obj_exporter_regression_test, edges)
_export.params.forward_axis = IO_AXIS_Y;
_export.params.up_axis = IO_AXIS_Z;
_export.params.export_materials = false;
compare_obj_export_to_golden(
"io_tests/blend_geometry/edges.blend", "io_tests/obj/edges.obj", "", _export.params);
compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "edges.blend",
"io_tests" SEP_STR "obj" SEP_STR "edges.obj",
"",
_export.params);
}
TEST_F(obj_exporter_regression_test, vertices)
@ -338,16 +345,19 @@ TEST_F(obj_exporter_regression_test, vertices)
_export.params.forward_axis = IO_AXIS_Y;
_export.params.up_axis = IO_AXIS_Z;
_export.params.export_materials = false;
compare_obj_export_to_golden(
"io_tests/blend_geometry/vertices.blend", "io_tests/obj/vertices.obj", "", _export.params);
compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "vertices.blend",
"io_tests" SEP_STR "obj" SEP_STR "vertices.obj",
"",
_export.params);
}
TEST_F(obj_exporter_regression_test, non_uniform_scale)
{
OBJExportParamsDefault _export;
_export.params.export_materials = false;
compare_obj_export_to_golden("io_tests/blend_geometry/non_uniform_scale.blend",
"io_tests/obj/non_uniform_scale.obj",
compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR
"non_uniform_scale.blend",
"io_tests" SEP_STR "obj" SEP_STR "non_uniform_scale.obj",
"",
_export.params);
}
@ -359,8 +369,10 @@ TEST_F(obj_exporter_regression_test, nurbs_as_nurbs)
_export.params.up_axis = IO_AXIS_Z;
_export.params.export_materials = false;
_export.params.export_curves_as_nurbs = true;
compare_obj_export_to_golden(
"io_tests/blend_geometry/nurbs.blend", "io_tests/obj/nurbs.obj", "", _export.params);
compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "nurbs.blend",
"io_tests" SEP_STR "obj" SEP_STR "nurbs.obj",
"",
_export.params);
}
TEST_F(obj_exporter_regression_test, nurbs_curves_as_nurbs)
@ -370,8 +382,8 @@ TEST_F(obj_exporter_regression_test, nurbs_curves_as_nurbs)
_export.params.up_axis = IO_AXIS_Z;
_export.params.export_materials = false;
_export.params.export_curves_as_nurbs = true;
compare_obj_export_to_golden("io_tests/blend_geometry/nurbs_curves.blend",
"io_tests/obj/nurbs_curves.obj",
compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "nurbs_curves.blend",
"io_tests" SEP_STR "obj" SEP_STR "nurbs_curves.obj",
"",
_export.params);
}
@ -383,8 +395,10 @@ TEST_F(obj_exporter_regression_test, nurbs_as_mesh)
_export.params.up_axis = IO_AXIS_Z;
_export.params.export_materials = false;
_export.params.export_curves_as_nurbs = false;
compare_obj_export_to_golden(
"io_tests/blend_geometry/nurbs.blend", "io_tests/obj/nurbs_mesh.obj", "", _export.params);
compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "nurbs.blend",
"io_tests" SEP_STR "obj" SEP_STR "nurbs_mesh.obj",
"",
_export.params);
}
TEST_F(obj_exporter_regression_test, cube_all_data_triangulated)
@ -394,8 +408,8 @@ TEST_F(obj_exporter_regression_test, cube_all_data_triangulated)
_export.params.up_axis = IO_AXIS_Z;
_export.params.export_materials = false;
_export.params.export_triangulated_mesh = true;
compare_obj_export_to_golden("io_tests/blend_geometry/cube_all_data.blend",
"io_tests/obj/cube_all_data_triangulated.obj",
compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "cube_all_data.blend",
"io_tests" SEP_STR "obj" SEP_STR "cube_all_data_triangulated.obj",
"",
_export.params);
}
@ -406,8 +420,9 @@ TEST_F(obj_exporter_regression_test, cube_normal_edit)
_export.params.forward_axis = IO_AXIS_Y;
_export.params.up_axis = IO_AXIS_Z;
_export.params.export_materials = false;
compare_obj_export_to_golden("io_tests/blend_geometry/cube_normal_edit.blend",
"io_tests/obj/cube_normal_edit.obj",
compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR
"cube_normal_edit.blend",
"io_tests" SEP_STR "obj" SEP_STR "cube_normal_edit.obj",
"",
_export.params);
}
@ -419,8 +434,9 @@ TEST_F(obj_exporter_regression_test, cube_vertex_groups)
_export.params.export_normals = false;
_export.params.export_uv = false;
_export.params.export_vertex_groups = true;
compare_obj_export_to_golden("io_tests/blend_geometry/cube_vertex_groups.blend",
"io_tests/obj/cube_vertex_groups.obj",
compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR
"cube_vertex_groups.blend",
"io_tests" SEP_STR "obj" SEP_STR "cube_vertex_groups.obj",
"",
_export.params);
}
@ -430,8 +446,9 @@ TEST_F(obj_exporter_regression_test, cubes_positioned)
OBJExportParamsDefault _export;
_export.params.export_materials = false;
_export.params.global_scale = 2.0f;
compare_obj_export_to_golden("io_tests/blend_geometry/cubes_positioned.blend",
"io_tests/obj/cubes_positioned.obj",
compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR
"cubes_positioned.blend",
"io_tests" SEP_STR "obj" SEP_STR "cubes_positioned.obj",
"",
_export.params);
}
@ -443,8 +460,9 @@ TEST_F(obj_exporter_regression_test, cubes_vertex_colors)
_export.params.export_normals = false;
_export.params.export_uv = false;
_export.params.export_materials = false;
compare_obj_export_to_golden("io_tests/blend_geometry/cubes_vertex_colors.blend",
"io_tests/obj/cubes_vertex_colors.obj",
compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR
"cubes_vertex_colors.blend",
"io_tests" SEP_STR "obj" SEP_STR "cubes_vertex_colors.obj",
"",
_export.params);
}
@ -453,9 +471,10 @@ TEST_F(obj_exporter_regression_test, cubes_with_textures_strip)
{
OBJExportParamsDefault _export;
_export.params.path_mode = PATH_REFERENCE_STRIP;
compare_obj_export_to_golden("io_tests/blend_geometry/cubes_with_textures.blend",
"io_tests/obj/cubes_with_textures.obj",
"io_tests/obj/cubes_with_textures.mtl",
compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR
"cubes_with_textures.blend",
"io_tests" SEP_STR "obj" SEP_STR "cubes_with_textures.obj",
"io_tests" SEP_STR "obj" SEP_STR "cubes_with_textures.mtl",
_export.params);
}
@ -463,9 +482,10 @@ TEST_F(obj_exporter_regression_test, cubes_with_textures_relative)
{
OBJExportParamsDefault _export;
_export.params.path_mode = PATH_REFERENCE_RELATIVE;
compare_obj_export_to_golden("io_tests/blend_geometry/cubes_with_textures.blend",
"io_tests/obj/cubes_with_textures_rel.obj",
"io_tests/obj/cubes_with_textures_rel.mtl",
compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR
"cubes_with_textures.blend",
"io_tests" SEP_STR "obj" SEP_STR "cubes_with_textures_rel.obj",
"io_tests" SEP_STR "obj" SEP_STR "cubes_with_textures_rel.mtl",
_export.params);
}
@ -476,8 +496,9 @@ TEST_F(obj_exporter_regression_test, suzanne_all_data)
_export.params.up_axis = IO_AXIS_Z;
_export.params.export_materials = false;
_export.params.export_smooth_groups = true;
compare_obj_export_to_golden("io_tests/blend_geometry/suzanne_all_data.blend",
"io_tests/obj/suzanne_all_data.obj",
compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR
"suzanne_all_data.blend",
"io_tests" SEP_STR "obj" SEP_STR "suzanne_all_data.obj",
"",
_export.params);
}
@ -486,8 +507,10 @@ TEST_F(obj_exporter_regression_test, all_curves)
{
OBJExportParamsDefault _export;
_export.params.export_materials = false;
compare_obj_export_to_golden(
"io_tests/blend_scene/all_curves.blend", "io_tests/obj/all_curves.obj", "", _export.params);
compare_obj_export_to_golden("io_tests" SEP_STR "blend_scene" SEP_STR "all_curves.blend",
"io_tests" SEP_STR "obj" SEP_STR "all_curves.obj",
"",
_export.params);
}
TEST_F(obj_exporter_regression_test, all_curves_as_nurbs)
@ -495,8 +518,8 @@ TEST_F(obj_exporter_regression_test, all_curves_as_nurbs)
OBJExportParamsDefault _export;
_export.params.export_materials = false;
_export.params.export_curves_as_nurbs = true;
compare_obj_export_to_golden("io_tests/blend_scene/all_curves.blend",
"io_tests/obj/all_curves_as_nurbs.obj",
compare_obj_export_to_golden("io_tests" SEP_STR "blend_scene" SEP_STR "all_curves.blend",
"io_tests" SEP_STR "obj" SEP_STR "all_curves_as_nurbs.obj",
"",
_export.params);
}
@ -508,9 +531,9 @@ TEST_F(obj_exporter_regression_test, all_objects)
_export.params.up_axis = IO_AXIS_Z;
_export.params.export_smooth_groups = true;
_export.params.export_colors = true;
compare_obj_export_to_golden("io_tests/blend_scene/all_objects.blend",
"io_tests/obj/all_objects.obj",
"io_tests/obj/all_objects.mtl",
compare_obj_export_to_golden("io_tests" SEP_STR "blend_scene" SEP_STR "all_objects.blend",
"io_tests" SEP_STR "obj" SEP_STR "all_objects.obj",
"io_tests" SEP_STR "obj" SEP_STR "all_objects.mtl",
_export.params);
}
@ -521,9 +544,9 @@ TEST_F(obj_exporter_regression_test, all_objects_mat_groups)
_export.params.up_axis = IO_AXIS_Z;
_export.params.export_smooth_groups = true;
_export.params.export_material_groups = true;
compare_obj_export_to_golden("io_tests/blend_scene/all_objects.blend",
"io_tests/obj/all_objects_mat_groups.obj",
"io_tests/obj/all_objects_mat_groups.mtl",
compare_obj_export_to_golden("io_tests" SEP_STR "blend_scene" SEP_STR "all_objects.blend",
"io_tests" SEP_STR "obj" SEP_STR "all_objects_mat_groups.obj",
"io_tests" SEP_STR "obj" SEP_STR "all_objects_mat_groups.mtl",
_export.params);
}
@ -532,9 +555,9 @@ TEST_F(obj_exporter_regression_test, materials_without_pbr)
OBJExportParamsDefault _export;
_export.params.export_normals = false;
_export.params.path_mode = PATH_REFERENCE_RELATIVE;
compare_obj_export_to_golden("io_tests/blend_geometry/materials_pbr.blend",
"io_tests/obj/materials_without_pbr.obj",
"io_tests/obj/materials_without_pbr.mtl",
compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "materials_pbr.blend",
"io_tests" SEP_STR "obj" SEP_STR "materials_without_pbr.obj",
"io_tests" SEP_STR "obj" SEP_STR "materials_without_pbr.mtl",
_export.params);
}
@ -544,9 +567,9 @@ TEST_F(obj_exporter_regression_test, materials_pbr)
_export.params.export_normals = false;
_export.params.path_mode = PATH_REFERENCE_RELATIVE;
_export.params.export_pbr_extensions = true;
compare_obj_export_to_golden("io_tests/blend_geometry/materials_pbr.blend",
"io_tests/obj/materials_pbr.obj",
"io_tests/obj/materials_pbr.mtl",
compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "materials_pbr.blend",
"io_tests" SEP_STR "obj" SEP_STR "materials_pbr.obj",
"io_tests" SEP_STR "obj" SEP_STR "materials_pbr.mtl",
_export.params);
}

View File

@ -72,6 +72,12 @@ typedef struct AssetMetaData {
/** Optional description of this asset for display in the UI. Dynamic length. */
char *description;
/** Optional copyright of this asset for display in the UI. Dynamic length. */
char *copyright;
/** Optional license of this asset for display in the UI. Dynamic length. */
char *license;
/** User defined tags for this asset. The asset manager uses these for filtering, but how they
* function exactly (e.g. how they are registered to provide a list of searchable available tags)
* is up to the asset-engine. */

View File

@ -580,7 +580,7 @@ enum {
SEQ_USE_PROXY = (1 << 15),
SEQ_IGNORE_CHANNEL_LOCK = (1 << 16),
SEQ_AUTO_PLAYBACK_RATE = (1 << 17),
SEQ_FLAG_UNUSED_18 = (1 << 18), /* cleared */
SEQ_SINGLE_FRAME_CONTENT = (1 << 18),
SEQ_FLAG_UNUSED_19 = (1 << 19), /* cleared */
SEQ_FLAG_UNUSED_21 = (1 << 21), /* cleared */

View File

@ -552,6 +552,7 @@ enum {
V3D_OVERLAY_SCULPT_SHOW_MASK = (1 << 14),
V3D_OVERLAY_SCULPT_SHOW_FACE_SETS = (1 << 15),
V3D_OVERLAY_SCULPT_CURVES_CAGE = (1 << 16),
V3D_OVERLAY_SHOW_LIGHT_COLORS = (1 << 17),
};
/** #View3DOverlay.edit_flag */

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