Ver Fonte

mdl_bones.py tool now works correctly, removed old docs

UwU há 4 dias atrás
pai
commit
834096e413
3 ficheiros alterados com 23 adições e 253 exclusões
  1. 12 115
      docs/OS-Waves-rendering-issues.md
  2. 0 130
      docs/hotswap-extended-state.md
  3. 11 8
      tools/mdl_bones.py

+ 12 - 115
docs/OS-Waves-rendering-issues.md

@@ -1,123 +1,20 @@
-# OS Waves (3014738359) - Rendering Issues Investigation
+# OS Waves (3014738359) - Open Rendering Issue
 
-Investigation of rendering differences between linux-wallpaperengine and the native
-Windows Wallpaper Engine for the OS Waves wallpaper (workshop ID 3014738359).
+The earlier investigation of this wallpaper listed six differences from the native
+Windows Wallpaper Engine. Script-driven user settings, text objects, the orthographic
+"auto" camera and `autosize` are implemented now, and skipping passthrough images
+without effects matches the real engine (`CImage.cpp`, `sub_140175830`). One item is
+still open.
 
-## Wallpaper Structure
+## Audio spectrum response may differ
 
-The wallpaper consists of 8 scene objects rendered in this order:
-
-| Object ID | Name | Type | Effects | Status |
-|-----------|------|------|---------|--------|
-| 85 | Solid1 | Image (solid) | None | Renders correctly |
-| 173 | Windows_logo_-_2021.svg | Image | Pulse (audio) | Renders, audio response may differ |
-| 57 | 1024px-Archlinux-icon-crystal-64.svg | Image | Pulse (audio) | Renders, audio response may differ |
-| 61 | ubuntu-icon-logo-png-transparent | Image | Pulse (audio) | Renders, audio response may differ |
-| 140 | Clock | Text | N/A | **Not rendered** (text unsupported) |
-| 45 | cust | Image (passthrough) | None | **Possibly skipped** (passthrough w/o effects) |
-| 71 | 13 | Image | Water waves | Renders via shader |
-| 36 | 14 | Image | Water waves | Renders via shader |
-
-Bloom is applied as a post-processing pass (via `wpenginelinux.json`).
-
-## Issues Found
-
-### 1. Script-based UserSettings not executed (CRITICAL)
-
-**Files:** `src/WallpaperEngine/Data/Parsers/UserSettingParser.cpp:21-24`
-
-The wallpaper defines 4 user settings with embedded JavaScript that dynamically
-control object position (Y and Z axes) via slider properties. The engine currently
-logs a warning and falls back to the default value, meaning objects are placed at
-their raw default positions instead of the script-computed ones.
-
-Example script from the wallpaper:
-```javascript
-export function update(value) {
-    value.y = scriptProperties.posY;  // slider 0-100
-    return value;
-}
-```
-
-**Impact:** Object positioning differs from Windows. Sliders for position
-adjustments are non-functional.
-
-### 2. Text objects not supported (MEDIUM)
-
-**Files:** `src/WallpaperEngine/Data/Parsers/ObjectParser.cpp:55-56`
-
-Object 140 ("Clock") is a text object and is silently skipped. On Windows this
-would render as a visible clock element.
-
-**Impact:** Missing UI element in the wallpaper.
-
-### 3. Orthogonal camera auto-size not implemented (MEDIUM)
-
-**Files:** `src/WallpaperEngine/Render/Wallpapers/CScene.cpp:37-39`
-
-When the scene camera uses `isAuto=true` for orthogonal projection, the engine
-has a TODO to calculate the projection based on content size. Currently falls
-through without adjusting.
-
-**Impact:** If enabled, overall scale and positioning of all objects could be wrong.
-
-### 4. Passthrough images without effects are skipped (LOW-MEDIUM)
-
-**Files:** `src/WallpaperEngine/Render/Objects/CImage.cpp:226-230`
-
-Object 45 ("cust") uses passthrough mode. The engine explicitly skips passthrough
-images that have no effects attached:
-```cpp
-if (this->m_image.model->passthrough && this->m_image.effects.empty()) {
-    return;
-}
-```
-
-**Impact:** If this object is meant to composite content from behind, it won't
-appear.
-
-### 5. Audio spectrum processing may differ (MEDIUM)
-
-**Files:** `src/WallpaperEngine/Render/Objects/Effects/CPass.cpp:625-630`
-
-The pulse effect on the OS logos uses:
+The pulse effect on the three OS logos (objects 173, 57 and 61) uses:
 - `AUDIOPROCESSING=3`
 - `audioamount=1.0`, `audiobounds=0.5-1.0`, `audioexponent=0.35`
 
