Compare commits

..

14 Commits

Author SHA1 Message Date
Nick Fisher
cb517c907d chore(release): publish packages
- thermion_dart@0.2.1-dev.0.0.8
 - thermion_flutter_web@0.1.0+9
 - thermion_flutter@0.2.1-dev.7
 - thermion_flutter_platform_interface@0.2.1-dev.7
 - thermion_flutter_ffi@0.2.1-dev.7
2024-10-14 09:36:55 +08:00
Nick Fisher
a6f5e59cbb fix: move ThermionWin32.h to include 2024-10-14 09:36:28 +08:00
Nick Fisher
9420143a36 chore(release): publish packages
- thermion_dart@0.2.1-dev.0.0.7
 - thermion_flutter_web@0.1.0+8
 - thermion_flutter@0.2.1-dev.6
 - thermion_flutter_platform_interface@0.2.1-dev.6
 - thermion_flutter_ffi@0.2.1-dev.6
2024-10-14 09:25:59 +08:00
Nick Fisher
eb8835b63a Merge pull request #69 from nmfisher/feature/multiple_swapchains
Support multiple views/widgets/swapchains
2024-10-14 12:05:21 +11:00
Nick Fisher
f9468db266 Windows embedder fixes 2024-10-14 11:23:56 +11:00
Nick Fisher
1135ba054c cleanup 2024-10-12 15:07:14 +11:00
Nick Fisher
8f7509a23f cleanup 2024-10-12 15:06:57 +11:00
Nick Fisher
cba9ee98ad (flutter) set view renderable on Windows 2024-10-12 14:56:04 +11:00
Nick Fisher
0ec0fef8f3 move standalone Windows file to extras 2024-10-12 14:55:41 +11:00
Nick Fisher
a348562f56 cleanup 2024-10-12 14:36:51 +11:00
Nick Fisher
392a606bbc don't call endFrame() if no views were rendered 2024-10-12 14:36:05 +11:00
Nick Fisher
440bed4485 add files to run standalone Windows 2024-10-12 14:34:42 +11:00
Nick Fisher
a321966e5b fix Windows build.dart to avoid native_assets fork; add implementations for ThermionFlutterWindows 2024-10-12 02:14:37 +11:00
Nick Fisher
f180c1018f mobile gesture handler 2024-10-11 15:37:03 +08:00
40 changed files with 1089 additions and 296 deletions

View File

