Compare commits

...

56 Commits

Author SHA1 Message Date
Nick Fisher
c635bd3813 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
2024-06-22 10:29:41 +08:00
Nick Fisher
b4f9a5c2af docs: update with links to playground 2024-06-22 10:29:08 +08:00
Nick Fisher
0222ba2d6c chore(release): publish packages
- thermion_dart@0.1.1
2024-06-21 17:18:43 +08:00
Nick Fisher
a066df55f9 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
2024-06-21 16:52:19 +08:00
Nick Fisher
aa85dcfa20 fix: add dummy asset to build.dart on Linux builds so we can use the package on a Linux host 2024-06-21 16:52:10 +08:00
Nick Fisher
a3370a775f 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
2024-06-21 16:21:02 +08:00
Nick Fisher
b889fddcfa fix: exit build.dart early on Linux builds so we can use the package on a Linux host 2024-06-21 16:20:42 +08:00
Nick Fisher
090c36c92b 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
2024-06-21 15:24:19 +08:00
Nick Fisher
9eb2285433 refactor: rearrange some stubs/imports for easier web WASM deployment 2024-06-21 15:20:08 +08:00
Nick Fisher
c28c518057 chore(release): publish packages
- thermion_flutter@0.1.1+2
2024-06-21 14:09:24 +08:00
Nick Fisher
81ffbdd824 fix: update Flutter example project to use new API
fix: don't keep example project Podfile.lock in repo
2024-06-21 11:46:25 +08:00
Nick Fisher
55b5c7068e fix: add logging dependency to thermion_flutter 2024-06-21 11:30:23 +08:00
Nick Fisher
78a758f5d4 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
2024-06-21 11:26:34 +08:00
Nick Fisher
fd19fc1c68 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
2024-06-21 11:19:16 +08:00
Nick Fisher
cc3020c268 remove dependency_overrides 2024-06-21 11:19:16 +08:00
Nick Fisher
6902c27d02 fix: catch exception if gizmo unavailable in ThermionGestureDestectorDesktop 2024-06-21 11:19:16 +08:00
Nick Fisher
0ec7e2af95 refactor: export ThermionViewerWasm for web and hide FFI/WASM version 2024-06-21 11:19:16 +08:00
Nick Fisher
c6c918907a rename web interop classes 2024-06-21 11:19:16 +08:00
Nick Fisher
d3891e481e fix log dir creation in build.dart 2024-06-21 11:19:16 +08:00
Nick Fisher
563ffd2902 Merge branch 'develop' of github.com:nmfisher/thermion into develop 2024-06-21 11:18:33 +08:00
Nick Fisher
2ce5cb1f62 Merge pull request #41 from Hannnes1/feature/melos
Add melos
2024-06-21 02:08:34 +10:00
Hannes
842c6b1581 Remove unnecessary files from git 2024-06-20 09:07:53 +02:00
Hannes
b833b2d288 Add melos 2024-06-19 15:47:00 +02:00
Nick Fisher
013730b108 destroy existing texture if it exists but size does match requested dimensions when createTexture is called 2024-06-19 16:39:10 +08:00
Nick Fisher
846f45bb63 don't destroy dummy image texture if clearBackgroundImage has been called without setBackgroundImage having been called first 2024-06-19 16:38:14 +08:00
Nick Fisher
735612af66 bump pubspec dependency versions 2024-06-19 13:39:43 +08:00
Nick Fisher
e823083e86 update quickstart to use new API 2024-06-19 13:32:40 +08:00
Nick Fisher
ae15f2114e update quickstart iOS project 2024-06-19 13:32:16 +08:00
Nick Fisher
cb17bf0ffd increment version number for thermion_flutter 2024-06-19 13:32:03 +08:00
Nick Fisher
84101b457f add onDispose() implementations for web viewers 2024-06-19 13:31:09 +08:00
Nick Fisher
aefedf8c00 CHANGELOG 2024-06-19 13:24:08 +08:00
Nick Fisher
ab793387bc use Unmanaged.passRetained rather than passUnretained for ThermionDartTexture 2024-06-19 13:21:47 +08:00
Nick Fisher
534e5b46a8 (Swift/macos) dont make ThermionFlutterTexture inherit from ThermionDartTexture; reuse render callbacks/resource loader functions; add check for concurrent calls to destroyTexture 2024-06-19 13:20:04 +08:00
Nick Fisher
3468e7da55 dont make ThermionFlutterTexture inherit from ThermionDartTexture 2024-06-19 13:19:04 +08:00
Nick Fisher
1f58717635 only require ThermionViewer to be passed to ThermionWidget 2024-06-19 13:17:11 +08:00
Nick Fisher
7dc0e91e6b fix RenderLoop to use (C) ResourceLoaderWrapper and destroy RL when viewer is destroyed 2024-06-19 13:16:33 +08:00
Nick Fisher
49b8e7023b free all resources in FilamentViewer destructor 2024-06-19 13:15:36 +08:00
Nick Fisher
a455e4f88a construct (C++) ResourceLoaderWrapperImpl from (C) ResourceLoaderWrapper 2024-06-19 13:13:36 +08:00
Nick Fisher
3785580b01 free all resources in SceneManager 2024-06-19 13:12:28 +08:00
Nick Fisher
6cfa86fe75 make ResourceBuffer.hpp C++ only and add constructor to create from C struct 2024-06-19 13:11:01 +08:00
Nick Fisher
dc70909715 move Scene class to own file and add dispose() method 2024-06-19 13:04:34 +08:00
Nick Fisher
89f49f98c4 replace print with Logger 2024-06-19 13:01:20 +08:00
Nick Fisher
025e39318d add implementation for onDispose to ThermionViewerFFI 2024-06-19 12:59:16 +08:00
Nick Fisher
cfa7805970 add onDispose method to ThermionViewer to register callbacks on disposal 2024-06-19 12:57:49 +08:00
Nick Fisher
31e68df1c5 make ThermionFlutterPlugin static, remove dispose() and add internal listener for ThermionViewer.onDispose 2024-06-19 12:57:16 +08:00
Nick Fisher
8e75555540 use C header for ResourceBuffer in Swift bridging 2024-06-19 12:52:58 +08:00
Nick Fisher
c8fa220362 remove dispose()/viewer getter from platform interface, rename initialize to createViewer 2024-06-19 12:52:39 +08:00
Nick Fisher
8e892a1f23 log to file for build.dart, check for SUCCESS token 2024-06-19 12:51:39 +08:00
Nick Fisher
ee8769b7d7 use C header for ResourceBuffer in Swift bridging 2024-06-19 12:50:29 +08:00
Nick Fisher
7ecf097861 reduce quickstart Dart SDK min version 2024-06-18 16:29:53 +08:00
Nick Fisher
4f4be9b7d7 rename Flutter method channels 2024-06-18 16:29:19 +08:00
Nick Fisher
338fe3bb9f Merge branch 'master' into develop 2024-06-17 14:49:02 +08:00
Nick Fisher
04b1498cd9 Merge pull request #39 from 1l0/fix-link
Fix broken links
2024-06-17 16:48:44 +10:00
Nick Fisher
1d3dab88d1 bump pubspec version 2024-06-17 14:48:13 +08:00
1l0
ec16f97023 fix link 2024-06-17 15:36:57 +09:00
Nick Fisher
436b978537 rename/export Flutter widgets 2024-06-17 13:19:24 +08:00
88 changed files with 3183 additions and 1912 deletions

