Why Every Radar Beacon Went Dark
A five-minute code walkthrough of the D3D11 dazzle renderer early-return, the mystery it caused, and the single line that fixed it.
Video length target: ~5-7 minutes. Recording setup: VS Code / editor with file tree visible, split view. In-game recording optional for the cold open. Prereqs for viewer: Familiarity with C++ and real-time rendering basics.
Cold open (0:00 – 0:30)
[VISUAL: In-game footage, night map. Fly the camera past a GLA radar van, a Chinese Command Center antenna, a USA comms tower. Pause on the antenna tip. In the original Generals a red pinprick glows there. In our shot, nothing.]
Say:
"See that antenna? In the original 2003 game, it glows red. In our D3D11 port, for about six months, it didn't."
Say:
"Nobody touched the art. Nobody changed the W3D model. One line of C++ turned every beacon, every afterburner, every comms light off at the same time. Let's go find it."
Act 1 — What a dazzle actually is (0:30 – 1:30)
[VISUAL: Cut to editor. Open Core/Libraries/Source/WWVegas/WW3D2/dazzle.h. Scroll to DazzleRenderObjClass and DazzleTypeClass.]
Say:
"A 'dazzle' in the W3D engine is a billboard glow. Radar beacons, antenna lights, jet afterburners, muzzle flashes — all the same primitive. Camera-facing quad, additive blend, one texture."
[VISUAL: Open Core/Libraries/Source/WWVegas/WW3D2/RequiredAssets/Dazzle.INI. Scroll slowly through a [SUN] block.]
Say:
"Each type is an INI entry. HaloIntensity, FadeoutStart, FadeoutEnd, a color.
DazzleRenderObjClass::Init_From_INIloads them into a static array at startup. An artist drops one into a.w3dfile by type name, and the loader binds the instance to its type."
- [SHOW:] The
DazzleTypeClass::Calculate_Intensitiessignature in dazzle.h — point atcamera_forward,dazzle_direction, anddistanceparameters. - [SHOW:] Highlight that the final intensity is
pow(dot(camera, dazzle_dir), intensity_pow)— i.e. glow falls off sharply as you look at it from the side.
Say:
"The math matters later. Remember: intensity is a dot product, and it can fade to zero at glancing angles. That's not a bug, that's the look."
Act 2 — Following the dispatch (1:30 – 3:00)
[VISUAL: Open GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/D3D11Shims.cpp. Jump to line 6988. Highlight the CLASSID_DAZZLE branch.]
Say:
"Scene traversal time. The D3D11 shim walks every render object in the visible set and switches on class ID."
else if (classId == RenderObjClass::CLASSID_DAZZLE) { robj->On_Frame_Update(); RenderDazzleDX11(robj, rinfo); }
Say:
"Mesh goes to the model renderer. Particle buffer has its own path. Dazzle goes here. Let's follow it."
[VISUAL: Ctrl-click or F12 on RenderDazzleDX11. Land at line 9951. Scroll down — don't look at the top yet. Go to line 9980 and below.]
Say:
"This is the meat of the function. Grab the camera transform. Grab the dazzle's world transform. Compute direction and distance from camera to dazzle."
const Matrix3D& camTM = rinfo.Camera.Get_Transform();
Vector3 cameraPos(camTM[0][3], camTM[1][3], camTM[2][3]);
Vector3 camForward(camTM[0][2], camTM[1][2], camTM[2][2]);
[VISUAL: Scroll to line 9994 — the Calculate_Intensities call.]
Say:
"Hand those vectors to the type class. It returns three scalars: dazzle intensity, size, halo intensity. Intensity under one percent? We skip the draw. That's the cheap gate."
[VISUAL: Scroll to line 10022 — the billboard construction.]
float rightX = camTM[0][0], rightY = camTM[1][0], rightZ = camTM[2][0]; float upX = camTM[0][1], upY = camTM[1][1], upZ = camTM[2][1]; // ... normalize, scale by halfSize ...
Say:
"Camera right and up vectors, normalized, scaled. Offset the dazzle's world position by plus-or-minus right and up. Six verts, two triangles, additive blend, draw. Textbook camera-facing quad."
[VISUAL: Scroll to line 10054.]
renderer.SetAdditive3DState();
renderer.Draw3DNoIndex(vb, 6, tex, identity, {1,1,1,1});
Say:
"That is a complete, working dazzle renderer. It's all here. So why was every beacon dark?"
Act 3 — The twist (3:00 – 4:00)
[VISUAL: Scroll back up to line 9951. Pause on the comment block above the function body. Highlight line-by-line.]
Say:
"Here's the top of the same function."
static void RenderDazzleDX11(RenderObjClass* robj, RenderInfoClass& rinfo) { // Dazzle render objects are camera-facing additive billboards used by // W3D for radar beacon lights, antenna glows, aircraft afterburners, // building spotlights, and some weapon muzzle flashes. They were // previously short-circuited in this port because the comment claimed // muzzle flashes were already handled by the model's MuzzleFX // sub-objects — but that only covers a subset of dazzles, leaving // every other beacon/glow dark.
[VISUAL: Highlight the original guard — the line that used to be here before the fix. Narrator mimes the deleted line with a strikethrough.]
Say:
"The original port author wrote an early
returnat the top of this function. Right above the working code. The reasoning in the comment is the interesting part."
Say:
"They noticed some dazzles were also muzzle flashes. They noticed the
MuzzleFXsub-object system already draws muzzle flashes a different way. So they thought: if I draw dazzles here, muzzle flashes render twice. Disable the whole function, problem solved."
[VISUAL: Pan across a row of units in-game — tank, humvee, hovercraft — none of them are firing.]
Say:
"It is solved. For muzzle flashes. For the other 95 percent of dazzles — radar beacons, antenna lights, afterburners, comms towers, spotlights — they're just off. Forever. Silently."
Say:
"The gate was at the wrong level. They disabled an entire rendering feature because one specific use case had a conflict."
Act 4 — The fix (4:00 – 5:00)
[VISUAL: Show the git diff. A single deleted line. The early return, gone.]
Say:
"The fix is deleting one line. That's it. The implementation underneath was complete and correct the whole time."
[VISUAL: Launch the game. Same night map as the cold open. Fly to the same antenna. It's glowing red now. Pan to a parked MiG — afterburner cones. Pan to a radar van — antenna tip glows.]
Say:
"Every beacon back. Every antenna back. Afterburners back."
[VISUAL: Back to the comment block in the editor, highlight the last three lines.]
// Risk: any model that ships BOTH a dazzle AND a MuzzleFX sub-object // will render the muzzle flash twice; if that turns out to be visible, // gate this on the dazzle's owning render-obj being not a muzzle-flash // sub-object instead of a global toggle.
Say:
"The original concern is real. I kept it in the comment. Shipping assets don't overlap, so nothing double-renders in practice. If a mod hits it, the fix is local — check the sub-object type, skip that one dazzle. Not turn the whole system off."
Outro (5:00 – 5:30)
[VISUAL: Split screen. Before on the left, after on the right. Same camera angle. Left is dead. Right is alive.]
Say:
"Lesson from this one: 'disabled with a comment' is a landmine. When you find a conflict, gate on the specific problem, not on the entire feature. One correct guard, five characters wide, beats shutting off a thousand lights."
Say:
"Next video we chase the same pattern in the particle system — a single flag that strobed every toxin puddle in the game. See you there."
B-roll suggestions
- Nighttime flyover of USA01 campaign base showing comms tower lights blinking.
- Formation of MiGs on afterburner over water, side-view, golden-hour.
- Close-up on a Scud Launcher with siren dazzles active (if any ship with dazzles).
- Editor scroll from the top of
D3D11Shims.cppto line 9938 as narrator talks through file structure. - Git blame of the deleted early-return line, hovering to show the original commit message.
Takeaway
The episode is worth five minutes because it is a pure case of "the code works, the feature is off." No logic bug, no race, no math error — just a gate placed one level too high. The fix is visual and immediate, and the meta-lesson (scope your disables to the specific conflict) generalizes to every codebase. Good opener for a series on port archaeology.