-The DSP algorithm for audio spectrum analysis may produce different results than
-the Windows implementation.
-
-**Impact:** Logo pulse animations may respond differently to audio.
-
-### 6. Fullscreen/autosize behavior incomplete (LOW)
-
-**Files:** `src/WallpaperEngine/Render/Objects/CImage.cpp:66`
-
-There is a TODO asking what `autosize` should do. Fullscreen layers force size to
-scene dimensions, but autosize behavior is undefined.
-
-**Impact:** Minor sizing differences for affected layers.
-
-## Fix Applied: parallaxDepth UserSetting Support
-
-**Commit:** `fix: support UserSetting objects for parallaxDepth`
-
-The original crash was caused by `parallaxDepth` being declared as `glm::vec2`
-while the wallpaper's scene.json uses it as a UserSetting object:
-```json
-{"user": "parallaxstrength", "value": "0.19000 0.19000"}
-```
-
-### Changes made:
-- `src/WallpaperEngine/Data/Model/Object.h`: Changed `parallaxDepth` from
-  `glm::vec2` to `UserSettingUniquePtr` in both `ImageData` and `ParticleData`
-- `src/WallpaperEngine/Data/Parsers/ObjectParser.cpp`: Changed 3 parse sites from
-  `it.optional()` to `it.user()` to handle both plain values and UserSetting objects
-- `src/WallpaperEngine/Render/Objects/CImage.cpp`: Updated access to dereference
-  through the UserSetting wrapper (`->value->getVec2()`)
-
-This follows the same pattern already used by `origin`, `scale`, `angles`,
-`visible`, `alpha`, and `color`.
+Nobody has compared the logo pulse against Windows side by side, so whether our
+spectrum analysis (`Audio/SpectrumNormalizer`) drives these parameters the same way is
+unknown. Checking it needs the same audio playing on both engines at once.
 
 ## Reproduction
 
@@ -125,6 +22,6 @@ This follows the same pattern already used by `origin`, `scale`, `angles`,
 linux-wallpaperengine \
   --assets-dir ~/.local/share/Steam/steamapps/common/wallpaper_engine/assets \
   --screen-root DP-2 --screen-root HDMI-A-1 \
