Merge branch 'master' into sculpt-dev

This commit is contained in:
Pablo Dobarro 2021-03-09 18:44:15 +01:00
commit 251440f538
32 changed files with 970 additions and 2450 deletions

View File

@ -386,13 +386,11 @@ extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG])
- (id)init
{
self = [super init];
if (self) {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:@selector(windowWillClose:)
name:NSWindowWillCloseNotification
object:nil];
}
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:@selector(windowWillClose:)
name:NSWindowWillCloseNotification
object:nil];
return self;
}
@ -563,97 +561,96 @@ GHOST_TSuccess GHOST_SystemCocoa::init()
SetFrontProcess(&psn);
}*/
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[NSApplication sharedApplication]; // initializes NSApp
@autoreleasepool {
[NSApplication sharedApplication]; // initializes NSApp
if ([NSApp mainMenu] == nil) {
NSMenu *mainMenubar = [[NSMenu alloc] init];
NSMenuItem *menuItem;
NSMenu *windowMenu;
NSMenu *appMenu;
if ([NSApp mainMenu] == nil) {
NSMenu *mainMenubar = [[NSMenu alloc] init];
NSMenuItem *menuItem;
NSMenu *windowMenu;
NSMenu *appMenu;
// Create the application menu
appMenu = [[NSMenu alloc] initWithTitle:@"Blender"];
// Create the application menu
appMenu = [[NSMenu alloc] initWithTitle:@"Blender"];
[appMenu addItemWithTitle:@"About Blender"
action:@selector(orderFrontStandardAboutPanel:)
keyEquivalent:@""];
[appMenu addItem:[NSMenuItem separatorItem]];
[appMenu addItemWithTitle:@"About Blender"
action:@selector(orderFrontStandardAboutPanel:)
keyEquivalent:@""];
[appMenu addItem:[NSMenuItem separatorItem]];
menuItem = [appMenu addItemWithTitle:@"Hide Blender"
action:@selector(hide:)
keyEquivalent:@"h"];
[menuItem setKeyEquivalentModifierMask:NSEventModifierFlagCommand];
menuItem = [appMenu addItemWithTitle:@"Hide Blender"
action:@selector(hide:)
keyEquivalent:@"h"];
[menuItem setKeyEquivalentModifierMask:NSEventModifierFlagCommand];
menuItem = [appMenu addItemWithTitle:@"Hide Others"
action:@selector(hideOtherApplications:)
keyEquivalent:@"h"];
[menuItem
setKeyEquivalentModifierMask:(NSEventModifierFlagOption | NSEventModifierFlagCommand)];
menuItem = [appMenu addItemWithTitle:@"Hide Others"
action:@selector(hideOtherApplications:)
keyEquivalent:@"h"];
[menuItem
setKeyEquivalentModifierMask:(NSEventModifierFlagOption | NSEventModifierFlagCommand)];
[appMenu addItemWithTitle:@"Show All"
action:@selector(unhideAllApplications:)
keyEquivalent:@""];
[appMenu addItemWithTitle:@"Show All"
action:@selector(unhideAllApplications:)
keyEquivalent:@""];
menuItem = [appMenu addItemWithTitle:@"Quit Blender"
action:@selector(terminate:)
keyEquivalent:@"q"];
[menuItem setKeyEquivalentModifierMask:NSEventModifierFlagCommand];
menuItem = [appMenu addItemWithTitle:@"Quit Blender"
action:@selector(terminate:)
keyEquivalent:@"q"];
[menuItem setKeyEquivalentModifierMask:NSEventModifierFlagCommand];
menuItem = [[NSMenuItem alloc] init];
[menuItem setSubmenu:appMenu];
menuItem = [[NSMenuItem alloc] init];
[menuItem setSubmenu:appMenu];
[mainMenubar addItem:menuItem];
[menuItem release];
[NSApp performSelector:@selector(setAppleMenu:) withObject:appMenu]; // Needed for 10.5
[appMenu release];
[mainMenubar addItem:menuItem];
[menuItem release];
[NSApp performSelector:@selector(setAppleMenu:) withObject:appMenu]; // Needed for 10.5
[appMenu release];
// Create the window menu
windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
// Create the window menu
windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
menuItem = [windowMenu addItemWithTitle:@"Minimize"
action:@selector(performMiniaturize:)
keyEquivalent:@"m"];
[menuItem setKeyEquivalentModifierMask:NSEventModifierFlagCommand];
menuItem = [windowMenu addItemWithTitle:@"Minimize"
action:@selector(performMiniaturize:)
keyEquivalent:@"m"];
[menuItem setKeyEquivalentModifierMask:NSEventModifierFlagCommand];
[windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];
[windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];
menuItem = [windowMenu addItemWithTitle:@"Enter Full Screen"
action:@selector(toggleFullScreen:)
keyEquivalent:@"f"];
[menuItem
setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand];
menuItem = [windowMenu addItemWithTitle:@"Enter Full Screen"
action:@selector(toggleFullScreen:)
keyEquivalent:@"f"];
[menuItem
setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand];
menuItem = [windowMenu addItemWithTitle:@"Close"
action:@selector(performClose:)
keyEquivalent:@"w"];
[menuItem setKeyEquivalentModifierMask:NSEventModifierFlagCommand];
menuItem = [windowMenu addItemWithTitle:@"Close"
action:@selector(performClose:)
keyEquivalent:@"w"];
[menuItem setKeyEquivalentModifierMask:NSEventModifierFlagCommand];
menuItem = [[NSMenuItem alloc] init];
[menuItem setSubmenu:windowMenu];
menuItem = [[NSMenuItem alloc] init];
[menuItem setSubmenu:windowMenu];
[mainMenubar addItem:menuItem];
[menuItem release];
[mainMenubar addItem:menuItem];
[menuItem release];
[NSApp setMainMenu:mainMenubar];
[NSApp setWindowsMenu:windowMenu];
[windowMenu release];
[NSApp setMainMenu:mainMenubar];
[NSApp setWindowsMenu:windowMenu];
[windowMenu release];
}
if ([NSApp delegate] == nil) {
CocoaAppDelegate *appDelegate = [[CocoaAppDelegate alloc] init];
[appDelegate setSystemCocoa:this];
[NSApp setDelegate:appDelegate];
}
// AppKit provides automatic window tabbing. Blender is a single-tabbed application
// without a macOS tab bar, and should explicitly opt-out of this. This is also
// controlled by the macOS user default #NSWindowTabbingEnabled.
NSWindow.allowsAutomaticWindowTabbing = NO;
[NSApp finishLaunching];
}
if ([NSApp delegate] == nil) {
CocoaAppDelegate *appDelegate = [[CocoaAppDelegate alloc] init];
[appDelegate setSystemCocoa:this];
[NSApp setDelegate:appDelegate];
}
// AppKit provides automatic window tabbing. Blender is a single-tabbed application without a
// macOS tab bar, and should explicitly opt-out of this. This is also controlled by the macOS
// user default #NSWindowTabbingEnabled.
NSWindow.allowsAutomaticWindowTabbing = NO;
[NSApp finishLaunching];
[pool drain];
}
return success;
}
@ -676,30 +673,26 @@ GHOST_TUns8 GHOST_SystemCocoa::getNumDisplays() const
{
// Note that OS X supports monitor hot plug
// We do not support multiple monitors at the moment
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
GHOST_TUns8 count = [[NSScreen screens] count];
[pool drain];
return count;
@autoreleasepool {
return NSScreen.screens.count;
}
}
void GHOST_SystemCocoa::getMainDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Get visible frame, that is frame excluding dock and top menu bar
NSRect frame = [[NSScreen mainScreen] visibleFrame];
@autoreleasepool {
// Get visible frame, that is frame excluding dock and top menu bar
NSRect frame = [[NSScreen mainScreen] visibleFrame];
// Returns max window contents (excluding title bar...)
NSRect contentRect = [NSWindow
contentRectForFrameRect:frame
styleMask:(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
NSWindowStyleMaskMiniaturizable)];
// Returns max window contents (excluding title bar...)
NSRect contentRect = [NSWindow
contentRectForFrameRect:frame
styleMask:(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
NSWindowStyleMaskMiniaturizable)];
width = contentRect.size.width;
height = contentRect.size.height;
[pool drain];
width = contentRect.size.width;
height = contentRect.size.height;
}
}
void GHOST_SystemCocoa::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const
@ -720,53 +713,52 @@ GHOST_IWindow *GHOST_SystemCocoa::createWindow(const char *title,
const bool is_dialog,
const GHOST_IWindow *parentWindow)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
GHOST_IWindow *window = NULL;
@autoreleasepool {
// Get the available rect for including window contents
NSRect frame = [[NSScreen mainScreen] visibleFrame];
NSRect contentRect = [NSWindow
contentRectForFrameRect:frame
styleMask:(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
NSWindowStyleMaskMiniaturizable)];
// Get the available rect for including window contents
NSRect frame = [[NSScreen mainScreen] visibleFrame];
NSRect contentRect = [NSWindow
contentRectForFrameRect:frame
styleMask:(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
NSWindowStyleMaskMiniaturizable)];
GHOST_TInt32 bottom = (contentRect.size.height - 1) - height - top;
GHOST_TInt32 bottom = (contentRect.size.height - 1) - height - top;
// Ensures window top left is inside this available rect
left = left > contentRect.origin.x ? left : contentRect.origin.x;
// Add contentRect.origin.y to respect docksize
bottom = bottom > contentRect.origin.y ? bottom + contentRect.origin.y : contentRect.origin.y;
// Ensures window top left is inside this available rect
left = left > contentRect.origin.x ? left : contentRect.origin.x;
// Add contentRect.origin.y to respect docksize
bottom = bottom > contentRect.origin.y ? bottom + contentRect.origin.y : contentRect.origin.y;
window = new GHOST_WindowCocoa(this,
title,
left,
bottom,
width,
height,
state,
type,
glSettings.flags & GHOST_glStereoVisual,
glSettings.flags & GHOST_glDebugContext,
is_dialog,
(GHOST_WindowCocoa *)parentWindow);
window = new GHOST_WindowCocoa(this,
title,
left,
bottom,
width,
height,
state,
type,
glSettings.flags & GHOST_glStereoVisual,
glSettings.flags & GHOST_glDebugContext,
is_dialog,
(GHOST_WindowCocoa *)parentWindow);
if (window->getValid()) {
// Store the pointer to the window
GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
m_windowManager->addWindow(window);
m_windowManager->setActiveWindow(window);
/* Need to tell window manager the new window is the active one
* (Cocoa does not send the event activate upon window creation). */
pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window));
pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
if (window->getValid()) {
// Store the pointer to the window
GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
m_windowManager->addWindow(window);
m_windowManager->setActiveWindow(window);
/* Need to tell window manager the new window is the active one
* (Cocoa does not send the event activate upon window creation). */
pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window));
pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
}
else {
GHOST_PRINT("GHOST_SystemCocoa::createWindow(): window invalid\n");
delete window;
window = NULL;
}
}
else {
GHOST_PRINT("GHOST_SystemCocoa::createWindow(): window invalid\n");
delete window;
window = NULL;
}
[pool drain];
return window;
}
@ -841,29 +833,28 @@ GHOST_TSuccess GHOST_SystemCocoa::setMouseCursorPosition(GHOST_TInt32 x, GHOST_T
if (!window)
return GHOST_kFailure;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSScreen *windowScreen = window->getScreen();
NSRect screenRect = [windowScreen frame];
@autoreleasepool {
NSScreen *windowScreen = window->getScreen();
NSRect screenRect = [windowScreen frame];
// Set position relative to current screen
xf -= screenRect.origin.x;
yf -= screenRect.origin.y;
// Set position relative to current screen
xf -= screenRect.origin.x;
yf -= screenRect.origin.y;
// Quartz Display Services uses the old coordinates (top left origin)
yf = screenRect.size.height - yf;
// Quartz Display Services uses the old coordinates (top left origin)
yf = screenRect.size.height - yf;
CGDisplayMoveCursorToPoint((CGDirectDisplayID)[[[windowScreen deviceDescription]
objectForKey:@"NSScreenNumber"] unsignedIntValue],
CGPointMake(xf, yf));
CGDisplayMoveCursorToPoint((CGDirectDisplayID)[[[windowScreen deviceDescription]
objectForKey:@"NSScreenNumber"] unsignedIntValue],
CGPointMake(xf, yf));
// See https://stackoverflow.com/a/17559012. By default, hardware events
// will be suppressed for 500ms after a synthetic mouse event. For unknown
// reasons CGEventSourceSetLocalEventsSuppressionInterval does not work,
// however calling CGAssociateMouseAndMouseCursorPosition also removes the
// delay, even if this is undocumented.
CGAssociateMouseAndMouseCursorPosition(true);
[pool drain];
// See https://stackoverflow.com/a/17559012. By default, hardware events
// will be suppressed for 500ms after a synthetic mouse event. For unknown
// reasons CGEventSourceSetLocalEventsSuppressionInterval does not work,
// however calling CGAssociateMouseAndMouseCursorPosition also removes the
// delay, even if this is undocumented.
CGAssociateMouseAndMouseCursorPosition(true);
}
return GHOST_kSuccess;
}
@ -928,42 +919,40 @@ bool GHOST_SystemCocoa::processEvents(bool waitForEvent)
}
#endif
do {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
event = [NSApp nextEventMatchingMask:NSEventMaskAny
untilDate:[NSDate distantPast]
inMode:NSDefaultRunLoopMode
dequeue:YES];
if (event == nil) {
[pool drain];
break;
}
@autoreleasepool {
event = [NSApp nextEventMatchingMask:NSEventMaskAny
untilDate:[NSDate distantPast]
inMode:NSDefaultRunLoopMode
dequeue:YES];
if (event == nil) {
break;
}
anyProcessed = true;
anyProcessed = true;
// Send event to NSApp to ensure Mac wide events are handled,
// this will send events to CocoaWindow which will call back
// to handleKeyEvent, handleMouseEvent and handleTabletEvent
// Send event to NSApp to ensure Mac wide events are handled,
// this will send events to CocoaWindow which will call back
// to handleKeyEvent, handleMouseEvent and handleTabletEvent
// There is on special exception for ctrl+(shift)+tab. We do not
// get keyDown events delivered to the view because they are
// special hotkeys to switch between views, so override directly
// There is on special exception for ctrl+(shift)+tab. We do not
// get keyDown events delivered to the view because they are
// special hotkeys to switch between views, so override directly
if ([event type] == NSEventTypeKeyDown && [event keyCode] == kVK_Tab &&
([event modifierFlags] & NSEventModifierFlagControl)) {
handleKeyEvent(event);
}
else {
// For some reason NSApp is swallowing the key up events when modifier
// key is pressed, even if there seems to be no apparent reason to do
// so, as a workaround we always handle these up events.
if ([event type] == NSEventTypeKeyUp &&
([event modifierFlags] & (NSEventModifierFlagCommand | NSEventModifierFlagOption)))
if ([event type] == NSEventTypeKeyDown && [event keyCode] == kVK_Tab &&
([event modifierFlags] & NSEventModifierFlagControl)) {
handleKeyEvent(event);
}
else {
// For some reason NSApp is swallowing the key up events when modifier
// key is pressed, even if there seems to be no apparent reason to do
// so, as a workaround we always handle these up events.
if ([event type] == NSEventTypeKeyUp &&
([event modifierFlags] & (NSEventModifierFlagCommand | NSEventModifierFlagOption)))
handleKeyEvent(event);
[NSApp sendEvent:event];
[NSApp sendEvent:event];
}
}
[pool drain];
} while (event != nil);
#if 0
} while (waitForEvent && !anyProcessed); // Needed only for timer implementation
@ -1677,10 +1666,8 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
NSEventPhase momentumPhase = NSEventPhaseNone;
NSEventPhase phase = NSEventPhaseNone;
if ([event respondsToSelector:@selector(momentumPhase)])
momentumPhase = [event momentumPhase];
if ([event respondsToSelector:@selector(phase)])
phase = [event phase];
momentumPhase = [event momentumPhase];
phase = [event phase];
/* when pressing a key while momentum scrolling continues after
* lifting fingers off the trackpad, the action can unexpectedly
@ -1953,78 +1940,48 @@ GHOST_TUns8 *GHOST_SystemCocoa::getClipboard(bool selection) const
GHOST_TUns8 *temp_buff;
size_t pastedTextSize;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
@autoreleasepool {
NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
if (pasteBoard == nil) {
[pool drain];
return NULL;
}
NSString *textPasted = [pasteBoard stringForType:NSStringPboardType];
NSArray *supportedTypes = [NSArray arrayWithObjects:NSStringPboardType, nil];
if (textPasted == nil) {
return NULL;
}
NSString *bestType = [[NSPasteboard generalPasteboard] availableTypeFromArray:supportedTypes];
pastedTextSize = [textPasted lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
if (bestType == nil) {
[pool drain];
return NULL;
}
temp_buff = (GHOST_TUns8 *)malloc(pastedTextSize + 1);
NSString *textPasted = [pasteBoard stringForType:NSStringPboardType];
if (temp_buff == NULL) {
return NULL;
}
if (textPasted == nil) {
[pool drain];
return NULL;
}
strncpy(
(char *)temp_buff, [textPasted cStringUsingEncoding:NSUTF8StringEncoding], pastedTextSize);
pastedTextSize = [textPasted lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
temp_buff[pastedTextSize] = '\0';
temp_buff = (GHOST_TUns8 *)malloc(pastedTextSize + 1);
if (temp_buff == NULL) {
[pool drain];
return NULL;
}
strncpy(
(char *)temp_buff, [textPasted cStringUsingEncoding:NSUTF8StringEncoding], pastedTextSize);
temp_buff[pastedTextSize] = '\0';
[pool drain];
if (temp_buff) {
return temp_buff;
}
else {
return NULL;
if (temp_buff) {
return temp_buff;
}
else {
return NULL;
}
}
}
void GHOST_SystemCocoa::putClipboard(GHOST_TInt8 *buffer, bool selection) const
{
NSString *textToCopy;
if (selection)
return; // for copying the selection, used on X11
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
@autoreleasepool {
NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
if (pasteBoard == nil) {
[pool drain];
return;
NSPasteboard *pasteBoard = NSPasteboard.generalPasteboard;
[pasteBoard declareTypes:@[ NSStringPboardType ] owner:nil];
NSString *textToCopy = [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
[pasteBoard setString:textToCopy forType:NSStringPboardType];
}
NSArray *supportedTypes = [NSArray arrayWithObject:NSStringPboardType];
[pasteBoard declareTypes:supportedTypes owner:nil];
textToCopy = [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
[pasteBoard setString:textToCopy forType:NSStringPboardType];
[pool drain];
}

@ -1 +1 @@
Subproject commit aafea2abb18bb42e7d31a6926b2caba90f4e0316
Subproject commit b06e7fe345e4a313eb701692e5d45033131caee1

@ -1 +1 @@
Subproject commit 117faa96af35685d72e5e01f9a386d163d874133
Subproject commit ef3104dae302dcfb08b21e32d10b548bf304bd29

View File

@ -4509,6 +4509,7 @@ def km_sculpt(params):
# Dynamic topology
("sculpt.dynamic_topology_toggle", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
("sculpt.dyntopo_detail_size_edit", {"type": 'D', "value": 'PRESS', "shift": True}, None),
("sculpt.set_detail_size", {"type": 'D', "value": 'PRESS', "shift": True, "alt": True}, None),
# Remesh
("object.voxel_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
("object.voxel_size_edit", {"type": 'R', "value": 'PRESS', "shift": True}, None),

View File

@ -3395,7 +3395,8 @@ def km_sculpt(params):
{"properties": [("data_path", 'scene.tool_settings.sculpt.show_mask')]}),
# Dynamic topology
("sculpt.dynamic_topology_toggle", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
("sculpt.set_detail_size", {"type": 'D', "value": 'PRESS', "shift": True}, None),
("sculpt.dyntopo_detail_size_edit", {"type": 'D', "value": 'PRESS', "shift": True}, None),
("sculpt.set_detail_size", {"type": 'D', "value": 'PRESS', "shift": True, "alt": True}, None),
# Remesh
("object.voxel_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
("object.quadriflow_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True, "alt": True}, None),

View File

@ -29,6 +29,8 @@
#include "BLI_map.hh"
#include "BLI_string_ref.hh"
#include "BKE_cryptomatte.h"
struct ID;
namespace blender::bke::cryptomatte {
@ -103,4 +105,13 @@ struct CryptomatteStampDataCallbackData {
static void extract_layer_manifest(void *_data, const char *propname, char *propvalue, int len);
};
struct CryptomatteSessionDeleter {
void operator()(CryptomatteSession *session)
{
BKE_cryptomatte_free(session);
}
};
using CryptomatteSessionPtr = std::unique_ptr<CryptomatteSession, CryptomatteSessionDeleter>;
} // namespace blender::bke::cryptomatte

View File

@ -131,6 +131,10 @@ set(SRC
intern/fmodifier.c
intern/font.c
intern/freestyle.c
intern/geometry_component_instances.cc
intern/geometry_component_mesh.cc
intern/geometry_component_pointcloud.cc
intern/geometry_component_volume.cc
intern/geometry_set.cc
intern/geometry_set_instances.cc
intern/gpencil.c
@ -430,6 +434,7 @@ set(SRC
nla_private.h
particle_private.h
tracking_private.h
intern/attribute_access_intern.hh
intern/CCGSubSurf.h
intern/CCGSubSurf_inline.h
intern/CCGSubSurf_intern.h

File diff suppressed because it is too large Load Diff

View File

@ -75,17 +75,15 @@ static void test_cryptomatte_manifest(std::string expected, std::string manifest
TEST(cryptomatte, layer_from_manifest)
{
test_cryptomatte_manifest("{}", "{}");
test_cryptomatte_manifest("{\"Object\":\"12345678\"}", "{\"Object\": \"12345678\"}");
test_cryptomatte_manifest("{\"Object\":\"12345678\",\"Object2\":\"87654321\"}",
"{\"Object\":\"12345678\",\"Object2\":\"87654321\"}");
test_cryptomatte_manifest(R"({"Object":"12345678"})", R"({"Object": "12345678"})");
test_cryptomatte_manifest(R"({"Object":"12345678","Object2":"87654321")})",
R"({"Object":"12345678","Object2":"87654321"})");
test_cryptomatte_manifest(R"({"Object":"12345678","Object2":"87654321"})",
R"( { "Object" : "12345678" , "Object2" : "87654321" } )");
test_cryptomatte_manifest(R"({"Object\"01\"":"12345678"})", R"({"Object\"01\"": "12345678"})");
test_cryptomatte_manifest(
"{\"Object\":\"12345678\",\"Object2\":\"87654321\"}",
" { \"Object\" : \"12345678\" , \"Object2\" : \"87654321\" } ");
test_cryptomatte_manifest("{\"Object\\\"01\\\"\":\"12345678\"}",
"{\"Object\\\"01\\\"\": \"12345678\"}");
test_cryptomatte_manifest(
"{\"Object\\\"01\\\"\":\"12345678\",\"Object\":\"12345678\",\"Object2\":\"87654321\"}",
"{\"Object\\\"01\\\"\":\"12345678\",\"Object\":\"12345678\", \"Object2\":\"87654321\"}");
R"({"Object\"01\"":"12345678","Object":"12345678","Object2":"87654321"})",
R"({"Object\"01\"":"12345678","Object":"12345678", "Object2":"87654321"})");
}
TEST(cryptomatte, extract_layer_hash_from_metadata_key)
@ -125,7 +123,7 @@ static void validate_cryptomatte_session_from_stamp_data(void *UNUSED(data),
EXPECT_STREQ("uint32_to_float32", propvalue);
}
else if (prop_name == "cryptomatte/87f095e/manifest") {
EXPECT_STREQ("{\"Object\":\"12345678\"}", propvalue);
EXPECT_STREQ(R"({"Object":"12345678"})", propvalue);
}
else if (prop_name == "cryptomatte/c42daa7/name") {
@ -138,7 +136,7 @@ static void validate_cryptomatte_session_from_stamp_data(void *UNUSED(data),
EXPECT_STREQ("uint32_to_float32", propvalue);
}
else if (prop_name == "cryptomatte/c42daa7/manifest") {
EXPECT_STREQ("{\"Object2\":\"87654321\"}", propvalue);
EXPECT_STREQ(R"({"Object2":"87654321"})", propvalue);
}
else {
@ -153,12 +151,12 @@ TEST(cryptomatte, session_from_stamp_data)
MEM_callocN(sizeof(RenderResult), __func__));
BKE_render_result_stamp_data(render_result, "cryptomatte/qwerty/name", "layer1");
BKE_render_result_stamp_data(
render_result, "cryptomatte/qwerty/manifest", "{\"Object\":\"12345678\"}");
render_result, "cryptomatte/qwerty/manifest", R"({"Object":"12345678"})");
BKE_render_result_stamp_data(render_result, "cryptomatte/uiop/name", "layer2");
BKE_render_result_stamp_data(
render_result, "cryptomatte/uiop/manifest", "{\"Object2\":\"87654321\"}");
CryptomatteSession *session = BKE_cryptomatte_init_from_render_result(render_result);
EXPECT_NE(session, nullptr);
render_result, "cryptomatte/uiop/manifest", R"({"Object2":"87654321"})");
CryptomatteSessionPtr session(BKE_cryptomatte_init_from_render_result(render_result));
EXPECT_NE(session.get(), nullptr);
RE_FreeRenderResult(render_result);
/* Create StampData from CryptomatteSession. */
@ -166,14 +164,13 @@ TEST(cryptomatte, session_from_stamp_data)
BLI_strncpy(view_layer.name, "viewlayername", sizeof(view_layer.name));
RenderResult *render_result2 = static_cast<RenderResult *>(
MEM_callocN(sizeof(RenderResult), __func__));
BKE_cryptomatte_store_metadata(session, render_result2, &view_layer);
BKE_cryptomatte_store_metadata(session.get(), render_result2, &view_layer);
/* Validate StampData. */
BKE_stamp_info_callback(
nullptr, render_result2->stamp_data, validate_cryptomatte_session_from_stamp_data, false);
RE_FreeRenderResult(render_result2);
BKE_cryptomatte_free(session);
}
} // namespace blender::bke::cryptomatte::tests

