496 三角面2.00 × 0.80 × 1.73 m
在代码中使用
在编辑器中改编hex-tile.js · 296 行
自包含的 ES 模块,仅依赖 three。调用工厂函数,然后把返回的 group 添加到场景中。
/**
* Hex Tile — Codels 3D model library
* License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
* Triangles: 496 · Size (W × H × D): 2.00 × 0.80 × 1.73 m
*
* Usage:
* import { createHexTile } from './hex-tile.js';
*
* const model = createHexTile();
* 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 createHexTile() {
const root = new THREE.Group();
root.name = 'HexTile';
// Materials
const topMat = new THREE.MeshStandardMaterial({ name: 'top', color: 0x6aae45, roughness: 0.9, flatShading: true });
const topLightMat = new THREE.MeshStandardMaterial({ name: 'topLight', color: 0x7fb95f, roughness: 0.85, flatShading: true });
const topDarkMat = new THREE.MeshStandardMaterial({ name: 'topDark', color: 0x578f39, roughness: 0.9, flatShading: true });
const bodyMat = new THREE.MeshStandardMaterial({ name: 'body', color: 0x8b5d3b, roughness: 0.95, flatShading: true });
const deepMat = new THREE.MeshStandardMaterial({ name: 'deep', color: 0x6a4630, roughness: 0.95, flatShading: true });
const rockMat = new THREE.MeshStandardMaterial({ name: 'rock', color: 0x8e8a83, roughness: 0.9, flatShading: true });
const greenMat = new THREE.MeshStandardMaterial({ name: 'green', color: 0x3f7a33, roughness: 0.9, flatShading: true });
const accentMat = new THREE.MeshStandardMaterial({ name: 'accent', color: 0xfff3c4, roughness: 0.7, flatShading: true });
const accent2Mat = new THREE.MeshStandardMaterial({ name: 'accent2', color: 0xf58fb0, roughness: 0.6, flatShading: true });
// Shared geometries
const cylinderGeo = new THREE.CylinderGeometry(0.012, 0.016, 0.18, 4);
const dodecahedronGeo = new THREE.DodecahedronGeometry(0.0468, 0);
const coneGeo = new THREE.ConeGeometry(0.0288, 0.072, 3);
// Parts
const surface = new THREE.Mesh(new THREE.CylinderGeometry(1, 1, 0.12, 6, 1, false, Math.PI / 6), topMat);
surface.name = 'surface';
surface.position.set(0, 0.44, 0);
root.add(surface);
const body = new THREE.Mesh(new THREE.CylinderGeometry(0.97, 0.97, 0.209, 6, 1, false, Math.PI / 6), bodyMat);
body.name = 'body';
body.position.set(0, 0.2755, 0);
root.add(body);
const base = new THREE.Mesh(new THREE.CylinderGeometry(0.97, 0.97, 0.171, 6, 1, false, Math.PI / 6), deepMat);
base.name = 'base';
base.position.set(0, 0.0855, 0);
root.add(base);
const details = new THREE.Group();
details.name = 'details';
root.add(details);
const patch1 = new THREE.Mesh(extrude([[0.4717, 0.0394], [0.2938, 0.0427], [0.0573, 0.1736], [0.0771, 0.3403], [0.2723, 0.4612], [0.4358, 0.4618], [0.5476, 0.2406]], 0.01), topDarkMat);
patch1.name = 'patch1';
patch1.position.set(0, 0.505, 0);
patch1.rotation.set(deg(-90), 0, 0);
details.add(patch1);
const patch2 = new THREE.Mesh(extrude([[-0.0732, -0.3011], [-0.2387, -0.3727], [-0.4988, -0.2549], [-0.56, -0.1181], [-0.4127, 0.0316], [-0.1184, 0.0082], [-0.0405, -0.1544]], 0.01), topLightMat);
patch2.name = 'patch2';
patch2.position.set(0, 0.505, 0);
patch2.rotation.set(deg(-90), 0, 0);
details.add(patch2);
const tuft1 = new THREE.Group();
tuft1.name = 'tuft1';
tuft1.position.set(-0.3031, 0.5, 0.3029);
details.add(tuft1);
const cone8 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.1924, 3), topDarkMat);
cone8.position.set(0.041, 0.0762, 0.0137);
cone8.rotation.set(deg(25.3383), deg(50.8657), deg(-25.1413));
cone8.scale.set(1, 1, 0.45);
tuft1.add(cone8);
const cone9 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.1993, 3), topMat);
cone9.position.set(-0.0347, 0.0797, 0.0257);
cone9.rotation.set(deg(-140.2443), deg(63.3564), deg(147.906));
cone9.scale.set(1, 1, 0.45);
tuft1.add(cone9);
const cone10 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.1667, 3), greenMat);
cone10.position.set(-0.0181, 0.0634, -0.0392);
cone10.rotation.set(deg(-13.7635), deg(5.5164), deg(6.0235));
cone10.scale.set(1, 1, 0.45);
tuft1.add(cone10);
const tuft2 = new THREE.Group();
tuft2.name = 'tuft2';
tuft2.position.set(0.1043, 0.5, -0.3634);
details.add(tuft2);
const cone12 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.1797, 3), topDarkMat);
cone12.position.set(0.0429, 0.0699, -0.0047);
cone12.rotation.set(deg(-0.3726), deg(4.9327), deg(-15.981));
cone12.scale.set(1, 1, 0.45);
tuft2.add(cone12);
const cone13 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.1834, 3), topMat);
cone13.position.set(-0.0003, 0.0717, 0.0432);
cone13.rotation.set(deg(17.6008), deg(18.2339), deg(0.1169));
cone13.scale.set(1, 1, 0.45);
tuft2.add(cone13);
const cone14 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.1856, 3), greenMat);
cone14.position.set(-0.043, 0.0728, 0.0041);
cone14.rotation.set(deg(-3.5682), deg(14.0817), deg(22.5281));
cone14.scale.set(1, 1, 0.45);
tuft2.add(cone14);
const cone15 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.1827, 3), topDarkMat);
cone15.position.set(-0.0163, 0.0713, -0.04);
cone15.rotation.set(deg(-16.2697), deg(15.7195), deg(6.114));
cone15.scale.set(1, 1, 0.45);
tuft2.add(cone15);
const tuft3 = new THREE.Group();
tuft3.name = 'tuft3';
tuft3.position.set(-0.1928, 0.5, 0.101);
details.add(tuft3);
const cone17 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.223, 3), topDarkMat);
cone17.position.set(0.0379, 0.0915, -0.0207);
cone17.rotation.set(deg(6.2912), deg(41.3223), deg(-23.9996));
cone17.scale.set(1, 1, 0.45);
tuft3.add(cone17);
const cone18 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.244, 3), topMat);
cone18.position.set(-0.0325, 0.102, 0.0284);
cone18.rotation.set(deg(-146.2924), deg(59.88), deg(154.3996));
cone18.scale.set(1, 1, 0.45);
tuft3.add(cone18);
const cone19 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.2394, 3), greenMat);
cone19.position.set(-0.0118, 0.0997, -0.0416);
cone19.rotation.set(deg(-16.8488), deg(34.4344), deg(4.8096));
cone19.scale.set(1, 1, 0.45);
tuft3.add(cone19);
const tuft4 = new THREE.Group();
tuft4.name = 'tuft4';
tuft4.position.set(0.2107, 0.5, -0.1535);
details.add(tuft4);
const cone21 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.1878, 3), topDarkMat);
cone21.position.set(0.043, 0.0739, 0.0042);
cone21.rotation.set(deg(3.287), deg(10.7682), deg(-11.4784));
cone21.scale.set(1, 1, 0.45);
tuft4.add(cone21);
const cone22 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.2226, 3), topMat);
cone22.position.set(-0.0028, 0.0913, 0.0431);
cone22.rotation.set(deg(9.4257), deg(81.1143), deg(6.6417));
cone22.scale.set(1, 1, 0.45);
tuft4.add(cone22);
const cone23 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.2123, 3), greenMat);
cone23.position.set(-0.0395, 0.0861, 0.0175);
cone23.rotation.set(deg(-0.5782), deg(25.4893), deg(18.7119));
cone23.scale.set(1, 1, 0.45);
tuft4.add(cone23);
const cone24 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.2367, 3), topDarkMat);
cone24.position.set(0.0107, 0.0984, -0.0419);
cone24.rotation.set(deg(2.1663), deg(77.3768), deg(-18.0181));
cone24.scale.set(1, 1, 0.45);
tuft4.add(cone24);
const tuft5 = new THREE.Group();
tuft5.name = 'tuft5';
tuft5.position.set(-0.2142, 0.5, 0.2096);
details.add(tuft5);
const cone26 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.2261, 3), topDarkMat);
cone26.position.set(0.0431, 0.0931, -0.0026);
cone26.rotation.set(deg(141.3569), deg(58.5614), deg(-138.1705));
cone26.scale.set(1, 1, 0.45);
tuft5.add(cone26);
const cone27 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.1951, 3), topMat);
cone27.position.set(-0.0292, 0.0775, 0.0319);
cone27.rotation.set(deg(7.9667), deg(9.9352), deg(8.7159));
cone27.scale.set(1, 1, 0.45);
tuft5.add(cone27);
const cone28 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.1669, 3), greenMat);
cone28.position.set(-0.0059, 0.0635, -0.0428);
cone28.rotation.set(deg(173.2223), deg(77.6077), deg(168.4115));
cone28.scale.set(1, 1, 0.45);
tuft5.add(cone28);
const tuft6 = new THREE.Group();
tuft6.name = 'tuft6';
tuft6.position.set(0.1284, 0.5, -0.1911);
details.add(tuft6);
const cone30 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.2427, 3), topDarkMat);
cone30.position.set(0.0425, 0.1013, 0.0078);
cone30.rotation.set(deg(130.0297), deg(69.5196), deg(-125.1185));
cone30.scale.set(1, 1, 0.45);
tuft6.add(cone30);
const cone31 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.1997, 3), topMat);
cone31.position.set(-0.0143, 0.0799, 0.0408);
cone31.rotation.set(deg(10.9485), deg(41.2069), deg(7.249));
cone31.scale.set(1, 1, 0.45);
tuft6.add(cone31);
const cone32 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.1582, 3), greenMat);
cone32.position.set(-0.0426, 0.0591, 0.0074);
cone32.rotation.set(deg(-39.7414), deg(60.1891), deg(47.7368));
cone32.scale.set(1, 1, 0.45);
tuft6.add(cone32);
const cone33 = new THREE.Mesh(new THREE.ConeGeometry(0.048, 0.2001, 3), topDarkMat);
cone33.position.set(0.0109, 0.0801, -0.0418);
cone33.rotation.set(deg(6.3458), deg(78.7871), deg(-25.5541));
cone33.scale.set(1, 1, 0.45);
tuft6.add(cone33);
const flower1 = new THREE.Group();
flower1.name = 'flower1';
flower1.position.set(-0.5107, 0.5, -0.3568);
details.add(flower1);
const cylinder35 = new THREE.Mesh(cylinderGeo, greenMat);
cylinder35.position.set(0, 0.07, 0);
flower1.add(cylinder35);
const dodecahedron36 = new THREE.Mesh(dodecahedronGeo, accentMat);
dodecahedron36.position.set(0, 0.17, 0);
dodecahedron36.scale.set(1, 0.6, 1);
flower1.add(dodecahedron36);
const cone37 = new THREE.Mesh(coneGeo, greenMat);
cone37.position.set(0.0144, 0.0252, 0);
cone37.rotation.set(0, deg(185.7646), deg(-40));
cone37.scale.set(1, 1, 0.4);
flower1.add(cone37);
const flower2 = new THREE.Group();
flower2.name = 'flower2';
flower2.position.set(-0.4265, 0.5, 0.1462);
details.add(flower2);
const cylinder39 = new THREE.Mesh(cylinderGeo, greenMat);
cylinder39.position.set(0, 0.07, 0);
flower2.add(cylinder39);
const dodecahedron40 = new THREE.Mesh(dodecahedronGeo, accent2Mat);
dodecahedron40.position.set(0, 0.17, 0);
dodecahedron40.scale.set(1, 0.6, 1);
flower2.add(dodecahedron40);
const cone41 = new THREE.Mesh(coneGeo, greenMat);
cone41.position.set(0.0144, 0.0252, 0);
cone41.rotation.set(0, deg(103.3109), deg(-40));
cone41.scale.set(1, 1, 0.4);
flower2.add(cone41);
const flower3 = new THREE.Group();
flower3.name = 'flower3';
flower3.position.set(-0.1494, 0.5, -0.4188);
details.add(flower3);
const cylinder43 = new THREE.Mesh(cylinderGeo, greenMat);
cylinder43.position.set(0, 0.07, 0);
flower3.add(cylinder43);
const dodecahedron44 = new THREE.Mesh(dodecahedronGeo, accentMat);
dodecahedron44.position.set(0, 0.17, 0);
dodecahedron44.scale.set(1, 0.6, 1);
flower3.add(dodecahedron44);
const cone45 = new THREE.Mesh(coneGeo, greenMat);
cone45.position.set(0.0144, 0.0252, 0);
cone45.rotation.set(0, deg(125.2183), deg(-40));
cone45.scale.set(1, 1, 0.4);
flower3.add(cone45);
const bush = new THREE.Group();
bush.name = 'bush';
bush.position.set(-0.3079, 0.47, -0.3866);
details.add(bush);
const icosahedron47 = new THREE.Mesh(jitter(new THREE.IcosahedronGeometry(0.2, 0), 0.02, 3), greenMat);
icosahedron47.position.set(0, 0.15, 0);
bush.add(icosahedron47);
const icosahedron48 = new THREE.Mesh(jitter(new THREE.IcosahedronGeometry(0.14, 0), 0.015, 4), topDarkMat);
icosahedron48.position.set(0.15, 0.1, 0.05);
bush.add(icosahedron48);
const pebble = new THREE.Mesh(jitter(new THREE.DodecahedronGeometry(0.07, 0), 0.0126, 437), rockMat);
pebble.name = 'pebble';
pebble.position.set(-0.2622, 0.5234, -0.4648);
pebble.rotation.set(deg(0.3929), deg(175.0807), deg(5.2691));
pebble.scale.set(1.1326, 0.4783, 1);
details.add(pebble);
root.traverse((o) => {
if (o.isMesh) {
o.castShadow = true;
o.receiveShadow = true;
}
});
return root;
}
export default createHexTile;
评分与评论
0 条评论
暂无评论。分享一下你是如何使用这个模型的,或者有哪些可以改进的地方。
相关模型
同一分类或相似标签









