mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-09 11:22:25 +01:00
Improve rain shader
This commit is contained in:
447
rendering/common/windscreenwater.cpp
Normal file
447
rendering/common/windscreenwater.cpp
Normal file
@@ -0,0 +1,447 @@
|
||||
/*
|
||||
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/common/windscreenwater.h"
|
||||
|
||||
#include "model/Model3d.h"
|
||||
#include "model/material.h"
|
||||
#include "rendering/renderer.h"
|
||||
#include "simulation/simulation.h"
|
||||
#include "simulation/simulationenvironment.h"
|
||||
#include "utilities/Globals.h"
|
||||
#include "utilities/Logs.h"
|
||||
#include "vehicle/DynObj.h"
|
||||
#include "vehicle/Train.h"
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// the param of mat_rain_windscreen the slot of a glass goes to (water_slot: param[2].z) and the one of the size of its drops
|
||||
// (rain_scale: param[2].y)
|
||||
std::size_t constexpr SLOT_PARAM{2};
|
||||
// how fast the rain and the snow fall, m/s (as the particles of the rain and the snow, gfx::gpu_particles)
|
||||
float constexpr RAIN_FALL{7.f};
|
||||
float constexpr SNOW_FALL{1.1f};
|
||||
// the share of the wind the rain drifts with (as the particles of the rain and the snow)
|
||||
double constexpr WIND_SHARE{0.5};
|
||||
// the smallest raindrop landing as a drop of its own, mm: the smaller ones are the mist of the wet glass
|
||||
float constexpr LEAST_DROP{0.5f};
|
||||
// the water a flake of snow brings, mm3 on average (water_spawn.comp: 0.3 .. 2.5)
|
||||
float constexpr FLAKE_WATER{1.4f};
|
||||
// the most landings tried on a glass in a step
|
||||
std::uint32_t constexpr MOST_TRIES{1024};
|
||||
// the longest step, seconds: a longer frame slows the water down rather than let it jump
|
||||
double constexpr LONGEST_STEP{0.05};
|
||||
|
||||
// whether the material is a glass: one of mat_rain_windscreen, with a mask of the wipers
|
||||
bool is_glass(opengl_material const *Material)
|
||||
{
|
||||
return (Material != nullptr) && (Material->shader != nullptr) && (Material->shader->texture_conf.count("wipermask") > 0) && (Material->shader->param_conf.count("water_slot") > 0);
|
||||
}
|
||||
|
||||
// calls Visit for every submodel of the branch, its children and its siblings
|
||||
template <typename Visit> void visit_submodels(TSubModel *Submodel, Visit &&Visitor)
|
||||
{
|
||||
while (Submodel != nullptr)
|
||||
{
|
||||
Visitor(Submodel);
|
||||
visit_submodels(Submodel->ChildGet(), Visitor);
|
||||
Submodel = Submodel->NextGet();
|
||||
}
|
||||
}
|
||||
|
||||
// the vertices of a submodel, wherever they are kept
|
||||
gfx::vertex_array const &submodel_vertices(TSubModel const *Submodel)
|
||||
{
|
||||
return (Submodel->m_geometry.handle != null_handle ? GfxRenderer->Vertices(Submodel->m_geometry.handle) : Submodel->Vertices);
|
||||
}
|
||||
|
||||
// the transformation of a submodel to the space of the model
|
||||
glm::mat4 submodel_transform(TSubModel const *Submodel)
|
||||
{
|
||||
float4x4 matrix;
|
||||
Submodel->ParentMatrix(&matrix);
|
||||
return glm::make_mat4(matrix.readArray());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool windscreen_water::available()
|
||||
{
|
||||
if (m_state == 0)
|
||||
{
|
||||
m_state = (gpu_init() ? 1 : -1);
|
||||
if (m_state < 0)
|
||||
{
|
||||
WriteLog("windscreen water: the simulation can't be used, the windscreens make their water up");
|
||||
}
|
||||
}
|
||||
return m_state > 0;
|
||||
}
|
||||
|
||||
void windscreen_water::release()
|
||||
{
|
||||
assign(false);
|
||||
m_glasses = {};
|
||||
m_glasscount = 0;
|
||||
m_vehicle = nullptr;
|
||||
m_cab = nullptr;
|
||||
}
|
||||
|
||||
void windscreen_water::reset()
|
||||
{
|
||||
release();
|
||||
m_state = 0;
|
||||
}
|
||||
|
||||
void windscreen_water::assign(bool const Assigned)
|
||||
{
|
||||
if (false == bool(m_host.material))
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (std::size_t slot = 0; slot < m_glasscount; ++slot)
|
||||
{
|
||||
auto const &glass{m_glasses[slot]};
|
||||
if (auto *material{m_host.material(glass.material)}; is_glass(material))
|
||||
{
|
||||
material->params[SLOT_PARAM].z = (Assigned && glass.baked ? static_cast<float>(slot + 1) : 0.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void windscreen_water::rasterize(TSubModel *Submodel, material_handle const Material, glm::vec3 const &Inside, std::vector<glm::vec4> &Axes) const
|
||||
{
|
||||
if ((Submodel->m_material != Material) || (Submodel->eType != GL_TRIANGLES))
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto const &vertices{submodel_vertices(Submodel)};
|
||||
if (vertices.size() < 3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto const transform{submodel_transform(Submodel)};
|
||||
gfx::index_array const *indices{nullptr};
|
||||
if ((Submodel->m_geometry.handle != null_handle) && (Submodel->m_geometry.index_count > 0))
|
||||
{
|
||||
indices = &GfxRenderer->Indices(Submodel->m_geometry.handle);
|
||||
}
|
||||
else if (false == Submodel->Indices.empty())
|
||||
{
|
||||
indices = &Submodel->Indices;
|
||||
}
|
||||
auto const count{indices != nullptr ? indices->size() : vertices.size()};
|
||||
auto const size{static_cast<int>(gl::WATER_MAP)};
|
||||
for (std::size_t first = 0; first + 2 < count; first += 3)
|
||||
{
|
||||
std::array<glm::vec3, 3> position;
|
||||
std::array<glm::vec2, 3> uv;
|
||||
bool valid{true};
|
||||
for (std::size_t corner = 0; corner < 3; ++corner)
|
||||
{
|
||||
auto const index{indices != nullptr ? static_cast<std::size_t>((*indices)[first + corner]) : first + corner};
|
||||
if (index >= vertices.size())
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
position[corner] = glm::vec3{transform * glm::vec4{vertices[index].position, 1.f}};
|
||||
uv[corner] = vertices[index].texture;
|
||||
}
|
||||
if (false == valid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// the steps along the glass for the steps of u and v: the edges of the triangle in both spaces
|
||||
glm::mat2 const uvedges{uv[1] - uv[0], uv[2] - uv[0]};
|
||||
auto const det{glm::determinant(uvedges)};
|
||||
if (std::abs(det) < 1e-12f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto const inverse{glm::inverse(uvedges)};
|
||||
auto const e1{position[1] - position[0]};
|
||||
auto const e2{position[2] - position[0]};
|
||||
// [tu tv] = [e1 e2] * inverse
|
||||
auto const tu{e1 * inverse[0][0] + e2 * inverse[0][1]};
|
||||
auto const tv{e1 * inverse[1][0] + e2 * inverse[1][1]};
|
||||
auto const centre{(position[0] + position[1] + position[2]) / 3.f};
|
||||
auto const out{glm::dot(glm::cross(tu, tv), centre - Inside) >= 0.f ? 1.f : -1.f};
|
||||
// a triangle laid on a repeat of the texture is taken to the first one (the mask of the wipers repeats with it)
|
||||
auto const shift{glm::floor((uv[0] + uv[1] + uv[2]) / 3.f)};
|
||||
for (auto &corner : uv)
|
||||
{
|
||||
corner -= shift;
|
||||
}
|
||||
auto const lower{glm::max(glm::ivec2{glm::floor(glm::min(glm::min(uv[0], uv[1]), uv[2]) * static_cast<float>(size))}, glm::ivec2{0})};
|
||||
auto const upper{glm::min(glm::ivec2{glm::ceil(glm::max(glm::max(uv[0], uv[1]), uv[2]) * static_cast<float>(size))}, glm::ivec2{size - 1})};
|
||||
for (int y = lower.y; y <= upper.y; ++y)
|
||||
{
|
||||
for (int x = lower.x; x <= upper.x; ++x)
|
||||
{
|
||||
// the middle of the texel inside the triangle (barycentric)
|
||||
auto const point{(glm::vec2{static_cast<float>(x), static_cast<float>(y)} + 0.5f) / static_cast<float>(size)};
|
||||
auto const weights{inverse * (point - uv[0])};
|
||||
if ((weights.x < -1e-4f) || (weights.y < -1e-4f) || (weights.x + weights.y > 1.f + 1e-4f))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto const texel{static_cast<std::size_t>(y) * gl::WATER_MAP + static_cast<std::size_t>(x)};
|
||||
Axes[texel * 2] = glm::vec4{tu, 1.f};
|
||||
Axes[texel * 2 + 1] = glm::vec4{tv, out};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void windscreen_water::build(TModel3d &Cab)
|
||||
{
|
||||
auto *root{Cab.GetSMRoot()};
|
||||
if ((root == nullptr) || (false == bool(m_host.material)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// the materials of the glasses, in the order they come in the model; and a point inside the cab, the middle of the box
|
||||
// around the whole model (the glasses are around it)
|
||||
std::vector<material_handle> materials;
|
||||
glm::vec3 lower{std::numeric_limits<float>::max()};
|
||||
glm::vec3 upper{std::numeric_limits<float>::lowest()};
|
||||
visit_submodels(root, [&](TSubModel *Submodel) {
|
||||
auto const &vertices{submodel_vertices(Submodel)};
|
||||
if (false == vertices.empty())
|
||||
{
|
||||
auto const transform{submodel_transform(Submodel)};
|
||||
for (auto const &vertex : vertices)
|
||||
{
|
||||
auto const position{glm::vec3{transform * glm::vec4{vertex.position, 1.f}}};
|
||||
lower = glm::min(lower, position);
|
||||
upper = glm::max(upper, position);
|
||||
}
|
||||
}
|
||||
auto const material{Submodel->m_material};
|
||||
if ((material != null_handle) && (material > 0) && (Submodel->eType == GL_TRIANGLES) && (std::find(materials.begin(), materials.end(), material) == materials.end()) && is_glass(m_host.material(material)))
|
||||
{
|
||||
materials.push_back(material);
|
||||
}
|
||||
});
|
||||
if (materials.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (materials.size() > gl::WATER_SLOTS)
|
||||
{
|
||||
WriteLog("windscreen water: the cab has " + std::to_string(materials.size()) + " glasses, the water of the first " + std::to_string(gl::WATER_SLOTS) + " is simulated");
|
||||
materials.resize(gl::WATER_SLOTS);
|
||||
}
|
||||
auto const inside{(lower + upper) * 0.5f};
|
||||
auto const texels{static_cast<std::size_t>(gl::WATER_MAP) * gl::WATER_MAP};
|
||||
auto const size{static_cast<int>(gl::WATER_MAP)};
|
||||
for (auto const material : materials)
|
||||
{
|
||||
std::vector<glm::vec4> axes(texels * 2, glm::vec4{0.f});
|
||||
visit_submodels(root, [&](TSubModel *Submodel) { rasterize(Submodel, material, inside, axes); });
|
||||
auto &glass{m_glasses[m_glasscount]};
|
||||
glass = windscreen_water::glass{};
|
||||
glass.material = material;
|
||||
auto const *opengl{m_host.material(material)};
|
||||
glass.mask = opengl->textures[opengl->shader->texture_conf.at("wipermask").id];
|
||||
for (std::size_t texel = 0; texel < texels; ++texel)
|
||||
{
|
||||
if (axes[texel * 2].w > 0.f)
|
||||
{
|
||||
auto const density{glm::length(glm::cross(glm::vec3{axes[texel * 2]}, glm::vec3{axes[texel * 2 + 1]}))};
|
||||
glass.densest = std::max(glass.densest, density);
|
||||
glass.area += density / static_cast<float>(texels);
|
||||
}
|
||||
}
|
||||
if (glass.densest <= 0.f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// the edges spread out by two texels, so the filtering of the map near them takes the glass along
|
||||
for (int pass = 0; pass < 2; ++pass)
|
||||
{
|
||||
auto const source{axes};
|
||||
for (int y = 0; y < size; ++y)
|
||||
{
|
||||
for (int x = 0; x < size; ++x)
|
||||
{
|
||||
auto const texel{static_cast<std::size_t>(y * size + x)};
|
||||
if (source[texel * 2].w > 0.f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (int k = 0; k < 8; ++k)
|
||||
{
|
||||
static int const dx[]{1, -1, 0, 0, 1, 1, -1, -1};
|
||||
static int const dy[]{0, 0, 1, -1, 1, -1, 1, -1};
|
||||
auto const nx{x + dx[k]};
|
||||
auto const ny{y + dy[k]};
|
||||
if ((nx < 0) || (ny < 0) || (nx >= size) || (ny >= size))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto const neighbour{static_cast<std::size_t>(ny * size + nx)};
|
||||
if (source[neighbour * 2].w > 0.f)
|
||||
{
|
||||
axes[texel * 2] = glm::vec4{glm::vec3{source[neighbour * 2]}, 0.5f};
|
||||
axes[texel * 2 + 1] = source[neighbour * 2 + 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
gpu_setup(m_glasscount, axes);
|
||||
WriteLog("windscreen water: glass " + std::to_string(m_glasscount + 1) + " of the cab, material " + opengl->GetName() + ", " + std::to_string(glass.area) + " m2");
|
||||
++m_glasscount;
|
||||
}
|
||||
}
|
||||
|
||||
void windscreen_water::update(double const Seconds)
|
||||
{
|
||||
if (false == available())
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto const *vehicle{simulation::Train != nullptr ? simulation::Train->Dynamic() : nullptr};
|
||||
auto *cab{vehicle != nullptr ? vehicle->mdKabina : nullptr};
|
||||
if ((vehicle != m_vehicle) || (cab != m_cab))
|
||||
{
|
||||
// another cab: its glasses dry
|
||||
release();
|
||||
m_vehicle = vehicle;
|
||||
m_cab = cab;
|
||||
m_bladesbefore = glm::vec4{-1.f};
|
||||
if (cab != nullptr)
|
||||
{
|
||||
build(*cab);
|
||||
}
|
||||
}
|
||||
if (m_glasscount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gl::water_ubs block{};
|
||||
// the blades of the wipers of the vehicle, as the glasses read them (the channels of the mask follow the wipers of the cab
|
||||
// the vehicle is driven from)
|
||||
glm::vec4 blades{-1.f};
|
||||
if (auto const cabactive{vehicle->MoverParameters->CabActive}; cabactive != 0)
|
||||
{
|
||||
auto const count{static_cast<int>(vehicle->dWiperPos.size())};
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
if (i < count)
|
||||
{
|
||||
auto const index{cabactive > 0 ? i : count - 1 - i};
|
||||
blades[i] = static_cast<float>(std::clamp(vehicle->dWiperPos[index], 0.0, 1.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
// the space of the model of the cab, drawn with the matrix of the vehicle
|
||||
auto const toworld{glm::dmat3{vehicle->mMatrix}};
|
||||
auto const tomodel{glm::inverse(toworld)};
|
||||
auto const up{glm::normalize(glm::vec3{tomodel * glm::dvec3{0.0, 1.0, 0.0}})};
|
||||
auto const air{glm::vec3{tomodel * (glm::dvec3{simulation::Environment.wind()} * WIND_SHARE - vehicle->VectorFront() * vehicle->MoverParameters->V)}};
|
||||
auto const heavy{std::clamp(Global.Overcast - 1.f, 0.f, 1.f)};
|
||||
auto const snow{Global.Weather == "snow:"};
|
||||
auto const fall{snow ? SNOW_FALL : RAIN_FALL};
|
||||
|
||||
// the masks of the wipers baked into the maps once the textures are there
|
||||
for (std::size_t slot = 0; slot < m_glasscount; ++slot)
|
||||
{
|
||||
auto &glass{m_glasses[slot]};
|
||||
if (false == glass.baked)
|
||||
{
|
||||
block.counts = glm::uvec4{0u, 0u, static_cast<std::uint32_t>(slot), 0u};
|
||||
glass.baked = gpu_bake(slot, glass.mask, block);
|
||||
}
|
||||
}
|
||||
assign(true);
|
||||
|
||||
auto const seconds{std::clamp(Seconds, 0.0, LONGEST_STEP)};
|
||||
if (seconds <= 0.0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_time += seconds;
|
||||
++m_step;
|
||||
|
||||
// the drops landing: as many raindrops a second come to a square metre of the ground as the rain gives water (from light rain at
|
||||
// 1 mm/h to a downpour at 30 mm/h) in the drops of the Marshall-Palmer distribution above the least landing as a drop of its own
|
||||
// (exponential, the mean diameter falling with the rain); a glass gets them in the share it faces the rain coming at it
|
||||
// against the cab (water_spawn.comp)
|
||||
float perground{0.f}; // the drops coming to a square metre of the ground in a second
|
||||
auto const rate{1.f + 29.f * std::pow(heavy, 1.5f)}; // mm/h
|
||||
auto const lambda{4.1f * std::pow(rate, -0.21f)}; // 1 / mm
|
||||
if (heavy > 0.f)
|
||||
{
|
||||
auto const water{rate / 3600.f * 1e6f}; // mm3 a second on a square metre
|
||||
if (snow)
|
||||
{
|
||||
perground = water * 0.25f / FLAKE_WATER;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the share of the water in the drops above the least one, and their mean volume
|
||||
auto const x{lambda * LEAST_DROP};
|
||||
auto const share{std::exp(-x) * (1.f + x + x * x / 2.f + x * x * x / 6.f)};
|
||||
auto const d0{LEAST_DROP};
|
||||
auto const mean{0.5236f * (d0 * d0 * d0 + 3.f * d0 * d0 / lambda + 6.f * d0 / (lambda * lambda) + 6.f / (lambda * lambda * lambda))};
|
||||
perground = water * share / mean;
|
||||
}
|
||||
}
|
||||
// the landings tried on a glass: as if the whole of its uv held the glass at its densest, the ones at the less dense places and
|
||||
// facing the rain less are left out. the share of a landing left over goes to the next step
|
||||
auto const coming{glm::length(air - up * fall)};
|
||||
std::uint32_t most{0};
|
||||
m_tries = 0;
|
||||
for (std::size_t slot = 0; slot < m_glasscount; ++slot)
|
||||
{
|
||||
auto &glass{m_glasses[slot]};
|
||||
auto const *material{m_host.material(glass.material)};
|
||||
auto const simulated{glass.baked && is_glass(material)};
|
||||
glass.landings += static_cast<double>(perground / fall * coming * glass.densest) * seconds;
|
||||
auto const whole{std::floor(glass.landings)};
|
||||
glass.landings -= whole;
|
||||
auto const tries{simulated ? static_cast<std::uint32_t>(std::min<double>(whole, MOST_TRIES)) : 0u};
|
||||
most = std::max(most, tries);
|
||||
m_tries += tries;
|
||||
auto const scale{(material != nullptr) && std::isfinite(material->params[SLOT_PARAM].y) ? std::max(material->params[SLOT_PARAM].y, 0.05f) : 1.f};
|
||||
block.slots[slot] = glm::vec4{static_cast<float>(tries), 1.f / glass.densest, simulated ? 1.f : 0.f, scale};
|
||||
}
|
||||
|
||||
block.step = glm::vec4{static_cast<float>(seconds), static_cast<float>(m_time), heavy, snow ? 1.f : 0.f};
|
||||
block.up = glm::vec4{up, Global.AirTemperature};
|
||||
block.air = glm::vec4{air, fall};
|
||||
block.blades = blades;
|
||||
block.blades_before = glm::vec4{
|
||||
m_bladesbefore.x < 0.f ? blades.x : m_bladesbefore.x, m_bladesbefore.y < 0.f ? blades.y : m_bladesbefore.y, m_bladesbefore.z < 0.f ? blades.z : m_bladesbefore.z, m_bladesbefore.w < 0.f ? blades.w : m_bladesbefore.w};
|
||||
block.rain = glm::vec4{LEAST_DROP, lambda, 1.f, 0.f};
|
||||
block.counts = glm::uvec4{m_step * 2654435761u, most, 0u, 0u};
|
||||
m_bladesbefore = blades;
|
||||
gpu_step(block, most);
|
||||
}
|
||||
|
||||
std::string windscreen_water::info() const
|
||||
{
|
||||
if ((m_state <= 0) || (m_glasscount == 0))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
return "windscreen water: " + std::to_string(m_glasscount) + " glasses, " + std::to_string(m_tries) + " drops landing tried in the frame\n";
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
113
rendering/common/windscreenwater.h
Normal file
113
rendering/common/windscreenwater.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
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 <array>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "gl/ubo.h"
|
||||
#include "interfaces/ITexture.h"
|
||||
#include "utilities/Classes.h"
|
||||
|
||||
struct opengl_material;
|
||||
class TModel3d;
|
||||
class TSubModel;
|
||||
class TDynamicObject;
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
|
||||
// the water on the windscreens of the cab of the train, simulated on the gpu, whichever api runs it.
|
||||
//
|
||||
// a glass is a material of mat_rain_windscreen (one with a mask of the wipers) in the model of the cab of the train, up to
|
||||
// gl::WATER_SLOTS of them; the material is told its slot in its param water_slot (1 + the slot, the drawing of the glass reads
|
||||
// the water of the slot where it's there, mat_rain_windscreen.frag). the water lies in the coordinates of the texture of the glass
|
||||
// (uv), as the mask does. the map of a glass is made here from the triangles of the model with its material: for the texels of
|
||||
// the uv on the glass, the step along the glass (metres, the space of the model of the cab) for a step of u and of v, and which
|
||||
// way is out of the cab; the passes of the gpu bake the mask of the wipers into it. once a frame the step of the simulation is
|
||||
// worked out here (the time, the rain or snow, gravity and the air against the cab in the space of its model, the blades) and
|
||||
// the passes run it (water_*.comp: the landing drops, the drops moving and carried by the blades, the grids, the drops flowing
|
||||
// together); what they keep and the glasses read is the storage of the water (water.glsl, gl::WATER_*_OFFSET)
|
||||
class windscreen_water
|
||||
{
|
||||
public:
|
||||
// what the renderer gives: the materials of its manager, which get their slots
|
||||
struct host
|
||||
{
|
||||
std::function<opengl_material *(material_handle)> material;
|
||||
};
|
||||
|
||||
windscreen_water() = default;
|
||||
virtual ~windscreen_water() = default;
|
||||
windscreen_water(windscreen_water const &) = delete;
|
||||
windscreen_water &operator=(windscreen_water const &) = delete;
|
||||
|
||||
void set_host(host Host)
|
||||
{
|
||||
m_host = std::move(Host);
|
||||
}
|
||||
// whether the passes could be made
|
||||
bool available();
|
||||
// once a frame, before the colour passes: the cab of the train followed, the step of the simulation run. Seconds: of the
|
||||
// simulation since the frame before (0 when it's paused)
|
||||
void update(double Seconds);
|
||||
// the materials let go of their slots, the glasses forgotten (the storage stays as it is, unused)
|
||||
void release();
|
||||
std::string info() const;
|
||||
|
||||
protected:
|
||||
// the glasses forgotten and the programs taken as not made yet (the renderer let go of them), made again when next needed
|
||||
void reset();
|
||||
// the programs and the storage; false: the simulation can't be used, the glasses make their water up
|
||||
virtual bool gpu_init() = 0;
|
||||
// the slot set up for a glass: its drops, its grid and its paths cleared, its header saying it isn't simulated (until the
|
||||
// first step), the axes of its map (two vec4 a texel, gl::WATER_MAP x WATER_MAP texels, see water.glsl) put in the storage
|
||||
virtual void gpu_setup(std::size_t Slot, std::vector<glm::vec4> const &Axes) = 0;
|
||||
// the mask of the wipers of a glass (a texture of the renderer) baked into its map; false when the texture isn't ready yet
|
||||
// (tried again with the next frame)
|
||||
virtual bool gpu_bake(std::size_t Slot, texture_handle Mask, gl::water_ubs const &Block) = 0;
|
||||
// the step of the simulation of the glasses Block says are simulated; Tries: the most landings tried on a glass
|
||||
virtual void gpu_step(gl::water_ubs const &Block, std::uint32_t Tries) = 0;
|
||||
|
||||
private:
|
||||
struct glass
|
||||
{
|
||||
material_handle material{null_handle};
|
||||
texture_handle mask{null_handle};
|
||||
float densest{0.f}; // the most metres squared a unit of the uv holds
|
||||
float area{0.f}; // metres squared of the glass
|
||||
double landings{0.0}; // the share of a landing left over from the steps before
|
||||
bool baked{false};
|
||||
};
|
||||
|
||||
// the glasses of the model of a cab found and set up
|
||||
void build(TModel3d &Cab);
|
||||
// the triangles of a glass laid into its map; Inside: a point inside the cab
|
||||
void rasterize(TSubModel *Submodel, material_handle Material, glm::vec3 const &Inside, std::vector<glm::vec4> &Axes) const;
|
||||
// the slots given to the materials of the glasses (every frame: a material made anew takes its default params)
|
||||
void assign(bool Assigned);
|
||||
|
||||
host m_host;
|
||||
int m_state{0}; // 0: not set up yet, 1: ready, -1: can't be used
|
||||
TDynamicObject const *m_vehicle{nullptr};
|
||||
TModel3d const *m_cab{nullptr};
|
||||
std::array<glass, gl::WATER_SLOTS> m_glasses{};
|
||||
std::size_t m_glasscount{0};
|
||||
glm::vec4 m_bladesbefore{-1.f};
|
||||
double m_time{0.0}; // seconds the simulation runs
|
||||
std::uint32_t m_step{0};
|
||||
std::uint32_t m_tries{0}; // landings tried in the last step, all glasses
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
Reference in New Issue
Block a user