add setFogOptions

This commit is contained in:
Nick Fisher
2025-05-24 15:05:03 +08:00
parent 4d6c008299
commit 81fb0fb583
5 changed files with 356 additions and 222 deletions

View File

@@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:thermion_dart/src/filament/src/implementation/ffi_texture.dart';
import 'package:thermion_dart/src/filament/src/interface/scene.dart'; import 'package:thermion_dart/src/filament/src/interface/scene.dart';
import 'package:thermion_dart/src/filament/src/implementation/ffi_filament_app.dart'; import 'package:thermion_dart/src/filament/src/implementation/ffi_filament_app.dart';
import 'package:thermion_dart/src/filament/src/implementation/ffi_render_target.dart'; import 'package:thermion_dart/src/filament/src/implementation/ffi_render_target.dart';
@@ -189,7 +190,8 @@ class FFIView extends View {
var viewport = await getViewport(); var viewport = await getViewport();
y = viewport.height - y; y = viewport.height - y;
if (FILAMENT_WASM) { if (FILAMENT_WASM) {
View_pickRenderThread(view, pickRequestId, x, y, _onPickResultHolder.pointer); View_pickRenderThread(
view, pickRequestId, x, y, _onPickResultHolder.pointer);
} else { } else {
View_pick(view, pickRequestId, x, y, _onPickResultHolder.pointer); View_pick(view, pickRequestId, x, y, _onPickResultHolder.pointer);
} }
@@ -218,4 +220,25 @@ class FFIView extends View {
fragZ: fragZ, fragZ: fragZ,
)); ));
} }
@override
Future setFogOptions(FogOptions options) async {
final tFogOptions = Struct.create<TFogOptions>();
tFogOptions.cutOffDistance = options.cutOffDistance;
tFogOptions.enabled = options.enabled;
tFogOptions.density = options.density;
tFogOptions.distance = options.distance;
tFogOptions.fogColorFromIbl = options.fogColorFromIbl;
tFogOptions.height = options.height;
tFogOptions.heightFalloff = options.heightFalloff;
tFogOptions.inScatteringSize = options.inScatteringSize;
tFogOptions.inScatteringStart = options.inScatteringStart;
tFogOptions.maximumOpacity = options.maximumOpacity;
tFogOptions.skyColor =
(options.skyColor as FFITexture?)?.pointer ?? nullptr;
tFogOptions.linearColor.x = options.linearColor.r;
tFogOptions.linearColor.y = options.linearColor.x;
tFogOptions.linearColor.z = options.linearColor.x;
View_setFogOptions(this.view, tFogOptions.address);
}
} }

View File

