/** * 精细 3D 设备模型工厂 * 参考 ITYUNWEI modelFactory.ts 设计 */ import * as THREE from 'three'; export type DeviceStatus = 'normal' | 'warning' | 'critical' | 'offline'; export type DeviceType = 'server' | 'switch' | 'router' | 'firewall' | 'storage' | 'workstation'; const statusColors: Record = { normal: 0x10b981, warning: 0xf59e0b, critical: 0xef4444, offline: 0x6b7280, }; // 不同设备类型的默认发光颜色 const typeColors: Record = { server: 0x60a5fa, // 蓝色 - 服务器 switch: 0x22d3ee, // 青色 - 交换机 router: 0xa78bfa, // 紫色 - 路由器 firewall: 0xf87171, // 红色 - 防火墙 storage: 0xfbbf24, // 黄色 - 存储 workstation: 0x34d399, // 绿色 - 工作站 }; /** 创建设备基础材质:金属外壳 + 状态发光 */ function createBodyMaterial(status: DeviceStatus, type?: DeviceType): THREE.MeshStandardMaterial { // 根据状态和类型决定发光颜色 let emissiveColor = statusColors[status]; if (status === 'normal' && type) { emissiveColor = typeColors[type] || emissiveColor; } const intensity = status === 'normal' ? 0.5 : status === 'warning' ? 0.7 : status === 'critical' ? 1.0 : 0.2; return new THREE.MeshStandardMaterial({ color: 0x2c3e5a, metalness: 0.55, roughness: 0.35, emissive: emissiveColor, emissiveIntensity: intensity, side: THREE.DoubleSide, }); } /** LED 指示灯材质 */ function createLedMaterial(color: number, intensity = 0.8): THREE.MeshStandardMaterial { return new THREE.MeshStandardMaterial({ color, emissive: color, emissiveIntensity: intensity, metalness: 0.2, roughness: 0.3, side: THREE.DoubleSide, }); } /** 前面板塑料材质 */ function createPanelMaterial(): THREE.MeshStandardMaterial { return new THREE.MeshStandardMaterial({ color: 0x334155, metalness: 0.3, roughness: 0.6, side: THREE.DoubleSide, }); } /** 端口金属材质 */ function createPortMaterial(): THREE.MeshStandardMaterial { return new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.9, roughness: 0.2, side: THREE.DoubleSide, }); } // ============================================================ // 1. 服务器 Server - 机架式服务器 // ============================================================ export function createServerModel(status: DeviceStatus, scale = 1.5): THREE.Group { const group = new THREE.Group(); const bodyMat = createBodyMaterial(status, 'server'); const panelMat = createPanelMaterial(); // 主体机箱(扁平长方体) const body = new THREE.Mesh( new THREE.BoxGeometry(scale * 1.6, scale * 0.5, scale * 1.0), bodyMat ); body.userData.isBody = true; group.add(body); // 前面板 const frontPanel = new THREE.Mesh( new THREE.BoxGeometry(scale * 1.62, scale * 0.46, scale * 0.04), panelMat ); frontPanel.position.z = scale * 0.52; group.add(frontPanel); // 前面板 - 硬盘槽位(4个) for (let i = 0; i < 4; i++) { const tray = new THREE.Mesh( new THREE.BoxGeometry(scale * 0.3, scale * 0.35, scale * 0.02), new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.5, roughness: 0.5 }) ); tray.position.set(-scale * 0.55 + i * scale * 0.37, 0, scale * 0.54); group.add(tray); // 硬盘槽位 LED 灯 const ledColor = i === 0 ? statusColors.normal : i === 1 ? statusColors.warning : 0x475569; const led = new THREE.Mesh( new THREE.SphereGeometry(0.02, 8, 8), createLedMaterial(ledColor, i < 2 ? 1.0 : 0.5) ); led.position.set(-scale * 0.55 + i * scale * 0.37, scale * 0.12, scale * 0.56); group.add(led); } // 服务器状态 LED 灯(右上角) const powerLed = new THREE.Mesh( new THREE.SphereGeometry(0.025, 10, 10), createLedMaterial(statusColors[status], 1.2) ); powerLed.position.set(scale * 0.68, scale * 0.18, scale * 0.56); group.add(powerLed); const statusLed = new THREE.Mesh( new THREE.SphereGeometry(0.02, 10, 10), createLedMaterial(0x22d3ee, 1.0) ); statusLed.position.set(scale * 0.72, scale * 0.18, scale * 0.56); group.add(statusLed); // 顶部散热格栅 for (let i = 0; i < 6; i++) { const fin = new THREE.Mesh( new THREE.BoxGeometry(scale * 1.2, 0.01, 0.03), new THREE.MeshStandardMaterial({ color: 0x475569, metalness: 0.8, roughness: 0.3 }) ); fin.position.set(0, scale * 0.26, -scale * 0.3 + i * scale * 0.12); group.add(fin); } return group; } // ============================================================ // 2. 交换机 Switch - 扁平机架式 + 端口阵列 // ============================================================ export function createSwitchModel(status: DeviceStatus, scale = 1.8): THREE.Group { const group = new THREE.Group(); const bodyMat = createBodyMaterial(status, 'switch'); const panelMat = createPanelMaterial(); const portMat = createPortMaterial(); const earMat = new THREE.MeshStandardMaterial({ color: 0x475569, metalness: 0.8, roughness: 0.3 }); // 主体机箱(非常扁平) const body = new THREE.Mesh( new THREE.BoxGeometry(scale * 1.8, scale * 0.28, scale * 0.8), bodyMat ); body.userData.isBody = true; group.add(body); // 前面板 const frontPanel = new THREE.Mesh( new THREE.BoxGeometry(scale * 1.82, scale * 0.24, scale * 0.03), panelMat ); frontPanel.position.z = scale * 0.41; group.add(frontPanel); // 机架耳 const leftEar = new THREE.Mesh( new THREE.BoxGeometry(scale * 0.12, scale * 0.22, scale * 0.15), earMat ); leftEar.position.set(-scale * 0.97, 0, 0); group.add(leftEar); const rightEar = leftEar.clone(); rightEar.position.x = scale * 0.97; group.add(rightEar); // 端口阵列(24口,2行 x 12列) const portSize = scale * 0.06; const portGap = scale * 0.08; for (let row = 0; row < 2; row++) { for (let col = 0; col < 12; col++) { const port = new THREE.Mesh( new THREE.BoxGeometry(portSize, portSize, scale * 0.05), portMat ); const startX = -scale * 0.72 + col * portGap; const y = row === 0 ? scale * 0.06 : -scale * 0.06; port.position.set(startX, y, scale * 0.44); group.add(port); // 端口上方的小 LED(亮绿色) const portLed = new THREE.Mesh( new THREE.SphereGeometry(0.012, 6, 6), createLedMaterial(0x22c55e, 0.8) ); portLed.position.set(startX, y + scale * 0.05, scale * 0.47); group.add(portLed); } } // 品牌区域 const brandArea = new THREE.Mesh( new THREE.BoxGeometry(scale * 0.3, scale * 0.08, 0.035), new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.5, roughness: 0.5 }) ); brandArea.position.set(-scale * 0.82, scale * 0.12, scale * 0.44); group.add(brandArea); // 状态 LED 灯组 const ledPositions = [ { color: statusColors[status], x: scale * 0.82, y: scale * 0.08 }, { color: 0x22d3ee, x: scale * 0.87, y: scale * 0.08 }, { color: 0x22c55e, x: scale * 0.82, y: -scale * 0.08 }, { color: 0xf59e0b, x: scale * 0.87, y: -scale * 0.08 }, ]; ledPositions.forEach(({ color, x, y }) => { const led = new THREE.Mesh( new THREE.SphereGeometry(0.018, 8, 8), createLedMaterial(color, 1.0) ); led.position.set(x, y, scale * 0.44); group.add(led); }); return group; } // ============================================================ // 3. 路由器 Router - 1U 机架式 + 天线 + 端口阵列 // ============================================================ export function createRouterModel(status: DeviceStatus, scale = 1.8): THREE.Group { const group = new THREE.Group(); const bodyMat = createBodyMaterial(status, 'router'); const panelMat = createPanelMaterial(); const portMat = createPortMaterial(); const earMat = new THREE.MeshStandardMaterial({ color: 0x475569, metalness: 0.8, roughness: 0.3 }); // 1U 主体机箱 const body = new THREE.Mesh( new THREE.BoxGeometry(scale * 1.6, scale * 0.35, scale * 0.9), bodyMat ); body.userData.isBody = true; group.add(body); // 前面板 const frontPanel = new THREE.Mesh( new THREE.BoxGeometry(scale * 1.62, scale * 0.31, scale * 0.03), panelMat ); frontPanel.position.z = scale * 0.47; group.add(frontPanel); // 机架耳 const leftEar = new THREE.Mesh( new THREE.BoxGeometry(scale * 0.1, scale * 0.28, scale * 0.12), earMat ); leftEar.position.set(-scale * 0.86, 0, 0); group.add(leftEar); const rightEar = leftEar.clone(); rightEar.position.x = scale * 0.86; group.add(rightEar); // 品牌区域 + 状态 LED const brandArea = new THREE.Mesh( new THREE.BoxGeometry(scale * 0.22, scale * 0.07, 0.035), new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.6, roughness: 0.4 }) ); brandArea.position.set(-scale * 0.65, scale * 0.1, scale * 0.49); group.add(brandArea); // 状态 LED 组 const routerLeds = [ { color: statusColors[status], x: -scale * 0.38 }, { color: 0x22d3ee, x: -scale * 0.33 }, { color: 0x22c55e, x: -scale * 0.28 }, { color: 0xf59e0b, x: -scale * 0.23 }, ]; routerLeds.forEach(({ color, x }) => { const led = new THREE.Mesh( new THREE.SphereGeometry(0.018, 8, 8), createLedMaterial(color, 1.0) ); led.position.set(x, scale * 0.1, scale * 0.49); group.add(led); }); // WAN 口(光纤口样式) const sfpMat = new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.7, roughness: 0.3 }); const wanPort = new THREE.Mesh( new THREE.BoxGeometry(scale * 0.12, scale * 0.14, scale * 0.06), sfpMat ); wanPort.position.set(-scale * 0.05, -scale * 0.02, scale * 0.5); group.add(wanPort); const wanRing = new THREE.Mesh( new THREE.TorusGeometry(scale * 0.045, 0.008, 6, 16), new THREE.MeshStandardMaterial({ color: 0xfbbf24, emissive: 0xfbbf24, emissiveIntensity: 0.6, metalness: 0.5, roughness: 0.3 }) ); wanRing.rotation.x = Math.PI / 2; wanRing.position.set(-scale * 0.05, scale * 0.08, scale * 0.53); group.add(wanRing); // LAN 口阵列(4个) for (let i = 0; i < 4; i++) { const port = new THREE.Mesh( new THREE.BoxGeometry(scale * 0.1, scale * 0.13, scale * 0.05), portMat ); port.position.set(scale * 0.2 + i * scale * 0.13, -scale * 0.02, scale * 0.5); group.add(port); const lanLed = new THREE.Mesh( new THREE.SphereGeometry(0.012, 6, 6), createLedMaterial(0x22c55e, 0.9) ); lanLed.position.set(scale * 0.2 + i * scale * 0.13, -scale * 0.11, scale * 0.52); group.add(lanLed); } // 天线 const antennaMat = new THREE.MeshStandardMaterial({ color: 0x64748b, metalness: 0.9, roughness: 0.2 }); // 天线 1 - 左侧直立 const ant1 = new THREE.Mesh( new THREE.CylinderGeometry(0.012, 0.015, scale * 0.7, 8), antennaMat ); ant1.position.set(-scale * 0.55, scale * 0.5, -scale * 0.1); group.add(ant1); const ant1Tip = new THREE.Mesh( new THREE.SphereGeometry(0.022, 10, 10), createLedMaterial(0x22d3ee, 1.2) ); ant1Tip.position.set(-scale * 0.55, scale * 0.87, -scale * 0.1); group.add(ant1Tip); // 天线 2 - 右侧略向后倾斜 const ant2 = new THREE.Mesh( new THREE.CylinderGeometry(0.012, 0.015, scale * 0.65, 8), antennaMat ); ant2.position.set(scale * 0.55, scale * 0.48, -scale * 0.15); ant2.rotation.x = 0.15; ant2.rotation.z = -0.1; group.add(ant2); const ant2Tip = new THREE.Mesh( new THREE.SphereGeometry(0.022, 10, 10), createLedMaterial(0x22d3ee, 1.2) ); ant2Tip.position.set(scale * 0.52, scale * 0.83, -scale * 0.2); group.add(ant2Tip); return group; } // ============================================================ // 4. 防火墙 Firewall - 1U 机架式 + LCD 屏 + 盾形浮雕 // ============================================================ export function createFirewallModel(status: DeviceStatus, scale = 1.8): THREE.Group { const group = new THREE.Group(); const bodyMat = createBodyMaterial(status, 'firewall'); const panelMat = createPanelMaterial(); const portMat = createPortMaterial(); const earMat = new THREE.MeshStandardMaterial({ color: 0x475569, metalness: 0.8, roughness: 0.3 }); // 1U 主体机箱 const body = new THREE.Mesh( new THREE.BoxGeometry(scale * 1.8, scale * 0.36, scale * 0.9), bodyMat ); body.userData.isBody = true; group.add(body); // 前面板 const frontPanel = new THREE.Mesh( new THREE.BoxGeometry(scale * 1.82, scale * 0.32, scale * 0.03), panelMat ); frontPanel.position.z = scale * 0.48; group.add(frontPanel); // 机架耳 const leftEar = new THREE.Mesh( new THREE.BoxGeometry(scale * 0.1, scale * 0.29, scale * 0.12), earMat ); leftEar.position.set(-scale * 0.97, 0, 0); group.add(leftEar); const rightEar = leftEar.clone(); rightEar.position.x = scale * 0.97; group.add(rightEar); // 红色安全装饰条 const redStrip = new THREE.Mesh( new THREE.BoxGeometry(scale * 1.55, 0.018, 0.035), new THREE.MeshStandardMaterial({ color: 0xef4444, emissive: 0xef4444, emissiveIntensity: 0.4, metalness: 0.3, roughness: 0.5, }) ); redStrip.position.set(0, scale * 0.15, scale * 0.5); group.add(redStrip); // LCD 状态显示屏 const lcdFrame = new THREE.Mesh( new THREE.BoxGeometry(scale * 0.32, scale * 0.2, scale * 0.04), new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.6, roughness: 0.4 }) ); lcdFrame.position.set(-scale * 0.55, -scale * 0.02, scale * 0.49); group.add(lcdFrame); // LCD 显示面(发光蓝绿色) const lcdScreen = new THREE.Mesh( new THREE.BoxGeometry(scale * 0.28, scale * 0.16, 0.01), new THREE.MeshStandardMaterial({ color: 0x0d9488, emissive: 0x14b8a6, emissiveIntensity: 0.7, metalness: 0.2, roughness: 0.3, }) ); lcdScreen.position.set(-scale * 0.55, -scale * 0.02, scale * 0.52); group.add(lcdScreen); // 盾形浮雕 const shieldEmbossMat = new THREE.MeshStandardMaterial({ color: 0x6366f1, metalness: 0.7, roughness: 0.3, emissive: 0x6366f1, emissiveIntensity: 0.35, }); const shieldTop = new THREE.Mesh( new THREE.ConeGeometry(scale * 0.08, scale * 0.14, 4), shieldEmbossMat ); shieldTop.rotation.y = Math.PI / 4; shieldTop.position.set(-scale * 0.18, -scale * 0.03, scale * 0.5); group.add(shieldTop); const shieldBody = new THREE.Mesh( new THREE.BoxGeometry(scale * 0.12, scale * 0.14, 0.02), shieldEmbossMat ); shieldBody.position.set(-scale * 0.18, -scale * 0.13, scale * 0.5); group.add(shieldBody); // 盾心 const shieldCore = new THREE.Mesh( new THREE.SphereGeometry(0.012, 8, 8), createLedMaterial(0x22d3ee, 1.2) ); shieldCore.position.set(-scale * 0.18, -scale * 0.07, scale * 0.52); group.add(shieldCore); // 状态 LED 组 const fwLeds = [ { color: statusColors[status], x: scale * 0.02 }, { color: 0xef4444, x: scale * 0.08 }, { color: 0xf59e0b, x: scale * 0.14 }, { color: 0x22c55e, x: scale * 0.2 }, ]; fwLeds.forEach(({ color, x }) => { const led = new THREE.Mesh( new THREE.SphereGeometry(0.018, 8, 8), createLedMaterial(color, 1.0) ); led.position.set(x, scale * 0.08, scale * 0.5); group.add(led); }); // SFP 口 const sfpMat = new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.7, roughness: 0.3 }); for (let i = 0; i < 2; i++) { const sfp = new THREE.Mesh( new THREE.BoxGeometry(scale * 0.1, scale * 0.12, scale * 0.06), sfpMat ); sfp.position.set(scale * 0.45 + i * scale * 0.15, -scale * 0.02, scale * 0.5); group.add(sfp); const sfpLed = new THREE.Mesh( new THREE.SphereGeometry(0.012, 6, 6), createLedMaterial(0x22d3ee, 1.0) ); sfpLed.position.set(scale * 0.45 + i * scale * 0.15, -scale * 0.12, scale * 0.52); group.add(sfpLed); } // 网口 for (let i = 0; i < 2; i++) { const port = new THREE.Mesh( new THREE.BoxGeometry(scale * 0.08, scale * 0.1, scale * 0.05), portMat ); port.position.set(scale * 0.78 + i * scale * 0.11, -scale * 0.03, scale * 0.5); group.add(port); const portLed = new THREE.Mesh( new THREE.SphereGeometry(0.01, 6, 6), createLedMaterial(0x22c55e, 0.9) ); portLed.position.set(scale * 0.78 + i * scale * 0.11, -scale * 0.11, scale * 0.52); group.add(portLed); } return group; } // ============================================================ // 5. 存储 Storage - 磁盘阵列 // ============================================================ export function createStorageModel(status: DeviceStatus, scale = 1.5): THREE.Group { const group = new THREE.Group(); const bodyMat = createBodyMaterial(status, 'storage'); const panelMat = createPanelMaterial(); // 主体大型方体 const body = new THREE.Mesh( new THREE.BoxGeometry(scale * 1.4, scale * 0.9, scale * 1.1), bodyMat ); body.userData.isBody = true; group.add(body); // 前面板 const frontPanel = new THREE.Mesh( new THREE.BoxGeometry(scale * 1.42, scale * 0.82, scale * 0.04), panelMat ); frontPanel.position.z = scale * 0.57; group.add(frontPanel); // 磁盘槽位(3行 x 4列) const driveW = scale * 0.28; const driveH = scale * 0.18; const startX = -scale * 0.55; const startY = scale * 0.32; for (let row = 0; row < 3; row++) { for (let col = 0; col < 4; col++) { const drive = new THREE.Mesh( new THREE.BoxGeometry(driveW, driveH, scale * 0.025), new THREE.MeshStandardMaterial({ color: row === 2 && col >= 2 ? 0x0f172a : 0x1e293b, metalness: 0.4, roughness: 0.5, }) ); drive.position.set( startX + col * (driveW + 0.02), startY - row * (driveH + 0.04), scale * 0.6 ); group.add(drive); // 磁盘 LED const isActive = !(row === 2 && col >= 2); const ledColor = isActive ? row === 0 && col === 0 ? statusColors[status] : 0x22c55e : 0x475569; const led = new THREE.Mesh( new THREE.SphereGeometry(0.015, 8, 8), createLedMaterial(ledColor, isActive ? 0.8 : 0.3) ); led.position.set( startX + col * (driveW + 0.02) - driveW * 0.35, startY - row * (driveH + 0.04), scale * 0.62 ); group.add(led); } } // 状态区域 const statusArea = new THREE.Mesh( new THREE.BoxGeometry(scale * 0.3, scale * 0.1, scale * 0.045), new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.6, roughness: 0.4 }) ); statusArea.position.set(-scale * 0.5, -scale * 0.32, scale * 0.59); group.add(statusArea); const stLeds = [ { color: statusColors[status], x: -scale * 0.58 }, { color: 0x22d3ee, x: -scale * 0.53 }, { color: 0x22c55e, x: -scale * 0.48 }, ]; stLeds.forEach(({ color, x }) => { const led = new THREE.Mesh( new THREE.SphereGeometry(0.02, 10, 10), createLedMaterial(color, 1.0) ); led.position.set(x, -scale * 0.32, scale * 0.61); group.add(led); }); return group; } // ============================================================ // 统一的设备模型创建入口 // ============================================================ export function createDeviceModel(type: DeviceType, status: DeviceStatus, scale?: number): THREE.Group { switch (type) { case 'server': return createServerModel(status, scale ?? 1.5); case 'switch': return createSwitchModel(status, scale ?? 1.8); case 'router': return createRouterModel(status, scale ?? 2.0); case 'firewall': return createFirewallModel(status, scale ?? 2.0); case 'storage': return createStorageModel(status, scale ?? 1.6); case 'workstation': // 工作站用服务器模型代替(简化版) return createServerModel(status, scale ?? 1.4); default: return createServerModel(status, scale ?? 1.5); } }