fix(vfx): classify hardwareless particle emitters once
This commit is contained in:
parent
921712f412
commit
5b3fb17775
8 changed files with 374 additions and 18 deletions
|
|
@ -207,6 +207,15 @@ anchors: `CPhysicsPart::GetMaxDegradeDistance` `0x0050D510`,
|
|||
`CPhysicsObj::ShouldDrawParticles` `0x0050FE60`, and
|
||||
`ParticleEmitter::UpdateParticles` `0x0051D180`.
|
||||
|
||||
Hardwareless ParticleEmitterInfo records are also retained exactly. Retail
|
||||
`ParticleEmitter::SetInfo @ 0x0051CE90` returns false when
|
||||
`hw_gfxobj_id == INVALID_DID`; it does not substitute the software GfxObj.
|
||||
`EmitterDescRegistry` negative-caches that authored outcome and the hook sink
|
||||
reports it once per DAT ID rather than once per owner. This is classification
|
||||
and bounded diagnostics around the existing `DatCollection`, not a fallback
|
||||
reader or invented VFX. Evidence:
|
||||
`docs/research/2026-07-26-retail-hardwareless-particle-emitter-diagnostics.md`.
|
||||
|
||||
**Retail shared world-alpha seam (2026-07-18).** WorldBuilder's editor
|
||||
renderers classify translucent mesh batches correctly, but they have no live
|
||||
retail `CPartCell`/`CShadowPart` list and render particles in a separate
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
# Retail hardwareless particle-emitter resolution
|
||||
|
||||
Date: 2026-07-26
|
||||
Oracle: matching Sept-2013 `acclient.exe` + `acclient.pdb`
|
||||
|
||||
## Symptom
|
||||
|
||||
Dense scenes repeatedly logged that ParticleEmitterInfo records such as
|
||||
`0x320002D6` and `0x320003B6`–`0x320003B9` were “not found” for many different
|
||||
owners. The records were present in the installed DAT, so the message was
|
||||
misleading and each owner repeated the same failed resolution work.
|
||||
|
||||
## Named-retail behavior
|
||||
|
||||
`ParticleEmitter::SetInfo @ 0x0051CE90` begins with:
|
||||
|
||||
```text
|
||||
Destroy()
|
||||
info = suppliedInfo
|
||||
|
||||
if info.hw_gfxobj_id == INVALID_DID:
|
||||
Destroy()
|
||||
return false
|
||||
|
||||
particleObject = makeParticleObject(info.max_particles, info.sorting_sphere)
|
||||
...
|
||||
```
|
||||
|
||||
`ParticleEmitter::SetInfo(DataID) @ 0x0051D3C0` first loads the
|
||||
ParticleEmitterInfo and delegates to the overload above.
|
||||
`ParticleManager::CreateParticleEmitter @ 0x0051B6C0` destroys the attempted
|
||||
emitter when either `SetInfo` or parenting fails. Retail does not substitute
|
||||
the software `gfxobj_id` for an absent hardware GfxObj.
|
||||
|
||||
## Installed-DAT audit
|
||||
|
||||
The production `DatCollection` and `EmitterDescRegistry` were exercised
|
||||
through `ProjectileVfxAudit --emitters`:
|
||||
|
||||
| Emitter | Record | Software GfxObj | Hardware GfxObj | Retail outcome |
|
||||
|---|---|---:|---:|---|
|
||||
| `0x320002D6` | present/parsed | present | `0` | skip |
|
||||
| `0x320003B6` | present/parsed | present | `0` | skip |
|
||||
| `0x320003B7` | present/parsed | present | `0` | skip |
|
||||
| `0x320003B8` | present/parsed | present | `0` | skip |
|
||||
| `0x320003B9` | present/parsed | present | `0` | skip |
|
||||
|
||||
These are authored hardwareless records, not missing DAT files.
|
||||
|
||||
## Cross-reference
|
||||
|
||||
- ACViewer `ParticleEmitter.SetInfo` returns false when `HWGfxObjID == 0`.
|
||||
Its render initialization explicitly comments that this case is expected and
|
||||
skips the absent emitter.
|
||||
- WorldBuilder contains no contradictory live-client hardware-emitter
|
||||
fallback. Its particle code is an editor/rendering reference, while the
|
||||
retail client remains the behavior oracle.
|
||||
|
||||
## Runtime contract
|
||||
|
||||
`EmitterDescRegistry` now stores either one resolved descriptor or one
|
||||
classified negative result per DAT ID:
|
||||
|
||||
```text
|
||||
MissingEmitterInfo
|
||||
InvalidHardwareGfxObjId
|
||||
MissingHardwareGfxObj
|
||||
```
|
||||
|
||||
The particle hook sink reports the first failure for each emitter DAT ID, not
|
||||
one line per owner. Hardwareless records say that retail skipped them rather
|
||||
than claiming the ParticleEmitterInfo was absent. No fallback asset or
|
||||
particle is fabricated.
|
||||
|
||||
Registration of an explicit runtime descriptor clears a cached negative
|
||||
result, preserving test/tool injection and future hot-reload behavior.
|
||||
|
||||
## Conformance coverage
|
||||
|
||||
- `EmitterDescRegistry_NegativeCachesMissingDatAndRegisterClearsFailure`
|
||||
- `EmitterDescRegistry_ClassifiesAndCachesAuthoredHardwarelessEmitter`
|
||||
- `MissingEmitterAsset_ReportsOnlyOnceAcrossManyOwners`
|
||||
Loading…
Add table
Add a link
Reference in a new issue