feat!: big refactor to support multiple swapchains
This commit is contained in:
@@ -53,11 +53,9 @@ class FreeFlightInputHandlerDelegate implements InputHandlerDelegate {
|
||||
_queuedZoomDelta += delta.z;
|
||||
break;
|
||||
case InputAction.PICK:
|
||||
// Assuming PICK is used for zoom in this context
|
||||
_queuedZoomDelta += delta.z;
|
||||
break;
|
||||
case InputAction.NONE:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -81,14 +79,17 @@ class FreeFlightInputHandlerDelegate implements InputHandlerDelegate {
|
||||
}
|
||||
|
||||
final activeCamera = await viewer.getActiveCamera();
|
||||
Matrix4 currentViewMatrix = activeCamera.getViewMatrix();
|
||||
|
||||
|
||||
Matrix4 currentViewMatrix = await activeCamera.getViewMatrix();
|
||||
Matrix4 currentTransform = await viewer.getLocalTransform(await entity);
|
||||
Vector3 currentPosition = currentTransform.getTranslation();
|
||||
Quaternion currentRotation =
|
||||
Quaternion.fromRotation(currentTransform.getRotation());
|
||||
|
||||
// Calculate relative transform
|
||||
Matrix4 relativeTransform = Matrix4.identity();
|
||||
Vector3 relativeTranslation = Vector3.zero();
|
||||
Quaternion relativeRotation = Quaternion.identity();
|
||||
|
||||
// Apply rotation
|
||||
if (_queuedRotationDelta.length2 > 0.0) {
|
||||
double deltaX =
|
||||
@@ -99,9 +100,7 @@ class FreeFlightInputHandlerDelegate implements InputHandlerDelegate {
|
||||
Quaternion yawRotation = Quaternion.axisAngle(_up, -deltaX);
|
||||
Quaternion pitchRotation = Quaternion.axisAngle(_right, -deltaY);
|
||||
|
||||
currentRotation = currentRotation * pitchRotation * yawRotation;
|
||||
currentRotation.normalize();
|
||||
|
||||
relativeRotation = pitchRotation * yawRotation;
|
||||
_queuedRotationDelta = Vector2.zero();
|
||||
}
|
||||
|
||||
@@ -113,16 +112,14 @@ class FreeFlightInputHandlerDelegate implements InputHandlerDelegate {
|
||||
double deltaX = _queuedPanDelta.x * panSensitivity * viewer.pixelRatio;
|
||||
double deltaY = _queuedPanDelta.y * panSensitivity * viewer.pixelRatio;
|
||||
|
||||
Vector3 panOffset = right * deltaX + up * deltaY;
|
||||
currentPosition += panOffset;
|
||||
|
||||
relativeTranslation += right * deltaX + up * deltaY;
|
||||
_queuedPanDelta = Vector2.zero();
|
||||
}
|
||||
|
||||
// Apply zoom
|
||||
if (_queuedZoomDelta != 0.0) {
|
||||
Vector3 forward = _forward.clone()..applyQuaternion(currentRotation);
|
||||
currentPosition += forward * -_queuedZoomDelta * zoomSensitivity;
|
||||
relativeTranslation += forward * -_queuedZoomDelta * zoomSensitivity;
|
||||
_queuedZoomDelta = 0.0;
|
||||
}
|
||||
|
||||
@@ -132,21 +129,37 @@ class FreeFlightInputHandlerDelegate implements InputHandlerDelegate {
|
||||
Vector3 right = _right.clone()..applyQuaternion(currentRotation);
|
||||
Vector3 up = _up.clone()..applyQuaternion(currentRotation);
|
||||
|
||||
Vector3 moveOffset = right * _queuedMoveDelta.x +
|
||||
relativeTranslation += right * _queuedMoveDelta.x +
|
||||
up * _queuedMoveDelta.y +
|
||||
forward * _queuedMoveDelta.z;
|
||||
currentPosition += moveOffset;
|
||||
|
||||
_queuedMoveDelta = Vector3.zero();
|
||||
}
|
||||
|
||||
// Constrain position
|
||||
currentPosition = _constrainPosition(currentPosition);
|
||||
// If the managed entity is not the active camera, we need to apply the rotation from the current camera model matrix
|
||||
// to the entity's translation
|
||||
if (await entity != activeCamera.getEntity()) {
|
||||
Matrix4 modelMatrix = await activeCamera.getModelMatrix();
|
||||
relativeTranslation = modelMatrix.getRotation() * relativeTranslation;
|
||||
}
|
||||
|
||||
// Compose relative transform
|
||||
relativeTransform = Matrix4.compose(
|
||||
relativeTranslation, relativeRotation, Vector3(1, 1, 1));
|
||||
|
||||
// Apply relative transform to current transform
|
||||
Matrix4 newTransform = currentTransform * relativeTransform;
|
||||
|
||||
// Extract new position and constrain it
|
||||
Vector3 newPosition = newTransform.getTranslation();
|
||||
newPosition = _constrainPosition(newPosition);
|
||||
|
||||
// Recompose final transform with constrained position
|
||||
Matrix4 finalTransform = Matrix4.compose(newPosition,
|
||||
Quaternion.fromRotation(newTransform.getRotation()), Vector3(1, 1, 1));
|
||||
|
||||
// Update camera
|
||||
Matrix4 newModelMatrix =
|
||||
Matrix4.compose(currentPosition, currentRotation, Vector3(1, 1, 1));
|
||||
await viewer.setTransform(await entity, newModelMatrix);
|
||||
await viewer.setTransform(await entity, finalTransform);
|
||||
|
||||
_executing = false;
|
||||
}
|
||||
|
||||
@@ -29,13 +29,16 @@ class OverTheShoulderCameraDelegate implements InputHandlerDelegate {
|
||||
final cameraUp = Vector3(0, 1, 0);
|
||||
var cameraLookAt = Vector3(0, 0.5, 3);
|
||||
|
||||
final void Function(Matrix4 transform)? onUpdate;
|
||||
|
||||
OverTheShoulderCameraDelegate(this.viewer, this.player, this.camera,
|
||||
{this.rotationSensitivity = 0.001,
|
||||
this.movementSensitivity = 0.1,
|
||||
this.zoomSensitivity = 0.1,
|
||||
this.panSensitivity = 0.1,
|
||||
this.clampY,
|
||||
ThermionEntity? entity}) {}
|
||||
ThermionEntity? entity,
|
||||
this.onUpdate}) {}
|
||||
|
||||
@override
|
||||
Future<void> queue(InputAction action, Vector3? delta) async {
|
||||
@@ -97,7 +100,9 @@ class OverTheShoulderCameraDelegate implements InputHandlerDelegate {
|
||||
double deltaY =
|
||||
_queuedRotationDelta.y * rotationSensitivity * viewer.pixelRatio;
|
||||
|
||||
cameraLookAt = Matrix4.rotationY(-deltaX) * Matrix4.rotationX(-deltaY) * cameraLookAt;
|
||||
cameraLookAt = Matrix4.rotationY(-deltaX) *
|
||||
Matrix4.rotationX(-deltaY) *
|
||||
cameraLookAt;
|
||||
_queuedRotationDelta = Vector2.zero();
|
||||
}
|
||||
|
||||
@@ -109,8 +114,7 @@ class OverTheShoulderCameraDelegate implements InputHandlerDelegate {
|
||||
|
||||
await viewer.queueTransformUpdates(
|
||||
[camera.getEntity(), player], [newCameraTransform, newPlayerTransform]);
|
||||
|
||||
onUpdate?.call(newPlayerTransform);
|
||||
_executing = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ Future<void> withVoidCallback(
|
||||
nativeCallable.close();
|
||||
}
|
||||
|
||||
Future<int> withPointerCallback<T extends NativeType>(
|
||||
Future<Pointer<T>> withPointerCallback<T extends NativeType>(
|
||||
Function(Pointer<NativeFunction<Void Function(Pointer<T>)>>)
|
||||
func) async {
|
||||
final completer = Completer<Pointer<T>>();
|
||||
@@ -39,7 +39,7 @@ Future<int> withPointerCallback<T extends NativeType>(
|
||||
func.call(nativeCallable.nativeFunction);
|
||||
var ptr = await completer.future;
|
||||
nativeCallable.close();
|
||||
return ptr.address;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
Future<bool> withBoolCallback(
|
||||
|
||||
@@ -27,6 +27,101 @@ external ffi.Pointer<TSceneManager> Viewer_getSceneManager(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.Pointer<TRenderTarget> Function(
|
||||
ffi.Pointer<TViewer>, ffi.IntPtr, ffi.Uint32, ffi.Uint32)>(isLeaf: true)
|
||||
external ffi.Pointer<TRenderTarget> Viewer_createRenderTarget(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
int texture,
|
||||
int width,
|
||||
int height,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.Void Function(
|
||||
ffi.Pointer<TViewer>, ffi.Pointer<TRenderTarget>)>(isLeaf: true)
|
||||
external void Viewer_destroyRenderTarget(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
ffi.Pointer<TRenderTarget> tRenderTarget,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.Void Function(
|
||||
ffi.Pointer<TViewer>, ffi.Pointer<TRenderTarget>)>(isLeaf: true)
|
||||
external void Viewer_setRenderTarget(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
ffi.Pointer<TRenderTarget> tRenderTarget,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.Pointer<TSwapChain> Function(ffi.Pointer<TViewer>,
|
||||
ffi.Pointer<ffi.Void>, ffi.Uint32, ffi.Uint32)>(isLeaf: true)
|
||||
external ffi.Pointer<TSwapChain> Viewer_createSwapChain(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
ffi.Pointer<ffi.Void> window,
|
||||
int width,
|
||||
int height,
|
||||
);
|
||||
|
||||
@ffi.Native<ffi.Void Function(ffi.Pointer<TViewer>, ffi.Pointer<TSwapChain>)>(
|
||||
isLeaf: true)
|
||||
external void Viewer_destroySwapChain(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
ffi.Pointer<TSwapChain> swapChain,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.Bool Function(
|
||||
ffi.Pointer<TViewer>,
|
||||
ffi.Pointer<TSwapChain>,
|
||||
ffi.Uint64,
|
||||
ffi.Pointer<ffi.Void>,
|
||||
ffi.Pointer<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(ffi.Pointer<ffi.Void> buf, ffi.Size size,
|
||||
ffi.Pointer<ffi.Void> data)>>,
|
||||
ffi.Pointer<ffi.Void>)>(isLeaf: true)
|
||||
external bool Viewer_render(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
ffi.Pointer<TSwapChain> swapChain,
|
||||
int frameTimeInNanos,
|
||||
ffi.Pointer<ffi.Void> pixelBuffer,
|
||||
ffi.Pointer<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(ffi.Pointer<ffi.Void> buf, ffi.Size size,
|
||||
ffi.Pointer<ffi.Void> data)>>
|
||||
callback,
|
||||
ffi.Pointer<ffi.Void> data,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.Void Function(
|
||||
ffi.Pointer<TViewer>,
|
||||
ffi.Pointer<TSwapChain>,
|
||||
ffi.Pointer<ffi.Uint8>,
|
||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
|
||||
external void Viewer_capture(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
ffi.Pointer<TSwapChain> swapChain,
|
||||
ffi.Pointer<ffi.Uint8> pixelBuffer,
|
||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> callback,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.Void Function(
|
||||
ffi.Pointer<TViewer>,
|
||||
ffi.Pointer<TSwapChain>,
|
||||
ffi.Pointer<TRenderTarget>,
|
||||
ffi.Pointer<ffi.Uint8>,
|
||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
|
||||
external void Viewer_captureRenderTarget(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
ffi.Pointer<TSwapChain> swapChain,
|
||||
ffi.Pointer<TRenderTarget> renderTarget,
|
||||
ffi.Pointer<ffi.Uint8> pixelBuffer,
|
||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> callback,
|
||||
);
|
||||
|
||||
@ffi.Native<ffi.Pointer<TEngine> Function(ffi.Pointer<TViewer>)>(isLeaf: true)
|
||||
external ffi.Pointer<TEngine> Viewer_getEngine(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
@@ -47,16 +142,6 @@ external void Engine_setTransform(
|
||||
double4x4 transform,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.Void Function(
|
||||
ffi.Pointer<TViewer>, ffi.IntPtr, ffi.Uint32, ffi.Uint32)>(isLeaf: true)
|
||||
external void create_render_target(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
int texture,
|
||||
int width,
|
||||
int height,
|
||||
);
|
||||
|
||||
@ffi.Native<ffi.Void Function(ffi.Pointer<TViewer>)>(isLeaf: true)
|
||||
external void clear_background_image(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
@@ -300,52 +385,6 @@ external void set_view_frustum_culling(
|
||||
bool enabled,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.Bool Function(
|
||||
ffi.Pointer<TViewer>,
|
||||
ffi.Uint64,
|
||||
ffi.Pointer<ffi.Void>,
|
||||
ffi.Pointer<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(ffi.Pointer<ffi.Void> buf, ffi.Size size,
|
||||
ffi.Pointer<ffi.Void> data)>>,
|
||||
ffi.Pointer<ffi.Void>)>(isLeaf: true)
|
||||
external bool render(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
int frameTimeInNanos,
|
||||
ffi.Pointer<ffi.Void> pixelBuffer,
|
||||
ffi.Pointer<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(ffi.Pointer<ffi.Void> buf, ffi.Size size,
|
||||
ffi.Pointer<ffi.Void> data)>>
|
||||
callback,
|
||||
ffi.Pointer<ffi.Void> data,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.Void Function(ffi.Pointer<TViewer>, ffi.Pointer<ffi.Uint8>,
|
||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
|
||||
external void capture(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
ffi.Pointer<ffi.Uint8> pixelBuffer,
|
||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> callback,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.Void Function(ffi.Pointer<TViewer>, ffi.Pointer<ffi.Void>, ffi.Uint32,
|
||||
ffi.Uint32)>(isLeaf: true)
|
||||
external void create_swap_chain(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
ffi.Pointer<ffi.Void> window,
|
||||
int width,
|
||||
int height,
|
||||
);
|
||||
|
||||
@ffi.Native<ffi.Void Function(ffi.Pointer<TViewer>)>(isLeaf: true)
|
||||
external void destroy_swap_chain(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
);
|
||||
|
||||
@ffi.Native<ffi.Void Function(ffi.Pointer<TViewer>, ffi.Float)>(isLeaf: true)
|
||||
external void set_frame_interval(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
@@ -1111,19 +1150,6 @@ external ffi.Pointer<ffi.Char> get_entity_name_at(
|
||||
bool renderableOnly,
|
||||
);
|
||||
|
||||
@ffi.Native<ffi.Void Function(ffi.Pointer<TViewer>, ffi.Bool)>(isLeaf: true)
|
||||
external void set_recording(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
bool recording,
|
||||
);
|
||||
|
||||
@ffi.Native<ffi.Void Function(ffi.Pointer<TViewer>, ffi.Pointer<ffi.Char>)>(
|
||||
isLeaf: true)
|
||||
external void set_recording_output_directory(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
ffi.Pointer<ffi.Char> outputDirectory,
|
||||
);
|
||||
|
||||
@ffi.Native<ffi.Void Function()>(isLeaf: true)
|
||||
external void ios_dummy();
|
||||
|
||||
@@ -1484,36 +1510,65 @@ external void create_filament_viewer_render_thread(
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.Void Function(
|
||||
ffi.Pointer<TViewer>,
|
||||
ffi.Pointer<ffi.Void>,
|
||||
ffi.Uint32,
|
||||
ffi.Uint32,
|
||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
|
||||
external void create_swap_chain_render_thread(
|
||||
ffi.Void Function(
|
||||
ffi.Pointer<TViewer>,
|
||||
ffi.Pointer<ffi.Void>,
|
||||
ffi.Uint32,
|
||||
ffi.Uint32,
|
||||
ffi.Pointer<
|
||||
ffi
|
||||
.NativeFunction<ffi.Void Function(ffi.Pointer<TSwapChain>)>>)>(
|
||||
isLeaf: true)
|
||||
external void Viewer_createSwapChainRenderThread(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
ffi.Pointer<ffi.Void> surface,
|
||||
int width,
|
||||
int height,
|
||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<TSwapChain>)>>
|
||||
onComplete,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.Void Function(ffi.Pointer<TViewer>, ffi.Pointer<TSwapChain>,
|
||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
|
||||
external void Viewer_destroySwapChainRenderThread(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
ffi.Pointer<TSwapChain> swapChain,
|
||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
|
||||
);
|
||||
|
||||
@ffi.Native<ffi.Void Function(ffi.Pointer<TViewer>, ffi.Pointer<TSwapChain>)>(
|
||||
isLeaf: true)
|
||||
external void Viewer_renderRenderThread(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
ffi.Pointer<TSwapChain> swapChain,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.Void Function(
|
||||
ffi.Pointer<TViewer>,
|
||||
ffi.Pointer<TSwapChain>,
|
||||
ffi.Pointer<ffi.Uint8>,
|
||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
|
||||
external void Viewer_captureRenderThread(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
ffi.Pointer<TSwapChain> swapChain,
|
||||
ffi.Pointer<ffi.Uint8> out,
|
||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.Void Function(ffi.Pointer<TViewer>,
|
||||
ffi.Void Function(
|
||||
ffi.Pointer<TViewer>,
|
||||
ffi.Pointer<TSwapChain>,
|
||||
ffi.Pointer<TRenderTarget>,
|
||||
ffi.Pointer<ffi.Uint8>,
|
||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
|
||||
external void destroy_swap_chain_render_thread(
|
||||
external void Viewer_captureRenderTargetRenderThread(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.Void Function(ffi.Pointer<TViewer>, ffi.IntPtr, ffi.Uint32, ffi.Uint32,
|
||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
|
||||
external void create_render_target_render_thread(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
int nativeTextureId,
|
||||
int width,
|
||||
int height,
|
||||
ffi.Pointer<TSwapChain> swapChain,
|
||||
ffi.Pointer<TRenderTarget> renderTarget,
|
||||
ffi.Pointer<ffi.Uint8> out,
|
||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
|
||||
);
|
||||
|
||||
@@ -1522,20 +1577,6 @@ external void destroy_filament_viewer_render_thread(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
);
|
||||
|
||||
@ffi.Native<ffi.Void Function(ffi.Pointer<TViewer>)>(isLeaf: true)
|
||||
external void render_render_thread(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.Void Function(ffi.Pointer<TViewer>, ffi.Pointer<ffi.Uint8>,
|
||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
|
||||
external void capture_render_thread(
|
||||
ffi.Pointer<TViewer> viewer,
|
||||
ffi.Pointer<ffi.Uint8> out,
|
||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
|
||||
);
|
||||
|
||||
@ffi.Native<FilamentRenderCallback Function(FilamentRenderCallback)>(
|
||||
isLeaf: true)
|
||||
external FilamentRenderCallback make_render_callback_fn_pointer(
|
||||
@@ -1927,6 +1968,10 @@ final class TViewer extends ffi.Opaque {}
|
||||
|
||||
final class TSceneManager extends ffi.Opaque {}
|
||||
|
||||
final class TRenderTarget extends ffi.Opaque {}
|
||||
|
||||
final class TSwapChain extends ffi.Opaque {}
|
||||
|
||||
final class TMaterialKey extends ffi.Struct {
|
||||
@ffi.Bool()
|
||||
external bool doubleSided;
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:ffi';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
import 'package:animation_tools_dart/animation_tools_dart.dart';
|
||||
import 'package:thermion_dart/src/viewer/src/shared_types/swap_chain.dart';
|
||||
import 'package:vector_math/vector_math_64.dart';
|
||||
import 'package:vector_math/vector_math_64.dart' as v64;
|
||||
import '../../../../entities/gizmo.dart';
|
||||
@@ -96,10 +97,21 @@ class ThermionViewerFFI extends ThermionViewer {
|
||||
_initialize();
|
||||
}
|
||||
|
||||
Future createRenderTarget(
|
||||
double width, double height, int textureHandle) async {
|
||||
await withVoidCallback((callback) => create_render_target_render_thread(
|
||||
_viewer!, textureHandle, width.toInt(), height.toInt(), callback));
|
||||
///
|
||||
///
|
||||
///
|
||||
Future<RenderTarget> createRenderTarget(
|
||||
int width, int height, int textureHandle) async {
|
||||
final renderTarget =
|
||||
Viewer_createRenderTarget(_viewer!, textureHandle, width, height);
|
||||
return FFIRenderTarget(renderTarget, _viewer!);
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
Future setRenderTarget(FFIRenderTarget renderTarget) async {
|
||||
Viewer_setRenderTarget(_viewer!, renderTarget.renderTarget);
|
||||
}
|
||||
|
||||
Future updateViewportAndCameraProjection(double width, double height) async {
|
||||
@@ -129,18 +141,13 @@ class ThermionViewerFFI extends ThermionViewer {
|
||||
}
|
||||
}
|
||||
|
||||
Future createSwapChain(double width, double height,
|
||||
Future<SwapChain> createSwapChain(int width, int height,
|
||||
{Pointer<Void>? surface}) async {
|
||||
await withVoidCallback((callback) {
|
||||
create_swap_chain_render_thread(_viewer!, surface ?? nullptr,
|
||||
var swapChain = await withPointerCallback<TSwapChain>((callback) {
|
||||
return Viewer_createSwapChainRenderThread(_viewer!, surface ?? nullptr,
|
||||
width.toInt(), height.toInt(), callback);
|
||||
});
|
||||
}
|
||||
|
||||
Future destroySwapChain() async {
|
||||
await withVoidCallback((callback) {
|
||||
destroy_swap_chain_render_thread(_viewer!, callback);
|
||||
});
|
||||
return FFISwapChain(swapChain, _viewer!);
|
||||
}
|
||||
|
||||
Gizmo? _gizmo;
|
||||
@@ -150,7 +157,7 @@ class ThermionViewerFFI extends ThermionViewer {
|
||||
final uberarchivePtr =
|
||||
uberArchivePath?.toNativeUtf8(allocator: allocator).cast<Char>() ??
|
||||
nullptr;
|
||||
var viewer = await withPointerCallback(
|
||||
_viewer = await withPointerCallback(
|
||||
(Pointer<NativeFunction<Void Function(Pointer<TViewer>)>> callback) {
|
||||
create_filament_viewer_render_thread(
|
||||
_sharedContext,
|
||||
@@ -161,7 +168,7 @@ class ThermionViewerFFI extends ThermionViewer {
|
||||
_renderCallbackOwner,
|
||||
callback);
|
||||
});
|
||||
_viewer = Pointer.fromAddress(viewer);
|
||||
|
||||
allocator.free(uberarchivePtr);
|
||||
if (_viewer!.address == 0) {
|
||||
throw Exception("Failed to create viewer. Check logs for details");
|
||||
@@ -202,25 +209,30 @@ class ThermionViewerFFI extends ThermionViewer {
|
||||
///
|
||||
///
|
||||
@override
|
||||
Future render() async {
|
||||
render_render_thread(_viewer!);
|
||||
Future render(FFISwapChain swapChain) async {
|
||||
Viewer_renderRenderThread(_viewer!, swapChain.swapChain);
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
@override
|
||||
Future<Uint8List> capture() async {
|
||||
Future<Uint8List> capture(FFISwapChain swapChain,
|
||||
{FFIRenderTarget? renderTarget}) async {
|
||||
final length = this.viewportDimensions.$1.toInt() *
|
||||
this.viewportDimensions.$2.toInt() *
|
||||
4;
|
||||
final out = allocator<Uint8>(length);
|
||||
final out = Uint8List(length);
|
||||
await withVoidCallback((cb) {
|
||||
capture_render_thread(_viewer!, out, cb);
|
||||
if (renderTarget != null) {
|
||||
Viewer_captureRenderTargetRenderThread(
|
||||
_viewer!, swapChain.swapChain, renderTarget.renderTarget, out.address, cb);
|
||||
} else {
|
||||
Viewer_captureRenderThread(
|
||||
_viewer!, swapChain.swapChain, out.address, cb);
|
||||
}
|
||||
});
|
||||
final data = Uint8List.fromList(out.asTypedList(length));
|
||||
allocator.free(out);
|
||||
return data;
|
||||
return out;
|
||||
}
|
||||
|
||||
///
|
||||
@@ -246,9 +258,7 @@ class ThermionViewerFFI extends ThermionViewer {
|
||||
await setRendering(false);
|
||||
await clearEntities();
|
||||
await clearLights();
|
||||
print("DESTROYING");
|
||||
destroy_filament_viewer_render_thread(_viewer!);
|
||||
print("DESTROYED");
|
||||
_sceneManager = null;
|
||||
_viewer = null;
|
||||
await _pickResultController.close();
|
||||
@@ -259,7 +269,6 @@ class ThermionViewerFFI extends ThermionViewer {
|
||||
await callback.call();
|
||||
}
|
||||
_onDispose.clear();
|
||||
print("DONE");
|
||||
}
|
||||
|
||||
///
|
||||
@@ -409,24 +418,24 @@ class ThermionViewerFFI extends ThermionViewer {
|
||||
@override
|
||||
Future<ThermionEntity> addDirectLight(DirectLight directLight) async {
|
||||
var entity = add_light(
|
||||
_viewer!,
|
||||
directLight.type.index,
|
||||
directLight.color,
|
||||
directLight.intensity,
|
||||
directLight.position.x,
|
||||
directLight.position.y,
|
||||
directLight.position.z,
|
||||
directLight.direction.x,
|
||||
directLight.direction.y,
|
||||
directLight.direction.z,
|
||||
directLight.falloffRadius,
|
||||
directLight.spotLightConeInner,
|
||||
directLight.spotLightConeOuter,
|
||||
directLight.sunAngularRadius,
|
||||
directLight.sunHaloSize,
|
||||
directLight.sunHaloFallof,
|
||||
directLight.castShadows,
|
||||
);
|
||||
_viewer!,
|
||||
directLight.type.index,
|
||||
directLight.color,
|
||||
directLight.intensity,
|
||||
directLight.position.x,
|
||||
directLight.position.y,
|
||||
directLight.position.z,
|
||||
directLight.direction.x,
|
||||
directLight.direction.y,
|
||||
directLight.direction.z,
|
||||
directLight.falloffRadius,
|
||||
directLight.spotLightConeInner,
|
||||
directLight.spotLightConeOuter,
|
||||
directLight.sunAngularRadius,
|
||||
directLight.sunHaloSize,
|
||||
directLight.sunHaloFallof,
|
||||
directLight.castShadows,
|
||||
);
|
||||
if (entity == _FILAMENT_ASSET_ERROR) {
|
||||
throw Exception("Failed to add light to scene");
|
||||
}
|
||||
@@ -1747,24 +1756,6 @@ class ThermionViewerFFI extends ThermionViewer {
|
||||
return names;
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
@override
|
||||
Future setRecording(bool recording) async {
|
||||
set_recording(_viewer!, recording);
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
@override
|
||||
Future setRecordingOutputDirectory(String outputDir) async {
|
||||
var pathPtr = outputDir.toNativeUtf8(allocator: allocator);
|
||||
set_recording_output_directory(_viewer!, pathPtr.cast<Char>());
|
||||
allocator.free(pathPtr);
|
||||
}
|
||||
|
||||
final _collisions = <ThermionEntity, NativeCallable>{};
|
||||
|
||||
///
|
||||
@@ -2254,3 +2245,28 @@ class ThermionFFIMaterialInstance extends MaterialInstance {
|
||||
_pointer, name.toNativeUtf8().cast<Char>(), x, y);
|
||||
}
|
||||
}
|
||||
|
||||
class FFIRenderTarget extends RenderTarget {
|
||||
final Pointer<TRenderTarget> renderTarget;
|
||||
final Pointer<TViewer> viewer;
|
||||
|
||||
FFIRenderTarget(this.renderTarget, this.viewer);
|
||||
|
||||
@override
|
||||
Future destroy() async {
|
||||
Viewer_destroyRenderTarget(viewer, renderTarget);
|
||||
}
|
||||
}
|
||||
|
||||
class FFISwapChain extends SwapChain {
|
||||
final Pointer<TSwapChain> swapChain;
|
||||
final Pointer<TViewer> viewer;
|
||||
|
||||
FFISwapChain(this.swapChain, this.viewer);
|
||||
|
||||
Future destroy() async {
|
||||
await withVoidCallback((callback) {
|
||||
Viewer_destroySwapChainRenderThread(viewer, swapChain, callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
abstract class RenderTarget {
|
||||
Future destroy();
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
library shared_types;
|
||||
|
||||
export 'swap_chain.dart';
|
||||
export 'render_target.dart';
|
||||
export 'camera.dart';
|
||||
export 'material.dart';
|
||||
export 'texture.dart';
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
abstract class SwapChain {
|
||||
Future destroy();
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import 'package:vector_math/vector_math_64.dart';
|
||||
import 'dart:async';
|
||||
import 'package:animation_tools_dart/animation_tools_dart.dart';
|
||||
|
||||
import 'shared_types/swap_chain.dart';
|
||||
|
||||
const double kNear = 0.05;
|
||||
const double kFar = 1000.0;
|
||||
const double kFocalLength = 28.0;
|
||||
@@ -60,7 +62,7 @@ abstract class ThermionViewer {
|
||||
///
|
||||
/// Render a single frame immediately.
|
||||
///
|
||||
Future render();
|
||||
Future render(covariant SwapChain swapChain);
|
||||
|
||||
///
|
||||
/// Requests a single frame to be rendered. This is only intended to be used internally.
|
||||
@@ -70,7 +72,23 @@ abstract class ThermionViewer {
|
||||
///
|
||||
/// Render a single frame and copy the pixel buffer to [out].
|
||||
///
|
||||
Future<Uint8List> capture();
|
||||
Future<Uint8List> capture(covariant SwapChain swapChain, { covariant RenderTarget? renderTarget });
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
Future<SwapChain> createSwapChain(int width, int height);
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
Future<RenderTarget> createRenderTarget(int width, int height, int textureHandle);
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
Future setRenderTarget(covariant RenderTarget renderTarget);
|
||||
|
||||
|
||||
///
|
||||
/// Sets the framerate for continuous rendering when [setRendering] is enabled.
|
||||
@@ -368,7 +386,8 @@ abstract class ThermionViewer {
|
||||
/// Sets multiple transforms (relative to parent) simultaneously for [entity].
|
||||
/// Uses mutex to ensure that transform updates aren't split across frames.
|
||||
///
|
||||
Future queueTransformUpdates(List<ThermionEntity> entities, List<Matrix4> transforms);
|
||||
Future queueTransformUpdates(
|
||||
List<ThermionEntity> entities, List<Matrix4> transforms);
|
||||
|
||||
///
|
||||
/// Updates the bone matrices for [entity] (which must be the ThermionEntity
|
||||
@@ -757,17 +776,6 @@ abstract class ThermionViewer {
|
||||
Future<List<String>> getChildEntityNames(ThermionEntity entity,
|
||||
{bool renderableOnly = true});
|
||||
|
||||
///
|
||||
/// If [recording] is set to true, each frame the framebuffer/texture will be written to /tmp/output_*.png.
|
||||
/// This will impact performance; handle with care.
|
||||
///
|
||||
Future setRecording(bool recording);
|
||||
|
||||
///
|
||||
/// Sets the output directory where recorded PNGs will be placed.
|
||||
///
|
||||
Future setRecordingOutputDirectory(String outputDirectory);
|
||||
|
||||
///
|
||||
/// An [entity] will only be animatable after an animation component is attached.
|
||||
/// Any calls to [playAnimation]/[setBoneAnimation]/[setMorphAnimation] will have no visual effect until [addAnimationComponent] has been called on the instance.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:thermion_dart/src/viewer/src/shared_types/swap_chain.dart';
|
||||
import 'package:thermion_dart/thermion_dart.dart';
|
||||
import 'package:vector_math/vector_math_64.dart';
|
||||
import 'dart:async';
|
||||
@@ -723,12 +724,6 @@ class ThermionViewerStub extends ThermionViewer {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> capture() {
|
||||
// TODO: implement capture
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Aabb2> getBoundingBox(ThermionEntity entity) {
|
||||
// TODO: implement getBoundingBox
|
||||
@@ -1020,5 +1015,23 @@ class ThermionViewerStub extends ThermionViewer {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<SwapChain> createSwapChain(int width, int height) {
|
||||
// TODO: implement createSwapChain
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<RenderTarget> createRenderTarget(int width, int height, int textureHandle) {
|
||||
// TODO: implement createRenderTarget
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future setRenderTarget(covariant RenderTarget renderTarget) {
|
||||
// TODO: implement setRenderTarget
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ extern "C"
|
||||
typedef struct TEntityManager TEntityManager;
|
||||
typedef struct TViewer TViewer;
|
||||
typedef struct TSceneManager TSceneManager;
|
||||
typedef struct TRenderTarget TRenderTarget;
|
||||
typedef struct TSwapChain TSwapChain;
|
||||
|
||||
struct TMaterialKey {
|
||||
bool doubleSided = 1;
|
||||
|
||||
@@ -77,6 +77,7 @@ namespace thermion_filament
|
||||
void updateViewport(uint32_t width, uint32_t height);
|
||||
bool render(
|
||||
uint64_t frameTimeInNanos,
|
||||
SwapChain* swapChain,
|
||||
void *pixelBuffer,
|
||||
void (*callback)(void *buf, size_t size, void *data),
|
||||
void *data);
|
||||
@@ -90,10 +91,12 @@ namespace thermion_filament
|
||||
float getCameraFov(bool horizontal);
|
||||
void setCameraFov(double fovDegrees, bool horizontal);
|
||||
|
||||
void createSwapChain(const void *surface, uint32_t width, uint32_t height);
|
||||
void destroySwapChain();
|
||||
SwapChain* createSwapChain(const void *surface, uint32_t width, uint32_t height);
|
||||
void destroySwapChain(SwapChain* swapChain);
|
||||
|
||||
void createRenderTarget(intptr_t textureId, uint32_t width, uint32_t height);
|
||||
RenderTarget* createRenderTarget(intptr_t textureId, uint32_t width, uint32_t height);
|
||||
void destroyRenderTarget(RenderTarget* renderTarget);
|
||||
void setRenderTarget(RenderTarget* renderTarget);
|
||||
|
||||
Renderer *getRenderer();
|
||||
|
||||
@@ -139,9 +142,8 @@ namespace thermion_filament
|
||||
void clearLights();
|
||||
void setPostProcessing(bool enabled);
|
||||
|
||||
void setRecording(bool recording);
|
||||
void setRecordingOutputDirectory(const char *path);
|
||||
void capture(uint8_t *out, bool useFence, void (*onComplete)());
|
||||
void capture(uint8_t *out, bool useFence, SwapChain* swapChain, void (*onComplete)());
|
||||
void capture(uint8_t *out, bool useFence, SwapChain* swapChain, RenderTarget* renderTarget, void (*onComplete)());
|
||||
|
||||
void setAntiAliasing(bool msaaEnabled, bool fxaaEnabled, bool taaEnabled);
|
||||
void setDepthOfField();
|
||||
@@ -164,10 +166,11 @@ namespace thermion_filament
|
||||
Engine *_engine = nullptr;
|
||||
thermion_filament::ThreadPool *_tp = nullptr;
|
||||
Renderer *_renderer = nullptr;
|
||||
SwapChain *_swapChain = nullptr;
|
||||
SceneManager *_sceneManager = nullptr;
|
||||
std::vector<RenderTarget*> _renderTargets;
|
||||
std::vector<SwapChain*> _swapChains;
|
||||
|
||||
std::mutex mtx; // mutex to ensure thread safety when removing assets
|
||||
std::mutex _renderMutex; // mutex to ensure thread safety when removing assets
|
||||
|
||||
std::vector<utils::Entity> _lights;
|
||||
Texture *_skyboxTexture = nullptr;
|
||||
@@ -208,12 +211,10 @@ namespace thermion_filament
|
||||
void savePng(void *data, size_t size, int frameNumber);
|
||||
void createBackgroundImage();
|
||||
|
||||
time_point_t _recordingStartTime = std::chrono::high_resolution_clock::now();
|
||||
|
||||
time_point_t _fpsCounterStartTime = std::chrono::high_resolution_clock::now();
|
||||
|
||||
bool _recording = false;
|
||||
std::string _recordingOutputDirectory = std::string("/tmp");
|
||||
std::mutex _recordingMutex;
|
||||
|
||||
std::mutex _imageMutex;
|
||||
double _cumulativeAnimationUpdateTime = 0;
|
||||
int _frameCount = 0;
|
||||
|
||||
@@ -57,13 +57,37 @@ extern "C"
|
||||
EMSCRIPTEN_KEEPALIVE TViewer *create_filament_viewer(const void *const context, const void *const loader, void *const platform, const char *uberArchivePath);
|
||||
EMSCRIPTEN_KEEPALIVE void destroy_filament_viewer(TViewer *viewer);
|
||||
EMSCRIPTEN_KEEPALIVE TSceneManager *Viewer_getSceneManager(TViewer *viewer);
|
||||
EMSCRIPTEN_KEEPALIVE TRenderTarget* Viewer_createRenderTarget(TViewer *viewer, intptr_t texture, uint32_t width, uint32_t height);
|
||||
EMSCRIPTEN_KEEPALIVE void Viewer_destroyRenderTarget(TViewer *viewer, TRenderTarget* tRenderTarget);
|
||||
EMSCRIPTEN_KEEPALIVE void Viewer_setRenderTarget(TViewer *viewer, TRenderTarget* tRenderTarget);
|
||||
EMSCRIPTEN_KEEPALIVE TSwapChain *Viewer_createSwapChain(TViewer *viewer, const void *const window, uint32_t width, uint32_t height);
|
||||
EMSCRIPTEN_KEEPALIVE void Viewer_destroySwapChain(TViewer *viewer, TSwapChain* swapChain);
|
||||
EMSCRIPTEN_KEEPALIVE bool Viewer_render(
|
||||
TViewer *viewer,
|
||||
TSwapChain *swapChain,
|
||||
uint64_t frameTimeInNanos,
|
||||
void *pixelBuffer,
|
||||
void (*callback)(void *buf, size_t size, void *data),
|
||||
void *data);
|
||||
EMSCRIPTEN_KEEPALIVE void Viewer_capture(
|
||||
TViewer *viewer,
|
||||
TSwapChain *swapChain,
|
||||
uint8_t *pixelBuffer,
|
||||
void (*callback)(void));
|
||||
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderTarget(
|
||||
TViewer *viewer,
|
||||
TSwapChain *swapChain,
|
||||
TRenderTarget *renderTarget,
|
||||
uint8_t *pixelBuffer,
|
||||
void (*callback)(void));
|
||||
|
||||
|
||||
// Engine
|
||||
EMSCRIPTEN_KEEPALIVE TEngine *Viewer_getEngine(TViewer* viewer);
|
||||
EMSCRIPTEN_KEEPALIVE TCamera *Engine_getCameraComponent(TEngine* tEngine, EntityId entityId);
|
||||
EMSCRIPTEN_KEEPALIVE void Engine_setTransform(TEngine* tEngine, EntityId entity, double4x4 transform);
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void create_render_target(TViewer *viewer, intptr_t texture, uint32_t width, uint32_t height);
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void clear_background_image(TViewer *viewer);
|
||||
EMSCRIPTEN_KEEPALIVE void set_background_image(TViewer *viewer, const char *path, bool fillHeight);
|
||||
EMSCRIPTEN_KEEPALIVE void set_background_image_position(TViewer *viewer, float x, float y, bool clamp);
|
||||
@@ -108,18 +132,8 @@ extern "C"
|
||||
EMSCRIPTEN_KEEPALIVE EntityId get_main_camera(TViewer *viewer);
|
||||
EMSCRIPTEN_KEEPALIVE bool set_camera(TViewer *viewer, EntityId entity, const char *nodeName);
|
||||
EMSCRIPTEN_KEEPALIVE void set_view_frustum_culling(TViewer *viewer, bool enabled);
|
||||
EMSCRIPTEN_KEEPALIVE bool render(
|
||||
TViewer *viewer,
|
||||
uint64_t frameTimeInNanos,
|
||||
void *pixelBuffer,
|
||||
void (*callback)(void *buf, size_t size, void *data),
|
||||
void *data);
|
||||
EMSCRIPTEN_KEEPALIVE void capture(
|
||||
TViewer *viewer,
|
||||
uint8_t *pixelBuffer,
|
||||
void (*callback)(void));
|
||||
EMSCRIPTEN_KEEPALIVE void create_swap_chain(TViewer *viewer, const void *const window, uint32_t width, uint32_t height);
|
||||
EMSCRIPTEN_KEEPALIVE void destroy_swap_chain(TViewer *viewer);
|
||||
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void set_frame_interval(TViewer *viewer, float interval);
|
||||
EMSCRIPTEN_KEEPALIVE void update_viewport(TViewer *viewer, uint32_t width, uint32_t height);
|
||||
EMSCRIPTEN_KEEPALIVE void scroll_begin(TViewer *viewer);
|
||||
@@ -271,8 +285,7 @@ extern "C"
|
||||
EMSCRIPTEN_KEEPALIVE int get_entity_count(TSceneManager *sceneManager, const EntityId target, bool renderableOnly);
|
||||
EMSCRIPTEN_KEEPALIVE void get_entities(TSceneManager *sceneManager, const EntityId target, bool renderableOnly, EntityId *out);
|
||||
EMSCRIPTEN_KEEPALIVE const char *get_entity_name_at(TSceneManager *sceneManager, const EntityId target, int index, bool renderableOnly);
|
||||
EMSCRIPTEN_KEEPALIVE void set_recording(TViewer *viewer, bool recording);
|
||||
EMSCRIPTEN_KEEPALIVE void set_recording_output_directory(TViewer *viewer, const char *outputDirectory);
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void ios_dummy();
|
||||
EMSCRIPTEN_KEEPALIVE void thermion_flutter_free(void *ptr);
|
||||
EMSCRIPTEN_KEEPALIVE void add_collision_component(TSceneManager *sceneManager, EntityId entityId, void (*callback)(const EntityId entityId1, const EntityId entityId2), bool affectsCollidingTransform);
|
||||
|
||||
@@ -25,12 +25,14 @@ extern "C"
|
||||
void (*renderCallback)(void *const renderCallbackOwner),
|
||||
void *const renderCallbackOwner,
|
||||
void (*callback)(TViewer *viewer));
|
||||
EMSCRIPTEN_KEEPALIVE void create_swap_chain_render_thread(TViewer *viewer, void *const surface, uint32_t width, uint32_t height, void (*onComplete)());
|
||||
EMSCRIPTEN_KEEPALIVE void destroy_swap_chain_render_thread(TViewer *viewer, void (*onComplete)());
|
||||
EMSCRIPTEN_KEEPALIVE void create_render_target_render_thread(TViewer *viewer, intptr_t nativeTextureId, uint32_t width, uint32_t height, void (*onComplete)());
|
||||
EMSCRIPTEN_KEEPALIVE void Viewer_createSwapChainRenderThread(TViewer *viewer, void *const surface, 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, TSwapChain* swapChain);
|
||||
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderThread(TViewer *viewer, TSwapChain* swapChain, uint8_t* out, void (*onComplete)());
|
||||
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderTargetRenderThread(TViewer *viewer, TSwapChain* swapChain, TRenderTarget* renderTarget, uint8_t* out, void (*onComplete)());
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void destroy_filament_viewer_render_thread(TViewer *viewer);
|
||||
EMSCRIPTEN_KEEPALIVE void render_render_thread(TViewer *viewer);
|
||||
EMSCRIPTEN_KEEPALIVE void capture_render_thread(TViewer *viewer, uint8_t* out, void (*onComplete)());
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE FilamentRenderCallback make_render_callback_fn_pointer(FilamentRenderCallback);
|
||||
EMSCRIPTEN_KEEPALIVE void set_rendering_render_thread(TViewer *viewer, bool rendering, void(*onComplete)());
|
||||
EMSCRIPTEN_KEEPALIVE void request_frame_render_thread(TViewer *viewer, void(*onComplete)());
|
||||
|
||||
@@ -739,7 +739,12 @@ namespace thermion_filament
|
||||
FilamentViewer::~FilamentViewer()
|
||||
{
|
||||
clearLights();
|
||||
destroySwapChain();
|
||||
for(auto swapChain : _swapChains) {
|
||||
_engine->destroy(swapChain);
|
||||
}
|
||||
|
||||
_swapChains.clear();
|
||||
|
||||
if (!_imageEntity.isNull())
|
||||
{
|
||||
_engine->destroy(_imageEntity);
|
||||
@@ -760,25 +765,29 @@ namespace thermion_filament
|
||||
|
||||
Renderer *FilamentViewer::getRenderer() { return _renderer; }
|
||||
|
||||
void FilamentViewer::createSwapChain(const void *window, uint32_t width, uint32_t height)
|
||||
SwapChain* FilamentViewer::createSwapChain(const void *window, uint32_t width, uint32_t height)
|
||||
{
|
||||
std::lock_guard lock(_renderMutex);
|
||||
SwapChain* swapChain;
|
||||
#if TARGET_OS_IPHONE
|
||||
_swapChain = _engine->createSwapChain((void *)window, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER);
|
||||
swapChain = _engine->createSwapChain((void *)window, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER);
|
||||
#else
|
||||
if (window)
|
||||
{
|
||||
_swapChain = _engine->createSwapChain((void *)window, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE);
|
||||
swapChain = _engine->createSwapChain((void *)window, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE);
|
||||
Log("Created window swapchain.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("Created headless swapchain.");
|
||||
_swapChain = _engine->createSwapChain(width, height, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE | filament::SwapChain::CONFIG_HAS_STENCIL_BUFFER);
|
||||
swapChain = _engine->createSwapChain(width, height, filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT | filament::backend::SWAP_CHAIN_CONFIG_READABLE | filament::SwapChain::CONFIG_HAS_STENCIL_BUFFER);
|
||||
}
|
||||
#endif
|
||||
_swapChains.push_back(swapChain);
|
||||
return swapChain;
|
||||
}
|
||||
|
||||
void FilamentViewer::createRenderTarget(intptr_t texture, uint32_t width, uint32_t height)
|
||||
RenderTarget* FilamentViewer::createRenderTarget(intptr_t texture, uint32_t width, uint32_t height)
|
||||
{
|
||||
// Create filament textures and render targets (note the color buffer has the import call)
|
||||
auto rtColor = filament::Texture::Builder()
|
||||
@@ -796,40 +805,44 @@ namespace thermion_filament
|
||||
.usage(filament::Texture::Usage::DEPTH_ATTACHMENT | filament::Texture::Usage::SAMPLEABLE)
|
||||
.format(filament::Texture::InternalFormat::DEPTH32F)
|
||||
.build(*_engine);
|
||||
_rt = filament::RenderTarget::Builder()
|
||||
auto rt = filament::RenderTarget::Builder()
|
||||
.texture(RenderTarget::AttachmentPoint::COLOR, rtColor)
|
||||
.texture(RenderTarget::AttachmentPoint::DEPTH, rtDepth)
|
||||
.build(*_engine);
|
||||
|
||||
_view->setRenderTarget(_rt);
|
||||
|
||||
Log("Created render target for texture id %ld (%u x %u)", (long)texture, width, height);
|
||||
return rt;
|
||||
}
|
||||
|
||||
void FilamentViewer::destroySwapChain()
|
||||
void FilamentViewer::destroyRenderTarget(RenderTarget* renderTarget) {
|
||||
std::lock_guard lock(_renderMutex);
|
||||
auto rtDepth = renderTarget->getTexture(RenderTarget::AttachmentPoint::DEPTH);
|
||||
if(rtDepth) {
|
||||
_engine->destroy(rtDepth);
|
||||
}
|
||||
auto rtColor = renderTarget->getTexture(RenderTarget::AttachmentPoint::COLOR);
|
||||
if(rtColor) {
|
||||
_engine->destroy(rtColor);
|
||||
}
|
||||
_engine->destroy(renderTarget);
|
||||
auto it = std::find(_renderTargets.begin(), _renderTargets.end(), renderTarget);
|
||||
if(it != _renderTargets.end()) {
|
||||
_renderTargets.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void FilamentViewer::setRenderTarget(RenderTarget *renderTarget) {
|
||||
std::lock_guard lock(_renderMutex);
|
||||
_view->setRenderTarget(renderTarget);
|
||||
}
|
||||
|
||||
void FilamentViewer::destroySwapChain(SwapChain *swapChain)
|
||||
{
|
||||
if (_rt)
|
||||
{
|
||||
_view->setRenderTarget(nullptr);
|
||||
auto rtDepth = _rt->getTexture(RenderTarget::AttachmentPoint::DEPTH);
|
||||
if(rtDepth) {
|
||||
_engine->destroy(rtDepth);
|
||||
Log("destroyed depth");
|
||||
}
|
||||
auto rtColor = _rt->getTexture(RenderTarget::AttachmentPoint::COLOR);
|
||||
if(rtColor) {
|
||||
_engine->destroy(rtColor);
|
||||
Log("destroyed color");
|
||||
}
|
||||
_engine->destroy(_rt);
|
||||
_rt = nullptr;
|
||||
}
|
||||
if (_swapChain)
|
||||
{
|
||||
_engine->destroy(_swapChain);
|
||||
_swapChain = nullptr;
|
||||
Log("Swapchain destroyed.");
|
||||
std::lock_guard lock(_renderMutex);
|
||||
auto it = std::find(_swapChains.begin(), _swapChains.end(), swapChain);
|
||||
if(it != _swapChains.end()) {
|
||||
_swapChains.erase(it);
|
||||
}
|
||||
_engine->destroy(swapChain);
|
||||
Log("Swapchain destroyed.");
|
||||
#ifdef __EMSCRIPTEN__
|
||||
_engine->execute();
|
||||
#else
|
||||
@@ -845,10 +858,10 @@ namespace thermion_filament
|
||||
|
||||
void FilamentViewer::removeEntity(EntityId asset)
|
||||
{
|
||||
mtx.lock();
|
||||
_renderMutex.lock();
|
||||
// todo - what if we are using a camera from this asset?
|
||||
_sceneManager->remove(asset);
|
||||
mtx.unlock();
|
||||
_renderMutex.unlock();
|
||||
}
|
||||
|
||||
///
|
||||
@@ -987,7 +1000,6 @@ namespace thermion_filament
|
||||
|
||||
void FilamentViewer::removeSkybox()
|
||||
{
|
||||
Log("Removing skybox");
|
||||
_scene->setSkybox(nullptr);
|
||||
if (_skybox)
|
||||
{
|
||||
@@ -1114,12 +1126,13 @@ namespace thermion_filament
|
||||
|
||||
bool FilamentViewer::render(
|
||||
uint64_t frameTimeInNanos,
|
||||
SwapChain* swapChain,
|
||||
void *pixelBuffer,
|
||||
void (*callback)(void *buf, size_t size, void *data),
|
||||
void *data)
|
||||
{
|
||||
|
||||
if (!_view || !_swapChain)
|
||||
if (!_view || !swapChain)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1152,7 +1165,7 @@ namespace thermion_filament
|
||||
}
|
||||
|
||||
// Render the scene, unless the renderer wants to skip the frame.
|
||||
bool beginFrame = _renderer->beginFrame(_swapChain, frameTimeInNanos);
|
||||
bool beginFrame = _renderer->beginFrame(swapChain, frameTimeInNanos);
|
||||
if (!beginFrame)
|
||||
{
|
||||
_skippedFrames++;
|
||||
@@ -1165,33 +1178,6 @@ namespace thermion_filament
|
||||
|
||||
_frameCount++;
|
||||
|
||||
if (_recording)
|
||||
{
|
||||
Viewport const &vp = _view->getViewport();
|
||||
size_t pixelBufferSize = vp.width * vp.height * 4;
|
||||
auto *pixelBuffer = new uint8_t[pixelBufferSize];
|
||||
auto callback = [](void *buf, size_t size, void *data)
|
||||
{
|
||||
auto frameCallbackData = (FrameCallbackData *)data;
|
||||
auto viewer = (FilamentViewer *)frameCallbackData->viewer;
|
||||
viewer->savePng(buf, size, frameCallbackData->frameNumber);
|
||||
delete frameCallbackData;
|
||||
};
|
||||
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
auto elapsed = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - _recordingStartTime).count());
|
||||
|
||||
auto frameNumber = uint32_t(floor(elapsed / _frameInterval));
|
||||
|
||||
auto userData = new FrameCallbackData{this, frameNumber};
|
||||
|
||||
auto pbd = Texture::PixelBufferDescriptor(
|
||||
pixelBuffer, pixelBufferSize,
|
||||
Texture::Format::RGBA,
|
||||
Texture::Type::UBYTE, nullptr, callback, userData);
|
||||
|
||||
_renderer->readPixels(_rt, 0, 0, vp.width, vp.height, std::move(pbd));
|
||||
}
|
||||
_renderer->endFrame();
|
||||
}
|
||||
#ifdef __EMSCRIPTEN__
|
||||
@@ -1206,9 +1192,14 @@ namespace thermion_filament
|
||||
}
|
||||
};
|
||||
|
||||
void FilamentViewer::capture(uint8_t *out, bool useFence, void (*onComplete)())
|
||||
void FilamentViewer::capture(uint8_t *out, bool useFence, SwapChain* swapChain, void (*onComplete)())
|
||||
{
|
||||
|
||||
if(!swapChain) {
|
||||
Log("NO SWAPCHAIN");
|
||||
return;
|
||||
}
|
||||
|
||||
Viewport const &vp = _view->getViewport();
|
||||
size_t pixelBufferSize = vp.width * vp.height * 4;
|
||||
auto *pixelBuffer = new uint8_t[pixelBufferSize];
|
||||
@@ -1240,18 +1231,9 @@ namespace thermion_filament
|
||||
pixelBuffer, pixelBufferSize,
|
||||
Texture::Format::RGBA,
|
||||
Texture::Type::UBYTE, dispatcher, callback, userData);
|
||||
_renderer->beginFrame(_swapChain, 0);
|
||||
|
||||
_renderer->render(_view);
|
||||
|
||||
if (_rt)
|
||||
{
|
||||
_renderer->readPixels(_rt, 0, 0, vp.width, vp.height, std::move(pbd));
|
||||
}
|
||||
else
|
||||
{
|
||||
_renderer->readPixels(0, 0, vp.width, vp.height, std::move(pbd));
|
||||
}
|
||||
_renderer->beginFrame(swapChain, 0);
|
||||
_renderer->render(_view);
|
||||
_renderer->readPixels(0, 0, vp.width, vp.height, std::move(pbd));
|
||||
_renderer->endFrame();
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
@@ -1263,74 +1245,57 @@ namespace thermion_filament
|
||||
}
|
||||
}
|
||||
|
||||
void FilamentViewer::savePng(void *buf, size_t size, int frameNumber)
|
||||
void FilamentViewer::capture(uint8_t *out, bool useFence, SwapChain* swapChain, RenderTarget* renderTarget, void (*onComplete)())
|
||||
{
|
||||
// std::lock_guard lock(_recordingMutex);
|
||||
// if (!_recording)
|
||||
// {
|
||||
// delete[] static_cast<uint8_t *>(buf);
|
||||
// return;
|
||||
// }
|
||||
|
||||
if(!renderTarget) {
|
||||
Log("NO SWAPCHAIN");
|
||||
return;
|
||||
}
|
||||
|
||||
Viewport const &vp = _view->getViewport();
|
||||
size_t pixelBufferSize = vp.width * vp.height * 4;
|
||||
auto *pixelBuffer = new uint8_t[pixelBufferSize];
|
||||
auto callback = [](void *buf, size_t size, void *data)
|
||||
{
|
||||
auto frameCallbackData = (std::vector<void *> *)data;
|
||||
uint8_t *out = (uint8_t *)(frameCallbackData->at(0));
|
||||
void *callbackPtr = frameCallbackData->at(1);
|
||||
|
||||
std::packaged_task<void()> lambda([=]() mutable
|
||||
{
|
||||
int digits = 6;
|
||||
std::ostringstream stringStream;
|
||||
stringStream << _recordingOutputDirectory << "/output_";
|
||||
stringStream << std::setfill('0') << std::setw(digits);
|
||||
stringStream << std::to_string(frameNumber);
|
||||
stringStream << ".png";
|
||||
memcpy(out, buf, size);
|
||||
delete frameCallbackData;
|
||||
if(callbackPtr) {
|
||||
void (*callback)(void) = (void (*)(void))callbackPtr;
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
std::string filename = stringStream.str();
|
||||
// Create a fence
|
||||
Fence* fence = nullptr;
|
||||
if(useFence) {
|
||||
fence = _engine->createFence();
|
||||
}
|
||||
|
||||
std::ofstream wf(filename, std::ios::out | std::ios::binary);
|
||||
auto userData = new std::vector<void *>{out, (void *)onComplete};
|
||||
|
||||
LinearImage image(toLinearWithAlpha<uint8_t>(vp.width, vp.height, vp.width * 4,
|
||||
static_cast<uint8_t *>(buf)));
|
||||
auto dispatcher = new CaptureCallbackHandler();
|
||||
|
||||
auto result = image::ImageEncoder::encode(
|
||||
wf, image::ImageEncoder::Format::PNG, image, std::string(""), std::string(""));
|
||||
auto pbd = Texture::PixelBufferDescriptor(
|
||||
pixelBuffer, pixelBufferSize,
|
||||
Texture::Format::RGBA,
|
||||
Texture::Type::UBYTE, dispatcher, callback, userData);
|
||||
_renderer->beginFrame(swapChain, 0);
|
||||
_renderer->render(_view);
|
||||
_renderer->readPixels(renderTarget, 0, 0, vp.width, vp.height, std::move(pbd));
|
||||
_renderer->endFrame();
|
||||
|
||||
delete[] static_cast<uint8_t *>(buf);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
Log("Failed to encode");
|
||||
}
|
||||
|
||||
wf.close();
|
||||
if (!wf.good())
|
||||
{
|
||||
Log("Write failed!");
|
||||
} });
|
||||
_tp->add_task(lambda);
|
||||
#ifdef __EMSCRIPTEN__
|
||||
_engine->execute();
|
||||
emscripten_webgl_commit_frame();
|
||||
#endif
|
||||
if(fence) {
|
||||
Fence::waitAndDestroy(fence);
|
||||
}
|
||||
|
||||
void FilamentViewer::setRecordingOutputDirectory(const char *path)
|
||||
{
|
||||
_recordingOutputDirectory = std::string(path);
|
||||
auto outputDirAsPath = std::filesystem::path(path);
|
||||
if (!std::filesystem::is_directory(outputDirAsPath))
|
||||
{
|
||||
std::filesystem::create_directories(outputDirAsPath);
|
||||
}
|
||||
}
|
||||
|
||||
void FilamentViewer::setRecording(bool recording)
|
||||
{
|
||||
// std::lock_guard lock(_recordingMutex);
|
||||
if (recording)
|
||||
{
|
||||
_tp = new thermion_filament::ThreadPool(16);
|
||||
_recordingStartTime = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
else
|
||||
{
|
||||
delete _tp;
|
||||
}
|
||||
this->_recording = recording;
|
||||
}
|
||||
|
||||
Camera* FilamentViewer::getCamera(EntityId entity) {
|
||||
@@ -1381,7 +1346,7 @@ namespace thermion_filament
|
||||
|
||||
void FilamentViewer::grabBegin(float x, float y, bool pan)
|
||||
{
|
||||
if (!_view || !_mainCamera || !_swapChain)
|
||||
if (!_view || !_mainCamera)
|
||||
{
|
||||
Log("View not ready, ignoring grab");
|
||||
return;
|
||||
@@ -1395,7 +1360,7 @@ namespace thermion_filament
|
||||
|
||||
void FilamentViewer::grabUpdate(float x, float y)
|
||||
{
|
||||
if (!_view || !_swapChain)
|
||||
if (!_view )
|
||||
{
|
||||
Log("View not ready, ignoring grab");
|
||||
return;
|
||||
@@ -1413,7 +1378,7 @@ namespace thermion_filament
|
||||
|
||||
void FilamentViewer::grabEnd()
|
||||
{
|
||||
if (!_view || !_mainCamera || !_swapChain)
|
||||
if (!_view || !_mainCamera )
|
||||
{
|
||||
Log("View not ready, ignoring grab");
|
||||
return;
|
||||
|
||||
@@ -56,9 +56,23 @@ extern "C"
|
||||
return reinterpret_cast<TEngine*>(engine);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void create_render_target(TViewer *viewer, intptr_t texture, uint32_t width, uint32_t height)
|
||||
EMSCRIPTEN_KEEPALIVE TRenderTarget* Viewer_createRenderTarget(TViewer *tViewer, intptr_t texture, uint32_t width, uint32_t height)
|
||||
{
|
||||
((FilamentViewer *)viewer)->createRenderTarget(texture, width, height);
|
||||
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
|
||||
auto renderTarget = viewer->createRenderTarget(texture, width, height);
|
||||
return reinterpret_cast<TRenderTarget*>(renderTarget);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void Viewer_destroyRenderTarget(TViewer *tViewer, TRenderTarget* tRenderTarget) {
|
||||
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
|
||||
auto renderTarget = reinterpret_cast<RenderTarget*>(tRenderTarget);
|
||||
viewer->destroyRenderTarget(renderTarget);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void Viewer_setRenderTarget(TViewer *tViewer, TRenderTarget* tRenderTarget) {
|
||||
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
|
||||
auto renderTarget = reinterpret_cast<RenderTarget*>(tRenderTarget);
|
||||
viewer->setRenderTarget(renderTarget);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void destroy_filament_viewer(TViewer *viewer)
|
||||
@@ -349,18 +363,22 @@ extern "C"
|
||||
cam->setModelMatrix(mat);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE bool render(
|
||||
TViewer *viewer,
|
||||
EMSCRIPTEN_KEEPALIVE bool Viewer_render(
|
||||
TViewer *tViewer,
|
||||
TSwapChain *tSwapChain,
|
||||
uint64_t frameTimeInNanos,
|
||||
void *pixelBuffer,
|
||||
void (*callback)(void *buf, size_t size, void *data),
|
||||
void *data)
|
||||
{
|
||||
return ((FilamentViewer *)viewer)->render(frameTimeInNanos, pixelBuffer, callback, data);
|
||||
auto swapChain = reinterpret_cast<SwapChain*>(tSwapChain);
|
||||
auto viewer = reinterpret_cast<FilamentViewer*>(tViewer);
|
||||
return viewer->render(frameTimeInNanos, swapChain, pixelBuffer, callback, data);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void capture(
|
||||
TViewer *viewer,
|
||||
EMSCRIPTEN_KEEPALIVE void Viewer_capture(
|
||||
TViewer *tViewer,
|
||||
TSwapChain* tSwapChain,
|
||||
uint8_t *pixelBuffer,
|
||||
void (*callback)(void))
|
||||
{
|
||||
@@ -369,7 +387,27 @@ extern "C"
|
||||
#else
|
||||
bool useFence = false;
|
||||
#endif
|
||||
((FilamentViewer *)viewer)->capture(pixelBuffer, useFence, callback);
|
||||
auto swapChain = reinterpret_cast<SwapChain*>(tSwapChain);
|
||||
auto viewer = reinterpret_cast<FilamentViewer*>(tViewer);
|
||||
viewer->capture(pixelBuffer, useFence, swapChain, callback);
|
||||
};
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderTarget(
|
||||
TViewer *tViewer,
|
||||
TSwapChain* tSwapChain,
|
||||
TRenderTarget *tRenderTarget,
|
||||
uint8_t *pixelBuffer,
|
||||
void (*callback)(void))
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
bool useFence = true;
|
||||
#else
|
||||
bool useFence = false;
|
||||
#endif
|
||||
auto swapChain = reinterpret_cast<SwapChain*>(tSwapChain);
|
||||
auto renderTarget = reinterpret_cast<RenderTarget*>(tRenderTarget);
|
||||
auto viewer = reinterpret_cast<FilamentViewer*>(tViewer);
|
||||
viewer->capture(pixelBuffer, useFence, swapChain, renderTarget, callback);
|
||||
};
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void set_frame_interval(
|
||||
@@ -379,14 +417,17 @@ extern "C"
|
||||
((FilamentViewer *)viewer)->setFrameInterval(frameInterval);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void destroy_swap_chain(TViewer *viewer)
|
||||
EMSCRIPTEN_KEEPALIVE void Viewer_destroySwapChain(TViewer *tViewer, TSwapChain* tSwapChain)
|
||||
{
|
||||
((FilamentViewer *)viewer)->destroySwapChain();
|
||||
auto viewer = reinterpret_cast<FilamentViewer*>(tViewer);
|
||||
auto swapChain = reinterpret_cast<SwapChain*>(tSwapChain);
|
||||
viewer->destroySwapChain(swapChain);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void create_swap_chain(TViewer *viewer, const void *const window, uint32_t width, uint32_t height)
|
||||
{
|
||||
((FilamentViewer *)viewer)->createSwapChain(window, width, height);
|
||||
EMSCRIPTEN_KEEPALIVE TSwapChain *Viewer_createSwapChain(TViewer *tViewer, const void *const window, uint32_t width, uint32_t height) {
|
||||
auto viewer = reinterpret_cast<FilamentViewer*>(tViewer);
|
||||
auto swapChain = viewer->createSwapChain(window, width, height);
|
||||
return reinterpret_cast<TSwapChain*>(swapChain);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void update_viewport(TViewer *viewer, uint32_t width, uint32_t height)
|
||||
@@ -837,16 +878,6 @@ extern "C"
|
||||
return ((SceneManager *)sceneManager)->getEntityNameAt(target, index, renderableOnly);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void set_recording(TViewer *viewer, bool recording)
|
||||
{
|
||||
((FilamentViewer *)viewer)->setRecording(recording);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void set_recording_output_directory(TViewer *viewer, const char *outputDirectory)
|
||||
{
|
||||
((FilamentViewer *)viewer)->setRecordingOutputDirectory(outputDirectory);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void ios_dummy()
|
||||
{
|
||||
Log("Dummy called");
|
||||
|
||||
@@ -49,8 +49,8 @@ public:
|
||||
|
||||
~RenderLoop()
|
||||
{
|
||||
_render = false;
|
||||
_stop = true;
|
||||
target = nullptr;
|
||||
_cv.notify_one();
|
||||
#ifdef __EMSCRIPTEN__
|
||||
pthread_join(t, NULL);
|
||||
@@ -82,20 +82,20 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void requestFrame(void (*callback)())
|
||||
void requestFrame(SwapChain* swapChain, void (*callback)())
|
||||
{
|
||||
this->_render = true;
|
||||
this->target = swapChain;
|
||||
this->_requestFrameRenderCallback = callback;
|
||||
}
|
||||
|
||||
void iter()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
if (_render)
|
||||
if (target)
|
||||
{
|
||||
doRender();
|
||||
doRender(target);
|
||||
this->_requestFrameRenderCallback();
|
||||
_render = false;
|
||||
target = nullptr;
|
||||
|
||||
// Calculate and print FPS
|
||||
auto currentTime = std::chrono::high_resolution_clock::now();
|
||||
@@ -123,7 +123,7 @@ public:
|
||||
}
|
||||
|
||||
_cv.wait_for(lock, std::chrono::microseconds(1000), [this]
|
||||
{ return !_tasks.empty() || _stop || _render; });
|
||||
{ return !_tasks.empty() || _stop; });
|
||||
|
||||
if (_stop)
|
||||
return;
|
||||
@@ -169,14 +169,14 @@ public:
|
||||
{
|
||||
std::packaged_task<void()> lambda([=]() mutable
|
||||
{
|
||||
_render = false;
|
||||
target = nullptr;
|
||||
_viewer = nullptr;
|
||||
destroy_filament_viewer(reinterpret_cast<TViewer*>(viewer)); });
|
||||
auto fut = add_task(lambda);
|
||||
fut.wait();
|
||||
}
|
||||
|
||||
bool doRender()
|
||||
bool doRender(SwapChain *swapChain)
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
if (emscripten_is_webgl_context_lost(_context) == EM_TRUE)
|
||||
@@ -187,7 +187,8 @@ public:
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
auto rendered = render(_viewer, 0, nullptr, nullptr, nullptr);
|
||||
TSwapChain *tSwapChain = reinterpret_cast<TSwapChain*>(swapChain);
|
||||
auto rendered = Viewer_render(_viewer, tSwapChain, 0, nullptr, nullptr, nullptr);
|
||||
if (_renderCallback)
|
||||
{
|
||||
_renderCallback(_renderCallbackOwner);
|
||||
@@ -217,7 +218,7 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
std::atomic_bool _render = false;
|
||||
SwapChain* target;
|
||||
|
||||
private:
|
||||
void(*_requestFrameRenderCallback)() = nullptr;
|
||||
@@ -270,16 +271,31 @@ extern "C"
|
||||
_rl = nullptr;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void create_swap_chain_render_thread(TViewer *viewer,
|
||||
EMSCRIPTEN_KEEPALIVE void Viewer_createSwapChainRenderThread(TViewer *viewer,
|
||||
void *const surface,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
void (*onComplete)())
|
||||
void (*onComplete)(TSwapChain*))
|
||||
{
|
||||
std::packaged_task<void()> lambda(
|
||||
[=]() mutable
|
||||
{
|
||||
create_swap_chain(viewer, surface, width, height);
|
||||
auto *swapChain = Viewer_createSwapChain(viewer, surface, width, height);
|
||||
#ifdef __EMSCRIPTEN__
|
||||
MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0); }, onComplete);
|
||||
#else
|
||||
onComplete(swapChain);
|
||||
#endif
|
||||
});
|
||||
auto fut = _rl->add_task(lambda);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void Viewer_destroySwapChainRenderThread(TViewer *viewer, TSwapChain *swapChain, void (*onComplete)())
|
||||
{
|
||||
std::packaged_task<void()> lambda(
|
||||
[=]() mutable
|
||||
{
|
||||
Viewer_destroySwapChain(viewer, swapChain);
|
||||
#ifdef __EMSCRIPTEN__
|
||||
MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0); }, onComplete);
|
||||
#else
|
||||
@@ -289,40 +305,8 @@ extern "C"
|
||||
auto fut = _rl->add_task(lambda);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void destroy_swap_chain_render_thread(TViewer *viewer, void (*onComplete)())
|
||||
{
|
||||
std::packaged_task<void()> lambda(
|
||||
[=]() mutable
|
||||
{
|
||||
destroy_swap_chain(viewer);
|
||||
#ifdef __EMSCRIPTEN__
|
||||
MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0); }, onComplete);
|
||||
#else
|
||||
onComplete();
|
||||
#endif
|
||||
});
|
||||
auto fut = _rl->add_task(lambda);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void create_render_target_render_thread(TViewer *viewer,
|
||||
intptr_t nativeTextureId,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
void (*onComplete)())
|
||||
{
|
||||
std::packaged_task<void()> lambda([=]() mutable
|
||||
{
|
||||
create_render_target(viewer, nativeTextureId, width, height);
|
||||
#ifdef __EMSCRIPTEN__
|
||||
MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0); }, onComplete);
|
||||
#else
|
||||
onComplete();
|
||||
#endif
|
||||
});
|
||||
auto fut = _rl->add_task(lambda);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void request_frame_render_thread(TViewer *viewer, void(*onComplete)())
|
||||
EMSCRIPTEN_KEEPALIVE void Viewer_requestFrameRenderThread(TViewer *viewer, TSwapChain* tSwapChain, void(*onComplete)())
|
||||
{
|
||||
if (!_rl)
|
||||
{
|
||||
@@ -330,7 +314,7 @@ extern "C"
|
||||
}
|
||||
else
|
||||
{
|
||||
_rl->requestFrame(onComplete);
|
||||
_rl->requestFrame(reinterpret_cast<SwapChain*>(tSwapChain), onComplete);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,17 +327,24 @@ extern "C"
|
||||
auto fut = _rl->add_task(lambda);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void render_render_thread(TViewer *viewer)
|
||||
EMSCRIPTEN_KEEPALIVE void Viewer_renderRenderThread(TViewer *viewer, TSwapChain *tSwapChain)
|
||||
{
|
||||
std::packaged_task<void()> lambda([=]() mutable
|
||||
{ _rl->doRender(); });
|
||||
{ _rl->doRender(reinterpret_cast<SwapChain*>(tSwapChain)); });
|
||||
auto fut = _rl->add_task(lambda);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void capture_render_thread(TViewer *viewer, uint8_t *pixelBuffer, void (*onComplete)())
|
||||
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderThread(TViewer *viewer, TSwapChain *tSwapChain, uint8_t *pixelBuffer, void (*onComplete)())
|
||||
{
|
||||
std::packaged_task<void()> lambda([=]() mutable
|
||||
{ capture(viewer, pixelBuffer, onComplete); });
|
||||
{ Viewer_capture(viewer, tSwapChain, pixelBuffer, onComplete); });
|
||||
auto fut = _rl->add_task(lambda);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderTargetRenderThread(TViewer *viewer, TSwapChain *tSwapChain, TRenderTarget* tRenderTarget, uint8_t *pixelBuffer, void (*onComplete)())
|
||||
{
|
||||
std::packaged_task<void()> lambda([=]() mutable
|
||||
{ Viewer_captureRenderTarget(viewer, tSwapChain, tRenderTarget, pixelBuffer, onComplete); });
|
||||
auto fut = _rl->add_task(lambda);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ void main() async {
|
||||
|
||||
group('camera', () {
|
||||
test('getCameraModelMatrix, getCameraPosition, rotation', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
var matrix = await viewer.getCameraModelMatrix();
|
||||
expect(matrix.trace(), 4);
|
||||
|
||||
@@ -26,7 +26,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('getCameraViewMatrix', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
|
||||
var modelMatrix = await viewer.getCameraModelMatrix();
|
||||
var viewMatrix = await viewer.getCameraViewMatrix();
|
||||
@@ -49,20 +49,20 @@ void main() async {
|
||||
});
|
||||
|
||||
test('getCameraProjectionMatrix', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
var projectionMatrix = await viewer.getCameraProjectionMatrix();
|
||||
print(projectionMatrix);
|
||||
});
|
||||
|
||||
test('getCameraCullingProjectionMatrix', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
var matrix = await viewer.getCameraCullingProjectionMatrix();
|
||||
print(matrix);
|
||||
throw Exception("TODO");
|
||||
});
|
||||
|
||||
test('getCameraFrustum', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
var frustum = await viewer.getCameraFrustum();
|
||||
print(frustum.plane5.normal);
|
||||
print(frustum.plane5.constant);
|
||||
@@ -76,7 +76,7 @@ void main() async {
|
||||
|
||||
test('set custom projection/culling matrix', () async {
|
||||
var viewer =
|
||||
await createViewer(bg: kRed, cameraPosition: Vector3(0, 0, 4));
|
||||
await testHelper.createViewer(bg: kRed, cameraPosition: Vector3(0, 0, 4));
|
||||
var camera = await viewer.getMainCamera();
|
||||
final cube = await viewer.createGeometry(GeometryHelper.cube());
|
||||
|
||||
@@ -98,7 +98,7 @@ void main() async {
|
||||
|
||||
test('setting transform on camera updates model matrix (no parent)',
|
||||
() async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
|
||||
var cameraEntity = await viewer.getMainCameraEntity();
|
||||
var camera = await viewer.getMainCamera();
|
||||
@@ -114,7 +114,7 @@ void main() async {
|
||||
|
||||
test('setting transform on camera updates model matrix (with parent)',
|
||||
() async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
|
||||
var cameraEntity = await viewer.getMainCameraEntity();
|
||||
var camera = await viewer.getMainCamera();
|
||||
@@ -140,7 +140,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('create camera', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
|
||||
await viewer.setCameraPosition(0, 0, 5);
|
||||
await viewer.setBackgroundColor(1.0, 0.0, 1.0, 1.0);
|
||||
|
||||
56
thermion_dart/test/gltf_tests.dart
Normal file
56
thermion_dart/test/gltf_tests.dart
Normal file
@@ -0,0 +1,56 @@
|
||||
import 'dart:io';
|
||||
import 'package:thermion_dart/thermion_dart.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:vector_math/vector_math_64.dart';
|
||||
|
||||
import 'helpers.dart';
|
||||
|
||||
void main() async {
|
||||
final testHelper = TestHelper("gltf");
|
||||
group("gltf", () {
|
||||
test('load glb from file', () async {
|
||||
var viewer = await testHelper.createViewer(bg: kRed, cameraPosition: Vector3(0, 1, 5));
|
||||
var model = await viewer.loadGlb("file://${testHelper.testDir}/assets/cube.glb");
|
||||
await viewer
|
||||
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5));
|
||||
await testHelper.capture(viewer, "load_glb_from_file");
|
||||
await viewer.dispose();
|
||||
});
|
||||
|
||||
test('load glb from buffer', () async {
|
||||
var viewer = await testHelper.createViewer();
|
||||
var buffer = File("${testHelper.testDir}/cube.glb").readAsBytesSync();
|
||||
var model = await viewer.loadGlbFromBuffer(buffer);
|
||||
await viewer.transformToUnitCube(model);
|
||||
await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0);
|
||||
await viewer.setCameraPosition(0, 1, 5);
|
||||
await viewer
|
||||
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5));
|
||||
await testHelper.capture(viewer, "load_glb_from_buffer");
|
||||
});
|
||||
|
||||
test('load glb from buffer with priority', () async {
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.addDirectLight(DirectLight.sun());
|
||||
await viewer.setBackgroundColor(1.0, 1.0, 1.0, 1.0);
|
||||
await viewer.setCameraPosition(0, 3, 5);
|
||||
await viewer
|
||||
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5));
|
||||
|
||||
var buffer = File("${testHelper.testDir}/cube.glb").readAsBytesSync();
|
||||
var model1 = await viewer.loadGlbFromBuffer(buffer, priority: 7);
|
||||
var model2 = await viewer.loadGlbFromBuffer(buffer, priority: 0);
|
||||
|
||||
for (final entity in await viewer.getChildEntities(model1, true)) {
|
||||
await viewer.setMaterialPropertyFloat4(
|
||||
entity, "baseColorFactor", 0, 0, 0, 1.0, 1.0);
|
||||
}
|
||||
for (final entity in await viewer.getChildEntities(model2, true)) {
|
||||
await viewer.setMaterialPropertyFloat4(
|
||||
entity, "baseColorFactor", 0, 0, 1.0, 0.0, 1.0);
|
||||
}
|
||||
await testHelper.capture(viewer, "load_glb_from_buffer_with_priority");
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import 'package:image/image.dart';
|
||||
import 'package:thermion_dart/src/swift/swift_bindings.g.dart';
|
||||
import 'package:thermion_dart/src/utils/dart_resources.dart';
|
||||
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_dart.g.dart';
|
||||
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_viewer_ffi.dart';
|
||||
import 'package:thermion_dart/src/viewer/src/ffi/thermion_viewer_ffi.dart';
|
||||
import 'package:thermion_dart/thermion_dart.dart';
|
||||
import 'package:vector_math/vector_math_64.dart';
|
||||
@@ -55,7 +56,6 @@ extension on Uri {
|
||||
String get name => pathSegments.where((e) => e != '').last;
|
||||
}
|
||||
|
||||
|
||||
Future<Uint8List> savePixelBufferToBmp(
|
||||
Uint8List pixelBuffer, int width, int height, String outputPath) async {
|
||||
var data = await pixelBufferToBmp(pixelBuffer, width, height);
|
||||
@@ -65,7 +65,8 @@ Future<Uint8List> savePixelBufferToBmp(
|
||||
}
|
||||
|
||||
class TestHelper {
|
||||
|
||||
late SwapChain swapChain;
|
||||
late RenderTarget renderTarget;
|
||||
late Directory outDir;
|
||||
late String testDir;
|
||||
|
||||
@@ -81,7 +82,7 @@ class TestHelper {
|
||||
Future capture(ThermionViewer viewer, String outputFilename) async {
|
||||
await Future.delayed(Duration(milliseconds: 10));
|
||||
var outPath = p.join(outDir.path, "$outputFilename.bmp");
|
||||
var pixelBuffer = await viewer.capture();
|
||||
var pixelBuffer = await viewer.capture(swapChain); //, renderTarget: renderTarget);
|
||||
await savePixelBufferToBmp(
|
||||
pixelBuffer,
|
||||
viewer.viewportDimensions.$1.toInt(),
|
||||
@@ -89,6 +90,53 @@ class TestHelper {
|
||||
outPath);
|
||||
return pixelBuffer;
|
||||
}
|
||||
|
||||
Future<ThermionViewer> createViewer(
|
||||
{img.Color? bg,
|
||||
Vector3? cameraPosition,
|
||||
viewportDimensions = (width: 500, height: 500)}) async {
|
||||
final packageUri = findPackageRoot('thermion_dart');
|
||||
|
||||
final lib = ThermionDartTexture1(DynamicLibrary.open(
|
||||
'${packageUri.toFilePath()}/native/lib/macos/swift/libthermion_swift.dylib'));
|
||||
final object = ThermionDartTexture.new1(lib);
|
||||
object.initWithWidth_height_(
|
||||
viewportDimensions.width, viewportDimensions.height);
|
||||
|
||||
final resourceLoader = calloc<ResourceLoaderWrapper>(1);
|
||||
var loadToOut = NativeCallable<
|
||||
Void Function(Pointer<Char>,
|
||||
Pointer<ResourceBuffer>)>.listener(DartResourceLoader.loadResource);
|
||||
|
||||
resourceLoader.ref.loadToOut = loadToOut.nativeFunction;
|
||||
var freeResource = NativeCallable<Void Function(ResourceBuffer)>.listener(
|
||||
DartResourceLoader.freeResource);
|
||||
resourceLoader.ref.freeResource = freeResource.nativeFunction;
|
||||
|
||||
var viewer = ThermionViewerFFI(resourceLoader: resourceLoader.cast<Void>());
|
||||
|
||||
await viewer.initialized;
|
||||
swapChain = await viewer.createSwapChain(
|
||||
viewportDimensions.width, viewportDimensions.height);
|
||||
renderTarget = await viewer.createRenderTarget(
|
||||
viewportDimensions.width,
|
||||
viewportDimensions.height,
|
||||
object.metalTextureAddress);
|
||||
await viewer.setRenderTarget(renderTarget as FFIRenderTarget);
|
||||
await viewer.updateViewportAndCameraProjection(
|
||||
viewportDimensions.width.toDouble(),
|
||||
viewportDimensions.height.toDouble());
|
||||
if (bg != null) {
|
||||
await viewer.setBackgroundColor(
|
||||
bg.r.toDouble(), bg.g.toDouble(), bg.b.toDouble(), bg.a.toDouble());
|
||||
}
|
||||
|
||||
if (cameraPosition != null) {
|
||||
await viewer.setCameraPosition(
|
||||
cameraPosition.x, cameraPosition.y, cameraPosition.z);
|
||||
}
|
||||
return viewer;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Uint8List> pixelBufferToBmp(
|
||||
@@ -203,50 +251,6 @@ int _linearToSRGB(double linearValue) {
|
||||
}
|
||||
}
|
||||
|
||||
Future<ThermionViewer> createViewer(
|
||||
{img.Color? bg,
|
||||
Vector3? cameraPosition,
|
||||
viewportDimensions = (width: 500, height: 500)}) async {
|
||||
final packageUri = findPackageRoot('thermion_dart');
|
||||
|
||||
final lib = ThermionDartTexture1(DynamicLibrary.open(
|
||||
'${packageUri.toFilePath()}/native/lib/macos/swift/libthermion_swift.dylib'));
|
||||
final object = ThermionDartTexture.new1(lib);
|
||||
object.initWithWidth_height_(
|
||||
viewportDimensions.width, viewportDimensions.height);
|
||||
|
||||
final resourceLoader = calloc<ResourceLoaderWrapper>(1);
|
||||
var loadToOut = NativeCallable<
|
||||
Void Function(Pointer<Char>,
|
||||
Pointer<ResourceBuffer>)>.listener(DartResourceLoader.loadResource);
|
||||
|
||||
resourceLoader.ref.loadToOut = loadToOut.nativeFunction;
|
||||
var freeResource = NativeCallable<Void Function(ResourceBuffer)>.listener(
|
||||
DartResourceLoader.freeResource);
|
||||
resourceLoader.ref.freeResource = freeResource.nativeFunction;
|
||||
|
||||
var viewer = ThermionViewerFFI(resourceLoader: resourceLoader.cast<Void>());
|
||||
|
||||
await viewer.initialized;
|
||||
await viewer.createSwapChain(viewportDimensions.width.toDouble(),
|
||||
viewportDimensions.height.toDouble());
|
||||
await viewer.createRenderTarget(viewportDimensions.width.toDouble(),
|
||||
viewportDimensions.height.toDouble(), object.metalTextureAddress);
|
||||
await viewer.updateViewportAndCameraProjection(
|
||||
viewportDimensions.width.toDouble(),
|
||||
viewportDimensions.height.toDouble());
|
||||
if (bg != null) {
|
||||
await viewer.setBackgroundColor(
|
||||
bg.r.toDouble(), bg.g.toDouble(), bg.b.toDouble(), bg.a.toDouble());
|
||||
}
|
||||
|
||||
if (cameraPosition != null) {
|
||||
await viewer.setCameraPosition(
|
||||
cameraPosition.x, cameraPosition.y, cameraPosition.z);
|
||||
}
|
||||
return viewer;
|
||||
}
|
||||
|
||||
Uint8List poissonBlend(List<Uint8List> textures, int width, int height) {
|
||||
final int numTextures = textures.length;
|
||||
final int size = width * height;
|
||||
|
||||
@@ -15,24 +15,22 @@ void main() async {
|
||||
|
||||
group('background', () {
|
||||
test('set background color to solid green', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setBackgroundColor(0.0, 1.0, 0.0, 1.0);
|
||||
await testHelper.capture(viewer, "set_background_color_to_solid_green");
|
||||
await viewer.dispose();
|
||||
});
|
||||
|
||||
test('set background color to full transparency', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setBackgroundColor(0.0, 1.0, 0.0, 0.0);
|
||||
await testHelper.capture(
|
||||
viewer, "set_background_color_to_transparent_green");
|
||||
await viewer.dispose();
|
||||
});
|
||||
|
||||
|
||||
|
||||
test('set background image', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setBackgroundImage(
|
||||
"file:///${testHelper.testDir}/assets/cube_texture_512x512.png");
|
||||
await viewer.setPostProcessing(true);
|
||||
@@ -42,57 +40,11 @@ void main() async {
|
||||
});
|
||||
});
|
||||
|
||||
group("gltf", () {
|
||||
test('load glb from file', () async {
|
||||
var viewer = await createViewer();
|
||||
var model = await viewer.loadGlb("file://${testHelper.testDir}/cube.glb");
|
||||
await viewer.transformToUnitCube(model);
|
||||
await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0);
|
||||
await viewer.setCameraPosition(0, 1, 5);
|
||||
await viewer
|
||||
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5));
|
||||
await testHelper.capture(viewer, "load_glb_from_file");
|
||||
});
|
||||
|
||||
test('load glb from buffer', () async {
|
||||
var viewer = await createViewer();
|
||||
var buffer = File("${testHelper.testDir}/cube.glb").readAsBytesSync();
|
||||
var model = await viewer.loadGlbFromBuffer(buffer);
|
||||
await viewer.transformToUnitCube(model);
|
||||
await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0);
|
||||
await viewer.setCameraPosition(0, 1, 5);
|
||||
await viewer
|
||||
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5));
|
||||
await testHelper.capture(viewer, "load_glb_from_buffer");
|
||||
});
|
||||
|
||||
test('load glb from buffer with priority', () async {
|
||||
var viewer = await createViewer();
|
||||
await viewer.addDirectLight(DirectLight.sun());
|
||||
await viewer.setBackgroundColor(1.0, 1.0, 1.0, 1.0);
|
||||
await viewer.setCameraPosition(0, 3, 5);
|
||||
await viewer
|
||||
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5));
|
||||
|
||||
var buffer = File("${testHelper.testDir}/cube.glb").readAsBytesSync();
|
||||
var model1 = await viewer.loadGlbFromBuffer(buffer, priority: 7);
|
||||
var model2 = await viewer.loadGlbFromBuffer(buffer, priority: 0);
|
||||
|
||||
for (final entity in await viewer.getChildEntities(model1, true)) {
|
||||
await viewer.setMaterialPropertyFloat4(
|
||||
entity, "baseColorFactor", 0, 0, 0, 1.0, 1.0);
|
||||
}
|
||||
for (final entity in await viewer.getChildEntities(model2, true)) {
|
||||
await viewer.setMaterialPropertyFloat4(
|
||||
entity, "baseColorFactor", 0, 0, 1.0, 0.0, 1.0);
|
||||
}
|
||||
await testHelper.capture(viewer, "load_glb_from_buffer_with_priority");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
group("scene update events", () {
|
||||
test('add light fires SceneUpdateEvent', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
|
||||
final success = Completer<bool>();
|
||||
var light = DirectLight(
|
||||
@@ -116,7 +68,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('remove light fires SceneUpdateEvent', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
|
||||
final success = Completer<bool>();
|
||||
var light = await viewer.addDirectLight(DirectLight.point());
|
||||
@@ -135,7 +87,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('add geometry fires SceneUpdateEvent', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
|
||||
final success = Completer<bool>();
|
||||
var geometry = GeometryHelper.cube();
|
||||
@@ -153,7 +105,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('remove geometry fires SceneUpdateEvent', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
var geometry = await viewer.createGeometry(GeometryHelper.cube());
|
||||
final success = Completer<bool>();
|
||||
|
||||
@@ -171,7 +123,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('loadGlb fires SceneUpdateEvent', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
|
||||
final success = Completer<bool>();
|
||||
|
||||
@@ -191,7 +143,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('remove glb fires SceneUpdateEvent', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
final uri = "${testHelper.testDir}/cube.glb";
|
||||
var entity = await viewer.loadGlb(uri, keepData: false);
|
||||
|
||||
@@ -211,7 +163,7 @@ void main() async {
|
||||
|
||||
group("custom geometry", () {
|
||||
test('create cube (no uvs/normals)', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.addLight(LightType.SUN, 6500, 1000000, 0, 0, 0, 0, 0, -1);
|
||||
await viewer.setCameraPosition(0, 2, 6);
|
||||
await viewer
|
||||
@@ -224,7 +176,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('create cube (no normals)', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
var light = await viewer.addLight(
|
||||
LightType.POINT, 6500, 10000000, 0, 2, 0, 0, 0, 0,
|
||||
falloffRadius: 100.0);
|
||||
@@ -238,7 +190,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('create cube (with normals)', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
|
||||
var light = await viewer.addLight(
|
||||
LightType.POINT, 6500, 10000000, 0, 2, 0, 0, 0, 0,
|
||||
@@ -254,7 +206,7 @@ void main() async {
|
||||
|
||||
test('create cube with custom ubershader material instance (color)',
|
||||
() async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.addLight(LightType.SUN, 6500, 1000000, 0, 0, 0, 0, 0, -1);
|
||||
await viewer.setCameraPosition(0, 2, 6);
|
||||
await viewer
|
||||
@@ -276,7 +228,7 @@ void main() async {
|
||||
|
||||
test('create cube with custom ubershader material instance (texture)',
|
||||
() async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.addLight(LightType.SUN, 6500, 1000000, 0, 0, 0, 0, 0, -1);
|
||||
await viewer.setCameraPosition(0, 2, 6);
|
||||
await viewer
|
||||
@@ -299,7 +251,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('unlit material with color only', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setCameraPosition(0, 0, 6);
|
||||
await viewer.setBackgroundColor(1.0, 0.0, 0.0, 1.0);
|
||||
await viewer.setPostProcessing(true);
|
||||
@@ -318,7 +270,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('create cube with custom material instance (unlit)', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setCameraPosition(0, 2, 6);
|
||||
await viewer
|
||||
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8));
|
||||
@@ -367,7 +319,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('create sphere (no normals)', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0);
|
||||
await viewer.setCameraPosition(0, 0, 6);
|
||||
await viewer
|
||||
@@ -378,7 +330,7 @@ void main() async {
|
||||
|
||||
group("MaterialInstance", () {
|
||||
test('disable depth write', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setBackgroundColor(1.0, 0.0, 0.0, 1.0);
|
||||
await viewer.setCameraPosition(0, 0, 6);
|
||||
await viewer.addDirectLight(
|
||||
@@ -467,7 +419,7 @@ void main() async {
|
||||
|
||||
group("materials", () {
|
||||
test('set float4 material property for custom geometry', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
|
||||
await viewer.setCameraPosition(0, 0, 6);
|
||||
await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0);
|
||||
@@ -482,7 +434,7 @@ void main() async {
|
||||
await testHelper.capture(viewer, "set_material_float4_post");
|
||||
});
|
||||
test('set float material property for custom geometry', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
|
||||
await viewer.setCameraPosition(0, 0, 6);
|
||||
await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0);
|
||||
@@ -500,7 +452,7 @@ void main() async {
|
||||
|
||||
test('set float material property (roughness) for custom geometry',
|
||||
() async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
|
||||
await viewer.setCameraPosition(0, 0, 6);
|
||||
await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0);
|
||||
@@ -522,7 +474,7 @@ void main() async {
|
||||
group("transforms & parenting", () {
|
||||
test('set multiple transforms simultaneously with setTransforms', () async {
|
||||
var viewer =
|
||||
await createViewer(bg: kRed, cameraPosition: Vector3(0, 0, 5));
|
||||
await testHelper.createViewer(bg: kRed, cameraPosition: Vector3(0, 0, 5));
|
||||
final cube1 = await viewer.createGeometry(GeometryHelper.cube());
|
||||
final cube2 = await viewer.createGeometry(GeometryHelper.cube());
|
||||
|
||||
@@ -534,14 +486,14 @@ void main() async {
|
||||
Matrix4.translation(Vector3(1, 0, 0))
|
||||
]);
|
||||
|
||||
await viewer.render();
|
||||
await viewer.render(testHelper.swapChain);
|
||||
|
||||
await testHelper.capture(viewer, "set_multiple_transforms");
|
||||
});
|
||||
|
||||
test('getParent and getAncestor both return null when entity has no parent',
|
||||
() async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
|
||||
final cube = await viewer.createGeometry(GeometryHelper.cube());
|
||||
|
||||
@@ -552,7 +504,7 @@ void main() async {
|
||||
test(
|
||||
'getParent returns the parent entity after one has been set via setParent',
|
||||
() async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
|
||||
final cube1 = await viewer.createGeometry(GeometryHelper.cube());
|
||||
|
||||
@@ -566,7 +518,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('getAncestor returns the ultimate parent entity', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
|
||||
final grandparent = await viewer.createGeometry(GeometryHelper.cube());
|
||||
final parent = await viewer.createGeometry(GeometryHelper.cube());
|
||||
@@ -579,7 +531,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('set position based on screenspace coord', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
print(await viewer.getCameraFov(true));
|
||||
await viewer.createIbl(1.0, 1.0, 1.0, 1000);
|
||||
await viewer.setCameraPosition(0, 0, 6);
|
||||
@@ -591,7 +543,7 @@ void main() async {
|
||||
await viewer.queuePositionUpdateFromViewportCoords(cube, 0, 0);
|
||||
|
||||
// we need an explicit render call here to process the transform queue
|
||||
await viewer.render();
|
||||
await viewer.render(testHelper.swapChain);
|
||||
|
||||
await testHelper.capture(viewer, "set_position_from_viewport_coords");
|
||||
});
|
||||
@@ -599,7 +551,7 @@ void main() async {
|
||||
|
||||
group("layers & overlays", () {
|
||||
test('enable grid overlay', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setBackgroundColor(0, 0, 0, 1);
|
||||
await viewer
|
||||
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8));
|
||||
@@ -612,7 +564,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('load glb from buffer with layer', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
|
||||
await viewer.setBackgroundColor(1, 0, 1, 1);
|
||||
await viewer.setCameraPosition(0, 2, 5);
|
||||
@@ -629,7 +581,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('change layer visibility at runtime', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
|
||||
await viewer.setBackgroundColor(1, 0, 1, 1);
|
||||
await viewer.setCameraPosition(0, 2, 5);
|
||||
@@ -719,7 +671,7 @@ void main() async {
|
||||
|
||||
group("stencil", () {
|
||||
test('set stencil highlight for glb', () async {
|
||||
final viewer = await createViewer();
|
||||
final viewer = await testHelper.createViewer();
|
||||
var model = await viewer.loadGlb("${testHelper.testDir}/cube.glb", keepData: true);
|
||||
await viewer.setPostProcessing(true);
|
||||
|
||||
@@ -736,7 +688,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('set stencil highlight for geometry', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setPostProcessing(true);
|
||||
await viewer.setBackgroundColor(0.0, 1.0, 0.0, 1.0);
|
||||
await viewer.setCameraPosition(0, 2, 5);
|
||||
@@ -754,7 +706,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('set stencil highlight for gltf asset', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setPostProcessing(true);
|
||||
await viewer.setBackgroundColor(0.0, 1.0, 0.0, 1.0);
|
||||
await viewer.setCameraPosition(0, 1, 5);
|
||||
@@ -774,7 +726,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('set stencil highlight for multiple geometry ', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setPostProcessing(true);
|
||||
await viewer.setBackgroundColor(0.0, 1.0, 0.0, 1.0);
|
||||
await viewer.setCameraPosition(0, 1, 5);
|
||||
@@ -797,7 +749,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('set stencil highlight for multiple gltf assets ', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setPostProcessing(true);
|
||||
await viewer.setBackgroundColor(0.0, 1.0, 0.0, 1.0);
|
||||
await viewer.setCameraPosition(0, 1, 5);
|
||||
@@ -824,7 +776,7 @@ void main() async {
|
||||
|
||||
group("texture", () {
|
||||
test("create/apply/dispose texture", () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
|
||||
var textureData =
|
||||
File("${testHelper.testDir}/assets/cube_texture_512x512.png").readAsBytesSync();
|
||||
@@ -863,7 +815,7 @@ void main() async {
|
||||
|
||||
group("render thread", () {
|
||||
test("request frame on render thread", () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
viewer.requestFrame();
|
||||
|
||||
await Future.delayed(Duration(milliseconds: 20));
|
||||
@@ -875,7 +827,7 @@ void main() async {
|
||||
// test("unproject", () async {
|
||||
// final dimensions = (width: 1280, height: 768);
|
||||
|
||||
// var viewer = await createViewer(viewportDimensions: dimensions);
|
||||
// var viewer = await testHelper.createViewer(viewportDimensions: dimensions);
|
||||
// await viewer.setPostProcessing(false);
|
||||
// // await viewer.setToneMapping(ToneMapper.LINEAR);
|
||||
// await viewer.setBackgroundColor(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
@@ -15,7 +15,7 @@ void main() async {
|
||||
|
||||
group("texture tests", () {
|
||||
test('apply texture to custom ubershader material instance', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.addLight(LightType.SUN, 6500, 1000000, 0, 0, 0, 0, 0, -1);
|
||||
await viewer.setCameraPosition(0, 2, 6);
|
||||
await viewer
|
||||
@@ -38,7 +38,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('unlit material with color only', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setCameraPosition(0, 0, 6);
|
||||
await viewer.setBackgroundColor(1.0, 0.0, 0.0, 1.0);
|
||||
await viewer.setPostProcessing(true);
|
||||
@@ -57,7 +57,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('create cube with custom material instance (unlit)', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setCameraPosition(0, 2, 6);
|
||||
await viewer
|
||||
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8));
|
||||
@@ -106,7 +106,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('create sphere (no normals)', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0);
|
||||
await viewer.setCameraPosition(0, 0, 6);
|
||||
await viewer
|
||||
@@ -117,7 +117,7 @@ void main() async {
|
||||
|
||||
group("MaterialInstance", () {
|
||||
test('disable depth write', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setBackgroundColor(1.0, 0.0, 0.0, 1.0);
|
||||
await viewer.setCameraPosition(0, 0, 6);
|
||||
await viewer.addDirectLight(
|
||||
@@ -149,7 +149,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('set uv scaling (unlit)', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setBackgroundColor(1.0, 0.0, 0.0, 1.0);
|
||||
await viewer.setCameraPosition(0, 0, 6);
|
||||
await viewer.addDirectLight(
|
||||
@@ -174,7 +174,7 @@ void main() async {
|
||||
|
||||
group("stencil", () {
|
||||
test('set stencil highlight for glb', () async {
|
||||
final viewer = await createViewer();
|
||||
final viewer = await testHelper.createViewer();
|
||||
var model = await viewer.loadGlb("${testHelper.testDir}/cube.glb", keepData: true);
|
||||
await viewer.setPostProcessing(true);
|
||||
|
||||
@@ -191,7 +191,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('set stencil highlight for geometry', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setPostProcessing(true);
|
||||
await viewer.setBackgroundColor(0.0, 1.0, 0.0, 1.0);
|
||||
await viewer.setCameraPosition(0, 2, 5);
|
||||
@@ -209,7 +209,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('set stencil highlight for gltf asset', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setPostProcessing(true);
|
||||
await viewer.setBackgroundColor(0.0, 1.0, 0.0, 1.0);
|
||||
await viewer.setCameraPosition(0, 1, 5);
|
||||
@@ -229,7 +229,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('set stencil highlight for multiple geometry ', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setPostProcessing(true);
|
||||
await viewer.setBackgroundColor(0.0, 1.0, 0.0, 1.0);
|
||||
await viewer.setCameraPosition(0, 1, 5);
|
||||
@@ -252,7 +252,7 @@ void main() async {
|
||||
});
|
||||
|
||||
test('set stencil highlight for multiple gltf assets ', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.setPostProcessing(true);
|
||||
await viewer.setBackgroundColor(0.0, 1.0, 0.0, 1.0);
|
||||
await viewer.setCameraPosition(0, 1, 5);
|
||||
@@ -279,7 +279,7 @@ void main() async {
|
||||
|
||||
group("texture", () {
|
||||
test("create/apply/dispose texture", () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
|
||||
var textureData =
|
||||
File("${testHelper.testDir}/assets/cube_texture_512x512.png").readAsBytesSync();
|
||||
@@ -318,7 +318,7 @@ void main() async {
|
||||
|
||||
group("render thread", () {
|
||||
test("request frame on render thread", () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
viewer.requestFrame();
|
||||
|
||||
await Future.delayed(Duration(milliseconds: 20));
|
||||
@@ -330,7 +330,7 @@ void main() async {
|
||||
// test("unproject", () async {
|
||||
// final dimensions = (width: 1280, height: 768);
|
||||
|
||||
// var viewer = await createViewer(viewportDimensions: dimensions);
|
||||
// var viewer = await testHelper.createViewer(viewportDimensions: dimensions);
|
||||
// await viewer.setPostProcessing(false);
|
||||
// // await viewer.setToneMapping(ToneMapper.LINEAR);
|
||||
// await viewer.setBackgroundColor(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
@@ -8,7 +8,7 @@ void main() async {
|
||||
|
||||
group("skybox", () {
|
||||
test('load skybox', () async {
|
||||
var viewer = await createViewer();
|
||||
var viewer = await testHelper.createViewer();
|
||||
await viewer.loadSkybox(
|
||||
"file:///${testHelper.testDir}/assets/default_env_skybox.ktx");
|
||||
await testHelper.capture(viewer, "load_skybox");
|
||||
|
||||
@@ -20,6 +20,9 @@ class ThermionFlutterFFI extends ThermionFlutterPlatform {
|
||||
|
||||
ThermionFlutterFFI._() {}
|
||||
|
||||
RenderTarget? _renderTarget;
|
||||
SwapChain? _swapChain;
|
||||
|
||||
static void registerWith() {
|
||||
ThermionFlutterPlatform.instance = ThermionFlutterFFI._();
|
||||
}
|
||||
@@ -125,7 +128,7 @@ class ThermionFlutterFFI extends ThermionFlutterPlatform {
|
||||
return _textures.first;
|
||||
} else {
|
||||
await _viewer!.setRendering(false);
|
||||
await _viewer!.destroySwapChain();
|
||||
await _swapChain?.destroy();
|
||||
await destroyTexture(_textures.first);
|
||||
_textures.clear();
|
||||
}
|
||||
@@ -152,27 +155,26 @@ class ThermionFlutterFFI extends ThermionFlutterPlatform {
|
||||
final texture = ThermionFlutterTexture(flutterTextureId, hardwareTextureId,
|
||||
physicalWidth, physicalHeight, surfaceAddress);
|
||||
|
||||
await _viewer?.createSwapChain(
|
||||
physicalWidth.toDouble(), physicalHeight.toDouble(),
|
||||
await _viewer?.createSwapChain(physicalWidth, physicalHeight,
|
||||
surface: texture.surfaceAddress == null
|
||||
? nullptr
|
||||
: Pointer<Void>.fromAddress(texture.surfaceAddress!));
|
||||
|
||||
if (texture.hardwareTextureId != null) {
|
||||
if (_renderTarget != null) {
|
||||
await _renderTarget!.destroy();
|
||||
}
|
||||
// ignore: unused_local_variable
|
||||
var renderTarget = await _viewer?.createRenderTarget(
|
||||
physicalWidth.toDouble(),
|
||||
physicalHeight.toDouble(),
|
||||
_renderTarget = await _viewer?.createRenderTarget(
|
||||
physicalWidth,
|
||||
physicalHeight,
|
||||
texture.hardwareTextureId!);
|
||||
}
|
||||
|
||||
await _viewer?.updateViewportAndCameraProjection(
|
||||
physicalWidth.toDouble(), physicalHeight.toDouble());
|
||||
_viewer?.render();
|
||||
_creatingTexture = false;
|
||||
|
||||
_textures.add(texture);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
@@ -217,7 +219,7 @@ class ThermionFlutterFFI extends ThermionFlutterPlatform {
|
||||
_resizing = true;
|
||||
bool wasRendering = _viewer!.rendering;
|
||||
await _viewer!.setRendering(false);
|
||||
await _viewer!.destroySwapChain();
|
||||
await _swapChain?.destroy();
|
||||
await destroyTexture(texture);
|
||||
|
||||
var result = await _channel
|
||||
@@ -230,7 +232,7 @@ class ThermionFlutterFFI extends ThermionFlutterPlatform {
|
||||
var newTexture =
|
||||
ThermionFlutterTexture(result[0], result[1], width, height, result[2]);
|
||||
|
||||
await _viewer!.createSwapChain(width.toDouble(), height.toDouble(),
|
||||
await _viewer!.createSwapChain(width, height,
|
||||
surface: newTexture.surfaceAddress == null
|
||||
? nullptr
|
||||
: Pointer<Void>.fromAddress(newTexture.surfaceAddress!));
|
||||
|
||||
Reference in New Issue
Block a user