Compare commits

...

55 Commits

Author SHA1 Message Date
jingyun
d16474784d fix:3的模型动态修改模型文件 2025-08-25 15:09:07 +08:00
jingyun
1c07d576d3 init 2025-08-22 15:14:09 +08:00
Nick Fisher
961b2ae1ee chore(release): publish packages
- thermion_dart@0.3.3
 - thermion_flutter_method_channel@0.3.3
 - thermion_flutter_platform_interface@0.3.3
 - thermion_flutter_web@0.3.3
 - thermion_flutter@0.3.3
2025-07-24 11:40:35 +08:00
Nick Fisher
a7ac118899 chore(release): publish packages
- thermion_dart@0.3.3-pre
 - thermion_flutter@0.3.3-pre
 - thermion_flutter_method_channel@0.3.3-pre
 - thermion_flutter_platform_interface@0.3.3-pre
 - thermion_flutter_web@0.3.3-pre
2025-07-17 12:13:53 +08:00
Nick Fisher
d92ad4ef12 feat: allow passing renderTargetColorTextureFormat via ThermionFlutterOptions 2025-07-17 12:11:20 +08:00
Nick Fisher
437e91e7bd fix: fix Windows build.dart 2025-07-17 10:54:00 +08:00
Nick Fisher
e169bf6c41 nit: reorder build.dart imports 2025-07-16 18:33:47 +08:00
Nick Fisher
23b060c329 nit: change gltf logging from Log to TRACE 2025-07-16 18:33:47 +08:00
Nick Fisher
5fd0a10630 re-enable EMSCRIPTEN_KEEPALIVE qualifiers for headers. These are not needed for Emscripten builds but the same name is used for Windows __declspec(dllimport), so this is still needed on Windows. We should probably rename this to API_EXPORT or something similar 2025-07-16 18:33:47 +08:00
Nick Fisher
ddfb649733 ci: add windows workflow 2025-07-16 18:15:04 +08:00
Nick Fisher
ecffc5b62a use async loading for camera_manipulation sample 2025-07-16 18:08:21 +08:00
Nick Fisher
429b8eb93b doc: improve addToScene/removeFromScene documentation 2025-07-16 13:00:27 +08:00
Nick Fisher
413faec849 fix: add nan/negative checks inside setLensProjection 2025-07-14 11:50:52 +08:00
Nick Fisher
433b6373a9 docs: replace thermion_flutter README with symlink to thermion_dart README 2025-07-14 10:13:58 +08:00
Nick Fisher
ad2c5afb7f docs: update contributor list 2025-07-14 10:13:38 +08:00
Nick Fisher
a561c847a7 (test) don't load dylib on macos in testing 2025-07-09 10:47:22 +08:00
Nick Fisher
ef7ba24ecc don't fade grid axes depending on camera angle 2025-07-09 10:46:37 +08:00
Nick Fisher
ee176d2684 explicitly list packages in melos.yaml 2025-07-09 10:41:53 +08:00
Nick Fisher
fa168df28f chore(release): publish packages
- thermion_dart@0.3.2
 - thermion_flutter@0.3.2
 - thermion_flutter_method_channel@0.3.2
 - thermion_flutter_web@0.3.2
 - thermion_flutter_platform_interface@0.3.2
2025-07-08 11:48:25 +08:00
Nick Fisher
ba3d016c1a fix: add missing destroySwapchain argument for web 2025-07-08 11:47:47 +08:00
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
108 changed files with 11394 additions and 3321 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
@@ -104,8 +117,28 @@ jobs:
# D:\a\thermion\thermion\thermion_dart\.dart_tool\thermion_dart\log\build.log
/Users/runner/work/thermion/thermion/thermion_dart/.dart_tool/thermion_dart/log/build.log
retention-days: 5
flutter_examples_windows:
name: flutter_examples_windows
runs-on: windows-latest
defaults:
run:
working-directory: examples/flutter
steps:
- uses: actions/checkout@v4
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: master
- run: cd quickstart && flutter pub get && flutter build windows
- run: cd picking && flutter pub get && flutter build windows
- name: Upload logs
if: failure() || steps.build.outcome == 'failure'
uses: actions/upload-artifact@v4
with:
name: build-logs
path: |
D:\a\thermion\thermion\thermion_dart\.dart_tool\thermion_dart\log\build.log
retention-days: 5
# thermion_dart:
# name: thermion_dart
# 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-24
### Changes
@@ -11,95 +11,184 @@ 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.3`](#thermion_dart---v033)
- [`thermion_flutter_method_channel` - `v0.3.3`](#thermion_flutter_method_channel---v033)
- [`thermion_flutter_platform_interface` - `v0.3.3`](#thermion_flutter_platform_interface---v033)
- [`thermion_flutter_web` - `v0.3.3`](#thermion_flutter_web---v033)
- [`thermion_flutter` - `v0.3.3`](#thermion_flutter---v033)
---
#### `thermion_dart` - `v0.3.0`
#### `thermion_dart` - `v0.3.3`
- **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).
- Bump "thermion_dart" to `0.3.3`.
#### `thermion_flutter` - `v0.3.0`
#### `thermion_flutter_method_channel` - `v0.3.3`
- **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.
- Bump "thermion_flutter_method_channel" to `0.3.3`.
#### `thermion_flutter_method_channel` - `v0.3.0`
#### `thermion_flutter_platform_interface` - `v0.3.3`
- **REFACTOR**: rename ThermionFlutterTexture->PlatformTextureDescriptor.
- **BREAKING** **FEAT**: rename thermion_flutter_ffi package to thermion_flutter_method_channel.
- Bump "thermion_flutter_platform_interface" to `0.3.3`.
#### `thermion_flutter_web` - `v0.3.0`
#### `thermion_flutter_web` - `v0.3.3`
- **REFACTOR**: rename ThermionFlutterTexture->PlatformTextureDescriptor.
- Bump "thermion_flutter_web" to `0.3.3`.
#### `thermion_flutter_platform_interface` - `v0.3.0`
#### `thermion_flutter` - `v0.3.3`
- **REFACTOR**: rename ThermionFlutterTexture->PlatformTextureDescriptor.
- **FEAT**: create separate createTexture and createTextureAndBindToView interface methods.
- Bump "thermion_flutter" to `0.3.3`.
## 2025-07-17
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`thermion_dart` - `v0.3.3-pre`](#thermion_dart---v033-pre)
- [`thermion_flutter` - `v0.3.3-pre`](#thermion_flutter---v033-pre)
- [`thermion_flutter_method_channel` - `v0.3.3-pre`](#thermion_flutter_method_channel---v033-pre)
- [`thermion_flutter_platform_interface` - `v0.3.3-pre`](#thermion_flutter_platform_interface---v033-pre)
- [`thermion_flutter_web` - `v0.3.3-pre`](#thermion_flutter_web---v033-pre)
---
#### `thermion_dart` - `v0.3.3-pre`
- **FIX**: fix Windows build.dart.
- **FIX**: add nan/negative checks inside setLensProjection.
#### `thermion_flutter` - `v0.3.3-pre`
- **DOCS**: replace thermion_flutter README with symlink to thermion_dart README.
#### `thermion_flutter_method_channel` - `v0.3.3-pre`
- **FEAT**: allow passing renderTargetColorTextureFormat via ThermionFlutterOptions.
#### `thermion_flutter_platform_interface` - `v0.3.3-pre`
- **FEAT**: allow passing renderTargetColorTextureFormat via ThermionFlutterOptions.
#### `thermion_flutter_web` - `v0.3.3-pre`
- Bump "thermion_flutter_web" to `0.3.3-pre`.
## 2025-07-08
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`thermion_dart` - `v0.3.2`](#thermion_dart---v032)
- [`thermion_flutter` - `v0.3.2`](#thermion_flutter---v032)
- [`thermion_flutter_method_channel` - `v0.3.2`](#thermion_flutter_method_channel---v032)
- [`thermion_flutter_web` - `v0.3.2`](#thermion_flutter_web---v032)
- [`thermion_flutter_platform_interface` - `v0.3.2`](#thermion_flutter_platform_interface---v032)
---
#### `thermion_dart` - `v0.3.2`
- Bump "thermion_dart" to `0.3.2`.
#### `thermion_flutter` - `v0.3.2`
- Bump "thermion_flutter" to `0.3.2`.
#### `thermion_flutter_method_channel` - `v0.3.2`
- Bump "thermion_flutter_method_channel" to `0.3.2`.
#### `thermion_flutter_web` - `v0.3.2`
- **FIX**: add missing destroySwapchain argument for web.
#### `thermion_flutter_platform_interface` - `v0.3.2`
- Bump "thermion_flutter_platform_interface" to `0.3.2`.
## 2025-07-08
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`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.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.
#### `thermion_flutter` - `v0.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.
#### `thermion_flutter_method_channel` - `v0.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.
#### `thermion_flutter_web` - `v0.3.1`
#### `thermion_flutter_platform_interface` - `v0.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.
# 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
### Changes

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

@@ -79,4 +79,4 @@ Thank you to the following people:
- @LukasPoque for CI/refactoring work
- @alexmercerind for his work on integrating ANGLE textures on Flutter Windows
- @BrutalCoding for documentation fixes
- @chenriji for testing and bug fixes

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,20 @@
# 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:
Follow the steps listed in [Getting Started](./getting_started) to configure your Flutter installation and project.
```yaml
dependencies:
thermion_flutter: ^latest_version
```
If you're running Windows, delete the `examples/flutter/quickstart/assets` symlink and copy the `assets` folder from `examples/assets` to `examples/flutter/quickstart/assets`.
## 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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 960 KiB

View File

@@ -0,0 +1 @@
{"asset":{"version":"2.0","generator":"babylon.js glTF exporter for 3dsmax 2021 v20210607.3"},"scene":0,"scenes":[{"nodes":[0]}],"nodes":[{"mesh":0,"translation":[0.0,-1.14751673,-5.015955E-08],"name":"Cylinder001"}],"meshes":[{"primitives":[{"attributes":{"POSITION":1,"NORMAL":2,"TEXCOORD_0":3},"indices":0,"material":0},{"attributes":{"POSITION":5,"NORMAL":6,"TEXCOORD_0":7},"indices":4,"material":1},{"attributes":{"POSITION":9,"NORMAL":10,"TEXCOORD_0":11},"indices":8,"material":2},{"attributes":{"POSITION":13,"NORMAL":14,"TEXCOORD_0":15},"indices":12,"material":3},{"attributes":{"POSITION":17,"NORMAL":18,"TEXCOORD_0":19},"indices":16,"material":4}],"name":"Cylinder001"}],"accessors":[{"bufferView":0,"componentType":5123,"count":858,"type":"SCALAR","name":"accessorIndices"},{"bufferView":1,"componentType":5126,"count":858,"max":[0.9673312,-0.0665806457,0.9673312],"min":[-0.9673312,-0.09678768,-0.9673312],"type":"VEC3","name":"accessorPositions"},{"bufferView":1,"byteOffset":10296,"componentType":5126,"count":858,"type":"VEC3","name":"accessorNormals"},{"bufferView":2,"componentType":5126,"count":858,"type":"VEC2","name":"accessorUVs"},{"bufferView":0,"byteOffset":1716,"componentType":5123,"count":14592,"type":"SCALAR","name":"accessorIndices"},{"bufferView":1,"byteOffset":20592,"componentType":5126,"count":14592,"max":[1.01214123,2.33892131,1.02725148],"min":[-1.01214123,-0.09678768,-1.01214123],"type":"VEC3","name":"accessorPositions"},{"bufferView":1,"byteOffset":195696,"componentType":5126,"count":14592,"type":"VEC3","name":"accessorNormals"},{"bufferView":2,"byteOffset":6864,"componentType":5126,"count":14592,"type":"VEC2","name":"accessorUVs"},{"bufferView":0,"byteOffset":30900,"componentType":5123,"count":1620,"type":"SCALAR","name":"accessorIndices"},{"bufferView":1,"byteOffset":370800,"componentType":5126,"count":1620,"max":[1.006612,2.3633275,1.006612],"min":[-1.006612,2.338921,-1.006612],"type":"VEC3","name":"accessorPositions"},{"bufferView":1,"byteOffset":390240,"componentType":5126,"count":1620,"type":"VEC3","name":"accessorNormals"},{"bufferView":2,"byteOffset":123600,"componentType":5126,"count":1620,"type":"VEC2","name":"accessorUVs"},{"bufferView":0,"byteOffset":34140,"componentType":5123,"count":318,"type":"SCALAR","name":"accessorIndices"},{"bufferView":1,"byteOffset":409680,"componentType":5126,"count":318,"max":[0.9454922,2.3548696,0.9454922],"min":[-0.9454921,0.0617354736,-0.945492268],"type":"VEC3","name":"accessorPositions"},{"bufferView":1,"byteOffset":413496,"componentType":5126,"count":318,"type":"VEC3","name":"accessorNormals"},{"bufferView":2,"byteOffset":136560,"componentType":5126,"count":318,"type":"VEC2","name":"accessorUVs"},{"bufferView":0,"byteOffset":34776,"componentType":5123,"count":11520,"type":"SCALAR","name":"accessorIndices"},{"bufferView":1,"byteOffset":417312,"componentType":5126,"count":11520,"max":[0.196413681,2.12139344,1.90955186],"min":[-0.196414649,0.165008187,1.00927675],"type":"VEC3","name":"accessorPositions"},{"bufferView":1,"byteOffset":555552,"componentType":5126,"count":11520,"type":"VEC3","name":"accessorNormals"},{"bufferView":2,"byteOffset":139104,"componentType":5126,"count":11520,"type":"VEC2","name":"accessorUVs"}],"bufferViews":[{"buffer":0,"byteLength":57816,"name":"bufferViewScalar"},{"buffer":0,"byteOffset":57816,"byteLength":693792,"byteStride":12,"name":"bufferViewFloatVec3"},{"buffer":0,"byteOffset":751608,"byteLength":231264,"byteStride":8,"name":"bufferViewFloatVec2"}],"buffers":[{"uri":"model.bin","byteLength":982872}],"materials":[{"pbrMetallicRoughness":{"metallicFactor":0.0,"roughnessFactor":0.9},"doubleSided":true,"name":"01 - Default"},{"pbrMetallicRoughness":{"baseColorTexture":{"index":0},"metallicFactor":0.0,"roughnessFactor":0.9},"doubleSided":true,"name":"02 - Default"},{"pbrMetallicRoughness":{"baseColorFactor":[0.0,1.0,0.117647067,1.0],"metallicFactor":0.0,"roughnessFactor":0.9},"doubleSided":true,"name":"03 - Default"},{"pbrMetallicRoughness":{"baseColorFactor":[0.2784314,0.0,0.9921569,1.0],"metallicFactor":0.0,"roughnessFactor":0.9},"doubleSided":true,"name":"04 - Default"},{"pbrMetallicRoughness":{"baseColorFactor":[0.9725491,0.776470661,0.0,1.0],"metallicFactor":0.0,"roughnessFactor":0.9},"doubleSided":true,"name":"05 - Default"}],"textures":[{"sampler":0,"source":0,"name":"texture.jpg"}],"images":[{"uri":"./texture.jpg"}],"samplers":[{"magFilter":9729,"minFilter":9987}]}

BIN
examples/assets/cup/new.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 720 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 MiB

Binary file not shown.

View File

