feat: Jolt physics — cubes and spheres fall with gravity, hit floor
- Engine.Physics (JoltPhysicsSharp) integrated into CortexEngine.App - 4 cubes: DynamicBox, mass proportional to scale, fall from height 5-10 - 3 spheres: DynamicSphere, mass 1.5, fall from height 7-12 - Floor: StaticBox (20x0.5x20), friction 0.8 - Torus knot + grid: no physics (visual only) - Physics bodies initialized lazily on first frame (IsInitialized flag) - e.Set() moved outside world.Each to avoid mutation during iteration - physicsWorld.Update + SyncTransforms every frame - Removed manual rotation — physics handles movement now - Camera at (0, 5, -15) for better view of falling objects - 0 validation errors, ~1300 FPS
This commit is contained in:
@@ -23,6 +23,7 @@
|
|||||||
<ProjectReference Include="..\Engine.Core\Engine.Core.csproj" />
|
<ProjectReference Include="..\Engine.Core\Engine.Core.csproj" />
|
||||||
<ProjectReference Include="..\Engine.Graphics\Engine.Graphics.csproj" />
|
<ProjectReference Include="..\Engine.Graphics\Engine.Graphics.csproj" />
|
||||||
<ProjectReference Include="..\Engine.Graphics.Vulkan\Engine.Graphics.Vulkan.csproj" />
|
<ProjectReference Include="..\Engine.Graphics.Vulkan\Engine.Graphics.Vulkan.csproj" />
|
||||||
|
<ProjectReference Include="..\Engine.Physics\Engine.Physics.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Engine.Core.Components;
|
|||||||
using Engine.Graphics;
|
using Engine.Graphics;
|
||||||
using Engine.Graphics.Loaders;
|
using Engine.Graphics.Loaders;
|
||||||
using Engine.Graphics.Vulkan;
|
using Engine.Graphics.Vulkan;
|
||||||
|
using Engine.Physics;
|
||||||
using Flecs.NET.Core;
|
using Flecs.NET.Core;
|
||||||
|
|
||||||
namespace CortexEngine.App;
|
namespace CortexEngine.App;
|
||||||
@@ -12,7 +13,7 @@ class Program
|
|||||||
{
|
{
|
||||||
static async Task Main(string[] args)
|
static async Task Main(string[] args)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Cortex Engine — Vulkan Scene (pure P/Invoke)...");
|
Console.WriteLine("Cortex Engine — Vulkan Scene + Physics (pure P/Invoke)...");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -23,10 +24,11 @@ class Program
|
|||||||
using var renderer = renderContext.CreateRenderer();
|
using var renderer = renderContext.CreateRenderer();
|
||||||
|
|
||||||
using var world = World.Create();
|
using var world = World.Create();
|
||||||
|
using var physicsWorld = new PhysicsWorld();
|
||||||
|
|
||||||
var cameraEntity = world.Entity("Camera")
|
var cameraEntity = world.Entity("Camera")
|
||||||
.Set(new Camera(
|
.Set(new Camera(
|
||||||
new Vector3(0, 3, -12),
|
new Vector3(0, 5, -15),
|
||||||
new Vector3(0, 0.5f, 0),
|
new Vector3(0, 0.5f, 0),
|
||||||
Vector3.UnitY,
|
Vector3.UnitY,
|
||||||
MathF.PI / 4f,
|
MathF.PI / 4f,
|
||||||
@@ -44,7 +46,6 @@ class Program
|
|||||||
var frames = 0;
|
var frames = 0;
|
||||||
var lastFpsTime = 0.0;
|
var lastFpsTime = 0.0;
|
||||||
var timing = new Timing();
|
var timing = new Timing();
|
||||||
var totalTime = 0.0f;
|
|
||||||
|
|
||||||
while (!window.ShouldClose)
|
while (!window.ShouldClose)
|
||||||
{
|
{
|
||||||
@@ -64,28 +65,23 @@ class Program
|
|||||||
|
|
||||||
cameraController.Update(input, (float)timing.DeltaTime);
|
cameraController.Update(input, (float)timing.DeltaTime);
|
||||||
|
|
||||||
totalTime += (float)timing.DeltaTime;
|
var toInit = new List<(Entity, RigidBody, Transform)>();
|
||||||
var angle = totalTime * 0.3f;
|
world.Each((Entity e, ref RigidBody rb, ref Transform t) =>
|
||||||
|
|
||||||
var torusEntity = world.Lookup("TorusKnot");
|
|
||||||
if ((ulong)torusEntity.Id != 0)
|
|
||||||
{
|
{
|
||||||
var t = torusEntity.Get<Transform>();
|
if (!rb.IsInitialized)
|
||||||
t.Rotation = Quaternion.CreateFromAxisAngle(Vector3.UnitY, angle);
|
toInit.Add((e, rb, t));
|
||||||
torusEntity.Set(t);
|
});
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < 4; i++)
|
foreach (var (e, rb, t) in toInit)
|
||||||
{
|
{
|
||||||
var cubeEntity = world.Lookup($"Cube{(i == 0 ? "Left" : i == 1 ? "Right" : i == 2 ? "Front" : "Back")}");
|
var rbInit = rb;
|
||||||
if ((ulong)cubeEntity.Id != 0)
|
rbInit.IsInitialized = true;
|
||||||
{
|
e.Set(rbInit);
|
||||||
var t = cubeEntity.Get<Transform>();
|
physicsWorld.CreateBody(e, rbInit, t);
|
||||||
t.Rotation = Quaternion.CreateFromAxisAngle(Vector3.UnitY, angle * (1f + i * 0.3f));
|
|
||||||
cubeEntity.Set(t);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
physicsWorld.Update((float)timing.DeltaTime);
|
||||||
|
physicsWorld.SyncTransforms(world);
|
||||||
renderer.RenderWorld(world);
|
renderer.RenderWorld(world);
|
||||||
|
|
||||||
frames++;
|
frames++;
|
||||||
@@ -111,20 +107,19 @@ class Program
|
|||||||
static void CreateScene(World world)
|
static void CreateScene(World world)
|
||||||
{
|
{
|
||||||
var torusKnot = LoadMesh("Content/torusknot.obj", new Vector3(0.9f, 0.7f, 0.3f));
|
var torusKnot = LoadMesh("Content/torusknot.obj", new Vector3(0.9f, 0.7f, 0.3f));
|
||||||
var cube = LoadMesh("Content/cube.obj", new Vector3(0.8f, 0.5f, 0.2f));
|
|
||||||
var sphere = ProceduralMesh.CreateSphere(0.7f, 32, 16, new Vector3(0.3f, 0.6f, 0.9f));
|
var sphere = ProceduralMesh.CreateSphere(0.7f, 32, 16, new Vector3(0.3f, 0.6f, 0.9f));
|
||||||
var grid = ProceduralMesh.CreateGrid(20, 1.0f, new Vector3(0.4f, 0.4f, 0.45f));
|
var grid = ProceduralMesh.CreateGrid(20, 1.0f, new Vector3(0.4f, 0.4f, 0.45f));
|
||||||
|
|
||||||
world.Entity("TorusKnot")
|
world.Entity("TorusKnot")
|
||||||
.Set(new Transform(new Vector3(0, 1.5f, 0), Quaternion.Identity, new Vector3(1.5f)))
|
.Set(new Transform(new Vector3(0, 4f, 0), Quaternion.Identity, new Vector3(1.5f)))
|
||||||
.Set(torusKnot);
|
.Set(torusKnot);
|
||||||
|
|
||||||
var cubes = new (string name, Vector3 pos, float scale, Vector3 color)[]
|
var cubes = new (string name, Vector3 pos, float scale, Vector3 color)[]
|
||||||
{
|
{
|
||||||
("CubeLeft", new(-5, 1, 0), 1.5f, new(0.8f, 0.2f, 0.2f)),
|
("CubeLeft", new(-5, 5, 0), 1.5f, new(0.8f, 0.2f, 0.2f)),
|
||||||
("CubeRight", new(5, 1, 0), 1.5f, new(0.2f, 0.8f, 0.3f)),
|
("CubeRight", new(5, 8, 0), 1.5f, new(0.2f, 0.8f, 0.3f)),
|
||||||
("CubeFront", new(0, 1, 5), 1.5f, new(0.2f, 0.4f, 0.9f)),
|
("CubeFront", new(0, 6, 5), 1.5f, new(0.2f, 0.4f, 0.9f)),
|
||||||
("CubeBack", new(0, 1, -5), 1.5f, new(0.9f, 0.9f, 0.2f)),
|
("CubeBack", new(0, 10, -5), 1.5f, new(0.9f, 0.9f, 0.2f)),
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var (name, pos, scale, color) in cubes)
|
foreach (var (name, pos, scale, color) in cubes)
|
||||||
@@ -132,26 +127,29 @@ class Program
|
|||||||
var c = LoadMesh("Content/cube.obj", color);
|
var c = LoadMesh("Content/cube.obj", color);
|
||||||
world.Entity(name)
|
world.Entity(name)
|
||||||
.Set(new Transform(pos, Quaternion.Identity, new Vector3(scale)))
|
.Set(new Transform(pos, Quaternion.Identity, new Vector3(scale)))
|
||||||
.Set(c);
|
.Set(c)
|
||||||
|
.Set(RigidBody.DynamicBox(new Vector3(scale * 0.5f), mass: scale * 2f));
|
||||||
}
|
}
|
||||||
|
|
||||||
var spheres = new (string name, Vector3 pos)[]
|
var spheres = new (string name, Vector3 pos)[]
|
||||||
{
|
{
|
||||||
("Sphere1", new(-3, 3, -2)),
|
("Sphere1", new(-3, 7, -2)),
|
||||||
("Sphere2", new(3, 3, 2)),
|
("Sphere2", new(3, 9, 2)),
|
||||||
("Sphere3", new(-2, 4, 3)),
|
("Sphere3", new(-2, 12, 3)),
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var (name, pos) in spheres)
|
foreach (var (name, pos) in spheres)
|
||||||
{
|
{
|
||||||
world.Entity(name)
|
world.Entity(name)
|
||||||
.Set(new Transform(pos, Quaternion.Identity, Vector3.One))
|
.Set(new Transform(pos, Quaternion.Identity, Vector3.One))
|
||||||
.Set(sphere);
|
.Set(sphere)
|
||||||
|
.Set(RigidBody.DynamicSphere(0.7f, mass: 1.5f));
|
||||||
}
|
}
|
||||||
|
|
||||||
world.Entity("Floor")
|
world.Entity("Floor")
|
||||||
.Set(new Transform(new Vector3(0, -0.5f, 0), Quaternion.Identity, new Vector3(20, 0.5f, 20)))
|
.Set(new Transform(new Vector3(0, -0.5f, 0), Quaternion.Identity, new Vector3(20, 0.5f, 20)))
|
||||||
.Set(LoadMesh("Content/cube.obj", new Vector3(0.3f, 0.3f, 0.35f)));
|
.Set(LoadMesh("Content/cube.obj", new Vector3(0.3f, 0.3f, 0.35f)))
|
||||||
|
.Set(RigidBody.StaticBox(new Vector3(20, 0.5f, 20)));
|
||||||
|
|
||||||
world.Entity("Grid")
|
world.Entity("Grid")
|
||||||
.Set(new Transform(Vector3.Zero, Quaternion.Identity, Vector3.One))
|
.Set(new Transform(Vector3.Zero, Quaternion.Identity, Vector3.One))
|
||||||
@@ -159,7 +157,7 @@ class Program
|
|||||||
|
|
||||||
var entityCount = 0;
|
var entityCount = 0;
|
||||||
world.Each((Entity e, ref Transform _) => entityCount++);
|
world.Each((Entity e, ref Transform _) => entityCount++);
|
||||||
Console.WriteLine($"[Scene] {entityCount} entities: torus knot + 4 cubes + 3 spheres + floor + grid");
|
Console.WriteLine($"[Scene] {entityCount} entities: torus knot + 4 dynamic cubes + 3 dynamic spheres + static floor + grid");
|
||||||
}
|
}
|
||||||
|
|
||||||
static Mesh LoadMesh(string path, Vector3 color)
|
static Mesh LoadMesh(string path, Vector3 color)
|
||||||
|
|||||||
Reference in New Issue
Block a user