Spaces:
Running
Running
feat: body follows head continuously; body sweeps in idle scan
Browse files
app.js
CHANGED
|
@@ -28,8 +28,10 @@ const TRACK = {
|
|
| 28 |
maxPitchUpDeg: 45, // how far above level the head may look while tracking (nose up)
|
| 29 |
maxPitchDownDeg: 25, // how far below level
|
| 30 |
maxHeadYawFromBodyDeg: 55, // hard SDK limit is 65
|
| 31 |
-
bodyFollowStartDeg:
|
| 32 |
-
bodyRateDegPerSec:
|
|
|
|
|
|
|
| 33 |
maxBodyYawDeg: 150, // hard SDK limit is 160
|
| 34 |
noFaceResetSec: 3.0, // ease back to center after this long without a face
|
| 35 |
noticeAfterAbsentSec: 4.0, // (legacy) new-arrival nod gap; per-person nod is handled by the tracker now
|
|
@@ -485,11 +487,12 @@ class HowdyApp {
|
|
| 485 |
let yaw = this.headYaw + dYaw;
|
| 486 |
let pitch = clamp(this.headPitch + dPitch, -TRACK.maxPitchUpDeg, TRACK.maxPitchDownDeg);
|
| 487 |
|
| 488 |
-
// Body
|
| 489 |
if (this.bodyFollow) {
|
| 490 |
const offset = yaw - this.bodyYaw;
|
| 491 |
if (Math.abs(offset) > TRACK.bodyFollowStartDeg) {
|
| 492 |
-
const
|
|
|
|
| 493 |
this.bodyYaw = clamp(this.bodyYaw + move, -TRACK.maxBodyYawDeg, TRACK.maxBodyYawDeg);
|
| 494 |
}
|
| 495 |
}
|
|
@@ -517,10 +520,13 @@ class HowdyApp {
|
|
| 517 |
// Slow look-around while nobody is close: head only, body drifts home
|
| 518 |
scan(t, dt) {
|
| 519 |
const k = Math.min(1, dt * 1.5);
|
| 520 |
-
const
|
| 521 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 522 |
this.headPitch = lerp(this.headPitch, TRACK.restPitchDeg, k);
|
| 523 |
-
if (Math.abs(this.bodyYaw) > 0.5) this.bodyYaw = lerp(this.bodyYaw, 0, k * 0.5); else this.bodyYaw = 0;
|
| 524 |
}
|
| 525 |
|
| 526 |
easeHome(dt) {
|
|
|
|
| 28 |
maxPitchUpDeg: 45, // how far above level the head may look while tracking (nose up)
|
| 29 |
maxPitchDownDeg: 25, // how far below level
|
| 30 |
maxHeadYawFromBodyDeg: 55, // hard SDK limit is 65
|
| 31 |
+
bodyFollowStartDeg: 6, // head-body offset beyond which the body turns to settle under the head
|
| 32 |
+
bodyRateDegPerSec: 70, // max body turn speed
|
| 33 |
+
bodyGainPerSec: 2.5, // body speed = offset * gain (capped at bodyRateDegPerSec); higher = body leads sooner
|
| 34 |
+
scanBodyAmpDeg: 35, // idle look-around: how far the body sweeps (head adds scanAmpDeg on top)
|
| 35 |
maxBodyYawDeg: 150, // hard SDK limit is 160
|
| 36 |
noFaceResetSec: 3.0, // ease back to center after this long without a face
|
| 37 |
noticeAfterAbsentSec: 4.0, // (legacy) new-arrival nod gap; per-person nod is handled by the tracker now
|
|
|
|
| 487 |
let yaw = this.headYaw + dYaw;
|
| 488 |
let pitch = clamp(this.headPitch + dPitch, -TRACK.maxPitchUpDeg, TRACK.maxPitchDownDeg);
|
| 489 |
|
| 490 |
+
// Body settles under the head: speed proportional to the offset, so the head leads and the body follows through
|
| 491 |
if (this.bodyFollow) {
|
| 492 |
const offset = yaw - this.bodyYaw;
|
| 493 |
if (Math.abs(offset) > TRACK.bodyFollowStartDeg) {
|
| 494 |
+
const speed = Math.min(TRACK.bodyRateDegPerSec, Math.abs(offset) * TRACK.bodyGainPerSec);
|
| 495 |
+
const move = Math.sign(offset) * Math.min(Math.abs(offset) - TRACK.bodyFollowStartDeg * 0.5, speed * dt);
|
| 496 |
this.bodyYaw = clamp(this.bodyYaw + move, -TRACK.maxBodyYawDeg, TRACK.maxBodyYawDeg);
|
| 497 |
}
|
| 498 |
}
|
|
|
|
| 520 |
// Slow look-around while nobody is close: head only, body drifts home
|
| 521 |
scan(t, dt) {
|
| 522 |
const k = Math.min(1, dt * 1.5);
|
| 523 |
+
const phase = (t / TRACK.scanPeriodSec) * Math.PI * 2;
|
| 524 |
+
// Body sweeps slowly; head leads the body a little (phase-advanced) so it looks like it's peering around
|
| 525 |
+
const bodyTarget = this.bodyFollow ? TRACK.scanBodyAmpDeg * Math.sin(phase) : 0;
|
| 526 |
+
const headTarget = bodyTarget + TRACK.scanAmpDeg * 0.5 * Math.sin(phase + 0.6);
|
| 527 |
+
this.bodyYaw = clamp(lerp(this.bodyYaw, bodyTarget, k * 0.6), -TRACK.maxBodyYawDeg, TRACK.maxBodyYawDeg);
|
| 528 |
+
this.headYaw = lerp(this.headYaw, headTarget, k);
|
| 529 |
this.headPitch = lerp(this.headPitch, TRACK.restPitchDeg, k);
|
|
|
|
| 530 |
}
|
| 531 |
|
| 532 |
easeHome(dt) {
|