Codels
1,2 k tris0,21 × 0,22 × 0,22 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.
/**
 * Candles — Codels 3D model library
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 1,150 · Size (W × H × D): 0.21 × 0.22 × 0.22 m
 *
 * Usage:
 *   import { createCandle } from './candle.js';
 *   
 *   const model = createCandle();
 *   model.position.set(0, 0, 0);
 *   scene.add(model);
 */
import * as THREE from 'three';

const deg = THREE.MathUtils.degToRad;

// Deterministic vertex displacement (organic low-poly look).
function jitter(geometry, amount, seed) {
  const pos = geometry.attributes.position;
  const h = (a, b, c, s) => {
    let x = Math.imul(a, 73856093) ^ Math.imul(b, 19349663) ^ Math.imul(c, 83492791) ^ Math.imul(s, 2654435761);
    x = Math.imul(x ^ (x >>> 16), 2246822507);
    x = Math.imul(x ^ (x >>> 13), 3266489909);
    return ((x ^ (x >>> 16)) >>> 0) / 4294967295 - 0.5;
  };
  for (let i = 0; i < pos.count; i++) {
    const x = pos.getX(i), y = pos.getY(i), z = pos.getZ(i);
    const qx = Math.round(x * 1000), qy = Math.round(y * 1000), qz = Math.round(z * 1000);
    pos.setXYZ(i, x + h(qx, qy, qz, seed) * 2 * amount, y + h(qx, qy, qz, seed + 1) * 2 * amount, z + h(qx, qy, qz, seed + 2) * 2 * amount);
  }
  pos.needsUpdate = true;
  geometry.computeVertexNormals();
  return geometry;
}