@@ -0,0 +1,270 @@
7767517
268 473
Input input.1 0 1 data
Convolution Conv_0 1 1 data 193 0=64 1=3 4=1 5=1 6=1728
Split splitncnn_0 1 8 193 193_splitncnn_0 193_splitncnn_1 193_splitncnn_2 193_splitncnn_3 193_splitncnn_4 193_splitncnn_5 193_splitncnn_6 193_splitncnn_7
Convolution Conv_1 1 1 193_splitncnn_7 195 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_1 1 4 195 195_splitncnn_0 195_splitncnn_1 195_splitncnn_2 195_splitncnn_3
Concat Concat_3 2 1 193_splitncnn_6 195_splitncnn_3 196
Convolution Conv_4 1 1 196 198 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_2 1 3 198 198_splitncnn_0 198_splitncnn_1 198_splitncnn_2
Concat Concat_6 3 1 193_splitncnn_5 195_splitncnn_2 198_splitncnn_2 199
Convolution Conv_7 1 1 199 201 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_3 1 2 201 201_splitncnn_0 201_splitncnn_1
Concat Concat_9 4 1 193_splitncnn_4 195_splitncnn_1 198_splitncnn_1 201_splitncnn_1 202
Convolution Conv_10 1 1 202 204 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_12 5 1 193_splitncnn_3 195_splitncnn_0 198_splitncnn_0 201_splitncnn_0 204 205
Convolution Conv_13 1 1 205 206 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_16 2 1 206 193_splitncnn_2 209 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_4 1 6 209 209_splitncnn_0 209_splitncnn_1 209_splitncnn_2 209_splitncnn_3 209_splitncnn_4 209_splitncnn_5
Convolution Conv_17 1 1 209_splitncnn_5 211 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_5 1 4 211 211_splitncnn_0 211_splitncnn_1 211_splitncnn_2 211_splitncnn_3
Concat Concat_19 2 1 209_splitncnn_4 211_splitncnn_3 212
Convolution Conv_20 1 1 212 214 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_6 1 3 214 214_splitncnn_0 214_splitncnn_1 214_splitncnn_2
Concat Concat_22 3 1 209_splitncnn_3 211_splitncnn_2 214_splitncnn_2 215
Convolution Conv_23 1 1 215 217 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_7 1 2 217 217_splitncnn_0 217_splitncnn_1
Concat Concat_25 4 1 209_splitncnn_2 211_splitncnn_1 214_splitncnn_1 217_splitncnn_1 218
Convolution Conv_26 1 1 218 220 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_28 5 1 209_splitncnn_1 211_splitncnn_0 214_splitncnn_0 217_splitncnn_0 220 221
Convolution Conv_29 1 1 221 222 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_32 2 1 222 209_splitncnn_0 225 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_8 1 6 225 225_splitncnn_0 225_splitncnn_1 225_splitncnn_2 225_splitncnn_3 225_splitncnn_4 225_splitncnn_5
Convolution Conv_33 1 1 225_splitncnn_5 227 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_9 1 4 227 227_splitncnn_0 227_splitncnn_1 227_splitncnn_2 227_splitncnn_3
Concat Concat_35 2 1 225_splitncnn_4 227_splitncnn_3 228
Convolution Conv_36 1 1 228 230 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_10 1 3 230 230_splitncnn_0 230_splitncnn_1 230_splitncnn_2
Concat Concat_38 3 1 225_splitncnn_3 227_splitncnn_2 230_splitncnn_2 231
Convolution Conv_39 1 1 231 233 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_11 1 2 233 233_splitncnn_0 233_splitncnn_1
Concat Concat_41 4 1 225_splitncnn_2 227_splitncnn_1 230_splitncnn_1 233_splitncnn_1 234
Convolution Conv_42 1 1 234 236 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_44 5 1 225_splitncnn_1 227_splitncnn_0 230_splitncnn_0 233_splitncnn_0 236 237
Convolution Conv_45 1 1 237 238 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_48 2 1 238 225_splitncnn_0 241 0=1 -23301=2,2.000000e-01,1.000000e+00
Eltwise Add_51 2 1 241 193_splitncnn_1 244 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_12 1 7 244 244_splitncnn_0 244_splitncnn_1 244_splitncnn_2 244_splitncnn_3 244_splitncnn_4 244_splitncnn_5 244_splitncnn_6
Convolution Conv_52 1 1 244_splitncnn_6 246 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_13 1 4 246 246_splitncnn_0 246_splitncnn_1 246_splitncnn_2 246_splitncnn_3
Concat Concat_54 2 1 244_splitncnn_5 246_splitncnn_3 247
Convolution Conv_55 1 1 247 249 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_14 1 3 249 249_splitncnn_0 249_splitncnn_1 249_splitncnn_2
Concat Concat_57 3 1 244_splitncnn_4 246_splitncnn_2 249_splitncnn_2 250
Convolution Conv_58 1 1 250 252 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_15 1 2 252 252_splitncnn_0 252_splitncnn_1
Concat Concat_60 4 1 244_splitncnn_3 246_splitncnn_1 249_splitncnn_1 252_splitncnn_1 253
Convolution Conv_61 1 1 253 255 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_63 5 1 244_splitncnn_2 246_splitncnn_0 249_splitncnn_0 252_splitncnn_0 255 256
Convolution Conv_64 1 1 256 257 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_67 2 1 257 244_splitncnn_1 260 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_16 1 6 260 260_splitncnn_0 260_splitncnn_1 260_splitncnn_2 260_splitncnn_3 260_splitncnn_4 260_splitncnn_5
Convolution Conv_68 1 1 260_splitncnn_5 262 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_17 1 4 262 262_splitncnn_0 262_splitncnn_1 262_splitncnn_2 262_splitncnn_3
Concat Concat_70 2 1 260_splitncnn_4 262_splitncnn_3 263
Convolution Conv_71 1 1 263 265 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_18 1 3 265 265_splitncnn_0 265_splitncnn_1 265_splitncnn_2
Concat Concat_73 3 1 260_splitncnn_3 262_splitncnn_2 265_splitncnn_2 266
Convolution Conv_74 1 1 266 268 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_19 1 2 268 268_splitncnn_0 268_splitncnn_1
Concat Concat_76 4 1 260_splitncnn_2 262_splitncnn_1 265_splitncnn_1 268_splitncnn_1 269
Convolution Conv_77 1 1 269 271 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_79 5 1 260_splitncnn_1 262_splitncnn_0 265_splitncnn_0 268_splitncnn_0 271 272
Convolution Conv_80 1 1 272 273 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_83 2 1 273 260_splitncnn_0 276 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_20 1 6 276 276_splitncnn_0 276_splitncnn_1 276_splitncnn_2 276_splitncnn_3 276_splitncnn_4 276_splitncnn_5
Convolution Conv_84 1 1 276_splitncnn_5 278 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_21 1 4 278 278_splitncnn_0 278_splitncnn_1 278_splitncnn_2 278_splitncnn_3
Concat Concat_86 2 1 276_splitncnn_4 278_splitncnn_3 279
Convolution Conv_87 1 1 279 281 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_22 1 3 281 281_splitncnn_0 281_splitncnn_1 281_splitncnn_2
Concat Concat_89 3 1 276_splitncnn_3 278_splitncnn_2 281_splitncnn_2 282
Convolution Conv_90 1 1 282 284 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_23 1 2 284 284_splitncnn_0 284_splitncnn_1
Concat Concat_92 4 1 276_splitncnn_2 278_splitncnn_1 281_splitncnn_1 284_splitncnn_1 285
Convolution Conv_93 1 1 285 287 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_95 5 1 276_splitncnn_1 278_splitncnn_0 281_splitncnn_0 284_splitncnn_0 287 288
Convolution Conv_96 1 1 288 289 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_99 2 1 289 276_splitncnn_0 292 0=1 -23301=2,2.000000e-01,1.000000e+00
Eltwise Add_102 2 1 292 244_splitncnn_0 295 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_24 1 7 295 295_splitncnn_0 295_splitncnn_1 295_splitncnn_2 295_splitncnn_3 295_splitncnn_4 295_splitncnn_5 295_splitncnn_6
Convolution Conv_103 1 1 295_splitncnn_6 297 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_25 1 4 297 297_splitncnn_0 297_splitncnn_1 297_splitncnn_2 297_splitncnn_3
Concat Concat_105 2 1 295_splitncnn_5 297_splitncnn_3 298
Convolution Conv_106 1 1 298 300 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_26 1 3 300 300_splitncnn_0 300_splitncnn_1 300_splitncnn_2
Concat Concat_108 3 1 295_splitncnn_4 297_splitncnn_2 300_splitncnn_2 301
Convolution Conv_109 1 1 301 303 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_27 1 2 303 303_splitncnn_0 303_splitncnn_1
Concat Concat_111 4 1 295_splitncnn_3 297_splitncnn_1 300_splitncnn_1 303_splitncnn_1 304
Convolution Conv_112 1 1 304 306 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_114 5 1 295_splitncnn_2 297_splitncnn_0 300_splitncnn_0 303_splitncnn_0 306 307
Convolution Conv_115 1 1 307 308 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_118 2 1 308 295_splitncnn_1 311 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_28 1 6 311 311_splitncnn_0 311_splitncnn_1 311_splitncnn_2 311_splitncnn_3 311_splitncnn_4 311_splitncnn_5
Convolution Conv_119 1 1 311_splitncnn_5 313 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_29 1 4 313 313_splitncnn_0 313_splitncnn_1 313_splitncnn_2 313_splitncnn_3
Concat Concat_121 2 1 311_splitncnn_4 313_splitncnn_3 314
Convolution Conv_122 1 1 314 316 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_30 1 3 316 316_splitncnn_0 316_splitncnn_1 316_splitncnn_2
Concat Concat_124 3 1 311_splitncnn_3 313_splitncnn_2 316_splitncnn_2 317
Convolution Conv_125 1 1 317 319 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_31 1 2 319 319_splitncnn_0 319_splitncnn_1
Concat Concat_127 4 1 311_splitncnn_2 313_splitncnn_1 316_splitncnn_1 319_splitncnn_1 320
Convolution Conv_128 1 1 320 322 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_130 5 1 311_splitncnn_1 313_splitncnn_0 316_splitncnn_0 319_splitncnn_0 322 323
Convolution Conv_131 1 1 323 324 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_134 2 1 324 311_splitncnn_0 327 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_32 1 6 327 327_splitncnn_0 327_splitncnn_1 327_splitncnn_2 327_splitncnn_3 327_splitncnn_4 327_splitncnn_5
Convolution Conv_135 1 1 327_splitncnn_5 329 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_33 1 4 329 329_splitncnn_0 329_splitncnn_1 329_splitncnn_2 329_splitncnn_3
Concat Concat_137 2 1 327_splitncnn_4 329_splitncnn_3 330
Convolution Conv_138 1 1 330 332 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_34 1 3 332 332_splitncnn_0 332_splitncnn_1 332_splitncnn_2
Concat Concat_140 3 1 327_splitncnn_3 329_splitncnn_2 332_splitncnn_2 333
Convolution Conv_141 1 1 333 335 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_35 1 2 335 335_splitncnn_0 335_splitncnn_1
Concat Concat_143 4 1 327_splitncnn_2 329_splitncnn_1 332_splitncnn_1 335_splitncnn_1 336
Convolution Conv_144 1 1 336 338 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_146 5 1 327_splitncnn_1 329_splitncnn_0 332_splitncnn_0 335_splitncnn_0 338 339
Convolution Conv_147 1 1 339 340 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_150 2 1 340 327_splitncnn_0 343 0=1 -23301=2,2.000000e-01,1.000000e+00
Eltwise Add_153 2 1 343 295_splitncnn_0 346 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_36 1 7 346 346_splitncnn_0 346_splitncnn_1 346_splitncnn_2 346_splitncnn_3 346_splitncnn_4 346_splitncnn_5 346_splitncnn_6
Convolution Conv_154 1 1 346_splitncnn_6 348 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_37 1 4 348 348_splitncnn_0 348_splitncnn_1 348_splitncnn_2 348_splitncnn_3
Concat Concat_156 2 1 346_splitncnn_5 348_splitncnn_3 349
Convolution Conv_157 1 1 349 351 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_38 1 3 351 351_splitncnn_0 351_splitncnn_1 351_splitncnn_2
Concat Concat_159 3 1 346_splitncnn_4 348_splitncnn_2 351_splitncnn_2 352
Convolution Conv_160 1 1 352 354 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_39 1 2 354 354_splitncnn_0 354_splitncnn_1
Concat Concat_162 4 1 346_splitncnn_3 348_splitncnn_1 351_splitncnn_1 354_splitncnn_1 355
Convolution Conv_163 1 1 355 357 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_165 5 1 346_splitncnn_2 348_splitncnn_0 351_splitncnn_0 354_splitncnn_0 357 358
Convolution Conv_166 1 1 358 359 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_169 2 1 359 346_splitncnn_1 362 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_40 1 6 362 362_splitncnn_0 362_splitncnn_1 362_splitncnn_2 362_splitncnn_3 362_splitncnn_4 362_splitncnn_5
Convolution Conv_170 1 1 362_splitncnn_5 364 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_41 1 4 364 364_splitncnn_0 364_splitncnn_1 364_splitncnn_2 364_splitncnn_3
Concat Concat_172 2 1 362_splitncnn_4 364_splitncnn_3 365
Convolution Conv_173 1 1 365 367 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_42 1 3 367 367_splitncnn_0 367_splitncnn_1 367_splitncnn_2
Concat Concat_175 3 1 362_splitncnn_3 364_splitncnn_2 367_splitncnn_2 368
Convolution Conv_176 1 1 368 370 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_43 1 2 370 370_splitncnn_0 370_splitncnn_1
Concat Concat_178 4 1 362_splitncnn_2 364_splitncnn_1 367_splitncnn_1 370_splitncnn_1 371
Convolution Conv_179 1 1 371 373 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_181 5 1 362_splitncnn_1 364_splitncnn_0 367_splitncnn_0 370_splitncnn_0 373 374
Convolution Conv_182 1 1 374 375 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_185 2 1 375 362_splitncnn_0 378 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_44 1 6 378 378_splitncnn_0 378_splitncnn_1 378_splitncnn_2 378_splitncnn_3 378_splitncnn_4 378_splitncnn_5
Convolution Conv_186 1 1 378_splitncnn_5 380 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_45 1 4 380 380_splitncnn_0 380_splitncnn_1 380_splitncnn_2 380_splitncnn_3
Concat Concat_188 2 1 378_splitncnn_4 380_splitncnn_3 381
Convolution Conv_189 1 1 381 383 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_46 1 3 383 383_splitncnn_0 383_splitncnn_1 383_splitncnn_2
Concat Concat_191 3 1 378_splitncnn_3 380_splitncnn_2 383_splitncnn_2 384
Convolution Conv_192 1 1 384 386 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_47 1 2 386 386_splitncnn_0 386_splitncnn_1
Concat Concat_194 4 1 378_splitncnn_2 380_splitncnn_1 383_splitncnn_1 386_splitncnn_1 387
Convolution Conv_195 1 1 387 389 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_197 5 1 378_splitncnn_1 380_splitncnn_0 383_splitncnn_0 386_splitncnn_0 389 390
Convolution Conv_198 1 1 390 391 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_201 2 1 391 378_splitncnn_0 394 0=1 -23301=2,2.000000e-01,1.000000e+00
Eltwise Add_204 2 1 394 346_splitncnn_0 397 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_48 1 7 397 397_splitncnn_0 397_splitncnn_1 397_splitncnn_2 397_splitncnn_3 397_splitncnn_4 397_splitncnn_5 397_splitncnn_6
Convolution Conv_205 1 1 397_splitncnn_6 399 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_49 1 4 399 399_splitncnn_0 399_splitncnn_1 399_splitncnn_2 399_splitncnn_3
Concat Concat_207 2 1 397_splitncnn_5 399_splitncnn_3 400
Convolution Conv_208 1 1 400 402 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_50 1 3 402 402_splitncnn_0 402_splitncnn_1 402_splitncnn_2
Concat Concat_210 3 1 397_splitncnn_4 399_splitncnn_2 402_splitncnn_2 403
Convolution Conv_211 1 1 403 405 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_51 1 2 405 405_splitncnn_0 405_splitncnn_1
Concat Concat_213 4 1 397_splitncnn_3 399_splitncnn_1 402_splitncnn_1 405_splitncnn_1 406
Convolution Conv_214 1 1 406 408 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_216 5 1 397_splitncnn_2 399_splitncnn_0 402_splitncnn_0 405_splitncnn_0 408 409
Convolution Conv_217 1 1 409 410 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_220 2 1 410 397_splitncnn_1 413 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_52 1 6 413 413_splitncnn_0 413_splitncnn_1 413_splitncnn_2 413_splitncnn_3 413_splitncnn_4 413_splitncnn_5
Convolution Conv_221 1 1 413_splitncnn_5 415 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_53 1 4 415 415_splitncnn_0 415_splitncnn_1 415_splitncnn_2 415_splitncnn_3
Concat Concat_223 2 1 413_splitncnn_4 415_splitncnn_3 416
Convolution Conv_224 1 1 416 418 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_54 1 3 418 418_splitncnn_0 418_splitncnn_1 418_splitncnn_2
Concat Concat_226 3 1 413_splitncnn_3 415_splitncnn_2 418_splitncnn_2 419
Convolution Conv_227 1 1 419 421 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_55 1 2 421 421_splitncnn_0 421_splitncnn_1
Concat Concat_229 4 1 413_splitncnn_2 415_splitncnn_1 418_splitncnn_1 421_splitncnn_1 422
Convolution Conv_230 1 1 422 424 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_232 5 1 413_splitncnn_1 415_splitncnn_0 418_splitncnn_0 421_splitncnn_0 424 425
Convolution Conv_233 1 1 425 426 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_236 2 1 426 413_splitncnn_0 429 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_56 1 6 429 429_splitncnn_0 429_splitncnn_1 429_splitncnn_2 429_splitncnn_3 429_splitncnn_4 429_splitncnn_5
Convolution Conv_237 1 1 429_splitncnn_5 431 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_57 1 4 431 431_splitncnn_0 431_splitncnn_1 431_splitncnn_2 431_splitncnn_3
Concat Concat_239 2 1 429_splitncnn_4 431_splitncnn_3 432
Convolution Conv_240 1 1 432 434 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_58 1 3 434 434_splitncnn_0 434_splitncnn_1 434_splitncnn_2
Concat Concat_242 3 1 429_splitncnn_3 431_splitncnn_2 434_splitncnn_2 435
Convolution Conv_243 1 1 435 437 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_59 1 2 437 437_splitncnn_0 437_splitncnn_1
Concat Concat_245 4 1 429_splitncnn_2 431_splitncnn_1 434_splitncnn_1 437_splitncnn_1 438
Convolution Conv_246 1 1 438 440 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_248 5 1 429_splitncnn_1 431_splitncnn_0 434_splitncnn_0 437_splitncnn_0 440 441
Convolution Conv_249 1 1 441 442 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_252 2 1 442 429_splitncnn_0 445 0=1 -23301=2,2.000000e-01,1.000000e+00
Eltwise Add_255 2 1 445 397_splitncnn_0 448 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_60 1 7 448 448_splitncnn_0 448_splitncnn_1 448_splitncnn_2 448_splitncnn_3 448_splitncnn_4 448_splitncnn_5 448_splitncnn_6
Convolution Conv_256 1 1 448_splitncnn_6 450 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_61 1 4 450 450_splitncnn_0 450_splitncnn_1 450_splitncnn_2 450_splitncnn_3
Concat Concat_258 2 1 448_splitncnn_5 450_splitncnn_3 451
Convolution Conv_259 1 1 451 453 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_62 1 3 453 453_splitncnn_0 453_splitncnn_1 453_splitncnn_2
Concat Concat_261 3 1 448_splitncnn_4 450_splitncnn_2 453_splitncnn_2 454
Convolution Conv_262 1 1 454 456 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_63 1 2 456 456_splitncnn_0 456_splitncnn_1
Concat Concat_264 4 1 448_splitncnn_3 450_splitncnn_1 453_splitncnn_1 456_splitncnn_1 457
Convolution Conv_265 1 1 457 459 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_267 5 1 448_splitncnn_2 450_splitncnn_0 453_splitncnn_0 456_splitncnn_0 459 460
Convolution Conv_268 1 1 460 461 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_271 2 1 461 448_splitncnn_1 464 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_64 1 6 464 464_splitncnn_0 464_splitncnn_1 464_splitncnn_2 464_splitncnn_3 464_splitncnn_4 464_splitncnn_5
Convolution Conv_272 1 1 464_splitncnn_5 466 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_65 1 4 466 466_splitncnn_0 466_splitncnn_1 466_splitncnn_2 466_splitncnn_3
Concat Concat_274 2 1 464_splitncnn_4 466_splitncnn_3 467
Convolution Conv_275 1 1 467 469 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_66 1 3 469 469_splitncnn_0 469_splitncnn_1 469_splitncnn_2
Concat Concat_277 3 1 464_splitncnn_3 466_splitncnn_2 469_splitncnn_2 470
Convolution Conv_278 1 1 470 472 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_67 1 2 472 472_splitncnn_0 472_splitncnn_1
Concat Concat_280 4 1 464_splitncnn_2 466_splitncnn_1 469_splitncnn_1 472_splitncnn_1 473
Convolution Conv_281 1 1 473 475 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_283 5 1 464_splitncnn_1 466_splitncnn_0 469_splitncnn_0 472_splitncnn_0 475 476
Convolution Conv_284 1 1 476 477 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_287 2 1 477 464_splitncnn_0 480 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_68 1 6 480 480_splitncnn_0 480_splitncnn_1 480_splitncnn_2 480_splitncnn_3 480_splitncnn_4 480_splitncnn_5
Convolution Conv_288 1 1 480_splitncnn_5 482 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_69 1 4 482 482_splitncnn_0 482_splitncnn_1 482_splitncnn_2 482_splitncnn_3
Concat Concat_290 2 1 480_splitncnn_4 482_splitncnn_3 483
Convolution Conv_291 1 1 483 485 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_70 1 3 485 485_splitncnn_0 485_splitncnn_1 485_splitncnn_2
Concat Concat_293 3 1 480_splitncnn_3 482_splitncnn_2 485_splitncnn_2 486
Convolution Conv_294 1 1 486 488 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_71 1 2 488 488_splitncnn_0 488_splitncnn_1
Concat Concat_296 4 1 480_splitncnn_2 482_splitncnn_1 485_splitncnn_1 488_splitncnn_1 489
Convolution Conv_297 1 1 489 491 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_299 5 1 480_splitncnn_1 482_splitncnn_0 485_splitncnn_0 488_splitncnn_0 491 492
Convolution Conv_300 1 1 492 493 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_303 2 1 493 480_splitncnn_0 496 0=1 -23301=2,2.000000e-01,1.000000e+00
Eltwise Add_306 2 1 496 448_splitncnn_0 499 0=1 -23301=2,2.000000e-01,1.000000e+00
Convolution Conv_307 1 1 499 500 0=64 1=3 4=1 5=1 6=36864
BinaryOp Add_308 2 1 193_splitncnn_0 500 501
Interp Resize_310 1 1 501 506 0=1 1=2.000000e+00 2=2.000000e+00
Convolution Conv_311 1 1 506 508 0=64 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Interp Resize_314 1 1 508 513 0=1 1=2.000000e+00 2=2.000000e+00
Convolution Conv_315 1 1 513 515 0=64 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Convolution Conv_317 1 1 515 517 0=64 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Convolution Conv_319 1 1 517 output 0=3 1=3 4=1 5=1 6=1728

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 960 KiB

