Skip to content

Modernize and organize project to current .NET standards, make cross-platform#5

Open
gtkramer wants to merge 45 commits into
NCDyson:masterfrom
gtkramer:master
Open

Modernize and organize project to current .NET standards, make cross-platform#5
gtkramer wants to merge 45 commits into
NCDyson:masterfrom
gtkramer:master

Conversation

@gtkramer
Copy link
Copy Markdown

@gtkramer gtkramer commented May 30, 2026

Let me know if you want to change anything. This incorporates changes from @Casuallynoted, @taarna23, as well as my own. It fixes #4, wires up bounding box logic, gets the project to run cross-platform with modern .NET, and aligns repo artifacts and organization to current .NET standards.

gtkramer and others added 30 commits May 29, 2026 02:32
Modernize the project and improve cross-platform compatibility
Have the camera move more closely with the mouse
Loading previously ran the whole CCSFile read+init inside the render
callback (on the UI thread), so parsing a large file froze the UI and
stalled the animation. Split loading into a CPU-only parse phase that
runs on a background Task and a GL-upload phase that stays on the render
callback (the only place the GL context is current):

- Scene.LoadCCSFile -> ReadCCSFile (parse, any thread) + InitCCSFile (GL).
- EnqueueGlJob is now callable from any thread; it marshals the
  RequestNextFrameRendering wake-up to the UI thread.
- MainWindow.LoadFiles parses on Task.Run, enqueues the GL init, then
  adds the tree node via the dispatcher; read failures are caught/logged.

Because logging now happens off the UI thread, guard Logger's de-dup
dictionaries with a lock and restructure AppendLog to marshal to the UI
thread before echoing, so each message is written exactly once.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The windows were ported WinForms-style: each declared private fields
mirroring its controls, assigned them via this.FindControl<T>("name") in
the constructor, and defined a manual InitializeComponent that called
AvaloniaXamlLoader.Load. Avalonia's XAML compiler already generates
strongly-typed fields for every x:Name'd control plus InitializeComponent,
so all of that was redundant boilerplate with only runtime-checked names.

Drop the manual InitializeComponent methods and the ~36 FindControl
lookups across the four windows and reference the generated fields
directly. This also removes the AvGrid alias in MainWindow (the generated
fields are already typed, so the StudioCCS.Grid vs Avalonia.Controls.Grid
ambiguity no longer surfaces). No behavior change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The main window populated its TreeViews by recursively constructing
TreeViewItems in code (BuildTreeItem/BuildSceneAnimeItem) and drove the
View-menu toggles + status bar through imperative handlers
(ApplyViewMenu, IsChecked lookups, a per-frame UpdateStatus).

Move this to idiomatic Avalonia:

- A light MainViewModel (ViewModelBase + INotifyPropertyChanged) exposes
  the tree data sources (CcsRoots / SceneRoots), the render-option toggles
  (which write straight through to the static Scene), and the status text.
  It's a thin shim over Scene, not a full MVVM layer.
