@@ -23,6 +23,8 @@ public interface IPrimitiveComponent
2323 public bool IsOpaque { get ; }
2424 public bool IsVisible { get ; set ; }
2525
26+ public void SetMaterialVisibility ( uint materialIndex , bool visible ) ;
27+
2628 internal MaterialSection ? SelectedMaterial { get ; }
2729}
2830
@@ -64,7 +66,7 @@ public bool IsVisible
6466 if ( field == value ) return ;
6567
6668 field = value ;
67- MarkDirty ( DirtyFlags . Visibility ) ;
69+ SetMaterialsVisible ( value ) ;
6870 }
6971 } = true ;
7072
@@ -213,6 +215,30 @@ public void SnapToGround()
213215 SetLocalTransform ( transform ) ;
214216 }
215217
218+ public bool IsMaterialVisible ( uint materialIndex ) => materialIndex >= Materials . Length || Materials [ materialIndex ] is not { IsVisible : false } ;
219+ public void SetMaterialVisibility ( uint materialIndex , bool visible )
220+ {
221+ if ( materialIndex >= Materials . Length || Materials [ materialIndex ] is not { } material ) return ;
222+ if ( material . IsVisible == visible ) return ;
223+
224+ material . IsVisible = visible ;
225+ MarkVisibilityDirty ( ) ;
226+ }
227+ private void SetMaterialsVisible ( bool visible )
228+ {
229+ if ( Materials is { } materials )
230+ {
231+ foreach ( var material in materials )
232+ {
233+ material ? . IsVisible = visible;
234+ }
235+ }
236+
237+ MarkVisibilityDirty( ) ;
238+ }
239+
240+ private void MarkVisibilityDirty ( ) => MarkDirty ( DirtyFlags . Visibility | ( IsOutlined ? DirtyFlags . Outline : DirtyFlags . None ) ) ;
241+
216242 protected override void BeginPlay ( ActorManager scene )
217243 {
218244 base . BeginPlay ( scene ) ;
@@ -224,12 +250,21 @@ protected override void BeginPlay(ActorManager scene)
224250
225251 private const string HeaderLabel = "Mesh" ;
226252 private HeaderButtons HeaderButtons => field ??= new HeaderButtons( HeaderLabel )
227- . Add ( ( ) => IsVisible ? Settings . EyeIcon : Settings . EyeSlashIcon , ( ) => "Toggle Visibility" ,
253+ . Add( ( ) => MaterialVisibilityIcon , ( ) => "Toggle Visibility" ,
228254 ( ) => { IsVisible = ! IsVisible ; } , null ,
229- ( ) => IsVisible ? null : Settings . RedColor )
255+ ( ) => MaterialVisibilityColor )
230256 . Add ( "\uf0c5 " , "Copy Path" , ( ) => ImGui . SetClipboardText ( Descriptor . Path ) )
231257 . Add ( "\uf05a " , "Primitive Info" , ( ) => ImGui . OpenPopup ( "##PrimitiveInfo" ) ) ;
232258
259+ private PropertyToggleButton [ ] SectionButtons => field ??=
260+ [
261+ new PropertyToggleButton (
262+ ( ) => MaterialVisibilityIcon ,
263+ ( ) => ImGui . OpenPopup ( SectionVisibilityPopup ) ,
264+ ( ) => "Section Visibility" ,
265+ textColor : ( ) => MaterialVisibilityColor )
266+ ] ;
267+
233268 private PropertyToggleButton [ ] MaterialButtons => field ??=
234269 [
235270 new PropertyToggleButton (
@@ -319,7 +354,8 @@ public override void DrawControls()
319354 EditorUI . Text ( "Draw Distance" , $ "Min: { DrawDistance . X } , Max: { DrawDistance . Y } ") ;
320355 }
321356
322- EditorUI . Property ( $ "Sections ({ lod . Sections . Length } )") ;
357+ EditorUI . PropertyWithToggle ( $ "Sections ({ lod . Sections . Length } )", SectionButtons ) ;
358+ DrawSectionVisibilityPopup ( lod ) ;
323359 var selected = lod . Sections [ _sectionIndex ] ;
324360 _materialIndex = ( int ) selected . MaterialIndex ;
325361
@@ -342,7 +378,7 @@ public override void DrawControls()
342378 ImGui . EndCombo ( ) ;
343379 }
344380
345- EditorUI . Caption ( $ "Material { selected . MaterialIndex } , { ( selected . CastShadow && CastShadow ? "casts shadow" : "no shadow" ) } ") ;
381+ EditorUI . Caption ( $ "Material { selected . MaterialIndex } , { ( selected . CastShadow && CastShadow ? "casts shadow" : "no shadow" ) } { ( SelectedMaterial is { IsVisible : false } ? ", hidden" : "" ) } ") ;
346382 ImGui . Spacing ( ) ;
347383 ImGui . EndGroup ( ) ;
348384
@@ -368,6 +404,64 @@ public override void DrawControls()
368404 } ) ;
369405 }
370406
407+ private const string SectionVisibilityPopup = "##SectionVisibility" ;
408+ private bool HasHiddenMaterial => Array. Exists( Materials , x => x is { IsVisible : false } ) ;
409+ private bool HasVisibleMaterial => Array. Exists( Materials , x => x is not { IsVisible : false } ) ;
410+ private string MaterialVisibilityIcon => HasVisibleMaterial ? HasHiddenMaterial ? Settings. EyeLowVisionIcon : Settings. EyeIcon : Settings. EyeSlashIcon;
411+ private Vector4 ? MaterialVisibilityColor => HasVisibleMaterial ? HasHiddenMaterial ? Settings. OrangeColor : null : Settings. RedColor;
412+
413+ private void DrawSectionVisibilityPopup ( LodDescriptor < TVertex > lod )
414+ {
415+ if ( ! ImGui . BeginPopup ( SectionVisibilityPopup ) ) return ;
416+
417+ var sections = lod . Sections ;
418+ var frameHeight = ImGui . GetFrameHeight ( ) ;
419+
420+ var nameWidth = 0f ;
421+ for ( var i = 0 ; i < sections . Length ; i ++ )
422+ {
423+ nameWidth = MathF . Max ( nameWidth , ImGui . CalcTextSize ( $ "{ i } : { sections [ i ] . Name } ") . X ) ;
424+ }
425+
426+ var style = ImGui . GetStyle ( ) ;
427+ var width = Math . Clamp ( frameHeight + style . ItemSpacing . X + nameWidth + style . ScrollbarSize , frameHeight * 8f , frameHeight * 16f ) ;
428+
429+ var anyVisible = HasVisibleMaterial ;
430+ if ( EditorUI . IconButton ( MaterialVisibilityIcon , anyVisible ? "Hide All" : "Show All" , textColor : MaterialVisibilityColor ) )
431+ {
432+ SetMaterialsVisible ( ! anyVisible ) ;
433+ }
434+ ImGui . SameLine ( ) ;
435+ ImGui . AlignTextToFramePadding ( ) ;
436+ ImGui . TextDisabled ( $ "{ sections . Length } Section{ ( sections . Length != 1 ? "s" : "" ) } ") ;
437+ ImGui . Separator ( ) ;
438+
439+ var height = Math . Min ( sections . Length , 5 ) * ImGui . GetFrameHeightWithSpacing ( ) ;
440+ if ( ImGui . BeginChild ( "##SectionList" , new Vector2 ( width , height ) ) )
441+ {
442+ for ( var i = 0 ; i < sections . Length ; i ++ )
443+ {
444+ var section = sections [ i ] ;
445+ var material = section . MaterialIndex < Materials . Length ? Materials [ section . MaterialIndex ] : null ;
446+ var isVisible = material is not { IsVisible : false } ;
447+
448+ ImGui . PushID ( i ) ;
449+ if ( EditorUI . IconButton ( isVisible ? Settings . EyeIcon : Settings . EyeSlashIcon , null , material != null , isVisible ? null : Settings . RedColor ) && material != null )
450+ {
451+ SetMaterialVisibility ( section . MaterialIndex , ! isVisible ) ;
452+ }
453+ ImGui . SameLine ( ) ;
454+ ImGui . AlignTextToFramePadding ( ) ;
455+ if ( isVisible ) ImGui . TextUnformatted ( $ "{ i } : { section . Name } ") ;
456+ else ImGui . TextDisabled ( $ "{ i } : { section . Name } ") ;
457+ ImGui . PopID ( ) ;
458+ }
459+ }
460+ ImGui . EndChild ( ) ;
461+
462+ ImGui . EndPopup ( ) ;
463+ }
464+
371465 private void DrawInfoPopup ( )
372466 {
373467 var viewport = ImGui . GetMainViewport ( ) ;
0 commit comments