ADD: BVH Selection ongoing

This commit is contained in:
2025-11-19 16:24:51 +09:00
parent 4061cbed4a
commit ac35de6104
2 changed files with 205 additions and 0 deletions

147
src/scene/mesh_bvh.cpp Normal file
View File

@@ -0,0 +1,147 @@
#include "mesh_bvh.h"
#include <limits>
#include <thread>
#include <scene/vk_loader.h>
#include "glm/gtx/norm.hpp"
std::unique_ptr<MeshBVH> build_mesh_bvh(const MeshAsset &mesh,
std::span<const Vertex> vertices,
std::span<const uint32_t> indices)
{
if (vertices.empty() || indices.size() < 3 || mesh.surfaces.empty())
{
return {};
}
size_t totalTriangles = 0;
for (const GeoSurface &surf: mesh.surfaces)
{
totalTriangles += static_cast<size_t>(surf.count / 3);
}
if (totalTriangles == 0)
{
return {};
}
auto result = std::make_unique<MeshBVH>();
result->primitives.reserve(totalTriangles);
result->primitiveRefs.reserve(totalTriangles);
for (uint32_t s = 0; s < mesh.surfaces.size(); ++s)
{
const GeoSurface &surf = mesh.surfaces[s];
uint32_t start = surf.startIndex;
uint32_t end = surf.startIndex + surf.count;
if (start >= indices.size())
{
continue;
}
if (end > indices.size())
{
end = static_cast<uint32_t>(indices.size());
}
for (uint32_t idx = start; idx + 2 < end; idx += 3)
{
uint32_t i0 = indices[idx + 0];
uint32_t i1 = indices[idx + 1];
uint32_t i2 = indices[idx + 2];
if (i0 >= vertices.size() || i1 >= vertices.size() || i2 >= vertices.size())
{
continue;
}
const glm::vec3 &p0 = vertices[i0].position;
const glm::vec3 &p1 = vertices[i1].position;
const glm::vec3 &p2 = vertices[i2].position;
PrimitiveF prim{};
prim.bounds.expand(Vec3<float>(p0.x, p0.y, p0.z));
prim.bounds.expand(Vec3<float>(p1.x, p1.y, p1.z));
prim.bounds.expand(Vec3<float>(p2.x, p2.y, p2.z));
result->primitives.push_back(prim);
MeshBVHPrimitiveRef ref{};
ref.surfaceIndex = s;
ref.firstIndex = idx;
result->primitiveRefs.push_back(ref);
}
}
if (result->primitives.empty())
{
return {};
}
unsigned int threadCount = std::thread::hardware_concurrency();
if (threadCount == 0)
{
threadCount = 1;
}
tf::Executor executor{threadCount};
result->nodes = buildLBVH<uint64_t>(executor, result->primitives, MortonSortMethod::RadixSort);
return result;
}
bool intersect_ray_mesh_bvh(const MeshBVH &bvh,
const glm::mat4 &worldTransform,
const glm::vec3 &rayOriginWorld,
const glm::vec3 &rayDirWorld,
MeshBVHPickHit &outHit)
{
outHit = {};
if (bvh.nodes.empty() || bvh.primitives.empty())
{
return false;
}
if (glm::length2(rayDirWorld) < 1e-8f)
{
return false;
}
glm::mat4 invM = glm::inverse(worldTransform);
glm::vec3 originLocal = glm::vec3(invM * glm::vec4(rayOriginWorld, 1.0f));
glm::vec3 dirLocal = glm::vec3(invM * glm::vec4(rayDirWorld, 0.0f));
if (glm::length2(dirLocal) < 1e-8f)
{
return false;
}
dirLocal = glm::normalize(dirLocal);
Ray ray(Vec3<float>(originLocal.x, originLocal.y, originLocal.z),
Vec3<float>(dirLocal.x, dirLocal.y, dirLocal.z));
uint32_t primIdx = 0;
float tLocal = 0.0f;
if (!traverseBVHClosestHit<float>(bvh.nodes, bvh.primitives, ray, primIdx, tLocal))
{
return false;
}
if (primIdx >= bvh.primitiveRefs.size())
{
return false;
}
const MeshBVHPrimitiveRef &ref = bvh.primitiveRefs[primIdx];
glm::vec3 localHit = originLocal + dirLocal * tLocal;
glm::vec3 worldHit = glm::vec3(worldTransform * glm::vec4(localHit, 1.0f));
outHit.hit = true;
outHit.localPos = localHit;
outHit.worldPos = worldHit;
outHit.surfaceIndex = ref.surfaceIndex;
outHit.firstIndex = ref.firstIndex;
outHit.t = glm::length(worldHit - rayOriginWorld);
return true;
}

58
src/scene/mesh_bvh.h Normal file
View File

@@ -0,0 +1,58 @@
#pragma once
#include <cstdint>
#include <memory>
#include <span>
#include <vector>
#include <glm/mat4x4.hpp>
#include <glm/vec3.hpp>
#include <core/vk_types.h>
#include <bvh/BVH.h>
struct MeshAsset;
// For each BVH primitive, record which surface and which triangle
// (by starting index into the index buffer) it represents.
struct MeshBVHPrimitiveRef
{
uint32_t surfaceIndex = 0;
uint32_t firstIndex = 0;
};
// CPU-side BVH for a mesh, built in mesh-local space.
struct MeshBVH
{
std::vector<PrimitiveF> primitives;
std::vector<BVHNodeF> nodes;
std::vector<MeshBVHPrimitiveRef> primitiveRefs;
};
// Build a mesh-local BVH for a triangle mesh.
// vertices/indices must match the GPU data uploaded for 'mesh'.
// Returns nullptr if no triangles are found.
std::unique_ptr<MeshBVH> build_mesh_bvh(const MeshAsset &mesh,
std::span<const Vertex> vertices,
std::span<const uint32_t> indices);
struct MeshBVHPickHit
{
bool hit = false;
float t = 0.0f; // world-space distance along ray
glm::vec3 localPos{0.0f}; // hit position in mesh-local space
glm::vec3 worldPos{0.0f}; // hit position in world space
uint32_t surfaceIndex = 0; // hit GeoSurface index
uint32_t firstIndex = 0; // index into mesh index buffer (triangle start)
};
// Raymesh BVH intersection in world space.
// Returns true on hit and fills outHit.
bool intersect_ray_mesh_bvh(const MeshBVH &bvh,
const glm::mat4 &worldTransform,
const glm::vec3 &rayOriginWorld,
const glm::vec3 &rayDirWorld,
MeshBVHPickHit &outHit);