Codels
1.1K tris0.56 × 0.56 × 0.51 m

Use it in code

Remix in the editor
Self-contained ES module — only depends on three. Call the factory and add the group to your scene.
/**
 * Penguin — Codels 3D model library
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 1,072 · Size (W × H × D): 0.56 × 0.56 × 0.51 m
 *
 * Usage:
 *   import { createPenguin } from './penguin.js';
 *   
 *   const model = createPenguin();
 *   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);
}

export function createPenguin() {
  const root = new THREE.Group();
  root.name = 'Penguin';

  // Materials
  const backMat = new THREE.MeshStandardMaterial({ name: 'back', color: 0x2c3550, roughness: 0.6, flatShading: true });
  const bellyMat = new THREE.MeshStandardMaterial({ name: 'belly', color: 0xf5f3ee, roughness: 0.7, flatShading: true });
  const beakMat = new THREE.MeshStandardMaterial({ name: 'beak', color: 0xf5a131, roughness: 0.5, flatShading: true });
  const eyeMat = new THREE.MeshStandardMaterial({ name: 'eye', color: 0x151318, roughness: 0.25, flatShading: true });
  const shineMat = new THREE.MeshStandardMaterial({ name: 'shine', color: 0xffffff, roughness: 0.3, flatShading: true });
  const blushMat = new THREE.MeshStandardMaterial({ name: 'blush', color: 0xf2a2a8, roughness: 0.7, flatShading: true });
  const accentMat = new THREE.MeshStandardMaterial({ name: 'accent', color: 0xe2473f, roughness: 0.9, flatShading: true });
  const accent2Mat = new THREE.MeshStandardMaterial({ name: 'accent2', color: 0xf6f2ea, roughness: 0.95, flatShading: true });

  // Shared geometries
  const sphereGeo = new THREE.SphereGeometry(0.06, 8, 4);
  const latheGeo = lathe([[0, 0], [0.13, 0.012], [0.195, 0.07], [0.215, 0.17], [0.205, 0.28], [0.175, 0.38], [0.13, 0.46], [0.065, 0.51], [0, 0.525]], 12);
  const sphereGeo2 = new THREE.SphereGeometry(0.03, 6, 6);
  const sphereGeo3 = new THREE.SphereGeometry(0.009, 4, 3);
  const sphereGeo4 = new THREE.SphereGeometry(0.028, 6, 4);
  const sphereGeo5 = new THREE.SphereGeometry(0.1, 8, 5);

  // Parts
  const footL = new THREE.Group();
  footL.name = 'footL';
  footL.position.set(0.08, 0.03, 0.06);
  root.add(footL);
  const footPadL = new THREE.Mesh(sphereGeo, beakMat);
  footPadL.name = 'footPadL';
  footPadL.position.set(0, 0, 0.05);
  footPadL.scale.set(0.9, 0.5, 1.3);
  footL.add(footPadL);
  const footR = new THREE.Group();
  footR.name = 'footR';
  footR.position.set(-0.08, 0.03, 0.06);
  root.add(footR);
  const footPadR = new THREE.Mesh(sphereGeo, beakMat);
  footPadR.name = 'footPadR';
  footPadR.position.set(0, 0, 0.05);
  footPadR.scale.set(0.9, 0.5, 1.3);
  footR.add(footPadR);
  const body = new THREE.Group();
  body.name = 'body';
  root.add(body);
  const torso = new THREE.Mesh(latheGeo, backMat);
  torso.name = 'torso';
  torso.position.set(0, 0.03, 0);
  body.add(torso);
  const belly = new THREE.Mesh(latheGeo, bellyMat);
  belly.name = 'belly';
  belly.position.set(0, 0.03, 0.042);
  belly.scale.set(0.8, 0.86, 1);
  body.add(belly);
  const crownPeak = new THREE.Mesh(new THREE.SphereGeometry(0.06, 6, 4), backMat);
  crownPeak.name = 'crownPeak';
  crownPeak.position.set(0, 0.46, 0.0901);
  crownPeak.scale.set(1, 1.1, 0.8);
  body.add(crownPeak);
  const face = new THREE.Group();
  face.name = 'face';
  root.add(face);
  const eyeL = new THREE.Mesh(sphereGeo2, eyeMat);
  eyeL.name = 'eyeL';
  eyeL.position.set(0.058, 0.39, 0.1733);
  eyeL.rotation.set(0, deg(18), 0);
  eyeL.scale.set(0.85, 1.15, 0.5);
  face.add(eyeL);
  const eyeShineL = new THREE.Mesh(sphereGeo3, shineMat);
  eyeShineL.name = 'eyeShineL';
  eyeShineL.position.set(0.062, 0.406, 0.1863);
  face.add(eyeShineL);
  const cheekL = new THREE.Mesh(sphereGeo4, blushMat);
  cheekL.name = 'cheekL';
  cheekL.position.set(0.088, 0.358, 0.1749);
  cheekL.rotation.set(0, deg(38), 0);
  cheekL.scale.set(1, 0.55, 0.4);
  face.add(cheekL);
  const eyeR = new THREE.Mesh(sphereGeo2, eyeMat);
  eyeR.name = 'eyeR';
  eyeR.position.set(-0.058, 0.39, 0.1733);
  eyeR.rotation.set(0, deg(-18), 0);
  eyeR.scale.set(0.85, 1.15, 0.5);
  face.add(eyeR);
  const eyeShineR = new THREE.Mesh(sphereGeo3, shineMat);
  eyeShineR.name = 'eyeShineR';
  eyeShineR.position.set(-0.062, 0.406, 0.1863);
  face.add(eyeShineR);
  const cheekR = new THREE.Mesh(sphereGeo4, blushMat);
  cheekR.name = 'cheekR';
  cheekR.position.set(-0.088, 0.358, 0.1749);
  cheekR.rotation.set(0, deg(-38), 0);
  cheekR.scale.set(1, 0.55, 0.4);
  face.add(cheekR);
  const beak = new THREE.Mesh(new THREE.ConeGeometry(0.042, 0.085, 6), beakMat);
  beak.name = 'beak';
  beak.position.set(0, 0.345, 0.2411);
  beak.rotation.set(deg(90), 0, 0);
  beak.scale.set(1.25, 1, 0.7);
  face.add(beak);
  const flipperL = new THREE.Group();
  flipperL.name = 'flipperL';
  flipperL.position.set(0.189, 0.33, -0.01);
  flipperL.rotation.set(0, 0, deg(22));
  root.add(flipperL);
  const flipperPanelL = new THREE.Mesh(sphereGeo5, backMat);
  flipperPanelL.name = 'flipperPanelL';
  flipperPanelL.position.set(0.01, -0.09, 0);
  flipperPanelL.scale.set(0.28, 1, 0.55);
  flipperL.add(flipperPanelL);
  const flipperR = new THREE.Group();
  flipperR.name = 'flipperR';
  flipperR.position.set(-0.189, 0.33, -0.01);
  flipperR.rotation.set(0, 0, deg(-22));
  root.add(flipperR);
  const flipperPanelR = new THREE.Mesh(sphereGeo5, backMat);
  flipperPanelR.name = 'flipperPanelR';
  flipperPanelR.position.set(-0.01, -0.09, 0);
  flipperPanelR.scale.set(0.28, 1, 0.55);
  flipperR.add(flipperPanelR);
  const scarf = new THREE.Group();
  scarf.name = 'scarf';
  scarf.position.set(0, 0.28, 0);
  root.add(scarf);
  const scarfWrap = new THREE.Mesh(new THREE.TorusGeometry(0.2037, 0.03, 6, 14), accentMat);
  scarfWrap.name = 'scarfWrap';
  scarfWrap.position.set(0, 0, 0.02);
  scarfWrap.rotation.set(deg(90), 0, 0);
  scarfWrap.scale.set(1, 1.1, 1);
  scarf.add(scarfWrap);
  const scarfTail = new THREE.Group();
  scarfTail.name = 'scarfTail';
  scarfTail.position.set(0.1, -0.01, 0.2028);
  scarfTail.rotation.set(deg(10), deg(28), deg(8));
  scarf.add(scarfTail);
  const scarfEnd = new THREE.Mesh(new THREE.BoxGeometry(0.065, 0.15, 0.026), accentMat);
  scarfEnd.name = 'scarfEnd';
  scarfEnd.position.set(0, -0.075, 0);
  scarfTail.add(scarfEnd);
  const scarfStripe = new THREE.Mesh(new THREE.BoxGeometry(0.067, 0.022, 0.028), accent2Mat);
  scarfStripe.name = 'scarfStripe';
  scarfStripe.position.set(0, -0.12, 0);
  scarfTail.add(scarfStripe);

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

export default createPenguin;

Ratings & comments

—
No ratings yet — be the first.
5
0
4
0
3
0
2
0
1
0

Sign in to rate and comment.

0 comments

No comments yet. Share how you used this model or what could be better.

Related models

Same category or similar tags