Merge pull request #31 from wls50/master

support mouse picking in experimental renderer
This commit is contained in:
2025-11-16 19:10:03 +01:00
committed by GitHub
10 changed files with 250 additions and 27 deletions

3
.gitmodules vendored
View File

@@ -29,3 +29,6 @@
[submodule "ref/json"]
path = ref/json
url = https://github.com/nlohmann/json
[submodule "betterRenderer/thirdparty/bvh"]
path = betterRenderer/thirdparty/bvh
url = https://github.com/madmann91/bvh.git

View File

@@ -22,6 +22,9 @@ add_subdirectory("thirdparty/fmt")
add_subdirectory("thirdparty/entt")
add_subdirectory("thirdparty/fsr2")
set(CMAKE_CXX_STANDARD 20)
add_subdirectory("thirdparty/bvh")
add_subdirectory("mashadercompiler")
add_subdirectory("shaders")
@@ -105,5 +108,5 @@ endif ()
target_sources(${LIBMANUL_NAME} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/eu07_source/register.cpp")
set_target_properties(nvrhi yaml-cpp fmt EnTT glfw yaml-cpp-parse yaml-cpp-read yaml-cpp-sandbox PROPERTIES FOLDER "libraries")
target_link_libraries(${LIBMANUL_NAME} ${LIBMANUL_NAME}_fsr2 nvrhi yaml-cpp fmt EnTT glfw)
target_link_libraries(${LIBMANUL_NAME} ${LIBMANUL_NAME}_fsr2 nvrhi yaml-cpp fmt EnTT glfw bvh)
add_dependencies(${LIBMANUL_NAME} ${LIBMANUL_NAME}_shaders)

View File

@@ -25,12 +25,15 @@
#include <Classes.h>
#include <scene.h>
#include "nvrenderer_enums.h"
#include "quadtree.h"
#include "renderer.h"
#include "nvrenderer_enums.h"
#include "resource_registry.h"
#include "sky.h"
namespace Rt {
struct IRtModel;
}
template <typename Renderer>
struct MaRendererConstants {
static constexpr size_t NumMaterialPasses() noexcept {
@@ -188,6 +191,9 @@ class NvRenderer : public gfx_renderer, public MaResourceRegistry {
std::shared_ptr<struct Sky> m_sky;
std::shared_ptr<struct MaAutoExposure> m_auto_exposure;
std::unordered_map<TModel3d const *, std::shared_ptr<Rt::IRtModel>> rt_models;
std::shared_ptr<Rt::IRtModel> GetRtModel(TModel3d const *);
std::shared_ptr<struct MaConfig> m_config;
struct MaConfig *GetConfig() const { return m_config.get(); }
static struct MaConfig *Config();
@@ -213,6 +219,10 @@ class NvRenderer : public gfx_renderer, public MaResourceRegistry {
glm::dvec3 m_previous_env_position;
uint64_t m_previous_env_frame;
glm::dvec3 m_mouse_ro;
glm::dvec3 m_mouse_rd;
TSubModel const *m_picked_submodel = nullptr;
using section_sequence = std::vector<scene::basic_section *>;
using distancecell_pair = std::pair<double, scene::basic_cell *>;
using cell_sequence = std::vector<distancecell_pair>;

View File

@@ -14,18 +14,16 @@ struct MaResourceMapping {
entt::hashed_string m_key;
nvrhi::ResourceType m_type;
#define MA_RESOURCE_MAPPING_INITIALIZER(type) \
template <typename KeyType> \
static MaResourceMapping type(int slot, const KeyType& key) { \
MaResourceMapping mapping{}; \
mapping.m_slot = slot; \
mapping.m_key = static_cast<entt::hashed_string>(key); \
mapping.m_type = nvrhi::ResourceType::type; \
return mapping; \
#define MA_RESOURCE_MAPPING_INITIALIZER(type) \
static MaResourceMapping type(int slot, const char* key) { \
MaResourceMapping mapping{}; \
mapping.m_slot = slot; \
mapping.m_key = static_cast<entt::hashed_string>(key); \
mapping.m_type = nvrhi::ResourceType::type; \
return mapping; \
}
template <typename KeyType>
static MaResourceMapping Texture_SRV(int slot, const KeyType& key) {
static MaResourceMapping Texture_SRV(int slot, const char* key) {
MaResourceMapping mapping{};
mapping.m_slot = slot;
mapping.m_key = static_cast<entt::hashed_string>(key);

View File

@@ -37,6 +37,7 @@
#include <fmt/chrono.h>
#include <fmt/format.h>
#include "rt_model.h"
#include "tinyexr.h"
bool NvRenderer::Init(GLFWwindow *Window) {
@@ -71,8 +72,8 @@ bool NvRenderer::Init(GLFWwindow *Window) {
InitResourceRegistry();
RegisterTexture("noise_2d_ldr",
GetTextureManager()->FetchTexture(
"textures/noise/LDR_RGB1_0", GL_RGBA, 0, false));
GetTextureManager()->FetchTexture("textures/noise/LDR_RGB1_0",
GL_RGBA, 0, false));
m_imgui_renderer = std::make_shared<NvImguiRenderer>(this);
m_gbuffer = std::make_shared<NvGbuffer>(this);
@@ -374,6 +375,22 @@ bool NvRenderer::Render() {
m_backend->GetDevice()->executeCommandList(command_list_sky);
}
{
glm::dvec2 const mousepos =
glm::dvec2(2., -2.) *
(static_cast<glm::dvec2>(Global.cursor_pos) /
static_cast<glm::dvec2>(Global.window_size) -
.5);
m_mouse_ro = pass.m_origin;
glm::dvec4 mouse_rd_norm =
inverse(projection) * glm::dvec4(mousepos, 1., 1.);
mouse_rd_norm /= mouse_rd_norm.w;
m_mouse_rd =
normalize(inverse(transform) * glm::dvec4(mouse_rd_norm.xyz, 0.));
}
m_picked_submodel = nullptr;
if (!m_pause_animations) {
Animate(pass.m_origin, pass.m_draw_range, pass.m_frame_index);
}
@@ -729,10 +746,9 @@ void NvRenderer::SwapBuffers() {
m_backend->GetDevice()->runGarbageCollection();
}
float NvRenderer::Framerate() {
return 1000.f / (Timer::subsystem.mainloop_total.average());
//return 0.0f;
float NvRenderer::Framerate() {
return 1000.f / (Timer::subsystem.mainloop_total.average());
// return 0.0f;
}
gfx::geometrybank_handle NvRenderer::Create_Bank() {
@@ -1061,12 +1077,14 @@ ITexture const &NvRenderer::Texture(texture_handle const Texture) const {
}
void NvRenderer::Pick_Control_Callback(
std::function<void(TSubModel const *, const glm::vec2)> Callback) {}
std::function<void(TSubModel const *, const glm::vec2)> Callback) {
Callback(m_picked_submodel, {});
}
void NvRenderer::Pick_Node_Callback(
std::function<void(scene::basic_node *)> Callback) {}
TSubModel const *NvRenderer::Pick_Control() const { return nullptr; }
TSubModel const *NvRenderer::Pick_Control() const { return m_picked_submodel; }
scene::basic_node const *NvRenderer::Pick_Node() const { return nullptr; }
@@ -1217,6 +1235,17 @@ void NvRenderer::MakeScreenshot() {
}
}
std::shared_ptr<Rt::IRtModel> NvRenderer::GetRtModel(TModel3d const *model) {
if (!model) {
return nullptr;
}
auto &entry = rt_models[model];
if (!entry) {
entry = Rt::CreateRtModel(model, this);
}
return entry;
}
MaConfig *NvRenderer::Config() {
NvRenderer *me = dynamic_cast<NvRenderer *>(GfxRenderer.get());
return me ? me->GetConfig() : nullptr;
@@ -1873,6 +1902,10 @@ void NvRenderer::Animate(DynObj &instance, const glm::dvec3 &origin,
if (render_kabina) {
Animate(instance.m_renderable_kabina, instance.m_dynamic->mdKabina,
material, instance.m_distance, dynamic_transform, frame);
if (auto const rt_model = GetRtModel(instance.m_dynamic->mdKabina)) {
m_picked_submodel = rt_model->Intersect(instance.m_renderable_kabina,
m_mouse_ro, m_mouse_rd);
}
}
if (instance.m_dynamic->btnOn) instance.m_dynamic->TurnOff();

View File

@@ -386,12 +386,12 @@ bool NvTexture::CreateRhiTexture() {
.setHeight(m_height)
.setMipLevels(m_data.size())
.setFormat(m_format)
.setInitialState(nvrhi::ResourceStates::CopyDest)
.setInitialState(nvrhi::ResourceStates::ShaderResource)
.setKeepInitialState(true));
nvrhi::CommandListHandle command_list =
backend->GetDevice()->createCommandList(
nvrhi::CommandListParameters()
.setQueueType(nvrhi::CommandQueue::Copy)
.setQueueType(nvrhi::CommandQueue::Graphics)
.setEnableImmediateExecution(false));
command_list->open();
for (int mip = 0; mip < m_data.size(); ++mip) {
@@ -400,7 +400,7 @@ bool NvTexture::CreateRhiTexture() {
}
command_list->close();
backend->GetDevice()->executeCommandList(command_list,
nvrhi::CommandQueue::Copy);
nvrhi::CommandQueue::Graphics);
if (m_sz_texture->get_type() == "make:") {
auto const components{Split(std::string(m_sz_texture->get_name()), '?')};

View File

@@ -0,0 +1,151 @@
#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>
#include <vector>
#include "Model3d.h"
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)) {
prim_id = i;
std::tie(ray.tmax, u, v) = *hit;
result = submodel.submodel;
}
}
return prim_id != std::numeric_limits<size_t>::max();
});
}
}
return result;
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include <glm/vec3.hpp>
#include <memory>
#include "nvrenderer/nvrenderer.h"
class TSubModel;
class TModel3d;
namespace Rt {
struct IRtModel {
virtual TSubModel const* Intersect(NvRenderer::Renderable const& renderable,
glm::dvec3 const& ro,
glm::dvec3 const& rd) const {
return nullptr;
}
virtual ~IRtModel() = default;
};
std::shared_ptr<IRtModel> CreateRtModel(TModel3d const* src,
NvRenderer const* owner);
} // namespace Rt

View File

@@ -239,7 +239,7 @@ void CS_CullLights(in ComputeShaderInput input) {
bool is_opaque = false;
if(light.m_cos_outer < -1.5) {
while(dist < maxDist) {
for(int i = 0; i < 100 && dist < maxDist; ++i) {
float3 ro = dist * rd;
float distance = sdSphere(ro - lightPosVS, light.m_radius);
if(distance <= dist * tan_angle) {
@@ -252,7 +252,7 @@ void CS_CullLights(in ComputeShaderInput input) {
if(!is_opaque) {
dist = minDistOpaque - 1.e-2;
while(dist < maxDist) {
for(int i = 0; i < 100 && dist < maxDist; ++i) {
float3 ro = dist * rd;
float distance = sdSphere(ro - lightPosVS, light.m_radius);
if(distance <= dist * tan_angle) {
@@ -271,7 +271,7 @@ void CS_CullLights(in ComputeShaderInput input) {
float3x3 transform = {tang, lightDirVS, bitang};
float2 angle = {sqrt(1. - light.m_cos_outer * light.m_cos_outer), light.m_cos_outer};
while(dist < maxDist) {
for(int i = 0; i < 100 && dist < maxDist; ++i) {
float3 ro = dist * rd;
float distance = sdSolidAngle(mul(transform, ro - lightPosVS), angle, light.m_radius);
if(distance <= dist * tan_angle) {
@@ -284,7 +284,7 @@ void CS_CullLights(in ComputeShaderInput input) {
if(!is_opaque) {
dist = minDistOpaque - 1.e-2;
while(dist < maxDist) {
for(int i = 0; i < 100 && dist < maxDist; ++i) {
float3 ro = dist * rd;
float distance = sdSolidAngle(mul(transform, ro - lightPosVS), angle, light.m_radius);
if(distance <= dist * tan_angle) {