View File

@@ -0,0 +1 @@
{"asset":{"version":"2.0","generator":"babylon.js glTF exporter for 3dsmax 2021 v20210607.3"},"scene":0,"scenes":[{"nodes":[0]}],"nodes":[{"mesh":0,"translation":[0.0,-1.14751673,-5.015955E-08],"name":"Cylinder001"}],"meshes":[{"primitives":[{"attributes":{"POSITION":1,"NORMAL":2,"TEXCOORD_0":3},"indices":0,"material":0},{"attributes":{"POSITION":5,"NORMAL":6,"TEXCOORD_0":7},"indices":4,"material":1},{"attributes":{"POSITION":9,"NORMAL":10,"TEXCOORD_0":11},"indices":8,"material":2},{"attributes":{"POSITION":13,"NORMAL":14,"TEXCOORD_0":15},"indices":12,"material":3},{"attributes":{"POSITION":17,"NORMAL":18,"TEXCOORD_0":19},"indices":16,"material":4}],"name":"Cylinder001"}],"accessors":[{"bufferView":0,"componentType":5123,"count":858,"type":"SCALAR","name":"accessorIndices"},{"bufferView":1,"componentType":5126,"count":858,"max":[0.9673312,-0.0665806457,0.9673312],"min":[-0.9673312,-0.09678768,-0.9673312],"type":"VEC3","name":"accessorPositions"},{"bufferView":1,"byteOffset":10296,"componentType":5126,"count":858,"type":"VEC3","name":"accessorNormals"},{"bufferView":2,"componentType":5126,"count":858,"type":"VEC2","name":"accessorUVs"},{"bufferView":0,"byteOffset":1716,"componentType":5123,"count":14592,"type":"SCALAR","name":"accessorIndices"},{"bufferView":1,"byteOffset":20592,"componentType":5126,"count":14592,"max":[1.01214123,2.33892131,1.02725148],"min":[-1.01214123,-0.09678768,-1.01214123],"type":"VEC3","name":"accessorPositions"},{"bufferView":1,"byteOffset":195696,"componentType":5126,"count":14592,"type":"VEC3","name":"accessorNormals"},{"bufferView":2,"byteOffset":6864,"componentType":5126,"count":14592,"type":"VEC2","name":"accessorUVs"},{"bufferView":0,"byteOffset":30900,"componentType":5123,"count":1620,"type":"SCALAR","name":"accessorIndices"},{"bufferView":1,"byteOffset":370800,"componentType":5126,"count":1620,"max":[1.006612,2.3633275,1.006612],"min":[-1.006612,2.338921,-1.006612],"type":"VEC3","name":"accessorPositions"},{"bufferView":1,"byteOffset":390240,"componentType":5126,"count":1620,"type":"VEC3","name":"accessorNormals"},{"bufferView":2,"byteOffset":123600,"componentType":5126,"count":1620,"type":"VEC2","name":"accessorUVs"},{"bufferView":0,"byteOffset":34140,"componentType":5123,"count":318,"type":"SCALAR","name":"accessorIndices"},{"bufferView":1,"byteOffset":409680,"componentType":5126,"count":318,"max":[0.9454922,2.3548696,0.9454922],"min":[-0.9454921,0.0617354736,-0.945492268],"type":"VEC3","name":"accessorPositions"},{"bufferView":1,"byteOffset":413496,"componentType":5126,"count":318,"type":"VEC3","name":"accessorNormals"},{"bufferView":2,"byteOffset":136560,"componentType":5126,"count":318,"type":"VEC2","name":"accessorUVs"},{"bufferView":0,"byteOffset":34776,"componentType":5123,"count":11520,"type":"SCALAR","name":"accessorIndices"},{"bufferView":1,"byteOffset":417312,"componentType":5126,"count":11520,"max":[0.196413681,2.12139344,1.90955186],"min":[-0.196414649,0.165008187,1.00927675],"type":"VEC3","name":"accessorPositions"},{"bufferView":1,"byteOffset":555552,"componentType":5126,"count":11520,"type":"VEC3","name":"accessorNormals"},{"bufferView":2,"byteOffset":139104,"componentType":5126,"count":11520,"type":"VEC2","name":"accessorUVs"}],"bufferViews":[{"buffer":0,"byteLength":57816,"name":"bufferViewScalar"},{"buffer":0,"byteOffset":57816,"byteLength":693792,"byteStride":12,"name":"bufferViewFloatVec3"},{"buffer":0,"byteOffset":751608,"byteLength":231264,"byteStride":8,"name":"bufferViewFloatVec2"}],"buffers":[{"uri":"model.bin","byteLength":982872}],"materials":[{"pbrMetallicRoughness":{"metallicFactor":0.0,"roughnessFactor":0.9},"doubleSided":true,"name":"01 - Default"},{"pbrMetallicRoughness":{"baseColorTexture":{"index":0},"metallicFactor":0.0,"roughnessFactor":0.9},"doubleSided":true,"name":"02 - Default"},{"pbrMetallicRoughness":{"baseColorFactor":[0.0,1.0,0.117647067,1.0],"metallicFactor":0.0,"roughnessFactor":0.9},"doubleSided":true,"name":"03 - Default"},{"pbrMetallicRoughness":{"baseColorFactor":[0.2784314,0.0,0.9921569,1.0],"metallicFactor":0.0,"roughnessFactor":0.9},"doubleSided":true,"name":"04 - Default"},{"pbrMetallicRoughness":{"baseColorFactor":[0.9725491,0.776470661,0.0,1.0],"metallicFactor":0.0,"roughnessFactor":0.9},"doubleSided":true,"name":"05 - Default"}],"textures":[{"sampler":0,"source":0,"name":"texture.jpg"}],"images":[{"uri":"./texture.jpg"}],"samplers":[{"magFilter":9729,"minFilter":9987}]}

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 MiB

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1,74 @@
7767517
72 73
Input input.1 0 1 data
Split splitncnn_input0 1 2 data input_splitncnn_0 input_splitncnn_1
Convolution Conv_0 1 1 input_splitncnn_1 102 0=64 1=3 4=1 5=1 6=1728
PReLU PRelu_1 1 1 102 105 0=64
Convolution Conv_2 1 1 105 106 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_3 1 1 106 109 0=64
Convolution Conv_4 1 1 109 110 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_5 1 1 110 113 0=64
Convolution Conv_6 1 1 113 114 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_7 1 1 114 117 0=64
Convolution Conv_8 1 1 117 118 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_9 1 1 118 121 0=64
Convolution Conv_10 1 1 121 122 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_11 1 1 122 125 0=64
Convolution Conv_12 1 1 125 126 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_13 1 1 126 129 0=64
Convolution Conv_14 1 1 129 130 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_15 1 1 130 133 0=64
Convolution Conv_16 1 1 133 134 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_17 1 1 134 137 0=64
Convolution Conv_18 1 1 137 138 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_19 1 1 138 141 0=64
Convolution Conv_20 1 1 141 142 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_21 1 1 142 145 0=64
Convolution Conv_22 1 1 145 146 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_23 1 1 146 149 0=64
Convolution Conv_24 1 1 149 150 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_25 1 1 150 153 0=64
Convolution Conv_26 1 1 153 154 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_27 1 1 154 157 0=64
Convolution Conv_28 1 1 157 158 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_29 1 1 158 161 0=64
Convolution Conv_30 1 1 161 162 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_31 1 1 162 165 0=64
Convolution Conv_32 1 1 165 166 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_33 1 1 166 169 0=64
Convolution Conv_34 1 1 169 170 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_35 1 1 170 173 0=64
Convolution Conv_36 1 1 173 174 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_37 1 1 174 177 0=64
Convolution Conv_38 1 1 177 178 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_39 1 1 178 181 0=64
Convolution Conv_40 1 1 181 182 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_41 1 1 182 185 0=64
Convolution Conv_42 1 1 185 186 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_43 1 1 186 189 0=64
Convolution Conv_44 1 1 189 190 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_45 1 1 190 193 0=64
Convolution Conv_46 1 1 193 194 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_47 1 1 194 197 0=64
Convolution Conv_48 1 1 197 198 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_49 1 1 198 201 0=64
Convolution Conv_50 1 1 201 202 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_51 1 1 202 205 0=64
Convolution Conv_52 1 1 205 206 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_53 1 1 206 209 0=64
Convolution Conv_54 1 1 209 210 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_55 1 1 210 213 0=64
Convolution Conv_56 1 1 213 214 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_57 1 1 214 217 0=64
Convolution Conv_58 1 1 217 218 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_59 1 1 218 221 0=64
Convolution Conv_60 1 1 221 222 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_61 1 1 222 225 0=64
Convolution Conv_62 1 1 225 226 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_63 1 1 226 229 0=64
Convolution Conv_64 1 1 229 230 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_65 1 1 230 233 0=64
Convolution Conv_66 1 1 233 234 0=48 1=3 4=1 5=1 6=27648
PixelShuffle DepthToSpace_67 1 1 234 235 0=4
Interp Resize_68 1 1 input_splitncnn_0 240 0=1 1=4.000000e+00 2=4.000000e+00
BinaryOp Add_69 2 1 235 240 output

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
#!/bin/zsh
setopt null_glob
INPUT="./images"
OUTPUT="./images_output"
MODELPATH="./models"
MODELNAME="ultramix-balanced-4x"
TARGET_WIDTH="4320"
FORMAT="jpg" # 或 webp
SCALE=3
MAX_SIZE=$((20 * 1024 * 1024)) # 20MB
./bin/mac/upscayl-bin -i "$INPUT" -o "$OUTPUT" -f $FORMAT -m $MODELPATH -n $MODELNAME -w $TARGET_WIDTH -j 4:8:4 -v
echo "\n===== 输入目录: $INPUT ====="
for img in "$INPUT"/*.{png,jpg,jpeg}; do
[ -e "$img" ] || continue
size=$(stat -f%z "$img")
res=$(sips -g pixelWidth -g pixelHeight "$img" 2>/dev/null | awk '/pixelWidth|pixelHeight/ {print $2}' | xargs | sed 's/ /x/')
echo "$(basename "$img"): ${size} bytes, ${res}"
done
echo "\n===== 输出目录: $OUTPUT ====="
for img in "$OUTPUT"/*.{png,jpg,jpeg}; do
[ -e "$img" ] || continue
size=$(stat -f%z "$img")
res=$(sips -g pixelWidth -g pixelHeight "$img" 2>/dev/null | awk '/pixelWidth|pixelHeight/ {print $2}' | xargs | sed 's/ /x/')
echo "$(basename "$img"): ${size} bytes, ${res}"
done

114
examples/assets/upscale_to_8k.sh Executable file
View File

@@ -0,0 +1,114 @@
#!/bin/zsh
setopt null_glob
# 设置默认值或从环境变量获取
INPUT="${INPUT_DIR:-./images}"
OUTPUT="${OUTPUT_DIR:-./images_output}"
MODELPATH="${MODELPATH:-./models}"
MODELNAME="${MODELNAME:-ultramix-balanced-4x}"
BIN_PATH="${BIN_PATH:-./bin/mac/upscayl-bin}"
TARGET_WIDTH=4320
TARGET_HEIGHT=7680
FORMAT="jpg"
THREADS="4:8:4"
TARGET_SIZE=$((10 * 1024 * 1024)) # 10MB
MAX_SIZE=$((20 * 1024 * 1024)) # 20MB
echo "===== 开始批量处理图片 ====="
echo "输入目录: $INPUT"
echo "输出目录: $OUTPUT"
echo "模型路径: $MODELPATH"
echo "模型名称: $MODELNAME"
echo "二进制文件: $BIN_PATH"
# 确保输出目录存在
mkdir -p "$OUTPUT"
# 循环对每个图片单独执行 upscayl-bin输出路径为文件路径
for img in "$INPUT"/*.{png,jpg,jpeg}; do
[ -e "$img" ] || continue
base=$(basename "$img")
out_img="$OUTPUT/${base%.*}.$FORMAT"
compress=10 # 0为无压缩100为最大压缩
try=1
echo "\n==== 开始处理: $base ===="
echo "输入文件: $img"
echo "输出文件: $out_img"
echo "模型: $MODELNAME, 模型路径: $MODELPATH"
echo "格式: $FORMAT"
# 获取原图宽高
read orig_w orig_h <<< $(sips -g pixelWidth -g pixelHeight "$img" 2>/dev/null | awk '/pixelWidth/ {w=$2} /pixelHeight/ {h=$2} END{print w,h}')
if [ "$orig_w" -lt "$orig_h" ]; then
short=$orig_w
long=$orig_h
else
short=$orig_h
long=$orig_w
fi
echo "DEBUG: orig_w=$orig_w, orig_h=$orig_h, short=$short, long=$long"
scale1=$(echo "scale=8; 4320/$short" | bc)
scale2=$(echo "scale=8; 7680/$long" | bc)
echo "DEBUG: scale1=$scale1, scale2=$scale2"
# 取scale1和scale2的最大值
scale=$(echo "$scale1 > $scale2" | bc -l)
if [ "$scale" -eq 1 ]; then
use_scale=$scale1
else
use_scale=$scale2
fi
use_scale=$(printf "%f" "$use_scale")
echo "DEBUG: use_scale=$use_scale"
# 保证不缩小
scale_cmp=$(echo "$use_scale > 1" | bc -l)
echo "DEBUG: scale_cmp=$scale_cmp"
if [ "$scale_cmp" -eq 1 ]; then
target_w=$(awk -v w="$orig_w" -v s="$use_scale" 'BEGIN{printf "%d", w*s+0.5}')
target_h=$(awk -v h="$orig_h" -v s="$use_scale" 'BEGIN{printf "%d", h*s+0.5}')
echo "原图分辨率: ${orig_w}x${orig_h}, 目标分辨率: ${target_w}x${target_h} (短边≥4320, 长边≥7680)"
else
target_w=$orig_w
target_h=$orig_h
echo "原图分辨率: ${orig_w}x${orig_h}, 已满足短边≥4320且长边≥7680无需放大"
fi
while true; do
echo "尝试第 $try 次: compress=$compress"
upscale_cmd="\"$BIN_PATH\" -i \"$img\" -o \"$out_img\" -f $FORMAT -m \"$MODELPATH\" -n $MODELNAME -r \"${target_w}x${target_h}\" -c $compress -j $THREADS -v"
echo "执行命令: $upscale_cmd"
eval $upscale_cmd
if [ ! -f "$out_img" ]; then
echo "$base: 未生成图片compress=$compress"
break
fi
size=$(stat -f%z "$out_img")
res=$(sips -g pixelWidth -g pixelHeight "$out_img" 2>/dev/null | awk '/pixelWidth|pixelHeight/ {print $2}' | xargs | sed 's/ /x/')
echo "$base: compress=$compress, size=${size} bytes, 分辨率=${res}"
if [ $size -le $MAX_SIZE ] || [ $compress -ge 100 ]; then
echo "$base: 满足大小要求,最终 compress=$compress"
break
fi
# compress 每次递增10最大不超过100
compress=$((compress + 10))
[ $compress -gt 100 ] && compress=100
try=$((try+1))
[ $try -gt 10 ] && echo "$base: 达到最大尝试次数,停止调整" && break
done
done
echo "\n===== 输入目录: $INPUT ====="
for img in "$INPUT"/*.{png,jpg,jpeg}; do
[ -e "$img" ] || continue
size=$(stat -f%z "$img")
res=$(sips -g pixelWidth -g pixelHeight "$img" 2>/dev/null | awk '/pixelWidth|pixelHeight/ {print $2}' | xargs | sed 's/ /x/')
echo "$(basename "$img"): ${size} bytes, ${res}"
done
echo "\n===== 输出目录: $OUTPUT ====="
for img in "$OUTPUT"/*.{png,jpg,jpeg}; do
[ -e "$img" ] || continue
size=$(stat -f%z "$img")
res=$(sips -g pixelWidth -g pixelHeight "$img" 2>/dev/null | awk '/pixelWidth|pixelHeight/ {print $2}' | xargs | sed 's/ /x/')
echo "$(basename "$img"): ${size} bytes, ${res}"
done

View File

View File

@@ -0,0 +1,131 @@
#!/bin/zsh
setopt null_glob
# 从环境变量或参数获取配置
INPUT_FILE="${INPUT_FILE:-$1}"
OUTPUT_FILE="${OUTPUT_FILE:-$2}"
MODELPATH="${MODELPATH:-./models}"
MODELNAME="${MODELNAME:-ultramix-balanced-4x}"
BIN_PATH="${BIN_PATH:-./bin/mac/upscayl-bin}"
TARGET_WIDTH=4320
TARGET_HEIGHT=7680
FORMAT="jpg"
THREADS="4:8:4"
TARGET_SIZE=$((10 * 1024 * 1024)) # 10MB
MAX_SIZE=$((20 * 1024 * 1024)) # 20MB
# 检查输入参数
if [ -z "$INPUT_FILE" ] || [ -z "$OUTPUT_FILE" ]; then
echo "错误: 需要指定输入和输出文件路径"
echo "用法: $0 <输入文件> <输出文件>"
echo "或者设置环境变量 INPUT_FILE 和 OUTPUT_FILE"
exit 1
fi
if [ ! -f "$INPUT_FILE" ]; then
echo "错误: 输入文件不存在: $INPUT_FILE"
exit 1
fi
echo "===== 开始处理单张图片 ====="
echo "输入文件: $INPUT_FILE"
echo "输出文件: $OUTPUT_FILE"
echo "模型路径: $MODELPATH"
echo "模型名称: $MODELNAME"
echo "二进制文件: $BIN_PATH"
# 确保输出目录存在
output_dir=$(dirname "$OUTPUT_FILE")
mkdir -p "$output_dir"
base=$(basename "$INPUT_FILE")
compress=10 # 0为无压缩100为最大压缩
try=1
echo "\n==== 开始处理: $base ===="
echo "输入文件: $INPUT_FILE"
echo "输出文件: $OUTPUT_FILE"
echo "模型: $MODELNAME, 模型路径: $MODELPATH"
echo "格式: $FORMAT"
# 获取原图宽高
read orig_w orig_h <<< $(sips -g pixelWidth -g pixelHeight "$INPUT_FILE" 2>/dev/null | awk '/pixelWidth/ {w=$2} /pixelHeight/ {h=$2} END{print w,h}')
if [ "$orig_w" -lt "$orig_h" ]; then
short=$orig_w
long=$orig_h
else
short=$orig_h
long=$orig_w
fi
echo "DEBUG: orig_w=$orig_w, orig_h=$orig_h, short=$short, long=$long"
scale1=$(echo "scale=8; 4320/$short" | bc)
scale2=$(echo "scale=8; 7680/$long" | bc)
echo "DEBUG: scale1=$scale1, scale2=$scale2"
# 取scale1和scale2的最大值
scale=$(echo "$scale1 > $scale2" | bc -l)
if [ "$scale" -eq 1 ]; then
use_scale=$scale1
else
use_scale=$scale2
fi
use_scale=$(printf "%f" "$use_scale")
echo "DEBUG: use_scale=$use_scale"
# 保证不缩小
scale_cmp=$(echo "$use_scale > 1" | bc -l)
echo "DEBUG: scale_cmp=$scale_cmp"
if [ "$scale_cmp" -eq 1 ]; then
target_w=$(awk -v w="$orig_w" -v s="$use_scale" 'BEGIN{printf "%d", w*s+0.5}')
target_h=$(awk -v h="$orig_h" -v s="$use_scale" 'BEGIN{printf "%d", h*s+0.5}')
echo "原图分辨率: ${orig_w}x${orig_h}, 目标分辨率: ${target_w}x${target_h} (短边≥4320, 长边≥7680)"
else
target_w=$orig_w
target_h=$orig_h
echo "原图分辨率: ${orig_w}x${orig_h}, 已满足短边≥4320且长边≥7680无需放大"
fi
# 处理图片,循环调整压缩率直到满足大小要求
while true; do
echo "尝试第 $try 次: compress=$compress"
upscale_cmd="\"$BIN_PATH\" -i \"$INPUT_FILE\" -o \"$OUTPUT_FILE\" -f $FORMAT -m \"$MODELPATH\" -n $MODELNAME -r \"${target_w}x${target_h}\" -c $compress -j $THREADS -v"
echo "执行命令: $upscale_cmd"
eval $upscale_cmd
if [ ! -f "$OUTPUT_FILE" ]; then
echo "$base: 未生成图片compress=$compress"
echo "处理失败"
exit 1
fi
size=$(stat -f%z "$OUTPUT_FILE")
res=$(sips -g pixelWidth -g pixelHeight "$OUTPUT_FILE" 2>/dev/null | awk '/pixelWidth|pixelHeight/ {print $2}' | xargs | sed 's/ /x/')
echo "$base: compress=$compress, size=${size} bytes, 分辨率=${res}"
if [ $size -le $MAX_SIZE ] || [ $compress -ge 100 ]; then
echo "$base: 满足大小要求,最终 compress=$compress"
break
fi
# compress 每次递增10最大不超过100
compress=$((compress + 10))
[ $compress -gt 100 ] && compress=100
try=$((try+1))
[ $try -gt 10 ] && echo "$base: 达到最大尝试次数,停止调整" && break
done
echo "\n===== 处理完成 ====="
if [ -f "$OUTPUT_FILE" ]; then
input_size=$(stat -f%z "$INPUT_FILE")
output_size=$(stat -f%z "$OUTPUT_FILE")
input_res=$(sips -g pixelWidth -g pixelHeight "$INPUT_FILE" 2>/dev/null | awk '/pixelWidth|pixelHeight/ {print $2}' | xargs | sed 's/ /x/')
output_res=$(sips -g pixelWidth -g pixelHeight "$OUTPUT_FILE" 2>/dev/null | awk '/pixelWidth|pixelHeight/ {print $2}' | xargs | sed 's/ /x/')
echo "输入: $(basename "$INPUT_FILE") - ${input_size} bytes, ${input_res}"
echo "输出: $(basename "$OUTPUT_FILE") - ${output_size} bytes, ${output_res}"
echo "处理成功完成!"
else
echo "处理失败:未生成输出文件"
exit 1
fi

View File

@@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>12.0</string>
<string>13.0</string>
</dict>
</plist>

View File

@@ -1,22 +1,88 @@
PODS:
- DKImagePickerController/Core (4.3.9):
- DKImagePickerController/ImageDataManager
- DKImagePickerController/Resource
- DKImagePickerController/ImageDataManager (4.3.9)
- DKImagePickerController/PhotoGallery (4.3.9):
- DKImagePickerController/Core
- DKPhotoGallery
- DKImagePickerController/Resource (4.3.9)
- DKPhotoGallery (0.0.19):
- DKPhotoGallery/Core (= 0.0.19)
- DKPhotoGallery/Model (= 0.0.19)
- DKPhotoGallery/Preview (= 0.0.19)
- DKPhotoGallery/Resource (= 0.0.19)
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Core (0.0.19):
- DKPhotoGallery/Model
- DKPhotoGallery/Preview
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Model (0.0.19):
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Preview (0.0.19):
- DKPhotoGallery/Model
- DKPhotoGallery/Resource
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Resource (0.0.19):
- SDWebImage
- SwiftyGif
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
- Flutter (1.0.0)
- package_info_plus (0.4.5):
- Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- SDWebImage (5.21.1):
- SDWebImage/Core (= 5.21.1)
- SDWebImage/Core (5.21.1)
- SwiftyGif (5.4.5)
- thermion_flutter (0.0.1):
- Flutter
DEPENDENCIES:
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- thermion_flutter (from `.symlinks/plugins/thermion_flutter/ios`)
SPEC REPOS:
trunk:
- DKImagePickerController
- DKPhotoGallery
- SDWebImage
- SwiftyGif
EXTERNAL SOURCES:
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
Flutter:
:path: Flutter
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
thermion_flutter:
:path: ".symlinks/plugins/thermion_flutter/ios"
SPEC CHECKSUMS:
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
thermion_flutter: c965e09831858465a1a8df59ff97e40a4d002773
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
package_info_plus: 580e9a5f1b6ca5594e7c9ed5f92d1dfb2a66b5e1
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
SDWebImage: f29024626962457f3470184232766516dee8dfea
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
thermion_flutter: d0c4adbb1de69e5ecc244d70bbde81d8497246b2
PODFILE CHECKSUM: a57f30d18f102dd3ce366b1d62a55ecbef2158e5
COCOAPODS: 1.15.2
COCOAPODS: 1.16.2

View File

@@ -197,6 +197,7 @@
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
6DA127E3193C7EF5F7B6B4C0 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -284,6 +285,23 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
6DA127E3193C7EF5F7B6B4C0 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
78F05AF37D9E628BE6E89ABD /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -436,7 +454,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -452,7 +470,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = TM2B4SJXNJ;
DEVELOPMENT_TEAM = 979UK3QQJ3;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
@@ -567,7 +585,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -618,7 +636,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -636,7 +654,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = TM2B4SJXNJ;
DEVELOPMENT_TEAM = 979UK3QQJ3;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
@@ -660,7 +678,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = TM2B4SJXNJ;
DEVELOPMENT_TEAM = 979UK3QQJ3;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;

View File

@@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
@@ -54,11 +55,13 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">

View File

@@ -0,0 +1,114 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:thermion_flutter/thermion_flutter.dart';
class Cup extends StatefulWidget {
const Cup({super.key});
@override
State<Cup> createState() => _CupState();
}
class _CupState extends State<Cup> {
bool _isLoading = true;
String? _modelPath, _modelUri;
String _statusMessage = "正在加载本地模型...";
@override
void initState() {
super.initState();
_loadLocalModel();
}
// 从应用支持目录加载模型
Future<void> _loadLocalModel() async {
try {
setState(() {
_statusMessage = "获取本地模型路径...";
});
final supportDir = await getApplicationSupportDirectory();
final modelFile = File(p.join(supportDir.path, 'cup', 'model.gltf'));
String modelPath =
File(p.join(supportDir.path, 'cup', 'model.gltf')).absolute.path;
final cupDir = Directory('${supportDir.path}/cup');
final modelUri = File('${cupDir.path}/model.gltf').uri.toString();
print('加载本地模modelPath: $modelPath');
print('加载本地模modelUri: $modelUri');
if (await modelFile.exists()) {
setState(() {
_modelPath = modelPath;
_modelUri = modelUri;
_isLoading = false;
_statusMessage = "本地模型加载完成";
});
} else {
setState(() {
_isLoading = false;
_statusMessage = "本地模型文件不存在";
});
}
} catch (e) {
print('加载本地模型失败: $e');
setState(() {
_isLoading = false;
_statusMessage = "加载失败: $e";
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('本地模型查看'),
),
body: _isLoading
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircularProgressIndicator(),
const SizedBox(height: 16),
Text(_statusMessage),
],
),
)
: _modelPath == null
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error, size: 48, color: Colors.red),
const SizedBox(height: 16),
Text(_statusMessage),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _loadLocalModel,
child: const Text('重试'),
),
],
),
)
: ViewerWidget(
// assetPath: "file://$_modelPath",
assetPath: _modelUri,
skyboxPath: "assets/default_env_skybox.ktx",
iblPath: "assets/default_env_ibl.ktx",
transformToUnitCube: true,
initialCameraPosition: Vector3(0, 0, 6),
background: Colors.grey[200],
manipulatorType: ManipulatorType.ORBIT,
initial: const Center(
child: CircularProgressIndicator(),
),
),
);
}
}

View File

@@ -0,0 +1,142 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:thermion_flutter/thermion_flutter.dart';
// import 'package:thermion_dart/src/viewer/src/thermion_viewer_base.dart';
class CupPage1 extends StatelessWidget {
const CupPage1({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(children: [
Positioned.fill(
child: ViewerWidget(
assetPath: "assets/cup/model.gltf",
skyboxPath: "assets/default_env_skybox.ktx",
iblPath: "assets/default_env_ibl.ktx",
transformToUnitCube: true,
initialCameraPosition: Vector3(0, 0, 6),
background: Colors.blue,
manipulatorType: ManipulatorType.ORBIT,
onViewerAvailable: (viewer) async {
await Future.delayed(const Duration(seconds: 5));
await viewer.removeSkybox();
},
initial: Container(
color: Colors.grey,
),
))
]));
}
}
class CupPage extends StatefulWidget {
const CupPage({super.key});
@override
State<CupPage> createState() => _CupPageState();
}
class _CupPageState extends State<CupPage> {
bool _isLoading = true;
String? _modelPath, modelUri;
String _statusMessage = "正在加载本地模型...";
@override
void initState() {
super.initState();
_loadLocalModel();
}
// 从应用支持目录加载模型
Future<void> _loadLocalModel() async {
try {
setState(() {
_statusMessage = "获取本地模型路径...";
});
final supportDir = await getApplicationSupportDirectory();
final modelFile = File(p.join(supportDir.path, 'cup', 'model.gltf'));
String modelPath =
File(p.join(supportDir.path, 'cup', 'model.gltf')).absolute.path;
final cupDir = Directory('${supportDir.path}/cup');
final modelUri = File('${cupDir.path}/model.gltf').uri;
print('加载本地模modelPath: $modelPath');
print('加载本地模modelUri: $modelUri');
if (await modelFile.exists()) {
setState(() {
_modelPath = modelPath;
_isLoading = false;
_statusMessage = "本地模型加载完成";
});
} else {
setState(() {
_isLoading = false;
_statusMessage = "本地模型文件不存在";
});
}
} catch (e) {
print('加载本地模型失败: $e');
setState(() {
_isLoading = false;
_statusMessage = "加载失败: $e";
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('本地模型查看'),
),
body: _isLoading
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircularProgressIndicator(),
const SizedBox(height: 16),
Text(_statusMessage),
],
),
)
: _modelPath == null
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error, size: 48, color: Colors.red),
const SizedBox(height: 16),
Text(_statusMessage),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _loadLocalModel,
child: const Text('重试'),
),
],
),
)
: ViewerWidget(
assetPath: "file://$_modelPath",
// assetPath: modelUri,
skyboxPath: "assets/default_env_skybox.ktx",
iblPath: "assets/default_env_ibl.ktx",
transformToUnitCube: true,
initialCameraPosition: Vector3(0, 0, 6),
background: Colors.grey[200],
manipulatorType: ManipulatorType.ORBIT,
initial: const Center(
child: CircularProgressIndicator(),
),
),
);
}
}

View File

@@ -1,4 +1,407 @@
import 'dart:convert';
import 'dart:io';
import 'package:animations/utils/binary_manager.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'cup.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// 初始化3D模型资源
try {
await BinaryManager.initializeAppResources();
print('3D模型资源初始化成功');
} catch (e) {
print('无法初始化3D模型资源: $e');
}
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '3D模型纹理替换',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: ModelViewerWithTextureReplace(),
);
}
}
class ModelViewerWithTextureReplace extends StatefulWidget {
const ModelViewerWithTextureReplace({super.key});
@override
State<ModelViewerWithTextureReplace> createState() =>
_ModelViewerWithTextureReplaceState();
}
class _ModelViewerWithTextureReplaceState
extends State<ModelViewerWithTextureReplace> {
bool _isLoading = true;
String? _modelPath;
String? _texturePath;
int _keyValue = 0;
String _statusMessage = "正在加载模型...";
bool _isReplacingTexture = false;
File? _selectedImage;
@override
void initState() {
super.initState();
_load3DModels();
// _loadModel();
}
// 加载3D模型文件
Future<void> _load3DModels() async {
try {
setState(() {
_statusMessage = "获取模型路径...";
});
// 使用BinaryManager获取3D模型文件路径
// final supportDir = await getApplicationSupportDirectory();
// final cupDir = Directory('${supportDir.path}/cup');
// final modelFile = File('${cupDir.path}/model.gltf');
final modelPath = await BinaryManager.get3DModelPath("model.gltf");
final texturePath = await BinaryManager.get3DModelPath("texture.jpg");
// print("路径: $modelPath");
// print("文件是否存在: ${await File(modelPath).exists()}");
// print("文件路径: ${await File(modelPath).uri}");
//
// setState(() {
// _modelPath = File(modelPath).uri.toString(); // 确保是本地路径
// });
// if (!await File(modelPath).exists()) {
// print("❌ 模型文件不存在: $modelPath");
// }
// if (!await File(texturePath).exists()) {
// print("❌ 纹理文件不存在: $texturePath");
// }
print('modelPath=${modelPath}\ntexturePath=${texturePath}');
setState(() {
_modelPath = modelPath;
_texturePath = texturePath;
_isLoading = false;
_statusMessage = "加载完成";
});
} catch (e) {
print('加载3D模型失败: $e');
setState(() {
_isLoading = false;
_statusMessage = "加载失败: $e";
});
}
}
// 使用FilePicker选择新纹理
Future<void> _pickNewTexture() async {
if (_isReplacingTexture || _texturePath == null) return;
setState(() {
_isReplacingTexture = true;
_statusMessage = "正在选择纹理文件...";
});
try {
// 选择图片文件
FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.image,
allowMultiple: false,
);
if (result == null ||
result.files.isEmpty ||
result.files.single.path == null) {
setState(() {
_statusMessage = "取消选择";
});
return;
}
_selectedImage = File(result.files.single.path!);
final selectedFile = result.files.single;
final fileExtension = p.extension(selectedFile.path!).toLowerCase();
// 可选:仅允许特定图片格式
if (!['.jpg', '.jpeg', '.png'].contains(fileExtension)) {
setState(() {
_statusMessage = "仅支持 JPG / PNG 格式纹理";
});
return;
}
final newTexture = File(selectedFile.path!);
final supportDir = await getApplicationSupportDirectory();
final modelDir = p.join(supportDir.path, 'cup');
final modelPath = p.join(modelDir, 'model.gltf');
final modelFile = File(modelPath);
if (!await newTexture.exists()) {
setState(() {
_statusMessage = "所选纹理文件不存在";
});
return;
}
// 复制新纹理为固定名 new.jpg 到模型目录
final newTexturePath = p.join(modelDir, 'new.jpg');
await newTexture.copy(newTexturePath);
_texturePath = newTexturePath;
// 修改 GLTF 文件
if (await modelFile.exists()) {
String gltfContent = await modelFile.readAsString();
// ✅ 修复 GLTF 中非法负号格式(例如 "- 0.123" => "-0.123"
gltfContent = gltfContent.replaceAllMapped(
RegExp(r'-\s+(\d+(\.\d+)?)'),
(match) => '-${match.group(1)}',
);
final gltfJson = jsonDecode(gltfContent);
// 安全修改 images[0].uri
if (gltfJson['images'] != null && gltfJson['images'].isNotEmpty) {
gltfJson['images'][0]['uri'] = './new.jpg';
}
// 可选:修改 textures[0].name
if (gltfJson['textures'] != null && gltfJson['textures'].isNotEmpty) {
gltfJson['textures'][0]['name'] = 'new.jpg';
}
// 写回更新后的内容
final updatedContent =
const JsonEncoder.withIndent(' ').convert(gltfJson);
await modelFile.writeAsString(updatedContent);
print('GLTF文件已成功更新');
// 重建模型视图
setState(() {
_keyValue++;
_statusMessage = "纹理已更新为 new.jpg";
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('纹理已成功更新为 new.jpg')),
);
} else {
print('GLTF文件不存在: $modelPath');
setState(() {
_statusMessage = "GLTF文件不存在";
});
}
} catch (e) {
print('更换纹理失败: $e');
setState(() {
_statusMessage = "更换纹理失败: $e";
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('更换纹理失败: $e')),
);
} finally {
setState(() {
_isReplacingTexture = false;
});
}
}
// 重置纹理到原始状态
Future<void> _resetTexture() async {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Cup(),
// builder: (context) => CupPage(),
),
);
return;
if (_isReplacingTexture || _texturePath == null) return;
setState(() {
_isReplacingTexture = true;
_statusMessage = "正在重置纹理...";
});
try {
// 检查备份文件是否存在
final backupTexturePath = '${_texturePath!}.backup';
final backupFile = File(backupTexturePath);
if (await backupFile.exists()) {
// 恢复备份文件
await backupFile.copy(_texturePath!);
// 给渲染器一点时间来处理文件变化
await Future.delayed(const Duration(milliseconds: 500));
// 强制重建ViewerWidget以应用恢复的纹理
setState(() {
_keyValue++;
_statusMessage = "纹理已重置";
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('纹理已重置到原始状态')),
);
} else {
setState(() {
_statusMessage = "没有备份文件可恢复";
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('没有备份文件可恢复')),
);
}
} catch (e) {
print('重置纹理失败: $e');
setState(() {
_statusMessage = "重置纹理失败: $e";
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('重置纹理失败: $e')),
);
} finally {
setState(() {
_isReplacingTexture = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('3D模型纹理替换'),
actions: [
IconButton(
icon: const Icon(Icons.texture),
onPressed: _isReplacingTexture ? null : _pickNewTexture,
tooltip: '更换纹理',
),
],
),
body: _isLoading
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircularProgressIndicator(),
const SizedBox(height: 16),
Text(_statusMessage),
],
),
)
: _modelPath == null
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error, size: 48, color: Colors.red),
const SizedBox(height: 16),
const Text('加载模型失败'),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _load3DModels,
child: const Text('重试'),
),
],
),
)
: Column(
children: [
if (_selectedImage != null)
Container(
width: 300,
height: 300,
decoration: BoxDecoration(
border: Border.all(color: Colors.blue, width: 2),
borderRadius: BorderRadius.circular(12),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.file(
_selectedImage!,
fit: BoxFit.cover,
),
),
),
// Expanded(
// child: ViewerWidget(
// key: ValueKey(_keyValue),
// assetPath: "file://$_modelPath",
// skyboxPath: "assets/default_env_skybox.ktx",
// iblPath: "assets/default_env_ibl.ktx",
// transformToUnitCube: true,
// initialCameraPosition: Vector3(0, 0, 6),
// background: Colors.grey[200],
// manipulatorType: ManipulatorType.ORBIT,
// initial: const Center(
// child: CircularProgressIndicator(),
// ),
// ),
// ),
Container(
padding: const EdgeInsets.all(16),
color: Colors.grey[100],
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton.icon(
icon: const Icon(Icons.texture),
label: const Text('选择纹理图片'),
onPressed: _isReplacingTexture
? null
: _pickNewTexture,
),
ElevatedButton.icon(
icon: const Icon(Icons.refresh),
label: const Text('3d查看'),
onPressed:
_isReplacingTexture ? null : _resetTexture,
),
],
),
const SizedBox(height: 8),
Text(
_statusMessage,
style: TextStyle(
color: Colors.grey[600],
fontStyle: FontStyle.italic,
),
),
],
),
),
],
),
);
}
}
/*
import 'dart:async';
import 'package:animations/cup_page.dart';
import 'package:logging/logging.dart';
import 'package:flutter/material.dart' hide View;
import 'package:thermion_flutter/thermion_flutter.dart';
@@ -21,7 +424,7 @@ class MyApp extends StatelessWidget {
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Thermion Demo Home Page'),
home: const CupPage(),
);
}
}
@@ -39,7 +442,7 @@ class _MyHomePageState extends State<MyHomePage> {
ThermionViewer? _thermionViewer;
ThermionAsset? _asset;
final _droneUri = "assets/BusterDrone/scene.gltf";
final _droneUri = "assets/cup/model.gltf";
final _cubeUri = "assets/cube_with_morph_targets.glb";
String? _loaded;
@@ -68,7 +471,6 @@ class _MyHomePageState extends State<MyHomePage> {
gltfAnimations.clear();
gltfAnimations.addAll(labels);
selectedGltfAnimation = 0;
}
setState(() {});
}
@@ -165,3 +567,4 @@ class _MyHomePageState extends State<MyHomePage> {
]);
}
}
*/

