Added colors and textures

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-13 16:46:20 +01:00
parent 8ecc8e2fd4
commit 464a22e1f3
8 changed files with 388 additions and 66 deletions

View File

@@ -1 +1,4 @@
include: package:flutter_lints/flutter.yaml
formatter:
trailing_commas: preserve

BIN
assets/VSWAP.WL1 Normal file

Binary file not shown.

View File

@@ -0,0 +1,42 @@
import 'dart:typed_data';
import 'package:wolf_dart/classes/matrix.dart';
class VswapParser {
/// Extracts the 64x64 wall textures from VSWAP.WL1
static List<Matrix<int>> parseWalls(ByteData vswap) {
// 1. Read Header
int chunks = vswap.getUint16(0, Endian.little);
int spriteStart = vswap.getUint16(2, Endian.little);
// int soundStart = vswap.getUint16(4, Endian.little); // We don't need this yet
// 2. Read Offsets (Where does each chunk start in the file?)
List<int> offsets = [];
for (int i = 0; i < spriteStart; i++) {
offsets.add(vswap.getUint32(6 + (i * 4), Endian.little));
}
// 3. Extract the Wall Textures
List<List<List<int>>> textures = [];
// Walls are chunks 0 through (spriteStart - 1)
for (int i = 0; i < spriteStart; i++) {
int offset = offsets[i];
if (offset == 0) continue; // Empty chunk
// Walls are always exactly 64x64 pixels (4096 bytes)
// Note: Wolf3D stores pixels in COLUMN-MAJOR order (Top to bottom, then left to right)
List<List<int>> texture = List.generate(64, (_) => List.filled(64, 0));
for (int x = 0; x < 64; x++) {
for (int y = 0; y < 64; y++) {
int byteIndex = offset + (x * 64) + y;
texture[x][y] = vswap.getUint8(byteIndex);
}
}
textures.add(texture);
}
return textures;
}
}

View File

@@ -1,24 +1,29 @@
import 'package:flutter/services.dart';
import 'package:wolf_dart/classes/matrix.dart';
import 'package:wolf_dart/features/map/vswap_parser.dart';
import 'package:wolf_dart/features/map/wolf_level.dart';
import 'package:wolf_dart/features/map/wolf_map_parser.dart';
class WolfMap {
/// The fully parsed and decompressed levels from the game files.
final List<WolfLevel> levels;
final List<Matrix<int>> textures;
// A private constructor so we can only instantiate this from the async loader
WolfMap._(this.levels);
WolfMap._(this.levels, this.textures);
/// Asynchronously loads the map files and parses them into a new WolfMap instance.
static Future<WolfMap> load() async {
// 1. Load the binary data
final mapHead = await rootBundle.load("assets/MAPHEAD.WL1");
final gameMaps = await rootBundle.load("assets/GAMEMAPS.WL1");
final vswap = await rootBundle.load("assets/VSWAP.WL1");
// 2. Parse the data using the parser we just built
final parsedLevels = WolfMapParser.parseMaps(mapHead, gameMaps);
final parsedTextures = VswapParser.parseWalls(vswap);
// 3. Return the populated instance!
return WolfMap._(parsedLevels);
return WolfMap._(parsedLevels, parsedTextures);
}
}

View File

