Repo hygiene: drop internal planning files, clean docs for publication
Untrack TODO.md, RELEASING.md, RELEASE_NOTES.md, ideas/ and docs/CLAUDE_PROPER_OBJECT_REGISTRATION_PLAN.md -- internal planning and agent notes with no reason to be published. Files stay on disk locally. Replace remaining absolute machine paths in docs with relative ones (one referenced a separate private project), and swap decorative emoji for ASCII markers. README: lead with the no-longer-actively-developed notice.
This commit is contained in:
@@ -1,169 +0,0 @@
|
||||
# Proper Object Registration Plan (for Claude)
|
||||
|
||||
## Goal
|
||||
Stop ad-hoc list insertion and implement a **correct, engine-native object creation/registration path** for client-side markers in WoW 1.12.1.
|
||||
|
||||
Success = marker is visible in-world, no crash on create/destroy/toggle, and lifecycle uses native manager functions (not manual pointer surgery as primary path).
|
||||
|
||||
---
|
||||
|
||||
## Current Status Summary (Read First)
|
||||
|
||||
1. **Crash fixed, visibility not fixed**
|
||||
- We eliminated crash caused by callback slot garbage (`obj+0x174`) in manager iteration path.
|
||||
- Markers still invisible in both current test paths.
|
||||
|
||||
2. **What was tried**
|
||||
- Path A: Allocate world object + attach model + manual insert into manager list (`0x00C7CAEC`).
|
||||
- Path B: `CreateGameObject_WithProperties` + fallback manual insertion if unlinked.
|
||||
- Both now create/destroy cleanly, still not rendering.
|
||||
|
||||
3. **Known engine behaviors**
|
||||
- Manager iterator (`AllocateGameObject` region around `0x00683F80`) uses positions at `+0x5C/+0x60/+0x68` and calls render-management functions.
|
||||
- It may call callback pointer at `+0x174` if non-zero.
|
||||
- List pointers can be low-bit tagged.
|
||||
|
||||
4. **Conclusion so far**
|
||||
- List manipulation and crash stability are mostly solved.
|
||||
- Remaining blocker is likely **incorrect native registration/state initialization**, i.e. object not entering correct draw/classification path.
|
||||
|
||||
---
|
||||
|
||||
## Mandatory Context Files
|
||||
|
||||
Read these before coding:
|
||||
|
||||
1. `research/marker-visibility-debug-log.md`
|
||||
2. `docs/M2_MODEL_SYSTEM.md`
|
||||
3. `research/dynamic-object-research.md`
|
||||
4. `src/markers/markers.zig`
|
||||
5. Crash reference: `/media/bigfaststore/games/twmoa_1172/Errors/2026-02-27 07.11.41 Crash.txt`
|
||||
|
||||
Also review the CLI Ghidra skill:
|
||||
- `~/.claude/skills/ghidra-cli-wow-re/SKILL.md`
|
||||
|
||||
---
|
||||
|
||||
## Rules of Engagement
|
||||
|
||||
1. **Do not treat manual list insertion as final architecture.**
|
||||
2. **Do not trust decompiler signatures alone.** Verify calling conventions from bytes/prologue/RET cleanup.
|
||||
3. **Prefer discovering native add/remove APIs** used by real visible objects.
|
||||
4. **Every hypothesis must have an observable test log.**
|
||||
5. **No broad refactors until registration path is proven.**
|
||||
|
||||
---
|
||||
|
||||
## Execution Plan
|
||||
|
||||
## Phase 1 - Identify canonical native registration path
|
||||
|
||||
### Task 1.1: Recover true iterator and linkage semantics
|
||||
- Disassemble around `0x00683F80` (and nearby helpers) in detail.
|
||||
- Map exactly how nodes are traversed and linked (including pointer tagging and indirection via globals like `0x00C7CAE4/0x00C7CAEC`).
|
||||
- Produce pseudocode with verified offsets and conditions.
|
||||
|
||||
**Deliverable:** short technical note: "Manager traversal/link semantics".
|
||||
|
||||
### Task 1.2: Find native insertion/removal helpers
|
||||
- Find xrefs to `0x00C7CAEC` and offsets `+0x16C/+0x170`.
|
||||
- Identify functions that insert/remove objects in production code.
|
||||
- Verify one candidate by callsite context (who calls it and when).
|
||||
|
||||
**Deliverable:** candidate helper function list with addresses + confidence.
|
||||
|
||||
### Task 1.3: Find real creator path for visible world objects
|
||||
- Trace from known visible object creators (game objects/doodads/spell visuals) to registration.
|
||||
- Determine **minimal required function chain** from creation to render visibility.
|
||||
|
||||
**Deliverable:** canonical call chain diagram with function addresses.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 - Build proper registration wrapper in markers module
|
||||
|
||||
### Task 2.1: Implement native-path wrapper
|
||||
- Add a wrapper that uses discovered native creator/registration APIs.
|
||||
- Manual list insertion may remain only as fallback/diagnostic mode, not default.
|
||||
|
||||
### Task 2.2: Lifecycle correctness
|
||||
- Ensure destroy path uses matching native teardown/unlink APIs.
|
||||
- Prevent double-unlink and callback garbage.
|
||||
|
||||
### Task 2.3: Instrumentation
|
||||
- Keep concise debug logs for:
|
||||
- object pointer
|
||||
- model pointer
|
||||
- registration result path
|
||||
- destroy path confirmation
|
||||
|
||||
**Deliverable:** code in `src/markers/markers.zig` with clear mode labels.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 - Validation protocol (must pass)
|
||||
|
||||
Run and capture logs for:
|
||||
|
||||
1. `/mark on`
|
||||
2. visually confirm marker appears
|
||||
3. `/mark off`
|
||||
4. repeat 5+ times
|
||||
5. mode switching stress (`testa/testb` if retained)
|
||||
|
||||
Must pass:
|
||||
- no crash
|
||||
- no panic
|
||||
- marker visible at player position
|
||||
- clean destroy every cycle
|
||||
|
||||
If failed:
|
||||
- report exact failing step, log lines, and next smallest probe
|
||||
|
||||
---
|
||||
|
||||
## Expected Outputs from Claude
|
||||
|
||||
1. Updated code implementing native registration path.
|
||||
2. Updated `research/marker-visibility-debug-log.md` with:
|
||||
- what changed
|
||||
- what was validated
|
||||
- remaining unknowns (if any)
|
||||
3. Optional update to `docs/M2_MODEL_SYSTEM.md` for newly confirmed function semantics.
|
||||
4. Final summary:
|
||||
- root cause
|
||||
- final fix
|
||||
- evidence (logs + addresses)
|
||||
|
||||
---
|
||||
|
||||
## Suggested Command Workflow (CLI Ghidra)
|
||||
|
||||
Use headless scripts via:
|
||||
|
||||
```bash
|
||||
<ghidra-scripts>/run-analysis.sh /tmp/analysis.py
|
||||
```
|
||||
|
||||
Focus scripts on:
|
||||
- xrefs to manager globals and list offsets
|
||||
- disassembly around candidate insertion helpers
|
||||
- call graph from visible object creation paths
|
||||
|
||||
---
|
||||
|
||||
## Non-Goals (for this pass)
|
||||
|
||||
- New marker visuals
|
||||
- Effects polish
|
||||
- Optimizations unrelated to visibility/registration correctness
|
||||
- Expanding to many marker types before first visible stable marker
|
||||
|
||||
---
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- Marker visibility achieved using engine-native registration path (or proven minimal wrapper around it)
|
||||
- No crash across repeated create/destroy
|
||||
- Documentation updated with verified semantics
|
||||
- Ad-hoc list surgery no longer required as primary behavior
|
||||
@@ -39,8 +39,8 @@ struct Model {
|
||||
|
||||
| Offset | Type | Name | Description | Verified |
|
||||
|--------|------|------|-------------|----------|
|
||||
| +0x30 | `void*` | dataPtr | Pointer to geometry data container | ✓ |
|
||||
| +0x94 | `BoneMatrix*` | boneArray | **Pointer to bone matrix array** | ✓ |
|
||||
| +0x30 | `void*` | dataPtr | Pointer to geometry data container | [OK] |
|
||||
| +0x94 | `BoneMatrix*` | boneArray | **Pointer to bone matrix array** | [OK] |
|
||||
|
||||
### Nested Structure (Model+0x30)
|
||||
|
||||
@@ -148,8 +148,8 @@ struct MeshData {
|
||||
|
||||
| Offset | Type | Name | Description | Verified |
|
||||
|--------|------|------|-------------|----------|
|
||||
| +0x04 | `uint16_t` | vertexOffset | Start vertex in global array | ✓ |
|
||||
| +0x06 | `uint16_t` | vertexCount | **Loop terminator in applyBoneTransforms** | ✓ |
|
||||
| +0x04 | `uint16_t` | vertexOffset | Start vertex in global array | [OK] |
|
||||
| +0x06 | `uint16_t` | vertexCount | **Loop terminator in applyBoneTransforms** | [OK] |
|
||||
| +0x08 | `uint16_t` | indexOffset | Start index for this submesh | ? |
|
||||
| +0x0A | `uint16_t` | indexCount | Number of indices | ? |
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
# Hooking Quick Reference Guide
|
||||
|
||||
## 🚨 Critical Rules (Break These = Crash)
|
||||
## Critical Rules (Break These = Crash)
|
||||
|
||||
### 1. NEVER Use PUSHA/POPA Around C Function Calls
|
||||
```asm
|
||||
; ❌ WRONG - Will crash
|
||||
; [X] WRONG - Will crash
|
||||
pusha
|
||||
call _MyCFunction ; This modifies EAX/ECX/EDX
|
||||
popa ; This RESTORES old values, breaking everything
|
||||
|
||||
; ✅ RIGHT - Only save non-volatile registers
|
||||
; [OK] RIGHT - Only save non-volatile registers
|
||||
pushl %ebx
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
@@ -26,18 +26,18 @@ popl %ebx
|
||||
typedef void (__fastcall *Func_t)(void* thisPtr, void* edx, int arg);
|
||||
|
||||
static void __fastcall MyHook(void* thisPtr, void* edx, int arg) {
|
||||
// thisPtr from ECX ✅
|
||||
// edx is trash ✅
|
||||
// arg from stack ✅
|
||||
// thisPtr from ECX [OK]
|
||||
// edx is trash [OK]
|
||||
// arg from stack [OK]
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Steal Complete Instructions Only
|
||||
```cpp
|
||||
// ❌ WRONG
|
||||
// [X] WRONG
|
||||
#define STOLEN_BYTES 6 // No idea if this splits an instruction
|
||||
|
||||
// ✅ RIGHT
|
||||
// [OK] RIGHT
|
||||
// Disassemble: PUSH EBP (1) + MOV EBP,ESP (2) + SUB ESP,0x80 (6) = 9 bytes
|
||||
#define STOLEN_BYTES 9 // Documented complete instructions
|
||||
```
|
||||
@@ -54,7 +54,7 @@ static void __fastcall MyHook(void* thisPtr, void* edx, int arg) {
|
||||
|
||||
---
|
||||
|
||||
## 📋 Register Preservation Cheat Sheet
|
||||
## Register Preservation Cheat Sheet
|
||||
|
||||
| Register | Type | Who Saves It? | Can Hook Modify? |
|
||||
|----------|------|---------------|------------------|
|
||||
@@ -69,7 +69,7 @@ static void __fastcall MyHook(void* thisPtr, void* edx, int arg) {
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Calling Convention Quick Reference
|
||||
## Calling Convention Quick Reference
|
||||
|
||||
### __stdcall (WINAPI)
|
||||
- Args: stack (right to left)
|
||||
@@ -93,7 +93,7 @@ static void __fastcall MyHook(void* thisPtr, void* edx, int arg) {
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Safe Hook Templates
|
||||
## Safe Hook Templates
|
||||
|
||||
### Template 1: VTable Hook (Safest)
|
||||
```cpp
|
||||
@@ -165,7 +165,7 @@ __attribute__((naked)) static void NakedHook() {
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Safety Checklist
|
||||
## Safety Checklist
|
||||
|
||||
### Before Installing Hook:
|
||||
- [ ] Disassemble target to verify prologue
|
||||
@@ -190,7 +190,7 @@ __attribute__((naked)) static void NakedHook() {
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Common Crash Causes
|
||||
## Common Crash Causes
|
||||
|
||||
1. **PUSHA/POPA with C calls** → Volatile register corruption
|
||||
2. **Wrong calling convention** → ECX/EDX clobbered when needed
|
||||
@@ -203,7 +203,7 @@ __attribute__((naked)) static void NakedHook() {
|
||||
|
||||
---
|
||||
|
||||
## 📚 Research Sources
|
||||
## Research Sources
|
||||
|
||||
- [How to Hook Functions - Guided Hacking](https://guidedhacking.com/threads/how-to-hook-functions-code-detouring-guide.14185/)
|
||||
- [Thiscall Hooking - tresp4sser](https://tresp4sser.wordpress.com/2012/10/06/how-to-hook-thiscall-functions/)
|
||||
@@ -213,7 +213,7 @@ __attribute__((naked)) static void NakedHook() {
|
||||
|
||||
---
|
||||
|
||||
## 💡 Key Insight
|
||||
## Key Insight
|
||||
|
||||
**Hooking is NOT about blindly copying bytes.**
|
||||
|
||||
|
||||
+21
-21
@@ -5,62 +5,62 @@
|
||||
|
||||
## Critical Address Fixes
|
||||
|
||||
### ❌ ProcessWorldWithFrustum
|
||||
### [X] ProcessWorldWithFrustum
|
||||
- **Old Address:** `0x00683000` (WRONG - no function at this address)
|
||||
- **Correct Address:** `0x00682fa0`
|
||||
- **Ghidra Signature:** `undefined __fastcall ProcessWorldWithFrustum(float * frustumBounds)`
|
||||
- **First Byte:** `0x57` (PUSH EDI)
|
||||
- **Stolen Bytes:** 9 bytes
|
||||
- **Status:** ✅ FIXED in discovery_hooks.cpp
|
||||
- **Status:** [OK] FIXED in discovery_hooks.cpp
|
||||
|
||||
### ❌ RenderObjectsWithLOD
|
||||
### [X] RenderObjectsWithLOD
|
||||
- **Old Address:** `0x00684600` (WRONG - no function at this address)
|
||||
- **Correct Address:** `0x00684510`
|
||||
- **Ghidra Signature:** `undefined __stdcall RenderObjectsWithLOD(void)`
|
||||
- **First Byte:** `0x55` (PUSH EBP)
|
||||
- **Stolen Bytes:** 8 bytes
|
||||
- **Status:** ✅ FIXED in discovery_hooks.cpp
|
||||
- **Status:** [OK] FIXED in discovery_hooks.cpp
|
||||
|
||||
## Verified Correct Addresses
|
||||
|
||||
### ✅ ScenePresent
|
||||
### [OK] ScenePresent
|
||||
- **Address:** `0x0059a870`
|
||||
- **Ghidra Signature:** `void __fastcall CGxDeviceD3d::ScenePresent(CGxDeviceD3d * device)`
|
||||
- **First Byte:** `0x55` (PUSH EBP)
|
||||
- **Stolen Bytes:** 6 bytes
|
||||
- **Calling Convention:** __fastcall (saves ECX+EDX) ✅ Correct in code
|
||||
- **Calling Convention:** __fastcall (saves ECX+EDX) [OK] Correct in code
|
||||
|
||||
### ✅ CullAndProcessWorldChunks
|
||||
### [OK] CullAndProcessWorldChunks
|
||||
- **Address:** `0x00683040`
|
||||
- **Ghidra Signature:** `undefined __stdcall CullAndProcessWorldChunks(void)`
|
||||
- **First Byte:** `0x55` (PUSH EBP)
|
||||
- **Stolen Bytes:** 8 bytes
|
||||
- **Calling Convention:** __stdcall (no register saves) ✅ Correct in code
|
||||
- **Calling Convention:** __stdcall (no register saves) [OK] Correct in code
|
||||
|
||||
### ✅ ProcessStaticObjectsCulling
|
||||
### [OK] ProcessStaticObjectsCulling
|
||||
- **Address:** `0x00683bf0`
|
||||
- **Ghidra Signature:** `undefined __fastcall ProcessStaticObjectsCulling(int staticObjectManager)`
|
||||
- **First Byte:** `0x55` (PUSH EBP)
|
||||
- **Stolen Bytes:** 9 bytes
|
||||
- **Calling Convention:** __fastcall (saves ECX+EDX) ✅ Correct in code
|
||||
- **Calling Convention:** __fastcall (saves ECX+EDX) [OK] Correct in code
|
||||
|
||||
### ✅ CM2Scene_DrawModelBatch
|
||||
### [OK] CM2Scene_DrawModelBatch
|
||||
- **Address:** `0x0070cf70`
|
||||
- **Ghidra Signature:** `undefined __fastcall CM2Scene_DrawModelBatch(void * renderContext)`
|
||||
- **First Byte:** `0x55` (PUSH EBP)
|
||||
- **Stolen Bytes:** 8 bytes
|
||||
- **Calling Convention:** __fastcall (saves ECX+EDX) ✅ Correct in code
|
||||
- **Calling Convention:** __fastcall (saves ECX+EDX) [OK] Correct in code
|
||||
|
||||
## Calling Convention Summary
|
||||
|
||||
| Function | Ghidra Convention | Code Implementation | Status |
|
||||
|----------|------------------|---------------------|--------|
|
||||
| ScenePresent | __fastcall | __fastcall (ECX+EDX) | ✅ Correct |
|
||||
| ProcessWorldWithFrustum | __fastcall | __fastcall (ECX+EDX) | ✅ Correct |
|
||||
| CullAndProcessWorldChunks | __stdcall | __cdecl (no saves) | ✅ OK (both no reg saves) |
|
||||
| ProcessStaticObjectsCulling | __fastcall | __fastcall (ECX+EDX) | ✅ Correct |
|
||||
| RenderObjectsWithLOD | __stdcall | __cdecl (no saves) | ✅ OK (both no reg saves) |
|
||||
| CM2Scene_DrawModelBatch | __fastcall | __fastcall (ECX+EDX) | ✅ Correct |
|
||||
| ScenePresent | __fastcall | __fastcall (ECX+EDX) | [OK] Correct |
|
||||
| ProcessWorldWithFrustum | __fastcall | __fastcall (ECX+EDX) | [OK] Correct |
|
||||
| CullAndProcessWorldChunks | __stdcall | __cdecl (no saves) | OK (both no reg saves) |
|
||||
| ProcessStaticObjectsCulling | __fastcall | __fastcall (ECX+EDX) | [OK] Correct |
|
||||
| RenderObjectsWithLOD | __stdcall | __cdecl (no saves) | OK (both no reg saves) |
|
||||
| CM2Scene_DrawModelBatch | __fastcall | __fastcall (ECX+EDX) | [OK] Correct |
|
||||
|
||||
## Prologue Byte Analysis
|
||||
|
||||
@@ -87,9 +87,9 @@ The Ghidra MCP `get_bytes` tool only returns 1 byte regardless of the length par
|
||||
|
||||
## Recommendations
|
||||
|
||||
1. ✅ **Address fixes have been applied** to discovery_hooks.cpp
|
||||
2. ⚠️ **Test the DLL** after recompilation to verify hooks work correctly
|
||||
3. ⚠️ **Monitor for crashes** that could indicate incorrect stolen byte counts
|
||||
1. [OK] **Address fixes have been applied** to discovery_hooks.cpp
|
||||
2. [!] **Test the DLL** after recompilation to verify hooks work correctly
|
||||
3. [!] **Monitor for crashes** that could indicate incorrect stolen byte counts
|
||||
4. If crashes occur after address fix, use Ghidra GUI to manually count instruction bytes in prologues
|
||||
|
||||
## Next Steps
|
||||
|
||||
@@ -14,9 +14,9 @@ Any architectural approach is acceptable. The existing 3-pass stencil code is pr
|
||||
|
||||
The depth buffer doesn't distinguish "wall pixel" from "player pixel." After a frame is rendered, there's no way to know which depth values came from static geometry vs units. This makes pure post-process approaches (mask + edge detection in EndScene) unable to satisfy requirements 1 and 2 simultaneously:
|
||||
|
||||
- If you depth-test the mask render against the full depth buffer → walls occlude (✓) but players also occlude (✗)
|
||||
- If you skip depth testing → players don't occlude (✓) but walls also don't occlude (✗)
|
||||
- If you composite in EndScene after all rendering → outlines are on top of everything, including walls (✗)
|
||||
- If you depth-test the mask render against the full depth buffer → walls occlude ([OK]) but players also occlude ([X])
|
||||
- If you skip depth testing → players don't occlude ([OK]) but walls also don't occlude ([X])
|
||||
- If you composite in EndScene after all rendering → outlines are on top of everything, including walls ([X])
|
||||
|
||||
**You need depth information from BEFORE players/NPCs have rendered, but AFTER terrain/WMOs have rendered.** This is only available at a specific point during the frame - when M2 model batches begin processing.
|
||||
|
||||
@@ -26,9 +26,9 @@ The depth buffer doesn't distinguish "wall pixel" from "player pixel." After a f
|
||||
|
||||
**How it satisfies the requirements:**
|
||||
|
||||
1. **Batch reordering** moves outline targets to render first in the M2 batch list. At that point, only terrain + WMO depth exists in the depth buffer. The outline pass (pass 2) uses `ZENABLE=TRUE` → terrain/WMOs occlude the outline. ✓
|
||||
2. **Stencil bit** (`STENCIL_BIT_OUTLINE=0x02`) is written where outline pixels are drawn. Subsequent player/NPC batches test against this bit and fail where outline exists → players can't paint over outlines. ✓
|
||||
3. **Dead players** use `ZENABLE=FALSE` in pass 2 → outline ignores all depth → visible through walls. ✓
|
||||
1. **Batch reordering** moves outline targets to render first in the M2 batch list. At that point, only terrain + WMO depth exists in the depth buffer. The outline pass (pass 2) uses `ZENABLE=TRUE` → terrain/WMOs occlude the outline. [OK]
|
||||
2. **Stencil bit** (`STENCIL_BIT_OUTLINE=0x02`) is written where outline pixels are drawn. Subsequent player/NPC batches test against this bit and fail where outline exists → players can't paint over outlines. [OK]
|
||||
3. **Dead players** use `ZENABLE=FALSE` in pass 2 → outline ignores all depth → visible through walls. [OK]
|
||||
|
||||
**Downsides:**
|
||||
- 3 DIP calls per outline target (stencil mark + outline + normal redraw)
|
||||
@@ -79,9 +79,9 @@ This is what the current code already does, just cleaned up.
|
||||
|
||||
Given the requirements, the 3-pass stencil with batch reordering is the only approach that works without exotic depth buffer tricks. The key properties it provides:
|
||||
|
||||
1. **Outline renders when only static-world depth exists** (via batch reordering) → walls/terrain occlude ✓
|
||||
2. **Stencil bit prevents later units from overwriting outline** → players don't occlude ✓
|
||||
3. **Per-category depth disable** → dead player outlines through walls ✓
|
||||
1. **Outline renders when only static-world depth exists** (via batch reordering) → walls/terrain occlude [OK]
|
||||
2. **Stencil bit prevents later units from overwriting outline** → players don't occlude [OK]
|
||||
3. **Per-category depth disable** → dead player outlines through walls [OK]
|
||||
|
||||
## Improvements to Implement
|
||||
|
||||
|
||||
+53
-55
@@ -49,7 +49,7 @@ Functions in rendering vtable:
|
||||
|
||||
### World Rendering Orchestration (FOUND!)
|
||||
|
||||
#### **ProcessWorldWithFrustum** ⭐ MAIN WORLD ORCHESTRATOR
|
||||
#### **ProcessWorldWithFrustum** MAIN WORLD ORCHESTRATOR
|
||||
- **Address**: `0x00683000` (called at 0x0068302f)
|
||||
- **Signature**: `void __fastcall ProcessWorldWithFrustum(float *frustumBounds)`
|
||||
- **Role**: **PRIMARY WORLD RENDERING ORCHESTRATOR** - Coordinates terrain, objects, and models
|
||||
@@ -65,8 +65,7 @@ Functions in rendering vtable:
|
||||
- **`CullObjectsToRenderList(manager, 2)`** - Cull objects type 2
|
||||
- `ProcessStaticObjectsCulling()` - Cull static world objects
|
||||
4. `PopFrustumStack()` - Restore frustum state
|
||||
5. **`CullAndProcessWorldChunks()`** - Terrain chunk rendering ⭐
|
||||
- **Notes**: This is the function that needs to be hooked for complete render order control!
|
||||
5. **`CullAndProcessWorldChunks()`** - Terrain chunk rendering - **Notes**: This is the function that needs to be hooked for complete render order control!
|
||||
|
||||
#### **CullObjectsToRenderList**
|
||||
- **Address**: `0x00683ab0`
|
||||
@@ -161,7 +160,7 @@ Functions in rendering vtable:
|
||||
|
||||
### Terrain/Water Rendering
|
||||
|
||||
#### **CullAndProcessWorldChunks** ⭐ TERRAIN RENDERER
|
||||
#### **CullAndProcessWorldChunks** TERRAIN RENDERER
|
||||
- **Address**: `0x00683040`
|
||||
- **Called by**: `ProcessWorldWithFrustum` (after object culling)
|
||||
- **Signature**: `void CullAndProcessWorldChunks(void)`
|
||||
@@ -219,7 +218,7 @@ Functions in rendering vtable:
|
||||
#### **renderMeshWithLOD**
|
||||
- **Called by**: `RenderObjectsWithLOD` (0x00684510)
|
||||
- **Role**: **ACTUAL MESH DRAWING FUNCTION** - Likely calls DrawIndexedPrimitive
|
||||
- **Status**: ⚠️ Function body not yet located - CRITICAL to find!
|
||||
- **Status**: [!] Function body not yet located - CRITICAL to find!
|
||||
- **Importance**: This is where the final D3D drawing happens
|
||||
|
||||
#### **Function Pointer Rendering (CM2Scene_DrawModelBatch)**
|
||||
@@ -283,7 +282,7 @@ WoW uses multiple linked lists to manage culled objects that need to be rendered
|
||||
- **Data Address**: `DAT_00c7cb18` + offset (indexed by renderListIndex * 0xc)
|
||||
- **Indices**: 0, 1, 2 (likely: terrain objects, WMOs, M2 models)
|
||||
- **Populated by**: `CullObjectsToRenderList` (0x00683ab0)
|
||||
- **Processed by**: Unknown (⚠️ still need to find)
|
||||
- **Processed by**: Unknown ([!] still need to find)
|
||||
|
||||
#### **Static Object Render Lists (ProcessStaticObjectsCulling)**
|
||||
- **List 1 (Normal)**:
|
||||
@@ -303,7 +302,7 @@ WoW uses multiple linked lists to manage culled objects that need to be rendered
|
||||
#### **LOD Object Render List (RenderObjectsWithLOD)**
|
||||
- **List Head**: `PTR_00c7cae0`
|
||||
- **Offset**: `PTR_00c7cad8`
|
||||
- **Populated by**: Unknown (⚠️ likely populated during culling phase)
|
||||
- **Populated by**: Unknown ([!] likely populated during culling phase)
|
||||
- **Processed by**: `RenderObjectsWithLOD` (0x00684510)
|
||||
- **Notes**: Objects are rendered with LOD (Level of Detail) based on distance
|
||||
|
||||
@@ -336,32 +335,32 @@ WoW uses multiple linked lists to manage culled objects that need to be rendered
|
||||
- **Setup**: Per-stage configuration via `SetTextureStage()`
|
||||
- **Transforms**: Applied to stages 0 and 1 during model batch rendering
|
||||
|
||||
## COMPLETE RENDERING PIPELINE DISCOVERED! ✅
|
||||
## COMPLETE RENDERING PIPELINE DISCOVERED! [OK]
|
||||
|
||||
### What We Found
|
||||
|
||||
#### ✅ **Main World Orchestrator**: `ProcessWorldWithFrustum` (0x00683000)
|
||||
#### [OK] **Main World Orchestrator**: `ProcessWorldWithFrustum` (0x00683000)
|
||||
- Coordinates all world rendering
|
||||
- Culls objects to 3 render lists (indices 0, 1, 2)
|
||||
- Renders terrain chunks last via `CullAndProcessWorldChunks`
|
||||
|
||||
#### ✅ **Terrain Renderer**: `CullAndProcessWorldChunks` (0x00683040)
|
||||
#### [OK] **Terrain Renderer**: `CullAndProcessWorldChunks` (0x00683040)
|
||||
- Primary terrain chunk culling and rendering
|
||||
- Processes ADT tiles within camera frustum
|
||||
- **Currently renders AFTER objects** (called last in ProcessWorldWithFrustum)
|
||||
|
||||
#### ✅ **Object Culling**: `CullObjectsToRenderList` (0x00683ab0)
|
||||
#### [OK] **Object Culling**: `CullObjectsToRenderList` (0x00683ab0)
|
||||
- Called 3 times with indices: 1, 0, 2
|
||||
- Likely represents: terrain objects, WMOs, and M2 models
|
||||
- Frustum culls and adds to render lists
|
||||
|
||||
#### ✅ **Static Object Culling**: `ProcessStaticObjectsCulling` (0x00683bf0)
|
||||
#### [OK] **Static Object Culling**: `ProcessStaticObjectsCulling` (0x00683bf0)
|
||||
- Handles WMO groups and static doodads
|
||||
- Respects render flags
|
||||
|
||||
### ✅ **NEW DISCOVERIES** - Render List Processing Found!
|
||||
### [OK] **NEW DISCOVERIES** - Render List Processing Found!
|
||||
|
||||
#### ⭐ **RenderObjectsWithLOD** - RENDER LIST PROCESSOR
|
||||
#### **RenderObjectsWithLOD** - RENDER LIST PROCESSOR
|
||||
- **Address**: `0x00684510`
|
||||
- **Role**: **PRIMARY RENDER LIST PROCESSOR** - Iterates through object render list and draws them
|
||||
- **Signature**: `void RenderObjectsWithLOD(void)`
|
||||
@@ -376,14 +375,13 @@ WoW uses multiple linked lists to manage culled objects that need to be rendered
|
||||
- Sets render target
|
||||
- Calls `ProcessObjectGeometry()` to prepare geometry
|
||||
- Calls `UpdateObjectPosition()` to update transform
|
||||
- **Calls `renderMeshWithLOD()` to actually draw the mesh** ⭐
|
||||
5. Checks LOD distance (`_DAT_00867958`) to cull distant objects
|
||||
- **Calls `renderMeshWithLOD()` to actually draw the mesh** 5. Checks LOD distance (`_DAT_00867958`) to cull distant objects
|
||||
6. Calls `EndRender()` to finish rendering
|
||||
- **Notes**: This is the missing link between culling and rendering!
|
||||
- **Calls**: `renderMeshWithLOD()`, `ProcessObjectGeometry()`, `UpdateObjectPosition()`
|
||||
- **Status**: No xrefs found - likely called via function pointer or vtable
|
||||
|
||||
#### ⭐ **executeRenderCommands** - WMO/STATIC OBJECT RENDERER
|
||||
#### **executeRenderCommands** - WMO/STATIC OBJECT RENDERER
|
||||
- **Called by**: `ProcessStaticObjectsCulling` (at end of function)
|
||||
- **Role**: Renders static objects (WMOs, buildings, doodads) from the culled list
|
||||
- **Implementation**: Found inside `ProcessStaticObjectsCulling` as a loop:
|
||||
@@ -399,24 +397,24 @@ WoW uses multiple linked lists to manage culled objects that need to be rendered
|
||||
|
||||
### Still To Investigate
|
||||
|
||||
#### 🟡 **Main Frame/Game Loop**
|
||||
#### [~] **Main Frame/Game Loop**
|
||||
- **Status**: Need to find what calls `ProcessWorldWithFrustum`
|
||||
- **Question**: What is the top-level orchestrator above ProcessWorldWithFrustum?
|
||||
- **Hypothesis**: Called from main game loop, possibly via callback or vtable
|
||||
- **Search Strategy**: Look for functions that reference ScenePresent vtable or main loop
|
||||
|
||||
#### 🟡 **RenderObjectsWithLOD Caller**
|
||||
#### [~] **RenderObjectsWithLOD Caller**
|
||||
- **Status**: No xrefs found to this function
|
||||
- **Question**: Where/how is `RenderObjectsWithLOD` called?
|
||||
- **Hypothesis**: Called via function pointer after ProcessWorldWithFrustum completes
|
||||
- **Likely**: Part of render list processing phase between culling and present
|
||||
|
||||
#### 🟡 **renderMeshWithLOD Implementation**
|
||||
#### [~] **renderMeshWithLOD Implementation**
|
||||
- **Status**: Called by RenderObjectsWithLOD, but function not yet located
|
||||
- **Role**: Actual mesh drawing function - likely calls DrawIndexedPrimitive
|
||||
- **Need**: Find this function to understand final rendering stage
|
||||
|
||||
#### 🟡 **Connection to M2Scene**
|
||||
#### [~] **Connection to M2Scene**
|
||||
- **Status**: M2 model rendering chain is clear, but connection to world rendering unclear
|
||||
- **Question**: How does `CM2Scene_ExecuteRenderPass` get called in relation to `ProcessWorldWithFrustum`?
|
||||
- **Need**: Find the higher-level function that calls both
|
||||
@@ -544,41 +542,41 @@ Once terrain/WMO renderers are found:
|
||||
|
||||
## Summary
|
||||
|
||||
### ✅ MAJOR PROGRESS - Rendering Pipeline Nearly Complete!
|
||||
### [OK] MAJOR PROGRESS - Rendering Pipeline Nearly Complete!
|
||||
|
||||
This analysis has successfully discovered most of the WoW rendering architecture:
|
||||
|
||||
#### **Primary Discoveries**
|
||||
1. **✅ World Orchestrator**: `ProcessWorldWithFrustum` (0x00683000)
|
||||
1. **[OK] World Orchestrator**: `ProcessWorldWithFrustum` (0x00683000)
|
||||
- Coordinates frustum culling for all world objects
|
||||
- Processes 32 object managers with 3 render list types
|
||||
- Calls terrain renderer last
|
||||
|
||||
2. **✅ Terrain Renderer**: `CullAndProcessWorldChunks` (0x00683040)
|
||||
2. **[OK] Terrain Renderer**: `CullAndProcessWorldChunks` (0x00683040)
|
||||
- Primary ADT terrain chunk culling and rendering
|
||||
- Called AFTER object culling (currently renders terrain depth last)
|
||||
|
||||
3. **✅ Object Culling**: `CullObjectsToRenderList` (0x00683ab0)
|
||||
3. **[OK] Object Culling**: `CullObjectsToRenderList` (0x00683ab0)
|
||||
- Frustum culls objects to 3 render lists (indices: 1, 0, 2)
|
||||
- Adds to global render lists at PTR_00c7cb14/PTR_00c7cb18
|
||||
- Likely: terrain objects, WMOs, M2 models
|
||||
|
||||
4. **✅ Static Culling**: `ProcessStaticObjectsCulling` (0x00683bf0)
|
||||
4. **[OK] Static Culling**: `ProcessStaticObjectsCulling` (0x00683bf0)
|
||||
- Handles WMO groups and static doodads
|
||||
- **Contains inline WMO renderer** (`executeRenderCommands` loop at end)
|
||||
- Adds to render lists at PTR_00c7cadc/PTR_00c7cb54
|
||||
|
||||
5. **⭐ NEW - Render List Processor**: `RenderObjectsWithLOD` (0x00684510)
|
||||
5. **NEW - Render List Processor**: `RenderObjectsWithLOD` (0x00684510)
|
||||
- **CRITICAL DISCOVERY**: This processes the culled object render list!
|
||||
- Iterates through objects at PTR_00c7cae0
|
||||
- Calls `renderMeshWithLOD()` for actual drawing
|
||||
- Includes LOD distance culling
|
||||
|
||||
6. **✅ M2 Model Pipeline**: Complete chain from `CM2Scene_ExecuteRenderPass` → `CM2SceneRenderDraw` → `RenderBatches` → `CM2Scene_DrawModelBatch`
|
||||
6. **[OK] M2 Model Pipeline**: Complete chain from `CM2Scene_ExecuteRenderPass` → `CM2SceneRenderDraw` → `RenderBatches` → `CM2Scene_DrawModelBatch`
|
||||
|
||||
7. **✅ Render Lists Mapped**: Documented 7+ global render list structures and their usage
|
||||
7. **[OK] Render Lists Mapped**: Documented 7+ global render list structures and their usage
|
||||
|
||||
### 🎯 Updated Rendering Flow
|
||||
### Updated Rendering Flow
|
||||
|
||||
Based on discoveries, the likely flow is:
|
||||
|
||||
@@ -597,7 +595,7 @@ Based on discoveries, the likely flow is:
|
||||
- Cursor overlay
|
||||
- ISceneEnd → D3D Present
|
||||
|
||||
### 🎯 Key Insight for Selective Occlusion
|
||||
### Key Insight for Selective Occlusion
|
||||
|
||||
The render list processing happens AFTER culling. To control occlusion:
|
||||
|
||||
@@ -613,10 +611,10 @@ The render list processing happens AFTER culling. To control occlusion:
|
||||
4. Normal objects/players
|
||||
|
||||
### Remaining Critical Questions
|
||||
- ⚠️ **What calls RenderObjectsWithLOD?** (no xrefs found - likely function pointer or callback system)
|
||||
- ✅ **Found rendering mechanism**: Function pointer at `renderContext+0x40+0x11c` in CM2Scene_DrawModelBatch performs actual D3D draw
|
||||
- ⚠️ **Main game loop?** (what calls ProcessWorldWithFrustum? - no direct callers found, likely callback/vtable)
|
||||
- ⚠️ **Render order?** (do objects render before or after terrain?)
|
||||
- [!] **What calls RenderObjectsWithLOD?** (no xrefs found - likely function pointer or callback system)
|
||||
- [OK] **Found rendering mechanism**: Function pointer at `renderContext+0x40+0x11c` in CM2Scene_DrawModelBatch performs actual D3D draw
|
||||
- [!] **Main game loop?** (what calls ProcessWorldWithFrustum? - no direct callers found, likely callback/vtable)
|
||||
- [!] **Render order?** (do objects render before or after terrain?)
|
||||
|
||||
### Latest Discoveries (Session 2)
|
||||
|
||||
@@ -639,7 +637,7 @@ We now have MOST of the critical hook points for selective occlusion! The missin
|
||||
|
||||
---
|
||||
|
||||
## 🎯 COMPLETE HOOK POINT MAPPING (Session 3)
|
||||
## COMPLETE HOOK POINT MAPPING (Session 3)
|
||||
|
||||
### Terrain Rendering Pipeline
|
||||
**Culling**: `CullAndProcessWorldChunks` (0x00683040)
|
||||
@@ -702,7 +700,7 @@ We now have MOST of the critical hook points for selective occlusion! The missin
|
||||
|
||||
---
|
||||
|
||||
## 📋 RECOMMENDED HOOK STRATEGY FOR SELECTIVE OCCLUSION
|
||||
## RECOMMENDED HOOK STRATEGY FOR SELECTIVE OCCLUSION
|
||||
|
||||
### Option A: High-Level (Easiest)
|
||||
Hook `ProcessWorldWithFrustum` (0x00683000) to reorder rendering:
|
||||
@@ -730,27 +728,27 @@ Hook D3D device:
|
||||
|
||||
---
|
||||
|
||||
## ✅ FINAL DISCOVERY STATUS
|
||||
## [OK] FINAL DISCOVERY STATUS
|
||||
|
||||
### Completed ✅
|
||||
- ✅ Terrain culling function (CullAndProcessWorldChunks)
|
||||
- ✅ WMO culling AND rendering (ProcessStaticObjectsCulling with inline rendering)
|
||||
- ✅ Object culling (CullObjectsToRenderList)
|
||||
- ✅ Object processing (RenderObjectsWithLOD)
|
||||
- ✅ Function pointer rendering system (CM2Scene_DrawModelBatch)
|
||||
- ✅ Main world orchestrator (ProcessWorldWithFrustum)
|
||||
- ✅ Callback registration system (UpdateRenderCallback)
|
||||
- ✅ All render list structures mapped (7+ globals)
|
||||
### Completed [OK]
|
||||
- [OK] Terrain culling function (CullAndProcessWorldChunks)
|
||||
- [OK] WMO culling AND rendering (ProcessStaticObjectsCulling with inline rendering)
|
||||
- [OK] Object culling (CullObjectsToRenderList)
|
||||
- [OK] Object processing (RenderObjectsWithLOD)
|
||||
- [OK] Function pointer rendering system (CM2Scene_DrawModelBatch)
|
||||
- [OK] Main world orchestrator (ProcessWorldWithFrustum)
|
||||
- [OK] Callback registration system (UpdateRenderCallback)
|
||||
- [OK] All render list structures mapped (7+ globals)
|
||||
|
||||
### Architecture Understanding ✅
|
||||
- ✅ WoW uses callback-based rendering (no direct function calls found)
|
||||
- ✅ Rendering is event-driven through vtables and function pointers
|
||||
- ✅ Terrain rendering likely invoked through callback (not found statically)
|
||||
- ✅ Static analysis limited by callback architecture
|
||||
### Architecture Understanding [OK]
|
||||
- [OK] WoW uses callback-based rendering (no direct function calls found)
|
||||
- [OK] Rendering is event-driven through vtables and function pointers
|
||||
- [OK] Terrain rendering likely invoked through callback (not found statically)
|
||||
- [OK] Static analysis limited by callback architecture
|
||||
|
||||
### Remaining Unknowns (Acceptable)
|
||||
- ⚠️ Exact terrain render list processor (likely callback - can find at runtime)
|
||||
- ⚠️ Main game loop that invokes ProcessWorldWithFrustum (callback system)
|
||||
- ⚠️ Exact render order (can determine through runtime hooking)
|
||||
- [!] Exact terrain render list processor (likely callback - can find at runtime)
|
||||
- [!] Main game loop that invokes ProcessWorldWithFrustum (callback system)
|
||||
- [!] Exact render order (can determine through runtime hooking)
|
||||
|
||||
**Conclusion**: We have ALL the hook points needed for selective occlusion implementation. Runtime debugging can fill in the remaining details if needed.
|
||||
|
||||
+14
-14
@@ -120,11 +120,11 @@ pDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
|
||||
### Implementation Status [COMPLETED]
|
||||
|
||||
All objectives achieved - see "Working Implementation" at end of document:
|
||||
- ✅ Hook DrawIndexedPrimitive to detect corpse/target/raid-marked models
|
||||
- ✅ Stencil-based outline rendering in EndScene
|
||||
- ✅ Custom vertex shader for bone-animated outline expansion
|
||||
- ✅ Per-category visual effects (dark halo for dead, colored outline for marks/target)
|
||||
- ✅ Through-wall visibility with proper body/outline layering
|
||||
- [OK] Hook DrawIndexedPrimitive to detect corpse/target/raid-marked models
|
||||
- [OK] Stencil-based outline rendering in EndScene
|
||||
- [OK] Custom vertex shader for bone-animated outline expansion
|
||||
- [OK] Per-category visual effects (dark halo for dead, colored outline for marks/target)
|
||||
- [OK] Through-wall visibility with proper body/outline layering
|
||||
|
||||
### D3D9 "Chams" / Wallhack Technique
|
||||
|
||||
@@ -1780,9 +1780,9 @@ c7: 0.000 1.000 0.000 0.000 <- Y identity
|
||||
**Attempted:** Scale c0.x, c1.y, c2.x, c3.y by `g_outlineThickness` (1.2x)
|
||||
|
||||
**Result:**
|
||||
- Model silhouette appears larger ✓
|
||||
- But silhouette MOVES when view angle changes ✗
|
||||
- Silhouette covers corpse instead of outlining it ✗
|
||||
- Model silhouette appears larger [OK]
|
||||
- But silhouette MOVES when view angle changes [X]
|
||||
- Silhouette covers corpse instead of outlining it [X]
|
||||
|
||||
**Root Cause:** Scaling these projection-space constants distorts the view transformation, not the model. The model center isn't at origin in view space, so scaling pushes it in different directions based on camera angle.
|
||||
|
||||
@@ -2260,9 +2260,9 @@ Self-outline prevention: Local player is excluded from target and raid mark outl
|
||||
|
||||
### Status
|
||||
|
||||
- ✅ Full bone transform shader working
|
||||
- ✅ Stencil-based outline rendering
|
||||
- ✅ Per-category visual effects (outline vs dark halo)
|
||||
- ✅ Distance-based thickness scaling with min/max clamping
|
||||
- ✅ Through-wall visibility
|
||||
- ✅ Self-outline prevention
|
||||
- [OK] Full bone transform shader working
|
||||
- [OK] Stencil-based outline rendering
|
||||
- [OK] Per-category visual effects (outline vs dark halo)
|
||||
- [OK] Distance-based thickness scaling with min/max clamping
|
||||
- [OK] Through-wall visibility
|
||||
- [OK] Self-outline prevention
|
||||
|
||||
@@ -47,7 +47,7 @@ Consider alternative approaches:
|
||||
- **Screen-space dilation** (iterative morphological expand of silhouette) - simpler, no distance field needed
|
||||
- **Gaussian blur difference** - blur silhouette, subtract original, threshold
|
||||
- **Sobel/edge detection** on the silhouette RT
|
||||
- Docs in `/media/storage/projects/zig/weirdutils/docs/` describe these alternatives
|
||||
- Docs in `docs/` describe these alternatives
|
||||
- Reference articles: ameye.dev "5 ways to draw an outline", Ben Golus "Quest for Very Wide Outlines"
|
||||
|
||||
## Key Files
|
||||
@@ -58,7 +58,7 @@ Consider alternative approaches:
|
||||
- `reference/c_overlay/d3d9_hook.cpp` - C reference (uses 3-pass shell extrusion, not JFA)
|
||||
|
||||
## Build / Environment
|
||||
- `zig build` from `/media/storage/projects/zig/weirdutils/`
|
||||
- `zig build` from the repo root
|
||||
- Zig 0.15, target x86-windows-msvc (32-bit DLL injected into WoW 3.3.5)
|
||||
- Linux host (Arch, kernel 6.16.1), game via Wine/DXVK
|
||||
- Git last commit: `661e156` (game object occlusion fix)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Outline Port Notes
|
||||
|
||||
Pure Zig reimplementation of the WoW 1.12.1 unit outline system, ported from the Idris C/C++ reference at `/media/storage/projects/idris/dlls`.
|
||||
Pure Zig reimplementation of the WoW 1.12.1 unit outline system, ported from the earlier Idris C/C++ reference implementation (`reference/c_overlay/`, not included in this repo).
|
||||
|
||||
## Hooked Functions
|
||||
|
||||
|
||||
Reference in New Issue
Block a user