feat: add capture() function and expose viewportDimensions on ThermionViewer (allows easier saving of captured images to PNG)
This commit is contained in:
@@ -1059,6 +1059,15 @@ external void render_ffi(
|
|||||||
ffi.Pointer<ffi.Void> viewer,
|
ffi.Pointer<ffi.Void> viewer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ffi.Native<
|
||||||
|
ffi.Void Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Uint8>,
|
||||||
|
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>()
|
||||||
|
external void capture_ffi(
|
||||||
|
ffi.Pointer<ffi.Void> viewer,
|
||||||
|
ffi.Pointer<ffi.Uint8> out,
|
||||||
|
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
|
||||||
|
);
|
||||||
|
|
||||||
@ffi.Native<FilamentRenderCallback Function(FilamentRenderCallback)>()
|
@ffi.Native<FilamentRenderCallback Function(FilamentRenderCallback)>()
|
||||||
external FilamentRenderCallback make_render_callback_fn_pointer(
|
external FilamentRenderCallback make_render_callback_fn_pointer(
|
||||||
FilamentRenderCallback arg0,
|
FilamentRenderCallback arg0,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:thermion_dart/thermion_dart/scene.dart';
|
import 'package:thermion_dart/thermion_dart/scene.dart';
|
||||||
import 'package:vector_math/vector_math_64.dart';
|
import 'package:vector_math/vector_math_64.dart';
|
||||||
@@ -19,11 +20,11 @@ enum LightType {
|
|||||||
SPOT,
|
SPOT,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ShadowType {
|
enum ShadowType {
|
||||||
PCF, //!< percentage-closer filtered shadows (default)
|
PCF, //!< percentage-closer filtered shadows (default)
|
||||||
VSM, //!< variance shadows
|
VSM, //!< variance shadows
|
||||||
DPCF, //!< PCF with contact hardening simulation
|
DPCF, //!< PCF with contact hardening simulation
|
||||||
PCSS, //!< PCF with soft shadows and contact hardening
|
PCSS, //!< PCF with soft shadows and contact hardening
|
||||||
}
|
}
|
||||||
|
|
||||||
// copied from filament/backened/DriverEnums.h
|
// copied from filament/backened/DriverEnums.h
|
||||||
@@ -57,6 +58,11 @@ abstract class ThermionViewer {
|
|||||||
|
|
||||||
Future<bool> get initialized;
|
Future<bool> get initialized;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// The current dimensions of the viewport (in physical pixels).
|
||||||
|
///
|
||||||
|
late (double, double) viewportDimensions;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// The result(s) of calling [pick] (see below).
|
/// The result(s) of calling [pick] (see below).
|
||||||
/// This may be a broadcast stream, so you should ensure you have subscribed to this stream before calling [pick].
|
/// This may be a broadcast stream, so you should ensure you have subscribed to this stream before calling [pick].
|
||||||
@@ -79,6 +85,12 @@ abstract class ThermionViewer {
|
|||||||
///
|
///
|
||||||
Future render();
|
Future render();
|
||||||
|
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Render a single frame to the viewport and copy the pixel buffer to [out].
|
||||||
|
///
|
||||||
|
Future<Uint8List> capture();
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Sets the framerate for continuous rendering when [setRendering] is enabled.
|
/// Sets the framerate for continuous rendering when [setRendering] is enabled.
|
||||||
///
|
///
|
||||||
@@ -270,10 +282,9 @@ abstract class ThermionViewer {
|
|||||||
{List<String>? targetMeshNames});
|
{List<String>? targetMeshNames});
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Clear all current morph animations for [entity].
|
/// Clear all current morph animations for [entity].
|
||||||
///
|
///
|
||||||
Future clearMorphAnimationData(
|
Future clearMorphAnimationData(ThermionEntity entity);
|
||||||
ThermionEntity entity);
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Resets all bones in the given entity to their rest pose.
|
/// Resets all bones in the given entity to their rest pose.
|
||||||
@@ -736,7 +747,6 @@ abstract class ThermionViewer {
|
|||||||
/// Register a callback to be invoked when this viewer is disposed.
|
/// Register a callback to be invoked when this viewer is disposed.
|
||||||
///
|
///
|
||||||
void onDispose(Future Function() callback);
|
void onDispose(Future Function() callback);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class AbstractGizmo {
|
abstract class AbstractGizmo {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
import 'dart:typed_data';
|
||||||
import 'package:animation_tools_dart/animation_tools_dart.dart';
|
import 'package:animation_tools_dart/animation_tools_dart.dart';
|
||||||
import 'package:thermion_dart/thermion_dart/compatibility/compatibility.dart';
|
import 'package:thermion_dart/thermion_dart/compatibility/compatibility.dart';
|
||||||
import 'package:thermion_dart/thermion_dart/entities/gizmo.dart';
|
import 'package:thermion_dart/thermion_dart/entities/gizmo.dart';
|
||||||
@@ -24,8 +25,6 @@ class ThermionViewerFFI extends ThermionViewer {
|
|||||||
|
|
||||||
double _pixelRatio = 1.0;
|
double _pixelRatio = 1.0;
|
||||||
|
|
||||||
late (double, double) viewportDimensions;
|
|
||||||
|
|
||||||
Pointer<Void>? _sceneManager;
|
Pointer<Void>? _sceneManager;
|
||||||
|
|
||||||
Pointer<Void>? _viewer;
|
Pointer<Void>? _viewer;
|
||||||
@@ -163,6 +162,23 @@ class ThermionViewerFFI extends ThermionViewer {
|
|||||||
render_ffi(_viewer!);
|
render_ffi(_viewer!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Future<Uint8List> capture() async {
|
||||||
|
final length = this.viewportDimensions.$1.toInt() *
|
||||||
|
this.viewportDimensions.$2.toInt() *
|
||||||
|
4;
|
||||||
|
final out = allocator<Uint8>(length);
|
||||||
|
await withVoidCallback((cb) {
|
||||||
|
capture_ffi(_viewer!, out, cb);
|
||||||
|
});
|
||||||
|
final data = Uint8List.fromList(out.asTypedList(length));
|
||||||
|
allocator.free(out);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
@@ -198,7 +214,6 @@ class ThermionViewerFFI extends ThermionViewer {
|
|||||||
await callback.call();
|
await callback.call();
|
||||||
}
|
}
|
||||||
_onDispose.clear();
|
_onDispose.clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -602,13 +617,9 @@ class ThermionViewerFFI extends ThermionViewer {
|
|||||||
Future clearMorphAnimationData(ThermionEntity entity) async {
|
Future clearMorphAnimationData(ThermionEntity entity) async {
|
||||||
var meshEntities = await getChildEntities(entity, true);
|
var meshEntities = await getChildEntities(entity, true);
|
||||||
|
|
||||||
for(final childEntity in meshEntities) {
|
for (final childEntity in meshEntities) {
|
||||||
clear_morph_animation(
|
clear_morph_animation(_sceneManager!, childEntity);
|
||||||
_sceneManager!,
|
|
||||||
childEntity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -982,8 +993,8 @@ class ThermionViewerFFI extends ThermionViewer {
|
|||||||
bool replaceActive = true,
|
bool replaceActive = true,
|
||||||
double crossfade = 0.0,
|
double crossfade = 0.0,
|
||||||
double startOffset = 0.0}) async {
|
double startOffset = 0.0}) async {
|
||||||
play_animation(
|
play_animation(_sceneManager!, entity, index, loop, reverse, replaceActive,
|
||||||
_sceneManager!, entity, index, loop, reverse, replaceActive, crossfade, startOffset);
|
crossfade, startOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:thermion_dart/thermion_dart/scene.dart';
|
import 'package:thermion_dart/thermion_dart/scene.dart';
|
||||||
import 'package:thermion_dart/thermion_dart/thermion_viewer.dart';
|
import 'package:thermion_dart/thermion_dart/thermion_viewer.dart';
|
||||||
@@ -750,4 +751,10 @@ class ThermionViewerStub extends ThermionViewer {
|
|||||||
// TODO: implement setSoftShadowOptions
|
// TODO: implement setSoftShadowOptions
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Uint8List> capture() {
|
||||||
|
// TODO: implement capture
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ namespace thermion_filament
|
|||||||
|
|
||||||
void setRecording(bool recording);
|
void setRecording(bool recording);
|
||||||
void setRecordingOutputDirectory(const char *path);
|
void setRecordingOutputDirectory(const char *path);
|
||||||
|
void capture(uint8_t *out, void (*onComplete)());
|
||||||
|
|
||||||
void setAntiAliasing(bool msaaEnabled, bool fxaaEnabled, bool taaEnabled);
|
void setAntiAliasing(bool msaaEnabled, bool fxaaEnabled, bool taaEnabled);
|
||||||
void setDepthOfField();
|
void setDepthOfField();
|
||||||
|
|||||||
@@ -107,6 +107,10 @@ extern "C"
|
|||||||
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);
|
||||||
|
EMSCRIPTEN_KEEPALIVE void capture(
|
||||||
|
const void *const viewer,
|
||||||
|
uint8_t *pixelBuffer,
|
||||||
|
void (*callback)(void));
|
||||||
EMSCRIPTEN_KEEPALIVE void create_swap_chain(const void *const viewer, const void *const window, uint32_t width, uint32_t height);
|
EMSCRIPTEN_KEEPALIVE void create_swap_chain(const void *const viewer, const void *const window, uint32_t width, uint32_t height);
|
||||||
EMSCRIPTEN_KEEPALIVE void destroy_swap_chain(const void *const viewer);
|
EMSCRIPTEN_KEEPALIVE void destroy_swap_chain(const void *const viewer);
|
||||||
EMSCRIPTEN_KEEPALIVE void set_frame_interval(const void *const viewer, float interval);
|
EMSCRIPTEN_KEEPALIVE void set_frame_interval(const void *const viewer, float interval);
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ extern "C"
|
|||||||
EMSCRIPTEN_KEEPALIVE void create_render_target_ffi(void *const viewer, intptr_t nativeTextureId, uint32_t width, uint32_t height, void (*onComplete)());
|
EMSCRIPTEN_KEEPALIVE void create_render_target_ffi(void *const viewer, intptr_t nativeTextureId, uint32_t width, uint32_t height, void (*onComplete)());
|
||||||
EMSCRIPTEN_KEEPALIVE void destroy_filament_viewer_ffi(void *const viewer);
|
EMSCRIPTEN_KEEPALIVE void destroy_filament_viewer_ffi(void *const viewer);
|
||||||
EMSCRIPTEN_KEEPALIVE void render_ffi(void *const viewer);
|
EMSCRIPTEN_KEEPALIVE void render_ffi(void *const viewer);
|
||||||
|
EMSCRIPTEN_KEEPALIVE void capture_ffi(void *const 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_ffi(void *const viewer, bool rendering, void(*onComplete)());
|
EMSCRIPTEN_KEEPALIVE void set_rendering_ffi(void *const viewer, bool rendering, void(*onComplete)());
|
||||||
EMSCRIPTEN_KEEPALIVE void set_frame_interval_ffi(void *const viewer, float frameInterval);
|
EMSCRIPTEN_KEEPALIVE void set_frame_interval_ffi(void *const viewer, float frameInterval);
|
||||||
|
|||||||
@@ -1186,6 +1186,49 @@ namespace thermion_filament
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FilamentViewer::capture(uint8_t *out, void (*onComplete)()) {
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
void (*callback)(void) = (void (*)(void))callbackPtr;
|
||||||
|
memcpy(out, buf, size);
|
||||||
|
delete frameCallbackData;
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto userData = new std::vector<void*> { out, (void*)onComplete } ;
|
||||||
|
|
||||||
|
auto pbd = Texture::PixelBufferDescriptor(
|
||||||
|
pixelBuffer, pixelBufferSize,
|
||||||
|
Texture::Format::RGBA,
|
||||||
|
Texture::Type::UBYTE, nullptr, callback, userData);
|
||||||
|
_renderer->beginFrame(_swapChain, 0);
|
||||||
|
|
||||||
|
_renderer->render(_view);
|
||||||
|
|
||||||
|
if(_rt) {
|
||||||
|
_renderer->readPixels(_rt, 0, 0, vp.width, vp.height, std::move(pbd));
|
||||||
|
} else {
|
||||||
|
_renderer->readPixels(0, 0, vp.width, vp.height, std::move(pbd));
|
||||||
|
}
|
||||||
|
_renderer->endFrame();
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
_engine->execute();
|
||||||
|
emscripten_webgl_commit_frame();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void FilamentViewer::savePng(void *buf, size_t size, int frameNumber)
|
void FilamentViewer::savePng(void *buf, size_t size, int frameNumber)
|
||||||
{
|
{
|
||||||
// std::lock_guard lock(_recordingMutex);
|
// std::lock_guard lock(_recordingMutex);
|
||||||
|
|||||||
@@ -329,6 +329,13 @@ extern "C"
|
|||||||
((FilamentViewer *)viewer)->render(frameTimeInNanos, pixelBuffer, callback, data);
|
((FilamentViewer *)viewer)->render(frameTimeInNanos, pixelBuffer, callback, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EMSCRIPTEN_KEEPALIVE void capture(
|
||||||
|
const void *const viewer,
|
||||||
|
uint8_t *pixelBuffer,
|
||||||
|
void (*callback)(void)) {
|
||||||
|
((FilamentViewer *)viewer)->capture(pixelBuffer, callback);
|
||||||
|
};
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void set_frame_interval(
|
EMSCRIPTEN_KEEPALIVE void set_frame_interval(
|
||||||
const void *const viewer,
|
const void *const viewer,
|
||||||
float frameInterval)
|
float frameInterval)
|
||||||
|
|||||||
@@ -373,6 +373,12 @@ extern "C"
|
|||||||
auto fut = _rl->add_task(lambda);
|
auto fut = _rl->add_task(lambda);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EMSCRIPTEN_KEEPALIVE void capture_ffi(void *const viewer, uint8_t* pixelBuffer, void (*onComplete)()) {
|
||||||
|
std::packaged_task<void()> lambda([=]() mutable
|
||||||
|
{ capture(viewer, pixelBuffer, onComplete); });
|
||||||
|
auto fut = _rl->add_task(lambda);
|
||||||
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void
|
EMSCRIPTEN_KEEPALIVE void
|
||||||
set_background_color_ffi(void *const viewer, const float r, const float g,
|
set_background_color_ffi(void *const viewer, const float r, const float g,
|
||||||
const float b, const float a)
|
const float b, const float a)
|
||||||
|
|||||||
Reference in New Issue
Block a user