-  --fps 60 --silent \
+  --fps 60 \
   ~/.local/share/Steam/steamapps/workshop/content/431960/3014738359
 ```

+ 0 - 130
docs/hotswap-extended-state.md

@@ -1,130 +0,0 @@
-# Task: extend hotswap to cover layers, volume, and other live settings
-
-## Context
-
-`--list-objects` / `--disable-object` / `--enable-object` were recently added
-(see `ApplicationContext.cpp`), and the we_manager Electron app now has a UI
-for toggling layers and adjusting per-wallpaper volume live from a sidebar.
-
-Because those settings are only read from argv at process startup
-(`ApplicationContext::loadSettingsFromArgv`), the app currently applies any
-change by killing the running process and spawning a brand new one with the
-updated flags. That works, but it's the wrong tool for the job:
-
-- Full CEF/GL/DBus teardown and reinit on every toggle, visible as a flash.
-- A real bug we hit and fixed on the app side: `stopLwe()` sends SIGTERM and
-  moves on without waiting for the old process to actually exit. If it's
-  still shutting down when the new one starts, both processes are briefly
-  alive at once (we saw `DBus service org.linuxwallpaperengine.WaylandDetector
-  is already owned by another process` in the logs from exactly this). The
-  app now guards against the worst symptom (a corrupted `activeProcess`
-  reference that made the new launch hang forever - see
-  `we_manager/src/main/services/lwe.service.ts`, the `cleanup` closure in
-  `launchLweAsync`), but the underlying process-overlap window is still
-  there and still wasteful.
-- Every toggle costs ~2-3 seconds end to end for something that should be
-  near-instant.
-
-The hotswap mechanism (SIGUSR1 + control file) already exists for switching
-which background plays, without a process restart. This task is to extend
-it to carry more than just a path, so layers/volume/etc. can be updated the
-same lightweight way.
-
-## Current hotswap mechanism
-
-- `WallpaperApplication::checkHotswapRequest()` in
-  `src/WallpaperEngine/Application/WallpaperApplication.cpp` (~line 506).
-- Reads a single line (the new background path) from
-  `$XDG_RUNTIME_DIR/lwe-control` (or `/tmp/lwe-control`), triggered by
-  SIGUSR1 (`m_hotswapRequested` flag, set in `WallpaperApplication::signal`).
-- Fully reloads the project (`loadBackground`), re-runs
-  `setupPropertiesForProject`, and reconstructs a `CWallpaper` per screen via
-  `CWallpaper::fromWallpaper(...)`.
-- On the app side, this is driven by `hotReloadLwe()` in
-  `we_manager/src/main/services/lwe.service.ts`, which just writes the path
-  and sends the signal.
-
-## Why this should be less work than it sounds
-
-Both target settings already re-evaluate fresh on every reload, they're just
-not reachable from the control-file path yet:
-
-- **Layers**: `ApplicationContext::resolveObjectVisibility(id, name)`
-  (`ApplicationContext.cpp:261`) reads straight from
-  `settings.general.disabledObjects` / `enabledObjects`, and is called during
-  object construction (`CImage.cpp:865,929`, `CText.cpp:592`,
-  `CParticle.cpp:178`, `CScene.cpp:366`) - which already runs again on every
-  hotswap reload. If the control-file handler updates those two vectors on
-  `m_context.settings.general` *before* calling `loadBackground`, visibility
-  should just work without touching the object-construction code at all.
-
-- **Volume**: `GLPlayer::setVolume(double)`
-  (`VideoPlayback/MPV/GLPlayer.cpp:102`) already does a live
-  `mpv_set_property(..., "volume", ...)` on the existing mpv handle - no
-  reload needed at all for video wallpapers, it just needs a path from the
-  control file to this call. Scene/particle audio (`Render/Objects/CSound.h`)
-  wasn't checked in detail; confirm whether it has an equivalent live setter
-  or needs the same "re-apply on reload" treatment as layers.
-
-## Proposed design
-
-1. Replace the single-line control file with a small line-based or JSON
-   format so it can carry multiple fields without breaking existing readers,
-   e.g.:
-   ```
-   path=/path/to/wallpaper
-   disable-object=13
-   disable-object=110
-   enable-object=7
-   volume=42
-   ```
-   or a JSON object with the same fields. Either is fine; pick whichever is
-   less code given `checkHotswapRequest` is currently a simple
-   `std::getline`.
-
-2. In `checkHotswapRequest`, before calling `loadBackground`:
-   - If object fields are present, replace
-     `m_context.settings.general.disabledObjects` /
-     `enabledObjects` with the new lists.
-   - If a volume field is present and the current wallpaper is a video,
-     route it to `GLPlayer::setVolume` directly (no reload needed - this can
-     probably be handled as its own signal path, separate from a full
-     background reload, since it doesn't need `loadBackground` at all).
-
-3. Path stays optional in the payload: a volume-only or layers-only update
-   shouldn't require re-sending the current path and doing a full reload
-   when nothing about the *project* changed. Worth splitting into two
-   distinct requests internally: "reload background" (path/layers, since
-   layers currently only take effect through object construction) vs.
-   "live property push" (volume, and anything else with a direct setter).
-
-## Acceptance criteria
-
-- Toggling a layer on a running wallpaper no longer restarts the process
-  (no new PID, no DBus/CEF re-init log lines).
-- Changing volume on a running video wallpaper is audible within one mpv
-  property update, no visual interruption.
-- Existing plain path-only hotswap (playlist advance, "Play wallpaper" on a
-  different item) keeps working unchanged.
-- Rapid repeated toggles (stress case: 10 layer toggles in under 2 seconds)
-  don't crash, hang, or leak processes.
-
-## Follow-up work in we_manager (once this lands)
-
-- `src/main/services/lwe.service.ts`: extend `getControlFilePath`/
-  `hotReloadLwe` to write the richer payload, and add a variant that doesn't
-  require `isLweRunning()` to be false (currently only used as a fallback
-  inside `launchLweAsync`).
-- `src/renderer/src/components/common/DetailSidebar.tsx`: `toggleObject` and
-  `commitVolume` currently call `lwe.stop()` + `wallpaper.apply()` when
-  `isActive` is true - swap that for a new lighter IPC call (e.g.
-  `lwe.hotswapSettings(...)`) once the above exists, dropping the ~2-3s
-  relaunch wait and the "live" restart flash entirely.
-
-## Non-goals
-
-- fps: changing FPS live is a bigger change (render loop timing), not
-  covered here - keep it on the restart path unless someone wants to take
-  that on separately.
-- Web wallpapers: CEF browser reload semantics are different enough from
-  scene/video that they're out of scope for this task.

+ 11 - 8
tools/mdl_bones.py

@@ -3,10 +3,10 @@
 (to spot a mirrored/negative-scale bone), and translation - without needing a debug build.
 
 Mirrors the parsing in CImage.cpp's parsePuppetBones()/readPuppetMeshData(): MDLS header is
-9 bytes, then nextSectionOffset(u32) + boneCount(u32), then per bone: 1 padding byte + type(u32)
-+ parent(i32) + matrixBytes(u32) + [16 floats if matrixBytes==64] + null-terminated name. The
-16 floats are the file's row-major matrix - reshaping them row-major (not glm's column-major
-convention) gives the actual authored matrix back, which is what this script does.
+9 bytes, then nextSectionOffset(u32) + boneCount(u32), then per bone: null-terminated name
++ type(u32) + parent(i32) + matrixBytes(u32) + [16 floats if matrixBytes==64] + null-terminated
+extra string (empty for most rigs, jiggle/physics JSON for some). The 16 floats are the file's
+row-major matrix - reshaping them row-major (not glm's column-major convention) gives the actual authored matrix back, which is what this script does.
 
 Usage:
     mdl_bones.py <path-to-puppet.mdl>
@@ -40,7 +40,7 @@ def main(path):
     print('nextSectionOffset', next_section_offset, 'boneCount', bone_count)
 
     for i in range(bone_count):
-        offset += 1  # padding byte
+        name, offset = read_cstr(data, offset)
         offset += 4  # type, unused
         parent = struct.unpack_from('<i', data, offset)[0]
         offset += 4
@@ -51,18 +51,21 @@ def main(path):
             values = struct.unpack_from('<16f', data, offset)
             offset += 64
             matrix = np.array(values, dtype=float).reshape(4, 4)  # row-major as authored
+        elif matrix_bytes > 4096:
+            raise SystemExit(f"bone {i} has an implausible matrix byte count ({matrix_bytes}), layout desynced")
         else:
             offset += matrix_bytes
-        name, offset = read_cstr(data, offset)
+        extra, offset = read_cstr(data, offset)
+        extra_note = f" extra={extra}" if extra else ""
 
         if matrix is None:
-            print(f"{i} parent={parent} name={name!r} NO MATRIX ({matrix_bytes} bytes)")
+            print(f"{i} parent={parent} name={name!r} NO MATRIX ({matrix_bytes} bytes){extra_note}")
             continue
 
         det = np.linalg.det(matrix[:3, :3])
         translation = matrix[3, :3]  # row-vector convention: translation lives in the last row
         flag = "  <-- MIRRORED/NEGATIVE SCALE" if det < 0 else ""
-        print(f"{i} parent={parent} name={name!r} det3x3={det:.5f} translation={translation}{flag}")
+        print(f"{i} parent={parent} name={name!r} det3x3={det:.5f} translation={translation}{flag}{extra_note}")
 
 
 if __name__ == '__main__':