Codels
527 tris1,24 × 2,00 × 0,56 m
Módulo ES autocontido — depende apenas do three. Chame a factory e adicione o grupo à sua cena.
/**
 * Signpost — Codels 3D model library
 * License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
 * Triangles: 527 · Size (W × H × D): 1.24 × 2.00 × 0.56 m
 *
 * Usage:
 *   import { createSignpost } from './signpost.js';
 *   
 *   const model = createSignpost();
 *   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 createSignpost() {
  const root = new THREE.Group();
  root.name = 'Signpost';

  // Materials
  const postMat = new THREE.MeshStandardMaterial({ name: 'post', color: 0x7a5132, roughness: 0.9, flatShading: true });
  const capMat = new THREE.MeshStandardMaterial({ name: 'cap', color: 0x5c3d26, roughness: 0.9, flatShading: true });
  const boardMat = new THREE.MeshStandardMaterial({ name: 'board', color: 0xc08a52, roughness: 0.85, flatShading: true });
  const boardAltMat = new THREE.MeshStandardMaterial({ name: 'boardAlt', color: 0xa57747, roughness: 0.85, flatShading: true });
  const grooveMat = new THREE.MeshStandardMaterial({ name: 'groove', color: 0x563e25, roughness: 1, flatShading: true });
  const nailMat = new THREE.MeshStandardMaterial({ name: 'nail', color: 0x3a3a3e, roughness: 0.5, metalness: 0.5, flatShading: true });
  const dirtMat = new THREE.MeshStandardMaterial({ name: 'dirt', color: 0x5e4129, roughness: 1, flatShading: true });
  const stoneMat = new THREE.MeshStandardMaterial({ name: 'stone', color: 0x77726b, roughness: 0.95, flatShading: true });
  const grassMat = new THREE.MeshStandardMaterial({ name: 'grass', color: 0x5d8a3a, roughness: 0.9, flatShading: true });

  // Shared geometries
  const boxGeo = new THREE.BoxGeometry(0.374, 0.014, 0.004);
  const boxGeo2 = new THREE.BoxGeometry(0.2618, 0.014, 0.004);
  const boxGeo3 = new THREE.BoxGeometry(0.012, 0.012, 0.006);
  const boxGeo4 = new THREE.BoxGeometry(0.306, 0.014, 0.004);
  const boxGeo5 = new THREE.BoxGeometry(0.2142, 0.014, 0.004);
  const boxGeo6 = new THREE.BoxGeometry(0.272, 0.014, 0.004);
  const boxGeo7 = new THREE.BoxGeometry(0.1904, 0.014, 0.004);

  // Parts
  const post = new THREE.Mesh(new THREE.BoxGeometry(0.1, 1.9, 0.1), postMat);
  post.name = 'post';
  post.position.set(0, 0.95, 0);
  root.add(post);
  const cap = new THREE.Mesh(new THREE.ConeGeometry(0.085, 0.09, 4), capMat);
  cap.name = 'cap';
  cap.position.set(0, 1.945, 0);
  cap.rotation.set(0, deg(45), 0);
  root.add(cap);
  const board1 = new THREE.Group();
  board1.name = 'board1';
  board1.position.set(0, 1.62, 0);
  board1.rotation.set(0, deg(4), deg(3));
  root.add(board1);
  const board = new THREE.Mesh(extrude([[-0.07, -0.075], [0.55, -0.075], [0.66, 0], [0.55, 0.075], [-0.07, 0.075]], 0.03), boardMat);
  board.name = 'board';
  board.position.set(0, 0, 0.065);
  board1.add(board);
  const text = new THREE.Mesh(boxGeo, grooveMat);
  text.name = 'text';
  text.position.set(0.247, 0.025, 0.081);
  board1.add(text);
  const text2 = new THREE.Mesh(boxGeo2, grooveMat);
  text2.name = 'text';
  text2.position.set(0.1909, -0.025, 0.081);
  board1.add(text2);
  const text3 = new THREE.Mesh(boxGeo, grooveMat);
  text3.name = 'text';
  text3.position.set(0.247, 0.025, 0.049);
  board1.add(text3);
  const text4 = new THREE.Mesh(boxGeo2, grooveMat);
  text4.name = 'text';
  text4.position.set(0.1909, -0.025, 0.049);
  board1.add(text4);
  const nail = new THREE.Mesh(boxGeo3, nailMat);
  nail.name = 'nail';
  nail.position.set(0, -0.04, 0.082);
  board1.add(nail);
  const nail2 = new THREE.Mesh(boxGeo3, nailMat);
  nail2.name = 'nail';
  nail2.position.set(0, 0.04, 0.082);
  board1.add(nail2);
  const board2 = new THREE.Group();
  board2.name = 'board2';
  board2.position.set(0, 1.36, 0);
  board2.rotation.set(0, deg(176), deg(-4));
  root.add(board2);
  const board3 = new THREE.Mesh(extrude([[-0.07, -0.075], [0.47, -0.075], [0.58, 0], [0.47, 0.075], [-0.07, 0.075]], 0.03), boardAltMat);
  board3.name = 'board';
  board3.position.set(0, 0, 0.065);
  board2.add(board3);
  const text5 = new THREE.Mesh(boxGeo4, grooveMat);
  text5.name = 'text';
  text5.position.set(0.213, 0.025, 0.081);
  board2.add(text5);
  const text6 = new THREE.Mesh(boxGeo5, grooveMat);
  text6.name = 'text';
  text6.position.set(0.1671, -0.025, 0.081);
  board2.add(text6);
  const text7 = new THREE.Mesh(boxGeo4, grooveMat);
  text7.name = 'text';
  text7.position.set(0.213, 0.025, 0.049);
  board2.add(text7);
  const text8 = new THREE.Mesh(boxGeo5, grooveMat);
  text8.name = 'text';
  text8.position.set(0.1671, -0.025, 0.049);
  board2.add(text8);
  const nail3 = new THREE.Mesh(boxGeo3, nailMat);
  nail3.name = 'nail';
  nail3.position.set(0, -0.04, 0.082);
  board2.add(nail3);
  const nail4 = new THREE.Mesh(boxGeo3, nailMat);
  nail4.name = 'nail';
  nail4.position.set(0, 0.04, 0.082);
  board2.add(nail4);
  const board32 = new THREE.Group();
  board32.name = 'board3';
  board32.position.set(0, 1.1, 0);
  board32.rotation.set(0, deg(32), deg(2));
  root.add(board32);
  const board4 = new THREE.Mesh(extrude([[-0.07, -0.075], [0.43, -0.075], [0.54, 0], [0.43, 0.075], [-0.07, 0.075]], 0.03), boardMat);
  board4.name = 'board';
  board4.position.set(0, 0, 0.065);
  board32.add(board4);
  const text9 = new THREE.Mesh(boxGeo6, grooveMat);
  text9.name = 'text';
  text9.position.set(0.196, 0.025, 0.081);
  board32.add(text9);
  const text10 = new THREE.Mesh(boxGeo7, grooveMat);
  text10.name = 'text';
  text10.position.set(0.1552, -0.025, 0.081);
  board32.add(text10);
  const text11 = new THREE.Mesh(boxGeo6, grooveMat);
  text11.name = 'text';
  text11.position.set(0.196, 0.025, 0.049);
  board32.add(text11);
  const text12 = new THREE.Mesh(boxGeo7, grooveMat);
  text12.name = 'text';
  text12.position.set(0.1552, -0.025, 0.049);
  board32.add(text12);
  const nail5 = new THREE.Mesh(boxGeo3, nailMat);
  nail5.name = 'nail';
  nail5.position.set(0, -0.04, 0.082);
  board32.add(nail5);
  const nail6 = new THREE.Mesh(boxGeo3, nailMat);
  nail6.name = 'nail';
  nail6.position.set(0, 0.04, 0.082);
  board32.add(nail6);
  const mound = new THREE.Mesh(jitter(new THREE.SphereGeometry(0.26, 9, 3, 0, Math.PI * 2, 0, Math.PI / 2), 0.02, 2), dirtMat);
  mound.name = 'mound';
  mound.scale.set(1, 0.32, 1);
  root.add(mound);
  const stone = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.0533, 0), 0.0053, 0), stoneMat);
  stone.name = 'stone';
  stone.position.set(0.1576, 0.032, 0.181);
  stone.scale.set(1.1, 0.68, 1);
  root.add(stone);
  const stone2 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.047, 0), 0.0047, 1), stoneMat);
  stone2.name = 'stone';
  stone2.position.set(-0.1007, 0.0282, 0.2179);
  stone2.scale.set(1.1, 0.68, 1);
  root.add(stone2);
  const stone3 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.0577, 0), 0.0058, 2), stoneMat);
  stone3.name = 'stone';
  stone3.position.set(-0.2171, 0.0346, -0.1023);
  stone3.scale.set(1.1, 0.68, 1);
  root.add(stone3);
  const stone4 = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.0693, 0), 0.0069, 3), stoneMat);
  stone4.name = 'stone';
  stone4.position.set(0.0815, 0.0416, -0.2257);
  stone4.scale.set(1.1, 0.68, 1);
  root.add(stone4);
  const grassTuft = new THREE.Group();
  grassTuft.name = 'grassTuft';
  grassTuft.position.set(0.0272, 0.035, 0.1577);
  root.add(grassTuft);
  const blade = new THREE.Mesh(new THREE.ConeGeometry(0.018, 0.1311, 3), grassMat);
  blade.name = 'blade';
  blade.position.set(-0.018, 0.0656, 0);
  blade.rotation.set(deg(3.8163), 0, deg(-18));
  grassTuft.add(blade);
  const blade2 = new THREE.Mesh(new THREE.ConeGeometry(0.018, 0.1384, 3), grassMat);
  blade2.name = 'blade';
  blade2.position.set(0, 0.0692, 0);
  blade2.rotation.set(deg(-2.617), 0, 0);
  grassTuft.add(blade2);
  const blade3 = new THREE.Mesh(new THREE.ConeGeometry(0.018, 0.1366, 3), grassMat);
  blade3.name = 'blade';
  blade3.position.set(0.018, 0.0683, 0);
  blade3.rotation.set(deg(7.5175), 0, deg(18));
  grassTuft.add(blade3);
  const grassTuft2 = new THREE.Group();
  grassTuft2.name = 'grassTuft';
  grassTuft2.position.set(-0.1501, 0.035, -0.0553);
  root.add(grassTuft2);
  const blade4 = new THREE.Mesh(new THREE.ConeGeometry(0.018, 0.1264, 3), grassMat);
  blade4.name = 'blade';
  blade4.position.set(-0.018, 0.0632, 0);
  blade4.rotation.set(deg(4.8669), 0, deg(-18));
  grassTuft2.add(blade4);
  const blade5 = new THREE.Mesh(new THREE.ConeGeometry(0.018, 0.1211, 3), grassMat);
  blade5.name = 'blade';
  blade5.position.set(0, 0.0606, 0);
  blade5.rotation.set(deg(-0.191), 0, 0);
  grassTuft2.add(blade5);
  const blade6 = new THREE.Mesh(new THREE.ConeGeometry(0.018, 0.1368, 3), grassMat);
  blade6.name = 'blade';
  blade6.position.set(0.018, 0.0684, 0);
  blade6.rotation.set(deg(8.6145), 0, deg(18));
  grassTuft2.add(blade6);
  const grassTuft3 = new THREE.Group();
  grassTuft3.name = 'grassTuft';
  grassTuft3.position.set(0.123, 0.035, -0.1024);
  root.add(grassTuft3);
  const blade7 = new THREE.Mesh(new THREE.ConeGeometry(0.018, 0.149, 3), grassMat);
  blade7.name = 'blade';
  blade7.position.set(-0.018, 0.0745, 0);
  blade7.rotation.set(deg(-0.5731), 0, deg(-18));
  grassTuft3.add(blade7);
  const blade8 = new THREE.Mesh(new THREE.ConeGeometry(0.018, 0.1174, 3), grassMat);
  blade8.name = 'blade';
  blade8.position.set(0, 0.0587, 0);
  blade8.rotation.set(deg(5.7618), 0, 0);
  grassTuft3.add(blade8);
  const blade9 = new THREE.Mesh(new THREE.ConeGeometry(0.018, 0.1135, 3), grassMat);
  blade9.name = 'blade';
  blade9.position.set(0.018, 0.0568, 0);
  blade9.rotation.set(deg(5.0442), 0, deg(18));
  grassTuft3.add(blade9);

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

export default createSignpost;

Avaliações e comentários

—
Nenhuma avaliação ainda — seja o primeiro.
5
0
4
0
3
0
2
0
1
0

Entre para avaliar e comentar.

0 comentário

Nenhum comentário ainda. Conte como você usou este modelo ou o que poderia melhorar.

Modelos relacionados

Mesma categoria ou tags parecidas