From e67e312d2818d5d0e9d34e41c685a493e196114c Mon Sep 17 00:00:00 2001 From: emil28092005 Date: Thu, 18 Jun 2026 17:18:59 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20Jolt=20physics=20=E2=80=94=20cubes=20an?= =?UTF-8?q?d=20spheres=20fall=20with=20gravity,=20hit=20floor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- src/CortexEngine.App/CortexEngine.App.csproj | 1 + src/CortexEngine.App/Program.cs | 64 ++++++++++---------- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/CortexEngine.App/CortexEngine.App.csproj b/src/CortexEngine.App/CortexEngine.App.csproj index 51de3c3..48ecb7f 100644 --- a/src/CortexEngine.App/CortexEngine.App.csproj +++ b/src/CortexEngine.App/CortexEngine.App.csproj @@ -23,6 +23,7 @@ + diff --git a/src/CortexEngine.App/Program.cs b/src/CortexEngine.App/Program.cs index d2211d3..516196f 100644 --- a/src/CortexEngine.App/Program.cs +++ b/src/CortexEngine.App/Program.cs @@ -4,6 +4,7 @@ using Engine.Core.Components; using Engine.Graphics; using Engine.Graphics.Loaders; using Engine.Graphics.Vulkan; +using Engine.Physics; using Flecs.NET.Core; namespace CortexEngine.App; @@ -12,7 +13,7 @@ class Program { 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 { @@ -23,10 +24,11 @@ class Program using var renderer = renderContext.CreateRenderer(); using var world = World.Create(); + using var physicsWorld = new PhysicsWorld(); var cameraEntity = world.Entity("Camera") .Set(new Camera( - new Vector3(0, 3, -12), + new Vector3(0, 5, -15), new Vector3(0, 0.5f, 0), Vector3.UnitY, MathF.PI / 4f, @@ -44,7 +46,6 @@ class Program var frames = 0; var lastFpsTime = 0.0; var timing = new Timing(); - var totalTime = 0.0f; while (!window.ShouldClose) { @@ -64,28 +65,23 @@ class Program cameraController.Update(input, (float)timing.DeltaTime); - totalTime += (float)timing.DeltaTime; - var angle = totalTime * 0.3f; - - var torusEntity = world.Lookup("TorusKnot"); - if ((ulong)torusEntity.Id != 0) + var toInit = new List<(Entity, RigidBody, Transform)>(); + world.Each((Entity e, ref RigidBody rb, ref Transform t) => { - var t = torusEntity.Get(); - t.Rotation = Quaternion.CreateFromAxisAngle(Vector3.UnitY, angle); - torusEntity.Set(t); - } + if (!rb.IsInitialized) + toInit.Add((e, rb, 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")}"); - if ((ulong)cubeEntity.Id != 0) - { - var t = cubeEntity.Get(); - t.Rotation = Quaternion.CreateFromAxisAngle(Vector3.UnitY, angle * (1f + i * 0.3f)); - cubeEntity.Set(t); - } + var rbInit = rb; + rbInit.IsInitialized = true; + e.Set(rbInit); + physicsWorld.CreateBody(e, rbInit, t); } + physicsWorld.Update((float)timing.DeltaTime); + physicsWorld.SyncTransforms(world); renderer.RenderWorld(world); frames++; @@ -111,20 +107,19 @@ class Program static void CreateScene(World world) { 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 grid = ProceduralMesh.CreateGrid(20, 1.0f, new Vector3(0.4f, 0.4f, 0.45f)); 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); 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)), - ("CubeRight", new(5, 1, 0), 1.5f, new(0.2f, 0.8f, 0.3f)), - ("CubeFront", new(0, 1, 5), 1.5f, new(0.2f, 0.4f, 0.9f)), - ("CubeBack", new(0, 1, -5), 1.5f, new(0.9f, 0.9f, 0.2f)), + ("CubeLeft", new(-5, 5, 0), 1.5f, new(0.8f, 0.2f, 0.2f)), + ("CubeRight", new(5, 8, 0), 1.5f, new(0.2f, 0.8f, 0.3f)), + ("CubeFront", new(0, 6, 5), 1.5f, new(0.2f, 0.4f, 0.9f)), + ("CubeBack", new(0, 10, -5), 1.5f, new(0.9f, 0.9f, 0.2f)), }; foreach (var (name, pos, scale, color) in cubes) @@ -132,26 +127,29 @@ class Program var c = LoadMesh("Content/cube.obj", color); world.Entity(name) .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)[] { - ("Sphere1", new(-3, 3, -2)), - ("Sphere2", new(3, 3, 2)), - ("Sphere3", new(-2, 4, 3)), + ("Sphere1", new(-3, 7, -2)), + ("Sphere2", new(3, 9, 2)), + ("Sphere3", new(-2, 12, 3)), }; foreach (var (name, pos) in spheres) { world.Entity(name) .Set(new Transform(pos, Quaternion.Identity, Vector3.One)) - .Set(sphere); + .Set(sphere) + .Set(RigidBody.DynamicSphere(0.7f, mass: 1.5f)); } world.Entity("Floor") .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") .Set(new Transform(Vector3.Zero, Quaternion.Identity, Vector3.One)) @@ -159,7 +157,7 @@ class Program var entityCount = 0; 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)