Codels
1588 三角面23.8 × 15.9 × 34.5 m

在代码中使用

在编辑器中改编
自包含的 ES 模块,仅依赖 three。调用工厂函数,然后把返回的 group 添加到场景中。
/**
 * Space Shuttle — Codels 3D model library
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 1,588 · Size (W × H × D): 23.80 × 15.94 × 34.48 m
 *
 * Usage:
 *   import { createSpaceShuttle } from './space-shuttle.js';
 *   
 *   const model = createSpaceShuttle();
 *   model.position.set(0, 0, 0);
 *   scene.add(model);
 */
import * as THREE from 'three';

const deg = THREE.MathUtils.degToRad;

// Revolve a 2D profile [[radius, y], ...] around the Y axis.
function lathe(points, segments, phiStart = 0, phiLength = Math.PI * 2) {
  return new THREE.LatheGeometry(points.map(([x, y]) => new THREE.Vector2(x, y)), segments, phiStart, phiLength);
}

// 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);
}

export function createSpaceShuttle() {
  const root = new THREE.Group();
  root.name = 'SpaceShuttle';

  // Materials
  const whiteMat = new THREE.MeshStandardMaterial({ name: 'white', color: 0xebe9e3, roughness: 0.55, metalness: 0.05, flatShading: true });
  const tilesMat = new THREE.MeshStandardMaterial({ name: 'tiles', color: 0x26272b, roughness: 0.8, flatShading: true });
  const metalMat = new THREE.MeshStandardMaterial({ name: 'metal', color: 0x9aa0a8, roughness: 0.35, metalness: 0.7, flatShading: true });
  const darkMat = new THREE.MeshStandardMaterial({ name: 'dark', color: 0x1d1f23, roughness: 0.6, metalness: 0.3, flatShading: true });
  const glassMat = new THREE.MeshStandardMaterial({ name: 'glass', color: 0x25313e, roughness: 0.2, metalness: 0.35 });

  // Shared geometries
  const boxGeo = new THREE.BoxGeometry(0.05, 0.42, 0.9);
  const boxGeo2 = new THREE.BoxGeometry(0.05, 0.04, 17.5);
  const boxGeo3 = new THREE.BoxGeometry(5.6, 0.08, 0.06);
  const capsuleGeo = new THREE.CapsuleGeometry(1, 4.6, 3, 8);
  const cylinderGeo = new THREE.CylinderGeometry(0.42, 0.25, 0.7, 8);
  const cylinderGeo2 = new THREE.CylinderGeometry(1.12, 0.5, 2.8, 12);
  const cylinderGeo3 = new THREE.CylinderGeometry(1.04, 1.04, 0.04, 12);
  const cylinderGeo4 = new THREE.CylinderGeometry(0.45, 0.45, 0.36, 12);
  const cylinderGeo5 = new THREE.CylinderGeometry(0.225, 0.225, 0.38, 8);
  const cylinderGeo6 = new THREE.CylinderGeometry(0.16, 0.2, 2.1, 8);
  const boxGeo4 = new THREE.BoxGeometry(0.08, 1.47, 1.4);
  const cylinderGeo7 = new THREE.CylinderGeometry(0.6, 0.6, 0.36, 12);
  const cylinderGeo8 = new THREE.CylinderGeometry(0.3, 0.3, 0.38, 8);

  // Parts
  const orbiter = new THREE.Group();
  orbiter.name = 'orbiter';
  orbiter.position.set(0, 5, 0);
  root.add(orbiter);
  const fuselageTop = new THREE.Mesh(lathe([[0, -14], [2.45, -14], [2.8, -13.3], [2.8, 9]], 5, 1.1781, Math.PI * 1.25), whiteMat);
  fuselageTop.name = 'fuselageTop';
  fuselageTop.rotation.set(deg(90), 0, 0);
  orbiter.add(fuselageTop);
  const fuselageBelly = new THREE.Mesh(lathe([[0, -14], [2.45, -14], [2.8, -13.3], [2.8, 9]], 3, -1.1781, Math.PI * 0.75), tilesMat);
  fuselageBelly.name = 'fuselageBelly';
  fuselageBelly.rotation.set(deg(90), 0, 0);
  orbiter.add(fuselageBelly);
  const noseSection = new THREE.Group();
  noseSection.name = 'noseSection';
  noseSection.position.set(0, 2.5869, 9);
  noseSection.rotation.set(deg(6), 0, 0);
  orbiter.add(noseSection);
  const noseShape = new THREE.Group();
  noseShape.name = 'noseShape';
  noseShape.position.set(0, -2.5869, -9);
  noseSection.add(noseShape);
  const noseTop = new THREE.Mesh(lathe([[2.8, 9], [2.8, 11], [2.6, 12.8], [2.15, 14.4], [1.55, 15.9], [0.95, 17]], 5, 1.1781, Math.PI * 1.25), whiteMat);
  noseTop.name = 'noseTop';
  noseTop.rotation.set(deg(90), 0, 0);
  noseShape.add(noseTop);
  const noseBelly = new THREE.Mesh(lathe([[2.8, 9], [2.8, 11], [2.6, 12.8], [2.15, 14.4], [1.55, 15.9], [0.95, 17]], 3, -1.1781, Math.PI * 0.75), tilesMat);
  noseBelly.name = 'noseBelly';
  noseBelly.rotation.set(deg(90), 0, 0);
  noseShape.add(noseBelly);
  const noseCap = new THREE.Mesh(lathe([[0.95, 17], [0.45, 17.7], [0, 18]], 8, 0.3927, Math.PI * 2), tilesMat);
  noseCap.name = 'noseCap';
  noseCap.rotation.set(deg(90), 0, 0);
  noseShape.add(noseCap);
  const cockpit = new THREE.Mesh(extrude([[11.2, 2.4369], [14.5, 1.85], [13.25, 2.82], [11.2, 2.95]], 2.6), whiteMat);
  cockpit.name = 'cockpit';
  cockpit.rotation.set(0, deg(-90), 0);
  noseShape.add(cockpit);
  const windshield = new THREE.Mesh(new THREE.BoxGeometry(2.3, 0.05, 1.45), glassMat);
  windshield.name = 'windshield';
  windshield.position.set(0, 2.36, 13.89);
  windshield.rotation.set(deg(38), 0, 0);
  noseShape.add(windshield);
  const sideWindow = new THREE.Mesh(boxGeo, glassMat);
  sideWindow.name = 'sideWindow';
  sideWindow.position.set(1.31, 2.6, 12.6);
  noseShape.add(sideWindow);
  const sideWindow2 = new THREE.Mesh(boxGeo, glassMat);
  sideWindow2.name = 'sideWindow';
  sideWindow2.position.set(-1.31, 2.6, 12.6);
  noseShape.add(sideWindow2);
  const payloadSeam = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.04, 17.5), darkMat);
  payloadSeam.name = 'payloadSeam';
  payloadSeam.position.set(0, 2.5969, -3.6);
  orbiter.add(payloadSeam);
  const doorHinge = new THREE.Mesh(boxGeo2, darkMat);
  doorHinge.name = 'doorHinge';
  doorHinge.position.set(1.05, 2.5919, -3.6);
  orbiter.add(doorHinge);
  const doorHinge2 = new THREE.Mesh(boxGeo2, darkMat);
  doorHinge2.name = 'doorHinge';
  doorHinge2.position.set(-1.05, 2.5919, -3.6);
  orbiter.add(doorHinge2);
  const wingL = new THREE.Group();
  wingL.name = 'wingL';
  wingL.position.set(0, -2, 0);
  orbiter.add(wingL);
  const wingTop = new THREE.Mesh(extrude([[2, 6], [2.6, 6], [5, -1.5], [11.9, -10.6], [11.9, -12.4], [2, -13.6]], 0.28), whiteMat);
  wingTop.name = 'wingTop';
  wingTop.position.set(0, 0.14, 0);
  wingTop.rotation.set(deg(90), 0, 0);
  wingL.add(wingTop);
  const wingBelly = new THREE.Mesh(extrude([[2, 6], [2.6, 6], [5, -1.5], [11.9, -10.6], [11.9, -12.4], [2, -13.6]], 0.3), tilesMat);
  wingBelly.name = 'wingBelly';
  wingBelly.position.set(0, -0.15, 0);
  wingBelly.rotation.set(deg(90), 0, 0);
  wingL.add(wingBelly);
  const elevonLine = new THREE.Mesh(boxGeo3, darkMat);
  elevonLine.name = 'elevonLine';
  elevonLine.position.set(7, 0.29, -12);
  elevonLine.rotation.set(0, deg(7), 0);
  wingL.add(elevonLine);
  const wingR = new THREE.Group();
  wingR.name = 'wingR';
  wingR.position.set(0, -2, 0);
  orbiter.add(wingR);
  const wingTop2 = new THREE.Mesh(extrude([[-2, 6], [-2.6, 6], [-5, -1.5], [-11.9, -10.6], [-11.9, -12.4], [-2, -13.6]], 0.28), whiteMat);
  wingTop2.name = 'wingTop';
  wingTop2.position.set(0, 0.14, 0);
  wingTop2.rotation.set(deg(90), 0, 0);
  wingR.add(wingTop2);
  const wingBelly2 = new THREE.Mesh(extrude([[-2, 6], [-2.6, 6], [-5, -1.5], [-11.9, -10.6], [-11.9, -12.4], [-2, -13.6]], 0.3), tilesMat);
  wingBelly2.name = 'wingBelly';
  wingBelly2.position.set(0, -0.15, 0);
  wingBelly2.rotation.set(deg(90), 0, 0);
  wingR.add(wingBelly2);
  const elevonLine2 = new THREE.Mesh(boxGeo3, darkMat);
  elevonLine2.name = 'elevonLine';
  elevonLine2.position.set(-7, 0.29, -12);
  elevonLine2.rotation.set(0, deg(-7), 0);
  wingR.add(elevonLine2);
  const tailFin = new THREE.Mesh(extrude([[-5.6, 2.3869], [-11.4, 10.8869], [-13.3, 10.8869], [-14.1, 2.3869]], 0.45, [], [0.05, 0.05, 1]), whiteMat);
  tailFin.name = 'tailFin';
  tailFin.rotation.set(0, deg(-90), 0);
  orbiter.add(tailFin);
  const rudderLine = new THREE.Mesh(new THREE.BoxGeometry(0.6, 6.8, 0.08), darkMat);
  rudderLine.name = 'rudderLine';
  rudderLine.position.set(0, 7.1869, -12.25);
  rudderLine.rotation.set(deg(-7), 0, 0);
  orbiter.add(rudderLine);
  const omsPodL = new THREE.Mesh(capsuleGeo, whiteMat);
  omsPodL.name = 'omsPodL';
  omsPodL.position.set(1.75, 2.15, -10.4);
  omsPodL.rotation.set(deg(90), 0, 0);
  omsPodL.scale.set(1, 1, 0.85);
  orbiter.add(omsPodL);
  const omsNozzle = new THREE.Mesh(cylinderGeo, metalMat);
  omsNozzle.name = 'omsNozzle';
  omsNozzle.position.set(1.75, 2.15, -13.65);
  omsNozzle.rotation.set(deg(-90), 0, 0);
  orbiter.add(omsNozzle);
  const omsPodR = new THREE.Mesh(capsuleGeo, whiteMat);
  omsPodR.name = 'omsPodR';
  omsPodR.position.set(-1.75, 2.15, -10.4);
  omsPodR.rotation.set(deg(90), 0, 0);
  omsPodR.scale.set(1, 1, 0.85);
  orbiter.add(omsPodR);
  const omsNozzle2 = new THREE.Mesh(cylinderGeo, metalMat);
  omsNozzle2.name = 'omsNozzle';
  omsNozzle2.position.set(-1.75, 2.15, -13.65);
  omsNozzle2.rotation.set(deg(-90), 0, 0);
  orbiter.add(omsNozzle2);
  const bodyFlap = new THREE.Mesh(new THREE.BoxGeometry(4.6, 0.32, 2), tilesMat);
  bodyFlap.name = 'bodyFlap';
  bodyFlap.position.set(0, -2.35, -14.8);
  orbiter.add(bodyFlap);
  const mainEngineTop = new THREE.Group();
  mainEngineTop.name = 'mainEngineTop';
  mainEngineTop.position.set(0, 1.25, -14);
  orbiter.add(mainEngineTop);
  const bell = new THREE.Mesh(cylinderGeo2, metalMat);
  bell.name = 'bell';
  bell.position.set(0, 0, -1.4);
  bell.rotation.set(deg(-90), 0, 0);
  mainEngineTop.add(bell);
  const nozzleExit = new THREE.Mesh(cylinderGeo3, darkMat);
  nozzleExit.name = 'nozzleExit';
  nozzleExit.position.set(0, 0, -2.78);
  nozzleExit.rotation.set(deg(90), 0, 0);
  mainEngineTop.add(nozzleExit);
  const mainEngineL = new THREE.Group();
  mainEngineL.name = 'mainEngineL';
  mainEngineL.position.set(1.3, -0.95, -14);
  orbiter.add(mainEngineL);
  const bell2 = new THREE.Mesh(cylinderGeo2, metalMat);
  bell2.name = 'bell';
  bell2.position.set(0, 0, -1.4);
  bell2.rotation.set(deg(-90), 0, 0);
  mainEngineL.add(bell2);
  const nozzleExit2 = new THREE.Mesh(cylinderGeo3, darkMat);
  nozzleExit2.name = 'nozzleExit';
  nozzleExit2.position.set(0, 0, -2.78);
  nozzleExit2.rotation.set(deg(90), 0, 0);
  mainEngineL.add(nozzleExit2);
  const mainEngineR = new THREE.Group();
  mainEngineR.name = 'mainEngineR';
  mainEngineR.position.set(-1.3, -0.95, -14);
  orbiter.add(mainEngineR);
  const bell3 = new THREE.Mesh(cylinderGeo2, metalMat);
  bell3.name = 'bell';
  bell3.position.set(0, 0, -1.4);
  bell3.rotation.set(deg(-90), 0, 0);
  mainEngineR.add(bell3);
  const nozzleExit3 = new THREE.Mesh(cylinderGeo3, darkMat);
  nozzleExit3.name = 'nozzleExit';
  nozzleExit3.position.set(0, 0, -2.78);
  nozzleExit3.rotation.set(deg(90), 0, 0);
  mainEngineR.add(nozzleExit3);
  const noseGear = new THREE.Group();
  noseGear.name = 'noseGear';
  noseGear.position.set(0, -2.5869, 12.6);
  orbiter.add(noseGear);
  const strut = new THREE.Mesh(new THREE.CylinderGeometry(0.16, 0.2, 1.9631, 8), metalMat);
  strut.name = 'strut';
  strut.position.set(0, -0.9816, 0);
  noseGear.add(strut);
  const gearDoor = new THREE.Mesh(new THREE.BoxGeometry(0.08, 1.3742, 1.4), tilesMat);
  gearDoor.name = 'gearDoor';
  gearDoor.position.set(0.35, -0.8834, 0.1);
  noseGear.add(gearDoor);
  const wheels = new THREE.Group();
  wheels.name = 'wheels';
  wheels.position.set(0, -1.9631, 0);
  noseGear.add(wheels);
  const tire = new THREE.Mesh(cylinderGeo4, darkMat);
  tire.name = 'tire';
  tire.position.set(-0.32, 0, 0);
  tire.rotation.set(0, 0, deg(90));
  wheels.add(tire);
  const hub = new THREE.Mesh(cylinderGeo5, metalMat);
  hub.name = 'hub';
  hub.position.set(-0.32, 0, 0);
  hub.rotation.set(0, 0, deg(90));
  wheels.add(hub);
  const tire2 = new THREE.Mesh(cylinderGeo4, darkMat);
  tire2.name = 'tire';
  tire2.position.set(0.32, 0, 0);
  tire2.rotation.set(0, 0, deg(90));
  wheels.add(tire2);
  const hub2 = new THREE.Mesh(cylinderGeo5, metalMat);
  hub2.name = 'hub';
  hub2.position.set(0.32, 0, 0);
  hub2.rotation.set(0, 0, deg(90));
  wheels.add(hub2);
  const mainGearL = new THREE.Group();
  mainGearL.name = 'mainGearL';
  mainGearL.position.set(3.3, -2.3, -4.6);
  orbiter.add(mainGearL);
  const strut2 = new THREE.Mesh(cylinderGeo6, metalMat);
  strut2.name = 'strut';
  strut2.position.set(0, -1.05, 0);
  mainGearL.add(strut2);
  const gearDoor2 = new THREE.Mesh(boxGeo4, tilesMat);
  gearDoor2.name = 'gearDoor';
  gearDoor2.position.set(0.45, -0.945, 0.1);
  mainGearL.add(gearDoor2);
  const wheels2 = new THREE.Group();
  wheels2.name = 'wheels';
  wheels2.position.set(0, -2.1, 0);
  mainGearL.add(wheels2);
  const tire3 = new THREE.Mesh(cylinderGeo7, darkMat);
  tire3.name = 'tire';
  tire3.position.set(-0.32, 0, 0);
  tire3.rotation.set(0, 0, deg(90));
  wheels2.add(tire3);
  const hub3 = new THREE.Mesh(cylinderGeo8, metalMat);
  hub3.name = 'hub';
  hub3.position.set(-0.32, 0, 0);
  hub3.rotation.set(0, 0, deg(90));
  wheels2.add(hub3);
  const tire4 = new THREE.Mesh(cylinderGeo7, darkMat);
  tire4.name = 'tire';
  tire4.position.set(0.32, 0, 0);
  tire4.rotation.set(0, 0, deg(90));
  wheels2.add(tire4);
  const hub4 = new THREE.Mesh(cylinderGeo8, metalMat);
  hub4.name = 'hub';
  hub4.position.set(0.32, 0, 0);
  hub4.rotation.set(0, 0, deg(90));
  wheels2.add(hub4);
  const mainGearR = new THREE.Group();
  mainGearR.name = 'mainGearR';
  mainGearR.position.set(-3.3, -2.3, -4.6);
  orbiter.add(mainGearR);
  const strut3 = new THREE.Mesh(cylinderGeo6, metalMat);
  strut3.name = 'strut';
  strut3.position.set(0, -1.05, 0);
  mainGearR.add(strut3);
  const gearDoor3 = new THREE.Mesh(boxGeo4, tilesMat);
  gearDoor3.name = 'gearDoor';
  gearDoor3.position.set(-0.45, -0.945, 0.1);
  mainGearR.add(gearDoor3);
  const wheels3 = new THREE.Group();
  wheels3.name = 'wheels';
  wheels3.position.set(0, -2.1, 0);
  mainGearR.add(wheels3);
  const tire5 = new THREE.Mesh(cylinderGeo7, darkMat);
  tire5.name = 'tire';
  tire5.position.set(-0.32, 0, 0);
  tire5.rotation.set(0, 0, deg(90));
  wheels3.add(tire5);
  const hub5 = new THREE.Mesh(cylinderGeo8, metalMat);
  hub5.name = 'hub';
  hub5.position.set(-0.32, 0, 0);
  hub5.rotation.set(0, 0, deg(90));
  wheels3.add(hub5);
  const tire6 = new THREE.Mesh(cylinderGeo7, darkMat);
  tire6.name = 'tire';
  tire6.position.set(0.32, 0, 0);
  tire6.rotation.set(0, 0, deg(90));
  wheels3.add(tire6);
  const hub6 = new THREE.Mesh(cylinderGeo8, metalMat);
  hub6.name = 'hub';
  hub6.position.set(0.32, 0, 0);
  hub6.rotation.set(0, 0, deg(90));
  wheels3.add(hub6);

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

export default createSpaceShuttle;

评分与评论

—
暂无评分,快来抢先评分吧。
5
0
4
0
3
0
2
0
1
0

登录后即可评分和评论。

0 条评论

暂无评论。分享一下你是如何使用这个模型的,或者有哪些可以改进的地方。

相关模型

同一分类或相似标签