feat: (web) add capture() method and missing camera navigation controls
This commit is contained in:
@@ -48,6 +48,11 @@ class ThermionViewerJSDartBridge {
|
||||
@JSExport()
|
||||
JSPromise render() => viewer.render().toJS;
|
||||
|
||||
@JSExport()
|
||||
JSPromise<JSUint8Array> capture() {
|
||||
return viewer.capture().then((captured) => captured.toJS).toJS;
|
||||
}
|
||||
|
||||
@JSExport()
|
||||
JSPromise setFrameRate(int framerate) => viewer.setFrameRate(framerate).toJS;
|
||||
|
||||
@@ -342,15 +347,12 @@ class ThermionViewerJSDartBridge {
|
||||
double crossfade = 0.0,
|
||||
double startOffset = 0.0}) =>
|
||||
viewer
|
||||
.playAnimation(
|
||||
entity,
|
||||
index,
|
||||
loop: loop,
|
||||
reverse: reverse,
|
||||
replaceActive: replaceActive,
|
||||
crossfade: crossfade,
|
||||
startOffset: startOffset
|
||||
)
|
||||
.playAnimation(entity, index,
|
||||
loop: loop,
|
||||
reverse: reverse,
|
||||
replaceActive: replaceActive,
|
||||
crossfade: crossfade,
|
||||
startOffset: startOffset)
|
||||
.toJS;
|
||||
|
||||
@JSExport()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:js_interop';
|
||||
import 'dart:js_interop_unsafe';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:animation_tools_dart/animation_tools_dart.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
@@ -371,7 +372,8 @@ class ThermionViewerJS implements ThermionViewer {
|
||||
double crossfade = 0.0,
|
||||
double startOffset = 0.0}) async {
|
||||
await _shim
|
||||
.playAnimation(entity, index, loop, reverse, replaceActive, crossfade, startOffset)
|
||||
.playAnimation(
|
||||
entity, index, loop, reverse, replaceActive, crossfade, startOffset)
|
||||
.toDart;
|
||||
}
|
||||
|
||||
@@ -849,4 +851,10 @@ class ThermionViewerJS implements ThermionViewer {
|
||||
Future setSoftShadowOptions(double penumbraScale, double penumbraRatioScale) {
|
||||
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')
|
||||
external JSPromise render();
|
||||
|
||||
@JS('capture')
|
||||
external JSPromise<JSUint8Array> capture();
|
||||
|
||||
@JS('setFrameRate')
|
||||
external JSPromise setFrameRate(int framerate);
|
||||
|
||||
|
||||
@@ -31,6 +31,9 @@ extension type _EmscriptenModule(JSObject _) implements JSObject {
|
||||
external void stringToUTF8(
|
||||
JSString str, JSNumber ptr, JSNumber maxBytesToWrite);
|
||||
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 HEAPU32;
|
||||
external JSAny get HEAP32;
|
||||
@@ -52,6 +55,8 @@ class ThermionViewerWasm implements ThermionViewer {
|
||||
|
||||
String? assetPathPrefix;
|
||||
|
||||
late (double, double) viewportDimensions;
|
||||
|
||||
///
|
||||
/// Construct an instance of this class by explicitly passing the
|
||||
/// module instance via the [module] property, or by specifying [moduleName],
|
||||
@@ -81,6 +86,8 @@ class ThermionViewerWasm implements ThermionViewer {
|
||||
|
||||
JSNumber? _viewer;
|
||||
JSNumber? _sceneManager;
|
||||
int _width = 0;
|
||||
int _height = 0;
|
||||
|
||||
Future initialize(int width, int height, {String? uberArchivePath}) async {
|
||||
if (!_initialized) {
|
||||
@@ -103,7 +110,7 @@ class ThermionViewerWasm implements ThermionViewer {
|
||||
"create_filament_viewer",
|
||||
"void*",
|
||||
["void*".toJS, "void*".toJS, "void*".toJS, "string".toJS].toJS,
|
||||
[context, loader, null, uberArchivePath?.toJS].toJS,
|
||||
[context!, loader, null, uberArchivePath?.toJS].toJS,
|
||||
null) as JSNumber;
|
||||
await createSwapChain(width, height);
|
||||
updateViewportAndCameraProjection(width, height, 1.0);
|
||||
@@ -143,6 +150,9 @@ class ThermionViewerWasm implements ThermionViewer {
|
||||
|
||||
void updateViewportAndCameraProjection(
|
||||
int width, int height, double scaleFactor) {
|
||||
_width = width;
|
||||
_height = height;
|
||||
viewportDimensions = (width.toDouble(), height.toDouble());
|
||||
_module!.ccall(
|
||||
"update_viewport_and_camera_projection",
|
||||
"void",
|
||||
@@ -772,6 +782,54 @@ class ThermionViewerWasm implements ThermionViewer {
|
||||
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
|
||||
Scene get scene => throw UnimplementedError();
|
||||
|
||||
@@ -1330,21 +1388,21 @@ class ThermionViewerWasm implements ThermionViewer {
|
||||
}
|
||||
|
||||
@override
|
||||
Future panEnd() {
|
||||
// TODO: implement panEnd
|
||||
throw UnimplementedError();
|
||||
Future panEnd() async {
|
||||
_module!.ccall("grab_end", "void",
|
||||
["void*".toJS].toJS, [_viewer!].toJS, null);
|
||||
}
|
||||
|
||||
@override
|
||||
Future panStart(double x, double y) {
|
||||
// TODO: implement panStart
|
||||
throw UnimplementedError();
|
||||
Future panStart(double x, double y) async {
|
||||
_module!.ccall("grab_begin", "void",
|
||||
["void*".toJS, "float".toJS, "float".toJS, "bool".toJS].toJS, [_viewer!, x.toJS, y.toJS, true.toJS].toJS, null);
|
||||
}
|
||||
|
||||
@override
|
||||
Future panUpdate(double x, double y) {
|
||||
// TODO: implement panUpdate
|
||||
throw UnimplementedError();
|
||||
Future panUpdate(double x, double y) async {
|
||||
_module!.ccall("grab_update", "void",
|
||||
["void*".toJS, "float".toJS, "float".toJS].toJS, [_viewer!, x.toJS, y.toJS].toJS, null);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1525,12 +1583,6 @@ class ThermionViewerWasm implements ThermionViewer {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future rotateEnd() {
|
||||
// TODO: implement rotateEnd
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future rotateIbl(Matrix3 rotation) async {
|
||||
final ptr = _module!._malloc(9 * 4) as JSNumber;
|
||||
@@ -1544,15 +1596,21 @@ class ThermionViewerWasm implements ThermionViewer {
|
||||
}
|
||||
|
||||
@override
|
||||
Future rotateStart(double x, double y) {
|
||||
// TODO: implement rotateStart
|
||||
throw UnimplementedError();
|
||||
Future rotateStart(double x, double y) async {
|
||||
_module!.ccall("grab_begin", "void",
|
||||
["void*".toJS, "float".toJS, "float".toJS, "bool".toJS].toJS, [_viewer!, x.toJS, y.toJS, false.toJS].toJS, null);
|
||||
}
|
||||
|
||||
@override
|
||||
Future rotateUpdate(double x, double y) {
|
||||
// TODO: implement rotateUpdate
|
||||
throw UnimplementedError();
|
||||
Future rotateUpdate(double x, double y) async {
|
||||
_module!.ccall("grab_update", "void",
|
||||
["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
|
||||
@@ -1936,19 +1994,19 @@ class ThermionViewerWasm implements ThermionViewer {
|
||||
@override
|
||||
Future zoomBegin() async {
|
||||
_module!.ccall(
|
||||
"zoom_begin", "void", ["void*".toJS].toJS, [_viewer!].toJS, null);
|
||||
"scroll_begin", "void", ["void*".toJS].toJS, [_viewer!].toJS, null);
|
||||
}
|
||||
|
||||
@override
|
||||
Future zoomEnd() async {
|
||||
_module!
|
||||
.ccall("zoom_end", "void", ["void*".toJS].toJS, [_viewer!].toJS, null);
|
||||
.ccall("scroll_end", "void", ["void*".toJS].toJS, [_viewer!].toJS, null);
|
||||
}
|
||||
|
||||
@override
|
||||
Future zoomUpdate(double x, double y, double z) async {
|
||||
_module!.ccall(
|
||||
"zoom_update",
|
||||
"scroll_update",
|
||||
"void",
|
||||
["void*".toJS, "float".toJS, "float".toJS, "float".toJS].toJS,
|
||||
[_viewer!, x.toJS, y.toJS, z.toJS].toJS,
|
||||
|
||||
Reference in New Issue
Block a user