Codels
1,8 k tris1,16 × 0,80 × 0,73 m

Utiliser dans le code

Remixer dans l’éditeur
Module ES autonome — ne dépend que de three. Appelez la fonction factory et ajoutez le groupe à votre scène.
/**
 * Hover Drone — Codels 3D model library
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 1,784 · Size (W × H × D): 1.16 × 0.80 × 0.73 m
 *
 * Usage:
 *   import { createHoverDrone } from './hover-drone.js';
 *   
 *   const model = createHoverDrone();
 *   model.position.set(0, 0, 0);
 *   scene.add(model);
 *   
 *   // Animations: idle
 *   const mixer = new THREE.AnimationMixer(model);
 *   mixer.clipAction(THREE.AnimationClip.findByName(model.animations, 'idle')).play();
 *   // in your render loop: mixer.update(clock.getDelta());
 */
import * as THREE from 'three';

const deg = THREE.MathUtils.degToRad;

// Tube along a smooth curve through the given points.
function tube(path, radius, tubularSegments, radialSegments, closed = false) {
  const curve = new THREE.CatmullRomCurve3(path.map(([x, y, z]) => new THREE.Vector3(x, y, z)), closed);
  return new THREE.TubeGeometry(curve, tubularSegments, radius, radialSegments, closed);
}

// Build animation clips from simple descriptions (Codels). Play them with THREE.AnimationMixer.
function codelsClips(THREE, entries) {
  var TAU = Math.PI * 2;
  var DEG = Math.PI / 180;
  var AX = { x: new THREE.Vector3(1, 0, 0), y: new THREE.Vector3(0, 1, 0), z: new THREE.Vector3(0, 0, 1) };
  var periodOf = function (a) { return a.type === 'spin' ? 360 / Math.abs(a.speed) : a.period; };
  var clips = [];

  // loops → 'idle'
  var loops = [];
  entries.forEach(function (e) {
    var l = e.anim.filter(function (a) { return a.type !== 'hinge'; });
    if (l.length) loops.push({ target: e.target, anim: l });
  });
  if (loops.length) {
    var periods = [];
    loops.forEach(function (e) { e.anim.forEach(function (a) { periods.push(periodOf(a)); }); });
    var longest = Math.max.apply(null, periods);
    var D = longest;
    var bestErr = Infinity;
    for (var m = 1; m <= 6 && bestErr > 0.01; m++) {
      var err = 0;
      periods.forEach(function (p) {
        var n = Math.max(1, Math.round((longest * m) / p));
        err = Math.max(err, Math.abs((longest * m) / n - p) / p);
      });
      if (err < bestErr - 1e-9) { bestErr = err; D = longest * m; }
    }
    var cycles = function (a) { return Math.max(1, Math.round(D / periodOf(a))); };
    var steps = 24;
    loops.forEach(function (e) { e.anim.forEach(function (a) { steps = Math.max(steps, cycles(a) * 24); }); });
    steps = Math.min(steps, 720);
    var tracks = [];
    loops.forEach(function (e) {
      var o = e.target;
      var q0 = o.quaternion.clone(), p0 = o.position.clone(), s0 = o.scale.clone();
      var q = new THREE.Quaternion(), turn = new THREE.Quaternion(), p = new THREE.Vector3(), s = new THREE.Vector3();
      var has = function (type) { return e.anim.some(function (a) { return a.type === type; }); };
      var turns = has('spin') || has('swing'), moves = has('bob'), grows = has('pulse');
      var times = [], qv = [], pv = [], sv = [];
      for (var k = 0; k <= steps; k++) {
        var t = (D * k) / steps;
        q.copy(q0); p.copy(p0); s.copy(s0);
        e.anim.forEach(function (a) {
          var n = cycles(a);
          var w = Math.sin(TAU * ((n * t) / D + (a.phase || 0)));
          if (a.type === 'spin') q.multiply(turn.setFromAxisAngle(AX[a.axis], (a.speed < 0 ? -TAU : TAU) * ((n * t) / D)));
          else if (a.type === 'swing') q.multiply(turn.setFromAxisAngle(AX[a.axis], a.angle * DEG * w));
          else if (a.type === 'bob') p.addScaledVector(AX[a.axis], a.distance * w);
          else if (a.type === 'pulse') s.multiplyScalar(1 + a.amount * w);
        });
        times.push(t);
        if (turns) qv.push(q.x, q.y, q.z, q.w);
        if (moves) pv.push(p.x, p.y, p.z);
        if (grows) sv.push(s.x, s.y, s.z);
      }
      if (turns) tracks.push(new THREE.QuaternionKeyframeTrack(o.uuid + '.quaternion', times, qv));
      if (moves) tracks.push(new THREE.VectorKeyframeTrack(o.uuid + '.position', times, pv));
      if (grows) tracks.push(new THREE.VectorKeyframeTrack(o.uuid + '.scale', times, sv));
    });
    clips.push(new THREE.AnimationClip('idle', D, tracks));
  }

  // hinge moves → one-shot clips ('open' …)
  var shots = {};
  entries.forEach(function (e) {
    e.anim.forEach(function (a) {
      if (a.type !== 'hinge') return;
      var name = a.clip || 'open';
      (shots[name] = shots[name] || []).push({ target: e.target, a: a });
    });
  });
  Object.keys(shots).forEach(function (name) {
    var tracks = [];
    var length = 0;
    shots[name].forEach(function (h) {
      var o = h.target, q0 = o.quaternion.clone(), q = new THREE.Quaternion(), turn = new THREE.Quaternion();
      var n = Math.max(4, Math.ceil(Math.abs(h.a.angle) / 10));
      var times = [], qv = [];
      for (var k = 0; k <= n; k++) {
        var f = k / n;
        var eased = f * f * (3 - 2 * f);
        q.copy(q0).multiply(turn.setFromAxisAngle(AX[h.a.axis], h.a.angle * DEG * eased));
        times.push(h.a.duration * f);
        qv.push(q.x, q.y, q.z, q.w);
      }
      length = Math.max(length, h.a.duration);
      tracks.push(new THREE.QuaternionKeyframeTrack(o.uuid + '.quaternion', times, qv));
    });
    clips.push(new THREE.AnimationClip(name, length, tracks));
  });
  return clips;
}

