From f0d8e8f23cadba85b8af0fbeadbd736c693c3315 Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 20 Oct 2025 12:36:36 -0400 Subject: Introduce dependabot configuration for Github Actions (#4859) --- .github/dependabot.yaml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/dependabot.yaml diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 0000000000..3e3e1df3e2 --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,7 @@ +version: 2 +updates: + + - package-ecosystem: github-actions + directory: / + schedule: + interval: monthly -- cgit v1.2.3 From 11f73b0e7ffa34a6a5402ce3d51b9d99831f6429 Mon Sep 17 00:00:00 2001 From: Rye Date: Sun, 19 Oct 2025 16:38:34 -0400 Subject: Fix tabs and trailing spaces in objc sources --- .pre-commit-config.yaml | 6 +- indra/llfilesystem/lldir_utils_objc.mm | 28 +-- indra/llplugin/slplugin/slplugin-objc.mm | 98 ++++----- indra/llwindow/llopenglview-objc.mm | 328 +++++++++++++++---------------- indra/llwindow/llwindowmacosx-objc.mm | 214 ++++++++++---------- indra/newview/llappdelegate-objc.mm | 154 +++++++-------- indra/newview/llappviewermacosx-objc.mm | 12 +- indra/newview/llfilepicker_mac.mm | 112 +++++------ scripts/code_tools/fix_whitespace.py | 2 +- 9 files changed, 477 insertions(+), 477 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8baac5a81d..7e754714c6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,14 +7,14 @@ repos: - id: no-trigraphs - id: copyright - id: end-of-file - files: \.(cpp|c|h|py|glsl|cmake|txt)$ + files: \.(cpp|c|m|mm|h|py|glsl|cmake|txt)$ exclude: language.txt - id: indent-with-spaces - files: \.(cpp|c|h|inl|py|glsl|cmake)$ + files: \.(cpp|c|m|mm|h|inl|py|glsl|cmake)$ - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: - id: check-xml - id: mixed-line-ending - id: trailing-whitespace - files: \.(cpp|c|h|inl|py|glsl|cmake|yaml|sh)$ + files: \.(cpp|c|m|mm|h|inl|py|glsl|cmake|yaml|sh)$ diff --git a/indra/llfilesystem/lldir_utils_objc.mm b/indra/llfilesystem/lldir_utils_objc.mm index 01fe9e1f2c..35513d5647 100644 --- a/indra/llfilesystem/lldir_utils_objc.mm +++ b/indra/llfilesystem/lldir_utils_objc.mm @@ -1,28 +1,28 @@ -/** +/** * @file lldir_utils_objc.mm * @brief Cocoa implementation of directory utilities for macOS * * $LicenseInfo:firstyear=2020&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2020, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ - */ + */ #if LL_DARWIN //WARNING: This file CANNOT use standard linden includes due to conflicts between definitions of BOOL @@ -39,18 +39,18 @@ std::string getSystemTempFolder() tempDir = @"/tmp"; result = std::string([tempDir UTF8String]); } - + return result; } -//findSystemDirectory scoped exclusively to this file. +//findSystemDirectory scoped exclusively to this file. std::string findSystemDirectory(NSSearchPathDirectory searchPathDirectory, NSSearchPathDomainMask domainMask) { std::string result; @autoreleasepool { NSString *path = nil; - + // Search for the path NSArray* paths = NSSearchPathForDirectoriesInDomains(searchPathDirectory, domainMask, @@ -60,10 +60,10 @@ std::string findSystemDirectory(NSSearchPathDirectory searchPathDirectory, path = [paths objectAtIndex:0]; //HACK: Always attempt to create directory, ignore errors. NSError *error = nil; - + [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error]; - - + + result = std::string([path UTF8String]); } } @@ -88,7 +88,7 @@ std::string getSystemResourceFolder() NSString *bundlePath = [[NSBundle mainBundle] resourcePath]; result = std::string([bundlePath UTF8String]); } - + return result; } @@ -102,7 +102,7 @@ std::string getSystemApplicationSupportFolder() { return findSystemDirectory (NSApplicationSupportDirectory, NSUserDomainMask); - + } #endif // LL_DARWIN diff --git a/indra/llplugin/slplugin/slplugin-objc.mm b/indra/llplugin/slplugin/slplugin-objc.mm index 68ff196eaf..adde594b59 100644 --- a/indra/llplugin/slplugin/slplugin-objc.mm +++ b/indra/llplugin/slplugin/slplugin-objc.mm @@ -7,21 +7,21 @@ * $LicenseInfo:firstyear=2010&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ * @@ -38,51 +38,51 @@ void LLCocoaPlugin::setupCocoa() { - static bool inited = false; - - if(!inited) - { - createAutoReleasePool(); - - // The following prevents the Cocoa command line parser from trying to open 'unknown' arguements as documents. - // ie. running './secondlife -set Language fr' would cause a pop-up saying can't open document 'fr' - // when init'ing the Cocoa App window. - [[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"]; - - // This is a bit of voodoo taken from the Apple sample code "CarbonCocoa_PictureCursor": - // http://developer.apple.com/samplecode/CarbonCocoa_PictureCursor/index.html - - // Needed for Carbon based applications which call into Cocoa - NSApplicationLoad(); - - // Must first call [[[NSWindow alloc] init] release] to get the NSWindow machinery set up so that NSCursor can use a window to cache the cursor image - [[[NSWindow alloc] init] release]; - + static bool inited = false; + + if(!inited) + { + createAutoReleasePool(); + + // The following prevents the Cocoa command line parser from trying to open 'unknown' arguements as documents. + // ie. running './secondlife -set Language fr' would cause a pop-up saying can't open document 'fr' + // when init'ing the Cocoa App window. + [[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"]; + + // This is a bit of voodoo taken from the Apple sample code "CarbonCocoa_PictureCursor": + // http://developer.apple.com/samplecode/CarbonCocoa_PictureCursor/index.html + + // Needed for Carbon based applications which call into Cocoa + NSApplicationLoad(); + + // Must first call [[[NSWindow alloc] init] release] to get the NSWindow machinery set up so that NSCursor can use a window to cache the cursor image + [[[NSWindow alloc] init] release]; + mPluginWindow = [NSApp mainWindow]; - - deleteAutoReleasePool(); - - inited = true; - } + + deleteAutoReleasePool(); + + inited = true; + } } static NSAutoreleasePool *sPool = NULL; void LLCocoaPlugin::createAutoReleasePool() { - if(!sPool) - { - sPool = [[NSAutoreleasePool alloc] init]; - } + if(!sPool) + { + sPool = [[NSAutoreleasePool alloc] init]; + } } void LLCocoaPlugin::deleteAutoReleasePool() { - if(sPool) - { - [sPool release]; - sPool = NULL; - } + if(sPool) + { + [sPool release]; + sPool = NULL; + } } LLCocoaPlugin::LLCocoaPlugin():mHackState(0) @@ -110,12 +110,12 @@ void LLCocoaPlugin::setupGroup() // { // // Start out with a window layer that's way out in front (fixes the problem with the menubar not getting hidden on first switch to fullscreen youtube) // SetWindowGroupName(layer_group, CFSTR("SLPlugin Layer")); - // SetWindowGroupLevel(layer_group, kCGOverlayWindowLevel); + // SetWindowGroupLevel(layer_group, kCGOverlayWindowLevel); // } - + } -void LLCocoaPlugin::updateWindows() +void LLCocoaPlugin::updateWindows() { // NSArray* window_list = [NSApp orderedWindows]; // NSWindow* current_window = [window_list objectAtIndex:0]; @@ -123,38 +123,38 @@ void LLCocoaPlugin::updateWindows() // bool this_is_front_process = false; // bool parent_is_front_process = false; // -// +// // // Check for a change in this process's frontmost window. // if ( current_window != mFrontWindow ) // { // // and figure out whether this process or its parent are currently frontmost // if ( current_window == parent_window ) parent_is_front_process = true; // if ( current_window == mPluginWindow ) this_is_front_process = true; -// +// // if (current_window != NULL && mFrontWindow == NULL) // { // // Opening the first window -// +// // if(mHackState == 0) // { // // Next time through the event loop, lower the window group layer // mHackState = 1; // } -// +// // if(parent_is_front_process) // { // // Bring this process's windows to the front. // [mPluginWindow makeKeyAndOrderFront:NSApp]; // [mPluginWindow setOrderedIndex:0]; // } -// +// // [NSApp activateIgnoringOtherApps:YES]; // } -// +// // else if (( current_window == NULL) && (mFrontWindow != NULL)) // { // // Closing the last window -// +// // if(this_is_front_process) // { // // Try to bring this process's parent to the front @@ -171,7 +171,7 @@ void LLCocoaPlugin::updateWindows() //// } // mHackState = 2; // } -// +// // mFrontWindow = [window_list objectAtIndex:0]; // } } diff --git a/indra/llwindow/llopenglview-objc.mm b/indra/llwindow/llopenglview-objc.mm index c9a62eedb1..403103991e 100644 --- a/indra/llwindow/llopenglview-objc.mm +++ b/indra/llwindow/llopenglview-objc.mm @@ -114,7 +114,7 @@ attributedStringInfo getSegments(NSAttributedString *str) - (unsigned long)getVramSize { CGLRendererInfoObj info = 0; - GLint vram_megabytes = 0; + GLint vram_megabytes = 0; int num_renderers = 0; CGLError the_err = CGLQueryRendererInfo (CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay), &info, &num_renderers); if(0 == the_err) @@ -132,29 +132,29 @@ attributedStringInfo getSegments(NSAttributedString *str) vram_megabytes = 256; } - return (unsigned long)vram_megabytes; // return value is in megabytes. + return (unsigned long)vram_megabytes; // return value is in megabytes. } - (void)viewDidMoveToWindow { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(windowResized:) name:NSWindowDidResizeNotification - object:[self window]]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowResized:) name:NSWindowDidResizeNotification + object:[self window]]; [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(windowWillMiniaturize:) name:NSWindowWillMiniaturizeNotification - object:[self window]]; + selector:@selector(windowWillMiniaturize:) name:NSWindowWillMiniaturizeNotification + object:[self window]]; [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification - object:[self window]]; + selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification + object:[self window]]; [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification - object:[self window]]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(windowDidChangeScreen:) name:NSWindowDidChangeScreenNotification - object:[self window]]; + selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification + object:[self window]]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowDidChangeScreen:) name:NSWindowDidChangeScreenNotification + object:[self window]]; NSRect wnd_rect = [[self window] frame]; @@ -188,28 +188,28 @@ attributedStringInfo getSegments(NSAttributedString *str) -(void)windowDidChangeScreen:(NSNotification *)notification; { - callWindowDidChangeScreen(); + callWindowDidChangeScreen(); } - (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [super dealloc]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; } - (id) init { - return [self initWithFrame:[self bounds] withSamples:2 andVsync:TRUE]; + return [self initWithFrame:[self bounds] withSamples:2 andVsync:TRUE]; } - (id) initWithSamples:(NSUInteger)samples { - return [self initWithFrame:[self bounds] withSamples:samples andVsync:TRUE]; + return [self initWithFrame:[self bounds] withSamples:samples andVsync:TRUE]; } - (id) initWithSamples:(NSUInteger)samples andVsync:(BOOL)vsync { - return [self initWithFrame:[self bounds] withSamples:samples andVsync:vsync]; + return [self initWithFrame:[self bounds] withSamples:samples andVsync:vsync]; } #if LL_DARWIN @@ -221,90 +221,90 @@ attributedStringInfo getSegments(NSAttributedString *str) - (id) initWithFrame:(NSRect)frame withSamples:(NSUInteger)samples andVsync:(BOOL)vsync { [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeURL]]; - [self initWithFrame:frame]; + [self initWithFrame:frame]; - // Initialize with a default "safe" pixel format that will work with versions dating back to OS X 10.6. - // Any specialized pixel formats, i.e. a core profile pixel format, should be initialized through rebuildContextWithFormat. - // 10.7 and 10.8 don't really care if we're defining a profile or not. If we don't explicitly request a core or legacy profile, it'll always assume a legacy profile (for compatibility reasons). - NSOpenGLPixelFormatAttribute attrs[] = { + // Initialize with a default "safe" pixel format that will work with versions dating back to OS X 10.6. + // Any specialized pixel formats, i.e. a core profile pixel format, should be initialized through rebuildContextWithFormat. + // 10.7 and 10.8 don't really care if we're defining a profile or not. If we don't explicitly request a core or legacy profile, it'll always assume a legacy profile (for compatibility reasons). + NSOpenGLPixelFormatAttribute attrs[] = { NSOpenGLPFANoRecovery, - NSOpenGLPFADoubleBuffer, - NSOpenGLPFAClosestPolicy, - NSOpenGLPFAAccelerated, - NSOpenGLPFASampleBuffers, 0, - NSOpenGLPFASamples, 0, - NSOpenGLPFAStencilSize, 8, - NSOpenGLPFADepthSize, 24, - NSOpenGLPFAAlphaSize, 8, - NSOpenGLPFAColorSize, 24, - NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core, - 0 + NSOpenGLPFADoubleBuffer, + NSOpenGLPFAClosestPolicy, + NSOpenGLPFAAccelerated, + NSOpenGLPFASampleBuffers, 0, + NSOpenGLPFASamples, 0, + NSOpenGLPFAStencilSize, 8, + NSOpenGLPFADepthSize, 24, + NSOpenGLPFAAlphaSize, 8, + NSOpenGLPFAColorSize, 24, + NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core, + 0 }; - NSOpenGLPixelFormat *pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs] autorelease]; + NSOpenGLPixelFormat *pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs] autorelease]; - if (pixelFormat == nil) - { - NSLog(@"Failed to create pixel format!", nil); - return nil; - } + if (pixelFormat == nil) + { + NSLog(@"Failed to create pixel format!", nil); + return nil; + } - NSOpenGLContext *glContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; + NSOpenGLContext *glContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; - if (glContext == nil) - { - NSLog(@"Failed to create OpenGL context!", nil); - return nil; - } + if (glContext == nil) + { + NSLog(@"Failed to create OpenGL context!", nil); + return nil; + } - [self setPixelFormat:pixelFormat]; + [self setPixelFormat:pixelFormat]; - //for retina support - [self setWantsBestResolutionOpenGLSurface:gHiDPISupport]; + //for retina support + [self setWantsBestResolutionOpenGLSurface:gHiDPISupport]; - [self setOpenGLContext:glContext]; + [self setOpenGLContext:glContext]; - [glContext setView:self]; + [glContext setView:self]; - [glContext makeCurrentContext]; + [glContext makeCurrentContext]; - if (vsync) - { - GLint value = 1; + if (vsync) + { + GLint value = 1; [glContext setValues:&value forParameter:NSOpenGLContextParameterSwapInterval]; - } else { - // supress this error after move to Xcode 7: - // error: null passed to a callee that requires a non-null argument [-Werror,-Wnonnull] - // Tried using ObjC 'nonnull' keyword as per SO article but didn't build - GLint swapInterval=0; + } else { + // supress this error after move to Xcode 7: + // error: null passed to a callee that requires a non-null argument [-Werror,-Wnonnull] + // Tried using ObjC 'nonnull' keyword as per SO article but didn't build + GLint swapInterval=0; [glContext setValues:&swapInterval forParameter:NSOpenGLContextParameterSwapInterval]; - } + } - return self; + return self; } - (BOOL) rebuildContext { - return [self rebuildContextWithFormat:[self pixelFormat]]; + return [self rebuildContextWithFormat:[self pixelFormat]]; } - (BOOL) rebuildContextWithFormat:(NSOpenGLPixelFormat *)format { - NSOpenGLContext *ctx = [self openGLContext]; + NSOpenGLContext *ctx = [self openGLContext]; - [ctx clearDrawable]; - [ctx initWithFormat:format shareContext:nil]; + [ctx clearDrawable]; + [ctx initWithFormat:format shareContext:nil]; - if (ctx == nil) - { - NSLog(@"Failed to create OpenGL context!", nil); - return false; - } + if (ctx == nil) + { + NSLog(@"Failed to create OpenGL context!", nil); + return false; + } - [self setOpenGLContext:ctx]; - [ctx setView:self]; - [ctx makeCurrentContext]; - return true; + [self setOpenGLContext:ctx]; + [ctx setView:self]; + [ctx makeCurrentContext]; + return true; } #if LL_DARWIN @@ -313,14 +313,14 @@ attributedStringInfo getSegments(NSAttributedString *str) - (CGLContextObj)getCGLContextObj { - NSOpenGLContext *ctx = [self openGLContext]; - return (CGLContextObj)[ctx CGLContextObj]; + NSOpenGLContext *ctx = [self openGLContext]; + return (CGLContextObj)[ctx CGLContextObj]; } - (CGLPixelFormatObj*)getCGLPixelFormatObj { - NSOpenGLPixelFormat *fmt = [self pixelFormat]; - return (CGLPixelFormatObj*)[fmt CGLPixelFormatObj]; + NSOpenGLPixelFormat *fmt = [self pixelFormat]; + return (CGLPixelFormatObj*)[fmt CGLPixelFormatObj]; } // Various events can be intercepted by our view, thus not reaching our window. @@ -365,29 +365,29 @@ attributedStringInfo getSegments(NSAttributedString *str) - (void) rightMouseDown:(NSEvent *)theEvent { - callRightMouseDown(mMousePos, [theEvent modifierFlags]); + callRightMouseDown(mMousePos, [theEvent modifierFlags]); } - (void) rightMouseUp:(NSEvent *)theEvent { - callRightMouseUp(mMousePos, [theEvent modifierFlags]); + callRightMouseUp(mMousePos, [theEvent modifierFlags]); } - (void)mouseMoved:(NSEvent *)theEvent { NSPoint dev_delta = [self convertPointToBacking:NSMakePoint([theEvent deltaX], [theEvent deltaY])]; - float mouseDeltas[] = { - float(dev_delta.x), - float(dev_delta.y) - }; + float mouseDeltas[] = { + float(dev_delta.x), + float(dev_delta.y) + }; - callDeltaUpdate(mouseDeltas, 0); + callDeltaUpdate(mouseDeltas, 0); NSPoint mPoint = [self convertPointToBacking:[theEvent locationInWindow]]; - mMousePos[0] = mPoint.x; - mMousePos[1] = mPoint.y; - callMouseMoved(mMousePos, 0); + mMousePos[0] = mPoint.x; + mMousePos[1] = mPoint.y; + callMouseMoved(mMousePos, 0); } // NSWindow doesn't trigger mouseMoved when the mouse is being clicked and dragged. @@ -395,23 +395,23 @@ attributedStringInfo getSegments(NSAttributedString *str) - (void) mouseDragged:(NSEvent *)theEvent { - // Trust the deltas supplied by NSEvent. - // The old CoreGraphics APIs we previously relied on are now flagged as obsolete. - // NSEvent isn't obsolete, and provides us with the correct deltas. + // Trust the deltas supplied by NSEvent. + // The old CoreGraphics APIs we previously relied on are now flagged as obsolete. + // NSEvent isn't obsolete, and provides us with the correct deltas. NSPoint dev_delta = [self convertPointToBacking:NSMakePoint([theEvent deltaX], [theEvent deltaY])]; - float mouseDeltas[] = { - float(dev_delta.x), - float(dev_delta.y) - }; + float mouseDeltas[] = { + float(dev_delta.x), + float(dev_delta.y) + }; - callDeltaUpdate(mouseDeltas, 0); + callDeltaUpdate(mouseDeltas, 0); - NSPoint mPoint = [self convertPointToBacking:[theEvent locationInWindow]]; - mMousePos[0] = mPoint.x; - mMousePos[1] = mPoint.y; - callMouseDragged(mMousePos, 0); + NSPoint mPoint = [self convertPointToBacking:[theEvent locationInWindow]]; + mMousePos[0] = mPoint.x; + mMousePos[1] = mPoint.y; + callMouseDragged(mMousePos, 0); } - (void) otherMouseDown:(NSEvent *)theEvent @@ -426,29 +426,29 @@ attributedStringInfo getSegments(NSAttributedString *str) - (void) rightMouseDragged:(NSEvent *)theEvent { - [self mouseDragged:theEvent]; + [self mouseDragged:theEvent]; } - (void) otherMouseDragged:(NSEvent *)theEvent { - [self mouseDragged:theEvent]; + [self mouseDragged:theEvent]; } - (void) scrollWheel:(NSEvent *)theEvent { - callScrollMoved(-[theEvent deltaX], -[theEvent deltaY]); + callScrollMoved(-[theEvent deltaX], -[theEvent deltaY]); } - (void) mouseExited:(NSEvent *)theEvent { - callMouseExit(); + callMouseExit(); } - (void) keyUp:(NSEvent *)theEvent { NativeKeyEventData eventData = extractKeyDataFromKeyEvent(theEvent); eventData.mKeyEvent = NativeKeyEventData::KEYUP; - callKeyUp(&eventData, [theEvent keyCode], [theEvent modifierFlags]); + callKeyUp(&eventData, [theEvent keyCode], [theEvent modifierFlags]); } - (void) keyDown:(NSEvent *)theEvent @@ -462,7 +462,7 @@ attributedStringInfo getSegments(NSAttributedString *str) // Because flagsChange event handler misses event when other window is activated, // e.g. OS Window for upload something or Input Window... // mModifiers instance variable is for insertText: or insertText:replacementRange: (by Pell Smit) - mModifiers = [theEvent modifierFlags]; + mModifiers = [theEvent modifierFlags]; NSString *str_no_modifiers = [theEvent charactersIgnoringModifiers]; unichar ch = 0; if (str_no_modifiers.length) @@ -490,8 +490,8 @@ attributedStringInfo getSegments(NSAttributedString *str) { NativeKeyEventData eventData = extractKeyDataFromModifierEvent(theEvent); - mModifiers = [theEvent modifierFlags]; - callModifier([theEvent modifierFlags]); + mModifiers = [theEvent modifierFlags]; + callModifier([theEvent modifierFlags]); NSInteger mask = 0; switch([theEvent keyCode]) @@ -532,69 +532,69 @@ attributedStringInfo getSegments(NSAttributedString *str) - (BOOL) acceptsFirstResponder { - return YES; + return YES; } - (NSDragOperation) draggingEntered:(id)sender { - NSPasteboard *pboard; + NSPasteboard *pboard; NSDragOperation sourceDragMask; - sourceDragMask = [sender draggingSourceOperationMask]; + sourceDragMask = [sender draggingSourceOperationMask]; - pboard = [sender draggingPasteboard]; + pboard = [sender draggingPasteboard]; if ([[pboard types] containsObject:NSPasteboardTypeURL]) - { - if (sourceDragMask & NSDragOperationLink) { - NSURL *fileUrl = [[pboard readObjectsForClasses:[NSArray arrayWithObject:[NSURL class]] options:[NSDictionary dictionary]] objectAtIndex:0]; - mLastDraggedUrl = [[fileUrl absoluteString] UTF8String]; + { + if (sourceDragMask & NSDragOperationLink) { + NSURL *fileUrl = [[pboard readObjectsForClasses:[NSArray arrayWithObject:[NSURL class]] options:[NSDictionary dictionary]] objectAtIndex:0]; + mLastDraggedUrl = [[fileUrl absoluteString] UTF8String]; return NSDragOperationLink; } - } - return NSDragOperationNone; + } + return NSDragOperationNone; } - (NSDragOperation)draggingUpdated:(id )sender { - callHandleDragUpdated(mLastDraggedUrl); + callHandleDragUpdated(mLastDraggedUrl); - return NSDragOperationLink; + return NSDragOperationLink; } - (void) draggingExited:(id)sender { - callHandleDragExited(mLastDraggedUrl); + callHandleDragExited(mLastDraggedUrl); } - (BOOL)prepareForDragOperation:(id < NSDraggingInfo >)sender { - return YES; + return YES; } - (BOOL) performDragOperation:(id)sender { - callHandleDragDropped(mLastDraggedUrl); - return true; + callHandleDragDropped(mLastDraggedUrl); + return true; } - (BOOL)hasMarkedText { - return mHasMarkedText; + return mHasMarkedText; } - (NSRange)markedRange { - int range[2]; - getPreeditMarkedRange(&range[0], &range[1]); - return NSMakeRange(range[0], range[1]); + int range[2]; + getPreeditMarkedRange(&range[0], &range[1]); + return NSMakeRange(range[0], range[1]); } - (NSRange)selectedRange { - int range[2]; - getPreeditSelectionRange(&range[0], &range[1]); - return NSMakeRange(range[0], range[1]); + int range[2]; + getPreeditSelectionRange(&range[0], &range[1]); + return NSMakeRange(range[0], range[1]); } - (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange @@ -680,21 +680,21 @@ attributedStringInfo getSegments(NSAttributedString *str) - (void)unmarkText { - [[self inputContext] discardMarkedText]; - resetPreedit(); - mHasMarkedText = FALSE; + [[self inputContext] discardMarkedText]; + resetPreedit(); + mHasMarkedText = FALSE; } // We don't support attributed strings. - (NSArray *)validAttributesForMarkedText { - return [NSArray array]; + return [NSArray array]; } // See above. - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { - return nil; + return nil; } - (void)insertText:(id)insertString @@ -707,9 +707,9 @@ attributedStringInfo getSegments(NSAttributedString *str) - (void)insertText:(id)aString replacementRange:(NSRange)replacementRange { - // SL-19801 Special workaround for system emoji picker - if ([aString length] == 2) - { + // SL-19801 Special workaround for system emoji picker + if ([aString length] == 2) + { @try { uint32_t b0 = [aString characterAtIndex:0]; @@ -727,7 +727,7 @@ attributedStringInfo getSegments(NSAttributedString *str) NSLog(@"Encountered an unsupported attributed character. Exception: %@ String: %@", e.name, aString); return; } - } + } @try { @@ -760,36 +760,36 @@ attributedStringInfo getSegments(NSAttributedString *str) if (!(mModifiers & NSEventModifierFlagCommand) && !(mModifiers & NSEventModifierFlagShift) && !(mModifiers & NSEventModifierFlagOption)) - { - callUnicodeCallback(13, 0); - } else { - callUnicodeCallback(13, mModifiers); - } + { + callUnicodeCallback(13, 0); + } else { + callUnicodeCallback(13, mModifiers); + } } - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint { - return NSNotFound; + return NSNotFound; } - (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { - float pos[4] = {0, 0, 0, 0}; - getPreeditLocation(pos, mMarkedTextLength); - return NSMakeRect(pos[0], pos[1], pos[2], pos[3]); + float pos[4] = {0, 0, 0, 0}; + getPreeditLocation(pos, mMarkedTextLength); + return NSMakeRect(pos[0], pos[1], pos[2], pos[3]); } - (void)doCommandBySelector:(SEL)aSelector { - if (aSelector == @selector(insertNewline:)) - { - [self insertNewline:self]; - } + if (aSelector == @selector(insertNewline:)) + { + [self insertNewline:self]; + } } - (BOOL)drawsVerticallyForCharacterAtIndex:(NSUInteger)charIndex { - return NO; + return NO; } - (void) allowMarkedTextInput:(bool)allowed @@ -824,7 +824,7 @@ attributedStringInfo getSegments(NSAttributedString *str) - (void) setGLView:(LLOpenGLView *)view { - glview = view; + glview = view; } - (void)keyDown:(NSEvent *)theEvent @@ -875,24 +875,24 @@ attributedStringInfo getSegments(NSAttributedString *str) - (id) init { - return self; + return self; } - (BOOL) becomeFirstResponder { - callFocus(); - return true; + callFocus(); + return true; } - (BOOL) resignFirstResponder { - callFocusLost(); - return true; + callFocusLost(); + return true; } - (void) close { - callQuitHandler(); + callQuitHandler(); } @end diff --git a/indra/llwindow/llwindowmacosx-objc.mm b/indra/llwindow/llwindowmacosx-objc.mm index 42cd95be5d..d902a82a3c 100644 --- a/indra/llwindow/llwindowmacosx-objc.mm +++ b/indra/llwindow/llwindowmacosx-objc.mm @@ -41,15 +41,15 @@ int createNSApp(int argc, const char *argv[]) { - return NSApplicationMain(argc, argv); + return NSApplicationMain(argc, argv); } void setupCocoa() { - static bool inited = false; - - if(!inited) - { + static bool inited = false; + + if(!inited) + { @autoreleasepool { // The following prevents the Cocoa command line parser from trying to open 'unknown' arguements as documents. // ie. running './secondlife -set Language fr' would cause a pop-up saying can't open document 'fr' @@ -57,8 +57,8 @@ void setupCocoa() [[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"]; } - inited = true; - } + inited = true; + } } bool copyToPBoard(const unsigned short *str, unsigned int len) @@ -66,7 +66,7 @@ bool copyToPBoard(const unsigned short *str, unsigned int len) @autoreleasepool { NSPasteboard *pboard = [NSPasteboard generalPasteboard]; [pboard clearContents]; - + NSArray *contentsToPaste = [[[NSArray alloc] initWithObjects:[NSString stringWithCharacters:str length:len], nil] autorelease]; return [pboard writeObjects:contentsToPaste]; } @@ -74,8 +74,8 @@ bool copyToPBoard(const unsigned short *str, unsigned int len) bool pasteBoardAvailable() { - NSArray *classArray = [NSArray arrayWithObject:[NSString class]]; - return [[NSPasteboard generalPasteboard] canReadObjectForClasses:classArray options:[NSDictionary dictionary]]; + NSArray *classArray = [NSArray arrayWithObject:[NSString class]]; + return [[NSPasteboard generalPasteboard] canReadObjectForClasses:classArray options:[NSDictionary dictionary]]; } unsigned short *copyFromPBoard() @@ -111,55 +111,55 @@ CursorRef createImageCursor(const char *fullpath, int hotspotX, int hotspotY) hotSpot:NSMakePoint(hotspotX, hotspotY) ] retain]; } - - return (CursorRef)cursor; + + return (CursorRef)cursor; } void setArrowCursor() { - NSCursor *cursor = [NSCursor arrowCursor]; - [NSCursor unhide]; - [cursor set]; + NSCursor *cursor = [NSCursor arrowCursor]; + [NSCursor unhide]; + [cursor set]; } void setIBeamCursor() { - NSCursor *cursor = [NSCursor IBeamCursor]; - [cursor set]; + NSCursor *cursor = [NSCursor IBeamCursor]; + [cursor set]; } void setPointingHandCursor() { - NSCursor *cursor = [NSCursor pointingHandCursor]; - [cursor set]; + NSCursor *cursor = [NSCursor pointingHandCursor]; + [cursor set]; } void setCopyCursor() { - NSCursor *cursor = [NSCursor dragCopyCursor]; - [cursor set]; + NSCursor *cursor = [NSCursor dragCopyCursor]; + [cursor set]; } void setCrossCursor() { - NSCursor *cursor = [NSCursor crosshairCursor]; - [cursor set]; + NSCursor *cursor = [NSCursor crosshairCursor]; + [cursor set]; } void setNotAllowedCursor() { - NSCursor *cursor = [NSCursor operationNotAllowedCursor]; - [cursor set]; + NSCursor *cursor = [NSCursor operationNotAllowedCursor]; + [cursor set]; } void hideNSCursor() { - [NSCursor hide]; + [NSCursor hide]; } void showNSCursor() { - [NSCursor unhide]; + [NSCursor unhide]; } #if LL_DARWIN @@ -179,42 +179,42 @@ bool isCGCursorVisible() void hideNSCursorTillMove(bool hide) { - [NSCursor setHiddenUntilMouseMoves:hide]; + [NSCursor setHiddenUntilMouseMoves:hide]; } // This is currently unused, since we want all our cursors to persist for the life of the app, but I've included it for completeness. OSErr releaseImageCursor(CursorRef ref) { - if( ref != NULL ) - { + if( ref != NULL ) + { @autoreleasepool { NSCursor *cursor = (NSCursor*)ref; [cursor autorelease]; } - } - else - { - return paramErr; - } - - return noErr; + } + else + { + return paramErr; + } + + return noErr; } OSErr setImageCursor(CursorRef ref) { - if( ref != NULL ) - { + if( ref != NULL ) + { @autoreleasepool { NSCursor *cursor = (NSCursor*)ref; [cursor set]; } - } - else - { - return paramErr; - } - - return noErr; + } + else + { + return paramErr; + } + + return noErr; } // Now for some unholy juggling between generic pointers and casting them to Obj-C objects! @@ -222,46 +222,46 @@ OSErr setImageCursor(CursorRef ref) NSWindowRef createNSWindow(int x, int y, int width, int height) { - LLNSWindow *window = [[LLNSWindow alloc]initWithContentRect:NSMakeRect(x, y, width, height) + LLNSWindow *window = [[LLNSWindow alloc]initWithContentRect:NSMakeRect(x, y, width, height) styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskResizable | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable backing:NSBackingStoreBuffered defer:NO]; - [window makeKeyAndOrderFront:nil]; - [window setAcceptsMouseMovedEvents:TRUE]; + [window makeKeyAndOrderFront:nil]; + [window setAcceptsMouseMovedEvents:TRUE]; [window setRestorable:FALSE]; // Viewer manages state from own settings - return window; + return window; } GLViewRef createOpenGLView(NSWindowRef window, unsigned int samples, bool vsync) { - LLOpenGLView *glview = [[LLOpenGLView alloc]initWithFrame:[(LLNSWindow*)window frame] withSamples:samples andVsync:vsync]; - [(LLNSWindow*)window setContentView:glview]; - return glview; + LLOpenGLView *glview = [[LLOpenGLView alloc]initWithFrame:[(LLNSWindow*)window frame] withSamples:samples andVsync:vsync]; + [(LLNSWindow*)window setContentView:glview]; + return glview; } void glSwapBuffers(void* context) { - [(NSOpenGLContext*)context flushBuffer]; + [(NSOpenGLContext*)context flushBuffer]; } CGLContextObj getCGLContextObj(GLViewRef view) { - return [(LLOpenGLView *)view getCGLContextObj]; + return [(LLOpenGLView *)view getCGLContextObj]; } CGLPixelFormatObj* getCGLPixelFormatObj(NSWindowRef window) { - LLOpenGLView *glview = [(LLNSWindow*)window contentView]; - return [glview getCGLPixelFormatObj]; + LLOpenGLView *glview = [(LLNSWindow*)window contentView]; + return [glview getCGLPixelFormatObj]; } unsigned long getVramSize(GLViewRef view) { - return [(LLOpenGLView *)view getVramSize]; + return [(LLOpenGLView *)view getVramSize]; } float getDeviceUnitSize(GLViewRef view) { - return [(LLOpenGLView*)view convertSizeToBacking:NSMakeSize(1, 1)].width; + return [(LLOpenGLView*)view convertSizeToBacking:NSMakeSize(1, 1)].width; } CGRect getContentViewRect(NSWindowRef window) @@ -276,48 +276,48 @@ CGRect getBackingViewRect(NSWindowRef window, GLViewRef view) void getWindowSize(NSWindowRef window, float* size) { - NSRect frame = [(LLNSWindow*)window frame]; - size[0] = frame.origin.x; - size[1] = frame.origin.y; - size[2] = frame.size.width; - size[3] = frame.size.height; + NSRect frame = [(LLNSWindow*)window frame]; + size[0] = frame.origin.x; + size[1] = frame.origin.y; + size[2] = frame.size.width; + size[3] = frame.size.height; } void setWindowSize(NSWindowRef window, int width, int height) { - NSRect frame = [(LLNSWindow*)window frame]; - frame.size.width = width; - frame.size.height = height; - [(LLNSWindow*)window setFrame:frame display:TRUE]; + NSRect frame = [(LLNSWindow*)window frame]; + frame.size.width = width; + frame.size.height = height; + [(LLNSWindow*)window setFrame:frame display:TRUE]; } void setWindowPos(NSWindowRef window, float* pos) { - NSPoint point; - point.x = pos[0]; - point.y = pos[1]; - [(LLNSWindow*)window setFrameOrigin:point]; + NSPoint point; + point.x = pos[0]; + point.y = pos[1]; + [(LLNSWindow*)window setFrameOrigin:point]; } void getCursorPos(NSWindowRef window, float* pos) { - NSPoint mLoc; - mLoc = [(LLNSWindow*)window mouseLocationOutsideOfEventStream]; - pos[0] = mLoc.x; - pos[1] = mLoc.y; + NSPoint mLoc; + mLoc = [(LLNSWindow*)window mouseLocationOutsideOfEventStream]; + pos[0] = mLoc.x; + pos[1] = mLoc.y; } void makeWindowOrderFront(NSWindowRef window) { - [(LLNSWindow*)window makeKeyAndOrderFront:nil]; + [(LLNSWindow*)window makeKeyAndOrderFront:nil]; } void convertScreenToWindow(NSWindowRef window, float *coord) { NSRect point = NSMakeRect(coord[0], coord[1], 0,0); - point = [(LLNSWindow*)window convertRectFromScreen:point]; - coord[0] = point.origin.x; - coord[1] = point.origin.y; + point = [(LLNSWindow*)window convertRectFromScreen:point]; + coord[0] = point.origin.x; + coord[1] = point.origin.y; } void convertRectToScreen(NSWindowRef window, float *coord) @@ -325,21 +325,21 @@ void convertRectToScreen(NSWindowRef window, float *coord) NSRect rect = NSMakeRect(coord[0], coord[1], coord[2], coord[3]);; rect = [(LLNSWindow*)window convertRectToScreen:rect]; - coord[0] = rect.origin.x; - coord[1] = rect.origin.y; - coord[2] = rect.size.width; - coord[3] = rect.size.height; + coord[0] = rect.origin.x; + coord[1] = rect.origin.y; + coord[2] = rect.size.width; + coord[3] = rect.size.height; } void convertRectFromScreen(NSWindowRef window, float *coord) { - NSRect point = NSMakeRect(coord[0], coord[1], coord[2], coord[3]); - point = [(LLNSWindow*)window convertRectFromScreen:point]; - - coord[0] = point.origin.x; - coord[1] = point.origin.y; - coord[2] = point.size.width; - coord[3] = point.size.height; + NSRect point = NSMakeRect(coord[0], coord[1], coord[2], coord[3]); + point = [(LLNSWindow*)window convertRectFromScreen:point]; + + coord[0] = point.origin.x; + coord[1] = point.origin.y; + coord[2] = point.size.width; + coord[3] = point.size.height; } void convertWindowToScreen(NSWindowRef window, float *coord) @@ -353,24 +353,24 @@ void convertWindowToScreen(NSWindowRef window, float *coord) void closeWindow(NSWindowRef window) { - [(LLNSWindow*)window close]; - [(LLNSWindow*)window release]; + [(LLNSWindow*)window close]; + [(LLNSWindow*)window release]; } void removeGLView(GLViewRef view) { - [(LLOpenGLView*)view clearGLContext]; - [(LLOpenGLView*)view removeFromSuperview]; + [(LLOpenGLView*)view clearGLContext]; + [(LLOpenGLView*)view removeFromSuperview]; } void setupInputWindow(NSWindowRef window, GLViewRef glview) { - [[(LLAppDelegate*)[NSApp delegate] inputView] setGLView:(LLOpenGLView*)glview]; + [[(LLAppDelegate*)[NSApp delegate] inputView] setGLView:(LLOpenGLView*)glview]; } void commitCurrentPreedit(GLViewRef glView) { - [(LLOpenGLView*)glView commitCurrentPreedit]; + [(LLOpenGLView*)glView commitCurrentPreedit]; } void allowDirectMarkedTextInput(bool allow, GLViewRef glView) @@ -380,20 +380,20 @@ void allowDirectMarkedTextInput(bool allow, GLViewRef glView) NSWindowRef getMainAppWindow() { - LLNSWindow *winRef = [(LLAppDelegate*)[[NSApplication sharedApplication] delegate] window]; - - [winRef setAcceptsMouseMovedEvents:TRUE]; - return winRef; + LLNSWindow *winRef = [(LLAppDelegate*)[[NSApplication sharedApplication] delegate] window]; + + [winRef setAcceptsMouseMovedEvents:TRUE]; + return winRef; } void makeFirstResponder(NSWindowRef window, GLViewRef view) { - [(LLNSWindow*)window makeFirstResponder:(LLOpenGLView*)view]; + [(LLNSWindow*)window makeFirstResponder:(LLOpenGLView*)view]; } void requestUserAttention() { - [[NSApplication sharedApplication] requestUserAttention:NSInformationalRequest]; + [[NSApplication sharedApplication] requestUserAttention:NSInformationalRequest]; } long showAlert(std::string text, std::string title, int type) @@ -401,7 +401,7 @@ long showAlert(std::string text, std::string title, int type) long ret = 0; @autoreleasepool { NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - + [alert setMessageText:[NSString stringWithCString:title.c_str() encoding:[NSString defaultCStringEncoding]]]; [alert setInformativeText:[NSString stringWithCString:text.c_str() encoding:[NSString defaultCStringEncoding]]]; if (type == 0) @@ -418,7 +418,7 @@ long showAlert(std::string text, std::string title, int type) } ret = [alert runModal]; } - + if (ret == NSAlertFirstButtonReturn) { if (type == 1) @@ -438,7 +438,7 @@ long showAlert(std::string text, std::string title, int type) ret = 1; } } - + return ret; } @@ -451,5 +451,5 @@ long showAlert(std::string text, std::string title, int type) unsigned int getModifiers() { - return [NSEvent modifierFlags]; + return [NSEvent modifierFlags]; } diff --git a/indra/newview/llappdelegate-objc.mm b/indra/newview/llappdelegate-objc.mm index b8fd3dc189..af18dca185 100644 --- a/indra/newview/llappdelegate-objc.mm +++ b/indra/newview/llappdelegate-objc.mm @@ -57,42 +57,42 @@ - (void) applicationDidFinishLaunching:(NSNotification *)notification { - // Call constructViewer() first so our logging subsystem is in place. This - // risks missing crashes in the LLAppViewerMacOSX constructor, but for - // present purposes it's more important to get the startup sequence - // properly logged. - // Someday I would like to modify the logging system so that calls before - // it's initialized are cached in a std::ostringstream and then, once it's - // initialized, "played back" into whatever handlers have been set up. - constructViewer(); + // Call constructViewer() first so our logging subsystem is in place. This + // risks missing crashes in the LLAppViewerMacOSX constructor, but for + // present purposes it's more important to get the startup sequence + // properly logged. + // Someday I would like to modify the logging system so that calls before + // it's initialized are cached in a std::ostringstream and then, once it's + // initialized, "played back" into whatever handlers have been set up. + constructViewer(); #if defined(LL_BUGSPLAT) infos("bugsplat setup"); - // Engage BugsplatStartupManager *before* calling initViewer() to handle - // any crashes during initialization. - // https://www.bugsplat.com/docs/platforms/os-x#initialization - [BugsplatStartupManager sharedManager].autoSubmitCrashReport = YES; - [BugsplatStartupManager sharedManager].askUserDetails = NO; - [BugsplatStartupManager sharedManager].delegate = self; - [[BugsplatStartupManager sharedManager] start]; + // Engage BugsplatStartupManager *before* calling initViewer() to handle + // any crashes during initialization. + // https://www.bugsplat.com/docs/platforms/os-x#initialization + [BugsplatStartupManager sharedManager].autoSubmitCrashReport = YES; + [BugsplatStartupManager sharedManager].askUserDetails = NO; + [BugsplatStartupManager sharedManager].delegate = self; + [[BugsplatStartupManager sharedManager] start]; #endif infos("post-bugsplat setup"); - frameTimer = nil; + frameTimer = nil; - [self languageUpdated]; + [self languageUpdated]; - if (initViewer()) - { - // Set up recurring calls to oneFrame (repeating timer with timeout 0) - // until applicationShouldTerminate. - frameTimer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:self - selector:@selector(oneFrame) userInfo:nil repeats:YES]; - } else { - exit(0); - } + if (initViewer()) + { + // Set up recurring calls to oneFrame (repeating timer with timeout 0) + // until applicationShouldTerminate. + frameTimer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:self + selector:@selector(oneFrame) userInfo:nil repeats:YES]; + } else { + exit(0); + } - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(languageUpdated) name:@"NSTextInputContextKeyboardSelectionDidChangeNotification" object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(languageUpdated) name:@"NSTextInputContextKeyboardSelectionDidChangeNotification" object:nil]; // [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:@selector(handleGetURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL]; } @@ -110,74 +110,74 @@ - (void) applicationDidBecomeActive:(NSNotification *)notification { - callWindowFocus(); + callWindowFocus(); } - (void) applicationDidResignActive:(NSNotification *)notification { - callWindowUnfocus(); + callWindowUnfocus(); } - (void) applicationDidHide:(NSNotification *)notification { - callWindowHide(); + callWindowHide(); } - (void) applicationDidUnhide:(NSNotification *)notification { - callWindowUnhide(); + callWindowUnhide(); } - (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication *)sender { - // run one frame to assess state - if (!pumpMainLoop()) - { - // pumpMainLoop() returns true when done, false if it wants to be - // called again. Since it returned false, do not yet cancel - // frameTimer. - handleQuit(); - [[NSApplication sharedApplication] stopModal]; - return NSTerminateCancel; - } else { - // pumpMainLoop() returned true: it's done. Okay, done with frameTimer. - [frameTimer release]; - cleanupViewer(); - return NSTerminateNow; - } + // run one frame to assess state + if (!pumpMainLoop()) + { + // pumpMainLoop() returns true when done, false if it wants to be + // called again. Since it returned false, do not yet cancel + // frameTimer. + handleQuit(); + [[NSApplication sharedApplication] stopModal]; + return NSTerminateCancel; + } else { + // pumpMainLoop() returned true: it's done. Okay, done with frameTimer. + [frameTimer release]; + cleanupViewer(); + return NSTerminateNow; + } } - (void) oneFrame { - bool appExiting = pumpMainLoop(); - if (appExiting) - { - // Once pumpMainLoop() reports that we're done, cancel frameTimer: - // stop the repetitive calls. - [frameTimer release]; - [[NSApplication sharedApplication] terminate:self]; - } + bool appExiting = pumpMainLoop(); + if (appExiting) + { + // Once pumpMainLoop() reports that we're done, cancel frameTimer: + // stop the repetitive calls. + [frameTimer release]; + [[NSApplication sharedApplication] terminate:self]; + } } - (void) showInputWindow:(bool)show withEvent:(NSEvent*)textEvent { - if (![self romanScript]) - { - if (show) - { - NSLog(@"Showing input window."); - [inputWindow makeKeyAndOrderFront:inputWindow]; + if (![self romanScript]) + { + if (show) + { + NSLog(@"Showing input window."); + [inputWindow makeKeyAndOrderFront:inputWindow]; if (textEvent != nil) { [[inputView inputContext] discardMarkedText]; [[inputView inputContext] handleEvent:textEvent]; } - } else { - NSLog(@"Hiding input window."); - [inputWindow orderOut:inputWindow]; - [window makeKeyAndOrderFront:window]; - } - } + } else { + NSLog(@"Hiding input window."); + [inputWindow orderOut:inputWindow]; + [window makeKeyAndOrderFront:window]; + } + } } // This will get called multiple times by NSNotificationCenter. @@ -187,15 +187,15 @@ - (void) languageUpdated { - TISInputSourceRef currentInput = TISCopyCurrentKeyboardInputSource(); - CFArrayRef languages = (CFArrayRef)TISGetInputSourceProperty(currentInput, kTISPropertyInputSourceLanguages); - + TISInputSourceRef currentInput = TISCopyCurrentKeyboardInputSource(); + CFArrayRef languages = (CFArrayRef)TISGetInputSourceProperty(currentInput, kTISPropertyInputSourceLanguages); + #if 0 // In the event of ever needing to add new language sources, change this to 1 and watch the terminal for "languages:" - NSLog(@"languages: %@", TISGetInputSourceProperty(currentInput, kTISPropertyInputSourceLanguages)); + NSLog(@"languages: %@", TISGetInputSourceProperty(currentInput, kTISPropertyInputSourceLanguages)); #endif - - // Typically the language we want is going to be the very first result in the array. - currentInputLanguage = (NSString*)CFArrayGetValueAtIndex(languages, 0); + + // Typically the language we want is going to be the very first result in the array. + currentInputLanguage = (NSString*)CFArrayGetValueAtIndex(languages, 0); } - (bool) romanScript @@ -209,7 +209,7 @@ return false; } } - + return true; } @@ -313,11 +313,11 @@ struct AttachmentInfo // We "happen to know" that info[0].basename is "SecondLife.old" -- due to // the fact that BugsplatMac only notices a crash during the viewer run - // following the crash. + // following the crash. // The Bugsplat service doesn't respect the MIME type above when returning // the log data to a browser, so take this opportunity to rename the file // from .old to _log.txt - info[0].basename = + info[0].basename = boost::filesystem::path(info[0].pathname).stem().string() + "_log.txt"; infos("attachmentsForBugsplatStartupManager attaching log " + info[0].basename); @@ -373,7 +373,7 @@ struct AttachmentInfo { [super sendEvent:event]; if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand)) - { + { [[self keyWindow] sendEvent:event]; } } diff --git a/indra/newview/llappviewermacosx-objc.mm b/indra/newview/llappviewermacosx-objc.mm index 2ea3f2f171..96a6bc6edc 100644 --- a/indra/newview/llappviewermacosx-objc.mm +++ b/indra/newview/llappviewermacosx-objc.mm @@ -5,27 +5,27 @@ * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ - */ + */ #if !defined LL_DARWIN - #error "Use only with macOS" + #error "Use only with macOS" #endif #import diff --git a/indra/newview/llfilepicker_mac.mm b/indra/newview/llfilepicker_mac.mm index 978069457c..6cb7c4ad51 100644 --- a/indra/newview/llfilepicker_mac.mm +++ b/indra/newview/llfilepicker_mac.mm @@ -1,25 +1,25 @@ -/** +/** * @file llfilepicker_mac.cpp * @brief OS-specific file picker * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -32,15 +32,15 @@ NSOpenPanel *init_panel(const std::vector* allowed_types, unsigned int flags) { int i; - + NSOpenPanel *panel = [NSOpenPanel openPanel]; NSMutableArray *fileTypes = nil; - - + + if ( allowed_types && !allowed_types->empty()) { fileTypes = [[NSMutableArray alloc] init]; - + for (i=0;isize();++i) { [fileTypes addObject: @@ -48,7 +48,7 @@ NSOpenPanel *init_panel(const std::vector* allowed_types, unsigned encoding:[NSString defaultCStringEncoding]]]; } } - + //[panel setMessage:@"Import one or more files or directories."]; [panel setAllowsMultipleSelection: ( (flags & F_MULTIPLE)?true:false ) ]; [panel setCanChooseDirectories: ( (flags & F_DIRECTORY)?true:false ) ]; @@ -56,7 +56,7 @@ NSOpenPanel *init_panel(const std::vector* allowed_types, unsigned [panel setResolvesAliases: true]; [panel setCanChooseFiles: ( (flags & F_FILE)?true:false )]; [panel setTreatsFilePackagesAsDirectories: ( flags & F_NAV_SUPPORT ) ]; - + if (fileTypes) { [panel setAllowedFileTypes:fileTypes]; @@ -77,7 +77,7 @@ std::unique_ptr> doLoadDialog(const std::vector> outfiles; @autoreleasepool - { + { int result; //Aura TODO: We could init a small window and release it at the end of this routine //for a modeless interface. @@ -85,17 +85,17 @@ std::unique_ptr> doLoadDialog(const std::vector 0) { outfiles.reset(new std::vector); } - + for (i=0; i* allowed_types, { @autoreleasepool - { + { // Note: might need to return and save this panel // so that it does not close immediately NSOpenPanel *panel = init_panel(allowed_types,flags); - + [panel beginWithCompletionHandler:^(NSModalResponse result) { std::vector outfiles; @@ -125,10 +125,10 @@ void doLoadDialogModeless(const std::vector* allowed_types, { NSArray *filesToOpen = [panel URLs]; int i, count = [filesToOpen count]; - + if (count > 0) { - + for (i=0; i* allowed_types, } } -std::unique_ptr doSaveDialog(const std::string* file, +std::unique_ptr doSaveDialog(const std::string* file, const std::string* type, const std::string* creator, const std::string* extension, @@ -157,18 +157,18 @@ std::unique_ptr doSaveDialog(const std::string* file, { std::unique_ptr outfile; @autoreleasepool - { + { NSSavePanel *panel = [NSSavePanel savePanel]; - + NSString *extensionns = [NSString stringWithCString:extension->c_str() encoding:[NSString defaultCStringEncoding]]; NSArray *fileType = [extensionns componentsSeparatedByString:@","]; - + //[panel setMessage:@"Save Image File"]; [panel setTreatsFilePackagesAsDirectories: ( flags & F_NAV_SUPPORT ) ]; [panel setCanSelectHiddenExtension:true]; [panel setAllowedFileTypes:fileType]; NSString *fileName = [NSString stringWithCString:file->c_str() encoding:[NSString defaultCStringEncoding]]; - + NSURL* url = [NSURL fileURLWithPath:fileName]; [panel setNameFieldStringValue: fileName]; [panel setDirectoryURL: url]; @@ -193,39 +193,39 @@ void doSaveDialogModeless(const std::string* file, void *userdata) { @autoreleasepool { - NSSavePanel *panel = [NSSavePanel savePanel]; - - NSString *extensionns = [NSString stringWithCString:extension->c_str() encoding:[NSString defaultCStringEncoding]]; - NSArray *fileType = [extensionns componentsSeparatedByString:@","]; - - //[panel setMessage:@"Save Image File"]; - [panel setTreatsFilePackagesAsDirectories: ( flags & F_NAV_SUPPORT ) ]; - [panel setCanSelectHiddenExtension:true]; - [panel setAllowedFileTypes:fileType]; - NSString *fileName = [NSString stringWithCString:file->c_str() encoding:[NSString defaultCStringEncoding]]; - - NSURL* url = [NSURL fileURLWithPath:fileName]; - [panel setNameFieldStringValue: fileName]; - [panel setDirectoryURL: url]; - - - [panel beginWithCompletionHandler:^(NSModalResponse result) - { + NSSavePanel *panel = [NSSavePanel savePanel]; + + NSString *extensionns = [NSString stringWithCString:extension->c_str() encoding:[NSString defaultCStringEncoding]]; + NSArray *fileType = [extensionns componentsSeparatedByString:@","]; + + //[panel setMessage:@"Save Image File"]; + [panel setTreatsFilePackagesAsDirectories: ( flags & F_NAV_SUPPORT ) ]; + [panel setCanSelectHiddenExtension:true]; + [panel setAllowedFileTypes:fileType]; + NSString *fileName = [NSString stringWithCString:file->c_str() encoding:[NSString defaultCStringEncoding]]; + + NSURL* url = [NSURL fileURLWithPath:fileName]; + [panel setNameFieldStringValue: fileName]; + [panel setDirectoryURL: url]; + + + [panel beginWithCompletionHandler:^(NSModalResponse result) + { if (result == NSModalResponseOK) - { - NSURL* url = [panel URL]; - NSString* p = [url path]; - std::string outfile([p UTF8String]); - - callback(true, outfile, userdata); - } - else // cancel - { - std::string outfile; - callback(false, outfile, userdata); - } - }]; - } + { + NSURL* url = [panel URL]; + NSString* p = [url path]; + std::string outfile([p UTF8String]); + + callback(true, outfile, userdata); + } + else // cancel + { + std::string outfile; + callback(false, outfile, userdata); + } + }]; + } } #endif diff --git a/scripts/code_tools/fix_whitespace.py b/scripts/code_tools/fix_whitespace.py index 7a88265479..d5f7ce1cce 100644 --- a/scripts/code_tools/fix_whitespace.py +++ b/scripts/code_tools/fix_whitespace.py @@ -70,7 +70,7 @@ def process_directory(directory, extensions, tab_stop): def main(): parser = argparse.ArgumentParser(description='Convert tabs to spaces in files, considering tab stops.') - parser.add_argument('-e', '--extensions', type=str, default='c,cpp,h,hpp,inl,py,glsl,cmake', help='Comma-separated list of file extensions to process (default: "c,cpp,h,hpp,inl,py,glsl,cmake")') + parser.add_argument('-e', '--extensions', type=str, default='c,cpp,m,mm,h,hpp,inl,py,glsl,cmake', help='Comma-separated list of file extensions to process (default: "c,cpp,h,hpp,inl,py,glsl,cmake")') parser.add_argument('-t', '--tabstop', type=int, default=4, help='Tab stop size (default: 4)') parser.add_argument('-d', '--directory', type=str, required=True, help='Directory to process') -- cgit v1.2.3 From 993851cc2a1441a99b3c1387ed5b4a9114c983f3 Mon Sep 17 00:00:00 2001 From: Rye Date: Sun, 19 Oct 2025 15:02:14 -0400 Subject: Ignore tabs to spaces for objc in blame --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index c68131402b..ce934f1a21 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,6 +1,7 @@ # Replace tabs with spaces 1b68f71348ecf3983b76b40d7940da8377f049b7 33418a77b716e122da9778869cdbabe97c83ff37 +6b974724826a038b0db794460b322eb4921da735 # Trim trailing whitespace a0b3021bdcf76859054fda8e30abb3ed47749e83 8444cd9562a6a7b755fcb075864e205122354192 -- cgit v1.2.3 From c847a5eaac635604a2490872c0ee8cc947273cf5 Mon Sep 17 00:00:00 2001 From: Rye Date: Sun, 19 Oct 2025 16:37:01 -0400 Subject: Update pre-commit hooks to v6.0.0 --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7e754714c6..d1dcc93976 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,7 +12,7 @@ repos: - id: indent-with-spaces files: \.(cpp|c|m|mm|h|inl|py|glsl|cmake)$ - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: check-xml - id: mixed-line-ending -- cgit v1.2.3 From 41e9595522c71534219158609e97186e4bfeac69 Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 20 Oct 2025 13:10:19 -0400 Subject: Rework new convex decomp into a physicsextensions stub package and fix havok and havok_tpv builds for darwin universal to utilize new vhacd stub (#4858) --- autobuild.xml | 30 -- indra/CMakeLists.txt | 4 +- indra/cmake/LLPhysicsExtensions.cmake | 24 +- indra/llconvexdecomposition/CMakeLists.txt | 39 -- .../llconvexdecomposition.cpp | 83 ---- .../llconvexdecomposition/llconvexdecomposition.h | 231 ---------- .../llconvexdecompositionvhacd.cpp | 492 --------------------- .../llconvexdecompositionvhacd.h | 339 -------------- indra/llphysicsextensionsos/CMakeLists.txt | 47 ++ .../llphysicsextensionsos/LLPathingLibStubImpl.cpp | 109 +++++ indra/llphysicsextensionsos/LLPathingLibStubImpl.h | 78 ++++ .../LLPhysicsExtensionsStubImpl.cpp | 51 +++ .../LLPhysicsExtensionsStubImpl.h | 46 ++ .../llconvexdecomposition.cpp | 83 ++++ .../llphysicsextensionsos/llconvexdecomposition.h | 231 ++++++++++ .../llconvexdecompositionvhacd.cpp | 492 +++++++++++++++++++++ .../llconvexdecompositionvhacd.h | 339 ++++++++++++++ indra/llphysicsextensionsos/llpathinglib.cpp | 83 ++++ indra/llphysicsextensionsos/llpathinglib.h | 187 ++++++++ .../llphysicsextensionsos/llphysicsextensions.cpp | 78 ++++ indra/llphysicsextensionsos/llphysicsextensions.h | 59 +++ indra/llprimitive/CMakeLists.txt | 11 +- indra/newview/CMakeLists.txt | 33 +- 23 files changed, 1927 insertions(+), 1242 deletions(-) delete mode 100644 indra/llconvexdecomposition/CMakeLists.txt delete mode 100644 indra/llconvexdecomposition/llconvexdecomposition.cpp delete mode 100644 indra/llconvexdecomposition/llconvexdecomposition.h delete mode 100644 indra/llconvexdecomposition/llconvexdecompositionvhacd.cpp delete mode 100644 indra/llconvexdecomposition/llconvexdecompositionvhacd.h create mode 100644 indra/llphysicsextensionsos/CMakeLists.txt create mode 100644 indra/llphysicsextensionsos/LLPathingLibStubImpl.cpp create mode 100644 indra/llphysicsextensionsos/LLPathingLibStubImpl.h create mode 100644 indra/llphysicsextensionsos/LLPhysicsExtensionsStubImpl.cpp create mode 100644 indra/llphysicsextensionsos/LLPhysicsExtensionsStubImpl.h create mode 100644 indra/llphysicsextensionsos/llconvexdecomposition.cpp create mode 100644 indra/llphysicsextensionsos/llconvexdecomposition.h create mode 100644 indra/llphysicsextensionsos/llconvexdecompositionvhacd.cpp create mode 100644 indra/llphysicsextensionsos/llconvexdecompositionvhacd.h create mode 100644 indra/llphysicsextensionsos/llpathinglib.cpp create mode 100644 indra/llphysicsextensionsos/llpathinglib.h create mode 100644 indra/llphysicsextensionsos/llphysicsextensions.cpp create mode 100644 indra/llphysicsextensionsos/llphysicsextensions.h diff --git a/autobuild.xml b/autobuild.xml index 8492dff304..2983307cbe 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -1460,36 +1460,6 @@ name llphysicsextensions_source - llphysicsextensions_stub - - platforms - - common - - archive - - hash - bc41438b10ac6474cf5560465a3662a64f9e65a81342e4c33f18f6694581c7ee28c9ee6f091c36e80a0b1e10c68205be71eb5f8e40fef115d2c744fc2bbfcb43 - hash_algorithm - blake2b - url - https://github.com/AlchemyViewer/llphysicsextensions_stub/releases/download/v1.0-cb4900e/llphysicsextensions_stub-1.0-common-17836965684.tar.zst - - name - common - - - license - internal - license_file - LICENSES/llphysicsextensions.txt - copyright - Copyright (c) 2010, Linden Research, Inc. - version - 1.0 - name - llphysicsextensions_stub - llphysicsextensions_tpv platforms diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 246bd7bfa0..8fde58fa43 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -61,9 +61,6 @@ add_subdirectory(cmake) add_subdirectory(${LIBS_OPEN_PREFIX}llaudio) add_subdirectory(${LIBS_OPEN_PREFIX}llappearance) add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter) -if (NOT HAVOK AND NOT HAVOK_TPV) - add_subdirectory(${LIBS_OPEN_PREFIX}llconvexdecomposition) -endif () add_subdirectory(${LIBS_OPEN_PREFIX}llcommon) add_subdirectory(${LIBS_OPEN_PREFIX}llcorehttp) add_subdirectory(${LIBS_OPEN_PREFIX}llimage) @@ -73,6 +70,7 @@ add_subdirectory(${LIBS_OPEN_PREFIX}llinventory) add_subdirectory(${LIBS_OPEN_PREFIX}llmath) add_subdirectory(${LIBS_OPEN_PREFIX}llmeshoptimizer) add_subdirectory(${LIBS_OPEN_PREFIX}llmessage) +add_subdirectory(${LIBS_OPEN_PREFIX}llphysicsextensionsos) add_subdirectory(${LIBS_OPEN_PREFIX}llprimitive) add_subdirectory(${LIBS_OPEN_PREFIX}llrender) add_subdirectory(${LIBS_OPEN_PREFIX}llfilesystem) diff --git a/indra/cmake/LLPhysicsExtensions.cmake b/indra/cmake/LLPhysicsExtensions.cmake index 25563546eb..84722b45a7 100644 --- a/indra/cmake/LLPhysicsExtensions.cmake +++ b/indra/cmake/LLPhysicsExtensions.cmake @@ -22,7 +22,6 @@ if (HAVOK) include(Havok) use_prebuilt_binary(llphysicsextensions_source) set(LLPHYSICSEXTENSIONS_SRC_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/src) - target_compile_definitions( llphysicsextensions_impl INTERFACE LL_HAVOK=1 ) if(DARWIN) set(LLPHYSICSEXTENSIONS_STUB_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/stub) # can't set these library dependencies per-arch here, need to do it using XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=*] in newview/CMakeLists.txt @@ -30,19 +29,18 @@ if (HAVOK) #target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensionsstub) else() target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions) + target_compile_definitions( llphysicsextensions_impl INTERFACE LL_HAVOK=1 ) endif() + target_include_directories( llphysicsextensions_impl INTERFACE ${LIBS_PREBUILT_DIR}/include/llphysicsextensions) elseif (HAVOK_TPV) use_prebuilt_binary(llphysicsextensions_tpv) - if(WINDOWS) - target_link_libraries( llphysicsextensions_impl INTERFACE ${ARCH_PREBUILT_DIRS}/llphysicsextensions_tpv.lib) - else() - target_link_libraries( llphysicsextensions_impl INTERFACE ${ARCH_PREBUILT_DIRS}/libllphysicsextensions_tpv.a) + if (NOT DARWIN) + if(WINDOWS) + target_link_libraries( llphysicsextensions_impl INTERFACE ${ARCH_PREBUILT_DIRS}/llphysicsextensions_tpv.lib) + elseif(LINUX) + target_link_libraries( llphysicsextensions_impl INTERFACE ${ARCH_PREBUILT_DIRS}/libllphysicsextensions_tpv.a) + endif() + target_compile_definitions( llphysicsextensions_impl INTERFACE LL_HAVOK=1 ) endif() - target_compile_definitions( llphysicsextensions_impl INTERFACE LL_HAVOK=1 ) -else (HAVOK) - use_prebuilt_binary(llphysicsextensions_stub) - set(LLPHYSICSEXTENSIONS_SRC_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/stub) - target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensionsstub) -endif (HAVOK) - -target_include_directories( llphysicsextensions_impl INTERFACE ${LIBS_PREBUILT_DIR}/include/llphysicsextensions) + target_include_directories( llphysicsextensions_impl INTERFACE ${LIBS_PREBUILT_DIR}/include/llphysicsextensions) +endif () diff --git a/indra/llconvexdecomposition/CMakeLists.txt b/indra/llconvexdecomposition/CMakeLists.txt deleted file mode 100644 index 7dae884db8..0000000000 --- a/indra/llconvexdecomposition/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -# -*- cmake -*- - -project(llconvexdecomposition) - -include(00-Common) -include(LLCommon) -include(LLMath) -include(VHACD) - -set(llconvexdecomposition_SOURCE_FILES - llconvexdecomposition.cpp - llconvexdecompositionvhacd.cpp - ) - -set(llconvexdecomposition_HEADER_FILES - CMakeLists.txt - llconvexdecomposition.h - llconvexdecompositionvhacd.h - ) - -set_source_files_properties(${llconvexdecomposition_HEADER_FILES} - PROPERTIES HEADER_FILE_ONLY TRUE) - -list(APPEND llconvexdecomposition_SOURCE_FILES ${llconvexdecomposition_HEADER_FILES}) - -add_library (llconvexdecomposition ${llconvexdecomposition_SOURCE_FILES}) -target_include_directories(llconvexdecomposition INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) - -target_link_libraries(llconvexdecomposition - llcommon - llmath - ll::vhacd) - -if(WINDOWS) - target_compile_options(llconvexdecomposition PRIVATE /bigobj) -endif() - -# Add tests - diff --git a/indra/llconvexdecomposition/llconvexdecomposition.cpp b/indra/llconvexdecomposition/llconvexdecomposition.cpp deleted file mode 100644 index 7b9d775c53..0000000000 --- a/indra/llconvexdecomposition/llconvexdecomposition.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/** -* @file llconvexdecomposition.cpp -* @author falcon@lindenlab.com -* @brief Inner implementation of LLConvexDecomposition interface -* -* $LicenseInfo:firstyear=2011&license=lgpl$ -* Second Life Viewer Source Code -* Copyright (C) 2011, Linden Research, Inc. -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License as published by the Free Software Foundation; -* version 2.1 of the License only. -* -* This library is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this library; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -* -* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -* $/LicenseInfo$ -*/ - -#include "linden_common.h" - -#include "llconvexdecompositionvhacd.h" -#include "llconvexdecomposition.h" - -bool LLConvexDecomposition::s_isInitialized = false; - -// static -bool LLConvexDecomposition::isFunctional() -{ - return LLConvexDecompositionVHACD::isFunctional(); -} - -// static -LLConvexDecomposition* LLConvexDecomposition::getInstance() -{ - if ( !s_isInitialized ) - { - return nullptr; - } - else - { - return LLConvexDecompositionVHACD::getInstance(); - } -} - -// static -LLCDResult LLConvexDecomposition::initSystem() -{ - LLCDResult result = LLConvexDecompositionVHACD::initSystem(); - if ( result == LLCD_OK ) - { - s_isInitialized = true; - } - return result; -} - -// static -LLCDResult LLConvexDecomposition::initThread() -{ - return LLConvexDecompositionVHACD::initThread(); -} - -// static -LLCDResult LLConvexDecomposition::quitThread() -{ - return LLConvexDecompositionVHACD::quitThread(); -} - -// static -LLCDResult LLConvexDecomposition::quitSystem() -{ - return LLConvexDecompositionVHACD::quitSystem(); -} - - diff --git a/indra/llconvexdecomposition/llconvexdecomposition.h b/indra/llconvexdecomposition/llconvexdecomposition.h deleted file mode 100644 index 8008bc6e12..0000000000 --- a/indra/llconvexdecomposition/llconvexdecomposition.h +++ /dev/null @@ -1,231 +0,0 @@ -/** - * @file llconvexdecomposition.cpp - * @brief LLConvexDecomposition interface definition - * - * $LicenseInfo:firstyear=2011&license=lgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2011, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_CONVEX_DECOMPOSITION -#define LL_CONVEX_DECOMPOSITION - -typedef int bool32; - -#if defined(_WIN32) || defined(_WIN64) -#define LLCD_CALL __cdecl -#else -#define LLCD_CALL -#endif - -struct LLCDParam -{ - enum LLCDParamType - { - LLCD_INVALID = 0, - LLCD_INTEGER, - LLCD_FLOAT, - LLCD_BOOLEAN, - LLCD_ENUM - }; - - struct LLCDEnumItem - { - const char* mName; - int mValue; - }; - - union LLCDValue - { - float mFloat; - int mIntOrEnumValue; - bool32 mBool; - }; - - union LLCDParamDetails - { - struct { - LLCDValue mLow; - LLCDValue mHigh; - LLCDValue mDelta; - } mRange; - - struct { - int mNumEnums; - LLCDEnumItem* mEnumsArray; - } mEnumValues; - }; - - const char* mName; - const char* mDescription; - LLCDParamType mType; - LLCDParamDetails mDetails; - LLCDValue mDefault; - int mStage; - - // WARNING: Only the LLConvexDecomposition implementation - // should change this value - int mReserved; -}; - -struct LLCDStageData -{ - const char* mName; - const char* mDescription; - bool32 mSupportsCallback; -}; - -struct LLCDMeshData -{ - enum IndexType - { - INT_16, - INT_32 - }; - - const float* mVertexBase; - int mVertexStrideBytes; - int mNumVertices; - const void* mIndexBase; - IndexType mIndexType; - int mIndexStrideBytes; - int mNumTriangles; -}; - -struct LLCDHull -{ - const float* mVertexBase; - int mVertexStrideBytes; - int mNumVertices; -}; - -enum LLCDResult -{ - LLCD_OK = 0, - LLCD_UNKOWN_ERROR, - LLCD_NULL_PTR, - LLCD_INVALID_STAGE, - LLCD_UNKNOWN_PARAM, - LLCD_BAD_VALUE, - LLCD_REQUEST_OUT_OF_RANGE, - LLCD_INVALID_MESH_DATA, - LLCD_INVALID_HULL_DATA, - LLCD_STAGE_NOT_READY, - LLCD_INVALID_THREAD, - LLCD_NOT_IMPLEMENTED -}; - -// This callback will receive a string describing the current subtask being performed -// as well as a pair of numbers indicating progress. (The values should not be interpreted -// as a completion percentage as 'current' may be greater than 'final'.) -// If the callback returns zero, the decomposition will be terminated -typedef int (LLCD_CALL *llcdCallbackFunc)(const char* description, int current_progress, int final_progress); - -class LLConvexDecomposition -{ -public: - // Obtain a pointer to the actual implementation - static LLConvexDecomposition* getInstance(); - - /// @returns false if this is the stub - static bool isFunctional(); - - static LLCDResult initSystem(); - static LLCDResult initThread(); - static LLCDResult quitThread(); - static LLCDResult quitSystem(); - - // Generate a decomposition object handle - virtual void genDecomposition(int& decomp) = 0; - // Delete decomposition object handle - virtual void deleteDecomposition(int decomp) = 0; - // Bind given decomposition handle - // Commands operate on currently bound decomposition - virtual void bindDecomposition(int decomp) = 0; - - // Sets *paramsOut to the address of the LLCDParam array and returns - // the number of parameters - virtual int getParameters(const LLCDParam** paramsOut) = 0; - - - // Sets *stagesOut to the address of the LLCDStageData array and returns - // the number of stages - virtual int getStages(const LLCDStageData** stagesOut) = 0; - - - // Set a parameter by name. Pass enum values as integers. - virtual LLCDResult setParam(const char* name, float val) = 0; - virtual LLCDResult setParam(const char* name, int val) = 0; - virtual LLCDResult setParam(const char* name, bool val) = 0; - - - // Set incoming mesh data. Data is copied to local buffers and will - // persist until the next setMeshData call - virtual LLCDResult setMeshData( const LLCDMeshData* data, bool vertex_based ) = 0; - - - // Register a callback to be called periodically during the specified stage - // See the typedef above for more information - virtual LLCDResult registerCallback( int stage, llcdCallbackFunc callback ) = 0; - - - // Execute the specified decomposition stage - virtual LLCDResult executeStage(int stage) = 0; - virtual LLCDResult buildSingleHull() = 0 ; - - - // Gets the number of hulls generated by the specified decompositions stage - virtual int getNumHullsFromStage(int stage) = 0; - - - // Populates hullOut to reference the internal copy of the requested hull - // The data will persist only until the next executeStage call for that stage. - virtual LLCDResult getHullFromStage( int stage, int hull, LLCDHull* hullOut ) = 0; - - virtual LLCDResult getSingleHull( LLCDHull* hullOut ) = 0 ; - - - // TODO: Implement lock of some kind to disallow this call if data not yet ready - // Populates the meshDataOut to reference the utility's copy of the mesh geometry - // for the hull and stage specified. - // You must copy this data if you want to continue using it after the next executeStage - // call - virtual LLCDResult getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut) = 0; - - - // Creates a mesh from hullIn and temporarily stores it internally in the utility. - // The mesh data persists only until the next call to getMeshFromHull - virtual LLCDResult getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut ) = 0; - - // Takes meshIn, generates a single convex hull from it, converts that to a mesh - // stored internally, and populates meshOut to reference the internally stored data. - // The data is persistent only until the next call to generateSingleHullMeshFromMesh - virtual LLCDResult generateSingleHullMeshFromMesh( LLCDMeshData* meshIn, LLCDMeshData* meshOut) = 0; - - // - /// Debug - virtual void loadMeshData(const char* fileIn, LLCDMeshData** meshDataOut) = 0; - -private: - static bool s_isInitialized; -}; - -#endif //LL_CONVEX_DECOMPOSITION - diff --git a/indra/llconvexdecomposition/llconvexdecompositionvhacd.cpp b/indra/llconvexdecomposition/llconvexdecompositionvhacd.cpp deleted file mode 100644 index 78876f9f36..0000000000 --- a/indra/llconvexdecomposition/llconvexdecompositionvhacd.cpp +++ /dev/null @@ -1,492 +0,0 @@ -/** -* @file llconvexdecompositionvhacd.cpp -* @author rye@alchemyviewer.org -* @brief A VHACD based implementation of LLConvexDecomposition -* -* $LicenseInfo:firstyear=2025&license=viewerlgpl$ -* Second Life Viewer Source Code -* Copyright (C) 2025, Linden Research, Inc. -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License as published by the Free Software Foundation; -* version 2.1 of the License only. -* -* This library is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this library; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -* -* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -* $/LicenseInfo$ -*/ - -#include "linden_common.h" - -#include "llmath.h" -#include "v3math.h" - -#include -#include - -#define ENABLE_VHACD_IMPLEMENTATION 1 -#include "VHACD.h" - -#include "llconvexdecompositionvhacd.h" - -constexpr S32 MAX_HULLS = 256; -constexpr S32 MAX_VERTICES_PER_HULL = 256; - -bool LLConvexDecompositionVHACD::isFunctional() -{ - return true; -} - -LLConvexDecomposition* LLConvexDecompositionVHACD::getInstance() -{ - return LLSimpleton::getInstance(); -} - -LLCDResult LLConvexDecompositionVHACD::initSystem() -{ - createInstance(); - return LLCD_OK; -} - -LLCDResult LLConvexDecompositionVHACD::initThread() -{ - return LLCD_OK; -} - -LLCDResult LLConvexDecompositionVHACD::quitThread() -{ - return LLCD_OK; -} - -LLCDResult LLConvexDecompositionVHACD::quitSystem() -{ - deleteSingleton(); - return LLCD_OK; -} - -LLConvexDecompositionVHACD::LLConvexDecompositionVHACD() -{ - //Create our vhacd instance and setup default parameters - mVHACD = VHACD::CreateVHACD(); - - mVHACDParameters.m_callback = &mVHACDCallback; - mVHACDParameters.m_logger = &mVHACDLogger; - - mDecompStages[0].mName = "Analyze"; - mDecompStages[0].mDescription = nullptr; - - LLCDParam param; - param.mName = "Fill Mode"; - param.mDescription = nullptr; - param.mType = LLCDParam::LLCD_ENUM; - param.mDetails.mEnumValues.mNumEnums = 3; - - static LLCDParam::LLCDEnumItem fill_enums[3]; - fill_enums[(size_t)VHACD::FillMode::FLOOD_FILL].mName = "Flood"; - fill_enums[(size_t)VHACD::FillMode::FLOOD_FILL].mValue = (int)VHACD::FillMode::FLOOD_FILL; - fill_enums[(size_t)VHACD::FillMode::SURFACE_ONLY].mName = "Surface Only"; - fill_enums[(size_t)VHACD::FillMode::SURFACE_ONLY].mValue = (int)VHACD::FillMode::SURFACE_ONLY; - fill_enums[(size_t)VHACD::FillMode::RAYCAST_FILL].mName = "Raycast"; - fill_enums[(size_t)VHACD::FillMode::RAYCAST_FILL].mValue = (int)VHACD::FillMode::RAYCAST_FILL; - - param.mDetails.mEnumValues.mEnumsArray = fill_enums; - param.mDefault.mIntOrEnumValue = (int)VHACD::FillMode::FLOOD_FILL; - param.mStage = 0; - param.mReserved = -1; - mDecompParams.push_back(param); - - enum EVoxelQualityLevels - { - E_LOW_QUALITY = 0, - E_NORMAL_QUALITY, - E_HIGH_QUALITY, - E_VERY_HIGH_QUALITY, - E_ULTRA_QUALITY, - E_MAX_QUALITY, - E_NUM_QUALITY_LEVELS - }; - - param.mName = "Voxel Resolution"; - param.mDescription = nullptr; - param.mType = LLCDParam::LLCD_ENUM; - param.mDetails.mEnumValues.mNumEnums = E_NUM_QUALITY_LEVELS; - - static LLCDParam::LLCDEnumItem voxel_quality_enums[E_NUM_QUALITY_LEVELS]; - voxel_quality_enums[E_LOW_QUALITY].mName = "Low"; - voxel_quality_enums[E_LOW_QUALITY].mValue = 200000; - voxel_quality_enums[E_NORMAL_QUALITY].mName = "Normal"; - voxel_quality_enums[E_NORMAL_QUALITY].mValue = 400000; - voxel_quality_enums[E_HIGH_QUALITY].mName = "High"; - voxel_quality_enums[E_HIGH_QUALITY].mValue = 800000; - voxel_quality_enums[E_VERY_HIGH_QUALITY].mName = "Very High"; - voxel_quality_enums[E_VERY_HIGH_QUALITY].mValue = 1200000; - voxel_quality_enums[E_ULTRA_QUALITY].mName = "Ultra"; - voxel_quality_enums[E_ULTRA_QUALITY].mValue = 1600000; - voxel_quality_enums[E_MAX_QUALITY].mName = "Maximum"; - voxel_quality_enums[E_MAX_QUALITY].mValue = 2000000; - - param.mDetails.mEnumValues.mEnumsArray = voxel_quality_enums; - param.mDefault.mIntOrEnumValue = 400000; - param.mStage = 0; - param.mReserved = -1; - mDecompParams.push_back(param); - - param.mName = "Num Hulls"; - param.mDescription = nullptr; - param.mType = LLCDParam::LLCD_FLOAT; - param.mDetails.mRange.mLow.mFloat = 1.f; - param.mDetails.mRange.mHigh.mFloat = MAX_HULLS; - param.mDetails.mRange.mDelta.mFloat = 1.f; - param.mDefault.mFloat = 8.f; - param.mStage = 0; - param.mReserved = -1; - mDecompParams.push_back(param); - - param.mName = "Num Vertices"; - param.mDescription = nullptr; - param.mType = LLCDParam::LLCD_FLOAT; - param.mDetails.mRange.mLow.mFloat = 3.f; - param.mDetails.mRange.mHigh.mFloat = MAX_VERTICES_PER_HULL; - param.mDetails.mRange.mDelta.mFloat = 1.f; - param.mDefault.mFloat = 32.f; - param.mStage = 0; - param.mReserved = -1; - mDecompParams.push_back(param); - - param.mName = "Error Tolerance"; - param.mDescription = nullptr; - param.mType = LLCDParam::LLCD_FLOAT; - param.mDetails.mRange.mLow.mFloat = 0.0001f; - param.mDetails.mRange.mHigh.mFloat = 99.f; - param.mDetails.mRange.mDelta.mFloat = 0.001f; - param.mDefault.mFloat = 1.f; - param.mStage = 0; - param.mReserved = -1; - mDecompParams.push_back(param); - - for (const LLCDParam& param : mDecompParams) - { - const char* const name = param.mName; - - switch (param.mType) - { - case LLCDParam::LLCD_FLOAT: - { - setParam(name, param.mDefault.mFloat); - break; - } - case LLCDParam::LLCD_ENUM: - case LLCDParam::LLCD_INTEGER: - { - setParam(name, param.mDefault.mIntOrEnumValue); - break; - } - case LLCDParam::LLCD_BOOLEAN: - { - setParam(name, (param.mDefault.mBool != 0)); - break; - } - case LLCDParam::LLCD_INVALID: - default: - { - break; - } - } - } -} - -LLConvexDecompositionVHACD::~LLConvexDecompositionVHACD() -{ - mBoundDecomp = nullptr; - mDecompData.clear(); - - mVHACD->Release(); -} - -void LLConvexDecompositionVHACD::genDecomposition(int& decomp) -{ - int new_decomp_id = static_cast(mDecompData.size()) + 1; - mDecompData[new_decomp_id] = LLDecompData(); - decomp = new_decomp_id; -} - -void LLConvexDecompositionVHACD::deleteDecomposition(int decomp) -{ - auto iter = mDecompData.find(decomp); - if (iter != mDecompData.end()) - { - if (mBoundDecomp == &iter->second) - { - mBoundDecomp = nullptr; - } - mDecompData.erase(iter); - } -} - -void LLConvexDecompositionVHACD::bindDecomposition(int decomp) -{ - auto iter = mDecompData.find(decomp); - if (iter != mDecompData.end()) - { - mBoundDecomp = &iter->second; - } - else - { - LL_WARNS() << "Failed to bind unknown decomposition: " << decomp << LL_ENDL; - mBoundDecomp = nullptr; - } -} - -LLCDResult LLConvexDecompositionVHACD::setParam(const char* name, float val) -{ - if (name == std::string("Num Hulls")) - { - mVHACDParameters.m_maxConvexHulls = llclamp(ll_round(val), 1, MAX_HULLS); - } - else if (name == std::string("Num Vertices")) - { - mVHACDParameters.m_maxNumVerticesPerCH = llclamp(ll_round(val), 3, MAX_VERTICES_PER_HULL); - } - else if (name == std::string("Error Tolerance")) - { - mVHACDParameters.m_minimumVolumePercentErrorAllowed = val; - } - return LLCD_OK; -} - -LLCDResult LLConvexDecompositionVHACD::setParam(const char* name, bool val) -{ - return LLCD_OK; -} - -LLCDResult LLConvexDecompositionVHACD::setParam(const char* name, int val) -{ - if (name == std::string("Fill Mode")) - { - mVHACDParameters.m_fillMode = (VHACD::FillMode)val; - } - else if (name == std::string("Voxel Resolution")) - { - mVHACDParameters.m_resolution = val; - } - return LLCD_OK; -} - -LLCDResult LLConvexDecompositionVHACD::setMeshData( const LLCDMeshData* data, bool vertex_based ) -{ - if (!mBoundDecomp) - { - return LLCD_NULL_PTR; - } - - return mBoundDecomp->mSourceMesh.from(data, vertex_based); -} - -LLCDResult LLConvexDecompositionVHACD::registerCallback(int stage, llcdCallbackFunc callback ) -{ - if (stage == 0) - { - mVHACDCallback.setCallbackFunc(callback); - return LLCD_OK; - } - else - { - return LLCD_INVALID_STAGE; - } -} - -LLCDResult LLConvexDecompositionVHACD::executeStage(int stage) -{ - if (!mBoundDecomp) - { - return LLCD_NULL_PTR; - } - - if (stage != 0) - { - return LLCD_INVALID_STAGE; - } - - mBoundDecomp->mDecomposedHulls.clear(); - - const auto& decomp_mesh = mBoundDecomp->mSourceMesh; - if (!mVHACD->Compute((const double* const)decomp_mesh.mVertices.data(), static_cast(decomp_mesh.mVertices.size()), (const uint32_t* const)decomp_mesh.mIndices.data(), static_cast(decomp_mesh.mIndices.size()), mVHACDParameters)) - { - return LLCD_INVALID_HULL_DATA; - } - - uint32_t num_nulls = mVHACD->GetNConvexHulls(); - if (num_nulls == 0) - { - return LLCD_INVALID_HULL_DATA; - } - - for (uint32_t i = 0; num_nulls > i; ++i) - { - VHACD::IVHACD::ConvexHull ch; - if (!mVHACD->GetConvexHull(i, ch)) - continue; - - LLConvexMesh out_mesh; - out_mesh.setVertices(ch.m_points); - out_mesh.setIndices(ch.m_triangles); - - mBoundDecomp->mDecomposedHulls.push_back(std::move(out_mesh)); - } - - mVHACD->Clean(); - - return LLCD_OK; -} - -LLCDResult LLConvexDecompositionVHACD::buildSingleHull() -{ - LL_INFOS() << "Building single hull mesh" << LL_ENDL; - if (!mBoundDecomp || mBoundDecomp->mSourceMesh.mVertices.empty()) - { - return LLCD_NULL_PTR; - } - - mBoundDecomp->mSingleHullMesh.clear(); - - VHACD::QuickHull quickhull; - uint32_t num_tris = quickhull.ComputeConvexHull(mBoundDecomp->mSourceMesh.mVertices, MAX_VERTICES_PER_HULL); - if (num_tris > 0) - { - mBoundDecomp->mSingleHullMesh.setVertices(quickhull.GetVertices()); - mBoundDecomp->mSingleHullMesh.setIndices(quickhull.GetIndices()); - - return LLCD_OK; - } - - return LLCD_INVALID_MESH_DATA; -} - -int LLConvexDecompositionVHACD::getNumHullsFromStage(int stage) -{ - if (!mBoundDecomp || stage != 0) - { - return 0; - } - - return narrow(mBoundDecomp->mDecomposedHulls.size()); -} - -LLCDResult LLConvexDecompositionVHACD::getSingleHull( LLCDHull* hullOut ) -{ - memset( hullOut, 0, sizeof(LLCDHull) ); - - if (!mBoundDecomp) - { - return LLCD_NULL_PTR; - } - - if (mBoundDecomp->mSingleHullMesh.vertices.empty()) - { - return LLCD_INVALID_HULL_DATA; - } - - mBoundDecomp->mSingleHullMesh.to(hullOut); - return LLCD_OK; -} - -LLCDResult LLConvexDecompositionVHACD::getHullFromStage( int stage, int hull, LLCDHull* hullOut ) -{ - memset( hullOut, 0, sizeof(LLCDHull) ); - - if (!mBoundDecomp) - { - return LLCD_NULL_PTR; - } - - if (stage != 0) - { - return LLCD_INVALID_STAGE; - } - - if (mBoundDecomp->mDecomposedHulls.empty() || mBoundDecomp->mDecomposedHulls.size() <= hull) - { - return LLCD_REQUEST_OUT_OF_RANGE; - } - - mBoundDecomp->mDecomposedHulls[hull].to(hullOut); - return LLCD_OK; -} - -LLCDResult LLConvexDecompositionVHACD::getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut ) -{ - memset( meshDataOut, 0, sizeof(LLCDMeshData)); - if (!mBoundDecomp) - { - return LLCD_NULL_PTR; - } - - if (stage != 0) - { - return LLCD_INVALID_STAGE; - } - - if (mBoundDecomp->mDecomposedHulls.empty() || mBoundDecomp->mDecomposedHulls.size() <= hull) - { - return LLCD_REQUEST_OUT_OF_RANGE; - } - - mBoundDecomp->mDecomposedHulls[hull].to(meshDataOut); - return LLCD_OK; -} - -LLCDResult LLConvexDecompositionVHACD::getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut ) -{ - memset(meshOut, 0, sizeof(LLCDMeshData)); - - LLVHACDMesh inMesh(hullIn); - - VHACD::QuickHull quickhull; - uint32_t num_tris = quickhull.ComputeConvexHull(inMesh.mVertices, MAX_VERTICES_PER_HULL); - if (num_tris > 0) - { - mMeshFromHullData.setVertices(quickhull.GetVertices()); - mMeshFromHullData.setIndices(quickhull.GetIndices()); - - mMeshFromHullData.to(meshOut); - return LLCD_OK; - } - - return LLCD_INVALID_HULL_DATA; -} - -LLCDResult LLConvexDecompositionVHACD::generateSingleHullMeshFromMesh(LLCDMeshData* meshIn, LLCDMeshData* meshOut) -{ - memset( meshOut, 0, sizeof(LLCDMeshData) ); - - LLVHACDMesh inMesh(meshIn, true); - - VHACD::QuickHull quickhull; - uint32_t num_tris = quickhull.ComputeConvexHull(inMesh.mVertices, MAX_VERTICES_PER_HULL); - if (num_tris > 0) - { - mSingleHullMeshFromMeshData.setVertices(quickhull.GetVertices()); - mSingleHullMeshFromMeshData.setIndices(quickhull.GetIndices()); - - mSingleHullMeshFromMeshData.to(meshOut); - return LLCD_OK; - } - - return LLCD_INVALID_MESH_DATA; -} - -void LLConvexDecompositionVHACD::loadMeshData(const char* fileIn, LLCDMeshData** meshDataOut) -{ - static LLCDMeshData meshData; - memset( &meshData, 0, sizeof(LLCDMeshData) ); - *meshDataOut = &meshData; -} diff --git a/indra/llconvexdecomposition/llconvexdecompositionvhacd.h b/indra/llconvexdecomposition/llconvexdecompositionvhacd.h deleted file mode 100644 index 675356629c..0000000000 --- a/indra/llconvexdecomposition/llconvexdecompositionvhacd.h +++ /dev/null @@ -1,339 +0,0 @@ -/** -* @file llconvexdecompositionvhacd.h -* @author rye@alchemyviewer.org -* @brief A VHACD based implementation of LLConvexDecomposition -* -* $LicenseInfo:firstyear=2025&license=viewerlgpl$ -* Second Life Viewer Source Code -* Copyright (C) 2025, Linden Research, Inc. -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License as published by the Free Software Foundation; -* version 2.1 of the License only. -* -* This library is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this library; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -* -* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -* $/LicenseInfo$ -*/ - -#ifndef LL_CONVEX_DECOMP_UTIL_VHACD_H -#define LL_CONVEX_DECOMP_UTIL_VHACD_H - -#include "llconvexdecomposition.h" -#include "llsingleton.h" -#include "llmath.h" - -#include - -#include "VHACD.h" - -class LLDecompDataVHACD; - -class LLConvexDecompositionVHACD : public LLSimpleton, public LLConvexDecomposition -{ - class VHACDCallback : public VHACD::IVHACD::IUserCallback - { - public: - void Update(const double overallProgress, const double stageProgress, const char* const stage, const char* operation) override - { - std::string out_msg = llformat("Stage: %s Operation: %s", stage, operation); - if (mCurrentStage != stage && mCurrentOperation != operation) - { - mCurrentStage = stage; - mCurrentOperation = operation; - LL_INFOS("VHACD") << out_msg << LL_ENDL; - } - - if(mCallbackFunc) - { - mCallbackFunc(out_msg.c_str(), ll_round(static_cast(stageProgress)), ll_round(static_cast(overallProgress))); - } - } - - void setCallbackFunc(llcdCallbackFunc func) - { - mCallbackFunc = func; - } - - private: - std::string mCurrentStage; - std::string mCurrentOperation; - llcdCallbackFunc mCallbackFunc = nullptr; - }; - - class VHACDLogger : public VHACD::IVHACD::IUserLogger - { - void Log(const char* const msg) override - { - LL_INFOS("VHACD") << msg << LL_ENDL; - } - }; - -public: - - LLConvexDecompositionVHACD(); - virtual ~LLConvexDecompositionVHACD(); - - static bool isFunctional(); - static LLConvexDecomposition* getInstance(); - static LLCDResult initSystem(); - static LLCDResult initThread(); - static LLCDResult quitThread(); - static LLCDResult quitSystem(); - - void genDecomposition(int& decomp); - void deleteDecomposition(int decomp); - void bindDecomposition(int decomp); - - // Sets *paramsOut to the address of the LLCDParam array and returns - // the length of the array - int getParameters(const LLCDParam** paramsOut) - { - *paramsOut = mDecompParams.data(); - return narrow(mDecompParams.size()); - } - - int getStages(const LLCDStageData** stagesOut) - { - *stagesOut = mDecompStages.data(); - return narrow(mDecompStages.size()); - } - - // Set a parameter by name. Returns false if out of bounds or unsupported parameter - LLCDResult setParam(const char* name, float val); - LLCDResult setParam(const char* name, int val); - LLCDResult setParam(const char* name, bool val); - LLCDResult setMeshData( const LLCDMeshData* data, bool vertex_based ); - LLCDResult registerCallback(int stage, llcdCallbackFunc callback ); - - LLCDResult executeStage(int stage); - LLCDResult buildSingleHull(); - - int getNumHullsFromStage(int stage); - - LLCDResult getHullFromStage( int stage, int hull, LLCDHull* hullOut ); - LLCDResult getSingleHull( LLCDHull* hullOut ) ; - - // TODO: Implement lock of some kind to disallow this call if data not yet ready - LLCDResult getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut); - LLCDResult getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut ); - - // For visualizing convex hull shapes in the viewer physics shape display - LLCDResult generateSingleHullMeshFromMesh( LLCDMeshData* meshIn, LLCDMeshData* meshOut); - - /// Debug - void loadMeshData(const char* fileIn, LLCDMeshData** meshDataOut); - -private: - std::vector mDecompParams; - std::array mDecompStages; - - struct LLVHACDMesh - { - using vertex_type = VHACD::Vertex; - using index_type = VHACD::Triangle; - using vertex_array_type = std::vector; - using index_array_type = std::vector; - - LLVHACDMesh() = default; - LLVHACDMesh(const LLCDHull* hullIn) - { - if (hullIn) - { - from(hullIn); - } - }; - - LLVHACDMesh(const LLCDMeshData* meshIn, bool vertex_based) - { - if (meshIn) - { - from(meshIn, vertex_based); - } - }; - - void clear() - { - mVertices.clear(); - mIndices.clear(); - } - - void setVertices(const float* data, int num_vertices, int vertex_stride_bytes) - { - vertex_array_type vertices; - vertices.reserve(num_vertices); - - const int stride = vertex_stride_bytes / sizeof(float); - for (int i = 0; i < num_vertices; ++i) - { - vertices.emplace_back(data[i * stride + 0], - data[i * stride + 1], - data[i * stride + 2]); - } - - mVertices = std::move(vertices); - } - - void setIndices(const void* data, int num_indices, int index_stride_bytes, LLCDMeshData::IndexType type) - { - index_array_type indices; - indices.reserve(num_indices); - - if (type == LLCDMeshData::INT_16) - { - const U16* index_data = static_cast(data); - const int stride = index_stride_bytes / sizeof(U16); - for (int i = 0; i < num_indices; ++i) - { - indices.emplace_back(index_data[i * stride + 0], - index_data[i * stride + 1], - index_data[i * stride + 2]); - } - } - else - { - const U32* index_data = static_cast(data); - const int stride = index_stride_bytes / sizeof(U32); - for (int i = 0; i < num_indices; ++i) - { - indices.emplace_back(index_data[i * stride + 0], - index_data[i * stride + 1], - index_data[i * stride + 2]); - } - } - - mIndices = std::move(indices); - } - - LLCDResult from(const LLCDHull* hullIn) - { - clear(); - - if (!hullIn || !hullIn->mVertexBase || (hullIn->mNumVertices < 3) || (hullIn->mVertexStrideBytes != 12 && hullIn->mVertexStrideBytes != 16)) - { - return LLCD_INVALID_HULL_DATA; - } - - setVertices(hullIn->mVertexBase, hullIn->mNumVertices, hullIn->mVertexStrideBytes); - - return LLCD_OK; - } - - LLCDResult from(const LLCDMeshData* meshIn, bool vertex_based) - { - clear(); - - if (!meshIn || !meshIn->mVertexBase || (meshIn->mNumVertices < 3) || (meshIn->mVertexStrideBytes != 12 && meshIn->mVertexStrideBytes != 16)) - { - return LLCD_INVALID_MESH_DATA; - } - - if (!vertex_based && ((meshIn->mNumTriangles < 1) || !meshIn->mIndexBase)) - { - return LLCD_INVALID_MESH_DATA; - } - - setVertices(meshIn->mVertexBase, meshIn->mNumVertices, meshIn->mVertexStrideBytes); - if(!vertex_based) - { - setIndices(meshIn->mIndexBase, meshIn->mNumTriangles, meshIn->mIndexStrideBytes, meshIn->mIndexType); - } - - return LLCD_OK; - } - - vertex_array_type mVertices; - index_array_type mIndices; - }; - - struct LLConvexMesh - { - using vertex_type = glm::vec3; - using index_type = glm::u32vec3; - using vertex_array_type = std::vector; - using index_array_type = std::vector; - - LLConvexMesh() = default; - - void clear() - { - vertices.clear(); - indices.clear(); - } - - void setVertices(const std::vector& in_vertices) - { - vertices.clear(); - vertices.reserve(in_vertices.size()); - - for (const auto& vertex : in_vertices) - { - vertices.emplace_back(narrow(vertex.mX), narrow(vertex.mY), narrow(vertex.mZ)); - } - } - - void setIndices(const std::vector& in_indices) - { - indices.clear(); - indices.reserve(in_indices.size()); - - for (const auto& triangle : in_indices) - { - indices.emplace_back(narrow(triangle.mI0), narrow(triangle.mI1), narrow(triangle.mI2)); - } - } - - void to(LLCDHull* meshOut) const - { - meshOut->mVertexBase = (float*)vertices.data(); - meshOut->mVertexStrideBytes = sizeof(vertex_type); - meshOut->mNumVertices = (int)vertices.size(); - } - - void to(LLCDMeshData* meshOut) const - { - meshOut->mVertexBase = (float*)vertices.data(); - meshOut->mVertexStrideBytes = sizeof(vertex_type); - meshOut->mNumVertices = (int)vertices.size(); - - meshOut->mIndexType = LLCDMeshData::INT_32; - meshOut->mIndexBase = indices.data(); - meshOut->mIndexStrideBytes = sizeof(index_type); - meshOut->mNumTriangles = (int)indices.size(); - } - - vertex_array_type vertices; - index_array_type indices; - }; - - struct LLDecompData - { - LLVHACDMesh mSourceMesh; - LLConvexMesh mSingleHullMesh; - - std::vector mDecomposedHulls; - }; - - std::unordered_map mDecompData; - - LLDecompData* mBoundDecomp = nullptr; - - VHACD::IVHACD* mVHACD = nullptr; - VHACDCallback mVHACDCallback; - VHACDLogger mVHACDLogger; - VHACD::IVHACD::Parameters mVHACDParameters; - - LLConvexMesh mMeshFromHullData; - LLConvexMesh mSingleHullMeshFromMeshData; -}; - -#endif //LL_CONVEX_DECOMP_UTIL_VHACD_H diff --git a/indra/llphysicsextensionsos/CMakeLists.txt b/indra/llphysicsextensionsos/CMakeLists.txt new file mode 100644 index 0000000000..c04115ef26 --- /dev/null +++ b/indra/llphysicsextensionsos/CMakeLists.txt @@ -0,0 +1,47 @@ +# -*- cmake -*- + +project(llphysicsextensionsos) + +include(00-Common) +include(LLCommon) +include(LLMath) +include(VHACD) + +set(llphysicsextensionsos_SOURCE_FILES + llconvexdecomposition.cpp + llconvexdecompositionvhacd.cpp + llpathinglib.cpp + LLPathingLibStubImpl.cpp + llphysicsextensions.cpp + LLPhysicsExtensionsStubImpl.cpp + ) + +set(llphysicsextensionsos_HEADER_FILES + CMakeLists.txt + llconvexdecomposition.h + llconvexdecompositionvhacd.h + llpathinglib.h + LLPathingLibStubImpl.h + llphysicsextensions.h + LLPhysicsExtensionsStubImpl.h + ) + +set_source_files_properties(${llphysicsextensionsos_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND llphysicsextensionsos_SOURCE_FILES ${llphysicsextensionsos_HEADER_FILES}) + +add_library (llphysicsextensionsos ${llphysicsextensionsos_SOURCE_FILES}) +target_include_directories(llphysicsextensionsos INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +target_link_libraries(llphysicsextensionsos + llcommon + llmath + ll::vhacd) + +if(WINDOWS) + target_compile_options(llphysicsextensionsos PRIVATE /bigobj) +endif() + +# Add tests + diff --git a/indra/llphysicsextensionsos/LLPathingLibStubImpl.cpp b/indra/llphysicsextensionsos/LLPathingLibStubImpl.cpp new file mode 100644 index 0000000000..9830fd1ad0 --- /dev/null +++ b/indra/llphysicsextensionsos/LLPathingLibStubImpl.cpp @@ -0,0 +1,109 @@ +/** +* @file LLPathingLibStubImpl.cpp +* @author prep@lindenlab.com +* @brief A stubbed implementation of LLPathingLib +* +* $LicenseInfo:firstyear=2012&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 20112010, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#include "linden_common.h" + +#include "llpathinglib.h" +#include "LLPathingLibStubImpl.h" + +#include "llsd.h" + +//============================================================================= +LLPathingLibImpl::LLPathingLibImpl() +{ +} + +LLPathingLibImpl::~LLPathingLibImpl() +{ + +} + +LLPathingLib* LLPathingLibImpl::getInstance() +{ + return NULL; +} + + +LLPathingLib::LLPLResult LLPathingLibImpl::initSystem() +{ + return LLPL_NOT_IMPLEMENTED; +} + +LLPathingLib::LLPLResult LLPathingLibImpl::quitSystem() +{ + return LLPL_NOT_IMPLEMENTED; +} + +LLPathingLib::LLPLResult LLPathingLibImpl::extractNavMeshSrcFromLLSD( const LLSD::Binary& dataBlock, int dir ) +{ + return LLPL_NOT_IMPLEMENTED; +} + +void LLPathingLibImpl::processNavMeshData() +{ +} + +LLPathingLibImpl::LLPLResult LLPathingLibImpl::generatePath( const PathingPacket& pathingPacket ) +{ + return LLPL_NOT_IMPLEMENTED; +} + +void LLPathingLibImpl::setNavMeshMaterialType( LLPLCharacterType materialType ) +{ +} + +void LLPathingLibImpl::setNavMeshColors( const NavMeshColors& color ) +{ +} + +void LLPathingLibImpl::renderNavMesh() +{ +} + +void LLPathingLibImpl::renderNavMeshEdges() +{ +} + +void LLPathingLibImpl::renderNavMeshShapesVBO( U32 shapeRenderFlags ) +{ +} + +void LLPathingLibImpl::renderPath() +{ +} + +void LLPathingLibImpl::renderPathBookend( LLRender& gl, LLPathingLib::LLPLPathBookEnd type ) +{ +} + +void LLPathingLibImpl::cleanupVBOManager() +{ +} + +void LLPathingLibImpl::cleanupResidual() +{ +} diff --git a/indra/llphysicsextensionsos/LLPathingLibStubImpl.h b/indra/llphysicsextensionsos/LLPathingLibStubImpl.h new file mode 100644 index 0000000000..8a97566e8c --- /dev/null +++ b/indra/llphysicsextensionsos/LLPathingLibStubImpl.h @@ -0,0 +1,78 @@ +/** +* @file LLPathingLibSubImpl.h +* @author prep@lindenlab.com +* @brief A stubbed implementation of LLPathingLib +* +* $LicenseInfo:firstyear=2012&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2012, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#ifndef LL_PATHING_LIB_H +#define LL_PATHING_LIB_H + +#include "llpathinglib.h" + +class LLSD; + +//============================================================================= +class LLPathingLibImpl : public LLPathingLib +{ +public: + LLPathingLibImpl(); + virtual ~LLPathingLibImpl(); + + // Obtain a pointer to the actual implementation + static LLPathingLib* getInstance(); + static LLPathingLib::LLPLResult initSystem(); + static LLPathingLib::LLPLResult quitSystem(); + + //Extract and store navmesh data from the llsd datablock sent down by the server + virtual LLPLResult extractNavMeshSrcFromLLSD( const LLSD::Binary& dataBlock, int dir ); + //Stitch any stored navmeshes together + virtual void processNavMeshData(); + + //Method used to generate and visualize a path on the viewers navmesh + virtual LLPLResult generatePath( const PathingPacket& pathingPacket ); + + //Set the material type for the heatmap type + virtual void setNavMeshMaterialType( LLPLCharacterType materialType ); + //Set the various navmesh colors + virtual void setNavMeshColors( const NavMeshColors& color ); + + //The entry method to rendering the client side navmesh + virtual void renderNavMesh(); + //The entry method to rendering the client side navmesh edges + virtual void renderNavMeshEdges(); + //The entry method to render the client navmesh shapes VBO + virtual void renderNavMeshShapesVBO( U32 shapeRenderFlags ); + //The entry method to render the clients designated path + virtual void renderPath(); + //The entry method to render the capsule bookends for the clients designated path + virtual void renderPathBookend( LLRender& gl, LLPathingLib::LLPLPathBookEnd type ); + + //Method to delete any vbo's that are currently being managed by the pathing library + virtual void cleanupVBOManager(); + //Method to cleanup any allocations within the implementation + virtual void cleanupResidual(); +}; + +#endif //LL_PATHING_LIB_H + diff --git a/indra/llphysicsextensionsos/LLPhysicsExtensionsStubImpl.cpp b/indra/llphysicsextensionsos/LLPhysicsExtensionsStubImpl.cpp new file mode 100644 index 0000000000..8401e16e9c --- /dev/null +++ b/indra/llphysicsextensionsos/LLPhysicsExtensionsStubImpl.cpp @@ -0,0 +1,51 @@ +/** +* @file LLPhysicsExtensionsStubImpl.cpp +* @author prep@lindenlab.com +* @brief A stubbed implementation of LLPhysicsExtensions +* +* $LicenseInfo:firstyear=2012&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2012, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#include "linden_common.h" + +#include "llphysicsextensions.h" +#include "LLPhysicsExtensionsStubImpl.h" + +//============================================================================= +LLPhysicsExtensionsImpl::LLPhysicsExtensionsImpl() +{ +} + +LLPhysicsExtensionsImpl::~LLPhysicsExtensionsImpl() +{ +} + +bool LLPhysicsExtensionsImpl::initSystem() +{ + return false; +} + +bool LLPhysicsExtensionsImpl::quitSystem() +{ + return false; +} + diff --git a/indra/llphysicsextensionsos/LLPhysicsExtensionsStubImpl.h b/indra/llphysicsextensionsos/LLPhysicsExtensionsStubImpl.h new file mode 100644 index 0000000000..135f92bb88 --- /dev/null +++ b/indra/llphysicsextensionsos/LLPhysicsExtensionsStubImpl.h @@ -0,0 +1,46 @@ +/** +* @file LLPhysicsExtensionsSubImpl.h +* @author prep@lindenlab.com +* @brief A stubbed implementation of LLPhysicsExtensions +* +* $LicenseInfo:firstyear=2012&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2012, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#ifndef LL_PHYSICS_EXTENSIONS_STUB_IMPL_H +#define LL_PHYSICS_EXTENSIONS_STUB_IMPL_H + +#include "llphysicsextensions.h" + +//============================================================================= +class LLPhysicsExtensionsImpl : public LLPhysicsExtensions +{ + public: + + LLPhysicsExtensionsImpl(); + virtual ~LLPhysicsExtensionsImpl(); + + static bool initSystem(); + static bool quitSystem(); +}; + +#endif //LL_PHYSICS_EXTENSIONS_STUB_IMPL_H + diff --git a/indra/llphysicsextensionsos/llconvexdecomposition.cpp b/indra/llphysicsextensionsos/llconvexdecomposition.cpp new file mode 100644 index 0000000000..7b9d775c53 --- /dev/null +++ b/indra/llphysicsextensionsos/llconvexdecomposition.cpp @@ -0,0 +1,83 @@ +/** +* @file llconvexdecomposition.cpp +* @author falcon@lindenlab.com +* @brief Inner implementation of LLConvexDecomposition interface +* +* $LicenseInfo:firstyear=2011&license=lgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2011, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#include "linden_common.h" + +#include "llconvexdecompositionvhacd.h" +#include "llconvexdecomposition.h" + +bool LLConvexDecomposition::s_isInitialized = false; + +// static +bool LLConvexDecomposition::isFunctional() +{ + return LLConvexDecompositionVHACD::isFunctional(); +} + +// static +LLConvexDecomposition* LLConvexDecomposition::getInstance() +{ + if ( !s_isInitialized ) + { + return nullptr; + } + else + { + return LLConvexDecompositionVHACD::getInstance(); + } +} + +// static +LLCDResult LLConvexDecomposition::initSystem() +{ + LLCDResult result = LLConvexDecompositionVHACD::initSystem(); + if ( result == LLCD_OK ) + { + s_isInitialized = true; + } + return result; +} + +// static +LLCDResult LLConvexDecomposition::initThread() +{ + return LLConvexDecompositionVHACD::initThread(); +} + +// static +LLCDResult LLConvexDecomposition::quitThread() +{ + return LLConvexDecompositionVHACD::quitThread(); +} + +// static +LLCDResult LLConvexDecomposition::quitSystem() +{ + return LLConvexDecompositionVHACD::quitSystem(); +} + + diff --git a/indra/llphysicsextensionsos/llconvexdecomposition.h b/indra/llphysicsextensionsos/llconvexdecomposition.h new file mode 100644 index 0000000000..8008bc6e12 --- /dev/null +++ b/indra/llphysicsextensionsos/llconvexdecomposition.h @@ -0,0 +1,231 @@ +/** + * @file llconvexdecomposition.cpp + * @brief LLConvexDecomposition interface definition + * + * $LicenseInfo:firstyear=2011&license=lgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_CONVEX_DECOMPOSITION +#define LL_CONVEX_DECOMPOSITION + +typedef int bool32; + +#if defined(_WIN32) || defined(_WIN64) +#define LLCD_CALL __cdecl +#else +#define LLCD_CALL +#endif + +struct LLCDParam +{ + enum LLCDParamType + { + LLCD_INVALID = 0, + LLCD_INTEGER, + LLCD_FLOAT, + LLCD_BOOLEAN, + LLCD_ENUM + }; + + struct LLCDEnumItem + { + const char* mName; + int mValue; + }; + + union LLCDValue + { + float mFloat; + int mIntOrEnumValue; + bool32 mBool; + }; + + union LLCDParamDetails + { + struct { + LLCDValue mLow; + LLCDValue mHigh; + LLCDValue mDelta; + } mRange; + + struct { + int mNumEnums; + LLCDEnumItem* mEnumsArray; + } mEnumValues; + }; + + const char* mName; + const char* mDescription; + LLCDParamType mType; + LLCDParamDetails mDetails; + LLCDValue mDefault; + int mStage; + + // WARNING: Only the LLConvexDecomposition implementation + // should change this value + int mReserved; +}; + +struct LLCDStageData +{ + const char* mName; + const char* mDescription; + bool32 mSupportsCallback; +}; + +struct LLCDMeshData +{ + enum IndexType + { + INT_16, + INT_32 + }; + + const float* mVertexBase; + int mVertexStrideBytes; + int mNumVertices; + const void* mIndexBase; + IndexType mIndexType; + int mIndexStrideBytes; + int mNumTriangles; +}; + +struct LLCDHull +{ + const float* mVertexBase; + int mVertexStrideBytes; + int mNumVertices; +}; + +enum LLCDResult +{ + LLCD_OK = 0, + LLCD_UNKOWN_ERROR, + LLCD_NULL_PTR, + LLCD_INVALID_STAGE, + LLCD_UNKNOWN_PARAM, + LLCD_BAD_VALUE, + LLCD_REQUEST_OUT_OF_RANGE, + LLCD_INVALID_MESH_DATA, + LLCD_INVALID_HULL_DATA, + LLCD_STAGE_NOT_READY, + LLCD_INVALID_THREAD, + LLCD_NOT_IMPLEMENTED +}; + +// This callback will receive a string describing the current subtask being performed +// as well as a pair of numbers indicating progress. (The values should not be interpreted +// as a completion percentage as 'current' may be greater than 'final'.) +// If the callback returns zero, the decomposition will be terminated +typedef int (LLCD_CALL *llcdCallbackFunc)(const char* description, int current_progress, int final_progress); + +class LLConvexDecomposition +{ +public: + // Obtain a pointer to the actual implementation + static LLConvexDecomposition* getInstance(); + + /// @returns false if this is the stub + static bool isFunctional(); + + static LLCDResult initSystem(); + static LLCDResult initThread(); + static LLCDResult quitThread(); + static LLCDResult quitSystem(); + + // Generate a decomposition object handle + virtual void genDecomposition(int& decomp) = 0; + // Delete decomposition object handle + virtual void deleteDecomposition(int decomp) = 0; + // Bind given decomposition handle + // Commands operate on currently bound decomposition + virtual void bindDecomposition(int decomp) = 0; + + // Sets *paramsOut to the address of the LLCDParam array and returns + // the number of parameters + virtual int getParameters(const LLCDParam** paramsOut) = 0; + + + // Sets *stagesOut to the address of the LLCDStageData array and returns + // the number of stages + virtual int getStages(const LLCDStageData** stagesOut) = 0; + + + // Set a parameter by name. Pass enum values as integers. + virtual LLCDResult setParam(const char* name, float val) = 0; + virtual LLCDResult setParam(const char* name, int val) = 0; + virtual LLCDResult setParam(const char* name, bool val) = 0; + + + // Set incoming mesh data. Data is copied to local buffers and will + // persist until the next setMeshData call + virtual LLCDResult setMeshData( const LLCDMeshData* data, bool vertex_based ) = 0; + + + // Register a callback to be called periodically during the specified stage + // See the typedef above for more information + virtual LLCDResult registerCallback( int stage, llcdCallbackFunc callback ) = 0; + + + // Execute the specified decomposition stage + virtual LLCDResult executeStage(int stage) = 0; + virtual LLCDResult buildSingleHull() = 0 ; + + + // Gets the number of hulls generated by the specified decompositions stage + virtual int getNumHullsFromStage(int stage) = 0; + + + // Populates hullOut to reference the internal copy of the requested hull + // The data will persist only until the next executeStage call for that stage. + virtual LLCDResult getHullFromStage( int stage, int hull, LLCDHull* hullOut ) = 0; + + virtual LLCDResult getSingleHull( LLCDHull* hullOut ) = 0 ; + + + // TODO: Implement lock of some kind to disallow this call if data not yet ready + // Populates the meshDataOut to reference the utility's copy of the mesh geometry + // for the hull and stage specified. + // You must copy this data if you want to continue using it after the next executeStage + // call + virtual LLCDResult getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut) = 0; + + + // Creates a mesh from hullIn and temporarily stores it internally in the utility. + // The mesh data persists only until the next call to getMeshFromHull + virtual LLCDResult getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut ) = 0; + + // Takes meshIn, generates a single convex hull from it, converts that to a mesh + // stored internally, and populates meshOut to reference the internally stored data. + // The data is persistent only until the next call to generateSingleHullMeshFromMesh + virtual LLCDResult generateSingleHullMeshFromMesh( LLCDMeshData* meshIn, LLCDMeshData* meshOut) = 0; + + // + /// Debug + virtual void loadMeshData(const char* fileIn, LLCDMeshData** meshDataOut) = 0; + +private: + static bool s_isInitialized; +}; + +#endif //LL_CONVEX_DECOMPOSITION + diff --git a/indra/llphysicsextensionsos/llconvexdecompositionvhacd.cpp b/indra/llphysicsextensionsos/llconvexdecompositionvhacd.cpp new file mode 100644 index 0000000000..78876f9f36 --- /dev/null +++ b/indra/llphysicsextensionsos/llconvexdecompositionvhacd.cpp @@ -0,0 +1,492 @@ +/** +* @file llconvexdecompositionvhacd.cpp +* @author rye@alchemyviewer.org +* @brief A VHACD based implementation of LLConvexDecomposition +* +* $LicenseInfo:firstyear=2025&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2025, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#include "linden_common.h" + +#include "llmath.h" +#include "v3math.h" + +#include +#include + +#define ENABLE_VHACD_IMPLEMENTATION 1 +#include "VHACD.h" + +#include "llconvexdecompositionvhacd.h" + +constexpr S32 MAX_HULLS = 256; +constexpr S32 MAX_VERTICES_PER_HULL = 256; + +bool LLConvexDecompositionVHACD::isFunctional() +{ + return true; +} + +LLConvexDecomposition* LLConvexDecompositionVHACD::getInstance() +{ + return LLSimpleton::getInstance(); +} + +LLCDResult LLConvexDecompositionVHACD::initSystem() +{ + createInstance(); + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::initThread() +{ + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::quitThread() +{ + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::quitSystem() +{ + deleteSingleton(); + return LLCD_OK; +} + +LLConvexDecompositionVHACD::LLConvexDecompositionVHACD() +{ + //Create our vhacd instance and setup default parameters + mVHACD = VHACD::CreateVHACD(); + + mVHACDParameters.m_callback = &mVHACDCallback; + mVHACDParameters.m_logger = &mVHACDLogger; + + mDecompStages[0].mName = "Analyze"; + mDecompStages[0].mDescription = nullptr; + + LLCDParam param; + param.mName = "Fill Mode"; + param.mDescription = nullptr; + param.mType = LLCDParam::LLCD_ENUM; + param.mDetails.mEnumValues.mNumEnums = 3; + + static LLCDParam::LLCDEnumItem fill_enums[3]; + fill_enums[(size_t)VHACD::FillMode::FLOOD_FILL].mName = "Flood"; + fill_enums[(size_t)VHACD::FillMode::FLOOD_FILL].mValue = (int)VHACD::FillMode::FLOOD_FILL; + fill_enums[(size_t)VHACD::FillMode::SURFACE_ONLY].mName = "Surface Only"; + fill_enums[(size_t)VHACD::FillMode::SURFACE_ONLY].mValue = (int)VHACD::FillMode::SURFACE_ONLY; + fill_enums[(size_t)VHACD::FillMode::RAYCAST_FILL].mName = "Raycast"; + fill_enums[(size_t)VHACD::FillMode::RAYCAST_FILL].mValue = (int)VHACD::FillMode::RAYCAST_FILL; + + param.mDetails.mEnumValues.mEnumsArray = fill_enums; + param.mDefault.mIntOrEnumValue = (int)VHACD::FillMode::FLOOD_FILL; + param.mStage = 0; + param.mReserved = -1; + mDecompParams.push_back(param); + + enum EVoxelQualityLevels + { + E_LOW_QUALITY = 0, + E_NORMAL_QUALITY, + E_HIGH_QUALITY, + E_VERY_HIGH_QUALITY, + E_ULTRA_QUALITY, + E_MAX_QUALITY, + E_NUM_QUALITY_LEVELS + }; + + param.mName = "Voxel Resolution"; + param.mDescription = nullptr; + param.mType = LLCDParam::LLCD_ENUM; + param.mDetails.mEnumValues.mNumEnums = E_NUM_QUALITY_LEVELS; + + static LLCDParam::LLCDEnumItem voxel_quality_enums[E_NUM_QUALITY_LEVELS]; + voxel_quality_enums[E_LOW_QUALITY].mName = "Low"; + voxel_quality_enums[E_LOW_QUALITY].mValue = 200000; + voxel_quality_enums[E_NORMAL_QUALITY].mName = "Normal"; + voxel_quality_enums[E_NORMAL_QUALITY].mValue = 400000; + voxel_quality_enums[E_HIGH_QUALITY].mName = "High"; + voxel_quality_enums[E_HIGH_QUALITY].mValue = 800000; + voxel_quality_enums[E_VERY_HIGH_QUALITY].mName = "Very High"; + voxel_quality_enums[E_VERY_HIGH_QUALITY].mValue = 1200000; + voxel_quality_enums[E_ULTRA_QUALITY].mName = "Ultra"; + voxel_quality_enums[E_ULTRA_QUALITY].mValue = 1600000; + voxel_quality_enums[E_MAX_QUALITY].mName = "Maximum"; + voxel_quality_enums[E_MAX_QUALITY].mValue = 2000000; + + param.mDetails.mEnumValues.mEnumsArray = voxel_quality_enums; + param.mDefault.mIntOrEnumValue = 400000; + param.mStage = 0; + param.mReserved = -1; + mDecompParams.push_back(param); + + param.mName = "Num Hulls"; + param.mDescription = nullptr; + param.mType = LLCDParam::LLCD_FLOAT; + param.mDetails.mRange.mLow.mFloat = 1.f; + param.mDetails.mRange.mHigh.mFloat = MAX_HULLS; + param.mDetails.mRange.mDelta.mFloat = 1.f; + param.mDefault.mFloat = 8.f; + param.mStage = 0; + param.mReserved = -1; + mDecompParams.push_back(param); + + param.mName = "Num Vertices"; + param.mDescription = nullptr; + param.mType = LLCDParam::LLCD_FLOAT; + param.mDetails.mRange.mLow.mFloat = 3.f; + param.mDetails.mRange.mHigh.mFloat = MAX_VERTICES_PER_HULL; + param.mDetails.mRange.mDelta.mFloat = 1.f; + param.mDefault.mFloat = 32.f; + param.mStage = 0; + param.mReserved = -1; + mDecompParams.push_back(param); + + param.mName = "Error Tolerance"; + param.mDescription = nullptr; + param.mType = LLCDParam::LLCD_FLOAT; + param.mDetails.mRange.mLow.mFloat = 0.0001f; + param.mDetails.mRange.mHigh.mFloat = 99.f; + param.mDetails.mRange.mDelta.mFloat = 0.001f; + param.mDefault.mFloat = 1.f; + param.mStage = 0; + param.mReserved = -1; + mDecompParams.push_back(param); + + for (const LLCDParam& param : mDecompParams) + { + const char* const name = param.mName; + + switch (param.mType) + { + case LLCDParam::LLCD_FLOAT: + { + setParam(name, param.mDefault.mFloat); + break; + } + case LLCDParam::LLCD_ENUM: + case LLCDParam::LLCD_INTEGER: + { + setParam(name, param.mDefault.mIntOrEnumValue); + break; + } + case LLCDParam::LLCD_BOOLEAN: + { + setParam(name, (param.mDefault.mBool != 0)); + break; + } + case LLCDParam::LLCD_INVALID: + default: + { + break; + } + } + } +} + +LLConvexDecompositionVHACD::~LLConvexDecompositionVHACD() +{ + mBoundDecomp = nullptr; + mDecompData.clear(); + + mVHACD->Release(); +} + +void LLConvexDecompositionVHACD::genDecomposition(int& decomp) +{ + int new_decomp_id = static_cast(mDecompData.size()) + 1; + mDecompData[new_decomp_id] = LLDecompData(); + decomp = new_decomp_id; +} + +void LLConvexDecompositionVHACD::deleteDecomposition(int decomp) +{ + auto iter = mDecompData.find(decomp); + if (iter != mDecompData.end()) + { + if (mBoundDecomp == &iter->second) + { + mBoundDecomp = nullptr; + } + mDecompData.erase(iter); + } +} + +void LLConvexDecompositionVHACD::bindDecomposition(int decomp) +{ + auto iter = mDecompData.find(decomp); + if (iter != mDecompData.end()) + { + mBoundDecomp = &iter->second; + } + else + { + LL_WARNS() << "Failed to bind unknown decomposition: " << decomp << LL_ENDL; + mBoundDecomp = nullptr; + } +} + +LLCDResult LLConvexDecompositionVHACD::setParam(const char* name, float val) +{ + if (name == std::string("Num Hulls")) + { + mVHACDParameters.m_maxConvexHulls = llclamp(ll_round(val), 1, MAX_HULLS); + } + else if (name == std::string("Num Vertices")) + { + mVHACDParameters.m_maxNumVerticesPerCH = llclamp(ll_round(val), 3, MAX_VERTICES_PER_HULL); + } + else if (name == std::string("Error Tolerance")) + { + mVHACDParameters.m_minimumVolumePercentErrorAllowed = val; + } + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::setParam(const char* name, bool val) +{ + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::setParam(const char* name, int val) +{ + if (name == std::string("Fill Mode")) + { + mVHACDParameters.m_fillMode = (VHACD::FillMode)val; + } + else if (name == std::string("Voxel Resolution")) + { + mVHACDParameters.m_resolution = val; + } + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::setMeshData( const LLCDMeshData* data, bool vertex_based ) +{ + if (!mBoundDecomp) + { + return LLCD_NULL_PTR; + } + + return mBoundDecomp->mSourceMesh.from(data, vertex_based); +} + +LLCDResult LLConvexDecompositionVHACD::registerCallback(int stage, llcdCallbackFunc callback ) +{ + if (stage == 0) + { + mVHACDCallback.setCallbackFunc(callback); + return LLCD_OK; + } + else + { + return LLCD_INVALID_STAGE; + } +} + +LLCDResult LLConvexDecompositionVHACD::executeStage(int stage) +{ + if (!mBoundDecomp) + { + return LLCD_NULL_PTR; + } + + if (stage != 0) + { + return LLCD_INVALID_STAGE; + } + + mBoundDecomp->mDecomposedHulls.clear(); + + const auto& decomp_mesh = mBoundDecomp->mSourceMesh; + if (!mVHACD->Compute((const double* const)decomp_mesh.mVertices.data(), static_cast(decomp_mesh.mVertices.size()), (const uint32_t* const)decomp_mesh.mIndices.data(), static_cast(decomp_mesh.mIndices.size()), mVHACDParameters)) + { + return LLCD_INVALID_HULL_DATA; + } + + uint32_t num_nulls = mVHACD->GetNConvexHulls(); + if (num_nulls == 0) + { + return LLCD_INVALID_HULL_DATA; + } + + for (uint32_t i = 0; num_nulls > i; ++i) + { + VHACD::IVHACD::ConvexHull ch; + if (!mVHACD->GetConvexHull(i, ch)) + continue; + + LLConvexMesh out_mesh; + out_mesh.setVertices(ch.m_points); + out_mesh.setIndices(ch.m_triangles); + + mBoundDecomp->mDecomposedHulls.push_back(std::move(out_mesh)); + } + + mVHACD->Clean(); + + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::buildSingleHull() +{ + LL_INFOS() << "Building single hull mesh" << LL_ENDL; + if (!mBoundDecomp || mBoundDecomp->mSourceMesh.mVertices.empty()) + { + return LLCD_NULL_PTR; + } + + mBoundDecomp->mSingleHullMesh.clear(); + + VHACD::QuickHull quickhull; + uint32_t num_tris = quickhull.ComputeConvexHull(mBoundDecomp->mSourceMesh.mVertices, MAX_VERTICES_PER_HULL); + if (num_tris > 0) + { + mBoundDecomp->mSingleHullMesh.setVertices(quickhull.GetVertices()); + mBoundDecomp->mSingleHullMesh.setIndices(quickhull.GetIndices()); + + return LLCD_OK; + } + + return LLCD_INVALID_MESH_DATA; +} + +int LLConvexDecompositionVHACD::getNumHullsFromStage(int stage) +{ + if (!mBoundDecomp || stage != 0) + { + return 0; + } + + return narrow(mBoundDecomp->mDecomposedHulls.size()); +} + +LLCDResult LLConvexDecompositionVHACD::getSingleHull( LLCDHull* hullOut ) +{ + memset( hullOut, 0, sizeof(LLCDHull) ); + + if (!mBoundDecomp) + { + return LLCD_NULL_PTR; + } + + if (mBoundDecomp->mSingleHullMesh.vertices.empty()) + { + return LLCD_INVALID_HULL_DATA; + } + + mBoundDecomp->mSingleHullMesh.to(hullOut); + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::getHullFromStage( int stage, int hull, LLCDHull* hullOut ) +{ + memset( hullOut, 0, sizeof(LLCDHull) ); + + if (!mBoundDecomp) + { + return LLCD_NULL_PTR; + } + + if (stage != 0) + { + return LLCD_INVALID_STAGE; + } + + if (mBoundDecomp->mDecomposedHulls.empty() || mBoundDecomp->mDecomposedHulls.size() <= hull) + { + return LLCD_REQUEST_OUT_OF_RANGE; + } + + mBoundDecomp->mDecomposedHulls[hull].to(hullOut); + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut ) +{ + memset( meshDataOut, 0, sizeof(LLCDMeshData)); + if (!mBoundDecomp) + { + return LLCD_NULL_PTR; + } + + if (stage != 0) + { + return LLCD_INVALID_STAGE; + } + + if (mBoundDecomp->mDecomposedHulls.empty() || mBoundDecomp->mDecomposedHulls.size() <= hull) + { + return LLCD_REQUEST_OUT_OF_RANGE; + } + + mBoundDecomp->mDecomposedHulls[hull].to(meshDataOut); + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut ) +{ + memset(meshOut, 0, sizeof(LLCDMeshData)); + + LLVHACDMesh inMesh(hullIn); + + VHACD::QuickHull quickhull; + uint32_t num_tris = quickhull.ComputeConvexHull(inMesh.mVertices, MAX_VERTICES_PER_HULL); + if (num_tris > 0) + { + mMeshFromHullData.setVertices(quickhull.GetVertices()); + mMeshFromHullData.setIndices(quickhull.GetIndices()); + + mMeshFromHullData.to(meshOut); + return LLCD_OK; + } + + return LLCD_INVALID_HULL_DATA; +} + +LLCDResult LLConvexDecompositionVHACD::generateSingleHullMeshFromMesh(LLCDMeshData* meshIn, LLCDMeshData* meshOut) +{ + memset( meshOut, 0, sizeof(LLCDMeshData) ); + + LLVHACDMesh inMesh(meshIn, true); + + VHACD::QuickHull quickhull; + uint32_t num_tris = quickhull.ComputeConvexHull(inMesh.mVertices, MAX_VERTICES_PER_HULL); + if (num_tris > 0) + { + mSingleHullMeshFromMeshData.setVertices(quickhull.GetVertices()); + mSingleHullMeshFromMeshData.setIndices(quickhull.GetIndices()); + + mSingleHullMeshFromMeshData.to(meshOut); + return LLCD_OK; + } + + return LLCD_INVALID_MESH_DATA; +} + +void LLConvexDecompositionVHACD::loadMeshData(const char* fileIn, LLCDMeshData** meshDataOut) +{ + static LLCDMeshData meshData; + memset( &meshData, 0, sizeof(LLCDMeshData) ); + *meshDataOut = &meshData; +} diff --git a/indra/llphysicsextensionsos/llconvexdecompositionvhacd.h b/indra/llphysicsextensionsos/llconvexdecompositionvhacd.h new file mode 100644 index 0000000000..675356629c --- /dev/null +++ b/indra/llphysicsextensionsos/llconvexdecompositionvhacd.h @@ -0,0 +1,339 @@ +/** +* @file llconvexdecompositionvhacd.h +* @author rye@alchemyviewer.org +* @brief A VHACD based implementation of LLConvexDecomposition +* +* $LicenseInfo:firstyear=2025&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2025, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#ifndef LL_CONVEX_DECOMP_UTIL_VHACD_H +#define LL_CONVEX_DECOMP_UTIL_VHACD_H + +#include "llconvexdecomposition.h" +#include "llsingleton.h" +#include "llmath.h" + +#include + +#include "VHACD.h" + +class LLDecompDataVHACD; + +class LLConvexDecompositionVHACD : public LLSimpleton, public LLConvexDecomposition +{ + class VHACDCallback : public VHACD::IVHACD::IUserCallback + { + public: + void Update(const double overallProgress, const double stageProgress, const char* const stage, const char* operation) override + { + std::string out_msg = llformat("Stage: %s Operation: %s", stage, operation); + if (mCurrentStage != stage && mCurrentOperation != operation) + { + mCurrentStage = stage; + mCurrentOperation = operation; + LL_INFOS("VHACD") << out_msg << LL_ENDL; + } + + if(mCallbackFunc) + { + mCallbackFunc(out_msg.c_str(), ll_round(static_cast(stageProgress)), ll_round(static_cast(overallProgress))); + } + } + + void setCallbackFunc(llcdCallbackFunc func) + { + mCallbackFunc = func; + } + + private: + std::string mCurrentStage; + std::string mCurrentOperation; + llcdCallbackFunc mCallbackFunc = nullptr; + }; + + class VHACDLogger : public VHACD::IVHACD::IUserLogger + { + void Log(const char* const msg) override + { + LL_INFOS("VHACD") << msg << LL_ENDL; + } + }; + +public: + + LLConvexDecompositionVHACD(); + virtual ~LLConvexDecompositionVHACD(); + + static bool isFunctional(); + static LLConvexDecomposition* getInstance(); + static LLCDResult initSystem(); + static LLCDResult initThread(); + static LLCDResult quitThread(); + static LLCDResult quitSystem(); + + void genDecomposition(int& decomp); + void deleteDecomposition(int decomp); + void bindDecomposition(int decomp); + + // Sets *paramsOut to the address of the LLCDParam array and returns + // the length of the array + int getParameters(const LLCDParam** paramsOut) + { + *paramsOut = mDecompParams.data(); + return narrow(mDecompParams.size()); + } + + int getStages(const LLCDStageData** stagesOut) + { + *stagesOut = mDecompStages.data(); + return narrow(mDecompStages.size()); + } + + // Set a parameter by name. Returns false if out of bounds or unsupported parameter + LLCDResult setParam(const char* name, float val); + LLCDResult setParam(const char* name, int val); + LLCDResult setParam(const char* name, bool val); + LLCDResult setMeshData( const LLCDMeshData* data, bool vertex_based ); + LLCDResult registerCallback(int stage, llcdCallbackFunc callback ); + + LLCDResult executeStage(int stage); + LLCDResult buildSingleHull(); + + int getNumHullsFromStage(int stage); + + LLCDResult getHullFromStage( int stage, int hull, LLCDHull* hullOut ); + LLCDResult getSingleHull( LLCDHull* hullOut ) ; + + // TODO: Implement lock of some kind to disallow this call if data not yet ready + LLCDResult getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut); + LLCDResult getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut ); + + // For visualizing convex hull shapes in the viewer physics shape display + LLCDResult generateSingleHullMeshFromMesh( LLCDMeshData* meshIn, LLCDMeshData* meshOut); + + /// Debug + void loadMeshData(const char* fileIn, LLCDMeshData** meshDataOut); + +private: + std::vector mDecompParams; + std::array mDecompStages; + + struct LLVHACDMesh + { + using vertex_type = VHACD::Vertex; + using index_type = VHACD::Triangle; + using vertex_array_type = std::vector; + using index_array_type = std::vector; + + LLVHACDMesh() = default; + LLVHACDMesh(const LLCDHull* hullIn) + { + if (hullIn) + { + from(hullIn); + } + }; + + LLVHACDMesh(const LLCDMeshData* meshIn, bool vertex_based) + { + if (meshIn) + { + from(meshIn, vertex_based); + } + }; + + void clear() + { + mVertices.clear(); + mIndices.clear(); + } + + void setVertices(const float* data, int num_vertices, int vertex_stride_bytes) + { + vertex_array_type vertices; + vertices.reserve(num_vertices); + + const int stride = vertex_stride_bytes / sizeof(float); + for (int i = 0; i < num_vertices; ++i) + { + vertices.emplace_back(data[i * stride + 0], + data[i * stride + 1], + data[i * stride + 2]); + } + + mVertices = std::move(vertices); + } + + void setIndices(const void* data, int num_indices, int index_stride_bytes, LLCDMeshData::IndexType type) + { + index_array_type indices; + indices.reserve(num_indices); + + if (type == LLCDMeshData::INT_16) + { + const U16* index_data = static_cast(data); + const int stride = index_stride_bytes / sizeof(U16); + for (int i = 0; i < num_indices; ++i) + { + indices.emplace_back(index_data[i * stride + 0], + index_data[i * stride + 1], + index_data[i * stride + 2]); + } + } + else + { + const U32* index_data = static_cast(data); + const int stride = index_stride_bytes / sizeof(U32); + for (int i = 0; i < num_indices; ++i) + { + indices.emplace_back(index_data[i * stride + 0], + index_data[i * stride + 1], + index_data[i * stride + 2]); + } + } + + mIndices = std::move(indices); + } + + LLCDResult from(const LLCDHull* hullIn) + { + clear(); + + if (!hullIn || !hullIn->mVertexBase || (hullIn->mNumVertices < 3) || (hullIn->mVertexStrideBytes != 12 && hullIn->mVertexStrideBytes != 16)) + { + return LLCD_INVALID_HULL_DATA; + } + + setVertices(hullIn->mVertexBase, hullIn->mNumVertices, hullIn->mVertexStrideBytes); + + return LLCD_OK; + } + + LLCDResult from(const LLCDMeshData* meshIn, bool vertex_based) + { + clear(); + + if (!meshIn || !meshIn->mVertexBase || (meshIn->mNumVertices < 3) || (meshIn->mVertexStrideBytes != 12 && meshIn->mVertexStrideBytes != 16)) + { + return LLCD_INVALID_MESH_DATA; + } + + if (!vertex_based && ((meshIn->mNumTriangles < 1) || !meshIn->mIndexBase)) + { + return LLCD_INVALID_MESH_DATA; + } + + setVertices(meshIn->mVertexBase, meshIn->mNumVertices, meshIn->mVertexStrideBytes); + if(!vertex_based) + { + setIndices(meshIn->mIndexBase, meshIn->mNumTriangles, meshIn->mIndexStrideBytes, meshIn->mIndexType); + } + + return LLCD_OK; + } + + vertex_array_type mVertices; + index_array_type mIndices; + }; + + struct LLConvexMesh + { + using vertex_type = glm::vec3; + using index_type = glm::u32vec3; + using vertex_array_type = std::vector; + using index_array_type = std::vector; + + LLConvexMesh() = default; + + void clear() + { + vertices.clear(); + indices.clear(); + } + + void setVertices(const std::vector& in_vertices) + { + vertices.clear(); + vertices.reserve(in_vertices.size()); + + for (const auto& vertex : in_vertices) + { + vertices.emplace_back(narrow(vertex.mX), narrow(vertex.mY), narrow(vertex.mZ)); + } + } + + void setIndices(const std::vector& in_indices) + { + indices.clear(); + indices.reserve(in_indices.size()); + + for (const auto& triangle : in_indices) + { + indices.emplace_back(narrow(triangle.mI0), narrow(triangle.mI1), narrow(triangle.mI2)); + } + } + + void to(LLCDHull* meshOut) const + { + meshOut->mVertexBase = (float*)vertices.data(); + meshOut->mVertexStrideBytes = sizeof(vertex_type); + meshOut->mNumVertices = (int)vertices.size(); + } + + void to(LLCDMeshData* meshOut) const + { + meshOut->mVertexBase = (float*)vertices.data(); + meshOut->mVertexStrideBytes = sizeof(vertex_type); + meshOut->mNumVertices = (int)vertices.size(); + + meshOut->mIndexType = LLCDMeshData::INT_32; + meshOut->mIndexBase = indices.data(); + meshOut->mIndexStrideBytes = sizeof(index_type); + meshOut->mNumTriangles = (int)indices.size(); + } + + vertex_array_type vertices; + index_array_type indices; + }; + + struct LLDecompData + { + LLVHACDMesh mSourceMesh; + LLConvexMesh mSingleHullMesh; + + std::vector mDecomposedHulls; + }; + + std::unordered_map mDecompData; + + LLDecompData* mBoundDecomp = nullptr; + + VHACD::IVHACD* mVHACD = nullptr; + VHACDCallback mVHACDCallback; + VHACDLogger mVHACDLogger; + VHACD::IVHACD::Parameters mVHACDParameters; + + LLConvexMesh mMeshFromHullData; + LLConvexMesh mSingleHullMeshFromMeshData; +}; + +#endif //LL_CONVEX_DECOMP_UTIL_VHACD_H diff --git a/indra/llphysicsextensionsos/llpathinglib.cpp b/indra/llphysicsextensionsos/llpathinglib.cpp new file mode 100644 index 0000000000..f41cb9c45f --- /dev/null +++ b/indra/llphysicsextensionsos/llpathinglib.cpp @@ -0,0 +1,83 @@ +/** +* @file llpathinglib.cpp +* @author prep@lindenlab.com +* @brief LLPathingLib core creation methods +* +* $LicenseInfo:firstyear=2012&license=lgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2011, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#include "linden_common.h" + +#include "LLPathingLibStubImpl.h" + +#include "llpathinglib.h" + +//============================================================================= + +/*static */bool LLPathingLib::s_isInitialized = false; + +//============================================================================= + + +/*static*/bool LLPathingLib::isFunctional() +{ + return false; +} + +/*static*/LLPathingLib* LLPathingLib::getInstance() +{ + if ( !s_isInitialized ) + { + return NULL; + } + else + { + return LLPathingLibImpl::getInstance(); + } +} + +//============================================================================= + +/*static */LLPathingLib::LLPLResult LLPathingLib::initSystem() +{ + if ( LLPathingLibImpl::initSystem() == LLPL_OK ) + { + s_isInitialized = true; + return LLPL_OK; + } + return LLPL_UNKOWN_ERROR; +} +//============================================================================= +/*static */LLPathingLib::LLPLResult LLPathingLib::quitSystem() +{ + LLPLResult quitResult = LLPL_UNKOWN_ERROR; + + if (s_isInitialized) + { + quitResult = LLPathingLibImpl::quitSystem(); + s_isInitialized = false; + } + + return quitResult; +} +//============================================================================= + diff --git a/indra/llphysicsextensionsos/llpathinglib.h b/indra/llphysicsextensionsos/llpathinglib.h new file mode 100644 index 0000000000..41583f1fd1 --- /dev/null +++ b/indra/llphysicsextensionsos/llpathinglib.h @@ -0,0 +1,187 @@ +/** + * @file llpathinglib.cpp + * @author prep@lindenlab.com + * @brief LLPathingLib interface definition + * + * $LicenseInfo:firstyear=2012&license=lgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_PATHING_LIBRARY +#define LL_PATHING_LIBRARY + +#include "llpreprocessor.h" +#include "llsd.h" +#include "v3dmath.h" +#include "v4math.h" + +#include "v4color.h" +#include "v4coloru.h" +#include "llphysicsextensions.h" + +typedef int bool32; + +#if defined(_WIN32) || defined(_WIN64) +#define LLCD_CALL __cdecl +#else +#define LLCD_CALL +#endif + +class LLRender; + +//============================================================================= +class LLPathingLib +{ + +public: + enum LLShapeType + { + LLST_WalkableObjects = 0, + LLST_ObstacleObjects, + LLST_MaterialPhantoms, + LLST_ExclusionPhantoms, + LLST_MaxShapeTypes = LLST_ExclusionPhantoms+1, + LLST_None = LLST_MaxShapeTypes+2, + LLST_SimpleBox = LLST_None+1, + LLST_SimpleCapsule = LLST_SimpleBox+1, + }; + + enum LLShapeTypeFlag + { + LLSTB_WalkableObjects = 0x1 << 1, + LLSTB_ObstacleObjects = 0x1 << 2, + LLSTB_MaterialPhantoms = 0x1 << 3, + LLSTB_ExclusionPhantoms = 0x1 << 4, + LLSTB_None = 0x1 << 5 + }; + + enum LLPLPathBookEnd + { + LLPL_START = 0, + LLPL_END, + }; + + enum LLPLResult + { + LLPL_OK = 0, + LLPL_NOTSET, + LLPL_ERROR, + LLPL_NO_NAVMESH, + LLPL_UNKOWN_ERROR, + LLPL_NO_PATH, + LLPL_PATH_GENERATED_OK, + LLPL_NOT_IMPLEMENTED, + }; + + enum LLPLCharacterType + { + LLPL_CHARACTER_TYPE_A = 4, + LLPL_CHARACTER_TYPE_B = 3, + LLPL_CHARACTER_TYPE_C = 2, + LLPL_CHARACTER_TYPE_D = 1, + LLPL_CHARACTER_TYPE_NONE = 0 + }; + + struct PathingPacket + { + PathingPacket() : mHasPointA(false), mHasPointB(false), mCharacterWidth(0.0f), mCharacterType(LLPL_CHARACTER_TYPE_NONE) {} + bool mHasPointA; + LLVector3 mStartPointA; + LLVector3 mEndPointA; + bool mHasPointB; + LLVector3 mStartPointB; + LLVector3 mEndPointB; + F32 mCharacterWidth; + LLPLCharacterType mCharacterType; + }; + + struct NavMeshColors + { + LLColor4U mWalkable; + LLColor4U mObstacle; + LLColor4U mMaterial; + LLColor4U mExclusion; + LLColor4U mConnectedEdge; + LLColor4U mBoundaryEdge; + LLColor4 mHeatColorBase; + LLColor4 mHeatColorMax; + LLColor4U mFaceColor; + LLColor4U mStarValid; + LLColor4U mStarInvalid; + LLColor4U mTestPath; + LLColor4U mWaterColor; + }; + +public: + //Ctor + LLPathingLib() {} + virtual ~LLPathingLib() {} + + /// @returns false if this is the stub + static bool isFunctional(); + + // Obtain a pointer to the actual implementation + static LLPathingLib* getInstance(); + static LLPathingLib::LLPLResult initSystem(); + static LLPathingLib::LLPLResult quitSystem(); + + //Extract and store navmesh data from the llsd datablock sent down by the server + virtual LLPLResult extractNavMeshSrcFromLLSD( const LLSD::Binary& dataBlock, int dir ) = 0; + //Stitch any stored navmeshes together + virtual void processNavMeshData( ) = 0; + + //Method used to generate and visualize a path on the viewers navmesh + virtual LLPLResult generatePath( const PathingPacket& pathingPacket ) = 0; + + //Set the material type for the heatmap type + virtual void setNavMeshMaterialType( LLPLCharacterType materialType ) = 0; + //Set the various navmesh colors + virtual void setNavMeshColors( const NavMeshColors& color ) = 0; + + //The entry method to rendering the client side navmesh + virtual void renderNavMesh() = 0; + //The entry method to rendering the client side navmesh edges + virtual void renderNavMeshEdges() = 0; + //The entry method to render the client navmesh shapes VBO + virtual void renderNavMeshShapesVBO( U32 shapeRenderFlags ) = 0; + //The entry method to render the clients designated path + virtual void renderPath() = 0; + //The entry method to render the capsule bookends for the clients designated path + virtual void renderPathBookend( LLRender& gl, LLPathingLib::LLPLPathBookEnd type ) = 0; + //Renders all of the generated simple shapes (using their default transforms) + virtual void renderSimpleShapes( LLRender& gl, F32 regionsWaterHeight ) = 0; + + //Method called from second life to create a capsule from properties of a character + virtual void createPhysicsCapsuleRep( F32 length, F32 radius, BOOL horizontal, const LLUUID& id ) = 0; + //Removes any cached physics capsule using a list of cached uuids + virtual void cleanupPhysicsCapsuleRepResiduals() = 0; + //Renders a selected uuids physics rep + virtual void renderSimpleShapeCapsuleID( LLRender& gl, const LLUUID& id, const LLVector3& pos, const LLQuaternion& rot ) = 0; + + //Method to delete any vbo's that are currently being managed by the pathing library + virtual void cleanupVBOManager( ) = 0; + //Method to cleanup any allocations within the implementation + virtual void cleanupResidual( ) = 0; +private: + static bool s_isInitialized; +}; + +#endif //LL_PATHING_LIBRARY diff --git a/indra/llphysicsextensionsos/llphysicsextensions.cpp b/indra/llphysicsextensionsos/llphysicsextensions.cpp new file mode 100644 index 0000000000..3bb8ffbf1a --- /dev/null +++ b/indra/llphysicsextensionsos/llphysicsextensions.cpp @@ -0,0 +1,78 @@ +/** +* @file llphysicsextensions.cpp +* @author nyx@lindenlab.com +* @brief LLPhysicsExtensions core initialization methods +* +* $LicenseInfo:firstyear=2012&license=lgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2011, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#include "linden_common.h" + +#include "llphysicsextensions.h" +#include "LLPhysicsExtensionsStubImpl.h" + + +//disable the undefined symbol optimization +//#pragma warning (disable : 4221) + +//============================================================================= + +/*static */bool LLPhysicsExtensions::s_isInitialized = false; + + +/*static*/bool LLPhysicsExtensions::isFunctional() +{ + return false; +} + +//============================================================================= + +/*static*/LLPhysicsExtensions* LLPhysicsExtensions::getInstance() +{ + if ( !s_isInitialized ) + { + return NULL; + } + else + { + return LLPhysicsExtensionsImpl::getInstance(); + } +} + +//============================================================================= + +/*static */bool LLPhysicsExtensions::initSystem() +{ + bool result = LLPhysicsExtensionsImpl::initSystem(); + if ( result ) + { + s_isInitialized = true; + } + return result; +} +//============================================================================= +/*static */bool LLPhysicsExtensions::quitSystem() +{ + return LLPhysicsExtensionsImpl::quitSystem(); +} +//============================================================================= + diff --git a/indra/llphysicsextensionsos/llphysicsextensions.h b/indra/llphysicsextensionsos/llphysicsextensions.h new file mode 100644 index 0000000000..fa23ebd725 --- /dev/null +++ b/indra/llphysicsextensionsos/llphysicsextensions.h @@ -0,0 +1,59 @@ +/** +* @file llphysicsextensions.h +* @author nyx@lindenlab.com +* @brief LLPhysicsExtensions core shared initialization +* routines +* +* $LicenseInfo:firstyear=2012&license=lgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2011, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#ifndef LL_PHYSICS_EXTENSIONS +#define LL_PHYSICS_EXTENSIONS + +#include "llpreprocessor.h" +#include "llsd.h" +#include "v3dmath.h" + +#define LLPHYSICSEXTENSIONS_VERSION "1.0" + +typedef int bool32; + +class LLPhysicsExtensions +{ + +public: + // Obtain a pointer to the actual implementation + static LLPhysicsExtensions* getInstance(); + + /// @returns false if this is the stub + static bool isFunctional(); + + static bool initSystem(); + static bool quitSystem(); + +private: + static bool s_isInitialized; +}; + +#endif //LL_PATHING_LIBRARY + + diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt index 9e90314a51..ff0cad58d6 100644 --- a/indra/llprimitive/CMakeLists.txt +++ b/indra/llprimitive/CMakeLists.txt @@ -65,15 +65,18 @@ target_link_libraries(llprimitive llxml llcharacter llrender - llphysicsextensions_impl ll::colladadom ll::glm ) -if (TARGET llconvexdecomposition) +if (HAVOK OR HAVOK_TPV) target_link_libraries(llprimitive - llconvexdecomposition - ) + llphysicsextensions_impl + ) +else() + target_link_libraries(llprimitive + llphysicsextensionsos + ) endif () #add unit tests diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 4c6c42e7a2..8869f4b1f6 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -50,7 +50,7 @@ include(VulkanGltf) include(ZLIBNG) include(LLPrimitive) -if (NOT HAVOK_TPV) +if (HAVOK) # When using HAVOK_TPV, the library is precompiled, so no need for this # Stub and probably havok lib itself is a hack, autobuild loads a 3p that really is a source tarball @@ -76,7 +76,7 @@ if (NOT HAVOK_TPV) target_compile_options( llphysicsextensions PRIVATE -Wno-unused-local-typedef) endif (DARWIN) endif() -endif (NOT HAVOK_TPV) +endif () set(viewer_SOURCE_FILES gltfscenemanager.cpp @@ -1952,13 +1952,30 @@ elseif (DARWIN) PROPERTIES RESOURCE SecondLife.xib LINK_FLAGS_RELEASE "${LINK_FLAGS_RELEASE} -Xlinker -dead_strip" - # arch specific flags for universal builds: https://stackoverflow.com/a/77942065 - XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=x86_64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_FULL" - XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=arm64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_STUB" - # only generate the .MAP file for llphysicsextensions_tpv on x86_64 - XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=x86_64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensions/$,$,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensions -Xlinker -map -Xlinker ${CMAKE_CURRENT_BINARY_DIR}/${VIEWER_BINARY_NAME}.MAP" - XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=arm64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensionsstub/$,$,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensionsstub" ) + if(HAVOK) + set_target_properties(${VIEWER_BINARY_NAME} + PROPERTIES + # arch specific flags for universal builds: https://stackoverflow.com/a/77942065 + XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=x86_64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_FULL -DLL_HAVOK=1" + XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=arm64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_STUB" + # only generate the .MAP file for llphysicsextensions_tpv on x86_64 + XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=x86_64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensions/$,$,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensions -Xlinker -map -Xlinker ${CMAKE_CURRENT_BINARY_DIR}/${VIEWER_BINARY_NAME}.MAP" + XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=arm64] "$(inherited) -L${CMAKE_BINARY_DIR}/llphysicsextensionsos/$,$,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensionsos" + ) + elseif(HAVOK_TPV) + set_target_properties(${VIEWER_BINARY_NAME} + PROPERTIES + # arch specific flags for universal builds: https://stackoverflow.com/a/77942065 + XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=x86_64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_FULL -DLL_HAVOK=1" + XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=arm64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_STUB" + # only generate the .MAP file for llphysicsextensions_tpv on x86_64 + XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=x86_64] "$(inherited) -L${ARCH_PREBUILT_DIRS}/ -lllphysicsextensions_tpv" + XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=arm64] "$(inherited) -L${CMAKE_BINARY_DIR}/llphysicsextensionsos/$,$,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensionsos" + ) + else() + target_link_libraries(${VIEWER_BINARY_NAME} llphysicsextensionsos) + endif() else (WINDOWS) # Linux set_target_properties(${VIEWER_BINARY_NAME} -- cgit v1.2.3 From 03690724188cb3fef1c2efed0f5626c93096ec2d Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Mon, 20 Oct 2025 19:57:25 +0300 Subject: #4861 Crash at LLVolumeFace::createOctree Looks like LLRiggedVolume::update goes out of bounds when selecting a face --- indra/llappearance/llpolymesh.cpp | 1 + indra/newview/llvovolume.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/indra/llappearance/llpolymesh.cpp b/indra/llappearance/llpolymesh.cpp index 719381b4fc..d5323e0b84 100644 --- a/indra/llappearance/llpolymesh.cpp +++ b/indra/llappearance/llpolymesh.cpp @@ -283,6 +283,7 @@ bool LLPolyMeshSharedData::loadMesh( const std::string& fileName ) LLFILE* fp = LLFile::fopen(fileName, "rb"); /*Flawfinder: ignore*/ if (!fp) { + LLError::LLUserWarningMsg::showMissingFiles(); LL_ERRS() << "can't open: " << fileName << LL_ENDL; return false; } diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 2370b948ef..aa230f4636 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -5025,7 +5025,7 @@ void LLRiggedVolume::update( else { face_begin = face_index; - face_end = face_begin + 1; + face_end = llmin(face_begin + 1, volume->getNumVolumeFaces()); } for (S32 i = face_begin; i < face_end; ++i) { -- cgit v1.2.3 From 53041adc3d0c257ea44f23071f2ba383c1639acc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Oct 2025 13:32:36 -0700 Subject: Bump pre-commit/action from 3.0.0 to 3.0.1 (#4864) Bumps [pre-commit/action](https://github.com/pre-commit/action) from 3.0.0 to 3.0.1. - [Release notes](https://github.com/pre-commit/action/releases) - [Commits](https://github.com/pre-commit/action/compare/v3.0.0...v3.0.1) --- updated-dependencies: - dependency-name: pre-commit/action dependency-version: 3.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pre-commit.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index d626eef38d..9391677c15 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -15,4 +15,4 @@ jobs: - uses: actions/setup-python@v4 with: python-version: 3.x - - uses: pre-commit/action@v3.0.0 + - uses: pre-commit/action@v3.0.1 -- cgit v1.2.3 From 6f1df1a8d2db537b4962a8ef1118119db849b336 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Oct 2025 13:32:50 -0700 Subject: Bump actions/checkout from 4 to 5 (#4867) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yaml | 6 +++--- .github/workflows/pre-commit.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 5a5628ede2..4c948e5586 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -86,7 +86,7 @@ jobs: variants: ${{ matrix.configuration }} steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} @@ -95,14 +95,14 @@ jobs: with: python-version: "3.11" - name: Checkout build variables - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: repository: secondlife/build-variables ref: master path: .build-variables - name: Checkout master-message-template - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: repository: secondlife/master-message-template path: .master-message-template diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 9391677c15..726e1cd889 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -11,7 +11,7 @@ jobs: pre-commit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-python@v4 with: python-version: 3.x -- cgit v1.2.3 From e12958161c6f4edec1300db2045bba97aff26048 Mon Sep 17 00:00:00 2001 From: RolfKal Date: Wed, 15 Oct 2025 01:00:42 +0200 Subject: Improve LLFile to be consistent between Windows and Linux/Mac --- indra/llcommon/llfile.cpp | 352 ++++++++++++++++++++++------------ indra/llcommon/llfile.h | 90 +++++++-- indra/llcommon/llsys.cpp | 8 - indra/llcrashlogger/llcrashlogger.cpp | 4 - indra/llfilesystem/llfilesystem.cpp | 3 - indra/llui/llviewereventrecorder.cpp | 2 - indra/newview/llvoicevivox.cpp | 1 - 7 files changed, 302 insertions(+), 158 deletions(-) diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index 53659ac13f..a24524d699 100644 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -35,7 +35,6 @@ #if LL_WINDOWS #include "llwin32headers.h" -#include // Windows errno #include #else #include @@ -49,6 +48,86 @@ static std::string empty; // variants of strerror() to report errors. #if LL_WINDOWS +namespace +{ + struct errentry + { + unsigned long oserr; // OS return value + int errcode; // System V error code + }; +} + +static errentry const errtable[] +{ + { ERROR_INVALID_FUNCTION, EINVAL }, // 1 + { ERROR_FILE_NOT_FOUND, ENOENT }, // 2 + { ERROR_PATH_NOT_FOUND, ENOENT }, // 3 + { ERROR_TOO_MANY_OPEN_FILES, EMFILE }, // 4 + { ERROR_ACCESS_DENIED, EACCES }, // 5 + { ERROR_INVALID_HANDLE, EBADF }, // 6 + { ERROR_ARENA_TRASHED, ENOMEM }, // 7 + { ERROR_NOT_ENOUGH_MEMORY, ENOMEM }, // 8 + { ERROR_INVALID_BLOCK, ENOMEM }, // 9 + { ERROR_BAD_ENVIRONMENT, E2BIG }, // 10 + { ERROR_BAD_FORMAT, ENOEXEC }, // 11 + { ERROR_INVALID_ACCESS, EINVAL }, // 12 + { ERROR_INVALID_DATA, EINVAL }, // 13 + { ERROR_INVALID_DRIVE, ENOENT }, // 15 + { ERROR_CURRENT_DIRECTORY, EACCES }, // 16 + { ERROR_NOT_SAME_DEVICE, EXDEV }, // 17 + { ERROR_NO_MORE_FILES, ENOENT }, // 18 + { ERROR_LOCK_VIOLATION, EACCES }, // 33 + { ERROR_BAD_NETPATH, ENOENT }, // 53 + { ERROR_NETWORK_ACCESS_DENIED, EACCES }, // 65 + { ERROR_BAD_NET_NAME, ENOENT }, // 67 + { ERROR_FILE_EXISTS, EEXIST }, // 80 + { ERROR_CANNOT_MAKE, EACCES }, // 82 + { ERROR_FAIL_I24, EACCES }, // 83 + { ERROR_INVALID_PARAMETER, EINVAL }, // 87 + { ERROR_NO_PROC_SLOTS, EAGAIN }, // 89 + { ERROR_DRIVE_LOCKED, EACCES }, // 108 + { ERROR_BROKEN_PIPE, EPIPE }, // 109 + { ERROR_DISK_FULL, ENOSPC }, // 112 + { ERROR_INVALID_TARGET_HANDLE, EBADF }, // 114 + { ERROR_WAIT_NO_CHILDREN, ECHILD }, // 128 + { ERROR_CHILD_NOT_COMPLETE, ECHILD }, // 129 + { ERROR_DIRECT_ACCESS_HANDLE, EBADF }, // 130 + { ERROR_NEGATIVE_SEEK, EINVAL }, // 131 + { ERROR_SEEK_ON_DEVICE, EACCES }, // 132 + { ERROR_DIR_NOT_EMPTY, ENOTEMPTY }, // 145 + { ERROR_NOT_LOCKED, EACCES }, // 158 + { ERROR_BAD_PATHNAME, ENOENT }, // 161 + { ERROR_MAX_THRDS_REACHED, EAGAIN }, // 164 + { ERROR_LOCK_FAILED, EACCES }, // 167 + { ERROR_ALREADY_EXISTS, EEXIST }, // 183 + { ERROR_FILENAME_EXCED_RANGE, ENOENT }, // 206 + { ERROR_NESTING_NOT_ALLOWED, EAGAIN }, // 215 + { ERROR_NO_UNICODE_TRANSLATION, EILSEQ }, // 1113 + { ERROR_NOT_ENOUGH_QUOTA, ENOMEM } // 1816 +}; + +// Number of elements in the error translation table +#define ERRTABLECOUNT (sizeof(errtable) / sizeof(errtable[0])) + +static int set_errno_from_oserror(unsigned long oserr) +{ + if (!oserr) + return 0; + + // Check the table for the OS error code + for (unsigned i{0}; i < ERRTABLECOUNT; ++i) + { + if (oserr == errtable[i].oserr) + { + _set_errno(errtable[i].errcode); + return -1; + } + } + + _set_errno(EINVAL); + return -1; +} + // On Windows, use strerror_s(). std::string strerr(int errn) { @@ -57,7 +136,61 @@ std::string strerr(int errn) return buffer; } -typedef std::basic_ios > _Myios; +inline bool is_slash(wchar_t const c) +{ + return c == L'\\' || c == L'/'; +} + +static std::wstring utf8path_to_wstring(const std::string& utf8path) +{ + if (utf8path.size() >= MAX_PATH) + { + // By prepending "\\?\" to a path, Windows widechar file APIs will not fail on long path names + std::wstring utf16path = L"\\\\?\\" + ll_convert(utf8path); + // We need to make sure that the path does not contain forward slashes as above + // prefix does bypass the path normalization that replaces slashes with backslashes + // before passing the path to kernel mode APIs + std::replace(utf16path.begin(), utf16path.end(), L'/', L'\\'); + return utf16path; + } + return ll_convert(utf8path); +} + +static unsigned short get_fileattr(const std::wstring& utf16path) +{ + unsigned short st_mode = 0; + HANDLE file_handle = CreateFileW(utf16path.c_str(), FILE_READ_ATTRIBUTES, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + if (file_handle == INVALID_HANDLE_VALUE) + { + set_errno_from_oserror(GetLastError()); + return 0; + } + + FILE_ATTRIBUTE_TAG_INFO attribute_info; + if (GetFileInformationByHandleEx(file_handle, FileAttributeTagInfo, &attribute_info, sizeof(attribute_info))) + { + // A volume path alone (only drive letter) is not recognized as directory while it technically is + bool is_directory = (attribute_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) || + (iswalpha(utf16path[0]) && utf16path[1] == ':' && + (!utf16path[2] || (is_slash(utf16path[2]) && !utf16path[3]))); + st_mode |= is_directory ? S_IFDIR : S_IFREG; + st_mode |= (attribute_info.FileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE; + // we do not try to guess executable flag + + // propagate user bits to group/other fields: + st_mode |= (st_mode & 0700) >> 3; + st_mode |= (st_mode & 0700) >> 6; + } + else + { + // Retrieve last error before calling CloseHandle() + set_errno_from_oserror(GetLastError()); + } + CloseHandle(file_handle); + return st_mode; +} #else // On Posix we want to call strerror_r(), but alarmingly, there are two @@ -108,18 +241,13 @@ std::string strerr(int errn) } #endif // ! LL_WINDOWS -// On either system, shorthand call just infers global 'errno'. -std::string strerr() -{ - return strerr(errno); -} - -int warnif(const std::string& desc, const std::string& filename, int rc, int accept=0) +int warnif(const std::string& desc, const std::string& filename, int rc, int accept = 0) { if (rc < 0) { // Capture errno before we start emitting output int errn = errno; + // For certain operations, a particular errno value might be // acceptable -- e.g. stat() could permit ENOENT, mkdir() could permit // EEXIST. Don't warn if caller explicitly says this errno is okay. @@ -176,62 +304,58 @@ int warnif(const std::string& desc, const std::string& filename, int rc, int acc // static int LLFile::mkdir(const std::string& dirname, int perms) { + // We often use mkdir() to ensure the existence of a directory that might + // already exist. There is no known case in which we want to call out as + // an error the requested directory already existing. #if LL_WINDOWS // permissions are ignored on Windows - std::wstring utf16dirname = ll_convert(dirname); - int rc = _wmkdir(utf16dirname.c_str()); + int rc = 0; + std::wstring utf16dirname = utf8path_to_wstring(dirname); + if (!CreateDirectoryW(utf16dirname.c_str(), nullptr)) + { + // Only treat other errors than an already existing file as a real error + unsigned long oserr = GetLastError(); + if (oserr != ERROR_ALREADY_EXISTS) + { + rc = set_errno_from_oserror(oserr); + } + } #else int rc = ::mkdir(dirname.c_str(), (mode_t)perms); -#endif - // We often use mkdir() to ensure the existence of a directory that might - // already exist. There is no known case in which we want to call out as - // an error the requested directory already existing. if (rc < 0 && errno == EEXIST) { // this is not the error you want, move along return 0; } +#endif // anything else might be a problem - return warnif("mkdir", dirname, rc, EEXIST); + return warnif("mkdir", dirname, rc); } // static -int LLFile::rmdir(const std::string& dirname) +int LLFile::rmdir(const std::string& dirname, int supress_error) { #if LL_WINDOWS - // permissions are ignored on Windows - std::wstring utf16dirname = ll_convert(dirname); + std::wstring utf16dirname = utf8path_to_wstring(dirname); int rc = _wrmdir(utf16dirname.c_str()); #else int rc = ::rmdir(dirname.c_str()); #endif - return warnif("rmdir", dirname, rc); + return warnif("rmdir", dirname, rc, supress_error); } // static LLFILE* LLFile::fopen(const std::string& filename, const char* mode) /* Flawfinder: ignore */ { #if LL_WINDOWS - std::wstring utf16filename = ll_convert(filename); + std::wstring utf16filename = utf8path_to_wstring(filename); std::wstring utf16mode = ll_convert(std::string(mode)); - return _wfopen(utf16filename.c_str(),utf16mode.c_str()); + return _wfopen(utf16filename.c_str(), utf16mode.c_str()); #else return ::fopen(filename.c_str(),mode); /* Flawfinder: ignore */ #endif } -LLFILE* LLFile::_fsopen(const std::string& filename, const char* mode, int sharingFlag) -{ -#if LL_WINDOWS - std::wstring utf16filename = ll_convert(filename); - std::wstring utf16mode = ll_convert(std::string(mode)); - return _wfsopen(utf16filename.c_str(),utf16mode.c_str(),sharingFlag); -#else - llassert(0);//No corresponding function on non-windows - return NULL; -#endif -} - int LLFile::close(LLFILE * file) { int ret_value = 0; @@ -264,8 +388,27 @@ std::string LLFile::getContents(const std::string& filename) int LLFile::remove(const std::string& filename, int supress_error) { #if LL_WINDOWS - std::wstring utf16filename = ll_convert(filename); - int rc = _wremove(utf16filename.c_str()); + // Posix remove() works on both files and directories although on Windows + // remove() and its wide char variant _wremove() only removes files just + // as its siblings unlink() and _wunlink(). + // If we really only want to support files we should instead use + // unlink() in the non-Windows part below too + int rc = 0; + ; + std::wstring utf16filename = utf8path_to_wstring(filename); + unsigned short st_mode = get_fileattr(utf16filename); + if (S_ISDIR(st_mode)) + { + rc = _wrmdir(utf16filename.c_str()); + } + else if (S_ISREG(st_mode)) + { + rc = _wunlink(utf16filename.c_str()); + } + else + { + rc = set_errno_from_oserror(ERROR_INVALID_PARAMETER); + } #else int rc = ::remove(filename.c_str()); #endif @@ -275,28 +418,38 @@ int LLFile::remove(const std::string& filename, int supress_error) int LLFile::rename(const std::string& filename, const std::string& newname, int supress_error) { #if LL_WINDOWS - std::wstring utf16filename = ll_convert(filename); - std::wstring utf16newname = ll_convert(newname); - int rc = _wrename(utf16filename.c_str(),utf16newname.c_str()); + // Posix rename() will gladly overwrite a file at newname if it exists, the Windows + // rename(), respectively _wrename(), will bark on that. Instead call directly the Windows + // API MoveFileEx() and use its flags to specify that overwrite is allowed. + std::wstring utf16filename = utf8path_to_wstring(filename); + std::wstring utf16newname = utf8path_to_wstring(newname); + int rc = 0; + if (!MoveFileExW(utf16filename.c_str(), utf16newname.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED)) + { + rc = set_errno_from_oserror(GetLastError()); + } #else int rc = ::rename(filename.c_str(),newname.c_str()); #endif return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, supress_error); } +// Make this a define rather than using magic numbers multiple times in the code +#define LLFILE_COPY_BUFFER_SIZE 16384 + bool LLFile::copy(const std::string& from, const std::string& to) { bool copied = false; - LLFILE* in = LLFile::fopen(from, "rb"); /* Flawfinder: ignore */ + LLFILE* in = fopen(from, "rb"); /* Flawfinder: ignore */ if (in) { - LLFILE* out = LLFile::fopen(to, "wb"); /* Flawfinder: ignore */ + LLFILE* out = fopen(to, "wb"); /* Flawfinder: ignore */ if (out) { - char buf[16384]; /* Flawfinder: ignore */ + char buf[LLFILE_COPY_BUFFER_SIZE]; /* Flawfinder: ignore */ size_t readbytes; bool write_ok = true; - while(write_ok && (readbytes = fread(buf, 1, 16384, in))) /* Flawfinder: ignore */ + while (write_ok && (readbytes = fread(buf, 1, LLFILE_COPY_BUFFER_SIZE, in))) /* Flawfinder: ignore */ { if (fwrite(buf, 1, readbytes, out) != readbytes) { @@ -315,31 +468,51 @@ bool LLFile::copy(const std::string& from, const std::string& to) return copied; } -int LLFile::stat(const std::string& filename, llstat* filestatus) +int LLFile::stat(const std::string& filename, llstat* filestatus, int supress_error) { #if LL_WINDOWS - std::wstring utf16filename = ll_convert(filename); - int rc = _wstat(utf16filename.c_str(),filestatus); + std::wstring utf16filename = utf8path_to_wstring(filename); + int rc = _wstat64(utf16filename.c_str(), filestatus); #else - int rc = ::stat(filename.c_str(),filestatus); + int rc = ::stat(filename.c_str(), filestatus); #endif - // We use stat() to determine existence (see isfile(), isdir()). - // Don't spam the log if the subject pathname doesn't exist. - return warnif("stat", filename, rc, ENOENT); + return warnif("stat", filename, rc, supress_error); } -bool LLFile::isdir(const std::string& filename) +// _wstat() is a bit heavyweight on Windows, use a more lightweight API +// to just get the attributes +unsigned short LLFile::getattr(const std::string& filename, int suppress_error) { - llstat st; +#if LL_WINDOWS + int rc = -1; + std::wstring utf16filename = utf8path_to_wstring(filename); + unsigned short st_mode = get_fileattr(utf16filename); + if (st_mode) + { + return st_mode; + } +#else + llstat filestatus; + int rc = ::stat(filename.c_str(), &filestatus); + if (rc == 0) + { + return filestatus.st_mode; + } +#endif + warnif("getattr", filename, rc, suppress_error); + return 0; +} - return stat(filename, &st) == 0 && S_ISDIR(st.st_mode); +bool LLFile::isdir(const std::string& filename) +{ + // Don't spam the log if the subject pathname doesn't exist. + return S_ISDIR(getattr(filename, ENOENT)); } bool LLFile::isfile(const std::string& filename) { - llstat st; - - return stat(filename, &st) == 0 && S_ISREG(st.st_mode); + // Don't spam the log if the subject pathname doesn't exist. + return S_ISREG(getattr(filename, ENOENT)); } const char *LLFile::tmpdir() @@ -368,75 +541,8 @@ const char *LLFile::tmpdir() return utf8path.c_str(); } - -/***************** Modified file stream created to overcome the incorrect behaviour of posix fopen in windows *******************/ - #if LL_WINDOWS -LLFILE * LLFile::_Fiopen(const std::string& filename, - std::ios::openmode mode) -{ // open a file - static const char *mods[] = - { // fopen mode strings corresponding to valid[i] - "r", "w", "w", "a", "rb", "wb", "wb", "ab", - "r+", "w+", "a+", "r+b", "w+b", "a+b", - 0}; - static const int valid[] = - { // valid combinations of open flags - ios_base::in, - ios_base::out, - ios_base::out | ios_base::trunc, - ios_base::out | ios_base::app, - ios_base::in | ios_base::binary, - ios_base::out | ios_base::binary, - ios_base::out | ios_base::trunc | ios_base::binary, - ios_base::out | ios_base::app | ios_base::binary, - ios_base::in | ios_base::out, - ios_base::in | ios_base::out | ios_base::trunc, - ios_base::in | ios_base::out | ios_base::app, - ios_base::in | ios_base::out | ios_base::binary, - ios_base::in | ios_base::out | ios_base::trunc - | ios_base::binary, - ios_base::in | ios_base::out | ios_base::app - | ios_base::binary, - 0}; - - LLFILE *fp = 0; - int n; - ios_base::openmode atendflag = mode & ios_base::ate; - ios_base::openmode norepflag = mode & ios_base::_Noreplace; - - if (mode & ios_base::_Nocreate) - mode |= ios_base::in; // file must exist - mode &= ~(ios_base::ate | ios_base::_Nocreate | ios_base::_Noreplace); - for (n = 0; valid[n] != 0 && valid[n] != mode; ++n) - ; // look for a valid mode - - if (valid[n] == 0) - return (0); // no valid mode - else if (norepflag && mode & (ios_base::out | ios_base::app) - && (fp = LLFile::fopen(filename, "r")) != 0) /* Flawfinder: ignore */ - { // file must not exist, close and fail - fclose(fp); - return (0); - } - else if (fp != 0 && fclose(fp) != 0) - return (0); // can't close after test open -// should open with protection here, if other than default - else if ((fp = LLFile::fopen(filename, mods[n])) == 0) /* Flawfinder: ignore */ - return (0); // open failed - - if (!atendflag || fseek(fp, 0, SEEK_END) == 0) - return (fp); // no need to seek to end, or seek succeeded - - fclose(fp); // can't position at end - return (0); -} - -#endif /* LL_WINDOWS */ - - -#if LL_WINDOWS /************** input file stream ********************************/ llifstream::llifstream() {} diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index 1661cbeb55..0bd6be7f94 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -41,8 +41,8 @@ typedef FILE LLFILE; #include #if LL_WINDOWS -// windows version of stat function and stat data structure are called _stat -typedef struct _stat llstat; +// windows version of stat function and stat data structure are called _statxx +typedef struct _stat64 llstat; #else typedef struct stat llstat; #include @@ -58,33 +58,89 @@ typedef struct stat llstat; #include "llstring.h" // safe char* -> std::string conversion +/// LLFile is a class of static functions operating on paths +/// All the functions with a path string input take UTF8 path/filenames class LL_COMMON_API LLFile { public: - // All these functions take UTF8 path/filenames. - static LLFILE* fopen(const std::string& filename,const char* accessmode); /* Flawfinder: ignore */ - static LLFILE* _fsopen(const std::string& filename,const char* accessmode,int sharingFlag); + /// open a file with the specified access mode + static LLFILE* fopen(const std::string& filename, const char* accessmode); /* Flawfinder: ignore */ + ///< 'accessmode' follows the rules of the Posix fopen() mode parameter + /// "r" open the file for reading only and positions the stream at the beginning + /// "r+" open the file for reading and writing and positions the stream at the beginning + /// "w" open the file for reading and writing and truncate it to zero length + /// "w+" open or create the file for reading and writing and truncate to zero length if it existed + /// "a" open the file for reading and writing and position the stream at the end of the file + /// "a+" open or create the file for reading and writing and position the stream at the end of the file + /// + /// in addition to these values, "b" can be appended to indicate binary stream access, but on Linux and Mac + /// this is strictly for compatibility and has no effect. On Windows this makes the file functions not + /// try to translate line endings. Windows also allows to append "t" to indicate text mode. If neither + /// "b" or "t" is defined, Windows uses the value set by _fmode which by default is _O_TEXT. + /// This means that it is always a good idea to append "b" specifically for binary file access to + /// avoid corruption of the binary consistency of the data stream when reading or writing + /// Other characters in 'accessmode' will usually cause an error as fopen will verify this parameter + /// @returns a valid LLFILE* pointer on success or NULL on failure static int close(LLFILE * file); + /// retrieve the content of a file into a string static std::string getContents(const std::string& filename); + ///< @returns the content of the file or an empty string on failure - // perms is a permissions mask like 0777 or 0700. In most cases it will - // be overridden by the user's umask. It is ignored on Windows. - // mkdir() considers "directory already exists" to be SUCCESS. + /// create a directory static int mkdir(const std::string& filename, int perms = 0700); - - static int rmdir(const std::string& filename); + ///< perms is a permissions mask like 0777 or 0700. In most cases it will be + /// overridden by the user's umask. It is ignored on Windows. + /// mkdir() considers "directory already exists" to be not an error. + /// @returns 0 on success and -1 on failure. + + //// remove a directory + static int rmdir(const std::string& filename, int supress_error = 0); + ///< pass ENOENT in the optional 'supress_error' parameter + /// if you don't want a warning in the log when the directory does not exist + /// @returns 0 on success and -1 on failure. + + /// remove a file or directory static int remove(const std::string& filename, int supress_error = 0); - static int rename(const std::string& filename,const std::string& newname, int supress_error = 0); - static bool copy(const std::string& from, const std::string& to); + ///< pass ENOENT in the optional 'supress_error' parameter + /// if you don't want a warning in the log when the directory does not exist + /// @returns 0 on success and -1 on failure. - static int stat(const std::string& filename,llstat* file_status); - static bool isdir(const std::string& filename); - static bool isfile(const std::string& filename); - static LLFILE * _Fiopen(const std::string& filename, - std::ios::openmode mode); + /// rename a file, + static int rename(const std::string& filename, const std::string& newname, int supress_error = 0); + ///< it will silently overwrite newname if it exists without returning an error + /// @returns 0 on success and -1 on failure. + + // copy the contents of file from 'from' to 'to' filename + static bool copy(const std::string& from, const std::string& to); + ///< @returns true on success and false on failure. + + /// return the file stat structure for filename + static int stat(const std::string& filename, llstat* file_status, int supress_error = ENOENT); + ///< for compatibility with existing uses of LL_File::stat() we use ENOENT as default in the + /// optional 'supress_error' parameter to avoid spamming the log with warnings when the API + /// is used to detect if a file exists + /// @returns 0 on success and -1 on failure. + + /// get the file or directory attributes for filename + static unsigned short getattr(const std::string& filename, int supress_error = 0); + ///< a more lightweight function on Windows to stat, that just returns the file attribute flags + /// pass ENOENT in the optional 'supress_error' parameter if you don't want a warning + /// in the log when the file or directory does not exist + /// @returns 0 on failure and a st_mode value with either S_IFDIR or S_IFREG set otherwise + /// together with the three access bits which under Windows only the write bit is relevant. + + /// check if filename is an existing directory + static bool isdir(const std::string& filename); + ///< @returns true if the path is for an existing directory + + /// check if filename is an existing file + static bool isfile(const std::string& filename); + ///< @returns true if the path is for an existing file + + /// return a path to the temporary directory on the system static const char * tmpdir(); }; diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 21b11c5311..270ca40086 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -1348,10 +1348,6 @@ bool gunzip_file(const std::string& srcfile, const std::string& dstfile) } while(gzeof(src) == 0); fclose(dst); dst = NULL; -#if LL_WINDOWS - // Rename in windows needs the dstfile to not exist. - LLFile::remove(dstfile, ENOENT); -#endif if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */ retval = true; err: @@ -1399,10 +1395,6 @@ bool gzip_file(const std::string& srcfile, const std::string& dstfile) gzclose(dst); dst = NULL; -#if LL_WINDOWS - // Rename in windows needs the dstfile to not exist. - LLFile::remove(dstfile); -#endif if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */ retval = true; err: diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp index b1b698375a..d5c585b45e 100644 --- a/indra/llcrashlogger/llcrashlogger.cpp +++ b/indra/llcrashlogger/llcrashlogger.cpp @@ -576,10 +576,6 @@ bool LLCrashLogger::init() std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log.old"); std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log"); -#if LL_WINDOWS - LLAPRFile::remove(old_log_file); -#endif - LLFile::rename(log_file.c_str(), old_log_file.c_str()); // Set the log file to crashreport.log diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp index 5ce5244107..541266af4f 100644 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -103,9 +103,6 @@ bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::ETyp const std::string old_filename = LLDiskCache::metaDataToFilepath(old_file_id, old_file_type); const std::string new_filename = LLDiskCache::metaDataToFilepath(new_file_id, new_file_type); - // Rename needs the new file to not exist. - LLFileSystem::removeFile(new_file_id, new_file_type, ENOENT); - if (LLFile::rename(old_filename, new_filename) != 0) { // We would like to return false here indicating the operation diff --git a/indra/llui/llviewereventrecorder.cpp b/indra/llui/llviewereventrecorder.cpp index 6d907d7e45..9eefba1390 100644 --- a/indra/llui/llviewereventrecorder.cpp +++ b/indra/llui/llviewereventrecorder.cpp @@ -34,8 +34,6 @@ LLViewerEventRecorder::LLViewerEventRecorder() { logEvents = false; // Remove any previous event log file std::string old_log_ui_events_to_llsd_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.old"); - LLFile::remove(old_log_ui_events_to_llsd_file, ENOENT); - mLogFilename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.llsd"); LLFile::rename(mLogFilename, old_log_ui_events_to_llsd_file, ENOENT); diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 6d29038a23..8792ae3285 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -1014,7 +1014,6 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon() std::string old_log = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SLVoice.old"); if (gDirUtilp->fileExists(new_log)) { - LLFile::remove(old_log, ENOENT); LLFile::rename(new_log, old_log); } -- cgit v1.2.3 From 49a850ccc14de253f9645427594f25c74931e4c5 Mon Sep 17 00:00:00 2001 From: RolfKal Date: Wed, 15 Oct 2025 20:00:27 +0200 Subject: Remove a trailing space and a missed LLFile::remove() call right before the LLFile::rename() --- indra/llcommon/llfile.h | 2 +- indra/newview/llappviewer.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index 0bd6be7f94..9d9c3cd08b 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -107,7 +107,7 @@ public: /// if you don't want a warning in the log when the directory does not exist /// @returns 0 on success and -1 on failure. - /// rename a file, + /// rename a file static int rename(const std::string& filename, const std::string& newname, int supress_error = 0); ///< it will silently overwrite newname if it exists without returning an error /// @returns 0 on success and -1 on failure. diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index eadc5e7e63..bfd8b1dcc7 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2398,7 +2398,6 @@ void LLAppViewer::initLoggingAndGetLastDuration() if (gDirUtilp->fileExists(user_data_path_cef_log)) { std::string user_data_path_cef_old = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "cef.old"); - LLFile::remove(user_data_path_cef_old, ENOENT); LLFile::rename(user_data_path_cef_log, user_data_path_cef_old); } } -- cgit v1.2.3 From 2adf1bbdcb64a86614cbe565636fe9203eaa7994 Mon Sep 17 00:00:00 2001 From: RolfKal Date: Thu, 16 Oct 2025 22:56:21 +0200 Subject: Remove trailing space --- indra/llcommon/llfile.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index 9d9c3cd08b..9747bb67d8 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -135,7 +135,7 @@ public: /// check if filename is an existing directory static bool isdir(const std::string& filename); ///< @returns true if the path is for an existing directory - + /// check if filename is an existing file static bool isfile(const std::string& filename); ///< @returns true if the path is for an existing file -- cgit v1.2.3 From f58a7c767354705962788864274c7a60ed89544e Mon Sep 17 00:00:00 2001 From: Frederick Martian Date: Sat, 18 Oct 2025 19:13:19 +0200 Subject: Make changes according to recommendations by Copilot - correction spelling of suppress_error - improved error handling in remove() based on functionality in get_fileattr() and somewhat changed error handling in get_fileattr() itself - call explicitly LLFile::fopen() to make sure we use the correct file path conversion under Windows Removing Flawfinder comments since Flawfinder isn't used in the viewer anymore Adding an option to support symlink detection in getattr() Adding comments to function implementation to indicate that they are really static functions of the LLFile class --- indra/llcommon/llfile.cpp | 154 +++++++++++++++++++++++++++------------------- indra/llcommon/llfile.h | 46 ++++++++++---- 2 files changed, 123 insertions(+), 77 deletions(-) diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index a24524d699..492c7c690b 100644 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -48,15 +48,18 @@ static std::string empty; // variants of strerror() to report errors. #if LL_WINDOWS +// For the situations where we directly call into Windows API functions we need to translate +// the Windows error codes into errno values namespace { struct errentry { - unsigned long oserr; // OS return value + unsigned long oserr; // Windows OS error value int errcode; // System V error code }; } +// translation table between Windows OS error value and System V errno code static errentry const errtable[] { { ERROR_INVALID_FUNCTION, EINVAL }, // 1 @@ -106,20 +109,17 @@ static errentry const errtable[] { ERROR_NOT_ENOUGH_QUOTA, ENOMEM } // 1816 }; -// Number of elements in the error translation table -#define ERRTABLECOUNT (sizeof(errtable) / sizeof(errtable[0])) - static int set_errno_from_oserror(unsigned long oserr) { if (!oserr) return 0; - // Check the table for the OS error code - for (unsigned i{0}; i < ERRTABLECOUNT; ++i) + // Check the table for the Windows OS error code + for (const struct errentry &entry : errtable) { - if (oserr == errtable[i].oserr) + if (oserr == entry.oserr) { - _set_errno(errtable[i].errcode); + _set_errno(entry.errcode); return -1; } } @@ -156,40 +156,46 @@ static std::wstring utf8path_to_wstring(const std::string& utf8path) return ll_convert(utf8path); } -static unsigned short get_fileattr(const std::wstring& utf16path) +static unsigned short get_fileattr(const std::wstring& utf16path, bool dontFollowSymLink = false) { - unsigned short st_mode = 0; - HANDLE file_handle = CreateFileW(utf16path.c_str(), FILE_READ_ATTRIBUTES, - FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); - if (file_handle == INVALID_HANDLE_VALUE) + unsigned long flags = FILE_FLAG_BACKUP_SEMANTICS; + if (dontFollowSymLink) { - set_errno_from_oserror(GetLastError()); - return 0; + flags |= FILE_FLAG_OPEN_REPARSE_POINT; } - - FILE_ATTRIBUTE_TAG_INFO attribute_info; - if (GetFileInformationByHandleEx(file_handle, FileAttributeTagInfo, &attribute_info, sizeof(attribute_info))) + HANDLE file_handle = CreateFileW(utf16path.c_str(), FILE_READ_ATTRIBUTES, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, OPEN_EXISTING, flags, nullptr); + if (file_handle != INVALID_HANDLE_VALUE) { - // A volume path alone (only drive letter) is not recognized as directory while it technically is - bool is_directory = (attribute_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) || - (iswalpha(utf16path[0]) && utf16path[1] == ':' && - (!utf16path[2] || (is_slash(utf16path[2]) && !utf16path[3]))); - st_mode |= is_directory ? S_IFDIR : S_IFREG; - st_mode |= (attribute_info.FileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE; - // we do not try to guess executable flag - - // propagate user bits to group/other fields: - st_mode |= (st_mode & 0700) >> 3; - st_mode |= (st_mode & 0700) >> 6; + FILE_ATTRIBUTE_TAG_INFO attribute_info; + if (GetFileInformationByHandleEx(file_handle, FileAttributeTagInfo, &attribute_info, sizeof(attribute_info))) + { + // A volume path alone (only drive letter) is not recognized as directory while it technically is + bool is_directory = (attribute_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) || + (iswalpha(utf16path[0]) && utf16path[1] == ':' && + (!utf16path[2] || (is_slash(utf16path[2]) && !utf16path[3]))); + unsigned short st_mode = is_directory ? S_IFDIR : + (attribute_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ? S_IFLNK : S_IFREG); + st_mode |= (attribute_info.FileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE; + // we do not try to guess executable flag + + // propagate user bits to group/other fields: + st_mode |= (st_mode & 0700) >> 3; + st_mode |= (st_mode & 0700) >> 6; + + CloseHandle(file_handle); + return st_mode; + } } - else + // Retrieve last error and set errno before calling CloseHandle() + set_errno_from_oserror(GetLastError()); + + if (file_handle != INVALID_HANDLE_VALUE) { - // Retrieve last error before calling CloseHandle() - set_errno_from_oserror(GetLastError()); + CloseHandle(file_handle); } - CloseHandle(file_handle); - return st_mode; + return 0; } #else @@ -241,7 +247,7 @@ std::string strerr(int errn) } #endif // ! LL_WINDOWS -int warnif(const std::string& desc, const std::string& filename, int rc, int accept = 0) +static int warnif(const std::string& desc, const std::string& filename, int rc, int accept = 0) { if (rc < 0) { @@ -333,7 +339,7 @@ int LLFile::mkdir(const std::string& dirname, int perms) } // static -int LLFile::rmdir(const std::string& dirname, int supress_error) +int LLFile::rmdir(const std::string& dirname, int suppress_error) { #if LL_WINDOWS std::wstring utf16dirname = utf8path_to_wstring(dirname); @@ -341,21 +347,22 @@ int LLFile::rmdir(const std::string& dirname, int supress_error) #else int rc = ::rmdir(dirname.c_str()); #endif - return warnif("rmdir", dirname, rc, supress_error); + return warnif("rmdir", dirname, rc, suppress_error); } // static -LLFILE* LLFile::fopen(const std::string& filename, const char* mode) /* Flawfinder: ignore */ +LLFILE* LLFile::fopen(const std::string& filename, const char* mode) { #if LL_WINDOWS std::wstring utf16filename = utf8path_to_wstring(filename); std::wstring utf16mode = ll_convert(std::string(mode)); return _wfopen(utf16filename.c_str(), utf16mode.c_str()); #else - return ::fopen(filename.c_str(),mode); /* Flawfinder: ignore */ + return ::fopen(filename.c_str(),mode); #endif } +// static int LLFile::close(LLFILE * file) { int ret_value = 0; @@ -366,9 +373,10 @@ int LLFile::close(LLFILE * file) return ret_value; } +// static std::string LLFile::getContents(const std::string& filename) { - LLFILE* fp = fopen(filename, "rb"); /* Flawfinder: ignore */ + LLFILE* fp = LLFile::fopen(filename, "rb"); if (fp) { fseek(fp, 0, SEEK_END); @@ -385,7 +393,8 @@ std::string LLFile::getContents(const std::string& filename) return LLStringUtil::null; } -int LLFile::remove(const std::string& filename, int supress_error) +// static +int LLFile::remove(const std::string& filename, int suppress_error) { #if LL_WINDOWS // Posix remove() works on both files and directories although on Windows @@ -393,8 +402,7 @@ int LLFile::remove(const std::string& filename, int supress_error) // as its siblings unlink() and _wunlink(). // If we really only want to support files we should instead use // unlink() in the non-Windows part below too - int rc = 0; - ; + int rc = -1; std::wstring utf16filename = utf8path_to_wstring(filename); unsigned short st_mode = get_fileattr(utf16filename); if (S_ISDIR(st_mode)) @@ -405,17 +413,25 @@ int LLFile::remove(const std::string& filename, int supress_error) { rc = _wunlink(utf16filename.c_str()); } - else + else if (st_mode) { + // it is something else than a file or directory + // this should not really happen as long as we do not allow for symlink + // detection in the optional parameter to get_fileattr() rc = set_errno_from_oserror(ERROR_INVALID_PARAMETER); } + else + { + // get_filattr() failed and already set errno, preserve it for correct error reporting + } #else int rc = ::remove(filename.c_str()); #endif - return warnif("remove", filename, rc, supress_error); + return warnif("remove", filename, rc, suppress_error); } -int LLFile::rename(const std::string& filename, const std::string& newname, int supress_error) +// static +int LLFile::rename(const std::string& filename, const std::string& newname, int suppress_error) { #if LL_WINDOWS // Posix rename() will gladly overwrite a file at newname if it exists, the Windows @@ -431,25 +447,26 @@ int LLFile::rename(const std::string& filename, const std::string& newname, int #else int rc = ::rename(filename.c_str(),newname.c_str()); #endif - return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, supress_error); + return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, suppress_error); } // Make this a define rather than using magic numbers multiple times in the code #define LLFILE_COPY_BUFFER_SIZE 16384 +// static bool LLFile::copy(const std::string& from, const std::string& to) { bool copied = false; - LLFILE* in = fopen(from, "rb"); /* Flawfinder: ignore */ + LLFILE* in = LLFile::fopen(from, "rb"); if (in) { - LLFILE* out = fopen(to, "wb"); /* Flawfinder: ignore */ + LLFILE* out = LLFile::fopen(to, "wb"); if (out) { - char buf[LLFILE_COPY_BUFFER_SIZE]; /* Flawfinder: ignore */ + char buf[LLFILE_COPY_BUFFER_SIZE]; size_t readbytes; bool write_ok = true; - while (write_ok && (readbytes = fread(buf, 1, LLFILE_COPY_BUFFER_SIZE, in))) /* Flawfinder: ignore */ + while (write_ok && (readbytes = fread(buf, 1, LLFILE_COPY_BUFFER_SIZE, in))) { if (fwrite(buf, 1, readbytes, out) != readbytes) { @@ -468,7 +485,8 @@ bool LLFile::copy(const std::string& from, const std::string& to) return copied; } -int LLFile::stat(const std::string& filename, llstat* filestatus, int supress_error) +// static +int LLFile::stat(const std::string& filename, llstat* filestatus, int suppress_error) { #if LL_WINDOWS std::wstring utf16filename = utf8path_to_wstring(filename); @@ -476,24 +494,25 @@ int LLFile::stat(const std::string& filename, llstat* filestatus, int supress_er #else int rc = ::stat(filename.c_str(), filestatus); #endif - return warnif("stat", filename, rc, supress_error); + return warnif("stat", filename, rc, suppress_error); } -// _wstat() is a bit heavyweight on Windows, use a more lightweight API -// to just get the attributes -unsigned short LLFile::getattr(const std::string& filename, int suppress_error) +// static +unsigned short LLFile::getattr(const std::string& filename, bool dontFollowSymLink, int suppress_error) { #if LL_WINDOWS - int rc = -1; + // _wstat64() is a bit heavyweight on Windows, use a more lightweight API + // to just get the attributes + int rc = -1; std::wstring utf16filename = utf8path_to_wstring(filename); - unsigned short st_mode = get_fileattr(utf16filename); + unsigned short st_mode = get_fileattr(utf16filename, dontFollowSymLink); if (st_mode) { return st_mode; } #else llstat filestatus; - int rc = ::stat(filename.c_str(), &filestatus); + int rc = dontFollowSymLink ? ::lstat(filename.c_str(), &filestatus) : ::stat(filename.c_str(), &filestatus); if (rc == 0) { return filestatus.st_mode; @@ -503,18 +522,25 @@ unsigned short LLFile::getattr(const std::string& filename, int suppress_error) return 0; } +// static bool LLFile::isdir(const std::string& filename) { - // Don't spam the log if the subject pathname doesn't exist. - return S_ISDIR(getattr(filename, ENOENT)); + return S_ISDIR(getattr(filename)); } +// static bool LLFile::isfile(const std::string& filename) { - // Don't spam the log if the subject pathname doesn't exist. - return S_ISREG(getattr(filename, ENOENT)); + return S_ISREG(getattr(filename)); +} + +// static +bool LLFile::islink(const std::string& filename) +{ + return S_ISLNK(getattr(filename, true)); } +// static const char *LLFile::tmpdir() { static std::string utf8path; diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index 9747bb67d8..04a2946ac4 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -41,8 +41,9 @@ typedef FILE LLFILE; #include #if LL_WINDOWS -// windows version of stat function and stat data structure are called _statxx -typedef struct _stat64 llstat; +// The Windows version of stat function and stat data structure are called _stat64 +// We use _stat64 here to support 64-bit st_size and time_t values +typedef struct _stat64 llstat; #else typedef struct stat llstat; #include @@ -56,6 +57,16 @@ typedef struct stat llstat; # define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR) #endif +// Windows C runtime library does not define this and does not support symlink detection in the +// stat functions but we do in our getattr() function +#ifndef S_IFLNK +#define S_IFLNK 0xA000 /* symlink */ +#endif + +#ifndef S_ISLNK +#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK) +#endif + #include "llstring.h" // safe char* -> std::string conversion /// LLFile is a class of static functions operating on paths @@ -96,39 +107,44 @@ public: /// @returns 0 on success and -1 on failure. //// remove a directory - static int rmdir(const std::string& filename, int supress_error = 0); - ///< pass ENOENT in the optional 'supress_error' parameter + static int rmdir(const std::string& filename, int suppress_error = 0); + ///< pass ENOENT in the optional 'suppress_error' parameter /// if you don't want a warning in the log when the directory does not exist /// @returns 0 on success and -1 on failure. /// remove a file or directory - static int remove(const std::string& filename, int supress_error = 0); - ///< pass ENOENT in the optional 'supress_error' parameter + static int remove(const std::string& filename, int suppress_error = 0); + ///< pass ENOENT in the optional 'suppress_error' parameter /// if you don't want a warning in the log when the directory does not exist /// @returns 0 on success and -1 on failure. /// rename a file - static int rename(const std::string& filename, const std::string& newname, int supress_error = 0); + static int rename(const std::string& filename, const std::string& newname, int suppress_error = 0); ///< it will silently overwrite newname if it exists without returning an error + /// Posix guarantees that if newname already exists, then there will be no moment + /// in which for other processes newname does not exist. There is no such guarantee + /// under Windows at this time. It may do it in the same way but the used Windows API + /// does not make such guarantees. /// @returns 0 on success and -1 on failure. - // copy the contents of file from 'from' to 'to' filename + /// copy the contents of file from 'from' to 'to' filename static bool copy(const std::string& from, const std::string& to); ///< @returns true on success and false on failure. /// return the file stat structure for filename - static int stat(const std::string& filename, llstat* file_status, int supress_error = ENOENT); + static int stat(const std::string& filename, llstat* file_status, int suppress_error = ENOENT); ///< for compatibility with existing uses of LL_File::stat() we use ENOENT as default in the - /// optional 'supress_error' parameter to avoid spamming the log with warnings when the API + /// optional 'suppress_error' parameter to avoid spamming the log with warnings when the API /// is used to detect if a file exists /// @returns 0 on success and -1 on failure. /// get the file or directory attributes for filename - static unsigned short getattr(const std::string& filename, int supress_error = 0); + static unsigned short getattr(const std::string& filename, bool dontFollowSymLink = false, int suppress_error = ENOENT); ///< a more lightweight function on Windows to stat, that just returns the file attribute flags - /// pass ENOENT in the optional 'supress_error' parameter if you don't want a warning - /// in the log when the file or directory does not exist + /// dontFollowSymLinks set to true returns the attributes of the symlink if it is one, rather than resolving it + /// we pass by default ENOENT in the optional 'suppress_error' parameter to not spam the log with + /// warnings when the file or directory does not exist /// @returns 0 on failure and a st_mode value with either S_IFDIR or S_IFREG set otherwise /// together with the three access bits which under Windows only the write bit is relevant. @@ -140,6 +156,10 @@ public: static bool isfile(const std::string& filename); ///< @returns true if the path is for an existing file + /// check if filename is a symlink + static bool islink(const std::string& filename); + ///< @returns true if the path is pointing at a symlink + /// return a path to the temporary directory on the system static const char * tmpdir(); }; -- cgit v1.2.3 From aa49a3d7e6da7b2b83d78759c65d21f9865ddea5 Mon Sep 17 00:00:00 2001 From: fmartian Date: Mon, 20 Oct 2025 22:23:59 +0200 Subject: Fix typo in comment --- indra/llcommon/llfile.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index 492c7c690b..a539e4fe28 100644 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -422,7 +422,7 @@ int LLFile::remove(const std::string& filename, int suppress_error) } else { - // get_filattr() failed and already set errno, preserve it for correct error reporting + // get_fileattr() failed and already set errno, preserve it for correct error reporting } #else int rc = ::remove(filename.c_str()); @@ -503,7 +503,7 @@ unsigned short LLFile::getattr(const std::string& filename, bool dontFollowSymLi #if LL_WINDOWS // _wstat64() is a bit heavyweight on Windows, use a more lightweight API // to just get the attributes - int rc = -1; + int rc = -1; std::wstring utf16filename = utf8path_to_wstring(filename); unsigned short st_mode = get_fileattr(utf16filename, dontFollowSymLink); if (st_mode) -- cgit v1.2.3 From 528c4b5f312a68056073c210c415c76d4eab4091 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Mon, 20 Oct 2025 23:54:00 +0300 Subject: #2427 Show torn off chat when clicking on a message popup --- indra/newview/llfloaterimcontainer.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp index d821d9a4a5..a0f2dbe197 100644 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -1698,6 +1698,11 @@ void LLFloaterIMContainer::showConversation(const LLUUID& session_id) if (session_floater) { session_floater->restoreFloater(); + if (session_floater->isTornOff() && session_floater->isMinimized()) + { + session_floater->setMinimized(false); + session_floater->setFocus(true); + } } } -- cgit v1.2.3 From 9a2ff1657d380c19ecb491bee1598e9ba037ef56 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Tue, 21 Oct 2025 09:30:14 +0300 Subject: #4871 Fix webrtc crash on tuningGetEnergy --- indra/newview/llvoicewebrtc.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index e6fcc7bd6b..fba7098906 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -396,7 +396,7 @@ void LLWebRTCVoiceClient::updateSettings() config.mNoiseSuppressionLevel = noiseSuppressionLevel; audioConfigChanged = true; } - if (audioConfigChanged) + if (audioConfigChanged && mWebRTCDeviceInterface) { mWebRTCDeviceInterface->setAudioConfig(config); } @@ -797,7 +797,10 @@ void LLWebRTCVoiceClient::tuningStart() { if (!mIsInTuningMode) { - mWebRTCDeviceInterface->setTuningMode(true); + if (mWebRTCDeviceInterface) + { + mWebRTCDeviceInterface->setTuningMode(true); + } mIsInTuningMode = true; } } @@ -806,7 +809,10 @@ void LLWebRTCVoiceClient::tuningStop() { if (mIsInTuningMode) { - mWebRTCDeviceInterface->setTuningMode(false); + if (mWebRTCDeviceInterface) + { + mWebRTCDeviceInterface->setTuningMode(false); + } mIsInTuningMode = false; } } @@ -839,6 +845,10 @@ void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) float LLWebRTCVoiceClient::tuningGetEnergy(void) { + if (!mWebRTCDeviceInterface) + { + return 0.f; + } float rms = mWebRTCDeviceInterface->getTuningAudioLevel(); return TUNING_LEVEL_START_POINT - TUNING_LEVEL_SCALE * rms; } @@ -866,7 +876,10 @@ void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList) clearCaptureDevices(); clearRenderDevices(); } - mWebRTCDeviceInterface->refreshDevices(); + if (mWebRTCDeviceInterface) + { + mWebRTCDeviceInterface->refreshDevices(); + } } @@ -1174,7 +1187,7 @@ void LLWebRTCVoiceClient::sendPositionUpdate(bool force) void LLWebRTCVoiceClient::updateOwnVolume() { F32 audio_level = 0.0f; - if (!mMuteMic) + if (!mMuteMic && mWebRTCDeviceInterface) { float rms = mWebRTCDeviceInterface->getPeerConnectionAudioLevel(); audio_level = LEVEL_START_POINT - LEVEL_SCALE * rms; -- cgit v1.2.3 From 0d5138c1f93a77ff52551a76d0d8575413550859 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Tue, 21 Oct 2025 20:34:23 +0300 Subject: #4839 Crash at LLPluginProcessParent::poll Missed lock --- indra/llplugin/llpluginprocessparent.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index afee099697..02c7d9507d 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -983,6 +983,7 @@ void LLPluginProcessParent::poll(F64 timeout) } // Remove instances in the done state from the sInstances map. + LLCoros::LockType lock(*sInstancesMutex); mapInstances_t::iterator itClean = sInstances.begin(); while (itClean != sInstances.end()) { -- cgit v1.2.3