diff --git a/web/pose_3d.js b/web/pose_3d.js index fbbe657..c4ef614 100644 --- a/web/pose_3d.js +++ b/web/pose_3d.js @@ -4,6 +4,8 @@ import * as THREE from 'three'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; import { TransformControls } from 'three/addons/controls/TransformControls.js'; +import { STLLoader } from 'three/addons/loaders/STLLoader.js'; +import URDFLoader from 'three/addons/loaders/URDFLoader.js'; const $ = (id) => document.getElementById(id); const POLL_MS = 1000; @@ -81,6 +83,45 @@ const SHOULDER = { left: new THREE.Vector3(0, 0.20, 0.60), }; +// ----- URDF loader ----- +// Carica il modello G1 reale a partire dal URDF vendored sotto +// /static/vendor/g1/. Le mesh STL referenziate nel URDF (path relativi tipo +// "meshes/pelvis.STL") vengono risolti contro lo stesso prefisso. +async function loadG1Urdf(scene) { + const loader = new URDFLoader(); + loader.loadMeshCb = function (path, manager, onComplete) { + // path arrives like "meshes/pelvis.STL"; resolve to our static path + const url = `/static/vendor/g1/${path}`; + const stl = new STLLoader(manager); + stl.load( + url, + (geometry) => { + const mat = new THREE.MeshStandardMaterial({ + color: 0x9099a3, metalness: 0.4, roughness: 0.6, + transparent: true, opacity: 0.85, + }); + const mesh = new THREE.Mesh(geometry, mat); + onComplete(mesh); + }, + undefined, + (err) => onComplete(null, err), + ); + }; + return new Promise((resolve, reject) => { + loader.load( + '/static/vendor/g1/g1.urdf', + (robot) => { + // G1 URDF uses Z-up too (Unitree convention) so no rotation needed + scene.add(robot); + console.log('[pose_3d] G1 URDF caricato:', robot.name || 'g1'); + resolve(robot); + }, + undefined, + (err) => reject(err), + ); + }); +} + // ----- scene init ----- function initScene() { const canvas = $('p3Canvas'); @@ -117,45 +158,12 @@ function initScene() { frontArrow.scale.set(0.18, 0.045, 1); // più largo ma sottile, "etichetta a terra" scene.add(frontArrow); - // Body silhouette: torso + head + legs + shoulders - const bodyMat = new THREE.MeshStandardMaterial({ - color: 0x4a5560, transparent: true, opacity: 0.6, + // Modello G1 reale via URDF (best-effort: se il loader fallisce, niente + // body — la tab funziona comunque coi soli polsi/UCS). + loadG1Urdf(scene).catch((e) => { + console.warn('[pose_3d] URDF G1 non caricato:', e.message); }); - // Torso: 20cm x 40cm x 50cm, centered at TORSO_POS - const torso = new THREE.Mesh(new THREE.BoxGeometry(0.20, 0.40, 0.50), bodyMat); - torso.position.copy(TORSO_POS); - scene.add(torso); - - // Head: sfera 12cm sopra il torso - const head = new THREE.Mesh( - new THREE.SphereGeometry(0.09, 24, 16), bodyMat, - ); - head.position.set(TORSO_POS.x, TORSO_POS.y, TORSO_POS.z + 0.25 + 0.09); - scene.add(head); - - // Hips (rectangle) just below torso - const hips = new THREE.Mesh(new THREE.BoxGeometry(0.22, 0.36, 0.10), bodyMat); - hips.position.set(TORSO_POS.x, TORSO_POS.y, TORSO_POS.z - 0.25 - 0.05); - scene.add(hips); - - // Gambe: 2 cilindri verticali, da hip a Z=0 - const legGeom = new THREE.CylinderGeometry(0.06, 0.06, 0.55, 16); - for (const ly of [-0.10, 0.10]) { - const leg = new THREE.Mesh(legGeom, bodyMat); - leg.rotation.x = Math.PI / 2; // asse cilindro su Z - leg.position.set(TORSO_POS.x, TORSO_POS.y + ly, (TORSO_POS.z - 0.30 - 0.275)); - scene.add(leg); - } - - // Spalle: 2 sferette in corrispondenza degli SHOULDER - const shoulderGeom = new THREE.SphereGeometry(0.04, 16, 12); - for (const side of ['right', 'left']) { - const sh = new THREE.Mesh(shoulderGeom, bodyMat); - sh.position.copy(SHOULDER[side]); - scene.add(sh); - } - // Arms for (const side of ['right', 'left']) { const wrist = makeWristMarker(side === 'right' ? 0x4da3ff : 0xff5e7e); diff --git a/web/vendor/g1/g1.urdf b/web/vendor/g1/g1.urdf new file mode 100644 index 0000000..156a5fd --- /dev/null +++ b/web/vendor/g1/g1.urdf @@ -0,0 +1,1476 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/vendor/g1/meshes/head_link.STL b/web/vendor/g1/meshes/head_link.STL new file mode 100644 index 0000000..2ee5fba Binary files /dev/null and b/web/vendor/g1/meshes/head_link.STL differ diff --git a/web/vendor/g1/meshes/left_ankle_pitch_link.STL b/web/vendor/g1/meshes/left_ankle_pitch_link.STL new file mode 100644 index 0000000..69de849 Binary files /dev/null and b/web/vendor/g1/meshes/left_ankle_pitch_link.STL differ diff --git a/web/vendor/g1/meshes/left_ankle_roll_link.STL b/web/vendor/g1/meshes/left_ankle_roll_link.STL new file mode 100644 index 0000000..8864e9f Binary files /dev/null and b/web/vendor/g1/meshes/left_ankle_roll_link.STL differ diff --git a/web/vendor/g1/meshes/left_elbow_link.STL b/web/vendor/g1/meshes/left_elbow_link.STL new file mode 100644 index 0000000..1a96d99 Binary files /dev/null and b/web/vendor/g1/meshes/left_elbow_link.STL differ diff --git a/web/vendor/g1/meshes/left_hand_index_0_link.STL b/web/vendor/g1/meshes/left_hand_index_0_link.STL new file mode 100644 index 0000000..8069369 Binary files /dev/null and b/web/vendor/g1/meshes/left_hand_index_0_link.STL differ diff --git a/web/vendor/g1/meshes/left_hand_index_1_link.STL b/web/vendor/g1/meshes/left_hand_index_1_link.STL new file mode 100644 index 0000000..89d231d Binary files /dev/null and b/web/vendor/g1/meshes/left_hand_index_1_link.STL differ diff --git a/web/vendor/g1/meshes/left_hand_middle_0_link.STL b/web/vendor/g1/meshes/left_hand_middle_0_link.STL new file mode 100644 index 0000000..8069369 Binary files /dev/null and b/web/vendor/g1/meshes/left_hand_middle_0_link.STL differ diff --git a/web/vendor/g1/meshes/left_hand_middle_1_link.STL b/web/vendor/g1/meshes/left_hand_middle_1_link.STL new file mode 100644 index 0000000..89d231d Binary files /dev/null and b/web/vendor/g1/meshes/left_hand_middle_1_link.STL differ diff --git a/web/vendor/g1/meshes/left_hand_palm_link.STL b/web/vendor/g1/meshes/left_hand_palm_link.STL new file mode 100644 index 0000000..7d595ed Binary files /dev/null and b/web/vendor/g1/meshes/left_hand_palm_link.STL differ diff --git a/web/vendor/g1/meshes/left_hand_thumb_0_link.STL b/web/vendor/g1/meshes/left_hand_thumb_0_link.STL new file mode 100644 index 0000000..3028bb4 Binary files /dev/null and b/web/vendor/g1/meshes/left_hand_thumb_0_link.STL differ diff --git a/web/vendor/g1/meshes/left_hand_thumb_1_link.STL b/web/vendor/g1/meshes/left_hand_thumb_1_link.STL new file mode 100644 index 0000000..d1c080c Binary files /dev/null and b/web/vendor/g1/meshes/left_hand_thumb_1_link.STL differ diff --git a/web/vendor/g1/meshes/left_hand_thumb_2_link.STL b/web/vendor/g1/meshes/left_hand_thumb_2_link.STL new file mode 100644 index 0000000..8b32e96 Binary files /dev/null and b/web/vendor/g1/meshes/left_hand_thumb_2_link.STL differ diff --git a/web/vendor/g1/meshes/left_hip_pitch_link.STL b/web/vendor/g1/meshes/left_hip_pitch_link.STL new file mode 100644 index 0000000..5b751c7 Binary files /dev/null and b/web/vendor/g1/meshes/left_hip_pitch_link.STL differ diff --git a/web/vendor/g1/meshes/left_hip_roll_link.STL b/web/vendor/g1/meshes/left_hip_roll_link.STL new file mode 100644 index 0000000..778437f Binary files /dev/null and b/web/vendor/g1/meshes/left_hip_roll_link.STL differ diff --git a/web/vendor/g1/meshes/left_hip_yaw_link.STL b/web/vendor/g1/meshes/left_hip_yaw_link.STL new file mode 100644 index 0000000..383093a Binary files /dev/null and b/web/vendor/g1/meshes/left_hip_yaw_link.STL differ diff --git a/web/vendor/g1/meshes/left_knee_link.STL b/web/vendor/g1/meshes/left_knee_link.STL new file mode 100644 index 0000000..f2e98e5 Binary files /dev/null and b/web/vendor/g1/meshes/left_knee_link.STL differ diff --git a/web/vendor/g1/meshes/left_rubber_hand.STL b/web/vendor/g1/meshes/left_rubber_hand.STL new file mode 100644 index 0000000..c44830f Binary files /dev/null and b/web/vendor/g1/meshes/left_rubber_hand.STL differ diff --git a/web/vendor/g1/meshes/left_shoulder_pitch_link.STL b/web/vendor/g1/meshes/left_shoulder_pitch_link.STL new file mode 100644 index 0000000..e698311 Binary files /dev/null and b/web/vendor/g1/meshes/left_shoulder_pitch_link.STL differ diff --git a/web/vendor/g1/meshes/left_shoulder_roll_link.STL b/web/vendor/g1/meshes/left_shoulder_roll_link.STL new file mode 100644 index 0000000..80bca84 Binary files /dev/null and b/web/vendor/g1/meshes/left_shoulder_roll_link.STL differ diff --git a/web/vendor/g1/meshes/left_shoulder_yaw_link.STL b/web/vendor/g1/meshes/left_shoulder_yaw_link.STL new file mode 100644 index 0000000..281e699 Binary files /dev/null and b/web/vendor/g1/meshes/left_shoulder_yaw_link.STL differ diff --git a/web/vendor/g1/meshes/left_wrist_pitch_link.STL b/web/vendor/g1/meshes/left_wrist_pitch_link.STL new file mode 100644 index 0000000..82cc224 Binary files /dev/null and b/web/vendor/g1/meshes/left_wrist_pitch_link.STL differ diff --git a/web/vendor/g1/meshes/left_wrist_roll_link.STL b/web/vendor/g1/meshes/left_wrist_roll_link.STL new file mode 100644 index 0000000..f3c263a Binary files /dev/null and b/web/vendor/g1/meshes/left_wrist_roll_link.STL differ diff --git a/web/vendor/g1/meshes/left_wrist_roll_rubber_hand.STL b/web/vendor/g1/meshes/left_wrist_roll_rubber_hand.STL new file mode 100644 index 0000000..8fa435b Binary files /dev/null and b/web/vendor/g1/meshes/left_wrist_roll_rubber_hand.STL differ diff --git a/web/vendor/g1/meshes/left_wrist_yaw_link.STL b/web/vendor/g1/meshes/left_wrist_yaw_link.STL new file mode 100644 index 0000000..31be4fd Binary files /dev/null and b/web/vendor/g1/meshes/left_wrist_yaw_link.STL differ diff --git a/web/vendor/g1/meshes/logo_link.STL b/web/vendor/g1/meshes/logo_link.STL new file mode 100644 index 0000000..e979209 Binary files /dev/null and b/web/vendor/g1/meshes/logo_link.STL differ diff --git a/web/vendor/g1/meshes/pelvis.STL b/web/vendor/g1/meshes/pelvis.STL new file mode 100644 index 0000000..691a779 Binary files /dev/null and b/web/vendor/g1/meshes/pelvis.STL differ diff --git a/web/vendor/g1/meshes/pelvis_contour_link.STL b/web/vendor/g1/meshes/pelvis_contour_link.STL new file mode 100644 index 0000000..4243433 Binary files /dev/null and b/web/vendor/g1/meshes/pelvis_contour_link.STL differ diff --git a/web/vendor/g1/meshes/right_ankle_pitch_link.STL b/web/vendor/g1/meshes/right_ankle_pitch_link.STL new file mode 100644 index 0000000..e77d8a2 Binary files /dev/null and b/web/vendor/g1/meshes/right_ankle_pitch_link.STL differ diff --git a/web/vendor/g1/meshes/right_ankle_roll_link.STL b/web/vendor/g1/meshes/right_ankle_roll_link.STL new file mode 100644 index 0000000..d4261dd Binary files /dev/null and b/web/vendor/g1/meshes/right_ankle_roll_link.STL differ diff --git a/web/vendor/g1/meshes/right_elbow_link.STL b/web/vendor/g1/meshes/right_elbow_link.STL new file mode 100644 index 0000000..f259e38 Binary files /dev/null and b/web/vendor/g1/meshes/right_elbow_link.STL differ diff --git a/web/vendor/g1/meshes/right_hand_index_0_link.STL b/web/vendor/g1/meshes/right_hand_index_0_link.STL new file mode 100644 index 0000000..f87ad32 Binary files /dev/null and b/web/vendor/g1/meshes/right_hand_index_0_link.STL differ diff --git a/web/vendor/g1/meshes/right_hand_index_1_link.STL b/web/vendor/g1/meshes/right_hand_index_1_link.STL new file mode 100644 index 0000000..6dea51a Binary files /dev/null and b/web/vendor/g1/meshes/right_hand_index_1_link.STL differ diff --git a/web/vendor/g1/meshes/right_hand_middle_0_link.STL b/web/vendor/g1/meshes/right_hand_middle_0_link.STL new file mode 100644 index 0000000..f87ad32 Binary files /dev/null and b/web/vendor/g1/meshes/right_hand_middle_0_link.STL differ diff --git a/web/vendor/g1/meshes/right_hand_middle_1_link.STL b/web/vendor/g1/meshes/right_hand_middle_1_link.STL new file mode 100644 index 0000000..6dea51a Binary files /dev/null and b/web/vendor/g1/meshes/right_hand_middle_1_link.STL differ diff --git a/web/vendor/g1/meshes/right_hand_palm_link.STL b/web/vendor/g1/meshes/right_hand_palm_link.STL new file mode 100644 index 0000000..5ae00a7 Binary files /dev/null and b/web/vendor/g1/meshes/right_hand_palm_link.STL differ diff --git a/web/vendor/g1/meshes/right_hand_thumb_0_link.STL b/web/vendor/g1/meshes/right_hand_thumb_0_link.STL new file mode 100644 index 0000000..1cae7f1 Binary files /dev/null and b/web/vendor/g1/meshes/right_hand_thumb_0_link.STL differ diff --git a/web/vendor/g1/meshes/right_hand_thumb_1_link.STL b/web/vendor/g1/meshes/right_hand_thumb_1_link.STL new file mode 100644 index 0000000..c141fbf Binary files /dev/null and b/web/vendor/g1/meshes/right_hand_thumb_1_link.STL differ diff --git a/web/vendor/g1/meshes/right_hand_thumb_2_link.STL b/web/vendor/g1/meshes/right_hand_thumb_2_link.STL new file mode 100644 index 0000000..e942923 Binary files /dev/null and b/web/vendor/g1/meshes/right_hand_thumb_2_link.STL differ diff --git a/web/vendor/g1/meshes/right_hip_pitch_link.STL b/web/vendor/g1/meshes/right_hip_pitch_link.STL new file mode 100644 index 0000000..998a0a0 Binary files /dev/null and b/web/vendor/g1/meshes/right_hip_pitch_link.STL differ diff --git a/web/vendor/g1/meshes/right_hip_roll_link.STL b/web/vendor/g1/meshes/right_hip_roll_link.STL new file mode 100644 index 0000000..47b2eeb Binary files /dev/null and b/web/vendor/g1/meshes/right_hip_roll_link.STL differ diff --git a/web/vendor/g1/meshes/right_hip_yaw_link.STL b/web/vendor/g1/meshes/right_hip_yaw_link.STL new file mode 100644 index 0000000..3718564 Binary files /dev/null and b/web/vendor/g1/meshes/right_hip_yaw_link.STL differ diff --git a/web/vendor/g1/meshes/right_knee_link.STL b/web/vendor/g1/meshes/right_knee_link.STL new file mode 100644 index 0000000..76d21a3 Binary files /dev/null and b/web/vendor/g1/meshes/right_knee_link.STL differ diff --git a/web/vendor/g1/meshes/right_rubber_hand.STL b/web/vendor/g1/meshes/right_rubber_hand.STL new file mode 100644 index 0000000..0aacffb Binary files /dev/null and b/web/vendor/g1/meshes/right_rubber_hand.STL differ diff --git a/web/vendor/g1/meshes/right_shoulder_pitch_link.STL b/web/vendor/g1/meshes/right_shoulder_pitch_link.STL new file mode 100644 index 0000000..3f5b4ed Binary files /dev/null and b/web/vendor/g1/meshes/right_shoulder_pitch_link.STL differ diff --git a/web/vendor/g1/meshes/right_shoulder_roll_link.STL b/web/vendor/g1/meshes/right_shoulder_roll_link.STL new file mode 100644 index 0000000..179d617 Binary files /dev/null and b/web/vendor/g1/meshes/right_shoulder_roll_link.STL differ diff --git a/web/vendor/g1/meshes/right_shoulder_yaw_link.STL b/web/vendor/g1/meshes/right_shoulder_yaw_link.STL new file mode 100644 index 0000000..2ba6076 Binary files /dev/null and b/web/vendor/g1/meshes/right_shoulder_yaw_link.STL differ diff --git a/web/vendor/g1/meshes/right_wrist_pitch_link.STL b/web/vendor/g1/meshes/right_wrist_pitch_link.STL new file mode 100644 index 0000000..da19454 Binary files /dev/null and b/web/vendor/g1/meshes/right_wrist_pitch_link.STL differ diff --git a/web/vendor/g1/meshes/right_wrist_roll_link.STL b/web/vendor/g1/meshes/right_wrist_roll_link.STL new file mode 100644 index 0000000..26868d2 Binary files /dev/null and b/web/vendor/g1/meshes/right_wrist_roll_link.STL differ diff --git a/web/vendor/g1/meshes/right_wrist_roll_rubber_hand.STL b/web/vendor/g1/meshes/right_wrist_roll_rubber_hand.STL new file mode 100644 index 0000000..c365aa9 Binary files /dev/null and b/web/vendor/g1/meshes/right_wrist_roll_rubber_hand.STL differ diff --git a/web/vendor/g1/meshes/right_wrist_yaw_link.STL b/web/vendor/g1/meshes/right_wrist_yaw_link.STL new file mode 100644 index 0000000..d788902 Binary files /dev/null and b/web/vendor/g1/meshes/right_wrist_yaw_link.STL differ diff --git a/web/vendor/g1/meshes/torso_constraint_L_link.STL b/web/vendor/g1/meshes/torso_constraint_L_link.STL new file mode 100644 index 0000000..75d82f5 Binary files /dev/null and b/web/vendor/g1/meshes/torso_constraint_L_link.STL differ diff --git a/web/vendor/g1/meshes/torso_constraint_L_rod_link.STL b/web/vendor/g1/meshes/torso_constraint_L_rod_link.STL new file mode 100644 index 0000000..6747f3f Binary files /dev/null and b/web/vendor/g1/meshes/torso_constraint_L_rod_link.STL differ diff --git a/web/vendor/g1/meshes/torso_constraint_R_link.STL b/web/vendor/g1/meshes/torso_constraint_R_link.STL new file mode 100644 index 0000000..5cb5958 Binary files /dev/null and b/web/vendor/g1/meshes/torso_constraint_R_link.STL differ diff --git a/web/vendor/g1/meshes/torso_constraint_R_rod_link.STL b/web/vendor/g1/meshes/torso_constraint_R_rod_link.STL new file mode 100644 index 0000000..95cf415 Binary files /dev/null and b/web/vendor/g1/meshes/torso_constraint_R_rod_link.STL differ diff --git a/web/vendor/g1/meshes/torso_link.STL b/web/vendor/g1/meshes/torso_link.STL new file mode 100644 index 0000000..17745af Binary files /dev/null and b/web/vendor/g1/meshes/torso_link.STL differ diff --git a/web/vendor/g1/meshes/torso_link_23dof_rev_1_0.STL b/web/vendor/g1/meshes/torso_link_23dof_rev_1_0.STL new file mode 100644 index 0000000..079aa81 Binary files /dev/null and b/web/vendor/g1/meshes/torso_link_23dof_rev_1_0.STL differ diff --git a/web/vendor/g1/meshes/torso_link_rev_1_0.STL b/web/vendor/g1/meshes/torso_link_rev_1_0.STL new file mode 100644 index 0000000..8a759a7 Binary files /dev/null and b/web/vendor/g1/meshes/torso_link_rev_1_0.STL differ diff --git a/web/vendor/g1/meshes/waist_constraint_L.STL b/web/vendor/g1/meshes/waist_constraint_L.STL new file mode 100644 index 0000000..911410f Binary files /dev/null and b/web/vendor/g1/meshes/waist_constraint_L.STL differ diff --git a/web/vendor/g1/meshes/waist_constraint_R.STL b/web/vendor/g1/meshes/waist_constraint_R.STL new file mode 100644 index 0000000..babe794 Binary files /dev/null and b/web/vendor/g1/meshes/waist_constraint_R.STL differ diff --git a/web/vendor/g1/meshes/waist_roll_link.STL b/web/vendor/g1/meshes/waist_roll_link.STL new file mode 100644 index 0000000..65831ab Binary files /dev/null and b/web/vendor/g1/meshes/waist_roll_link.STL differ diff --git a/web/vendor/g1/meshes/waist_roll_link_rev_1_0.STL b/web/vendor/g1/meshes/waist_roll_link_rev_1_0.STL new file mode 100644 index 0000000..a64f330 Binary files /dev/null and b/web/vendor/g1/meshes/waist_roll_link_rev_1_0.STL differ diff --git a/web/vendor/g1/meshes/waist_support_link.STL b/web/vendor/g1/meshes/waist_support_link.STL new file mode 100644 index 0000000..63660fb Binary files /dev/null and b/web/vendor/g1/meshes/waist_support_link.STL differ diff --git a/web/vendor/g1/meshes/waist_yaw_link.STL b/web/vendor/g1/meshes/waist_yaw_link.STL new file mode 100644 index 0000000..7d36b02 Binary files /dev/null and b/web/vendor/g1/meshes/waist_yaw_link.STL differ diff --git a/web/vendor/g1/meshes/waist_yaw_link_rev_1_0.STL b/web/vendor/g1/meshes/waist_yaw_link_rev_1_0.STL new file mode 100644 index 0000000..0fabb63 Binary files /dev/null and b/web/vendor/g1/meshes/waist_yaw_link_rev_1_0.STL differ diff --git a/web/vendor/three/addons/loaders/URDFClasses.js b/web/vendor/three/addons/loaders/URDFClasses.js new file mode 100644 index 0000000..ad10e28 --- /dev/null +++ b/web/vendor/three/addons/loaders/URDFClasses.js @@ -0,0 +1,469 @@ +import { Euler, Object3D, Vector3, Quaternion, Matrix4 } from 'three'; + +const _tempAxis = new Vector3(); +const _tempEuler = new Euler(); +const _tempTransform = new Matrix4(); +const _tempOrigTransform = new Matrix4(); +const _tempQuat = new Quaternion(); +const _tempScale = new Vector3(1.0, 1.0, 1.0); +const _tempPosition = new Vector3(); + +class URDFBase extends Object3D { + + constructor(...args) { + + super(...args); + this.urdfNode = null; + this.urdfName = ''; + + } + + copy(source, recursive) { + + super.copy(source, recursive); + + this.urdfNode = source.urdfNode; + this.urdfName = source.urdfName; + + return this; + + } + +} + +class URDFCollider extends URDFBase { + + constructor(...args) { + + super(...args); + this.isURDFCollider = true; + this.type = 'URDFCollider'; + + } + +} + +class URDFVisual extends URDFBase { + + constructor(...args) { + + super(...args); + this.isURDFVisual = true; + this.type = 'URDFVisual'; + + } + +} + +class URDFLink extends URDFBase { + + constructor(...args) { + + super(...args); + this.isURDFLink = true; + this.type = 'URDFLink'; + + } + +} + +class URDFJoint extends URDFBase { + + get jointType() { + + return this._jointType; + + } + + set jointType(v) { + + if (this.jointType === v) return; + this._jointType = v; + this.matrixWorldNeedsUpdate = true; + switch (v) { + + case 'fixed': + this.jointValue = []; + break; + + case 'continuous': + case 'revolute': + case 'prismatic': + this.jointValue = new Array(1).fill(0); + break; + + case 'planar': + // Planar joints are, 3dof: position XY and rotation Z. + this.jointValue = new Array(3).fill(0); + this.axis = new Vector3(0, 0, 1); + break; + + case 'floating': + this.jointValue = new Array(6).fill(0); + break; + + } + + } + + get angle() { + + return this.jointValue[0]; + + } + + constructor(...args) { + + super(...args); + + this.isURDFJoint = true; + this.type = 'URDFJoint'; + + this.jointValue = null; + this.jointType = 'fixed'; + this.axis = new Vector3(1, 0, 0); + this.limit = { lower: 0, upper: 0 }; + this.ignoreLimits = false; + + this.origPosition = null; + this.origQuaternion = null; + + this.mimicJoints = []; + + } + + /* Overrides */ + copy(source, recursive) { + + super.copy(source, recursive); + + this.jointType = source.jointType; + this.axis = source.axis.clone(); + this.limit.lower = source.limit.lower; + this.limit.upper = source.limit.upper; + this.ignoreLimits = false; + + this.jointValue = [...source.jointValue]; + + this.origPosition = source.origPosition ? source.origPosition.clone() : null; + this.origQuaternion = source.origQuaternion ? source.origQuaternion.clone() : null; + + this.mimicJoints = [...source.mimicJoints]; + + return this; + + } + + /* Public Functions */ + /** + * @param {...number|null} values The joint value components to set, optionally null for no-op + * @returns {boolean} Whether the invocation of this function resulted in an actual change to the joint value + */ + setJointValue(...values) { + + // Parse all incoming values into numbers except null, which we treat as a no-op for that value component. + values = values.map(v => v === null ? null : parseFloat(v)); + + if (!this.origPosition || !this.origQuaternion) { + + this.origPosition = this.position.clone(); + this.origQuaternion = this.quaternion.clone(); + + } + + let didUpdate = false; + + this.mimicJoints.forEach(joint => { + + didUpdate = joint.updateFromMimickedJoint(...values) || didUpdate; + + }); + + switch (this.jointType) { + + case 'fixed': { + + return didUpdate; + + } + case 'continuous': + case 'revolute': { + + let angle = values[0]; + if (angle == null) return didUpdate; + if (angle === this.jointValue[0]) return didUpdate; + + if (!this.ignoreLimits && this.jointType === 'revolute') { + + angle = Math.min(this.limit.upper, angle); + angle = Math.max(this.limit.lower, angle); + + } + + this.quaternion + .setFromAxisAngle(this.axis, angle) + .premultiply(this.origQuaternion); + + if (this.jointValue[0] !== angle) { + + this.jointValue[0] = angle; + this.matrixWorldNeedsUpdate = true; + return true; + + } else { + + return didUpdate; + + } + + } + + case 'prismatic': { + + let pos = values[0]; + if (pos == null) return didUpdate; + if (pos === this.jointValue[0]) return didUpdate; + + if (!this.ignoreLimits) { + + pos = Math.min(this.limit.upper, pos); + pos = Math.max(this.limit.lower, pos); + + } + + this.position.copy(this.origPosition); + _tempAxis.copy(this.axis).applyEuler(this.rotation); + this.position.addScaledVector(_tempAxis, pos); + + if (this.jointValue[0] !== pos) { + + this.jointValue[0] = pos; + this.matrixWorldNeedsUpdate = true; + return true; + + } else { + + return didUpdate; + + } + + } + + case 'floating': { + + // no-op if all values are identical to existing value or are null + if (this.jointValue.every((value, index) => values[index] === value || values[index] === null)) return didUpdate; + // Floating joints have six degrees of freedom: X, Y, Z, R, P, Y. + this.jointValue[0] = values[0] !== null ? values[0] : this.jointValue[0]; + this.jointValue[1] = values[1] !== null ? values[1] : this.jointValue[1]; + this.jointValue[2] = values[2] !== null ? values[2] : this.jointValue[2]; + this.jointValue[3] = values[3] !== null ? values[3] : this.jointValue[3]; + this.jointValue[4] = values[4] !== null ? values[4] : this.jointValue[4]; + this.jointValue[5] = values[5] !== null ? values[5] : this.jointValue[5]; + + // Compose transform of joint origin and transform due to joint values + _tempOrigTransform.compose(this.origPosition, this.origQuaternion, _tempScale); + _tempQuat.setFromEuler( + _tempEuler.set( + this.jointValue[3], + this.jointValue[4], + this.jointValue[5], + 'XYZ', + ), + ); + _tempPosition.set(this.jointValue[0], this.jointValue[1], this.jointValue[2]); + _tempTransform.compose(_tempPosition, _tempQuat, _tempScale); + + // Calcualte new transform + _tempOrigTransform.premultiply(_tempTransform); + this.position.setFromMatrixPosition(_tempOrigTransform); + this.rotation.setFromRotationMatrix(_tempOrigTransform); + + this.matrixWorldNeedsUpdate = true; + return true; + } + + case 'planar': { + + // no-op if all values are identical to existing value or are null + if (this.jointValue.every((value, index) => values[index] === value || values[index] === null)) return didUpdate; + + this.jointValue[0] = values[0] !== null ? values[0] : this.jointValue[0]; + this.jointValue[1] = values[1] !== null ? values[1] : this.jointValue[1]; + this.jointValue[2] = values[2] !== null ? values[2] : this.jointValue[2]; + + // Compose transform of joint origin and transform due to joint values + _tempOrigTransform.compose(this.origPosition, this.origQuaternion, _tempScale); + _tempQuat.setFromAxisAngle(this.axis, this.jointValue[2]); + _tempPosition.set(this.jointValue[0], this.jointValue[1], 0.0); + _tempTransform.compose(_tempPosition, _tempQuat, _tempScale); + + // Calculate new transform + _tempOrigTransform.premultiply(_tempTransform); + this.position.setFromMatrixPosition(_tempOrigTransform); + this.rotation.setFromRotationMatrix(_tempOrigTransform); + + this.matrixWorldNeedsUpdate = true; + return true; + } + + } + + return didUpdate; + + } + +} + +class URDFMimicJoint extends URDFJoint { + + constructor(...args) { + + super(...args); + this.type = 'URDFMimicJoint'; + this.mimicJoint = null; + this.offset = 0; + this.multiplier = 1; + + } + + updateFromMimickedJoint(...values) { + + const modifiedValues = values.map(x => x * this.multiplier + this.offset); + return super.setJointValue(...modifiedValues); + + } + + /* Overrides */ + copy(source, recursive) { + + super.copy(source, recursive); + + this.mimicJoint = source.mimicJoint; + this.offset = source.offset; + this.multiplier = source.multiplier; + + return this; + + } + +} + +class URDFRobot extends URDFLink { + + constructor(...args) { + + super(...args); + this.isURDFRobot = true; + this.urdfNode = null; + + this.urdfRobotNode = null; + this.robotName = null; + + this.links = null; + this.joints = null; + this.colliders = null; + this.visual = null; + this.frames = null; + + } + + copy(source, recursive) { + + super.copy(source, recursive); + + this.urdfRobotNode = source.urdfRobotNode; + this.robotName = source.robotName; + + this.links = {}; + this.joints = {}; + this.colliders = {}; + this.visual = {}; + + this.traverse(c => { + + if (c.isURDFJoint && c.urdfName in source.joints) { + + this.joints[c.urdfName] = c; + + } + + if (c.isURDFLink && c.urdfName in source.links) { + + this.links[c.urdfName] = c; + + } + + if (c.isURDFCollider && c.urdfName in source.colliders) { + + this.colliders[c.urdfName] = c; + + } + + if (c.isURDFVisual && c.urdfName in source.visual) { + + this.visual[c.urdfName] = c; + + } + + }); + + // Repair mimic joint references once we've re-accumulated all our joint data + for (const joint in this.joints) { + this.joints[joint].mimicJoints = this.joints[joint].mimicJoints.map((mimicJoint) => this.joints[mimicJoint.name]); + } + + this.frames = { + ...this.colliders, + ...this.visual, + ...this.links, + ...this.joints, + }; + + return this; + + } + + getFrame(name) { + + return this.frames[name]; + + } + + setJointValue(jointName, ...angle) { + + const joint = this.joints[jointName]; + if (joint) { + + return joint.setJointValue(...angle); + + } + + return false; + } + + setJointValues(values) { + + let didChange = false; + for (const name in values) { + + const value = values[name]; + if (Array.isArray(value)) { + + didChange = this.setJointValue(name, ...value) || didChange; + + } else { + + didChange = this.setJointValue(name, value) || didChange; + + } + + } + + return didChange; + + } + +} + +export { URDFRobot, URDFLink, URDFJoint, URDFMimicJoint, URDFVisual, URDFCollider }; diff --git a/web/vendor/three/addons/loaders/URDFLoader.js b/web/vendor/three/addons/loaders/URDFLoader.js new file mode 100644 index 0000000..d5d8049 --- /dev/null +++ b/web/vendor/three/addons/loaders/URDFLoader.js @@ -0,0 +1,661 @@ +import * as THREE from 'three'; +import { STLLoader } from 'three/addons/loaders/STLLoader.js'; +import { URDFRobot, URDFJoint, URDFLink, URDFCollider, URDFVisual, URDFMimicJoint } from './URDFClasses.js'; + +// ColladaLoader stubbed: G1 URDF uses only STL meshes, so we don't vendor the +// full ColladaLoader. If a future URDF references .dae files the default +// loadMeshCb will throw — supply a custom loadMeshCb in that case. +const ColladaLoader = class { constructor() { throw new Error('ColladaLoader non disponibile (solo STL supportato)'); } }; + +/* +Reference coordinate frames for THREE.js and ROS. +Both coordinate systems are right handed so the URDF is instantiated without +frame transforms. The resulting model can be rotated to rectify the proper up, +right, and forward directions + +THREE.js + Y + | + | + .-----X + / +Z + +ROS URDf + Z + | X + | / + Y-----. + +*/ + +const tempQuaternion = new THREE.Quaternion(); +const tempEuler = new THREE.Euler(); + +// take a vector "x y z" and process it into +// an array [x, y, z] +function processTuple(val) { + + if (!val) return [0, 0, 0]; + return val.trim().split(/\s+/g).map(num => parseFloat(num)); + +} + +// applies a rotation a threejs object in URDF order +function applyRotation(obj, rpy, additive = false) { + + // if additive is true the rotation is applied in + // addition to the existing rotation + if (!additive) obj.rotation.set(0, 0, 0); + + tempEuler.set(rpy[0], rpy[1], rpy[2], 'ZYX'); + tempQuaternion.setFromEuler(tempEuler); + tempQuaternion.multiply(obj.quaternion); + obj.quaternion.copy(tempQuaternion); + +} + +/* URDFLoader Class */ +// Loads and reads a URDF file into a THREEjs Object3D format +export default +class URDFLoader { + + constructor(manager) { + + this.manager = manager || THREE.DefaultLoadingManager; + this.loadMeshCb = this.defaultMeshLoader.bind(this); + this.parseVisual = true; + this.parseCollision = false; + this.packages = ''; + this.workingPath = ''; + this.fetchOptions = {}; + + } + + /* Public API */ + loadAsync(urdf) { + + return new Promise((resolve, reject) => { + + this.load(urdf, resolve, null, reject); + + }); + + } + + // urdf: The path to the URDF within the package OR absolute + // onComplete: Callback that is passed the model once loaded + load(urdf, onComplete, onProgress, onError) { + + // Check if a full URI is specified before + // prepending the package info + const manager = this.manager; + const workingPath = THREE.LoaderUtils.extractUrlBase(urdf); + const urdfPath = this.manager.resolveURL(urdf); + + manager.itemStart(urdfPath); + + fetch(urdfPath, this.fetchOptions) + .then(res => { + + if (res.ok) { + + if (onProgress) { + + onProgress(null); + + } + return res.text(); + + } else { + + throw new Error(`URDFLoader: Failed to load url '${ urdfPath }' with error code ${ res.status } : ${ res.statusText }.`); + + } + + }) + .then(data => { + + const model = this.parse(data, this.workingPath || workingPath); + onComplete(model); + manager.itemEnd(urdfPath); + + }) + .catch(e => { + + if (onError) { + + onError(e); + + } else { + + console.error('URDFLoader: Error loading file.', e); + + } + manager.itemError(urdfPath); + manager.itemEnd(urdfPath); + + }); + + } + + parse(content, workingPath = this.workingPath) { + + const packages = this.packages; + const loadMeshCb = this.loadMeshCb; + const parseVisual = this.parseVisual; + const parseCollision = this.parseCollision; + const manager = this.manager; + const linkMap = {}; + const jointMap = {}; + const materialMap = {}; + + // Resolves the path of mesh files + function resolvePath(path) { + + if (!/^package:\/\//.test(path)) { + + return workingPath ? workingPath + path : path; + + } + + // Remove "package://" keyword and split meshPath at the first slash + const [targetPkg, relPath] = path.replace(/^package:\/\//, '').split(/\/(.+)/); + + if (typeof packages === 'string') { + + // "pkg" is one single package + if (packages.endsWith(targetPkg)) { + + // "pkg" is the target package + return packages + '/' + relPath; + + } else { + + // Assume "pkg" is the target package's parent directory + return packages + '/' + targetPkg + '/' + relPath; + + } + + } else if (packages instanceof Function) { + + return packages(targetPkg) + '/' + relPath; + + } else if (typeof packages === 'object') { + + // "pkg" is a map of packages + if (targetPkg in packages) { + + return packages[targetPkg] + '/' + relPath; + + } else { + + console.error(`URDFLoader : ${ targetPkg } not found in provided package list.`); + return null; + + } + + } + + } + + // Process the URDF text format + function processUrdf(data) { + + let children; + if (data instanceof Document) { + + children = [ ...data.children ]; + + } else if (data instanceof Element) { + + children = [ data ]; + + } else { + + const parser = new DOMParser(); + const urdf = parser.parseFromString(data, 'text/xml'); + children = [ ...urdf.children ]; + + } + + const robotNode = children.filter(c => c.nodeName === 'robot').pop(); + return processRobot(robotNode); + + } + + // Process the node + function processRobot(robot) { + + const robotNodes = [ ...robot.children ]; + const links = robotNodes.filter(c => c.nodeName.toLowerCase() === 'link'); + const joints = robotNodes.filter(c => c.nodeName.toLowerCase() === 'joint'); + const materials = robotNodes.filter(c => c.nodeName.toLowerCase() === 'material'); + const obj = new URDFRobot(); + + obj.robotName = robot.getAttribute('name'); + obj.urdfRobotNode = robot; + + // Create the map + materials.forEach(m => { + + const name = m.getAttribute('name'); + materialMap[name] = processMaterial(m); + + }); + + // Create the map + const visualMap = {}; + const colliderMap = {}; + links.forEach(l => { + + const name = l.getAttribute('name'); + const isRoot = robot.querySelector(`child[link="${ name }"]`) === null; + linkMap[name] = processLink(l, visualMap, colliderMap, isRoot ? obj : null); + + }); + + // Create the map + joints.forEach(j => { + + const name = j.getAttribute('name'); + jointMap[name] = processJoint(j); + + }); + + obj.joints = jointMap; + obj.links = linkMap; + obj.colliders = colliderMap; + obj.visual = visualMap; + + // Link up mimic joints + const jointList = Object.values(jointMap); + jointList.forEach(j => { + + if (j instanceof URDFMimicJoint) { + + jointMap[j.mimicJoint].mimicJoints.push(j); + + } + + }); + + // Detect infinite loops of mimic joints + jointList.forEach(j => { + + const uniqueJoints = new Set(); + const iterFunction = joint => { + + if (uniqueJoints.has(joint)) { + + throw new Error('URDFLoader: Detected an infinite loop of mimic joints.'); + + } + + uniqueJoints.add(joint); + joint.mimicJoints.forEach(j => { + + iterFunction(j); + + }); + + }; + + iterFunction(j); + }); + + obj.frames = { + ...colliderMap, + ...visualMap, + ...linkMap, + ...jointMap, + }; + + return obj; + + } + + // Process joint nodes and parent them + function processJoint(joint) { + + const children = [ ...joint.children ]; + const jointType = joint.getAttribute('type'); + + let obj; + + const mimicTag = children.find(n => n.nodeName.toLowerCase() === 'mimic'); + if (mimicTag) { + + obj = new URDFMimicJoint(); + obj.mimicJoint = mimicTag.getAttribute('joint'); + obj.multiplier = parseFloat(mimicTag.getAttribute('multiplier') || 1.0); + obj.offset = parseFloat(mimicTag.getAttribute('offset') || 0.0); + + } else { + + obj = new URDFJoint(); + + } + + obj.urdfNode = joint; + obj.name = joint.getAttribute('name'); + obj.urdfName = obj.name; + obj.jointType = jointType; + + let parent = null; + let child = null; + let xyz = [0, 0, 0]; + let rpy = [0, 0, 0]; + + // Extract the attributes + children.forEach(n => { + + const type = n.nodeName.toLowerCase(); + if (type === 'origin') { + + xyz = processTuple(n.getAttribute('xyz')); + rpy = processTuple(n.getAttribute('rpy')); + + } else if (type === 'child') { + + child = linkMap[n.getAttribute('link')]; + + } else if (type === 'parent') { + + parent = linkMap[n.getAttribute('link')]; + + } else if (type === 'limit') { + + obj.limit.lower = parseFloat(n.getAttribute('lower') || obj.limit.lower); + obj.limit.upper = parseFloat(n.getAttribute('upper') || obj.limit.upper); + + } + }); + + // Join the links + parent.add(obj); + obj.add(child); + applyRotation(obj, rpy); + obj.position.set(xyz[0], xyz[1], xyz[2]); + + // Set up the rotate function + const axisNode = children.filter(n => n.nodeName.toLowerCase() === 'axis')[0]; + + if (axisNode) { + + const axisXYZ = axisNode.getAttribute('xyz').split(/\s+/g).map(num => parseFloat(num)); + obj.axis = new THREE.Vector3(axisXYZ[0], axisXYZ[1], axisXYZ[2]); + obj.axis.normalize(); + + } + + return obj; + + } + + // Process the nodes + function processLink(link, visualMap, colliderMap, target = null) { + + if (target === null) { + + target = new URDFLink(); + + } + + const children = [ ...link.children ]; + target.name = link.getAttribute('name'); + target.urdfName = target.name; + target.urdfNode = link; + + if (parseVisual) { + + const visualNodes = children.filter(n => n.nodeName.toLowerCase() === 'visual'); + visualNodes.forEach(vn => { + + const v = processLinkElement(vn, materialMap); + target.add(v); + + if (vn.hasAttribute('name')) { + + const name = vn.getAttribute('name'); + v.name = name; + v.urdfName = name; + visualMap[name] = v; + + } + + }); + + } + + if (parseCollision) { + + const collisionNodes = children.filter(n => n.nodeName.toLowerCase() === 'collision'); + collisionNodes.forEach(cn => { + + const c = processLinkElement(cn); + target.add(c); + + if (cn.hasAttribute('name')) { + + const name = cn.getAttribute('name'); + c.name = name; + c.urdfName = name; + colliderMap[name] = c; + + } + + }); + + } + + return target; + + } + + function processMaterial(node) { + + const matNodes = [ ...node.children ]; + const material = new THREE.MeshPhongMaterial(); + + material.name = node.getAttribute('name') || ''; + matNodes.forEach(n => { + + const type = n.nodeName.toLowerCase(); + if (type === 'color') { + + const rgba = + n + .getAttribute('rgba') + .split(/\s/g) + .map(v => parseFloat(v)); + + material.color.setRGB(rgba[0], rgba[1], rgba[2]); + material.opacity = rgba[3]; + material.transparent = rgba[3] < 1; + material.depthWrite = !material.transparent; + + } else if (type === 'texture') { + + // The URDF spec does not require that the tag include + // a filename attribute so skip loading the texture if not provided. + const filename = n.getAttribute('filename'); + if (filename) { + + const loader = new THREE.TextureLoader(manager); + const filePath = resolvePath(filename); + material.map = loader.load(filePath); + material.map.colorSpace = THREE.SRGBColorSpace; + + } + + } + }); + + return material; + + } + + // Process the visual and collision nodes into meshes + function processLinkElement(vn, materialMap = {}) { + + const isCollisionNode = vn.nodeName.toLowerCase() === 'collision'; + const children = [ ...vn.children ]; + let material = null; + + // get the material first + const materialNode = children.filter(n => n.nodeName.toLowerCase() === 'material')[0]; + if (materialNode) { + + const name = materialNode.getAttribute('name'); + if (name && name in materialMap) { + + material = materialMap[name]; + + } else { + + material = processMaterial(materialNode); + + } + + } else { + + material = new THREE.MeshPhongMaterial(); + + } + + const group = isCollisionNode ? new URDFCollider() : new URDFVisual(); + group.urdfNode = vn; + + children.forEach(n => { + + const type = n.nodeName.toLowerCase(); + if (type === 'geometry') { + + const geoType = n.children[0].nodeName.toLowerCase(); + if (geoType === 'mesh') { + + const filename = n.children[0].getAttribute('filename'); + const filePath = resolvePath(filename); + + // file path is null if a package directory is not provided. + if (filePath !== null) { + + const scaleAttr = n.children[0].getAttribute('scale'); + if (scaleAttr) { + + const scale = processTuple(scaleAttr); + group.scale.set(scale[0], scale[1], scale[2]); + + } + + loadMeshCb(filePath, manager, (obj, err) => { + + if (err) { + + console.error('URDFLoader: Error loading mesh.', err); + + } else if (obj) { + + if (obj instanceof THREE.Mesh) { + + obj.material = material; + + } + + // We don't expect non identity rotations or positions. In the case of + // COLLADA files the model might come in with a custom scale for unit + // conversion. + obj.position.set(0, 0, 0); + obj.quaternion.identity(); + group.add(obj); + + } + + }); + + } + + } else if (geoType === 'box') { + + const primitiveModel = new THREE.Mesh(); + primitiveModel.geometry = new THREE.BoxGeometry(1, 1, 1); + primitiveModel.material = material; + + const size = processTuple(n.children[0].getAttribute('size')); + primitiveModel.scale.set(size[0], size[1], size[2]); + + group.add(primitiveModel); + + } else if (geoType === 'sphere') { + + const primitiveModel = new THREE.Mesh(); + primitiveModel.geometry = new THREE.SphereGeometry(1, 30, 30); + primitiveModel.material = material; + + const radius = parseFloat(n.children[0].getAttribute('radius')) || 0; + primitiveModel.scale.set(radius, radius, radius); + + group.add(primitiveModel); + + } else if (geoType === 'cylinder') { + + const primitiveModel = new THREE.Mesh(); + primitiveModel.geometry = new THREE.CylinderGeometry(1, 1, 1, 30); + primitiveModel.material = material; + + const radius = parseFloat(n.children[0].getAttribute('radius')) || 0; + const length = parseFloat(n.children[0].getAttribute('length')) || 0; + primitiveModel.scale.set(radius, length, radius); + primitiveModel.rotation.set(Math.PI / 2, 0, 0); + + group.add(primitiveModel); + + } + + } else if (type === 'origin') { + + const xyz = processTuple(n.getAttribute('xyz')); + const rpy = processTuple(n.getAttribute('rpy')); + + group.position.set(xyz[0], xyz[1], xyz[2]); + group.rotation.set(0, 0, 0); + applyRotation(group, rpy); + + } + + }); + + return group; + + } + + return processUrdf(content); + + } + + // Default mesh loading function + defaultMeshLoader(path, manager, done) { + + if (/\.stl$/i.test(path)) { + + const loader = new STLLoader(manager); + loader.load(path, geom => { + const mesh = new THREE.Mesh(geom, new THREE.MeshPhongMaterial()); + done(mesh); + }); + + } else if (/\.dae$/i.test(path)) { + + const loader = new ColladaLoader(manager); + loader.load(path, dae => done(dae.scene)); + + } else { + + console.warn(`URDFLoader: Could not load model at ${ path }.\nNo loader available`); + + } + + } + +};