@energy8platform/golem 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/dist/editor.css +50 -0
- package/dist/editor.js +1315 -969
- package/dist/lib/cli.js +278 -19
- package/dist/lib/cli.js.map +4 -4
- package/dist/lib/editor/controls-panel.d.ts +2 -0
- package/dist/lib/editor/store.d.ts +140 -1
- package/dist/lib/editor-entry.js +278 -19
- package/dist/lib/editor-entry.js.map +4 -4
- package/dist/lib/harness.js +167 -5
- package/dist/lib/interactive-bones.d.ts +37 -0
- package/dist/lib/interactive-editor.d.ts +80 -0
- package/dist/lib/interactive-editor.js +744 -0
- package/dist/lib/interactive-editor.js.map +7 -0
- package/dist/lib/rig-anim.d.ts +5 -2
- package/dist/lib/rig-control-qa.d.ts +319 -0
- package/dist/lib/rig-controls.d.ts +13 -0
- package/dist/lib/rig-format.d.ts +912 -0
- package/dist/lib/rig-gates.d.ts +6 -6
- package/dist/lib/rig-runtime.d.ts +39 -2
- package/dist/lib/runtime.d.ts +3 -1
- package/dist/lib/runtime.js +170 -5
- package/dist/lib/runtime.js.map +4 -4
- package/dist/lib/tools.js +274 -15
- package/dist/lib/tools.js.map +4 -4
- package/package.json +7 -1
package/dist/lib/harness.js
CHANGED
|
@@ -49592,6 +49592,7 @@ ${parts.join("\n")}
|
|
|
49592
49592
|
});
|
|
49593
49593
|
var Interpolation = { interpolation: external_exports.enum(["segment", "pchip"]).optional() };
|
|
49594
49594
|
var Track = external_exports.discriminatedUnion("target", [
|
|
49595
|
+
external_exports.object({ target: external_exports.literal("control"), id: Id, prop: external_exports.literal("value"), ...Interpolation, keys: external_exports.array(Key).min(1) }),
|
|
49595
49596
|
external_exports.object({ target: external_exports.literal("bone"), id: Id, prop: BoneProp, ...Interpolation, keys: external_exports.array(Key).min(1) }),
|
|
49596
49597
|
external_exports.object({ target: external_exports.literal("slot"), id: Id, prop: SlotProp, ...Interpolation, keys: external_exports.array(Key).min(1) }),
|
|
49597
49598
|
external_exports.object({ target: external_exports.literal("deform"), id: Id, prop: DeformProp, ...Interpolation, keys: external_exports.array(Key).min(1) }),
|
|
@@ -49614,6 +49615,25 @@ ${parts.join("\n")}
|
|
|
49614
49615
|
/** if generated from a preset — keep params so the agent can re-tune instead of re-keying */
|
|
49615
49616
|
preset: external_exports.object({ name: external_exports.string(), params: external_exports.record(external_exports.unknown()) }).optional()
|
|
49616
49617
|
});
|
|
49618
|
+
var RigControl = external_exports.object({
|
|
49619
|
+
id: Id,
|
|
49620
|
+
label: external_exports.string().optional(),
|
|
49621
|
+
min: external_exports.number().finite(),
|
|
49622
|
+
max: external_exports.number().finite(),
|
|
49623
|
+
default: external_exports.number().finite()
|
|
49624
|
+
}).strict();
|
|
49625
|
+
var ControlValues = external_exports.array(external_exports.union([external_exports.number().finite(), external_exports.array(external_exports.number().finite()).min(1)])).min(2);
|
|
49626
|
+
var ControlChannel = external_exports.discriminatedUnion("target", [
|
|
49627
|
+
external_exports.object({ target: external_exports.literal("bone"), id: Id, prop: BoneProp, values: ControlValues }).strict(),
|
|
49628
|
+
external_exports.object({ target: external_exports.literal("slot"), id: Id, prop: external_exports.literal("alpha"), values: ControlValues }).strict(),
|
|
49629
|
+
external_exports.object({ target: external_exports.literal("deform"), id: Id, prop: external_exports.literal("vertices"), values: ControlValues }).strict()
|
|
49630
|
+
]);
|
|
49631
|
+
var ControlBinding = external_exports.object({
|
|
49632
|
+
id: Id,
|
|
49633
|
+
axes: external_exports.array(external_exports.object({ control: Id, keys: external_exports.array(external_exports.number().finite()).min(2).max(32) }).strict()).min(1).max(2),
|
|
49634
|
+
/** Flattened grid, first axis varies fastest. Linear / bilinear interpolation. */
|
|
49635
|
+
channels: external_exports.array(ControlChannel).min(1)
|
|
49636
|
+
}).strict();
|
|
49617
49637
|
var Reference = external_exports.object({
|
|
49618
49638
|
src: external_exports.string(),
|
|
49619
49639
|
opacity: external_exports.number().min(0).max(1).default(0.5),
|
|
@@ -49639,7 +49659,9 @@ ${parts.join("\n")}
|
|
|
49639
49659
|
ik: external_exports.array(IkConstraint).default([]),
|
|
49640
49660
|
transform: external_exports.array(TransformConstraint).default([]),
|
|
49641
49661
|
path: external_exports.array(PathConstraint).default([]),
|
|
49642
|
-
animations: external_exports.array(Animation)
|
|
49662
|
+
animations: external_exports.array(Animation),
|
|
49663
|
+
controls: external_exports.array(RigControl).optional(),
|
|
49664
|
+
controlBindings: external_exports.array(ControlBinding).optional()
|
|
49643
49665
|
});
|
|
49644
49666
|
var RigDocument = external_exports.preprocess((input) => {
|
|
49645
49667
|
const v2 = input?.version;
|
|
@@ -49714,6 +49736,8 @@ ${parts.join("\n")}
|
|
|
49714
49736
|
dup(doc.ik, "ik");
|
|
49715
49737
|
dup(doc.transform, "transform");
|
|
49716
49738
|
dup(doc.path, "path");
|
|
49739
|
+
dup(doc.controls ?? [], "control");
|
|
49740
|
+
dup(doc.controlBindings ?? [], "control binding");
|
|
49717
49741
|
const bones = new Set(doc.bones.map((b2) => b2.id));
|
|
49718
49742
|
const assets = new Map(doc.assets.map((a2) => [a2.id, a2]));
|
|
49719
49743
|
const slots = new Set(doc.slots.map((s2) => s2.id));
|
|
@@ -49788,13 +49812,47 @@ ${parts.join("\n")}
|
|
|
49788
49812
|
for (const b2 of c2.bones) if (!bones.has(b2)) errs.push(`path "${c2.id}": unknown bone "${b2}"`);
|
|
49789
49813
|
if (!slots.has(c2.target)) errs.push(`path "${c2.id}": unknown target slot "${c2.target}"`);
|
|
49790
49814
|
}
|
|
49815
|
+
const controls = new Map((doc.controls ?? []).map((c2) => [c2.id, c2]));
|
|
49816
|
+
const driven = /* @__PURE__ */ new Set();
|
|
49817
|
+
for (const c2 of controls.values()) if (!(c2.min < c2.max && c2.default >= c2.min && c2.default <= c2.max))
|
|
49818
|
+
errs.push(`control "${c2.id}": require min < max and default within range`);
|
|
49819
|
+
for (const b2 of doc.controlBindings ?? []) {
|
|
49820
|
+
const prefix = `control binding "${b2.id}"`;
|
|
49821
|
+
if (new Set(b2.axes.map((a2) => a2.control)).size !== b2.axes.length) errs.push(`${prefix}: axes must use different controls`);
|
|
49822
|
+
for (const axis of b2.axes) {
|
|
49823
|
+
const c2 = controls.get(axis.control);
|
|
49824
|
+
if (!c2) errs.push(`${prefix}: unknown control "${axis.control}"`);
|
|
49825
|
+
else if (axis.keys[0] !== c2.min || axis.keys.at(-1) !== c2.max) errs.push(`${prefix}: keys must span the control's full range`);
|
|
49826
|
+
if (axis.keys.some((v2, i2) => i2 > 0 && v2 <= axis.keys[i2 - 1])) errs.push(`${prefix}: keys must be strictly increasing`);
|
|
49827
|
+
}
|
|
49828
|
+
const count2 = b2.axes.reduce((n2, a2) => n2 * a2.keys.length, 1);
|
|
49829
|
+
for (const ch of b2.channels) {
|
|
49830
|
+
const key = `${ch.target}.${ch.id}.${ch.prop}`;
|
|
49831
|
+
if (driven.has(key)) errs.push(`${prefix}: channel ${key} already owned by a binding`);
|
|
49832
|
+
driven.add(key);
|
|
49833
|
+
if (ch.values.length !== count2) errs.push(`${prefix}: ${key} needs ${count2} grid values`);
|
|
49834
|
+
if (ch.target === "deform") {
|
|
49835
|
+
const att = attachments.get(ch.id);
|
|
49836
|
+
if (!att || att.type === "region") errs.push(`${prefix}: vertices need a mesh or path attachment`);
|
|
49837
|
+
else {
|
|
49838
|
+
const n2 = att.weights ? att.weights.reduce((s2, a2) => s2 + a2.length * 2, 0) : att.vertices.length;
|
|
49839
|
+
if (ch.values.some((v2) => !Array.isArray(v2) || v2.length !== n2)) errs.push(`${prefix}: ${key} needs arrays of ${n2} offsets`);
|
|
49840
|
+
}
|
|
49841
|
+
} else {
|
|
49842
|
+
if (!(ch.target === "bone" ? bones : slots).has(ch.id)) errs.push(`${prefix}: unknown ${ch.target} "${ch.id}"`);
|
|
49843
|
+
if (ch.values.some((v2) => typeof v2 !== "number")) errs.push(`${prefix}: ${key} needs numeric values`);
|
|
49844
|
+
if (ch.target === "slot" && ch.values.some((v2) => typeof v2 === "number" && (v2 < 0 || v2 > 1))) errs.push(`${prefix}: alpha must be in 0..1`);
|
|
49845
|
+
}
|
|
49846
|
+
}
|
|
49847
|
+
}
|
|
49791
49848
|
for (const a2 of doc.animations) {
|
|
49792
49849
|
const tracks = /* @__PURE__ */ new Set();
|
|
49793
49850
|
for (const t2 of a2.tracks) {
|
|
49794
49851
|
const tid = trackId(t2);
|
|
49795
49852
|
if (tracks.has(tid)) errs.push(`animation "${a2.id}": duplicate track ${tid}`);
|
|
49796
49853
|
tracks.add(tid);
|
|
49797
|
-
|
|
49854
|
+
if (driven.has(tid)) errs.push(`animation "${a2.id}": ${tid} is controlled by a binding; animate its control instead`);
|
|
49855
|
+
const known = t2.target === "bone" ? bones.has(t2.id) : t2.target === "slot" ? slots.has(t2.id) : t2.target === "deform" ? attachments.has(t2.id) : t2.target === "ik" ? iks.has(t2.id) : t2.target === "transform" ? transforms.has(t2.id) : t2.target === "path" ? paths.has(t2.id) : t2.target === "control" ? controls.has(t2.id) : true;
|
|
49798
49856
|
if (!known) errs.push(`animation "${a2.id}": unknown ${t2.target} "${t2.id}"`);
|
|
49799
49857
|
const bound = t2.target === "deform" ? DEFORM_BOUNDS[t2.prop] : void 0;
|
|
49800
49858
|
const att = t2.target === "deform" ? attachments.get(t2.id) : void 0;
|
|
@@ -49802,6 +49860,10 @@ ${parts.join("\n")}
|
|
|
49802
49860
|
for (const k2 of t2.keys) {
|
|
49803
49861
|
if (k2.t <= prev && prev >= 0) errs.push(`animation "${a2.id}" ${tid}: keys not strictly increasing`);
|
|
49804
49862
|
if (k2.t > a2.duration) errs.push(`animation "${a2.id}" ${tid}: key at ${k2.t}s exceeds duration`);
|
|
49863
|
+
if (t2.target === "control") {
|
|
49864
|
+
const c2 = controls.get(t2.id);
|
|
49865
|
+
if (typeof k2.v !== "number" || !Number.isFinite(k2.v) || c2 && (k2.v < c2.min || k2.v > c2.max)) errs.push(`animation "${a2.id}" ${tid}: control key outside its finite range`);
|
|
49866
|
+
}
|
|
49805
49867
|
if (t2.target === "slot" && t2.prop === "attachment") {
|
|
49806
49868
|
if (k2.v !== null && typeof k2.v !== "string") errs.push(`animation "${a2.id}" ${tid}: attachment key must be an attachment id or null`);
|
|
49807
49869
|
else if (typeof k2.v === "string") {
|
|
@@ -49840,6 +49902,52 @@ ${parts.join("\n")}
|
|
|
49840
49902
|
return doc;
|
|
49841
49903
|
}
|
|
49842
49904
|
|
|
49905
|
+
// src/rig-controls.ts
|
|
49906
|
+
function controlPose(doc, values) {
|
|
49907
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
49908
|
+
for (const [id, value] of Object.entries(values)) {
|
|
49909
|
+
const c2 = doc.controls?.find((c3) => c3.id === id);
|
|
49910
|
+
if (!c2) throw new Error(`unknown control "${id}"`);
|
|
49911
|
+
if (!Number.isFinite(value)) throw new Error(`control "${id}" must be finite`);
|
|
49912
|
+
out2.set(`control:${id}:value`, Math.max(c2.min, Math.min(c2.max, value)));
|
|
49913
|
+
}
|
|
49914
|
+
return out2;
|
|
49915
|
+
}
|
|
49916
|
+
function bracket(keys, value) {
|
|
49917
|
+
let i2 = 0;
|
|
49918
|
+
while (i2 < keys.length - 2 && value > keys[i2 + 1]) i2++;
|
|
49919
|
+
return [i2, Math.max(0, Math.min(1, (value - keys[i2]) / (keys[i2 + 1] - keys[i2])))];
|
|
49920
|
+
}
|
|
49921
|
+
function sampleBinding(binding, values) {
|
|
49922
|
+
const [x2, u2] = bracket(binding.axes[0].keys, values[0]);
|
|
49923
|
+
const [y2, v2] = binding.axes[1] ? bracket(binding.axes[1].keys, values[1]) : [0, 0];
|
|
49924
|
+
const width = binding.axes[0].keys.length, a2 = y2 * width + x2;
|
|
49925
|
+
const mix = (a3, b2, u3) => a3 + (b2 - a3) * u3;
|
|
49926
|
+
return binding.channels.map((ch) => {
|
|
49927
|
+
const read = (i2, j2) => j2 === void 0 ? ch.values[i2] : ch.values[i2][j2];
|
|
49928
|
+
const at = (j2) => {
|
|
49929
|
+
const top = mix(read(a2, j2), read(a2 + 1, j2), u2);
|
|
49930
|
+
return binding.axes[1] ? mix(top, mix(read(a2 + width, j2), read(a2 + width + 1, j2), u2), v2) : top;
|
|
49931
|
+
};
|
|
49932
|
+
return Array.isArray(ch.values[0]) ? ch.values[0].map((_, j2) => at(j2)) : at();
|
|
49933
|
+
});
|
|
49934
|
+
}
|
|
49935
|
+
function resolveControls(doc, input) {
|
|
49936
|
+
if (!doc.controlBindings?.length) return input;
|
|
49937
|
+
const out2 = new Map(input), controls = new Map(doc.controls?.map((c2) => [c2.id, c2]));
|
|
49938
|
+
for (const b2 of doc.controlBindings) {
|
|
49939
|
+
const values = b2.axes.map((axis) => {
|
|
49940
|
+
const c2 = controls.get(axis.control);
|
|
49941
|
+
const value = input.get(`control:${c2.id}:value`) ?? c2.default;
|
|
49942
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`control "${c2.id}" must be finite`);
|
|
49943
|
+
return Math.max(c2.min, Math.min(c2.max, value));
|
|
49944
|
+
});
|
|
49945
|
+
const sampled = sampleBinding(b2, values);
|
|
49946
|
+
b2.channels.forEach((ch, i2) => out2.set(`${ch.target}:${ch.id}:${ch.prop}`, sampled[i2]));
|
|
49947
|
+
}
|
|
49948
|
+
return out2;
|
|
49949
|
+
}
|
|
49950
|
+
|
|
49843
49951
|
// src/rig-anim.ts
|
|
49844
49952
|
var DEG2 = Math.PI / 180;
|
|
49845
49953
|
var named = {
|
|
@@ -49943,6 +50051,7 @@ ${parts.join("\n")}
|
|
|
49943
50051
|
}
|
|
49944
50052
|
function restValue(doc, key) {
|
|
49945
50053
|
const [target, id, prop] = key.split(":");
|
|
50054
|
+
if (target === "control") return doc.controls?.find((c2) => c2.id === id)?.default ?? 0;
|
|
49946
50055
|
if (target === "bone") return doc.bones.find((b2) => b2.id === id).transform[prop];
|
|
49947
50056
|
if (target === "slot") {
|
|
49948
50057
|
const s2 = doc.slots.find((s3) => s3.id === id);
|
|
@@ -50045,7 +50154,9 @@ ${parts.join("\n")}
|
|
|
50045
50154
|
for (const k2 of kids.get(id) ?? []) stack.push(k2.id);
|
|
50046
50155
|
}
|
|
50047
50156
|
}
|
|
50048
|
-
function evaluate(doc, pose) {
|
|
50157
|
+
function evaluate(doc, pose, modifiers) {
|
|
50158
|
+
pose = resolveControls(doc, pose);
|
|
50159
|
+
if (modifiers) for (const modify of modifiers) pose = modify(new Map(pose));
|
|
50049
50160
|
const skel = { doc, pose, bones: /* @__PURE__ */ new Map(), attachment: /* @__PURE__ */ new Map() };
|
|
50050
50161
|
for (const b2 of boneOrder(doc)) {
|
|
50051
50162
|
const s2 = localOf(doc, pose, b2);
|
|
@@ -50850,9 +50961,23 @@ ${parts.join("\n")}
|
|
|
50850
50961
|
this.state = new AnimationState(this, { ...opts, mix: opts.mix ?? 0.18 });
|
|
50851
50962
|
this.setPose(/* @__PURE__ */ new Map());
|
|
50852
50963
|
}
|
|
50964
|
+
inputPose = /* @__PURE__ */ new Map();
|
|
50965
|
+
controlOverrides = /* @__PURE__ */ new Map();
|
|
50966
|
+
/** Manual inputs override animated controls until resetControls; values are clamped to declared ranges. */
|
|
50967
|
+
setControls(values) {
|
|
50968
|
+
this.controlOverrides = controlPose(this.doc, values);
|
|
50969
|
+
this.setPose(this.inputPose);
|
|
50970
|
+
}
|
|
50971
|
+
resetControls() {
|
|
50972
|
+
this.controlOverrides.clear();
|
|
50973
|
+
this.setPose(this.inputPose);
|
|
50974
|
+
}
|
|
50853
50975
|
view = new Container();
|
|
50854
50976
|
state;
|
|
50855
50977
|
items = [];
|
|
50978
|
+
beforePose = /* @__PURE__ */ new Set();
|
|
50979
|
+
afterPose = /* @__PURE__ */ new Set();
|
|
50980
|
+
clipPoints = /* @__PURE__ */ new Map();
|
|
50856
50981
|
skel;
|
|
50857
50982
|
textures;
|
|
50858
50983
|
pages = /* @__PURE__ */ new Map();
|
|
@@ -50861,6 +50986,35 @@ ${parts.join("\n")}
|
|
|
50861
50986
|
/** slots the editor has selected; setPose draws them at GHOST_ALPHA at least */
|
|
50862
50987
|
ghosts = /* @__PURE__ */ new Set();
|
|
50863
50988
|
clipGraphics = /* @__PURE__ */ new Map();
|
|
50989
|
+
/** Read-only render handles for in-page tools. Nodes remain owned by the player. */
|
|
50990
|
+
get renderItems() {
|
|
50991
|
+
return this.items;
|
|
50992
|
+
}
|
|
50993
|
+
/** Current document-space clipping polygons, keyed by path attachment id. */
|
|
50994
|
+
get clipPolygons() {
|
|
50995
|
+
return this.clipPoints;
|
|
50996
|
+
}
|
|
50997
|
+
/**
|
|
50998
|
+
* Modify resolved control outputs before skeleton evaluation, constraints and skinning.
|
|
50999
|
+
* Callbacks run in registration order, each with its own map. Change control inputs via setControls.
|
|
51000
|
+
*/
|
|
51001
|
+
onBeforePose(callback) {
|
|
51002
|
+
this.beforePose.add(callback);
|
|
51003
|
+
return () => {
|
|
51004
|
+
this.beforePose.delete(callback);
|
|
51005
|
+
};
|
|
51006
|
+
}
|
|
51007
|
+
/** Subscribe after the base pose has been rebuilt. Return value removes the callback. */
|
|
51008
|
+
onAfterPose(callback) {
|
|
51009
|
+
this.afterPose.add(callback);
|
|
51010
|
+
return () => {
|
|
51011
|
+
this.afterPose.delete(callback);
|
|
51012
|
+
};
|
|
51013
|
+
}
|
|
51014
|
+
/** Rebuild the current pose without advancing playback; useful for reversible overlays. */
|
|
51015
|
+
refreshPose() {
|
|
51016
|
+
this.setPose(this.inputPose);
|
|
51017
|
+
}
|
|
50864
51018
|
/** whole atlas page as a plain texture (identity uv mapping) for skinned meshes */
|
|
50865
51019
|
pageTexture(tex) {
|
|
50866
51020
|
let page = this.pages.get(tex.source);
|
|
@@ -50915,7 +51069,11 @@ ${parts.join("\n")}
|
|
|
50915
51069
|
}
|
|
50916
51070
|
/** Apply a pose (rest where silent) to the display objects. */
|
|
50917
51071
|
setPose(pose) {
|
|
50918
|
-
|
|
51072
|
+
this.inputPose = new Map(pose);
|
|
51073
|
+
if (this.controlOverrides.size) pose = new Map([...pose, ...this.controlOverrides]);
|
|
51074
|
+
this.clipPoints.clear();
|
|
51075
|
+
const skel = this.skel = evaluate(this.doc, pose, this.beforePose);
|
|
51076
|
+
pose = skel.pose;
|
|
50919
51077
|
const clips = new Set(this.doc.slots.flatMap((s2) => s2.clip ? [s2.clip] : []));
|
|
50920
51078
|
for (const [id, g2] of this.clipGraphics) if (!clips.has(id)) {
|
|
50921
51079
|
g2.destroy();
|
|
@@ -50933,7 +51091,9 @@ ${parts.join("\n")}
|
|
|
50933
51091
|
const vertices = attachmentWorldVertices(skel, att.slot, att);
|
|
50934
51092
|
const n2 = Math.max(32, vertices.length * 4);
|
|
50935
51093
|
const points = samplePath(vertices, true, att.lengths, true, Array.from({ length: n2 }, (_, i2) => i2 / n2), true);
|
|
50936
|
-
|
|
51094
|
+
const polygon = points.flatMap((p2) => [p2.x, p2.y]);
|
|
51095
|
+
this.clipPoints.set(id, polygon);
|
|
51096
|
+
g2.clear().poly(polygon, true).fill(16777215);
|
|
50937
51097
|
}
|
|
50938
51098
|
const m2 = new Matrix();
|
|
50939
51099
|
const orderV = pose.get(pk("drawOrder", "order", "order"));
|
|
@@ -50954,6 +51114,7 @@ ${parts.join("\n")}
|
|
|
50954
51114
|
it.node.tint = rgbOf(color);
|
|
50955
51115
|
it.node.zIndex = order.size ? order.get(s2.id) ?? 0 : numOf(pose, pk("slot", s2.id, "z"), s2.z);
|
|
50956
51116
|
if (it.skinned) {
|
|
51117
|
+
it.node.setFromMatrix(m2.identity());
|
|
50957
51118
|
const v2 = attachmentWorldVertices(skel, s2.id, it.att);
|
|
50958
51119
|
const buf = it.skinned.mesh.geometry.getBuffer("aPosition");
|
|
50959
51120
|
buf.data.set(v2);
|
|
@@ -50986,6 +51147,7 @@ ${parts.join("\n")}
|
|
|
50986
51147
|
it.node.scale.y *= h2 / (this.textures.get(att.asset)?.height ?? h2);
|
|
50987
51148
|
}
|
|
50988
51149
|
}
|
|
51150
|
+
for (const callback of this.afterPose) callback(this);
|
|
50989
51151
|
}
|
|
50990
51152
|
/** World-space bone positions — used by editor gizmos and renderPreview overlays */
|
|
50991
51153
|
bonePositions() {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { RigPlayer } from "./rig-runtime";
|
|
2
|
+
import type { PartTransform } from "./interactive-editor";
|
|
3
|
+
export interface BoneEditPatch {
|
|
4
|
+
format: "golem.bone-edits";
|
|
5
|
+
version: 1;
|
|
6
|
+
documentId: string;
|
|
7
|
+
transforms: Record<string, PartTransform>;
|
|
8
|
+
}
|
|
9
|
+
export interface InteractiveBoneEditorOptions {
|
|
10
|
+
player: Pick<RigPlayer, "doc" | "onBeforePose" | "refreshPose">;
|
|
11
|
+
documentId: string;
|
|
12
|
+
onChange?: (patch: BoneEditPatch) => void;
|
|
13
|
+
onRender?: () => void;
|
|
14
|
+
historyLimit?: number;
|
|
15
|
+
}
|
|
16
|
+
/** Add bone offsets after expression controls, before constraints and native skinning. */
|
|
17
|
+
export declare function createInteractiveBoneEditor(options: InteractiveBoneEditorOptions): {
|
|
18
|
+
getTransform: (id: string) => {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
rotation: number;
|
|
22
|
+
scaleX: number;
|
|
23
|
+
scaleY: number;
|
|
24
|
+
};
|
|
25
|
+
exportPatch: () => BoneEditPatch;
|
|
26
|
+
validatePatch: (value: unknown) => BoneEditPatch;
|
|
27
|
+
readonly canUndo: boolean;
|
|
28
|
+
readonly canRedo: boolean;
|
|
29
|
+
setTransform(id: string, value: Partial<PartTransform>): void;
|
|
30
|
+
importPatch(value: unknown): void;
|
|
31
|
+
reset(id?: string): void;
|
|
32
|
+
setVisible(value: boolean): void;
|
|
33
|
+
undo(): void;
|
|
34
|
+
redo(): void;
|
|
35
|
+
dispose(): void;
|
|
36
|
+
};
|
|
37
|
+
export type InteractiveBoneEditor = ReturnType<typeof createInteractiveBoneEditor>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { RigPlayer } from "./rig-runtime";
|
|
2
|
+
export interface EditorPoint {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
}
|
|
6
|
+
export interface PartTransform {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
rotation: number;
|
|
10
|
+
scaleX: number;
|
|
11
|
+
scaleY: number;
|
|
12
|
+
}
|
|
13
|
+
export interface InteractiveEditPatch {
|
|
14
|
+
format: "golem.interactive-edits";
|
|
15
|
+
version: 1;
|
|
16
|
+
documentId: string;
|
|
17
|
+
transforms: Record<string, PartTransform>;
|
|
18
|
+
}
|
|
19
|
+
export interface EditorGroup {
|
|
20
|
+
id: string;
|
|
21
|
+
members: readonly string[];
|
|
22
|
+
pivotBone?: string;
|
|
23
|
+
}
|
|
24
|
+
export type InteractivePlayer = Pick<RigPlayer, "doc" | "view" | "world" | "renderItems" | "clipPolygons" | "onAfterPose" | "refreshPose">;
|
|
25
|
+
export interface InteractiveEditorOptions {
|
|
26
|
+
player: InteractivePlayer;
|
|
27
|
+
/** Transparent overlay aligned with the player's rendered viewport. It is cleared by the editor. */
|
|
28
|
+
overlay: HTMLCanvasElement;
|
|
29
|
+
/** Host-defined stable rig fingerprint; mismatched patches are rejected. */
|
|
30
|
+
documentId: string;
|
|
31
|
+
groups?: readonly EditorGroup[];
|
|
32
|
+
/** Override when overlay backing pixels differ from the renderer's logical coordinates. */
|
|
33
|
+
screenToDocument?: (client: EditorPoint) => EditorPoint;
|
|
34
|
+
documentToOverlay?: (point: EditorPoint) => EditorPoint;
|
|
35
|
+
alphaFallback?: "skip" | "geometry";
|
|
36
|
+
onChange?: (patch: InteractiveEditPatch) => void;
|
|
37
|
+
onSelection?: (id: string | null) => void;
|
|
38
|
+
onHover?: (id: string | null) => void;
|
|
39
|
+
onError?: (error: Error) => void;
|
|
40
|
+
onGestureStart?: () => void;
|
|
41
|
+
/** Render the host scene after a manual refresh; never called from the pose hook. */
|
|
42
|
+
onRender?: () => void;
|
|
43
|
+
historyLimit?: number;
|
|
44
|
+
}
|
|
45
|
+
export interface EditorFrame {
|
|
46
|
+
id: string;
|
|
47
|
+
center: EditorPoint;
|
|
48
|
+
corners: EditorPoint[];
|
|
49
|
+
handles: {
|
|
50
|
+
name: string;
|
|
51
|
+
p: EditorPoint;
|
|
52
|
+
}[];
|
|
53
|
+
}
|
|
54
|
+
/** Embedded part editor. Source documents are never mutated; edits replay after every evaluated pose. */
|
|
55
|
+
export declare function createInteractiveEditor(options: InteractiveEditorOptions): {
|
|
56
|
+
select: (id: string | null) => void;
|
|
57
|
+
hitTest: (p: EditorPoint) => string | null;
|
|
58
|
+
getFrame: (id: string) => EditorFrame | null;
|
|
59
|
+
getTransform: (id: string) => {
|
|
60
|
+
x: number;
|
|
61
|
+
y: number;
|
|
62
|
+
rotation: number;
|
|
63
|
+
scaleX: number;
|
|
64
|
+
scaleY: number;
|
|
65
|
+
};
|
|
66
|
+
readonly selection: string | null;
|
|
67
|
+
readonly canUndo: boolean;
|
|
68
|
+
readonly canRedo: boolean;
|
|
69
|
+
exportPatch: () => InteractiveEditPatch;
|
|
70
|
+
setEnabled(value: boolean): void;
|
|
71
|
+
setVisible(value: boolean): void;
|
|
72
|
+
setTransform(id: string, value: Partial<PartTransform>): void;
|
|
73
|
+
reset(id?: string): void;
|
|
74
|
+
undo(): void;
|
|
75
|
+
redo(): void;
|
|
76
|
+
importPatch(value: unknown): void;
|
|
77
|
+
dispose(): void;
|
|
78
|
+
};
|
|
79
|
+
export type InteractiveEditor = ReturnType<typeof createInteractiveEditor>;
|
|
80
|
+
export { createInteractiveBoneEditor, type BoneEditPatch, type InteractiveBoneEditorOptions } from "./interactive-bones";
|