Codels
642 üçgen2,00 × 0,75 × 2,00 m
Bağımsız bir ES modülü — yalnızca three paketine bağımlıdır. Fabrika fonksiyonunu çağırıp dönen grubu sahnene ekle.
/**
 * Dirt Path Tile — Codels 3D model library
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 642 · Size (W × H × D): 2.00 × 0.75 × 2.00 m
 *
 * Usage:
 *   import { createDirtPath } from './dirt-path.js';
 *   
 *   const model = createDirtPath();
 *   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;
}

// 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 createDirtPath() {
  const root = new THREE.Group();
  root.name = 'DirtPath';

  // Materials
  const grassMat = new THREE.MeshStandardMaterial({ name: 'grass', color: 0x6aae45, roughness: 0.9, flatShading: true });
  const grassDarkMat = new THREE.MeshStandardMaterial({ name: 'grassDark', color: 0x5b8836, roughness: 0.9, flatShading: true });
  const grassLightMat = new THREE.MeshStandardMaterial({ name: 'grassLight', color: 0x72ba59, roughness: 0.9, flatShading: true });
  const pathMat = new THREE.MeshStandardMaterial({ name: 'path', color: 0xad7f50, roughness: 0.95, flatShading: true });
  const pathDarkMat = new THREE.MeshStandardMaterial({ name: 'pathDark', color: 0x8f6640, roughness: 0.95, flatShading: true });
  const dirtMat = new THREE.MeshStandardMaterial({ name: 'dirt', color: 0x8b5d3b, roughness: 0.95, flatShading: true });
  const dirtDeepMat = new THREE.MeshStandardMaterial({ name: 'dirtDeep', color: 0x6a4630, roughness: 0.95, flatShading: true });
  const stoneMat = new THREE.MeshStandardMaterial({ name: 'stone', color: 0xa29d93, roughness: 0.9, flatShading: true });

  // Parts
  const dirt = new THREE.Mesh(new THREE.BoxGeometry(1.92, 0.209, 1.92), dirtMat);
  dirt.name = 'dirt';
  dirt.position.set(0, 0.2755, 0);
  root.add(dirt);
  const dirtDeep = new THREE.Mesh(new THREE.BoxGeometry(1.92, 0.171, 1.92), dirtDeepMat);
  dirtDeep.name = 'dirtDeep';
  dirtDeep.position.set(0, 0.0855, 0);
  root.add(dirtDeep);
  const grass1 = new THREE.Mesh(extrude([[-1, 1], [-0.5, 1], [-0.5, -1], [-1, -1]], 0.12), grassMat);
  grass1.name = 'grass1';
  grass1.position.set(0, 0.44, 0);
  grass1.rotation.set(deg(-90), 0, 0);
  root.add(grass1);
  const grass2 = new THREE.Mesh(extrude([[0.5, 1], [1, 1], [1, -1], [0.5, -1]], 0.12), grassMat);
  grass2.name = 'grass2';
  grass2.position.set(0, 0.44, 0);
  grass2.rotation.set(deg(-90), 0, 0);
  root.add(grass2);
  const path = new THREE.Mesh(extrude([[-0.5, 1], [0.5, 1], [0.5, -1], [-0.5, -1]], 0.07), pathMat);
  path.name = 'path';
  path.position.set(0, 0.415, 0);
  path.rotation.set(deg(-90), 0, 0);
  root.add(path);
  const fringe = new THREE.Group();
  fringe.name = 'fringe';
  root.add(fringe);
  const box7 = new THREE.Mesh(new THREE.BoxGeometry(0.2156, 0.1798, 0.04), grassMat);
  box7.position.set(-0.7979, 0.3101, 0.976);
  fringe.add(box7);
  const box8 = new THREE.Mesh(new THREE.BoxGeometry(0.2158, 0.0874, 0.04), grassMat);
  box8.position.set(-0.7877, 0.3563, -0.976);
  fringe.add(box8);
  const box9 = new THREE.Mesh(new THREE.BoxGeometry(0.1905, 0.1457, 0.04), grassMat);
  box9.position.set(0.7994, 0.3272, -0.976);
  fringe.add(box9);
  const box10 = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.115, 0.17), grassMat);
  box10.position.set(0.976, 0.3425, -0.7853);
  fringe.add(box10);
  const box11 = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.1113, 0.1554), grassMat);
  box11.position.set(0.976, 0.3443, -0.454);
  fringe.add(box11);
  const box12 = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.1094, 0.2055), grassMat);
  box12.position.set(0.976, 0.3453, -0.1871);
  fringe.add(box12);
  const box13 = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.0851, 0.1682), grassMat);
  box13.position.set(0.976, 0.3575, 0.4796);
  fringe.add(box13);
  const box14 = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.1712, 0.1956), grassMat);
  box14.position.set(0.976, 0.3144, 0.7785);
  fringe.add(box14);
  const box15 = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.0954, 0.1502), grassMat);
  box15.position.set(-0.976, 0.3523, -0.8074);
  fringe.add(box15);
  const box16 = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.1574, 0.1859), grassMat);
  box16.position.set(-0.976, 0.3213, -0.4916);
  fringe.add(box16);
  const box17 = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.1761, 0.1961), grassMat);
  box17.position.set(-0.976, 0.3119, -0.1584);
  fringe.add(box17);
  const box18 = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.1394, 0.1513), grassMat);
  box18.position.set(-0.976, 0.3303, 0.4579);
  fringe.add(box18);
  const dressing = new THREE.Group();
  dressing.name = 'dressing';
  root.add(dressing);
  const worn1 = new THREE.Mesh(extrude([[-0.0745, -0.6995], [-0.0305, -0.6409], [0.1293, -0.5974], [0.2275, -0.6875], [0.221, -0.7705], [0.089, -0.8382], [-0.0519, -0.7892]], 0.006), pathDarkMat);
  worn1.name = 'worn1';
  worn1.position.set(0, 0.453, 0);
  worn1.rotation.set(deg(-90), 0, 0);
  dressing.add(worn1);
  const worn2 = new THREE.Mesh(extrude([[-0.2011, 0.1627], [-0.0227, 0.1906], [0.1199, 0.1542], [0.1077, 0.0494], [-0.0323, -0.0422], [-0.1744, -0.0404], [-0.2242, 0.0878]], 0.006), pathDarkMat);
  worn2.name = 'worn2';
  worn2.position.set(0, 0.453, 0);
  worn2.rotation.set(deg(-90), 0, 0);
  dressing.add(worn2);
  const worn3 = new THREE.Mesh(extrude([[-0.2487, 0.5241], [-0.2589, 0.614], [-0.0921, 0.6962], [0.0827, 0.6296], [0.0727, 0.5352], [0.038, 0.4739], [-0.1518, 0.4463]], 0.006), pathDarkMat);
  worn3.name = 'worn3';
  worn3.position.set(0, 0.453, 0);
  worn3.rotation.set(deg(-90), 0, 0);
  dressing.add(worn3);
  const pebble1 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.0545, 0), 0.0098, 933), pathDarkMat);
  pebble1.name = 'pebble1';
  pebble1.position.set(-0.3046, 0.4741, 0.5199);
  pebble1.rotation.set(deg(-5.1355), deg(195.0355), deg(-0.6867));
  pebble1.scale.set(1.0611, 0.514, 1);
  dressing.add(pebble1);
  const pebble2 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.0551, 0), 0.0099, 109), stoneMat);
  pebble2.name = 'pebble2';
  pebble2.position.set(-0.1056, 0.4744, -0.2261);
  pebble2.rotation.set(deg(-1.9387), deg(38.0282), deg(4.9743));
  pebble2.scale.set(1.0729, 0.5148, 1);
  dressing.add(pebble2);
  const pebble3 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.0477, 0), 0.0086, 1), pathDarkMat);
  pebble3.name = 'pebble3';
  pebble3.position.set(-0.0891, 0.4771, 0.725);
  pebble3.rotation.set(deg(4.1901), deg(349.2382), deg(-4.5143));
  pebble3.scale.set(1.2568, 0.6166, 1);
  dressing.add(pebble3);
  const pebble4 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.0425, 0), 0.0076, 611), stoneMat);
  pebble4.name = 'pebble4';
  pebble4.position.set(-0.3112, 0.4724, 0.5891);
  pebble4.rotation.set(deg(-2.0773), deg(201.5282), deg(4.8889));
  pebble4.scale.set(1.3485, 0.5867, 1);
  dressing.add(pebble4);
  const pebble5 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.0387, 0), 0.007, 420), pathDarkMat);
  pebble5.name = 'pebble5';
  pebble5.position.set(0.0442, 0.4686, -0.6624);
  pebble5.rotation.set(deg(-0.0128), deg(135.6584), deg(4.3936));
  pebble5.scale.set(1.0926, 0.5917, 1);
  dressing.add(pebble5);
  const tuft1 = new THREE.Group();
  tuft1.name = 'tuft1';
  tuft1.position.set(-0.6526, 0.5, 0.8333);
  dressing.add(tuft1);
  const cone29 = new THREE.Mesh(new THREE.ConeGeometry(0.0438, 0.1994, 3), grassDarkMat);
  cone29.position.set(0.0356, 0.0797, -0.017);
  cone29.rotation.set(deg(137.8751), deg(61.9931), deg(-142.6378));
  cone29.scale.set(1, 1, 0.45);
  tuft1.add(cone29);
  const cone30 = new THREE.Mesh(new THREE.ConeGeometry(0.0438, 0.1445, 3), grassMat);
  cone30.position.set(-0.0122, 0.0522, 0.0375);
  cone30.rotation.set(deg(-128.7672), deg(77.9074), deg(148.1694));
  cone30.scale.set(1, 1, 0.45);
  tuft1.add(cone30);
  const cone31 = new THREE.Mesh(new THREE.ConeGeometry(0.0438, 0.2362, 3), grassLightMat);
  cone31.position.set(-0.0205, 0.0981, -0.0337);
  cone31.rotation.set(deg(-23.7899), deg(23.2306), deg(12.1636));
  cone31.scale.set(1, 1, 0.45);
  tuft1.add(cone31);
  const tuft2 = new THREE.Group();
  tuft2.name = 'tuft2';
  tuft2.position.set(0.6687, 0.5, 0.5);
  dressing.add(tuft2);
  const cone33 = new THREE.Mesh(new THREE.ConeGeometry(0.0582, 0.2643, 3), grassDarkMat);
  cone33.position.set(0.0482, 0.1122, -0.0206);
  cone33.rotation.set(deg(0.8922), deg(27.02), deg(-11.8179));
  cone33.scale.set(1, 1, 0.45);
  tuft2.add(cone33);
  const cone34 = new THREE.Mesh(new THREE.ConeGeometry(0.0582, 0.1896, 3), grassMat);
  cone34.position.set(-0.016, 0.0748, 0.0499);
  cone34.rotation.set(deg(8.4007), deg(37.9594), deg(4.5259));
  cone34.scale.set(1, 1, 0.45);
  tuft2.add(cone34);
  const cone35 = new THREE.Mesh(new THREE.ConeGeometry(0.0582, 0.272, 3), grassLightMat);
  cone35.position.set(-0.0428, 0.116, -0.0302);
  cone35.rotation.set(deg(-17.7681), deg(35.5851), deg(15.1477));
  cone35.scale.set(1, 1, 0.45);
  tuft2.add(cone35);
  const tuft3 = new THREE.Group();
  tuft3.name = 'tuft3';
  tuft3.position.set(-0.7174, 0.5, 0.1667);
  dressing.add(tuft3);
  const cone37 = new THREE.Mesh(new THREE.ConeGeometry(0.0575, 0.229, 3), grassDarkMat);
  cone37.position.set(0.0506, 0.0945, -0.0105);
  cone37.rotation.set(deg(18.6912), deg(59.3058), deg(-24.3468));
  cone37.scale.set(1, 1, 0.45);
  tuft3.add(cone37);
  const cone38 = new THREE.Mesh(new THREE.ConeGeometry(0.0575, 0.1917, 3), grassMat);
  cone38.position.set(-0.0235, 0.0759, 0.0461);
  cone38.rotation.set(deg(13.9972), deg(27.5981), deg(10.6038));
  cone38.scale.set(1, 1, 0.45);
  tuft3.add(cone38);
  const cone39 = new THREE.Mesh(new THREE.ConeGeometry(0.0575, 0.2161, 3), grassLightMat);
  cone39.position.set(-0.0474, 0.0881, 0.0208);
  cone39.rotation.set(deg(-31.2689), deg(69.5293), deg(38.7129));
  cone39.scale.set(1, 1, 0.45);
  tuft3.add(cone39);
  const cone40 = new THREE.Mesh(new THREE.ConeGeometry(0.0575, 0.1934, 3), grassDarkMat);
  cone40.position.set(-0.0097, 0.0767, -0.0508);
  cone40.rotation.set(deg(-13.3009), deg(39.6291), deg(2.8417));
  cone40.scale.set(1, 1, 0.45);
  tuft3.add(cone40);
  const tuft4 = new THREE.Group();
  tuft4.name = 'tuft4';
  tuft4.position.set(0.6365, 0.5, -0.1667);
  dressing.add(tuft4);
  const cone42 = new THREE.Mesh(new THREE.ConeGeometry(0.0457, 0.2179, 3), grassDarkMat);
  cone42.position.set(0.0406, 0.089, -0.0069);
  cone42.rotation.set(deg(15.7345), deg(58.6176), deg(-20.3068));
  cone42.scale.set(1, 1, 0.45);
  tuft4.add(cone42);
  const cone43 = new THREE.Mesh(new THREE.ConeGeometry(0.0457, 0.2491, 3), grassMat);
  cone43.position.set(0.0033, 0.1045, 0.041);
  cone43.rotation.set(deg(13.8075), deg(72.0061), deg(-2.895));
  cone43.scale.set(1, 1, 0.45);
  tuft4.add(cone43);
  const cone44 = new THREE.Mesh(new THREE.ConeGeometry(0.0457, 0.2183, 3), grassLightMat);
  cone44.position.set(-0.0375, 0.0892, -0.0168);
  cone44.rotation.set(deg(-19.6268), deg(52.2198), deg(18.241));
  cone44.scale.set(1, 1, 0.45);
  tuft4.add(cone44);
  const cone45 = new THREE.Mesh(new THREE.ConeGeometry(0.0457, 0.1747, 3), grassDarkMat);
  cone45.position.set(-0.0089, 0.0673, -0.0402);
  cone45.rotation.set(deg(-19.8908), deg(-0.1665), deg(4.2957));
  cone45.scale.set(1, 1, 0.45);
  tuft4.add(cone45);
  const tuft5 = new THREE.Group();
  tuft5.name = 'tuft5';
  tuft5.position.set(-0.6564, 0.5, -0.5);
  dressing.add(tuft5);
  const cone47 = new THREE.Mesh(new THREE.ConeGeometry(0.056, 0.1941, 3), grassDarkMat);
  cone47.position.set(0.0477, 0.077, 0.0161);
  cone47.rotation.set(deg(75.1591), deg(70.4397), deg(-69.8548));
  cone47.scale.set(1, 1, 0.45);
  tuft5.add(cone47);
  const cone48 = new THREE.Mesh(new THREE.ConeGeometry(0.056, 0.22, 3), grassMat);
  cone48.position.set(0.0175, 0.09, 0.0472);
  cone48.rotation.set(deg(20.5885), deg(29.7417), deg(-7.1509));
  cone48.scale.set(1, 1, 0.45);
  tuft5.add(cone48);
  const cone49 = new THREE.Mesh(new THREE.ConeGeometry(0.056, 0.1865, 3), grassLightMat);
  cone49.position.set(-0.0502, 0.0732, 0.0033);
  cone49.rotation.set(deg(-42.5896), deg(59.9304), deg(48.2469));
  cone49.scale.set(1, 1, 0.45);
  tuft5.add(cone49);
  const cone50 = new THREE.Mesh(new THREE.ConeGeometry(0.056, 0.2321, 3), grassDarkMat);
  cone50.position.set(-0.0045, 0.096, -0.0502);
  cone50.rotation.set(deg(167.9905), deg(60.6934), deg(177.4429));
  cone50.scale.set(1, 1, 0.45);
  tuft5.add(cone50);
  const tuft6 = new THREE.Group();
  tuft6.name = 'tuft6';
  tuft6.position.set(0.6339, 0.5, -0.8333);
  dressing.add(tuft6);
  const cone52 = new THREE.Mesh(new THREE.ConeGeometry(0.0554, 0.233, 3), grassDarkMat);
  cone52.position.set(0.0448, 0.0965, -0.0219);
  cone52.rotation.set(deg(143.7016), deg(56.8693), deg(-147.8669));
  cone52.scale.set(1, 1, 0.45);
  tuft6.add(cone52);
  const cone53 = new THREE.Mesh(new THREE.ConeGeometry(0.0554, 0.1875, 3), grassMat);
  cone53.position.set(-0.038, 0.0737, 0.0322);
  cone53.rotation.set(deg(-37.4615), deg(77.2868), deg(45.9832));
  cone53.scale.set(1, 1, 0.45);
  tuft6.add(cone53);
  const cone54 = new THREE.Mesh(new THREE.ConeGeometry(0.0554, 0.2523, 3), grassLightMat);
  cone54.position.set(-0.0024, 0.1062, -0.0498);
  cone54.rotation.set(deg(-14.632), deg(18.2907), deg(0.7153));
  cone54.scale.set(1, 1, 0.45);
  tuft6.add(cone54);
  const grassTuft1 = new THREE.Group();
  grassTuft1.name = 'grassTuft1';
  grassTuft1.position.set(0.7902, 0.5, -0.301);
  dressing.add(grassTuft1);
  const cone56 = new THREE.Mesh(new THREE.ConeGeometry(0.0469, 0.2307, 3), grassDarkMat);
  cone56.position.set(0.0419, 0.0954, 0.0057);
  cone56.rotation.set(deg(31.6252), deg(60.9441), deg(-32.8936));
  cone56.scale.set(1, 1, 0.45);
  grassTuft1.add(cone56);
  const cone57 = new THREE.Mesh(new THREE.ConeGeometry(0.0469, 0.1819, 3), grassMat);
  cone57.position.set(0.0023, 0.0709, 0.0422);
  cone57.rotation.set(deg(17.0975), deg(10.3802), deg(-0.9327));
  cone57.scale.set(1, 1, 0.45);
  grassTuft1.add(cone57);
  const cone58 = new THREE.Mesh(new THREE.ConeGeometry(0.0469, 0.1635, 3), grassLightMat);
  cone58.position.set(-0.0411, 0.0618, -0.01);
  cone58.rotation.set(deg(-41.5395), deg(60.7782), deg(40.6695));
  cone58.scale.set(1, 1, 0.45);
  grassTuft1.add(cone58);
  const cone59 = new THREE.Mesh(new THREE.ConeGeometry(0.0469, 0.206, 3), grassDarkMat);
  cone59.position.set(0.015, 0.083, -0.0395);
  cone59.rotation.set(deg(-7.2668), deg(31.1577), deg(-4.1385));
  cone59.scale.set(1, 1, 0.45);
  grassTuft1.add(cone59);
  const stone6 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.0753, 0), 0.0136, 359), stoneMat);
  stone6.name = 'stone6';
  stone6.position.set(0.7284, 0.5341, 0.7369);
  stone6.rotation.set(deg(3.3452), deg(84.1166), deg(-0.133));
  stone6.scale.set(1.0806, 0.5508, 1);
  dressing.add(stone6);

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

export default createDirtPath;

Puanlar ve yorumlar

—
Henüz puan yok — ilk puanı sen ver.
5
0
4
0
3
0
2
0
1
0

Puan vermek ve yorum yapmak için giriş yap.

0 yorum

Henüz yorum yok. Bu modeli nasıl kullandığını ya da neyin daha iyi olabileceğini paylaş.

Benzer modeller

Aynı kategori ya da benzer etiketler