Compare commits

...

35 Commits

Author SHA1 Message Date
Nick Fisher
e04d8e76c2 chore(release): publish packages
- thermion_dart@0.3.1
 - thermion_flutter@0.3.1
 - thermion_flutter_method_channel@0.3.1
 - thermion_flutter_web@0.3.1
 - thermion_flutter_platform_interface@0.3.1
2025-07-08 10:33:06 +08:00
Nick Fisher
353b33b7c3 fix: addDestroySwapchain argument to createViewer() (true by default). This is only used on iOS/macOS where a single swapchain is shared between all render targets 2025-07-07 17:37:28 +08:00
Nick Fisher
3c1b26af2c reverse orientation of camera volume 'lens' 2025-07-07 17:21:43 +08:00
Nick Fisher
b4ea80a84c don't allow creating instances for GridOverlay asset and only expose a single entity 2025-07-07 17:21:43 +08:00
Nick Fisher
db44bc6f74 formatting 2025-07-07 17:19:47 +08:00
Nick Fisher
c668549fb0 fix: add flush() to skybox/IBL destroy methods to ensure that textre upload callbacks are completed to avoid stalling 2025-07-07 17:19:36 +08:00
Nick Fisher
a66703b61c (flutter) (example) cleanup 2025-07-07 17:19:06 +08:00
Nick Fisher
cb8943ff72 formatting 2025-07-07 17:18:43 +08:00
Nick Fisher
9f59577f90 fix: duplicate setting for _grid 2025-07-07 16:40:15 +08:00
Nick Fisher
b86145d4c6 refactor: remove covariant keyword from createInstance args 2025-07-04 22:37:15 +08:00
Nick Fisher
cb8672f120 docs: remove camera_manipulation document 2025-07-03 17:49:51 +08:00
Nick Fisher
92578426ac docs: update quickstart 2025-07-03 17:17:44 +08:00
Nick Fisher
4a6479c4d8 docs: fix typo in link 2025-07-03 17:13:47 +08:00
Nick Fisher
2244d3fcb6 docs: remove code from thermion_flutter README.md and point to docs/repository example instead 2025-07-03 17:12:45 +08:00
Nick Fisher
003fd59269 (web) add -Wno-invalid-specialization 2025-07-03 16:23:24 +08:00
Nick Fisher
77e6ef7568 (CI) move to macos for web build 2025-07-03 16:15:20 +08:00
Nick Fisher
6f07d406f8 (CI) move compile-web-wasm to own step 2025-07-03 16:05:49 +08:00
Nick Fisher
bf5551e278 update Makefile to download wasm release libs 2025-07-03 15:51:23 +08:00
Nick Fisher
64577af352 (CI): update web build 2025-07-03 15:27:54 +08:00
Nick Fisher
951894be41 update CHANGELOG 2025-07-03 15:25:14 +08:00
Nick Fisher
c64b2b8659 docs: update showcase 2025-07-03 15:10:11 +08:00
Nick Fisher
322e77d2b7 add additional camera geometry object 2025-07-03 15:00:02 +08:00
Nick Fisher
a8a52bb2f4 docs: update quickstart/getting started/viewer docs 2025-07-03 14:32:07 +08:00
Nick Fisher
c7dfd293e2 (web) add cplusplus guards for material header 2025-07-03 14:22:26 +08:00
Nick Fisher
cf2498b45f (docs) update web 2025-07-03 14:22:08 +08:00
Nick Fisher
edb7538c36 (example) update viewer example + docs 2025-07-03 14:22:01 +08:00
Nick Fisher
b023e2fb97 chore(release): publish packages
- thermion_dart@0.3.0
 - thermion_flutter@0.3.0
 - thermion_flutter_method_channel@0.3.0
 - thermion_flutter_web@0.3.0
 - thermion_flutter_platform_interface@0.3.0
2025-07-03 13:33:19 +08:00
Nick Fisher
b51cc4b1d1 (flutter) remove direct local dependency in pubspec.yaml 2025-07-03 13:16:06 +08:00
Nick Fisher
d995ed8843 (flutter) cleanup widgets to satisfy pub.dev analyzer 2025-07-03 13:15:50 +08:00
Nick Fisher
b0d34bf6a8 (flutter) rename setExposure method in camera widget 2025-07-03 13:13:04 +08:00
Nick Fisher
2e28b0379d bump min Dart constraint to satisfy pub.dev 2025-07-03 13:09:36 +08:00
Nick Fisher
c899e30a7b chore(release): publish packages
- thermion_dart@0.3.0
 - thermion_flutter@0.3.0
 - thermion_flutter_method_channel@0.3.0
 - thermion_flutter_web@0.3.0
 - thermion_flutter_platform_interface@0.3.0
2025-07-03 13:04:44 +08:00
Nick Fisher
37f8558794 add stub method for resizeWebCanvas to make pub.dev happy 2025-07-03 13:04:24 +08:00
Nick Fisher
3cfa26d284 (web) add package:logging to pubspec to make pub.dev happy 2025-07-03 13:00:41 +08:00
Nick Fisher
b22a82e181 delete swift bindings from thermion_dart package 2025-07-03 12:59:28 +08:00
45 changed files with 501 additions and 904 deletions

View File

@@ -7,6 +7,20 @@ on:
branches: [ "develop" ]
jobs:
compile-web-wasm:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Setup CMake
uses: jwlawson/actions-setup-cmake@v2
with:
cmake-version: '3.25.0' # or 'latest'
- name: Setup Emscripten
uses: mymindstorm/setup-emsdk@v14
with:
version: 'latest'
- name: Compile web
run: make wasm
dart-tests:
runs-on: ubuntu-22.04
steps:
@@ -58,7 +72,6 @@ jobs:
path: |
${{ github.workspace }}/thermion_dart/.dart_tool/thermion_dart/log/build.log
retention-days: 5
flutter_examples:
name: flutter_examples
runs-on: macos-latest

View File

