feat: Step 4 load .obj and glTF models into ECS
- Add Vertex struct and Mesh ECS component (vertices + indices). - Add Vulkan IndexBuffer for indexed draws. - Add MeshRenderer that draws Mesh + Transform entities with vkCmdDrawIndexed. - Add minimal .obj loader (positions, faces) and glTF/glTF-binary loader via SharpGLTF.Core. - Rewrite shaders to 3D position + color; recompile to SPIR-V. - Update VulkanPipeline for new vertex format. - Ship a sample Content/cube.obj and wire Program.cs to load it. - Remove TriangleRenderer (replaced by MeshRenderer).
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using Engine.Core;
|
||||
using Engine.Core.Components;
|
||||
using Engine.Graphics;
|
||||
using Engine.Graphics.Loaders;
|
||||
using Flecs.NET.Core;
|
||||
|
||||
namespace CortexEngine.App;
|
||||
@@ -11,20 +13,27 @@ class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Cortex Engine Step 3 — Starting up...");
|
||||
Console.WriteLine("Cortex Engine Step 4 — Starting up...");
|
||||
|
||||
try
|
||||
{
|
||||
using var world = World.Create();
|
||||
using var window = new Sdl3Window("Cortex Engine — Step 3", 1280, 720);
|
||||
using var window = new Sdl3Window("Cortex Engine — Step 4", 1280, 720);
|
||||
var timing = new Timing();
|
||||
var input = new InputMapping();
|
||||
using var vulkan = new VulkanContext(window, enableValidation: false);
|
||||
using var swapchain = new Swapchain(vulkan);
|
||||
using var renderer = new TriangleRenderer(vulkan, swapchain);
|
||||
using var renderer = new MeshRenderer(vulkan, swapchain);
|
||||
|
||||
var triangle = world.Entity("Triangle")
|
||||
.Set(new Transform(new Vector3(0.0f, 0.0f, 0.0f)));
|
||||
var modelPath = FindModelPath(args);
|
||||
var mesh = modelPath.EndsWith(".gltf", StringComparison.OrdinalIgnoreCase)
|
||||
|| modelPath.EndsWith(".glb", StringComparison.OrdinalIgnoreCase)
|
||||
? GltfLoader.Load(modelPath, new Vector3(0.7f, 0.6f, 0.5f))
|
||||
: ObjLoader.Load(modelPath, new Vector3(0.7f, 0.6f, 0.5f));
|
||||
|
||||
var model = world.Entity("Model")
|
||||
.Set(new Transform(new Vector3(0.0f, 0.0f, 0.0f), Quaternion.Identity, new Vector3(0.5f)))
|
||||
.Set(mesh);
|
||||
|
||||
var frames = 0;
|
||||
var lastFpsTime = 0.0;
|
||||
@@ -46,14 +55,10 @@ class Program
|
||||
swapchain.Recreate(lastWidth, lastHeight);
|
||||
}
|
||||
|
||||
// Animate the entity transform.
|
||||
ref var transform = ref triangle.Ensure<Transform>();
|
||||
transform.Position = new Vector3(
|
||||
MathF.Sin((float)timing.TotalTime) * 0.5f,
|
||||
0.0f,
|
||||
0.0f);
|
||||
transform.Rotation = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, (float)timing.TotalTime);
|
||||
transform.Scale = Vector3.One * (1.0f + MathF.Sin((float)timing.TotalTime * 2.0f) * 0.2f);
|
||||
// Slowly rotate the model so we can see it in 3D.
|
||||
ref var transform = ref model.Ensure<Transform>();
|
||||
transform.Rotation = Quaternion.CreateFromAxisAngle(Vector3.UnitY, (float)timing.TotalTime * 0.5f)
|
||||
* Quaternion.CreateFromAxisAngle(Vector3.UnitX, (float)timing.TotalTime * 0.25f);
|
||||
|
||||
renderer.RenderWorld(world);
|
||||
|
||||
@@ -74,4 +79,25 @@ class Program
|
||||
Environment.Exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static string FindModelPath(string[] args)
|
||||
{
|
||||
if (args.Length > 0 && File.Exists(args[0]))
|
||||
return args[0];
|
||||
|
||||
var candidates = new[]
|
||||
{
|
||||
"Content/cube.obj",
|
||||
"Models/cube.obj",
|
||||
"cube.obj"
|
||||
};
|
||||
|
||||
foreach (var candidate in candidates)
|
||||
{
|
||||
if (File.Exists(candidate))
|
||||
return candidate;
|
||||
}
|
||||
|
||||
throw new FileNotFoundException("No model file found. Pass a .obj/.gltf/.glb path as argument or place Content/cube.obj next to the executable.");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user