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