View File

@ -405,6 +405,29 @@ static void update_vertex_normals_when_dirty(const GeometryComponent &component)
}
}
static bool get_shade_smooth(const MPoly &mpoly)
{
return mpoly.flag & ME_SMOOTH;
}
static void set_shade_smooth(MPoly &mpoly, const bool &value)
{
SET_FLAG_FROM_TEST(mpoly.flag, value, ME_SMOOTH);
}
static ReadAttributePtr make_shade_smooth_read_attribute(const void *data, const int domain_size)
{
return std::make_unique<DerivedArrayReadAttribute<MPoly, bool, get_shade_smooth>>(
ATTR_DOMAIN_POLYGON, Span<MPoly>((const MPoly *)data, domain_size));
}
static WriteAttributePtr make_shade_smooth_write_attribute(void *data, const int domain_size)
{
return std::make_unique<
DerivedArrayWriteAttribute<MPoly, bool, get_shade_smooth, set_shade_smooth>>(
ATTR_DOMAIN_POLYGON, MutableSpan<MPoly>((MPoly *)data, domain_size));
}
static float2 get_loop_uv(const MLoopUV &uv)
{
return float2(uv.uv);
@ -680,6 +703,19 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
nullptr,
nullptr);
static BuiltinCustomDataLayerProvider shade_smooth("shade_smooth",
ATTR_DOMAIN_POLYGON,
CD_PROP_BOOL,
CD_MPOLY,
BuiltinAttributeProvider::NonCreatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::NonDeletable,
polygon_access,
make_shade_smooth_read_attribute,
make_shade_smooth_write_attribute,
nullptr,
nullptr);
static BuiltinCustomDataLayerProvider vertex_normal("vertex_normal",
ATTR_DOMAIN_POINT,
CD_PROP_FLOAT3,
@ -713,7 +749,7 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
static CustomDataAttributeProvider edge_custom_data(ATTR_DOMAIN_EDGE, edge_access);
static CustomDataAttributeProvider polygon_custom_data(ATTR_DOMAIN_POLYGON, polygon_access);
return ComponentAttributeProviders({&position, &material_index, &vertex_normal},
return ComponentAttributeProviders({&position, &material_index, &vertex_normal, &shade_smooth},
{&uvs,
&vertex_colors,
&corner_custom_data,

View File

@ -310,437 +310,6 @@ Volume *GeometrySet::get_volume_for_write()
/** \} */
/* -------------------------------------------------------------------- */
/** \name Mesh Component
* \{ */
MeshComponent::MeshComponent() : GeometryComponent(GeometryComponentType::Mesh)
{
}
MeshComponent::~MeshComponent()
{
this->clear();
}
GeometryComponent *MeshComponent::copy() const
{
MeshComponent *new_component = new MeshComponent();
if (mesh_ != nullptr) {
new_component->mesh_ = BKE_mesh_copy_for_eval(mesh_, false);
new_component->ownership_ = GeometryOwnershipType::Owned;
new_component->vertex_group_names_ = blender::Map(vertex_group_names_);
}
return new_component;
}
void MeshComponent::clear()
{
BLI_assert(this->is_mutable());
if (mesh_ != nullptr) {
if (ownership_ == GeometryOwnershipType::Owned) {
BKE_id_free(nullptr, mesh_);
}
mesh_ = nullptr;
}
vertex_group_names_.clear();
}
bool MeshComponent::has_mesh() const
{
return mesh_ != nullptr;
}
/* Clear the component and replace it with the new mesh. */
void MeshComponent::replace(Mesh *mesh, GeometryOwnershipType ownership)
{
BLI_assert(this->is_mutable());
this->clear();
mesh_ = mesh;
ownership_ = ownership;
}
/* This function exists for the same reason as #vertex_group_names_. Non-nodes modifiers need to
* be able to replace the mesh data without losing the vertex group names, which may have come
* from another object. */
void MeshComponent::replace_mesh_but_keep_vertex_group_names(Mesh *mesh,
GeometryOwnershipType ownership)
{
BLI_assert(this->is_mutable());
if (mesh_ != nullptr) {
if (ownership_ == GeometryOwnershipType::Owned) {
BKE_id_free(nullptr, mesh_);
}
mesh_ = nullptr;
}
mesh_ = mesh;
ownership_ = ownership;
}
/* Return the mesh and clear the component. The caller takes over responsibility for freeing the
* mesh (if the component was responsible before). */
Mesh *MeshComponent::release()
{
BLI_assert(this->is_mutable());
Mesh *mesh = mesh_;
mesh_ = nullptr;
return mesh;
}
void MeshComponent::copy_vertex_group_names_from_object(const Object &object)
{
BLI_assert(this->is_mutable());
vertex_group_names_.clear();
int index = 0;
LISTBASE_FOREACH (const bDeformGroup *, group, &object.defbase) {
vertex_group_names_.add(group->name, index);
index++;
}
}
const blender::Map<std::string, int> &MeshComponent::vertex_group_names() const
{
return vertex_group_names_;
}
/* This is only exposed for the internal attribute API. */
blender::Map<std::string, int> &MeshComponent::vertex_group_names()
{
return vertex_group_names_;
}
/* Get the mesh from this component. This method can be used by multiple threads at the same
* time. Therefore, the returned mesh should not be modified. No ownership is transferred. */
const Mesh *MeshComponent::get_for_read() const
{
return mesh_;
}
/* Get the mesh from this component. This method can only be used when the component is mutable,
* i.e. it is not shared. The returned mesh can be modified. No ownership is transferred. */
Mesh *MeshComponent::get_for_write()
{
BLI_assert(this->is_mutable());
if (ownership_ == GeometryOwnershipType::ReadOnly) {
mesh_ = BKE_mesh_copy_for_eval(mesh_, false);
ownership_ = GeometryOwnershipType::Owned;
}
return mesh_;
}
bool MeshComponent::is_empty() const
{
return mesh_ == nullptr;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Pointcloud Component
* \{ */
PointCloudComponent::PointCloudComponent() : GeometryComponent(GeometryComponentType::PointCloud)
{
}
PointCloudComponent::~PointCloudComponent()
{
this->clear();
}
GeometryComponent *PointCloudComponent::copy() const
{
PointCloudComponent *new_component = new PointCloudComponent();
if (pointcloud_ != nullptr) {
new_component->pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_, false);
new_component->ownership_ = GeometryOwnershipType::Owned;
}
return new_component;
}
void PointCloudComponent::clear()
{
BLI_assert(this->is_mutable());
if (pointcloud_ != nullptr) {
if (ownership_ == GeometryOwnershipType::Owned) {
BKE_id_free(nullptr, pointcloud_);
}
pointcloud_ = nullptr;
}
}
bool PointCloudComponent::has_pointcloud() const
{
return pointcloud_ != nullptr;
}
/* Clear the component and replace it with the new point cloud. */
void PointCloudComponent::replace(PointCloud *pointcloud, GeometryOwnershipType ownership)
{
BLI_assert(this->is_mutable());
this->clear();
pointcloud_ = pointcloud;
ownership_ = ownership;
}
/* Return the point cloud and clear the component. The caller takes over responsibility for freeing
* the point cloud (if the component was responsible before). */
PointCloud *PointCloudComponent::release()
{
BLI_assert(this->is_mutable());
PointCloud *pointcloud = pointcloud_;
pointcloud_ = nullptr;
return pointcloud;
}
/* Get the point cloud from this component. This method can be used by multiple threads at the same
* time. Therefore, the returned point cloud should not be modified. No ownership is transferred.
*/
const PointCloud *PointCloudComponent::get_for_read() const
{
return pointcloud_;
}
/* Get the point cloud from this component. This method can only be used when the component is
* mutable, i.e. it is not shared. The returned point cloud can be modified. No ownership is
* transferred. */
PointCloud *PointCloudComponent::get_for_write()
{
BLI_assert(this->is_mutable());
if (ownership_ == GeometryOwnershipType::ReadOnly) {
pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_, false);
ownership_ = GeometryOwnershipType::Owned;
}
return pointcloud_;
}
bool PointCloudComponent::is_empty() const
{
return pointcloud_ == nullptr;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Instances Component
* \{ */
InstancesComponent::InstancesComponent() : GeometryComponent(GeometryComponentType::Instances)
{
}
GeometryComponent *InstancesComponent::copy() const
{
InstancesComponent *new_component = new InstancesComponent();
new_component->transforms_ = transforms_;
new_component->instanced_data_ = instanced_data_;
return new_component;
}
void InstancesComponent::clear()
{
instanced_data_.clear();
transforms_.clear();
}
void InstancesComponent::add_instance(Object *object, float4x4 transform, const int id)
{
InstancedData data;
data.type = INSTANCE_DATA_TYPE_OBJECT;
data.data.object = object;
this->add_instance(data, transform, id);
}
void InstancesComponent::add_instance(Collection *collection, float4x4 transform, const int id)
{
InstancedData data;
data.type = INSTANCE_DATA_TYPE_COLLECTION;
data.data.collection = collection;
this->add_instance(data, transform, id);
}
void InstancesComponent::add_instance(InstancedData data, float4x4 transform, const int id)
{
instanced_data_.append(data);
transforms_.append(transform);
ids_.append(id);
}
Span<InstancedData> InstancesComponent::instanced_data() const
{
return instanced_data_;
}
Span<float4x4> InstancesComponent::transforms() const
{
return transforms_;
}
Span<int> InstancesComponent::ids() const
{
return ids_;
}
MutableSpan<float4x4> InstancesComponent::transforms()
{
return transforms_;
}
int InstancesComponent::instances_amount() const
{
const int size = instanced_data_.size();
BLI_assert(transforms_.size() == size);
return size;
}
bool InstancesComponent::is_empty() const
{
return transforms_.size() == 0;
}
static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids)
{
using namespace blender;
Array<int> unique_ids(original_ids.size());
Set<int> used_unique_ids;
used_unique_ids.reserve(original_ids.size());
Vector<int> instances_with_id_collision;
for (const int instance_index : original_ids.index_range()) {
const int original_id = original_ids[instance_index];
if (used_unique_ids.add(original_id)) {
/* The original id has not been used by another instance yet. */
unique_ids[instance_index] = original_id;
}
else {
/* The original id of this instance collided with a previous instance, it needs to be looked
* at again in a second pass. Don't generate a new random id here, because this might collide
* with other existing ids. */
instances_with_id_collision.append(instance_index);
}
}
Map<int, RandomNumberGenerator> generator_by_original_id;
for (const int instance_index : instances_with_id_collision) {
const int original_id = original_ids[instance_index];
RandomNumberGenerator &rng = generator_by_original_id.lookup_or_add_cb(original_id, [&]() {
RandomNumberGenerator rng;
rng.seed_random(original_id);
return rng;
});
const int max_iteration = 100;
for (int iteration = 0;; iteration++) {
/* Try generating random numbers until an unused one has been found. */
const int random_id = rng.get_int32();
if (used_unique_ids.add(random_id)) {
/* This random id is not used by another instance. */
unique_ids[instance_index] = random_id;
break;
}
if (iteration == max_iteration) {
/* It seems to be very unlikely that we ever run into this case (assuming there are less
* than 2^30 instances). However, if that happens, it's better to use an id that is not
* unique than to be stuck in an infinite loop. */
unique_ids[instance_index] = original_id;
break;
}
}
}
return unique_ids;
}
blender::Span<int> InstancesComponent::almost_unique_ids() const
{
std::lock_guard lock(almost_unique_ids_mutex_);
if (almost_unique_ids_.size() != ids_.size()) {
almost_unique_ids_ = generate_unique_instance_ids(ids_);
}
return almost_unique_ids_;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Volume Component
* \{ */
VolumeComponent::VolumeComponent() : GeometryComponent(GeometryComponentType::Volume)
{
}
VolumeComponent::~VolumeComponent()
{
this->clear();
}
GeometryComponent *VolumeComponent::copy() const
{
VolumeComponent *new_component = new VolumeComponent();
if (volume_ != nullptr) {
new_component->volume_ = BKE_volume_copy_for_eval(volume_, false);
new_component->ownership_ = GeometryOwnershipType::Owned;
}
return new_component;
}
void VolumeComponent::clear()
{
BLI_assert(this->is_mutable());
if (volume_ != nullptr) {
if (ownership_ == GeometryOwnershipType::Owned) {
BKE_id_free(nullptr, volume_);
}
volume_ = nullptr;
}
}
bool VolumeComponent::has_volume() const
{
return volume_ != nullptr;
}
/* Clear the component and replace it with the new volume. */
void VolumeComponent::replace(Volume *volume, GeometryOwnershipType ownership)
{
BLI_assert(this->is_mutable());
this->clear();
volume_ = volume;
ownership_ = ownership;
}
/* Return the volume and clear the component. The caller takes over responsibility for freeing the
* volume (if the component was responsible before). */
Volume *VolumeComponent::release()
{
BLI_assert(this->is_mutable());
Volume *volume = volume_;
volume_ = nullptr;
return volume;
}
/* Get the volume from this component. This method can be used by multiple threads at the same
* time. Therefore, the returned volume should not be modified. No ownership is transferred. */
const Volume *VolumeComponent::get_for_read() const
{
return volume_;
}
/* Get the volume from this component. This method can only be used when the component is mutable,
* i.e. it is not shared. The returned volume can be modified. No ownership is transferred. */
Volume *VolumeComponent::get_for_write()
{
BLI_assert(this->is_mutable());
if (ownership_ == GeometryOwnershipType::ReadOnly) {
volume_ = BKE_volume_copy_for_eval(volume_, false);
ownership_ = GeometryOwnershipType::Owned;
}
return volume_;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name C API
* \{ */

View File

@ -378,8 +378,10 @@ static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups,
/* Don't copy attributes that are stored directly in the mesh data structs. */
Map<std::string, AttributeKind> attributes;
gather_attribute_info(
attributes, component_types, set_groups, {"position", "material_index", "vertex_normal"});
gather_attribute_info(attributes,
component_types,
set_groups,
{"position", "material_index", "vertex_normal", "shade_smooth"});
join_attributes(
set_groups, component_types, attributes, static_cast<GeometryComponent &>(dst_component));
}

View File

@ -171,7 +171,9 @@ static void lib_id_clear_library_data_ex(Main *bmain, ID *id)
/* Conceptually, an ID made local is not the same as the linked one anymore. Reflect that by
* regenerating its session UUID. */
BKE_lib_libblock_session_uuid_renew(id);
if ((id->tag & LIB_TAG_TEMP_MAIN) == 0) {
BKE_lib_libblock_session_uuid_renew(id);
}
/* We need to tag this IDs and all of its users, conceptually new local ID and original linked
* ones are two completely different data-blocks that were virtually remapped, even though in

View File

@ -1217,7 +1217,7 @@ static void write_panel_list(BlendWriter *writer, ListBase *lb)
}
}
static void write_area_regions(BlendWriter *writer, ScrArea *area)
static void write_area(BlendWriter *writer, ScrArea *area)
{
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
write_region(writer, region, area->spacetype);
@ -1359,7 +1359,7 @@ void BKE_screen_area_map_blend_write(BlendWriter *writer, ScrAreaMap *area_map)
BLO_write_struct(writer, ScrGlobalAreaData, area->global);
write_area_regions(writer, area);
write_area(writer, area);
area->butspacetype = SPACE_EMPTY; /* Unset again, was changed above. */
}

View File

@ -19,18 +19,11 @@
#include "COM_ChunkOrderHotspot.h"
#include <cmath>
ChunkOrderHotspot::ChunkOrderHotspot(int x, int y, float addition)
{
x = x;
y = y;
addition = addition;
}
double ChunkOrderHotspot::calc_distance(int x, int y)
{
int dx = x - x;
int dy = y - y;
int dx = this->x - x;
int dy = this->y - y;
double result = sqrt((double)(dx * dx + dy * dy));
result += (double)addition;
result += (double)this->addition;
return result;
}

View File

@ -27,8 +27,9 @@ struct ChunkOrderHotspot {
int y;
float addition;
public:
ChunkOrderHotspot(int x, int y, float addition);
ChunkOrderHotspot(int x, int y, float addition) : x(x), y(y), addition(addition)
{
}
double calc_distance(int x, int y);

View File

@ -28,7 +28,7 @@ class MultilayerBaseOperation : public BaseImageOperation {
protected:
RenderLayer *m_renderLayer;
RenderPass *m_renderPass;
ImBuf *getImBuf();
ImBuf *getImBuf() override;
public:
/**
@ -44,7 +44,7 @@ class MultilayerColorOperation : public MultilayerBaseOperation {
{
this->addOutputSocket(COM_DT_COLOR);
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler);
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
std::unique_ptr<MetaData> getMetaData() const override;
};
@ -55,7 +55,7 @@ class MultilayerValueOperation : public MultilayerBaseOperation {
{
this->addOutputSocket(COM_DT_VALUE);
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler);
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MultilayerVectorOperation : public MultilayerBaseOperation {
@ -65,5 +65,5 @@ class MultilayerVectorOperation : public MultilayerBaseOperation {
{
this->addOutputSocket(COM_DT_VECTOR);
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler);
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};

View File

@ -1074,6 +1074,11 @@ static int view_layer_remove_aov_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
if (view_layer->active_aov == NULL) {
return OPERATOR_FINISHED;
}
BKE_view_layer_remove_aov(view_layer, view_layer->active_aov);
RenderEngineType *engine_type = RE_engines_find(scene->r.engine);

View File

@ -78,11 +78,14 @@
#include "IMB_colormanagement.h"
#include "GPU_batch.h"
#include "GPU_batch_presets.h"
#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
#include "GPU_matrix.h"
#include "GPU_state.h"
#include "WM_api.h"
#include "WM_message.h"
#include "WM_toolsystem.h"
@ -10023,335 +10026,6 @@ static void SCULPT_OT_mask_by_color(wmOperatorType *ot)
1.0f);
}
/* -------------------------------------------------------------------- */
/** \name Dyntopo Detail Size Edit Operator
* \{ */
/* Defines how much the mouse movement will modify the detail size value. */
#define DETAIL_SIZE_DELTA_SPEED 0.08f
#define DETAIL_SIZE_DELTA_ACCURATE_SPEED 0.004f
typedef struct DyntopoDetailSizeEditCustomData {
void *draw_handle;
Object *active_object;
float init_mval[2];
float accurate_mval[2];
float outline_col[4];
bool accurate_mode;
bool sample_mode;
float init_detail_size;
float accurate_detail_size;
float detail_size;
float radius;
float preview_tri[3][3];
float gizmo_mat[4][4];
} DyntopoDetailSizeEditCustomData;
static void dyntopo_detail_size_parallel_lines_draw(uint pos3d,
DyntopoDetailSizeEditCustomData *cd,
const float start_co[3],
const float end_co[3],
bool flip,
const float angle)
{
float object_space_constant_detail = 1.0f /
(cd->detail_size * mat4_to_scale(cd->active_object->obmat));
/* The constant detail represents the maximum edge length allowed before subdividing it. If the
* triangle grid preview is created with this value it will represent an ideal mesh density where
* all edges have the exact maximum length, which never happens in practice. As the minimum edge
* length for dyntopo is 0.4 * max_edge_length, this adjust the detail size to the average
* between max and min edge length so the preview is more accurate. */
object_space_constant_detail *= 0.7f;
const float total_len = len_v3v3(cd->preview_tri[0], cd->preview_tri[1]);
const int tot_lines = (int)(total_len / object_space_constant_detail) + 1;
const float tot_lines_fl = total_len / object_space_constant_detail;
float spacing_disp[3];
sub_v3_v3v3(spacing_disp, end_co, start_co);
normalize_v3(spacing_disp);
float line_disp[3];
rotate_v2_v2fl(line_disp, spacing_disp, DEG2RAD(angle));
mul_v3_fl(spacing_disp, total_len / tot_lines_fl);
immBegin(GPU_PRIM_LINES, (uint)tot_lines * 2);
for (int i = 0; i < tot_lines; i++) {
float line_length;
if (flip) {
line_length = total_len * ((float)i / (float)tot_lines_fl);
}
else {
line_length = total_len * (1.0f - ((float)i / (float)tot_lines_fl));
}
float line_start[3];
copy_v3_v3(line_start, start_co);
madd_v3_v3v3fl(line_start, line_start, spacing_disp, i);
float line_end[3];
madd_v3_v3v3fl(line_end, line_start, line_disp, line_length);
immVertex3fv(pos3d, line_start);
immVertex3fv(pos3d, line_end);
}
immEnd();
}
static void dyntopo_detail_size_edit_draw(const bContext *UNUSED(C),
ARegion *UNUSED(ar),
void *arg)
{
DyntopoDetailSizeEditCustomData *cd = arg;
GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
uint pos3d = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_matrix_push();
GPU_matrix_mul(cd->gizmo_mat);
/* Draw Cursor */
immUniformColor4fv(cd->outline_col);
GPU_line_width(3.0f);
imm_draw_circle_wire_3d(pos3d, 0, 0, cd->radius, 80);
/* Draw Triangle. */
immUniformColor4f(0.9f, 0.9f, 0.9f, 0.8f);
immBegin(GPU_PRIM_LINES, 6);
immVertex3fv(pos3d, cd->preview_tri[0]);
immVertex3fv(pos3d, cd->preview_tri[1]);
immVertex3fv(pos3d, cd->preview_tri[1]);
immVertex3fv(pos3d, cd->preview_tri[2]);
immVertex3fv(pos3d, cd->preview_tri[2]);
immVertex3fv(pos3d, cd->preview_tri[0]);
immEnd();
/* Draw Grid */
GPU_line_width(1.0f);
dyntopo_detail_size_parallel_lines_draw(
pos3d, cd, cd->preview_tri[0], cd->preview_tri[1], false, 60.0f);
dyntopo_detail_size_parallel_lines_draw(
pos3d, cd, cd->preview_tri[0], cd->preview_tri[1], true, 120.0f);
dyntopo_detail_size_parallel_lines_draw(
pos3d, cd, cd->preview_tri[0], cd->preview_tri[2], false, -60.0f);
immUnbindProgram();
GPU_matrix_pop();
GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
}
static void dyntopo_detail_size_edit_cancel(bContext *C, wmOperator *op)
{
Object *active_object = CTX_data_active_object(C);
SculptSession *ss = active_object->sculpt;
ARegion *region = CTX_wm_region(C);
DyntopoDetailSizeEditCustomData *cd = op->customdata;
ED_region_draw_cb_exit(region->type, cd->draw_handle);
ss->draw_faded_cursor = false;
MEM_freeN(op->customdata);
ED_workspace_status_text(C, NULL);
}
static void dyntopo_detail_size_sample_from_surface(Object *ob,
DyntopoDetailSizeEditCustomData *cd)
{
SculptSession *ss = ob->sculpt;
const int active_vertex = SCULPT_active_vertex_get(ss);
float len_accum = 0;
int num_neighbors = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, active_vertex, ni) {
len_accum += len_v3v3(SCULPT_vertex_co_get(ss, active_vertex),
SCULPT_vertex_co_get(ss, ni.index));
num_neighbors++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
if (num_neighbors > 0) {
const float avg_edge_len = len_accum / num_neighbors;
/* Use 0.7 as the average of min and max dyntopo edge length. */
const float detail_size = 0.7f / (avg_edge_len * mat4_to_scale(cd->active_object->obmat));
cd->detail_size = clamp_f(detail_size, 1.0f, 500.0f);
}
}
static void dyntopo_detail_size_update_from_mouse_delta(DyntopoDetailSizeEditCustomData *cd,
const wmEvent *event)
{
const float mval[2] = {event->mval[0], event->mval[1]};
float detail_size_delta;
if (cd->accurate_mode) {
detail_size_delta = mval[0] - cd->accurate_mval[0];
cd->detail_size = cd->accurate_detail_size +
detail_size_delta * DETAIL_SIZE_DELTA_ACCURATE_SPEED;
}
else {
detail_size_delta = mval[0] - cd->init_mval[0];
cd->detail_size = cd->init_detail_size + detail_size_delta * DETAIL_SIZE_DELTA_SPEED;
}
if (event->type == EVT_LEFTSHIFTKEY && event->val == KM_PRESS) {
cd->accurate_mode = true;
copy_v2_v2(cd->accurate_mval, mval);
cd->accurate_detail_size = cd->detail_size;
}
if (event->type == EVT_LEFTSHIFTKEY && event->val == KM_RELEASE) {
cd->accurate_mode = false;
cd->accurate_detail_size = 0.0f;
}
cd->detail_size = clamp_f(cd->detail_size, 1.0f, 500.0f);
}
static int dyntopo_detail_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
Object *active_object = CTX_data_active_object(C);
SculptSession *ss = active_object->sculpt;
ARegion *region = CTX_wm_region(C);
DyntopoDetailSizeEditCustomData *cd = op->customdata;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
/* Cancel modal operator */
if ((event->type == EVT_ESCKEY && event->val == KM_PRESS) ||
(event->type == RIGHTMOUSE && event->val == KM_PRESS)) {
dyntopo_detail_size_edit_cancel(C, op);
ED_region_tag_redraw(region);
return OPERATOR_FINISHED;
}
/* Finish modal operator */
if ((event->type == LEFTMOUSE && event->val == KM_RELEASE) ||
(event->type == EVT_RETKEY && event->val == KM_PRESS) ||
(event->type == EVT_PADENTER && event->val == KM_PRESS)) {
ED_region_draw_cb_exit(region->type, cd->draw_handle);
sd->constant_detail = cd->detail_size;
ss->draw_faded_cursor = false;
MEM_freeN(op->customdata);
ED_region_tag_redraw(region);
ED_workspace_status_text(C, NULL);
return OPERATOR_FINISHED;
}
ED_region_tag_redraw(region);
if (event->type == EVT_LEFTCTRLKEY && event->val == KM_PRESS) {
cd->sample_mode = true;
}
if (event->type == EVT_LEFTCTRLKEY && event->val == KM_RELEASE) {
cd->sample_mode = false;
}
/* Sample mode sets the detail size sampling the average edge length under the surface. */
if (cd->sample_mode) {
dyntopo_detail_size_sample_from_surface(active_object, cd);
return OPERATOR_RUNNING_MODAL;
}
/* Regular mode, changes the detail size by moving the cursor. */
dyntopo_detail_size_update_from_mouse_delta(cd, event);
return OPERATOR_RUNNING_MODAL;
}
static int dyntopo_detail_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ARegion *region = CTX_wm_region(C);
Object *active_object = CTX_data_active_object(C);
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
DyntopoDetailSizeEditCustomData *cd = MEM_callocN(sizeof(DyntopoDetailSizeEditCustomData),
"Dyntopo Detail Size Edit OP Custom Data");
/* Initial operator Custom Data setup. */
cd->draw_handle = ED_region_draw_cb_activate(
region->type, dyntopo_detail_size_edit_draw, cd, REGION_DRAW_POST_VIEW);
cd->active_object = active_object;
cd->init_mval[0] = event->mval[0];
cd->init_mval[1] = event->mval[1];
cd->detail_size = sd->constant_detail;
cd->init_detail_size = sd->constant_detail;
copy_v4_v4(cd->outline_col, brush->add_col);
op->customdata = cd;
SculptSession *ss = active_object->sculpt;
cd->radius = ss->cursor_radius;
/* Generates the matrix to position the gizmo in the surface of the mesh using the same location
* and orientation as the brush cursor. */
float cursor_trans[4][4], cursor_rot[4][4];
const float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f};
float quat[4];
copy_m4_m4(cursor_trans, active_object->obmat);
translate_m4(
cursor_trans, ss->cursor_location[0], ss->cursor_location[1], ss->cursor_location[2]);
float cursor_normal[3];
if (!is_zero_v3(ss->cursor_sampled_normal)) {
copy_v3_v3(cursor_normal, ss->cursor_sampled_normal);
}
else {
copy_v3_v3(cursor_normal, ss->cursor_normal);
}
rotation_between_vecs_to_quat(quat, z_axis, cursor_normal);
quat_to_mat4(cursor_rot, quat);
copy_m4_m4(cd->gizmo_mat, cursor_trans);
mul_m4_m4_post(cd->gizmo_mat, cursor_rot);
/* Initialize the position of the triangle vertices. */
const float y_axis[3] = {0.0f, cd->radius, 0.0f};
for (int i = 0; i < 3; i++) {
zero_v3(cd->preview_tri[i]);
rotate_v2_v2fl(cd->preview_tri[i], y_axis, DEG2RAD(120.0f * i));
}
SCULPT_vertex_random_access_ensure(ss);
WM_event_add_modal_handler(C, op);
ED_region_tag_redraw(region);
ss->draw_faded_cursor = true;
const char *status_str = TIP_(
"Move the mouse to change the dyntopo detail size. LMB: confirm size, ESC/RMB: cancel");
ED_workspace_status_text(C, status_str);
return OPERATOR_RUNNING_MODAL;
}
static bool dyntopo_detail_size_edit_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
return SCULPT_mode_poll(C) && ob->sculpt->bm && (sd->flags & SCULPT_DYNTOPO_DETAIL_CONSTANT);
}
static void SCULPT_OT_dyntopo_detail_size_edit(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Edit Dyntopo Detail Size";
ot->description = "Modify the constant detail size of dyntopo interactively";
ot->idname = "SCULPT_OT_dyntopo_detail_size_edit";
/* api callbacks */
ot->poll = dyntopo_detail_size_edit_poll;
ot->invoke = dyntopo_detail_size_edit_invoke;
ot->modal = dyntopo_detail_size_edit_modal;
ot->cancel = dyntopo_detail_size_edit_cancel;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
typedef enum eSculptMaskInitMode {
SCULPT_MASK_INIT_RANDOM_PER_VERTEX,
SCULPT_MASK_INIT_RANDOM_PER_FACE_SET,

View File

@ -37,10 +37,17 @@
#include "DEG_depsgraph.h"
#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
#include "GPU_matrix.h"
#include "GPU_state.h"
#include "WM_api.h"
#include "WM_types.h"
#include "ED_screen.h"
#include "ED_sculpt.h"
#include "ED_space_api.h"
#include "ED_view3d.h"
#include "sculpt_intern.h"
@ -359,8 +366,12 @@ void SCULPT_OT_sample_detail_size(wmOperatorType *ot)
/* Dynamic-topology detail size.
*
* This should be improved further, perhaps by showing a triangle
* grid rather than brush alpha. */
* Currently, there are two operators editing the detail size:
* - SCULPT_OT_set_detail_size uses radial control for all methods
* - SCULPT_OT_dyntopo_detail_size_edit shows a triangle grid representation of the detail
* resolution (for constant detail method, falls back to radial control for the remaining methods).
*/
static void set_brush_rc_props(PointerRNA *ptr, const char *prop)
{
char *path = BLI_sprintfN("tool_settings.sculpt.brush.%s", prop);
@ -368,7 +379,7 @@ static void set_brush_rc_props(PointerRNA *ptr, const char *prop)
MEM_freeN(path);
}
static int sculpt_set_detail_size_exec(bContext *C, wmOperator *UNUSED(op))
static void sculpt_detail_size_set_radial_control(bContext *C)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
@ -394,6 +405,11 @@ static int sculpt_set_detail_size_exec(bContext *C, wmOperator *UNUSED(op))
WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr);
WM_operator_properties_free(&props_ptr);
}
static int sculpt_set_detail_size_exec(bContext *C, wmOperator *UNUSED(op))
{
sculpt_detail_size_set_radial_control(C);
return OPERATOR_FINISHED;
}
@ -412,3 +428,334 @@ void SCULPT_OT_set_detail_size(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* -------------------------------------------------------------------- */
/** \name Dyntopo Detail Size Edit Operator
* \{ */
/* Defines how much the mouse movement will modify the detail size value. */
#define DETAIL_SIZE_DELTA_SPEED 0.08f
#define DETAIL_SIZE_DELTA_ACCURATE_SPEED 0.004f
typedef struct DyntopoDetailSizeEditCustomData {
void *draw_handle;
Object *active_object;
float init_mval[2];
float accurate_mval[2];
float outline_col[4];
bool accurate_mode;
bool sample_mode;
float init_detail_size;
float accurate_detail_size;
float detail_size;
float radius;
float preview_tri[3][3];
float gizmo_mat[4][4];
} DyntopoDetailSizeEditCustomData;
static void dyntopo_detail_size_parallel_lines_draw(uint pos3d,
DyntopoDetailSizeEditCustomData *cd,
const float start_co[3],
const float end_co[3],
bool flip,
const float angle)
{
float object_space_constant_detail = 1.0f /
(cd->detail_size * mat4_to_scale(cd->active_object->obmat));
/* The constant detail represents the maximum edge length allowed before subdividing it. If the
* triangle grid preview is created with this value it will represent an ideal mesh density where
* all edges have the exact maximum length, which never happens in practice. As the minimum edge
* length for dyntopo is 0.4 * max_edge_length, this adjust the detail size to the average
* between max and min edge length so the preview is more accurate. */
object_space_constant_detail *= 0.7f;
const float total_len = len_v3v3(cd->preview_tri[0], cd->preview_tri[1]);
const int tot_lines = (int)(total_len / object_space_constant_detail) + 1;
const float tot_lines_fl = total_len / object_space_constant_detail;
float spacing_disp[3];
sub_v3_v3v3(spacing_disp, end_co, start_co);
normalize_v3(spacing_disp);
float line_disp[3];
rotate_v2_v2fl(line_disp, spacing_disp, DEG2RAD(angle));
mul_v3_fl(spacing_disp, total_len / tot_lines_fl);
immBegin(GPU_PRIM_LINES, (uint)tot_lines * 2);
for (int i = 0; i < tot_lines; i++) {
float line_length;
if (flip) {
line_length = total_len * ((float)i / (float)tot_lines_fl);
}
else {
line_length = total_len * (1.0f - ((float)i / (float)tot_lines_fl));
}
float line_start[3];
copy_v3_v3(line_start, start_co);
madd_v3_v3v3fl(line_start, line_start, spacing_disp, i);
float line_end[3];
madd_v3_v3v3fl(line_end, line_start, line_disp, line_length);
immVertex3fv(pos3d, line_start);
immVertex3fv(pos3d, line_end);
}
immEnd();
}
static void dyntopo_detail_size_edit_draw(const bContext *UNUSED(C),
ARegion *UNUSED(ar),
void *arg)
{
DyntopoDetailSizeEditCustomData *cd = arg;
GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
uint pos3d = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_matrix_push();
GPU_matrix_mul(cd->gizmo_mat);
/* Draw Cursor */
immUniformColor4fv(cd->outline_col);
GPU_line_width(3.0f);
imm_draw_circle_wire_3d(pos3d, 0, 0, cd->radius, 80);
/* Draw Triangle. */
immUniformColor4f(0.9f, 0.9f, 0.9f, 0.8f);
immBegin(GPU_PRIM_LINES, 6);
immVertex3fv(pos3d, cd->preview_tri[0]);
immVertex3fv(pos3d, cd->preview_tri[1]);
immVertex3fv(pos3d, cd->preview_tri[1]);
immVertex3fv(pos3d, cd->preview_tri[2]);
immVertex3fv(pos3d, cd->preview_tri[2]);
immVertex3fv(pos3d, cd->preview_tri[0]);
immEnd();
/* Draw Grid */
GPU_line_width(1.0f);
dyntopo_detail_size_parallel_lines_draw(
pos3d, cd, cd->preview_tri[0], cd->preview_tri[1], false, 60.0f);
dyntopo_detail_size_parallel_lines_draw(
pos3d, cd, cd->preview_tri[0], cd->preview_tri[1], true, 120.0f);
dyntopo_detail_size_parallel_lines_draw(
pos3d, cd, cd->preview_tri[0], cd->preview_tri[2], false, -60.0f);
immUnbindProgram();
GPU_matrix_pop();
GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
}
static void dyntopo_detail_size_edit_cancel(bContext *C, wmOperator *op)
{
Object *active_object = CTX_data_active_object(C);
SculptSession *ss = active_object->sculpt;
ARegion *region = CTX_wm_region(C);
DyntopoDetailSizeEditCustomData *cd = op->customdata;
ED_region_draw_cb_exit(region->type, cd->draw_handle);
ss->draw_faded_cursor = false;
MEM_freeN(op->customdata);
ED_workspace_status_text(C, NULL);
}
static void dyntopo_detail_size_sample_from_surface(Object *ob,
DyntopoDetailSizeEditCustomData *cd)
{
SculptSession *ss = ob->sculpt;
const int active_vertex = SCULPT_active_vertex_get(ss);
float len_accum = 0;
int num_neighbors = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, active_vertex, ni) {
len_accum += len_v3v3(SCULPT_vertex_co_get(ss, active_vertex),
SCULPT_vertex_co_get(ss, ni.index));
num_neighbors++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
if (num_neighbors > 0) {
const float avg_edge_len = len_accum / num_neighbors;
/* Use 0.7 as the average of min and max dyntopo edge length. */
const float detail_size = 0.7f / (avg_edge_len * mat4_to_scale(cd->active_object->obmat));
cd->detail_size = clamp_f(detail_size, 1.0f, 500.0f);
}
}
static void dyntopo_detail_size_update_from_mouse_delta(DyntopoDetailSizeEditCustomData *cd,
const wmEvent *event)
{
const float mval[2] = {event->mval[0], event->mval[1]};
float detail_size_delta;
if (cd->accurate_mode) {
detail_size_delta = mval[0] - cd->accurate_mval[0];
cd->detail_size = cd->accurate_detail_size +
detail_size_delta * DETAIL_SIZE_DELTA_ACCURATE_SPEED;
}
else {
detail_size_delta = mval[0] - cd->init_mval[0];
cd->detail_size = cd->init_detail_size + detail_size_delta * DETAIL_SIZE_DELTA_SPEED;
}
if (event->type == EVT_LEFTSHIFTKEY && event->val == KM_PRESS) {
cd->accurate_mode = true;
copy_v2_v2(cd->accurate_mval, mval);
cd->accurate_detail_size = cd->detail_size;
}
if (event->type == EVT_LEFTSHIFTKEY && event->val == KM_RELEASE) {
cd->accurate_mode = false;
cd->accurate_detail_size = 0.0f;
}
cd->detail_size = clamp_f(cd->detail_size, 1.0f, 500.0f);
}
static int dyntopo_detail_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
Object *active_object = CTX_data_active_object(C);
SculptSession *ss = active_object->sculpt;
ARegion *region = CTX_wm_region(C);
DyntopoDetailSizeEditCustomData *cd = op->customdata;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
/* Cancel modal operator */
if ((event->type == EVT_ESCKEY && event->val == KM_PRESS) ||
(event->type == RIGHTMOUSE && event->val == KM_PRESS)) {
dyntopo_detail_size_edit_cancel(C, op);
ED_region_tag_redraw(region);
return OPERATOR_FINISHED;
}
/* Finish modal operator */
if ((event->type == LEFTMOUSE && event->val == KM_RELEASE) ||
(event->type == EVT_RETKEY && event->val == KM_PRESS) ||
(event->type == EVT_PADENTER && event->val == KM_PRESS)) {
ED_region_draw_cb_exit(region->type, cd->draw_handle);
sd->constant_detail = cd->detail_size;
ss->draw_faded_cursor = false;
MEM_freeN(op->customdata);
ED_region_tag_redraw(region);
ED_workspace_status_text(C, NULL);
return OPERATOR_FINISHED;
}
ED_region_tag_redraw(region);
if (event->type == EVT_LEFTCTRLKEY && event->val == KM_PRESS) {
cd->sample_mode = true;
}
if (event->type == EVT_LEFTCTRLKEY && event->val == KM_RELEASE) {
cd->sample_mode = false;
}
/* Sample mode sets the detail size sampling the average edge length under the surface. */
if (cd->sample_mode) {
dyntopo_detail_size_sample_from_surface(active_object, cd);
return OPERATOR_RUNNING_MODAL;
}
/* Regular mode, changes the detail size by moving the cursor. */
dyntopo_detail_size_update_from_mouse_delta(cd, event);
return OPERATOR_RUNNING_MODAL;
}
static int dyntopo_detail_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
/* Fallback to radial control for modes other than SCULPT_DYNTOPO_DETAIL_CONSTANT [same as in
* SCULPT_OT_set_detail_size]. */
if (!(sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL))) {
sculpt_detail_size_set_radial_control(C);
return OPERATOR_FINISHED;
}
/* Special method for SCULPT_DYNTOPO_DETAIL_CONSTANT. */
ARegion *region = CTX_wm_region(C);
Object *active_object = CTX_data_active_object(C);
Brush *brush = BKE_paint_brush(&sd->paint);
DyntopoDetailSizeEditCustomData *cd = MEM_callocN(sizeof(DyntopoDetailSizeEditCustomData),
"Dyntopo Detail Size Edit OP Custom Data");
/* Initial operator Custom Data setup. */
cd->draw_handle = ED_region_draw_cb_activate(
region->type, dyntopo_detail_size_edit_draw, cd, REGION_DRAW_POST_VIEW);
cd->active_object = active_object;
cd->init_mval[0] = event->mval[0];
cd->init_mval[1] = event->mval[1];
cd->detail_size = sd->constant_detail;
cd->init_detail_size = sd->constant_detail;
copy_v4_v4(cd->outline_col, brush->add_col);
op->customdata = cd;
SculptSession *ss = active_object->sculpt;
cd->radius = ss->cursor_radius;
/* Generates the matrix to position the gizmo in the surface of the mesh using the same location
* and orientation as the brush cursor. */
float cursor_trans[4][4], cursor_rot[4][4];
const float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f};
float quat[4];
copy_m4_m4(cursor_trans, active_object->obmat);
translate_m4(
cursor_trans, ss->cursor_location[0], ss->cursor_location[1], ss->cursor_location[2]);
float cursor_normal[3];
if (!is_zero_v3(ss->cursor_sampled_normal)) {
copy_v3_v3(cursor_normal, ss->cursor_sampled_normal);
}
else {
copy_v3_v3(cursor_normal, ss->cursor_normal);
}
rotation_between_vecs_to_quat(quat, z_axis, cursor_normal);
quat_to_mat4(cursor_rot, quat);
copy_m4_m4(cd->gizmo_mat, cursor_trans);
mul_m4_m4_post(cd->gizmo_mat, cursor_rot);
/* Initize the position of the triangle vertices. */
const float y_axis[3] = {0.0f, cd->radius, 0.0f};
for (int i = 0; i < 3; i++) {
zero_v3(cd->preview_tri[i]);
rotate_v2_v2fl(cd->preview_tri[i], y_axis, DEG2RAD(120.0f * i));
}
SCULPT_vertex_random_access_ensure(ss);
WM_event_add_modal_handler(C, op);
ED_region_tag_redraw(region);
ss->draw_faded_cursor = true;
const char *status_str = TIP_(
"Move the mouse to change the dyntopo detail size. LMB: confirm size, ESC/RMB: cancel");
ED_workspace_status_text(C, status_str);
return OPERATOR_RUNNING_MODAL;
}
void SCULPT_OT_dyntopo_detail_size_edit(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Edit Dyntopo Detail Size";
ot->description = "Modify the detail size of dyntopo interactively";
ot->idname = "SCULPT_OT_dyntopo_detail_size_edit";
/* api callbacks */
ot->poll = sculpt_and_dynamic_topology_poll;
ot->invoke = dyntopo_detail_size_edit_invoke;
ot->modal = dyntopo_detail_size_edit_modal;
ot->cancel = dyntopo_detail_size_edit_cancel;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}

View File

@ -1473,6 +1473,7 @@ void sculpt_expand_modal_keymap(struct wmKeyConfig *keyconf);
void SCULPT_OT_detail_flood_fill(struct wmOperatorType *ot);
void SCULPT_OT_sample_detail_size(struct wmOperatorType *ot);
void SCULPT_OT_set_detail_size(struct wmOperatorType *ot);
void SCULPT_OT_dyntopo_detail_size_edit(struct wmOperatorType *ot);
/* Dyntopo. */
void SCULPT_OT_dynamic_topology_toggle(struct wmOperatorType *ot);

View File

@ -152,7 +152,7 @@ void TreeElementID::postExpand(SpaceOutliner &space_outliner) const
bool TreeElementID::expandPoll(const SpaceOutliner &space_outliner) const
{
const TreeStoreElem *tsepar = legacy_te_.parent ? TREESTORE(legacy_te_.parent) : nullptr;
return (tsepar == NULL || tsepar->type != TSE_ID_BASE || space_outliner.filter_id_type);
return (tsepar == nullptr || tsepar->type != TSE_ID_BASE || space_outliner.filter_id_type);
}
void TreeElementID::expand_animation_data(SpaceOutliner &space_outliner,

View File

@ -2791,7 +2791,7 @@ static void rna_def_modifier_boolean(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_hole_tolerant", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", eBooleanModifierFlag_HoleTolerant);
RNA_def_property_ui_text(prop, "Hole tolerant", "Better results when there are holes (slower)");
RNA_def_property_ui_text(prop, "Hole Tolerant", "Better results when there are holes (slower)");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
/* BMesh debugging options, only used when G_DEBUG is set */
@ -3139,7 +3139,8 @@ static void rna_def_modifier_uvproject(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_PROPORTIONAL);
RNA_def_property_range(prop, 1, FLT_MAX);
RNA_def_property_ui_range(prop, 1, 1000, 1, 3);
RNA_def_property_ui_text(prop, "Aspect X", "Horizontal aspect ratio (only used for camera projectors)");
RNA_def_property_ui_text(
prop, "Aspect X", "Horizontal aspect ratio (only used for camera projectors)");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "aspect_y", PROP_FLOAT, PROP_NONE);
@ -3147,7 +3148,8 @@ static void rna_def_modifier_uvproject(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_PROPORTIONAL);
RNA_def_property_range(prop, 1, FLT_MAX);
RNA_def_property_ui_range(prop, 1, 1000, 1, 3);
RNA_def_property_ui_text(prop, "Aspect Y", "Vertical aspect ratio (only used for camera projectors)");
RNA_def_property_ui_text(
prop, "Aspect Y", "Vertical aspect ratio (only used for camera projectors)");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "scale_x", PROP_FLOAT, PROP_NONE);

View File

@ -136,7 +136,7 @@ void RNA_api_pose(StructRNA *srna)
RNA_def_function_ui_description(
func,
"Apply the given action to this pose by evaluating it at a specific time. Only updates the "
"pose of selected bones, or all bones if none are selected");
"pose of selected bones, or all bones if none are selected.");
parm = RNA_def_pointer(func, "action", "Action", "Action", "The Action containing the pose");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);

