Vulkan: Clearing Storage Buffers #105487
|
@ -2,4 +2,4 @@ ${CommitTitle}
|
|||
|
||||
${CommitBody}
|
||||
|
||||
Pull Request #${PullRequestIndex}
|
||||
Pull Request: https://projects.blender.org/blender/blender/pulls/${PullRequestIndex}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
${PullRequestTitle}
|
||||
|
||||
Pull Request #${PullRequestIndex}
|
||||
Pull Request: https://projects.blender.org/blender/blender/pulls/${PullRequestIndex}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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}>"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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', []),
|
||||
]
|
||||
|
|
|
@ -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),
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
||||
|
|
|
@ -79,6 +79,7 @@ class TEXT_HT_footer(Header):
|
|||
text=iface_("Text: External")
|
||||
if text.library
|
||||
else iface_("Text: Internal"),
|
||||
translate=False
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 \
|
||||
} \
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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_) {
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 ¶ms,
|
||||
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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]) {
|
||||
|
|
|
@ -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 ¶ms,
|
||||
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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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, ¶ms);
|
||||
|
||||
/* #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, ¶ms);
|
||||
uvedit_pack_islands_multi(scene, &ob, 1, &bm, nullptr, ¶ms);
|
||||
|
||||
/* Write back from BMesh to Mesh. */
|
||||
BMeshToMeshParams bm_to_me_params{};
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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 ¶ms [[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 ¶ms [[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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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*/,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue