Codels
1,5 тыс. треуг.2,45 × 1,88 × 7,22 m

Использовать в коде

Ремикс в редакторе
Самодостаточный ES-модуль — зависит только от three. Вызовите фабрику и добавьте группу в сцену.
/**
 * Speedboat — Codels 3D model library
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 1,512 · Size (W × H × D): 2.45 × 1.88 × 7.22 m
 *
 * Usage:
 *   import { createSpeedboat } from './speedboat.js';
 *   
 *   const model = createSpeedboat();
 *   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);
}

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

export function createSpeedboat() {
  const root = new THREE.Group();
  root.name = 'Speedboat';

  // Materials
  const hullMat = new THREE.MeshStandardMaterial({ name: 'hull', color: 0xeef1f3, roughness: 0.35, metalness: 0.1, flatShading: true });
  const accentMat = new THREE.MeshStandardMaterial({ name: 'accent', color: 0xd63f30, roughness: 0.4, metalness: 0.1, flatShading: true });
  const glassMat = new THREE.MeshStandardMaterial({ name: 'glass', color: 0x25313e, roughness: 0.2, metalness: 0.35 });
  const seatMat = new THREE.MeshStandardMaterial({ name: 'seat', color: 0xe5d6b8, roughness: 0.7, flatShading: true });
  const deckMat = new THREE.MeshStandardMaterial({ name: 'deck', color: 0xb98a5a, roughness: 0.85, flatShading: true });
  const darkMat = new THREE.MeshStandardMaterial({ name: 'dark', color: 0x2a2d33, roughness: 0.5, metalness: 0.35, flatShading: true });
  const metalMat = new THREE.MeshStandardMaterial({ name: 'metal', color: 0xc3c9d0, roughness: 0.3, metalness: 0.75, flatShading: true });
  const navRedMat = new THREE.MeshStandardMaterial({ name: 'navRed', color: 0xff4a3a, roughness: 0.4, emissive: 0xff2a1a, emissiveIntensity: 1.3, flatShading: true });
  const navGreenMat = new THREE.MeshStandardMaterial({ name: 'navGreen', color: 0x4dff7a, roughness: 0.4, emissive: 0x1fe05a, emissiveIntensity: 1.3, flatShading: true });

  // Shared geometries
  const boxGeo = new THREE.BoxGeometry(0.05, 0.05, 0.22);
  const boxGeo2 = new THREE.BoxGeometry(0.07, 0.06, 0.1);
  const boxGeo3 = new THREE.BoxGeometry(0.035, 0.36, 0.62);
  const boxGeo4 = new THREE.BoxGeometry(0.52, 0.32, 0.5);
  const boxGeo5 = new THREE.BoxGeometry(0.52, 0.55, 0.14);
  const boxGeo6 = new THREE.BoxGeometry(0.5, 0.08, 0.48);
  const cylinderGeo = new THREE.CylinderGeometry(0.018, 0.018, 0.26, 4);
  const boxGeo7 = new THREE.BoxGeometry(0.1, 0.17, 0.02);

  // Parts
  const hull = new THREE.Group();
  hull.name = 'hull';
  root.add(hull);
  const bottomHull = new THREE.Mesh(lathe([[0, -3], [1.12, -3], [1.2, -2], [1.2, -0.5], [1.12, 0.8], [0.92, 1.8], [0.6, 2.6], [0.25, 3.15], [0, 3.4]], 4, -Math.PI / 2, Math.PI), hullMat);
  bottomHull.name = 'bottomHull';
  bottomHull.position.set(0, 0.72, 0);
  bottomHull.rotation.set(deg(90), 0, 0);
  bottomHull.scale.set(1, 1, 0.6);
  hull.add(bottomHull);
  const topsides = new THREE.Mesh(extrude([[-1.12, -3], [-1.2, -2], [-1.2, -0.5], [-1.12, 0.8], [-0.92, 1.8], [-0.6, 2.6], [-0.25, 3.15], [0, 3.4], [0.25, 3.15], [0.6, 2.6], [0.92, 1.8], [1.12, 0.8], [1.2, -0.5], [1.2, -2], [1.12, -3]], 0.5, [[[-1.028, -2.9], [-1.1, -2], [-1.1, -0.5], [-1.02, 0.8], [-0.82, 1.8], [-0.5, 2.6], [-0.15, 3.15], [-0.08, 3.22], [0.08, 3.22], [0.15, 3.15], [0.5, 2.6], [0.82, 1.8], [1.02, 0.8], [1.1, -0.5], [1.1, -2], [1.028, -2.9]]]), hullMat);
  topsides.name = 'topsides';
  topsides.position.set(0, 0.97, 0);
  topsides.rotation.set(deg(90), 0, 0);
  hull.add(topsides);
  const cockpitFloor = new THREE.Mesh(extrude([[-1.028, -2.9], [-1.1, -2], [-1.1, -0.5], [-1.02, 0.8], [-0.82, 1.8], [-0.5, 2.6], [-0.15, 3.15], [-0.08, 3.22], [0.08, 3.22], [0.15, 3.15], [0.5, 2.6], [0.82, 1.8], [1.02, 0.8], [1.1, -0.5], [1.1, -2], [1.028, -2.9]], 0.06), deckMat);
  cockpitFloor.name = 'cockpitFloor';
  cockpitFloor.position.set(0, 0.75, 0);
  cockpitFloor.rotation.set(deg(90), 0, 0);
  hull.add(cockpitFloor);
  const bowDeck = new THREE.Mesh(extrude([[-1.1262, 0.7], [-1.12, 0.8], [-0.92, 1.8], [-0.6, 2.6], [-0.25, 3.15], [0, 3.4], [0.25, 3.15], [0.6, 2.6], [0.92, 1.8], [1.12, 0.8], [1.1262, 0.7]], 0.56), hullMat);
  bowDeck.name = 'bowDeck';
  bowDeck.position.set(0, 1, 0);
  bowDeck.rotation.set(deg(90), 0, 0);
  hull.add(bowDeck);
  const sunDeck = new THREE.Mesh(extrude([[-1.12, -3], [-1.164, -2.45], [1.164, -2.45], [1.12, -3]], 0.52), hullMat);
  sunDeck.name = 'sunDeck';
  sunDeck.position.set(0, 0.98, 0);
  sunDeck.rotation.set(deg(90), 0, 0);
  hull.add(sunDeck);
  const sunPad = new THREE.Mesh(extrude([[-1.0096, -2.88], [-1.0344, -2.57], [1.0344, -2.57], [1.0096, -2.88]], 0.08), seatMat);
  sunPad.name = 'sunPad';
  sunPad.position.set(0, 1.28, 0);
  sunPad.rotation.set(deg(90), 0, 0);
  hull.add(sunPad);
  const stripe = new THREE.Mesh(extrude([[-1.12, -3], [-1.2, -2], [-1.2, -0.5], [-1.12, 0.8], [-0.92, 1.8], [-0.6, 2.6], [-0.25, 3.15], [0, 3.4], [0.25, 3.15], [0.6, 2.6], [0.92, 1.8], [1.12, 0.8], [1.2, -0.5], [1.2, -2], [1.12, -3]], 0.2, [[[-1.028, -2.9], [-1.1, -2], [-1.1, -0.5], [-1.02, 0.8], [-0.82, 1.8], [-0.5, 2.6], [-0.15, 3.15], [-0.08, 3.22], [0.08, 3.22], [0.15, 3.15], [0.5, 2.6], [0.82, 1.8], [1.02, 0.8], [1.1, -0.5], [1.1, -2], [1.028, -2.9]]]), accentMat);
  stripe.name = 'stripe';
  stripe.position.set(0, 0.92, 0);
  stripe.rotation.set(deg(90), 0, 0);
  stripe.scale.set(1.02, 1.003, 1);
  hull.add(stripe);
  const pinstripe = new THREE.Mesh(extrude([[-1.12, -3], [-1.2, -2], [-1.2, -0.5], [-1.12, 0.8], [-0.92, 1.8], [-0.6, 2.6], [-0.25, 3.15], [0, 3.4], [0.25, 3.15], [0.6, 2.6], [0.92, 1.8], [1.12, 0.8], [1.2, -0.5], [1.2, -2], [1.12, -3]], 0.04, [[[-1.028, -2.9], [-1.1, -2], [-1.1, -0.5], [-1.02, 0.8], [-0.82, 1.8], [-0.5, 2.6], [-0.15, 3.15], [-0.08, 3.22], [0.08, 3.22], [0.15, 3.15], [0.5, 2.6], [0.82, 1.8], [1.02, 0.8], [1.1, -0.5], [1.1, -2], [1.028, -2.9]]]), darkMat);
  pinstripe.name = 'pinstripe';
  pinstripe.position.set(0, 1.09, 0);
  pinstripe.rotation.set(deg(90), 0, 0);
  pinstripe.scale.set(1.02, 1.003, 1);
  hull.add(pinstripe);
  const rubRail = new THREE.Mesh(extrude([[-1.12, -3], [-1.2, -2], [-1.2, -0.5], [-1.12, 0.8], [-0.92, 1.8], [-0.6, 2.6], [-0.25, 3.15], [0, 3.4], [0.25, 3.15], [0.6, 2.6], [0.92, 1.8], [1.12, 0.8], [1.2, -0.5], [1.2, -2], [1.12, -3]], 0.05, [[[-1.028, -2.9], [-1.1, -2], [-1.1, -0.5], [-1.02, 0.8], [-0.82, 1.8], [-0.5, 2.6], [-0.15, 3.15], [-0.08, 3.22], [0.08, 3.22], [0.15, 3.15], [0.5, 2.6], [0.82, 1.8], [1.02, 0.8], [1.1, -0.5], [1.1, -2], [1.028, -2.9]]]), darkMat);
  rubRail.name = 'rubRail';
  rubRail.position.set(0, 1.225, 0);
  rubRail.rotation.set(deg(90), 0, 0);
  rubRail.scale.set(1.012, 1.003, 1);
  hull.add(rubRail);
  const deckStripe = new THREE.Mesh(new THREE.BoxGeometry(0.22, 0.02, 2.1), accentMat);
  deckStripe.name = 'deckStripe';
  deckStripe.position.set(0, 1.29, 2);
  hull.add(deckStripe);
  const cleat = new THREE.Mesh(boxGeo, metalMat);
  cleat.name = 'cleat';
  cleat.position.set(0.85, 1.26, -2.75);
  hull.add(cleat);
  const cleat2 = new THREE.Mesh(boxGeo, metalMat);
  cleat2.name = 'cleat';
  cleat2.position.set(-0.85, 1.26, -2.75);
  hull.add(cleat2);
  const bowLightHousing = new THREE.Mesh(new THREE.BoxGeometry(0.16, 0.08, 0.14), darkMat);
  bowLightHousing.name = 'bowLightHousing';
  bowLightHousing.position.set(0, 1.32, 2.72);
  hull.add(bowLightHousing);
  const navLightPort = new THREE.Mesh(boxGeo2, navRedMat);
  navLightPort.name = 'navLightPort';
  navLightPort.position.set(0.045, 1.39, 2.72);
  hull.add(navLightPort);
  const navLightStarboard = new THREE.Mesh(boxGeo2, navGreenMat);
  navLightStarboard.name = 'navLightStarboard';
  navLightStarboard.position.set(-0.045, 1.39, 2.72);
  hull.add(navLightStarboard);
  const windshield = new THREE.Group();
  windshield.name = 'windshield';
  root.add(windshield);
  const frontGlass = new THREE.Mesh(new THREE.BoxGeometry(1.5, 0.42, 0.035), glassMat);
  frontGlass.name = 'frontGlass';
  frontGlass.position.set(0, 1.44, 0.6);
  frontGlass.rotation.set(deg(-32), 0, 0);
  windshield.add(frontGlass);
  const sideGlass = new THREE.Mesh(boxGeo3, glassMat);
  sideGlass.name = 'sideGlass';
  sideGlass.position.set(0.97, 1.42, 0.33);
  sideGlass.rotation.set(0, deg(24), 0);
  windshield.add(sideGlass);
  const sideGlass2 = new THREE.Mesh(boxGeo3, glassMat);
  sideGlass2.name = 'sideGlass';
  sideGlass2.position.set(-0.97, 1.42, 0.33);
  sideGlass2.rotation.set(0, deg(-24), 0);
  windshield.add(sideGlass2);
  const frame = new THREE.Mesh(new THREE.BoxGeometry(1.52, 0.04, 0.05), darkMat);
  frame.name = 'frame';
  frame.position.set(0, 1.62, 0.49);
  windshield.add(frame);
  const cockpit = new THREE.Group();
  cockpit.name = 'cockpit';
  root.add(cockpit);
  const console = new THREE.Mesh(new THREE.BoxGeometry(0.62, 0.55, 0.42), hullMat);
  console.name = 'console';
  console.position.set(-0.5, 1.055, 0.35);
  cockpit.add(console);
  const dash = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.03, 0.3), darkMat);
  dash.name = 'dash';
  dash.position.set(-0.5, 1.36, 0.32);
  dash.rotation.set(deg(-25), 0, 0);
  cockpit.add(dash);
  const steeringWheel = new THREE.Group();
  steeringWheel.name = 'steeringWheel';
  steeringWheel.position.set(-0.5, 1.4, 0.12);
  steeringWheel.rotation.set(deg(-35), 0, 0);
  cockpit.add(steeringWheel);
  const rim = new THREE.Mesh(new THREE.TorusGeometry(0.17, 0.025, 4, 12), darkMat);
  rim.name = 'rim';
  steeringWheel.add(rim);
  const box27 = new THREE.Mesh(new THREE.BoxGeometry(0.32, 0.03, 0.03), darkMat);
  steeringWheel.add(box27);
  const seatDriver = new THREE.Mesh(boxGeo4, seatMat);
  seatDriver.name = 'seatDriver';
  seatDriver.position.set(-0.5, 0.94, -0.45);
  cockpit.add(seatDriver);
  const backrestDriver = new THREE.Mesh(boxGeo5, seatMat);
  backrestDriver.name = 'backrestDriver';
  backrestDriver.position.set(-0.5, 1.33, -0.72);
  backrestDriver.rotation.set(deg(-12), 0, 0);
  cockpit.add(backrestDriver);
  const box30 = new THREE.Mesh(boxGeo6, accentMat);
  box30.position.set(-0.5, 1.14, -0.45);
  cockpit.add(box30);
  const seatPassenger = new THREE.Mesh(boxGeo4, seatMat);
  seatPassenger.name = 'seatPassenger';
  seatPassenger.position.set(0.5, 0.94, -0.45);
  cockpit.add(seatPassenger);
  const backrestPassenger = new THREE.Mesh(boxGeo5, seatMat);
  backrestPassenger.name = 'backrestPassenger';
  backrestPassenger.position.set(0.5, 1.33, -0.72);
  backrestPassenger.rotation.set(deg(-12), 0, 0);
  cockpit.add(backrestPassenger);
  const box33 = new THREE.Mesh(boxGeo6, accentMat);
  box33.position.set(0.5, 1.14, -0.45);
  cockpit.add(box33);
  const rearBench = new THREE.Mesh(new THREE.BoxGeometry(2, 0.36, 0.52), seatMat);
  rearBench.name = 'rearBench';
  rearBench.position.set(0, 0.96, -2.1);
  cockpit.add(rearBench);
  const box35 = new THREE.Mesh(new THREE.BoxGeometry(2, 0.08, 0.5), accentMat);
  box35.position.set(0, 1.18, -2.1);
  cockpit.add(box35);
  const bowRail = new THREE.Group();
  bowRail.name = 'bowRail';
  root.add(bowRail);
  const rail = new THREE.Mesh(tube([[-0.82, 1.52, 1], [-0.55, 1.52, 2.2], [-0.2, 1.52, 2.95], [0, 1.52, 3.1], [0.2, 1.52, 2.95], [0.55, 1.52, 2.2], [0.82, 1.52, 1]], 0.022, 16, 4), metalMat);
  rail.name = 'rail';
  bowRail.add(rail);
  const cylinder38 = new THREE.Mesh(cylinderGeo, metalMat);
  cylinder38.position.set(-0.82, 1.4, 1);
  bowRail.add(cylinder38);
  const cylinder39 = new THREE.Mesh(cylinderGeo, metalMat);
  cylinder39.position.set(-0.55, 1.4, 2.2);
  bowRail.add(cylinder39);
  const cylinder40 = new THREE.Mesh(cylinderGeo, metalMat);
  cylinder40.position.set(0, 1.4, 3.1);
  bowRail.add(cylinder40);
  const cylinder41 = new THREE.Mesh(cylinderGeo, metalMat);
  cylinder41.position.set(0.55, 1.4, 2.2);
  bowRail.add(cylinder41);
  const cylinder42 = new THREE.Mesh(cylinderGeo, metalMat);
  cylinder42.position.set(0.82, 1.4, 1);
  bowRail.add(cylinder42);
  const motor = new THREE.Group();
  motor.name = 'motor';
  motor.position.set(0, 1.24, -3.08);
  root.add(motor);
  const clampBracket = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.24, 0.18), darkMat);
  clampBracket.name = 'clampBracket';
  clampBracket.position.set(0, -0.06, 0.05);
  motor.add(clampBracket);
  const cowling = new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.5, 0.58), darkMat);
  cowling.name = 'cowling';
  cowling.position.set(0, 0.25, -0.34);
  motor.add(cowling);
  const cowlTop = new THREE.Mesh(new THREE.SphereGeometry(0.3, 8, 3, 0, Math.PI * 2, 0, Math.PI / 2), darkMat);
  cowlTop.name = 'cowlTop';
  cowlTop.position.set(0, 0.5, -0.34);
  cowlTop.scale.set(0.66, 0.45, 0.96);
  motor.add(cowlTop);
  const cowlStripe = new THREE.Mesh(new THREE.BoxGeometry(0.41, 0.07, 0.59), accentMat);
  cowlStripe.name = 'cowlStripe';
  cowlStripe.position.set(0, 0.36, -0.34);
  motor.add(cowlStripe);
  const leg = new THREE.Mesh(new THREE.BoxGeometry(0.16, 0.97, 0.26), darkMat);
  leg.name = 'leg';
  leg.position.set(0, -0.485, -0.3);
  motor.add(leg);
  const cavitationPlate = new THREE.Mesh(new THREE.BoxGeometry(0.38, 0.03, 0.5), darkMat);
  cavitationPlate.name = 'cavitationPlate';
  cavitationPlate.position.set(0, -0.77, -0.35);
  motor.add(cavitationPlate);
  const gearHousing = new THREE.Mesh(new THREE.CapsuleGeometry(0.09, 0.3, 3, 8), darkMat);
  gearHousing.name = 'gearHousing';
  gearHousing.position.set(0, -0.97, -0.33);
  gearHousing.rotation.set(deg(90), 0, 0);
  motor.add(gearHousing);
  const skeg = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.2, 0.2), darkMat);
  skeg.name = 'skeg';
  skeg.position.set(0, -1.14, -0.36);
  motor.add(skeg);
  const propeller = new THREE.Group();
  propeller.name = 'propeller';
  propeller.position.set(0, -0.97, -0.62);
  motor.add(propeller);
  const propCap = new THREE.Mesh(new THREE.ConeGeometry(0.06, 0.12, 6), metalMat);
  propCap.name = 'propCap';
  propCap.position.set(0, 0, -0.05);
  propCap.rotation.set(deg(-90), 0, 0);
  propeller.add(propCap);
  const blade1 = new THREE.Group();
  blade1.name = 'blade1';
  propeller.add(blade1);
  const box55 = new THREE.Mesh(boxGeo7, metalMat);
  box55.position.set(0, 0.11, 0);
  box55.rotation.set(0, deg(25), 0);
  blade1.add(box55);
  const blade2 = new THREE.Group();
  blade2.name = 'blade2';
  blade2.rotation.set(0, 0, deg(120));
  propeller.add(blade2);
  const box57 = new THREE.Mesh(boxGeo7, metalMat);
  box57.position.set(0, 0.11, 0);
  box57.rotation.set(0, deg(25), 0);
  blade2.add(box57);
  const blade3 = new THREE.Group();
  blade3.name = 'blade3';
  blade3.rotation.set(0, 0, deg(240));
  propeller.add(blade3);
  const box59 = new THREE.Mesh(boxGeo7, metalMat);
  box59.position.set(0, 0.11, 0);
  box59.rotation.set(0, deg(25), 0);
  blade3.add(box59);

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

export default createSpeedboat;

Оценки и комментарии

—
Оценок пока нет — будьте первым.
5
0
4
0
3
0
2
0
1
0

Войдите, чтобы ставить оценки и оставлять комментарии.

0 комментариев

Комментариев пока нет. Расскажите, как вы использовали эту модель или что можно улучшить.

Похожие модели

Та же категория или похожие теги