7.6 KiB
7.6 KiB
SlaveMatrix Engine Rewrite Plan
Goal
Rearchitect the game from a decompiled monolith with hardcoded Japanese string lookups, BinaryFormatter resources, and GDI+ rendering into a moddable, data-driven engine with Vulkan/Silk.NET rendering, YAML sidecar definitions, and SVG vector assets.
Key Decisions
- Clean break from save compatibility (no BinaryFormatter/RenameMap)
- Vulkan/Silk.NET rendering backend
- YAML sidecar data files for body part definitions
- Full modding support: new body parts, races, color schemes, animation states
- SVG extraction for vector graphics data; Vulkan rendering at runtime
- Runtime catalog built from sidecar files at load time
- No MigrateKeys -- resource data will have keys renamed at extraction time, not at runtime
Current State
- Phase 0 complete (
engine-rewritebranch, pushed togitea) - 843 BodyPartClasses with Japanese filenames (not yet touched)
- 13 binary
Objresources embedded in.resx(5.5MB largest) MigrateKeys()runtime key-mutation for 4 of 13 resources (fragile, incomplete)- 1,660 unique string keys across all resource lookups
- BinaryFormatter double-wrapped serialization
- GDI+ rendering with cardinal splines, GraphicsPath, hit-testing
- Existing
Ser.ToJson/Ser.UnJsoninfrastructure (patched for private fields and OrderedDictionary)
Phase 0 Deliverables
Asset pipeline tool — SlaveMatrix.Extract CLI:
- Loads all 13 binary resources via
ObjLoadRaw()(GDI+-free deserialization) - Applies
MigrateKeys()for 23 KeyMap entries - Exports structured JSON intermediate format
- Converts cardinal spline curves to cubic Bézier SVG paths
- Generates YAML sidecar files per body part
- Outputs
Catalog.yamlruntime index
Asset directory — SlaveMatrix/Assets/:
Assets/
Catalog.yaml <- 175 entries, auto-generated
Parts/ <- one directory per body part key
BaseHair/ <- English name (after MigrateKeys)
part.yaml <- id, original_key, resource, joints, fields, variants
x0y0.svg <- morph variant (X=0, Y=0)
BackHair0/
part.yaml
x0y0.svg ... x0y21.svg <- 22 morph variants
FrontHair/
part.yaml
x0y0.svg ... x0y18.svg <- 19 morph variants
上着ボトム後/ <- Japanese name (not in KeyMap yet)
part.yaml
x0y0.svg, x0y1.svg
...
Races/ <- (planned, Phase 2)
Colors/ <- (planned, Phase 3)
Key stats:
- 175 body parts extracted across 13 resources
- 838 SVG files (one per morph variant)
- 175 YAML sidecar files
- 1 Catalog.yaml index
- KeyMap entries for 23 Japanese→English mappings
Phase 0: Resource Extraction & Asset Pipeline ✅ COMPLETE
0.1: Fix JSON Serialization ✅
- Add
JsonConverter<OrderedDictionary<T1,T2>>for proper dictionary round-tripping - Manual export walker instead of JsonSerializer serialization (avoids GDI+ and self-reference issues)
- Add
ObjLoadRaw()for GDI+-free binary resource loading - Clean JSON methods using
JsonConvertAPI with consistent settings
0.2: Build Key Map ✅
- Added hair class entries:
基髪→BaseHair,胸毛→ChestHair,前髪→FrontHair - 23 total KeyMap entries (pre-existing + new)
- No Pars-level recursive migration needed (manual export avoids it)
0.3: Write Extraction Console App ✅
- Created
SlaveMatrix.Extractconsole project - Loads all 13
Objfrom embedded resources viaObjLoadRaw() - Applies
MigrateKeys()with current KeyMap - Exports each
Objto intermediate JSON file - For SVG export: converts cardinal spline to cubic Bezier via Catmull-Rom tension formula
- For YAML: exports part metadata (id, original_key, resource, morph dimensions, joints, variant SVGs)
- Outputs
Catalog.yamlindex of all parts
0.4: Validate Extraction ✅
- Manual inspection of SVG output confirms valid path data with cubic Bezier curves
- All 175 parts export without errors
- 838 SVGs generated across all morph variants
Phase 1: Engine Foundation
- New .NET 8 solution:
SlaveMatrix.Engine,SlaveMatrix.Modding,SlaveMatrix.Game,SlaveMatrix.AssetTool - Vulkan/Silk.NET renderer: GPU path rendering, fill/stroke, hit-testing
- YAML part definition schema (connections, variants, colors, joints)
- Asset loader: scan
Assets/directories, build runtimePartCatalog - Morph interpolation between SVG variants
Phase 2: Character Assembly & Joints
- Explicit joint definitions in YAML (no proximity-based matching)
- Connection resolution: parent-child graph from catalog
- Character builder: given race template, assemble character from parts
- Runtime catalog on startup
Phase 3: Game Logic Migration
- YAML-based character saves (replace EleD serialization)
- YAML-driven color palette system (replace
配色指定/BodyColorSet) - Pose/animation system (same morph concept, YAML-defined)
- Port UI
Phase 4: Modding API
- Mod package format: manifest.yaml + Parts/ + Races/ + Colors/
- Mod loader: merge catalogs, validate connections
- Hot reload during development
Phase 5: Polish & Release
- Build pipeline: MSBuild targets for asset packing
- Performance: GPU instancing, spatial partitioning
- Visual regression tests
Resource Architecture
Current (Binary)
Resources.resx -> byte[] -> BinaryFormatter(BinaryFormatter(Obj)) -> Obj
Obj.Difss keys are Japanese
MigrateKeys() renames some keys at runtime (fragile)
Target (YAML + SVG) ✅ Phase 0 complete
Assets/
Catalog.yaml <- auto-generated index
Parts/
BaseHair/ <- one dir per body part (English name)
part.yaml <- sidecar: id, original_key, resource, morph, joints, fields
x0y0.svg <- morph variant SVG (X index, Y index)
x0y1.svg <- next morph variant
...
Races/ <- (planned, Phase 2)
Colors/ <- (planned, Phase 3)
Game loads YAML+SVG at runtime, no embedded binary resources needed.
Modders add directories under Assets/Parts/ to add new parts.
Rendering Architecture
Current (GDI+)
System.Drawing.Graphics.FillPath/DrawPathfor fills and strokesGraphicsPath.AddCurve(points, tension)for cardinal splines- Separate hit-test pass with unique colors per
Par Difs.ValueX/ValueYcontrols morph interpolation (discrete variant selection)
Target (Vulkan/Silk.NET)
- GPU path rendering: tessellate cubic Bezier curves into triangles
- Separate hit-test pass: render with unique colors to offscreen buffer, read pixel
- Morph interpolation: blend control point positions between SVG variants
- YAML-driven pose system replaces hardcoded
Difsindex selection
Data Format: YAML Part Definition (actual)
id: BaseHair
original_key: 基髪
resource: 胴体
morph_x: 1
morph_y: 1
variants:
- x: 0
y: 0
file: x0y0.svg
fields:
- name: 髪
joints:
- position: [0.34, 0.34]
- position: [0.39, 0.34]
- position: [0.37, 0.33]
- position: [0.37, 0.34]
Data Format: YAML Race Template (planned)
id: human
parts:
torso: { variant: human, morph_x: 0.5, morph_y: 0.3 }
head: { variant: human }
hair: { variant: long }
left_arm: { variant: human }
right_arm: { variant: human }
left_leg: { variant: human }
right_leg: { variant: human }
colors:
skin: { palette: skin_tones, index: 0 }
hair: { palette: hair_colors, index: 3 }