Files
syncro_multi_agente/THREEJS_USAGE.md

4.4 KiB
Raw Permalink Blame History

Three.js — come usare plotter.glb

import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';

const loader = new GLTFLoader();
loader.load('plotter.glb', (gltf) => {
  const root = gltf.scene;
  scene.add(root);

  // ----------------------------------------------------------
  // 1) Dati Fusion (joints, asBuiltJoints, motionLinks)
  // Salvati in scene.extras.fusion come stringa JSON
  // ----------------------------------------------------------
  const sceneIndex = gltf.parser.json.scene ?? 0;
  const sceneJson  = gltf.parser.json.scenes[sceneIndex];
  let fusion = null;
  try {
    const raw = sceneJson?.extras?.fusion;
    fusion = raw ? JSON.parse(raw) : null;
  } catch (e) {
    console.warn('Fusion extras non parsabile', e);
  }

  if (fusion) {
    console.log('Joints:', fusion.joints.length);
    console.log('AsBuilt:', fusion.asBuiltJoints.length);
    console.log('MotionLinks:', fusion.motionLinks.length);
    console.log('Metadata:', fusion.metadata);
  }

  // ----------------------------------------------------------
  // 2) Indice nome -> Object3D per trovare i nodi del joint
  // Ogni Empty ha userData.fusion_fullPathName
  // ----------------------------------------------------------
  const byFullPath = new Map();
  root.traverse((obj) => {
    const fp = obj.userData?.fusion_fullPathName;
    if (fp) byFullPath.set(fp, obj);
  });

  // ----------------------------------------------------------
  // 3) Esempio: applicare un joint slider
  // ----------------------------------------------------------
  const j = fusion.joints.find(jt => jt.type === 'slider');
  if (j) {
    const child = byFullPath.get(j.childFullPath);
    if (child) {
      const axis = new THREE.Vector3(...j.axis).normalize();
      // Sposta il child lungo l'asse di 100 mm = 0.1 m
      child.position.addScaledVector(axis, 0.1);
    }
  }

  // ----------------------------------------------------------
  // 4) Esempio: applicare un joint revolute
  // ----------------------------------------------------------
  const r = fusion.joints.find(jt => jt.type === 'revolute');
  if (r) {
    const child = byFullPath.get(r.childFullPath);
    if (child) {
      const axis = new THREE.Vector3(...r.axis).normalize();
      const angle = Math.PI / 4; // 45 gradi
      // Rotazione attorno all'asse, applicata in local space
      child.rotateOnAxis(axis, angle);
    }
  }
});

Struttura di scene.extras.fusion

{
  "metadata": { "units": "mm", "scaleFactor": 0.01, ... },
  "joints": [
    {
      "name": "joint_carrello_y",
      "type": "slider",          // rigid | revolute | slider | cylindrical | pin_slot | planar | ball
      "parent": "CARRELLO_X",
      "child":  "CARRELLO_Y",
      "parentFullPath": "TELAIO:1+CARRELLO_X:1",
      "childFullPath":  "TELAIO:1+CARRELLO_X:1+CARRELLO_Y:1",
      "origin": [x, y, z],       // in metri
      "axis":   [x, y, z],       // direzione unitaria
      "secondaryAxis": [...],    // solo pin_slot / planar
      "slideLimits":    { "minimumValue": 0, "maximumValue": 0.22, ... },  // metri
      "rotationLimits": { ... },                                            // radianti
      "slideValue":     0.05,    // metri (posizione corrente)
      "rotationValue":  0.5,     // radianti
      "isSuppressed":   false,
      "isLightBulbOn":  true,
      "isLocked":       false
    }
  ],
  "asBuiltJoints": [ /* stessa shape di joints */ ],
  "motionLinks": [
    {
      "name": "ML1",
      "joint1": "joint_motor",
      "joint2": "joint_belt",
      "ratio": 2.5,
      "reversed": false,
      "isSuppressed": false
    }
  ]
}

Note

  • Unità: GLB e dati joint sono in metri.
  • Asse Y-up: convenzione glTF (cambiata da Blender al momento dell'export).
  • Nodi Fusion: ogni Empty Blender ha userData.fusion_fullPathName, userData.fusion_componentName, userData.fusion_id (visibili in Object3D.userData lato Three.js).
  • Mesh: sono parentate sotto gli Empty corrispondenti. Per muovere un sotto-assieme, sposta l'Empty (Three.js eredita le trasformazioni come ti aspetti).
  • Motion links: la logica di "vincolo" tra joints (ratio, reversed) la devi implementare tu nel render loop: leggi slideValue/rotationValue di joint1, calcola joint2 = joint1 × ratio (con segno), applica a byFullPath.get(joint2.childFullPath).