@@ -0,0 +1,293 @@
import 'dart:math';
import 'package:flutter/material.dart';
extension WolfPaletteMatch on Color {
/// Finds the index of the closest color in the wolfPalette
int findClosestIndex(List<Color> palette) {
int closestIndex = 0;
double minDistance = double.infinity;
for (int i = 0; i < palette.length; i++) {
final Color pColor = palette[i];
// Calculate squared Euclidean distance (skipping sqrt for performance)
double distance =
pow(r - pColor.r, 2).toDouble() +
pow(g - pColor.g, 2).toDouble() +
pow(b - pColor.b, 2).toDouble();
if (distance < minDistance) {
minDistance = distance;
closestIndex = i;
}
}
return closestIndex;
}
/// Returns the actual Color object from the palette that matches best
Color toWolfColor(List<Color> palette) {
return palette[findClosestIndex(palette)];
}
}
abstract class ColorPalette {
static const List<Color> vga = [
Color(0xFF000000),
Color(0xFF0000AA),
Color(0xFF00AA00),
Color(0xFF00AAAA),
Color(0xFFAA0000),
Color(0xFFAA00AA),
Color(0xFFAA5500),
Color(0xFFAAAAAA),
Color(0xFF555555),
Color(0xFF5555FF),
Color(0xFF55FF55),
Color(0xFF55FFFF),
Color(0xFFFF5555),
Color(0xFFFF55FF),
Color(0xFFFFFF55),
Color(0xFFFFFFFF),
Color(0xFFEEEEEE),
Color(0xFFDEDEDE),
Color(0xFFD2D2D2),
Color(0xFFC2C2C2),
Color(0xFFB6B6B6),
Color(0xFFAAAAAA),
Color(0xFF999999),
Color(0xFF8D8D8D),
Color(0xFF7D7D7D),
Color(0xFF717171),
Color(0xFF656565),
Color(0xFF555555),
Color(0xFF484848),
Color(0xFF383838),
Color(0xFF2C2C2C),
Color(0xFF202020),
Color(0xFFFF0000),
Color(0xFFEE0000),
Color(0xFFE20000),
Color(0xFFD60000),
Color(0xFFCA0000),
Color(0xFFBE0000),
Color(0xFFB20000),
Color(0xFFA50000),
Color(0xFF990000),
Color(0xFF890000),
Color(0xFF7D0000),
Color(0xFF710000),
Color(0xFF650000),
Color(0xFF590000),
Color(0xFF4C0000),
Color(0xFF400000),
Color(0xFFFFDADA),
Color(0xFFFFBABA),
Color(0xFFFF9D9D),
Color(0xFFFF7D7D),
Color(0xFFFF5D5D),
Color(0xFFFF4040),
Color(0xFFFF2020),
Color(0xFFFF0000),
Color(0xFFFFAA5D),
Color(0xFFFF9940),
Color(0xFFFF8920),
Color(0xFFFF7900),
Color(0xFFE66D00),
Color(0xFFCE6100),
Color(0xFFB65500),
Color(0xFF9D4C00),
Color(0xFFFFFFDA),
Color(0xFFFFFFBA),
Color(0xFFFFFF9D),
Color(0xFFFFFF7D),
Color(0xFFFFFA5D),
Color(0xFFFFF640),
Color(0xFFFFF620),
Color(0xFFFFF600),
Color(0xFFE6DA00),
Color(0xFFCEC600),
Color(0xFFB6AE00),
Color(0xFF9D9D00),
Color(0xFF858500),
Color(0xFF716D00),
Color(0xFF595500),
Color(0xFF404000),
Color(0xFFD2FF5D),
Color(0xFFC6FF40),
Color(0xFFB6FF20),
Color(0xFFA1FF00),
Color(0xFF91E600),
Color(0xFF81CE00),
Color(0xFF75B600),
Color(0xFF619D00),
Color(0xFFDAFFDA),
Color(0xFFBEFFBA),
Color(0xFF9DFF9D),
Color(0xFF81FF7D),
Color(0xFF61FF5D),
Color(0xFF40FF40),
Color(0xFF20FF20),
Color(0xFF00FF00),
Color(0xFF00FF00),
Color(0xFF00EE00),
Color(0xFF00E200),
Color(0xFF00D600),
Color(0xFF04CA00),
Color(0xFF04BE00),
Color(0xFF04B200),
Color(0xFF04A500),
Color(0xFF049900),
Color(0xFF048900),
Color(0xFF047D00),
Color(0xFF047100),
Color(0xFF046500),
Color(0xFF045900),
Color(0xFF044C00),
Color(0xFF044000),
Color(0xFFDAFFFF),
Color(0xBAFFFFFF),
Color(0x9DFFFFFF),
Color(0x7DFFFAFF),
Color(0xFF5DFFFF),
Color(0x40FFFFFF),
Color(0x20FFFFFF),
Color(0x00FFFFFF),
Color(0x00E6E6FF),
Color(0x00CECEFF),
Color(0x00B6B6FF),
Color(0x009D9DFF),
Color(0x008585FF),
Color(0x007171FF),
Color(0x005959FF),
Color(0x004040FF),
Color(0xFF5DBEFF),
Color(0x40B2FFFF),
Color(0x20AAFFFF),
Color(0x009DFFFF),
Color(0x008DE6FF),
Color(0x007DCEFF),
Color(0x006DB6FF),
Color(0x005D9DFF),
Color(0xDADADAFF),
Color(0xBABEFFFF),
Color(0x9D9DFFFF),
Color(0x7D81FFFF),
Color(0x5d61ffff),
Color(0x4040FFFF),
Color(0x2024FFFF),
Color(0x0004FFFF),
Color(0xFF0000FF),
Color(0x0000EEFF),
Color(0x0000E2FF),
Color(0x0000D6FF),
Color(0xFF0000CA),
Color(0x0000BEFF),
Color(0x0000B2FF),
Color(0x0000A5FF),
Color(0xFF000099),
Color(0x000089FF),
Color(0x00007DFF),
Color(0x000071FF),
Color(0xFF000065),
Color(0x000059FF),
Color(0x00004CFF),
Color(0x000040FF),
Color(0xFF282828),
Color(0xFFFFE234),
Color(0xFFFFD624),
Color(0xFFFFCE18),
Color(0xFFFFC208),
Color(0xFFFFB600),
Color(0xFFB620FF),
Color(0xAA00FFFF),
Color(0xFF9900E6),
Color(0x8100CEFF),
Color(0x7500B6FF),
Color(0x61009DFF),
Color(0xFF500085),
Color(0x440071FF),
Color(0x340059FF),
Color(0x280040FF),
Color(0xFFFFDAFF),
Color(0xFFFFBAFF),
Color(0xFFFF9DFF),
Color(0xFFFF7DFF),
Color(0xFFFF5DFF),
Color(0xFFFF40FF),
Color(0xFFFF20FF),
Color(0xFFFF00FF),
Color(0xFFE200E6),
Color(0xCA00CEFF),
Color(0xB600B6FF),
Color(0x9D009DFF),
Color(0xFF850085),
Color(0x6D0071FF),
Color(0x590059FF),
Color(0x400040FF),
Color(0xFFFFEADE),
Color(0xFFFFE2D2),
Color(0xFFFFDAC6),
Color(0xFFFFD6BE),
Color(0xFFFFCEB2),
Color(0xFFFFC6A5),
Color(0xFFFFBE9D),
Color(0xFFFFBA91),
Color(0xFFFFB281),
Color(0xFFFA571F),
Color(0xFFFF9D61),
Color(0xFFF2955D),
Color(0xFFEA8D59),
Color(0xFFDE8955),
Color(0xFFD28150),
Color(0xFFCA7D4C),
Color(0xFFBE7948),
Color(0xFFB67144),
Color(0xFFAA6940),
Color(0xFFA1653C),
Color(0xFF9D6138),
Color(0xFF915D34),
Color(0xFF895930),
Color(0xFF81502C),
Color(0xFF754C28),
Color(0xFF6D4824),
Color(0xFF5D4020),
Color(0xFF553C1C),
Color(0xFF483818),
Color(0x403018FF),
Color(0x382C14FF),
Color(0x28200CFF),
Color(0xFF610065),
Color(0x006565FF),
Color(0x006161FF),
Color(0x00001CFF),
Color(0xFF00002C),
Color(0x302410FF),
Color(0x480048FF),
Color(0x500050FF),
Color(0xFF000034),
Color(0x1C1C1CFF),
Color(0x4C4C4CFF),
Color(0x5D5D5DFF),
Color(0xFF404040),
Color(0x303030FF),
Color(0x343434FF),
Color(0xDAF6F6FF),
Color(0xBAEAEAFF),
Color(0x00009dde),
Color(0x00075cac),
Color(0x48C2C2FF),
Color(0xFF20B6B6),
Color(0x20B2B2FF),
Color(0x00A5A5FF),
Color(0x009999FF),
Color(0xFF008D8D),
Color(0x008585FF),
Color(0x007D7DFF),
Color(0x007979FF),
Color(0xFF007575),
Color(0x007171FF),
Color(0x006D6DFF),
Color(0x990089FF),
];
}

