Fix game object occlusion: render outline targets last in batch order
Outline targets now render after all other M2 models instead of before, so the depth buffer contains full scene geometry (game objects, other characters, NPCs) when stencil marks are written. Previously only terrain+WMO depth was available, causing outlines to show through fences, mailboxes, lamp posts, and other game objects.
This commit is contained in:
@@ -8,10 +8,11 @@
|
||||
//! marks where models pass the terrain depth test (stencil=1 = visible).
|
||||
//! - **Reset**: forces D24S8 depth/stencil format, releases resources.
|
||||
//!
|
||||
//! Batch reordering in model_hook.zig ensures outline targets render first in
|
||||
//! CM2SceneRenderDraw, when only terrain+WMO depth exists. The DIP hook writes
|
||||
//! stencil marks using the game's own depth buffer; EndScene replay uses these
|
||||
//! marks to gate silhouette drawing (terrain/WMO occlusion without DS copy).
|
||||
//! Batch reordering in model_hook.zig ensures outline targets render LAST in
|
||||
//! CM2SceneRenderDraw, after all other M2 models (game objects, characters,
|
||||
//! NPCs) have filled the depth buffer. The DIP hook writes stencil marks
|
||||
//! using the game's own depth buffer; EndScene replay uses these marks to
|
||||
//! gate silhouette drawing (full scene occlusion including game objects).
|
||||
|
||||
const std = @import("std");
|
||||
const hook = @import("hook");
|
||||
@@ -796,10 +797,11 @@ fn hkDIP(
|
||||
frame_has_outlines = true;
|
||||
}
|
||||
|
||||
// Mark terrain-visible pixels in stencil for this outline target.
|
||||
// At this point (outline targets draw first due to batch reordering),
|
||||
// the game's DS has only terrain+WMO depth. Pixels that pass the depth
|
||||
// test get stencil=1; pixels behind terrain fail and keep stencil=0.
|
||||
// Mark visible pixels in stencil for this outline target.
|
||||
// At this point (outline targets draw last due to batch reordering),
|
||||
// the game's DS has terrain+WMO+all non-outline M2 model depth.
|
||||
// Pixels that pass the depth test get stencil=1; pixels behind any
|
||||
// scene geometry fail and keep stencil=0.
|
||||
// EndScene uses these marks to gate silhouette rendering.
|
||||
const s_enable = deviceGetRS(device, types.D3DRS.STENCILENABLE);
|
||||
const s_func = deviceGetRS(device, types.D3DRS.STENCILFUNC);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! WoW model rendering pipeline hooks.
|
||||
//!
|
||||
//! Hooks three WoW functions to integrate outline rendering:
|
||||
//! - CM2SceneRenderDraw — captures terrain depth before M2 models draw.
|
||||
//! - CM2SceneRenderDraw — reorders batches so outline targets render last.
|
||||
//! - CM2Model_ManageRenderListNode — classifies models on render-list add.
|
||||
//! - CM2Scene_DrawBatchProjected — flags the DIP hook for outline rendering.
|
||||
//!
|
||||
@@ -62,9 +62,11 @@ var reordered_indices: [MAX_REORDER]i32 = undefined;
|
||||
// __thiscall(this, viewMatrix, batchData, batchIndices, batchCount)
|
||||
// Native thiscall detour — no thunk needed.
|
||||
//
|
||||
// Reorders batch indices so outline targets draw first, when the game's
|
||||
// depth buffer contains only terrain + WMO geometry. The DIP hook renders
|
||||
// silhouettes using the game's own DS for depth testing (ZWRITEENABLE=FALSE).
|
||||
// Reorders batch indices so outline targets draw LAST. Non-outline models
|
||||
// (game objects, other characters, NPCs) render first, filling the depth
|
||||
// buffer with full scene geometry. When outline targets then render, the
|
||||
// DIP hook writes stencil marks against this complete depth buffer, so
|
||||
// outlines are properly occluded by all scene objects (not just terrain/WMOs).
|
||||
|
||||
fn renderDrawDetour(this: u32, view_matrix: u32, batch_data: u32, batch_indices: u32, batch_count: u32) callconv(THISCALL) void {
|
||||
// One-time: install D3D9 hooks now that the game is actively rendering.
|
||||
@@ -96,9 +98,13 @@ fn renderDrawDetour(this: u32, view_matrix: u32, batch_data: u32, batch_indices:
|
||||
return;
|
||||
}
|
||||
|
||||
// Pass 2: partition — outline targets first, then everything else.
|
||||
var outline_pos: u32 = 0;
|
||||
var normal_pos: u32 = outline_count;
|
||||
// Pass 2: partition — non-outline models first, outline targets last.
|
||||
// Rendering non-outline models first fills the depth buffer with game
|
||||
// objects, other characters, etc., so outline target stencil marks
|
||||
// respect full scene occlusion (not just terrain+WMO).
|
||||
const normal_count = batch_count - outline_count;
|
||||
var normal_pos: u32 = 0;
|
||||
var outline_pos: u32 = normal_count;
|
||||
for (0..batch_count) |i| {
|
||||
const batch_idx = indices[i];
|
||||
const idx_u: u32 = @bitCast(batch_idx);
|
||||
|
||||
Reference in New Issue
Block a user