Compare commits
55 Commits
thermion_f
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d16474784d | ||
|
|
1c07d576d3 | ||
|
|
961b2ae1ee | ||
|
|
a7ac118899 | ||
|
|
d92ad4ef12 | ||
|
|
437e91e7bd | ||
|
|
e169bf6c41 | ||
|
|
23b060c329 | ||
|
|
5fd0a10630 | ||
|
|
ddfb649733 | ||
|
|
ecffc5b62a | ||
|
|
429b8eb93b | ||
|
|
413faec849 | ||
|
|
433b6373a9 | ||
|
|
ad2c5afb7f | ||
|
|
a561c847a7 | ||
|
|
ef7ba24ecc | ||
|
|
ee176d2684 | ||
|
|
fa168df28f | ||
|
|
ba3d016c1a | ||
|
|
e04d8e76c2 | ||
|
|
353b33b7c3 | ||
|
|
3c1b26af2c | ||
|
|
b4ea80a84c | ||
|
|
db44bc6f74 | ||
|
|
c668549fb0 | ||
|
|
a66703b61c | ||
|
|
cb8943ff72 | ||
|
|
9f59577f90 | ||
|
|
b86145d4c6 | ||
|
|
cb8672f120 | ||
|
|
92578426ac | ||
|
|
4a6479c4d8 | ||
|
|
2244d3fcb6 | ||
|
|
003fd59269 | ||
|
|
77e6ef7568 | ||
|
|
6f07d406f8 | ||
|
|
bf5551e278 | ||
|
|
64577af352 | ||
|
|
951894be41 | ||
|
|
c64b2b8659 | ||
|
|
322e77d2b7 | ||
|
|
a8a52bb2f4 | ||
|
|
c7dfd293e2 | ||
|
|
cf2498b45f | ||
|
|
edb7538c36 | ||
|
|
b023e2fb97 | ||
|
|
b51cc4b1d1 | ||
|
|
d995ed8843 | ||
|
|
b0d34bf6a8 | ||
|
|
2e28b0379d | ||
|
|
c899e30a7b | ||
|
|
37f8558794 | ||
|
|
3cfa26d284 | ||
|
|
b22a82e181 |
39
.github/workflows/dart.yml
vendored
@@ -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
|
||||
|
||||
235
CHANGELOG.md
@@ -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
|
||||
|
||||
17
Makefile
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"Getting Started",
|
||||
[
|
||||
["Overview", "/"],
|
||||
["Getting Started", "/getting_started"],
|
||||
["Quick Start", "/quickstart"],
|
||||
["Viewer", "/viewer"],
|
||||
["Camera Manipulation", "/camera_manipulation"]
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||

|
||||
|
||||
</Accordion>
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
[](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.
|
||||
|
||||
[](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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||

|
||||
|
||||
</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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
35
docs/web.mdx
@@ -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
|
||||
|
||||
BIN
examples/assets/bin/mac/upscayl-bin
Executable file
BIN
examples/assets/bin/windows/upscayl-bin.exe
Normal file
BIN
examples/assets/bin/windows/vcomp140.dll
Normal file
BIN
examples/assets/bin/windows/vcomp140d.dll
Normal file
BIN
examples/assets/cup/model.bin
Normal file
|
After Width: | Height: | Size: 960 KiB |
1
examples/assets/cup/model.gltf
Normal 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
|
After Width: | Height: | Size: 720 KiB |
BIN
examples/assets/cup/texture.jpg
Normal file
|
After Width: | Height: | Size: 8.2 MiB |
BIN
examples/assets/cup/texture_tmp.jpg
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
examples/assets/image/texture.jpg
Normal file
|
After Width: | Height: | Size: 8.2 MiB |
BIN
examples/assets/models/digital-art-4x.bin
Normal file
270
examples/assets/models/digital-art-4x.param
Normal 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
|
||||
BIN
examples/assets/models/high-fidelity-4x.bin
Normal file
1001
examples/assets/models/high-fidelity-4x.param
Normal file
BIN
examples/assets/models/model.bin
Normal file
|
After Width: | Height: | Size: 960 KiB |
1
examples/assets/models/model.gltf
Normal 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/models/remacri-4x.bin
Normal file
1372
examples/assets/models/remacri-4x.param
Normal file
BIN
examples/assets/models/texture.jpg
Normal file
|
After Width: | Height: | Size: 8.2 MiB |
BIN
examples/assets/models/ultramix-balanced-4x.bin
Normal file
1372
examples/assets/models/ultramix-balanced-4x.param
Normal file
BIN
examples/assets/models/ultrasharp-4x.bin
Normal file
1001
examples/assets/models/ultrasharp-4x.param
Normal file
BIN
examples/assets/models/upscayl-lite-4x.bin
Normal file
74
examples/assets/models/upscayl-lite-4x.param
Normal 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
|
||||
BIN
examples/assets/models/upscayl-standard-4x.bin
Normal file
1001
examples/assets/models/upscayl-standard-4x.param
Normal file
30
examples/assets/upscale_resize.sh
Executable 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
@@ -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
|
||||
0
examples/assets/upscale_to_8k_single.bat
Normal file
131
examples/assets/upscale_to_8k_single.sh
Executable 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
|
||||
@@ -21,6 +21,6 @@
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>12.0</string>
|
||||
<string>13.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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">
|
||||
|
||||
114
examples/flutter/animations/lib/cup.dart
Normal 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(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
142
examples/flutter/animations/lib/cup_page.dart
Normal 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(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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> {
|
||||
]);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
284
examples/flutter/animations/lib/main1.dart
Normal 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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
190
examples/flutter/animations/lib/model_viewer.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
405
examples/flutter/animations/lib/utils/binary_manager.dart
Normal 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 不需要设置执行权限
|
||||
}
|
||||
}
|
||||
204
examples/flutter/animations/lib/utils/model_manager.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
103
examples/flutter/camera_manipulation/lib/main.dart
Normal 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"}"))
|
||||
],
|
||||
)
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
])))
|
||||
]));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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$();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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,
|
||||
|
||||
@@ -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!);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -8,5 +8,5 @@ GRID_PACKAGE:
|
||||
GRID_GRID_OFFSET:
|
||||
.int 0
|
||||
GRID_GRID_SIZE:
|
||||
.int 50913
|
||||
.int 49793
|
||||
|
||||
|
||||
@@ -8,5 +8,5 @@ _GRID_PACKAGE:
|
||||
_GRID_GRID_OFFSET:
|
||||
.int 0
|
||||
_GRID_GRID_SIZE:
|
||||
.int 50913
|
||||
.int 49793
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||

|
||||
|
||||
<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!,
|
||||
)),
|
||||
]);
|
||||
}
|
||||
```
|
||||
1
thermion_flutter/thermion_flutter/README.md
Symbolic link
@@ -0,0 +1 @@
|
||||
../../thermion_dart/README.md
|
||||
@@ -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(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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!,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||