refactoring + texture projection

This commit is contained in:
Nick Fisher
2025-03-25 09:39:02 +08:00
parent 0cbbc058e0
commit 999b1e613f
33 changed files with 7357 additions and 1168 deletions

View File

@@ -342,4 +342,10 @@ class BackgroundImage extends ThermionAsset {
// TODO: implement transformToUnitCube
throw UnimplementedError();
}
@override
Future<MaterialInstance> getMaterialInstanceAt({ThermionEntity? entity, int index = 0}) {
// TODO: implement getMaterialInstanceAt
throw UnimplementedError();
}
}

View File

@@ -370,6 +370,14 @@ class FFIAsset extends ThermionAsset {
// }
}
Future<MaterialInstance> getMaterialInstanceAt(
{ThermionEntity? entity, int index = 0}) async {
entity ??= this.entity;
var ptr = RenderableManager_getMaterialInstanceAt(
Engine_getRenderableManager(app.engine), entity, 0);
return FFIMaterialInstance(ptr, app);
}
///
///
///

View File

@@ -120,12 +120,7 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
///
///
///
Future setRenderable(covariant FFIView view, bool renderable) async {
await view.setRenderable(renderable);
await _updateRenderableSwapChains();
}
Future _updateRenderableSwapChains() async {
Future updateRenderOrder() async {
for (final swapChain in _swapChains.keys) {
final views = _swapChains[swapChain];
if (views == null) {
@@ -174,6 +169,28 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
return FFISwapChain(swapChain);
}
///
///
///
Future<View> createView() async {
final view = await FFIView(
await withPointerCallback<TView>(
(cb) => Engine_createViewRenderThread(engine, cb)),
this);
await view.setFrustumCullingEnabled(true);
View_setBlendMode(view.view, TBlendMode.TRANSLUCENT);
View_setShadowsEnabled(view.view, false);
View_setStencilBufferEnabled(view.view, false);
View_setAntiAliasing(view.view, false, false, false);
View_setDitheringEnabled(view.view, false);
View_setRenderQuality(view.view, TQualityLevel.MEDIUM);
return view;
}
Future<Scene> createScene() async {
return FFIScene(Engine_createScene(engine));
}
///
///
///
@@ -195,7 +212,7 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
continue;
}
for (final view in _swapChains[swapChain]!) {
await setRenderable(view, false);
await view.setRenderable(false);
}
}
for (final swapChain in _swapChains.keys.toList()) {
@@ -224,10 +241,30 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
///
Future<RenderTarget> createRenderTarget(int width, int height,
{covariant FFITexture? color, covariant FFITexture? depth}) async {
if (color == null) {
color = await createTexture(width, height,
flags: {
TextureUsage.TEXTURE_USAGE_SAMPLEABLE,
TextureUsage.TEXTURE_USAGE_COLOR_ATTACHMENT,
TextureUsage.TEXTURE_USAGE_BLIT_SRC
},
textureFormat: TextureFormat.RGBA8) as FFITexture;
}
if (depth == null) {
depth = await createTexture(width, height,
flags: {
TextureUsage.TEXTURE_USAGE_SAMPLEABLE,
TextureUsage.TEXTURE_USAGE_DEPTH_ATTACHMENT
},
textureFormat: TextureFormat.DEPTH32F) as FFITexture;
}
final renderTarget = await withPointerCallback<TRenderTarget>((cb) {
RenderTarget_createRenderThread(engine, width, height,
color?.pointer ?? nullptr, depth?.pointer ?? nullptr, cb);
RenderTarget_createRenderThread(
engine, width, height, color!.pointer, depth!.pointer, cb);
});
if (renderTarget == nullptr) {
throw Exception("Failed to create RenderTarget");
}
return FFIRenderTarget(renderTarget, this);
}
@@ -488,7 +525,24 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
_swapChains[swapChain] = [];
}
_swapChains[swapChain]!.add(view);
await _updateRenderableSwapChains();
_swapChains[swapChain]!
.sort((a, b) => a.renderOrder.compareTo(b.renderOrder));
await updateRenderOrder();
}
///
///
///
@override
Future unregister(
covariant FFISwapChain swapChain, covariant FFIView view) async {
if (!_swapChains.containsKey(swapChain)) {
_swapChains[swapChain] = [];
}
_swapChains[swapChain]!.remove(view);
_swapChains[swapChain]!
.sort((a, b) => a.renderOrder.compareTo(b.renderOrder));
await updateRenderOrder();
}
final _hooks = <Future Function()>[];
@@ -603,38 +657,59 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
///
///
///
Future<Uint8List> capture(covariant FFIView view,
{bool captureRenderTarget = false}) async {
final viewport = await view.getViewport();
final swapChain = _swapChains.keys
.firstWhere((x) => _swapChains[x]?.contains(view) == true);
final out = Uint8List(viewport.width * viewport.height * 4);
Future<List<(View, Uint8List)>> capture(covariant FFISwapChain swapChain,
{covariant FFIView? view,
bool captureRenderTarget = false,
PixelDataFormat pixelDataFormat = PixelDataFormat.RGBA,
PixelDataType pixelDataType = PixelDataType.FLOAT,
Future Function(View)? beforeRender}) async {
await updateRenderOrder();
await withBoolCallback((cb) {
Renderer_beginFrameRenderThread(renderer, swapChain.swapChain, 0, cb);
});
await withVoidCallback((cb) {
Renderer_renderRenderThread(
renderer,
view.view,
cb,
);
});
if (captureRenderTarget && view.renderTarget == null) {
throw Exception();
final views = <FFIView>[];
if (view != null) {
views.add(view);
} else {
views.addAll(_swapChains[swapChain]!);
}
await withVoidCallback((cb) {
Renderer_readPixelsRenderThread(
renderer,
view.view,
captureRenderTarget ? view.renderTarget!.renderTarget : nullptr,
TPixelDataFormat.PIXELDATAFORMAT_RGBA,
TPixelDataType.PIXELDATATYPE_UBYTE,
out.address,
cb,
);
});
final pixelBuffers = <(View, Uint8List)>[];
for (final view in views) {
beforeRender?.call(view);
final viewport = await view.getViewport();
final pixelBuffer = Uint8List(viewport.width * viewport.height * 4 * sizeOf<Float>());
await withVoidCallback((cb) {
Renderer_renderRenderThread(
renderer,
view.view,
cb,
);
});
if (captureRenderTarget && view.renderTarget == null) {
throw Exception();
}
await withVoidCallback((cb) {
Renderer_readPixelsRenderThread(
renderer,
view.view,
view.renderTarget == null ? nullptr : view.renderTarget!.renderTarget,
// TPixelDataFormat.PIXELDATAFORMAT_RGBA,
// TPixelDataType.PIXELDATATYPE_UBYTE,
pixelDataFormat.value,
pixelDataType.value,
pixelBuffer.address,
pixelBuffer.length,
cb
);
});
pixelBuffers.add((view, pixelBuffer));
}
await withVoidCallback((cb) {
Renderer_endFrameRenderThread(renderer, cb);
});
@@ -642,7 +717,7 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
await withVoidCallback((cb) {
Engine_flushAndWaitRenderThead(engine, cb);
});
return out;
return pixelBuffers;
}
///
@@ -761,8 +836,15 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
(cb) => GltfResourceLoader_createRenderThread(engine, nullptr, cb));
final gizmo = await withPointerCallback<TGizmo>((cb) {
Gizmo_createRenderThread(engine, gltfAssetLoader, gltfResourceLoader, nameComponentManager,
view.view, _gizmoMaterial!.pointer, TGizmoType.values[gizmoType.index], cb);
Gizmo_createRenderThread(
engine,
gltfAssetLoader,
gltfResourceLoader,
nameComponentManager,
view.view,
_gizmoMaterial!.pointer,
TGizmoType.values[gizmoType.index],
cb);
});
if (gizmo == nullptr) {
throw Exception("Failed to create gizmo");
@@ -779,6 +861,48 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
entities: gizmoEntities.toSet()
..add(SceneAsset_getEntity(gizmo.cast<TSceneAsset>())));
}
///
///
///
@override
Future<ThermionAsset> createGeometry(Geometry geometry, Pointer animationManager,
{List<MaterialInstance>? materialInstances,
bool keepData = false,
bool addToScene = true}) async {
var assetPtr = await withPointerCallback<TSceneAsset>((callback) {
var ptrList = Int64List(materialInstances?.length ?? 0);
if (materialInstances != null && materialInstances.isNotEmpty) {
ptrList.setRange(
0,
materialInstances.length,
materialInstances
.cast<FFIMaterialInstance>()
.map((mi) => mi.pointer.address)
.toList());
}
return SceneAsset_createGeometryRenderThread(
engine,
geometry.vertices.address,
geometry.vertices.length,
geometry.normals.address,
geometry.normals.length,
geometry.uvs.address,
geometry.uvs.length,
geometry.indices.address,
geometry.indices.length,
geometry.primitiveType.index,
ptrList.address.cast<Pointer<TMaterialInstance>>(),
ptrList.length,
callback);
});
if (assetPtr == nullptr) {
throw Exception("Failed to create geometry");
}
return FFIAsset(assetPtr, this, animationManager.cast<TAnimationManager>());
}
}
class FinalizableUint8List implements Finalizable {

View File

@@ -9,6 +9,9 @@ import 'callbacks.dart';
import 'ffi_camera.dart';
class FFIView extends View {
int _renderOrder = 0;
int get renderOrder => _renderOrder;
final Pointer<TView> view;
final FFIFilamentApp app;
@@ -24,6 +27,17 @@ class FFIView extends View {
}
}
///
///
///
Future setRenderOrder(int order) async {
this._renderOrder = order;
await FilamentApp.instance!.updateRenderOrder();
}
///
///
///
Future setRenderable(bool renderable) async {
this._renderable = renderable;
}
@@ -138,6 +152,10 @@ class FFIView extends View {
View_setLayerEnabled(view, layer.value, visible);
}
Future setBlendMode(TBlendMode blendMode) async {
View_setBlendMode(view, blendMode);
}
@override
Future<Scene> getScene() async {
final ptr = View_getScene(view);

View File

@@ -659,6 +659,12 @@ external void View_setScene(
ffi.Pointer<TScene> tScene,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<TView>, ffi.Bool)>(isLeaf: true)
external void View_setFrontFaceWindingInverted(
ffi.Pointer<TView> tView,
bool inverted,
);
@ffi.Native<
ffi.Void Function(ffi.Pointer<TView>, ffi.Uint32, ffi.Uint32, ffi.Uint32,
PickCallback)>(isLeaf: true)
@@ -1471,7 +1477,8 @@ external void Renderer_renderStandaloneView(
ffi.Pointer<TRenderTarget>,
ffi.Int,
ffi.Int,
ffi.Pointer<ffi.Uint8>)>(isLeaf: true)
ffi.Pointer<ffi.Uint8>,
ffi.Size)>(isLeaf: true)
external void Renderer_readPixels(
ffi.Pointer<TRenderer> tRenderer,
ffi.Pointer<TView> tView,
@@ -1479,6 +1486,7 @@ external void Renderer_readPixels(
int tPixelBufferFormat,
int tPixelDataType,
ffi.Pointer<ffi.Uint8> out,
int outLength,
);
@ffi.Native<
@@ -1976,6 +1984,7 @@ external void Renderer_renderStandaloneViewRenderThread(
ffi.UnsignedInt,
ffi.UnsignedInt,
ffi.Pointer<ffi.Uint8>,
ffi.Size,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
external void Renderer_readPixelsRenderThread(
ffi.Pointer<TRenderer> tRenderer,
@@ -1984,6 +1993,7 @@ external void Renderer_readPixelsRenderThread(
int tPixelBufferFormat,
int tPixelDataType,
ffi.Pointer<ffi.Uint8> out,
int outLength,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
);

View File

@@ -79,20 +79,11 @@ class ThermionViewerFFI extends ThermionViewer {
Future _initialize() async {
_logger.info("Initializing ThermionViewerFFI");
view = FFIView(
await withPointerCallback<TView>(
(cb) => Engine_createViewRenderThread(app.engine, cb)),
app);
await view.setFrustumCullingEnabled(true);
View_setBlendMode(view.view, TBlendMode.TRANSLUCENT);
View_setShadowsEnabled(view.view, false);
View_setStencilBufferEnabled(view.view, false);
View_setAntiAliasing(view.view, false, false, false);
View_setDitheringEnabled(view.view, false);
View_setRenderQuality(view.view, TQualityLevel.MEDIUM);
view = await FilamentApp.instance!.createView() as FFIView;
await FilamentApp.instance!.setClearOptions(0.0, 0.0, 0.0, 0.0);
scene = FFIScene(Engine_createScene(app.engine));
scene = await FilamentApp.instance!.createScene() as FFIScene;
await view.setScene(scene);
final camera = FFICamera(
await withPointerCallback<TCamera>(
@@ -125,7 +116,7 @@ class ThermionViewerFFI extends ThermionViewer {
@override
Future setRendering(bool render) async {
_rendering = render;
await app.setRenderable(view, render);
await view.setRenderable(render);
}
///
@@ -684,38 +675,9 @@ class ThermionViewerFFI extends ThermionViewer {
{List<MaterialInstance>? materialInstances,
bool keepData = false,
bool addToScene = true}) async {
var assetPtr = await withPointerCallback<TSceneAsset>((callback) {
var ptrList = Int64List(materialInstances?.length ?? 0);
if (materialInstances != null && materialInstances.isNotEmpty) {
ptrList.setRange(
0,
materialInstances.length,
materialInstances
.cast<FFIMaterialInstance>()
.map((mi) => mi.pointer.address)
.toList());
}
final asset =
await FilamentApp.instance!.createGeometry(geometry, animationManager, materialInstances: materialInstances) as FFIAsset;
return SceneAsset_createGeometryRenderThread(
app.engine,
geometry.vertices.address,
geometry.vertices.length,
geometry.normals.address,
geometry.normals.length,
geometry.uvs.address,
geometry.uvs.length,
geometry.indices.address,
geometry.indices.length,
geometry.primitiveType.index,
ptrList.address.cast<Pointer<TMaterialInstance>>(),
ptrList.length,
callback);
});
if (assetPtr == nullptr) {
throw Exception("Failed to create geometry");
}
var asset = FFIAsset(assetPtr, app, animationManager);
if (addToScene) {
await scene.add(asset);
}
@@ -730,8 +692,8 @@ class ThermionViewerFFI extends ThermionViewer {
@override
Future<GizmoAsset> getGizmo(GizmoType gizmoType) async {
if (_gizmos[gizmoType] == null) {
_gizmos[gizmoType] =
await FilamentApp.instance!.createGizmo(view, animationManager, gizmoType);
_gizmos[gizmoType] = await FilamentApp.instance!
.createGizmo(view, animationManager, gizmoType);
}
return _gizmos[gizmoType]!;
}