- The TreeViews bind ItemsSource to those collections and render via a
  shared compiled-binding TreeDataTemplate over CcsTreeNode; per-node-type
  context menus move to ContextRequested handlers (CcsTreeNode lives in the
  portable model and shouldn't carry UI command info).
- View-menu items two-way bind IsChecked to the view-model; the status bar
  binds its text. A timer just refreshes the camera string.
- CcsTreeNode.Nodes becomes an ObservableCollection so the scene tree
  updates when animations are added/removed after binding.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
StudioCCS.Grid (the static OpenGL grid-renderer helper) collided with
Avalonia.Controls.Grid, which had forced a 'using AvGrid = ...' alias in
the window code-behind. Rename the class (and its file) to GridRenderer so
the name no longer shadows the Avalonia control. Only the three call sites
in Scene.cs reference it; the "Grid" shader-file name and log strings are
unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Bring the bone editor in line with the rest of the UI and remove
duplication that had accumulated across the windows:

- EditBoneWindow now binds its TreeView to CcsTreeNodes (Tag = the bone)
  via ItemsSource instead of hand-building TreeViewItems, matching the CCS
  and scene trees. The BoneNodeTag wrapper is gone (the CCSObject is the
  Tag directly).
- The CcsTreeNode TreeDataTemplate moves to App-level resources so all
  three trees share one definition rather than redeclaring it.
- File-picker type descriptors (All / CCS / Bin) are centralized in a
  FileFilters helper, replacing three inline copies across the load,
  load-matrix, and pose dialogs.
- The two ContextRequested handlers share helpers (ContextNode, Menu,
  OpenNodeMenu) so node extraction, menu construction (single IList cast),
  and pointer-placed opening are written once; pattern matching is
  standardized on 'is not'.

No behavior change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Preview/Scene/All toggle was three ToggleButtons whose mutual
exclusion was hand-maintained with a _suppressModeEvents reentrancy guard
and manual IsChecked juggling in code-behind — the one imperative holdout
in an otherwise VM-bound UI.

Replace them with grouped RadioButtons (mutually exclusive by design)
bound to a new MainViewModel.Mode property (with Is*Mode bool wrappers for
the bindings). Mode is the single source of truth and writes through to
Scene.SceneDisplay. Code-behind now only mirrors mode changes into the
panel layout (kept there because the column GridLength collapse doesn't
bind cleanly), via the view-model's PropertyChanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Scrolling up now zooms the camera in (toward the model) rather than out,
which matches the common convention. Negated the wheel delta forwarded
from the viewport to Scene.MouseWheel.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Clicking a tree item's label now expands/collapses it, so users don't
have to hit the small expand/collapse chevron. A shared TreeViewExpand
helper handles the TreeView's Tapped event: it ignores taps on the
chevron (which already toggles) and otherwise toggles IsExpanded on the
tapped row if it has children. Wired to all three trees (CCS objects,
scene animations, clump bones).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The orbit camera reconstructs its orientation with Matrix4.LookAt and a
fixed +Y up-vector. At exactly +/-90 deg pitch the view direction aligns
with that up-vector, the camera basis (cross(forward, up)) collapses, and
the model and axis gizmo flip 180 deg (gimbal lock at the pole).

Clamp pitch to +/-89.9 deg so the turntable never reaches the singular
orientation: rotation stays smooth and level right up to a near-top-down
view. 89.9 keeps an effectively straight-down view while staying well
clear of float precision issues (cos(89.9 deg) ~= 0.0017).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Shaders (data/shaders/*.{vsh,fsh,gsh}) and the vertex blobs
(data/bin/*.bin) are now compiled in as AvaloniaResource items and read
at runtime through avares:// URIs via a new EmbeddedData helper, instead
of opening FileStream/StreamReader against files copied to the output
directory. This drops the data/**/* copy-to-output rule; only
blenderDummyImport.py is still copied out.

Since the data is now immutable and assembly-resident, the disk
reload-from-file paths no longer make sense: removed the unused
AxisMarker.Reload() and demoted WireHelper.ReadBin() to private (it has
only the single internal Init() caller).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Only the GL bindings and math types are used; Avalonia provides the
window and GL context. Dropping the OpenTK metapackage removes the
unused GLFW windowing native redist, audio, compute, and input packages.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The old static Logger owned plumbing the framework already provides and
had several design flaws: severity was encoded as a System.Drawing.Color
(so the core had no log levels and couldn't filter), dedup keyed on
string.GetHashCode() (collisions could silently drop distinct messages),
the LogOnceCode half was dead, and caller info was captured then discarded.

Adopt Microsoft.Extensions.Logging via a thin static facade (Log.Error/
Warning/Info) over a LoggerFactory configured once at startup. This matches
the codebase's existing static-utility idiom (no DI container) while letting
the framework own levels, filtering, formatting, and the stdout sink.

What we still own is just the two things we actually care about:
- PanelLoggerProvider: routes formatted output to the in-app log panel and
  maps LogLevel -> Color in the view layer (the only place Color now lives).
- LogOnce: per-frame flood protection, keyed on the message string (fixes
  the GetHashCode collision risk). Replaces the old LogType machinery; the
  4 render-loop call sites now pass once: true.

Log.* also trims trailing newlines centrally, so each sink owns line
termination (the framework console provider and the panel provider each
append one) instead of every call site baking in "\n".

Verified: app loads CCS files, GL context initializes, and info/warn/fail
all emit through the console provider with correct level prefixes and no
double-newlines (0 across 3526 log lines over a 30-file load).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The in-app log panel was a plain read-only TextBox: it ignored the colour
the provider passed and showed no severity, so it read as flat, level-less
text while stdout (via the console provider) was clearly tiered. Make the
panel a copy of stdout.

- PanelLoggerProvider now prefixes each line with the console's level
  abbreviation ("info:/warn:/fail:" etc.) and emits one line per log call
  (no trailing newline; the facade already trims). Severity colours are
  brightened to read on a dark surface.
- The panel becomes a virtualizing ListBox of colour-coded lines (LogLine:
  text + brush), keeping it responsive under heavy logging where the old
  Text += concat was O(n^2). Backing collection is capped at 2000 lines,
  oldest dropped first, and auto-scrolls to the newest.
- The panel background is fixed dark (not theme-dependent) so white/orange/
  red stay legible regardless of the OS light/dark setting.

Verified live: loaded several CCS files and confirmed the panel renders
orange "warn:" and grey "info:" lines on the dark console, mirroring stdout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The SimpleConsole formatter printed "info: StudioCCS[0] message" — the
category/eventId is constant and redundant in a single-app process, and it
made stdout diverge from the in-app log panel ("info: message").

Replace SimpleConsole with a small custom ConsoleFormatter that prints just
"<level>: <message>", ANSI-colouring the level tag only on a real terminal
(suppressed when stdout is redirected). The level abbreviation now comes from
a shared LogLevelTag helper used by both the console formatter and the panel
provider, so the two renderings can't drift apart.

Verified: stdout shows "info:/warn:/fail: <message>" with no "StudioCCS[0]"
and no stray ANSI escapes when redirected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The console and panel each had their own severity-colour table (console:
ANSI yellow/green/red; panel: orange/grey/bright-red) and disagreed on scope
(console coloured just the level tag, panel coloured the whole line). So even
matching text rendered with different colours.

Introduce LogPalette as the single source of truth for severity colours (RGB).
The console formatter emits a 24-bit truecolor ANSI escape from it; the panel
builds its brush from the same values — so the two are identical by
construction. Both now colour only the severity tag: the panel template renders
the tag in its colour and ": <message>" in a neutral light grey, matching the
console (which resets colour before the message).

Verified: panel shows an orange "warn:" tag with a neutral message, and a pty
capture of stdout shows "\e[38;2;255;165;0mwarn\e[0m: ..." — same orange,
tag only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The GL profile was requested only through X11PlatformOptions, so the
desktop OpenGL 3.3 (#version 330 + geometry shader) context the CCS
shaders require was set up on Linux alone. On Windows the app fell back
to ANGLE (OpenGL ES), which cannot compile those shaders.

Choose the profile by platform from one shared desktop profile list:
Windows switches to native WGL (RenderingMode=Wgl + WglProfiles), Linux
keeps X11 GlProfiles. macOS (AvaloniaNative) exposes no GL-version
selector, so it is documented as requiring on-device verification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Encoding.Default is platform-dependent (Windows-1252 on Windows, UTF-8
elsewhere), so any name byte >= 0x80 decoded differently per OS. CCS
asset names are ASCII; using Encoding.ASCII makes parsing deterministic
across platforms.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Store text files with LF in the repo while checking them out with each
platform's native ending (CRLF on Windows, LF elsewhere), keeping history
consistent without fighting local tooling. Mark known binary assets
(.dds/.png/.bin/.ccs) so they are never normalized.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
texture2D was removed from GLSL in the 330 core profile; desktop Mesa
and NVIDIA/AMD drivers accept it leniently, but Apple's strict core
compiler rejects it, which would fail shader compilation on macOS.
Switch the three active samplers to the overloaded texture() call.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verify the negotiated context is desktop GL 3.2+ in OnOpenGlInit; if it
is older, disable the viewport with a clear log message instead of
binding shaders that never compiled and spamming GL errors into a black
viewport every frame. The disabled state fills the viewport dark red and
stops requesting frames rather than spinning.

Also register a KHR_debug callback in debug builds (where the context is
4.3+) to surface driver warnings and errors through the existing log,
making platform-specific rendering issues diagnosable instead of silent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The capability guard accepted 3.2, but the shaders are #version 330 and
need GL 3.3, so a 3.2 context would pass the guard and then fail every
shader compile - the same silent black viewport the guard exists to
prevent. Raise the floor to 3.3 and drop the unusable 3.2 entry from the
context negotiation list. 3.3 is available everywhere; macOS meets it
with its 4.1 core context (it offers only 3.2 or 4.1, never 3.3).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Five shaders (CCSClump and Triangle) declared bare #version 330 while the
rest used #version 330 core. The two are equivalent on a core context
(330 defaults to core), so this is a consistency/intent change, not a
behavioral one: every shader now explicitly states it targets the core
profile and relies on no compatibility features.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A SharpDevelop/Windows-era toolchain had saved 46 source, shader, and doc
files as UTF-8 with a leading BOM (EF BB BF). The marker is redundant for
UTF-8, breaks naive first-line parsing (it hid shader #version lines from
grep), and is out of place in a cross-platform repo. Remove the three BOM
bytes from every affected file - content and LF line endings are otherwise
untouched.

Add a root .editorconfig pinning charset = utf-8 (no BOM) so Windows
editors do not silently reintroduce it, complementing the existing
.gitattributes line-ending normalization.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ask the WGL/X11 backends for OpenGL 4.6 (the highest version) first so
capable Windows/Linux drivers also grant KHR_debug (core in 4.3), which
lets the debug-build output callback actually engage. The list still
falls back to 3.3 - the minimum the #version 330 shaders require - when a
driver cannot provide anything newer. macOS is unaffected (AvaloniaNative
ignores this list and caps at a 4.1 core context).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the render-info readout out of the full-width bottom strip and into
the viewport column: render mode in a top bar, live camera state in a
bottom bar, so the GL surface is sandwiched between them and the tree
spans the full side. The mode bar gets the full viewport width since the
list grows as more views are toggled on.

Also reformat the camera readout with grouped axis labels, degree marks,
and fixed-width fields rendered in a monospace font so it stays legible
and stops jittering as it refreshes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Fluent-themed controls already tracked the OS light/dark setting, but
the GL viewport (clear colour + grid lines, set in raw GL) and the Mode/
Camera status bars (hardcoded #202020) stayed dark regardless, which looked
out of place against a light UI.

Drive the viewport clear/grid colours from Scene.BackgroundColor/GridColor,
re-applied every frame and updated on ActualThemeVariantChanged so an OS
theme switch is reflected live. Move the status-bar palette into theme-keyed
ResourceDictionaries (DynamicResource) so the bars re-resolve on a switch.
The dark values are unchanged; only a light-mode variant is added. The log
panel stays fixed-dark by design (its severity colours need a dark ground).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
gtkramer and others added 15 commits May 29, 2026 06:07
Convert the plain-text, tab-indented readme into proper Markdown:
heading hierarchy, lists, fenced code blocks, blockquotes for asides,
and tables for the CCS generations, model types, and controls.

Add Requirements, Building & Running, a table of contents, and a
License section for completeness, fix assorted typos, and rename
Readme.md to README.md to match the conventional casing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Write per-vertex RGB color alongside each OBJ position and split the
vertex/texcoord/normal output into separate passes.

Open the SMD output with FileMode.Create instead of FileMode.Truncate so
export no longer throws when the target file does not already exist, and
re-enable the companion bind-pose (_bind.smd) dump.

Ported from Casuallynoted's fork (commit 848d9a5) onto the Avalonia base.
Rework the rotation/position/scale tracks so a controller's interpolated
pose threads rotation into position into scale, keyed by object name, and
stop discarding duplicate keyframes (use Keys.Count and skip the
Keys.Remove on equal frame numbers) so interpolation has the keys it needs.
Includes the per-bone coordinate corrections for the .hack player skeleton.

Add per-frame preview export of the selected animation to SMD
(CCSAnime/CCSClump.DumpPreviewToSMD, Scene.DumpPreviewToSMD) wired to a
debug-only 'Dump Preview to .SMD...' menu item, drop the triangle-strip
winding alternation, return raw radians from ReadVec3Rotation, and add
Util.toDeg.

Ported from taarna23's fork onto the Avalonia base. The fork's .NET/OpenTK
upgrade and whitespace reformatting are intentionally omitted; only the
functional animation/export changes are carried over.
Expand .editorconfig to enforce UTF-8 (no BOM), LF line endings, final
newlines, no trailing whitespace, and 4-space indentation, plus the
Microsoft C# coding conventions (using placement, language keywords over
BCL types, brace style, naming) as analyzer/IDE rules.

Run `dotnet format` and normalize the tree to match:
- reindent the legacy tab-indented files (libCCS, Scene, GL helpers) to
  4 spaces; sort using directives
- strip trailing whitespace and add missing final newlines everywhere
- convert stray tabs to spaces in shaders, the data XML, and comments;
  keep the intentional display tabs in CCSAnime.Controllers.cs as \t
- remove the auto-generated SharpDevelop header comment from 40 files
  (none contained file-specific documentation)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
EditorConfig's end_of_line has no "native" value, so the previous
end_of_line = lf fought .gitattributes' per-OS checkout (* text=auto):
on Windows, files checked out as CRLF were rewritten to LF on save,
producing mixed working trees.

Resolve in favor of native-per-OS checkout: drop end_of_line from
.editorconfig so editors preserve whatever Git checked out, and keep
* text=auto so the repo stays LF while working trees follow the OS
(CRLF on Windows when core.autocrlf is enabled, LF on Linux/macOS).
Expand both files' comments to document the coupling.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the TestTriangle debug renderer and its Triangle shaders, the
unreferenced ScalableLine and CCSDeformModel shaders, the unused
SceneInstanceObject stub and commented-out SceneClumpInstance class in
Scene.cs, and the unused ArcBallCamera two-arg constructor plus its
commented-out property block.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The bounding box program was unfinished and unreachable: it was loaded
under the wrong name ("BBox" vs the BoundingBox.* shader files), its
fragment shader was empty and its geometry shader had no main(), the
vertex attribute/uniform names did not match the C#, and Init() both
referenced non-existent struct fields in Marshal.OffsetOf and wiped the
extents read from the file. Nothing ever drew the boxes, and the wrong
shader name would have thrown rather than failing gracefully.

- Harden Scene.LoadShader so a missing/misnamed shader logs and returns
  -1 (via the existing ProgramID == -1 path) instead of throwing.
- Write a complete BoundingBox shader set: the vertex stage passes the
  AABB min/max and colour through an interface block, the geometry stage
  expands the point into the 12 wireframe edges via UMatrix, and the
  fragment stage outputs the colour.
- Fix CCSBoundingBox: load "BoundingBox", correct the OffsetOf field
  names (Minimum/Maximum), keep the read extents and set a wireframe
  colour, and add a Render() that draws one point per box.
- Initialise and release BBoxList in CCSFile.Init/DeInit (previously
  skipped, so boxes were never set up).
- Add a DrawBoundingBoxes scene toggle, wire it through MainViewModel,
  and add a "Draw Bounding Boxes" menu item.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Regroup the catch-all root namespace and the flat 40-file libCCS library
so every folder maps to an idiomatic namespace grouped by functional area:

- libCCS -> StudioCCS.FileFormat, subdivided into .Geometry, .Materials,
  .Animation, .SceneObjects (Light/Camera/Dummy), and .Raw (opaque
  passthrough sections); the format/parsing core stays at the .FileFormat
  root. The namespace is named for the layer rather than the format's
  "CCS" initials, so it does not stutter against the CCS-prefixed type
  names (StudioCCS.FileFormat.Geometry.CCSModel).
- OpenGL render code -> StudioCCS.Rendering, with the gizmo renderers
  under .Gizmos; EmbeddedData -> StudioCCS.Resources
- Correct the Logging folder/namespace mismatch (now StudioCCS.Logging)
- Keep the shared tree-model types (CcsTreeNode, TreeNodeTag) at the root
  so no XAML or .csproj changes are needed

A single GlobalUsings.cs declares the new namespaces project-wide, which
preserves the codebase's import-free ergonomics with no per-file using
churn. Also align three filenames to their type names (CCSBinaryBlob,
CCSFileHeader, IndexObjectEntry).

Pure reorganization with no behavior change: the build is clean (0/0) and
the app launches, initializes OpenGL, and renders.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Every CCS-format type uses all-caps "CCS" (CCSFile, CCSModel, ...), but a
few identifiers still used the soft "Ccs" casing. Normalize them all:

- CcsTreeNode / CcsTreeNodeCollection -> CCSTreeNode(Collection), with the
  file renamed to CCSTreeNode.cs
- CcsRoots -> CCSRoots, CcsNodeTemplate -> CCSNodeTemplate,
  FileFilters.Ccs -> .CCS, and the OnCcsTree*/BuildCcsNodeMenu members

Updates all references, including the App.axaml TreeDataTemplate
x:DataType/x:Key, the CCSRoots binding, and event wiring. The lowercase
`ccs` cases (xmlns prefix, the ccsTree control field, the *.ccs file
pattern) are intentionally left as-is per camelCase/XML conventions.

No behavior change: build is clean (0/0) and the app launches and renders.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The shaders and gizmo vertex blobs were embedded from a top-level data/
folder, but every one of them is loaded by the Rendering layer (Scene
loads shaders by name; the gizmos load their own blobs). Move them -- and
the loader itself -- next to that code:

- data/shaders/ -> Rendering/Shaders/ (kept as one name-keyed pool that
  Scene.LoadProgram resolves)
- data/bin/*.bin -> Rendering/Gizmos/, beside the AxisMarker/WireHelper
  code that reads them; WireHelpers.bin is renamed to WireHelper.bin to
  match its class and shaders
- EmbeddedData (the avares:// loader, whose only callers are Scene and the
  gizmos) -> Rendering/ (StudioCCS.Rendering); the one-file Resources
  folder/namespace is dropped

Updates the Scene shader-path prefix, the gizmo blob paths, the two
AvaloniaResource globs, and GlobalUsings.cs. The avares:// URIs are
assembly-relative to the source path, so they resolve under Rendering/.

No behavior change: build is clean (0/0) and the app launches, loads the
shaders/blobs from their new paths, and renders.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Gen1_Scenes.xml (Gen1 scene reference data) and blenderDummyImport.py (a
Blender import helper) were a lone orphan in data/xml/ and a script at the
repo root. Collect them under Extras/, a neutral catch-all for supporting
files, which fully retires the data/ folder.

The Blender script is not used by this C# application, so drop its
CopyToOutputDirectory entry from the csproj -- it stays in the source tree
only and is no longer copied to the build output.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
More or less done with own changes
CCSFile.Read(string) opened a FileStream and BinaryReader without ever
disposing them, leaking a file handle on every load. Wrap both in using
declarations so the handle is released when loading finishes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The helper takes a name and a type but compared only the type, returning
the first object of that type regardless of name. The bug is inherited
verbatim from upstream and the method has no callers yet, but its
contract is a name-and-type lookup; compare ObjectName as well, mirroring
the working GetObjectByNameAndType<T> right below it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@taarna23
Copy link
Copy Markdown

Interesting. I just recently updated to .net core 10, myself. It's allowed me to keep working on this on linux. I have a bit of a hack-jobby (pun intended?) fix for animations in my repo now, too, though the interpolation is as bad as it always was.

@gtkramer
Copy link
Copy Markdown
Author

gtkramer commented May 31, 2026

Can you tell me more about your Linux setup? Are you maybe using WSL? I tried cloning your master branch on Arch Linux, ran dotnet build and dotnet run, and this is what I got:

$ dotnet build
    /tmp/StudioCCS/libCCS/CCSModel.cs(762,6): warning CA1416: This call site is reachable on all platforms. 'TreeNodeCollection.Add(TreeNode)' is only supported on: 'windows' 6.1 and later. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416)
    /tmp/StudioCCS/libCCS/CCSModel.cs(762,6): warning CA1416: This call site is reachable on all platforms. 'TreeNode.Nodes' is only supported on: 'windows' 6.1 and later. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416)
<... omitted many more lines ...>
    /tmp/StudioCCS/libCCS/CCSModel.cs(751,6): warning CA1416: This call site is reachable on all platforms. 'TreeNodeCollection.Add(TreeNode)' is only supported on: 'windows' 6.1 and later. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416)
    /tmp/StudioCCS/libCCS/CCSModel.cs(746,28): warning CA1416: This call site is reachable on all platforms. 'TreeNode' is only supported on: 'windows' 6.1 and later. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416)

Build succeeded with 557 warning(s) in 1.1s

$ dotnet run
Unhandled exception: An error occurred trying to start process '/tmp/StudioCCS/bin/Debug/net10.0-windows/win-x64/StudioCCS.exe' with working directory '/tmp/StudioCCS'. Exec format error

I tried rm -rf bin obj and then redoing this, but that didn't seem to help. Do you maybe have more enabling changes locally that haven't been pushed yet?

I checked my Windows box, and what's in this PR built and ran fine there.

@taarna23
Copy link
Copy Markdown

Oh, yes. There are a mountain of warnings. I'm running Garuda Linux, which is based on Arch. I honestly never used dotnet run. I just test from inside VSCode.

@gtkramer
Copy link
Copy Markdown
Author

Do you maybe have Mono installed on Guarda? That would explain how a Windows Forms app can run on Linux, since Mono ships with its own implementation.

@taarna23
Copy link
Copy Markdown

I don't believe so. It just launches in WINE.

@gtkramer
Copy link
Copy Markdown
Author

That makes sense now. I wanted to do away with the necessary Wine layer for the Windows Forms bits and go native on Linux so anyone can git clone and go without extra setup. Given Windows Forms is in maintenance mode, Avalonia seemed like the modern forward-looking choice.

@taarna23
Copy link
Copy Markdown

That's a great plan. Given that I'd never dragged an older app into modernity like this, that felt like a good start for me. Honestly, I was just happy not having to borrow my partner's laptop to work on the darn thing. I didn't even realize anyone else was working on this. It's good to see.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Vertex Color Export/FBX Export

2 participants