Repository navigation
0.10.0 - #19
Merged
Merged
0.10.0#19
Conversation
…l for async sync
Async sync setups can now serve on-demand sync REQUESTS: targets marked
requestable get a local, unsynced "base/Req/target" Bool, and the send cycle
jumps to a requested slot at the next step boundary instead of waiting out the
pass. Redirects carry the ring's exit time, so requests queue at boundaries
(one per step, cycle order) and never shorten a slot's dwell; the serving
driver clears the flag. Costs no synced bits.
Raising a request is packaged as a per-state Sync Request component: records
live in GraphFrameData, materialized as a DaerD-managed localOnly Parameter
Driver, editable from the state inspector, with the sync layer regenerated in
place when it has to start listening. Recipes get .Requestable(...).
Since a generated sync layer's graph is machinery nobody reads, the centre
pane now shows a dedicated settings panel for it (shared form with the
wizard), with the raw graph one click away.
Also:
- default base name is now derived from the controller's asset GUID
("DD" + 6 hex digits) so two distributions multiplexing on one avatar don't
both own "Async/*"; unnamed recipe setups keep a saved "Async" for compat
- parameter store: bulk SetSynced, an "Add All" action adding missing
controller parameters as unsynced rows (MA Parameters prefab workflow), and
a one-click fix unsyncing multiplexed targets behind the existing warning
- forward-port of the 0.9.1 fix generating the Empty clip as a controller
sub-asset
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… time, linear smooth, digit separation, trigonometry Eight new gadget kinds alongside the existing arithmetic/logic set: - Reciprocal (1/x): a normalized Direct tree covers x > 1 (an empty motion weighing x-1 next to an "output = 1" clip weighing 1), and a supporting layer covers 0 < x < 1 with a 1/x curve clip read by motion time. - Divide: B's reciprocal into output/Inv, multiplied with A. - Frame Time: a looping clock layer (one unit per second) subtracted from its own previous reading — output is the seconds since the last frame. - Smooth (Linear): constant-speed follow via a delta parameter and a step-size weight; pairs with Frame Time for frame-rate independence. - Separate Digits: splits a 0..1 float into tenths/hundredths/thousandths through the subnormal-float division trick (the constants are the algorithm and are ported verbatim). - Sine / Cosine / Tangent: one-second curve clips played by motion time, 0..1 = one turn; tan is pinned to +100 at its poles. Supporting layers are appended at the end of the controller on purpose: they write the same parameters as the blend tree, and only a later layer overrides an earlier one. Their states keep Write Defaults OFF so an idle state doesn't stomp the tree's result. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…the Project window immediately Two gadgets that stay entirely inside the blend tree layer: - LUT (Curve): bakes an arbitrary AnimationCurve into a 1D tree — thresholds are evenly spaced samples of the curve's time axis, children AAP clips of its value, deduped per distinct value. Preset shapes (Linear / Sqrt / Square / Smoothstep / Ease Out) fill the curve field, which stays editable. - Atan2: a Freeform Directional ring holding its own angle in turns (0..1 counter-clockwise from +X, the unit Sine/Cosine consume). The 0/1 seam is pinned inside a narrow wedge at +X; a value-0 origin child covers vanishing vectors. Ring resolution is configurable (8..64 directions). Also fixes sub-asset visibility: AddObjectToAsset alone leaves new clips and trees out of the Project window until the next save, so gadget applies, the graph's blend tree state creation and nested-tree adds now save and reimport the controller once per batch (DbtBuilder.CommitSubAssets). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e depths Every blend tree stage costs one frame, so two branches tapping the same parameter at different depths read different frames of it. The buffer turns that into a tool: a chain of identity remaps, one per frame (1..8), inserted on the shallower branch so both signals arrive at the same age. A single frame is a bare identity remap; longer chains route through intermediate stage parameters, which go through the same "must be new" gate as the output so a name collision can't silently corrupt what owned it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GKDgZCam5r4RjxbgByEzZM
…ones
c.Gadgets("Layer") declares the per-frame float math the parameter panel's Add
menu generates — all 22 kinds, each argument taken as a typed parameter handle
or a plain name. The named layer, the supporting layers some gadgets bring, and
every parameter under a declared output name are swept before each Generate: a
gadget refuses to write an output that already exists, so without the sweep the
second Generate would report every one of them as a collision and stack the
layers besides. One sub-asset flush per layer now, instead of one per gadget.
Exported recipes come in two halves of one partial class: "<Name>.Generated.cs"
is rewritten on every export, "<Name>.cs" is the author's and is written only
once. The round trip — export, reshape the code, Generate, export again — threw
the reshaping away every time; now the re-export lands beside it, and its own
git diff is a clean report of what changed in the controller since last time.
Compare on the recipe asset holds the two halves against each other at the IR
level, so reshaping by hand or with an AI is checkable instead of hopeful: it
passes on a loop that replaces three spelled-out states and fails on a state
that quietly changed name. A single-file recipe from before the split migrates
on export, keeping its .meta so existing recipe assets stay bound to it.
Layers a post step generates (async sync cycles, gadget layers) now carry the
recipe's ownership mark, which is what the panels' "add it in the recipe
instead" hints were already written for.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GKDgZCam5r4RjxbgByEzZM
The skill still told the reader to edit the exported file, which is now the half every export overwrites. It gets the file map, the delegating one-liner it is meant to replace, and the fields it must not redeclare (they live in the generated half, reachable because the class is partial). Compare leads the verification list now — it holds both halves against each other without touching a controller, so it is the cheap check that a reshaping kept its meaning. Two things it cannot see are called out: post steps (Raw, async sync, gadgets) are outside the IR, and gadget calls in particular must keep their order, since a later gadget may read an earlier one's output. Also adds the other half of the workflow the split exists for: after a re-export only the generated file moves, so its git diff is the change list to carry into the reshaped half. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GKDgZCam5r4RjxbgByEzZM
…errides A StateMachineBehaviour can sit on a state machine as well as on a state, and the IR modelled only the latter. Exporting a controller dropped those silently, Generate deleted them from the controller when it rebuilt the layer, and Verify reported nothing — the field was missing on both sides of the comparison, so there was no difference to find. Every other part of DaerD already handles them (layer copy, cleanup, the analyzer, parameter usage), which is what made this one easy to miss. The machine now carries its behaviours through parse, rebuild, diff and export, and MachineScope grows the same BehaviourJson / Behaviour<T> pair a state has — recorded unchained, since it returns the base scope and folding it into the declaration line would retype the variable and break the layout block. Synced layers get the other half of their overrides too: the behaviours a layer runs on a source state instead of the source's own. They are instances of their own rather than borrowed, so applying one creates it and stores it in the controller, the way AddStateMachineBehaviour would have. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GKDgZCam5r4RjxbgByEzZM
ParameterStore.Analyze and ClipRepather.Analyze build analyzer issues while ControllerAnalyzer.Analyze calls them, so the nested Issue/Severity/Kind types tied those model classes back to the analyzer. Move them to top-level types in Editor/Model/AnalyzerIssue.cs: AnalyzerIssue, IssueSeverity and IssueKind (the bare names Severity and Kind are too generic at namespace scope, and ParameterStore already has a Kind member). Bodies, fields and docs are unchanged; only the type names at the call sites move. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
The iterative Tarjan implementation is a pure graph algorithm with no analyzer knowledge, so it moves verbatim to Editor/Utils/StronglyConnectedComponents.cs as StronglyConnectedComponents.Compute. FindTerminalStateGroups stays in the analyzer — deciding which components are "trapped" is analyzer-domain logic — and now calls the utility. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
Direct tests of StronglyConnectedComponents.Compute pinning the behaviour the analyzer relies on: singleton nodes are reported as components of their own, component ids come out in reverse topological order (sinks first), cycles and self-loops collapse, and an empty graph yields no components. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
…ions GraphSync's private IsStateInStateMachine / IsStateMachineInStateMachine are plain direct-child lookups with no graph knowledge. They move to AnimatorControllerExtensions as ContainsState / ContainsStateMachine, matching the ContainsTree(this container, target) shape the file already uses; the bodies are unchanged, only the container becomes the receiver. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
ControllerCleanup, ParameterUsageFinder and StateSearch each carried a byte-identical private BuildPathLabel (only the parameter name and the leading comment differed). One copy moves to AnimatorControllerExtensions as PathLabel(this path, layerName); the three privates are deleted and rewired. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
AsyncSyncBuilder and NetworkSyncBuilder held byte-identical private EnsureParameter helpers that already delegated to DbtBuilder.FindParameter. The body moves to DbtBuilder.EnsureParameter unchanged and both call it. DbtBuilder.EnsureFloatParameter is NOT the same helper — it is Float-only and assigns an explicit default — so it stays as it is. ToggleBuilder's copy also stays: it reports back through an out parameter whether it created the parameter, which its callers use to decide about the default value. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
MakeUniqueLayerName was byte-identical to DbtBuilder.UniqueLayerName down to the local Taken function; drop the panel copy and call the shared one. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
ControllerBuilder.cs held all 15 top-level authoring types in 1193 lines. Each type moves verbatim into its own file under Editor/Authoring/, keeping the same namespace so no call site changes; the `// ----` section separators travel with the group they head. No type was nested, so nothing was unnested and no access path changed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
RecipeExporter's nested Driver class (752 lines of the file's 1058) becomes top-level RecipeDriver in its own file. The class body moves verbatim apart from de-indenting one level; its internal `// ----` sections and the `// ---- driving the builder ----` separator travel with it. The only RecipeExporter member the driver reached for was the Header text helper, called three times. Access widening required: - RecipeExporter.Header(string): private static -> internal static Those three call sites now read RecipeExporter.Header(...). Nothing else in the exporter was touched: Export, RegisterAssets, the Result/FieldRef DTOs and all text composition stay put, and the emitted C# is byte-identical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
The fold machinery (FoldPlan, PlanFolds, BehaviourSignature, EmitFolds, Fold,
LoopVar, HasWiring, FoldThreshold) answers a question about the shape of the
generated text, not about the controller, so it moves verbatim into a
RecipeFoldPlanner collaborator the driver constructs and holds. Its only
couplings out were the builder — passed to the planner's constructor, so
`_c.Script` reads unchanged — and one call back into EmitBehaviour.
Access changes, all private -> internal, none of them widening past the
assembly:
- RecipeFoldPlanner.FoldPlan (nested type), .PlanFolds, .EmitFolds,
.HasWiring: the planner's surface toward the driver
- RecipeDriver.EmitBehaviour: the one call the planner makes back
BehaviourSignature, Fold, LoopVar and FoldThreshold stay private. Method
bodies changed only in their access paths: EmitBehaviour gains `_driver.`,
and the driver reads RecipeFoldPlanner.PlanFolds / .HasWiring /
.FoldPlan and `_folds.EmitFolds`. The emitted C# is byte-identical.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
BuildSlots, BuildSchedule, EffectiveWeights, Gcd, RepairAdjacency, ResolveScheduleOverride and EffectiveSchedule move out of the god class verbatim. AsyncSyncBuilder keeps same-name delegators, so the tests, the wizard and the recipe builder see exactly the same surface. Slot and Request stay on the facade — they are part of that surface — and are aliased into the new file so every moved signature reads as it did before. Gcd and RepairAdjacency widen from private to internal: Warnings (still on the facade) needs Gcd, and the pure math is now reachable for tests of its own. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
The graph selection was sorted into per-kind buckets in three places with the same type switch. Build it once, in one read-only class, so the command classes can later be handed plain model lists instead of graph elements. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
ResolveEncoding, ResolveEmptyClip, FloatChannelsUsed, CycleSeconds, RefreshIntervals, FreeSlots, CompressedBits, DirectBits and ChannelTypes move out verbatim; AsyncSyncBuilder keeps same-name delegators so the wizard's preview and the tests call exactly what they called before. ChannelTypes widens from private to internal — Warnings and GeneratedParameters stay on the facade and still need it. The IndexEncoding.Auto doc now points at AsyncSyncCost.ResolveEncoding so the cref keeps resolving. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
IndexParameter, BitParameter, ChannelParameter, FloatChannelParameter, RequestParameter and both DefaultBaseName overloads move out verbatim; AsyncSyncBuilder keeps same-name delegators, including the internal DefaultBaseName(guid, taken) seam the tests call. RequestParameter's doc now says AsyncSyncBuilder.IsReservedName, which is where that check stayed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
State / sub-state-machine creation, motion assignment and pack / unpack only touch the controller asset, so they move to a view-free class that can be driven headlessly. GraphSync keeps the public entry points and the rebuild and selection sequencing that follows each command. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
The five Multi* helpers in InspectorPanel were one skeleton written out five times: read the first item, mark the control mixed when the others disagree, then write the new value to every item inside a single undo group. MultiEditGui holds that skeleton once; Bool / Float / Text / ObjectField / Interruption are typed wrappers over it with the rendering and undo names they always had. Interruption keeps its asymmetric naming (group "Edit Transitions", per-item "Edit Transition"), and MultiBool's refreshEdges flag becomes an afterApply callback so the transition editor still repaints its edges after a bulk edit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
The 223-line build and its Apply-only helpers (SlotStateName, AddChannelCopies, ClearStateMachine) move out of the facade verbatim — same statements, same Undo calls in the same order, one UndoScope. AsyncSyncBuilder.Apply stays public and delegates, so nothing that runs a request changes. The new file imports the facade statically: the moved code goes on saying Request, Slot, BuildSlots, IndexParameter and so on, exactly as it did inside it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
The inspector kept the separator line, the row selection tint and the parameter lookups behind the condition popups as private helpers, while the layers panel spelled the same tint out inline. PanelGui holds them once; the bodies move verbatim, tables and all. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
Frames and memo notes live in the controller's GraphFrameData holder, so the whole edit half is asset work. It moves to a view-free class that also takes over the holder reference: it used to be a GraphSync field assigned from six places, and a single owner keeps every reader on the same instance. GraphSync keeps the wrappers that measure node layout, refresh visuals and rebuild. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
Three panels reacted to a wizard that had rewritten the controller with the same four calls: notify parameters, layers and the graph, then open the layer it landed in. The extension holds that body once. The parameters panel had no layer to open, and keeps that difference by leaving the index off. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
Every frame / note field edit was the same four lines around a one-line change. One private Mutate helper now holds them; the entry points keep their names and pass their own undo label, so the undo history is untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
Apply keeps its statements in the same order; the contiguous slices between them become private methods with the locals they used passed in — parameter creation, layer resolution, empty clip, index bit names, the send ring, the decoder, the parameter store and the saved config. No statement crosses an Undo call, the UndoScope still wraps the whole run, and every comment stays with its code. What is left in Apply is the outline: fifteen lines that name the steps. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
The leftover / exposed sub-asset scan owns the two caches it fills, so both move with it. The panel still drops them when the controller changes, now through Clear(). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
Every RefreshStateNode / RefreshAllStateNodes / RefreshFrameVisuals / RefreshNoteVisuals / FindNoteNode().BeginEdit() call an inspector section made on GraphSync is now a NotifyGraphVisualsChanged / NotifyNoteEditRequested on the context; AnimatorGraphView makes the same GraphSync call from its subscriber. The events are synchronous with one subscriber, so each site keeps its exact former effect, at the same point in the same frame: - state badge repaints (behaviour add / remove / paste, sync requests, Write Defaults, multi-state motion + WD) -> NotifyGraphVisualsChanged(state) - frame box, note box -> ...(frame) / ...(note) - bulk Write Defaults (RefreshAllStateNodes) -> GraphVisuals.AllStateNodes - mute / solo / condition edits (the Edges loop) -> GraphVisuals.AllEdges - "Edit Text in Graph" -> NotifyNoteEditRequested Two bulk sentinels rather than one: the overview repainted only state nodes and the multi-transition editor only edges, and neither site is widened. Sections whose only reason to hold a GraphSync was one of these refreshes take the context instead (behaviours, multi-state behaviours, VRC drawers, sync requests, overview); the ones that also issue commands keep both. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
TransitionInspector took an AnimatorGraphView for two things: the selected edges and the Sync behind it. It now takes the GraphSync directly and asks the context for the selection. GetSelectedTransitionGroups returns one (isDefault, transitions) entry per selected edge, so the pool is assembled from model data and no TransitionEdge crosses into the panel. The "no edge selected in the graph, fall back to what Context.Selection names" step moved verbatim into the provider on the view side, where the edge lookup belongs — same edges, same order, same pool. DeleteTransitionRow and AddTransitionToAnchorEdge stay on GraphSync: both need the edge object itself (its endpoint nodes), and GraphSync is the sanctioned command facade. They now call it through the injected _sync rather than reaching through the view. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
The last direct panel-to-view reference. The panel now reads the multi-state selection through DaerDContext.GetSelectedStates() — the provider the graph view registered — and passes the injected GraphSync down to the sections that issue commands. DaerDWindow hands it _graphView.Sync. grep -rn 'AnimatorGraphView' Editor/Panels/ is now empty: panels and the graph talk only through the context and the command facade, as DaerDContext.cs:8-38 says they should. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
Includes followup/inspector-i18n: the remaining inspector literals localized, and the inspector decoupled from the graph view via context events/providers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
Sine / Cosine / Tangent were a layer and nothing else: a curve read by a state's motion time, which no blend tree can drive. A 1D tree reaches the same place without leaving the tree — blending between adjacent thresholds IS interpolating a table, so children holding f(i/N) at threshold i/N are the function, sampled. Same trick Lut1D already uses, with the curve fixed. TrigValue is reused verbatim, poles and all, so tan still pins its two samples to +100 rather than to whichever side of pi/2 the float rounded to. Clips are shared by value the way Lut1D shares them: 65 children come out as 56 clips for sin, 62 for cos and tan. Accuracy is the sample count's business now rather than the curve's: 64 samples per period leave sin and cos within 1.2e-3 of the true value, well inside the 1/127 a synced float can carry. UsesDbtLayer is constant true from here — every kind is a blend tree child — but stays a predicate: it is the question the wizard and the recipe builder actually ask, and it is public API. SupportingLayerNames keeps returning the old "Out sin(x)" names so a regenerate reclaims the layer a controller built by an older version is still carrying, instead of stranding it beside the new tree with both writing the output. Removing a layer that isn't there is a no-op, so listing costs nothing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
The exact half of 1/x — a normalized Direct core reading (input - 1) as a weight — only ever covered inputs from 1 up, because the shift would have to weigh negative below that and Direct weights clamp at 0. A motion-time layer at the end of the controller covered the rest. It doesn't have to: what the core is short of below 1 is exactly (1/u - 1), and that is a function of the input, so a 1D tree can hold it and a third Direct child can add it back. Direct children animating the same AAP stack sum, so core + table = 1/u. The table's top threshold is input 1 holding 0, which is also what a 1D tree clamps to above it — so from 1 up the table contributes nothing at all and the exact half stands alone, unchanged. Its thresholds are a geometric ladder from 1 down to 1/240 in 96 rungs, not an even one. Linear interpolation of 1/u across a step of ratio r overshoots by (sqrt(r) + 1/sqrt(r) - 2) relative regardless of where the step sits, so one ratio buys the same accuracy everywhere: 8.2e-4 measured across the whole band, against a uniform ladder that would be worthless approaching 0. Below the floor the tree clamps, which keeps the old 240 output cap. Above 1 the result stays bit-exact. Trade-off: 97 one-key clips as sub-assets where the layer was one curve clip plus an empty one — for a layer, two states, two transitions and a whole motion-time state machine removed. Divide is unchanged structurally; it just no longer inherits a layer, only the extra frame of lag. One transient stays, as it was: the core reads last frame's shift while the table reads this frame's input, so a frame that crosses 1 has the two halves describing different inputs. The layer handoff had the same artifact. SupportingLayerNames still reports "Out 1/x" and "Out/Inv 1/x" so that a regenerate reclaims the layer a controller built by an older version is still carrying, rather than leaving it to overwrite the new tree's output from the end of the layer list. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
The trig kinds and the reciprocal describe their shape out loud, and the shape changed: both are lookup tables inside the blend tree layer now, and the reciprocal is exact only from 1 up. The "this operation is a layer of its own" help box loses its condition too — no kind answers false to UsesDbtLayer any more, so only the FrameTime wording is ever shown, and it is unchanged. The old msgids stay in ja.po: nothing else uses them today, but a stale translation costs nothing and dropping one is unrecoverable from the .po alone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
…ocal Comment-only: Reciprocal/Sine no longer mention a supporting layer or motion time, and Divide inherits an extra frame of lag rather than a layer. The generic mentions of supporting layers stay — FrameTime still brings one. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
Trig and reciprocal gadgets now live entirely inside the Direct BlendTree as 1D lookup trees; FrameTime is the only gadget that still adds a layer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Fi8sHWGkXzjBSZoKAA1kM
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP2yGLXcBYDqW7nDAfaUpj
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP2yGLXcBYDqW7nDAfaUpj
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP2yGLXcBYDqW7nDAfaUpj
…nityf Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP2yGLXcBYDqW7nDAfaUpj
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP2yGLXcBYDqW7nDAfaUpj
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP2yGLXcBYDqW7nDAfaUpj
…me screen Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP2yGLXcBYDqW7nDAfaUpj
…f by one Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP2yGLXcBYDqW7nDAfaUpj
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP2yGLXcBYDqW7nDAfaUpj
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP2yGLXcBYDqW7nDAfaUpj
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP2yGLXcBYDqW7nDAfaUpj
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP2yGLXcBYDqW7nDAfaUpj
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP2yGLXcBYDqW7nDAfaUpj
…indow Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP2yGLXcBYDqW7nDAfaUpj
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP2yGLXcBYDqW7nDAfaUpj
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
daerd | 8c9f20f | Aug 09 2026, 06:11 AM |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP2yGLXcBYDqW7nDAfaUpj # Conflicts: # Editor/Model/AsyncSyncBuilder.cs # Editor/Window/AsyncSyncWindow.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.