From 6d3f3c58ab02eb9ed628e6e76399b5e409bcd3e3 Mon Sep 17 00:00:00 2001 From: Chris Hocking Date: Tue, 11 Jul 2023 23:49:33 +1000 Subject: [PATCH] Restored previous WIP code - All working, however the "Input rotation" parameter has some caching issues. --- Scripts/build_rust.sh | 2 +- Scripts/build_rust_clean.sh | 17 + Source/Frameworks/gyroflow/Cargo.toml | 4 +- Source/Frameworks/gyroflow/inc/gyroflow.h | 6 + Source/Frameworks/gyroflow/src/lib.rs | 70 +- Source/Gyroflow.xcodeproj/project.pbxproj | 44 +- Source/Gyroflow/Plugin/CustomButtonView.h | 22 + ...aunchGyroflowView.m => CustomButtonView.m} | 83 +- Source/Gyroflow/Plugin/CustomDropZoneView.h | 22 + Source/Gyroflow/Plugin/CustomDropZoneView.m | 166 +++ Source/Gyroflow/Plugin/GyroflowConstants.h | 41 +- Source/Gyroflow/Plugin/GyroflowParameters.h | 16 +- Source/Gyroflow/Plugin/GyroflowParameters.m | 30 +- Source/Gyroflow/Plugin/GyroflowPlugIn.h | 11 +- Source/Gyroflow/Plugin/GyroflowPlugIn.m | 1288 ++++++++++++++--- .../Plugin/ImportGyroflowProjectView.h | 19 - .../Plugin/ImportGyroflowProjectView.m | 252 ---- Source/Gyroflow/Plugin/LaunchGyroflowView.h | 19 - Source/Gyroflow/Plugin/MetalDeviceCache.h | 4 +- Source/Gyroflow/Plugin/MetalDeviceCache.m | 86 +- .../Plugin/ReloadGyroflowProjectView.h | 19 - .../Plugin/ReloadGyroflowProjectView.m | 228 --- .../Plugin/TileableRemoteGyroflow.metal | 116 -- .../TileableRemoteGyroflowShaderTypes.h | 27 - .../Gyroflow Toolbox/Gyroflow Toolbox.moef | 51 +- 25 files changed, 1535 insertions(+), 1108 deletions(-) create mode 100755 Scripts/build_rust_clean.sh create mode 100644 Source/Gyroflow/Plugin/CustomButtonView.h rename Source/Gyroflow/Plugin/{LaunchGyroflowView.m => CustomButtonView.m} (59%) create mode 100644 Source/Gyroflow/Plugin/CustomDropZoneView.h create mode 100644 Source/Gyroflow/Plugin/CustomDropZoneView.m delete mode 100644 Source/Gyroflow/Plugin/ImportGyroflowProjectView.h delete mode 100644 Source/Gyroflow/Plugin/ImportGyroflowProjectView.m delete mode 100644 Source/Gyroflow/Plugin/LaunchGyroflowView.h delete mode 100644 Source/Gyroflow/Plugin/ReloadGyroflowProjectView.h delete mode 100644 Source/Gyroflow/Plugin/ReloadGyroflowProjectView.m delete mode 100644 Source/Gyroflow/Plugin/TileableRemoteGyroflow.metal delete mode 100644 Source/Gyroflow/Plugin/TileableRemoteGyroflowShaderTypes.h diff --git a/Scripts/build_rust.sh b/Scripts/build_rust.sh index a6ca509f..bae14180 100755 --- a/Scripts/build_rust.sh +++ b/Scripts/build_rust.sh @@ -7,7 +7,7 @@ export SCRIPT_HOME ; SCRIPT_HOME="$(dirname "$(greadlink -f "$0")")" export REPO_HOME ; REPO_HOME="$(greadlink -f "${SCRIPT_HOME}/../")" cd "${REPO_HOME}/Source/Frameworks/gyroflow" -~/.cargo/bin/cargo clean +#~/.cargo/bin/cargo clean ~/.cargo/bin/cargo update ~/.cargo/bin/cargo build --release --target x86_64-apple-darwin ~/.cargo/bin/cargo build --release --target aarch64-apple-darwin diff --git a/Scripts/build_rust_clean.sh b/Scripts/build_rust_clean.sh new file mode 100755 index 00000000..a6ca509f --- /dev/null +++ b/Scripts/build_rust_clean.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -eu +set -o pipefail + +export SCRIPT_HOME ; SCRIPT_HOME="$(dirname "$(greadlink -f "$0")")" +export REPO_HOME ; REPO_HOME="$(greadlink -f "${SCRIPT_HOME}/../")" + +cd "${REPO_HOME}/Source/Frameworks/gyroflow" +~/.cargo/bin/cargo clean +~/.cargo/bin/cargo update +~/.cargo/bin/cargo build --release --target x86_64-apple-darwin +~/.cargo/bin/cargo build --release --target aarch64-apple-darwin +lipo -create -output "${REPO_HOME}/Source/Frameworks/gyroflow/target/libgyroflow_toolbox.dylib" "${REPO_HOME}/Source/Frameworks/gyroflow/target/x86_64-apple-darwin/release/libgyroflow_toolbox.dylib" "${REPO_HOME}/Source/Frameworks/gyroflow/target/aarch64-apple-darwin/release/libgyroflow_toolbox.dylib" +/bin/mv "${REPO_HOME}/Source/Frameworks/gyroflow/target/libgyroflow_toolbox.dylib" "${REPO_HOME}/Source/Frameworks/gyroflow/binary/libgyroflow_toolbox.dylib" +cd "${REPO_HOME}/Source/Frameworks/gyroflow/binary" +/usr/bin/install_name_tool -id "@rpath/libgyroflow_toolbox.dylib" libgyroflow_toolbox.dylib diff --git a/Source/Frameworks/gyroflow/Cargo.toml b/Source/Frameworks/gyroflow/Cargo.toml index 2f16f4b9..e0ccd2ae 100644 --- a/Source/Frameworks/gyroflow/Cargo.toml +++ b/Source/Frameworks/gyroflow/Cargo.toml @@ -3,7 +3,7 @@ name = "gyroflow-toolbox" version = "1.1.0" authors = ["Chris Hocking ", "Adrian "] edition = "2021" -description = "Gyroflow Toolbox" +description = "Connects the Gyroflow Toolbox FxPlug4 API to the gyroflow_core Engine" [lib] crate-type =["cdylib"] @@ -20,4 +20,4 @@ lazy_static = "1.4.0" lru = "0.10" nalgebra = { version = "0.32", features = ["serde-serialize"] } once_cell = "1.16.0" -metal = { version = "0.25.0", git = "https://github.com/gfx-rs/metal-rs.git", rev = "a6a0446" } \ No newline at end of file +metal = { version = "0.25.0", git = "https://github.com/gfx-rs/metal-rs.git", rev = "a6a0446" } diff --git a/Source/Frameworks/gyroflow/inc/gyroflow.h b/Source/Frameworks/gyroflow/inc/gyroflow.h index 1e3c8a1a..ca64ac8d 100644 --- a/Source/Frameworks/gyroflow/inc/gyroflow.h +++ b/Source/Frameworks/gyroflow/inc/gyroflow.h @@ -23,6 +23,12 @@ 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 input_rotation, + double video_rotation, 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 e50013bb..f818c46a 100644 --- a/Source/Frameworks/gyroflow/src/lib.rs +++ b/Source/Frameworks/gyroflow/src/lib.rs @@ -46,6 +46,12 @@ 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, + input_rotation: f64, + video_rotation: f64, in_mtl_tex: *mut std::ffi::c_void, out_mtl_tex: *mut std::ffi::c_void, command_queue: *mut std::ffi::c_void, @@ -61,13 +67,13 @@ pub extern "C" fn processFrame( .init().ok(); Mutex::new(logger) }); - + //--------------------------------------------------------- // Get Pixel Format: //--------------------------------------------------------- let pixel_format_pointer = unsafe { CStr::from_ptr(pixel_format) }; let pixel_format_string = pixel_format_pointer.to_string_lossy(); - + // ------------------------------------------------------------------------------- // You can't use &str across FFI boundary, it's a Rust type. // You have to use C-compatible char pointer, so path: *const c_char and then @@ -76,13 +82,13 @@ pub extern "C" fn processFrame( // ------------------------------------------------------------------------------- let path_pointer = unsafe { CStr::from_ptr(path) }; let path_string = path_pointer.to_string_lossy(); - + //--------------------------------------------------------- // Convert the output width and height to `usize`: //--------------------------------------------------------- let output_width: usize = width as usize; let output_height: usize = height as usize; - + //--------------------------------------------------------- // Convert the number of bytes to `usize`: //--------------------------------------------------------- @@ -103,7 +109,7 @@ pub extern "C" fn processFrame( // Setup the Gyroflow Manager: //--------------------------------------------------------- let manager = StabilizationManager::default(); - + //--------------------------------------------------------- // Import the Gyroflow Data: //--------------------------------------------------------- @@ -164,7 +170,7 @@ pub extern "C" fn processFrame( params.fov = fov; params_changed = true; } - + //--------------------------------------------------------- // Set the Lens Correction: //--------------------------------------------------------- @@ -172,19 +178,51 @@ 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 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: @@ -194,7 +232,7 @@ pub extern "C" fn processFrame( manager.recompute_blocking(); manager.params.write().calculate_ramped_timestamps(&manager.keyframes.read(), false, false); } - + //--------------------------------------------------------- // Calculate buffer size and stride: //--------------------------------------------------------- @@ -210,17 +248,17 @@ pub extern "C" fn processFrame( rect: None, data: BufferSource::Metal { texture: out_mtl_tex as *mut metal::MTLTexture, command_queue: command_queue as *mut metal::MTLCommandQueue }, rotation: None, - texture_copy: true, + texture_copy: false, }, input: BufferDescription { 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 }, - rotation: None, - texture_copy: true, + rotation: Some(input_rotation as f32), + texture_copy: false, } }; - + let _stabilization_result = match pixel_format_string.as_ref() { "BGRA8Unorm" => { manager.process_pixels::(timestamp, &mut buffers) @@ -237,7 +275,7 @@ pub extern "C" fn processFrame( return result.into_raw() } }; - + //--------------------------------------------------------- // Output the Stabilization result to the Console: //--------------------------------------------------------- diff --git a/Source/Gyroflow.xcodeproj/project.pbxproj b/Source/Gyroflow.xcodeproj/project.pbxproj index 90be2e9b..b5ed0492 100644 --- a/Source/Gyroflow.xcodeproj/project.pbxproj +++ b/Source/Gyroflow.xcodeproj/project.pbxproj @@ -20,7 +20,6 @@ 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 */; }; @@ -30,10 +29,9 @@ 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 */; }; - 228E9E1829517D8200B2571E /* ImportGyroflowProjectView.m in Sources */ = {isa = PBXBuildFile; fileRef = 228E9E1729517D8200B2571E /* ImportGyroflowProjectView.m */; }; - 228E9E1C2951DB6900B2571E /* ReloadGyroflowProjectView.m in Sources */ = {isa = PBXBuildFile; fileRef = 228E9E1B2951DB6900B2571E /* ReloadGyroflowProjectView.m */; }; + 22BED1C32A5D6805000562A3 /* CustomButtonView.m in Sources */ = {isa = PBXBuildFile; fileRef = 22BED1BF2A5D6805000562A3 /* CustomButtonView.m */; }; + 22BED1C42A5D6805000562A3 /* CustomDropZoneView.m in Sources */ = {isa = PBXBuildFile; fileRef = 22BED1C22A5D6805000562A3 /* CustomDropZoneView.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 */ @@ -97,9 +95,7 @@ 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 = ""; }; @@ -113,14 +109,12 @@ 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 = ""; }; - 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 = ""; }; + 22BED1BF2A5D6805000562A3 /* CustomButtonView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CustomButtonView.m; path = "../../../../../../Desktop/WIP Gyroflow on GitHub/GyroflowToolbox-df8de2438817be9f802629da3510916f78447d9c/Source/Gyroflow/Plugin/CustomButtonView.m"; sourceTree = ""; }; + 22BED1C02A5D6805000562A3 /* CustomButtonView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CustomButtonView.h; path = "../../../../../../Desktop/WIP Gyroflow on GitHub/GyroflowToolbox-df8de2438817be9f802629da3510916f78447d9c/Source/Gyroflow/Plugin/CustomButtonView.h"; sourceTree = ""; }; + 22BED1C12A5D6805000562A3 /* CustomDropZoneView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CustomDropZoneView.h; path = "../../../../../../Desktop/WIP Gyroflow on GitHub/GyroflowToolbox-df8de2438817be9f802629da3510916f78447d9c/Source/Gyroflow/Plugin/CustomDropZoneView.h"; sourceTree = ""; }; + 22BED1C22A5D6805000562A3 /* CustomDropZoneView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CustomDropZoneView.m; path = "../../../../../../Desktop/WIP Gyroflow on GitHub/GyroflowToolbox-df8de2438817be9f802629da3510916f78447d9c/Source/Gyroflow/Plugin/CustomDropZoneView.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 */ @@ -208,7 +202,6 @@ 2239AB7B2945A67600028B77 /* Rust */, 228E9E13294E815E00B2571E /* Code */, 228E9E12294E815200B2571E /* Metal Device Cache */, - 228E9E14294E816D00B2571E /* Shader */, 228E9E15294E817700B2571E /* Extras */, ); path = Plugin; @@ -293,15 +286,6 @@ name = Code; sourceTree = ""; }; - 228E9E14294E816D00B2571E /* Shader */ = { - isa = PBXGroup; - children = ( - 2239AB402943F8F600028B77 /* TileableRemoteGyroflow.metal */, - 2239AB432943F8F600028B77 /* TileableRemoteGyroflowShaderTypes.h */, - ); - name = Shader; - sourceTree = ""; - }; 228E9E15294E817700B2571E /* Extras */ = { isa = PBXGroup; children = ( @@ -317,12 +301,10 @@ 22DAC7632953D1CF001F2E06 /* Custom Views */ = { isa = PBXGroup; children = ( - 22DAC7612953CD4F001F2E06 /* LaunchGyroflowView.h */, - 22DAC7602953CD4F001F2E06 /* LaunchGyroflowView.m */, - 228E9E1629517D8200B2571E /* ImportGyroflowProjectView.h */, - 228E9E1729517D8200B2571E /* ImportGyroflowProjectView.m */, - 228E9E1A2951DB6800B2571E /* ReloadGyroflowProjectView.h */, - 228E9E1B2951DB6900B2571E /* ReloadGyroflowProjectView.m */, + 22BED1C02A5D6805000562A3 /* CustomButtonView.h */, + 22BED1BF2A5D6805000562A3 /* CustomButtonView.m */, + 22BED1C12A5D6805000562A3 /* CustomDropZoneView.h */, + 22BED1C22A5D6805000562A3 /* CustomDropZoneView.m */, ); name = "Custom Views"; sourceTree = ""; @@ -486,13 +468,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 228E9E1C2951DB6900B2571E /* ReloadGyroflowProjectView.m in Sources */, 2239AB392943F8F600028B77 /* MetalDeviceCache.m in Sources */, - 228E9E1829517D8200B2571E /* ImportGyroflowProjectView.m in Sources */, - 2239AB412943F8F600028B77 /* TileableRemoteGyroflow.metal in Sources */, + 22BED1C32A5D6805000562A3 /* CustomButtonView.m in Sources */, 2239AB452943F8F600028B77 /* main.m in Sources */, + 22BED1C42A5D6805000562A3 /* CustomDropZoneView.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 new file mode 100644 index 00000000..23cf4a44 --- /dev/null +++ b/Source/Gyroflow/Plugin/CustomButtonView.h @@ -0,0 +1,22 @@ +// +// 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/LaunchGyroflowView.m b/Source/Gyroflow/Plugin/CustomButtonView.m similarity index 59% rename from Source/Gyroflow/Plugin/LaunchGyroflowView.m rename to Source/Gyroflow/Plugin/CustomButtonView.m index 8b5408bd..54e5b32a 100644 --- a/Source/Gyroflow/Plugin/LaunchGyroflowView.m +++ b/Source/Gyroflow/Plugin/CustomButtonView.m @@ -1,62 +1,79 @@ // -// LaunchGyroflowView.m -// Gyroflow Toolbox +// CustomButtonView.m +// Gyroflow Toolbox Renderer // -// Created by Chris Hocking on 22/12/2022. +// Created by Chris Hocking on 29/01/2023. // -#import "LaunchGyroflowView.h" -#import "GyroflowConstants.h" - +#import "CustomButtonView.h" #import -#import -@implementation LaunchGyroflowView { - NSButton* _cachedButton; +@implementation CustomButtonView { + NSButton* _button; } //--------------------------------------------------------- // Initialize: //--------------------------------------------------------- -- (instancetype)initWithFrame:(NSRect)frameRect - andAPIManager:(id)apiManager +- (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; + //--------------------------------------------------------- - // Cache the API Manager: + // Cache the parent plugin & button ID: //--------------------------------------------------------- - _apiManager = apiManager; + _parentPlugin = parentPlugin; + _buttonID = buttonID; //--------------------------------------------------------- - // Add the "Import Gyroflow Project" button: + // Add the button: //--------------------------------------------------------- - NSButton *button = [[NSButton alloc]initWithFrame:NSMakeRect(0, 0, 130, 30)]; // x y w h + 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:@"Launch Gyroflow"]; + [button setTitle:buttonTitle]; [button setTarget:self]; [button setAction:@selector(buttonPressed)]; - _cachedButton = button; - [self addSubview:_cachedButton]; + _button = button; + [self addSubview:_button]; } 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 (_cachedButton) { - [_cachedButton release]; + if (_button) { + [_button release]; } [super dealloc]; @@ -72,32 +89,6 @@ - (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/CustomDropZoneView.h b/Source/Gyroflow/Plugin/CustomDropZoneView.h new file mode 100644 index 00000000..82bb5961 --- /dev/null +++ b/Source/Gyroflow/Plugin/CustomDropZoneView.h @@ -0,0 +1,22 @@ +// +// 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 new file mode 100644 index 00000000..57fa082d --- /dev/null +++ b/Source/Gyroflow/Plugin/CustomDropZoneView.m @@ -0,0 +1,166 @@ +// +// 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 6a2c73eb..f371ebc4 100644 --- a/Source/Gyroflow/Plugin/GyroflowConstants.h +++ b/Source/Gyroflow/Plugin/GyroflowConstants.h @@ -1,6 +1,6 @@ // // GyroflowConstants.h -// Gyroflow Toolbox +// Gyroflow Toolbox Renderer // // Created by Chris Hocking on 20/12/2022. // @@ -11,20 +11,33 @@ //--------------------------------------------------------- // Plugin Parameter Constants: //--------------------------------------------------------- -enum { - kCB_LaunchGyroflow = 20, - kCB_ImportGyroflowProject = 30, - kCB_LoadedGyroflowProject = 40, - kCB_ReloadGyroflowProject = 50, +enum { + + kCB_DropZone = 10, + + kCB_LaunchGyroflow = 20, + kCB_LoadLastGyroflowProject = 25, + kCB_ImportGyroflowProject = 30, + kCB_LoadedGyroflowProject = 40, + kCB_ReloadGyroflowProject = 50, + + kCB_GyroflowProjectPath = 60, + kCB_GyroflowProjectBookmarkData = 70, + kCB_GyroflowProjectData = 80, - kCB_GyroflowProjectPath = 60, - kCB_GyroflowProjectBookmarkData = 70, - kCB_GyroflowProjectData = 80, - - kCB_GyroflowParameters = 90, - kCB_FOV = 100, - kCB_Smoothness = 110, - kCB_LensCorrection = 120, + kCB_GyroflowParameters = 90, + kCB_FOV = 100, + kCB_Smoothness = 110, + kCB_LensCorrection = 120, + + kCB_HorizonLock = 130, + kCB_HorizonRoll = 140, + + kCB_PositionOffsetX = 150, + kCB_PositionOffsetY = 160, + kCB_InputRotation = 170, + kCB_VideoRotation = 180, + kCB_VideoSpeed = 190, }; //--------------------------------------------------------- diff --git a/Source/Gyroflow/Plugin/GyroflowParameters.h b/Source/Gyroflow/Plugin/GyroflowParameters.h index 354c5296..f630c6d5 100644 --- a/Source/Gyroflow/Plugin/GyroflowParameters.h +++ b/Source/Gyroflow/Plugin/GyroflowParameters.h @@ -1,6 +1,6 @@ // // GyroflowParameters.h -// Gyroflow Toolbox +// Gyroflow Toolbox Renderer // // Created by Chris Hocking on 10/12/2022. // @@ -15,6 +15,13 @@ NSNumber *fov; NSNumber *smoothness; NSNumber *lensCorrection; + + NSNumber *horizonLock; + NSNumber *horizonRoll; + NSNumber *positionOffsetX; + NSNumber *positionOffsetY; + NSNumber *inputRotation; + NSNumber *videoRotation; } @property (nonatomic, copy) NSString *gyroflowPath; @@ -24,4 +31,11 @@ @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 *inputRotation; +@property (nonatomic, copy) NSNumber *videoRotation; + @end diff --git a/Source/Gyroflow/Plugin/GyroflowParameters.m b/Source/Gyroflow/Plugin/GyroflowParameters.m index 568dee7b..be269d9b 100644 --- a/Source/Gyroflow/Plugin/GyroflowParameters.m +++ b/Source/Gyroflow/Plugin/GyroflowParameters.m @@ -1,6 +1,6 @@ // // GyroflowParameters.m -// Gyroflow Toolbox +// Gyroflow Toolbox Renderer // // Created by Chris Hocking on 10/12/2022. // @@ -23,6 +23,13 @@ @implementation GyroflowParameters @synthesize smoothness; @synthesize lensCorrection; +@synthesize horizonLock; +@synthesize horizonRoll; +@synthesize positionOffsetX; +@synthesize positionOffsetY; +@synthesize inputRotation; +@synthesize videoRotation; + + (BOOL)supportsSecureCoding { return YES; @@ -36,6 +43,13 @@ - (void)dealloc { [smoothness release]; [lensCorrection release]; + [horizonLock release]; + [horizonRoll release]; + [positionOffsetX release]; + [positionOffsetY release]; + [inputRotation release]; + [videoRotation release]; + [super dealloc]; } @@ -47,6 +61,13 @@ - (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.inputRotation = [decoder decodeObjectOfClass:[NSNumber class] forKey:@"inputRotation"]; + self.videoRotation = [decoder decodeObjectOfClass:[NSNumber class] forKey:@"videoRotation"]; } return self; } @@ -58,6 +79,13 @@ - (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:inputRotation forKey:@"inputRotation"]; + [encoder encodeObject:videoRotation forKey:@"videoRotation"]; } @end diff --git a/Source/Gyroflow/Plugin/GyroflowPlugIn.h b/Source/Gyroflow/Plugin/GyroflowPlugIn.h index 52563c5f..12f5ab3b 100644 --- a/Source/Gyroflow/Plugin/GyroflowPlugIn.h +++ b/Source/Gyroflow/Plugin/GyroflowPlugIn.h @@ -1,6 +1,6 @@ // // GyroflowPlugIn.h -// Gyroflow Toolbox +// Gyroflow Toolbox Renderer // // Created by Chris Hocking on 10/12/2022. // @@ -8,10 +8,15 @@ #import #import -@interface GyroflowPlugIn : NSObject { +@interface GyroflowPlugIn : NSObject { + //--------------------------------------------------------- + // Cached Custom Views: + //--------------------------------------------------------- NSView* launchGyroflowView; NSView* importGyroflowProjectView; - NSView* reloadGyroflowProjectView; + NSView* reloadGyroflowProjectView; + NSView* loadLastGyroflowProjectView; + NSView* dropZoneView; } @property (assign) id apiManager; @end diff --git a/Source/Gyroflow/Plugin/GyroflowPlugIn.m b/Source/Gyroflow/Plugin/GyroflowPlugIn.m index a3fd9d51..80e362ac 100644 --- a/Source/Gyroflow/Plugin/GyroflowPlugIn.m +++ b/Source/Gyroflow/Plugin/GyroflowPlugIn.m @@ -1,6 +1,6 @@ // // GyroflowPlugIn.m -// Gyroflow Toolbox +// Gyroflow Toolbox Renderer // // Created by Chris Hocking on 10/12/2022. // @@ -14,11 +14,8 @@ #import "GyroflowParameters.h" #import "GyroflowConstants.h" -#import "LaunchGyroflowView.h" -#import "ImportGyroflowProjectView.h" -#import "ReloadGyroflowProjectView.h" - -#import "TileableRemoteGyroflowShaderTypes.h" +#import "CustomButtonView.h" +#import "CustomDropZoneView.h" #import "MetalDeviceCache.h" @@ -29,6 +26,11 @@ #include #include +#include +#include +#include +#include + //--------------------------------------------------------- // Gyroflow FxPlug4 Implementation: //--------------------------------------------------------- @@ -47,6 +49,21 @@ - (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; @@ -129,38 +146,49 @@ - (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 = [[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: - //--------------------------------------------------------- + NSView* view = [[CustomButtonView alloc] initWithAPIManager:_apiManager + parentPlugin:self + buttonID:kCB_ImportGyroflowProject + buttonTitle:@"Import Gyroflow Project"]; importGyroflowProjectView = view; return view; } else if (parameterID == kCB_ReloadGyroflowProject) { - 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: - //--------------------------------------------------------- + NSView* view = [[CustomButtonView alloc] initWithAPIManager:_apiManager + parentPlugin:self + buttonID:kCB_ReloadGyroflowProject + buttonTitle:@"Reload Gyroflow Project"]; reloadGyroflowProjectView = view; return view; } else if (parameterID == kCB_LaunchGyroflow) { - 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: - //--------------------------------------------------------- + NSView* view = [[CustomButtonView alloc] initWithAPIManager:_apiManager + parentPlugin:self + buttonID:kCB_LaunchGyroflow + buttonTitle:@"Open in Gyroflow"]; 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] BUG - createViewForParameterID requested a parameterID that we haven't allowed for: %u", (unsigned int)parameterID); + NSLog(@"[Gyroflow Toolbox Renderer] BUG - createViewForParameterID requested a parameterID that we haven't allowed for: %u", (unsigned int)parameterID); return nil; } } @@ -177,7 +205,7 @@ - (NSView*)createViewForParameterID:(UInt32)parameterID //--------------------------------------------------------- - (void)pluginInstanceAddedToDocument { - NSLog(@"[Gyroflow Toolbox] pluginInstanceAddedToDocument!"); + //NSLog(@"[Gyroflow Toolbox Renderer] pluginInstanceAddedToDocument!"); } //--------------------------------------------------------- @@ -195,24 +223,41 @@ - (BOOL)addParametersWithError:(NSError**)error { if (error != nil) { - NSString* description = [NSString stringWithFormat:@"[Gyroflow Toolbox] Unable to get the FxParameterCreationAPI_v5 in %s", __func__]; + NSString* description = [NSString stringWithFormat:@"[Gyroflow Toolbox Renderer] Unable to get the FxParameterCreationAPI_v5 in %s", __func__]; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_APIUnavailable userInfo:@{ NSLocalizedDescriptionKey : description }]; } return NO; } - + //--------------------------------------------------------- - // ADD PARAMETER: 'Launch Gyroflow' Button + // ADD PARAMETER: Drop Zone //--------------------------------------------------------- - if (![paramAPI addCustomParameterWithName:@"Launch Gyroflow" - parameterID:kCB_LaunchGyroflow + if (![paramAPI addCustomParameterWithName:@"Drop Zone" + parameterID:kCB_DropZone defaultValue:@0 parameterFlags:kFxParameterFlag_CUSTOM_UI | kFxParameterFlag_NOT_ANIMATABLE]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_LaunchGyroflow"}; + 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]; @@ -229,7 +274,7 @@ - (BOOL)addParametersWithError:(NSError**)error parameterFlags:kFxParameterFlag_CUSTOM_UI | kFxParameterFlag_NOT_ANIMATABLE]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_ImportGyroflowProject"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_ImportGyroflowProject"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -238,15 +283,15 @@ - (BOOL)addParametersWithError:(NSError**)error } //--------------------------------------------------------- - // ADD PARAMETER: 'Loaded Gyroflow Project' Text Box + // ADD PARAMETER: 'Import Last Saved Project' Button //--------------------------------------------------------- - if (![paramAPI addStringParameterWithName:@"Loaded Gyroflow Project" - parameterID:kCB_LoadedGyroflowProject - defaultValue:@"" - parameterFlags:kFxParameterFlag_DISABLED | kFxParameterFlag_NOT_ANIMATABLE]) + 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] Unable to add parameter: kCB_LoadedGyroflowProject"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_LoadLastGyroflowProject"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -255,7 +300,24 @@ - (BOOL)addParametersWithError:(NSError**)error } //--------------------------------------------------------- - // ADD PARAMETER: 'Import Gyroflow Project' Button + // ADD PARAMETER: 'Open in Gyroflow' Button + //--------------------------------------------------------- + if (![paramAPI addCustomParameterWithName:@"Open in 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_LaunchGyroflow"}; + *error = [NSError errorWithDomain:FxPlugErrorDomain + code:kFxError_InvalidParameter + userInfo:userInfo]; + } + return NO; + } + + //--------------------------------------------------------- + // ADD PARAMETER: 'Reload Gyroflow Project' Button //--------------------------------------------------------- if (![paramAPI addCustomParameterWithName:@"Reload Gyroflow Project" parameterID:kCB_ReloadGyroflowProject @@ -263,7 +325,7 @@ - (BOOL)addParametersWithError:(NSError**)error parameterFlags:kFxParameterFlag_CUSTOM_UI | kFxParameterFlag_NOT_ANIMATABLE]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_ReloadGyroflowProject"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_ReloadGyroflowProject"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -280,7 +342,7 @@ - (BOOL)addParametersWithError:(NSError**)error parameterFlags:kFxParameterFlag_HIDDEN | kFxParameterFlag_NOT_ANIMATABLE]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_GyroflowProjectPath"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_GyroflowProjectPath"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -297,7 +359,7 @@ - (BOOL)addParametersWithError:(NSError**)error parameterFlags:kFxParameterFlag_HIDDEN | kFxParameterFlag_NOT_ANIMATABLE]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_GyroflowProjectBookmarkData"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_GyroflowProjectBookmarkData"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -314,14 +376,14 @@ - (BOOL)addParametersWithError:(NSError**)error parameterFlags:kFxParameterFlag_HIDDEN | kFxParameterFlag_NOT_ANIMATABLE]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_GyroflowProjectData"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_GyroflowProjectData"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; } return NO; } - + //--------------------------------------------------------- // START GROUP: 'Gyroflow Parameters' //--------------------------------------------------------- @@ -330,7 +392,7 @@ - (BOOL)addParametersWithError:(NSError**)error parameterFlags:kFxParameterFlag_DEFAULT]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_GyroflowParameters"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_GyroflowParameters"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -340,6 +402,8 @@ - (BOOL)addParametersWithError:(NSError**)error //--------------------------------------------------------- // ADD PARAMETER: 'FOV' Slider + // + // NOTE: 0.1 to 0.3 in Gyroflow OpenFX //--------------------------------------------------------- if (![paramAPI addFloatSliderWithName:@"FOV" parameterID:kCB_FOV @@ -352,7 +416,7 @@ - (BOOL)addParametersWithError:(NSError**)error parameterFlags:kFxParameterFlag_DEFAULT]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_FOV"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_FOV"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -362,6 +426,8 @@ - (BOOL)addParametersWithError:(NSError**)error //--------------------------------------------------------- // ADD PARAMETER: 'Smoothness' Slider + // + // NOTE: 0.01 to 3.00 in Gyroflow OpenFX //--------------------------------------------------------- if (![paramAPI addFloatSliderWithName:@"Smoothness" parameterID:kCB_Smoothness @@ -374,7 +440,7 @@ - (BOOL)addParametersWithError:(NSError**)error parameterFlags:kFxParameterFlag_DEFAULT]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_Smoothness"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add parameter: kCB_Smoothness"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -384,10 +450,13 @@ - (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:100.0 + defaultValue:0.0 parameterMin:0.0 parameterMax:100.0 sliderMin:0.0 @@ -396,21 +465,169 @@ - (BOOL)addParametersWithError:(NSError**)error parameterFlags:kFxParameterFlag_DEFAULT]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add parameter: kCB_LensCorrection"}; + 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 + 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_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"}; + *error = [NSError errorWithDomain:FxPlugErrorDomain + code:kFxError_InvalidParameter + userInfo:userInfo]; + } + return NO; + } + + //--------------------------------------------------------- + // ADD PARAMETER: 'Input Rotation' Slider + // + // Resolve UI: -360 to 360 + // Gyroflow UI: TBC + // Internally: TBC + //--------------------------------------------------------- + if (![paramAPI addFloatSliderWithName:@"Input Rotation" + parameterID:kCB_InputRotation + 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_InputRotation"}; + *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; + } + //--------------------------------------------------------- // END GROUP: 'Gyroflow Parameters' //--------------------------------------------------------- if (![paramAPI endParameterSubGroup]) { if (error != NULL) { - NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] Unable to add end 'Gyroflow Parameters' Parameter"}; + NSDictionary* userInfo = @{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] Unable to add end 'Gyroflow Parameters' Parameter"}; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter userInfo:userInfo]; @@ -446,7 +663,7 @@ - (BOOL)pluginState:(NSData**)pluginState //--------------------------------------------------------- id timingAPI = [_apiManager apiForProtocol:@protocol(FxTimingAPI_v4)]; if (timingAPI == nil) { - NSLog(@"[Gyroflow Toolbox] Unable to retrieve FxTimingAPI_v4 in pluginStateAtTime."); + NSLog(@"[Gyroflow Toolbox Renderer] Unable to retrieve FxTimingAPI_v4 in pluginStateAtTime."); if (error != NULL) { *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_FailedToLoadTimingAPI @@ -463,7 +680,7 @@ - (BOOL)pluginState:(NSData**)pluginState //--------------------------------------------------------- id paramGetAPI = [_apiManager apiForProtocol:@protocol(FxParameterRetrievalAPI_v6)]; if (paramGetAPI == nil) { - NSLog(@"[Gyroflow Toolbox] Unable to retrieve FxParameterRetrievalAPI_v6 in pluginStateAtTime."); + NSLog(@"[Gyroflow Toolbox Renderer] Unable to retrieve FxParameterRetrievalAPI_v6 in pluginStateAtTime."); if (error != NULL) { *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_FailedToLoadParameterGetAPI @@ -489,18 +706,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: //--------------------------------------------------------- @@ -510,6 +727,22 @@ - (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: //--------------------------------------------------------- @@ -544,6 +777,48 @@ - (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]; + + //--------------------------------------------------------- + // Input Rotation: + //--------------------------------------------------------- + double inputRotation; + [paramGetAPI getFloatValue:&inputRotation fromParameter:kCB_InputRotation atTime:renderTime]; + params.inputRotation = [NSNumber numberWithDouble:inputRotation]; + + //--------------------------------------------------------- + // Video Rotation: + //--------------------------------------------------------- + double videoRotation; + [paramGetAPI getFloatValue:&videoRotation fromParameter:kCB_VideoRotation atTime:renderTime]; + params.videoRotation = [NSNumber numberWithDouble:videoRotation]; //--------------------------------------------------------- // Write the parameters to the pluginState as `NSData`: @@ -552,7 +827,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] ERROR - Failed to create newPluginState due to '%@'", [newPluginStateError localizedDescription]]; + NSString* errorMessage = [NSString stringWithFormat:@"[Gyroflow Toolbox Renderer] ERROR - Failed to create newPluginState due to '%@'", [newPluginStateError localizedDescription]]; *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_FailedToCreatePluginState userInfo:@{ NSLocalizedDescriptionKey : errorMessage }]; @@ -567,7 +842,7 @@ - (BOOL)pluginState:(NSData**)pluginState } else { *error = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_PlugInStateIsNil - userInfo:@{ NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] pluginState is nil in -pluginState." }]; + userInfo:@{ NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] pluginState is nil in -pluginState." }]; succeeded = NO; } @@ -598,7 +873,7 @@ - (BOOL)destinationImageRect:(FxRect *)destinationImageRect if (outError != NULL) { *outError = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_ThirdPartyDeveloperStart + 5 - userInfo:@{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] FATAL ERROR - No sourceImages in -destinationImageRect."}]; + userInfo:@{NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] FATAL ERROR - No sourceImages in -destinationImageRect."}]; } return NO; } @@ -658,7 +933,7 @@ - (BOOL)renderDestinationImage:(FxImageTile *)destinationImage if (outError != NULL) { *outError = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter - userInfo:@{ NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] FATAL ERROR - Invalid plugin state received from host." }]; + userInfo:@{ NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] FATAL ERROR - Invalid plugin state received from host." }]; } return NO; } @@ -670,7 +945,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] FATAL ERROR - Parameters was nil in -renderDestinationImage due to '%@'.", [paramsError localizedDescription]]; + NSString *errorMessage = [NSString stringWithFormat:@"[Gyroflow Toolbox Renderer] FATAL ERROR - Parameters was nil in -renderDestinationImage due to '%@'.", [paramsError localizedDescription]]; if (outError != NULL) { *outError = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter @@ -678,12 +953,6 @@ - (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: @@ -695,6 +964,13 @@ - (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 *inputRotation = params.inputRotation; + NSNumber *videoRotation = params.videoRotation; + //--------------------------------------------------------- // Set up the renderer, in this case we are using Metal. //--------------------------------------------------------- @@ -719,7 +995,7 @@ - (BOOL)renderDestinationImage:(FxImageTile *)destinationImage if (outError != NULL) { *outError = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter - userInfo:@{ NSLocalizedDescriptionKey : @"[Gyroflow Toolbox] FATAL ERROR - Unable to get command queue. May need to increase cache size." }]; + userInfo:@{ NSLocalizedDescriptionKey : @"[Gyroflow Toolbox Renderer] FATAL ERROR - Unable to get command queue. May need to increase cache size." }]; } return NO; } @@ -730,6 +1006,11 @@ - (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: //--------------------------------------------------------- @@ -745,7 +1026,7 @@ - (BOOL)renderDestinationImage:(FxImageTile *)destinationImage inputPixelFormat = @"RGBAf"; numberOfBytes = 4; } else { - NSString *errorMessage = [NSString stringWithFormat:@"[Gyroflow Toolbox] BUG - Unsupported pixelFormat for inputTexture: %lu", (unsigned long)inputTexture.pixelFormat]; + NSString *errorMessage = [NSString stringWithFormat:@"[Gyroflow Toolbox Renderer] BUG - Unsupported pixelFormat for inputTexture: %lu", (unsigned long)inputTexture.pixelFormat]; if (outError != NULL) { *outError = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_UnsupportedPixelFormat @@ -765,8 +1046,14 @@ - (BOOL)renderDestinationImage:(FxImageTile *)destinationImage int64_t sourceTimestamp = [timestamp floatValue]; double sourceFOV = [fov doubleValue]; double sourceSmoothness = [smoothness doubleValue]; - double sourceLensCorrection = [lensCorrection doubleValue]; - + double sourceLensCorrection = [lensCorrection doubleValue] / 100.0; + double sourceHorizonLock = [horizonLock doubleValue]; + double sourceHorizonRoll = [horizonRoll doubleValue]; + double sourcePositionOffsetX = [positionOffsetX doubleValue]; + double sourcePositionOffsetY = [positionOffsetY doubleValue]; + double sourceInputRotation = [inputRotation doubleValue]; + double sourceVideoRotation = [videoRotation doubleValue]; + //--------------------------------------------------------- // Only trigger the Rust function if we have Gyroflow Data: //--------------------------------------------------------- @@ -785,19 +1072,25 @@ - (BOOL)renderDestinationImage:(FxImageTile *)destinationImage sourceFOV, // double sourceSmoothness, // double sourceLensCorrection, // double + sourceHorizonLock, // double + sourceHorizonRoll, // double + sourcePositionOffsetX, // double + sourcePositionOffsetY, // double + sourceInputRotation, // double + sourceVideoRotation, // double inputTexture, // MTLTexture - inputTexture, // MTLTexture + outputTexture, // MTLTexture commandQueue // MTLCommandQueue ); NSString *resultString = [NSString stringWithUTF8String: result]; - //NSLog(@"[Gyroflow Toolbox] resultString: %@", resultString); + //NSLog(@"[Gyroflow Toolbox Renderer] resultString: %@", resultString); if ([resultString isEqualToString:@"FAIL"]) { //--------------------------------------------------------- // If we get a "FAIL" then abort: //--------------------------------------------------------- - NSString *errorMessage = @"[Gyroflow Toolbox] A fail message was received from the Rust function."; + NSString *errorMessage = @"[Gyroflow Toolbox Renderer] A fail message was received from the Rust function."; if (outError != NULL) { *outError = [NSError errorWithDomain:FxPlugErrorDomain code:kFxError_InvalidParameter @@ -811,7 +1104,7 @@ - (BOOL)renderDestinationImage:(FxImageTile *)destinationImage // Debugging: //--------------------------------------------------------- /* - NSString *debugMessage = [NSString stringWithFormat:@"[Gyroflow Toolbox] RENDERING A FRAME:\n"]; + NSString *debugMessage = [NSString stringWithFormat:@"[Gyroflow Toolbox Renderer] 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]; @@ -825,214 +1118,779 @@ - (BOOL)renderDestinationImage:(FxImageTile *)destinationImage NSLog(@"%@", debugMessage); */ - //NSLog(@"[Gyroflow Toolbox] inputTexture.debugDescription: %@", inputTexture.debugDescription); + //NSLog(@"[Gyroflow Toolbox Renderer] inputTexture.debugDescription: %@", inputTexture.debugDescription); //--------------------------------------------------------- - // Setup our output texture: + // Return the command queue back into the cache, + // so we can re-use it again: //--------------------------------------------------------- - id outputTexture = [destinationImage metalTextureForDevice:[deviceCache deviceWithRegistryID:destinationImage.deviceRegistryID]]; + [deviceCache returnCommandQueueToCache:commandQueue]; + + //--------------------------------------------------------- + // Release the Input Textures: + //--------------------------------------------------------- + //[inputTexture setPurgeableState:MTLPurgeableStateEmpty]; + return YES; +} + +//--------------------------------------------------------- +// +#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 { //--------------------------------------------------------- - // Setup our Color Attachment Descriptor. - // - // MTLRenderPassColorAttachmentDescriptor: A color render - // target that serves as the output destination for color - // pixels generated by a render pass. + // 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; + } + //--------------------------------------------------------- - MTLRenderPassColorAttachmentDescriptor* colorAttachmentDescriptor = [[MTLRenderPassColorAttachmentDescriptor alloc] init]; - colorAttachmentDescriptor.texture = outputTexture; + // Use the Action API to allow us to change the parameters: + //--------------------------------------------------------- + [actionAPI startAction:self]; //--------------------------------------------------------- - // 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. + // Load the Parameter Retrieval API: + //--------------------------------------------------------- + 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; + } + //--------------------------------------------------------- - colorAttachmentDescriptor.clearColor = MTLClearColorMake(1.0, 0.5, 0.0, 1.0); + // Get the existing Gyroflow project path: + //--------------------------------------------------------- + NSString *existingProjectPath = nil; + [paramGetAPI getStringParameterValue:&existingProjectPath fromParameter:kCB_GyroflowProjectPath]; + NSURL *existingProjectURL = [NSURL fileURLWithPath:existingProjectPath]; + //--------------------------------------------------------- - // 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. + // Open Gyroflow or the current Gyroflow Project: //--------------------------------------------------------- - colorAttachmentDescriptor.loadAction = MTLLoadActionClear; + 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]; + } //--------------------------------------------------------- - // Setup our Render Pass Descriptor. - // - // MTLRenderPassDescriptor: A group of render targets that - // hold the results of a render pass. + // Stop Action API: //--------------------------------------------------------- - MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; - renderPassDescriptor.colorAttachments[0] = colorAttachmentDescriptor; + [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!"); + 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 { //--------------------------------------------------------- - // Create a new Command Buffer: + // 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; + } + //--------------------------------------------------------- - id commandBuffer = [commandQueue commandBuffer]; - commandBuffer.label = @"GyroFlow Command Buffer"; - [commandBuffer enqueue]; + // Use the Action API to allow us to change the parameters: + //--------------------------------------------------------- + [actionAPI startAction:self]; //--------------------------------------------------------- - // Setup our Command Encoder. - // - // renderCommandEncoderWithDescriptor: Creates an object - // from a descriptor to encode a rendering pass into the - // command buffer. + // Load the Parameter Retrieval API: //--------------------------------------------------------- - id commandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; + 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; + } //--------------------------------------------------------- - // Calculate the vertex coordinates and the texture - // coordinates: + // Get the encoded bookmark string: //--------------------------------------------------------- - 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 } } - }; + NSString *encodedBookmark; + [paramGetAPI getStringParameterValue:&encodedBookmark fromParameter:kCB_GyroflowProjectBookmarkData]; //--------------------------------------------------------- - // Setup our viewport: - // - // MTLViewport: A 3D rectangular region for the viewport - // clipping. + // Make sure there's actually encoded bookmark data: //--------------------------------------------------------- - MTLViewport viewport = { 0, 0, outputWidth, outputHeight, -1.0, 1.0 }; + 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; + } //--------------------------------------------------------- - // Sets the viewport used for transformations and clipping. + // Decode the Base64 bookmark data: + //--------------------------------------------------------- + NSData *decodedBookmark = [[[NSData alloc] initWithBase64EncodedString:encodedBookmark + options:0] autorelease]; + + //--------------------------------------------------------- + // Resolve the decoded bookmark data into a + // security-scoped URL: //--------------------------------------------------------- - [commandEncoder setViewport:viewport]; + 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; + } //--------------------------------------------------------- - // Setup our Render Pipeline State. - // - // MTLRenderPipelineState: An object that contains graphics - // functions and configuration state to use in a render - // command. + // 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: //--------------------------------------------------------- - id pipelineState = [deviceCache pipelineStateWithRegistryID:sourceImages[0].deviceRegistryID - pixelFormat:pixelFormat]; + [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]; + //--------------------------------------------------------- - // Sets the current render pipeline state object: + // Stop Action API: //--------------------------------------------------------- - [commandEncoder setRenderPipelineState:pipelineState]; + [actionAPI endAction:self]; //--------------------------------------------------------- - // Sets a block of data for the vertex shader: + // Show success message: + //--------------------------------------------------------- + [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: //--------------------------------------------------------- - [commandEncoder setVertexBytes:vertices - length:sizeof(vertices) - atIndex:BVI_Vertices]; + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + alert.icon = [NSImage imageNamed:@"GyroflowToolbox"]; + 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); + + //--------------------------------------------------------- + // 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; + } + //--------------------------------------------------------- - // Set the viewport size: + // Setup an NSOpenPanel: //--------------------------------------------------------- - simd_uint2 viewportSize = { - (unsigned int)(outputWidth), - (unsigned int)(outputHeight) - }; + 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; + } + + NSString *selectedGyroflowProjectFile = [[url lastPathComponent] stringByDeletingPathExtension]; + NSString *selectedGyroflowProjectPath = [url path]; + NSString *selectedGyroflowProjectBookmarkData = [bookmark base64EncodedStringWithOptions:0]; + //--------------------------------------------------------- - // Sets a block of data for the vertex shader: + // Read the Gyroflow Project Data from File: //--------------------------------------------------------- - [commandEncoder setVertexBytes:&viewportSize - length:sizeof(viewportSize) - atIndex:BVI_ViewportSize]; + 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; + } //--------------------------------------------------------- - // Sets a texture for the fragment function at an index - // in the texture argument table: + // Read the JSON data: //--------------------------------------------------------- - [commandEncoder setFragmentTexture:inputTexture - atIndex:BTI_InputImage]; + 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; + } //--------------------------------------------------------- - // 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. + // Make sure there's Gyro Data in the Gyroflow Project: //--------------------------------------------------------- - [commandEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip - vertexStart:0 - vertexCount:4]; + /* + 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; + } + */ + /* + + TODO: + + after calling import_gyroflow_*, get this bool: let has_motion = { let gyro = stab.gyro.read(); !gyro.file_metadata.raw_imu.is_empty() || !gyro.file_metadata.quaternions.is_empty() };, that's the best method + + */ + //--------------------------------------------------------- - // 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. + // Get the current 'FOV' value: //--------------------------------------------------------- - [commandEncoder endEncoding]; + NSDictionary *stabilizationData = [jsonData objectForKey:@"stabilization"]; + NSNumber *fov = [stabilizationData objectForKey:@"fov"]; + + //--------------------------------------------------------- + // Get the current 'Smoothness' value: + //--------------------------------------------------------- + NSArray *smoothnessParams = [stabilizationData objectForKey:@"smoothing_params"]; + NSNumber *smoothness = nil; + for (NSDictionary *param in smoothnessParams) { + if ([param[@"name"] isEqualToString:@"smoothness"]) { + smoothness = param[@"value"]; + break; + } + } //--------------------------------------------------------- - // 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. + // Get the current 'Lens Correction' value: + //--------------------------------------------------------- + NSNumber *lensCorrection = [stabilizationData objectForKey:@"lens_correction_amount"]; + + //--------------------------------------------------------- + // Use the Action API to allow us to change the parameters: //--------------------------------------------------------- - [commandBuffer commit]; + [actionAPI startAction:self]; //--------------------------------------------------------- - // Blocks execution of the current thread until execution - // of the command buffer is completed. + // Load the Parameter Set API: //--------------------------------------------------------- - [commandBuffer waitUntilCompleted]; + 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': + //--------------------------------------------------------- + [paramSetAPI setStringParameterValue:selectedGyroflowProjectPath toParameter:kCB_GyroflowProjectPath]; //--------------------------------------------------------- - // Release the `colorAttachmentDescriptor` we created - // earlier: + // Update 'Gyroflow Project Bookmark Data': //--------------------------------------------------------- - [colorAttachmentDescriptor release]; + [paramSetAPI setStringParameterValue:selectedGyroflowProjectBookmarkData toParameter:kCB_GyroflowProjectBookmarkData]; //--------------------------------------------------------- - // Return the command queue back into the cache, - // so we can re-use it again: + // Update 'Gyroflow Project Data': //--------------------------------------------------------- - [deviceCache returnCommandQueueToCache:commandQueue]; + [paramSetAPI setStringParameterValue:selectedGyroflowProjectData toParameter:kCB_GyroflowProjectData]; + + //--------------------------------------------------------- + // Update 'Loaded Gyroflow Project' Text Box: + //--------------------------------------------------------- + [paramSetAPI setStringParameterValue:selectedGyroflowProjectFile toParameter:kCB_LoadedGyroflowProject]; //--------------------------------------------------------- - // Release the Input Textures: + // Set parameters from Gyroflow Project file: //--------------------------------------------------------- - [inputTexture setPurgeableState:MTLPurgeableStateEmpty]; + 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]; + } - return YES; + //--------------------------------------------------------- + // 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 parameters 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]; } @end diff --git a/Source/Gyroflow/Plugin/ImportGyroflowProjectView.h b/Source/Gyroflow/Plugin/ImportGyroflowProjectView.h deleted file mode 100644 index d80e77b2..00000000 --- a/Source/Gyroflow/Plugin/ImportGyroflowProjectView.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// 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 deleted file mode 100644 index f0986c13..00000000 --- a/Source/Gyroflow/Plugin/ImportGyroflowProjectView.m +++ /dev/null @@ -1,252 +0,0 @@ -// -// 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 deleted file mode 100644 index 4b5465e3..00000000 --- a/Source/Gyroflow/Plugin/LaunchGyroflowView.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// 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/MetalDeviceCache.h b/Source/Gyroflow/Plugin/MetalDeviceCache.h index 9bec7793..c306be20 100644 --- a/Source/Gyroflow/Plugin/MetalDeviceCache.h +++ b/Source/Gyroflow/Plugin/MetalDeviceCache.h @@ -1,6 +1,6 @@ // // MetalDeviceCache.h -// Gyroflow Toolbox +// Gyroflow Toolbox Renderer // // Created by Chris Hocking on 10/12/2022. // @@ -19,8 +19,6 @@ + (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 503cbcfc..e775fcc9 100644 --- a/Source/Gyroflow/Plugin/MetalDeviceCache.m +++ b/Source/Gyroflow/Plugin/MetalDeviceCache.m @@ -1,6 +1,6 @@ // // MetalDeviceCache.m -// Gyroflow Toolbox +// Gyroflow Toolbox Renderer // // Created by Chris Hocking on 10/12/2022. // @@ -19,7 +19,7 @@ #import "MetalDeviceCache.h" -const NSUInteger kMaxCommandQueues = 10; +const NSUInteger kMaxCommandQueues = 5; static NSString* kKey_InUse = @"InUse"; static NSString* kKey_CommandQueue = @"CommandQueue"; @@ -28,7 +28,6 @@ @interface MetalDeviceCacheItem : NSObject @property (readonly) id gpuDevice; -@property (readonly) id pipelineState; @property (retain) NSMutableArray* commandQueueCache; @property (readonly) NSLock* commandQueueCacheLock; @property (readonly) MTLPixelFormat pixelFormat; @@ -70,50 +69,15 @@ - (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) || - (_pipelineState == nil)) - { + if ((_gpuDevice == nil) || (_commandQueueCache == nil) || (_commandQueueCacheLock == nil)) { [self release]; self = nil; } @@ -127,7 +91,6 @@ - (void)dealloc [_gpuDevice release]; [_commandQueueCache release]; [_commandQueueCacheLock release]; - [_pipelineState release]; [super dealloc]; } @@ -274,47 +237,6 @@ - (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 deleted file mode 100644 index 8856e86f..00000000 --- a/Source/Gyroflow/Plugin/ReloadGyroflowProjectView.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// 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 deleted file mode 100644 index 2019ff31..00000000 --- a/Source/Gyroflow/Plugin/ReloadGyroflowProjectView.m +++ /dev/null @@ -1,228 +0,0 @@ -// -// 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 deleted file mode 100644 index 6b43e96b..00000000 --- a/Source/Gyroflow/Plugin/TileableRemoteGyroflow.metal +++ /dev/null @@ -1,116 +0,0 @@ -// -// 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 deleted file mode 100644 index 70396ed0..00000000 --- a/Source/Gyroflow/Plugin/TileableRemoteGyroflowShaderTypes.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// 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 a0613e21..49127e06 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 @@ -2,7 +2,7 @@ -5.6.3 +5.6.4 Channel @@ -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 @@ -158,17 +158,20 @@ 3 0 100 + 0 + 1 2 - + + + 36864 153600 1 0 - 10016 10013 @@ -204,7 +207,7 @@ 1 0 - 24576 + 16384 524304 @@ -223,6 +226,11 @@ 131087 + + + + + 4 @@ -251,18 +259,30 @@ - 8589934609 - + 8589934608 + 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** + + 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** @@ -274,7 +294,13 @@ - + + + + + + + @@ -313,6 +339,7 @@ 1200 1200 0.033333333333333333 + 0 1 0