3
.gitignore vendored
View File

@@ -7,3 +7,6 @@
.DS_Store
**/*/build
**/*/pubspec.lock
pubspec_overrides.yaml
/pubspec.lock
*.iml

231
CHANGELOG.md Normal file
View File

@@ -0,0 +1,231 @@
# 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-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
---
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
---
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
---
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
---
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
---
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
---
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.

View File

@@ -1,9 +1,10 @@
![Thermion Logo](docs/logo.png)
<p align="center">
<a href="https://docs.page/nmfisher/flutter_filament/quickstart">Quickstart (Flutter)</a> •
<a href="https://docs.page/nmfisher/flutter_filament">Documentation</a> •
<a href="https://github.com/nmfisher/thermion/docs/examples/">Showcase</a> •
<a href="https://docs.page/nmfisher/thermion/quickstart">Quickstart (Flutter)</a> •
<a href="https://docs.page/nmfisher/thermion">Documentation</a> •
<a href="https://docs.page/nmfisher/thermion/examples">Showcase</a> •
<a href="https://dartpad.thermion.dev/">Playground</a> •
<a href="https://discord.gg/h2VdDK3EAQ">Discord</a>
</p>

View File

@@ -7,7 +7,8 @@
"Getting Started",
[
["Overview", "/"],
["Quick Start", "/quickstart"]
["Quick Start", "/quickstart"],
["Playground", "https://dartpad.thermion.dev"]
]
],
["Misc.", [["Contributing", "/contributing"]]]

View File

@@ -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/).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 78 KiB

View File

@@ -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();
}

View File

@@ -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<MyApp> 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<ExampleWidget> {
final _plugin = ThermionFlutterPlugin();
ThermionViewer? _viewer;
EdgeInsets _viewportMargin = EdgeInsets.zero;
@@ -99,105 +94,103 @@ class ExampleWidgetState extends State<ExampleWidget> {
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _plugin.initialized,
builder: (_, AsyncSnapshot<bool> 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!),
// )
]
]);
}
}

View File

