1,4 k tris0,40 × 0,45 × 0,29 m
Utiliser dans le code
Remixer dans l’éditeurbonsai.js · 303 lignes
Module ES autonome — ne dépend que de three. Appelez la fonction factory et ajoutez le groupe à votre scène.
/**
* Bonsai Tree — Codels 3D model library
* License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
* Triangles: 1,369 · Size (W × H × D): 0.40 × 0.45 × 0.29 m
*
* Usage:
* import { createBonsai } from './bonsai.js';
*
* const model = createBonsai();
* 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;
}
export function createBonsai() {
const root = new THREE.Group();
root.name = 'Bonsai';
// Materials
const glazeMat = new THREE.MeshStandardMaterial({ name: 'glaze', color: 0x3e6a80, roughness: 0.35, flatShading: true });
const glazeDarkMat = new THREE.MeshStandardMaterial({ name: 'glazeDark', color: 0x2b4a5a, roughness: 0.45, flatShading: true });
const soilMat = new THREE.MeshStandardMaterial({ name: 'soil', color: 0x4a3527, roughness: 1, flatShading: true });
const mossMat = new THREE.MeshStandardMaterial({ name: 'moss', color: 0x58763a, roughness: 1, flatShading: true });
const stoneMat = new THREE.MeshStandardMaterial({ name: 'stone', color: 0x8b8781, roughness: 0.9, flatShading: true });
const barkMat = new THREE.MeshStandardMaterial({ name: 'bark', color: 0x6b5444, roughness: 0.95, flatShading: true });
const leafMat = new THREE.MeshStandardMaterial({ name: 'leaf', color: 0x4f8a3a, roughness: 0.85, flatShading: true });
const leafLightMat = new THREE.MeshStandardMaterial({ name: 'leafLight', color: 0x60a054, roughness: 0.85, flatShading: true });
const leafDarkMat = new THREE.MeshStandardMaterial({ name: 'leafDark', color: 0x3a6c2d, roughness: 0.9, flatShading: true });
// Shared geometries
const boxGeo = new THREE.BoxGeometry(0.366, 0.012, 0.014);
const boxGeo2 = new THREE.BoxGeometry(0.014, 0.012, 0.266);
const boxGeo3 = new THREE.BoxGeometry(0.035, 0.014, 0.028);
const dodecahedronGeo = jitter(new THREE.DodecahedronGeometry(0.034, 0), 0.0034, 79);
// Parts
const pot = new THREE.Mesh(new THREE.CylinderGeometry(0.1838, 0.1655, 0.07, 4, 1, false, Math.PI / 4), glazeMat);
pot.name = 'pot';
pot.position.set(0, 0.049, 0);
pot.scale.set(1.3846, 1, 1);
root.add(pot);
const rimFront = new THREE.Mesh(boxGeo, glazeMat);
rimFront.name = 'rimFront';
rimFront.position.set(0, 0.086, 0.126);
root.add(rimFront);
const rimBack = new THREE.Mesh(boxGeo, glazeMat);
rimBack.name = 'rimBack';
rimBack.position.set(0, 0.086, -0.126);
root.add(rimBack);
const rimLeft = new THREE.Mesh(boxGeo2, glazeMat);
rimLeft.name = 'rimLeft';
rimLeft.position.set(-0.176, 0.086, 0);
root.add(rimLeft);
const rimRight = new THREE.Mesh(boxGeo2, glazeMat);
rimRight.name = 'rimRight';
rimRight.position.set(0.176, 0.086, 0);
root.add(rimRight);
const soil = new THREE.Mesh(new THREE.BoxGeometry(0.339, 0.006, 0.239), soilMat);
soil.name = 'soil';
soil.position.set(0, 0.084, 0);
root.add(soil);
const band = new THREE.Mesh(new THREE.BoxGeometry(0.368, 0.006, 0.268), glazeDarkMat);
band.name = 'band';
band.position.set(0, 0.017, 0);
band.scale.set(0.93, 1, 0.93);
root.add(band);
const footRF = new THREE.Mesh(boxGeo3, glazeDarkMat);
footRF.name = 'footRF';
footRF.position.set(0.1296, 0.007, 0.0858);
root.add(footRF);
const footLF = new THREE.Mesh(boxGeo3, glazeDarkMat);
footLF.name = 'footLF';
footLF.position.set(-0.1296, 0.007, 0.0858);
root.add(footLF);
const footRB = new THREE.Mesh(boxGeo3, glazeDarkMat);
footRB.name = 'footRB';
footRB.position.set(0.1296, 0.007, -0.0858);
root.add(footRB);
const footLB = new THREE.Mesh(boxGeo3, glazeDarkMat);
footLB.name = 'footLB';
footLB.position.set(-0.1296, 0.007, -0.0858);
root.add(footLB);
const trunk1 = new THREE.Mesh(new THREE.CylinderGeometry(0.0264, 0.03, 0.095, 6), barkMat);
trunk1.name = 'trunk1';
trunk1.position.set(-0.0299, 0.109, 0.0082);
trunk1.rotation.set(deg(7.7613), deg(-2.3924), deg(-34.2184));
root.add(trunk1);
const trunk2 = new THREE.Mesh(new THREE.CylinderGeometry(0.0228, 0.0264, 0.0743, 6), barkMat);
trunk2.name = 'trunk2';
trunk2.position.set(-0.0044, 0.162, 0.0127);
trunk2.rotation.set(deg(-0.1878), deg(0.0232), deg(-14.0788));
root.add(trunk2);
const knot1 = new THREE.Mesh(new THREE.DodecahedronGeometry(0.0269, 0), barkMat);
knot1.name = 'knot1';
knot1.position.set(-0.0106, 0.137, 0.0128);
root.add(knot1);
const trunk3 = new THREE.Mesh(new THREE.CylinderGeometry(0.0192, 0.0228, 0.0732, 6), barkMat);
trunk3.name = 'trunk3';
trunk3.position.set(-0.0071, 0.212, 0.0079);
trunk3.rotation.set(deg(-10.1652), deg(-1.7534), deg(19.5246));
root.add(trunk3);
const knot2 = new THREE.Mesh(new THREE.DodecahedronGeometry(0.0233, 0), barkMat);
knot2.name = 'knot2';
knot2.position.set(0.0019, 0.187, 0.0126);
root.add(knot2);
const trunk4 = new THREE.Mesh(new THREE.CylinderGeometry(0.0156, 0.0192, 0.0748, 6), barkMat);
trunk4.name = 'trunk4';
trunk4.position.set(-0.0301, 0.262, -0.0044);
trunk4.rotation.set(deg(-14.6521), deg(-3.7073), deg(28.2588));
root.add(trunk4);
const knot3 = new THREE.Mesh(new THREE.DodecahedronGeometry(0.0196, 0), barkMat);
knot3.name = 'knot3';
knot3.position.set(-0.0161, 0.237, 0.0031);
root.add(knot3);
const trunk5 = new THREE.Mesh(new THREE.CylinderGeometry(0.012, 0.0156, 0.0648, 6), barkMat);
trunk5.name = 'trunk5';
trunk5.position.set(-0.0487, 0.312, -0.0189);
trunk5.rotation.set(deg(-15.6053), deg(-1.3611), deg(9.9086));
root.add(trunk5);
const knot4 = new THREE.Mesh(new THREE.DodecahedronGeometry(0.0159, 0), barkMat);
knot4.name = 'knot4';
knot4.position.set(-0.0441, 0.287, -0.0118);
root.add(knot4);
const trunk6 = new THREE.Mesh(new THREE.CylinderGeometry(0.0084, 0.012, 0.064, 6), barkMat);
trunk6.name = 'trunk6';
trunk6.position.set(-0.0417, 0.362, -0.0298);
trunk6.rotation.set(deg(-7.8283), deg(1.7051), deg(-24.5402));
root.add(trunk6);
const knot5 = new THREE.Mesh(new THREE.DodecahedronGeometry(0.0122, 0), barkMat);
knot5.name = 'knot5';
knot5.position.set(-0.0532, 0.337, -0.026);
root.add(knot5);
const root1 = new THREE.Mesh(new THREE.CylinderGeometry(0.004, 0.016, 0.0747, 5), barkMat);
root1.name = 'root1';
root1.position.set(-0.0141, 0.1015, 0.011);
root1.rotation.set(deg(11.9258), deg(-15.1006), deg(-103.5221));
root.add(root1);
const root2 = new THREE.Mesh(new THREE.CylinderGeometry(0.004, 0.016, 0.0788, 5), barkMat);
root2.name = 'root2';
root2.position.set(-0.0298, 0.1015, 0.0362);
root2.rotation.set(deg(85.6121), deg(-33.7798), deg(-36.3005));
root.add(root2);
const root3 = new THREE.Mesh(new THREE.CylinderGeometry(0.004, 0.016, 0.0812, 5), barkMat);
root3.name = 'root3';
root3.position.set(-0.0853, 0.1015, 0.019);
root3.rotation.set(deg(25.3586), deg(27.1437), deg(94.0342));
root.add(root3);
const root4 = new THREE.Mesh(new THREE.CylinderGeometry(0.004, 0.016, 0.0874, 5), barkMat);
root4.name = 'root4';
root4.position.set(-0.0846, 0.1015, -0.0198);
root4.rotation.set(deg(-40.5705), deg(-34.7502), deg(80.4997));
root.add(root4);
const root5 = new THREE.Mesh(new THREE.CylinderGeometry(0.004, 0.016, 0.0671, 5), barkMat);
root5.name = 'root5';
root5.position.set(-0.0413, 0.1015, -0.0273);
root5.rotation.set(deg(-104.2073), deg(18.3255), deg(-14.3121));
root.add(root5);
const moss = new THREE.Mesh(new THREE.SphereGeometry(0.075, 9, 3, 0, Math.PI * 2, 0, Math.PI / 2), mossMat);
moss.name = 'moss';
moss.position.set(-0.0492, 0.075, 0.0036);
moss.scale.set(1.3, 0.35, 1);
root.add(moss);
const stone = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.03, 0), 0.004, 6), stoneMat);
stone.name = 'stone';
stone.position.set(0.099, 0.1045, 0.0455);
stone.rotation.set(deg(20), deg(40), 0);
stone.scale.set(1.3, 0.7, 1);
root.add(stone);
const branch1 = new THREE.Mesh(new THREE.CylinderGeometry(0.0084, 0.0115, 0.0855, 5), barkMat);
branch1.name = 'branch1';
branch1.position.set(-0.0407, 0.2233, 0.0019);
branch1.rotation.set(deg(-10.3167), deg(-7.4873), deg(71.8704));
root.add(branch1);
const branch1Tip = new THREE.Mesh(new THREE.CylinderGeometry(0.0052, 0.0084, 0.0832, 5), barkMat);
branch1Tip.name = 'branch1Tip';
branch1Tip.position.set(-0.1137, 0.2283, -0.0118);
branch1Tip.rotation.set(deg(-10.7792), deg(-12.181), deg(97.0343));
root.add(branch1Tip);
const pad1 = new THREE.Mesh(jitter(new THREE.IcosahedronGeometry(0.0668, 1), 0.0053, 46), leafMat);
pad1.name = 'pad1';
pad1.position.set(-0.1502, 0.2398, -0.0187);
pad1.rotation.set(0, deg(97.4248), 0);
pad1.scale.set(1.25, 0.42, 1);
root.add(pad1);
const pad1Tuft1 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.0398, 0), 0.004, 78), leafLightMat);
pad1Tuft1.name = 'pad1Tuft1';
pad1Tuft1.position.set(-0.1387, 0.2498, -0.0509);
pad1Tuft1.rotation.set(deg(24.1267), deg(188.4649), 0);
pad1Tuft1.scale.set(1.2, 0.48, 1.1);
root.add(pad1Tuft1);
const pad1Tuft2 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.0431, 0), 0.0043, 79), leafDarkMat);
pad1Tuft2.name = 'pad1Tuft2';
pad1Tuft2.position.set(-0.1303, 0.2345, -0.0484);
pad1Tuft2.rotation.set(deg(6.7371), deg(165.189), 0);
pad1Tuft2.scale.set(1.2, 0.48, 1.1);
root.add(pad1Tuft2);
const branch2 = new THREE.Mesh(new THREE.CylinderGeometry(0.0068, 0.0094, 0.0912, 5), barkMat);
branch2.name = 'branch2';
branch2.position.set(-0.0015, 0.2935, -0.0062);
branch2.rotation.set(deg(-0.6241), deg(0.2999), deg(-51.3327));
root.add(branch2);
const branch2Tip = new THREE.Mesh(new THREE.CylinderGeometry(0.0043, 0.0068, 0.0741, 5), barkMat);
branch2Tip.name = 'branch2Tip';
branch2Tip.position.set(0.0635, 0.3135, -0.0071);
branch2Tip.rotation.set(deg(-0.7861), deg(0.9443), deg(-100.4485));
root.add(branch2Tip);
const pad2 = new THREE.Mesh(jitter(new THREE.IcosahedronGeometry(0.0602, 1), 0.0048, 46), leafMat);
pad2.name = 'pad2';
pad2.position.set(0.096, 0.3238, -0.0075);
pad2.rotation.set(0, deg(32.8232), 0);
pad2.scale.set(1.25, 0.42, 1);
root.add(pad2);
const pad2Tuft1 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.0407, 0), 0.0041, 78), leafLightMat);
pad2Tuft1.name = 'pad2Tuft1';
pad2Tuft1.position.set(0.1229, 0.3328, -0.0294);
pad2Tuft1.rotation.set(deg(37.3861), deg(26.1082), 0);
pad2Tuft1.scale.set(1.2, 0.48, 1.1);
root.add(pad2Tuft1);
const pad2Tuft2 = new THREE.Mesh(dodecahedronGeo, leafDarkMat);
pad2Tuft2.name = 'pad2Tuft2';
pad2Tuft2.position.set(0.1347, 0.3189, -0.0121);
pad2Tuft2.rotation.set(deg(11.1515), deg(195.4389), 0);
pad2Tuft2.scale.set(1.2, 0.48, 1.1);
root.add(pad2Tuft2);
const branch3 = new THREE.Mesh(new THREE.CylinderGeometry(0.0053, 0.0072, 0.0793, 5), barkMat);
branch3.name = 'branch3';
branch3.position.set(-0.0644, 0.3361, -0.0353);
branch3.rotation.set(deg(-28.8645), deg(-5.5386), deg(21.2895));
root.add(branch3);
const branch3Tip = new THREE.Mesh(new THREE.CylinderGeometry(0.0033, 0.0053, 0.0525, 5), barkMat);
branch3Tip.name = 'branch3Tip';
branch3Tip.position.set(-0.0901, 0.3586, -0.0695);
branch3Tip.rotation.set(deg(-76.1885), deg(-37.5766), deg(46.9181));
root.add(branch3Tip);
const pad3 = new THREE.Mesh(jitter(new THREE.IcosahedronGeometry(0.0579, 1), 0.0046, 46), leafMat);
pad3.name = 'pad3';
pad3.position.set(-0.103, 0.3671, -0.0867);
pad3.rotation.set(0, deg(238.8763), 0);
pad3.scale.set(1.25, 0.42, 1);
root.add(pad3);
const pad3Tuft1 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.033, 0), 0.0033, 78), leafLightMat);
pad3Tuft1.name = 'pad3Tuft1';
pad3Tuft1.position.set(-0.0727, 0.3758, -0.1038);
pad3Tuft1.rotation.set(deg(11.2761), deg(256.7619), 0);
pad3Tuft1.scale.set(1.2, 0.48, 1.1);
root.add(pad3Tuft1);
const pad3Tuft2 = new THREE.Mesh(dodecahedronGeo, leafDarkMat);
pad3Tuft2.name = 'pad3Tuft2';
pad3Tuft2.position.set(-0.091, 0.3625, -0.1141);
pad3Tuft2.rotation.set(deg(35.3374), deg(331.932), 0);
pad3Tuft2.scale.set(1.2, 0.48, 1.1);
root.add(pad3Tuft2);
const apex = new THREE.Mesh(jitter(new THREE.IcosahedronGeometry(0.075, 1), 0.006, 46), leafMat);
apex.name = 'apex';
apex.position.set(-0.0301, 0.407, -0.0336);
apex.rotation.set(0, deg(220.5175), 0);
apex.scale.set(1.25, 0.42, 1);
root.add(apex);
const apexTuft1 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.0515, 0), 0.0052, 78), leafLightMat);
apexTuft1.name = 'apexTuft1';
apexTuft1.position.set(-0.0086, 0.4183, -0.0672);
apexTuft1.rotation.set(deg(29.9446), deg(280.4837), 0);
apexTuft1.scale.set(1.2, 0.48, 1.1);
root.add(apexTuft1);
const apexTuft2 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.0468, 0), 0.0047, 79), leafDarkMat);
apexTuft2.name = 'apexTuft2';
apexTuft2.position.set(-0.0776, 0.401, -0.0419);
apexTuft2.rotation.set(deg(4.3419), deg(53.9511), 0);
apexTuft2.scale.set(1.2, 0.48, 1.1);
root.add(apexTuft2);
root.traverse((o) => {
if (o.isMesh) {
o.castShadow = true;
o.receiveShadow = true;
}
});
return root;
}
export default createBonsai;
Notes et commentaires
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









