ADD: BVH Selection ongoing
This commit is contained in:
147
src/scene/mesh_bvh.cpp
Normal file
147
src/scene/mesh_bvh.cpp
Normal 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
58
src/scene/mesh_bvh.h
Normal 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)
|
||||
};
|
||||
|
||||
// Ray–mesh 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);
|
||||
|
||||
Reference in New Issue
Block a user