Codels
1.6K tris0.85 × 0.84 × 0.67 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.
/**
 * Ghost — Codels 3D model library
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 1,596 · Size (W × H × D): 0.85 × 0.83 × 0.67 m
 *
 * Usage:
 *   import { createGhost } from './ghost.js';
 *   
 *   const model = createGhost();
 *   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 createGhost() {
  const root = new THREE.Group();
  root.name = 'Ghost';

  // Materials
  const sheetMat = new THREE.MeshStandardMaterial({ name: 'sheet', color: 0xeef1fb, roughness: 0.55, emissive: 0x9fc2ff, emissiveIntensity: 0.22, flatShading: true });
  const faceMat = new THREE.MeshStandardMaterial({ name: 'face', color: 0x1d1a26, roughness: 0.3, flatShading: true });
  const mouthMat = new THREE.MeshStandardMaterial({ name: 'mouth', color: 0x2d1830, roughness: 0.6, flatShading: true });
  const blushMat = new THREE.MeshStandardMaterial({ name: 'blush', color: 0xf5a3b8, roughness: 0.7, flatShading: true });
  const shineMat = new THREE.MeshStandardMaterial({ name: 'shine', color: 0xffffff, roughness: 0.3, flatShading: true });

  // Shared geometries
  const latheGeo = lathe([[0, 0], [0.03, 0.01], [0.055, 0.035], [0.075, 0.08], [0.088, 0.16], [0.06, 0.19], [0, 0.2]], 7);
  const latheGeo2 = lathe([[0, 0], [0.03, 0.01], [0.055, 0.035], [0.075, 0.08], [0.088, 0.125], [0.06, 0.155], [0, 0.165]], 7);
  const sphereGeo = new THREE.SphereGeometry(0.07, 8, 6);
  const sphereGeo2 = new THREE.SphereGeometry(0.042, 8, 6);
  const sphereGeo3 = new THREE.SphereGeometry(0.012, 4, 3);
  const sphereGeo4 = new THREE.SphereGeometry(0.03, 6, 4);

  // Parts
  const body = new THREE.Group();
  body.name = 'body';
  root.add(body);
  const sheet = new THREE.Mesh(lathe([[0, 0.13], [0.33, 0.115], [0.325, 0.2], [0.3, 0.34], [0.28, 0.48], [0.265, 0.6], [0.225, 0.71], [0.15, 0.79], [0.07, 0.825], [0, 0.835]], 14), sheetMat);
  sheet.name = 'sheet';
  body.add(sheet);
  const hem = new THREE.Group();
  hem.name = 'hem';
  root.add(hem);
  const hemDrip1 = new THREE.Mesh(latheGeo, sheetMat);
  hemDrip1.name = 'hemDrip1';
  hemDrip1.position.set(0.0426, 0, 0.2818);
  hemDrip1.rotation.set(deg(-13.8428), 0, deg(2.0921));
  hem.add(hemDrip1);
  const hemDrip2 = new THREE.Mesh(latheGeo2, sheetMat);
  hemDrip2.name = 'hemDrip2';
  hemDrip2.position.set(0.1896, 0, 0.1923);
  hemDrip2.rotation.set(deg(-9.9693), 0, deg(9.8292));
  hem.add(hemDrip2);
  const hemDrip3 = new THREE.Mesh(latheGeo, sheetMat);
  hemDrip3.name = 'hemDrip3';
  hemDrip3.position.set(0.2812, 0, 0.0466);
  hemDrip3.rotation.set(deg(-2.2879), 0, deg(13.8118));
  hem.add(hemDrip3);
  const hemDrip4 = new THREE.Mesh(latheGeo2, sheetMat);
  hemDrip4.name = 'hemDrip4';
  hemDrip4.position.set(0.2414, 0, -0.1209);
  hemDrip4.rotation.set(deg(6.2674), 0, deg(12.5188));
  hem.add(hemDrip4);
  const hemDrip5 = new THREE.Mesh(latheGeo, sheetMat);
  hemDrip5.name = 'hemDrip5';
  hemDrip5.position.set(0.1312, 0, -0.253);
  hemDrip5.rotation.set(deg(12.4288), 0, deg(6.444));
  hem.add(hemDrip5);
  const hemDrip6 = new THREE.Mesh(latheGeo2, sheetMat);
  hemDrip6.name = 'hemDrip6';
  hemDrip6.position.set(-0.0403, 0, -0.267);
  hemDrip6.rotation.set(deg(13.8428), 0, deg(-2.0921));
  hem.add(hemDrip6);
  const hemDrip7 = new THREE.Mesh(latheGeo, sheetMat);
  hemDrip7.name = 'hemDrip7';
  hemDrip7.position.set(-0.2001, 0, -0.2029);
  hemDrip7.rotation.set(deg(9.9693), 0, deg(-9.8292));
  hem.add(hemDrip7);
  const hemDrip8 = new THREE.Mesh(latheGeo2, sheetMat);
  hemDrip8.name = 'hemDrip8';
  hemDrip8.position.set(-0.2664, 0, -0.0441);
  hemDrip8.rotation.set(deg(2.2879), 0, deg(-13.8118));
  hem.add(hemDrip8);
  const hemDrip9 = new THREE.Mesh(latheGeo, sheetMat);
  hemDrip9.name = 'hemDrip9';
  hemDrip9.position.set(-0.2548, 0, 0.1276);
  hemDrip9.rotation.set(deg(-6.2674), 0, deg(-12.5188));
  hem.add(hemDrip9);
  const hemDrip10 = new THREE.Mesh(latheGeo2, sheetMat);
  hemDrip10.name = 'hemDrip10';
  hemDrip10.position.set(-0.1243, 0, 0.2397);
  hemDrip10.rotation.set(deg(-12.4288), 0, deg(-6.444));
  hem.add(hemDrip10);
  const armL = new THREE.Group();
  armL.name = 'armL';
  armL.position.set(0.2543, 0.45, 0.02);
  armL.rotation.set(0, deg(-15), deg(25));
  root.add(armL);
  const armNubL = new THREE.Mesh(sphereGeo, sheetMat);
  armNubL.name = 'armNubL';
  armNubL.position.set(0.085, 0, 0);
  armNubL.scale.set(1.55, 0.72, 0.75);
  armL.add(armNubL);
  const armR = new THREE.Group();
  armR.name = 'armR';
  armR.position.set(-0.2543, 0.45, 0.02);
  armR.rotation.set(0, deg(15), deg(-25));
  root.add(armR);
  const armNubR = new THREE.Mesh(sphereGeo, sheetMat);
  armNubR.name = 'armNubR';
  armNubR.position.set(-0.085, 0, 0);
  armNubR.scale.set(1.55, 0.72, 0.75);
  armR.add(armNubR);
  const face = new THREE.Group();
  face.name = 'face';
  root.add(face);
  const eyeL = new THREE.Mesh(sphereGeo2, faceMat);
  eyeL.name = 'eyeL';
  eyeL.position.set(0.085, 0.58, 0.2416);
  eyeL.rotation.set(0, deg(18), 0);
  eyeL.scale.set(0.75, 1.3, 0.5);
  face.add(eyeL);
  const eyeShineL = new THREE.Mesh(sphereGeo3, shineMat);
  eyeShineL.name = 'eyeShineL';
  eyeShineL.position.set(0.077, 0.605, 0.2596);
  face.add(eyeShineL);
  const cheekL = new THREE.Mesh(sphereGeo4, blushMat);
  cheekL.name = 'cheekL';
  cheekL.position.set(0.15, 0.51, 0.224);
  cheekL.rotation.set(0, deg(32), 0);
  cheekL.scale.set(1, 0.55, 0.45);
  face.add(cheekL);
  const eyeR = new THREE.Mesh(sphereGeo2, faceMat);
  eyeR.name = 'eyeR';
  eyeR.position.set(-0.085, 0.58, 0.2416);
  eyeR.rotation.set(0, deg(-18), 0);
  eyeR.scale.set(0.75, 1.3, 0.5);
  face.add(eyeR);
  const eyeShineR = new THREE.Mesh(sphereGeo3, shineMat);
  eyeShineR.name = 'eyeShineR';
  eyeShineR.position.set(-0.093, 0.605, 0.2596);
  face.add(eyeShineR);
  const cheekR = new THREE.Mesh(sphereGeo4, blushMat);
  cheekR.name = 'cheekR';
  cheekR.position.set(-0.15, 0.51, 0.224);
  cheekR.rotation.set(0, deg(-32), 0);
  cheekR.scale.set(1, 0.55, 0.45);
  face.add(cheekR);
  const mouth = new THREE.Mesh(new THREE.SphereGeometry(0.045, 8, 6), mouthMat);
  mouth.name = 'mouth';
  mouth.position.set(0, 0.47, 0.2674);
  mouth.scale.set(0.8, 1.05, 0.5);
  face.add(mouth);

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

export default createGhost;

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