diff --git a/.gitmodules b/.gitmodules index 1b0e0b96..df7f5dda 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/betterRenderer/CMakeLists.txt b/betterRenderer/CMakeLists.txt index c2b9110b..1d60abd2 100644 --- a/betterRenderer/CMakeLists.txt +++ b/betterRenderer/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/betterRenderer/renderer/include/nvrenderer/nvrenderer.h b/betterRenderer/renderer/include/nvrenderer/nvrenderer.h index d9058cb7..9cded346 100644 --- a/betterRenderer/renderer/include/nvrenderer/nvrenderer.h +++ b/betterRenderer/renderer/include/nvrenderer/nvrenderer.h @@ -25,12 +25,15 @@ #include #include +#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 struct MaRendererConstants { static constexpr size_t NumMaterialPasses() noexcept { @@ -188,6 +191,9 @@ class NvRenderer : public gfx_renderer, public MaResourceRegistry { std::shared_ptr m_sky; std::shared_ptr m_auto_exposure; + std::unordered_map> rt_models; + std::shared_ptr GetRtModel(TModel3d const *); + std::shared_ptr 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; using distancecell_pair = std::pair; using cell_sequence = std::vector; diff --git a/betterRenderer/renderer/include/nvrenderer/resource_registry.h b/betterRenderer/renderer/include/nvrenderer/resource_registry.h index 26142017..5256a384 100644 --- a/betterRenderer/renderer/include/nvrenderer/resource_registry.h +++ b/betterRenderer/renderer/include/nvrenderer/resource_registry.h @@ -14,18 +14,16 @@ struct MaResourceMapping { entt::hashed_string m_key; nvrhi::ResourceType m_type; -#define MA_RESOURCE_MAPPING_INITIALIZER(type) \ - template \ - static MaResourceMapping type(int slot, const KeyType& key) { \ - MaResourceMapping mapping{}; \ - mapping.m_slot = slot; \ - mapping.m_key = static_cast(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(key); \ + mapping.m_type = nvrhi::ResourceType::type; \ + return mapping; \ } - template - 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(key); diff --git a/betterRenderer/renderer/source/nvrenderer.cpp b/betterRenderer/renderer/source/nvrenderer.cpp index c936e1db..b48eb18a 100644 --- a/betterRenderer/renderer/source/nvrenderer.cpp +++ b/betterRenderer/renderer/source/nvrenderer.cpp @@ -37,6 +37,7 @@ #include #include +#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(this); m_gbuffer = std::make_shared(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(Global.cursor_pos) / + static_cast(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 Callback) {} + std::function Callback) { + Callback(m_picked_submodel, {}); +} void NvRenderer::Pick_Node_Callback( std::function 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 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(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(); diff --git a/betterRenderer/renderer/source/nvtexture.cpp b/betterRenderer/renderer/source/nvtexture.cpp index 8a40656c..a6c58a9c 100644 --- a/betterRenderer/renderer/source/nvtexture.cpp +++ b/betterRenderer/renderer/source/nvtexture.cpp @@ -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()), '?')}; diff --git a/betterRenderer/renderer/source/rt_model.cpp b/betterRenderer/renderer/source/rt_model.cpp new file mode 100644 index 00000000..7bd2156f --- /dev/null +++ b/betterRenderer/renderer/source/rt_model.cpp @@ -0,0 +1,151 @@ +#include "rt_model.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "Model3d.h" + +namespace Rt { + +using Bbox = bvh::v2::BBox; +using Vec = bvh::v2::Vec; +using Tri = bvh::v2::PrecomputedTri; +using Node = bvh::v2::Node; + +struct RtSubmodel { + TSubModel const* submodel; + bvh::v2::Bvh bvh; + std::vector tris; +}; + +struct RtModel : IRtModel { + std::vector submodels; + std::unordered_map 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::CreateRtModel(TModel3d const* src, + NvRenderer const* owner) { + auto model = std::make_shared(); + 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 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 bboxes(dest.tris.size()); + std::vector 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::Config config; + config.quality = bvh::v2::DefaultBuilder::Quality::High; + dest.bvh = bvh::v2::DefaultBuilder::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::Index, 64> stack; + bvh::v2::Ray ray{}; + ray.tmax = std::numeric_limits::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::max(); + stack.size = 0; + submodel.bvh.intersect( + 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::max(); + }); + } + } + return result; +} \ No newline at end of file diff --git a/betterRenderer/renderer/source/rt_model.h b/betterRenderer/renderer/source/rt_model.h new file mode 100644 index 00000000..e1733189 --- /dev/null +++ b/betterRenderer/renderer/source/rt_model.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +#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 CreateRtModel(TModel3d const* src, + NvRenderer const* owner); + +} // namespace Rt \ No newline at end of file diff --git a/betterRenderer/shaders/manul/forward_plus/forward_plus.hlsl b/betterRenderer/shaders/manul/forward_plus/forward_plus.hlsl index c886b7f0..ef90b431 100644 --- a/betterRenderer/shaders/manul/forward_plus/forward_plus.hlsl +++ b/betterRenderer/shaders/manul/forward_plus/forward_plus.hlsl @@ -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) { diff --git a/betterRenderer/thirdparty/bvh b/betterRenderer/thirdparty/bvh new file mode 160000 index 00000000..ac41ab88 --- /dev/null +++ b/betterRenderer/thirdparty/bvh @@ -0,0 +1 @@ +Subproject commit ac41ab88a32d0c247009d8ed456fd2795c1ee023