Codels
928 三角面4.00 × 2.82 × 3.00 m

在代码中使用

在编辑器中改编
自包含的 ES 模块,仅依赖 three。调用工厂函数,然后把返回的 group 添加到场景中。
/**
 * Grass Platform — Codels 3D model library
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 928 · Size (W × H × D): 4.00 × 2.82 × 3.00 m
 *
 * Usage:
 *   import { createGrassPlatform } from './grass-platform.js';
 *   
 *   const model = createGrassPlatform();
 *   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 createGrassPlatform() {
  const root = new THREE.Group();
  root.name = 'GrassPlatform';

  // Materials
  const grassMat = new THREE.MeshStandardMaterial({ name: 'grass', color: 0x63c74a, roughness: 0.85, flatShading: true });
  const tuftMat = new THREE.MeshStandardMaterial({ name: 'tuft', color: 0x4ba13a, roughness: 0.9, flatShading: true });
  const dirtMat = new THREE.MeshStandardMaterial({ name: 'dirt', color: 0x9a6440, roughness: 0.95, flatShading: true });
  const rockMat = new THREE.MeshStandardMaterial({ name: 'rock', color: 0xa39482, roughness: 0.95, flatShading: true });
  const rockDarkMat = new THREE.MeshStandardMaterial({ name: 'rockDark', color: 0x877a6a, roughness: 0.95, flatShading: true });
  const petalMat = new THREE.MeshStandardMaterial({ name: 'petal', color: 0xfff1b8, roughness: 0.7, flatShading: true });
  const petalPinkMat = new THREE.MeshStandardMaterial({ name: 'petalPink', color: 0xff8fb3, roughness: 0.7, flatShading: true });

  // Shared geometries
  const cylinderGeo = new THREE.CylinderGeometry(0.016, 0.02, 0.22, 4);
  const sphereGeo = new THREE.SphereGeometry(0.075, 6, 4);

  // Parts
  const grassTop = new THREE.Mesh(new THREE.BoxGeometry(4, 0.3, 3), grassMat);
  grassTop.name = 'grassTop';
  grassTop.position.set(0, 2.35, 0);
  root.add(grassTop);
  const earth = new THREE.Mesh(new THREE.BoxGeometry(3.92, 0.6, 2.92), dirtMat);
  earth.name = 'earth';
  earth.position.set(0, 1.95, 0);
  root.add(earth);
  const fringe = new THREE.Group();
  fringe.name = 'fringe';
  root.add(fringe);
  const box4 = new THREE.Mesh(new THREE.BoxGeometry(0.2692, 0.2881, 0.06), grassMat);
  box4.position.set(-1.706, 2.0759, 1.464);
  fringe.add(box4);
  const box5 = new THREE.Mesh(new THREE.BoxGeometry(0.1978, 0.2025, 0.06), grassMat);
  box5.position.set(-1.2489, 2.1188, 1.464);
  fringe.add(box5);
  const box6 = new THREE.Mesh(new THREE.BoxGeometry(0.2621, 0.2696, 0.06), grassMat);
  box6.position.set(-0.8576, 2.0852, 1.464);
  fringe.add(box6);
  const box7 = new THREE.Mesh(new THREE.BoxGeometry(0.2003, 0.1595, 0.06), grassMat);
  box7.position.set(-0.4062, 2.1402, 1.464);
  fringe.add(box7);
  const box8 = new THREE.Mesh(new THREE.BoxGeometry(0.2134, 0.136, 0.06), grassMat);
  box8.position.set(-0.0083, 2.152, 1.464);
  fringe.add(box8);
  const box9 = new THREE.Mesh(new THREE.BoxGeometry(0.2223, 0.1933, 0.06), grassMat);
  box9.position.set(0.4473, 2.1234, 1.464);
  fringe.add(box9);
  const box10 = new THREE.Mesh(new THREE.BoxGeometry(0.2254, 0.2128, 0.06), grassMat);
  box10.position.set(0.853, 2.1136, 1.464);
  fringe.add(box10);
  const box11 = new THREE.Mesh(new THREE.BoxGeometry(0.2932, 0.2664, 0.06), grassMat);
  box11.position.set(1.2579, 2.0868, 1.464);
  fringe.add(box11);
  const box12 = new THREE.Mesh(new THREE.BoxGeometry(0.3011, 0.1794, 0.06), grassMat);
  box12.position.set(1.7208, 2.1303, 1.464);
  fringe.add(box12);
  const box13 = new THREE.Mesh(new THREE.BoxGeometry(0.2327, 0.2483, 0.06), grassMat);
  box13.position.set(-1.6902, 2.0959, -1.464);
  fringe.add(box13);
  const box14 = new THREE.Mesh(new THREE.BoxGeometry(0.2091, 0.2317, 0.06), grassMat);
  box14.position.set(-1.2866, 2.1042, -1.464);
  fringe.add(box14);
  const box15 = new THREE.Mesh(new THREE.BoxGeometry(0.2884, 0.2954, 0.06), grassMat);
  box15.position.set(-0.8604, 2.0723, -1.464);
  fringe.add(box15);
  const box16 = new THREE.Mesh(new THREE.BoxGeometry(0.2001, 0.2461, 0.06), grassMat);
  box16.position.set(-0.4492, 2.097, -1.464);
  fringe.add(box16);
  const box17 = new THREE.Mesh(new THREE.BoxGeometry(0.3052, 0.2196, 0.06), grassMat);
  box17.position.set(-0.0023, 2.1102, -1.464);
  fringe.add(box17);
  const box18 = new THREE.Mesh(new THREE.BoxGeometry(0.2381, 0.2101, 0.06), grassMat);
  box18.position.set(0.4115, 2.115, -1.464);
  fringe.add(box18);
  const box19 = new THREE.Mesh(new THREE.BoxGeometry(0.2496, 0.1668, 0.06), grassMat);
  box19.position.set(0.8867, 2.1366, -1.464);
  fringe.add(box19);
  const box20 = new THREE.Mesh(new THREE.BoxGeometry(0.2937, 0.2221, 0.06), grassMat);
  box20.position.set(1.3107, 2.109, -1.464);
  fringe.add(box20);
  const box21 = new THREE.Mesh(new THREE.BoxGeometry(0.2209, 0.1935, 0.06), grassMat);
  box21.position.set(1.6933, 2.1233, -1.464);
  fringe.add(box21);
  const box22 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.2584, 0.2464), grassMat);
  box22.position.set(1.964, 2.0908, -1.1918);
  fringe.add(box22);
  const box23 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.2334, 0.2576), grassMat);
  box23.position.set(1.964, 2.1033, -0.8121);
  fringe.add(box23);
  const box24 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.1804, 0.2542), grassMat);
  box24.position.set(1.964, 2.1298, -0.4134);
  fringe.add(box24);
  const box25 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.1209, 0.2447), grassMat);
  box25.position.set(1.964, 2.1596, -0.0312);
  fringe.add(box25);
  const box26 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.271, 0.2245), grassMat);
  box26.position.set(1.964, 2.0845, 0.4042);
  fringe.add(box26);
  const box27 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.1616, 0.2299), grassMat);
  box27.position.set(1.964, 2.1392, 0.817);
  fringe.add(box27);
  const box28 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.224, 0.189), grassMat);
  box28.position.set(1.964, 2.108, 1.2155);
  fringe.add(box28);
  const box29 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.2212, 0.2547), grassMat);
  box29.position.set(-1.964, 2.1094, -1.1865);
  fringe.add(box29);
  const box30 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.1474, 0.2318), grassMat);
  box30.position.set(-1.964, 2.1463, -0.7795);
  fringe.add(box30);
  const box31 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.1605, 0.1986), grassMat);
  box31.position.set(-1.964, 2.1398, -0.4364);
  fringe.add(box31);
  const box32 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.2068, 0.2004), grassMat);
  box32.position.set(-1.964, 2.1166, 0.0249);
  fringe.add(box32);
  const box33 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.1637, 0.1844), grassMat);
  box33.position.set(-1.964, 2.1381, 0.4225);
  fringe.add(box33);
  const box34 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.2366, 0.1984), grassMat);
  box34.position.set(-1.964, 2.1017, 0.8361);
  fringe.add(box34);
  const box35 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.1726, 0.2424), grassMat);
  box35.position.set(-1.964, 2.1337, 1.2494);
  fringe.add(box35);
  const underside = new THREE.Group();
  underside.name = 'underside';
  underside.scale.set(3.9, 1, 2.9);
  root.add(underside);
  const rockBody = new THREE.Mesh(jitter(new THREE.CylinderGeometry(0.6859, 0.297, 1.073, 4, 3), 0.035, 5), rockMat);
  rockBody.name = 'rockBody';
  rockBody.position.set(0, 1.1635, 0);
  rockBody.rotation.set(0, deg(45), 0);
  underside.add(rockBody);
  const tip = new THREE.Mesh(new THREE.ConeGeometry(0.6, 1.0025, 5), rockDarkMat);
  tip.name = 'tip';
  tip.position.set(0, 0.5013, 0);
  tip.rotation.set(deg(180), deg(52.3075), 0);
  root.add(tip);
  const stalactite1 = new THREE.Mesh(new THREE.ConeGeometry(0.372, 0.3333, 5), rockDarkMat);
  stalactite1.name = 'stalactite1';
  stalactite1.position.set(0.7367, 0.7822, 0.1905);
  stalactite1.rotation.set(deg(180), deg(3.242), 0);
  root.add(stalactite1);
  const stalactite2 = new THREE.Mesh(new THREE.ConeGeometry(0.3831, 0.5308, 5), rockMat);
  stalactite2.name = 'stalactite2';
  stalactite2.position.set(-0.1318, 0.6835, 0.5717);
  stalactite2.rotation.set(deg(180), deg(24.0697), 0);
  root.add(stalactite2);
  const stalactite3 = new THREE.Mesh(new THREE.ConeGeometry(0.3366, 0.5336, 5), rockDarkMat);
  stalactite3.name = 'stalactite3';
  stalactite3.position.set(-0.3429, 0.6821, -0.5209);
  stalactite3.rotation.set(deg(180), deg(42.4591), 0);
  root.add(stalactite3);
  const boulder1 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.32, 0), 0.0384, 20), rockDarkMat);
  boulder1.name = 'boulder1';
  boulder1.position.set(1.4953, 1.4562, -0.1806);
  root.add(boulder1);
  const boulder2 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.32, 0), 0.0384, 21), rockDarkMat);
  boulder2.name = 'boulder2';
  boulder2.position.set(-1.3801, 1.4142, -0.4029);
  root.add(boulder2);
  const tuft1 = new THREE.Group();
  tuft1.name = 'tuft1';
  tuft1.position.set(-0.8231, 2.5, -0.5925);
  root.add(tuft1);
  const cone45 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.3368, 4), tuftMat);
  cone45.position.set(0.0699, 0.1384, 0.0029);
  cone45.rotation.set(deg(0.5873), deg(4.3031), deg(-13.9877));
  tuft1.add(cone45);
  const cone46 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.3461, 4), tuftMat);
  cone46.position.set(-0.0316, 0.1431, 0.0625);
  cone46.rotation.set(deg(12.4903), deg(22.2316), deg(6.3239));
  tuft1.add(cone46);
  const cone47 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.3384, 4), tuftMat);
  cone47.position.set(-0.0317, 0.1392, -0.0624);
  cone47.rotation.set(deg(-12.4845), deg(4.5793), deg(6.3355));
  tuft1.add(cone47);
  const tuft2 = new THREE.Group();
  tuft2.name = 'tuft2';
  tuft2.position.set(-0.8622, 2.5, 0.609);
  root.add(tuft2);
  const cone49 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.3117, 4), tuftMat);
  cone49.position.set(0.0684, 0.1259, 0.0148);
  cone49.rotation.set(deg(2.9506), deg(28.3553), deg(-13.6855));
  tuft2.add(cone49);
  const cone50 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.3053, 4), tuftMat);
  cone50.position.set(-0.0158, 0.1227, 0.0682);
  cone50.rotation.set(deg(13.6373), deg(27.7775), deg(3.1661));
  tuft2.add(cone50);
  const cone51 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.2609, 4), tuftMat);
  cone51.position.set(-0.049, 0.1005, -0.05);
  cone51.rotation.set(deg(-9.9981), deg(41.9708), deg(9.7999));
  tuft2.add(cone51);
  const tuft3 = new THREE.Group();
  tuft3.name = 'tuft3';
  tuft3.position.set(-0.8815, 2.5, 1.1204);
  root.add(tuft3);
  const cone53 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.3223, 4), tuftMat);
  cone53.position.set(0.0662, 0.1312, 0.0226);
  cone53.rotation.set(deg(4.5289), deg(36.0245), deg(-13.2472));
  tuft3.add(cone53);
  const cone54 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.2232, 4), tuftMat);
  cone54.position.set(-0.0251, 0.0816, 0.0653);
  cone54.rotation.set(deg(13.0655), deg(18.5439), deg(5.0291));
  tuft3.add(cone54);
  const cone55 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.2324, 4), tuftMat);
  cone55.position.set(-0.0195, 0.0862, -0.0672);
  cone55.rotation.set(deg(-13.4442), deg(8.0463), deg(3.9054));
  tuft3.add(cone55);
  const tuft4 = new THREE.Group();
  tuft4.name = 'tuft4';
  tuft4.position.set(1.7782, 2.5, 1.167);
  root.add(tuft4);
  const cone57 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.3133, 4), tuftMat);
  cone57.position.set(0.0629, 0.1267, -0.0307);
  cone57.rotation.set(deg(-6.1346), deg(84.4887), deg(-12.5844));
  tuft4.add(cone57);
  const cone58 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.2395, 4), tuftMat);
  cone58.position.set(-0.0311, 0.0898, 0.0627);
  cone58.rotation.set(deg(12.5451), deg(84.4965), deg(6.2145));
  tuft4.add(cone58);
  const cone59 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.34, 4), tuftMat);
  cone59.position.set(-0.0331, 0.14, -0.0617);
  cone59.rotation.set(deg(-12.3401), deg(56.3412), deg(6.6123));
  tuft4.add(cone59);
  const tuft5 = new THREE.Group();
  tuft5.name = 'tuft5';
  tuft5.position.set(0.7444, 2.5, 0.7846);
  root.add(tuft5);
  const cone61 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.2302, 4), tuftMat);
  cone61.position.set(0.0697, 0.0851, -0.0068);
  cone61.rotation.set(deg(-1.3508), deg(7.2705), deg(-13.9347));
  tuft5.add(cone61);
  const cone62 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.3239, 4), tuftMat);
  cone62.position.set(-0.0217, 0.132, 0.0666);
  cone62.rotation.set(deg(13.3133), deg(3.6086), deg(4.3307));
  tuft5.add(cone62);
  const cone63 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.2621, 4), tuftMat);
  cone63.position.set(-0.051, 0.1011, -0.048);
  cone63.rotation.set(deg(-9.5984), deg(23.7475), deg(10.1917));
  tuft5.add(cone63);
  const tuft6 = new THREE.Group();
  tuft6.name = 'tuft6';
  tuft6.position.set(-1.0249, 2.5, -0.3635);
  root.add(tuft6);
  const cone65 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.3035, 4), tuftMat);
  cone65.position.set(0.0685, 0.1218, 0.0145);
  cone65.rotation.set(deg(2.9003), deg(83.9748), deg(-13.6963));
  tuft6.add(cone65);
  const cone66 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.2858, 4), tuftMat);
  cone66.position.set(-0.0554, 0.1129, 0.0429);
  cone66.rotation.set(deg(8.5702), deg(73.2091), deg(11.0703));
  tuft6.add(cone66);
  const cone67 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.2387, 4), tuftMat);
  cone67.position.set(-0.0407, 0.0894, -0.057);
  cone67.rotation.set(deg(-11.3937), deg(68.0117), deg(8.1354));
  tuft6.add(cone67);
  const tuft7 = new THREE.Group();
  tuft7.name = 'tuft7';
  tuft7.position.set(-1.4803, 2.5, -0.18);
  root.add(tuft7);
  const cone69 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.2969, 4), tuftMat);
  cone69.position.set(0.0676, 0.1185, -0.0183);
  cone69.rotation.set(deg(-3.6551), deg(82.0903), deg(-13.5144));
  tuft7.add(cone69);
  const cone70 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.3562, 4), tuftMat);
  cone70.position.set(-0.0573, 0.1481, 0.0401);
  cone70.rotation.set(deg(8.0292), deg(66.6529), deg(11.4687));
  tuft7.add(cone70);
  const cone71 = new THREE.Mesh(new THREE.ConeGeometry(0.065, 0.2319, 4), tuftMat);
  cone71.position.set(-0.0359, 0.086, -0.0601);
  cone71.rotation.set(deg(-12.0189), deg(70.6536), deg(7.1796));
  tuft7.add(cone71);
  const flower1 = new THREE.Group();
  flower1.name = 'flower1';
  flower1.position.set(1.7318, 2.5, 0.3789);
  root.add(flower1);
  const cylinder73 = new THREE.Mesh(cylinderGeo, tuftMat);
  cylinder73.position.set(0, 0.09, 0);
  flower1.add(cylinder73);
  const sphere74 = new THREE.Mesh(sphereGeo, petalMat);
  sphere74.position.set(0, 0.21, 0);
  sphere74.scale.set(1, 0.7, 1);
  flower1.add(sphere74);
  const flower2 = new THREE.Group();
  flower2.name = 'flower2';
  flower2.position.set(-0.3844, 2.5, 0.1216);
  root.add(flower2);
  const cylinder76 = new THREE.Mesh(cylinderGeo, tuftMat);
  cylinder76.position.set(0, 0.09, 0);
  flower2.add(cylinder76);
  const sphere77 = new THREE.Mesh(sphereGeo, petalMat);
  sphere77.position.set(0, 0.21, 0);
  sphere77.scale.set(1, 0.7, 1);
  flower2.add(sphere77);
  const flower3 = new THREE.Group();
  flower3.name = 'flower3';
  flower3.position.set(0.7658, 2.5, -0.0129);
  root.add(flower3);
  const cylinder79 = new THREE.Mesh(cylinderGeo, tuftMat);
  cylinder79.position.set(0, 0.09, 0);
  flower3.add(cylinder79);
  const sphere80 = new THREE.Mesh(sphereGeo, petalPinkMat);
  sphere80.position.set(0, 0.21, 0);
  sphere80.scale.set(1, 0.7, 1);
  flower3.add(sphere80);
  const flower4 = new THREE.Group();
  flower4.name = 'flower4';
  flower4.position.set(0.1232, 2.5, 0.75);
  root.add(flower4);
  const cylinder82 = new THREE.Mesh(cylinderGeo, tuftMat);
  cylinder82.position.set(0, 0.09, 0);
  flower4.add(cylinder82);
  const sphere83 = new THREE.Mesh(sphereGeo, petalMat);
  sphere83.position.set(0, 0.21, 0);
  sphere83.scale.set(1, 0.7, 1);
  flower4.add(sphere83);

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

export default createGrassPlatform;

评分与评论

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

登录后即可评分和评论。

0 条评论

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

相关模型

同一分类或相似标签