Codels
696 三角面6.11 × 5.61 × 3.35 m

在代码中使用

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

  // Materials
  const stoneMat = new THREE.MeshStandardMaterial({ name: 'stone', color: 0x9a8470, roughness: 0.92, flatShading: true });
  const stoneLightMat = new THREE.MeshStandardMaterial({ name: 'stoneLight', color: 0xac9b85, roughness: 0.9, flatShading: true });
  const stoneDarkMat = new THREE.MeshStandardMaterial({ name: 'stoneDark', color: 0x786457, roughness: 0.95, flatShading: true });
  const stoneWarmMat = new THREE.MeshStandardMaterial({ name: 'stoneWarm', color: 0xa28e72, roughness: 0.92, flatShading: true });
  const grassMat = new THREE.MeshStandardMaterial({ name: 'grass', color: 0x58833a, roughness: 0.95, flatShading: true });
  const grassDarkMat = new THREE.MeshStandardMaterial({ name: 'grassDark', color: 0x47702f, roughness: 1, flatShading: true });

  // Parts
  const cliff = new THREE.Group();
  cliff.name = 'cliff';
  cliff.position.set(0, 0.0566, 0);
  root.add(cliff);
  const stratum1 = new THREE.Mesh(jitter(extrude([[-3, 1.35], [3, 1.35], [3, -1.4882], [2.5567, -1.5524], [1.8758, -1.6237], [1.1078, -1.6113], [0.6875, -1.5653], [-0.176, -1.519], [-0.6323, -1.5178], [-1.1795, -1.4863], [-1.6981, -1.0908], [-2.4269, -1.0537], [-3, -1.3992]], 1.0821), 0.0649, 217), stoneWarmMat);
  stratum1.name = 'stratum1';
  stratum1.position.set(0, 0.541, 0);
  stratum1.rotation.set(deg(-90), 0, 0);
  cliff.add(stratum1);
  const stratum2 = new THREE.Mesh(jitter(extrude([[-3, 1.35], [3, 1.35], [3, -1.275], [2.5567, -1.391], [1.8758, -1.5027], [1.1078, -1.2853], [0.6875, -1.4531], [-0.176, -1.3579], [-0.6323, -1.5031], [-1.1795, -1.283], [-1.6981, -1.0177], [-2.4269, -0.9103], [-3, -1.0696]], 0.8873), 0.0532, 218), stoneLightMat);
  stratum2.name = 'stratum2';
  stratum2.position.set(0, 1.5257, 0);
  stratum2.rotation.set(deg(-90), 0, 0);
  cliff.add(stratum2);
  const stratum3 = new THREE.Mesh(jitter(extrude([[-3, 1.35], [3, 1.35], [3, -0.8778], [2.5567, -1.0941], [1.8758, -1.2291], [1.1078, -1.1454], [0.6875, -0.9087], [-0.176, -1.1725], [-0.6323, -1.168], [-1.1795, -0.9633], [-1.6981, -0.7049], [-2.4269, -0.5579], [-3, -0.8934]], 0.5347), 0.0321, 219), stoneMat);
  stratum3.name = 'stratum3';
  stratum3.position.set(0, 2.2366, 0);
  stratum3.rotation.set(deg(-90), 0, 0);
  cliff.add(stratum3);
  const stratum4 = new THREE.Mesh(jitter(extrude([[-3, 1.35], [3, 1.35], [3, -0.879], [2.5567, -0.9653], [1.8758, -0.9533], [1.1078, -0.9355], [0.6875, -0.8393], [-0.176, -1.0715], [-0.6323, -0.9541], [-1.1795, -0.7952], [-1.6981, -0.6488], [-2.4269, -0.5143], [-3, -0.5727]], 0.8727), 0.0524, 220), stoneDarkMat);
  stratum4.name = 'stratum4';
  stratum4.position.set(0, 2.9403, 0);
  stratum4.rotation.set(deg(-90), 0, 0);
  cliff.add(stratum4);
  const stratum5 = new THREE.Mesh(jitter(extrude([[-3, 1.35], [3, 1.35], [3, -1.2996], [2.5567, -1.3665], [1.8758, -1.4197], [1.1078, -1.2248], [0.6875, -1.3594], [-0.176, -1.3022], [-0.6323, -1.4103], [-1.1795, -1.1084], [-1.6981, -0.9899], [-2.4269, -0.7586], [-3, -1.1111]], 0.8831), 0.053, 221), stoneWarmMat);
  stratum5.name = 'stratum5';
  stratum5.position.set(0, 3.8182, 0);
  stratum5.rotation.set(deg(-90), 0, 0);
  cliff.add(stratum5);
  const stratum6 = new THREE.Mesh(jitter(extrude([[-3, 1.35], [3, 1.35], [3, -0.817], [2.5567, -1.1111], [1.8758, -1.0332], [1.1078, -1.0456], [0.6875, -0.8499], [-0.176, -0.8983], [-0.6323, -1.0613], [-1.1795, -0.7787], [-1.6981, -0.6481], [-2.4269, -0.5142], [-3, -0.8274]], 0.7402), 0.0444, 222), stoneMat);
  stratum6.name = 'stratum6';
  stratum6.position.set(0, 4.6299, 0);
  stratum6.rotation.set(deg(-90), 0, 0);
  cliff.add(stratum6);
  const boulder1 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.542, 0), 0.0813, 49), stoneLightMat);
  boulder1.name = 'boulder1';
  boulder1.position.set(-1.1795, 1.3105, 1.246);
  boulder1.rotation.set(deg(70.3431), deg(252.3563), 0);
  boulder1.scale.set(1.2, 0.8, 0.8);
  cliff.add(boulder1);
  const boulder2 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.5743, 0), 0.0861, 50), stoneDarkMat);
  boulder2.name = 'boulder2';
  boulder2.position.set(0.6875, 1.8469, 1.194);
  boulder2.rotation.set(deg(59.3889), deg(51.3757), 0);
  boulder2.scale.set(1.2, 0.8, 0.8);
  cliff.add(boulder2);
  const grassTop = new THREE.Mesh(jitter(extrude([[-3, 1.35], [3, 1.35], [3, -0.777], [2.5567, -1.0711], [1.8758, -0.9932], [1.1078, -1.0056], [0.6875, -0.8099], [-0.176, -0.8583], [-0.6323, -1.0213], [-1.1795, -0.7387], [-1.6981, -0.6081], [-2.4269, -0.4742], [-3, -0.7874]], 0.16), 0.03, 7), grassMat);
  grassTop.name = 'grassTop';
  grassTop.position.set(0, 5.06, 0);
  grassTop.rotation.set(deg(-90), 0, 0);
  cliff.add(grassTop);
  const bush1 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.2639, 0), 0.0317, 77), grassDarkMat);
  bush1.name = 'bush1';
  bush1.position.set(-1.9805, 5.2387, -0.8059);
  bush1.rotation.set(deg(16.2333), deg(101.9843), 0);
  bush1.scale.set(1, 0.7, 1);
  cliff.add(bush1);
  const bush2 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.3768, 0), 0.0452, 78), grassMat);
  bush2.name = 'bush2';
  bush2.position.set(0.1712, 5.2896, -0.4049);
  bush2.rotation.set(deg(28.3234), deg(341.7536), 0);
  bush2.scale.set(1, 0.7, 1);
  cliff.add(bush2);
  const bush3 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.2605, 0), 0.0313, 79), grassDarkMat);
  bush3.name = 'bush3';
  bush3.position.set(2.2031, 5.2372, -0.095);
  bush3.rotation.set(deg(57.6155), deg(336.1826), 0);
  bush3.scale.set(1, 0.7, 1);
  cliff.add(bush3);
  const rubble1 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.2275, 0), 0.0341, 119), stoneMat);
  rubble1.name = 'rubble1';
  rubble1.position.set(-2.3957, 0.1588, 1.6322);
  rubble1.rotation.set(deg(26.2946), deg(27.8798), 0);
  rubble1.scale.set(1, 0.65, 1);
  cliff.add(rubble1);
  const rubble2 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.2527, 0), 0.0379, 120), stoneDarkMat);
  rubble2.name = 'rubble2';
  rubble2.position.set(-1.4102, 0.1766, 1.6459);
  rubble2.rotation.set(deg(2.5141), deg(187.0549), 0);
  rubble2.scale.set(1, 0.65, 1);
  cliff.add(rubble2);
  const rubble3 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.2828, 0), 0.0424, 121), stoneMat);
  rubble3.name = 'rubble3';
  rubble3.position.set(-0.3755, 0.2025, 1.7087);
  rubble3.rotation.set(deg(30.4868), deg(132.2735), 0);
  rubble3.scale.set(1, 0.65, 1);
  cliff.add(rubble3);
  const rubble4 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.2196, 0), 0.0329, 122), stoneDarkMat);
  rubble4.name = 'rubble4';
  rubble4.position.set(1.0895, 0.1916, 1.6106);
  rubble4.rotation.set(deg(76.7033), deg(33.0454), 0);
  rubble4.scale.set(1, 0.65, 1);
  cliff.add(rubble4);
  const rubble5 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.2325, 0), 0.0349, 123), stoneMat);
  rubble5.name = 'rubble5';
  rubble5.position.set(2.3383, 0.1705, 1.6147);
  rubble5.rotation.set(deg(23.2269), deg(100.1775), 0);
  rubble5.scale.set(1, 0.65, 1);
  cliff.add(rubble5);

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

export default createCliffWall;

评分与评论

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

登录后即可评分和评论。

0 条评论

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

相关模型

同一分类或相似标签