Codels
1.4K tris0.08 × 0.21 × 0.08 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.
/**
 * Ice Cream Cone — Codels 3D model library
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 1,442 · Size (W × H × D): 0.08 × 0.21 × 0.08 m
 *
 * Usage:
 *   import { createIceCream } from './ice-cream.js';
 *   
 *   const model = createIceCream();
 *   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);
}

// Tube along a smooth curve through the given points.
function tube(path, radius, tubularSegments, radialSegments, closed = false) {
  const curve = new THREE.CatmullRomCurve3(path.map(([x, y, z]) => new THREE.Vector3(x, y, z)), closed);
  return new THREE.TubeGeometry(curve, tubularSegments, radius, radialSegments, closed);
}

export function createIceCream() {
  const root = new THREE.Group();
  root.name = 'IceCream';

  // Materials
  const coneMat = new THREE.MeshStandardMaterial({ name: 'cone', color: 0xe0a659, roughness: 0.8, flatShading: true });
  const waffleMat = new THREE.MeshStandardMaterial({ name: 'waffle', color: 0xb0742f, roughness: 0.85, flatShading: true });
  const f1Mat = new THREE.MeshStandardMaterial({ name: 'f1', color: 0xf49ac1, roughness: 0.55 });
  const f2Mat = new THREE.MeshStandardMaterial({ name: 'f2', color: 0xa5e3c9, roughness: 0.55 });
  const s1Mat = new THREE.MeshStandardMaterial({ name: 's1', color: 0xff5a5f, roughness: 0.4, flatShading: true });
  const s2Mat = new THREE.MeshStandardMaterial({ name: 's2', color: 0xffd23f, roughness: 0.4, flatShading: true });
  const s3Mat = new THREE.MeshStandardMaterial({ name: 's3', color: 0x3ec1d3, roughness: 0.4, flatShading: true });
  const s4Mat = new THREE.MeshStandardMaterial({ name: 's4', color: 0xffffff, roughness: 0.4, flatShading: true });

  // Shared geometries
  const boxGeo = new THREE.BoxGeometry(0.0085, 0.0022, 0.0022);

  // Parts
  const cone = new THREE.Mesh(lathe([[0, 0], [0.004, 0.006], [0.033, 0.118], [0.031, 0.12], [0, 0.12]], 12), coneMat);
  cone.name = 'cone';
  root.add(cone);
  const waffleA1 = new THREE.Mesh(tube([[0.0027, 0.016, 0.0067], [0.0079, 0.032, 0.0082], [0.0141, 0.048, 0.0063], [0.0196, 0.064, 0.0007], [0.0224, 0.08, -0.0081], [0.0208, 0.096, -0.0186], [0.0142, 0.112, -0.0287]], 0.0013, 6, 3), waffleMat);
  waffleA1.name = 'waffleA1';
  root.add(waffleA1);
  const waffleA2 = new THREE.Mesh(tube([[0.0072, 0.016, -0.0005], [0.0102, 0.032, -0.005], [0.0104, 0.048, -0.0115], [0.0068, 0.064, -0.0184], [-0.0008, 0.08, -0.0237], [-0.0113, 0.096, -0.0255], [-0.023, 0.112, -0.0224]], 0.0013, 6, 3), waffleMat);
  waffleA2.name = 'waffleA2';
  root.add(waffleA2);
  const waffleA3 = new THREE.Mesh(tube([[0.0017, 0.016, -0.007], [-0.0016, 0.032, -0.0112], [-0.0077, 0.048, -0.0134], [-0.0154, 0.064, -0.0121], [-0.0228, 0.08, -0.0066], [-0.0278, 0.096, 0.0028], [-0.0284, 0.112, 0.0149]], 0.0013, 6, 3), waffleMat);
  waffleA3.name = 'waffleA3';
  root.add(waffleA3);
  const waffleA4 = new THREE.Mesh(tube([[-0.0061, 0.016, -0.0038], [-0.0112, 0.032, -0.002], [-0.0151, 0.048, 0.0032], [-0.0163, 0.064, 0.0109], [-0.0133, 0.08, 0.0197], [-0.0059, 0.096, 0.0273], [0.0054, 0.112, 0.0316]], 0.0013, 6, 3), waffleMat);
  waffleA4.name = 'waffleA4';
  root.add(waffleA4);
  const waffleA5 = new THREE.Mesh(tube([[-0.0055, 0.016, 0.0046], [-0.0053, 0.032, 0.01], [-0.0016, 0.048, 0.0154], [0.0054, 0.064, 0.0189], [0.0146, 0.08, 0.0188], [0.0241, 0.096, 0.014], [0.0317, 0.112, 0.0046]], 0.0013, 6, 3), waffleMat);
  waffleA5.name = 'waffleA5';
  root.add(waffleA5);
  const waffleB1 = new THREE.Mesh(tube([[-0.0027, 0.016, 0.0067], [-0.0079, 0.032, 0.0082], [-0.0141, 0.048, 0.0063], [-0.0196, 0.064, 0.0007], [-0.0224, 0.08, -0.0081], [-0.0208, 0.096, -0.0186], [-0.0142, 0.112, -0.0287]], 0.0013, 6, 3), waffleMat);
  waffleB1.name = 'waffleB1';
  root.add(waffleB1);
  const waffleB2 = new THREE.Mesh(tube([[0.0055, 0.016, 0.0046], [0.0053, 0.032, 0.01], [0.0016, 0.048, 0.0154], [-0.0054, 0.064, 0.0189], [-0.0146, 0.08, 0.0188], [-0.0241, 0.096, 0.014], [-0.0317, 0.112, 0.0046]], 0.0013, 6, 3), waffleMat);
  waffleB2.name = 'waffleB2';
  root.add(waffleB2);
  const waffleB3 = new THREE.Mesh(tube([[0.0061, 0.016, -0.0038], [0.0112, 0.032, -0.002], [0.0151, 0.048, 0.0032], [0.0163, 0.064, 0.0109], [0.0133, 0.08, 0.0197], [0.0059, 0.096, 0.0273], [-0.0054, 0.112, 0.0316]], 0.0013, 6, 3), waffleMat);
  waffleB3.name = 'waffleB3';
  root.add(waffleB3);
  const waffleB4 = new THREE.Mesh(tube([[-0.0017, 0.016, -0.007], [0.0016, 0.032, -0.0112], [0.0077, 0.048, -0.0134], [0.0154, 0.064, -0.0121], [0.0228, 0.08, -0.0066], [0.0278, 0.096, 0.0028], [0.0284, 0.112, 0.0149]], 0.0013, 6, 3), waffleMat);
  waffleB4.name = 'waffleB4';
  root.add(waffleB4);
  const waffleB5 = new THREE.Mesh(tube([[-0.0072, 0.016, -0.0005], [-0.0102, 0.032, -0.005], [-0.0104, 0.048, -0.0115], [-0.0068, 0.064, -0.0184], [0.0008, 0.08, -0.0237], [0.0113, 0.096, -0.0255], [0.023, 0.112, -0.0224]], 0.0013, 6, 3), waffleMat);
  waffleB5.name = 'waffleB5';
  root.add(waffleB5);
  const rim = new THREE.Mesh(new THREE.TorusGeometry(0.034, 0.0055, 4, 12), coneMat);
  rim.name = 'rim';
  rim.position.set(0, 0.116, 0);
  rim.rotation.set(deg(90), 0, 0);
  root.add(rim);
  const scoop1 = new THREE.Mesh(new THREE.SphereGeometry(0.037, 11, 8), f1Mat);
  scoop1.name = 'scoop1';
  scoop1.position.set(0, 0.134, 0);
  scoop1.scale.set(1, 0.88, 1);
  root.add(scoop1);
  const scoopLip1 = new THREE.Mesh(jitter(new THREE.TorusGeometry(0.0318, 0.0089, 4, 12), 0.0026, 3), f1Mat);
  scoopLip1.name = 'scoopLip1';
  scoopLip1.position.set(0, 0.1185, 0);
  scoopLip1.rotation.set(deg(90), 0, 0);
  root.add(scoopLip1);
  const scoop2 = new THREE.Mesh(new THREE.SphereGeometry(0.035, 11, 8), f2Mat);
  scoop2.name = 'scoop2';
  scoop2.position.set(0.003, 0.1828, 0);
  scoop2.scale.set(1, 0.88, 1);
  root.add(scoop2);
  const scoopLip2 = new THREE.Mesh(jitter(new THREE.TorusGeometry(0.0301, 0.0084, 4, 12), 0.0025, 4), f2Mat);
  scoopLip2.name = 'scoopLip2';
  scoopLip2.position.set(0.003, 0.1681, 0);
  scoopLip2.rotation.set(deg(90), 0, deg(20));
  root.add(scoopLip2);
  const drip1 = new THREE.Mesh(new THREE.CapsuleGeometry(0.0045, 0.016, 2, 5), f1Mat);
  drip1.name = 'drip1';
  drip1.position.set(0.0212, 0.112, 0.0303);
  root.add(drip1);
  const drip2 = new THREE.Mesh(new THREE.CapsuleGeometry(0.0045, 0.011, 2, 5), f1Mat);
  drip2.name = 'drip2';
  drip2.position.set(0.0185, 0.1145, -0.032);
  root.add(drip2);
  const drip3 = new THREE.Mesh(new THREE.CapsuleGeometry(0.0045, 0.014, 2, 5), f1Mat);
  drip3.name = 'drip3';
  drip3.position.set(-0.0357, 0.113, -0.0096);
  root.add(drip3);
  const sprinkle1 = new THREE.Mesh(boxGeo, s1Mat);
  sprinkle1.name = 'sprinkle1';
  sprinkle1.position.set(-0.0138, 0.2085, -0.0082);
  sprinkle1.rotation.set(deg(-99.7956), deg(-30.0891), deg(60.2466));
  root.add(sprinkle1);
  const sprinkle2 = new THREE.Mesh(boxGeo, s2Mat);
  sprinkle2.name = 'sprinkle2';
  sprinkle2.position.set(0.0141, 0.2119, 0.0065);
  sprinkle2.rotation.set(deg(-84.3671), deg(8.6837), deg(144.8202));
  root.add(sprinkle2);
  const sprinkle3 = new THREE.Mesh(boxGeo, s3Mat);
  sprinkle3.name = 'sprinkle3';
  sprinkle3.position.set(0.0322, 0.1981, -0.0059);
  sprinkle3.rotation.set(deg(-90), deg(53.9253), deg(100.142));
  root.add(sprinkle3);
  const sprinkle4 = new THREE.Mesh(boxGeo, s4Mat);
  sprinkle4.name = 'sprinkle4';
  sprinkle4.position.set(-0.0272, 0.1897, -0.0133);
  sprinkle4.rotation.set(deg(-142.4105), deg(-69.6677), deg(-72.9864));
  root.add(sprinkle4);
  const sprinkle5 = new THREE.Mesh(boxGeo, s1Mat);
  sprinkle5.name = 'sprinkle5';
  sprinkle5.position.set(0.0218, 0.2083, 0.0016);
  sprinkle5.rotation.set(deg(-90), deg(31.5008), deg(145.0105));
  root.add(sprinkle5);
  const sprinkle6 = new THREE.Mesh(boxGeo, s2Mat);
  sprinkle6.name = 'sprinkle6';
  sprinkle6.position.set(-0.02, 0.206, -0.0017);
  sprinkle6.rotation.set(deg(-99.7956), deg(-30.0891), deg(51.6527));
  root.add(sprinkle6);
  const sprinkle7 = new THREE.Mesh(boxGeo, s3Mat);
  sprinkle7.name = 'sprinkle7';
  sprinkle7.position.set(-0.0268, 0.1939, -0.0115);
  sprinkle7.rotation.set(deg(-142.4106), deg(-69.6677), deg(-27.7617));
  root.add(sprinkle7);
  const sprinkle8 = new THREE.Mesh(boxGeo, s4Mat);
  sprinkle8.name = 'sprinkle8';
  sprinkle8.position.set(-0.0158, 0.1973, -0.0231);
  sprinkle8.rotation.set(deg(-136.0503), deg(-31.9576), deg(2.3355));
  root.add(sprinkle8);
  const sprinkle9 = new THREE.Mesh(boxGeo, s1Mat);
  sprinkle9.name = 'sprinkle9';
  sprinkle9.position.set(0.0073, 0.2122, 0.0101);
  sprinkle9.rotation.set(deg(-80.5778), deg(4.2756), deg(161.4039));
  root.add(sprinkle9);
  const sprinkle10 = new THREE.Mesh(boxGeo, s2Mat);
  sprinkle10.name = 'sprinkle10';
  sprinkle10.position.set(0.0272, 0.2017, -0.0116);
  sprinkle10.rotation.set(deg(-126.5788), deg(42.8392), deg(-169.8255));
  root.add(sprinkle10);
  const sprinkle11 = new THREE.Mesh(boxGeo, s3Mat);
  sprinkle11.name = 'sprinkle11';
  sprinkle11.position.set(-0.0027, 0.2125, -0.0073);
  sprinkle11.rotation.set(deg(-97.8501), deg(-6.7495), deg(33.6298));
  root.add(sprinkle11);
  const sprinkle12 = new THREE.Mesh(boxGeo, s4Mat);
  sprinkle12.name = 'sprinkle12';
  sprinkle12.position.set(-0.0104, 0.2112, 0.0046);
  sprinkle12.rotation.set(deg(-80.2044), deg(-30.0891), deg(146.7579));
  root.add(sprinkle12);
  const sprinkle13 = new THREE.Mesh(boxGeo, s1Mat);
  sprinkle13.name = 'sprinkle13';
  sprinkle13.position.set(0.016, 0.2111, -0.0062);
  sprinkle13.rotation.set(deg(-108.3309), deg(26.0761), deg(164.8385));
  root.add(sprinkle13);
  const sprinkle14 = new THREE.Mesh(boxGeo, s2Mat);
  sprinkle14.name = 'sprinkle14';
  sprinkle14.position.set(0.0278, 0.203, 0.0087);
  sprinkle14.rotation.set(deg(-53.4212), deg(42.8392), deg(153.1879));
  root.add(sprinkle14);
  const sprinkle15 = new THREE.Mesh(boxGeo, s3Mat);
  sprinkle15.name = 'sprinkle15';
  sprinkle15.position.set(0.02, 0.1908, 0.0279);
  sprinkle15.rotation.set(deg(-13.4105), deg(23.9523), deg(47.4731));
  root.add(sprinkle15);
  const sprinkle16 = new THREE.Mesh(boxGeo, s4Mat);
  sprinkle16.name = 'sprinkle16';
  sprinkle16.position.set(-0.0283, 0.1856, 0.0127);
  sprinkle16.rotation.set(deg(-37.5895), deg(-69.6677), deg(37.6022));
  root.add(sprinkle16);
  const sprinkle17 = new THREE.Mesh(boxGeo, s1Mat);
  sprinkle17.name = 'sprinkle17';
  sprinkle17.position.set(0.0216, 0.2076, 0.0079);
  sprinkle17.rotation.set(deg(-71.6691), deg(26.0761), deg(163.6766));
  root.add(sprinkle17);
  const sprinkle18 = new THREE.Mesh(boxGeo, s2Mat);
  sprinkle18.name = 'sprinkle18';
  sprinkle18.position.set(-0.0091, 0.1931, -0.0306);
  sprinkle18.rotation.set(deg(-167.6412), deg(-7.9947), deg(-43.1097));
  root.add(sprinkle18);
  const sprinkle19 = new THREE.Mesh(boxGeo, s3Mat);
  sprinkle19.name = 'sprinkle19';
  sprinkle19.position.set(0.0083, 0.1857, -0.0345);
  sprinkle19.rotation.set(deg(-166.5895), deg(23.9523), deg(-132.2799));
  root.add(sprinkle19);
  const sprinkle20 = new THREE.Mesh(boxGeo, s4Mat);
  sprinkle20.name = 'sprinkle20';
  sprinkle20.position.set(-0.0077, 0.212, 0.0065);
  sprinkle20.rotation.set(deg(-87.0577), deg(-9.9158), deg(130.2375));
  root.add(sprinkle20);

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

export default createIceCream;

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