From 1d3dab88d15b757d284544e37572322a1262b18f Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Mon, 17 Jun 2024 14:48:13 +0800 Subject: [PATCH 01/51] bump pubspec version --- thermion_flutter/thermion_flutter/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thermion_flutter/thermion_flutter/pubspec.yaml b/thermion_flutter/thermion_flutter/pubspec.yaml index cf1b22f2..e53a562d 100644 --- a/thermion_flutter/thermion_flutter/pubspec.yaml +++ b/thermion_flutter/thermion_flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: thermion_flutter description: Flutter plugin for 3D rendering with the Thermion toolkit. -version: 0.0.3 +version: 0.0.4-pre homepage: https://docs.page/nmfisher/thermion repository: https://github.com/nmfisher/thermion From 4f4be9b7d712fe4a7016b19c91fb79598a57ac07 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Tue, 18 Jun 2024 16:29:19 +0800 Subject: [PATCH 02/51] rename Flutter method channels --- .../main/kotlin/app/polyvox/filament/ThermionFlutterPlugin.kt | 2 +- .../ios/Classes/SwiftThermionFlutterPlugin.swift | 2 +- .../thermion_flutter/linux/thermion_flutter_plugin.cc | 2 +- .../macos/Classes/SwiftThermionFlutterPlugin.swift | 2 +- .../thermion_flutter/windows/thermion_flutter_plugin.cpp | 2 +- .../thermion_flutter_ffi/lib/thermion_flutter_ffi.dart | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/thermion_flutter/thermion_flutter/android/src/main/kotlin/app/polyvox/filament/ThermionFlutterPlugin.kt b/thermion_flutter/thermion_flutter/android/src/main/kotlin/app/polyvox/filament/ThermionFlutterPlugin.kt index 52a5c7cb..ec922948 100644 --- a/thermion_flutter/thermion_flutter/android/src/main/kotlin/app/polyvox/filament/ThermionFlutterPlugin.kt +++ b/thermion_flutter/thermion_flutter/android/src/main/kotlin/app/polyvox/filament/ThermionFlutterPlugin.kt @@ -55,7 +55,7 @@ class RenderCallbackImpl(plugin:ThermionFlutterPlugin) : RenderCallback { class ThermionFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware, LoadFilamentResourceFromOwner, FreeFilamentResourceFromOwner { companion object { - const val CHANNEL_NAME = "app.polyvox.filament/event" + const val CHANNEL_NAME = "dev.thermion.flutter/event" const val TAG = "FilamentPlugin" } diff --git a/thermion_flutter/thermion_flutter/ios/Classes/SwiftThermionFlutterPlugin.swift b/thermion_flutter/thermion_flutter/ios/Classes/SwiftThermionFlutterPlugin.swift index d950a950..473e76e8 100644 --- a/thermion_flutter/thermion_flutter/ios/Classes/SwiftThermionFlutterPlugin.swift +++ b/thermion_flutter/thermion_flutter/ios/Classes/SwiftThermionFlutterPlugin.swift @@ -127,7 +127,7 @@ public class SwiftThermionFlutterPlugin: NSObject, FlutterPlugin { public static func register(with registrar: FlutterPluginRegistrar) { let _messenger = registrar.messenger(); messenger = _messenger; - let channel = FlutterMethodChannel(name: "app.polyvox.filament/event", binaryMessenger: _messenger) + let channel = FlutterMethodChannel(name: "dev.thermion.flutter/event", binaryMessenger: _messenger) let instance = SwiftThermionFlutterPlugin(textureRegistry: registrar.textures(), registrar:registrar) registrar.addMethodCallDelegate(instance, channel: channel) } diff --git a/thermion_flutter/thermion_flutter/linux/thermion_flutter_plugin.cc b/thermion_flutter/thermion_flutter/linux/thermion_flutter_plugin.cc index aa786fee..ac0e64f3 100644 --- a/thermion_flutter/thermion_flutter/linux/thermion_flutter_plugin.cc +++ b/thermion_flutter/thermion_flutter/linux/thermion_flutter_plugin.cc @@ -871,7 +871,7 @@ void thermion_flutter_plugin_register_with_registrar(FlPluginRegistrar* registra g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); g_autoptr(FlMethodChannel) channel = fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar), - "app.polyvox.filament/event", + "dev.thermion.flutter/event", FL_METHOD_CODEC(codec)); fl_method_channel_set_method_call_handler(channel, method_call_cb, g_object_ref(plugin), diff --git a/thermion_flutter/thermion_flutter/macos/Classes/SwiftThermionFlutterPlugin.swift b/thermion_flutter/thermion_flutter/macos/Classes/SwiftThermionFlutterPlugin.swift index 55918f12..ca7ae110 100644 --- a/thermion_flutter/thermion_flutter/macos/Classes/SwiftThermionFlutterPlugin.swift +++ b/thermion_flutter/thermion_flutter/macos/Classes/SwiftThermionFlutterPlugin.swift @@ -65,7 +65,7 @@ public class SwiftThermionFlutterPlugin: NSObject, FlutterPlugin { public static func register(with registrar: FlutterPluginRegistrar) { let _messenger = registrar.messenger; messenger = _messenger; - let channel = FlutterMethodChannel(name: "app.polyvox.filament/event", binaryMessenger: _messenger) + let channel = FlutterMethodChannel(name: "dev.thermion.flutter/event", binaryMessenger: _messenger) let instance = SwiftThermionFlutterPlugin(textureRegistry: registrar.textures, registrar:registrar) registrar.addMethodCallDelegate(instance, channel: channel) } diff --git a/thermion_flutter/thermion_flutter/windows/thermion_flutter_plugin.cpp b/thermion_flutter/thermion_flutter/windows/thermion_flutter_plugin.cpp index 6e870225..b5c0f261 100644 --- a/thermion_flutter/thermion_flutter/windows/thermion_flutter_plugin.cpp +++ b/thermion_flutter/thermion_flutter/windows/thermion_flutter_plugin.cpp @@ -43,7 +43,7 @@ void ThermionFlutterPlugin::RegisterWithRegistrar( flutter::PluginRegistrarWindows *registrar) { auto channel = std::make_unique>( - registrar->messenger(), "app.polyvox.filament/event", + registrar->messenger(), "dev.thermion.flutter/event", &flutter::StandardMethodCodec::GetInstance()); auto plugin = std::make_unique( diff --git a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart b/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart index 415925a3..07786138 100644 --- a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart +++ b/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart @@ -10,7 +10,7 @@ import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dar /// to create rendering contexts, callbacks and surfaces (either backing texture(s). /// class ThermionFlutterFFI extends ThermionFlutterPlatform { - final _channel = const MethodChannel("app.polyvox.filament/event"); + final _channel = const MethodChannel("dev.thermion.flutter/event"); late final ThermionViewerFFI viewer; From 7ecf0978619f12e1f298775da3fd81db2f8cf23d Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Tue, 18 Jun 2024 16:29:53 +0800 Subject: [PATCH 03/51] reduce quickstart Dart SDK min version --- examples/flutter/quickstart/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/flutter/quickstart/pubspec.yaml b/examples/flutter/quickstart/pubspec.yaml index 73825cd9..0f35e5bb 100644 --- a/examples/flutter/quickstart/pubspec.yaml +++ b/examples/flutter/quickstart/pubspec.yaml @@ -19,7 +19,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: '>=3.5.0-250.0.dev <4.0.0' + sdk: '>=3.3.0 <4.0.0' # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions From ee8769b7d76def6097a97bd457ac7606d6497e23 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 12:50:29 +0800 Subject: [PATCH 04/51] use C header for ResourceBuffer in Swift bridging --- .../ios/Classes/ResourceBuffer.c | 7 +- .../ios/include/ResourceBuffer.h | 44 +++++++++ .../ios/include/ResourceBuffer.hpp | 91 ------------------- ...iftThermionFlutterPlugin-Bridging-Header.h | 4 +- .../macos/include/ResourceBuffer.h | 44 +++++++++ .../macos/include/ResourceBuffer.hpp | 91 ------------------- ...iftThermionFlutterPlugin-Bridging-Header.h | 5 +- .../macos/thermion_flutter.podspec | 4 +- 8 files changed, 101 insertions(+), 189 deletions(-) create mode 100644 thermion_flutter/thermion_flutter/ios/include/ResourceBuffer.h delete mode 100644 thermion_flutter/thermion_flutter/ios/include/ResourceBuffer.hpp create mode 100644 thermion_flutter/thermion_flutter/macos/include/ResourceBuffer.h delete mode 100644 thermion_flutter/thermion_flutter/macos/include/ResourceBuffer.hpp diff --git a/thermion_flutter/thermion_flutter/ios/Classes/ResourceBuffer.c b/thermion_flutter/thermion_flutter/ios/Classes/ResourceBuffer.c index caf1ca6f..293215ee 100644 --- a/thermion_flutter/thermion_flutter/ios/Classes/ResourceBuffer.c +++ b/thermion_flutter/thermion_flutter/ios/Classes/ResourceBuffer.c @@ -1,8 +1,11 @@ -#include "ResourceBuffer.hpp" +#include "ResourceBuffer.h" -ResourceLoaderWrapper *make_resource_loader(LoadFilamentResourceFromOwner loadFn, FreeFilamentResourceFromOwner freeFn, void *const owner) +void *make_resource_loader(LoadFilamentResourceFromOwner loadFn, FreeFilamentResourceFromOwner freeFn, void *const owner) { ResourceLoaderWrapper *rlw = (ResourceLoaderWrapper *)malloc(sizeof(ResourceLoaderWrapper)); + rlw->loadResource = NULL; + rlw->freeResource = NULL; + rlw->loadToOut = NULL; rlw->loadFromOwner = loadFn; rlw->freeFromOwner = freeFn; rlw->owner = owner; diff --git a/thermion_flutter/thermion_flutter/ios/include/ResourceBuffer.h b/thermion_flutter/thermion_flutter/ios/include/ResourceBuffer.h new file mode 100644 index 00000000..eab0ee0d --- /dev/null +++ b/thermion_flutter/thermion_flutter/ios/include/ResourceBuffer.h @@ -0,0 +1,44 @@ +#ifndef RESOURCE_BUFFER_H +#define RESOURCE_BUFFER_H + +#include +#include + +// +// A ResourceBuffer is a unified interface for working with +// binary assets across various platforms. +// This is simply: +// 1) a pointer to some data +// 2) the length of the data +// 3) an ID that can be passed back to the native platform to release the underlying asset when needed. +// +typedef struct ResourceBuffer +{ + const void *const data; + const int32_t size; + const int32_t id; + +#if defined(__cplusplus) + ResourceBuffer(void *const data, int32_t size, int32_t id) : data(data), size(size), id(id) {} +#endif +} ResourceBuffer; + +typedef void (*LoadFilamentResourceIntoOutPointer)(const char *uri, ResourceBuffer *out); +typedef ResourceBuffer (*LoadFilamentResource)(const char *uri); +typedef ResourceBuffer (*LoadFilamentResourceFromOwner)(const char *const, void *const owner); +typedef void (*FreeFilamentResource)(ResourceBuffer); +typedef void (*FreeFilamentResourceFromOwner)(ResourceBuffer, void *const owner); + +typedef struct ResourceLoaderWrapper +{ + LoadFilamentResource loadResource; + FreeFilamentResource freeResource; + LoadFilamentResourceFromOwner loadFromOwner; + FreeFilamentResourceFromOwner freeFromOwner; + void *owner; + LoadFilamentResourceIntoOutPointer loadToOut; +} ResourceLoaderWrapper; + +void *make_resource_loader(LoadFilamentResourceFromOwner loadFn, FreeFilamentResourceFromOwner freeFn, void *const owner); + +#endif diff --git a/thermion_flutter/thermion_flutter/ios/include/ResourceBuffer.hpp b/thermion_flutter/thermion_flutter/ios/include/ResourceBuffer.hpp deleted file mode 100644 index 97822546..00000000 --- a/thermion_flutter/thermion_flutter/ios/include/ResourceBuffer.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef RESOURCE_BUFFER_H -#define RESOURCE_BUFFER_H - -#include -#include - -// -// A ResourceBuffer is a unified interface for working with -// binary assets across various platforms. -// This is simply: -// 1) a pointer to some data -// 2) the length of the data -// 3) an ID that can be passed back to the native platform to release the underlying asset when needed. -// -struct ResourceBuffer -{ - const void *const data; - const int32_t size; - const int32_t id; -}; - -typedef struct ResourceBuffer ResourceBuffer; -typedef ResourceBuffer (*LoadFilamentResource)(const char *uri); -typedef ResourceBuffer (*LoadFilamentResourceFromOwner)(const char *const, void *const owner); -typedef void (*FreeFilamentResource)(ResourceBuffer); -typedef void (*FreeFilamentResourceFromOwner)(ResourceBuffer, void *const owner); - -struct ResourceLoaderWrapper -{ - LoadFilamentResource loadResource; - FreeFilamentResource freeResource; - LoadFilamentResourceFromOwner loadFromOwner; - FreeFilamentResourceFromOwner freeFromOwner; - void *owner; -}; -typedef struct ResourceLoaderWrapper ResourceLoaderWrapper; - - -#if defined(__cplusplus) - -namespace thermion_filament { - -struct ResourceLoaderWrapperImpl : public ResourceLoaderWrapper -{ - - ResourceLoaderWrapperImpl(LoadFilamentResource loader, FreeFilamentResource freeResource) - { - loadFromOwner = nullptr; - freeFromOwner = nullptr; - loadResource = loader; - freeResource = freeResource; - owner = nullptr; - } - - ResourceLoaderWrapperImpl(LoadFilamentResourceFromOwner loader, FreeFilamentResourceFromOwner freeResource, void * owner) - { - loadResource = nullptr; - freeResource = nullptr; - loadFromOwner = loader; - freeFromOwner = freeResource; - owner = owner; - } - - ResourceBuffer load(const char *uri) const - { - if (loadFromOwner) - { - auto rb = loadFromOwner(uri, owner); - return rb; - } - auto rb = loadResource(uri); - return rb; - } - - void free(ResourceBuffer rb) const - { - if (freeFromOwner) - { - freeFromOwner(rb, owner); - } - else - { - freeResource(rb); - } - } -}; - -} -#endif - -#endif diff --git a/thermion_flutter/thermion_flutter/ios/include/SwiftThermionFlutterPlugin-Bridging-Header.h b/thermion_flutter/thermion_flutter/ios/include/SwiftThermionFlutterPlugin-Bridging-Header.h index 96ec1fc8..2c229776 100644 --- a/thermion_flutter/thermion_flutter/ios/include/SwiftThermionFlutterPlugin-Bridging-Header.h +++ b/thermion_flutter/thermion_flutter/ios/include/SwiftThermionFlutterPlugin-Bridging-Header.h @@ -3,9 +3,9 @@ #include -#include "ResourceBuffer.hpp" +#include "ResourceBuffer.h" -ResourceLoaderWrapper *make_resource_loader(LoadFilamentResourceFromOwner loadFn, FreeFilamentResourceFromOwner freeFn, void *const owner); +void *make_resource_loader(LoadFilamentResourceFromOwner loadFn, FreeFilamentResourceFromOwner freeFn, void *const owner); // ResourceLoaderWrapper *make_resource_loader(LoadFilamentResourceFromOwner loadFn, FreeFilamentResourceFromOwner freeFn, void *const owner) // { diff --git a/thermion_flutter/thermion_flutter/macos/include/ResourceBuffer.h b/thermion_flutter/thermion_flutter/macos/include/ResourceBuffer.h new file mode 100644 index 00000000..0e159c30 --- /dev/null +++ b/thermion_flutter/thermion_flutter/macos/include/ResourceBuffer.h @@ -0,0 +1,44 @@ +#ifndef RESOURCE_BUFFER_H +#define RESOURCE_BUFFER_H + +#include +#include + +// +// A ResourceBuffer is a unified interface for working with +// binary assets across various platforms. +// This is simply: +// 1) a pointer to some data +// 2) the length of the data +// 3) an ID that can be passed back to the native platform to release the underlying asset when needed. +// +typedef struct ResourceBuffer +{ + const void *const data; + const int32_t size; + const int32_t id; + +#if defined(__cplusplus) + ResourceBuffer(void *const data, int32_t size, int32_t id) : data(data), size(size), id(id) {} +#endif +} ResourceBuffer; + +typedef void (*LoadFilamentResourceIntoOutPointer)(const char *uri, ResourceBuffer *out); +typedef ResourceBuffer (*LoadFilamentResource)(const char *uri); +typedef ResourceBuffer (*LoadFilamentResourceFromOwner)(const char *const, void *const owner); +typedef void (*FreeFilamentResource)(ResourceBuffer); +typedef void (*FreeFilamentResourceFromOwner)(ResourceBuffer, void *const owner); + +typedef struct ResourceLoaderWrapper +{ + LoadFilamentResource loadResource; + FreeFilamentResource freeResource; + LoadFilamentResourceFromOwner loadFromOwner; + FreeFilamentResourceFromOwner freeFromOwner; + void *owner; + LoadFilamentResourceIntoOutPointer loadToOut; +} ResourceLoaderWrapper; + +ResourceLoaderWrapper *make_resource_loader(LoadFilamentResourceFromOwner loadFn, FreeFilamentResourceFromOwner freeFn, void *const owner); + +#endif diff --git a/thermion_flutter/thermion_flutter/macos/include/ResourceBuffer.hpp b/thermion_flutter/thermion_flutter/macos/include/ResourceBuffer.hpp deleted file mode 100644 index 97822546..00000000 --- a/thermion_flutter/thermion_flutter/macos/include/ResourceBuffer.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef RESOURCE_BUFFER_H -#define RESOURCE_BUFFER_H - -#include -#include - -// -// A ResourceBuffer is a unified interface for working with -// binary assets across various platforms. -// This is simply: -// 1) a pointer to some data -// 2) the length of the data -// 3) an ID that can be passed back to the native platform to release the underlying asset when needed. -// -struct ResourceBuffer -{ - const void *const data; - const int32_t size; - const int32_t id; -}; - -typedef struct ResourceBuffer ResourceBuffer; -typedef ResourceBuffer (*LoadFilamentResource)(const char *uri); -typedef ResourceBuffer (*LoadFilamentResourceFromOwner)(const char *const, void *const owner); -typedef void (*FreeFilamentResource)(ResourceBuffer); -typedef void (*FreeFilamentResourceFromOwner)(ResourceBuffer, void *const owner); - -struct ResourceLoaderWrapper -{ - LoadFilamentResource loadResource; - FreeFilamentResource freeResource; - LoadFilamentResourceFromOwner loadFromOwner; - FreeFilamentResourceFromOwner freeFromOwner; - void *owner; -}; -typedef struct ResourceLoaderWrapper ResourceLoaderWrapper; - - -#if defined(__cplusplus) - -namespace thermion_filament { - -struct ResourceLoaderWrapperImpl : public ResourceLoaderWrapper -{ - - ResourceLoaderWrapperImpl(LoadFilamentResource loader, FreeFilamentResource freeResource) - { - loadFromOwner = nullptr; - freeFromOwner = nullptr; - loadResource = loader; - freeResource = freeResource; - owner = nullptr; - } - - ResourceLoaderWrapperImpl(LoadFilamentResourceFromOwner loader, FreeFilamentResourceFromOwner freeResource, void * owner) - { - loadResource = nullptr; - freeResource = nullptr; - loadFromOwner = loader; - freeFromOwner = freeResource; - owner = owner; - } - - ResourceBuffer load(const char *uri) const - { - if (loadFromOwner) - { - auto rb = loadFromOwner(uri, owner); - return rb; - } - auto rb = loadResource(uri); - return rb; - } - - void free(ResourceBuffer rb) const - { - if (freeFromOwner) - { - freeFromOwner(rb, owner); - } - else - { - freeResource(rb); - } - } -}; - -} -#endif - -#endif diff --git a/thermion_flutter/thermion_flutter/macos/include/SwiftThermionFlutterPlugin-Bridging-Header.h b/thermion_flutter/thermion_flutter/macos/include/SwiftThermionFlutterPlugin-Bridging-Header.h index 0a005191..3e8b248f 100644 --- a/thermion_flutter/thermion_flutter/macos/include/SwiftThermionFlutterPlugin-Bridging-Header.h +++ b/thermion_flutter/thermion_flutter/macos/include/SwiftThermionFlutterPlugin-Bridging-Header.h @@ -3,11 +3,14 @@ #include -#include "ResourceBuffer.hpp" +#include "ResourceBuffer.h" ResourceLoaderWrapper *make_resource_loader(LoadFilamentResourceFromOwner loadFn, FreeFilamentResourceFromOwner freeFn, void *const owner) { ResourceLoaderWrapper *rlw = (ResourceLoaderWrapper *)malloc(sizeof(ResourceLoaderWrapper)); + rlw->loadResource = NULL; + rlw->freeResource = NULL; + rlw->loadToOut = NULL; rlw->loadFromOwner = loadFn; rlw->freeFromOwner = freeFn; rlw->owner = owner; diff --git a/thermion_flutter/thermion_flutter/macos/thermion_flutter.podspec b/thermion_flutter/thermion_flutter/macos/thermion_flutter.podspec index 632360f6..b11c286d 100644 --- a/thermion_flutter/thermion_flutter/macos/thermion_flutter.podspec +++ b/thermion_flutter/thermion_flutter/macos/thermion_flutter.podspec @@ -13,8 +13,8 @@ A new Flutter plugin project. s.license = { :file => '../LICENSE' } s.author = { 'Your Company' => 'email@example.com' } s.source = { :path => '.' } - s.source_files = 'Classes/*', 'include/ResourceBuffer.hpp','include/SwiftThermionFlutterPlugin-Bridging-Header.h' - s.public_header_files = 'include/SwiftThermionFlutterPlugin-Bridging-Header.h', 'include/ResourceBuffer.hpp' + s.source_files = 'Classes/*', 'include/ResourceBuffer.h','include/SwiftThermionFlutterPlugin-Bridging-Header.h' + s.public_header_files = 'include/SwiftThermionFlutterPlugin-Bridging-Header.h', 'include/ResourceBuffer.h' s.dependency 'FlutterMacOS' s.platform = :osx, '13' From 8e892a1f23471c52b709539484b21d42f0ff9bc3 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 12:51:39 +0800 Subject: [PATCH 05/51] log to file for build.dart, check for SUCCESS token --- thermion_dart/hook/build.dart | 173 +++++++++++++++++++--------------- 1 file changed, 97 insertions(+), 76 deletions(-) diff --git a/thermion_dart/hook/build.dart b/thermion_dart/hook/build.dart index 70615995..6f7ce0ca 100644 --- a/thermion_dart/hook/build.dart +++ b/thermion_dart/hook/build.dart @@ -6,9 +6,19 @@ import 'package:native_toolchain_c/native_toolchain_c.dart'; void main(List args) async { await build(args, (config, output) async { + var logDir = Directory( + "${config.packageRoot.toFilePath()}/.dart_tool/thermion_dart/log/"); + if (!logDir.existsSync()) { + logDir.createSync(); + } + var logFile = File(logDir.path + "/build.log"); - final logger = Logger("")..level = Level.ALL - ..onRecord.listen((record) => print(record.message)); + final logger = Logger("") + ..level = Level.ALL + ..onRecord.listen((record) => logFile.writeAsStringSync( + record.message + "\n", + mode: FileMode.append, + flush: true)); var platform = config.targetOS.toString().toLowerCase(); @@ -71,7 +81,7 @@ void main(List args) async { } else { libs.add("stdc++"); } - final flags = []; + final flags = []; //"-fsanitize=address"]; final defines = {}; var frameworks = []; @@ -85,7 +95,15 @@ void main(List args) async { } if (platform == "ios") { - frameworks.addAll(['Foundation', 'CoreGraphics', 'QuartzCore', 'GLKit', "Metal", 'CoreVideo', 'OpenGLES']); + frameworks.addAll([ + 'Foundation', + 'CoreGraphics', + 'QuartzCore', + 'GLKit', + "Metal", + 'CoreVideo', + 'OpenGLES' + ]); } else if (platform == "macos") { frameworks.addAll([ 'Foundation', @@ -107,7 +125,7 @@ void main(List args) async { sources: sources, includes: ['native/include', 'native/include/filament'], defines: defines, - // UNCOMMENT THIS IF YOU ARE BUILDING WITH THE CUSTOM native_toolchain_c FORK FOR WINDOWS + // UNCOMMENT THIS IF YOU ARE BUILDING WITH THE CUSTOM native_toolchain_c FORK FOR WINDOWS // linkWith: linkWith, flags: [ if (platform == "macos") '-mmacosx-version-min=13.0', @@ -162,87 +180,90 @@ void main(List args) async { }); } -String _FILAMENT_VERSION ="v1.51.2"; +String _FILAMENT_VERSION = "v1.51.2"; String _getLibraryUrl(String platform, String mode) { - return "https://pub-c8b6266320924116aaddce03b5313c0a.r2.dev/filament-${_FILAMENT_VERSION}-${platform}-${mode}.zip"; + return "https://pub-c8b6266320924116aaddce03b5313c0a.r2.dev/filament-${_FILAMENT_VERSION}-${platform}-${mode}.zip"; } // // Download precompiled Filament libraries for the target platform from Cloudflare. // Future getLibDir(BuildConfig config, Logger logger) async { + var platform = config.targetOS.toString().toLowerCase(); - var platform = config.targetOS.toString().toLowerCase(); - - // Except on Windows, most users will only need release builds of Filament. - // Debug builds are probably only relevant if you're a package developer debugging an internal Filament issue - // or if you're working on Flutter+Windows (which requires the CRT debug DLLs). - // Also note that there are known driver issues with Android debug builds, e.g.: - // https://github.com/google/filament/issues/7162 - // (these aren't present in Filament release builds). - // However, if you know what you're doing, you can change "release" to "debug" below. - // TODO - check if we can pass this as a CLI compiler flag - var mode = "release"; - if(platform == "windows") { - mode = config.buildMode == BuildMode.debug ? "debug" : "release"; - } + // Except on Windows, most users will only need release builds of Filament. + // Debug builds are probably only relevant if you're a package developer debugging an internal Filament issue + // or if you're working on Flutter+Windows (which requires the CRT debug DLLs). + // Also note that there are known driver issues with Android debug builds, e.g.: + // https://github.com/google/filament/issues/7162 + // (these aren't present in Filament release builds). + // However, if you know what you're doing, you can change "release" to "debug" below. + // TODO - check if we can pass this as a CLI compiler flag + var mode = "release"; + if (platform == "windows") { + mode = config.buildMode == BuildMode.debug ? "debug" : "release"; + } - var libDir = Directory("${config.packageRoot.toFilePath()}/.dart_tool/thermion_dart/lib/${_FILAMENT_VERSION}/$platform/$mode/"); + var libDir = Directory( + "${config.packageRoot.toFilePath()}/.dart_tool/thermion_dart/lib/${_FILAMENT_VERSION}/$platform/$mode/"); - if (platform == "android") { - final archExtension = switch (config.targetArchitecture) { - Architecture.arm => "armeabi-v7a", - Architecture.arm64 => "arm64-v8a", - Architecture.x64 => "x86_64", - Architecture.ia32 => "x86", - _ => throw FormatException('Invalid') - }; - libDir = Directory("${libDir.path}/$archExtension/"); - } else if(platform == "windows") { - if(config.targetArchitecture != Architecture.x64) { - throw Exception("Unsupported architecture : ${config.targetArchitecture}"); + if (platform == "android") { + final archExtension = switch (config.targetArchitecture) { + Architecture.arm => "armeabi-v7a", + Architecture.arm64 => "arm64-v8a", + Architecture.x64 => "x86_64", + Architecture.ia32 => "x86", + _ => throw FormatException('Invalid') + }; + libDir = Directory("${libDir.path}/$archExtension/"); + } else if (platform == "windows") { + if (config.targetArchitecture != Architecture.x64) { + throw Exception( + "Unsupported architecture : ${config.targetArchitecture}"); + } + } + + final url = _getLibraryUrl(platform, mode); + + final filename = url.split("/").last; + + // We will write an empty file called success to the unzip directory after successfully downloading/extracting the prebuilt libraries. + // If this file already exists, we assume everything has been successfully extracted and skip + final unzipDir = platform == "android" ? libDir.parent.path : libDir.path; + final successToken = File("$unzipDir/success"); + final libraryZip = File("$unzipDir/$filename"); + + if (!successToken.existsSync()) { + if (libraryZip.existsSync()) { + libraryZip.deleteSync(); + } + + if (!libraryZip.parent.existsSync()) { + libraryZip.parent.createSync(recursive: true); + } + + logger.info( + "Downloading prebuilt libraries for $platform/$mode from $url to ${libraryZip}, files will be unzipped to ${unzipDir}"); + final request = await HttpClient().getUrl(Uri.parse(url)); + final response = await request.close(); + + await response.pipe(libraryZip.openWrite()); + + final archive = ZipDecoder().decodeBytes(await libraryZip.readAsBytes()); + + for (final file in archive) { + final filename = file.name; + if (file.isFile) { + final data = file.content as List; + final f = File('${unzipDir}/$filename'); + await f.create(recursive: true); + await f.writeAsBytes(data); + } else { + final d = Directory('${unzipDir}/$filename'); + await d.create(recursive: true); } } - - final url = _getLibraryUrl(platform, mode); - - final filename = url.split("/").last; - - // Assume that the libraries exist if the directory containing them exists. - if (!libDir.existsSync()) { - - final unzipDir = platform == "android" ? libDir.parent.path : libDir.path; - - final libraryZip = File("$unzipDir/$filename"); - - if(libraryZip.existsSync()) { - libraryZip.deleteSync(); - } - - if(!libraryZip.parent.existsSync()) { - libraryZip.parent.createSync(recursive: true); - } - - logger.info("Downloading prebuilt libraries for $platform/$mode from $url to ${libraryZip}, files will be unzipped to ${unzipDir}"); - final request = await HttpClient().getUrl(Uri.parse(url)); - final response = await request.close(); - - await response.pipe(libraryZip.openWrite()); - - final archive = ZipDecoder().decodeBytes(await libraryZip.readAsBytes()); - - for (final file in archive) { - final filename = file.name; - if (file.isFile) { - final data = file.content as List; - final f = File('${unzipDir}/$filename'); - await f.create(recursive: true); - await f.writeAsBytes(data); - } else { - final d = Directory('${unzipDir}/$filename'); - await d.create(recursive: true); - } - } - } - return libDir; + successToken.writeAsStringSync("SUCCESS"); + } + return libDir; } From c8fa22036264e55b441b9f2228c355a5ded93b7e Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 12:52:39 +0800 Subject: [PATCH 06/51] remove dispose()/viewer getter from platform interface, rename initialize to createViewer --- .../lib/thermion_flutter_platform_interface.dart | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/thermion_flutter/thermion_flutter_platform_interface/lib/thermion_flutter_platform_interface.dart b/thermion_flutter/thermion_flutter_platform_interface/lib/thermion_flutter_platform_interface.dart index b7425e64..1ba13a90 100644 --- a/thermion_flutter/thermion_flutter_platform_interface/lib/thermion_flutter_platform_interface.dart +++ b/thermion_flutter/thermion_flutter_platform_interface/lib/thermion_flutter_platform_interface.dart @@ -9,8 +9,7 @@ abstract class ThermionFlutterPlatform extends PlatformInterface { static final Object _token = Object(); - static late ThermionFlutterPlatform _instance; - + static late final ThermionFlutterPlatform _instance; static ThermionFlutterPlatform get instance => _instance; static set instance(ThermionFlutterPlatform instance) { @@ -18,9 +17,7 @@ abstract class ThermionFlutterPlatform extends PlatformInterface { _instance = instance; } - ThermionViewer get viewer; - - Future initialize({String? uberArchivePath}); + Future createViewer({String? uberArchivePath}); Future createTexture( int width, int height, int offsetLeft, int offsetRight); @@ -30,5 +27,4 @@ abstract class ThermionFlutterPlatform extends PlatformInterface { Future resizeTexture(ThermionFlutterTexture texture, int width, int height, int offsetLeft, int offsetRight); - void dispose(); } From 8e7555554051e437060772806eb9e4906fba4fa9 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 12:52:58 +0800 Subject: [PATCH 07/51] use C header for ResourceBuffer in Swift bridging --- .../thermion_flutter/ios/thermion_flutter.podspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/thermion_flutter/thermion_flutter/ios/thermion_flutter.podspec b/thermion_flutter/thermion_flutter/ios/thermion_flutter.podspec index 549abac6..47a52e93 100644 --- a/thermion_flutter/thermion_flutter/ios/thermion_flutter.podspec +++ b/thermion_flutter/thermion_flutter/ios/thermion_flutter.podspec @@ -13,8 +13,8 @@ A new flutter plugin project. s.license = { :file => '../LICENSE' } s.author = { 'Your Company' => 'email@example.com' } s.source = { :path => '.' } - s.source_files = 'Classes/*', 'include/SwiftThermionFlutterPlugin-Bridging-Header.h','include/ResourceBuffer.hpp', 'src/ResourceBuffer.c' - s.public_header_files = 'include/SwiftThermionFlutterPlugin-Bridging-Header.h', 'include/ResourceBuffer.hpp' + s.source_files = 'Classes/*', 'include/SwiftThermionFlutterPlugin-Bridging-Header.h','include/ResourceBuffer.h', 'src/ResourceBuffer.c' + s.public_header_files = 'include/SwiftThermionFlutterPlugin-Bridging-Header.h', 'include/ResourceBuffer.h' s.dependency 'Flutter' s.platform = :ios, '13.0' s.static_framework = true From 31e68df1c5a010d2cd23db4ca086f888f4805335 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 12:57:16 +0800 Subject: [PATCH 08/51] make ThermionFlutterPlugin static, remove dispose() and add internal listener for ThermionViewer.onDispose --- .../lib/thermion/thermion_flutter_plugin.dart | 91 +++++++++---------- .../lib/thermion_flutter_ffi.dart | 88 +++++++++--------- 2 files changed, 90 insertions(+), 89 deletions(-) diff --git a/thermion_flutter/thermion_flutter/lib/thermion/thermion_flutter_plugin.dart b/thermion_flutter/thermion_flutter/lib/thermion/thermion_flutter_plugin.dart index 9ff1e327..3c784cc9 100644 --- a/thermion_flutter/thermion_flutter/lib/thermion/thermion_flutter_plugin.dart +++ b/thermion_flutter/thermion_flutter/lib/thermion/thermion_flutter_plugin.dart @@ -1,103 +1,100 @@ import 'dart:async'; -import 'dart:ui'; import 'package:thermion_dart/thermion_dart.dart'; import 'package:flutter/widgets.dart'; import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart'; import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart'; /// -/// Handles all platform-specific initialization work necessary to create a -/// backing rendering surface in a Flutter application. -/// Instantiates/wraps a [ThermionViewer], +/// Handles all platform-specific initialization to create a backing rendering +/// surface in a Flutter application and lifecycle listeners to pause rendering +/// when the app is inactive or in the background. +/// Call [createViewer] to create an instance of [ThermionViewer]. +/// This is a lightweight singleton that /// class ThermionFlutterPlugin { - ThermionViewer get _viewer => ThermionFlutterPlatform.instance.viewer; + + ThermionFlutterPlugin._(); - bool _wasRenderingOnInactive = false; + static AppLifecycleListener? _appLifecycleListener; - void _handleStateChange(AppLifecycleState state) async { - await initialized; + static bool _initializing = false; + + static ThermionViewer? _viewer; + + static bool _wasRenderingOnInactive = false; + + static void _handleStateChange(AppLifecycleState state) async { + if (_viewer == null) { + return; + } + + await _viewer!.initialized; switch (state) { case AppLifecycleState.detached: - print("Detached"); if (!_wasRenderingOnInactive) { - _wasRenderingOnInactive = _viewer.rendering; + _wasRenderingOnInactive = _viewer!.rendering; } - await _viewer.setRendering(false); + await _viewer!.setRendering(false); break; case AppLifecycleState.hidden: - print("Hidden"); if (!_wasRenderingOnInactive) { - _wasRenderingOnInactive = _viewer.rendering; + _wasRenderingOnInactive = _viewer!.rendering; } - await _viewer.setRendering(false); + await _viewer!.setRendering(false); break; case AppLifecycleState.inactive: - print("Inactive"); if (!_wasRenderingOnInactive) { - _wasRenderingOnInactive = _viewer.rendering; + _wasRenderingOnInactive = _viewer!.rendering; } // on Windows in particular, restoring a window after minimizing stalls the renderer (and the whole application) for a considerable length of time. // disabling rendering on minimize seems to fix the issue (so I wonder if there's some kind of command buffer that's filling up while the window is minimized). - await _viewer.setRendering(false); + await _viewer!.setRendering(false); break; case AppLifecycleState.paused: - print("Paused"); if (!_wasRenderingOnInactive) { - _wasRenderingOnInactive = _viewer.rendering; + _wasRenderingOnInactive = _viewer!.rendering; } - await _viewer.setRendering(false); + await _viewer!.setRendering(false); break; case AppLifecycleState.resumed: - print("Resumed"); - await _viewer.setRendering(_wasRenderingOnInactive); + await _viewer!.setRendering(_wasRenderingOnInactive); break; } } - AppLifecycleListener? _appLifecycleListener; - - final _initialized = Completer(); - Future get initialized => _initialized.future; - - bool _initializing = false; - - Future initialize({String? uberArchivePath}) async { - _initializing = true; - if (_initialized.isCompleted) { - return ThermionFlutterPlatform.instance.viewer; + static Future createViewer({String? uberArchivePath}) async { + if (_initializing) { + throw Exception("Existing call to createViewer has not completed."); } - await ThermionFlutterPlatform.instance - .initialize(uberArchivePath: uberArchivePath); - + _initializing = true; + _viewer = await ThermionFlutterPlatform.instance + .createViewer(uberArchivePath: uberArchivePath); _appLifecycleListener = AppLifecycleListener( onStateChange: _handleStateChange, ); - _viewer.initialized; - _initialized.complete(true); + _viewer!.onDispose(() async { + _viewer = null; + _appLifecycleListener?.dispose(); + _appLifecycleListener = null; + }); _initializing = false; - return ThermionFlutterPlatform.instance.viewer; + return _viewer!; } - Future createTexture( + static Future createTexture( int width, int height, int offsetLeft, int offsetRight) async { return ThermionFlutterPlatform.instance .createTexture(width, height, offsetLeft, offsetRight); } - Future destroyTexture(ThermionFlutterTexture texture) async { + static Future destroyTexture(ThermionFlutterTexture texture) async { return ThermionFlutterPlatform.instance.destroyTexture(texture); } @override - Future resizeTexture(ThermionFlutterTexture texture, + static Future resizeTexture(ThermionFlutterTexture texture, int width, int height, int offsetLeft, int offsetRight) async { return ThermionFlutterPlatform.instance .resizeTexture(texture, width, height, offsetLeft, offsetRight); } - - void dispose() { - ThermionFlutterPlatform.instance.dispose(); - _appLifecycleListener?.dispose(); - } } diff --git a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart b/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart index 07786138..77cdcea8 100644 --- a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart +++ b/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart @@ -6,21 +6,24 @@ import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_in import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart'; /// -/// A subclass of [ThermionViewerFFI] that uses Flutter platform channels -/// to create rendering contexts, callbacks and surfaces (either backing texture(s). +/// An implementation of [ThermionFlutterPlatform] that uses a Flutter platform +/// channel to create a rendering context, resource loaders, and +/// render target(s). /// class ThermionFlutterFFI extends ThermionFlutterPlatform { final _channel = const MethodChannel("dev.thermion.flutter/event"); - late final ThermionViewerFFI viewer; + ThermionViewerFFI? _viewer; + + ThermionFlutterFFI._() {} static void registerWith() { - ThermionFlutterPlatform.instance = ThermionFlutterFFI(); + ThermionFlutterPlatform.instance = ThermionFlutterFFI._(); } final _textures = {}; - Future initialize({String? uberArchivePath}) async { + Future createViewer({String? uberArchivePath}) async { var resourceLoader = Pointer.fromAddress( await _channel.invokeMethod("getResourceLoaderWrapper")); @@ -46,22 +49,24 @@ class ThermionFlutterFFI extends ThermionFlutterPlatform { ? nullptr : Pointer.fromAddress(sharedContext); - viewer = ThermionViewerFFI( + _viewer = ThermionViewerFFI( resourceLoader: resourceLoader, renderCallback: renderCallback, renderCallbackOwner: renderCallbackOwner, driver: driverPtr, sharedContext: sharedContextPtr, uberArchivePath: uberArchivePath); - await viewer.initialized; + await _viewer!.initialized; + return _viewer!; } bool _creatingTexture = false; + bool _destroyingTexture = false; Future _waitForTextureCreationToComplete() async { var iter = 0; - while (_creatingTexture) { + while (_creatingTexture || _destroyingTexture) { await Future.delayed(Duration(milliseconds: 50)); iter++; if (iter > 10) { @@ -125,39 +130,43 @@ class ThermionFlutterFFI extends ThermionFlutterPlatform { print( "Created texture with flutter texture id ${flutterTextureId}, hardwareTextureId $hardwareTextureId and surfaceAddress $surfaceAddress"); - viewer.viewportDimensions = (width.toDouble(), height.toDouble()); + _viewer?.viewportDimensions = (width.toDouble(), height.toDouble()); final texture = ThermionFlutterTexture( - flutterTextureId, hardwareTextureId, width, height, surfaceAddress); + flutterTextureId, hardwareTextureId, width, height, surfaceAddress); - await viewer.createSwapChain(width.toDouble(), height.toDouble(), - surface: texture.surfaceAddress == null - ? nullptr - : Pointer.fromAddress(texture.surfaceAddress!)); + await _viewer?.createSwapChain(width.toDouble(), height.toDouble(), + surface: texture.surfaceAddress == null + ? nullptr + : Pointer.fromAddress(texture.surfaceAddress!)); if (texture.hardwareTextureId != null) { - print("Creating render target"); // ignore: unused_local_variable - var renderTarget = await viewer.createRenderTarget( - width.toDouble(), height.toDouble(), texture.hardwareTextureId!); + var renderTarget = await _viewer?.createRenderTarget( + width.toDouble(), height.toDouble(), texture.hardwareTextureId!); } - - await viewer.updateViewportAndCameraProjection( - width.toDouble(), height.toDouble()); - viewer.render(); + + await _viewer?.updateViewportAndCameraProjection(width.toDouble(), height.toDouble()); + _viewer?.render(); _creatingTexture = false; - + _textures.add(texture); - + return texture; } + /// - /// Called by [ThermionWidget] to destroy a texture. Don't call this yourself. + /// Destroy a texture and clean up the texture cache (if applicable). /// Future destroyTexture(ThermionFlutterTexture texture) async { - await _channel.invokeMethod("destroyTexture", texture.flutterTextureId); + if (_creatingTexture || _destroyingTexture) { + throw Exception("Cannot destroy texture while concurrent call to createTexture/destroyTexture has not completed"); + } + _destroyingTexture = true; _textures.remove(texture); + await _channel.invokeMethod("destroyTexture", texture.flutterTextureId); + _destroyingTexture = false; } bool _resizing = false; @@ -172,14 +181,14 @@ class ThermionFlutterFFI extends ThermionFlutterPlatform { throw Exception("Resize underway"); } - if ((width - viewer.viewportDimensions.$1).abs() < 0.001 || - (height - viewer.viewportDimensions.$2).abs() < 0.001) { + if ((width - _viewer!.viewportDimensions.$1).abs() < 0.001 || + (height - _viewer!.viewportDimensions.$2).abs() < 0.001) { return texture; } _resizing = true; - bool wasRendering = viewer.rendering; - await viewer.setRendering(false); - await viewer.destroySwapChain(); + bool wasRendering = _viewer!.rendering; + await _viewer!.setRendering(false); + await _viewer!.destroySwapChain(); await destroyTexture(texture); var result = await _channel @@ -188,34 +197,29 @@ class ThermionFlutterFFI extends ThermionFlutterPlatform { if (result == null || result[0] == -1) { throw Exception("Failed to create texture"); } - viewer.viewportDimensions = (width.toDouble(), height.toDouble()); + _viewer!.viewportDimensions = (width.toDouble(), height.toDouble()); var newTexture = ThermionFlutterTexture(result[0], result[1], width, height, result[2]); - await viewer.createSwapChain(width.toDouble(), height.toDouble(), + await _viewer!.createSwapChain(width.toDouble(), height.toDouble(), surface: newTexture.surfaceAddress == null ? nullptr : Pointer.fromAddress(newTexture.surfaceAddress!)); if (newTexture.hardwareTextureId != null) { // ignore: unused_local_variable - var renderTarget = await viewer.createRenderTarget( + var renderTarget = await _viewer!.createRenderTarget( width.toDouble(), height.toDouble(), newTexture.hardwareTextureId!); } - await viewer.updateViewportAndCameraProjection( - width.toDouble(), height.toDouble()); + await _viewer! + .updateViewportAndCameraProjection(width.toDouble(), height.toDouble()); - viewer.viewportDimensions = (width.toDouble(), height.toDouble()); + _viewer!.viewportDimensions = (width.toDouble(), height.toDouble()); if (wasRendering) { - await viewer.setRendering(true); + await _viewer!.setRendering(true); } _textures.add(newTexture); _resizing = false; return newTexture; } - - @override - void dispose() { - // TODO: implement dispose - } } From cfa780597071d44d2fc4fad5a8765da1fb971266 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 12:57:49 +0800 Subject: [PATCH 09/51] add onDispose method to ThermionViewer to register callbacks on disposal --- .../lib/thermion_dart/thermion_viewer.dart | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/thermion_dart/lib/thermion_dart/thermion_viewer.dart b/thermion_dart/lib/thermion_dart/thermion_viewer.dart index 93cf4995..22fe9eb8 100644 --- a/thermion_dart/lib/thermion_dart/thermion_viewer.dart +++ b/thermion_dart/lib/thermion_dart/thermion_viewer.dart @@ -46,7 +46,6 @@ class TextureDetails { } abstract class ThermionViewer { - Future get initialized; /// @@ -279,11 +278,11 @@ abstract class ThermionViewer { /// [fadeInInSecs]/[fadeOutInSecs]/[maxDelta] are used to cross-fade between /// the current active glTF animation ("animation1") and the animation you /// set via this method ("animation2"). The bone orientations will be - /// linearly interpolated between animation1 and animation2; at time 0, - /// the orientation will be 100% animation1, at time [fadeInInSecs], the + /// linearly interpolated between animation1 and animation2; at time 0, + /// the orientation will be 100% animation1, at time [fadeInInSecs], the /// animation will be ((1 - maxDelta) * animation1) + (maxDelta * animation2). - /// This will be applied in reverse after [fadeOutInSecs]. - /// + /// This will be applied in reverse after [fadeOutInSecs]. + /// /// Future addBoneAnimation(ThermionEntity entity, BoneAnimationData animation, {int skinIndex = 0, @@ -701,6 +700,11 @@ abstract class ThermionViewer { /// /// AbstractGizmo? get gizmo; + + /// + /// Register a callback to be invoked when this viewer is disposed. + /// + void onDispose(Future Function() callback); } /// @@ -746,6 +750,7 @@ abstract class Scene { /// /// void registerEntity(ThermionEntity entity); + } abstract class AbstractGizmo { From 025e39318db71185fd817f3bd6fe17f2d4ed6c70 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 12:59:16 +0800 Subject: [PATCH 10/51] add implementation for onDispose to ThermionViewerFFI --- .../thermion_dart/thermion_viewer_ffi.dart | 66 ++++++++++++------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart b/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart index ab83957e..894b689f 100644 --- a/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart +++ b/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart @@ -1,15 +1,11 @@ import 'dart:async'; -import 'dart:io'; import 'dart:math'; import 'package:animation_tools_dart/animation_tools_dart.dart'; import 'package:thermion_dart/thermion_dart/compatibility/compatibility.dart'; - import 'package:thermion_dart/thermion_dart/entities/gizmo.dart'; - import 'package:vector_math/vector_math_64.dart'; import 'thermion_viewer.dart'; import 'scene.dart'; -import 'compatibility/compatibility.dart'; // ignore: constant_identifier_names const ThermionEntity _FILAMENT_ASSET_ERROR = 0; @@ -19,8 +15,8 @@ typedef RenderCallback = Pointer)>>; class ThermionViewerFFI extends ThermionViewer { final _compat = Compatibility(); - late SceneImpl _scene; - Scene get scene => _scene; + SceneImpl? _scene; + Scene get scene => _scene!; double _pixelRatio = 1.0; @@ -172,14 +168,39 @@ class ThermionViewerFFI extends ThermionViewer { set_frame_interval_ffi(_viewer!, interval); } + final _onDispose = []; + /// /// /// @override Future dispose() async { + if (_viewer == null) { + // we've already cleaned everything up, ignore the call to dispose + return; + } + await setRendering(false); + await clearEntities(); + await clearLights(); + await _scene!.dispose(); + _scene = null; destroy_filament_viewer_ffi(_viewer!); + _sceneManager = null; _viewer = null; + await _pickResultController.close(); + + for (final callback in _onDispose) { + await callback.call(); + } + _onDispose.clear(); + } + + /// + /// + /// + void onDispose(Future Function() callback) { + _onDispose.add(callback); } /// @@ -317,7 +338,7 @@ class ThermionViewerFFI extends ThermionViewer { throw Exception("Failed to add light to scene"); } - _scene.registerLight(entity); + _scene!.registerLight(entity); return entity; } @@ -326,7 +347,7 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future removeLight(ThermionEntity entity) async { - _scene.unregisterLight(entity); + _scene!.unregisterLight(entity); remove_light_ffi(_viewer!, entity); } @@ -337,7 +358,7 @@ class ThermionViewerFFI extends ThermionViewer { Future clearLights() async { clear_lights_ffi(_viewer!); - _scene.clearLights(); + _scene!.clearLights(); } /// @@ -393,7 +414,7 @@ class ThermionViewerFFI extends ThermionViewer { if (entity == _FILAMENT_ASSET_ERROR) { throw Exception("An error occurred loading the asset at $path"); } - _scene.registerEntity(entity); + _scene!.registerEntity(entity); return entity; } @@ -404,11 +425,6 @@ class ThermionViewerFFI extends ThermionViewer { @override Future loadGltf(String path, String relativeResourcePath, {bool force = false}) async { - // if (Platform.isWindows && !force) { - // throw Exception( - // "loadGltf has a race condition on Windows which is likely to crash your program. If you really want to try, pass force=true to loadGltf"); - // } - final pathPtr = path.toNativeUtf8(allocator: allocator).cast(); final relativeResourcePathPtr = relativeResourcePath.toNativeUtf8(allocator: allocator).cast(); @@ -419,7 +435,7 @@ class ThermionViewerFFI extends ThermionViewer { if (entity == _FILAMENT_ASSET_ERROR) { throw Exception("An error occurred loading the asset at $path"); } - _scene.registerEntity(entity); + _scene!.registerEntity(entity); return entity; } @@ -676,8 +692,8 @@ class ThermionViewerFFI extends ThermionViewer { Future addBoneAnimation(ThermionEntity entity, BoneAnimationData animation, {int skinIndex = 0, double fadeOutInSecs = 0.0, - double fadeInInSecs = 0.0, - double maxDelta=1.0}) async { + double fadeInInSecs = 0.0, + double maxDelta = 1.0}) async { if (animation.space != Space.Bone && animation.space != Space.ParentWorldRotation) { throw UnimplementedError("TODO - support ${animation.space}"); @@ -720,7 +736,7 @@ class ThermionViewerFFI extends ThermionViewer { var world = Matrix4.identity(); // this odd use of ! is intentional, without it, the WASM optimizer gets in trouble var parentBoneEntity = (await getParent(boneEntity))!; - while(true) { + while (true) { if (!bones.contains(parentBoneEntity!)) { break; } @@ -758,7 +774,7 @@ class ThermionViewerFFI extends ThermionViewer { numFrames, animation.frameLengthInMs, fadeOutInSecs, - fadeInInSecs, + fadeInInSecs, maxDelta); } allocator.free(data); @@ -894,7 +910,7 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future removeEntity(ThermionEntity entity) async { - _scene.unregisterEntity(entity); + _scene!.unregisterEntity(entity); await withVoidCallback( (callback) => remove_entity_ffi(_viewer!, entity, callback)); @@ -911,7 +927,7 @@ class ThermionViewerFFI extends ThermionViewer { await withVoidCallback((callback) { clear_entities_ffi(_viewer!, callback); }); - _scene.clearEntities(); + _scene!.clearEntities(); } /// @@ -1296,7 +1312,7 @@ class ThermionViewerFFI extends ThermionViewer { x: (x / _pixelRatio).toDouble(), y: (viewportDimensions.$2 - y) / _pixelRatio )); - _scene.registerSelected(entityId); + _scene!.registerSelected(entityId); } late NativeCallable @@ -1307,7 +1323,7 @@ class ThermionViewerFFI extends ThermionViewer { /// @override void pick(int x, int y) async { - _scene.unregisterSelected(); + _scene!.unregisterSelected(); filament_pick( _viewer!, @@ -1624,7 +1640,7 @@ class ThermionViewerFFI extends ThermionViewer { throw Exception("Failed to create geometry"); } - _scene.registerEntity(entity); + _scene!.registerEntity(entity); allocator.free(materialPathPtr); allocator.free(vertexPtr); From 89f49f98c4ad2452550d9bc1fef54394912576db Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 13:01:20 +0800 Subject: [PATCH 11/51] replace print with Logger --- .../thermion_dart/thermion_viewer_ffi.dart | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart b/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart index 894b689f..0d9daf48 100644 --- a/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart +++ b/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart @@ -6,6 +6,7 @@ import 'package:thermion_dart/thermion_dart/entities/gizmo.dart'; import 'package:vector_math/vector_math_64.dart'; import 'thermion_viewer.dart'; import 'scene.dart'; +import 'package:logging/logging.dart'; // ignore: constant_identifier_names const ThermionEntity _FILAMENT_ASSET_ERROR = 0; @@ -13,6 +14,7 @@ const ThermionEntity _FILAMENT_ASSET_ERROR = 0; typedef RenderCallback = Pointer)>>; class ThermionViewerFFI extends ThermionViewer { + final _logger = Logger("ThermionViewerFFI"); final _compat = Compatibility(); SceneImpl? _scene; @@ -68,7 +70,7 @@ class ThermionViewerFFI extends ThermionViewer { NativeCallable.listener( _onPickResult); } catch (err) { - print( + _logger.severe( "Failed to set pick result callback. This is expected if running on web/wasm"); } _initialize(); @@ -185,7 +187,7 @@ class ThermionViewerFFI extends ThermionViewer { await _scene!.dispose(); _scene = null; destroy_filament_viewer_ffi(_viewer!); - + _sceneManager = null; _viewer = null; await _pickResultController.close(); @@ -624,14 +626,12 @@ class ThermionViewerFFI extends ThermionViewer { var meshEntity = meshEntities[i]; if (targetMeshNames?.contains(meshName) == false) { - print("Skipping $meshName, not contained in target"); + _logger.info("Skipping $meshName, not contained in target"); continue; } var meshMorphTargets = await getMorphTargetNames(entity, meshEntity); - print("Got mesh morph targets ${meshMorphTargets}"); - var intersection = animation.morphTargets .toSet() .intersection(meshMorphTargets.toSet()) @@ -726,7 +726,7 @@ class ThermionViewerFFI extends ThermionViewer { var boneName = animation.bones[i]; var entityBoneIndex = boneNames.indexOf(boneName); if (entityBoneIndex == -1) { - print("Warning : bone $boneName not found, skipping"); + _logger.warning("Bone $boneName not found, skipping"); continue; } var boneEntity = bones[entityBoneIndex]; @@ -1000,7 +1000,6 @@ class ThermionViewerFFI extends ThermionViewer { var animations = await getAnimationNames(entity); var index = animations.indexOf(name); var duration = await getAnimationDuration(entity, index); - print("Duration for $name : $duration"); await playAnimation(entity, index, loop: loop, reverse: reverse, @@ -1430,9 +1429,6 @@ class ThermionViewerFFI extends ThermionViewer { throw Exception("No viewer available"); } - print( - "WARNING: getCameraProjectionMatrix and getCameraCullingProjectionMatrix are not reliable. Consider these broken"); - var arrayPtr = get_camera_projection_matrix(_viewer!); var doubleList = arrayPtr.asTypedList(16); var projectionMatrix = Matrix4.fromList(doubleList); @@ -1448,7 +1444,7 @@ class ThermionViewerFFI extends ThermionViewer { if (_viewer == null) { throw Exception("No viewer available"); } - print( + throw Exception( "WARNING: getCameraProjectionMatrix and getCameraCullingProjectionMatrix are not reliable. Consider these broken"); var arrayPtr = get_camera_culling_projection_matrix(_viewer!); var doubleList = arrayPtr.asTypedList(16); @@ -1467,7 +1463,6 @@ class ThermionViewerFFI extends ThermionViewer { } var arrayPtr = get_camera_frustum(_viewer!); var doubleList = arrayPtr.asTypedList(24); - print(doubleList); var frustum = Frustum(); frustum.plane0.setFromComponents( From dc709097157eb543a04739d36cc3123e2e233933 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 13:04:34 +0800 Subject: [PATCH 12/51] move Scene class to own file and add dispose() method --- thermion_dart/lib/thermion_dart/scene.dart | 140 +++++------------- .../lib/thermion_dart/scene_impl.dart | 125 ++++++++++++++++ .../lib/thermion_dart/thermion_viewer.dart | 47 +----- .../thermion_dart/thermion_viewer_ffi.dart | 3 +- 4 files changed, 162 insertions(+), 153 deletions(-) create mode 100644 thermion_dart/lib/thermion_dart/scene_impl.dart diff --git a/thermion_dart/lib/thermion_dart/scene.dart b/thermion_dart/lib/thermion_dart/scene.dart index 2bb820b4..76d86d43 100644 --- a/thermion_dart/lib/thermion_dart/scene.dart +++ b/thermion_dart/lib/thermion_dart/scene.dart @@ -1,120 +1,48 @@ +import 'package:thermion_dart/thermion_dart/thermion_viewer.dart'; import 'dart:async'; -import 'thermion_viewer.dart'; - /// /// For now, this class just holds the entities that have been loaded (though not necessarily visible in the Filament Scene). /// -class SceneImpl extends Scene { - - ThermionViewer controller; - - SceneImpl(this.controller); - - @override +abstract class Scene { + /// + /// The last entity clicked/tapped in the viewport (internally, the result of calling pick); ThermionEntity? selected; - final _onUpdatedController = StreamController.broadcast(); - @override - Stream get onUpdated => _onUpdatedController.stream; - - final _onLoadController = StreamController.broadcast(); - @override - Stream get onLoad => _onLoadController.stream; - - final _onUnloadController = StreamController.broadcast(); - @override - Stream get onUnload => _onUnloadController.stream; - - final _lights = {}; - final _entities = {}; - - void registerLight(ThermionEntity entity) { - _lights.add(entity); - _onLoadController.sink.add(entity); - _onUpdatedController.add(true); - } - - void unregisterLight(ThermionEntity entity) async { - var children = await controller.getChildEntities(entity, true); - if (selected == entity || children.contains(selected)) { - selected = null; - controller.gizmo?.detach(); - } - _lights.remove(entity); - _onUnloadController.add(entity); - _onUpdatedController.add(true); - } - - void unregisterEntity(ThermionEntity entity) async { - var children = await controller.getChildEntities(entity, true); - if (selected == entity || children.contains(selected)) { - selected = null; - - controller.gizmo?.detach(); - } - _entities.remove(entity); - _onUnloadController.add(entity); - _onUpdatedController.add(true); - } - - void registerEntity(ThermionEntity entity) { - _entities.add(entity); - _onLoadController.sink.add(entity); - _onUpdatedController.add(true); - } - - void clearLights() { - for (final light in _lights) { - if (selected == light) { - selected = null; - controller.gizmo?.detach(); - } - _onUnloadController.add(light); - } - - _lights.clear(); - _onUpdatedController.add(true); - } - - void clearEntities() { - for (final entity in _entities) { - if (selected == entity) { - selected = null; - controller.gizmo?.detach(); - } - _onUnloadController.add(entity); - } - _entities.clear(); - _onUpdatedController.add(true); - } + /// + /// A Stream updated whenever an entity is added/removed from the scene. + /// + Stream get onUpdated; /// - /// Lists all entities currently loaded (not necessarily active in the scene). + /// A Stream containing every ThermionEntity added to the scene (i.e. via [loadGlb], [loadGltf] or [addLight]). + /// This is provided for convenience so you can set listeners in front-end widgets that can respond to entity loads without manually passing around the ThermionEntity returned from those methods. /// - Iterable listLights() { - return _lights; - } + Stream get onLoad; - @override - Iterable listEntities() { - return _entities; - } + /// + /// A Stream containing every ThermionEntity removed from the scene (i.e. via [removeEntity], [clearEntities], [removeLight] or [clearLights]). - void registerSelected(ThermionEntity entity) { - selected = entity; - _onUpdatedController.add(true); - } + Stream get onUnload; - void unregisterSelected() { - selected = null; - _onUpdatedController.add(true); - } + /// + /// Lists all light entities currently loaded (not necessarily active in the scene). Does not account for instances. + /// + Iterable listLights(); - @override - void select(ThermionEntity entity) { - selected = entity; - controller.gizmo?.attach(entity); - _onUpdatedController.add(true); - } -} + /// + /// Lists all entities currently loaded (not necessarily active in the scene). Does not account for instances. + /// + Iterable listEntities(); + + /// + /// Attach the gizmo to the specified entity. + /// + void select(ThermionEntity entity); + + /// + /// + /// + void registerEntity(ThermionEntity entity); + +} \ No newline at end of file diff --git a/thermion_dart/lib/thermion_dart/scene_impl.dart b/thermion_dart/lib/thermion_dart/scene_impl.dart new file mode 100644 index 00000000..a31c8848 --- /dev/null +++ b/thermion_dart/lib/thermion_dart/scene_impl.dart @@ -0,0 +1,125 @@ +import 'dart:async'; +import 'package:thermion_dart/thermion_dart/scene.dart'; +import 'thermion_viewer.dart'; + +/// +/// For now, this class just holds the entities that have been loaded (though not necessarily visible in the Filament Scene). +/// +class SceneImpl extends Scene { + ThermionViewer controller; + + SceneImpl(this.controller); + + @override + ThermionEntity? selected; + + final _onUpdatedController = StreamController.broadcast(); + @override + Stream get onUpdated => _onUpdatedController.stream; + + final _onLoadController = StreamController.broadcast(); + @override + Stream get onLoad => _onLoadController.stream; + + final _onUnloadController = StreamController.broadcast(); + @override + Stream get onUnload => _onUnloadController.stream; + + final _lights = {}; + final _entities = {}; + + void registerLight(ThermionEntity entity) { + _lights.add(entity); + _onLoadController.sink.add(entity); + _onUpdatedController.add(true); + } + + void unregisterLight(ThermionEntity entity) async { + var children = await controller.getChildEntities(entity, true); + if (selected == entity || children.contains(selected)) { + selected = null; + controller.gizmo?.detach(); + } + _lights.remove(entity); + _onUnloadController.add(entity); + _onUpdatedController.add(true); + } + + void unregisterEntity(ThermionEntity entity) async { + var children = await controller.getChildEntities(entity, true); + if (selected == entity || children.contains(selected)) { + selected = null; + + controller.gizmo?.detach(); + } + _entities.remove(entity); + _onUnloadController.add(entity); + _onUpdatedController.add(true); + } + + void registerEntity(ThermionEntity entity) { + _entities.add(entity); + _onLoadController.sink.add(entity); + _onUpdatedController.add(true); + } + + void clearLights() { + for (final light in _lights) { + if (selected == light) { + selected = null; + controller.gizmo?.detach(); + } + _onUnloadController.add(light); + } + + _lights.clear(); + _onUpdatedController.add(true); + } + + void clearEntities() { + for (final entity in _entities) { + if (selected == entity) { + selected = null; + controller.gizmo?.detach(); + } + _onUnloadController.add(entity); + } + _entities.clear(); + _onUpdatedController.add(true); + } + + /// + /// Lists all entities currently loaded (not necessarily active in the scene). + /// + Iterable listLights() { + return _lights; + } + + @override + Iterable listEntities() { + return _entities; + } + + void registerSelected(ThermionEntity entity) { + selected = entity; + _onUpdatedController.add(true); + } + + void unregisterSelected() { + selected = null; + _onUpdatedController.add(true); + } + + @override + void select(ThermionEntity entity) { + selected = entity; + controller.gizmo?.attach(entity); + _onUpdatedController.add(true); + } + + Future dispose() async { + await _onLoadController.close(); + await _onUnloadController.close(); + await _onUpdatedController.close(); + } +} diff --git a/thermion_dart/lib/thermion_dart/thermion_viewer.dart b/thermion_dart/lib/thermion_dart/thermion_viewer.dart index 22fe9eb8..73f458ec 100644 --- a/thermion_dart/lib/thermion_dart/thermion_viewer.dart +++ b/thermion_dart/lib/thermion_dart/thermion_viewer.dart @@ -1,5 +1,6 @@ import 'dart:math'; +import 'package:thermion_dart/thermion_dart/scene.dart'; import 'package:vector_math/vector_math_64.dart'; import 'dart:async'; import 'package:animation_tools_dart/animation_tools_dart.dart'; @@ -707,52 +708,6 @@ abstract class ThermionViewer { void onDispose(Future Function() callback); } -/// -/// For now, this class just holds the entities that have been loaded (though not necessarily visible in the Filament Scene). -/// -abstract class Scene { - /// - /// The last entity clicked/tapped in the viewport (internally, the result of calling pick); - ThermionEntity? selected; - - /// - /// A Stream updated whenever an entity is added/removed from the scene. - /// - Stream get onUpdated; - - /// - /// A Stream containing every ThermionEntity added to the scene (i.e. via [loadGlb], [loadGltf] or [addLight]). - /// This is provided for convenience so you can set listeners in front-end widgets that can respond to entity loads without manually passing around the ThermionEntity returned from those methods. - /// - Stream get onLoad; - - /// - /// A Stream containing every ThermionEntity removed from the scene (i.e. via [removeEntity], [clearEntities], [removeLight] or [clearLights]). - - Stream get onUnload; - - /// - /// Lists all light entities currently loaded (not necessarily active in the scene). Does not account for instances. - /// - Iterable listLights(); - - /// - /// Lists all entities currently loaded (not necessarily active in the scene). Does not account for instances. - /// - Iterable listEntities(); - - /// - /// Attach the gizmo to the specified entity. - /// - void select(ThermionEntity entity); - - /// - /// - /// - void registerEntity(ThermionEntity entity); - -} - abstract class AbstractGizmo { bool get isActive; diff --git a/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart b/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart index 0d9daf48..7dc0018f 100644 --- a/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart +++ b/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart @@ -3,9 +3,10 @@ import 'dart:math'; import 'package:animation_tools_dart/animation_tools_dart.dart'; import 'package:thermion_dart/thermion_dart/compatibility/compatibility.dart'; import 'package:thermion_dart/thermion_dart/entities/gizmo.dart'; +import 'package:thermion_dart/thermion_dart/scene.dart'; import 'package:vector_math/vector_math_64.dart'; import 'thermion_viewer.dart'; -import 'scene.dart'; +import 'scene_impl.dart'; import 'package:logging/logging.dart'; // ignore: constant_identifier_names From 6cfa86fe750d4bd4d4df3ecfae5dee5ab4fd2eca Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 13:11:01 +0800 Subject: [PATCH 13/51] make ResourceBuffer.hpp C++ only and add constructor to create from C struct --- thermion_dart/native/include/ResourceBuffer.hpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/thermion_dart/native/include/ResourceBuffer.hpp b/thermion_dart/native/include/ResourceBuffer.hpp index 57c68bda..94f64c05 100644 --- a/thermion_dart/native/include/ResourceBuffer.hpp +++ b/thermion_dart/native/include/ResourceBuffer.hpp @@ -3,7 +3,6 @@ #include "ResourceBuffer.h" -#if defined(__cplusplus) #ifndef __EMSCRIPTEN__ #include using namespace std::chrono_literals; @@ -15,6 +14,15 @@ namespace thermion_filament struct ResourceLoaderWrapperImpl : public ResourceLoaderWrapper { + ResourceLoaderWrapperImpl(ResourceLoaderWrapper* wrapper) { + loadFromOwner = wrapper->loadFromOwner; + freeFromOwner = wrapper->freeFromOwner; + loadResource = wrapper->loadResource; + freeResource = wrapper->freeResource; + owner = wrapper->owner; + loadToOut = wrapper->loadToOut; + } + ResourceLoaderWrapperImpl(LoadFilamentResource loader, FreeFilamentResource freeResource) { loadFromOwner = nullptr; @@ -73,4 +81,3 @@ namespace thermion_filament } #endif -#endif From 3785580b01607f7225bf4bdad21e0b912a0e41f7 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 13:12:28 +0800 Subject: [PATCH 14/51] free all resources in SceneManager --- thermion_dart/native/include/SceneManager.hpp | 8 +- thermion_dart/native/src/SceneManager.cpp | 79 ++++++++++--------- 2 files changed, 45 insertions(+), 42 deletions(-) diff --git a/thermion_dart/native/include/SceneManager.hpp b/thermion_dart/native/include/SceneManager.hpp index f9f6efaa..484b75d2 100644 --- a/thermion_dart/native/include/SceneManager.hpp +++ b/thermion_dart/native/include/SceneManager.hpp @@ -179,7 +179,6 @@ namespace thermion_filament gltfio::TextureProvider *_stbDecoder = nullptr; gltfio::TextureProvider *_ktxDecoder = nullptr; std::mutex _mutex; - Material *_gizmoMaterial; utils::NameComponentManager *_ncm; @@ -201,8 +200,9 @@ namespace thermion_filament const char *entityName); EntityId addGizmo(); - utils::Entity _gizmoX; - utils::Entity _gizmoY; - utils::Entity _gizmoZ; + utils::Entity _gizmo[3]; + Material* _gizmoMaterial; + MaterialInstance* _gizmoMaterialInstances[3]; + }; } diff --git a/thermion_dart/native/src/SceneManager.cpp b/thermion_dart/native/src/SceneManager.cpp index c75a6f4d..2329492f 100644 --- a/thermion_dart/native/src/SceneManager.cpp +++ b/thermion_dart/native/src/SceneManager.cpp @@ -93,10 +93,28 @@ namespace thermion_filament SceneManager::~SceneManager() { + + destroyAll(); + + for(int i =0; i < 3; i++) { + _engine->destroy(_gizmo[i]); + _engine->destroy(_gizmoMaterialInstances[i]); + } + + _engine->destroy(_gizmoMaterial); _gltfResourceLoader->asyncCancelLoad(); _ubershaderProvider->destroyMaterials(); - destroyAll(); + + delete _animationComponentManager; + delete _collisionComponentManager; + delete _ncm; + + delete _gltfResourceLoader; + delete _stbDecoder; + delete _ktxDecoder; + delete _ubershaderProvider; AssetLoader::destroy(&_assetLoader); + } int SceneManager::getInstanceCount(EntityId entityId) @@ -2045,72 +2063,57 @@ namespace thermion_filament auto &entityManager = EntityManager::get(); - _gizmoY = entityManager.create(); - auto materialY = _gizmoMaterial->createInstance(); - materialY->setParameter("color", math::float3{1.0f, 0.0f, 0.0f}); + _gizmo[1] = entityManager.create(); + _gizmoMaterialInstances[1] = _gizmoMaterial->createInstance(); + _gizmoMaterialInstances[1]->setParameter("color", math::float3{1.0f, 0.0f, 0.0f}); RenderableManager::Builder(1) .boundingBox({{}, {1.0f, 1.0f, 1.0f}}) - .material(0, materialY) + .material(0, _gizmoMaterialInstances[1]) .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, ib, 0, indexCount) .culling(false) - .build(*_engine, _gizmoY); + .build(*_engine, _gizmo[1]); - _gizmoX = entityManager.create(); - auto materialX = _gizmoMaterial->createInstance(); - materialX->setParameter("color", math::float3{0.0f, 1.0f, 0.0f}); + _gizmo[0] = entityManager.create(); + _gizmoMaterialInstances[0] = _gizmoMaterial->createInstance(); + _gizmoMaterialInstances[0]->setParameter("color", math::float3{0.0f, 1.0f, 0.0f}); auto xTransform = math::mat4f::translation(math::float3{0.0f, 0.05f, -0.05f}) * math::mat4f::rotation(-math::F_PI_2, math::float3{0, 0, 1}); auto *instanceBufferX = InstanceBuffer::Builder(1).localTransforms(&xTransform).build(*_engine); RenderableManager::Builder(1) .boundingBox({{}, {1.0f, 1.0f, 1.0f}}) .instances(1, instanceBufferX) - .material(0, materialX) + .material(0, _gizmoMaterialInstances[0]) .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, ib, 0, indexCount) .culling(false) - .build(*_engine, _gizmoX); + .build(*_engine, _gizmo[0]); - _gizmoZ = entityManager.create(); - auto materialZ = _gizmoMaterial->createInstance(); - materialZ->setParameter("color", math::float3{0.0f, 0.0f, 1.0f}); + _gizmo[2] = entityManager.create(); + _gizmoMaterialInstances[2] = _gizmoMaterial->createInstance(); + _gizmoMaterialInstances[2]->setParameter("color", math::float3{0.0f, 0.0f, 1.0f}); auto zTransform = math::mat4f::translation(math::float3{0.0f, 0.05f, -0.05f}) * math::mat4f::rotation(3 * math::F_PI_2, math::float3{1, 0, 0}); auto *instanceBufferZ = InstanceBuffer::Builder(1).localTransforms(&zTransform).build(*_engine); RenderableManager::Builder(1) .boundingBox({{}, {1.0f, 1.0f, 1.0f}}) .instances(1, instanceBufferZ) - .material(0, materialZ) + .material(0, _gizmoMaterialInstances[2]) .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, ib, 0, indexCount) .culling(false) - .build(*_engine, _gizmoZ); - - // auto localTransforms = math::mat4f[3] { - // math::mat4f(), - // math::mat4f::translation(math::float3 { 0.0f, 0.05f, -0.05f}) * math::mat4f::rotation(3 * math::F_PI_2, math::float3 { 1, 0, 0 }) , - // math::mat4f::translation(math::float3 { 0.0f, 0.05f, -0.05f}) * math::mat4f::rotation(math::F_PI_2, math::float3 { 0, 0, 1 }) - // }; - - // RenderableManager::Builder(1) - // .boundingBox({{}, {1.0f, 1.0f, 1.0f}}) - // .instances(3, instanceBuffer) - // .material(0, _gizmoMaterial->getDefaultInstance()) - // .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, - // ib, 0, indexCount) - // .culling(false) - // .build(*_engine, _gizmo); + .build(*_engine, _gizmo[2]); auto &rm = _engine->getRenderableManager(); - rm.setPriority(rm.getInstance(_gizmoX), 7); - rm.setPriority(rm.getInstance(_gizmoY), 7); - rm.setPriority(rm.getInstance(_gizmoZ), 7); - return Entity::smuggle(_gizmoX); + rm.setPriority(rm.getInstance(_gizmo[0]), 7); + rm.setPriority(rm.getInstance(_gizmo[1]), 7); + rm.setPriority(rm.getInstance(_gizmo[2]), 7); + return Entity::smuggle(_gizmo[0]); } void SceneManager::getGizmo(EntityId *out) { - out[0] = Entity::smuggle(_gizmoX); - out[1] = Entity::smuggle(_gizmoY); - out[2] = Entity::smuggle(_gizmoZ); + out[0] = Entity::smuggle(_gizmo[0]); + out[1] = Entity::smuggle(_gizmo[1]); + out[2] = Entity::smuggle(_gizmo[2]); } } // namespace thermion_filament From a455e4f88a8628415bb8c9d4beb76ce0b241a719 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 13:13:36 +0800 Subject: [PATCH 15/51] construct (C++) ResourceLoaderWrapperImpl from (C) ResourceLoaderWrapper --- thermion_dart/native/src/ThermionDartApi.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/thermion_dart/native/src/ThermionDartApi.cpp b/thermion_dart/native/src/ThermionDartApi.cpp index a11cde46..bee3ab48 100644 --- a/thermion_dart/native/src/ThermionDartApi.cpp +++ b/thermion_dart/native/src/ThermionDartApi.cpp @@ -27,7 +27,8 @@ extern "C" EMSCRIPTEN_KEEPALIVE const void *create_filament_viewer(const void *context, const void *const loader, void *const platform, const char *uberArchivePath) { - auto viewer = (const void *)new FilamentViewer(context, (const ResourceLoaderWrapperImpl *const)loader, platform, uberArchivePath); + const auto * loaderImpl = new ResourceLoaderWrapperImpl((ResourceLoaderWrapper*)loader); + auto viewer = (const void *)new FilamentViewer(context, loaderImpl, platform, uberArchivePath); return viewer; } From 49b8e7023b5431d9a0547f928efe011862c59593 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 13:15:36 +0800 Subject: [PATCH 16/51] free all resources in FilamentViewer destructor --- thermion_dart/native/src/FilamentViewer.cpp | 29 +++++++++------------ 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/thermion_dart/native/src/FilamentViewer.cpp b/thermion_dart/native/src/FilamentViewer.cpp index 653d009b..ad26485f 100644 --- a/thermion_dart/native/src/FilamentViewer.cpp +++ b/thermion_dart/native/src/FilamentViewer.cpp @@ -126,11 +126,10 @@ namespace thermion_filament static const uint16_t sFullScreenTriangleIndices[3] = {0, 1, 2}; - FilamentViewer::FilamentViewer(const void *sharedContext, const ResourceLoaderWrapperImpl *const ResourceLoaderWrapperImpl, void *const platform, const char *uberArchivePath) - : _resourceLoaderWrapper(ResourceLoaderWrapperImpl) + FilamentViewer::FilamentViewer(const void *sharedContext, const ResourceLoaderWrapperImpl *const resourceLoader, void *const platform, const char *uberArchivePath) + : _resourceLoaderWrapper(resourceLoader) { _context = (void*) sharedContext; - ASSERT_POSTCONDITION(_resourceLoaderWrapper != nullptr, "Resource loader must be non-null"); #if TARGET_OS_IPHONE @@ -269,7 +268,6 @@ namespace thermion_filament .culling(false) .build(*_engine, _imageEntity); _scene->addEntity(_imageEntity); - Log("Added imageEntity %d", _imageEntity); } void FilamentViewer::setAntiAliasing(bool msaa, bool fxaa, bool taa) @@ -444,7 +442,8 @@ namespace thermion_filament new ktxreader::Ktx1Bundle(static_cast(rb.data), static_cast(rb.size)); - // because the ResourceBuffer will go out of scope before the texture callback is invoked, we need to make a copy to the heap + // the ResourceBuffer will go out of scope before the texture callback is invoked + // make a copy to the heap ResourceBuffer *rbCopy = new ResourceBuffer(rb); std::vector *callbackData = new std::vector{(void *)_resourceLoaderWrapper, rbCopy}; @@ -680,22 +679,21 @@ namespace thermion_filament FilamentViewer::~FilamentViewer() { - clearEntities(); + clearLights(); + destroySwapChain(); + _engine->destroy(_imageEntity); + _engine->destroy(_imageTexture); + _engine->destroy(_imageVb); + _engine->destroy(_imageIb); + _engine->destroy(_imageMaterial); delete _sceneManager; - - for (auto it : _lights) - { - _engine->destroy(it); - } - _engine->destroyCameraComponent(_mainCamera->getEntity()); _mainCamera = nullptr; _engine->destroy(_view); _engine->destroy(_scene); _engine->destroy(_renderer); - _engine->destroy(_swapChain); - - Engine::destroy(&_engine); // clears engine* + Engine::destroy(&_engine); + delete _resourceLoaderWrapper; } Renderer *FilamentViewer::getRenderer() { return _renderer; } @@ -930,7 +928,6 @@ namespace thermion_filament } Log("Loading skybox from path %s", skyboxPath); - ResourceBuffer skyboxBuffer = _resourceLoaderWrapper->load(skyboxPath); // because this will go out of scope before the texture callback is invoked, we need to make a copy of the variable itself (not its contents) From 7dc0e91e6bffd82469fd52a996793fd1d4d1dbf4 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 13:16:33 +0800 Subject: [PATCH 17/51] fix RenderLoop to use (C) ResourceLoaderWrapper and destroy RL when viewer is destroyed --- .../native/src/ThermionDartFFIApi.cpp | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/thermion_dart/native/src/ThermionDartFFIApi.cpp b/thermion_dart/native/src/ThermionDartFFIApi.cpp index 5791738a..f763830d 100644 --- a/thermion_dart/native/src/ThermionDartFFIApi.cpp +++ b/thermion_dart/native/src/ThermionDartFFIApi.cpp @@ -15,7 +15,6 @@ extern "C" } #include - #endif #include "ThermionDartFFIApi.h" @@ -52,6 +51,7 @@ public: { _stop = true; pthread_join(t, NULL); + Log("Render loop killed"); } static void mainLoop(void* arg) { @@ -103,48 +103,47 @@ public: } } - void createViewer(void *const context, void *const platform, + void createViewer(void *const context, + void *const platform, const char *uberArchivePath, - const ResourceLoaderWrapperImpl *const loader, + const ResourceLoaderWrapper *const loader, void (*renderCallback)(void *), void *const owner, void (*callback)(void *const)) { _renderCallback = renderCallback; _renderCallbackOwner = owner; - std::packaged_task lambda([=]() mutable + std::packaged_task lambda([=]() mutable { - FilamentViewer* viewer = nullptr; #ifdef __EMSCRIPTEN__ _context = thermion_dart_web_create_gl_context(); auto success = emscripten_webgl_make_context_current((EMSCRIPTEN_WEBGL_CONTEXT_HANDLE)_context); if(success != EMSCRIPTEN_RESULT_SUCCESS) { std::cout << "Failed to make context current." << std::endl; - return viewer; + return; } glClearColor(0.0, 0.5, 0.5, 1.0); glClear(GL_COLOR_BUFFER_BIT); // emscripten_webgl_commit_frame(); - viewer = (FilamentViewer*) create_filament_viewer((void* const) _context, loader, platform, uberArchivePath); + _viewer = (FilamentViewer*) create_filament_viewer((void* const) _context, loader, platform, uberArchivePath); MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0, $1); - }, callback, viewer); + }, callback, _viewer); #else - viewer = (FilamentViewer*)create_filament_viewer(context, loader, platform, uberArchivePath); - callback(viewer); + _viewer = (FilamentViewer*)create_filament_viewer(context, loader, platform, uberArchivePath); + callback(_viewer); #endif - _viewer = viewer; - return viewer; }); + }); auto fut = add_task(lambda); } void destroyViewer(FilamentViewer* viewer) { - std::packaged_task lambda([=]() mutable - { + std::packaged_task lambda([=]() mutable { _rendering = false; + _viewer = nullptr; destroy_filament_viewer(viewer); }); auto fut = add_task(lambda); @@ -237,13 +236,15 @@ extern "C" { _rl = new RenderLoop(); } - _rl->createViewer(context, platform, uberArchivePath, (const ResourceLoaderWrapperImpl *const)loader, + _rl->createViewer(context, platform, uberArchivePath, (const ResourceLoaderWrapper *const)loader, renderCallback, renderCallbackOwner, callback); } EMSCRIPTEN_KEEPALIVE void destroy_filament_viewer_ffi(void *const viewer) { _rl->destroyViewer((FilamentViewer*)viewer); + delete _rl; + _rl = nullptr; } EMSCRIPTEN_KEEPALIVE void create_swap_chain_ffi(void *const viewer, From 1f58717635cd253cd3b11f6ca6e5c5bef6a7bbf8 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 13:17:11 +0800 Subject: [PATCH 18/51] only require ThermionViewer to be passed to ThermionWidget --- .../lib/thermion/widgets/thermion_widget.dart | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget.dart b/thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget.dart index ed8fc6cf..4cd26d4c 100644 --- a/thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget.dart +++ b/thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget.dart @@ -9,7 +9,7 @@ import 'package:thermion_flutter/thermion_flutter.dart'; import 'resize_observer.dart'; class ThermionWidget extends StatefulWidget { - final ThermionFlutterPlugin plugin; + final ThermionViewer viewer; /// /// The content to render before the texture widget is available. @@ -17,7 +17,7 @@ class ThermionWidget extends StatefulWidget { /// final Widget? initial; - const ThermionWidget({Key? key, this.initial, required this.plugin}) + const ThermionWidget({Key? key, this.initial, required this.viewer}) : super(key: key); @override @@ -30,12 +30,22 @@ class _ThermionWidgetState extends State { @override void initState() { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { - await widget.plugin.initialized; + await widget.viewer.initialized; + widget.viewer.onDispose(() async { + if (_texture != null) { + var texture = _texture; + _texture = null; + if (mounted) { + setState(() {}); + } + await ThermionFlutterPlugin.destroyTexture(texture!); + } + }); var dpr = MediaQuery.of(context).devicePixelRatio; var size = ((context.findRenderObject()) as RenderBox).size; var width = (dpr * size.width).ceil(); var height = (dpr * size.height).ceil(); - _texture = await widget.plugin.createTexture(width, height, 0, 0); + _texture = await ThermionFlutterPlugin.createTexture(width, height, 0, 0); if (mounted) { setState(() {}); @@ -50,18 +60,20 @@ class _ThermionWidgetState extends State { Future _resizeTexture(Size newSize) async { _resizeTimer?.cancel(); _resizeTimer = Timer(Duration(milliseconds: 500), () async { - if (_resizing) { + if (_resizing || !mounted) { return; } _resizeTimer!.cancel(); _resizing = true; var oldTexture = _texture; _texture = null; - setState(() {}); + if (!mounted) { + return; + } var dpr = MediaQuery.of(context).devicePixelRatio; - _texture = await widget.plugin.resizeTexture(oldTexture!, + _texture = await ThermionFlutterPlugin.resizeTexture(oldTexture!, (dpr * newSize.width).ceil(), (dpr * newSize.height).ceil(), 0, 0); setState(() {}); _resizing = false; From 3468e7da553d18d298ebeb249eee60f82f89674e Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 13:19:04 +0800 Subject: [PATCH 19/51] dont make ThermionFlutterTexture inherit from ThermionDartTexture --- .../macos/Classes/ThermionFlutterTexture.swift | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/thermion_flutter/thermion_flutter/macos/Classes/ThermionFlutterTexture.swift b/thermion_flutter/thermion_flutter/macos/Classes/ThermionFlutterTexture.swift index 1a2f4c1d..57eea716 100644 --- a/thermion_flutter/thermion_flutter/macos/Classes/ThermionFlutterTexture.swift +++ b/thermion_flutter/thermion_flutter/macos/Classes/ThermionFlutterTexture.swift @@ -2,20 +2,24 @@ import Foundation import GLKit import FlutterMacOS -public class ThermionFlutterTexture : ThermionDartTexture, FlutterTexture { +public class ThermionFlutterTexture : NSObject, FlutterTexture { var flutterTextureId: Int64 = -1 var registry: FlutterTextureRegistry + var texture: ThermionDartTexture init(registry:FlutterTextureRegistry, width:Int64, height:Int64) { self.registry = registry - super.init(width:width, height:height) + self.texture = ThermionDartTexture(width:width, height: height) + super.init() self.flutterTextureId = registry.register(self) - } public func copyPixelBuffer() -> Unmanaged? { - return Unmanaged.passRetained(pixelBuffer!); + if(self.texture.pixelBuffer == nil) { + return nil + } + return Unmanaged.passRetained(self.texture.pixelBuffer!); } public func onTextureUnregistered(_ texture:FlutterTexture) { @@ -24,7 +28,7 @@ public class ThermionFlutterTexture : ThermionDartTexture, FlutterTexture { public func destroy() { self.registry.unregisterTexture(self.flutterTextureId) - self.destroyTexture() + self.texture.destroyTexture() } -} \ No newline at end of file +} From 534e5b46a82443cbc2dab2f8b900f569f376e024 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 13:20:04 +0800 Subject: [PATCH 20/51] (Swift/macos) dont make ThermionFlutterTexture inherit from ThermionDartTexture; reuse render callbacks/resource loader functions; add check for concurrent calls to destroyTexture --- .../Classes/SwiftThermionFlutterPlugin.swift | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/thermion_flutter/thermion_flutter/macos/Classes/SwiftThermionFlutterPlugin.swift b/thermion_flutter/thermion_flutter/macos/Classes/SwiftThermionFlutterPlugin.swift index ca7ae110..ba07fcc0 100644 --- a/thermion_flutter/thermion_flutter/macos/Classes/SwiftThermionFlutterPlugin.swift +++ b/thermion_flutter/thermion_flutter/macos/Classes/SwiftThermionFlutterPlugin.swift @@ -8,6 +8,8 @@ public class SwiftThermionFlutterPlugin: NSObject, FlutterPlugin { var texture: ThermionFlutterTexture? var createdAt = Date() + + var destroying = false var resources:[UInt32:NSData] = [:] @@ -70,43 +72,54 @@ public class SwiftThermionFlutterPlugin: NSObject, FlutterPlugin { registrar.addMethodCallDelegate(instance, channel: channel) } + var resourceLoaderWrapper:UnsafeMutablePointer? = nil + var renderCallbackHolder:[Any] = [] + init(textureRegistry: FlutterTextureRegistry, registrar:FlutterPluginRegistrar) { self.registry = textureRegistry; self.registrar = registrar } - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { let methodName = call.method; switch methodName { case "getResourceLoaderWrapper": - var resourceLoaderWrapper = make_resource_loader(loadResource, freeResource, Unmanaged.passUnretained(self).toOpaque()) - result(unsafeBitCast(resourceLoaderWrapper, to:Int64.self)) + if(resourceLoaderWrapper == nil) { + resourceLoaderWrapper = make_resource_loader(loadResource, freeResource, Unmanaged.passUnretained(self).toOpaque()) + } + result(Int64(Int(bitPattern: resourceLoaderWrapper!))) case "getRenderCallback": - let renderCallback = markTextureFrameAvailable - let resultArray:[Any] = [ - unsafeBitCast(renderCallback, to:Int64.self), unsafeBitCast(Unmanaged.passUnretained(self), to:UInt64.self)] - result(resultArray) + if(renderCallbackHolder.isEmpty) { + renderCallbackHolder.append(unsafeBitCast(markTextureFrameAvailable, to:Int64.self)) + renderCallbackHolder.append(unsafeBitCast(Unmanaged.passUnretained(self), to:UInt64.self)) + } + result(renderCallbackHolder) case "getDriverPlatform": result(nil) case "getSharedContext": result(nil) case "createTexture": + if(destroying) { + result(nil) + return + } let args = call.arguments as! [Any] let width = args[0] as! Int64 let height = args[1] as! Int64 self.texture = ThermionFlutterTexture(registry: registry, width: width, height: height) - if(self.texture?.metalTextureAddress == -1) { + if(self.texture!.texture.metalTextureAddress == -1) { result(nil) } else { - result([self.texture!.flutterTextureId as Any, self.texture?.metalTextureAddress, nil]) + result([self.texture!.flutterTextureId as Any, self.texture!.texture.metalTextureAddress, nil]) } case "destroyTexture": + self.destroying = true self.texture?.destroy() self.texture = nil result(true) + self.destroying = false default: result(FlutterMethodNotImplemented) } From ab793387bc9d16339d0e008f1d89a51f289fb91c Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 13:21:47 +0800 Subject: [PATCH 21/51] use Unmanaged.passRetained rather than passUnretained for ThermionDartTexture --- .../macos/Classes/ThermionDartTexture.swift | 52 +++++++++++-------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/thermion_flutter/thermion_flutter/macos/Classes/ThermionDartTexture.swift b/thermion_flutter/thermion_flutter/macos/Classes/ThermionDartTexture.swift index c17a7aab..8e7e7c98 100644 --- a/thermion_flutter/thermion_flutter/macos/Classes/ThermionDartTexture.swift +++ b/thermion_flutter/thermion_flutter/macos/Classes/ThermionDartTexture.swift @@ -11,18 +11,20 @@ import GLKit ] as [CFString : Any] as CFDictionary @objc public var cvMetalTextureCache:CVMetalTextureCache? + @objc public var metalDevice:MTLDevice? + @objc public var cvMetalTexture:CVMetalTexture? @objc public var metalTexture:MTLTexture? - @objc public var metalDevice:MTLDevice? @objc public var metalTextureAddress:Int = -1 @objc override public init() { } - @objc public init(width:Int64, height:Int64) { - - self.metalDevice = MTLCreateSystemDefaultDevice()! + @objc public init(width:Int64, height:Int64) { + if(self.metalDevice == nil) { + self.metalDevice = MTLCreateSystemDefaultDevice()! + } // create pixel buffer if(CVPixelBufferCreate(kCFAllocatorDefault, Int(width), Int(height), @@ -31,37 +33,36 @@ import GLKit metalTextureAddress = -1; return } - - var cvret = CVMetalTextureCacheCreate( - kCFAllocatorDefault, - nil, - metalDevice!, - nil, - &cvMetalTextureCache); - if(cvret != 0) { - print("Error creating Metal texture cache") - metalTextureAddress = -1 - return + if self.cvMetalTextureCache == nil { + let cacheCreationResult = CVMetalTextureCacheCreate( + kCFAllocatorDefault, + nil, + self.metalDevice!, + nil, + &self.cvMetalTextureCache) + if(cacheCreationResult != kCVReturnSuccess) { + print("Error creating Metal texture cache") + metalTextureAddress = -1 + return + } } - cvret = CVMetalTextureCacheCreateTextureFromImage( + let cvret = CVMetalTextureCacheCreateTextureFromImage( kCFAllocatorDefault, - cvMetalTextureCache!, + self.cvMetalTextureCache!, pixelBuffer!, nil, MTLPixelFormat.bgra8Unorm, Int(width), Int(height), 0, &cvMetalTexture) - if(cvret != 0) { + if(cvret != kCVReturnSuccess) { print("Error creating texture from image") metalTextureAddress = -1 return } metalTexture = CVMetalTextureGetTexture(cvMetalTexture!) - let metalTexturePtr = Unmanaged.passUnretained(metalTexture!).toOpaque() + let metalTexturePtr = Unmanaged.passRetained(metalTexture!).toOpaque() metalTextureAddress = Int(bitPattern:metalTexturePtr) - - print("Created metal texture @ \(metalTextureAddress)") - + // CVPixelBufferLockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue: 0)) // let bufferWidth = Int(CVPixelBufferGetWidth(pixelBuffer!)) // let bufferHeight = Int(CVPixelBufferGetHeight(pixelBuffer!)) @@ -95,7 +96,12 @@ import GLKit } @objc public func destroyTexture() { - metalTexture = nil + CVMetalTextureCacheFlush(self.cvMetalTextureCache!, 0) + self.metalTexture = nil + self.cvMetalTexture = nil + self.pixelBuffer = nil + self.metalDevice = nil + self.cvMetalTextureCache = nil } From aefedf8c00e0bd96a5d2e9440172866d0e9811ef Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 13:24:08 +0800 Subject: [PATCH 22/51] CHANGELOG --- thermion_flutter/thermion_flutter/CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/thermion_flutter/thermion_flutter/CHANGELOG.md b/thermion_flutter/thermion_flutter/CHANGELOG.md index e69de29b..a285b88c 100644 --- a/thermion_flutter/thermion_flutter/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter/CHANGELOG.md @@ -0,0 +1,8 @@ +## 0.1.0 +* [ThermionFlutterPlugin] is now static and [dispose] has been removed. Call [createViewer] to obtain an instance of [ThermionViewer]. If you need to release all resources, call [dispose] on [ThermionViewer] +* Fixed memory leaks + +## 0.0.4 +* First release of Dart-only package + + From 84101b457f6a32ac4b69ba7a06deea7570d8169a Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 13:31:09 +0800 Subject: [PATCH 23/51] add onDispose() implementations for web viewers --- .../web/interop/filament_viewer_js.dart | 15 +++++++- .../web/interop/filament_viewer_wasm.dart | 35 ++++++++++++++++--- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/interop/filament_viewer_js.dart b/thermion_dart/lib/thermion_dart/compatibility/web/interop/filament_viewer_js.dart index 7abe2477..928b3bcf 100644 --- a/thermion_dart/lib/thermion_dart/compatibility/web/interop/filament_viewer_js.dart +++ b/thermion_dart/lib/thermion_dart/compatibility/web/interop/filament_viewer_js.dart @@ -3,9 +3,10 @@ import 'dart:js_interop_unsafe'; import 'dart:math'; import 'package:animation_tools_dart/animation_tools_dart.dart'; +import 'package:thermion_dart/thermion_dart/scene.dart'; import 'package:thermion_dart/thermion_dart/thermion_viewer.dart'; -import 'package:thermion_dart/thermion_dart/scene.dart'; +import 'package:thermion_dart/thermion_dart/scene_impl.dart'; import 'package:vector_math/vector_math_64.dart'; import 'shims/thermion_viewer_js_shim.dart'; @@ -56,6 +57,9 @@ class ThermionViewerFFIJS implements ThermionViewer { @override Future dispose() async { await _shim.dispose().toDart; + for (final callback in _onDispose) { + callback.call(); + } } @override @@ -813,4 +817,13 @@ class ThermionViewerFFIJS implements ThermionViewer { Future updateBoneMatrices(ThermionEntity entity) { return _shim.updateBoneMatrices(entity).toDart; } + + final _onDispose = []; + + /// + /// + /// + void onDispose(Future Function() callback) { + _onDispose.add(callback); + } } diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/interop/filament_viewer_wasm.dart b/thermion_dart/lib/thermion_dart/compatibility/web/interop/filament_viewer_wasm.dart index cbca9454..e0921ed7 100644 --- a/thermion_dart/lib/thermion_dart/compatibility/web/interop/filament_viewer_wasm.dart +++ b/thermion_dart/lib/thermion_dart/compatibility/web/interop/filament_viewer_wasm.dart @@ -3,6 +3,7 @@ import 'dart:js_interop_unsafe'; import 'dart:math'; import 'dart:typed_data' as td; import 'dart:typed_data'; +import 'package:thermion_dart/thermion_dart/scene.dart'; import 'package:web/web.dart'; import 'package:animation_tools_dart/animation_tools_dart.dart'; import 'package:thermion_dart/thermion_dart/thermion_viewer.dart'; @@ -62,7 +63,6 @@ class ThermionViewerFFIWasm implements ThermionViewer { ["void*".toJS, "void*".toJS, "void*".toJS, "string".toJS].toJS, [context, loader, null, uberArchivePath?.toJS].toJS, null) as JSBigInt; - print("Created viewer"); await createSwapChain(width, height); _updateViewportAndCameraProjection(width, height, 1.0); _sceneManager = _module.ccall("get_scene_manager", "void*", @@ -84,7 +84,6 @@ class ThermionViewerFFIWasm implements ThermionViewer { [_viewer!].toJS, null); } - @override void _updateViewportAndCameraProjection( int width, int height, double scaleFactor) { _module.ccall( @@ -110,10 +109,28 @@ class ThermionViewerFFIWasm implements ThermionViewer { @override Future dispose() async { + if (_viewer == null) { + // we've already cleaned everything up, ignore the call to dispose + return; + } + await setRendering(false); + await clearEntities(); + await clearLights(); + _destroyViewer(); + + _sceneManager = null; + _viewer = null; + + for (final callback in _onDispose) { + await callback.call(); + } + _onDispose.clear(); + + } + + void _destroyViewer() { _module.ccall("destroy_filament_viewer", "void", ["void*".toJS].toJS, [_viewer].toJS, null); - _initialized = false; - _viewer = null; } @override @@ -956,7 +973,6 @@ class ThermionViewerFFIWasm implements ThermionViewer { } @override - // TODO: implement scene Scene get scene => throw UnimplementedError(); @override @@ -1444,4 +1460,13 @@ class ThermionViewerFFIWasm implements ThermionViewer { // TODO: implement zoomUpdate throw UnimplementedError(); } + + final _onDispose = []; + + /// + /// + /// + void onDispose(Future Function() callback) { + _onDispose.add(callback); + } } From cb17bf0ffd8600d9e16d5de37a2051e7d64270b4 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 13:32:03 +0800 Subject: [PATCH 24/51] increment version number for thermion_flutter --- thermion_flutter/thermion_flutter/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thermion_flutter/thermion_flutter/pubspec.yaml b/thermion_flutter/thermion_flutter/pubspec.yaml index e53a562d..a5454dd9 100644 --- a/thermion_flutter/thermion_flutter/pubspec.yaml +++ b/thermion_flutter/thermion_flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: thermion_flutter description: Flutter plugin for 3D rendering with the Thermion toolkit. -version: 0.0.4-pre +version: 0.1.0-pre homepage: https://docs.page/nmfisher/thermion repository: https://github.com/nmfisher/thermion From ae15f2114e73eb037f6f71410c4faa7e7a393905 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 13:32:16 +0800 Subject: [PATCH 25/51] update quickstart iOS project --- examples/flutter/quickstart/ios/Podfile | 2 +- examples/flutter/quickstart/ios/Podfile.lock | 22 +++++ .../ios/Runner.xcodeproj/project.pbxproj | 97 +++++++++++++++++++ .../contents.xcworkspacedata | 3 + 4 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 examples/flutter/quickstart/ios/Podfile.lock diff --git a/examples/flutter/quickstart/ios/Podfile b/examples/flutter/quickstart/ios/Podfile index d97f17e2..3e44f9c6 100644 --- a/examples/flutter/quickstart/ios/Podfile +++ b/examples/flutter/quickstart/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '12.0' +platform :ios, '13.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/examples/flutter/quickstart/ios/Podfile.lock b/examples/flutter/quickstart/ios/Podfile.lock new file mode 100644 index 00000000..81dd9d63 --- /dev/null +++ b/examples/flutter/quickstart/ios/Podfile.lock @@ -0,0 +1,22 @@ +PODS: + - Flutter (1.0.0) + - thermion_flutter (0.0.1): + - Flutter + +DEPENDENCIES: + - Flutter (from `Flutter`) + - thermion_flutter (from `.symlinks/plugins/thermion_flutter/ios`) + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + thermion_flutter: + :path: ".symlinks/plugins/thermion_flutter/ios" + +SPEC CHECKSUMS: + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + thermion_flutter: c965e09831858465a1a8df59ff97e40a4d002773 + +PODFILE CHECKSUM: a57f30d18f102dd3ce366b1d62a55ecbef2158e5 + +COCOAPODS: 1.15.2 diff --git a/examples/flutter/quickstart/ios/Runner.xcodeproj/project.pbxproj b/examples/flutter/quickstart/ios/Runner.xcodeproj/project.pbxproj index 8f6ac5fd..313884be 100644 --- a/examples/flutter/quickstart/ios/Runner.xcodeproj/project.pbxproj +++ b/examples/flutter/quickstart/ios/Runner.xcodeproj/project.pbxproj @@ -14,6 +14,8 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + F657CCCFB062AF56C7F1E15C /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D988E66CA93AB1163F3B4F2 /* Pods_RunnerTests.framework */; }; + F8DC37C0013EE3DC8802435E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B0831D015898C7047513698C /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -42,12 +44,17 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 31D54E0731320192F7631461 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3680A9DFE5EC3E59C051B544 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 3D988E66CA93AB1163F3B4F2 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 704E1C99CAABFB056933D88F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 8C0FE5C77038897D844C5D42 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -55,6 +62,9 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9CD72080C199DAE3518D4C3D /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + B0831D015898C7047513698C /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BA68F2C1572D8BC92CC4C4FF /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -62,12 +72,30 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + F8DC37C0013EE3DC8802435E /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FA72758808529BDB8770545F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F657CCCFB062AF56C7F1E15C /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 0DB20859A61D425430A5D097 /* Frameworks */ = { + isa = PBXGroup; + children = ( + B0831D015898C7047513698C /* Pods_Runner.framework */, + 3D988E66CA93AB1163F3B4F2 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 331C8082294A63A400263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( @@ -76,6 +104,20 @@ path = RunnerTests; sourceTree = ""; }; + 4A129A4D3EB41F9DE0778AAC /* Pods */ = { + isa = PBXGroup; + children = ( + 704E1C99CAABFB056933D88F /* Pods-Runner.debug.xcconfig */, + 3680A9DFE5EC3E59C051B544 /* Pods-Runner.release.xcconfig */, + 8C0FE5C77038897D844C5D42 /* Pods-Runner.profile.xcconfig */, + 31D54E0731320192F7631461 /* Pods-RunnerTests.debug.xcconfig */, + BA68F2C1572D8BC92CC4C4FF /* Pods-RunnerTests.release.xcconfig */, + 9CD72080C199DAE3518D4C3D /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -94,6 +136,8 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, + 4A129A4D3EB41F9DE0778AAC /* Pods */, + 0DB20859A61D425430A5D097 /* Frameworks */, ); sourceTree = ""; }; @@ -128,8 +172,10 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( + 78F05AF37D9E628BE6E89ABD /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, + FA72758808529BDB8770545F /* Frameworks */, ); buildRules = ( ); @@ -145,6 +191,7 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + C7C2718364AC4E2451407065 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, @@ -238,6 +285,28 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 78F05AF37D9E628BE6E89ABD /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -253,6 +322,28 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + C7C2718364AC4E2451407065 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -365,6 +456,7 @@ DEVELOPMENT_TEAM = TM2B4SJXNJ; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -379,6 +471,7 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 31D54E0731320192F7631461 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -396,6 +489,7 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = BA68F2C1572D8BC92CC4C4FF /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -411,6 +505,7 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 9CD72080C199DAE3518D4C3D /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -545,6 +640,7 @@ DEVELOPMENT_TEAM = TM2B4SJXNJ; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -568,6 +664,7 @@ DEVELOPMENT_TEAM = TM2B4SJXNJ; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/examples/flutter/quickstart/ios/Runner.xcworkspace/contents.xcworkspacedata b/examples/flutter/quickstart/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16..21a3cc14 100644 --- a/examples/flutter/quickstart/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/examples/flutter/quickstart/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + From e823083e86470a9f71ed812d352df00278c8c53b Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 13:32:40 +0800 Subject: [PATCH 26/51] update quickstart to use new API --- examples/flutter/quickstart/lib/main.dart | 70 ++++++++++++++--------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/examples/flutter/quickstart/lib/main.dart b/examples/flutter/quickstart/lib/main.dart index 9c8f5473..58bb4c45 100644 --- a/examples/flutter/quickstart/lib/main.dart +++ b/examples/flutter/quickstart/lib/main.dart @@ -4,7 +4,6 @@ import 'package:thermion_flutter/thermion_flutter.dart'; import 'package:vector_math/vector_math_64.dart' as v; import 'dart:math'; - void main() { runApp(const MyApp()); } @@ -35,40 +34,59 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { bool _loaded = false; - late ThermionFlutterPlugin _thermionFlutterPlugin; - late Future _thermionViewer; + ThermionViewer? _thermionViewer; @override void initState() { super.initState(); - _thermionFlutterPlugin = ThermionFlutterPlugin(); - _thermionViewer = _thermionFlutterPlugin.initialize(); + } + + ThermionFlutterTexture? _texture; + + Future _load() async { + var viewer = await ThermionFlutterPlugin.createViewer(); + _thermionViewer = viewer; + _thermionViewer!.loadSkybox("assets/default_env_skybox.ktx"); + _thermionViewer!.loadGlb("assets/cube.glb"); + + _thermionViewer!.setCameraPosition(0, 1, 10); + _thermionViewer!.setCameraRotation( + v.Quaternion.axisAngle(v.Vector3(1, 0, 0), -30 / 180 * pi) * + v.Quaternion.axisAngle(v.Vector3(0, 1, 0), 15 / 180 * pi)); + _thermionViewer!.addLight(LightType.SUN, 7500, 50000, 0, 0, 0, 1, -1, -1); + _thermionViewer!.setRendering(true); + _loaded = true; + + setState(() {}); + } + + Future _unload() async { + await ThermionFlutterPlugin.destroyTexture(_texture!); + var viewer = _thermionViewer!; + _thermionViewer = null; + setState(() {}); + await viewer.dispose(); + _loaded = false; + setState(() {}); + } + + Widget _loadButton() { + return Center( + child: ElevatedButton(child: const Text("Load"), onPressed: _load)); + } + + Widget _unloadButton() { + return Center( + child: ElevatedButton(child: const Text("Unload"), onPressed: _unload)); } @override Widget build(BuildContext context) { return Stack(children: [ - Positioned.fill(child: ThermionWidget(plugin: _thermionFlutterPlugin)), - if (!_loaded) - Center( - child: ElevatedButton( - child: const Text("Load"), - onPressed: () async { - var viewer = await _thermionViewer; - await viewer.loadSkybox("assets/default_env_skybox.ktx"); - await viewer.loadGlb("assets/cube.glb"); - - await viewer.setCameraPosition(0, 1, 10); - await viewer.setCameraRotation(v.Quaternion.axisAngle( - v.Vector3(1, 0, 0), -30 / 180 * pi) * - v.Quaternion.axisAngle( - v.Vector3(0, 1, 0), 15 / 180 * pi)); - await viewer.addLight( - LightType.SUN, 7500, 50000, 0, 0, 0, 1, -1, -1); - await viewer.setRendering(true); - _loaded = true; - setState(() {}); - })) + if (_thermionViewer != null) + Positioned.fill(child: ThermionWidget(viewer: _thermionViewer!)), + if (!_loaded) _loadButton(), + if (_loaded) _unloadButton(), ]); } } From 735612af6612dff7021a4752b7e10b35411ce2e7 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 13:39:43 +0800 Subject: [PATCH 27/51] bump pubspec dependency versions --- thermion_dart/pubspec.yaml | 2 +- thermion_flutter/thermion_flutter_ffi/pubspec.yaml | 6 +++--- .../thermion_flutter_platform_interface/pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/thermion_dart/pubspec.yaml b/thermion_dart/pubspec.yaml index cde4cec3..24f387e5 100644 --- a/thermion_dart/pubspec.yaml +++ b/thermion_dart/pubspec.yaml @@ -1,6 +1,6 @@ name: thermion_dart description: 3D rendering toolkit for Dart. -version: 0.0.4 +version: 0.1.0 homepage: https://docs.page/nmfisher/thermion repository: https://github.com/nmfisher/thermion diff --git a/thermion_flutter/thermion_flutter_ffi/pubspec.yaml b/thermion_flutter/thermion_flutter_ffi/pubspec.yaml index f68e2118..3ad22514 100644 --- a/thermion_flutter/thermion_flutter_ffi/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_ffi/pubspec.yaml @@ -1,7 +1,7 @@ name: thermion_flutter_ffi description: An FFI interface for the thermion_flutter plugin (all platforms except web). repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter -version: 0.0.1 +version: 0.1.0 environment: sdk: ">=3.3.0 <4.0.0" @@ -22,8 +22,8 @@ dependencies: flutter: sdk: flutter plugin_platform_interface: ^2.1.0 - thermion_flutter_platform_interface: ^0.0.1-pre - thermion_dart: ^0.0.1-pre2 + thermion_flutter_platform_interface: ^0.1.0 + thermion_dart: ^0.1.0 dev_dependencies: flutter_test: diff --git a/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml b/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml index d2ef55ab..5f170f65 100644 --- a/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml @@ -1,7 +1,7 @@ name: thermion_flutter_platform_interface description: A common platform interface for the thermion_flutter plugin. repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter -version: 0.0.1 +version: 0.1.0 environment: sdk: ">=3.3.0 <4.0.0" From 846f45bb63e1c8b926a5cb02bc698d46c30ffcd7 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 16:38:14 +0800 Subject: [PATCH 28/51] don't destroy dummy image texture if clearBackgroundImage has been called without setBackgroundImage having been called first --- thermion_dart/native/include/FilamentViewer.hpp | 1 + thermion_dart/native/src/FilamentViewer.cpp | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/thermion_dart/native/include/FilamentViewer.hpp b/thermion_dart/native/include/FilamentViewer.hpp index 52cc7ff6..32f59744 100644 --- a/thermion_dart/native/include/FilamentViewer.hpp +++ b/thermion_dart/native/include/FilamentViewer.hpp @@ -211,6 +211,7 @@ namespace thermion_filament uint32_t _imageWidth = 0; mat4f _imageScale; Texture *_imageTexture = nullptr; + Texture *_dummyImageTexture = nullptr; utils::Entity _imageEntity; VertexBuffer *_imageVb = nullptr; IndexBuffer *_imageIb = nullptr; diff --git a/thermion_dart/native/src/FilamentViewer.cpp b/thermion_dart/native/src/FilamentViewer.cpp index ad26485f..fbc091c0 100644 --- a/thermion_dart/native/src/FilamentViewer.cpp +++ b/thermion_dart/native/src/FilamentViewer.cpp @@ -214,7 +214,7 @@ namespace thermion_filament Log("Created scene maager"); - _imageTexture = Texture::Builder() + _dummyImageTexture = Texture::Builder() .width(1) .height(1) .levels(0x01) @@ -229,7 +229,7 @@ namespace thermion_filament .build(*_engine); _imageMaterial->setDefaultParameter("showImage", 0); _imageMaterial->setDefaultParameter("backgroundColor", RgbaType::sRGB, float4(1.0f, 1.0f, 1.0f, 0.0f)); - _imageMaterial->setDefaultParameter("image", _imageTexture, _imageSampler); + _imageMaterial->setDefaultParameter("image", _dummyImageTexture, _imageSampler); } catch (...) { @@ -549,6 +549,7 @@ namespace thermion_filament void FilamentViewer::clearBackgroundImage() { + _imageMaterial->setDefaultParameter("image", _dummyImageTexture, _imageSampler); _imageMaterial->setDefaultParameter("showImage", 0); if (_imageTexture) { From 013730b10851b341d44edc64df0ac6f2c60cb96b Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 16:39:10 +0800 Subject: [PATCH 29/51] destroy existing texture if it exists but size does match requested dimensions when createTexture is called --- .../lib/thermion_flutter_ffi.dart | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart b/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart index 77cdcea8..9a930778 100644 --- a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart +++ b/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart @@ -109,10 +109,15 @@ class ThermionFlutterFFI extends ThermionFlutterPlatform { if (_textures.length > 1) { throw Exception("Multiple textures not yet supported"); - } else if (_textures.length == 1 && - _textures.first.height == height && - _textures.first.width == width) { - return _textures.first; + } else if (_textures.length == 1) { + if (_textures.first.height == height && _textures.first.width == width) { + return _textures.first; + } else { + await _viewer!.setRendering(false); + await _viewer!.destroySwapChain(); + await destroyTexture(_textures.first); + _textures.clear(); + } } _creatingTexture = true; @@ -146,7 +151,8 @@ class ThermionFlutterFFI extends ThermionFlutterPlatform { width.toDouble(), height.toDouble(), texture.hardwareTextureId!); } - await _viewer?.updateViewportAndCameraProjection(width.toDouble(), height.toDouble()); + await _viewer?.updateViewportAndCameraProjection( + width.toDouble(), height.toDouble()); _viewer?.render(); _creatingTexture = false; @@ -155,13 +161,13 @@ class ThermionFlutterFFI extends ThermionFlutterPlatform { return texture; } - /// /// Destroy a texture and clean up the texture cache (if applicable). /// Future destroyTexture(ThermionFlutterTexture texture) async { if (_creatingTexture || _destroyingTexture) { - throw Exception("Cannot destroy texture while concurrent call to createTexture/destroyTexture has not completed"); + throw Exception( + "Cannot destroy texture while concurrent call to createTexture/destroyTexture has not completed"); } _destroyingTexture = true; _textures.remove(texture); From b833b2d28848a01b49be15a55863757fdf350819 Mon Sep 17 00:00:00 2001 From: Hannes Date: Wed, 19 Jun 2024 13:17:43 +0200 Subject: [PATCH 30/51] Add melos --- .gitignore | 1 + melos.yaml | 5 + melos_thermion_workspace.iml | 12 + pubspec.lock | 341 ++++++++++++++++++ pubspec.yaml | 6 + thermion_dart/melos_thermion_dart.iml | 16 + thermion_flutter/thermion_dart | 1 - .../melos_thermion_flutter.iml | 29 ++ .../melos_thermion_flutter_ffi.iml | 29 ++ ...os_thermion_flutter_platform_interface.iml | 29 ++ .../melos_thermion_flutter_web.iml | 29 ++ 11 files changed, 497 insertions(+), 1 deletion(-) create mode 100644 melos.yaml create mode 100644 melos_thermion_workspace.iml create mode 100644 pubspec.lock create mode 100644 pubspec.yaml create mode 100644 thermion_dart/melos_thermion_dart.iml delete mode 120000 thermion_flutter/thermion_dart create mode 100644 thermion_flutter/thermion_flutter/melos_thermion_flutter.iml create mode 100644 thermion_flutter/thermion_flutter_ffi/melos_thermion_flutter_ffi.iml create mode 100644 thermion_flutter/thermion_flutter_platform_interface/melos_thermion_flutter_platform_interface.iml create mode 100644 thermion_flutter/thermion_flutter_web/melos_thermion_flutter_web.iml diff --git a/.gitignore b/.gitignore index 12aef55c..1f7c6e9e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ .DS_Store **/*/build **/*/pubspec.lock +pubspec_overrides.yaml diff --git a/melos.yaml b/melos.yaml new file mode 100644 index 00000000..dfa7fc3e --- /dev/null +++ b/melos.yaml @@ -0,0 +1,5 @@ +name: thermion_workspace + +packages: + - thermion_dart + - thermion_flutter/** diff --git a/melos_thermion_workspace.iml b/melos_thermion_workspace.iml new file mode 100644 index 00000000..96815595 --- /dev/null +++ b/melos_thermion_workspace.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 00000000..0f972b96 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,341 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + ansi_styles: + dependency: transitive + description: + name: ansi_styles + sha256: "9c656cc12b3c27b17dd982b2cc5c0cfdfbdabd7bc8f3ae5e8542d9867b47ce8a" + url: "https://pub.dev" + source: hosted + version: "0.3.2+1" + args: + dependency: transitive + description: + name: args + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + url: "https://pub.dev" + source: hosted + version: "2.5.0" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + charcode: + dependency: transitive + description: + name: charcode + sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 + url: "https://pub.dev" + source: hosted + version: "1.3.1" + cli_launcher: + dependency: transitive + description: + name: cli_launcher + sha256: "5e7e0282b79e8642edd6510ee468ae2976d847a0a29b3916e85f5fa1bfe24005" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 + url: "https://pub.dev" + source: hosted + version: "0.4.1" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + url: "https://pub.dev" + source: hosted + version: "1.19.0" + conventional_commit: + dependency: transitive + description: + name: conventional_commit + sha256: dec15ad1118f029c618651a4359eb9135d8b88f761aa24e4016d061cd45948f2 + url: "https://pub.dev" + source: hosted + version: "0.6.0+1" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + graphs: + dependency: transitive + description: + name: graphs + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + url: "https://pub.dev" + source: hosted + version: "2.3.1" + http: + dependency: transitive + description: + name: http + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "40f592dd352890c3b60fec1b68e786cefb9603e05ff303dbc4dda49b304ecdf4" + url: "https://pub.dev" + source: hosted + version: "4.1.0" + intl: + dependency: transitive + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" + source: hosted + version: "0.19.0" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + melos: + dependency: "direct dev" + description: + name: melos + sha256: a3f06ed871e0348cb99909ad5ddf5f8b53cc61d894c302b5417d2db1ee7ec381 + url: "https://pub.dev" + source: hosted + version: "6.1.0" + meta: + dependency: transitive + description: + name: meta + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + url: "https://pub.dev" + source: hosted + version: "1.15.0" + mustache_template: + dependency: transitive + description: + name: mustache_template + sha256: a46e26f91445bfb0b60519be280555b06792460b27b19e2b19ad5b9740df5d1c + url: "https://pub.dev" + source: hosted + version: "2.0.0" + path: + dependency: transitive + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + platform: + dependency: transitive + description: + name: platform + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" + url: "https://pub.dev" + source: hosted + version: "3.1.5" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + process: + dependency: transitive + description: + name: process + sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" + url: "https://pub.dev" + source: hosted + version: "5.0.2" + prompts: + dependency: transitive + description: + name: prompts + sha256: "3773b845e85a849f01e793c4fc18a45d52d7783b4cb6c0569fad19f9d0a774a1" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + pub_updater: + dependency: transitive + description: + name: pub_updater + sha256: "54e8dc865349059ebe7f163d6acce7c89eb958b8047e6d6e80ce93b13d7c9e60" + url: "https://pub.dev" + source: hosted + version: "0.4.0" + pubspec: + dependency: transitive + description: + name: pubspec + sha256: f534a50a2b4d48dc3bc0ec147c8bd7c304280fff23b153f3f11803c4d49d927e + url: "https://pub.dev" + source: hosted + version: "2.3.0" + quiver: + dependency: transitive + description: + name: quiver + sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 + url: "https://pub.dev" + source: hosted + version: "3.2.1" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + url: "https://pub.dev" + source: hosted + version: "0.7.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + uri: + dependency: transitive + description: + name: uri + sha256: "889eea21e953187c6099802b7b4cf5219ba8f3518f604a1033064d45b1b8268a" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + web: + dependency: transitive + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" + yaml_edit: + dependency: transitive + description: + name: yaml_edit + sha256: e9c1a3543d2da0db3e90270dbb1e4eebc985ee5e3ffe468d83224472b2194a5f + url: "https://pub.dev" + source: hosted + version: "2.2.1" +sdks: + dart: ">=3.4.0 <4.0.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 00000000..3ab56ff1 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,6 @@ +name: thermion_workspace + +environment: + sdk: '>=3.0.0 <4.0.0' +dev_dependencies: + melos: ^6.1.0 diff --git a/thermion_dart/melos_thermion_dart.iml b/thermion_dart/melos_thermion_dart.iml new file mode 100644 index 00000000..389d07a1 --- /dev/null +++ b/thermion_dart/melos_thermion_dart.iml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/thermion_flutter/thermion_dart b/thermion_flutter/thermion_dart deleted file mode 120000 index da4db14f..00000000 --- a/thermion_flutter/thermion_dart +++ /dev/null @@ -1 +0,0 @@ -../thermion_dart \ No newline at end of file diff --git a/thermion_flutter/thermion_flutter/melos_thermion_flutter.iml b/thermion_flutter/thermion_flutter/melos_thermion_flutter.iml new file mode 100644 index 00000000..9fc8ce79 --- /dev/null +++ b/thermion_flutter/thermion_flutter/melos_thermion_flutter.iml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/thermion_flutter/thermion_flutter_ffi/melos_thermion_flutter_ffi.iml b/thermion_flutter/thermion_flutter_ffi/melos_thermion_flutter_ffi.iml new file mode 100644 index 00000000..9fc8ce79 --- /dev/null +++ b/thermion_flutter/thermion_flutter_ffi/melos_thermion_flutter_ffi.iml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/thermion_flutter/thermion_flutter_platform_interface/melos_thermion_flutter_platform_interface.iml b/thermion_flutter/thermion_flutter_platform_interface/melos_thermion_flutter_platform_interface.iml new file mode 100644 index 00000000..9fc8ce79 --- /dev/null +++ b/thermion_flutter/thermion_flutter_platform_interface/melos_thermion_flutter_platform_interface.iml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/thermion_flutter/thermion_flutter_web/melos_thermion_flutter_web.iml b/thermion_flutter/thermion_flutter_web/melos_thermion_flutter_web.iml new file mode 100644 index 00000000..9fc8ce79 --- /dev/null +++ b/thermion_flutter/thermion_flutter_web/melos_thermion_flutter_web.iml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 842c6b1581e38eca00cfd35620b9122a21d4bce8 Mon Sep 17 00:00:00 2001 From: Hannes Date: Thu, 20 Jun 2024 09:07:53 +0200 Subject: [PATCH 31/51] Remove unnecessary files from git --- .gitignore | 2 + melos_thermion_workspace.iml | 12 - pubspec.lock | 341 ------------------ thermion_dart/melos_thermion_dart.iml | 16 - .../melos_thermion_flutter.iml | 29 -- .../melos_thermion_flutter_ffi.iml | 29 -- ...os_thermion_flutter_platform_interface.iml | 29 -- .../melos_thermion_flutter_web.iml | 29 -- 8 files changed, 2 insertions(+), 485 deletions(-) delete mode 100644 melos_thermion_workspace.iml delete mode 100644 pubspec.lock delete mode 100644 thermion_dart/melos_thermion_dart.iml delete mode 100644 thermion_flutter/thermion_flutter/melos_thermion_flutter.iml delete mode 100644 thermion_flutter/thermion_flutter_ffi/melos_thermion_flutter_ffi.iml delete mode 100644 thermion_flutter/thermion_flutter_platform_interface/melos_thermion_flutter_platform_interface.iml delete mode 100644 thermion_flutter/thermion_flutter_web/melos_thermion_flutter_web.iml diff --git a/.gitignore b/.gitignore index 1f7c6e9e..dad2f3af 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ **/*/build **/*/pubspec.lock pubspec_overrides.yaml +/pubspec.lock +*.iml diff --git a/melos_thermion_workspace.iml b/melos_thermion_workspace.iml deleted file mode 100644 index 96815595..00000000 --- a/melos_thermion_workspace.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index 0f972b96..00000000 --- a/pubspec.lock +++ /dev/null @@ -1,341 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - ansi_styles: - dependency: transitive - description: - name: ansi_styles - sha256: "9c656cc12b3c27b17dd982b2cc5c0cfdfbdabd7bc8f3ae5e8542d9867b47ce8a" - url: "https://pub.dev" - source: hosted - version: "0.3.2+1" - args: - dependency: transitive - description: - name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" - url: "https://pub.dev" - source: hosted - version: "2.5.0" - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - charcode: - dependency: transitive - description: - name: charcode - sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 - url: "https://pub.dev" - source: hosted - version: "1.3.1" - cli_launcher: - dependency: transitive - description: - name: cli_launcher - sha256: "5e7e0282b79e8642edd6510ee468ae2976d847a0a29b3916e85f5fa1bfe24005" - url: "https://pub.dev" - source: hosted - version: "0.3.1" - cli_util: - dependency: transitive - description: - name: cli_util - sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 - url: "https://pub.dev" - source: hosted - version: "0.4.1" - clock: - dependency: transitive - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - collection: - dependency: transitive - description: - name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf - url: "https://pub.dev" - source: hosted - version: "1.19.0" - conventional_commit: - dependency: transitive - description: - name: conventional_commit - sha256: dec15ad1118f029c618651a4359eb9135d8b88f761aa24e4016d061cd45948f2 - url: "https://pub.dev" - source: hosted - version: "0.6.0+1" - file: - dependency: transitive - description: - name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - glob: - dependency: transitive - description: - name: glob - sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - graphs: - dependency: transitive - description: - name: graphs - sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 - url: "https://pub.dev" - source: hosted - version: "2.3.1" - http: - dependency: transitive - description: - name: http - sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" - url: "https://pub.dev" - source: hosted - version: "1.2.1" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "40f592dd352890c3b60fec1b68e786cefb9603e05ff303dbc4dda49b304ecdf4" - url: "https://pub.dev" - source: hosted - version: "4.1.0" - intl: - dependency: transitive - description: - name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf - url: "https://pub.dev" - source: hosted - version: "0.19.0" - io: - dependency: transitive - description: - name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" - url: "https://pub.dev" - source: hosted - version: "1.0.4" - json_annotation: - dependency: transitive - description: - name: json_annotation - sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" - url: "https://pub.dev" - source: hosted - version: "4.9.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb - url: "https://pub.dev" - source: hosted - version: "0.12.16+1" - melos: - dependency: "direct dev" - description: - name: melos - sha256: a3f06ed871e0348cb99909ad5ddf5f8b53cc61d894c302b5417d2db1ee7ec381 - url: "https://pub.dev" - source: hosted - version: "6.1.0" - meta: - dependency: transitive - description: - name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 - url: "https://pub.dev" - source: hosted - version: "1.15.0" - mustache_template: - dependency: transitive - description: - name: mustache_template - sha256: a46e26f91445bfb0b60519be280555b06792460b27b19e2b19ad5b9740df5d1c - url: "https://pub.dev" - source: hosted - version: "2.0.0" - path: - dependency: transitive - description: - name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" - url: "https://pub.dev" - source: hosted - version: "1.9.0" - platform: - dependency: transitive - description: - name: platform - sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" - url: "https://pub.dev" - source: hosted - version: "3.1.5" - pool: - dependency: transitive - description: - name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.dev" - source: hosted - version: "1.5.1" - process: - dependency: transitive - description: - name: process - sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" - url: "https://pub.dev" - source: hosted - version: "5.0.2" - prompts: - dependency: transitive - description: - name: prompts - sha256: "3773b845e85a849f01e793c4fc18a45d52d7783b4cb6c0569fad19f9d0a774a1" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - pub_updater: - dependency: transitive - description: - name: pub_updater - sha256: "54e8dc865349059ebe7f163d6acce7c89eb958b8047e6d6e80ce93b13d7c9e60" - url: "https://pub.dev" - source: hosted - version: "0.4.0" - pubspec: - dependency: transitive - description: - name: pubspec - sha256: f534a50a2b4d48dc3bc0ec147c8bd7c304280fff23b153f3f11803c4d49d927e - url: "https://pub.dev" - source: hosted - version: "2.3.0" - quiver: - dependency: transitive - description: - name: quiver - sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 - url: "https://pub.dev" - source: hosted - version: "3.2.1" - source_span: - dependency: transitive - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" - url: "https://pub.dev" - source: hosted - version: "1.11.1" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.dev" - source: hosted - version: "2.1.2" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" - url: "https://pub.dev" - source: hosted - version: "0.7.2" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" - uri: - dependency: transitive - description: - name: uri - sha256: "889eea21e953187c6099802b7b4cf5219ba8f3518f604a1033064d45b1b8268a" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - web: - dependency: transitive - description: - name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" - url: "https://pub.dev" - source: hosted - version: "0.5.1" - yaml: - dependency: transitive - description: - name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" - url: "https://pub.dev" - source: hosted - version: "3.1.2" - yaml_edit: - dependency: transitive - description: - name: yaml_edit - sha256: e9c1a3543d2da0db3e90270dbb1e4eebc985ee5e3ffe468d83224472b2194a5f - url: "https://pub.dev" - source: hosted - version: "2.2.1" -sdks: - dart: ">=3.4.0 <4.0.0" diff --git a/thermion_dart/melos_thermion_dart.iml b/thermion_dart/melos_thermion_dart.iml deleted file mode 100644 index 389d07a1..00000000 --- a/thermion_dart/melos_thermion_dart.iml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/thermion_flutter/thermion_flutter/melos_thermion_flutter.iml b/thermion_flutter/thermion_flutter/melos_thermion_flutter.iml deleted file mode 100644 index 9fc8ce79..00000000 --- a/thermion_flutter/thermion_flutter/melos_thermion_flutter.iml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/thermion_flutter/thermion_flutter_ffi/melos_thermion_flutter_ffi.iml b/thermion_flutter/thermion_flutter_ffi/melos_thermion_flutter_ffi.iml deleted file mode 100644 index 9fc8ce79..00000000 --- a/thermion_flutter/thermion_flutter_ffi/melos_thermion_flutter_ffi.iml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/thermion_flutter/thermion_flutter_platform_interface/melos_thermion_flutter_platform_interface.iml b/thermion_flutter/thermion_flutter_platform_interface/melos_thermion_flutter_platform_interface.iml deleted file mode 100644 index 9fc8ce79..00000000 --- a/thermion_flutter/thermion_flutter_platform_interface/melos_thermion_flutter_platform_interface.iml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/thermion_flutter/thermion_flutter_web/melos_thermion_flutter_web.iml b/thermion_flutter/thermion_flutter_web/melos_thermion_flutter_web.iml deleted file mode 100644 index 9fc8ce79..00000000 --- a/thermion_flutter/thermion_flutter_web/melos_thermion_flutter_web.iml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From d3891e481e7bb01de8eb65234a005aed7b58bed0 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 16:48:36 +0800 Subject: [PATCH 32/51] fix log dir creation in build.dart --- thermion_dart/hook/build.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/thermion_dart/hook/build.dart b/thermion_dart/hook/build.dart index 6f7ce0ca..c5a0b361 100644 --- a/thermion_dart/hook/build.dart +++ b/thermion_dart/hook/build.dart @@ -7,9 +7,9 @@ import 'package:native_toolchain_c/native_toolchain_c.dart'; void main(List args) async { await build(args, (config, output) async { var logDir = Directory( - "${config.packageRoot.toFilePath()}/.dart_tool/thermion_dart/log/"); + "${config.packageRoot.toFilePath()}.dart_tool/thermion_dart/log/"); if (!logDir.existsSync()) { - logDir.createSync(); + logDir.createSync(recursive: true); } var logFile = File(logDir.path + "/build.log"); From c6c918907acd02afba104765c2379f15ae16302c Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 19 Jun 2024 18:25:38 +0800 Subject: [PATCH 33/51] rename web interop classes --- .../compatibility/web/compatibility_ffi.dart | 118 ++++++++++++++++++ ....dart => thermion_viewer_dart_bridge.dart} | 6 +- ...viewer_js.dart => thermion_viewer_js.dart} | 6 +- ...er_wasm.dart => thermion_viewer_wasm.dart} | 15 ++- 4 files changed, 131 insertions(+), 14 deletions(-) create mode 100644 thermion_dart/lib/thermion_dart/compatibility/web/compatibility_ffi.dart rename thermion_dart/lib/thermion_dart/compatibility/web/interop/{filament_viewer_js_dart_bridge.dart => thermion_viewer_dart_bridge.dart} (99%) rename thermion_dart/lib/thermion_dart/compatibility/web/interop/{filament_viewer_js.dart => thermion_viewer_js.dart} (99%) rename thermion_dart/lib/thermion_dart/compatibility/web/interop/{filament_viewer_wasm.dart => thermion_viewer_wasm.dart} (99%) diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/compatibility_ffi.dart b/thermion_dart/lib/thermion_dart/compatibility/web/compatibility_ffi.dart new file mode 100644 index 00000000..f8da247c --- /dev/null +++ b/thermion_dart/lib/thermion_dart/compatibility/web/compatibility_ffi.dart @@ -0,0 +1,118 @@ +import 'dart:async'; +import 'dart:js_interop'; +import 'package:thermion_dart/thermion_dart/compatibility/web/interop.dart'; + +import "allocator.dart"; + +export "allocator.dart"; +export "thermion_dart.g.dart"; + +export 'package:ffi/ffi.dart' hide StringUtf8Pointer, Utf8Pointer; +export 'dart:ffi' + hide + Uint8Pointer, + FloatPointer, + DoublePointer, + Int32Pointer, + Int64Pointer, + PointerPointer, + Allocator; + +const allocator = Allocator(); + +@AbiSpecificIntegerMapping({ + Abi.androidArm: Uint8(), + Abi.androidArm64: Uint8(), + Abi.androidIA32: Int8(), + Abi.androidX64: Int8(), + Abi.androidRiscv64: Uint8(), + Abi.fuchsiaArm64: Uint8(), + Abi.fuchsiaX64: Int8(), + Abi.fuchsiaRiscv64: Uint8(), + Abi.iosArm: Int8(), + Abi.iosArm64: Int8(), + Abi.iosX64: Int8(), + Abi.linuxArm: Uint8(), + Abi.linuxArm64: Uint8(), + Abi.linuxIA32: Int8(), + Abi.linuxX64: Int8(), + Abi.linuxRiscv32: Uint8(), + Abi.linuxRiscv64: Uint8(), + Abi.macosArm64: Int8(), + Abi.macosX64: Int8(), + Abi.windowsArm64: Int8(), + Abi.windowsIA32: Int8(), + Abi.windowsX64: Int8(), +}) +final class FooChar extends AbiSpecificInteger { + const FooChar(); +} + +class Compatibility { + final _foo = FooChar(); +} + +Future withVoidCallback( + Function(Pointer>) func) async { + JSArray retVal = createVoidCallback(); + var promise = retVal.toDart[0] as JSPromise; + var fnPtrAddress = retVal.toDart[1] as JSNumber; + var fnPtr = Pointer>.fromAddress( + fnPtrAddress.toDartInt); + func(fnPtr); + await promise.toDart; +} + +Future withVoidPointerCallback( + void Function(Pointer)>>) + func) async { + JSArray retVal = createVoidPointerCallback(); + var promise = retVal.toDart[0] as JSPromise; + + var fnPtrAddress = retVal.toDart[1] as JSNumber; + var fnPtr = Pointer)>>.fromAddress( + fnPtrAddress.toDartInt); + func(fnPtr); + final addr = await promise.toDart; + return addr.toDartInt; +} + +Future withBoolCallback( + Function(Pointer>) func) async { + JSArray retVal = createBoolCallback(); + var promise = retVal.toDart[0] as JSPromise; + + var fnPtrAddress = retVal.toDart[1] as JSNumber; + var fnPtr = Pointer>.fromAddress( + fnPtrAddress.toDartInt); + func(fnPtr); + final addr = await promise.toDart; + return addr.toDart; +} + +Future withIntCallback( + Function(Pointer>) func) async { + JSArray retVal = createBoolCallback(); + var promise = retVal.toDart[0] as JSPromise; + + var fnPtrAddress = retVal.toDart[1] as JSNumber; + var fnPtr = Pointer>.fromAddress( + fnPtrAddress.toDartInt); + func(fnPtr); + final addr = await promise.toDart; + return addr.toDartInt; +} + +Future withCharPtrCallback( + Function(Pointer)>>) + func) async { + JSArray retVal = createVoidPointerCallback(); + var promise = retVal.toDart[0] as JSPromise; + + var fnPtrAddress = retVal.toDart[1] as JSNumber; + var fnPtr = Pointer)>>.fromAddress( + fnPtrAddress.toDartInt); + func(fnPtr); + final addr = await promise.toDart; + return Pointer.fromAddress(addr.toDartInt).toDartString(); +} diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/interop/filament_viewer_js_dart_bridge.dart b/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_dart_bridge.dart similarity index 99% rename from thermion_dart/lib/thermion_dart/compatibility/web/interop/filament_viewer_js_dart_bridge.dart rename to thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_dart_bridge.dart index edffe64c..d3ed6db2 100644 --- a/thermion_dart/lib/thermion_dart/compatibility/web/interop/filament_viewer_js_dart_bridge.dart +++ b/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_dart_bridge.dart @@ -20,14 +20,14 @@ import 'package:vector_math/vector_math_64.dart'; /// the browser console). /// @JSExport() -class ThermionViewerFFIJSDartBridge { +class ThermionViewerJSDartBridge { final ThermionViewer viewer; - ThermionViewerFFIJSDartBridge(this.viewer); + ThermionViewerJSDartBridge(this.viewer); void bind( {String globalPropertyName = "filamentViewer"}) { - var wrapper = createJSInteropWrapper(this) + var wrapper = createJSInteropWrapper(this) as ThermionViewerJSShim; globalContext.setProperty(globalPropertyName.toJS, wrapper); } diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/interop/filament_viewer_js.dart b/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_js.dart similarity index 99% rename from thermion_dart/lib/thermion_dart/compatibility/web/interop/filament_viewer_js.dart rename to thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_js.dart index 928b3bcf..fbd47777 100644 --- a/thermion_dart/lib/thermion_dart/compatibility/web/interop/filament_viewer_js.dart +++ b/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_js.dart @@ -14,15 +14,15 @@ import 'shims/thermion_viewer_js_shim.dart'; /// An [ThermionViewer] implementation that forwards calls to /// a corresponding Javascript shim implementation (see [ThermionViewerJSShim]). /// -class ThermionViewerFFIJS implements ThermionViewer { +class ThermionViewerJS implements ThermionViewer { late final ThermionViewerJSShim _shim; - ThermionViewerFFIJS.fromGlobalProperty(String globalPropertyName) { + ThermionViewerJS.fromGlobalProperty(String globalPropertyName) { this._shim = globalContext.getProperty(globalPropertyName.toJS) as ThermionViewerJSShim; } - ThermionViewerFFIJS(this._shim); + ThermionViewerJS(this._shim); @override Future get initialized async { diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/interop/filament_viewer_wasm.dart b/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_wasm.dart similarity index 99% rename from thermion_dart/lib/thermion_dart/compatibility/web/interop/filament_viewer_wasm.dart rename to thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_wasm.dart index e0921ed7..46d2341b 100644 --- a/thermion_dart/lib/thermion_dart/compatibility/web/interop/filament_viewer_wasm.dart +++ b/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_wasm.dart @@ -34,20 +34,19 @@ extension type _EmscriptenModule(JSObject _) implements JSObject { /// An [ThermionViewer] implementation that forwards calls to /// the (Emscripten-generated) ThermionDart JS module. /// -class ThermionViewerFFIWasm implements ThermionViewer { +class ThermionViewerWasm implements ThermionViewer { late _EmscriptenModule _module; bool _initialized = false; bool _rendering = false; - ThermionViewerFFIWasm() { - _module = window.getProperty<_EmscriptenModule>("df".toJS); + ThermionViewerWasm({String moduleName="thermion_dart"}) { + _module = window.getProperty<_EmscriptenModule>(moduleName.toJS); } - JSBigInt? _viewer; - JSBigInt? _sceneManager; + JSNumber? _viewer; + JSNumber? _sceneManager; - @override Future initialize(int width, int height, {String? uberArchivePath}) async { final context = _module.ccall("thermion_dart_web_create_gl_context", "int", [].toJS, [].toJS, null); @@ -62,11 +61,11 @@ class ThermionViewerFFIWasm implements ThermionViewer { "void*", ["void*".toJS, "void*".toJS, "void*".toJS, "string".toJS].toJS, [context, loader, null, uberArchivePath?.toJS].toJS, - null) as JSBigInt; + null) as JSNumber; await createSwapChain(width, height); _updateViewportAndCameraProjection(width, height, 1.0); _sceneManager = _module.ccall("get_scene_manager", "void*", - ["void*".toJS].toJS, [_viewer!].toJS, null) as JSBigInt; + ["void*".toJS].toJS, [_viewer!].toJS, null) as JSNumber; _initialized = true; } From 0ec7e2af95fe084331435ebf8105a03875f31218 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 21 Jun 2024 11:09:47 +0800 Subject: [PATCH 34/51] refactor: export ThermionViewerWasm for web and hide FFI/WASM version --- thermion_dart/lib/thermion_dart.dart | 2 +- .../compatibility/web/compatibility.dart | 118 -- .../shims/thermion_dart_api_js_shim.dart | 403 ----- .../interop/thermion_viewer_dart_bridge.dart | 16 +- .../web/interop/thermion_viewer_js.dart | 2 +- .../{shims => }/thermion_viewer_js_shim.dart | 0 .../web/interop/thermion_viewer_wasm.dart | 1326 +++++++++++------ thermion_dart/native/web/CMakeLists.txt | 14 +- .../lib/thermion_flutter.dart | 1 - .../lib/thermion_flutter_web.dart | 41 +- 10 files changed, 890 insertions(+), 1033 deletions(-) delete mode 100644 thermion_dart/lib/thermion_dart/compatibility/web/interop/shims/thermion_dart_api_js_shim.dart rename thermion_dart/lib/thermion_dart/compatibility/web/interop/{shims => }/thermion_viewer_js_shim.dart (100%) diff --git a/thermion_dart/lib/thermion_dart.dart b/thermion_dart/lib/thermion_dart.dart index 0d69812a..6ccf7c64 100644 --- a/thermion_dart/lib/thermion_dart.dart +++ b/thermion_dart/lib/thermion_dart.dart @@ -1,5 +1,5 @@ library filament_dart; export 'thermion_dart/thermion_viewer.dart'; -export 'thermion_dart/thermion_viewer_ffi.dart'; +export 'thermion_dart/compatibility/web/interop/thermion_viewer_wasm.dart' if (dart.library.io) 'thermion_dart/thermion_viewer_ffi.dart'; export 'thermion_dart/entities/entity_transform_controller.dart'; diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/compatibility.dart b/thermion_dart/lib/thermion_dart/compatibility/web/compatibility.dart index f8da247c..e69de29b 100644 --- a/thermion_dart/lib/thermion_dart/compatibility/web/compatibility.dart +++ b/thermion_dart/lib/thermion_dart/compatibility/web/compatibility.dart @@ -1,118 +0,0 @@ -import 'dart:async'; -import 'dart:js_interop'; -import 'package:thermion_dart/thermion_dart/compatibility/web/interop.dart'; - -import "allocator.dart"; - -export "allocator.dart"; -export "thermion_dart.g.dart"; - -export 'package:ffi/ffi.dart' hide StringUtf8Pointer, Utf8Pointer; -export 'dart:ffi' - hide - Uint8Pointer, - FloatPointer, - DoublePointer, - Int32Pointer, - Int64Pointer, - PointerPointer, - Allocator; - -const allocator = Allocator(); - -@AbiSpecificIntegerMapping({ - Abi.androidArm: Uint8(), - Abi.androidArm64: Uint8(), - Abi.androidIA32: Int8(), - Abi.androidX64: Int8(), - Abi.androidRiscv64: Uint8(), - Abi.fuchsiaArm64: Uint8(), - Abi.fuchsiaX64: Int8(), - Abi.fuchsiaRiscv64: Uint8(), - Abi.iosArm: Int8(), - Abi.iosArm64: Int8(), - Abi.iosX64: Int8(), - Abi.linuxArm: Uint8(), - Abi.linuxArm64: Uint8(), - Abi.linuxIA32: Int8(), - Abi.linuxX64: Int8(), - Abi.linuxRiscv32: Uint8(), - Abi.linuxRiscv64: Uint8(), - Abi.macosArm64: Int8(), - Abi.macosX64: Int8(), - Abi.windowsArm64: Int8(), - Abi.windowsIA32: Int8(), - Abi.windowsX64: Int8(), -}) -final class FooChar extends AbiSpecificInteger { - const FooChar(); -} - -class Compatibility { - final _foo = FooChar(); -} - -Future withVoidCallback( - Function(Pointer>) func) async { - JSArray retVal = createVoidCallback(); - var promise = retVal.toDart[0] as JSPromise; - var fnPtrAddress = retVal.toDart[1] as JSNumber; - var fnPtr = Pointer>.fromAddress( - fnPtrAddress.toDartInt); - func(fnPtr); - await promise.toDart; -} - -Future withVoidPointerCallback( - void Function(Pointer)>>) - func) async { - JSArray retVal = createVoidPointerCallback(); - var promise = retVal.toDart[0] as JSPromise; - - var fnPtrAddress = retVal.toDart[1] as JSNumber; - var fnPtr = Pointer)>>.fromAddress( - fnPtrAddress.toDartInt); - func(fnPtr); - final addr = await promise.toDart; - return addr.toDartInt; -} - -Future withBoolCallback( - Function(Pointer>) func) async { - JSArray retVal = createBoolCallback(); - var promise = retVal.toDart[0] as JSPromise; - - var fnPtrAddress = retVal.toDart[1] as JSNumber; - var fnPtr = Pointer>.fromAddress( - fnPtrAddress.toDartInt); - func(fnPtr); - final addr = await promise.toDart; - return addr.toDart; -} - -Future withIntCallback( - Function(Pointer>) func) async { - JSArray retVal = createBoolCallback(); - var promise = retVal.toDart[0] as JSPromise; - - var fnPtrAddress = retVal.toDart[1] as JSNumber; - var fnPtr = Pointer>.fromAddress( - fnPtrAddress.toDartInt); - func(fnPtr); - final addr = await promise.toDart; - return addr.toDartInt; -} - -Future withCharPtrCallback( - Function(Pointer)>>) - func) async { - JSArray retVal = createVoidPointerCallback(); - var promise = retVal.toDart[0] as JSPromise; - - var fnPtrAddress = retVal.toDart[1] as JSNumber; - var fnPtr = Pointer)>>.fromAddress( - fnPtrAddress.toDartInt); - func(fnPtr); - final addr = await promise.toDart; - return Pointer.fromAddress(addr.toDartInt).toDartString(); -} diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/interop/shims/thermion_dart_api_js_shim.dart b/thermion_dart/lib/thermion_dart/compatibility/web/interop/shims/thermion_dart_api_js_shim.dart deleted file mode 100644 index dab1daed..00000000 --- a/thermion_dart/lib/thermion_dart/compatibility/web/interop/shims/thermion_dart_api_js_shim.dart +++ /dev/null @@ -1,403 +0,0 @@ -@JS() -library thermion_flutter_js; - -import 'dart:js_interop'; - -import 'package:thermion_dart/thermion_dart/thermion_viewer.dart'; - -/// -/// An extension type on [JSObject] that represents a -/// Javascript shim implementation for the [ThermionViewer] interface. -/// -extension type ThermionDartAPIJSShim(JSObject _) implements JSObject { - - @JS('wasm_test') - external JSPromise wasm_test(String str); - - @JS('set_rendering') - external JSPromise set_rendering(bool render); - - @JS('render') - external JSPromise render(); - - @JS('setFrameRate') - external JSPromise setFrameRate(int framerate); - - @JS('setBackgroundImage') - external JSPromise setBackgroundImage(String path, bool fillHeight); - - @JS('setBackgroundImagePosition') - external JSPromise setBackgroundImagePosition(double x, double y, bool clamp); - - @JS('clearBackgroundImage') - external JSPromise clearBackgroundImage(); - - @JS('setBackgroundColor') - external JSPromise setBackgroundColor( - double r, double g, double b, double alpha); - - @JS('loadSkybox') - external JSPromise loadSkybox(String skyboxPath); - - @JS('removeSkybox') - external JSPromise removeSkybox(); - - @JS('loadIbl') - external JSPromise loadIbl(String lightingPath, double intensity); - - @JS('rotateIbl') - external JSPromise rotateIbl(JSArray rotationMatrix); - - @JS('removeIbl') - external JSPromise removeIbl(); - - @JS('addLight') - external JSPromise addLight( - int type, - double colour, - double intensity, - double posX, - double posY, - double posZ, - double dirX, - double dirY, - double dirZ, - double falloffRadius, - double spotLightConeInner, - double spotLightConeOuter, - double sunAngularRadius, - double sunHaloSize, - double sunHaloFallof, - bool castShadows); - - @JS('removeLight') - external JSPromise removeLight(ThermionEntity light); - - @JS('clearLights') - external JSPromise clearLights(); - - @JS('loadGlb') - external JSPromise loadGlb(String path, int numInstances); - - @JS('createInstance') - external JSPromise createInstance(ThermionEntity entity); - - @JS('getInstanceCount') - external JSPromise getInstanceCount(ThermionEntity entity); - - @JS('getInstances') - external JSPromise> getInstances(ThermionEntity entity); - - @JS('loadGltf') - external JSPromise loadGltf( - String path, String relativeResourcePath); - - @JS('panStart') - external JSPromise panStart(double x, double y); - - @JS('panUpdate') - external JSPromise panUpdate(double x, double y); - - @JS('panEnd') - external JSPromise panEnd(); - - @JS('rotateStart') - external JSPromise rotateStart(double x, double y); - - @JS('rotateUpdate') - external JSPromise rotateUpdate(double x, double y); - - @JS('rotateEnd') - external JSPromise rotateEnd(); - - @JS('setMorphTargetWeights') - external JSPromise setMorphTargetWeights( - ThermionEntity entity, JSArray weights); - - @JS('getMorphTargetNames') - external JSPromise> getMorphTargetNames( - ThermionEntity entity, ThermionEntity childEntity); - - @JS('getBoneNames') - external JSPromise> getBoneNames( - ThermionEntity entity, int skinIndex); - - @JS('getAnimationNames') - external JSPromise> getAnimationNames( - ThermionEntity entity); - - @JS('getAnimationDuration') - external JSPromise getAnimationDuration( - ThermionEntity entity, int animationIndex); - - @JS('setMorphAnimationData') - external JSPromise setMorphAnimationData( - ThermionEntity entity, - JSArray> animation, - JSArray morphTargets, - JSArray? targetMeshNames, - double frameLengthInMs); - - @JS('resetBones') - external JSPromise resetBones(ThermionEntity entity); - - @JS('addBoneAnimation') - external JSPromise addBoneAnimation( - ThermionEntity entity, - JSArray bones, - JSArray>> frameData, - JSNumber frameLengthInMs, - JSNumber spaceEnum, - JSNumber skinIndex, - JSNumber fadeInInSecs, - JSNumber fadeOutInSecs, - JSNumber maxDelta); - - @JS('removeEntity') - external JSPromise removeEntity(ThermionEntity entity); - - @JS('clearEntities') - external JSPromise clearEntities(); - - @JS('zoomBegin') - external JSPromise zoomBegin(); - - @JS('zoomUpdate') - external JSPromise zoomUpdate(double x, double y, double z); - - @JS('zoomEnd') - external JSPromise zoomEnd(); - - @JS('playAnimation') - external JSPromise playAnimation( - ThermionEntity entity, - int index, - bool loop, - bool reverse, - bool replaceActive, - double crossfade, - ); - - @JS('playAnimationByName') - external JSPromise playAnimationByName( - ThermionEntity entity, - String name, - bool loop, - bool reverse, - bool replaceActive, - double crossfade, - ); - - @JS('setAnimationFrame') - external JSPromise setAnimationFrame( - ThermionEntity entity, int index, int animationFrame); - - @JS('stopAnimation') - external JSPromise stopAnimation(ThermionEntity entity, int animationIndex); - - @JS('stopAnimationByName') - external JSPromise stopAnimationByName(ThermionEntity entity, String name); - - @JS('setCamera') - external JSPromise setCamera(ThermionEntity entity, String? name); - - @JS('setMainCamera') - external JSPromise setMainCamera(); - - @JS('getMainCamera') - external JSPromise getMainCamera(); - - @JS('setCameraFov') - external JSPromise setCameraFov(double degrees, double width, double height); - - @JS('setToneMapping') - external JSPromise setToneMapping(int mapper); - - @JS('setBloom') - external JSPromise setBloom(double bloom); - - @JS('setCameraFocalLength') - external JSPromise setCameraFocalLength(double focalLength); - - @JS('setCameraCulling') - external JSPromise setCameraCulling(double near, double far); - - @JS('getCameraCullingNear') - external JSPromise getCameraCullingNear(); - - @JS('getCameraCullingFar') - external JSPromise getCameraCullingFar(); - - @JS('setCameraFocusDistance') - external JSPromise setCameraFocusDistance(double focusDistance); - - @JS('getCameraPosition') - external JSPromise> getCameraPosition(); - - @JS('getCameraModelMatrix') - external JSPromise> getCameraModelMatrix(); - - @JS('getCameraViewMatrix') - external JSPromise> getCameraViewMatrix(); - - @JS('getCameraProjectionMatrix') - external JSPromise> getCameraProjectionMatrix(); - - @JS('getCameraCullingProjectionMatrix') - external JSPromise> getCameraCullingProjectionMatrix(); - - @JS('getCameraFrustum') - external JSPromise getCameraFrustum(); - - @JS('setCameraPosition') - external JSPromise setCameraPosition(double x, double y, double z); - - @JS('getCameraRotation') - external JSPromise> getCameraRotation(); - - @JS('moveCameraToAsset') - external JSPromise moveCameraToAsset(ThermionEntity entity); - - @JS('setViewFrustumCulling') - external JSPromise setViewFrustumCulling(JSBoolean enabled); - - @JS('setCameraExposure') - external JSPromise setCameraExposure( - double aperture, double shutterSpeed, double sensitivity); - - @JS('setCameraRotation') - external JSPromise setCameraRotation(JSArray quaternion); - - @JS('setCameraModelMatrix') - external JSPromise setCameraModelMatrix(JSArray matrix); - - @JS('setMaterialColor') - external JSPromise setMaterialColor(ThermionEntity entity, String meshName, - int materialIndex, double r, double g, double b, double a); - - @JS('transformToUnitCube') - external JSPromise transformToUnitCube(ThermionEntity entity); - - @JS('setPosition') - external JSPromise setPosition( - ThermionEntity entity, double x, double y, double z); - - @JS('setScale') - external JSPromise setScale(ThermionEntity entity, double scale); - - @JS('setRotation') - external JSPromise setRotation( - ThermionEntity entity, double rads, double x, double y, double z); - - @JS('queuePositionUpdate') - external JSPromise queuePositionUpdate( - ThermionEntity entity, double x, double y, double z, bool relative); - - @JS('queueRotationUpdate') - external JSPromise queueRotationUpdate(ThermionEntity entity, double rads, - double x, double y, double z, bool relative); - - @JS('queueRotationUpdateQuat') - external JSPromise queueRotationUpdateQuat( - ThermionEntity entity, JSArray quat, bool relative); - - @JS('setPostProcessing') - external JSPromise setPostProcessing(bool enabled); - - @JS('setAntiAliasing') - external JSPromise setAntiAliasing(bool msaa, bool fxaa, bool taa); - - @JS('setRotationQuat') - external JSPromise setRotationQuat( - ThermionEntity entity, JSArray rotation); - - @JS('reveal') - external JSPromise reveal(ThermionEntity entity, String? meshName); - - @JS('hide') - external JSPromise hide(ThermionEntity entity, String? meshName); - - @JS('pick') - external void pick(int x, int y); - - @JS('getNameForEntity') - external String? getNameForEntity(ThermionEntity entity); - - @JS('setCameraManipulatorOptions') - external JSPromise setCameraManipulatorOptions( - int mode, - double orbitSpeedX, - double orbitSpeedY, - double zoomSpeed, - ); - - @JS('getChildEntities') - external JSPromise> getChildEntities( - ThermionEntity parent, bool renderableOnly); - - @JS('getChildEntity') - external JSPromise getChildEntity( - ThermionEntity parent, String childName); - - @JS('getChildEntityNames') - external JSPromise> getChildEntityNames( - ThermionEntity entity, bool renderableOnly); - - @JS('setRecording') - external JSPromise setRecording(JSBoolean recording); - - @JS('setRecordingOutputDirectory') - external JSPromise setRecordingOutputDirectory(String outputDirectory); - - @JS('addAnimationComponent') - external JSPromise addAnimationComponent(ThermionEntity entity); - - @JS('removeAnimationComponent') - external JSPromise removeAnimationComponent(ThermionEntity entity); - - @JS('addCollisionComponent') - external JSPromise addCollisionComponent(ThermionEntity entity); - - @JS('removeCollisionComponent') - external JSPromise removeCollisionComponent(ThermionEntity entity); - - @JS('createGeometry') - external JSPromise createGeometry(JSArray vertices, - JSArray indices, String? materialPath, int primitiveType); - - @JS('setParent') - external JSPromise setParent(ThermionEntity child, ThermionEntity parent); - - @JS('getParent') - external JSPromise getParent(ThermionEntity child); - - @JS('getParent') - external JSPromise getBone( - ThermionEntity child, int boneIndex, int skinIndex); - - @JS('testCollisions') - external JSPromise testCollisions(ThermionEntity entity); - - @JS('setPriority') - external JSPromise setPriority(ThermionEntity entityId, int priority); - - @JS('getLocalTransform') - external JSPromise> getLocalTransform( - ThermionEntity entity); - - @JS('getWorldTransform') - external JSPromise> getWorldTransform( - ThermionEntity entity); - - @JS('updateBoneMatrices') - external JSPromise updateBoneMatrices(ThermionEntity entity); - - @JS('setTransform') - external JSPromise setTransform( - ThermionEntity entity, JSArray transform); - - @JS('setBoneTransform') - external JSPromise setBoneTransform( - ThermionEntity entity, int boneIndex, JSArray transform, int skinIndex); -} - diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_dart_bridge.dart b/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_dart_bridge.dart index d3ed6db2..85db072c 100644 --- a/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_dart_bridge.dart +++ b/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_dart_bridge.dart @@ -2,7 +2,7 @@ library thermion_flutter_js; import 'dart:js_interop'; -import 'package:thermion_dart/thermion_dart/compatibility/web/interop/shims/thermion_viewer_js_shim.dart'; +import 'package:thermion_dart/thermion_dart/compatibility/web/interop/thermion_viewer_js_shim.dart'; import 'package:vector_math/vector_math_64.dart' as v64; import 'package:animation_tools_dart/animation_tools_dart.dart'; @@ -15,8 +15,8 @@ import 'package:vector_math/vector_math_64.dart'; /// A (Dart) class that wraps a (Dart) instance of [ThermionViewer], /// but exported to JS by binding to a global property. /// This is effectively an implementation of [ThermionViewerJSShim]; -/// allowing users to interact with an instance of [ThermionViewer] -/// (presumably compiled to WASM) from any Javascript context (including +/// allowing users to interact with an instance of [ThermionViewer] +/// (presumably compiled to WASM) from any Javascript context (including /// the browser console). /// @JSExport() @@ -24,9 +24,8 @@ class ThermionViewerJSDartBridge { final ThermionViewer viewer; ThermionViewerJSDartBridge(this.viewer); - - void bind( - {String globalPropertyName = "filamentViewer"}) { + + void bind({String globalPropertyName = "thermionViewer"}) { var wrapper = createJSInteropWrapper(this) as ThermionViewerJSShim; globalContext.setProperty(globalPropertyName.toJS, wrapper); @@ -131,10 +130,13 @@ class ThermionViewerJSDartBridge { @JSExport() JSPromise loadGlb(String path, {int numInstances = 1}) { + print("Loading GLB from path $path with numInstances $numInstances"); return viewer .loadGlb(path, numInstances: numInstances) .then((entity) => entity.toJS) - .toJS; + .catchError((err) { + print("Error: $err"); + }).toJS; } @JSExport() diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_js.dart b/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_js.dart index fbd47777..ab325097 100644 --- a/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_js.dart +++ b/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_js.dart @@ -8,7 +8,7 @@ import 'package:thermion_dart/thermion_dart/thermion_viewer.dart'; import 'package:thermion_dart/thermion_dart/scene_impl.dart'; import 'package:vector_math/vector_math_64.dart'; -import 'shims/thermion_viewer_js_shim.dart'; +import 'thermion_viewer_js_shim.dart'; /// /// An [ThermionViewer] implementation that forwards calls to diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/interop/shims/thermion_viewer_js_shim.dart b/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_js_shim.dart similarity index 100% rename from thermion_dart/lib/thermion_dart/compatibility/web/interop/shims/thermion_viewer_js_shim.dart rename to thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_js_shim.dart diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_wasm.dart b/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_wasm.dart index 46d2341b..abde3ba6 100644 --- a/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_wasm.dart +++ b/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_wasm.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:js_interop'; import 'dart:js_interop_unsafe'; import 'dart:math'; @@ -9,6 +10,8 @@ import 'package:animation_tools_dart/animation_tools_dart.dart'; import 'package:thermion_dart/thermion_dart/thermion_viewer.dart'; import 'package:vector_math/vector_math_64.dart'; +export 'thermion_viewer_dart_bridge.dart'; + extension type _EmscriptenModule(JSObject _) implements JSObject { external JSAny? ccall(String name, String returnType, JSArray argTypes, JSArray args, JSAny? opts); @@ -40,7 +43,7 @@ class ThermionViewerWasm implements ThermionViewer { bool _initialized = false; bool _rendering = false; - ThermionViewerWasm({String moduleName="thermion_dart"}) { + ThermionViewerWasm({String moduleName = "thermion_dart"}) { _module = window.getProperty<_EmscriptenModule>(moduleName.toJS); } @@ -124,7 +127,6 @@ class ThermionViewerWasm implements ThermionViewer { await callback.call(); } _onDispose.clear(); - } void _destroyViewer() { @@ -375,51 +377,6 @@ class ThermionViewerWasm implements ThermionViewer { return entityId.toDartInt; } - @override - Future clearBackgroundImage() { - // TODO: implement clearBackgroundImage - throw UnimplementedError(); - } - - @override - Future clearEntities() { - // TODO: implement clearEntities - throw UnimplementedError(); - } - - @override - Future clearLights() { - // TODO: implement clearLights - throw UnimplementedError(); - } - - @override - Future createGeometry(List vertices, List indices, - {String? materialPath, - PrimitiveType primitiveType = PrimitiveType.TRIANGLES}) { - // TODO: implement createGeometry - throw UnimplementedError(); - } - - @override - Future createInstance(ThermionEntity entity) { - // TODO: implement createInstance - throw UnimplementedError(); - } - - @override - Future getAnimationDuration( - ThermionEntity entity, int animationIndex) { - // TODO: implement getAnimationDuration - throw UnimplementedError(); - } - - @override - Future> getAnimationNames(ThermionEntity entity) { - // TODO: implement getAnimationNames - throw UnimplementedError(); - } - @override Future> getBoneNames(ThermionEntity entity, {int skinIndex = 0}) async { @@ -455,60 +412,6 @@ class ThermionViewerWasm implements ThermionViewer { return names; } - @override - Future getCameraCullingFar() { - // TODO: implement getCameraCullingFar - throw UnimplementedError(); - } - - @override - Future getCameraCullingNear() { - // TODO: implement getCameraCullingNear - throw UnimplementedError(); - } - - @override - Future getCameraCullingProjectionMatrix() { - // TODO: implement getCameraCullingProjectionMatrix - throw UnimplementedError(); - } - - @override - Future getCameraFrustum() { - // TODO: implement getCameraFrustum - throw UnimplementedError(); - } - - @override - Future getCameraModelMatrix() { - // TODO: implement getCameraModelMatrix - throw UnimplementedError(); - } - - @override - Future getCameraPosition() { - // TODO: implement getCameraPosition - throw UnimplementedError(); - } - - @override - Future getCameraProjectionMatrix() { - // TODO: implement getCameraProjectionMatrix - throw UnimplementedError(); - } - - @override - Future getCameraRotation() { - // TODO: implement getCameraRotation - throw UnimplementedError(); - } - - @override - Future getCameraViewMatrix() { - // TODO: implement getCameraViewMatrix - throw UnimplementedError(); - } - @override Future> getChildEntities( ThermionEntity parent, bool renderableOnly) async { @@ -575,31 +478,6 @@ class ThermionViewerWasm implements ThermionViewer { return names; } - @override - Future getInstanceCount(ThermionEntity entity) { - // TODO: implement getInstanceCount - throw UnimplementedError(); - } - - @override - Future> getInstances(ThermionEntity entity) { - // TODO: implement getInstances - throw UnimplementedError(); - } - - @override - Future getInverseBindMatrix(ThermionEntity parent, int boneIndex, - {int skinIndex = 0}) { - // TODO: implement getInverseBindMatrix - throw UnimplementedError(); - } - - @override - Future getLocalTransform(ThermionEntity entity) { - // TODO: implement getLocalTransform - throw UnimplementedError(); - } - @override Future getMainCamera() async { final entityId = _module.ccall( @@ -782,35 +660,6 @@ class ThermionViewerWasm implements ThermionViewer { await promise.toDart; } - @override - Future moveCameraToAsset(ThermionEntity entity) { - // TODO: implement moveCameraToAsset - throw UnimplementedError(); - } - - @override - Future panEnd() { - // TODO: implement panEnd - throw UnimplementedError(); - } - - @override - Future panStart(double x, double y) { - // TODO: implement panStart - throw UnimplementedError(); - } - - @override - Future panUpdate(double x, double y) { - // TODO: implement panUpdate - throw UnimplementedError(); - } - - @override - void pick(int x, int y) { - // TODO: implement pick - } - @override Future playAnimation(ThermionEntity entity, int index, {bool loop = false, @@ -841,75 +690,6 @@ class ThermionViewerWasm implements ThermionViewer { null); } - @override - Future playAnimationByName(ThermionEntity entity, String name, - {bool loop = false, - bool reverse = false, - bool replaceActive = true, - double crossfade = 0.0}) { - // TODO: implement playAnimationByName - throw UnimplementedError(); - } - - @override - Future queuePositionUpdate( - ThermionEntity entity, double x, double y, double z, - {bool relative = false}) { - // TODO: implement queuePositionUpdate - throw UnimplementedError(); - } - - @override - Future queueRotationUpdate( - ThermionEntity entity, double rads, double x, double y, double z, - {bool relative = false}) { - // TODO: implement queueRotationUpdate - throw UnimplementedError(); - } - - @override - Future queueRotationUpdateQuat(ThermionEntity entity, Quaternion quat, - {bool relative = false}) { - // TODO: implement queueRotationUpdateQuat - throw UnimplementedError(); - } - - @override - Future removeAnimationComponent(ThermionEntity entity) { - // TODO: implement removeAnimationComponent - throw UnimplementedError(); - } - - @override - Future removeCollisionComponent(ThermionEntity entity) { - // TODO: implement removeCollisionComponent - throw UnimplementedError(); - } - - @override - Future removeEntity(ThermionEntity entity) { - // TODO: implement removeEntity - throw UnimplementedError(); - } - - @override - Future removeIbl() { - // TODO: implement removeIbl - throw UnimplementedError(); - } - - @override - Future removeLight(ThermionEntity light) { - // TODO: implement removeLight - throw UnimplementedError(); - } - - @override - Future removeSkybox() { - // TODO: implement removeSkybox - throw UnimplementedError(); - } - int _last = 0; @override @@ -935,52 +715,9 @@ class ThermionViewerWasm implements ThermionViewer { null); } - @override - Future resetBones(ThermionEntity entity) { - // TODO: implement resetBones - throw UnimplementedError(); - } - - @override - Future reveal(ThermionEntity entity, String? meshName) { - // TODO: implement reveal - throw UnimplementedError(); - } - - @override - Future rotateEnd() { - // TODO: implement rotateEnd - throw UnimplementedError(); - } - - @override - Future rotateIbl(Matrix3 rotation) { - // TODO: implement rotateIbl - throw UnimplementedError(); - } - - @override - Future rotateStart(double x, double y) { - // TODO: implement rotateStart - throw UnimplementedError(); - } - - @override - Future rotateUpdate(double x, double y) { - // TODO: implement rotateUpdate - throw UnimplementedError(); - } - @override Scene get scene => throw UnimplementedError(); - @override - Future setAnimationFrame( - ThermionEntity entity, int index, int animationFrame) { - // TODO: implement setAnimationFrame - throw UnimplementedError(); - } - @override Future setAntiAliasing(bool msaa, bool fxaa, bool taa) async { _module.ccall( @@ -991,85 +728,6 @@ class ThermionViewerWasm implements ThermionViewer { null); } - @override - Future setBackgroundImage(String path, {bool fillHeight = false}) { - // TODO: implement setBackgroundImage - throw UnimplementedError(); - } - - @override - Future setBackgroundImagePosition(double x, double y, {bool clamp = false}) { - // TODO: implement setBackgroundImagePosition - throw UnimplementedError(); - } - - @override - Future setBloom(double bloom) { - // TODO: implement setBloom - throw UnimplementedError(); - } - - @override - Future setBoneTransform( - ThermionEntity entity, int boneIndex, Matrix4 transform, - {int skinIndex = 0}) { - // TODO: implement setBoneTransform - throw UnimplementedError(); - } - - @override - Future setCamera(ThermionEntity entity, String? name) { - // TODO: implement setCamera - throw UnimplementedError(); - } - - @override - Future setCameraCulling(double near, double far) { - // TODO: implement setCameraCulling - throw UnimplementedError(); - } - - @override - Future setCameraExposure( - double aperture, double shutterSpeed, double sensitivity) { - // TODO: implement setCameraExposure - throw UnimplementedError(); - } - - @override - Future setCameraFocalLength(double focalLength) { - // TODO: implement setCameraFocalLength - throw UnimplementedError(); - } - - @override - Future setCameraFocusDistance(double focusDistance) { - // TODO: implement setCameraFocusDistance - throw UnimplementedError(); - } - - @override - Future setCameraFov(double degrees, double width, double height) { - // TODO: implement setCameraFov - throw UnimplementedError(); - } - - @override - Future setCameraManipulatorOptions( - {ManipulatorMode mode = ManipulatorMode.ORBIT, - double orbitSpeedX = 0.01, - double orbitSpeedY = 0.01, - double zoomSpeed = 0.01}) { - // TODO: implement setCameraManipulatorOptions - throw UnimplementedError(); - } - - @override - Future setCameraModelMatrix(List matrix) { - // TODO: implement setCameraModelMatrix - throw UnimplementedError(); - } - @override Future setCameraPosition(double x, double y, double z) async { _module.ccall( @@ -1097,80 +755,6 @@ class ThermionViewerWasm implements ThermionViewer { null); } - @override - Future setFrameRate(int framerate) { - // TODO: implement setFrameRate - throw UnimplementedError(); - } - - @override - Future setMainCamera() { - // TODO: implement setMainCamera - throw UnimplementedError(); - } - - @override - Future setMaterialColor(ThermionEntity entity, String meshName, - int materialIndex, double r, double g, double b, double a) { - // TODO: implement setMaterialColor - throw UnimplementedError(); - } - - // @override - // Future setMorphAnimationData( - // ThermionEntity entity, MorphAnimationData animation, - // {List? targetMeshNames}) async { - // final morphTargetNames = await getMorphTargetNames(entity, entity); - - // // We need to create a JS array for the morph indices and morph data - // final numFrames = animation.numFrames; - // final numMorphTargets = morphTargetNames.length; - // final numBytes = numFrames * numMorphTargets * 4; - // final floatPtr = _module._malloc(numBytes); - // final morphIndicesPtr = _module._malloc(numFrames * 4); - - // // Extract the morph data for the target morph targets - // final morphData = animation.extract(morphTargets: targetMeshNames); - - // // Create a list of morph indices based on the target morph targets - // final morphIndices = targetMeshNames != null - // ? animation._getMorphTargetIndices(targetMeshNames) - // : List.generate(morphTargetNames.length, (i) => i); - // final morphIndicesList = td.Int32List.fromList(morphIndices); - - // // Set the morph data and indices into the JS arrays - // _module.writeArrayToMemory(morphData.buffer.asUint8List(morphData.offsetInBytes).toJS, floatPtr); - // _module.writeArrayToMemory(morphIndicesList.buffer.asUint8List(morphData.offsetInBytes).toJS, morphIndicesPtr); - - // // Set the morph animation data - // _module.ccall( - // "set_morph_animation", - // "bool", - // [ - // "void*".toJS, - // "int".toJS, - // "float*".toJS, - // "int*".toJS, - // "int".toJS, - // "int".toJS, - // "float".toJS - // ].toJS, - // [ - // _sceneManager!, - // entity.toJS, - // floatPtr, - // morphIndicesPtr, - // numMorphTargets.toJS, - // numFrames.toJS, - // animation.frameLengthInMs.toJS - // ].toJS, - // null); - - // // Free the memory allocated for the JS arrays - // _module._free(floatPtr); - // _module._free(morphIndicesPtr); - // } - @override Future setMorphAnimationData( ThermionEntity entity, MorphAnimationData animation, @@ -1282,18 +866,6 @@ class ThermionViewerWasm implements ThermionViewer { } } - @override - Future setMorphTargetWeights(ThermionEntity entity, List weights) { - // TODO: implement setMorphTargetWeights - throw UnimplementedError(); - } - - @override - Future setParent(ThermionEntity child, ThermionEntity parent) { - // TODO: implement setParent - throw UnimplementedError(); - } - @override Future setPosition( ThermionEntity entity, double x, double y, double z) async { @@ -1312,30 +884,6 @@ class ThermionViewerWasm implements ThermionViewer { ["void*".toJS, "bool".toJS].toJS, [_viewer!, enabled.toJS].toJS, null); } - @override - Future setPriority(ThermionEntity entityId, int priority) { - // TODO: implement setPriority - throw UnimplementedError(); - } - - @override - Future setRecording(bool recording) { - // TODO: implement setRecording - throw UnimplementedError(); - } - - @override - Future setRecordingOutputDirectory(String outputDirectory) { - // TODO: implement setRecordingOutputDirectory - throw UnimplementedError(); - } - - @override - Future setRendering(bool render) { - // TODO: implement setRendering - throw UnimplementedError(); - } - @override Future setRotation( ThermionEntity entity, double rads, double x, double y, double z) async { @@ -1388,36 +936,873 @@ class ThermionViewerWasm implements ThermionViewer { null); } + final _onDispose = []; + + /// + /// + /// + void onDispose(Future Function() callback) { + _onDispose.add(callback); + } + @override - Future setScale(ThermionEntity entity, double scale) { - // TODO: implement setScale + Future clearBackgroundImage() async { + _module.ccall("clear_background_image", "void", ["void*".toJS].toJS, + [_viewer!].toJS, null); + } + + @override + Future clearEntities() async { + _module.ccall( + "clear_entities", "void", ["void*".toJS].toJS, [_viewer!].toJS, null); + } + + @override + Future clearLights() async { + _module.ccall( + "clear_lights", "void", ["void*".toJS].toJS, [_viewer!].toJS, null); + } + + @override + Future createGeometry(List vertices, List indices, + {String? materialPath, + PrimitiveType primitiveType = PrimitiveType.TRIANGLES}) async { + final verticesData = td.Float32List.fromList(vertices); + final indicesData = Uint16List.fromList(indices); + final verticesPtr = _module._malloc(verticesData.lengthInBytes); + final indicesPtr = _module._malloc(indicesData.lengthInBytes); + _module.writeArrayToMemory( + verticesData.buffer.asUint8List().toJS, verticesPtr); + _module.writeArrayToMemory( + indicesData.buffer.asUint8List().toJS, indicesPtr); + + final entityId = _module.ccall( + "create_geometry", + "int", + [ + "void*".toJS, + "float*".toJS, + "int".toJS, + "uint16_t*".toJS, + "int".toJS, + "int".toJS, + "string".toJS + ].toJS, + [ + _viewer!, + verticesPtr, + vertices.length.toJS, + indicesPtr, + indices.length.toJS, + primitiveType.index.toJS, + materialPath?.toJS ?? "".toJS, + ].toJS, + null) as JSNumber; + + _module._free(verticesPtr); + _module._free(indicesPtr); + + if (entityId.toDartInt == -1) { + throw Exception("Failed to create geometry"); + } + return entityId.toDartInt; + } + + @override + Future createInstance(ThermionEntity entity) async { + final result = _module.ccall( + "create_instance", + "int", + ["void*".toJS, "int".toJS].toJS, + [_sceneManager!, entity.toJS].toJS, + null) as JSNumber; + if (result.toDartInt == -1) { + throw Exception("Failed to create instance of entity ${entity}"); + } + return result.toDartInt; + } + + @override + Future getAnimationDuration( + ThermionEntity entity, int animationIndex) async { + final result = _module.ccall( + "get_animation_duration", + "float", + ["void*".toJS, "int".toJS, "int".toJS].toJS, + [_sceneManager!, entity.toJS, animationIndex.toJS].toJS, + null) as JSNumber; + return result.toDartDouble; + } + + @override + Future getAnimationCount(ThermionEntity entity) async { + final animationCount = _module.ccall( + "get_animation_count", + "int", + ["void*".toJS, "int".toJS].toJS, + [_sceneManager!, entity.toJS].toJS, + null) as JSNumber; + return animationCount.toDartInt; + } + + @override + Future> getAnimationNames(ThermionEntity entity) async { + final animationCount = await getAnimationCount(entity); + final names = []; + for (int i = 0; i < animationCount; i++) { + final namePtr = _module._malloc(256) as JSNumber; + _module.ccall( + "get_animation_name", + "void", + ["void*".toJS, "int".toJS, "char*".toJS, "int".toJS].toJS, + [_sceneManager!, entity.toJS, namePtr, i.toJS].toJS, + null); + names.add(_module.UTF8ToString(namePtr).toDart); + _module._free(namePtr); + } + return names; + } + + @override + Future getCameraCullingFar() async { + final result = _module.ccall("get_camera_culling_far", "double", + ["void*".toJS].toJS, [_viewer!].toJS, null) as JSNumber; + return result.toDartDouble; + } + + @override + Future getCameraCullingNear() async { + final result = _module.ccall("get_camera_culling_near", "double", + ["void*".toJS].toJS, [_viewer!].toJS, null) as JSNumber; + return result.toDartDouble; + } + + @override + Future getCameraCullingProjectionMatrix() async { + final ptr = _module._malloc(16 * 8) as JSNumber; + _module.ccall("get_camera_culling_projection_matrix", "void", + ["void*".toJS, "double*".toJS].toJS, [_viewer!, ptr].toJS, null); + final matrix = Matrix4.zero(); + for (int i = 0; i < 16; i++) { + matrix[i] = (_module.getValue((ptr.toDartInt + (i * 8)).toJS, "double") + as JSNumber) + .toDartDouble; + } + _module._free(ptr); + return matrix; + } + + @override + Future getCameraFrustum() async { + final ptr = _module._malloc(24 * 8) as JSNumber; + _module.ccall("get_camera_frustum", "void", + ["void*".toJS, "double*".toJS].toJS, [_viewer!, ptr].toJS, null); + final planes = List.generate(6, (i) { + final offset = i * 4; + return Plane() + ..setFromComponents( + (_module.getValue((ptr.toDartInt + (offset * 8)).toJS, "double") + as JSNumber) + .toDartDouble, + (_module.getValue( + (ptr.toDartInt + ((offset + 1) * 8)).toJS, "double") + as JSNumber) + .toDartDouble, + (_module.getValue( + (ptr.toDartInt + ((offset + 2) * 8)).toJS, "double") + as JSNumber) + .toDartDouble, + (_module.getValue( + (ptr.toDartInt + ((offset + 3) * 8)).toJS, "double") + as JSNumber) + .toDartDouble); + }); + _module._free(ptr); + throw UnimplementedError(); + // return Frustum()..plane0 = planes[0]..plane1 =planes[1]..plane2 =planes[2]..plane3 =planes[3], planes[4], planes[5]); + } + + @override + Future getCameraModelMatrix() async { + final ptr = _module._malloc(16 * 8) as JSNumber; + _module.ccall("get_camera_model_matrix", "void", + ["void*".toJS, "double*".toJS].toJS, [_viewer!, ptr].toJS, null); + final matrix = _matrixFromPtr(ptr); + _module._free(ptr); + return matrix; + } + + @override + Future getCameraPosition() async { + final ptr = _module._malloc(3 * 8) as JSNumber; + _module.ccall("get_camera_position", "void", + ["void*".toJS, "void*".toJS].toJS, [_viewer!, ptr].toJS, null); + final pos = Vector3( + (_module.getValue(ptr.toDartInt.toJS, "double") as JSNumber) + .toDartDouble, + (_module.getValue((ptr.toDartInt + 8).toJS, "double") as JSNumber) + .toDartDouble, + (_module.getValue((ptr.toDartInt + 16).toJS, "double") as JSNumber) + .toDartDouble); + _module._free(ptr); + return pos; + } + + @override + Future getCameraProjectionMatrix() async { + final ptr = _module._malloc(16 * 8) as JSNumber; + _module.ccall("get_camera_projection_matrix", "void", + ["void*".toJS, "double*".toJS].toJS, [_viewer!, ptr].toJS, null); + final matrix = _matrixFromPtr(ptr); + _module._free(ptr); + return matrix; + } + + @override + Future getCameraRotation() async { + final model = await getCameraModelMatrix(); + final rotation = model.getRotation(); + return rotation; + } + + @override + Future getCameraViewMatrix() async { + final ptr = _module._malloc(16 * 8) as JSNumber; + _module.ccall("get_camera_view_matrix", "void", + ["void*".toJS, "double*".toJS].toJS, [_viewer!, ptr].toJS, null); + final matrix = Matrix4.zero(); + for (int i = 0; i < 16; i++) { + matrix[i] = (_module.getValue((ptr.toDartInt + (i * 8)).toJS, "double") + as JSNumber) + .toDartDouble; + } + _module._free(ptr); + return matrix; + } + + @override + Future getInstanceCount(ThermionEntity entity) async { + final result = _module.ccall( + "get_instance_count", + "int", + ["void*".toJS, "int".toJS].toJS, + [_sceneManager!, entity.toJS].toJS, + null) as JSNumber; + return result.toDartInt; + } + + @override + Future> getInstances(ThermionEntity entity) async { + final instanceCount = await getInstanceCount(entity); + final buf = _module._malloc(instanceCount * 4) as JSNumber; + _module.ccall( + "get_instances", + "void", + ["void*".toJS, "int".toJS, "int*".toJS].toJS, + [_sceneManager!, entity.toJS, buf].toJS, + null); + final instances = []; + for (int i = 0; i < instanceCount; i++) { + final instanceId = + _module.getValue((buf.toDartInt + (i * 4)).toJS, "i32") as JSNumber; + instances.add(instanceId.toDartInt); + } + _module._free(buf); + return instances; + } + + @override + Future getInverseBindMatrix(ThermionEntity parent, int boneIndex, + {int skinIndex = 0}) async { + final ptr = _module._malloc(16 * 4) as JSNumber; + _module.ccall( + "get_inverse_bind_matrix", + "void", + ["void*".toJS, "int".toJS, "int".toJS, "int".toJS, "float*".toJS].toJS, + [_sceneManager!, parent.toJS, skinIndex.toJS, boneIndex.toJS, ptr].toJS, + null); + final matrix = _matrixFromPtr(ptr); + _module._free(ptr); + return matrix; + } + + @override + Future getLocalTransform(ThermionEntity entity) async { + final ptr = _module._malloc(16 * 4) as JSNumber; + _module.ccall( + "get_local_transform", + "void", + ["void*".toJS, "int".toJS, "float*".toJS].toJS, + [_sceneManager!, entity.toJS, ptr].toJS, + null); + final matrix = _matrixFromPtr(ptr); + _module._free(ptr); + return matrix; + } + + @override + Future moveCameraToAsset(ThermionEntity entity) async { + _module.ccall("move_camera_to_asset", "void", + ["void*".toJS, "int".toJS].toJS, [_viewer!, entity.toJS].toJS, null); + } + + @override + Future panEnd() { + // TODO: implement panEnd throw UnimplementedError(); } @override - Future setToneMapping(ToneMapper mapper) { - // TODO: implement setToneMapping + Future panStart(double x, double y) { + // TODO: implement panStart throw UnimplementedError(); } @override - Future setTransform(ThermionEntity entity, Matrix4 transform) { - // TODO: implement setTransform + Future panUpdate(double x, double y) { + // TODO: implement panUpdate throw UnimplementedError(); } @override - Future setViewFrustumCulling(bool enabled) { - // TODO: implement setViewFrustumCulling + void pick(int x, int y) { + throw UnimplementedError(); + // _module.ccall("filament_pick", "void", + // ["void*".toJS, "int".toJS, "int".toJS, "void*".toJS].toJS, [ + // _viewer!, + // x.toJS, + // y.toJS, + // (entityId, x, y) {}.toJS + // ]); + } + + @override + Future playAnimationByName(ThermionEntity entity, String name, + {bool loop = false, + bool reverse = false, + bool replaceActive = true, + double crossfade = 0.0}) async { + final animationNames = await getAnimationNames(entity); + final index = animationNames.indexOf(name); + if (index == -1) { + throw Exception("Animation ${name} not found."); + } + return playAnimation(entity, index, + loop: loop, + reverse: reverse, + replaceActive: replaceActive, + crossfade: crossfade); + } + + @override + Future queuePositionUpdate( + ThermionEntity entity, double x, double y, double z, + {bool relative = false}) async { + _module.ccall( + "queue_position_update", + "void", + [ + "void*".toJS, + "int".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "bool".toJS + ].toJS, + [_sceneManager!, entity.toJS, x.toJS, y.toJS, z.toJS, relative.toJS] + .toJS, + null); + } + + @override + Future queueRotationUpdate( + ThermionEntity entity, double rads, double x, double y, double z, + {bool relative = false}) async { + _module.ccall( + "queue_rotation_update", + "void", + [ + "void*".toJS, + "int".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "bool".toJS + ].toJS, + [ + _sceneManager!, + entity.toJS, + rads.toJS, + x.toJS, + y.toJS, + z.toJS, + relative.toJS + ].toJS, + null); + } + + @override + Future queueRotationUpdateQuat(ThermionEntity entity, Quaternion quat, + {bool relative = false}) async { + _module.ccall( + "queue_rotation_update", + "void", + [ + "void*".toJS, + "int".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "bool".toJS + ].toJS, + [ + _sceneManager!, + entity.toJS, + quat.radians.toJS, + quat.x.toJS, + quat.y.toJS, + quat.z.toJS, + relative.toJS + ].toJS, + null); + } + + @override + Future removeAnimationComponent(ThermionEntity entity) async { + _module.ccall( + "remove_animation_component", + "void", + ["void*".toJS, "int".toJS].toJS, + [_sceneManager!, entity.toJS].toJS, + null); + } + + @override + Future removeCollisionComponent(ThermionEntity entity) async { + _module.ccall( + "remove_collision_component", + "void", + ["void*".toJS, "int".toJS].toJS, + [_sceneManager!, entity.toJS].toJS, + null); + } + + @override + Future removeEntity(ThermionEntity entity) async { + _module.ccall("remove_entity", "void", ["void*".toJS, "int".toJS].toJS, + [_viewer!, entity.toJS].toJS, null); + } + + @override + Future removeIbl() async { + _module.ccall( + "remove_ibl", "void", ["void*".toJS].toJS, [_viewer!].toJS, null); + } + + @override + Future removeLight(ThermionEntity light) async { + _module.ccall("remove_light", "void", ["void*".toJS, "int".toJS].toJS, + [_viewer!, light.toJS].toJS, null); + } + + @override + Future removeSkybox() async { + _module.ccall( + "remove_skybox", "void", ["void*".toJS].toJS, [_viewer!].toJS, null); + } + + @override + Future resetBones(ThermionEntity entity) async { + _module.ccall("reset_to_rest_pose", "void", ["void*".toJS, "int".toJS].toJS, + [_sceneManager!, entity.toJS].toJS, null); + } + + @override + Future reveal(ThermionEntity entity, String? meshName) async { + if (meshName != null) { + final result = _module.ccall( + "reveal_mesh", + "int", + ["void*".toJS, "int".toJS, "string".toJS].toJS, + [_sceneManager!, entity.toJS, meshName.toJS].toJS, + null) as JSNumber; + if (result.toDartInt == -1) { + throw Exception( + "Failed to reveal mesh ${meshName} on entity ${entity.toJS}"); + } + } else { + throw Exception( + "Cannot reveal mesh, meshName must be specified when invoking this method"); + } + } + + @override + Future rotateEnd() { + // TODO: implement rotateEnd throw UnimplementedError(); } @override - Future stopAnimation(ThermionEntity entity, int animationIndex) { - // TODO: implement stopAnimation + Future rotateIbl(Matrix3 rotation) async { + final ptr = _module._malloc(9 * 4) as JSNumber; + for (int i = 0; i < 9; i++) { + _module.setValue( + (ptr.toDartInt + (i * 4)).toJS, rotation.storage[i].toJS, "float"); + } + _module.ccall("rotate_ibl", "void", ["void*".toJS, "float*".toJS].toJS, + [_viewer!, ptr].toJS, null); + _module._free(ptr); + } + + @override + Future rotateStart(double x, double y) { + // TODO: implement rotateStart throw UnimplementedError(); } + @override + Future rotateUpdate(double x, double y) { + // TODO: implement rotateUpdate + throw UnimplementedError(); + } + + @override + Future setAnimationFrame( + ThermionEntity entity, int index, int animationFrame) async { + _module.ccall( + "set_animation_frame", + "void", + ["void*".toJS, "int".toJS, "int".toJS, "int".toJS].toJS, + [ + _sceneManager!, + entity.toJS, + index.toJS, + animationFrame.toJS, + ].toJS, + null); + } + + @override + Future setBackgroundImage(String path, {bool fillHeight = false}) async { + _module.ccall( + "set_background_image", + "void", + ["void*".toJS, "string".toJS, "bool".toJS].toJS, + [_viewer!, path.toJS, fillHeight.toJS].toJS, + null); + } + + @override + Future setBackgroundImagePosition(double x, double y, + {bool clamp = false}) async { + _module.ccall( + "set_background_image_position", + "void", + ["void*".toJS, "float".toJS, "float".toJS, "bool".toJS].toJS, + [_viewer!, x.toJS, y.toJS, clamp.toJS].toJS, + null); + } + + @override + Future setBloom(double bloom) async { + _module.ccall("set_bloom", "void", ["void*".toJS, "float".toJS].toJS, + [_viewer!, bloom.toJS].toJS, null); + } + + @override + Future setBoneTransform( + ThermionEntity entity, int boneIndex, Matrix4 transform, + {int skinIndex = 0}) async { + final ptr = _module._malloc(16 * 4) as JSNumber; + for (int i = 0; i < 16; i++) { + _module.setValue( + (ptr.toDartInt + (i * 4)).toJS, transform.storage[i].toJS, "float"); + } + final result = _module.ccall( + "set_bone_transform", + "bool", + ["void*".toJS, "int".toJS, "int".toJS, "int".toJS, "float*".toJS].toJS, + [_sceneManager!, entity.toJS, skinIndex.toJS, boneIndex.toJS, ptr].toJS, + null) as JSBoolean; + _module._free(ptr); + if (!result.toDart) { + throw Exception("Failed to set bone transform"); + } + } + + @override + Future setCamera(ThermionEntity entity, String? name) async { + final result = _module.ccall( + "set_camera", + "bool", + ["void*".toJS, "int".toJS, "string".toJS].toJS, + [_viewer!, entity.toJS, (name ?? "").toJS].toJS, + null) as JSBoolean; + if (!result.toDart) { + throw Exception("Failed to set camera to entity ${entity}"); + } + } + + @override + Future setCameraCulling(double near, double far) async { + _module.ccall( + "set_camera_culling", + "void", + ["void*".toJS, "double".toJS, "double".toJS].toJS, + [_viewer!, near.toJS, far.toJS].toJS, + null); + } + + @override + Future setCameraExposure( + double aperture, double shutterSpeed, double sensitivity) async { + _module.ccall( + "set_camera_exposure", + "void", + ["void*".toJS, "float".toJS, "float".toJS, "float".toJS].toJS, + [ + _viewer!, + aperture.toJS, + shutterSpeed.toJS, + sensitivity.toJS, + ].toJS, + null); + } + + @override + Future setCameraFocalLength(double focalLength) async { + _module.ccall( + "set_camera_focal_length", + "void", + ["void*".toJS, "float".toJS].toJS, + [_viewer!, focalLength.toJS].toJS, + null); + } + + @override + Future setCameraFocusDistance(double focusDistance) async { + _module.ccall( + "set_camera_focus_distance", + "void", + ["void*".toJS, "float".toJS].toJS, + [_viewer!, focusDistance.toJS].toJS, + null); + } + + @override + Future setCameraFov(double degrees, double width, double height) async { + _module.ccall( + "set_camera_fov", + "void", + ["void*".toJS, "float".toJS, "float".toJS].toJS, + [_viewer!, degrees.toJS, (width / height).toJS].toJS, + null); + } + + @override + Future setCameraManipulatorOptions( + {ManipulatorMode mode = ManipulatorMode.ORBIT, + double orbitSpeedX = 0.01, + double orbitSpeedY = 0.01, + double zoomSpeed = 0.01}) async { + _module.ccall( + "set_camera_manipulator_options", + "void", + ["void*".toJS, "int".toJS, "double".toJS, "double".toJS, "double".toJS] + .toJS, + [ + _viewer!, + mode.index.toJS, + orbitSpeedX.toJS, + orbitSpeedY.toJS, + zoomSpeed.toJS + ].toJS, + null); + } + + @override + Future setCameraModelMatrix(List matrix) async { + assert(matrix.length == 16, "Matrix must have 16 elements"); + final ptr = _module._malloc(16 * 8) as JSNumber; + for (int i = 0; i < 16; i++) { + _module.setValue( + (ptr.toDartInt + (i * 8)).toJS, matrix[i].toJS, "double"); + } + _module.ccall("set_camera_model_matrix", "void", + ["void*".toJS, "float*".toJS].toJS, [_viewer!, ptr].toJS, null); + _module._free(ptr); + } + + @override + Future setFrameRate(int framerate) async { + _module.ccall( + "set_frame_interval", + "void", + ["void*".toJS, "float".toJS].toJS, + [_viewer!, (1 / framerate).toJS].toJS, + null); + } + + @override + Future setMainCamera() async { + _module.ccall( + "set_main_camera", "void", ["void*".toJS].toJS, [_viewer!].toJS, null); + } + + @override + Future setMaterialColor(ThermionEntity entity, String meshName, + int materialIndex, double r, double g, double b, double a) async { + final result = _module.ccall( + "set_material_color", + "bool", + [ + "void*".toJS, + "int".toJS, + "string".toJS, + "int".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "float".toJS + ].toJS, + [ + _sceneManager!, + entity.toJS, + meshName.toJS, + materialIndex.toJS, + r.toJS, + g.toJS, + b.toJS, + a.toJS + ].toJS, + null) as JSBoolean; + if (!result.toDart) { + throw Exception("Failed to set material color"); + } + } + + @override + Future setMorphTargetWeights( + ThermionEntity entity, List weights) async { + final numWeights = weights.length; + final ptr = _module._malloc(numWeights * 4) as JSNumber; + for (int i = 0; i < numWeights; i++) { + _module.setValue( + (ptr.toDartInt + (i * 4)).toJS, weights[i].toJS, "float"); + } + final result = _module.ccall( + "set_morph_target_weights", + "bool", + ["void*".toJS, "int".toJS, "float*".toJS, "int".toJS].toJS, + [_sceneManager!, entity.toJS, ptr, numWeights.toJS].toJS, + null) as JSBoolean; + _module._free(ptr); + if (!result.toDart) { + throw Exception("Failed to set morph target weights"); + } + } + + @override + Future setParent(ThermionEntity child, ThermionEntity parent) async { + _module.ccall( + "set_parent", + "void", + ["void*".toJS, "int".toJS, "int".toJS].toJS, + [_sceneManager!, child.toJS, parent.toJS].toJS, + null); + } + + @override + Future setPriority(ThermionEntity entityId, int priority) { + // TODO: implement setPriority + throw UnimplementedError(); + } + + @override + Future setRecording(bool recording) async { + _module.ccall("set_recording", "void", ["void*".toJS, "bool".toJS].toJS, + [_viewer!, recording.toJS].toJS, null); + } + + @override + Future setRecordingOutputDirectory(String outputDirectory) async { + _module.ccall( + "set_recording_output_directory", + "void", + ["void*".toJS, "string".toJS].toJS, + [_viewer!, outputDirectory.toJS].toJS, + null); + } + + Timer? _renderLoop; + + @override + Future setRendering(bool render) async { + if (render && !_rendering) { + _rendering = true; + _renderLoop = Timer.periodic(Duration(microseconds: 16667), (_) { + this.render(); + }); + } else if (!render && _rendering) { + _rendering = false; + _renderLoop?.cancel(); + _renderLoop = null; + } + } + + @override + Future setScale(ThermionEntity entity, double scale) async { + _module.ccall( + "set_scale", + "void", + ["void*".toJS, "int".toJS, "float".toJS].toJS, + [_sceneManager!, entity.toJS, scale.toJS].toJS, + null); + } + + @override + Future setToneMapping(ToneMapper mapper) async { + _module.ccall("set_tone_mapping", "void", ["void*".toJS, "int".toJS].toJS, + [_viewer!, mapper.index.toJS].toJS, null); + } + + @override + Future setTransform(ThermionEntity entity, Matrix4 transform) async { + final ptr = _module._malloc(16 * 4) as JSNumber; + for (int i = 0; i < 16; i++) { + _module.setValue( + (ptr.toDartInt + (i * 4)).toJS, transform.storage[i].toJS, "float"); + } + final result = _module.ccall( + "set_transform", + "bool", + ["void*".toJS, "int".toJS, "float*".toJS].toJS, + [_sceneManager!, entity.toJS, ptr].toJS, + null) as JSBoolean; + _module._free(ptr); + if (!result.toDart) { + throw Exception("Failed to set transform"); + } + } + + @override + Future setViewFrustumCulling(bool enabled) async { + _module.ccall("set_view_frustum_culling", "void", + ["void*".toJS, "bool".toJS].toJS, [_viewer!, enabled.toJS].toJS, null); + } + + @override + Future stopAnimation(ThermionEntity entity, int animationIndex) async { + _module.ccall( + "stop_animation", + "void", + ["void*".toJS, "int".toJS, "int".toJS].toJS, + [_sceneManager!, entity.toJS, animationIndex.toJS].toJS, + null); + } + @override Future stopAnimationByName(ThermionEntity entity, String name) { // TODO: implement stopAnimationByName @@ -1459,13 +1844,4 @@ class ThermionViewerWasm implements ThermionViewer { // TODO: implement zoomUpdate throw UnimplementedError(); } - - final _onDispose = []; - - /// - /// - /// - void onDispose(Future Function() callback) { - _onDispose.add(callback); - } } diff --git a/thermion_dart/native/web/CMakeLists.txt b/thermion_dart/native/web/CMakeLists.txt index f9587ffe..42fdf646 100644 --- a/thermion_dart/native/web/CMakeLists.txt +++ b/thermion_dart/native/web/CMakeLists.txt @@ -49,7 +49,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY build/out) add_executable(${MODULE_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/../src/SceneManager.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/../src/ThermionViewerFFI.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../src/FilamentViewer.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../src/ThermionDartApi.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../src/ThermionDartFFIApi.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../src/StreamBufferAdapter.cpp" @@ -221,10 +221,10 @@ target_link_libraries(${MODULE_NAME} tinyexr ) -add_custom_command(TARGET ${MODULE_NAME} POST_BUILD - COMMAND dart --enable-experiment=native-assets run ffigen --config ffigen/web.yaml WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../ - COMMAND ${CMAKE_COMMAND} -DINPUTFILE=${CMAKE_CURRENT_SOURCE_DIR}/../../lib/thermion_dart/compatibility/web/thermion_dart.g.dart -DOUTPUTFILE=${CMAKE_CURRENT_SOURCE_DIR}/../../lib/thermion_dart/compatibility/web/thermion_dart.g.dart "-DTO_REPLACE=symbol: '" "-DREPLACEMENT=symbol: '_" -P ${CMAKE_CURRENT_SOURCE_DIR}/replace_in_file.cmake - # COMMAND ${CMAKE_COMMAND} -DINPUTFILE=${CMAKE_CURRENT_SOURCE_DIR}/build/build/out/thermion_dart.js -DOUTPUTFILE=${CMAKE_CURRENT_SOURCE_DIR}/build/build/out/thermion_dart.js "-DTO_REPLACE=var moduleRtn" "-DREPLACEMENT=var moduleRtn\;GLctx=moduleArg.ctx" -P ${CMAKE_CURRENT_SOURCE_DIR}/replace_in_file.cmake - VERBATIM -) +# add_custom_command(TARGET ${MODULE_NAME} POST_BUILD +# COMMAND dart --enable-experiment=native-assets run ffigen --config ffigen/web.yaml WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../ +# COMMAND ${CMAKE_COMMAND} -DINPUTFILE=${CMAKE_CURRENT_SOURCE_DIR}/../../lib/thermion_dart/compatibility/web/thermion_dart.g.dart -DOUTPUTFILE=${CMAKE_CURRENT_SOURCE_DIR}/../../lib/thermion_dart/compatibility/web/thermion_dart.g.dart "-DTO_REPLACE=symbol: '" "-DREPLACEMENT=symbol: '_" -P ${CMAKE_CURRENT_SOURCE_DIR}/replace_in_file.cmake +# # COMMAND ${CMAKE_COMMAND} -DINPUTFILE=${CMAKE_CURRENT_SOURCE_DIR}/build/build/out/thermion_dart.js -DOUTPUTFILE=${CMAKE_CURRENT_SOURCE_DIR}/build/build/out/thermion_dart.js "-DTO_REPLACE=var moduleRtn" "-DREPLACEMENT=var moduleRtn\;GLctx=moduleArg.ctx" -P ${CMAKE_CURRENT_SOURCE_DIR}/replace_in_file.cmake +# VERBATIM +# ) diff --git a/thermion_flutter/thermion_flutter/lib/thermion_flutter.dart b/thermion_flutter/thermion_flutter/lib/thermion_flutter.dart index d48d5747..efb90338 100644 --- a/thermion_flutter/thermion_flutter/lib/thermion_flutter.dart +++ b/thermion_flutter/thermion_flutter/lib/thermion_flutter.dart @@ -3,5 +3,4 @@ library thermion_flutter; export 'thermion/thermion_flutter_plugin.dart'; export 'thermion/widgets/thermion_widget.dart'; export 'thermion/widgets/camera/gestures/thermion_gesture_detector.dart'; - export 'package:thermion_dart/thermion_dart.dart'; diff --git a/thermion_flutter/thermion_flutter_web/lib/thermion_flutter_web.dart b/thermion_flutter/thermion_flutter_web/lib/thermion_flutter_web.dart index c5a20055..5cad8d52 100644 --- a/thermion_flutter/thermion_flutter_web/lib/thermion_flutter_web.dart +++ b/thermion_flutter/thermion_flutter_web/lib/thermion_flutter_web.dart @@ -1,9 +1,9 @@ +import 'package:thermion_dart/thermion_dart/compatibility/web/interop/thermion_viewer_wasm.dart'; import 'package:thermion_dart/thermion_dart/thermion_viewer.dart'; import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart'; import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; -import 'package:thermion_dart/thermion_dart/compatibility/web/interop/thermion_dart_js_extension_type.dart'; -import 'package:thermion_dart/thermion_dart/compatibility/web/interop/js_interop_filament_viewer.dart'; +import 'package:web/web.dart'; class ThermionFlutterWebPlugin extends ThermionFlutterPlatform { static void registerWith(Registrar registrar) { @@ -12,30 +12,31 @@ class ThermionFlutterWebPlugin extends ThermionFlutterPlatform { @override Future createTexture( - int width, int height, int offsetLeft, int offsetRight) async {} - - @override - Future destroyTexture(ThermionFlutterTexture texture) async {} - - @override - void dispose() { - // TODO: implement dispose + int width, int height, int offsetLeft, int offsetRight) async { + return ThermionFlutterTexture(null, null, 0, 0, null); } @override - Future initialize({String? uberArchivePath}) async { - print("Creating viewer in web plugin"); - viewer = JsInteropThermionViewerFFI("filamentViewer"); - print("Waiting for initialized"); - await viewer.initialized; - print("int complete"); + Future destroyTexture(ThermionFlutterTexture texture) async { + // noop } @override Future resizeTexture(ThermionFlutterTexture texture, - int width, int height, int offsetLeft, int offsetRight) async {} + int width, int height, int offsetLeft, int offsetRight) async { + return ThermionFlutterTexture(null, null, 0, 0, null); + } - @override - // TODO: implement viewer - late final ThermionViewer viewer; + Future createViewer({String? uberArchivePath}) async { + final canvas = document.getElementById("canvas") as HTMLCanvasElement; + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + + var width = window.innerWidth; + var height = window.innerHeight; + + var viewer = ThermionViewerWasm(); + await viewer.initialize(width, height, uberArchivePath: uberArchivePath); + return viewer; + } } From 6902c27d024da6538dea4170a055cd75a85781ec Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 21 Jun 2024 11:10:28 +0800 Subject: [PATCH 35/51] fix: catch exception if gizmo unavailable in ThermionGestureDestectorDesktop --- .../gestures/thermion_gesture_detector_desktop.dart | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/thermion_flutter/thermion_flutter/lib/thermion/widgets/camera/gestures/thermion_gesture_detector_desktop.dart b/thermion_flutter/thermion_flutter/lib/thermion/widgets/camera/gestures/thermion_gesture_detector_desktop.dart index 38af660b..e01e96b3 100644 --- a/thermion_flutter/thermion_flutter/lib/thermion/widgets/camera/gestures/thermion_gesture_detector_desktop.dart +++ b/thermion_flutter/thermion_flutter/lib/thermion/widgets/camera/gestures/thermion_gesture_detector_desktop.dart @@ -1,5 +1,5 @@ import 'dart:async'; - +import 'package:logging/logging.dart'; import 'package:thermion_dart/thermion_dart/thermion_viewer.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -52,7 +52,8 @@ class ThermionGestureDetectorDesktop extends StatefulWidget { class _ThermionGestureDetectorDesktopState extends State { - /// + final _logger = Logger("_ThermionGestureDetectorDesktopState"); + /// /// // ignore: unused_field @@ -65,7 +66,12 @@ class _ThermionGestureDetectorDesktopState @override void initState() { super.initState(); - _gizmo = widget.controller.gizmo; + try { + _gizmo = widget.controller.gizmo; + } catch (err) { + _logger.warning( + "Failed to get gizmo. If you are running on WASM, this is expected"); + } } @override From cc3020c26875127b347d4f36bd074f07ab797681 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 21 Jun 2024 11:15:08 +0800 Subject: [PATCH 36/51] remove dependency_overrides --- examples/flutter/example/pubspec.yaml | 3 +-- examples/flutter/quickstart/pubspec.yaml | 6 +----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/examples/flutter/example/pubspec.yaml b/examples/flutter/example/pubspec.yaml index 1985ba79..218dc262 100644 --- a/examples/flutter/example/pubspec.yaml +++ b/examples/flutter/example/pubspec.yaml @@ -13,7 +13,6 @@ dependencies: sdk: flutter path_provider: thermion_flutter: - path: ../../../thermion_flutter/thermion_flutter cupertino_icons: ^1.0.2 web: @@ -36,4 +35,4 @@ flutter: - assets/BusterDrone/ - assets/BusterDrone/textures/ - assets/FlightHelmet/ - \ No newline at end of file + diff --git a/examples/flutter/quickstart/pubspec.yaml b/examples/flutter/quickstart/pubspec.yaml index 0f35e5bb..a0db16e2 100644 --- a/examples/flutter/quickstart/pubspec.yaml +++ b/examples/flutter/quickstart/pubspec.yaml @@ -30,11 +30,7 @@ environment: dependencies: flutter: sdk: flutter - thermion_flutter: - path: ../../../thermion_flutter/thermion_flutter - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. + thermion_flutter: cupertino_icons: ^1.0.8 vector_math: ^2.1.4 From fd19fc1c6810c1e79d930d6564e13bc62c858c73 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 21 Jun 2024 11:18:05 +0800 Subject: [PATCH 37/51] fix: use preserveDrawingBuffer=true on web using createImageFromImageBitmap to import the canvas directly into a Flutter app results in flickering unless this is set to true (as the drawing buffer is otherwise cleared after each render, which may not line up with Flutter's ticker --- thermion_dart/native/web/src/cpp/ThermionDartWebApi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thermion_dart/native/web/src/cpp/ThermionDartWebApi.cpp b/thermion_dart/native/web/src/cpp/ThermionDartWebApi.cpp index a55270ca..0c441841 100644 --- a/thermion_dart/native/web/src/cpp/ThermionDartWebApi.cpp +++ b/thermion_dart/native/web/src/cpp/ThermionDartWebApi.cpp @@ -125,7 +125,7 @@ extern "C" attr.stencil = EM_FALSE; attr.antialias = EM_FALSE; attr.explicitSwapControl = EM_FALSE; - attr.preserveDrawingBuffer = EM_FALSE; + attr.preserveDrawingBuffer = EM_TRUE; attr.proxyContextToMainThread = EMSCRIPTEN_WEBGL_CONTEXT_PROXY_DISALLOW; attr.enableExtensionsByDefault = EM_TRUE; attr.renderViaOffscreenBackBuffer = EM_FALSE; From 78a758f5d421fd2aeec5e37f030f181a11598849 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 21 Jun 2024 11:26:34 +0800 Subject: [PATCH 38/51] chore(release): publish packages - thermion_dart@0.1.0+1 - thermion_flutter@0.1.1+1 - thermion_flutter_web@0.0.1+1 - thermion_flutter_platform_interface@0.1.0+1 - thermion_flutter_ffi@0.1.0+1 --- CHANGELOG.md | 46 +++++++++++++++++++ thermion_dart/CHANGELOG.md | 5 ++ thermion_dart/pubspec.yaml | 2 +- .../thermion_flutter/CHANGELOG.md | 5 ++ .../thermion_flutter/pubspec.yaml | 10 ++-- .../thermion_flutter_ffi/CHANGELOG.md | 4 ++ .../thermion_flutter_ffi/pubspec.yaml | 6 +-- .../CHANGELOG.md | 4 ++ .../pubspec.yaml | 4 +- .../thermion_flutter_web/CHANGELOG.md | 4 ++ .../thermion_flutter_web/pubspec.yaml | 6 +-- 11 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..13127bc6 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,46 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## 2024-06-21 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`thermion_dart` - `v0.1.0+1`](#thermion_dart---v0101) + - [`thermion_flutter` - `v0.1.1+1`](#thermion_flutter---v0111) + - [`thermion_flutter_web` - `v0.0.1+1`](#thermion_flutter_web---v0011) + - [`thermion_flutter_platform_interface` - `v0.1.0+1`](#thermion_flutter_platform_interface---v0101) + - [`thermion_flutter_ffi` - `v0.1.0+1`](#thermion_flutter_ffi---v0101) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `thermion_flutter_platform_interface` - `v0.1.0+1` + - `thermion_flutter_ffi` - `v0.1.0+1` + +--- + +#### `thermion_dart` - `v0.1.0+1` + + - **REFACTOR**: export ThermionViewerWasm for web and hide FFI/WASM version. + - **FIX**: use preserveDrawingBuffer=true on web. + +#### `thermion_flutter` - `v0.1.1+1` + + - **REFACTOR**: export ThermionViewerWasm for web and hide FFI/WASM version. + - **FIX**: catch exception if gizmo unavailable in ThermionGestureDestectorDesktop. + +#### `thermion_flutter_web` - `v0.0.1+1` + + - **REFACTOR**: export ThermionViewerWasm for web and hide FFI/WASM version. + diff --git a/thermion_dart/CHANGELOG.md b/thermion_dart/CHANGELOG.md index feb7a4bf..f1466801 100644 --- a/thermion_dart/CHANGELOG.md +++ b/thermion_dart/CHANGELOG.md @@ -1,2 +1,7 @@ +## 0.1.0+1 + + - **REFACTOR**: export ThermionViewerWasm for web and hide FFI/WASM version. + - **FIX**: use preserveDrawingBuffer=true on web. + ## 0.0.1 * First release of Dart-only package diff --git a/thermion_dart/pubspec.yaml b/thermion_dart/pubspec.yaml index 24f387e5..6fac110b 100644 --- a/thermion_dart/pubspec.yaml +++ b/thermion_dart/pubspec.yaml @@ -1,6 +1,6 @@ name: thermion_dart description: 3D rendering toolkit for Dart. -version: 0.1.0 +version: 0.1.0+1 homepage: https://docs.page/nmfisher/thermion repository: https://github.com/nmfisher/thermion diff --git a/thermion_flutter/thermion_flutter/CHANGELOG.md b/thermion_flutter/thermion_flutter/CHANGELOG.md index a285b88c..000548c4 100644 --- a/thermion_flutter/thermion_flutter/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.1.1+1 + + - **REFACTOR**: export ThermionViewerWasm for web and hide FFI/WASM version. + - **FIX**: catch exception if gizmo unavailable in ThermionGestureDestectorDesktop. + ## 0.1.0 * [ThermionFlutterPlugin] is now static and [dispose] has been removed. Call [createViewer] to obtain an instance of [ThermionViewer]. If you need to release all resources, call [dispose] on [ThermionViewer] * Fixed memory leaks diff --git a/thermion_flutter/thermion_flutter/pubspec.yaml b/thermion_flutter/thermion_flutter/pubspec.yaml index a5454dd9..bf1b63b0 100644 --- a/thermion_flutter/thermion_flutter/pubspec.yaml +++ b/thermion_flutter/thermion_flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: thermion_flutter description: Flutter plugin for 3D rendering with the Thermion toolkit. -version: 0.1.0-pre +version: 0.1.1+1 homepage: https://docs.page/nmfisher/thermion repository: https://github.com/nmfisher/thermion @@ -17,10 +17,10 @@ dependencies: plugin_platform_interface: ^2.0.0 ffi: ^2.1.2 animation_tools_dart: ^0.0.4 - thermion_dart: ^0.0.4 - thermion_flutter_platform_interface: ^0.0.1 - thermion_flutter_ffi: ^0.0.1 - thermion_flutter_web: ^0.0.1 + thermion_dart: ^0.1.0+1 + thermion_flutter_platform_interface: ^0.1.0+1 + thermion_flutter_ffi: ^0.1.0+1 + thermion_flutter_web: ^0.0.1+1 dev_dependencies: flutter_test: diff --git a/thermion_flutter/thermion_flutter_ffi/CHANGELOG.md b/thermion_flutter/thermion_flutter_ffi/CHANGELOG.md index feb7a4bf..edcb4280 100644 --- a/thermion_flutter/thermion_flutter_ffi/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter_ffi/CHANGELOG.md @@ -1,2 +1,6 @@ +## 0.1.0+1 + + - Update a dependency to the latest release. + ## 0.0.1 * First release of Dart-only package diff --git a/thermion_flutter/thermion_flutter_ffi/pubspec.yaml b/thermion_flutter/thermion_flutter_ffi/pubspec.yaml index 3ad22514..0911a020 100644 --- a/thermion_flutter/thermion_flutter_ffi/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_ffi/pubspec.yaml @@ -1,7 +1,7 @@ name: thermion_flutter_ffi description: An FFI interface for the thermion_flutter plugin (all platforms except web). repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter -version: 0.1.0 +version: 0.1.0+1 environment: sdk: ">=3.3.0 <4.0.0" @@ -22,8 +22,8 @@ dependencies: flutter: sdk: flutter plugin_platform_interface: ^2.1.0 - thermion_flutter_platform_interface: ^0.1.0 - thermion_dart: ^0.1.0 + thermion_flutter_platform_interface: ^0.1.0+1 + thermion_dart: ^0.1.0+1 dev_dependencies: flutter_test: diff --git a/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md b/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md index 24e9bcce..258f5a73 100644 --- a/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md @@ -1,2 +1,6 @@ +## 0.1.0+1 + + - Update a dependency to the latest release. + ## 0.0.1 * First release diff --git a/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml b/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml index 5f170f65..4863ce86 100644 --- a/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml @@ -1,7 +1,7 @@ name: thermion_flutter_platform_interface description: A common platform interface for the thermion_flutter plugin. repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter -version: 0.1.0 +version: 0.1.0+1 environment: sdk: ">=3.3.0 <4.0.0" @@ -11,7 +11,7 @@ dependencies: flutter: sdk: flutter plugin_platform_interface: ^2.1.0 - thermion_dart: ^0.0.1-pre2 + thermion_dart: ^0.1.0+1 dev_dependencies: flutter_test: diff --git a/thermion_flutter/thermion_flutter_web/CHANGELOG.md b/thermion_flutter/thermion_flutter_web/CHANGELOG.md index feb7a4bf..b9bec338 100644 --- a/thermion_flutter/thermion_flutter_web/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter_web/CHANGELOG.md @@ -1,2 +1,6 @@ +## 0.0.1+1 + + - **REFACTOR**: export ThermionViewerWasm for web and hide FFI/WASM version. + ## 0.0.1 * First release of Dart-only package diff --git a/thermion_flutter/thermion_flutter_web/pubspec.yaml b/thermion_flutter/thermion_flutter_web/pubspec.yaml index f96ff058..218e81c6 100644 --- a/thermion_flutter/thermion_flutter_web/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_web/pubspec.yaml @@ -1,7 +1,7 @@ name: thermion_flutter_web description: A web platform interface for the thermion_flutter plugin. repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter -version: 0.0.1 +version: 0.0.1+1 environment: sdk: ">=3.3.0 <4.0.0" @@ -20,8 +20,8 @@ dependencies: sdk: flutter plugin_platform_interface: ^2.1.0 web: ^0.5.1 - thermion_dart: ^0.0.1-pre - thermion_flutter_platform_interface: ^0.0.1-pre + thermion_dart: ^0.1.0+1 + thermion_flutter_platform_interface: ^0.1.0+1 flutter_web_plugins: sdk: flutter From 55b5c7068e9405988e01a254ce8586b39ec7b2b4 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 21 Jun 2024 11:30:23 +0800 Subject: [PATCH 39/51] fix: add logging dependency to thermion_flutter --- thermion_flutter/thermion_flutter/pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/thermion_flutter/thermion_flutter/pubspec.yaml b/thermion_flutter/thermion_flutter/pubspec.yaml index bf1b63b0..8a91cc1b 100644 --- a/thermion_flutter/thermion_flutter/pubspec.yaml +++ b/thermion_flutter/thermion_flutter/pubspec.yaml @@ -21,6 +21,7 @@ dependencies: thermion_flutter_platform_interface: ^0.1.0+1 thermion_flutter_ffi: ^0.1.0+1 thermion_flutter_web: ^0.0.1+1 + logging: ^1.2.0 dev_dependencies: flutter_test: From 81ffbdd82462a32b155e4800b8b9e0fbf98e1470 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 21 Jun 2024 11:44:56 +0800 Subject: [PATCH 40/51] fix: update Flutter example project to use new API fix: don't keep example project Podfile.lock in repo --- .../flutter/example/lib/example_viewport.dart | 16 +- examples/flutter/example/lib/main.dart | 207 +++++++++--------- .../example/lib/menus/asset_submenu.dart | 44 ++-- .../example/lib/menus/camera_submenu.dart | 34 +-- .../example/lib/menus/controller_menu.dart | 45 ++-- .../example/lib/menus/rendering_submenu.dart | 20 +- .../flutter/example/lib/menus/scene_menu.dart | 8 +- examples/flutter/example/macos/Podfile.lock | 29 --- examples/flutter/example/pubspec.yaml | 13 ++ .../lib/thermion_flutter.dart | 1 + 10 files changed, 197 insertions(+), 220 deletions(-) delete mode 100644 examples/flutter/example/macos/Podfile.lock diff --git a/examples/flutter/example/lib/example_viewport.dart b/examples/flutter/example/lib/example_viewport.dart index 4b48ea6c..4be74ea2 100644 --- a/examples/flutter/example/lib/example_viewport.dart +++ b/examples/flutter/example/lib/example_viewport.dart @@ -1,36 +1,34 @@ import 'package:flutter/widgets.dart'; -import 'package:thermion_flutter/filament/widgets/camera/entity_controller_mouse_widget.dart'; -import 'package:thermion_flutter/filament/widgets/camera/gestures/filament_gesture_detector.dart'; -import 'package:thermion_flutter/filament/widgets/filament_widget.dart'; +import 'package:thermion_flutter/thermion/widgets/camera/entity_controller_mouse_widget.dart'; import 'package:thermion_flutter/thermion_flutter.dart'; import 'package:thermion_dart/thermion_dart/entities/entity_transform_controller.dart'; class ExampleViewport extends StatelessWidget { - final ThermionFlutterPlugin? controller; + final ThermionViewer? viewer; final EntityTransformController? entityTransformController; final EdgeInsets padding; final FocusNode keyboardFocusNode; const ExampleViewport( {super.key, - required this.controller, + required this.viewer, required this.padding, required this.keyboardFocusNode, this.entityTransformController}); @override Widget build(BuildContext context) { - return controller != null + return viewer != null ? Padding( padding: padding, child: EntityTransformMouseControllerWidget( transformController: entityTransformController, - child: FilamentGestureDetector( + child: ThermionGestureDetector( showControlOverlay: true, - controller: controller!.viewer, + controller: viewer!, child: ThermionWidget( - plugin: controller!, + viewer: viewer!, )))) : Container(); } diff --git a/examples/flutter/example/lib/main.dart b/examples/flutter/example/lib/main.dart index b5cbf62c..4ab80781 100644 --- a/examples/flutter/example/lib/main.dart +++ b/examples/flutter/example/lib/main.dart @@ -1,14 +1,10 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:thermion_flutter/filament/widgets/debug/entity_list_widget.dart'; -import 'package:thermion_flutter_example/camera_matrix_overlay.dart'; +import 'package:thermion_flutter/thermion/widgets/debug/entity_list_widget.dart'; import 'package:thermion_flutter_example/menus/controller_menu.dart'; import 'package:thermion_flutter_example/example_viewport.dart'; -import 'package:thermion_dart/thermion_dart/entities/entity_transform_controller.dart'; import 'package:thermion_flutter_example/menus/scene_menu.dart'; import 'package:thermion_flutter/thermion_flutter.dart'; -import 'package:thermion_flutter_example/picker_result_widget.dart'; - const loadDefaultScene = bool.hasEnvironment('--load-default-scene'); @@ -38,8 +34,7 @@ class _MyAppState extends State with SingleTickerProviderStateMixin { bodyMedium: TextStyle(fontSize: 12))), // showPerformanceOverlay: true, home: const Scaffold( - backgroundColor: Color(0x00000000), - body: ExampleWidget())); + backgroundColor: Color(0x00000000), body: ExampleWidget())); } } @@ -55,7 +50,7 @@ class ExampleWidget extends StatefulWidget { enum MenuType { controller, assets, camera, misc } class ExampleWidgetState extends State { - final _plugin = ThermionFlutterPlugin(); + ThermionViewer? _viewer; EdgeInsets _viewportMargin = EdgeInsets.zero; @@ -99,105 +94,103 @@ class ExampleWidgetState extends State { @override Widget build(BuildContext context) { - return FutureBuilder( - future: _plugin.initialized, - builder: (_, AsyncSnapshot initialized) { - var isInitialized = initialized.data == true; - - return Stack( - fit: StackFit.expand, - children: [ - if (isInitialized) - Positioned.fill( - child: ExampleViewport( - controller: isInitialized ? _plugin : null, - entityTransformController: _transformController, - padding: _viewportMargin, - keyboardFocusNode: _sharedFocusNode), + return Stack(fit: StackFit.expand, children: [ + if (_viewer != null) + Positioned.fill( + child: ExampleViewport( + viewer: _viewer!, + entityTransformController: _transformController, + padding: _viewportMargin, + keyboardFocusNode: _sharedFocusNode), + ), + Positioned( + bottom: 30, + left: 0, + right: 10, + height: 30, + child: Container( + height: 30, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30), + color: Colors.white.withOpacity(0.25), ), - Positioned( - bottom: 30, - left: 0, - right: 10, - height: 30, - child: Container( - height: 30, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(30), - color: Colors.white.withOpacity(0.25), - ), - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - ControllerMenu( - sharedFocusNode: _sharedFocusNode, - controller: _plugin, - onToggleViewport: () { - setState(() { - _viewportMargin = - (_viewportMargin == EdgeInsets.zero) - ? const EdgeInsets.all(30) - : EdgeInsets.zero; - }); - }, - onControllerDestroyed: () {}, - onControllerCreated: () {}), - SceneMenu( - sharedFocusNode: _sharedFocusNode, - controller: _plugin, - ), - GestureDetector( - onTap: () async { - await _plugin.viewer.loadGlb( - 'assets/shapes/shapes.glb', - numInstances: 1); - }, - child: Container( - color: Colors.transparent, - child: const Text("shapes.glb"))), - const SizedBox(width: 5), - GestureDetector( - onTap: () async { - await _plugin.viewer.loadGlb('assets/1.glb'); - }, - child: Container( - color: Colors.transparent, - child: const Text("1.glb"))), - const SizedBox(width: 5), - GestureDetector( - onTap: () async { - await _plugin.viewer.loadGlb('assets/2.glb'); - }, - child: Container( - color: Colors.transparent, - child: const Text("2.glb"))), - const SizedBox(width: 5), - GestureDetector( - onTap: () async { - await _plugin.viewer.loadGlb('assets/3.glb'); - }, - child: Container( - color: Colors.transparent, - child: const Text("3.glb"))), - Expanded(child: Container()), - ]))), - if (isInitialized) ...[ - Positioned(top:10, left:10, width:200, height:200, child:Container( - child:EntityListWidget(controller: _plugin.viewer))), - // Padding( - // padding: const EdgeInsets.only(top: 10, left: 20, right: 20), - // child: ValueListenableBuilder( - // valueListenable: showProjectionMatrices, - // builder: (ctx, value, child) => CameraMatrixOverlay( - // controller: _plugin.viewer, showProjectionMatrices: value)), - // ), - // Align( - // alignment: Alignment.topRight, - // child: PickerResultWidget(controller: _plugin.viewer), - // ) - ] - ]); - }); + padding: const EdgeInsets.symmetric(horizontal: 10), + child: + Row(crossAxisAlignment: CrossAxisAlignment.center, children: [ + ViewerMenu( + sharedFocusNode: _sharedFocusNode, + viewer: _viewer, + onToggleViewport: () { + setState(() { + _viewportMargin = (_viewportMargin == EdgeInsets.zero) + ? const EdgeInsets.all(30) + : EdgeInsets.zero; + }); + }, + onViewerDestroyed: () { + setState(() { + _viewer = null; + }); + }, + onViewerCreated: (v) { + setState(() { + _viewer = v; + }); + }), + SceneMenu( + sharedFocusNode: _sharedFocusNode, + controller: _viewer, + ), + GestureDetector( + onTap: () async { + await _viewer! + .loadGlb('assets/shapes/shapes.glb', numInstances: 1); + }, + child: Container( + color: Colors.transparent, + child: const Text("shapes.glb"))), + const SizedBox(width: 5), + GestureDetector( + onTap: () async { + await _viewer!.loadGlb('assets/1.glb'); + }, + child: Container( + color: Colors.transparent, child: const Text("1.glb"))), + const SizedBox(width: 5), + GestureDetector( + onTap: () async { + await _viewer!.loadGlb('assets/2.glb'); + }, + child: Container( + color: Colors.transparent, child: const Text("2.glb"))), + const SizedBox(width: 5), + GestureDetector( + onTap: () async { + await _viewer!.loadGlb('assets/3.glb'); + }, + child: Container( + color: Colors.transparent, child: const Text("3.glb"))), + Expanded(child: Container()), + ]))), + if (_viewer != null) ...[ + Positioned( + top: 10, + left: 10, + width: 200, + height: 200, + child: Container(child: EntityListWidget(controller: _viewer!))), + // Padding( + // padding: const EdgeInsets.only(top: 10, left: 20, right: 20), + // child: ValueListenableBuilder( + // valueListenable: showProjectionMatrices, + // builder: (ctx, value, child) => CameraMatrixOverlay( + // controller: _viewer!, showProjectionMatrices: value)), + // ), + // Align( + // alignment: Alignment.topRight, + // child: PickerResultWidget(controller: _viewer!), + // ) + ] + ]); } } diff --git a/examples/flutter/example/lib/menus/asset_submenu.dart b/examples/flutter/example/lib/menus/asset_submenu.dart index 3ddc3d08..78705f9c 100644 --- a/examples/flutter/example/lib/menus/asset_submenu.dart +++ b/examples/flutter/example/lib/menus/asset_submenu.dart @@ -8,8 +8,8 @@ import 'package:vector_math/vector_math_64.dart' as v; import 'package:thermion_dart/thermion_dart/thermion_viewer.dart'; class AssetSubmenu extends StatefulWidget { - final ThermionFlutterPlugin controller; - const AssetSubmenu({super.key, required this.controller}); + final ThermionViewer viewer; + const AssetSubmenu({super.key, required this.viewer}); @override State createState() => _AssetSubmenuState(); @@ -26,8 +26,8 @@ class _AssetSubmenuState extends State { MenuItemButton( closeOnActivate: false, onPressed: () async { - var entity = await widget.controller.viewer.getChildEntity( - widget.controller.viewer.scene.listEntities().last, "Cylinder"); + var entity = await widget.viewer.getChildEntity( + widget.viewer.scene.listEntities().last, "Cylinder"); await showDialog( context: context, builder: (BuildContext context) { @@ -39,8 +39,8 @@ class _AssetSubmenuState extends State { child: const Text('Find Cylinder entity by name')), MenuItemButton( onPressed: () async { - widget.controller.viewer.setPosition( - widget.controller.viewer.scene.listEntities().last, + widget.viewer.setPosition( + widget.viewer.scene.listEntities().last, 1.0, 1.0, -1.0); @@ -50,8 +50,8 @@ class _AssetSubmenuState extends State { MenuItemButton( onPressed: () async { final color = Colors.purple; - widget.controller.viewer.setMaterialColor( - widget.controller.viewer.scene.listEntities().last, + widget.viewer.setMaterialColor( + widget.viewer.scene.listEntities().last, "Cone", 0, color.red / 255.0, @@ -85,14 +85,14 @@ class _AssetSubmenuState extends State { -1.0, ]; var indices = [0, 1, 2, 2, 3, 0]; - var geom = await widget.controller.viewer.createGeometry( + var geom = await widget.viewer.createGeometry( verts, indices, materialPath: "asset://assets/solidcolor.filamat"); }, child: const Text("Quad")), MenuItemButton( onPressed: () async { - await widget.controller.viewer.createGeometry([ + await widget.viewer.createGeometry([ 0, 0, 0, @@ -118,14 +118,14 @@ class _AssetSubmenuState extends State { _geometrySubmenu(), MenuItemButton( onPressed: () async { - await widget.controller.viewer.addLight( + await widget.viewer.addLight( LightType.DIRECTIONAL, 6500, 100000, 0, 1, 0, 0, -1, 0); }, child: const Text("Add directional light"), ), MenuItemButton( onPressed: () async { - await widget.controller.viewer.addLight( + await widget.viewer.addLight( LightType.POINT, 6500, 100000, 0, 1, 0, 0, -1, 0, falloffRadius: 1.0); }, @@ -133,7 +133,7 @@ class _AssetSubmenuState extends State { ), MenuItemButton( onPressed: () async { - await widget.controller.viewer.addLight( + await widget.viewer.addLight( LightType.SPOT, 6500, 1000000, 0, 0, 0, 0, 1, 0, spotLightConeInner: 0.1, spotLightConeOuter: 0.4, @@ -143,26 +143,26 @@ class _AssetSubmenuState extends State { ), MenuItemButton( onPressed: () async { - await widget.controller.viewer.clearLights(); + await widget.viewer.clearLights(); }, child: const Text("Clear lights"), ), MenuItemButton( onPressed: () { final color = const Color(0xAA73C9FA); - widget.controller.viewer.setBackgroundColor(color.red / 255.0, + widget.viewer.setBackgroundColor(color.red / 255.0, color.green / 255.0, color.blue / 255.0, 1.0); }, child: const Text("Set background color")), MenuItemButton( onPressed: () { - widget.controller.viewer + widget.viewer .setBackgroundImage('assets/background.ktx'); }, child: const Text("Load background image")), MenuItemButton( onPressed: () { - widget.controller.viewer.setBackgroundImage( + widget.viewer.setBackgroundImage( 'assets/background.ktx', fillHeight: true); }, @@ -170,9 +170,9 @@ class _AssetSubmenuState extends State { MenuItemButton( onPressed: () { if (ExampleWidgetState.hasSkybox) { - widget.controller.viewer.removeSkybox(); + widget.viewer.removeSkybox(); } else { - widget.controller.viewer + widget.viewer .loadSkybox('assets/default_env/default_env_skybox.ktx'); } ExampleWidgetState.hasSkybox = !ExampleWidgetState.hasSkybox; @@ -182,18 +182,18 @@ class _AssetSubmenuState extends State { : 'Load skybox')), MenuItemButton( onPressed: () { - widget.controller.viewer + widget.viewer .loadIbl('assets/default_env/default_env_ibl.ktx'); }, child: const Text('Load IBL')), MenuItemButton( onPressed: () { - widget.controller.viewer.removeIbl(); + widget.viewer.removeIbl(); }, child: const Text('Remove IBL')), MenuItemButton( onPressed: () async { - await widget.controller.viewer.clearEntities(); + await widget.viewer.clearEntities(); }, child: const Text('Clear assets')), ], diff --git a/examples/flutter/example/lib/menus/camera_submenu.dart b/examples/flutter/example/lib/menus/camera_submenu.dart index 5113264e..30033684 100644 --- a/examples/flutter/example/lib/menus/camera_submenu.dart +++ b/examples/flutter/example/lib/menus/camera_submenu.dart @@ -8,8 +8,8 @@ import 'package:thermion_dart/thermion_dart/thermion_viewer.dart'; import 'package:thermion_flutter_example/main.dart'; class CameraSubmenu extends StatefulWidget { - final ThermionFlutterPlugin controller; - const CameraSubmenu({super.key, required this.controller}); + final ThermionViewer viewer; + const CameraSubmenu({super.key, required this.viewer}); @override State createState() => _CameraSubmenuState(); @@ -22,10 +22,10 @@ class _CameraSubmenuState extends State { @override void initState() { super.initState(); - widget.controller.viewer.initialized.then((_) { - widget.controller.viewer.getCameraCullingNear().then((v) { + widget.viewer.initialized.then((_) { + widget.viewer.getCameraCullingNear().then((v) { _near = v; - widget.controller.viewer.getCameraCullingFar().then((v) { + widget.viewer.getCameraCullingFar().then((v) { _far = v; setState(() {}); }); @@ -56,7 +56,7 @@ class _CameraSubmenuState extends State { menuChildren: [1.0, 7.0, 14.0, 28.0, 56.0] .map((v) => MenuItemButton( onPressed: () { - widget.controller.viewer.setCameraFocalLength(v); + widget.viewer.setCameraFocalLength(v); }, child: Text( v.toStringAsFixed(2), @@ -71,7 +71,7 @@ class _CameraSubmenuState extends State { _near = v; print("Setting camera culling to $_near $_far!"); - widget.controller.viewer.setCameraCulling(_near!, _far!); + widget.viewer.setCameraCulling(_near!, _far!); }, child: Text( v.toStringAsFixed(2), @@ -85,7 +85,7 @@ class _CameraSubmenuState extends State { onPressed: () { _far = v; print("Setting camera culling to $_near! $_far"); - widget.controller.viewer.setCameraCulling(_near!, _far!); + widget.viewer.setCameraCulling(_near!, _far!); }, child: Text( v.toStringAsFixed(2), @@ -95,21 +95,21 @@ class _CameraSubmenuState extends State { child: const Text("Set far")), MenuItemButton( onPressed: () async { - widget.controller.viewer.setCameraPosition(1.0, 1.0, -1.0); + widget.viewer.setCameraPosition(1.0, 1.0, -1.0); }, child: const Text('Set position to 1, 1, -1 (leave rotation as-is)'), ), MenuItemButton( onPressed: () async { - widget.controller.viewer.setCameraPosition(0.0, 0.0, 0.0); - widget.controller.viewer.setCameraRotation( + widget.viewer.setCameraPosition(0.0, 0.0, 0.0); + widget.viewer.setCameraRotation( v.Quaternion.axisAngle(v.Vector3(0, 0.0, 1.0), 0.0)); }, child: const Text('Move to 0,0,0, facing towards 0,0,-1'), ), MenuItemButton( onPressed: () { - widget.controller.viewer.setCameraRotation( + widget.viewer.setCameraRotation( v.Quaternion.axisAngle(v.Vector3(0, 1, 0), pi / 4)); }, child: const Text("Rotate camera 45 degrees around y axis"), @@ -118,7 +118,7 @@ class _CameraSubmenuState extends State { onPressed: () { ExampleWidgetState.frustumCulling = !ExampleWidgetState.frustumCulling; - widget.controller.viewer + widget.viewer .setViewFrustumCulling(ExampleWidgetState.frustumCulling); }, child: Text( @@ -128,7 +128,7 @@ class _CameraSubmenuState extends State { closeOnActivate: false, onPressed: () async { var projMatrix = - await widget.controller.viewer.getCameraProjectionMatrix(); + await widget.viewer.getCameraProjectionMatrix(); await showDialog( context: context, builder: (ctx) { @@ -147,7 +147,7 @@ class _CameraSubmenuState extends State { menuChildren: ManipulatorMode.values.map((mm) { return MenuItemButton( onPressed: () { - widget.controller.viewer.setCameraManipulatorOptions( + widget.viewer.setCameraManipulatorOptions( mode: mm, orbitSpeedX: ExampleWidgetState.orbitSpeedX, orbitSpeedY: ExampleWidgetState.orbitSpeedY, @@ -169,7 +169,7 @@ class _CameraSubmenuState extends State { return MenuItemButton( onPressed: () { ExampleWidgetState.zoomSpeed = speed; - widget.controller.viewer.setCameraManipulatorOptions( + widget.viewer.setCameraManipulatorOptions( orbitSpeedX: ExampleWidgetState.orbitSpeedX, orbitSpeedY: ExampleWidgetState.orbitSpeedY, zoomSpeed: ExampleWidgetState.zoomSpeed); @@ -191,7 +191,7 @@ class _CameraSubmenuState extends State { onPressed: () { ExampleWidgetState.orbitSpeedX = speed; ExampleWidgetState.orbitSpeedY = speed; - widget.controller.viewer.setCameraManipulatorOptions( + widget.viewer.setCameraManipulatorOptions( orbitSpeedX: ExampleWidgetState.orbitSpeedX, orbitSpeedY: ExampleWidgetState.orbitSpeedY, zoomSpeed: ExampleWidgetState.zoomSpeed); diff --git a/examples/flutter/example/lib/menus/controller_menu.dart b/examples/flutter/example/lib/menus/controller_menu.dart index 2ba71624..08bd89d4 100644 --- a/examples/flutter/example/lib/menus/controller_menu.dart +++ b/examples/flutter/example/lib/menus/controller_menu.dart @@ -5,29 +5,31 @@ import 'package:flutter/widgets.dart'; import 'package:thermion_flutter/thermion_flutter.dart'; -class ControllerMenu extends StatefulWidget { - final ThermionFlutterPlugin controller; +class ViewerMenu extends StatefulWidget { + final ThermionViewer? viewer; final void Function() onToggleViewport; - final void Function() onControllerCreated; - final void Function() onControllerDestroyed; + final void Function(ThermionViewer viewer) onViewerCreated; + final void Function() onViewerDestroyed; final FocusNode sharedFocusNode; - - ControllerMenu( - {required this.controller, - required this.onControllerCreated, - required this.onControllerDestroyed, + ViewerMenu( + { + required this.viewer, + required this.onViewerCreated, + required this.onViewerDestroyed, required this.sharedFocusNode, required this.onToggleViewport}); @override - State createState() => _ControllerMenuState(); + State createState() => _ViewerMenuState(); } -class _ControllerMenuState extends State { - void _createController({String? uberArchivePath}) async { - widget.controller.initialize(uberArchivePath: uberArchivePath); - widget.onControllerCreated(); +class _ViewerMenuState extends State { + void _createViewer({String? uberArchivePath}) async { + var viewer = await ThermionFlutterPlugin.createViewer( + uberArchivePath: uberArchivePath); + await viewer.initialized; + widget.onViewerCreated(viewer); } @override @@ -36,7 +38,7 @@ class _ControllerMenuState extends State { } @override - void didUpdateWidget(ControllerMenu oldWidget) { + void didUpdateWidget(ViewerMenu oldWidget) { super.didUpdateWidget(oldWidget); } @@ -50,14 +52,14 @@ class _ControllerMenuState extends State { child: const Text("Create ThermionFlutterPlugin (default ubershader)"), onPressed: () { - _createController(); + _createViewer(); }, ), MenuItemButton( child: const Text( "Create ThermionFlutterPlugin (custom ubershader - lit opaque only)"), onPressed: () { - _createController( + _createViewer( uberArchivePath: Platform.isWindows ? "assets/lit_opaque_32.uberz" : Platform.isMacOS @@ -73,8 +75,8 @@ class _ControllerMenuState extends State { MenuItemButton( child: const Text("Destroy viewer"), onPressed: () async { - widget.controller.dispose(); - widget.onControllerDestroyed(); + widget.viewer!.dispose(); + widget.onViewerDestroyed(); setState(() {}); }, ) @@ -89,8 +91,7 @@ class _ControllerMenuState extends State { onPressed: widget.onToggleViewport, ) ], - builder: - (BuildContext context, MenuController controller, Widget? child) { + builder: (BuildContext context, MenuController controller, Widget? child) { return TextButton( onPressed: () { if (controller.isOpen) { @@ -99,7 +100,7 @@ class _ControllerMenuState extends State { controller.open(); } }, - child: const Text("Controller / Viewer"), + child: const Text("Viewer / Viewer"), ); }); } diff --git a/examples/flutter/example/lib/menus/rendering_submenu.dart b/examples/flutter/example/lib/menus/rendering_submenu.dart index 0a6ced87..c075c0ad 100644 --- a/examples/flutter/example/lib/menus/rendering_submenu.dart +++ b/examples/flutter/example/lib/menus/rendering_submenu.dart @@ -4,9 +4,9 @@ import 'package:thermion_flutter_example/main.dart'; import 'package:thermion_dart/thermion_dart/thermion_viewer.dart'; class RenderingSubmenu extends StatefulWidget { - final ThermionFlutterPlugin controller; + final ThermionViewer viewer; - const RenderingSubmenu({super.key, required this.controller}); + const RenderingSubmenu({super.key, required this.viewer}); @override State createState() => _RenderingSubmenuState(); @@ -19,14 +19,14 @@ class _RenderingSubmenuState extends State { menuChildren: [ MenuItemButton( onPressed: () { - widget.controller.viewer.render(); + widget.viewer.render(); }, child: const Text("Render single frame"), ), MenuItemButton( onPressed: () { ExampleWidgetState.rendering = !ExampleWidgetState.rendering; - widget.controller.viewer.setRendering(ExampleWidgetState.rendering); + widget.viewer.setRendering(ExampleWidgetState.rendering); }, child: Text( "Set continuous rendering to ${!ExampleWidgetState.rendering}"), @@ -35,14 +35,14 @@ class _RenderingSubmenuState extends State { onPressed: () { ExampleWidgetState.framerate = ExampleWidgetState.framerate == 60 ? 30 : 60; - widget.controller.viewer.setFrameRate(ExampleWidgetState.framerate); + widget.viewer.setFrameRate(ExampleWidgetState.framerate); }, child: Text( "Toggle framerate (currently ${ExampleWidgetState.framerate}) "), ), MenuItemButton( onPressed: () { - widget.controller.viewer.setToneMapping(ToneMapper.LINEAR); + widget.viewer.setToneMapping(ToneMapper.LINEAR); }, child: const Text("Set tone mapping to linear"), ), @@ -50,7 +50,7 @@ class _RenderingSubmenuState extends State { onPressed: () { ExampleWidgetState.postProcessing = !ExampleWidgetState.postProcessing; - widget.controller.viewer + widget.viewer .setPostProcessing(ExampleWidgetState.postProcessing); }, child: Text( @@ -60,7 +60,7 @@ class _RenderingSubmenuState extends State { onPressed: () { ExampleWidgetState.antiAliasingMsaa = !ExampleWidgetState.antiAliasingMsaa; - widget.controller.viewer.setAntiAliasing( + widget.viewer.setAntiAliasing( ExampleWidgetState.antiAliasingMsaa, ExampleWidgetState.antiAliasingFxaa, ExampleWidgetState.antiAliasingTaa); @@ -72,7 +72,7 @@ class _RenderingSubmenuState extends State { onPressed: () { ExampleWidgetState.antiAliasingFxaa = !ExampleWidgetState.antiAliasingFxaa; - widget.controller.viewer.setAntiAliasing( + widget.viewer.setAntiAliasing( ExampleWidgetState.antiAliasingMsaa, ExampleWidgetState.antiAliasingFxaa, ExampleWidgetState.antiAliasingTaa); @@ -83,7 +83,7 @@ class _RenderingSubmenuState extends State { MenuItemButton( onPressed: () { ExampleWidgetState.recording = !ExampleWidgetState.recording; - widget.controller.viewer.setRecording(ExampleWidgetState.recording); + widget.viewer.setRecording(ExampleWidgetState.recording); }, child: Text( "Turn recording ${ExampleWidgetState.recording ? "OFF" : "ON"}) "), diff --git a/examples/flutter/example/lib/menus/scene_menu.dart b/examples/flutter/example/lib/menus/scene_menu.dart index fbd1bf71..3a5e1d9b 100644 --- a/examples/flutter/example/lib/menus/scene_menu.dart +++ b/examples/flutter/example/lib/menus/scene_menu.dart @@ -6,7 +6,7 @@ import 'package:thermion_flutter_example/menus/camera_submenu.dart'; import 'package:thermion_flutter_example/menus/rendering_submenu.dart'; class SceneMenu extends StatefulWidget { - final ThermionFlutterPlugin? controller; + final ThermionViewer? controller; final FocusNode sharedFocusNode; const SceneMenu( @@ -37,11 +37,11 @@ class _SceneMenuState extends State { ? [] : [ RenderingSubmenu( - controller: widget.controller!, + viewer: widget.controller!, ), - AssetSubmenu(controller: widget.controller!), + AssetSubmenu(viewer: widget.controller!), CameraSubmenu( - controller: widget.controller!, + viewer: widget.controller!, ), ], builder: diff --git a/examples/flutter/example/macos/Podfile.lock b/examples/flutter/example/macos/Podfile.lock deleted file mode 100644 index 94fb2021..00000000 --- a/examples/flutter/example/macos/Podfile.lock +++ /dev/null @@ -1,29 +0,0 @@ -PODS: - - FlutterMacOS (1.0.0) - - path_provider_foundation (0.0.1): - - Flutter - - FlutterMacOS - - thermion_flutter (0.0.1): - - FlutterMacOS - -DEPENDENCIES: - - FlutterMacOS (from `Flutter/ephemeral`) - - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - - thermion_flutter (from `Flutter/ephemeral/.symlinks/plugins/thermion_flutter/macos`) - -EXTERNAL SOURCES: - FlutterMacOS: - :path: Flutter/ephemeral - path_provider_foundation: - :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin - thermion_flutter: - :path: Flutter/ephemeral/.symlinks/plugins/thermion_flutter/macos - -SPEC CHECKSUMS: - FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 - thermion_flutter: e4895ade6b14d9efb6e17ed2436e89dbb87fa998 - -PODFILE CHECKSUM: 1888651be91a8ad58692c1add9ce24279fd4e950 - -COCOAPODS: 1.15.2 diff --git a/examples/flutter/example/pubspec.yaml b/examples/flutter/example/pubspec.yaml index 218dc262..2f10f80c 100644 --- a/examples/flutter/example/pubspec.yaml +++ b/examples/flutter/example/pubspec.yaml @@ -13,9 +13,22 @@ dependencies: sdk: flutter path_provider: thermion_flutter: + path: ../../../thermion_flutter/thermion_flutter cupertino_icons: ^1.0.2 web: +dependency_overrides: + thermion_flutter: + path: ../../../thermion_flutter/thermion_flutter + thermion_flutter_platform_interface: + path: ../../../thermion_flutter/thermion_flutter_platform_interface + thermion_flutter_ffi: + path: ../../../thermion_flutter/thermion_flutter_ffi + thermion_flutter_web: + path: ../../../thermion_flutter/thermion_flutter_web + thermion_dart: + path: ../../../thermion_dart + dev_dependencies: flutter_test: sdk: flutter diff --git a/thermion_flutter/thermion_flutter/lib/thermion_flutter.dart b/thermion_flutter/thermion_flutter/lib/thermion_flutter.dart index efb90338..d48d5747 100644 --- a/thermion_flutter/thermion_flutter/lib/thermion_flutter.dart +++ b/thermion_flutter/thermion_flutter/lib/thermion_flutter.dart @@ -3,4 +3,5 @@ library thermion_flutter; export 'thermion/thermion_flutter_plugin.dart'; export 'thermion/widgets/thermion_widget.dart'; export 'thermion/widgets/camera/gestures/thermion_gesture_detector.dart'; + export 'package:thermion_dart/thermion_dart.dart'; From c28c518057ca4ada6cd6e16c596fa4b6cb1f20e3 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 21 Jun 2024 14:09:24 +0800 Subject: [PATCH 41/51] chore(release): publish packages - thermion_flutter@0.1.1+2 --- CHANGELOG.md | 22 +++++++++++++++++++ .../thermion_flutter/CHANGELOG.md | 5 +++++ .../thermion_flutter/pubspec.yaml | 2 +- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13127bc6..3b60fecf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,28 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 2024-06-21 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`thermion_flutter` - `v0.1.1+2`](#thermion_flutter---v0112) + +--- + +#### `thermion_flutter` - `v0.1.1+2` + + - **FIX**: update Flutter example project to use new API. + - **FIX**: add logging dependency to thermion_flutter. + + ## 2024-06-21 ### Changes diff --git a/thermion_flutter/thermion_flutter/CHANGELOG.md b/thermion_flutter/thermion_flutter/CHANGELOG.md index 000548c4..28ac486e 100644 --- a/thermion_flutter/thermion_flutter/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.1.1+2 + + - **FIX**: update Flutter example project to use new API. + - **FIX**: add logging dependency to thermion_flutter. + ## 0.1.1+1 - **REFACTOR**: export ThermionViewerWasm for web and hide FFI/WASM version. diff --git a/thermion_flutter/thermion_flutter/pubspec.yaml b/thermion_flutter/thermion_flutter/pubspec.yaml index 8a91cc1b..951677cd 100644 --- a/thermion_flutter/thermion_flutter/pubspec.yaml +++ b/thermion_flutter/thermion_flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: thermion_flutter description: Flutter plugin for 3D rendering with the Thermion toolkit. -version: 0.1.1+1 +version: 0.1.1+2 homepage: https://docs.page/nmfisher/thermion repository: https://github.com/nmfisher/thermion From 9eb228543314f181bbe10870da2faadc33984516 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 21 Jun 2024 15:13:41 +0800 Subject: [PATCH 42/51] refactor: rearrange some stubs/imports for easier web WASM deployment --- melos.yaml | 1 - thermion_dart/lib/thermion_dart.dart | 5 +- .../compatibility/compatibility.dart | 4 +- .../compatibility/web/compatibility.dart | 0 .../web/{ => ffi}/allocator.dart | 2 +- .../web/{ => ffi}/compatibility_ffi.dart | 2 +- .../compatibility/web/{ => ffi}/interop.dart | 0 .../web/{ => ffi}/thermion_dart.g.dart | 0 .../web/interop/thermion_viewer_wasm.dart | 11 +- .../lib/thermion_dart/thermion_viewer.dart | 1 + .../thermion_dart/thermion_viewer_ffi.dart | 3 +- .../thermion_dart/thermion_viewer_stub.dart | 729 ++++++++++++++++++ thermion_dart/pubspec.yaml | 1 + thermion_dart/test/integration_test.dart | 4 +- .../lib/thermion_flutter_ffi.dart | 1 + 15 files changed, 749 insertions(+), 15 deletions(-) delete mode 100644 thermion_dart/lib/thermion_dart/compatibility/web/compatibility.dart rename thermion_dart/lib/thermion_dart/compatibility/web/{ => ffi}/allocator.dart (98%) rename thermion_dart/lib/thermion_dart/compatibility/web/{ => ffi}/compatibility_ffi.dart (97%) rename thermion_dart/lib/thermion_dart/compatibility/web/{ => ffi}/interop.dart (100%) rename thermion_dart/lib/thermion_dart/compatibility/web/{ => ffi}/thermion_dart.g.dart (100%) create mode 100644 thermion_dart/lib/thermion_dart/thermion_viewer_stub.dart diff --git a/melos.yaml b/melos.yaml index dfa7fc3e..213832ce 100644 --- a/melos.yaml +++ b/melos.yaml @@ -1,5 +1,4 @@ name: thermion_workspace - packages: - thermion_dart - thermion_flutter/** diff --git a/thermion_dart/lib/thermion_dart.dart b/thermion_dart/lib/thermion_dart.dart index 6ccf7c64..c283d51a 100644 --- a/thermion_dart/lib/thermion_dart.dart +++ b/thermion_dart/lib/thermion_dart.dart @@ -1,5 +1,8 @@ library filament_dart; export 'thermion_dart/thermion_viewer.dart'; -export 'thermion_dart/compatibility/web/interop/thermion_viewer_wasm.dart' if (dart.library.io) 'thermion_dart/thermion_viewer_ffi.dart'; +export 'thermion_dart/thermion_viewer_stub.dart' + if (dart.library.io) 'thermion_dart/thermion_viewer_ffi.dart' + if (dart.library.js_interop)'thermion_dart/compatibility/web/interop/thermion_viewer_wasm.dart'; + export 'thermion_dart/entities/entity_transform_controller.dart'; diff --git a/thermion_dart/lib/thermion_dart/compatibility/compatibility.dart b/thermion_dart/lib/thermion_dart/compatibility/compatibility.dart index 55e1f5e0..a8435873 100644 --- a/thermion_dart/lib/thermion_dart/compatibility/compatibility.dart +++ b/thermion_dart/lib/thermion_dart/compatibility/compatibility.dart @@ -1,3 +1 @@ -export 'web/compatibility.dart' if (dart.library.io) 'native/compatibility.dart'; - - +export 'native/compatibility.dart'; diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/compatibility.dart b/thermion_dart/lib/thermion_dart/compatibility/web/compatibility.dart deleted file mode 100644 index e69de29b..00000000 diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/allocator.dart b/thermion_dart/lib/thermion_dart/compatibility/web/ffi/allocator.dart similarity index 98% rename from thermion_dart/lib/thermion_dart/compatibility/web/allocator.dart rename to thermion_dart/lib/thermion_dart/compatibility/web/ffi/allocator.dart index c19ace21..41d8f6d8 100644 --- a/thermion_dart/lib/thermion_dart/compatibility/web/allocator.dart +++ b/thermion_dart/lib/thermion_dart/compatibility/web/ffi/allocator.dart @@ -6,7 +6,7 @@ import 'dart:convert'; import 'dart:ffi' as ffi hide Uint8Pointer, FloatPointer; import 'dart:typed_data'; -import 'package:thermion_dart/thermion_dart/compatibility/web/thermion_dart.g.dart'; +import 'package:thermion_dart/thermion_dart/compatibility/web/ffi/thermion_dart.g.dart'; import 'package:ffi/ffi.dart'; export 'package:ffi/ffi.dart' hide StringUtf8Pointer, Utf8Pointer; diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/compatibility_ffi.dart b/thermion_dart/lib/thermion_dart/compatibility/web/ffi/compatibility_ffi.dart similarity index 97% rename from thermion_dart/lib/thermion_dart/compatibility/web/compatibility_ffi.dart rename to thermion_dart/lib/thermion_dart/compatibility/web/ffi/compatibility_ffi.dart index f8da247c..e9242cc2 100644 --- a/thermion_dart/lib/thermion_dart/compatibility/web/compatibility_ffi.dart +++ b/thermion_dart/lib/thermion_dart/compatibility/web/ffi/compatibility_ffi.dart @@ -1,6 +1,6 @@ import 'dart:async'; import 'dart:js_interop'; -import 'package:thermion_dart/thermion_dart/compatibility/web/interop.dart'; +import 'package:thermion_dart/thermion_dart/compatibility/web/ffi/interop.dart'; import "allocator.dart"; diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/interop.dart b/thermion_dart/lib/thermion_dart/compatibility/web/ffi/interop.dart similarity index 100% rename from thermion_dart/lib/thermion_dart/compatibility/web/interop.dart rename to thermion_dart/lib/thermion_dart/compatibility/web/ffi/interop.dart diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/thermion_dart.g.dart b/thermion_dart/lib/thermion_dart/compatibility/web/ffi/thermion_dart.g.dart similarity index 100% rename from thermion_dart/lib/thermion_dart/compatibility/web/thermion_dart.g.dart rename to thermion_dart/lib/thermion_dart/compatibility/web/ffi/thermion_dart.g.dart diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_wasm.dart b/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_wasm.dart index abde3ba6..b881443f 100644 --- a/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_wasm.dart +++ b/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_wasm.dart @@ -33,8 +33,11 @@ extension type _EmscriptenModule(JSObject _) implements JSObject { external JSAny get HEAP32; } +typedef ThermionViewerImpl = ThermionViewerWasm; + + /// -/// An [ThermionViewer] implementation that forwards calls to +/// A [ThermionViewer] implementation that forwards calls to /// the (Emscripten-generated) ThermionDart JS module. /// class ThermionViewerWasm implements ThermionViewer { @@ -43,8 +46,8 @@ class ThermionViewerWasm implements ThermionViewer { bool _initialized = false; bool _rendering = false; - ThermionViewerWasm({String moduleName = "thermion_dart"}) { - _module = window.getProperty<_EmscriptenModule>(moduleName.toJS); + ThermionViewerWasm({JSObject? module, String moduleName = "thermion_dart"}) { + _module = module as _EmscriptenModule? ?? window.getProperty<_EmscriptenModule>(moduleName.toJS); } JSNumber? _viewer; @@ -1687,7 +1690,7 @@ class ThermionViewerWasm implements ThermionViewer { Future setMorphTargetWeights( ThermionEntity entity, List weights) async { final numWeights = weights.length; - final ptr = _module._malloc(numWeights * 4) as JSNumber; + final ptr = _module._malloc(numWeights * 4); for (int i = 0; i < numWeights; i++) { _module.setValue( (ptr.toDartInt + (i * 4)).toJS, weights[i].toJS, "float"); diff --git a/thermion_dart/lib/thermion_dart/thermion_viewer.dart b/thermion_dart/lib/thermion_dart/thermion_viewer.dart index 73f458ec..332f5a83 100644 --- a/thermion_dart/lib/thermion_dart/thermion_viewer.dart +++ b/thermion_dart/lib/thermion_dart/thermion_viewer.dart @@ -47,6 +47,7 @@ class TextureDetails { } abstract class ThermionViewer { + Future get initialized; /// diff --git a/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart b/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart index 7dc0018f..fba9b24c 100644 --- a/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart +++ b/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart @@ -9,6 +9,8 @@ import 'thermion_viewer.dart'; import 'scene_impl.dart'; import 'package:logging/logging.dart'; +typedef ThermionViewerImpl = ThermionViewerFFI; + // ignore: constant_identifier_names const ThermionEntity _FILAMENT_ASSET_ERROR = 0; @@ -16,7 +18,6 @@ typedef RenderCallback = Pointer)>>; class ThermionViewerFFI extends ThermionViewer { final _logger = Logger("ThermionViewerFFI"); - final _compat = Compatibility(); SceneImpl? _scene; Scene get scene => _scene!; diff --git a/thermion_dart/lib/thermion_dart/thermion_viewer_stub.dart b/thermion_dart/lib/thermion_dart/thermion_viewer_stub.dart new file mode 100644 index 00000000..2fff9d97 --- /dev/null +++ b/thermion_dart/lib/thermion_dart/thermion_viewer_stub.dart @@ -0,0 +1,729 @@ +import 'dart:math'; + +import 'package:thermion_dart/thermion_dart/scene.dart'; +import 'package:thermion_dart/thermion_dart/thermion_viewer.dart'; +import 'package:vector_math/vector_math_64.dart'; +import 'dart:async'; +import 'package:animation_tools_dart/animation_tools_dart.dart'; + +typedef ThermionViewerImpl = ThermionViewerStub; + +class ThermionViewerStub extends ThermionViewer { + @override + Future addAnimationComponent(ThermionEntity entity) { + // TODO: implement addAnimationComponent + throw UnimplementedError(); + } + + @override + Future addBoneAnimation(ThermionEntity entity, BoneAnimationData animation, + {int skinIndex = 0, + double fadeInInSecs = 0.0, + double fadeOutInSecs = 0.0, + double maxDelta = 1.0}) { + // TODO: implement addBoneAnimation + throw UnimplementedError(); + } + + @override + Future addCollisionComponent(ThermionEntity entity, + {void Function(int entityId1, int entityId2)? callback, + bool affectsTransform = false}) { + // TODO: implement addCollisionComponent + throw UnimplementedError(); + } + + @override + Future addLight( + LightType type, + double colour, + double intensity, + double posX, + double posY, + double posZ, + double dirX, + double dirY, + double dirZ, + {double falloffRadius = 1.0, + double spotLightConeInner = pi / 8, + double spotLightConeOuter = pi / 4, + double sunAngularRadius = 0.545, + double sunHaloSize = 10.0, + double sunHaloFallof = 80.0, + bool castShadows = true}) { + // TODO: implement addLight + throw UnimplementedError(); + } + + @override + Future clearBackgroundImage() { + // TODO: implement clearBackgroundImage + throw UnimplementedError(); + } + + @override + Future clearEntities() { + // TODO: implement clearEntities + throw UnimplementedError(); + } + + @override + Future clearLights() { + // TODO: implement clearLights + throw UnimplementedError(); + } + + @override + Future createGeometry(List vertices, List indices, + {String? materialPath, + PrimitiveType primitiveType = PrimitiveType.TRIANGLES}) { + // TODO: implement createGeometry + throw UnimplementedError(); + } + + @override + Future createInstance(ThermionEntity entity) { + // TODO: implement createInstance + throw UnimplementedError(); + } + + @override + Future dispose() { + // TODO: implement dispose + throw UnimplementedError(); + } + + @override + Future getAnimationDuration( + ThermionEntity entity, int animationIndex) { + // TODO: implement getAnimationDuration + throw UnimplementedError(); + } + + @override + Future> getAnimationNames(ThermionEntity entity) { + // TODO: implement getAnimationNames + throw UnimplementedError(); + } + + @override + Future getBone(ThermionEntity parent, int boneIndex, + {int skinIndex = 0}) { + // TODO: implement getBone + throw UnimplementedError(); + } + + @override + Future> getBoneNames(ThermionEntity entity, + {int skinIndex = 0}) { + // TODO: implement getBoneNames + throw UnimplementedError(); + } + + @override + Future getCameraCullingFar() { + // TODO: implement getCameraCullingFar + throw UnimplementedError(); + } + + @override + Future getCameraCullingNear() { + // TODO: implement getCameraCullingNear + throw UnimplementedError(); + } + + @override + Future getCameraCullingProjectionMatrix() { + // TODO: implement getCameraCullingProjectionMatrix + throw UnimplementedError(); + } + + @override + Future getCameraFrustum() { + // TODO: implement getCameraFrustum + throw UnimplementedError(); + } + + @override + Future getCameraModelMatrix() { + // TODO: implement getCameraModelMatrix + throw UnimplementedError(); + } + + @override + Future getCameraPosition() { + // TODO: implement getCameraPosition + throw UnimplementedError(); + } + + @override + Future getCameraProjectionMatrix() { + // TODO: implement getCameraProjectionMatrix + throw UnimplementedError(); + } + + @override + Future getCameraRotation() { + // TODO: implement getCameraRotation + throw UnimplementedError(); + } + + @override + Future getCameraViewMatrix() { + // TODO: implement getCameraViewMatrix + throw UnimplementedError(); + } + + @override + Future> getChildEntities( + ThermionEntity parent, bool renderableOnly) { + // TODO: implement getChildEntities + throw UnimplementedError(); + } + + @override + Future getChildEntity( + ThermionEntity parent, String childName) { + // TODO: implement getChildEntity + throw UnimplementedError(); + } + + @override + Future> getChildEntityNames(ThermionEntity entity, + {bool renderableOnly = true}) { + // TODO: implement getChildEntityNames + throw UnimplementedError(); + } + + @override + Future getInstanceCount(ThermionEntity entity) { + // TODO: implement getInstanceCount + throw UnimplementedError(); + } + + @override + Future> getInstances(ThermionEntity entity) { + // TODO: implement getInstances + throw UnimplementedError(); + } + + @override + Future getInverseBindMatrix(ThermionEntity parent, int boneIndex, + {int skinIndex = 0}) { + // TODO: implement getInverseBindMatrix + throw UnimplementedError(); + } + + @override + Future getLocalTransform(ThermionEntity entity) { + // TODO: implement getLocalTransform + throw UnimplementedError(); + } + + @override + Future getMainCamera() { + // TODO: implement getMainCamera + throw UnimplementedError(); + } + + @override + Future> getMorphTargetNames( + ThermionEntity entity, ThermionEntity childEntity) { + // TODO: implement getMorphTargetNames + throw UnimplementedError(); + } + + @override + String? getNameForEntity(ThermionEntity entity) { + // TODO: implement getNameForEntity + throw UnimplementedError(); + } + + @override + Future getParent(ThermionEntity child) { + // TODO: implement getParent + throw UnimplementedError(); + } + + @override + Future getWorldTransform(ThermionEntity entity) { + // TODO: implement getWorldTransform + throw UnimplementedError(); + } + + @override + // TODO: implement gizmo + AbstractGizmo? get gizmo => throw UnimplementedError(); + + @override + Future hide(ThermionEntity entity, String? meshName) { + // TODO: implement hide + throw UnimplementedError(); + } + + @override + // TODO: implement initialized + Future get initialized => throw UnimplementedError(); + + @override + Future loadGlb(String path, {int numInstances = 1}) { + // TODO: implement loadGlb + throw UnimplementedError(); + } + + @override + Future loadGltf(String path, String relativeResourcePath, + {bool force = false}) { + // TODO: implement loadGltf + throw UnimplementedError(); + } + + @override + Future loadIbl(String lightingPath, {double intensity = 30000}) { + // TODO: implement loadIbl + throw UnimplementedError(); + } + + @override + Future loadSkybox(String skyboxPath) { + // TODO: implement loadSkybox + throw UnimplementedError(); + } + + @override + Future moveCameraToAsset(ThermionEntity entity) { + // TODO: implement moveCameraToAsset + throw UnimplementedError(); + } + + @override + void onDispose(Future Function() callback) { + // TODO: implement onDispose + } + + @override + Future panEnd() { + // TODO: implement panEnd + throw UnimplementedError(); + } + + @override + Future panStart(double x, double y) { + // TODO: implement panStart + throw UnimplementedError(); + } + + @override + Future panUpdate(double x, double y) { + // TODO: implement panUpdate + throw UnimplementedError(); + } + + @override + void pick(int x, int y) { + // TODO: implement pick + } + + @override + // TODO: implement pickResult + Stream get pickResult => throw UnimplementedError(); + + @override + Future playAnimation(ThermionEntity entity, int index, + {bool loop = false, + bool reverse = false, + bool replaceActive = true, + double crossfade = 0.0}) { + // TODO: implement playAnimation + throw UnimplementedError(); + } + + @override + Future playAnimationByName(ThermionEntity entity, String name, + {bool loop = false, + bool reverse = false, + bool replaceActive = true, + double crossfade = 0.0}) { + // TODO: implement playAnimationByName + throw UnimplementedError(); + } + + @override + Future queuePositionUpdate( + ThermionEntity entity, double x, double y, double z, + {bool relative = false}) { + // TODO: implement queuePositionUpdate + throw UnimplementedError(); + } + + @override + Future queueRotationUpdate( + ThermionEntity entity, double rads, double x, double y, double z, + {bool relative = false}) { + // TODO: implement queueRotationUpdate + throw UnimplementedError(); + } + + @override + Future queueRotationUpdateQuat(ThermionEntity entity, Quaternion quat, + {bool relative = false}) { + // TODO: implement queueRotationUpdateQuat + throw UnimplementedError(); + } + + @override + Future removeAnimationComponent(ThermionEntity entity) { + // TODO: implement removeAnimationComponent + throw UnimplementedError(); + } + + @override + Future removeCollisionComponent(ThermionEntity entity) { + // TODO: implement removeCollisionComponent + throw UnimplementedError(); + } + + @override + Future removeEntity(ThermionEntity entity) { + // TODO: implement removeEntity + throw UnimplementedError(); + } + + @override + Future removeIbl() { + // TODO: implement removeIbl + throw UnimplementedError(); + } + + @override + Future removeLight(ThermionEntity light) { + // TODO: implement removeLight + throw UnimplementedError(); + } + + @override + Future removeSkybox() { + // TODO: implement removeSkybox + throw UnimplementedError(); + } + + @override + Future render() { + // TODO: implement render + throw UnimplementedError(); + } + + @override + // TODO: implement rendering + bool get rendering => throw UnimplementedError(); + + @override + Future resetBones(ThermionEntity entity) { + // TODO: implement resetBones + throw UnimplementedError(); + } + + @override + Future reveal(ThermionEntity entity, String? meshName) { + // TODO: implement reveal + throw UnimplementedError(); + } + + @override + Future rotateEnd() { + // TODO: implement rotateEnd + throw UnimplementedError(); + } + + @override + Future rotateIbl(Matrix3 rotation) { + // TODO: implement rotateIbl + throw UnimplementedError(); + } + + @override + Future rotateStart(double x, double y) { + // TODO: implement rotateStart + throw UnimplementedError(); + } + + @override + Future rotateUpdate(double x, double y) { + // TODO: implement rotateUpdate + throw UnimplementedError(); + } + + @override + // TODO: implement scene + Scene get scene => throw UnimplementedError(); + + @override + Future setAnimationFrame( + ThermionEntity entity, int index, int animationFrame) { + // TODO: implement setAnimationFrame + throw UnimplementedError(); + } + + @override + Future setAntiAliasing(bool msaa, bool fxaa, bool taa) { + // TODO: implement setAntiAliasing + throw UnimplementedError(); + } + + @override + Future setBackgroundColor(double r, double g, double b, double alpha) { + // TODO: implement setBackgroundColor + throw UnimplementedError(); + } + + @override + Future setBackgroundImage(String path, {bool fillHeight = false}) { + // TODO: implement setBackgroundImage + throw UnimplementedError(); + } + + @override + Future setBackgroundImagePosition(double x, double y, {bool clamp = false}) { + // TODO: implement setBackgroundImagePosition + throw UnimplementedError(); + } + + @override + Future setBloom(double bloom) { + // TODO: implement setBloom + throw UnimplementedError(); + } + + @override + Future setBoneTransform( + ThermionEntity entity, int boneIndex, Matrix4 transform, + {int skinIndex = 0}) { + // TODO: implement setBoneTransform + throw UnimplementedError(); + } + + @override + Future setCamera(ThermionEntity entity, String? name) { + // TODO: implement setCamera + throw UnimplementedError(); + } + + @override + Future setCameraCulling(double near, double far) { + // TODO: implement setCameraCulling + throw UnimplementedError(); + } + + @override + Future setCameraExposure( + double aperture, double shutterSpeed, double sensitivity) { + // TODO: implement setCameraExposure + throw UnimplementedError(); + } + + @override + Future setCameraFocalLength(double focalLength) { + // TODO: implement setCameraFocalLength + throw UnimplementedError(); + } + + @override + Future setCameraFocusDistance(double focusDistance) { + // TODO: implement setCameraFocusDistance + throw UnimplementedError(); + } + + @override + Future setCameraFov(double degrees, double width, double height) { + // TODO: implement setCameraFov + throw UnimplementedError(); + } + + @override + Future setCameraManipulatorOptions( + {ManipulatorMode mode = ManipulatorMode.ORBIT, + double orbitSpeedX = 0.01, + double orbitSpeedY = 0.01, + double zoomSpeed = 0.01}) { + // TODO: implement setCameraManipulatorOptions + throw UnimplementedError(); + } + + @override + Future setCameraModelMatrix(List matrix) { + // TODO: implement setCameraModelMatrix + throw UnimplementedError(); + } + + @override + Future setCameraPosition(double x, double y, double z) { + // TODO: implement setCameraPosition + throw UnimplementedError(); + } + + @override + Future setCameraRotation(Quaternion quaternion) { + // TODO: implement setCameraRotation + throw UnimplementedError(); + } + + @override + Future setFrameRate(int framerate) { + // TODO: implement setFrameRate + throw UnimplementedError(); + } + + @override + Future setMainCamera() { + // TODO: implement setMainCamera + throw UnimplementedError(); + } + + @override + Future setMaterialColor(ThermionEntity entity, String meshName, + int materialIndex, double r, double g, double b, double a) { + // TODO: implement setMaterialColor + throw UnimplementedError(); + } + + @override + Future setMorphAnimationData( + ThermionEntity entity, MorphAnimationData animation, + {List? targetMeshNames}) { + // TODO: implement setMorphAnimationData + throw UnimplementedError(); + } + + @override + Future setMorphTargetWeights(ThermionEntity entity, List weights) { + // TODO: implement setMorphTargetWeights + throw UnimplementedError(); + } + + @override + Future setParent(ThermionEntity child, ThermionEntity parent) { + // TODO: implement setParent + throw UnimplementedError(); + } + + @override + Future setPosition(ThermionEntity entity, double x, double y, double z) { + // TODO: implement setPosition + throw UnimplementedError(); + } + + @override + Future setPostProcessing(bool enabled) { + // TODO: implement setPostProcessing + throw UnimplementedError(); + } + + @override + Future setPriority(ThermionEntity entityId, int priority) { + // TODO: implement setPriority + throw UnimplementedError(); + } + + @override + Future setRecording(bool recording) { + // TODO: implement setRecording + throw UnimplementedError(); + } + + @override + Future setRecordingOutputDirectory(String outputDirectory) { + // TODO: implement setRecordingOutputDirectory + throw UnimplementedError(); + } + + @override + Future setRendering(bool render) { + // TODO: implement setRendering + throw UnimplementedError(); + } + + @override + Future setRotation( + ThermionEntity entity, double rads, double x, double y, double z) { + // TODO: implement setRotation + throw UnimplementedError(); + } + + @override + Future setRotationQuat(ThermionEntity entity, Quaternion rotation) { + // TODO: implement setRotationQuat + throw UnimplementedError(); + } + + @override + Future setScale(ThermionEntity entity, double scale) { + // TODO: implement setScale + throw UnimplementedError(); + } + + @override + Future setToneMapping(ToneMapper mapper) { + // TODO: implement setToneMapping + throw UnimplementedError(); + } + + @override + Future setTransform(ThermionEntity entity, Matrix4 transform) { + // TODO: implement setTransform + throw UnimplementedError(); + } + + @override + Future setViewFrustumCulling(bool enabled) { + // TODO: implement setViewFrustumCulling + throw UnimplementedError(); + } + + @override + Future stopAnimation(ThermionEntity entity, int animationIndex) { + // TODO: implement stopAnimation + throw UnimplementedError(); + } + + @override + Future stopAnimationByName(ThermionEntity entity, String name) { + // TODO: implement stopAnimationByName + throw UnimplementedError(); + } + + @override + Future testCollisions(ThermionEntity entity) { + // TODO: implement testCollisions + throw UnimplementedError(); + } + + @override + Future transformToUnitCube(ThermionEntity entity) { + // TODO: implement transformToUnitCube + throw UnimplementedError(); + } + + @override + Future updateBoneMatrices(ThermionEntity entity) { + // TODO: implement updateBoneMatrices + throw UnimplementedError(); + } + + @override + Future zoomBegin() { + // TODO: implement zoomBegin + throw UnimplementedError(); + } + + @override + Future zoomEnd() { + // TODO: implement zoomEnd + throw UnimplementedError(); + } + + @override + Future zoomUpdate(double x, double y, double z) { + // TODO: implement zoomUpdate + throw UnimplementedError(); + } +} diff --git a/thermion_dart/pubspec.yaml b/thermion_dart/pubspec.yaml index 6fac110b..74a3499b 100644 --- a/thermion_dart/pubspec.yaml +++ b/thermion_dart/pubspec.yaml @@ -15,6 +15,7 @@ dependencies: native_toolchain_c: ^0.4.2 archive: ^3.6.1 web: ^0.5.1 + logging: ^1.2.0 dev_dependencies: ffigen: ^11.0.0 diff --git a/thermion_dart/test/integration_test.dart b/thermion_dart/test/integration_test.dart index db155186..eaab19fb 100644 --- a/thermion_dart/test/integration_test.dart +++ b/thermion_dart/test/integration_test.dart @@ -1,9 +1,7 @@ -import 'dart:ffi'; import 'dart:io'; import 'package:thermion_dart/thermion_dart/swift/swift_bindings.g.dart'; +import 'package:thermion_dart/thermion_dart/thermion_viewer_ffi.dart'; import 'package:thermion_dart/thermion_dart/utils/dart_resources.dart'; -import 'package:ffi/ffi.dart'; -import 'package:thermion_dart/thermion_dart.dart'; import 'package:thermion_dart/thermion_dart/compatibility/compatibility.dart'; import 'package:test/test.dart'; import 'package:animation_tools_dart/animation_tools_dart.dart'; diff --git a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart b/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart index 9a930778..c34e3fea 100644 --- a/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart +++ b/thermion_flutter/thermion_flutter_ffi/lib/thermion_flutter_ffi.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'dart:ffi'; import 'package:thermion_dart/thermion_dart.dart'; +import 'package:thermion_dart/thermion_dart/thermion_viewer_ffi.dart'; import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart'; import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart'; From 090c36c92b6a7f80af5d3337576b5d255cf870d2 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 21 Jun 2024 15:24:19 +0800 Subject: [PATCH 43/51] chore(release): publish packages - thermion_dart@0.1.0+2 - thermion_flutter_ffi@0.1.0+2 - thermion_flutter_web@0.0.1+2 - thermion_flutter@0.1.1+3 - thermion_flutter_platform_interface@0.1.0+2 --- CHANGELOG.md | 37 ++++++++++++++++++ docs/images/drawing.png | Bin 130 -> 79668 bytes thermion_dart/CHANGELOG.md | 4 ++ thermion_dart/pubspec.yaml | 2 +- .../thermion_flutter/CHANGELOG.md | 4 ++ .../thermion_flutter/pubspec.yaml | 10 ++--- .../thermion_flutter_ffi/CHANGELOG.md | 4 ++ .../thermion_flutter_ffi/pubspec.yaml | 6 +-- .../CHANGELOG.md | 4 ++ .../pubspec.yaml | 4 +- .../thermion_flutter_web/CHANGELOG.md | 4 ++ .../thermion_flutter_web/pubspec.yaml | 6 +-- 12 files changed, 71 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b60fecf..2d5723ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,43 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 2024-06-21 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`thermion_dart` - `v0.1.0+2`](#thermion_dart---v0102) + - [`thermion_flutter_ffi` - `v0.1.0+2`](#thermion_flutter_ffi---v0102) + - [`thermion_flutter_web` - `v0.0.1+2`](#thermion_flutter_web---v0012) + - [`thermion_flutter` - `v0.1.1+3`](#thermion_flutter---v0113) + - [`thermion_flutter_platform_interface` - `v0.1.0+2`](#thermion_flutter_platform_interface---v0102) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `thermion_flutter_web` - `v0.0.1+2` + - `thermion_flutter` - `v0.1.1+3` + - `thermion_flutter_platform_interface` - `v0.1.0+2` + +--- + +#### `thermion_dart` - `v0.1.0+2` + + - **REFACTOR**: rearrange some stubs/imports for easier web WASM deployment. + +#### `thermion_flutter_ffi` - `v0.1.0+2` + + - **REFACTOR**: rearrange some stubs/imports for easier web WASM deployment. + + ## 2024-06-21 ### Changes diff --git a/docs/images/drawing.png b/docs/images/drawing.png index 87e1931d0a397bae3e3a3dea75ce1cf89a17c9f4..57684da7e3cde296eac34dcd7b9ac5183dca32c3 100644 GIT binary patch literal 79668 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.3.0 <4.0.0" @@ -22,8 +22,8 @@ dependencies: flutter: sdk: flutter plugin_platform_interface: ^2.1.0 - thermion_flutter_platform_interface: ^0.1.0+1 - thermion_dart: ^0.1.0+1 + thermion_flutter_platform_interface: ^0.1.0+2 + thermion_dart: ^0.1.0+2 dev_dependencies: flutter_test: diff --git a/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md b/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md index 258f5a73..d48431e7 100644 --- a/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.0+2 + + - Update a dependency to the latest release. + ## 0.1.0+1 - Update a dependency to the latest release. diff --git a/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml b/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml index 4863ce86..214bb4e4 100644 --- a/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml @@ -1,7 +1,7 @@ name: thermion_flutter_platform_interface description: A common platform interface for the thermion_flutter plugin. repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter -version: 0.1.0+1 +version: 0.1.0+2 environment: sdk: ">=3.3.0 <4.0.0" @@ -11,7 +11,7 @@ dependencies: flutter: sdk: flutter plugin_platform_interface: ^2.1.0 - thermion_dart: ^0.1.0+1 + thermion_dart: ^0.1.0+2 dev_dependencies: flutter_test: diff --git a/thermion_flutter/thermion_flutter_web/CHANGELOG.md b/thermion_flutter/thermion_flutter_web/CHANGELOG.md index b9bec338..4e0cf851 100644 --- a/thermion_flutter/thermion_flutter_web/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.1+2 + + - Update a dependency to the latest release. + ## 0.0.1+1 - **REFACTOR**: export ThermionViewerWasm for web and hide FFI/WASM version. diff --git a/thermion_flutter/thermion_flutter_web/pubspec.yaml b/thermion_flutter/thermion_flutter_web/pubspec.yaml index 218e81c6..9a895349 100644 --- a/thermion_flutter/thermion_flutter_web/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_web/pubspec.yaml @@ -1,7 +1,7 @@ name: thermion_flutter_web description: A web platform interface for the thermion_flutter plugin. repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter -version: 0.0.1+1 +version: 0.0.1+2 environment: sdk: ">=3.3.0 <4.0.0" @@ -20,8 +20,8 @@ dependencies: sdk: flutter plugin_platform_interface: ^2.1.0 web: ^0.5.1 - thermion_dart: ^0.1.0+1 - thermion_flutter_platform_interface: ^0.1.0+1 + thermion_dart: ^0.1.0+2 + thermion_flutter_platform_interface: ^0.1.0+2 flutter_web_plugins: sdk: flutter From b889fddcfab24152e9d964a4bda431a7e12f0d4a Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 21 Jun 2024 16:20:42 +0800 Subject: [PATCH 44/51] fix: exit build.dart early on Linux builds so we can use the package on a Linux host --- thermion_dart/hook/build.dart | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/thermion_dart/hook/build.dart b/thermion_dart/hook/build.dart index c5a0b361..4e550f53 100644 --- a/thermion_dart/hook/build.dart +++ b/thermion_dart/hook/build.dart @@ -22,6 +22,14 @@ void main(List args) async { var platform = config.targetOS.toString().toLowerCase(); + // We don't support Linux (yet), so the native/Filament libraries won't be + // compiled/available. However, we still want to be able to run the Dart + // package itself on a Linux host(e.g. for dart_services backed), so if + // we detect that we're running on Linux, just exit here. + if (platform == "linux") { + return; + } + var libDir = config.dryRun ? "" : (await getLibDir(config, logger)).path; final packageName = config.packageName; @@ -81,7 +89,7 @@ void main(List args) async { } else { libs.add("stdc++"); } - final flags = []; //"-fsanitize=address"]; + final flags = []; //"-fsanitize=address"]; final defines = {}; var frameworks = []; From a3370a775ff042a759e5129a39b00c1f6506649f Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 21 Jun 2024 16:21:02 +0800 Subject: [PATCH 45/51] chore(release): publish packages - thermion_dart@0.1.0+3 - thermion_flutter_web@0.0.1+3 - thermion_flutter@0.1.1+4 - thermion_flutter_platform_interface@0.1.0+3 - thermion_flutter_ffi@0.1.0+3 --- CHANGELOG.md | 34 +++++++++++++++++++ thermion_dart/CHANGELOG.md | 4 +++ thermion_dart/pubspec.yaml | 2 +- .../thermion_flutter/CHANGELOG.md | 4 +++ .../thermion_flutter/pubspec.yaml | 10 +++--- .../thermion_flutter_ffi/CHANGELOG.md | 4 +++ .../thermion_flutter_ffi/pubspec.yaml | 6 ++-- .../CHANGELOG.md | 4 +++ .../pubspec.yaml | 4 +-- .../thermion_flutter_web/CHANGELOG.md | 4 +++ .../thermion_flutter_web/pubspec.yaml | 6 ++-- 11 files changed, 68 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d5723ea..cddc4e7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,40 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 2024-06-21 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`thermion_dart` - `v0.1.0+3`](#thermion_dart---v0103) + - [`thermion_flutter_web` - `v0.0.1+3`](#thermion_flutter_web---v0013) + - [`thermion_flutter` - `v0.1.1+4`](#thermion_flutter---v0114) + - [`thermion_flutter_platform_interface` - `v0.1.0+3`](#thermion_flutter_platform_interface---v0103) + - [`thermion_flutter_ffi` - `v0.1.0+3`](#thermion_flutter_ffi---v0103) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `thermion_flutter_web` - `v0.0.1+3` + - `thermion_flutter` - `v0.1.1+4` + - `thermion_flutter_platform_interface` - `v0.1.0+3` + - `thermion_flutter_ffi` - `v0.1.0+3` + +--- + +#### `thermion_dart` - `v0.1.0+3` + + - **FIX**: exit build.dart early on Linux builds so we can use the package on a Linux host. + + ## 2024-06-21 ### Changes diff --git a/thermion_dart/CHANGELOG.md b/thermion_dart/CHANGELOG.md index fec36de7..a22ae60a 100644 --- a/thermion_dart/CHANGELOG.md +++ b/thermion_dart/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.0+3 + + - **FIX**: exit build.dart early on Linux builds so we can use the package on a Linux host. + ## 0.1.0+2 - **REFACTOR**: rearrange some stubs/imports for easier web WASM deployment. diff --git a/thermion_dart/pubspec.yaml b/thermion_dart/pubspec.yaml index fe7450d1..17d338fd 100644 --- a/thermion_dart/pubspec.yaml +++ b/thermion_dart/pubspec.yaml @@ -1,6 +1,6 @@ name: thermion_dart description: 3D rendering toolkit for Dart. -version: 0.1.0+2 +version: 0.1.0+3 homepage: https://docs.page/nmfisher/thermion repository: https://github.com/nmfisher/thermion diff --git a/thermion_flutter/thermion_flutter/CHANGELOG.md b/thermion_flutter/thermion_flutter/CHANGELOG.md index 35c7808b..f262d779 100644 --- a/thermion_flutter/thermion_flutter/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.1+4 + + - Update a dependency to the latest release. + ## 0.1.1+3 - Update a dependency to the latest release. diff --git a/thermion_flutter/thermion_flutter/pubspec.yaml b/thermion_flutter/thermion_flutter/pubspec.yaml index d04d104a..937460f2 100644 --- a/thermion_flutter/thermion_flutter/pubspec.yaml +++ b/thermion_flutter/thermion_flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: thermion_flutter description: Flutter plugin for 3D rendering with the Thermion toolkit. -version: 0.1.1+3 +version: 0.1.1+4 homepage: https://docs.page/nmfisher/thermion repository: https://github.com/nmfisher/thermion @@ -17,10 +17,10 @@ dependencies: plugin_platform_interface: ^2.0.0 ffi: ^2.1.2 animation_tools_dart: ^0.0.4 - thermion_dart: ^0.1.0+2 - thermion_flutter_platform_interface: ^0.1.0+2 - thermion_flutter_ffi: ^0.1.0+2 - thermion_flutter_web: ^0.0.1+2 + thermion_dart: ^0.1.0+3 + thermion_flutter_platform_interface: ^0.1.0+3 + thermion_flutter_ffi: ^0.1.0+3 + thermion_flutter_web: ^0.0.1+3 logging: ^1.2.0 dev_dependencies: diff --git a/thermion_flutter/thermion_flutter_ffi/CHANGELOG.md b/thermion_flutter/thermion_flutter_ffi/CHANGELOG.md index c3dc196c..1c9cd748 100644 --- a/thermion_flutter/thermion_flutter_ffi/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter_ffi/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.0+3 + + - Update a dependency to the latest release. + ## 0.1.0+2 - **REFACTOR**: rearrange some stubs/imports for easier web WASM deployment. diff --git a/thermion_flutter/thermion_flutter_ffi/pubspec.yaml b/thermion_flutter/thermion_flutter_ffi/pubspec.yaml index b6a02395..60a48b09 100644 --- a/thermion_flutter/thermion_flutter_ffi/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_ffi/pubspec.yaml @@ -1,7 +1,7 @@ name: thermion_flutter_ffi description: An FFI interface for the thermion_flutter plugin (all platforms except web). repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter -version: 0.1.0+2 +version: 0.1.0+3 environment: sdk: ">=3.3.0 <4.0.0" @@ -22,8 +22,8 @@ dependencies: flutter: sdk: flutter plugin_platform_interface: ^2.1.0 - thermion_flutter_platform_interface: ^0.1.0+2 - thermion_dart: ^0.1.0+2 + thermion_flutter_platform_interface: ^0.1.0+3 + thermion_dart: ^0.1.0+3 dev_dependencies: flutter_test: diff --git a/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md b/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md index d48431e7..49e6056e 100644 --- a/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.0+3 + + - Update a dependency to the latest release. + ## 0.1.0+2 - Update a dependency to the latest release. diff --git a/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml b/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml index 214bb4e4..91c02d51 100644 --- a/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml @@ -1,7 +1,7 @@ name: thermion_flutter_platform_interface description: A common platform interface for the thermion_flutter plugin. repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter -version: 0.1.0+2 +version: 0.1.0+3 environment: sdk: ">=3.3.0 <4.0.0" @@ -11,7 +11,7 @@ dependencies: flutter: sdk: flutter plugin_platform_interface: ^2.1.0 - thermion_dart: ^0.1.0+2 + thermion_dart: ^0.1.0+3 dev_dependencies: flutter_test: diff --git a/thermion_flutter/thermion_flutter_web/CHANGELOG.md b/thermion_flutter/thermion_flutter_web/CHANGELOG.md index 4e0cf851..bc67e010 100644 --- a/thermion_flutter/thermion_flutter_web/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.1+3 + + - Update a dependency to the latest release. + ## 0.0.1+2 - Update a dependency to the latest release. diff --git a/thermion_flutter/thermion_flutter_web/pubspec.yaml b/thermion_flutter/thermion_flutter_web/pubspec.yaml index 9a895349..a7956a55 100644 --- a/thermion_flutter/thermion_flutter_web/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_web/pubspec.yaml @@ -1,7 +1,7 @@ name: thermion_flutter_web description: A web platform interface for the thermion_flutter plugin. repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter -version: 0.0.1+2 +version: 0.0.1+3 environment: sdk: ">=3.3.0 <4.0.0" @@ -20,8 +20,8 @@ dependencies: sdk: flutter plugin_platform_interface: ^2.1.0 web: ^0.5.1 - thermion_dart: ^0.1.0+2 - thermion_flutter_platform_interface: ^0.1.0+2 + thermion_dart: ^0.1.0+3 + thermion_flutter_platform_interface: ^0.1.0+3 flutter_web_plugins: sdk: flutter From aa85dcfa201eeba04c04e4075fc496397a228519 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 21 Jun 2024 16:52:10 +0800 Subject: [PATCH 46/51] fix: add dummy asset to build.dart on Linux builds so we can use the package on a Linux host --- thermion_dart/hook/build.dart | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/thermion_dart/hook/build.dart b/thermion_dart/hook/build.dart index 4e550f53..da71bf8b 100644 --- a/thermion_dart/hook/build.dart +++ b/thermion_dart/hook/build.dart @@ -22,11 +22,29 @@ void main(List args) async { var platform = config.targetOS.toString().toLowerCase(); - // We don't support Linux (yet), so the native/Filament libraries won't be - // compiled/available. However, we still want to be able to run the Dart - // package itself on a Linux host(e.g. for dart_services backed), so if - // we detect that we're running on Linux, just exit here. + // We don't support Linux (yet), so the native/Filament libraries won't be + // compiled/available. However, we still want to be able to run the Dart + // package itself on a Linux host(e.g. for dart_services backed), so if + // we detect that we're running on Linux, add some dummy native code + // assets and exit early. if (platform == "linux") { + final linkMode = DynamicLoadingBundled(); + final name = "thermion_dart.dart"; + final libUri = config.outputDirectory + .resolve(config.targetOS.libraryFileName(name, linkMode)); + output.addAssets( + [ + NativeCodeAsset( + package: config.packageName, + name: name, + file: libUri, + linkMode: linkMode, + os: config.targetOS, + architecture: config.dryRun ? null : config.targetArchitecture, + ) + ], + linkInPackage: null, + ); return; } From a066df55f9b458130c56abbcf0532b59e9e04389 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 21 Jun 2024 16:52:19 +0800 Subject: [PATCH 47/51] chore(release): publish packages - thermion_dart@0.1.0+4 - thermion_flutter_web@0.0.1+4 - thermion_flutter_platform_interface@0.1.0+4 - thermion_flutter@0.1.1+5 - thermion_flutter_ffi@0.1.0+4 --- CHANGELOG.md | 34 +++++++++++++++++++ thermion_dart/CHANGELOG.md | 4 +++ thermion_dart/pubspec.yaml | 2 +- .../thermion_flutter/CHANGELOG.md | 4 +++ .../thermion_flutter/pubspec.yaml | 10 +++--- .../thermion_flutter_ffi/CHANGELOG.md | 4 +++ .../thermion_flutter_ffi/pubspec.yaml | 6 ++-- .../CHANGELOG.md | 4 +++ .../pubspec.yaml | 4 +-- .../thermion_flutter_web/CHANGELOG.md | 4 +++ .../thermion_flutter_web/pubspec.yaml | 6 ++-- 11 files changed, 68 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cddc4e7e..71fa77a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,40 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 2024-06-21 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`thermion_dart` - `v0.1.0+4`](#thermion_dart---v0104) + - [`thermion_flutter_web` - `v0.0.1+4`](#thermion_flutter_web---v0014) + - [`thermion_flutter_platform_interface` - `v0.1.0+4`](#thermion_flutter_platform_interface---v0104) + - [`thermion_flutter` - `v0.1.1+5`](#thermion_flutter---v0115) + - [`thermion_flutter_ffi` - `v0.1.0+4`](#thermion_flutter_ffi---v0104) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `thermion_flutter_web` - `v0.0.1+4` + - `thermion_flutter_platform_interface` - `v0.1.0+4` + - `thermion_flutter` - `v0.1.1+5` + - `thermion_flutter_ffi` - `v0.1.0+4` + +--- + +#### `thermion_dart` - `v0.1.0+4` + + - **FIX**: add dummy asset to build.dart on Linux builds so we can use the package on a Linux host. + + ## 2024-06-21 ### Changes diff --git a/thermion_dart/CHANGELOG.md b/thermion_dart/CHANGELOG.md index a22ae60a..2a6e99a1 100644 --- a/thermion_dart/CHANGELOG.md +++ b/thermion_dart/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.0+4 + + - **FIX**: add dummy asset to build.dart on Linux builds so we can use the package on a Linux host. + ## 0.1.0+3 - **FIX**: exit build.dart early on Linux builds so we can use the package on a Linux host. diff --git a/thermion_dart/pubspec.yaml b/thermion_dart/pubspec.yaml index 17d338fd..6a580f37 100644 --- a/thermion_dart/pubspec.yaml +++ b/thermion_dart/pubspec.yaml @@ -1,6 +1,6 @@ name: thermion_dart description: 3D rendering toolkit for Dart. -version: 0.1.0+3 +version: 0.1.0+4 homepage: https://docs.page/nmfisher/thermion repository: https://github.com/nmfisher/thermion diff --git a/thermion_flutter/thermion_flutter/CHANGELOG.md b/thermion_flutter/thermion_flutter/CHANGELOG.md index f262d779..0d8503ba 100644 --- a/thermion_flutter/thermion_flutter/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.1+5 + + - Update a dependency to the latest release. + ## 0.1.1+4 - Update a dependency to the latest release. diff --git a/thermion_flutter/thermion_flutter/pubspec.yaml b/thermion_flutter/thermion_flutter/pubspec.yaml index 937460f2..db175ddd 100644 --- a/thermion_flutter/thermion_flutter/pubspec.yaml +++ b/thermion_flutter/thermion_flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: thermion_flutter description: Flutter plugin for 3D rendering with the Thermion toolkit. -version: 0.1.1+4 +version: 0.1.1+5 homepage: https://docs.page/nmfisher/thermion repository: https://github.com/nmfisher/thermion @@ -17,10 +17,10 @@ dependencies: plugin_platform_interface: ^2.0.0 ffi: ^2.1.2 animation_tools_dart: ^0.0.4 - thermion_dart: ^0.1.0+3 - thermion_flutter_platform_interface: ^0.1.0+3 - thermion_flutter_ffi: ^0.1.0+3 - thermion_flutter_web: ^0.0.1+3 + thermion_dart: ^0.1.0+4 + thermion_flutter_platform_interface: ^0.1.0+4 + thermion_flutter_ffi: ^0.1.0+4 + thermion_flutter_web: ^0.0.1+4 logging: ^1.2.0 dev_dependencies: diff --git a/thermion_flutter/thermion_flutter_ffi/CHANGELOG.md b/thermion_flutter/thermion_flutter_ffi/CHANGELOG.md index 1c9cd748..dad0cd70 100644 --- a/thermion_flutter/thermion_flutter_ffi/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter_ffi/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.0+4 + + - Update a dependency to the latest release. + ## 0.1.0+3 - Update a dependency to the latest release. diff --git a/thermion_flutter/thermion_flutter_ffi/pubspec.yaml b/thermion_flutter/thermion_flutter_ffi/pubspec.yaml index 60a48b09..3af124fa 100644 --- a/thermion_flutter/thermion_flutter_ffi/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_ffi/pubspec.yaml @@ -1,7 +1,7 @@ name: thermion_flutter_ffi description: An FFI interface for the thermion_flutter plugin (all platforms except web). repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter -version: 0.1.0+3 +version: 0.1.0+4 environment: sdk: ">=3.3.0 <4.0.0" @@ -22,8 +22,8 @@ dependencies: flutter: sdk: flutter plugin_platform_interface: ^2.1.0 - thermion_flutter_platform_interface: ^0.1.0+3 - thermion_dart: ^0.1.0+3 + thermion_flutter_platform_interface: ^0.1.0+4 + thermion_dart: ^0.1.0+4 dev_dependencies: flutter_test: diff --git a/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md b/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md index 49e6056e..a3907b11 100644 --- a/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.0+4 + + - Update a dependency to the latest release. + ## 0.1.0+3 - Update a dependency to the latest release. diff --git a/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml b/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml index 91c02d51..d2b61d65 100644 --- a/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml @@ -1,7 +1,7 @@ name: thermion_flutter_platform_interface description: A common platform interface for the thermion_flutter plugin. repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter -version: 0.1.0+3 +version: 0.1.0+4 environment: sdk: ">=3.3.0 <4.0.0" @@ -11,7 +11,7 @@ dependencies: flutter: sdk: flutter plugin_platform_interface: ^2.1.0 - thermion_dart: ^0.1.0+3 + thermion_dart: ^0.1.0+4 dev_dependencies: flutter_test: diff --git a/thermion_flutter/thermion_flutter_web/CHANGELOG.md b/thermion_flutter/thermion_flutter_web/CHANGELOG.md index bc67e010..dd933b09 100644 --- a/thermion_flutter/thermion_flutter_web/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.1+4 + + - Update a dependency to the latest release. + ## 0.0.1+3 - Update a dependency to the latest release. diff --git a/thermion_flutter/thermion_flutter_web/pubspec.yaml b/thermion_flutter/thermion_flutter_web/pubspec.yaml index a7956a55..c6c3e225 100644 --- a/thermion_flutter/thermion_flutter_web/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_web/pubspec.yaml @@ -1,7 +1,7 @@ name: thermion_flutter_web description: A web platform interface for the thermion_flutter plugin. repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter -version: 0.0.1+3 +version: 0.0.1+4 environment: sdk: ">=3.3.0 <4.0.0" @@ -20,8 +20,8 @@ dependencies: sdk: flutter plugin_platform_interface: ^2.1.0 web: ^0.5.1 - thermion_dart: ^0.1.0+3 - thermion_flutter_platform_interface: ^0.1.0+3 + thermion_dart: ^0.1.0+4 + thermion_flutter_platform_interface: ^0.1.0+4 flutter_web_plugins: sdk: flutter From 0222ba2d6c6483e56b99f0440759150229b5d57b Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 21 Jun 2024 17:18:43 +0800 Subject: [PATCH 48/51] chore(release): publish packages - thermion_dart@0.1.1 --- CHANGELOG.md | 21 +++++++++++++++++++ thermion_dart/CHANGELOG.md | 4 ++++ thermion_dart/pubspec.yaml | 2 +- .../thermion_flutter/pubspec.yaml | 2 +- .../thermion_flutter_ffi/pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- .../thermion_flutter_web/pubspec.yaml | 2 +- 7 files changed, 30 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71fa77a9..2b426b2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,27 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 2024-06-21 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`thermion_dart` - `v0.1.1`](#thermion_dart---v011) + +--- + +#### `thermion_dart` - `v0.1.1` + + - Bump "thermion_dart" to `0.1.1`. + + ## 2024-06-21 ### Changes diff --git a/thermion_dart/CHANGELOG.md b/thermion_dart/CHANGELOG.md index 2a6e99a1..e4526a46 100644 --- a/thermion_dart/CHANGELOG.md +++ b/thermion_dart/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.1 + + - Bump "thermion_dart" to `0.1.1`. + ## 0.1.0+4 - **FIX**: add dummy asset to build.dart on Linux builds so we can use the package on a Linux host. diff --git a/thermion_dart/pubspec.yaml b/thermion_dart/pubspec.yaml index 6a580f37..6385bc3a 100644 --- a/thermion_dart/pubspec.yaml +++ b/thermion_dart/pubspec.yaml @@ -1,6 +1,6 @@ name: thermion_dart description: 3D rendering toolkit for Dart. -version: 0.1.0+4 +version: 0.1.1 homepage: https://docs.page/nmfisher/thermion repository: https://github.com/nmfisher/thermion diff --git a/thermion_flutter/thermion_flutter/pubspec.yaml b/thermion_flutter/thermion_flutter/pubspec.yaml index db175ddd..ac29eaeb 100644 --- a/thermion_flutter/thermion_flutter/pubspec.yaml +++ b/thermion_flutter/thermion_flutter/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: plugin_platform_interface: ^2.0.0 ffi: ^2.1.2 animation_tools_dart: ^0.0.4 - thermion_dart: ^0.1.0+4 + thermion_dart: ^0.1.1 thermion_flutter_platform_interface: ^0.1.0+4 thermion_flutter_ffi: ^0.1.0+4 thermion_flutter_web: ^0.0.1+4 diff --git a/thermion_flutter/thermion_flutter_ffi/pubspec.yaml b/thermion_flutter/thermion_flutter_ffi/pubspec.yaml index 3af124fa..b85a17e5 100644 --- a/thermion_flutter/thermion_flutter_ffi/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_ffi/pubspec.yaml @@ -23,7 +23,7 @@ dependencies: sdk: flutter plugin_platform_interface: ^2.1.0 thermion_flutter_platform_interface: ^0.1.0+4 - thermion_dart: ^0.1.0+4 + thermion_dart: ^0.1.1 dev_dependencies: flutter_test: diff --git a/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml b/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml index d2b61d65..88677b34 100644 --- a/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml @@ -11,7 +11,7 @@ dependencies: flutter: sdk: flutter plugin_platform_interface: ^2.1.0 - thermion_dart: ^0.1.0+4 + thermion_dart: ^0.1.1 dev_dependencies: flutter_test: diff --git a/thermion_flutter/thermion_flutter_web/pubspec.yaml b/thermion_flutter/thermion_flutter_web/pubspec.yaml index c6c3e225..051d69d5 100644 --- a/thermion_flutter/thermion_flutter_web/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_web/pubspec.yaml @@ -20,7 +20,7 @@ dependencies: sdk: flutter plugin_platform_interface: ^2.1.0 web: ^0.5.1 - thermion_dart: ^0.1.0+4 + thermion_dart: ^0.1.1 thermion_flutter_platform_interface: ^0.1.0+4 flutter_web_plugins: sdk: flutter From b4f9a5c2afb1c7716ba661dd5b4cfe2ba148959e Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Sat, 22 Jun 2024 10:29:08 +0800 Subject: [PATCH 49/51] docs: update with links to playground --- README.md | 1 + docs.json | 3 ++- docs/contributing.mdx | 4 ++++ thermion_dart/README.md | 1 + thermion_flutter/thermion_flutter/README.md | 3 ++- 5 files changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2cc67dfc..00d364a3 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ Quickstart (Flutter)DocumentationShowcase • + PlaygroundDiscord

diff --git a/docs.json b/docs.json index f177d25e..9221f82f 100644 --- a/docs.json +++ b/docs.json @@ -7,7 +7,8 @@ "Getting Started", [ ["Overview", "/"], - ["Quick Start", "/quickstart"] + ["Quick Start", "/quickstart"], + ["Playground", "https://dartpad.thermion.dev"] ] ], ["Misc.", [["Contributing", "/contributing"]]] diff --git a/docs/contributing.mdx b/docs/contributing.mdx index a4466ca5..3f3758f3 100644 --- a/docs/contributing.mdx +++ b/docs/contributing.mdx @@ -4,3 +4,7 @@ Thermion is an open source project and we welcome all contributions from every l Please [join us on Discord](https://discord.gg/h2VdDK3EAQ) if you'd like some guidance or just want to chat. +## + +We are now using [Melos](https://melos.invertase.dev/) to manage the repository. This lets us auto-generate changelogs & versioning from commit messages, so if you wish to submit a PR, please use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/). + diff --git a/thermion_dart/README.md b/thermion_dart/README.md index 12053abd..0f4dd7ba 100644 --- a/thermion_dart/README.md +++ b/thermion_dart/README.md @@ -4,6 +4,7 @@ Quickstart (Flutter)DocumentationShowcase • + PlaygroundDiscord

diff --git a/thermion_flutter/thermion_flutter/README.md b/thermion_flutter/thermion_flutter/README.md index 1c9b88a3..af32f1b4 100644 --- a/thermion_flutter/thermion_flutter/README.md +++ b/thermion_flutter/thermion_flutter/README.md @@ -3,7 +3,8 @@

Quickstart (Flutter)Documentation • - Showcase • + Showcase • + PlaygroundDiscord

From c635bd38137a1dd7121a3691b39fe0178cd49660 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Sat, 22 Jun 2024 10:29:41 +0800 Subject: [PATCH 50/51] chore(release): publish packages - thermion_dart@0.1.1+1 - thermion_flutter@0.1.1+6 - thermion_flutter_web@0.0.1+5 - thermion_flutter_platform_interface@0.1.0+5 - thermion_flutter_ffi@0.1.0+5 --- CHANGELOG.md | 37 +++++++++++++++++++ thermion_dart/CHANGELOG.md | 4 ++ thermion_dart/pubspec.yaml | 2 +- .../thermion_flutter/CHANGELOG.md | 4 ++ .../thermion_flutter/pubspec.yaml | 10 ++--- .../thermion_flutter_ffi/CHANGELOG.md | 4 ++ .../thermion_flutter_ffi/pubspec.yaml | 6 +-- .../CHANGELOG.md | 4 ++ .../pubspec.yaml | 4 +- .../thermion_flutter_web/CHANGELOG.md | 4 ++ .../thermion_flutter_web/pubspec.yaml | 6 +-- 11 files changed, 71 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b426b2a..d0395fe9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,43 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 2024-06-22 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`thermion_dart` - `v0.1.1+1`](#thermion_dart---v0111) + - [`thermion_flutter` - `v0.1.1+6`](#thermion_flutter---v0116) + - [`thermion_flutter_web` - `v0.0.1+5`](#thermion_flutter_web---v0015) + - [`thermion_flutter_platform_interface` - `v0.1.0+5`](#thermion_flutter_platform_interface---v0105) + - [`thermion_flutter_ffi` - `v0.1.0+5`](#thermion_flutter_ffi---v0105) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `thermion_flutter_web` - `v0.0.1+5` + - `thermion_flutter_platform_interface` - `v0.1.0+5` + - `thermion_flutter_ffi` - `v0.1.0+5` + +--- + +#### `thermion_dart` - `v0.1.1+1` + + - **DOCS**: update with links to playground. + +#### `thermion_flutter` - `v0.1.1+6` + + - **DOCS**: update with links to playground. + + ## 2024-06-21 ### Changes diff --git a/thermion_dart/CHANGELOG.md b/thermion_dart/CHANGELOG.md index e4526a46..0e468c1b 100644 --- a/thermion_dart/CHANGELOG.md +++ b/thermion_dart/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.1+1 + + - **DOCS**: update with links to playground. + ## 0.1.1 - Bump "thermion_dart" to `0.1.1`. diff --git a/thermion_dart/pubspec.yaml b/thermion_dart/pubspec.yaml index 6385bc3a..1e2a9b72 100644 --- a/thermion_dart/pubspec.yaml +++ b/thermion_dart/pubspec.yaml @@ -1,6 +1,6 @@ name: thermion_dart description: 3D rendering toolkit for Dart. -version: 0.1.1 +version: 0.1.1+1 homepage: https://docs.page/nmfisher/thermion repository: https://github.com/nmfisher/thermion diff --git a/thermion_flutter/thermion_flutter/CHANGELOG.md b/thermion_flutter/thermion_flutter/CHANGELOG.md index 0d8503ba..924bceb3 100644 --- a/thermion_flutter/thermion_flutter/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.1+6 + + - **DOCS**: update with links to playground. + ## 0.1.1+5 - Update a dependency to the latest release. diff --git a/thermion_flutter/thermion_flutter/pubspec.yaml b/thermion_flutter/thermion_flutter/pubspec.yaml index ac29eaeb..1145a139 100644 --- a/thermion_flutter/thermion_flutter/pubspec.yaml +++ b/thermion_flutter/thermion_flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: thermion_flutter description: Flutter plugin for 3D rendering with the Thermion toolkit. -version: 0.1.1+5 +version: 0.1.1+6 homepage: https://docs.page/nmfisher/thermion repository: https://github.com/nmfisher/thermion @@ -17,10 +17,10 @@ dependencies: plugin_platform_interface: ^2.0.0 ffi: ^2.1.2 animation_tools_dart: ^0.0.4 - thermion_dart: ^0.1.1 - thermion_flutter_platform_interface: ^0.1.0+4 - thermion_flutter_ffi: ^0.1.0+4 - thermion_flutter_web: ^0.0.1+4 + thermion_dart: ^0.1.1+1 + thermion_flutter_platform_interface: ^0.1.0+5 + thermion_flutter_ffi: ^0.1.0+5 + thermion_flutter_web: ^0.0.1+5 logging: ^1.2.0 dev_dependencies: diff --git a/thermion_flutter/thermion_flutter_ffi/CHANGELOG.md b/thermion_flutter/thermion_flutter_ffi/CHANGELOG.md index dad0cd70..95d3d6e1 100644 --- a/thermion_flutter/thermion_flutter_ffi/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter_ffi/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.0+5 + + - Update a dependency to the latest release. + ## 0.1.0+4 - Update a dependency to the latest release. diff --git a/thermion_flutter/thermion_flutter_ffi/pubspec.yaml b/thermion_flutter/thermion_flutter_ffi/pubspec.yaml index b85a17e5..01b42d78 100644 --- a/thermion_flutter/thermion_flutter_ffi/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_ffi/pubspec.yaml @@ -1,7 +1,7 @@ name: thermion_flutter_ffi description: An FFI interface for the thermion_flutter plugin (all platforms except web). repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter -version: 0.1.0+4 +version: 0.1.0+5 environment: sdk: ">=3.3.0 <4.0.0" @@ -22,8 +22,8 @@ dependencies: flutter: sdk: flutter plugin_platform_interface: ^2.1.0 - thermion_flutter_platform_interface: ^0.1.0+4 - thermion_dart: ^0.1.1 + thermion_flutter_platform_interface: ^0.1.0+5 + thermion_dart: ^0.1.1+1 dev_dependencies: flutter_test: diff --git a/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md b/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md index a3907b11..554835f0 100644 --- a/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.0+5 + + - Update a dependency to the latest release. + ## 0.1.0+4 - Update a dependency to the latest release. diff --git a/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml b/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml index 88677b34..c9f48b4b 100644 --- a/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_platform_interface/pubspec.yaml @@ -1,7 +1,7 @@ name: thermion_flutter_platform_interface description: A common platform interface for the thermion_flutter plugin. repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter -version: 0.1.0+4 +version: 0.1.0+5 environment: sdk: ">=3.3.0 <4.0.0" @@ -11,7 +11,7 @@ dependencies: flutter: sdk: flutter plugin_platform_interface: ^2.1.0 - thermion_dart: ^0.1.1 + thermion_dart: ^0.1.1+1 dev_dependencies: flutter_test: diff --git a/thermion_flutter/thermion_flutter_web/CHANGELOG.md b/thermion_flutter/thermion_flutter_web/CHANGELOG.md index dd933b09..6d161ca4 100644 --- a/thermion_flutter/thermion_flutter_web/CHANGELOG.md +++ b/thermion_flutter/thermion_flutter_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.1+5 + + - Update a dependency to the latest release. + ## 0.0.1+4 - Update a dependency to the latest release. diff --git a/thermion_flutter/thermion_flutter_web/pubspec.yaml b/thermion_flutter/thermion_flutter_web/pubspec.yaml index 051d69d5..4e0ea4f1 100644 --- a/thermion_flutter/thermion_flutter_web/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_web/pubspec.yaml @@ -1,7 +1,7 @@ name: thermion_flutter_web description: A web platform interface for the thermion_flutter plugin. repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter -version: 0.0.1+4 +version: 0.0.1+5 environment: sdk: ">=3.3.0 <4.0.0" @@ -20,8 +20,8 @@ dependencies: sdk: flutter plugin_platform_interface: ^2.1.0 web: ^0.5.1 - thermion_dart: ^0.1.1 - thermion_flutter_platform_interface: ^0.1.0+4 + thermion_dart: ^0.1.1+1 + thermion_flutter_platform_interface: ^0.1.0+5 flutter_web_plugins: sdk: flutter From 0571f3e65398759ebfc3732a8dfdb459ae2f1b42 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Tue, 25 Jun 2024 19:04:18 +0800 Subject: [PATCH 51/51] docs: add Android & Windows pages --- docs.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs.json b/docs.json index 9221f82f..bb7ab58a 100644 --- a/docs.json +++ b/docs.json @@ -11,7 +11,11 @@ ["Playground", "https://dartpad.thermion.dev"] ] ], - ["Misc.", [["Contributing", "/contributing"]]] + ["Misc.", [ + ["Windows", "/windows"], + ["Android", "/android"], + ["Contributing", "/contributing"] + ]] ] }