feat: (web) add capture() method and missing camera navigation controls
This commit is contained in:
@@ -48,6 +48,11 @@ class ThermionViewerJSDartBridge {
|
|||||||
@JSExport()
|
@JSExport()
|
||||||
JSPromise render() => viewer.render().toJS;
|
JSPromise render() => viewer.render().toJS;
|
||||||
|
|
||||||
|
@JSExport()
|
||||||
|
JSPromise<JSUint8Array> capture() {
|
||||||
|
return viewer.capture().then((captured) => captured.toJS).toJS;
|
||||||
|
}
|
||||||
|
|
||||||
@JSExport()
|
@JSExport()
|
||||||
JSPromise setFrameRate(int framerate) => viewer.setFrameRate(framerate).toJS;
|
JSPromise setFrameRate(int framerate) => viewer.setFrameRate(framerate).toJS;
|
||||||
|
|
||||||
@@ -342,15 +347,12 @@ class ThermionViewerJSDartBridge {
|
|||||||
double crossfade = 0.0,
|
double crossfade = 0.0,
|
||||||
double startOffset = 0.0}) =>
|
double startOffset = 0.0}) =>
|
||||||
viewer
|
viewer
|
||||||
.playAnimation(
|
.playAnimation(entity, index,
|
||||||
entity,
|
loop: loop,
|
||||||
index,
|
reverse: reverse,
|
||||||
loop: loop,
|
replaceActive: replaceActive,
|
||||||
reverse: reverse,
|
crossfade: crossfade,
|
||||||
replaceActive: replaceActive,
|
startOffset: startOffset)
|
||||||
crossfade: crossfade,
|
|
||||||
startOffset: startOffset
|
|
||||||
)
|
|
||||||
.toJS;
|
.toJS;
|
||||||
|
|
||||||
@JSExport()
|
@JSExport()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:js_interop';
|
import 'dart:js_interop';
|
||||||
import 'dart:js_interop_unsafe';
|
import 'dart:js_interop_unsafe';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:animation_tools_dart/animation_tools_dart.dart';
|
import 'package:animation_tools_dart/animation_tools_dart.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
@@ -371,7 +372,8 @@ class ThermionViewerJS implements ThermionViewer {
|
|||||||
double crossfade = 0.0,
|
double crossfade = 0.0,
|
||||||
double startOffset = 0.0}) async {
|
double startOffset = 0.0}) async {
|
||||||
await _shim
|
await _shim
|
||||||
.playAnimation(entity, index, loop, reverse, replaceActive, crossfade, startOffset)
|
.playAnimation(
|
||||||
|
entity, index, loop, reverse, replaceActive, crossfade, startOffset)
|
||||||
.toDart;
|
.toDart;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -849,4 +851,10 @@ class ThermionViewerJS implements ThermionViewer {
|
|||||||
Future setSoftShadowOptions(double penumbraScale, double penumbraRatioScale) {
|
Future setSoftShadowOptions(double penumbraScale, double penumbraRatioScale) {
|
||||||
return _shim.setSoftShadowOptions(penumbraScale, penumbraRatioScale).toDart;
|
return _shim.setSoftShadowOptions(penumbraScale, penumbraRatioScale).toDart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Uint8List> capture() async {
|
||||||
|
final captured = await _shim.capture().toDart;
|
||||||
|
return captured.toDart;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ extension type ThermionViewerJSShim(JSObject _) implements JSObject {
|
|||||||
@JS('render')
|
@JS('render')
|
||||||
external JSPromise render();
|
external JSPromise render();
|
||||||
|
|
||||||
|
@JS('capture')
|
||||||
|
external JSPromise<JSUint8Array> capture();
|
||||||
|
|
||||||
@JS('setFrameRate')
|
@JS('setFrameRate')
|
||||||
external JSPromise setFrameRate(int framerate);
|
external JSPromise setFrameRate(int framerate);
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ extension type _EmscriptenModule(JSObject _) implements JSObject {
|
|||||||
external void stringToUTF8(
|
external void stringToUTF8(
|
||||||
JSString str, JSNumber ptr, JSNumber maxBytesToWrite);
|
JSString str, JSNumber ptr, JSNumber maxBytesToWrite);
|
||||||
external void writeArrayToMemory(JSUint8Array data, JSNumber ptr);
|
external void writeArrayToMemory(JSUint8Array data, JSNumber ptr);
|
||||||
|
|
||||||
|
external JSNumber addFunction(JSFunction f, String signature);
|
||||||
|
external void removeFunction(JSNumber f);
|
||||||
external JSAny get ALLOC_STACK;
|
external JSAny get ALLOC_STACK;
|
||||||
external JSAny get HEAPU32;
|
external JSAny get HEAPU32;
|
||||||
external JSAny get HEAP32;
|
external JSAny get HEAP32;
|
||||||
@@ -52,6 +55,8 @@ class ThermionViewerWasm implements ThermionViewer {
|
|||||||
|
|
||||||
String? assetPathPrefix;
|
String? assetPathPrefix;
|
||||||
|
|
||||||
|
late (double, double) viewportDimensions;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Construct an instance of this class by explicitly passing the
|
/// Construct an instance of this class by explicitly passing the
|
||||||
/// module instance via the [module] property, or by specifying [moduleName],
|
/// module instance via the [module] property, or by specifying [moduleName],
|
||||||
@@ -81,6 +86,8 @@ class ThermionViewerWasm implements ThermionViewer {
|
|||||||
|
|
||||||
JSNumber? _viewer;
|
JSNumber? _viewer;
|
||||||
JSNumber? _sceneManager;
|
JSNumber? _sceneManager;
|
||||||
|
int _width = 0;
|
||||||
|
int _height = 0;
|
||||||
|
|
||||||
Future initialize(int width, int height, {String? uberArchivePath}) async {
|
Future initialize(int width, int height, {String? uberArchivePath}) async {
|
||||||
if (!_initialized) {
|
if (!_initialized) {
|
||||||
@@ -103,7 +110,7 @@ class ThermionViewerWasm implements ThermionViewer {
|
|||||||
"create_filament_viewer",
|
"create_filament_viewer",
|
||||||
"void*",
|
"void*",
|
||||||
["void*".toJS, "void*".toJS, "void*".toJS, "string".toJS].toJS,
|
["void*".toJS, "void*".toJS, "void*".toJS, "string".toJS].toJS,
|
||||||
[context, loader, null, uberArchivePath?.toJS].toJS,
|
[context!, loader, null, uberArchivePath?.toJS].toJS,
|
||||||
null) as JSNumber;
|
null) as JSNumber;
|
||||||
await createSwapChain(width, height);
|
await createSwapChain(width, height);
|
||||||
updateViewportAndCameraProjection(width, height, 1.0);
|
updateViewportAndCameraProjection(width, height, 1.0);
|
||||||
@@ -143,6 +150,9 @@ class ThermionViewerWasm implements ThermionViewer {
|
|||||||
|
|
||||||
void updateViewportAndCameraProjection(
|
void updateViewportAndCameraProjection(
|
||||||
int width, int height, double scaleFactor) {
|
int width, int height, double scaleFactor) {
|
||||||
|
_width = width;
|
||||||
|
_height = height;
|
||||||
|
viewportDimensions = (width.toDouble(), height.toDouble());
|
||||||
_module!.ccall(
|
_module!.ccall(
|
||||||
"update_viewport_and_camera_projection",
|
"update_viewport_and_camera_projection",
|
||||||
"void",
|
"void",
|
||||||
@@ -772,6 +782,54 @@ class ThermionViewerWasm implements ThermionViewer {
|
|||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Uint8List> capture() async {
|
||||||
|
bool wasRendering = rendering;
|
||||||
|
await setRendering(false);
|
||||||
|
final pixelBuffer = _module!._malloc(_width * _height * 4) as JSNumber;
|
||||||
|
final completer = Completer();
|
||||||
|
final callback = () {
|
||||||
|
print("Callback invoked!");
|
||||||
|
completer.complete();
|
||||||
|
};
|
||||||
|
final callbackPtr = _module!.addFunction(callback.toJS, "v");
|
||||||
|
|
||||||
|
print("Aded functrion ${callbackPtr}, calling capture...");
|
||||||
|
_module!.ccall(
|
||||||
|
"capture",
|
||||||
|
"void",
|
||||||
|
["void*".toJS, "uint8_t*".toJS, "void*".toJS].toJS,
|
||||||
|
[_viewer!, pixelBuffer, callbackPtr].toJS,
|
||||||
|
null);
|
||||||
|
print("Waiting for completer...");
|
||||||
|
int iter = 0;
|
||||||
|
while (true) {
|
||||||
|
await Future.delayed(Duration(milliseconds: 5));
|
||||||
|
await render();
|
||||||
|
if (completer.isCompleted) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
iter++;
|
||||||
|
if (iter > 1000) {
|
||||||
|
_module!._free(pixelBuffer);
|
||||||
|
throw Exception("Failed to complete capture");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a Uint8ClampedList to store the pixel data
|
||||||
|
var data = Uint8List(_width * _height * 4);
|
||||||
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
data[i] = (_module!.getValue(((pixelBuffer.toDartInt) + i).toJS, "i8")
|
||||||
|
as JSNumber)
|
||||||
|
.toDartInt;
|
||||||
|
}
|
||||||
|
_module!._free(pixelBuffer);
|
||||||
|
await setRendering(wasRendering);
|
||||||
|
print("Captured to ${data.length} pixel buffer");
|
||||||
|
_module!.removeFunction(callbackPtr);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Scene get scene => throw UnimplementedError();
|
Scene get scene => throw UnimplementedError();
|
||||||
|
|
||||||
@@ -1330,21 +1388,21 @@ class ThermionViewerWasm implements ThermionViewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future panEnd() {
|
Future panEnd() async {
|
||||||
// TODO: implement panEnd
|
_module!.ccall("grab_end", "void",
|
||||||
throw UnimplementedError();
|
["void*".toJS].toJS, [_viewer!].toJS, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future panStart(double x, double y) {
|
Future panStart(double x, double y) async {
|
||||||
// TODO: implement panStart
|
_module!.ccall("grab_begin", "void",
|
||||||
throw UnimplementedError();
|
["void*".toJS, "float".toJS, "float".toJS, "bool".toJS].toJS, [_viewer!, x.toJS, y.toJS, true.toJS].toJS, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future panUpdate(double x, double y) {
|
Future panUpdate(double x, double y) async {
|
||||||
// TODO: implement panUpdate
|
_module!.ccall("grab_update", "void",
|
||||||
throw UnimplementedError();
|
["void*".toJS, "float".toJS, "float".toJS].toJS, [_viewer!, x.toJS, y.toJS].toJS, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1525,12 +1583,6 @@ class ThermionViewerWasm implements ThermionViewer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future rotateEnd() {
|
|
||||||
// TODO: implement rotateEnd
|
|
||||||
throw UnimplementedError();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future rotateIbl(Matrix3 rotation) async {
|
Future rotateIbl(Matrix3 rotation) async {
|
||||||
final ptr = _module!._malloc(9 * 4) as JSNumber;
|
final ptr = _module!._malloc(9 * 4) as JSNumber;
|
||||||
@@ -1544,15 +1596,21 @@ class ThermionViewerWasm implements ThermionViewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future rotateStart(double x, double y) {
|
Future rotateStart(double x, double y) async {
|
||||||
// TODO: implement rotateStart
|
_module!.ccall("grab_begin", "void",
|
||||||
throw UnimplementedError();
|
["void*".toJS, "float".toJS, "float".toJS, "bool".toJS].toJS, [_viewer!, x.toJS, y.toJS, false.toJS].toJS, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future rotateUpdate(double x, double y) {
|
Future rotateUpdate(double x, double y) async {
|
||||||
// TODO: implement rotateUpdate
|
_module!.ccall("grab_update", "void",
|
||||||
throw UnimplementedError();
|
["void*".toJS, "float".toJS, "float".toJS].toJS, [_viewer!, x.toJS, y.toJS].toJS, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future rotateEnd() async {
|
||||||
|
_module!.ccall("grab_end", "void",
|
||||||
|
["void*".toJS].toJS, [_viewer!].toJS, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1936,19 +1994,19 @@ class ThermionViewerWasm implements ThermionViewer {
|
|||||||
@override
|
@override
|
||||||
Future zoomBegin() async {
|
Future zoomBegin() async {
|
||||||
_module!.ccall(
|
_module!.ccall(
|
||||||
"zoom_begin", "void", ["void*".toJS].toJS, [_viewer!].toJS, null);
|
"scroll_begin", "void", ["void*".toJS].toJS, [_viewer!].toJS, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future zoomEnd() async {
|
Future zoomEnd() async {
|
||||||
_module!
|
_module!
|
||||||
.ccall("zoom_end", "void", ["void*".toJS].toJS, [_viewer!].toJS, null);
|
.ccall("scroll_end", "void", ["void*".toJS].toJS, [_viewer!].toJS, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future zoomUpdate(double x, double y, double z) async {
|
Future zoomUpdate(double x, double y, double z) async {
|
||||||
_module!.ccall(
|
_module!.ccall(
|
||||||
"zoom_update",
|
"scroll_update",
|
||||||
"void",
|
"void",
|
||||||
["void*".toJS, "float".toJS, "float".toJS, "float".toJS].toJS,
|
["void*".toJS, "float".toJS, "float".toJS, "float".toJS].toJS,
|
||||||
[_viewer!, x.toJS, y.toJS, z.toJS].toJS,
|
[_viewer!, x.toJS, y.toJS, z.toJS].toJS,
|
||||||
|
|||||||
Reference in New Issue
Block a user