View File

@@ -0,0 +1,284 @@
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:thermion_flutter/thermion_flutter.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '3D模型纹理替换',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const ModelViewerScreen(),
);
}
}
class ModelViewerScreen extends StatefulWidget {
const ModelViewerScreen({super.key});
@override
State<ModelViewerScreen> createState() => _ModelViewerScreenState();
}
class _ModelViewerScreenState extends State<ModelViewerScreen> {
bool _isLoading = true;
String? _modelPath;
String? _texturePath;
int _keyValue = 0;
String _statusMessage = "正在加载模型...";
@override
void initState() {
super.initState();
_loadModel();
}
// 加载模型文件到临时目录
Future<void> _loadModel() async {
try {
setState(() {
_statusMessage = "准备模型目录...";
});
final directory = await getApplicationDocumentsDirectory();
final modelDir = Directory('${directory.path}/model');
// 确保目录存在
if (!await modelDir.exists()) {
await modelDir.create(recursive: true);
}
setState(() {
_statusMessage = "加载GLTF文件...";
});
// 加载 .gltf 文件并写入本地
final gltfData =
await DefaultAssetBundle.of(context).load('assets/cup/model.gltf');
final gltfFile = File('${modelDir.path}/model.gltf');
await gltfFile.writeAsBytes(gltfData.buffer.asUint8List());
/// ✅ 打印本地路径(关键)
print('✅ GLTF 本地路径: ${gltfFile.path}');
print('✅ 是否存在: ${await gltfFile.exists()}');
setState(() {
_statusMessage = "加载模型数据...";
});
// 加载 .bin 文件并写入本地
final binData =
await DefaultAssetBundle.of(context).load('assets/cup/model.bin');
final binFile = File('${modelDir.path}/model.bin');
await binFile.writeAsBytes(binData.buffer.asUint8List());
setState(() {
_statusMessage = "加载纹理...";
});
// 加载纹理文件并写入本地
final textureData =
await DefaultAssetBundle.of(context).load('assets/cup/texture.jpg');
final textureFile = File('${modelDir.path}/texture.jpg');
await textureFile.writeAsBytes(textureData.buffer.asUint8List());
/// ✅ 打印纹理路径
print('✅ 纹理本地路径: ${textureFile.path}');
print('✅ 是否存在: ${await textureFile.exists()}');
setState(() {
_modelPath = gltfFile.path;
_texturePath = textureFile.path;
_isLoading = false;
_statusMessage = "加载完成";
});
} catch (e) {
print('❌ 加载模型失败: $e');
setState(() {
_isLoading = false;
_statusMessage = "加载失败: $e";
});
}
}
// 使用FilePicker选择新纹理 - 修复配置错误
Future<void> _pickNewTexture() async {
try {
setState(() {
_statusMessage = "正在选择纹理文件...";
});
// 使用FilePicker选择文件 - 修复配置
// 选项1: 使用FileType.image不能指定allowedExtensions
FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.image,
allowMultiple: false,
);
// 选项2: 如果需要特定扩展名使用FileType.custom
/*
FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowMultiple: false,
allowedExtensions: ['jpg', 'jpeg', 'png', 'bmp', 'tga'],
);
*/
if (result != null &&
result.files.isNotEmpty &&
result.files.single.path != null &&
_texturePath != null) {
setState(() {
_statusMessage = "正在应用新纹理...";
});
// 获取选中的文件
PlatformFile file = result.files.first;
File newTexture = File(file.path!);
// 检查文件是否存在
if (await newTexture.exists()) {
// 复制选中的图片到纹理路径
await newTexture.copy(_texturePath!);
// 强制重建ViewerWidget以应用新纹理
setState(() {
_keyValue++;
_statusMessage = "纹理已更新";
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('纹理已成功更新')),
);
} else {
setState(() {
_statusMessage = "文件不存在";
});
}
} else {
setState(() {
_statusMessage = "取消选择";
});
}
} catch (e) {
print('更换纹理失败: $e');
setState(() {
_statusMessage = "更换纹理失败";
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('更换纹理失败: $e')),
);
}
return;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('3D模型纹理替换'),
actions: [
IconButton(
icon: const Icon(Icons.texture),
onPressed: _pickNewTexture,
tooltip: '更换纹理',
),
],
),
body: _isLoading
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircularProgressIndicator(),
const SizedBox(height: 16),
Text(_statusMessage),
],
),
)
: _modelPath == null
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error, size: 48, color: Colors.red),
const SizedBox(height: 16),
const Text('加载模型失败'),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _loadModel,
child: const Text('重试'),
),
],
),
)
: Column(
children: [
Expanded(
child: ViewerWidget(
key: ValueKey(_keyValue),
assetPath: _modelPath!,
skyboxPath: "assets/default_env_skybox.ktx",
iblPath: "assets/default_env_ibl.ktx",
transformToUnitCube: true,
initialCameraPosition: Vector3(0, 0, 6),
background: Colors.grey[200],
manipulatorType: ManipulatorType.ORBIT,
initial: const Center(
child: CircularProgressIndicator(),
),
),
),
Container(
padding: const EdgeInsets.all(16),
color: Colors.grey[100],
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton.icon(
icon: const Icon(Icons.texture),
label: const Text('选择纹理图片'),
onPressed: _pickNewTexture,
),
ElevatedButton.icon(
icon: const Icon(Icons.refresh),
label: const Text('重置视图'),
onPressed: () {
setState(() {
_keyValue++;
});
},
),
],
),
const SizedBox(height: 8),
Text(
_statusMessage,
style: TextStyle(
color: Colors.grey[600],
fontStyle: FontStyle.italic,
),
),
],
),
),
],
),
);
}
}

