Codels
1404 Tris2,10 × 3,12 × 0,60 m

Im Code verwenden

Im Editor remixen
Eigenständiges ES-Modul — hängt nur von three ab. Ruf die Factory auf und füge die Gruppe deiner Szene hinzu.
/**
 * Locked Dungeon Door — Codels 3D model library
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 1,404 · Size (W × H × D): 2.10 × 3.12 × 0.60 m
 *
 * Usage:
 *   import { createLockedDoor } from './locked-door.js';
 *   
 *   const model = createLockedDoor();
 *   model.position.set(0, 0, 0);
 *   scene.add(model);
 *   
 *   // Animations: open
 *   const mixer = new THREE.AnimationMixer(model);
 *   const open = mixer.clipAction(THREE.AnimationClip.findByName(model.animations, 'open'));
 *   open.setLoop(THREE.LoopOnce, 1);
 *   open.clampWhenFinished = true;
 *   open.play(); // back: open.timeScale = -1; open.paused = false;
 *   // in your render loop: mixer.update(clock.getDelta());
 */
import * as THREE from 'three';

const deg = THREE.MathUtils.degToRad;

// Extrude a 2D outline along Z (centered). bevel = [thickness, size, segments].
function extrude(outline, depth, holes = [], bevel = null) {
  const shape = new THREE.Shape(outline.map(([x, y]) => new THREE.Vector2(x, y)));
  for (const hole of holes) shape.holes.push(new THREE.Path(hole.map(([x, y]) => new THREE.Vector2(x, y))));
  const options = bevel
    ? { depth, bevelEnabled: true, bevelThickness: bevel[0], bevelSize: bevel[1], bevelSegments: bevel[2] }
    : { depth, bevelEnabled: false };
  return new THREE.ExtrudeGeometry(shape, options).translate(0, 0, -depth / 2);
}

