Merge branch 'master' into sculpt-dev
This commit is contained in:
commit
251440f538
|
@ -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
|
|
@ -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),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
* \{ */
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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", "");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue