Avatar works seated: only shoulders required, bust mode without hips
Sitting at a desk the hips are rarely visible, which made the avatar never appear. The pelvis is now synthesized below the shoulders when hidden (torso/head/arms as a bust; legs return when actually visible). The output also reports diagnostics to the panel: pose tracker failed, or avatar active with no body detected for 5s. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
08414070a9
commit
a0edb14d1e
2 files changed
+30
-7
No files matched your search
+11
-6
@@ -1027,15 +1027,20 @@ class InteractiveSim {
|
|||||||
// tracked skeleton 1:1 — raise your arm and the robot raises its arm.
|
// tracked skeleton 1:1 — raise your arm and the robot raises its arm.
|
||||||
if (mode === 'avatar') {
|
if (mode === 'avatar') {
|
||||||
const L = this.lmsRaw;
|
const L = this.lmsRaw;
|
||||||
const ok = this.modelSim && L && L[11] && L[11].vis > 0.5 && L[12].vis > 0.5
|
// Only the shoulders are required: seated at a desk the hips are often
|
||||||
&& L[23].vis > 0.4 && L[24].vis > 0.4;
|
// hidden, so the pelvis is synthesized and the robot shows as a bust.
|
||||||
|
const ok = this.modelSim && L && L[11] && L[11].vis > 0.5 && L[12].vis > 0.5;
|
||||||
if (!ok) { this._drawField(7, e, audio, canvas, 0); return; }
|
if (!ok) { this._drawField(7, e, audio, canvas, 0); return; }
|
||||||
const S = 2.6;
|
const S = 2.6;
|
||||||
const W = (i) => [(L[i].x - 0.5)*S, (0.5 - L[i].y)*S, -(L[i].z || 0)*1.2];
|
const W = (i) => [(L[i].x - 0.5)*S, (0.5 - L[i].y)*S, -(L[i].z || 0)*1.2];
|
||||||
const mid = (p, q) => [(p[0]+q[0])/2, (p[1]+q[1])/2, (p[2]+q[2])/2];
|
const mid = (p, q) => [(p[0]+q[0])/2, (p[1]+q[1])/2, (p[2]+q[2])/2];
|
||||||
const sh1 = W(11), sh2 = W(12), hp1 = W(23), hp2 = W(24);
|
const sh1 = W(11), sh2 = W(12);
|
||||||
const neck = mid(sh1, sh2), pelvis = mid(hp1, hp2);
|
const neck = mid(sh1, sh2);
|
||||||
const shw = Math.max(0.15, Math.hypot(sh1[0]-sh2[0], sh1[1]-sh2[1], sh1[2]-sh2[2]));
|
const shw = Math.max(0.15, Math.hypot(sh1[0]-sh2[0], sh1[1]-sh2[1], sh1[2]-sh2[2]));
|
||||||
|
const hipsVis = L[23].vis > 0.4 && L[24].vis > 0.4;
|
||||||
|
const hp1 = hipsVis ? W(23) : [neck[0]-shw*0.28, neck[1]-shw*1.25, neck[2]];
|
||||||
|
const hp2 = hipsVis ? W(24) : [neck[0]+shw*0.28, neck[1]-shw*1.25, neck[2]];
|
||||||
|
const pelvis = mid(hp1, hp2);
|
||||||
const t = shw;
|
const t = shw;
|
||||||
const metal = [0.60, 0.64, 0.72], dark = [0.28, 0.30, 0.36];
|
const metal = [0.60, 0.64, 0.72], dark = [0.28, 0.30, 0.36];
|
||||||
const acc = e.colorB || [0.2, 1, 1];
|
const acc = e.colorB || [0.2, 1, 1];
|
||||||
@@ -1063,9 +1068,9 @@ class InteractiveSim {
|
|||||||
};
|
};
|
||||||
arm(sh1, 13, 15); arm(sh2, 14, 16);
|
arm(sh1, 13, 15); arm(sh2, 14, 16);
|
||||||
ball(sh1, t*0.16, dark); ball(sh2, t*0.16, dark);
|
ball(sh1, t*0.16, dark); ball(sh2, t*0.16, dark);
|
||||||
// legs with feet
|
// legs with feet (only when actually visible: seated = bust only)
|
||||||
const leg = (h, knI, anI) => {
|
const leg = (h, knI, anI) => {
|
||||||
if (L[knI].vis < 0.4 || L[anI].vis < 0.4) return;
|
if (!hipsVis || L[knI].vis < 0.4 || L[anI].vis < 0.4) return;
|
||||||
const kn = W(knI), an = W(anI);
|
const kn = W(knI), an = W(anI);
|
||||||
limb(h, kn, t*0.16, metal);
|
limb(h, kn, t*0.16, metal);
|
||||||
limb(kn, an, t*0.13, metal);
|
limb(kn, an, t*0.13, metal);
|
||||||
|
|||||||
+19
-1
@@ -549,7 +549,7 @@ function avTick(now) {
|
|||||||
djv.report({ type: 'autoVj', on: true, name: e.name, mood, bpm: audio.bpm || 0 });
|
djv.report({ type: 'autoVj', on: true, name: e.name, mood, bpm: audio.bpm || 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
let camErrReported = false;
|
let camErrReported = false, poseErrReported = false, avatarHintAt = 0, avatarHinted = false;
|
||||||
function frame() {
|
function frame() {
|
||||||
const t = (performance.now() - startTime) / 1000;
|
const t = (performance.now() - startTime) / 1000;
|
||||||
audio.update();
|
audio.update();
|
||||||
@@ -564,6 +564,24 @@ function frame() {
|
|||||||
djv.report({ type: 'error', message: 'Camera non disponibile (' + viz.inter.camErr +
|
djv.report({ type: 'error', message: 'Camera non disponibile (' + viz.inter.camErr +
|
||||||
') — l’effetto Interattivo funziona comunque con il mouse sull’output' });
|
') — l’effetto Interattivo funziona comunque con il mouse sull’output' });
|
||||||
}
|
}
|
||||||
|
// Body-tracking diagnostics: tracker failed, or avatar active with no body.
|
||||||
|
const now2 = performance.now();
|
||||||
|
if (viz.inter && viz.inter.poseState === 'error' && !poseErrReported) {
|
||||||
|
poseErrReported = true;
|
||||||
|
djv.report({ type: 'error', message: 'Tracking corpo non disponibile: le famiglie ' +
|
||||||
|
'Interattivo usano solo il movimento (Avatar non può funzionare)' });
|
||||||
|
}
|
||||||
|
if (viz.effect && viz.effect.interactiveMode === 'avatar' && viz.inter) {
|
||||||
|
if (viz.inter.lmsRaw) { avatarHintAt = 0; avatarHinted = true; }
|
||||||
|
else if (!avatarHinted) {
|
||||||
|
if (!avatarHintAt) avatarHintAt = now2;
|
||||||
|
else if (now2 - avatarHintAt > 5000) {
|
||||||
|
avatarHinted = true;
|
||||||
|
djv.report({ type: 'error', message: 'Avatar: nessun corpo rilevato — mettiti davanti ' +
|
||||||
|
'alla camera (almeno il busto ben inquadrato e illuminato)' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { avatarHintAt = 0; }
|
||||||
|
|
||||||
// Report beat rising-edges so the control window can auto-cycle effects.
|
// Report beat rising-edges so the control window can auto-cycle effects.
|
||||||
if (a.beat > 0.6 && prevBeat <= 0.6) djv.report({ type: 'beat' });
|
if (a.beat > 0.6 && prevBeat <= 0.6) djv.report({ type: 'beat' });
|
||||||
|
|||||||
Reference in new issue
Block a user