2D Polygon Collider

This commit is contained in:
Simon Lübeß
2023-08-13 12:03:06 +02:00
parent 4e5d3cfd64
commit 74b17c8599
5 changed files with 177 additions and 6 deletions
@@ -71,6 +71,7 @@ namespace GlitchyEditor.EditWindows
ShowComponentEditor<Rigidbody2DComponent>("Rigidbody 2D", entity, => ShowRigidBody2DComponentEditor, => ShowComponentContextMenu<Rigidbody2DComponent>);
ShowComponentEditor<BoxCollider2DComponent>("Box collider 2D", entity, => ShowBoxCollider2DComponentEditor, => ShowComponentContextMenu<BoxCollider2DComponent>);
ShowComponentEditor<CircleCollider2DComponent>("Circle collider 2D", entity, => ShowCircleCollider2DComponentEditor, => ShowComponentContextMenu<CircleCollider2DComponent>);
ShowComponentEditor<PolygonCollider2DComponent>("Polygon collider 2D", entity, => ShowPolygonCollider2DComponentEditor, => ShowComponentContextMenu<PolygonCollider2DComponent>);
ShowComponentEditor<ScriptComponent>("Script Component", entity, => ShowScriptComponentEditor, => ShowComponentContextMenu<ScriptComponent>);
ShowAddComponentButton(entity);
@@ -399,7 +400,7 @@ namespace GlitchyEditor.EditWindows
boxCollider.Offset = offset;
float2 size = boxCollider.Size;
if (ImGui.Editfloat2("Size", ref size, .Zero, 0.1f, textWidth))
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;
@@ -424,7 +425,6 @@ namespace GlitchyEditor.EditWindows
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;
@@ -450,6 +450,51 @@ namespace GlitchyEditor.EditWindows
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 = .();
@@ -1158,6 +1203,7 @@ namespace GlitchyEditor.EditWindows
ShowComponentButton<Rigidbody2DComponent>("Rigidbody 2D");
ShowComponentButton<BoxCollider2DComponent>("Box collider 2D");
ShowComponentButton<CircleCollider2DComponent>("Circle collider 2D");
ShowComponentButton<PolygonCollider2DComponent>("Polygon collider 2D");
ShowComponentButton<MeshComponent>("Mesh");
ShowComponentButton<MeshRendererComponent>("Mesh Renderer");
ShowComponentButton<ScriptComponent>("C# Script");
+21 -2
View File
@@ -405,12 +405,31 @@ namespace GlitchyEditor
for (var (entity, transform, collider) in _activeScene.GetEntities<TransformComponent, BoxCollider2DComponent>())
{
Renderer2D.DrawRect(transform.WorldTransform * Matrix.Translation(collider.Offset.X, collider.Offset.Y, 0) * Matrix.Scaling(collider.Size.X * 2, collider.Size.Y * 2, 0), .Green);
Renderer2D.DrawRect(transform.WorldTransform * Matrix.Translation(collider.Offset.X, collider.Offset.Y, 0) * Matrix.Scaling(collider.Size.X * 2, collider.Size.Y * 2, 0), .Green, entity.Index);
}
for (var (entity, transform, collider) in _activeScene.GetEntities<TransformComponent, CircleCollider2DComponent>())
{
Renderer2D.DrawCircle(transform.WorldTransform * Matrix.Translation(collider.Offset.X, collider.Offset.Y, 0) * Matrix.Scaling(collider.Radius * 2), (Texture2D)null, ColorRGBA(0f, 1f, 0f), 0.01f);
Renderer2D.DrawCircle(transform.WorldTransform * Matrix.Translation(collider.Offset.X, collider.Offset.Y, 0) * Matrix.Scaling(collider.Radius * 2), (Texture2D)null, ColorRGBA(0f, 1f, 0f), 0.01f, .(0, 0, 1, 1), entity.Index);
}
for (var (entity, transform, collider) in _activeScene.GetEntities<TransformComponent, PolygonCollider2DComponent>())
{
Matrix colliderTransform = transform.WorldTransform * Matrix.Translation(collider.Offset.X, collider.Offset.Y, 0);
float3 firstPosition = (colliderTransform * float4(collider.Vertices[0], 0, 1)).XYZ;
float3 lastPosition = firstPosition;
for (int i = 1; i < collider.VertexCount; i++)
{
float3 position = (colliderTransform * float4(collider.Vertices[i], 0, 1)).XYZ;
Renderer2D.DrawLine(lastPosition, position, .Green, entity.Index);
lastPosition = position;
}
Renderer2D.DrawLine(lastPosition, firstPosition, .Green, entity.Index);
}
}
@@ -505,6 +505,34 @@ namespace GlitchyEngine.World
internal ref b2Vec2 b2Offset mut => ref *(Box2D.b2Vec2*)(void*)&Offset;
}
// Todo: This component is rather large (> 1 Cache line)
struct PolygonCollider2DComponent
{
public float2 Offset = .(0.0f, 0.0f);
// 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;
public float2[8] Vertices = .();
public int8 VertexCount = 3;
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 ScriptComponent : IDisposableComponent
{
private String _scriptClassName = null;
+45 -2
View File
@@ -88,6 +88,7 @@ namespace GlitchyEngine.World
CopyComponents<Rigidbody2DComponent>(this, target);
CopyComponents<BoxCollider2DComponent>(this, target);
CopyComponents<CircleCollider2DComponent>(this, target);
CopyComponents<PolygonCollider2DComponent>(this, target);
// Copy ScriptComponents... needs extra handling for the script instances
for (let (sourceHandle, sourceComponent) in _ecsWorld.Enumerate<ScriptComponent>())
@@ -365,6 +366,13 @@ namespace GlitchyEngine.World
InitCircleCollider2D(entity, circleCollider);
}
for (let (ecsHandle, polygonCollider) in _ecsWorld.Enumerate<PolygonCollider2DComponent>())
{
Entity entity = .(ecsHandle, this);
InitPolygonCollider2D(entity, polygonCollider);
}
}
private void InitializeRigidbody2D(Entity entity, Rigidbody2DComponent* rigidBody)
@@ -426,8 +434,6 @@ namespace GlitchyEngine.World
let localToWorld = entity.Transform.WorldTransform;
let worldToRigidbody = entityWithRigidbody.Transform.WorldTransform.Invert();
let boxShape = Box2D.Shape.CreatePolygon();
Box2D.b2Vec2[4] points = .(
.(-1, -1),
.( 1, -1),
@@ -444,6 +450,7 @@ namespace GlitchyEngine.World
points[i] = (worldToRigidbody * localToWorld * float4(points[i], 0, 1)).XY;
}
let boxShape = Box2D.Shape.CreatePolygon();
Shape.PolygonSet(boxShape, &points, 4);
b2FixtureDef fixtureDef = .();
@@ -486,6 +493,42 @@ namespace GlitchyEngine.World
circleCollider.RuntimeFixture = fixture;
}
private void InitPolygonCollider2D(Entity entity, PolygonCollider2DComponent* polygonCollider)
{
if (FindParentWithRigidbody(entity, let entityWithRigidbody, let rigidbody, let parentToRigidbody) case .Err)
return;
let localToWorld = entity.Transform.WorldTransform;
let worldToRigidbody = entityWithRigidbody.Transform.WorldTransform.Invert();
Box2D.b2Vec2[8] points = .();
for (int i < polygonCollider.VertexCount)
{
points[i] = ((float2)polygonCollider.Vertices[i] + polygonCollider.Offset);
if (entity == entityWithRigidbody)
points[i] = entity.Transform.Scale.XY * (float2)points[i];
points[i] = (worldToRigidbody * localToWorld * float4(points[i], 0, 1)).XY;
}
let polygonShape = Box2D.Shape.CreatePolygon();
Shape.PolygonSet(polygonShape, &points, polygonCollider.VertexCount);
b2FixtureDef fixtureDef = .();
fixtureDef.shape = polygonShape;
fixtureDef.density = polygonCollider.Density;
fixtureDef.friction = polygonCollider.Friction;
fixtureDef.restitution = polygonCollider.Restitution;
fixtureDef.restitutionThreshold = polygonCollider.RestitutionThreshold;
fixtureDef.userData = (void*)(uint)entity.Handle;
Log.EngineLogger.AssertDebug(rigidbody.RuntimeBody != null);
b2Fixture* fixture = Box2D.Body.CreateFixture(rigidbody.RuntimeBody, &fixtureDef);
polygonCollider.RuntimeFixture = fixture;
}
public void OnSimulationStop()
{
Box2D.World.Delete(_physicsWorld2D);
@@ -177,6 +177,19 @@ class SceneSerializer
Serialize.Value(writer, "RestitutionThreshold", component.RestitutionThreshold);
});
SerializeComponent<PolygonCollider2DComponent>(writer, entity, "PolygonCollider2D", scope (component) =>
{
Serialize.Value(writer, "Offset", component.Offset);
Serialize.Value(writer, "Vertices", component.Vertices);
Serialize.Value(writer, "VertexCount", component.VertexCount);
Serialize.Value(writer, "Density", component.Density);
Serialize.Value(writer, "Friction", component.Friction);
Serialize.Value(writer, "Restitution", component.Restitution);
Serialize.Value(writer, "RestitutionThreshold", component.RestitutionThreshold);
});
SerializeComponent<MeshComponent>(writer, entity, "MeshComponent", scope (component) =>
{
Serialize.Value(writer, "Mesh", component.Mesh);
@@ -526,6 +539,28 @@ class SceneSerializer
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;
}));
case "PolygonCollider2D":
Try!(DeserializeComponent<PolygonCollider2DComponent>(reader, entity, scope (component) =>
{
Deserialize.Value(reader, "Offset", out component.Offset);
reader.EntryEnd();
Deserialize.Value(reader, "Vertices", out component.Vertices);
reader.EntryEnd();
Deserialize.Value(reader, "VertexCount", out component.VertexCount);
reader.EntryEnd();
Deserialize.Value(reader, "Density", out component.Density);
reader.EntryEnd();
Deserialize.Value(reader, "Friction", out component.Friction);