mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-04 01:52:05 +01:00
Migration cleanup and renderer documentation
This commit is contained in:
18
EU07.cpp
18
EU07.cpp
@@ -81,6 +81,15 @@ LONG WINAPI CrashHandler(EXCEPTION_POINTERS *ExceptionInfo)
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// lets the log service write what is still queued and stops it; does nothing once Application.exit() has done that
|
||||||
|
static void stop_log_service()
|
||||||
|
{
|
||||||
|
Global.applicationQuitOrder = true;
|
||||||
|
auto const it = Global.threads.find("LogService");
|
||||||
|
if (it != Global.threads.end() && it->second.joinable())
|
||||||
|
it->second.join();
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
#ifdef WITHDUMPGEN
|
#ifdef WITHDUMPGEN
|
||||||
@@ -91,6 +100,8 @@ int main(int argc, char *argv[])
|
|||||||
// init start timestamp
|
// init start timestamp
|
||||||
Global.startTimestamp = std::chrono::steady_clock::now();
|
Global.startTimestamp = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
int exitcode{0};
|
||||||
|
|
||||||
// quick short-circuit for standalone e3d export
|
// quick short-circuit for standalone e3d export
|
||||||
if (argc == 6 && std::string(argv[1]) == "-e3d")
|
if (argc == 6 && std::string(argv[1]) == "-e3d")
|
||||||
{
|
{
|
||||||
@@ -140,6 +151,10 @@ int main(int argc, char *argv[])
|
|||||||
result = Application.run();
|
result = Application.run();
|
||||||
Application.exit();
|
Application.exit();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
exitcode = result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (std::bad_alloc const &Error)
|
catch (std::bad_alloc const &Error)
|
||||||
{
|
{
|
||||||
@@ -154,9 +169,10 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
stop_log_service();
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
#endif
|
#endif
|
||||||
std::_Exit(0); // skip destructors, there are ordering errors which causes segfaults
|
std::_Exit(exitcode); // skip destructors, there are ordering errors which causes segfaults
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
       [](LICENSE) [](https://github.com/MaSzyna-EU07/maszyna/commits/master) [](https://github.com/MaSzyna-EU07/maszyna/issues) [](https://github.com/MaSzyna-EU07/maszyna/pulls) [](https://github.com/MaSzyna-EU07/maszyna/stargazers) [](https://github.com/MaSzyna-EU07/maszyna/forks)    
|
) [](LICENSE) [](https://github.com/MaSzyna-EU07/maszyna/commits/master) [](https://github.com/MaSzyna-EU07/maszyna/issues) [](https://github.com/MaSzyna-EU07/maszyna/pulls) [](https://github.com/MaSzyna-EU07/maszyna/stargazers) [](https://github.com/MaSzyna-EU07/maszyna/forks)    
|
||||||
|
|
||||||
MaSzyna executable source code is licensed under the [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/) and comes with a large pack of free assets under a [custom license](https://eu07.pl/docs/inne/readme_pliki/en-licence.html).
|
MaSzyna executable source code is licensed under the [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/) and comes with a large pack of free assets under a [custom license](https://eu07.pl/docs/inne/readme_pliki/en-licence.html).
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ MaSzyna compiles and runs natively on **Linux** and **Windows**. Other platforms
|
|||||||
|
|
||||||
**3) Graphics API**
|
**3) Graphics API**
|
||||||
|
|
||||||
- **OpenGL 3.0** or newer
|
- **OpenGL 4.6** (core profile), required by the default renderer; see [docs/opengl46-renderer.md](docs/opengl46-renderer.md)
|
||||||
- **DirectX 12** via NVRHI (optional, Windows only, for Better Renderer)
|
- **DirectX 12** via NVRHI (optional, Windows only, for Better Renderer)
|
||||||
|
|
||||||
|
|
||||||
@@ -105,6 +105,8 @@ MaSzyna compiles and runs natively on **Linux** and **Windows**. Other platforms
|
|||||||
|
|
||||||
> **Linux note:** `WITH_BETTER_RENDERER` uses DirectX 12 through NVRHI and is **not supported on Linux**.
|
> **Linux note:** `WITH_BETTER_RENDERER` uses DirectX 12 through NVRHI and is **not supported on Linux**.
|
||||||
|
|
||||||
|
> **macOS note:** macOS provides OpenGL only up to 4.1, so the OpenGL 4.6 renderer does not run there.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -232,7 +234,7 @@ If detection still fails, padding the file (with comments) up to ~1 MB may hel
|
|||||||
- OS: **Windows 10 64-bit**
|
- OS: **Windows 10 64-bit**
|
||||||
- Processor: **2 core at least 2 GHz (e.g. Intel Core i3-2100 / AMD Athlon II X2)**
|
- Processor: **2 core at least 2 GHz (e.g. Intel Core i3-2100 / AMD Athlon II X2)**
|
||||||
- RAM: **6 GB RAM**
|
- RAM: **6 GB RAM**
|
||||||
- Graphics Card: **supporting OpenGL 3.3 with at least 1GB VRAM**
|
- Graphics Card: **supporting OpenGL 4.6 with at least 1GB VRAM**
|
||||||
- **150 GB of free disk space**
|
- **150 GB of free disk space**
|
||||||
- Sound card supporting OpenAL with stereo
|
- Sound card supporting OpenAL with stereo
|
||||||
- Optional COM ports for custom control panel setups.
|
- Optional COM ports for custom control panel setups.
|
||||||
|
|||||||
@@ -1230,12 +1230,6 @@ static std::string describe_gl_driver()
|
|||||||
|
|
||||||
int eu07_application::init_glfw()
|
int eu07_application::init_glfw()
|
||||||
{
|
{
|
||||||
{
|
|
||||||
int glfw_major, glfw_minor, glfw_rev;
|
|
||||||
glfwGetVersion(&glfw_major, &glfw_minor, &glfw_rev);
|
|
||||||
m_glfwversion = glfw_major * 10000 + glfw_minor * 100 + glfw_minor;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (glfwInit() == GLFW_FALSE)
|
if (glfwInit() == GLFW_FALSE)
|
||||||
{
|
{
|
||||||
ErrorLog("Bad init: failed to initialize glfw");
|
ErrorLog("Bad init: failed to initialize glfw");
|
||||||
|
|||||||
@@ -123,7 +123,6 @@ private:
|
|||||||
mode_stack m_modestack; // current behaviour mode
|
mode_stack m_modestack; // current behaviour mode
|
||||||
python_taskqueue m_taskqueue;
|
python_taskqueue m_taskqueue;
|
||||||
std::vector<GLFWwindow *> m_windows;
|
std::vector<GLFWwindow *> m_windows;
|
||||||
int m_glfwversion;
|
|
||||||
|
|
||||||
std::optional<network::manager> m_network;
|
std::optional<network::manager> m_network;
|
||||||
std::optional<headtrack> m_headtrack;
|
std::optional<headtrack> m_headtrack;
|
||||||
|
|||||||
@@ -1447,8 +1447,6 @@ debug_panel::update_section_renderer( std::vector<text_line> &Output ) const {
|
|||||||
textline += "NVRHI on ";
|
textline += "NVRHI on ";
|
||||||
}
|
}
|
||||||
|
|
||||||
textline += ", VBO";
|
|
||||||
|
|
||||||
textline += " ";
|
textline += " ";
|
||||||
|
|
||||||
if( false == Global.LastGLError.empty() ) {
|
if( false == Global.LastGLError.empty() ) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
| Console/ | Hardware I/O |
|
| Console/ | Hardware I/O |
|
||||||
| application/ | Core simulation loop, time, environment, state serializer, sounds |
|
| application/ | Core simulation loop, time, environment, state serializer, sounds |
|
||||||
| scene/ | Scene graph, scene nodes, scene editor, utilities |
|
| scene/ | Scene graph, scene nodes, scene editor, utilities |
|
||||||
| rendering/ | All renderers (OpenGL legacy and full), geometry banks, particles, lights, frustum |
|
| rendering/ | Renderers (the OpenGL 4.6 one is in rendering/opengl46/), geometry banks, particles, lights, frustum |
|
||||||
| audio/ | Sound, audio, and audio renderer |
|
| audio/ | Sound, audio, and audio renderer |
|
||||||
| environment/ | Sky, sun, moon, stars, skydome |
|
| environment/ | Sky, sun, moon, stars, skydome |
|
||||||
| application/ | Application modes, UI layers/panels, driver/editor/scenario modes |
|
| application/ | Application modes, UI layers/panels, driver/editor/scenario modes |
|
||||||
|
|||||||
130
docs/clustered-lighting.md
Normal file
130
docs/clustered-lighting.md
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
# Clustered lighting
|
||||||
|
|
||||||
|
How the OpenGL 4.6 renderer (`rendering/opengl46/`) gives the shaders the lights of vehicle headlights.
|
||||||
|
For the settings and what changed for players see [opengl46-renderer.md](opengl46-renderer.md).
|
||||||
|
|
||||||
|
## Idea
|
||||||
|
|
||||||
|
The view frustum of the main pass is cut into a grid of **clusters**: 16 x 9 screen tiles and 24 depth slices,
|
||||||
|
3456 in all. A compute shader works out which lights reach each cluster, once per frame. The fragment shader
|
||||||
|
finds the cluster of its fragment and loops over the lights of that cluster only, so the cost of shading
|
||||||
|
depends on the lights that are near a fragment and not on the number of lights in the scene. The sun is not
|
||||||
|
a part of this: it is a single directional light, handled in the shader as before, with the cascaded shadow map.
|
||||||
|
|
||||||
|
All of the sizes are constants in `gl/ubo.h`; they reach the shaders as `#define`s written by
|
||||||
|
`gl/glsl_common.cpp` from the same constants.
|
||||||
|
|
||||||
|
## Buffers
|
||||||
|
|
||||||
|
| Binding | Kind | Name (GLSL) | Content | Written by |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| SSBO 0 | std430 | `light_ssbo` | header (`ambient`, `fog_color`, `lights_total`, `lights_count`, `lights_clustered`) and `lights[]` (slot 0 is the sun) | CPU, `Update_Lights()` |
|
||||||
|
| SSBO 1 | std430 | `cluster_aabb_ssbo` | bounding box of every cluster in view space | `cluster_build.comp` |
|
||||||
|
| SSBO 2 | std430 | `cluster_range_ssbo` | `(offset, count)` of every cluster into the index list | `cluster_cull.comp` |
|
||||||
|
| SSBO 3 | std430 | `cluster_index_ssbo` | shared list of indices into `lights[]` | `cluster_cull.comp` |
|
||||||
|
| SSBO 4 | std430 | `cluster_counter_ssbo` | `index_count` (allocator of the list) and `overflow_count` | zeroed by the CPU, `cluster_cull.comp` |
|
||||||
|
| UBO 2 | std140 | `cluster_ubo` | grid parameters of the pass: projection, near / far, slice scale and bias, target size, `valid` | CPU, `cluster_grid::update()` |
|
||||||
|
|
||||||
|
The UBO numbers go together with the scene (0), model (1) and instance (3) blocks; the SSBOs have a binding
|
||||||
|
space of their own. Layouts of the structures are checked with `static_assert`s on sizes and offsets in
|
||||||
|
`gl/ubo.h`, and have to stay equal to the blocks in `gl/glsl_common.cpp`.
|
||||||
|
|
||||||
|
Sizes: `MAX_LIGHTS` = 256 (the sun and 255 headlights), `MAX_LIGHTS_PER_CLUSTER` = 64. The buffers have a
|
||||||
|
fixed size, so changing the resolution, the field of view or the draw range only makes the grid parameters
|
||||||
|
and boxes be recomputed.
|
||||||
|
|
||||||
|
`gl::light_data.lights_total` is the number of lights written to `lights[]`, which the culling reads.
|
||||||
|
`lights_count` is the length of the plain loop over the lights (the sun and the first `dynamiclights` headlights) used
|
||||||
|
when the lights are not clustered. `lights_clustered` is set per pass and tells the shading which one to use.
|
||||||
|
|
||||||
|
## Frame order
|
||||||
|
|
||||||
|
In `Render_pass()` for the color pass of a view (`opengl46_renderer.cpp`):
|
||||||
|
|
||||||
|
1. `Update_Clusters(vp)`: `cluster_grid::update()` compares the projection and near / far plane with those the
|
||||||
|
boxes were built for. When they differ, `cluster_build.comp` recomputes the boxes (one invocation per
|
||||||
|
cluster, 54 work groups of 64). This is the "clusters" timer.
|
||||||
|
2. `Update_Lights(...)`: sorts the lights of the scene (`simulation::Lights`) by distance squared over
|
||||||
|
intensity, skips the lights that are off or farther than 1 km, writes each lit headlight to `lights[]`
|
||||||
|
in view space, and uploads the header and the part of the array in use.
|
||||||
|
3. `Cull_Lights(vp)`: the CPU zeroes the counters, then `cluster_cull.comp` runs (one invocation per cluster, 54
|
||||||
|
work groups of 64): it tests every light against the box of its cluster and writes the indices of the ones
|
||||||
|
that reach it. Then `glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT | GL_BUFFER_UPDATE_BARRIER_BIT)`. This is the
|
||||||
|
"cull" timer.
|
||||||
|
4. The scene is drawn. Opaque, transparent and the cab all take the lights from the cluster lists.
|
||||||
|
5. With `gfx.lights.clusterheatmap`, `Render_cluster_heatmap()` draws the number of lights per cluster over
|
||||||
|
the finished image.
|
||||||
|
|
||||||
|
Steps 1 and 3 are skipped when neither `gfx.lights.clustered` nor the heat map is on.
|
||||||
|
|
||||||
|
The grid and its buffers belong to a **viewport** (`viewport_config::clusters`): the main view, the extra
|
||||||
|
viewports and the eyes of a VR headset each have their own, because each has its own projection. The
|
||||||
|
indexed binding points are context state, so `cluster_grid::bind()` is called at the start of every pass that
|
||||||
|
uses the grid.
|
||||||
|
|
||||||
|
Passes without a grid: the orthographic view of the editor (`valid` = 0), the environment map (it gets its own
|
||||||
|
`Update_Lights(..., false)`: the sun and ambient light, `lights_total` = 1) and the model previews. They set
|
||||||
|
`lights_clustered` = 0 and use the plain loop.
|
||||||
|
|
||||||
|
## The grid
|
||||||
|
|
||||||
|
Depth is the clip space `w` of a point, which equals `1 / gl_FragCoord.w` of its fragment. Unlike the view
|
||||||
|
space `z` it is right for tilted screens (VR, extra viewports), where the screen is not perpendicular to the view axis. The projection is
|
||||||
|
reverse-Z with the far plane at infinity, so the `znear` / `zfar` of the pass bound the slices.
|
||||||
|
|
||||||
|
- Slice: `floor(log(w) * slice_scale + slice_bias)`, with `slice_scale = 24 / ln(zfar / znear)` and
|
||||||
|
`slice_bias = -ln(znear) * slice_scale`; exponential spacing from `znear` to `zfar`.
|
||||||
|
- Tile: `gl_FragCoord.xy` scaled to the 16 x 9 grid; both the tile and the slice are clamped to the grid
|
||||||
|
(a coordinate just under the edge rounds up to the next tile in float).
|
||||||
|
- Index: `x + 16 * (y + 9 * z)`, with `x` and `y` counted from the lower left corner, as `gl_FragCoord`.
|
||||||
|
- Box: `x`, `y` and `w` of the clip space are affine in the view-space position, so the eight corners of a
|
||||||
|
cluster (`ndc.xy` times `w` at the near and far side of the slice) map back to view space through the
|
||||||
|
inverse of the corresponding 3x3 part of the projection. The box is the min and max of the corners.
|
||||||
|
|
||||||
|
## Culling
|
||||||
|
|
||||||
|
`cluster_cull.comp` handles one cluster per invocation and goes through `lights[1 .. lights_total)`.
|
||||||
|
|
||||||
|
- A light's reach is the distance where its brightest possible contribution falls under `MIN_CONTRIBUTION`
|
||||||
|
(1/1024) of the display range, at most `MAX_RANGE` (1000 m, as the 1 km filter on the CPU). Headlights are tested against
|
||||||
|
that sphere and against the frustum of their projection (four side planes and the near plane, with 5 cm of margin);
|
||||||
|
point lights against a sphere; spot lights against a sphere and a cone; directional lights reach everything.
|
||||||
|
- A cluster takes up to 64 lights. The ones that come first in `lights[]` win, which is the order from
|
||||||
|
`Update_Lights()`. The pairs that did not fit are counted in `overflow_count`.
|
||||||
|
- The list is one shared array: each cluster reserves its place with one `atomicAdd` on `index_count`.
|
||||||
|
- The counters are copied to a ring of four small buffers with fences and read some frames later, so the
|
||||||
|
statistics never stall the GPU.
|
||||||
|
|
||||||
|
The headlight projection has no far plane (infinite, reverse-Z), so the reach of a headlight comes from
|
||||||
|
its falloff and the sphere.
|
||||||
|
|
||||||
|
## Shading
|
||||||
|
|
||||||
|
`shaders/light_common.glsl`:
|
||||||
|
|
||||||
|
- `fragment_cluster()` gives the cluster index of the fragment as described above.
|
||||||
|
- `add_headlights()` is the single loop over headlights, used by `apply_lights()` and by the normal-map
|
||||||
|
materials without sun: if `lights_clustered` is set it goes through
|
||||||
|
`cluster_light_indices[offset .. offset + count)`, otherwise through `1 .. lights_count - 1` of `lights[]`.
|
||||||
|
- `calc_headlights()` samples the beam texture only inside the 0..1 window of the projection, with
|
||||||
|
`textureGrad` and derivatives taken from the position before the loop: fragments of one quad may have
|
||||||
|
different lists at a cluster edge, and derivatives inside divergent control flow are undefined.
|
||||||
|
- The sun and `calc_shadow()` (cascaded shadow map with `textureGather`) are outside the loop.
|
||||||
|
|
||||||
|
`light_ssbo` and the cluster blocks are declared only for the fragment and compute stages; GL 4.6 does not
|
||||||
|
guarantee any storage blocks in the vertex and geometry stages.
|
||||||
|
|
||||||
|
## Changing it
|
||||||
|
|
||||||
|
- Grid size, per-cluster capacity, `MAX_LIGHTS`, binding numbers: `gl/ubo.h`, then `gl/glsl_common.cpp` takes them over.
|
||||||
|
The work group size in the shaders and the number of groups dispatched both come from `CLUSTER_*_GROUP_SIZE`.
|
||||||
|
- Check the shaders with `ci_shadervalidator` (see [opengl46-renderer.md](opengl46-renderer.md)); CI does it for
|
||||||
|
changes in `shaders/` and `gl/`.
|
||||||
|
- Only vehicle headlights feed `lights[]` today. The culling already knows `SPOT`, `POINT` and `DIR`, but
|
||||||
|
`add_headlights()` runs `calc_headlights()` for every listed light, so a new kind of light also needs
|
||||||
|
its own branch there.
|
||||||
|
|
||||||
|
## Not covered
|
||||||
|
|
||||||
|
- The environment map pass has no clusters of its own; it has the sun only.
|
||||||
|
- A grid for extra viewports and VR is built, but those paths have not been verified on hardware.
|
||||||
99
docs/opengl46-renderer.md
Normal file
99
docs/opengl46-renderer.md
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
# OpenGL 4.6 renderer
|
||||||
|
|
||||||
|
The default renderer (`gfxrenderer default`, the `modern` backend, `rendering/opengl46/`) runs on an
|
||||||
|
**OpenGL 4.6 core profile** context. There is no fallback to an older version, and the OpenGL 2.1 (legacy)
|
||||||
|
renderer and the OpenGL ES / ANGLE path no longer exist.
|
||||||
|
|
||||||
|
For how the lights are handled, see [clustered-lighting.md](clustered-lighting.md).
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- A graphics card and driver that provide **OpenGL 4.6** (core profile). What the driver offers is written to
|
||||||
|
the log at start: `Renderer:`, `Vendor:` and `GL version:`.
|
||||||
|
- A CPU with AVX2, as before.
|
||||||
|
- Windows and Linux. macOS provides OpenGL only up to 4.1, so the renderer does not run there.
|
||||||
|
- The experimental Better Renderer (`gfxrenderer experimental`) is a separate renderer and is not affected.
|
||||||
|
|
||||||
|
On Linux, `glxinfo -B` should report an `OpenGL core profile version string` of 4.6 or newer.
|
||||||
|
|
||||||
|
### If the simulator does not start
|
||||||
|
|
||||||
|
When the context cannot be created, the log (`log.txt`) contains a line like
|
||||||
|
|
||||||
|
```
|
||||||
|
Bad init: failed to create OpenGL 4.6 core profile context (<GLFW error>); the graphics card/driver provides: OpenGL X.Y ..., <renderer> (<vendor>)
|
||||||
|
```
|
||||||
|
|
||||||
|
On Windows the same text is shown in a message box. Updating the graphics driver is the usual fix; a card
|
||||||
|
which does not support OpenGL 4.6 cannot run this renderer. The process ends with a non-zero exit code.
|
||||||
|
|
||||||
|
## eu07.ini
|
||||||
|
|
||||||
|
### Removed options
|
||||||
|
|
||||||
|
Old `eu07.ini` files still load. These entries are read and ignored, with a line in the log:
|
||||||
|
|
||||||
|
| Entry | What happens now |
|
||||||
|
|---|---|
|
||||||
|
| `gfxrenderer legacy`, `simple` or any other value except `default`, `full` and `experimental` | the default renderer is used (`eu07.ini: gfxrenderer "X" is no longer supported, using "default"`) |
|
||||||
|
| `usevbo` | ignored (`eu07.ini: "usevbo" is no longer supported and was ignored`) |
|
||||||
|
| `gfx.usegles` | ignored, same message |
|
||||||
|
| `gfx.angleplatform` | ignored, same message |
|
||||||
|
|
||||||
|
`gfxrenderer full` is still taken for `default`.
|
||||||
|
|
||||||
|
### New options
|
||||||
|
|
||||||
|
| Entry | Default | Meaning |
|
||||||
|
|---|---|---|
|
||||||
|
| `gfx.lights.clustered yes/no` | `yes` | `yes`: every lit vehicle headlight within 1 km lights the surroundings (see below). `no`: the shading goes through the first `dynamiclights` headlights only, as the old renderer did; useful to compare images or to work around a problem |
|
||||||
|
| `gfx.lights.clusterheatmap yes/no` | `no` | debug view: the number of lights in each cluster, drawn over the image |
|
||||||
|
|
||||||
|
### Changed option
|
||||||
|
|
||||||
|
`dynamiclights` (0..7) used to limit the number of headlight sets that light the surroundings. It is still
|
||||||
|
read, but it only takes effect with `gfx.lights.clustered no`.
|
||||||
|
|
||||||
|
## What looks different from the previous renderer
|
||||||
|
|
||||||
|
- **No limit of 7 lit headlights.** All lit headlights within 1 km take part in the lighting (each lit end
|
||||||
|
of a vehicle counts separately), up to 255 in the buffer and 64 reaching any one part of the view. Beyond
|
||||||
|
these capacities the farthest and dimmest lights are dropped, and the statistics show how many.
|
||||||
|
- **Outside the beam texture a headlight adds nothing.** Before, the edge of the texture was stretched
|
||||||
|
beyond the beam, which could leave a faint glow around vehicles. This also applies with
|
||||||
|
`gfx.lights.clustered no`.
|
||||||
|
- **Reflections (environment map)** are lit with the sun as seen from the reflection itself, and vehicle
|
||||||
|
headlights no longer light the scene in them.
|
||||||
|
- **Motion blur and SSAO** read the real depth of the scene. Before, the depth texture was read in a way the
|
||||||
|
specification leaves undefined, and the result depended on the driver.
|
||||||
|
- **Lines are 1 pixel wide**, as they always were on this kind of context. The widths the code asked for
|
||||||
|
(catenary wires, map tracks) never took effect.
|
||||||
|
|
||||||
|
The editor (F11, orthographic view), the reflection pass and the model previews do not use clusters. The
|
||||||
|
editor goes through the first `dynamiclights` headlights, the reflections through the sun only.
|
||||||
|
|
||||||
|
## Statistics and debugging
|
||||||
|
|
||||||
|
- **Shift+F12**, Gfx Renderer: the `lights:` row gives the number of lit headlight sets, how many did not fit,
|
||||||
|
and `clustered` or `first N`. With clusters on, `clusters:` (rebuilding of the grid, which happens when the
|
||||||
|
projection or the draw range change) and `cull:` (assigning lights to clusters, every frame) show the GPU
|
||||||
|
time, and `cluster lists:` the size of the lists and the number of dropped (cluster, light) pairs
|
||||||
|
(`overflow`, with its peak). A non-zero overflow means some cluster held more than 64 lights.
|
||||||
|
- **Light clusters** window (in debug mode, menu *Windows*, item *Headlight config* opens the renderer debug
|
||||||
|
windows): the switch for clustered lights, the heat map, the grid state and the same counters.
|
||||||
|
- `gfx.gldebug yes` turns on the OpenGL debug output (`KHR_debug`); messages go to the log.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
`-DWITH_OPENGL_MODERN=ON` (default) builds this renderer; `WITH_OPENGL_LEGACY` and the GLES / ANGLE options
|
||||||
|
are gone. Linux builds still use `-DWITH_BETTER_RENDERER=OFF`.
|
||||||
|
|
||||||
|
All shaders in `shaders/` can be checked without a graphics card against GLSL 4.60, compute stage included:
|
||||||
|
|
||||||
|
```
|
||||||
|
ci_shadervalidator/build.sh
|
||||||
|
cd ci_shadervalidator && ./validateshaders
|
||||||
|
```
|
||||||
|
|
||||||
|
It needs `g++` with C++20, `glslangValidator` (Debian/Ubuntu: `glslang-tools`) and the `ref/glm` submodule.
|
||||||
|
The same check runs in CI for changes to shaders and the `gl/` wrappers.
|
||||||
@@ -9,6 +9,13 @@ namespace gl
|
|||||||
thread_local static bindable<T>* active;
|
thread_local static bindable<T>* active;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
~bindable()
|
||||||
|
{
|
||||||
|
// deleting an object leaves its address free for the next one, which must not be taken for the bound one
|
||||||
|
if (active == this)
|
||||||
|
active = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void bind()
|
void bind()
|
||||||
{
|
{
|
||||||
if (active == this)
|
if (active == this)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cstring>
|
|
||||||
#include "shader.h"
|
#include "shader.h"
|
||||||
#include "glsl_common.h"
|
#include "glsl_common.h"
|
||||||
#include "utilities/Logs.h"
|
#include "utilities/Logs.h"
|
||||||
|
|||||||
@@ -1198,7 +1198,7 @@ opengl_texture::set_filtering() const
|
|||||||
|
|
||||||
if (Global.AnisotropicFiltering >= 0) {
|
if (Global.AnisotropicFiltering >= 0) {
|
||||||
// anisotropic filtering
|
// anisotropic filtering
|
||||||
::glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, Global.AnisotropicFiltering );
|
::glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, Global.AnisotropicFiltering );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ inline std::vector<glm::vec4> const ndcfrustumshapepoints //
|
|||||||
{-1, 1, 1, 1}, // z-far
|
{-1, 1, 1, 1}, // z-far
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::vector<std::size_t> const frustumshapepoinstorder { 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 };
|
|
||||||
|
|
||||||
// generic frustum class. used to determine if objects are inside current view area
|
// generic frustum class. used to determine if objects are inside current view area
|
||||||
|
|
||||||
class cFrustum {
|
class cFrustum {
|
||||||
|
|||||||
@@ -108,7 +108,6 @@ bool opengl46_renderer::Init(GLFWwindow *Window)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
WriteLog("preparing renderer..");
|
WriteLog("preparing renderer..");
|
||||||
OpenGLMatrices.upload() = false; // set matrix stack in virtual mode
|
|
||||||
|
|
||||||
m_window = Window;
|
m_window = Window;
|
||||||
|
|
||||||
@@ -495,7 +494,7 @@ bool opengl46_renderer::init_viewport(viewport_config &vp)
|
|||||||
vp.main_texv = std::make_unique<opengl_texture>();
|
vp.main_texv = std::make_unique<opengl_texture>();
|
||||||
vp.main_texv->alloc_rendertarget(Global.gfx_postfx_motionblur_format, GL_RG, vp.width, vp.height);
|
vp.main_texv->alloc_rendertarget(Global.gfx_postfx_motionblur_format, GL_RG, vp.width, vp.height);
|
||||||
vp.main_fb->attach(*vp.main_texv, GL_COLOR_ATTACHMENT1);
|
vp.main_fb->attach(*vp.main_texv, GL_COLOR_ATTACHMENT1);
|
||||||
} else
|
}
|
||||||
|
|
||||||
if( !vp.main_fb->is_complete() ) {
|
if( !vp.main_fb->is_complete() ) {
|
||||||
ErrorLog( "main framebuffer setup failed" );
|
ErrorLog( "main framebuffer setup failed" );
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ void bind_for_upload(opengl_texture const &Texture)
|
|||||||
void set_anisotropy(GLenum const Target)
|
void set_anisotropy(GLenum const Target)
|
||||||
{
|
{
|
||||||
if (Global.AnisotropicFiltering >= 0)
|
if (Global.AnisotropicFiltering >= 0)
|
||||||
::glTexParameterf(Target, GL_TEXTURE_MAX_ANISOTROPY_EXT, Global.AnisotropicFiltering);
|
::glTexParameterf(Target, GL_TEXTURE_MAX_ANISOTROPY, Global.AnisotropicFiltering);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -47,53 +47,38 @@ public:
|
|||||||
glm::mat4 const top { m_stack.back() };
|
glm::mat4 const top { m_stack.back() };
|
||||||
m_stack.emplace_back( top ); }
|
m_stack.emplace_back( top ); }
|
||||||
void
|
void
|
||||||
pop_matrix( bool const Upload = true ) {
|
pop_matrix() {
|
||||||
if( m_stack.size() > 1 ) {
|
if( m_stack.size() > 1 ) {
|
||||||
m_stack.pop_back();
|
m_stack.pop_back(); } }
|
||||||
if( Upload ) { upload(); } } }
|
|
||||||
void
|
void
|
||||||
load_identity( bool const Upload = true ) {
|
load_identity() {
|
||||||
m_stack.back() = glm::mat4( 1.f );
|
m_stack.back() = glm::mat4( 1.f ); }
|
||||||
if( Upload ) { upload(); } }
|
|
||||||
void
|
void
|
||||||
load_matrix( glm::mat4 const &Matrix, bool const Upload = true ) {
|
load_matrix( glm::mat4 const &Matrix ) {
|
||||||
m_stack.back() = Matrix;
|
m_stack.back() = Matrix; }
|
||||||
if( Upload ) { upload(); } }
|
|
||||||
void
|
void
|
||||||
rotate( float const Angle, glm::vec3 const &Axis, bool const Upload = true ) {
|
rotate( float const Angle, glm::vec3 const &Axis ) {
|
||||||
m_stack.back() = glm::rotate( m_stack.back(), Angle, Axis );
|
m_stack.back() = glm::rotate( m_stack.back(), Angle, Axis ); }
|
||||||
if( Upload ) { upload(); } }
|
|
||||||
void
|
void
|
||||||
translate( glm::vec3 const &Translation, bool const Upload = true ) {
|
translate( glm::vec3 const &Translation ) {
|
||||||
m_stack.back() = glm::translate( m_stack.back(), Translation );
|
m_stack.back() = glm::translate( m_stack.back(), Translation ); }
|
||||||
if( Upload ) { upload(); } }
|
|
||||||
void
|
void
|
||||||
scale( glm::vec3 const &Scale, bool const Upload = true ) {
|
scale( glm::vec3 const &Scale ) {
|
||||||
m_stack.back() = glm::scale( m_stack.back(), Scale );
|
m_stack.back() = glm::scale( m_stack.back(), Scale ); }
|
||||||
if( Upload ) { upload(); } }
|
|
||||||
void
|
void
|
||||||
multiply( glm::mat4 const &Matrix, bool const Upload = true ) {
|
multiply( glm::mat4 const &Matrix ) {
|
||||||
m_stack.back() *= Matrix;
|
m_stack.back() *= Matrix; }
|
||||||
if( Upload ) { upload(); } }
|
|
||||||
void
|
void
|
||||||
ortho( float const Left, float const Right, float const Bottom, float const Top, float const Znear, float const Zfar, bool const Upload = true ) {
|
ortho( float const Left, float const Right, float const Bottom, float const Top, float const Znear, float const Zfar ) {
|
||||||
m_stack.back() *= glm::ortho( Left, Right, Bottom, Top, Znear, Zfar );
|
m_stack.back() *= glm::ortho( Left, Right, Bottom, Top, Znear, Zfar ); }
|
||||||
if( Upload ) { upload(); } }
|
|
||||||
void
|
void
|
||||||
perspective( float const Fovy, float const Aspect, float const Znear, float const Zfar, bool const Upload = true ) {
|
perspective( float const Fovy, float const Aspect, float const Znear, float const Zfar ) {
|
||||||
m_stack.back() *= glm::perspective( Fovy, Aspect, Znear, Zfar );
|
m_stack.back() *= glm::perspective( Fovy, Aspect, Znear, Zfar ); }
|
||||||
if( Upload ) { upload(); } }
|
|
||||||
void
|
void
|
||||||
look_at( glm::vec3 const &Eye, glm::vec3 const &Center, glm::vec3 const &Up, bool const Upload = true ) {
|
look_at( glm::vec3 const &Eye, glm::vec3 const &Center, glm::vec3 const &Up ) {
|
||||||
m_stack.back() *= glm::lookAt( Eye, Center, Up );
|
m_stack.back() *= glm::lookAt( Eye, Center, Up ); }
|
||||||
if( Upload ) { upload(); } }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// methods:
|
|
||||||
// the core profile has no fixed pipeline matrices to upload to; the renderer passes the matrices to its shaders itself
|
|
||||||
void
|
|
||||||
upload() {}
|
|
||||||
|
|
||||||
// members:
|
// members:
|
||||||
std::vector<glm::mat4> m_stack;
|
std::vector<glm::mat4> m_stack;
|
||||||
};
|
};
|
||||||
@@ -110,9 +95,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// methods:
|
// methods:
|
||||||
bool &
|
|
||||||
upload() {
|
|
||||||
return m_upload; }
|
|
||||||
void
|
void
|
||||||
mode( GLuint const Mode ) {
|
mode( GLuint const Mode ) {
|
||||||
switch( Mode ) {
|
switch( Mode ) {
|
||||||
@@ -133,11 +115,11 @@ public:
|
|||||||
void
|
void
|
||||||
push_matrix() { m_stacks[ m_mode ].push_matrix(); }
|
push_matrix() { m_stacks[ m_mode ].push_matrix(); }
|
||||||
void
|
void
|
||||||
pop_matrix() { m_stacks[ m_mode ].pop_matrix( m_upload ); }
|
pop_matrix() { m_stacks[ m_mode ].pop_matrix(); }
|
||||||
void
|
void
|
||||||
load_identity() { m_stacks[ m_mode ].load_identity( m_upload ); }
|
load_identity() { m_stacks[ m_mode ].load_identity(); }
|
||||||
void
|
void
|
||||||
load_matrix( glm::mat4 const &Matrix ) { m_stacks[ m_mode ].load_matrix( Matrix, m_upload ); }
|
load_matrix( glm::mat4 const &Matrix ) { m_stacks[ m_mode ].load_matrix( Matrix ); }
|
||||||
template <typename Type_>
|
template <typename Type_>
|
||||||
void
|
void
|
||||||
load_matrix( Type_ const *Matrix ) { load_matrix( glm::make_mat4( Matrix ) ); }
|
load_matrix( Type_ const *Matrix ) { load_matrix( glm::make_mat4( Matrix ) ); }
|
||||||
@@ -149,8 +131,7 @@ public:
|
|||||||
glm::vec3(
|
glm::vec3(
|
||||||
static_cast<float>( X ),
|
static_cast<float>( X ),
|
||||||
static_cast<float>( Y ),
|
static_cast<float>( Y ),
|
||||||
static_cast<float>( Z ) ),
|
static_cast<float>( Z ) ) ); }
|
||||||
m_upload ); }
|
|
||||||
template <typename Type_>
|
template <typename Type_>
|
||||||
void
|
void
|
||||||
translate( Type_ const X, Type_ const Y, Type_ const Z ) {
|
translate( Type_ const X, Type_ const Y, Type_ const Z ) {
|
||||||
@@ -158,8 +139,7 @@ public:
|
|||||||
glm::vec3(
|
glm::vec3(
|
||||||
static_cast<float>( X ),
|
static_cast<float>( X ),
|
||||||
static_cast<float>( Y ),
|
static_cast<float>( Y ),
|
||||||
static_cast<float>( Z ) ),
|
static_cast<float>( Z ) ) ); }
|
||||||
m_upload ); }
|
|
||||||
template <typename Type_>
|
template <typename Type_>
|
||||||
void
|
void
|
||||||
scale( Type_ const X, Type_ const Y, Type_ const Z ) {
|
scale( Type_ const X, Type_ const Y, Type_ const Z ) {
|
||||||
@@ -167,14 +147,12 @@ public:
|
|||||||
glm::vec3(
|
glm::vec3(
|
||||||
static_cast<float>( X ),
|
static_cast<float>( X ),
|
||||||
static_cast<float>( Y ),
|
static_cast<float>( Y ),
|
||||||
static_cast<float>( Z ) ),
|
static_cast<float>( Z ) ) ); }
|
||||||
m_upload ); }
|
|
||||||
template <typename Type_>
|
template <typename Type_>
|
||||||
void
|
void
|
||||||
multiply( Type_ const *Matrix ) {
|
multiply( Type_ const *Matrix ) {
|
||||||
m_stacks[ m_mode ].multiply(
|
m_stacks[ m_mode ].multiply(
|
||||||
glm::make_mat4( Matrix ),
|
glm::make_mat4( Matrix ) ); }
|
||||||
m_upload ); }
|
|
||||||
template <typename Type_>
|
template <typename Type_>
|
||||||
void
|
void
|
||||||
ortho( Type_ const Left, Type_ const Right, Type_ const Bottom, Type_ const Top, Type_ const Znear, Type_ const Zfar ) {
|
ortho( Type_ const Left, Type_ const Right, Type_ const Bottom, Type_ const Top, Type_ const Znear, Type_ const Zfar ) {
|
||||||
@@ -184,8 +162,7 @@ public:
|
|||||||
static_cast<float>( Bottom ),
|
static_cast<float>( Bottom ),
|
||||||
static_cast<float>( Top ),
|
static_cast<float>( Top ),
|
||||||
static_cast<float>( Znear ),
|
static_cast<float>( Znear ),
|
||||||
static_cast<float>( Zfar ),
|
static_cast<float>( Zfar ) ); }
|
||||||
m_upload ); }
|
|
||||||
template <typename Type_>
|
template <typename Type_>
|
||||||
void
|
void
|
||||||
perspective( Type_ const Fovy, Type_ const Aspect, Type_ const Znear, Type_ const Zfar ) {
|
perspective( Type_ const Fovy, Type_ const Aspect, Type_ const Znear, Type_ const Zfar ) {
|
||||||
@@ -193,8 +170,7 @@ public:
|
|||||||
static_cast<float>( glm::radians( Fovy ) ),
|
static_cast<float>( glm::radians( Fovy ) ),
|
||||||
static_cast<float>( Aspect ),
|
static_cast<float>( Aspect ),
|
||||||
static_cast<float>( Znear ),
|
static_cast<float>( Znear ),
|
||||||
static_cast<float>( Zfar ),
|
static_cast<float>( Zfar ) ); }
|
||||||
m_upload ); }
|
|
||||||
template <typename Type_>
|
template <typename Type_>
|
||||||
void
|
void
|
||||||
look_at( Type_ const Eyex, Type_ const Eyey, Type_ const Eyez, Type_ const Centerx, Type_ const Centery, Type_ const Centerz, Type_ const Upx, Type_ const Upy, Type_ const Upz ) {
|
look_at( Type_ const Eyex, Type_ const Eyey, Type_ const Eyez, Type_ const Centerx, Type_ const Centery, Type_ const Centerz, Type_ const Upx, Type_ const Upy, Type_ const Upz ) {
|
||||||
@@ -210,14 +186,12 @@ public:
|
|||||||
glm::vec3(
|
glm::vec3(
|
||||||
static_cast<float>( Upx ),
|
static_cast<float>( Upx ),
|
||||||
static_cast<float>( Upy ),
|
static_cast<float>( Upy ),
|
||||||
static_cast<float>( Upz ) ),
|
static_cast<float>( Upz ) ) ); }
|
||||||
m_upload ); }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// members:
|
// members:
|
||||||
stack_mode m_mode{ stack_mode::gl_projection };
|
stack_mode m_mode{ stack_mode::gl_projection };
|
||||||
openglstack_array m_stacks;
|
openglstack_array m_stacks;
|
||||||
bool m_upload { true };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern opengl_matrices OpenGLMatrices;
|
extern opengl_matrices OpenGLMatrices;
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ void screenshot_manager::screenshot_save_thread( char *img, int w, int h )
|
|||||||
|
|
||||||
void screenshot_manager::make_screenshot()
|
void screenshot_manager::make_screenshot()
|
||||||
{
|
{
|
||||||
auto img = new char[Global.fb_size.x * Global.fb_size.y * 4];
|
// rows are tightly packed RGB: the renderer sets GL_PACK_ALIGNMENT to 1
|
||||||
|
auto img = new char[Global.fb_size.x * Global.fb_size.y * 3];
|
||||||
glReadPixels(0, 0, Global.fb_size.x, Global.fb_size.y, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)img);
|
glReadPixels(0, 0, Global.fb_size.x, Global.fb_size.y, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)img);
|
||||||
//m7t: use pbo
|
//m7t: use pbo
|
||||||
|
|
||||||
|
|||||||
@@ -77,8 +77,11 @@ std::mutex logMutex;
|
|||||||
|
|
||||||
void LogService()
|
void LogService()
|
||||||
{
|
{
|
||||||
while (!Global.applicationQuitOrder)
|
bool quitting{false};
|
||||||
|
while (!quitting)
|
||||||
{
|
{
|
||||||
|
// read before the stacks are drained, so everything logged before the quit order still gets written
|
||||||
|
quitting = Global.applicationQuitOrder;
|
||||||
{
|
{
|
||||||
// --- Obsługa InfoStack ---
|
// --- Obsługa InfoStack ---
|
||||||
while (!InfoStack.empty())
|
while (!InfoStack.empty())
|
||||||
@@ -144,7 +147,8 @@ void LogService()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
if (!quitting)
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ ui::map_panel::map_panel() : ui_panel(STR_C("Map"), false)
|
|||||||
window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
|
window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
|
||||||
|
|
||||||
#ifdef WITH_OPENGL_MODERN
|
#ifdef WITH_OPENGL_MODERN
|
||||||
opengl46_renderer *gl33 = dynamic_cast<opengl46_renderer*>(GfxRenderer.get());
|
opengl46_renderer *renderer = dynamic_cast<opengl46_renderer*>(GfxRenderer.get());
|
||||||
if (!gl33) {
|
if (!renderer) {
|
||||||
ErrorLog("map not supported on old renderer");
|
ErrorLog("map not supported on old renderer");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -137,27 +137,27 @@ void ui::map_panel::render_map_texture(glm::mat4 transform, glm::vec2 surface_si
|
|||||||
scene_ubo->bind_uniform();
|
scene_ubo->bind_uniform();
|
||||||
|
|
||||||
#ifdef WITH_OPENGL_MODERN
|
#ifdef WITH_OPENGL_MODERN
|
||||||
opengl46_renderer *gl33 = dynamic_cast<opengl46_renderer*>(GfxRenderer.get());
|
opengl46_renderer *renderer = dynamic_cast<opengl46_renderer*>(GfxRenderer.get());
|
||||||
|
|
||||||
gl33->Draw_Geometry(m_section_handles.begin(), m_section_handles.end());
|
renderer->Draw_Geometry(m_section_handles.begin(), m_section_handles.end());
|
||||||
|
|
||||||
scene_ubs.cascade_end = glm::vec4(0.7f, 0.7f, 0.0f, 0.0f);
|
scene_ubs.cascade_end = glm::vec4(0.7f, 0.7f, 0.0f, 0.0f);
|
||||||
scene_ubo->update(scene_ubs);
|
scene_ubo->update(scene_ubs);
|
||||||
gl33->Draw_Geometry(m_colored_paths.future.begin(), m_colored_paths.future.end());
|
renderer->Draw_Geometry(m_colored_paths.future.begin(), m_colored_paths.future.end());
|
||||||
|
|
||||||
scene_ubs.cascade_end = glm::vec4(0.0f, 1.0f, 0.0f, 0.0f);
|
scene_ubs.cascade_end = glm::vec4(0.0f, 1.0f, 0.0f, 0.0f);
|
||||||
scene_ubo->update(scene_ubs);
|
scene_ubo->update(scene_ubs);
|
||||||
gl33->Draw_Geometry(m_colored_paths.switches.begin(), m_colored_paths.switches.end());
|
renderer->Draw_Geometry(m_colored_paths.switches.begin(), m_colored_paths.switches.end());
|
||||||
|
|
||||||
scene_ubs.cascade_end = glm::vec4(1.0f, 0.0f, 0.0f, 0.0f);
|
scene_ubs.cascade_end = glm::vec4(1.0f, 0.0f, 0.0f, 0.0f);
|
||||||
scene_ubo->update(scene_ubs);
|
scene_ubo->update(scene_ubs);
|
||||||
gl33->Draw_Geometry(m_colored_paths.occupied.begin(), m_colored_paths.occupied.end());
|
renderer->Draw_Geometry(m_colored_paths.occupied.begin(), m_colored_paths.occupied.end());
|
||||||
|
|
||||||
scene_ubs.cascade_end = glm::vec4(0.3f, 0.3f, 1.0f, 0.0f);
|
scene_ubs.cascade_end = glm::vec4(0.3f, 0.3f, 1.0f, 0.0f);
|
||||||
scene_ubo->update(scene_ubs);
|
scene_ubo->update(scene_ubs);
|
||||||
gl33->Draw_Geometry(m_colored_paths.highlighted.begin(), m_colored_paths.highlighted.end());
|
renderer->Draw_Geometry(m_colored_paths.highlighted.begin(), m_colored_paths.highlighted.end());
|
||||||
|
|
||||||
gl33->Bind_Texture(0, m_icon_atlas);
|
renderer->Bind_Texture(0, m_icon_atlas);
|
||||||
m_poi_shader->bind();
|
m_poi_shader->bind();
|
||||||
scene_ubs.cascade_end = glm::vec4(1.0f / (surface_size / 200.0f), 1.0f, 0.0f);
|
scene_ubs.cascade_end = glm::vec4(1.0f / (surface_size / 200.0f), 1.0f, 0.0f);
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ void ui::map_panel::render_map_texture(glm::mat4 transform, glm::vec2 surface_si
|
|||||||
}
|
}
|
||||||
|
|
||||||
scene_ubo->update(scene_ubs);
|
scene_ubo->update(scene_ubs);
|
||||||
gl33->Draw_Geometry(simulation::Region->get_map_poi_geometry());
|
renderer->Draw_Geometry(simulation::Region->get_map_poi_geometry());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (Global.iMultisampling)
|
if (Global.iMultisampling)
|
||||||
|
|||||||
Reference in New Issue
Block a user