Codels
668 tris0.16 × 0.28 × 0.30 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.
/**
 * Duck — Codels 3D model library
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 668 · Size (W × H × D): 0.16 × 0.28 × 0.30 m
 *
 * Usage:
 *   import { createDuck } from './duck.js';
 *   
 *   const model = createDuck();
 *   model.position.set(0, 0, 0);
 *   scene.add(model);
 */
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);
}

export function createDuck() {
  const root = new THREE.Group();
  root.name = 'Duck';

  // Materials
  const downMat = new THREE.MeshStandardMaterial({ name: 'down', color: 0xffd23f, roughness: 0.9, flatShading: true });
  const whiteMat = new THREE.MeshStandardMaterial({ name: 'white', color: 0xf4f1ea, roughness: 0.7, flatShading: true });
  const billMat = new THREE.MeshStandardMaterial({ name: 'bill', color: 0xf39a2e, roughness: 0.55, flatShading: true });
  const feetMat = new THREE.MeshStandardMaterial({ name: 'feet', color: 0xf2842a, roughness: 0.6, flatShading: true });
  const eyeMat = new THREE.MeshStandardMaterial({ name: 'eye', color: 0x171314, roughness: 0.3, flatShading: true });

  // Shared geometries
  const cylinderGeo = new THREE.CylinderGeometry(0.017, 0.017, 0.09, 6);
  const extrudeGeo = extrude([[-0.012, 0], [0.012, 0], [0.05, 0.065], [0.018, 0.055], [0, 0.078], [-0.018, 0.055], [-0.05, 0.065]], 0.012);
  const sphereGeo = new THREE.SphereGeometry(0.1, 8, 5);
  const sphereGeo2 = new THREE.SphereGeometry(0.0221, 6, 4);
  const sphereGeo3 = new THREE.SphereGeometry(0.0078, 4, 3);

  // Parts
  const duck = new THREE.Group();
  duck.name = 'duck';
  duck.scale.setScalar(0.5);
  root.add(duck);
  const legL = new THREE.Group();
  legL.name = 'legL';
  legL.position.set(0.06, 0.09, 0);
  duck.add(legL);
  const shinL = new THREE.Mesh(cylinderGeo, feetMat);
  shinL.name = 'shinL';
  shinL.position.set(0, -0.045, 0);
  legL.add(shinL);
  const footL = new THREE.Mesh(extrudeGeo, feetMat);
  footL.name = 'footL';
  footL.position.set(0, -0.084, -0.012);
  footL.rotation.set(deg(90), 0, 0);
  legL.add(footL);
  const legR = new THREE.Group();
  legR.name = 'legR';
  legR.position.set(-0.06, 0.09, 0);
  duck.add(legR);
  const shinR = new THREE.Mesh(cylinderGeo, feetMat);
  shinR.name = 'shinR';
  shinR.position.set(0, -0.045, 0);
  legR.add(shinR);
  const footR = new THREE.Mesh(extrudeGeo, feetMat);
  footR.name = 'footR';
  footR.position.set(0, -0.084, -0.012);
  footR.rotation.set(deg(90), 0, 0);
  legR.add(footR);
  const body = new THREE.Group();
  body.name = 'body';
  duck.add(body);
  const torso = new THREE.Mesh(new THREE.SphereGeometry(0.17, 10, 7), downMat);
  torso.name = 'torso';
  torso.position.set(0, 0.1974, -0.01);
  torso.scale.set(0.85, 0.82, 1.12);
  body.add(torso);
  const tail = new THREE.Mesh(new THREE.ConeGeometry(0.075, 0.13, 6), downMat);
  tail.name = 'tail';
  tail.position.set(0, 0.2674, -0.19);
  tail.rotation.set(deg(-62), 0, 0);
  body.add(tail);
  const wingL = new THREE.Group();
  wingL.name = 'wingL';
  wingL.position.set(0.119, 0.2424, -0.015);
  duck.add(wingL);
  const wingPanelL = new THREE.Mesh(sphereGeo, downMat);
  wingPanelL.name = 'wingPanelL';
  wingPanelL.position.set(0.012, -0.045, -0.04);
  wingPanelL.rotation.set(deg(-12), 0, 0);
  wingPanelL.scale.set(0.28, 0.55, 1.05);
  wingL.add(wingPanelL);
  const wingR = new THREE.Group();
  wingR.name = 'wingR';
  wingR.position.set(-0.119, 0.2424, -0.015);
  duck.add(wingR);
  const wingPanelR = new THREE.Mesh(sphereGeo, downMat);
  wingPanelR.name = 'wingPanelR';
  wingPanelR.position.set(-0.012, -0.045, -0.04);
  wingPanelR.rotation.set(deg(-12), 0, 0);
  wingPanelR.scale.set(0.28, 0.55, 1.05);
  wingR.add(wingPanelR);
  const head = new THREE.Group();
  head.name = 'head';
  head.position.set(0, 0.2741, 0.11);
  duck.add(head);
  const neck = new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.065, 0.075, 8), downMat);
  neck.name = 'neck';
  neck.position.set(0, 0.0375, 0);
  head.add(neck);
  const skull = new THREE.Mesh(new THREE.SphereGeometry(0.13, 10, 7), downMat);
  skull.name = 'skull';
  skull.position.set(0, 0.1465, 0.02);
  head.add(skull);
  const bill = new THREE.Mesh(new THREE.SphereGeometry(0.0806, 8, 4), billMat);
  bill.name = 'bill';
  bill.position.set(0, 0.1244, 0.1474);
  bill.scale.set(1, 0.32, 1.15);
  head.add(bill);
  const eyeL = new THREE.Mesh(sphereGeo2, eyeMat);
  eyeL.name = 'eyeL';
  eyeL.position.set(0.0806, 0.1764, 0.1136);
  head.add(eyeL);
  const eyeShineL = new THREE.Mesh(sphereGeo3, whiteMat);
  eyeShineL.name = 'eyeShineL';
  eyeShineL.position.set(0.0903, 0.1855, 0.1266);
  head.add(eyeShineL);
  const eyeR = new THREE.Mesh(sphereGeo2, eyeMat);
  eyeR.name = 'eyeR';
  eyeR.position.set(-0.0806, 0.1764, 0.1136);
  head.add(eyeR);
  const eyeShineR = new THREE.Mesh(sphereGeo3, whiteMat);
  eyeShineR.name = 'eyeShineR';
  eyeShineR.position.set(-0.0903, 0.1855, 0.1266);
  head.add(eyeShineR);
  const tuft = new THREE.Mesh(new THREE.ConeGeometry(0.025, 0.05, 4), downMat);
  tuft.name = 'tuft';
  tuft.position.set(0, 0.27, 0.01);
  tuft.rotation.set(deg(-20), 0, 0);
  head.add(tuft);

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

export default createDuck;

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