From 49269bbb43e241a62f45d9c41c98b69a23ed2c76 Mon Sep 17 00:00:00 2001 From: xLinka Date: Wed, 13 Nov 2024 18:27:38 +0000 Subject: [PATCH] Wizards Plus Isusereyetracking node --- .../Components/Wizards/LightSourcesWizard.cs | 189 +++++++++++++++ .../Wizards/MeshColliderManagementWizard.cs | 227 ++++++++++++++++++ .../Users/Status/IsUserEyeTracking.cs | 31 +++ 3 files changed, 447 insertions(+) create mode 100644 ProjectObsidian/Components/Wizards/LightSourcesWizard.cs create mode 100644 ProjectObsidian/Components/Wizards/MeshColliderManagementWizard.cs create mode 100644 ProjectObsidian/ProtoFlux/Users/Status/IsUserEyeTracking.cs diff --git a/ProjectObsidian/Components/Wizards/LightSourcesWizard.cs b/ProjectObsidian/Components/Wizards/LightSourcesWizard.cs new file mode 100644 index 0000000..4188181 --- /dev/null +++ b/ProjectObsidian/Components/Wizards/LightSourcesWizard.cs @@ -0,0 +1,189 @@ +using FrooxEngine.UIX; +using System; +using FrooxEngine; +using FrooxEngine.Undo; +using Elements.Core; +using System.Collections.Generic; + +namespace Obsidian +{ + [Category("Obsidian/Wizards")] + public class LightSourcesWizard : Component, IDeveloperInterface + { + public readonly SyncRef Root; + public readonly Sync ProcessPointLights; + public readonly Sync ProcessSpotLights; + public readonly Sync ProcessDirectionalLights; + public readonly Sync ProcessDisabled; + public readonly Sync TargetShadowType; + public readonly Sync FilterColors; + public readonly Sync Color; + private readonly SyncRef _tag; + private readonly SyncRef _intensityField; + private readonly SyncRef _rangeField; + private readonly SyncRef _spotAngleField; + private readonly SyncRef _maxColorVariance; + + protected override void OnAwake() + { + base.OnAwake(); + InitializeSettings(); + } + + private void InitializeSettings() + { + ProcessPointLights.Value = true; + ProcessSpotLights.Value = true; + ProcessDirectionalLights.Value = true; + ProcessDisabled.Value = false; + } + + protected override void OnAttach() + { + base.OnAttach(); + SetupUI(); + } + + private void SetupUI() + { + UIBuilder ui = RadiantUI_Panel.SetupPanel(Slot, "Light Source Wizard", new float2(500f, 1100f)); + Slot.LocalScale *= 0.0005f; + RadiantUI_Constants.SetupEditorStyle(ui); + + ui.VerticalLayout(4f); + ui.Style.Height = 24f; + + ui.Text("Light Source Wizard", alignment: Alignment.MiddleCenter); + + SetupMainUI(ui); + SetupColorFilterUI(ui); + SetupLightControlsUI(ui); + SetupLightActionsUI(ui); + } + + private void SetupMainUI(UIBuilder ui) + { + ui.Text("Process Root"); + ui.Next("Root"); + ui.Current.AttachComponent().Setup(Root); + + AddBooleanOption(ui, "Point Lights", ProcessPointLights); + AddBooleanOption(ui, "Spot Lights", ProcessSpotLights); + AddBooleanOption(ui, "Directional Lights", ProcessDirectionalLights); + AddBooleanOption(ui, "Disabled Lights", ProcessDisabled); + + ui.Text("Tag Filter"); + _tag.Target = ui.TextField(); + } + + private void SetupColorFilterUI(UIBuilder ui) + { + AddBooleanOption(ui, "Filter Colors", FilterColors); + ui.Text("Color Filter"); + ui.ColorXMemberEditor(Color); + ui.Text("Max Color Variance"); + _maxColorVariance.Target = ui.FloatField(0.0f, 1f, int.MaxValue); + } + + private void SetupLightControlsUI(UIBuilder ui) + { + ui.Text("-------"); + ui.EnumMemberEditor(TargetShadowType); + ui.Button("Set Shadow Type", SetShadowType); + + ui.Text("-------"); + _intensityField.Target = ui.FloatField(0.0f, float.PositiveInfinity, int.MaxValue); + ui.Button("Multiply Intensity", MultiplyIntensity); + ui.Button("Set Intensity", SetIntensity); + + ui.Text("-------"); + _rangeField.Target = ui.FloatField(0.0f, float.PositiveInfinity, int.MaxValue); + ui.Button("Multiply Range", MultiplyRange); + ui.Button("Set Range", SetRange); + + ui.Text("-------"); + _spotAngleField.Target = ui.FloatField(0.0f, 180.0f, int.MaxValue); + ui.Button("Set Spot Angle", SetSpotAngle); + } + + private void SetupLightActionsUI(UIBuilder ui) + { + ui.Text("-------"); + ui.Button("Enable Lights", Enable); + ui.Button("Disable Lights", Disable); + ui.Button("Delete Lights", Remove); + } + + private void AddBooleanOption(UIBuilder ui, string label, Sync syncValue) + { + ui.HorizontalElementWithLabel(label, 0.8f, () => ui.BooleanMemberEditor(syncValue)); + } + + private void SetShadowType(IButton button, ButtonEventData eventData) => + ProcessLights(l => { l.ShadowType.CreateUndoPoint(); l.ShadowType.Value = TargetShadowType.Value; }); + + private void MultiplyIntensity(IButton button, ButtonEventData eventData) => + ProcessLights(l => { l.Intensity.CreateUndoPoint(); l.Intensity.Value *= (float)_intensityField.Target.ParsedValue; }); + + private void SetIntensity(IButton button, ButtonEventData eventData) => + ProcessLights(l => { l.Intensity.CreateUndoPoint(); l.Intensity.Value = (float)_intensityField.Target.ParsedValue; }); + + private void MultiplyRange(IButton button, ButtonEventData eventData) => + ProcessLights(l => { l.Range.CreateUndoPoint(); l.Range.Value *= (float)_rangeField.Target.ParsedValue; }); + + private void SetRange(IButton button, ButtonEventData eventData) => + ProcessLights(l => { l.Range.CreateUndoPoint(); l.Range.Value = (float)_rangeField.Target.ParsedValue; }); + + private void SetSpotAngle(IButton button, ButtonEventData eventData) => + ProcessLights(l => { l.SpotAngle.CreateUndoPoint(); l.SpotAngle.Value = (float)_spotAngleField.Target.ParsedValue; }); + + private void Remove(IButton button, ButtonEventData eventData) => + ProcessLights(l => l.UndoableDestroy()); + + private void Disable(IButton button, ButtonEventData eventData) => + ProcessLights(l => { l.EnabledField.CreateUndoPoint(); l.Enabled = false; }); + + private void Enable(IButton button, ButtonEventData eventData) => + ProcessLights(l => { l.EnabledField.CreateUndoPoint(); l.Enabled = true; }); + + private void ProcessLights(Action process) + { + string tag = _tag.Target.TargetString; + color filterColor = (color)Color.Value; + World.BeginUndoBatch("Modify lights"); + + foreach (Light light in GetFilteredLights(tag, filterColor)) + { + process(light); + } + + World.EndUndoBatch(); + } + + private IEnumerable GetFilteredLights(string tag, color filterColor) + { + return (Root.Target ?? World.RootSlot).GetComponentsInChildren(l => + IsLightEligible(l, tag, filterColor)); + } + + private bool IsLightEligible(Light light, string tag, color filterColor) + { + if (!ProcessDisabled.Value && (!light.Enabled || !light.Slot.IsActive)) return false; + if (!string.IsNullOrEmpty(tag) && light.Slot.Tag != tag) return false; + if (FilterColors && !ColorWithinTolerance((color)light.Color.Value, filterColor, _maxColorVariance.Target.ParsedValue)) return false; + + return light.LightType.Value switch + { + LightType.Point => ProcessPointLights.Value, + LightType.Directional => ProcessDirectionalLights.Value, + LightType.Spot => ProcessSpotLights.Value, + _ => false + }; + } + + private bool ColorWithinTolerance(color col1, color col2, float tolerance) => + Math.Abs(col1.r - col2.r) < tolerance && + Math.Abs(col1.g - col2.g) < tolerance && + Math.Abs(col1.b - col2.b) < tolerance; + } +} diff --git a/ProjectObsidian/Components/Wizards/MeshColliderManagementWizard.cs b/ProjectObsidian/Components/Wizards/MeshColliderManagementWizard.cs new file mode 100644 index 0000000..5d022ee --- /dev/null +++ b/ProjectObsidian/Components/Wizards/MeshColliderManagementWizard.cs @@ -0,0 +1,227 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using FrooxEngine; +using FrooxEngine.UIX; +using Elements.Core; +using FrooxEngine.Undo; + +namespace Obsidian +{ + public enum ReplacementColliderComponent + { + BoxCollider, + SphereCollider, + CapsuleCollider, + CylinderCollider, + ConvexHullCollider + } + + public enum SetupBoundsType + { + None, + SetupFromLocalBounds, + SetupFromPreciseBounds, + } + + public enum UseTagMode + { + IgnoreTag, + IncludeOnlyWithTag, + ExcludeAllWithTag + } + + [Category("Obsidian/Wizards")] + public class MeshColliderManagementWizard : Component, IDeveloperInterface + { + public readonly SyncRef Root; + public readonly Sync IgnoreInactive; + public readonly Sync IgnoreDisabled; + public readonly Sync IgnoreNonPersistent; + public readonly Sync PreserveColliderSettings; + public readonly Sync replacementColliderComponent; + public readonly Sync setupBoundsType; + public readonly Sync useTagMode; + public readonly Sync HighlightDuration; + public readonly Sync HighlightColor; // Changed to colorX to avoid color type mismatch + public readonly SyncRef tag; + public readonly SyncRef resultsText; + + private Slot _scrollAreaRoot; + private UIBuilder _listBuilder; + + protected override void OnAttach() + { + base.OnAttach(); + InitializeUI(); + } + + private void InitializeUI() + { + UIBuilder ui = RadiantUI_Panel.SetupPanel(Slot, "Mesh Collider Management Wizard", new float2(420f, 800f)); + Slot.LocalScale *= 0.0005f; + RadiantUI_Constants.SetupEditorStyle(ui); + + ui.VerticalLayout(4f); + ui.Style.Height = 24f; + + ui.Text("Root Slot"); + ui.Next("Root"); + ui.Current.AttachComponent().Setup(Root); + + AddBooleanOption(ui, "Ignore Inactive", IgnoreInactive); + AddBooleanOption(ui, "Ignore Disabled", IgnoreDisabled); + AddBooleanOption(ui, "Ignore Non-Persistent", IgnoreNonPersistent); + AddBooleanOption(ui, "Preserve Collider Settings", PreserveColliderSettings); + + ui.Text("Tag Filter"); + ui.Next("Tag"); + tag.Target = ui.TextField(); + ui.EnumMemberEditor(useTagMode); + + ui.Text("Replacement Collider Component"); + ui.EnumMemberEditor(replacementColliderComponent); + + ui.Text("Setup Bounds Type"); + ui.EnumMemberEditor(setupBoundsType); + + ui.Text("Highlight Duration"); + ui.FloatField(HighlightDuration); + ui.Text("Highlight Color"); + ui.ColorXMemberEditor(HighlightColor); + + ui.Button("List MeshColliders", (IButton button, ButtonEventData eventData) => OnListMeshColliders()); + ui.Button("Replace All MeshColliders", (IButton button, ButtonEventData eventData) => OnReplaceAllMeshColliders()); + ui.Button("Remove All MeshColliders", (IButton button, ButtonEventData eventData) => OnRemoveAllMeshColliders()); + + resultsText.Target = ui.Text(""); + } + + private void AddBooleanOption(UIBuilder ui, string label, Sync syncValue) + { + ui.HorizontalElementWithLabel(label, 0.8f, () => ui.BooleanMemberEditor(syncValue)); + } + + private void OnListMeshColliders() + { + PopulateList(); + } + + private void PopulateList() + { + _scrollAreaRoot?.DestroyChildren(); + GetMeshColliders().ForEach(mc => CreateColliderElement(mc)); + } + + private void CreateColliderElement(MeshCollider mc) + { + var element = _listBuilder.Next("Element"); + var refField = element.AttachComponent>(); + refField.Reference.Target = mc; + + var builder = new UIBuilder(element); + builder.HorizontalLayout(10f); + + builder.Button("Jump", (IButton button, ButtonEventData eventData) => JumpToCollider(mc.Slot)); + builder.Button("Highlight", (IButton button, ButtonEventData eventData) => HighlightCollider(mc.Slot)); + builder.Button("Replace", (IButton button, ButtonEventData eventData) => ReplaceCollider(mc)); + builder.Button("Remove", (IButton button, ButtonEventData eventData) => RemoveCollider(mc)); + + builder.Current.AttachComponent().Setup(refField.Reference); + } + + private List GetMeshColliders() + { + string tagText = tag.Target?.ToString() ?? string.Empty; + var colliders = (Root.Target ?? World.RootSlot) + .GetComponentsInChildren() + .Where(mc => (!IgnoreInactive.Value || mc.Slot.IsActive) + && (!IgnoreDisabled.Value || mc.Enabled) + && (!IgnoreNonPersistent.Value || mc.IsPersistent) + && (useTagMode.Value == UseTagMode.IgnoreTag + || (useTagMode.Value == UseTagMode.IncludeOnlyWithTag && mc.Slot.Tag == tagText) + || (useTagMode.Value == UseTagMode.ExcludeAllWithTag && mc.Slot.Tag != tagText))) + .ToList(); + + ShowResults($"{colliders.Count} MeshColliders found."); + return colliders; + } + + private void ShowResults(string message) + { + if (resultsText.Target != null) + resultsText.Target.Content.Value = message; + } + + private void JumpToCollider(Slot slot) + { + var userRoot = LocalUser?.GetComponent(); + if (userRoot != null) + { + userRoot.JumpToPoint(slot.GlobalPosition, distance: 1.5f); + } + else + { + UniLog.Log("Failed to retrieve UserRoot or LocalUser."); + } + } + + private void HighlightCollider(Slot slot) + { + HighlightHelper.FlashHighlight(slot, null, HighlightColor.Value, HighlightDuration.Value); + } + + private void ReplaceCollider(MeshCollider mc) + { + var slot = mc.Slot; + mc.UndoableDestroy(); + + Component newCollider = replacementColliderComponent.Value switch + { + ReplacementColliderComponent.BoxCollider => slot.AttachComponent(), + ReplacementColliderComponent.SphereCollider => slot.AttachComponent(), + ReplacementColliderComponent.CapsuleCollider => slot.AttachComponent(), + ReplacementColliderComponent.CylinderCollider => slot.AttachComponent(), + ReplacementColliderComponent.ConvexHullCollider => slot.AttachComponent(), + _ => null + }; + + SetupColliderBounds(newCollider, mc); + } + + private void SetupColliderBounds(Component collider, Component mc) + { + if (collider is BoxCollider bc && mc is BoxCollider oldBc) + { + bc.Type.Value = oldBc.Type.Value; + bc.Mass.Value = oldBc.Mass.Value; + bc.Size.Value = oldBc.Size.Value; + } + else if (collider is SphereCollider sc && mc is SphereCollider oldSc) + { + sc.Radius.Value = oldSc.Radius.Value; + sc.Mass.Value = oldSc.Mass.Value; + } + // Additional collider type handling as needed + } + + private void RemoveCollider(MeshCollider mc) + { + mc.UndoableDestroy(); + PopulateList(); + } + + private void OnReplaceAllMeshColliders() + { + GetMeshColliders().ForEach(mc => ReplaceCollider(mc)); + ShowResults("All matching MeshColliders replaced."); + } + + private void OnRemoveAllMeshColliders() + { + GetMeshColliders().ForEach(mc => mc.UndoableDestroy()); + PopulateList(); + ShowResults("All matching MeshColliders removed."); + } + } +} diff --git a/ProjectObsidian/ProtoFlux/Users/Status/IsUserEyeTracking.cs b/ProjectObsidian/ProtoFlux/Users/Status/IsUserEyeTracking.cs new file mode 100644 index 0000000..f468577 --- /dev/null +++ b/ProjectObsidian/ProtoFlux/Users/Status/IsUserEyeTracking.cs @@ -0,0 +1,31 @@ +using FrooxEngine; +using FrooxEngine.ProtoFlux; +using FrooxEngine.ProtoFlux.Runtimes.Execution.Nodes; +using ProtoFlux.Core; +using ProtoFlux.Runtimes.Execution; + +namespace ProtoFlux.Runtimes.Execution.Nodes.Obsidian.Users.Status +{ + [NodeCategory("Obsidian/Users/Status")] + [NodeName("Is User Eye Tracking")] + public class IsUserEyeTracking : ValueFunctionNode + { + public readonly ObjectInput User; + public readonly ObjectInput Side; + + protected override bool Compute(FrooxEngineContext context) + { + User user = User.Evaluate(context); + if (user != null) + { + EyeTrackingStreamManager eyeTrackingStreamManager = user.Root.GetRegisteredComponent(); + if (eyeTrackingStreamManager != null) + { + EyeSide side = Side.Evaluate(context); + return eyeTrackingStreamManager.GetIsTracking(side); + } + } + return false; + } + } +}