Codels
1369 三角面0.40 × 0.45 × 0.29 m

在代码中使用

在编辑器中改编
自包含的 ES 模块,仅依赖 three。调用工厂函数,然后把返回的 group 添加到场景中。
/**
 * 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;

评分与评论

—
暂无评分,快来抢先评分吧。
5
0
4
0
3
0
2
0
1
0

登录后即可评分和评论。

0 条评论

暂无评论。分享一下你是如何使用这个模型的,或者有哪些可以改进的地方。

相关模型

同一分类或相似标签