2036 三角面9.90 × 6.68 × 11.7 m
在代码中使用
在编辑器中改编habitat-dome.js · 456 行
自包含的 ES 模块,仅依赖 three。调用工厂函数,然后把返回的 group 添加到场景中。
/**
* Habitat Dome — Codels 3D model library
* License: CC0-1.0 · Units: meters · Y-up · Front: +Z · Origin: center of the base (y = 0)
* Triangles: 2,036 · Size (W × H × D): 9.90 × 6.68 × 11.67 m
*
* Usage:
* import { createHabitatDome } from './habitat-dome.js';
*
* const model = createHabitatDome();
* model.position.set(0, 0, 0);
* scene.add(model);
*/
import * as THREE from 'three';
const deg = THREE.MathUtils.degToRad;
// 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);
}
// Tube along a smooth curve through the given points.
function tube(path, radius, tubularSegments, radialSegments, closed = false) {
const curve = new THREE.CatmullRomCurve3(path.map(([x, y, z]) => new THREE.Vector3(x, y, z)), closed);
return new THREE.TubeGeometry(curve, tubularSegments, radius, radialSegments, closed);
}
export function createHabitatDome() {
const root = new THREE.Group();
root.name = 'HabitatDome';
// Materials
const shellMat = new THREE.MeshStandardMaterial({ name: 'shell', color: 0xdde2e8, roughness: 0.5, metalness: 0.2, flatShading: true });
const frameMat = new THREE.MeshStandardMaterial({ name: 'frame', color: 0x3a4049, roughness: 0.55, metalness: 0.45, flatShading: true });
const baseMat = new THREE.MeshStandardMaterial({ name: 'base', color: 0x5d646e, roughness: 0.7, metalness: 0.3, flatShading: true });
const metalMat = new THREE.MeshStandardMaterial({ name: 'metal', color: 0x8a929c, roughness: 0.4, metalness: 0.6, flatShading: true });
const windowMat = new THREE.MeshStandardMaterial({ name: 'window', color: 0xffb04a, roughness: 0.3, emissive: 0xffb04a, emissiveIntensity: 0.9, flatShading: true });
const glowMat = new THREE.MeshStandardMaterial({ name: 'glow', color: 0x4fd8ff, roughness: 0.4, emissive: 0x38c8ff, emissiveIntensity: 1.2, flatShading: true });
const hazardMat = new THREE.MeshStandardMaterial({ name: 'hazard', color: 0xf2b630, roughness: 0.55, metalness: 0.2, flatShading: true });
const beaconMat = new THREE.MeshStandardMaterial({ name: 'beacon', color: 0xff4a3a, roughness: 0.4, emissive: 0xff2a1f, emissiveIntensity: 1.4, flatShading: true });
// Shared geometries
const boxGeo = new THREE.BoxGeometry(0.5, 0.06, 0.06);
const torusGeo = new THREE.TorusGeometry(4, 0.07, 4, 5, Math.PI / 2);
const extrudeGeo = extrude([[-0.6892, -0.435], [0.6892, -0.435], [0.5863, 0.435], [-0.5863, 0.435]], 0.08);
const extrudeGeo2 = extrude([[-0.3773, -0.3775], [0.3773, -0.3775], [0.1983, 0.3775], [-0.1983, 0.3775]], 0.08);
const extrudeGeo3 = extrude([[-1.35, -0.05], [1.35, -0.05], [1.35, 2.082], [0.864, 2.55], [-0.864, 2.55], [-1.35, 2.082]], 0.2);
const boxGeo2 = new THREE.BoxGeometry(0.62, 1.9, 0.05);
const boxGeo3 = new THREE.BoxGeometry(0.1, 0.42, 0.02);
const boxGeo4 = new THREE.BoxGeometry(0.04, 0.08, 1.2919);
const boxGeo5 = new THREE.BoxGeometry(0.08, 0.14, 1.6763);
const cylinderGeo = new THREE.CylinderGeometry(0.46, 0.5, 0.2, 8);
const capsuleGeo = new THREE.CapsuleGeometry(0.38, 1, 2, 8);
const cylinderGeo2 = new THREE.CylinderGeometry(0.4, 0.4, 0.12, 8);
// Parts
const foundation = new THREE.Mesh(new THREE.CylinderGeometry(4.25, 4.45, 0.5, 12), baseMat);
foundation.name = 'foundation';
foundation.position.set(0, 0.25, 0);
root.add(foundation);
const skirt = new THREE.Mesh(new THREE.CylinderGeometry(4.08, 4.12, 0.25, 12), frameMat);
skirt.name = 'skirt';
skirt.position.set(0, 0.625, 0);
root.add(skirt);
const baseLight1 = new THREE.Mesh(boxGeo, glowMat);
baseLight1.name = 'baseLight1';
baseLight1.position.set(1.144, 0.32, 4.2694);
baseLight1.rotation.set(0, deg(15), 0);
root.add(baseLight1);
const baseLight2 = new THREE.Mesh(boxGeo, glowMat);
baseLight2.name = 'baseLight2';
baseLight2.position.set(3.1254, 0.32, 3.1254);
baseLight2.rotation.set(0, deg(45), 0);
root.add(baseLight2);
const baseLight3 = new THREE.Mesh(boxGeo, glowMat);
baseLight3.name = 'baseLight3';
baseLight3.position.set(4.2694, 0.32, 1.144);
baseLight3.rotation.set(0, deg(75), 0);
root.add(baseLight3);
const baseLight4 = new THREE.Mesh(boxGeo, glowMat);
baseLight4.name = 'baseLight4';
baseLight4.position.set(4.2694, 0.32, -1.144);
baseLight4.rotation.set(0, deg(105), 0);
root.add(baseLight4);
const baseLight5 = new THREE.Mesh(boxGeo, glowMat);
baseLight5.name = 'baseLight5';
baseLight5.position.set(3.1254, 0.32, -3.1254);
baseLight5.rotation.set(0, deg(135), 0);
root.add(baseLight5);
const baseLight6 = new THREE.Mesh(boxGeo, glowMat);
baseLight6.name = 'baseLight6';
baseLight6.position.set(1.144, 0.32, -4.2694);
baseLight6.rotation.set(0, deg(165), 0);
root.add(baseLight6);
const baseLight7 = new THREE.Mesh(boxGeo, glowMat);
baseLight7.name = 'baseLight7';
baseLight7.position.set(-1.144, 0.32, -4.2694);
baseLight7.rotation.set(0, deg(195), 0);
root.add(baseLight7);
const baseLight8 = new THREE.Mesh(boxGeo, glowMat);
baseLight8.name = 'baseLight8';
baseLight8.position.set(-3.1254, 0.32, -3.1254);
baseLight8.rotation.set(0, deg(225), 0);
root.add(baseLight8);
const baseLight9 = new THREE.Mesh(boxGeo, glowMat);
baseLight9.name = 'baseLight9';
baseLight9.position.set(-4.2694, 0.32, -1.144);
baseLight9.rotation.set(0, deg(255), 0);
root.add(baseLight9);
const baseLight10 = new THREE.Mesh(boxGeo, glowMat);
baseLight10.name = 'baseLight10';
baseLight10.position.set(-4.2694, 0.32, 1.144);
baseLight10.rotation.set(0, deg(285), 0);
root.add(baseLight10);
const baseLight11 = new THREE.Mesh(boxGeo, glowMat);
baseLight11.name = 'baseLight11';
baseLight11.position.set(-3.1254, 0.32, 3.1254);
baseLight11.rotation.set(0, deg(315), 0);
root.add(baseLight11);
const baseLight12 = new THREE.Mesh(boxGeo, glowMat);
baseLight12.name = 'baseLight12';
baseLight12.position.set(-1.144, 0.32, 4.2694);
baseLight12.rotation.set(0, deg(345), 0);
root.add(baseLight12);
const dome = new THREE.Group();
dome.name = 'dome';
dome.position.set(0, 0.75, 0);
dome.rotation.set(0, deg(15), 0);
root.add(dome);
const shell = new THREE.Mesh(new THREE.SphereGeometry(4, 12, 5, 0, Math.PI * 2, 0, Math.PI / 2), shellMat);
shell.name = 'shell';
dome.add(shell);
const latRib1 = new THREE.Mesh(new THREE.TorusGeometry(1.2361, 0.07, 4, 12), frameMat);
latRib1.name = 'latRib1';
latRib1.position.set(0, 3.8042, 0);
latRib1.rotation.set(deg(90), 0, 0);
dome.add(latRib1);
const latRib2 = new THREE.Mesh(new THREE.TorusGeometry(2.3511, 0.07, 4, 12), frameMat);
latRib2.name = 'latRib2';
latRib2.position.set(0, 3.2361, 0);
latRib2.rotation.set(deg(90), 0, 0);
dome.add(latRib2);
const latRib3 = new THREE.Mesh(new THREE.TorusGeometry(3.2361, 0.07, 4, 12), frameMat);
latRib3.name = 'latRib3';
latRib3.position.set(0, 2.3511, 0);
latRib3.rotation.set(deg(90), 0, 0);
dome.add(latRib3);
const latRib4 = new THREE.Mesh(new THREE.TorusGeometry(3.8042, 0.07, 4, 12), frameMat);
latRib4.name = 'latRib4';
latRib4.position.set(0, 1.2361, 0);
latRib4.rotation.set(deg(90), 0, 0);
dome.add(latRib4);
const baseRib = new THREE.Mesh(new THREE.TorusGeometry(4, 0.1, 4, 12), frameMat);
baseRib.name = 'baseRib';
baseRib.position.set(0, 0.02, 0);
baseRib.rotation.set(deg(90), 0, 0);
dome.add(baseRib);
const meridian1 = new THREE.Mesh(torusGeo, frameMat);
meridian1.name = 'meridian1';
dome.add(meridian1);
const meridian2 = new THREE.Mesh(torusGeo, frameMat);
meridian2.name = 'meridian2';
meridian2.rotation.set(0, deg(60), 0);
dome.add(meridian2);
const meridian3 = new THREE.Mesh(torusGeo, frameMat);
meridian3.name = 'meridian3';
meridian3.rotation.set(0, deg(120), 0);
dome.add(meridian3);
const meridian4 = new THREE.Mesh(torusGeo, frameMat);
meridian4.name = 'meridian4';
meridian4.rotation.set(0, deg(180), 0);
dome.add(meridian4);
const meridian5 = new THREE.Mesh(torusGeo, frameMat);
meridian5.name = 'meridian5';
meridian5.rotation.set(0, deg(240), 0);
dome.add(meridian5);
const meridian6 = new THREE.Mesh(torusGeo, frameMat);
meridian6.name = 'meridian6';
meridian6.rotation.set(0, deg(300), 0);
dome.add(meridian6);
const window1 = new THREE.Group();
window1.name = 'window1';
window1.rotation.set(0, deg(60), 0);
root.add(window1);
const pane = new THREE.Mesh(extrudeGeo, windowMat);
pane.name = 'pane';
pane.position.set(0, 2.5436, 3.4002);
pane.rotation.set(deg(-26.2047), 0, 0);
window1.add(pane);
const window2 = new THREE.Group();
window2.name = 'window2';
window2.rotation.set(0, deg(90), 0);
root.add(window2);
const pane2 = new THREE.Mesh(extrudeGeo, windowMat);
pane2.name = 'pane';
pane2.position.set(0, 2.5436, 3.4002);
pane2.rotation.set(deg(-26.2047), 0, 0);
window2.add(pane2);
const window3 = new THREE.Group();
window3.name = 'window3';
window3.rotation.set(0, deg(120), 0);
root.add(window3);
const pane3 = new THREE.Mesh(extrudeGeo, windowMat);
pane3.name = 'pane';
pane3.position.set(0, 2.5436, 3.4002);
pane3.rotation.set(deg(-26.2047), 0, 0);
window3.add(pane3);
const window4 = new THREE.Group();
window4.name = 'window4';
window4.rotation.set(0, deg(150), 0);
root.add(window4);
const pane4 = new THREE.Mesh(extrudeGeo, windowMat);
pane4.name = 'pane';
pane4.position.set(0, 2.5436, 3.4002);
pane4.rotation.set(deg(-26.2047), 0, 0);
window4.add(pane4);
const window5 = new THREE.Group();
window5.name = 'window5';
window5.rotation.set(0, deg(180), 0);
root.add(window5);
const pane5 = new THREE.Mesh(extrudeGeo, windowMat);
pane5.name = 'pane';
pane5.position.set(0, 2.5436, 3.4002);
pane5.rotation.set(deg(-26.2047), 0, 0);
window5.add(pane5);
const window6 = new THREE.Group();
window6.name = 'window6';
window6.rotation.set(0, deg(210), 0);
root.add(window6);
const pane6 = new THREE.Mesh(extrudeGeo, windowMat);
pane6.name = 'pane';
pane6.position.set(0, 2.5436, 3.4002);
pane6.rotation.set(deg(-26.2047), 0, 0);
window6.add(pane6);
const window7 = new THREE.Group();
window7.name = 'window7';
window7.rotation.set(0, deg(240), 0);
root.add(window7);
const pane7 = new THREE.Mesh(extrudeGeo, windowMat);
pane7.name = 'pane';
pane7.position.set(0, 2.5436, 3.4002);
pane7.rotation.set(deg(-26.2047), 0, 0);
window7.add(pane7);
const window8 = new THREE.Group();
window8.name = 'window8';
window8.rotation.set(0, deg(270), 0);
root.add(window8);
const pane8 = new THREE.Mesh(extrudeGeo, windowMat);
pane8.name = 'pane';
pane8.position.set(0, 2.5436, 3.4002);
pane8.rotation.set(deg(-26.2047), 0, 0);
window8.add(pane8);
const window9 = new THREE.Group();
window9.name = 'window9';
window9.rotation.set(0, deg(300), 0);
root.add(window9);
const pane9 = new THREE.Mesh(extrudeGeo, windowMat);
pane9.name = 'pane';
pane9.position.set(0, 2.5436, 3.4002);
pane9.rotation.set(deg(-26.2047), 0, 0);
window9.add(pane9);
const skylight1 = new THREE.Group();
skylight1.name = 'skylight1';
skylight1.rotation.set(0, deg(30), 0);
root.add(skylight1);
const pane10 = new THREE.Mesh(extrudeGeo2, windowMat);
pane10.name = 'pane';
pane10.position.set(0, 4.2701, 1.7325);
pane10.rotation.set(deg(-62.1884), 0, 0);
skylight1.add(pane10);
const skylight2 = new THREE.Group();
skylight2.name = 'skylight2';
skylight2.rotation.set(0, deg(150), 0);
root.add(skylight2);
const pane11 = new THREE.Mesh(extrudeGeo2, windowMat);
pane11.name = 'pane';
pane11.position.set(0, 4.2701, 1.7325);
pane11.rotation.set(deg(-62.1884), 0, 0);
skylight2.add(pane11);
const skylight3 = new THREE.Group();
skylight3.name = 'skylight3';
skylight3.rotation.set(0, deg(270), 0);
root.add(skylight3);
const pane12 = new THREE.Mesh(extrudeGeo2, windowMat);
pane12.name = 'pane';
pane12.position.set(0, 4.2701, 1.7325);
pane12.rotation.set(deg(-62.1884), 0, 0);
skylight3.add(pane12);
const cupola = new THREE.Mesh(new THREE.CylinderGeometry(0.75, 0.9, 0.35, 8), frameMat);
cupola.name = 'cupola';
cupola.position.set(0, 4.805, 0);
root.add(cupola);
const cupolaGlass = new THREE.Mesh(new THREE.CylinderGeometry(0.6, 0.7, 0.18, 8), windowMat);
cupolaGlass.name = 'cupolaGlass';
cupolaGlass.position.set(0, 5.07, 0);
root.add(cupolaGlass);
const cupolaCap = new THREE.Mesh(new THREE.CylinderGeometry(0.4, 0.62, 0.14, 8), shellMat);
cupolaCap.name = 'cupolaCap';
cupolaCap.position.set(0, 5.23, 0);
root.add(cupolaCap);
const antenna = new THREE.Mesh(new THREE.CylinderGeometry(0.03, 0.04, 1.3, 5), metalMat);
antenna.name = 'antenna';
antenna.position.set(0.25, 5.9, -0.1);
root.add(antenna);
const beacon = new THREE.Mesh(new THREE.SphereGeometry(0.08, 6, 4), beaconMat);
beacon.name = 'beacon';
beacon.position.set(0.25, 6.6, -0.1);
root.add(beacon);
const antennaArm = new THREE.Mesh(new THREE.BoxGeometry(0.6, 0.03, 0.03), metalMat);
antennaArm.name = 'antennaArm';
antennaArm.position.set(0.25, 6.2, -0.1);
antennaArm.rotation.set(0, deg(30), 0);
root.add(antennaArm);
const airlockTunnel = new THREE.Mesh(extrude([[-1.25, 0], [1.25, 0], [1.25, 2.05], [0.8, 2.5], [-0.8, 2.5], [-1.25, 2.05]], 2.5), shellMat);
airlockTunnel.name = 'airlockTunnel';
airlockTunnel.position.set(0, 0.5, 4.25);
root.add(airlockTunnel);
const airlockRib = new THREE.Mesh(extrudeGeo3, frameMat);
airlockRib.name = 'airlockRib';
airlockRib.position.set(0, 0.5, 3.8581);
root.add(airlockRib);
const airlockRib2 = new THREE.Mesh(extrudeGeo3, frameMat);
airlockRib2.name = 'airlockRib';
airlockRib2.position.set(0, 0.5, 5.42);
root.add(airlockRib2);
const airlockBase = new THREE.Mesh(new THREE.BoxGeometry(2.8, 0.5, 2.7), baseMat);
airlockBase.name = 'airlockBase';
airlockBase.position.set(0, 0.25, 4.35);
root.add(airlockBase);
const doorFrame = new THREE.Mesh(new THREE.BoxGeometry(1.5, 2.1, 0.04), frameMat);
doorFrame.name = 'doorFrame';
doorFrame.position.set(0, 1.6, 5.54);
root.add(doorFrame);
const doorL = new THREE.Mesh(boxGeo2, metalMat);
doorL.name = 'doorL';
doorL.position.set(-0.33, 1.55, 5.56);
root.add(doorL);
const box64 = new THREE.Mesh(boxGeo3, hazardMat);
box64.position.set(-0.1, 0.95, 5.595);
box64.rotation.set(0, 0, deg(35));
root.add(box64);
const box65 = new THREE.Mesh(boxGeo3, hazardMat);
box65.position.set(-0.3, 0.95, 5.595);
box65.rotation.set(0, 0, deg(35));
root.add(box65);
const box66 = new THREE.Mesh(boxGeo3, hazardMat);
box66.position.set(-0.5, 0.95, 5.595);
box66.rotation.set(0, 0, deg(35));
root.add(box66);
const doorR = new THREE.Mesh(boxGeo2, metalMat);
doorR.name = 'doorR';
doorR.position.set(0.33, 1.55, 5.56);
root.add(doorR);
const box68 = new THREE.Mesh(boxGeo3, hazardMat);
box68.position.set(0.1, 0.95, 5.595);
box68.rotation.set(0, 0, deg(-35));
root.add(box68);
const box69 = new THREE.Mesh(boxGeo3, hazardMat);
box69.position.set(0.3, 0.95, 5.595);
box69.rotation.set(0, 0, deg(-35));
root.add(box69);
const box70 = new THREE.Mesh(boxGeo3, hazardMat);
box70.position.set(0.5, 0.95, 5.595);
box70.rotation.set(0, 0, deg(-35));
root.add(box70);
const doorSeam = new THREE.Mesh(new THREE.BoxGeometry(0.03, 1.9, 0.02), glowMat);
doorSeam.name = 'doorSeam';
doorSeam.position.set(0, 1.55, 5.59);
root.add(doorSeam);
const doorLight = new THREE.Mesh(new THREE.BoxGeometry(1.2, 0.08, 0.06), glowMat);
doorLight.name = 'doorLight';
doorLight.position.set(0, 2.75, 5.54);
root.add(doorLight);
const keypad = new THREE.Mesh(new THREE.BoxGeometry(0.2, 0.3, 0.06), frameMat);
keypad.name = 'keypad';
keypad.position.set(0.95, 1.45, 5.54);
root.add(keypad);
const keypadLight = new THREE.Mesh(new THREE.BoxGeometry(0.12, 0.12, 0.04), beaconMat);
keypadLight.name = 'keypadLight';
keypadLight.position.set(0.95, 1.5, 5.58);
root.add(keypadLight);
const tunnelStripL = new THREE.Mesh(boxGeo4, glowMat);
tunnelStripL.name = 'tunnelStripL';
tunnelStripL.position.set(-1.27, 1.7, 4.654);
root.add(tunnelStripL);
const tunnelStripR = new THREE.Mesh(boxGeo4, glowMat);
tunnelStripR.name = 'tunnelStripR';
tunnelStripR.position.set(1.27, 1.7, 4.654);
root.add(tunnelStripR);
const ramp = new THREE.Mesh(new THREE.BoxGeometry(1.6, 0.08, 1.6763), metalMat);
ramp.name = 'ramp';
ramp.position.set(0, 0.29, 6.4);
ramp.rotation.set(deg(17.354), 0, 0);
root.add(ramp);
const box78 = new THREE.Mesh(boxGeo5, hazardMat);
box78.position.set(-0.82, 0.32, 6.4);
box78.rotation.set(deg(17.354), 0, 0);
root.add(box78);
const box79 = new THREE.Mesh(boxGeo5, hazardMat);
box79.position.set(0.82, 0.32, 6.4);
box79.rotation.set(deg(17.354), 0, 0);
root.add(box79);
const tankFoot = new THREE.Mesh(cylinderGeo, frameMat);
tankFoot.name = 'tankFoot';
tankFoot.position.set(-4.95, 0.1, -0.55);
root.add(tankFoot);
const sideTank = new THREE.Mesh(capsuleGeo, shellMat);
sideTank.name = 'sideTank';
sideTank.position.set(-4.95, 1, -0.55);
root.add(sideTank);
const tankBand = new THREE.Mesh(cylinderGeo2, hazardMat);
tankBand.name = 'tankBand';
tankBand.position.set(-4.95, 0.95, -0.55);
root.add(tankBand);
const tankFoot2 = new THREE.Mesh(cylinderGeo, frameMat);
tankFoot2.name = 'tankFoot';
tankFoot2.position.set(-4.95, 0.1, 0.45);
root.add(tankFoot2);
const sideTank2 = new THREE.Mesh(capsuleGeo, shellMat);
sideTank2.name = 'sideTank';
sideTank2.position.set(-4.95, 1, 0.45);
root.add(sideTank2);
const tankBand2 = new THREE.Mesh(cylinderGeo2, hazardMat);
tankBand2.name = 'tankBand';
tankBand2.position.set(-4.95, 0.95, 0.45);
root.add(tankBand2);
const tankPipe = new THREE.Mesh(tube([[-4.65, 1.4, -0.05], [-4.25, 1.3, -0.05], [-4.05, 1.1, -0.05]], 0.07, 6, 5), metalMat);
tankPipe.name = 'tankPipe';
root.add(tankPipe);
const ventBlock = new THREE.Mesh(new THREE.BoxGeometry(1.3, 1.1, 1), frameMat);
ventBlock.name = 'ventBlock';
ventBlock.position.set(3.5002, 0.55, -3.5002);
ventBlock.rotation.set(0, deg(135), 0);
root.add(ventBlock);
const ventGlow = new THREE.Mesh(new THREE.BoxGeometry(0.9, 0.06, 0.7), glowMat);
ventGlow.name = 'ventGlow';
ventGlow.position.set(3.5002, 1.12, -3.5002);
ventGlow.rotation.set(0, deg(135), 0);
root.add(ventGlow);
root.traverse((o) => {
if (o.isMesh) {
o.castShadow = true;
o.receiveShadow = true;
}
});
return root;
}
export default createHabitatDome;
评分与评论
0 条评论
暂无评论。分享一下你是如何使用这个模型的,或者有哪些可以改进的地方。
相关模型
同一分类或相似标签