// 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 createLockedDoor() {
  const root = new THREE.Group();
  root.name = 'LockedDoor';

  // Materials
  const stoneMat = new THREE.MeshStandardMaterial({ name: 'stone', color: 0x8d929c, roughness: 0.9, flatShading: true });
  const stoneDarkMat = new THREE.MeshStandardMaterial({ name: 'stoneDark', color: 0x6c717b, roughness: 0.95, flatShading: true });
  const woodMat = new THREE.MeshStandardMaterial({ name: 'wood', color: 0x7a4a26, roughness: 0.85, flatShading: true });
  const woodAltMat = new THREE.MeshStandardMaterial({ name: 'woodAlt', color: 0x683f20, roughness: 0.85, flatShading: true });
  const ironMat = new THREE.MeshStandardMaterial({ name: 'iron', color: 0x3a3d44, roughness: 0.5, metalness: 0.6, flatShading: true });
  const brassMat = new THREE.MeshStandardMaterial({ name: 'brass', color: 0xd9a93a, roughness: 0.3, metalness: 0.7, flatShading: true });
  const glowMat = new THREE.MeshStandardMaterial({ name: 'glow', color: 0x90e8ff, roughness: 0.3, emissive: 0x46d9ff, emissiveIntensity: 1.2, flatShading: true });
  const tympanumMat = new THREE.MeshStandardMaterial({ name: 'tympanum', color: 0x4b4f58, roughness: 0.95, flatShading: true });

  // Shared geometries
  const boxGeo = new THREE.BoxGeometry(0.42, 0.48, 0.4);
  const boxGeo2 = new THREE.BoxGeometry(0.36, 0.4, 0.4);
  const boxGeo3 = new THREE.BoxGeometry(0.42, 0.14, 0.4);
  const boxGeo4 = new THREE.BoxGeometry(0.224, 1.99, 0.1);
  const boxGeo5 = new THREE.BoxGeometry(1.1, 0.1, 0.02);
  const cylinderGeo = new THREE.CylinderGeometry(0.035, 0.035, 0.22, 8);
  const cylinderGeo2 = new THREE.CylinderGeometry(0.018, 0.022, 0.02, 6);
  const cylinderGeo3 = new THREE.CylinderGeometry(0.016, 0.016, 0.03, 6);

  // Parts
  const threshold = new THREE.Mesh(new THREE.BoxGeometry(2.1, 0.06, 0.6), stoneDarkMat);
  threshold.name = 'threshold';
  threshold.position.set(0, 0.03, 0.05);
  root.add(threshold);
  const jambLeft = new THREE.Mesh(boxGeo, stoneDarkMat);
  jambLeft.name = 'jambLeft';
  jambLeft.position.set(-0.81, 0.31, 0);
  root.add(jambLeft);
  const box3 = new THREE.Mesh(boxGeo2, stoneMat);
  box3.position.set(-0.78, 0.77, 0);
  root.add(box3);
  const box4 = new THREE.Mesh(boxGeo, stoneMat);
  box4.position.set(-0.81, 1.23, 0);
  root.add(box4);
  const box5 = new THREE.Mesh(boxGeo2, stoneDarkMat);
  box5.position.set(-0.78, 1.69, 0);
  root.add(box5);
  const box6 = new THREE.Mesh(boxGeo3, stoneMat);
  box6.position.set(-0.81, 1.98, 0);
  root.add(box6);
  const jambRight = new THREE.Mesh(boxGeo, stoneMat);
  jambRight.name = 'jambRight';
  jambRight.position.set(0.81, 0.31, 0);
  root.add(jambRight);
  const box8 = new THREE.Mesh(boxGeo2, stoneMat);
  box8.position.set(0.78, 0.77, 0);
  root.add(box8);
  const box9 = new THREE.Mesh(boxGeo, stoneDarkMat);
  box9.position.set(0.81, 1.23, 0);
  root.add(box9);
  const box10 = new THREE.Mesh(boxGeo2, stoneMat);
  box10.position.set(0.78, 1.69, 0);
  root.add(box10);
  const box11 = new THREE.Mesh(boxGeo3, stoneMat);
  box11.position.set(0.81, 1.98, 0);
  root.add(box11);
  const arch = new THREE.Group();
  arch.name = 'arch';
  arch.position.set(0, 2.06, 0);
  root.add(arch);
  const extrude13 = new THREE.Mesh(extrude([[0.9799, 0.0118], [0.9685, 0.1499], [0.9376, 0.2851], [0.888, 0.4146], [0.5437, 0.2538], [0.574, 0.1746], [0.5929, 0.0918], [0.6, 0.0072]], 0.4), stoneMat);
  arch.add(extrude13);
  const extrude14 = new THREE.Mesh(extrude([[0.8778, 0.4358], [0.8075, 0.5553], [0.7211, 0.6637], [0.6202, 0.7588], [0.3797, 0.4646], [0.4415, 0.4063], [0.4944, 0.34], [0.5374, 0.2668]], 0.4), stoneDarkMat);
  arch.add(extrude14);
  const extrude15 = new THREE.Mesh(extrude([[0.6018, 0.7735], [0.4866, 0.8507], [0.3617, 0.9108], [0.2295, 0.9527], [0.1405, 0.5833], [0.2214, 0.5576], [0.2979, 0.5208], [0.3684, 0.4736]], 0.4), stoneMat);
  arch.add(extrude15);
  const keystone = new THREE.Mesh(extrude([[0.2235, 1.0362], [0.075, 1.0573], [-0.075, 1.0573], [-0.2235, 1.0362], [-0.1265, 0.5865], [-0.0424, 0.5985], [0.0424, 0.5985], [0.1265, 0.5865]], 0.44), stoneMat);
  keystone.name = 'keystone';
  arch.add(keystone);
  const extrude17 = new THREE.Mesh(extrude([[-0.2295, 0.9527], [-0.3617, 0.9108], [-0.4866, 0.8507], [-0.6018, 0.7735], [-0.3684, 0.4736], [-0.2979, 0.5208], [-0.2214, 0.5576], [-0.1405, 0.5833]], 0.4), stoneMat);
  arch.add(extrude17);
  const extrude18 = new THREE.Mesh(extrude([[-0.6202, 0.7588], [-0.7211, 0.6637], [-0.8075, 0.5553], [-0.8778, 0.4358], [-0.5374, 0.2668], [-0.4944, 0.34], [-0.4415, 0.4063], [-0.3797, 0.4646]], 0.4), stoneDarkMat);
  arch.add(extrude18);
  const extrude19 = new THREE.Mesh(extrude([[-0.888, 0.4146], [-0.9376, 0.2851], [-0.9685, 0.1499], [-0.9799, 0.0118], [-0.6, 0.0072], [-0.5929, 0.0918], [-0.574, 0.1746], [-0.5437, 0.2538]], 0.4), stoneMat);
  arch.add(extrude19);
  const tympanum = new THREE.Mesh(extrude([[0.6, 0], [0.5543, 0.2296], [0.4243, 0.4243], [0.2296, 0.5543], [0, 0.6], [-0.2296, 0.5543], [-0.4243, 0.4243], [-0.5543, 0.2296], [-0.6, 0], [-0.6, 0]], 0.16), tympanumMat);
  tympanum.name = 'tympanum';
  tympanum.position.set(0, 2.06, 0.02);
  root.add(tympanum);
  const lintel = new THREE.Mesh(new THREE.BoxGeometry(1.2, 0.1, 0.18), woodAltMat);
  lintel.name = 'lintel';
  lintel.position.set(0, 2.11, 0.03);
  root.add(lintel);
  const sigilRing = new THREE.Mesh(new THREE.TorusGeometry(0.17, 0.02, 4, 16), glowMat);
  sigilRing.name = 'sigilRing';
  sigilRing.position.set(0, 2.36, 0.105);
  root.add(sigilRing);
  const sigilKeyhole = new THREE.Mesh(extrude([[0.0318, -0.0318], [0.0441, -0.0088], [0.0416, 0.0172], [0.025, 0.0374], [0, 0.045], [-0.025, 0.0374], [-0.0416, 0.0172], [-0.0441, -0.0088], [-0.0318, -0.0318], [-0.0382, -0.108], [0.0383, -0.108]], 0.02), glowMat);
  sigilKeyhole.name = 'sigilKeyhole';
  sigilKeyhole.position.set(0, 2.39, 0.11);
  root.add(sigilKeyhole);
  const door = new THREE.Group();
  door.name = 'door';
  door.position.set(-0.59, 0.06, 0);
  root.add(door);
  const plank1 = new THREE.Mesh(boxGeo4, woodMat);
  plank1.name = 'plank1';
  plank1.position.set(0.118, 0.995, 0.05);
  door.add(plank1);
  const plank2 = new THREE.Mesh(boxGeo4, woodAltMat);
  plank2.name = 'plank2';
  plank2.position.set(0.354, 0.995, 0.05);
  door.add(plank2);
  const plank3 = new THREE.Mesh(boxGeo4, woodMat);
  plank3.name = 'plank3';
  plank3.position.set(0.59, 0.995, 0.05);
  door.add(plank3);
  const plank4 = new THREE.Mesh(boxGeo4, woodAltMat);
  plank4.name = 'plank4';
  plank4.position.set(0.826, 0.995, 0.05);
  door.add(plank4);
  const plank5 = new THREE.Mesh(boxGeo4, woodMat);
  plank5.name = 'plank5';
  plank5.position.set(1.062, 0.995, 0.05);
  door.add(plank5);
  const box30 = new THREE.Mesh(boxGeo5, ironMat);
  box30.position.set(0.57, 0.35, 0.11);
  door.add(box30);
  const hingeLow = new THREE.Mesh(cylinderGeo, ironMat);
  hingeLow.name = 'hingeLow';
  hingeLow.position.set(0, 0.35, 0);
  door.add(hingeLow);
  const cylinder32 = new THREE.Mesh(cylinderGeo2, brassMat);
  cylinder32.position.set(0.12, 0.35, 0.125);
  cylinder32.rotation.set(deg(90), 0, 0);
  door.add(cylinder32);
  const cylinder33 = new THREE.Mesh(cylinderGeo2, brassMat);
  cylinder33.position.set(0.296, 0.35, 0.125);
  cylinder33.rotation.set(deg(90), 0, 0);
  door.add(cylinder33);
  const cylinder34 = new THREE.Mesh(cylinderGeo2, brassMat);
  cylinder34.position.set(0.472, 0.35, 0.125);
  cylinder34.rotation.set(deg(90), 0, 0);
  door.add(cylinder34);
  const cylinder35 = new THREE.Mesh(cylinderGeo2, brassMat);
  cylinder35.position.set(0.648, 0.35, 0.125);
  cylinder35.rotation.set(deg(90), 0, 0);
  door.add(cylinder35);
  const cylinder36 = new THREE.Mesh(cylinderGeo2, brassMat);
  cylinder36.position.set(0.824, 0.35, 0.125);
  cylinder36.rotation.set(deg(90), 0, 0);
  door.add(cylinder36);
  const cylinder37 = new THREE.Mesh(cylinderGeo2, brassMat);
  cylinder37.position.set(1, 0.35, 0.125);
  cylinder37.rotation.set(deg(90), 0, 0);
  door.add(cylinder37);
  const box38 = new THREE.Mesh(boxGeo5, ironMat);
  box38.position.set(0.57, 1.6, 0.11);
  door.add(box38);
  const hingeHigh = new THREE.Mesh(cylinderGeo, ironMat);
  hingeHigh.name = 'hingeHigh';
  hingeHigh.position.set(0, 1.6, 0);
  door.add(hingeHigh);
  const cylinder40 = new THREE.Mesh(cylinderGeo2, brassMat);
  cylinder40.position.set(0.12, 1.6, 0.125);
  cylinder40.rotation.set(deg(90), 0, 0);
  door.add(cylinder40);
  const cylinder41 = new THREE.Mesh(cylinderGeo2, brassMat);
  cylinder41.position.set(0.296, 1.6, 0.125);
  cylinder41.rotation.set(deg(90), 0, 0);
  door.add(cylinder41);
  const cylinder42 = new THREE.Mesh(cylinderGeo2, brassMat);
  cylinder42.position.set(0.472, 1.6, 0.125);
  cylinder42.rotation.set(deg(90), 0, 0);
  door.add(cylinder42);
  const cylinder43 = new THREE.Mesh(cylinderGeo2, brassMat);
  cylinder43.position.set(0.648, 1.6, 0.125);
  cylinder43.rotation.set(deg(90), 0, 0);
  door.add(cylinder43);
  const cylinder44 = new THREE.Mesh(cylinderGeo2, brassMat);
  cylinder44.position.set(0.824, 1.6, 0.125);
  cylinder44.rotation.set(deg(90), 0, 0);
  door.add(cylinder44);
  const cylinder45 = new THREE.Mesh(cylinderGeo2, brassMat);
  cylinder45.position.set(1, 1.6, 0.125);
  cylinder45.rotation.set(deg(90), 0, 0);
  door.add(cylinder45);
  const box46 = new THREE.Mesh(new THREE.BoxGeometry(0.09, 1.4658, 0.016), ironMat);
  box46.position.set(0.59, 0.975, 0.108);
  box46.rotation.set(0, 0, deg(-41.9582));
  door.add(box46);
  const cylinder47 = new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.05, 0.02, 8), ironMat);
  cylinder47.position.set(0.98, 1.3, 0.11);
  cylinder47.rotation.set(deg(90), 0, 0);
  door.add(cylinder47);
  const ringPull = new THREE.Mesh(new THREE.TorusGeometry(0.08, 0.014, 4, 12), ironMat);
  ringPull.name = 'ringPull';
  ringPull.position.set(0.98, 1.22, 0.135);
  ringPull.rotation.set(deg(-12), 0, 0);
  door.add(ringPull);
  const hasp = new THREE.Mesh(new THREE.BoxGeometry(0.07, 0.2, 0.02), ironMat);
  hasp.name = 'hasp';
  hasp.position.set(0.98, 1.02, 0.112);
  door.add(hasp);
  const staple = new THREE.Mesh(new THREE.TorusGeometry(0.03, 0.01, 4, 8, Math.PI), ironMat);
  staple.name = 'staple';
  staple.position.set(0.98, 0.96, 0.125);
  staple.rotation.set(deg(90), 0, 0);
  door.add(staple);
  const padlock = new THREE.Group();
  padlock.name = 'padlock';
  padlock.position.set(0.98, 0.9, 0.135);
  door.add(padlock);
  const lockBody = new THREE.Mesh(new THREE.BoxGeometry(0.18, 0.15, 0.08), brassMat);
  lockBody.name = 'lockBody';
  lockBody.position.set(0, -0.075, 0);
  padlock.add(lockBody);
  const box53 = new THREE.Mesh(new THREE.BoxGeometry(0.19, 0.03, 0.085), brassMat);
  box53.position.set(0, -0.01, 0);
  padlock.add(box53);
  const keyholeGlow = new THREE.Mesh(extrude([[0.0127, -0.0127], [0.0177, -0.0035], [0.0166, 0.0069], [0.01, 0.015], [0, 0.018], [-0.01, 0.015], [-0.0166, 0.0069], [-0.0177, -0.0035], [-0.0127, -0.0127], [-0.0153, -0.0432], [0.0153, -0.0432]], 0.01), glowMat);
  keyholeGlow.name = 'keyholeGlow';
  keyholeGlow.position.set(0, -0.06, 0.042);
  padlock.add(keyholeGlow);
  const shackle = new THREE.Mesh(new THREE.TorusGeometry(0.06, 0.016, 5, 10, Math.PI), ironMat);
  shackle.name = 'shackle';
  padlock.add(shackle);
  const cylinder56 = new THREE.Mesh(cylinderGeo3, ironMat);
  cylinder56.position.set(-0.06, 0.01, 0);
  padlock.add(cylinder56);
  const cylinder57 = new THREE.Mesh(cylinderGeo3, ironMat);
  cylinder57.position.set(0.06, 0.01, 0);
  padlock.add(cylinder57);

  // Animations ('open') — play them with THREE.AnimationMixer
  root.animations = codelsClips(THREE, [
    { target: door, anim: [{ type: 'hinge', axis: 'y', angle: 95, duration: 1.55 }] },
  ]);

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

export default createLockedDoor;

Bewertungen & Kommentare

—
Noch keine Bewertungen — gib die erste ab.
5
0
4
0
3
0
2
0
1
0

Melde dich an, um zu bewerten und zu kommentieren.

0 Kommentare

Noch keine Kommentare. Erzähl, wie du dieses Modell genutzt hast oder was besser sein könnte.

Ähnliche Modelle

Gleiche Kategorie oder ähnliche Tags