diff --git a/GlitchyEditor/content/Scenes/physics2D.scene b/GlitchyEditor/content/Scenes/physics2D.scene index af77592..4922e7c 100644 --- a/GlitchyEditor/content/Scenes/physics2D.scene +++ b/GlitchyEditor/content/Scenes/physics2D.scene @@ -18,7 +18,8 @@ Y = 0, Z = 1, W = 1 - } + }, + IsCircle = false }, TransformComponent = { Position = { @@ -79,7 +80,8 @@ Y = 0, Z = 1, W = 1 - } + }, + IsCircle = false }, TransformComponent = { Position = { @@ -160,9 +162,68 @@ OrthographicHeight = 10, OrthographicNearPlane = 0, OrthographicFarPlane = 10, - AspectRatio = 2.864979, + AspectRatio = 2.01173, FixedAspectRatio = false } + }, + { + Id = 15710354273720487680, + NameComponent = { + Name = "Circle" + }, + SpriterRendererComponent = { + Color = { + R = 1, + G = 0, + B = 0, + A = 1 + }, + UvTransform = { + X = 0, + Y = 0, + Z = 1, + W = 1 + }, + IsCircle = true + }, + TransformComponent = { + Position = { + X = -0.121203, + Y = 0.66016, + Z = 0 + }, + Rotation = { + X = 0, + Y = 0, + Z = 0, + W = 1 + }, + Scale = { + X = 1, + Y = 1, + Z = 1 + }, + EditorEulerRotation = { + X = 0, + Y = 0, + Z = 0 + } + }, + Rigidbody2D = { + BodyType = .Dynamic, + FixedRotation = false + }, + CircleCollider2D = { + Offset = { + X = 0, + Y = 0 + }, + Radius = 0.5, + Density = 1, + Friction = 0.5, + Restitution = 0, + RestitutionThreshold = 0.5 + } } ] } \ No newline at end of file diff --git a/GlitchyEditor/content/Shaders/circlebatch.hlsl b/GlitchyEditor/content/Shaders/circlebatch.hlsl index 6a51598..206adc7 100644 --- a/GlitchyEditor/content/Shaders/circlebatch.hlsl +++ b/GlitchyEditor/content/Shaders/circlebatch.hlsl @@ -1,3 +1,5 @@ +#define EDITOR + Texture2D Texture : register(t0); SamplerState Sampler : register(s0); @@ -14,6 +16,9 @@ struct VS_Input float4 Color : COLOR; float4 UVTransform : TEXCOORD1; float InnerRadius : TEXCOORD2; +#ifdef EDITOR + uint EntityId : ENTITYID; +#endif }; struct PS_Input @@ -22,6 +27,9 @@ struct PS_Input float2 RawPos : TEXCOORD0; float2 Texcoord : TEXCOORD1; float4 Color : COLOR; +#ifdef EDITOR + nointerpolation uint EntityId : ENTITYID; +#endif float InnerRadius : TEXCOORD2; }; @@ -35,11 +43,25 @@ PS_Input VS(VS_Input input) output.Color = input.Color; output.InnerRadius = input.InnerRadius; +#ifdef EDITOR + output.EntityId = input.EntityId; +#endif + return output; } -float4 PS(PS_Input input) : SV_Target0 +struct PS_Output { + float4 Color : SV_Target0; +#ifdef EDITOR + uint EntityId : SV_TARGET1; +#endif +}; + +PS_Output PS(PS_Input input) +{ + PS_Output output; + float2 uv = input.RawPos * 2; float distance = 1.0f - length(uv); @@ -53,10 +75,14 @@ float4 PS(PS_Input input) : SV_Target0 // Discard invisible pixels clip(amount - 0.5f); - float4 color = Texture.Sample(Sampler, input.Texcoord) * input.Color; - color.a *= amount; + output.Color = Texture.Sample(Sampler, input.Texcoord) * input.Color; + output.Color.a *= amount; - return color; +#ifdef EDITOR + output.EntityId = input.EntityId; +#endif + + return output; } #pragma Effect[VS=VS; PS=PS] \ No newline at end of file diff --git a/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf b/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf index 7abacdd..9307e55 100644 --- a/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf +++ b/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf @@ -66,6 +66,7 @@ namespace GlitchyEditor.EditWindows 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); ShowAddComponentButton(entity); } @@ -278,6 +279,8 @@ namespace GlitchyEditor.EditWindows ImGui.EditVector<4>("UV Transform", ref *(float[4]*)&spriteRendererComponent.UvTransform); + + ImGui.Checkbox("Is Circle", &spriteRendererComponent.IsCircle); } private static void ShowMeshRendererComponentEditor(Entity entity, MeshRendererComponent* meshRendererComponent) @@ -471,6 +474,37 @@ namespace GlitchyEditor.EditWindows 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; + + + Vector2 offset = circleCollider.Offset; + if (ImGui.EditVector2("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 LabelColumn(StringView label) { ImGui.TextUnformatted(label); @@ -613,6 +647,7 @@ namespace GlitchyEditor.EditWindows ShowComponentButton("Light"); ShowComponentButton("Rigidbody 2D"); ShowComponentButton("Box collider 2D"); + ShowComponentButton("Circle collider 2D"); ImGui.EndCombo(); } diff --git a/GlitchyEngine/src/Renderer/Renderer2D.bf b/GlitchyEngine/src/Renderer/Renderer2D.bf index 1fefa3a..d5ac7e4 100644 --- a/GlitchyEngine/src/Renderer/Renderer2D.bf +++ b/GlitchyEngine/src/Renderer/Renderer2D.bf @@ -236,8 +236,8 @@ namespace GlitchyEngine.Renderer VertexElement(.R32G32B32A32_Float, "TRANSFORM", false, 3, 1, (.)-1, .PerInstanceData, 1), VertexElement(.R32G32B32A32_Float, "COLOR", false, 0, 1, (.)-1, .PerInstanceData, 1), VertexElement(.R32G32B32A32_Float, "TEXCOORD", false, 1, 1, (.)-1, .PerInstanceData, 1), - VertexElement(.R32_Float, "TEXCOORD", false, 2, 1, (.)-1, .PerInstanceData, 1), - VertexElement(.R32_UInt, "ENTITYID", false, 0, 1, (.)-1, .PerInstanceData, 1) + VertexElement(.R32_UInt, "ENTITYID", false, 0, 1, (.)-1, .PerInstanceData, 1), + VertexElement(.R32_Float, "TEXCOORD", false, 2, 1, (.)-1, .PerInstanceData, 1) ); s_circleBatchBinding = new GeometryBinding(); @@ -873,7 +873,10 @@ namespace GlitchyEngine.Renderer public static void DrawSprite(Matrix transform, SpriterRendererComponent* spriteRenderer, uint32 entityId) { - DrawQuad(transform, spriteRenderer.Sprite ?? s_whiteTexture, spriteRenderer.Color, spriteRenderer.UvTransform, entityId); + if (spriteRenderer.IsCircle) + DrawCircle(transform, spriteRenderer.Sprite ?? s_whiteTexture, spriteRenderer.Color, 1.0f, spriteRenderer.UvTransform, entityId); + else + DrawQuad(transform, spriteRenderer.Sprite ?? s_whiteTexture, spriteRenderer.Color, spriteRenderer.UvTransform, entityId); } // Textured quad pivot @@ -912,7 +915,7 @@ namespace GlitchyEngine.Renderer DrawCircle(transform, texture, color, innerRadius, uvTransform); } - public static void DrawCircle(Matrix transform, Texture2D texture, ColorRGBA color = .White, float innerRadius = 1.0f, Vector4 uvTransform = .(0, 0, 1, 1)) + public static void DrawCircle(Matrix transform, Texture2D texture, ColorRGBA color = .White, float innerRadius = 1.0f, Vector4 uvTransform = .(0, 0, 1, 1), uint32 entityId = uint32.MaxValue) { Debug.Profiler.ProfileRendererFunction!(); @@ -920,7 +923,7 @@ namespace GlitchyEngine.Renderer Log.EngineLogger.AssertDebug(s_sceneRunning, "Missing call of BeginScene."); #endif - QueueCircleInstance(transform, color, texture, transform.Translation.Z, uvTransform, innerRadius); + QueueCircleInstance(transform, color, texture, transform.Translation.Z, uvTransform, innerRadius, entityId); if(s_drawOrder == .Immediate) { diff --git a/GlitchyEngine/src/World/Components.bf b/GlitchyEngine/src/World/Components.bf index 05c7788..4acece6 100644 --- a/GlitchyEngine/src/World/Components.bf +++ b/GlitchyEngine/src/World/Components.bf @@ -2,6 +2,7 @@ using System; using GlitchyEngine.Math; using GlitchyEngine.Renderer; using GlitchyEngine.Core; +using Box2D; namespace GlitchyEngine.World { @@ -49,6 +50,8 @@ namespace GlitchyEngine.World public ColorRGBA Color = .White; public Vector4 UvTransform = .(0, 0, 1, 1); + public bool IsCircle = false; + public this() { } @@ -363,7 +366,15 @@ namespace GlitchyEngine.World public bool FixedRotation = false; - internal int _runtimeBody = 0; + private int _runtimeBody = 0; + + internal b2Body* RuntimeBody + { + [Inline] + get => (b2Body*)(void*)_runtimeBody; + [Inline] + set mut => _runtimeBody = (int)(void*)value; + } } struct BoxCollider2DComponent @@ -377,6 +388,43 @@ namespace GlitchyEngine.World public float Restitution = 0.0f; public float RestitutionThreshold = 0.5f; - internal int _runtimeFixture = 0; + private int _runtimeFixture = 0; + + internal b2Fixture* RuntimeFixture + { + [Inline] + get => (b2Fixture*)(void*)_runtimeFixture; + [Inline] + set mut => _runtimeFixture = (int)(void*)value; + } + + [Inline] + internal ref b2Vec2 b2Offset mut => ref *(Box2D.b2Vec2*)(void*)&Offset; + } + + struct CircleCollider2DComponent + { + public Vector2 Offset = .(0.0f, 0.0f); + + public float Radius = 0.5f; + + // TODO: move into 2D physics material + public float Density = 1.0f; + public float Friction = 0.5f; + public float Restitution = 0.0f; + public float RestitutionThreshold = 0.5f; + + private int _runtimeFixture = 0; + + internal b2Fixture* RuntimeFixture + { + [Inline] + get => (b2Fixture*)(void*)_runtimeFixture; + [Inline] + set mut => _runtimeFixture = (int)(void*)value; + } + + [Inline] + internal ref b2Vec2 b2Offset mut => ref *(Box2D.b2Vec2*)(void*)&Offset; } } \ No newline at end of file diff --git a/GlitchyEngine/src/World/Scene.bf b/GlitchyEngine/src/World/Scene.bf index 30deaee..fc78dd4 100644 --- a/GlitchyEngine/src/World/Scene.bf +++ b/GlitchyEngine/src/World/Scene.bf @@ -121,12 +121,13 @@ namespace GlitchyEngine.World b2Body* body = Box2D.World.CreateBody(_physicsWorld2D, &def); Box2D.Body.SetFixedRotation(body, rigidBody.FixedRotation); - rigidBody._runtimeBody = (int)(void*)body; + rigidBody.RuntimeBody = body; if (entity.TryGetComponent(let boxCollider)) { b2Shape* boxShape = Box2D.Shape.CreatePolygon(); Box2D.Shape.PolygonSetAsBox(boxShape, boxCollider.Size.X * transform.Scale.X, boxCollider.Size.Y * transform.Scale.Y); + Box2D.Shape.PolygonSetAsBoxWithCenterAngle(boxShape, boxCollider.Size.X * transform.Scale.X, boxCollider.Size.Y * transform.Scale.Y, ref boxCollider.b2Offset, 0.0f); b2FixtureDef fixtureDef = .(); fixtureDef.shape = boxShape; @@ -136,7 +137,24 @@ namespace GlitchyEngine.World fixtureDef.restitutionThreshold = boxCollider.RestitutionThreshold; b2Fixture* fixture = Box2D.Body.CreateFixture(body, &fixtureDef); - boxCollider._runtimeFixture = (int)(void*)fixture; + boxCollider.RuntimeFixture = fixture; + } + + if (entity.TryGetComponent(let circleCollider)) + { + b2Shape* circleShape = Box2D.Shape.CreateCircle(); + Box2D.Shape.CircleSetPosition(circleShape, ref circleCollider.b2Offset); + Box2D.Shape.SetRadius(circleShape, circleCollider.Radius); + + b2FixtureDef fixtureDef = .(); + fixtureDef.shape = circleShape; + fixtureDef.density = circleCollider.Density; + fixtureDef.friction = circleCollider.Friction; + fixtureDef.restitution = circleCollider.Restitution; + fixtureDef.restitutionThreshold = circleCollider.RestitutionThreshold; + + b2Fixture* fixture = Box2D.Body.CreateFixture(body, &fixtureDef); + circleCollider.RuntimeFixture = fixture; } } } @@ -184,7 +202,7 @@ namespace GlitchyEngine.World var transform = entity.Transform; var rigidbody = entry.Component; - b2Body* body = (b2Body*)(void*)rigidbody._runtimeBody; + b2Body* body = rigidbody.RuntimeBody; b2Vec2 position = Box2D.Body.GetPosition(body); float angle = Box2D.Body.GetAngle(body); diff --git a/GlitchyEngine/src/World/SceneSerializer.bf b/GlitchyEngine/src/World/SceneSerializer.bf index 3bf9e51..49780d1 100644 --- a/GlitchyEngine/src/World/SceneSerializer.bf +++ b/GlitchyEngine/src/World/SceneSerializer.bf @@ -88,6 +88,7 @@ namespace GlitchyEngine.World Serialize.Value(writer, "Color", component.Color); Serialize.Value(writer, "UvTransform", component.UvTransform); + Serialize.Value(writer, "IsCircle", component.IsCircle); }); SerializeComponent(writer, entity, "TransformComponent", scope (component) => @@ -156,6 +157,17 @@ namespace GlitchyEngine.World Serialize.Value(writer, "Restitution", component.Restitution); Serialize.Value(writer, "RestitutionThreshold", component.RestitutionThreshold); }); + + SerializeComponent(writer, entity, "CircleCollider2D", scope (component) => + { + Serialize.Value(writer, "Offset", component.Offset); + Serialize.Value(writer, "Radius", component.Radius); + + Serialize.Value(writer, "Density", component.Density); + Serialize.Value(writer, "Friction", component.Friction); + Serialize.Value(writer, "Restitution", component.Restitution); + Serialize.Value(writer, "RestitutionThreshold", component.RestitutionThreshold); + }); } writer.EntryEnd(); @@ -297,6 +309,8 @@ namespace GlitchyEngine.World Try!(Deserialize.Value(reader, "Color", out component.Color)); reader.EntryEnd(); Try!(Deserialize.Value(reader, "UvTransform", out component.UvTransform)); + reader.EntryEnd(); + Try!(Deserialize.Value(reader, "IsCircle", out component.IsCircle)); return .Ok; })); @@ -419,6 +433,24 @@ namespace GlitchyEngine.World reader.EntryEnd(); Deserialize.Value(reader, "RestitutionThreshold", out component.RestitutionThreshold); + return .Ok; + })); + case "CircleCollider2D": + Try!(DeserializeComponent(reader, entity, scope (component) => + { + Deserialize.Value(reader, "Offset", out component.Offset); + reader.EntryEnd(); + Deserialize.Value(reader, "Radius", out component.Radius); + reader.EntryEnd(); + + Deserialize.Value(reader, "Density", out component.Density); + reader.EntryEnd(); + Deserialize.Value(reader, "Friction", out component.Friction); + reader.EntryEnd(); + Deserialize.Value(reader, "Restitution", out component.Restitution); + reader.EntryEnd(); + Deserialize.Value(reader, "RestitutionThreshold", out component.RestitutionThreshold); + return .Ok; }));