mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-03-22 15:05:03 +01:00
1116 lines
42 KiB
C++
1116 lines
42 KiB
C++
#include "config.h"
|
|
#include "motioncache.h"
|
|
#include "nvrenderer/nvrenderer.h"
|
|
#include "nvrendererbackend.h"
|
|
|
|
#include <AnimModel.h>
|
|
#include <simulation.h>
|
|
|
|
namespace BatchingUtils {
|
|
|
|
struct RadiusTester {
|
|
glm::dvec3 m_origin;
|
|
double m_radius_sqr;
|
|
bool BoxVisible(const glm::dvec3& origin, const glm::dvec3& extent) const {
|
|
glm::dvec3 closest = glm::clamp(m_origin, origin - extent, origin + extent);
|
|
return glm::distance2(closest, m_origin) < m_radius_sqr;
|
|
}
|
|
RadiusTester(const glm::dvec3& origin, double radius)
|
|
: m_origin(origin), m_radius_sqr(radius * radius) {}
|
|
};
|
|
|
|
} // namespace BatchingUtils
|
|
|
|
void NvRenderer::GatherModelsForBatching() {
|
|
struct BatchedId {
|
|
gfx::geometry_handle m_geometry;
|
|
material_handle m_material;
|
|
int m_tile_x;
|
|
int m_tile_y;
|
|
};
|
|
struct BatchedIdHash {
|
|
constexpr static size_t HashCombine(std::size_t seed, std::size_t hash) {
|
|
return hash + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
|
}
|
|
size_t operator()(const BatchedId& rhs) const {
|
|
std::hash<uint64_t> hasher;
|
|
return HashCombine(HashCombine(HashCombine(hasher(rhs.m_geometry),
|
|
hasher(rhs.m_material)),
|
|
hasher(rhs.m_tile_x)),
|
|
hasher(rhs.m_tile_y));
|
|
}
|
|
};
|
|
struct BatchedIdEq {
|
|
bool operator()(const BatchedId& lhs, const BatchedId& rhs) const {
|
|
return lhs.m_geometry == rhs.m_geometry &&
|
|
lhs.m_material == rhs.m_material && lhs.m_tile_x == rhs.m_tile_x &&
|
|
lhs.m_tile_y == rhs.m_tile_y;
|
|
}
|
|
};
|
|
std::unordered_map<BatchedId, uint32_t, BatchedIdHash, BatchedIdEq> batches{};
|
|
double tile_size_std = 500.;
|
|
double tile_size_loddable = 250.;
|
|
auto get_batch_for_geometry =
|
|
[&batches, this, tile_size_std, tile_size_loddable](
|
|
gfx::geometry_handle geometry, material_handle material,
|
|
const glm::dvec3& origin, bool likely_loddable, bool& is_new,
|
|
const glm::vec3& diffuse) -> GeometryBatch* {
|
|
// is_new = true;
|
|
// return &m_geometry_batches.emplace_back();
|
|
double tile_size = likely_loddable ? tile_size_loddable : tile_size_std;
|
|
BatchedId id{};
|
|
id.m_geometry = geometry;
|
|
id.m_material = material;
|
|
id.m_tile_x = glm::floor(origin.x / tile_size);
|
|
id.m_tile_y = glm::floor(origin.z / tile_size);
|
|
auto [it, is_actually_new] = batches.emplace(id, 0);
|
|
is_new = is_actually_new;
|
|
if (is_actually_new) {
|
|
it->second = m_geometry_batches.size();
|
|
auto& batch = m_geometry_batches.emplace_back();
|
|
batch.m_name =
|
|
std::to_string(id.m_tile_x) + "_" + std::to_string(id.m_tile_y);
|
|
batch.m_diffuse = diffuse;
|
|
return &batch;
|
|
}
|
|
return &m_geometry_batches[it->second];
|
|
};
|
|
auto sqr_dist_to_screensize =
|
|
[this](double sqr_dist, gfx::geometry_handle geometry) -> double {
|
|
const auto [origin, extent] = GetGeometryBounds(geometry);
|
|
double radius = glm::length(extent);
|
|
return SqrDistToScreenSize(sqr_dist, radius);
|
|
};
|
|
for (auto section : simulation::Region->m_sections) {
|
|
if (!section) continue;
|
|
for (const auto& cell : section->m_cells) {
|
|
for (auto instance : cell.m_instancesopaque) {
|
|
static bool (*is_allowed_animation)(TAnimType) =
|
|
[](TAnimType anim) -> bool {
|
|
switch (anim) {
|
|
case TAnimType::at_None:
|
|
case TAnimType::at_Wind:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
};
|
|
static void (*for_each_submodel)(
|
|
TSubModel const* sm, std::function<bool(TSubModel const*)>) =
|
|
[](TSubModel const* sm,
|
|
std::function<bool(TSubModel const*)> callback) {
|
|
for (; sm; sm = sm->Next) {
|
|
if (callback(sm)) for_each_submodel(sm->Child, callback);
|
|
}
|
|
};
|
|
auto root = instance->Model()->GetSMRoot();
|
|
auto alpha = instance->Material() ? instance->Material()->textures_alpha
|
|
: 0x20200020;
|
|
bool likely_loddable = false;
|
|
for_each_submodel(root, [&](TSubModel const* sm) -> bool {
|
|
if (sm->fSquareMinDist >= 0.) {
|
|
likely_loddable = true;
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
bool fully_batchable = true;
|
|
root->ReplacableSet(instance->Material()->replacable_skins, 0);
|
|
glm::dvec3 angle = glm::radians(instance->Angles());
|
|
glm::dmat4 transform =
|
|
glm::translate(static_cast<glm::dvec3>(instance->location())) *
|
|
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.});
|
|
for_each_submodel(root, [&](TSubModel const* sm) -> bool {
|
|
// Model światła wyłączonego -> zmienny stan widoczności
|
|
if (std::find(instance->LightsOff.begin(), instance->LightsOff.end(),
|
|
sm) != instance->LightsOff.end()) {
|
|
fully_batchable = false;
|
|
return false;
|
|
}
|
|
// Model światła włączonego -> zmienny stan widoczności
|
|
if (std::find(instance->LightsOn.begin(), instance->LightsOn.end(),
|
|
sm) != instance->LightsOn.end()) {
|
|
fully_batchable = false;
|
|
return false;
|
|
}
|
|
// Model animowany przez event
|
|
if (std::find_if(instance->m_animlist.begin(),
|
|
instance->m_animlist.end(),
|
|
[sm](std::shared_ptr<TAnimContainer> ac) -> bool {
|
|
return ac->pSubModel == sm;
|
|
}) != instance->m_animlist.end()) {
|
|
fully_batchable = false;
|
|
return false;
|
|
}
|
|
// Model animowany
|
|
if ((sm->Flags() & 0x4000) && !is_allowed_animation(sm->b_Anim)) {
|
|
fully_batchable = false;
|
|
return false;
|
|
}
|
|
// Model przezroczysty -> kontynować sprawdzanie w hierarchii
|
|
if (sm->Flags() & 0x0000002F & alpha) {
|
|
fully_batchable = false;
|
|
return true;
|
|
}
|
|
if (sm->eType >= TP_ROTATOR) return true;
|
|
m_batched_instances.emplace(CombinePointers(instance, sm));
|
|
float4x4 matrix;
|
|
sm->ParentMatrix(&matrix);
|
|
if (!sm->m_geometry.handle) return true;
|
|
auto material = sm->m_material < 0
|
|
? sm->ReplacableSkinId[-sm->m_material]
|
|
: sm->m_material;
|
|
if (!material) return true;
|
|
auto sm_transform =
|
|
transform *
|
|
static_cast<glm::dmat4>(glm::make_mat4(matrix.readArray()));
|
|
auto [origin, extent] = GetGeometryBounds(sm->m_geometry.handle);
|
|
bool new_batch;
|
|
auto batch = get_batch_for_geometry(sm->m_geometry.handle, material,
|
|
sm_transform[3], likely_loddable,
|
|
new_batch, sm->f4Diffuse);
|
|
if (new_batch) {
|
|
batch->m_geometry = sm->m_geometry.handle;
|
|
batch->m_instance_origin = origin;
|
|
batch->m_material = material;
|
|
batch->m_name +=
|
|
"_" + instance->Model()->NameGet() + "_" + sm->pName;
|
|
batch->m_sqr_distance_max = sm->fSquareMaxDist;
|
|
batch->m_sqr_distance_min = sm->fSquareMinDist;
|
|
}
|
|
batch->m_transforms.emplace_back(sm_transform);
|
|
return true;
|
|
});
|
|
// Model w całości statyczny? -> można pomijać ewaluację TAnimObj
|
|
if (fully_batchable) {
|
|
m_batched_instances.emplace(CombinePointers(instance, nullptr));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
glm::dvec3 world_min{std::numeric_limits<double>::max()};
|
|
glm::dvec3 world_max{-std::numeric_limits<double>::max()};
|
|
for (auto& batch : m_geometry_batches) {
|
|
auto [origin, extent] = GetGeometryBounds(batch.m_geometry);
|
|
batch.m_instance_radius = glm::length(extent);
|
|
glm::dvec3 batch_min{std::numeric_limits<double>::max()};
|
|
glm::dvec3 batch_max{-std::numeric_limits<double>::max()};
|
|
for (const auto& transform : batch.m_transforms) {
|
|
for (int i = 0; i < 8; ++i) {
|
|
glm::dvec3 corner =
|
|
transform *
|
|
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.};
|
|
batch_min = glm::min(batch_min, corner);
|
|
batch_max = glm::max(batch_max, corner);
|
|
}
|
|
}
|
|
world_min = glm::min(batch_min, world_min);
|
|
world_max = glm::max(batch_max, world_max);
|
|
batch.m_origin = .5 * (batch_max + batch_min);
|
|
batch.m_extent = .5 * (batch_max - batch_min);
|
|
}
|
|
QuadTreeBuilder builder{world_min.x, world_min.z, world_max.x, world_max.z};
|
|
for (int i = 0; i < m_geometry_batches.size(); ++i) {
|
|
const auto& batch = m_geometry_batches[i];
|
|
builder.Insert(i, batch.m_origin - batch.m_extent,
|
|
batch.m_origin + batch.m_extent);
|
|
}
|
|
m_batch_quadtree.Build(builder);
|
|
}
|
|
|
|
void NvRenderer::GatherTracksForBatching() {
|
|
struct TrackBatchId {
|
|
glm::dvec3 m_geometry_origin;
|
|
material_handle m_material;
|
|
};
|
|
struct TrackBatchIdHash {
|
|
constexpr static size_t HashCombine(std::size_t seed, std::size_t hash) {
|
|
return hash + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
|
}
|
|
size_t operator()(const TrackBatchId& rhs) const {
|
|
std::hash<uint64_t> hasher;
|
|
std::hash<double> hasher_d;
|
|
return HashCombine(
|
|
HashCombine(HashCombine(hasher(rhs.m_material),
|
|
hasher_d(rhs.m_geometry_origin.x)),
|
|
hasher_d(rhs.m_geometry_origin.y)),
|
|
hasher_d(rhs.m_geometry_origin.z));
|
|
}
|
|
};
|
|
struct TrackBatchIdEq {
|
|
bool operator()(const TrackBatchId& lhs, const TrackBatchId& rhs) const {
|
|
return lhs.m_material == rhs.m_material &&
|
|
lhs.m_geometry_origin == rhs.m_geometry_origin;
|
|
}
|
|
};
|
|
struct TrackEntry {
|
|
glm::dvec3 m_origin;
|
|
glm::dvec3 m_extent;
|
|
gfx::geometry_handle* m_original_geometry;
|
|
};
|
|
struct TrackBatch {
|
|
glm::dvec3 m_geometry_origin{0.};
|
|
material_handle m_material = 0;
|
|
glm::dvec3 m_world_min = glm::dvec3{std::numeric_limits<double>::max()};
|
|
glm::dvec3 m_world_max = glm::dvec3{-std::numeric_limits<double>::max()};
|
|
std::vector<TrackEntry> m_tracks{};
|
|
};
|
|
|
|
std::vector<TrackBatch> batches{};
|
|
std::unordered_map<TrackBatchId, uint32_t, TrackBatchIdHash, TrackBatchIdEq>
|
|
batch_mapping{};
|
|
glm::dvec3 world_min{std::numeric_limits<double>::max()};
|
|
glm::dvec3 world_max{-std::numeric_limits<double>::max()};
|
|
auto add_geometry = [&](TTrack* track, material_handle material,
|
|
gfx::geometry_handle* geometry) {
|
|
auto [origin, extent] = GetGeometryBounds(*geometry);
|
|
origin += track->m_origin;
|
|
world_min = glm::min(world_min, origin - extent);
|
|
world_max = glm::max(world_max, origin + extent);
|
|
TrackBatchId id{};
|
|
id.m_geometry_origin = track->m_origin;
|
|
id.m_material = material;
|
|
auto [it, is_actually_new] = batch_mapping.emplace(id, 0);
|
|
if (is_actually_new) {
|
|
it->second = batches.size();
|
|
auto& batch = batches.emplace_back();
|
|
batch.m_material = material;
|
|
batch.m_geometry_origin = track->m_origin;
|
|
}
|
|
auto& batch = batches[it->second];
|
|
batch.m_world_min = glm::min(batch.m_world_min, origin - extent);
|
|
batch.m_world_max = glm::max(batch.m_world_max, origin + extent);
|
|
auto& entry = batch.m_tracks.emplace_back();
|
|
entry.m_extent = extent;
|
|
entry.m_origin = origin;
|
|
entry.m_original_geometry = geometry;
|
|
};
|
|
for (auto section : simulation::Region->m_sections) {
|
|
if (!section) continue;
|
|
for (const auto& cell : section->m_cells) {
|
|
for (const auto track : cell.m_paths) {
|
|
if (!track) continue;
|
|
for (auto& geometry : track->Geometry1)
|
|
add_geometry(track, track->m_material1, &geometry);
|
|
for (auto& geometry : track->Geometry2)
|
|
add_geometry(track, track->m_material2, &geometry);
|
|
if (auto extension = track->SwitchExtension.get())
|
|
add_geometry(track, extension->m_material3, &extension->Geometry3);
|
|
}
|
|
}
|
|
}
|
|
QuadTreeBuilder builder{world_min.x, world_min.z, world_max.x, world_max.z};
|
|
m_track_batches.resize(batches.size());
|
|
for (int i = 0; i < batches.size(); ++i) {
|
|
const auto& batch = batches[i];
|
|
builder.Insert(i, batch.m_world_min, batch.m_world_max);
|
|
auto& track_batch = m_track_batches[i];
|
|
track_batch.m_diffuse = {1.f, 1.f, 1.f};
|
|
track_batch.m_name = Material(batch.m_material)->GetName();
|
|
track_batch.m_origin = .5 * (batch.m_world_max + batch.m_world_min);
|
|
track_batch.m_extent = .5 * (batch.m_world_max - batch.m_world_min);
|
|
track_batch.m_material = batch.m_material;
|
|
track_batch.m_geometry_origin = batch.m_geometry_origin;
|
|
std::vector<gfx::basic_vertex> vertices{};
|
|
std::vector<gfx::vertex_userdata> userdatas{};
|
|
std::vector<gfx::basic_index> indices{};
|
|
for (int j = 0; j < batch.m_tracks.size(); ++j) {
|
|
const auto& track = batch.m_tracks[j];
|
|
if (!(track.m_original_geometry->bank &&
|
|
track.m_original_geometry->chunk))
|
|
continue;
|
|
auto& chunk = m_geometry_banks[track.m_original_geometry->bank - 1]
|
|
.m_chunks[track.m_original_geometry->chunk - 1];
|
|
// assert(chunk.m_indices.size());
|
|
track_batch.m_draw_commands.emplace_back()
|
|
.setStartVertexLocation(vertices.size())
|
|
.setStartIndexLocation(indices.size())
|
|
.setVertexCount(chunk.m_indices.size());
|
|
auto& region = track_batch.m_regions.emplace_back();
|
|
region.m_start_vertex = vertices.size();
|
|
region.m_vertex_count = chunk.m_vertices.size();
|
|
region.m_start_index = indices.size();
|
|
region.m_index_count = chunk.m_indices.size();
|
|
vertices.insert(vertices.end(), chunk.m_vertices.begin(),
|
|
chunk.m_vertices.end());
|
|
indices.insert(indices.end(), chunk.m_indices.begin(),
|
|
chunk.m_indices.end());
|
|
chunk.m_replace_impl = std::bind(&NvRenderer::TrackBatchPartialUpdate,
|
|
this, i, j, std::placeholders::_1);
|
|
}
|
|
track_batch.m_regions_need_update.resize(
|
|
track_batch.m_draw_commands.size());
|
|
auto bank = Create_Bank();
|
|
track_batch.m_geometry =
|
|
Insert(indices, vertices, userdatas, bank, GL_TRIANGLES);
|
|
}
|
|
m_track_quadtree.Build(builder);
|
|
}
|
|
|
|
void NvRenderer::GatherShapesForBatching() {
|
|
float mult_lines = Config()->m_weight_lines;
|
|
float mult_traction = Config()->m_weight_tractions;
|
|
glm::dvec3 world_min{std::numeric_limits<double>::max()};
|
|
glm::dvec3 world_max{-std::numeric_limits<double>::max()};
|
|
auto add_shape = [&](const scene::shape_node::shapenode_data& data) {
|
|
if (!data.material || !data.geometry) return;
|
|
auto [origin, extent] = GetGeometryBounds(data.geometry);
|
|
origin += data.origin;
|
|
auto& shape = m_shapes.emplace_back();
|
|
shape.m_extent = extent;
|
|
shape.m_origin = origin;
|
|
shape.m_geometry = data.geometry;
|
|
shape.m_material = data.material;
|
|
shape.m_draw_in_opaque = !data.translucent;
|
|
shape.m_draw_in_transparent = data.translucent;
|
|
shape.m_geometry_origin = data.origin;
|
|
shape.m_diffuse = data.lighting.diffuse;
|
|
world_min = glm::min(world_min, origin - extent);
|
|
world_max = glm::max(world_max, origin + extent);
|
|
};
|
|
auto add_line = [&](const scene::lines_node::linesnode_data& data) {
|
|
auto [origin, extent] = GetGeometryBounds(data.geometry);
|
|
origin += data.origin;
|
|
auto& line = m_lines.emplace_back();
|
|
line.m_extent = extent;
|
|
line.m_origin = origin;
|
|
line.m_geometry = data.geometry;
|
|
line.m_geometry_origin = data.origin;
|
|
line.m_line_width = data.line_width * mult_lines;
|
|
line.m_color = data.lighting.diffuse;
|
|
line.m_metalness = 0.f;
|
|
line.m_roughness = 1.f;
|
|
world_min = glm::min(world_min, origin - extent);
|
|
world_max = glm::max(world_max, origin + extent);
|
|
};
|
|
auto add_traction = [&](const TTraction* traction) {
|
|
auto [origin, extent] = GetGeometryBounds(traction->m_geometry);
|
|
origin += traction->m_origin;
|
|
auto& line = m_lines.emplace_back();
|
|
line.m_extent = extent;
|
|
line.m_origin = origin;
|
|
line.m_geometry = traction->m_geometry;
|
|
line.m_geometry_origin = traction->m_origin;
|
|
line.m_line_width = traction->WireThickness * mult_traction;
|
|
line.m_metalness = 1.f;
|
|
switch (traction->Material) {
|
|
case 1: {
|
|
if (TestFlag(traction->DamageFlag, 1)) {
|
|
line.m_color = {0.00000f, 0.32549f, 0.2882353f}; // zielona miedź
|
|
line.m_roughness = .8f;
|
|
} else {
|
|
line.m_color = {0.35098f, 0.22549f, 0.1f}; // czerwona miedź
|
|
line.m_roughness = .1f;
|
|
}
|
|
break;
|
|
}
|
|
case 2: {
|
|
if (TestFlag(traction->DamageFlag, 1)) {
|
|
line.m_color = {0.10f, 0.10f, 0.10f}; // czarne Al
|
|
line.m_roughness = .8f;
|
|
} else {
|
|
line.m_color = {0.25f, 0.25f, 0.25f}; // srebrne Al
|
|
line.m_roughness = .1f;
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
world_min = glm::min(world_min, origin - extent);
|
|
world_max = glm::max(world_max, origin + extent);
|
|
};
|
|
for (auto section : simulation::Region->m_sections) {
|
|
if (!section) continue;
|
|
for (const auto& shape : section->m_shapes) add_shape(shape.data());
|
|
for (const auto& cell : section->m_cells) {
|
|
for (const auto& shape : cell.m_shapesopaque) add_shape(shape.data());
|
|
for (const auto& shape : cell.m_shapestranslucent)
|
|
add_shape(shape.data());
|
|
for (const auto& line : cell.m_lines) add_line(line.data());
|
|
for (const auto& traction : cell.m_traction) add_traction(traction);
|
|
}
|
|
}
|
|
|
|
QuadTreeBuilder shape_builder{world_min.x, world_min.z, world_max.x,
|
|
world_max.z};
|
|
for (int i = 0; i < m_shapes.size(); ++i) {
|
|
const auto& shape = m_shapes[i];
|
|
shape_builder.Insert(i, shape.m_origin - shape.m_extent,
|
|
shape.m_origin + shape.m_extent);
|
|
}
|
|
m_shape_quadtree.Build(shape_builder);
|
|
|
|
QuadTreeBuilder line_builder{world_min.x, world_min.z, world_max.x,
|
|
world_max.z};
|
|
for (int i = 0; i < m_lines.size(); ++i) {
|
|
const auto& line = m_lines[i];
|
|
line_builder.Insert(i, line.m_origin - line.m_extent,
|
|
line.m_origin + line.m_extent);
|
|
}
|
|
m_line_quadtree.Build(line_builder);
|
|
}
|
|
|
|
void NvRenderer::GatherAnimatedsForBatching() {
|
|
glm::dvec3 world_min{std::numeric_limits<double>::max()};
|
|
glm::dvec3 world_max{-std::numeric_limits<double>::max()};
|
|
auto add_instance = [&](TAnimModel* model) {
|
|
if (m_batched_instances.find(CombinePointers(model, nullptr)) !=
|
|
m_batched_instances.end())
|
|
return;
|
|
m_batched_instances.emplace(CombinePointers(model, nullptr));
|
|
auto const flags = model->Flags();
|
|
auto alpha =
|
|
(model->Material() != nullptr ? model->Material()->textures_alpha
|
|
: 0x30300030);
|
|
|
|
glm::dvec3 origin = model->location();
|
|
glm::dvec3 extent = glm::dvec3(model->radius());
|
|
auto& animated = m_animateds.emplace_back();
|
|
animated.m_extent = extent;
|
|
animated.m_origin = origin;
|
|
animated.m_name = model->name();
|
|
animated.m_model = model;
|
|
animated.m_animatable = model->iNumLights || model->m_animlist.size();
|
|
animated.m_render_in_forward = alpha & flags & 0x2F2F002F;
|
|
animated.m_render_in_deferred = (alpha ^ 0x0F0F000F) & flags & 0x1F1F001F;
|
|
animated.m_sqr_distance_max = model->m_rangesquaredmax;
|
|
animated.m_sqr_distance_min = model->m_rangesquaredmin;
|
|
world_min = glm::min(world_min, origin - extent);
|
|
world_max = glm::max(world_max, origin + extent);
|
|
};
|
|
for (auto section : simulation::Region->m_sections) {
|
|
if (!section) continue;
|
|
for (const auto& cell : section->m_cells) {
|
|
for (const auto& instance : cell.m_instancesopaque)
|
|
add_instance(instance);
|
|
for (const auto& instance : cell.m_instancetranslucent)
|
|
add_instance(instance);
|
|
}
|
|
}
|
|
QuadTreeBuilder builder{world_min.x, world_min.z, world_max.x, world_max.z};
|
|
for (int i = 0; i < m_animateds.size(); ++i) {
|
|
const auto& instance = m_animateds[i];
|
|
builder.Insert(i, instance.m_origin - instance.m_extent,
|
|
instance.m_origin + instance.m_extent);
|
|
}
|
|
m_animated_quadtree.Build(builder);
|
|
}
|
|
|
|
void NvRenderer::GatherCellsForAnimation() {
|
|
glm::dvec3 world_min{std::numeric_limits<double>::max()};
|
|
glm::dvec3 world_max{-std::numeric_limits<double>::max()};
|
|
auto add_cell = [&](scene::basic_cell* sz_cell) {
|
|
auto& cell = m_cells.emplace_back();
|
|
cell.m_cell = sz_cell;
|
|
cell.m_origin = sz_cell->m_area.center;
|
|
cell.m_extent = glm::dvec3(sz_cell->m_area.radius);
|
|
world_min = glm::min(world_min, cell.m_origin - cell.m_extent);
|
|
world_max = glm::max(world_max, cell.m_origin + cell.m_extent);
|
|
};
|
|
for (auto section : simulation::Region->m_sections) {
|
|
if (!section) continue;
|
|
for (auto& cell : section->m_cells) {
|
|
bool has_switch = false;
|
|
for (const auto& track : cell.m_paths) {
|
|
if (track->SwitchExtension) {
|
|
has_switch = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!has_switch) continue;
|
|
add_cell(&cell);
|
|
}
|
|
}
|
|
QuadTreeBuilder builder{world_min.x, world_min.z, world_max.x, world_max.z};
|
|
for (int i = 0; i < m_cells.size(); ++i) {
|
|
const auto& cell = m_cells[i];
|
|
builder.Insert(i, cell.m_origin - cell.m_extent,
|
|
cell.m_origin + cell.m_extent);
|
|
}
|
|
m_cell_quadtree.Build(builder);
|
|
}
|
|
|
|
void NvRenderer::GatherDynamics() {
|
|
auto add_dynamic = [&](TDynamicObject* obj) {
|
|
if (!obj->mdModel) return;
|
|
auto& dynamic = m_dynamics.emplace_back();
|
|
dynamic.m_dynamic = obj;
|
|
dynamic.m_radius = obj->radius();
|
|
};
|
|
for (const auto dynamic : simulation::Vehicles.sequence()) {
|
|
add_dynamic(dynamic);
|
|
}
|
|
}
|
|
|
|
bool NvRenderer::TrackBatchPartialUpdate(size_t batch_index, size_t track_index,
|
|
gfx::vertex_array& vertices) {
|
|
auto& batch = m_track_batches[batch_index];
|
|
auto& chunk = m_geometry_banks[batch.m_geometry.bank - 1]
|
|
.m_chunks[batch.m_geometry.chunk - 1];
|
|
const auto& region = batch.m_regions[track_index];
|
|
if (vertices.size() > region.m_vertex_count) return false;
|
|
std::copy(vertices.begin(), vertices.end(),
|
|
chunk.m_vertices.begin() + region.m_start_vertex);
|
|
if (chunk.m_vertex_buffer) batch.m_regions_need_update[track_index] = true;
|
|
return true;
|
|
}
|
|
|
|
void NvRenderer::RenderBatches(const RenderPass& pass) {
|
|
static std::vector<std::pair<size_t, size_t>> batches_to_render;
|
|
batches_to_render.clear();
|
|
|
|
if (!pass.m_draw_instances) return;
|
|
m_drawcall_counter = &m_drawcalls_instances;
|
|
|
|
auto motion_scope = m_motion_cache->SetInstance(nullptr);
|
|
const auto& motion_cache = m_motion_cache->Get(nullptr);
|
|
glm::dmat4 batch_transform =
|
|
pass.m_transform * motion_cache.m_cached_transform;
|
|
glm::dmat4 history_transform =
|
|
pass.m_history_transform * motion_cache.m_history_transform;
|
|
|
|
m_batch_quadtree.ForEach(pass, [&](uint32_t batch_index) {
|
|
GeometryBatch& batch = m_geometry_batches[batch_index];
|
|
glm::dvec3 closest_point =
|
|
glm::clamp(pass.m_origin, batch.m_origin - batch.m_extent,
|
|
batch.m_origin + batch.m_extent) -
|
|
pass.m_origin;
|
|
if (glm::dot(closest_point, closest_point) >= batch.m_sqr_distance_max)
|
|
return;
|
|
if (batch.m_transforms.size() > 0) {
|
|
double min_dist = std::numeric_limits<double>::max();
|
|
double max_dist = -std::numeric_limits<double>::max();
|
|
for (const auto& instance_transform : batch.m_transforms) {
|
|
if (!pass.SphereVisible(
|
|
instance_transform * glm::dvec4(batch.m_instance_origin, 1.),
|
|
batch.m_instance_radius))
|
|
continue;
|
|
glm::dvec3 offset = instance_transform[3].xyz - pass.m_origin;
|
|
double dist = glm::dot(offset, offset);
|
|
min_dist = glm::min(min_dist, dist);
|
|
max_dist = glm::max(max_dist, dist);
|
|
}
|
|
if (min_dist > max_dist) return;
|
|
if (min_dist >= batch.m_sqr_distance_max ||
|
|
min_dist < batch.m_sqr_distance_min)
|
|
return;
|
|
|
|
if (!batch.m_static_instance_buffer) {
|
|
pass.m_command_list_preparation->beginMarker(batch.m_name.c_str());
|
|
std::vector<glm::mat3x4> instances;
|
|
instances.reserve(batch.m_transforms.size());
|
|
glm::dmat4 pre_transform = glm::translate(-batch.m_origin);
|
|
for (const auto& instance_transform : batch.m_transforms) {
|
|
instances.emplace_back(static_cast<glm::mat3x4>(
|
|
glm::transpose(pre_transform * instance_transform)));
|
|
}
|
|
batch.m_static_instance_buffer =
|
|
GetBackend()->GetDevice()->createBuffer(
|
|
nvrhi::BufferDesc()
|
|
.setByteSize(instances.size() * sizeof(instances[0]))
|
|
.setIsVertexBuffer(true)
|
|
.setInitialState(nvrhi::ResourceStates::VertexBuffer)
|
|
.setKeepInitialState(true));
|
|
pass.m_command_list_preparation->writeBuffer(
|
|
batch.m_static_instance_buffer, instances.data(),
|
|
instances.size() * sizeof(instances[0]));
|
|
pass.m_command_list_preparation->setPermanentBufferState(
|
|
batch.m_static_instance_buffer,
|
|
nvrhi::ResourceStates::VertexBuffer);
|
|
pass.m_command_list_preparation->endMarker();
|
|
}
|
|
|
|
const auto material = m_material_cache[batch.m_material - 1];
|
|
if (!material.ShouldRenderInPass(pass)) return;
|
|
|
|
union {
|
|
uint64_t m_command_index;
|
|
struct {
|
|
uint32_t m_priority;
|
|
uint32_t m_material_index;
|
|
};
|
|
} command_index;
|
|
|
|
command_index.m_priority = 0;
|
|
if (material.m_masked_shadow) command_index.m_priority |= 1;
|
|
|
|
command_index.m_material_index = batch.m_material;
|
|
|
|
batches_to_render.emplace_back(command_index.m_command_index,
|
|
batch_index);
|
|
|
|
// if (indexed)
|
|
// pass.m_command_list_draw->drawIndexedIndirect(0);
|
|
// else
|
|
// pass.m_command_list_draw->drawIndirect(0);
|
|
// m_drawcall_counter->Draw(draw_arguments);
|
|
}
|
|
});
|
|
if (pass.m_sort_batches) {
|
|
std::sort(batches_to_render.begin(), batches_to_render.end(),
|
|
[](const auto& lhs, const auto& rhs) -> bool {
|
|
return std::get<0>(lhs) < std::get<0>(rhs);
|
|
});
|
|
}
|
|
for (const auto& [material_handle, batch_index] : batches_to_render) {
|
|
const GeometryBatch& batch = m_geometry_batches[batch_index];
|
|
nvrhi::GraphicsState gfx_state;
|
|
nvrhi::DrawArguments draw_arguments{};
|
|
bool indexed;
|
|
float alpha_threshold;
|
|
if (!BindGeometry(batch.m_geometry, pass, gfx_state, draw_arguments,
|
|
indexed))
|
|
return;
|
|
|
|
if (!BindMaterial(batch.m_material, DrawType::InstancedModel, pass,
|
|
gfx_state, alpha_threshold))
|
|
return;
|
|
|
|
BindConstants(pass, gfx_state);
|
|
|
|
gfx_state.addVertexBuffer(nvrhi::VertexBufferBinding().setSlot(1).setBuffer(
|
|
batch.m_static_instance_buffer));
|
|
|
|
pass.m_command_list_draw->beginMarker(batch.m_name.c_str());
|
|
|
|
pass.m_command_list_draw->setGraphicsState(gfx_state);
|
|
|
|
UpdateDrawData(pass, batch_transform * glm::translate(batch.m_origin),
|
|
history_transform * glm::translate(batch.m_origin),
|
|
alpha_threshold, 1.f, 0.f, batch.m_diffuse);
|
|
|
|
draw_arguments.setInstanceCount(batch.m_transforms.size());
|
|
if (indexed)
|
|
pass.m_command_list_draw->drawIndexed(draw_arguments);
|
|
else
|
|
pass.m_command_list_draw->draw(draw_arguments);
|
|
|
|
pass.m_command_list_draw->endMarker();
|
|
m_drawcall_counter->Draw(draw_arguments);
|
|
}
|
|
m_drawcall_counter = nullptr;
|
|
}
|
|
|
|
void NvRenderer::RenderTracks(const RenderPass& pass) {
|
|
if (!pass.m_draw_track) return;
|
|
m_drawcall_counter = &m_drawcalls_track;
|
|
|
|
auto motion_scope = m_motion_cache->SetInstance(nullptr);
|
|
const auto& motion_cache = m_motion_cache->Get(nullptr);
|
|
glm::dmat4 batch_transform =
|
|
pass.m_transform * motion_cache.m_cached_transform;
|
|
glm::dmat4 history_transform =
|
|
pass.m_history_transform * motion_cache.m_history_transform;
|
|
|
|
m_track_quadtree.ForEach(pass, [&](size_t batch_index) {
|
|
TrackBatch& batch = m_track_batches[batch_index];
|
|
nvrhi::GraphicsState gfx_state;
|
|
nvrhi::DrawArguments draw_arguments{};
|
|
bool indexed;
|
|
float alpha_threshold;
|
|
if (!BindGeometry(batch.m_geometry, pass, gfx_state, draw_arguments,
|
|
indexed))
|
|
return;
|
|
if (!BindMaterial(batch.m_material, DrawType::Model, pass, gfx_state,
|
|
alpha_threshold))
|
|
return;
|
|
|
|
if (!batch.m_command_buffer) {
|
|
batch.m_command_buffer = GetBackend()->GetDevice()->createBuffer(
|
|
nvrhi::BufferDesc()
|
|
.setByteSize(batch.m_draw_commands.size() *
|
|
sizeof(nvrhi::DrawArguments))
|
|
.setStructStride(sizeof(nvrhi::DrawArguments))
|
|
.setIsDrawIndirectArgs(true)
|
|
.setInitialState(nvrhi::ResourceStates::IndirectArgument)
|
|
.setKeepInitialState(true));
|
|
pass.m_command_list_preparation->writeBuffer(
|
|
batch.m_command_buffer, batch.m_draw_commands.data(),
|
|
batch.m_draw_commands.size() * sizeof(nvrhi::DrawArguments));
|
|
}
|
|
|
|
{ // Partial buffer update (needed immediately for legacy animations)
|
|
if (const auto& chunk = m_geometry_banks[batch.m_geometry.bank - 1]
|
|
.m_chunks[batch.m_geometry.chunk - 1];
|
|
chunk.m_vertex_buffer) {
|
|
for (int i = 0; i < batch.m_regions.size(); ++i) {
|
|
if (!batch.m_regions_need_update[i]) continue;
|
|
batch.m_regions_need_update[i] = false;
|
|
const auto& region = batch.m_regions[i];
|
|
pass.m_command_list_preparation->writeBuffer(
|
|
chunk.m_vertex_buffer, &chunk.m_vertices[region.m_start_vertex],
|
|
sizeof(gfx::basic_vertex) * region.m_vertex_count,
|
|
sizeof(gfx::basic_vertex) * region.m_start_vertex);
|
|
}
|
|
}
|
|
}
|
|
|
|
gfx_state.setIndirectParams(batch.m_command_buffer);
|
|
|
|
BindConstants(pass, gfx_state);
|
|
|
|
glm::dmat4 instance_transform = glm::translate(batch.m_geometry_origin);
|
|
|
|
pass.m_command_list_draw->beginMarker(batch.m_name.c_str());
|
|
|
|
pass.m_command_list_draw->setGraphicsState(gfx_state);
|
|
|
|
UpdateDrawData(pass, batch_transform * instance_transform,
|
|
history_transform * instance_transform, alpha_threshold, 1.f,
|
|
0.f, batch.m_diffuse);
|
|
|
|
pass.m_command_list_draw->drawIndexedIndirect(0,
|
|
batch.m_draw_commands.size());
|
|
|
|
m_drawcall_counter->Draw(draw_arguments);
|
|
|
|
pass.m_command_list_draw->endMarker();
|
|
});
|
|
m_drawcall_counter = nullptr;
|
|
}
|
|
|
|
void NvRenderer::RenderShapes(const RenderPass& pass) {
|
|
if (!pass.m_draw_shapes) return;
|
|
m_drawcall_counter = &m_drawcalls_shape;
|
|
|
|
auto motion_scope = m_motion_cache->SetInstance(nullptr);
|
|
const auto& motion_cache = m_motion_cache->Get(nullptr);
|
|
glm::dmat4 batch_transform =
|
|
pass.m_transform * motion_cache.m_cached_transform;
|
|
glm::dmat4 history_transform =
|
|
pass.m_history_transform * motion_cache.m_history_transform;
|
|
|
|
m_shape_quadtree.ForEach(pass, [&](uint32_t shape_index) {
|
|
const auto& shape = m_shapes[shape_index];
|
|
switch (pass.m_type) {
|
|
case RenderPassType::Forward:
|
|
if (!shape.m_draw_in_transparent) return;
|
|
break;
|
|
default:
|
|
if (!shape.m_draw_in_opaque) return;
|
|
}
|
|
nvrhi::GraphicsState gfx_state;
|
|
nvrhi::DrawArguments draw_arguments{};
|
|
bool indexed;
|
|
float alpha_threshold;
|
|
if (!BindGeometry(shape.m_geometry, pass, gfx_state, draw_arguments,
|
|
indexed))
|
|
return;
|
|
if (!BindMaterial(shape.m_material, DrawType::Model, pass, gfx_state,
|
|
alpha_threshold))
|
|
return;
|
|
|
|
BindConstants(pass, gfx_state);
|
|
|
|
glm::dmat4 instance_transform = glm::translate(shape.m_geometry_origin);
|
|
|
|
// pass.m_command_list_draw->beginMarker(batch.m_name.c_str());
|
|
|
|
pass.m_command_list_draw->setGraphicsState(gfx_state);
|
|
|
|
UpdateDrawData(pass, batch_transform * instance_transform,
|
|
history_transform * instance_transform, alpha_threshold, 1.f,
|
|
0.f, shape.m_diffuse);
|
|
|
|
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);
|
|
});
|
|
}
|
|
|
|
void NvRenderer::RenderLines(const NvRenderer::RenderPass& pass) {
|
|
if (!pass.m_draw_lines) return;
|
|
m_drawcall_counter = &m_drawcalls_line;
|
|
|
|
auto motion_scope = m_motion_cache->SetInstance(nullptr);
|
|
const auto& motion_cache = m_motion_cache->Get(nullptr);
|
|
glm::dmat4 batch_transform =
|
|
pass.m_transform * motion_cache.m_cached_transform;
|
|
glm::dmat4 history_transform =
|
|
pass.m_history_transform * motion_cache.m_history_transform;
|
|
|
|
pass.m_command_list_draw->beginMarker("Lines");
|
|
|
|
m_line_quadtree.ForEach(pass, [&](uint32_t shape_index) {
|
|
const auto& line = m_lines[shape_index];
|
|
|
|
nvrhi::GraphicsState gfx_state;
|
|
nvrhi::DrawArguments draw_arguments{};
|
|
bool indexed;
|
|
if (!BindGeometry(line.m_geometry, pass, gfx_state, draw_arguments,
|
|
indexed))
|
|
return;
|
|
if (!BindLineMaterial(DrawType::Model, pass, gfx_state)) return;
|
|
|
|
BindConstants(pass, gfx_state);
|
|
|
|
glm::dmat4 instance_transform = glm::translate(line.m_geometry_origin);
|
|
|
|
// pass.m_command_list_draw->beginMarker(batch.m_name.c_str());
|
|
|
|
pass.m_command_list_draw->setGraphicsState(gfx_state);
|
|
|
|
UpdateDrawDataLine(pass, batch_transform * instance_transform,
|
|
history_transform * instance_transform, line);
|
|
|
|
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();
|
|
}
|
|
|
|
void NvRenderer::RenderAnimateds(const RenderPass& pass) {
|
|
auto motion_scope = m_motion_cache->SetInstance(nullptr);
|
|
const auto& motion_cache = m_motion_cache->Get(nullptr);
|
|
glm::dvec3 history_origin = motion_cache.m_history_transform[3];
|
|
|
|
struct RenderCommand {
|
|
enum ObjectType { ObjectType_Animated, ObjectType_Dynamic };
|
|
uint32_t m_render_distance;
|
|
ObjectType m_type;
|
|
uint32_t m_index;
|
|
void SetDistance(float distance) {
|
|
union {
|
|
uint32_t asuint;
|
|
float asfloat;
|
|
} val;
|
|
val.asfloat = distance;
|
|
constexpr static uint32_t sign_mask = 1 << 31;
|
|
m_render_distance =
|
|
val.asuint & sign_mask ? val.asuint : sign_mask - val.asuint;
|
|
}
|
|
};
|
|
|
|
struct RenderCommandComparer {
|
|
bool operator()(const RenderCommand& lhs, const RenderCommand& rhs) const {
|
|
return lhs.m_render_distance < rhs.m_render_distance;
|
|
}
|
|
};
|
|
|
|
static std::vector<RenderCommand> render_commands{};
|
|
render_commands.clear();
|
|
|
|
if (pass.m_draw_tanimobj) {
|
|
m_animated_quadtree.ForEach(pass, [&](uint32_t animated_index) {
|
|
const auto& instance = m_animateds[animated_index];
|
|
|
|
switch (pass.m_type) {
|
|
case RenderPassType::Forward:
|
|
if (!instance.m_renderable.m_render_in_forward) return;
|
|
break;
|
|
default:
|
|
if (!instance.m_renderable.m_render_in_deferred) return;
|
|
}
|
|
|
|
double distance = glm::distance2(instance.m_origin, pass.m_origin);
|
|
if (distance >= instance.m_sqr_distance_max ||
|
|
distance < instance.m_sqr_distance_min)
|
|
return;
|
|
|
|
RenderCommand render_command{};
|
|
render_command.SetDistance(distance);
|
|
render_command.m_type = RenderCommand::ObjectType_Animated;
|
|
render_command.m_index = animated_index;
|
|
|
|
render_commands.emplace_back(render_command);
|
|
});
|
|
}
|
|
|
|
if (pass.m_draw_dynamic) {
|
|
for (uint32_t dynamic_index : m_renderable_dynamics) {
|
|
const auto& dynamic = m_dynamics[dynamic_index];
|
|
|
|
switch (pass.m_type) {
|
|
case RenderPassType::Forward:
|
|
if (!dynamic.m_renderable.m_render_in_forward) continue;
|
|
break;
|
|
default:
|
|
if (!dynamic.m_renderable.m_render_in_deferred) continue;
|
|
}
|
|
|
|
if (!pass.SphereVisible(dynamic.m_location, dynamic.m_radius)) continue;
|
|
|
|
RenderCommand render_command{};
|
|
render_command.SetDistance(dynamic.m_distance);
|
|
render_command.m_type = RenderCommand::ObjectType_Dynamic;
|
|
render_command.m_index = dynamic_index;
|
|
|
|
render_commands.emplace_back(render_command);
|
|
}
|
|
}
|
|
|
|
if (pass.m_type == RenderPassType::Forward && pass.m_sort_transparents) {
|
|
std::sort(render_commands.begin(), render_commands.end(),
|
|
RenderCommandComparer{});
|
|
}
|
|
|
|
for (const auto& command : render_commands) {
|
|
switch (command.m_type) {
|
|
case RenderCommand::ObjectType_Animated:
|
|
m_drawcall_counter = &m_drawcalls_tanimobj;
|
|
pass.m_command_list_draw->beginMarker(
|
|
m_animateds[command.m_index].m_model->name().c_str());
|
|
Render(m_animateds[command.m_index].m_renderable, pass, history_origin,
|
|
m_animateds[command.m_index].m_distance);
|
|
pass.m_command_list_draw->endMarker();
|
|
break;
|
|
case RenderCommand::ObjectType_Dynamic:
|
|
m_drawcall_counter = &m_drawcalls_dynamic;
|
|
pass.m_command_list_draw->beginMarker(
|
|
m_dynamics[command.m_index].m_dynamic->name().c_str());
|
|
Render(m_dynamics[command.m_index].m_renderable, pass, history_origin,
|
|
m_dynamics[command.m_index].m_distance);
|
|
pass.m_command_list_draw->endMarker();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void NvRenderer::RenderKabina(const RenderPass& pass) {
|
|
if (!pass.m_draw_dynamic) return;
|
|
if (m_dynamic_with_kabina >= m_dynamics.size()) return;
|
|
|
|
m_drawcall_counter = &m_drawcalls_dynamic;
|
|
|
|
auto motion_scope = m_motion_cache->SetInstance(nullptr);
|
|
const auto& motion_cache = m_motion_cache->Get(nullptr);
|
|
glm::dvec3 history_origin = motion_cache.m_history_transform[3];
|
|
|
|
const auto& dynamic = m_dynamics[m_dynamic_with_kabina];
|
|
switch (pass.m_type) {
|
|
case RenderPassType::Forward:
|
|
if (!dynamic.m_renderable_kabina.m_render_in_forward) return;
|
|
break;
|
|
default:
|
|
if (!dynamic.m_renderable_kabina.m_render_in_deferred) return;
|
|
}
|
|
pass.m_command_list_draw->beginMarker("Render cab");
|
|
Render(dynamic.m_renderable_kabina, pass, history_origin, dynamic.m_distance);
|
|
pass.m_command_list_draw->endMarker();
|
|
}
|
|
|
|
void NvRenderer::Render(const Renderable& renderable, const RenderPass& pass,
|
|
const glm::dvec3& history_origin, double distance) {
|
|
for (const auto& item : renderable.m_items) {
|
|
if (item.m_render_in_forward != (pass.m_type == RenderPassType::Forward))
|
|
continue;
|
|
if (distance < item.m_sqr_distance_min ||
|
|
distance >= item.m_sqr_distance_max)
|
|
continue;
|
|
const auto& [origin, extent] = GetGeometryBounds(item.m_geometry);
|
|
if (!pass.BoxVisible(item.m_transform, origin, extent)) continue;
|
|
|
|
nvrhi::GraphicsState gfx_state;
|
|
nvrhi::DrawArguments draw_arguments{};
|
|
bool indexed;
|
|
float alpha_threshold;
|
|
if (!BindGeometry(item.m_geometry, pass, gfx_state, draw_arguments,
|
|
indexed))
|
|
continue;
|
|
if (!BindMaterial(item.m_material, DrawType::Model, pass, gfx_state,
|
|
alpha_threshold))
|
|
continue;
|
|
|
|
BindConstants(pass, gfx_state);
|
|
|
|
pass.m_command_list_draw->beginMarker(item.m_name.data());
|
|
|
|
pass.m_command_list_draw->setGraphicsState(gfx_state);
|
|
|
|
auto transform = item.m_transform;
|
|
transform[3] -= pass.m_origin;
|
|
|
|
auto history_transform = item.m_history_transform;
|
|
history_transform[3] += history_origin;
|
|
|
|
UpdateDrawData(pass, pass.m_transform * transform,
|
|
pass.m_history_transform * history_transform,
|
|
alpha_threshold, item.m_opacity, item.m_selfillum,
|
|
item.m_diffuse);
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
void NvRenderer::Animate(const glm::dvec3& origin, double radius,
|
|
uint64_t frame) {
|
|
{
|
|
auto motion_scope = m_motion_cache->SetInstance(nullptr);
|
|
auto& motion_cache = m_motion_cache->Get(nullptr);
|
|
motion_cache.m_history_transform =
|
|
std::exchange(motion_cache.m_cached_transform, glm::translate(-origin));
|
|
}
|
|
|
|
BatchingUtils::RadiusTester tester{origin, radius};
|
|
|
|
m_renderable_dynamics.clear();
|
|
m_dynamic_with_kabina = static_cast<uint32_t>(-1);
|
|
|
|
for (uint32_t i = 0; i < m_dynamics.size(); ++i) {
|
|
auto& dynamic = m_dynamics[i];
|
|
dynamic.m_location = dynamic.m_dynamic->vPosition;
|
|
dynamic.m_distance = glm::distance2(dynamic.m_location, origin);
|
|
|
|
if (Global.pCamera.m_owner == dynamic.m_dynamic) m_dynamic_with_kabina = i;
|
|
|
|
if (m_dynamic_with_kabina == i ||
|
|
dynamic.m_radius * dynamic.m_radius / dynamic.m_distance >=
|
|
.005 * .005) {
|
|
m_renderable_dynamics.emplace_back(i);
|
|
Animate(dynamic, origin, frame);
|
|
}
|
|
}
|
|
|
|
m_cell_quadtree.ForEach(tester, [&](uint32_t cell_index) {
|
|
const auto& cell = m_cells[cell_index];
|
|
cell.m_cell->RaAnimate(frame);
|
|
});
|
|
|
|
m_animated_quadtree.ForEach(tester, [&](uint32_t animated_index) {
|
|
auto& instance = m_animateds[animated_index];
|
|
instance.m_distance = glm::distance2(instance.m_origin, origin);
|
|
if (!instance.m_animatable && instance.m_was_once_animated) return;
|
|
Animate(instance, origin, frame);
|
|
});
|
|
}
|
|
|
|
void NvRenderer::GatherSpotLights(const RenderPass& pass) {
|
|
m_renderable_spotlights.clear();
|
|
auto traverse_spotlights = [&](const Renderable& renderable) {
|
|
for (const auto& spotlight : renderable.m_spotlights) {
|
|
if (pass.SphereVisible(spotlight.m_origin, spotlight.m_radius))
|
|
m_renderable_spotlights.emplace_back(spotlight);
|
|
}
|
|
};
|
|
for (const auto& dyn_id : m_renderable_dynamics) {
|
|
const auto& dynamic = m_dynamics[dyn_id];
|
|
traverse_spotlights(dynamic.m_renderable);
|
|
if (dyn_id == m_dynamic_with_kabina)
|
|
traverse_spotlights(dynamic.m_renderable_kabina);
|
|
}
|
|
m_animated_quadtree.ForEach(
|
|
BatchingUtils::RadiusTester{pass.m_origin, pass.m_draw_range},
|
|
[&](uint32_t animated_index) {
|
|
auto& instance = m_animateds[animated_index];
|
|
if (instance.m_renderable.m_spotlights.empty()) return;
|
|
traverse_spotlights(instance.m_renderable);
|
|
});
|
|
}
|