using ImGui; using GlitchyEngine.World; using System; using GlitchyEngine.Math; using System.Collections; using GlitchyEngine.Renderer; using GlitchyEngine; using GlitchyEngine.Content; using GlitchyEngine.Scripting; using GlitchyEngine.Core; using Mono; namespace GlitchyEditor.EditWindows { using internal GlitchyEngine.World.TransformComponent; class ComponentEditWindow : EditorWindow { public const String s_WindowTitle = "Components"; private EntityHierarchyWindow _entityHierarchyWindow; private List<(Type, ComponentAttribute attribute)> _componentTypes = new .() ~ delete _; public this(EntityHierarchyWindow entityHierarchyWindow) { _entityHierarchyWindow = entityHierarchyWindow; for (let type in Type.Types) { if (let componentAttribute = type.GetCustomAttribute()) { _componentTypes.Add((type, componentAttribute)); } } } protected override void InternalShow() { ImGui.PushStyleVar(.WindowMinSize, ImGui.Vec2(1000, 100)); if(!ImGui.Begin(s_WindowTitle, &_open, .None)) { ImGui.PopStyleVar(); ImGui.End(); return; } if (_entityHierarchyWindow.SelectionSize == 1) { Entity entity = _entityHierarchyWindow.GetSelectedEntity(0); ShowComponents(entity); } ImGui.PopStyleVar(); ImGui.End(); } private void ShowComponents(Entity entity) { ShowNameComponentEditor(entity); ShowComponentEditor("Transform", entity, => ShowTransformComponentEditor); ShowComponentEditor("Camera", entity, => ShowCameraComponentEditor, => ShowComponentContextMenu); ShowComponentEditor("Sprite Renderer", entity, => ShowSpriteRendererComponentEditor, => ShowComponentContextMenu); ShowComponentEditor("Circle Renderer", entity, => ShowCircleRendererComponentEditor, => ShowComponentContextMenu); ShowComponentEditor("Mesh Renderer", entity, => ShowMeshRendererComponentEditor, => ShowComponentContextMenu); ShowComponentEditor("Light", entity, => ShowLightComponentEditor, => ShowComponentContextMenu); ShowComponentEditor("Mesh", entity, => ShowMeshComponentEditor, => ShowComponentContextMenu); ShowComponentEditor("Rigidbody 2D", entity, => ShowRigidBody2DComponentEditor, => ShowComponentContextMenu); ShowComponentEditor("Box collider 2D", entity, => ShowBoxCollider2DComponentEditor, => ShowComponentContextMenu); ShowComponentEditor("Circle collider 2D", entity, => ShowCircleCollider2DComponentEditor, => ShowComponentContextMenu); ShowComponentEditor("Polygon collider 2D", entity, => ShowPolygonCollider2DComponentEditor, => ShowComponentContextMenu); ShowComponentEditor("Script Component", entity, => ShowScriptComponentEditor, => ShowComponentContextMenu); ShowAddComponentButton(entity); } public void DrawSceneGUI(Entity entity) { DrawComponenSceneGUI(entity, => ShowPolygonColliderSceneGUI); } static void ShowPolygonColliderSceneGUI(Entity entity, PolygonCollider2DComponent* collider) { for (int i < collider.VertexCount) { Matrix localToWorld = entity.Transform.WorldTransform * Matrix.Translation(collider.Offset.X, collider.Offset.Y, 0); Matrix transform = Matrix.Translation(float3(collider.Vertices[i], 0)) * localToWorld; if (Handles.ShowGizmo(ref transform, .TRANSLATE_X | .TRANSLATE_Y, true, id: (int32)i)) { transform = localToWorld.Invert() * transform; collider.Vertices[i] = transform.Translation.XY; } } } private static void DrawComponenSceneGUI(Entity entity, function void(Entity, TComponent*) showSceneGUI) where TComponent: struct, new { if (!entity.HasComponent()) return; TComponent* component = entity.GetComponent(); showSceneGUI(entity, component); } private static void ShowComponentContextMenu(Entity entity, TComponent* component) where TComponent: struct, new { if (ImGui.Selectable("Remove Component")) entity.RemoveComponent(); } private static void ShowComponentEditor(String header, Entity entity, function void(Entity, TComponent*) showComponentEditor, function void(Entity, TComponent*) showComponentContextMenu = null) where TComponent: struct, new { if (!entity.HasComponent()) return; TComponent* component = entity.GetComponent(); ImGui.PushID(header); bool nodeOpen = ImGui.TreeNodeEx(header.CStr(), .DefaultOpen | .AllowItemOverlap | .Framed | .SpanFullWidth); if (showComponentContextMenu != null) { ImGui.SameLine(ImGui.GetWindowContentRegionMax().x - ImGui.CalcTextSize("...").x - 2 * ImGui.GetStyle().FramePadding.x); if (ImGui.SmallButton("...")) { ImGui.OpenPopup("component_popup"); } if (ImGui.BeginPopup("component_popup")) { showComponentContextMenu(entity, component); ImGui.EndPopup(); } } if (nodeOpen) { if (entity.TryGetComponent(let actualComponent)) showComponentEditor(entity, actualComponent); ImGui.TreePop(); } ImGui.PopID(); } private static void ShowNameComponentEditor(Entity entity) { if (!entity.HasComponent()) return; char8[256] nameBuffer = default; NameComponent* component = entity.GetComponent(); StringView name = null; if(component != null) { name = component.Name; } else { name = scope:: $"Entity {entity.Handle.[Friend]Index}"; } // Copy name to buffer Internal.MemCpy(&nameBuffer, name.Ptr, Math.Min(nameBuffer.Count, name.Length)); if(ImGui.InputText("Name", &nameBuffer, nameBuffer.Count)) { if(component == null) { component = entity.AddComponent(); } component.Name = StringView(&nameBuffer); } } private static void ShowTransformComponentEditor(Entity entity, TransformComponent* transform) { float textWidth = ImGui.CalcTextSize("Position".CStr()).x; textWidth = Math.Max(textWidth, ImGui.CalcTextSize("Rotation".CStr()).x); textWidth = Math.Max(textWidth, ImGui.CalcTextSize("Scale".CStr()).x); textWidth += ImGui.GetStyle().FramePadding.x * 3.0f; float3 position = transform.Position; if (ImGui.EditFloat3("Position", ref position, .Zero, 0.1f, textWidth)) { transform.Position = position; // if necessary reposition Rigidbody2D if (entity.TryGetComponent(let rigidbody2D)) { rigidbody2D.SetPosition(position.XY); } } float3 rotationEuler = MathHelper.ToDegrees(transform.EditorRotationEuler); bool3 componentEditable = true; if (entity.HasComponent()) { componentEditable.XY = false; rotationEuler.XY = 0; } if (ImGui.EditFloat3("Rotation", ref rotationEuler, .Zero, 0.1f, textWidth, componentEnabled: componentEditable)) { transform.EditorRotationEuler = MathHelper.ToRadians(rotationEuler); // if necessary reposition Rigidbody2D if (entity.TryGetComponent(let rigidbody2D)) { rigidbody2D.SetAngle(rotationEuler.Z); } } float3 scale = transform.Scale; if (ImGui.EditFloat3("Scale", ref scale, .One, 0.1f, textWidth)) transform.Scale = scale; } static String[] strings = new String[]("Orthographic", "Perspective", "Perspective (Infinite)") ~ delete _; private static void ShowCameraComponentEditor(Entity entity, CameraComponent* cameraComponent) { ImGui.Checkbox("Primary", &cameraComponent.Primary); var camera = ref cameraComponent.Camera; String typeName = strings[camera.ProjectionType.Underlying]; if (ImGui.BeginCombo("Projection", typeName.CStr())) { for (int i = 0; i < 3; i++) { bool isSelected = (typeName == strings[i]); if (ImGui.Selectable(strings[i], isSelected)) { camera.ProjectionType = (.)i; } if (isSelected) ImGui.SetItemDefaultFocus(); } ImGui.EndCombo(); } if (camera.ProjectionType == .Perspective) { float fovY = MathHelper.ToDegrees(camera.PerspectiveFovY); if (ImGui.DragFloat("Fov Y", &fovY, 0.1f)) camera.PerspectiveFovY = MathHelper.ToRadians(fovY); float near = camera.PerspectiveNearPlane; if (ImGui.DragFloat("Near", &near, 0.1f)) camera.PerspectiveNearPlane = near; float far = camera.PerspectiveFarPlane; if (ImGui.DragFloat("Far", &far, 0.1f)) camera.PerspectiveFarPlane = far; } else if (camera.ProjectionType == .InfinitePerspective) { float fovY = MathHelper.ToDegrees(camera.PerspectiveFovY); if (ImGui.DragFloat("Vertical FOV", &fovY, 0.1f)) camera.PerspectiveFovY = MathHelper.ToRadians(fovY); float near = camera.PerspectiveNearPlane; if (ImGui.DragFloat("Near", &near, 0.1f)) camera.PerspectiveNearPlane = near; } else if (camera.ProjectionType == .Orthographic) { float size = camera.OrthographicHeight; if (ImGui.DragFloat("Size", &size, 0.1f)) camera.OrthographicHeight = size; float near = camera.OrthographicNearPlane; if (ImGui.DragFloat("Near", &near, 0.1f)) camera.OrthographicNearPlane = near; float far = camera.OrthographicFarPlane; if (ImGui.DragFloat("Far", &far, 0.1f)) camera.OrthographicFarPlane = far; } bool fixedAspectRatio = camera.FixedAspectRatio; if (ImGui.Checkbox("Fixed Aspect Ratio", &fixedAspectRatio)) camera.FixedAspectRatio = fixedAspectRatio; if (fixedAspectRatio) { float aspect = camera.AspectRatio; if (ImGui.DragFloat("Aspect Ratio", &aspect, 0.1f)) camera.AspectRatio = aspect; } } private static void ShowSpriteRendererComponentEditor(Entity entity, SpriteRendererComponent* spriteRendererComponent) { ColorRGBA spriteColor = ColorRGBA.LinearToSRGB(spriteRendererComponent.Color); if (ImGui.ColorEdit4("Color", ref spriteColor)) spriteRendererComponent.Color = ColorRGBA.SRgbToLinear(spriteColor); ImGui.Button("Texture"); if (ImGui.BeginDragDropTarget()) { ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem); if (payload != null) { Log.EngineLogger.Warning(""); StringView path = .((char8*)payload.Data, (int)payload.DataSize); spriteRendererComponent.Sprite = Content.LoadAsset(path); } ImGui.EndDragDropTarget(); } ImGui.EditVector<4>("UV Transform", ref *(float[4]*)&spriteRendererComponent.UvTransform); } private static void ShowCircleRendererComponentEditor(Entity entity, CircleRendererComponent* circleRendererComponent) { ColorRGBA spriteColor = ColorRGBA.LinearToSRGB(circleRendererComponent.Color); if (ImGui.ColorEdit4("Color", ref spriteColor)) circleRendererComponent.Color = ColorRGBA.SRgbToLinear(spriteColor); ImGui.Button("Texture"); if (ImGui.BeginDragDropTarget()) { ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem); if (payload != null) { Log.EngineLogger.Warning(""); StringView path = .((char8*)payload.Data, (int)payload.DataSize); circleRendererComponent.Sprite = Content.LoadAsset(path); } ImGui.EndDragDropTarget(); } ImGui.EditVector<4>("UV Transform", ref *(float[4]*)&circleRendererComponent.UvTransform); ImGui.DragFloat("Inner Radius", &circleRendererComponent.InnerRadius, 0.1f, 0.0f, 1.0f); } private static void ShowMeshRendererComponentEditor(Entity entity, MeshRendererComponent* meshRendererComponent) { ImGui.TextUnformatted("Material:"); ImGui.SameLine(); Material material = meshRendererComponent.Material; StringView identifier = material?.Identifier ?? "None"; ImGui.Button(identifier.ToScopeCStr!()); if (ImGui.BeginDragDropTarget()) { ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem); if (payload != null) { StringView fullpath = .((char8*)payload.Data, (int)payload.DataSize); meshRendererComponent.Material = Content.LoadAsset(fullpath); } ImGui.EndDragDropTarget(); } /*Effect effect = material?.Effect; if (effect == null) return;*/ // Show a preview of the material here! } private static void ShowRigidBody2DComponentEditor(Entity entity, Rigidbody2DComponent* rigidBodyComponent) { const String[?] bodyTypeStrings = .("Static", "Dynamic", "Kinematic"); String bodyTypeName = bodyTypeStrings[rigidBodyComponent.BodyType.Underlying]; if (ImGui.BeginCombo("Type", bodyTypeName.CStr())) { for (int i = 0; i < 3; i++) { bool isSelected = (bodyTypeName == bodyTypeStrings[i]); if (ImGui.Selectable(bodyTypeStrings[i], isSelected)) { rigidBodyComponent.BodyType = (.)i; } if (isSelected) ImGui.SetItemDefaultFocus(); } ImGui.EndCombo(); } ImGui.Checkbox("Fixed Rotation", &rigidBodyComponent.FixedRotation); } private static void ShowBoxCollider2DComponentEditor(Entity entity, BoxCollider2DComponent* boxCollider) { float textWidth = ImGui.CalcTextSize("Offset".CStr()).x; textWidth += ImGui.GetStyle().FramePadding.x * 3.0f; float2 offset = boxCollider.Offset; if (ImGui.EditFloat2("Offset", ref offset, .Zero, 0.1f, textWidth)) boxCollider.Offset = offset; float2 size = boxCollider.Size; if (ImGui.EditFloat2("Size", ref size, .Zero, 0.1f, textWidth, float2(0.01f, 0.01f), float.PositiveInfinity.XX)) boxCollider.Size = size; float density = boxCollider.Density; if (ImGui.DragFloat("Density", &density, 0.0f, 0.1f, textWidth)) boxCollider.Density = density; float friction = boxCollider.Friction; if (ImGui.DragFloat("Friction", &friction, 0.0f, 0.1f, textWidth)) boxCollider.Friction = friction; float restitution = boxCollider.Restitution; if (ImGui.DragFloat("Restitution", &restitution, 0.0f, 0.1f, textWidth)) boxCollider.Restitution = restitution; float restitutionThreshold = boxCollider.RestitutionThreshold; if (ImGui.DragFloat("RestitutionThreshold", &restitutionThreshold, 0.0f, 0.1f, textWidth)) boxCollider.RestitutionThreshold = restitutionThreshold; } private static void ShowCircleCollider2DComponentEditor(Entity entity, CircleCollider2DComponent* circleCollider) { float textWidth = ImGui.CalcTextSize("Offset".CStr()).x; textWidth += ImGui.GetStyle().FramePadding.x * 3.0f; float2 offset = circleCollider.Offset; if (ImGui.EditFloat2("Offset", ref offset, .Zero, 0.1f, textWidth)) circleCollider.Offset = offset; float radius = circleCollider.Radius; if (ImGui.DragFloat("Radius", &radius, 0.0f, 0.1f, textWidth)) circleCollider.Radius = radius; float density = circleCollider.Density; if (ImGui.DragFloat("Density", &density, 0.0f, 0.1f, textWidth)) circleCollider.Density = density; float friction = circleCollider.Friction; if (ImGui.DragFloat("Friction", &friction, 0.0f, 0.1f, textWidth)) circleCollider.Friction = friction; float restitution = circleCollider.Restitution; if (ImGui.DragFloat("Restitution", &restitution, 0.0f, 0.1f, textWidth)) circleCollider.Restitution = restitution; float restitutionThreshold = circleCollider.RestitutionThreshold; if (ImGui.DragFloat("RestitutionThreshold", &restitutionThreshold, 0.0f, 0.1f, textWidth)) circleCollider.RestitutionThreshold = restitutionThreshold; } private static void ShowPolygonCollider2DComponentEditor(Entity entity, PolygonCollider2DComponent* polygonCollider) { float textWidth = ImGui.CalcTextSize("Offset".CStr()).x; textWidth += ImGui.GetStyle().FramePadding.x * 3.0f; float2 offset = polygonCollider.Offset; if (ImGui.EditFloat2("Offset", ref offset, .Zero, 0.1f, textWidth)) polygonCollider.Offset = offset; // TODO: Handles in editor, etc... if (ImGui.CollapsingHeader("Vertices")) { for (int i < polygonCollider.VertexCount) { ImGui.EditFloat2(scope $"{i}", ref polygonCollider.Vertices[i]); } } ImGui.BeginDisabled(polygonCollider.VertexCount >= 8); if (ImGui.Button("Add Vertex")) polygonCollider.VertexCount++; ImGui.EndDisabled(); ImGui.BeginDisabled(polygonCollider.VertexCount <= 3); if (ImGui.Button("Remove Vertex")) polygonCollider.VertexCount--; ImGui.EndDisabled(); float density = polygonCollider.Density; if (ImGui.DragFloat("Density", &density, 0.0f, 0.1f, textWidth)) polygonCollider.Density = density; float friction = polygonCollider.Friction; if (ImGui.DragFloat("Friction", &friction, 0.0f, 0.1f, textWidth)) polygonCollider.Friction = friction; float restitution = polygonCollider.Restitution; if (ImGui.DragFloat("Restitution", &restitution, 0.0f, 0.1f, textWidth)) polygonCollider.Restitution = restitution; float restitutionThreshold = polygonCollider.RestitutionThreshold; if (ImGui.DragFloat("RestitutionThreshold", &restitutionThreshold, 0.0f, 0.1f, textWidth)) polygonCollider.RestitutionThreshold = restitutionThreshold; } private static void ShowScriptComponentEditor(Entity entity, ScriptComponent* scriptComponent) { static char8[64] buffer = .(); StringView search = StringView(); char8* scriptLabel = scriptComponent.ScriptClassName.ToScopeCStr!() ?? "Select Script..."; if (ImGui.Button(scriptLabel)) ImGui.OpenPopup("SelectScript"); if (ImGui.BeginPopup("SelectScript")) { ImGui.InputText("##ScriptSearch", &buffer, buffer.Count); search = StringView(&buffer); for (let (className, scriptClass) in ScriptEngine.EntityClasses) { if (!search.IsWhiteSpace && !className.Contains(search, true)) continue; if (ImGui.Selectable(className.ToScopeCStr!(), className == scriptComponent.ScriptClassName)) { scriptComponent.ScriptClassName = scriptClass.FullName; ScriptEngine.CreateScriptFieldMap(entity); } } ImGui.EndPopup(); } ScriptEngine.ShowScriptEditor(entity, scriptComponent); } private static void ShowEnumSelector(ScriptFieldInstance* field, StringView fieldName, ScriptClass scriptClass) { ScriptField scriptField = scriptClass.Fields[fieldName]; SharpEnum enumType = scriptField.SharpType as SharpEnum; Log.EngineLogger.Assert(enumType != null, "Enum must have a SharpEnum!"); // Simply get Enum as a uint64 var fieldValue = field.GetData(); StringView valueName = ""; if (enumType.Values.TryGetValue(fieldValue, let enumValue)) valueName = enumValue.Name; if (ImGui.BeginCombo(fieldName.Ptr, valueName.Ptr)) { for (let (entryValue, enumEntry) in enumType.Values) { if (ImGui.Selectable(enumEntry.Name.Ptr, fieldValue == entryValue)) field.SetData(entryValue); } ImGui.EndCombo(); } } private static Entity? ShowEntitySelector() { static char8[128] entitySearch = .(); Entity? result = null; if (ImGui.BeginPopup("ENTITY_SELECTOR")) { ImGui.TextUnformatted("Search:"); ImGui.SameLine(); if (ImGui.InputText("##entitySearcher", &entitySearch, (uint64)entitySearch.Count)) { } ImGui.EndPopup(); } return result; } private static void ShowEntityReceiver(ScriptFieldInstance* field, StringView fieldName, ScriptClass scriptClass) { var entityId = field.GetData(); Result fieldEntity = Editor.Instance.CurrentScene.GetEntityByID(entityId); String entityName = scope .(32); if (entityId == UUID(0)) entityName.Set("None"); else if (fieldEntity case .Ok(Entity e)) entityName.Set(e.Name); else entityName..Clear().AppendF($"Missing entity ({entityId})"); ImGui.Text($"{fieldName}: "); ImGui.SameLine(); if (ImGui.Button(entityName)) { if (fieldEntity case .Ok) Editor.Instance.EntityHierarchyWindow.HighlightEntity(fieldEntity); } if (ImGui.BeginDragDropTarget()) { ImGui.Payload* peekPayload = ImGui.AcceptDragDropPayload(.Entity, .AcceptPeekOnly); bool allowDrop = false; if (peekPayload != null) { Entity draggedEntity = *(Entity*)peekPayload.Data; ScriptClass draggedScriptClass = ScriptEngine.[Friend]s_EntityRoot; if (draggedEntity.TryGetComponent(let draggedScript)) { var draggedClassName = draggedScript.ScriptClassName; draggedScriptClass = ScriptEngine.GetScriptClass(draggedClassName); } // TODO: I don't like the fact, that we are using mono directly ScriptField scriptField = scriptClass.Fields[fieldName]; var fieldMonoClass = Mono.mono_type_get_class(scriptField.GetMonoType()); allowDrop = draggedScriptClass.[Friend]IsSubclass(fieldMonoClass); } if (allowDrop) { ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.Entity); if (payload != null) { Log.EngineLogger.AssertDebug(payload.DataSize == sizeof(Entity)); Entity droppedEntity = *(Entity*)payload.Data; if (droppedEntity.IsValid) field.SetData(droppedEntity.UUID); } } ImGui.EndDragDropTarget(); } } //private static Entity? private static void ShowComponentReceiver(ScriptFieldInstance* field, StringView fieldName, ScriptClass scriptClass) { var entityId = field.GetData(); Result fieldEntity = Editor.Instance.CurrentScene.GetEntityByID(entityId); String entityName = scope .(32); if (entityId == UUID(0)) entityName.Set("None"); else if (fieldEntity case .Ok(Entity e)) entityName.Set(e.Name); else entityName..Clear().AppendF($"Missing reference ({entityId})"); ImGui.Text($"{fieldName}: "); ImGui.SameLine(); if (ImGui.Button(entityName)) { if (fieldEntity case .Ok) Editor.Instance.EntityHierarchyWindow.HighlightEntity(fieldEntity); } if (ImGui.BeginDragDropTarget()) { ImGui.Payload* peekPayload = ImGui.AcceptDragDropPayload(.Entity, .AcceptPeekOnly); bool allowDrop = false; if (peekPayload != null) { Entity draggedEntity = *(Entity*)peekPayload.Data; // TODO: I don't like the fact, that we are using mono directly ScriptField scriptField = scriptClass.Fields[fieldName]; // For some reason we have to retrieve the MonoType like this. Using scriptField.GetMonoType() directly returns the wrong type... MonoReflectionType* reflectionType = Mono.mono_type_get_object(ScriptEngine.[Friend]s_AppDomain, scriptField.GetMonoType()); MonoType* actualMonoType = Mono.mono_reflection_type_get_type(reflectionType); // TODO: We shouldn't abuse the script glue like that. // ScriptGlue should only be called by C#, not by Beef. if (ScriptGlue.[Friend]s_HasComponentMethods.TryGetValue(actualMonoType, let has_component)) { allowDrop = has_component(draggedEntity); } else { Log.EngineLogger.Warning($"No HasComponent-Function found for field {scriptField.Name}"); allowDrop = false; } } if (allowDrop) { ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.Entity); if (payload != null) { Log.EngineLogger.AssertDebug(payload.DataSize == sizeof(Entity)); Entity droppedEntity = *(Entity*)payload.Data; if (droppedEntity.IsValid) field.SetData(droppedEntity.UUID); } } ImGui.EndDragDropTarget(); } } private static void LabelColumn(StringView label) { ImGui.TextUnformatted(label); ImGui.NextColumn(); } private static void ShowLightComponentEditor(Entity entity, LightComponent* lightComponent) { ImGui.Columns(2); defer ImGui.Columns(1); const String[?] strings = String[]("Directional", "Point", "Spot"); var light = ref lightComponent.SceneLight; String typeName = strings[light.LightType.Underlying]; LabelColumn("Type"); if (ImGui.BeginCombo("##Type", typeName.CStr())) { for (int i = 0; i < 3; i++) { bool isSelected = (typeName == strings[i]); if (ImGui.Selectable(strings[i], isSelected)) { light.LightType = (.)i; } if (isSelected) ImGui.SetItemDefaultFocus(); } ImGui.EndCombo(); } ImGui.NextColumn(); LabelColumn("Color"); ColorRGB color = ColorRGB.LinearToSRGB(light.Color); if (ImGui.ColorEdit3("##Color", ref color)) light.Color = ColorRGB.SRgbToLinear(color); ImGui.NextColumn(); LabelColumn("Illuminance"); float illuminance = light.Illuminance; if (ImGui.DragFloat("##Illuminance", &illuminance, 0.1f, 0.0f, float.MaxValue)) light.Illuminance = illuminance; } private static void ShowMeshComponentEditor(Entity entity, MeshComponent* meshComponent) { ImGui.TextUnformatted("Mesh:"); ImGui.SameLine(); GeometryBinding mesh = meshComponent.Mesh; StringView identifier = mesh?.Identifier ?? (meshComponent.Mesh.IsValid ? "" : "None"); ImGui.Button(identifier.ToScopeCStr!()); if (ImGui.BeginDragDropTarget()) { ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem); if (payload != null) { StringView fullpath = .((char8*)payload.Data, (int)payload.DataSize); /*int idx = fullpath.IndexOf('#'); if (idx == -1) { // Doesn't make sense here, we NEED a sub asset Runtime.NotImplemented(); } StringView filePath = fullpath.Substring(0, idx); StringView meshName = fullpath.Substring(idx + 1);*/ meshComponent.Mesh = Content.LoadAsset(fullpath); // TODO: support multiple primitives (treat every primitive as a single mesh? or: mesh can have multiple primitives) /*using (GeometryBinding binding = ModelLoader.LoadMesh(filePath, meshName, 0)) { meshComponent.Mesh = binding; }*/ //ModelLoader.LoadModel(scope .(path), ) /*using (Texture2D newTexture = new Texture2D(path, true)) { newTexture.SamplerState = SamplerStateManager.AnisotropicWrap; material.SetTexture(texture.key, newTexture); }*/ } ImGui.EndDragDropTarget(); } } private static void ShowAddComponentButton(Entity entity) { static char8[128] searchBuffer = .(); static StringView searchFilter = .(); static float buttonWidth = 100; ImGui.NewLine(); ImGui.Separator(); ImGui.NewLine(); void ShowComponentButton(String name) where TComponent : struct, new { // If the entity already has this component, don't show the option to add it if (entity.HasComponent()) return; float textWidth = ImGui.CalcTextSize(name.CStr()).x; buttonWidth = Math.Max(buttonWidth, textWidth + ImGui.GetStyle().FramePadding.x * 2); if (name.Contains(searchFilter, true) && ImGui.Selectable(name)) { if (!entity.HasComponent()) entity.AddComponent(); } } float textWidth = ImGui.CalcTextSize("Add Component...").x; buttonWidth = Math.Max(buttonWidth, textWidth + ImGui.GetStyle().FramePadding.x * 2); ImGui.PushItemWidth(buttonWidth); ImGui.NewLine(); ImGui.SameLine(ImGui.GetContentRegionMax().x / 2 - buttonWidth / 2); if (ImGui.BeginCombo("##add_component_combo", "Add Component...", .NoArrowButton)) { if (ImGui.InputText("##search_component_name", &searchBuffer, (.)searchBuffer.Count)) { searchFilter = .(&searchBuffer); } ImGui.Separator(); ShowComponentButton("Camera"); ShowComponentButton("Sprite Renderer"); ShowComponentButton("Circle Renderer"); ShowComponentButton("Light"); ShowComponentButton("Rigidbody 2D"); ShowComponentButton("Box collider 2D"); ShowComponentButton("Circle collider 2D"); ShowComponentButton("Polygon collider 2D"); ShowComponentButton("Mesh"); ShowComponentButton("Mesh Renderer"); ShowComponentButton("C# Script"); ImGui.EndCombo(); } ImGui.PopItemWidth(); } } }