Refactor rendering architecture and replace rasterizer with renderer

- Introduced SoftwareRenderer as a pixel-accurate software rendering backend.
- Removed the obsolete wolf_3d_rasterizer.dart file.
- Created a new wolf_3d_renderer.dart file to centralize rendering exports.
- Updated tests to accommodate the new rendering structure, including pushwall and projection sampling tests.
- Modified the WolfAsciiRenderer and WolfFlutterRenderer to utilize the new SoftwareRenderer.
- Enhanced enemy spawn tests to include new enemy states.

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-19 11:38:07 +01:00
parent ac6edb030e
commit 786ba4b450
22 changed files with 952 additions and 684 deletions

View File

@@ -6,9 +6,9 @@ import 'dart:io';
import 'package:wolf_3d_dart/wolf_3d_engine.dart';
import 'package:wolf_3d_dart/wolf_3d_input.dart';
import 'package:wolf_3d_dart/wolf_3d_rasterizer.dart';
import 'package:wolf_3d_dart/wolf_3d_renderer.dart';
/// Runs the Wolf3D engine inside a terminal using CLI-specific rasterizers.
/// Runs the Wolf3D engine inside a terminal using CLI-specific renderers.
///
/// The loop owns raw-stdin handling, renderer switching, terminal size checks,
/// and frame pacing. It expects [engine.input] to be a [CliInput] instance so
@@ -25,22 +25,22 @@ class CliGameLoop {
'CliGameLoop requires a CliInput instance.',
),
primaryRasterizer = AsciiRasterizer(
mode: AsciiRasterizerMode.terminalAnsi,
primaryRenderer = AsciiRenderer(
mode: AsciiRendererMode.terminalAnsi,
),
secondaryRasterizer = SixelRasterizer() {
_rasterizer = primaryRasterizer;
secondaryRenderer = SixelRenderer() {
_renderer = primaryRenderer;
}
final WolfEngine engine;
final CliRasterizer primaryRasterizer;
final CliRasterizer secondaryRasterizer;
final CliRendererBackend primaryRenderer;
final CliRendererBackend secondaryRenderer;
final CliInput input;
final void Function(int code) onExit;
final Stopwatch _stopwatch = Stopwatch();
final Stream<List<int>> _stdinStream = stdin.asBroadcastStream();
late CliRasterizer _rasterizer;
late CliRendererBackend _renderer;
StreamSubscription<List<int>>? _stdinSubscription;
Timer? _timer;
bool _isRunning = false;
@@ -52,9 +52,9 @@ class CliGameLoop {
return;
}
if (primaryRasterizer is SixelRasterizer) {
final sixel = primaryRasterizer as SixelRasterizer;
sixel.isSixelSupported = await SixelRasterizer.checkTerminalSixelSupport(
if (primaryRenderer is SixelRenderer) {
final sixel = primaryRenderer as SixelRenderer;
sixel.isSixelSupported = await SixelRenderer.checkTerminalSixelSupport(
inputStream: _stdinStream,
);
}
@@ -116,11 +116,11 @@ class CliGameLoop {
}
if (bytes.contains(9)) {
// Tab swaps between rasterizers so renderer debugging stays available
// Tab swaps between renderers so renderer debugging stays available
// without restarting the process.
_rasterizer = identical(_rasterizer, secondaryRasterizer)
? primaryRasterizer
: secondaryRasterizer;
_renderer = identical(_renderer, secondaryRenderer)
? primaryRenderer
: secondaryRenderer;
stdout.write('\x1b[2J\x1b[H');
return;
}
@@ -136,7 +136,7 @@ class CliGameLoop {
if (stdout.hasTerminal) {
final int cols = stdout.terminalColumns;
final int rows = stdout.terminalLines;
if (!_rasterizer.prepareTerminalFrame(
if (!_renderer.prepareTerminalFrame(
engine,
columns: cols,
rows: rows,
@@ -145,7 +145,7 @@ class CliGameLoop {
// game does not keep advancing while the user resizes the terminal.
stdout.write('\x1b[2J\x1b[H');
stdout.write(
_rasterizer.buildTerminalSizeWarning(columns: cols, rows: rows),
_renderer.buildTerminalSizeWarning(columns: cols, rows: rows),
);
_lastTick = _stopwatch.elapsed;
@@ -160,6 +160,6 @@ class CliGameLoop {
stdout.write('\x1b[H');
engine.tick(elapsed);
stdout.write(_rasterizer.render(engine));
stdout.write(_renderer.render(engine));
}
}