diff --git a/Source/Configuration/Shared.xcconfig b/Source/Configuration/Shared.xcconfig index d747c926..93427614 100644 --- a/Source/Configuration/Shared.xcconfig +++ b/Source/Configuration/Shared.xcconfig @@ -1,4 +1,4 @@ HARDCODED_DEVELOPER_ID=Developer ID Application: LateNite Films Pty Ltd HARDCODED_COPYRIGHT=Copyright © LateNite Films Pty Ltd 2022-2023. All Rights Reserved. -HARDCODED_VERSION=1.0.1 -HARDCODED_BUILD=12 +HARDCODED_VERSION=1.0.0 +HARDCODED_BUILD=11 diff --git a/Source/Frameworks/gyroflow/Cargo.toml b/Source/Frameworks/gyroflow/Cargo.toml index 0660d44e..944c797f 100644 --- a/Source/Frameworks/gyroflow/Cargo.toml +++ b/Source/Frameworks/gyroflow/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gyroflow-toolbox" -version = "1.0.1" +version = "1.0.0" authors = ["Chris Hocking ", "Adrian "] edition = "2021" @@ -16,7 +16,7 @@ gyroflow-core = { git = "https://github.com/gyroflow/gyroflow.git" } log = "0.4.17" oslog = "0.2.0" lazy_static = "1.4" -lru = "0.10" -nalgebra = { version = "0.32", features = ["serde-serialize"] } +lru = "0.8" +nalgebra = { version = "0.31", features = ["serde-serialize"] } once_cell = "1.16.0" metal = "0.24.0" diff --git a/Source/Frameworks/gyroflow/inc/gyroflow.h b/Source/Frameworks/gyroflow/inc/gyroflow.h index 1d5f9a14..1e3c8a1a 100644 --- a/Source/Frameworks/gyroflow/inc/gyroflow.h +++ b/Source/Frameworks/gyroflow/inc/gyroflow.h @@ -23,12 +23,6 @@ const char* processFrame( double fov, double smoothness, double lens_correction, - double horizon_lock, - double horizon_roll, - double position_offset_x, - double position_offset_y, - double video_rotation, - double video_speed, void *in_mtl_texture, void *out_mtl_texture, void *command_queue diff --git a/Source/Frameworks/gyroflow/src/lib.rs b/Source/Frameworks/gyroflow/src/lib.rs index f0e8e0a2..ea81b558 100644 --- a/Source/Frameworks/gyroflow/src/lib.rs +++ b/Source/Frameworks/gyroflow/src/lib.rs @@ -46,12 +46,6 @@ pub extern "C" fn processFrame( fov: f64, smoothness: f64, lens_correction: f64, - horizon_lock: f64, - horizon_roll: f64, - position_offset_x: f64, - position_offset_y: f64, - video_rotation: f64, - video_speed: f64, in_mtl_tex: *mut std::ffi::c_void, out_mtl_tex: *mut std::ffi::c_void, command_queue: *mut std::ffi::c_void, @@ -143,7 +137,7 @@ pub extern "C" fn processFrame( // Force the background color to transparent: //--------------------------------------------------------- let background_color: Vector4 = Vector4::new(0.0, 0.0, 0.0, 0.0); - manager.set_background_color(background_color); + manager.stabilization.write().set_background(background_color); }, Err(e) => { //--------------------------------------------------------- @@ -178,62 +172,19 @@ pub extern "C" fn processFrame( params.lens_correction_amount = lens_correction; params_changed = true; } - - //--------------------------------------------------------- - // Set the Position Offset X: - //--------------------------------------------------------- - if params.adaptive_zoom_center_offset.0 != position_offset_x / 100.0 { - params.adaptive_zoom_center_offset.0 = position_offset_x / 100.0; - params_changed = true; - } - - //--------------------------------------------------------- - // Set the Position Offset Y: - //--------------------------------------------------------- - if params.adaptive_zoom_center_offset.1 != position_offset_y / 100.0 { - params.adaptive_zoom_center_offset.1 = position_offset_y / 100.0; - params_changed = true; - } - - //--------------------------------------------------------- - // Set the Video Rotation: - //--------------------------------------------------------- - if params.video_rotation != video_rotation { - params.video_rotation = video_rotation; - params_changed = true; - } - - //--------------------------------------------------------- - // Set the Video Speed: - //--------------------------------------------------------- - if params.video_speed != video_speed / 100.0 { - params.video_speed = video_speed / 100.0; - params_changed = true; - } } //--------------------------------------------------------- - // Set the Smoothing Parameters: + // Set the Smoothness: //--------------------------------------------------------- { - //--------------------------------------------------------- - // Set the Smoothness: - //--------------------------------------------------------- let mut smoothing = manager.smoothing.write(); if smoothing.current().get_parameter("smoothness") != smoothness { smoothing.current_mut().set_parameter("smoothness", smoothness); params_changed = true; } - - //--------------------------------------------------------- - // Set the Horizon Lock: - //--------------------------------------------------------- - if smoothing.horizon_lock.lock_enabled != (horizon_lock > 0.0) || smoothing.horizon_lock.horizonlockpercent != horizon_lock || smoothing.horizon_lock.horizonroll != horizon_roll { - smoothing.horizon_lock.set_horizon(horizon_lock, horizon_roll); - params_changed = true; - } } - + //--------------------------------------------------------- // If something has changed, Invalidate & Recompute, to // make sure everything is up-to-date: @@ -241,7 +192,7 @@ pub extern "C" fn processFrame( if params_changed { manager.invalidate_smoothing(); manager.recompute_blocking(); - manager.params.write().calculate_ramped_timestamps(&manager.keyframes.read(), false, false); + manager.params.write().calculate_ramped_timestamps(&manager.keyframes.read()); } //--------------------------------------------------------- @@ -258,13 +209,13 @@ pub extern "C" fn processFrame( size: (output_width, output_height, input_stride), rect: None, data: BufferSource::Metal { texture: in_mtl_tex as *mut metal::MTLTexture, command_queue: command_queue as *mut metal::MTLCommandQueue }, - texture_copy: false + texture_copy: true }, output: BufferDescription { size: (output_width, output_height, output_stride), rect: None, data: BufferSource::Metal { texture: out_mtl_tex as *mut metal::MTLTexture, command_queue: command_queue as *mut metal::MTLCommandQueue }, - texture_copy: false + texture_copy: true } }; diff --git a/Source/Gyroflow.xcodeproj/project.pbxproj b/Source/Gyroflow.xcodeproj/project.pbxproj index b3fba2e1..5f0a8468 100644 --- a/Source/Gyroflow.xcodeproj/project.pbxproj +++ b/Source/Gyroflow.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 2239AB372943F8F600028B77 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2239AB352943F8F600028B77 /* InfoPlist.strings */; }; 2239AB392943F8F600028B77 /* MetalDeviceCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 2239AB382943F8F600028B77 /* MetalDeviceCache.m */; }; 2239AB3C2943F8F600028B77 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2239AB3A2943F8F600028B77 /* MainMenu.xib */; }; + 2239AB412943F8F600028B77 /* TileableRemoteGyroflow.metal in Sources */ = {isa = PBXBuildFile; fileRef = 2239AB402943F8F600028B77 /* TileableRemoteGyroflow.metal */; }; 2239AB452943F8F600028B77 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 2239AB442943F8F600028B77 /* main.m */; }; 2239AB492943F8F600028B77 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2239AB472943F8F600028B77 /* InfoPlist.strings */; }; 2239AB4C2943F8F600028B77 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2239AB4A2943F8F600028B77 /* Localizable.strings */; }; @@ -29,9 +30,10 @@ 2239AB792945979800028B77 /* libgyroflow_toolbox.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = 2239AB772945979800028B77 /* libgyroflow_toolbox.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 224321B629615B0C00EA591A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 224321B529615B0C00EA591A /* Assets.xcassets */; }; 224321B829615C4400EA591A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 224321B729615C4400EA591A /* Assets.xcassets */; }; - 22442A1B29C5A097006A56D6 /* CustomDropZoneView.m in Sources */ = {isa = PBXBuildFile; fileRef = 22442A1929C5A097006A56D6 /* CustomDropZoneView.m */; }; - 2264334F29864BDF002C0F9E /* CustomButtonView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2264334D29864BDF002C0F9E /* CustomButtonView.m */; }; + 228E9E1829517D8200B2571E /* ImportGyroflowProjectView.m in Sources */ = {isa = PBXBuildFile; fileRef = 228E9E1729517D8200B2571E /* ImportGyroflowProjectView.m */; }; + 228E9E1C2951DB6900B2571E /* ReloadGyroflowProjectView.m in Sources */ = {isa = PBXBuildFile; fileRef = 228E9E1B2951DB6900B2571E /* ReloadGyroflowProjectView.m */; }; 22DAC75F2953B1A7001F2E06 /* GyroflowDocument.icns in Resources */ = {isa = PBXBuildFile; fileRef = 22DAC75E2953B1A7001F2E06 /* GyroflowDocument.icns */; }; + 22DAC7622953CD4F001F2E06 /* LaunchGyroflowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 22DAC7602953CD4F001F2E06 /* LaunchGyroflowView.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -95,7 +97,9 @@ 2239AB362943F8F600028B77 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 2239AB382943F8F600028B77 /* MetalDeviceCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MetalDeviceCache.m; sourceTree = ""; }; 2239AB3B2943F8F600028B77 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 2239AB402943F8F600028B77 /* TileableRemoteGyroflow.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = TileableRemoteGyroflow.metal; sourceTree = ""; }; 2239AB422943F8F600028B77 /* SandboxEntitlements.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SandboxEntitlements.entitlements; sourceTree = ""; }; + 2239AB432943F8F600028B77 /* TileableRemoteGyroflowShaderTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TileableRemoteGyroflowShaderTypes.h; sourceTree = ""; }; 2239AB442943F8F600028B77 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 2239AB462943F8F600028B77 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 2239AB482943F8F600028B77 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -109,12 +113,14 @@ 2239AB7C2945A69100028B77 /* lib.rs */ = {isa = PBXFileReference; lastKnownFileType = text; name = lib.rs; path = Frameworks/gyroflow/src/lib.rs; sourceTree = SOURCE_ROOT; }; 224321B529615B0C00EA591A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 224321B729615C4400EA591A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 22442A1929C5A097006A56D6 /* CustomDropZoneView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CustomDropZoneView.m; sourceTree = ""; }; - 22442A1A29C5A097006A56D6 /* CustomDropZoneView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomDropZoneView.h; sourceTree = ""; }; - 2264334D29864BDF002C0F9E /* CustomButtonView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CustomButtonView.m; sourceTree = ""; }; - 2264334E29864BDF002C0F9E /* CustomButtonView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomButtonView.h; sourceTree = ""; }; + 228E9E1629517D8200B2571E /* ImportGyroflowProjectView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImportGyroflowProjectView.h; sourceTree = ""; }; + 228E9E1729517D8200B2571E /* ImportGyroflowProjectView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImportGyroflowProjectView.m; sourceTree = ""; }; 228E9E1929517DDA00B2571E /* GyroflowConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GyroflowConstants.h; sourceTree = ""; }; + 228E9E1A2951DB6800B2571E /* ReloadGyroflowProjectView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReloadGyroflowProjectView.h; sourceTree = ""; }; + 228E9E1B2951DB6900B2571E /* ReloadGyroflowProjectView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReloadGyroflowProjectView.m; sourceTree = ""; }; 22DAC75E2953B1A7001F2E06 /* GyroflowDocument.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = GyroflowDocument.icns; sourceTree = ""; }; + 22DAC7602953CD4F001F2E06 /* LaunchGyroflowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LaunchGyroflowView.m; sourceTree = ""; }; + 22DAC7612953CD4F001F2E06 /* LaunchGyroflowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LaunchGyroflowView.h; sourceTree = ""; }; 22E4989F29492E8100580F67 /* Cargo.toml */ = {isa = PBXFileReference; lastKnownFileType = text; name = Cargo.toml; path = Frameworks/gyroflow/Cargo.toml; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ @@ -202,6 +208,7 @@ children = ( 228E9E13294E815E00B2571E /* Code */, 228E9E12294E815200B2571E /* Metal Device Cache */, + 228E9E14294E816D00B2571E /* Shader */, 228E9E15294E817700B2571E /* Extras */, ); path = Plugin; @@ -275,19 +282,25 @@ 228E9E13294E815E00B2571E /* Code */ = { isa = PBXGroup; children = ( - 22442A1A29C5A097006A56D6 /* CustomDropZoneView.h */, - 22442A1929C5A097006A56D6 /* CustomDropZoneView.m */, - 2264334E29864BDF002C0F9E /* CustomButtonView.h */, - 2264334D29864BDF002C0F9E /* CustomButtonView.m */, 228E9E1929517DDA00B2571E /* GyroflowConstants.h */, 2239AB2E2943F8F600028B77 /* GyroflowPlugIn.h */, 2239AB312943F8F600028B77 /* GyroflowPlugIn.m */, 2239AB58294400CE00028B77 /* GyroflowParameters.h */, 2239AB56294400B500028B77 /* GyroflowParameters.m */, + 22DAC7632953D1CF001F2E06 /* Custom Views */, ); name = Code; sourceTree = ""; }; + 228E9E14294E816D00B2571E /* Shader */ = { + isa = PBXGroup; + children = ( + 2239AB402943F8F600028B77 /* TileableRemoteGyroflow.metal */, + 2239AB432943F8F600028B77 /* TileableRemoteGyroflowShaderTypes.h */, + ); + name = Shader; + sourceTree = ""; + }; 228E9E15294E817700B2571E /* Extras */ = { isa = PBXGroup; children = ( @@ -300,6 +313,19 @@ name = Extras; sourceTree = ""; }; + 22DAC7632953D1CF001F2E06 /* Custom Views */ = { + isa = PBXGroup; + children = ( + 22DAC7612953CD4F001F2E06 /* LaunchGyroflowView.h */, + 22DAC7602953CD4F001F2E06 /* LaunchGyroflowView.m */, + 228E9E1629517D8200B2571E /* ImportGyroflowProjectView.h */, + 228E9E1729517D8200B2571E /* ImportGyroflowProjectView.m */, + 228E9E1A2951DB6800B2571E /* ReloadGyroflowProjectView.h */, + 228E9E1B2951DB6900B2571E /* ReloadGyroflowProjectView.m */, + ); + name = "Custom Views"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -459,11 +485,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 228E9E1C2951DB6900B2571E /* ReloadGyroflowProjectView.m in Sources */, 2239AB392943F8F600028B77 /* MetalDeviceCache.m in Sources */, - 2264334F29864BDF002C0F9E /* CustomButtonView.m in Sources */, - 22442A1B29C5A097006A56D6 /* CustomDropZoneView.m in Sources */, + 228E9E1829517D8200B2571E /* ImportGyroflowProjectView.m in Sources */, + 2239AB412943F8F600028B77 /* TileableRemoteGyroflow.metal in Sources */, 2239AB452943F8F600028B77 /* main.m in Sources */, 2239AB57294400B500028B77 /* GyroflowParameters.m in Sources */, + 22DAC7622953CD4F001F2E06 /* LaunchGyroflowView.m in Sources */, 2239AB322943F8F600028B77 /* GyroflowPlugIn.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Source/Gyroflow/Plugin/CustomButtonView.h b/Source/Gyroflow/Plugin/CustomButtonView.h deleted file mode 100644 index 23cf4a44..00000000 --- a/Source/Gyroflow/Plugin/CustomButtonView.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// CustomButtonView.h -// Gyroflow Toolbox Renderer -// -// Created by Chris Hocking on 29/01/2023. -// - -#import -#import - -@interface CustomButtonView : NSView -{ - id _apiManager; - id _parentPlugin; - int _buttonID; -} - -- (instancetype)initWithAPIManager:(id)apiManager - parentPlugin:(id)parentPlugin - buttonID:(UInt32)buttonID - buttonTitle:(NSString*)buttonTitle; -@end diff --git a/Source/Gyroflow/Plugin/CustomDropZoneView.h b/Source/Gyroflow/Plugin/CustomDropZoneView.h deleted file mode 100644 index 82bb5961..00000000 --- a/Source/Gyroflow/Plugin/CustomDropZoneView.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// CustomButtonView.h -// Gyroflow Toolbox Renderer -// -// Created by Chris Hocking on 29/01/2023. -// - -#import -#import - -@interface CustomDropZoneView : NSView -{ - id _apiManager; - id _parentPlugin; - int _buttonID; -} - -- (instancetype)initWithAPIManager:(id)apiManager - parentPlugin:(id)parentPlugin - buttonID:(UInt32)buttonID - buttonTitle:(NSString*)buttonTitle; -@end diff --git a/Source/Gyroflow/Plugin/CustomDropZoneView.m b/Source/Gyroflow/Plugin/CustomDropZoneView.m deleted file mode 100644 index 57fa082d..00000000 --- a/Source/Gyroflow/Plugin/CustomDropZoneView.m +++ /dev/null @@ -1,166 +0,0 @@ -// -// CustomButtonView.m -// Gyroflow Toolbox Renderer -// -// Created by Chris Hocking on 29/01/2023. -// - -#import "CustomDropZoneView.h" -#import - -static NSString *const kFinalCutProUTI = @"com.apple.flexo.proFFPasteboardUTI"; - -@interface CustomDropZoneView () - -@property (nonatomic) bool dragIsOver; - -@end - -@implementation CustomDropZoneView { - NSButton* _button; -} - -//--------------------------------------------------------- -// Initialize: -//--------------------------------------------------------- -- (instancetype)initWithAPIManager:(id)apiManager - parentPlugin:(id)parentPlugin - buttonID:(UInt32)buttonID - buttonTitle:(NSString*)buttonTitle -{ - int buttonWidth = 200; - int buttonHeight = 32; - - NSRect frameRect = NSMakeRect(0, 0, buttonWidth, buttonHeight); // x y w h - self = [super initWithFrame:frameRect]; - - if (self != nil) - { - _apiManager = apiManager; - - [self registerForDraggedTypes:@[kFinalCutProUTI]]; - - self.wantsLayer = YES; - self.layer.backgroundColor = [[NSColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1.0] CGColor]; - self.layer.borderColor = [[NSColor grayColor] CGColor]; - self.layer.borderWidth = 2.0; - - //--------------------------------------------------------- - // Cache the parent plugin & button ID: - //--------------------------------------------------------- - _parentPlugin = parentPlugin; - _buttonID = buttonID; - - //--------------------------------------------------------- - // Add the button: - //--------------------------------------------------------- - /* - NSButton *button = [[NSButton alloc]initWithFrame:NSMakeRect(0, 0, buttonWidth, buttonHeight)]; // x y w h - [button setButtonType:NSButtonTypeMomentaryPushIn]; - [button setBezelStyle: NSBezelStyleRounded]; - button.layer.backgroundColor = [NSColor colorWithCalibratedRed:66 green:66 blue:66 alpha:1].CGColor; - button.layer.shadowColor = [NSColor blackColor].CGColor; - [button setBordered:YES]; - [button setTitle:buttonTitle]; - [button setTarget:self]; - [button setAction:@selector(buttonPressed)]; - - _button = button; - [self addSubview:_button]; - */ - } - - return self; -} - -//--------------------------------------------------------- -// Awake From NIB: -//--------------------------------------------------------- -- (void) awakeFromNib { - NSArray *sortedPasteboardTypes = @[@"com.apple.finalcutpro.xml.v1-10", @"com.apple.finalcutpro.xml.v1-9", @"com.apple.finalcutpro.xml"]; - [self registerForDraggedTypes:sortedPasteboardTypes]; - -} - -//--------------------------------------------------------- -// Dragging Entered: -//--------------------------------------------------------- -- (NSDragOperation)draggingEntered:(id )sender { - NSArray *sortedPasteboardTypes = @[@"com.apple.finalcutpro.xml.v1-10", @"com.apple.finalcutpro.xml.v1-9", @"com.apple.finalcutpro.xml"]; - for (NSPasteboardType pasteboardType in sortedPasteboardTypes) { - if ( [[[sender draggingPasteboard] types] containsObject:pasteboardType] ) { - _dragIsOver = true; - [self needsDisplay]; - return NSDragOperationCopy; - } - } - return NSDragOperationNone; -} - -- (BOOL)prepareForDragOperation:(id)sender { - return YES; -} - -- (BOOL)performDragOperation:(id)sender { - NSPasteboard *pasteboard = [sender draggingPasteboard]; - NSString *finalCutProData = [pasteboard stringForType:kFinalCutProUTI]; - - // Handle the dropped Final Cut Pro data - if (finalCutProData) { - NSLog(@"Dropped Final Cut Pro data: %@", finalCutProData); - } - - return YES; -} - -//--------------------------------------------------------- -// Dragging Exited: -//--------------------------------------------------------- -- (void)draggingExited:(nullable id )sender { - _dragIsOver = false; - [self needsDisplay]; -} - -//--------------------------------------------------------- -// Deallocates the memory occupied by the receiver: -//--------------------------------------------------------- -- (void)dealloc -{ - if (_button) { - [_button release]; - } - - [super dealloc]; -} - -//--------------------------------------------------------- -// Draw the NSView: -//--------------------------------------------------------- -- (void)drawRect:(NSRect)dirtyRect { - [NSGraphicsContext saveGraphicsState]; - [super drawRect:dirtyRect]; - if (_dragIsOver) - { - [[[NSColor keyboardFocusIndicatorColor] colorWithAlphaComponent:0.25] set]; - NSRectFill(NSInsetRect(self.bounds, 1, 1)); - } - [NSGraphicsContext restoreGraphicsState]; -} - - - -//--------------------------------------------------------- -// Because custom views are hosted in an overlay window, -// the first click on them will normally just make the -// overlay window be the key window, and it will require a -// second click in order to actually tell the view to -// start responding. By returning YES from this method, the -// first click begins user interaction with the view. -//--------------------------------------------------------- -- (BOOL)acceptsFirstMouse:(NSEvent *)event -{ - return YES; -} - -@end - diff --git a/Source/Gyroflow/Plugin/GyroflowConstants.h b/Source/Gyroflow/Plugin/GyroflowConstants.h index d780804e..6a2c73eb 100644 --- a/Source/Gyroflow/Plugin/GyroflowConstants.h +++ b/Source/Gyroflow/Plugin/GyroflowConstants.h @@ -1,6 +1,6 @@ // // GyroflowConstants.h -// Gyroflow Toolbox Renderer +// Gyroflow Toolbox // // Created by Chris Hocking on 20/12/2022. // @@ -11,12 +11,8 @@ //--------------------------------------------------------- // Plugin Parameter Constants: //--------------------------------------------------------- -enum { - - kCB_DropZone = 10, - +enum { kCB_LaunchGyroflow = 20, - kCB_LoadLastGyroflowProject = 25, kCB_ImportGyroflowProject = 30, kCB_LoadedGyroflowProject = 40, kCB_ReloadGyroflowProject = 50, @@ -29,14 +25,6 @@ enum { kCB_FOV = 100, kCB_Smoothness = 110, kCB_LensCorrection = 120, - - kCB_HorizonLock = 130, - kCB_HorizonRoll = 140, - - kCB_PositionOffsetX = 150, - kCB_PositionOffsetY = 160, - kCB_VideoRotation = 170, - kCB_VideoSpeed = 180, }; //--------------------------------------------------------- diff --git a/Source/Gyroflow/Plugin/GyroflowParameters.h b/Source/Gyroflow/Plugin/GyroflowParameters.h index b5d4f795..354c5296 100644 --- a/Source/Gyroflow/Plugin/GyroflowParameters.h +++ b/Source/Gyroflow/Plugin/GyroflowParameters.h @@ -1,6 +1,6 @@ // // GyroflowParameters.h -// Gyroflow Toolbox Renderer +// Gyroflow Toolbox // // Created by Chris Hocking on 10/12/2022. // @@ -15,13 +15,6 @@ NSNumber *fov; NSNumber *smoothness; NSNumber *lensCorrection; - - NSNumber *horizonLock; - NSNumber *horizonRoll; - NSNumber *positionOffsetX; - NSNumber *positionOffsetY; - NSNumber *videoRotation; - NSNumber *videoSpeed; } @property (nonatomic, copy) NSString *gyroflowPath; @@ -31,11 +24,4 @@ @property (nonatomic, copy) NSNumber *smoothness; @property (nonatomic, copy) NSNumber *lensCorrection; -@property (nonatomic, copy) NSNumber *horizonLock; -@property (nonatomic, copy) NSNumber *horizonRoll; -@property (nonatomic, copy) NSNumber *positionOffsetX; -@property (nonatomic, copy) NSNumber *positionOffsetY; -@property (nonatomic, copy) NSNumber *videoRotation; -@property (nonatomic, copy) NSNumber *videoSpeed; - @end diff --git a/Source/Gyroflow/Plugin/GyroflowParameters.m b/Source/Gyroflow/Plugin/GyroflowParameters.m index d1cd3c3e..568dee7b 100644 --- a/Source/Gyroflow/Plugin/GyroflowParameters.m +++ b/Source/Gyroflow/Plugin/GyroflowParameters.m @@ -1,6 +1,6 @@ // // GyroflowParameters.m -// Gyroflow Toolbox Renderer +// Gyroflow Toolbox // // Created by Chris Hocking on 10/12/2022. // @@ -23,13 +23,6 @@ @implementation GyroflowParameters @synthesize smoothness; @synthesize lensCorrection; -@synthesize horizonLock; -@synthesize horizonRoll; -@synthesize positionOffsetX; -@synthesize positionOffsetY; -@synthesize videoRotation; -@synthesize videoSpeed; - + (BOOL)supportsSecureCoding { return YES; @@ -43,13 +36,6 @@ - (void)dealloc { [smoothness release]; [lensCorrection release]; - [horizonLock release]; - [horizonRoll release]; - [positionOffsetX release]; - [positionOffsetY release]; - [videoRotation release]; - [videoSpeed release]; - [super dealloc]; } @@ -61,13 +47,6 @@ - (id)initWithCoder:(NSCoder *)decoder { self.fov = [decoder decodeObjectOfClass:[NSNumber class] forKey:@"fov"]; self.smoothness = [decoder decodeObjectOfClass:[NSNumber class] forKey:@"smoothness"]; self.lensCorrection = [decoder decodeObjectOfClass:[NSNumber class] forKey:@"lensCorrection"]; - - self.horizonLock = [decoder decodeObjectOfClass:[NSNumber class] forKey:@"horizonLock"]; - self.horizonRoll = [decoder decodeObjectOfClass:[NSNumber class] forKey:@"horizonRoll"]; - self.positionOffsetX = [decoder decodeObjectOfClass:[NSNumber class] forKey:@"positionOffsetX"]; - self.positionOffsetY = [decoder decodeObjectOfClass:[NSNumber class] forKey:@"positionOffsetY"]; - self.videoRotation = [decoder decodeObjectOfClass:[NSNumber class] forKey:@"videoRotation"]; - self.videoSpeed = [decoder decodeObjectOfClass:[NSNumber class] forKey:@"videoSpeed"]; } return self; } @@ -79,13 +58,6 @@ - (void)encodeWithCoder:(NSCoder *)encoder { [encoder encodeObject:fov forKey:@"fov"]; [encoder encodeObject:smoothness forKey:@"smoothness"]; [encoder encodeObject:lensCorrection forKey:@"lensCorrection"]; - - [encoder encodeObject:horizonLock forKey:@"horizonLock"]; - [encoder encodeObject:horizonRoll forKey:@"horizonRoll"]; - [encoder encodeObject:positionOffsetX forKey:@"positionOffsetX"]; - [encoder encodeObject:positionOffsetY forKey:@"positionOffsetY"]; - [encoder encodeObject:videoRotation forKey:@"videoRotation"]; - [encoder encodeObject:videoSpeed forKey:@"videoSpeed"]; } @end diff --git a/Source/Gyroflow/Plugin/GyroflowPlugIn.h b/Source/Gyroflow/Plugin/GyroflowPlugIn.h index 12f5ab3b..52563c5f 100644 --- a/Source/Gyroflow/Plugin/GyroflowPlugIn.h +++ b/Source/Gyroflow/Plugin/GyroflowPlugIn.h @@ -1,6 +1,6 @@ // // GyroflowPlugIn.h -// Gyroflow Toolbox Renderer +// Gyroflow Toolbox // // Created by Chris Hocking on 10/12/2022. // @@ -8,15 +8,10 @@ #import #import -@interface GyroflowPlugIn : NSObject { - //--------------------------------------------------------- - // Cached Custom Views: - //--------------------------------------------------------- +@interface GyroflowPlugIn : NSObject { NSView* launchGyroflowView; NSView* importGyroflowProjectView; - NSView* reloadGyroflowProjectView; - NSView* loadLastGyroflowProjectView; - NSView* dropZoneView; + NSView* reloadGyroflowProjectView; } @property (assign) id apiManager; @end diff --git a/Source/Gyroflow/Plugin/GyroflowPlugIn.m b/Source/Gyroflow/Plugin/GyroflowPlugIn.m index b096af2a..a3fd9d51 100644 --- a/Source/Gyroflow/Plugin/GyroflowPlugIn.m +++ b/Source/Gyroflow/Plugin/GyroflowPlugIn.m @@ -1,6 +1,6 @@ // // GyroflowPlugIn.m -// Gyroflow Toolbox Renderer +// Gyroflow Toolbox // // Created by Chris Hocking on 10/12/2022. // @@ -14,8 +14,11 @@ #import "GyroflowParameters.h" #import "GyroflowConstants.h" -#import "CustomButtonView.h" -#import "CustomDropZoneView.h" +#import "LaunchGyroflowView.h" +#import "ImportGyroflowProjectView.h" +#import "ReloadGyroflowProjectView.h" + +#import "TileableRemoteGyroflowShaderTypes.h" #import "MetalDeviceCache.h" @@ -26,11 +29,6 @@ #include #include -#include -#include -#include -#include - //--------------------------------------------------------- // Gyroflow FxPlug4 Implementation: //--------------------------------------------------------- @@ -49,21 +47,6 @@ - (nullable instancetype)initWithAPIManager:(id)newApiManager; self = [super init]; if (self != nil) { - //--------------------------------------------------------- - // Write log file to disk when using NSLog: - //--------------------------------------------------------- - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); - NSString *applicationSupportDirectory = [paths firstObject]; - NSLog(@"applicationSupportDirectory: '%@'", applicationSupportDirectory); - - NSString* logPath = [applicationSupportDirectory stringByAppendingString:@"/FxPlug.log"]; - - freopen([logPath fileSystemRepresentation],"a+",stderr); - NSLog(@"[Gyroflow Toolbox Renderer] --------------------------------- START OF NEW SESSION ---------------------------------"); - NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; - NSString *build = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]; - NSLog(@"[Gyroflow Toolbox Renderer] Version: %@ (%@)", version, build); - _apiManager = newApiManager; } return self; @@ -146,49 +129,38 @@ - (BOOL)properties:(NSDictionary * _Nonnull *)properties // // Provides an NSView to be associated with the given // parameter. -// -// NOTE: It seems we need to cache the NSView, otherwise it -// gets deallocated prematurely: //--------------------------------------------------------- - (NSView*)createViewForParameterID:(UInt32)parameterID { if (parameterID == kCB_ImportGyroflowProject) { - NSView* view = [[CustomButtonView alloc] initWithAPIManager:_apiManager - parentPlugin:self - buttonID:kCB_ImportGyroflowProject - buttonTitle:@"Import Gyroflow Project"]; + NSView* view = [[ImportGyroflowProjectView alloc] initWithFrame:NSMakeRect(0, 0, 135, 32) // x y w h + andAPIManager:_apiManager]; + //--------------------------------------------------------- + // It seems we need to cache the NSView, otherwise it gets + // deallocated prematurely: + //--------------------------------------------------------- importGyroflowProjectView = view; return view; } else if (parameterID == kCB_ReloadGyroflowProject) { - NSView* view = [[CustomButtonView alloc] initWithAPIManager:_apiManager - parentPlugin:self - buttonID:kCB_ReloadGyroflowProject - buttonTitle:@"Reload Gyroflow Project"]; + NSView* view = [[ReloadGyroflowProjectView alloc] initWithFrame:NSMakeRect(0, 0, 135, 32) // x y w h + andAPIManager:_apiManager]; + //--------------------------------------------------------- + // It seems we need to cache the NSView, otherwise it gets + // deallocated prematurely: + //--------------------------------------------------------- reloadGyroflowProjectView = view; return view; } else if (parameterID == kCB_LaunchGyroflow) { - NSView* view = [[CustomButtonView alloc] initWithAPIManager:_apiManager - parentPlugin:self - buttonID:kCB_LaunchGyroflow - buttonTitle:@"Open in Gyroflow"]; + NSView* view = [[LaunchGyroflowView alloc] initWithFrame:NSMakeRect(0, 0, 135, 32) // x y w h + andAPIManager:_apiManager]; + //--------------------------------------------------------- + // It seems we need to cache the NSView, otherwise it gets + // deallocated prematurely: + //--------------------------------------------------------- launchGyroflowView = view; return view; - } else if (parameterID == kCB_LoadLastGyroflowProject) { - NSView* view = [[CustomButtonView alloc] initWithAPIManager:_apiManager - parentPlugin:self - buttonID:kCB_LoadLastGyroflowProject - buttonTitle:@"Import Last Saved Project"]; - loadLastGyroflowProjectView = view; - return view; - } else if (parameterID == kCB_DropZone) { - NSView* view = [[CustomDropZoneView alloc] initWithAPIManager:_apiManager - parentPlugin:self - buttonID:kCB_DropZone - buttonTitle:@"Drop Zone"]; - dropZoneView = view; - return view; } else { - NSLog(@"[Gyroflow Toolbox Renderer] BUG - createViewForParameterID requested a parameterID that we haven't allowed for: %u", (unsigned int)parameterID); + NSLog(@"[Gyroflow Toolbox] BUG - createViewForParameterID requested a parameterID that we haven't allowed for: %u", (unsigned int)parameterID); return nil; } } @@ -205,7 +177,7 @@ - (NSView*)createViewForParameterID:(UInt32)parameterID //--------------------------------------------------------- - (void)pluginInstanceAddedToDocument { - //NSLog(@"[Gyroflow Toolbox Renderer] pluginInstanceAddedToDocument!"); + NSLog(@"[Gyroflow Toolbox] pluginInstanceAddedToDocument!"); } //--------------------------------------------------------- @@ -223,58 +195,24 @@ - (BOOL)addParametersWithError:(NSError**)error { if (error != nil) { - NSString* description = [NSString stringWithFormat:@"[Gyroflow Toolbox Renderer] Unable to get the FxParameterCreationAPI_v5 in %s", __func__]; + NSString* description = [NSString stringWithFormat:@"[Gyroflow Toolbox] Unable to get the FxParameterCreationAPI_v5 in %s", __func__]; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_APIUnavailable userInfo:@{ NSLocalizedDescriptionKey : description }]; } return NO; } - + //--------------------------------------------------------- - // ADD PARAMETER: Drop Zone + // ADD PARAMETER: 'Launch Gyroflow' Button //--------------------------------------------------------- - if (![paramAPI addCustomParameterWithName:@"Drop Zone" - parameterID:kCB_DropZone + if (![paramAPI addCustomParameterWithName:@"Launch Gyroflow" + parameterID:kCB_LaunchGyroflow defaultValue:@0 parameterFlags:kFxParameterFlag_CUSTOM_UI | kFxParameterFlag_NOT_ANIMATABLE]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_DropZone"}; - *error = [NSError errorWithDomain:FxPlugErrorDomain - code:kFxError_InvalidParameter - userInfo:userInfo]; - } - return NO; - } - - //--------------------------------------------------------- - // ADD PARAMETER: 'Loaded Gyroflow Project' Text Box - //--------------------------------------------------------- - if (![paramAPI addStringParameterWithName:@"Loaded Gyroflow Project" - parameterID:kCB_LoadedGyroflowProject - defaultValue:@"NOTHING LOADED" - parameterFlags:kFxParameterFlag_DISABLED | kFxParameterFlag_NOT_ANIMATABLE]) - { - if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_LoadedGyroflowProject"}; - *error = [NSError errorWithDomain:FxPlugErrorDomain - code:kFxError_InvalidParameter - userInfo:userInfo]; - } - return NO; - } - - //--------------------------------------------------------- - // ADD PARAMETER: 'Loaded Gyroflow Project' Text Box - //--------------------------------------------------------- - if (![paramAPI addStringParameterWithName:@"Loaded Gyroflow Project" - parameterID:kCB_LoadedGyroflowProject - defaultValue:@"NOTHING LOADED" - parameterFlags:kFxParameterFlag_DISABLED | kFxParameterFlag_NOT_ANIMATABLE]) - { - if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_LoadedGyroflowProject"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_LaunchGyroflow"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -291,24 +229,7 @@ - (BOOL)addParametersWithError:(NSError**)error parameterFlags:kFxParameterFlag_CUSTOM_UI | kFxParameterFlag_NOT_ANIMATABLE]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_ImportGyroflowProject"}; - *error = [NSError errorWithDomain:FxPlugErrorDomain - code:kFxError_InvalidParameter - userInfo:userInfo]; - } - return NO; - } - - //--------------------------------------------------------- - // ADD PARAMETER: 'Import Last Saved Project' Button - //--------------------------------------------------------- - if (![paramAPI addCustomParameterWithName:@"Import Last Saved Project" - parameterID:kCB_LoadLastGyroflowProject - defaultValue:@0 - parameterFlags:kFxParameterFlag_CUSTOM_UI | kFxParameterFlag_NOT_ANIMATABLE]) - { - if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_LoadLastGyroflowProject"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_ImportGyroflowProject"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -317,15 +238,15 @@ - (BOOL)addParametersWithError:(NSError**)error } //--------------------------------------------------------- - // ADD PARAMETER: 'Open in Gyroflow' Button + // ADD PARAMETER: 'Loaded Gyroflow Project' Text Box //--------------------------------------------------------- - if (![paramAPI addCustomParameterWithName:@"Open in Gyroflow" - parameterID:kCB_LaunchGyroflow - defaultValue:@0 - parameterFlags:kFxParameterFlag_CUSTOM_UI | kFxParameterFlag_NOT_ANIMATABLE]) + if (![paramAPI addStringParameterWithName:@"Loaded Gyroflow Project" + parameterID:kCB_LoadedGyroflowProject + defaultValue:@"" + parameterFlags:kFxParameterFlag_DISABLED | kFxParameterFlag_NOT_ANIMATABLE]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_LaunchGyroflow"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_LoadedGyroflowProject"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -334,7 +255,7 @@ - (BOOL)addParametersWithError:(NSError**)error } //--------------------------------------------------------- - // ADD PARAMETER: 'Reload Gyroflow Project' Button + // ADD PARAMETER: 'Import Gyroflow Project' Button //--------------------------------------------------------- if (![paramAPI addCustomParameterWithName:@"Reload Gyroflow Project" parameterID:kCB_ReloadGyroflowProject @@ -342,7 +263,7 @@ - (BOOL)addParametersWithError:(NSError**)error parameterFlags:kFxParameterFlag_CUSTOM_UI | kFxParameterFlag_NOT_ANIMATABLE]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_ReloadGyroflowProject"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_ReloadGyroflowProject"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -359,7 +280,7 @@ - (BOOL)addParametersWithError:(NSError**)error parameterFlags:kFxParameterFlag_HIDDEN | kFxParameterFlag_NOT_ANIMATABLE]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_GyroflowProjectPath"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_GyroflowProjectPath"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -376,7 +297,7 @@ - (BOOL)addParametersWithError:(NSError**)error parameterFlags:kFxParameterFlag_HIDDEN | kFxParameterFlag_NOT_ANIMATABLE]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_GyroflowProjectBookmarkData"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_GyroflowProjectBookmarkData"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -393,7 +314,7 @@ - (BOOL)addParametersWithError:(NSError**)error parameterFlags:kFxParameterFlag_HIDDEN | kFxParameterFlag_NOT_ANIMATABLE]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_GyroflowProjectData"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_GyroflowProjectData"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -409,7 +330,7 @@ - (BOOL)addParametersWithError:(NSError**)error parameterFlags:kFxParameterFlag_DEFAULT]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_GyroflowParameters"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_GyroflowParameters"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -419,8 +340,6 @@ - (BOOL)addParametersWithError:(NSError**)error //--------------------------------------------------------- // ADD PARAMETER: 'FOV' Slider - // - // NOTE: 0.1 to 0.3 in Gyroflow OpenFX //--------------------------------------------------------- if (![paramAPI addFloatSliderWithName:@"FOV" parameterID:kCB_FOV @@ -433,7 +352,7 @@ - (BOOL)addParametersWithError:(NSError**)error parameterFlags:kFxParameterFlag_DEFAULT]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_FOV"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_FOV"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -443,8 +362,6 @@ - (BOOL)addParametersWithError:(NSError**)error //--------------------------------------------------------- // ADD PARAMETER: 'Smoothness' Slider - // - // NOTE: 0.01 to 3.00 in Gyroflow OpenFX //--------------------------------------------------------- if (![paramAPI addFloatSliderWithName:@"Smoothness" parameterID:kCB_Smoothness @@ -457,7 +374,7 @@ - (BOOL)addParametersWithError:(NSError**)error parameterFlags:kFxParameterFlag_DEFAULT]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_Smoothness"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_Smoothness"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -467,37 +384,10 @@ - (BOOL)addParametersWithError:(NSError**)error //--------------------------------------------------------- // ADD PARAMETER: 'Lens Correction' Slider - // - // NOTE: In the Gyroflow user interface it shows 0 to 100, - // however internally it's actually 0 to 1. //--------------------------------------------------------- if (![paramAPI addFloatSliderWithName:@"Lens Correction" parameterID:kCB_LensCorrection - defaultValue:0.0 - parameterMin:0.0 - parameterMax:100.0 - sliderMin:0.0 - sliderMax:100.0 - delta:0.1 - parameterFlags:kFxParameterFlag_DEFAULT]) - { - if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_LensCorrection"}; - *error = [NSError errorWithDomain:FxPlugErrorDomain - code:kFxError_InvalidParameter - userInfo:userInfo]; - } - return NO; - } - - //--------------------------------------------------------- - // ADD PARAMETER: 'Horizon Lock' Slider - // - // NOTE: 0 to 100 in Gyroflow OpenFX - //--------------------------------------------------------- - if (![paramAPI addFloatSliderWithName:@"Horizon Lock" - parameterID:kCB_HorizonLock - defaultValue:0.0 + defaultValue:100.0 parameterMin:0.0 parameterMax:100.0 sliderMin:0.0 @@ -506,145 +396,21 @@ - (BOOL)addParametersWithError:(NSError**)error parameterFlags:kFxParameterFlag_DEFAULT]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_HorizonLock"}; - *error = [NSError errorWithDomain:FxPlugErrorDomain - code:kFxError_InvalidParameter - userInfo:userInfo]; - } - return NO; - } - - //--------------------------------------------------------- - // ADD PARAMETER: 'Horizon Roll' Slider - // - // NOTE: -100 to 100 in Gyroflow OpenFX - //--------------------------------------------------------- - if (![paramAPI addFloatSliderWithName:@"Horizon Roll" - parameterID:kCB_HorizonRoll - defaultValue:0.0 - parameterMin:-100.0 - parameterMax:100.0 - sliderMin:-100.0 - sliderMax:100.0 - delta:0.1 - parameterFlags:kFxParameterFlag_DEFAULT]) - { - if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_HorizonRoll"}; - *error = [NSError errorWithDomain:FxPlugErrorDomain - code:kFxError_InvalidParameter - userInfo:userInfo]; - } - return NO; - } - - //--------------------------------------------------------- - // ADD PARAMETER: 'Position Offset X' Slider - // - // NOTE: -100 to 100 in Gyroflow OpenFX - //--------------------------------------------------------- - if (![paramAPI addFloatSliderWithName:@"Position Offset X" - parameterID:kCB_PositionOffsetX - defaultValue:0.0 - parameterMin:-100.0 - parameterMax:100.0 - sliderMin:-100.0 - sliderMax:100.0 - delta:0.1 - parameterFlags:kFxParameterFlag_DEFAULT]) - { - if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_PositionOffsetX"}; - *error = [NSError errorWithDomain:FxPlugErrorDomain - code:kFxError_InvalidParameter - userInfo:userInfo]; - } - return NO; - } - - //--------------------------------------------------------- - // ADD PARAMETER: 'Position Offset Y' Slider - // - // NOTE: -100 to 100 in Gyroflow OpenFX - //--------------------------------------------------------- - if (![paramAPI addFloatSliderWithName:@"Position Offset Y" - parameterID:kCB_PositionOffsetY - defaultValue:0.0 - parameterMin:-100.0 - parameterMax:100.0 - sliderMin:-100.0 - sliderMax:100.0 - delta:0.1 - parameterFlags:kFxParameterFlag_DEFAULT]) - { - if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_PositionOffsetY"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_LensCorrection"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; } return NO; } - - //--------------------------------------------------------- - // ADD PARAMETER: 'Video Rotation' Slider - // - // Resolve UI: -360 to 360 - // Gyroflow UI: TBC - // Internally: TBC - //--------------------------------------------------------- - if (![paramAPI addFloatSliderWithName:@"Video Rotation" - parameterID:kCB_VideoRotation - defaultValue:0.0 - parameterMin:-360.0 - parameterMax:360.0 - sliderMin:-360.0 - sliderMax:360.0 - delta:0.1 - parameterFlags:kFxParameterFlag_DEFAULT]) - { - if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_VideoRotation"}; - *error = [NSError errorWithDomain:FxPlugErrorDomain - code:kFxError_InvalidParameter - userInfo:userInfo]; - } - return NO; - } - - //--------------------------------------------------------- - // ADD PARAMETER: 'Video Speed' Slider - // - // Resolve UI: 0 to 1000 - // Gyroflow UI: 10 to 1000 - // Internally: 0.1 to 10 - //--------------------------------------------------------- - if (![paramAPI addFloatSliderWithName:@"Video Speed" - parameterID:kCB_VideoSpeed - defaultValue:100.0 - parameterMin:10.0 - parameterMax:1000.0 - sliderMin:10.0 - sliderMax:1000.0 - delta:1 - parameterFlags:kFxParameterFlag_DEFAULT]) - { - if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_VideoSpeed"}; - *error = [NSError errorWithDomain:FxPlugErrorDomain - code:kFxError_InvalidParameter - userInfo:userInfo]; - } - return NO; - } - + //--------------------------------------------------------- // END GROUP: 'Gyroflow Parameters' //--------------------------------------------------------- if (![paramAPI endParameterSubGroup]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add end 'Gyroflow Parameters' Parameter"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add end 'Gyroflow Parameters' Parameter"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -680,7 +446,7 @@ - (BOOL)pluginState:(NSData**)pluginState //--------------------------------------------------------- id timingAPI = [_apiManager apiForProtocol:@protocol(FxTimingAPI_v4)]; if (timingAPI == nil) { - NSLog(@"[Gyroflow Toolbox Renderer] Unable to retrieve FxTimingAPI_v4 in pluginStateAtTime."); + NSLog(@"[Gyroflow Toolbox] Unable to retrieve FxTimingAPI_v4 in pluginStateAtTime."); if (error != NULL) { *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_FailedToLoadTimingAPI @@ -697,7 +463,7 @@ - (BOOL)pluginState:(NSData**)pluginState //--------------------------------------------------------- id paramGetAPI = [_apiManager apiForProtocol:@protocol(FxParameterRetrievalAPI_v6)]; if (paramGetAPI == nil) { - NSLog(@"[Gyroflow Toolbox Renderer] Unable to retrieve FxParameterRetrievalAPI_v6 in pluginStateAtTime."); + NSLog(@"[Gyroflow Toolbox] Unable to retrieve FxParameterRetrievalAPI_v6 in pluginStateAtTime."); if (error != NULL) { *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_FailedToLoadParameterGetAPI @@ -723,18 +489,18 @@ - (BOOL)pluginState:(NSData**)pluginState CMTime timelineTime = kCMTimeZero; [timingAPI timelineTime:&timelineTime fromInputTime:renderTime]; - + CMTime startTimeOfInputToFilter = kCMTimeZero; [timingAPI startTimeForEffect:&startTimeOfInputToFilter]; - + CMTime startTimeOfInputToFilterInTimelineTime = kCMTimeZero; [timingAPI timelineTime:&startTimeOfInputToFilterInTimelineTime fromInputTime:startTimeOfInputToFilter]; - + Float64 timelineTimeMinusStartTimeOfInputToFilterNumerator = (Float64)timelineTime.value * (Float64)startTimeOfInputToFilterInTimelineTime.timescale - (Float64)startTimeOfInputToFilterInTimelineTime.value * (Float64)timelineTime.timescale; Float64 timelineTimeMinusStartTimeOfInputToFilterDenominator = (Float64)timelineTime.timescale * (Float64)startTimeOfInputToFilterInTimelineTime.timescale; Float64 frame = ( ((Float64)timelineTimeMinusStartTimeOfInputToFilterNumerator / (Float64)timelineTimeMinusStartTimeOfInputToFilterDenominator) / ((Float64)timelineFrameDuration.value / (Float64)timelineFrameDuration.timescale) ); - + //--------------------------------------------------------- // Calculate the Timestamp: //--------------------------------------------------------- @@ -744,21 +510,6 @@ - (BOOL)pluginState:(NSData**)pluginState Float64 timestamp = (frame / frameRate) * 1000000.0; params.timestamp = [[[NSNumber alloc] initWithFloat:timestamp] autorelease]; - - NSLog(@"---------------------------------"); - NSLog(@"timelineFrameDuration: %.2f seconds", CMTimeGetSeconds(timelineFrameDuration)); - NSLog(@"timelineTime: %.2f seconds", CMTimeGetSeconds(timelineTime)); - NSLog(@"startTimeOfInputToFilter: %.2f seconds", CMTimeGetSeconds(startTimeOfInputToFilter)); - NSLog(@"startTimeOfInputToFilterInTimelineTime: %.2f seconds", CMTimeGetSeconds(startTimeOfInputToFilterInTimelineTime)); - NSLog(@"timelineTimeMinusStartTimeOfInputToFilterNumerator: %f", timelineTimeMinusStartTimeOfInputToFilterNumerator); - NSLog(@"timelineTimeMinusStartTimeOfInputToFilterDenominator: %f", timelineTimeMinusStartTimeOfInputToFilterDenominator); - NSLog(@"frame: %f", frame); - NSLog(@"timelineFpsNumerator: %f", timelineFpsNumerator); - NSLog(@"timelineFpsDenominator: %f", timelineFpsDenominator); - NSLog(@"frameRate: %f", frameRate); - NSLog(@"timestamp: %f", timestamp); - NSLog(@"---------------------------------"); - //--------------------------------------------------------- // Gyroflow Path: //--------------------------------------------------------- @@ -793,48 +544,6 @@ - (BOOL)pluginState:(NSData**)pluginState double lensCorrection; [paramGetAPI getFloatValue:&lensCorrection fromParameter:kCB_LensCorrection atTime:renderTime]; params.lensCorrection = [NSNumber numberWithDouble:lensCorrection]; - - //--------------------------------------------------------- - // Horizon Lock: - //--------------------------------------------------------- - double horizonLock; - [paramGetAPI getFloatValue:&horizonLock fromParameter:kCB_HorizonLock atTime:renderTime]; - params.horizonLock = [NSNumber numberWithDouble:horizonLock]; - - //--------------------------------------------------------- - // Horizon Roll: - //--------------------------------------------------------- - double horizonRoll; - [paramGetAPI getFloatValue:&horizonRoll fromParameter:kCB_HorizonRoll atTime:renderTime]; - params.horizonRoll = [NSNumber numberWithDouble:horizonRoll]; - - //--------------------------------------------------------- - // Position Offset X: - //--------------------------------------------------------- - double positionOffsetX; - [paramGetAPI getFloatValue:&positionOffsetX fromParameter:kCB_PositionOffsetX atTime:renderTime]; - params.positionOffsetX = [NSNumber numberWithDouble:positionOffsetX]; - - //--------------------------------------------------------- - // Position Offset Y: - //--------------------------------------------------------- - double positionOffsetY; - [paramGetAPI getFloatValue:&positionOffsetY fromParameter:kCB_PositionOffsetY atTime:renderTime]; - params.positionOffsetY = [NSNumber numberWithDouble:positionOffsetY]; - - //--------------------------------------------------------- - // Video Rotation: - //--------------------------------------------------------- - double videoRotation; - [paramGetAPI getFloatValue:&videoRotation fromParameter:kCB_VideoRotation atTime:renderTime]; - params.videoRotation = [NSNumber numberWithDouble:videoRotation]; - - //--------------------------------------------------------- - // Video Speed: - //--------------------------------------------------------- - double videoSpeed; - [paramGetAPI getFloatValue:&videoSpeed fromParameter:kCB_VideoSpeed atTime:renderTime]; - params.videoSpeed = [NSNumber numberWithDouble:videoSpeed]; //--------------------------------------------------------- // Write the parameters to the pluginState as `NSData`: @@ -843,7 +552,7 @@ - (BOOL)pluginState:(NSData**)pluginState NSData *newPluginState = [NSKeyedArchiver archivedDataWithRootObject:params requiringSecureCoding:YES error:&newPluginStateError]; if (newPluginState == nil) { if (error != NULL) { - NSString* errorMessage = [NSString stringWithFormat:@"[Gyroflow Toolbox Renderer] ERROR - Failed to create newPluginState due to '%@'", [newPluginStateError localizedDescription]]; + NSString* errorMessage = [NSString stringWithFormat:@"[Gyroflow Toolbox] ERROR - Failed to create newPluginState due to '%@'", [newPluginStateError localizedDescription]]; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_FailedToCreatePluginState userInfo:@{ NSLocalizedDescriptionKey : errorMessage }]; @@ -858,7 +567,7 @@ - (BOOL)pluginState:(NSData**)pluginState } else { *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_PlugInStateIsNil - userInfo:@{ NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] pluginState is nil in -pluginState." }]; + userInfo:@{ NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] pluginState is nil in -pluginState." }]; succeeded = NO; } @@ -889,7 +598,7 @@ - (BOOL)destinationImageRect:(FxRect *)destinationImageRect if (outError != NULL) { *outError = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_ThirdPartyDeveloperStart + 5 - userInfo:@{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] FATAL ERROR - No sourceImages in -destinationImageRect."}]; + userInfo:@{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] FATAL ERROR - No sourceImages in -destinationImageRect."}]; } return NO; } @@ -949,7 +658,7 @@ - (BOOL)renderDestinationImage:(FxImageTile *)destinationImage if (outError != NULL) { *outError = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter - userInfo:@{ NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] FATAL ERROR - Invalid plugin state received from host." }]; + userInfo:@{ NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] FATAL ERROR - Invalid plugin state received from host." }]; } return NO; } @@ -961,7 +670,7 @@ - (BOOL)renderDestinationImage:(FxImageTile *)destinationImage NSError *paramsError; GyroflowParameters *params = [NSKeyedUnarchiver unarchivedObjectOfClass:[GyroflowParameters class] fromData:pluginState error:¶msError]; if (params == nil) { - NSString *errorMessage = [NSString stringWithFormat:@"[Gyroflow Toolbox Renderer] FATAL ERROR - Parameters was nil in -renderDestinationImage due to '%@'.", [paramsError localizedDescription]]; + NSString *errorMessage = [NSString stringWithFormat:@"[Gyroflow Toolbox] FATAL ERROR - Parameters was nil in -renderDestinationImage due to '%@'.", [paramsError localizedDescription]]; if (outError != NULL) { *outError = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter @@ -969,6 +678,12 @@ - (BOOL)renderDestinationImage:(FxImageTile *)destinationImage } return NO; } + + //--------------------------------------------------------- + // Get output width and height: + //--------------------------------------------------------- + float outputWidth = (float)(destinationImage.tilePixelBounds.right - destinationImage.tilePixelBounds.left); + float outputHeight = (float)(destinationImage.tilePixelBounds.top - destinationImage.tilePixelBounds.bottom); //--------------------------------------------------------- // Get the parameter data: @@ -980,13 +695,6 @@ - (BOOL)renderDestinationImage:(FxImageTile *)destinationImage NSNumber *smoothness = params.smoothness; NSNumber *lensCorrection = params.lensCorrection; - NSNumber *horizonLock = params.horizonLock; - NSNumber *horizonRoll = params.horizonRoll; - NSNumber *positionOffsetX = params.positionOffsetX; - NSNumber *positionOffsetY = params.positionOffsetY; - NSNumber *videoRotation = params.videoRotation; - NSNumber *videoSpeed = params.videoSpeed; - //--------------------------------------------------------- // Set up the renderer, in this case we are using Metal. //--------------------------------------------------------- @@ -1011,7 +719,7 @@ - (BOOL)renderDestinationImage:(FxImageTile *)destinationImage if (outError != NULL) { *outError = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter - userInfo:@{ NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] FATAL ERROR - Unable to get command queue. May need to increase cache size." }]; + userInfo:@{ NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] FATAL ERROR - Unable to get command queue. May need to increase cache size." }]; } return NO; } @@ -1022,11 +730,6 @@ - (BOOL)renderDestinationImage:(FxImageTile *)destinationImage id inputDevice = [deviceCache deviceWithRegistryID:sourceImages[0].deviceRegistryID]; id inputTexture = [sourceImages[0] metalTextureForDevice:inputDevice]; - //--------------------------------------------------------- - // Setup our output texture: - //--------------------------------------------------------- - id outputTexture = [destinationImage metalTextureForDevice:[deviceCache deviceWithRegistryID:destinationImage.deviceRegistryID]]; - //--------------------------------------------------------- // Determine the Pixel Format: //--------------------------------------------------------- @@ -1042,7 +745,7 @@ - (BOOL)renderDestinationImage:(FxImageTile *)destinationImage inputPixelFormat = @"RGBAf"; numberOfBytes = 4; } else { - NSString *errorMessage = [NSString stringWithFormat:@"[Gyroflow Toolbox Renderer] BUG - Unsupported pixelFormat for inputTexture: %lu", (unsigned long)inputTexture.pixelFormat]; + NSString *errorMessage = [NSString stringWithFormat:@"[Gyroflow Toolbox] BUG - Unsupported pixelFormat for inputTexture: %lu", (unsigned long)inputTexture.pixelFormat]; if (outError != NULL) { *outError = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_UnsupportedPixelFormat @@ -1062,14 +765,8 @@ - (BOOL)renderDestinationImage:(FxImageTile *)destinationImage int64_t sourceTimestamp = [timestamp floatValue]; double sourceFOV = [fov doubleValue]; double sourceSmoothness = [smoothness doubleValue]; - double sourceLensCorrection = [lensCorrection doubleValue] / 100.0; - double sourceHorizonLock = [horizonLock doubleValue]; - double sourceHorizonRoll = [horizonRoll doubleValue]; - double sourcePositionOffsetX = [positionOffsetX doubleValue]; - double sourcePositionOffsetY = [positionOffsetY doubleValue]; - double sourceVideoRotation = [videoRotation doubleValue]; - double sourceVideoSpeed = [videoSpeed doubleValue]; - + double sourceLensCorrection = [lensCorrection doubleValue]; + //--------------------------------------------------------- // Only trigger the Rust function if we have Gyroflow Data: //--------------------------------------------------------- @@ -1088,25 +785,19 @@ - (BOOL)renderDestinationImage:(FxImageTile *)destinationImage sourceFOV, // double sourceSmoothness, // double sourceLensCorrection, // double - sourceHorizonLock, // double - sourceHorizonRoll, // double - sourcePositionOffsetX, // double - sourcePositionOffsetY, // double - sourceVideoRotation, // double - sourceVideoSpeed, // double inputTexture, // MTLTexture - outputTexture, // MTLTexture + inputTexture, // MTLTexture commandQueue // MTLCommandQueue ); NSString *resultString = [NSString stringWithUTF8String: result]; - //NSLog(@"[Gyroflow Toolbox Renderer] resultString: %@", resultString); + //NSLog(@"[Gyroflow Toolbox] resultString: %@", resultString); if ([resultString isEqualToString:@"FAIL"]) { //--------------------------------------------------------- // If we get a "FAIL" then abort: //--------------------------------------------------------- - NSString *errorMessage = @"[Gyroflow Toolbox Renderer] A fail message was received from the Rust function."; + NSString *errorMessage = @"[Gyroflow Toolbox] A fail message was received from the Rust function."; if (outError != NULL) { *outError = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter @@ -1120,7 +811,7 @@ - (BOOL)renderDestinationImage:(FxImageTile *)destinationImage // Debugging: //--------------------------------------------------------- /* - NSString *debugMessage = [NSString stringWithFormat:@"[Gyroflow Toolbox Renderer] RENDERING A FRAME:\n"]; + NSString *debugMessage = [NSString stringWithFormat:@"[Gyroflow Toolbox] RENDERING A FRAME:\n"]; debugMessage = [debugMessage stringByAppendingFormat:@"processFrame result: %@\n", resultString]; debugMessage = [debugMessage stringByAppendingFormat:@"inputTexture.width: %lu\n", (unsigned long)inputTexture.width]; debugMessage = [debugMessage stringByAppendingFormat:@"inputTexture.height: %lu\n", (unsigned long)inputTexture.height]; @@ -1134,768 +825,214 @@ - (BOOL)renderDestinationImage:(FxImageTile *)destinationImage NSLog(@"%@", debugMessage); */ - //NSLog(@"[Gyroflow Toolbox Renderer] inputTexture.debugDescription: %@", inputTexture.debugDescription); - - //--------------------------------------------------------- - // Return the command queue back into the cache, - // so we can re-use it again: - //--------------------------------------------------------- - [deviceCache returnCommandQueueToCache:commandQueue]; - - //--------------------------------------------------------- - // Release the Input Textures: - //--------------------------------------------------------- - //[inputTexture setPurgeableState:MTLPurgeableStateEmpty]; - - return YES; -} + //NSLog(@"[Gyroflow Toolbox] inputTexture.debugDescription: %@", inputTexture.debugDescription); -//--------------------------------------------------------- -// -#pragma mark - Buttons -// -//--------------------------------------------------------- - -//--------------------------------------------------------- -// Custom Button View Pressed: -//--------------------------------------------------------- -- (void)customButtonViewPressed:(UInt32)buttonID -{ - if (buttonID == kCB_LaunchGyroflow) { - [self buttonLaunchGyroflow]; - } else if (buttonID == kCB_LoadLastGyroflowProject) { - [self buttonLoadLastGyroflowProject]; - } else if (buttonID == kCB_ImportGyroflowProject) { - [self buttonImportGyroflowProject]; - } else if (buttonID == kCB_ReloadGyroflowProject) { - [self buttonReloadGyroflowProject]; - } -} - -//--------------------------------------------------------- -// BUTTON: 'Launch Gyroflow' -//--------------------------------------------------------- -- (void)buttonLaunchGyroflow { - //--------------------------------------------------------- - // Load the Custom Parameter Action API: //--------------------------------------------------------- - id actionAPI = [_apiManager apiForProtocol:@protocol(FxCustomParameterActionAPI_v4)]; - if (actionAPI == nil) { - [self showAlertWithMessage:@"An error has occurred." info:@"Unable to retrieve 'FxCustomParameterActionAPI_v4'. This shouldn't happen, so it's probably a bug."]; - return; - } - - //--------------------------------------------------------- - // Use the Action API to allow us to change the parameters: + // Setup our output texture: //--------------------------------------------------------- - [actionAPI startAction:self]; + id outputTexture = [destinationImage metalTextureForDevice:[deviceCache deviceWithRegistryID:destinationImage.deviceRegistryID]]; //--------------------------------------------------------- - // Load the Parameter Retrieval API: + // Setup our Color Attachment Descriptor. + // + // MTLRenderPassColorAttachmentDescriptor: A color render + // target that serves as the output destination for color + // pixels generated by a render pass. //--------------------------------------------------------- - id paramGetAPI = [_apiManager apiForProtocol:@protocol(FxParameterRetrievalAPI_v6)]; - if (paramGetAPI == nil) { - //--------------------------------------------------------- - // Stop Action API: - //--------------------------------------------------------- - [actionAPI endAction:self]; - - [self showAlertWithMessage:@"An error has occurred." info:@"Unable to retrieve 'FxParameterRetrievalAPI_v6'.\n\nThis shouldn't happen, so it's probably a bug."]; - return; - } + MTLRenderPassColorAttachmentDescriptor* colorAttachmentDescriptor = [[MTLRenderPassColorAttachmentDescriptor alloc] init]; + colorAttachmentDescriptor.texture = outputTexture; //--------------------------------------------------------- - // Get the existing Gyroflow project path: + // If the loadAction property of the attachment is set to + // MTLLoadActionClear, then at the start of a render pass, + // the GPU fills the texture with the value stored in the + // clearColor property. Otherwise, the GPU ignores the + // clearColor property. + // + // The clearColor property represents a set of RGBA + // components. The default value is: + // + // (0.0, 0.0, 0.0, 1.0) (black). + // + // Use the MTLClearColorMake function to construct + // a MTLClearColor value. //--------------------------------------------------------- - NSString *existingProjectPath = nil; - [paramGetAPI getStringParameterValue:&existingProjectPath fromParameter:kCB_GyroflowProjectPath]; + colorAttachmentDescriptor.clearColor = MTLClearColorMake(1.0, 0.5, 0.0, 1.0); - NSURL *existingProjectURL = [NSURL fileURLWithPath:existingProjectPath]; - //--------------------------------------------------------- - // Open Gyroflow or the current Gyroflow Project: + // Types of actions performed for an attachment at the + // start of a rendering pass: + // + // * MTLLoadActionDontCare + // The GPU has permission to discard the existing + // contents of the attachment at the start of the + // render pass, replacing them with arbitrary data. + // + // * MTLLoadActionLoad + // The GPU preserves the existing contents of the + // attachment at the start of the render pass. + // + // * MTLLoadActionClear + // The GPU writes a value to every pixel in the + // attachment at the start of the render pass. //--------------------------------------------------------- - NSString *bundleIdentifier = @"xyz.gyroflow"; - NSURL *appURL = [[NSWorkspace sharedWorkspace] URLForApplicationWithBundleIdentifier:bundleIdentifier]; - - if (appURL == nil) { - NSLog(@"[Gyroflow Toolbox Renderer] Could not find Gyroflow Installation"); - [actionAPI endAction:self]; - [self showAlertWithMessage:@"Failed to launch Gyroflow." info:@"Please check that Gyroflow is installed and try again."]; - return; - } - - if (existingProjectPath == nil || [existingProjectPath isEqualToString:@""] || existingProjectURL == nil) { - NSLog(@"[Gyroflow Toolbox Renderer] Could not find existing project."); - [[NSWorkspace sharedWorkspace] openURL:appURL]; - } else { - //--------------------------------------------------------- - // Get the encoded bookmark string: - //--------------------------------------------------------- - NSString *encodedBookmark; - [paramGetAPI getStringParameterValue:&encodedBookmark fromParameter:kCB_GyroflowProjectBookmarkData]; - - //--------------------------------------------------------- - // Make sure there's actually encoded bookmark data: - //--------------------------------------------------------- - if ([encodedBookmark isEqualToString:@""]) { - NSLog(@"[Gyroflow Toolbox Renderer] Encoded Bookmark is empty."); - [[NSWorkspace sharedWorkspace] openURL:appURL]; - [actionAPI endAction:self]; - return; - } - - //--------------------------------------------------------- - // Decode the Base64 bookmark data: - //--------------------------------------------------------- - NSData *decodedBookmark = [[[NSData alloc] initWithBase64EncodedString:encodedBookmark - options:0] autorelease]; - - //--------------------------------------------------------- - // Resolve the decoded bookmark data into a - // security-scoped URL: - //--------------------------------------------------------- - NSError *bookmarkError = nil; - BOOL isStale = NO; - - NSURL *url = [NSURL URLByResolvingBookmarkData:decodedBookmark - options:NSURLBookmarkResolutionWithSecurityScope - relativeToURL:nil - bookmarkDataIsStale:&isStale - error:&bookmarkError]; - - if (bookmarkError != nil) { - NSLog(@"[Gyroflow Toolbox Renderer] Bookmark error: %@", bookmarkError.localizedDescription); - [[NSWorkspace sharedWorkspace] openURL:appURL]; - [actionAPI endAction:self]; - return; - } - - //--------------------------------------------------------- - // Read the Gyroflow Project Data from File: - //--------------------------------------------------------- - if (![url startAccessingSecurityScopedResource]) { - NSLog(@"[Gyroflow Toolbox Renderer] Failed to start security scoped resource: %@", url); - [[NSWorkspace sharedWorkspace] openURL:appURL]; - [actionAPI endAction:self]; - return; - } - - //--------------------------------------------------------- - // There is an existing project path, so load Gyroflow - // with that path: - //--------------------------------------------------------- - //NSURL *appURL = [[NSWorkspace sharedWorkspace] URLForApplicationWithBundleIdentifier:bundleIdentifier]; - //NSWorkspaceOpenConfiguration *config = [[[NSWorkspaceOpenConfiguration alloc] init] autorelease]; - //[[NSWorkspace sharedWorkspace] openURLs:@[url] withApplicationAtURL:appURL configuration:config completionHandler:nil]; - - // NOTE TO SELF: For some dumb reason the above fails with a sandboxing error, but the below works: - - [[NSWorkspace sharedWorkspace] openURL:url]; - - [url stopAccessingSecurityScopedResource]; - } + colorAttachmentDescriptor.loadAction = MTLLoadActionClear; //--------------------------------------------------------- - // Stop Action API: + // Setup our Render Pass Descriptor. + // + // MTLRenderPassDescriptor: A group of render targets that + // hold the results of a render pass. //--------------------------------------------------------- - [actionAPI endAction:self]; -} - -//--------------------------------------------------------- -// BUTTON: 'Import Gyroflow Project' -//--------------------------------------------------------- -- (void)buttonImportGyroflowProject { - [self importGyroflowProjectWithOptionalURL:nil]; -} - -//--------------------------------------------------------- -// BUTTON: 'Load Last Gyroflow Project' -//--------------------------------------------------------- -- (void)buttonLoadLastGyroflowProject { - NSLog(@"[Gyroflow Toolbox Renderer] Load Last Gyroflow Project Pressed!"); + MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; + renderPassDescriptor.colorAttachments[0] = colorAttachmentDescriptor; - if ([self canReadGyroflowPreferencesFile]) { - //--------------------------------------------------------- - // We can read the Gyroflow Preferences file: - //--------------------------------------------------------- - NSLog(@"[Gyroflow Toolbox Renderer] We can read the preferences file."); - [self readLastProjectFromGyroflowPreferencesFile]; - } else { - //--------------------------------------------------------- - // We can't read the Gyroflow Preferences file, so lets - // try get sandbox access: - //--------------------------------------------------------- - NSLog(@"[Gyroflow Toolbox Renderer] We can't read the preferences file."); - NSURL* gyroflowPlistURL = [self getGyroflowPreferencesFileURL]; - [self requestSandboxAccessWithURL:gyroflowPlistURL]; - } -} - -//--------------------------------------------------------- -// BUTTON: 'Reload Gyroflow Project' -//--------------------------------------------------------- -- (void)buttonReloadGyroflowProject { - //--------------------------------------------------------- - // Load the Custom Parameter Action API: - //--------------------------------------------------------- - id actionAPI = [_apiManager apiForProtocol:@protocol(FxCustomParameterActionAPI_v4)]; - if (actionAPI == nil) { - [self showAlertWithMessage:@"An error has occurred." info:@"Unable to retrieve 'FxCustomParameterActionAPI_v4'. This shouldn't happen, so it's probably a bug."]; - return; - } - //--------------------------------------------------------- - // Use the Action API to allow us to change the parameters: + // Create a new Command Buffer: //--------------------------------------------------------- - [actionAPI startAction:self]; + id commandBuffer = [commandQueue commandBuffer]; + commandBuffer.label = @"GyroFlow Command Buffer"; + [commandBuffer enqueue]; //--------------------------------------------------------- - // Load the Parameter Retrieval API: + // Setup our Command Encoder. + // + // renderCommandEncoderWithDescriptor: Creates an object + // from a descriptor to encode a rendering pass into the + // command buffer. //--------------------------------------------------------- - id paramGetAPI = [_apiManager apiForProtocol:@protocol(FxParameterRetrievalAPI_v6)]; - if (paramGetAPI == nil) { - [self showAlertWithMessage:@"An error has occurred." info:@"Unable to retrieve 'FxParameterRetrievalAPI_v6'.\n\nThis shouldn't happen, so it's probably a bug."]; - return; - } + id commandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; //--------------------------------------------------------- - // Get the encoded bookmark string: + // Calculate the vertex coordinates and the texture + // coordinates: //--------------------------------------------------------- - NSString *encodedBookmark; - [paramGetAPI getStringParameterValue:&encodedBookmark fromParameter:kCB_GyroflowProjectBookmarkData]; + Vertex2D vertices[] = { + { { outputWidth / 2.0, -outputHeight / 2.0 }, { 1.0, 1.0 } }, + { { -outputWidth / 2.0, -outputHeight / 2.0 }, { 0.0, 1.0 } }, + { { outputWidth / 2.0, outputHeight / 2.0 }, { 1.0, 0.0 } }, + { { -outputWidth / 2.0, outputHeight / 2.0 }, { 0.0, 0.0 } } + }; //--------------------------------------------------------- - // Make sure there's actually encoded bookmark data: + // Setup our viewport: + // + // MTLViewport: A 3D rectangular region for the viewport + // clipping. //--------------------------------------------------------- - if ([encodedBookmark isEqualToString:@""]) { - //--------------------------------------------------------- - // Stop Action API: - //--------------------------------------------------------- - [actionAPI endAction:self]; - - [self showAlertWithMessage:@"An error has occurred." info:@"There's no previous security-scoped bookmark data found.\n\nPlease make sure you import a Gyroflow Project before attempting to reload."]; - return; - } + MTLViewport viewport = { 0, 0, outputWidth, outputHeight, -1.0, 1.0 }; //--------------------------------------------------------- - // Decode the Base64 bookmark data: - //--------------------------------------------------------- - NSData *decodedBookmark = [[[NSData alloc] initWithBase64EncodedString:encodedBookmark - options:0] autorelease]; - - //--------------------------------------------------------- - // Resolve the decoded bookmark data into a - // security-scoped URL: + // Sets the viewport used for transformations and clipping. //--------------------------------------------------------- - NSError *bookmarkError = nil; - BOOL isStale = NO; + [commandEncoder setViewport:viewport]; - NSURL *url = [NSURL URLByResolvingBookmarkData:decodedBookmark - options:NSURLBookmarkResolutionWithSecurityScope - relativeToURL:nil - bookmarkDataIsStale:&isStale - error:&bookmarkError]; - - if (bookmarkError != nil) { - //--------------------------------------------------------- - // Stop Action API: - //--------------------------------------------------------- - [actionAPI endAction:self]; - - [self showAlertWithMessage:@"An error has occurred." info:[NSString stringWithFormat:@"Failed to resolve bookmark due to:\n\n%@", [bookmarkError localizedDescription]]]; - return; - } - - //--------------------------------------------------------- - // Load the Parameter Set API: //--------------------------------------------------------- - id paramSetAPI = [_apiManager apiForProtocol:@protocol(FxParameterSettingAPI_v5)]; - if (paramSetAPI == nil) - { - //--------------------------------------------------------- - // Stop Action API: - //--------------------------------------------------------- - [actionAPI endAction:self]; - - [self showAlertWithMessage:@"An error has occurred." info:@"Unable to retrieve 'FxParameterSettingAPI_v5'.\n\nThis shouldn't happen, so it's probably a bug."]; - return; - } - - //--------------------------------------------------------- - // Read the Gyroflow Project Data from File: + // Setup our Render Pipeline State. + // + // MTLRenderPipelineState: An object that contains graphics + // functions and configuration state to use in a render + // command. //--------------------------------------------------------- - [url startAccessingSecurityScopedResource]; + id pipelineState = [deviceCache pipelineStateWithRegistryID:sourceImages[0].deviceRegistryID + pixelFormat:pixelFormat]; - NSError *readError = nil; - NSString *selectedGyroflowProjectData = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&readError]; - [url stopAccessingSecurityScopedResource]; - if (readError != nil) { - //--------------------------------------------------------- - // Stop Action API: - //--------------------------------------------------------- - [actionAPI endAction:self]; - - [self showAlertWithMessage:@"An error has occurred." info:[NSString stringWithFormat:@"Failed to read Gyroflow Project file due to:\n\n%@", [readError localizedDescription]]]; - return; - } - - //--------------------------------------------------------- - // Update 'Gyroflow Project Data': //--------------------------------------------------------- - [paramSetAPI setParameterFlags:kFxParameterFlag_DEFAULT toParameter:kCB_GyroflowProjectData]; - [paramSetAPI setStringParameterValue:selectedGyroflowProjectData toParameter:kCB_GyroflowProjectData]; - [paramSetAPI setParameterFlags:kFxParameterFlag_HIDDEN toParameter:kCB_GyroflowProjectData]; - + // Sets the current render pipeline state object: //--------------------------------------------------------- - // Stop Action API: - //--------------------------------------------------------- - [actionAPI endAction:self]; + [commandEncoder setRenderPipelineState:pipelineState]; //--------------------------------------------------------- - // Show success message: + // Sets a block of data for the vertex shader: //--------------------------------------------------------- - [self showAlertWithMessage:@"Success!" info:@"The Gyroflow Project has been successfully reloaded from disk."]; -} - -//--------------------------------------------------------- -// -#pragma mark - Open Last Gyroflow Project -// -//--------------------------------------------------------- - -//--------------------------------------------------------- -// Request Sandbox access to the Gyroflow Preferences file: -//--------------------------------------------------------- -- (void)requestSandboxAccessWithURL:(NSURL*)gyroflowPlistURL { - //--------------------------------------------------------- - // Show popup with instructions: - //--------------------------------------------------------- - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - alert.alertStyle = NSAlertStyleInformational; - alert.messageText = @"Permission Required"; - alert.informativeText = @"Gyroflow Toolbox requires explicit permission to access your Gyroflow Preferences, so that it can determine the last opened project.\n\nPlease click 'Grant Access' on the next Open Folder window to continue."; - [alert beginSheetModalForWindow:loadLastGyroflowProjectView.window completionHandler:^(NSModalResponse result){ - //--------------------------------------------------------- - // Display an open panel: - //--------------------------------------------------------- - UTType *plistType = [UTType typeWithIdentifier:@"com.apple.property-list"]; - NSArray *allowedContentTypes = [NSArray arrayWithObject:plistType]; - - NSOpenPanel* panel = [NSOpenPanel openPanel]; - [panel setCanChooseDirectories:NO]; - [panel setCanCreateDirectories:NO]; - [panel setCanChooseFiles:YES]; - [panel setAllowsMultipleSelection:NO]; - [panel setDirectoryURL:gyroflowPlistURL]; - [panel setAllowedContentTypes:allowedContentTypes]; - [panel setPrompt:@"Grant Access"]; - [panel setMessage:@"Please click 'Grant Access' to allow access to the Gyroflow Preferences file:"]; - - [panel beginSheetModalForWindow:loadLastGyroflowProjectView.window completionHandler:^(NSModalResponse result){ - if (result != NSModalResponseOK) { - return; - } - - NSURL *url = [panel URL]; - [url startAccessingSecurityScopedResource]; - - //--------------------------------------------------------- - // Create a new app-scope security-scoped bookmark for - // future sessions: - //--------------------------------------------------------- - NSError *bookmarkError = nil; - NSURLBookmarkCreationOptions bookmarkOptions = NSURLBookmarkCreationWithSecurityScope; - NSData *bookmark = [url bookmarkDataWithOptions:bookmarkOptions - includingResourceValuesForKeys:nil - relativeToURL:nil - error:&bookmarkError]; - - if (bookmarkError != nil) { - NSString *errorMessage = [NSString stringWithFormat:@"Failed to create a security-scoped bookmark due to the following error:\n\n %@", [bookmarkError localizedDescription]]; - NSLog(@"[Gyroflow Toolbox] %@", errorMessage); - [self showAlertWithMessage:@"An error has occurred" info:errorMessage]; - [url stopAccessingSecurityScopedResource]; - return; - } - - if (bookmark == nil) { - [self showAlertWithMessage:@"An error has occurred" info:@"Failed to create a security-scoped bookmark due to the Bookmark being 'nil' and the error message is also being 'nil'"]; - [url stopAccessingSecurityScopedResource]; - return; - } - - //NSLog(@"[Gyroflow Toolbox] Bookmark created successfully for: %@", [url path]); - - NSUserDefaults *userDefaults = [[NSUserDefaults alloc] init]; - [userDefaults setObject:bookmark forKey:@"gyroFlowPreferencesBookmarkData"]; - [userDefaults release]; - - [url stopAccessingSecurityScopedResource]; - - [self readLastProjectFromGyroflowPreferencesFile]; - }]; - }]; -} - -//--------------------------------------------------------- -// Can we read the Gyroflow Preferences File? -//--------------------------------------------------------- -- (BOOL)canReadGyroflowPreferencesFile { - NSUserDefaults *userDefaults = [[NSUserDefaults alloc] init]; - NSData *gyroFlowPreferencesBookmarkData = [userDefaults dataForKey:@"gyroFlowPreferencesBookmarkData"]; - [userDefaults release]; - - if (gyroFlowPreferencesBookmarkData == nil) { - return NO; - } - - BOOL staleBookmark; - NSURL *url = nil; - NSError *bookmarkError = nil; - url = [NSURL URLByResolvingBookmarkData:gyroFlowPreferencesBookmarkData - options:NSURLBookmarkResolutionWithSecurityScope - relativeToURL:nil - bookmarkDataIsStale:&staleBookmark - error:&bookmarkError]; - - if (bookmarkError != nil) { - NSLog(@"[Gyroflow Toolbox Renderer] Failed to read Gyroflow Preferences Bookmark Data: %@", bookmarkError.localizedDescription); - return NO; - } - - if (staleBookmark) { - NSLog(@"[Gyroflow Toolbox Renderer] Stale Gyroflow Preferences Bookmark."); - return NO; - } - - if (url == nil) { - NSLog(@"[Gyroflow Toolbox Renderer] Gyroflow Preferences Bookmark is nil."); - return NO; - } - - if (![url startAccessingSecurityScopedResource]) { - NSLog(@"[Gyroflow Toolbox Renderer] Failed to start accessing the security scope resource for the Gyroflow Preferences."); - return NO; - } - - NSDictionary *preferences = [NSDictionary dictionaryWithContentsOfURL:url]; - - [url stopAccessingSecurityScopedResource]; - - if (preferences == nil) { - NSLog(@"[Gyroflow Toolbox Renderer] Gyroflow Preferences Dictionary is nil."); - return NO; - } - - return YES; -} - -//--------------------------------------------------------- -// Read Last Project from Gyroflow Preferences File: -//--------------------------------------------------------- -- (void)readLastProjectFromGyroflowPreferencesFile { - - NSUserDefaults *userDefaults = [[NSUserDefaults alloc] init]; - NSData *gyroFlowPreferencesBookmarkData = [userDefaults dataForKey:@"gyroFlowPreferencesBookmarkData"]; - [userDefaults release]; - - if (gyroFlowPreferencesBookmarkData == nil) { - NSString *errorMessage = @"Failed to access Gyroflow's Preferences file."; - NSLog(@"[Gyroflow Toolbox Renderer] %@", errorMessage); - [self showAlertWithMessage:@"An error has occurred" info:errorMessage]; - return; - } - - BOOL staleBookmark; - NSURL *url = nil; - NSError *bookmarkError = nil; - url = [NSURL URLByResolvingBookmarkData:gyroFlowPreferencesBookmarkData - options:NSURLBookmarkResolutionWithSecurityScope - relativeToURL:nil - bookmarkDataIsStale:&staleBookmark - error:&bookmarkError]; - - if (bookmarkError != nil) { - NSString *errorMessage = [NSString stringWithFormat:@"Failed to access Gyroflow's Preferences file due to a bookmark error:\n\n%@", bookmarkError.localizedDescription]; - NSLog(@"[Gyroflow Toolbox Renderer] %@", errorMessage); - [self showAlertWithMessage:@"An error has occurred" info:errorMessage]; - return; - } - - if (staleBookmark) { - NSString *errorMessage = @"Failed to access Gyroflow's Preferences file due to a stale bookmark."; - NSLog(@"[Gyroflow Toolbox Renderer] %@", errorMessage); - [self showAlertWithMessage:@"An error has occurred" info:errorMessage]; - return; - } - - if (![url startAccessingSecurityScopedResource]) { - NSString *errorMessage = @"Failed to start accessing the security scoped resource for Gyroflow's Preferences file."; - NSLog(@"[Gyroflow Toolbox Renderer] %@", errorMessage); - [self showAlertWithMessage:@"An error has occurred" info:errorMessage]; - return; - } - - NSDictionary *preferences = [NSDictionary dictionaryWithContentsOfURL:url]; - - [url stopAccessingSecurityScopedResource]; - - if (preferences == nil) { - NSString *errorMessage = @"Failed to read the Gyroflow's Preferences file."; - NSLog(@"[Gyroflow Toolbox Renderer] %@", errorMessage); - [self showAlertWithMessage:@"An error has occurred" info:errorMessage]; - return; - } - - NSString *lastProjectPath = [preferences valueForKey:@"lastProject"]; - NSURL *lastProjectURL = [NSURL fileURLWithPath:lastProjectPath]; - - [self importGyroflowProjectWithOptionalURL:lastProjectURL]; -} - -//--------------------------------------------------------- -// Import Gyroflow Project with Optional URL: -//--------------------------------------------------------- -- (void)importGyroflowProjectWithOptionalURL:(NSURL*)optionalURL { - - NSLog(@"[Gyroflow Toolbox Renderer] Import Gyroflow Project with Optional URL Triggered: %@", optionalURL); + [commandEncoder setVertexBytes:vertices + length:sizeof(vertices) + atIndex:BVI_Vertices]; //--------------------------------------------------------- - // Load the Custom Parameter Action API: + // Set the viewport size: //--------------------------------------------------------- - id actionAPI = [_apiManager apiForProtocol:@protocol(FxCustomParameterActionAPI_v4)]; - if (actionAPI == nil) { - [self showAlertWithMessage:@"An error has occurred." info:@"Unable to retrieve 'FxCustomParameterActionAPI_v4' in ImportGyroflowProjectView's 'buttonPressed'. This shouldn't happen."]; - return; - } - - //--------------------------------------------------------- - // Setup an NSOpenPanel: - //--------------------------------------------------------- - NSOpenPanel* panel = [NSOpenPanel openPanel]; - [panel setCanChooseDirectories:NO]; - [panel setCanCreateDirectories:YES]; - [panel setCanChooseFiles:YES]; - [panel setAllowsMultipleSelection:NO]; - [panel setDirectoryURL:optionalURL]; - - //--------------------------------------------------------- - // Limit the file type to .gyroflow files: - //--------------------------------------------------------- - UTType *gyroflowExtension = [UTType typeWithFilenameExtension:@"gyroflow"]; - NSArray *allowedContentTypes = [NSArray arrayWithObject:gyroflowExtension]; - [panel setAllowedContentTypes:allowedContentTypes]; - - //--------------------------------------------------------- - // Open the panel: - //--------------------------------------------------------- - NSModalResponse result = [panel runModal]; - if (result != NSModalResponseOK) { - return; - } - - //--------------------------------------------------------- - // Start accessing security scoped resource: - //--------------------------------------------------------- - NSURL *url = [panel URL]; - BOOL startedOK = [url startAccessingSecurityScopedResource]; - if (startedOK == NO) { - [self showAlertWithMessage:@"An error has occurred." info:@"Failed to startAccessingSecurityScopedResource. This shouldn't happen."]; - return; - } - - //--------------------------------------------------------- - // Create a Security Scope Bookmark, so we can reload - // later: - //--------------------------------------------------------- - NSError *bookmarkError = nil; - NSURLBookmarkCreationOptions bookmarkOptions = NSURLBookmarkCreationWithSecurityScope | NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess; - NSData *bookmark = [url bookmarkDataWithOptions:bookmarkOptions - includingResourceValuesForKeys:nil - relativeToURL:nil - error:&bookmarkError]; - - if (bookmarkError != nil) { - NSString *errorMessage = [NSString stringWithFormat:@"Failed to resolve bookmark due to:\n\n%@", [bookmarkError localizedDescription]]; - NSLog(@"[Gyroflow Toolbox Renderer] %@", errorMessage); - [self showAlertWithMessage:@"An error has occurred." info:errorMessage]; - return; - } - - if (bookmark == nil) { - NSString *errorMessage = @"Bookmark data is nil. This shouldn't happen."; - NSLog(@"[Gyroflow Toolbox Renderer] %@", errorMessage); - [self showAlertWithMessage:@"An error has occurred." info:errorMessage]; - return; - } + simd_uint2 viewportSize = { + (unsigned int)(outputWidth), + (unsigned int)(outputHeight) + }; - NSString *selectedGyroflowProjectFile = [[url lastPathComponent] stringByDeletingPathExtension]; - NSString *selectedGyroflowProjectPath = [url path]; - NSString *selectedGyroflowProjectBookmarkData = [bookmark base64EncodedStringWithOptions:0]; - //--------------------------------------------------------- - // Read the Gyroflow Project Data from File: + // Sets a block of data for the vertex shader: //--------------------------------------------------------- - NSError *readError = nil; - NSString *selectedGyroflowProjectData = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&readError]; - if (readError != nil) { - NSString *errorMessage = [NSString stringWithFormat:@"Failed to read Gyroflow Project File due to:\n\n%@", [readError localizedDescription]]; - NSLog(@"[Gyroflow Toolbox Renderer] %@", errorMessage); - [self showAlertWithMessage:@"An error has occurred." info:errorMessage]; - return; - } + [commandEncoder setVertexBytes:&viewportSize + length:sizeof(viewportSize) + atIndex:BVI_ViewportSize]; //--------------------------------------------------------- - // Read the JSON data: + // Sets a texture for the fragment function at an index + // in the texture argument table: //--------------------------------------------------------- - NSData *data = [selectedGyroflowProjectData dataUsingEncoding:NSUTF8StringEncoding]; - NSError *jsonError = nil; - NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonError]; - - if (jsonError != nil) { - NSString *errorMessage = [NSString stringWithFormat:@"There was an unexpected error reading the JSON data:\n\n%@", jsonError.localizedDescription]; - NSLog(@"[Gyroflow Toolbox Renderer] %@", errorMessage); - [self showAlertWithMessage:@"Failed to open Gyroflow Project" info:errorMessage]; - return; - } + [commandEncoder setFragmentTexture:inputTexture + atIndex:BTI_InputImage]; //--------------------------------------------------------- - // Make sure there's Gyro Data in the Gyroflow Project: - //--------------------------------------------------------- - NSDictionary *gyroSource = [jsonData objectForKey:@"gyro_source"]; - if (![gyroSource objectForKey:@"raw_imu"]) { - NSString *errorMessage = @"The Gyroflow file you imported doesn't seem to contain any gyro data.\n\nPlease try exporting from Gyroflow again using the 'Export project file (including gyro data)' option."; - NSLog(@"[Gyroflow Toolbox Renderer] %@", errorMessage); - [self showAlertWithMessage:@"Gyro Data Not Found." info:errorMessage]; - return; - } - - //--------------------------------------------------------- - // Get the current 'FOV' value: - //--------------------------------------------------------- - NSDictionary *stabilizationData = [jsonData objectForKey:@"stabilization"]; - NSNumber *fov = [stabilizationData objectForKey:@"fov"]; - - //--------------------------------------------------------- - // Get the current 'Smoothness' value: + // drawPrimitives: Encodes a command to render one instance + // of primitives using vertex data in contiguous array + // elements. + // + // MTLPrimitiveTypeTriangleStrip: For every three adjacent + // vertices, rasterize a triangle. //--------------------------------------------------------- - NSArray *smoothnessParams = [stabilizationData objectForKey:@"smoothing_params"]; - NSNumber *smoothness = nil; - for (NSDictionary *param in smoothnessParams) { - if ([param[@"name"] isEqualToString:@"smoothness"]) { - smoothness = param[@"value"]; - break; - } - } + [commandEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip + vertexStart:0 + vertexCount:4]; //--------------------------------------------------------- - // Get the current 'Lens Correction' value: - //--------------------------------------------------------- - NSNumber *lensCorrection = [stabilizationData objectForKey:@"lens_correction_amount"]; - - //--------------------------------------------------------- - // Use the Action API to allow us to change the parameters: + // Declares that all command generation from the encoder + // is completed. After `endEncoding` is called, the + // command encoder has no further use. You cannot encode + // any other commands with this encoder. //--------------------------------------------------------- - [actionAPI startAction:self]; + [commandEncoder endEncoding]; //--------------------------------------------------------- - // Load the Parameter Set API: - //--------------------------------------------------------- - id paramSetAPI = [_apiManager apiForProtocol:@protocol(FxParameterSettingAPI_v5)]; - if (paramSetAPI == nil) - { - NSString *errorMessage = @"Unable to retrieve FxParameterSettingAPI_v5 in 'selectFileButtonPressed'. This shouldn't happen."; - NSLog(@"[Gyroflow Toolbox Renderer] %@", errorMessage); - [self showAlertWithMessage:@"An error has occurred." info:errorMessage]; - return; - } - - //--------------------------------------------------------- - // Update 'Gyroflow Project Path': + // Commits the command buffer for execution. + // After you call the commit method, the MTLDevice schedules + // and executes the commands in the command buffer. If you + // haven’t already enqueued the command buffer with a call + // to enqueue, calling this function also enqueues the + // command buffer. The GPU executes the command buffer + // after any command buffers enqueued before it on the same + // command queue. + // + // You can only commit a command buffer once. You can’t + // commit a command buffer if the command buffer has an + // active command encoder. Once you commit a command buffer, + // you may not encode additional commands into it, nor can + // you add a schedule or completion handler. //--------------------------------------------------------- - [paramSetAPI setStringParameterValue:selectedGyroflowProjectPath toParameter:kCB_GyroflowProjectPath]; + [commandBuffer commit]; //--------------------------------------------------------- - // Update 'Gyroflow Project Bookmark Data': + // Blocks execution of the current thread until execution + // of the command buffer is completed. //--------------------------------------------------------- - [paramSetAPI setStringParameterValue:selectedGyroflowProjectBookmarkData toParameter:kCB_GyroflowProjectBookmarkData]; + [commandBuffer waitUntilCompleted]; //--------------------------------------------------------- - // Update 'Gyroflow Project Data': + // Release the `colorAttachmentDescriptor` we created + // earlier: //--------------------------------------------------------- - [paramSetAPI setStringParameterValue:selectedGyroflowProjectData toParameter:kCB_GyroflowProjectData]; + [colorAttachmentDescriptor release]; //--------------------------------------------------------- - // Update 'Loaded Gyroflow Project' Text Box: + // Return the command queue back into the cache, + // so we can re-use it again: //--------------------------------------------------------- - [paramSetAPI setStringParameterValue:selectedGyroflowProjectFile toParameter:kCB_LoadedGyroflowProject]; + [deviceCache returnCommandQueueToCache:commandQueue]; //--------------------------------------------------------- - // Set parameters from Gyroflow Project file: - //--------------------------------------------------------- - if (fov != nil) { - [paramSetAPI setFloatValue:[fov floatValue] toParameter:kCB_FOV atTime:kCMTimeZero]; - } - if (smoothness != nil) { - [paramSetAPI setFloatValue:[smoothness floatValue] toParameter:kCB_Smoothness atTime:kCMTimeZero]; - } - if (lensCorrection != nil) { - [paramSetAPI setFloatValue:[lensCorrection floatValue] * 100.0 toParameter:kCB_LensCorrection atTime:kCMTimeZero]; - } - - //--------------------------------------------------------- - // Stop Action API: - //--------------------------------------------------------- - [actionAPI endAction:self]; - - //--------------------------------------------------------- - // Stop accessing security scoped resource: + // Release the Input Textures: //--------------------------------------------------------- - [url stopAccessingSecurityScopedResource]; + [inputTexture setPurgeableState:MTLPurgeableStateEmpty]; - //--------------------------------------------------------- - // Show Victory Message: - //--------------------------------------------------------- - [self showAlertWithMessage:@"Success!" info:@"The Gyroflow Project has been successfully imported.\n\nYou can now adjust the FOV, Smoothness and Lens Correction as required."]; -} - -//--------------------------------------------------------- -// -#pragma mark - Helpers -// -//--------------------------------------------------------- - -//--------------------------------------------------------- -// Get user home directory path: -//--------------------------------------------------------- -- (NSString*)getUserHomeDirectoryPath { - struct passwd *pw = getpwuid(getuid()); - assert(pw); - return [NSString stringWithUTF8String:pw->pw_dir]; -} - -//--------------------------------------------------------- -// Get the NSURL of the Gyroflow Preferences file: -//--------------------------------------------------------- -- (NSURL*)getGyroflowPreferencesFileURL { - NSString *userHomeDirectoryPath = [self getUserHomeDirectoryPath]; - NSURL* userHomeDirectoryURL = [NSURL URLWithString:userHomeDirectoryPath]; - NSURL* gyroflowPlistURL = [userHomeDirectoryURL URLByAppendingPathComponent:@"/Library/Preferences/com.gyroflow-xyz.Gyroflow.plist"]; - return gyroflowPlistURL; -} - -//--------------------------------------------------------- -// Show Alert: -//--------------------------------------------------------- -- (void)showAlertWithMessage:(NSString*)message info:(NSString*)info -{ - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - alert.icon = [NSImage imageNamed:@"GyroflowToolbox"]; - alert.alertStyle = NSAlertStyleInformational; - alert.messageText = message; - alert.informativeText = info; - [alert runModal]; + return YES; } @end diff --git a/Source/Gyroflow/Plugin/ImportGyroflowProjectView.h b/Source/Gyroflow/Plugin/ImportGyroflowProjectView.h new file mode 100644 index 00000000..d80e77b2 --- /dev/null +++ b/Source/Gyroflow/Plugin/ImportGyroflowProjectView.h @@ -0,0 +1,19 @@ +// +// ImportGyroflowProjectView.h +// Gyroflow Toolbox +// +// Created by Chris Hocking on 20/12/2022. +// + +#import +#import + +@interface ImportGyroflowProjectView : NSView +{ + id _apiManager; +} + +- (instancetype)initWithFrame:(NSRect)frameRect + andAPIManager:(id)apiManager; + +@end diff --git a/Source/Gyroflow/Plugin/ImportGyroflowProjectView.m b/Source/Gyroflow/Plugin/ImportGyroflowProjectView.m new file mode 100644 index 00000000..99fbc195 --- /dev/null +++ b/Source/Gyroflow/Plugin/ImportGyroflowProjectView.m @@ -0,0 +1,250 @@ +// +// ImportGyroflowProjectView.m +// Gyroflow Toolbox +// +// Created by Chris Hocking on 20/12/2022. +// + +#import "ImportGyroflowProjectView.h" +#import "GyroflowConstants.h" + +#import +#import + +@implementation ImportGyroflowProjectView { + NSButton* _cachedButton; +} + +//--------------------------------------------------------- +// Initialize: +//--------------------------------------------------------- +- (instancetype)initWithFrame:(NSRect)frameRect + andAPIManager:(id)apiManager +{ + self = [super initWithFrame:frameRect]; + + if (self != nil) + { + //--------------------------------------------------------- + // Cache the API Manager: + //--------------------------------------------------------- + _apiManager = apiManager; + + //--------------------------------------------------------- + // Add the "Import Gyroflow Project" button: + //--------------------------------------------------------- + NSButton *button = [[NSButton alloc]initWithFrame:NSMakeRect(0, 0, 130, 30)]; // x y w h + [button setButtonType:NSButtonTypeMomentaryPushIn]; + [button setBezelStyle: NSBezelStyleRounded]; + button.layer.backgroundColor = [NSColor colorWithCalibratedRed:66 green:66 blue:66 alpha:1].CGColor; + button.layer.shadowColor = [NSColor blackColor].CGColor; + [button setBordered:YES]; + [button setTitle:@"Import Project"]; + [button setTarget:self]; + [button setAction:@selector(buttonPressed)]; + + _cachedButton = button; + [self addSubview:_cachedButton]; + } + + return self; +} + +//--------------------------------------------------------- +// Deallocates the memory occupied by the receiver: +//--------------------------------------------------------- +- (void)dealloc +{ + if (_cachedButton) { + [_cachedButton release]; + } + + [super dealloc]; +} + +//--------------------------------------------------------- +// Draw the NSView: +//--------------------------------------------------------- +- (void)drawRect:(NSRect)dirtyRect { + //--------------------------------------------------------- + // Draw the button: + //--------------------------------------------------------- + [super drawRect:dirtyRect]; +} + +//--------------------------------------------------------- +// Triggered when the button is pressed: +//--------------------------------------------------------- +- (void)buttonPressed { + //--------------------------------------------------------- + // Load the Custom Parameter Action API: + //--------------------------------------------------------- + id actionAPI = [_apiManager apiForProtocol:@protocol(FxCustomParameterActionAPI_v4)]; + if (actionAPI == nil) { + [self showAlertWithMessage:@"An error has occurred." info:@"Unable to retrieve 'FxCustomParameterActionAPI_v4' in ImportGyroflowProjectView's 'buttonPressed'. This shouldn't happen."]; + return; + } + + //--------------------------------------------------------- + // Setup an NSOpenPanel: + //--------------------------------------------------------- + NSOpenPanel* panel = [NSOpenPanel openPanel]; + [panel setCanChooseDirectories:NO]; + [panel setCanCreateDirectories:YES]; + [panel setCanChooseFiles:YES]; + [panel setAllowsMultipleSelection:NO]; + + //--------------------------------------------------------- + // Limit the file type to .gyroflow files: + //--------------------------------------------------------- + UTType *gyroflowExtension = [UTType typeWithFilenameExtension:@"gyroflow"]; + NSArray *allowedContentTypes = [NSArray arrayWithObject:gyroflowExtension]; + [panel setAllowedContentTypes:allowedContentTypes]; + + //--------------------------------------------------------- + // Open the panel: + //--------------------------------------------------------- + NSModalResponse result = [panel runModal]; + if (result != NSModalResponseOK) { + return; + } + + //--------------------------------------------------------- + // Start accessing security scoped resource: + //--------------------------------------------------------- + NSURL *url = [panel URL]; + BOOL startedOK = [url startAccessingSecurityScopedResource]; + if (startedOK == NO) { + [self showAlertWithMessage:@"An error has occurred." info:@"Failed to startAccessingSecurityScopedResource. This shouldn't happen."]; + return; + } + + //--------------------------------------------------------- + // Create a Security Scope Bookmark, so we can reload + // later: + //--------------------------------------------------------- + NSError *bookmarkError = nil; + NSURLBookmarkCreationOptions bookmarkOptions = NSURLBookmarkCreationWithSecurityScope | NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess; + NSData *bookmark = [url bookmarkDataWithOptions:bookmarkOptions + includingResourceValuesForKeys:nil + relativeToURL:nil + error:&bookmarkError]; + + if (bookmarkError != nil) { + [self showAlertWithMessage:@"An error has occurred." info:[NSString stringWithFormat:@"Failed to resolve bookmark due to:\n\n%@", [bookmarkError localizedDescription]]]; + return; + } else if (bookmark == nil) { + [self showAlertWithMessage:@"An error has occurred." info:@"Bookmark data is nil. This shouldn't happen."]; + return; + } + + NSString *selectedGyroflowProjectFile = [[url lastPathComponent] stringByDeletingPathExtension]; + NSString *selectedGyroflowProjectPath = [url path]; + NSString *selectedGyroflowProjectBookmarkData = [bookmark base64EncodedStringWithOptions:0]; + + //--------------------------------------------------------- + // Read the Gyroflow Project Data from File: + //--------------------------------------------------------- + NSError *readError = nil; + NSString *selectedGyroflowProjectData = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&readError]; + if (readError != nil) { + [self showAlertWithMessage:@"An error has occurred." info:[NSString stringWithFormat:@"Failed to read Gyroflow Project File due to:\n\n%@", [readError localizedDescription]]]; + return; + } + + //--------------------------------------------------------- + // Make sure there's Processed Gyro Data in the Gyroflow + // Project Data: + //--------------------------------------------------------- + if (![selectedGyroflowProjectData containsString:@"integrated_quaternions"]) { + [self showAlertWithMessage:@"Processed Gyro Data Not Found." info:@"The Gyroflow file you imported doesn't seem to contain any processed gyro data.\n\nPlease try exporting from Gyroflow again using the 'Export project file (including processed gyro data)' option."]; + return; + } + + //--------------------------------------------------------- + // Use the Action API to allow us to change the parameters: + //--------------------------------------------------------- + [actionAPI startAction:self]; + + //--------------------------------------------------------- + // Load the Parameter Set API: + //--------------------------------------------------------- + id paramSetAPI = [_apiManager apiForProtocol:@protocol(FxParameterSettingAPI_v5)]; + if (paramSetAPI == nil) + { + [self showAlertWithMessage:@"An error has occurred." info:@"Unable to retrieve FxParameterSettingAPI_v5 in 'selectFileButtonPressed'. This shouldn't happen."]; + return; + } + + //--------------------------------------------------------- + // Update 'Gyroflow Project Path': + //--------------------------------------------------------- + [paramSetAPI setParameterFlags:kFxParameterFlag_DEFAULT toParameter:kCB_GyroflowProjectPath]; + [paramSetAPI setStringParameterValue:selectedGyroflowProjectPath toParameter:kCB_GyroflowProjectPath]; + [paramSetAPI setParameterFlags:kFxParameterFlag_HIDDEN toParameter:kCB_GyroflowProjectPath]; + + //--------------------------------------------------------- + // Update 'Gyroflow Project Bookmark Data': + //--------------------------------------------------------- + [paramSetAPI setParameterFlags:kFxParameterFlag_DEFAULT toParameter:kCB_GyroflowProjectBookmarkData]; + [paramSetAPI setStringParameterValue:selectedGyroflowProjectBookmarkData toParameter:kCB_GyroflowProjectBookmarkData]; + [paramSetAPI setParameterFlags:kFxParameterFlag_HIDDEN toParameter:kCB_GyroflowProjectBookmarkData]; + + //--------------------------------------------------------- + // Update 'Gyroflow Project Data': + //--------------------------------------------------------- + [paramSetAPI setParameterFlags:kFxParameterFlag_DEFAULT toParameter:kCB_GyroflowProjectData]; + [paramSetAPI setStringParameterValue:selectedGyroflowProjectData toParameter:kCB_GyroflowProjectData]; + [paramSetAPI setParameterFlags:kFxParameterFlag_HIDDEN toParameter:kCB_GyroflowProjectData]; + + //--------------------------------------------------------- + // Update 'Loaded Gyroflow Project' Text Box: + //--------------------------------------------------------- + [paramSetAPI setParameterFlags:kFxParameterFlag_DEFAULT toParameter:kCB_LoadedGyroflowProject]; + [paramSetAPI setStringParameterValue:selectedGyroflowProjectFile toParameter:kCB_LoadedGyroflowProject]; + [paramSetAPI setParameterFlags:kFxParameterFlag_DISABLED | kFxParameterFlag_NOT_ANIMATABLE toParameter:kCB_LoadedGyroflowProject]; + + //--------------------------------------------------------- + // Stop Action API: + //--------------------------------------------------------- + [actionAPI endAction:self]; + + //--------------------------------------------------------- + // Stop accessing security scoped resource: + //--------------------------------------------------------- + [url stopAccessingSecurityScopedResource]; + + //--------------------------------------------------------- + // Show Victory Message: + //--------------------------------------------------------- + [self showAlertWithMessage:@"Success!" info:@"The Gyroflow Project has been successfully imported.\n\nYou can now adjust the FOV, Smoothness and Lens Correction as required."]; +} + +//--------------------------------------------------------- +// Show Alert: +//--------------------------------------------------------- +- (void)showAlertWithMessage:(NSString*)message info:(NSString*)info +{ + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + alert.icon = [NSImage imageNamed:@"GyroflowToolbox"]; + alert.alertStyle = NSAlertStyleInformational; + alert.messageText = message; + alert.informativeText = info; + [alert runModal]; +} + +//--------------------------------------------------------- +// Because custom views are hosted in an overlay window, +// the first click on them will normally just make the +// overlay window be the key window, and it will require a +// second click in order to actually tell the view to +// start responding. By returning YES from this method, the +// first click begins user interaction with the view. +//--------------------------------------------------------- +- (BOOL)acceptsFirstMouse:(NSEvent *)event +{ + return YES; +} + +@end + diff --git a/Source/Gyroflow/Plugin/LaunchGyroflowView.h b/Source/Gyroflow/Plugin/LaunchGyroflowView.h new file mode 100644 index 00000000..4b5465e3 --- /dev/null +++ b/Source/Gyroflow/Plugin/LaunchGyroflowView.h @@ -0,0 +1,19 @@ +// +// LaunchGyroflowView.h +// Gyroflow Toolbox +// +// Created by Chris Hocking on 22/12/2022. +// + +#import +#import + +@interface LaunchGyroflowView : NSView +{ + id _apiManager; +} + +- (instancetype)initWithFrame:(NSRect)frameRect + andAPIManager:(id)apiManager; + +@end diff --git a/Source/Gyroflow/Plugin/CustomButtonView.m b/Source/Gyroflow/Plugin/LaunchGyroflowView.m similarity index 59% rename from Source/Gyroflow/Plugin/CustomButtonView.m rename to Source/Gyroflow/Plugin/LaunchGyroflowView.m index 54e5b32a..8b5408bd 100644 --- a/Source/Gyroflow/Plugin/CustomButtonView.m +++ b/Source/Gyroflow/Plugin/LaunchGyroflowView.m @@ -1,79 +1,62 @@ // -// CustomButtonView.m -// Gyroflow Toolbox Renderer +// LaunchGyroflowView.m +// Gyroflow Toolbox // -// Created by Chris Hocking on 29/01/2023. +// Created by Chris Hocking on 22/12/2022. // -#import "CustomButtonView.h" +#import "LaunchGyroflowView.h" +#import "GyroflowConstants.h" + #import +#import -@implementation CustomButtonView { - NSButton* _button; +@implementation LaunchGyroflowView { + NSButton* _cachedButton; } //--------------------------------------------------------- // Initialize: //--------------------------------------------------------- -- (instancetype)initWithAPIManager:(id)apiManager - parentPlugin:(id)parentPlugin - buttonID:(UInt32)buttonID - buttonTitle:(NSString*)buttonTitle +- (instancetype)initWithFrame:(NSRect)frameRect + andAPIManager:(id)apiManager { - int buttonWidth = 200; - int buttonHeight = 32; - - NSRect frameRect = NSMakeRect(0, 0, buttonWidth, buttonHeight); // x y w h self = [super initWithFrame:frameRect]; if (self != nil) { - _apiManager = apiManager; - //--------------------------------------------------------- - // Cache the parent plugin & button ID: + // Cache the API Manager: //--------------------------------------------------------- - _parentPlugin = parentPlugin; - _buttonID = buttonID; + _apiManager = apiManager; //--------------------------------------------------------- - // Add the button: + // Add the "Import Gyroflow Project" button: //--------------------------------------------------------- - NSButton *button = [[NSButton alloc]initWithFrame:NSMakeRect(0, 0, buttonWidth, buttonHeight)]; // x y w h + NSButton *button = [[NSButton alloc]initWithFrame:NSMakeRect(0, 0, 130, 30)]; // x y w h [button setButtonType:NSButtonTypeMomentaryPushIn]; [button setBezelStyle: NSBezelStyleRounded]; button.layer.backgroundColor = [NSColor colorWithCalibratedRed:66 green:66 blue:66 alpha:1].CGColor; button.layer.shadowColor = [NSColor blackColor].CGColor; [button setBordered:YES]; - [button setTitle:buttonTitle]; + [button setTitle:@"Launch Gyroflow"]; [button setTarget:self]; [button setAction:@selector(buttonPressed)]; - _button = button; - [self addSubview:_button]; + _cachedButton = button; + [self addSubview:_cachedButton]; } return self; } -//--------------------------------------------------------- -// Triggered when the button is pressed: -//--------------------------------------------------------- -- (void)buttonPressed { - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wobjc-method-access" - [_parentPlugin customButtonViewPressed:_buttonID]; - #pragma clang diagnostic pop - -} - //--------------------------------------------------------- // Deallocates the memory occupied by the receiver: //--------------------------------------------------------- - (void)dealloc { - if (_button) { - [_button release]; + if (_cachedButton) { + [_cachedButton release]; } [super dealloc]; @@ -89,6 +72,32 @@ - (void)drawRect:(NSRect)dirtyRect { [super drawRect:dirtyRect]; } +//--------------------------------------------------------- +// Triggered when the button is pressed: +//--------------------------------------------------------- +- (void)buttonPressed { + NSString *bundleIdentifier = @"xyz.gyroflow"; + NSURL *appURL = [[NSWorkspace sharedWorkspace] URLForApplicationWithBundleIdentifier:bundleIdentifier]; + if (appURL) { + [[NSWorkspace sharedWorkspace] openURL:appURL]; + } else { + [self showAlertWithMessage:@"Failed to launch Gyroflow." info:@"Please check that Gyroflow is installed in your Applications folder and try again."]; + } +} + +//--------------------------------------------------------- +// Show Alert: +//--------------------------------------------------------- +- (void)showAlertWithMessage:(NSString*)message info:(NSString*)info +{ + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + alert.icon = [NSImage imageNamed:@"GyroflowToolbox"]; + alert.alertStyle = NSAlertStyleInformational; + alert.messageText = message; + alert.informativeText = info; + [alert runModal]; +} + //--------------------------------------------------------- // Because custom views are hosted in an overlay window, // the first click on them will normally just make the diff --git a/Source/Gyroflow/Plugin/MetalDeviceCache.h b/Source/Gyroflow/Plugin/MetalDeviceCache.h index c306be20..9bec7793 100644 --- a/Source/Gyroflow/Plugin/MetalDeviceCache.h +++ b/Source/Gyroflow/Plugin/MetalDeviceCache.h @@ -1,6 +1,6 @@ // // MetalDeviceCache.h -// Gyroflow Toolbox Renderer +// Gyroflow Toolbox // // Created by Chris Hocking on 10/12/2022. // @@ -19,6 +19,8 @@ + (MTLPixelFormat)MTLPixelFormatForImageTile:(FxImageTile*)imageTile; - (id)deviceWithRegistryID:(uint64_t)registryID; +- (id)pipelineStateWithRegistryID:(uint64_t)registryID + pixelFormat:(MTLPixelFormat)pixFormat; - (id)commandQueueWithRegistryID:(uint64_t)registryID pixelFormat:(MTLPixelFormat)pixFormat; - (void)returnCommandQueueToCache:(id)commandQueue; diff --git a/Source/Gyroflow/Plugin/MetalDeviceCache.m b/Source/Gyroflow/Plugin/MetalDeviceCache.m index e775fcc9..503cbcfc 100644 --- a/Source/Gyroflow/Plugin/MetalDeviceCache.m +++ b/Source/Gyroflow/Plugin/MetalDeviceCache.m @@ -1,6 +1,6 @@ // // MetalDeviceCache.m -// Gyroflow Toolbox Renderer +// Gyroflow Toolbox // // Created by Chris Hocking on 10/12/2022. // @@ -19,7 +19,7 @@ #import "MetalDeviceCache.h" -const NSUInteger kMaxCommandQueues = 5; +const NSUInteger kMaxCommandQueues = 10; static NSString* kKey_InUse = @"InUse"; static NSString* kKey_CommandQueue = @"CommandQueue"; @@ -28,6 +28,7 @@ @interface MetalDeviceCacheItem : NSObject @property (readonly) id gpuDevice; +@property (readonly) id pipelineState; @property (retain) NSMutableArray* commandQueueCache; @property (readonly) NSLock* commandQueueCacheLock; @property (readonly) MTLPixelFormat pixelFormat; @@ -69,15 +70,50 @@ - (instancetype)initWithDevice:(id)device [_commandQueueCache addObject:commandDict]; } - + + //--------------------------------------------------------- + // Load all the shader files with a .metal file extension + // in the project: + //--------------------------------------------------------- + id defaultLibrary = [[_gpuDevice newDefaultLibrary] autorelease]; + + //--------------------------------------------------------- + // Load the vertex function from the library: + //--------------------------------------------------------- + id vertexFunction = [[defaultLibrary newFunctionWithName:@"vertexShader"] autorelease]; + + //--------------------------------------------------------- + // Load the fragment function from the library: + //--------------------------------------------------------- + id fragmentFunction = [[defaultLibrary newFunctionWithName:@"fragmentShader"] autorelease]; + + //--------------------------------------------------------- + // Configure a pipeline descriptor that is used to create + // a pipeline state: + //--------------------------------------------------------- + MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[[MTLRenderPipelineDescriptor alloc] init] autorelease]; + pipelineStateDescriptor.label = @"Gyroflow Toolbox Pipeline"; + pipelineStateDescriptor.vertexFunction = vertexFunction; + pipelineStateDescriptor.fragmentFunction = fragmentFunction; + pipelineStateDescriptor.colorAttachments[0].pixelFormat = pixFormat; _pixelFormat = pixFormat; + NSError* error = nil; + _pipelineState = [_gpuDevice newRenderPipelineStateWithDescriptor:pipelineStateDescriptor + error:&error]; + if (error != nil) + { + NSLog (@"Error generating Gyroflow Toolbox pipeline state: %@", error); + } + if (_commandQueueCache != nil) { _commandQueueCacheLock = [[NSLock alloc] init]; } - if ((_gpuDevice == nil) || (_commandQueueCache == nil) || (_commandQueueCacheLock == nil)) { + if ((_gpuDevice == nil) || (_commandQueueCache == nil) || (_commandQueueCacheLock == nil) || + (_pipelineState == nil)) + { [self release]; self = nil; } @@ -91,6 +127,7 @@ - (void)dealloc [_gpuDevice release]; [_commandQueueCache release]; [_commandQueueCacheLock release]; + [_pipelineState release]; [super dealloc]; } @@ -237,6 +274,47 @@ - (void)dealloc return nil; } +- (id)pipelineStateWithRegistryID:(uint64_t)registryID + pixelFormat:(MTLPixelFormat)pixFormat +{ + for (MetalDeviceCacheItem* nextCacheItem in deviceCaches) + { + if ((nextCacheItem.gpuDevice.registryID == registryID) && + (nextCacheItem.pixelFormat == pixFormat)) + { + return nextCacheItem.pipelineState; + } + } + + //--------------------------------------------------------- + // Didn't find one, so create one with the right settings: + //--------------------------------------------------------- + NSArray>* devices = MTLCopyAllDevices(); + id device = nil; + for (id nextDevice in devices) + { + if (nextDevice.registryID == registryID) + { + device = nextDevice; + } + } + + id result = nil; + if (device != nil) + { + MetalDeviceCacheItem* newCacheItem = [[[MetalDeviceCacheItem alloc] initWithDevice:device + pixelFormat:pixFormat] + autorelease]; + if (newCacheItem != nil) + { + [deviceCaches addObject:newCacheItem]; + result = newCacheItem.pipelineState; + } + } + [devices release]; + return result; +} + - (id)commandQueueWithRegistryID:(uint64_t)registryID pixelFormat:(MTLPixelFormat)pixFormat; { diff --git a/Source/Gyroflow/Plugin/ReloadGyroflowProjectView.h b/Source/Gyroflow/Plugin/ReloadGyroflowProjectView.h new file mode 100644 index 00000000..8856e86f --- /dev/null +++ b/Source/Gyroflow/Plugin/ReloadGyroflowProjectView.h @@ -0,0 +1,19 @@ +// +// ReloadGyroflowProjectView.h +// Gyroflow Toolbox +// +// Created by Chris Hocking on 20/12/2022. +// + +#import +#import + +@interface ReloadGyroflowProjectView : NSView +{ + id _apiManager; +} + +- (instancetype)initWithFrame:(NSRect)frameRect + andAPIManager:(id)apiManager; + +@end diff --git a/Source/Gyroflow/Plugin/ReloadGyroflowProjectView.m b/Source/Gyroflow/Plugin/ReloadGyroflowProjectView.m new file mode 100644 index 00000000..2019ff31 --- /dev/null +++ b/Source/Gyroflow/Plugin/ReloadGyroflowProjectView.m @@ -0,0 +1,228 @@ +// +// ReloadGyroflowProjectView.m +// Gyroflow Toolbox +// +// Created by Chris Hocking on 20/12/2022. +// + +#import "ReloadGyroflowProjectView.h" +#import "GyroflowConstants.h" + +#import +#import + +@implementation ReloadGyroflowProjectView { + NSButton* _cachedButton; +} + +//--------------------------------------------------------- +// Initialize: +//--------------------------------------------------------- +- (instancetype)initWithFrame:(NSRect)frameRect + andAPIManager:(id)apiManager +{ + self = [super initWithFrame:frameRect]; + + if (self != nil) + { + //--------------------------------------------------------- + // Cache the API Manager: + //--------------------------------------------------------- + _apiManager = apiManager; + + //--------------------------------------------------------- + // Add the "Import Gyroflow Project" button: + //--------------------------------------------------------- + NSButton *button = [[NSButton alloc]initWithFrame:NSMakeRect(0, 0, 130, 30)]; // x y w h + [button setButtonType:NSButtonTypeMomentaryPushIn]; + [button setBezelStyle: NSBezelStyleRounded]; + button.layer.backgroundColor = [NSColor colorWithCalibratedRed:66 green:66 blue:66 alpha:1].CGColor; + button.layer.shadowColor = [NSColor blackColor].CGColor; + [button setBordered:YES]; + [button setTitle:@"Reload Project"]; + [button setTarget:self]; + [button setAction:@selector(buttonPressed)]; + + _cachedButton = button; + [self addSubview:_cachedButton]; + } + + return self; +} + +//--------------------------------------------------------- +// Deallocates the memory occupied by the receiver: +//--------------------------------------------------------- +- (void)dealloc +{ + if (_cachedButton) { + [_cachedButton release]; + } + + [super dealloc]; +} + +//--------------------------------------------------------- +// Draw the NSView: +//--------------------------------------------------------- +- (void)drawRect:(NSRect)dirtyRect { + //--------------------------------------------------------- + // Draw the button: + //--------------------------------------------------------- + [super drawRect:dirtyRect]; +} + +//--------------------------------------------------------- +// Triggered when the button is pressed: +//--------------------------------------------------------- +- (void)buttonPressed { + //--------------------------------------------------------- + // Load the Custom Parameter Action API: + //--------------------------------------------------------- + id actionAPI = [_apiManager apiForProtocol:@protocol(FxCustomParameterActionAPI_v4)]; + if (actionAPI == nil) { + [self showAlertWithMessage:@"An error has occurred." info:@"Unable to retrieve 'FxCustomParameterActionAPI_v4'. This shouldn't happen, so it's probably a bug."]; + return; + } + + //--------------------------------------------------------- + // Use the Action API to allow us to change the parameters: + //--------------------------------------------------------- + [actionAPI startAction:self]; + + //--------------------------------------------------------- + // Load the Parameter Retrieval API: + //--------------------------------------------------------- + id paramGetAPI = [_apiManager apiForProtocol:@protocol(FxParameterRetrievalAPI_v6)]; + if (paramGetAPI == nil) { + [self showAlertWithMessage:@"An error has occurred." info:@"Unable to retrieve 'FxParameterRetrievalAPI_v6'.\n\nThis shouldn't happen, so it's probably a bug."]; + return; + } + + //--------------------------------------------------------- + // Get the encoded bookmark string: + //--------------------------------------------------------- + NSString *encodedBookmark; + [paramGetAPI getStringParameterValue:&encodedBookmark fromParameter:kCB_GyroflowProjectBookmarkData]; + + //--------------------------------------------------------- + // Make sure there's actually encoded bookmark data: + //--------------------------------------------------------- + if ([encodedBookmark isEqualToString:@""]) { + //--------------------------------------------------------- + // Stop Action API: + //--------------------------------------------------------- + [actionAPI endAction:self]; + + [self showAlertWithMessage:@"An error has occurred." info:@"There's no previous security-scoped bookmark data found.\n\nPlease make sure you import a Gyroflow Project before attempting to reload."]; + return; + } + + //--------------------------------------------------------- + // Decode the Base64 bookmark data: + //--------------------------------------------------------- + NSData *decodedBookmark = [[[NSData alloc] initWithBase64EncodedString:encodedBookmark + options:0] autorelease]; + + //--------------------------------------------------------- + // Resolve the decoded bookmark data into a + // security-scoped URL: + //--------------------------------------------------------- + NSError *bookmarkError = nil; + BOOL isStale = NO; + + NSURL *url = [NSURL URLByResolvingBookmarkData:decodedBookmark + options:NSURLBookmarkResolutionWithSecurityScope + relativeToURL:nil + bookmarkDataIsStale:&isStale + error:&bookmarkError]; + + if (bookmarkError != nil) { + //--------------------------------------------------------- + // Stop Action API: + //--------------------------------------------------------- + [actionAPI endAction:self]; + + [self showAlertWithMessage:@"An error has occurred." info:[NSString stringWithFormat:@"Failed to resolve bookmark due to:\n\n%@", [bookmarkError localizedDescription]]]; + return; + } + + //--------------------------------------------------------- + // Load the Parameter Set API: + //--------------------------------------------------------- + id paramSetAPI = [_apiManager apiForProtocol:@protocol(FxParameterSettingAPI_v5)]; + if (paramSetAPI == nil) + { + //--------------------------------------------------------- + // Stop Action API: + //--------------------------------------------------------- + [actionAPI endAction:self]; + + [self showAlertWithMessage:@"An error has occurred." info:@"Unable to retrieve 'FxParameterSettingAPI_v5'.\n\nThis shouldn't happen, so it's probably a bug."]; + return; + } + + //--------------------------------------------------------- + // Read the Gyroflow Project Data from File: + //--------------------------------------------------------- + [url startAccessingSecurityScopedResource]; + + NSError *readError = nil; + NSString *selectedGyroflowProjectData = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&readError]; + [url stopAccessingSecurityScopedResource]; + if (readError != nil) { + //--------------------------------------------------------- + // Stop Action API: + //--------------------------------------------------------- + [actionAPI endAction:self]; + + [self showAlertWithMessage:@"An error has occurred." info:[NSString stringWithFormat:@"Failed to read Gyroflow Project file due to:\n\n%@", [readError localizedDescription]]]; + return; + } + + //--------------------------------------------------------- + // Update 'Gyroflow Project Data': + //--------------------------------------------------------- + [paramSetAPI setParameterFlags:kFxParameterFlag_DEFAULT toParameter:kCB_GyroflowProjectData]; + [paramSetAPI setStringParameterValue:selectedGyroflowProjectData toParameter:kCB_GyroflowProjectData]; + [paramSetAPI setParameterFlags:kFxParameterFlag_HIDDEN toParameter:kCB_GyroflowProjectData]; + + //--------------------------------------------------------- + // Stop Action API: + //--------------------------------------------------------- + [actionAPI endAction:self]; + + //--------------------------------------------------------- + // Show success message: + //--------------------------------------------------------- + [self showAlertWithMessage:@"Success!" info:@"The Gyroflow Project has been successfully reloaded from disk."]; +} + +//--------------------------------------------------------- +// Show Alert: +//--------------------------------------------------------- +- (void)showAlertWithMessage:(NSString*)message info:(NSString*)info +{ + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + alert.icon = [NSImage imageNamed:@"GyroflowToolbox"]; + alert.alertStyle = NSAlertStyleInformational; + alert.messageText = message; + alert.informativeText = info; + [alert runModal]; +} + +//--------------------------------------------------------- +// Because custom views are hosted in an overlay window, +// the first click on them will normally just make the +// overlay window be the key window, and it will require a +// second click in order to actually tell the view to +// start responding. By returning YES from this method, the +// first click begins user interaction with the view. +//--------------------------------------------------------- +- (BOOL)acceptsFirstMouse:(NSEvent *)event +{ + return YES; +} + +@end + diff --git a/Source/Gyroflow/Plugin/TileableRemoteGyroflow.metal b/Source/Gyroflow/Plugin/TileableRemoteGyroflow.metal new file mode 100644 index 00000000..6b43e96b --- /dev/null +++ b/Source/Gyroflow/Plugin/TileableRemoteGyroflow.metal @@ -0,0 +1,116 @@ +// +// TileableRemoteGyroflow.metal +// Gyroflow for Final Cut Pro +// +// Created by Chris Hocking on 10/12/2022. +// + +#include +#include + +using namespace metal; + +#include "TileableRemoteGyroflowShaderTypes.h" + +typedef struct +{ + //--------------------------------------------------------- + // The [[position]] attribute of this member indicates that + // this value is the clip space position of the vertex when + // this structure is returned from the vertex function: + //--------------------------------------------------------- + float4 clipSpacePosition [[position]]; + + //--------------------------------------------------------- + // Since this member does not have a special attribute, the + // rasterizer interpolates its value with the values of the + // other triangle vertices and then passes the interpolated + // value to the fragment shader for each fragment in the + // triangle: + //--------------------------------------------------------- + float2 textureCoordinate; + +} RasterizerData; + +vertex RasterizerData + +//--------------------------------------------------------- +// Vertex function: +//--------------------------------------------------------- +vertexShader(uint vertexID [[vertex_id]], + constant Vertex2D *vertexArray [[buffer(BVI_Vertices)]], + constant vector_uint2 *viewportSizePointer [[buffer(BVI_ViewportSize)]]) +{ + RasterizerData out; + + //--------------------------------------------------------- + // Index into our array of positions to get the current + // vertex. Our positions are specified in pixel dimensions + // (i.e. a value of 100 is 100 pixels from the origin): + //--------------------------------------------------------- + float2 pixelSpacePosition = vertexArray[vertexID].position.xy; + + //--------------------------------------------------------- + // Get the size of the drawable so that we can convert to + // normalized device coordinates: + //--------------------------------------------------------- + float2 viewportSize = float2(*viewportSizePointer); + + //--------------------------------------------------------- + // The output position of every vertex shader is in clip + // space (also known as normalized device coordinate space, + // or NDC). A value of (-1.0, -1.0) in clip-space represents + // the lower-left corner of the viewport whereas (1.0, 1.0) + // represents the upper-right corner of the viewport. + //--------------------------------------------------------- + + //--------------------------------------------------------- + // In order to convert from positions in pixel space to + // positions in clip space we divide the pixel coordinates + // by half the size of the viewport. + //--------------------------------------------------------- + out.clipSpacePosition.xy = pixelSpacePosition / (viewportSize / 2.0); + + //--------------------------------------------------------- + // Set the z component of our clip space position 0 + // (since we're only rendering in 2-Dimensions): + //--------------------------------------------------------- + out.clipSpacePosition.z = 0.0; + + //--------------------------------------------------------- + // Set the w component to 1.0 since we don't need a + // perspective divide, which is also not necessary when + // rendering in 2-Dimensions: + //--------------------------------------------------------- + out.clipSpacePosition.w = 1.0; + + //--------------------------------------------------------- + // Pass our input textureCoordinate straight to our output + // RasterizerData. This value will be interpolated with the + // other textureCoordinate values in the vertices that make + // up the triangle. + //--------------------------------------------------------- + out.textureCoordinate = vertexArray[vertexID].textureCoordinate; + + return out; +} + +//--------------------------------------------------------- +// Fragment function: +//--------------------------------------------------------- +fragment float4 fragmentShader(RasterizerData in [[stage_in]], + texture2d colorTexture [[ texture(BTI_InputImage) ]]) +{ + constexpr sampler textureSampler (mag_filter::linear, + min_filter::linear); + + //--------------------------------------------------------- + // Sample the texture to obtain a color: + //--------------------------------------------------------- + half4 colorSample = colorTexture.sample(textureSampler, in.textureCoordinate); + + //--------------------------------------------------------- + // We return the color of the texture: + //--------------------------------------------------------- + return float4(colorSample); +} diff --git a/Source/Gyroflow/Plugin/TileableRemoteGyroflowShaderTypes.h b/Source/Gyroflow/Plugin/TileableRemoteGyroflowShaderTypes.h new file mode 100644 index 00000000..70396ed0 --- /dev/null +++ b/Source/Gyroflow/Plugin/TileableRemoteGyroflowShaderTypes.h @@ -0,0 +1,27 @@ +// +// TileableRemoteGyroflowShaderTypes.h +// Gyroflow for Final Cut Pro +// +// Created by Chris Hocking on 10/12/2022. +// + +#ifndef TileableRemoteGyroflowShaderTypes_h +#define TileableRemoteGyroflowShaderTypes_h + +#import + +typedef enum GyroflowVertexInputIndex { + BVI_Vertices = 0, + BVI_ViewportSize = 1 +} GyroflowVertexInputIndex; + +typedef enum GyroflowTextureIndex { + BTI_InputImage = 0 +} GyroflowTextureIndex; + +typedef struct Vertex2D { + vector_float2 position; + vector_float2 textureCoordinate; +} Vertex2D; + +#endif /* TileableRemoteGyroflowShaderTypes_h */ diff --git a/Source/Gyroflow/Wrapper Application/Resources/Motion Templates/Gyroflow Toolbox/Gyroflow Toolbox.moef b/Source/Gyroflow/Wrapper Application/Resources/Motion Templates/Gyroflow Toolbox/Gyroflow Toolbox.moef index 04aef73c..a0613e21 100644 --- a/Source/Gyroflow/Wrapper Application/Resources/Motion Templates/Gyroflow Toolbox/Gyroflow Toolbox.moef +++ b/Source/Gyroflow/Wrapper Application/Resources/Motion Templates/Gyroflow Toolbox/Gyroflow Toolbox.moef @@ -73,7 +73,7 @@ 0 Active Camera 0 - + @@ -110,7 +110,7 @@ 1 1.5555555555555556 1.5555555555555556 - + @@ -118,8 +118,8 @@ 0 0 0 - - + + 0 @@ -161,11 +161,9 @@ 2 - - + - - + @@ -254,24 +252,17 @@ 8589934609 - - NOTHING LOADED - - + 0 *********J2eMb-gOLBoA11I*E61-*I4-klM75NZQbBdPqtN743mMqVdRaJmJ0FoPr-M74xWOaJXR5AG**44c3wE1otHGqJtNKF-QaBcOLNZQh660Ec9L2FVR437QolZNq3XSJwE1o7gOKtYF43oMIxWOaJXR6*0U*4X1EsDJGFiRKlgHl0GMb-gOLBoA11I*E61-*I4-kdM75NZQbBdPqtN743mMqVdRaJmJ0FoPr-M74xWOaJXR5AG**44c3wE1otHGqJtNKF-QaBcOLNZQh260Jh1RLBoPqoUF43oMM*-cUgAJGFiRKlg2**62FcY8H6rGIlMKZpX*********E2*********1E*******************4I6**U*2E*O*0E*8E*m*1Q*GE-C*3g*PE-j*52*RE-v*F*********0*E*********E*******************-2E** *********J2eMb-gOLBoA11I*E61-*I4-klM75NZQbBdPqtN743mMqVdRaJmJ0FoPr-M74xWOaJXR5AG**44c3wE1otHGqJtNKF-QaBcOLNZQh660Ec9L2FVR437QolZNq3XSJwE1o7gOKtYF43oMIxWOaJXR6*0U*4X1EsDJGFiRKlgHl0GMb-gOLBoA11I*E61-*I4-kdM75NZQbBdPqtN743mMqVdRaJmJ0FoPr-M74xWOaJXR5AG**44c3wE1otHGqJtNKF-QaBcOLNZQh260Jh1RLBoPqoUF43oMM*-cUgAJGFiRKlg2**62FcY8H6rGIlMKZpX*********E2*********1E*******************4I6**U*2E*O*0E*8E*m*1Q*GE-C*3g*PE-j*52*RE-v*F*********0*E*********E*******************-2E** - - 0 - *********J2eMb-gOLBoA11I*E61-*I4-klM75NZQbBdPqtN743mMqVdRaJmJ0FoPr-M74xWOaJXR5AG**44c3wE1otHGqJtNKF-QaBcOLNZQh660Ec9L2FVR437QolZNq3XSJwE1o7gOKtYF43oMIxWOaJXR6*0U*4X1EsDJGFiRKlgHl0GMb-gOLBoA11I*E61-*I4-kdM75NZQbBdPqtN743mMqVdRaJmJ0FoPr-M74xWOaJXR5AG**44c3wE1otHGqJtNKF-QaBcOLNZQh260Jh1RLBoPqoUF43oMM*-cUgAJGFiRKlg2**62FcY8H6rGIlMKZpX*********E2*********1E*******************4I6**U*2E*O*0E*8E*m*1Q*GE-C*3g*PE-j*52*RE-v*F*********0*E*********E*******************-2E** - *********J2eMb-gOLBoA11I*E61-*I4-klM75NZQbBdPqtN743mMqVdRaJmJ0FoPr-M74xWOaJXR5AG**44c3wE1otHGqJtNKF-QaBcOLNZQh660Ec9L2FVR437QolZNq3XSJwE1o7gOKtYF43oMIxWOaJXR6*0U*4X1EsDJGFiRKlgHl0GMb-gOLBoA11I*E61-*I4-kdM75NZQbBdPqtN743mMqVdRaJmJ0FoPr-M74xWOaJXR5AG**44c3wE1otHGqJtNKF-QaBcOLNZQh260Jh1RLBoPqoUF43oMM*-cUgAJGFiRKlg2**62FcY8H6rGIlMKZpX*********E2*********1E*******************4I6**U*2E*O*0E*8E*m*1Q*GE-C*3g*PE-j*52*RE-v*F*********0*E*********E*******************-2E** - - + 0 *********J2eMb-gOLBoA11I*E61-*I4-klM75NZQbBdPqtN743mMqVdRaJmJ0FoPr-M74xWOaJXR5AG**44c3wE1otHGqJtNKF-QaBcOLNZQh660Ec9L2FVR437QolZNq3XSJwE1o7gOKtYF43oMIxWOaJXR6*0U*4X1EsDJGFiRKlgHl0GMb-gOLBoA11I*E61-*I4-kdM75NZQbBdPqtN743mMqVdRaJmJ0FoPr-M74xWOaJXR5AG**44c3wE1otHGqJtNKF-QaBcOLNZQh260Jh1RLBoPqoUF43oMM*-cUgAJGFiRKlg2**62FcY8H6rGIlMKZpX*********E2*********1E*******************4I6**U*2E*O*0E*8E*m*1Q*GE-C*3g*PE-j*52*RE-v*F*********0*E*********E*******************-2E** *********J2eMb-gOLBoA11I*E61-*I4-klM75NZQbBdPqtN743mMqVdRaJmJ0FoPr-M74xWOaJXR5AG**44c3wE1otHGqJtNKF-QaBcOLNZQh660Ec9L2FVR437QolZNq3XSJwE1o7gOKtYF43oMIxWOaJXR6*0U*4X1EsDJGFiRKlgHl0GMb-gOLBoA11I*E61-*I4-kdM75NZQbBdPqtN743mMqVdRaJmJ0FoPr-M74xWOaJXR5AG**44c3wE1otHGqJtNKF-QaBcOLNZQh260Jh1RLBoPqoUF43oMM*-cUgAJGFiRKlg2**62FcY8H6rGIlMKZpX*********E2*********1E*******************4I6**U*2E*O*0E*8E*m*1Q*GE-C*3g*PE-j*52*RE-v*F*********0*E*********E*******************-2E** + 0 *********J2eMb-gOLBoA11I*E61-*I4-klM75NZQbBdPqtN743mMqVdRaJmJ0FoPr-M74xWOaJXR5AG**44c3wE1otHGqJtNKF-QaBcOLNZQh660Ec9L2FVR437QolZNq3XSJwE1o7gOKtYF43oMIxWOaJXR6*0U*4X1EsDJGFiRKlgHl0GMb-gOLBoA11I*E61-*I4-kdM75NZQbBdPqtN743mMqVdRaJmJ0FoPr-M74xWOaJXR5AG**44c3wE1otHGqJtNKF-QaBcOLNZQh260Jh1RLBoPqoUF43oMM*-cUgAJGFiRKlg2**62FcY8H6rGIlMKZpX*********E2*********1E*******************4I6**U*2E*O*0E*8E*m*1Q*GE-C*3g*PE-j*52*RE-v*F*********0*E*********E*******************-2E** @@ -283,13 +274,7 @@ - - - - - - - +