Codels
668 tris0,26 × 0,36 × 0,50 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.
/**
 * Fish — Codels 3D model library
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the model (it floats — pickups, planets)
 * Triangles: 668 · Size (W × H × D): 0.26 × 0.36 × 0.50 m
 *
 * Usage:
 *   import { createFish } from './fish.js';
 *   
 *   const model = createFish();
 *   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 createFish() {
  const root = new THREE.Group();
  root.name = 'Fish';

  // Materials
  const bodyMat = new THREE.MeshStandardMaterial({ name: 'body', color: 0x3fa4e0, roughness: 0.55, flatShading: true });
  const bellyMat = new THREE.MeshStandardMaterial({ name: 'belly', color: 0xf3ead6, roughness: 0.6, flatShading: true });
  const finMat = new THREE.MeshStandardMaterial({ name: 'fin', color: 0xf2b13c, roughness: 0.6, flatShading: true });
  const eyeWhiteMat = new THREE.MeshStandardMaterial({ name: 'eyeWhite', color: 0xffffff, roughness: 0.3, flatShading: true });
  const pupilMat = new THREE.MeshStandardMaterial({ name: 'pupil', color: 0x161417, roughness: 0.2, flatShading: true });

  // Shared geometries
  const sphereGeo = new THREE.SphereGeometry(0.036, 8, 6);
  const sphereGeo2 = new THREE.SphereGeometry(0.02, 8, 6);
  const extrudeGeo = extrude([[0, 0.02], [0.07, 0.035], [0.08, -0.01], [0, -0.02]], 0.012);

  // Parts
  const fish = new THREE.Group();
  fish.name = 'fish';
  fish.position.set(0, -0.008, 0.07);
  root.add(fish);
  const body = new THREE.Group();
  body.name = 'body';
  fish.add(body);
  const bodyShell = new THREE.Mesh(lathe([[0, -0.2], [0.04, -0.18], [0.085, -0.11], [0.115, -0.02], [0.118, 0.04], [0.1, 0.11], [0.065, 0.165], [0, 0.195]], 10), bodyMat);
  bodyShell.name = 'bodyShell';
  bodyShell.rotation.set(deg(90), 0, 0);
  bodyShell.scale.set(0.62, 1, 1.12);
  body.add(bodyShell);
  const bellyShell = new THREE.Mesh(lathe([[0, -0.184], [0.0388, -0.1656], [0.0825, -0.1012], [0.1116, -0.0184], [0.1145, 0.0368], [0.0842, 0.1012], [0.0471, 0.1518], [0, 0.1794]], 10), bellyMat);
  bellyShell.name = 'bellyShell';
  bellyShell.position.set(0, -0.026, -0.006);
  bellyShell.rotation.set(deg(90), 0, 0);
  bellyShell.scale.set(0.5952, 1, 1.064);
  body.add(bellyShell);
  const eyeL = new THREE.Mesh(sphereGeo, eyeWhiteMat);
  eyeL.name = 'eyeL';
  eyeL.position.set(0.056, 0.03, 0.11);
  eyeL.scale.set(0.7, 1, 1);
  body.add(eyeL);
  const pupilL = new THREE.Mesh(sphereGeo2, pupilMat);
  pupilL.name = 'pupilL';
  pupilL.position.set(0.076, 0.032, 0.116);
  pupilL.scale.set(0.6, 1, 1);
  body.add(pupilL);
  const eyeR = new THREE.Mesh(sphereGeo, eyeWhiteMat);
  eyeR.name = 'eyeR';
  eyeR.position.set(-0.056, 0.03, 0.11);
  eyeR.scale.set(0.7, 1, 1);
  body.add(eyeR);
  const pupilR = new THREE.Mesh(sphereGeo2, pupilMat);
  pupilR.name = 'pupilR';
  pupilR.position.set(-0.076, 0.032, 0.116);
  pupilR.scale.set(0.6, 1, 1);
  body.add(pupilR);
  const tail = new THREE.Group();
  tail.name = 'tail';
  tail.position.set(0, 0, -0.17);
  fish.add(tail);
  const tailFin = new THREE.Mesh(extrude([[0, 0.03], [0.1, 0.12], [0.135, 0.11], [0.085, 0], [0.135, -0.11], [0.1, -0.12], [0, -0.03]], 0.022), finMat);
  tailFin.name = 'tailFin';
  tailFin.rotation.set(0, deg(90), 0);
  tail.add(tailFin);
  const dorsalFin = new THREE.Group();
  dorsalFin.name = 'dorsalFin';
  dorsalFin.position.set(0, 0.1088, -0.01);
  fish.add(dorsalFin);
  const dorsal = new THREE.Mesh(extrude([[-0.07, 0], [0.1, 0], [0.07, 0.07], [-0.01, 0.1]], 0.018), finMat);
  dorsal.name = 'dorsal';
  dorsal.rotation.set(0, deg(90), 0);
  dorsalFin.add(dorsal);
  const ventralFin = new THREE.Group();
  ventralFin.name = 'ventralFin';
  ventralFin.position.set(0, -0.1032, -0.04);
  fish.add(ventralFin);
  const ventral = new THREE.Mesh(extrude([[-0.03, 0], [0.06, 0], [0.06, -0.05]], 0.014), finMat);
  ventral.name = 'ventral';
  ventral.rotation.set(0, deg(90), 0);
  ventralFin.add(ventral);
  const finL = new THREE.Group();
  finL.name = 'finL';
  finL.position.set(0.0685, -0.035, 0.03);
  finL.rotation.set(0, deg(-40), deg(25));
  fish.add(finL);
  const pectoralL = new THREE.Mesh(extrudeGeo, finMat);
  pectoralL.name = 'pectoralL';
  pectoralL.rotation.set(0, deg(90), 0);
  finL.add(pectoralL);
  const finR = new THREE.Group();
  finR.name = 'finR';
  finR.position.set(-0.0685, -0.035, 0.03);
  finR.rotation.set(0, deg(40), deg(-25));
  fish.add(finR);
  const pectoralR = new THREE.Mesh(extrudeGeo, finMat);
  pectoralR.name = 'pectoralR';
  pectoralR.rotation.set(0, deg(90), 0);
  finR.add(pectoralR);

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

export default createFish;

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