@@ -3,6 +3,74 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## 2024-10-14
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`thermion_dart` - `v0.2.1-dev.0.0.8`](#thermion_dart---v021-dev008)
- [`thermion_flutter_web` - `v0.1.0+9`](#thermion_flutter_web---v0109)
- [`thermion_flutter` - `v0.2.1-dev.7`](#thermion_flutter---v021-dev7)
- [`thermion_flutter_platform_interface` - `v0.2.1-dev.7`](#thermion_flutter_platform_interface---v021-dev7)
- [`thermion_flutter_ffi` - `v0.2.1-dev.7`](#thermion_flutter_ffi---v021-dev7)
Packages with dependency updates only:
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
- `thermion_flutter_web` - `v0.1.0+9`
- `thermion_flutter` - `v0.2.1-dev.7`
- `thermion_flutter_platform_interface` - `v0.2.1-dev.7`
- `thermion_flutter_ffi` - `v0.2.1-dev.7`
---
#### `thermion_dart` - `v0.2.1-dev.0.0.8`
- **FIX**: move ThermionWin32.h to include.
## 2024-10-14
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`thermion_dart` - `v0.2.1-dev.0.0.7`](#thermion_dart---v021-dev007)
- [`thermion_flutter_web` - `v0.1.0+8`](#thermion_flutter_web---v0108)
- [`thermion_flutter` - `v0.2.1-dev.6`](#thermion_flutter---v021-dev6)
- [`thermion_flutter_platform_interface` - `v0.2.1-dev.6`](#thermion_flutter_platform_interface---v021-dev6)
- [`thermion_flutter_ffi` - `v0.2.1-dev.6`](#thermion_flutter_ffi---v021-dev6)
Packages with dependency updates only:
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
- `thermion_flutter_web` - `v0.1.0+8`
- `thermion_flutter` - `v0.2.1-dev.6`
- `thermion_flutter_platform_interface` - `v0.2.1-dev.6`
- `thermion_flutter_ffi` - `v0.2.1-dev.6`
---
#### `thermion_dart` - `v0.2.1-dev.0.0.7`
- Bump "thermion_dart" to `0.2.1-dev.0.0.7`.
## 2024-10-10
### Changes

View File

@@ -1,5 +1,19 @@
## Android
### Min SDK version
Thermion requires Android SDK version 22, so change your `app/android/build.gradle` to match this version or higher
```groovy
defaultConfig {
...
minSdk = 22
...
}
```
### Shrink/Minify Resources
In release mode, you must add the following to your `app/build.gradle`:
```

View File

@@ -1,3 +1,11 @@
## 0.2.1-dev.0.0.8
- **FIX**: move ThermionWin32.h to include.
## 0.2.1-dev.0.0.7
- Bump "thermion_dart" to `0.2.1-dev.0.0.7`.
## 0.2.1-dev.0.0.6
- Bump "thermion_dart" to `0.2.1-dev.0.0.6`.

View File

@@ -0,0 +1,30 @@
cmake_minimum_required(VERSION 3.14)
set(PROJECT_NAME "thermion_windows")
project(${PROJECT_NAME} LANGUAGES C CXX)
cmake_policy(VERSION 3.14...3.25)
add_compile_definitions(WGL_USE_BACKING_WINDOW)
add_compile_definitions(UNICODE)
add_library(${PROJECT_NAME} SHARED
"thermion_window.cpp"
)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20)
target_include_directories(${PROJECT_NAME} INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_SOURCE_DIR}/../../../../thermion_dart/native/include/filament"
"${CMAKE_SOURCE_DIR}/../../../../thermion_dart/native/include"
${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_link_libraries(${PROJECT_NAME} PRIVATE
Shlwapi
opengl32
dwmapi
comctl32
)

View File

@@ -0,0 +1,244 @@
#include "thermion_window.h"
#include <cstdint>
#include <iostream>
#include <chrono>
#include <thread>
#include <Windows.h>
#include <dwmapi.h>
#include <ShObjIdl.h>
#pragma comment(lib, "dwmapi.lib")
#pragma comment(lib, "comctl32.lib")
namespace thermion {
static constexpr auto kClassName = L"THERMION_WINDOW";
static constexpr auto kWindowName = L"thermion_window";
static bool was_window_hidden_due_to_minimize_ = false;
static WPARAM last_wm_size_wparam_ = SIZE_RESTORED;
uint64_t last_thread_time_ = 0;
static constexpr auto kNativeViewPositionAndShowDelay = 300;
typedef enum _WINDOWCOMPOSITIONATTRIB {
WCA_UNDEFINED = 0,
WCA_NCRENDERING_ENABLED = 1,
WCA_NCRENDERING_POLICY = 2,
WCA_TRANSITIONS_FORCEDISABLED = 3,
WCA_ALLOW_NCPAINT = 4,
WCA_CAPTION_BUTTON_BOUNDS = 5,
WCA_NONCLIENT_RTL_LAYOUT = 6,
WCA_FORCE_ICONIC_REPRESENTATION = 7,
WCA_EXTENDED_FRAME_BOUNDS = 8,
WCA_HAS_ICONIC_BITMAP = 9,
WCA_THEME_ATTRIBUTES = 10,
WCA_NCRENDERING_EXILED = 11,
WCA_NCADORNMENTINFO = 12,
WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
WCA_VIDEO_OVERLAY_ACTIVE = 14,
WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
WCA_DISALLOW_PEEK = 16,
WCA_CLOAK = 17,
WCA_CLOAKED = 18,
WCA_ACCENT_POLICY = 19,
WCA_FREEZE_REPRESENTATION = 20,
WCA_EVER_UNCLOAKED = 21,
WCA_VISUAL_OWNER = 22,
WCA_HOLOGRAPHIC = 23,
WCA_EXCLUDED_FROM_DDA = 24,
WCA_PASSIVEUPDATEMODE = 25,
WCA_USEDARKMODECOLORS = 26,
WCA_LAST = 27
} WINDOWCOMPOSITIONATTRIB;
typedef struct _WINDOWCOMPOSITIONATTRIBDATA {
WINDOWCOMPOSITIONATTRIB Attrib;
PVOID pvData;
SIZE_T cbData;
} WINDOWCOMPOSITIONATTRIBDATA;
typedef enum _ACCENT_STATE {
ACCENT_DISABLED = 0,
ACCENT_ENABLE_GRADIENT = 1,
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
ACCENT_ENABLE_BLURBEHIND = 3,
ACCENT_ENABLE_ACRYLICBLURBEHIND = 4,
ACCENT_ENABLE_HOSTBACKDROP = 5,
ACCENT_INVALID_STATE = 6
} ACCENT_STATE;
typedef struct _ACCENT_POLICY {
ACCENT_STATE AccentState;
DWORD AccentFlags;
DWORD GradientColor;
DWORD AnimationId;
} ACCENT_POLICY;
typedef BOOL(WINAPI* _GetWindowCompositionAttribute)(
HWND, WINDOWCOMPOSITIONATTRIBDATA*);
typedef BOOL(WINAPI* _SetWindowCompositionAttribute)(
HWND, WINDOWCOMPOSITIONATTRIBDATA*);
static _SetWindowCompositionAttribute g_set_window_composition_attribute = NULL;
static bool g_set_window_composition_attribute_initialized = false;
typedef LONG NTSTATUS, *PNTSTATUS;
#define STATUS_SUCCESS (0x00000000)
typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
RTL_OSVERSIONINFOW GetWindowsVersion() {
HMODULE hmodule = ::GetModuleHandleW(L"ntdll.dll");
if (hmodule) {
RtlGetVersionPtr rtl_get_version_ptr =
(RtlGetVersionPtr)::GetProcAddress(hmodule, "RtlGetVersion");
if (rtl_get_version_ptr != nullptr) {
RTL_OSVERSIONINFOW rovi = {0};
rovi.dwOSVersionInfoSize = sizeof(rovi);
if (STATUS_SUCCESS == rtl_get_version_ptr(&rovi)) {
return rovi;
}
}
}
RTL_OSVERSIONINFOW rovi = {0};
return rovi;
}
void SetWindowComposition(HWND window, int32_t accent_state,
int32_t gradient_color) {
// TODO: Look for a better available API.
if (GetWindowsVersion().dwBuildNumber >= 18362) {
if (!g_set_window_composition_attribute_initialized) {
auto user32 = ::GetModuleHandleA("user32.dll");
if (user32) {
g_set_window_composition_attribute =
reinterpret_cast<_SetWindowCompositionAttribute>(
::GetProcAddress(user32, "SetWindowCompositionAttribute"));
if (g_set_window_composition_attribute) {
g_set_window_composition_attribute_initialized = true;
}
}
}
ACCENT_POLICY accent = {static_cast<ACCENT_STATE>(accent_state), 2,
static_cast<DWORD>(gradient_color), 0};
WINDOWCOMPOSITIONATTRIBDATA data;
data.Attrib = WCA_ACCENT_POLICY;
data.pvData = &accent;
data.cbData = sizeof(accent);
g_set_window_composition_attribute(window, &data);
}
}
LRESULT CALLBACK FilamentWindowProc(HWND const window, UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
switch (message) {
std::cout << message <<std::endl;
case WM_MOUSEMOVE: {
TRACKMOUSEEVENT event;
event.cbSize = sizeof(event);
event.hwndTrack = window;
event.dwFlags = TME_HOVER;
event.dwHoverTime = 200;
auto user_data = ::GetWindowLongPtr(window, GWLP_USERDATA);
if (user_data) {
HWND flutterRootWindow = reinterpret_cast<HWND>(user_data);
::SetForegroundWindow(flutterRootWindow);
LONG ex_style = ::GetWindowLong(flutterRootWindow, GWL_EXSTYLE);
ex_style &= ~(WS_EX_TRANSPARENT | WS_EX_LAYERED);
::SetWindowLong(flutterRootWindow, GWL_EXSTYLE, ex_style);
}
break;
}
case WM_ERASEBKGND: {
HDC hdc = (HDC)wparam;
RECT rect;
GetClientRect(window, &rect);
// Get the ThermionWindow instance associated with this window
ThermionWindow* thermionWindow = reinterpret_cast<ThermionWindow*>(
GetWindowLongPtr(window, GWLP_USERDATA));
if (thermionWindow) {
HBRUSH brush = CreateSolidBrush(RGB(0, 0, 255));
FillRect(hdc, &rect, brush);
DeleteObject(brush);
}
break;
}
case WM_SIZE:
break;
case WM_MOVE:
case WM_MOVING:
case WM_ACTIVATE:
case WM_WINDOWPOSCHANGED: {
// NativeViewCore::GetInstance()->SetHitTestBehavior(0);
auto user_data = ::GetWindowLongPtr(window, GWLP_USERDATA);
if (user_data) {
HWND flutterRootWindow = reinterpret_cast<HWND>(user_data);
::SetForegroundWindow(flutterRootWindow);
// NativeViewCore::GetInstance()->SetHitTestBehavior(0);
LONG ex_style = ::GetWindowLong(flutterRootWindow, GWL_EXSTYLE);
ex_style &= ~(WS_EX_TRANSPARENT | WS_EX_LAYERED);
::SetWindowLong(flutterRootWindow, GWL_EXSTYLE, ex_style);
}
break;
}
default:
break;
}
return ::DefWindowProc(window, message, wparam, lparam);
}
ThermionWindow::ThermionWindow(int width,
int height,
int left,
int top) : _width(width), _height(height), _left(left), _top(top) {
// create the HWND for Filament
auto window_class = WNDCLASSEX{};
::SecureZeroMemory(&window_class, sizeof(window_class));
window_class.cbSize = sizeof(window_class);
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.lpfnWndProc = FilamentWindowProc;
window_class.hInstance = 0;
window_class.lpszClassName = kClassName;
window_class.hCursor = ::LoadCursorW(nullptr, IDC_ARROW);
window_class.hbrBackground = ::CreateSolidBrush(RGB(0,255,0));
::RegisterClassExW(&window_class);
_windowHandle = ::CreateWindow(kClassName, kWindowName, WS_OVERLAPPEDWINDOW,
0, 0, _width, _height, nullptr,
nullptr, GetModuleHandle(nullptr), nullptr);
// Disable DWM animations
auto disable_window_transitions = TRUE;
DwmSetWindowAttribute(_windowHandle, DWMWA_TRANSITIONS_FORCEDISABLED,
&disable_window_transitions,
sizeof(disable_window_transitions));
auto style = ::GetWindowLong(_windowHandle, GWL_STYLE);
style &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX |
WS_EX_APPWINDOW);
::SetWindowLong(_windowHandle, GWL_STYLE, style);
// // remove taskbar entry for the window we created
// ITaskbarList3* taskbar = nullptr;
// ::CoCreateInstance(CLSID_TaskbarList, 0, CLSCTX_INPROC_SERVER,
// IID_PPV_ARGS(&taskbar));
// taskbar->DeleteTab(_windowHandle);
// taskbar->Release();
::ShowWindow(_windowHandle, SW_SHOW);
UpdateWindow(_windowHandle);
}
void ThermionWindow::Resize(int width, int height, int left, int top) {
_width = width;
_height = height;
_left = left;
_top = top;
}
HWND ThermionWindow::GetHandle() { return _windowHandle; }
} // namespace thermion_flutter

View File

@@ -0,0 +1,57 @@
#pragma once
#ifdef _WIN32
#ifdef IS_DLL
#define EMSCRIPTEN_KEEPALIVE __declspec(dllimport)
#else
#define EMSCRIPTEN_KEEPALIVE __declspec(dllexport)
#endif
#include <cstdint>
#include <iostream>
#include <Windows.h>
namespace thermion {
///
/// Instantiating a ThermionWindow creates a HWND that can be passed
/// to Filament to create a swapchain.
///
///
class ThermionWindow {
public:
ThermionWindow(
int width,
int height,
int left,
int top);
HWND GetHandle();
void Resize(int width, int height, int left, int top);
private:
HWND _windowHandle;
uint32_t _width = 0;
uint32_t _height = 0;
uint32_t _left = 0;
uint32_t _top = 0;
};
static ThermionWindow* _window;
extern "C" {
EMSCRIPTEN_KEEPALIVE intptr_t create_thermion_window(int width, int height, int left, int top) {
_window = new ThermionWindow(width, height, left, top);
return (intptr_t)_window->GetHandle();
}
EMSCRIPTEN_KEEPALIVE void update() {
MSG msg;
if(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
}
#endif

View File

@@ -32,19 +32,14 @@ void main(List<String> args) async {
final name = "thermion_dart.dart";
final libUri = config.outputDirectory
.resolve(config.targetOS.libraryFileName(name, linkMode));
output.addAssets(
[
NativeCodeAsset(
package: config.packageName,
name: name,
file: libUri,
linkMode: linkMode,
os: config.targetOS,
architecture: config.dryRun ? null : config.targetArchitecture,
)
],
linkInPackage: null,
);
output.addAsset(NativeCodeAsset(
package: config.packageName,
name: name,
file: libUri,
linkMode: linkMode,
os: config.targetOS,
architecture: config.dryRun ? null : config.targetArchitecture,
));
return;
}
@@ -58,7 +53,7 @@ void main(List<String> args) async {
.map((f) => f.path)
.toList();
sources.addAll([
"${config.packageRoot.toFilePath()}/native/include/material/gizmo.c",
"${config.packageRoot.toFilePath()}/native/include/material/gizmo_material.c",
"${config.packageRoot.toFilePath()}/native/include/material/image.c",
"${config.packageRoot.toFilePath()}/native/include/material/grid.c",
"${config.packageRoot.toFilePath()}/native/include/material/unlit.c",
@@ -92,7 +87,6 @@ void main(List<String> args) async {
"basis_transcoder"
];
if (platform == "windows") {
libDir = Directory(libDir).uri.toFilePath();
libs = libs.map((lib) => "${libDir}${lib}.lib").toList();
@@ -118,7 +112,12 @@ void main(List<String> args) async {
defines["WIN32"] = "1";
defines["_DEBUG"] = "1";
defines["_DLL"] = "1";
flags.addAll(["/std:c++20", "/MDd"]);
flags.addAll([
"/std:c++20",
"/MDd",
"/VERBOSE",
...defines.keys.map((k) => "/D$k=${defines[k]}").toList()
]);
}
if (platform == "ios") {
@@ -149,16 +148,26 @@ void main(List<String> args) async {
name: packageName,
language: Language.cpp,
assetName: 'thermion_dart.dart',
sources: sources,
includes: ['native/include', 'native/include/filament'],
defines: defines,
sources: platform == "windows" ? [] : sources,
includes: platform == "windows"
? []
: ['native/include', 'native/include/filament'],
defines: platform == "windows" ? {} : defines,
flags: [
if (platform == "macos") '-mmacosx-version-min=13.0',
if (platform == "ios") '-mios-version-min=13.0',
...flags,
...frameworks,
...libs.map((lib) => "-l$lib"),
"-L$libDir",
if (platform != "windows") ...libs.map((lib) => "-l$lib"),
if (platform != "windows") "-L$libDir",
if (platform == "windows") ...[
"/I${config.packageRoot.toFilePath()}\\native\\include",
"/I${config.packageRoot.toFilePath()}native\\include\\filament",
...sources,
'/link',
"/LIBPATH:$libDir",
'/DLL',
]
],
dartBuildFiles: ['hook/build.dart'],
);
@@ -177,17 +186,24 @@ void main(List<String> args) async {
Architecture.ia32 => "i686-linux-android",
_ => throw FormatException('Invalid')
};
var compilerPath = config.cCompiler.compiler!.path;
if(Platform.isWindows && compilerPath.startsWith("/")) {
if (Platform.isWindows && compilerPath.startsWith("/")) {
compilerPath = compilerPath.substring(1);
}
var ndkRoot = File(compilerPath).parent.parent.uri.toFilePath(windows:true);
var stlPath =
File([ndkRoot, "sysroot", "usr", "lib", archExtension, "libc++_shared.so"].join(Platform.pathSeparator));
var ndkRoot =
File(compilerPath).parent.parent.uri.toFilePath(windows: true);
var stlPath = File([
ndkRoot,
"sysroot",
"usr",
"lib",
archExtension,
"libc++_shared.so"
].join(Platform.pathSeparator));
output.addAsset(NativeCodeAsset(
package: "thermion_dart",
name: "libc++_shared.so",
@@ -201,7 +217,7 @@ void main(List<String> args) async {
if (config.targetOS == "windows") {
output.addAsset(
NativeCodeAsset(
package: "thermion_dart",
package: config.packageName,
name: "thermion_dart.dll",
linkMode: DynamicLoadingBundled(),
os: config.targetOS,

View File

@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:math';
import 'package:logging/logging.dart';
import 'package:thermion_dart/thermion_dart.dart';
import 'package:vector_math/vector_math_64.dart';
@@ -26,6 +27,8 @@ class DelegateInputHandler implements InputHandler {
Map<InputType, InputAction> _actions = {
InputType.LMB_HOLD_AND_MOVE: InputAction.TRANSLATE,
InputType.SCALE1: InputAction.TRANSLATE,
InputType.SCALE2: InputAction.ZOOM,
InputType.MMB_HOLD_AND_MOVE: InputAction.ROTATE,
InputType.SCROLLWHEEL: InputAction.TRANSLATE,
InputType.POINTER_MOVE: InputAction.NONE,
@@ -99,6 +102,8 @@ class DelegateInputHandler implements InputHandler {
InputType.KEYDOWN_W: InputAction.TRANSLATE,
InputType.KEYDOWN_S: InputAction.TRANSLATE,
InputType.KEYDOWN_D: InputAction.TRANSLATE,
InputType.SCALE1: InputAction.TRANSLATE,
InputType.SCALE2: InputAction.ZOOM,
if (freeLook) InputType.POINTER_MOVE: InputAction.ROTATE,
});
@@ -235,15 +240,6 @@ class DelegateInputHandler implements InputHandler {
@override
Future<bool> get initialized => viewer.initialized;
@override
Future<void> onScaleEnd() async {}
@override
Future<void> onScaleStart() async {}
@override
Future<void> onScaleUpdate() async {}
@override
void setActionForType(InputType gestureType, InputAction gestureAction) {
_actions[gestureType] = gestureAction;
@@ -261,4 +257,26 @@ class DelegateInputHandler implements InputHandler {
void keyUp(PhysicalKey key) {
_pressedKeys.remove(key);
}
@override
Future<void> onScaleEnd(int pointerCount) async {}
@override
Future<void> onScaleStart(Vector2 localPosition, int pointerCount) async {
// noop
}
@override
Future<void> onScaleUpdate(Vector2 focalPoint, Vector2 focalPointDelta,
double horizontalScale, double verticalScale, double scale, int pointerCount) async {
if (pointerCount == 1) {
_inputDeltas[InputType.SCALE1] =
Vector3(focalPointDelta.x, focalPointDelta.y, 0);
} else if (pointerCount == 2) {
_inputDeltas[InputType.SCALE2] =
Vector3(0, 0, max(horizontalScale, verticalScale));
} else {
throw UnimplementedError("Only pointerCount <= 2 supported");
}
}
}

View File

@@ -35,9 +35,9 @@ abstract class InputHandler {
Future<void> onPointerMove(
Vector2 localPosition, Vector2 delta, bool isMiddle);
Future<void> onPointerUp(bool isMiddle);
Future<void> onScaleStart();
Future<void> onScaleUpdate();
Future<void> onScaleEnd();
Future<void> onScaleStart(Vector2 focalPoint, int pointerCount);
Future<void> onScaleUpdate(Vector2 focalPoint, Vector2 focalPointDelta, double horizontalScale, double verticalScale, double scale, int pointerCount);
Future<void> onScaleEnd(int pointerCount);
Future<bool> get initialized;
Future dispose();

View File

@@ -142,6 +142,17 @@ external void Viewer_setViewRenderable(
bool renderable,
);
@ffi.Native<
ffi.Void Function(ffi.Pointer<TViewer>, ffi.Pointer<ffi.Char>, ffi.Float,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
external void Viewer_loadIblRenderThread(
ffi.Pointer<TViewer> viewer,
ffi.Pointer<ffi.Char> iblPath,
double intensity,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
);
@ffi.Native<
ffi.Void Function(
ffi.Pointer<TViewer>,

View File

@@ -360,7 +360,12 @@ class ThermionViewerFFI extends ThermionViewer {
Future loadIbl(String lightingPath, {double intensity = 30000}) async {
final pathPtr =
lightingPath.toNativeUtf8(allocator: allocator).cast<Char>();
load_ibl(_viewer!, pathPtr, intensity);
await withVoidCallback((cb) {
Viewer_loadIblRenderThread(_viewer!, pathPtr, intensity, cb);
});
}
///

View File

@@ -21,14 +21,14 @@ extern "C"
typedef struct TScene TScene;
struct TMaterialKey {
bool doubleSided = 1;
bool unlit = 1;
bool hasVertexColors = 1;
bool hasBaseColorTexture = 1;
bool hasNormalTexture = 1;
bool hasOcclusionTexture = 1;
bool hasEmissiveTexture = 1;
bool useSpecularGlossiness = 1;
bool doubleSided = true;
bool unlit = true;
bool hasVertexColors = true;
bool hasBaseColorTexture = true;
bool hasNormalTexture = true;
bool hasOcclusionTexture = true;
bool hasEmissiveTexture = true;
bool useSpecularGlossiness = true;
int alphaMode = 4;
bool enableDiagnostics = 4;
union {
@@ -43,42 +43,42 @@ extern "C"
};
#else
struct {
bool hasMetallicRoughnessTexture = 1;
bool hasMetallicRoughnessTexture = true;
uint8_t metallicRoughnessUV = 7;
};
struct {
bool hasSpecularGlossinessTexture = 1;
bool hasSpecularGlossinessTexture = true;
uint8_t specularGlossinessUV = 7;
};
#endif
};
uint8_t baseColorUV;
// -- 32 bit boundary --
bool hasClearCoatTexture = 1;
bool hasClearCoatTexture = true;
uint8_t clearCoatUV = 7;
bool hasClearCoatRoughnessTexture = 1;
bool hasClearCoatRoughnessTexture = true;
uint8_t clearCoatRoughnessUV = 7;
bool hasClearCoatNormalTexture = 1;
bool hasClearCoatNormalTexture = true;
uint8_t clearCoatNormalUV = 7;
bool hasClearCoat = 1;
bool hasTransmission = 1;
bool hasClearCoat = true;
bool hasTransmission = true;
bool hasTextureTransforms = 6;
// -- 32 bit boundary --
uint8_t emissiveUV;
uint8_t aoUV;
uint8_t normalUV;
bool hasTransmissionTexture = 1;
bool hasTransmissionTexture = true;
uint8_t transmissionUV = 7;
// -- 32 bit boundary --
bool hasSheenColorTexture = 1;
bool hasSheenColorTexture = true;
uint8_t sheenColorUV = 7;
bool hasSheenRoughnessTexture = 1;
bool hasSheenRoughnessTexture = true;
uint8_t sheenRoughnessUV = 7;
bool hasVolumeThicknessTexture = 1;
bool hasVolumeThicknessTexture = true;
uint8_t volumeThicknessUV = 7;
bool hasSheen = 1;
bool hasIOR = 1;
bool hasVolume = 1;
bool hasSheen = true;
bool hasIOR = true;
bool hasVolume = true;
} ;
typedef struct TMaterialKey TMaterialKey;

View File

@@ -99,7 +99,7 @@ extern "C"
EMSCRIPTEN_KEEPALIVE void load_skybox(TViewer *viewer, const char *skyboxPath);
EMSCRIPTEN_KEEPALIVE void load_ibl(TViewer *viewer, const char *iblPath, float intensity);
EMSCRIPTEN_KEEPALIVE void Viewer_loadIbl(TViewer *viewer, const char *iblPath, float intensity);
EMSCRIPTEN_KEEPALIVE void create_ibl(TViewer *viewer, float r, float g, float b, float intensity);
EMSCRIPTEN_KEEPALIVE void rotate_ibl(TViewer *viewer, float *rotationMatrix);
EMSCRIPTEN_KEEPALIVE void remove_skybox(TViewer *viewer);

View File

@@ -19,7 +19,7 @@ extern "C"
typedef int32_t EntityId;
typedef void (*FilamentRenderCallback)(void *const owner);
void Viewer_createOnRenderThread(
EMSCRIPTEN_KEEPALIVE void Viewer_createOnRenderThread(
void *const context,
void *const platform,
const char *uberArchivePath,
@@ -27,68 +27,68 @@ extern "C"
void (*renderCallback)(void *const renderCallbackOwner),
void *const renderCallbackOwner,
void (*callback)(TViewer *viewer));
void Viewer_createSwapChainRenderThread(TViewer *viewer, void *const surface, void (*onComplete)(TSwapChain*));
void Viewer_createHeadlessSwapChainRenderThread(TViewer *viewer, uint32_t width, uint32_t height, void (*onComplete)(TSwapChain*));
void Viewer_destroySwapChainRenderThread(TViewer *viewer, TSwapChain* swapChain, void (*onComplete)());
void Viewer_renderRenderThread(TViewer *viewer, TView* view, TSwapChain* swapChain);
void Viewer_captureRenderThread(TViewer *viewer, TView* view, TSwapChain* swapChain, uint8_t* out, void (*onComplete)());
void Viewer_captureRenderTargetRenderThread(TViewer *viewer, TView* view, TSwapChain* swapChain, TRenderTarget* renderTarget, uint8_t* out, void (*onComplete)());
void Viewer_requestFrameRenderThread(TViewer *viewer, void(*onComplete)());
void View_setToneMappingRenderThread(TView *tView, TEngine *tEngine, thermion::ToneMapping toneMapping);
void View_setBloomRenderThread(TView *tView, double bloom);
EMSCRIPTEN_KEEPALIVE void Viewer_createSwapChainRenderThread(TViewer *viewer, void *const surface, void (*onComplete)(TSwapChain*));
EMSCRIPTEN_KEEPALIVE void Viewer_createHeadlessSwapChainRenderThread(TViewer *viewer, uint32_t width, uint32_t height, void (*onComplete)(TSwapChain*));
EMSCRIPTEN_KEEPALIVE void Viewer_destroySwapChainRenderThread(TViewer *viewer, TSwapChain* swapChain, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_renderRenderThread(TViewer *viewer, TView* view, TSwapChain* swapChain);
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderThread(TViewer *viewer, TView* view, TSwapChain* swapChain, uint8_t* out, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderTargetRenderThread(TViewer *viewer, TView* view, TSwapChain* swapChain, TRenderTarget* renderTarget, uint8_t* out, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_requestFrameRenderThread(TViewer *viewer, void(*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_loadIblRenderThread(TViewer *viewer, const char *iblPath, float intensity, void(*onComplete)());
EMSCRIPTEN_KEEPALIVE void View_setToneMappingRenderThread(TView *tView, TEngine *tEngine, thermion::ToneMapping toneMapping);
EMSCRIPTEN_KEEPALIVE void View_setBloomRenderThread(TView *tView, double bloom);
void destroy_filament_viewer_render_thread(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void destroy_filament_viewer_render_thread(TViewer *viewer);
FilamentRenderCallback make_render_callback_fn_pointer(FilamentRenderCallback);
void set_rendering_render_thread(TViewer *viewer, bool rendering, void(*onComplete)());
EMSCRIPTEN_KEEPALIVE void set_rendering_render_thread(TViewer *viewer, bool rendering, void(*onComplete)());
void set_frame_interval_render_thread(TViewer *viewer, float frameInterval);
void set_background_color_render_thread(TViewer *viewer, const float r, const float g, const float b, const float a);
void clear_background_image_render_thread(TViewer *viewer);
void set_background_image_render_thread(TViewer *viewer, const char *path, bool fillHeight, void (*onComplete)());
void set_background_image_position_render_thread(TViewer *viewer, float x, float y, bool clamp);
void load_skybox_render_thread(TViewer *viewer, const char *skyboxPath, void (*onComplete)());
void remove_skybox_render_thread(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void set_frame_interval_render_thread(TViewer *viewer, float frameInterval);
EMSCRIPTEN_KEEPALIVE void set_background_color_render_thread(TViewer *viewer, const float r, const float g, const float b, const float a);
EMSCRIPTEN_KEEPALIVE void clear_background_image_render_thread(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void set_background_image_render_thread(TViewer *viewer, const char *path, bool fillHeight, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void set_background_image_position_render_thread(TViewer *viewer, float x, float y, bool clamp);
EMSCRIPTEN_KEEPALIVE void load_skybox_render_thread(TViewer *viewer, const char *skyboxPath, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void remove_skybox_render_thread(TViewer *viewer);
void SceneManager_loadGlbFromBufferRenderThread(TSceneManager *sceneManager, const uint8_t *const data, size_t length, int numInstances, bool keepData, int priority, int layer, bool loadResourcesAsync, void (*callback)(EntityId));
void load_glb_render_thread(TSceneManager *sceneManager, const char *assetPath, int numInstances, bool keepData, void (*callback)(EntityId));
void load_gltf_render_thread(TSceneManager *sceneManager, const char *assetPath, const char *relativePath, bool keepData, void (*callback)(EntityId));
void create_instance_render_thread(TSceneManager *sceneManager, EntityId entityId, void (*callback)(EntityId));
void remove_entity_render_thread(TViewer *viewer, EntityId asset, void (*callback)());
void clear_entities_render_thread(TViewer *viewer, void (*callback)());
EMSCRIPTEN_KEEPALIVE void SceneManager_loadGlbFromBufferRenderThread(TSceneManager *sceneManager, const uint8_t *const data, size_t length, int numInstances, bool keepData, int priority, int layer, bool loadResourcesAsync, void (*callback)(EntityId));
EMSCRIPTEN_KEEPALIVE void load_glb_render_thread(TSceneManager *sceneManager, const char *assetPath, int numInstances, bool keepData, void (*callback)(EntityId));
EMSCRIPTEN_KEEPALIVE void load_gltf_render_thread(TSceneManager *sceneManager, const char *assetPath, const char *relativePath, bool keepData, void (*callback)(EntityId));
EMSCRIPTEN_KEEPALIVE void create_instance_render_thread(TSceneManager *sceneManager, EntityId entityId, void (*callback)(EntityId));
EMSCRIPTEN_KEEPALIVE void remove_entity_render_thread(TViewer *viewer, EntityId asset, void (*callback)());
EMSCRIPTEN_KEEPALIVE void clear_entities_render_thread(TViewer *viewer, void (*callback)());
void apply_weights_render_thread(
EMSCRIPTEN_KEEPALIVE void apply_weights_render_thread(
TSceneManager *sceneManager,
EntityId asset,
const char *const entityName,
float *const weights,
int count);
void set_animation_frame_render_thread(TSceneManager *sceneManager, EntityId asset, int animationIndex, int animationFrame);
void stop_animation_render_thread(TSceneManager *sceneManager, EntityId asset, int index);
void get_animation_count_render_thread(TSceneManager *sceneManager, EntityId asset, void (*callback)(int));
void get_animation_name_render_thread(TSceneManager *sceneManager, EntityId asset, char *const outPtr, int index, void (*callback)());
void get_morph_target_name_render_thread(TSceneManager *sceneManager, EntityId assetEntity, EntityId childEntity, char *const outPtr, int index, void (*callback)());
void get_morph_target_name_count_render_thread(TSceneManager *sceneManager, EntityId asset, EntityId childEntity, void (*callback)(int32_t));
void set_morph_target_weights_render_thread(TSceneManager *sceneManager,
EMSCRIPTEN_KEEPALIVE void set_animation_frame_render_thread(TSceneManager *sceneManager, EntityId asset, int animationIndex, int animationFrame);
EMSCRIPTEN_KEEPALIVE void stop_animation_render_thread(TSceneManager *sceneManager, EntityId asset, int index);
EMSCRIPTEN_KEEPALIVE void get_animation_count_render_thread(TSceneManager *sceneManager, EntityId asset, void (*callback)(int));
EMSCRIPTEN_KEEPALIVE void get_animation_name_render_thread(TSceneManager *sceneManager, EntityId asset, char *const outPtr, int index, void (*callback)());
EMSCRIPTEN_KEEPALIVE void get_morph_target_name_render_thread(TSceneManager *sceneManager, EntityId assetEntity, EntityId childEntity, char *const outPtr, int index, void (*callback)());
EMSCRIPTEN_KEEPALIVE void get_morph_target_name_count_render_thread(TSceneManager *sceneManager, EntityId asset, EntityId childEntity, void (*callback)(int32_t));
EMSCRIPTEN_KEEPALIVE void set_morph_target_weights_render_thread(TSceneManager *sceneManager,
EntityId asset,
const float *const morphData,
int numWeights,
void (*callback)(bool));
void update_bone_matrices_render_thread(TSceneManager *sceneManager,
EMSCRIPTEN_KEEPALIVE void update_bone_matrices_render_thread(TSceneManager *sceneManager,
EntityId asset, void(*callback)(bool));
void set_bone_transform_render_thread(
EMSCRIPTEN_KEEPALIVE void set_bone_transform_render_thread(
TSceneManager *sceneManager,
EntityId asset,
int skinIndex,
int boneIndex,
const float *const transform,
void (*callback)(bool));
void set_post_processing_render_thread(TViewer *viewer, bool enabled);
void reset_to_rest_pose_render_thread(TSceneManager *sceneManager, EntityId entityId, void(*callback)());
void create_geometry_render_thread(
EMSCRIPTEN_KEEPALIVE void set_post_processing_render_thread(TViewer *viewer, bool enabled);
EMSCRIPTEN_KEEPALIVE void reset_to_rest_pose_render_thread(TSceneManager *sceneManager, EntityId entityId, void(*callback)());
EMSCRIPTEN_KEEPALIVE void create_geometry_render_thread(
TSceneManager *sceneManager,
float *vertices,
int numVertices,
@@ -102,7 +102,7 @@ extern "C"
TMaterialInstance *materialInstance,
bool keepData,
void (*callback)(EntityId));
void unproject_texture_render_thread(TViewer* viewer, EntityId entity, uint8_t* input, uint32_t inputWidth, uint32_t inputHeight, uint8_t* out, uint32_t outWidth, uint32_t outHeight, void(*callback)());
EMSCRIPTEN_KEEPALIVE void unproject_texture_render_thread(TViewer* viewer, EntityId entity, uint8_t* input, uint32_t inputWidth, uint32_t inputHeight, uint8_t* out, uint32_t outWidth, uint32_t outHeight, void(*callback)());
#ifdef __cplusplus

View File

@@ -0,0 +1,36 @@
#pragma once
#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "shell32.lib")
#pragma comment(lib, "dwmapi.lib")
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "filament.lib")
#pragma comment(lib, "bluevk.lib")
#pragma comment(lib, "bluegl.lib")
#pragma comment(lib, "backend.lib")
#pragma comment(lib, "filameshio.lib")
#pragma comment(lib, "viewer.lib")
#pragma comment(lib, "filamat.lib")
#pragma comment(lib, "geometry.lib")
#pragma comment(lib, "utils.lib")
#pragma comment(lib, "filabridge.lib")
#pragma comment(lib, "gltfio_core.lib")
#pragma comment(lib, "filament-iblprefilter.lib")
#pragma comment(lib, "image.lib")
#pragma comment(lib, "imageio.lib")
#pragma comment(lib, "tinyexr.lib")
#pragma comment(lib, "filaflat.lib")
#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")
#pragma comment(lib, "smol-v.lib")
#pragma comment(lib, "uberarchive.lib")
#pragma comment(lib, "zstd.lib")
#pragma comment(lib, "basis_transcoder.lib")

View File

@@ -767,7 +767,7 @@ namespace thermion
// bloom can be a bit glitchy (some Intel iGPUs won't render when postprocessing is enabled and bloom is disabled,
// and render targets on MacOS flicker when bloom is disabled.
// Here, we enable bloom, but set to 0 strength
view->setBloomOptions({.enabled=true, .strength = 0});
view->setBloomOptions({.strength = 0, .enabled=true });
view->setShadowingEnabled(false);
view->setScreenSpaceRefractionEnabled(false);
view->setPostProcessingEnabled(false);
@@ -1051,8 +1051,8 @@ namespace thermion
_renderer->render(view);
}
}
_renderer->endFrame();
}
_renderer->endFrame();
}
#ifdef __EMSCRIPTEN__
_engine->execute();

View File

@@ -1,3 +1,8 @@
#ifdef _WIN32
#define _USE_MATH_DEFINES
#include <cmath>
#endif
#include "GridOverlay.hpp"
#include <filament/Engine.h>

View File

@@ -1,14 +1,7 @@
#ifdef _WIN32
#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "opengl32.lib")
#include "ThermionWin32.h"
#endif
#include "ResourceBuffer.hpp"
#include "FilamentViewer.hpp"
#include "filament/LightManager.h"
#include "Log.hpp"
#include "ThreadPool.hpp"
#include <thread>
#include <functional>
@@ -16,6 +9,12 @@
#include <emscripten/emscripten.h>
#endif
#include "filament/LightManager.h"
#include "ResourceBuffer.hpp"
#include "FilamentViewer.hpp"
#include "Log.hpp"
#include "ThreadPool.hpp"
using namespace thermion;
extern "C"
@@ -93,7 +92,7 @@ extern "C"
((FilamentViewer *)viewer)->createIbl(r, g, b, intensity);
}
EMSCRIPTEN_KEEPALIVE void load_ibl(TViewer *viewer, const char *iblPath, float intensity)
EMSCRIPTEN_KEEPALIVE void Viewer_loadIbl(TViewer *viewer, const char *iblPath, float intensity)
{
((FilamentViewer *)viewer)->loadIbl(iblPath, intensity);
}
@@ -230,62 +229,62 @@ extern "C"
return reinterpret_cast<TCamera *>(filamentCamera);
}
double4x4 get_camera_model_matrix(TCamera *camera)
EMSCRIPTEN_KEEPALIVE double4x4 get_camera_model_matrix(TCamera *camera)
{
const auto &mat = reinterpret_cast<filament::Camera *>(camera)->getModelMatrix();
return convert_mat4_to_double4x4(mat);
}
double4x4 get_camera_view_matrix(TCamera *camera)
EMSCRIPTEN_KEEPALIVE double4x4 get_camera_view_matrix(TCamera *camera)
{
const auto &mat = reinterpret_cast<filament::Camera *>(camera)->getViewMatrix();
return convert_mat4_to_double4x4(mat);
}
double4x4 get_camera_projection_matrix(TCamera *camera)
EMSCRIPTEN_KEEPALIVE double4x4 get_camera_projection_matrix(TCamera *camera)
{
const auto &mat = reinterpret_cast<filament::Camera *>(camera)->getProjectionMatrix();
return convert_mat4_to_double4x4(mat);
}
double4x4 get_camera_culling_projection_matrix(TCamera *camera)
EMSCRIPTEN_KEEPALIVE double4x4 get_camera_culling_projection_matrix(TCamera *camera)
{
const auto &mat = reinterpret_cast<filament::Camera *>(camera)->getCullingProjectionMatrix();
return convert_mat4_to_double4x4(mat);
}
void set_camera_projection_matrix(TCamera *camera, double4x4 matrix, double near, double far)
EMSCRIPTEN_KEEPALIVE void set_camera_projection_matrix(TCamera *camera, double4x4 matrix, double near, double far)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
const auto &mat = convert_double4x4_to_mat4(matrix);
cam->setCustomProjection(mat, near, far);
}
void Camera_setLensProjection(TCamera *camera, double near, double far, double aspect, double focalLength)
EMSCRIPTEN_KEEPALIVE void Camera_setLensProjection(TCamera *camera, double near, double far, double aspect, double focalLength)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
cam->setLensProjection(focalLength, aspect, near, far);
}
void Camera_setModelMatrix(TCamera *camera, double4x4 matrix)
EMSCRIPTEN_KEEPALIVE void Camera_setModelMatrix(TCamera *camera, double4x4 matrix)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
cam->setModelMatrix(convert_double4x4_to_mat4(matrix));
}
double get_camera_near(TCamera *camera)
EMSCRIPTEN_KEEPALIVE double get_camera_near(TCamera *camera)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
return cam->getNear();
}
double get_camera_culling_far(TCamera *camera)
EMSCRIPTEN_KEEPALIVE double get_camera_culling_far(TCamera *camera)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
return cam->getCullingFar();
}
const double *const get_camera_frustum(TCamera *camera)
EMSCRIPTEN_KEEPALIVE const double *const get_camera_frustum(TCamera *camera)
{
const auto frustum = reinterpret_cast<filament::Camera *>(camera)->getFrustum();
@@ -304,7 +303,6 @@ extern "C"
return array;
}
EMSCRIPTEN_KEEPALIVE void set_camera_focus_distance(TCamera *camera, float distance)
{
@@ -703,8 +701,9 @@ extern "C"
EMSCRIPTEN_KEEPALIVE void SceneManager_queueTransformUpdates(TSceneManager *tSceneManager, EntityId *entities, const double *const transforms, int numEntities)
{
auto *sceneManager = reinterpret_cast<SceneManager *>(tSceneManager);
math::mat4 matrices[
numEntities];
std::vector<math::mat4> matrices(
numEntities);
for (int i = 0; i < numEntities; i++)
{
matrices[i] = math::mat4(
@@ -723,7 +722,7 @@ extern "C"
transforms[i * 16 + 14],
transforms[i * 16 + 15]);
}
sceneManager->queueTransformUpdates(entities, matrices, numEntities);
sceneManager->queueTransformUpdates(entities, matrices.data(), numEntities);
}
EMSCRIPTEN_KEEPALIVE bool update_bone_matrices(TSceneManager *sceneManager, EntityId entityId)
@@ -955,10 +954,10 @@ extern "C"
EMSCRIPTEN_KEEPALIVE void set_material_property_float4(TSceneManager *sceneManager, EntityId entity, int materialIndex, const char *property, double4 value)
{
filament::math::float4 filamentValue;
filamentValue.x = static_cast<float32_t>(value.x);
filamentValue.y = static_cast<float32_t>(value.y);
filamentValue.z = static_cast<float32_t>(value.z);
filamentValue.w = static_cast<float32_t>(value.w);
filamentValue.x = static_cast<float>(value.x);
filamentValue.y = static_cast<float>(value.y);
filamentValue.z = static_cast<float>(value.z);
filamentValue.w = static_cast<float>(value.w);
((SceneManager *)sceneManager)->setMaterialProperty(entity, materialIndex, property, filamentValue);
}

View File

@@ -184,7 +184,7 @@ extern "C"
static RenderLoop *_rl;
void Viewer_createOnRenderThread(
EMSCRIPTEN_KEEPALIVE void Viewer_createOnRenderThread(
void *const context, void *const platform, const char *uberArchivePath,
const void *const loader,
void (*renderCallback)(void *const renderCallbackOwner),
@@ -200,14 +200,14 @@ extern "C"
renderCallback, renderCallbackOwner, callback);
}
void destroy_filament_viewer_render_thread(TViewer *viewer)
EMSCRIPTEN_KEEPALIVE void destroy_filament_viewer_render_thread(TViewer *viewer)
{
_rl->destroyViewer((FilamentViewer *)viewer);
delete _rl;
_rl = nullptr;
}
void Viewer_createHeadlessSwapChainRenderThread(TViewer *viewer,
EMSCRIPTEN_KEEPALIVE void Viewer_createHeadlessSwapChainRenderThread(TViewer *viewer,
uint32_t width,
uint32_t height,
void (*onComplete)(TSwapChain*))
@@ -221,7 +221,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void Viewer_createSwapChainRenderThread(TViewer *viewer,
EMSCRIPTEN_KEEPALIVE void Viewer_createSwapChainRenderThread(TViewer *viewer,
void *const surface,
void (*onComplete)(TSwapChain*))
{
@@ -234,7 +234,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void Viewer_destroySwapChainRenderThread(TViewer *viewer, TSwapChain *swapChain, void (*onComplete)())
EMSCRIPTEN_KEEPALIVE void Viewer_destroySwapChainRenderThread(TViewer *viewer, TSwapChain *swapChain, void (*onComplete)())
{
std::packaged_task<void()> lambda(
[=]() mutable
@@ -246,7 +246,7 @@ extern "C"
}
void Viewer_requestFrameRenderThread(TViewer *viewer, void(*onComplete)())
EMSCRIPTEN_KEEPALIVE void Viewer_requestFrameRenderThread(TViewer *viewer, void(*onComplete)())
{
if (!_rl)
{
@@ -258,7 +258,17 @@ extern "C"
}
}
void
EMSCRIPTEN_KEEPALIVE void Viewer_loadIblRenderThread(TViewer *viewer, const char *iblPath, float intensity, void(*onComplete)()) {
std::packaged_task<void()> lambda(
[=]() mutable
{
Viewer_loadIbl(viewer, iblPath, intensity);
onComplete();
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void
set_frame_interval_render_thread(TViewer *viewer, float frameIntervalInMilliseconds)
{
_rl->setFrameIntervalInMilliseconds(frameIntervalInMilliseconds);
@@ -267,7 +277,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void Viewer_renderRenderThread(TViewer *viewer, TView *tView, TSwapChain *tSwapChain)
EMSCRIPTEN_KEEPALIVE void Viewer_renderRenderThread(TViewer *viewer, TView *tView, TSwapChain *tSwapChain)
{
std::packaged_task<void()> lambda([=]() mutable
{
@@ -276,21 +286,21 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void Viewer_captureRenderThread(TViewer *viewer, TView *view, TSwapChain *tSwapChain, uint8_t *pixelBuffer, void (*onComplete)())
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderThread(TViewer *viewer, TView *view, TSwapChain *tSwapChain, uint8_t *pixelBuffer, void (*onComplete)())
{
std::packaged_task<void()> lambda([=]() mutable
{ Viewer_capture(viewer, view, tSwapChain, pixelBuffer, onComplete); });
auto fut = _rl->add_task(lambda);
}
void Viewer_captureRenderTargetRenderThread(TViewer *viewer, TView *view, TSwapChain *tSwapChain, TRenderTarget* tRenderTarget, uint8_t *pixelBuffer, void (*onComplete)())
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderTargetRenderThread(TViewer *viewer, TView *view, TSwapChain *tSwapChain, TRenderTarget* tRenderTarget, uint8_t *pixelBuffer, void (*onComplete)())
{
std::packaged_task<void()> lambda([=]() mutable
{ Viewer_captureRenderTarget(viewer, view, tSwapChain, tRenderTarget, pixelBuffer, onComplete); });
auto fut = _rl->add_task(lambda);
}
void
EMSCRIPTEN_KEEPALIVE void
set_background_color_render_thread(TViewer *viewer, const float r, const float g,
const float b, const float a)
{
@@ -300,7 +310,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void load_gltf_render_thread(TSceneManager *sceneManager,
EMSCRIPTEN_KEEPALIVE void load_gltf_render_thread(TSceneManager *sceneManager,
const char *path,
const char *relativeResourcePath,
bool keepData,
@@ -314,7 +324,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void load_glb_render_thread(TSceneManager *sceneManager,
EMSCRIPTEN_KEEPALIVE void load_glb_render_thread(TSceneManager *sceneManager,
const char *path,
int numInstances,
bool keepData,
@@ -330,7 +340,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void SceneManager_loadGlbFromBufferRenderThread(TSceneManager *sceneManager,
EMSCRIPTEN_KEEPALIVE void SceneManager_loadGlbFromBufferRenderThread(TSceneManager *sceneManager,
const uint8_t *const data,
size_t length,
int numInstances,
@@ -350,14 +360,14 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void clear_background_image_render_thread(TViewer *viewer)
EMSCRIPTEN_KEEPALIVE void clear_background_image_render_thread(TViewer *viewer)
{
std::packaged_task<void()> lambda([=]
{ clear_background_image(viewer); });
auto fut = _rl->add_task(lambda);
}
void set_background_image_render_thread(TViewer *viewer,
EMSCRIPTEN_KEEPALIVE void set_background_image_render_thread(TViewer *viewer,
const char *path,
bool fillHeight, void (*callback)())
{
@@ -369,7 +379,8 @@ extern "C"
});
auto fut = _rl->add_task(lambda);
}
void set_background_image_position_render_thread(TViewer *viewer,
EMSCRIPTEN_KEEPALIVE void set_background_image_position_render_thread(TViewer *viewer,
float x, float y,
bool clamp)
{
@@ -379,7 +390,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void load_skybox_render_thread(TViewer *viewer,
EMSCRIPTEN_KEEPALIVE void load_skybox_render_thread(TViewer *viewer,
const char *skyboxPath,
void (*onComplete)())
{
@@ -390,30 +401,22 @@ extern "C"
});
auto fut = _rl->add_task(lambda);
}
void load_ibl_render_thread(TViewer *viewer, const char *iblPath,
float intensity)
{
std::packaged_task<void()> lambda(
[=]
{ load_ibl(viewer, iblPath, intensity); });
auto fut = _rl->add_task(lambda);
}
void remove_skybox_render_thread(TViewer *viewer)
EMSCRIPTEN_KEEPALIVE void remove_skybox_render_thread(TViewer *viewer)
{
std::packaged_task<void()> lambda([=]
{ remove_skybox(viewer); });
auto fut = _rl->add_task(lambda);
}
void remove_ibl_render_thread(TViewer *viewer)
EMSCRIPTEN_KEEPALIVE void remove_ibl_render_thread(TViewer *viewer)
{
std::packaged_task<void()> lambda([=]
{ remove_ibl(viewer); });
auto fut = _rl->add_task(lambda);
}
void remove_entity_render_thread(TViewer *viewer,
EMSCRIPTEN_KEEPALIVE void remove_entity_render_thread(TViewer *viewer,
EntityId asset, void (*callback)())
{
std::packaged_task<void()> lambda([=]
@@ -424,7 +427,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void clear_entities_render_thread(TViewer *viewer, void (*callback)())
EMSCRIPTEN_KEEPALIVE void clear_entities_render_thread(TViewer *viewer, void (*callback)())
{
std::packaged_task<void()> lambda([=]
{
@@ -435,7 +438,7 @@ extern "C"
}
void
EMSCRIPTEN_KEEPALIVE void
get_morph_target_name_render_thread(TSceneManager *sceneManager, EntityId assetEntity,
EntityId childEntity, char *const outPtr, int index, void (*callback)())
{
@@ -447,7 +450,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void
EMSCRIPTEN_KEEPALIVE void
get_morph_target_name_count_render_thread(TSceneManager *sceneManager, EntityId assetEntity,
EntityId childEntity, void (*callback)(int))
{
@@ -459,7 +462,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void set_animation_frame_render_thread(TSceneManager *sceneManager,
EMSCRIPTEN_KEEPALIVE void set_animation_frame_render_thread(TSceneManager *sceneManager,
EntityId asset,
int animationIndex,
int animationFrame)
@@ -469,7 +472,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void stop_animation_render_thread(TSceneManager *sceneManager,
EMSCRIPTEN_KEEPALIVE void stop_animation_render_thread(TSceneManager *sceneManager,
EntityId asset, int index)
{
std::packaged_task<void()> lambda(
@@ -478,7 +481,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void get_animation_count_render_thread(TSceneManager *sceneManager,
EMSCRIPTEN_KEEPALIVE void get_animation_count_render_thread(TSceneManager *sceneManager,
EntityId asset,
void (*callback)(int))
{
@@ -492,7 +495,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void get_animation_name_render_thread(TSceneManager *sceneManager,
EMSCRIPTEN_KEEPALIVE void get_animation_name_render_thread(TSceneManager *sceneManager,
EntityId asset,
char *const outPtr,
int index,
@@ -507,7 +510,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void
EMSCRIPTEN_KEEPALIVE void
get_name_for_entity_render_thread(TSceneManager *sceneManager, const EntityId entityId, void (*callback)(const char *))
{
std::packaged_task<const char *()> lambda(
@@ -520,7 +523,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void set_morph_target_weights_render_thread(TSceneManager *sceneManager,
EMSCRIPTEN_KEEPALIVE void set_morph_target_weights_render_thread(TSceneManager *sceneManager,
EntityId asset,
const float *const morphData,
int numWeights,
@@ -535,7 +538,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void set_bone_transform_render_thread(
EMSCRIPTEN_KEEPALIVE void set_bone_transform_render_thread(
TSceneManager *sceneManager,
EntityId asset,
int skinIndex,
@@ -553,7 +556,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void update_bone_matrices_render_thread(TSceneManager *sceneManager,
EMSCRIPTEN_KEEPALIVE void update_bone_matrices_render_thread(TSceneManager *sceneManager,
EntityId entity, void (*callback)(bool))
{
std::packaged_task<void()> lambda(
@@ -565,7 +568,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void View_setToneMappingRenderThread(TView *tView, TEngine *tEngine, thermion::ToneMapping toneMapping) {
EMSCRIPTEN_KEEPALIVE void View_setToneMappingRenderThread(TView *tView, TEngine *tEngine, thermion::ToneMapping toneMapping) {
std::packaged_task<void()> lambda(
[=]
{
@@ -574,7 +577,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void View_setBloomRenderThread(TView *tView, double bloom) {
EMSCRIPTEN_KEEPALIVE void View_setBloomRenderThread(TView *tView, double bloom) {
std::packaged_task<void()> lambda(
[=]
{
@@ -583,7 +586,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void reset_to_rest_pose_render_thread(TSceneManager *sceneManager, EntityId entityId, void (*callback)())
EMSCRIPTEN_KEEPALIVE void reset_to_rest_pose_render_thread(TSceneManager *sceneManager, EntityId entityId, void (*callback)())
{
std::packaged_task<void()> lambda(
[=]
@@ -594,7 +597,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void create_geometry_render_thread(
EMSCRIPTEN_KEEPALIVE void create_geometry_render_thread(
TSceneManager *sceneManager,
float *vertices,
int numVertices,
@@ -619,7 +622,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
void unproject_texture_render_thread(TViewer* viewer, EntityId entity, uint8_t *input, uint32_t inputWidth, uint32_t inputHeight, uint8_t *out, uint32_t outWidth, uint32_t outHeight, void (*callback)())
EMSCRIPTEN_KEEPALIVE void unproject_texture_render_thread(TViewer* viewer, EntityId entity, uint8_t *input, uint32_t inputWidth, uint32_t inputHeight, uint8_t *out, uint32_t outWidth, uint32_t outHeight, void (*callback)())
{
std::packaged_task<void()> lambda(
[=]

View File

@@ -177,6 +177,8 @@ namespace filament
case Mode::ORBIT:
return new OrbitManipulator<FLOAT>(mode, details);
}
// just to make MSVC happy
return new OrbitManipulator<FLOAT>(mode, details);
}
template <typename FLOAT>

View File

@@ -1,6 +1,6 @@
name: thermion_dart
description: 3D rendering toolkit for Dart.
version: 0.2.1-dev.0.0.6
version: 0.2.1-dev.0.0.8
homepage: https://thermion.dev
repository: https://github.com/nmfisher/thermion
@@ -13,11 +13,11 @@ dependencies:
ffi: ^2.1.2
animation_tools_dart: ^0.1.0
native_toolchain_c: ^0.4.2
native_assets_cli: ^0.6.1
archive: ^3.6.1
web: ^1.0.0
logging: ^1.2.0
http: ^1.2.2
native_assets_cli: ^0.6.1
dev_dependencies:
ffigen: ^13.0.0

View File

@@ -1,3 +1,11 @@
## 0.2.1-dev.7
- Update a dependency to the latest release.
## 0.2.1-dev.6
- Update a dependency to the latest release.
## 0.2.1-dev.5
- Update a dependency to the latest release.

View File

@@ -110,7 +110,7 @@ class _ThermionListenerWidgetState extends State<ThermionListenerWidget> {
Widget _mobile(double pixelRatio) {
return _MobileListenerWidget(
gestureHandler: widget.gestureHandler, pixelRatio: pixelRatio);
gestureHandler: widget.gestureHandler, pixelRatio: pixelRatio, child:widget.child);
}
@override
@@ -135,8 +135,10 @@ class _ThermionListenerWidgetState extends State<ThermionListenerWidget> {
class _MobileListenerWidget extends StatefulWidget {
final InputHandler gestureHandler;
final double pixelRatio;
final Widget? child;
const _MobileListenerWidget({Key? key, required this.gestureHandler, required this.pixelRatio})
const _MobileListenerWidget(
{Key? key, required this.gestureHandler, required this.pixelRatio, this.child})
: super(key: key);
@override
@@ -154,22 +156,29 @@ class _MobileListenerWidgetState extends State<_MobileListenerWidget> {
@override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onTapDown: (details) => widget.gestureHandler
.onPointerDown(details.localPosition.toVector2() * widget.pixelRatio, false),
onDoubleTap: () {
widget.gestureHandler.setActionForType(InputType.SCALE1,
isPan ? InputAction.TRANSLATE : InputAction.ROTATE);
},
onScaleStart: (details) async {
await widget.gestureHandler.onScaleStart();
},
onScaleUpdate: (details) async {
await widget.gestureHandler.onScaleUpdate();
},
onScaleEnd: (details) async {
await widget.gestureHandler.onScaleUpdate();
},
);
behavior: HitTestBehavior.translucent,
onTapDown: (details) => widget.gestureHandler.onPointerDown(
details.localPosition.toVector2() * widget.pixelRatio, false),
onDoubleTap: () {
widget.gestureHandler.setActionForType(InputType.SCALE1,
isPan ? InputAction.TRANSLATE : InputAction.ROTATE);
},
onScaleStart: (details) async {
await widget.gestureHandler.onScaleStart(
details.localFocalPoint.toVector2(), details.pointerCount);
},
onScaleUpdate: (ScaleUpdateDetails details) async {
await widget.gestureHandler.onScaleUpdate(
details.localFocalPoint.toVector2(),
details.focalPointDelta.toVector2(),
details.horizontalScale,
details.verticalScale,
details.scale,
details.pointerCount);
},
onScaleEnd: (details) async {
await widget.gestureHandler.onScaleEnd(details.pointerCount);
},
child: widget.child);
}
}

View File

@@ -95,7 +95,7 @@ class _ThermionWidgetState extends State<ThermionWidget> {
}
if (Platform.isWindows) {
return ThermionWidgetWindows(viewer: widget.viewer);
return ThermionWidgetWindows(viewer: widget.viewer, view: view!, initial: widget.initial, onResize: widget.onResize);
}
return ThermionTextureWidget(

View File

@@ -1,14 +1,168 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:thermion_flutter/thermion_flutter.dart';
import 'package:thermion_flutter/src/widgets/src/resize_observer.dart';
import 'package:thermion_flutter/src/widgets/src/transparent_filament_widget.dart';
import 'package:thermion_flutter/thermion_flutter.dart' as t;
import 'package:thermion_flutter_platform_interface/thermion_flutter_window.dart';
class ThermionWidgetWindows extends StatelessWidget {
class ThermionWidgetWindows extends StatefulWidget {
final ThermionViewer viewer;
final t.ThermionViewer viewer;
final t.View view;
///
///
///
final Widget? initial;
///
/// A callback that will be invoked whenever this widget (and the underlying texture is resized).
///
final Future Function(Size size, t.View view, double pixelRatio)? onResize;
const ThermionWidgetWindows({super.key, required this.viewer, this.initial, this.onResize, required this.view});
@override
State<StatefulWidget> createState() => _ThermionWidgetWindowsState();
}
class _ThermionWidgetWindowsState extends State<ThermionWidgetWindows> {
ThermionFlutterWindow? _window;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await widget.viewer.initialized;
var dpr = MediaQuery.of(context).devicePixelRatio;
final renderBox = ((context.findRenderObject()) as RenderBox);
var size = renderBox.size;
var width = (size.width * dpr).ceil();
var height = (size.height * dpr).ceil();
final offset = renderBox.localToGlobal(Offset.zero);
final offsetLeft = (offset.dx * dpr).toInt();
final offsetTop = (offset.dy * dpr).toInt();
_window = await t.ThermionFlutterPlatform.instance.createWindow(width, height, offsetLeft, offsetTop);
await widget.view.updateViewport(_window!.width, _window!.height);
try {
await widget.onResize?.call(
Size(_window!.width.toDouble(), _window!.height.toDouble()),
widget.view,
dpr);
} catch (err, st) {
print(err);
print(st);
}
if (mounted) {
setState(() {});
}
_requestFrame();
widget.viewer.onDispose(() async {
var window = _window;
if (mounted) {
setState(() {});
}
await window?.destroy();
});
});
}
bool _rendering = false;
void _requestFrame() {
WidgetsBinding.instance.scheduleFrameCallback((d) async {
if (widget.viewer.rendering && !_rendering) {
_rendering = true;
await widget.viewer.requestFrame();
_rendering = false;
}
_requestFrame();
});
}
final _resizing = <Future>[];
Timer? _resizeTimer;
Future _resize(Size oldSize, Size newSize) async {
await Future.wait(_resizing);
_resizeTimer?.cancel();
_resizeTimer = Timer(const Duration(milliseconds: 100), () async {
await Future.wait(_resizing);
if (!mounted) {
return;
}
if (newSize.width == _window?.width &&
newSize.height == _window?.height) {
return;
}
final completer = Completer();
_resizing.add(completer.future);
final dpr = MediaQuery.of(context).devicePixelRatio;
newSize *= dpr;
var newWidth = newSize.width.ceil();
var newHeight = newSize.height.ceil();
final renderBox = context.findRenderObject() as RenderBox;
final offset = renderBox.localToGlobal(Offset.zero);
final offsetLeft = (offset.dx * dpr).toInt();
final offsetTop = (offset.dy * dpr).toInt();
await _window?.resize(
newWidth,
newHeight,
offsetLeft,
offsetTop,
);
await widget.view.updateViewport(_window!.width, _window!.height);
await widget.onResize?.call(
Size(_window!.width.toDouble(), _window!.height.toDouble()),
widget.view,
dpr);
if (!mounted) {
return;
}
setState(() {});
completer.complete();
_resizing.remove(completer.future);
});
}
const ThermionWidgetWindows({super.key, required this.viewer});
@override
Widget build(BuildContext context) {
// TODO: implement build
throw UnimplementedError();
if (_window == null) {
return widget.initial ?? Container(color: Colors.red);
}
return ResizeObserver(
onResized: _resize,
child: CustomPaint(painter:TransparencyPainter()));
}
}

View File

@@ -1,6 +1,6 @@
name: thermion_flutter
description: Flutter plugin for 3D rendering with the Thermion toolkit.
version: 0.2.1-dev.5
version: 0.2.1-dev.7
homepage: https://thermion.dev
repository: https://github.com/nmfisher/thermion
@@ -17,13 +17,19 @@ dependencies:
plugin_platform_interface: ^2.0.0
ffi: ^2.1.2
animation_tools_dart: ^0.1.0
thermion_dart: ^0.2.1-dev.0.0.6
thermion_flutter_platform_interface: ^0.2.1-dev.5
thermion_flutter_ffi: ^0.2.1-dev.5
thermion_flutter_web: ^0.1.0+7
thermion_dart: ^0.2.1-dev.0.0.8
thermion_flutter_platform_interface: ^0.2.1-dev.7
thermion_flutter_ffi: ^0.2.1-dev.7
thermion_flutter_web: ^0.1.0+9
logging: ^1.2.0
web: ^1.0.0
dependency_overrides:
thermion_dart:
path: ../../thermion_dart
thermion_flutter_platform_interface:
path: ../thermion_flutter_platform_interface
thermion_flutter_ffi:
path: ../thermion_flutter_ffi
dev_dependencies:
flutter_test:
sdk: flutter

View File

@@ -152,8 +152,20 @@ LRESULT CALLBACK FilamentWindowProc(HWND const window, UINT const message,
break;
}
case WM_ERASEBKGND: {
// Prevent erasing of |window| when it is unfocused and minimized or
// moved out of screen etc.
HDC hdc = (HDC)wparam;
RECT rect;
GetClientRect(window, &rect);
// Get the BackingWindow instance associated with this window
BackingWindow* backing_window = reinterpret_cast<BackingWindow*>(
GetWindowLongPtr(window, GWLP_USERDATA));
if (backing_window) {
HBRUSH brush = CreateSolidBrush(RGB(0, 255, 0));
FillRect(hdc, &rect, brush);
DeleteObject(brush);
}
break;
}
case WM_SIZE:
@@ -346,8 +358,6 @@ void BackingWindow::Resize(int width, int height, int left, int top) {
_top = top;
RECT flutterViewRect;
::GetWindowRect(_flutterViewWindow, &flutterViewRect);
std::cout << "Resizing to " << _width << " x " << _height << " with LT" << _left << " " << _top << " flutter view rect" << flutterViewRect.left << " " << flutterViewRect.top << " " << flutterViewRect.right << " " << flutterViewRect.bottom << std::endl;
::SetWindowPos(_windowHandle, _flutterRootWindow, flutterViewRect.left + _left,
flutterViewRect.top + _top, _width, _height,
SWP_NOACTIVATE);

View File

@@ -172,8 +172,6 @@ void ThermionFlutterPlugin::CreateTexture(
auto height = (uint32_t)round(dHeight );
auto left = (uint32_t)round(dLeft );
auto top = (uint32_t)round(dTop );
std::cout << "Using " << width << "x" << height << std::endl;
// create a single shared context for the life of the application
// this will be used to create a backing texture and passed to Filament
@@ -182,8 +180,10 @@ void ThermionFlutterPlugin::CreateTexture(
_context = std::make_unique<FlutterEGLContext>(_pluginRegistrar, _textureRegistrar);
#else
_context = std::make_unique<WGLContext>(_pluginRegistrar, _textureRegistrar);
std::cout << "Created WGL context" << std::endl;
#endif
}
_context->CreateRenderingSurface(width, height, std::move(result), left, top);
}
@@ -210,7 +210,7 @@ void ThermionFlutterPlugin::DestroyTexture(
void ThermionFlutterPlugin::HandleMethodCall(
const flutter::MethodCall<flutter::EncodableValue> &methodCall,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
// std::cout << methodCall.method_name().c_str() << std::endl;
if (methodCall.method_name() == "usesBackingWindow") {
result->Success(flutter::EncodableValue(
#ifdef WGL_USE_BACKING_WINDOW
@@ -239,18 +239,19 @@ void ThermionFlutterPlugin::HandleMethodCall(
int dHeight = *(std::get_if<int>(&(args->at(1))));
int dLeft = *(std::get_if<int>(&(args->at(2))));
int dTop = *(std::get_if<int>(&(args->at(3))));
auto width = (uint32_t)round(dWidth );
auto height = (uint32_t)round(dHeight );
auto left = (uint32_t)round(dLeft );
auto top = (uint32_t)round(dTop );
auto width = static_cast<uint32_t>(dWidth);
auto height = static_cast<uint32_t>(dHeight);
auto left = static_cast<uint32_t>(dLeft);
auto top = static_cast<uint32_t>(dTop );
_context->ResizeRenderingSurface(width, height, left, top);
result->Success();
#else
result->Error("ERROR", "resizeWindow is only available when using a backing window");
#endif
} else if (methodCall.method_name() == "createTexture") {
} else if (methodCall.method_name() == "createWindow") {
CreateTexture(methodCall, std::move(result));
} else if (methodCall.method_name() == "destroyTexture") {
} else if (methodCall.method_name() == "destroyWindow") {
DestroyTexture(methodCall, std::move(result));
} else if (methodCall.method_name() == "getRenderCallback") {
flutter::EncodableList resultList;

View File

@@ -45,7 +45,7 @@ WGLContext::WGLContext(flutter::PluginRegistrarWindows *pluginRegistrar,
0,
0,
0,
16, // Number of bits for the depthbuffer
24, // Number of bits for the depthbuffer
0, // Number of bits for the stencilbuffer
0, // Number of Aux buffers in the framebuffer.
PFD_MAIN_PLANE,
@@ -117,6 +117,8 @@ void WGLContext::CreateRenderingSurface(
} else {
ResizeRenderingSurface(width, height, left, top);
}
// std::cout << "created window size " << width << "x" << height << " at " << left << "," << top << " with backing handle" << _backingWindow->GetHandle() << std::endl;
std::vector<flutter::EncodableValue> resultList;
resultList.push_back(flutter::EncodableValue()); // return null for Flutter texture ID
resultList.push_back(flutter::EncodableValue()); // return null for hardware texture ID

View File

@@ -1,3 +1,11 @@
## 0.2.1-dev.7
- Update a dependency to the latest release.
## 0.2.1-dev.6
- Update a dependency to the latest release.
## 0.2.1-dev.5
- Update a dependency to the latest release.

View File

@@ -7,16 +7,19 @@ import 'package:thermion_flutter_ffi/thermion_flutter_method_channel_interface.d
import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart';
import 'package:logging/logging.dart';
import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart';
import 'package:thermion_flutter_platform_interface/thermion_flutter_window.dart';
import 'platform_texture.dart';
///
/// An implementation of [ThermionFlutterPlatform] that uses
/// Flutter platform channels to create a rendering context,
/// resource loaders, and surface/render target(s).
/// resource loaders, and a texture that will be used as a render target
/// for a headless swapchain.
///
class ThermionFlutterTextureBackedPlatform
extends ThermionFlutterMethodChannelInterface {
final _logger = Logger("ThermionFlutterTextureBackedPlatform");
static SwapChain? _swapChain;
@@ -52,11 +55,10 @@ class ThermionFlutterTextureBackedPlatform
await texture.resize(width, height, 0, 0);
return texture;
}
// On MacOS, we currently use textures/render targets, so there's no window to resize
@override
Future<ThermionFlutterTexture?> resizeWindow(
int width, int height, int offsetTop, int offsetRight) {
Future<ThermionFlutterWindow> createWindow(int width, int height, int offsetLeft, int offsetTop) {
// TODO: implement createWindow
throw UnimplementedError();
}
}

View File

@@ -1,34 +1,45 @@
import 'dart:async';
import 'package:flutter/services.dart';
import 'dart:ffi';
import 'package:thermion_dart/thermion_dart.dart';
import 'package:thermion_dart/src/viewer/src/ffi/thermion_viewer_ffi.dart';
import 'package:thermion_flutter_ffi/thermion_flutter_method_channel_interface.dart';
import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart';
import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart';
import 'package:logging/logging.dart';
import 'package:thermion_flutter_platform_interface/thermion_flutter_window.dart';
///
/// An implementation of [ThermionFlutterPlatform] that uses
/// Flutter platform channels to create a rendering context,
/// resource loaders, and surface/render target(s).
/// A Windows-only implementation of [ThermionFlutterPlatform] that uses
/// a Flutter platform channel to create a rendering context,
/// resource loader and a native HWND that will be sit behind the running
/// Flutter application.
///
class ThermionFlutterWindows
extends ThermionFlutterMethodChannelInterface {
final _channel = const MethodChannel("dev.thermion.flutter/event");
final _logger = Logger("ThermionFlutterWindows");
ThermionViewerFFI? _viewer;
ThermionFlutterWindows._() {}
ThermionViewer? _viewer;
SwapChain? _swapChain;
ThermionFlutterWindows._() {}
static void registerWith() {
ThermionFlutterPlatform.instance = ThermionFlutterWindows._();
}
@override
Future<ThermionViewer> createViewer({ThermionFlutterOptions? options}) async {
if(_viewer != null) {
throw Exception("Only one viewer should be instantiated over the life of the app");
}
_viewer = await super.createViewer(options: options);
return _viewer!;
}
///
/// Not supported on Windows. Throws an exception.
///
@@ -37,64 +48,85 @@ class ThermionFlutterWindows
throw UnimplementedError();
}
bool _resizing = false;
@override
Future<ThermionFlutterWindow> createWindow(int width, int height, int offsetLeft, int offsetTop) async {
var result = await _channel
.invokeMethod("createWindow", [width, height, offsetLeft, offsetLeft]);
if (result == null || result[2] == -1) {
throw Exception("Failed to create window");
}
var window =
ThermionFlutterWindowImpl(result[2], _channel, viewer!);
await window.resize(width, height, offsetLeft, offsetTop);
var view = await _viewer!.getViewAt(0);
await view.updateViewport(width, height);
_swapChain = await _viewer!.createSwapChain(window.handle);
await view.setRenderable(true, _swapChain!);
return window;
}
}
class ThermionFlutterWindowImpl extends ThermionFlutterWindow {
final ThermionViewer viewer;
final int handle;
int height = 0;
int width = 0;
int offsetLeft = 0;
int offsetTop = 0;
final MethodChannel _channel;
ThermionFlutterWindowImpl(this.handle, this._channel, this.viewer);
@override
Future destroy() async {
await _channel
.invokeMethod("destroyWindow", this.handle);
}
@override
Future markFrameAvailable() {
// TODO: implement markFrameAvailable
throw UnimplementedError();
}
bool _resizing = false;
///
/// Called by [ThermionWidget] to resize a texture. Don't call this yourself.
/// Called by [ThermionWidget] to resize the window. Don't call this yourself.
///
@override
Future resizeWindow(
Future resize(
int width, int height, int offsetLeft, int offsetTop) async {
if (_resizing) {
throw Exception("Resize underway");
}
throw Exception("TODO");
if (width == this.width && height == this.height && this.offsetLeft == offsetLeft && this.offsetTop == offsetTop) {
return;
}
// final view = await this._viewer!.getViewAt(0);
// final viewport = await view.getViewport();
// final swapChain = await this._viewer.getSwapChainAt(0);
this.width = width;
this.height = height;
this.offsetLeft = offsetLeft;
this.offsetTop = offsetTop;
// if (width == viewport.width && height - viewport.height == 0) {
// return;
// }
_resizing = true;
// _resizing = true;
// bool wasRendering = _viewer!.rendering;
// await _viewer!.setRendering(false);
// await _swapChain?.destroy();
// var result = await _channel
// .invokeMethod("createTexture", [width, height, offsetLeft, offsetLeft]);
// if (result == null || result[0] == -1) {
// throw Exception("Failed to create texture");
// }
// var newTexture =
// ThermionFlutterTexture(result[0], result[1], width, height, result[2]);
// await _viewer!.createSwapChain(width, height,
// surface: newTexture.surfaceAddress == null
// ? nullptr
// : Pointer<Void>.fromAddress(newTexture.surfaceAddress!));
// if (newTexture.hardwareTextureId != null) {
// // ignore: unused_local_variable
// var renderTarget = await _viewer!
// .createRenderTarget(width, height, newTexture.hardwareTextureId!);
// }
// await _viewer!
// .updateViewportAndCameraProjection(width.toDouble(), height.toDouble());
// if (wasRendering) {
// await _viewer!.setRendering(true);
// }
// _textures.add(newTexture);
// _resizing = false;
// return newTexture;
await _channel
.invokeMethod("resizeWindow", [width, height, offsetLeft, offsetTop]);
_resizing = false;
}
}
}

View File

@@ -1,7 +1,7 @@
name: thermion_flutter_ffi
description: An FFI interface for the thermion_flutter plugin (all platforms except web).
repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter
version: 0.2.1-dev.5
version: 0.2.1-dev.7
environment:
sdk: ">=3.3.0 <4.0.0"
@@ -22,11 +22,17 @@ dependencies:
flutter:
sdk: flutter
plugin_platform_interface: ^2.1.0
thermion_flutter_platform_interface: ^0.2.1-dev.5
thermion_dart: ^0.2.1-dev.0.0.6
thermion_flutter_platform_interface: ^0.2.1-dev.7
thermion_dart: ^0.2.1-dev.0.0.8
logging: ^1.2.0
dependency_overrides:
thermion_dart:
path: ../../thermion_dart
thermion_flutter_platform_interface:
path: ../thermion_flutter_platform_interface
dev_dependencies:
flutter_test:
sdk: flutter
mockito: ^5.0.0

View File

@@ -1,3 +1,11 @@
## 0.2.1-dev.7
- Update a dependency to the latest release.
## 0.2.1-dev.6
- Update a dependency to the latest release.
## 0.2.1-dev.5
- Update a dependency to the latest release.

View File

@@ -4,6 +4,7 @@ import 'package:thermion_dart/thermion_dart.dart' as t;
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'package:thermion_dart/thermion_dart.dart';
import 'thermion_flutter_texture.dart';
import 'thermion_flutter_window.dart';
class ThermionFlutterOptions {
final String? uberarchivePath;
@@ -41,7 +42,13 @@ abstract class ThermionFlutterPlatform extends PlatformInterface {
t.View view, int width, int height);
///
/// Create a rendering window.
///
/// This is internal; unless you are [thermion_*] package developer, don't
/// call this yourself. May not be supported on all platforms.
///
Future resizeWindow(int width, int height, int offsetTop, int offsetRight);
Future<ThermionFlutterWindow> createWindow(
int width, int height, int offsetLeft, int offsetTop);
}

View File

@@ -0,0 +1,16 @@
abstract class ThermionFlutterWindow {
int get width;
int get height;
int get handle;
///
/// Destroy a texture and clean up the texture cache (if applicable).
///
Future destroy();
Future resize(int width, int height, int left, int top);
Future markFrameAvailable();
}

View File

@@ -1,7 +1,7 @@
name: thermion_flutter_platform_interface
description: A common platform interface for the thermion_flutter plugin.
repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter
version: 0.2.1-dev.5
version: 0.2.1-dev.7
environment:
sdk: ">=3.3.0 <4.0.0"
@@ -11,7 +11,7 @@ dependencies:
flutter:
sdk: flutter
plugin_platform_interface: ^2.1.0
thermion_dart: ^0.2.1-dev.0.0.6
thermion_dart: ^0.2.1-dev.0.0.8
dev_dependencies:
flutter_test:

View File

@@ -1,3 +1,11 @@
## 0.1.0+9
- Update a dependency to the latest release.
## 0.1.0+8
- Update a dependency to the latest release.
## 0.1.0+7
- Update a dependency to the latest release.

View File

@@ -1,7 +1,7 @@
name: thermion_flutter_web
description: A web platform interface for the thermion_flutter plugin.
repository: https://github.com/nmfisher/thermion_flutter/thermion_flutter
version: 0.1.0+7
version: 0.1.0+9
environment:
sdk: ">=3.3.0 <4.0.0"
@@ -20,8 +20,8 @@ dependencies:
sdk: flutter
plugin_platform_interface: ^2.1.0
web: ^1.0.0
thermion_dart: ^0.2.1-dev.0.0.6
thermion_flutter_platform_interface: ^0.2.1-dev.5
thermion_dart: ^0.2.1-dev.0.0.8
thermion_flutter_platform_interface: ^0.2.1-dev.7
flutter_web_plugins:
sdk: flutter