don't share a single material instance for all highlighted objects

destroy highlight material instances on highlight asset destroyed
expose highlight instances on Scene via getAssetForHighlight
This commit is contained in:
Nick Fisher
2025-06-26 15:32:05 +08:00
parent b5d9a06422
commit a7b0ed6f21
3 changed files with 40 additions and 18 deletions

View File

@@ -47,26 +47,42 @@ class FFIScene extends Scene {
/// ///
@override @override
Future removeStencilHighlight(ThermionAsset asset) async { Future removeStencilHighlight(ThermionAsset asset) async {
if (!_highlighted.contains(asset)) { if (!_highlightInstances.containsKey(asset)) {
_logger _logger
.warning("No stencil highlight for asset (entity ${asset.entity})"); .warning("No stencil highlight for asset (entity ${asset.entity})");
return; return;
} }
_logger _logger
.info("Removing stencil highlight for asset (entity ${asset.entity})"); .info("Removing stencil highlight for asset (entity ${asset.entity})");
_highlighted.remove(asset);
final highlight = _highlightInstances[asset]!; final highlight = _highlightInstances[asset]!;
_highlightInstances.remove(asset);
await remove(highlight); await remove(highlight);
final materialInstance = await highlight.getMaterialInstanceAt();
await FilamentApp.instance!.destroyAsset(highlight); await FilamentApp.instance!.destroyAsset(highlight);
await materialInstance.destroy();
_logger _logger
.info("Removed stencil highlight for asset (entity ${asset.entity})"); .info("Removed stencil highlight for asset (entity ${asset.entity})");
} }
static MaterialInstance? _highlightMaterialInstance; final _highlightInstances = <ThermionAsset, ThermionAsset>{};
final _highlightInstances = <ThermionAsset, FFIAsset>{};
final _highlighted = <ThermionAsset>{}; Future<ThermionAsset?> getAssetForHighlight(ThermionEntity entity) async {
for (final asset in _highlightInstances.keys) {
var highlightAsset = _highlightInstances[asset]!;
if (highlightAsset.entity == entity) {
return asset;
}
for (final child in await highlightAsset.getChildEntities()) {
if (child == entity) {
return asset;
}
}
}
return null;
}
/// ///
/// ///
@@ -80,21 +96,21 @@ class FFIScene extends Scene {
int primitiveIndex = 0}) async { int primitiveIndex = 0}) async {
entity ??= asset.entity; entity ??= asset.entity;
if (_highlighted.contains(asset)) { if (_highlightInstances.containsKey(asset)) {
_logger _logger
.info("Stencil highlight exists for asset (entity ${asset.entity})"); .info("Stencil highlight exists for asset (entity ${asset.entity})");
var instance = _highlightInstances[asset];
var highlightMaterialInstance = await instance!.getMaterialInstanceAt();
await highlightMaterialInstance.setParameterFloat4(
"baseColorFactor", r, g, b, 1.0);
} else { } else {
_highlighted.add(asset); var highlightMaterialInstance =
_highlightMaterialInstance ??=
await FilamentApp.instance!.createUnlitMaterialInstance(); await FilamentApp.instance!.createUnlitMaterialInstance();
var highlightInstance = await asset var highlightInstance = await asset
.createInstance(materialInstances: [_highlightMaterialInstance!]); .createInstance(materialInstances: [highlightMaterialInstance]);
_highlightInstances[asset] = highlightInstance as FFIAsset;
await highlightInstance.setCastShadows(false); await highlightInstance.setCastShadows(false);
await highlightInstance.setReceiveShadows(false); await highlightInstance.setReceiveShadows(false);
_highlightInstances[asset] = highlightInstance as FFIAsset;
var sourceMaterialInstance = var sourceMaterialInstance =
await asset.getMaterialInstanceAt(entity: entity); await asset.getMaterialInstanceAt(entity: entity);
@@ -108,13 +124,13 @@ class FFIScene extends Scene {
await sourceMaterialInstance await sourceMaterialInstance
.setStencilReferenceValue(View.STENCIL_HIGHLIGHT_REFERENCE_VALUE); .setStencilReferenceValue(View.STENCIL_HIGHLIGHT_REFERENCE_VALUE);
await _highlightMaterialInstance! await highlightMaterialInstance
.setStencilCompareFunction(SamplerCompareFunction.NE); .setStencilCompareFunction(SamplerCompareFunction.NE);
await _highlightMaterialInstance! await highlightMaterialInstance
.setStencilReferenceValue(View.STENCIL_HIGHLIGHT_REFERENCE_VALUE); .setStencilReferenceValue(View.STENCIL_HIGHLIGHT_REFERENCE_VALUE);
await _highlightMaterialInstance!.setDepthCullingEnabled(true); await highlightMaterialInstance.setDepthCullingEnabled(true);
await _highlightMaterialInstance! await highlightMaterialInstance.setParameterFloat4(
.setParameterFloat4("baseColorFactor", r, g, b, 1.0); "baseColorFactor", r, g, b, 1.0);
await add(highlightInstance); await add(highlightInstance);

View File

@@ -22,6 +22,11 @@ abstract class Scene {
/// ///
Future removeEntity(ThermionEntity entity); Future removeEntity(ThermionEntity entity);
///
///
///
Future<ThermionAsset?> getAssetForHighlight(ThermionEntity entity);
/// Renders an outline around [entity] with the given color. /// Renders an outline around [entity] with the given color.
/// ///
/// ///

View File

@@ -24,6 +24,7 @@ void main() async {
await testHelper.capture(viewer.view, "stencil_highlight"); await testHelper.capture(viewer.view, "stencil_highlight");
await scene.removeStencilHighlight(cube); await scene.removeStencilHighlight(cube);
await testHelper.capture(viewer.view, "stencil_highlight_removed"); await testHelper.capture(viewer.view, "stencil_highlight_removed");
}, createStencilBuffer: true); }, createStencilBuffer: true);
}); });