feat: add selection to meshrendercomponent

This commit is contained in:
2026-10-22 22:24:59 +02:00
parent f99147b1a2
commit f055bdcbf1
16 changed files with 1103 additions and 68 deletions
+70
View File
@@ -1,4 +1,5 @@
#include <algorithm>
#include <array>
#include <cmath>
#include <filesystem>
#include <string>
@@ -263,6 +264,74 @@ namespace {
"invalid primitive keys must not build a mesh");
}
void testPrimitiveShapes()
{
using PrimitiveMeshes::Shape;
const std::array<PrimitiveMeshes::Desc, 6> descs{{
{.shape = Shape::Box, .size = {2.0f, 0.5f, 3.0f}},
{.shape = Shape::Plane, .size = {4.0f, 1.0f, 6.0f}},
{.shape = Shape::Sphere, .radius = 0.75f},
{.shape = Shape::Cylinder, .radius = 0.5f, .height = 3.0f},
{.shape = Shape::Capsule, .radius = 0.4f, .height = 2.0f},
{.shape = Shape::Capsule, .radius = 1.0f, .height = 1.0f},
}};
for (const auto& desc : descs) {
const std::string key = PrimitiveMeshes::MakeKey(desc);
const auto parsed = PrimitiveMeshes::ParseKey(key + "#2");
check(parsed.has_value() && *parsed == desc, "every primitive must rebuild exactly from its key");
const CPUMesh mesh = PrimitiveMeshes::Make(desc);
check(mesh.name == key, "primitives must be named by their key");
check(!mesh.indices.empty() && mesh.indices.size() % 3 == 0, "primitives must be made of triangles");
bool valid = true;
for (const std::uint32_t index : mesh.indices) {
valid = valid && index < mesh.vertices.size();
}
check(valid, "primitive indices must point at their own vertices");
bool outward = true;
bool tangents = true;
for (const auto& vertex : mesh.vertices) {
outward = outward && close(glm::length(vertex.normal), 1.0f) && glm::dot(vertex.position, vertex.normal) > -0.001f;
tangents = tangents && close(glm::dot(glm::vec3{vertex.tangent}, vertex.normal), 0.0f) &&
close(std::abs(vertex.tangent.w), 1.0f);
}
check(outward, "primitive normals must be unit length and point away from the center");
check(tangents, "primitive tangents must lie in the surface with a handedness of +-1");
bool counterClockwise = true;
for (std::size_t i = 0; i < mesh.indices.size(); i += 3) {
const auto& a = mesh.vertices[mesh.indices[i]];
const auto& b = mesh.vertices[mesh.indices[i + 1]];
const auto& c = mesh.vertices[mesh.indices[i + 2]];
const glm::vec3 faceNormal = glm::cross(b.position - a.position, c.position - a.position);
counterClockwise = counterClockwise && glm::dot(faceNormal, a.normal + b.normal + c.normal) >= 0.0f;
}
check(counterClockwise, "primitive triangles must be counter clockwise seen from outside");
}
const CPUMesh sphere = PrimitiveMeshes::Make(descs[2]);
check(close(sphere.maxPos.x, 0.75f) && close(sphere.minPos.y, -0.75f), "a sphere must reach its radius");
const CPUMesh plane = PrimitiveMeshes::Make(descs[1]);
check(close(plane.maxPos.x, 2.0f) && close(plane.maxPos.z, 3.0f) && close(plane.maxPos.y, 0.0f),
"a plane must be flat on y = 0 with its size on x and z");
const CPUMesh capsule = PrimitiveMeshes::Make(descs[4]);
check(close(capsule.maxPos.y, 1.0f) && close(capsule.maxPos.x, 0.4f),
"a capsule height must be its full length, like the collider");
const CPUMesh cylinder = PrimitiveMeshes::Make(descs[3]);
check(close(cylinder.maxPos.y, 1.5f) && close(cylinder.minPos.y, -1.5f), "a cylinder must be centered on its height");
check(!PrimitiveMeshes::ParseKey("prim://sphere?radius=1&radius=2").has_value() &&
!PrimitiveMeshes::ParseKey("prim://cylinder?radius=1").has_value() &&
!PrimitiveMeshes::ParseKey("prim://plane?size=1,2,3").has_value() &&
!PrimitiveMeshes::ParseKey("prim://cone?radius=1").has_value() &&
!PrimitiveMeshes::ParseKey("prim://sphere?radius=-1").has_value() &&
!PrimitiveMeshes::ParseKey("prim://sphere").has_value(),
"malformed primitive keys must be rejected");
}
void testIndirectDrawListBatchesByMesh()
{
std::vector<GPUMesh> meshes(2);
@@ -357,6 +426,7 @@ void RunGraphicsTests()
testPrimitiveBoxMesh();
testSimpleColorMaterial();
testPrimitiveMeshKeys();
testPrimitiveShapes();
testDeletionQueueWaitsForFrame();
testIndirectDrawListBatchesByMesh();
}