mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 14:29:20 +02:00
Ordering them before the imports in the same file was not enough: a project header on line 1 can import a module several levels down whose global module fragment already pulled in the same standard header, and that include guard does not cross the module boundary. Putting them first means the guard is set before anything can import. Skips vendored single-header libraries and anything inside an #if.
159 lines
5.5 KiB
C++
159 lines
5.5 KiB
C++
#include <array>
|
|
#include <cstddef>
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <tuple>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
#include "rt_model.h"
|
|
|
|
#include <bvh/v2/bvh.h>
|
|
#include <bvh/v2/default_builder.h>
|
|
#include <bvh/v2/executor.h>
|
|
#include <bvh/v2/stack.h>
|
|
#include <bvh/v2/thread_pool.h>
|
|
#include <bvh/v2/tri.h>
|
|
|
|
|
|
|
|
import eu07.simcore;
|
|
namespace Rt {
|
|
|
|
using Bbox = bvh::v2::BBox<float, 3>;
|
|
using Vec = bvh::v2::Vec<float, 3>;
|
|
using Tri = bvh::v2::PrecomputedTri<float>;
|
|
using Node = bvh::v2::Node<float, 3>;
|
|
|
|
struct RtSubmodel {
|
|
TSubModel const* submodel;
|
|
bvh::v2::Bvh<Node> bvh;
|
|
std::vector<Tri> tris;
|
|
};
|
|
|
|
struct RtModel : IRtModel {
|
|
std::vector<RtSubmodel> submodels;
|
|
std::unordered_map<size_t, int> submodel_mapping;
|
|
|
|
RtModel& FromModel3d(TModel3d const* src, NvRenderer const* owner);
|
|
void FromSubmodel(TSubModel const* submodel, NvRenderer const* owner);
|
|
const TSubModel* Intersect(NvRenderer::Renderable const& renderable,
|
|
glm::dvec3 const& ro,
|
|
glm::dvec3 const& rd) const override;
|
|
};
|
|
} // namespace Rt
|
|
|
|
std::shared_ptr<Rt::IRtModel> Rt::CreateRtModel(TModel3d const* src,
|
|
NvRenderer const* owner) {
|
|
auto model = std::make_shared<RtModel>();
|
|
model->FromModel3d(src, owner);
|
|
return model;
|
|
}
|
|
|
|
Rt::RtModel& Rt::RtModel::FromModel3d(TModel3d const* src,
|
|
NvRenderer const* owner) {
|
|
FromSubmodel(src->Root, owner);
|
|
return *this;
|
|
}
|
|
|
|
void Rt::RtModel::FromSubmodel(TSubModel const* submodel,
|
|
NvRenderer const* owner) {
|
|
if (!submodel) {
|
|
return;
|
|
}
|
|
for (; !!submodel; submodel = submodel->Next) {
|
|
if (submodel->fSquareMinDist >
|
|
0.) { // Only LOD0 contributes to RT structure
|
|
continue;
|
|
}
|
|
if (submodel->eType == GL_TRIANGLES ||
|
|
submodel->eType == GL_TRIANGLE_STRIP) {
|
|
submodel_mapping[submodel->m_geometry.handle] = submodels.size();
|
|
auto& dest = submodels.emplace_back();
|
|
auto const& verts = owner->Vertices(submodel->m_geometry.handle);
|
|
auto const& indices = owner->Indices(submodel->m_geometry.handle);
|
|
auto add_triangle = [&](glm::vec3 const& a, glm::vec3 const& b,
|
|
glm::vec3 const& c) {
|
|
dest.tris.emplace_back(Vec(a.x, a.y, a.z), Vec(b.x, b.y, b.z),
|
|
Vec(c.x, c.y, c.z));
|
|
};
|
|
bool const is_indexed = !indices.empty();
|
|
for (int idx = 0, num = is_indexed ? indices.size() : verts.size();
|
|
idx < num; idx += 3) {
|
|
std::array<int, 3> tri_indices;
|
|
for (int i = 0; i < 3; ++i) {
|
|
if (is_indexed)
|
|
tri_indices[i] = indices[idx + i];
|
|
else
|
|
tri_indices[i] = idx + i;
|
|
}
|
|
add_triangle(verts[tri_indices[0]].position,
|
|
verts[tri_indices[1]].position,
|
|
verts[tri_indices[2]].position);
|
|
}
|
|
|
|
bvh::v2::ThreadPool thread_pool;
|
|
bvh::v2::ParallelExecutor executor(thread_pool);
|
|
|
|
// Get triangle centers and bounding boxes (required for BVH builder)
|
|
std::vector<Bbox> bboxes(dest.tris.size());
|
|
std::vector<Vec> centers(dest.tris.size());
|
|
executor.for_each(0, dest.tris.size(), [&](size_t begin, size_t end) {
|
|
for (size_t i = begin; i < end; ++i) {
|
|
bboxes[i] = dest.tris[i].get_bbox();
|
|
centers[i] = dest.tris[i].get_center();
|
|
}
|
|
});
|
|
|
|
dest.submodel = submodel;
|
|
|
|
bvh::v2::DefaultBuilder<Node>::Config config;
|
|
config.quality = bvh::v2::DefaultBuilder<Node>::Quality::High;
|
|
dest.bvh = bvh::v2::DefaultBuilder<Node>::build(thread_pool, bboxes,
|
|
centers, config);
|
|
}
|
|
FromSubmodel(submodel->Child, owner);
|
|
}
|
|
}
|
|
|
|
const TSubModel* Rt::RtModel::Intersect(
|
|
NvRenderer::Renderable const& renderable, glm::dvec3 const& ro,
|
|
glm::dvec3 const& rd) const {
|
|
TSubModel const* result = nullptr;
|
|
bvh::v2::SmallStack<bvh::v2::Bvh<Node>::Index, 64> stack;
|
|
bvh::v2::Ray<float, 3> ray{};
|
|
ray.tmax = std::numeric_limits<float>::max();
|
|
for (auto const& item : renderable.m_items) {
|
|
if (item.m_sqr_distance_min > 0. || item.m_render_in_forward) {
|
|
continue;
|
|
}
|
|
if (auto const it = submodel_mapping.find(item.m_geometry);
|
|
it != submodel_mapping.end()) {
|
|
auto const& submodel = submodels[it->second];
|
|
glm::dmat4 const submodel_matrix =
|
|
glm::inverse(glm::dmat4(item.m_transform));
|
|
glm::vec3 ro_local = submodel_matrix * glm::dvec4(ro, 1.);
|
|
glm::vec3 rd_local = normalize(submodel_matrix * glm::dvec4(rd, 0.));
|
|
ray.org = {ro_local.x, ro_local.y, ro_local.z};
|
|
ray.dir = {rd_local.x, rd_local.y, rd_local.z};
|
|
float u, v;
|
|
size_t prim_id = std::numeric_limits<size_t>::max();
|
|
stack.size = 0;
|
|
submodel.bvh.intersect<true, false>(
|
|
ray, submodel.bvh.get_root().index, stack,
|
|
[&](size_t const begin, size_t const end) {
|
|
for (size_t i = begin; i < end; ++i) {
|
|
size_t const j = submodel.bvh.prim_ids[i];
|
|
if (auto hit = submodel.tris[j].intersect(ray);
|
|
!!hit && dot(rd_local, glm::make_vec3(
|
|
submodel.tris[j].n.values)) > 0.) {
|
|
prim_id = i;
|
|
std::tie(ray.tmax, u, v) = *hit;
|
|
result = submodel.submodel;
|
|
}
|
|
}
|
|
return prim_id != std::numeric_limits<size_t>::max();
|
|
});
|
|
}
|
|
}
|
|
return result;
|
|
} |