View File

@@ -3,15 +3,18 @@ import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:wolf_dart/classes/linear_coordinates.dart';
import 'package:wolf_dart/classes/matrix.dart';
import 'package:wolf_dart/features/renderer/color_palette.dart';
class RaycasterPainter extends CustomPainter {
final Matrix<int> map;
final List<Matrix<int>> textures;
final LinearCoordinates player;
final double playerAngle;
final double fov;
RaycasterPainter({
required this.map,
required this.textures,
required this.player,
required this.playerAngle,
required this.fov,
@@ -139,6 +142,7 @@ class RaycasterPainter extends CustomPainter {
side,
size,
hitWallId,
textures,
);
}
}
@@ -151,77 +155,47 @@ class RaycasterPainter extends CustomPainter {
int side,
Size size,
int hitWallId,
List<Matrix<int>> textures,
) {
if (distance <= 0.01) distance = 0.01;
double wallHeight = size.height / distance;
double drawStart = (size.height / 2) - (wallHeight / 2);
double drawEnd = (size.height / 2) + (wallHeight / 2);
int drawStart = ((size.height / 2) - (wallHeight / 2)).toInt();
// --- PROCEDURAL TEXTURE LOGIC ---
Color baseColor;
// 1. PERFECT TEXTURE MAPPING
// Wolf3D stores textures in pairs. Even = N/S (Light), Odd = E/W (Dark).
int texNum = ((hitWallId - 1) * 2).clamp(0, textures.length - 2);
if (side == 1) texNum += 1; // Instantly use the native dark texture!
// Draw a dark edge on the sides of the block to create "tiles"
if (wallX < 0.05 || wallX > 0.95) {
baseColor = Colors.black87;
} else {
switch (hitWallId) {
case 1:
case 2:
case 3:
baseColor = Colors.grey[600]!; // Standard Grey Stone
break;
case 7:
case 8:
case 19:
baseColor = Colors.brown[600]!; // Wood Paneling
break;
case 9:
case 10:
baseColor = Colors.indigo[800]!; // Blue Stone
break;
case 17:
baseColor = Colors.red[900]!; // Red Brick
break;
case 41:
case 42:
baseColor = Colors.blueGrey; // Elevator walls
break;
default:
baseColor = Colors.teal; // Fallback for unknown IDs
int texX = (wallX * 64).toInt().clamp(0, 63);
if (side == 0 && math.cos(playerAngle) > 0) texX = 63 - texX;
if (side == 1 && math.sin(playerAngle) < 0) texX = 63 - texX;
double startY = drawStart.toDouble();
double stepY = wallHeight / 64.0;
for (int ty = 0; ty < 64; ty++) {
int colorByte = textures[texNum][texX][ty];
// 2. NO MORE SHADOW MATH
// Because we selected the correct dark texture above, we just draw the raw color!
Color pixelColor = ColorPalette.vga[colorByte];
double endY = startY + stepY;
if (endY > 0 && startY < size.height) {
canvas.drawLine(
Offset(x.toDouble(), startY),
Offset(x.toDouble(), endY),
Paint()
..color = pixelColor
..strokeWidth = 1.1,
);
}
startY += stepY;
}
// Faux-Lighting: Darken East/West walls to give a 3D pop to corners
if (side == 1) {
baseColor = Color.fromARGB(
255,
((baseColor.r * 255).round().clamp(0, 255) * 0.7).toInt(),
((baseColor.g * 255).round().clamp(0, 255) * 0.7).toInt(),
((baseColor.b * 255).round().clamp(0, 255) * 0.7).toInt(),
);
}
// Depth cueing: Dim colors as they get further away
double dimFactor = (1.0 - (distance / 15)).clamp(0.0, 1.0);
Color finalColor = Color.fromARGB(
255,
((baseColor.r * 255).round().clamp(0, 255) * dimFactor).toInt(),
((baseColor.g * 255).round().clamp(0, 255) * dimFactor).toInt(),
((baseColor.b * 255).round().clamp(0, 255) * dimFactor).toInt(),
);
final paint = Paint()
..color = finalColor
..strokeWidth =
1.1 // Prevent transparent gaps between line strokes
..style = PaintingStyle.stroke;
canvas.drawLine(
Offset(x.toDouble(), drawStart),
Offset(x.toDouble(), drawEnd),
paint,
);
}
@override

View File

@@ -158,6 +158,7 @@ class _WolfRendererState extends State<WolfRenderer>
size: Size(constraints.maxWidth, constraints.maxHeight),
painter: RaycasterPainter(
map: currentLevel,
textures: gameMap.textures,
player: player,
playerAngle: playerAngle,
fov: fov,

View File

@@ -1,4 +1,8 @@
import 'package:flutter/material.dart';
import 'package:wolf_dart/features/renderer/renderer.dart';
void main() => runApp(const MaterialApp(home: WolfRenderer()));
void main() {
runApp(
const MaterialApp(home: WolfRenderer()),
);
}