View File

@ -1216,6 +1216,11 @@ void RNA_api_keymaps(StructRNA *srna)
func = RNA_def_function(srna, "new", "rna_keymap_new"); /* add_keymap */
RNA_def_function_flag(func, FUNC_USE_REPORTS);
RNA_def_function_ui_description(
func,
"Ensure the keymap exists. This will return the one with the given name/space type/region "
"type, or create a new one if it does not exist yet.");
parm = RNA_def_string(func, "name", NULL, 0, "Name", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
RNA_def_enum(func, "space_type", rna_enum_space_type_items, SPACE_EMPTY, "Space Type", "");

View File

@ -864,13 +864,15 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
static void solver_options_panel_draw(const bContext *UNUSED(C), Panel *panel)
{
uiLayout *layout = panel->layout;
uiLayout *col = uiLayoutColumn(layout, true);
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL);
const bool use_exact = RNA_enum_get(ptr, "solver") == eBooleanModifierSolver_Exact;
const bool operand_object = RNA_enum_get(ptr, "operand_type") == eBooleanModifierFlag_Object;
uiLayoutSetPropSep(layout, true);
uiLayout *col = uiLayoutColumn(layout, true);
if (use_exact) {
/* When operand is collection, we always use_self. */
if (operand_object) {
@ -883,7 +885,6 @@ static void solver_options_panel_draw(const bContext *UNUSED(C), Panel *panel)
}
if (G.debug) {
col = uiLayoutColumn(layout, true);
uiItemR(col, ptr, "debug_options", 0, NULL, ICON_NONE);
}
}

View File

@ -424,6 +424,10 @@ class GeometryNodesEvaluator {
if (socket_ref->bsocket()->type != SOCK_GEOMETRY) {
continue;
}
if (socket_ref->is_multi_input_socket()) {
/* Not needed currently. */
continue;
}
bNodeTree *btree_cow = node->btree();
bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow);

View File

@ -120,13 +120,17 @@ class GeoNodeExecParams {
template<typename T> Vector<T> extract_multi_input(StringRef identifier)
{
Vector<T> values;
values.append(input_values_.extract<T>(identifier));
int i = 1;
std::string sub_identifier = identifier + "[1]";
while (input_values_.contains(sub_identifier)) {
int index = 0;
while (true) {
std::string sub_identifier = identifier;
if (index > 0) {
sub_identifier += "[" + std::to_string(index) + "]";
}
if (!input_values_.contains(sub_identifier)) {
break;
}
values.append(input_values_.extract<T>(sub_identifier));
i++;
sub_identifier = identifier + "[" + std::to_string(i) + "]";
index++;
}
return values;
}

View File

@ -224,7 +224,7 @@ static void join_components(Span<const MeshComponent *> src_components, Geometry
/* Don't copy attributes that are stored directly in the mesh data structs. */
join_attributes(to_base_components(src_components),
dst_component,
{"position", "material_index", "vertex_normal"});
{"position", "material_index", "vertex_normal", "shade_smooth"});
}
static void join_components(Span<const PointCloudComponent *> src_components, GeometrySet &result)

View File

@ -149,7 +149,7 @@ BLI_NOINLINE static KDTree_3d *build_kdtree(Span<Vector<float3>> positions_all,
KDTree_3d *kdtree = BLI_kdtree_3d_new(initial_points_len);
int i_point = 0;
for (const Vector<float3> positions : positions_all) {
for (const Vector<float3> &positions : positions_all) {
for (const float3 position : positions) {
BLI_kdtree_3d_insert(kdtree, i_point, position);
i_point++;
@ -611,13 +611,13 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
"Density Attribute");
if (density <= 0.0f) {
params.set_output("Geometry", std::move(GeometrySet()));
params.set_output("Geometry", GeometrySet());
return;
}
Vector<GeometryInstanceGroup> set_groups = bke::geometry_set_gather_instances(geometry_set);
if (set_groups.is_empty()) {
params.set_output("Geometry", std::move(GeometrySet()));
params.set_output("Geometry", GeometrySet());
return;
}
@ -631,7 +631,7 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
if (set_groups.is_empty()) {
params.error_message_add(NodeWarningType::Error, TIP_("Input geometry must contain a mesh"));
params.set_output("Geometry", std::move(GeometrySet()));
params.set_output("Geometry", GeometrySet());
return;
}

View File

@ -46,6 +46,9 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co
{
OutputAttributePtr position_attribute = component.attribute_try_get_for_output(
"position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
if (!position_attribute) {
return;
}
ReadAttributePtr attribute = params.get_input_attribute(
"Translation", component, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, nullptr);
if (!attribute) {

@ -1 +1 @@
Subproject commit c37d8bd28ddddb8f1b0dff5739d75f8004e8034f
Subproject commit b66c22e1fb977bf8dd3797ebedc28fbe28f0305e