feat: expose setCastShadows/setReceiveShadows

This commit is contained in:
Nick Fisher
2025-03-05 14:47:55 +08:00
parent cca9fba79f
commit 593bca3365
7 changed files with 206 additions and 39 deletions

View File

@@ -2502,7 +2502,6 @@ external bool RenderableManager_isRenderable(
int entityId, int entityId,
); );
/// Checks if the given entity has a renderable component
@ffi.Native<ffi.Bool Function(ffi.Pointer<TRenderableManager>, EntityId)>( @ffi.Native<ffi.Bool Function(ffi.Pointer<TRenderableManager>, EntityId)>(
isLeaf: true) isLeaf: true)
external bool RenderableManager_hasComponent( external bool RenderableManager_hasComponent(
@@ -2510,13 +2509,11 @@ external bool RenderableManager_hasComponent(
int entityId, int entityId,
); );
/// Returns true if this manager has no components
@ffi.Native<ffi.Bool Function(ffi.Pointer<TRenderableManager>)>(isLeaf: true) @ffi.Native<ffi.Bool Function(ffi.Pointer<TRenderableManager>)>(isLeaf: true)
external bool RenderableManager_empty( external bool RenderableManager_empty(
ffi.Pointer<TRenderableManager> tRenderableManager, ffi.Pointer<TRenderableManager> tRenderableManager,
); );
/// Returns whether a light channel is enabled on a specified renderable
@ffi.Native< @ffi.Native<
ffi.Bool Function(ffi.Pointer<TRenderableManager>, EntityId, ffi.Bool Function(ffi.Pointer<TRenderableManager>, EntityId,
ffi.UnsignedInt)>(isLeaf: true) ffi.UnsignedInt)>(isLeaf: true)
@@ -2526,7 +2523,6 @@ external bool RenderableManager_getLightChannel(
int channel, int channel,
); );
/// Checks if the renderable can cast shadows
@ffi.Native<ffi.Bool Function(ffi.Pointer<TRenderableManager>, EntityId)>( @ffi.Native<ffi.Bool Function(ffi.Pointer<TRenderableManager>, EntityId)>(
isLeaf: true) isLeaf: true)
external bool RenderableManager_isShadowCaster( external bool RenderableManager_isShadowCaster(
@@ -2534,7 +2530,24 @@ external bool RenderableManager_isShadowCaster(
int entityId, int entityId,
); );
/// Checks if the renderable can receive shadows @ffi.Native<
ffi.Void Function(
ffi.Pointer<TRenderableManager>, EntityId, ffi.Bool)>(isLeaf: true)
external void RenderableManager_setCastShadows(
ffi.Pointer<TRenderableManager> tRenderableManager,
int entityId,
bool castShadows,
);
@ffi.Native<
ffi.Void Function(
ffi.Pointer<TRenderableManager>, EntityId, ffi.Bool)>(isLeaf: true)
external void RenderableManager_setReceiveShadows(
ffi.Pointer<TRenderableManager> tRenderableManager,
int entityId,
bool receiveShadows,
);
@ffi.Native<ffi.Bool Function(ffi.Pointer<TRenderableManager>, EntityId)>( @ffi.Native<ffi.Bool Function(ffi.Pointer<TRenderableManager>, EntityId)>(
isLeaf: true) isLeaf: true)
external bool RenderableManager_isShadowReceiver( external bool RenderableManager_isShadowReceiver(
@@ -2542,7 +2555,6 @@ external bool RenderableManager_isShadowReceiver(
int entityId, int entityId,
); );
/// Returns whether large-scale fog is enabled for this renderable
@ffi.Native<ffi.Bool Function(ffi.Pointer<TRenderableManager>, EntityId)>( @ffi.Native<ffi.Bool Function(ffi.Pointer<TRenderableManager>, EntityId)>(
isLeaf: true) isLeaf: true)
external bool RenderableManager_getFogEnabled( external bool RenderableManager_getFogEnabled(

View File

@@ -111,7 +111,7 @@ class ThermionViewerFFI extends ThermionViewer {
if (view == nullptr) { if (view == nullptr) {
throw Exception("Failed to create view"); throw Exception("Failed to create view");
} }
return FFIView(view, _viewer!,_engine!); return FFIView(view, _viewer!, _engine!);
} }
/// ///
@@ -1678,9 +1678,6 @@ class ThermionViewerFFI extends ThermionViewer {
throw Exception("Failed to create geometry"); throw Exception("Failed to create geometry");
} }
print(
" is shadow caster : ${RenderableManager_isShadowCaster(_renderableManager!, SceneAsset_getEntity(assetPtr))} is shadow recevier : ${RenderableManager_isShadowReceiver(_renderableManager!, SceneAsset_getEntity(assetPtr))} ");
var asset = FFIAsset( var asset = FFIAsset(
assetPtr, _sceneManager!, _engine!, _unlitMaterialProvider!, this); assetPtr, _sceneManager!, _engine!, _unlitMaterialProvider!, this);
@@ -2185,10 +2182,36 @@ class ThermionViewerFFI extends ThermionViewer {
gizmoEntities.toSet() gizmoEntities.toSet()
..add(SceneAsset_getEntity(gizmo.cast<TSceneAsset>()))); ..add(SceneAsset_getEntity(gizmo.cast<TSceneAsset>())));
} }
///
///
///
Future setCastShadows(ThermionEntity entity, bool castShadows) async {
RenderableManager_setCastShadows(_renderableManager!, entity, castShadows);
}
///
///
///
Future<bool> isCastShadowsEnabled(ThermionEntity entity) async {
return RenderableManager_isShadowCaster(_renderableManager!, entity);
}
///
///
///
Future setReceiveShadows(ThermionEntity entity, bool receiveShadows) async {
RenderableManager_setReceiveShadows(_renderableManager!, entity, receiveShadows);
}
///
///
///
Future<bool> isReceiveShadowsEnabled(ThermionEntity entity) async {
return RenderableManager_isShadowReceiver(_renderableManager!, entity);
}
} }
class FFISwapChain extends SwapChain { class FFISwapChain extends SwapChain {
final Pointer<TSwapChain> swapChain; final Pointer<TSwapChain> swapChain;
final Pointer<TViewer> viewer; final Pointer<TViewer> viewer;

View File

@@ -918,4 +918,24 @@ abstract class ThermionViewer {
/// Throws an exception if the index is out-of-bounds. /// Throws an exception if the index is out-of-bounds.
/// ///
Camera getCameraAt(int index); Camera getCameraAt(int index);
///
///
///
Future setCastShadows(ThermionEntity entity, bool castShadows);
///
///
///
Future<bool> isCastShadowsEnabled(ThermionEntity entity);
///
///
///
Future setReceiveShadows(ThermionEntity entity, bool receiveShadows);
///
///
///
Future<bool> isReceiveShadowsEnabled(ThermionEntity entity);
} }

View File

@@ -12,37 +12,13 @@ extern "C"
EMSCRIPTEN_KEEPALIVE void RenderableManager_setPriority(TRenderableManager *tRenderableManager, EntityId entityId, int priority); EMSCRIPTEN_KEEPALIVE void RenderableManager_setPriority(TRenderableManager *tRenderableManager, EntityId entityId, int priority);
EMSCRIPTEN_KEEPALIVE TMaterialInstance *RenderableManager_getMaterialInstanceAt(TRenderableManager *tRenderableManager, EntityId entityId, int primitiveIndex); EMSCRIPTEN_KEEPALIVE TMaterialInstance *RenderableManager_getMaterialInstanceAt(TRenderableManager *tRenderableManager, EntityId entityId, int primitiveIndex);
EMSCRIPTEN_KEEPALIVE bool RenderableManager_isRenderable(TRenderableManager *tRenderableManager, EntityId entityId); EMSCRIPTEN_KEEPALIVE bool RenderableManager_isRenderable(TRenderableManager *tRenderableManager, EntityId entityId);
// New boolean methods
/**
* Checks if the given entity has a renderable component
*/
EMSCRIPTEN_KEEPALIVE bool RenderableManager_hasComponent(TRenderableManager *tRenderableManager, EntityId entityId); EMSCRIPTEN_KEEPALIVE bool RenderableManager_hasComponent(TRenderableManager *tRenderableManager, EntityId entityId);
/**
* Returns true if this manager has no components
*/
EMSCRIPTEN_KEEPALIVE bool RenderableManager_empty(TRenderableManager *tRenderableManager); EMSCRIPTEN_KEEPALIVE bool RenderableManager_empty(TRenderableManager *tRenderableManager);
/**
* Returns whether a light channel is enabled on a specified renderable
*/
EMSCRIPTEN_KEEPALIVE bool RenderableManager_getLightChannel(TRenderableManager *tRenderableManager, EntityId entityId, unsigned int channel); EMSCRIPTEN_KEEPALIVE bool RenderableManager_getLightChannel(TRenderableManager *tRenderableManager, EntityId entityId, unsigned int channel);
/**
* Checks if the renderable can cast shadows
*/
EMSCRIPTEN_KEEPALIVE bool RenderableManager_isShadowCaster(TRenderableManager *tRenderableManager, EntityId entityId); EMSCRIPTEN_KEEPALIVE bool RenderableManager_isShadowCaster(TRenderableManager *tRenderableManager, EntityId entityId);
EMSCRIPTEN_KEEPALIVE void RenderableManager_setCastShadows(TRenderableManager *tRenderableManager, EntityId entityId, bool castShadows);
/** EMSCRIPTEN_KEEPALIVE void RenderableManager_setReceiveShadows(TRenderableManager *tRenderableManager, EntityId entityId, bool receiveShadows);
* Checks if the renderable can receive shadows
*/
EMSCRIPTEN_KEEPALIVE bool RenderableManager_isShadowReceiver(TRenderableManager *tRenderableManager, EntityId entityId); EMSCRIPTEN_KEEPALIVE bool RenderableManager_isShadowReceiver(TRenderableManager *tRenderableManager, EntityId entityId);
/**
* Returns whether large-scale fog is enabled for this renderable
*/
EMSCRIPTEN_KEEPALIVE bool RenderableManager_getFogEnabled(TRenderableManager *tRenderableManager, EntityId entityId); EMSCRIPTEN_KEEPALIVE bool RenderableManager_getFogEnabled(TRenderableManager *tRenderableManager, EntityId entityId);
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -72,21 +72,45 @@ namespace thermion
const auto &entity = utils::Entity::import(entityId); const auto &entity = utils::Entity::import(entityId);
auto renderableInstance = renderableManager->getInstance(entity); auto renderableInstance = renderableManager->getInstance(entity);
if (!renderableInstance.isValid()) { if (!renderableInstance.isValid()) {
Log("Error: invalid renderable");
return false; return false;
} }
return renderableManager->isShadowCaster(renderableInstance); return renderableManager->isShadowCaster(renderableInstance);
} }
EMSCRIPTEN_KEEPALIVE void RenderableManager_setCastShadows(TRenderableManager *tRenderableManager, EntityId entityId, bool enabled) {
auto *renderableManager = reinterpret_cast<filament::RenderableManager *>(tRenderableManager);
const auto &entity = utils::Entity::import(entityId);
auto renderableInstance = renderableManager->getInstance(entity);
if (!renderableInstance.isValid()) {
Log("Error: invalid renderable");
return;
}
return renderableManager->setCastShadows(renderableInstance, enabled);
}
EMSCRIPTEN_KEEPALIVE bool RenderableManager_isShadowReceiver(TRenderableManager *tRenderableManager, EntityId entityId) { EMSCRIPTEN_KEEPALIVE bool RenderableManager_isShadowReceiver(TRenderableManager *tRenderableManager, EntityId entityId) {
auto *renderableManager = reinterpret_cast<filament::RenderableManager *>(tRenderableManager); auto *renderableManager = reinterpret_cast<filament::RenderableManager *>(tRenderableManager);
const auto &entity = utils::Entity::import(entityId); const auto &entity = utils::Entity::import(entityId);
auto renderableInstance = renderableManager->getInstance(entity); auto renderableInstance = renderableManager->getInstance(entity);
if (!renderableInstance.isValid()) { if (!renderableInstance.isValid()) {
Log("Error: invalid renderable");
return false; return false;
} }
return renderableManager->isShadowReceiver(renderableInstance); return renderableManager->isShadowReceiver(renderableInstance);
} }
EMSCRIPTEN_KEEPALIVE void RenderableManager_setReceiveShadows(TRenderableManager *tRenderableManager, EntityId entityId, bool enabled) {
auto *renderableManager = reinterpret_cast<filament::RenderableManager *>(tRenderableManager);
const auto &entity = utils::Entity::import(entityId);
auto renderableInstance = renderableManager->getInstance(entity);
if (!renderableInstance.isValid()) {
Log("Error: invalid renderable");
return;
}
return renderableManager->setReceiveShadows(renderableInstance, enabled);
}
EMSCRIPTEN_KEEPALIVE bool RenderableManager_getFogEnabled(TRenderableManager *tRenderableManager, EntityId entityId) { EMSCRIPTEN_KEEPALIVE bool RenderableManager_getFogEnabled(TRenderableManager *tRenderableManager, EntityId entityId) {
auto *renderableManager = reinterpret_cast<filament::RenderableManager *>(tRenderableManager); auto *renderableManager = reinterpret_cast<filament::RenderableManager *>(tRenderableManager);
const auto &entity = utils::Entity::import(entityId); const auto &entity = utils::Entity::import(entityId);

View File

@@ -387,7 +387,7 @@ void main() async {
await viewer.loadIbl( await viewer.loadIbl(
"file://${testHelper.testDir}/assets/default_env_ibl.ktx", "file://${testHelper.testDir}/assets/default_env_ibl.ktx",
intensity: 1000); intensity: 1000);
// await viewer.setShadowsEnabled(true);
await viewer.addDirectLight(DirectLight.sun( await viewer.addDirectLight(DirectLight.sun(
intensity: 500000, intensity: 500000,
castShadows: true, castShadows: true,

View File

@@ -0,0 +1,112 @@
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("material");
group("shadow tests", () {
test('enable/disable shadows', () async {
await testHelper.withViewer((viewer) async {
await viewer.setPostProcessing(true);
await viewer.setShadowsEnabled(true);
await viewer.setShadowType(ShadowType.PCF);
var materialInsance = await viewer.createUbershaderMaterialInstance();
await materialInsance.setParameterFloat4(
"baseColorFactor", 0.0, 1.0, 0.0, 1.0);
await viewer.addDirectLight(DirectLight.sun(
intensity: 50000,
castShadows: true,
direction: Vector3(1, -0.5, 0).normalized()));
final plane = await viewer.createGeometry(
GeometryHelper.plane(
normals: true, uvs: true, width: 10, height: 10),
materialInstances: [materialInsance]);
expect(await viewer.isCastShadowsEnabled(plane.entity), true);
expect(await viewer.isReceiveShadowsEnabled(plane.entity), true);
await viewer.createGeometry(
GeometryHelper.cube(
normals: true,
uvs: true,
),
materialInstances: [materialInsance]);
await testHelper.capture(viewer, "shadows_enabled");
await viewer.setShadowsEnabled(false);
await testHelper.capture(viewer, "shadows_disabled");
}, bg: kRed);
});
test('enable/disable cast shadows', () async {
await testHelper.withViewer((viewer) async {
await viewer.setPostProcessing(true);
await viewer.setShadowsEnabled(true);
await viewer.setShadowType(ShadowType.PCF);
var materialInsance = await viewer.createUbershaderMaterialInstance();
await materialInsance.setParameterFloat4(
"baseColorFactor", 0.0, 1.0, 0.0, 1.0);
await viewer.addDirectLight(DirectLight.sun(
intensity: 50000,
castShadows: true,
direction: Vector3(1, -0.5, 0).normalized()));
final plane = await viewer.createGeometry(
GeometryHelper.plane(
normals: true, uvs: true, width: 10, height: 10),
materialInstances: [materialInsance]);
final cube = await viewer.createGeometry(
GeometryHelper.cube(
normals: true,
uvs: true,
),
materialInstances: [materialInsance]);
expect(await viewer.isCastShadowsEnabled(cube.entity), true);
await testHelper.capture(viewer, "cast_shadows_enabled");
await viewer.setCastShadows(cube.entity, false);
expect(await viewer.isCastShadowsEnabled(cube.entity), false);
await testHelper.capture(viewer, "cast_shadows_disabled");
}, bg: kRed);
});
test('enable/disable receive shadows', () async {
await testHelper.withViewer((viewer) async {
await viewer.setPostProcessing(true);
await viewer.setShadowsEnabled(true);
await viewer.setShadowType(ShadowType.PCF);
var materialInsance = await viewer.createUbershaderMaterialInstance();
await materialInsance.setParameterFloat4(
"baseColorFactor", 0.0, 1.0, 0.0, 1.0);
await viewer.addDirectLight(DirectLight.sun(
intensity: 50000,
castShadows: true,
direction: Vector3(1, -0.5, 0).normalized()));
final plane = await viewer.createGeometry(
GeometryHelper.plane(
normals: true, uvs: true, width: 10, height: 10),
materialInstances: [materialInsance]);
final cube = await viewer.createGeometry(
GeometryHelper.cube(
normals: true,
uvs: true,
),
materialInstances: [materialInsance]);
expect(await viewer.isReceiveShadowsEnabled(plane.entity), true);
await testHelper.capture(viewer, "receive_shadows_enabled");
await viewer.setReceiveShadows(plane.entity, false);
expect(await viewer.isReceiveShadowsEnabled(plane.entity), false);
await testHelper.capture(viewer, "receive_shadows_disabled");
}, bg: kRed);
});
});
}