feat(pose_3d): selezione polso + gizmo translate/rotate sul ghost
This commit is contained in:
1 parent
7a561cfa67
commit
64500944d6
1 file changed
+118
+118
@@ -197,6 +197,27 @@ function updateRealWrists() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function syncGhostsToTargetOrReal() {
|
||||||
|
for (const side of ['right', 'left']) {
|
||||||
|
const ghost = state.ghosts[side];
|
||||||
|
if (!ghost) continue;
|
||||||
|
if (state.target[side]) {
|
||||||
|
// ghost segue il target locale (mosso dal gizmo)
|
||||||
|
ghost.visible = true;
|
||||||
|
} else if (side === state.active_side) {
|
||||||
|
// ghost allineato al reale (preparato per drag)
|
||||||
|
const real = state.current[side];
|
||||||
|
if (real) {
|
||||||
|
ghost.position.set(real.position[0], real.position[1], real.position[2]);
|
||||||
|
ghost.quaternion.set(real.quaternion[0], real.quaternion[1],
|
||||||
|
real.quaternion[2], real.quaternion[3]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ghost.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateReadout() {
|
function updateReadout() {
|
||||||
const side = state.active_side;
|
const side = state.active_side;
|
||||||
const pose = state.target[side] || state.current[side];
|
const pose = state.target[side] || state.current[side];
|
||||||
@@ -226,6 +247,7 @@ async function pollState() {
|
|||||||
state.saved_poses = s.poses || {};
|
state.saved_poses = s.poses || {};
|
||||||
state.live_max_step_m = s.live_max_step_m || 0.15;
|
state.live_max_step_m = s.live_max_step_m || 0.15;
|
||||||
updateRealWrists();
|
updateRealWrists();
|
||||||
|
syncGhostsToTargetOrReal();
|
||||||
updateReadout();
|
updateReadout();
|
||||||
// Aggiorna dropdown pose salvate
|
// Aggiorna dropdown pose salvate
|
||||||
refreshPoseSelect();
|
refreshPoseSelect();
|
||||||
@@ -248,6 +270,99 @@ function refreshPoseSelect() {
|
|||||||
sel.value = cur;
|
sel.value = cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----- selezione polso via click -----
|
||||||
|
function setupSelection() {
|
||||||
|
const { renderer, camera, transform } = state.three;
|
||||||
|
const raycaster = new THREE.Raycaster();
|
||||||
|
const mouse = new THREE.Vector2();
|
||||||
|
const canvas = renderer.domElement;
|
||||||
|
|
||||||
|
canvas.addEventListener('pointerdown', (e) => {
|
||||||
|
// Se sta interagendo con il gizmo, non fare nulla
|
||||||
|
if (transform.dragging) return;
|
||||||
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
|
||||||
|
mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
|
||||||
|
raycaster.setFromCamera(mouse, camera);
|
||||||
|
// Verifica intersezione con sphere destra o sinistra
|
||||||
|
const candidates = [state.arms.right.userData.sphere,
|
||||||
|
state.arms.left.userData.sphere];
|
||||||
|
const hits = raycaster.intersectObjects(candidates, false);
|
||||||
|
if (hits.length > 0) {
|
||||||
|
const sphere = hits[0].object;
|
||||||
|
const side = sphere.parent.userData.side;
|
||||||
|
activateSide(side);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function activateSide(side) {
|
||||||
|
state.active_side = side;
|
||||||
|
// sync radio buttons
|
||||||
|
for (const r of document.querySelectorAll('input[name=p3Side]')) {
|
||||||
|
r.checked = (r.value === side);
|
||||||
|
}
|
||||||
|
// Attacca gizmo al ghost (lo creiamo se non esiste, posizionandolo sul reale)
|
||||||
|
ensureGhostInit(side);
|
||||||
|
const { transform } = state.three;
|
||||||
|
transform.attach(state.ghosts[side]);
|
||||||
|
state.ghosts[side].visible = true;
|
||||||
|
updateReadout();
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureGhostInit(side) {
|
||||||
|
// Se non c'è target locale, allinea ghost al reale (preparato per drag)
|
||||||
|
if (!state.target[side]) {
|
||||||
|
const real = state.current[side];
|
||||||
|
if (real) {
|
||||||
|
state.ghosts[side].position.set(real.position[0], real.position[1], real.position[2]);
|
||||||
|
state.ghosts[side].quaternion.set(real.quaternion[0], real.quaternion[1],
|
||||||
|
real.quaternion[2], real.quaternion[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- gizmo mode toggle -----
|
||||||
|
function bindGizmoControls() {
|
||||||
|
for (const r of document.querySelectorAll('input[name=p3Gizmo]')) {
|
||||||
|
r.addEventListener('change', () => {
|
||||||
|
state.gizmo_mode = r.value;
|
||||||
|
state.three.transform.setMode(r.value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// tasti t/r
|
||||||
|
window.addEventListener('keydown', (e) => {
|
||||||
|
if (!$('tab-pose-3d')?.classList.contains('active')) return;
|
||||||
|
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
||||||
|
if (e.key === 't') { setGizmoMode('translate'); }
|
||||||
|
if (e.key === 'r') { setGizmoMode('rotate'); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setGizmoMode(mode) {
|
||||||
|
state.gizmo_mode = mode;
|
||||||
|
state.three.transform.setMode(mode);
|
||||||
|
for (const r of document.querySelectorAll('input[name=p3Gizmo]')) {
|
||||||
|
r.checked = (r.value === mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- target tracking durante drag gizmo -----
|
||||||
|
function bindGizmoEvents() {
|
||||||
|
const { transform } = state.three;
|
||||||
|
transform.addEventListener('change', () => {
|
||||||
|
const side = state.active_side;
|
||||||
|
const ghost = state.ghosts[side];
|
||||||
|
if (!ghost) return;
|
||||||
|
state.target[side] = {
|
||||||
|
position: [ghost.position.x, ghost.position.y, ghost.position.z],
|
||||||
|
quaternion: [ghost.quaternion.x, ghost.quaternion.y,
|
||||||
|
ghost.quaternion.z, ghost.quaternion.w],
|
||||||
|
};
|
||||||
|
updateReadout();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ----- side change listener -----
|
// ----- side change listener -----
|
||||||
function bindSideControls() {
|
function bindSideControls() {
|
||||||
for (const r of document.querySelectorAll('input[name=p3Side]')) {
|
for (const r of document.querySelectorAll('input[name=p3Side]')) {
|
||||||
@@ -265,7 +380,10 @@ function ensureInit() {
|
|||||||
if (!$('p3Canvas')) return;
|
if (!$('p3Canvas')) return;
|
||||||
inited = true;
|
inited = true;
|
||||||
initScene();
|
initScene();
|
||||||
|
setupSelection();
|
||||||
bindSideControls();
|
bindSideControls();
|
||||||
|
bindGizmoControls();
|
||||||
|
bindGizmoEvents();
|
||||||
pollState();
|
pollState();
|
||||||
pollTimer = setInterval(pollState, POLL_MS);
|
pollTimer = setInterval(pollState, POLL_MS);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user