527 tris1,24 × 2,00 × 0,56 m
Utiliser dans le code
Remixer dans l’éditeursignpost.js · 271 lignes
Module ES autonome — ne dépend que de three. Appelez la fonction factory et ajoutez le groupe à votre scène.
/**
* 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;
Notes et commentaires
0 commentaire
Aucun commentaire pour l’instant. Dites comment vous avez utilisé ce modèle ou ce qui pourrait être amélioré.
Modèles similaires
Même catégorie ou étiquettes proches









