diff --git a/thermion_dart/lib/thermion_dart/viewer/ffi/thermion_dart.g.dart b/thermion_dart/lib/thermion_dart/viewer/ffi/thermion_dart.g.dart index 09dc272e..e87ac730 100644 --- a/thermion_dart/lib/thermion_dart/viewer/ffi/thermion_dart.g.dart +++ b/thermion_dart/lib/thermion_dart/viewer/ffi/thermion_dart.g.dart @@ -1155,10 +1155,18 @@ external void get_bounding_box_to_out( @ffi.Native, ffi.Int, ffi.Bool)>( isLeaf: true) -external void set_layer_enabled( +external void set_layer_visibility( ffi.Pointer sceneManager, int layer, - bool enabled, + bool visible, +); + +@ffi.Native, EntityId, ffi.Int)>( + isLeaf: true) +external void set_visibility_layer( + ffi.Pointer sceneManager, + int entity, + int layer, ); @ffi.Native< @@ -1203,6 +1211,15 @@ external void remove_stencil_highlight( int entity, ); +@ffi.Native< + ffi.Pointer Function( + ffi.Pointer, EntityId, ffi.Int)>(isLeaf: true) +external ffi.Pointer get_material_instance_at( + ffi.Pointer sceneManager, + int entity, + int materialIndex, +); + @ffi.Native< ffi.Void Function(ffi.Pointer, EntityId, ffi.Int, ffi.Pointer, ffi.Float)>(isLeaf: true) diff --git a/thermion_dart/lib/thermion_dart/viewer/ffi/thermion_viewer_ffi.dart b/thermion_dart/lib/thermion_dart/viewer/ffi/thermion_viewer_ffi.dart index b32206e2..f015ecad 100644 --- a/thermion_dart/lib/thermion_dart/viewer/ffi/thermion_viewer_ffi.dart +++ b/thermion_dart/lib/thermion_dart/viewer/ffi/thermion_viewer_ffi.dart @@ -1920,8 +1920,17 @@ class ThermionViewerFFI extends ThermionViewer { /// /// /// - Future setLayerEnabled(int layer, bool enabled) async { - set_layer_enabled(_sceneManager!, layer, enabled); + Future setLayerVisibility(int layer, bool visible) { + set_layer_visibility(_sceneManager!, layer, visible); + return Future.value(); + } + + /// + /// + /// + Future setVisibilityLayer(ThermionEntity entity, int layer) { + set_visibility_layer(_sceneManager!, entity, layer); + return Future.value(); } /// diff --git a/thermion_dart/lib/thermion_dart/viewer/thermion_viewer_base.dart b/thermion_dart/lib/thermion_dart/viewer/thermion_viewer_base.dart index 388d91eb..f0081542 100644 --- a/thermion_dart/lib/thermion_dart/viewer/thermion_viewer_base.dart +++ b/thermion_dart/lib/thermion_dart/viewer/thermion_viewer_base.dart @@ -545,7 +545,7 @@ abstract class ThermionViewer { Future moveCameraToAsset(ThermionEntity entity); /// - /// Enables/disables frustum culling. Currently we don't expose a method for manipulating the camera projection/culling matrices so this is your only option to deal with unwanted near/far clipping. + /// Enables/disables frustum culling. /// Future setViewFrustumCulling(bool enabled); @@ -596,14 +596,15 @@ abstract class ThermionViewer { /// Sets the material property [propertyName] under material [materialIndex] for [entity] to [value]. /// [entity] must have a Renderable attached. /// - Future setMaterialPropertyInt(ThermionEntity entity, String propertyName, - int materialIndex, int value); + Future setMaterialPropertyInt( + ThermionEntity entity, String propertyName, int materialIndex, int value); /// /// Sets the depthWrite material instance at [materialIndex] for [entity] to [enabled]. /// [entity] must have a Renderable attached. /// - Future setMaterialDepthWrite(ThermionEntity entity, int materialIndex, bool enabled); + Future setMaterialDepthWrite( + ThermionEntity entity, int materialIndex, bool enabled); /// /// Scale [entity] to fit within the unit cube. @@ -853,7 +854,12 @@ abstract class ThermionViewer { /// We place all scene assets in layer 0 (enabled by default), gizmos in layer 1 (enabled by default), world grid in layer 2 (disabled by default). /// Use this method to toggle visibility of the respective layer. /// - Future setLayerEnabled(int layer, bool enabled); + Future setLayerVisibility(int layer, bool visible); + + /// + /// Assigns [entity] to visibility layer [layer]. + /// + Future setVisibilityLayer(ThermionEntity entity, int layer); /// /// Show/hide the translation gizmo. diff --git a/thermion_dart/native/include/SceneManager.hpp b/thermion_dart/native/include/SceneManager.hpp index ae1eef93..95052bfe 100644 --- a/thermion_dart/native/include/SceneManager.hpp +++ b/thermion_dart/native/include/SceneManager.hpp @@ -55,6 +55,7 @@ namespace thermion_filament enum LAYERS { DEFAULT_ASSETS = 0, + BACKGROUND = 6, OVERLAY = 7, }; @@ -229,11 +230,8 @@ namespace thermion_filament /// /// Toggles the visibility of the given layer. - /// Layer 0 - regular scene assets - /// Layer 1 - unused - /// Layer 2 - grid /// - void setLayerEnabled(int layer, bool enabled); + void setLayerVisibility(SceneManager::LAYERS layer, bool enabled); /// /// Creates an entity with the specified geometry/material/normals and adds to the scene. @@ -284,6 +282,8 @@ namespace thermion_filament return _assetLoader->createInstance(asset); } + MaterialInstance* getMaterialInstanceAt(EntityId entityId, int materialIndex); + void setMaterialProperty(EntityId entity, int materialIndex, const char* property, float value); void setMaterialProperty(EntityId entity, int materialIndex, const char* property, int32_t value); void setMaterialProperty(EntityId entityId, int materialIndex, const char* property, filament::math::float4& value); @@ -298,6 +298,8 @@ namespace thermion_filament MaterialInstance* createUnlitMaterialInstance(); + void setVisibilityLayer(EntityId entityId, int layer); + private: gltfio::AssetLoader *_assetLoader = nullptr; const ResourceLoaderWrapperImpl *const _resourceLoaderWrapper; diff --git a/thermion_dart/native/include/ThermionDartApi.h b/thermion_dart/native/include/ThermionDartApi.h index 93314125..7d98deaf 100644 --- a/thermion_dart/native/include/ThermionDartApi.h +++ b/thermion_dart/native/include/ThermionDartApi.h @@ -269,11 +269,13 @@ extern "C" EMSCRIPTEN_KEEPALIVE void get_gizmo(void *const sceneManager, EntityId *out); EMSCRIPTEN_KEEPALIVE Aabb2 get_bounding_box(void *const sceneManager, EntityId entity); EMSCRIPTEN_KEEPALIVE void get_bounding_box_to_out(void *const sceneManager, EntityId entity, float *minX, float *minY, float *maxX, float *maxY); - EMSCRIPTEN_KEEPALIVE void set_layer_enabled(void *const sceneManager, int layer, bool enabled); + EMSCRIPTEN_KEEPALIVE void set_layer_visibility(void *const sceneManager, int layer, bool visible); + EMSCRIPTEN_KEEPALIVE void set_visibility_layer(void *const sceneManager, EntityId entity, int layer); EMSCRIPTEN_KEEPALIVE void pick_gizmo(void *const sceneManager, int x, int y, void (*callback)(EntityId entityId, int x, int y)); EMSCRIPTEN_KEEPALIVE void set_gizmo_visibility(void *const sceneManager, bool visible); EMSCRIPTEN_KEEPALIVE void set_stencil_highlight(void *const sceneManager, EntityId entity, float r, float g, float b); EMSCRIPTEN_KEEPALIVE void remove_stencil_highlight(void *const sceneManager, EntityId entity); + EMSCRIPTEN_KEEPALIVE TMaterialInstance* get_material_instance_at(void *const sceneManager, EntityId entity, int materialIndex); EMSCRIPTEN_KEEPALIVE void set_material_property_float(void *const sceneManager, EntityId entity, int materialIndex, const char *property, float value); EMSCRIPTEN_KEEPALIVE void set_material_property_int(void *const sceneManager, EntityId entity, int materialIndex, const char *property, int value); EMSCRIPTEN_KEEPALIVE void set_material_property_float4(void *const sceneManager, EntityId entity, int materialIndex, const char *property, double4 value); diff --git a/thermion_dart/native/src/FilamentViewer.cpp b/thermion_dart/native/src/FilamentViewer.cpp index 2dd12708..73333c0f 100644 --- a/thermion_dart/native/src/FilamentViewer.cpp +++ b/thermion_dart/native/src/FilamentViewer.cpp @@ -584,6 +584,7 @@ namespace thermion_filament .material(0, _imageMaterial->getDefaultInstance()) .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, _imageVb, _imageIb, 0, 3) + .layerMask(0xFF, 1u << SceneManager::LAYERS::BACKGROUND) .culling(false) .build(*_engine, _imageEntity); _scene->addEntity(_imageEntity); @@ -969,7 +970,11 @@ namespace thermion_filament delete vec; }, callbackData); _skybox = - filament::Skybox::Builder().environment(_skyboxTexture).build(*_engine); + filament::Skybox::Builder() + .environment(_skyboxTexture) + .build(*_engine); + + _skybox->setLayerMask(0xFF, 1u << SceneManager::LAYERS::BACKGROUND); _scene->setSkybox(_skybox); } diff --git a/thermion_dart/native/src/SceneManager.cpp b/thermion_dart/native/src/SceneManager.cpp index 25c3b3d7..b1134a50 100644 --- a/thermion_dart/native/src/SceneManager.cpp +++ b/thermion_dart/native/src/SceneManager.cpp @@ -104,6 +104,7 @@ namespace thermion_filament _scene->addEntity(_gridOverlay->grid()); _view->setLayerEnabled(SceneManager::LAYERS::DEFAULT_ASSETS, true); + _view->setLayerEnabled(SceneManager::LAYERS::BACKGROUND, true); // skybox + image _view->setLayerEnabled(SceneManager::LAYERS::OVERLAY, false); // world grid + gizmo } @@ -238,6 +239,17 @@ namespace thermion_filament return eid; } + void SceneManager::setVisibilityLayer(EntityId entityId, int layer) { + auto& rm = _engine->getRenderableManager(); + auto renderable = rm.getInstance(utils::Entity::import(entityId)); + if(!renderable.isValid()) { + Log("Warning: no renderable found"); + } + + rm.setLayerMask(renderable, 0xFF, 1u << layer); + + } + EntityId SceneManager::loadGlbFromBuffer(const uint8_t *data, size_t length, int numInstances, bool keepData, int priority, int layer) { @@ -2393,7 +2405,7 @@ namespace thermion_filament return Aabb2{minX, minY, maxX, maxY}; } - void SceneManager::setLayerEnabled(int layer, bool enabled) + void SceneManager::setLayerVisibility(LAYERS layer, bool enabled) { _view->setLayerEnabled(layer, enabled); } @@ -2528,6 +2540,18 @@ EntityId SceneManager::createGeometry( return entityId; } + MaterialInstance* SceneManager::getMaterialInstanceAt(EntityId entityId, int materialIndex) { + auto entity = Entity::import(entityId); + const auto &rm = _engine->getRenderableManager(); + auto renderableInstance = rm.getInstance(entity); + if (!renderableInstance.isValid()) + { + Log("Error retrieving material instance: no renderable found for entity %d"); + return std::nullptr_t(); + } + return rm.getMaterialInstanceAt(renderableInstance, materialIndex); + } + void SceneManager::setMaterialProperty(EntityId entityId, int materialIndex, const char *property, float value) { auto entity = Entity::import(entityId); @@ -2620,7 +2644,6 @@ EntityId SceneManager::createGeometry( return; } auto materialInstance = rm.getMaterialInstanceAt(renderableInstance, materialIndex); - materialInstance->setDepthWrite(enabled); } diff --git a/thermion_dart/native/src/ThermionDartApi.cpp b/thermion_dart/native/src/ThermionDartApi.cpp index fd7beb6a..1022102a 100644 --- a/thermion_dart/native/src/ThermionDartApi.cpp +++ b/thermion_dart/native/src/ThermionDartApi.cpp @@ -927,9 +927,14 @@ extern "C" *maxY = box.maxY; } - EMSCRIPTEN_KEEPALIVE void set_layer_enabled(void *const sceneManager, int layer, bool enabled) + EMSCRIPTEN_KEEPALIVE void set_visibility_layer(void *const sceneManager, EntityId entity, int layer) { + ((SceneManager*)sceneManager)->setVisibilityLayer(entity, layer); + } + + + EMSCRIPTEN_KEEPALIVE void set_layer_visibility(void *const sceneManager, int layer, bool visible) { - ((SceneManager *)sceneManager)->setLayerEnabled(layer, enabled); + ((SceneManager *)sceneManager)->setLayerVisibility((SceneManager::LAYERS)layer, visible); } EMSCRIPTEN_KEEPALIVE void thermion_flutter_free(void *ptr) @@ -962,9 +967,13 @@ extern "C" ((SceneManager *)sceneManager)->setMaterialProperty(entity, materialIndex, property, value); } + EMSCRIPTEN_KEEPALIVE TMaterialInstance* get_material_instance_at(void *const sceneManager, EntityId entity, int materialIndex) { + auto instance = ((SceneManager *)sceneManager)->getMaterialInstanceAt(entity, materialIndex); + return reinterpret_cast(instance); + } + EMSCRIPTEN_KEEPALIVE void set_material_property_int(void *const sceneManager, EntityId entity, int materialIndex, const char *property, int32_t value) { - ((SceneManager *)sceneManager)->setMaterialProperty(entity, materialIndex, property, value); } diff --git a/thermion_dart/test/integration_test.dart b/thermion_dart/test/integration_test.dart index e76a7bb3..891986e5 100644 --- a/thermion_dart/test/integration_test.dart +++ b/thermion_dart/test/integration_test.dart @@ -569,10 +569,9 @@ void main() async { await viewer.setMaterialDepthWrite(cube2, 0, true); await _capture(viewer, "geometry_enable_depth_write"); - // with depth write disabled on both materials, the red cube will render in front of the white cube + // with depth write disabled on the first material, the red cube will render in front of the white cube // (relying on insertion order) await viewer.setMaterialDepthWrite(cube1, 0, false); - await viewer.setMaterialDepthWrite(cube2, 0, false); await _capture(viewer, "geometry_disable_depth_write_insertion_order"); @@ -649,9 +648,9 @@ void main() async { .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8)); await viewer.setCameraPosition(0, 2, 0); await _capture(viewer, "grid_overlay_default"); - await viewer.setLayerEnabled(7, true); + await viewer.setLayerVisibility(7, true); await _capture(viewer, "grid_overlay_enabled"); - await viewer.setLayerEnabled(7, false); + await viewer.setLayerVisibility(7, false); await _capture(viewer, "grid_overlay_disabled"); }); @@ -666,9 +665,36 @@ void main() async { var buffer = File("$testDir/cube.glb").readAsBytesSync(); var model = await viewer.loadGlbFromBuffer(buffer, layer: 1); await _capture(viewer, "load_glb_from_buffer_with_layer_disabled"); - await viewer.setLayerEnabled(1, true); + await viewer.setLayerVisibility(1, true); await _capture(viewer, "load_glb_from_buffer_with_layer_enabled"); }); + + test('change layer visibility at runtime', () async { + var viewer = await createViewer(); + + await viewer.setBackgroundColor(1, 0, 1, 1); + await viewer.setCameraPosition(0, 2, 5); + await viewer + .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); + + var cube = await viewer.createGeometry(GeometryHelper.cube()); + await _capture(viewer, "change_layer_visibility_at_runtime_default"); + + // all entities set to layer 0 by default, so this should now be invisible + await viewer.setLayerVisibility(0, false); + await _capture( + viewer, "change_layer_visibility_at_runtime_layer0_invisible"); + + // now change the visibility layer to 5, should be invisible + await viewer.setVisibilityLayer(cube, 5); + await _capture( + viewer, "change_layer_visibility_at_runtime_layer5_invisible"); + + // now toggle layer 5 visibility, cube should now be visible + await viewer.setLayerVisibility(5, true); + await _capture( + viewer, "change_layer_visibility_at_runtime_layer5_visible"); + }); }); // test('point light', () async {