668 tris0,16 × 0,28 × 0,30 m
Utiliser dans le code
Remixer dans l’éditeurduck.js · 158 lignes
Module ES autonome — ne dépend que de three. Appelez la fonction factory et ajoutez le groupe à votre scène.
/**
* 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;
Notes et commentaires
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









