Initial commit: Fusion->Blender->GLB pipeline + contract for ThreeJS bridge
This commit is contained in:
119
THREEJS_USAGE.md
Normal file
119
THREEJS_USAGE.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Three.js — come usare plotter.glb
|
||||
|
||||
## Caricamento e accesso a joints/motion links
|
||||
|
||||
```javascript
|
||||
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`
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"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)`.
|
||||
Reference in New Issue
Block a user