@@ -2,10 +2,39 @@ import 'package:thermion_dart/src/filament/src/interface/layers.dart';
import 'package:thermion_dart/src/filament/src/interface/scene.dart'; import 'package:thermion_dart/src/filament/src/interface/scene.dart';
import 'package:thermion_dart/thermion_dart.dart'; import 'package:thermion_dart/thermion_dart.dart';
enum BlendMode { class FogOptions {
opaque, final double distance;
transparent final double cutOffDistance;
final double maximumOpacity;
final double height;
final double heightFalloff;
late final Vector3 linearColor;
final double density;
final double inScatteringStart;
final double inScatteringSize;
final bool fogColorFromIbl;
final Texture? skyColor;
final bool enabled;
FogOptions(
{this.enabled = false,
this.distance = 0.0,
this.cutOffDistance = double.infinity,
this.maximumOpacity = 1.0,
this.height = 0,
this.heightFalloff = 1,
Vector3? linearColor = null,
this.density = 0.1,
this.inScatteringStart = 0,
this.inScatteringSize = -1,
this.fogColorFromIbl = false,
this.skyColor = null}) {
this.linearColor = linearColor ?? Vector3(1, 1, 1);
}
} }
enum BlendMode { opaque, transparent }
/// ///
/// The viewport currently attached to a [View]. /// The viewport currently attached to a [View].
/// ///
@@ -23,6 +52,10 @@ class Viewport {
enum QualityLevel { LOW, MEDIUM, HIGH, ULTRA } enum QualityLevel { LOW, MEDIUM, HIGH, ULTRA }
abstract class View { abstract class View {
/// Gets the scene currently associated with this View.
///
///
Future<Scene> getScene(); Future<Scene> getScene();
Future<Viewport> getViewport(); Future<Viewport> getViewport();
Future setViewport(int width, int height); Future setViewport(int width, int height);
@@ -43,8 +76,13 @@ abstract class View {
Future setBloom(bool enabled, double strength); Future setBloom(bool enabled, double strength);
Future setBlendMode(BlendMode blendMode); Future setBlendMode(BlendMode blendMode);
Future setRenderQuality(QualityLevel quality); Future setRenderQuality(QualityLevel quality);
Future setLayerVisibility(VisibilityLayers layer, bool visible); Future setLayerVisibility(VisibilityLayers layer, bool visible);
/// Sets the fog options for this view.
/// Fog is disabled by default
///
Future setFogOptions(FogOptions options);
/// ///
/// Call [pick] to hit-test renderable entities at given viewport coordinates /// Call [pick] to hit-test renderable entities at given viewport coordinates
@@ -56,11 +94,8 @@ abstract class View {
/// ///
Future pick(int x, int y, void Function(PickResult) resultHandler); Future pick(int x, int y, void Function(PickResult) resultHandler);
/// ///
/// ///
/// ///
Future dispose(); Future dispose();
} }

View File

@@ -17,6 +17,24 @@ struct TViewport {
}; };
typedef struct TViewport TViewport; typedef struct TViewport TViewport;
/**
* Copied from FogOptions in View.h
*/
struct TFogOptions {
float distance = 0.0f;
float cutOffDistance = INFINITY;
float maximumOpacity = 1.0f;
float height = 0.0f;
float heightFalloff = 1.0f;
double3 linearColor = { 1.0f, 1.0f, 1.0f };
float density = 0.1f;
float inScatteringStart = 0.0f;
float inScatteringSize = -1.0f;
bool fogColorFromIbl = false;
TTexture* skyColor = nullptr;
bool enabled = false;
};
enum TToneMapping enum TToneMapping
{ {
ACES, ACES,
@@ -68,6 +86,7 @@ EMSCRIPTEN_KEEPALIVE void View_setDitheringEnabled(TView *tView, bool enabled);
EMSCRIPTEN_KEEPALIVE bool View_isDitheringEnabled(TView *tView); EMSCRIPTEN_KEEPALIVE bool View_isDitheringEnabled(TView *tView);
EMSCRIPTEN_KEEPALIVE void View_setScene(TView *tView, TScene *tScene); EMSCRIPTEN_KEEPALIVE void View_setScene(TView *tView, TScene *tScene);
EMSCRIPTEN_KEEPALIVE void View_setFrontFaceWindingInverted(TView *tView, bool inverted); EMSCRIPTEN_KEEPALIVE void View_setFrontFaceWindingInverted(TView *tView, bool inverted);
EMSCRIPTEN_KEEPALIVE void View_setFogOptions(TView *tView, TFogOptions *tFogOptions);
typedef void (*PickCallback)(uint32_t requestId, EntityId entityId, float depth, float fragX, float fragY, float fragZ); typedef void (*PickCallback)(uint32_t requestId, EntityId entityId, float depth, float fragX, float fragY, float fragZ);
EMSCRIPTEN_KEEPALIVE void View_pick(TView* tView, uint32_t requestId, uint32_t x, uint32_t y, PickCallback callback); EMSCRIPTEN_KEEPALIVE void View_pick(TView* tView, uint32_t requestId, uint32_t x, uint32_t y, PickCallback callback);

View File

@@ -13,14 +13,16 @@
#include "Log.hpp" #include "Log.hpp"
#ifdef __cplusplus #ifdef __cplusplus
namespace thermion { namespace thermion
extern "C"
{ {
using namespace filament; extern "C"
{
using namespace filament;
#endif #endif
EMSCRIPTEN_KEEPALIVE void View_setBlendMode(TView *tView, TBlendMode tBlendMode) { EMSCRIPTEN_KEEPALIVE void View_setBlendMode(TView *tView, TBlendMode tBlendMode)
{
auto view = reinterpret_cast<View *>(tView); auto view = reinterpret_cast<View *>(tView);
view->setBlendMode(static_cast<filament::View::BlendMode>(tBlendMode)); view->setBlendMode(static_cast<filament::View::BlendMode>(tBlendMode));
} }
@@ -28,7 +30,7 @@ using namespace filament;
EMSCRIPTEN_KEEPALIVE TViewport View_getViewport(TView *tView) EMSCRIPTEN_KEEPALIVE TViewport View_getViewport(TView *tView)
{ {
auto view = reinterpret_cast<View *>(tView); auto view = reinterpret_cast<View *>(tView);
auto & vp = view->getViewport(); auto &vp = view->getViewport();
TViewport tvp; TViewport tvp;
tvp.left = vp.left; tvp.left = vp.left;
tvp.bottom = vp.bottom; tvp.bottom = vp.bottom;
@@ -44,7 +46,8 @@ using namespace filament;
TRACE("Set viewport to %dx%d", width, height); TRACE("Set viewport to %dx%d", width, height);
} }
EMSCRIPTEN_KEEPALIVE TRenderTarget *View_getRenderTarget(TView *tView) { EMSCRIPTEN_KEEPALIVE TRenderTarget *View_getRenderTarget(TView *tView)
{
auto view = reinterpret_cast<View *>(tView); auto view = reinterpret_cast<View *>(tView);
auto tRenderTarget = reinterpret_cast<TRenderTarget *>(view->getRenderTarget()); auto tRenderTarget = reinterpret_cast<TRenderTarget *>(view->getRenderTarget());
return tRenderTarget; return tRenderTarget;
@@ -68,7 +71,6 @@ using namespace filament;
auto view = reinterpret_cast<View *>(tView); auto view = reinterpret_cast<View *>(tView);
view->setPostProcessingEnabled(enabled); view->setPostProcessingEnabled(enabled);
TRACE("Set postprocessing enabled : %d", enabled); TRACE("Set postprocessing enabled : %d", enabled);
} }
EMSCRIPTEN_KEEPALIVE void View_setShadowsEnabled(TView *tView, bool enabled) EMSCRIPTEN_KEEPALIVE void View_setShadowsEnabled(TView *tView, bool enabled)
@@ -104,13 +106,15 @@ using namespace filament;
#endif #endif
} }
EMSCRIPTEN_KEEPALIVE void View_setColorGrading(TView *tView, TColorGrading *tColorGrading) { EMSCRIPTEN_KEEPALIVE void View_setColorGrading(TView *tView, TColorGrading *tColorGrading)
auto *view = reinterpret_cast<View*>(tView); {
auto *view = reinterpret_cast<View *>(tView);
auto *colorGrading = reinterpret_cast<ColorGrading *>(tColorGrading); auto *colorGrading = reinterpret_cast<ColorGrading *>(tColorGrading);
view->setColorGrading(colorGrading); view->setColorGrading(colorGrading);
} }
EMSCRIPTEN_KEEPALIVE TColorGrading *ColorGrading_create(TEngine* tEngine, TToneMapping tToneMapping) { EMSCRIPTEN_KEEPALIVE TColorGrading *ColorGrading_create(TEngine *tEngine, TToneMapping tToneMapping)
{
auto engine = reinterpret_cast<Engine *>(tEngine); auto engine = reinterpret_cast<Engine *>(tEngine);
ToneMapper *tm; ToneMapper *tm;
@@ -153,65 +157,76 @@ using namespace filament;
view->setAntiAliasing(fxaa ? AntiAliasing::FXAA : AntiAliasing::NONE); view->setAntiAliasing(fxaa ? AntiAliasing::FXAA : AntiAliasing::NONE);
} }
EMSCRIPTEN_KEEPALIVE void View_setLayerEnabled(TView* tView, int layer, bool enabled) { EMSCRIPTEN_KEEPALIVE void View_setLayerEnabled(TView *tView, int layer, bool enabled)
{
auto view = reinterpret_cast<View *>(tView); auto view = reinterpret_cast<View *>(tView);
view->setLayerEnabled(layer, enabled); view->setLayerEnabled(layer, enabled);
} }
EMSCRIPTEN_KEEPALIVE void View_setCamera(TView *tView, TCamera *tCamera) { EMSCRIPTEN_KEEPALIVE void View_setCamera(TView *tView, TCamera *tCamera)
{
auto view = reinterpret_cast<View *>(tView); auto view = reinterpret_cast<View *>(tView);
auto *camera = reinterpret_cast<Camera *>(tCamera); auto *camera = reinterpret_cast<Camera *>(tCamera);
view->setCamera(camera); view->setCamera(camera);
} }
EMSCRIPTEN_KEEPALIVE TScene* View_getScene(TView* tView) { EMSCRIPTEN_KEEPALIVE TScene *View_getScene(TView *tView)
{
auto view = reinterpret_cast<View *>(tView); auto view = reinterpret_cast<View *>(tView);
return reinterpret_cast<TScene*>(view->getScene()); return reinterpret_cast<TScene *>(view->getScene());
} }
EMSCRIPTEN_KEEPALIVE TCamera* View_getCamera(TView *tView) { EMSCRIPTEN_KEEPALIVE TCamera *View_getCamera(TView *tView)
{
auto view = reinterpret_cast<View *>(tView); auto view = reinterpret_cast<View *>(tView);
return reinterpret_cast<TCamera*>(&(view->getCamera())); return reinterpret_cast<TCamera *>(&(view->getCamera()));
} }
EMSCRIPTEN_KEEPALIVE void View_setStencilBufferEnabled(TView *tView, bool enabled) { EMSCRIPTEN_KEEPALIVE void View_setStencilBufferEnabled(TView *tView, bool enabled)
{
auto view = reinterpret_cast<View *>(tView); auto view = reinterpret_cast<View *>(tView);
view->setStencilBufferEnabled(enabled); view->setStencilBufferEnabled(enabled);
} }
EMSCRIPTEN_KEEPALIVE bool View_isStencilBufferEnabled(TView *tView) { EMSCRIPTEN_KEEPALIVE bool View_isStencilBufferEnabled(TView *tView)
{
auto view = reinterpret_cast<View *>(tView); auto view = reinterpret_cast<View *>(tView);
return view->isStencilBufferEnabled(); return view->isStencilBufferEnabled();
} }
EMSCRIPTEN_KEEPALIVE void View_pick(TView *tView, uint32_t requestId, uint32_t x, uint32_t y, PickCallback callback) EMSCRIPTEN_KEEPALIVE void View_pick(TView *tView, uint32_t requestId, uint32_t x, uint32_t y, PickCallback callback)
{ {
auto *view = reinterpret_cast<View *>(tView); auto *view = reinterpret_cast<View *>(tView);
view->pick(x, y, [=](filament::View::PickingQueryResult const &result) { view->pick(x, y, [=](filament::View::PickingQueryResult const &result)
callback(requestId, utils::Entity::smuggle(result.renderable), result.depth, result.fragCoords.x, result.fragCoords.y, result.fragCoords.z); { callback(requestId, utils::Entity::smuggle(result.renderable), result.depth, result.fragCoords.x, result.fragCoords.y, result.fragCoords.z); });
});
} }
EMSCRIPTEN_KEEPALIVE void View_setDitheringEnabled(TView *tView, bool enabled) { EMSCRIPTEN_KEEPALIVE void View_setDitheringEnabled(TView *tView, bool enabled)
{
auto *view = reinterpret_cast<View *>(tView); auto *view = reinterpret_cast<View *>(tView);
if(enabled) { if (enabled)
{
view->setDithering(Dithering::TEMPORAL); view->setDithering(Dithering::TEMPORAL);
} else { }
else
{
view->setDithering(Dithering::NONE); view->setDithering(Dithering::NONE);
} }
} }
EMSCRIPTEN_KEEPALIVE bool View_isDitheringEnabled(TView *tView) { EMSCRIPTEN_KEEPALIVE bool View_isDitheringEnabled(TView *tView)
{
auto *view = reinterpret_cast<View *>(tView); auto *view = reinterpret_cast<View *>(tView);
return view->getDithering() == Dithering::TEMPORAL; return view->getDithering() == Dithering::TEMPORAL;
} }
EMSCRIPTEN_KEEPALIVE void View_setRenderQuality(TView *tView, TQualityLevel qualityLevel) { EMSCRIPTEN_KEEPALIVE void View_setRenderQuality(TView *tView, TQualityLevel qualityLevel)
{
auto view = reinterpret_cast<View *>(tView); auto view = reinterpret_cast<View *>(tView);
RenderQuality rq; RenderQuality rq;
rq.hdrColorBuffer = (filament::QualityLevel)qualityLevel; rq.hdrColorBuffer = (filament::QualityLevel)qualityLevel;
switch(rq.hdrColorBuffer) { switch (rq.hdrColorBuffer)
{
case filament::QualityLevel::LOW: case filament::QualityLevel::LOW:
TRACE("Render Quality: LOW"); TRACE("Render Quality: LOW");
break; break;
@@ -229,19 +244,43 @@ using namespace filament;
view->setRenderQuality(rq); view->setRenderQuality(rq);
} }
EMSCRIPTEN_KEEPALIVE void View_setScene(TView *tView, TScene *tScene) { EMSCRIPTEN_KEEPALIVE void View_setScene(TView *tView, TScene *tScene)
auto *view = reinterpret_cast<View*>(tView); {
auto *scene = reinterpret_cast<Scene*>(tScene); auto *view = reinterpret_cast<View *>(tView);
auto *scene = reinterpret_cast<Scene *>(tScene);
view->setScene(scene); view->setScene(scene);
} }
EMSCRIPTEN_KEEPALIVE void View_setFrontFaceWindingInverted(TView *tView, bool inverted) { EMSCRIPTEN_KEEPALIVE void View_setFrontFaceWindingInverted(TView *tView, bool inverted)
auto *view = reinterpret_cast<View*>(tView); {
auto *view = reinterpret_cast<View *>(tView);
view->setFrontFaceWindingInverted(inverted); view->setFrontFaceWindingInverted(inverted);
} }
EMSCRIPTEN_KEEPALIVE void View_setFogOptions(TView *tView, TFogOptions *tFogOptions)
{
auto view = reinterpret_cast<View *>(tView);
FogOptions fogOptions {
.distance = tFogOptions->distance,
.cutOffDistance = tFogOptions->cutOffDistance,
.maximumOpacity = tFogOptions->maximumOpacity,
.height = tFogOptions->height,
.heightFalloff = tFogOptions->heightFalloff,
.color = LinearColor(tFogOptions->linearColor.x, tFogOptions->linearColor.y, tFogOptions->linearColor.z),
.density = tFogOptions->density,
.inScatteringStart = tFogOptions->inScatteringStart,
.inScatteringSize = tFogOptions->inScatteringSize,
.fogColorFromIbl = tFogOptions->fogColorFromIbl,
.skyColor = reinterpret_cast<Texture *>(tFogOptions->skyColor),
.enabled = tFogOptions->enabled
};
TRACE("Setting fog enabled to %d (tFogOptions->cutOffDistance %f)", fogOptions.enabled, tFogOptions->cutOffDistance);
view->setFogOptions(fogOptions);
}
#ifdef __cplusplus #ifdef __cplusplus
} }
} }
#endif #endif

View File

@@ -179,7 +179,7 @@ void main() async {
type: LightType.SUN, type: LightType.SUN,
color: 6500, color: 6500,
intensity: 100000000, intensity: 100000000,
direction: Vector3(0,0,-1), direction: Vector3(0, 0, -1),
position: Vector3.zero())); position: Vector3.zero()));
await scene.addEntity(light); await scene.addEntity(light);
@@ -188,7 +188,7 @@ void main() async {
await materialInstance2.setParameterTexture("baseColorMap", texture, await materialInstance2.setParameterTexture("baseColorMap", texture,
await FilamentApp.instance!.createTextureSampler()); await FilamentApp.instance!.createTextureSampler());
await materialInstance2.setParameterInt("baseColorIndex",0); await materialInstance2.setParameterInt("baseColorIndex", 0);
await materialInstance2.setParameterFloat4("baseColorFactor", 1, 1, 1, 1); await materialInstance2.setParameterFloat4("baseColorFactor", 1, 1, 1, 1);
await cube.setMaterialInstanceAt(materialInstance2 as FFIMaterialInstance); await cube.setMaterialInstanceAt(materialInstance2 as FFIMaterialInstance);
@@ -259,7 +259,7 @@ void main() async {
type: LightType.SUN, type: LightType.SUN,
color: 6500, color: 6500,
intensity: 100000000, intensity: 100000000,
direction: Vector3(0,0,-1), direction: Vector3(0, 0, -1),
position: Vector3.zero())); position: Vector3.zero()));
await scene.addEntity(light); await scene.addEntity(light);
@@ -268,7 +268,7 @@ void main() async {
await materialInstance2.setParameterTexture("baseColorMap", texture, await materialInstance2.setParameterTexture("baseColorMap", texture,
await FilamentApp.instance!.createTextureSampler()); await FilamentApp.instance!.createTextureSampler());
await materialInstance2.setParameterInt("baseColorIndex",0); await materialInstance2.setParameterInt("baseColorIndex", 0);
await materialInstance2.setParameterFloat4("baseColorFactor", 1, 1, 1, 1); await materialInstance2.setParameterFloat4("baseColorFactor", 1, 1, 1, 1);
await cube.setMaterialInstanceAt(materialInstance2 as FFIMaterialInstance); await cube.setMaterialInstanceAt(materialInstance2 as FFIMaterialInstance);
@@ -281,6 +281,24 @@ void main() async {
p.join(testHelper.outDir.path, "render_target_as_texture.bmp"), p.join(testHelper.outDir.path, "render_target_as_texture.bmp"),
isFloat: true); isFloat: true);
}); });
test('fog tests', () async {
await testHelper.withViewer((viewer) async {
var cube = await FilamentApp.instance!
.createGeometry(GeometryHelper.cube(flipUvs: true), nullptr);
await viewer.addToScene(cube);
final camera = await viewer.getActiveCamera();
await camera.lookAt(Vector3(1, 3, 5), focus: Vector3.zero());
await testHelper.capture(viewer.view, "fog_options_disabled");
await viewer.view.setFogOptions(FogOptions(
enabled: true, distance: 0, density: 0.5));
await testHelper.capture(viewer.view, "fog_options_enabled");
}, addSkybox: true, postProcessing: true);
});
} }
// test('one swapchain, render view to render target', () async { // test('one swapchain, render view to render target', () async {