17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-17 14:29:20 +02:00
Files
maszyna/betterRenderer/renderer/source/nvrenderer.cpp
maj00r 26501b0fd7 hoist standard includes above everything else
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.
2026-09-10 01:02:17 +02:00

2103 lines
76 KiB
C++

#include <array>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <future>
#include <limits>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "nvrenderer/nvrenderer.h"
#include <nvrhi/utils.h>
#include "auto_exposure.h"
#include "bloom.h"
#include "config.h"
#include "contactshadows.h"
#include "csm.h"
#include "environment.h"
#include "fsr.h"
#include "gbuffer.h"
#include "gbufferblitpass.h"
#include "gbufferlighting.h"
#include "instancecache.h"
#include "materialparser.h"
#include "motioncache.h"
#include "nvrenderer_imgui.h"
#include "nvrendererbackend.h"
#include "nvtexture.h"
#include "sky.h"
#include "ssao.h"
#include "tonemap.h"
#include <fmt/chrono.h>
#include <fmt/format.h>
#include "rt_model.h"
#include "tinyexr.h"
#include "windshield_rain.h"
#include "utilities/utilities_macros.h"
import eu07.simcore;
import eu07.utilities.logs;
import eu07.utilities.timer;
import eu07.application.application;
import eu07.simulation.simulation;
#define TINYEXR_USE_MINIZ 0
#define TINYEXR_USE_STB_ZLIB 1
#define TINYEXR_IMPLEMENTATION
import eu07.glm;
import eu07.utilities.utilities;
bool NvRenderer::Init(GLFWwindow *Window) {
m_message_callback = std::make_shared<NvRendererMessageCallback>();
m_config = std::make_shared<MaConfig>();
m_config->Init("renderercfg.yml");
switch (m_api) {
case Api::D3D12:
#if LIBMANUL_WITH_D3D12
if (!InitForD3D12(Window)) {
return false;
}
#else
ErrorLog("D3D12 rendering backend not available in current Eu07 build!");
return false;
#endif
break;
case Api::Vulkan:
#if LIBMANUL_WITH_VULKAN
if (!InitForVulkan(Window)) {
return false;
}
#else
ErrorLog("Vulkan rendering backend not available in current Eu07 build!");
return false;
#endif
break;
}
m_backend->Init();
InitResourceRegistry();
RegisterTexture("noise_2d_ldr",
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);
m_gbuffer_cube = std::make_shared<NvGbuffer>(this);
m_gbuffer_shadow = std::make_shared<NvGbuffer>(this);
m_contact_shadows = std::make_shared<MaContactShadows>(this, m_gbuffer.get());
m_shadow_map = std::make_shared<MaShadowMap>(this);
m_environment = std::make_shared<MaEnvironment>(this);
m_sky = std::make_shared<Sky>(this, m_environment.get());
m_ssao = std::make_shared<NvSsao>(this);
m_gbuffer_lighting = std::make_shared<GbufferLighting>(this);
m_gbuffer_blit = std::make_shared<GbufferBlitPass>(
this, m_gbuffer.get(), m_gbuffer_shadow.get(), m_ssao.get(),
m_environment.get(), m_shadow_map.get(), m_contact_shadows.get(),
m_sky.get());
m_auto_exposure = std::make_shared<MaAutoExposure>(this);
m_fsr = std::make_shared<NvFSR>(this);
m_bloom = std::make_shared<Bloom>(GetBackend());
m_windshield_rain = std::make_shared<WindshieldRain>();
// protect from undefined framebuffer size in ini (default -1)
int w = Global.gfx_framebuffer_width, h = Global.gfx_framebuffer_height;
if (w < 0) w = Global.window_size.x;
if (h < 0) h = Global.window_size.y;
m_gbuffer->Init(w, h, false, false, false);
m_contact_shadows->Init();
m_shadow_map->Init();
m_gbuffer_cube->Init(512, 512, true, false, false);
const auto &shadow_config = GetConfig()->m_shadow;
m_gbuffer_shadow->Init(shadow_config.m_resolution, shadow_config.m_resolution,
false, true, m_shadow_map->m_cascade_matrices.size());
m_ssao->Init();
m_sky->Init();
m_environment->Init("farm_field_puresky_8k.hdr", -1.f);
m_gbuffer_lighting->Init();
m_gbuffer_blit->Init();
m_auto_exposure->Init();
m_fsr->Init();
// m_taa->Init();
// m_tonemap = std::make_shared<TonemapPass>(m_backend.get(),
// m_fsr->m_output);
// m_bloom->Init(m_fsr->m_output);
m_bloom->Init(m_fsr->m_output);
m_tonemap = std::make_shared<TonemapPass>(this, m_bloom->m_working_texture);
m_tonemap->Init();
m_framebuffer_forward = GetBackend()->GetDevice()->createFramebuffer(
nvrhi::FramebufferDesc()
.addColorAttachment(m_gbuffer_blit->m_output)
.setDepthAttachment(m_gbuffer->m_gbuffer_depth));
m_motion_cache = std::make_shared<MotionCache>();
m_instance_cache = std::make_shared<InstanceBank>();
m_command_list = m_backend->GetDevice()->createCommandList();
RegisterResource(true, "shadow_depths", m_gbuffer_shadow->m_gbuffer_depth,
nvrhi::ResourceType::Texture_SRV);
// RegisterResource(true, "gbuffer_depth", m_gbuffer->m_gbuffer_depth,
// nvrhi::ResourceType::Texture_SRV);
m_windshield_rain->Init(this);
if (!InitMaterials()) return false;
return true;
}
bool NvRenderer::AddViewport(
const global_settings::extraviewport_config &conf) {
return false;
}
void NvRenderer::Shutdown() {}
bool NvRenderer::Render() {
std::scoped_lock lock(m_mtx_context_lock);
Timer::subsystem.gfx_total
.start(); // note: gfx_total is actually frame total, clean this up
m_backend->UpdateWindowSize();
m_backend->BeginFrame();
nvrhi::CommandListHandle command_list_clear =
m_backend->GetDevice()->createCommandList(/*
nvrhi::CommandListParameters().setEnableImmediateExecution(false)*/);
nvrhi::FramebufferHandle framebuffer = m_backend->GetCurrentFramebuffer();
command_list_clear->open();
// nvrhi::utils::ClearColorAttachment(
// command_list_clear, framebuffer, 0,
// nvrhi::Color(51.0f / 255.0f, 102.0f / 255.0f, 85.0f / 255.0f, 1.0f));
command_list_clear->close();
m_backend->GetDevice()->executeCommandList(command_list_clear);
struct Frustum {
std::array<glm::dvec4, 6> m_planes;
Frustum(const glm::dmat4 &view_projection) {
glm::mat4 m = glm::transpose(view_projection);
m_planes[0] = {m[3] + m[0]};
m_planes[1] = {m[3] - m[0]};
m_planes[2] = {m[3] + m[1]};
m_planes[3] = {m[3] - m[1]};
m_planes[4] = {m[2]};
m_planes[5] = {m[3] - m[2]};
for (auto &plane : m_planes) {
plane /= glm::length(static_cast<glm::dvec3>(plane.xyz));
}
}
bool SphereInside(const glm::dvec3 &origin, double radius) const {
for (const auto &plane : m_planes) {
if (glm::dot(glm::dvec4(origin, 1.), plane) < -radius) {
return false;
}
}
return true;
}
bool BoxInside(const glm::dvec3 &origin, const glm::dvec3 &extent) const {
glm::dvec4 p[8];
for (int i = 0; i < 8; ++i) {
p[i] =
glm::dvec4{(i & 1) ? origin.x + extent.x : origin.x - extent.x,
(i & 2) ? origin.y + extent.y : origin.y - extent.y,
(i & 4) ? origin.z + extent.z : origin.z - extent.z, 1.};
}
for (const auto &plane : m_planes) {
bool inside = false;
for (int j = 0; j < 8; ++j) {
if (glm::dot(p[j], plane) > 0.) {
inside = true;
break;
}
}
if (!inside) return false;
}
return true;
}
bool BoxInside(const glm::dmat4x3 &transform, const glm::dvec3 &origin,
const glm::dvec3 &extent) const {
glm::dvec4 p[8];
for (int i = 0; i < 8; ++i) {
p[i] =
glm::dvec4{(i & 1) ? origin.x + extent.x : origin.x - extent.x,
(i & 2) ? origin.y + extent.y : origin.y - extent.y,
(i & 4) ? origin.z + extent.z : origin.z - extent.z, 1.};
p[i] = glm::dvec4(transform * p[i], 1.);
}
for (const auto &plane : m_planes) {
bool inside = false;
for (int j = 0; j < 8; ++j) {
if (glm::dot(p[j], plane) > 0.) {
inside = true;
break;
}
}
if (!inside) return false;
}
return true;
}
};
struct SingleFrustumTester : public IFrustumTester {
Frustum m_frustum;
SingleFrustumTester(const glm::dmat4 &projection, const glm::dmat4 &view)
: m_frustum(projection * view) {}
virtual bool IntersectSphere(const glm::dvec3 &origin,
double radius) const override {
return m_frustum.SphereInside(origin, radius);
}
virtual bool IntersectBox(const glm::dvec3 &origin,
const glm::dvec3 &extent) const override {
return m_frustum.BoxInside(origin, extent);
}
virtual bool IntersectBox(const glm::dmat4x3 &transform,
const glm::dvec3 &origin,
const glm::dvec3 &extent) const override {
return m_frustum.BoxInside(transform, origin, extent);
}
};
struct CubeFrustumTester : public IFrustumTester {
Frustum m_frustum;
CubeFrustumTester()
: m_frustum(glm::orthoRH_ZO(-Global.reflectiontune.range_instances,
Global.reflectiontune.range_instances,
-Global.reflectiontune.range_instances,
Global.reflectiontune.range_instances,
-Global.reflectiontune.range_instances,
Global.reflectiontune.range_instances)) {}
virtual bool IntersectSphere(const glm::dvec3 &origin,
double radius) const override {
return m_frustum.SphereInside(origin, radius);
}
virtual bool IntersectBox(const glm::dvec3 &origin,
const glm::dvec3 &extent) const override {
return m_frustum.BoxInside(origin, extent);
}
virtual bool IntersectBox(const glm::dmat4x3 &transform,
const glm::dvec3 &origin,
const glm::dvec3 &extent) const override {
return m_frustum.BoxInside(transform, origin, extent);
}
};
if (simulation::is_ready && !Global.gfx_skiprendering) {
if (m_scene_initialized) {
m_fsr->BeginFrame();
glm::dmat4 transform{1.};
RenderPass pass{};
pass.m_draw_shapes = m_draw_shapes;
pass.m_draw_lines = m_draw_lines;
pass.m_draw_tanimobj = m_draw_tanimobj;
pass.m_draw_dynamic = m_draw_dynamic;
pass.m_draw_track = m_draw_track;
pass.m_draw_instances = m_draw_instances;
pass.m_sort_batches = m_sort_batches;
pass.m_sort_transparents = m_sort_transparents;
// modelview
// if (!DebugCameraFlag) {
pass.m_origin = Global.pCamera.Pos;
Global.pCamera.SetMatrix(transform);
pass.m_draw_range = Global.BaseDrawRange * Global.fDistanceFactor;
// if (!Global.headtrack_conf.magic_window || false) {
// pass.m_camera.position() +=
// Global.viewport_move * glm::dmat3(transform);
// transform =
// glm::dmat4(glm::inverse(Global.viewport_rotate)) * transform;
// }
pass.m_frame_index = GetCurrentFrame();
transform = transform *
glm::translate(static_cast<glm::dvec3>(Global.pCamera.Pos));
transform = static_cast<glm::dmat4>(static_cast<glm::dmat3>(transform));
//} else {
// pass.m_camera.position() = Global.pDebugCamera.Pos;
// Global.pDebugCamera.SetMatrix(transform);
//}
// glm::dvec3 view_pos = static_cast<glm::dvec3>(Global.pCamera.Pos);
// glm::dvec3 view_center =
// static_cast<glm::dvec3>(Global.pCamera.LookAt); glm::dvec3 view_up =
// static_cast<glm::dvec3>(Global.pCamera.vUp);
//
// glm::dvec3 view_dir = glm::rotateY(
// glm::rotateX(glm::rotateZ(-view_center, Global.pCamera.Angle.z),
// Global.pCamera.Angle.x),
// Global.pCamera.Angle.y);
//
// transform = glm::lookAtRH(view_pos, view_pos + view_dir, view_up);
m_drawcall_counter = nullptr;
m_drawcalls_dynamic.Reset();
m_drawcalls_shape.Reset();
m_drawcalls_line.Reset();
m_drawcalls_tanimobj.Reset();
m_drawcalls_track.Reset();
m_drawcalls_instances.Reset();
pass.m_type = RenderPassType::Deferred;
pass.m_framebuffer = m_gbuffer->m_framebuffer;
auto info = pass.m_framebuffer->getFramebufferInfo();
pass.m_viewport_state =
nvrhi::ViewportState().addViewportAndScissorRect(info.getViewport());
double fov = glm::radians(static_cast<double>(Global.FieldOfView) /
Global.ZoomFactor);
glm::dmat4 projection = glm::perspectiveFovRH_ZO(
fov, static_cast<double>(Global.window_size.x),
static_cast<double>(Global.window_size.y), pass.m_draw_range, .1);
SingleFrustumTester frustum_tester{projection, transform};
DrawConstants draw_constants{};
glm::dmat4 jitter = m_fsr->GetJitterMatrix();
draw_constants.m_jittered_projection =
static_cast<glm::mat4>(jitter * projection);
draw_constants.m_projection = static_cast<glm::mat4>(projection);
draw_constants.m_projection_history =
std::exchange(m_previous_projection, draw_constants.m_projection);
uint64_t sky_instance_id;
{
nvrhi::CommandListHandle command_list_sky =
m_backend->GetDevice()->createCommandList();
command_list_sky->open();
command_list_sky->beginMarker("Sky LUT render");
m_sky->Render(command_list_sky, projection, transform);
command_list_sky->endMarker();
command_list_sky->close();
sky_instance_id =
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);
{
glm::dvec4 mouse_ro_norm =
inverse(projection) * glm::dvec4(mousepos, 1., 1.);
mouse_ro_norm /= mouse_ro_norm.w;
m_mouse_ro = pass.m_origin + static_cast<glm::dvec3>(
inverse(transform) *
glm::dvec4(mouse_ro_norm.xyz, 1.));
}
{
glm::dvec4 mouse_rd_norm =
inverse(projection) * glm::dvec4(mousepos, 0., 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);
}
nvrhi::CommandListHandle command_list =
m_backend->GetDevice()->createCommandList();
command_list->open();
command_list->beginMarker("Application render");
command_list->beginMarker("Render scene");
pass.m_projection = projection;
pass.m_command_list_preparation = command_list;
pass.m_command_list_draw = command_list;
m_gbuffer->Clear(pass.m_command_list_draw);
pass.m_command_list_preparation->writeBuffer(
m_drawconstant_buffer, &draw_constants, sizeof(draw_constants));
pass.m_frustum_tester = &frustum_tester;
pass.m_transform = transform;
pass.m_history_transform =
std::exchange(m_previous_view, pass.m_transform);
command_list->beginMarker("Depth only");
pass.m_type = RenderPassType::DepthOnly;
RenderKabina(pass);
RenderShapes(pass);
RenderBatches(pass);
RenderTracks(pass);
RenderAnimateds(pass);
RenderLines(pass);
command_list->endMarker();
command_list->beginMarker("Gbuffer fill");
pass.m_type = RenderPassType::Deferred;
Timer::subsystem.gfx_color.start();
RenderKabina(pass);
RenderShapes(pass);
RenderBatches(pass);
RenderTracks(pass);
RenderAnimateds(pass);
RenderLines(pass);
command_list->endMarker();
GatherSpotLights(pass);
Timer::subsystem.gfx_color.stop();
glm::dmat4 view_proj = projection * transform;
glm::dmat4 previous_view_proj =
std::exchange(m_previous_view_proj, view_proj);
glm::dmat4 reproject = glm::inverse(view_proj) * previous_view_proj;
command_list->beginMarker("Render skybox");
m_environment->Render(command_list, jitter, projection, transform,
previous_view_proj);
command_list->endMarker();
command_list->endMarker();
{ // Shadow render pass
Timer::subsystem.gfx_shadows.start();
command_list->beginMarker("Shadow render");
glm::vec3 light_color, light_direction;
m_sky->CalcLighting(light_direction, light_color);
m_shadow_map->CalcCascades(
glm::perspectiveFovRH_ZO(fov,
static_cast<double>(Global.window_size.x),
static_cast<double>(Global.window_size.y),
.1, pass.m_draw_range),
transform, -light_direction);
m_shadow_map->UpdateConstants(command_list);
m_gbuffer_shadow->Clear(command_list);
if (dot(light_color, light_color) > 0.) {
for (int cascade = 0;
cascade < m_shadow_map->m_cascade_matrices.size(); ++cascade) {
RenderPass pass{};
pass.m_draw_shapes =
m_draw_shapes; // && Global.shadowtune.fidelity >= 0;
pass.m_draw_lines =
m_draw_lines; // && Global.shadowtune.fidelity >= 0;
pass.m_draw_tanimobj =
m_draw_tanimobj; // && Global.reflectiontune.fidelity >= 1;
pass.m_draw_dynamic =
m_draw_dynamic; // && Global.reflectiontune.fidelity >= 2;
pass.m_draw_track =
m_draw_track; // && Global.reflectiontune.fidelity >= 0;
pass.m_draw_instances = m_draw_instances;
pass.m_sort_batches = m_sort_batches;
pass.m_sort_transparents = m_sort_transparents;
SingleFrustumTester frustum_tester{
m_shadow_map->m_cascade_matrices_clamped[cascade],
glm::dmat4{1.}};
pass.m_origin = Global.pCamera.Pos;
pass.m_transform = glm::dmat3{1.};
// pass.m_camera.position() =
// simulation::Train->Dynamic()->vPosition;
pass.m_draw_range = 150.;
pass.m_type = RenderPassType::ShadowMap;
pass.m_framebuffer =
m_gbuffer_shadow->m_slice_framebuffers[cascade];
pass.m_command_list_draw = command_list;
pass.m_command_list_preparation = command_list;
pass.m_frustum_tester = &frustum_tester;
pass.m_projection = m_shadow_map->m_cascade_matrices[cascade];
auto info = pass.m_framebuffer->getFramebufferInfo();
pass.m_viewport_state =
nvrhi::ViewportState().addViewportAndScissorRect(
info.getViewport());
// CullBatches(pass);
// for (int i = 0.; i < 6.; ++i) {
// pass.m_viewport_state.addViewportAndScissorRect(
// nvrhi::Viewport(i * 2048, (i + 1) * 2048, 0, 2048, 0, 1));
// }
RenderKabina(pass);
RenderShapes(pass);
RenderBatches(pass);
RenderTracks(pass);
RenderAnimateds(pass);
}
}
Timer::subsystem.gfx_shadows.stop();
command_list->endMarker();
}
if (m_environment->IsReady()) { // CubeMap render pass
command_list->beginMarker("Envmap render");
Timer::subsystem.gfx_reflections.start();
// CubeDrawConstants draw_constants{};
// for (int i = 0; i < std::size(draw_constants.m_face_projection); ++i)
// {
// draw_constants.m_face_projection[i] =
// glm::perspectiveFovRH_ZO(
// glm::radians(90.f), 1.f, 1.f, .1f,
// static_cast<float>(Global.BaseDrawRange *
// Global.fDistanceFactor)) *
// glm::lookAtRH(glm::vec3{0.f, 0.f, 0.f}, targets[i], ups[i]);
// }
//
// command_list->writeBuffer(m_cubedrawconstant_buffer, &draw_constants,
// sizeof(draw_constants));
static glm::dvec3 targets[]{{-1., 0., 0.}, {1., 0., 0.}, {0., 1., 0.},
{0., -1., 0.}, {0., 0., 1.}, {0., 0., -1.}};
static glm::dvec3 ups[]{{0., 1., 0.}, {0., 1., 0.}, {0., 0., -1.},
{0., 0., 1.}, {0., 1., 0.}, {0., 1., 0.}};
glm::dvec3 current_camera_pos = Global.pCamera.Pos;
uint64_t current_frame = GetCurrentFrame();
uint64_t previous_frame =
std::exchange(m_previous_env_frame, current_frame);
m_gbuffer_cube->Clear(command_list);
for (int face = 0; face < 6; ++face) {
m_instance_cache->Clear();
glm::dmat4 transform =
glm::lookAtRH(glm::dvec3{0., 0., 0.}, targets[face], ups[face]);
glm::dmat4 projection = glm::perspectiveFovRH_ZO(
M_PI_2, 1., 1.,
static_cast<double>(Global.reflectiontune.range_instances), .1);
SingleFrustumTester frustum_tester{projection, transform};
RenderPass pass{};
pass.m_transform = transform;
pass.m_draw_shapes =
m_draw_shapes && Global.reflectiontune.fidelity >= 0;
pass.m_draw_lines =
m_draw_lines && Global.reflectiontune.fidelity >= 2;
pass.m_draw_tanimobj =
m_draw_tanimobj && Global.reflectiontune.fidelity >= 1;
pass.m_draw_dynamic =
m_draw_dynamic && Global.reflectiontune.fidelity >= 2;
pass.m_draw_track =
m_draw_track && Global.reflectiontune.fidelity >= 0;
pass.m_draw_instances = m_draw_instances;
pass.m_sort_batches = m_sort_batches;
pass.m_sort_transparents = m_sort_transparents;
pass.m_origin = current_camera_pos;
// pass.m_camera.position() = simulation::Train->Dynamic()->vPosition;
pass.m_draw_range = Global.BaseDrawRange * Global.fDistanceFactor;
pass.m_type = RenderPassType::CubeMap;
pass.m_framebuffer = m_gbuffer_cube->m_slice_framebuffers[face];
pass.m_command_list_draw = command_list;
pass.m_command_list_preparation = command_list;
pass.m_frustum_tester = &frustum_tester;
pass.m_projection = projection;
auto info = pass.m_framebuffer->getFramebufferInfo();
pass.m_viewport_state =
nvrhi::ViewportState().addViewportAndScissorRect(
info.getViewport());
// for (int i = 0.; i < 6.; ++i) {
// pass.m_viewport_state.addViewportAndScissorRect(
// nvrhi::Viewport(i * 2048, (i + 1) * 2048, 0, 2048, 0, 1));
// }
// CullBatches(pass);
RenderShapes(pass);
RenderBatches(pass);
RenderTracks(pass);
RenderAnimateds(pass);
}
Timer::subsystem.gfx_reflections.stop();
command_list->endMarker();
m_envir_can_filter = true;
}
m_contact_shadows->UpdateConstants(command_list, projection, transform,
Global.DayLight.direction,
pass.m_frame_index);
m_contact_shadows->Render(command_list);
m_ssao->Render(command_list, draw_constants.m_projection,
pass.m_frame_index);
// m_sky_transmittance->Render(command_list);
Timer::subsystem.gfx_swap.start();
command_list->beginMarker("Deferred lights");
m_gbuffer_lighting->Render(pass);
command_list->endMarker();
command_list->beginMarker("GBuffer blit");
m_gbuffer_blit->Render(command_list, transform, jitter * projection);
command_list->endMarker();
if (true) {
m_windshield_rain->Render(pass);
command_list->beginMarker("Forward pass");
pass.m_framebuffer = m_framebuffer_forward;
pass.m_type = RenderPassType::Forward;
RenderShapes(pass);
RenderAnimateds(pass);
RenderKabina(pass);
command_list->endMarker();
}
m_auto_exposure->Render(command_list);
m_fsr->Render(command_list, pass.m_draw_range, .1, fov);
m_bloom->Render(command_list);
m_tonemap->Render(command_list);
for (auto &bank : m_geometry_banks) {
for (auto &chunk : bank.m_chunks) {
if (glm::distance2(chunk.m_last_position_requested, pass.m_origin) >
Global.BaseDrawRange * Global.BaseDrawRange) {
chunk.m_is_uptodate = false;
chunk.m_index_buffer = nullptr;
chunk.m_vertex_buffer = nullptr;
}
}
}
for (auto &material : m_material_cache) {
if (glm::distance2(material.m_last_position_requested, pass.m_origin) >
10 * 10) {
material.m_binding_set = nullptr;
material.m_binding_set_cubemap = nullptr;
material.m_binding_set_shadow = nullptr;
}
}
command_list->endMarker();
command_list->close();
m_backend->GetDevice()->executeCommandList(command_list);
GetTextureManager()->Cleanup(pass.m_origin);
for (auto &material : m_material_cache) {
if (GetCurrentFrame() - material.m_last_frame_requested > 100) {
material.m_binding_sets.fill(nullptr);
material.m_last_texture_updates.fill(0);
}
}
Timer::subsystem.gfx_swap.stop();
if (m_envir_can_filter)
m_environment->Filter(sky_instance_id, m_gbuffer_cube.get());
} else {
// Prepare all section geometries to be uploaded to gpu as soon as we need
// them
for (auto section : simulation::Region->m_sections) {
if (!section) continue;
section->create_geometry();
}
GatherModelsForBatching();
GatherTracksForBatching();
GatherShapesForBatching();
GatherAnimatedsForBatching();
GatherCellsForAnimation();
GatherDynamics();
// RenderPass pass{};
// pass.m_command_list_preparation = command_list;
// for (uint32_t bank = 0; bank < m_geometry_banks.size(); ++bank) {
// for (uint32_t chunk = 0; chunk <
// m_geometry_banks[bank].m_chunks.size();
// ++chunk) {
// m_geometry_banks[bank].m_chunks[chunk].m_last_frame_requested =
// GetCurrentFrame();
// UpdateGeometry({bank + 1, chunk + 1}, pass);
// }
// }
m_scene_initialized = true;
}
}
m_previous_env_position = Global.pCamera.Pos;
// Timer::subsystem.gfx_reflections.start();
// Timer::subsystem.gfx_reflections.stop();
Timer::subsystem.gfx_gui.start();
DebugUi();
m_sky->OnGui();
m_bloom->OnGui();
Application.render_ui();
Timer::subsystem.gfx_gui.stop();
Timer::subsystem.gfx_total.stop();
return true;
}
void NvRenderer::SwapBuffers() {
m_backend->Present();
// if (!m_upload_event_query ||
// m_backend->GetDevice()->pollEventQuery(m_upload_event_query)) {
// {
// m_upload_event_query = m_backend->GetDevice()->createEventQuery();
// size_t frame_index = GetCurrentFrame();
//
// for (auto &bank : m_geometry_banks) {
// if (bank.m_last_frame_requested == frame_index &&
// !bank.m_is_uptodate) {
// bank.m_uploaded_query = m_upload_event_query;
// }
// }
//
// RenderPass pass{};
// pass.m_type = RenderPassType::Deferred;
// pass.m_frame_index = frame_index;
// pass.m_command_list_preparation =
// m_backend->GetDevice()->createCommandList();
// pass.m_command_list_preparation->open();
// for (gfx::geometry_handle handle{1, 0};
// handle.bank <= m_geometry_banks.size(); ++handle.bank) {
// auto &bank = m_geometry_banks[handle.bank - 1];
// if (bank.m_is_uptodate) continue;
// UpdateGeometryBank(handle, pass, true);
// }
// pass.m_command_list_preparation->close();
// m_backend->GetDevice()->executeCommandList(pass.m_command_list_preparation);
// m_backend->GetDevice()->setEventQuery(m_upload_event_query,
// nvrhi::CommandQueue::Graphics);
// }
// }
m_backend->GetDevice()->runGarbageCollection();
}
float NvRenderer::Framerate() {
return 1000.f / (Timer::subsystem.mainloop_total.average());
// return 0.0f;
}
gfx::geometrybank_handle NvRenderer::Create_Bank() {
gfx::geometrybank_handle handle{
static_cast<uint32_t>(m_geometry_banks.size() + 1), 0};
auto &bank = m_geometry_banks.emplace_back();
return handle;
}
gfx::geometry_handle NvRenderer::Insert(
gfx::index_array &Indices, gfx::vertex_array &Vertices,
gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry,
int const Type) {
if (!Type || Type >= TP_ROTATOR) return {};
auto &bank = m_geometry_banks[Geometry.bank - 1];
gfx::geometrybank_handle handle{
Geometry.bank, static_cast<uint32_t>(bank.m_chunks.size() + 1)};
auto &chunk = bank.m_chunks.emplace_back();
chunk.m_indices = std::move(Indices);
chunk.m_vertices = std::move(Vertices);
switch (Type) {
case GL_TRIANGLES:
break;
case GL_LINES:
case GL_LINE_LOOP:
case GL_LINE_STRIP:
break;
default:
break;
}
chunk.UpdateBounds();
chunk.m_is_uptodate = false;
bank.m_is_uptodate = false;
return handle;
}
gfx::geometry_handle NvRenderer::Insert(
gfx::vertex_array &Vertices, gfx::userdata_array &Userdata,
gfx::geometrybank_handle const &Geometry, int const Type) {
if (!Type || Type >= TP_ROTATOR) return {};
auto &bank = m_geometry_banks[Geometry.bank - 1];
gfx::geometrybank_handle handle{
Geometry.bank, static_cast<uint32_t>(bank.m_chunks.size() + 1)};
auto &chunk = bank.m_chunks.emplace_back();
chunk.m_indices = {};
gfx::calculate_tangents(Vertices, {}, Type);
switch (Type) {
case GL_TRIANGLES:
chunk.m_vertices = std::move(Vertices);
break;
case GL_TRIANGLE_STRIP:
chunk.m_vertices = std::move(Vertices);
for (int i = 2; i < std::size(chunk.m_vertices); ++i) {
if (i % 2) {
chunk.m_indices.emplace_back(i);
chunk.m_indices.emplace_back(i - 1);
chunk.m_indices.emplace_back(i - 2);
} else {
chunk.m_indices.emplace_back(i - 2);
chunk.m_indices.emplace_back(i - 1);
chunk.m_indices.emplace_back(i);
}
}
break;
case GL_TRIANGLE_FAN:
chunk.m_vertices = std::move(Vertices);
for (int i = 2; i < std::size(chunk.m_vertices); ++i) {
chunk.m_indices.emplace_back(0);
chunk.m_indices.emplace_back(i - 1);
chunk.m_indices.emplace_back(i);
}
break;
case GL_LINES:
chunk.m_vertices = std::move(Vertices);
break;
case GL_LINE_LOOP:
chunk.m_vertices = std::move(Vertices);
chunk.m_indices.reserve(chunk.m_vertices.size() * 2);
for (int i = 1; i <= chunk.m_vertices.size(); ++i) {
chunk.m_indices.emplace_back(i - 1);
chunk.m_indices.emplace_back(i % chunk.m_vertices.size());
}
break;
case GL_LINE_STRIP:
chunk.m_vertices = std::move(Vertices);
chunk.m_indices.reserve((chunk.m_vertices.size() - 1) * 2);
for (int i = 1; i < chunk.m_vertices.size(); ++i) {
chunk.m_indices.emplace_back(i - 1);
chunk.m_indices.emplace_back(i);
}
break;
default:;
break;
}
chunk.UpdateBounds();
chunk.m_is_uptodate = false;
bank.m_is_uptodate = false;
return handle;
}
bool NvRenderer::Replace(gfx::vertex_array &Vertices,
gfx::userdata_array &Userdata,
gfx::geometry_handle const &Geometry, int const Type,
std::size_t const Offset) {
if (!Type || Type >= TP_ROTATOR) return false;
auto &bank = m_geometry_banks[Geometry.bank - 1];
auto &chunk = bank.m_chunks[Geometry.chunk - 1];
gfx::calculate_tangents(Vertices, {}, Type);
if (chunk.m_replace_impl) return chunk.m_replace_impl(Vertices, Type);
chunk.m_indices = {};
switch (Type) {
case GL_TRIANGLES:
chunk.m_vertices = std::move(Vertices);
break;
case GL_TRIANGLE_STRIP:
chunk.m_vertices = std::move(Vertices);
for (int i = 2; i < std::size(chunk.m_vertices); ++i) {
if (i % 2) {
chunk.m_indices.emplace_back(i);
chunk.m_indices.emplace_back(i - 1);
chunk.m_indices.emplace_back(i - 2);
} else {
chunk.m_indices.emplace_back(i - 2);
chunk.m_indices.emplace_back(i - 1);
chunk.m_indices.emplace_back(i);
}
}
break;
case GL_LINES:
case GL_LINE_LOOP:
case GL_LINE_STRIP:
break;
default:
break;
}
chunk.UpdateBounds();
chunk.m_is_uptodate = false;
bank.m_is_uptodate = false;
return true;
}
bool NvRenderer::Append(gfx::vertex_array &Vertices,
gfx::userdata_array &Userdata,
gfx::geometry_handle const &Geometry, int const Type) {
return false;
}
gfx::index_array const &NvRenderer::Indices(
gfx::geometry_handle const &handle) const {
// if (!handle.bank || !handle.chunk || handle.bank > m_geometry_banks.size()
// ||
// handle.chunk > m_geometry_banks[handle.bank - 1].m_chunks.size())
// return {};
return m_geometry_banks[handle.bank - 1].m_chunks[handle.chunk - 1].m_indices;
}
gfx::vertex_array const &NvRenderer::Vertices(
gfx::geometry_handle const &handle) const {
// if (!handle.bank || !handle.chunk || handle.bank > m_geometry_banks.size()
// ||
// handle.chunk > m_geometry_banks[handle.bank - 1].m_chunks.size())
// return {};
return m_geometry_banks[handle.bank - 1]
.m_chunks[handle.chunk - 1]
.m_vertices;
}
const gfx::userdata_array &NvRenderer::UserData(
const gfx::geometry_handle &Geometry) const {
const static gfx::userdata_array array{};
return array;
}
material_handle NvRenderer::Fetch_Material(std::string const &Filename,
bool const Loadnow) {
auto filename{Filename};
if (contains(filename, '|')) {
filename.erase(
filename.find('|')); // po | może być nazwa kolejnej tekstury
}
// discern references to textures generated by a script
// TBD: support file: for file resources?
// process supplied resource name
if (auto const isgenerated{filename.find("make:") == 0 ||
filename.find("internal_src:") == 0}) {
// generated resource
// scheme:(user@)path?query
// remove scheme indicator
// filename.erase(0, filename.find(':') + 1);
//// TBD, TODO: allow shader specification as part of the query?
// erase_leading_slashes(filename);
} else {
// regular file resource
// (filepath/)filename.extension
erase_extension(filename);
replace_slashes(filename);
erase_leading_slashes(filename);
}
const auto &[it, is_new] = m_material_map.try_emplace(filename, -1);
if (is_new) {
MaterialAdapter adapter{};
adapter.Parse(filename);
auto shader = adapter.GetShader();
MaterialTemplate *material_template = nullptr;
if (const auto template_found =
m_material_templates.find(adapter.GetShader());
template_found != m_material_templates.end()) {
material_template = template_found->second.get();
}
auto &cache = m_material_cache.emplace_back(material_template);
cache.Init();
it->second = static_cast<int>(m_material_cache.size());
cache.m_name = filename;
cache.m_opacity = adapter.GetOpacity();
cache.m_shadow_rank = adapter.GetShadowRank();
cache.m_size = adapter.GetSize();
cache.m_masked_shadow = false;
auto texture_manager = GetTextureManager();
for (int i = 0; i < material_template->m_texture_bindings.size(); ++i) {
const auto &binding = material_template->m_texture_bindings[i];
auto handle = texture_manager->FetchTexture(
static_cast<std::string>(
adapter.GetTexturePathForEntry(binding.m_name)),
binding.m_hint, adapter.GetTextureSizeBiasForEntry(binding.m_name),
true);
cache.m_texture_handles[i] = handle;
auto traits = texture_manager->GetTraits(handle);
traits[MaTextureTraits_NoFilter] = binding.disable_filter;
traits[MaTextureTraits_NoAnisotropy] = binding.disable_anisotropy;
traits[MaTextureTraits_NoMipBias] = binding.disable_mip_bias;
cache.RegisterTexture(binding.m_name.c_str(), handle);
cache.RegisterResource(false, binding.m_sampler_name.c_str(),
texture_manager->GetSamplerForTraits(
traits, RenderPassType::Deferred),
nvrhi::ResourceType::Sampler);
if (binding.m_name == cache.m_template->m_masked_shadow_texture &&
texture_manager->IsValidHandle(handle)) {
cache.m_masked_shadow =
texture_manager->GetTexture(handle)->m_has_alpha;
}
}
// cache.m_textures.resize(cache.m_template->m_texture_bindings.size());
// for (int i = 0; i < cache.m_textures.size(); ++i) {
// const auto &binding = cache.m_template->m_texture_bindings[i];
// cache.m_textures[i] = ;
// if (i == cache.m_template->m_masked_shadow_texture &&
// m_texture_manager->IsValidHandle(cache.m_textures[i])) {
// cache.m_masked_shadow =
// m_texture_manager->GetTexture(cache.m_textures[i])->m_has_alpha;
// }
// }
// if (auto masked_shadow = adapter.GetMaskedShadowOverride())
// cache.m_masked_shadow = *masked_shadow;
cache.m_pipelines.fill(nullptr);
for (int j = 0; j < static_cast<int>(DrawType::Num); ++j) {
for (int i = 0; i < static_cast<int>(RenderPassType::Num); ++i) {
const auto pass = static_cast<RenderPassType>(i);
const auto draw = static_cast<DrawType>(j);
auto pipeline =
cache.m_template->GetPipeline(pass, draw, cache.m_masked_shadow);
cache.m_pipelines[Constants::GetPipelineIndex(pass, draw)] = pipeline;
}
}
}
return it->second;
}
void NvRenderer::Bind_Material(material_handle const Material,
TSubModel const *sm,
lighting_data const *lighting) {}
IMaterial const *NvRenderer::Material(material_handle const handle) const {
if (!handle || handle > m_material_cache.size()) {
return &m_material_cache[0];
}
return &m_material_cache[handle - 1];
}
std::shared_ptr<gl::program> NvRenderer::Fetch_Shader(std::string const &name) {
return std::shared_ptr<gl::program>();
}
texture_handle NvRenderer::Fetch_Texture(std::string const &Filename,
bool const Loadnow,
GLint format_hint) {
return GetTextureManager()->FetchTexture(Filename, format_hint, 0, false);
}
void NvRenderer::Bind_Texture(texture_handle const Texture) {}
void NvRenderer::Bind_Texture(std::size_t const Unit,
texture_handle const Texture) {}
ITexture &NvRenderer::Texture(texture_handle const Texture) {
if (auto texture_manager = GetTextureManager();
texture_manager->IsValidHandle(Texture))
return *texture_manager->GetTexture(Texture);
return *ITexture::null_texture();
}
ITexture const &NvRenderer::Texture(texture_handle const Texture) const {
if (auto texture_manager = GetTextureManager();
texture_manager->IsValidHandle(Texture))
return *texture_manager->GetTexture(Texture);
return *ITexture::null_texture();
}
void NvRenderer::Pick_Control_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 m_picked_submodel; }
scene::basic_node const *NvRenderer::Pick_Node() const { return nullptr; }
glm::dvec3 NvRenderer::Mouse_Position() const { return glm::dvec3(); }
void NvRenderer::Update(double const Deltatime) {}
void NvRenderer::Update_Pick_Control() {}
void NvRenderer::Update_Pick_Node() {}
glm::dvec3 NvRenderer::Update_Mouse_Position() { return glm::dvec3(); }
bool NvRenderer::Debug_Ui_State(std::optional<bool> param) {
if (param) {
m_debug_ui_active = *param;
}
return m_debug_ui_active;
}
std::string const &NvRenderer::info_times() const {
// TODO: insert return statement here
static std::string placeholder = "";
return placeholder;
}
std::string const &NvRenderer::info_stats() const {
// TODO: insert return statement here
static std::string placeholder = "";
return placeholder;
}
imgui_renderer *NvRenderer::GetImguiRenderer() {
return m_imgui_renderer.get();
}
void NvRenderer::MakeScreenshot() {
nvrhi::CommandListHandle command_list =
GetBackend()->GetDevice()->createCommandList();
command_list->open();
nvrhi::ITexture *source = m_tonemap->m_source;
nvrhi::TextureDesc staging_desc = source->getDesc();
nvrhi::StagingTextureHandle staging_texture =
GetBackend()->GetDevice()->createStagingTexture(
staging_desc, nvrhi::CpuAccessMode::Read);
command_list->copyTexture(staging_texture, nvrhi::TextureSlice(), source,
nvrhi::TextureSlice());
command_list->close();
GetBackend()->GetDevice()->executeCommandList(command_list);
size_t row_pitch;
auto map = GetBackend()->GetDevice()->mapStagingTexture(
staging_texture, nvrhi::TextureSlice().resolve(staging_desc),
nvrhi::CpuAccessMode::Read, &row_pitch);
std::string output{};
output.resize(row_pitch * staging_desc.height);
memcpy(output.data(), map, output.size());
GetBackend()->GetDevice()->unmapStagingTexture(staging_texture);
{
EXRHeader header;
InitEXRHeader(&header);
EXRImage image;
InitEXRImage(&image);
image.num_channels = 3;
std::vector<uint16_t> images[3];
images[0].resize(staging_desc.width * staging_desc.height);
images[1].resize(staging_desc.width * staging_desc.height);
images[2].resize(staging_desc.width * staging_desc.height);
// Split RGBRGBRGB... into R, G and B layer
for (int y = 0; y < staging_desc.height; ++y) {
const uint16_t *src =
reinterpret_cast<uint16_t *>(output.data() + row_pitch * y);
size_t row_start = y * staging_desc.width;
for (int x = 0; x < staging_desc.width; ++x) {
images[0][row_start + x] = src[4 * x + 0];
images[1][row_start + x] = src[4 * x + 1];
images[2][row_start + x] = src[4 * x + 2];
}
}
uint16_t *image_ptr[3];
image_ptr[0] = images[2].data(); // B
image_ptr[1] = images[1].data(); // G
image_ptr[2] = images[0].data(); // R
image.images = reinterpret_cast<unsigned char **>(image_ptr);
image.width = static_cast<int>(staging_desc.width);
image.height = static_cast<int>(staging_desc.height);
header.num_channels = 3;
header.channels = static_cast<EXRChannelInfo *>(
malloc(sizeof(EXRChannelInfo) * header.num_channels));
// Must be (A)BGR order, since most of EXR viewers expect this channel
// order.
strncpy(header.channels[0].name, "B", 255);
header.channels[0].name[strlen("B")] = '\0';
strncpy(header.channels[1].name, "G", 255);
header.channels[1].name[strlen("G")] = '\0';
strncpy(header.channels[2].name, "R", 255);
header.channels[2].name[strlen("R")] = '\0';
header.channels[0].p_linear = 1;
header.channels[1].p_linear = 1;
header.channels[2].p_linear = 1;
header.pixel_types =
static_cast<int *>(malloc(sizeof(int) * header.num_channels));
header.requested_pixel_types =
static_cast<int *>(malloc(sizeof(int) * header.num_channels));
for (int i = 0; i < header.num_channels; i++) {
header.pixel_types[i] =
TINYEXR_PIXELTYPE_HALF; // pixel type of input image
header.requested_pixel_types[i] =
TINYEXR_PIXELTYPE_HALF; // pixel type of output image to be stored in
// .EXR
}
const char *err = nullptr; // or nullptr in C++11 or later.
if (int ret = SaveEXRImageToFile(
&image, &header,
fmt::format("{}@{}_{:%Y%m%d_%H%M%S}.exr",
simulation::Train != nullptr
? simulation::Train->Occupied()->Name
: "",
Global.SceneryFile, fmt::localtime(std::time(nullptr)))
.c_str(),
&err);
ret != TINYEXR_SUCCESS) {
fprintf(stderr, "Save EXR err: %s\n", err);
FreeEXRErrorMessage(err); // free's buffer for an error message
//__debugbreak();
}
// printf("Saved exr file. [ %s ] \n", outfilename);
free(header.channels);
free(header.pixel_types);
free(header.requested_pixel_types);
}
}
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;
}
NvRenderer::GeometryBounds NvRenderer::GetGeometryBounds(
gfx::geometry_handle const &handle) const {
if (!handle.bank || handle.bank > m_geometry_banks.size()) return {};
auto &bank = m_geometry_banks[handle.bank - 1];
if (!handle.chunk || handle.chunk > bank.m_chunks.size()) return {};
auto &chunk = bank.m_chunks[handle.chunk - 1];
return {chunk.m_origin, chunk.m_extent};
}
size_t NvRenderer::GetCurrentFrame() const { return m_fsr->m_current_frame; }
void NvRenderer::DebugUi() {
if (!m_debug_ui_active) return;
if (ImGui::Begin("Renderer Debug", &m_debug_ui_active,
ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::LabelText("Draws Dynamic", "%d (%d triangles)",
m_drawcalls_dynamic.m_drawcalls,
m_drawcalls_dynamic.m_triangles);
ImGui::LabelText("Draws TAnimObj", "%d (%d triangles)",
m_drawcalls_tanimobj.m_drawcalls,
m_drawcalls_tanimobj.m_triangles);
ImGui::LabelText("Draws Shape", "%d (%d triangles)",
m_drawcalls_shape.m_drawcalls,
m_drawcalls_shape.m_triangles);
ImGui::LabelText("Draws Line", "%d (%d triangles)",
m_drawcalls_line.m_drawcalls,
m_drawcalls_line.m_triangles);
ImGui::LabelText("Draws Track", "%d (%d triangles)",
m_drawcalls_track.m_drawcalls,
m_drawcalls_track.m_triangles);
ImGui::LabelText("Draws Instances", "%d (%d triangles)",
m_drawcalls_instances.m_drawcalls,
m_drawcalls_instances.m_triangles);
ImGui::Checkbox("Draw Dynamics", &m_draw_dynamic);
ImGui::Checkbox("Draw TAnimObjs", &m_draw_tanimobj);
ImGui::Checkbox("Draw Shapes", &m_draw_shapes);
ImGui::Checkbox("Draw Lines", &m_draw_lines);
ImGui::Checkbox("Draw Tracks", &m_draw_track);
ImGui::Checkbox("Draw Instances", &m_draw_instances);
ImGui::Checkbox("Sort Batches", &m_sort_batches);
ImGui::Checkbox("Sort Transparents", &m_sort_transparents);
ImGui::Checkbox("Pause Animation updates", &m_pause_animations);
if (ImGui::Button("Sky Config")) m_sky->ShowGui();
ImGui::SameLine();
if (ImGui::Button("Bloom Config")) m_bloom->ShowGui();
ImGui::SameLine();
m_ssao->OnGui(ImGui::Button("GTAO Config"));
ImGui::SameLine();
if (ImGui::Button("Take Screenshot")) MakeScreenshot();
}
ImGui::End();
}
void NvRenderer::UpdateGeometry(gfx::geometry_handle handle,
const RenderPass &pass, bool *p_is_uploading) {
if (!handle.bank || handle.bank > m_geometry_banks.size()) return;
auto &bank = m_geometry_banks[handle.bank - 1];
if (!handle.chunk || handle.chunk > bank.m_chunks.size()) return;
auto &chunk = bank.m_chunks[handle.chunk - 1];
bool is_uploading =
chunk.m_chunk_ready_handle &&
!m_backend->GetDevice()->pollEventQuery(chunk.m_chunk_ready_handle);
if (p_is_uploading) *p_is_uploading = is_uploading;
if (is_uploading) return;
chunk.m_chunk_ready_handle = nullptr;
if (chunk.m_is_uptodate) return; // Nothing to do
if (chunk.m_last_frame_requested != pass.m_frame_index) {
return;
}
// chunk.m_index_offset = index_count;
// chunk.m_vertex_offset = vertex_count;
chunk.m_indexed = !!chunk.m_indices.size();
// vertex_count += chunk.m_vertices.size();
// index_count += chunk.m_indices.size();
if (chunk.m_vertices.size() &&
(!chunk.m_vertex_buffer ||
chunk.m_vertices.size() > chunk.m_vertex_count)) {
chunk.m_vertex_buffer = m_backend->GetDevice()->createBuffer(
nvrhi::BufferDesc()
.setIsVertexBuffer(true)
.setByteSize(chunk.m_vertices.size() * sizeof(gfx::basic_vertex))
.setInitialState(nvrhi::ResourceStates::VertexBuffer)
.setKeepInitialState(true));
}
if (chunk.m_indices.size() &&
(!chunk.m_index_buffer || chunk.m_indices.size() > chunk.m_index_count)) {
chunk.m_index_buffer = m_backend->GetDevice()->createBuffer(
nvrhi::BufferDesc()
.setIsIndexBuffer(true)
.setByteSize(chunk.m_indices.size() * sizeof(gfx::basic_index))
.setInitialState(nvrhi::ResourceStates::IndexBuffer)
.setKeepInitialState(true));
}
chunk.m_vertex_count = chunk.m_vertices.size();
chunk.m_index_count = chunk.m_indices.size();
if (chunk.m_vertex_buffer)
pass.m_command_list_preparation->writeBuffer(
chunk.m_vertex_buffer, chunk.m_vertices.data(),
chunk.m_vertices.size() * sizeof(gfx::basic_vertex));
if (chunk.m_index_buffer)
pass.m_command_list_preparation->writeBuffer(
chunk.m_index_buffer, chunk.m_indices.data(),
chunk.m_indices.size() * sizeof(gfx::basic_index));
// nvrhi::CommandListHandle command_list = pass.m_command_list_preparation;
//
// if (big_and_chunky) {
// command_list = m_backend->GetDevice()->createCommandList();
// command_list->open();
// chunk.m_chunk_ready_handle = m_backend->GetDevice()->createEventQuery();
// }
//
// if (chunk.m_vertex_buffer)
// command_list->writeBuffer(
// chunk.m_vertex_buffer, chunk.m_vertices.data(),
// chunk.m_vertices.size() * sizeof(gfx::basic_vertex));
//
// if (chunk.m_index_buffer)
// command_list->writeBuffer(
// chunk.m_index_buffer, chunk.m_indices.data(),
// chunk.m_indices.size() * sizeof(gfx::basic_index));
//
// if (big_and_chunky) {
// command_list->close();
// m_backend->GetDevice()->executeCommandList(command_list);
// m_backend->GetDevice()->setEventQuery(chunk.m_chunk_ready_handle,
// nvrhi::CommandQueue::Graphics);
// }
chunk.m_is_uptodate = true;
// bool needs_initialize_all = false;
//
// if (!bank.m_vertex_buffer || vertex_count > bank.m_vertex_count ||
// index_count > bank.m_vertex_count) {
// // Need to create new set of buffers (either not enough space to upload,
// or
// // not initialized yet)
// if (!allow_full_rebuild) {
// return;
// }
// bank.m_vertex_count = vertex_count;
// bank.m_index_count = index_count;
// if (bank.m_vertex_count)
// bank.m_vertex_buffer = m_backend->GetDevice()->createBuffer(
// nvrhi::BufferDesc()
// .setIsVertexBuffer(true)
// .setByteSize(vertex_count * sizeof(gfx::basic_vertex))
// .setInitialState(nvrhi::ResourceStates::VertexBuffer)
// .setCpuAccess(nvrhi::CpuAccessMode::Write)
// .setKeepInitialState(true));
// if (bank.m_index_count)
// bank.m_index_buffer = m_backend->GetDevice()->createBuffer(
// nvrhi::BufferDesc()
// .setIsIndexBuffer(true)
// .setByteSize(index_count * sizeof(gfx::basic_index))
// .setInitialState(nvrhi::ResourceStates::IndexBuffer)
// .setCpuAccess(nvrhi::CpuAccessMode::Write)
// .setKeepInitialState(true));
// else
// bank.m_index_buffer = nullptr;
// needs_initialize_all = true;
// }
//
//// Upload vertex chunks
// if (bank.m_vertex_buffer) {
// for (auto &chunk : bank.m_chunks) {
// if (!needs_initialize_all && chunk.m_is_uptodate) {
// continue;
// }
// pass.m_command_list_preparation->writeBuffer(
// bank.m_vertex_buffer, chunk.m_vertices.data(),
// chunk.m_vertex_count * sizeof(gfx::basic_vertex),
// chunk.m_vertex_offset * sizeof(gfx::basic_vertex));
// }
// }
//
//// Upload index chunks
// if (bank.m_index_buffer) {
// for (auto &chunk : bank.m_chunks) {
// if (!needs_initialize_all && chunk.m_is_uptodate) {
// continue;
// }
// chunk.m_is_uptodate = true;
// if (chunk.m_indexed)
// pass.m_command_list_preparation->writeBuffer(
// bank.m_index_buffer, chunk.m_indices.data(),
// chunk.m_index_count * sizeof(gfx::basic_index),
// chunk.m_index_offset * sizeof(gfx::basic_index));
// }
// }
// Set uptodate flag
// if (!needs_further_updates) bank.m_is_uptodate = true;
}
nvrhi::InputLayoutHandle NvRenderer::GetInputLayout(DrawType draw_type) {
// auto &input_layout = m_input_layouts[static_cast<size_t>(draw_type)];
// if (!input_layout) {
// switch (draw_type) {
// case DrawType::Model: {
// break;
// }
// }
// }
// return input_layout;
return nullptr;
}
void NvRenderer::UpdateDrawData(const RenderPass &pass,
const glm::dmat4 &transform,
const glm::dmat4 &history_transform,
float opacity_threshold, float opacity_mult,
float selfillum, const glm::vec3 &diffuse) {
switch (pass.m_type) {
case RenderPassType::RendererWarmUp:
return;
case RenderPassType::ShadowMap: {
PushConstantsShadow data{};
data.m_modelviewprojection = pass.m_projection * transform;
data.m_alpha_threshold = opacity_threshold;
pass.m_command_list_draw->setPushConstants(&data, sizeof(data));
break;
}
case RenderPassType::CubeMap: {
PushConstantsCubemap data{};
data.m_modelview = static_cast<glm::mat3x4>(glm::transpose(transform));
data.m_alpha_mult = opacity_mult;
data.m_alpha_threshold = opacity_threshold;
data.m_selfillum =
selfillum * Config()->m_lighting.m_selfillum_multiplier;
data.m_diffuse = diffuse;
pass.m_command_list_draw->setPushConstants(&data, sizeof(data));
break;
}
case RenderPassType::DepthOnly:
case RenderPassType::Deferred:
case RenderPassType::Forward: {
PushConstantsDraw data{};
data.m_modelview = static_cast<glm::mat3x4>(glm::transpose(transform));
data.m_modelview_history =
static_cast<glm::mat3x4>(glm::transpose(history_transform));
data.m_alpha_mult = opacity_mult;
data.m_alpha_threshold = opacity_threshold;
data.m_selfillum =
selfillum * Config()->m_lighting.m_selfillum_multiplier;
data.m_diffuse = diffuse;
pass.m_command_list_draw->setPushConstants(&data, sizeof(data));
break;
}
}
}
void NvRenderer::UpdateDrawDataLine(const RenderPass &pass,
const glm::dmat4 &transform,
const glm::dmat4 &history_transform,
const Line &line) {
switch (pass.m_type) {
case RenderPassType::Deferred:
case RenderPassType::Forward: {
PushConstantsLine data{};
data.m_modelview = static_cast<glm::mat3x4>(glm::transpose(transform));
data.m_modelview_history =
static_cast<glm::mat3x4>(glm::transpose(history_transform));
data.m_color = line.m_color;
data.m_line_weight = line.m_line_width;
data.m_metalness = line.m_metalness;
data.m_roughness = line.m_roughness;
pass.m_command_list_draw->setPushConstants(&data, sizeof(data));
break;
}
default:
return;
}
}
void NvRenderer::BindConstants(const RenderPass &pass,
nvrhi::GraphicsState &gfx_state) {
gfx_state.setFramebuffer(pass.m_framebuffer);
gfx_state.setViewport(pass.m_viewport_state);
// switch (pass.m_type) {
// case RenderPassType::CubeMap:
// gfx_state.addBindingSet(m_binding_set_cubedrawconstants);
// break;
// case RenderPassType::Forward:
// gfx_state.addBindingSet(m_binding_set_drawconstants);
// gfx_state.addBindingSet(
// m_binding_set_forward[m_environment->GetCurrentSetIndex()]);
// break;
// case RenderPassType::Deferred:
// gfx_state.addBindingSet(m_binding_set_drawconstants);
// break;
// case RenderPassType::ShadowMap:
// gfx_state.addBindingSet(m_binding_set_shadowdrawconstants);
// break;
// }
}
bool NvRenderer::BindMaterial(material_handle handle, DrawType draw_type,
const RenderPass &pass,
nvrhi::GraphicsState &gfx_state,
float &alpha_threshold, bool *out_is_refractive) {
if (pass.m_type == RenderPassType::RendererWarmUp) return true;
if (!handle || handle > m_material_cache.size()) {
return false;
}
MaterialCache &cache = m_material_cache[handle - 1];
if(out_is_refractive) {
*out_is_refractive = cache.m_template->m_enable_refraction;
}
if (!cache.ShouldRenderInPass(pass)) return false;
auto pipeline_index = Constants::GetPipelineIndex(pass.m_type, draw_type);
if (!cache.m_pipelines[pipeline_index]) {
return false;
}
cache.m_last_position_requested = pass.m_origin;
if (cache.m_last_texture_updates[pipeline_index] <
cache.GetLastStreamingTextureUpdate()) {
cache.m_template->CreateBindingSet(pipeline_index, cache);
}
if (!cache.m_binding_sets[pipeline_index]) return false;
cache.m_last_frame_requested = GetCurrentFrame();
cache.UpdateLastStreamingTextureUse(pass.m_origin);
gfx_state.setPipeline(cache.m_pipelines[pipeline_index]);
gfx_state.addBindingSet(cache.m_binding_sets[pipeline_index]);
if (cache.m_opacity) {
alpha_threshold =
(pass.m_type == RenderPassType::Forward ? -cache.m_opacity.value()
: cache.m_opacity.value());
} else {
alpha_threshold = (pass.m_type == RenderPassType::Forward ? 0.f : .5f);
}
return true;
}
bool NvRenderer::BindLineMaterial(NvRenderer::DrawType draw_type,
const NvRenderer::RenderPass &pass,
nvrhi::GraphicsState &gfx_state) {
if (draw_type != DrawType::Model || pass.m_type != RenderPassType::Deferred)
return false;
gfx_state.setPipeline(m_pso_line);
gfx_state.addBindingSet(m_binding_set_line);
return true;
}
bool NvRenderer::BindGeometry(gfx::geometry_handle handle,
const RenderPass &pass,
nvrhi::GraphicsState &gfx_state,
nvrhi::DrawArguments &args, bool &indexed) {
if (!handle.bank || !handle.chunk || handle.bank > m_geometry_banks.size())
return false;
auto &bank = m_geometry_banks[handle.bank - 1];
if (handle.chunk > bank.m_chunks.size()) return false;
auto &chunk = bank.m_chunks[handle.chunk - 1];
bank.m_last_frame_requested = pass.m_frame_index;
chunk.m_last_frame_requested = pass.m_frame_index;
chunk.m_last_position_requested = pass.m_origin;
bool is_uploading;
UpdateGeometry(handle, pass, &is_uploading);
if (is_uploading) return false;
// if (bank.m_uploaded_query &&
// !m_backend->GetDevice()->pollEventQuery(bank.m_uploaded_query))
// return false;
// gfx_state.addVertexBuffer(
// nvrhi::VertexBufferBinding()
// .setBuffer(bank.m_vertex_buffer)
// .setSlot(0)
// .setOffset(chunk.m_vertex_offset * sizeof(gfx::basic_vertex))
// .setSize(chunk.m_vertex_count * sizeof(gfx::basic_vertex)));
// gfx_state.setIndexBuffer(
// nvrhi::IndexBufferBinding()
// .setBuffer(bank.m_index_buffer)
// .setFormat(sizeof(gfx::basic_index) == 2 ? nvrhi::Format::R16_UINT
// :
// nvrhi::Format::R32_UINT)
// .setOffset(chunk.m_index_offset * sizeof(gfx::basic_index))
// .setSize(chunk.m_index_count * sizeof(gfx::basic_index)));
gfx_state.addVertexBuffer(
nvrhi::VertexBufferBinding().setBuffer(chunk.m_vertex_buffer).setSlot(0));
gfx_state.setIndexBuffer(nvrhi::IndexBufferBinding()
.setBuffer(chunk.m_index_buffer)
.setFormat(sizeof(gfx::basic_index) == 2
? nvrhi::Format::R16_UINT
: nvrhi::Format::R32_UINT));
args.setStartVertexLocation(0).setStartIndexLocation(0).setVertexCount(
chunk.m_indexed ? chunk.m_index_count : chunk.m_vertex_count);
indexed = chunk.m_indexed;
return chunk.m_vertex_buffer && (!indexed || chunk.m_index_buffer);
}
void NvRenderer::Render(TAnimModel *Instance, const RenderPass &pass) {
if (!pass.m_draw_tanimobj) return;
m_drawcall_counter = &m_drawcalls_tanimobj;
if (!Instance->m_visible) {
return;
}
double distancesquared = glm::distance2(Instance->location(), pass.m_origin);
pass.m_command_list_draw->beginMarker(Instance->m_name.c_str());
TSubModel::iInstance = reinterpret_cast<size_t>(Instance);
auto instance_scope = m_motion_cache->SetInstance(Instance);
if (Instance->pModel) {
// renderowanie rekurencyjne submodeli
Render(Instance->pModel, Instance->Material(), distancesquared, pass);
}
pass.m_command_list_draw->endMarker();
}
bool NvRenderer::Render(TDynamicObject *Dynamic, const RenderPass &pass) {
if (!pass.m_draw_dynamic) return false;
m_drawcall_counter = &m_drawcalls_dynamic;
// lod visibility ranges are defined for base (x 1.0) viewing distance. for
// render we adjust them for actual range multiplier and zoom
double squaredistance;
glm::dvec3 const originoffset = Dynamic->vPosition - pass.m_origin;
squaredistance =
glm::length2(originoffset / static_cast<double>(Global.ZoomFactor));
pass.m_command_list_draw->beginMarker(Dynamic->name().c_str());
TSubModel::iInstance = reinterpret_cast<size_t>(Dynamic);
auto instance_scope = m_motion_cache->SetInstance(Dynamic);
// render
if (Dynamic->mdLowPolyInt) {
// Render_lowpoly(Dynamic, squaredistance, false);
Render(Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance, pass);
}
if (Dynamic->mdLoad) {
// renderowanie nieprzezroczystego ładunku
Render(Dynamic->mdLoad, Dynamic->Material(), squaredistance, pass);
}
if (Dynamic->mdModel) {
// main model
Render(Dynamic->mdModel, Dynamic->Material(), squaredistance, pass);
}
// optional attached models
for (auto *attachment : Dynamic->mdAttachments) {
Render(attachment, Dynamic->Material(), squaredistance, pass);
}
// optional coupling adapters
// Render_coupler_adapter(Dynamic, squaredistance, end::front);
// Render_coupler_adapter(Dynamic, squaredistance, end::rear);
// TODO: check if this reset is needed. In theory each object should render
// all parts based on its own instance data anyway?
// if (Dynamic->btnOn)
// Dynamic->TurnOff(); // przywrócenie domyślnych pozycji submodeli
pass.m_command_list_draw->endMarker();
return true;
}
bool NvRenderer::Render(TModel3d *Model, material_data const *material,
float const Squaredistance, const RenderPass &pass) {
// int alpha = material ? material->textures_alpha : 0x30300030;
// if (pass.m_type == RenderPassType::Forward) {
// if (!(alpha & Model->iFlags & 0x2F2F002F)) {
// // nothing to render
// return false;
// }
// } else {
// alpha ^=
// 0x0F0F000F; // odwrócenie flag tekstur, aby wyłapać nieprzezroczyste
// if (!(alpha & Model->iFlags & 0x1F1F001F)) {
// // czy w ogóle jest co robić w tym cyklu?
// return false;
// }
// }
// pass.m_command_list_draw->beginMarker(Model->NameGet().c_str());
// Model->Root->fSquareDist = Squaredistance; // zmienna globalna!
//
//// setup
// Model->Root->ReplacableSet((material ? material->replacable_skins :
// nullptr),
// alpha);
//
//// render
// Render(Model->Root, pass);
//
// pass.m_command_list_draw->endMarker();
// post-render cleanup
return true;
}
void NvRenderer::Render(TSubModel *Submodel, const RenderPass &pass) {
// for (; Submodel; Submodel = Submodel->Next) {
// if (!Submodel->iVisible) continue;
//
// if (Submodel->fSquareDist >= Submodel->fSquareMaxDist ||
// Submodel->fSquareDist < Submodel->fSquareMinDist)
// continue;
//
// if (m_batched_instances.find(CombinePointers(
// reinterpret_cast<const void *>(TSubModel::iInstance), Submodel))
// == m_batched_instances.end() &&
// (Submodel->iAlpha & Submodel->iFlags &
// ((pass.m_type == RenderPassType::Forward) ? 0x2F : 0x1F)) &&
// Submodel->eType < TP_ROTATOR) {
// nvrhi::GraphicsState gfx_state{};
//
// nvrhi::DrawArguments draw_arguments{};
// bool indexed;
// float alpha_threshold;
//
// if (!BindGeometry(Submodel->m_geometry.handle, pass, gfx_state,
// draw_arguments, indexed))
// return;
// // textures...
// if (Submodel->m_material < 0) { // zmienialne skóry
// if (!BindMaterial(Submodel->ReplacableSkinId[-Submodel->m_material],
// DrawType::Model, pass, gfx_state, alpha_threshold))
// return;
// } else {
// // również 0
// if (!BindMaterial(Submodel->m_material, DrawType::Model, pass,
// gfx_state, alpha_threshold))
// return;
// }
// pass.m_command_list_draw->beginMarker(Submodel->pName.c_str());
//
// float emission =
// Submodel->fLight > Global.fLuminance ? Submodel->f4Emision.a : 0.f;
//
// BindConstants(pass, gfx_state);
//
// pass.m_command_list_draw->setGraphicsState(gfx_state);
//
// auto &motion_cache = m_motion_cache->Get(Submodel);
// UpdateDrawData(
// pass, pass.m_transform * motion_cache.m_cached_transform,
// pass.m_history_transform * motion_cache.m_history_transform,
// alpha_threshold, 1.f, emission);
//
// if (indexed)
// pass.m_command_list_draw->drawIndexed(draw_arguments);
// else
// pass.m_command_list_draw->draw(draw_arguments);
// m_drawcall_counter->Draw(draw_arguments);
// pass.m_command_list_draw->endMarker();
// }
// if (Submodel->Child != nullptr) {
// if (Submodel->iAlpha & Submodel->iFlags &
// ((pass.m_type == RenderPassType::Forward) ? 0x002F0000
// : 0x001F0000)) {
// Render(Submodel->Child, pass);
// }
// }
//}
}
bool NvRenderer::Render_cab(TDynamicObject const *Dynamic,
const RenderPass &pass) {
// if (!pass.m_draw_dynamic) return false;
// m_drawcall_counter = &m_drawcalls_dynamic;
// if (FreeFlyModeFlag || !Dynamic->bDisplayCab ||
// (Dynamic->mdKabina == Dynamic->mdModel)) {
// return false;
// }
// TSubModel::iInstance = reinterpret_cast<size_t>(Dynamic);
// auto instance_scope = m_motion_cache->SetInstance(Dynamic);
// if (Dynamic->mdKabina)
// return Render(Dynamic->mdKabina, Dynamic->Material(), 0.0, pass);
// else
// return false;
return true;
}
void NvRenderer::Animate(AnimObj &instance, const glm::dvec3 &origin,
uint64_t frame) {
instance.m_was_once_animated = true;
instance.m_model->RaAnimate(frame);
instance.m_model->RaPrepare();
auto instance_scope = m_motion_cache->SetInstance(instance.m_model);
TSubModel::iInstance = reinterpret_cast<size_t>(instance.m_model);
instance.m_renderable.Reset();
double distance = glm::distance2(instance.m_origin, origin);
if (!instance.m_animatable) distance = -1.;
Animate(instance.m_renderable, instance.m_model->Model(),
instance.m_model->Material(), distance, instance.m_origin,
glm::radians(instance.m_model->vAngle), frame);
}
void NvRenderer::Animate(DynObj &instance, const glm::dvec3 &origin,
uint64_t frame) {
// setup
TSubModel::iInstance = reinterpret_cast<size_t>(
instance.m_dynamic); // żeby nie robić cudzych animacji
instance.m_dynamic
->ABuLittleUpdate( // ustawianie zmiennych submodeli dla wspólnego modelu
0);
glm::dmat4 dynamic_transform =
glm::translate(instance.m_location) *
static_cast<glm::dmat4>(instance.m_dynamic->mMatrix);
const material_data *material = instance.m_dynamic->Material();
bool render_kabina = Global.pCamera.m_owner == instance.m_dynamic &&
instance.m_dynamic->mdKabina;
auto instance_scope = m_motion_cache->SetInstance(instance.m_dynamic);
instance.m_renderable.Reset();
instance.m_renderable_kabina.Reset();
// render
if (instance.m_dynamic->mdLowPolyInt) {
// Render_lowpoly(Dynamic, squaredistance, false);
Animate(instance.m_renderable, instance.m_dynamic->mdLowPolyInt, material,
instance.m_distance, dynamic_transform, frame);
}
if (instance.m_dynamic->mdLoad) {
// renderowanie nieprzezroczystego ładunku
Animate(instance.m_renderable, instance.m_dynamic->mdLoad, material,
instance.m_distance,
dynamic_transform * glm::translate(static_cast<glm::dvec3>(
instance.m_dynamic->LoadOffset)),
frame);
}
if (instance.m_dynamic->mdModel) {
// main model
Animate(instance.m_renderable, instance.m_dynamic->mdModel, material,
instance.m_distance, dynamic_transform, frame);
}
// optional attached models
for (auto *attachment : instance.m_dynamic->mdAttachments) {
Animate(instance.m_renderable, attachment, material, instance.m_distance,
dynamic_transform, frame);
}
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();
}
void NvRenderer::Animate(Renderable &renderable, TModel3d *Model,
const material_data *material, double distance,
const glm::dvec3 &position, const glm::vec3 &angle,
uint64_t frame) {
return Animate(
renderable, Model, material, distance,
glm::translate(static_cast<glm::dvec3>(position)) *
glm::rotate(static_cast<double>(angle.y), glm::dvec3{0., 1., 0.}) *
glm::rotate(static_cast<double>(angle.x), glm::dvec3{1., 0., 0.}) *
glm::rotate(static_cast<double>(angle.z), glm::dvec3{0., 0., 1.}),
frame);
}
void NvRenderer::Animate(Renderable &renderable, TModel3d *Model,
const material_data *material, double distance,
const glm::dmat4 &transform, uint64_t frame) {
Model->Root->pRoot = Model;
// render
Animate(renderable, Model->Root, material, distance, transform, frame);
}
void NvRenderer::Animate(Renderable &renderable, TSubModel *Submodel,
const material_data *material, double distance,
const glm::dmat4 &transform, uint64_t frame) {
static material_handle null_material[5]{0};
const material_handle *replacable =
material ? material->replacable_skins : null_material;
const int alpha = material ? material->textures_alpha : 0x30300030;
for (; Submodel; Submodel = Submodel->Next) {
if (!Submodel->iVisible) continue;
if (distance >= 0. && (distance >= Submodel->fSquareMaxDist ||
distance < Submodel->fSquareMinDist))
continue;
glm::dmat4 model_transform = transform;
if (Submodel->iFlags & 0xC000) {
glm::mat4 anim_transform{1.f};
if (Submodel->fMatrix) {
anim_transform = glm::make_mat4(Submodel->fMatrix->readArray());
}
if (Submodel->b_aAnim != TAnimType::at_None) {
Submodel->RaAnimation(anim_transform, Submodel->b_aAnim);
}
model_transform =
model_transform * static_cast<glm::dmat4>(anim_transform);
}
if (Submodel->eType < TP_ROTATOR &&
m_batched_instances.find(CombinePointers(
reinterpret_cast<const void *>(TSubModel::iInstance), Submodel)) ==
m_batched_instances.end()) {
auto &item = renderable.m_items.emplace_back();
item.m_name = Submodel->pName.c_str();
auto &motion_cache = m_motion_cache->Get(Submodel);
motion_cache.m_history_transform =
std::exchange(motion_cache.m_cached_transform, model_transform);
item.m_transform = motion_cache.m_cached_transform;
item.m_history_transform = motion_cache.m_history_transform;
item.m_material = Submodel->m_material < 0
? replacable[-Submodel->m_material]
: Submodel->m_material;
item.m_geometry = Submodel->m_geometry.handle;
item.m_render_in_forward = alpha & Submodel->Flags() & 0x0000002F;
item.m_opacity = Submodel->Opacity;
if ((Submodel->f4Emision.a > 0.f) &&
(Global.fLuminance < Submodel->fLight))
item.m_selfillum = Submodel->f4Emision.a;
else
item.m_selfillum = 0.f;
if (distance < 0.) {
item.m_sqr_distance_max = Submodel->fSquareMaxDist;
item.m_sqr_distance_min = Submodel->fSquareMinDist;
item.m_history_transform = item.m_transform;
} else {
item.m_sqr_distance_max = std::numeric_limits<double>::max();
item.m_sqr_distance_min = -std::numeric_limits<double>::max();
}
item.m_diffuse = Submodel->f4Diffuse;
renderable.m_render_in_forward |= item.m_render_in_forward;
renderable.m_render_in_deferred |= !item.m_render_in_forward;
}
else if (Submodel->eType == TP_FREESPOTLIGHT) {
if ((Submodel->f4Emision.a <= 0.f) ||
(Global.fLuminance >= Submodel->fLight))
continue;
Renderable::SpotLight &spotlight = renderable.m_spotlights.emplace_back();
spotlight.m_origin = model_transform[3];
spotlight.m_direction = glm::normalize(-model_transform[2]);
spotlight.m_color =
Submodel->f4Diffuse * Config()->m_lighting.m_freespot_multiplier;
spotlight.m_cos_outer_cone = Submodel->fCosFalloffAngle;
spotlight.m_cos_inner_cone = Submodel->fCosHotspotAngle;
spotlight.m_radius = glm::sqrt(
1.f / Config()->m_lighting.m_luminance_threshold *
glm::dot(spotlight.m_color, glm::vec3(.2126f, .7152f, .0722f)));
}
// auto last_frame =
// std::exchange(motion_cache.m_cached_frame, m_frame_index);
// assert(last_frame != pass.m_frame_index);
Animate(renderable, Submodel->Child, material, distance, model_transform,
frame);
}
}
void NvRenderer::GeometryChunk::UpdateBounds() {
glm::vec3 min{std::numeric_limits<float>::max()};
glm::vec3 max{-std::numeric_limits<float>::max()};
for (const auto &vertex : m_vertices) {
min = glm::min(min, vertex.position);
max = glm::max(max, vertex.position);
}
m_origin = .5f * (max + min);
m_extent = .5f * (max - min);
}
bool NvRenderer::MaterialCache::ShouldRenderInPass(
const RenderPass &pass) const {
if ((pass.m_type == RenderPassType::ShadowMap ||
pass.m_type == RenderPassType::CubeMap) &&
m_shadow_rank > Global.gfx_shadow_rank_cutoff) {
return false;
}
return true;
}