// 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 createCandle() {
  const root = new THREE.Group();
  root.name = 'Candle';

  // Materials
  const waxMat = new THREE.MeshStandardMaterial({ name: 'wax', color: 0xf1dfb8, roughness: 0.55, flatShading: true });
  const dripMat = new THREE.MeshStandardMaterial({ name: 'drip', color: 0xfbe8bf, roughness: 0.45, flatShading: true });
  const dishMat = new THREE.MeshStandardMaterial({ name: 'dish', color: 0xc08a3e, roughness: 0.35, metalness: 0.55, flatShading: true });
  const wickMat = new THREE.MeshStandardMaterial({ name: 'wick', color: 0x2a2420, roughness: 1, flatShading: true });
  const flameOutMat = new THREE.MeshStandardMaterial({ name: 'flameOut', color: 0xff9a2a, roughness: 1, emissive: 0xff7a10, emissiveIntensity: 1.4, flatShading: true });
  const flameInMat = new THREE.MeshStandardMaterial({ name: 'flameIn', color: 0xfff1a8, roughness: 1, emissive: 0xffd860, emissiveIntensity: 1.8, flatShading: true });

  // Shared geometries
  const cylinderGeo = new THREE.CylinderGeometry(0.0018, 0.002, 0.014, 4);
  const sphereGeo = new THREE.SphereGeometry(0.0095, 7, 5);
  const coneGeo = new THREE.ConeGeometry(0.0094, 0.034, 7);

  // Parts
  const dish = new THREE.Mesh(lathe([[0, 0], [0.085, 0], [0.096, 0.006], [0.108, 0.019], [0.104, 0.022], [0.088, 0.012], [0, 0.012]], 14), dishMat);
  dish.name = 'dish';
  root.add(dish);
  const candle1 = new THREE.Group();
  candle1.name = 'candle1';
  candle1.position.set(-0.03, 0, -0.028);
  root.add(candle1);
  const waxPool = new THREE.Mesh(jitter(new THREE.CylinderGeometry(0.0348, 0.0408, 0.006, 9), 0.0015, 1), dripMat);
  waxPool.name = 'waxPool';
  waxPool.position.set(0, 0.014, 0);
  candle1.add(waxPool);
  const body = new THREE.Mesh(new THREE.CylinderGeometry(0.024, 0.025, 0.16, 9), waxMat);
  body.name = 'body';
  body.position.set(0, 0.092, 0);
  candle1.add(body);
  const rim = new THREE.Mesh(jitter(new THREE.CylinderGeometry(0.0254, 0.0245, 0.01, 9), 0.0018, 4), dripMat);
  rim.name = 'rim';
  rim.position.set(0, 0.171, 0);
  candle1.add(rim);
  const drip = new THREE.Mesh(new THREE.CapsuleGeometry(0.0045, 0.0441, 2, 5), dripMat);
  drip.name = 'drip';
  drip.position.set(-0.0151, 0.1459, -0.0181);
  candle1.add(drip);
  const drip2 = new THREE.Mesh(new THREE.CapsuleGeometry(0.0045, 0.0402, 2, 5), dripMat);
  drip2.name = 'drip';
  drip2.position.set(0.0232, 0.1479, 0.0039);
  candle1.add(drip2);
  const drip3 = new THREE.Mesh(new THREE.CapsuleGeometry(0.0045, 0.0423, 2, 5), dripMat);
  drip3.name = 'drip';
  drip3.position.set(-0.0135, 0.1468, 0.0192);
  candle1.add(drip3);
  const wick = new THREE.Mesh(cylinderGeo, wickMat);
  wick.name = 'wick';
  wick.position.set(0, 0.179, 0);
  candle1.add(wick);
  const flameBase = new THREE.Mesh(sphereGeo, flameOutMat);
  flameBase.name = 'flameBase';
  flameBase.position.set(0, 0.188, 0);
  flameBase.scale.set(1, 1.15, 1);
  candle1.add(flameBase);
  const flameTip = new THREE.Mesh(coneGeo, flameInMat);
  flameTip.name = 'flameTip';
  flameTip.position.set(0, 0.205, 0);
  candle1.add(flameTip);
  const candle2 = new THREE.Group();
  candle2.name = 'candle2';
  candle2.position.set(0.042, 0, -0.012);
  candle2.rotation.set(0, deg(70), 0);
  root.add(candle2);
  const waxPool2 = new THREE.Mesh(jitter(new THREE.CylinderGeometry(0.0305, 0.0357, 0.006, 9), 0.0015, 2), dripMat);
  waxPool2.name = 'waxPool';
  waxPool2.position.set(0, 0.014, 0);
  candle2.add(waxPool2);
  const body2 = new THREE.Mesh(new THREE.CylinderGeometry(0.021, 0.0218, 0.115, 9), waxMat);
  body2.name = 'body';
  body2.position.set(0, 0.0695, 0);
  candle2.add(body2);
  const rim2 = new THREE.Mesh(jitter(new THREE.CylinderGeometry(0.0223, 0.0214, 0.01, 9), 0.0018, 5), dripMat);
  rim2.name = 'rim';
  rim2.position.set(0, 0.126, 0);
  candle2.add(rim2);
  const drip4 = new THREE.Mesh(new THREE.CapsuleGeometry(0.0045, 0.0233, 2, 5), dripMat);
  drip4.name = 'drip';
  drip4.position.set(-0.0129, 0.1113, -0.0161);
  candle2.add(drip4);
  const drip5 = new THREE.Mesh(new THREE.CapsuleGeometry(0.0045, 0.0446, 2, 5), dripMat);
  drip5.name = 'drip';
  drip5.position.set(0.0171, 0.1007, 0.0114);
  candle2.add(drip5);
  const wick2 = new THREE.Mesh(cylinderGeo, wickMat);
  wick2.name = 'wick';
  wick2.position.set(0, 0.134, 0);
  candle2.add(wick2);
  const flameBase2 = new THREE.Mesh(sphereGeo, flameOutMat);
  flameBase2.name = 'flameBase';
  flameBase2.position.set(0, 0.143, 0);
  flameBase2.scale.set(1, 1.15, 1);
  candle2.add(flameBase2);
  const flameTip2 = new THREE.Mesh(coneGeo, flameInMat);
  flameTip2.name = 'flameTip';
  flameTip2.position.set(0, 0.16, 0);
  candle2.add(flameTip2);
  const candle3 = new THREE.Group();
  candle3.name = 'candle3';
  candle3.position.set(0, 0, 0.042);
  candle3.rotation.set(0, deg(140), 0);
  root.add(candle3);
  const waxPool3 = new THREE.Mesh(jitter(new THREE.CylinderGeometry(0.0276, 0.0323, 0.006, 9), 0.0015, 3), dripMat);
  waxPool3.name = 'waxPool';
  waxPool3.position.set(0, 0.014, 0);
  candle3.add(waxPool3);
  const body3 = new THREE.Mesh(new THREE.CylinderGeometry(0.019, 0.0198, 0.075, 9), waxMat);
  body3.name = 'body';
  body3.position.set(0, 0.0495, 0);
  candle3.add(body3);
  const rim3 = new THREE.Mesh(jitter(new THREE.CylinderGeometry(0.0201, 0.0194, 0.01, 9), 0.0018, 6), dripMat);
  rim3.name = 'rim';
  rim3.position.set(0, 0.086, 0);
  candle3.add(rim3);
  const drip6 = new THREE.Mesh(new THREE.CapsuleGeometry(0.0045, 0.0254, 2, 5), dripMat);
  drip6.name = 'drip';
  drip6.position.set(0.0162, 0.0703, 0.0092);
  candle3.add(drip6);
  const drip7 = new THREE.Mesh(new THREE.CapsuleGeometry(0.0045, 0.0216, 2, 5), dripMat);
  drip7.name = 'drip';
  drip7.position.set(0.0041, 0.0722, 0.0182);
  candle3.add(drip7);
  const drip8 = new THREE.Mesh(new THREE.CapsuleGeometry(0.0045, 0.0341, 2, 5), dripMat);
  drip8.name = 'drip';
  drip8.position.set(0.0098, 0.0659, 0.0159);
  candle3.add(drip8);
  const wick3 = new THREE.Mesh(cylinderGeo, wickMat);
  wick3.name = 'wick';
  wick3.position.set(0, 0.094, 0);
  candle3.add(wick3);
  const flameBase3 = new THREE.Mesh(sphereGeo, flameOutMat);
  flameBase3.name = 'flameBase';
  flameBase3.position.set(0, 0.103, 0);
  flameBase3.scale.set(1, 1.15, 1);
  candle3.add(flameBase3);
  const flameTip3 = new THREE.Mesh(coneGeo, flameInMat);
  flameTip3.name = 'flameTip';
  flameTip3.position.set(0, 0.12, 0);
  candle3.add(flameTip3);

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

export default createCandle;

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