422 tris1.31 × 1.00 × 1.42 m
Use it in code
Remix in the editortree-stump.js · 139 lines
Self-contained ES module — only depends on three. Call the factory and add the group to your scene.
/**
* Tree Stump — Codels 3D model library
* License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
* Triangles: 422 · Size (W × H × D): 1.31 × 1.00 × 1.42 m
*
* Usage:
* import { createTreeStump } from './tree-stump.js';
*
* const model = createTreeStump();
* 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 createTreeStump() {
const root = new THREE.Group();
root.name = 'TreeStump';
// Materials
const barkMat = new THREE.MeshStandardMaterial({ name: 'bark', color: 0x5f4631, roughness: 0.95, flatShading: true });
const cutMat = new THREE.MeshStandardMaterial({ name: 'cut', color: 0xd3a262, roughness: 0.85, flatShading: true });
const ringMat = new THREE.MeshStandardMaterial({ name: 'ring', color: 0x9c6a3a, roughness: 0.9, flatShading: true });
const steelMat = new THREE.MeshStandardMaterial({ name: 'steel', color: 0x9aa1a8, roughness: 0.35, metalness: 0.6, flatShading: true });
const handleMat = new THREE.MeshStandardMaterial({ name: 'handle', color: 0xa5733f, roughness: 0.8, flatShading: true });
// Shared geometries
const cylinderGeo = new THREE.CylinderGeometry(0.0714, 0.1176, 0.2716, 6, 1, true);
// Parts
const body = new THREE.Mesh(new THREE.CylinderGeometry(0.42, 0.4452, 0.45, 9), barkMat);
body.name = 'body';
body.position.set(0, 0.225, 0);
root.add(body);
const foot = new THREE.Mesh(new THREE.CylinderGeometry(0.4452, 0.5376, 0.14, 9), barkMat);
foot.name = 'foot';
foot.position.set(0, 0.07, 0);
root.add(foot);
const cut = new THREE.Mesh(new THREE.CylinderGeometry(0.3696, 0.3696, 0.02, 9), cutMat);
cut.name = 'cut';
cut.position.set(0, 0.448, 0);
root.add(cut);
const ring1 = new THREE.Mesh(new THREE.RingGeometry(0.0661, 0.0821, 18), ringMat);
ring1.name = 'ring1';
ring1.position.set(0.0017, 0.46, -0.0022);
ring1.rotation.set(deg(-90), 0, 0);
root.add(ring1);
const ring2 = new THREE.Mesh(new THREE.RingGeometry(0.1688, 0.1848, 18), ringMat);
ring2.name = 'ring2';
ring2.position.set(0.0051, 0.46, -0.0029);
ring2.rotation.set(deg(-90), 0, 0);
root.add(ring2);
const ring3 = new THREE.Mesh(new THREE.RingGeometry(0.2715, 0.2875, 18), ringMat);
ring3.name = 'ring3';
ring3.position.set(0.0035, 0.46, -0.0048);
ring3.rotation.set(deg(-90), 0, 0);
root.add(ring3);
const pith = new THREE.Mesh(new THREE.CircleGeometry(0.02, 6), ringMat);
pith.name = 'pith';
pith.position.set(0, 0.46, 0);
pith.rotation.set(deg(-90), 0, 0);
root.add(pith);
const root1 = new THREE.Mesh(cylinderGeo, barkMat);
root1.name = 'root1';
root1.position.set(0.3125, 0.1257, 0.1726);
root1.rotation.set(deg(33.4409), deg(-35.534), deg(-93.695));
root.add(root1);
const rootTip1 = new THREE.Mesh(new THREE.CylinderGeometry(0.0357, 0.0714, 0.2884, 6), barkMat);
rootTip1.name = 'rootTip1';
rootTip1.position.set(0.5352, 0.0553, 0.2956);
rootTip1.rotation.set(deg(33.1732), deg(-29.1521), deg(-82.2427));
root.add(rootTip1);
const root2 = new THREE.Mesh(cylinderGeo, barkMat);
root2.name = 'root2';
root2.position.set(-0.0713, 0.1257, 0.3498);
root2.rotation.set(deg(109.3161), deg(15.5908), deg(11.09));
root.add(root2);
const rootTip2 = new THREE.Mesh(new THREE.CylinderGeometry(0.0357, 0.0714, 0.2423, 6), barkMat);
rootTip2.name = 'rootTip2';
rootTip2.position.set(-0.1174, 0.0553, 0.5762);
rootTip2.rotation.set(deg(98.0673), deg(13.3943), deg(11.6427));
root.add(rootTip2);
const root3 = new THREE.Mesh(cylinderGeo, barkMat);
root3.name = 'root3';
root3.position.set(-0.2839, 0.1257, -0.2165);
root3.rotation.set(deg(-48.6488), deg(-41.4683), deg(79.8885));
root.add(root3);
const rootTip3 = new THREE.Mesh(new THREE.CylinderGeometry(0.0357, 0.0714, 0.3021, 6), barkMat);
rootTip3.name = 'rootTip3';
rootTip3.position.set(-0.4917, 0.0553, -0.3749);
rootTip3.rotation.set(deg(-45.9965), deg(-33.4629), deg(70.6168));
root.add(rootTip3);
const root4 = new THREE.Mesh(cylinderGeo, barkMat);
root4.name = 'root4';
root4.position.set(0.1689, 0.1257, -0.3145);
root4.rotation.set(deg(-94.6237), deg(34.9152), deg(-32.3526));
root.add(root4);
const rootTip4 = new THREE.Mesh(new THREE.CylinderGeometry(0.0357, 0.0714, 0.3238, 6), barkMat);
rootTip4.name = 'rootTip4';
rootTip4.position.set(0.2977, 0.0553, -0.5545);
rootTip4.rotation.set(deg(-82.143), deg(28.176), deg(-32.1295));
root.add(rootTip4);
const axe = new THREE.Group();
axe.name = 'axe';
axe.position.set(-0.063, 0.593, 0.0504);
axe.rotation.set(0, deg(35), deg(24));
axe.scale.setScalar(1.25);
root.add(axe);
const axeHead = new THREE.Mesh(extrude([[-0.035, 0.05], [-0.035, -0.04], [-0.09, -0.15], [-0.065, -0.168], [0, -0.175], [0.07, -0.168], [0.1, -0.15], [0.035, -0.04], [0.035, 0.05]], 0.03), steelMat);
axeHead.name = 'axeHead';
axe.add(axeHead);
const axeHandle = new THREE.Mesh(new THREE.CylinderGeometry(0.022, 0.018, 0.78, 6), handleMat);
axeHandle.name = 'axeHandle';
axeHandle.position.set(0.33, 0.015, 0);
axeHandle.rotation.set(0, 0, deg(-90));
axe.add(axeHandle);
root.traverse((o) => {
if (o.isMesh) {
o.castShadow = true;
o.receiveShadow = true;
}
});
return root;
}
export default createTreeStump;
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









