|
|
@@ -5,6 +5,7 @@
|
|
|
#include "../scene_renderer.h"
|
|
|
#include "ground_utils.h"
|
|
|
#include "map/terrain.h"
|
|
|
+#include <GL/gl.h>
|
|
|
#include <QVector2D>
|
|
|
#include <QVector3D>
|
|
|
#include <algorithm>
|
|
|
@@ -30,6 +31,10 @@ void RiverbankRenderer::configure(
|
|
|
m_grid_width = height_map.getWidth();
|
|
|
m_grid_height = height_map.getHeight();
|
|
|
m_heights = height_map.getHeightData();
|
|
|
+ m_visibilityTexture.reset();
|
|
|
+ m_cachedVisibilityVersion = 0;
|
|
|
+ m_visibilityWidth = 0;
|
|
|
+ m_visibilityHeight = 0;
|
|
|
build_meshes();
|
|
|
}
|
|
|
|
|
|
@@ -103,11 +108,9 @@ void RiverbankRenderer::build_meshes() {
|
|
|
QVector3D const perpendicular(-dir.z(), 0.0F, dir.x());
|
|
|
float const half_width = segment.width * 0.5F;
|
|
|
|
|
|
- // Multi-ring cross-section for volumetric bank geometry
|
|
|
- // Each ring has a distance from water edge and height profile
|
|
|
- constexpr int k_rings_per_side = 5; // water_edge, inner_mid, crest, outer_mid, terrain_blend
|
|
|
- constexpr int k_total_rings = k_rings_per_side * 2; // both sides
|
|
|
-
|
|
|
+ constexpr int k_rings_per_side = 5;
|
|
|
+ constexpr int k_total_rings = k_rings_per_side * 2;
|
|
|
+
|
|
|
int length_steps =
|
|
|
static_cast<int>(std::ceil(length / (m_tile_size * 0.5F))) + 1;
|
|
|
length_steps = std::max(length_steps, 8);
|
|
|
@@ -142,90 +145,100 @@ void RiverbankRenderer::build_meshes() {
|
|
|
QVector3D const center_offset = perpendicular * meander;
|
|
|
center_pos += center_offset;
|
|
|
|
|
|
- // Define cross-section profile for both sides
|
|
|
- // Heights: water edge slightly above water, crest raised, outer blends to terrain
|
|
|
struct RingProfile {
|
|
|
- float distance_from_water; // perpendicular distance from water edge
|
|
|
- float height_offset; // height above/below terrain
|
|
|
- };
|
|
|
-
|
|
|
- constexpr RingProfile k_left_rings[k_rings_per_side] = {
|
|
|
- {0.0F, 0.02F}, // water edge - just above water (halved from 0.05)
|
|
|
- {0.125F, 0.175F}, // inner midslope (halved from 0.25, 0.35)
|
|
|
- {0.25F, 0.3F}, // crest - peak of bank (halved from 0.5, 0.6)
|
|
|
- {0.375F, 0.125F}, // outer midslope (halved from 0.75, 0.25)
|
|
|
- {0.5F, -0.15F} // terrain blend - well below terrain to avoid z-fighting on hills
|
|
|
+ float distance_from_water;
|
|
|
+ float height_offset;
|
|
|
};
|
|
|
-
|
|
|
- // Add some noise-based width variation to ring distances
|
|
|
- float const ring_noise = noise(center_pos.x() * 3.0F, center_pos.z() * 3.0F) * 0.075F;
|
|
|
- float const base_bank_width = 0.5F + ring_noise; // Halved from 1.0F
|
|
|
-
|
|
|
- // Build vertices for left bank rings
|
|
|
- unsigned int const ring_start_idx = static_cast<unsigned int>(vertices.size());
|
|
|
-
|
|
|
+
|
|
|
+ constexpr RingProfile k_left_rings[k_rings_per_side] = {{0.0F, 0.02F},
|
|
|
+ {0.125F, 0.175F},
|
|
|
+ {0.25F, 0.3F},
|
|
|
+ {0.375F, 0.125F},
|
|
|
+ {0.5F, -0.15F}};
|
|
|
+
|
|
|
+ float const ring_noise =
|
|
|
+ noise(center_pos.x() * 3.0F, center_pos.z() * 3.0F) * 0.075F;
|
|
|
+ float const base_bank_width = 0.5F + ring_noise;
|
|
|
+
|
|
|
+ unsigned int const ring_start_idx =
|
|
|
+ static_cast<unsigned int>(vertices.size());
|
|
|
+
|
|
|
for (int ring = 0; ring < k_rings_per_side; ++ring) {
|
|
|
- float const ring_dist = k_left_rings[ring].distance_from_water * base_bank_width;
|
|
|
+ float const ring_dist =
|
|
|
+ k_left_rings[ring].distance_from_water * base_bank_width;
|
|
|
float const ring_height = k_left_rings[ring].height_offset;
|
|
|
-
|
|
|
- QVector3D const ring_pos = center_pos - perpendicular * (half_width + width_variation + ring_dist);
|
|
|
+
|
|
|
+ QVector3D const ring_pos =
|
|
|
+ center_pos -
|
|
|
+ perpendicular * (half_width + width_variation + ring_dist);
|
|
|
float const terrain_height = sample_height(ring_pos.x(), ring_pos.z());
|
|
|
-
|
|
|
- if (ring == 0) { // water edge
|
|
|
+
|
|
|
+ if (ring == 0) {
|
|
|
samples.push_back(ring_pos);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
Vertex vtx{};
|
|
|
vtx.position[0] = ring_pos.x();
|
|
|
vtx.position[1] = terrain_height + ring_height;
|
|
|
vtx.position[2] = ring_pos.z();
|
|
|
-
|
|
|
- // Calculate normal based on slope between rings
|
|
|
+
|
|
|
QVector3D normal;
|
|
|
if (ring == 0) {
|
|
|
- // Water edge: use normal pointing toward next ring
|
|
|
- QVector3D const next_ring_pos = center_pos - perpendicular *
|
|
|
- (half_width + width_variation + k_left_rings[1].distance_from_water * base_bank_width);
|
|
|
- float const next_terrain = sample_height(next_ring_pos.x(), next_ring_pos.z());
|
|
|
- QVector3D slope_vec(
|
|
|
- next_ring_pos.x() - ring_pos.x(),
|
|
|
- (next_terrain + k_left_rings[1].height_offset) - (terrain_height + ring_height),
|
|
|
- next_ring_pos.z() - ring_pos.z()
|
|
|
- );
|
|
|
+
|
|
|
+ QVector3D const next_ring_pos =
|
|
|
+ center_pos -
|
|
|
+ perpendicular *
|
|
|
+ (half_width + width_variation +
|
|
|
+ k_left_rings[1].distance_from_water * base_bank_width);
|
|
|
+ float const next_terrain =
|
|
|
+ sample_height(next_ring_pos.x(), next_ring_pos.z());
|
|
|
+ QVector3D slope_vec(next_ring_pos.x() - ring_pos.x(),
|
|
|
+ (next_terrain + k_left_rings[1].height_offset) -
|
|
|
+ (terrain_height + ring_height),
|
|
|
+ next_ring_pos.z() - ring_pos.z());
|
|
|
normal = QVector3D::crossProduct(slope_vec, dir).normalized();
|
|
|
} else if (ring == k_rings_per_side - 1) {
|
|
|
- // Outer edge: use normal from previous ring
|
|
|
+
|
|
|
unsigned int prev_idx = ring_start_idx + ring - 1;
|
|
|
- QVector3D prev_pos(vertices[prev_idx].position[0],
|
|
|
- vertices[prev_idx].position[1],
|
|
|
- vertices[prev_idx].position[2]);
|
|
|
+ QVector3D prev_pos(vertices[prev_idx].position[0],
|
|
|
+ vertices[prev_idx].position[1],
|
|
|
+ vertices[prev_idx].position[2]);
|
|
|
QVector3D slope_vec(ring_pos.x() - prev_pos.x(),
|
|
|
- (terrain_height + ring_height) - prev_pos.y(),
|
|
|
- ring_pos.z() - prev_pos.z());
|
|
|
+ (terrain_height + ring_height) - prev_pos.y(),
|
|
|
+ ring_pos.z() - prev_pos.z());
|
|
|
normal = QVector3D::crossProduct(slope_vec, dir).normalized();
|
|
|
} else {
|
|
|
- // Middle rings: average of adjacent slopes
|
|
|
+
|
|
|
unsigned int prev_idx = ring_start_idx + ring - 1;
|
|
|
- QVector3D prev_pos(vertices[prev_idx].position[0],
|
|
|
- vertices[prev_idx].position[1],
|
|
|
- vertices[prev_idx].position[2]);
|
|
|
-
|
|
|
- QVector3D const next_ring_pos = center_pos - perpendicular *
|
|
|
- (half_width + width_variation + k_left_rings[ring + 1].distance_from_water * base_bank_width);
|
|
|
- float const next_terrain = sample_height(next_ring_pos.x(), next_ring_pos.z());
|
|
|
-
|
|
|
+ QVector3D prev_pos(vertices[prev_idx].position[0],
|
|
|
+ vertices[prev_idx].position[1],
|
|
|
+ vertices[prev_idx].position[2]);
|
|
|
+
|
|
|
+ QVector3D const next_ring_pos =
|
|
|
+ center_pos -
|
|
|
+ perpendicular * (half_width + width_variation +
|
|
|
+ k_left_rings[ring + 1].distance_from_water *
|
|
|
+ base_bank_width);
|
|
|
+ float const next_terrain =
|
|
|
+ sample_height(next_ring_pos.x(), next_ring_pos.z());
|
|
|
+
|
|
|
QVector3D slope_from_prev(ring_pos.x() - prev_pos.x(),
|
|
|
- (terrain_height + ring_height) - prev_pos.y(),
|
|
|
- ring_pos.z() - prev_pos.z());
|
|
|
- QVector3D slope_to_next(next_ring_pos.x() - ring_pos.x(),
|
|
|
- (next_terrain + k_left_rings[ring + 1].height_offset) - (terrain_height + ring_height),
|
|
|
- next_ring_pos.z() - ring_pos.z());
|
|
|
-
|
|
|
- QVector3D n1 = QVector3D::crossProduct(slope_from_prev, dir).normalized();
|
|
|
- QVector3D n2 = QVector3D::crossProduct(slope_to_next, dir).normalized();
|
|
|
+ (terrain_height + ring_height) -
|
|
|
+ prev_pos.y(),
|
|
|
+ ring_pos.z() - prev_pos.z());
|
|
|
+ QVector3D slope_to_next(
|
|
|
+ next_ring_pos.x() - ring_pos.x(),
|
|
|
+ (next_terrain + k_left_rings[ring + 1].height_offset) -
|
|
|
+ (terrain_height + ring_height),
|
|
|
+ next_ring_pos.z() - ring_pos.z());
|
|
|
+
|
|
|
+ QVector3D n1 =
|
|
|
+ QVector3D::crossProduct(slope_from_prev, dir).normalized();
|
|
|
+ QVector3D n2 =
|
|
|
+ QVector3D::crossProduct(slope_to_next, dir).normalized();
|
|
|
normal = ((n1 + n2) * 0.5F).normalized();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
vtx.normal[0] = normal.x();
|
|
|
vtx.normal[1] = normal.y();
|
|
|
vtx.normal[2] = normal.z();
|
|
|
@@ -233,67 +246,80 @@ void RiverbankRenderer::build_meshes() {
|
|
|
vtx.tex_coord[1] = t;
|
|
|
vertices.push_back(vtx);
|
|
|
}
|
|
|
-
|
|
|
- // Build vertices for right bank rings (mirror of left)
|
|
|
+
|
|
|
for (int ring = 0; ring < k_rings_per_side; ++ring) {
|
|
|
- float const ring_dist = k_left_rings[ring].distance_from_water * base_bank_width;
|
|
|
+ float const ring_dist =
|
|
|
+ k_left_rings[ring].distance_from_water * base_bank_width;
|
|
|
float const ring_height = k_left_rings[ring].height_offset;
|
|
|
-
|
|
|
- QVector3D const ring_pos = center_pos + perpendicular * (half_width + width_variation + ring_dist);
|
|
|
+
|
|
|
+ QVector3D const ring_pos =
|
|
|
+ center_pos +
|
|
|
+ perpendicular * (half_width + width_variation + ring_dist);
|
|
|
float const terrain_height = sample_height(ring_pos.x(), ring_pos.z());
|
|
|
-
|
|
|
- if (ring == 0) { // water edge
|
|
|
+
|
|
|
+ if (ring == 0) {
|
|
|
samples.push_back(ring_pos);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
Vertex vtx{};
|
|
|
vtx.position[0] = ring_pos.x();
|
|
|
vtx.position[1] = terrain_height + ring_height;
|
|
|
vtx.position[2] = ring_pos.z();
|
|
|
-
|
|
|
- // Calculate normal (mirror logic from left side)
|
|
|
+
|
|
|
QVector3D normal;
|
|
|
if (ring == 0) {
|
|
|
- QVector3D const next_ring_pos = center_pos + perpendicular *
|
|
|
- (half_width + width_variation + k_left_rings[1].distance_from_water * base_bank_width);
|
|
|
- float const next_terrain = sample_height(next_ring_pos.x(), next_ring_pos.z());
|
|
|
- QVector3D slope_vec(
|
|
|
- next_ring_pos.x() - ring_pos.x(),
|
|
|
- (next_terrain + k_left_rings[1].height_offset) - (terrain_height + ring_height),
|
|
|
- next_ring_pos.z() - ring_pos.z()
|
|
|
- );
|
|
|
+ QVector3D const next_ring_pos =
|
|
|
+ center_pos +
|
|
|
+ perpendicular *
|
|
|
+ (half_width + width_variation +
|
|
|
+ k_left_rings[1].distance_from_water * base_bank_width);
|
|
|
+ float const next_terrain =
|
|
|
+ sample_height(next_ring_pos.x(), next_ring_pos.z());
|
|
|
+ QVector3D slope_vec(next_ring_pos.x() - ring_pos.x(),
|
|
|
+ (next_terrain + k_left_rings[1].height_offset) -
|
|
|
+ (terrain_height + ring_height),
|
|
|
+ next_ring_pos.z() - ring_pos.z());
|
|
|
normal = QVector3D::crossProduct(dir, slope_vec).normalized();
|
|
|
} else if (ring == k_rings_per_side - 1) {
|
|
|
unsigned int prev_idx = ring_start_idx + k_rings_per_side + ring - 1;
|
|
|
- QVector3D prev_pos(vertices[prev_idx].position[0],
|
|
|
- vertices[prev_idx].position[1],
|
|
|
- vertices[prev_idx].position[2]);
|
|
|
+ QVector3D prev_pos(vertices[prev_idx].position[0],
|
|
|
+ vertices[prev_idx].position[1],
|
|
|
+ vertices[prev_idx].position[2]);
|
|
|
QVector3D slope_vec(ring_pos.x() - prev_pos.x(),
|
|
|
- (terrain_height + ring_height) - prev_pos.y(),
|
|
|
- ring_pos.z() - prev_pos.z());
|
|
|
+ (terrain_height + ring_height) - prev_pos.y(),
|
|
|
+ ring_pos.z() - prev_pos.z());
|
|
|
normal = QVector3D::crossProduct(dir, slope_vec).normalized();
|
|
|
} else {
|
|
|
unsigned int prev_idx = ring_start_idx + k_rings_per_side + ring - 1;
|
|
|
- QVector3D prev_pos(vertices[prev_idx].position[0],
|
|
|
- vertices[prev_idx].position[1],
|
|
|
- vertices[prev_idx].position[2]);
|
|
|
-
|
|
|
- QVector3D const next_ring_pos = center_pos + perpendicular *
|
|
|
- (half_width + width_variation + k_left_rings[ring + 1].distance_from_water * base_bank_width);
|
|
|
- float const next_terrain = sample_height(next_ring_pos.x(), next_ring_pos.z());
|
|
|
-
|
|
|
+ QVector3D prev_pos(vertices[prev_idx].position[0],
|
|
|
+ vertices[prev_idx].position[1],
|
|
|
+ vertices[prev_idx].position[2]);
|
|
|
+
|
|
|
+ QVector3D const next_ring_pos =
|
|
|
+ center_pos +
|
|
|
+ perpendicular * (half_width + width_variation +
|
|
|
+ k_left_rings[ring + 1].distance_from_water *
|
|
|
+ base_bank_width);
|
|
|
+ float const next_terrain =
|
|
|
+ sample_height(next_ring_pos.x(), next_ring_pos.z());
|
|
|
+
|
|
|
QVector3D slope_from_prev(ring_pos.x() - prev_pos.x(),
|
|
|
- (terrain_height + ring_height) - prev_pos.y(),
|
|
|
- ring_pos.z() - prev_pos.z());
|
|
|
- QVector3D slope_to_next(next_ring_pos.x() - ring_pos.x(),
|
|
|
- (next_terrain + k_left_rings[ring + 1].height_offset) - (terrain_height + ring_height),
|
|
|
- next_ring_pos.z() - ring_pos.z());
|
|
|
-
|
|
|
- QVector3D n1 = QVector3D::crossProduct(dir, slope_from_prev).normalized();
|
|
|
- QVector3D n2 = QVector3D::crossProduct(dir, slope_to_next).normalized();
|
|
|
+ (terrain_height + ring_height) -
|
|
|
+ prev_pos.y(),
|
|
|
+ ring_pos.z() - prev_pos.z());
|
|
|
+ QVector3D slope_to_next(
|
|
|
+ next_ring_pos.x() - ring_pos.x(),
|
|
|
+ (next_terrain + k_left_rings[ring + 1].height_offset) -
|
|
|
+ (terrain_height + ring_height),
|
|
|
+ next_ring_pos.z() - ring_pos.z());
|
|
|
+
|
|
|
+ QVector3D n1 =
|
|
|
+ QVector3D::crossProduct(dir, slope_from_prev).normalized();
|
|
|
+ QVector3D n2 =
|
|
|
+ QVector3D::crossProduct(dir, slope_to_next).normalized();
|
|
|
normal = ((n1 + n2) * 0.5F).normalized();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
vtx.normal[0] = normal.x();
|
|
|
vtx.normal[1] = normal.y();
|
|
|
vtx.normal[2] = normal.z();
|
|
|
@@ -301,91 +327,85 @@ void RiverbankRenderer::build_meshes() {
|
|
|
vtx.tex_coord[1] = t;
|
|
|
vertices.push_back(vtx);
|
|
|
}
|
|
|
-
|
|
|
- // Add water-level skirts (vertical geometry down to water surface)
|
|
|
- // Left water skirt
|
|
|
+
|
|
|
{
|
|
|
Vertex const &water_edge_left = vertices[ring_start_idx];
|
|
|
Vertex skirt_vtx = water_edge_left;
|
|
|
- skirt_vtx.position[1] = -0.05F; // Water surface level
|
|
|
+ skirt_vtx.position[1] = -0.05F;
|
|
|
skirt_vtx.normal[0] = -perpendicular.x();
|
|
|
skirt_vtx.normal[1] = 0.0F;
|
|
|
skirt_vtx.normal[2] = -perpendicular.z();
|
|
|
vertices.push_back(skirt_vtx);
|
|
|
}
|
|
|
-
|
|
|
- // Right water skirt
|
|
|
+
|
|
|
{
|
|
|
- Vertex const &water_edge_right = vertices[ring_start_idx + k_rings_per_side];
|
|
|
+ Vertex const &water_edge_right =
|
|
|
+ vertices[ring_start_idx + k_rings_per_side];
|
|
|
Vertex skirt_vtx = water_edge_right;
|
|
|
- skirt_vtx.position[1] = -0.05F; // Water surface level
|
|
|
+ skirt_vtx.position[1] = -0.05F;
|
|
|
skirt_vtx.normal[0] = perpendicular.x();
|
|
|
skirt_vtx.normal[1] = 0.0F;
|
|
|
skirt_vtx.normal[2] = perpendicular.z();
|
|
|
vertices.push_back(skirt_vtx);
|
|
|
}
|
|
|
-
|
|
|
- // Connect rings with triangle strips
|
|
|
+
|
|
|
if (i < length_steps - 1) {
|
|
|
unsigned int const base_idx = ring_start_idx;
|
|
|
- unsigned int const next_base_idx = base_idx + k_total_rings + 2; // +2 for skirts
|
|
|
-
|
|
|
- // Left bank strips
|
|
|
+ unsigned int const next_base_idx = base_idx + k_total_rings + 2;
|
|
|
+
|
|
|
for (int ring = 0; ring < k_rings_per_side - 1; ++ring) {
|
|
|
unsigned int idx0 = base_idx + ring;
|
|
|
unsigned int idx1 = base_idx + ring + 1;
|
|
|
unsigned int idx2 = next_base_idx + ring;
|
|
|
unsigned int idx3 = next_base_idx + ring + 1;
|
|
|
-
|
|
|
+
|
|
|
indices.push_back(idx0);
|
|
|
indices.push_back(idx2);
|
|
|
indices.push_back(idx1);
|
|
|
-
|
|
|
+
|
|
|
indices.push_back(idx1);
|
|
|
indices.push_back(idx2);
|
|
|
indices.push_back(idx3);
|
|
|
}
|
|
|
-
|
|
|
- // Right bank strips
|
|
|
+
|
|
|
for (int ring = 0; ring < k_rings_per_side - 1; ++ring) {
|
|
|
unsigned int idx0 = base_idx + k_rings_per_side + ring;
|
|
|
unsigned int idx1 = base_idx + k_rings_per_side + ring + 1;
|
|
|
unsigned int idx2 = next_base_idx + k_rings_per_side + ring;
|
|
|
unsigned int idx3 = next_base_idx + k_rings_per_side + ring + 1;
|
|
|
-
|
|
|
+
|
|
|
indices.push_back(idx0);
|
|
|
indices.push_back(idx1);
|
|
|
indices.push_back(idx2);
|
|
|
-
|
|
|
+
|
|
|
indices.push_back(idx1);
|
|
|
indices.push_back(idx3);
|
|
|
indices.push_back(idx2);
|
|
|
}
|
|
|
-
|
|
|
- // Water skirt strips
|
|
|
+
|
|
|
{
|
|
|
unsigned int left_top = base_idx;
|
|
|
unsigned int left_bottom = base_idx + k_total_rings;
|
|
|
unsigned int left_top_next = next_base_idx;
|
|
|
unsigned int left_bottom_next = next_base_idx + k_total_rings;
|
|
|
-
|
|
|
+
|
|
|
indices.push_back(left_top);
|
|
|
indices.push_back(left_bottom);
|
|
|
indices.push_back(left_top_next);
|
|
|
-
|
|
|
+
|
|
|
indices.push_back(left_bottom);
|
|
|
indices.push_back(left_bottom_next);
|
|
|
indices.push_back(left_top_next);
|
|
|
-
|
|
|
+
|
|
|
unsigned int right_top = base_idx + k_rings_per_side;
|
|
|
unsigned int right_bottom = base_idx + k_total_rings + 1;
|
|
|
unsigned int right_top_next = next_base_idx + k_rings_per_side;
|
|
|
unsigned int right_bottom_next = next_base_idx + k_total_rings + 1;
|
|
|
-
|
|
|
+
|
|
|
indices.push_back(right_top);
|
|
|
indices.push_back(right_top_next);
|
|
|
indices.push_back(right_bottom);
|
|
|
-
|
|
|
+
|
|
|
indices.push_back(right_bottom);
|
|
|
indices.push_back(right_top_next);
|
|
|
indices.push_back(right_bottom_next);
|
|
|
@@ -417,6 +437,77 @@ void RiverbankRenderer::submit(Renderer &renderer, ResourceManager *resources) {
|
|
|
|
|
|
renderer.set_current_shader(shader);
|
|
|
|
|
|
+ auto *backend = renderer.backend();
|
|
|
+ auto &visibility = Game::Map::VisibilityService::instance();
|
|
|
+ const bool use_visibility = visibility.is_initialized();
|
|
|
+
|
|
|
+ Texture *visibility_tex = nullptr;
|
|
|
+ QVector2D visibility_size(0.0F, 0.0F);
|
|
|
+
|
|
|
+ if (use_visibility) {
|
|
|
+ const int vis_w = visibility.getWidth();
|
|
|
+ const int vis_h = visibility.getHeight();
|
|
|
+ const std::uint64_t version = visibility.version();
|
|
|
+ bool const size_changed =
|
|
|
+ (vis_w != m_visibilityWidth) || (vis_h != m_visibilityHeight);
|
|
|
+
|
|
|
+ if (!m_visibilityTexture || size_changed) {
|
|
|
+ m_visibilityTexture = std::make_unique<Texture>();
|
|
|
+ m_visibilityTexture->create_empty(vis_w, vis_h, Texture::Format::RGBA);
|
|
|
+ m_visibilityTexture->set_filter(Texture::Filter::Nearest,
|
|
|
+ Texture::Filter::Nearest);
|
|
|
+ m_visibilityTexture->set_wrap(Texture::Wrap::ClampToEdge,
|
|
|
+ Texture::Wrap::ClampToEdge);
|
|
|
+ m_visibilityWidth = vis_w;
|
|
|
+ m_visibilityHeight = vis_h;
|
|
|
+ m_cachedVisibilityVersion = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (version != m_cachedVisibilityVersion || size_changed) {
|
|
|
+ auto cells = visibility.snapshotCells();
|
|
|
+ std::vector<unsigned char> texels(
|
|
|
+ static_cast<std::size_t>(vis_w * vis_h * 4), 0U);
|
|
|
+
|
|
|
+ for (int z = 0; z < vis_h; ++z) {
|
|
|
+ for (int x = 0; x < vis_w; ++x) {
|
|
|
+ int const idx = z * vis_w + x;
|
|
|
+ unsigned char val = 0U;
|
|
|
+ switch (static_cast<Game::Map::VisibilityState>(cells[idx])) {
|
|
|
+ case Game::Map::VisibilityState::Visible:
|
|
|
+ val = 255U;
|
|
|
+ break;
|
|
|
+ case Game::Map::VisibilityState::Explored:
|
|
|
+ val = 128U;
|
|
|
+ break;
|
|
|
+ case Game::Map::VisibilityState::Unseen:
|
|
|
+ default:
|
|
|
+ val = 0U;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ texels[static_cast<std::size_t>(idx * 4)] = val;
|
|
|
+ texels[static_cast<std::size_t>(idx * 4 + 3)] = 255;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ m_visibilityTexture->bind();
|
|
|
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, vis_w, vis_h, GL_RGBA,
|
|
|
+ GL_UNSIGNED_BYTE, texels.data());
|
|
|
+ visibility_tex = m_visibilityTexture.get();
|
|
|
+ m_cachedVisibilityVersion = version;
|
|
|
+ } else {
|
|
|
+ visibility_tex = m_visibilityTexture.get();
|
|
|
+ }
|
|
|
+
|
|
|
+ visibility_size =
|
|
|
+ QVector2D(static_cast<float>(vis_w), static_cast<float>(vis_h));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (backend != nullptr) {
|
|
|
+ backend->setRiverbankVisibility(use_visibility && visibility_tex != nullptr,
|
|
|
+ visibility_tex, visibility_size,
|
|
|
+ m_tile_size, m_exploredDimFactor);
|
|
|
+ }
|
|
|
+
|
|
|
QMatrix4x4 model;
|
|
|
model.setToIdentity();
|
|
|
|
|
|
@@ -433,8 +524,36 @@ void RiverbankRenderer::submit(Renderer &renderer, ResourceManager *resources) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- // Always render - fog overlay handles visibility naturally (like terrain)
|
|
|
- renderer.mesh(mesh, model, QVector3D(1.0F, 1.0F, 1.0F), nullptr, 1.0F);
|
|
|
+ float segment_visibility = 1.0F;
|
|
|
+ if (use_visibility) {
|
|
|
+ enum class SegmentState { Hidden, Explored, Visible };
|
|
|
+ SegmentState state = SegmentState::Hidden;
|
|
|
+
|
|
|
+ const auto &samples = m_visibilitySamples[mesh_index - 1];
|
|
|
+ if (samples.empty()) {
|
|
|
+ state = SegmentState::Visible;
|
|
|
+ }
|
|
|
+ for (const auto &sample : samples) {
|
|
|
+ if (visibility.isVisibleWorld(sample.x(), sample.z())) {
|
|
|
+ state = SegmentState::Visible;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if ((state == SegmentState::Hidden) &&
|
|
|
+ visibility.isExploredWorld(sample.x(), sample.z())) {
|
|
|
+ state = SegmentState::Explored;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (state == SegmentState::Hidden) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ segment_visibility =
|
|
|
+ (state == SegmentState::Visible) ? 1.0F : m_exploredDimFactor;
|
|
|
+ }
|
|
|
+
|
|
|
+ renderer.mesh(mesh, model, QVector3D(1.0F, 1.0F, 1.0F), nullptr,
|
|
|
+ segment_visibility);
|
|
|
}
|
|
|
|
|
|
renderer.set_current_shader(nullptr);
|