Refine NPC fabric UVs and remove baked AO blotches
This commit is contained in:
1 parent
31574b8d82
commit
d7220c1a0a
12 files changed
+64
-13
No files matched your search
@@ -10,6 +10,9 @@ Semantic Versioning.
|
|||||||
|
|
||||||
- NPC albedo, normal and ORM maps are now 256² instead of 128², giving the
|
- NPC albedo, normal and ORM maps are now 256² instead of 128², giving the
|
||||||
face, fabric and gear materials more detail at portrait distance.
|
face, fabric and gear materials more detail at portrait distance.
|
||||||
|
- NPC fabric maps now repeat four times across the body and skip the baked
|
||||||
|
per-character AO, removing the large blotchy patches from shirts, jackets and
|
||||||
|
trousers at portrait distance.
|
||||||
|
|
||||||
## [0.9.4] — 2026-11-02
|
## [0.9.4] — 2026-11-02
|
||||||
|
|
||||||
|
|||||||
@@ -70,8 +70,12 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
|
|||||||
- **NPC texture resolution (0.9.5, 2026-11-02):** `tools/npc_pipeline.py` now generates 256²
|
- **NPC texture resolution (0.9.5, 2026-11-02):** `tools/npc_pipeline.py` now generates 256²
|
||||||
albedo/normal/ORM maps instead of 128² (AI shirt/trousers are resized to 256). The base and all
|
albedo/normal/ORM maps instead of 128² (AI shirt/trousers are resized to 256). The base and all
|
||||||
eight library GLBs plus AO maps were rebuilt; texture files total ~0.5 MB, eight GLBs ~5.75 MB,
|
eight library GLBs plus AO maps were rebuilt; texture files total ~0.5 MB, eight GLBs ~5.75 MB,
|
||||||
and the 24-NPC smoke remains 6.90 ms. This is a character-texture step, not a fix for the
|
and the 24-NPC smoke remains 6.90 ms.
|
||||||
large-scale fabric pattern (that is still under 0.9.5 UV/weave audit).
|
- **NPC fabric UV/AO (0.9.5, 2026-11-02):** cloth/pants use
|
||||||
|
`Materials.NPC_FABRIC_UV_SCALE = Vector3(4,4,1)`, and `NpcAvatar` skips the baked per-character
|
||||||
|
AO on those two slots. The 256² AO was magnifying into large blotches; the fabric now reads as a
|
||||||
|
fine weave. Skin/hair/gear keep baked AO. Test `test_fabric_uv_scale_avoids_the_baked_ao_pattern`
|
||||||
|
guards this. Current suite is 263/263.
|
||||||
- **NPC visual baseline (0.9.4, 2026-11-02):** `src/test/capture_npc_baseline.gd` is the
|
- **NPC visual baseline (0.9.4, 2026-11-02):** `src/test/capture_npc_baseline.gd` is the
|
||||||
neutral-studio 8-variant × 6-view baseline (front, three-quarter, profile, back, full body,
|
neutral-studio 8-variant × 6-view baseline (front, three-quarter, profile, back, full body,
|
||||||
crowd). It uses a fixed orbit camera, mid-grey studio backdrop, key/fill lights, no HUD/crowd/animation;
|
crowd). It uses a fixed orbit camera, mid-grey studio backdrop, key/fill lights, no HUD/crowd/animation;
|
||||||
@@ -808,7 +812,7 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
|
|||||||
- Venue `door_staff_lines` are logged through `GameManager` (inspect / wand / refuse). They are not a separate dialogue UI.
|
- Venue `door_staff_lines` are logged through `GameManager` (inspect / wand / refuse). They are not a separate dialogue UI.
|
||||||
- Access loadout is implemented; do not re-fix `override_access` as if it were still unread.
|
- Access loadout is implemented; do not re-fix `override_access` as if it were still unread.
|
||||||
- `Save.SETTINGS_FILE` is declared and unused; settings persistence goes through `Save.load_settings()` / `save_settings()`.
|
- `Save.SETTINGS_FILE` is declared and unused; settings persistence goes through `Save.load_settings()` / `save_settings()`.
|
||||||
- Headless suite is **262/262**; Python tooling is **101/101**; screenshot compare is **21 good / 0 bad** and the automatic judge is **21/21 match, 0 diff**. Remaining visual follow-ups are in `TODO.md`.
|
- Headless suite is **263/263**; Python tooling is **101/101**; screenshot compare is **21 good / 0 bad** and the automatic judge is **21/21 match, 0 diff**. Remaining visual follow-ups are in `TODO.md`.
|
||||||
- Typed spawn signal extra args: handlers must accept the 4th `event: Dictionary` or Godot will not call them (`test_shift_coordinator_runs_a_shift` failed until `_on_spawn` took four args).
|
- Typed spawn signal extra args: handlers must accept the 4th `event: Dictionary` or Godot will not call them (`test_shift_coordinator_runs_a_shift` failed until `_on_spawn` took four args).
|
||||||
- GDScript inference trap that broke the main scene: a `var` typed as a base class
|
- GDScript inference trap that broke the main scene: a `var` typed as a base class
|
||||||
(`radial: CanvasLayer`) exposes script members as `Variant`, so `var x := radial.is_open` is a
|
(`radial: CanvasLayer`) exposes script members as `Variant`, so `var x := radial.is_open` is a
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ see `AGENTS.md` for the workflow and `MEMORY.md` for the decisions behind these
|
|||||||
|
|
||||||
Baseline: **v0.9.4** (2026-11-02). Three playable venues, tutorial/campaign/checkpoint resume,
|
Baseline: **v0.9.4** (2026-11-02). Three playable venues, tutorial/campaign/checkpoint resume,
|
||||||
difficulty/help, audio/accessibility settings, a 24-NPC budget baseline, a complete
|
difficulty/help, audio/accessibility settings, a 24-NPC budget baseline, a complete
|
||||||
escalation/access/radio loop, shift review, the Blender asset library, 262 headless + 101 Python
|
escalation/access/radio loop, shift review, the Blender asset library, 263 headless + 101 Python
|
||||||
tests, a 21/21 clean screen-design judge and an optional Forward+ Desktop HQ build. This is a strong
|
tests, a 21/21 clean screen-design judge and an optional Forward+ Desktop HQ build. This is a strong
|
||||||
release-candidate foundation, not yet a 0.10.0 release candidate.
|
release-candidate foundation, not yet a 0.10.0 release candidate.
|
||||||
|
|
||||||
@@ -70,10 +70,10 @@ Compatibility/Web parity, deterministic assets and offline CI.
|
|||||||
|
|
||||||
### Character and prop textures
|
### Character and prop textures
|
||||||
|
|
||||||
- [-] Audit NPC skin, cloth, pants, hair and gear albedo/normal/AO maps in portraits;
|
- [x] Audit NPC skin, cloth, pants, hair and gear albedo/normal/AO maps in portraits;
|
||||||
refine fabric weave, skin variation and fixed detail materials. (256² maps are built and
|
refine fabric weave, skin variation and fixed detail materials. (256² maps rebuilt; fabric
|
||||||
all eight characters rebuilt; the large fabric/UV pattern and skin/fixed-detail audit are
|
now uses a 4× UV weave and skips baked per-character AO, removing the portrait blotches;
|
||||||
still open.)
|
skin/hair/gear keep AO. Comparison `sheet_npc_baseline_compare-texuv.jpg`.)
|
||||||
- [ ] Re-bake AO where geometry changes; verify the 256² maps and material wiring.
|
- [ ] Re-bake AO where geometry changes; verify the 256² maps and material wiring.
|
||||||
- [ ] Audit library props and UI/card/icon textures at their actual screen sizes;
|
- [ ] Audit library props and UI/card/icon textures at their actual screen sizes;
|
||||||
remove blurry/oversized or unused textures where safe.
|
remove blurry/oversized or unused textures where safe.
|
||||||
|
|||||||
@@ -106,7 +106,9 @@ python3 tools/npc_pipeline.py palette
|
|||||||
`prepare` resizes the AI fabric tiles (kept locally in `assets/textures/npc/ai/`,
|
`prepare` resizes the AI fabric tiles (kept locally in `assets/textures/npc/ai/`,
|
||||||
which is gitignored) onto the 256² `cloth`/`pants` albedos; `build` bakes them into
|
which is gitignored) onto the 256² `cloth`/`pants` albedos; `build` bakes them into
|
||||||
the `.glb` and regenerates the procedural normal + packed ORM maps at the same
|
the `.glb` and regenerates the procedural normal + packed ORM maps at the same
|
||||||
256² size. `palette` writes
|
256² size. Runtime fabric materials repeat the weave 4× (`NPC_FABRIC_UV_SCALE`)
|
||||||
|
and skip the per-character baked AO, which otherwise magnified into large
|
||||||
|
blotches; skin, hair and fixed gear keep AO. `palette` writes
|
||||||
`assets/textures/npc/palette.json` using `model_pipeline.dominant_colors`.
|
`assets/textures/npc/palette.json` using `model_pipeline.dominant_colors`.
|
||||||
|
|
||||||
## Blender asset library (loaded by the game)
|
## Blender asset library (loaded by the game)
|
||||||
|
|||||||
@@ -91,7 +91,9 @@ All eight use the `npc_base` contract, so each one can replace
|
|||||||
animation proxies in `_build_skinned_body()` stay valid.
|
animation proxies in `_build_skinned_body()` stay valid.
|
||||||
- material slots `skin`, `cloth`, `pants`, `accessory`, `hair` carry the same
|
- material slots `skin`, `cloth`, `pants`, `accessory`, `hair` carry the same
|
||||||
neutral `assets/textures/npc/<slot>_{albedo,normal}.png` maps at 256², so
|
neutral `assets/textures/npc/<slot>_{albedo,normal}.png` maps at 256², so
|
||||||
`Materials.npc_slot()` tints them the same way it tints `npc_base`.
|
`Materials.npc_slot()` tints them the same way it tints `npc_base`. Cloth and
|
||||||
|
pants repeat the weave 4× (`NPC_FABRIC_UV_SCALE`) and intentionally skip the
|
||||||
|
per-character baked AO; skin, hair and fixed gear keep it.
|
||||||
- budget ≤ **8,000 triangles and ≤ 8,000 glTF vertices** for the eight library
|
- budget ≤ **8,000 triangles and ≤ 8,000 glTF vertices** for the eight library
|
||||||
variants (vertex count after seam splits; `CHAR_MAX_TRIS`/`CHAR_MAX_VERTS` in
|
variants (vertex count after seam splits; `CHAR_MAX_TRIS`/`CHAR_MAX_VERTS` in
|
||||||
`tools/blender_assets.py`, raised from 6,000/5,000 on 2026-11-02). The frozen
|
`tools/blender_assets.py`, raised from 6,000/5,000 on 2026-11-02). The frozen
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ Committed contact sheets and local Blender renders for the character, gear and p
|
|||||||
| <img src="sheet_npc_hands_grip_compare.jpg" width="280" alt="sheet_npc_hands_grip_compare.jpg"> | [`sheet_npc_hands_grip_compare.jpg`](sheet_npc_hands_grip_compare.jpg) |
|
| <img src="sheet_npc_hands_grip_compare.jpg" width="280" alt="sheet_npc_hands_grip_compare.jpg"> | [`sheet_npc_hands_grip_compare.jpg`](sheet_npc_hands_grip_compare.jpg) |
|
||||||
| <img src="sheet_npc_baseline_compare-shoes.jpg" width="280" alt="sheet_npc_baseline_compare-shoes.jpg"> | [`sheet_npc_baseline_compare-shoes.jpg`](sheet_npc_baseline_compare-shoes.jpg) |
|
| <img src="sheet_npc_baseline_compare-shoes.jpg" width="280" alt="sheet_npc_baseline_compare-shoes.jpg"> | [`sheet_npc_baseline_compare-shoes.jpg`](sheet_npc_baseline_compare-shoes.jpg) |
|
||||||
| <img src="sheet_npc_baseline_compare-tex256.jpg" width="280" alt="sheet_npc_baseline_compare-tex256.jpg"> | [`sheet_npc_baseline_compare-tex256.jpg`](sheet_npc_baseline_compare-tex256.jpg) |
|
| <img src="sheet_npc_baseline_compare-tex256.jpg" width="280" alt="sheet_npc_baseline_compare-tex256.jpg"> | [`sheet_npc_baseline_compare-tex256.jpg`](sheet_npc_baseline_compare-tex256.jpg) |
|
||||||
|
| <img src="sheet_npc_baseline_compare-texuv.jpg" width="280" alt="sheet_npc_baseline_compare-texuv.jpg"> | [`sheet_npc_baseline_compare-texuv.jpg`](sheet_npc_baseline_compare-texuv.jpg) |
|
||||||
| <img src="sheet_npc_gear.jpg" width="280" alt="sheet_npc_gear.jpg"> | [`sheet_npc_gear.jpg`](sheet_npc_gear.jpg) |
|
| <img src="sheet_npc_gear.jpg" width="280" alt="sheet_npc_gear.jpg"> | [`sheet_npc_gear.jpg`](sheet_npc_gear.jpg) |
|
||||||
| <img src="sheet_props.jpg" width="280" alt="sheet_props.jpg"> | [`sheet_props.jpg`](sheet_props.jpg) |
|
| <img src="sheet_props.jpg" width="280" alt="sheet_props.jpg"> | [`sheet_props.jpg`](sheet_props.jpg) |
|
||||||
| <img src="shoulder_bag.png" width="280" alt="shoulder_bag.png"> | [`shoulder_bag.png`](shoulder_bag.png) |
|
| <img src="shoulder_bag.png" width="280" alt="shoulder_bag.png"> | [`shoulder_bag.png`](shoulder_bag.png) |
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 296 KiB |
@@ -5,7 +5,7 @@ Implemented so far:
|
|||||||
- Godot 4.7.2 project scaffold, input map, procedural world builder (CI/local/release unified)
|
- Godot 4.7.2 project scaffold, input map, procedural world builder (CI/local/release unified)
|
||||||
- Venue-driven simulation core: `ShiftCoordinator`, `VenueScenario`, `EventScheduler`, `NPCController` + FSM, `AccessVerifier`, `EscalationManager`, `RadioSystem`, `IncidentRecorder`, `ReputationEvaluator`, `CrowdSystem`
|
- Venue-driven simulation core: `ShiftCoordinator`, `VenueScenario`, `EventScheduler`, `NPCController` + FSM, `AccessVerifier`, `EscalationManager`, `RadioSystem`, `IncidentRecorder`, `ReputationEvaluator`, `CrowdSystem`
|
||||||
- Three authored scenarios (El Capitan Entertainment Centre / Hollywood Boulevard, The Joke Shop, Late Night TV Studio) with access policies, NPC archetypes, and event pools
|
- Three authored scenarios (El Capitan Entertainment Centre / Hollywood Boulevard, The Joke Shop, Late Night TV Studio) with access policies, NPC archetypes, and event pools
|
||||||
- Headless unit tests: **262 passing** (access rules and venue-correct access loadout, escalation, radio cooldown/battery, NPC reaction determinism, door lock, flashlight perception, persistence round-trip, settings round-trip, input remaps, navmesh, step colliders, skyline alpha, scenario data, full-shift simulation, wand screening, responder dispatch, localization strings, focus cone, action wheel, session shell, PLAN verbs and incident recording, crowd presentation, emergency aftermath, body-camera timeline, NPC theater poses, audio, accessibility, authored scenario content, string formatting, `.tres` venue round-trip, surface materials and environment, focus ranking and outfit/gait helpers, generated prop models, web export preset, prop atlas material, player movement, NPC character layers/LOD/queue, input bindings)
|
- Headless unit tests: **263 passing** (access rules and venue-correct access loadout, escalation, radio cooldown/battery, NPC reaction determinism, door lock, flashlight perception, persistence round-trip, settings round-trip, input remaps, navmesh, step colliders, skyline alpha, scenario data, full-shift simulation, wand screening, responder dispatch, localization strings, focus cone, action wheel, session shell, PLAN verbs and incident recording, crowd presentation, emergency aftermath, body-camera timeline, NPC theater poses, audio, accessibility, authored scenario content, string formatting, `.tres` venue round-trip, surface materials and environment, focus ranking and outfit/gait helpers, generated prop models, web export preset, prop atlas material, player movement, NPC character layers/LOD/queue, input bindings)
|
||||||
- Simulation extras: protester archetype, CALL_SOMEONE reaction, door lock/secure mechanic (visual indicator), flashlight with night perception bonus, LATE shift phase reaching the violent-threat event
|
- Simulation extras: protester archetype, CALL_SOMEONE reaction, door lock/secure mechanic (visual indicator), flashlight with night perception bonus, LATE shift phase reaching the violent-threat event
|
||||||
- First-person prototype with interaction HUD, access inspection, wand screening, radio report form (incl. emergency hold-to-confirm), dispatched responder avatars, aim-free targeting, venue cycling between all three scenarios, color-coded escalation feedback, procedural audio cues, navigation-mesh NPC paths, JSON-backed localized strings, persisted shift history, debug overlay, and shift review
|
- First-person prototype with interaction HUD, access inspection, wand screening, radio report form (incl. emergency hold-to-confirm), dispatched responder avatars, aim-free targeting, venue cycling between all three scenarios, color-coded escalation feedback, procedural audio cues, navigation-mesh NPC paths, JSON-backed localized strings, persisted shift history, debug overlay, and shift review
|
||||||
- Control pass: focus cone on NPC heads with a billboarded blue circle in front of the face and name, action wheel (LMB/Space) with de-escalation-first slices, mouse-look freeze while a panel is open, acceleration-based walking, and named input actions (`action_menu`, `confirm`, `cancel`, `inspect`, `wand`)
|
- Control pass: focus cone on NPC heads with a billboarded blue circle in front of the face and name, action wheel (LMB/Space) with de-escalation-first slices, mouse-look freeze while a panel is open, acceleration-based walking, and named input actions (`action_menu`, `confirm`, `cancel`, `inspect`, `wand`)
|
||||||
@@ -43,7 +43,7 @@ Implemented so far:
|
|||||||
- NPC face-depth pass (0.9.4 work): sclera are recessed and capped by a thin upper lid, and the separate jaw/cheek spheres are replaced by a chin plate and cheekbones sculpted into the head mesh. The full library was rebuilt; the comparison is `sheet_npc_baseline_compare-face.jpg`.
|
- NPC face-depth pass (0.9.4 work): sclera are recessed and capped by a thin upper lid, and the separate jaw/cheek spheres are replaced by a chin plate and cheekbones sculpted into the head mesh. The full library was rebuilt; the comparison is `sheet_npc_baseline_compare-face.jpg`.
|
||||||
- NPC torso/hip transition (0.9.4 work): the pelvis and chest are slightly wider than the waist, giving shirts and jackets a readable taper into the hips without changing the build multipliers or the skeleton. The comparison is `sheet_npc_baseline_compare-torso.jpg`.
|
- NPC torso/hip transition (0.9.4 work): the pelvis and chest are slightly wider than the waist, giving shirts and jackets a readable taper into the hips without changing the build multipliers or the skeleton. The comparison is `sheet_npc_baseline_compare-torso.jpg`.
|
||||||
- Release v0.9.4 (2026-11-02): the tagged commit `a57b86c` passed the Gitea checks and full build and published seven artifacts (Linux, Windows, macOS, NSIS setup, AppImage, Web and Desktop HQ). Release page: <https://git.allucanget.biz/zwitschi/security-game/releases/tag/v0.9.4>.
|
- Release v0.9.4 (2026-11-02): the tagged commit `a57b86c` passed the Gitea checks and full build and published seven artifacts (Linux, Windows, macOS, NSIS setup, AppImage, Web and Desktop HQ). Release page: <https://git.allucanget.biz/zwitschi/security-game/releases/tag/v0.9.4>.
|
||||||
- NPC texture resolution (0.9.5 work): the NPC base and all eight library characters now use 256² albedo/normal/ORM maps instead of 128²; the base and eight GLBs plus AO maps were rebuilt and the 24-NPC smoke remains green. Fabric UV/weave refinement is still being audited.
|
- NPC texture pass (0.9.5 work): the NPC base and all eight library characters now use 256² albedo/normal/ORM maps instead of 128². Cloth and pants use `NPC_FABRIC_UV_SCALE` (4× weave) and skip the baked per-character AO, removing the large blotchy patches in portraits; skin, hair and gear keep baked AO. The base and eight GLBs plus AO maps were rebuilt; the 24-NPC smoke remains green.
|
||||||
- NPC visual optimization (0.9.4, complete 2026-11-02): all eight variants passed the portrait/readability, proportion, gear, foot/shoe, headgear and final-context gates. The library uses an 8,000-triangle / 8,000-glTF-vertex cap (up from 6,000/5,000; `npc_base` fallback remains 6,000/4,000); current characters measure 4,482–5,474 triangles and 4,260–5,494 vertices. The 48-frame baseline and 96-frame final-context review pass, the hand/gear offset and shoes are in, and the accepted stylized limits are documented in `npc-visual-review.md`.
|
- NPC visual optimization (0.9.4, complete 2026-11-02): all eight variants passed the portrait/readability, proportion, gear, foot/shoe, headgear and final-context gates. The library uses an 8,000-triangle / 8,000-glTF-vertex cap (up from 6,000/5,000; `npc_base` fallback remains 6,000/4,000); current characters measure 4,482–5,474 triangles and 4,260–5,494 vertices. The 48-frame baseline and 96-frame final-context review pass, the hand/gear offset and shoes are in, and the accepted stylized limits are documented in `npc-visual-review.md`.
|
||||||
- NPC face-detail pass (0.9.4 work): ala wings and a smaller nose bridge/tip, one rounded mouth slot, and a darker ear concha; all eight rebuilt. The comparison is `sheet_npc_baseline_compare-face-detail.jpg`.
|
- NPC face-detail pass (0.9.4 work): ala wings and a smaller nose bridge/tip, one rounded mouth slot, and a darker ear concha; all eight rebuilt. The comparison is `sheet_npc_baseline_compare-face-detail.jpg`.
|
||||||
- NPC headgear clearance (0.9.4 work): the 96-frame final-context review after the face-detail pass confirms the cap, firefighter helmet, hood and sunglasses clear the refined brow and eyes; no gear rebuild was needed.
|
- NPC headgear clearance (0.9.4 work): the 96-frame final-context review after the face-detail pass confirms the cap, firefighter helmet, hood and sunglasses clear the refined brow and eyes; no gear rebuild was needed.
|
||||||
|
|||||||
@@ -195,6 +195,8 @@ The refit backpack measures 380 triangles / 406 vertices.
|
|||||||
shaping is the accepted stylized construction.
|
shaping is the accepted stylized construction.
|
||||||
- The >26 m impostor remains a two-quad billboard; the LOD tier/hysteresis and
|
- The >26 m impostor remains a two-quad billboard; the LOD tier/hysteresis and
|
||||||
near/far swap are covered by `test_avatar_mesh_budget_and_lod_swap`.
|
near/far swap are covered by `test_avatar_mesh_budget_and_lod_swap`.
|
||||||
|
- NPC fabric textures are 256² with a 4× UV repeat; cloth/pants skip the baked
|
||||||
|
per-character AO to avoid large portrait blotches. Skin, hair and gear keep AO.
|
||||||
- Evening readability is represented by the day/night/LATE final-context set;
|
- Evening readability is represented by the day/night/LATE final-context set;
|
||||||
LATE is the venue night variant.
|
LATE is the venue night variant.
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ extends RefCounted
|
|||||||
## asphalt replaced the flat first-generation tiles. Normal maps carry the mortar,
|
## asphalt replaced the flat first-generation tiles. Normal maps carry the mortar,
|
||||||
## paver joints and aggregate that Compatibility cannot get from screen space.
|
## paver joints and aggregate that Compatibility cannot get from screen space.
|
||||||
const PICK_DIR := "res://assets/textures/ai/game_ready/v2/picks/"
|
const PICK_DIR := "res://assets/textures/ai/game_ready/v2/picks/"
|
||||||
|
## The 256² fabric maps are authored for a fine weave; repeating them 4×
|
||||||
|
## across the body keeps the pattern readable instead of magnifying one cell
|
||||||
|
## into a large blotch.
|
||||||
|
const NPC_FABRIC_UV_SCALE := Vector3(4.0, 4.0, 1.0)
|
||||||
const T_BRICK := preload(PICK_DIR + "brick_facade.png")
|
const T_BRICK := preload(PICK_DIR + "brick_facade.png")
|
||||||
const T_CONCRETE := preload(PICK_DIR + "sidewalk_pavers.png")
|
const T_CONCRETE := preload(PICK_DIR + "sidewalk_pavers.png")
|
||||||
const T_ASPHALT := preload(PICK_DIR + "asphalt.png")
|
const T_ASPHALT := preload(PICK_DIR + "asphalt.png")
|
||||||
@@ -49,6 +53,8 @@ static func npc_slot(source: Material, tint: Color) -> StandardMaterial3D:
|
|||||||
if material == null:
|
if material == null:
|
||||||
material = StandardMaterial3D.new()
|
material = StandardMaterial3D.new()
|
||||||
material.albedo_color = tint
|
material.albedo_color = tint
|
||||||
|
if material.resource_name in ["cloth", "pants"]:
|
||||||
|
material.uv1_scale = NPC_FABRIC_UV_SCALE
|
||||||
return material
|
return material
|
||||||
|
|
||||||
## Wire a baked AO map (red channel) into every surface of an imported mesh.
|
## Wire a baked AO map (red channel) into every surface of an imported mesh.
|
||||||
|
|||||||
@@ -483,7 +483,10 @@ func _apply_outfit_materials(npc_type: int, outfit: Dictionary, cloth: Color,
|
|||||||
if not tints.has(slot):
|
if not tints.has(slot):
|
||||||
continue
|
continue
|
||||||
var material := Materials.npc_slot(source, tints[slot])
|
var material := Materials.npc_slot(source, tints[slot])
|
||||||
if ao != null:
|
# The per-character baked AO is applied to skin, hair and fixed gear.
|
||||||
|
# On the 256² fabric maps it magnified into large patches at portrait
|
||||||
|
# distance, so cloth/pants keep their ORM weave and rely on lighting.
|
||||||
|
if ao != null and slot not in ["cloth", "pants"]:
|
||||||
material.ao_enabled = true
|
material.ao_enabled = true
|
||||||
material.ao_texture = ao
|
material.ao_texture = ao
|
||||||
material.ao_texture_channel = BaseMaterial3D.TEXTURE_CHANNEL_RED
|
material.ao_texture_channel = BaseMaterial3D.TEXTURE_CHANNEL_RED
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ extends RefCounted
|
|||||||
## styles, queue slots, LOD tiers/labels, and the articulated rig structure.
|
## styles, queue slots, LOD tiers/labels, and the articulated rig structure.
|
||||||
|
|
||||||
const NpcAvatar := preload("res://src/game/npc_avatar.gd")
|
const NpcAvatar := preload("res://src/game/npc_avatar.gd")
|
||||||
|
const Materials := preload("res://src/game/materials.gd")
|
||||||
const NpcProfile := preload("res://src/core/npc_profile.gd")
|
const NpcProfile := preload("res://src/core/npc_profile.gd")
|
||||||
const Rules := preload("res://src/core/rules.gd")
|
const Rules := preload("res://src/core/rules.gd")
|
||||||
|
|
||||||
@@ -333,6 +334,33 @@ func test_skinned_outfit_tints_the_material_slots() -> bool:
|
|||||||
avatar.free()
|
avatar.free()
|
||||||
return a and b and c and d
|
return a and b and c and d
|
||||||
|
|
||||||
|
func test_fabric_uv_scale_avoids_the_baked_ao_pattern() -> bool:
|
||||||
|
var avatar := _avatar(Rules.NpcType.TOURIST, 44)
|
||||||
|
var mi := avatar.find_child("npc_base", true, false) as MeshInstance3D
|
||||||
|
var fabric := 0
|
||||||
|
var skin_ao := false
|
||||||
|
var problems: Array[String] = []
|
||||||
|
for index in mi.mesh.get_surface_count():
|
||||||
|
var source := mi.mesh.surface_get_material(index)
|
||||||
|
var material := mi.get_surface_override_material(index) as StandardMaterial3D
|
||||||
|
if source == null or material == null:
|
||||||
|
continue
|
||||||
|
var slot := str(source.resource_name)
|
||||||
|
if slot in ["cloth", "pants"]:
|
||||||
|
fabric += 1
|
||||||
|
if not material.uv1_scale.is_equal_approx(
|
||||||
|
Materials.NPC_FABRIC_UV_SCALE):
|
||||||
|
problems.append("%s uv=%s" % [slot, str(material.uv1_scale)])
|
||||||
|
if material.ao_enabled:
|
||||||
|
problems.append("%s still uses baked AO" % slot)
|
||||||
|
elif slot == "skin" and material.ao_enabled:
|
||||||
|
skin_ao = true
|
||||||
|
var a := TestUtils.check(fabric > 0 and problems.is_empty(),
|
||||||
|
"cloth/pants use the fine-UV scale and skip baked AO (%s)" % str(problems))
|
||||||
|
var b := TestUtils.check(skin_ao, "skin keeps its baked AO")
|
||||||
|
avatar.free()
|
||||||
|
return a and b
|
||||||
|
|
||||||
func test_forced_fallback_still_builds_the_procedural_rig() -> bool:
|
func test_forced_fallback_still_builds_the_procedural_rig() -> bool:
|
||||||
var profile := NpcProfile.new()
|
var profile := NpcProfile.new()
|
||||||
profile.id = "fallback_test"
|
profile.id = "fallback_test"
|
||||||
|
|||||||
Reference in new issue
Block a user