diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart index 1f6232b4..d0dcef07 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart @@ -2502,7 +2502,6 @@ external bool RenderableManager_isRenderable( int entityId, ); -/// Checks if the given entity has a renderable component @ffi.Native, EntityId)>( isLeaf: true) external bool RenderableManager_hasComponent( @@ -2510,13 +2509,11 @@ external bool RenderableManager_hasComponent( int entityId, ); -/// Returns true if this manager has no components @ffi.Native)>(isLeaf: true) external bool RenderableManager_empty( ffi.Pointer tRenderableManager, ); -/// Returns whether a light channel is enabled on a specified renderable @ffi.Native< ffi.Bool Function(ffi.Pointer, EntityId, ffi.UnsignedInt)>(isLeaf: true) @@ -2526,7 +2523,6 @@ external bool RenderableManager_getLightChannel( int channel, ); -/// Checks if the renderable can cast shadows @ffi.Native, EntityId)>( isLeaf: true) external bool RenderableManager_isShadowCaster( @@ -2534,7 +2530,24 @@ external bool RenderableManager_isShadowCaster( int entityId, ); -/// Checks if the renderable can receive shadows +@ffi.Native< + ffi.Void Function( + ffi.Pointer, EntityId, ffi.Bool)>(isLeaf: true) +external void RenderableManager_setCastShadows( + ffi.Pointer tRenderableManager, + int entityId, + bool castShadows, +); + +@ffi.Native< + ffi.Void Function( + ffi.Pointer, EntityId, ffi.Bool)>(isLeaf: true) +external void RenderableManager_setReceiveShadows( + ffi.Pointer tRenderableManager, + int entityId, + bool receiveShadows, +); + @ffi.Native, EntityId)>( isLeaf: true) external bool RenderableManager_isShadowReceiver( @@ -2542,7 +2555,6 @@ external bool RenderableManager_isShadowReceiver( int entityId, ); -/// Returns whether large-scale fog is enabled for this renderable @ffi.Native, EntityId)>( isLeaf: true) external bool RenderableManager_getFogEnabled( diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart index 659b6413..48b457b1 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart @@ -111,7 +111,7 @@ class ThermionViewerFFI extends ThermionViewer { if (view == nullptr) { 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"); } - print( - " is shadow caster : ${RenderableManager_isShadowCaster(_renderableManager!, SceneAsset_getEntity(assetPtr))} is shadow recevier : ${RenderableManager_isShadowReceiver(_renderableManager!, SceneAsset_getEntity(assetPtr))} "); - var asset = FFIAsset( assetPtr, _sceneManager!, _engine!, _unlitMaterialProvider!, this); @@ -2185,10 +2182,36 @@ class ThermionViewerFFI extends ThermionViewer { gizmoEntities.toSet() ..add(SceneAsset_getEntity(gizmo.cast()))); } + + /// + /// + /// + Future setCastShadows(ThermionEntity entity, bool castShadows) async { + RenderableManager_setCastShadows(_renderableManager!, entity, castShadows); + } + + /// + /// + /// + Future isCastShadowsEnabled(ThermionEntity entity) async { + return RenderableManager_isShadowCaster(_renderableManager!, entity); + } + + /// + /// + /// + Future setReceiveShadows(ThermionEntity entity, bool receiveShadows) async { + RenderableManager_setReceiveShadows(_renderableManager!, entity, receiveShadows); + } + + /// + /// + /// + Future isReceiveShadowsEnabled(ThermionEntity entity) async { + return RenderableManager_isShadowReceiver(_renderableManager!, entity); + } } - - class FFISwapChain extends SwapChain { final Pointer swapChain; final Pointer viewer; diff --git a/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart b/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart index 49119c06..394d444d 100644 --- a/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart +++ b/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart @@ -918,4 +918,24 @@ abstract class ThermionViewer { /// Throws an exception if the index is out-of-bounds. /// Camera getCameraAt(int index); + + /// + /// + /// + Future setCastShadows(ThermionEntity entity, bool castShadows); + + /// + /// + /// + Future isCastShadowsEnabled(ThermionEntity entity); + + /// + /// + /// + Future setReceiveShadows(ThermionEntity entity, bool receiveShadows); + + /// + /// + /// + Future isReceiveShadowsEnabled(ThermionEntity entity); } diff --git a/thermion_dart/native/include/c_api/TRenderableManager.h b/thermion_dart/native/include/c_api/TRenderableManager.h index 037efff2..f1ce5a11 100644 --- a/thermion_dart/native/include/c_api/TRenderableManager.h +++ b/thermion_dart/native/include/c_api/TRenderableManager.h @@ -12,37 +12,13 @@ extern "C" EMSCRIPTEN_KEEPALIVE void RenderableManager_setPriority(TRenderableManager *tRenderableManager, EntityId entityId, int priority); EMSCRIPTEN_KEEPALIVE TMaterialInstance *RenderableManager_getMaterialInstanceAt(TRenderableManager *tRenderableManager, EntityId entityId, int primitiveIndex); 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); - - /** - * Returns true if this manager has no components - */ 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); - - /** - * Checks if the renderable can cast shadows - */ EMSCRIPTEN_KEEPALIVE bool RenderableManager_isShadowCaster(TRenderableManager *tRenderableManager, EntityId entityId); - - /** - * Checks if the renderable can receive shadows - */ + EMSCRIPTEN_KEEPALIVE void RenderableManager_setCastShadows(TRenderableManager *tRenderableManager, EntityId entityId, bool castShadows); + EMSCRIPTEN_KEEPALIVE void RenderableManager_setReceiveShadows(TRenderableManager *tRenderableManager, EntityId entityId, bool receiveShadows); 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); #ifdef __cplusplus diff --git a/thermion_dart/native/src/c_api/TRenderableManager.cpp b/thermion_dart/native/src/c_api/TRenderableManager.cpp index cba4739a..0f9976a7 100644 --- a/thermion_dart/native/src/c_api/TRenderableManager.cpp +++ b/thermion_dart/native/src/c_api/TRenderableManager.cpp @@ -72,21 +72,45 @@ namespace thermion const auto &entity = utils::Entity::import(entityId); auto renderableInstance = renderableManager->getInstance(entity); if (!renderableInstance.isValid()) { + Log("Error: invalid renderable"); return false; } return renderableManager->isShadowCaster(renderableInstance); } + EMSCRIPTEN_KEEPALIVE void RenderableManager_setCastShadows(TRenderableManager *tRenderableManager, EntityId entityId, bool enabled) { + auto *renderableManager = reinterpret_cast(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) { auto *renderableManager = reinterpret_cast(tRenderableManager); const auto &entity = utils::Entity::import(entityId); auto renderableInstance = renderableManager->getInstance(entity); if (!renderableInstance.isValid()) { + Log("Error: invalid renderable"); return false; } return renderableManager->isShadowReceiver(renderableInstance); } + EMSCRIPTEN_KEEPALIVE void RenderableManager_setReceiveShadows(TRenderableManager *tRenderableManager, EntityId entityId, bool enabled) { + auto *renderableManager = reinterpret_cast(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) { auto *renderableManager = reinterpret_cast(tRenderableManager); const auto &entity = utils::Entity::import(entityId); diff --git a/thermion_dart/test/material_tests.dart b/thermion_dart/test/material_tests.dart index 45e5ac78..5b6b57e0 100644 --- a/thermion_dart/test/material_tests.dart +++ b/thermion_dart/test/material_tests.dart @@ -387,7 +387,7 @@ void main() async { await viewer.loadIbl( "file://${testHelper.testDir}/assets/default_env_ibl.ktx", intensity: 1000); - // await viewer.setShadowsEnabled(true); + await viewer.addDirectLight(DirectLight.sun( intensity: 500000, castShadows: true, diff --git a/thermion_dart/test/shadow_tests.dart b/thermion_dart/test/shadow_tests.dart new file mode 100644 index 00000000..29ca63be --- /dev/null +++ b/thermion_dart/test/shadow_tests.dart @@ -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); + }); + }); +}