View File

@@ -0,0 +1,190 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:thermion_flutter/thermion_flutter.dart';
class LocalModelViewer extends StatefulWidget {
@override
_LocalModelViewerState createState() => _LocalModelViewerState();
}
class _LocalModelViewerState extends State<LocalModelViewer> {
ThermionViewer? _viewer;
ThermionAsset? _asset;
bool _isLoading = true;
bool _hasError = false;
String _statusMessage = "初始化中...";
@override
void initState() {
super.initState();
ThermionFlutterPlugin.createViewer().then((viewer) async {
_viewer = viewer;
_loadLocalModel(); // 在 viewer 初始化之后再加载模型
});
}
Future<void> _loadLocalModel() async {
try {
setState(() {
_isLoading = true;
_statusMessage = "获取本地模型路径...";
});
final supportDir = await getApplicationSupportDirectory();
final modelDir = p.join(supportDir.path, 'cup');
final modelPath = p.join(modelDir, 'model.gltf');
final modelFile = File(modelPath);
if (await modelFile.exists()) {
// 检查二进制文件
final binPath = p.join(modelDir, 'model.bin');
final binFile = File(binPath);
if (!await binFile.exists()) {
throw Exception("二进制文件不存在: $binPath");
}
// 检查文件大小
final binSize = await binFile.length();
if (binSize == 0) {
throw Exception("二进制文件为空");
}
// 检查纹理文件
final texturePath = p.join(modelDir, 'texture.jpg');
final textureFile = File(texturePath);
if (!await textureFile.exists()) {
throw Exception("纹理文件不存在: $texturePath");
}
setState(() {
_statusMessage = "读取和修改模型数据...";
});
// 读取GLTF文件内容
String gltfContent = await modelFile.readAsString();
// 修改纹理路径(去掉"./"前缀)
gltfContent = gltfContent.replaceAll('"./texture.jpg"', '"new.jpg"');
// 转换为字节数组
Uint8List gltfBytes = Uint8List.fromList(utf8.encode(gltfContent));
// 使用绝对路径作为资源URI
final resourceUri = "file://${Directory(modelDir).absolute.path}";
setState(() {
_statusMessage = "加载模型中...";
});
final asset = await _viewer?.loadGltfFromBuffer(
gltfBytes,
resourceUri: resourceUri,
addToScene: true,
);
setState(() {
_asset = asset;
_isLoading = false;
_statusMessage = "模型加载完成";
});
} else {
setState(() {
_isLoading = false;
_statusMessage = "本地模型文件不存在";
});
}
} catch (e) {
print('加载本地模型失败: $e');
setState(() {
_isLoading = false;
_hasError = true;
_statusMessage = "加载失败: $e";
});
}
}
@override
Widget build(BuildContext context) {
if (_isLoading) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text(_statusMessage),
],
),
);
}
if (_hasError || _viewer == null) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline, color: Colors.red, size: 48),
SizedBox(height: 16),
Text("加载3D模型失败"),
Text(_statusMessage, style: TextStyle(color: Colors.red)),
SizedBox(height: 16),
ElevatedButton(
onPressed: _loadLocalModel,
child: Text("重试"),
),
],
),
);
}
return Scaffold(
appBar: AppBar(
title: const Text('本地模型查看'),
),
body: ThermionListenerWidget(
inputHandler: DelegateInputHandler.fixedOrbit(_viewer!),
child: ThermionWidget(
viewer: _viewer!,
showFpsCounter: true, // 可选显示FPS计数器
),
),
);
// 使用ThermionWidget显示3D内容
return ThermionListenerWidget(
inputHandler: DelegateInputHandler.fixedOrbit(_viewer!),
child: ThermionWidget(
viewer: _viewer!,
showFpsCounter: true, // 可选显示FPS计数器
),
);
}
Future<void> deleteModelFile() async {
// 获取应用支持目录
final supportDir = await getApplicationSupportDirectory();
// 构建 model.gltf 的完整路径
final modelFile = File(p.join(supportDir.path, 'cup', 'model.gltf'));
// 检查文件是否存在,然后删除
if (await modelFile.exists()) {
await modelFile.delete();
print('model.gltf 已删除');
} else {
print('model.gltf 文件不存在');
}
}
@override
void dispose() {
// deleteModelFile();
// 清理资源
_viewer?.dispose();
super.dispose();
}
}

View File

@@ -0,0 +1,405 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
class BinaryManager {
/// 当前应用版本(用于初始化检查)
static String? _currentVersion;
/// 获取当前应用版本
static Future<String> _getCurrentVersion() async {
if (_currentVersion == null) {
final packageInfo = await PackageInfo.fromPlatform();
_currentVersion = '${packageInfo.version}+${packageInfo.buildNumber}';
}
return _currentVersion!;
}
/// 检查是否已完成初始化(基于版本号)
static Future<bool> isInitialized() async {
final supportDir = await getApplicationSupportDirectory();
final currentVersion = await _getCurrentVersion();
final initMarker = File(
p.join(supportDir.path, '.initialized_$currentVersion'),
);
return await initMarker.exists();
}
/// 标记初始化完成(文件名包含版本号)
static Future<void> markInitialized() async {
final supportDir = await getApplicationSupportDirectory();
final currentVersion = await _getCurrentVersion();
final initMarker = File(
p.join(supportDir.path, '.initialized_$currentVersion'),
);
await initMarker.writeAsString('${DateTime.now().toIso8601String()}\n');
}
/// 从assets目录提取文件到指定路径
static Future<void> _extractAsset(String assetPath, String localPath) async {
final localFile = File(localPath);
try {
// 从assets中读取数据
final ByteData data = await rootBundle.load(assetPath);
final List<int> bytes = data.buffer.asUint8List();
// 确保目录存在
final dir = Directory(p.dirname(localPath));
if (!await dir.exists()) {
await dir.create(recursive: true);
}
// 写入本地文件
await localFile.writeAsBytes(bytes);
print('成功提取文件: $assetPath -> $localPath');
} catch (e) {
print('提取资源文件失败 $assetPath: $e');
rethrow;
}
}
/// 初始化应用程序资源 - 将所有必要的assets迁移到系统文件夹
static Future<void> initializeAppResources() async {
final currentVersion = await _getCurrentVersion();
if (await isInitialized()) {
print('应用程序资源已初始化,当前版本: $currentVersion');
return;
}
try {
// 迁移shell脚本
await _migrateShellScript();
// 迁移二进制文件
await _migrateBinaries();
// 迁移模型文件
await _migrateModels();
// 迁移3D模型文件
await migrate3DModels();
// 标记初始化完成
await markInitialized();
print('应用程序资源初始化完成');
} catch (e) {
print('应用程序资源初始化失败: $e');
rethrow;
}
}
/// 迁移3D模型文件
static Future<void> migrate3DModels() async {
print('开始迁移3D模型文件...');
final supportDir = await getApplicationSupportDirectory();
final models3dDir = Directory(p.join(supportDir.path, 'cup'));
// 创建3D模型目录
if (!await models3dDir.exists()) {
await models3dDir.create(recursive: true);
}
// 3D模型文件列表
final model3dFiles = ['model.gltf', 'model.bin', 'texture.jpg'];
// 迁移所有3D模型文件
for (final modelFile in model3dFiles) {
try {
// if (modelFile == 'model.gltf') {
// // 对于GLTF文件我们需要修改内容
// await _extractAndModifyGltfAsset(
// 'assets/cup/$modelFile',
// p.join(models3dDir.path, modelFile),
// );
// } else {
// 对于其他文件,直接复制
await _extractAsset(
'assets/cup/$modelFile',
p.join(models3dDir.path, modelFile),
);
// }
} catch (e) {
print('迁移3D模型文件失败 $modelFile: $e');
// 继续迁移其他模型文件
}
}
print('3D模型文件迁移完成');
}
static Future<void> _extractAndModifyGltfAsset(
String assetPath, String targetPath) async {
try {
// 读取资源文件内容
final byteData = await rootBundle.load(assetPath);
final buffer = byteData.buffer;
final gltfContent = utf8.decode(
buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes));
// 修改纹理路径(去掉"./"前缀)
final modifiedGltfContent =
gltfContent.replaceAll('"./texture.jpg"', '"texture.jpg"');
// 写入修改后的内容到目标文件
final file = File(targetPath);
await file.writeAsBytes(utf8.encode(modifiedGltfContent));
print('已修改并迁移: $assetPath -> $targetPath');
} catch (e) {
print('提取并修改GLTF资源失败: $e');
rethrow;
}
}
static Future<void> _migrateShellScript() async {
print('开始迁移脚本文件...');
final supportDir = await getApplicationSupportDirectory();
// 迁移 Shell 脚本 (macOS/Linux)
final shellScriptPath = p.join(supportDir.path, 'upscale_to_8k_single.sh');
await _extractAsset('assets/upscale_to_8k_single.sh', shellScriptPath);
// 设置脚本执行权限(仅限 macOS/Linux
if (Platform.isMacOS || Platform.isLinux) {
try {
final result = await Process.run('chmod', ['+x', shellScriptPath]);
if (result.exitCode == 0) {
print('成功设置Shell脚本执行权限: $shellScriptPath');
} else {
print('设置Shell脚本执行权限失败: ${result.stderr}');
}
} catch (e) {
print('设置Shell脚本权限异常: $e');
}
}
print('脚本文件迁移完成');
}
/// 迁移二进制文件
static Future<void> _migrateBinaries() async {
print('开始迁移二进制文件...');
final supportDir = await getApplicationSupportDirectory();
final binDir = Directory(p.join(supportDir.path, 'bin'));
// 创建bin目录
if (!await binDir.exists()) {
await binDir.create(recursive: true);
}
// macOS 二进制文件
final macBinDir = Directory(p.join(binDir.path, 'macos'));
if (!await macBinDir.exists()) {
await macBinDir.create(recursive: true);
}
await _extractAsset(
'assets/bin/mac/upscayl-bin',
p.join(macBinDir.path, 'upscayl-bin'),
);
// Windows 二进制文件
final winBinDir = Directory(p.join(binDir.path, 'windows'));
if (!await winBinDir.exists()) {
await winBinDir.create(recursive: true);
}
await _extractAsset(
'assets/bin/windows/upscayl-bin.exe',
p.join(winBinDir.path, 'upscayl-bin.exe'),
);
await _extractAsset(
'assets/bin/windows/vcomp140.dll',
p.join(winBinDir.path, 'vcomp140.dll'),
);
await _extractAsset(
'assets/bin/windows/vcomp140d.dll',
p.join(winBinDir.path, 'vcomp140d.dll'),
);
// 设置 macOS/Linux 二进制文件执行权限
if (Platform.isMacOS || Platform.isLinux) {
final binPath = p.join(macBinDir.path, 'upscayl-bin');
try {
final result = await Process.run('chmod', ['+x', binPath]);
if (result.exitCode == 0) {
print('成功设置执行权限: $binPath');
} else {
print('设置执行权限失败: ${result.stderr}');
}
} catch (e) {
print('设置权限异常: $e');
}
}
print('二进制文件迁移完成');
}
/// 迁移模型文件
static Future<void> _migrateModels() async {
print('开始迁移模型文件...');
final supportDir = await getApplicationSupportDirectory();
final modelsDir = Directory(p.join(supportDir.path, 'models'));
// 创建models目录
if (!await modelsDir.exists()) {
await modelsDir.create(recursive: true);
}
// 模型文件列表
final modelFiles = [
'high-fidelity-4x.bin',
'high-fidelity-4x.param',
'digital-art-4x.bin',
'digital-art-4x.param',
'remacri-4x.bin',
'remacri-4x.param',
'ultrasharp-4x.bin',
'ultrasharp-4x.param',
'upscayl-standard-4x.bin',
'upscayl-standard-4x.param',
'ultramix-balanced-4x.bin',
'ultramix-balanced-4x.param',
'upscayl-lite-4x.bin',
'upscayl-lite-4x.param',
];
// 迁移所有模型文件
for (final modelFile in modelFiles) {
try {
await _extractAsset(
'assets/models/$modelFile',
p.join(modelsDir.path, modelFile),
);
} catch (e) {
print('迁移模型文件失败 $modelFile: $e');
// 继续迁移其他模型文件
}
}
print('模型文件迁移完成');
}
/// 获取模型文件目录路径
static Future<String> getModelsPath() async {
final supportDir = await getApplicationSupportDirectory();
return p.join(supportDir.path, 'cup', "model.gltf");
}
/// 获取3D模型文件目录路径
static Future<String> get3DModelsPath() async {
final supportDir = await getApplicationSupportDirectory();
return p.join(supportDir.path, 'cup');
}
static Future<Map<String, dynamic>> readLocalJson() async {
// 获取应用支持目录(默认在 ~/Library/Application Support/
final appSupportDir = await getApplicationSupportDirectory();
final targetDir = Directory('${appSupportDir.path}/cup');
// 检查目录是否存在,若不存在则创建
if (!await targetDir.exists()) {
await targetDir.create(recursive: true);
}
print('file=${targetDir.path}/model.gltf');
// 构建 JSON 文件路径
final file = File('${targetDir.path}/model.gltf');
if (await file.exists()) {
final content = await file.readAsString();
return jsonDecode(content);
} else {
throw Exception('JSON 文件不存在');
}
}
/// 获取特定的3D模型文件路径
static Future<String> get3DModelPath(String filename) async {
final models3dPath = await get3DModelsPath();
return p.join(models3dPath, filename);
}
/// 获取脚本文件路径 (仅适用于macOS/Linux)
static Future<String> getScriptPath() async {
if (Platform.isWindows) {
throw UnsupportedError('Windows平台已使用专门的UpscaleWindowsService不再需要脚本文件');
}
final supportDir = await getApplicationSupportDirectory();
final scriptName = 'upscale_to_8k_single.sh';
final scriptPath = p.join(supportDir.path, scriptName);
final scriptFile = File(scriptPath);
if (!await scriptFile.exists()) {
throw FileSystemException(
'脚本文件不存在: $scriptPath\n请确保应用程序已正确初始化资源',
scriptPath,
);
}
return scriptPath;
}
/// 获取二进制文件路径
static Future<String> getBinaryPath() async {
final supportDir = await getApplicationSupportDirectory();
String platformDir;
String binaryName;
if (Platform.isMacOS) {
platformDir = 'macos';
binaryName = 'upscayl-bin';
} else if (Platform.isWindows) {
platformDir = 'windows';
binaryName = 'upscayl-bin.exe';
} else if (Platform.isLinux) {
platformDir = 'macos'; // Linux 使用 macOS 的二进制文件
binaryName = 'upscayl-bin';
} else {
throw UnsupportedError('不支持的操作系统: ${Platform.operatingSystem}');
}
final binPath = p.join(supportDir.path, 'bin', platformDir, binaryName);
// 检查文件是否存在
final binFile = File(binPath);
if (!await binFile.exists()) {
throw FileSystemException('二进制文件不存在: $binPath\n请确保应用程序已正确初始化资源', binPath);
}
return binPath;
}
/// 设置脚本执行权限
static Future<void> setScriptExecutable(String scriptPath) async {
// 设置脚本执行权限(仅限 macOS/Linux
if (Platform.isMacOS || Platform.isLinux) {
try {
final result = await Process.run('chmod', ['+x', scriptPath]);
if (result.exitCode == 0) {
print('成功设置脚本执行权限: $scriptPath');
} else {
print('设置脚本执行权限失败: ${result.stderr}');
}
} catch (e) {
print('设置脚本权限异常: $e');
}
}
// Windows 不需要设置执行权限
}
}