@@ -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<StatefulWidget> createState() => _AssetSubmenuState();
@@ -26,8 +26,8 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
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<AssetSubmenu> {
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<AssetSubmenu> {
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<AssetSubmenu> {
-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<AssetSubmenu> {
_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<AssetSubmenu> {
),
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<AssetSubmenu> {
),
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<AssetSubmenu> {
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<AssetSubmenu> {
: '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')),
],

View File

@@ -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<StatefulWidget> createState() => _CameraSubmenuState();
@@ -22,10 +22,10 @@ class _CameraSubmenuState extends State<CameraSubmenu> {
@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<CameraSubmenu> {
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<CameraSubmenu> {
_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<CameraSubmenu> {
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<CameraSubmenu> {
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<CameraSubmenu> {
onPressed: () {
ExampleWidgetState.frustumCulling =
!ExampleWidgetState.frustumCulling;
widget.controller.viewer
widget.viewer
.setViewFrustumCulling(ExampleWidgetState.frustumCulling);
},
child: Text(
@@ -128,7 +128,7 @@ class _CameraSubmenuState extends State<CameraSubmenu> {
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<CameraSubmenu> {
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<CameraSubmenu> {
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<CameraSubmenu> {
onPressed: () {
ExampleWidgetState.orbitSpeedX = speed;
ExampleWidgetState.orbitSpeedY = speed;
widget.controller.viewer.setCameraManipulatorOptions(
widget.viewer.setCameraManipulatorOptions(
orbitSpeedX: ExampleWidgetState.orbitSpeedX,
orbitSpeedY: ExampleWidgetState.orbitSpeedY,
zoomSpeed: ExampleWidgetState.zoomSpeed);

View File

@@ -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<StatefulWidget> createState() => _ControllerMenuState();
State<StatefulWidget> createState() => _ViewerMenuState();
}
class _ControllerMenuState extends State<ControllerMenu> {
void _createController({String? uberArchivePath}) async {
widget.controller.initialize(uberArchivePath: uberArchivePath);
widget.onControllerCreated();
class _ViewerMenuState extends State<ViewerMenu> {
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<ControllerMenu> {
}
@override
void didUpdateWidget(ControllerMenu oldWidget) {
void didUpdateWidget(ViewerMenu oldWidget) {
super.didUpdateWidget(oldWidget);
}
@@ -50,14 +52,14 @@ class _ControllerMenuState extends State<ControllerMenu> {
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<ControllerMenu> {
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<ControllerMenu> {
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<ControllerMenu> {
controller.open();
}
},
child: const Text("Controller / Viewer"),
child: const Text("Viewer / Viewer"),
);
});
}

View File

@@ -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<StatefulWidget> createState() => _RenderingSubmenuState();
@@ -19,14 +19,14 @@ class _RenderingSubmenuState extends State<RenderingSubmenu> {
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<RenderingSubmenu> {
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<RenderingSubmenu> {
onPressed: () {
ExampleWidgetState.postProcessing =
!ExampleWidgetState.postProcessing;
widget.controller.viewer
widget.viewer
.setPostProcessing(ExampleWidgetState.postProcessing);
},
child: Text(
@@ -60,7 +60,7 @@ class _RenderingSubmenuState extends State<RenderingSubmenu> {
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<RenderingSubmenu> {
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<RenderingSubmenu> {
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"}) "),

View File

@@ -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<SceneMenu> {
? []
: <Widget>[
RenderingSubmenu(
controller: widget.controller!,
viewer: widget.controller!,
),
AssetSubmenu(controller: widget.controller!),
AssetSubmenu(viewer: widget.controller!),
CameraSubmenu(
controller: widget.controller!,
viewer: widget.controller!,
),
],
builder:

View File

@@ -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

View File

@@ -17,6 +17,18 @@ dependencies:
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
@@ -36,4 +48,4 @@ flutter:
- assets/BusterDrone/
- assets/BusterDrone/textures/
- assets/FlightHelmet/

View File

@@ -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'

View File

@@ -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

View File

@@ -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 = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
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 = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
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 = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
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 = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
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 = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
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 = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
/* 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 = "<group>";
};
331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
@@ -76,6 +104,20 @@
path = RunnerTests;
sourceTree = "<group>";
};
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 = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
@@ -94,6 +136,8 @@
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
4A129A4D3EB41F9DE0778AAC /* Pods */,
0DB20859A61D425430A5D097 /* Frameworks */,
);
sourceTree = "<group>";
};
@@ -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",

View File

@@ -4,4 +4,7 @@
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -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<MyHomePage> {
bool _loaded = false;
late ThermionFlutterPlugin _thermionFlutterPlugin;
late Future<ThermionViewer> _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(),
]);
}
}

View File

@@ -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
@@ -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

4
melos.yaml Normal file
View File

@@ -0,0 +1,4 @@
name: thermion_workspace
packages:
- thermion_dart
- thermion_flutter/**

6
pubspec.yaml Normal file
View File

@@ -0,0 +1,6 @@
name: thermion_workspace
environment:
sdk: '>=3.0.0 <4.0.0'
dev_dependencies:
melos: ^6.1.0

View File

@@ -1,2 +1,27 @@
## 0.1.1+1
- **DOCS**: update with links to playground.
## 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.
## 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.
## 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

View File

@@ -4,6 +4,7 @@
<a href="https://docs.page/nmfisher/flutter_filament/quickstart">Quickstart (Flutter)</a> •
<a href="https://docs.page/nmfisher/flutter_filament">Documentation</a> •
<a href="https://github.com/nmfisher/thermion/docs/examples/">Showcase</a> •
<a href="https://dartpad.thermion.dev/">Playground</a> •
<a href="https://discord.gg/h2VdDK3EAQ">Discord</a>
</p>

View File

@@ -6,12 +6,48 @@ import 'package:native_toolchain_c/native_toolchain_c.dart';
void main(List<String> args) async {
await build(args, (config, output) async {
var logDir = Directory(
"${config.packageRoot.toFilePath()}.dart_tool/thermion_dart/log/");
if (!logDir.existsSync()) {
logDir.createSync(recursive: true);
}
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();
// 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;
}
var libDir = config.dryRun ? "" : (await getLibDir(config, logger)).path;
final packageName = config.packageName;
@@ -71,7 +107,7 @@ void main(List<String> args) async {
} else {
libs.add("stdc++");
}
final flags = [];
final flags = []; //"-fsanitize=address"];
final defines = <String, String?>{};
var frameworks = [];
@@ -85,7 +121,15 @@ void main(List<String> 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 +151,7 @@ void main(List<String> 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 +206,90 @@ void main(List<String> 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<Directory> 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<int>;
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<int>;
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;
}

View File

@@ -1,5 +1,8 @@
library filament_dart;
export 'thermion_dart/thermion_viewer.dart';
export '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';

View File

@@ -1,3 +1 @@
export 'web/compatibility.dart' if (dart.library.io) 'native/compatibility.dart';
export 'native/compatibility.dart';

View File

@@ -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;

View File

@@ -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";

View File

@@ -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<JSNumber> rotationMatrix);
@JS('removeIbl')
external JSPromise removeIbl();
@JS('addLight')
external JSPromise<JSNumber> 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<JSNumber> loadGlb(String path, int numInstances);
@JS('createInstance')
external JSPromise<JSNumber> createInstance(ThermionEntity entity);
@JS('getInstanceCount')
external JSPromise<JSNumber> getInstanceCount(ThermionEntity entity);
@JS('getInstances')
external JSPromise<JSArray<JSNumber>> getInstances(ThermionEntity entity);
@JS('loadGltf')
external JSPromise<JSNumber> 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<JSNumber> weights);
@JS('getMorphTargetNames')
external JSPromise<JSArray<JSString>> getMorphTargetNames(
ThermionEntity entity, ThermionEntity childEntity);
@JS('getBoneNames')
external JSPromise<JSArray<JSString>> getBoneNames(
ThermionEntity entity, int skinIndex);
@JS('getAnimationNames')
external JSPromise<JSArray<JSString>> getAnimationNames(
ThermionEntity entity);
@JS('getAnimationDuration')
external JSPromise<JSNumber> getAnimationDuration(
ThermionEntity entity, int animationIndex);
@JS('setMorphAnimationData')
external JSPromise setMorphAnimationData(
ThermionEntity entity,
JSArray<JSArray<JSNumber>> animation,
JSArray<JSString> morphTargets,
JSArray<JSString>? targetMeshNames,
double frameLengthInMs);
@JS('resetBones')
external JSPromise resetBones(ThermionEntity entity);
@JS('addBoneAnimation')
external JSPromise addBoneAnimation(
ThermionEntity entity,
JSArray<JSString> bones,
JSArray<JSArray<JSArray<JSNumber>>> 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<JSNumber> 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<JSNumber> getCameraCullingNear();
@JS('getCameraCullingFar')
external JSPromise<JSNumber> getCameraCullingFar();
@JS('setCameraFocusDistance')
external JSPromise setCameraFocusDistance(double focusDistance);
@JS('getCameraPosition')
external JSPromise<JSArray<JSNumber>> getCameraPosition();
@JS('getCameraModelMatrix')
external JSPromise<JSArray<JSNumber>> getCameraModelMatrix();
@JS('getCameraViewMatrix')
external JSPromise<JSArray<JSNumber>> getCameraViewMatrix();
@JS('getCameraProjectionMatrix')
external JSPromise<JSArray<JSNumber>> getCameraProjectionMatrix();
@JS('getCameraCullingProjectionMatrix')
external JSPromise<JSArray<JSNumber>> getCameraCullingProjectionMatrix();
@JS('getCameraFrustum')
external JSPromise<JSObject> getCameraFrustum();
@JS('setCameraPosition')
external JSPromise setCameraPosition(double x, double y, double z);
@JS('getCameraRotation')
external JSPromise<JSArray<JSNumber>> 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<JSNumber> quaternion);
@JS('setCameraModelMatrix')
external JSPromise setCameraModelMatrix(JSArray<JSNumber> 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<JSNumber> 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<JSNumber> 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<JSArray<JSNumber>> getChildEntities(
ThermionEntity parent, bool renderableOnly);
@JS('getChildEntity')
external JSPromise<JSNumber> getChildEntity(
ThermionEntity parent, String childName);
@JS('getChildEntityNames')
external JSPromise<JSArray<JSString>> 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<JSNumber> createGeometry(JSArray<JSNumber> vertices,
JSArray<JSNumber> indices, String? materialPath, int primitiveType);
@JS('setParent')
external JSPromise setParent(ThermionEntity child, ThermionEntity parent);
@JS('getParent')
external JSPromise<JSNumber> getParent(ThermionEntity child);
@JS('getParent')
external JSPromise<JSNumber> 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<JSArray<JSNumber>> getLocalTransform(
ThermionEntity entity);
@JS('getWorldTransform')
external JSPromise<JSArray<JSNumber>> getWorldTransform(
ThermionEntity entity);
@JS('updateBoneMatrices')
external JSPromise updateBoneMatrices(ThermionEntity entity);
@JS('setTransform')
external JSPromise setTransform(
ThermionEntity entity, JSArray<JSNumber> transform);
@JS('setBoneTransform')
external JSPromise setBoneTransform(
ThermionEntity entity, int boneIndex, JSArray<JSNumber> transform, int skinIndex);
}

View File

@@ -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,19 +15,18 @@ 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()
class ThermionViewerFFIJSDartBridge {
class ThermionViewerJSDartBridge {
final ThermionViewer viewer;
ThermionViewerFFIJSDartBridge(this.viewer);
void bind(
{String globalPropertyName = "filamentViewer"}) {
var wrapper = createJSInteropWrapper<ThermionViewerFFIJSDartBridge>(this)
ThermionViewerJSDartBridge(this.viewer);
void bind({String globalPropertyName = "thermionViewer"}) {
var wrapper = createJSInteropWrapper<ThermionViewerJSDartBridge>(this)
as ThermionViewerJSShim;
globalContext.setProperty(globalPropertyName.toJS, wrapper);
}
@@ -131,10 +130,13 @@ class ThermionViewerFFIJSDartBridge {
@JSExport()
JSPromise<JSNumber> 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()

View File

@@ -3,25 +3,26 @@ 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';
import '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<bool> get initialized async {
@@ -56,6 +57,9 @@ class ThermionViewerFFIJS implements ThermionViewer {
@override
Future<void> 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 = <Future Function()>[];
///
///
///
void onDispose(Future Function() callback) {
_onDispose.add(callback);
}
}

View File

@@ -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<bool>.broadcast();
@override
Stream<bool> get onUpdated => _onUpdatedController.stream;
final _onLoadController = StreamController<ThermionEntity>.broadcast();
@override
Stream<ThermionEntity> get onLoad => _onLoadController.stream;
final _onUnloadController = StreamController<ThermionEntity>.broadcast();
@override
Stream<ThermionEntity> get onUnload => _onUnloadController.stream;
final _lights = <ThermionEntity>{};
final _entities = <ThermionEntity>{};
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<bool> 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<ThermionEntity> listLights() {
return _lights;
}
Stream<ThermionEntity> get onLoad;
@override
Iterable<ThermionEntity> 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<ThermionEntity> 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<ThermionEntity> 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<ThermionEntity> listEntities();
///
/// Attach the gizmo to the specified entity.
///
void select(ThermionEntity entity);
///
///
///
void registerEntity(ThermionEntity entity);
}

View File

@@ -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<bool>.broadcast();
@override
Stream<bool> get onUpdated => _onUpdatedController.stream;
final _onLoadController = StreamController<ThermionEntity>.broadcast();
@override
Stream<ThermionEntity> get onLoad => _onLoadController.stream;
final _onUnloadController = StreamController<ThermionEntity>.broadcast();
@override
Stream<ThermionEntity> get onUnload => _onUnloadController.stream;
final _lights = <ThermionEntity>{};
final _entities = <ThermionEntity>{};
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<ThermionEntity> listLights() {
return _lights;
}
@override
Iterable<ThermionEntity> 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();
}
}

View File

@@ -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';
@@ -279,11 +280,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,51 +702,11 @@ abstract class ThermionViewer {
///
///
AbstractGizmo? get gizmo;
}
///
/// 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.
/// Register a callback to be invoked when this viewer is disposed.
///
Stream<bool> 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<ThermionEntity> get onLoad;
///
/// A Stream containing every ThermionEntity removed from the scene (i.e. via [removeEntity], [clearEntities], [removeLight] or [clearLights]).
Stream<ThermionEntity> get onUnload;
///
/// Lists all light entities currently loaded (not necessarily active in the scene). Does not account for instances.
///
Iterable<ThermionEntity> listLights();
///
/// Lists all entities currently loaded (not necessarily active in the scene). Does not account for instances.
///
Iterable<ThermionEntity> listEntities();
///
/// Attach the gizmo to the specified entity.
///
void select(ThermionEntity entity);
///
///
///
void registerEntity(ThermionEntity entity);
void onDispose(Future Function() callback);
}
abstract class AbstractGizmo {

View File

@@ -1,15 +1,15 @@
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:thermion_dart/thermion_dart/scene.dart';
import 'package:vector_math/vector_math_64.dart';
import 'thermion_viewer.dart';
import 'scene.dart';
import 'compatibility/compatibility.dart';
import 'scene_impl.dart';
import 'package:logging/logging.dart';
typedef ThermionViewerImpl = ThermionViewerFFI;
// ignore: constant_identifier_names
const ThermionEntity _FILAMENT_ASSET_ERROR = 0;
@@ -17,10 +17,10 @@ const ThermionEntity _FILAMENT_ASSET_ERROR = 0;
typedef RenderCallback = Pointer<NativeFunction<Void Function(Pointer<Void>)>>;
class ThermionViewerFFI extends ThermionViewer {
final _compat = Compatibility();
final _logger = Logger("ThermionViewerFFI");
late SceneImpl _scene;
Scene get scene => _scene;
SceneImpl? _scene;
Scene get scene => _scene!;
double _pixelRatio = 1.0;
@@ -72,7 +72,7 @@ class ThermionViewerFFI extends ThermionViewer {
NativeCallable<Void Function(Int32 entityId, Int x, Int y)>.listener(
_onPickResult);
} catch (err) {
print(
_logger.severe(
"Failed to set pick result callback. This is expected if running on web/wasm");
}
_initialize();
@@ -172,14 +172,39 @@ class ThermionViewerFFI extends ThermionViewer {
set_frame_interval_ffi(_viewer!, interval);
}
final _onDispose = <Future Function()>[];
///
///
///
@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 +342,7 @@ class ThermionViewerFFI extends ThermionViewer {
throw Exception("Failed to add light to scene");
}
_scene.registerLight(entity);
_scene!.registerLight(entity);
return entity;
}
@@ -326,7 +351,7 @@ class ThermionViewerFFI extends ThermionViewer {
///
@override
Future removeLight(ThermionEntity entity) async {
_scene.unregisterLight(entity);
_scene!.unregisterLight(entity);
remove_light_ffi(_viewer!, entity);
}
@@ -337,7 +362,7 @@ class ThermionViewerFFI extends ThermionViewer {
Future clearLights() async {
clear_lights_ffi(_viewer!);
_scene.clearLights();
_scene!.clearLights();
}
///
@@ -393,7 +418,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 +429,6 @@ class ThermionViewerFFI extends ThermionViewer {
@override
Future<ThermionEntity> 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<Char>();
final relativeResourcePathPtr =
relativeResourcePath.toNativeUtf8(allocator: allocator).cast<Char>();
@@ -419,7 +439,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;
}
@@ -608,14 +628,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())
@@ -676,8 +694,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}");
@@ -710,7 +728,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];
@@ -720,7 +738,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 +776,7 @@ class ThermionViewerFFI extends ThermionViewer {
numFrames,
animation.frameLengthInMs,
fadeOutInSecs,
fadeInInSecs,
fadeInInSecs,
maxDelta);
}
allocator.free(data);
@@ -894,7 +912,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 +929,7 @@ class ThermionViewerFFI extends ThermionViewer {
await withVoidCallback((callback) {
clear_entities_ffi(_viewer!, callback);
});
_scene.clearEntities();
_scene!.clearEntities();
}
///
@@ -984,7 +1002,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,
@@ -1296,7 +1313,7 @@ class ThermionViewerFFI extends ThermionViewer {
x: (x / _pixelRatio).toDouble(),
y: (viewportDimensions.$2 - y) / _pixelRatio
));
_scene.registerSelected(entityId);
_scene!.registerSelected(entityId);
}
late NativeCallable<Void Function(Int32 entityId, Int x, Int y)>
@@ -1307,7 +1324,7 @@ class ThermionViewerFFI extends ThermionViewer {
///
@override
void pick(int x, int y) async {
_scene.unregisterSelected();
_scene!.unregisterSelected();
filament_pick(
_viewer!,
@@ -1414,9 +1431,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);
@@ -1432,7 +1446,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);
@@ -1451,7 +1465,6 @@ class ThermionViewerFFI extends ThermionViewer {
}
var arrayPtr = get_camera_frustum(_viewer!);
var doubleList = arrayPtr.asTypedList(24);
print(doubleList);
var frustum = Frustum();
frustum.plane0.setFromComponents(
@@ -1624,7 +1637,7 @@ class ThermionViewerFFI extends ThermionViewer {
throw Exception("Failed to create geometry");
}
_scene.registerEntity(entity);
_scene!.registerEntity(entity);
allocator.free(materialPathPtr);
allocator.free(vertexPtr);

View File

@@ -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<ThermionEntity> 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<double> vertices, List<int> indices,
{String? materialPath,
PrimitiveType primitiveType = PrimitiveType.TRIANGLES}) {
// TODO: implement createGeometry
throw UnimplementedError();
}
@override
Future<ThermionEntity> createInstance(ThermionEntity entity) {
// TODO: implement createInstance
throw UnimplementedError();
}
@override
Future dispose() {
// TODO: implement dispose
throw UnimplementedError();
}
@override
Future<double> getAnimationDuration(
ThermionEntity entity, int animationIndex) {
// TODO: implement getAnimationDuration
throw UnimplementedError();
}
@override
Future<List<String>> getAnimationNames(ThermionEntity entity) {
// TODO: implement getAnimationNames
throw UnimplementedError();
}
@override
Future<ThermionEntity> getBone(ThermionEntity parent, int boneIndex,
{int skinIndex = 0}) {
// TODO: implement getBone
throw UnimplementedError();
}
@override
Future<List<String>> getBoneNames(ThermionEntity entity,
{int skinIndex = 0}) {
// TODO: implement getBoneNames
throw UnimplementedError();
}
@override
Future<double> getCameraCullingFar() {
// TODO: implement getCameraCullingFar
throw UnimplementedError();
}
@override
Future<double> getCameraCullingNear() {
// TODO: implement getCameraCullingNear
throw UnimplementedError();
}
@override
Future<Matrix4> getCameraCullingProjectionMatrix() {
// TODO: implement getCameraCullingProjectionMatrix
throw UnimplementedError();
}
@override
Future<Frustum> getCameraFrustum() {
// TODO: implement getCameraFrustum
throw UnimplementedError();
}
@override
Future<Matrix4> getCameraModelMatrix() {
// TODO: implement getCameraModelMatrix
throw UnimplementedError();
}
@override
Future<Vector3> getCameraPosition() {
// TODO: implement getCameraPosition
throw UnimplementedError();
}
@override
Future<Matrix4> getCameraProjectionMatrix() {
// TODO: implement getCameraProjectionMatrix
throw UnimplementedError();
}
@override
Future<Matrix3> getCameraRotation() {
// TODO: implement getCameraRotation
throw UnimplementedError();
}
@override
Future<Matrix4> getCameraViewMatrix() {
// TODO: implement getCameraViewMatrix
throw UnimplementedError();
}
@override
Future<List<ThermionEntity>> getChildEntities(
ThermionEntity parent, bool renderableOnly) {
// TODO: implement getChildEntities
throw UnimplementedError();
}
@override
Future<ThermionEntity> getChildEntity(
ThermionEntity parent, String childName) {
// TODO: implement getChildEntity
throw UnimplementedError();
}
@override
Future<List<String>> getChildEntityNames(ThermionEntity entity,
{bool renderableOnly = true}) {
// TODO: implement getChildEntityNames
throw UnimplementedError();
}
@override
Future<int> getInstanceCount(ThermionEntity entity) {
// TODO: implement getInstanceCount
throw UnimplementedError();
}
@override
Future<List<ThermionEntity>> getInstances(ThermionEntity entity) {
// TODO: implement getInstances
throw UnimplementedError();
}
@override
Future<Matrix4> getInverseBindMatrix(ThermionEntity parent, int boneIndex,
{int skinIndex = 0}) {
// TODO: implement getInverseBindMatrix
throw UnimplementedError();
}
@override
Future<Matrix4> getLocalTransform(ThermionEntity entity) {
// TODO: implement getLocalTransform
throw UnimplementedError();
}
@override
Future<ThermionEntity> getMainCamera() {
// TODO: implement getMainCamera
throw UnimplementedError();
}
@override
Future<List<String>> getMorphTargetNames(
ThermionEntity entity, ThermionEntity childEntity) {
// TODO: implement getMorphTargetNames
throw UnimplementedError();
}
@override
String? getNameForEntity(ThermionEntity entity) {
// TODO: implement getNameForEntity
throw UnimplementedError();
}
@override
Future<ThermionEntity?> getParent(ThermionEntity child) {
// TODO: implement getParent
throw UnimplementedError();
}
@override
Future<Matrix4> 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<bool> get initialized => throw UnimplementedError();
@override
Future<ThermionEntity> loadGlb(String path, {int numInstances = 1}) {
// TODO: implement loadGlb
throw UnimplementedError();
}
@override
Future<ThermionEntity> 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<FilamentPickResult> 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<double> 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<String>? targetMeshNames}) {
// TODO: implement setMorphAnimationData
throw UnimplementedError();
}
@override
Future setMorphTargetWeights(ThermionEntity entity, List<double> 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();
}
}

View File

@@ -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;

View File

@@ -3,7 +3,6 @@
#include "ResourceBuffer.h"
#if defined(__cplusplus)
#ifndef __EMSCRIPTEN__
#include <thread>
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

View File

@@ -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];
};
}

View File

@@ -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
@@ -215,7 +214,7 @@ namespace thermion_filament
Log("Created scene maager");
_imageTexture = Texture::Builder()
_dummyImageTexture = Texture::Builder()
.width(1)
.height(1)
.levels(0x01)
@@ -230,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 (...)
{
@@ -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<const uint8_t *>(rb.data),
static_cast<uint32_t>(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<void *> *callbackData = new std::vector<void *>{(void *)_resourceLoaderWrapper, rbCopy};
@@ -550,6 +549,7 @@ namespace thermion_filament
void FilamentViewer::clearBackgroundImage()
{
_imageMaterial->setDefaultParameter("image", _dummyImageTexture, _imageSampler);
_imageMaterial->setDefaultParameter("showImage", 0);
if (_imageTexture)
{
@@ -680,22 +680,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 +929,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)

View File

@@ -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

View File

@@ -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;
}

View File

@@ -15,7 +15,6 @@ extern "C"
}
#include <pthread.h>
#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<FilamentViewer *()> lambda([=]() mutable
std::packaged_task<void()> 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<void()> lambda([=]() mutable
{
std::packaged_task<void()> 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,

View File

@@ -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
# )

View File

@@ -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;

View File

@@ -1,6 +1,6 @@
name: thermion_dart
description: 3D rendering toolkit for Dart.
version: 0.0.4
version: 0.1.1+1
homepage: https://docs.page/nmfisher/thermion
repository: https://github.com/nmfisher/thermion
@@ -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

View File

@@ -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';

View File

@@ -1 +0,0 @@
../thermion_dart

View File

@@ -0,0 +1,34 @@
## 0.1.1+6
- **DOCS**: update with links to playground.
## 0.1.1+5
- Update a dependency to the latest release.
## 0.1.1+4
- Update a dependency to the latest release.
## 0.1.1+3
- Update a dependency to the latest release.
## 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.
- **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
## 0.0.4
* First release of Dart-only package

View File

@@ -3,7 +3,8 @@
<p align="center">
<a href="https://docs.page/nmfisher/flutter_filament/quickstart">Quickstart (Flutter)</a> •
<a href="https://docs.page/nmfisher/flutter_filament">Documentation</a> •
<a href="https://github.com/nmfisher/thermion/docs/examples/">Showcase</a> •
<a href="https://github.com/nmfisher/thermion/docs/examples/">Showcase</a> •
<a href="https://dartpad.thermion.dev/">Playground</a> •
<a href="https://discord.gg/h2VdDK3EAQ">Discord</a>
</p>

View File

@@ -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"
}

View File

@@ -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;

View File

@@ -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)
}

View File

@@ -0,0 +1,44 @@
#ifndef RESOURCE_BUFFER_H
#define RESOURCE_BUFFER_H
#include <stdint.h>
#include <stdlib.h>
//
// 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

View File

@@ -1,91 +0,0 @@
#ifndef RESOURCE_BUFFER_H
#define RESOURCE_BUFFER_H
#include <stdint.h>
#include <stdlib.h>
//
// 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

View File

@@ -3,9 +3,9 @@
#include <stdint.h>
#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)
// {

View File

@@ -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

View File

@@ -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<bool>();
Future<bool> get initialized => _initialized.future;
bool _initializing = false;
Future<ThermionViewer> initialize({String? uberArchivePath}) async {
_initializing = true;
if (_initialized.isCompleted) {
return ThermionFlutterPlatform.instance.viewer;
static Future<ThermionViewer> 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<ThermionFlutterTexture?> createTexture(
static Future<ThermionFlutterTexture?> 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<ThermionFlutterTexture?> resizeTexture(ThermionFlutterTexture texture,
static Future<ThermionFlutterTexture?> 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();
}
}

View File

@@ -3,15 +3,15 @@ import 'dart:io';
import 'package:thermion_dart/thermion_dart/thermion_viewer.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'filament_gesture_detector_desktop.dart';
import 'filament_gesture_detector_mobile.dart';
import 'thermion_gesture_detector_desktop.dart';
import 'thermion_gesture_detector_mobile.dart';
enum GestureType { rotateCamera, panCamera, panBackground }
///
/// A widget that translates finger/mouse gestures to zoom/pan/rotate actions.
///
class FilamentGestureDetector extends StatelessWidget {
class ThermionGestureDetector extends StatelessWidget {
///
/// The content to display below the gesture detector/listener widget.
/// This will usually be a ThermionWidget (so you can navigate by directly interacting with the viewport), but this is not necessary.
@@ -45,7 +45,7 @@ class FilamentGestureDetector extends StatelessWidget {
final void Function(ScaleUpdateDetails)? onScaleUpdate;
final void Function(ScaleEndDetails)? onScaleEnd;
const FilamentGestureDetector(
const ThermionGestureDetector(
{Key? key,
required this.controller,
this.child,
@@ -68,7 +68,7 @@ class FilamentGestureDetector extends StatelessWidget {
if (kIsWeb || Platform.isLinux ||
Platform.isWindows ||
Platform.isMacOS) {
return FilamentGestureDetectorDesktop(
return ThermionGestureDetectorDesktop(
controller: controller,
child: child,
showControlOverlay: showControlOverlay,
@@ -76,7 +76,7 @@ class FilamentGestureDetector extends StatelessWidget {
enablePicking: enablePicking,
);
} else {
return FilamentGestureDetectorMobile(
return ThermionGestureDetectorMobile(
controller: controller,
child: child,
showControlOverlay: showControlOverlay,

View File

@@ -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';
@@ -7,7 +7,7 @@ import 'package:flutter/material.dart';
///
/// A widget that translates finger/mouse gestures to zoom/pan/rotate actions.
///
class FilamentGestureDetectorDesktop extends StatefulWidget {
class ThermionGestureDetectorDesktop extends StatefulWidget {
///
/// The content to display below the gesture detector/listener widget.
/// This will usually be a ThermionWidget (so you can navigate by directly interacting with the viewport), but this is not necessary.
@@ -37,7 +37,7 @@ class FilamentGestureDetectorDesktop extends StatefulWidget {
///
final bool enablePicking;
const FilamentGestureDetectorDesktop(
const ThermionGestureDetectorDesktop(
{Key? key,
required this.controller,
this.child,
@@ -47,12 +47,13 @@ class FilamentGestureDetectorDesktop extends StatefulWidget {
: super(key: key);
@override
State<StatefulWidget> createState() => _FilamentGestureDetectorDesktopState();
State<StatefulWidget> createState() => _ThermionGestureDetectorDesktopState();
}
class _FilamentGestureDetectorDesktopState
extends State<FilamentGestureDetectorDesktop> {
///
class _ThermionGestureDetectorDesktopState
extends State<ThermionGestureDetectorDesktop> {
final _logger = Logger("_ThermionGestureDetectorDesktopState");
///
///
// ignore: unused_field
@@ -65,11 +66,16 @@ class _FilamentGestureDetectorDesktopState
@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
void didUpdateWidget(FilamentGestureDetectorDesktop oldWidget) {
void didUpdateWidget(ThermionGestureDetectorDesktop oldWidget) {
if (widget.showControlOverlay != oldWidget.showControlOverlay ||
widget.enableCamera != oldWidget.enableCamera ||
widget.enablePicking != oldWidget.enablePicking) {

View File

@@ -7,7 +7,7 @@ enum GestureType { rotateCamera, panCamera, panBackground }
///
/// A widget that translates finger/mouse gestures to zoom/pan/rotate actions.
///
class FilamentGestureDetectorMobile extends StatefulWidget {
class ThermionGestureDetectorMobile extends StatefulWidget {
///
/// The content to display below the gesture detector/listener widget.
/// This will usually be a ThermionWidget (so you can navigate by directly interacting with the viewport), but this is not necessary.
@@ -43,7 +43,7 @@ class FilamentGestureDetectorMobile extends StatefulWidget {
final void Function(ScaleUpdateDetails)? onScaleUpdate;
final void Function(ScaleEndDetails)? onScaleEnd;
const FilamentGestureDetectorMobile(
const ThermionGestureDetectorMobile(
{Key? key,
required this.controller,
this.child,
@@ -57,11 +57,11 @@ class FilamentGestureDetectorMobile extends StatefulWidget {
: super(key: key);
@override
State<StatefulWidget> createState() => _FilamentGestureDetectorMobileState();
State<StatefulWidget> createState() => _ThermionGestureDetectorMobileState();
}
class _FilamentGestureDetectorMobileState
extends State<FilamentGestureDetectorMobile> {
class _ThermionGestureDetectorMobileState
extends State<ThermionGestureDetectorMobile> {
GestureType gestureType = GestureType.panCamera;
final _icons = {
@@ -116,7 +116,7 @@ class _FilamentGestureDetectorMobileState
}
@override
void didUpdateWidget(FilamentGestureDetectorMobile oldWidget) {
void didUpdateWidget(ThermionGestureDetectorMobile oldWidget) {
if (widget.showControlOverlay != oldWidget.showControlOverlay ||
widget.enableCamera != oldWidget.enableCamera ||
widget.enablePicking != oldWidget.enablePicking) {

View File

@@ -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<ThermionWidget> {
@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<ThermionWidget> {
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;

View File

@@ -2,4 +2,6 @@ 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';

View File

@@ -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),

View File

@@ -8,6 +8,8 @@ public class SwiftThermionFlutterPlugin: NSObject, FlutterPlugin {
var texture: ThermionFlutterTexture?
var createdAt = Date()
var destroying = false
var resources:[UInt32:NSData] = [:]
@@ -65,48 +67,59 @@ 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)
}
var resourceLoaderWrapper:UnsafeMutablePointer<ResourceLoaderWrapper>? = 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)
}

View File

@@ -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
}

View File

@@ -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<CVPixelBuffer>? {
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()
}
}
}

View File

@@ -0,0 +1,44 @@
#ifndef RESOURCE_BUFFER_H
#define RESOURCE_BUFFER_H
#include <stdint.h>
#include <stdlib.h>
//
// 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

View File

@@ -1,91 +0,0 @@
#ifndef RESOURCE_BUFFER_H
#define RESOURCE_BUFFER_H
#include <stdint.h>
#include <stdlib.h>
//
// 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

View File

@@ -3,11 +3,14 @@
#include <stdint.h>
#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;

View File

@@ -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'

View File

@@ -1,6 +1,6 @@
name: thermion_flutter
description: Flutter plugin for 3D rendering with the Thermion toolkit.
version: 0.0.3
version: 0.1.1+6
homepage: https://docs.page/nmfisher/thermion
repository: https://github.com/nmfisher/thermion
@@ -17,10 +17,11 @@ 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.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:
flutter_test:

View File

@@ -43,7 +43,7 @@ void ThermionFlutterPlugin::RegisterWithRegistrar(
flutter::PluginRegistrarWindows *registrar) {
auto channel =
std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
registrar->messenger(), "app.polyvox.filament/event",
registrar->messenger(), "dev.thermion.flutter/event",
&flutter::StandardMethodCodec::GetInstance());
auto plugin = std::make_unique<ThermionFlutterPlugin>(

View File

@@ -1,2 +1,22 @@
## 0.1.0+5
- Update a dependency to the latest release.
## 0.1.0+4
- Update a dependency to the latest release.
## 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.
## 0.1.0+1
- Update a dependency to the latest release.
## 0.0.1
* First release of Dart-only package

View File

@@ -2,25 +2,29 @@ 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';
///
/// 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("app.polyvox.filament/event");
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 = <ThermionFlutterTexture>{};
Future initialize({String? uberArchivePath}) async {
Future<ThermionViewer> createViewer({String? uberArchivePath}) async {
var resourceLoader = Pointer<Void>.fromAddress(
await _channel.invokeMethod("getResourceLoaderWrapper"));
@@ -46,22 +50,24 @@ class ThermionFlutterFFI extends ThermionFlutterPlatform {
? nullptr
: Pointer<Void>.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) {
@@ -104,10 +110,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;
@@ -125,39 +136,44 @@ 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<Void>.fromAddress(texture.surfaceAddress!));
await _viewer?.createSwapChain(width.toDouble(), height.toDouble(),
surface: texture.surfaceAddress == null
? nullptr
: Pointer<Void>.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(
await _viewer?.updateViewportAndCameraProjection(
width.toDouble(), height.toDouble());
viewer.render();
_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 +188,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 +204,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<Void>.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
}
}

View File

@@ -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+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.0.1-pre
thermion_dart: ^0.0.1-pre2
thermion_flutter_platform_interface: ^0.1.0+5
thermion_dart: ^0.1.1+1
dev_dependencies:
flutter_test:

View File

@@ -1,2 +1,22 @@
## 0.1.0+5
- Update a dependency to the latest release.
## 0.1.0+4
- Update a dependency to the latest release.
## 0.1.0+3
- Update a dependency to the latest release.
## 0.1.0+2
- Update a dependency to the latest release.
## 0.1.0+1
- Update a dependency to the latest release.
## 0.0.1
* First release

View File

@@ -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<ThermionViewer> createViewer({String? uberArchivePath});
Future<ThermionFlutterTexture?> createTexture(
int width, int height, int offsetLeft, int offsetRight);
@@ -30,5 +27,4 @@ abstract class ThermionFlutterPlatform extends PlatformInterface {
Future<ThermionFlutterTexture?> resizeTexture(ThermionFlutterTexture texture,
int width, int height, int offsetLeft, int offsetRight);
void dispose();
}

View File

@@ -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+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.0.1-pre2
thermion_dart: ^0.1.1+1
dev_dependencies:
flutter_test:

View File

@@ -1,2 +1,22 @@
## 0.0.1+5
- Update a dependency to the latest release.
## 0.0.1+4
- Update a dependency to the latest release.
## 0.0.1+3
- Update a dependency to the latest release.
## 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.
## 0.0.1
* First release of Dart-only package

View File

@@ -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<ThermionFlutterTexture?> 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<ThermionFlutterTexture?> 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<ThermionViewer> 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;
}
}

View File

@@ -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+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.0.1-pre
thermion_flutter_platform_interface: ^0.0.1-pre
thermion_dart: ^0.1.1+1
thermion_flutter_platform_interface: ^0.1.0+5
flutter_web_plugins:
sdk: flutter