export function createHoverDrone() {
  const root = new THREE.Group();
  root.name = 'HoverDrone';

  // Materials
  const bodyMat = new THREE.MeshStandardMaterial({ name: 'body', color: 0xeef0f3, roughness: 0.35, metalness: 0.2, flatShading: true });
  const accentMat = new THREE.MeshStandardMaterial({ name: 'accent', color: 0xff7a2f, roughness: 0.45, metalness: 0.15, flatShading: true });
  const darkMat = new THREE.MeshStandardMaterial({ name: 'dark', color: 0x2c3038, roughness: 0.55, metalness: 0.45, flatShading: true });
  const eyeMat = new THREE.MeshStandardMaterial({ name: 'eye', color: 0x35d9ff, roughness: 0.3, emissive: 0x35d9ff, emissiveIntensity: 1.7, flatShading: true });
  const navRedMat = new THREE.MeshStandardMaterial({ name: 'navRed', color: 0xff3b3b, roughness: 0.4, emissive: 0xff2020, emissiveIntensity: 1.4, flatShading: true });

  // Shared geometries
  const boxGeo = new THREE.BoxGeometry(0.07, 0.05, 0.32);
  const torusGeo = new THREE.TorusGeometry(0.15, 0.03, 4, 12);
  const boxGeo2 = new THREE.BoxGeometry(0.3, 0.014, 0.016);
  const boxGeo3 = new THREE.BoxGeometry(0.016, 0.014, 0.3);
  const cylinderGeo = new THREE.CylinderGeometry(0.035, 0.04, 0.06, 6);
  const cylinderGeo2 = new THREE.CylinderGeometry(0.022, 0.022, 0.03, 6);
  const boxGeo4 = new THREE.BoxGeometry(0.025, 0.006, 0.09);
  const sphereGeo = new THREE.SphereGeometry(0.022, 5, 3);
  const sphereGeo2 = new THREE.SphereGeometry(0.03, 5, 3);

  // Parts
  const drone = new THREE.Group();
  drone.name = 'drone';
  root.add(drone);
  const body = new THREE.Group();
  body.name = 'body';
  drone.add(body);
  const shell = new THREE.Mesh(new THREE.SphereGeometry(0.22, 12, 8), bodyMat);
  shell.name = 'shell';
  shell.position.set(0, 0.44, 0);
  shell.scale.set(1, 0.78, 1.12);
  body.add(shell);
  const topCap = new THREE.Mesh(new THREE.SphereGeometry(0.2266, 12, 3, 0, Math.PI * 2, 0, 0.95), accentMat);
  topCap.name = 'topCap';
  topCap.position.set(0, 0.44, 0);
  topCap.scale.set(1, 0.78, 1.12);
  body.add(topCap);
  const belly = new THREE.Mesh(new THREE.SphereGeometry(0.2244, 12, 2, 0, Math.PI * 2, 2.35, 0.79), darkMat);
  belly.name = 'belly';
  belly.position.set(0, 0.44, 0);
  belly.scale.set(1, 0.78, 1.12);
  body.add(belly);
  const antenna = new THREE.Mesh(new THREE.CylinderGeometry(0.012, 0.018, 0.16, 5), darkMat);
  antenna.name = 'antenna';
  antenna.position.set(-0.06, 0.6828, -0.06);
  body.add(antenna);
  const antennaTip = new THREE.Mesh(new THREE.SphereGeometry(0.022, 6, 4), eyeMat);
  antennaTip.name = 'antennaTip';
  antennaTip.position.set(-0.06, 0.7728, -0.06);
  body.add(antennaTip);
  const eye = new THREE.Group();
  eye.name = 'eye';
  eye.position.set(0, 0.435, 0.2364);
  drone.add(eye);
  const eyeSocket = new THREE.Mesh(new THREE.CylinderGeometry(0.1, 0.105, 0.06, 12), darkMat);
  eyeSocket.name = 'eyeSocket';
  eyeSocket.position.set(0, 0, -0.012);
  eyeSocket.rotation.set(deg(90), 0, 0);
  eye.add(eyeSocket);
  const iris = new THREE.Mesh(new THREE.CylinderGeometry(0.072, 0.072, 0.03, 12), eyeMat);
  iris.name = 'iris';
  iris.position.set(0, 0, 0.012);
  iris.rotation.set(deg(90), 0, 0);
  eye.add(iris);
  const pupil = new THREE.Mesh(new THREE.CylinderGeometry(0.03, 0.03, 0.03, 8), darkMat);
  pupil.name = 'pupil';
  pupil.position.set(0, 0, 0.02);
  pupil.rotation.set(deg(90), 0, 0);
  eye.add(pupil);
  const glint = new THREE.Mesh(new THREE.SphereGeometry(0.014, 6, 4), bodyMat);
  glint.name = 'glint';
  glint.position.set(0.03, 0.03, 0.03);
  eye.add(glint);
  const eyeRim = new THREE.Mesh(new THREE.TorusGeometry(0.1, 0.012, 3, 12), accentMat);
  eyeRim.name = 'eyeRim';
  eyeRim.position.set(0, 0, 0.018);
  eye.add(eyeRim);
  const arm1 = new THREE.Mesh(boxGeo, darkMat);
  arm1.name = 'arm1';
  arm1.position.set(0.2356, 0.45, 0.1099);
  arm1.rotation.set(0, deg(65), 0);
  drone.add(arm1);
  const duct1 = new THREE.Mesh(torusGeo, accentMat);
  duct1.name = 'duct1';
  duct1.position.set(0.3988, 0.47, 0.186);
  duct1.rotation.set(deg(90), 0, 0);
  drone.add(duct1);
  const box16 = new THREE.Mesh(boxGeo2, darkMat);
  box16.position.set(0.3988, 0.45, 0.186);
  drone.add(box16);
  const box17 = new THREE.Mesh(boxGeo3, darkMat);
  box17.position.set(0.3988, 0.45, 0.186);
  drone.add(box17);
  const motor1 = new THREE.Mesh(cylinderGeo, darkMat);
  motor1.name = 'motor1';
  motor1.position.set(0.3988, 0.465, 0.186);
  drone.add(motor1);
  const rotor1 = new THREE.Group();
  rotor1.name = 'rotor1';
  rotor1.position.set(0.3988, 0.5, 0.186);
  drone.add(rotor1);
  const hub = new THREE.Mesh(cylinderGeo2, bodyMat);
  hub.name = 'hub';
  rotor1.add(hub);
  const box21 = new THREE.Mesh(boxGeo4, bodyMat);
  box21.position.set(0, 0, 0.06);
  box21.rotation.set(0, 0, deg(8));
  rotor1.add(box21);
  const box22 = new THREE.Mesh(boxGeo4, bodyMat);
  box22.position.set(0.052, 0, -0.03);
  box22.rotation.set(0, deg(120), deg(8));
  rotor1.add(box22);
  const box23 = new THREE.Mesh(boxGeo4, bodyMat);
  box23.position.set(-0.052, 0, -0.03);
  box23.rotation.set(0, deg(240), deg(8));
  rotor1.add(box23);
  const navLight1 = new THREE.Mesh(sphereGeo, eyeMat);
  navLight1.name = 'navLight1';
  navLight1.position.set(0.5619, 0.47, 0.262);
  drone.add(navLight1);
  const leg1 = new THREE.Mesh(tube([[0.1813, 0.36, 0.0845], [0.2719, 0.18, 0.1268], [0.29, 0.03, 0.1352]], 0.014, 6, 4), darkMat);
  leg1.name = 'leg1';
  drone.add(leg1);
  const foot1 = new THREE.Mesh(sphereGeo2, accentMat);
  foot1.name = 'foot1';
  foot1.position.set(0.29, 0.03, 0.1352);
  drone.add(foot1);
  const arm2 = new THREE.Mesh(boxGeo, darkMat);
  arm2.name = 'arm2';
  arm2.position.set(0.2356, 0.45, -0.1099);
  arm2.rotation.set(0, deg(115), 0);
  drone.add(arm2);
  const duct2 = new THREE.Mesh(torusGeo, bodyMat);
  duct2.name = 'duct2';
  duct2.position.set(0.3988, 0.47, -0.186);
  duct2.rotation.set(deg(90), 0, 0);
  drone.add(duct2);
  const box29 = new THREE.Mesh(boxGeo2, darkMat);
  box29.position.set(0.3988, 0.45, -0.186);
  drone.add(box29);
  const box30 = new THREE.Mesh(boxGeo3, darkMat);
  box30.position.set(0.3988, 0.45, -0.186);
  drone.add(box30);
  const motor2 = new THREE.Mesh(cylinderGeo, darkMat);
  motor2.name = 'motor2';
  motor2.position.set(0.3988, 0.465, -0.186);
  drone.add(motor2);
  const rotor2 = new THREE.Group();
  rotor2.name = 'rotor2';
  rotor2.position.set(0.3988, 0.5, -0.186);
  drone.add(rotor2);
  const hub2 = new THREE.Mesh(cylinderGeo2, bodyMat);
  hub2.name = 'hub';
  rotor2.add(hub2);
  const box34 = new THREE.Mesh(boxGeo4, bodyMat);
  box34.position.set(0, 0, 0.06);
  box34.rotation.set(0, 0, deg(8));
  rotor2.add(box34);
  const box35 = new THREE.Mesh(boxGeo4, bodyMat);
  box35.position.set(0.052, 0, -0.03);
  box35.rotation.set(0, deg(120), deg(8));
  rotor2.add(box35);
  const box36 = new THREE.Mesh(boxGeo4, bodyMat);
  box36.position.set(-0.052, 0, -0.03);
  box36.rotation.set(0, deg(240), deg(8));
  rotor2.add(box36);
  const navLight2 = new THREE.Mesh(sphereGeo, navRedMat);
  navLight2.name = 'navLight2';
  navLight2.position.set(0.5619, 0.47, -0.262);
  drone.add(navLight2);
  const leg2 = new THREE.Mesh(tube([[0.1813, 0.36, -0.0845], [0.2719, 0.18, -0.1268], [0.29, 0.03, -0.1352]], 0.014, 6, 4), darkMat);
  leg2.name = 'leg2';
  drone.add(leg2);
  const foot2 = new THREE.Mesh(sphereGeo2, accentMat);
  foot2.name = 'foot2';
  foot2.position.set(0.29, 0.03, -0.1352);
  drone.add(foot2);
  const arm3 = new THREE.Mesh(boxGeo, darkMat);
  arm3.name = 'arm3';
  arm3.position.set(-0.2356, 0.45, -0.1099);
  arm3.rotation.set(0, deg(245), 0);
  drone.add(arm3);
  const duct3 = new THREE.Mesh(torusGeo, bodyMat);
  duct3.name = 'duct3';
  duct3.position.set(-0.3988, 0.47, -0.186);
  duct3.rotation.set(deg(90), 0, 0);
  drone.add(duct3);
  const box42 = new THREE.Mesh(boxGeo2, darkMat);
  box42.position.set(-0.3988, 0.45, -0.186);
  drone.add(box42);
  const box43 = new THREE.Mesh(boxGeo3, darkMat);
  box43.position.set(-0.3988, 0.45, -0.186);
  drone.add(box43);
  const motor3 = new THREE.Mesh(cylinderGeo, darkMat);
  motor3.name = 'motor3';
  motor3.position.set(-0.3988, 0.465, -0.186);
  drone.add(motor3);
  const rotor3 = new THREE.Group();
  rotor3.name = 'rotor3';
  rotor3.position.set(-0.3988, 0.5, -0.186);
  drone.add(rotor3);
  const hub3 = new THREE.Mesh(cylinderGeo2, bodyMat);
  hub3.name = 'hub';
  rotor3.add(hub3);
  const box47 = new THREE.Mesh(boxGeo4, bodyMat);
  box47.position.set(0, 0, 0.06);
  box47.rotation.set(0, 0, deg(8));
  rotor3.add(box47);
  const box48 = new THREE.Mesh(boxGeo4, bodyMat);
  box48.position.set(0.052, 0, -0.03);
  box48.rotation.set(0, deg(120), deg(8));
  rotor3.add(box48);
  const box49 = new THREE.Mesh(boxGeo4, bodyMat);
  box49.position.set(-0.052, 0, -0.03);
  box49.rotation.set(0, deg(240), deg(8));
  rotor3.add(box49);
  const navLight3 = new THREE.Mesh(sphereGeo, navRedMat);
  navLight3.name = 'navLight3';
  navLight3.position.set(-0.5619, 0.47, -0.262);
  drone.add(navLight3);
  const leg3 = new THREE.Mesh(tube([[-0.1813, 0.36, -0.0845], [-0.2719, 0.18, -0.1268], [-0.29, 0.03, -0.1352]], 0.014, 6, 4), darkMat);
  leg3.name = 'leg3';
  drone.add(leg3);
  const foot3 = new THREE.Mesh(sphereGeo2, accentMat);
  foot3.name = 'foot3';
  foot3.position.set(-0.29, 0.03, -0.1352);
  drone.add(foot3);
  const arm4 = new THREE.Mesh(boxGeo, darkMat);
  arm4.name = 'arm4';
  arm4.position.set(-0.2356, 0.45, 0.1099);
  arm4.rotation.set(0, deg(295), 0);
  drone.add(arm4);
  const duct4 = new THREE.Mesh(torusGeo, accentMat);
  duct4.name = 'duct4';
  duct4.position.set(-0.3988, 0.47, 0.186);
  duct4.rotation.set(deg(90), 0, 0);
  drone.add(duct4);
  const box55 = new THREE.Mesh(boxGeo2, darkMat);
  box55.position.set(-0.3988, 0.45, 0.186);
  drone.add(box55);
  const box56 = new THREE.Mesh(boxGeo3, darkMat);
  box56.position.set(-0.3988, 0.45, 0.186);
  drone.add(box56);
  const motor4 = new THREE.Mesh(cylinderGeo, darkMat);
  motor4.name = 'motor4';
  motor4.position.set(-0.3988, 0.465, 0.186);
  drone.add(motor4);
  const rotor4 = new THREE.Group();
  rotor4.name = 'rotor4';
  rotor4.position.set(-0.3988, 0.5, 0.186);
  drone.add(rotor4);
  const hub4 = new THREE.Mesh(cylinderGeo2, bodyMat);
  hub4.name = 'hub';
  rotor4.add(hub4);
  const box60 = new THREE.Mesh(boxGeo4, bodyMat);
  box60.position.set(0, 0, 0.06);
  box60.rotation.set(0, 0, deg(8));
  rotor4.add(box60);
  const box61 = new THREE.Mesh(boxGeo4, bodyMat);
  box61.position.set(0.052, 0, -0.03);
  box61.rotation.set(0, deg(120), deg(8));
  rotor4.add(box61);
  const box62 = new THREE.Mesh(boxGeo4, bodyMat);
  box62.position.set(-0.052, 0, -0.03);
  box62.rotation.set(0, deg(240), deg(8));
  rotor4.add(box62);
  const navLight4 = new THREE.Mesh(sphereGeo, eyeMat);
  navLight4.name = 'navLight4';
  navLight4.position.set(-0.5619, 0.47, 0.262);
  drone.add(navLight4);
  const leg4 = new THREE.Mesh(tube([[-0.1813, 0.36, 0.0845], [-0.2719, 0.18, 0.1268], [-0.29, 0.03, 0.1352]], 0.014, 6, 4), darkMat);
  leg4.name = 'leg4';
  drone.add(leg4);
  const foot4 = new THREE.Mesh(sphereGeo2, accentMat);
  foot4.name = 'foot4';
  foot4.position.set(-0.29, 0.03, 0.1352);
  drone.add(foot4);

  // Animations ('idle') — play them with THREE.AnimationMixer
  root.animations = codelsClips(THREE, [
    { target: rotor1, anim: [{ type: 'spin', axis: 'y', speed: 1080 }] },
    { target: rotor2, anim: [{ type: 'spin', axis: 'y', speed: -1080 }] },
    { target: rotor3, anim: [{ type: 'spin', axis: 'y', speed: 1080 }] },
    { target: rotor4, anim: [{ type: 'spin', axis: 'y', speed: -1080 }] },
  ]);

  root.traverse((o) => {
    if (o.isMesh) {
      o.castShadow = true;
      o.receiveShadow = true;
    }
  });
  return root;
}

export default createHoverDrone;

Notes et commentaires

—
Aucune note pour l’instant — soyez le premier.
5
0
4
0
3
0
2
0
1
0

Connectez-vous pour noter et commenter.

0 commentaire

Aucun commentaire pour l’instant. Dites comment vous avez utilisé ce modèle ou ce qui pourrait être amélioré.

Modèles similaires

Même catégorie ou étiquettes proches