1.4K tris0.08 × 0.21 × 0.08 m
Use it in code
Remix in the editorice-cream.js · 246 lines
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
0 comments
No comments yet. Share how you used this model or what could be better.
Related models
Same category or similar tags