View File

@@ -0,0 +1,204 @@
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
class ModelManager {
/// 当前应用版本(用于初始化检查)
static String? _currentVersion;
/// 获取当前应用版本
static Future<String> _getCurrentVersion() async {
if (_currentVersion == null) {
final packageInfo = await PackageInfo.fromPlatform();
_currentVersion = '${packageInfo.version}+${packageInfo.buildNumber}';
}
return _currentVersion!;
}
/// 检查3D模型资源是否已完成初始化基于版本号
static Future<bool> isInitialized() async {
final supportDir = await getApplicationSupportDirectory();
final currentVersion = await _getCurrentVersion();
final initMarker = File(
p.join(supportDir.path, '3d_models', '.initialized_$currentVersion'),
);
return await initMarker.exists();
}
/// 标记3D模型资源初始化完成
static Future<void> markInitialized() async {
final supportDir = await getApplicationSupportDirectory();
final currentVersion = await _getCurrentVersion();
final modelsDir = Directory(p.join(supportDir.path, '3d_models'));
if (!await modelsDir.exists()) {
await modelsDir.create(recursive: true);
}
final initMarker = File(
p.join(modelsDir.path, '.initialized_$currentVersion'),
);
await initMarker.writeAsString('${DateTime.now().toIso8601String()}\n');
}
/// 从assets目录提取文件到指定路径
static Future<void> _extractAsset(String assetPath, String localPath) async {
final localFile = File(localPath);
final localDir = Directory(p.dirname(localPath));
// 确保目录存在
if (!await localDir.exists()) {
await localDir.create(recursive: true);
}
try {
// 从assets中读取数据
final ByteData data = await rootBundle.load(assetPath);
final List<int> bytes = data.buffer.asUint8List();
// 写入本地文件
await localFile.writeAsBytes(bytes);
print('成功提取3D模型文件: $assetPath -> $localPath');
} catch (e) {
print('提取3D模型资源文件失败 $assetPath: $e');
rethrow;
}
}
/// 初始化3D模型资源
static Future<void> initializeAppResources() async {
final currentVersion = await _getCurrentVersion();
if (await isInitialized()) {
print('3D模型资源已初始化当前版本: $currentVersion');
return;
}
try {
// 迁移模型文件
await _migrateModels();
// 标记初始化完成
await markInitialized();
print('3D模型资源初始化完成');
} catch (e) {
print('3D模型资源初始化失败: $e');
rethrow;
}
}
/// 迁移3D模型文件
/// 迁移3D模型文件
static Future<void> _migrateModels() async {
print('开始迁移3D模型文件...');
final supportDir = await getApplicationSupportDirectory();
final modelsDir = Directory(p.join(supportDir.path, 'models'));
// 创建3D模型目录
if (!await modelsDir.exists()) {
await modelsDir.create(recursive: true);
}
// 创建cup子目录
final cupDir = Directory(p.join(modelsDir.path, 'cup'));
if (!await cupDir.exists()) {
await cupDir.create(recursive: true);
}
// 3D模型文件列表 - 修正路径
final modelFiles = [
'cup/model.gltf',
'cup/model.bin',
'cup/texture.jpg',
];
// 迁移所有3D模型文件
for (final modelFile in modelFiles) {
try {
// 确保源文件路径正确
final assetPath = 'assets/$modelFile';
// 检查资源是否存在
try {
await rootBundle.load(assetPath);
} catch (e) {
print('资源文件不存在: $assetPath');
continue;
}
await _extractAsset(
assetPath,
p.join(modelsDir.path, modelFile),
);
} catch (e) {
print('迁移3D模型文件失败 $modelFile: $e');
// 继续迁移其他模型文件
}
}
print('3D模型文件迁移完成');
}
/// 获取3D模型文件目录路径
static Future<String> getModelsPath() async {
final supportDir = await getApplicationSupportDirectory();
return p.join(supportDir.path, 'models/cup/');
}
/// 获取特定3D模型文件的路径
static Future<String> getModelFilePath(String relativePath) async {
final modelsPath = await getModelsPath();
final filePath = p.join(modelsPath, relativePath);
final file = File(filePath);
if (!await file.exists()) {
throw FileSystemException(
'3D模型文件不存在: $filePath\n请确保应用程序已正确初始化3D模型资源',
filePath,
);
}
return filePath;
}
/// 获取GLTF模型文件路径
static Future<String> getGltfModelPath() async {
return getModelFilePath('cup/model.gltf');
}
/// 获取BIN模型文件路径
static Future<String> getBinModelPath() async {
return getModelFilePath('cup/model.bin');
}
/// 获取纹理文件路径
static Future<String> getTexturePath() async {
return getModelFilePath('cup/texture.jpg');
}
/// 更新纹理文件
static Future<void> updateTexture(File newTextureFile) async {
try {
final texturePath = await getTexturePath();
final textureFile = File(texturePath);
// 删除旧纹理文件
if (await textureFile.exists()) {
await textureFile.delete();
}
// 复制新纹理文件
await newTextureFile.copy(texturePath);
print('纹理文件更新成功: $texturePath');
} catch (e) {
print('更新纹理文件失败: $e');
rethrow;
}
}
}

View File

@@ -5,8 +5,14 @@
import FlutterMacOS
import Foundation
import file_picker
import package_info_plus
import path_provider_foundation
import thermion_flutter
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SwiftThermionFlutterPlugin.register(with: registry.registrar(forPlugin: "SwiftThermionFlutterPlugin"))
}

View File

@@ -1,22 +1,41 @@
PODS:
- file_picker (0.0.1):
- FlutterMacOS
- FlutterMacOS (1.0.0)
- package_info_plus (0.0.1):
- FlutterMacOS
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- thermion_flutter (0.0.1):
- FlutterMacOS
DEPENDENCIES:
- file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- thermion_flutter (from `Flutter/ephemeral/.symlinks/plugins/thermion_flutter/macos`)
EXTERNAL SOURCES:
file_picker:
:path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos
FlutterMacOS:
:path: Flutter/ephemeral
package_info_plus:
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
path_provider_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
thermion_flutter:
:path: Flutter/ephemeral/.symlinks/plugins/thermion_flutter/macos
SPEC CHECKSUMS:
file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
thermion_flutter: debb51a861788780ce40e11e4400c2e5a8681fdf
package_info_plus: a8a591e70e87ce97ce5d21b2594f69cea9e0312f
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
thermion_flutter: d5d52ef933321eedbee6c4ddda4259c8cbcc84ed
PODFILE CHECKSUM: 1888651be91a8ad58692c1add9ce24279fd4e950
COCOAPODS: 1.15.2
COCOAPODS: 1.16.2

View File

@@ -3,9 +3,19 @@
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<false/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>com.apple.security.files.pictures.read-write</key>
<true/>
<key>com.apple.security.files.bookmarks.app-scope</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>

View File

@@ -3,6 +3,20 @@
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<false/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>com.apple.security.files.pictures.read-write</key>
<true/>
<key>com.apple.security.files.bookmarks.app-scope</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>

View File

@@ -16,6 +16,12 @@ dependencies:
win32: 5.5.1
web: 1.0.0
thermion_flutter: ^0.2.1-dev.9
file_picker: ^8.3.2
path_provider: ^2.1.5
package_info_plus: ^8.0.2
path: ^1.8.0
dev_dependencies:
flutter_test:
@@ -40,3 +46,9 @@ flutter:
- assets/
- assets/BusterDrone/
- assets/BusterDrone/textures/
- assets/cup/
- assets/image/
- assets/bin/mac/
- assets/bin/windows/
- assets/models/
- assets/upscale_to_8k_single.sh

View File

@@ -0,0 +1,103 @@
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:logging/logging.dart';
import 'package:flutter/material.dart';
import 'package:thermion_flutter/thermion_flutter.dart';
void main() {
runApp(const MyApp());
Logger.root.onRecord.listen((record) {
print(record);
});
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Thermion Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Thermion 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> {
late DelegateInputHandler _fixedOrbitInputHandler;
late DelegateInputHandler _freeFlightInputHandler;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
_thermionViewer = await ThermionFlutterPlugin.createViewer();
var assetData = await rootBundle.load("assets/cube.glb");
var asset =
await _thermionViewer!.loadGltfFromBuffer(assetData.buffer.asUint8List(assetData.offsetInBytes), keepData: true, loadResourcesAsync: true);
await _thermionViewer!.loadSkybox("assets/default_env_skybox.ktx");
await _thermionViewer!.loadIbl("assets/default_env_ibl.ktx");
await _thermionViewer!.setPostProcessing(true);
await _thermionViewer!.setRendering(true);
_fixedOrbitInputHandler =
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);
_freeFlightInputHandler =
DelegateInputHandler.flight(_thermionViewer!)
..setActionForType(InputType.MMB_HOLD_AND_MOVE, InputAction.ROTATE)
..setActionForType(InputType.SCALE1, InputAction.ROTATE)
..setActionForType(InputType.SCALE2, InputAction.ZOOM)
..setActionForType(InputType.SCROLLWHEEL, InputAction.ZOOM);
setState(() {});
});
}
ThermionViewer? _thermionViewer;
bool isOrbit = true;
@override
Widget build(BuildContext context) {
return Stack(children: [
if (_thermionViewer != null) ...[
Positioned.fill(
child: ThermionListenerWidget(
inputHandler: isOrbit
? _fixedOrbitInputHandler : _freeFlightInputHandler,
child:ThermionWidget(
viewer: _thermionViewer!,
))),
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
onPressed: () {
isOrbit = !isOrbit;
setState(() {});
},
child: Text("Switch to ${isOrbit ? "Free Flight" : "Orbit"}"))
],
)
],
]);
}
}

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

@@ -108,10 +108,13 @@ fragment {
1.0
);
color.rgb = (axes.x < 1e-8) ? color.rgb : AXIS_COLOR_X;
color.rgb = (axes.z < 1e-8) ? color.rgb : AXIS_COLOR_Z;
material.baseColor = color * gridAlpha;
if(axes.x > 1e-8) {
material.baseColor = vec4(AXIS_COLOR_X, 1.0);
} else if(axes.z > 1e-8) {
material.baseColor = vec4(AXIS_COLOR_Z, 1.0);
} else {
material.baseColor = color * gridAlpha;
}
}
}

View File

