победа над водопадами, шейдеры m2
This commit is contained in:
@@ -243,6 +243,140 @@ src/tools/m2_to_gltf.py
|
||||
- WoW material flags не полностью совпадают;
|
||||
- billboards/leaf bones пока не как в клиенте.
|
||||
|
||||
### M2 Animation Research
|
||||
|
||||
Проверка по `reference/whoa`, `reference/open-realm` и `reference/wow.export` показала, что анимации M2 нельзя надежно тащить как обычный Godot/glTF skeletal mesh.
|
||||
|
||||
Оригинальная схема M2:
|
||||
|
||||
- для каждой кости считается матрица через pivot transform: translation/rotation/scale вокруг `bone.pivot`;
|
||||
- parent chain считается вручную;
|
||||
- каждый skin section использует свой bone palette через `bone_combo_index`;
|
||||
- vertex bone indices интерпретируются относительно palette текущего skin section.
|
||||
|
||||
Именно так делают:
|
||||
|
||||
- `reference/whoa/src/model/CM2Model.cpp` - расчет `m_boneMatrices`;
|
||||
- `reference/whoa/src/model/CM2SceneRender.cpp` - upload bone palette из `boneCombos`;
|
||||
- `reference/open-realm/games/world-of-warcraft/renderer/m2/r_m2.c` - `M2_CalculateBoneMatrices` и `M2_UploadBatchBones`;
|
||||
- `reference/wow.export/src/js/3D/renderers/M2RendererGL.js` - ручной pivot-based runtime skinning.
|
||||
|
||||
Вывод: старый экспериментальный путь через cached GLB ломал композитные doodad M2, потому что анимировал node с bind-pivot translation напрямую. Конвертер переведен на схему `pivot node -> animated bone node`, аналогичную wow.export `modelsExportWithBonePrefix`.
|
||||
|
||||
Runtime принимает для M2-анимаций только GLB с меткой:
|
||||
|
||||
```text
|
||||
asset.extras.openwc_m2_anim_schema = pivot_prefix_v1
|
||||
```
|
||||
|
||||
Это защищает мир от старых битых GLB cache. Точечно пересобраны тестовые animated M2:
|
||||
|
||||
```text
|
||||
data/cache/m2_glb/GryphonRoost01.glb
|
||||
data/cache/m2_glb/Fish.glb
|
||||
data/cache/m2_glb/eagle.glb
|
||||
```
|
||||
|
||||
Ограничение runtime:
|
||||
|
||||
```text
|
||||
enable_m2_animated_instances = true
|
||||
m2_animated_max_primitives = 3
|
||||
m2_animated_denylist_patterns = ["gryphonroost"]
|
||||
m2_animated_allowlist_patterns = ["creature/fish/", "creature/eagle/", "world/critter/"]
|
||||
```
|
||||
|
||||
`GryphonRoost` stays out of the animated GLB path. The cached GLB has `Stand`
|
||||
tracks, but the current converter/runtime breaks its composite skin/material layout.
|
||||
Use the native M2 runtime path for this class of doodad; the old GLB animation
|
||||
path remains disabled for `gryphonroost`.
|
||||
|
||||
Native M2 animation first pass for composite doodads:
|
||||
|
||||
- `M2Loader.load_m2_animated()` parses M2 bones, Stand track keys, `.skin` bone lookup/palette data, and expanded skinned surfaces.
|
||||
- `M2NativeAnimatedBuilder` builds a regular `ArrayMesh` and keeps the original per-surface skin data.
|
||||
- `M2NativeAnimator` evaluates the selected Stand sequence and applies WoW-style bone matrices:
|
||||
`T(pivot + translation) * R * S * T(-pivot) * parent`.
|
||||
- Vertex influences are resolved through `.skin` local bone indices and the M2 `boneCombos` palette, matching the original section/batch renderer model used by WoW/whoa.
|
||||
- `StreamingWorldLoader` routes only `gryphonroost` through this native path; simple critters still use the existing GLB allowlist and most M2 world props stay on static MultiMesh.
|
||||
- Current implementation rebuilds the animated `ArrayMesh` on CPU. This is correct enough for the problematic rare doodads and gives us the same data layout that can later move to shader/GPU skinning.
|
||||
|
||||
Причина: композитные doodad вроде `GryphonRoost` снова ломают визуал при GLB-анимации. До M2-native renderer все world placement M2 должны оставаться статическими. GLB-анимация оставлена только как вручную включаемый debug experiment через allowlist.
|
||||
|
||||
Дополнительно static M2 cache lookup ищет both original-case and lowercase paths. Это важно для ADT paths вроде `world/GENERIC/...`, потому что запеченный nested cache лежит lowercase. Static path также не использует GLB с `pivot_prefix_v1`, чтобы animated test cache не мог подменить корректный `.tscn`.
|
||||
|
||||
Полноценный M2-native animation renderer/cache все еще остается правильным долгосрочным решением для 1:1 поведения, particles/ribbons и производительности на большом количестве animated props.
|
||||
|
||||
### Blizzlike Shader Research
|
||||
|
||||
Идея для следующего visual fidelity слоя: уходить от Godot `StandardMaterial3D` для WoW assets к собственным WoW-style `ShaderMaterial`.
|
||||
|
||||
Что видно по whoa:
|
||||
|
||||
- M2 использует маленькие shader permutations вроде `Diffuse_T1`, `Diffuse_T2`, `Diffuse_Env`, `Combiners_Mod`, `Combiners_Add`, `Combiners_Mod2x`;
|
||||
- material state задается отдельно: blend mode, alpha ref, culling, depth test/write;
|
||||
- light model старый и ручной: ambient + diffuse + emissive + fog constants, а не PBR;
|
||||
- texture stages учитывают wrap/clamp, texture transform matrices и env/sphere map.
|
||||
|
||||
Практический вывод для проекта:
|
||||
|
||||
- terrain уже ближе к WoW, потому что использует `ShaderMaterial` и ручной light/fog;
|
||||
- M2/WMO пока используют `StandardMaterial3D`, поэтому выглядят не совсем blizzlike;
|
||||
- следующий шаг - `wow_m2_material` / `wow_wmo_material` с WoW blend modes, alpha ref, combiners и ручным fog/light.
|
||||
|
||||
### M2 ShaderMaterial First Pass
|
||||
|
||||
Implemented file: `addons/mpq_extractor/loaders/wow_m2_material.gd`.
|
||||
|
||||
`M2Builder._build_material()` now returns a WoW-style `ShaderMaterial` instead of `StandardMaterial3D`. The same path is reused by `M2NativeAnimatedBuilder`, so static M2 batches and the native animated `gryphonroost` path share material behavior.
|
||||
|
||||
Current M2 shader layer:
|
||||
|
||||
- maps M2 blend modes 0..6 to Godot shader variants: opaque, alpha-key, alpha blend, additive, mod and mod2x approximation;
|
||||
- uses alpha reference values observed in `reference/whoa/src/model/CM2SceneRender.cpp`: 0 for opaque, about 0.878 for alpha-key, 1/255 for blended passes;
|
||||
- applies M2 render flags for unlit (`0x01`), two-sided/culling (`0x04`), depth test (`0x08`) and depth write (`0x10`);
|
||||
- keeps BLP mipmap generation and anisotropic sampler hints;
|
||||
- respects M2 texture wrap flags by compiling repeat/clamp shader variants;
|
||||
- `M2Loader` exposes UV2, `texture_coord_combos`, `texture_combiner_combos`, `m2_flags`, batch `shader_id`, `texture_count` and texture-coordinate combo indices;
|
||||
- `M2Loader` also exposes `m2_colors`, `texture_weights`, `texture_transforms`, `texture_weight_combos` and `texture_transform_combos`;
|
||||
- `M2Builder` now resolves the first two texture stages from texture combos and passes combiner state into `WowM2Material`;
|
||||
- `WowM2Material` has a first-pass implementation for combiner ops: opaque, mod, decal/fade, add, mod2x, mod2xNA/addNA and a cheap env-map UV approximation;
|
||||
- static M2 color/alpha tracks and texture weight tracks are applied to `mesh_color`, matching the `whoa` render path where batch alpha is multiplied by color alpha and texture weight;
|
||||
- static texture transform translation/scale is applied per texture stage through shader UV transform uniforms;
|
||||
- animated texture transform translation tracks are approximated as GPU UV scroll (`translation_speed * TIME`) per texture stage, without per-frame CPU material updates;
|
||||
- native animated M2 rebuild path preserves UV2 and the same material combiner state.
|
||||
- uses a simple old-client-style ambient + diffuse light model and disables PBR/specular behavior.
|
||||
|
||||
Known limits:
|
||||
|
||||
- only the first two texture stages are represented;
|
||||
- texture transform translation animation is supported as linear UV scroll; rotation/scale texture animation and time-varying color/weight tracks are still future work;
|
||||
- exact env/sphere map math, model emissive tracks and full WoW render-state parity are still future work;
|
||||
- WMO has a matching first-pass shader layer, but still needs full material flag parity.
|
||||
|
||||
### WMO ShaderMaterial First Pass
|
||||
|
||||
Implemented file: `addons/mpq_extractor/loaders/wow_wmo_material.gd`.
|
||||
|
||||
`WMOBuilder._build_material()` now returns a WoW-style `ShaderMaterial` instead of `StandardMaterial3D`. WMO group meshes already carry `Mesh.ARRAY_COLOR` from group vertex colors, so the shader treats WMO as mostly baked-lit geometry:
|
||||
|
||||
- blend modes 0..2 map to opaque, alpha-key and alpha blend variants;
|
||||
- alpha-key uses discard instead of transparent sorting;
|
||||
- texture repeat stays enabled for WMO wall/floor tiling;
|
||||
- BLP mipmap generation and anisotropic sampler hints are preserved;
|
||||
- shader is `unshaded` and multiplies texture color by WMO vertex color with a small ambient floor;
|
||||
- DBC zone light is applied as a soft tint only, so baked WMO vertex colors and texture albedo stay readable;
|
||||
- `color2` is passed as a secondary/detail tint, and `texture2` is used as a fallback detail texture when `texture1` is absent;
|
||||
- `WMOLoader` exposes MOMT `diffuse_color`, `emissive_color`, `texture1`, `texture2`, `flags2` and `color2`;
|
||||
- `WMOBuilder` loads `texture1` as a conservative secondary detail/multiply layer and passes MOMT diffuse/emissive colors into the shader;
|
||||
- culling is disabled like the previous WMO material path, which avoids missing interior/backface geometry.
|
||||
|
||||
Known limits:
|
||||
|
||||
- deeper MOMT shader semantics, exact secondary texture mode and `texture2` usage are still approximate;
|
||||
- WMO fog/light constants are not wired into this material yet;
|
||||
- this first pass prioritizes matching old-client baked lighting behavior over physical lighting.
|
||||
|
||||
## WMO Rendering
|
||||
|
||||
Цель: WMO не должны фризить сцену при догрузке, особенно большие здания/города.
|
||||
@@ -324,6 +458,31 @@ addons/mpq_extractor/loaders/wow_liquid_material.gd
|
||||
- LiquidType.dbc texture selection пока не используется;
|
||||
- waterfalls/flowing water требуют отдельного M2/material path.
|
||||
|
||||
## Shared WoW Shader Globals
|
||||
|
||||
Current terrain/M2/WMO/liquid shaders are intentionally `unshaded`: Godot lighting is not a good match for old WoW material rules, M2 blend modes, WMO baked vertex colors and control-splat terrain. To keep them visually tied to the active zone without updating every material instance, `WowSkyController` owns a shared global shader state:
|
||||
|
||||
```text
|
||||
wow_ambient_color
|
||||
wow_light_color
|
||||
wow_light_dir
|
||||
wow_fog_color
|
||||
wow_fog_range
|
||||
wow_fog_density
|
||||
wow_sun_elevation
|
||||
```
|
||||
|
||||
These parameters are registered through `RenderingServer.global_shader_parameter_add` and updated from `Light.dbc` / `LightParams.dbc` sampling in `src/scenes/sky/wow_sky_controller.gd`.
|
||||
|
||||
Consumers:
|
||||
|
||||
- `addons/mpq_extractor/loaders/adt_builder.gd`: control-splat, fallback layered terrain and single-texture terrain.
|
||||
- `addons/mpq_extractor/loaders/wow_m2_material.gd`: M2 combiner material uses zone ambient/diffuse light and distance fog.
|
||||
- `addons/mpq_extractor/loaders/wow_wmo_material.gd`: WMO material keeps baked vertex lighting, but tints it by zone light and applies distance fog.
|
||||
- `addons/mpq_extractor/loaders/wow_liquid_material.gd`: liquids use the shared fog color/range/density.
|
||||
|
||||
The fog distance is computed from view-space position passed from `vertex()` to `fragment()`. This avoids per-camera material updates and keeps shader code compatible with Godot 4.6's spatial shader parser.
|
||||
|
||||
## Sky And Lighting
|
||||
|
||||
Добавлен `WowSkyController`:
|
||||
@@ -451,7 +610,7 @@ res://data/cache/wmo_render_v1
|
||||
Baked terrain required: 4
|
||||
Splat terrain required: 1
|
||||
Control splat required: 3
|
||||
WMO streaming resource: 1
|
||||
WMO streaming resource: 2
|
||||
```
|
||||
|
||||
## Debug Logs
|
||||
@@ -532,7 +691,7 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
|
||||
|
||||
- M2 animation/skinning.
|
||||
- M2 particles/ribbons.
|
||||
- Full WoW material flag parity.
|
||||
- Full WoW material flag parity. Current first pass covers common M2 combiners and WMO shader ids 0, 3, 5, 6, 8, 9, 12, 13, 15, 16, 17 approximately.
|
||||
- True WMO portal/room culling.
|
||||
- Selective cheap dynamic shadows.
|
||||
- Liquid depth fade/shore blending.
|
||||
@@ -540,6 +699,42 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
|
||||
- Better skybox transition validation per zone.
|
||||
- Automated renderer regression scenes/screenshots.
|
||||
|
||||
## 2026-07-04 WMO Shader Pass
|
||||
|
||||
- WMO materials now consume shared `wow_light_dir` and `wow_sun_elevation` shader globals.
|
||||
- Cached WMO meshes are refreshed at attach time, so already baked render-cache meshes can receive the latest WMO material shader without immediately rebuilding the whole WMO cache.
|
||||
- The WMO shader now uses normals for a cheap WoW-style ambient + directional lighting term instead of relying only on texture * vertex color.
|
||||
- Added approximate pixel shader branches for common WMO modes: diffuse, env, env metal, two-layer diffuse, terrain-style two-layer, diffuse emissive and mod2x variants.
|
||||
- Still missing for true parity: second/third WMO vertex color streams, extra WMO texture coordinate sets, third+ texture samplers, exact env-map coordinate generation and portal/room visibility.
|
||||
|
||||
## 2026-07-04 M2 Shader Mapping Pass
|
||||
|
||||
- M2 material build now maps raw skin `shaderID` through the same vertex/pixel shader selection rules used by `wow.export`.
|
||||
- `WowM2Material` receives `vertex_shader_id` and `pixel_shader_id` uniforms instead of relying only on generic two-stage ops.
|
||||
- Vertex shader ids 0..18 now drive UV source selection, UV2 use, environment coordinates and edge-fade modes.
|
||||
- Pixel shader ids 0..29 have first-pass two-texture combiner behavior for the common opaque/mod/add/mod2x/alpha cases.
|
||||
- M2 materials now load and bind up to four texture combo entries. This enables first-pass support for glow/specular/crossfade/guild/const combiner cases that need `tex3` or `tex4`.
|
||||
- Texture weight combos are exposed to the shader as `tex_sample_alpha`, matching the common WoW combiner weight slots.
|
||||
- Native animated M2 surfaces use the same `_build_material()` path, so this also applies to runtime-animated M2 objects.
|
||||
- Debug metadata is stored on materials: raw `wow_m2_shader_id`, mapped `wow_m2_vertex_shader_id`, and mapped `wow_m2_pixel_shader_id`.
|
||||
- Transparent M2 passes such as waterfalls now avoid depth writes/prepass (`depth_draw_never`). This prevents layered alpha planes from self-occluding into white/solid webbing while keeping cutout foliage on the opaque-depth path.
|
||||
- Scrolling alpha-blend M2 models with `waterfall` in the path get a conservative soft-alpha profile: lower opacity, stronger alpha falloff and UV-edge fade. This hides the rectangular card edges without changing normal foliage/cutout props.
|
||||
- `.skin` texture unit `priority` is exposed as `priority_plane` for future transparent-pass ordering. This needs the native GDExtension DLL to be rebuilt when the editor is not holding it open.
|
||||
- Waterfalls seen near terrain cliffs are usually WMO embedded doodads rather than ADT `MDDF` placements. WMO doodad transforms now use the converted MODD quaternion directly, without the old extra `+90 deg` M2 yaw offset.
|
||||
- WMO render/scene caches now carry version guards for this transform path. Rebuild `wmo_render_v1`/`wmo_tscn` after changing WMO doodad placement code; old caches are ignored by runtime.
|
||||
- Elwynn/Thunder Falls style outdoor waterfalls are ADT `MDDF` M2 placements. Regular ADT M2 doodads use the native cached Godot-space Euler directly via `Basis.from_euler(rot)`; do not apply a global yaw/order remap, because it flips ordinary props such as wagons, stalls, barrels, fences and trees. `ElwynnCliffRock01/02` are a model-specific exception: these are open-backed shell meshes, so they use a narrow `YZX` placement correction with `rot.y + 180 deg` to push the hollow side into the terrain. The waterfall transform correction is also filename-allowlisted to the actual sheet M2s (`NewWaterfall`, `ElwynnTallWaterfall01`). Sheet M2s keep the calibrated world-space `+90 deg` Godot-Y yaw to anchor the top to the upper pool, plus model-specific twist where needed. Tall waterfall sheets twist around their local fall line with an origin offset so top/bottom anchors stay fixed instead of rotating around the model pivot; `ElwynnTallWaterfall01` uses the opposite twist sign from `NewWaterfall` to face the correct side. Do not apply the raw-M2/WoWee render-space basis here; that double-converts M2 vertices. Do not cull boundary waterfall placements by `floor(world_pos / TILE_SIZE)`: tiles can carry a small cross-border placement that is still the active streamed instance at low M2 radius. Future cross-tile dedupe should use `MDDF.uniqueId` plus an ADT cache format bump/rebake.
|
||||
- Still missing for true parity: exact shader array entries beyond the common WotLK set, particles/ribbons, full edge-scan tuning, exact guild/color constants and specialized blend-state edge cases.
|
||||
|
||||
## 2026-07-07 World Shader Light Stabilization
|
||||
|
||||
- DBC sky colors remain the source for `Environment` and sky rendering, but material shader globals now receive desaturated/neutralized ambient, diffuse and fog colors. This prevents Elwynn daylight profiles from tinting all ADT/M2/WMO albedo cyan or green.
|
||||
- ADT terrain shaders now transform normals with `MODEL_NORMAL_MATRIX` before comparing them against the world-space `wow_light_dir`. Using fragment `NORMAL` directly made terrain lighting depend on the local surface basis and produced blue/green patches on slopes and roads.
|
||||
- ADT, M2 and WMO material fog clamps `wow_fog_density` before mixing into `ALBEDO`; fog should add distance haze, not repaint nearby assets.
|
||||
- M2/WMO shaders now reduce zone-color strength and clamp final albedo/specular/emissive contribution. Additive/parity edge cases still need exact WoW pass ordering later, but ordinary Elwynn props should no longer produce overexposed or acid-green surfaces.
|
||||
- Human building WMO materials such as `HumanTwoStory` and `GoldshireInn` carry strong painted `MOMT.diffColor` values, including green trim colors. Textured WMO surfaces now use this as a soft hue tint instead of multiplying texture albedo by the raw RGB value; untextured WMO surfaces still use a muted material paint color.
|
||||
- WMO window materials such as `MM_ELWYNN_WND_EXT__01.BLP` carry high `MOMT.sidnColor`/emissive values. Emissive is texture-masked and capped for window paths so it brightens the pane pattern instead of flooding the whole quad white.
|
||||
- Runtime WMO cache material refresh is versioned separately from WMO geometry cache. Shader-only WMO material fixes should bump `WMO_MATERIAL_REFRESH_VERSION` instead of forcing a full `wmo_render_v1` rebake.
|
||||
|
||||
## Practical Rule For Future Work
|
||||
|
||||
If something improves quality but creates visible hitch, it is not done. Move it to bake/cache/background work, split finalization over frames, or prewarm it before the player can see it.
|
||||
|
||||
Reference in New Issue
Block a user