diablo2-web/docs/lighting_reverse_engineerin...

119 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Diablo II v1.13c Reverse Engineering Specification: `dLightMap.c` Lighting Pipeline
> **Ground Truth Binary**: `samples/d2/D2Client.dll` (v1.13c, PE ImageBase `0x6fab0000`, file timestamp 2010-03-09).
> **Source File Origin**: `..\Source\D2Client\DRAW\dLightMap.c` (string located at VA `0x6fb84104`).
> **Target Assembly Range**: `0x6fb58000` – `0x6fb5a000` (dLightMap.c), `0x6faef000` – `0x6faefee0` (light debug & map tile interface), `0x6fb10000` – `0x6fb10500` (floor tile rasterizer).
---
## 1. Light Grid Architecture & Topography
### 1.1 Dimensions & Coordinate Space
- **Grid Dimensions**: Exactly **48 × 48 cells** (2,304 cells total, hex `0x900`).
- Column index $x \in [0, 47]$ (`cmp ecx, 0x30`).
- Row index $y \in [0, 47]$ (`cmp eax, 0x30`).
- Linear address mapping: `index = 48 * y + x` (`lea eax, [eax + eax*2]; shl eax, 4; add eax, ecx`).
- **Spatial Resolution & Scale**:
- The lighting grid steps by **8 sub-tiles** per cell (`sar ecx, 3` / `add esi, 8`).
- World sub-tile coordinates $(x_{\text{sub}}, y_{\text{sub}})$ are converted to grid coordinates by:
$$x_{\text{grid}} = (x_{\text{sub}} \gg 3) - x_{\text{origin}}$$
$$y_{\text{grid}} = (y_{\text{sub}} \gg 3) - y_{\text{origin}}$$
Where $(x_{\text{origin}}, y_{\text{origin}})$ are the grid origin sub-tile offsets (`ds:0x6fbb4df0`, `ds:0x6fbb4df4`).
### 1.2 Cell Memory Layout (8 Bytes per Cell)
The primary lighting array is located at base address `0x6fbb5208` with an 8-byte stride per cell (`ecx * 8 + 0x6fbb5208`):
```c
struct D2LightCellStrc {
uint8_t nFlags; // +0x00: Obstacle / cell flag
uint8_t pad[3]; // +0x01..+0x03
uint8_t nIntensity; // +0x04: Clamped intensity [0..255]
uint8_t nRed; // +0x05: Red color channel [0..255]
uint8_t nGreen; // +0x06: Green color channel [0..255]
uint8_t nBlue; // +0x07: Blue color channel [0..255]
};
```
---
## 2. Mathematical Formulas (Decompiled from Assembly)
### 2.1 Fast Euclidean Distance Approximation (Alpha Max Plus Beta Min)
In `0x6fb597b4` – `0x6fb597d9`, Blizzard avoids floating-point square root `sqrt(dx^2 + dy^2)` using an integer approximation:
```assembly
6fb597b4: cmp ecx, eax
6fb597b6: jl 0x6fb597cb
6fb597b8: imul ecx, ecx, 0x3d7 ; 983
6fb597be: imul eax, eax, 0x197 ; 407
6fb597c4: add ecx, eax
6fb597c6: shr ecx, 0xa ; >> 10 (1024)
```
Mathematical representation:
$$\text{dist}(dx, dy) = \frac{983 \cdot \max(|dx|, |dy|) + 407 \cdot \min(|dx|, |dy|)}{1024}$$
Where:
- $\frac{983}{1024} \approx 0.95996$
- $\frac{407}{1024} \approx 0.39746$
- Maximum theoretical error across all angles is $\le 3.96\%$.
### 2.2 Light Falloff & Obstacle Attenuation Formula
In `0x6fb597de` – `0x6fb597fa`:
```assembly
6fb597de: mov edx, [esi+0x18] ; Light Radius R
6fb597e1: sub edx, ecx ; (R - dist)
6fb597e3: imul edx, [esp+0x3c] ; * intensitySlope (16.16 fixed point)
6fb597f1: sar edx, 16 ; >> 16
6fb597e8: sar edi, 1 ; edi = obstacle factor
6fb597ea: mov eax, 8
6fb597ef: sub eax, edi ; (8 - obstacle)
6fb597f4: imul eax, edx ; (8 - obstacle) * attenuated
6fb597f7: sar eax, 3 ; >> 3 (/ 8)
```
Mathematical representation:
$$\text{IntensitySlope} = \frac{\text{BaseIntensity} \ll 16}{R}$$
$$\text{Attenuated} = \max\left(0, \frac{(R - \text{dist}) \cdot \text{IntensitySlope}}{65536}\right)$$
$$\text{FinalCellIntensity} = \frac{(8 - \text{ObstacleFactor}) \cdot \text{Attenuated}}{8}$$
- When $\text{ObstacleFactor} = 0$: Light transmits at $100\%$ ($\frac{8-0}{8} = 1.0$).
- When $\text{ObstacleFactor} \ge 16$: Light is completely blocked (`cmp edi, 0x10; jge skip`).
### 2.3 Multi-Source Color Blending (Weighted Energy Conservation)
In `0x6fb58a91` – `0x6fb58b45`:
When multiple lights illuminate the same grid cell, their colors are combined via intensity-weighted averaging:
```assembly
6fb58a91: lea ecx, [esi+edx*1] ; newIntensity = existing + incoming
6fb58a94: cmp ecx, 0xff
...
6fb58abd: mov edi, [ebx*4 + 0x6fbb4e00] ; 65536 / newIntensity table lookup
6fb58ac4: imul ecx, esi ; existingRed * existingIntensity
6fb58acd: imul ebp, edx ; incomingRed * incomingIntensity
6fb58ad0: add ecx, ebp
6fb58ad2: imul ecx, edi ; sum * (65536 / newIntensity)
6fb58ad5: shr ecx, 0x10 ; >> 16
```
Mathematical representation:
$$I_{\text{total}} = \min(255, I_{\text{existing}} + I_{\text{incoming}})$$
$$R_{\text{blended}} = \min\left(255, \frac{R_{\text{existing}} \cdot I_{\text{existing}} + R_{\text{incoming}} \cdot I_{\text{incoming}}}{I_{\text{total}}}\right)$$
$$G_{\text{blended}} = \min\left(255, \frac{G_{\text{existing}} \cdot I_{\text{existing}} + G_{\text{incoming}} \cdot I_{\text{incoming}}}{I_{\text{total}}}\right)$$
$$B_{\text{blended}} = \min\left(255, \frac{B_{\text{existing}} \cdot I_{\text{existing}} + B_{\text{incoming}} \cdot I_{\text{incoming}}}{I_{\text{total}}}\right)$$
### 2.4 Sub-Tile Bilinear Interpolation (8.8 Fixed Point)
In `0x6fb58373` – `0x6fb58385`:
```assembly
mov ebx, 0x100 ; 256
sub ebx, eax ; 256 - factor
imul eax, edx ; factor * L2
imul ebx, edi ; (256 - factor) * L1
add ebx, eax
sar ebx, 0x8 ; >> 8
```
Interpolates smoothly between neighboring cells using fixed-point factor $f \in [0, 256]$:
$$\text{Sample} = \frac{(256 - f) \cdot L_1 + f \cdot L_2}{256}$$
---
## 3. Light Sources & Defaults
- **Player Base Light Radius**: Default is radius 10–13 sub-tiles adjusted by item `STAT_LIGHTRADIUS` (`CUnit.c` / `dLightMap.c`).
- **Monster Default Radius**: 8 sub-tiles (`mov ebx, 0x8` at `0x6fb59894`).
- **Missile Lights**: Read directly from `Missiles.txt` `Light`, `Red`, `Green`, `Blue` columns.
- **Flicker**: Perturbation index updated every logic frame (25Hz) modifying radius by $\pm 1$ sub-tile.
- **2D Software Mode Fallback**: When `bColoredLighting` is false (`ds:0x6fb8e830 == 0`), $R=G=B=I$ and the intensity directly drives the 32-level PL2 colormap row.