@@ -1,4 +1,8 @@
name: thermion_workspace
packages:
- thermion_dart
- thermion_flutter/**
- thermion_flutter/thermion_flutter_platform_interface
- thermion_flutter/thermion_flutter_method_channel
- thermion_flutter/thermion_flutter_web
- thermion_flutter/thermion_flutter

View File

@@ -1,3 +1,26 @@
## 0.3.3
- Bump "thermion_dart" to `0.3.3`.
## 0.3.3-pre
- **FIX**: fix Windows build.dart.
- **FIX**: add nan/negative checks inside setLensProjection.
## 0.3.2
- Bump "thermion_dart" to `0.3.2`.
## 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

@@ -1,10 +1,11 @@
import 'dart:io';
import 'package:archive/archive.dart';
import 'package:code_assets/code_assets.dart';
import 'package:hooks/hooks.dart';
import 'package:native_toolchain_c/native_toolchain_c.dart';
import 'package:logging/logging.dart';
import 'package:native_toolchain_c/native_toolchain_c.dart';
import 'package:path/path.dart' as path;
void main(List<String> args) async {
@@ -130,7 +131,7 @@ void main(List<String> args) async {
var frameworks = [];
if (platform != "windows") {
if (targetOS != OS.windows) {
flags.addAll(['-std=c++17']);
} else {
defines["WIN32"] = "1";
@@ -193,7 +194,7 @@ void main(List<String> args) async {
name: packageName,
language: Language.cpp,
assetName: 'thermion_dart.dart',
sources: platform == "windows" ? [] : sources,
sources: targetOS == OS.windows ? [] : sources,
includes: platform == "windows"
? []
: ['native/include', 'native/include/filament'],
@@ -219,12 +220,12 @@ void main(List<String> args) async {
"/I${path.join(pkgRootFilePath, "native", "include", "windows", "vulkan")}",
"@${srcs.uri.toFilePath(windows: true)}",
// ...sources,
'/link',
"/LIBPATH:$libDir",
'/DLL',
// '/link',
// "/LIBPATH:$libDir",
// '/DLL',
]
],
dartBuildFiles: ['hook/build.dart'],
libraryDirectories: [libDir],
);
await cbuilder.run(

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

@@ -5,11 +5,10 @@ import '../../../utils/src/matrix.dart';
class FFICamera extends Camera<Pointer<TCamera>> {
final Pointer<TCamera> camera;
@override
Pointer<TCamera> getNativeHandle() {
return camera;
}
final FFIFilamentApp app;
@@ -92,6 +91,16 @@ class FFICamera extends Camera<Pointer<TCamera>> {
double far = kFar,
double aspect = 1.0,
double focalLength = kFocalLength}) async {
if (near.isNaN ||
far.isNaN ||
aspect.isNaN ||
focalLength.isNaN ||
near.isNegative ||
far.isNegative ||
aspect.isNegative ||
focalLength.isNegative) {
throw FormatException();
}
Camera_setLensProjection(camera, near, far, aspect, focalLength);
}

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);
@@ -343,13 +344,14 @@ abstract class ThermionViewer {
int getCameraCount();
///
/// Adds the asset to the scene, meaning the asset will be rendered/visible.
/// Adds the asset to the scene. All renderable entities attached to
/// the asset will be visible.
///
Future addToScene(covariant ThermionAsset asset);
///
/// Removes the asset from the scene, meaning the asset will not be rendered/visible.
/// The asset itself will remain valid.
/// Removes the asset from the scene. None of the renderable entities
/// attached to the asset will be visible, but the asset itself remains valid.
///
Future removeFromScene(covariant ThermionAsset asset);
}

View File

@@ -25,7 +25,6 @@
#pragma comment(lib, "dracodec.lib")
#pragma comment(lib, "ibl.lib")
#pragma comment(lib, "ktxreader.lib")
#pragma comment(lib, "png.lib")
#pragma comment(lib, "z.lib")
#pragma comment(lib, "stb.lib")
#pragma comment(lib, "uberzlib.lib")

View File

@@ -1,5 +1,9 @@
#pragma once
#ifdef _WIN32
#include "ThermionWin32.h"
#endif
#ifdef __cplusplus
namespace thermion {
extern "C"
@@ -16,35 +20,35 @@ enum TProjection {
typedef enum TProjection TProjection;
// Camera methods
void Camera_setExposure(TCamera *camera, float aperture, float shutterSpeed, float sensitivity);
double4x4 Camera_getModelMatrix(TCamera *const camera);
double4x4 Camera_getViewMatrix(TCamera *const camera);
double4x4 Camera_getProjectionMatrix(TCamera *const camera);
double4x4 Camera_getCullingProjectionMatrix(TCamera *const camera);
void Camera_getFrustum(TCamera *camera, double* out);
void Camera_setProjectionMatrix(TCamera *camera, double *matrix, double near, double far);
void Camera_setProjectionFromFov(TCamera *camera, double fovInDegrees, double aspect, double near, double far, bool horizontal);
double Camera_getFocalLength(TCamera *const camera);
double4x4 Camera_getViewMatrix(TCamera *const camera);
double4x4 Camera_getModelMatrix(TCamera* camera);
void Camera_lookAt(TCamera* camera, double3 eye, double3 focus, double3 up);
EMSCRIPTEN_KEEPALIVE void Camera_setExposure(TCamera *camera, float aperture, float shutterSpeed, float sensitivity);
EMSCRIPTEN_KEEPALIVE double4x4 Camera_getModelMatrix(TCamera *const camera);
EMSCRIPTEN_KEEPALIVE double4x4 Camera_getViewMatrix(TCamera *const camera);
EMSCRIPTEN_KEEPALIVE double4x4 Camera_getProjectionMatrix(TCamera *const camera);
EMSCRIPTEN_KEEPALIVE double4x4 Camera_getCullingProjectionMatrix(TCamera *const camera);
EMSCRIPTEN_KEEPALIVE void Camera_getFrustum(TCamera *camera, double* out);
EMSCRIPTEN_KEEPALIVE void Camera_setProjectionMatrix(TCamera *camera, double *matrix, double near, double far);
EMSCRIPTEN_KEEPALIVE void Camera_setProjectionFromFov(TCamera *camera, double fovInDegrees, double aspect, double near, double far, bool horizontal);
EMSCRIPTEN_KEEPALIVE double Camera_getFocalLength(TCamera *const camera);
EMSCRIPTEN_KEEPALIVE double4x4 Camera_getViewMatrix(TCamera *const camera);
EMSCRIPTEN_KEEPALIVE double4x4 Camera_getModelMatrix(TCamera* camera);
EMSCRIPTEN_KEEPALIVE void Camera_lookAt(TCamera* camera, double3 eye, double3 focus, double3 up);
double Camera_getNear(TCamera *camera);
double Camera_getCullingFar(TCamera *camera);
float Camera_getFov(TCamera *camera, bool horizontal);
double Camera_getFocusDistance(TCamera *camera);
void Camera_setFocusDistance(TCamera *camera, float focusDistance);
EMSCRIPTEN_KEEPALIVE double Camera_getNear(TCamera *camera);
EMSCRIPTEN_KEEPALIVE double Camera_getCullingFar(TCamera *camera);
EMSCRIPTEN_KEEPALIVE float Camera_getFov(TCamera *camera, bool horizontal);
EMSCRIPTEN_KEEPALIVE double Camera_getFocusDistance(TCamera *camera);
EMSCRIPTEN_KEEPALIVE void Camera_setFocusDistance(TCamera *camera, float focusDistance);
void Camera_setCustomProjectionWithCulling(
EMSCRIPTEN_KEEPALIVE void Camera_setCustomProjectionWithCulling(
TCamera* camera,
double4x4 projectionMatrix,
double near,
double far
);
void Camera_setModelMatrix(TCamera* camera, double *tModelMatrix);
void Camera_setLensProjection(TCamera *camera, double near, double far, double aspect, double focalLength);
EntityId Camera_getEntity(TCamera* camera);
void Camera_setProjection(TCamera *const tCamera, TProjection projection, double left, double right,
EMSCRIPTEN_KEEPALIVE void Camera_setModelMatrix(TCamera* camera, double *tModelMatrix);
EMSCRIPTEN_KEEPALIVE void Camera_setLensProjection(TCamera *camera, double near, double far, double aspect, double focalLength);
EMSCRIPTEN_KEEPALIVE EntityId Camera_getEntity(TCamera* camera);
EMSCRIPTEN_KEEPALIVE void Camera_setProjection(TCamera *const tCamera, TProjection projection, double left, double right,
double bottom, double top,
double near, double far);

View File

@@ -16,16 +16,16 @@ namespace thermion
typedef int32_t EntityId;
typedef void (*FilamentRenderCallback)(void *const owner);
void RenderThread_create();
void RenderThread_destroy();
void RenderThread_requestFrameAsync();
void RenderThread_setRenderTicker(TRenderTicker *tRenderTicker);
void RenderThread_addTask(void (*task)());
EMSCRIPTEN_KEEPALIVE void RenderThread_create();
EMSCRIPTEN_KEEPALIVE void RenderThread_destroy();
EMSCRIPTEN_KEEPALIVE void RenderThread_requestFrameAsync();
EMSCRIPTEN_KEEPALIVE void RenderThread_setRenderTicker(TRenderTicker *tRenderTicker);
EMSCRIPTEN_KEEPALIVE void RenderThread_addTask(void (*task)());
void RenderTicker_renderRenderThread(TRenderTicker *tRenderTicker, uint64_t frameTimeInNanos, uint32_t requestId, VoidCallback onComplete);
void AnimationManager_createRenderThread(TEngine *tEngine, TScene *tScene, void (*onComplete)(TAnimationManager *));
EMSCRIPTEN_KEEPALIVE void RenderTicker_renderRenderThread(TRenderTicker *tRenderTicker, uint64_t frameTimeInNanos, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void AnimationManager_createRenderThread(TEngine *tEngine, TScene *tScene, void (*onComplete)(TAnimationManager *));
void Engine_createRenderThread(
EMSCRIPTEN_KEEPALIVE void Engine_createRenderThread(
TBackend backend,
void* platform,
void* sharedContext,
@@ -33,22 +33,22 @@ namespace thermion
bool disableHandleUseAfterFreeCheck,
void (*onComplete)(TEngine *)
);
void Engine_createRendererRenderThread(TEngine *tEngine, void (*onComplete)(TRenderer *));
void Engine_createSwapChainRenderThread(TEngine *tEngine, void *window, uint64_t flags, void (*onComplete)(TSwapChain *));
void Engine_createHeadlessSwapChainRenderThread(TEngine *tEngine, uint32_t width, uint32_t height, uint64_t flags, void (*onComplete)(TSwapChain *));
void Engine_createCameraRenderThread(TEngine* tEngine, void (*onComplete)(TCamera *));
void Engine_createViewRenderThread(TEngine *tEngine, void (*onComplete)(TView *));
void Engine_buildMaterialRenderThread(TEngine *tEngine, const uint8_t *materialData, size_t length, void (*onComplete)(TMaterial *));
void Engine_destroyRenderThread(TEngine *tEngine, uint32_t requestId, VoidCallback onComplete);
void Engine_destroySwapChainRenderThread(TEngine *tEngine, TSwapChain *tSwapChain, uint32_t requestId, VoidCallback onComplete);
void Engine_destroyViewRenderThread(TEngine *tEngine, TView *tView, uint32_t requestId, VoidCallback onComplete);
void Engine_destroySceneRenderThread(TEngine *tEngine, TScene *tScene, uint32_t requestId, VoidCallback onComplete);
void Engine_destroyColorGradingRenderThread(TEngine *tEngine, TColorGrading *tColorGrading, uint32_t requestId, VoidCallback onComplete);
void Engine_destroyMaterialRenderThread(TEngine *tEngine, TMaterial *tMaterial, uint32_t requestId, VoidCallback onComplete);
void Engine_destroyMaterialInstanceRenderThread(TEngine *tEngine, TMaterialInstance *tMaterialInstance, uint32_t requestId, VoidCallback onComplete);
void Engine_destroySkyboxRenderThread(TEngine *tEngine, TSkybox *tSkybox, uint32_t requestId, VoidCallback onComplete);
void Engine_destroyIndirectLightRenderThread(TEngine *tEngine, TIndirectLight *tIndirectLight, uint32_t requestId, VoidCallback onComplete);
void Texture_buildRenderThread(TEngine *engine,
EMSCRIPTEN_KEEPALIVE void Engine_createRendererRenderThread(TEngine *tEngine, void (*onComplete)(TRenderer *));
EMSCRIPTEN_KEEPALIVE void Engine_createSwapChainRenderThread(TEngine *tEngine, void *window, uint64_t flags, void (*onComplete)(TSwapChain *));
EMSCRIPTEN_KEEPALIVE void Engine_createHeadlessSwapChainRenderThread(TEngine *tEngine, uint32_t width, uint32_t height, uint64_t flags, void (*onComplete)(TSwapChain *));
EMSCRIPTEN_KEEPALIVE void Engine_createCameraRenderThread(TEngine* tEngine, void (*onComplete)(TCamera *));
EMSCRIPTEN_KEEPALIVE void Engine_createViewRenderThread(TEngine *tEngine, void (*onComplete)(TView *));
EMSCRIPTEN_KEEPALIVE void Engine_buildMaterialRenderThread(TEngine *tEngine, const uint8_t *materialData, size_t length, void (*onComplete)(TMaterial *));
EMSCRIPTEN_KEEPALIVE void Engine_destroyRenderThread(TEngine *tEngine, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Engine_destroySwapChainRenderThread(TEngine *tEngine, TSwapChain *tSwapChain, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Engine_destroyViewRenderThread(TEngine *tEngine, TView *tView, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Engine_destroySceneRenderThread(TEngine *tEngine, TScene *tScene, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Engine_destroyColorGradingRenderThread(TEngine *tEngine, TColorGrading *tColorGrading, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Engine_destroyMaterialRenderThread(TEngine *tEngine, TMaterial *tMaterial, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Engine_destroyMaterialInstanceRenderThread(TEngine *tEngine, TMaterialInstance *tMaterialInstance, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Engine_destroySkyboxRenderThread(TEngine *tEngine, TSkybox *tSkybox, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Engine_destroyIndirectLightRenderThread(TEngine *tEngine, TIndirectLight *tIndirectLight, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Texture_buildRenderThread(TEngine *engine,
uint32_t width,
uint32_t height,
uint32_t depth,
@@ -59,25 +59,25 @@ namespace thermion
TTextureFormat format,
void (*onComplete)(TTexture*)
);
void Texture_generateMipMapsRenderThread(TTexture *tTexture, TEngine *tEngine, uint32_t requestId, VoidCallback onComplete);
void Ktx1Reader_createTextureRenderThread(TEngine *tEngine, TKtx1Bundle *tBundle, uint32_t requestId, VoidCallback onTextureUploadComplete, void (*onComplete)(TTexture *));
EMSCRIPTEN_KEEPALIVE void Texture_generateMipMapsRenderThread(TTexture *tTexture, TEngine *tEngine, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Ktx1Reader_createTextureRenderThread(TEngine *tEngine, TKtx1Bundle *tBundle, uint32_t requestId, VoidCallback onTextureUploadComplete, void (*onComplete)(TTexture *));
void Engine_destroyTextureRenderThread(TEngine *engine, TTexture* tTexture, uint32_t requestId, VoidCallback onComplete);
void Engine_createFenceRenderThread(TEngine *tEngine, void (*onComplete)(TFence*));
void Fence_waitAndDestroyRenderThread(TFence *tFence, uint32_t requestId, VoidCallback onComplete);
void Engine_destroyFenceRenderThread(TEngine *tEngine, TFence *tFence, uint32_t requestId, VoidCallback onComplete);
void Engine_flushAndWaitRenderThread(TEngine *tEngine, uint32_t requestId, VoidCallback onComplete);
void Engine_executeRenderThread(TEngine *tEngine, uint32_t requestId, VoidCallback onComplete);
void Engine_buildSkyboxRenderThread(TEngine *tEngine, TTexture *tTexture, void (*onComplete)(TSkybox *));
void Engine_buildIndirectLightFromIrradianceTextureRenderThread(TEngine *tEngine, TTexture *tReflectionsTexture, TTexture* tIrradianceTexture, float intensity, void (*onComplete)(TIndirectLight *));
void Engine_buildIndirectLightFromIrradianceHarmonicsRenderThread(TEngine *tEngine, TTexture *tReflectionsTexture, float *harmonics, float intensity, void (*onComplete)(TIndirectLight *));
EMSCRIPTEN_KEEPALIVE void Engine_destroyTextureRenderThread(TEngine *engine, TTexture* tTexture, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Engine_createFenceRenderThread(TEngine *tEngine, void (*onComplete)(TFence*));
EMSCRIPTEN_KEEPALIVE void Fence_waitAndDestroyRenderThread(TFence *tFence, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Engine_destroyFenceRenderThread(TEngine *tEngine, TFence *tFence, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Engine_flushAndWaitRenderThread(TEngine *tEngine, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Engine_executeRenderThread(TEngine *tEngine, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Engine_buildSkyboxRenderThread(TEngine *tEngine, TTexture *tTexture, void (*onComplete)(TSkybox *));
EMSCRIPTEN_KEEPALIVE void Engine_buildIndirectLightFromIrradianceTextureRenderThread(TEngine *tEngine, TTexture *tReflectionsTexture, TTexture* tIrradianceTexture, float intensity, void (*onComplete)(TIndirectLight *));
EMSCRIPTEN_KEEPALIVE void Engine_buildIndirectLightFromIrradianceHarmonicsRenderThread(TEngine *tEngine, TTexture *tReflectionsTexture, float *harmonics, float intensity, void (*onComplete)(TIndirectLight *));
void Renderer_setClearOptionsRenderThread(TRenderer *tRenderer, double clearR, double clearG, double clearB, double clearA, uint8_t clearStencil, bool clear, bool discard, uint32_t requestId, VoidCallback onComplete);
void Renderer_beginFrameRenderThread(TRenderer *tRenderer, TSwapChain *tSwapChain, uint64_t frameTimeInNanos, void (*onComplete)(bool));
void Renderer_endFrameRenderThread(TRenderer *tRenderer, uint32_t requestId, VoidCallback onComplete);
void Renderer_renderRenderThread(TRenderer *tRenderer, TView *tView, uint32_t requestId, VoidCallback onComplete);
void Renderer_renderStandaloneViewRenderThread(TRenderer *tRenderer, TView *tView, uint32_t requestId, VoidCallback onComplete);
void Renderer_readPixelsRenderThread(
EMSCRIPTEN_KEEPALIVE void Renderer_setClearOptionsRenderThread(TRenderer *tRenderer, double clearR, double clearG, double clearB, double clearA, uint8_t clearStencil, bool clear, bool discard, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Renderer_beginFrameRenderThread(TRenderer *tRenderer, TSwapChain *tSwapChain, uint64_t frameTimeInNanos, void (*onComplete)(bool));
EMSCRIPTEN_KEEPALIVE void Renderer_endFrameRenderThread(TRenderer *tRenderer, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Renderer_renderRenderThread(TRenderer *tRenderer, TView *tView, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Renderer_renderStandaloneViewRenderThread(TRenderer *tRenderer, TView *tView, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Renderer_readPixelsRenderThread(
TRenderer *tRenderer,
uint32_t width, uint32_t height, uint32_t xOffset, uint32_t yOffset,
TRenderTarget *tRenderTarget,
@@ -87,29 +87,29 @@ namespace thermion
size_t outLength,
uint32_t requestId, VoidCallback onComplete);
void Material_createInstanceRenderThread(TMaterial *tMaterial, void (*onComplete)(TMaterialInstance *));
void Material_createImageMaterialRenderThread(TEngine *tEngine, void (*onComplete)(TMaterial *));
void Material_createGizmoMaterialRenderThread(TEngine *tEngine, void (*onComplete)(TMaterial *));
void Material_createOutlineMaterialRenderThread(TEngine *tEngine, void (*onComplete)(TMaterial *));
EMSCRIPTEN_KEEPALIVE void Material_createInstanceRenderThread(TMaterial *tMaterial, void (*onComplete)(TMaterialInstance *));
EMSCRIPTEN_KEEPALIVE void Material_createImageMaterialRenderThread(TEngine *tEngine, void (*onComplete)(TMaterial *));
EMSCRIPTEN_KEEPALIVE void Material_createGizmoMaterialRenderThread(TEngine *tEngine, void (*onComplete)(TMaterial *));
EMSCRIPTEN_KEEPALIVE void Material_createOutlineMaterialRenderThread(TEngine *tEngine, void (*onComplete)(TMaterial *));
void ColorGrading_createRenderThread(TEngine *tEngine, TToneMapping toneMapping, void (*callback)(TColorGrading *));
void View_pickRenderThread(TView *tView, uint32_t requestId, uint32_t x, uint32_t y, PickCallback callback);
void View_setColorGradingRenderThread(TView *tView, TColorGrading *tColorGrading, uint32_t requestId, VoidCallback onComplete);
void View_setBloomRenderThread(TView *tView, bool enabled, double strength, uint32_t requestId, VoidCallback onComplete);
void View_setCameraRenderThread(TView *tView, TCamera *tCamera, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void ColorGrading_createRenderThread(TEngine *tEngine, TToneMapping toneMapping, void (*callback)(TColorGrading *));
EMSCRIPTEN_KEEPALIVE void View_pickRenderThread(TView *tView, uint32_t requestId, uint32_t x, uint32_t y, PickCallback callback);
EMSCRIPTEN_KEEPALIVE void View_setColorGradingRenderThread(TView *tView, TColorGrading *tColorGrading, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void View_setBloomRenderThread(TView *tView, bool enabled, double strength, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void View_setCameraRenderThread(TView *tView, TCamera *tCamera, uint32_t requestId, VoidCallback onComplete);
void SceneAsset_createGridRenderThread(TEngine *tEngine, TMaterial * tMaterial, void (*callback)(TSceneAsset *));
EMSCRIPTEN_KEEPALIVE void SceneAsset_createGridRenderThread(TEngine *tEngine, TMaterial * tMaterial, void (*callback)(TSceneAsset *));
void SceneAsset_destroyRenderThread(TSceneAsset *tSceneAsset, uint32_t requestId, VoidCallback onComplete);
void SceneAsset_createFromFilamentAssetRenderThread(
EMSCRIPTEN_KEEPALIVE void SceneAsset_destroyRenderThread(TSceneAsset *tSceneAsset, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void SceneAsset_createFromFilamentAssetRenderThread(
TEngine *tEngine,
TGltfAssetLoader *tAssetLoader,
TNameComponentManager *tNameComponentManager,
TFilamentAsset *tFilamentAsset,
void (*onComplete)(TSceneAsset *)
);
void SceneAsset_createInstanceRenderThread(TSceneAsset *asset, TMaterialInstance **tMaterialInstances, int materialInstanceCount, void (*callback)(TSceneAsset *));
void SceneAsset_createGeometryRenderThread(
EMSCRIPTEN_KEEPALIVE void SceneAsset_createInstanceRenderThread(TSceneAsset *asset, TMaterialInstance **tMaterialInstances, int materialInstanceCount, void (*callback)(TSceneAsset *));
EMSCRIPTEN_KEEPALIVE void SceneAsset_createGeometryRenderThread(
TEngine *tEngine,
float *vertices,
uint32_t numVertices,
@@ -124,7 +124,7 @@ namespace thermion
int materialInstanceCount,
void (*callback)(TSceneAsset *)
);
void MaterialProvider_createMaterialInstanceRenderThread(
EMSCRIPTEN_KEEPALIVE void MaterialProvider_createMaterialInstanceRenderThread(
TMaterialProvider *tMaterialProvider,
bool doubleSided,
bool unlit,
@@ -166,12 +166,12 @@ namespace thermion
bool hasVolume,
void (*callback)(TMaterialInstance *));
void AnimationManager_updateBoneMatricesRenderThread(
EMSCRIPTEN_KEEPALIVE void AnimationManager_updateBoneMatricesRenderThread(
TAnimationManager *tAnimationManager,
TSceneAsset *sceneAsset,
void (*callback)(bool));
void AnimationManager_setMorphTargetWeightsRenderThread(
EMSCRIPTEN_KEEPALIVE void AnimationManager_setMorphTargetWeightsRenderThread(
TAnimationManager *tAnimationManager,
EntityId entityId,
const float *const morphData,
@@ -179,16 +179,16 @@ namespace thermion
void (*callback)(bool));
// Image methods
void Image_createEmptyRenderThread(uint32_t width, uint32_t height, uint32_t channel, void (*onComplete)(TLinearImage *));
void Image_decodeRenderThread(uint8_t* data, size_t length, const char* name, bool alpha, void (*onComplete)(TLinearImage *));
void Image_getBytesRenderThread(TLinearImage *tLinearImage, void (*onComplete)(float *));
void Image_destroyRenderThread(TLinearImage *tLinearImage, uint32_t requestId, VoidCallback onComplete);
void Image_getWidthRenderThread(TLinearImage *tLinearImage, void (*onComplete)(uint32_t));
void Image_getHeightRenderThread(TLinearImage *tLinearImage, void (*onComplete)(uint32_t));
void Image_getChannelsRenderThread(TLinearImage *tLinearImage, void (*onComplete)(uint32_t));
EMSCRIPTEN_KEEPALIVE void Image_createEmptyRenderThread(uint32_t width, uint32_t height, uint32_t channel, void (*onComplete)(TLinearImage *));
EMSCRIPTEN_KEEPALIVE void Image_decodeRenderThread(uint8_t* data, size_t length, const char* name, bool alpha, void (*onComplete)(TLinearImage *));
EMSCRIPTEN_KEEPALIVE void Image_getBytesRenderThread(TLinearImage *tLinearImage, void (*onComplete)(float *));
EMSCRIPTEN_KEEPALIVE void Image_destroyRenderThread(TLinearImage *tLinearImage, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Image_getWidthRenderThread(TLinearImage *tLinearImage, void (*onComplete)(uint32_t));
EMSCRIPTEN_KEEPALIVE void Image_getHeightRenderThread(TLinearImage *tLinearImage, void (*onComplete)(uint32_t));
EMSCRIPTEN_KEEPALIVE void Image_getChannelsRenderThread(TLinearImage *tLinearImage, void (*onComplete)(uint32_t));
void Texture_loadImageRenderThread(
EMSCRIPTEN_KEEPALIVE void Texture_loadImageRenderThread(
TEngine *tEngine,
TTexture *tTexture,
TLinearImage *tImage,
@@ -197,7 +197,7 @@ namespace thermion
int level,
void (*onComplete)(bool)
);
void Texture_setImageRenderThread(
EMSCRIPTEN_KEEPALIVE void Texture_setImageRenderThread(
TEngine *tEngine,
TTexture *tTexture,
uint32_t level,
@@ -213,8 +213,8 @@ namespace thermion
uint32_t pixelDataType,
void (*onComplete)(bool)
);
void RenderTarget_getColorTextureRenderThread(TRenderTarget *tRenderTarget, void (*onComplete)(TTexture *));
void RenderTarget_createRenderThread(
EMSCRIPTEN_KEEPALIVE void RenderTarget_getColorTextureRenderThread(TRenderTarget *tRenderTarget, void (*onComplete)(TTexture *));
EMSCRIPTEN_KEEPALIVE void RenderTarget_createRenderThread(
TEngine *tEngine,
uint32_t width,
uint32_t height,
@@ -222,7 +222,7 @@ namespace thermion
TTexture *depth,
void (*onComplete)(TRenderTarget *)
);
void RenderTarget_destroyRenderThread(
EMSCRIPTEN_KEEPALIVE void RenderTarget_destroyRenderThread(
TEngine *tEngine,
TRenderTarget *tRenderTarget,
uint32_t requestId, VoidCallback onComplete
@@ -230,8 +230,8 @@ namespace thermion
// TextureSampler methods
void TextureSampler_createRenderThread(void (*onComplete)(TTextureSampler*));
void TextureSampler_createWithFilteringRenderThread(
EMSCRIPTEN_KEEPALIVE void TextureSampler_createRenderThread(void (*onComplete)(TTextureSampler*));
EMSCRIPTEN_KEEPALIVE void TextureSampler_createWithFilteringRenderThread(
TSamplerMinFilter minFilter,
TSamplerMagFilter magFilter,
TSamplerWrapMode wrapS,
@@ -239,53 +239,53 @@ namespace thermion
TSamplerWrapMode wrapR,
void (*onComplete)(TTextureSampler*)
);
void TextureSampler_createWithComparisonRenderThread(
EMSCRIPTEN_KEEPALIVE void TextureSampler_createWithComparisonRenderThread(
TSamplerCompareMode compareMode,
TSamplerCompareFunc compareFunc,
void (*onComplete)(TTextureSampler*)
);
void TextureSampler_setMinFilterRenderThread(
EMSCRIPTEN_KEEPALIVE void TextureSampler_setMinFilterRenderThread(
TTextureSampler* sampler,
TSamplerMinFilter filter,
uint32_t requestId, VoidCallback onComplete
);
void TextureSampler_setMagFilterRenderThread(
EMSCRIPTEN_KEEPALIVE void TextureSampler_setMagFilterRenderThread(
TTextureSampler* sampler,
TSamplerMagFilter filter,
uint32_t requestId, VoidCallback onComplete
);
void TextureSampler_setWrapModeSRenderThread(
EMSCRIPTEN_KEEPALIVE void TextureSampler_setWrapModeSRenderThread(
TTextureSampler* sampler,
TSamplerWrapMode mode,
uint32_t requestId, VoidCallback onComplete
);
void TextureSampler_setWrapModeTRenderThread(
EMSCRIPTEN_KEEPALIVE void TextureSampler_setWrapModeTRenderThread(
TTextureSampler* sampler,
TSamplerWrapMode mode,
uint32_t requestId, VoidCallback onComplete
);
void TextureSampler_setWrapModeRRenderThread(
EMSCRIPTEN_KEEPALIVE void TextureSampler_setWrapModeRRenderThread(
TTextureSampler* sampler,
TSamplerWrapMode mode,
uint32_t requestId, VoidCallback onComplete
);
void TextureSampler_setAnisotropyRenderThread(
EMSCRIPTEN_KEEPALIVE void TextureSampler_setAnisotropyRenderThread(
TTextureSampler* sampler,
double anisotropy,
uint32_t requestId, VoidCallback onComplete
);
void TextureSampler_setCompareModeRenderThread(
EMSCRIPTEN_KEEPALIVE void TextureSampler_setCompareModeRenderThread(
TTextureSampler* sampler,
TSamplerCompareMode mode,
TTextureSamplerCompareFunc func,
uint32_t requestId, VoidCallback onComplete
);
void TextureSampler_destroyRenderThread(
EMSCRIPTEN_KEEPALIVE void TextureSampler_destroyRenderThread(
TTextureSampler* sampler,
uint32_t requestId, VoidCallback onComplete
);
void AnimationManager_setBoneTransformRenderThread(
EMSCRIPTEN_KEEPALIVE void AnimationManager_setBoneTransformRenderThread(
TAnimationManager *tAnimationManager,
EntityId asset,
int skinIndex,
@@ -293,18 +293,18 @@ namespace thermion
const float *const transform,
void (*callback)(bool));
void AnimationManager_resetToRestPoseRenderThread(TAnimationManager *tAnimationManager, TSceneAsset *tSceneAsset, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void AnimationManager_resetToRestPoseRenderThread(TAnimationManager *tAnimationManager, TSceneAsset *tSceneAsset, uint32_t requestId, VoidCallback onComplete);
void GltfAssetLoader_createRenderThread(TEngine *tEngine, TMaterialProvider *tMaterialProvider, TNameComponentManager *tNameComponentManager, void (*callback)(TGltfAssetLoader *));
void GltfResourceLoader_createRenderThread(TEngine *tEngine, void (*callback)(TGltfResourceLoader *));
void GltfResourceLoader_destroyRenderThread(TEngine *tEngine, TGltfResourceLoader *tResourceLoader, uint32_t requestId, VoidCallback onComplete);
void GltfResourceLoader_loadResourcesRenderThread(TGltfResourceLoader *tGltfResourceLoader, TFilamentAsset *tFilamentAsset, void (*callback)(bool));
void GltfResourceLoader_addResourceDataRenderThread(TGltfResourceLoader *tGltfResourceLoader, const char *uri, uint8_t *data, size_t length, uint32_t requestId, VoidCallback onComplete);
void GltfResourceLoader_asyncBeginLoadRenderThread(TGltfResourceLoader *tGltfResourceLoader, TFilamentAsset *tFilamentAsset, void (*callback)(bool));
void GltfResourceLoader_asyncUpdateLoadRenderThread(TGltfResourceLoader *tGltfResourceLoader);
void GltfResourceLoader_asyncGetLoadProgressRenderThread(TGltfResourceLoader *tGltfResourceLoader, void (*callback)(float));
EMSCRIPTEN_KEEPALIVE void GltfAssetLoader_createRenderThread(TEngine *tEngine, TMaterialProvider *tMaterialProvider, TNameComponentManager *tNameComponentManager, void (*callback)(TGltfAssetLoader *));
EMSCRIPTEN_KEEPALIVE void GltfResourceLoader_createRenderThread(TEngine *tEngine, void (*callback)(TGltfResourceLoader *));
EMSCRIPTEN_KEEPALIVE void GltfResourceLoader_destroyRenderThread(TEngine *tEngine, TGltfResourceLoader *tResourceLoader, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void GltfResourceLoader_loadResourcesRenderThread(TGltfResourceLoader *tGltfResourceLoader, TFilamentAsset *tFilamentAsset, void (*callback)(bool));
EMSCRIPTEN_KEEPALIVE void GltfResourceLoader_addResourceDataRenderThread(TGltfResourceLoader *tGltfResourceLoader, const char *uri, uint8_t *data, size_t length, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void GltfResourceLoader_asyncBeginLoadRenderThread(TGltfResourceLoader *tGltfResourceLoader, TFilamentAsset *tFilamentAsset, void (*callback)(bool));
EMSCRIPTEN_KEEPALIVE void GltfResourceLoader_asyncUpdateLoadRenderThread(TGltfResourceLoader *tGltfResourceLoader);
EMSCRIPTEN_KEEPALIVE void GltfResourceLoader_asyncGetLoadProgressRenderThread(TGltfResourceLoader *tGltfResourceLoader, void (*callback)(float));
void GltfAssetLoader_loadRenderThread(
EMSCRIPTEN_KEEPALIVE void GltfAssetLoader_loadRenderThread(
TEngine *tEngine,
TGltfAssetLoader *tAssetLoader,
uint8_t *data,
@@ -312,8 +312,8 @@ namespace thermion
uint8_t numInstances,
void (*callback)(TFilamentAsset *)
);
void Scene_addFilamentAssetRenderThread(TScene* tScene, TFilamentAsset *tAsset, uint32_t requestId, VoidCallback onComplete);
void Gizmo_createRenderThread(
EMSCRIPTEN_KEEPALIVE void Scene_addFilamentAssetRenderThread(TScene* tScene, TFilamentAsset *tAsset, uint32_t requestId, VoidCallback onComplete);
EMSCRIPTEN_KEEPALIVE void Gizmo_createRenderThread(
TEngine *tEngine,
TGltfAssetLoader *tAssetLoader,
TGltfResourceLoader *tGltfResourceLoader,
@@ -330,4 +330,3 @@ namespace thermion
}
}
#endif

View File

@@ -8,5 +8,5 @@ GRID_PACKAGE:
GRID_GRID_OFFSET:
.int 0
GRID_GRID_SIZE:
.int 50913
.int 49793

View File

@@ -8,5 +8,5 @@ _GRID_PACKAGE:
_GRID_GRID_OFFSET:
.int 0
_GRID_GRID_SIZE:
.int 50913
.int 49793

File diff suppressed because it is too large Load Diff

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

@@ -85,10 +85,10 @@ EMSCRIPTEN_KEEPALIVE TFilamentAsset *GltfAssetLoader_load(
const char *const *const resourceUris = asset->getResourceUris();
const size_t resourceUriCount = asset->getResourceUriCount();
Log("glTF asset : %d resource URIs, %d instances", resourceUriCount, numInstances);
TRACE("Loading glTF asset with %d resource URIs (allocating %d reserved instances", resourceUriCount, numInstances);
for(int i = 0; i < resourceUriCount; i++) {
Log("%s", resourceUris[i]);
TRACE("%s", resourceUris[i]);
}
return reinterpret_cast<TFilamentAsset *>(asset);

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.3
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

@@ -82,10 +82,6 @@ class TestHelper {
testDir = Directory("${packageUri}test").path;
outDir = Directory("$testDir/output/${dir}");
outDir.createSync(recursive: true);
if (Platform.isMacOS) {
DynamicLibrary.open('${testDir}/generated/objective_c.dylib');
DynamicLibrary.open('${testDir}/generated/libThermionTextureSwift.dylib');
}
}
///
@@ -226,6 +222,10 @@ class TestHelper {
FFIRenderTarget? renderTarget;
if (createRenderTarget) {
// if (Platform.isMacOS) {
// DynamicLibrary.open('${testDir}/generated/objective_c.dylib');
// DynamicLibrary.open('${testDir}/generated/libThermionTextureSwift.dylib');
// }
// var metalColorTexture = await createTexture(
// viewportDimensions.width, viewportDimensions.height);
// var metalDepthTexture = await createTexture(

View File

@@ -1,3 +1,25 @@
## 0.3.3
- Bump "thermion_flutter" to `0.3.3`.
## 0.3.3-pre
- **DOCS**: replace thermion_flutter README with symlink to thermion_dart README.
## 0.3.2
- Bump "thermion_flutter" to `0.3.2`.
## 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,63 +0,0 @@
![Thermion Logo](https://raw.githubusercontent.com/nmfisher/flutter_filament/f19ea9b/docs/logo.png)
<p align="center">
<a href="https://thermion.dev/quickstart">Quickstart (Flutter)</a> •
<a href="https://thermion.dev/quickstart">Documentation</a> •
<a href="https://thermion.dev/showcase">Showcase</a> •
<a href="https://dartpad.thermion.dev/">Playground</a> •
<a href="https://discord.gg/h2VdDK3EAQ">Discord</a>
</p>
## Cross-platform 3D engine for Dart and Flutter.
<a href="https://pub.dev/packages/thermion_dart"><img src="https://img.shields.io/pub/v/thermion_dart?label=pub.dev&labelColor=333940&logo=dart&color=00589B" alt="pub"></a>
<a href="https://github.com/nmfisher/thermion"><img src="https://img.shields.io/github/stars/nmfisher/flutter_filament?style=flat&label=stars&labelColor=333940&color=8957e5&logo=github" alt="github"></a>
<a href="https://discord.gg/h2VdDK3EAQ"><img src="https://img.shields.io/discord/993167615587520602?logo=discord&logoColor=fff&labelColor=333940" alt="discord"></a>
<a href="https://github.com/nmfisher/thermion"><img src="https://img.shields.io/github/contributors/nmfisher/flutter_filament?logo=github&labelColor=333940" alt="contributors"></a>
### Quickstart (Flutter)
```
_thermionViewer = await ThermionFlutterPlugin.createViewer();
// Geometry and models are represented as "entities". Here, we load a glTF
// file containing a plain cube.
// By default, all paths are treated as asset paths. To load from a file
// instead, use file:// URIs.
var entity =
await _thermionViewer!.loadGlb("assets/cube.glb", keepData: true);
// Thermion uses a right-handed coordinate system where +Y is up and -Z is
// "into" the screen.
// By default, the camera is located at (0,0,0) looking at (0,0,-1); this
// would place it directly inside the cube we just loaded.
//
// Let's move the camera to (0,0,10) to ensure the cube is visible in the
// viewport.
await _thermionViewer!.setCameraPosition(0, 0, 10);
// Without a light source, your scene will be totally black. Let's load a skybox
// (a cubemap image that is rendered behind everything else in the scene)
// and an image-based indirect light that has been precomputed from the same
// skybox.
await _thermionViewer!.loadSkybox("assets/default_env_skybox.ktx");
await _thermionViewer!.loadIbl("assets/default_env_ibl.ktx");
// Finally, you need to explicitly enable rendering. Setting rendering to
// false is designed to allow you to pause rendering to conserve battery life
await _thermionViewer!.setRendering(true);
```
and then in your Flutter application:
```
@override
Widget build(BuildContext context) {
return Stack(children: [
if (_thermionViewer != null)
Positioned.fill(
child: ThermionWidget(
viewer: _thermionViewer!,
)),
]);
}
```

View File

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

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.3
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.3
thermion_flutter_platform_interface: ^0.3.3
thermion_flutter_method_channel: ^0.3.3
thermion_flutter_web: ^0.3.3
logging: ^1.2.0
web: ^1.0.0

Some files were not shown because too many files have changed in this diff Show More