mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-01 23:59:12 +01:00
Vulkan: scene rendering (D1–D7) on a scene walk shared with GL (N6a, N6b)
This commit is contained in:
@@ -1,527 +0,0 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "rendering/opengl46/opengl46locallights.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
|
||||
#include "utilities/Globals.h"
|
||||
#include "utilities/utilities.h"
|
||||
#include "scene/scene.h"
|
||||
#include "model/AnimModel.h"
|
||||
#include "model/Model3d.h"
|
||||
#include "vehicle/DynObj.h"
|
||||
#include "vehicle/Train.h"
|
||||
#include "simulation/simulation.h"
|
||||
#include "rendering/geometrybank.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
// a light ends where its contribution falls under this. it is above the limit of the culling of the clusters, as there are
|
||||
// many more of these lights than headlights, and each of them lights only its surroundings
|
||||
float const LIGHT_THRESHOLD{0.01f};
|
||||
// no light of a model reaches farther than this, metres
|
||||
float const LIGHT_RANGE_MAX{60.f};
|
||||
// the self-illumination threshold of the lamps of scenery models (Light_On, ls_Dark) and full darkness: between them
|
||||
// the lights of models and compartments fade in, as they would be lost in the daylight anyway
|
||||
float const LIGHT_DUSK_LUMINANCE{0.325f};
|
||||
float const LIGHT_DAY_LUMINANCE{1.0f};
|
||||
|
||||
glm::vec3 to_linear(glm::vec3 const &Color)
|
||||
{
|
||||
return glm::pow(glm::max(Color, glm::vec3{0.f}), glm::vec3{2.2f});
|
||||
}
|
||||
|
||||
float max_component(glm::vec3 const &Color)
|
||||
{
|
||||
return std::max(Color.r, std::max(Color.g, Color.b));
|
||||
}
|
||||
|
||||
// distance at which a light of the colour falls under the threshold with the square of the distance
|
||||
float light_range(glm::vec3 const &Color)
|
||||
{
|
||||
return std::min(std::sqrt(max_component(Color) / LIGHT_THRESHOLD), LIGHT_RANGE_MAX);
|
||||
}
|
||||
|
||||
// model space transform of the parent of the submodel, from the static transforms of the submodels above it; false if the
|
||||
// submodel is not in the tree
|
||||
bool find_parent_transform(TSubModel const *Submodel, TSubModel const *Target, glm::mat4 const &Parent, glm::mat4 &Result)
|
||||
{
|
||||
for (auto const *submodel{Submodel}; submodel != nullptr; submodel = submodel->Next)
|
||||
{
|
||||
if (submodel == Target)
|
||||
{
|
||||
Result = Parent;
|
||||
return true;
|
||||
}
|
||||
auto transform{Parent};
|
||||
if ((submodel->iFlags & 0xC000) && (submodel->fMatrix != nullptr))
|
||||
{
|
||||
transform *= glm::make_mat4(submodel->fMatrix->readArray());
|
||||
}
|
||||
if (find_parent_transform(submodel->Child, Target, transform, Result))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// bounds of the geometry of the submodel, of its children and, with Siblings, of the submodels after it, in the space of Transform
|
||||
void extend_bounds(TSubModel const *Submodel, glm::mat4 const &Transform, gfx::geometrybank_manager const &Geometry, bool const Siblings, glm::vec3 &Min, glm::vec3 &Max,
|
||||
bool &Lightpoints)
|
||||
{
|
||||
for (auto const *submodel{Submodel}; submodel != nullptr; submodel = (Siblings ? submodel->Next : nullptr))
|
||||
{
|
||||
auto transform{Transform};
|
||||
if ((submodel->iFlags & 0xC000) && (submodel->fMatrix != nullptr))
|
||||
{
|
||||
transform *= glm::make_mat4(submodel->fMatrix->readArray());
|
||||
}
|
||||
if (submodel->eType == TP_FREESPOTLIGHT)
|
||||
{
|
||||
Lightpoints = true;
|
||||
}
|
||||
else if ((submodel->eType < TP_ROTATOR) && (submodel->m_geometry.handle != null_handle))
|
||||
{
|
||||
for (auto const &vertex : Geometry.vertices(submodel->m_geometry.handle))
|
||||
{
|
||||
auto const point{glm::vec3{transform * glm::vec4{vertex.position, 1.f}}};
|
||||
Min = glm::min(Min, point);
|
||||
Max = glm::max(Max, point);
|
||||
}
|
||||
}
|
||||
extend_bounds(submodel->Child, transform, Geometry, true, Min, Max, Lightpoints);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void opengl46_local_lights::gather(glm::dvec3 const &Camera, scene::basic_region &Region, gfx::geometrybank_manager const &Geometry, unsigned int const Framestamp)
|
||||
{
|
||||
auto const start{std::chrono::steady_clock::now()};
|
||||
|
||||
m_lights.clear();
|
||||
m_cablampon = false;
|
||||
m_scenerycount = m_vehiclecount = m_compartmentcount = 0;
|
||||
|
||||
// the editor adds, moves and removes instances in the cells. it can also do it in the frame it is turned off in
|
||||
auto const rebuild{EditorModeFlag || m_editorseen};
|
||||
m_editorseen = EditorModeFlag;
|
||||
|
||||
if (Global.gfx_lights_clustered)
|
||||
{
|
||||
if (Global.gfx_lights_cab)
|
||||
{
|
||||
gather_cab_lamp(Geometry);
|
||||
}
|
||||
|
||||
// lamps are lost in the daylight; this also spares the work of gathering them in the middle of the day
|
||||
auto const darkness{1.f - glm::smoothstep(LIGHT_DUSK_LUMINANCE, LIGHT_DAY_LUMINANCE, static_cast<float>(Global.fLuminance))};
|
||||
auto const range{Global.gfx_lights_range};
|
||||
|
||||
if ((darkness > 0.f) && (range > 0.f) && Global.gfx_lights_scenery)
|
||||
{
|
||||
auto const freespotbrightness{Global.gfx_lights_freespot_intensity * darkness};
|
||||
// sections of the region within the range, the same way the render picks them
|
||||
auto const camerax{static_cast<int>(std::floor(Camera.x / scene::EU07_SECTIONSIZE + scene::EU07_REGIONSIDESECTIONCOUNT / 2))};
|
||||
auto const cameraz{static_cast<int>(std::floor(Camera.z / scene::EU07_SECTIONSIZE + scene::EU07_REGIONSIDESECTIONCOUNT / 2))};
|
||||
auto const reach{static_cast<int>(std::ceil(range / scene::EU07_SECTIONSIZE))};
|
||||
for (int row = std::max(0, cameraz - reach); row <= std::min(scene::EU07_REGIONSIDESECTIONCOUNT - 1, cameraz + reach); ++row)
|
||||
{
|
||||
for (int column = std::max(0, camerax - reach); column <= std::min(scene::EU07_REGIONSIDESECTIONCOUNT - 1, camerax + reach); ++column)
|
||||
{
|
||||
auto *section{Region.m_sections[row * scene::EU07_REGIONSIDESECTIONCOUNT + column]};
|
||||
if (section == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (auto &cell : section->m_cells)
|
||||
{
|
||||
if ((false == cell.m_active) || (glm::length(cell.m_area.center - Camera) > range + cell.m_area.radius))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (auto *instance : cell_instances(cell, rebuild).instances)
|
||||
{
|
||||
gather_instance(instance, Camera, freespotbrightness, Framestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((darkness > 0.f) && (range > 0.f) && Global.gfx_lights_vehicles)
|
||||
{
|
||||
auto const freespotbrightness{Global.gfx_lights_freespot_intensity * darkness};
|
||||
auto const compartmentbrightness{Global.gfx_lights_compartment_intensity * darkness};
|
||||
for (auto *vehicle : simulation::Vehicles.sequence())
|
||||
{
|
||||
if (glm::length(vehicle->GetPosition() - Camera) > range + vehicle->radius())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
gather_vehicle(vehicle, Camera, Geometry, freespotbrightness, compartmentbrightness);
|
||||
}
|
||||
}
|
||||
|
||||
// the most important lights first: when the light buffer or the list of a cluster fills up, the others are left out
|
||||
std::sort(std::begin(m_lights), std::end(m_lights), [&Camera](light const &Left, light const &Right) {
|
||||
return glm::length2(Left.position - Camera) / std::max(max_component(Left.color), 1e-6f) < glm::length2(Right.position - Camera) / std::max(max_component(Right.color), 1e-6f);
|
||||
});
|
||||
}
|
||||
|
||||
auto const elapsed{std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start).count()};
|
||||
m_gathertime = (m_gathertime == 0.0 ? elapsed : m_gathertime * 0.95 + elapsed * 0.05);
|
||||
}
|
||||
|
||||
void opengl46_local_lights::reset()
|
||||
{
|
||||
m_lights.clear();
|
||||
m_cablampon = false;
|
||||
m_modellights.clear();
|
||||
m_cells.clear();
|
||||
m_sections.clear();
|
||||
m_models.clear();
|
||||
m_scenerycount = m_vehiclecount = m_compartmentcount = m_placed = m_dropped = 0;
|
||||
}
|
||||
|
||||
void opengl46_local_lights::gather_instance(TAnimModel *Instance, glm::dvec3 const &Camera, float const Brightness, unsigned int const Framestamp)
|
||||
{
|
||||
if ((false == Instance->m_visible) || (Instance->pModel == nullptr))
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto const offset{Instance->location() - Camera};
|
||||
if (glm::length(offset) > Global.gfx_lights_range + Instance->radius())
|
||||
{
|
||||
return;
|
||||
}
|
||||
// the same distance the render picks the lods and the range of the instance by
|
||||
auto const distancesquared{glm::length2(offset / static_cast<double>(Global.ZoomFactor)) / Global.fDistanceFactor};
|
||||
if ((distancesquared < Instance->m_rangesquaredmin) || (distancesquared >= Instance->m_rangesquaredmax))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Instance->RaAnimate(Framestamp);
|
||||
Instance->RaPrepare();
|
||||
TSubModel::fSquareDist = static_cast<float>(distancesquared);
|
||||
|
||||
auto const count{m_lights.size()};
|
||||
auto const placement{glm::translate(glm::dmat4{1.0}, Instance->location()) * glm::dmat4{Instance->rotation_scale()}};
|
||||
m_walkowner = Instance;
|
||||
walk(Instance->pModel->Root, glm::mat4{1.f}, placement, Brightness);
|
||||
m_walkowner = nullptr;
|
||||
m_scenerycount += static_cast<std::uint32_t>(m_lights.size() - count);
|
||||
}
|
||||
|
||||
void opengl46_local_lights::gather_vehicle(TDynamicObject *Vehicle, glm::dvec3 const &Camera, gfx::geometrybank_manager const &Geometry, float const Freespotbrightness,
|
||||
float const Compartmentbrightness)
|
||||
{
|
||||
if ((false == Global.render_cab) && (Global.pCamera.m_owner == Vehicle))
|
||||
{
|
||||
// the render leaves the vehicle out as well
|
||||
return;
|
||||
}
|
||||
// light points: the models with any are gone through after the setup the render makes before it draws the vehicle
|
||||
std::array<TModel3d *, 2> models{Vehicle->mdModel, Vehicle->mdLowPolyInt};
|
||||
auto const haslightpoints = [this](TModel3d *Model) { return (Model != nullptr) && has_light_points(Model); };
|
||||
if (std::any_of(std::begin(models), std::end(models), haslightpoints) || std::any_of(std::begin(Vehicle->mdAttachments), std::end(Vehicle->mdAttachments), haslightpoints))
|
||||
{
|
||||
TSubModel::iInstance = reinterpret_cast<std::uintptr_t>(Vehicle);
|
||||
TSubModel::iVariantSeed = Vehicle->variant_seed();
|
||||
auto const squaredistance{glm::length2(glm::vec3{Vehicle->vPosition - Camera} / Global.ZoomFactor)};
|
||||
Vehicle->ABuLittleUpdate(squaredistance);
|
||||
|
||||
auto const placement{glm::translate(glm::dmat4{1.0}, Vehicle->vPosition) * Vehicle->mMatrix};
|
||||
auto const count{m_lights.size()};
|
||||
auto const walkmodel = [&](TModel3d *Model) {
|
||||
if (haslightpoints(Model))
|
||||
{
|
||||
TSubModel::fSquareDist = squaredistance;
|
||||
walk(Model->Root, glm::mat4{1.f}, placement, Freespotbrightness);
|
||||
}
|
||||
};
|
||||
walkmodel(Vehicle->mdModel);
|
||||
for (auto *attachment : Vehicle->mdAttachments)
|
||||
{
|
||||
walkmodel(attachment);
|
||||
}
|
||||
walkmodel(Vehicle->mdLowPolyInt);
|
||||
m_vehiclecount += static_cast<std::uint32_t>(m_lights.size() - count);
|
||||
|
||||
if (Vehicle->btnOn)
|
||||
{
|
||||
Vehicle->TurnOff(); // the state of the shared model goes back to the default, as after the render
|
||||
}
|
||||
}
|
||||
|
||||
// lit compartments, the way TDynamicObject::Update() works out their self-illumination: cab sections come first and are lit at
|
||||
// the level of their lamp, the others at the level of the compartment lights
|
||||
if ((Vehicle->mdLowPolyInt != nullptr) && (Compartmentbrightness > 0.f))
|
||||
{
|
||||
auto const owncab{m_cablampon && (simulation::Train != nullptr) && (simulation::Train->Dynamic() == Vehicle)};
|
||||
auto cabsection{true};
|
||||
for (auto const §ion : Vehicle->Sections)
|
||||
{
|
||||
auto const &name{section.compartment->pName};
|
||||
cabsection &= (name.size() >= 4) && name.starts_with("cab");
|
||||
auto const level{section.light_level * (cabsection ? 1.f : Vehicle->MoverParameters->CompartmentLights.intensity)};
|
||||
if ((level <= 0.f) || (Global.fLuminance > section.compartment->fLight) || (section.compartment->iVisible == 0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (cabsection && owncab)
|
||||
{
|
||||
// the lamp of the occupied cab is a light of its own
|
||||
continue;
|
||||
}
|
||||
auto const &data{this->section(Vehicle->mdLowPolyInt, section.compartment, Geometry)};
|
||||
if (data.has_light_points || (data.extent == glm::vec3{0.f}))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
light compartmentlight;
|
||||
compartmentlight.position = Vehicle->GetWorldPosition(glm::dvec3{data.center});
|
||||
compartmentlight.color = to_linear(Vehicle->InteriorLight) * (level * Compartmentbrightness);
|
||||
// the light goes out of the windows a little; there are no shadows to keep it in, so it doesn't reach far
|
||||
compartmentlight.range = std::clamp(glm::length(data.extent) * 1.5f, 2.f, std::min(light_range(compartmentlight.color), 15.f));
|
||||
m_lights.emplace_back(compartmentlight);
|
||||
++m_compartmentcount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void opengl46_local_lights::gather_cab_lamp(gfx::geometrybank_manager const &Geometry)
|
||||
{
|
||||
// the cab is drawn, Render_cab() conditions
|
||||
if (FreeFlyModeFlag || (false == Global.render_cab) || (simulation::Train == nullptr))
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto const *vehicle{simulation::Train->Dynamic()};
|
||||
if ((vehicle == nullptr) || (false == vehicle->bDisplayCab) || (vehicle->mdKabina == nullptr) || (vehicle->mdKabina == vehicle->mdModel))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (vehicle->InteriorLightLevel <= 0.f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// as bright as the ambient light it used to add to the cab: lost in the daylight
|
||||
auto const luminance{Global.fLuminance * (vehicle->fShade > 0.0f ? vehicle->fShade : 1.0f)};
|
||||
auto const level{vehicle->InteriorLightLevel * std::clamp(1.25f - static_cast<float>(luminance), 0.f, 1.f)};
|
||||
if (level <= 0.f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto const &bounds{model_bounds(vehicle->mdKabina, Geometry)};
|
||||
if (bounds.extent == glm::vec3{0.f})
|
||||
{
|
||||
return;
|
||||
}
|
||||
// under the ceiling, in the middle of the cab
|
||||
auto const lamp{glm::vec3{bounds.center.x, std::max(bounds.center.y, bounds.center.y + bounds.extent.y - 0.25f), bounds.center.z}};
|
||||
m_cablamp = light{};
|
||||
m_cablamp.position = vehicle->GetWorldPosition(glm::dvec3{lamp});
|
||||
m_cablampvehicle = vehicle;
|
||||
m_cablamplocal = lamp;
|
||||
m_cablamp.color = to_linear(vehicle->InteriorLight) * (level * Global.gfx_lights_cab_intensity);
|
||||
// within the cab: without shadows (gfx.lights.shadows) the lamp would light the track below and the body of the vehicle around it
|
||||
m_cablamp.range = std::clamp(glm::length(bounds.extent) * 1.25f, 2.5f, 8.f);
|
||||
m_cablampon = (max_component(m_cablamp.color) > 0.f);
|
||||
}
|
||||
|
||||
void opengl46_local_lights::walk(TSubModel *Submodel, glm::mat4 const &Transform, glm::dmat4 const &Placement, float const Brightness)
|
||||
{
|
||||
// visibility, lod ranges, transforms and animations as in opengl46_renderer::Render_Alpha(TSubModel *)
|
||||
for (auto *submodel{Submodel}; submodel != nullptr; submodel = submodel->Next)
|
||||
{
|
||||
if ((submodel->iVisible == 0) || (TSubModel::fSquareDist < submodel->fSquareMinDist) || (TSubModel::fSquareDist >= submodel->fSquareMaxDist))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto transform{Transform};
|
||||
if (submodel->iFlags & 0xC000)
|
||||
{
|
||||
if (submodel->fMatrix != nullptr)
|
||||
{
|
||||
transform *= glm::make_mat4(submodel->fMatrix->readArray());
|
||||
}
|
||||
if (submodel->b_aAnim != TAnimType::at_None)
|
||||
{
|
||||
submodel->RaAnimation(transform, submodel->b_aAnim);
|
||||
}
|
||||
}
|
||||
if (submodel->eType == TP_FREESPOTLIGHT)
|
||||
{
|
||||
add_light_point(*submodel, transform, Placement, Brightness);
|
||||
}
|
||||
if (submodel->Child != nullptr)
|
||||
{
|
||||
walk(submodel->Child, transform, Placement, Brightness);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void opengl46_local_lights::add_light_point(TSubModel const &Submodel, glm::mat4 const &Transform, glm::dmat4 const &Placement, float const Brightness)
|
||||
{
|
||||
// lit points: with self-illumination, past its threshold. the compartments of vehicles set it with their lights
|
||||
if ((Submodel.f4Emision.a <= 0.f) || (Global.fLuminance >= Submodel.fLight) || (Submodel.fVisible <= 0.f))
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto const diffuse{Submodel.DiffuseOverride.r < 0.f ? // -1 indicates no override
|
||||
glm::vec3{Submodel.f4Diffuse} :
|
||||
Submodel.DiffuseOverride};
|
||||
auto const color{to_linear(diffuse) * (Brightness * Submodel.fHotspotPower * Submodel.fVisible * std::min(Submodel.f4Emision.a, 1.f))};
|
||||
if (max_component(color) <= LIGHT_THRESHOLD)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
light lightpoint;
|
||||
lightpoint.position = glm::dvec3{Placement * glm::dvec4{glm::dvec3{Transform[3]}, 1.0}};
|
||||
auto const axis{glm::vec3{glm::dmat3{Placement} * glm::dvec3{-glm::vec3{Transform[2]}}}};
|
||||
lightpoint.direction = (glm::length2(axis) > 0.f ? glm::normalize(axis) : glm::vec3{0.f, -1.f, 0.f});
|
||||
lightpoint.color = color;
|
||||
// the cone of the glare: where the point is seen from is where it shines. the glare goes from no brightness at the falloff
|
||||
// angle to full at the hotspot angle, which some models have the wrong way round
|
||||
lightpoint.cos_outer = std::clamp(Submodel.fCosFalloffAngle, -1.f, 0.999f);
|
||||
lightpoint.cos_inner = std::clamp(std::max(Submodel.fCosHotspotAngle, lightpoint.cos_outer + 0.05f), lightpoint.cos_outer + 0.001f, 1.f);
|
||||
lightpoint.range = light_range(color);
|
||||
if (m_walkowner != nullptr)
|
||||
{
|
||||
lightpoint.owner = m_walkowner;
|
||||
lightpoint.part = &Submodel;
|
||||
}
|
||||
m_lights.emplace_back(lightpoint);
|
||||
}
|
||||
|
||||
bool opengl46_local_lights::has_light_points(TModel3d *Model)
|
||||
{
|
||||
auto lookup{m_modellights.find(Model)};
|
||||
if (lookup != m_modellights.end())
|
||||
{
|
||||
return lookup->second;
|
||||
}
|
||||
auto found{false};
|
||||
std::vector<TSubModel const *> pending{Model->Root};
|
||||
while ((false == found) && (false == pending.empty()))
|
||||
{
|
||||
auto const *submodel{pending.back()};
|
||||
pending.pop_back();
|
||||
for (; submodel != nullptr; submodel = submodel->Next)
|
||||
{
|
||||
if (submodel->eType == TP_FREESPOTLIGHT)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (submodel->Child != nullptr)
|
||||
{
|
||||
pending.emplace_back(submodel->Child);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_modellights.emplace(Model, found);
|
||||
return found;
|
||||
}
|
||||
|
||||
opengl46_local_lights::cell_data const &opengl46_local_lights::cell_instances(scene::basic_cell &Cell, bool const Rebuild)
|
||||
{
|
||||
// light points are drawn in the translucent phase, so the instances which have them are on the translucent list of the cell
|
||||
auto &instances{Cell.m_instancetranslucent};
|
||||
auto &data{m_cells[&Cell]};
|
||||
if (Rebuild || (data.source_size != instances.size()) || (data.source_data != static_cast<void const *>(instances.data())))
|
||||
{
|
||||
data.instances.clear();
|
||||
for (auto *instance : instances)
|
||||
{
|
||||
if ((instance->pModel != nullptr) && has_light_points(instance->pModel))
|
||||
{
|
||||
data.instances.emplace_back(instance);
|
||||
}
|
||||
}
|
||||
data.source_size = instances.size();
|
||||
data.source_data = instances.data();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
opengl46_local_lights::section_data const &opengl46_local_lights::section(TModel3d *Model, TSubModel *Section, gfx::geometrybank_manager const &Geometry)
|
||||
{
|
||||
auto lookup{m_sections.find(Section)};
|
||||
if (lookup != m_sections.end())
|
||||
{
|
||||
return lookup->second;
|
||||
}
|
||||
section_data data;
|
||||
glm::mat4 transform{1.f};
|
||||
if (find_parent_transform(Model->Root, Section, glm::mat4{1.f}, transform))
|
||||
{
|
||||
glm::vec3 min{std::numeric_limits<float>::max()};
|
||||
glm::vec3 max{std::numeric_limits<float>::lowest()};
|
||||
extend_bounds(Section, transform, Geometry, false, min, max, data.has_light_points);
|
||||
if (min.x <= max.x)
|
||||
{
|
||||
data.center = 0.5f * (min + max);
|
||||
data.extent = 0.5f * (max - min);
|
||||
}
|
||||
}
|
||||
return m_sections.emplace(Section, data).first->second;
|
||||
}
|
||||
|
||||
opengl46_local_lights::section_data const &opengl46_local_lights::model_bounds(TModel3d *Model, gfx::geometrybank_manager const &Geometry)
|
||||
{
|
||||
auto lookup{m_models.find(Model)};
|
||||
if (lookup != m_models.end())
|
||||
{
|
||||
return lookup->second;
|
||||
}
|
||||
section_data data;
|
||||
glm::vec3 min{std::numeric_limits<float>::max()};
|
||||
glm::vec3 max{std::numeric_limits<float>::lowest()};
|
||||
extend_bounds(Model->Root, glm::mat4{1.f}, Geometry, true, min, max, data.has_light_points);
|
||||
if (min.x <= max.x)
|
||||
{
|
||||
data.center = 0.5f * (min + max);
|
||||
data.extent = 0.5f * (max - min);
|
||||
}
|
||||
return m_models.emplace(Model, data).first->second;
|
||||
}
|
||||
|
||||
std::string opengl46_local_lights::info() const
|
||||
{
|
||||
return "local lights: " + std::to_string(m_lights.size()) + " (scenery " + std::to_string(m_scenerycount) + ", vehicles " + std::to_string(m_vehiclecount) + ", compartments " +
|
||||
std::to_string(m_compartmentcount) + ")" + (m_cablampon ? " + cab lamp" : "") + ", placed " + std::to_string(m_placed) + ", over capacity " + std::to_string(m_dropped) +
|
||||
", gather " + to_string(m_gathertime, 2) + " ms";
|
||||
}
|
||||
|
||||
void opengl46_local_lights::draw_debug_ui()
|
||||
{
|
||||
ImGui::TextUnformatted("local lights (need clustered lights)");
|
||||
ImGui::Checkbox("light points of scenery models", &Global.gfx_lights_scenery);
|
||||
ImGui::Checkbox("light points and compartments of vehicles", &Global.gfx_lights_vehicles);
|
||||
ImGui::Checkbox("cab lamp as a light", &Global.gfx_lights_cab);
|
||||
ImGui::SliderFloat("light point brightness", &Global.gfx_lights_freespot_intensity, 0.f, 50.f);
|
||||
ImGui::SliderFloat("compartment brightness", &Global.gfx_lights_compartment_intensity, 0.f, 10.f);
|
||||
ImGui::SliderFloat("cab lamp brightness", &Global.gfx_lights_cab_intensity, 0.f, 10.f);
|
||||
ImGui::SliderFloat("cab ambient kept", &Global.gfx_lights_cab_bounce, 0.f, 1.f);
|
||||
ImGui::SliderFloat("range, m", &Global.gfx_lights_range, 0.f, 1000.f);
|
||||
ImGui::Text("gathered %u: scenery %u, vehicles %u, compartments %u%s", static_cast<unsigned>(m_lights.size()), m_scenerycount, m_vehiclecount, m_compartmentcount,
|
||||
m_cablampon ? ", cab lamp" : "");
|
||||
ImGui::Text("in the light buffer %u, over capacity %u", m_placed, m_dropped);
|
||||
ImGui::Text("gather %.2f ms (cpu)", m_gathertime);
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace scene
|
||||
{
|
||||
class basic_region;
|
||||
class basic_cell;
|
||||
} // namespace scene
|
||||
namespace gfx
|
||||
{
|
||||
class geometrybank_manager;
|
||||
}
|
||||
class TModel3d;
|
||||
class TSubModel;
|
||||
class TAnimModel;
|
||||
class TDynamicObject;
|
||||
|
||||
// lights of the opengl 4.6 renderer other than the sun and the vehicle headlights, which go to the light clusters:
|
||||
// - the light points (freespot lights) of scenery models and of vehicles, taken the way the experimental renderer takes them:
|
||||
// a point which is visible, has self-illumination (emission) and is past its self-illumination threshold shines as a spot
|
||||
// light along the -z axis of its submodel, with the cone of its glare. lamps of scenery models switched with Light_On / Light_Off
|
||||
// and the self-illumination of vehicle compartments (TDynamicObject::Sections) work through the same state
|
||||
// - the lit compartments of vehicles (low poly interior sections) without light points of their own: a point light in the middle
|
||||
// - the lamp of the occupied cab: a point light under the top of the cab model, in place of the ambient light it used to add
|
||||
// gathered once per frame around the camera, written to the light buffer by opengl46_renderer::Update_Lights()
|
||||
class opengl46_local_lights
|
||||
{
|
||||
public:
|
||||
struct light
|
||||
{
|
||||
glm::dvec3 position; // world space
|
||||
glm::vec3 direction{0.f, -1.f, 0.f}; // world space, unit; axis of the cone
|
||||
glm::vec3 color; // linear, the brightness included
|
||||
float cos_outer{-1.f}; // cosine of the half angle of the cone; not above 0: the light is not limited to a cone
|
||||
float cos_inner{-1.f}; // cosine of the half angle within which the light is at its full brightness; above cos_outer
|
||||
float range{0.f}; // distance at which the light ends, metres
|
||||
// light point of a scenery model: the instance and the submodel it comes from, which name the lamp from frame to frame
|
||||
// (opengl46_local_shadows keeps the shadow maps of scenery lamps by them). nullptr for the other lights
|
||||
void const *owner{nullptr};
|
||||
void const *part{nullptr};
|
||||
};
|
||||
|
||||
opengl46_local_lights() = default;
|
||||
opengl46_local_lights(const opengl46_local_lights &) = delete;
|
||||
opengl46_local_lights &operator=(const opengl46_local_lights &) = delete;
|
||||
|
||||
// collects the lights within Global.gfx_lights_range of Camera, for the frame about to be drawn. it sets the state of the
|
||||
// shared models of the instances and vehicles it goes through, as the render does before it draws each of them.
|
||||
// Geometry: the geometry the models were loaded to, for the bounds of compartments and cabs; Framestamp: the number of the frame
|
||||
void gather(glm::dvec3 const &Camera, scene::basic_region &Region, gfx::geometrybank_manager const &Geometry, unsigned int const Framestamp);
|
||||
// forgets the lights and everything kept about the scene, which is about to be replaced (a scenery being loaded)
|
||||
void reset();
|
||||
// the lights of models and vehicles, the most important (closest and brightest) first
|
||||
std::vector<light> const &lights() const
|
||||
{
|
||||
return m_lights;
|
||||
}
|
||||
// the lamp of the occupied cab, when it is on and the cab is drawn; nullptr otherwise
|
||||
light const *cab_lamp() const
|
||||
{
|
||||
return m_cablampon ? &m_cablamp : nullptr;
|
||||
}
|
||||
// the vehicle of the cab lamp and the position of the lamp in the space of its cab model (mdKabina), when the lamp is on
|
||||
TDynamicObject const *cab_lamp_vehicle() const
|
||||
{
|
||||
return m_cablampon ? m_cablampvehicle : nullptr;
|
||||
}
|
||||
glm::vec3 const &cab_lamp_local() const
|
||||
{
|
||||
return m_cablamplocal;
|
||||
}
|
||||
// counts of the last pass which wrote the lights to the light buffer: placed, and left out for the lack of room
|
||||
void placed(std::uint32_t const Placed, std::uint32_t const Dropped)
|
||||
{
|
||||
m_placed = Placed;
|
||||
m_dropped = Dropped;
|
||||
}
|
||||
// line for the statistics of the debug panel
|
||||
std::string info() const;
|
||||
// controls and counters for the debug window
|
||||
void draw_debug_ui();
|
||||
|
||||
private:
|
||||
struct section_data
|
||||
{
|
||||
glm::vec3 center{0.f}; // middle of the bounds, model space
|
||||
glm::vec3 extent{0.f}; // half the size of the bounds
|
||||
bool has_light_points{false}; // the section holds light points, which light it in place of the one made for it
|
||||
};
|
||||
struct cell_data
|
||||
{
|
||||
std::vector<TAnimModel *> instances; // instances of models with light points
|
||||
// what the list was made from: when the instance list of the cell changes, the list is made anew
|
||||
std::size_t source_size{0};
|
||||
void const *source_data{nullptr};
|
||||
};
|
||||
|
||||
// lights of the light points of the submodel tree, gone through the way the render goes through it. Transform: model space of
|
||||
// Submodel's parent, Placement: model space to world space, Brightness: of a light point of white colour
|
||||
void walk(TSubModel *Submodel, glm::mat4 const &Transform, glm::dmat4 const &Placement, float const Brightness);
|
||||
void add_light_point(TSubModel const &Submodel, glm::mat4 const &Transform, glm::dmat4 const &Placement, float const Brightness);
|
||||
void gather_instance(TAnimModel *Instance, glm::dvec3 const &Camera, float const Brightness, unsigned int const Framestamp);
|
||||
void gather_vehicle(TDynamicObject *Vehicle, glm::dvec3 const &Camera, gfx::geometrybank_manager const &Geometry, float const Freespotbrightness, float const Compartmentbrightness);
|
||||
void gather_cab_lamp(gfx::geometrybank_manager const &Geometry);
|
||||
bool has_light_points(TModel3d *Model);
|
||||
cell_data const &cell_instances(scene::basic_cell &Cell, bool const Rebuild);
|
||||
section_data const §ion(TModel3d *Model, TSubModel *Section, gfx::geometrybank_manager const &Geometry);
|
||||
section_data const &model_bounds(TModel3d *Model, gfx::geometrybank_manager const &Geometry);
|
||||
|
||||
std::vector<light> m_lights;
|
||||
light m_cablamp;
|
||||
bool m_cablampon{false};
|
||||
TDynamicObject const *m_cablampvehicle{nullptr};
|
||||
glm::vec3 m_cablamplocal{0.f};
|
||||
std::unordered_map<TModel3d const *, bool> m_modellights; // whether the model has light points
|
||||
std::unordered_map<scene::basic_cell const *, cell_data> m_cells;
|
||||
std::unordered_map<TSubModel const *, section_data> m_sections; // compartments, by their submodel
|
||||
std::unordered_map<TModel3d const *, section_data> m_models; // bounds of cab models
|
||||
TAnimModel const *m_walkowner{nullptr}; // scenery instance whose light points walk() goes through; nullptr for vehicles
|
||||
bool m_editorseen{false}; // the editor was on in the last frame: the instance lists of the cells may have changed
|
||||
// statistics
|
||||
std::uint32_t m_scenerycount{0};
|
||||
std::uint32_t m_vehiclecount{0};
|
||||
std::uint32_t m_compartmentcount{0};
|
||||
std::uint32_t m_placed{0};
|
||||
std::uint32_t m_dropped{0};
|
||||
double m_gathertime{0.0}; // milliseconds, smoothed
|
||||
};
|
||||
@@ -350,7 +350,7 @@ glm::vec4 opengl46_local_shadows::weights() const
|
||||
return glm::vec4{static_cast<float>(CAB_SLOT), SHADOW_NEAR, 2.f / static_cast<float>(std::max(m_size, 1)), setup(m_level).radius};
|
||||
}
|
||||
|
||||
void opengl46_local_shadows::update_lamps(std::vector<opengl46_local_lights::light> const &Lights, glm::dvec3 const &Camera, std::uint64_t const Version, unsigned int const Framestamp)
|
||||
void opengl46_local_shadows::update_lamps(std::vector<gfx::local_lights::light> const &Lights, glm::dvec3 const &Camera, std::uint64_t const Version, unsigned int const Framestamp)
|
||||
{
|
||||
m_frame = Framestamp;
|
||||
m_wanted.clear();
|
||||
@@ -368,7 +368,7 @@ void opengl46_local_shadows::update_lamps(std::vector<opengl46_local_lights::lig
|
||||
m_sceneryleft = setup(level).sceneryperframe;
|
||||
auto const reach{static_cast<double>(Global.gfx_lights_shadows_lamps_range)};
|
||||
// the lights come the most important first: the first ones within reach get the slots
|
||||
std::vector<opengl46_local_lights::light const *> picked;
|
||||
std::vector<gfx::local_lights::light const *> picked;
|
||||
for (auto const &light : Lights)
|
||||
{
|
||||
if (picked.size() >= m_slots.size())
|
||||
@@ -382,7 +382,7 @@ void opengl46_local_shadows::update_lamps(std::vector<opengl46_local_lights::lig
|
||||
picked.emplace_back(&light);
|
||||
}
|
||||
// lamps which already have a slot keep it
|
||||
std::vector<opengl46_local_lights::light const *> unplaced;
|
||||
std::vector<gfx::local_lights::light const *> unplaced;
|
||||
for (auto const *light : picked)
|
||||
{
|
||||
auto placed{false};
|
||||
@@ -520,7 +520,7 @@ void opengl46_local_shadows::lamps_timer_end()
|
||||
m_lampscputime = (m_lampscputime == 0.0 ? elapsed : m_lampscputime * 0.9 + elapsed * 0.1);
|
||||
}
|
||||
|
||||
opengl46_local_shadows::lamp_slot const *opengl46_local_shadows::lamp(opengl46_local_lights::light const &Light, std::size_t &Slot) const
|
||||
opengl46_local_shadows::lamp_slot const *opengl46_local_shadows::lamp(gfx::local_lights::light const &Light, std::size_t &Slot) const
|
||||
{
|
||||
if (Light.owner == nullptr)
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include "gl/shader.h"
|
||||
#include "gl/query.h"
|
||||
#include "rendering/opengl46/opengl46locallights.h"
|
||||
#include "rendering/common/locallights.h"
|
||||
#include "rendering/lightarray.h"
|
||||
|
||||
class TDynamicObject;
|
||||
@@ -98,7 +98,7 @@ class opengl46_local_shadows
|
||||
// a lamp with a slot
|
||||
struct lamp_slot
|
||||
{
|
||||
void const *owner{nullptr}; // the light it is, see opengl46_local_lights::light
|
||||
void const *owner{nullptr}; // the light it is, see gfx::local_lights::light
|
||||
void const *part{nullptr};
|
||||
glm::dvec3 position{0.0}; // world space
|
||||
float range{0.f}; // of the light, metres
|
||||
@@ -111,7 +111,7 @@ class opengl46_local_shadows
|
||||
// picks the lamps which get shadows in this frame from the gathered lights (the most important first): the scenery lamps
|
||||
// within reach of Camera. a lamp keeps its slot while it is picked; one which got a slot or moved, and all of them when the
|
||||
// scene changes (Version), need their scenery drawn. (re)makes the maps when their count or size changed
|
||||
void update_lamps(std::vector<opengl46_local_lights::light> const &Lights, glm::dvec3 const &Camera, std::uint64_t const Version, unsigned int const Framestamp);
|
||||
void update_lamps(std::vector<gfx::local_lights::light> const &Lights, glm::dvec3 const &Camera, std::uint64_t const Version, unsigned int const Framestamp);
|
||||
// forgets the lamps: the scene they come from is about to be replaced (a scenery being loaded)
|
||||
void reset_lamps();
|
||||
// slots of the lamps picked in this frame
|
||||
@@ -141,7 +141,7 @@ class opengl46_local_shadows
|
||||
void lamps_timer_begin();
|
||||
void lamps_timer_end();
|
||||
// the slot of the lamp the light is, if it has its shadows ready, nullptr otherwise
|
||||
lamp_slot const *lamp(opengl46_local_lights::light const &Light, std::size_t &Slot) const;
|
||||
lamp_slot const *lamp(gfx::local_lights::light const &Light, std::size_t &Slot) const;
|
||||
// the headlight_weights of the light of the lamp in the slot, see weights()
|
||||
glm::vec4 lamp_weights(std::size_t const Slot) const;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -40,14 +40,15 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "rendering/opengl46/opengl46weather.h"
|
||||
#include "rendering/opengl46/opengl46exposure.h"
|
||||
#include "rendering/opengl46/opengl46ao.h"
|
||||
#include "rendering/opengl46/opengl46locallights.h"
|
||||
#include "rendering/common/locallights.h"
|
||||
#include "rendering/opengl46/opengl46localshadows.h"
|
||||
#include "rendering/opengl46/opengl46instancecull.h"
|
||||
#include "rendering/opengl46/opengl46hiz.h"
|
||||
#include "rendering/opengl46/opengl46shadowfit.h"
|
||||
#include "rendering/common/scenewalk.h"
|
||||
|
||||
// bare-bones render controller, in lack of anything better yet
|
||||
class opengl46_renderer : public gfx_renderer {
|
||||
class opengl46_renderer : public gfx_renderer, public gfx::scene_walk {
|
||||
public:
|
||||
// constructors
|
||||
opengl46_renderer() = default;
|
||||
@@ -189,64 +190,42 @@ class opengl46_renderer : public gfx_renderer {
|
||||
static gfx_backend_desc backend_desc();
|
||||
|
||||
private:
|
||||
using gfx::scene_walk::Render;
|
||||
using gfx::scene_walk::Render_Alpha;
|
||||
// the draws of the shared walk of the scene
|
||||
void walk_material(material_handle const Material, TSubModel const *Submodel = nullptr, lighting_data const *Lighting = nullptr) override;
|
||||
void walk_shadow_material(material_handle const Material) override;
|
||||
opengl_material const &walk_material_data(material_handle const Material) const override;
|
||||
opengl_material const &walk_material_data(TSubModel const *Submodel) const override;
|
||||
void walk_program(walk_shader const Program) override;
|
||||
void walk_texture(std::size_t const Unit, texture_handle const Texture) override;
|
||||
void walk_blending(bool const Alpha) override;
|
||||
void walk_sunlight(float const Factor = 1.f) override;
|
||||
void walk_environment(TEnvironmentType const Environment = e_flat) override;
|
||||
void walk_ambient_save() override;
|
||||
void walk_ambient_add(glm::vec3 const &Addition) override;
|
||||
void walk_ambient_restore() override;
|
||||
glm::vec4 walk_sun_ambient() const override;
|
||||
void walk_draw(gfx::geometry_handle const &Geometry) override;
|
||||
void walk_draw(std::vector<gfx::geometrybank_handle>::iterator First, std::vector<gfx::geometrybank_handle>::iterator Last) override;
|
||||
std::uint32_t walk_instances(glm::mat4 const *Modelviews, std::size_t const Count) override;
|
||||
void walk_cull_face(bool const Enabled) override;
|
||||
void walk_glare(bool const Enabled) override;
|
||||
void walk_glare_draw(TSubModel *Submodel, gfx::geometry_handle const &Geometry) override;
|
||||
void walk_light_query(TSubModel *Submodel, bool const Begin) override;
|
||||
// the gpu culling of the instances, models and sleepers, the depth pass of the whole scene
|
||||
void walk_region_begin() override;
|
||||
void walk_editor_cursor() override;
|
||||
bool walk_bucket(scene::basic_cell::instance_bucket_key const &Key, scene::basic_cell::instance_bucket const &Bucket, bool const Bucketrange, scene::bounding_area const &Area) override;
|
||||
void walk_bucket_culled(scene::basic_cell::instance_bucket const &Bucket) override;
|
||||
bool walk_queue_model(TAnimModel *Instance, bool const Inrange) override;
|
||||
bool walk_queue_sleepers(TTrack *Track) override;
|
||||
void walk_sleepers_drawn(TTrack const &Track, std::vector<float> const &Lodbounds, std::vector<std::pair<float, glm::mat4>> const &Drawn) override;
|
||||
bool walk_instances_begin() override;
|
||||
void walk_instances_compare(scene::basic_cell::instance_bucket_key const &Key, std::vector<std::uint32_t> const &Bandcounts) override;
|
||||
void walk_instances_end() override;
|
||||
|
||||
// types
|
||||
enum class rendermode
|
||||
{
|
||||
none,
|
||||
color,
|
||||
shadows,
|
||||
reflections,
|
||||
pickcontrols,
|
||||
pickscenery
|
||||
};
|
||||
|
||||
struct debug_stats
|
||||
{
|
||||
int dynamics{0};
|
||||
int models{0};
|
||||
int submodels{0};
|
||||
int paths{0};
|
||||
int traction{0};
|
||||
int shapes{0};
|
||||
int lines{0};
|
||||
int particles{0};
|
||||
int drawcalls{0};
|
||||
int instanced_drawcalls{0}; // drawcalls issued via instanced render path (one per TModel3d batch)
|
||||
int instances{0}; // total instances drawn via instanced render path
|
||||
int triangles{0};
|
||||
|
||||
debug_stats& operator+=( const debug_stats& Right ) {
|
||||
dynamics += Right.dynamics;
|
||||
models += Right.models;
|
||||
submodels += Right.submodels;
|
||||
paths += Right.paths;
|
||||
traction += Right.traction;
|
||||
shapes += Right.shapes;
|
||||
lines += Right.lines;
|
||||
particles += Right.particles;
|
||||
drawcalls += Right.drawcalls;
|
||||
instanced_drawcalls += Right.instanced_drawcalls;
|
||||
instances += Right.instances;
|
||||
return *this; }
|
||||
};
|
||||
|
||||
using section_sequence = std::vector<scene::basic_section *>;
|
||||
using distancecell_pair = std::pair<double, scene::basic_cell *>;
|
||||
using cell_sequence = std::vector<distancecell_pair>;
|
||||
|
||||
struct renderpass_config
|
||||
{
|
||||
opengl_camera pass_camera;
|
||||
opengl_camera viewport_camera;
|
||||
rendermode draw_mode{rendermode::none};
|
||||
float draw_range{0.0f};
|
||||
// depth range of the projection of a color pass; the far plane is only the end of the range which is drawn,
|
||||
// as the projection matrix itself has none
|
||||
float znear{0.0f};
|
||||
float zfar{0.0f};
|
||||
debug_stats draw_stats;
|
||||
};
|
||||
|
||||
struct viewport_config {
|
||||
int width;
|
||||
int height;
|
||||
@@ -349,33 +328,6 @@ class opengl46_renderer : public gfx_renderer {
|
||||
// Position in world space, Rotation from world axes to its axes; Near is the distance of the near plane from the projector
|
||||
void Headlight_Projector(light_array::light_record const &Light, glm::dvec3 &Position, glm::mat3 &Rotation, glm::mat4 &Projection, float &Near, float &Fieldofview);
|
||||
bool Render(world_environment *Environment);
|
||||
void Render(scene::basic_region *Region);
|
||||
void Render(section_sequence::iterator First, section_sequence::iterator Last);
|
||||
void Render(cell_sequence::iterator First, cell_sequence::iterator Last);
|
||||
void Render(scene::shape_node const &Shape, bool const Ignorerange);
|
||||
void Render(TAnimModel *Instance);
|
||||
// an instance of a bucket of a scene cell, queued to be drawn by Render_Instanced()
|
||||
struct queued_instance {
|
||||
scene::basic_cell::instance_bucket const *bucket;
|
||||
std::size_t index; // of the instance and its record in the bucket
|
||||
};
|
||||
// batched render path for many TAnimModel instances that share the same TModel3d.
|
||||
// Caller guarantees every instance was instanceable when bucketed (no per-instance lights,
|
||||
// no submodel animation, same replacable skins); the records of the instances tell which
|
||||
// ones still are. Instances are individually frustum/distance culled by their records, the
|
||||
// TAnimModel itself is read only for the first instance drawn. Each lod band drawn counts as
|
||||
// one instanced_drawcall in draw_stats; the instances drawn are added to the instances counter.
|
||||
// Bandcounts, when given, gets the number of instances drawn in each lod band.
|
||||
void Render_Instanced( TModel3d *Model, std::vector<queued_instance> const &Instances, std::vector<std::uint32_t> *Bandcounts = nullptr );
|
||||
// adds the bucketed instances of a scene cell to the frame-level buckets drawn by Render_Instanced(),
|
||||
// less the ones their records show to be out of sight in the current render pass
|
||||
void Queue_Instances( scene::basic_cell const *Cell );
|
||||
// makes the records of the instances held by a bucket of a scene cell, with locations relative to Origin
|
||||
void Update_Instance_Records( TModel3d const *Model, scene::basic_cell::instance_bucket const &Bucket, glm::dvec3 const &Origin );
|
||||
// whether none of the instances of a bucket can pass the culling of the current pass, by the bounds of their records
|
||||
bool Bucket_Culled( scene::basic_cell::instance_bucket const &Bucket ) const;
|
||||
// the sphere around the locations of the visible instances of a bucket, with a margin for the precision of the records
|
||||
static scene::bounding_area Bucket_Area( scene::basic_cell::instance_bucket const &Bucket );
|
||||
// places supplied modelview matrices in the instance buffer of the frame. returns: base instance of the first of them
|
||||
std::uint32_t Upload_Instance_Modelviews( glm::mat4 const *Modelviews, std::size_t const Count );
|
||||
// binds the instance buffer of the frame for the vertex shaders (INSTANCE_SSBO_BINDING)
|
||||
@@ -479,39 +431,20 @@ class opengl46_renderer : public gfx_renderer {
|
||||
std::array<std::optional<std::array<glm::vec3, 8>>, gl::MAX_CASCADES> &Areas );
|
||||
// whether the depth pass of the whole scene draws the material bound last (Bind_Material()) and the geometry
|
||||
bool Scene_Depth_Draws( gfx::geometry_handle const &Geometry ) const;
|
||||
bool Render(TDynamicObject *Dynamic);
|
||||
bool Render(TModel3d *Model, material_data const *Material, float const Squaredistance, glm::dvec3 const &Position, glm::vec3 const &Angle);
|
||||
bool Render(TModel3d *Model, material_data const *Material, float const Squaredistance);
|
||||
void Render(TSubModel *Submodel);
|
||||
void Render(TTrack *Track);
|
||||
void Render(scene::basic_cell::path_sequence::const_iterator First, scene::basic_cell::path_sequence::const_iterator Last);
|
||||
// renders the per-track sleeper instances (TTrack::m_sleeper_local_transforms) via GPU instancing.
|
||||
// caller must already have the camera-relative world-space transform set on the matrix stack.
|
||||
// no-op if the track has no sleepermodel, Global.SleeperDistance is 0, or the camera is beyond
|
||||
// Global.SleeperDistance from the track origin.
|
||||
void Render_Sleepers( TTrack *Track );
|
||||
// queues the sleepers of a track in the gpu culling of the pass (Global.gfx_gpucull_sleepers), with the gates of
|
||||
// Render_Sleepers(), making their records first when they're missing or out of date
|
||||
void Queue_Sleepers( TTrack *Track );
|
||||
// renders the sleepers and switch timbers of the tracks in range (TTrack::m_sleepers3d), laying them first where they're out of date.
|
||||
// the parts of the model are drawn in instanced batches for the whole run of tracks, the runs of beams out of sight left out
|
||||
void Render_Timbers( scene::basic_cell::path_sequence::const_iterator First, scene::basic_cell::path_sequence::const_iterator Last );
|
||||
// queues a model instance drawn on its own in the gpu culling of the pass (Global.gfx_gpucull_models), as a bucket of
|
||||
// its own, making its record first when it's missing or out of date. Inrange: the cell of the instance lies within the
|
||||
// range of the pass. returns true when the instance isn't drawn by the cpu in the pass: it's drawn from the result of the
|
||||
// culling, or it was culled and recorded by the depth pass of the whole scene, or it lies out of range
|
||||
bool Queue_Model( TAnimModel *Instance, bool const Inrange );
|
||||
bool Render_cab(TDynamicObject const *Dynamic, float const Lightlevel, bool const Alpha = false);
|
||||
bool Render_interior( bool const Alpha = false );
|
||||
bool Render_lowpoly( TDynamicObject *Dynamic, float const Squaredistance, bool const Setup, bool const Alpha = false );
|
||||
bool Render_coupler_adapter( TDynamicObject *Dynamic, float const Squaredistance, int const End, bool const Alpha = false );
|
||||
// releases gl textures used only by vehicles farther than gfx.textures.releasedistance times the draw range
|
||||
void Update_Texture_Release();
|
||||
// textures used by materials of the sub-models of specified model, replaceable skins excluded
|
||||
std::vector<texture_handle> const &Model_Textures( TModel3d *Model );
|
||||
// has the data of the textures read ahead of the camera, with gfx.textures.streaming
|
||||
void Update_Texture_Prefetch();
|
||||
void Render(TMemCell *Memcell);
|
||||
void Render_particles();
|
||||
void Render_precipitation();
|
||||
// the view of the colour pass being drawn, for the particles of the gpu
|
||||
@@ -519,24 +452,14 @@ class opengl46_renderer : public gfx_renderer {
|
||||
// the grass of the heightmap terrain around the camera of the main view; Future: the matrix of the motion vectors
|
||||
void Render_grass(glm::mat4 const &Future);
|
||||
void Render_vr_models();
|
||||
void Render_Alpha(scene::basic_region *Region);
|
||||
void Render_Alpha(cell_sequence::reverse_iterator First, cell_sequence::reverse_iterator Last);
|
||||
void Render_Alpha(TAnimModel *Instance);
|
||||
void Render_Alpha(TTraction *Traction);
|
||||
void Render_Alpha(scene::lines_node const &Lines);
|
||||
void Render_editor_overlay();
|
||||
bool Render_Alpha(TDynamicObject *Dynamic);
|
||||
bool Render_Alpha(TModel3d *Model, material_data const *Material, float const Squaredistance, glm::dvec3 const &Position, glm::vec3 const &Angle);
|
||||
bool Render_Alpha(TModel3d *Model, material_data const *Material, float const Squaredistance);
|
||||
void Render_Alpha(TSubModel *Submodel);
|
||||
void Render_Alpha_text(TSubModel *Submodel); // characters of a text display
|
||||
// writes the lighting of the pass about to be drawn to the light buffer: the sun, the ambient light and the fog, and the headlights of Lights
|
||||
// unless Headlights is false, as in the pass which draws the environment map, where lights of vehicles would only get baked into the map.
|
||||
// Clustered tells the shading to take the headlights from the lists of the clusters (see Cull_Lights()) and not to go through the first few.
|
||||
// with Headlights and Clustered the cab lamp (first) and the lamps of models and vehicles (after the headlights) are written as well
|
||||
void Update_Lights(light_array &Lights, bool const Headlights = true, bool const Clustered = false);
|
||||
// writes a light of a model or a vehicle, or the cab lamp, to the slot of the light buffer, in the view space of the pass
|
||||
void Write_Local_Light(gl::light_element &Light, opengl46_local_lights::light const &Source, glm::dvec3 const &Camera, glm::mat4 const &View);
|
||||
void Write_Local_Light(gl::light_element &Light, gfx::local_lights::light const &Source, glm::dvec3 const &Camera, glm::mat4 const &View);
|
||||
// brings the light cluster grid of the viewport up to date with the projection of the color pass being drawn.
|
||||
// Targetsize is the size in pixels of what the pass draws to, which gl_FragCoord of the shading counts in
|
||||
void Update_Clusters(viewport_config &vp, glm::ivec2 const &Targetsize);
|
||||
@@ -547,8 +470,6 @@ class opengl46_renderer : public gfx_renderer {
|
||||
void Render_cluster_heatmap(viewport_config &vp, gl::framebuffer *Target, glm::ivec2 const &Targetsize);
|
||||
// ambient occlusion from the resolved depth (vp.main_texd), multiplied into the image of Target. Mode: Global.gfx_postfx_ao
|
||||
void Render_ambient_occlusion(viewport_config &vp, gl::framebuffer &Target, int const Mode);
|
||||
glm::vec3 pick_color(std::size_t const Index);
|
||||
std::size_t pick_index(glm::ivec3 const &Color);
|
||||
|
||||
bool init_viewport(viewport_config &vp);
|
||||
|
||||
@@ -590,8 +511,6 @@ class opengl46_renderer : public gfx_renderer {
|
||||
/*
|
||||
float m_sunandviewangle; // cached dot product of sunlight and camera vectors
|
||||
*/
|
||||
gfx::geometry_handle m_billboardgeometry{0, 0};
|
||||
texture_handle m_glaretexture{-1};
|
||||
texture_handle m_suntexture{-1};
|
||||
texture_handle m_moontexture{-1};
|
||||
texture_handle m_smoketexture{-1};
|
||||
@@ -612,8 +531,6 @@ class opengl46_renderer : public gfx_renderer {
|
||||
opengl46_gpu_particles m_gpuparticles; // the smoke, rain and snow of the gpu (gfx.smoke.gpu, gfx.precipitation.gpu)
|
||||
bool m_particlesoft{false}; // the depth for the soft particles is bound in the colour pass being drawn
|
||||
|
||||
unsigned int m_framestamp; // id of currently rendered gfx frame
|
||||
unsigned int m_timbersweepframe { 0 }; // the frame the beams of the tracks far away were last let go in
|
||||
float m_framerate;
|
||||
double m_updateaccumulator{0.0};
|
||||
std::string m_debugtimestext;
|
||||
@@ -638,29 +555,12 @@ class opengl46_renderer : public gfx_renderer {
|
||||
float m_specularopaquescalefactor{1.f};
|
||||
float m_speculartranslucentscalefactor{1.f};
|
||||
|
||||
float m_fogrange = 2000.0f;
|
||||
|
||||
renderpass_config m_renderpass; // parameters for current render pass
|
||||
section_sequence m_sectionqueue; // list of sections in current render pass
|
||||
cell_sequence m_cellqueue;
|
||||
// frame-level accumulation of per-cell opaque instance buckets. Each visited
|
||||
// cell's buckets are merged here keyed by (TModel3d*, skins), so that
|
||||
// Render_Instanced() runs once per unique model across the whole pass instead
|
||||
// of once per cell -- collapsing many tiny instanced draws into a few large
|
||||
// batches. Reused every pass; the vectors are emptied (not freed) at the top of Render(scene::basic_region*).
|
||||
std::unordered_map<scene::basic_cell::instance_bucket_key, std::vector<queued_instance>, scene::basic_cell::instance_bucket_key_hash> m_frame_instance_buckets;
|
||||
// textures used by vehicle models, for the release of far textures
|
||||
std::unordered_map<TModel3d const *, std::vector<texture_handle>> m_modeltextures;
|
||||
// the read ahead of textures (Update_Texture_Prefetch()): the cell it goes on with
|
||||
std::size_t m_prefetchcursor { 0 };
|
||||
std::chrono::steady_clock::time_point m_prefetchvehicletime;
|
||||
std::chrono::steady_clock::time_point m_prefetchdroptime;
|
||||
// geometry of neighbouring tracks of a cell which share the material, gathered to be drawn together. reused by each Render(path_sequence)
|
||||
std::vector<gfx::geometrybank_handle> m_pathbatch;
|
||||
// instance records kept by the buckets of scene cells are good as long as they carry this number.
|
||||
// it changes with each frame drawn with the scenery editor active, as the editor moves and resizes instances in place,
|
||||
// and whenever scene::node_state_version does, as instances are shown, hidden or drawn on their own
|
||||
unsigned int m_instancerecordsversion { 1 };
|
||||
unsigned int m_nodestateversion { 0 }; // scene::node_state_version which m_instancerecordsversion was last checked against
|
||||
// culling of instances in compute passes, run along the first color pass of the main viewport of a frame
|
||||
// when Global.gfx_gpucull is set: the instances of the pass are drawn from its result (1), or the result is compared
|
||||
@@ -683,7 +583,6 @@ class opengl46_renderer : public gfx_renderer {
|
||||
int m_shadowcascade { -1 }; // the region is drawn for this cascade of the sun shadows
|
||||
// Instances_Cast_Shadow() of the groups of the shadow pass under way
|
||||
std::unordered_map<scene::basic_cell::instance_bucket_key, bool, scene::basic_cell::instance_bucket_key_hash> m_shadowcasters;
|
||||
std::vector<std::uint32_t> m_instance_bandcounts; // reported by Render_Instanced() for the comparison
|
||||
// the sleepers of tracks culled on the gpu with the instances of the main view (Global.gfx_gpucull_sleepers): records made
|
||||
// from TTrack::m_sleeper_local_transforms when the track is queued first, and again when they or the instance records
|
||||
// (editor, m_instancerecordsversion) went out of date. the records of tracks not queued for a while are let go
|
||||
@@ -710,10 +609,6 @@ class opengl46_renderer : public gfx_renderer {
|
||||
std::unordered_map<TAnimModel const *, model_records> m_modelrecords;
|
||||
bool m_modelspass { false }; // the models drawn on their own of the pass under way go to its gpu culling
|
||||
bool m_indirectcolourmodels { false }; // with m_indirectcolourpending: the depth pass culled and recorded them too
|
||||
// comparison (Global.gfx_gpucull 2): the key of the model Render( TAnimModel * ) is called for, and the models it drew in
|
||||
// the pass with their scaled squared distances, reported once the culling ran
|
||||
scene::basic_cell::instance_bucket_key const *m_modelcomparekey { nullptr };
|
||||
std::vector<std::pair<scene::basic_cell::instance_bucket_key, float>> m_modelcpudistances;
|
||||
bool m_sleeperspass { false }; // the sleepers of the pass under way go to its gpu culling (the one of the main view)
|
||||
bool m_indirectcoloursleepers { false }; // with m_indirectcolourpending: the depth pass culled and recorded the sleepers too
|
||||
// counts per lod band of the sleepers Render_Sleepers() drew in the pass, by model and skin, for the comparison (they're
|
||||
@@ -786,7 +681,6 @@ class opengl46_renderer : public gfx_renderer {
|
||||
};
|
||||
std::unordered_map<scene::basic_cell::instance_bucket_key, walk_record, scene::basic_cell::instance_bucket_key_hash> m_walkrecords;
|
||||
int m_walkrankcutoff { 0 }; // Global.gfx_shadow_rank_cutoff the walks were made with
|
||||
bool m_indirectchanging { false }; // the walk under way met a submodel which may look different in the next frame
|
||||
// what the walks of the frame did, for the panel: walks made, of them not kept, walks taken from earlier frames, and the cpu time
|
||||
// of the walks and of putting the draws in order with their blocks uploaded
|
||||
struct walk_counts {
|
||||
@@ -949,14 +843,10 @@ class opengl46_renderer : public gfx_renderer {
|
||||
bool m_contactshadowslast { false };
|
||||
// a vehicle drawn in the colour pass of the last frame moved, and one drawn in this frame did (motion blur, Render_pass())
|
||||
bool m_vehiclesmoved { true };
|
||||
bool m_vehiclesmoving { false };
|
||||
gl::gpu_timer m_contactshadowstimer; // resolve of the depth
|
||||
renderpass_config m_colorpass; // parametrs of most recent color pass
|
||||
std::array<renderpass_config, 3> m_shadowpass; // parametrs of most recent shadowmap pass for each of csm stages
|
||||
std::vector<TSubModel const *> m_pickcontrolsitems;
|
||||
std::vector<TSubModel const *> m_picksurfaceitems;
|
||||
TSubModel const *m_pickcontrolitem{nullptr};
|
||||
std::vector<scene::basic_node *> m_picksceneryitems;
|
||||
scene::basic_node *m_picksceneryitem{nullptr};
|
||||
glm::vec3 m_worldmousecoordinates { 0.f };
|
||||
bool m_mousehit { false };
|
||||
@@ -997,21 +887,16 @@ class opengl46_renderer : public gfx_renderer {
|
||||
uint32_t m_lights_lit = 0;
|
||||
uint32_t m_lights_dropped = 0;
|
||||
// lights of models and vehicles and the cab lamp, gathered once per frame for the light clusters
|
||||
opengl46_local_lights m_locallights;
|
||||
// the last Update_Lights() placed the cab lamp in the light buffer: the cab is lit by it, not by an addition to the ambient light
|
||||
bool m_cablamplit = false;
|
||||
gfx::local_lights m_locallights;
|
||||
// the ambient light of the light buffer while the cab is lit (walk_ambient_save())
|
||||
packed_vec3 m_cabambient { 0.f };
|
||||
// cube shadow maps of the lamps
|
||||
opengl46_local_shadows m_localshadows;
|
||||
// the shadow pass draws to a cube shadow map, with the programs of m_localshadows
|
||||
bool m_cubeshadows = false;
|
||||
// the shadow pass draws the kept scenery part of the map of a lamp: no vehicles, and not the model of the lamp itself
|
||||
bool m_cubescenery = false;
|
||||
TAnimModel const *m_cubeskipinstance = nullptr;
|
||||
// vehicles near the camera which cast the shadows of scenery lamps, and those of them by the lamp being drawn
|
||||
std::vector<TDynamicObject *> m_lampshadowvehicles;
|
||||
std::vector<TDynamicObject *> m_lampshadowslot;
|
||||
// the shadow pass leaves this vehicle out (the one the headlights of the map being drawn belong to)
|
||||
TDynamicObject const *m_shadowskipvehicle = nullptr;
|
||||
// headlights which may get shadow maps in this frame, and their importance
|
||||
std::vector<std::pair<float, light_array::light_record const *>> m_headlightshadowcandidates;
|
||||
std::vector<light_array::light_record const *> m_headlightshadowpicked;
|
||||
@@ -1041,44 +926,7 @@ class opengl46_renderer : public gfx_renderer {
|
||||
// matrices of their instances past the ones placed earlier in the frame, and draw them from that base instance.
|
||||
// persistently mapped with Global.gfx_bufferstreaming
|
||||
std::unique_ptr<gl::frame_stream> m_instancestream;
|
||||
// when > 0, the renderer is inside a Render_Instanced() call. The draw(handle)
|
||||
// method routes to glDrawElementsInstanced* with this many instances from
|
||||
// m_current_instance_base onwards rather than a single regular draw. The vertex shader reads
|
||||
// per-instance modelview from instance_ssbo[gl_BaseInstance + gl_InstanceID]. Reset to 0 on exit.
|
||||
std::size_t m_current_instance_count { 0 };
|
||||
std::uint32_t m_current_instance_base { 0 };
|
||||
// camera-space transforms of the sleepers and timbers drawn by Render_Timbers, by the model and its part or its level of
|
||||
// detail; kept from frame to frame to reuse their allocations
|
||||
struct timber_bucket {
|
||||
track_sleepers::beam_model const *model { nullptr };
|
||||
int part { -1 }; // of a model put together from parts; -1: the whole model
|
||||
float band { 0.f }; // the squared distance the level of detail of the whole model is seen from
|
||||
std::vector<glm::mat4> instances;
|
||||
};
|
||||
std::vector<timber_bucket> m_timberbuckets;
|
||||
// persistent scratch buffer for Render_Instanced(): holds the per-instance
|
||||
// camera-space root modelview matrices for the batch currently being built.
|
||||
// Kept as a member rather than a function-local so its heap allocation is
|
||||
// reused across calls -- clear() retains capacity, so once it has grown to
|
||||
// the largest batch seen, steady-state frames perform no allocation here.
|
||||
std::vector<glm::mat4> m_instance_modelviews;
|
||||
// further persistent scratch buffers for Render_Instanced(): culled instances paired with their
|
||||
// squared distance, and the lod bounds of the drawn model which split them into lod bands
|
||||
struct instance_survivor {
|
||||
glm::mat4 modelview;
|
||||
float distancesquared;
|
||||
std::uint32_t band;
|
||||
};
|
||||
std::vector<instance_survivor> m_instance_survivors;
|
||||
std::vector<float> m_instance_lodbounds;
|
||||
std::vector<TSubModel const *> m_instance_lodpending;
|
||||
// per sort key (lod band and distance slot): first index in m_instance_modelviews (with end sentinel)
|
||||
// and fill cursor; per lod band: nearest distance
|
||||
std::vector<std::size_t> m_instance_bandstarts;
|
||||
std::vector<std::size_t> m_instance_bandcursors;
|
||||
std::vector<float> m_instance_banddistances;
|
||||
gl::scene_ubs scene_ubs;
|
||||
gl::model_ubs model_ubs;
|
||||
gl::light_data light_data;
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<gl::program>> m_shaders;
|
||||
@@ -1167,8 +1015,6 @@ class opengl46_renderer : public gfx_renderer {
|
||||
std::unique_ptr<opengl_texture> ldr_tex;
|
||||
std::unique_ptr<gl::framebuffer> ldr_fb;
|
||||
} m_preview;
|
||||
// set while a preview is drawn: submodels faded out entirely are skipped, instead of being drawn invisible over the stencil
|
||||
bool m_previewing { false };
|
||||
|
||||
static bool renderer_register;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user