642 三角面2.00 × 0.75 × 2.00 m
在代码中使用
在编辑器中改编dirt-path.js · 341 行
自包含的 ES 模块,仅依赖 three。调用工厂函数,然后把返回的 group 添加到场景中。
/**
* 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;
评分与评论
0 条评论
暂无评论。分享一下你是如何使用这个模型的,或者有哪些可以改进的地方。
相关模型
同一分类或相似标签









