17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-11 01:39:41 +01:00
Commit Graph

123 Commits

Author SHA1 Message Date
d89b7dddf0 Merge branch 'master' into feat/renderers-cleanup 2026-11-07 22:12:02 +01:00
dccb26b77a Merge pull request #234 from dos1/tristate
Train: Handle 3-way light switches in headlighttoggle actions
2026-11-07 20:24:19 +00:00
cd52ec8799 Merge pull request #259 from MaSzyna-EU07/fix/sm03-engine-shutdown
fix(cab): a diesel with only the starter button stops its engine with the main switch off command (eu07/maszyna#427)
2026-11-07 20:23:21 +00:00
pwarszawski
04b208bbaf Merge branch 'fix/est3-valve' into feat/ai-braking-earlier-steps 2026-11-07 21:17:12 +01:00
pwarszawski
71d4645e09 Merge remote-tracking branch 'origin/master' into fix/est3-valve 2026-11-07 21:17:04 +01:00
pwarszawski
8d1d9eec9b AI braking: smaller steps, earlier, while approaching a stop or a lower speed
Changes to how the AI works the train brake on the way to a lower speed
or a stop, so that it uses few, early applications instead of a late
deep one:

- Approaching a lower speed, the deceleration may fall short of the
  demand by only half of a step (fBrake_a1) before the brake is applied
  harder. With a full step the demand crept up meanwhile and a long G
  train got its next step many seconds too late.
- The shortfall is checked every 3 s. If it keeps growing at a handle
  left alone for longer than the brake takes to respond, the AI adds a
  small step at once instead of waiting until the margin calls for a
  deep one. A shortfall that shrinks on its own (brake still filling,
  blocks gripping harder at lower speed) does not trigger it.
- Releasing, a position short of the first one counts as giving the
  minimum stage of the distributor, not less.
- Holding the speed on a descent, the predicted excess speed is taken
  off over the time the brake takes to respond, not at once, so a slow
  brake does not get a deep application that then takes as long to
  release.
2026-11-07 18:10:47 +01:00
d4120ea5db Merge branch 'master' into feat/renderers-cleanup 2026-11-07 16:44:51 +01:00
pwarszawski
439e892abd AI: use the locomotive releaser only when starting off
The rule "wyluzuj lokomotywe, to szybciej ruszymy" released the
locomotive's own brake whenever power was asked for and the locomotive
cylinder held more than 0.4 bar. It had no speed condition, so it also
fired in motion, at 50-75 km/h, right after an application. On a
locomotive with the EStED distributor (E186) this vented the control
reservoir; with the pipe not yet recharged the locomotive brake then
faded to nothing at the next application.

The rule is meant for starting off, so it now applies only below
1 km/h.
2026-11-07 15:35:35 +01:00
917c472cc9 Merge pull request #257 from MaSzyna-EU07/fix/vehicle-removal-smoke
fix(gfx): a vehicle taken off the tracks stops smoking (#90)
2026-11-07 13:28:31 +00:00
jerrrrycho
7e426192b7 fix(cab): a diesel with only the starter button stops its engine with the main switch off command (eu07/maszyna#427)
The SM03 had no way to stop its engine. Its fuel pump starts on its own, so the fuel pump switch is ignored; the
engine runs the converter feeding the low voltage circuit, so the battery switch doesn't starve the fuel pump either;
and its cab has only the starter button, for which TTrain::OnCommand_linebreakeropen() kept a legacy branch which
animated the button and returned without opening the circuit. MainSwitch(false) itself stops the engine fine.

The command now opens the circuit on diesels whose cab has only the starter button
(TMoverParameters::MainCircuitOpensWithStarterOnly()), without animating the button, which held down would start
the engine again. Other vehicles with only the button closing the circuit keep ignoring the command.

Tests: tests/unit/test_engine_shutdown.cpp, on a shunter made after the SM03.
2026-11-07 14:06:38 +01:00
Sebastian Krzyszkowiak
4a256477e0 Train: Handle 3-way light switches in headlighttoggle actions
This lets them be fully operated with mouse and VR pointers.
2026-11-07 13:37:27 +01:00
3f6a10f1fe Merge pull request #237 from MaSzyna-EU07/feat/track-uneven
Feat/track uneven
2026-11-07 11:18:49 +00:00
jerrrrycho
4489dd7469 test(gfx): unit tests of the smoke of a vehicle taken off the tracks (#90)
The spawn count of the smoke source of a vehicle moves out of
smoke_source::update() into smoke::vehicle_spawn_count() (vehicle/smoke.h),
a free function on the mover state, which joins the core library so the
unit tests can call it the way the source does. The rates are the same as
before, the steady rate of the source still counted in before the share of
the engine.

Tests: tests/unit/test_smoke.cpp. A vehicle taken off the tracks spawns no
smoke and drops the pending particles, with the engine starting, idling or
under load; a road vehicle back from the pool puffs again; the engine adds
to the steady rate; the pending count stays within the room of the source.
Two of the four fail without the fix.

Checked again on td.scn with the SM42 driven into a portal: the smoke
source keeps puffing on the run and drops to 0 particles once the vehicle
is taken off the tracks.
2026-11-07 11:38:49 +01:00
jerrrrycho
f94b308062 fix(sound): check for the player train in the outer noise test of a vehicle
The test whether a vehicle belongs to the player train dereferenced
simulation::Train without a null check (commented out long ago). The
driver mode switches to the free camera in the same frame the player train
goes through a portal, before the sounds are updated, so it isn't known to
crash, but nothing else guards it.
2026-11-07 09:37:40 +01:00
pwarszawski
18bc7e06bf AI braking: move the uphill power-off check into its own function
The condition added for holding the speed uphill near a stop pushed the
cognitive complexity of control_tractive_force() to 26, over the 25
SonarCloud allows. It now lives in is_too_fast_uphill(); the behaviour
is unchanged.
2026-11-02 16:43:16 +01:00
pwarszawski
1f3cf4fe56 AI braking: don't let the running brake test override a stop ahead
adjust_desired_speed_for_braking_test runs after the target speed was
worked out and overwrites AccDesired. In its third phase it holds the
deceleration near fAccThreshold until the speed has dropped by 8 km/h.
On a 20 per mille descent that application barely holds the speed, so
the phase can last minutes, and a stop signal coming up meanwhile is
ignored. In one real-time run an E186 with 19 container wagons started
the test 1290 m before a stop signal, lost 7 km/h by the signal and
overran it by 274 m after the emergency application.

While the test runs the AI now uses whichever of the two asks for the
harder braking: the test may brake harder than the route needs, never
softer.

Measured in real time, four runs of 32 consists each on a 20 per mille
descent, on the flat and on the descent with half the rail adhesion:
the test no longer runs within 600 m of a stop signal (before: down to
414 m on this branch, and 14 m with the overrun above in a build with
other brake changes), no overruns, stops 8-15 m short of the signal 86,
117 and 83 of 128 (before 90, 117 and 78).
2026-11-02 16:02:14 +01:00
pwarszawski
6b82f1471a AI braking: take power off when holding the speed uphill near a stop
Within fMaxProximityDist of a stop, going uphill, the AI holds its speed
with AccDesired = min(0, AccPreferred). control_tractive_force only cut
the power uphill when AccDesired < 0, so an AccDesired of exactly 0 kept
the controller where it was. A consist that came into that range under
power kept full power and crept up to the signal.

Holding the speed uphill on the way to a stop now asks for no speeding
up: power comes off once the train is faster than AccDesired allows.

With the current brake valves this rarely shows: on a 10 per mille climb,
four real-time runs of 32 consists each, the E186 with 25 empty 408W drew
power in 0-14 samples of the last 50 m before and 0-8 after, no overruns
either way. It surfaced once the AI predicted the ESt3 brake closer to
what the valve delivers, where the same train crept to 12 km/h from 40 m
out and overran the signal; with this change it stopped short in all runs.
2026-11-02 16:02:06 +01:00
maj00r
a68c535be9 fix(track): the cab keeps the shake a track doesn't replace, and the editor redraws all that an edit changes
The side sway from the lateral acceleration now goes off only on a track with a sideways value, and the random jolts on one with a vertical or a sideways value. Dips at the rail joints and cross level alone replace neither. On a switch it goes by the path set and what its joined tracks allow.

An edit in the editor also rebuilds the other switches of a joined group with the tracks they meet, and the tracks of a rail run whose joints moved. Removing a track does the same. While dragging, the tracks further off wait until the drag ends.

The clatter of a bogie's axles was right: the followers stand at the pivots and the offsets are the places of the wheel sets. The misleading name goes.

Also what Sonar had left: a parameter as a span, a const method, a struct and its member apart, commented out code, and a function of the grading tool split, with the same output.
2026-11-01 20:12:33 +01:00
jerrrrycho
d995674bee fix(physics): automatic couplers don't catch above the safe collision… (#217)
* fix: automatic coupler crashes, horns, compressor start delay, radio and cab lever sounds

fix(cab): the line contactors lamp goes out with the pressure switch (eu07/maszyna#470)

The lamp of the open line contactors, which tells the start can be made, went out at 1 bar in the brake
cylinders, while the pressure switch cuts the start off only at 2 bar (or below 3.6 bar in the main pipe), so
with the brakes partly applied the start could be made with the lamp out. With a pressure switch fitted the lamp
now follows it; without one it goes out at 1 bar as before. The same holds for the lamp of the second unit, which
used to keep its last state between 1 bar and the cut-off. The condition is
TMoverParameters::LineContactorsLampLit().

Tests: tests/unit/test_line_contactors.cpp.

refactor: Sonar findings of PR 217

- horn_supply::pneumatic shadowed the pneumatic enumerator of the sound flags (cpp:S1117), it's horn_supply::air;
  the supplies read with using enum (S6177),
- CouplerForce(): the automatic coupling refused or caught without another level of nested ifs (S134), the
  same outcome: too fast, the coupling is locked; caught, coupled and locked; otherwise left to try again,
- CompressorCheck(): the power the compressor doesn't draw yet noted without a TODO (S1135),
- tests: the previous crash damage setting in an in-class initializer (S3230), the loops which stop early with
  break rather than in their conditions (S886), the stand-ins of test::pair in a std::vector rather than an array
  (S5945), units in the comments of the example without /* */ (S1103).

test: the cases the changes of the branch left without a test

- Cntrl.: BDelay1 to BDelay4 reach the brake delays (now through std::format), the default start types of the
  ground and converter overload relays of a multiple unit and of other vehicles (out of the nested conditionals),
- the compressor: fed from the low voltage it waits from the low voltage coming on; the pressure switch stops it
  above the cut-out pressure, without one the excess air is vented (the vented fraction rewritten),
- the horns: a vehicle without any air sounds as before, the threshold pressure, an electric horn on 110 V,
- the positions of a cab control below zero, and a control without position sounds.

refactor(cab): the kind of the train heating switch as a flag

Read once with the vehicle definition, rather than compared as text in each of the switch commands.

feat(cab): train heating switch springing back (MaSzyna-EU07/maszyna#85)

TrainHeating=[toggle]|impulse in the Switches: section of the vehicle definition makes the train heating switch
an unstable one: it goes up to switch the heating on, down to switch it off, and back to the middle once let go,
instead of staying where it was put. The position of such a switch isn't set from the heating state when the cab
is entered.

Tests: tests/unit/test_switches.cpp.

feat(sound): mirror sounds (MaSzyna-EU07/maszyna#84, eu07/maszyna#322)

The sound definitions of a vehicle (.mmd) take mirroropen: and mirrorclose:, played as a mirror sets off
unfolding or folding, and the locations section mirrors: <offset>... end, the offsets along the vehicle of the
mirror pairs, as for the doors (negative ahead of the centre), each pair at the sides of the vehicle. As the
animation, only the mirrors at the occupied cab sound. The start of a move is mirror_moves::motion in
vehicle/mirrors.h.

Tests: tests/unit/test_mirrors.cpp.

feat(cab): joint oil and fuel pump switch (MaSzyna-EU07/maszyna#81)

A cab control oilfuelpump_sw: switches both the oil and the fuel pump at once, with the new command
oilfuelpumptoggle (no key by default). As a two-state switch it turns both pumps off when both are on and both on
otherwise; as an impulse switch it's held to start them, like the separate pump switches. Its position follows
the pumps when the cab is entered.

refactor(editor): text fields filled without strncpy (MaSzyna-EU07/maszyna#112)

The last three strncpy calls Sonar flags (cpp:S5816), in the road and track editors, copy the text into the
ImGui field buffers with std::string::copy and terminate it right after; the text gets cut to the buffer as
before.

fix: automatic coupler crashes, horns, compressor start delay, radio and cab lever sounds

- automatic couplers don't catch above the safe collision speed (#86): with crash damage on they couple at a
  closing speed of 15 km/h at most, a faster hit locks the coupling until the vehicles part and the collision
  runs its course. On the way: CollisionVelocity() read a head-on 10 + 10 km/h as 14.1 km/h (quaternions turn
  by half the angle), it's now taken from the touching ends; CollisionDetect() took the other vehicle's speed
  the wrong way round for vehicles facing each other. Head-on hits now derail sooner, at the intended limit,
- a single horn lever can't sound both tones at once (eu07/maszyna#392),
- a skid ends once the vehicle stops on locked wheels (eu07/maszyna#479),
- electronic destination signs are blanked at the end of the timetable (eu07/maszyna#446),
- horns need the air or the voltage to sound (eu07/maszyna#290, #76): HornType=[Pneumatic]|Electric and
  Horn3Type in Cntrl.; a pneumatic horn needs 0.2 MPa in the main reservoir (or the main pipe without one),
  an electric one the low voltage; the lever stays where it was put,
- compressor start delay (#67): CompressorStartDelay [s] in Brake., counted from the compressor getting its
  power, after the engine starts say; none by default,
- the channel and volume buttons of a radio without power do nothing (eu07/maszyna#429); the power check of
  the radio is TMoverParameters::RadioPowered(),
- a lever moved smoothly sounds the positions it passes (#83).

Sonar findings in the functions changed: nesting, nested conditionals, commented-out code, std::format,
references to const. Unit tests: test::pair for two vehicles running into each other, test_couplers,
test_horn, test_wheelslip, test_compressor, test_radio, test_gauge_positions.

* fix: review findings of PR 217

- the pressures of the mover are in bar (MinCP=7.0 in the vehicle definitions), so the 0.2 the pneumatic horns
  needed was 0.2 bar; it's 2 bar now, and the tests take their pressures in bar too,
- the lamps of the ground relay and of the motor overload relays follow the pressure switch as the line
  contactors one does, TMoverParameters::MainCircuitLampsEnabled(), so the lamps of one cab go out together,
- the joint oil and fuel pump switch leaves out a pump started automatically, which ignores the switching:
  with one, it couldn't turn the other off, and showed off with both running (OilFuelPumpsEnabled()),
- the kind of the train heating switch comes from the occupied vehicle on entering the cab too, as in the
  switch commands; in a multiple unit these are different vehicles,
- CouplerForce() put the force taken back by the other vehicle on its far coupler when the vehicles faced each
  other (Couplers[1 - End] rather than the coupler it's coupled with),
- the velocity difference along the coupler axis computed once, CouplerVelocityDifference(), for CouplerForce()
  and CollisionVelocity(),
- the attachcoupler flag added without the SetFlag() call with 0, the index of the mirror moves noted as by side.

Tests: the coupler force of vehicles facing each other, the joint switch with an automatic pump, the lamps of
the main circuit, the horn and compressor pressures in bar.

* fix: coupler force of vehicles facing each other taken back along the other vehicle's axis

CForce runs along the axis of its own vehicle, so the force the other vehicle takes back on its coupler of the pair
is turned around once more when the vehicles face each other (-CF * DirPatch()), as that vehicle computes it itself.

Tests: the coupler forces of the pair facing each other and facing the same way.
2026-11-01 09:12:36 +01:00
maj00r
9364b03e1a fix(track): what Sonar reports on the unevenness
The blockers are in the EP brake of a vehicle, older than this work: its
arrays made with new[] are vectors now, which also removes the deletes.

- The caches of the unevenness of a track are no longer mutable: what fills
  them isn't const any more.
- TSegment gets back to RaPositionGet in one piece, with what the path has
  to do with the others in one struct.
- Less nesting in the group of switches and in the fields of the editor,
  nested conditionals written out, and the attribute is read from a
  string_view. A test no longer counts in floating point.

No change in behaviour.
2026-10-31 20:17:47 +01:00
1ad6d8b2bd Merge remote-tracking branch 'origin/feat/track-uneven' into feat/renderers-cleanup 2026-10-31 19:52:29 +01:00
154c42cb66 Merge remote-tracking branch 'origin/feat/track-uneven' into feat/renderers-cleanup 2026-10-31 19:13:23 +01:00
maj00r
2b8d7d7cf3 refactor(track): what goes with unevenness in one place, readable
The rules stay in TrackUneven.h with no dependence on the engine, as they can
be tested. Their use on the tracks moves out of Track.cpp to TrackUneven.cpp,
split into steps: the group of switches, the values of its paths, the meeting
of two ends. One set of helpers for the paths of a track takes the place of
the same conditions written out in several places, and the value at the ends
of a switch is worked out in one place. The clatter of an axle leaves the long
Update of a vehicle. The fields in the editor say themselves when they show.

No change in behaviour.
2026-10-31 19:05:56 +01:00
maj00r
371de52342 fix(track): what the review of unevenness found in the engine
- A path of no length gave a joint with a vector across of NaN to the path
  joined to it, on any scenery, with unevenness or not. It now has no
  vector across, and so makes no joint.
- Axles no longer stray along the track. It moved vehicles against each
  other, changing the distance their couplers go by.
- The bend under an axle goes straight on past the ends of a path: the
  polynomial ran off on a short curved one, with a kick in the cab shake.
- A track left out of its rail run, where tracks are joined one way only,
  no longer walks the whole run again on every call.
- The clatter of an axle goes by the outer axle on its side of the vehicle,
  and not always by the same one, as the two can be on different tracks.
- A vertex works out the amounts once, and the foot of the ballast not at all.
2026-10-31 19:02:55 +01:00
24b805b348 Merge remote-tracking branch 'origin/feat/track-uneven' into feat/renderers-cleanup 2026-10-31 18:53:48 +01:00
maj00r
7aff1ee9ff feat(vehicle): an uneven track shakes the cab by itself
On such a track the sway drawn from the lateral acceleration, that is
from the curve radius, and the jolts drawn at random are off. Tracks
with no unevenness shake the cab as before. Hunting stays: it goes by the
vehicle and its speed, not by the track.
2026-10-31 18:29:22 +01:00
maj00r
b2dcb8a42e fix(vehicle): wheel clatter on an uneven track doesn't loop into a buzz
The phase of the rails was wrapped to the rail length of the track the
vehicle is on, while an axle can be on a track with longer rails. From
then on every frame looked like a joint crossed.
2026-10-31 18:21:28 +01:00
maj00r
e5266e1e6b feat(track): track unevenness declared in the track entry
Usage, optional attributes of a track entry, before endtrack:

  uneven <mm>       peak deviation of a rail on a 10 m chord, 0-100; normal and switch
  unevenup <mm>     up and down alone, instead of uneven; normal and switch
  unevenside <mm>   sideways alone, instead of uneven; normal and switch
  unevencross <mm>  cross level alone, instead of uneven; normal only
  jointdip <mm>     depth of the dip at rail joints, every sound distance; normal only,
                    default 0.4 of the up and down component

Values of uneven follow PLK Id-14 appendix 1: 6 mm is the limit at 160 km/h, 10 at 120,
18 at 80, 35 at 40; half of it for a track in a good state. Example:

  uneven 18
  unevencross 25
  jointdip 6
  endtrack

The same fields are in the editor, track panel, section Ride and sound. See
track_uneven_doc.md.

The unevenness is a displacement field over the plan of the scenery, with no random
numbers: one place strays the same every time, and joined tracks, the paths of a switch,
sleepermodel and axles at one place move together. Tracks meet at a joint at the lower of
their values. Joined switches take the lowest of theirs, the tracks joined to them come to
it, and a track with none of a component keeps the switch to its design in it. The clatter
comes from the dipping joints. Curve radius and grade for the physics stay as designed.

Tracks with it get geometry every 2 m on straights: 4x the vertices on Baltyk with all
tracks flagged, 0.2 ms more per frame on td.scn.
2026-10-31 15:47:10 +01:00
55372571b3 Merge branch 'master' into feat/renderers-cleanup 2026-10-29 20:42:39 +01:00
1447dcf075 Walk the shadow passes and the culled model groups on worker threads in the Vulkan renderer 2026-10-27 20:56:11 +01:00
550d47e97c Merge remote-tracking branch 'jano211/siec-radio' into feat/renderers-cleanup 2026-10-26 16:36:36 +01:00
3e2d292709 perf(gpucull): keep the walks of the submodel trees of the instances from frame to frame and make them again only when the model changes (gfx.gpucull.walkcache) 2026-10-24 01:01:43 +02:00
1a42009dc2 perf(physics): performance.multithreading switches the work over many threads, which each job sizes by how much it has: the physics steps a thread per 128 vehicle steps, up to all logical cores 2026-10-23 21:07:05 +02:00
9f7217256b fix(physics): the external program messages (multiplayer) no longer keep the physics steps in one thread, only network sessions do; the debug panel tells what keeps them in one 2026-10-23 20:12:02 +02:00
60667588b5 perf(physics): the steps of the vehicle physics on many threads, in groups of vehicles which act on one another (performance.cpucores, 0: all cores) 2026-10-23 19:56:36 +02:00
f17462b186 perf(physics): road vehicles farther than 1 km from the camera go by one step of the physics in a frame (physics.roadlod); count what resets the switching time of the vehicles (compressor, mains, diesel, physics woken) in the debug panel 2026-10-23 18:47:01 +02:00
a04efbf968 perf(physics): vehicles standing still and held by their brakes let their physics sleep (physics.sleepheld) 2026-10-23 18:20:49 +02:00
e054fe900d perf(debug): count what keeps the physics on in rail vehicles standing still (active cab, speed, acceleration, switching, ezt/dmu, moving neighbour), for the debug panel 2026-10-23 18:06:12 +02:00
0aa1e2d4b6 perf(debug): forces and update of the vehicles by kind in the debug panel: rail and road, within 1 km of the camera and farther, with the counts of vehicles and of the ones standing still 2026-10-23 17:54:05 +02:00
5a53c32b0a perf(debug): forces and update of the vehicles by kind in the debug panel: rail and road, within 1 km of the camera and farther, with the counts of vehicles and of the ones standing still 2026-10-23 17:51:15 +02:00
52df707f30 perf(debug): parts of the vehicle update in the debug panel: scenery animations, power grid, roads, preparation, forces, vehicle update and ai, with the counts of vehicles and physics steps; input and the start of the ui frame timed apart 2026-10-23 17:26:44 +02:00
8fbf056749 fix(ai): a road order holding the vehicle keeps the road orders past it from telling the vehicle to drive on 2026-10-22 18:01:04 +02:00
f4f8c75e49 fix(ai): orders of road junctions and level crossings measured along the lane they lie on, so they aren't misplaced on short lanes 2026-10-22 17:16:33 +02:00
pwarszawski
8112733e0d Merge remote-tracking branch 'origin/master' into fix187
# Conflicts:
#	McZapkie/Oerlikon_ESt.h
#	McZapkie/hamulce.h
2026-10-22 07:08:29 +02:00
40a4267bd3 Merge pull request #189 from pwarszawski/fix/ai-ready-released-brakes
AI: don't cut the power for a brake already released below 0.4 bar
2026-10-21 09:00:34 +02:00
918c056b83 Merge branch 'master' into feat/gl46-migration 2026-10-17 20:57:15 +02:00
IwanXXI
b4b1e6c32f Broadcast radio receiver in the cab
Plays internet radio streams (http, mp3) through a positional emitter in the cab, with analog tuning, inter-station noise and two speaker profiles. Stations are listed in fmradio.txt, cab controls are defined with fmradio entries in the .mmd file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 00:14:32 +02:00
jerrrrycho
c7f9796387 fix(ai): release horn when human driver takes over from AI (#210)
* fix(ai): release what the AI was holding when the player takes over

* fix(ai): keep the light selector lookup within its table, Sonar findings of the handover

- the light selector size comes from the vehicle definition (LightsList Size)
  and isn't limited, while the table holds 18 positions; a bigger selector
  read past the table, and so did a table index other than the front or
  rear one. Only the positions the table holds are looked at now, and the
  selector stays where it is for a bad table index (cpp:S2259),
- the lambdas of the handover capture what they use explicitly (cpp:S3608),
- the chained assignments resetting the headlight state on the AI taking
  over are split (cpp:S1121).
2026-10-16 14:44:30 +02:00
61bfc6eb1a Move modern OpenGL renderer to rendering/opengl46 and rename classes 2026-10-15 01:12:05 +02:00
3d3eef2e1c Remove legacy OpenGL 2.1 renderer 2026-10-15 00:06:31 +02:00