@@ -3,7 +3,7 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## 2025-07-03
## 2025-07-08
### Changes
@@ -11,94 +11,59 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
Packages with breaking changes:
- [`thermion_dart` - `v0.3.0`](#thermion_dart---v030)
- [`thermion_flutter` - `v0.3.0`](#thermion_flutter---v030)
- [`thermion_flutter_method_channel` - `v0.3.0`](#thermion_flutter_method_channel---v030)
- There are no breaking changes in this release.
Packages with other changes:
- [`thermion_flutter_web` - `v0.3.0`](#thermion_flutter_web---v030)
- [`thermion_flutter_platform_interface` - `v0.3.0`](#thermion_flutter_platform_interface---v030)
- [`thermion_dart` - `v0.3.1`](#thermion_dart---v031)
- [`thermion_flutter` - `v0.3.1`](#thermion_flutter---v031)
- [`thermion_flutter_method_channel` - `v0.3.1`](#thermion_flutter_method_channel---v031)
- [`thermion_flutter_web` - `v0.3.1`](#thermion_flutter_web---v031)
- [`thermion_flutter_platform_interface` - `v0.3.1`](#thermion_flutter_platform_interface---v031)
---
#### `thermion_dart` - `v0.3.0`
#### `thermion_dart` - `v0.3.1`
- **REFACTOR**: gizmo/input handler improvements.
- **REFACTOR**: add createGizmoRenderThread.
- **REFACTOR**: Gizmo internals.
- **REFACTOR**: dont require GizmoInputHandler to wrap an existing InputHandler (you can do this by creating your own InputHandler that wraps two children.
- **FIX**: glTF instancing when loaded via buffer.
- **FIX**: don't return entity from SceneManager_addLightRenderThread.
- **FIX**: return light entity from SceneManager.
- **FIX**: store reference to material instances in ThermionViewer so they can be cleaned up on dispose.
- **FIX**: remove MaterialInstance from SceneManager storage when destroyed.
- **FIX**: add destroyCamera to ThermionViewer interface.
- **FIX**: UV calculation for geometry.
- **FIX**: use createGizmoRenderThread.
- **FIX**: remove MaterialInstance from SceneManager storage when destroyed.
- **FIX**: move removeIbl to render thread.
- **FIX**: move material/instance creation to render thread.
- **FIX**: allow destroying instances independently of owner.
- **FIX**: remove MaterialInstance from SceneManager storage when destroyed.
- **FIX**: use render thread methods for grid overlay creation and create ubershader instance.
- **FIX**: only use Windows-style ndkRoot when building on Windows.
- **FIX**: set overlay layer visibility when adding grid.
- **FIX**: only use Windows-style ndkRoot when building on Windows.
- **FIX**: when creating geometry, normals/uvs are set to false by default. remove wirefame camera container (can now be replaced by bounding box methods.
- **FIX**: fix highlights after first.
- **FEAT**: remove bounding box from SceneAsset and create renderable wireframe bounding box in ThermionAsset.
- **FEAT**: add setTransparencyMode to Dart Material class.
- **FEAT**: expose attached entity as Stream on GizmoInputHandler.
- **FEAT**: allow custom material for grid overlay, and material creation from Uint8List.
- **FEAT**: allow setting material instance directly on ThermionAsset.
- **FEAT**: allow passing custom material for grid overlay.
- **FEAT**: allow passing custom material for grid overlay.
- **FEAT**: allow passing custom material for grid overlay.
- **FEAT**: more rotation gizmo improvements.
- **FEAT**: rotation gizmo improvements.
- **FEAT**: add rotation gizmo.
- **FEAT**: add rotation gizmo asset + resource file.
- **FEAT**: add rotation gizmo asset + resource file.
- **FEAT**: use existing material instances when creating an instance of GeometrySceneAsset and no material instance is passed.
- **FEAT**: re-implement grid overlay.
- **FEAT**: add gizmo.glb to assets/resources.
- **FEAT**: add TRACE macro.
- **FEAT**: update Filament to v1.56.4.
- **FEAT**: expose setCastShadows/setReceiveShadows.
- **FEAT**: re-add uvScale, vertexScale to unlit material.
- **FEAT**: re-add uvScale, vertexScale to unlit material.
- **BREAKING** **REFACTOR**: move light methods from FilamentViewer to SceneManager/TLightManager and rename clearLights/clearAssets to destroyLights/destroyAssets.
- **BREAKING** **REFACTOR**: rename removeAsset to destroyAsset.
- **BREAKING** **FIX**: rename removeEntity to removeAsset.
- **BREAKING** **FEAT**: change default near/far to 0.1/100.0.
- **BREAKING** **FEAT**: use raw pointer scale (>1 meaning zoom in, <1 meaning zoom out) rather than binary -1/1 for DelegateInputHandler.
- **BREAKING** **FEAT**: remove Viewer setRenderTarget method (use the View method instead).
- **REFACTOR**: remove covariant keyword from createInstance args.
- **FIX**: add flush() to skybox/IBL destroy methods to ensure that textre upload callbacks are completed to avoid stalling.
- **FIX**: duplicate setting for _grid.
#### `thermion_flutter` - `v0.3.0`
#### `thermion_flutter` - `v0.3.1`
- **REFACTOR**: rename ThermionFlutterTexture->PlatformTextureDescriptor.
- **FIX**: rename msPerFrame property.
- **FEAT**: add FocusNode to ThermionListenerWidget.
- **FEAT**: use new createTextureAndBindToView in ThermionTextureWidget.
- **BREAKING** **REFACTOR**: move light methods from FilamentViewer to SceneManager/TLightManager and rename clearLights/clearAssets to destroyLights/destroyAssets.
- **BREAKING** **FEAT**: remove superseded ThermionWindows widget.
- **BREAKING** **FEAT**: rename thermion_flutter_ffi package to thermion_flutter_method_channel.
- **FIX**: addDestroySwapchain argument to createViewer() (true by default). This is only used on iOS/macOS where a single swapchain is shared between all render targets.
- **DOCS**: fix typo in link.
- **DOCS**: remove code from thermion_flutter README.md and point to docs/repository example instead.
#### `thermion_flutter_method_channel` - `v0.3.0`
#### `thermion_flutter_method_channel` - `v0.3.1`
- **REFACTOR**: rename ThermionFlutterTexture->PlatformTextureDescriptor.
- **BREAKING** **FEAT**: rename thermion_flutter_ffi package to thermion_flutter_method_channel.
- **FIX**: addDestroySwapchain argument to createViewer() (true by default). This is only used on iOS/macOS where a single swapchain is shared between all render targets.
#### `thermion_flutter_web` - `v0.3.0`
#### `thermion_flutter_web` - `v0.3.1`
- **REFACTOR**: rename ThermionFlutterTexture->PlatformTextureDescriptor.
#### `thermion_flutter_platform_interface` - `v0.3.1`
#### `thermion_flutter_platform_interface` - `v0.3.0`
- **FIX**: addDestroySwapchain argument to createViewer() (true by default). This is only used on iOS/macOS where a single swapchain is shared between all render targets.
- **REFACTOR**: rename ThermionFlutterTexture->PlatformTextureDescriptor.
- **FEAT**: create separate createTexture and createTextureAndBindToView interface methods.
# Change Log
#### v0.3.0
This release involved considerable internal refactoring, allowing us to expose more Filament functionality on the Dart side. Previously, most of this functionality was
rigidly implemented in C++ and didn't allow for end-users to take advantage of Filament directly.
This also means there are a number of breaking changes from `0.2.1`. To summarize:
- `ViewerWidget` has been introduced. This is a Flutter widget for users who only need basic rendering and don't need/want to deal with camera/materials/etc directly.
- Users who want more fine-grained control than a `ViewerWidget` can still work with `ThermionViewer` and `ThermionWidget`.
- The singleton `FilamentApp.instance` exposes methods for working almost directly with the underlying Filament engine (e.g. loading custom materials from `Uint8List`, creating textures, etc).
- New interfaces have been added for `Material`, `MaterialInstance`, `Texture`, `View`, `Scene` and `Camera`.
- `ThermionAsset` replaces `ThermionEntity` as the main interface for scene objects.
- Transforms/material instances should be set directly by `asset.setTransform`, `asset.setMaterialInstanceAt`
- Material properties can be set directly on the `MaterialInstance`, e.g. `materialInstance.setParameterFloat4("baseColorFactor", 1.0, 0.0, 0.0, 1.0);
- Linux binaries have been added to `thermion_dart`. This package can be run on Linux (which we are using for CI and automated testing) but there are not yet any Flutter bindings, so `thermion_flutter` cannot run on Linux yet.
- On Windows, `thermion_flutter` now uses the Vulkan backend. This is still experimental and will have limited supported on older hardware (pre-2018).
- Web support for `thermion_dart` has now reached parity with other platforms, though should still be considered experimental. Some manual steps are required to run in a Flutter app or a Dart web app.
## 2025-01-08

View File

@@ -1,8 +1,17 @@
wasm:
cd thermion_dart/native/web &&\
mkdir -p build &&\
cd build &&\
emcmake cmake .. &&\
@if [ ! -f thermion_dart/native/web/lib/release/filament-v1.58.0-web-release.zip ]; then \
echo "Downloading filament-v1.58.0-web-release.zip..."; \
mkdir -p thermion_dart/native/web/lib/release; \
curl -L -o thermion_dart/native/web/lib/release/filament-v1.58.0-web-release.zip \
https://pub-c8b6266320924116aaddce03b5313c0a.r2.dev/filament-v1.58.0-web-release.zip; \
echo "Extracting filament-v1.58.0-web-release.zip..."; \
cd thermion_dart/native/web/lib/release && \
unzip filament-v1.58.0-web-release.zip; \
fi
cd thermion_dart/native/web && \
mkdir -p build && \
cd build && \
emcmake cmake .. && \
emmake make
wasm-clean:
cd thermion_dart/native/web && rm -rf build

View File

@@ -7,6 +7,7 @@
"Getting Started",
[
["Overview", "/"],
["Getting Started", "/getting_started"],
["Quick Start", "/quickstart"],
["Viewer", "/viewer"],
["Camera Manipulation", "/camera_manipulation"]

View File

@@ -1,63 +0,0 @@
## Camera Manipulation (Flutter)
> You can find the entire project below in the [flutter/quickstart](https://github.com/nmfisher/thermion/examples/flutter/camera_manipulation) folder.
A `ThermionListenerWidget` is one option for manipulating the camera with an input device (e.g. mouse or touchscreen gestures).
This will generally wrap a `ThermionWidget`, meaning the entire viewport will act as a receiver for gesture events.
> You can position this independently (for example, stacked vertically beneath the viewport), but this will not translate picking queries correctly.
```
@override
Widget build(BuildContext context) {
return Stack(children: [
if (_thermionViewer != null)
Positioned.fill(
child: ThermionListenerWidget(
inputHandler:
DelegateInputHandler.fixedOrbit(_thermionViewer!)
..setActionForType(InputType.MMB_HOLD_AND_MOVE, InputAction.ROTATE)
..setActionForType(InputType.SCALE1, InputAction.ROTATE)
..setActionForType(InputType.SCALE2, InputAction.ZOOM)
..setActionForType(InputType.SCROLLWHEEL, InputAction.ZOOM),
child: ThermionWidget(
viewer: _thermionViewer!,
))),
]);
```
`ThermionListenerWidget` is a very simple widget; it simply forwards pointer, gesture and keyboard events to the provided [InputHandler], which must decide how to interpret those events.
For example, one [InputHandler] implementation might interpret mouse pointer movement as "rotate the camera", whereas a separate implementation might interpret it as "translate this specific entity".
Thermion provides two default InputHandler implementations for manipulating the camera: [DelegateInputHandler.fixedOrbit] and [DelegateInputHandler.flight].
[DelegateInputHandler.fixedOrbit] will rotate the camera in a fixed orbit around a target point (the origin, by default), and also allow zooming in/out (subject to a minimum distance, which is configurable).
By default, [DelegateInputHandler.fixedOrbit] will:
- rotate the camera when the middle mouse button is held and the pointer is moved (on desktop), and when a single swipe left/right/up/down is detected (on mobile)
- zoom the camera when the scroll wheel is scrolled up/down (on desktop), and when a pinch gesture is detected (on mobile)
You can change the action for a specific input type by calling `setActionForType`; for example, if you wanted to rotate the camera by moving the mouse pointer while holding the left mouse button, you would call:
```
setActionForType(InputType.LMB_HOLD_AND_MOVE, InputAction.ROTATE)
```
See the [InputType] and [InputAction] enums for available input types and actions.
[DelegateInputHandler.flight] will translate keyboard and mouse/touchscreen gestures to free flight camera manipulation.
By default:
- holding the middle mouse button will control the pitch/roll/yaw of the camera
- holding the left mouse button will pan the camera left/right/up/down
- the middle mouse button will zoom/dolly the camera in/out
- the WASD keys will pan the camera left/right/up/down and dolly the camera forward/backward
If these don't exactly fit your use case, you can create your own [InputHandler] implementation. If you think it would be useful to other users, please feel free to submit a PR for your implementation to be included in the base Thermion package.

40
docs/getting_started.mdx Normal file
View File

@@ -0,0 +1,40 @@
## Getting Started
Thermion currently requires the Flutter `master` channel with the `native-assets` experiment enabled.
1. Switch to Flutter master channel, upgrade Flutter, create a new project, then add `thermion_flutter` as a dependency
```bash
$ flutter channel master
$ flutter upgrade
$ flutter config --enable-native-assets
$ cd your_flutter_project
$ flutter pub add thermion_flutter
```
2. If running on iOS or MacOS, change the minimum deployment target to OSX 13
<Accordion title="Click to open iOS/MacOS instructions">
Make sure the `platform` entry refers to `13.0` in your Podfile.
In `macos/Podfile` (for macOS):
```
platform :osx, '13.0'
```
In `ios/Podfile`, (for iOS):
```
platform :ios, '13.0'
```
Then open XCode:
```
open macos/Runner.xcworkspace
```
and change the minimum deployment target to 13.0:
![XCode screenshot](images/macos_min_deployment.png)
</Accordion>

View File

@@ -1,23 +1,18 @@
# ViewerWidget Documentation
# Quick Start
`ViewerWidget` is a simplified wrapper around the Thermion 3D viewer that makes it easy to display 3D models in your Flutter application.
If all you need is a quick and easy route to rendering a single 3D model in your Flutter application, start with `ViewerWidget`.
## Overview
This is a simplified, Flutter-only wrapper around the underlying 3D rendering API with sane defaults for most people.
`ViewerWidget` handles the setup and configuration of a Thermion viewer, including:
`ViewerWidget` handles all the setup and configuration of the underlying Thermion API, including:
- Loading 3D models (glTF assets)
- Configuring skyboxes and image-based lighting
- Setting up camera positions and manipulators
- Managing the rendering lifecycle
## Installation
## Setup
First, make sure you have the Thermion Flutter plugin added to your dependencies:
```yaml
dependencies:
thermion_flutter: ^latest_version
```
Follow the steps listed in [Getting Started](./getting_started) to configure your Flutter installation and project.
## Basic Usage

View File

@@ -1,18 +1,25 @@
## Showcase
## KTX Viewer
https://ktxviewer.com
A basic HTML + Dart site for viewing KTX textures in browser.
## DartPad Playground
A custom DartPad that lets you experiment with Thermion from your browser (currently, only Chrome is supported).
[![Screenshot of Thermion Dartpad](images/dartpad.thermion.dev_.png)](https://dartpad.thermion.dev)
(Outdated, needs to be upgraded to Thermion `0.3.0`);
## mixreel (Flutter/Web)
Create 3D worlds and translate to AI video.
[![Screenshot of the mixreeel app](images/ixlabs.app_app.png)](https://mixreel.ai)
## Nick Fisher
My personal website, where I create an interactive clone of myself with Avaturn & Cartesia (no Flutter, made with Thermion and the [Jaspr Dart UI framework](https://github.com/schultek/jaspr)).
@@ -21,3 +28,4 @@ My personal website, where I create an interactive clone of myself with Avaturn

View File

@@ -1,43 +1,14 @@
## Quickstart (Flutter)
## ThermionViewer (Flutter)
> You can find the entire project below in the [examples/flutter/quickstart](https://github.com/nmfisher/thermion/tree/master/examples/flutter/quickstart) folder of the repository.
If you just want to display a 3D object in the viewport with some basic camera controls, use the [ViewerWidget described in the Quickstart section](./quickstart).
1. Switch to Flutter master channel, upgrade Flutter, create a new project, then add `thermion_flutter` as a dependency
If you need want more fine-grained control
```bash
$ flutter channel master
$ flutter upgrade
$ flutter config --enable-native-assets
$ flutter create thermion_sample_project && cd thermion_sample_project
$ flutter pub add thermion_flutter
```
> You can find the entire project below in the [examples/flutter/viewer](https://github.com/nmfisher/thermion/tree/master/examples/flutter/viewer) folder of the repository.
2. If running on iOS or MacOS, change the minimum deployment target to OSX 13
<Accordion title="Click to open iOS/MacOS instructions">
Make sure the `platform` entry refers to `13.0` in your Podfile.
In `macos/Podfile` (for macOS):
```
platform :osx, '13.0'
```
In `ios/Podfile`, (for iOS):
```
platform :ios, '13.0'
```
Then open XCode:
```
open macos/Runner.xcworkspace
```
and change the minimum deployment target to 13.0:
![XCode screenshot](images/macos_min_deployment.png)
</Accordion>
2. Add a folder containing your assets (glTF model + skybox ktx) to your `pubspec.yaml` asset list
@@ -53,11 +24,11 @@ flutter
```dart
class _MyAppState extends State<MyApp> {
  late ThermionFlutterPlugin _thermionFlutterPlugin; 
late Future<ThermionViewer> _thermionViewer;
  void initState() {   
_thermionFlutterPlugin = ThermionFlutterPlugin();   
_thermionViewer = _thermionFlutterPlugin.createViewer(); 
ThermionFlutterPlugin.createViewer().then((viewer) {
_thermionViewer = viewer;
});
}
}

View File

@@ -1,26 +1,41 @@
## Web
First, you must manually compile the wasm+javascript module. This requires:
Web support is still experimental and currently requires you to manually compile Thermion to WASM first.
Requirements:
1) GNU Make
2) CMake
3) Emscripten
From the project root directory
```
make wasm
thermion % ls -l
total 272
drwxr-xr-x 4 nickfisher staff 128 Jul 3 14:06 assets
-rw-r--r-- 1 nickfisher staff 84532 Jul 3 14:06 CHANGELOG.md
-rw-r--r-- 1 nickfisher staff 2349 Jul 3 14:06 Dockerfile
drwxr-xr-x 19 nickfisher staff 608 Jul 3 14:06 docs
-rw-r--r-- 1 nickfisher staff 748 Jul 3 14:06 docs.json
drwxr-xr-x 7 nickfisher staff 224 Jan 8 17:01 examples
-rw-r--r-- 1 nickfisher staff 11341 Oct 23 2024 LICENSE
-rw-r--r-- 1 nickfisher staff 2161 Jul 3 14:06 Makefile
drwxr-xr-x@ 13 nickfisher staff 416 Jul 3 14:06 materials
-rw-r--r--@ 1 nickfisher staff 517 Oct 23 2024 melos_thermion_workspace.iml
-rw-r--r-- 1 nickfisher staff 77 Oct 23 2024 melos.yaml
-rw-r--r--@ 1 nickfisher staff 9865 Jul 1 13:03 pubspec.lock
-rw-r--r-- 1 nickfisher staff 97 Jun 12 11:38 pubspec.yaml
-rw-r--r-- 1 nickfisher staff 3355 Jul 3 14:06 README.md
drwxr-xr-x@ 22 nickfisher staff 704 Jul 3 14:06 thermion_dart
drwxr-xr-x 7 nickfisher staff 224 Jul 3 14:06 thermion_flutter
thermion % make wasm
```
### Flutter
Thermion requires Android SDK version 22, so change your `app/android/build.gradle` to match this version or higher
Copy thermion_dart.js and thermion_dart.wasm to the `/web` folder for your target app.
```groovy
defaultConfig {
...
minSdk = 22
...
}
```
```
flutter run -d chrome --web-header Cross-Origin-Embedder-Policy=require-corp --web-header Cross-Origin-Opener-Policy=same-origin

View File

@@ -80,7 +80,7 @@ class _MyHomePageState extends State<MyHomePage> {
await _thermionViewer!.loadSkybox("assets/default_env_skybox.ktx");
await _thermionViewer!.loadIbl("assets/default_env_ibl.ktx");
await _thermionViewer!.setBackgroundColor(0, 0, 1, 1);
// await _thermionViewer!.setBackgroundColor(0, 0, 1, 1);
// The underlying Filament rendering engine exposes a number of
// post-processing options (anti-aliasing, bloom, etc).
@@ -93,13 +93,6 @@ class _MyHomePageState extends State<MyHomePage> {
// false is designed to allow you to pause rendering to conserve battery life
await _thermionViewer!.setRendering(true);
// Timer.periodic(Duration(seconds: 1), (_) async {
// final rnd = Random();
// await camera.setLensProjection();
// await _thermionViewer!.setBackgroundColor(rnd.nextDouble(),rnd.nextDouble(), 0, 1.0);
// });
setState(() {});
}
@@ -120,25 +113,16 @@ class _MyHomePageState extends State<MyHomePage> {
child: ElevatedButton(onPressed: _load, child: const Text("Load")));
}
Widget _renderButton() {
Widget _changeBgColor() {
return Center(
child: ElevatedButton(
onPressed: () async {
// final rnd = Random();
// await FilamentApp.instance!.setClearColor(
// rnd.nextDouble(), rnd.nextDouble(), rnd.nextDouble(), 1.0);
// final camera = await _thermionViewer!.getActiveCamera();
// await _thermionViewer!.setRendering(false);
// // await camera.setLensProjection();
// await _thermionViewer!.removeSkybox();
// // await _thermionViewer!.setBackgroundColor(rnd.nextDouble(), 1.0, 0, 1.0);
// await _thermionViewer!.clearBackgroundImage(destroy: true);
// // await _thermionViewer!.setBackgroundImage("/Users/nickfisher/Documents/thermion/thermion_dart/test/assets/cube_texture_512x512.png");
// await FilamentApp.instance!.render();
final rnd = Random();
await _thermionViewer!.removeSkybox();
await _thermionViewer!.setBackgroundColor(
rnd.nextDouble(), rnd.nextDouble(), rnd.nextDouble(), 1.0);
},
child: const Text("Render")));
child: const Text("Change background color")));
}
Widget _unloadButton() {
@@ -153,9 +137,9 @@ class _MyHomePageState extends State<MyHomePage> {
body: Stack(children: [
if (_thermionViewer != null)
Positioned.fill(
child: ThermionWidget(
child: ThermionListenerWidget(inputHandler: DelegateInputHandler.fixedOrbit(_thermionViewer!), child:ThermionWidget(
viewer: _thermionViewer!,
)),
))),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
@@ -165,7 +149,7 @@ class _MyHomePageState extends State<MyHomePage> {
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (_thermionViewer == null) _loadButton(),
if (_thermionViewer != null) _renderButton(),
if (_thermionViewer != null) _changeBgColor(),
if (_thermionViewer != null) _unloadButton(),
])))
]));

View File

@@ -558,7 +558,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
@@ -641,7 +641,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
@@ -691,7 +691,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;

View File

@@ -1,3 +1,13 @@
## 0.3.1
- **REFACTOR**: remove covariant keyword from createInstance args.
- **FIX**: add flush() to skybox/IBL destroy methods to ensure that textre upload callbacks are completed to avoid stalling.
- **FIX**: duplicate setting for _grid.
## 0.3.0
- n
## 0.3.0
> Note: This release has breaking changes.

View File

@@ -221,8 +221,11 @@ extension DartBigIntExtension on int {
}
extension Float32ListExtension on Float32List {
Uint8List asUint8List() {
return this.buffer.asUint8List(this.offsetInBytes);
}
}
}
void resizeWebCanvas(int width, int height) {
throw UnsupportedError("Not supported on non-web platforms");
}

View File

@@ -157,7 +157,6 @@ extension Uint8ListExtension on Uint8List {
extension Float32ListExtension on Float32List {
Pointer<Float32> get address {
final ptr = getPointer<Float32>(this, this.toJS);
final bar =
Float32ArrayWrapper(NativeLibrary.instance.HEAPU8.buffer, ptr, length)
@@ -167,7 +166,8 @@ extension Float32ListExtension on Float32List {
}
Uint8List asUint8List() {
var ptr = Pointer<Uint8>(_NativeLibrary.instance._emscripten_get_byte_offset(this.toJS));
var ptr = Pointer<Uint8>(
_NativeLibrary.instance._emscripten_get_byte_offset(this.toJS));
return ptr.asTypedList(length * 4);
}
}
@@ -468,3 +468,7 @@ void stackRestore(Pointer ptr) =>
void getStackFree() {
print(_NativeLibrary.instance._emscripten_stack_get_free());
}
void resizeWebCanvas(int width, int height) {
Thermion_resizeCanvas(width, height);
}

View File

@@ -132,7 +132,7 @@ class FFIAsset extends ThermionAsset {
///
@override
Future<FFIAsset> createInstance(
{covariant List<MaterialInstance>? materialInstances = null}) async {
{List<MaterialInstance>? materialInstances = null}) async {
if(isInstance) {
return instanceOwner!.createInstance(materialInstances: materialInstances);
}

View File

@@ -12,16 +12,39 @@ class GridOverlay extends FFIAsset {
static Future<GridOverlay> create(
FFIFilamentApp app, Pointer<TAnimationManager> animationManager) async {
if (_overlay == null) {
_gridMaterial ??= FFIMaterial(Material_createGridMaterial(app.engine), app);
_gridMaterial ??=
FFIMaterial(Material_createGridMaterial(app.engine), app);
final asset = await withPointerCallback<TSceneAsset>((cb) =>
SceneAsset_createGridRenderThread(
app.engine, _gridMaterial!.getNativeHandle(), cb));
_overlay = GridOverlay(asset, app, animationManager);
var materialInstance = await _overlay!.getMaterialInstanceAt();
await materialInstance.setParameterFloat3("gridColor", 0.1, 0.1, 0.1);
await materialInstance
.setTransparencyMode(TransparencyMode.TWO_PASSES_TWO_SIDES);
await materialInstance.setCullingMode(CullingMode.NONE);
await materialInstance.setParameterFloat3("gridColor", 0.3, 0.35, 0.3);
final ffiAsset =
FFIAsset(asset, FilamentApp.instance as FFIFilamentApp, nullptr);
await FilamentApp.instance!.setPriority(ffiAsset.entity, 0);
for (final child in await ffiAsset.getChildEntities()) {
await FilamentApp.instance!.setPriority(child, 7);
}
// await materialInstance.setParameterFloat("distance", 10.0);
}
return _overlay!;
}
///
///
///
@override
Future<FFIAsset> createInstance(
{List<MaterialInstance>? materialInstances = null}) async {
throw Exception(
"Only a single instance of the grid overlay can be created");
}
}

View File

@@ -117,7 +117,7 @@ abstract class ThermionAsset {
/// call [Scene.add].
///
Future<ThermionAsset> createInstance(
{covariant List<MaterialInstance>? materialInstances = null});
{List<MaterialInstance>? materialInstances = null});
///
/// Returns the number of instances associated with this asset.

View File

@@ -7,7 +7,6 @@ import 'package:thermion_dart/thermion_dart.dart';
typedef PointerEventDetails = (Vector2 localPosition, Vector2 delta);
abstract class InputHandlerDelegate {
Future handle(List<InputEvent> events) async {
// noop, override to implement
}
@@ -36,34 +35,48 @@ class DelegateInputHandler implements InputHandler {
bool _ready = false;
bool _processing = false;
DelegateInputHandler(
{required this.viewer, required this.delegates, this.batch = true}) {
DelegateInputHandler({
required this.viewer,
required this.delegates,
this.batch = true,
}) {
FilamentApp.instance!.registerRequestFrameHook(process);
viewer.initialized.then((_) {
this._ready = true;
});
}
factory DelegateInputHandler.fixedOrbit(ThermionViewer viewer,
{double minimumDistance = 0.1,
Vector3? target,
InputSensitivityOptions sensitivity = const InputSensitivityOptions(),
ThermionEntity? entity}) {
return DelegateInputHandler(viewer: viewer, delegates: [
OrbitInputHandlerDelegate(viewer.view,
factory DelegateInputHandler.fixedOrbit(
ThermionViewer viewer, {
double minimumDistance = 0.1,
Vector3? target,
InputSensitivityOptions sensitivity = const InputSensitivityOptions(),
ThermionEntity? entity,
}) {
return DelegateInputHandler(
viewer: viewer,
delegates: [
OrbitInputHandlerDelegate(
viewer.view,
sensitivity: sensitivity,
minZoomDistance: minimumDistance,
maxZoomDistance: 1000.0)
]);
maxZoomDistance: 1000.0,
),
],
);
}
factory DelegateInputHandler.flight(ThermionViewer viewer,
{bool freeLook = false,
InputSensitivityOptions sensitivity = const InputSensitivityOptions(),
ThermionEntity? entity}) =>
DelegateInputHandler(viewer: viewer, delegates: [
FreeFlightInputHandlerDelegateV2(viewer.view, sensitivity: sensitivity)
]);
factory DelegateInputHandler.flight(
ThermionViewer viewer, {
bool freeLook = false,
InputSensitivityOptions sensitivity = const InputSensitivityOptions(),
ThermionEntity? entity,
}) => DelegateInputHandler(
viewer: viewer,
delegates: [
FreeFlightInputHandlerDelegateV2(viewer.view, sensitivity: sensitivity),
],
);
Future<void> process() async {
_processing = true;

View File

@@ -1,449 +0,0 @@
// ignore_for_file: camel_case_types, non_constant_identifier_names, unused_element, unused_field, return_of_invalid_type, void_checks, annotate_overrides, no_leading_underscores_for_local_identifiers, library_private_types_in_public_apia
// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
// ignore_for_file: type=lint
import 'dart:ffi' as ffi;
import 'package:objective_c/objective_c.dart' as objc;
@ffi.Native<
ffi.Pointer<objc.ObjCObject> Function(
ffi.Pointer<objc.ObjCObject>, ffi.Pointer<ffi.Void>)>()
external ffi.Pointer<objc.ObjCObject>
_ThermionTextureSwift_protocolTrampoline_1mbt9g9(
ffi.Pointer<objc.ObjCObject> target,
ffi.Pointer<ffi.Void> arg0,
);
late final _class_ThermionTextureSwift =
objc.getClass("swift_module.ThermionTextureSwift");
late final _sel_isKindOfClass_ = objc.registerName("isKindOfClass:");
final _objc_msgSend_19nvye5 = objc.msgSendPointer
.cast<
ffi.NativeFunction<
ffi.Bool Function(
ffi.Pointer<objc.ObjCObject>,
ffi.Pointer<objc.ObjCSelector>,
ffi.Pointer<objc.ObjCObject>)>>()
.asFunction<
bool Function(ffi.Pointer<objc.ObjCObject>,
ffi.Pointer<objc.ObjCSelector>, ffi.Pointer<objc.ObjCObject>)>();
late final _sel_cvMetalTextureCache = objc.registerName("cvMetalTextureCache");
final _objc_msgSend_13yqbb6 = objc.msgSendPointer
.cast<
ffi.NativeFunction<
ffi.Int Function(ffi.Pointer<objc.ObjCObject>,
ffi.Pointer<objc.ObjCSelector>)>>()
.asFunction<
int Function(
ffi.Pointer<objc.ObjCObject>, ffi.Pointer<objc.ObjCSelector>)>();
late final _sel_setCvMetalTextureCache_ =
objc.registerName("setCvMetalTextureCache:");
final _objc_msgSend_9o8504 = objc.msgSendPointer
.cast<
ffi.NativeFunction<
ffi.Void Function(ffi.Pointer<objc.ObjCObject>,
ffi.Pointer<objc.ObjCSelector>, ffi.Int)>>()
.asFunction<
void Function(ffi.Pointer<objc.ObjCObject>,
ffi.Pointer<objc.ObjCSelector>, int)>();
/// WARNING: MTLDevice is a stub. To generate bindings for this class, include
/// MTLDevice in your config's objc-protocols list.
///
/// MTLDevice
interface class MTLDevice extends objc.ObjCProtocolBase {
MTLDevice._(ffi.Pointer<objc.ObjCObject> pointer,
{bool retain = false, bool release = false})
: super(pointer, retain: retain, release: release);
/// Constructs a [MTLDevice] that points to the same underlying object as [other].
MTLDevice.castFrom(objc.ObjCObjectBase other)
: this._(other.ref.pointer, retain: true, release: true);
/// Constructs a [MTLDevice] that wraps the given raw object pointer.
MTLDevice.castFromPointer(ffi.Pointer<objc.ObjCObject> other,
{bool retain = false, bool release = false})
: this._(other, retain: retain, release: release);
}
late final _sel_metalDevice = objc.registerName("metalDevice");
final _objc_msgSend_151sglz = objc.msgSendPointer
.cast<
ffi.NativeFunction<
ffi.Pointer<objc.ObjCObject> Function(ffi.Pointer<objc.ObjCObject>,
ffi.Pointer<objc.ObjCSelector>)>>()
.asFunction<
ffi.Pointer<objc.ObjCObject> Function(
ffi.Pointer<objc.ObjCObject>, ffi.Pointer<objc.ObjCSelector>)>();
late final _sel_setMetalDevice_ = objc.registerName("setMetalDevice:");
final _objc_msgSend_xtuoz7 = objc.msgSendPointer
.cast<
ffi.NativeFunction<
ffi.Void Function(
ffi.Pointer<objc.ObjCObject>,
ffi.Pointer<objc.ObjCSelector>,
ffi.Pointer<objc.ObjCObject>)>>()
.asFunction<
void Function(ffi.Pointer<objc.ObjCObject>,
ffi.Pointer<objc.ObjCSelector>, ffi.Pointer<objc.ObjCObject>)>();
late final _sel_cvMetalTexture = objc.registerName("cvMetalTexture");
late final _sel_setCvMetalTexture_ = objc.registerName("setCvMetalTexture:");
/// WARNING: MTLTexture is a stub. To generate bindings for this class, include
/// MTLTexture in your config's objc-protocols list.
///
/// MTLTexture
interface class MTLTexture extends objc.ObjCProtocolBase {
MTLTexture._(ffi.Pointer<objc.ObjCObject> pointer,
{bool retain = false, bool release = false})
: super(pointer, retain: retain, release: release);
/// Constructs a [MTLTexture] that points to the same underlying object as [other].
MTLTexture.castFrom(objc.ObjCObjectBase other)
: this._(other.ref.pointer, retain: true, release: true);
/// Constructs a [MTLTexture] that wraps the given raw object pointer.
MTLTexture.castFromPointer(ffi.Pointer<objc.ObjCObject> other,
{bool retain = false, bool release = false})
: this._(other, retain: retain, release: release);
}
late final _sel_metalTexture = objc.registerName("metalTexture");
late final _sel_setMetalTexture_ = objc.registerName("setMetalTexture:");
late final _sel_metalTextureAddress = objc.registerName("metalTextureAddress");
final _objc_msgSend_1hz7y9r = objc.msgSendPointer
.cast<
ffi.NativeFunction<
ffi.Long Function(ffi.Pointer<objc.ObjCObject>,
ffi.Pointer<objc.ObjCSelector>)>>()
.asFunction<
int Function(
ffi.Pointer<objc.ObjCObject>, ffi.Pointer<objc.ObjCSelector>)>();
late final _sel_setMetalTextureAddress_ =
objc.registerName("setMetalTextureAddress:");
final _objc_msgSend_4sp4xj = objc.msgSendPointer
.cast<
ffi.NativeFunction<
ffi.Void Function(ffi.Pointer<objc.ObjCObject>,
ffi.Pointer<objc.ObjCSelector>, ffi.Long)>>()
.asFunction<
void Function(ffi.Pointer<objc.ObjCObject>,
ffi.Pointer<objc.ObjCSelector>, int)>();
typedef instancetype = ffi.Pointer<objc.ObjCObject>;
typedef Dartinstancetype = objc.ObjCObjectBase;
late final _sel_init = objc.registerName("init");
late final _sel_initWithWidth_height_isDepth_isStencil_ =
objc.registerName("initWithWidth:height:isDepth:isStencil:");
final _objc_msgSend_1v8gk45 = objc.msgSendPointer
.cast<
ffi.NativeFunction<
ffi.Pointer<objc.ObjCObject> Function(
ffi.Pointer<objc.ObjCObject>,
ffi.Pointer<objc.ObjCSelector>,
ffi.Int64,
ffi.Int64,
ffi.Bool,
ffi.Bool)>>()
.asFunction<
ffi.Pointer<objc.ObjCObject> Function(ffi.Pointer<objc.ObjCObject>,
ffi.Pointer<objc.ObjCSelector>, int, int, bool, bool)>();
late final _sel_destroyTexture = objc.registerName("destroyTexture");
final _objc_msgSend_1pl9qdv = objc.msgSendPointer
.cast<
ffi.NativeFunction<
ffi.Void Function(ffi.Pointer<objc.ObjCObject>,
ffi.Pointer<objc.ObjCSelector>)>>()
.asFunction<
void Function(
ffi.Pointer<objc.ObjCObject>, ffi.Pointer<objc.ObjCSelector>)>();
late final _sel_fillWithPNGImageWithImageURL_ =
objc.registerName("fillWithPNGImageWithImageURL:");
late final _sel_fillColor = objc.registerName("fillColor");
late final _sel_getTextureBytes = objc.registerName("getTextureBytes");
late final _sel_new = objc.registerName("new");
late final _sel_allocWithZone_ = objc.registerName("allocWithZone:");
final _objc_msgSend_1cwp428 = objc.msgSendPointer
.cast<
ffi.NativeFunction<
ffi.Pointer<objc.ObjCObject> Function(ffi.Pointer<objc.ObjCObject>,
ffi.Pointer<objc.ObjCSelector>, ffi.Pointer<objc.NSZone>)>>()
.asFunction<
ffi.Pointer<objc.ObjCObject> Function(ffi.Pointer<objc.ObjCObject>,
ffi.Pointer<objc.ObjCSelector>, ffi.Pointer<objc.NSZone>)>();
late final _sel_alloc = objc.registerName("alloc");
late final _sel_self = objc.registerName("self");
ffi.Pointer<objc.ObjCObject> _ObjCBlock_objcObjCObject_ffiVoid_fnPtrTrampoline(
ffi.Pointer<objc.ObjCBlockImpl> block, ffi.Pointer<ffi.Void> arg0) =>
block.ref.target
.cast<
ffi.NativeFunction<
ffi.Pointer<objc.ObjCObject> Function(
ffi.Pointer<ffi.Void> arg0)>>()
.asFunction<
ffi.Pointer<objc.ObjCObject> Function(
ffi.Pointer<ffi.Void>)>()(arg0);
ffi.Pointer<ffi.Void> _ObjCBlock_objcObjCObject_ffiVoid_fnPtrCallable =
ffi.Pointer.fromFunction<
ffi.Pointer<objc.ObjCObject> Function(
ffi.Pointer<objc.ObjCBlockImpl>, ffi.Pointer<ffi.Void>)>(
_ObjCBlock_objcObjCObject_ffiVoid_fnPtrTrampoline)
.cast();
ffi.Pointer<objc.ObjCObject>
_ObjCBlock_objcObjCObject_ffiVoid_closureTrampoline(
ffi.Pointer<objc.ObjCBlockImpl> block,
ffi.Pointer<ffi.Void> arg0) =>
(objc.getBlockClosure(block) as ffi.Pointer<objc.ObjCObject> Function(
ffi.Pointer<ffi.Void>))(arg0);
ffi.Pointer<ffi.Void> _ObjCBlock_objcObjCObject_ffiVoid_closureCallable =
ffi.Pointer.fromFunction<
ffi.Pointer<objc.ObjCObject> Function(
ffi.Pointer<objc.ObjCBlockImpl>, ffi.Pointer<ffi.Void>)>(
_ObjCBlock_objcObjCObject_ffiVoid_closureTrampoline)
.cast();
/// Construction methods for `objc.ObjCBlock<ffi.Pointer<objc.ObjCObject> Function(ffi.Pointer<ffi.Void>)>`.
abstract final class ObjCBlock_objcObjCObject_ffiVoid {
/// Returns a block that wraps the given raw block pointer.
static objc
.ObjCBlock<ffi.Pointer<objc.ObjCObject> Function(ffi.Pointer<ffi.Void>)>
castFromPointer(ffi.Pointer<objc.ObjCBlockImpl> pointer,
{bool retain = false, bool release = false}) =>
objc.ObjCBlock<
ffi.Pointer<objc.ObjCObject> Function(ffi.Pointer<ffi.Void>)>(
pointer,
retain: retain,
release: release);
/// Creates a block from a C function pointer.
///
/// This block must be invoked by native code running on the same thread as
/// the isolate that registered it. Invoking the block on the wrong thread
/// will result in a crash.
static objc.ObjCBlock<ffi.Pointer<objc.ObjCObject> Function(ffi.Pointer<ffi.Void>)>
fromFunctionPointer(
ffi.Pointer<
ffi.NativeFunction<
ffi.Pointer<objc.ObjCObject> Function(
ffi.Pointer<ffi.Void> arg0)>>
ptr) =>
objc.ObjCBlock<ffi.Pointer<objc.ObjCObject> Function(ffi.Pointer<ffi.Void>)>(
objc.newPointerBlock(_ObjCBlock_objcObjCObject_ffiVoid_fnPtrCallable, ptr.cast()),
retain: false,
release: true);
/// Creates a block from a Dart function.
///
/// This block must be invoked by native code running on the same thread as
/// the isolate that registered it. Invoking the block on the wrong thread
/// will result in a crash.
///
/// If `keepIsolateAlive` is true, this block will keep this isolate alive
/// until it is garbage collected by both Dart and ObjC.
static objc
.ObjCBlock<ffi.Pointer<objc.ObjCObject> Function(ffi.Pointer<ffi.Void>)>
fromFunction(objc.ObjCObjectBase Function(ffi.Pointer<ffi.Void>) fn,
{bool keepIsolateAlive = true}) =>
objc.ObjCBlock<
ffi.Pointer<objc.ObjCObject> Function(ffi.Pointer<ffi.Void>)>(
objc.newClosureBlock(
_ObjCBlock_objcObjCObject_ffiVoid_closureCallable,
(ffi.Pointer<ffi.Void> arg0) =>
fn(arg0).ref.retainAndAutorelease(),
keepIsolateAlive),
retain: false,
release: true);
}
/// Call operator for `objc.ObjCBlock<ffi.Pointer<objc.ObjCObject> Function(ffi.Pointer<ffi.Void>)>`.
extension ObjCBlock_objcObjCObject_ffiVoid_CallExtension on objc
.ObjCBlock<ffi.Pointer<objc.ObjCObject> Function(ffi.Pointer<ffi.Void>)> {
objc.ObjCObjectBase call(ffi.Pointer<ffi.Void> arg0) => objc.ObjCObjectBase(
ref.pointer.ref.invoke
.cast<
ffi.NativeFunction<
ffi.Pointer<objc.ObjCObject> Function(
ffi.Pointer<objc.ObjCBlockImpl> block,
ffi.Pointer<ffi.Void> arg0)>>()
.asFunction<
ffi.Pointer<objc.ObjCObject> Function(
ffi.Pointer<objc.ObjCBlockImpl>,
ffi.Pointer<ffi.Void>)>()(ref.pointer, arg0),
retain: true,
release: true);
}
late final _sel_retain = objc.registerName("retain");
late final _sel_autorelease = objc.registerName("autorelease");
/// ThermionTextureSwift
class ThermionTextureSwift extends objc.NSObject {
ThermionTextureSwift._(ffi.Pointer<objc.ObjCObject> pointer,
{bool retain = false, bool release = false})
: super.castFromPointer(pointer, retain: retain, release: release);
/// Constructs a [ThermionTextureSwift] that points to the same underlying object as [other].
ThermionTextureSwift.castFrom(objc.ObjCObjectBase other)
: this._(other.ref.pointer, retain: true, release: true);
/// Constructs a [ThermionTextureSwift] that wraps the given raw object pointer.
ThermionTextureSwift.castFromPointer(ffi.Pointer<objc.ObjCObject> other,
{bool retain = false, bool release = false})
: this._(other, retain: retain, release: release);
/// Returns whether [obj] is an instance of [ThermionTextureSwift].
static bool isInstance(objc.ObjCObjectBase obj) {
return _objc_msgSend_19nvye5(
obj.ref.pointer, _sel_isKindOfClass_, _class_ThermionTextureSwift);
}
/// cvMetalTextureCache
int get cvMetalTextureCache {
return _objc_msgSend_13yqbb6(this.ref.pointer, _sel_cvMetalTextureCache);
}
/// setCvMetalTextureCache:
set cvMetalTextureCache(int value) {
_objc_msgSend_9o8504(this.ref.pointer, _sel_setCvMetalTextureCache_, value);
}
/// metalDevice
MTLDevice? get metalDevice {
final _ret = _objc_msgSend_151sglz(this.ref.pointer, _sel_metalDevice);
return _ret.address == 0
? null
: MTLDevice.castFromPointer(_ret, retain: true, release: true);
}
/// setMetalDevice:
set metalDevice(MTLDevice? value) {
_objc_msgSend_xtuoz7(this.ref.pointer, _sel_setMetalDevice_,
value?.ref.pointer ?? ffi.nullptr);
}
/// cvMetalTexture
int get cvMetalTexture {
return _objc_msgSend_13yqbb6(this.ref.pointer, _sel_cvMetalTexture);
}
/// setCvMetalTexture:
set cvMetalTexture(int value) {
_objc_msgSend_9o8504(this.ref.pointer, _sel_setCvMetalTexture_, value);
}
/// metalTexture
MTLTexture? get metalTexture {
final _ret = _objc_msgSend_151sglz(this.ref.pointer, _sel_metalTexture);
return _ret.address == 0
? null
: MTLTexture.castFromPointer(_ret, retain: true, release: true);
}
/// setMetalTexture:
set metalTexture(MTLTexture? value) {
_objc_msgSend_xtuoz7(this.ref.pointer, _sel_setMetalTexture_,
value?.ref.pointer ?? ffi.nullptr);
}
/// metalTextureAddress
int get metalTextureAddress {
return _objc_msgSend_1hz7y9r(this.ref.pointer, _sel_metalTextureAddress);
}
/// setMetalTextureAddress:
set metalTextureAddress(int value) {
_objc_msgSend_4sp4xj(this.ref.pointer, _sel_setMetalTextureAddress_, value);
}
/// init
ThermionTextureSwift init() {
final _ret =
_objc_msgSend_151sglz(this.ref.retainAndReturnPointer(), _sel_init);
return ThermionTextureSwift.castFromPointer(_ret,
retain: false, release: true);
}
/// initWithWidth:height:isDepth:isStencil:
ThermionTextureSwift initWithWidth_height_isDepth_isStencil_(
int width, int height, bool isDepth, bool isStencil) {
final _ret = _objc_msgSend_1v8gk45(
this.ref.retainAndReturnPointer(),
_sel_initWithWidth_height_isDepth_isStencil_,
width,
height,
isDepth,
isStencil);
return ThermionTextureSwift.castFromPointer(_ret,
retain: false, release: true);
}
/// destroyTexture
void destroyTexture() {
_objc_msgSend_1pl9qdv(this.ref.pointer, _sel_destroyTexture);
}
/// fillWithPNGImageWithImageURL:
bool fillWithPNGImageWithImageURL_(objc.NSURL imageURL) {
return _objc_msgSend_19nvye5(this.ref.pointer,
_sel_fillWithPNGImageWithImageURL_, imageURL.ref.pointer);
}
/// fillColor
void fillColor() {
_objc_msgSend_1pl9qdv(this.ref.pointer, _sel_fillColor);
}
/// getTextureBytes
objc.NSData? getTextureBytes() {
final _ret = _objc_msgSend_151sglz(this.ref.pointer, _sel_getTextureBytes);
return _ret.address == 0
? null
: objc.NSData.castFromPointer(_ret, retain: true, release: true);
}
/// new
static ThermionTextureSwift new$() {
final _ret = _objc_msgSend_151sglz(_class_ThermionTextureSwift, _sel_new);
return ThermionTextureSwift.castFromPointer(_ret,
retain: false, release: true);
}
/// allocWithZone:
static ThermionTextureSwift allocWithZone_(ffi.Pointer<objc.NSZone> zone) {
final _ret = _objc_msgSend_1cwp428(
_class_ThermionTextureSwift, _sel_allocWithZone_, zone);
return ThermionTextureSwift.castFromPointer(_ret,
retain: false, release: true);
}
/// alloc
static ThermionTextureSwift alloc() {
final _ret = _objc_msgSend_151sglz(_class_ThermionTextureSwift, _sel_alloc);
return ThermionTextureSwift.castFromPointer(_ret,
retain: false, release: true);
}
/// self
ThermionTextureSwift self$1() {
final _ret = _objc_msgSend_151sglz(this.ref.pointer, _sel_self);
return ThermionTextureSwift.castFromPointer(_ret,
retain: true, release: true);
}
/// retain
ThermionTextureSwift retain() {
final _ret = _objc_msgSend_151sglz(this.ref.pointer, _sel_retain);
return ThermionTextureSwift.castFromPointer(_ret,
retain: true, release: true);
}
/// autorelease
ThermionTextureSwift autorelease() {
final _ret = _objc_msgSend_151sglz(this.ref.pointer, _sel_autorelease);
return ThermionTextureSwift.castFromPointer(_ret,
retain: true, release: true);
}
/// Returns a new instance of ThermionTextureSwift constructed with the default `new` method.
factory ThermionTextureSwift() => new$();
}

View File

@@ -1,63 +0,0 @@
#include <stdint.h>
#import <Foundation/Foundation.h>
#import <objc/message.h>
#import "../../../native/include/generated/ThermionTextureSwiftObjCAPI.h"
#if !__has_feature(objc_arc)
#error "This file must be compiled with ARC enabled"
#endif
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
typedef struct {
int64_t version;
void* (*newWaiter)(void);
void (*awaitWaiter)(void*);
void* (*currentIsolate)(void);
void (*enterIsolate)(void*);
void (*exitIsolate)(void);
int64_t (*getMainPortId)(void);
bool (*getCurrentThreadOwnsIsolate)(int64_t);
} DOBJC_Context;
id objc_retainBlock(id);
#define BLOCKING_BLOCK_IMPL(ctx, BLOCK_SIG, INVOKE_DIRECT, INVOKE_LISTENER) \
assert(ctx->version >= 1); \
void* targetIsolate = ctx->currentIsolate(); \
int64_t targetPort = ctx->getMainPortId == NULL ? 0 : ctx->getMainPortId(); \
return BLOCK_SIG { \
void* currentIsolate = ctx->currentIsolate(); \
bool mayEnterIsolate = \
currentIsolate == NULL && \
ctx->getCurrentThreadOwnsIsolate != NULL && \
ctx->getCurrentThreadOwnsIsolate(targetPort); \
if (currentIsolate == targetIsolate || mayEnterIsolate) { \
if (mayEnterIsolate) { \
ctx->enterIsolate(targetIsolate); \
} \
INVOKE_DIRECT; \
if (mayEnterIsolate) { \
ctx->exitIsolate(); \
} \
} else { \
void* waiter = ctx->newWaiter(); \
INVOKE_LISTENER; \
ctx->awaitWaiter(waiter); \
} \
};
Protocol* _ThermionTextureSwift_MTLDevice(void) { return @protocol(MTLDevice); }
Protocol* _ThermionTextureSwift_MTLTexture(void) { return @protocol(MTLTexture); }
typedef id (^ProtocolTrampoline)(void * sel);
__attribute__((visibility("default"))) __attribute__((used))
id _ThermionTextureSwift_protocolTrampoline_1mbt9g9(id target, void * sel) {
return ((ProtocolTrampoline)((id (*)(id, SEL, SEL))objc_msgSend)(target, @selector(getDOBJCDartProtocolMethodForSelector:), sel))(sel);
}
#undef BLOCKING_BLOCK_IMPL
#pragma clang diagnostic pop

View File

@@ -502,8 +502,171 @@ class GeometryHelper {
return Geometry(vertices, indices, normals: _normals, uvs: _uvs);
}
static Geometry camera({
double bodyWidth = 0.6, // X-axis (medium width)
double bodyHeight = 0.7, // Y-axis (medium height)
double bodyDepth = 1.4, // Z-axis (LONG dimension - camera body extends back)
double lensRadius = 0.3,
double lensLength = 0.4,
bool normals = true,
bool uvs = true,
}) {
List<double> verticesList = [];
List<double> normalsList = [];
List<double> uvsList = [];
List<int> indices = [];
static Geometry wireframeCamera({
// Helper function to add a vertex with normal and UV
void addVertex(double x, double y, double z, double nx, double ny, double nz, double u, double v) {
verticesList.addAll([x, y, z]);
if (normals) normalsList.addAll([nx, ny, nz]);
if (uvs) uvsList.addAll([u, v]);
}
int currentIndex = 0;
// === CAMERA BODY (Rectangular box) ===
// Now: width=1.0, height=0.6, depth=1.4 (long)
// The front face (Z=+halfDepth) is the short face where lens attaches
double halfWidth = bodyWidth / 2; // 0.5 (medium)
double halfHeight = bodyHeight / 2; // 0.3 (short)
double halfDepth = bodyDepth / 2; // 0.7 (long - extends backward)
// Front face (SHORT face - where lens attaches) - Z = +halfDepth
addVertex(-halfWidth, -halfHeight, halfDepth, 0, 0, 1, 0, 0); // 0
addVertex(halfWidth, -halfHeight, halfDepth, 0, 0, 1, 1, 0); // 1
addVertex(halfWidth, halfHeight, halfDepth, 0, 0, 1, 1, 1); // 2
addVertex(-halfWidth, halfHeight, halfDepth, 0, 0, 1, 0, 1); // 3
// Back face (SHORT face) - Z = -halfDepth
addVertex(halfWidth, -halfHeight, -halfDepth, 0, 0, -1, 0, 0); // 4
addVertex(-halfWidth, -halfHeight, -halfDepth, 0, 0, -1, 1, 0); // 5
addVertex(-halfWidth, halfHeight, -halfDepth, 0, 0, -1, 1, 1); // 6
addVertex(halfWidth, halfHeight, -halfDepth, 0, 0, -1, 0, 1); // 7
// Top face (LONG face) - Y = +halfHeight
addVertex(-halfWidth, halfHeight, halfDepth, 0, 1, 0, 0, 0); // 8
addVertex(halfWidth, halfHeight, halfDepth, 0, 1, 0, 1, 0); // 9
addVertex(halfWidth, halfHeight, -halfDepth, 0, 1, 0, 1, 1); // 10
addVertex(-halfWidth, halfHeight, -halfDepth, 0, 1, 0, 0, 1); // 11
// Bottom face (LONG face) - Y = -halfHeight
addVertex(-halfWidth, -halfHeight, -halfDepth, 0, -1, 0, 0, 0); // 12
addVertex(halfWidth, -halfHeight, -halfDepth, 0, -1, 0, 1, 0); // 13
addVertex(halfWidth, -halfHeight, halfDepth, 0, -1, 0, 1, 1); // 14
addVertex(-halfWidth, -halfHeight, halfDepth, 0, -1, 0, 0, 1); // 15
// Right face (LONG face) - X = +halfWidth
addVertex(halfWidth, -halfHeight, halfDepth, 1, 0, 0, 0, 0); // 16
addVertex(halfWidth, -halfHeight, -halfDepth, 1, 0, 0, 1, 0); // 17
addVertex(halfWidth, halfHeight, -halfDepth, 1, 0, 0, 1, 1); // 18
addVertex(halfWidth, halfHeight, halfDepth, 1, 0, 0, 0, 1); // 19
// Left face (LONG face) - X = -halfWidth
addVertex(-halfWidth, -halfHeight, -halfDepth, -1, 0, 0, 0, 0); // 20
addVertex(-halfWidth, -halfHeight, halfDepth, -1, 0, 0, 1, 0); // 21
addVertex(-halfWidth, halfHeight, halfDepth, -1, 0, 0, 1, 1); // 22
addVertex(-halfWidth, halfHeight, -halfDepth, -1, 0, 0, 0, 1); // 23
// Body indices
List<int> bodyIndices = [
// Front face
0, 1, 2, 0, 2, 3,
// Back face
4, 5, 6, 4, 6, 7,
// Top face
8, 9, 10, 8, 10, 11,
// Bottom face
12, 13, 14, 12, 14, 15,
// Right face
16, 17, 18, 16, 18, 19,
// Left face
20, 21, 22, 20, 22, 23
];
indices.addAll(bodyIndices);
currentIndex = 24;
// === CONICAL LENS ===
int segments = 16;
double lensApexZ = -halfDepth; // Apex touches the front face (short face)
double lensBaseZ = -halfDepth - lensLength; // Base extends outward along Z-axis
// Lens apex (tip of the cone - touching the camera body at center of front face)
addVertex(0, 0, lensApexZ, 0, 0, -1, 0.5, 0);
int apexIndex = currentIndex;
currentIndex++;
// Lens base circle (the wide part extending outward)
List<int> baseIndices = [];
for (int i = 0; i < segments; i++) {
double theta = i * 2 * pi / segments;
double x = lensRadius * cos(theta);
double y = lensRadius * sin(theta);
// Calculate normal for cone side (pointing outward from cone surface)
double normalX = x / lensRadius; // Normalized radial component
double normalY = y / lensRadius;
double normalZ = lensRadius / lensLength; // Axial component based on cone slope
// Normalize the normal vector
double normalLength = sqrt(normalX * normalX + normalY * normalY + normalZ * normalZ);
normalX /= normalLength;
normalY /= normalLength;
normalZ /= normalLength;
addVertex(x, y, lensBaseZ, normalX, normalY, normalZ, i / segments, 1);
baseIndices.add(currentIndex);
currentIndex++;
}
// Create cone side triangles
for (int i = 0; i < segments; i++) {
int current = baseIndices[i];
int next = baseIndices[(i + 1) % segments];
// Triangle from apex to base edge (counter-clockwise when viewed from outside)
indices.addAll([apexIndex, next, current]);
}
// === LENS BASE (flat circular face at the wide end) ===
// Center of lens base
addVertex(0, 0, lensBaseZ, 0, 0, 1, 0.5, 0.5);
int baseCenterIndex = currentIndex;
currentIndex++;
// Base circle vertices (separate from cone vertices for proper normals)
List<int> baseFaceIndices = [];
for (int i = 0; i < segments; i++) {
double theta = i * 2 * pi / segments;
double x = lensRadius * cos(theta);
double y = lensRadius * sin(theta);
double u = 0.5 + 0.5 * cos(theta);
double v = 0.5 + 0.5 * sin(theta);
addVertex(x, y, lensBaseZ, 0, 0, 1, u, v);
baseFaceIndices.add(currentIndex);
currentIndex++;
}
// Create base face triangles (facing outward from camera)
for (int i = 0; i < segments; i++) {
int current = baseFaceIndices[i];
int next = baseFaceIndices[(i + 1) % segments];
// Triangle from center to edge (counter-clockwise when viewed from outside)
indices.addAll([baseCenterIndex, current, next]);
}
Float32List vertices = Float32List.fromList(verticesList);
Float32List? _normals = normals ? Float32List.fromList(normalsList) : null;
Float32List? _uvs = uvs ? Float32List.fromList(uvsList) : null;
return Geometry(vertices, Uint16List.fromList(indices), normals: _normals, uvs: _uvs);
}
static Geometry wireframeCamera({
double sphereRadius = 0.2,
double frustumDistance = 1.0,
double frustumNear = 0.5,

View File

@@ -394,6 +394,7 @@ class ThermionViewerFFI extends ThermionViewer {
}
if (_skyboxTextureUploadComplete != null) {
await FilamentApp.instance!.flush();
await _skyboxTextureUploadComplete;
_skyboxTextureUploadComplete = null;
}
@@ -414,6 +415,7 @@ class ThermionViewerFFI extends ThermionViewer {
await ibl.destroy();
}
if (_iblTextureUploadComplete != null) {
await FilamentApp.instance!.flush();
await _iblTextureUploadComplete!;
_iblTextureUploadComplete = null;
}
@@ -691,7 +693,7 @@ class ThermionViewerFFI extends ThermionViewer {
///
///
Future setGridOverlayVisibility(bool visible) async {
_grid ??= _grid = await GridOverlay.create(app, animationManager);
_grid ??= await GridOverlay.create(app, animationManager);
if (visible) {
await scene.add(_grid!);

View File

@@ -83,7 +83,8 @@ abstract class ThermionViewer {
Future clearBackgroundImage({bool destroy = false});
///
/// Sets the color for the background plane (positioned at the maximum depth, i.e. behind all other objects including the skybox).
/// Sets the color for the background plane (positioned at the maximum depth,
/// i.e. behind all other objects including the skybox).
///
Future setBackgroundColor(double r, double g, double b, double alpha);

View File

@@ -3,11 +3,15 @@
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern const uint8_t GRID_PACKAGE[];
extern int GRID_GRID_OFFSET;
extern int GRID_GRID_SIZE;
#ifdef __cplusplus
}
#endif
#define GRID_GRID_DATA (GRID_PACKAGE + GRID_GRID_OFFSET)
#endif

View File

@@ -3,11 +3,16 @@
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern const uint8_t OUTLINE_PACKAGE[];
extern int OUTLINE_OUTLINE_OFFSET;
extern int OUTLINE_OUTLINE_SIZE;
#ifdef __cplusplus
}
#endif
#define OUTLINE_OUTLINE_DATA (OUTLINE_PACKAGE + OUTLINE_OUTLINE_OFFSET)
#endif

View File

@@ -37,6 +37,10 @@ public:
MaterialInstance** getMaterialInstances() override { return &_materialInstance; }
size_t getMaterialInstanceCount() override { return 1; }
utils::Entity getEntity() override {
return _gridEntity;
}
void addAllEntities(Scene* scene) override;
void removeAllEntities(Scene* scene) override;

View File

@@ -209,10 +209,7 @@ namespace thermion
SceneAsset *GridOverlay::createInstance(MaterialInstance **materialInstances, size_t materialInstanceCount)
{
auto instance = std::make_unique<GridOverlay>(_engine, _material);
auto *raw = instance.get();
_instances.push_back(std::move(instance));
return reinterpret_cast<SceneAsset *>(raw);
return nullptr;
}
void GridOverlay::addAllEntities(Scene *scene)
@@ -250,7 +247,7 @@ namespace thermion
}
size_t GridOverlay::getChildEntityCount() {
return 1;
return 0;
}
Entity GridOverlay::findEntityByName(const char *name)

View File

@@ -54,7 +54,7 @@ set(EMCC_CFLAGS ${EMCC_CFLAGS} -sSTACK_SIZE=10485760)
# set(EMCC_CFLAGS ${EMCC_CFLAGS} -sLINKABLE=1)
# set(EMCC_CFLAGS ${EMCC_CFLAGS} -sSIDE_MODULE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -std=c++17 -DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -std=c++17 -Wno-invalid-specialization -DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=1")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
if(CMAKE_BUILD_TYPE STREQUAL "Debug")

View File

@@ -1,11 +1,11 @@
name: thermion_dart
description: 3D rendering toolkit for Dart.
version: 0.3.0
version: 0.3.1
homepage: https://thermion.dev
repository: https://github.com/nmfisher/thermion
environment:
sdk: ">=3.5.4 <4.0.0"
sdk: ">=3.6.0-0 <4.0.0"
dependencies:
vector_math: ^2.1.2

View File

@@ -1,3 +1,13 @@
## 0.3.1
- **FIX**: addDestroySwapchain argument to createViewer() (true by default). This is only used on iOS/macOS where a single swapchain is shared between all render targets.
- **DOCS**: fix typo in link.
- **DOCS**: remove code from thermion_flutter README.md and point to docs/repository example instead.
## 0.3.0
- Bump "thermion_flutter" to `0.3.0`.
## 0.3.0
> Note: This release has breaking changes.

View File

@@ -1,96 +1,10 @@
For a more thorough example with both Flutter and pure Dart, see the [examples folder in the repository](https://github.com/nmfisher/thermion/tree/develop/examples).
# Examples
[flutter/quickstart/lib/main.dart](https://github.com/nmfisher/thermion/tree/develop/examples)
See the [Getting Started](https://thermion.dev/getting_started) and [Quickstart](https://thermion.dev/quickstart) sections of the documentation for step-by-step instructions in creating a Flutter app.
You can also jump straight to the [Quickstart example in the repository](https://github.com/nmfisher/thermion/tree/develop/examples/flutter/quickstart/lib/main.dart) for an example of a Flutter app that uses a simple 3D viewer widget.
```dart
import 'package:flutter/material.dart';
import 'package:thermion_flutter/thermion_flutter.dart';
For a more advanced example, see the [Viewer example in the repository](https://github.com/nmfisher/thermion/tree/develop/examples/flutter/viewer/lib/main.dart).
import 'package:vector_math/vector_math_64.dart' as v;
import 'dart:math';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _loaded = false;
ThermionViewer? _thermionViewer;
@override
void initState() {
super.initState();
}
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 {
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: [
if (_thermionViewer != null)
Positioned.fill(child: ThermionWidget(viewer: _thermionViewer!)),
if (!_loaded) _loadButton(),
if (_loaded) _unloadButton(),
]);
}
}
```

View File

@@ -11,8 +11,8 @@ import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_in
class ThermionFlutterPlugin {
ThermionFlutterPlugin._();
static Future<ThermionViewer> createViewer() {
return ThermionFlutterPlatform.instance.createViewer();
static Future<ThermionViewer> createViewer({bool destroySwapchain = true}) {
return ThermionFlutterPlatform.instance.createViewer(destroySwapchain: destroySwapchain);
}
}

View File

@@ -2,8 +2,6 @@ import 'package:thermion_dart/thermion_dart.dart';
import 'package:flutter/material.dart';
import '../../../utils/camera_orientation.dart';
import 'dart:math';
class CameraOptionsWidget extends StatefulWidget {
final Camera camera;
final CameraOrientation cameraOrientation;
@@ -51,7 +49,7 @@ class _CameraOptionsWidgetState extends State<CameraOptionsWidget> {
}
Future _set() async {
await widget.camera.setCameraExposure(
await widget.camera.setExposure(
double.parse(_apertureController.text),
double.parse(_speedController.text),
double.parse(_sensitivityController.text));
@@ -59,9 +57,6 @@ class _CameraOptionsWidgetState extends State<CameraOptionsWidget> {
setState(() {});
}
double _bloom = 0.0;
double _focalLength = 26.0;
@override
Widget build(BuildContext context) {
return Theme(

View File

@@ -112,6 +112,7 @@ class _ViewerWidgetState extends State<ViewerWidget> {
});
}
@override
void didUpdateWidget(ViewerWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.manipulatorType != widget.manipulatorType) {
@@ -140,13 +141,13 @@ class _ViewerWidgetState extends State<ViewerWidget> {
viewport = thermionWidget;
case ManipulatorType.ORBIT:
viewport = ThermionListenerWidget(
key: ObjectKey(ManipulatorType.ORBIT),
key: const ObjectKey(ManipulatorType.ORBIT),
inputHandler: DelegateInputHandler.fixedOrbit(viewer!,
minimumDistance: widget.initialCameraPosition.length),
child: thermionWidget);
case ManipulatorType.FREE_FLIGHT:
viewport = ThermionListenerWidget(
key: ObjectKey(ManipulatorType.FREE_FLIGHT),
key: const ObjectKey(ManipulatorType.FREE_FLIGHT),
inputHandler: DelegateInputHandler.flight(viewer!),
child: thermionWidget);
}
@@ -178,13 +179,13 @@ class _ViewerWidgetState extends State<ViewerWidget> {
await camera.lookAt(widget.initialCameraPosition);
await viewer!.setRendering(true);
if (widget.background != null) {
await viewer!.setBackgroundColor(widget.background!.r,
widget.background!.g, widget.background!.b, widget.background!.a);
}
await viewer!.setRendering(true);
thermionWidget = ThermionWidget(
key: ObjectKey(DateTime.now()),
viewer: viewer!,

View File

@@ -1,6 +1,6 @@
name: thermion_flutter
description: Flutter plugin for 3D rendering with the Thermion toolkit.
version: 0.3.0
version: 0.3.1
homepage: https://thermion.dev
repository: https://github.com/nmfisher/thermion
@@ -17,11 +17,10 @@ dependencies:
plugin_platform_interface: ^2.0.0
ffi: ^2.1.2
animation_tools_dart: ^0.1.0
thermion_dart: ^0.3.0
thermion_flutter_platform_interface: ^0.3.0
thermion_flutter_method_channel:
path: ../thermion_flutter_method_channel
thermion_flutter_web: ^0.3.0
thermion_dart: ^0.3.1
thermion_flutter_platform_interface: ^0.3.1
thermion_flutter_method_channel: ^0.3.1
thermion_flutter_web: ^0.3.1
logging: ^1.2.0
web: ^1.0.0

View File

@@ -1,3 +1,11 @@
## 0.3.1
- **FIX**: addDestroySwapchain argument to createViewer() (true by default). This is only used on iOS/macOS where a single swapchain is shared between all render targets.
## 0.3.0
- Bump "thermion_flutter_method_channel" to `0.3.0`.
## 0.3.0
> Note: This release has breaking changes.

View File

@@ -46,7 +46,7 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform {
return asset.buffer.asUint8List(asset.offsetInBytes);
}
Future<ThermionViewer> createViewer() async {
Future<ThermionViewer> createViewer({bool destroySwapchain = true}) async {
var driverPlatform = await channel.invokeMethod("getDriverPlatform");
var platformPtr = driverPlatform == null
@@ -118,7 +118,13 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform {
// TODO - see if we can use `renderStandaloneView` in FilamentViewer to
// avoid this
if (Platform.isMacOS || Platform.isIOS) {
_swapChain = await FilamentApp.instance!.createHeadlessSwapChain(1, 1, hasStencilBuffer: true);
if(destroySwapchain && _swapChain != null) {
await FilamentApp.instance!.destroySwapChain(_swapChain!);
_swapChain = null;
}
_swapChain ??= await FilamentApp.instance!
.createHeadlessSwapChain(1, 1, hasStencilBuffer: true);
await FilamentApp.instance!.register(_swapChain!, viewer.view);
await viewer.view.setRenderable(true);
}
@@ -179,10 +185,8 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform {
}
_swapChain = await FilamentApp.instance!.createHeadlessSwapChain(
descriptor.width,
descriptor.height,
hasStencilBuffer: true
);
descriptor.width, descriptor.height,
hasStencilBuffer: true);
_logger.info(
"Created headless swapchain ${descriptor.width}x${descriptor.height}",

View File

@@ -1,7 +1,7 @@
name: thermion_flutter_method_channel
description: Desktop + mobile implementation for texture creation + registration with Flutter.
repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter
version: 0.3.0
version: 0.3.1
environment:
sdk: ">=3.3.0 <4.0.0"
@@ -23,8 +23,8 @@ dependencies:
flutter:
sdk: flutter
plugin_platform_interface: ^2.1.0
thermion_flutter_platform_interface: ^0.3.0
thermion_dart: ^0.3.0
thermion_flutter_platform_interface: ^0.3.1
thermion_dart: ^0.3.1
logging: ^1.2.0
dependency_overrides:
thermion_dart:

View File

@@ -1,3 +1,11 @@
## 0.3.1
- **FIX**: addDestroySwapchain argument to createViewer() (true by default). This is only used on iOS/macOS where a single swapchain is shared between all render targets.
## 0.3.0
- Bump "thermion_flutter_platform_interface" to `0.3.0`.
## 0.3.0
- **REFACTOR**: rename ThermionFlutterTexture->PlatformTextureDescriptor.

View File

@@ -65,7 +65,7 @@ abstract class ThermionFlutterPlatform extends PlatformInterface {
///
///
///
Future<ThermionViewer> createViewer() {
Future<ThermionViewer> createViewer({bool destroySwapchain = true}) {
throw UnimplementedError();
}

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.3.0
version: 0.3.1
environment:
sdk: ">=3.3.0 <4.0.0"
@@ -11,7 +11,7 @@ dependencies:
flutter:
sdk: flutter
plugin_platform_interface: ^2.1.0
thermion_dart: ^0.3.0
thermion_dart: ^0.3.1
dev_dependencies:
flutter_test:

View File

@@ -1,3 +1,5 @@
## 0.3.1
## 0.3.0
- **REFACTOR**: rename ThermionFlutterTexture->PlatformTextureDescriptor.

View File

@@ -117,7 +117,7 @@ class ThermionFlutterWebPlugin extends ThermionFlutterPlatform {
///
void resizeCanvas(double width, double height) async {
_logger.info("Resizing canvas to ${width}x${height}");
Thermion_resizeCanvas((window.devicePixelRatio * width).ceil(),
resizeWebCanvas((window.devicePixelRatio * width).ceil(),
(window.devicePixelRatio * height).ceil());
}
}

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.3.0
version: 0.3.1
environment:
sdk: ">=3.3.0 <4.0.0"
@@ -20,10 +20,11 @@ dependencies:
sdk: flutter
plugin_platform_interface: ^2.1.0
web: ^1.0.0
thermion_dart: ^0.3.0
thermion_flutter_platform_interface: ^0.3.0
thermion_dart: ^0.3.1
thermion_flutter_platform_interface: ^0.3.1
flutter_web_plugins:
sdk: flutter
logging: ^1.3.0
dependency_overrides:
thermion_flutter_platform_interface:
path: ../thermion_flutter_platform_interface