@d5techs/3dgs-lib 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +236 -0
- package/dist/3dgs-lib.cjs +12012 -0
- package/dist/3dgs-lib.cjs.map +1 -0
- package/dist/3dgs-lib.js +12012 -0
- package/dist/3dgs-lib.js.map +1 -0
- package/dist/App.d.ts +142 -0
- package/dist/core/BoundingBoxRenderer.d.ts +70 -0
- package/dist/core/Camera.d.ts +38 -0
- package/dist/core/OrbitControls.d.ts +101 -0
- package/dist/core/Renderer.d.ts +69 -0
- package/dist/core/ViewportGizmo.d.ts +83 -0
- package/dist/core/gizmo/ArcShape.d.ts +90 -0
- package/dist/core/gizmo/ArrowShape.d.ts +51 -0
- package/dist/core/gizmo/BoxLineShape.d.ts +47 -0
- package/dist/core/gizmo/PlaneShape.d.ts +48 -0
- package/dist/core/gizmo/Shape.d.ts +117 -0
- package/dist/core/gizmo/SphereShape.d.ts +29 -0
- package/dist/core/gizmo/TransformGizmoV2.d.ts +203 -0
- package/dist/core/gizmo/index.d.ts +14 -0
- package/dist/core/math/Mat4.d.ts +38 -0
- package/dist/core/math/Quat.d.ts +52 -0
- package/dist/core/math/Ray.d.ts +65 -0
- package/dist/core/math/Vec3.d.ts +39 -0
- package/dist/gs/GSSplatRenderer.d.ts +217 -0
- package/dist/gs/GSSplatRendererMobile.d.ts +147 -0
- package/dist/gs/GSSplatSorter.d.ts +105 -0
- package/dist/gs/GSSplatSorterMobile.d.ts +86 -0
- package/dist/gs/IGSSplatRenderer.d.ts +123 -0
- package/dist/gs/PLYLoader.d.ts +22 -0
- package/dist/gs/PLYLoaderMobile.d.ts +62 -0
- package/dist/gs/SplatLoader.d.ts +25 -0
- package/dist/gs/TextureCompressor.d.ts +57 -0
- package/dist/index.d.ts +44 -0
- package/dist/interaction/GizmoManager.d.ts +132 -0
- package/dist/loaders/GLBLoader.d.ts +67 -0
- package/dist/loaders/MTLParser.d.ts +65 -0
- package/dist/loaders/OBJLoader.d.ts +68 -0
- package/dist/loaders/OBJParser.d.ts +115 -0
- package/dist/mesh/Mesh.d.ts +52 -0
- package/dist/mesh/MeshRenderer.d.ts +74 -0
- package/dist/scene/SceneManager.d.ts +136 -0
- package/package.json +62 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"3dgs-lib.cjs","sources":["../src/core/Renderer.ts","../src/core/Camera.ts","../src/core/OrbitControls.ts","../src/core/ViewportGizmo.ts","../src/core/BoundingBoxRenderer.ts","../src/mesh/Mesh.ts","../src/mesh/MeshRenderer.ts","../src/loaders/GLBLoader.ts","../src/loaders/OBJParser.ts","../src/loaders/MTLParser.ts","../src/loaders/OBJLoader.ts","../src/gs/PLYLoader.ts","../src/gs/PLYLoaderMobile.ts","../src/gs/SplatLoader.ts","../src/gs/GSSplatSorter.ts","../src/gs/IGSSplatRenderer.ts","../src/gs/GSSplatRenderer.ts","../src/gs/TextureCompressor.ts","../src/gs/GSSplatSorterMobile.ts","../src/gs/GSSplatRendererMobile.ts","../src/scene/SceneManager.ts","../src/core/math/Vec3.ts","../src/core/math/Ray.ts","../src/core/math/Quat.ts","../src/core/math/Mat4.ts","../src/core/gizmo/Shape.ts","../src/core/gizmo/ArrowShape.ts","../src/core/gizmo/PlaneShape.ts","../src/core/gizmo/SphereShape.ts","../src/core/gizmo/ArcShape.ts","../src/core/gizmo/BoxLineShape.ts","../src/core/gizmo/TransformGizmoV2.ts","../src/interaction/GizmoManager.ts","../src/App.ts"],"sourcesContent":["/**\r\n * Renderer - WebGPU 初始化 + 帧提交\r\n * 只负责 WebGPU 设备管理和渲染通道\r\n */\r\nexport class Renderer {\r\n private canvas: HTMLCanvasElement;\r\n private _device!: GPUDevice;\r\n private _context!: GPUCanvasContext;\r\n private _format!: GPUTextureFormat;\r\n private _depthTexture!: GPUTexture;\r\n private _depthTextureView!: GPUTextureView;\r\n \r\n private commandEncoder!: GPUCommandEncoder;\r\n private renderPassEncoder!: GPURenderPassEncoder;\r\n\r\n // ResizeObserver 引用(用于清理)\r\n private resizeObserver: ResizeObserver | null = null;\r\n\r\n // 背景颜色\r\n private _clearColor: GPUColorDict = { r: 0.15, g: 0.15, b: 0.15, a: 1.0 };\r\n\r\n constructor(canvas: HTMLCanvasElement) {\r\n this.canvas = canvas;\r\n }\r\n\r\n /**\r\n * 设置背景颜色\r\n */\r\n setClearColor(r: number, g: number, b: number, a: number = 1.0): void {\r\n this._clearColor = { r, g, b, a };\r\n }\r\n\r\n /**\r\n * 通过十六进制设置背景颜色\r\n */\r\n setClearColorHex(hex: string): void {\r\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\r\n if (result) {\r\n this._clearColor = {\r\n r: parseInt(result[1], 16) / 255,\r\n g: parseInt(result[2], 16) / 255,\r\n b: parseInt(result[3], 16) / 255,\r\n a: 1.0,\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * 获取背景颜色(十六进制)\r\n */\r\n getClearColorHex(): string {\r\n const r = Math.round(this._clearColor.r * 255).toString(16).padStart(2, '0');\r\n const g = Math.round(this._clearColor.g * 255).toString(16).padStart(2, '0');\r\n const b = Math.round(this._clearColor.b * 255).toString(16).padStart(2, '0');\r\n return `#${r}${g}${b}`;\r\n }\r\n\r\n get device(): GPUDevice {\r\n return this._device;\r\n }\r\n\r\n get context(): GPUCanvasContext {\r\n return this._context;\r\n }\r\n\r\n get format(): GPUTextureFormat {\r\n return this._format;\r\n }\r\n\r\n get depthFormat(): GPUTextureFormat {\r\n return 'depth24plus';\r\n }\r\n\r\n /**\r\n * 获取渲染宽度(像素)\r\n */\r\n get width(): number {\r\n return this.canvas.width;\r\n }\r\n\r\n /**\r\n * 获取渲染高度(像素)\r\n */\r\n get height(): number {\r\n return this.canvas.height;\r\n }\r\n\r\n /**\r\n * 初始化 WebGPU\r\n */\r\n async init(): Promise<void> {\r\n // 检查 WebGPU 支持\r\n if (!navigator.gpu) {\r\n throw new Error('WebGPU 不受支持');\r\n }\r\n\r\n // 获取适配器\r\n const adapter = await navigator.gpu.requestAdapter({\r\n powerPreference: 'high-performance',\r\n });\r\n if (!adapter) {\r\n throw new Error('无法获取 GPU 适配器');\r\n }\r\n\r\n // 获取设备,请求更高的缓冲区大小限制以支持大型模型\r\n const adapterLimits = adapter.limits;\r\n this._device = await adapter.requestDevice({\r\n requiredLimits: {\r\n maxBufferSize: adapterLimits.maxBufferSize,\r\n maxStorageBufferBindingSize: adapterLimits.maxStorageBufferBindingSize,\r\n },\r\n });\r\n this._device.lost.then((info) => {\r\n // GPU 设备丢失(静默处理)\r\n });\r\n\r\n // 配置 canvas 上下文\r\n this._context = this.canvas.getContext('webgpu') as GPUCanvasContext;\r\n if (!this._context) {\r\n throw new Error('无法获取 WebGPU 上下文');\r\n }\r\n\r\n this._format = navigator.gpu.getPreferredCanvasFormat();\r\n this._context.configure({\r\n device: this._device,\r\n format: this._format,\r\n alphaMode: 'premultiplied',\r\n colorSpace: 'srgb',\r\n });\r\n\r\n // 创建深度纹理\r\n this.createDepthTexture();\r\n\r\n // 监听 canvas 大小变化\r\n this.setupResizeObserver();\r\n }\r\n\r\n /**\r\n * 创建深度纹理\r\n */\r\n private createDepthTexture(): void {\r\n if (this._depthTexture) {\r\n this._depthTexture.destroy();\r\n }\r\n\r\n this._depthTexture = this._device.createTexture({\r\n size: {\r\n width: this.canvas.width,\r\n height: this.canvas.height,\r\n },\r\n format: this.depthFormat,\r\n usage: GPUTextureUsage.RENDER_ATTACHMENT,\r\n });\r\n this._depthTextureView = this._depthTexture.createView();\r\n }\r\n\r\n /**\r\n * 设置 resize 监听\r\n */\r\n private setupResizeObserver(): void {\r\n this.resizeObserver = new ResizeObserver((entries) => {\r\n for (const entry of entries) {\r\n const { width, height } = entry.contentRect;\r\n \r\n // 检测是否为移动设备\r\n const isMobile = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(\r\n navigator.userAgent.toLowerCase()\r\n );\r\n \r\n // 移动端限制 DPI 为 1.5,避免 canvas 过大导致 GPU 崩溃\r\n // 桌面端使用完整 DPI\r\n const maxDpr = isMobile ? 1.5 : 3;\r\n const dpr = Math.min(window.devicePixelRatio || 1, maxDpr);\r\n \r\n this.canvas.width = Math.floor(width * dpr);\r\n this.canvas.height = Math.floor(height * dpr);\r\n \r\n this.createDepthTexture();\r\n }\r\n });\r\n this.resizeObserver.observe(this.canvas);\r\n }\r\n\r\n /**\r\n * 销毁渲染器资源\r\n */\r\n destroy(): void {\r\n // 断开 ResizeObserver\r\n if (this.resizeObserver) {\r\n this.resizeObserver.disconnect();\r\n this.resizeObserver = null;\r\n }\r\n\r\n // 销毁深度纹理\r\n if (this._depthTexture) {\r\n this._depthTexture.destroy();\r\n }\r\n }\r\n\r\n /**\r\n * 开始帧 - 创建命令编码器和渲染通道\r\n */\r\n beginFrame(): GPURenderPassEncoder {\r\n const colorTexture = this._context.getCurrentTexture();\r\n const colorView = colorTexture.createView();\r\n\r\n this.commandEncoder = this._device.createCommandEncoder();\r\n \r\n this.renderPassEncoder = this.commandEncoder.beginRenderPass({\r\n colorAttachments: [\r\n {\r\n view: colorView,\r\n clearValue: this._clearColor,\r\n loadOp: 'clear',\r\n storeOp: 'store',\r\n },\r\n ],\r\n depthStencilAttachment: {\r\n view: this._depthTextureView,\r\n depthClearValue: 1.0,\r\n depthLoadOp: 'clear',\r\n depthStoreOp: 'store',\r\n },\r\n });\r\n\r\n return this.renderPassEncoder;\r\n }\r\n\r\n /**\r\n * 结束帧 - 提交命令\r\n */\r\n endFrame(): void {\r\n this.renderPassEncoder.end();\r\n this._device.queue.submit([this.commandEncoder.finish()]);\r\n }\r\n\r\n /**\r\n * 获取 canvas 宽高比\r\n */\r\n getAspectRatio(): number {\r\n return this.canvas.width / this.canvas.height;\r\n }\r\n}\r\n","/**\r\n * Camera - 相机矩阵计算\r\n * 只负责视图矩阵和投影矩阵\r\n */\r\nexport class Camera {\r\n // 相机参数\r\n position: Float32Array = new Float32Array([0, 0, 5]);\r\n target: Float32Array = new Float32Array([0, 0, 0]);\r\n up: Float32Array = new Float32Array([0, 1, 0]);\r\n\r\n // 投影参数\r\n fov: number = Math.PI / 4; // 45度\r\n aspect: number = 1;\r\n near: number = 0.001;\r\n far: number = 10000;\r\n\r\n // 矩阵\r\n viewMatrix: Float32Array = new Float32Array(16);\r\n projectionMatrix: Float32Array = new Float32Array(16);\r\n viewProjectionMatrix: Float32Array = new Float32Array(16);\r\n\r\n constructor() {\r\n this.updateMatrix();\r\n }\r\n\r\n /**\r\n * 设置宽高比\r\n */\r\n setAspect(aspect: number): void {\r\n this.aspect = aspect;\r\n }\r\n\r\n /**\r\n * 更新视图和投影矩阵\r\n */\r\n updateMatrix(): void {\r\n this.updateViewMatrix();\r\n this.updateProjectionMatrix();\r\n this.multiplyMatrices(\r\n this.viewProjectionMatrix,\r\n this.projectionMatrix,\r\n this.viewMatrix,\r\n );\r\n }\r\n\r\n /**\r\n * 计算视图矩阵 (lookAt)\r\n */\r\n private updateViewMatrix(): void {\r\n const eye = this.position;\r\n const target = this.target;\r\n const up = this.up;\r\n\r\n // 计算相机坐标系\r\n const zAxis = this.normalize(this.subtract(eye, target));\r\n const xAxis = this.normalize(this.cross(up, zAxis));\r\n const yAxis = this.cross(zAxis, xAxis);\r\n\r\n // 构建视图矩阵 (列主序)\r\n this.viewMatrix[0] = xAxis[0];\r\n this.viewMatrix[1] = yAxis[0];\r\n this.viewMatrix[2] = zAxis[0];\r\n this.viewMatrix[3] = 0;\r\n\r\n this.viewMatrix[4] = xAxis[1];\r\n this.viewMatrix[5] = yAxis[1];\r\n this.viewMatrix[6] = zAxis[1];\r\n this.viewMatrix[7] = 0;\r\n\r\n this.viewMatrix[8] = xAxis[2];\r\n this.viewMatrix[9] = yAxis[2];\r\n this.viewMatrix[10] = zAxis[2];\r\n this.viewMatrix[11] = 0;\r\n\r\n this.viewMatrix[12] = -this.dot(xAxis, eye);\r\n this.viewMatrix[13] = -this.dot(yAxis, eye);\r\n this.viewMatrix[14] = -this.dot(zAxis, eye);\r\n this.viewMatrix[15] = 1;\r\n }\r\n\r\n /**\r\n * 计算投影矩阵 (透视投影)\r\n */\r\n private updateProjectionMatrix(): void {\r\n const f = 1.0 / Math.tan(this.fov / 2);\r\n const rangeInv = 1 / (this.near - this.far);\r\n\r\n // 列主序\r\n this.projectionMatrix[0] = f / this.aspect;\r\n this.projectionMatrix[1] = 0;\r\n this.projectionMatrix[2] = 0;\r\n this.projectionMatrix[3] = 0;\r\n\r\n this.projectionMatrix[4] = 0;\r\n this.projectionMatrix[5] = f;\r\n this.projectionMatrix[6] = 0;\r\n this.projectionMatrix[7] = 0;\r\n\r\n this.projectionMatrix[8] = 0;\r\n this.projectionMatrix[9] = 0;\r\n this.projectionMatrix[10] = (this.near + this.far) * rangeInv;\r\n this.projectionMatrix[11] = -1;\r\n\r\n this.projectionMatrix[12] = 0;\r\n this.projectionMatrix[13] = 0;\r\n this.projectionMatrix[14] = this.near * this.far * rangeInv * 2;\r\n this.projectionMatrix[15] = 0;\r\n }\r\n\r\n // ========== 向量/矩阵工具函数 ==========\r\n\r\n private subtract(a: Float32Array, b: Float32Array): Float32Array {\r\n return new Float32Array([a[0] - b[0], a[1] - b[1], a[2] - b[2]]);\r\n }\r\n\r\n private cross(a: Float32Array, b: Float32Array): Float32Array {\r\n return new Float32Array([\r\n a[1] * b[2] - a[2] * b[1],\r\n a[2] * b[0] - a[0] * b[2],\r\n a[0] * b[1] - a[1] * b[0],\r\n ]);\r\n }\r\n\r\n private dot(a: Float32Array, b: Float32Array): number {\r\n return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];\r\n }\r\n\r\n private normalize(v: Float32Array): Float32Array {\r\n const len = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);\r\n if (len < 1e-10) return new Float32Array([0, 0, 1]); // 返回默认方向,避免除零\r\n return new Float32Array([v[0] / len, v[1] / len, v[2] / len]);\r\n }\r\n\r\n private multiplyMatrices(\r\n out: Float32Array,\r\n a: Float32Array,\r\n b: Float32Array,\r\n ): void {\r\n for (let i = 0; i < 4; i++) {\r\n for (let j = 0; j < 4; j++) {\r\n out[i * 4 + j] =\r\n a[j] * b[i * 4] +\r\n a[j + 4] * b[i * 4 + 1] +\r\n a[j + 8] * b[i * 4 + 2] +\r\n a[j + 12] * b[i * 4 + 3];\r\n }\r\n }\r\n }\r\n}\r\n","import { Camera } from \"./Camera\";\r\n\r\n/**\r\n * OrbitControls - 轨道控制器\r\n * 只负责鼠标/触摸输入控制相机\r\n */\r\nexport class OrbitControls {\r\n private camera: Camera;\r\n private canvas: HTMLCanvasElement;\r\n\r\n // 球坐标参数\r\n distance: number = 5;\r\n theta: number = 0; // 水平角 (绕Y轴)\r\n phi: number = Math.PI / 4; // 垂直角 (从Y轴向下)\r\n\r\n // 限制\r\n minDistance: number = 0.001;\r\n maxDistance: number = Infinity;\r\n minPhi: number = 0.01;\r\n maxPhi: number = Math.PI - 0.01;\r\n\r\n // 灵敏度\r\n rotateSpeed: number = 0.005;\r\n zoomSpeed: number = 0.001;\r\n panSpeed: number = 0.005;\r\n \r\n // 移动端触摸灵敏度\r\n touchZoomSpeed: number = 0.01;\r\n touchPanSpeed: number = 0.003;\r\n\r\n // 状态\r\n private isDragging: boolean = false;\r\n private lastX: number = 0;\r\n private lastY: number = 0;\r\n\r\n // 触摸手势状态\r\n private touchMode: 'none' | 'rotate' | 'zoom-pan' = 'none';\r\n private lastTouchDistance: number = 0;\r\n private lastTouchCenter: { x: number; y: number } = { x: 0, y: 0 };\r\n\r\n // 启用/禁用\r\n enabled: boolean = true;\r\n\r\n // 绑定的事件处理函数(用于移除监听器)\r\n private boundOnMouseDown: (e: MouseEvent) => void;\r\n private boundOnMouseMove: (e: MouseEvent) => void;\r\n private boundOnMouseUp: (e: MouseEvent) => void;\r\n private boundOnWheel: (e: WheelEvent) => void;\r\n private boundOnTouchStart: (e: TouchEvent) => void;\r\n private boundOnTouchMove: (e: TouchEvent) => void;\r\n private boundOnTouchEnd: (e: TouchEvent) => void;\r\n private boundOnContextMenu: (e: Event) => void;\r\n\r\n constructor(camera: Camera, canvas: HTMLCanvasElement) {\r\n this.camera = camera;\r\n this.canvas = canvas;\r\n\r\n // 绑定事件处理函数\r\n this.boundOnMouseDown = this.onMouseDown.bind(this);\r\n this.boundOnMouseMove = this.onMouseMove.bind(this);\r\n this.boundOnMouseUp = this.onMouseUp.bind(this);\r\n this.boundOnWheel = this.onWheel.bind(this);\r\n this.boundOnTouchStart = this.onTouchStart.bind(this);\r\n this.boundOnTouchMove = this.onTouchMove.bind(this);\r\n this.boundOnTouchEnd = this.onTouchEnd.bind(this);\r\n this.boundOnContextMenu = (e: Event) => e.preventDefault();\r\n\r\n this.setupEventListeners();\r\n this.update();\r\n }\r\n\r\n /**\r\n * 设置事件监听\r\n */\r\n private setupEventListeners(): void {\r\n // 鼠标事件\r\n this.canvas.addEventListener(\"mousedown\", this.boundOnMouseDown);\r\n this.canvas.addEventListener(\"mousemove\", this.boundOnMouseMove);\r\n this.canvas.addEventListener(\"mouseup\", this.boundOnMouseUp);\r\n this.canvas.addEventListener(\"mouseleave\", this.boundOnMouseUp);\r\n this.canvas.addEventListener(\"wheel\", this.boundOnWheel, {\r\n passive: false,\r\n });\r\n\r\n // 触摸事件\r\n this.canvas.addEventListener(\"touchstart\", this.boundOnTouchStart, {\r\n passive: false,\r\n });\r\n this.canvas.addEventListener(\"touchmove\", this.boundOnTouchMove, {\r\n passive: false,\r\n });\r\n this.canvas.addEventListener(\"touchend\", this.boundOnTouchEnd);\r\n\r\n // 禁用右键菜单\r\n this.canvas.addEventListener(\"contextmenu\", this.boundOnContextMenu);\r\n }\r\n\r\n /**\r\n * 移除事件监听\r\n */\r\n private removeEventListeners(): void {\r\n this.canvas.removeEventListener(\"mousedown\", this.boundOnMouseDown);\r\n this.canvas.removeEventListener(\"mousemove\", this.boundOnMouseMove);\r\n this.canvas.removeEventListener(\"mouseup\", this.boundOnMouseUp);\r\n this.canvas.removeEventListener(\"mouseleave\", this.boundOnMouseUp);\r\n this.canvas.removeEventListener(\"wheel\", this.boundOnWheel);\r\n this.canvas.removeEventListener(\"touchstart\", this.boundOnTouchStart);\r\n this.canvas.removeEventListener(\"touchmove\", this.boundOnTouchMove);\r\n this.canvas.removeEventListener(\"touchend\", this.boundOnTouchEnd);\r\n this.canvas.removeEventListener(\"contextmenu\", this.boundOnContextMenu);\r\n }\r\n\r\n /**\r\n * 销毁控制器\r\n */\r\n destroy(): void {\r\n this.removeEventListeners();\r\n }\r\n\r\n private onMouseDown(e: MouseEvent): void {\r\n if (!this.enabled) return;\r\n this.isDragging = true;\r\n this.lastX = e.clientX;\r\n this.lastY = e.clientY;\r\n }\r\n\r\n private onMouseMove(e: MouseEvent): void {\r\n if (!this.enabled || !this.isDragging) return;\r\n\r\n const deltaX = e.clientX - this.lastX;\r\n const deltaY = e.clientY - this.lastY;\r\n this.lastX = e.clientX;\r\n this.lastY = e.clientY;\r\n\r\n // 左键旋转\r\n if (e.buttons === 1) {\r\n this.theta -= deltaX * this.rotateSpeed;\r\n this.phi -= deltaY * this.rotateSpeed; // 修复:向上拖动时相机向上\r\n this.phi = Math.max(this.minPhi, Math.min(this.maxPhi, this.phi));\r\n }\r\n // 右键平移(拖动场景模式:向上拖动场景向上移动)\r\n else if (e.buttons === 2) {\r\n const panX = -deltaX * this.panSpeed * this.distance;\r\n const panY = deltaY * this.panSpeed * this.distance;\r\n\r\n // 计算平移向量\r\n const sinTheta = Math.sin(this.theta);\r\n const cosTheta = Math.cos(this.theta);\r\n\r\n this.camera.target[0] += panX * cosTheta;\r\n this.camera.target[2] += panX * sinTheta;\r\n this.camera.target[1] += panY;\r\n }\r\n\r\n this.update();\r\n }\r\n\r\n private onMouseUp(): void {\r\n this.isDragging = false;\r\n }\r\n\r\n private onWheel(e: WheelEvent): void {\r\n e.preventDefault();\r\n if (!this.enabled) return;\r\n this.distance += e.deltaY * this.zoomSpeed * this.distance;\r\n this.distance = Math.max(\r\n this.minDistance,\r\n Math.min(this.maxDistance, this.distance),\r\n );\r\n this.update();\r\n }\r\n\r\n private onTouchStart(e: TouchEvent): void {\r\n e.preventDefault();\r\n if (!this.enabled) return;\r\n \r\n if (e.touches.length === 1) {\r\n // 单指:旋转模式\r\n this.touchMode = 'rotate';\r\n this.isDragging = true;\r\n this.lastX = e.touches[0].clientX;\r\n this.lastY = e.touches[0].clientY;\r\n } else if (e.touches.length === 2) {\r\n // 双指:缩放+平移模式\r\n this.touchMode = 'zoom-pan';\r\n this.isDragging = true;\r\n this.lastTouchDistance = this.getTouchDistance(e.touches);\r\n this.lastTouchCenter = this.getTouchCenter(e.touches);\r\n }\r\n }\r\n\r\n private onTouchMove(e: TouchEvent): void {\r\n e.preventDefault();\r\n if (!this.enabled || !this.isDragging) return;\r\n\r\n if (e.touches.length === 1 && this.touchMode === 'rotate') {\r\n // 单指旋转\r\n const deltaX = e.touches[0].clientX - this.lastX;\r\n const deltaY = e.touches[0].clientY - this.lastY;\r\n this.lastX = e.touches[0].clientX;\r\n this.lastY = e.touches[0].clientY;\r\n\r\n this.theta -= deltaX * this.rotateSpeed;\r\n this.phi -= deltaY * this.rotateSpeed;\r\n this.phi = Math.max(this.minPhi, Math.min(this.maxPhi, this.phi));\r\n\r\n this.update();\r\n } else if (e.touches.length === 2) {\r\n // 双指缩放 + 平移\r\n const currentDistance = this.getTouchDistance(e.touches);\r\n const currentCenter = this.getTouchCenter(e.touches);\r\n\r\n // 缩放:基于双指距离变化\r\n if (this.lastTouchDistance > 0) {\r\n const scale = this.lastTouchDistance / currentDistance;\r\n this.distance *= Math.pow(scale, this.touchZoomSpeed * 100);\r\n this.distance = Math.max(\r\n this.minDistance,\r\n Math.min(this.maxDistance, this.distance)\r\n );\r\n }\r\n\r\n // 平移:基于双指中心点移动\r\n const deltaX = currentCenter.x - this.lastTouchCenter.x;\r\n const deltaY = currentCenter.y - this.lastTouchCenter.y;\r\n\r\n const panX = -deltaX * this.touchPanSpeed * this.distance;\r\n const panY = deltaY * this.touchPanSpeed * this.distance;\r\n\r\n // 计算平移向量(考虑相机朝向)\r\n const sinTheta = Math.sin(this.theta);\r\n const cosTheta = Math.cos(this.theta);\r\n\r\n this.camera.target[0] += panX * cosTheta;\r\n this.camera.target[2] += panX * sinTheta;\r\n this.camera.target[1] += panY;\r\n\r\n // 更新上一次的触摸状态\r\n this.lastTouchDistance = currentDistance;\r\n this.lastTouchCenter = currentCenter;\r\n\r\n this.update();\r\n }\r\n }\r\n\r\n private onTouchEnd(e: TouchEvent): void {\r\n if (e.touches.length === 0) {\r\n // 所有手指离开\r\n this.isDragging = false;\r\n this.touchMode = 'none';\r\n this.lastTouchDistance = 0;\r\n } else if (e.touches.length === 1) {\r\n // 从双指变为单指,切换到旋转模式\r\n this.touchMode = 'rotate';\r\n this.lastX = e.touches[0].clientX;\r\n this.lastY = e.touches[0].clientY;\r\n }\r\n }\r\n\r\n /**\r\n * 计算双指之间的距离\r\n */\r\n private getTouchDistance(touches: TouchList): number {\r\n const dx = touches[0].clientX - touches[1].clientX;\r\n const dy = touches[0].clientY - touches[1].clientY;\r\n return Math.sqrt(dx * dx + dy * dy);\r\n }\r\n\r\n /**\r\n * 计算双指的中心点\r\n */\r\n private getTouchCenter(touches: TouchList): { x: number; y: number } {\r\n return {\r\n x: (touches[0].clientX + touches[1].clientX) / 2,\r\n y: (touches[0].clientY + touches[1].clientY) / 2,\r\n };\r\n }\r\n\r\n /**\r\n * 根据球坐标更新相机位置\r\n */\r\n update(): void {\r\n const sinPhi = Math.sin(this.phi);\r\n const cosPhi = Math.cos(this.phi);\r\n const sinTheta = Math.sin(this.theta);\r\n const cosTheta = Math.cos(this.theta);\r\n\r\n // 球坐标转笛卡尔坐标\r\n this.camera.position[0] =\r\n this.camera.target[0] + this.distance * sinPhi * sinTheta;\r\n this.camera.position[1] = this.camera.target[1] + this.distance * cosPhi;\r\n this.camera.position[2] =\r\n this.camera.target[2] + this.distance * sinPhi * cosTheta;\r\n\r\n this.camera.updateMatrix();\r\n }\r\n\r\n /**\r\n * 切换到标准视图\r\n * @param axis 轴 'X' | 'Y' | 'Z'\r\n * @param positive 是否正向\r\n * @param animate 是否动画过渡\r\n */\r\n setViewAxis(axis: string, positive: boolean, animate: boolean = true): void {\r\n let targetTheta = this.theta;\r\n let targetPhi = this.phi;\r\n\r\n switch (axis) {\r\n case \"X\":\r\n // X 轴:从右侧看(正)或从左侧看(负)\r\n targetTheta = positive ? Math.PI / 2 : -Math.PI / 2;\r\n targetPhi = Math.PI / 2;\r\n break;\r\n case \"Y\":\r\n // Y 轴:从上方看(正)或从下方看(负)\r\n targetPhi = positive ? 0.01 : Math.PI - 0.01;\r\n break;\r\n case \"Z\":\r\n // Z 轴:从前方看(正)或从后方看(负)\r\n targetTheta = positive ? 0 : Math.PI;\r\n targetPhi = Math.PI / 2;\r\n break;\r\n }\r\n\r\n if (animate) {\r\n this.animateToView(targetTheta, targetPhi);\r\n } else {\r\n this.theta = targetTheta;\r\n this.phi = targetPhi;\r\n this.update();\r\n }\r\n }\r\n\r\n /**\r\n * 动画过渡到目标视图\r\n */\r\n private animateToView(targetTheta: number, targetPhi: number): void {\r\n const startTheta = this.theta;\r\n const startPhi = this.phi;\r\n const duration = 300; // 毫秒\r\n const startTime = performance.now();\r\n\r\n // 计算最短旋转路径\r\n let deltaTheta = targetTheta - startTheta;\r\n while (deltaTheta > Math.PI) deltaTheta -= Math.PI * 2;\r\n while (deltaTheta < -Math.PI) deltaTheta += Math.PI * 2;\r\n\r\n const animate = (currentTime: number) => {\r\n const elapsed = currentTime - startTime;\r\n const progress = Math.min(elapsed / duration, 1);\r\n\r\n // 使用 ease-out 缓动\r\n const eased = 1 - Math.pow(1 - progress, 3);\r\n\r\n this.theta = startTheta + deltaTheta * eased;\r\n this.phi = startPhi + (targetPhi - startPhi) * eased;\r\n this.update();\r\n\r\n if (progress < 1) {\r\n requestAnimationFrame(animate);\r\n }\r\n };\r\n\r\n requestAnimationFrame(animate);\r\n }\r\n\r\n /**\r\n * 设置相机目标点(控制器旋转中心)\r\n * @param x X 坐标\r\n * @param y Y 坐标\r\n * @param z Z 坐标\r\n */\r\n setTarget(x: number, y: number, z: number): void {\r\n this.camera.target[0] = x;\r\n this.camera.target[1] = y;\r\n this.camera.target[2] = z;\r\n this.update();\r\n }\r\n\r\n /**\r\n * 获取当前目标点\r\n */\r\n getTarget(): [number, number, number] {\r\n return [\r\n this.camera.target[0],\r\n this.camera.target[1],\r\n this.camera.target[2],\r\n ];\r\n }\r\n\r\n /**\r\n * 根据模型参数自动调整相机位置和参数\r\n * @param center 模型中心点\r\n * @param radius 模型包围球半径\r\n * @param animate 是否使用动画过渡\r\n */\r\n frameModel(\r\n center: [number, number, number],\r\n radius: number,\r\n animate: boolean = true,\r\n ): void {\r\n // 计算合适的相机距离:确保模型完全在视野内\r\n // distance = radius / tan(fov/2),加一些余量\r\n const fovRad = this.camera.fov;\r\n const halfFov = fovRad / 2;\r\n const marginFactor = 1.5; // 留一些边距\r\n const targetDistance = (radius / Math.tan(halfFov)) * marginFactor;\r\n\r\n // 确保距离不会太小\r\n const clampedDistance = Math.max(this.minDistance, targetDistance);\r\n\r\n if (animate) {\r\n this.animateToFrame(center, clampedDistance);\r\n } else {\r\n // 直接设置\r\n this.camera.target[0] = center[0];\r\n this.camera.target[1] = center[1];\r\n this.camera.target[2] = center[2];\r\n this.distance = clampedDistance;\r\n this.update();\r\n }\r\n }\r\n\r\n /**\r\n * 动画过渡到目标帧(包含目标点和距离)\r\n */\r\n private animateToFrame(\r\n targetCenter: [number, number, number],\r\n targetDistance: number,\r\n ): void {\r\n const startTarget = [\r\n this.camera.target[0],\r\n this.camera.target[1],\r\n this.camera.target[2],\r\n ];\r\n const startDistance = this.distance;\r\n const duration = 400; // 毫秒\r\n const startTime = performance.now();\r\n\r\n const animate = (currentTime: number) => {\r\n const elapsed = currentTime - startTime;\r\n const progress = Math.min(elapsed / duration, 1);\r\n\r\n // 使用 ease-out 缓动\r\n const eased = 1 - Math.pow(1 - progress, 3);\r\n\r\n // 插值目标点\r\n this.camera.target[0] =\r\n startTarget[0] + (targetCenter[0] - startTarget[0]) * eased;\r\n this.camera.target[1] =\r\n startTarget[1] + (targetCenter[1] - startTarget[1]) * eased;\r\n this.camera.target[2] =\r\n startTarget[2] + (targetCenter[2] - startTarget[2]) * eased;\r\n\r\n // 插值距离\r\n this.distance = startDistance + (targetDistance - startDistance) * eased;\r\n\r\n this.update();\r\n\r\n if (progress < 1) {\r\n requestAnimationFrame(animate);\r\n }\r\n };\r\n\r\n requestAnimationFrame(animate);\r\n }\r\n}\r\n","import { Camera } from \"./Camera\";\r\nimport { Renderer } from \"./Renderer\";\r\n\r\n/**\r\n * Gizmo 轴配置\r\n */\r\ninterface AxisConfig {\r\n direction: [number, number, number];\r\n color: [number, number, number];\r\n label: string;\r\n}\r\n\r\n/**\r\n * WGSL Shader - Gizmo 渲染\r\n */\r\nconst gizmoShaderCode = /* wgsl */ `\r\nstruct Uniforms {\r\n viewMatrix: mat4x4<f32>,\r\n projMatrix: mat4x4<f32>,\r\n}\r\n\r\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\r\n\r\nstruct VertexInput {\r\n @location(0) position: vec3<f32>,\r\n @location(1) color: vec3<f32>,\r\n}\r\n\r\nstruct VertexOutput {\r\n @builtin(position) position: vec4<f32>,\r\n @location(0) color: vec3<f32>,\r\n}\r\n\r\n@vertex\r\nfn vs_main(input: VertexInput) -> VertexOutput {\r\n var output: VertexOutput;\r\n let worldPos = vec4<f32>(input.position, 1.0);\r\n output.position = uniforms.projMatrix * uniforms.viewMatrix * worldPos;\r\n output.color = input.color;\r\n return output;\r\n}\r\n\r\n@fragment\r\nfn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {\r\n return vec4<f32>(input.color, 1.0);\r\n}\r\n`;\r\n\r\n/**\r\n * ViewportGizmo - 视口坐标轴指示器\r\n * 在画布右上角显示当前相机朝向\r\n */\r\nexport class ViewportGizmo {\r\n private renderer: Renderer;\r\n private camera: Camera;\r\n private canvas: HTMLCanvasElement;\r\n\r\n // 渲染资源\r\n private pipeline!: GPURenderPipeline;\r\n private uniformBuffer!: GPUBuffer;\r\n private bindGroup!: GPUBindGroup;\r\n private vertexBuffer!: GPUBuffer;\r\n private indexBuffer!: GPUBuffer;\r\n private vertexCount: number = 0;\r\n private indexCount: number = 0;\r\n\r\n // Gizmo 配置\r\n private size: number = 200; // Gizmo 尺寸(像素)\r\n private margin: number = 20; // 边距\r\n\r\n // Gizmo 投影矩阵\r\n private projMatrix: Float32Array = new Float32Array(16);\r\n private viewMatrix: Float32Array = new Float32Array(16);\r\n\r\n // 轴配置\r\n private axes: AxisConfig[] = [\r\n { direction: [1, 0, 0], color: [0.9, 0.2, 0.2], label: \"X\" }, // 红色 X\r\n { direction: [0, 1, 0], color: [0.2, 0.9, 0.2], label: \"Y\" }, // 绿色 Y\r\n { direction: [0, 0, 1], color: [0.2, 0.4, 0.9], label: \"Z\" }, // 蓝色 Z\r\n ];\r\n\r\n // 交互回调\r\n private onAxisClick?: (axis: string, positive: boolean) => void;\r\n\r\n constructor(renderer: Renderer, camera: Camera, canvas: HTMLCanvasElement) {\r\n this.renderer = renderer;\r\n this.camera = camera;\r\n this.canvas = canvas;\r\n\r\n this.createPipeline();\r\n this.createGeometry();\r\n this.createUniformBuffer();\r\n this.setupOrthoProjection();\r\n }\r\n\r\n /**\r\n * 设置轴点击回调\r\n */\r\n setOnAxisClick(callback: (axis: string, positive: boolean) => void): void {\r\n this.onAxisClick = callback;\r\n }\r\n\r\n /**\r\n * 创建渲染管线\r\n */\r\n private createPipeline(): void {\r\n const device = this.renderer.device;\r\n\r\n const shaderModule = device.createShaderModule({\r\n code: gizmoShaderCode,\r\n });\r\n\r\n const bindGroupLayout = device.createBindGroupLayout({\r\n entries: [\r\n {\r\n binding: 0,\r\n visibility: GPUShaderStage.VERTEX,\r\n buffer: { type: \"uniform\" },\r\n },\r\n ],\r\n });\r\n\r\n const pipelineLayout = device.createPipelineLayout({\r\n bindGroupLayouts: [bindGroupLayout],\r\n });\r\n\r\n // 顶点布局: position(3) + color(3)\r\n const vertexBufferLayout: GPUVertexBufferLayout = {\r\n arrayStride: 24,\r\n attributes: [\r\n { shaderLocation: 0, offset: 0, format: \"float32x3\" },\r\n { shaderLocation: 1, offset: 12, format: \"float32x3\" },\r\n ],\r\n };\r\n\r\n this.pipeline = device.createRenderPipeline({\r\n layout: pipelineLayout,\r\n vertex: {\r\n module: shaderModule,\r\n entryPoint: \"vs_main\",\r\n buffers: [vertexBufferLayout],\r\n },\r\n fragment: {\r\n module: shaderModule,\r\n entryPoint: \"fs_main\",\r\n targets: [{ format: this.renderer.format }],\r\n },\r\n primitive: {\r\n topology: \"triangle-list\",\r\n cullMode: \"none\",\r\n },\r\n depthStencil: {\r\n format: this.renderer.depthFormat,\r\n depthWriteEnabled: true,\r\n depthCompare: \"less\",\r\n },\r\n });\r\n }\r\n\r\n /**\r\n * 创建 Gizmo 几何体(三个轴 + 箭头)\r\n */\r\n private createGeometry(): void {\r\n const vertices: number[] = [];\r\n const indices: number[] = [];\r\n let vertexOffset = 0;\r\n\r\n const axisLength = 0.8;\r\n const axisRadius = 0.04;\r\n const coneLength = 0.25;\r\n const coneRadius = 0.1;\r\n const segments = 12;\r\n\r\n for (const axis of this.axes) {\r\n const [dx, dy, dz] = axis.direction;\r\n const [r, g, b] = axis.color;\r\n\r\n // 创建轴的圆柱体\r\n const cylResult = this.createCylinder(\r\n [0, 0, 0],\r\n [dx * axisLength, dy * axisLength, dz * axisLength],\r\n axisRadius,\r\n segments,\r\n [r, g, b],\r\n vertexOffset,\r\n );\r\n vertices.push(...cylResult.vertices);\r\n indices.push(...cylResult.indices);\r\n vertexOffset += cylResult.vertexCount;\r\n\r\n // 创建箭头圆锥\r\n const coneStart: [number, number, number] = [\r\n dx * axisLength,\r\n dy * axisLength,\r\n dz * axisLength,\r\n ];\r\n const coneEnd: [number, number, number] = [\r\n dx * (axisLength + coneLength),\r\n dy * (axisLength + coneLength),\r\n dz * (axisLength + coneLength),\r\n ];\r\n const coneResult = this.createCone(\r\n coneStart,\r\n coneEnd,\r\n coneRadius,\r\n segments,\r\n [r, g, b],\r\n vertexOffset,\r\n );\r\n vertices.push(...coneResult.vertices);\r\n indices.push(...coneResult.indices);\r\n vertexOffset += coneResult.vertexCount;\r\n\r\n // 创建负方向的小球\r\n const sphereResult = this.createSphere(\r\n [-dx * 0.15, -dy * 0.15, -dz * 0.15],\r\n 0.08,\r\n 8,\r\n [r * 0.6, g * 0.6, b * 0.6],\r\n vertexOffset,\r\n );\r\n vertices.push(...sphereResult.vertices);\r\n indices.push(...sphereResult.indices);\r\n vertexOffset += sphereResult.vertexCount;\r\n }\r\n\r\n // 创建中心球\r\n const centerResult = this.createSphere(\r\n [0, 0, 0],\r\n 0.1,\r\n 12,\r\n [0.5, 0.5, 0.5],\r\n vertexOffset,\r\n );\r\n vertices.push(...centerResult.vertices);\r\n indices.push(...centerResult.indices);\r\n\r\n this.vertexCount = vertices.length / 6;\r\n this.indexCount = indices.length;\r\n\r\n const vertexData = new Float32Array(vertices);\r\n const indexData = new Uint16Array(indices);\r\n\r\n const device = this.renderer.device;\r\n\r\n this.vertexBuffer = device.createBuffer({\r\n size: vertexData.byteLength,\r\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\r\n });\r\n device.queue.writeBuffer(this.vertexBuffer, 0, vertexData);\r\n\r\n this.indexBuffer = device.createBuffer({\r\n size: indexData.byteLength,\r\n usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,\r\n });\r\n device.queue.writeBuffer(this.indexBuffer, 0, indexData);\r\n }\r\n\r\n /**\r\n * 创建圆柱体几何\r\n */\r\n private createCylinder(\r\n start: [number, number, number],\r\n end: [number, number, number],\r\n radius: number,\r\n segments: number,\r\n color: [number, number, number],\r\n indexOffset: number,\r\n ): { vertices: number[]; indices: number[]; vertexCount: number } {\r\n const vertices: number[] = [];\r\n const indices: number[] = [];\r\n\r\n // 计算方向和长度\r\n const dx = end[0] - start[0];\r\n const dy = end[1] - start[1];\r\n const dz = end[2] - start[2];\r\n const length = Math.sqrt(dx * dx + dy * dy + dz * dz);\r\n\r\n // 计算旋转矩阵\r\n const dir = [dx / length, dy / length, dz / length];\r\n const up = Math.abs(dir[1]) < 0.99 ? [0, 1, 0] : [1, 0, 0];\r\n const right = this.cross(\r\n up as [number, number, number],\r\n dir as [number, number, number],\r\n );\r\n this.normalize(right);\r\n const actualUp = this.cross(dir as [number, number, number], right);\r\n\r\n // 生成圆柱顶点\r\n for (let i = 0; i <= segments; i++) {\r\n const angle = (i / segments) * Math.PI * 2;\r\n const cos = Math.cos(angle);\r\n const sin = Math.sin(angle);\r\n\r\n // 底面\r\n const nx0 = right[0] * cos + actualUp[0] * sin;\r\n const ny0 = right[1] * cos + actualUp[1] * sin;\r\n const nz0 = right[2] * cos + actualUp[2] * sin;\r\n vertices.push(\r\n start[0] + nx0 * radius,\r\n start[1] + ny0 * radius,\r\n start[2] + nz0 * radius,\r\n color[0],\r\n color[1],\r\n color[2],\r\n );\r\n\r\n // 顶面\r\n vertices.push(\r\n end[0] + nx0 * radius,\r\n end[1] + ny0 * radius,\r\n end[2] + nz0 * radius,\r\n color[0],\r\n color[1],\r\n color[2],\r\n );\r\n }\r\n\r\n // 生成索引\r\n for (let i = 0; i < segments; i++) {\r\n const i0 = indexOffset + i * 2;\r\n const i1 = indexOffset + i * 2 + 1;\r\n const i2 = indexOffset + (i + 1) * 2;\r\n const i3 = indexOffset + (i + 1) * 2 + 1;\r\n indices.push(i0, i1, i2, i2, i1, i3);\r\n }\r\n\r\n return { vertices, indices, vertexCount: (segments + 1) * 2 };\r\n }\r\n\r\n /**\r\n * 创建圆锥几何\r\n */\r\n private createCone(\r\n base: [number, number, number],\r\n tip: [number, number, number],\r\n radius: number,\r\n segments: number,\r\n color: [number, number, number],\r\n indexOffset: number,\r\n ): { vertices: number[]; indices: number[]; vertexCount: number } {\r\n const vertices: number[] = [];\r\n const indices: number[] = [];\r\n\r\n const dx = tip[0] - base[0];\r\n const dy = tip[1] - base[1];\r\n const dz = tip[2] - base[2];\r\n const length = Math.sqrt(dx * dx + dy * dy + dz * dz);\r\n const dir = [dx / length, dy / length, dz / length];\r\n\r\n const up = Math.abs(dir[1]) < 0.99 ? [0, 1, 0] : [1, 0, 0];\r\n const right = this.cross(\r\n up as [number, number, number],\r\n dir as [number, number, number],\r\n );\r\n this.normalize(right);\r\n const actualUp = this.cross(dir as [number, number, number], right);\r\n\r\n // 尖端\r\n vertices.push(tip[0], tip[1], tip[2], color[0], color[1], color[2]);\r\n\r\n // 底面圆环\r\n for (let i = 0; i <= segments; i++) {\r\n const angle = (i / segments) * Math.PI * 2;\r\n const cos = Math.cos(angle);\r\n const sin = Math.sin(angle);\r\n const nx = right[0] * cos + actualUp[0] * sin;\r\n const ny = right[1] * cos + actualUp[1] * sin;\r\n const nz = right[2] * cos + actualUp[2] * sin;\r\n vertices.push(\r\n base[0] + nx * radius,\r\n base[1] + ny * radius,\r\n base[2] + nz * radius,\r\n color[0],\r\n color[1],\r\n color[2],\r\n );\r\n }\r\n\r\n // 索引(侧面)\r\n for (let i = 0; i < segments; i++) {\r\n indices.push(indexOffset, indexOffset + i + 1, indexOffset + i + 2);\r\n }\r\n\r\n // 底面中心\r\n const baseCenterIdx = indexOffset + segments + 2;\r\n vertices.push(\r\n base[0],\r\n base[1],\r\n base[2],\r\n color[0] * 0.7,\r\n color[1] * 0.7,\r\n color[2] * 0.7,\r\n );\r\n\r\n // 底面索引\r\n for (let i = 0; i < segments; i++) {\r\n indices.push(baseCenterIdx, indexOffset + i + 2, indexOffset + i + 1);\r\n }\r\n\r\n return { vertices, indices, vertexCount: segments + 3 };\r\n }\r\n\r\n /**\r\n * 创建球体几何\r\n */\r\n private createSphere(\r\n center: [number, number, number],\r\n radius: number,\r\n segments: number,\r\n color: [number, number, number],\r\n indexOffset: number,\r\n ): { vertices: number[]; indices: number[]; vertexCount: number } {\r\n const vertices: number[] = [];\r\n const indices: number[] = [];\r\n const rings = segments / 2;\r\n\r\n for (let ring = 0; ring <= rings; ring++) {\r\n const phi = (ring / rings) * Math.PI;\r\n const sinPhi = Math.sin(phi);\r\n const cosPhi = Math.cos(phi);\r\n\r\n for (let seg = 0; seg <= segments; seg++) {\r\n const theta = (seg / segments) * Math.PI * 2;\r\n const x = center[0] + radius * sinPhi * Math.cos(theta);\r\n const y = center[1] + radius * cosPhi;\r\n const z = center[2] + radius * sinPhi * Math.sin(theta);\r\n vertices.push(x, y, z, color[0], color[1], color[2]);\r\n }\r\n }\r\n\r\n for (let ring = 0; ring < rings; ring++) {\r\n for (let seg = 0; seg < segments; seg++) {\r\n const current = indexOffset + ring * (segments + 1) + seg;\r\n const next = current + segments + 1;\r\n indices.push(current, next, current + 1);\r\n indices.push(current + 1, next, next + 1);\r\n }\r\n }\r\n\r\n return { vertices, indices, vertexCount: (rings + 1) * (segments + 1) };\r\n }\r\n\r\n /**\r\n * 创建 uniform buffer\r\n */\r\n private createUniformBuffer(): void {\r\n const device = this.renderer.device;\r\n\r\n // viewMatrix(64) + projMatrix(64) = 128 bytes\r\n this.uniformBuffer = device.createBuffer({\r\n size: 128,\r\n usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\r\n });\r\n\r\n const bindGroupLayout = this.pipeline.getBindGroupLayout(0);\r\n this.bindGroup = device.createBindGroup({\r\n layout: bindGroupLayout,\r\n entries: [{ binding: 0, resource: { buffer: this.uniformBuffer } }],\r\n });\r\n }\r\n\r\n /**\r\n * 设置正交投影矩阵\r\n */\r\n private setupOrthoProjection(): void {\r\n const s = 1.5; // 场景大小\r\n // 正交投影矩阵\r\n this.projMatrix[0] = 1 / s;\r\n this.projMatrix[5] = 1 / s;\r\n this.projMatrix[10] = -1 / 10;\r\n this.projMatrix[14] = 0;\r\n this.projMatrix[15] = 1;\r\n }\r\n\r\n /**\r\n * 更新 Gizmo 视图矩阵(从相机提取旋转部分)\r\n */\r\n private updateViewMatrix(): void {\r\n // 从相机视图矩阵提取旋转部分(去除平移)\r\n const camView = this.camera.viewMatrix;\r\n\r\n // 复制旋转部分\r\n this.viewMatrix[0] = camView[0];\r\n this.viewMatrix[1] = camView[1];\r\n this.viewMatrix[2] = camView[2];\r\n this.viewMatrix[3] = 0;\r\n\r\n this.viewMatrix[4] = camView[4];\r\n this.viewMatrix[5] = camView[5];\r\n this.viewMatrix[6] = camView[6];\r\n this.viewMatrix[7] = 0;\r\n\r\n this.viewMatrix[8] = camView[8];\r\n this.viewMatrix[9] = camView[9];\r\n this.viewMatrix[10] = camView[10];\r\n this.viewMatrix[11] = 0;\r\n\r\n // 设置固定的观察距离\r\n this.viewMatrix[12] = 0;\r\n this.viewMatrix[13] = 0;\r\n this.viewMatrix[14] = -3;\r\n this.viewMatrix[15] = 1;\r\n }\r\n\r\n /**\r\n * 渲染 Gizmo\r\n */\r\n render(pass: GPURenderPassEncoder): void {\r\n // 更新视图矩阵\r\n this.updateViewMatrix();\r\n\r\n // 计算 viewport 位置(右上角)\r\n const dpr = window.devicePixelRatio || 1;\r\n let gizmoSize = Math.floor(this.size * dpr);\r\n const marginX = Math.floor(this.margin * dpr);\r\n const marginY = Math.floor(this.margin * dpr);\r\n\r\n // 确保 Gizmo 不会超出 canvas 范围\r\n const maxSize = Math.min(\r\n this.canvas.width - marginX * 2,\r\n this.canvas.height - marginY * 2,\r\n );\r\n if (maxSize < 50) {\r\n // canvas 太小,跳过渲染\r\n return;\r\n }\r\n gizmoSize = Math.min(gizmoSize, maxSize);\r\n\r\n const x = Math.max(0, this.canvas.width - gizmoSize - marginX);\r\n const y = marginY;\r\n\r\n // 设置 viewport\r\n pass.setViewport(x, y, gizmoSize, gizmoSize, 0, 1);\r\n pass.setScissorRect(x, y, gizmoSize, gizmoSize);\r\n\r\n // 更新 uniform\r\n this.renderer.device.queue.writeBuffer(\r\n this.uniformBuffer,\r\n 0,\r\n new Float32Array(this.viewMatrix),\r\n );\r\n this.renderer.device.queue.writeBuffer(\r\n this.uniformBuffer,\r\n 64,\r\n new Float32Array(this.projMatrix),\r\n );\r\n\r\n // 绘制\r\n pass.setPipeline(this.pipeline);\r\n pass.setBindGroup(0, this.bindGroup);\r\n pass.setVertexBuffer(0, this.vertexBuffer);\r\n pass.setIndexBuffer(this.indexBuffer, \"uint16\");\r\n pass.drawIndexed(this.indexCount);\r\n\r\n // 恢复全屏 viewport\r\n pass.setViewport(0, 0, this.canvas.width, this.canvas.height, 0, 1);\r\n pass.setScissorRect(0, 0, this.canvas.width, this.canvas.height);\r\n }\r\n\r\n /**\r\n * 处理点击事件,检测是否点击了某个轴\r\n */\r\n handleClick(clientX: number, clientY: number): boolean {\r\n const rect = this.canvas.getBoundingClientRect();\r\n const dpr = window.devicePixelRatio || 1;\r\n\r\n // 计算 Gizmo 区域\r\n const gizmoSize = this.size;\r\n const marginX = this.margin;\r\n const marginY = this.margin;\r\n const gizmoLeft = rect.right - gizmoSize - marginX;\r\n const gizmoTop = rect.top + marginY;\r\n const gizmoRight = gizmoLeft + gizmoSize;\r\n const gizmoBottom = gizmoTop + gizmoSize;\r\n\r\n // 检查是否在 Gizmo 区域内\r\n if (\r\n clientX < gizmoLeft ||\r\n clientX > gizmoRight ||\r\n clientY < gizmoTop ||\r\n clientY > gizmoBottom\r\n ) {\r\n return false;\r\n }\r\n\r\n // 计算在 Gizmo 中的相对位置(-1 到 1)\r\n const relX = ((clientX - gizmoLeft) / gizmoSize) * 2 - 1;\r\n const relY = -(((clientY - gizmoTop) / gizmoSize) * 2 - 1);\r\n\r\n // 检测点击的轴\r\n const clickedAxis = this.detectClickedAxis(relX, relY);\r\n if (clickedAxis && this.onAxisClick) {\r\n this.onAxisClick(clickedAxis.axis, clickedAxis.positive);\r\n return true;\r\n }\r\n\r\n return false;\r\n }\r\n\r\n /**\r\n * 检测点击了哪个轴\r\n */\r\n private detectClickedAxis(\r\n relX: number,\r\n relY: number,\r\n ): { axis: string; positive: boolean } | null {\r\n // 将屏幕坐标转换为 Gizmo 空间\r\n // 使用视图矩阵的逆来判断\r\n const threshold = 0.4;\r\n\r\n // 计算各轴在屏幕上的投影位置\r\n for (const axis of this.axes) {\r\n const [dx, dy, dz] = axis.direction;\r\n\r\n // 正向轴端点\r\n const posX =\r\n this.viewMatrix[0] * dx +\r\n this.viewMatrix[4] * dy +\r\n this.viewMatrix[8] * dz;\r\n const posY =\r\n this.viewMatrix[1] * dx +\r\n this.viewMatrix[5] * dy +\r\n this.viewMatrix[9] * dz;\r\n\r\n // 检查正向\r\n const distPos = Math.sqrt(\r\n (relX - posX * 0.5) ** 2 + (relY - posY * 0.5) ** 2,\r\n );\r\n if (distPos < threshold) {\r\n return { axis: axis.label, positive: true };\r\n }\r\n\r\n // 检查负向\r\n const distNeg = Math.sqrt(\r\n (relX + posX * 0.15) ** 2 + (relY + posY * 0.15) ** 2,\r\n );\r\n if (distNeg < threshold * 0.5) {\r\n return { axis: axis.label, positive: false };\r\n }\r\n }\r\n\r\n return null;\r\n }\r\n\r\n // 向量工具函数\r\n private cross(\r\n a: [number, number, number],\r\n b: [number, number, number],\r\n ): [number, number, number] {\r\n return [\r\n a[1] * b[2] - a[2] * b[1],\r\n a[2] * b[0] - a[0] * b[2],\r\n a[0] * b[1] - a[1] * b[0],\r\n ];\r\n }\r\n\r\n private normalize(v: [number, number, number]): void {\r\n const len = Math.sqrt(v[0] ** 2 + v[1] ** 2 + v[2] ** 2);\r\n if (len > 0) {\r\n v[0] /= len;\r\n v[1] /= len;\r\n v[2] /= len;\r\n }\r\n }\r\n\r\n /**\r\n * 设置 Gizmo 大小\r\n */\r\n setSize(size: number): void {\r\n this.size = size;\r\n }\r\n\r\n /**\r\n * 设置边距\r\n */\r\n setMargin(margin: number): void {\r\n this.margin = margin;\r\n }\r\n}\r\n","import { Renderer } from \"./Renderer\";\r\nimport { Camera } from \"./Camera\";\r\n\r\n/**\r\n * BoundingBox 数据结构\r\n */\r\nexport interface BoundingBox {\r\n min: [number, number, number];\r\n max: [number, number, number];\r\n}\r\n\r\n/**\r\n * BoundingBoxProvider - 包围盒数据提供者接口\r\n * 实现此接口的对象可以动态提供包围盒数据\r\n */\r\nexport interface BoundingBoxProvider {\r\n getBoundingBox(): BoundingBox | null;\r\n}\r\n\r\n/**\r\n * BoundingBoxRenderer - 包围盒线框渲染器\r\n * 用于显示选中对象的包围盒,支持动态跟随\r\n */\r\nexport class BoundingBoxRenderer {\r\n private renderer: Renderer;\r\n private camera: Camera;\r\n \r\n // GPU 资源\r\n private pipeline: GPURenderPipeline | null = null;\r\n private uniformBuffer: GPUBuffer | null = null;\r\n private bindGroup: GPUBindGroup | null = null;\r\n private vertexBuffer: GPUBuffer | null = null;\r\n \r\n // 包围盒数据提供者(动态模式)\r\n private provider: BoundingBoxProvider | null = null;\r\n \r\n // 静态包围盒(备用)\r\n private staticBoundingBox: BoundingBox | null = null;\r\n \r\n // 线条颜色 (白色)\r\n private lineColor: [number, number, number] = [1.0, 1.0, 1.0];\r\n \r\n // 角落线段长度比例 (相对于边长)\r\n private cornerRatio: number = 0.2;\r\n \r\n constructor(renderer: Renderer, camera: Camera) {\r\n this.renderer = renderer;\r\n this.camera = camera;\r\n this.createPipeline();\r\n this.createVertexBuffer();\r\n }\r\n \r\n /**\r\n * 创建渲染管线\r\n */\r\n private createPipeline(): void {\r\n const device = this.renderer.device;\r\n \r\n const shaderCode = `\r\n struct Uniforms {\r\n viewProjection: mat4x4<f32>,\r\n }\r\n\r\n @group(0) @binding(0) var<uniform> uniforms: Uniforms;\r\n\r\n struct VertexInput {\r\n @location(0) position: vec3<f32>,\r\n @location(1) color: vec3<f32>,\r\n }\r\n\r\n struct VertexOutput {\r\n @builtin(position) position: vec4<f32>,\r\n @location(0) color: vec3<f32>,\r\n }\r\n\r\n @vertex\r\n fn vertexMain(input: VertexInput) -> VertexOutput {\r\n var output: VertexOutput;\r\n output.position = uniforms.viewProjection * vec4<f32>(input.position, 1.0);\r\n output.color = input.color;\r\n return output;\r\n }\r\n\r\n @fragment\r\n fn fragmentMain(input: VertexOutput) -> @location(0) vec4<f32> {\r\n return vec4<f32>(input.color, 1.0);\r\n }\r\n `;\r\n \r\n const shaderModule = device.createShaderModule({ code: shaderCode });\r\n \r\n // Uniform buffer: viewProjection (64 bytes)\r\n this.uniformBuffer = device.createBuffer({\r\n size: 64,\r\n usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\r\n });\r\n \r\n const bindGroupLayout = device.createBindGroupLayout({\r\n entries: [{\r\n binding: 0,\r\n visibility: GPUShaderStage.VERTEX,\r\n buffer: { type: \"uniform\" },\r\n }],\r\n });\r\n \r\n this.bindGroup = device.createBindGroup({\r\n layout: bindGroupLayout,\r\n entries: [{ binding: 0, resource: { buffer: this.uniformBuffer } }],\r\n });\r\n \r\n const pipelineLayout = device.createPipelineLayout({\r\n bindGroupLayouts: [bindGroupLayout],\r\n });\r\n \r\n this.pipeline = device.createRenderPipeline({\r\n layout: pipelineLayout,\r\n vertex: {\r\n module: shaderModule,\r\n entryPoint: \"vertexMain\",\r\n buffers: [{\r\n arrayStride: 24, // 6 floats * 4 bytes\r\n attributes: [\r\n { shaderLocation: 0, offset: 0, format: \"float32x3\" },\r\n { shaderLocation: 1, offset: 12, format: \"float32x3\" },\r\n ],\r\n }],\r\n },\r\n fragment: {\r\n module: shaderModule,\r\n entryPoint: \"fragmentMain\",\r\n targets: [{\r\n format: this.renderer.format,\r\n }],\r\n },\r\n primitive: {\r\n topology: \"line-list\",\r\n cullMode: \"none\",\r\n },\r\n depthStencil: {\r\n format: this.renderer.depthFormat,\r\n depthWriteEnabled: false,\r\n depthCompare: \"always\", // 始终可见\r\n },\r\n });\r\n }\r\n \r\n /**\r\n * 创建顶点缓冲区(预分配)\r\n */\r\n private createVertexBuffer(): void {\r\n const device = this.renderer.device;\r\n // 48 个顶点 * 6 floats * 4 bytes = 1152 bytes\r\n this.vertexBuffer = device.createBuffer({\r\n size: 1152,\r\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\r\n });\r\n }\r\n \r\n /**\r\n * 设置包围盒数据提供者(动态模式)\r\n * 每帧会从 provider 获取最新的包围盒数据\r\n */\r\n setProvider(provider: BoundingBoxProvider | null): void {\r\n this.provider = provider;\r\n this.staticBoundingBox = null;\r\n }\r\n \r\n /**\r\n * 设置静态包围盒(不会自动更新)\r\n */\r\n setBoundingBox(box: BoundingBox | null): void {\r\n this.staticBoundingBox = box;\r\n this.provider = null;\r\n }\r\n \r\n /**\r\n * 清除包围盒\r\n */\r\n clear(): void {\r\n this.provider = null;\r\n this.staticBoundingBox = null;\r\n }\r\n \r\n /**\r\n * 设置线条颜色\r\n */\r\n setLineColor(r: number, g: number, b: number): void {\r\n this.lineColor = [r, g, b];\r\n }\r\n \r\n /**\r\n * 生成顶点数据\r\n */\r\n private generateVertices(box: BoundingBox): Float32Array {\r\n const { min, max } = box;\r\n const [r, g, b] = this.lineColor;\r\n \r\n // 计算各边长度\r\n const dx = max[0] - min[0];\r\n const dy = max[1] - min[1];\r\n const dz = max[2] - min[2];\r\n \r\n // 角落线段长度\r\n const lx = dx * this.cornerRatio;\r\n const ly = dy * this.cornerRatio;\r\n const lz = dz * this.cornerRatio;\r\n \r\n // 8 个角落,每个角落 3 条线段,每条线段 2 个顶点\r\n // 总共 8 * 3 * 2 = 48 个顶点\r\n const vertices: number[] = [];\r\n \r\n // 辅助函数:添加线段\r\n const addLine = (x1: number, y1: number, z1: number, x2: number, y2: number, z2: number) => {\r\n vertices.push(x1, y1, z1, r, g, b);\r\n vertices.push(x2, y2, z2, r, g, b);\r\n };\r\n \r\n // 角落 0: min, min, min\r\n addLine(min[0], min[1], min[2], min[0] + lx, min[1], min[2]);\r\n addLine(min[0], min[1], min[2], min[0], min[1] + ly, min[2]);\r\n addLine(min[0], min[1], min[2], min[0], min[1], min[2] + lz);\r\n \r\n // 角落 1: max, min, min\r\n addLine(max[0], min[1], min[2], max[0] - lx, min[1], min[2]);\r\n addLine(max[0], min[1], min[2], max[0], min[1] + ly, min[2]);\r\n addLine(max[0], min[1], min[2], max[0], min[1], min[2] + lz);\r\n \r\n // 角落 2: min, max, min\r\n addLine(min[0], max[1], min[2], min[0] + lx, max[1], min[2]);\r\n addLine(min[0], max[1], min[2], min[0], max[1] - ly, min[2]);\r\n addLine(min[0], max[1], min[2], min[0], max[1], min[2] + lz);\r\n \r\n // 角落 3: max, max, min\r\n addLine(max[0], max[1], min[2], max[0] - lx, max[1], min[2]);\r\n addLine(max[0], max[1], min[2], max[0], max[1] - ly, min[2]);\r\n addLine(max[0], max[1], min[2], max[0], max[1], min[2] + lz);\r\n \r\n // 角落 4: min, min, max\r\n addLine(min[0], min[1], max[2], min[0] + lx, min[1], max[2]);\r\n addLine(min[0], min[1], max[2], min[0], min[1] + ly, max[2]);\r\n addLine(min[0], min[1], max[2], min[0], min[1], max[2] - lz);\r\n \r\n // 角落 5: max, min, max\r\n addLine(max[0], min[1], max[2], max[0] - lx, min[1], max[2]);\r\n addLine(max[0], min[1], max[2], max[0], min[1] + ly, max[2]);\r\n addLine(max[0], min[1], max[2], max[0], min[1], max[2] - lz);\r\n \r\n // 角落 6: min, max, max\r\n addLine(min[0], max[1], max[2], min[0] + lx, max[1], max[2]);\r\n addLine(min[0], max[1], max[2], min[0], max[1] - ly, max[2]);\r\n addLine(min[0], max[1], max[2], min[0], max[1], max[2] - lz);\r\n \r\n // 角落 7: max, max, max\r\n addLine(max[0], max[1], max[2], max[0] - lx, max[1], max[2]);\r\n addLine(max[0], max[1], max[2], max[0], max[1] - ly, max[2]);\r\n addLine(max[0], max[1], max[2], max[0], max[1], max[2] - lz);\r\n \r\n return new Float32Array(vertices);\r\n }\r\n \r\n /**\r\n * 渲染包围盒\r\n */\r\n render(pass: GPURenderPassEncoder): void {\r\n if (!this.pipeline || !this.bindGroup || !this.vertexBuffer || !this.uniformBuffer) {\r\n return;\r\n }\r\n \r\n // 获取当前包围盒\r\n let box: BoundingBox | null = null;\r\n \r\n if (this.provider) {\r\n box = this.provider.getBoundingBox();\r\n } else {\r\n box = this.staticBoundingBox;\r\n }\r\n \r\n if (!box) return;\r\n \r\n const device = this.renderer.device;\r\n \r\n // 每帧都更新顶点缓冲区(动态模式)\r\n const vertexData = this.generateVertices(box);\r\n device.queue.writeBuffer(this.vertexBuffer, 0, vertexData.buffer);\r\n \r\n // 更新 uniform buffer\r\n const vpMatrix = new Float32Array(this.camera.viewProjectionMatrix);\r\n device.queue.writeBuffer(this.uniformBuffer, 0, vpMatrix);\r\n \r\n // 渲染\r\n pass.setPipeline(this.pipeline);\r\n pass.setBindGroup(0, this.bindGroup);\r\n pass.setVertexBuffer(0, this.vertexBuffer);\r\n pass.draw(48); // 48 个顶点\r\n }\r\n \r\n /**\r\n * 销毁资源\r\n */\r\n destroy(): void {\r\n if (this.vertexBuffer) {\r\n this.vertexBuffer.destroy();\r\n this.vertexBuffer = null;\r\n }\r\n if (this.uniformBuffer) {\r\n this.uniformBuffer.destroy();\r\n this.uniformBuffer = null;\r\n }\r\n this.pipeline = null;\r\n this.bindGroup = null;\r\n }\r\n}\r\n","/**\r\n * Bounding Box 结构(与 GSSplatRenderer 共享接口)\r\n */\r\nexport interface MeshBoundingBox {\r\n min: [number, number, number];\r\n max: [number, number, number];\r\n center: [number, number, number];\r\n radius: number; // bounding sphere 半径\r\n}\r\n\r\n/**\r\n * Mesh - 网格数据结构\r\n * 存储 GPUBuffer + 变换属性\r\n */\r\nexport class Mesh {\r\n vertexBuffer: GPUBuffer;\r\n indexBuffer: GPUBuffer | null;\r\n vertexCount: number;\r\n indexCount: number;\r\n modelMatrix: Float32Array;\r\n\r\n // 顶点格式信息\r\n hasUV: boolean = false;\r\n indexFormat: 'uint16' | 'uint32' = 'uint16';\r\n\r\n // 变换属性(分离存储,便于 Gizmo 操作)\r\n position: Float32Array = new Float32Array([0, 0, 0]);\r\n rotation: Float32Array = new Float32Array([0, 0, 0]); // 欧拉角 (弧度)\r\n scale: Float32Array = new Float32Array([1, 1, 1]);\r\n\r\n // 本地空间的 bounding box(加载时计算,不随变换更新)\r\n private localBoundingBox: MeshBoundingBox | null = null;\r\n\r\n constructor(\r\n vertexBuffer: GPUBuffer,\r\n vertexCount: number,\r\n indexBuffer: GPUBuffer | null = null,\r\n indexCount: number = 0,\r\n boundingBox?: MeshBoundingBox\r\n ) {\r\n this.vertexBuffer = vertexBuffer;\r\n this.vertexCount = vertexCount;\r\n this.indexBuffer = indexBuffer;\r\n this.indexCount = indexCount;\r\n this.modelMatrix = new Float32Array([\r\n 1, 0, 0, 0,\r\n 0, 1, 0, 0,\r\n 0, 0, 1, 0,\r\n 0, 0, 0, 1,\r\n ]);\r\n this.localBoundingBox = boundingBox || null;\r\n }\r\n\r\n /**\r\n * 获取顶点 stride(字节数)\r\n */\r\n getVertexStride(): number {\r\n return this.hasUV ? 32 : 24;\r\n }\r\n\r\n /**\r\n * 设置本地 bounding box\r\n */\r\n setBoundingBox(bbox: MeshBoundingBox): void {\r\n this.localBoundingBox = bbox;\r\n }\r\n\r\n /**\r\n * 获取本地 bounding box\r\n */\r\n getLocalBoundingBox(): MeshBoundingBox | null {\r\n return this.localBoundingBox;\r\n }\r\n\r\n /**\r\n * 获取世界空间的 bounding box(考虑完整变换:缩放、旋转、平移)\r\n */\r\n getWorldBoundingBox(): MeshBoundingBox | null {\r\n if (!this.localBoundingBox) return null;\r\n\r\n const local = this.localBoundingBox;\r\n \r\n // 获取本地包围盒的 8 个角点\r\n const corners: [number, number, number][] = [\r\n [local.min[0], local.min[1], local.min[2]],\r\n [local.max[0], local.min[1], local.min[2]],\r\n [local.min[0], local.max[1], local.min[2]],\r\n [local.max[0], local.max[1], local.min[2]],\r\n [local.min[0], local.min[1], local.max[2]],\r\n [local.max[0], local.min[1], local.max[2]],\r\n [local.min[0], local.max[1], local.max[2]],\r\n [local.max[0], local.max[1], local.max[2]],\r\n ];\r\n \r\n // 使用 modelMatrix 变换所有角点\r\n const m = this.modelMatrix;\r\n const transformedCorners: [number, number, number][] = corners.map(([x, y, z]) => {\r\n const tx = m[0] * x + m[4] * y + m[8] * z + m[12];\r\n const ty = m[1] * x + m[5] * y + m[9] * z + m[13];\r\n const tz = m[2] * x + m[6] * y + m[10] * z + m[14];\r\n return [tx, ty, tz];\r\n });\r\n \r\n // 计算变换后的 AABB\r\n let minX = Infinity, minY = Infinity, minZ = Infinity;\r\n let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;\r\n \r\n for (const [x, y, z] of transformedCorners) {\r\n minX = Math.min(minX, x);\r\n minY = Math.min(minY, y);\r\n minZ = Math.min(minZ, z);\r\n maxX = Math.max(maxX, x);\r\n maxY = Math.max(maxY, y);\r\n maxZ = Math.max(maxZ, z);\r\n }\r\n \r\n const worldMin: [number, number, number] = [minX, minY, minZ];\r\n const worldMax: [number, number, number] = [maxX, maxY, maxZ];\r\n const worldCenter: [number, number, number] = [\r\n (minX + maxX) / 2,\r\n (minY + maxY) / 2,\r\n (minZ + maxZ) / 2,\r\n ];\r\n \r\n const dx = maxX - minX;\r\n const dy = maxY - minY;\r\n const dz = maxZ - minZ;\r\n const worldRadius = Math.sqrt(dx * dx + dy * dy + dz * dz) / 2;\r\n\r\n return { min: worldMin, max: worldMax, center: worldCenter, radius: worldRadius };\r\n }\r\n\r\n setPosition(x: number, y: number, z: number): void {\r\n this.position[0] = x;\r\n this.position[1] = y;\r\n this.position[2] = z;\r\n this.updateModelMatrix();\r\n }\r\n\r\n getPosition(): [number, number, number] {\r\n return [this.position[0], this.position[1], this.position[2]];\r\n }\r\n\r\n setRotation(rx: number, ry: number, rz: number): void {\r\n this.rotation[0] = rx;\r\n this.rotation[1] = ry;\r\n this.rotation[2] = rz;\r\n this.updateModelMatrix();\r\n }\r\n\r\n getRotation(): [number, number, number] {\r\n return [this.rotation[0], this.rotation[1], this.rotation[2]];\r\n }\r\n\r\n setScale(sx: number, sy: number, sz: number): void {\r\n this.scale[0] = sx;\r\n this.scale[1] = sy;\r\n this.scale[2] = sz;\r\n this.updateModelMatrix();\r\n }\r\n\r\n getScale(): [number, number, number] {\r\n return [this.scale[0], this.scale[1], this.scale[2]];\r\n }\r\n\r\n updateModelMatrix(): void {\r\n const [sx, sy, sz] = this.scale;\r\n const [rx, ry, rz] = this.rotation;\r\n const [tx, ty, tz] = this.position;\r\n\r\n const cx = Math.cos(rx), sx_ = Math.sin(rx);\r\n const cy = Math.cos(ry), sy_ = Math.sin(ry);\r\n const cz = Math.cos(rz), sz_ = Math.sin(rz);\r\n\r\n this.modelMatrix[0] = sx * (cy * cz);\r\n this.modelMatrix[1] = sx * (cy * sz_);\r\n this.modelMatrix[2] = sx * (-sy_);\r\n this.modelMatrix[3] = 0;\r\n\r\n this.modelMatrix[4] = sy * (sx_ * sy_ * cz - cx * sz_);\r\n this.modelMatrix[5] = sy * (sx_ * sy_ * sz_ + cx * cz);\r\n this.modelMatrix[6] = sy * (sx_ * cy);\r\n this.modelMatrix[7] = 0;\r\n\r\n this.modelMatrix[8] = sz * (cx * sy_ * cz + sx_ * sz_);\r\n this.modelMatrix[9] = sz * (cx * sy_ * sz_ - sx_ * cz);\r\n this.modelMatrix[10] = sz * (cx * cy);\r\n this.modelMatrix[11] = 0;\r\n\r\n this.modelMatrix[12] = tx;\r\n this.modelMatrix[13] = ty;\r\n this.modelMatrix[14] = tz;\r\n this.modelMatrix[15] = 1;\r\n }\r\n\r\n resetTransform(): void {\r\n this.position.set([0, 0, 0]);\r\n this.rotation.set([0, 0, 0]);\r\n this.scale.set([1, 1, 1]);\r\n this.updateModelMatrix();\r\n }\r\n\r\n destroy(): void {\r\n this.vertexBuffer.destroy();\r\n if (this.indexBuffer) {\r\n this.indexBuffer.destroy();\r\n }\r\n }\r\n}\r\n","import { Renderer } from \"../core/Renderer\";\r\nimport { Camera } from \"../core/Camera\";\r\nimport { Mesh, MeshBoundingBox } from \"./Mesh\";\r\nimport { MaterialData } from \"../loaders/GLBLoader\";\r\n\r\n/**\r\n * 带纹理的 Shader\r\n */\r\nconst shaderCodeTextured = /* wgsl */ `\r\nstruct Uniforms {\r\n viewProjection: mat4x4<f32>,\r\n model: mat4x4<f32>,\r\n baseColorFactor: vec4<f32>,\r\n lightDir: vec3<f32>,\r\n ambientIntensity: f32,\r\n}\r\n\r\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\r\n@group(0) @binding(1) var texSampler: sampler;\r\n@group(0) @binding(2) var baseColorTexture: texture_2d<f32>;\r\n\r\nstruct VertexInput {\r\n @location(0) position: vec3<f32>,\r\n @location(1) normal: vec3<f32>,\r\n @location(2) uv: vec2<f32>,\r\n}\r\n\r\nstruct VertexOutput {\r\n @builtin(position) position: vec4<f32>,\r\n @location(0) normal: vec3<f32>,\r\n @location(1) uv: vec2<f32>,\r\n}\r\n\r\n@vertex\r\nfn vs_main(input: VertexInput) -> VertexOutput {\r\n var output: VertexOutput;\r\n let worldPos = uniforms.model * vec4<f32>(input.position, 1.0);\r\n output.position = uniforms.viewProjection * worldPos;\r\n output.normal = normalize((uniforms.model * vec4<f32>(input.normal, 0.0)).xyz);\r\n output.uv = input.uv;\r\n return output;\r\n}\r\n\r\n@fragment\r\nfn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {\r\n let texColor = textureSample(baseColorTexture, texSampler, input.uv);\r\n let baseColor = texColor * uniforms.baseColorFactor;\r\n \r\n // Lambert 光照 + 环境光\r\n let normal = normalize(input.normal);\r\n let NdotL = max(dot(normal, uniforms.lightDir), 0.0);\r\n let diffuse = NdotL * (1.0 - uniforms.ambientIntensity);\r\n let lighting = uniforms.ambientIntensity + diffuse;\r\n \r\n return vec4<f32>(baseColor.rgb * lighting, baseColor.a);\r\n}\r\n`;\r\n\r\n/**\r\n * 无纹理的 Shader(使用 baseColorFactor)\r\n */\r\nconst shaderCodeUntextured = /* wgsl */ `\r\nstruct Uniforms {\r\n viewProjection: mat4x4<f32>,\r\n model: mat4x4<f32>,\r\n baseColorFactor: vec4<f32>,\r\n lightDir: vec3<f32>,\r\n ambientIntensity: f32,\r\n}\r\n\r\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\r\n\r\nstruct VertexInput {\r\n @location(0) position: vec3<f32>,\r\n @location(1) normal: vec3<f32>,\r\n}\r\n\r\nstruct VertexOutput {\r\n @builtin(position) position: vec4<f32>,\r\n @location(0) normal: vec3<f32>,\r\n}\r\n\r\n@vertex\r\nfn vs_main(input: VertexInput) -> VertexOutput {\r\n var output: VertexOutput;\r\n let worldPos = uniforms.model * vec4<f32>(input.position, 1.0);\r\n output.position = uniforms.viewProjection * worldPos;\r\n output.normal = normalize((uniforms.model * vec4<f32>(input.normal, 0.0)).xyz);\r\n return output;\r\n}\r\n\r\n@fragment\r\nfn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {\r\n let normal = normalize(input.normal);\r\n let NdotL = max(dot(normal, uniforms.lightDir), 0.0);\r\n let diffuse = NdotL * (1.0 - uniforms.ambientIntensity);\r\n let lighting = uniforms.ambientIntensity + diffuse;\r\n \r\n return vec4<f32>(uniforms.baseColorFactor.rgb * lighting, uniforms.baseColorFactor.a);\r\n}\r\n`;\r\n\r\n// Uniform buffer 大小: viewProjection(64) + model(64) + baseColorFactor(16) + lightDir(12) + ambientIntensity(4) = 160 bytes\r\nconst UNIFORM_BUFFER_SIZE = 160;\r\n\r\n/**\r\n * 渲染项(Mesh + Material + 独立的 uniform buffer)\r\n */\r\ninterface RenderItem {\r\n mesh: Mesh;\r\n material: MaterialData;\r\n uniformBuffer: GPUBuffer;\r\n bindGroup: GPUBindGroup;\r\n}\r\n\r\n/**\r\n * MeshRenderer - 网格渲染器\r\n * 支持纹理和材质,每个 mesh 有独立的 uniform buffer\r\n */\r\nexport class MeshRenderer {\r\n private renderer: Renderer;\r\n private camera: Camera;\r\n private items: RenderItem[] = [];\r\n\r\n // 有纹理的管线\r\n private pipelineTextured!: GPURenderPipeline;\r\n private pipelineTexturedDoubleSided!: GPURenderPipeline;\r\n private bindGroupLayoutTextured!: GPUBindGroupLayout;\r\n\r\n // 无纹理的管线\r\n private pipelineUntextured!: GPURenderPipeline;\r\n private pipelineUntexturedDoubleSided!: GPURenderPipeline;\r\n private bindGroupLayoutUntextured!: GPUBindGroupLayout;\r\n\r\n private sampler!: GPUSampler;\r\n private defaultTexture!: GPUTexture;\r\n\r\n // 光照方向\r\n private lightDir: Float32Array = new Float32Array([0.5, 0.7, 0.5]);\r\n // 环境光强度 (0-1)\r\n private ambientIntensity: number = 0.6;\r\n\r\n constructor(renderer: Renderer, camera: Camera) {\r\n this.renderer = renderer;\r\n this.camera = camera;\r\n this.createResources();\r\n this.createPipelines();\r\n }\r\n\r\n private createResources(): void {\r\n const device = this.renderer.device;\r\n\r\n // 创建采样器\r\n this.sampler = device.createSampler({\r\n magFilter: 'linear',\r\n minFilter: 'linear',\r\n mipmapFilter: 'linear',\r\n addressModeU: 'repeat',\r\n addressModeV: 'repeat',\r\n });\r\n\r\n // 创建默认白色纹理\r\n this.defaultTexture = device.createTexture({\r\n size: [1, 1, 1],\r\n format: 'rgba8unorm',\r\n usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,\r\n });\r\n device.queue.writeTexture(\r\n { texture: this.defaultTexture },\r\n new Uint8Array([255, 255, 255, 255]),\r\n { bytesPerRow: 4 },\r\n [1, 1, 1]\r\n );\r\n }\r\n\r\n private createPipelines(): void {\r\n const device = this.renderer.device;\r\n\r\n // === 有纹理的管线 ===\r\n const shaderModuleTextured = device.createShaderModule({ code: shaderCodeTextured });\r\n\r\n this.bindGroupLayoutTextured = device.createBindGroupLayout({\r\n entries: [\r\n { binding: 0, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: \"uniform\" } },\r\n { binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: { type: \"filtering\" } },\r\n { binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: \"float\" } },\r\n ],\r\n });\r\n\r\n const pipelineLayoutTextured = device.createPipelineLayout({\r\n bindGroupLayouts: [this.bindGroupLayoutTextured],\r\n });\r\n\r\n const vertexBufferLayoutTextured: GPUVertexBufferLayout = {\r\n arrayStride: 32,\r\n attributes: [\r\n { shaderLocation: 0, offset: 0, format: \"float32x3\" },\r\n { shaderLocation: 1, offset: 12, format: \"float32x3\" },\r\n { shaderLocation: 2, offset: 24, format: \"float32x2\" },\r\n ],\r\n };\r\n\r\n const basePipelineDescTextured: GPURenderPipelineDescriptor = {\r\n layout: pipelineLayoutTextured,\r\n vertex: {\r\n module: shaderModuleTextured,\r\n entryPoint: \"vs_main\",\r\n buffers: [vertexBufferLayoutTextured],\r\n },\r\n fragment: {\r\n module: shaderModuleTextured,\r\n entryPoint: \"fs_main\",\r\n targets: [{ format: this.renderer.format }],\r\n },\r\n primitive: { topology: \"triangle-list\", frontFace: \"ccw\" },\r\n depthStencil: { format: this.renderer.depthFormat, depthWriteEnabled: true, depthCompare: \"less\" },\r\n };\r\n\r\n this.pipelineTextured = device.createRenderPipeline({\r\n ...basePipelineDescTextured,\r\n primitive: { ...basePipelineDescTextured.primitive, cullMode: \"back\" },\r\n });\r\n\r\n this.pipelineTexturedDoubleSided = device.createRenderPipeline({\r\n ...basePipelineDescTextured,\r\n primitive: { ...basePipelineDescTextured.primitive, cullMode: \"none\" },\r\n });\r\n\r\n // === 无纹理的管线 ===\r\n const shaderModuleUntextured = device.createShaderModule({ code: shaderCodeUntextured });\r\n\r\n this.bindGroupLayoutUntextured = device.createBindGroupLayout({\r\n entries: [\r\n { binding: 0, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: \"uniform\" } },\r\n ],\r\n });\r\n\r\n const pipelineLayoutUntextured = device.createPipelineLayout({\r\n bindGroupLayouts: [this.bindGroupLayoutUntextured],\r\n });\r\n\r\n const vertexBufferLayoutUntextured: GPUVertexBufferLayout = {\r\n arrayStride: 24,\r\n attributes: [\r\n { shaderLocation: 0, offset: 0, format: \"float32x3\" },\r\n { shaderLocation: 1, offset: 12, format: \"float32x3\" },\r\n ],\r\n };\r\n\r\n const basePipelineDescUntextured: GPURenderPipelineDescriptor = {\r\n layout: pipelineLayoutUntextured,\r\n vertex: {\r\n module: shaderModuleUntextured,\r\n entryPoint: \"vs_main\",\r\n buffers: [vertexBufferLayoutUntextured],\r\n },\r\n fragment: {\r\n module: shaderModuleUntextured,\r\n entryPoint: \"fs_main\",\r\n targets: [{ format: this.renderer.format }],\r\n },\r\n primitive: { topology: \"triangle-list\", frontFace: \"ccw\" },\r\n depthStencil: { format: this.renderer.depthFormat, depthWriteEnabled: true, depthCompare: \"less\" },\r\n };\r\n\r\n this.pipelineUntextured = device.createRenderPipeline({\r\n ...basePipelineDescUntextured,\r\n primitive: { ...basePipelineDescUntextured.primitive, cullMode: \"back\" },\r\n });\r\n\r\n this.pipelineUntexturedDoubleSided = device.createRenderPipeline({\r\n ...basePipelineDescUntextured,\r\n primitive: { ...basePipelineDescUntextured.primitive, cullMode: \"none\" },\r\n });\r\n }\r\n\r\n /**\r\n * 添加网格(带材质)- 每个 mesh 创建独立的 uniform buffer\r\n */\r\n addMesh(mesh: Mesh, material?: MaterialData): void {\r\n const device = this.renderer.device;\r\n \r\n const mat = material || {\r\n baseColorFactor: [0.8, 0.8, 0.8, 1] as [number, number, number, number],\r\n baseColorTexture: null,\r\n metallicFactor: 0,\r\n roughnessFactor: 0.5,\r\n doubleSided: false,\r\n };\r\n\r\n // 为每个 mesh 创建独立的 uniform buffer\r\n const uniformBuffer = device.createBuffer({\r\n size: UNIFORM_BUFFER_SIZE,\r\n usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\r\n });\r\n\r\n // 创建 bind group\r\n let bindGroup: GPUBindGroup;\r\n \r\n if (mesh.hasUV) {\r\n const texture = mat.baseColorTexture || this.defaultTexture;\r\n bindGroup = device.createBindGroup({\r\n layout: this.bindGroupLayoutTextured,\r\n entries: [\r\n { binding: 0, resource: { buffer: uniformBuffer } },\r\n { binding: 1, resource: this.sampler },\r\n { binding: 2, resource: texture.createView() },\r\n ],\r\n });\r\n } else {\r\n bindGroup = device.createBindGroup({\r\n layout: this.bindGroupLayoutUntextured,\r\n entries: [\r\n { binding: 0, resource: { buffer: uniformBuffer } },\r\n ],\r\n });\r\n }\r\n\r\n this.items.push({ mesh, material: mat, uniformBuffer, bindGroup });\r\n }\r\n\r\n /**\r\n * 移除网格\r\n */\r\n removeMesh(mesh: Mesh): void {\r\n const index = this.items.findIndex(item => item.mesh === mesh);\r\n if (index !== -1) {\r\n const item = this.items[index];\r\n item.mesh.destroy();\r\n item.uniformBuffer.destroy();\r\n this.items.splice(index, 1);\r\n }\r\n }\r\n\r\n /**\r\n * 按索引移除网格\r\n */\r\n removeMeshByIndex(index: number): boolean {\r\n if (index >= 0 && index < this.items.length) {\r\n const item = this.items[index];\r\n item.mesh.destroy();\r\n item.uniformBuffer.destroy();\r\n this.items.splice(index, 1);\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * 清空所有网格\r\n */\r\n clear(): void {\r\n for (const item of this.items) {\r\n item.mesh.destroy();\r\n item.uniformBuffer.destroy();\r\n }\r\n this.items = [];\r\n }\r\n\r\n /**\r\n * 设置光照方向\r\n */\r\n setLightDirection(x: number, y: number, z: number): void {\r\n const len = Math.sqrt(x * x + y * y + z * z);\r\n this.lightDir[0] = x / len;\r\n this.lightDir[1] = y / len;\r\n this.lightDir[2] = z / len;\r\n }\r\n\r\n /**\r\n * 设置环境光强度\r\n */\r\n setAmbientIntensity(intensity: number): void {\r\n this.ambientIntensity = Math.max(0, Math.min(1, intensity));\r\n }\r\n\r\n /**\r\n * 获取环境光强度\r\n */\r\n getAmbientIntensity(): number {\r\n return this.ambientIntensity;\r\n }\r\n\r\n /**\r\n * 渲染所有网格\r\n */\r\n render(pass: GPURenderPassEncoder): void {\r\n if (this.items.length === 0) return;\r\n\r\n const device = this.renderer.device;\r\n const vpMatrix = new Float32Array(this.camera.viewProjectionMatrix);\r\n const lightData = new Float32Array([\r\n this.lightDir[0], this.lightDir[1], this.lightDir[2], this.ambientIntensity\r\n ]);\r\n\r\n for (const item of this.items) {\r\n const { mesh, material, uniformBuffer, bindGroup } = item;\r\n\r\n // 更新该 mesh 的 uniform buffer\r\n device.queue.writeBuffer(uniformBuffer, 0, vpMatrix.buffer);\r\n device.queue.writeBuffer(uniformBuffer, 64, mesh.modelMatrix.buffer);\r\n const colorData = new Float32Array(material.baseColorFactor);\r\n device.queue.writeBuffer(uniformBuffer, 128, colorData.buffer);\r\n device.queue.writeBuffer(uniformBuffer, 144, lightData.buffer);\r\n\r\n // 选择管线\r\n let pipeline: GPURenderPipeline;\r\n if (mesh.hasUV) {\r\n pipeline = material.doubleSided ? this.pipelineTexturedDoubleSided : this.pipelineTextured;\r\n } else {\r\n pipeline = material.doubleSided ? this.pipelineUntexturedDoubleSided : this.pipelineUntextured;\r\n }\r\n\r\n pass.setPipeline(pipeline);\r\n pass.setBindGroup(0, bindGroup);\r\n pass.setVertexBuffer(0, mesh.vertexBuffer);\r\n\r\n if (mesh.indexBuffer && mesh.indexCount > 0) {\r\n pass.setIndexBuffer(mesh.indexBuffer, mesh.indexFormat);\r\n pass.drawIndexed(mesh.indexCount);\r\n } else {\r\n pass.draw(mesh.vertexCount);\r\n }\r\n }\r\n }\r\n\r\n getMeshCount(): number {\r\n return this.items.length;\r\n }\r\n\r\n getMeshByIndex(index: number): Mesh | null {\r\n if (index >= 0 && index < this.items.length) {\r\n return this.items[index].mesh;\r\n }\r\n return null;\r\n }\r\n\r\n /**\r\n * 获取指定索引网格的材质颜色\r\n */\r\n getMeshColor(index: number): [number, number, number, number] | null {\r\n if (index >= 0 && index < this.items.length) {\r\n return [...this.items[index].material.baseColorFactor] as [number, number, number, number];\r\n }\r\n return null;\r\n }\r\n\r\n /**\r\n * 设置指定索引网格的材质颜色\r\n */\r\n setMeshColor(index: number, r: number, g: number, b: number, a: number = 1): boolean {\r\n if (index >= 0 && index < this.items.length) {\r\n this.items[index].material.baseColorFactor = [r, g, b, a];\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * 设置指定范围内所有网格的材质颜色\r\n */\r\n setMeshRangeColor(startIndex: number, count: number, r: number, g: number, b: number, a: number = 1): number {\r\n let modified = 0;\r\n for (let i = 0; i < count; i++) {\r\n if (this.setMeshColor(startIndex + i, r, g, b, a)) {\r\n modified++;\r\n }\r\n }\r\n return modified;\r\n }\r\n\r\n getCombinedBoundingBox(): MeshBoundingBox | null {\r\n if (this.items.length === 0) return null;\r\n\r\n let combinedMin: [number, number, number] | null = null;\r\n let combinedMax: [number, number, number] | null = null;\r\n\r\n for (const item of this.items) {\r\n const bbox = item.mesh.getWorldBoundingBox();\r\n if (!bbox) continue;\r\n\r\n if (combinedMin === null || combinedMax === null) {\r\n combinedMin = [...bbox.min];\r\n combinedMax = [...bbox.max];\r\n } else {\r\n combinedMin[0] = Math.min(combinedMin[0], bbox.min[0]);\r\n combinedMin[1] = Math.min(combinedMin[1], bbox.min[1]);\r\n combinedMin[2] = Math.min(combinedMin[2], bbox.min[2]);\r\n combinedMax[0] = Math.max(combinedMax[0], bbox.max[0]);\r\n combinedMax[1] = Math.max(combinedMax[1], bbox.max[1]);\r\n combinedMax[2] = Math.max(combinedMax[2], bbox.max[2]);\r\n }\r\n }\r\n\r\n if (combinedMin === null || combinedMax === null) return null;\r\n\r\n const center: [number, number, number] = [\r\n (combinedMin[0] + combinedMax[0]) / 2,\r\n (combinedMin[1] + combinedMax[1]) / 2,\r\n (combinedMin[2] + combinedMax[2]) / 2,\r\n ];\r\n const dx = combinedMax[0] - combinedMin[0];\r\n const dy = combinedMax[1] - combinedMin[1];\r\n const dz = combinedMax[2] - combinedMin[2];\r\n const radius = Math.sqrt(dx * dx + dy * dy + dz * dz) / 2;\r\n\r\n return { min: combinedMin, max: combinedMax, center, radius };\r\n }\r\n\r\n destroy(): void {\r\n this.clear();\r\n if (this.defaultTexture) this.defaultTexture.destroy();\r\n }\r\n}\r\n","import { Mesh, MeshBoundingBox } from '../mesh/Mesh';\r\n\r\n/**\r\n * GLB 文件格式常量\r\n */\r\nconst GLB_MAGIC = 0x46546C67; // 'glTF'\r\nconst GLB_VERSION = 2;\r\nconst CHUNK_TYPE_JSON = 0x4E4F534A; // 'JSON'\r\nconst CHUNK_TYPE_BIN = 0x004E4942; // 'BIN\\0'\r\n\r\n/**\r\n * glTF 访问器组件类型\r\n */\r\nconst COMPONENT_TYPES: Record<number, { size: number; type: 'float' | 'uint8' | 'uint16' | 'uint32' | 'int8' | 'int16' }> = {\r\n 5120: { size: 1, type: 'int8' }, // BYTE\r\n 5121: { size: 1, type: 'uint8' }, // UNSIGNED_BYTE\r\n 5122: { size: 2, type: 'int16' }, // SHORT\r\n 5123: { size: 2, type: 'uint16' }, // UNSIGNED_SHORT\r\n 5125: { size: 4, type: 'uint32' }, // UNSIGNED_INT\r\n 5126: { size: 4, type: 'float' }, // FLOAT\r\n};\r\n\r\n/**\r\n * glTF 类型元素数量\r\n */\r\nconst TYPE_SIZES: Record<string, number> = {\r\n SCALAR: 1,\r\n VEC2: 2,\r\n VEC3: 3,\r\n VEC4: 4,\r\n MAT2: 4,\r\n MAT3: 9,\r\n MAT4: 16,\r\n};\r\n\r\n/**\r\n * 材质数据\r\n */\r\nexport interface MaterialData {\r\n baseColorFactor: [number, number, number, number];\r\n baseColorTexture: GPUTexture | null;\r\n metallicFactor: number;\r\n roughnessFactor: number;\r\n doubleSided: boolean;\r\n}\r\n\r\n/**\r\n * 加载后的 Mesh 数据(包含材质)\r\n */\r\nexport interface LoadedMesh {\r\n mesh: Mesh;\r\n material: MaterialData;\r\n}\r\n\r\n/**\r\n * GLBLoader - GLB 文件加载器\r\n * 解析 GLB 文件并生成 Mesh[],支持贴图\r\n */\r\nexport class GLBLoader {\r\n private device: GPUDevice;\r\n private textureCache: Map<number, GPUTexture> = new Map();\r\n\r\n constructor(device: GPUDevice) {\r\n this.device = device;\r\n }\r\n\r\n /**\r\n * 加载 GLB 文件\r\n */\r\n async load(url: string): Promise<LoadedMesh[]> {\r\n const response = await fetch(url);\r\n if (!response.ok) {\r\n throw new Error(`无法加载 GLB 文件: ${url}`);\r\n }\r\n\r\n const arrayBuffer = await response.arrayBuffer();\r\n return this.parse(arrayBuffer);\r\n }\r\n\r\n /**\r\n * 解析 GLB 二进制数据\r\n */\r\n private async parse(buffer: ArrayBuffer): Promise<LoadedMesh[]> {\r\n const dataView = new DataView(buffer);\r\n let offset = 0;\r\n\r\n // 读取 GLB 头部\r\n const magic = dataView.getUint32(offset, true);\r\n offset += 4;\r\n if (magic !== GLB_MAGIC) {\r\n throw new Error('无效的 GLB 文件');\r\n }\r\n\r\n const version = dataView.getUint32(offset, true);\r\n offset += 4;\r\n if (version !== GLB_VERSION) {\r\n throw new Error(`不支持的 GLB 版本: ${version}`);\r\n }\r\n\r\n const _length = dataView.getUint32(offset, true);\r\n offset += 4;\r\n\r\n // 读取 JSON chunk\r\n const jsonChunkLength = dataView.getUint32(offset, true);\r\n offset += 4;\r\n const jsonChunkType = dataView.getUint32(offset, true);\r\n offset += 4;\r\n\r\n if (jsonChunkType !== CHUNK_TYPE_JSON) {\r\n throw new Error('第一个 chunk 必须是 JSON');\r\n }\r\n\r\n const jsonData = new Uint8Array(buffer, offset, jsonChunkLength);\r\n const jsonString = new TextDecoder().decode(jsonData);\r\n const gltf = JSON.parse(jsonString);\r\n offset += jsonChunkLength;\r\n\r\n // 读取 BIN chunk(可选)\r\n let binData: ArrayBuffer | null = null;\r\n if (offset < buffer.byteLength) {\r\n const binChunkLength = dataView.getUint32(offset, true);\r\n offset += 4;\r\n const binChunkType = dataView.getUint32(offset, true);\r\n offset += 4;\r\n\r\n if (binChunkType === CHUNK_TYPE_BIN) {\r\n binData = buffer.slice(offset, offset + binChunkLength);\r\n }\r\n }\r\n\r\n // 清空纹理缓存\r\n this.textureCache.clear();\r\n\r\n // 解析网格\r\n return this.parseMeshes(gltf, binData);\r\n }\r\n\r\n /**\r\n * 解析所有网格\r\n */\r\n private async parseMeshes(gltf: any, binData: ArrayBuffer | null): Promise<LoadedMesh[]> {\r\n const meshes: LoadedMesh[] = [];\r\n\r\n if (!gltf.meshes || !binData) {\r\n return meshes;\r\n }\r\n\r\n for (const gltfMesh of gltf.meshes) {\r\n for (const primitive of gltfMesh.primitives) {\r\n const loadedMesh = await this.parsePrimitive(gltf, primitive, binData);\r\n if (loadedMesh) {\r\n meshes.push(loadedMesh);\r\n }\r\n }\r\n }\r\n\r\n return meshes;\r\n }\r\n\r\n /**\r\n * 解析单个图元\r\n */\r\n private async parsePrimitive(gltf: any, primitive: any, binData: ArrayBuffer): Promise<LoadedMesh | null> {\r\n const attributes = primitive.attributes;\r\n \r\n // 获取位置数据\r\n if (attributes.POSITION === undefined) {\r\n return null;\r\n }\r\n\r\n const positionAccessor = gltf.accessors[attributes.POSITION];\r\n const positions = this.getAccessorData(gltf, positionAccessor, binData);\r\n\r\n // 获取法线数据(可选,如果没有则生成)\r\n let normals: Float32Array;\r\n if (attributes.NORMAL !== undefined) {\r\n const normalAccessor = gltf.accessors[attributes.NORMAL];\r\n const normalData = this.getAccessorData(gltf, normalAccessor, binData);\r\n normals = new Float32Array(normalData);\r\n } else {\r\n // 生成默认法线(指向 +Y)\r\n normals = new Float32Array(positions.length);\r\n for (let i = 0; i < positions.length; i += 3) {\r\n normals[i] = 0;\r\n normals[i + 1] = 1;\r\n normals[i + 2] = 0;\r\n }\r\n }\r\n\r\n // 获取 UV 坐标(可选)\r\n let uvs: Float32Array | null = null;\r\n if (attributes.TEXCOORD_0 !== undefined) {\r\n const uvAccessor = gltf.accessors[attributes.TEXCOORD_0];\r\n const uvData = this.getAccessorData(gltf, uvAccessor, binData);\r\n uvs = new Float32Array(uvData);\r\n }\r\n\r\n // 创建交错顶点数据: position(3) + normal(3) + uv(2)\r\n const vertexCount = positionAccessor.count;\r\n const hasUV = uvs !== null;\r\n const stride = hasUV ? 8 : 6; // 有 UV 时 8 floats,否则 6 floats\r\n const vertexData = new Float32Array(vertexCount * stride);\r\n \r\n for (let i = 0; i < vertexCount; i++) {\r\n const baseIdx = i * stride;\r\n vertexData[baseIdx + 0] = positions[i * 3 + 0];\r\n vertexData[baseIdx + 1] = positions[i * 3 + 1];\r\n vertexData[baseIdx + 2] = positions[i * 3 + 2];\r\n vertexData[baseIdx + 3] = normals[i * 3 + 0];\r\n vertexData[baseIdx + 4] = normals[i * 3 + 1];\r\n vertexData[baseIdx + 5] = normals[i * 3 + 2];\r\n if (hasUV && uvs) {\r\n vertexData[baseIdx + 6] = uvs[i * 2 + 0];\r\n vertexData[baseIdx + 7] = uvs[i * 2 + 1];\r\n }\r\n }\r\n\r\n // 创建顶点缓冲区\r\n const vertexBuffer = this.device.createBuffer({\r\n size: vertexData.byteLength,\r\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\r\n });\r\n this.device.queue.writeBuffer(vertexBuffer, 0, vertexData);\r\n\r\n // 获取索引数据(可选)\r\n let indexBuffer: GPUBuffer | null = null;\r\n let indexCount = 0;\r\n let indexFormat: 'uint16' | 'uint32' = 'uint16';\r\n\r\n if (primitive.indices !== undefined) {\r\n const indexAccessor = gltf.accessors[primitive.indices];\r\n const indices = this.getAccessorData(gltf, indexAccessor, binData);\r\n indexCount = indexAccessor.count;\r\n\r\n // 根据顶点数量决定索引格式\r\n if (vertexCount > 65535) {\r\n indexFormat = 'uint32';\r\n const indexData = new Uint32Array(indexCount);\r\n for (let i = 0; i < indexCount; i++) {\r\n indexData[i] = indices[i];\r\n }\r\n indexBuffer = this.device.createBuffer({\r\n size: indexData.byteLength,\r\n usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,\r\n });\r\n this.device.queue.writeBuffer(indexBuffer, 0, indexData);\r\n } else {\r\n const indexData = new Uint16Array(indexCount);\r\n for (let i = 0; i < indexCount; i++) {\r\n indexData[i] = indices[i];\r\n }\r\n indexBuffer = this.device.createBuffer({\r\n size: indexData.byteLength,\r\n usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,\r\n });\r\n this.device.queue.writeBuffer(indexBuffer, 0, indexData);\r\n }\r\n }\r\n\r\n // 计算 bounding box\r\n const boundingBox = this.computeBoundingBox(positions);\r\n\r\n // 解析材质\r\n const material = await this.parseMaterial(gltf, primitive.material, binData);\r\n\r\n // 创建 Mesh\r\n const mesh = new Mesh(vertexBuffer, vertexCount, indexBuffer, indexCount, boundingBox);\r\n mesh.hasUV = hasUV;\r\n mesh.indexFormat = indexFormat;\r\n\r\n return { mesh, material };\r\n }\r\n\r\n /**\r\n * 解析材质\r\n */\r\n private async parseMaterial(gltf: any, materialIndex: number | undefined, binData: ArrayBuffer): Promise<MaterialData> {\r\n // 默认材质\r\n const defaultMaterial: MaterialData = {\r\n baseColorFactor: [1, 1, 1, 1],\r\n baseColorTexture: null,\r\n metallicFactor: 1,\r\n roughnessFactor: 1,\r\n doubleSided: false,\r\n };\r\n\r\n if (materialIndex === undefined || !gltf.materials) {\r\n return defaultMaterial;\r\n }\r\n\r\n const gltfMaterial = gltf.materials[materialIndex];\r\n if (!gltfMaterial) {\r\n return defaultMaterial;\r\n }\r\n\r\n const material: MaterialData = { ...defaultMaterial };\r\n\r\n // 解析 doubleSided\r\n if (gltfMaterial.doubleSided !== undefined) {\r\n material.doubleSided = gltfMaterial.doubleSided;\r\n }\r\n\r\n // 解析 PBR 材质\r\n const pbr = gltfMaterial.pbrMetallicRoughness;\r\n if (pbr) {\r\n // baseColorFactor\r\n if (pbr.baseColorFactor) {\r\n material.baseColorFactor = pbr.baseColorFactor;\r\n }\r\n\r\n // metallicFactor\r\n if (pbr.metallicFactor !== undefined) {\r\n material.metallicFactor = pbr.metallicFactor;\r\n }\r\n\r\n // roughnessFactor\r\n if (pbr.roughnessFactor !== undefined) {\r\n material.roughnessFactor = pbr.roughnessFactor;\r\n }\r\n\r\n // baseColorTexture\r\n if (pbr.baseColorTexture) {\r\n const textureIndex = pbr.baseColorTexture.index;\r\n material.baseColorTexture = await this.loadTexture(gltf, textureIndex, binData);\r\n }\r\n }\r\n\r\n return material;\r\n }\r\n\r\n /**\r\n * 加载纹理\r\n */\r\n private async loadTexture(gltf: any, textureIndex: number, binData: ArrayBuffer): Promise<GPUTexture | null> {\r\n // 检查缓存\r\n if (this.textureCache.has(textureIndex)) {\r\n return this.textureCache.get(textureIndex)!;\r\n }\r\n\r\n if (!gltf.textures || !gltf.images) {\r\n return null;\r\n }\r\n\r\n const texture = gltf.textures[textureIndex];\r\n if (!texture || texture.source === undefined) {\r\n return null;\r\n }\r\n\r\n const image = gltf.images[texture.source];\r\n if (!image) {\r\n return null;\r\n }\r\n\r\n try {\r\n let imageBitmap: ImageBitmap;\r\n\r\n if (image.bufferView !== undefined) {\r\n // 从 buffer 加载图片\r\n const bufferView = gltf.bufferViews[image.bufferView];\r\n const byteOffset = bufferView.byteOffset || 0;\r\n const byteLength = bufferView.byteLength;\r\n const imageData = new Uint8Array(binData, byteOffset, byteLength);\r\n const blob = new Blob([imageData], { type: image.mimeType || 'image/png' });\r\n imageBitmap = await createImageBitmap(blob);\r\n } else if (image.uri) {\r\n // 从 URI 加载(data URI 或外部 URL)\r\n if (image.uri.startsWith('data:')) {\r\n const response = await fetch(image.uri);\r\n const blob = await response.blob();\r\n imageBitmap = await createImageBitmap(blob);\r\n } else {\r\n // 外部 URL - 这里简化处理,实际可能需要相对路径解析\r\n const response = await fetch(image.uri);\r\n const blob = await response.blob();\r\n imageBitmap = await createImageBitmap(blob);\r\n }\r\n } else {\r\n return null;\r\n }\r\n\r\n // 创建 GPU 纹理\r\n const gpuTexture = this.device.createTexture({\r\n size: [imageBitmap.width, imageBitmap.height, 1],\r\n format: 'rgba8unorm',\r\n usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,\r\n });\r\n\r\n this.device.queue.copyExternalImageToTexture(\r\n { source: imageBitmap },\r\n { texture: gpuTexture },\r\n [imageBitmap.width, imageBitmap.height]\r\n );\r\n\r\n // 缓存纹理\r\n this.textureCache.set(textureIndex, gpuTexture);\r\n\r\n return gpuTexture;\r\n } catch (error) {\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * 计算顶点数据的 bounding box\r\n */\r\n private computeBoundingBox(positions: Float32Array | Uint16Array | Uint32Array | Int8Array | Int16Array | Uint8Array): MeshBoundingBox {\r\n if (positions.length < 3) {\r\n return {\r\n min: [0, 0, 0],\r\n max: [0, 0, 0],\r\n center: [0, 0, 0],\r\n radius: 0,\r\n };\r\n }\r\n\r\n // 初始化为第一个点\r\n const min: [number, number, number] = [positions[0], positions[1], positions[2]];\r\n const max: [number, number, number] = [positions[0], positions[1], positions[2]];\r\n\r\n // 遍历所有顶点\r\n for (let i = 3; i < positions.length; i += 3) {\r\n const x = positions[i];\r\n const y = positions[i + 1];\r\n const z = positions[i + 2];\r\n\r\n min[0] = Math.min(min[0], x);\r\n min[1] = Math.min(min[1], y);\r\n min[2] = Math.min(min[2], z);\r\n max[0] = Math.max(max[0], x);\r\n max[1] = Math.max(max[1], y);\r\n max[2] = Math.max(max[2], z);\r\n }\r\n\r\n // 计算中心点\r\n const center: [number, number, number] = [\r\n (min[0] + max[0]) / 2,\r\n (min[1] + max[1]) / 2,\r\n (min[2] + max[2]) / 2,\r\n ];\r\n\r\n // 计算 bounding sphere 半径\r\n const dx = max[0] - min[0];\r\n const dy = max[1] - min[1];\r\n const dz = max[2] - min[2];\r\n const radius = Math.sqrt(dx * dx + dy * dy + dz * dz) / 2;\r\n\r\n return { min, max, center, radius };\r\n }\r\n\r\n /**\r\n * 获取访问器数据 - 修复字节对齐问题\r\n */\r\n private getAccessorData(gltf: any, accessor: any, binData: ArrayBuffer): Float32Array | Uint16Array | Uint32Array | Int8Array | Int16Array | Uint8Array {\r\n const bufferView = gltf.bufferViews[accessor.bufferView];\r\n const componentType = COMPONENT_TYPES[accessor.componentType];\r\n const typeSize = TYPE_SIZES[accessor.type];\r\n const count = accessor.count * typeSize;\r\n\r\n const byteOffset = (bufferView.byteOffset || 0) + (accessor.byteOffset || 0);\r\n const byteLength = count * componentType.size;\r\n\r\n // 复制数据到新的 ArrayBuffer 以避免对齐问题\r\n const alignedBuffer = new ArrayBuffer(byteLength);\r\n const srcView = new Uint8Array(binData, byteOffset, byteLength);\r\n const dstView = new Uint8Array(alignedBuffer);\r\n dstView.set(srcView);\r\n\r\n switch (componentType.type) {\r\n case 'float':\r\n return new Float32Array(alignedBuffer);\r\n case 'uint8':\r\n return new Uint8Array(alignedBuffer);\r\n case 'uint16':\r\n return new Uint16Array(alignedBuffer);\r\n case 'uint32':\r\n return new Uint32Array(alignedBuffer);\r\n case 'int8':\r\n return new Int8Array(alignedBuffer);\r\n case 'int16':\r\n return new Int16Array(alignedBuffer);\r\n default:\r\n throw new Error(`不支持的组件类型: ${accessor.componentType}`);\r\n }\r\n }\r\n\r\n /**\r\n * 创建测试立方体(用于调试)\r\n */\r\n createTestCube(): LoadedMesh {\r\n // 立方体顶点数据: position(3) + normal(3) + uv(2)\r\n const vertices = new Float32Array([\r\n // 前面 (z = 0.5)\r\n -0.5, -0.5, 0.5, 0, 0, 1, 0, 1,\r\n 0.5, -0.5, 0.5, 0, 0, 1, 1, 1,\r\n 0.5, 0.5, 0.5, 0, 0, 1, 1, 0,\r\n -0.5, 0.5, 0.5, 0, 0, 1, 0, 0,\r\n // 后面 (z = -0.5)\r\n 0.5, -0.5, -0.5, 0, 0, -1, 0, 1,\r\n -0.5, -0.5, -0.5, 0, 0, -1, 1, 1,\r\n -0.5, 0.5, -0.5, 0, 0, -1, 1, 0,\r\n 0.5, 0.5, -0.5, 0, 0, -1, 0, 0,\r\n // 上面 (y = 0.5)\r\n -0.5, 0.5, 0.5, 0, 1, 0, 0, 1,\r\n 0.5, 0.5, 0.5, 0, 1, 0, 1, 1,\r\n 0.5, 0.5, -0.5, 0, 1, 0, 1, 0,\r\n -0.5, 0.5, -0.5, 0, 1, 0, 0, 0,\r\n // 下面 (y = -0.5)\r\n -0.5, -0.5, -0.5, 0, -1, 0, 0, 1,\r\n 0.5, -0.5, -0.5, 0, -1, 0, 1, 1,\r\n 0.5, -0.5, 0.5, 0, -1, 0, 1, 0,\r\n -0.5, -0.5, 0.5, 0, -1, 0, 0, 0,\r\n // 右面 (x = 0.5)\r\n 0.5, -0.5, 0.5, 1, 0, 0, 0, 1,\r\n 0.5, -0.5, -0.5, 1, 0, 0, 1, 1,\r\n 0.5, 0.5, -0.5, 1, 0, 0, 1, 0,\r\n 0.5, 0.5, 0.5, 1, 0, 0, 0, 0,\r\n // 左面 (x = -0.5)\r\n -0.5, -0.5, -0.5, -1, 0, 0, 0, 1,\r\n -0.5, -0.5, 0.5, -1, 0, 0, 1, 1,\r\n -0.5, 0.5, 0.5, -1, 0, 0, 1, 0,\r\n -0.5, 0.5, -0.5, -1, 0, 0, 0, 0,\r\n ]);\r\n\r\n const indices = new Uint16Array([\r\n 0, 1, 2, 0, 2, 3, // 前\r\n 4, 5, 6, 4, 6, 7, // 后\r\n 8, 9, 10, 8, 10, 11, // 上\r\n 12, 13, 14, 12, 14, 15, // 下\r\n 16, 17, 18, 16, 18, 19, // 右\r\n 20, 21, 22, 20, 22, 23, // 左\r\n ]);\r\n\r\n const vertexBuffer = this.device.createBuffer({\r\n size: vertices.byteLength,\r\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\r\n });\r\n this.device.queue.writeBuffer(vertexBuffer, 0, vertices);\r\n\r\n const indexBuffer = this.device.createBuffer({\r\n size: indices.byteLength,\r\n usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,\r\n });\r\n this.device.queue.writeBuffer(indexBuffer, 0, indices);\r\n\r\n // 立方体 bounding box: -0.5 到 0.5\r\n const cubeBbox: MeshBoundingBox = {\r\n min: [-0.5, -0.5, -0.5],\r\n max: [0.5, 0.5, 0.5],\r\n center: [0, 0, 0],\r\n radius: Math.sqrt(0.75),\r\n };\r\n\r\n const mesh = new Mesh(vertexBuffer, 24, indexBuffer, 36, cubeBbox);\r\n mesh.hasUV = true;\r\n mesh.indexFormat = 'uint16';\r\n\r\n return {\r\n mesh,\r\n material: {\r\n baseColorFactor: [1, 1, 1, 1],\r\n baseColorTexture: null,\r\n metallicFactor: 0,\r\n roughnessFactor: 0.5,\r\n doubleSided: false,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * 创建测试球体\r\n */\r\n createTestSphere(radius: number = 0.5, segments: number = 32, rings: number = 16): LoadedMesh {\r\n const vertices: number[] = [];\r\n const indices: number[] = [];\r\n\r\n // 生成顶点\r\n for (let ring = 0; ring <= rings; ring++) {\r\n const phi = (ring / rings) * Math.PI;\r\n const sinPhi = Math.sin(phi);\r\n const cosPhi = Math.cos(phi);\r\n\r\n for (let seg = 0; seg <= segments; seg++) {\r\n const theta = (seg / segments) * Math.PI * 2;\r\n const sinTheta = Math.sin(theta);\r\n const cosTheta = Math.cos(theta);\r\n\r\n // 位置\r\n const x = radius * sinPhi * cosTheta;\r\n const y = radius * cosPhi;\r\n const z = radius * sinPhi * sinTheta;\r\n\r\n // 法线(球体法线就是归一化的位置)\r\n const nx = sinPhi * cosTheta;\r\n const ny = cosPhi;\r\n const nz = sinPhi * sinTheta;\r\n\r\n // UV\r\n const u = seg / segments;\r\n const v = ring / rings;\r\n\r\n vertices.push(x, y, z, nx, ny, nz, u, v);\r\n }\r\n }\r\n\r\n // 生成索引\r\n for (let ring = 0; ring < rings; ring++) {\r\n for (let seg = 0; seg < segments; seg++) {\r\n const current = ring * (segments + 1) + seg;\r\n const next = current + segments + 1;\r\n\r\n indices.push(current, next, current + 1);\r\n indices.push(current + 1, next, next + 1);\r\n }\r\n }\r\n\r\n const vertexData = new Float32Array(vertices);\r\n const indexData = new Uint16Array(indices);\r\n\r\n const vertexBuffer = this.device.createBuffer({\r\n size: vertexData.byteLength,\r\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\r\n });\r\n this.device.queue.writeBuffer(vertexBuffer, 0, vertexData);\r\n\r\n const indexBuffer = this.device.createBuffer({\r\n size: indexData.byteLength,\r\n usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,\r\n });\r\n this.device.queue.writeBuffer(indexBuffer, 0, indexData);\r\n\r\n // 球体 bounding box\r\n const sphereBbox: MeshBoundingBox = {\r\n min: [-radius, -radius, -radius],\r\n max: [radius, radius, radius],\r\n center: [0, 0, 0],\r\n radius: radius,\r\n };\r\n\r\n const mesh = new Mesh(vertexBuffer, vertexData.length / 8, indexBuffer, indexData.length, sphereBbox);\r\n mesh.hasUV = true;\r\n mesh.indexFormat = 'uint16';\r\n\r\n return {\r\n mesh,\r\n material: {\r\n baseColorFactor: [1, 1, 1, 1],\r\n baseColorTexture: null,\r\n metallicFactor: 0,\r\n roughnessFactor: 0.5,\r\n doubleSided: false,\r\n },\r\n };\r\n }\r\n}\r\n","/**\r\n * OBJParser - OBJ 文件文本解析器\r\n * 解析 OBJ 文件文本内容,提取几何数据\r\n */\r\n\r\n/**\r\n * 解析后的 OBJ 数据结构\r\n */\r\nexport interface ParsedOBJData {\r\n objects: ParsedObject[];\r\n}\r\n\r\n/**\r\n * 单个对象/组的数据\r\n */\r\nexport interface ParsedObject {\r\n name: string;\r\n positions: number[]; // 展开后的顶点位置 [x,y,z, x,y,z, ...]\r\n normals: number[]; // 展开后的法线 [nx,ny,nz, ...]\r\n uvs: number[]; // 展开后的 UV [u,v, u,v, ...]\r\n indices: number[]; // 三角形索引\r\n materialName: string | null;\r\n}\r\n\r\n/**\r\n * 面顶点索引结构\r\n */\r\ninterface FaceVertex {\r\n positionIndex: number;\r\n uvIndex: number | null;\r\n normalIndex: number | null;\r\n}\r\n\r\n/**\r\n * OBJ 文本解析器\r\n */\r\nexport class OBJParser {\r\n // 全局顶点池\r\n private positions: number[] = []; // v 指令收集的顶点位置\r\n private uvs: number[] = []; // vt 指令收集的纹理坐标\r\n private normals: number[] = []; // vn 指令收集的法线\r\n\r\n // 当前对象数据\r\n private currentObject: ParsedObject | null = null;\r\n private objects: ParsedObject[] = [];\r\n\r\n // 当前材质\r\n private currentMaterial: string | null = null;\r\n\r\n // 顶点去重映射 (用于索引缓冲区)\r\n private vertexMap: Map<string, number> = new Map();\r\n private vertexCount: number = 0;\r\n\r\n // 已警告的不支持指令集合(用于只警告首次)\r\n private warnedDirectives: Set<string> = new Set();\r\n\r\n /**\r\n * 解析 OBJ 文本内容\r\n * @param text OBJ 文件文本\r\n * @returns 解析后的数据结构\r\n */\r\n parse(text: string): ParsedOBJData {\r\n // 重置状态\r\n this.reset();\r\n\r\n // 按行分割文本\r\n const lines = text.split(/\\r?\\n/);\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n const line = lines[i].trim();\r\n \r\n // 跳过空行和注释\r\n if (line === '' || line.startsWith('#')) {\r\n continue;\r\n }\r\n\r\n this.parseLine(line, i + 1);\r\n }\r\n\r\n // 确保最后一个对象被添加\r\n this.finalizeCurrentObject();\r\n\r\n // 如果没有任何对象,但有面数据,创建默认对象\r\n if (this.objects.length === 0 && this.currentObject && this.currentObject.indices.length > 0) {\r\n this.objects.push(this.currentObject);\r\n }\r\n\r\n return { objects: this.objects };\r\n }\r\n\r\n /**\r\n * 重置解析器状态\r\n */\r\n private reset(): void {\r\n this.positions = [];\r\n this.uvs = [];\r\n this.normals = [];\r\n this.currentObject = null;\r\n this.objects = [];\r\n this.currentMaterial = null;\r\n this.vertexMap = new Map();\r\n this.vertexCount = 0;\r\n this.warnedDirectives = new Set();\r\n }\r\n\r\n /**\r\n * 警告不支持的指令(仅首次出现时警告)\r\n * @param directive 指令名称\r\n * @param lineNum 行号\r\n */\r\n private warnUnsupportedDirective(directive: string, lineNum: number): void {\r\n if (!this.warnedDirectives.has(directive)) {\r\n this.warnedDirectives.add(directive);\r\n }\r\n }\r\n\r\n /**\r\n * 解析单行\r\n */\r\n private parseLine(line: string, lineNum: number): void {\r\n // 分割指令和参数\r\n const parts = line.split(/\\s+/);\r\n const directive = parts[0].toLowerCase();\r\n\r\n try {\r\n switch (directive) {\r\n case 'v':\r\n this.parseVertex(parts);\r\n break;\r\n case 'vt':\r\n this.parseTextureCoord(parts);\r\n break;\r\n case 'vn':\r\n this.parseNormal(parts);\r\n break;\r\n case 'f':\r\n this.parseFace(parts, lineNum);\r\n break;\r\n case 'o':\r\n case 'g':\r\n this.parseObjectOrGroup(parts);\r\n break;\r\n case 'usemtl':\r\n this.parseUseMaterial(parts);\r\n break;\r\n case 'mtllib':\r\n // MTL 文件引用,在 OBJLoader 中处理\r\n break;\r\n case 's':\r\n // 平滑组,暂不支持,警告首次\r\n this.warnUnsupportedDirective(directive, lineNum);\r\n break;\r\n default:\r\n // 不支持的指令,警告首次\r\n this.warnUnsupportedDirective(directive, lineNum);\r\n break;\r\n }\r\n } catch (e) {\r\n // 解析错误,跳过该行\r\n }\r\n }\r\n\r\n /**\r\n * 解析顶点位置 (v x y z [w])\r\n */\r\n private parseVertex(parts: string[]): void {\r\n if (parts.length < 4) {\r\n throw new Error('顶点数据不完整');\r\n }\r\n\r\n const x = parseFloat(parts[1]);\r\n const y = parseFloat(parts[2]);\r\n const z = parseFloat(parts[3]);\r\n\r\n if (isNaN(x) || isNaN(y) || isNaN(z)) {\r\n throw new Error('无效的顶点坐标');\r\n }\r\n\r\n this.positions.push(x, y, z);\r\n }\r\n\r\n /**\r\n * 解析纹理坐标 (vt u [v] [w])\r\n */\r\n private parseTextureCoord(parts: string[]): void {\r\n if (parts.length < 2) {\r\n throw new Error('纹理坐标数据不完整');\r\n }\r\n\r\n const u = parseFloat(parts[1]);\r\n const v = parts.length > 2 ? parseFloat(parts[2]) : 0;\r\n\r\n if (isNaN(u) || isNaN(v)) {\r\n throw new Error('无效的纹理坐标');\r\n }\r\n\r\n this.uvs.push(u, v);\r\n }\r\n\r\n /**\r\n * 解析法线 (vn x y z)\r\n */\r\n private parseNormal(parts: string[]): void {\r\n if (parts.length < 4) {\r\n throw new Error('法线数据不完整');\r\n }\r\n\r\n const x = parseFloat(parts[1]);\r\n const y = parseFloat(parts[2]);\r\n const z = parseFloat(parts[3]);\r\n\r\n if (isNaN(x) || isNaN(y) || isNaN(z)) {\r\n throw new Error('无效的法线');\r\n }\r\n\r\n this.normals.push(x, y, z);\r\n }\r\n\r\n /**\r\n * 解析面 (f v1 v2 v3 ...)\r\n * 支持四种格式:\r\n * - f v v v (仅顶点)\r\n * - f v/vt v/vt v/vt (顶点/纹理)\r\n * - f v/vt/vn v/vt/vn v/vt/vn (顶点/纹理/法线)\r\n * - f v//vn v//vn v//vn (顶点//法线)\r\n */\r\n private parseFace(parts: string[], _lineNum: number): void {\r\n if (parts.length < 4) {\r\n throw new Error('面数据不完整,至少需要3个顶点');\r\n }\r\n\r\n // 确保有当前对象\r\n this.ensureCurrentObject();\r\n\r\n // 解析面顶点\r\n const faceVertices: FaceVertex[] = [];\r\n for (let i = 1; i < parts.length; i++) {\r\n const vertex = this.parseFaceVertex(parts[i]);\r\n if (vertex) {\r\n faceVertices.push(vertex);\r\n }\r\n }\r\n\r\n if (faceVertices.length < 3) {\r\n throw new Error('面顶点数量不足');\r\n }\r\n\r\n // 三角化多边形(扇形三角化)\r\n // 对于 n 边形 [v0, v1, v2, ..., vn-1]:\r\n // 生成三角形: (v0, v1, v2), (v0, v2, v3), ..., (v0, vn-2, vn-1)\r\n for (let i = 1; i < faceVertices.length - 1; i++) {\r\n this.addTriangle(faceVertices[0], faceVertices[i], faceVertices[i + 1]);\r\n }\r\n }\r\n\r\n /**\r\n * 解析面顶点索引\r\n * 支持格式: v, v/vt, v/vt/vn, v//vn\r\n */\r\n private parseFaceVertex(vertexStr: string): FaceVertex | null {\r\n const parts = vertexStr.split('/');\r\n \r\n // 解析位置索引(必需)\r\n const positionIndex = this.resolveIndex(parseInt(parts[0]), this.positions.length / 3);\r\n if (isNaN(positionIndex)) {\r\n return null;\r\n }\r\n\r\n // 解析纹理坐标索引(可选)\r\n let uvIndex: number | null = null;\r\n if (parts.length > 1 && parts[1] !== '') {\r\n uvIndex = this.resolveIndex(parseInt(parts[1]), this.uvs.length / 2);\r\n if (isNaN(uvIndex)) {\r\n uvIndex = null;\r\n }\r\n }\r\n\r\n // 解析法线索引(可选)\r\n let normalIndex: number | null = null;\r\n if (parts.length > 2 && parts[2] !== '') {\r\n normalIndex = this.resolveIndex(parseInt(parts[2]), this.normals.length / 3);\r\n if (isNaN(normalIndex)) {\r\n normalIndex = null;\r\n }\r\n }\r\n\r\n return { positionIndex, uvIndex, normalIndex };\r\n }\r\n\r\n /**\r\n * 解析索引,支持负索引\r\n * 负索引表示相对于当前顶点数的偏移(-1 表示最后一个顶点)\r\n */\r\n private resolveIndex(index: number, count: number): number {\r\n if (isNaN(index)) {\r\n return NaN;\r\n }\r\n\r\n if (index < 0) {\r\n // 负索引:-1 表示最后一个,-2 表示倒数第二个\r\n return count + index;\r\n } else {\r\n // 正索引:OBJ 索引从 1 开始\r\n return index - 1;\r\n }\r\n }\r\n\r\n /**\r\n * 添加三角形到当前对象\r\n */\r\n private addTriangle(v0: FaceVertex, v1: FaceVertex, v2: FaceVertex): void {\r\n const idx0 = this.addVertex(v0);\r\n const idx1 = this.addVertex(v1);\r\n const idx2 = this.addVertex(v2);\r\n\r\n this.currentObject!.indices.push(idx0, idx1, idx2);\r\n }\r\n\r\n /**\r\n * 添加顶点到当前对象,返回索引\r\n * 使用顶点去重,相同的顶点组合只添加一次\r\n */\r\n private addVertex(vertex: FaceVertex): number {\r\n // 创建顶点键用于去重\r\n const key = `${vertex.positionIndex}/${vertex.uvIndex ?? ''}/${vertex.normalIndex ?? ''}`;\r\n\r\n // 检查是否已存在\r\n if (this.vertexMap.has(key)) {\r\n return this.vertexMap.get(key)!;\r\n }\r\n\r\n // 添加新顶点\r\n const index = this.vertexCount++;\r\n this.vertexMap.set(key, index);\r\n\r\n // 添加位置数据\r\n const posIdx = vertex.positionIndex * 3;\r\n if (posIdx >= 0 && posIdx + 2 < this.positions.length) {\r\n this.currentObject!.positions.push(\r\n this.positions[posIdx],\r\n this.positions[posIdx + 1],\r\n this.positions[posIdx + 2]\r\n );\r\n } else {\r\n // 无效索引,使用默认值\r\n this.currentObject!.positions.push(0, 0, 0);\r\n }\r\n\r\n // 添加法线数据\r\n if (vertex.normalIndex !== null) {\r\n const normIdx = vertex.normalIndex * 3;\r\n if (normIdx >= 0 && normIdx + 2 < this.normals.length) {\r\n this.currentObject!.normals.push(\r\n this.normals[normIdx],\r\n this.normals[normIdx + 1],\r\n this.normals[normIdx + 2]\r\n );\r\n } else {\r\n this.currentObject!.normals.push(0, 1, 0);\r\n }\r\n }\r\n\r\n // 添加 UV 数据\r\n if (vertex.uvIndex !== null) {\r\n const uvIdx = vertex.uvIndex * 2;\r\n if (uvIdx >= 0 && uvIdx + 1 < this.uvs.length) {\r\n this.currentObject!.uvs.push(\r\n this.uvs[uvIdx],\r\n this.uvs[uvIdx + 1]\r\n );\r\n } else {\r\n this.currentObject!.uvs.push(0, 0);\r\n }\r\n }\r\n\r\n return index;\r\n }\r\n\r\n /**\r\n * 解析对象或组 (o name / g name)\r\n */\r\n private parseObjectOrGroup(parts: string[]): void {\r\n // 完成当前对象\r\n this.finalizeCurrentObject();\r\n\r\n // 创建新对象\r\n const name = parts.length > 1 ? parts.slice(1).join(' ') : 'default';\r\n this.createNewObject(name);\r\n }\r\n\r\n /**\r\n * 解析材质引用 (usemtl name)\r\n */\r\n private parseUseMaterial(parts: string[]): void {\r\n if (parts.length > 1) {\r\n this.currentMaterial = parts.slice(1).join(' ');\r\n \r\n // 如果当前对象已有面数据,需要创建新对象\r\n if (this.currentObject && this.currentObject.indices.length > 0) {\r\n this.finalizeCurrentObject();\r\n this.createNewObject(this.currentObject?.name || 'default');\r\n }\r\n \r\n if (this.currentObject) {\r\n this.currentObject.materialName = this.currentMaterial;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * 确保存在当前对象\r\n */\r\n private ensureCurrentObject(): void {\r\n if (!this.currentObject) {\r\n this.createNewObject('default');\r\n }\r\n }\r\n\r\n /**\r\n * 创建新对象\r\n */\r\n private createNewObject(name: string): void {\r\n this.currentObject = {\r\n name,\r\n positions: [],\r\n normals: [],\r\n uvs: [],\r\n indices: [],\r\n materialName: this.currentMaterial,\r\n };\r\n this.vertexMap = new Map();\r\n this.vertexCount = 0;\r\n }\r\n\r\n /**\r\n * 完成当前对象并添加到列表\r\n */\r\n private finalizeCurrentObject(): void {\r\n if (this.currentObject && this.currentObject.indices.length > 0) {\r\n this.objects.push(this.currentObject);\r\n }\r\n this.currentObject = null;\r\n }\r\n}\r\n","/**\r\n * MTLParser - MTL 材质文件解析器\r\n * 解析 MTL 文件文本内容,提取材质数据\r\n */\r\n\r\n/**\r\n * 解析后的材质数据\r\n */\r\nexport interface ParsedMaterial {\r\n name: string;\r\n diffuseColor: [number, number, number]; // Kd\r\n diffuseTexture: string | null; // map_Kd\r\n opacity: number; // d 或 1-Tr\r\n}\r\n\r\n/**\r\n * MTL 文本解析器\r\n */\r\nexport class MTLParser {\r\n // 当前材质\r\n private currentMaterial: ParsedMaterial | null = null;\r\n private materials: Map<string, ParsedMaterial> = new Map();\r\n\r\n /**\r\n * 解析 MTL 文本内容\r\n * @param text MTL 文件文本\r\n * @returns 材质名称到材质数据的映射\r\n */\r\n parse(text: string): Map<string, ParsedMaterial> {\r\n // 重置状态\r\n this.reset();\r\n\r\n // 按行分割文本\r\n const lines = text.split(/\\r?\\n/);\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n const line = lines[i].trim();\r\n\r\n // 跳过空行和注释\r\n if (line === '' || line.startsWith('#')) {\r\n continue;\r\n }\r\n\r\n this.parseLine(line, i + 1);\r\n }\r\n\r\n // 确保最后一个材质被添加\r\n this.finalizeCurrentMaterial();\r\n\r\n return this.materials;\r\n }\r\n\r\n /**\r\n * 重置解析器状态\r\n */\r\n private reset(): void {\r\n this.currentMaterial = null;\r\n this.materials = new Map();\r\n }\r\n\r\n /**\r\n * 解析单行\r\n */\r\n private parseLine(line: string, lineNum: number): void {\r\n // 分割指令和参数\r\n const parts = line.split(/\\s+/);\r\n const directive = parts[0].toLowerCase();\r\n\r\n try {\r\n switch (directive) {\r\n case 'newmtl':\r\n this.parseNewMaterial(parts);\r\n break;\r\n case 'kd':\r\n this.parseDiffuseColor(parts);\r\n break;\r\n case 'map_kd':\r\n this.parseDiffuseTexture(parts);\r\n break;\r\n case 'd':\r\n this.parseOpacity(parts);\r\n break;\r\n case 'tr':\r\n this.parseTransparency(parts);\r\n break;\r\n default:\r\n // 忽略不支持的指令 (Ka, Ks, Ns, illum, etc.)\r\n break;\r\n }\r\n } catch (e) {\r\n // 解析错误,跳过该行\r\n }\r\n }\r\n\r\n /**\r\n * 解析新材质定义 (newmtl name)\r\n */\r\n private parseNewMaterial(parts: string[]): void {\r\n // 完成当前材质\r\n this.finalizeCurrentMaterial();\r\n\r\n // 创建新材质\r\n const name = parts.length > 1 ? parts.slice(1).join(' ') : 'default';\r\n this.currentMaterial = this.createDefaultMaterial(name);\r\n }\r\n\r\n /**\r\n * 解析漫反射颜色 (Kd r g b)\r\n */\r\n private parseDiffuseColor(parts: string[]): void {\r\n if (!this.currentMaterial) {\r\n return;\r\n }\r\n\r\n if (parts.length < 4) {\r\n throw new Error('漫反射颜色数据不完整');\r\n }\r\n\r\n const r = parseFloat(parts[1]);\r\n const g = parseFloat(parts[2]);\r\n const b = parseFloat(parts[3]);\r\n\r\n if (isNaN(r) || isNaN(g) || isNaN(b)) {\r\n throw new Error('无效的漫反射颜色值');\r\n }\r\n\r\n this.currentMaterial.diffuseColor = [r, g, b];\r\n }\r\n\r\n /**\r\n * 解析漫反射纹理路径 (map_Kd path)\r\n */\r\n private parseDiffuseTexture(parts: string[]): void {\r\n if (!this.currentMaterial) {\r\n return;\r\n }\r\n\r\n if (parts.length < 2) {\r\n throw new Error('漫反射纹理路径不完整');\r\n }\r\n\r\n // 纹理路径可能包含空格,所以需要合并剩余部分\r\n const texturePath = parts.slice(1).join(' ');\r\n this.currentMaterial.diffuseTexture = texturePath;\r\n }\r\n\r\n /**\r\n * 解析透明度 (d factor)\r\n * d = 1.0 表示完全不透明,d = 0.0 表示完全透明\r\n */\r\n private parseOpacity(parts: string[]): void {\r\n if (!this.currentMaterial) {\r\n return;\r\n }\r\n\r\n if (parts.length < 2) {\r\n throw new Error('透明度数据不完整');\r\n }\r\n\r\n const opacity = parseFloat(parts[1]);\r\n\r\n if (isNaN(opacity)) {\r\n throw new Error('无效的透明度值');\r\n }\r\n\r\n this.currentMaterial.opacity = opacity;\r\n }\r\n\r\n /**\r\n * 解析透明度 (Tr factor)\r\n * Tr = 0.0 表示完全不透明,Tr = 1.0 表示完全透明\r\n * 与 d 相反,所以 opacity = 1 - Tr\r\n */\r\n private parseTransparency(parts: string[]): void {\r\n if (!this.currentMaterial) {\r\n return;\r\n }\r\n\r\n if (parts.length < 2) {\r\n throw new Error('透明度数据不完整');\r\n }\r\n\r\n const transparency = parseFloat(parts[1]);\r\n\r\n if (isNaN(transparency)) {\r\n throw new Error('无效的透明度值');\r\n }\r\n\r\n // Tr 是透明度,需要转换为不透明度\r\n this.currentMaterial.opacity = 1 - transparency;\r\n }\r\n\r\n /**\r\n * 创建默认材质\r\n */\r\n private createDefaultMaterial(name: string): ParsedMaterial {\r\n return {\r\n name,\r\n diffuseColor: [1, 1, 1], // 默认白色\r\n diffuseTexture: null,\r\n opacity: 1, // 默认完全不透明\r\n };\r\n }\r\n\r\n /**\r\n * 完成当前材质并添加到映射\r\n */\r\n private finalizeCurrentMaterial(): void {\r\n if (this.currentMaterial) {\r\n this.materials.set(this.currentMaterial.name, this.currentMaterial);\r\n }\r\n this.currentMaterial = null;\r\n }\r\n}\r\n","/**\r\n * OBJLoader - OBJ 文件加载器\r\n * 解析 OBJ 文件并生成 Mesh[],支持 MTL 材质\r\n */\r\n\r\nimport { Mesh, MeshBoundingBox } from '../mesh/Mesh';\r\nimport { MaterialData, LoadedMesh } from './GLBLoader';\r\nimport { OBJParser, ParsedOBJData, ParsedObject } from './OBJParser';\r\nimport { MTLParser, ParsedMaterial } from './MTLParser';\r\n\r\n/**\r\n * OBJLoader - OBJ 文件加载器\r\n * 解析 OBJ 文件并生成 LoadedMesh[]\r\n */\r\nexport class OBJLoader {\r\n private device: GPUDevice;\r\n private textureCache: Map<string, GPUTexture> = new Map();\r\n\r\n constructor(device: GPUDevice) {\r\n this.device = device;\r\n }\r\n\r\n /**\r\n * 加载 OBJ 文件\r\n * @param url OBJ 文件 URL\r\n * @returns 加载后的网格数组\r\n * \r\n * Requirements: 2.1, 2.2, 2.3\r\n */\r\n async load(url: string): Promise<LoadedMesh[]> {\r\n // Requirement 2.1: 获取文件内容\r\n const response = await fetch(url);\r\n \r\n // Requirement 2.2: 处理获取失败\r\n if (!response.ok) {\r\n throw new Error(`无法加载 OBJ 文件: ${url} (HTTP ${response.status})`);\r\n }\r\n\r\n const text = await response.text();\r\n \r\n // 计算基础 URL(用于加载 MTL 和纹理)\r\n const baseUrl = url.substring(0, url.lastIndexOf('/') + 1);\r\n \r\n // Requirement 2.3: 解析并返回 LoadedMesh 数组\r\n return this.parseFromText(text, baseUrl);\r\n }\r\n\r\n /**\r\n * 从文本解析 OBJ(用于直接传入内容)\r\n * @param text OBJ 文本内容\r\n * @param baseUrl 基础 URL(用于加载 MTL 和纹理)\r\n * @returns 加载后的网格数组\r\n * \r\n * Requirements: 2.1, 2.3\r\n */\r\n async parseFromText(text: string, baseUrl?: string): Promise<LoadedMesh[]> {\r\n // 解析 OBJ 文本\r\n const parser = new OBJParser();\r\n const parsedData = parser.parse(text);\r\n\r\n // 提取 MTL 文件引用\r\n const mtlFile = this.extractMTLReference(text);\r\n \r\n // 加载材质(如果有)\r\n let materials: Map<string, ParsedMaterial> = new Map();\r\n if (mtlFile && baseUrl) {\r\n materials = await this.loadMTL(baseUrl + mtlFile);\r\n }\r\n\r\n // 转换为 LoadedMesh 数组\r\n return this.createMeshes(parsedData, materials, baseUrl);\r\n }\r\n\r\n /**\r\n * 提取 MTL 文件引用\r\n */\r\n private extractMTLReference(text: string): string | null {\r\n const lines = text.split(/\\r?\\n/);\r\n for (const line of lines) {\r\n const trimmed = line.trim();\r\n if (trimmed.startsWith('mtllib ')) {\r\n return trimmed.substring(7).trim();\r\n }\r\n }\r\n return null;\r\n }\r\n\r\n /**\r\n * 加载 MTL 材质文件\r\n * Requirement 4.1, 4.4: 加载 MTL 文件,失败时使用默认材质\r\n */\r\n private async loadMTL(url: string): Promise<Map<string, ParsedMaterial>> {\r\n try {\r\n const response = await fetch(url);\r\n if (!response.ok) {\r\n return new Map();\r\n }\r\n const text = await response.text();\r\n const mtlParser = new MTLParser();\r\n return mtlParser.parse(text);\r\n } catch (e) {\r\n return new Map();\r\n }\r\n }\r\n\r\n /**\r\n * 创建 LoadedMesh 数组\r\n */\r\n private async createMeshes(\r\n parsedData: ParsedOBJData,\r\n materials: Map<string, ParsedMaterial>,\r\n baseUrl?: string\r\n ): Promise<LoadedMesh[]> {\r\n const loadedMeshes: LoadedMesh[] = [];\r\n\r\n for (const obj of parsedData.objects) {\r\n // 跳过空对象\r\n if (obj.indices.length === 0) {\r\n continue;\r\n }\r\n\r\n const loadedMesh = await this.createMesh(obj, materials, baseUrl);\r\n if (loadedMesh) {\r\n loadedMeshes.push(loadedMesh);\r\n }\r\n }\r\n\r\n return loadedMeshes;\r\n }\r\n\r\n /**\r\n * 创建单个 Mesh\r\n */\r\n private async createMesh(\r\n obj: ParsedObject,\r\n materials: Map<string, ParsedMaterial>,\r\n baseUrl?: string\r\n ): Promise<LoadedMesh | null> {\r\n const hasNormals = obj.normals.length > 0;\r\n const hasUVs = obj.uvs.length > 0;\r\n const vertexCount = obj.positions.length / 3;\r\n\r\n // 如果没有法线,生成平面法线\r\n let normals = obj.normals;\r\n if (!hasNormals) {\r\n normals = this.generateFlatNormals(obj.positions, obj.indices);\r\n }\r\n\r\n // 创建交错顶点数据: position(3) + normal(3) + uv(2)\r\n const stride = hasUVs ? 8 : 6; // floats per vertex\r\n const vertexData = new Float32Array(vertexCount * stride);\r\n\r\n for (let i = 0; i < vertexCount; i++) {\r\n const baseIdx = i * stride;\r\n // Position\r\n vertexData[baseIdx + 0] = obj.positions[i * 3 + 0];\r\n vertexData[baseIdx + 1] = obj.positions[i * 3 + 1];\r\n vertexData[baseIdx + 2] = obj.positions[i * 3 + 2];\r\n // Normal\r\n vertexData[baseIdx + 3] = normals[i * 3 + 0];\r\n vertexData[baseIdx + 4] = normals[i * 3 + 1];\r\n vertexData[baseIdx + 5] = normals[i * 3 + 2];\r\n // UV (if present)\r\n if (hasUVs) {\r\n vertexData[baseIdx + 6] = obj.uvs[i * 2 + 0];\r\n vertexData[baseIdx + 7] = obj.uvs[i * 2 + 1];\r\n }\r\n }\r\n\r\n // 创建顶点缓冲区\r\n const vertexBuffer = this.device.createBuffer({\r\n size: vertexData.byteLength,\r\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\r\n });\r\n this.device.queue.writeBuffer(vertexBuffer, 0, vertexData);\r\n\r\n // 创建索引缓冲区\r\n // Requirement 3.4: 根据顶点数选择索引格式\r\n const indexCount = obj.indices.length;\r\n let indexBuffer: GPUBuffer;\r\n let indexFormat: 'uint16' | 'uint32' = 'uint16';\r\n\r\n if (vertexCount > 65535) {\r\n indexFormat = 'uint32';\r\n const indexData = new Uint32Array(obj.indices);\r\n indexBuffer = this.device.createBuffer({\r\n size: indexData.byteLength,\r\n usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,\r\n });\r\n this.device.queue.writeBuffer(indexBuffer, 0, indexData);\r\n } else {\r\n const indexData = new Uint16Array(obj.indices);\r\n indexBuffer = this.device.createBuffer({\r\n size: indexData.byteLength,\r\n usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,\r\n });\r\n this.device.queue.writeBuffer(indexBuffer, 0, indexData);\r\n }\r\n\r\n // Requirement 3.5: 计算 bounding box\r\n const boundingBox = this.computeBoundingBox(obj.positions);\r\n\r\n // 创建 Mesh\r\n const mesh = new Mesh(vertexBuffer, vertexCount, indexBuffer, indexCount, boundingBox);\r\n mesh.hasUV = hasUVs;\r\n mesh.indexFormat = indexFormat;\r\n\r\n // 创建材质\r\n const material = await this.createMaterial(obj.materialName, materials, baseUrl);\r\n\r\n return { mesh, material };\r\n }\r\n\r\n /**\r\n * 生成平面法线(当 OBJ 缺少法线数据时)\r\n * Requirement 3.2: 从面几何计算平面法线\r\n */\r\n private generateFlatNormals(positions: number[], indices: number[]): number[] {\r\n const normals = new Array(positions.length).fill(0);\r\n\r\n // 遍历每个三角形\r\n for (let i = 0; i < indices.length; i += 3) {\r\n const i0 = indices[i];\r\n const i1 = indices[i + 1];\r\n const i2 = indices[i + 2];\r\n\r\n // 获取三角形顶点\r\n const v0 = [positions[i0 * 3], positions[i0 * 3 + 1], positions[i0 * 3 + 2]];\r\n const v1 = [positions[i1 * 3], positions[i1 * 3 + 1], positions[i1 * 3 + 2]];\r\n const v2 = [positions[i2 * 3], positions[i2 * 3 + 1], positions[i2 * 3 + 2]];\r\n\r\n // 计算边向量\r\n const edge1 = [v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]];\r\n const edge2 = [v2[0] - v0[0], v2[1] - v0[1], v2[2] - v0[2]];\r\n\r\n // 计算叉积(面法线)\r\n const nx = edge1[1] * edge2[2] - edge1[2] * edge2[1];\r\n const ny = edge1[2] * edge2[0] - edge1[0] * edge2[2];\r\n const nz = edge1[0] * edge2[1] - edge1[1] * edge2[0];\r\n\r\n // 归一化\r\n const len = Math.sqrt(nx * nx + ny * ny + nz * nz);\r\n const normalizedNx = len > 0 ? nx / len : 0;\r\n const normalizedNy = len > 0 ? ny / len : 1;\r\n const normalizedNz = len > 0 ? nz / len : 0;\r\n\r\n // 为三角形的每个顶点设置相同的法线(平面着色)\r\n for (const idx of [i0, i1, i2]) {\r\n normals[idx * 3 + 0] = normalizedNx;\r\n normals[idx * 3 + 1] = normalizedNy;\r\n normals[idx * 3 + 2] = normalizedNz;\r\n }\r\n }\r\n\r\n return normals;\r\n }\r\n\r\n /**\r\n * 计算顶点数据的 bounding box\r\n * Requirement 3.5: 计算并存储 bounding box 信息\r\n */\r\n private computeBoundingBox(positions: number[]): MeshBoundingBox {\r\n if (positions.length < 3) {\r\n return {\r\n min: [0, 0, 0],\r\n max: [0, 0, 0],\r\n center: [0, 0, 0],\r\n radius: 0,\r\n };\r\n }\r\n\r\n // 初始化为第一个点\r\n const min: [number, number, number] = [positions[0], positions[1], positions[2]];\r\n const max: [number, number, number] = [positions[0], positions[1], positions[2]];\r\n\r\n // 遍历所有顶点\r\n for (let i = 3; i < positions.length; i += 3) {\r\n const x = positions[i];\r\n const y = positions[i + 1];\r\n const z = positions[i + 2];\r\n\r\n min[0] = Math.min(min[0], x);\r\n min[1] = Math.min(min[1], y);\r\n min[2] = Math.min(min[2], z);\r\n max[0] = Math.max(max[0], x);\r\n max[1] = Math.max(max[1], y);\r\n max[2] = Math.max(max[2], z);\r\n }\r\n\r\n // 计算中心点\r\n const center: [number, number, number] = [\r\n (min[0] + max[0]) / 2,\r\n (min[1] + max[1]) / 2,\r\n (min[2] + max[2]) / 2,\r\n ];\r\n\r\n // 计算 bounding sphere 半径\r\n const dx = max[0] - min[0];\r\n const dy = max[1] - min[1];\r\n const dz = max[2] - min[2];\r\n const radius = Math.sqrt(dx * dx + dy * dy + dz * dz) / 2;\r\n\r\n return { min, max, center, radius };\r\n }\r\n\r\n /**\r\n * 创建材质数据\r\n * Requirement 4.3: 将材质关联到网格\r\n */\r\n private async createMaterial(\r\n materialName: string | null,\r\n materials: Map<string, ParsedMaterial>,\r\n baseUrl?: string\r\n ): Promise<MaterialData> {\r\n // 默认材质 - OBJ 模型默认双面渲染,因为很多 OBJ 模型没有正确的面朝向\r\n const defaultMaterial: MaterialData = {\r\n baseColorFactor: [1, 1, 1, 1],\r\n baseColorTexture: null,\r\n metallicFactor: 0,\r\n roughnessFactor: 0.5,\r\n doubleSided: true,\r\n };\r\n\r\n if (!materialName || !materials.has(materialName)) {\r\n return defaultMaterial;\r\n }\r\n\r\n const parsedMaterial = materials.get(materialName)!;\r\n \r\n // 转换为 MaterialData - OBJ 模型默认双面渲染\r\n const material: MaterialData = {\r\n baseColorFactor: [\r\n parsedMaterial.diffuseColor[0],\r\n parsedMaterial.diffuseColor[1],\r\n parsedMaterial.diffuseColor[2],\r\n parsedMaterial.opacity,\r\n ],\r\n baseColorTexture: null,\r\n metallicFactor: 0,\r\n roughnessFactor: 0.5,\r\n doubleSided: true,\r\n };\r\n\r\n // 加载漫反射纹理\r\n if (parsedMaterial.diffuseTexture && baseUrl) {\r\n material.baseColorTexture = await this.loadTexture(baseUrl + parsedMaterial.diffuseTexture);\r\n }\r\n\r\n return material;\r\n }\r\n\r\n /**\r\n * 加载纹理\r\n * Requirement 4.5, 6.3: 加载纹理,失败时使用 null\r\n */\r\n private async loadTexture(url: string): Promise<GPUTexture | null> {\r\n // 检查缓存\r\n if (this.textureCache.has(url)) {\r\n return this.textureCache.get(url)!;\r\n }\r\n\r\n try {\r\n const response = await fetch(url);\r\n if (!response.ok) {\r\n return null;\r\n }\r\n\r\n const blob = await response.blob();\r\n const imageBitmap = await createImageBitmap(blob);\r\n\r\n // 创建 GPU 纹理\r\n const gpuTexture = this.device.createTexture({\r\n size: [imageBitmap.width, imageBitmap.height, 1],\r\n format: 'rgba8unorm',\r\n usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,\r\n });\r\n\r\n this.device.queue.copyExternalImageToTexture(\r\n { source: imageBitmap },\r\n { texture: gpuTexture },\r\n [imageBitmap.width, imageBitmap.height]\r\n );\r\n\r\n // 缓存纹理\r\n this.textureCache.set(url, gpuTexture);\r\n\r\n return gpuTexture;\r\n } catch (error) {\r\n return null;\r\n }\r\n }\r\n}\r\n","/**\r\n * PLYLoader - 加载 3D Gaussian Splatting 的 PLY 文件\r\n * 支持 binary_little_endian 和 binary_big_endian 格式\r\n * 支持多种数据类型: float, double, int, uint, char, uchar, short, ushort\r\n */\r\n\r\n/**\r\n * CPU 端 Splat 数据结构\r\n */\r\nexport type SplatCPU = {\r\n mean: [number, number, number];\r\n scale: [number, number, number];\r\n rotation: [number, number, number, number]; // 四元数顺序: [w, x, y, z]\r\n colorDC: [number, number, number];\r\n opacity: number;\r\n shRest?: Float32Array; // 完整 SH 系数: L1(9) + L2(15) + L3(21) = 45\r\n};\r\n\r\n/**\r\n * PLY 数据类型到字节大小的映射\r\n */\r\nconst TYPE_SIZES: Record<string, number> = {\r\n char: 1,\r\n uchar: 1,\r\n int8: 1,\r\n uint8: 1,\r\n short: 2,\r\n ushort: 2,\r\n int16: 2,\r\n uint16: 2,\r\n int: 4,\r\n uint: 4,\r\n int32: 4,\r\n uint32: 4,\r\n float: 4,\r\n float32: 4,\r\n double: 8,\r\n float64: 8,\r\n};\r\n\r\n/**\r\n * PLY 文件格式类型\r\n */\r\ntype PLYFormat = \"binary_little_endian\" | \"binary_big_endian\" | \"ascii\";\r\n\r\n/**\r\n * PLY 属性信息\r\n */\r\ntype PropertyInfo = {\r\n name: string;\r\n type: string;\r\n byteOffset: number;\r\n byteSize: number;\r\n};\r\n\r\n/**\r\n * 解析 PLY header\r\n */\r\nfunction parseHeader(headerText: string): {\r\n vertexCount: number;\r\n properties: PropertyInfo[];\r\n stride: number;\r\n format: PLYFormat;\r\n} {\r\n const lines = headerText.split(\"\\n\");\r\n let vertexCount = 0;\r\n let format: PLYFormat = \"binary_little_endian\";\r\n const properties: PropertyInfo[] = [];\r\n let currentOffset = 0;\r\n let inVertexElement = false;\r\n\r\n for (const line of lines) {\r\n const trimmed = line.trim();\r\n\r\n // 解析格式\r\n if (trimmed.startsWith(\"format \")) {\r\n const parts = trimmed.split(/\\s+/);\r\n const formatStr = parts[1];\r\n if (formatStr === \"ascii\") {\r\n format = \"ascii\";\r\n } else if (formatStr === \"binary_big_endian\") {\r\n format = \"binary_big_endian\";\r\n } else if (formatStr === \"binary_little_endian\") {\r\n format = \"binary_little_endian\";\r\n } else {\r\n throw new Error(`不支持的 PLY 格式: ${formatStr}`);\r\n }\r\n }\r\n\r\n // 解析 element vertex N\r\n if (trimmed.startsWith(\"element vertex\")) {\r\n const parts = trimmed.split(/\\s+/);\r\n vertexCount = parseInt(parts[2], 10);\r\n inVertexElement = true;\r\n } else if (trimmed.startsWith(\"element \")) {\r\n // 其他 element 类型,停止收集属性\r\n inVertexElement = false;\r\n }\r\n\r\n // 解析 property <type> <name>(只收集 vertex element 的属性)\r\n if (inVertexElement && trimmed.startsWith(\"property\")) {\r\n const parts = trimmed.split(/\\s+/);\r\n // 跳过 list 类型属性\r\n if (parts[1] === \"list\") {\r\n continue;\r\n }\r\n const type = parts[1];\r\n const name = parts[2];\r\n const byteSize = TYPE_SIZES[type];\r\n\r\n if (byteSize === undefined) {\r\n throw new Error(`不支持的 PLY 属性类型: ${type}`);\r\n }\r\n\r\n properties.push({\r\n name,\r\n type,\r\n byteOffset: currentOffset,\r\n byteSize,\r\n });\r\n\r\n currentOffset += byteSize;\r\n }\r\n }\r\n\r\n return {\r\n vertexCount,\r\n properties,\r\n stride: currentOffset,\r\n format,\r\n };\r\n}\r\n\r\n/**\r\n * 验证 PLY 文件魔数\r\n */\r\nfunction validatePLYMagic(buffer: ArrayBuffer): void {\r\n const bytes = new Uint8Array(buffer, 0, Math.min(buffer.byteLength, 10));\r\n const decoder = new TextDecoder(\"ascii\");\r\n const header = decoder.decode(bytes);\r\n\r\n if (!header.startsWith(\"ply\")) {\r\n throw new Error(\"无效的 PLY 文件: 缺少 'ply' 魔数标识\");\r\n }\r\n}\r\n\r\n/**\r\n * 从 ArrayBuffer 中提取 header 文本和数据起始位置\r\n */\r\nfunction extractHeader(buffer: ArrayBuffer): {\r\n headerText: string;\r\n dataOffset: number;\r\n} {\r\n // 首先验证魔数\r\n validatePLYMagic(buffer);\r\n\r\n const bytes = new Uint8Array(buffer);\r\n const decoder = new TextDecoder(\"ascii\");\r\n\r\n // 查找 \"end_header\\n\" 或 \"end_header\\r\\n\" 标记\r\n const maxHeaderSize = Math.min(bytes.length, 10000); // header 不应超过 10KB\r\n const headerBytes = bytes.slice(0, maxHeaderSize);\r\n const headerText = decoder.decode(headerBytes);\r\n\r\n // 支持 Unix (\\n) 和 Windows (\\r\\n) 换行符\r\n let endIndex = headerText.indexOf(\"end_header\\n\");\r\n let markerLength = \"end_header\\n\".length;\r\n\r\n if (endIndex === -1) {\r\n endIndex = headerText.indexOf(\"end_header\\r\\n\");\r\n markerLength = \"end_header\\r\\n\".length;\r\n }\r\n\r\n if (endIndex === -1) {\r\n throw new Error(\"无法找到 PLY header 结束标记 'end_header'\");\r\n }\r\n\r\n const dataOffset = endIndex + markerLength;\r\n return {\r\n headerText: headerText.substring(0, endIndex),\r\n dataOffset,\r\n };\r\n}\r\n\r\n/**\r\n * 构建属性名到属性信息的映射\r\n */\r\nfunction buildPropertyMap(\r\n properties: PropertyInfo[]\r\n): Map<string, PropertyInfo> {\r\n const map = new Map<string, PropertyInfo>();\r\n for (const prop of properties) {\r\n map.set(prop.name, prop);\r\n }\r\n return map;\r\n}\r\n\r\n/**\r\n * 从 DataView 读取属性值(支持多种数据类型)\r\n */\r\nfunction readProperty(\r\n dataView: DataView,\r\n baseOffset: number,\r\n prop: PropertyInfo | undefined,\r\n littleEndian: boolean\r\n): number {\r\n if (prop === undefined) {\r\n return 0;\r\n }\r\n\r\n const offset = baseOffset + prop.byteOffset;\r\n\r\n switch (prop.type) {\r\n case \"float\":\r\n case \"float32\":\r\n return dataView.getFloat32(offset, littleEndian);\r\n case \"double\":\r\n case \"float64\":\r\n return dataView.getFloat64(offset, littleEndian);\r\n case \"int\":\r\n case \"int32\":\r\n return dataView.getInt32(offset, littleEndian);\r\n case \"uint\":\r\n case \"uint32\":\r\n return dataView.getUint32(offset, littleEndian);\r\n case \"short\":\r\n case \"int16\":\r\n return dataView.getInt16(offset, littleEndian);\r\n case \"ushort\":\r\n case \"uint16\":\r\n return dataView.getUint16(offset, littleEndian);\r\n case \"char\":\r\n case \"int8\":\r\n return dataView.getInt8(offset);\r\n case \"uchar\":\r\n case \"uint8\":\r\n return dataView.getUint8(offset);\r\n default:\r\n return dataView.getFloat32(offset, littleEndian);\r\n }\r\n}\r\n\r\n/**\r\n * Sigmoid 函数,用于将 opacity 从原始值转换为 [0, 1]\r\n */\r\nfunction sigmoid(x: number): number {\r\n return 1 / (1 + Math.exp(-x));\r\n}\r\n\r\n/**\r\n * 加载并解析 PLY 文件\r\n * @param url PLY 文件的 URL\r\n * @returns SplatCPU 数组\r\n */\r\nexport async function loadPLY(url: string): Promise<SplatCPU[]> {\r\n // 获取文件\r\n const response = await fetch(url);\r\n if (!response.ok) {\r\n throw new Error(`无法加载 PLY 文件: ${url}`);\r\n }\r\n const buffer = await response.arrayBuffer();\r\n\r\n // 解析 header\r\n const { headerText, dataOffset } = extractHeader(buffer);\r\n const { vertexCount, properties, stride, format } = parseHeader(headerText);\r\n\r\n // 验证格式\r\n if (format === \"ascii\") {\r\n throw new Error(\"不支持 ASCII 格式的 PLY 文件,请使用 binary_little_endian 或 binary_big_endian 格式\");\r\n }\r\n\r\n const littleEndian = format === \"binary_little_endian\";\r\n\r\n // 构建属性映射\r\n const propMap = buildPropertyMap(properties);\r\n\r\n // 获取各属性的信息\r\n const props = {\r\n x: propMap.get(\"x\"),\r\n y: propMap.get(\"y\"),\r\n z: propMap.get(\"z\"),\r\n scale_0: propMap.get(\"scale_0\"),\r\n scale_1: propMap.get(\"scale_1\"),\r\n scale_2: propMap.get(\"scale_2\"),\r\n rot_0: propMap.get(\"rot_0\"),\r\n rot_1: propMap.get(\"rot_1\"),\r\n rot_2: propMap.get(\"rot_2\"),\r\n rot_3: propMap.get(\"rot_3\"),\r\n f_dc_0: propMap.get(\"f_dc_0\"),\r\n f_dc_1: propMap.get(\"f_dc_1\"),\r\n f_dc_2: propMap.get(\"f_dc_2\"),\r\n opacity: propMap.get(\"opacity\"),\r\n };\r\n\r\n // 收集 f_rest_* 字段并按索引排序(用于 SH 系数)\r\n const shRestProps = properties\r\n .filter((p) => p.name.startsWith(\"f_rest_\"))\r\n .sort((a, b) => {\r\n const idxA = parseInt(a.name.replace(\"f_rest_\", \"\"), 10);\r\n const idxB = parseInt(b.name.replace(\"f_rest_\", \"\"), 10);\r\n return idxA - idxB;\r\n });\r\n\r\n // 打印前几个 f_rest_* 属性的名称,验证排序是否正确\r\n if (shRestProps.length > 0) {\r\n // 属性顺序验证(静默)\r\n }\r\n\r\n // 创建 DataView 用于读取二进制数据\r\n const dataView = new DataView(buffer, dataOffset);\r\n\r\n // 预分配共享的 SH 系数数组(优化内存)\r\n const shRestBuffer = new Float32Array(vertexCount * 45);\r\n\r\n // 解析每个 vertex\r\n const splats: SplatCPU[] = new Array(vertexCount);\r\n const SH_C0 = 0.28209479177387814;\r\n\r\n for (let i = 0; i < vertexCount; i++) {\r\n const base = i * stride;\r\n\r\n // 读取位置\r\n const x = readProperty(dataView, base, props.x, littleEndian);\r\n const y = readProperty(dataView, base, props.y, littleEndian);\r\n const z = readProperty(dataView, base, props.z, littleEndian);\r\n\r\n // 读取缩放 (exp 转换,因为 3DGS 存储的是 log scale)\r\n const scale_0 = Math.exp(readProperty(dataView, base, props.scale_0, littleEndian));\r\n const scale_1 = Math.exp(readProperty(dataView, base, props.scale_1, littleEndian));\r\n const scale_2 = Math.exp(readProperty(dataView, base, props.scale_2, littleEndian));\r\n\r\n // 读取旋转四元数\r\n const rot_0 = readProperty(dataView, base, props.rot_0, littleEndian);\r\n const rot_1 = readProperty(dataView, base, props.rot_1, littleEndian);\r\n const rot_2 = readProperty(dataView, base, props.rot_2, littleEndian);\r\n const rot_3 = readProperty(dataView, base, props.rot_3, littleEndian);\r\n\r\n // 归一化四元数\r\n const qlen = Math.sqrt(\r\n rot_0 * rot_0 + rot_1 * rot_1 + rot_2 * rot_2 + rot_3 * rot_3\r\n );\r\n const qnorm = qlen > 0 ? 1 / qlen : 1;\r\n\r\n // 读取颜色 DC 系数 (SH0)\r\n // 注意:不要在这里 clamp,因为 SH 贡献可能是负数\r\n // 最终颜色会在 shader 中 clamp\r\n const f_dc_0 = readProperty(dataView, base, props.f_dc_0, littleEndian);\r\n const f_dc_1 = readProperty(dataView, base, props.f_dc_1, littleEndian);\r\n const f_dc_2 = readProperty(dataView, base, props.f_dc_2, littleEndian);\r\n\r\n const colorR = 0.5 + SH_C0 * f_dc_0;\r\n const colorG = 0.5 + SH_C0 * f_dc_1;\r\n const colorB = 0.5 + SH_C0 * f_dc_2;\r\n\r\n // 读取 opacity (sigmoid 转换)\r\n const rawOpacity = readProperty(dataView, base, props.opacity, littleEndian);\r\n const opacity = sigmoid(rawOpacity);\r\n\r\n // 读取完整 SH 系数到共享 buffer\r\n const shOffset = i * 45;\r\n const shRest = shRestBuffer.subarray(shOffset, shOffset + 45);\r\n\r\n // PLY 文件中 f_rest_* 的顺序是 channel-first (参考 visionary):\r\n // [R0..R14, G0..G14, B0..B14] - 每通道 15 个系数\r\n // \r\n // 我们转换为 interleaved 格式 (与 visionary 一致):\r\n // [R0,G0,B0, R1,G1,B1, ...] - 每个基函数的 RGB 连续存储\r\n //\r\n // 这样 shader 中可以用 sh_coef(idx) 返回 vec3(R,G,B)\r\n //\r\n // 注意:shRestProps 的数量可能是 45 (完整 SH) 或更少\r\n // 实际 PLY 文件的 f_rest_* 数量 = (shDegree+1)^2 - 1 个系数 × 3 通道\r\n // 例如 shDegree=3 时: (3+1)^2 - 1 = 15 个系数/通道,共 45 个 f_rest_*\r\n\r\n const totalRestCount = shRestProps.length;\r\n const perChannel = Math.floor(totalRestCount / 3); // 每通道的系数数量\r\n \r\n for (let coefIdx = 0; coefIdx < perChannel; coefIdx++) {\r\n // PLY 中: R 在 [0..perChannel-1], G 在 [perChannel..2*perChannel-1], B 在 [2*perChannel..3*perChannel-1]\r\n const srcR = coefIdx;\r\n const srcG = perChannel + coefIdx;\r\n const srcB = 2 * perChannel + coefIdx;\r\n \r\n // 目标: interleaved [R0,G0,B0, R1,G1,B1, ...]\r\n const dstBase = coefIdx * 3;\r\n \r\n shRest[dstBase + 0] = srcR < shRestProps.length ? readProperty(dataView, base, shRestProps[srcR], littleEndian) : 0;\r\n shRest[dstBase + 1] = srcG < shRestProps.length ? readProperty(dataView, base, shRestProps[srcG], littleEndian) : 0;\r\n shRest[dstBase + 2] = srcB < shRestProps.length ? readProperty(dataView, base, shRestProps[srcB], littleEndian) : 0;\r\n }\r\n\r\n // 打印第一个点的 SH 系数用于调试\r\n if (i === 0) {\r\n // 调试信息(静默)\r\n }\r\n\r\n splats[i] = {\r\n mean: [x, y, z],\r\n scale: [scale_0, scale_1, scale_2],\r\n rotation: [\r\n rot_0 * qnorm,\r\n rot_1 * qnorm,\r\n rot_2 * qnorm,\r\n rot_3 * qnorm,\r\n ],\r\n colorDC: [colorR, colorG, colorB],\r\n opacity,\r\n shRest,\r\n };\r\n }\r\n\r\n return splats;\r\n}\r\n","/**\r\n * PLYLoaderMobile - 移动端优化的 PLY 加载器\r\n * \r\n * 优化点:\r\n * 1. 直接输出 GPU buffer 格式,避免中间对象\r\n * 2. 流式降采样,减少内存峰值\r\n * 3. 可选跳过 SH 系数(移动端 L0 模式)\r\n * 4. 使用 TypedArray 而非对象数组\r\n * 5. 支持多种 PLY 数据类型\r\n * 6. 确定性采样(基于文件内容的种子)\r\n */\r\n\r\n/**\r\n * PLY 数据类型到字节大小的映射\r\n */\r\nconst TYPE_SIZES: Record<string, number> = {\r\n char: 1,\r\n uchar: 1,\r\n int8: 1,\r\n uint8: 1,\r\n short: 2,\r\n ushort: 2,\r\n int16: 2,\r\n uint16: 2,\r\n int: 4,\r\n uint: 4,\r\n int32: 4,\r\n uint32: 4,\r\n float: 4,\r\n float32: 4,\r\n double: 8,\r\n float64: 8,\r\n};\r\n\r\n/**\r\n * PLY 文件格式类型\r\n */\r\ntype PLYFormat = \"binary_little_endian\" | \"binary_big_endian\" | \"ascii\";\r\n\r\n/**\r\n * 移动端加载配置\r\n */\r\nexport interface MobileLoadOptions {\r\n /** 最大 splat 数量,超过则降采样 */\r\n maxSplats?: number;\r\n /** 是否加载 SH 系数(false 时只加载 DC 颜色) */\r\n loadSH?: boolean;\r\n /** 进度回调 */\r\n onProgress?: (loaded: number, total: number) => void;\r\n /** 随机种子(用于确定性采样,默认使用文件大小作为种子) */\r\n seed?: number;\r\n}\r\n\r\n/**\r\n * 紧凑 Splat 数据(用于移动端)\r\n * 直接返回 Float32Array,而非对象数组\r\n */\r\nexport interface CompactSplatData {\r\n /** splat 数量 */\r\n count: number;\r\n /** 位置数据 Float32Array [x,y,z, x,y,z, ...] */\r\n positions: Float32Array;\r\n /** 缩放数据 Float32Array [sx,sy,sz, sx,sy,sz, ...] */\r\n scales: Float32Array;\r\n /** 旋转四元数 Float32Array [w,x,y,z, w,x,y,z, ...] */\r\n rotations: Float32Array;\r\n /** DC 颜色 Float32Array [r,g,b, r,g,b, ...] */\r\n colors: Float32Array;\r\n /** 不透明度 Float32Array [a, a, ...] */\r\n opacities: Float32Array;\r\n /** SH 系数(可选)Float32Array,每个 splat 45 个系数 */\r\n shCoeffs?: Float32Array;\r\n}\r\n\r\n/**\r\n * PLY 属性信息\r\n */\r\ninterface PropertyInfo {\r\n name: string;\r\n type: string;\r\n byteOffset: number;\r\n byteSize: number;\r\n}\r\n\r\n/**\r\n * 验证 PLY 文件魔数\r\n */\r\nfunction validatePLYMagic(buffer: ArrayBuffer): void {\r\n const bytes = new Uint8Array(buffer, 0, Math.min(buffer.byteLength, 10));\r\n const decoder = new TextDecoder(\"ascii\");\r\n const header = decoder.decode(bytes);\r\n\r\n if (!header.startsWith(\"ply\")) {\r\n throw new Error(\"无效的 PLY 文件: 缺少 'ply' 魔数标识\");\r\n }\r\n}\r\n\r\n/**\r\n * 解析 PLY header\r\n */\r\nfunction parseHeader(headerText: string): {\r\n vertexCount: number;\r\n properties: PropertyInfo[];\r\n stride: number;\r\n format: PLYFormat;\r\n} {\r\n const lines = headerText.split(\"\\n\");\r\n let vertexCount = 0;\r\n let format: PLYFormat = \"binary_little_endian\";\r\n const properties: PropertyInfo[] = [];\r\n let currentOffset = 0;\r\n let inVertexElement = false;\r\n\r\n for (const line of lines) {\r\n const trimmed = line.trim();\r\n\r\n // 解析格式\r\n if (trimmed.startsWith(\"format \")) {\r\n const parts = trimmed.split(/\\s+/);\r\n const formatStr = parts[1];\r\n if (formatStr === \"ascii\") {\r\n format = \"ascii\";\r\n } else if (formatStr === \"binary_big_endian\") {\r\n format = \"binary_big_endian\";\r\n } else if (formatStr === \"binary_little_endian\") {\r\n format = \"binary_little_endian\";\r\n } else {\r\n throw new Error(`不支持的 PLY 格式: ${formatStr}`);\r\n }\r\n }\r\n\r\n // 解析 element vertex N\r\n if (trimmed.startsWith(\"element vertex\")) {\r\n const parts = trimmed.split(/\\s+/);\r\n vertexCount = parseInt(parts[2], 10);\r\n inVertexElement = true;\r\n } else if (trimmed.startsWith(\"element \")) {\r\n inVertexElement = false;\r\n }\r\n\r\n // 解析 property(只收集 vertex element 的属性)\r\n if (inVertexElement && trimmed.startsWith(\"property\")) {\r\n const parts = trimmed.split(/\\s+/);\r\n if (parts[1] === \"list\") {\r\n continue;\r\n }\r\n const type = parts[1];\r\n const name = parts[2];\r\n const byteSize = TYPE_SIZES[type];\r\n\r\n if (byteSize === undefined) {\r\n throw new Error(`不支持的 PLY 属性类型: ${type}`);\r\n }\r\n\r\n properties.push({\r\n name,\r\n type,\r\n byteOffset: currentOffset,\r\n byteSize,\r\n });\r\n\r\n currentOffset += byteSize;\r\n }\r\n }\r\n\r\n return { vertexCount, properties, stride: currentOffset, format };\r\n}\r\n\r\n/**\r\n * 提取 header\r\n */\r\nfunction extractHeader(buffer: ArrayBuffer): {\r\n headerText: string;\r\n dataOffset: number;\r\n} {\r\n // 首先验证魔数\r\n validatePLYMagic(buffer);\r\n\r\n const bytes = new Uint8Array(buffer);\r\n const decoder = new TextDecoder(\"ascii\");\r\n\r\n const maxHeaderSize = Math.min(bytes.length, 10000);\r\n const headerBytes = bytes.slice(0, maxHeaderSize);\r\n const headerText = decoder.decode(headerBytes);\r\n\r\n // 支持 Unix (\\n) 和 Windows (\\r\\n) 换行符\r\n let endIndex = headerText.indexOf(\"end_header\\n\");\r\n let markerLength = \"end_header\\n\".length;\r\n\r\n if (endIndex === -1) {\r\n endIndex = headerText.indexOf(\"end_header\\r\\n\");\r\n markerLength = \"end_header\\r\\n\".length;\r\n }\r\n\r\n if (endIndex === -1) {\r\n throw new Error(\"无法找到 PLY header 结束标记 'end_header'\");\r\n }\r\n\r\n const dataOffset = endIndex + markerLength;\r\n return {\r\n headerText: headerText.substring(0, endIndex),\r\n dataOffset,\r\n };\r\n}\r\n\r\n/**\r\n * Sigmoid 函数\r\n */\r\nfunction sigmoid(x: number): number {\r\n return 1 / (1 + Math.exp(-x));\r\n}\r\n\r\n/**\r\n * 从 DataView 读取属性值(支持多种数据类型)\r\n */\r\nfunction readProperty(\r\n dataView: DataView,\r\n offset: number,\r\n type: string,\r\n littleEndian: boolean\r\n): number {\r\n switch (type) {\r\n case \"float\":\r\n case \"float32\":\r\n return dataView.getFloat32(offset, littleEndian);\r\n case \"double\":\r\n case \"float64\":\r\n return dataView.getFloat64(offset, littleEndian);\r\n case \"int\":\r\n case \"int32\":\r\n return dataView.getInt32(offset, littleEndian);\r\n case \"uint\":\r\n case \"uint32\":\r\n return dataView.getUint32(offset, littleEndian);\r\n case \"short\":\r\n case \"int16\":\r\n return dataView.getInt16(offset, littleEndian);\r\n case \"ushort\":\r\n case \"uint16\":\r\n return dataView.getUint16(offset, littleEndian);\r\n case \"char\":\r\n case \"int8\":\r\n return dataView.getInt8(offset);\r\n case \"uchar\":\r\n case \"uint8\":\r\n return dataView.getUint8(offset);\r\n default:\r\n return dataView.getFloat32(offset, littleEndian);\r\n }\r\n}\r\n\r\n/**\r\n * 简单的确定性伪随机数生成器 (Mulberry32)\r\n * 给定相同的种子,总是产生相同的序列\r\n */\r\nfunction createSeededRandom(seed: number): () => number {\r\n let state = seed >>> 0;\r\n return () => {\r\n state = (state + 0x6d2b79f5) >>> 0;\r\n let t = state;\r\n t = Math.imul(t ^ (t >>> 15), t | 1);\r\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\r\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\r\n };\r\n}\r\n\r\nconst SH_C0 = 0.28209479177387814;\r\n\r\n/**\r\n * 智能采样:基于重要性(opacity * scale)采样\r\n * 使用 Floyd 采样算法 + 确定性种子,保证相同文件产生相同结果\r\n * 时间复杂度 O(n),空间复杂度 O(k),其中 k 是采样数量\r\n */\r\nfunction computeImportanceSampling(\r\n buffer: ArrayBuffer,\r\n dataOffset: number,\r\n stride: number,\r\n totalCount: number,\r\n sampleCount: number,\r\n opacityOffset: number,\r\n opacityType: string,\r\n scale0Offset: number,\r\n scale0Type: string,\r\n scale1Offset: number,\r\n scale1Type: string,\r\n scale2Offset: number,\r\n scale2Type: string,\r\n littleEndian: boolean,\r\n seed: number\r\n): Uint32Array {\r\n const dataView = new DataView(buffer, dataOffset);\r\n const random = createSeededRandom(seed);\r\n\r\n // 计算每个 splat 的重要性分数\r\n const importance = new Float32Array(totalCount);\r\n let totalImportance = 0;\r\n\r\n for (let i = 0; i < totalCount; i++) {\r\n const base = i * stride;\r\n\r\n // 获取 opacity(需要 sigmoid 转换)\r\n const rawOpacity = opacityOffset >= 0\r\n ? readProperty(dataView, base + opacityOffset, opacityType, littleEndian)\r\n : 0;\r\n const opacity = sigmoid(rawOpacity);\r\n\r\n // 获取 scale(需要 exp 转换)\r\n const s0 = scale0Offset >= 0\r\n ? Math.exp(readProperty(dataView, base + scale0Offset, scale0Type, littleEndian))\r\n : 1;\r\n const s1 = scale1Offset >= 0\r\n ? Math.exp(readProperty(dataView, base + scale1Offset, scale1Type, littleEndian))\r\n : 1;\r\n const s2 = scale2Offset >= 0\r\n ? Math.exp(readProperty(dataView, base + scale2Offset, scale2Type, littleEndian))\r\n : 1;\r\n const maxScale = Math.max(s0, s1, s2);\r\n\r\n // 重要性分数:opacity * scale\r\n importance[i] = opacity * maxScale;\r\n totalImportance += importance[i];\r\n }\r\n\r\n // 使用加权随机采样(Reservoir Sampling with Weights)\r\n // 这是一种 O(n) 的算法,比完整排序更高效\r\n const result = new Uint32Array(sampleCount);\r\n const weights = new Float32Array(sampleCount);\r\n\r\n // 初始化:填充前 sampleCount 个元素\r\n for (let i = 0; i < Math.min(sampleCount, totalCount); i++) {\r\n result[i] = i;\r\n // 使用 -log(random) / weight 作为键值(Efraimidis-Spirakis 算法)\r\n weights[i] = importance[i] > 0 ? -Math.log(random()) / importance[i] : Infinity;\r\n }\r\n\r\n // 构建最小堆(用于维护 top-k)\r\n // 简化实现:直接找最大权重的位置\r\n let maxWeightIdx = 0;\r\n let maxWeight = weights[0];\r\n for (let i = 1; i < sampleCount; i++) {\r\n if (weights[i] > maxWeight) {\r\n maxWeight = weights[i];\r\n maxWeightIdx = i;\r\n }\r\n }\r\n\r\n // 处理剩余元素\r\n for (let i = sampleCount; i < totalCount; i++) {\r\n const key = importance[i] > 0 ? -Math.log(random()) / importance[i] : Infinity;\r\n\r\n // 如果当前元素的键值小于堆中最大的键值,替换它\r\n if (key < maxWeight) {\r\n result[maxWeightIdx] = i;\r\n weights[maxWeightIdx] = key;\r\n\r\n // 重新找最大权重\r\n maxWeight = weights[0];\r\n maxWeightIdx = 0;\r\n for (let j = 1; j < sampleCount; j++) {\r\n if (weights[j] > maxWeight) {\r\n maxWeight = weights[j];\r\n maxWeightIdx = j;\r\n }\r\n }\r\n }\r\n }\r\n\r\n // 按原始顺序排列(保持空间局部性,有利于缓存)\r\n result.sort((a, b) => a - b);\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 移动端优化的 PLY 加载器\r\n * 直接输出紧凑格式,避免创建大量中间对象\r\n */\r\nexport async function loadPLYMobile(\r\n url: string,\r\n options: MobileLoadOptions = {}\r\n): Promise<CompactSplatData> {\r\n // 获取文件\r\n const response = await fetch(url);\r\n if (!response.ok) {\r\n throw new Error(`无法加载 PLY 文件: ${url}`);\r\n }\r\n const buffer = await response.arrayBuffer();\r\n\r\n return parsePLYBuffer(buffer, options);\r\n}\r\n\r\n/**\r\n * 解析 PLY ArrayBuffer\r\n * 可以直接传入 ArrayBuffer 进行解析,用于本地文件加载\r\n */\r\nexport async function parsePLYBuffer(\r\n buffer: ArrayBuffer,\r\n options: MobileLoadOptions = {}\r\n): Promise<CompactSplatData> {\r\n const {\r\n maxSplats = 200000,\r\n loadSH = false,\r\n onProgress,\r\n } = options;\r\n\r\n // 使用文件大小作为默认种子(确保相同文件产生相同结果)\r\n const seed = options.seed ?? buffer.byteLength;\r\n\r\n // 解析 header\r\n const { headerText, dataOffset } = extractHeader(buffer);\r\n const { vertexCount, properties, stride, format } = parseHeader(headerText);\r\n\r\n // 验证格式\r\n if (format === \"ascii\") {\r\n throw new Error(\"不支持 ASCII 格式的 PLY 文件,请使用 binary_little_endian 或 binary_big_endian 格式\");\r\n }\r\n\r\n const littleEndian = format === \"binary_little_endian\";\r\n\r\n // 构建属性偏移映射\r\n const propMap = new Map<string, PropertyInfo>();\r\n for (const prop of properties) {\r\n propMap.set(prop.name, prop);\r\n }\r\n\r\n // 获取基本属性信息\r\n const getProp = (name: string) => propMap.get(name);\r\n const getOffset = (name: string) => propMap.get(name)?.byteOffset ?? -1;\r\n const getType = (name: string) => propMap.get(name)?.type ?? \"float\";\r\n\r\n const offsets = {\r\n x: getOffset(\"x\"),\r\n y: getOffset(\"y\"),\r\n z: getOffset(\"z\"),\r\n scale_0: getOffset(\"scale_0\"),\r\n scale_1: getOffset(\"scale_1\"),\r\n scale_2: getOffset(\"scale_2\"),\r\n rot_0: getOffset(\"rot_0\"),\r\n rot_1: getOffset(\"rot_1\"),\r\n rot_2: getOffset(\"rot_2\"),\r\n rot_3: getOffset(\"rot_3\"),\r\n f_dc_0: getOffset(\"f_dc_0\"),\r\n f_dc_1: getOffset(\"f_dc_1\"),\r\n f_dc_2: getOffset(\"f_dc_2\"),\r\n opacity: getOffset(\"opacity\"),\r\n };\r\n\r\n const types = {\r\n x: getType(\"x\"),\r\n y: getType(\"y\"),\r\n z: getType(\"z\"),\r\n scale_0: getType(\"scale_0\"),\r\n scale_1: getType(\"scale_1\"),\r\n scale_2: getType(\"scale_2\"),\r\n rot_0: getType(\"rot_0\"),\r\n rot_1: getType(\"rot_1\"),\r\n rot_2: getType(\"rot_2\"),\r\n rot_3: getType(\"rot_3\"),\r\n f_dc_0: getType(\"f_dc_0\"),\r\n f_dc_1: getType(\"f_dc_1\"),\r\n f_dc_2: getType(\"f_dc_2\"),\r\n opacity: getType(\"opacity\"),\r\n };\r\n\r\n // SH 系数属性(可选)\r\n let shProps: PropertyInfo[] = [];\r\n if (loadSH) {\r\n shProps = properties\r\n .filter((p) => p.name.startsWith(\"f_rest_\"))\r\n .sort((a, b) => {\r\n const idxA = parseInt(a.name.replace(\"f_rest_\", \"\"), 10);\r\n const idxB = parseInt(b.name.replace(\"f_rest_\", \"\"), 10);\r\n return idxA - idxB;\r\n });\r\n }\r\n\r\n // 计算实际加载数量\r\n const needSample = vertexCount > maxSplats;\r\n const actualCount = Math.min(vertexCount, maxSplats);\r\n \r\n // 估算内存使用(纹理压缩模式约 52 bytes/splat)\r\n const estimatedMemoryMB = (actualCount * 52) / (1024 * 1024);\r\n \r\n if (estimatedMemoryMB > 300) {\r\n // 高内存警告(静默处理)\r\n }\r\n \r\n // 如果需要降采样,使用智能采样(基于重要性,确定性种子)\r\n let sampleIndices: Uint32Array | null = null;\r\n if (needSample) {\r\n sampleIndices = computeImportanceSampling(\r\n buffer, dataOffset, stride, vertexCount, actualCount,\r\n offsets.opacity, types.opacity,\r\n offsets.scale_0, types.scale_0,\r\n offsets.scale_1, types.scale_1,\r\n offsets.scale_2, types.scale_2,\r\n littleEndian, seed\r\n );\r\n }\r\n\r\n // 预分配输出数组(一次性分配,避免多次扩容)\r\n const positions = new Float32Array(actualCount * 3);\r\n const scales = new Float32Array(actualCount * 3);\r\n const rotations = new Float32Array(actualCount * 4);\r\n const colors = new Float32Array(actualCount * 3);\r\n const opacities = new Float32Array(actualCount);\r\n const shCoeffs = loadSH ? new Float32Array(actualCount * 45) : undefined;\r\n\r\n // 创建 DataView\r\n const dataView = new DataView(buffer, dataOffset);\r\n\r\n // 流式解析\r\n let outputIdx = 0;\r\n let lastProgress = 0;\r\n\r\n for (let i = 0; i < actualCount; i++) {\r\n // 计算源索引:使用智能采样或直接索引\r\n const srcIdx = sampleIndices ? sampleIndices[i] : i;\r\n const base = srcIdx * stride;\r\n\r\n // 位置\r\n positions[outputIdx * 3 + 0] = offsets.x >= 0 ? readProperty(dataView, base + offsets.x, types.x, littleEndian) : 0;\r\n positions[outputIdx * 3 + 1] = offsets.y >= 0 ? readProperty(dataView, base + offsets.y, types.y, littleEndian) : 0;\r\n positions[outputIdx * 3 + 2] = offsets.z >= 0 ? readProperty(dataView, base + offsets.z, types.z, littleEndian) : 0;\r\n\r\n // 缩放(exp 转换)\r\n scales[outputIdx * 3 + 0] = offsets.scale_0 >= 0 ? Math.exp(readProperty(dataView, base + offsets.scale_0, types.scale_0, littleEndian)) : 1;\r\n scales[outputIdx * 3 + 1] = offsets.scale_1 >= 0 ? Math.exp(readProperty(dataView, base + offsets.scale_1, types.scale_1, littleEndian)) : 1;\r\n scales[outputIdx * 3 + 2] = offsets.scale_2 >= 0 ? Math.exp(readProperty(dataView, base + offsets.scale_2, types.scale_2, littleEndian)) : 1;\r\n\r\n // 旋转四元数(归一化)\r\n const rot_0 = offsets.rot_0 >= 0 ? readProperty(dataView, base + offsets.rot_0, types.rot_0, littleEndian) : 1;\r\n const rot_1 = offsets.rot_1 >= 0 ? readProperty(dataView, base + offsets.rot_1, types.rot_1, littleEndian) : 0;\r\n const rot_2 = offsets.rot_2 >= 0 ? readProperty(dataView, base + offsets.rot_2, types.rot_2, littleEndian) : 0;\r\n const rot_3 = offsets.rot_3 >= 0 ? readProperty(dataView, base + offsets.rot_3, types.rot_3, littleEndian) : 0;\r\n const qlen = Math.sqrt(rot_0 * rot_0 + rot_1 * rot_1 + rot_2 * rot_2 + rot_3 * rot_3);\r\n const qnorm = qlen > 0 ? 1 / qlen : 1;\r\n rotations[outputIdx * 4 + 0] = rot_0 * qnorm;\r\n rotations[outputIdx * 4 + 1] = rot_1 * qnorm;\r\n rotations[outputIdx * 4 + 2] = rot_2 * qnorm;\r\n rotations[outputIdx * 4 + 3] = rot_3 * qnorm;\r\n\r\n // DC 颜色(SH0 -> RGB)\r\n // 注意:不要在这里 clamp,因为 SH 贡献可能是负数\r\n // 最终颜色会在 shader 中 clamp\r\n const f_dc_0 = offsets.f_dc_0 >= 0 ? readProperty(dataView, base + offsets.f_dc_0, types.f_dc_0, littleEndian) : 0;\r\n const f_dc_1 = offsets.f_dc_1 >= 0 ? readProperty(dataView, base + offsets.f_dc_1, types.f_dc_1, littleEndian) : 0;\r\n const f_dc_2 = offsets.f_dc_2 >= 0 ? readProperty(dataView, base + offsets.f_dc_2, types.f_dc_2, littleEndian) : 0;\r\n colors[outputIdx * 3 + 0] = 0.5 + SH_C0 * f_dc_0;\r\n colors[outputIdx * 3 + 1] = 0.5 + SH_C0 * f_dc_1;\r\n colors[outputIdx * 3 + 2] = 0.5 + SH_C0 * f_dc_2;\r\n\r\n // 不透明度(sigmoid)\r\n const rawOpacity = offsets.opacity >= 0 ? readProperty(dataView, base + offsets.opacity, types.opacity, littleEndian) : 0;\r\n opacities[outputIdx] = sigmoid(rawOpacity);\r\n\r\n // SH 系数(可选)\r\n // PLY 文件中 f_rest_* 的顺序是 channel-first:\r\n // [R0..R14, G0..G14, B0..B14] - 每通道 15 个系数\r\n // 我们转换为 interleaved 格式: [R0,G0,B0, R1,G1,B1, ...]\r\n if (shCoeffs && shProps.length > 0) {\r\n const shBase = outputIdx * 45;\r\n const perChannel = Math.floor(shProps.length / 3); // 每通道的系数数量\r\n \r\n for (let coefIdx = 0; coefIdx < perChannel && coefIdx < 15; coefIdx++) {\r\n // PLY 中: R 在 [0..perChannel-1], G 在 [perChannel..2*perChannel-1], B 在 [2*perChannel..3*perChannel-1]\r\n const srcR = coefIdx;\r\n const srcG = perChannel + coefIdx;\r\n const srcB = 2 * perChannel + coefIdx;\r\n \r\n // 目标: interleaved [R0,G0,B0, R1,G1,B1, ...]\r\n const dstBase = coefIdx * 3;\r\n \r\n if (srcR < shProps.length) {\r\n const prop = shProps[srcR];\r\n shCoeffs[shBase + dstBase + 0] = readProperty(dataView, base + prop.byteOffset, prop.type, littleEndian);\r\n }\r\n if (srcG < shProps.length) {\r\n const prop = shProps[srcG];\r\n shCoeffs[shBase + dstBase + 1] = readProperty(dataView, base + prop.byteOffset, prop.type, littleEndian);\r\n }\r\n if (srcB < shProps.length) {\r\n const prop = shProps[srcB];\r\n shCoeffs[shBase + dstBase + 2] = readProperty(dataView, base + prop.byteOffset, prop.type, littleEndian);\r\n }\r\n }\r\n }\r\n\r\n outputIdx++;\r\n\r\n // 进度回调\r\n if (onProgress) {\r\n const progress = Math.floor((i / actualCount) * 100);\r\n if (progress > lastProgress) {\r\n lastProgress = progress;\r\n onProgress(i, actualCount);\r\n }\r\n }\r\n }\r\n\r\n return {\r\n count: outputIdx,\r\n positions,\r\n scales,\r\n rotations,\r\n colors,\r\n opacities,\r\n shCoeffs,\r\n };\r\n}\r\n\r\n/**\r\n * 将 CompactSplatData 转换为 GPU buffer 格式\r\n * 直接输出可以上传到 GPU 的 Float32Array\r\n * \r\n * @param data 紧凑 splat 数据\r\n * @param includeFullSH 是否包含完整 SH 系数(256 字节/splat),否则只包含基本数据(64 字节/splat)\r\n */\r\nexport function compactDataToGPUBuffer(\r\n data: CompactSplatData,\r\n includeFullSH: boolean = false\r\n): Float32Array {\r\n const count = data.count;\r\n\r\n if (includeFullSH) {\r\n // 完整格式:256 字节/splat = 64 floats\r\n const buffer = new Float32Array(count * 64);\r\n\r\n for (let i = 0; i < count; i++) {\r\n const offset = i * 64;\r\n\r\n // mean (vec3) + padding\r\n buffer[offset + 0] = data.positions[i * 3 + 0];\r\n buffer[offset + 1] = data.positions[i * 3 + 1];\r\n buffer[offset + 2] = data.positions[i * 3 + 2];\r\n buffer[offset + 3] = 0;\r\n\r\n // scale (vec3) + padding\r\n buffer[offset + 4] = data.scales[i * 3 + 0];\r\n buffer[offset + 5] = data.scales[i * 3 + 1];\r\n buffer[offset + 6] = data.scales[i * 3 + 2];\r\n buffer[offset + 7] = 0;\r\n\r\n // rotation (vec4)\r\n buffer[offset + 8] = data.rotations[i * 4 + 0];\r\n buffer[offset + 9] = data.rotations[i * 4 + 1];\r\n buffer[offset + 10] = data.rotations[i * 4 + 2];\r\n buffer[offset + 11] = data.rotations[i * 4 + 3];\r\n\r\n // colorDC (vec3) + opacity\r\n buffer[offset + 12] = data.colors[i * 3 + 0];\r\n buffer[offset + 13] = data.colors[i * 3 + 1];\r\n buffer[offset + 14] = data.colors[i * 3 + 2];\r\n buffer[offset + 15] = data.opacities[i];\r\n\r\n // SH 系数\r\n if (data.shCoeffs) {\r\n const shBase = i * 45;\r\n // sh1 (9 floats)\r\n for (let j = 0; j < 9; j++) {\r\n buffer[offset + 16 + j] = data.shCoeffs[shBase + j];\r\n }\r\n // sh2 (15 floats)\r\n for (let j = 0; j < 15; j++) {\r\n buffer[offset + 25 + j] = data.shCoeffs[shBase + 9 + j];\r\n }\r\n // sh3 (21 floats)\r\n for (let j = 0; j < 21; j++) {\r\n buffer[offset + 40 + j] = data.shCoeffs[shBase + 24 + j];\r\n }\r\n }\r\n // padding 已经是 0(Float32Array 默认初始化为 0)\r\n }\r\n\r\n return buffer;\r\n } else {\r\n // 紧凑格式:64 字节/splat = 16 floats(只包含基本渲染数据)\r\n const buffer = new Float32Array(count * 64); // 保持 256 字节对齐,但只填充基本数据\r\n \r\n for (let i = 0; i < count; i++) {\r\n const offset = i * 64;\r\n \r\n // mean (vec3) + padding\r\n buffer[offset + 0] = data.positions[i * 3 + 0];\r\n buffer[offset + 1] = data.positions[i * 3 + 1];\r\n buffer[offset + 2] = data.positions[i * 3 + 2];\r\n buffer[offset + 3] = 0;\r\n \r\n // scale (vec3) + padding\r\n buffer[offset + 4] = data.scales[i * 3 + 0];\r\n buffer[offset + 5] = data.scales[i * 3 + 1];\r\n buffer[offset + 6] = data.scales[i * 3 + 2];\r\n buffer[offset + 7] = 0;\r\n \r\n // rotation (vec4)\r\n buffer[offset + 8] = data.rotations[i * 4 + 0];\r\n buffer[offset + 9] = data.rotations[i * 4 + 1];\r\n buffer[offset + 10] = data.rotations[i * 4 + 2];\r\n buffer[offset + 11] = data.rotations[i * 4 + 3];\r\n \r\n // colorDC (vec3) + opacity\r\n buffer[offset + 12] = data.colors[i * 3 + 0];\r\n buffer[offset + 13] = data.colors[i * 3 + 1];\r\n buffer[offset + 14] = data.colors[i * 3 + 2];\r\n buffer[offset + 15] = data.opacities[i];\r\n \r\n // 其余保持为 0(SH 系数为空)\r\n }\r\n \r\n return buffer;\r\n }\r\n}\r\n","/**\r\n * SplatLoader - 加载 .splat 格式的 3D Gaussian Splatting 文件\r\n * \r\n * .splat 格式是一种紧凑的 3DGS 数据格式:\r\n * - 每个 splat 固定 32 字节,无文件头\r\n * - 数据布局: position(12) + scale(12) + color(3) + opacity(1) + rotation(4)\r\n * - 不包含高阶球谐系数,仅 DC 颜色\r\n * \r\n * 四元数顺序: [w, x, y, z](与 PLYLoader 保持一致)\r\n * \r\n * 参考: supersplat/src/loaders/splat.ts\r\n */\r\n\r\nimport { SplatCPU } from \"./PLYLoader\";\r\n\r\n/** .splat 文件每个 splat 的字节大小 */\r\nconst SPLAT_SIZE = 32;\r\n\r\n/** 最小有效文件大小(至少包含一个 splat) */\r\nconst MIN_FILE_SIZE = SPLAT_SIZE;\r\n\r\n/** 最大合理文件大小(防止内存溢出,约 1000 万 splats) */\r\nconst MAX_FILE_SIZE = SPLAT_SIZE * 10_000_000;\r\n\r\n/**\r\n * 验证 .splat 文件格式\r\n * 由于 .splat 没有魔数,只能通过文件大小和数据合理性来验证\r\n */\r\nfunction validateSplatFile(data: ArrayBufferLike): void {\r\n if (data.byteLength < MIN_FILE_SIZE) {\r\n throw new Error(`无效的 Splat 文件: 文件太小 (${data.byteLength} bytes),至少需要 ${MIN_FILE_SIZE} bytes`);\r\n }\r\n\r\n if (data.byteLength > MAX_FILE_SIZE) {\r\n throw new Error(`Splat 文件过大 (${(data.byteLength / 1024 / 1024).toFixed(1)} MB),超过最大限制`);\r\n }\r\n\r\n if (data.byteLength % SPLAT_SIZE !== 0) {\r\n throw new Error(`无效的 Splat 文件: 文件大小 (${data.byteLength} bytes) 不是 ${SPLAT_SIZE} 的整数倍`);\r\n }\r\n\r\n // 抽样检查数据合理性(检查前几个 splat 的位置是否为有效浮点数)\r\n const dataView = new DataView(data);\r\n const samplesToCheck = Math.min(10, Math.floor(data.byteLength / SPLAT_SIZE));\r\n\r\n for (let i = 0; i < samplesToCheck; i++) {\r\n const off = i * SPLAT_SIZE;\r\n const x = dataView.getFloat32(off + 0, true);\r\n const y = dataView.getFloat32(off + 4, true);\r\n const z = dataView.getFloat32(off + 8, true);\r\n\r\n // 检查是否为有效浮点数(非 NaN、非 Infinity)\r\n if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)) {\r\n throw new Error(`无效的 Splat 文件: 第 ${i} 个 splat 包含无效的位置数据`);\r\n }\r\n\r\n // 检查位置是否在合理范围内(-10000 到 10000)\r\n const MAX_POS = 10000;\r\n if (Math.abs(x) > MAX_POS || Math.abs(y) > MAX_POS || Math.abs(z) > MAX_POS) {\r\n // 位置超出常规范围\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Sigmoid 函数,与 PLYLoader 保持一致\r\n */\r\nfunction sigmoid(x: number): number {\r\n return 1 / (1 + Math.exp(-x));\r\n}\r\n\r\n/**\r\n * 逆 Sigmoid 函数\r\n * 将 [0, 1] 范围的值转换回原始 logit 值\r\n */\r\nfunction inverseSigmoid(y: number): number {\r\n // 避免 log(0) 和 log(负数)\r\n const clamped = Math.max(0.0001, Math.min(0.9999, y));\r\n return Math.log(clamped / (1 - clamped));\r\n}\r\n\r\n/**\r\n * 加载并解析 .splat 文件\r\n * @param url .splat 文件的 URL\r\n * @returns SplatCPU 数组\r\n */\r\nexport async function loadSplat(url: string): Promise<SplatCPU[]> {\r\n // 获取文件\r\n const response = await fetch(url);\r\n if (!response.ok) {\r\n throw new Error(`无法加载 Splat 文件: ${url}`);\r\n }\r\n const buffer = await response.arrayBuffer();\r\n\r\n return deserializeSplat(buffer);\r\n}\r\n\r\n/**\r\n * 从 ArrayBuffer 解析 splat 数据\r\n * @param data 文件的 ArrayBuffer\r\n * @returns SplatCPU 数组\r\n */\r\nexport function deserializeSplat(data: ArrayBufferLike): SplatCPU[] {\r\n // 验证文件格式\r\n validateSplatFile(data);\r\n\r\n const totalSplats = Math.floor(data.byteLength / SPLAT_SIZE);\r\n\r\n const dataView = new DataView(data);\r\n const splats: SplatCPU[] = new Array(totalSplats);\r\n\r\n // 预分配共享的 SH 系数数组(全为 0,因为 .splat 不包含 SH)\r\n const shRestBuffer = new Float32Array(totalSplats * 45);\r\n\r\n for (let i = 0; i < totalSplats; i++) {\r\n const off = i * SPLAT_SIZE;\r\n\r\n // 读取位置 (float32 × 3 = 12 bytes)\r\n const x = dataView.getFloat32(off + 0, true);\r\n const y = dataView.getFloat32(off + 4, true);\r\n const z = dataView.getFloat32(off + 8, true);\r\n\r\n // 读取缩放 (float32 × 3 = 12 bytes)\r\n // splat 格式存储原始缩放值,直接使用\r\n const scale_0 = dataView.getFloat32(off + 12, true);\r\n const scale_1 = dataView.getFloat32(off + 16, true);\r\n const scale_2 = dataView.getFloat32(off + 20, true);\r\n\r\n // 读取颜色 (uint8 × 3 = 3 bytes)\r\n // splat 格式直接存储 RGB 颜色 [0-255],归一化到 [0-1]\r\n const colorR = dataView.getUint8(off + 24) / 255;\r\n const colorG = dataView.getUint8(off + 25) / 255;\r\n const colorB = dataView.getUint8(off + 26) / 255;\r\n\r\n // 读取透明度 (uint8 × 1 = 1 byte)\r\n // .splat 格式存储的是 sigmoid 后的值 [0-255] 映射到 [0-1]\r\n // 为了与 PLYLoader 的 sigmoid 处理保持一致,我们:\r\n // 1. 先将 uint8 转换为 [0, 1] 范围\r\n // 2. 应用逆 sigmoid 得到原始 logit 值\r\n // 3. 再应用 sigmoid(这样与 PLYLoader 的处理流程一致)\r\n // \r\n // 实际上这等价于直接使用线性归一化,但保持代码逻辑一致性\r\n const opacityUint8 = dataView.getUint8(off + 27);\r\n const opacityNormalized = opacityUint8 / 255;\r\n // 直接使用归一化值作为最终 opacity(因为 .splat 已经是 sigmoid 后的值)\r\n const opacity = opacityNormalized;\r\n\r\n // 读取旋转四元数 (uint8 × 4 = 4 bytes)\r\n // 从 [0, 255] 映射到 [-1, 1]\r\n // 四元数顺序: [w, x, y, z]\r\n const rot_w = (dataView.getUint8(off + 28) - 128) / 128;\r\n const rot_x = (dataView.getUint8(off + 29) - 128) / 128;\r\n const rot_y = (dataView.getUint8(off + 30) - 128) / 128;\r\n const rot_z = (dataView.getUint8(off + 31) - 128) / 128;\r\n\r\n // 归一化四元数\r\n const qlen = Math.sqrt(\r\n rot_w * rot_w + rot_x * rot_x + rot_y * rot_y + rot_z * rot_z\r\n );\r\n const qnorm = qlen > 0 ? 1 / qlen : 1;\r\n\r\n // 使用共享 buffer 的子数组\r\n const shOffset = i * 45;\r\n const shRest = shRestBuffer.subarray(shOffset, shOffset + 45);\r\n\r\n splats[i] = {\r\n mean: [x, y, z],\r\n scale: [scale_0, scale_1, scale_2],\r\n rotation: [\r\n rot_w * qnorm,\r\n rot_x * qnorm,\r\n rot_y * qnorm,\r\n rot_z * qnorm,\r\n ],\r\n colorDC: [colorR, colorG, colorB],\r\n opacity,\r\n shRest,\r\n };\r\n }\r\n\r\n return splats;\r\n}\r\n","/**\r\n * GSSplatSorter - GPU Counting Sort 深度排序器\r\n *\r\n * 基于 PlayCanvas 的排序算法实现,使用 Counting Sort\r\n * 时间复杂度 O(n),远比 Bitonic Sort O(n log²n) 更快\r\n *\r\n * 流程:\r\n * 1. 剔除 + 计算深度 + 统计计数\r\n * 2. 前缀和计算偏移\r\n * 3. 散射到最终位置\r\n * \r\n * iOS 兼容性:\r\n * - iOS WebGPU 对大型原子数组支持有限\r\n * - 默认使用 65536 个桶(桌面/Android)\r\n * - iOS 使用 4096 个桶(减少原子操作压力)\r\n */\r\n\r\n// 默认配置\r\nconst DEFAULT_NUM_BUCKETS = 65536;\r\nconst IOS_NUM_BUCKETS = 4096; // iOS 使用更少的桶\r\nconst WORKGROUP_SIZE = 256;\r\n\r\n/**\r\n * 检测是否为 iOS 设备\r\n */\r\nfunction isIOSDevice(): boolean {\r\n if (typeof navigator === 'undefined') return false;\r\n const ua = navigator.userAgent || '';\r\n return /iphone|ipad|ipod/i.test(ua.toLowerCase()) || \r\n (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1); // iPad 伪装为 Mac\r\n}\r\n\r\n/**\r\n * 生成 Culling Shader 代码(支持可配置桶数量)\r\n */\r\nfunction generateCullingShaderCode(numBuckets: number): string {\r\n const bucketMask = numBuckets - 1; // 用于位运算\r\n const bucketBits = Math.log2(numBuckets); // 桶 ID 占用的位数\r\n const depthShift = 32 - bucketBits; // 深度值移位量\r\n \r\n return /* wgsl */ `\r\n/**\r\n * Pass 1: 剔除 + 深度计算 + 桶计数\r\n * 桶数量: ${numBuckets}\r\n */\r\n\r\nconst NUM_BUCKETS: u32 = ${numBuckets}u;\r\nconst BUCKET_MAX: u32 = ${numBuckets - 1}u;\r\n\r\nstruct Splat {\r\n mean: vec3<f32>,\r\n _pad0: f32,\r\n scale: vec3<f32>,\r\n _pad1: f32,\r\n rotation: vec4<f32>,\r\n colorDC: vec3<f32>,\r\n opacity: f32,\r\n sh1: array<f32, 9>,\r\n sh2: array<f32, 15>,\r\n sh3: array<f32, 21>,\r\n _pad2: array<f32, 3>,\r\n}\r\n\r\nstruct CameraUniforms {\r\n view: mat4x4<f32>,\r\n proj: mat4x4<f32>,\r\n model: mat4x4<f32>,\r\n cameraPos: vec3<f32>,\r\n _pad: f32,\r\n}\r\n\r\nstruct CullingParams {\r\n splatCount: u32,\r\n nearPlane: f32,\r\n farPlane: f32,\r\n screenWidth: f32,\r\n screenHeight: f32,\r\n pixelThreshold: f32,\r\n _pad0: f32,\r\n _pad1: f32,\r\n}\r\n\r\nstruct Counters {\r\n visibleCount: atomic<u32>,\r\n}\r\n\r\n@group(0) @binding(0) var<storage, read> splats: array<Splat>;\r\n@group(0) @binding(1) var<uniform> camera: CameraUniforms;\r\n@group(0) @binding(2) var<uniform> params: CullingParams;\r\n@group(0) @binding(3) var<storage, read_write> counters: Counters;\r\n@group(0) @binding(4) var<storage, read_write> visibleIndices: array<u32>;\r\n@group(0) @binding(5) var<storage, read_write> depthKeys: array<u32>;\r\n@group(0) @binding(6) var<storage, read_write> bucketCounts: array<atomic<u32>>;\r\n@group(0) @binding(7) var<storage, read_write> drawIndirect: array<u32>;\r\n\r\nfn maxScale(scale: vec3<f32>) -> f32 {\r\n return max(max(scale.x, scale.y), scale.z);\r\n}\r\n\r\n// 从模型矩阵提取最大缩放因子\r\nfn getModelMaxScale(model: mat4x4<f32>) -> f32 {\r\n let sx = length(model[0].xyz);\r\n let sy = length(model[1].xyz);\r\n let sz = length(model[2].xyz);\r\n return max(max(sx, sy), sz);\r\n}\r\n\r\n@compute @workgroup_size(${WORKGROUP_SIZE})\r\nfn cullAndCount(@builtin(global_invocation_id) gid: vec3<u32>) {\r\n let i = gid.x;\r\n if (i >= params.splatCount) {\r\n return;\r\n }\r\n \r\n let splat = splats[i];\r\n // 先应用模型矩阵变换到世界空间,再变换到视图空间\r\n let worldPos = camera.model * vec4<f32>(splat.mean, 1.0);\r\n let viewPos = camera.view * worldPos;\r\n let z = -viewPos.z;\r\n \r\n // 近平面剔除\r\n if (z < params.nearPlane) {\r\n return;\r\n }\r\n \r\n // 远平面剔除\r\n if (z > params.farPlane) {\r\n return;\r\n }\r\n \r\n // 视锥剔除\r\n let fx = camera.proj[0][0];\r\n let fy = camera.proj[1][1];\r\n let x_ndc = viewPos.x * fx / z;\r\n let y_ndc = viewPos.y * fy / z;\r\n // 考虑模型缩放对 splat 半径的影响\r\n let modelScale = getModelMaxScale(camera.model);\r\n let worldRadius = maxScale(splat.scale) * modelScale * 3.0;\r\n let r_ndc = worldRadius * max(fx, fy) / z;\r\n \r\n if (x_ndc < -1.0 - r_ndc || x_ndc > 1.0 + r_ndc) {\r\n return;\r\n }\r\n if (y_ndc < -1.0 - r_ndc || y_ndc > 1.0 + r_ndc) {\r\n return;\r\n }\r\n \r\n // 屏幕尺寸剔除\r\n let screenRadiusX = r_ndc * params.screenWidth * 0.5;\r\n let screenRadiusY = r_ndc * params.screenHeight * 0.5;\r\n let screenRadius = max(screenRadiusX, screenRadiusY);\r\n \r\n if (screenRadius < params.pixelThreshold) {\r\n return;\r\n }\r\n \r\n // 透明度剔除\r\n if (splat.opacity < 0.004) {\r\n return;\r\n }\r\n \r\n // 通过剔除,计算深度桶\r\n let depthRange = params.farPlane - params.nearPlane;\r\n let normalizedDepth = clamp((z - params.nearPlane) / depthRange, 0.0, 1.0);\r\n // 反转:让远处的桶 ID 更小,这样 counting sort 后远处的在前面\r\n let depthBucket = BUCKET_MAX - u32(normalizedDepth * f32(BUCKET_MAX));\r\n // 复合 key: 深度桶 + 原始索引低位作为 tie-breaker\r\n let depthKey = (depthBucket << ${32 - bucketBits}u) | (i & ${(1 << (32 - bucketBits)) - 1}u);\r\n \r\n // 分配可见索引位置\r\n let visibleIdx = atomicAdd(&counters.visibleCount, 1u);\r\n visibleIndices[visibleIdx] = i;\r\n depthKeys[visibleIdx] = depthKey;\r\n \r\n // 统计桶计数\r\n atomicAdd(&bucketCounts[depthBucket], 1u);\r\n}\r\n\r\n@compute @workgroup_size(1)\r\nfn resetCounters() {\r\n atomicStore(&counters.visibleCount, 0u);\r\n}\r\n\r\n@compute @workgroup_size(${WORKGROUP_SIZE})\r\nfn resetBucketCounts(@builtin(global_invocation_id) gid: vec3<u32>) {\r\n let i = gid.x;\r\n if (i < NUM_BUCKETS) {\r\n atomicStore(&bucketCounts[i], 0u);\r\n }\r\n}\r\n\r\n@compute @workgroup_size(1)\r\nfn updateDrawIndirect() {\r\n let count = atomicLoad(&counters.visibleCount);\r\n drawIndirect[0] = 4u;\r\n drawIndirect[1] = count;\r\n drawIndirect[2] = 0u;\r\n drawIndirect[3] = 0u;\r\n}\r\n`;\r\n}\r\n\r\n/**\r\n * 生成前缀和 Shader 代码\r\n */\r\nfunction generatePrefixSumShaderCode(numBuckets: number): string {\r\n return /* wgsl */ `\r\n/**\r\n * Pass 2: 串行前缀和\r\n * ${numBuckets} 个桶\r\n */\r\n\r\nconst NUM_BUCKETS: u32 = ${numBuckets}u;\r\n\r\n@group(0) @binding(0) var<storage, read_write> bucketCounts: array<u32>;\r\n@group(0) @binding(1) var<storage, read_write> bucketOffsets: array<u32>;\r\n\r\n@compute @workgroup_size(1)\r\nfn prefixSum() {\r\n var sum = 0u;\r\n for (var i = 0u; i < NUM_BUCKETS; i++) {\r\n bucketOffsets[i] = sum;\r\n sum += bucketCounts[i];\r\n }\r\n}\r\n`;\r\n}\r\n\r\n/**\r\n * 生成散射 Shader 代码\r\n */\r\nfunction generateScatterShaderCode(numBuckets: number): string {\r\n const bucketBits = Math.log2(numBuckets);\r\n const depthShift = 32 - bucketBits;\r\n \r\n return /* wgsl */ `\r\n/**\r\n * Pass 3: 散射到最终排序位置\r\n * 桶数量: ${numBuckets}\r\n */\r\n\r\nconst NUM_BUCKETS: u32 = ${numBuckets}u;\r\n\r\nstruct Counters {\r\n visibleCount: u32,\r\n}\r\n\r\n@group(0) @binding(0) var<storage, read> visibleIndices: array<u32>;\r\n@group(0) @binding(1) var<storage, read> depthKeys: array<u32>;\r\n@group(0) @binding(2) var<storage, read> bucketOffsets: array<u32>;\r\n@group(0) @binding(3) var<storage, read_write> bucketPositions: array<atomic<u32>>;\r\n@group(0) @binding(4) var<storage, read_write> sortedIndices: array<u32>;\r\n@group(0) @binding(5) var<storage, read> counters: Counters;\r\n\r\n@compute @workgroup_size(${WORKGROUP_SIZE})\r\nfn scatter(@builtin(global_invocation_id) gid: vec3<u32>) {\r\n let i = gid.x;\r\n if (i >= counters.visibleCount) {\r\n return;\r\n }\r\n \r\n let depthKey = depthKeys[i];\r\n let originalIndex = visibleIndices[i];\r\n \r\n // 从复合 key 提取深度桶\r\n let depthBucket = depthKey >> ${depthShift}u;\r\n \r\n // 在桶内分配位置\r\n let bucketOffset = bucketOffsets[depthBucket];\r\n let posInBucket = atomicAdd(&bucketPositions[depthBucket], 1u);\r\n let destIdx = bucketOffset + posInBucket;\r\n \r\n // 写入最终排序位置\r\n sortedIndices[destIdx] = originalIndex;\r\n}\r\n\r\n@compute @workgroup_size(${WORKGROUP_SIZE})\r\nfn resetBucketPositions(@builtin(global_invocation_id) gid: vec3<u32>) {\r\n let i = gid.x;\r\n if (i < NUM_BUCKETS) {\r\n atomicStore(&bucketPositions[i], 0u);\r\n }\r\n}\r\n`;\r\n}\r\n\r\n/**\r\n * 屏幕尺寸信息\r\n */\r\nexport interface ScreenInfo {\r\n width: number;\r\n height: number;\r\n}\r\n\r\n/**\r\n * 剔除参数\r\n */\r\nexport interface CullingOptions {\r\n nearPlane: number;\r\n farPlane: number;\r\n pixelThreshold: number;\r\n}\r\n\r\n/**\r\n * 排序器配置选项\r\n */\r\nexport interface SorterOptions {\r\n /** 深度桶数量(必须是 2 的幂次),默认根据平台自动选择 */\r\n numBuckets?: number;\r\n}\r\n\r\n/**\r\n * GSSplatSorter - GPU Counting Sort 排序器\r\n */\r\nexport class GSSplatSorter {\r\n private device: GPUDevice;\r\n private splatCount: number;\r\n\r\n // ============================================\r\n // Buffers\r\n // ============================================\r\n private cullingParamsBuffer: GPUBuffer;\r\n private countersBuffer: GPUBuffer;\r\n private visibleIndicesBuffer: GPUBuffer;\r\n private depthKeysBuffer: GPUBuffer;\r\n private bucketCountsBuffer: GPUBuffer;\r\n private bucketOffsetsBuffer: GPUBuffer;\r\n private bucketPositionsBuffer: GPUBuffer;\r\n private sortedIndicesBuffer: GPUBuffer;\r\n private drawIndirectBuffer: GPUBuffer;\r\n\r\n // ============================================\r\n // Pipelines\r\n // ============================================\r\n private resetCountersPipeline: GPUComputePipeline;\r\n private resetBucketCountsPipeline: GPUComputePipeline;\r\n private cullAndCountPipeline: GPUComputePipeline;\r\n private updateDrawIndirectPipeline: GPUComputePipeline;\r\n private prefixSumPipeline: GPUComputePipeline;\r\n private resetBucketPositionsPipeline: GPUComputePipeline;\r\n private scatterPipeline: GPUComputePipeline;\r\n\r\n // ============================================\r\n // Bind Groups\r\n // ============================================\r\n private cullingBindGroupLayout: GPUBindGroupLayout;\r\n private cullingBindGroup: GPUBindGroup;\r\n private prefixSumBindGroupLayout: GPUBindGroupLayout;\r\n private prefixSumBindGroup: GPUBindGroup;\r\n private scatterBindGroupLayout: GPUBindGroupLayout;\r\n private scatterBindGroup: GPUBindGroup;\r\n\r\n // 工作组大小和桶数量\r\n private readonly WORKGROUP_SIZE = WORKGROUP_SIZE;\r\n private readonly numBuckets: number;\r\n\r\n // 当前屏幕信息\r\n private screenWidth: number = 1920;\r\n private screenHeight: number = 1080;\r\n\r\n // 剔除选项\r\n private cullingOptions: CullingOptions = {\r\n nearPlane: 0.1,\r\n farPlane: 1000,\r\n pixelThreshold: 1.0,\r\n };\r\n\r\n constructor(\r\n device: GPUDevice,\r\n splatCount: number,\r\n splatBuffer: GPUBuffer,\r\n cameraBuffer: GPUBuffer,\r\n options: SorterOptions = {},\r\n ) {\r\n this.device = device;\r\n this.splatCount = splatCount;\r\n \r\n // 根据平台选择桶数量\r\n // iOS 使用较少的桶以避免原子操作问题\r\n const isIOS = isIOSDevice();\r\n this.numBuckets = options.numBuckets ?? (isIOS ? IOS_NUM_BUCKETS : DEFAULT_NUM_BUCKETS);\r\n\r\n // ============================================\r\n // 创建 Shader 模块(使用动态生成的代码)\r\n // 添加编译错误检查\r\n // ============================================\r\n const cullingCode = generateCullingShaderCode(this.numBuckets);\r\n const prefixSumCode = generatePrefixSumShaderCode(this.numBuckets);\r\n const scatterCode = generateScatterShaderCode(this.numBuckets);\r\n \r\n // 调试:在移动端输出 shader 代码用于诊断\r\n if (isIOS) {\r\n // iOS 诊断(静默)\r\n }\r\n \r\n const cullingModule = device.createShaderModule({\r\n code: cullingCode,\r\n label: 'culling-shader',\r\n });\r\n\r\n const prefixSumModule = device.createShaderModule({\r\n code: prefixSumCode,\r\n label: 'prefix-sum-shader',\r\n });\r\n\r\n const scatterModule = device.createShaderModule({\r\n code: scatterCode,\r\n label: 'scatter-shader',\r\n });\r\n \r\n // 检查 shader 编译错误\r\n cullingModule.getCompilationInfo().then(info => {\r\n if (info.messages.length > 0) {\r\n // shader 编译信息(静默)\r\n }\r\n });\r\n prefixSumModule.getCompilationInfo().then(info => {\r\n if (info.messages.length > 0) {\r\n // shader 编译信息(静默)\r\n }\r\n });\r\n scatterModule.getCompilationInfo().then(info => {\r\n if (info.messages.length > 0) {\r\n // shader 编译信息(静默)\r\n }\r\n });\r\n\r\n // ============================================\r\n // 创建 Buffers\r\n // ============================================\r\n\r\n this.cullingParamsBuffer = device.createBuffer({\r\n size: 32,\r\n usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\r\n });\r\n\r\n this.countersBuffer = device.createBuffer({\r\n size: 16,\r\n usage:\r\n GPUBufferUsage.STORAGE |\r\n GPUBufferUsage.COPY_DST |\r\n GPUBufferUsage.COPY_SRC,\r\n });\r\n\r\n this.visibleIndicesBuffer = device.createBuffer({\r\n size: splatCount * 4,\r\n usage: GPUBufferUsage.STORAGE,\r\n });\r\n\r\n this.depthKeysBuffer = device.createBuffer({\r\n size: splatCount * 4,\r\n usage: GPUBufferUsage.STORAGE,\r\n });\r\n\r\n this.bucketCountsBuffer = device.createBuffer({\r\n size: this.numBuckets * 4,\r\n usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,\r\n });\r\n\r\n this.bucketOffsetsBuffer = device.createBuffer({\r\n size: this.numBuckets * 4,\r\n usage: GPUBufferUsage.STORAGE,\r\n });\r\n\r\n this.bucketPositionsBuffer = device.createBuffer({\r\n size: this.numBuckets * 4,\r\n usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,\r\n });\r\n\r\n this.sortedIndicesBuffer = device.createBuffer({\r\n size: splatCount * 4,\r\n usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,\r\n });\r\n\r\n this.drawIndirectBuffer = device.createBuffer({\r\n size: 16,\r\n usage:\r\n GPUBufferUsage.STORAGE |\r\n GPUBufferUsage.INDIRECT |\r\n GPUBufferUsage.COPY_DST,\r\n });\r\n\r\n // scatterParamsBuffer 不再需要,scatter 直接从 countersBuffer 读取 visibleCount\r\n\r\n // ============================================\r\n // 创建 Bind Group Layouts 和 Pipelines\r\n // ============================================\r\n\r\n // Culling bind group layout (8 bindings)\r\n this.cullingBindGroupLayout = device.createBindGroupLayout({\r\n entries: [\r\n {\r\n binding: 0,\r\n visibility: GPUShaderStage.COMPUTE,\r\n buffer: { type: \"read-only-storage\" },\r\n },\r\n {\r\n binding: 1,\r\n visibility: GPUShaderStage.COMPUTE,\r\n buffer: { type: \"uniform\" },\r\n },\r\n {\r\n binding: 2,\r\n visibility: GPUShaderStage.COMPUTE,\r\n buffer: { type: \"uniform\" },\r\n },\r\n {\r\n binding: 3,\r\n visibility: GPUShaderStage.COMPUTE,\r\n buffer: { type: \"storage\" },\r\n },\r\n {\r\n binding: 4,\r\n visibility: GPUShaderStage.COMPUTE,\r\n buffer: { type: \"storage\" },\r\n },\r\n {\r\n binding: 5,\r\n visibility: GPUShaderStage.COMPUTE,\r\n buffer: { type: \"storage\" },\r\n },\r\n {\r\n binding: 6,\r\n visibility: GPUShaderStage.COMPUTE,\r\n buffer: { type: \"storage\" },\r\n },\r\n {\r\n binding: 7,\r\n visibility: GPUShaderStage.COMPUTE,\r\n buffer: { type: \"storage\" },\r\n },\r\n ],\r\n });\r\n\r\n const cullingPipelineLayout = device.createPipelineLayout({\r\n bindGroupLayouts: [this.cullingBindGroupLayout],\r\n });\r\n\r\n this.resetCountersPipeline = device.createComputePipeline({\r\n layout: cullingPipelineLayout,\r\n compute: { module: cullingModule, entryPoint: \"resetCounters\" },\r\n });\r\n\r\n this.resetBucketCountsPipeline = device.createComputePipeline({\r\n layout: cullingPipelineLayout,\r\n compute: { module: cullingModule, entryPoint: \"resetBucketCounts\" },\r\n });\r\n\r\n this.cullAndCountPipeline = device.createComputePipeline({\r\n layout: cullingPipelineLayout,\r\n compute: { module: cullingModule, entryPoint: \"cullAndCount\" },\r\n });\r\n\r\n this.updateDrawIndirectPipeline = device.createComputePipeline({\r\n layout: cullingPipelineLayout,\r\n compute: { module: cullingModule, entryPoint: \"updateDrawIndirect\" },\r\n });\r\n\r\n // Prefix sum bind group layout (2 bindings)\r\n this.prefixSumBindGroupLayout = device.createBindGroupLayout({\r\n entries: [\r\n {\r\n binding: 0,\r\n visibility: GPUShaderStage.COMPUTE,\r\n buffer: { type: \"storage\" },\r\n },\r\n {\r\n binding: 1,\r\n visibility: GPUShaderStage.COMPUTE,\r\n buffer: { type: \"storage\" },\r\n },\r\n ],\r\n });\r\n\r\n const prefixSumPipelineLayout = device.createPipelineLayout({\r\n bindGroupLayouts: [this.prefixSumBindGroupLayout],\r\n });\r\n\r\n this.prefixSumPipeline = device.createComputePipeline({\r\n layout: prefixSumPipelineLayout,\r\n compute: { module: prefixSumModule, entryPoint: \"prefixSum\" },\r\n });\r\n\r\n // Scatter bind group layout (6 bindings)\r\n this.scatterBindGroupLayout = device.createBindGroupLayout({\r\n entries: [\r\n {\r\n binding: 0,\r\n visibility: GPUShaderStage.COMPUTE,\r\n buffer: { type: \"read-only-storage\" },\r\n },\r\n {\r\n binding: 1,\r\n visibility: GPUShaderStage.COMPUTE,\r\n buffer: { type: \"read-only-storage\" },\r\n },\r\n {\r\n binding: 2,\r\n visibility: GPUShaderStage.COMPUTE,\r\n buffer: { type: \"read-only-storage\" },\r\n },\r\n {\r\n binding: 3,\r\n visibility: GPUShaderStage.COMPUTE,\r\n buffer: { type: \"storage\" },\r\n },\r\n {\r\n binding: 4,\r\n visibility: GPUShaderStage.COMPUTE,\r\n buffer: { type: \"storage\" },\r\n },\r\n {\r\n binding: 5,\r\n visibility: GPUShaderStage.COMPUTE,\r\n buffer: { type: \"read-only-storage\" },\r\n }, // countersBuffer\r\n ],\r\n });\r\n\r\n const scatterPipelineLayout = device.createPipelineLayout({\r\n bindGroupLayouts: [this.scatterBindGroupLayout],\r\n });\r\n\r\n this.resetBucketPositionsPipeline = device.createComputePipeline({\r\n layout: scatterPipelineLayout,\r\n compute: { module: scatterModule, entryPoint: \"resetBucketPositions\" },\r\n });\r\n\r\n this.scatterPipeline = device.createComputePipeline({\r\n layout: scatterPipelineLayout,\r\n compute: { module: scatterModule, entryPoint: \"scatter\" },\r\n });\r\n\r\n // ============================================\r\n // 创建 Bind Groups\r\n // ============================================\r\n\r\n this.cullingBindGroup = device.createBindGroup({\r\n layout: this.cullingBindGroupLayout,\r\n entries: [\r\n { binding: 0, resource: { buffer: splatBuffer } },\r\n { binding: 1, resource: { buffer: cameraBuffer } },\r\n { binding: 2, resource: { buffer: this.cullingParamsBuffer } },\r\n { binding: 3, resource: { buffer: this.countersBuffer } },\r\n { binding: 4, resource: { buffer: this.visibleIndicesBuffer } },\r\n { binding: 5, resource: { buffer: this.depthKeysBuffer } },\r\n { binding: 6, resource: { buffer: this.bucketCountsBuffer } },\r\n { binding: 7, resource: { buffer: this.drawIndirectBuffer } },\r\n ],\r\n });\r\n\r\n this.prefixSumBindGroup = device.createBindGroup({\r\n layout: this.prefixSumBindGroupLayout,\r\n entries: [\r\n { binding: 0, resource: { buffer: this.bucketCountsBuffer } },\r\n { binding: 1, resource: { buffer: this.bucketOffsetsBuffer } },\r\n ],\r\n });\r\n\r\n this.scatterBindGroup = device.createBindGroup({\r\n layout: this.scatterBindGroupLayout,\r\n entries: [\r\n { binding: 0, resource: { buffer: this.visibleIndicesBuffer } },\r\n { binding: 1, resource: { buffer: this.depthKeysBuffer } },\r\n { binding: 2, resource: { buffer: this.bucketOffsetsBuffer } },\r\n { binding: 3, resource: { buffer: this.bucketPositionsBuffer } },\r\n { binding: 4, resource: { buffer: this.sortedIndicesBuffer } },\r\n { binding: 5, resource: { buffer: this.countersBuffer } }, // 使用 GPU countersBuffer\r\n ],\r\n });\r\n }\r\n\r\n /**\r\n * 设置屏幕尺寸\r\n */\r\n setScreenSize(width: number, height: number): void {\r\n this.screenWidth = width;\r\n this.screenHeight = height;\r\n }\r\n\r\n /**\r\n * 设置剔除参数\r\n */\r\n setCullingOptions(options: Partial<CullingOptions>): void {\r\n this.cullingOptions = { ...this.cullingOptions, ...options };\r\n }\r\n\r\n /**\r\n * 执行剔除和排序\r\n * 每帧调用\r\n * \r\n * 优化:合并所有 compute pass 到单次 GPU 提交\r\n * WebGPU 保证同一 command buffer 中的命令按顺序执行\r\n */\r\n sort(): void {\r\n try {\r\n // ============================================\r\n // 更新参数\r\n // ============================================\r\n const cullingParamsData = new ArrayBuffer(32);\r\n const cullingParamsView = new DataView(cullingParamsData);\r\n cullingParamsView.setUint32(0, this.splatCount, true);\r\n cullingParamsView.setFloat32(4, this.cullingOptions.nearPlane, true);\r\n cullingParamsView.setFloat32(8, this.cullingOptions.farPlane, true);\r\n cullingParamsView.setFloat32(12, this.screenWidth, true);\r\n cullingParamsView.setFloat32(16, this.screenHeight, true);\r\n cullingParamsView.setFloat32(20, this.cullingOptions.pixelThreshold, true);\r\n cullingParamsView.setFloat32(24, 0, true);\r\n cullingParamsView.setFloat32(28, 0, true);\r\n this.device.queue.writeBuffer(\r\n this.cullingParamsBuffer,\r\n 0,\r\n cullingParamsData,\r\n );\r\n\r\n const cullWorkgroupCount = Math.ceil(this.splatCount / this.WORKGROUP_SIZE);\r\n const bucketResetWorkgroups = Math.ceil(\r\n this.numBuckets / this.WORKGROUP_SIZE,\r\n );\r\n\r\n // ============================================\r\n // 单次提交所有 Compute Pass\r\n // WebGPU 保证同一 encoder 中的 pass 按顺序执行\r\n // ============================================\r\n const encoder = this.device.createCommandEncoder();\r\n\r\n // Pass 1: 重置 visibleCount\r\n {\r\n const pass = encoder.beginComputePass();\r\n pass.setPipeline(this.resetCountersPipeline);\r\n pass.setBindGroup(0, this.cullingBindGroup);\r\n pass.dispatchWorkgroups(1);\r\n pass.end();\r\n }\r\n\r\n // Pass 2: 重置桶计数\r\n {\r\n const pass = encoder.beginComputePass();\r\n pass.setPipeline(this.resetBucketCountsPipeline);\r\n pass.setBindGroup(0, this.cullingBindGroup);\r\n pass.dispatchWorkgroups(bucketResetWorkgroups);\r\n pass.end();\r\n }\r\n\r\n // Pass 3: 剔除 + 深度计算 + 桶计数\r\n {\r\n const pass = encoder.beginComputePass();\r\n pass.setPipeline(this.cullAndCountPipeline);\r\n pass.setBindGroup(0, this.cullingBindGroup);\r\n pass.dispatchWorkgroups(cullWorkgroupCount);\r\n pass.end();\r\n }\r\n\r\n // Pass 4: 更新 DrawIndirect\r\n {\r\n const pass = encoder.beginComputePass();\r\n pass.setPipeline(this.updateDrawIndirectPipeline);\r\n pass.setBindGroup(0, this.cullingBindGroup);\r\n pass.dispatchWorkgroups(1);\r\n pass.end();\r\n }\r\n\r\n // Pass 5: 前缀和\r\n {\r\n const pass = encoder.beginComputePass();\r\n pass.setPipeline(this.prefixSumPipeline);\r\n pass.setBindGroup(0, this.prefixSumBindGroup);\r\n pass.dispatchWorkgroups(1);\r\n pass.end();\r\n }\r\n\r\n // Pass 6: 重置桶位置计数\r\n {\r\n const pass = encoder.beginComputePass();\r\n pass.setPipeline(this.resetBucketPositionsPipeline);\r\n pass.setBindGroup(0, this.scatterBindGroup);\r\n pass.dispatchWorkgroups(bucketResetWorkgroups);\r\n pass.end();\r\n }\r\n\r\n // Pass 7: 散射到最终位置\r\n {\r\n const pass = encoder.beginComputePass();\r\n pass.setPipeline(this.scatterPipeline);\r\n pass.setBindGroup(0, this.scatterBindGroup);\r\n pass.dispatchWorkgroups(cullWorkgroupCount);\r\n pass.end();\r\n }\r\n\r\n // 单次提交\r\n this.device.queue.submit([encoder.finish()]);\r\n } catch (error) {\r\n // 排序错误(静默处理)\r\n }\r\n }\r\n\r\n /**\r\n * 获取排序后的索引 buffer(用于渲染)\r\n */\r\n getIndicesBuffer(): GPUBuffer {\r\n return this.sortedIndicesBuffer;\r\n }\r\n\r\n /**\r\n * 获取 DrawIndirect buffer\r\n */\r\n getDrawIndirectBuffer(): GPUBuffer {\r\n return this.drawIndirectBuffer;\r\n }\r\n\r\n /**\r\n * 获取 splat 总数量\r\n */\r\n getSplatCount(): number {\r\n return this.splatCount;\r\n }\r\n\r\n /**\r\n * 销毁资源\r\n */\r\n destroy(): void {\r\n this.cullingParamsBuffer.destroy();\r\n this.countersBuffer.destroy();\r\n this.visibleIndicesBuffer.destroy();\r\n this.depthKeysBuffer.destroy();\r\n this.bucketCountsBuffer.destroy();\r\n this.bucketOffsetsBuffer.destroy();\r\n this.bucketPositionsBuffer.destroy();\r\n this.sortedIndicesBuffer.destroy();\r\n this.drawIndirectBuffer.destroy();\r\n }\r\n}\r\n","/**\r\n * IGSSplatRenderer - 3D Gaussian Splatting 渲染器统一接口\r\n * \r\n * 桌面端和移动端渲染器都实现此接口,消除平台判断代码\r\n */\r\n\r\nimport { CompactSplatData } from \"./PLYLoaderMobile\";\r\nimport { SplatCPU } from \"./PLYLoader\";\r\n\r\n/**\r\n * Bounding Box 结构\r\n */\r\nexport interface BoundingBox {\r\n min: [number, number, number];\r\n max: [number, number, number];\r\n center: [number, number, number];\r\n radius: number;\r\n}\r\n\r\n/**\r\n * SH 模式枚举\r\n */\r\nexport enum SHMode {\r\n L0 = 0, // 仅 DC 颜色(最快)\r\n L1 = 1, // DC + L1 SH\r\n L2 = 2, // DC + L1 + L2 SH\r\n L3 = 3, // 完整 SH(最高质量)\r\n}\r\n\r\n/**\r\n * 3D Gaussian Splatting 渲染器接口\r\n */\r\nexport interface IGSSplatRenderer {\r\n // ============================================\r\n // 数据设置\r\n // ============================================\r\n \r\n /**\r\n * 设置紧凑格式的 splat 数据(推荐)\r\n */\r\n setCompactData(data: CompactSplatData): void;\r\n \r\n /**\r\n * 设置原始 splat 数据(仅桌面端支持)\r\n * 移动端实现可以抛出错误或转换为紧凑格式\r\n */\r\n setData?(splats: SplatCPU[]): void;\r\n\r\n // ============================================\r\n // 渲染\r\n // ============================================\r\n \r\n /**\r\n * 渲染 splats\r\n */\r\n render(pass: GPURenderPassEncoder): void;\r\n\r\n // ============================================\r\n // 变换\r\n // ============================================\r\n \r\n /**\r\n * 设置位置\r\n */\r\n setPosition(x: number, y: number, z: number): void;\r\n \r\n /**\r\n * 获取位置\r\n */\r\n getPosition(): [number, number, number];\r\n \r\n /**\r\n * 设置旋转(欧拉角,弧度)\r\n */\r\n setRotation(x: number, y: number, z: number): void;\r\n \r\n /**\r\n * 获取旋转\r\n */\r\n getRotation(): [number, number, number];\r\n \r\n /**\r\n * 设置缩放\r\n */\r\n setScale(x: number, y: number, z: number): void;\r\n \r\n /**\r\n * 获取缩放\r\n */\r\n getScale(): [number, number, number];\r\n \r\n /**\r\n * 设置旋转/缩放中心点(pivot)\r\n */\r\n setPivot(x: number, y: number, z: number): void;\r\n \r\n /**\r\n * 获取旋转/缩放中心点(pivot)\r\n */\r\n getPivot(): [number, number, number];\r\n \r\n /**\r\n * 获取模型矩阵\r\n */\r\n getModelMatrix(): Float32Array;\r\n\r\n // ============================================\r\n // 查询\r\n // ============================================\r\n \r\n /**\r\n * 获取 splat 数量\r\n */\r\n getSplatCount(): number;\r\n \r\n /**\r\n * 获取 bounding box\r\n */\r\n getBoundingBox(): BoundingBox | null;\r\n\r\n // ============================================\r\n // SH 模式(可选,移动端可能不支持)\r\n // ============================================\r\n \r\n /**\r\n * 设置 SH 模式\r\n * 移动端可能只支持 L0\r\n */\r\n setSHMode?(mode: SHMode): void;\r\n \r\n /**\r\n * 获取当前 SH 模式\r\n */\r\n getSHMode?(): SHMode;\r\n \r\n /**\r\n * 是否支持指定的 SH 模式\r\n */\r\n supportsSHMode?(mode: SHMode): boolean;\r\n\r\n // ============================================\r\n // 生命周期\r\n // ============================================\r\n \r\n /**\r\n * 销毁资源\r\n */\r\n destroy(): void;\r\n}\r\n\r\n/**\r\n * 渲染器能力描述\r\n */\r\nexport interface RendererCapabilities {\r\n /** 支持的最高 SH 模式 */\r\n maxSHMode: SHMode;\r\n /** 是否支持原始 SplatCPU 数据 */\r\n supportsRawData: boolean;\r\n /** 是否为移动端优化版本 */\r\n isMobileOptimized: boolean;\r\n /** 最大支持的 splat 数量(0 表示无限制) */\r\n maxSplatCount: number;\r\n}\r\n\r\n/**\r\n * 获取渲染器能力(可选方法)\r\n */\r\nexport interface IGSSplatRendererWithCapabilities extends IGSSplatRenderer {\r\n getCapabilities(): RendererCapabilities;\r\n}\r\n","import { Renderer } from \"../core/Renderer\";\r\nimport { Camera } from \"../core/Camera\";\r\nimport { SplatCPU } from \"./PLYLoader\";\r\nimport { GSSplatSorter } from \"./GSSplatSorter\";\r\nimport { CompactSplatData, compactDataToGPUBuffer } from \"./PLYLoaderMobile\";\r\nimport {\r\n compressSplatsToTextures,\r\n destroyCompressedTextures,\r\n CompressedSplatTextures,\r\n} from \"./TextureCompressor\";\r\nimport { \r\n IGSSplatRenderer, \r\n BoundingBox as IBoundingBox, \r\n SHMode as ISHMode,\r\n RendererCapabilities,\r\n IGSSplatRendererWithCapabilities \r\n} from \"./IGSSplatRenderer\";\r\n\r\n/**\r\n * 检测是否为移动设备\r\n */\r\nfunction isMobileDevice(): boolean {\r\n if (typeof navigator === \"undefined\") return false;\r\n const ua =\r\n navigator.userAgent || navigator.vendor || (window as any).opera || \"\";\r\n\r\n // 检测移动设备\r\n const isMobileUA =\r\n /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(\r\n ua.toLowerCase(),\r\n );\r\n\r\n // 额外检测:触摸屏 + 小屏幕 = 移动设备\r\n const hasTouch = \"ontouchstart\" in window || navigator.maxTouchPoints > 0;\r\n const isSmallScreen = window.innerWidth <= 768;\r\n\r\n // 检测 iPad 伪装成 Mac 的情况\r\n const isIPadAsMac =\r\n navigator.platform === \"MacIntel\" && navigator.maxTouchPoints > 1;\r\n\r\n const result = isMobileUA || isIPadAsMac || (hasTouch && isSmallScreen);\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 性能等级\r\n */\r\nexport enum PerformanceTier {\r\n HIGH = \"high\", // 桌面高端 GPU\r\n MEDIUM = \"medium\", // 桌面中端\r\n LOW = \"low\", // 移动设备(所有移动设备都用 LOW!)\r\n}\r\n\r\n/**\r\n * 根据设备能力检测性能等级\r\n */\r\nfunction detectPerformanceTier(device: GPUDevice): PerformanceTier {\r\n const isMobile = isMobileDevice();\r\n const maxBufferSize = device.limits.maxBufferSize;\r\n const maxStorageBufferBindingSize = device.limits.maxStorageBufferBindingSize;\r\n\r\n // 移动设备一律使用 LOW!\r\n // 即使 GPU 报告支持大 buffer,移动端的散热和功耗限制也会导致崩溃\r\n if (isMobile) {\r\n return PerformanceTier.LOW;\r\n }\r\n\r\n // 桌面设备根据 buffer 大小判断\r\n if (maxStorageBufferBindingSize >= 1024 * 1024 * 1024) {\r\n // 1GB\r\n return PerformanceTier.HIGH;\r\n } else if (maxStorageBufferBindingSize >= 256 * 1024 * 1024) {\r\n // 256MB\r\n return PerformanceTier.MEDIUM;\r\n }\r\n return PerformanceTier.LOW;\r\n}\r\n\r\n/**\r\n * 移动端优化配置\r\n */\r\nexport interface MobileOptimizationConfig {\r\n // 最大渲染 splat 数量\r\n maxVisibleSplats: number;\r\n // 是否启用排序(关闭可大幅提升性能)\r\n enableSorting: boolean;\r\n // 排序频率(每 N 帧排序一次)\r\n sortEveryNFrames: number;\r\n // 是否使用紧凑数据格式\r\n useCompactFormat: boolean;\r\n // 像素剔除阈值(更大的值剔除更多小 splat)\r\n pixelCullThreshold: number;\r\n // SH 模式(移动端建议 L0)\r\n defaultSHMode: SHMode;\r\n}\r\n\r\n// ============================================\r\n// L0 Shader (仅 DC 颜色,无 SH 计算 - 高性能模式)\r\n// ============================================\r\nconst shaderCodeL0 = /* wgsl */ `\r\nstruct Uniforms {\r\n view: mat4x4<f32>,\r\n proj: mat4x4<f32>,\r\n model: mat4x4<f32>,\r\n cameraPos: vec3<f32>,\r\n _pad: f32,\r\n screenSize: vec2<f32>,\r\n _pad2: vec2<f32>,\r\n}\r\n\r\nstruct Splat {\r\n mean: vec3<f32>,\r\n _pad0: f32,\r\n scale: vec3<f32>,\r\n _pad1: f32,\r\n rotation: vec4<f32>,\r\n colorDC: vec3<f32>,\r\n opacity: f32,\r\n sh1: array<f32, 9>,\r\n sh2: array<f32, 15>,\r\n sh3: array<f32, 21>,\r\n _pad2: array<f32, 3>,\r\n}\r\n\r\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\r\n@group(0) @binding(1) var<storage, read> splats: array<Splat>;\r\n@group(0) @binding(2) var<storage, read> sortedIndices: array<u32>;\r\n\r\nstruct VertexOutput {\r\n @builtin(position) position: vec4<f32>,\r\n @location(0) localUV: vec2<f32>,\r\n @location(1) color: vec3<f32>,\r\n @location(2) opacity: f32,\r\n}\r\n\r\nconst QUAD_POSITIONS = array<vec2<f32>, 4>(\r\n vec2<f32>(-1.0, -1.0),\r\n vec2<f32>( 1.0, -1.0),\r\n vec2<f32>(-1.0, 1.0),\r\n vec2<f32>( 1.0, 1.0),\r\n);\r\n\r\n// 椭圆缩放因子: 控制高斯 splat 的渲染范围\r\n// 3.0 = 3σ 覆盖 99.7%,更大的值会让 splat 更大更柔和\r\nconst ELLIPSE_SCALE: f32 = 3.0;\r\n// 高斯衰减系数: 在 r=1 (即 3σ) 处衰减到 exp(-4.5) ≈ 0.011\r\nconst GAUSSIAN_DECAY: f32 = 4.5;\r\n// SH L0 常数: sqrt(1/(4*pi)) - 用于 DC 颜色计算\r\nconst SH_C0: f32 = 0.28209479177387814;\r\n\r\nfn quatToMat3(q: vec4<f32>) -> mat3x3<f32> {\r\n let w = q[0]; let x = q[1]; let y = q[2]; let z = q[3];\r\n let x2 = x + x; let y2 = y + y; let z2 = z + z;\r\n let xx = x * x2; let xy = x * y2; let xz = x * z2;\r\n let yy = y * y2; let yz = y * z2; let zz = z * z2;\r\n let wx = w * x2; let wy = w * y2; let wz = w * z2;\r\n return mat3x3<f32>(\r\n vec3<f32>(1.0 - (yy + zz), xy + wz, xz - wy),\r\n vec3<f32>(xy - wz, 1.0 - (xx + zz), yz + wx),\r\n vec3<f32>(xz + wy, yz - wx, 1.0 - (xx + yy))\r\n );\r\n}\r\n\r\n// 从模型矩阵提取三轴缩放因子\r\nfn getModelScale3(model: mat4x4<f32>) -> vec3<f32> {\r\n return vec3<f32>(\r\n length(model[0].xyz),\r\n length(model[1].xyz),\r\n length(model[2].xyz)\r\n );\r\n}\r\n\r\nfn computeCov2D(mean: vec3<f32>, scale: vec3<f32>, rotation: vec4<f32>, modelView: mat4x4<f32>, proj: mat4x4<f32>, modelScale: vec3<f32>) -> vec3<f32> {\r\n let R = quatToMat3(rotation);\r\n // 应用模型缩放到 splat scale (支持非均匀缩放)\r\n let scaledScale = scale * modelScale;\r\n let s2 = scaledScale * scaledScale;\r\n // 构建协方差矩阵 Sigma = R * diag(s²) * R^T\r\n let M = mat3x3<f32>(R[0] * s2.x, R[1] * s2.y, R[2] * s2.z);\r\n let Sigma = M * transpose(R);\r\n \r\n let viewPos = (modelView * vec4<f32>(mean, 1.0)).xyz;\r\n let viewRot = mat3x3<f32>(modelView[0].xyz, modelView[1].xyz, modelView[2].xyz);\r\n let SigmaView = viewRot * Sigma * transpose(viewRot);\r\n \r\n let fx = proj[0][0]; let fy = proj[1][1];\r\n let z = -viewPos.z;\r\n let z_clamped = max(z, 0.001);\r\n let z2 = z_clamped * z_clamped;\r\n \r\n // 雅可比矩阵: 从相机坐标到 NDC 的偏导数\r\n // 对于 WebGPU (相机看向 -Z): x_ndc = fx * x_cam / (-z_cam)\r\n let j1 = vec3<f32>(fx / z_clamped, 0.0, fx * viewPos.x / z2);\r\n let j2 = vec3<f32>(0.0, fy / z_clamped, fy * viewPos.y / z2);\r\n \r\n // 投影协方差: J * Sigma * J^T (只需要 2x2 上三角)\r\n let Sj1 = SigmaView * j1;\r\n let Sj2 = SigmaView * j2;\r\n return vec3<f32>(dot(j1, Sj1), dot(j1, Sj2), dot(j2, Sj2));\r\n}\r\n\r\nfn computeEllipseAxes(cov2D: vec3<f32>) -> mat2x2<f32> {\r\n let a = cov2D.x; let b = cov2D.y; let c = cov2D.z;\r\n let trace = a + c;\r\n let det = a * c - b * b;\r\n let disc = trace * trace - 4.0 * det;\r\n let sqrtDisc = sqrt(max(disc, 0.0));\r\n let lambda1 = max((trace + sqrtDisc) * 0.5, 0.0);\r\n let lambda2 = max((trace - sqrtDisc) * 0.5, 0.0);\r\n let r1 = sqrt(lambda1);\r\n let r2 = sqrt(lambda2);\r\n \r\n var axis1: vec2<f32>; var axis2: vec2<f32>;\r\n // 改进的数值稳定性: 检查特征向量是否可靠\r\n let eigenvecLen = abs(b) + abs(lambda1 - a);\r\n if (eigenvecLen > 1e-6) {\r\n axis1 = normalize(vec2<f32>(b, lambda1 - a));\r\n axis2 = vec2<f32>(-axis1.y, axis1.x);\r\n } else {\r\n // 退化情况: 使用标准轴\r\n if (a >= c) { axis1 = vec2<f32>(1.0, 0.0); axis2 = vec2<f32>(0.0, 1.0); }\r\n else { axis1 = vec2<f32>(0.0, 1.0); axis2 = vec2<f32>(1.0, 0.0); }\r\n }\r\n return mat2x2<f32>(axis1 * r1, axis2 * r2);\r\n}\r\n\r\n@vertex\r\nfn vs_main(@builtin(vertex_index) vertexIndex: u32, @builtin(instance_index) instanceIndex: u32) -> VertexOutput {\r\n var output: VertexOutput;\r\n let splatIndex = sortedIndices[instanceIndex];\r\n let splat = splats[splatIndex];\r\n let quadPos = QUAD_POSITIONS[vertexIndex];\r\n output.localUV = quadPos;\r\n \r\n // 计算 modelView 矩阵和模型缩放\r\n let modelView = uniforms.view * uniforms.model;\r\n let modelScale = getModelScale3(uniforms.model);\r\n \r\n let cov2D = computeCov2D(splat.mean, splat.scale, splat.rotation, modelView, uniforms.proj, modelScale);\r\n let axes = computeEllipseAxes(cov2D);\r\n let screenOffset = axes[0] * quadPos.x * ELLIPSE_SCALE + axes[1] * quadPos.y * ELLIPSE_SCALE;\r\n \r\n // 应用 model 变换到 splat 位置\r\n let worldPos = uniforms.model * vec4<f32>(splat.mean, 1.0);\r\n let viewPos = uniforms.view * worldPos;\r\n var clipPos = uniforms.proj * viewPos;\r\n clipPos.x = clipPos.x + screenOffset.x * clipPos.w;\r\n clipPos.y = clipPos.y + screenOffset.y * clipPos.w;\r\n output.position = clipPos;\r\n // DC 颜色已经在 PLYLoader 中预计算: colorDC = 0.5 + SH_C0 * f_dc\r\n output.color = splat.colorDC;\r\n output.opacity = splat.opacity;\r\n \r\n return output;\r\n}\r\n\r\n@fragment\r\nfn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {\r\n let r = length(input.localUV);\r\n if (r > 1.0) { discard; }\r\n let gaussianWeight = exp(-r * r * GAUSSIAN_DECAY);\r\n let alpha = input.opacity * gaussianWeight;\r\n if (alpha < 0.004) { discard; }\r\n let color = clamp(input.color, vec3<f32>(0.0), vec3<f32>(1.0));\r\n return vec4<f32>(color * alpha, alpha);\r\n}\r\n`;\r\n\r\n// ============================================\r\n// L1 Shader (DC + L1 SH)\r\n// ============================================\r\nconst shaderCodeL1 = /* wgsl */ `\r\nstruct Uniforms {\r\n view: mat4x4<f32>,\r\n proj: mat4x4<f32>,\r\n model: mat4x4<f32>,\r\n cameraPos: vec3<f32>,\r\n _pad: f32,\r\n screenSize: vec2<f32>,\r\n _pad2: vec2<f32>,\r\n}\r\n\r\nstruct Splat {\r\n mean: vec3<f32>,\r\n _pad0: f32,\r\n scale: vec3<f32>,\r\n _pad1: f32,\r\n rotation: vec4<f32>,\r\n colorDC: vec3<f32>,\r\n opacity: f32,\r\n sh1: array<f32, 9>,\r\n sh2: array<f32, 15>,\r\n sh3: array<f32, 21>,\r\n _pad2: array<f32, 3>,\r\n}\r\n\r\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\r\n@group(0) @binding(1) var<storage, read> splats: array<Splat>;\r\n@group(0) @binding(2) var<storage, read> sortedIndices: array<u32>;\r\n\r\nstruct VertexOutput {\r\n @builtin(position) position: vec4<f32>,\r\n @location(0) localUV: vec2<f32>,\r\n @location(1) color: vec3<f32>,\r\n @location(2) opacity: f32,\r\n}\r\n\r\nconst QUAD_POSITIONS = array<vec2<f32>, 4>(\r\n vec2<f32>(-1.0, -1.0),\r\n vec2<f32>( 1.0, -1.0),\r\n vec2<f32>(-1.0, 1.0),\r\n vec2<f32>( 1.0, 1.0),\r\n);\r\n\r\nconst ELLIPSE_SCALE: f32 = 3.0;\r\nconst GAUSSIAN_DECAY: f32 = 4.5;\r\n\r\n// SH 常数\r\nconst SH_C0: f32 = 0.28209479177387814; // sqrt(1/(4*pi)) - DC 颜色\r\nconst SH_C1: f32 = 0.4886025119029199; // sqrt(3/(4*pi)) - L1\r\n\r\n// 计算 L1 球谐函数贡献\r\n// 数据布局 (按基函数分组): [basis0_R, basis0_G, basis0_B, basis1_R, basis1_G, basis1_B, ...]\r\n// 即 sh1[0..2] = 第一个基函数的 RGB, sh1[3..5] = 第二个基函数的 RGB, sh1[6..8] = 第三个基函数的 RGB\r\n// 原始 3DGS 公式: result = SH_C1 * (-sh[0] * y + sh[1] * z - sh[2] * x)\r\n// 其中 sh[0], sh[1], sh[2] 是 vec3 (RGB)\r\nfn evalSH1(dir: vec3<f32>, sh1: array<f32, 9>) -> vec3<f32> {\r\n let x = dir.x;\r\n let y = dir.y;\r\n let z = dir.z;\r\n \r\n // sh[0] = vec3(sh1[0], sh1[1], sh1[2]) - 第一个基函数 (Y_1^{-1})\r\n // sh[1] = vec3(sh1[3], sh1[4], sh1[5]) - 第二个基函数 (Y_1^0)\r\n // sh[2] = vec3(sh1[6], sh1[7], sh1[8]) - 第三个基函数 (Y_1^1)\r\n let sh0 = vec3<f32>(sh1[0], sh1[1], sh1[2]);\r\n let sh1_1 = vec3<f32>(sh1[3], sh1[4], sh1[5]);\r\n let sh2 = vec3<f32>(sh1[6], sh1[7], sh1[8]);\r\n \r\n return SH_C1 * (-sh0 * y + sh1_1 * z - sh2 * x);\r\n}\r\n\r\nfn quatToMat3(q: vec4<f32>) -> mat3x3<f32> {\r\n let w = q[0]; let x = q[1]; let y = q[2]; let z = q[3];\r\n let x2 = x + x; let y2 = y + y; let z2 = z + z;\r\n let xx = x * x2; let xy = x * y2; let xz = x * z2;\r\n let yy = y * y2; let yz = y * z2; let zz = z * z2;\r\n let wx = w * x2; let wy = w * y2; let wz = w * z2;\r\n return mat3x3<f32>(\r\n vec3<f32>(1.0 - (yy + zz), xy + wz, xz - wy),\r\n vec3<f32>(xy - wz, 1.0 - (xx + zz), yz + wx),\r\n vec3<f32>(xz + wy, yz - wx, 1.0 - (xx + yy))\r\n );\r\n}\r\n\r\nfn getModelScale3(model: mat4x4<f32>) -> vec3<f32> {\r\n return vec3<f32>(\r\n length(model[0].xyz),\r\n length(model[1].xyz),\r\n length(model[2].xyz)\r\n );\r\n}\r\n\r\nfn computeCov2D(mean: vec3<f32>, scale: vec3<f32>, rotation: vec4<f32>, modelView: mat4x4<f32>, proj: mat4x4<f32>, modelScale: vec3<f32>) -> vec3<f32> {\r\n let R = quatToMat3(rotation);\r\n let scaledScale = scale * modelScale;\r\n let s2 = scaledScale * scaledScale;\r\n let M = mat3x3<f32>(R[0] * s2.x, R[1] * s2.y, R[2] * s2.z);\r\n let Sigma = M * transpose(R);\r\n \r\n let viewPos = (modelView * vec4<f32>(mean, 1.0)).xyz;\r\n let viewRot = mat3x3<f32>(modelView[0].xyz, modelView[1].xyz, modelView[2].xyz);\r\n let SigmaView = viewRot * Sigma * transpose(viewRot);\r\n \r\n let fx = proj[0][0]; let fy = proj[1][1];\r\n let z = -viewPos.z;\r\n let z_clamped = max(z, 0.001);\r\n let z2 = z_clamped * z_clamped;\r\n \r\n let j1 = vec3<f32>(fx / z_clamped, 0.0, fx * viewPos.x / z2);\r\n let j2 = vec3<f32>(0.0, fy / z_clamped, fy * viewPos.y / z2);\r\n let Sj1 = SigmaView * j1;\r\n let Sj2 = SigmaView * j2;\r\n return vec3<f32>(dot(j1, Sj1), dot(j1, Sj2), dot(j2, Sj2));\r\n}\r\n\r\nfn computeEllipseAxes(cov2D: vec3<f32>) -> mat2x2<f32> {\r\n let a = cov2D.x; let b = cov2D.y; let c = cov2D.z;\r\n let trace = a + c;\r\n let det = a * c - b * b;\r\n let disc = trace * trace - 4.0 * det;\r\n let sqrtDisc = sqrt(max(disc, 0.0));\r\n let lambda1 = max((trace + sqrtDisc) * 0.5, 0.0);\r\n let lambda2 = max((trace - sqrtDisc) * 0.5, 0.0);\r\n let r1 = sqrt(lambda1);\r\n let r2 = sqrt(lambda2);\r\n \r\n var axis1: vec2<f32>; var axis2: vec2<f32>;\r\n let eigenvecLen = abs(b) + abs(lambda1 - a);\r\n if (eigenvecLen > 1e-6) {\r\n axis1 = normalize(vec2<f32>(b, lambda1 - a));\r\n axis2 = vec2<f32>(-axis1.y, axis1.x);\r\n } else {\r\n if (a >= c) { axis1 = vec2<f32>(1.0, 0.0); axis2 = vec2<f32>(0.0, 1.0); }\r\n else { axis1 = vec2<f32>(0.0, 1.0); axis2 = vec2<f32>(1.0, 0.0); }\r\n }\r\n return mat2x2<f32>(axis1 * r1, axis2 * r2);\r\n}\r\n\r\n@vertex\r\nfn vs_main(@builtin(vertex_index) vertexIndex: u32, @builtin(instance_index) instanceIndex: u32) -> VertexOutput {\r\n var output: VertexOutput;\r\n let splatIndex = sortedIndices[instanceIndex];\r\n let splat = splats[splatIndex];\r\n let quadPos = QUAD_POSITIONS[vertexIndex];\r\n output.localUV = quadPos;\r\n \r\n let modelView = uniforms.view * uniforms.model;\r\n let modelScale = getModelScale3(uniforms.model);\r\n \r\n let cov2D = computeCov2D(splat.mean, splat.scale, splat.rotation, modelView, uniforms.proj, modelScale);\r\n let axes = computeEllipseAxes(cov2D);\r\n let screenOffset = axes[0] * quadPos.x * ELLIPSE_SCALE + axes[1] * quadPos.y * ELLIPSE_SCALE;\r\n \r\n let worldPos = uniforms.model * vec4<f32>(splat.mean, 1.0);\r\n let viewPos = uniforms.view * worldPos;\r\n var clipPos = uniforms.proj * viewPos;\r\n clipPos.x = clipPos.x + screenOffset.x * clipPos.w;\r\n clipPos.y = clipPos.y + screenOffset.y * clipPos.w;\r\n output.position = clipPos;\r\n \r\n // SH 计算 - 使用局部坐标系中的方向\r\n // 将相机位置转换到局部坐标系\r\n // cam_local = A^{-1} * (cam_world - t) = (A^T / s^2) * (cam_world - t)\r\n // 其中 A 是模型矩阵的 3x3 部分,s^2 是缩放的平方\r\n let A = mat3x3<f32>(\r\n uniforms.model[0].xyz,\r\n uniforms.model[1].xyz,\r\n uniforms.model[2].xyz\r\n );\r\n let modelTranslation = uniforms.model[3].xyz;\r\n let s2 = max(1e-12, (dot(A[0], A[0]) + dot(A[1], A[1]) + dot(A[2], A[2])) / 3.0);\r\n let camLocal = (transpose(A) * (uniforms.cameraPos - modelTranslation)) / s2;\r\n // 方向:从相机指向 splat(与 visionary 一致)\r\n let dirLocal = normalize(splat.mean - camLocal);\r\n \r\n let shColor = evalSH1(dirLocal, splat.sh1);\r\n // DC 颜色已经在 PLYLoader 中预计算,这里只加 SH 贡献\r\n // 使用 max 确保颜色不为负(与 visionary 一致)\r\n output.color = max(vec3<f32>(0.0), splat.colorDC + shColor);\r\n output.opacity = splat.opacity;\r\n \r\n return output;\r\n}\r\n\r\n@fragment\r\nfn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {\r\n let r = length(input.localUV);\r\n if (r > 1.0) { discard; }\r\n let gaussianWeight = exp(-r * r * GAUSSIAN_DECAY);\r\n let alpha = input.opacity * gaussianWeight;\r\n if (alpha < 0.004) { discard; }\r\n let color = clamp(input.color, vec3<f32>(0.0), vec3<f32>(1.0));\r\n return vec4<f32>(color * alpha, alpha);\r\n}\r\n`;\r\n\r\n// ============================================\r\n// L2 Shader (DC + L1 + L2 SH)\r\n// ============================================\r\nconst shaderCodeL2 = /* wgsl */ `\r\nstruct Uniforms {\r\n view: mat4x4<f32>,\r\n proj: mat4x4<f32>,\r\n model: mat4x4<f32>,\r\n cameraPos: vec3<f32>,\r\n _pad: f32,\r\n screenSize: vec2<f32>,\r\n _pad2: vec2<f32>,\r\n}\r\n\r\nstruct Splat {\r\n mean: vec3<f32>,\r\n _pad0: f32,\r\n scale: vec3<f32>,\r\n _pad1: f32,\r\n rotation: vec4<f32>,\r\n colorDC: vec3<f32>,\r\n opacity: f32,\r\n sh1: array<f32, 9>,\r\n sh2: array<f32, 15>,\r\n sh3: array<f32, 21>,\r\n _pad2: array<f32, 3>,\r\n}\r\n\r\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\r\n@group(0) @binding(1) var<storage, read> splats: array<Splat>;\r\n@group(0) @binding(2) var<storage, read> sortedIndices: array<u32>;\r\n\r\nstruct VertexOutput {\r\n @builtin(position) position: vec4<f32>,\r\n @location(0) localUV: vec2<f32>,\r\n @location(1) color: vec3<f32>,\r\n @location(2) opacity: f32,\r\n}\r\n\r\nconst QUAD_POSITIONS = array<vec2<f32>, 4>(\r\n vec2<f32>(-1.0, -1.0),\r\n vec2<f32>( 1.0, -1.0),\r\n vec2<f32>(-1.0, 1.0),\r\n vec2<f32>( 1.0, 1.0),\r\n);\r\n\r\nconst ELLIPSE_SCALE: f32 = 3.0;\r\nconst GAUSSIAN_DECAY: f32 = 4.5;\r\n\r\n// SH 常数\r\nconst SH_C0: f32 = 0.28209479177387814; // sqrt(1/(4*pi)) - DC 颜色\r\nconst SH_C1: f32 = 0.4886025119029199; // sqrt(3/(4*pi)) - L1\r\nconst SH_C2_0: f32 = 1.0925484305920792;\r\nconst SH_C2_1: f32 = -1.0925484305920792;\r\nconst SH_C2_2: f32 = 0.31539156525252005;\r\nconst SH_C2_3: f32 = -1.0925484305920792;\r\nconst SH_C2_4: f32 = 0.5462742152960396;\r\n\r\n// 数据布局 (按基函数分组): [basis0_RGB, basis1_RGB, basis2_RGB]\r\nfn evalSH1(dir: vec3<f32>, sh1: array<f32, 9>) -> vec3<f32> {\r\n let x = dir.x;\r\n let y = dir.y;\r\n let z = dir.z;\r\n let sh0 = vec3<f32>(sh1[0], sh1[1], sh1[2]);\r\n let sh1_1 = vec3<f32>(sh1[3], sh1[4], sh1[5]);\r\n let sh2 = vec3<f32>(sh1[6], sh1[7], sh1[8]);\r\n return SH_C1 * (-sh0 * y + sh1_1 * z - sh2 * x);\r\n}\r\n\r\n// 数据布局 (按基函数分组): [basis0_RGB, basis1_RGB, ..., basis4_RGB]\r\nfn evalSH2(dir: vec3<f32>, sh2: array<f32, 15>) -> vec3<f32> {\r\n let x = dir.x; let y = dir.y; let z = dir.z;\r\n let xx = x * x; let yy = y * y; let zz = z * z;\r\n let xy = x * y; let yz = y * z; let xz = x * z;\r\n\r\n // L2 基函数\r\n let b0 = SH_C2_0 * xy;\r\n let b1 = SH_C2_1 * yz;\r\n let b2 = SH_C2_2 * (2.0 * zz - xx - yy);\r\n let b3 = SH_C2_3 * xz;\r\n let b4 = SH_C2_4 * (xx - yy);\r\n\r\n // 数据按基函数分组: sh2[0..2]=basis0_RGB, sh2[3..5]=basis1_RGB, ...\r\n let c0 = vec3<f32>(sh2[0], sh2[1], sh2[2]);\r\n let c1 = vec3<f32>(sh2[3], sh2[4], sh2[5]);\r\n let c2 = vec3<f32>(sh2[6], sh2[7], sh2[8]);\r\n let c3 = vec3<f32>(sh2[9], sh2[10], sh2[11]);\r\n let c4 = vec3<f32>(sh2[12], sh2[13], sh2[14]);\r\n \r\n return c0 * b0 + c1 * b1 + c2 * b2 + c3 * b3 + c4 * b4;\r\n}\r\n\r\nfn quatToMat3(q: vec4<f32>) -> mat3x3<f32> {\r\n let w = q[0]; let x = q[1]; let y = q[2]; let z = q[3];\r\n let x2 = x + x; let y2 = y + y; let z2 = z + z;\r\n let xx = x * x2; let xy = x * y2; let xz = x * z2;\r\n let yy = y * y2; let yz = y * z2; let zz = z * z2;\r\n let wx = w * x2; let wy = w * y2; let wz = w * z2;\r\n return mat3x3<f32>(\r\n vec3<f32>(1.0 - (yy + zz), xy + wz, xz - wy),\r\n vec3<f32>(xy - wz, 1.0 - (xx + zz), yz + wx),\r\n vec3<f32>(xz + wy, yz - wx, 1.0 - (xx + yy))\r\n );\r\n}\r\n\r\nfn getModelScale3(model: mat4x4<f32>) -> vec3<f32> {\r\n return vec3<f32>(\r\n length(model[0].xyz),\r\n length(model[1].xyz),\r\n length(model[2].xyz)\r\n );\r\n}\r\n\r\nfn computeCov2D(mean: vec3<f32>, scale: vec3<f32>, rotation: vec4<f32>, modelView: mat4x4<f32>, proj: mat4x4<f32>, modelScale: vec3<f32>) -> vec3<f32> {\r\n let R = quatToMat3(rotation);\r\n let scaledScale = scale * modelScale;\r\n let s2 = scaledScale * scaledScale;\r\n let M = mat3x3<f32>(R[0] * s2.x, R[1] * s2.y, R[2] * s2.z);\r\n let Sigma = M * transpose(R);\r\n \r\n let viewPos = (modelView * vec4<f32>(mean, 1.0)).xyz;\r\n let viewRot = mat3x3<f32>(modelView[0].xyz, modelView[1].xyz, modelView[2].xyz);\r\n let SigmaView = viewRot * Sigma * transpose(viewRot);\r\n \r\n let fx = proj[0][0]; let fy = proj[1][1];\r\n let z = -viewPos.z;\r\n let z_clamped = max(z, 0.001);\r\n let z2 = z_clamped * z_clamped;\r\n \r\n let j1 = vec3<f32>(fx / z_clamped, 0.0, fx * viewPos.x / z2);\r\n let j2 = vec3<f32>(0.0, fy / z_clamped, fy * viewPos.y / z2);\r\n let Sj1 = SigmaView * j1;\r\n let Sj2 = SigmaView * j2;\r\n return vec3<f32>(dot(j1, Sj1), dot(j1, Sj2), dot(j2, Sj2));\r\n}\r\n\r\nfn computeEllipseAxes(cov2D: vec3<f32>) -> mat2x2<f32> {\r\n let a = cov2D.x; let b = cov2D.y; let c = cov2D.z;\r\n let trace = a + c;\r\n let det = a * c - b * b;\r\n let disc = trace * trace - 4.0 * det;\r\n let sqrtDisc = sqrt(max(disc, 0.0));\r\n let lambda1 = max((trace + sqrtDisc) * 0.5, 0.0);\r\n let lambda2 = max((trace - sqrtDisc) * 0.5, 0.0);\r\n let r1 = sqrt(lambda1);\r\n let r2 = sqrt(lambda2);\r\n \r\n var axis1: vec2<f32>; var axis2: vec2<f32>;\r\n let eigenvecLen = abs(b) + abs(lambda1 - a);\r\n if (eigenvecLen > 1e-6) {\r\n axis1 = normalize(vec2<f32>(b, lambda1 - a));\r\n axis2 = vec2<f32>(-axis1.y, axis1.x);\r\n } else {\r\n if (a >= c) { axis1 = vec2<f32>(1.0, 0.0); axis2 = vec2<f32>(0.0, 1.0); }\r\n else { axis1 = vec2<f32>(0.0, 1.0); axis2 = vec2<f32>(1.0, 0.0); }\r\n }\r\n return mat2x2<f32>(axis1 * r1, axis2 * r2);\r\n}\r\n\r\n@vertex\r\nfn vs_main(@builtin(vertex_index) vertexIndex: u32, @builtin(instance_index) instanceIndex: u32) -> VertexOutput {\r\n var output: VertexOutput;\r\n let splatIndex = sortedIndices[instanceIndex];\r\n let splat = splats[splatIndex];\r\n let quadPos = QUAD_POSITIONS[vertexIndex];\r\n output.localUV = quadPos;\r\n \r\n let modelView = uniforms.view * uniforms.model;\r\n let modelScale = getModelScale3(uniforms.model);\r\n \r\n let cov2D = computeCov2D(splat.mean, splat.scale, splat.rotation, modelView, uniforms.proj, modelScale);\r\n let axes = computeEllipseAxes(cov2D);\r\n let screenOffset = axes[0] * quadPos.x * ELLIPSE_SCALE + axes[1] * quadPos.y * ELLIPSE_SCALE;\r\n \r\n let worldPos = uniforms.model * vec4<f32>(splat.mean, 1.0);\r\n let viewPos = uniforms.view * worldPos;\r\n var clipPos = uniforms.proj * viewPos;\r\n clipPos.x = clipPos.x + screenOffset.x * clipPos.w;\r\n clipPos.y = clipPos.y + screenOffset.y * clipPos.w;\r\n output.position = clipPos;\r\n \r\n // SH 计算 - 使用局部坐标系中的方向 (与 visionary 一致)\r\n let A = mat3x3<f32>(\r\n uniforms.model[0].xyz,\r\n uniforms.model[1].xyz,\r\n uniforms.model[2].xyz\r\n );\r\n let modelTranslation = uniforms.model[3].xyz;\r\n let s2 = max(1e-12, (dot(A[0], A[0]) + dot(A[1], A[1]) + dot(A[2], A[2])) / 3.0);\r\n let camLocal = (transpose(A) * (uniforms.cameraPos - modelTranslation)) / s2;\r\n let dirLocal = normalize(splat.mean - camLocal);\r\n \r\n let shColor1 = evalSH1(dirLocal, splat.sh1);\r\n let shColor2 = evalSH2(dirLocal, splat.sh2);\r\n output.color = max(vec3<f32>(0.0), splat.colorDC + shColor1 + shColor2);\r\n output.opacity = splat.opacity;\r\n \r\n return output;\r\n}\r\n\r\n@fragment\r\nfn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {\r\n let r = length(input.localUV);\r\n if (r > 1.0) { discard; }\r\n let gaussianWeight = exp(-r * r * GAUSSIAN_DECAY);\r\n let alpha = input.opacity * gaussianWeight;\r\n if (alpha < 0.004) { discard; }\r\n let color = clamp(input.color, vec3<f32>(0.0), vec3<f32>(1.0));\r\n return vec4<f32>(color * alpha, alpha);\r\n}\r\n`;\r\n\r\n// ============================================\r\n// L3 Shader (DC + L1 + L2 + L3 SH - 完整)\r\n// ============================================\r\nconst shaderCodeL3 = /* wgsl */ `\r\nstruct Uniforms {\r\n view: mat4x4<f32>,\r\n proj: mat4x4<f32>,\r\n model: mat4x4<f32>,\r\n cameraPos: vec3<f32>,\r\n _pad: f32,\r\n screenSize: vec2<f32>,\r\n _pad2: vec2<f32>,\r\n}\r\n\r\nstruct Splat {\r\n mean: vec3<f32>,\r\n _pad0: f32,\r\n scale: vec3<f32>,\r\n _pad1: f32,\r\n rotation: vec4<f32>,\r\n colorDC: vec3<f32>,\r\n opacity: f32,\r\n sh1: array<f32, 9>,\r\n sh2: array<f32, 15>,\r\n sh3: array<f32, 21>,\r\n _pad2: array<f32, 3>,\r\n}\r\n\r\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\r\n@group(0) @binding(1) var<storage, read> splats: array<Splat>;\r\n@group(0) @binding(2) var<storage, read> sortedIndices: array<u32>;\r\n\r\nstruct VertexOutput {\r\n @builtin(position) position: vec4<f32>,\r\n @location(0) localUV: vec2<f32>,\r\n @location(1) color: vec3<f32>,\r\n @location(2) opacity: f32,\r\n}\r\n\r\nconst QUAD_POSITIONS = array<vec2<f32>, 4>(\r\n vec2<f32>(-1.0, -1.0),\r\n vec2<f32>( 1.0, -1.0),\r\n vec2<f32>(-1.0, 1.0),\r\n vec2<f32>( 1.0, 1.0),\r\n);\r\n\r\nconst ELLIPSE_SCALE: f32 = 3.0;\r\nconst GAUSSIAN_DECAY: f32 = 4.5;\r\n\r\n// SH 常数\r\nconst SH_C0: f32 = 0.28209479177387814; // sqrt(1/(4*pi)) - DC 颜色\r\nconst SH_C1: f32 = 0.4886025119029199; // sqrt(3/(4*pi)) - L1\r\nconst SH_C2_0: f32 = 1.0925484305920792;\r\nconst SH_C2_1: f32 = -1.0925484305920792;\r\nconst SH_C2_2: f32 = 0.31539156525252005;\r\nconst SH_C2_3: f32 = -1.0925484305920792;\r\nconst SH_C2_4: f32 = 0.5462742152960396;\r\nconst SH_C3_0: f32 = -0.5900435899266435;\r\nconst SH_C3_1: f32 = 2.890611442640554;\r\nconst SH_C3_2: f32 = -0.4570457994644658;\r\nconst SH_C3_3: f32 = 0.3731763325901154;\r\nconst SH_C3_4: f32 = -0.4570457994644658;\r\nconst SH_C3_5: f32 = 1.445305721320277;\r\nconst SH_C3_6: f32 = -0.5900435899266435;\r\n\r\n// 数据布局 (按基函数分组): [basis0_RGB, basis1_RGB, basis2_RGB]\r\nfn evalSH1(dir: vec3<f32>, sh1: array<f32, 9>) -> vec3<f32> {\r\n let x = dir.x;\r\n let y = dir.y;\r\n let z = dir.z;\r\n let sh0 = vec3<f32>(sh1[0], sh1[1], sh1[2]);\r\n let sh1_1 = vec3<f32>(sh1[3], sh1[4], sh1[5]);\r\n let sh2 = vec3<f32>(sh1[6], sh1[7], sh1[8]);\r\n return SH_C1 * (-sh0 * y + sh1_1 * z - sh2 * x);\r\n}\r\n\r\n// 数据布局 (按基函数分组): [basis0_RGB, ..., basis4_RGB]\r\nfn evalSH2(dir: vec3<f32>, sh2: array<f32, 15>) -> vec3<f32> {\r\n let x = dir.x; let y = dir.y; let z = dir.z;\r\n let xx = x * x; let yy = y * y; let zz = z * z;\r\n let xy = x * y; let yz = y * z; let xz = x * z;\r\n\r\n let b0 = SH_C2_0 * xy;\r\n let b1 = SH_C2_1 * yz;\r\n let b2 = SH_C2_2 * (2.0 * zz - xx - yy);\r\n let b3 = SH_C2_3 * xz;\r\n let b4 = SH_C2_4 * (xx - yy);\r\n\r\n let c0 = vec3<f32>(sh2[0], sh2[1], sh2[2]);\r\n let c1 = vec3<f32>(sh2[3], sh2[4], sh2[5]);\r\n let c2 = vec3<f32>(sh2[6], sh2[7], sh2[8]);\r\n let c3 = vec3<f32>(sh2[9], sh2[10], sh2[11]);\r\n let c4 = vec3<f32>(sh2[12], sh2[13], sh2[14]);\r\n \r\n return c0 * b0 + c1 * b1 + c2 * b2 + c3 * b3 + c4 * b4;\r\n}\r\n\r\n// 数据布局 (按基函数分组): [basis0_RGB, ..., basis6_RGB]\r\nfn evalSH3(dir: vec3<f32>, sh3: array<f32, 21>) -> vec3<f32> {\r\n let x = dir.x; let y = dir.y; let z = dir.z;\r\n let xx = x * x; let yy = y * y; let zz = z * z;\r\n let xy = x * y; let yz = y * z; let xz = x * z;\r\n\r\n let b0 = SH_C3_0 * y * (3.0 * xx - yy);\r\n let b1 = SH_C3_1 * xy * z;\r\n let b2 = SH_C3_2 * y * (4.0 * zz - xx - yy);\r\n let b3 = SH_C3_3 * z * (2.0 * zz - 3.0 * xx - 3.0 * yy);\r\n let b4 = SH_C3_4 * x * (4.0 * zz - xx - yy);\r\n let b5 = SH_C3_5 * z * (xx - yy);\r\n let b6 = SH_C3_6 * x * (xx - 3.0 * yy);\r\n\r\n let c0 = vec3<f32>(sh3[0], sh3[1], sh3[2]);\r\n let c1 = vec3<f32>(sh3[3], sh3[4], sh3[5]);\r\n let c2 = vec3<f32>(sh3[6], sh3[7], sh3[8]);\r\n let c3 = vec3<f32>(sh3[9], sh3[10], sh3[11]);\r\n let c4 = vec3<f32>(sh3[12], sh3[13], sh3[14]);\r\n let c5 = vec3<f32>(sh3[15], sh3[16], sh3[17]);\r\n let c6 = vec3<f32>(sh3[18], sh3[19], sh3[20]);\r\n \r\n return c0 * b0 + c1 * b1 + c2 * b2 + c3 * b3 + c4 * b4 + c5 * b5 + c6 * b6;\r\n}\r\n\r\nfn quatToMat3(q: vec4<f32>) -> mat3x3<f32> {\r\n let w = q[0]; let x = q[1]; let y = q[2]; let z = q[3];\r\n let x2 = x + x; let y2 = y + y; let z2 = z + z;\r\n let xx = x * x2; let xy = x * y2; let xz = x * z2;\r\n let yy = y * y2; let yz = y * z2; let zz = z * z2;\r\n let wx = w * x2; let wy = w * y2; let wz = w * z2;\r\n return mat3x3<f32>(\r\n vec3<f32>(1.0 - (yy + zz), xy + wz, xz - wy),\r\n vec3<f32>(xy - wz, 1.0 - (xx + zz), yz + wx),\r\n vec3<f32>(xz + wy, yz - wx, 1.0 - (xx + yy))\r\n );\r\n}\r\n\r\nfn getModelScale3(model: mat4x4<f32>) -> vec3<f32> {\r\n return vec3<f32>(\r\n length(model[0].xyz),\r\n length(model[1].xyz),\r\n length(model[2].xyz)\r\n );\r\n}\r\n\r\nfn computeCov2D(mean: vec3<f32>, scale: vec3<f32>, rotation: vec4<f32>, modelView: mat4x4<f32>, proj: mat4x4<f32>, modelScale: vec3<f32>) -> vec3<f32> {\r\n let R = quatToMat3(rotation);\r\n let scaledScale = scale * modelScale;\r\n let s2 = scaledScale * scaledScale;\r\n let M = mat3x3<f32>(R[0] * s2.x, R[1] * s2.y, R[2] * s2.z);\r\n let Sigma = M * transpose(R);\r\n \r\n let viewPos = (modelView * vec4<f32>(mean, 1.0)).xyz;\r\n let viewRot = mat3x3<f32>(modelView[0].xyz, modelView[1].xyz, modelView[2].xyz);\r\n let SigmaView = viewRot * Sigma * transpose(viewRot);\r\n \r\n let fx = proj[0][0]; let fy = proj[1][1];\r\n let z = -viewPos.z;\r\n let z_clamped = max(z, 0.001);\r\n let z2 = z_clamped * z_clamped;\r\n \r\n let j1 = vec3<f32>(fx / z_clamped, 0.0, fx * viewPos.x / z2);\r\n let j2 = vec3<f32>(0.0, fy / z_clamped, fy * viewPos.y / z2);\r\n let Sj1 = SigmaView * j1;\r\n let Sj2 = SigmaView * j2;\r\n return vec3<f32>(dot(j1, Sj1), dot(j1, Sj2), dot(j2, Sj2));\r\n}\r\n\r\nfn computeEllipseAxes(cov2D: vec3<f32>) -> mat2x2<f32> {\r\n let a = cov2D.x; let b = cov2D.y; let c = cov2D.z;\r\n let trace = a + c;\r\n let det = a * c - b * b;\r\n let disc = trace * trace - 4.0 * det;\r\n let sqrtDisc = sqrt(max(disc, 0.0));\r\n let lambda1 = max((trace + sqrtDisc) * 0.5, 0.0);\r\n let lambda2 = max((trace - sqrtDisc) * 0.5, 0.0);\r\n let r1 = sqrt(lambda1);\r\n let r2 = sqrt(lambda2);\r\n \r\n var axis1: vec2<f32>; var axis2: vec2<f32>;\r\n let eigenvecLen = abs(b) + abs(lambda1 - a);\r\n if (eigenvecLen > 1e-6) {\r\n axis1 = normalize(vec2<f32>(b, lambda1 - a));\r\n axis2 = vec2<f32>(-axis1.y, axis1.x);\r\n } else {\r\n if (a >= c) { axis1 = vec2<f32>(1.0, 0.0); axis2 = vec2<f32>(0.0, 1.0); }\r\n else { axis1 = vec2<f32>(0.0, 1.0); axis2 = vec2<f32>(1.0, 0.0); }\r\n }\r\n return mat2x2<f32>(axis1 * r1, axis2 * r2);\r\n}\r\n\r\n@vertex\r\nfn vs_main(@builtin(vertex_index) vertexIndex: u32, @builtin(instance_index) instanceIndex: u32) -> VertexOutput {\r\n var output: VertexOutput;\r\n let splatIndex = sortedIndices[instanceIndex];\r\n let splat = splats[splatIndex];\r\n let quadPos = QUAD_POSITIONS[vertexIndex];\r\n output.localUV = quadPos;\r\n \r\n // 计算 modelView 矩阵和模型缩放\r\n let modelView = uniforms.view * uniforms.model;\r\n let modelScale = getModelScale3(uniforms.model);\r\n \r\n let cov2D = computeCov2D(splat.mean, splat.scale, splat.rotation, modelView, uniforms.proj, modelScale);\r\n let axes = computeEllipseAxes(cov2D);\r\n let screenOffset = axes[0] * quadPos.x * ELLIPSE_SCALE + axes[1] * quadPos.y * ELLIPSE_SCALE;\r\n \r\n // 应用 model 变换到 splat 位置\r\n let worldPos = uniforms.model * vec4<f32>(splat.mean, 1.0);\r\n let viewPos = uniforms.view * worldPos;\r\n var clipPos = uniforms.proj * viewPos;\r\n clipPos.x = clipPos.x + screenOffset.x * clipPos.w;\r\n clipPos.y = clipPos.y + screenOffset.y * clipPos.w;\r\n output.position = clipPos;\r\n \r\n // SH 计算 - 使用局部坐标系中的方向 (与 visionary 一致)\r\n let A = mat3x3<f32>(\r\n uniforms.model[0].xyz,\r\n uniforms.model[1].xyz,\r\n uniforms.model[2].xyz\r\n );\r\n let modelTranslation = uniforms.model[3].xyz;\r\n let s2 = max(1e-12, (dot(A[0], A[0]) + dot(A[1], A[1]) + dot(A[2], A[2])) / 3.0);\r\n let camLocal = (transpose(A) * (uniforms.cameraPos - modelTranslation)) / s2;\r\n let dirLocal = normalize(splat.mean - camLocal);\r\n \r\n let shColor1 = evalSH1(dirLocal, splat.sh1);\r\n let shColor2 = evalSH2(dirLocal, splat.sh2);\r\n let shColor3 = evalSH3(dirLocal, splat.sh3);\r\n output.color = max(vec3<f32>(0.0), splat.colorDC + shColor1 + shColor2 + shColor3);\r\n output.opacity = splat.opacity;\r\n \r\n return output;\r\n}\r\n\r\n@fragment\r\nfn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {\r\n let r = length(input.localUV);\r\n if (r > 1.0) { discard; }\r\n let gaussianWeight = exp(-r * r * GAUSSIAN_DECAY);\r\n let alpha = input.opacity * gaussianWeight;\r\n if (alpha < 0.004) { discard; }\r\n let color = clamp(input.color, vec3<f32>(0.0), vec3<f32>(1.0));\r\n return vec4<f32>(color * alpha, alpha);\r\n}\r\n`;\r\n\r\n/**\r\n * SH 模式枚举\r\n * @deprecated 使用 IGSSplatRenderer 中的 SHMode\r\n */\r\nexport enum SHMode {\r\n L0 = 0, // 仅 DC 颜色 (高性能)\r\n L1 = 1, // DC + L1 SH\r\n L2 = 2, // DC + L1 + L2 SH\r\n L3 = 3, // DC + L1 + L2 + L3 SH (完整)\r\n}\r\n\r\n/**\r\n * Bounding Box 结构\r\n * @deprecated 使用 IGSSplatRenderer 中的 BoundingBox\r\n */\r\nexport interface BoundingBox {\r\n min: [number, number, number];\r\n max: [number, number, number];\r\n center: [number, number, number];\r\n radius: number; // bounding sphere 半径\r\n}\r\n\r\n// 重新导出接口类型以保持向后兼容\r\nexport type { IBoundingBox as GSBoundingBox };\r\n\r\n/**\r\n * GPU Splat 结构的字节大小\r\n * 256 bytes per splat (64 floats), 对齐到 WGSL struct 布局\r\n * 包含: mean(3) + pad(1) + scale(3) + pad(1) + rotation(4) + colorDC(3) + opacity(1)\r\n * + sh1(9) + sh2(15) + sh3(21) + pad(3) = 64 floats\r\n */\r\nconst SPLAT_BYTE_SIZE = 256;\r\nconst SPLAT_FLOAT_COUNT = 64;\r\n\r\n/**\r\n * 紧凑 Splat 结构(移动端优化)\r\n * 64 bytes per splat (16 floats), 仅包含基本渲染数据\r\n * 包含: mean(3) + pad(1) + scale(3) + pad(1) + rotation(4) + colorDC(3) + opacity(1) = 16 floats\r\n */\r\nconst SPLAT_COMPACT_BYTE_SIZE = 64;\r\nconst SPLAT_COMPACT_FLOAT_COUNT = 16;\r\n\r\n/**\r\n * 不同性能等级的默认配置\r\n *\r\n * 重要:排序对渲染效果至关重要!\r\n * 没有正确排序会导致 alpha blending 错误,看起来像\"椭球叠加\"\r\n *\r\n * 注意:maxVisibleSplats 会影响显示完整度\r\n * - 如果场景有 300 万 splat,设置 30 万只会显示 10%\r\n * - 移动端可以通过 setOptimizationConfig 调整\r\n */\r\nconst PERFORMANCE_CONFIGS: Record<PerformanceTier, MobileOptimizationConfig> = {\r\n [PerformanceTier.HIGH]: {\r\n maxVisibleSplats: Infinity,\r\n enableSorting: true,\r\n sortEveryNFrames: 1,\r\n useCompactFormat: false,\r\n pixelCullThreshold: 1.0,\r\n defaultSHMode: SHMode.L1,\r\n },\r\n [PerformanceTier.MEDIUM]: {\r\n maxVisibleSplats: Infinity,\r\n enableSorting: true,\r\n sortEveryNFrames: 1,\r\n useCompactFormat: false,\r\n pixelCullThreshold: 1.0,\r\n defaultSHMode: SHMode.L1,\r\n },\r\n [PerformanceTier.LOW]: {\r\n maxVisibleSplats: Infinity, \r\n enableSorting: true,\r\n sortEveryNFrames: 1,\r\n useCompactFormat: false,\r\n pixelCullThreshold: 1.0,\r\n defaultSHMode: SHMode.L0,\r\n },\r\n};\r\n\r\n/**\r\n * GSSplatRenderer - 3D Gaussian Splatting 渲染器\r\n * 使用 instanced quad 方式渲染 splats\r\n * 实现 IGSSplatRenderer 接口\r\n *\r\n * 优化功能:\r\n * - GPU 可见性剔除 (视锥/近平面/屏幕尺寸)\r\n * - DrawIndirect 避免 CPU submit 开销\r\n * - 仅对可见 splat 排序\r\n * - 移动端自动优化(降低排序频率、使用紧凑格式)\r\n */\r\nexport class GSSplatRenderer implements IGSSplatRendererWithCapabilities {\r\n private renderer: Renderer;\r\n private camera: Camera;\r\n\r\n private pipelineL0!: GPURenderPipeline;\r\n private pipelineL1!: GPURenderPipeline;\r\n private pipelineL2!: GPURenderPipeline;\r\n private pipelineL3!: GPURenderPipeline;\r\n private pipelineL0Compact!: GPURenderPipeline; // 移动端紧凑格式管线\r\n private bindGroupLayout!: GPUBindGroupLayout;\r\n private bindGroupLayoutCompact!: GPUBindGroupLayout; // 紧凑格式的 layout\r\n private uniformBuffer!: GPUBuffer;\r\n\r\n private splatBuffer: GPUBuffer | null = null;\r\n private splatCount: number = 0;\r\n private bindGroup: GPUBindGroup | null = null;\r\n\r\n // 深度排序器(含剔除功能)- 使用 V2 分桶稳定排序\r\n private sorter: GSSplatSorter | null = null;\r\n\r\n // 是否启用 DrawIndirect (剔除优化)\r\n // 注意:在某些移动设备上可能有问题,可以禁用作为备用\r\n private useDrawIndirect: boolean = true;\r\n\r\n // 是否为移动设备(用于调试)\r\n private isMobile: boolean = false;\r\n\r\n // 像素剔除阈值 (小于此像素的 splat 会被剔除)\r\n private pixelCullThreshold: number = 1.0;\r\n\r\n // SH 模式:L0/L1/L2/L3\r\n private shMode: SHMode = SHMode.L1;\r\n\r\n // 点云的 bounding box(在 setData 时计算)\r\n private boundingBox: BoundingBox | null = null;\r\n\r\n // ============================================\r\n // 变换相关 (position, rotation, scale)\r\n // ============================================\r\n private position: [number, number, number] = [0, 0, 0];\r\n private rotation: [number, number, number] = [0, 0, 0]; // Euler angles (radians)\r\n private scale: [number, number, number] = [1, 1, 1];\r\n private pivot: [number, number, number] = [0, 0, 0]; // 旋转/缩放中心点\r\n private modelMatrix: Float32Array = new Float32Array(16); // 4x4 model matrix\r\n\r\n // ============================================\r\n // 移动端优化相关\r\n // ============================================\r\n private performanceTier: PerformanceTier;\r\n private optimizationConfig: MobileOptimizationConfig;\r\n private frameCount: number = 0;\r\n private useCompactFormat: boolean = false;\r\n\r\n constructor(renderer: Renderer, camera: Camera) {\r\n this.renderer = renderer;\r\n this.camera = camera;\r\n\r\n // 检测设备类型\r\n this.isMobile = isMobileDevice();\r\n\r\n // 检测性能等级\r\n this.performanceTier = detectPerformanceTier(renderer.device);\r\n this.optimizationConfig = { ...PERFORMANCE_CONFIGS[this.performanceTier] };\r\n\r\n // 应用默认配置\r\n this.pixelCullThreshold = this.optimizationConfig.pixelCullThreshold;\r\n this.shMode = this.optimizationConfig.defaultSHMode;\r\n this.useCompactFormat = this.optimizationConfig.useCompactFormat;\r\n\r\n this.createPipelines();\r\n this.createUniformBuffer();\r\n this.updateModelMatrix(); // 初始化模型矩阵为单位矩阵\r\n }\r\n\r\n // ============================================\r\n // Transform 方法\r\n // ============================================\r\n\r\n /**\r\n * 设置位置\r\n */\r\n setPosition(x: number, y: number, z: number): void {\r\n this.position = [x, y, z];\r\n this.updateModelMatrix();\r\n }\r\n\r\n /**\r\n * 获取位置\r\n */\r\n getPosition(): [number, number, number] {\r\n return [...this.position];\r\n }\r\n\r\n /**\r\n * 设置旋转 (欧拉角, 弧度)\r\n */\r\n setRotation(x: number, y: number, z: number): void {\r\n this.rotation = [x, y, z];\r\n this.updateModelMatrix();\r\n }\r\n\r\n /**\r\n * 获取旋转\r\n */\r\n getRotation(): [number, number, number] {\r\n return [...this.rotation];\r\n }\r\n\r\n /**\r\n * 设置缩放\r\n */\r\n setScale(x: number, y: number, z: number): void {\r\n this.scale = [x, y, z];\r\n this.updateModelMatrix();\r\n }\r\n\r\n /**\r\n * 获取缩放\r\n */\r\n getScale(): [number, number, number] {\r\n return [...this.scale];\r\n }\r\n\r\n /**\r\n * 设置旋转/缩放中心点 (pivot)\r\n */\r\n setPivot(x: number, y: number, z: number): void {\r\n this.pivot = [x, y, z];\r\n this.updateModelMatrix();\r\n }\r\n\r\n /**\r\n * 获取旋转/缩放中心点 (pivot)\r\n */\r\n getPivot(): [number, number, number] {\r\n return [...this.pivot];\r\n }\r\n\r\n /**\r\n * 更新模型矩阵\r\n * 变换顺序: T * Tp * R * S * Tp^-1\r\n * 即: 先移到原点,缩放,旋转,再移回pivot,最后应用用户平移\r\n */\r\n private updateModelMatrix(): void {\r\n const [tx, ty, tz] = this.position;\r\n const [rx, ry, rz] = this.rotation;\r\n const [sx, sy, sz] = this.scale;\r\n const [px, py, pz] = this.pivot;\r\n\r\n // 计算旋转矩阵分量 (Euler XYZ 顺序)\r\n const cx = Math.cos(rx), sx1 = Math.sin(rx);\r\n const cy = Math.cos(ry), sy1 = Math.sin(ry);\r\n const cz = Math.cos(rz), sz1 = Math.sin(rz);\r\n\r\n // 组合旋转矩阵 R = Rz * Ry * Rx\r\n const r00 = cy * cz;\r\n const r01 = sx1 * sy1 * cz - cx * sz1;\r\n const r02 = cx * sy1 * cz + sx1 * sz1;\r\n const r10 = cy * sz1;\r\n const r11 = sx1 * sy1 * sz1 + cx * cz;\r\n const r12 = cx * sy1 * sz1 - sx1 * cz;\r\n const r20 = -sy1;\r\n const r21 = sx1 * cy;\r\n const r22 = cx * cy;\r\n\r\n // 模型矩阵 = T * Tp * R * S * Tp^-1\r\n // 其中 Tp 是平移到 pivot 的矩阵,Tp^-1 是平移回原点的矩阵\r\n // \r\n // 展开后:\r\n // M = T * Tp * R * S * Tp^-1\r\n // \r\n // 对于点 p,变换后的点 p' = M * p\r\n // p' = T * Tp * R * S * Tp^-1 * p\r\n // = T * Tp * R * S * (p - pivot)\r\n // = T * (pivot + R * S * (p - pivot))\r\n // = position + pivot + R * S * (p - pivot)\r\n //\r\n // 设 RS = R * S (旋转缩放矩阵)\r\n // p' = position + pivot + RS * p - RS * pivot\r\n // = position + pivot - RS * pivot + RS * p\r\n //\r\n // 所以平移部分 = position + pivot - RS * pivot\r\n // = position + (I - RS) * pivot\r\n\r\n // RS 矩阵 (旋转 * 缩放)\r\n const rs00 = r00 * sx, rs01 = r01 * sy, rs02 = r02 * sz;\r\n const rs10 = r10 * sx, rs11 = r11 * sy, rs12 = r12 * sz;\r\n const rs20 = r20 * sx, rs21 = r21 * sy, rs22 = r22 * sz;\r\n\r\n // 计算 (I - RS) * pivot\r\n const dpx = px - (rs00 * px + rs01 * py + rs02 * pz);\r\n const dpy = py - (rs10 * px + rs11 * py + rs12 * pz);\r\n const dpz = pz - (rs20 * px + rs21 * py + rs22 * pz);\r\n\r\n // 最终平移 = position + (I - RS) * pivot\r\n const finalTx = tx + dpx;\r\n const finalTy = ty + dpy;\r\n const finalTz = tz + dpz;\r\n\r\n // 模型矩阵 (列主序)\r\n this.modelMatrix[0] = rs00;\r\n this.modelMatrix[1] = rs10;\r\n this.modelMatrix[2] = rs20;\r\n this.modelMatrix[3] = 0;\r\n\r\n this.modelMatrix[4] = rs01;\r\n this.modelMatrix[5] = rs11;\r\n this.modelMatrix[6] = rs21;\r\n this.modelMatrix[7] = 0;\r\n\r\n this.modelMatrix[8] = rs02;\r\n this.modelMatrix[9] = rs12;\r\n this.modelMatrix[10] = rs22;\r\n this.modelMatrix[11] = 0;\r\n\r\n this.modelMatrix[12] = finalTx;\r\n this.modelMatrix[13] = finalTy;\r\n this.modelMatrix[14] = finalTz;\r\n this.modelMatrix[15] = 1;\r\n }\r\n\r\n /**\r\n * 获取当前模型矩阵\r\n */\r\n getModelMatrix(): Float32Array {\r\n return this.modelMatrix;\r\n }\r\n\r\n /**\r\n * 获取当前性能等级\r\n */\r\n getPerformanceTier(): PerformanceTier {\r\n return this.performanceTier;\r\n }\r\n\r\n /**\r\n * 手动设置优化配置\r\n */\r\n setOptimizationConfig(config: Partial<MobileOptimizationConfig>): void {\r\n this.optimizationConfig = { ...this.optimizationConfig, ...config };\r\n this.pixelCullThreshold = this.optimizationConfig.pixelCullThreshold;\r\n if (config.defaultSHMode !== undefined) {\r\n this.shMode = config.defaultSHMode;\r\n }\r\n }\r\n\r\n /**\r\n * 获取当前优化配置\r\n */\r\n getOptimizationConfig(): MobileOptimizationConfig {\r\n return { ...this.optimizationConfig };\r\n }\r\n\r\n /**\r\n * 设置 SH 模式\r\n * @param mode L0/L1/L2/L3\r\n */\r\n setSHMode(mode: SHMode): void {\r\n this.shMode = mode;\r\n }\r\n\r\n /**\r\n * 获取当前 SH 模式\r\n */\r\n getSHMode(): SHMode {\r\n return this.shMode;\r\n }\r\n\r\n /**\r\n * 设置是否启用 DrawIndirect (剔除优化)\r\n * 启用后会在 GPU 上进行可见性剔除,仅绘制可见 splat\r\n */\r\n setUseDrawIndirect(enabled: boolean): void {\r\n this.useDrawIndirect = enabled;\r\n }\r\n\r\n /**\r\n * 设置像素剔除阈值\r\n * 屏幕上小于此像素数的 splat 会被剔除\r\n * @param threshold 像素阈值,默认 1.0\r\n */\r\n setPixelCullThreshold(threshold: number): void {\r\n this.pixelCullThreshold = threshold;\r\n }\r\n\r\n /**\r\n * 创建渲染管线 (L0/L1/L2/L3 四个版本)\r\n */\r\n private createPipelines(): void {\r\n const device = this.renderer.device;\r\n\r\n // 创建 shader 模块\r\n const shaderModuleL0 = device.createShaderModule({ code: shaderCodeL0 });\r\n const shaderModuleL1 = device.createShaderModule({ code: shaderCodeL1 });\r\n const shaderModuleL2 = device.createShaderModule({ code: shaderCodeL2 });\r\n const shaderModuleL3 = device.createShaderModule({ code: shaderCodeL3 });\r\n\r\n // 创建 bind group layout\r\n this.bindGroupLayout = device.createBindGroupLayout({\r\n entries: [\r\n {\r\n // uniform buffer (view + proj matrices)\r\n binding: 0,\r\n visibility: GPUShaderStage.VERTEX,\r\n buffer: { type: \"uniform\" },\r\n },\r\n {\r\n // storage buffer (splats array)\r\n binding: 1,\r\n visibility: GPUShaderStage.VERTEX,\r\n buffer: { type: \"read-only-storage\" },\r\n },\r\n {\r\n // storage buffer (sorted indices)\r\n binding: 2,\r\n visibility: GPUShaderStage.VERTEX,\r\n buffer: { type: \"read-only-storage\" },\r\n },\r\n ],\r\n });\r\n\r\n // 创建 pipeline layout\r\n const pipelineLayout = device.createPipelineLayout({\r\n bindGroupLayouts: [this.bindGroupLayout],\r\n });\r\n\r\n // 共享的管线描述符基础配置\r\n const basePipelineDesc = {\r\n layout: pipelineLayout,\r\n primitive: {\r\n topology: \"triangle-strip\" as GPUPrimitiveTopology,\r\n },\r\n depthStencil: {\r\n format: this.renderer.depthFormat,\r\n depthWriteEnabled: false,\r\n depthCompare: \"always\" as GPUCompareFunction,\r\n },\r\n };\r\n\r\n const blendState = {\r\n color: {\r\n srcFactor: \"one\" as GPUBlendFactor,\r\n dstFactor: \"one-minus-src-alpha\" as GPUBlendFactor,\r\n operation: \"add\" as GPUBlendOperation,\r\n },\r\n alpha: {\r\n srcFactor: \"one\" as GPUBlendFactor,\r\n dstFactor: \"one-minus-src-alpha\" as GPUBlendFactor,\r\n operation: \"add\" as GPUBlendOperation,\r\n },\r\n };\r\n\r\n // 创建各级别渲染管线\r\n const createPipeline = (module: GPUShaderModule) =>\r\n device.createRenderPipeline({\r\n ...basePipelineDesc,\r\n vertex: { module, entryPoint: \"vs_main\", buffers: [] },\r\n fragment: {\r\n module,\r\n entryPoint: \"fs_main\",\r\n targets: [{ format: this.renderer.format, blend: blendState }],\r\n },\r\n });\r\n\r\n this.pipelineL0 = createPipeline(shaderModuleL0);\r\n this.pipelineL1 = createPipeline(shaderModuleL1);\r\n this.pipelineL2 = createPipeline(shaderModuleL2);\r\n this.pipelineL3 = createPipeline(shaderModuleL3);\r\n }\r\n\r\n /**\r\n * 创建 uniform buffer\r\n * 布局: view (64 bytes) + proj (64 bytes) + model (64 bytes) + cameraPos (12 bytes) + padding (4 bytes) + screenSize (8 bytes) + padding (8 bytes) = 224 bytes\r\n */\r\n private createUniformBuffer(): void {\r\n this.uniformBuffer = this.renderer.device.createBuffer({\r\n size: 224, // view (64) + proj (64) + model (64) + cameraPos (12) + padding (4) + screenSize (8) + padding (8)\r\n usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\r\n });\r\n }\r\n\r\n /**\r\n * 设置 splat 数据\r\n * @param splats CPU 端的 splat 数组\r\n */\r\n setData(splats: SplatCPU[]): void {\r\n const device = this.renderer.device;\r\n\r\n // 销毁旧的资源\r\n if (this.splatBuffer) {\r\n this.splatBuffer.destroy();\r\n }\r\n if (this.sorter) {\r\n this.sorter.destroy();\r\n this.sorter = null;\r\n }\r\n\r\n // ============================================\r\n // 移动端优化:限制最大 splat 数量\r\n // ============================================\r\n const originalCount = splats.length;\r\n const maxSplats = this.optimizationConfig.maxVisibleSplats;\r\n\r\n if (splats.length > maxSplats && maxSplats !== Infinity) {\r\n // 均匀降采样\r\n const step = splats.length / maxSplats;\r\n const sampledSplats: SplatCPU[] = [];\r\n for (let i = 0; i < maxSplats; i++) {\r\n const idx = Math.floor(i * step);\r\n sampledSplats.push(splats[idx]);\r\n }\r\n splats = sampledSplats;\r\n }\r\n\r\n this.splatCount = splats.length;\r\n this.frameCount = 0; // 重置帧计数,确保第一帧会排序\r\n\r\n if (this.splatCount === 0) {\r\n this.splatBuffer = null;\r\n this.bindGroup = null;\r\n this.boundingBox = null;\r\n return;\r\n }\r\n\r\n // 计算 bounding box\r\n this.boundingBox = this.computeBoundingBox(splats);\r\n\r\n // ============================================\r\n // 根据配置选择数据格式\r\n // ============================================\r\n const useCompact = this.useCompactFormat;\r\n const floatCount = useCompact\r\n ? SPLAT_COMPACT_FLOAT_COUNT\r\n : SPLAT_FLOAT_COUNT;\r\n const byteSize = useCompact ? SPLAT_COMPACT_BYTE_SIZE : SPLAT_BYTE_SIZE;\r\n\r\n // 创建 CPU 端数据数组\r\n const data = new Float32Array(this.splatCount * floatCount);\r\n\r\n for (let i = 0; i < this.splatCount; i++) {\r\n const splat = splats[i];\r\n const offset = i * floatCount;\r\n\r\n // mean (vec3) + padding\r\n data[offset + 0] = splat.mean[0];\r\n data[offset + 1] = splat.mean[1];\r\n data[offset + 2] = splat.mean[2];\r\n data[offset + 3] = 0; // padding\r\n\r\n // scale (vec3) + padding\r\n data[offset + 4] = splat.scale[0];\r\n data[offset + 5] = splat.scale[1];\r\n data[offset + 6] = splat.scale[2];\r\n data[offset + 7] = 0; // padding\r\n\r\n // rotation (vec4)\r\n data[offset + 8] = splat.rotation[0];\r\n data[offset + 9] = splat.rotation[1];\r\n data[offset + 10] = splat.rotation[2];\r\n data[offset + 11] = splat.rotation[3];\r\n\r\n // colorDC (vec3) + opacity\r\n data[offset + 12] = splat.colorDC[0];\r\n data[offset + 13] = splat.colorDC[1];\r\n data[offset + 14] = splat.colorDC[2];\r\n data[offset + 15] = splat.opacity;\r\n\r\n // 紧凑格式不包含 SH 系数\r\n if (!useCompact) {\r\n const shRest = splat.shRest;\r\n\r\n // sh1 (array<f32, 9>) - L1 SH 系数\r\n for (let j = 0; j < 9; j++) {\r\n data[offset + 16 + j] = shRest ? shRest[j] : 0;\r\n }\r\n\r\n // sh2 (array<f32, 15>) - L2 SH 系数\r\n for (let j = 0; j < 15; j++) {\r\n data[offset + 25 + j] = shRest ? shRest[9 + j] : 0;\r\n }\r\n\r\n // sh3 (array<f32, 21>) - L3 SH 系数\r\n for (let j = 0; j < 21; j++) {\r\n data[offset + 40 + j] = shRest ? shRest[24 + j] : 0;\r\n }\r\n\r\n // padding (array<f32, 3>)\r\n data[offset + 61] = 0;\r\n data[offset + 62] = 0;\r\n data[offset + 63] = 0;\r\n }\r\n }\r\n\r\n // 创建 GPU buffer\r\n this.splatBuffer = device.createBuffer({\r\n size: data.byteLength,\r\n usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,\r\n });\r\n\r\n // 上传数据\r\n device.queue.writeBuffer(this.splatBuffer, 0, data);\r\n\r\n // 创建深度排序器(含剔除功能 + 量化深度稳定排序)\r\n this.sorter = new GSSplatSorter(\r\n device,\r\n this.splatCount,\r\n this.splatBuffer,\r\n this.uniformBuffer,\r\n );\r\n\r\n // 初始化排序器参数\r\n this.sorter.setScreenSize(this.renderer.width, this.renderer.height);\r\n this.sorter.setCullingOptions({\r\n nearPlane: this.camera.near,\r\n farPlane: this.camera.far,\r\n pixelThreshold: this.pixelCullThreshold,\r\n });\r\n\r\n // 创建 bind group (包含排序后的索引 buffer)\r\n this.bindGroup = device.createBindGroup({\r\n layout: this.bindGroupLayout,\r\n entries: [\r\n {\r\n binding: 0,\r\n resource: { buffer: this.uniformBuffer },\r\n },\r\n {\r\n binding: 1,\r\n resource: { buffer: this.splatBuffer },\r\n },\r\n {\r\n binding: 2,\r\n resource: { buffer: this.sorter.getIndicesBuffer() },\r\n },\r\n ],\r\n });\r\n\r\n const memoryMB = (data.byteLength / (1024 * 1024)).toFixed(2);\r\n }\r\n\r\n /**\r\n * 设置紧凑格式的 splat 数据(移动端优化)\r\n * 直接接受 CompactSplatData,避免创建中间对象\r\n * @param compactData 紧凑格式的 splat 数据\r\n */\r\n setCompactData(compactData: CompactSplatData): void {\r\n try {\r\n const device = this.renderer.device;\r\n\r\n // 销毁旧的资源\r\n if (this.splatBuffer) {\r\n this.splatBuffer.destroy();\r\n }\r\n if (this.sorter) {\r\n this.sorter.destroy();\r\n this.sorter = null;\r\n }\r\n\r\n this.splatCount = compactData.count;\r\n this.frameCount = 0; // 重置帧计数,确保第一帧会排序\r\n\r\n if (this.splatCount === 0) {\r\n this.splatBuffer = null;\r\n this.bindGroup = null;\r\n this.boundingBox = null;\r\n return;\r\n }\r\n\r\n // 计算 bounding box\r\n this.boundingBox = this.computeBoundingBoxFromCompact(compactData);\r\n\r\n // 转换为 GPU buffer 格式\r\n // 始终包含 SH 数据(如果存在),以便用户可以在运行时切换 SH 模式\r\n const includeSH = compactData.shCoeffs !== undefined;\r\n const gpuData = compactDataToGPUBuffer(compactData, includeSH);\r\n\r\n // 创建 GPU buffer\r\n this.splatBuffer = device.createBuffer({\r\n size: gpuData.byteLength,\r\n usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,\r\n });\r\n\r\n // 上传数据\r\n device.queue.writeBuffer(this.splatBuffer, 0, gpuData.buffer);\r\n\r\n // 创建深度排序器\r\n this.sorter = new GSSplatSorter(\r\n device,\r\n this.splatCount,\r\n this.splatBuffer,\r\n this.uniformBuffer,\r\n );\r\n\r\n // 初始化排序器参数\r\n this.sorter.setScreenSize(this.renderer.width, this.renderer.height);\r\n this.sorter.setCullingOptions({\r\n nearPlane: this.camera.near,\r\n farPlane: this.camera.far,\r\n pixelThreshold: this.pixelCullThreshold,\r\n });\r\n\r\n // 创建 bind group\r\n this.bindGroup = device.createBindGroup({\r\n layout: this.bindGroupLayout,\r\n entries: [\r\n { binding: 0, resource: { buffer: this.uniformBuffer } },\r\n { binding: 1, resource: { buffer: this.splatBuffer } },\r\n { binding: 2, resource: { buffer: this.sorter.getIndicesBuffer() } },\r\n ],\r\n });\r\n\r\n const memoryMB = (gpuData.byteLength / (1024 * 1024)).toFixed(2);\r\n } catch (error) {\r\n // 重置状态避免后续渲染崩溃\r\n this.splatCount = 0;\r\n this.splatBuffer = null;\r\n this.bindGroup = null;\r\n this.sorter = null;\r\n }\r\n }\r\n\r\n /**\r\n * 从紧凑数据计算 bounding box\r\n */\r\n private computeBoundingBoxFromCompact(data: CompactSplatData): BoundingBox {\r\n if (data.count === 0) {\r\n return { min: [0, 0, 0], max: [0, 0, 0], center: [0, 0, 0], radius: 0 };\r\n }\r\n\r\n const positions = data.positions;\r\n const min: [number, number, number] = [\r\n positions[0],\r\n positions[1],\r\n positions[2],\r\n ];\r\n const max: [number, number, number] = [\r\n positions[0],\r\n positions[1],\r\n positions[2],\r\n ];\r\n\r\n for (let i = 1; i < data.count; i++) {\r\n const x = positions[i * 3 + 0];\r\n const y = positions[i * 3 + 1];\r\n const z = positions[i * 3 + 2];\r\n min[0] = Math.min(min[0], x);\r\n min[1] = Math.min(min[1], y);\r\n min[2] = Math.min(min[2], z);\r\n max[0] = Math.max(max[0], x);\r\n max[1] = Math.max(max[1], y);\r\n max[2] = Math.max(max[2], z);\r\n }\r\n\r\n const center: [number, number, number] = [\r\n (min[0] + max[0]) / 2,\r\n (min[1] + max[1]) / 2,\r\n (min[2] + max[2]) / 2,\r\n ];\r\n\r\n const dx = max[0] - min[0];\r\n const dy = max[1] - min[1];\r\n const dz = max[2] - min[2];\r\n const radius = Math.sqrt(dx * dx + dy * dy + dz * dz) / 2;\r\n\r\n return { min, max, center, radius };\r\n }\r\n\r\n /**\r\n * 渲染 splats\r\n * @param pass 渲染通道编码器\r\n */\r\n render(pass: GPURenderPassEncoder): void {\r\n if (this.splatCount === 0 || !this.bindGroup || !this.sorter) {\r\n return;\r\n }\r\n\r\n this.frameCount++;\r\n\r\n // 更新 view uniform\r\n this.renderer.device.queue.writeBuffer(\r\n this.uniformBuffer,\r\n 0,\r\n new Float32Array(this.camera.viewMatrix),\r\n );\r\n // 更新 proj uniform\r\n this.renderer.device.queue.writeBuffer(\r\n this.uniformBuffer,\r\n 64,\r\n new Float32Array(this.camera.projectionMatrix),\r\n );\r\n // 更新 model uniform\r\n this.renderer.device.queue.writeBuffer(\r\n this.uniformBuffer,\r\n 128,\r\n new Float32Array(this.modelMatrix),\r\n );\r\n // 更新 cameraPos uniform\r\n this.renderer.device.queue.writeBuffer(\r\n this.uniformBuffer,\r\n 192,\r\n new Float32Array(this.camera.position),\r\n );\r\n // 更新 screenSize uniform (用于抗锯齿)\r\n this.renderer.device.queue.writeBuffer(\r\n this.uniformBuffer,\r\n 208,\r\n new Float32Array([this.renderer.width, this.renderer.height, 0, 0]),\r\n );\r\n\r\n // 更新排序器参数(屏幕尺寸和剔除选项)\r\n this.sorter.setScreenSize(this.renderer.width, this.renderer.height);\r\n this.sorter.setCullingOptions({\r\n nearPlane: this.camera.near,\r\n farPlane: this.camera.far,\r\n pixelThreshold: this.pixelCullThreshold,\r\n });\r\n\r\n // ============================================\r\n // 深度排序(对 3DGS 效果至关重要)\r\n // 注意:第一帧必须排序,否则渲染顺序错误\r\n // ============================================\r\n const isFirstFrame = this.frameCount === 1;\r\n const shouldSort =\r\n this.optimizationConfig.enableSorting &&\r\n (isFirstFrame ||\r\n this.frameCount % this.optimizationConfig.sortEveryNFrames === 0);\r\n\r\n if (shouldSort) {\r\n // 执行 GPU 剔除和深度排序 (在 render pass 开始前)\r\n // 注意: sort() 会在内部创建并提交 compute command buffer\r\n this.sorter.sort();\r\n }\r\n\r\n // 根据 SH 模式选择管线\r\n const pipelines = [\r\n this.pipelineL0,\r\n this.pipelineL1,\r\n this.pipelineL2,\r\n this.pipelineL3,\r\n ];\r\n const pipeline = pipelines[this.shMode];\r\n\r\n // 设置管线和绑定组\r\n pass.setPipeline(pipeline);\r\n pass.setBindGroup(0, this.bindGroup);\r\n\r\n // 绘制\r\n if (this.useDrawIndirect) {\r\n // 使用 DrawIndirect:仅绘制可见 splat\r\n // DrawIndirect buffer 由 GPU 剔除 pass 更新\r\n pass.drawIndirect(this.sorter.getDrawIndirectBuffer(), 0);\r\n } else {\r\n // 传统方式:绘制所有 splat\r\n pass.draw(4, this.splatCount);\r\n }\r\n }\r\n\r\n /**\r\n * 获取 splat 数量\r\n */\r\n getSplatCount(): number {\r\n return this.splatCount;\r\n }\r\n\r\n /**\r\n * 获取点云的 bounding box\r\n * @returns BoundingBox 或 null(如果没有点云数据)\r\n */\r\n getBoundingBox(): BoundingBox | null {\r\n return this.boundingBox;\r\n }\r\n\r\n /**\r\n * 计算点云的 bounding box\r\n * @param splats splat 数组\r\n * @returns BoundingBox\r\n */\r\n private computeBoundingBox(splats: SplatCPU[]): BoundingBox {\r\n if (splats.length === 0) {\r\n return {\r\n min: [0, 0, 0],\r\n max: [0, 0, 0],\r\n center: [0, 0, 0],\r\n radius: 0,\r\n };\r\n }\r\n\r\n // 初始化为第一个点\r\n const min: [number, number, number] = [\r\n splats[0].mean[0],\r\n splats[0].mean[1],\r\n splats[0].mean[2],\r\n ];\r\n const max: [number, number, number] = [\r\n splats[0].mean[0],\r\n splats[0].mean[1],\r\n splats[0].mean[2],\r\n ];\r\n\r\n // 遍历所有点,计算 min/max\r\n for (let i = 1; i < splats.length; i++) {\r\n const [x, y, z] = splats[i].mean;\r\n min[0] = Math.min(min[0], x);\r\n min[1] = Math.min(min[1], y);\r\n min[2] = Math.min(min[2], z);\r\n max[0] = Math.max(max[0], x);\r\n max[1] = Math.max(max[1], y);\r\n max[2] = Math.max(max[2], z);\r\n }\r\n\r\n // 计算中心点\r\n const center: [number, number, number] = [\r\n (min[0] + max[0]) / 2,\r\n (min[1] + max[1]) / 2,\r\n (min[2] + max[2]) / 2,\r\n ];\r\n\r\n // 计算 bounding sphere 半径(从中心到最远角)\r\n const dx = max[0] - min[0];\r\n const dy = max[1] - min[1];\r\n const dz = max[2] - min[2];\r\n const radius = Math.sqrt(dx * dx + dy * dy + dz * dz) / 2;\r\n\r\n return { min, max, center, radius };\r\n }\r\n\r\n // ============================================\r\n // IGSSplatRenderer 接口实现\r\n // ============================================\r\n\r\n /**\r\n * 是否支持指定的 SH 模式\r\n */\r\n supportsSHMode(mode: ISHMode): boolean {\r\n return mode >= ISHMode.L0 && mode <= ISHMode.L3;\r\n }\r\n\r\n /**\r\n * 获取渲染器能力\r\n */\r\n getCapabilities(): RendererCapabilities {\r\n return {\r\n maxSHMode: ISHMode.L3,\r\n supportsRawData: true,\r\n isMobileOptimized: false,\r\n maxSplatCount: 0, // 无限制(受 GPU 内存限制)\r\n };\r\n }\r\n\r\n /**\r\n * 销毁资源\r\n */\r\n destroy(): void {\r\n if (this.splatBuffer) {\r\n this.splatBuffer.destroy();\r\n this.splatBuffer = null;\r\n }\r\n if (this.sorter) {\r\n this.sorter.destroy();\r\n this.sorter = null;\r\n }\r\n this.uniformBuffer.destroy();\r\n this.splatCount = 0;\r\n this.bindGroup = null;\r\n }\r\n}\r\n","/**\r\n * TextureCompressor - 移动端纹理压缩工具\r\n * 将 splat 数据压缩为纹理格式,大幅减少 GPU 内存占用\r\n *\r\n * 内存对比:\r\n * - 原始 Storage Buffer: 256 bytes/splat\r\n * - 纹理压缩: ~52 bytes/splat (约 5x 压缩)\r\n * \r\n * 数据布局(保证精度):\r\n * - positionTexture (RGBA32Float): x, y, z, unused - 16 bytes\r\n * - scaleRotTexture1 (RGBA32Float): scale_x, scale_y, scale_z, rot_w - 16 bytes\r\n * - scaleRotTexture2 (RGBA32Float): rot_x, rot_y, rot_z, unused - 16 bytes\r\n * - colorTexture (RGBA8Unorm): r, g, b, opacity - 4 bytes\r\n * 总计: 52 bytes/splat\r\n * \r\n * 注意:使用 RGBA32Float 替代 RGBA16Float 以保证 scale 和 rotation 的精度,\r\n * 避免平面等细节渲染出现块状伪影。\r\n */\r\n\r\nimport { CompactSplatData } from \"./PLYLoaderMobile\";\r\n\r\n/**\r\n * 压缩后的纹理数据\r\n */\r\nexport interface CompressedSplatTextures {\r\n // 纹理尺寸\r\n width: number;\r\n height: number;\r\n count: number;\r\n\r\n // 位置纹理 (RGBA32Float) - 完整精度\r\n positionTexture: GPUTexture;\r\n\r\n // 缩放+旋转纹理 (RGBA32Float) - 保证精度\r\n // R: scale_x, G: scale_y, B: scale_z, A: rot_w\r\n scaleRotTexture1: GPUTexture;\r\n // R: rot_x, G: rot_y, B: rot_z, A: unused\r\n scaleRotTexture2: GPUTexture;\r\n\r\n // 颜色+不透明度纹理 (RGBA8Unorm)\r\n // R: color_r, G: color_g, B: color_b, A: opacity\r\n colorTexture: GPUTexture;\r\n\r\n // Bounding box (用于剔除优化)\r\n boundingBox: {\r\n min: [number, number, number];\r\n max: [number, number, number];\r\n };\r\n}\r\n\r\n/**\r\n * 计算纹理尺寸\r\n * 将 splat 数量映射到 2D 纹理尺寸\r\n * @param count splat 数量\r\n * @returns 纹理宽度和高度(向上取整到 4 的倍数)\r\n */\r\nexport function calculateTextureDimensions(count: number): { width: number; height: number } {\r\n if (count <= 0) {\r\n return { width: 4, height: 4 };\r\n }\r\n\r\n // 计算近似的正方形边长\r\n const side = Math.ceil(Math.sqrt(count));\r\n \r\n // 向上取整到 4 的倍数(GPU 纹理对齐优化)\r\n const alignedSide = Math.ceil(side / 4) * 4;\r\n \r\n // 确保能容纳所有 splat\r\n let width = alignedSide;\r\n let height = alignedSide;\r\n \r\n // 如果正方形不够,增加高度\r\n while (width * height < count) {\r\n height += 4;\r\n }\r\n\r\n return { width, height };\r\n}\r\n\r\n/**\r\n * 计算 bounding box\r\n */\r\nfunction computeBoundingBox(positions: Float32Array, count: number): {\r\n min: [number, number, number];\r\n max: [number, number, number];\r\n} {\r\n if (count === 0) {\r\n return {\r\n min: [0, 0, 0],\r\n max: [0, 0, 0],\r\n };\r\n }\r\n\r\n const min: [number, number, number] = [positions[0], positions[1], positions[2]];\r\n const max: [number, number, number] = [positions[0], positions[1], positions[2]];\r\n\r\n for (let i = 1; i < count; i++) {\r\n const x = positions[i * 3 + 0];\r\n const y = positions[i * 3 + 1];\r\n const z = positions[i * 3 + 2];\r\n\r\n min[0] = Math.min(min[0], x);\r\n min[1] = Math.min(min[1], y);\r\n min[2] = Math.min(min[2], z);\r\n max[0] = Math.max(max[0], x);\r\n max[1] = Math.max(max[1], y);\r\n max[2] = Math.max(max[2], z);\r\n }\r\n\r\n return { min, max };\r\n}\r\n\r\n/**\r\n * 将 splat 数据压缩为纹理格式\r\n * @param device GPU 设备\r\n * @param data 紧凑格式的 splat 数据\r\n * @returns 压缩后的纹理数据\r\n */\r\nexport function compressSplatsToTextures(\r\n device: GPUDevice,\r\n data: CompactSplatData\r\n): CompressedSplatTextures {\r\n const count = data.count;\r\n const { width, height } = calculateTextureDimensions(count);\r\n const totalPixels = width * height;\r\n\r\n // 计算 bounding box\r\n const boundingBox = computeBoundingBox(data.positions, count);\r\n\r\n // ============================================\r\n // 准备 CPU 端数据\r\n // ============================================\r\n\r\n // 位置纹理数据 (RGBA32Float) - 使用 Float32Array\r\n const positionData = new Float32Array(totalPixels * 4);\r\n\r\n // 缩放+旋转纹理数据 (RGBA32Float) - 使用 Float32Array 保证精度\r\n const scaleRotData1 = new Float32Array(totalPixels * 4);\r\n const scaleRotData2 = new Float32Array(totalPixels * 4);\r\n\r\n // 颜色纹理数据 (RGBA8Unorm)\r\n const colorData = new Uint8Array(totalPixels * 4);\r\n\r\n // ============================================\r\n // 填充数据\r\n // ============================================\r\n for (let i = 0; i < count; i++) {\r\n const pixelOffset = i * 4;\r\n\r\n // 位置数据 - 直接存储 float32\r\n positionData[pixelOffset + 0] = data.positions[i * 3 + 0];\r\n positionData[pixelOffset + 1] = data.positions[i * 3 + 1];\r\n positionData[pixelOffset + 2] = data.positions[i * 3 + 2];\r\n positionData[pixelOffset + 3] = 0; // unused\r\n\r\n // scaleRotTexture1: scale_x, scale_y, scale_z, rot_w (直接存储 float32)\r\n scaleRotData1[pixelOffset + 0] = data.scales[i * 3 + 0];\r\n scaleRotData1[pixelOffset + 1] = data.scales[i * 3 + 1];\r\n scaleRotData1[pixelOffset + 2] = data.scales[i * 3 + 2];\r\n scaleRotData1[pixelOffset + 3] = data.rotations[i * 4 + 0]; // rot_w\r\n\r\n // scaleRotTexture2: rot_x, rot_y, rot_z, unused (直接存储 float32)\r\n scaleRotData2[pixelOffset + 0] = data.rotations[i * 4 + 1]; // rot_x\r\n scaleRotData2[pixelOffset + 1] = data.rotations[i * 4 + 2]; // rot_y\r\n scaleRotData2[pixelOffset + 2] = data.rotations[i * 4 + 3]; // rot_z\r\n scaleRotData2[pixelOffset + 3] = 0; // unused\r\n\r\n // 颜色数据 (已经是 0-1 范围,转换为 0-255)\r\n const r = data.colors[i * 3 + 0];\r\n const g = data.colors[i * 3 + 1];\r\n const b = data.colors[i * 3 + 2];\r\n const opacity = data.opacities[i];\r\n\r\n colorData[pixelOffset + 0] = Math.round(Math.max(0, Math.min(1, r)) * 255);\r\n colorData[pixelOffset + 1] = Math.round(Math.max(0, Math.min(1, g)) * 255);\r\n colorData[pixelOffset + 2] = Math.round(Math.max(0, Math.min(1, b)) * 255);\r\n colorData[pixelOffset + 3] = Math.round(Math.max(0, Math.min(1, opacity)) * 255);\r\n }\r\n\r\n // ============================================\r\n // 创建 GPU 纹理\r\n // ============================================\r\n const textureUsage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST;\r\n\r\n // 位置纹理 (RGBA32Float) - 完整精度\r\n const positionTexture = device.createTexture({\r\n size: { width, height },\r\n format: \"rgba32float\",\r\n usage: textureUsage,\r\n });\r\n\r\n // 缩放+旋转纹理1 (RGBA32Float) - 保证精度\r\n const scaleRotTexture1 = device.createTexture({\r\n size: { width, height },\r\n format: \"rgba32float\",\r\n usage: textureUsage,\r\n });\r\n\r\n // 缩放+旋转纹理2 (RGBA32Float) - 保证精度\r\n const scaleRotTexture2 = device.createTexture({\r\n size: { width, height },\r\n format: \"rgba32float\",\r\n usage: textureUsage,\r\n });\r\n\r\n // 颜色纹理 (RGBA8Unorm)\r\n const colorTexture = device.createTexture({\r\n size: { width, height },\r\n format: \"rgba8unorm\",\r\n usage: textureUsage,\r\n });\r\n\r\n // ============================================\r\n // 上传数据到 GPU\r\n // ============================================\r\n \r\n // 位置纹理 (RGBA32Float = 16 bytes per pixel)\r\n device.queue.writeTexture(\r\n { texture: positionTexture },\r\n positionData,\r\n { bytesPerRow: width * 16 },\r\n { width, height }\r\n );\r\n\r\n // 缩放+旋转纹理1 (RGBA32Float = 16 bytes per pixel)\r\n device.queue.writeTexture(\r\n { texture: scaleRotTexture1 },\r\n scaleRotData1,\r\n { bytesPerRow: width * 16 },\r\n { width, height }\r\n );\r\n\r\n // 缩放+旋转纹理2 (RGBA32Float = 16 bytes per pixel)\r\n device.queue.writeTexture(\r\n { texture: scaleRotTexture2 },\r\n scaleRotData2,\r\n { bytesPerRow: width * 16 },\r\n { width, height }\r\n );\r\n\r\n // 颜色纹理 (RGBA8Unorm = 4 bytes per pixel)\r\n device.queue.writeTexture(\r\n { texture: colorTexture },\r\n colorData,\r\n { bytesPerRow: width * 4 },\r\n { width, height }\r\n );\r\n\r\n // 计算内存占用\r\n const memoryBytes = \r\n width * height * 16 + // positionTexture (RGBA32Float)\r\n width * height * 16 + // scaleRotTexture1 (RGBA32Float)\r\n width * height * 16 + // scaleRotTexture2 (RGBA32Float)\r\n width * height * 4; // colorTexture (RGBA8Unorm)\r\n const memoryMB = memoryBytes / (1024 * 1024);\r\n const bytesPerSplat = memoryBytes / count;\r\n\r\n return {\r\n width,\r\n height,\r\n count,\r\n positionTexture,\r\n scaleRotTexture1,\r\n scaleRotTexture2,\r\n colorTexture,\r\n boundingBox,\r\n };\r\n}\r\n\r\n/**\r\n * 销毁压缩纹理资源\r\n * @param textures 压缩纹理数据\r\n */\r\nexport function destroyCompressedTextures(textures: CompressedSplatTextures): void {\r\n textures.positionTexture.destroy();\r\n textures.scaleRotTexture1.destroy();\r\n textures.scaleRotTexture2.destroy();\r\n textures.colorTexture.destroy();\r\n}\r\n","/**\r\n * GSSplatSorterMobile - 移动端优化的 GPU 排序器\r\n *\r\n * 与主排序器的区别:\r\n * 1. 使用紧凑的位置数据(仅 xyz,无 scale/rotation)\r\n * 2. 简化的剔除逻辑(移除屏幕尺寸剔除)\r\n * 3. 针对 iOS 优化的桶数量\r\n */\r\n\r\n// 默认配置\r\nconst DEFAULT_NUM_BUCKETS = 65536;\r\nconst IOS_NUM_BUCKETS = 4096;\r\nconst WORKGROUP_SIZE = 256;\r\n\r\n/**\r\n * 排序器配置\r\n */\r\nexport interface SorterOptions {\r\n numBuckets?: number;\r\n}\r\n\r\n/**\r\n * 剔除选项\r\n */\r\nexport interface CullingOptions {\r\n nearPlane: number;\r\n farPlane: number;\r\n pixelThreshold: number;\r\n}\r\n\r\n/**\r\n * 检测是否为 iOS 设备\r\n */\r\nfunction isIOSDevice(): boolean {\r\n if (typeof navigator === \"undefined\") return false;\r\n const ua = navigator.userAgent || \"\";\r\n return (\r\n /iphone|ipad|ipod/i.test(ua.toLowerCase()) ||\r\n (navigator.platform === \"MacIntel\" && navigator.maxTouchPoints > 1)\r\n );\r\n}\r\n\r\n/**\r\n * 生成剔除 Shader(简化版,仅使用位置数据)\r\n */\r\nfunction generateCullingShaderCode(numBuckets: number): string {\r\n const bucketBits = Math.log2(numBuckets);\r\n\r\n return /* wgsl */ `\r\n/**\r\n * Pass 1: 剔除 + 深度计算 + 桶计数(移动端简化版)\r\n * 仅使用位置数据,不包含 scale 剔除\r\n */\r\n\r\nconst NUM_BUCKETS: u32 = ${numBuckets}u;\r\nconst BUCKET_MAX: u32 = ${numBuckets - 1}u;\r\n\r\nstruct CameraUniforms {\r\n view: mat4x4<f32>,\r\n proj: mat4x4<f32>,\r\n model: mat4x4<f32>,\r\n cameraPos: vec3<f32>,\r\n _pad: f32,\r\n}\r\n\r\nstruct CullingParams {\r\n splatCount: u32,\r\n nearPlane: f32,\r\n farPlane: f32,\r\n screenWidth: f32,\r\n screenHeight: f32,\r\n pixelThreshold: f32,\r\n _pad0: f32,\r\n _pad1: f32,\r\n}\r\n\r\nstruct Counters {\r\n visibleCount: atomic<u32>,\r\n}\r\n\r\n// 位置数据:紧密打包格式 [x0,y0,z0,x1,y1,z1,...]\r\n// 注意:不使用 array<vec3<f32>> 因为 vec3 有 16 字节对齐要求\r\n@group(0) @binding(0) var<storage, read> positions: array<f32>;\r\n@group(0) @binding(1) var<uniform> camera: CameraUniforms;\r\n@group(0) @binding(2) var<uniform> params: CullingParams;\r\n@group(0) @binding(3) var<storage, read_write> counters: Counters;\r\n@group(0) @binding(4) var<storage, read_write> visibleIndices: array<u32>;\r\n@group(0) @binding(5) var<storage, read_write> depthKeys: array<u32>;\r\n@group(0) @binding(6) var<storage, read_write> bucketCounts: array<atomic<u32>>;\r\n@group(0) @binding(7) var<storage, read_write> drawIndirect: array<u32>;\r\n\r\n@compute @workgroup_size(${WORKGROUP_SIZE})\r\nfn cullAndCount(@builtin(global_invocation_id) gid: vec3<u32>) {\r\n let i = gid.x;\r\n if (i >= params.splatCount) {\r\n return;\r\n }\r\n \r\n // 手动读取位置(避免 vec3 对齐问题)\r\n let base = i * 3u;\r\n let position = vec3<f32>(positions[base], positions[base + 1u], positions[base + 2u]);\r\n // 先应用模型矩阵变换到世界空间,再变换到视图空间\r\n let worldPos = camera.model * vec4<f32>(position, 1.0);\r\n let viewPos = camera.view * worldPos;\r\n let z = -viewPos.z;\r\n \r\n // 近平面剔除\r\n if (z < params.nearPlane) {\r\n return;\r\n }\r\n \r\n // 远平面剔除\r\n if (z > params.farPlane) {\r\n return;\r\n }\r\n \r\n // 视锥剔除(简化版,不考虑 splat 半径)\r\n let fx = camera.proj[0][0];\r\n let fy = camera.proj[1][1];\r\n let x_ndc = viewPos.x * fx / z;\r\n let y_ndc = viewPos.y * fy / z;\r\n \r\n // 放宽边界以避免边缘裁剪\r\n let margin: f32 = 0.5;\r\n if (x_ndc < -1.0 - margin || x_ndc > 1.0 + margin) {\r\n return;\r\n }\r\n if (y_ndc < -1.0 - margin || y_ndc > 1.0 + margin) {\r\n return;\r\n }\r\n \r\n // 通过剔除,计算深度桶\r\n let depthRange = params.farPlane - params.nearPlane;\r\n let normalizedDepth = clamp((z - params.nearPlane) / depthRange, 0.0, 1.0);\r\n let depthBucket = BUCKET_MAX - u32(normalizedDepth * f32(BUCKET_MAX));\r\n let depthKey = (depthBucket << ${32 - bucketBits}u) | (i & ${(1 << (32 - bucketBits)) - 1}u);\r\n \r\n // 分配可见索引位置\r\n let visibleIdx = atomicAdd(&counters.visibleCount, 1u);\r\n visibleIndices[visibleIdx] = i;\r\n depthKeys[visibleIdx] = depthKey;\r\n \r\n // 统计桶计数\r\n atomicAdd(&bucketCounts[depthBucket], 1u);\r\n}\r\n\r\n@compute @workgroup_size(1)\r\nfn resetCounters() {\r\n atomicStore(&counters.visibleCount, 0u);\r\n}\r\n\r\n@compute @workgroup_size(${WORKGROUP_SIZE})\r\nfn resetBucketCounts(@builtin(global_invocation_id) gid: vec3<u32>) {\r\n let i = gid.x;\r\n if (i < NUM_BUCKETS) {\r\n atomicStore(&bucketCounts[i], 0u);\r\n }\r\n}\r\n\r\n@compute @workgroup_size(1)\r\nfn updateDrawIndirect() {\r\n let count = atomicLoad(&counters.visibleCount);\r\n drawIndirect[0] = 4u;\r\n drawIndirect[1] = count;\r\n drawIndirect[2] = 0u;\r\n drawIndirect[3] = 0u;\r\n}\r\n`;\r\n}\r\n\r\n/**\r\n * 生成前缀和 Shader\r\n */\r\nfunction generatePrefixSumShaderCode(numBuckets: number): string {\r\n return /* wgsl */ `\r\nconst NUM_BUCKETS: u32 = ${numBuckets}u;\r\n\r\n@group(0) @binding(0) var<storage, read_write> bucketCounts: array<u32>;\r\n@group(0) @binding(1) var<storage, read_write> bucketOffsets: array<u32>;\r\n\r\n@compute @workgroup_size(1)\r\nfn prefixSum() {\r\n var sum = 0u;\r\n for (var i = 0u; i < NUM_BUCKETS; i++) {\r\n bucketOffsets[i] = sum;\r\n sum += bucketCounts[i];\r\n }\r\n}\r\n`;\r\n}\r\n\r\n/**\r\n * 生成散射 Shader\r\n */\r\nfunction generateScatterShaderCode(numBuckets: number): string {\r\n const bucketBits = Math.log2(numBuckets);\r\n\r\n return /* wgsl */ `\r\nconst NUM_BUCKETS: u32 = ${numBuckets}u;\r\n\r\nstruct Counters {\r\n visibleCount: u32,\r\n}\r\n\r\n@group(0) @binding(0) var<storage, read> visibleIndices: array<u32>;\r\n@group(0) @binding(1) var<storage, read> depthKeys: array<u32>;\r\n@group(0) @binding(2) var<storage, read> bucketOffsets: array<u32>;\r\n@group(0) @binding(3) var<storage, read_write> bucketPositions: array<atomic<u32>>;\r\n@group(0) @binding(4) var<storage, read_write> sortedIndices: array<u32>;\r\n@group(0) @binding(5) var<storage, read> counters: Counters;\r\n\r\n@compute @workgroup_size(${WORKGROUP_SIZE})\r\nfn scatter(@builtin(global_invocation_id) gid: vec3<u32>) {\r\n let i = gid.x;\r\n if (i >= counters.visibleCount) {\r\n return;\r\n }\r\n \r\n let depthKey = depthKeys[i];\r\n let bucket = depthKey >> ${32 - bucketBits}u;\r\n let baseOffset = bucketOffsets[bucket];\r\n let localOffset = atomicAdd(&bucketPositions[bucket], 1u);\r\n let finalIdx = baseOffset + localOffset;\r\n \r\n sortedIndices[finalIdx] = visibleIndices[i];\r\n}\r\n\r\n@compute @workgroup_size(${WORKGROUP_SIZE})\r\nfn resetBucketPositions(@builtin(global_invocation_id) gid: vec3<u32>) {\r\n let i = gid.x;\r\n if (i < NUM_BUCKETS) {\r\n atomicStore(&bucketPositions[i], 0u);\r\n }\r\n}\r\n`;\r\n}\r\n\r\n/**\r\n * GSSplatSorterMobile - 移动端排序器\r\n */\r\nexport class GSSplatSorterMobile {\r\n private device: GPUDevice;\r\n private splatCount: number;\r\n\r\n // Buffers\r\n private cullingParamsBuffer: GPUBuffer;\r\n private countersBuffer: GPUBuffer;\r\n private visibleIndicesBuffer: GPUBuffer;\r\n private depthKeysBuffer: GPUBuffer;\r\n private bucketCountsBuffer: GPUBuffer;\r\n private bucketOffsetsBuffer: GPUBuffer;\r\n private bucketPositionsBuffer: GPUBuffer;\r\n private sortedIndicesBuffer: GPUBuffer;\r\n private drawIndirectBuffer: GPUBuffer;\r\n\r\n // Pipelines\r\n private resetCountersPipeline: GPUComputePipeline;\r\n private resetBucketCountsPipeline: GPUComputePipeline;\r\n private cullAndCountPipeline: GPUComputePipeline;\r\n private updateDrawIndirectPipeline: GPUComputePipeline;\r\n private prefixSumPipeline: GPUComputePipeline;\r\n private resetBucketPositionsPipeline: GPUComputePipeline;\r\n private scatterPipeline: GPUComputePipeline;\r\n\r\n // Bind Groups\r\n private cullingBindGroupLayout: GPUBindGroupLayout;\r\n private cullingBindGroup: GPUBindGroup;\r\n private prefixSumBindGroupLayout: GPUBindGroupLayout;\r\n private prefixSumBindGroup: GPUBindGroup;\r\n private scatterBindGroupLayout: GPUBindGroupLayout;\r\n private scatterBindGroup: GPUBindGroup;\r\n\r\n private readonly WORKGROUP_SIZE = WORKGROUP_SIZE;\r\n private readonly numBuckets: number;\r\n\r\n // 屏幕信息\r\n private screenWidth: number = 1920;\r\n private screenHeight: number = 1080;\r\n\r\n // 剔除选项\r\n private cullingOptions: CullingOptions = {\r\n nearPlane: 0.1,\r\n farPlane: 1000,\r\n pixelThreshold: 1.0,\r\n };\r\n\r\n constructor(\r\n device: GPUDevice,\r\n splatCount: number,\r\n positionsBuffer: GPUBuffer, // 紧凑位置数据\r\n cameraBuffer: GPUBuffer,\r\n options: SorterOptions = {}\r\n ) {\r\n this.device = device;\r\n this.splatCount = splatCount;\r\n\r\n const isIOS = isIOSDevice();\r\n this.numBuckets = options.numBuckets ?? (isIOS ? IOS_NUM_BUCKETS : DEFAULT_NUM_BUCKETS);\r\n\r\n // 创建 Shader 模块\r\n const cullingModule = device.createShaderModule({\r\n code: generateCullingShaderCode(this.numBuckets),\r\n label: \"mobile-culling-shader\",\r\n });\r\n\r\n const prefixSumModule = device.createShaderModule({\r\n code: generatePrefixSumShaderCode(this.numBuckets),\r\n label: \"mobile-prefix-sum-shader\",\r\n });\r\n\r\n const scatterModule = device.createShaderModule({\r\n code: generateScatterShaderCode(this.numBuckets),\r\n label: \"mobile-scatter-shader\",\r\n });\r\n\r\n // 创建 Buffers\r\n this.cullingParamsBuffer = device.createBuffer({\r\n size: 32,\r\n usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\r\n });\r\n\r\n this.countersBuffer = device.createBuffer({\r\n size: 16,\r\n usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,\r\n });\r\n\r\n this.visibleIndicesBuffer = device.createBuffer({\r\n size: splatCount * 4,\r\n usage: GPUBufferUsage.STORAGE,\r\n });\r\n\r\n this.depthKeysBuffer = device.createBuffer({\r\n size: splatCount * 4,\r\n usage: GPUBufferUsage.STORAGE,\r\n });\r\n\r\n this.bucketCountsBuffer = device.createBuffer({\r\n size: this.numBuckets * 4,\r\n usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,\r\n });\r\n\r\n this.bucketOffsetsBuffer = device.createBuffer({\r\n size: this.numBuckets * 4,\r\n usage: GPUBufferUsage.STORAGE,\r\n });\r\n\r\n this.bucketPositionsBuffer = device.createBuffer({\r\n size: this.numBuckets * 4,\r\n usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,\r\n });\r\n\r\n this.sortedIndicesBuffer = device.createBuffer({\r\n size: splatCount * 4,\r\n usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,\r\n });\r\n\r\n this.drawIndirectBuffer = device.createBuffer({\r\n size: 16,\r\n usage: GPUBufferUsage.STORAGE | GPUBufferUsage.INDIRECT | GPUBufferUsage.COPY_DST,\r\n });\r\n\r\n // 创建 Bind Group Layouts 和 Pipelines\r\n // Culling bind group layout\r\n this.cullingBindGroupLayout = device.createBindGroupLayout({\r\n entries: [\r\n { binding: 0, visibility: GPUShaderStage.COMPUTE, buffer: { type: \"read-only-storage\" } },\r\n { binding: 1, visibility: GPUShaderStage.COMPUTE, buffer: { type: \"uniform\" } },\r\n { binding: 2, visibility: GPUShaderStage.COMPUTE, buffer: { type: \"uniform\" } },\r\n { binding: 3, visibility: GPUShaderStage.COMPUTE, buffer: { type: \"storage\" } },\r\n { binding: 4, visibility: GPUShaderStage.COMPUTE, buffer: { type: \"storage\" } },\r\n { binding: 5, visibility: GPUShaderStage.COMPUTE, buffer: { type: \"storage\" } },\r\n { binding: 6, visibility: GPUShaderStage.COMPUTE, buffer: { type: \"storage\" } },\r\n { binding: 7, visibility: GPUShaderStage.COMPUTE, buffer: { type: \"storage\" } },\r\n ],\r\n });\r\n\r\n const cullingPipelineLayout = device.createPipelineLayout({\r\n bindGroupLayouts: [this.cullingBindGroupLayout],\r\n });\r\n\r\n this.resetCountersPipeline = device.createComputePipeline({\r\n layout: cullingPipelineLayout,\r\n compute: { module: cullingModule, entryPoint: \"resetCounters\" },\r\n });\r\n\r\n this.resetBucketCountsPipeline = device.createComputePipeline({\r\n layout: cullingPipelineLayout,\r\n compute: { module: cullingModule, entryPoint: \"resetBucketCounts\" },\r\n });\r\n\r\n this.cullAndCountPipeline = device.createComputePipeline({\r\n layout: cullingPipelineLayout,\r\n compute: { module: cullingModule, entryPoint: \"cullAndCount\" },\r\n });\r\n\r\n this.updateDrawIndirectPipeline = device.createComputePipeline({\r\n layout: cullingPipelineLayout,\r\n compute: { module: cullingModule, entryPoint: \"updateDrawIndirect\" },\r\n });\r\n\r\n // Prefix sum bind group layout\r\n this.prefixSumBindGroupLayout = device.createBindGroupLayout({\r\n entries: [\r\n { binding: 0, visibility: GPUShaderStage.COMPUTE, buffer: { type: \"storage\" } },\r\n { binding: 1, visibility: GPUShaderStage.COMPUTE, buffer: { type: \"storage\" } },\r\n ],\r\n });\r\n\r\n const prefixSumPipelineLayout = device.createPipelineLayout({\r\n bindGroupLayouts: [this.prefixSumBindGroupLayout],\r\n });\r\n\r\n this.prefixSumPipeline = device.createComputePipeline({\r\n layout: prefixSumPipelineLayout,\r\n compute: { module: prefixSumModule, entryPoint: \"prefixSum\" },\r\n });\r\n\r\n // Scatter bind group layout\r\n this.scatterBindGroupLayout = device.createBindGroupLayout({\r\n entries: [\r\n { binding: 0, visibility: GPUShaderStage.COMPUTE, buffer: { type: \"read-only-storage\" } },\r\n { binding: 1, visibility: GPUShaderStage.COMPUTE, buffer: { type: \"read-only-storage\" } },\r\n { binding: 2, visibility: GPUShaderStage.COMPUTE, buffer: { type: \"read-only-storage\" } },\r\n { binding: 3, visibility: GPUShaderStage.COMPUTE, buffer: { type: \"storage\" } },\r\n { binding: 4, visibility: GPUShaderStage.COMPUTE, buffer: { type: \"storage\" } },\r\n { binding: 5, visibility: GPUShaderStage.COMPUTE, buffer: { type: \"read-only-storage\" } },\r\n ],\r\n });\r\n\r\n const scatterPipelineLayout = device.createPipelineLayout({\r\n bindGroupLayouts: [this.scatterBindGroupLayout],\r\n });\r\n\r\n this.scatterPipeline = device.createComputePipeline({\r\n layout: scatterPipelineLayout,\r\n compute: { module: scatterModule, entryPoint: \"scatter\" },\r\n });\r\n\r\n this.resetBucketPositionsPipeline = device.createComputePipeline({\r\n layout: scatterPipelineLayout,\r\n compute: { module: scatterModule, entryPoint: \"resetBucketPositions\" },\r\n });\r\n\r\n // 创建 Bind Groups\r\n this.cullingBindGroup = device.createBindGroup({\r\n layout: this.cullingBindGroupLayout,\r\n entries: [\r\n { binding: 0, resource: { buffer: positionsBuffer } },\r\n { binding: 1, resource: { buffer: cameraBuffer } },\r\n { binding: 2, resource: { buffer: this.cullingParamsBuffer } },\r\n { binding: 3, resource: { buffer: this.countersBuffer } },\r\n { binding: 4, resource: { buffer: this.visibleIndicesBuffer } },\r\n { binding: 5, resource: { buffer: this.depthKeysBuffer } },\r\n { binding: 6, resource: { buffer: this.bucketCountsBuffer } },\r\n { binding: 7, resource: { buffer: this.drawIndirectBuffer } },\r\n ],\r\n });\r\n\r\n this.prefixSumBindGroup = device.createBindGroup({\r\n layout: this.prefixSumBindGroupLayout,\r\n entries: [\r\n { binding: 0, resource: { buffer: this.bucketCountsBuffer } },\r\n { binding: 1, resource: { buffer: this.bucketOffsetsBuffer } },\r\n ],\r\n });\r\n\r\n this.scatterBindGroup = device.createBindGroup({\r\n layout: this.scatterBindGroupLayout,\r\n entries: [\r\n { binding: 0, resource: { buffer: this.visibleIndicesBuffer } },\r\n { binding: 1, resource: { buffer: this.depthKeysBuffer } },\r\n { binding: 2, resource: { buffer: this.bucketOffsetsBuffer } },\r\n { binding: 3, resource: { buffer: this.bucketPositionsBuffer } },\r\n { binding: 4, resource: { buffer: this.sortedIndicesBuffer } },\r\n { binding: 5, resource: { buffer: this.countersBuffer } },\r\n ],\r\n });\r\n }\r\n\r\n /**\r\n * 设置屏幕尺寸\r\n */\r\n setScreenSize(width: number, height: number): void {\r\n this.screenWidth = width;\r\n this.screenHeight = height;\r\n }\r\n\r\n /**\r\n * 设置剔除选项\r\n */\r\n setCullingOptions(options: Partial<CullingOptions>): void {\r\n this.cullingOptions = { ...this.cullingOptions, ...options };\r\n }\r\n\r\n /**\r\n * 执行排序\r\n */\r\n sort(): void {\r\n try {\r\n // 更新剔除参数\r\n const cullingParamsData = new ArrayBuffer(32);\r\n const cullingParamsView = new DataView(cullingParamsData);\r\n cullingParamsView.setUint32(0, this.splatCount, true);\r\n cullingParamsView.setFloat32(4, this.cullingOptions.nearPlane, true);\r\n cullingParamsView.setFloat32(8, this.cullingOptions.farPlane, true);\r\n cullingParamsView.setFloat32(12, this.screenWidth, true);\r\n cullingParamsView.setFloat32(16, this.screenHeight, true);\r\n cullingParamsView.setFloat32(20, this.cullingOptions.pixelThreshold, true);\r\n cullingParamsView.setFloat32(24, 0, true);\r\n cullingParamsView.setFloat32(28, 0, true);\r\n this.device.queue.writeBuffer(this.cullingParamsBuffer, 0, cullingParamsData);\r\n\r\n const cullWorkgroupCount = Math.ceil(this.splatCount / this.WORKGROUP_SIZE);\r\n const bucketResetWorkgroups = Math.ceil(this.numBuckets / this.WORKGROUP_SIZE);\r\n\r\n const encoder = this.device.createCommandEncoder();\r\n\r\n // Pass 1: 重置计数器\r\n {\r\n const pass = encoder.beginComputePass();\r\n pass.setPipeline(this.resetCountersPipeline);\r\n pass.setBindGroup(0, this.cullingBindGroup);\r\n pass.dispatchWorkgroups(1);\r\n pass.end();\r\n }\r\n\r\n // Pass 2: 重置桶计数\r\n {\r\n const pass = encoder.beginComputePass();\r\n pass.setPipeline(this.resetBucketCountsPipeline);\r\n pass.setBindGroup(0, this.cullingBindGroup);\r\n pass.dispatchWorkgroups(bucketResetWorkgroups);\r\n pass.end();\r\n }\r\n\r\n // Pass 3: 剔除 + 深度计算 + 桶计数\r\n {\r\n const pass = encoder.beginComputePass();\r\n pass.setPipeline(this.cullAndCountPipeline);\r\n pass.setBindGroup(0, this.cullingBindGroup);\r\n pass.dispatchWorkgroups(cullWorkgroupCount);\r\n pass.end();\r\n }\r\n\r\n // Pass 4: 更新 DrawIndirect\r\n {\r\n const pass = encoder.beginComputePass();\r\n pass.setPipeline(this.updateDrawIndirectPipeline);\r\n pass.setBindGroup(0, this.cullingBindGroup);\r\n pass.dispatchWorkgroups(1);\r\n pass.end();\r\n }\r\n\r\n // Pass 5: 前缀和\r\n {\r\n const pass = encoder.beginComputePass();\r\n pass.setPipeline(this.prefixSumPipeline);\r\n pass.setBindGroup(0, this.prefixSumBindGroup);\r\n pass.dispatchWorkgroups(1);\r\n pass.end();\r\n }\r\n\r\n // Pass 6: 重置桶位置计数\r\n {\r\n const pass = encoder.beginComputePass();\r\n pass.setPipeline(this.resetBucketPositionsPipeline);\r\n pass.setBindGroup(0, this.scatterBindGroup);\r\n pass.dispatchWorkgroups(bucketResetWorkgroups);\r\n pass.end();\r\n }\r\n\r\n // Pass 7: 散射到最终位置\r\n {\r\n const pass = encoder.beginComputePass();\r\n pass.setPipeline(this.scatterPipeline);\r\n pass.setBindGroup(0, this.scatterBindGroup);\r\n pass.dispatchWorkgroups(cullWorkgroupCount);\r\n pass.end();\r\n }\r\n\r\n this.device.queue.submit([encoder.finish()]);\r\n } catch (error) {\r\n // 排序错误(静默处理)\r\n }\r\n }\r\n\r\n /**\r\n * 获取排序后的索引 buffer\r\n */\r\n getIndicesBuffer(): GPUBuffer {\r\n return this.sortedIndicesBuffer;\r\n }\r\n\r\n /**\r\n * 获取 DrawIndirect buffer\r\n */\r\n getDrawIndirectBuffer(): GPUBuffer {\r\n return this.drawIndirectBuffer;\r\n }\r\n\r\n /**\r\n * 获取 splat 数量\r\n */\r\n getSplatCount(): number {\r\n return this.splatCount;\r\n }\r\n\r\n /**\r\n * 销毁资源\r\n */\r\n destroy(): void {\r\n this.cullingParamsBuffer.destroy();\r\n this.countersBuffer.destroy();\r\n this.visibleIndicesBuffer.destroy();\r\n this.depthKeysBuffer.destroy();\r\n this.bucketCountsBuffer.destroy();\r\n this.bucketOffsetsBuffer.destroy();\r\n this.bucketPositionsBuffer.destroy();\r\n this.sortedIndicesBuffer.destroy();\r\n this.drawIndirectBuffer.destroy();\r\n }\r\n}\r\n","/**\r\n * GSSplatRendererMobile - 移动端优化的 3D Gaussian Splatting 渲染器\r\n *\r\n * 优化特点:\r\n * 1. 使用纹理存储 splat 数据,减少 GPU 内存占用 (~52 bytes/splat vs 256 bytes)\r\n * 2. 仅支持 L0 模式(无 SH 计算)\r\n * 3. 从纹理采样获取 splat 属性(使用 RGBA32Float 保证精度)\r\n * 4. 使用简化的排序器\r\n */\r\n\r\nimport { Renderer } from \"../core/Renderer\";\r\nimport { Camera } from \"../core/Camera\";\r\nimport { CompactSplatData } from \"./PLYLoaderMobile\";\r\nimport {\r\n CompressedSplatTextures,\r\n compressSplatsToTextures,\r\n destroyCompressedTextures,\r\n} from \"./TextureCompressor\";\r\nimport { GSSplatSorterMobile } from \"./GSSplatSorterMobile\";\r\nimport { IGSSplatRenderer, BoundingBox, SHMode, RendererCapabilities, IGSSplatRendererWithCapabilities } from \"./IGSSplatRenderer\";\r\n\r\n// 重新导出 BoundingBox 以保持向后兼容\r\nexport type { BoundingBox } from \"./IGSSplatRenderer\";\r\n\r\n// ============================================\r\n// 移动端 L0 Shader - 从纹理采样数据(简化版)\r\n// ============================================\r\nconst shaderCodeMobileL0 = /* wgsl */ `\r\nstruct Uniforms {\r\n view: mat4x4<f32>,\r\n proj: mat4x4<f32>,\r\n model: mat4x4<f32>,\r\n cameraPos: vec3<f32>,\r\n _pad: f32,\r\n screenSize: vec2<f32>,\r\n _pad2: vec2<f32>,\r\n textureSize: vec2<f32>, // 纹理尺寸 (用于坐标计算)\r\n _pad3: vec2<f32>,\r\n}\r\n\r\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\r\n@group(0) @binding(1) var<storage, read> sortedIndices: array<u32>;\r\n\r\n// 纹理绑定 - 4 张纹理(使用 RGBA32Float 保证精度)\r\n@group(1) @binding(0) var positionTex: texture_2d<f32>; // RGBA32Float: xyz + unused\r\n@group(1) @binding(1) var scaleRotTex1: texture_2d<f32>; // RGBA32Float: scale_xyz + rot_w\r\n@group(1) @binding(2) var scaleRotTex2: texture_2d<f32>; // RGBA32Float: rot_xyz + unused\r\n@group(1) @binding(3) var colorTex: texture_2d<f32>; // RGBA8Unorm: rgb + opacity\r\n\r\nstruct VertexOutput {\r\n @builtin(position) position: vec4<f32>,\r\n @location(0) localUV: vec2<f32>,\r\n @location(1) color: vec3<f32>,\r\n @location(2) opacity: f32,\r\n}\r\n\r\nconst QUAD_POSITIONS = array<vec2<f32>, 4>(\r\n vec2<f32>(-1.0, -1.0),\r\n vec2<f32>( 1.0, -1.0),\r\n vec2<f32>(-1.0, 1.0),\r\n vec2<f32>( 1.0, 1.0),\r\n);\r\n\r\nconst ELLIPSE_SCALE: f32 = 3.0;\r\n\r\n// 将索引转换为纹理坐标\r\nfn indexToTexCoord(index: u32) -> vec2<u32> {\r\n let texWidth = u32(uniforms.textureSize.x);\r\n let x = index % texWidth;\r\n let y = index / texWidth;\r\n return vec2<u32>(x, y);\r\n}\r\n\r\n// 四元数转旋转矩阵\r\nfn quatToMat3(q: vec4<f32>) -> mat3x3<f32> {\r\n let w = q[0]; let x = q[1]; let y = q[2]; let z = q[3];\r\n let x2 = x + x; let y2 = y + y; let z2 = z + z;\r\n let xx = x * x2; let xy = x * y2; let xz = x * z2;\r\n let yy = y * y2; let yz = y * z2; let zz = z * z2;\r\n let wx = w * x2; let wy = w * y2; let wz = w * z2;\r\n return mat3x3<f32>(\r\n vec3<f32>(1.0 - (yy + zz), xy + wz, xz - wy),\r\n vec3<f32>(xy - wz, 1.0 - (xx + zz), yz + wx),\r\n vec3<f32>(xz + wy, yz - wx, 1.0 - (xx + yy))\r\n );\r\n}\r\n\r\n// 从模型矩阵提取统一缩放因子(取 X 轴向量长度)\r\nfn getModelScale(model: mat4x4<f32>) -> f32 {\r\n return length(model[0].xyz);\r\n}\r\n\r\n// 计算 2D 协方差\r\nfn computeCov2D(mean: vec3<f32>, scale: vec3<f32>, rotation: vec4<f32>, modelView: mat4x4<f32>, proj: mat4x4<f32>, modelScale: f32) -> vec3<f32> {\r\n let R = quatToMat3(rotation);\r\n // 应用模型缩放到 splat scale\r\n let scaledScale = scale * modelScale;\r\n let s2 = scaledScale * scaledScale;\r\n let M = mat3x3<f32>(R[0] * s2.x, R[1] * s2.y, R[2] * s2.z);\r\n let Sigma = M * transpose(R);\r\n let viewPos = (modelView * vec4<f32>(mean, 1.0)).xyz;\r\n let viewRot = mat3x3<f32>(modelView[0].xyz, modelView[1].xyz, modelView[2].xyz);\r\n let SigmaView = viewRot * Sigma * transpose(viewRot);\r\n let fx = proj[0][0]; let fy = proj[1][1];\r\n let z = -viewPos.z;\r\n let z_clamped = max(z, 0.001);\r\n let z2 = z_clamped * z_clamped;\r\n // 雅可比矩阵: 从相机坐标 (x_cam, y_cam, z_cam) 到 NDC 的偏导数\r\n // x_ndc = fx * x_cam / (-z_cam), 所以 dx_ndc/dz_cam = fx * x_cam / z_cam^2 (正号!)\r\n let j1 = vec3<f32>(fx / z_clamped, 0.0, fx * viewPos.x / z2);\r\n let j2 = vec3<f32>(0.0, fy / z_clamped, fy * viewPos.y / z2);\r\n let Sj1 = SigmaView * j1;\r\n let Sj2 = SigmaView * j2;\r\n return vec3<f32>(dot(j1, Sj1), dot(j1, Sj2), dot(j2, Sj2));\r\n}\r\n\r\n// 计算椭圆轴\r\nfn computeEllipseAxes(cov2D: vec3<f32>) -> mat2x2<f32> {\r\n let a = cov2D.x; let b = cov2D.y; let c = cov2D.z;\r\n let trace = a + c;\r\n let det = a * c - b * b;\r\n let disc = trace * trace - 4.0 * det;\r\n let sqrtDisc = sqrt(max(disc, 0.0));\r\n let lambda1 = max((trace + sqrtDisc) * 0.5, 0.0);\r\n let lambda2 = max((trace - sqrtDisc) * 0.5, 0.0);\r\n let r1 = sqrt(lambda1);\r\n let r2 = sqrt(lambda2);\r\n var axis1: vec2<f32>; var axis2: vec2<f32>;\r\n if (abs(b) > 1e-6) {\r\n axis1 = normalize(vec2<f32>(b, lambda1 - a));\r\n axis2 = vec2<f32>(-axis1.y, axis1.x);\r\n } else {\r\n if (a >= c) { axis1 = vec2<f32>(1.0, 0.0); axis2 = vec2<f32>(0.0, 1.0); }\r\n else { axis1 = vec2<f32>(0.0, 1.0); axis2 = vec2<f32>(1.0, 0.0); }\r\n }\r\n return mat2x2<f32>(axis1 * r1, axis2 * r2);\r\n}\r\n\r\n@vertex\r\nfn vs_main(@builtin(vertex_index) vertexIndex: u32, @builtin(instance_index) instanceIndex: u32) -> VertexOutput {\r\n var output: VertexOutput;\r\n \r\n // 获取排序后的索引\r\n let splatIndex = sortedIndices[instanceIndex];\r\n let texCoord = indexToTexCoord(splatIndex);\r\n \r\n // 从纹理采样位置数据(RGBA32Float,直接读取)\r\n let posSample = textureLoad(positionTex, texCoord, 0);\r\n let mean = posSample.xyz;\r\n \r\n // 从纹理采样缩放和旋转(RGBA16Float,GPU 自动转换为 f32)\r\n let scaleRot1 = textureLoad(scaleRotTex1, texCoord, 0);\r\n let scaleRot2 = textureLoad(scaleRotTex2, texCoord, 0);\r\n \r\n let scale = scaleRot1.xyz;\r\n let rotation = vec4<f32>(scaleRot1.w, scaleRot2.x, scaleRot2.y, scaleRot2.z);\r\n \r\n // 从纹理采样颜色(RGBA8Unorm,GPU 自动归一化到 0-1)\r\n let colorSample = textureLoad(colorTex, texCoord, 0);\r\n let color = colorSample.rgb;\r\n let opacity = colorSample.a;\r\n \r\n // 计算顶点位置\r\n let quadPos = QUAD_POSITIONS[vertexIndex];\r\n output.localUV = quadPos;\r\n \r\n // 计算 modelView 矩阵和模型缩放\r\n let modelView = uniforms.view * uniforms.model;\r\n let modelScale = getModelScale(uniforms.model);\r\n \r\n let cov2D = computeCov2D(mean, scale, rotation, modelView, uniforms.proj, modelScale);\r\n let axes = computeEllipseAxes(cov2D);\r\n let screenOffset = axes[0] * quadPos.x * ELLIPSE_SCALE + axes[1] * quadPos.y * ELLIPSE_SCALE;\r\n \r\n // 应用 model 变换到 splat 位置\r\n let worldPos = uniforms.model * vec4<f32>(mean, 1.0);\r\n let viewPos = uniforms.view * worldPos;\r\n var clipPos = uniforms.proj * viewPos;\r\n clipPos.x = clipPos.x + screenOffset.x * clipPos.w;\r\n clipPos.y = clipPos.y + screenOffset.y * clipPos.w;\r\n output.position = clipPos;\r\n output.color = color;\r\n output.opacity = opacity;\r\n \r\n return output;\r\n}\r\n\r\n@fragment\r\nfn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {\r\n let r = length(input.localUV);\r\n if (r > 1.0) { discard; }\r\n let gaussianWeight = exp(-r * r * 4.0);\r\n let alpha = input.opacity * gaussianWeight;\r\n if (alpha < 0.004) { discard; } // 丢弃几乎透明的像素\r\n let color = clamp(input.color, vec3<f32>(0.0), vec3<f32>(1.0));\r\n return vec4<f32>(color * alpha, alpha);\r\n}\r\n`;\r\n\r\n/**\r\n * GSSplatRendererMobile - 移动端优化渲染器\r\n * 实现 IGSSplatRenderer 接口\r\n */\r\nexport class GSSplatRendererMobile implements IGSSplatRendererWithCapabilities {\r\n private renderer: Renderer;\r\n private camera: Camera;\r\n\r\n // GPU 资源\r\n private pipeline!: GPURenderPipeline;\r\n private uniformBindGroupLayout!: GPUBindGroupLayout;\r\n private textureBindGroupLayout!: GPUBindGroupLayout;\r\n private uniformBuffer!: GPUBuffer;\r\n private uniformBindGroup: GPUBindGroup | null = null;\r\n private textureBindGroup: GPUBindGroup | null = null;\r\n\r\n // 压缩纹理数据\r\n private compressedTextures: CompressedSplatTextures | null = null;\r\n private splatCount: number = 0;\r\n\r\n // 排序器\r\n private sorter: GSSplatSorterMobile | null = null;\r\n\r\n // 位置缓冲区(用于排序)\r\n private positionsBuffer: GPUBuffer | null = null;\r\n\r\n // Bounding box\r\n private boundingBox: BoundingBox | null = null;\r\n\r\n // 帧计数(用于排序频率控制)\r\n private frameCount: number = 0;\r\n private sortEveryNFrames: number = 1;\r\n\r\n // ============================================\r\n // 变换相关 (position, rotation, scale)\r\n // ============================================\r\n private position: [number, number, number] = [0, 0, 0];\r\n private rotation: [number, number, number] = [0, 0, 0]; // Euler angles (radians)\r\n private scaleValue: [number, number, number] = [1, 1, 1];\r\n private pivot: [number, number, number] = [0, 0, 0]; // 旋转/缩放中心点\r\n private modelMatrix: Float32Array = new Float32Array(16); // 4x4 model matrix\r\n\r\n constructor(renderer: Renderer, camera: Camera) {\r\n this.renderer = renderer;\r\n this.camera = camera;\r\n\r\n this.createPipeline();\r\n this.createUniformBuffer();\r\n this.updateModelMatrix(); // 初始化模型矩阵为单位矩阵\r\n }\r\n\r\n // ============================================\r\n // Transform 方法\r\n // ============================================\r\n\r\n /**\r\n * 设置位置\r\n */\r\n setPosition(x: number, y: number, z: number): void {\r\n this.position = [x, y, z];\r\n this.updateModelMatrix();\r\n }\r\n\r\n /**\r\n * 获取位置\r\n */\r\n getPosition(): [number, number, number] {\r\n return [...this.position];\r\n }\r\n\r\n /**\r\n * 设置旋转 (欧拉角, 弧度)\r\n */\r\n setRotation(x: number, y: number, z: number): void {\r\n this.rotation = [x, y, z];\r\n this.updateModelMatrix();\r\n }\r\n\r\n /**\r\n * 获取旋转\r\n */\r\n getRotation(): [number, number, number] {\r\n return [...this.rotation];\r\n }\r\n\r\n /**\r\n * 设置缩放\r\n */\r\n setScale(x: number, y: number, z: number): void {\r\n this.scaleValue = [x, y, z];\r\n this.updateModelMatrix();\r\n }\r\n\r\n /**\r\n * 获取缩放\r\n */\r\n getScale(): [number, number, number] {\r\n return [...this.scaleValue];\r\n }\r\n\r\n /**\r\n * 设置旋转/缩放中心点 (pivot)\r\n */\r\n setPivot(x: number, y: number, z: number): void {\r\n this.pivot = [x, y, z];\r\n this.updateModelMatrix();\r\n }\r\n\r\n /**\r\n * 获取旋转/缩放中心点 (pivot)\r\n */\r\n getPivot(): [number, number, number] {\r\n return [...this.pivot];\r\n }\r\n\r\n /**\r\n * 更新模型矩阵\r\n * 变换顺序: T * Tp * R * S * Tp^-1\r\n * 即: 先移到原点,缩放,旋转,再移回pivot,最后应用用户平移\r\n */\r\n private updateModelMatrix(): void {\r\n const [tx, ty, tz] = this.position;\r\n const [rx, ry, rz] = this.rotation;\r\n const [sx, sy, sz] = this.scaleValue;\r\n const [px, py, pz] = this.pivot;\r\n\r\n // 计算旋转矩阵分量 (Euler XYZ 顺序)\r\n const cx = Math.cos(rx), sx1 = Math.sin(rx);\r\n const cy = Math.cos(ry), sy1 = Math.sin(ry);\r\n const cz = Math.cos(rz), sz1 = Math.sin(rz);\r\n\r\n // 组合旋转矩阵 R = Rz * Ry * Rx\r\n const r00 = cy * cz;\r\n const r01 = sx1 * sy1 * cz - cx * sz1;\r\n const r02 = cx * sy1 * cz + sx1 * sz1;\r\n const r10 = cy * sz1;\r\n const r11 = sx1 * sy1 * sz1 + cx * cz;\r\n const r12 = cx * sy1 * sz1 - sx1 * cz;\r\n const r20 = -sy1;\r\n const r21 = sx1 * cy;\r\n const r22 = cx * cy;\r\n\r\n // RS 矩阵 (旋转 * 缩放)\r\n const rs00 = r00 * sx, rs01 = r01 * sy, rs02 = r02 * sz;\r\n const rs10 = r10 * sx, rs11 = r11 * sy, rs12 = r12 * sz;\r\n const rs20 = r20 * sx, rs21 = r21 * sy, rs22 = r22 * sz;\r\n\r\n // 计算 (I - RS) * pivot\r\n const dpx = px - (rs00 * px + rs01 * py + rs02 * pz);\r\n const dpy = py - (rs10 * px + rs11 * py + rs12 * pz);\r\n const dpz = pz - (rs20 * px + rs21 * py + rs22 * pz);\r\n\r\n // 最终平移 = position + (I - RS) * pivot\r\n const finalTx = tx + dpx;\r\n const finalTy = ty + dpy;\r\n const finalTz = tz + dpz;\r\n\r\n // 模型矩阵 (列主序)\r\n this.modelMatrix[0] = rs00;\r\n this.modelMatrix[1] = rs10;\r\n this.modelMatrix[2] = rs20;\r\n this.modelMatrix[3] = 0;\r\n\r\n this.modelMatrix[4] = rs01;\r\n this.modelMatrix[5] = rs11;\r\n this.modelMatrix[6] = rs21;\r\n this.modelMatrix[7] = 0;\r\n\r\n this.modelMatrix[8] = rs02;\r\n this.modelMatrix[9] = rs12;\r\n this.modelMatrix[10] = rs22;\r\n this.modelMatrix[11] = 0;\r\n\r\n this.modelMatrix[12] = finalTx;\r\n this.modelMatrix[13] = finalTy;\r\n this.modelMatrix[14] = finalTz;\r\n this.modelMatrix[15] = 1;\r\n }\r\n\r\n /**\r\n * 获取当前模型矩阵\r\n */\r\n getModelMatrix(): Float32Array {\r\n return this.modelMatrix;\r\n }\r\n\r\n /**\r\n * 创建渲染管线\r\n */\r\n private createPipeline(): void {\r\n const device = this.renderer.device;\r\n\r\n // 创建 shader 模块\r\n const shaderModule = device.createShaderModule({\r\n code: shaderCodeMobileL0,\r\n label: \"mobile-splat-shader\",\r\n });\r\n\r\n // Uniform bind group layout (group 0)\r\n this.uniformBindGroupLayout = device.createBindGroupLayout({\r\n entries: [\r\n {\r\n binding: 0,\r\n visibility: GPUShaderStage.VERTEX,\r\n buffer: { type: \"uniform\" },\r\n },\r\n {\r\n binding: 1,\r\n visibility: GPUShaderStage.VERTEX,\r\n buffer: { type: \"read-only-storage\" },\r\n },\r\n ],\r\n });\r\n\r\n // Texture bind group layout (group 1) - 简化版 4 张纹理\r\n this.textureBindGroupLayout = device.createBindGroupLayout({\r\n entries: [\r\n {\r\n // positionTex (RGBA32Float)\r\n binding: 0,\r\n visibility: GPUShaderStage.VERTEX,\r\n texture: { sampleType: \"unfilterable-float\" },\r\n },\r\n {\r\n // scaleRotTex1 (RGBA32Float)\r\n binding: 1,\r\n visibility: GPUShaderStage.VERTEX,\r\n texture: { sampleType: \"unfilterable-float\" },\r\n },\r\n {\r\n // scaleRotTex2 (RGBA32Float)\r\n binding: 2,\r\n visibility: GPUShaderStage.VERTEX,\r\n texture: { sampleType: \"unfilterable-float\" },\r\n },\r\n {\r\n // colorTex (RGBA8Unorm)\r\n binding: 3,\r\n visibility: GPUShaderStage.VERTEX,\r\n texture: { sampleType: \"unfilterable-float\" },\r\n },\r\n ],\r\n });\r\n\r\n // Pipeline layout\r\n const pipelineLayout = device.createPipelineLayout({\r\n bindGroupLayouts: [this.uniformBindGroupLayout, this.textureBindGroupLayout],\r\n });\r\n\r\n // Blend state\r\n const blendState: GPUBlendState = {\r\n color: {\r\n srcFactor: \"one\",\r\n dstFactor: \"one-minus-src-alpha\",\r\n operation: \"add\",\r\n },\r\n alpha: {\r\n srcFactor: \"one\",\r\n dstFactor: \"one-minus-src-alpha\",\r\n operation: \"add\",\r\n },\r\n };\r\n\r\n // 创建管线\r\n this.pipeline = device.createRenderPipeline({\r\n layout: pipelineLayout,\r\n vertex: {\r\n module: shaderModule,\r\n entryPoint: \"vs_main\",\r\n buffers: [],\r\n },\r\n fragment: {\r\n module: shaderModule,\r\n entryPoint: \"fs_main\",\r\n targets: [\r\n {\r\n format: this.renderer.format,\r\n blend: blendState,\r\n },\r\n ],\r\n },\r\n primitive: {\r\n topology: \"triangle-strip\",\r\n },\r\n depthStencil: {\r\n format: this.renderer.depthFormat,\r\n depthWriteEnabled: false,\r\n depthCompare: \"always\",\r\n },\r\n });\r\n\r\n }\r\n\r\n /**\r\n * 创建 uniform buffer\r\n * 布局: view (64) + proj (64) + model (64) + cameraPos (12) + pad (4) + screenSize (8) + pad (8) + textureSize (8) + pad (8) = 240 bytes\r\n */\r\n private createUniformBuffer(): void {\r\n this.uniformBuffer = this.renderer.device.createBuffer({\r\n size: 240,\r\n usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\r\n });\r\n }\r\n\r\n /**\r\n * 设置紧凑格式的 splat 数据\r\n * @param data 紧凑格式的 splat 数据\r\n */\r\n setCompactData(data: CompactSplatData): void {\r\n try {\r\n const device = this.renderer.device;\r\n\r\n // 销毁旧资源\r\n this.destroyInternal();\r\n\r\n this.splatCount = data.count;\r\n this.frameCount = 0;\r\n\r\n if (this.splatCount === 0) {\r\n return;\r\n }\r\n\r\n // 压缩数据到纹理\r\n this.compressedTextures = compressSplatsToTextures(device, data);\r\n\r\n // 计算 bounding box\r\n this.boundingBox = this.computeBoundingBox(data);\r\n\r\n // 创建位置缓冲区(用于排序)\r\n this.positionsBuffer = device.createBuffer({\r\n size: data.count * 12, // 3 floats per position\r\n usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,\r\n });\r\n // 使用 new Float32Array 确保是标准 ArrayBuffer\r\n device.queue.writeBuffer(this.positionsBuffer, 0, new Float32Array(data.positions));\r\n\r\n // 创建排序器\r\n this.sorter = new GSSplatSorterMobile(\r\n device,\r\n this.splatCount,\r\n this.positionsBuffer,\r\n this.uniformBuffer\r\n );\r\n\r\n this.sorter.setScreenSize(this.renderer.width, this.renderer.height);\r\n this.sorter.setCullingOptions({\r\n nearPlane: this.camera.near,\r\n farPlane: this.camera.far,\r\n pixelThreshold: 1.0,\r\n });\r\n\r\n // 创建 bind groups\r\n this.createBindGroups();\r\n\r\n const memoryMB = (\r\n this.compressedTextures.width *\r\n this.compressedTextures.height *\r\n 52 / // 约 52 bytes per texel (16+16+16+4)\r\n (1024 * 1024)\r\n ).toFixed(2);\r\n } catch (error) {\r\n this.splatCount = 0;\r\n this.compressedTextures = null;\r\n this.sorter = null;\r\n }\r\n }\r\n\r\n /**\r\n * 创建 bind groups\r\n */\r\n private createBindGroups(): void {\r\n if (!this.compressedTextures || !this.sorter) return;\r\n\r\n const device = this.renderer.device;\r\n\r\n // Uniform bind group (group 0)\r\n this.uniformBindGroup = device.createBindGroup({\r\n layout: this.uniformBindGroupLayout,\r\n entries: [\r\n {\r\n binding: 0,\r\n resource: { buffer: this.uniformBuffer },\r\n },\r\n {\r\n binding: 1,\r\n resource: { buffer: this.sorter.getIndicesBuffer() },\r\n },\r\n ],\r\n });\r\n\r\n // Texture bind group (group 1) - 简化版 4 张纹理\r\n this.textureBindGroup = device.createBindGroup({\r\n layout: this.textureBindGroupLayout,\r\n entries: [\r\n {\r\n binding: 0,\r\n resource: this.compressedTextures.positionTexture.createView(),\r\n },\r\n {\r\n binding: 1,\r\n resource: this.compressedTextures.scaleRotTexture1.createView(),\r\n },\r\n {\r\n binding: 2,\r\n resource: this.compressedTextures.scaleRotTexture2.createView(),\r\n },\r\n {\r\n binding: 3,\r\n resource: this.compressedTextures.colorTexture.createView(),\r\n },\r\n ],\r\n });\r\n }\r\n\r\n /**\r\n * 计算 bounding box\r\n */\r\n private computeBoundingBox(data: CompactSplatData): BoundingBox {\r\n if (data.count === 0) {\r\n return { min: [0, 0, 0], max: [0, 0, 0], center: [0, 0, 0], radius: 0 };\r\n }\r\n\r\n const positions = data.positions;\r\n const min: [number, number, number] = [positions[0], positions[1], positions[2]];\r\n const max: [number, number, number] = [positions[0], positions[1], positions[2]];\r\n\r\n for (let i = 1; i < data.count; i++) {\r\n const x = positions[i * 3 + 0];\r\n const y = positions[i * 3 + 1];\r\n const z = positions[i * 3 + 2];\r\n min[0] = Math.min(min[0], x);\r\n min[1] = Math.min(min[1], y);\r\n min[2] = Math.min(min[2], z);\r\n max[0] = Math.max(max[0], x);\r\n max[1] = Math.max(max[1], y);\r\n max[2] = Math.max(max[2], z);\r\n }\r\n\r\n const center: [number, number, number] = [\r\n (min[0] + max[0]) / 2,\r\n (min[1] + max[1]) / 2,\r\n (min[2] + max[2]) / 2,\r\n ];\r\n\r\n const dx = max[0] - min[0];\r\n const dy = max[1] - min[1];\r\n const dz = max[2] - min[2];\r\n const radius = Math.sqrt(dx * dx + dy * dy + dz * dz) / 2;\r\n\r\n return { min, max, center, radius };\r\n }\r\n\r\n /**\r\n * 渲染\r\n * @param pass 渲染通道编码器\r\n */\r\n render(pass: GPURenderPassEncoder): void {\r\n if (\r\n this.splatCount === 0 ||\r\n !this.uniformBindGroup ||\r\n !this.textureBindGroup ||\r\n !this.sorter ||\r\n !this.compressedTextures\r\n ) {\r\n return;\r\n }\r\n\r\n this.frameCount++;\r\n\r\n // 更新 uniform buffer\r\n const device = this.renderer.device;\r\n device.queue.writeBuffer(this.uniformBuffer, 0, new Float32Array(this.camera.viewMatrix));\r\n device.queue.writeBuffer(this.uniformBuffer, 64, new Float32Array(this.camera.projectionMatrix));\r\n device.queue.writeBuffer(this.uniformBuffer, 128, new Float32Array(this.modelMatrix));\r\n device.queue.writeBuffer(this.uniformBuffer, 192, new Float32Array(this.camera.position));\r\n device.queue.writeBuffer(\r\n this.uniformBuffer,\r\n 208,\r\n new Float32Array([this.renderer.width, this.renderer.height, 0, 0])\r\n );\r\n device.queue.writeBuffer(\r\n this.uniformBuffer,\r\n 224,\r\n new Float32Array([this.compressedTextures.width, this.compressedTextures.height, 0, 0])\r\n );\r\n\r\n // 更新排序器参数\r\n this.sorter.setScreenSize(this.renderer.width, this.renderer.height);\r\n this.sorter.setCullingOptions({\r\n nearPlane: this.camera.near,\r\n farPlane: this.camera.far,\r\n pixelThreshold: 1.0,\r\n });\r\n\r\n // 排序(第一帧必须排序)\r\n const isFirstFrame = this.frameCount === 1;\r\n const shouldSort = isFirstFrame || this.frameCount % this.sortEveryNFrames === 0;\r\n\r\n if (shouldSort) {\r\n this.sorter.sort();\r\n }\r\n\r\n // 渲染\r\n pass.setPipeline(this.pipeline);\r\n pass.setBindGroup(0, this.uniformBindGroup);\r\n pass.setBindGroup(1, this.textureBindGroup);\r\n pass.drawIndirect(this.sorter.getDrawIndirectBuffer(), 0);\r\n }\r\n\r\n /**\r\n * 获取 splat 数量\r\n */\r\n getSplatCount(): number {\r\n return this.splatCount;\r\n }\r\n\r\n /**\r\n * 获取 bounding box\r\n */\r\n getBoundingBox(): BoundingBox | null {\r\n return this.boundingBox;\r\n }\r\n\r\n /**\r\n * 设置排序频率\r\n * @param n 每 n 帧排序一次\r\n */\r\n setSortFrequency(n: number): void {\r\n this.sortEveryNFrames = Math.max(1, n);\r\n }\r\n\r\n // ============================================\r\n // IGSSplatRenderer 接口实现 - SH 模式\r\n // ============================================\r\n\r\n /**\r\n * 设置 SH 模式(移动端仅支持 L0)\r\n */\r\n setSHMode(mode: SHMode): void {\r\n // 移动端仅支持 L0 模式\r\n }\r\n\r\n /**\r\n * 获取当前 SH 模式\r\n */\r\n getSHMode(): SHMode {\r\n return SHMode.L0;\r\n }\r\n\r\n /**\r\n * 是否支持指定的 SH 模式\r\n */\r\n supportsSHMode(mode: SHMode): boolean {\r\n return mode === SHMode.L0;\r\n }\r\n\r\n /**\r\n * 获取渲染器能力\r\n */\r\n getCapabilities(): RendererCapabilities {\r\n return {\r\n maxSHMode: SHMode.L0,\r\n supportsRawData: false,\r\n isMobileOptimized: true,\r\n maxSplatCount: 0, // 无限制(受 GPU 内存限制)\r\n };\r\n }\r\n\r\n /**\r\n * 内部销毁资源(不销毁管线)\r\n */\r\n private destroyInternal(): void {\r\n if (this.compressedTextures) {\r\n destroyCompressedTextures(this.compressedTextures);\r\n this.compressedTextures = null;\r\n }\r\n\r\n if (this.sorter) {\r\n this.sorter.destroy();\r\n this.sorter = null;\r\n }\r\n\r\n if (this.positionsBuffer) {\r\n this.positionsBuffer.destroy();\r\n this.positionsBuffer = null;\r\n }\r\n\r\n this.uniformBindGroup = null;\r\n this.textureBindGroup = null;\r\n this.splatCount = 0;\r\n this.boundingBox = null;\r\n }\r\n\r\n /**\r\n * 销毁资源\r\n */\r\n destroy(): void {\r\n this.destroyInternal();\r\n }\r\n}\r\n","/**\r\n * SceneManager - 场景管理器\r\n * \r\n * 负责管理场景中的所有对象:\r\n * - Mesh 网格\r\n * - Splat 点云\r\n * - 场景查询(bounding box 等)\r\n */\r\n\r\nimport { Mesh } from \"../mesh/Mesh\";\r\nimport { MeshRenderer } from \"../mesh/MeshRenderer\";\r\nimport { IGSSplatRenderer, BoundingBox } from \"../gs/IGSSplatRenderer\";\r\nimport { MeshBoundingBox } from \"../mesh/Mesh\";\r\n\r\n/**\r\n * 场景对象类型\r\n */\r\nexport type SceneObjectType = 'mesh' | 'splat' | 'meshGroup';\r\n\r\n/**\r\n * 场景对象信息\r\n */\r\nexport interface SceneObjectInfo {\r\n type: SceneObjectType;\r\n index: number;\r\n name?: string;\r\n}\r\n\r\n/**\r\n * SceneManager - 场景管理器\r\n */\r\nexport class SceneManager {\r\n private meshRenderer: MeshRenderer;\r\n private gsRenderer: IGSSplatRenderer | null = null;\r\n\r\n constructor(meshRenderer: MeshRenderer) {\r\n this.meshRenderer = meshRenderer;\r\n }\r\n\r\n // ============================================\r\n // Splat 渲染器管理\r\n // ============================================\r\n\r\n /**\r\n * 设置 GS Splat 渲染器\r\n */\r\n setGSRenderer(renderer: IGSSplatRenderer | null): void {\r\n this.gsRenderer = renderer;\r\n }\r\n\r\n /**\r\n * 获取 GS Splat 渲染器\r\n */\r\n getGSRenderer(): IGSSplatRenderer | null {\r\n return this.gsRenderer;\r\n }\r\n\r\n /**\r\n * 是否有 Splat 数据\r\n */\r\n hasSplats(): boolean {\r\n return this.gsRenderer !== null && this.gsRenderer.getSplatCount() > 0;\r\n }\r\n\r\n // ============================================\r\n // Mesh 管理(委托给 MeshRenderer)\r\n // ============================================\r\n\r\n /**\r\n * 获取 Mesh 数量\r\n */\r\n getMeshCount(): number {\r\n return this.meshRenderer.getMeshCount();\r\n }\r\n\r\n /**\r\n * 获取指定索引的 Mesh\r\n */\r\n getMeshByIndex(index: number): Mesh | null {\r\n return this.meshRenderer.getMeshByIndex(index);\r\n }\r\n\r\n /**\r\n * 获取指定范围的 Mesh\r\n */\r\n getMeshRange(startIndex: number, count: number): Mesh[] {\r\n const meshes: Mesh[] = [];\r\n for (let i = 0; i < count; i++) {\r\n const mesh = this.meshRenderer.getMeshByIndex(startIndex + i);\r\n if (mesh) {\r\n meshes.push(mesh);\r\n }\r\n }\r\n return meshes;\r\n }\r\n\r\n /**\r\n * 清空所有 Mesh\r\n */\r\n clearMeshes(): void {\r\n this.meshRenderer.clear();\r\n }\r\n\r\n /**\r\n * 按索引移除 Mesh\r\n */\r\n removeMeshByIndex(index: number): boolean {\r\n return this.meshRenderer.removeMeshByIndex(index);\r\n }\r\n\r\n // ============================================\r\n // Splat 管理\r\n // ============================================\r\n\r\n /**\r\n * 获取 Splat 数量\r\n */\r\n getSplatCount(): number {\r\n return this.gsRenderer?.getSplatCount() ?? 0;\r\n }\r\n\r\n /**\r\n * 清空 Splats\r\n */\r\n clearSplats(): void {\r\n if (this.gsRenderer) {\r\n this.gsRenderer.destroy();\r\n this.gsRenderer = null;\r\n }\r\n }\r\n\r\n // ============================================\r\n // Splat 变换\r\n // ============================================\r\n\r\n /**\r\n * 设置 Splat 位置\r\n */\r\n setSplatPosition(x: number, y: number, z: number): void {\r\n this.gsRenderer?.setPosition(x, y, z);\r\n }\r\n\r\n /**\r\n * 获取 Splat 位置\r\n */\r\n getSplatPosition(): [number, number, number] | null {\r\n return this.gsRenderer?.getPosition() ?? null;\r\n }\r\n\r\n /**\r\n * 设置 Splat 旋转\r\n */\r\n setSplatRotation(x: number, y: number, z: number): void {\r\n this.gsRenderer?.setRotation(x, y, z);\r\n }\r\n\r\n /**\r\n * 获取 Splat 旋转\r\n */\r\n getSplatRotation(): [number, number, number] | null {\r\n return this.gsRenderer?.getRotation() ?? null;\r\n }\r\n\r\n /**\r\n * 设置 Splat 缩放\r\n */\r\n setSplatScale(x: number, y: number, z: number): void {\r\n this.gsRenderer?.setScale(x, y, z);\r\n }\r\n\r\n /**\r\n * 获取 Splat 缩放\r\n */\r\n getSplatScale(): [number, number, number] | null {\r\n return this.gsRenderer?.getScale() ?? null;\r\n }\r\n\r\n // ============================================\r\n // SH 模式\r\n // ============================================\r\n\r\n /**\r\n * 设置 SH 模式\r\n */\r\n setSHMode(mode: 0 | 1 | 2 | 3): void {\r\n if (this.gsRenderer?.setSHMode) {\r\n this.gsRenderer.setSHMode(mode);\r\n }\r\n }\r\n\r\n /**\r\n * 获取当前 SH 模式\r\n */\r\n getSHMode(): number {\r\n return this.gsRenderer?.getSHMode?.() ?? 0;\r\n }\r\n\r\n // ============================================\r\n // Bounding Box 查询\r\n // ============================================\r\n\r\n /**\r\n * 获取 Mesh 的组合 bounding box\r\n */\r\n getMeshBoundingBox(): MeshBoundingBox | null {\r\n return this.meshRenderer.getCombinedBoundingBox();\r\n }\r\n\r\n /**\r\n * 获取 Splat 的 bounding box\r\n */\r\n getSplatBoundingBox(): BoundingBox | null {\r\n return this.gsRenderer?.getBoundingBox() ?? null;\r\n }\r\n\r\n /**\r\n * 获取指定 Mesh 范围的组合 bounding box\r\n */\r\n getMeshRangeBoundingBox(startIndex: number, count: number): BoundingBox | null {\r\n const meshes = this.getMeshRange(startIndex, count);\r\n if (meshes.length === 0) return null;\r\n\r\n let combinedMin: [number, number, number] | null = null;\r\n let combinedMax: [number, number, number] | null = null;\r\n\r\n for (const mesh of meshes) {\r\n const bbox = mesh.getWorldBoundingBox();\r\n if (!bbox) continue;\r\n\r\n if (combinedMin === null || combinedMax === null) {\r\n combinedMin = [...bbox.min];\r\n combinedMax = [...bbox.max];\r\n } else {\r\n combinedMin[0] = Math.min(combinedMin[0], bbox.min[0]);\r\n combinedMin[1] = Math.min(combinedMin[1], bbox.min[1]);\r\n combinedMin[2] = Math.min(combinedMin[2], bbox.min[2]);\r\n combinedMax[0] = Math.max(combinedMax[0], bbox.max[0]);\r\n combinedMax[1] = Math.max(combinedMax[1], bbox.max[1]);\r\n combinedMax[2] = Math.max(combinedMax[2], bbox.max[2]);\r\n }\r\n }\r\n\r\n if (combinedMin === null || combinedMax === null) return null;\r\n\r\n const center: [number, number, number] = [\r\n (combinedMin[0] + combinedMax[0]) / 2,\r\n (combinedMin[1] + combinedMax[1]) / 2,\r\n (combinedMin[2] + combinedMax[2]) / 2,\r\n ];\r\n const dx = combinedMax[0] - combinedMin[0];\r\n const dy = combinedMax[1] - combinedMin[1];\r\n const dz = combinedMax[2] - combinedMin[2];\r\n const radius = Math.sqrt(dx * dx + dy * dy + dz * dz) / 2;\r\n\r\n return { min: combinedMin, max: combinedMax, center, radius };\r\n }\r\n\r\n /**\r\n * 获取整个场景的组合 bounding box\r\n */\r\n getSceneBoundingBox(): BoundingBox | null {\r\n let combinedMin: [number, number, number] | null = null;\r\n let combinedMax: [number, number, number] | null = null;\r\n\r\n // 1. 获取 Mesh 的 bounding box\r\n const meshBBox = this.getMeshBoundingBox();\r\n if (meshBBox) {\r\n combinedMin = [...meshBBox.min];\r\n combinedMax = [...meshBBox.max];\r\n }\r\n\r\n // 2. 获取 Splat 的 bounding box\r\n const splatBBox = this.getSplatBoundingBox();\r\n if (splatBBox) {\r\n if (combinedMin === null || combinedMax === null) {\r\n combinedMin = [...splatBBox.min];\r\n combinedMax = [...splatBBox.max];\r\n } else {\r\n combinedMin[0] = Math.min(combinedMin[0], splatBBox.min[0]);\r\n combinedMin[1] = Math.min(combinedMin[1], splatBBox.min[1]);\r\n combinedMin[2] = Math.min(combinedMin[2], splatBBox.min[2]);\r\n combinedMax[0] = Math.max(combinedMax[0], splatBBox.max[0]);\r\n combinedMax[1] = Math.max(combinedMax[1], splatBBox.max[1]);\r\n combinedMax[2] = Math.max(combinedMax[2], splatBBox.max[2]);\r\n }\r\n }\r\n\r\n if (combinedMin === null || combinedMax === null) {\r\n return null;\r\n }\r\n\r\n const center: [number, number, number] = [\r\n (combinedMin[0] + combinedMax[0]) / 2,\r\n (combinedMin[1] + combinedMax[1]) / 2,\r\n (combinedMin[2] + combinedMax[2]) / 2,\r\n ];\r\n const dx = combinedMax[0] - combinedMin[0];\r\n const dy = combinedMax[1] - combinedMin[1];\r\n const dz = combinedMax[2] - combinedMin[2];\r\n const radius = Math.sqrt(dx * dx + dy * dy + dz * dz) / 2;\r\n\r\n return { min: combinedMin, max: combinedMax, center, radius };\r\n }\r\n\r\n // ============================================\r\n // 材质颜色\r\n // ============================================\r\n\r\n /**\r\n * 获取指定索引 Mesh 的材质颜色\r\n */\r\n getMeshColor(index: number): [number, number, number, number] | null {\r\n return this.meshRenderer.getMeshColor(index);\r\n }\r\n\r\n /**\r\n * 设置指定索引 Mesh 的材质颜色\r\n */\r\n setMeshColor(index: number, r: number, g: number, b: number, a: number = 1): boolean {\r\n return this.meshRenderer.setMeshColor(index, r, g, b, a);\r\n }\r\n\r\n /**\r\n * 设置指定范围内所有 Mesh 的材质颜色\r\n */\r\n setMeshRangeColor(startIndex: number, count: number, r: number, g: number, b: number, a: number = 1): number {\r\n return this.meshRenderer.setMeshRangeColor(startIndex, count, r, g, b, a);\r\n }\r\n\r\n /**\r\n * 销毁场景管理器\r\n */\r\n destroy(): void {\r\n this.clearSplats();\r\n // 注意:meshRenderer 的销毁由 App 负责\r\n }\r\n}\r\n","/**\r\n * Vec3 - 3D Vector utility class\r\n * Provides vector operations for 3D mathematics\r\n */\r\nexport class Vec3 {\r\n x: number;\r\n y: number;\r\n z: number;\r\n\r\n constructor(x: number = 0, y: number = 0, z: number = 0) {\r\n this.x = x;\r\n this.y = y;\r\n this.z = z;\r\n }\r\n\r\n // Factory methods\r\n static fromArray(arr: Float32Array | number[], offset: number = 0): Vec3 {\r\n return new Vec3(arr[offset], arr[offset + 1], arr[offset + 2]);\r\n }\r\n\r\n static zero(): Vec3 {\r\n return new Vec3(0, 0, 0);\r\n }\r\n\r\n static one(): Vec3 {\r\n return new Vec3(1, 1, 1);\r\n }\r\n\r\n // Basic operations (return new Vec3)\r\n add(v: Vec3): Vec3 {\r\n return new Vec3(this.x + v.x, this.y + v.y, this.z + v.z);\r\n }\r\n\r\n subtract(v: Vec3): Vec3 {\r\n return new Vec3(this.x - v.x, this.y - v.y, this.z - v.z);\r\n }\r\n\r\n multiply(scalar: number): Vec3 {\r\n return new Vec3(this.x * scalar, this.y * scalar, this.z * scalar);\r\n }\r\n\r\n divide(scalar: number): Vec3 {\r\n return new Vec3(this.x / scalar, this.y / scalar, this.z / scalar);\r\n }\r\n\r\n // In-place operations (modify this)\r\n addInPlace(v: Vec3): Vec3 {\r\n this.x += v.x;\r\n this.y += v.y;\r\n this.z += v.z;\r\n return this;\r\n }\r\n\r\n subtractInPlace(v: Vec3): Vec3 {\r\n this.x -= v.x;\r\n this.y -= v.y;\r\n this.z -= v.z;\r\n return this;\r\n }\r\n\r\n multiplyInPlace(scalar: number): Vec3 {\r\n this.x *= scalar;\r\n this.y *= scalar;\r\n this.z *= scalar;\r\n return this;\r\n }\r\n\r\n // Vector operations\r\n dot(v: Vec3): number {\r\n return this.x * v.x + this.y * v.y + this.z * v.z;\r\n }\r\n\r\n cross(v: Vec3): Vec3 {\r\n return new Vec3(\r\n this.y * v.z - this.z * v.y,\r\n this.z * v.x - this.x * v.z,\r\n this.x * v.y - this.y * v.x,\r\n );\r\n }\r\n\r\n length(): number {\r\n return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);\r\n }\r\n\r\n lengthSquared(): number {\r\n return this.x * this.x + this.y * this.y + this.z * this.z;\r\n }\r\n\r\n distance(v: Vec3): number {\r\n const dx = this.x - v.x;\r\n const dy = this.y - v.y;\r\n const dz = this.z - v.z;\r\n return Math.sqrt(dx * dx + dy * dy + dz * dz);\r\n }\r\n\r\n distanceSquared(v: Vec3): number {\r\n const dx = this.x - v.x;\r\n const dy = this.y - v.y;\r\n const dz = this.z - v.z;\r\n return dx * dx + dy * dy + dz * dz;\r\n }\r\n\r\n // Normalization\r\n normalize(): Vec3 {\r\n const len = this.length();\r\n if (len < 1e-10) {\r\n // Handle zero-length vector - return default direction\r\n return new Vec3(0, 0, 1);\r\n }\r\n return new Vec3(this.x / len, this.y / len, this.z / len);\r\n }\r\n\r\n normalizeInPlace(): Vec3 {\r\n const len = this.length();\r\n if (len < 1e-10) {\r\n // Handle zero-length vector - set to default direction\r\n this.x = 0;\r\n this.y = 0;\r\n this.z = 1;\r\n return this;\r\n }\r\n this.x /= len;\r\n this.y /= len;\r\n this.z /= len;\r\n return this;\r\n }\r\n\r\n // Utility\r\n clone(): Vec3 {\r\n return new Vec3(this.x, this.y, this.z);\r\n }\r\n\r\n toArray(): [number, number, number] {\r\n return [this.x, this.y, this.z];\r\n }\r\n\r\n set(x: number, y: number, z: number): Vec3 {\r\n this.x = x;\r\n this.y = y;\r\n this.z = z;\r\n return this;\r\n }\r\n\r\n /**\r\n * Check if this vector equals another vector\r\n */\r\n equals(v: Vec3): boolean {\r\n return this.x === v.x && this.y === v.y && this.z === v.z;\r\n }\r\n\r\n /**\r\n * Check if this vector approximately equals another vector\r\n */\r\n equalsApprox(v: Vec3, epsilon: number = 1e-6): boolean {\r\n return (\r\n Math.abs(this.x - v.x) < epsilon &&\r\n Math.abs(this.y - v.y) < epsilon &&\r\n Math.abs(this.z - v.z) < epsilon\r\n );\r\n }\r\n}\r\n","import { Vec3 } from \"./Vec3\";\r\nimport { Camera } from \"../Camera\";\r\n\r\n/**\r\n * Ray - Ray utility class\r\n * Provides ray operations for 3D picking and intersection tests\r\n */\r\nexport class Ray {\r\n origin: Vec3;\r\n direction: Vec3; // Should be normalized\r\n\r\n constructor(origin: Vec3, direction: Vec3) {\r\n this.origin = origin;\r\n this.direction = direction;\r\n }\r\n\r\n /**\r\n * Create a ray from screen coordinates\r\n * @param screenX - Screen X coordinate (0 to canvasWidth)\r\n * @param screenY - Screen Y coordinate (0 to canvasHeight)\r\n * @param canvasWidth - Canvas width in pixels\r\n * @param canvasHeight - Canvas height in pixels\r\n * @param camera - Camera instance\r\n */\r\n static fromScreenPoint(\r\n screenX: number,\r\n screenY: number,\r\n canvasWidth: number,\r\n canvasHeight: number,\r\n camera: Camera,\r\n ): Ray {\r\n // Convert screen coordinates to normalized device coordinates (-1 to 1)\r\n const ndcX = (screenX / canvasWidth) * 2 - 1;\r\n const ndcY = -(screenY / canvasHeight) * 2 + 1; // Flip Y axis\r\n\r\n // Create inverse view-projection matrix\r\n const invViewProj = Ray.invertMatrix(camera.viewProjectionMatrix);\r\n\r\n // Transform NDC point to world space (near plane)\r\n const nearPoint = Ray.transformPoint(invViewProj, ndcX, ndcY, -1);\r\n const farPoint = Ray.transformPoint(invViewProj, ndcX, ndcY, 1);\r\n\r\n // Ray origin is camera position\r\n const origin = new Vec3(\r\n camera.position[0],\r\n camera.position[1],\r\n camera.position[2],\r\n );\r\n\r\n // Ray direction is from near to far point\r\n const direction = new Vec3(\r\n farPoint.x - nearPoint.x,\r\n farPoint.y - nearPoint.y,\r\n farPoint.z - nearPoint.z,\r\n ).normalize();\r\n\r\n return new Ray(origin, direction);\r\n }\r\n\r\n /**\r\n * Get point at distance along ray\r\n * @param distance - Distance along ray\r\n */\r\n at(distance: number): Vec3 {\r\n return new Vec3(\r\n this.origin.x + this.direction.x * distance,\r\n this.origin.y + this.direction.y * distance,\r\n this.origin.z + this.direction.z * distance,\r\n );\r\n }\r\n\r\n /**\r\n * Intersect ray with plane\r\n * @param planeOrigin - Point on the plane\r\n * @param planeNormal - Normal vector of the plane (should be normalized)\r\n * @returns Distance along ray to intersection, or null if parallel/no intersection\r\n */\r\n intersectPlane(planeOrigin: Vec3, planeNormal: Vec3): number | null {\r\n const denom = this.direction.dot(planeNormal);\r\n\r\n // Check if ray is parallel to plane\r\n if (Math.abs(denom) < 1e-6) {\r\n return null;\r\n }\r\n\r\n const t = planeOrigin.subtract(this.origin).dot(planeNormal) / denom;\r\n\r\n // Return null if intersection is behind ray origin\r\n if (t < 0) {\r\n return null;\r\n }\r\n\r\n return t;\r\n }\r\n\r\n /**\r\n * Compute distance from ray to a point\r\n * @param point - Point in 3D space\r\n */\r\n distanceToPoint(point: Vec3): number {\r\n const v = point.subtract(this.origin);\r\n const t = v.dot(this.direction);\r\n\r\n // If t < 0, closest point is the ray origin\r\n if (t < 0) {\r\n return this.origin.distance(point);\r\n }\r\n\r\n // Closest point on ray\r\n const closestPoint = this.at(t);\r\n return closestPoint.distance(point);\r\n }\r\n\r\n /**\r\n * Compute distance from ray to a line segment (for capsule hit testing)\r\n * @param segmentStart - Start point of line segment\r\n * @param segmentEnd - End point of line segment\r\n */\r\n distanceToSegment(segmentStart: Vec3, segmentEnd: Vec3): number {\r\n const segmentDir = segmentEnd.subtract(segmentStart);\r\n const segmentLength = segmentDir.length();\r\n\r\n // Handle degenerate segment (point)\r\n if (segmentLength < 1e-10) {\r\n return this.distanceToPoint(segmentStart);\r\n }\r\n\r\n const segmentDirNorm = segmentDir.divide(segmentLength);\r\n\r\n // Compute closest points between ray and line segment\r\n const w0 = this.origin.subtract(segmentStart);\r\n const a = this.direction.dot(this.direction);\r\n const b = this.direction.dot(segmentDirNorm);\r\n const c = segmentDirNorm.dot(segmentDirNorm);\r\n const d = this.direction.dot(w0);\r\n const e = segmentDirNorm.dot(w0);\r\n\r\n const denom = a * c - b * b;\r\n let t = 0; // Parameter on ray\r\n let s = 0; // Parameter on segment\r\n\r\n if (Math.abs(denom) < 1e-6) {\r\n // Ray and segment are parallel\r\n t = 0;\r\n s = e / c;\r\n } else {\r\n t = (b * e - c * d) / denom;\r\n s = (a * e - b * d) / denom;\r\n }\r\n\r\n // Clamp t to non-negative (ray starts at origin)\r\n t = Math.max(0, t);\r\n\r\n // Clamp s to segment bounds [0, segmentLength]\r\n s = Math.max(0, Math.min(segmentLength, s));\r\n\r\n const pointOnRay = this.at(t);\r\n const pointOnSegment = segmentStart.add(segmentDirNorm.multiply(s));\r\n\r\n return pointOnRay.distance(pointOnSegment);\r\n }\r\n\r\n /**\r\n * Clone this ray\r\n */\r\n clone(): Ray {\r\n return new Ray(this.origin.clone(), this.direction.clone());\r\n }\r\n\r\n /**\r\n * Transform ray by a matrix\r\n * @param matrix - Transformation matrix\r\n */\r\n transform(matrix: { transformPoint(v: Vec3): Vec3; transformVector(v: Vec3): Vec3 }): Ray {\r\n const newOrigin = matrix.transformPoint(this.origin);\r\n const newDirection = matrix.transformVector(this.direction).normalize();\r\n return new Ray(newOrigin, newDirection);\r\n }\r\n\r\n /**\r\n * Intersect ray with triangle using Möller–Trumbore algorithm\r\n * @param v0 - First vertex of triangle\r\n * @param v1 - Second vertex of triangle\r\n * @param v2 - Third vertex of triangle\r\n * @returns Distance to intersection, or null if no intersection\r\n */\r\n intersectTriangle(v0: Vec3, v1: Vec3, v2: Vec3): number | null {\r\n const EPSILON = 1e-6;\r\n \r\n const edge1 = v1.subtract(v0);\r\n const edge2 = v2.subtract(v0);\r\n \r\n const h = this.direction.cross(edge2);\r\n const a = edge1.dot(h);\r\n \r\n // Ray is parallel to triangle\r\n if (Math.abs(a) < EPSILON) {\r\n return null;\r\n }\r\n \r\n const f = 1.0 / a;\r\n const s = this.origin.subtract(v0);\r\n const u = f * s.dot(h);\r\n \r\n // Intersection is outside triangle\r\n if (u < 0.0 || u > 1.0) {\r\n return null;\r\n }\r\n \r\n const q = s.cross(edge1);\r\n const v = f * this.direction.dot(q);\r\n \r\n // Intersection is outside triangle\r\n if (v < 0.0 || u + v > 1.0) {\r\n return null;\r\n }\r\n \r\n const t = f * edge2.dot(q);\r\n \r\n // Intersection is behind ray origin\r\n if (t < EPSILON) {\r\n return null;\r\n }\r\n \r\n return t;\r\n }\r\n\r\n // Helper methods for matrix operations\r\n private static invertMatrix(m: Float32Array): Float32Array {\r\n const inv = new Float32Array(16);\r\n const e = m;\r\n\r\n inv[0] =\r\n e[5] * e[10] * e[15] -\r\n e[5] * e[11] * e[14] -\r\n e[9] * e[6] * e[15] +\r\n e[9] * e[7] * e[14] +\r\n e[13] * e[6] * e[11] -\r\n e[13] * e[7] * e[10];\r\n inv[4] =\r\n -e[4] * e[10] * e[15] +\r\n e[4] * e[11] * e[14] +\r\n e[8] * e[6] * e[15] -\r\n e[8] * e[7] * e[14] -\r\n e[12] * e[6] * e[11] +\r\n e[12] * e[7] * e[10];\r\n inv[8] =\r\n e[4] * e[9] * e[15] -\r\n e[4] * e[11] * e[13] -\r\n e[8] * e[5] * e[15] +\r\n e[8] * e[7] * e[13] +\r\n e[12] * e[5] * e[11] -\r\n e[12] * e[7] * e[9];\r\n inv[12] =\r\n -e[4] * e[9] * e[14] +\r\n e[4] * e[10] * e[13] +\r\n e[8] * e[5] * e[14] -\r\n e[8] * e[6] * e[13] -\r\n e[12] * e[5] * e[10] +\r\n e[12] * e[6] * e[9];\r\n\r\n inv[1] =\r\n -e[1] * e[10] * e[15] +\r\n e[1] * e[11] * e[14] +\r\n e[9] * e[2] * e[15] -\r\n e[9] * e[3] * e[14] -\r\n e[13] * e[2] * e[11] +\r\n e[13] * e[3] * e[10];\r\n inv[5] =\r\n e[0] * e[10] * e[15] -\r\n e[0] * e[11] * e[14] -\r\n e[8] * e[2] * e[15] +\r\n e[8] * e[3] * e[14] +\r\n e[12] * e[2] * e[11] -\r\n e[12] * e[3] * e[10];\r\n inv[9] =\r\n -e[0] * e[9] * e[15] +\r\n e[0] * e[11] * e[13] +\r\n e[8] * e[1] * e[15] -\r\n e[8] * e[3] * e[13] -\r\n e[12] * e[1] * e[11] +\r\n e[12] * e[3] * e[9];\r\n inv[13] =\r\n e[0] * e[9] * e[14] -\r\n e[0] * e[10] * e[13] -\r\n e[8] * e[1] * e[14] +\r\n e[8] * e[2] * e[13] +\r\n e[12] * e[1] * e[10] -\r\n e[12] * e[2] * e[9];\r\n\r\n inv[2] =\r\n e[1] * e[6] * e[15] -\r\n e[1] * e[7] * e[14] -\r\n e[5] * e[2] * e[15] +\r\n e[5] * e[3] * e[14] +\r\n e[13] * e[2] * e[7] -\r\n e[13] * e[3] * e[6];\r\n inv[6] =\r\n -e[0] * e[6] * e[15] +\r\n e[0] * e[7] * e[14] +\r\n e[4] * e[2] * e[15] -\r\n e[4] * e[3] * e[14] -\r\n e[12] * e[2] * e[7] +\r\n e[12] * e[3] * e[6];\r\n inv[10] =\r\n e[0] * e[5] * e[15] -\r\n e[0] * e[7] * e[13] -\r\n e[4] * e[1] * e[15] +\r\n e[4] * e[3] * e[13] +\r\n e[12] * e[1] * e[7] -\r\n e[12] * e[3] * e[5];\r\n inv[14] =\r\n -e[0] * e[5] * e[14] +\r\n e[0] * e[6] * e[13] +\r\n e[4] * e[1] * e[14] -\r\n e[4] * e[2] * e[13] -\r\n e[12] * e[1] * e[6] +\r\n e[12] * e[2] * e[5];\r\n\r\n inv[3] =\r\n -e[1] * e[6] * e[11] +\r\n e[1] * e[7] * e[10] +\r\n e[5] * e[2] * e[11] -\r\n e[5] * e[3] * e[10] -\r\n e[9] * e[2] * e[7] +\r\n e[9] * e[3] * e[6];\r\n inv[7] =\r\n e[0] * e[6] * e[11] -\r\n e[0] * e[7] * e[10] -\r\n e[4] * e[2] * e[11] +\r\n e[4] * e[3] * e[10] +\r\n e[8] * e[2] * e[7] -\r\n e[8] * e[3] * e[6];\r\n inv[11] =\r\n -e[0] * e[5] * e[11] +\r\n e[0] * e[7] * e[9] +\r\n e[4] * e[1] * e[11] -\r\n e[4] * e[3] * e[9] -\r\n e[8] * e[1] * e[7] +\r\n e[8] * e[3] * e[5];\r\n inv[15] =\r\n e[0] * e[5] * e[10] -\r\n e[0] * e[6] * e[9] -\r\n e[4] * e[1] * e[10] +\r\n e[4] * e[2] * e[9] +\r\n e[8] * e[1] * e[6] -\r\n e[8] * e[2] * e[5];\r\n\r\n const det = e[0] * inv[0] + e[1] * inv[4] + e[2] * inv[8] + e[3] * inv[12];\r\n\r\n if (Math.abs(det) < 1e-10) {\r\n // Return identity if singular\r\n const identity = new Float32Array(16);\r\n identity[0] = identity[5] = identity[10] = identity[15] = 1;\r\n return identity;\r\n }\r\n\r\n const invDet = 1.0 / det;\r\n const result = new Float32Array(16);\r\n for (let i = 0; i < 16; i++) {\r\n result[i] = inv[i] * invDet;\r\n }\r\n\r\n return result;\r\n }\r\n\r\n private static transformPoint(\r\n m: Float32Array,\r\n x: number,\r\n y: number,\r\n z: number,\r\n ): Vec3 {\r\n const w = m[3] * x + m[7] * y + m[11] * z + m[15];\r\n return new Vec3(\r\n (m[0] * x + m[4] * y + m[8] * z + m[12]) / w,\r\n (m[1] * x + m[5] * y + m[9] * z + m[13]) / w,\r\n (m[2] * x + m[6] * y + m[10] * z + m[14]) / w,\r\n );\r\n }\r\n}\r\n","import { Vec3 } from \"./Vec3\";\r\n\r\n/**\r\n * Quat - Quaternion utility class\r\n * Provides quaternion operations for 3D rotations\r\n */\r\nexport class Quat {\r\n x: number;\r\n y: number;\r\n z: number;\r\n w: number;\r\n\r\n constructor(x: number = 0, y: number = 0, z: number = 0, w: number = 1) {\r\n this.x = x;\r\n this.y = y;\r\n this.z = z;\r\n this.w = w;\r\n }\r\n\r\n // Factory methods\r\n static identity(): Quat {\r\n return new Quat(0, 0, 0, 1);\r\n }\r\n\r\n /**\r\n * Create quaternion from Euler angles (ZYX order)\r\n * @param x - Rotation around X axis in radians\r\n * @param y - Rotation around Y axis in radians\r\n * @param z - Rotation around Z axis in radians\r\n */\r\n static fromEuler(x: number, y: number, z: number): Quat {\r\n const cx = Math.cos(x * 0.5);\r\n const cy = Math.cos(y * 0.5);\r\n const cz = Math.cos(z * 0.5);\r\n const sx = Math.sin(x * 0.5);\r\n const sy = Math.sin(y * 0.5);\r\n const sz = Math.sin(z * 0.5);\r\n\r\n // ZYX order\r\n return new Quat(\r\n sx * cy * cz - cx * sy * sz,\r\n cx * sy * cz + sx * cy * sz,\r\n cx * cy * sz - sx * sy * cz,\r\n cx * cy * cz + sx * sy * sz,\r\n );\r\n }\r\n\r\n /**\r\n * Create quaternion from axis-angle representation\r\n * @param axis - Rotation axis (should be normalized)\r\n * @param angle - Rotation angle in radians\r\n */\r\n static fromAxisAngle(axis: Vec3, angle: number): Quat {\r\n const halfAngle = angle * 0.5;\r\n const s = Math.sin(halfAngle);\r\n return new Quat(axis.x * s, axis.y * s, axis.z * s, Math.cos(halfAngle));\r\n }\r\n\r\n // Operations\r\n /**\r\n * Multiply this quaternion by another (this * q)\r\n */\r\n multiply(q: Quat): Quat {\r\n return new Quat(\r\n this.w * q.x + this.x * q.w + this.y * q.z - this.z * q.y,\r\n this.w * q.y - this.x * q.z + this.y * q.w + this.z * q.x,\r\n this.w * q.z + this.x * q.y - this.y * q.x + this.z * q.w,\r\n this.w * q.w - this.x * q.x - this.y * q.y - this.z * q.z,\r\n );\r\n }\r\n\r\n /**\r\n * Convert quaternion to Euler angles (ZYX order)\r\n */\r\n toEuler(): Vec3 {\r\n // Roll (x-axis rotation)\r\n const sinr_cosp = 2 * (this.w * this.x + this.y * this.z);\r\n const cosr_cosp = 1 - 2 * (this.x * this.x + this.y * this.y);\r\n const roll = Math.atan2(sinr_cosp, cosr_cosp);\r\n\r\n // Pitch (y-axis rotation)\r\n const sinp = 2 * (this.w * this.y - this.z * this.x);\r\n let pitch: number;\r\n if (Math.abs(sinp) >= 1) {\r\n pitch = (Math.sign(sinp) * Math.PI) / 2; // Use 90 degrees if out of range\r\n } else {\r\n pitch = Math.asin(sinp);\r\n }\r\n\r\n // Yaw (z-axis rotation)\r\n const siny_cosp = 2 * (this.w * this.z + this.x * this.y);\r\n const cosy_cosp = 1 - 2 * (this.y * this.y + this.z * this.z);\r\n const yaw = Math.atan2(siny_cosp, cosy_cosp);\r\n\r\n return new Vec3(roll, pitch, yaw);\r\n }\r\n\r\n // Normalization\r\n normalize(): Quat {\r\n const len = Math.sqrt(\r\n this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w,\r\n );\r\n if (len < 1e-10) {\r\n return Quat.identity();\r\n }\r\n return new Quat(this.x / len, this.y / len, this.z / len, this.w / len);\r\n }\r\n\r\n normalizeInPlace(): Quat {\r\n const len = Math.sqrt(\r\n this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w,\r\n );\r\n if (len < 1e-10) {\r\n this.x = 0;\r\n this.y = 0;\r\n this.z = 0;\r\n this.w = 1;\r\n return this;\r\n }\r\n this.x /= len;\r\n this.y /= len;\r\n this.z /= len;\r\n this.w /= len;\r\n return this;\r\n }\r\n\r\n // Interpolation\r\n /**\r\n * Spherical linear interpolation between this quaternion and another\r\n * @param q - Target quaternion\r\n * @param t - Interpolation factor (0 to 1)\r\n */\r\n slerp(q: Quat, t: number): Quat {\r\n let dot = this.x * q.x + this.y * q.y + this.z * q.z + this.w * q.w;\r\n\r\n // If the dot product is negative, slerp won't take the shorter path\r\n // Fix by reversing one quaternion\r\n let q2 = q;\r\n if (dot < 0) {\r\n q2 = new Quat(-q.x, -q.y, -q.z, -q.w);\r\n dot = -dot;\r\n }\r\n\r\n // If quaternions are very close, use linear interpolation\r\n if (dot > 0.9995) {\r\n return new Quat(\r\n this.x + t * (q2.x - this.x),\r\n this.y + t * (q2.y - this.y),\r\n this.z + t * (q2.z - this.z),\r\n this.w + t * (q2.w - this.w),\r\n ).normalize();\r\n }\r\n\r\n // Calculate coefficients\r\n const theta = Math.acos(dot);\r\n const sinTheta = Math.sin(theta);\r\n const w1 = Math.sin((1 - t) * theta) / sinTheta;\r\n const w2 = Math.sin(t * theta) / sinTheta;\r\n\r\n return new Quat(\r\n this.x * w1 + q2.x * w2,\r\n this.y * w1 + q2.y * w2,\r\n this.z * w1 + q2.z * w2,\r\n this.w * w1 + q2.w * w2,\r\n );\r\n }\r\n\r\n // Utility\r\n clone(): Quat {\r\n return new Quat(this.x, this.y, this.z, this.w);\r\n }\r\n\r\n /**\r\n * Get the inverse (conjugate for unit quaternions) of this quaternion\r\n */\r\n inverse(): Quat {\r\n const lenSq = this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;\r\n if (lenSq < 1e-10) {\r\n return Quat.identity();\r\n }\r\n const invLen = 1.0 / lenSq;\r\n return new Quat(\r\n -this.x * invLen,\r\n -this.y * invLen,\r\n -this.z * invLen,\r\n this.w * invLen\r\n );\r\n }\r\n\r\n /**\r\n * Transform a vector by this quaternion\r\n * @param v - Vector to transform\r\n */\r\n transformVector(v: Vec3): Vec3 {\r\n // q * v * q^-1\r\n const qx = this.x, qy = this.y, qz = this.z, qw = this.w;\r\n const vx = v.x, vy = v.y, vz = v.z;\r\n\r\n // Calculate quat * vec\r\n const ix = qw * vx + qy * vz - qz * vy;\r\n const iy = qw * vy + qz * vx - qx * vz;\r\n const iz = qw * vz + qx * vy - qy * vx;\r\n const iw = -qx * vx - qy * vy - qz * vz;\r\n\r\n // Calculate result * inverse quat\r\n return new Vec3(\r\n ix * qw + iw * -qx + iy * -qz - iz * -qy,\r\n iy * qw + iw * -qy + iz * -qx - ix * -qz,\r\n iz * qw + iw * -qz + ix * -qy - iy * -qx\r\n );\r\n }\r\n}\r\n","import { Vec3 } from \"./Vec3\";\r\nimport { Quat } from \"./Quat\";\r\n\r\n/**\r\n * Mat4 - 4x4 Matrix utility class\r\n * Provides matrix operations for 3D transformations\r\n * Storage is column-major order (OpenGL/WebGPU convention)\r\n */\r\nexport class Mat4 {\r\n elements: Float32Array; // 16 elements, column-major order\r\n\r\n constructor() {\r\n this.elements = new Float32Array(16);\r\n }\r\n\r\n // Factory methods\r\n static identity(): Mat4 {\r\n const m = new Mat4();\r\n m.elements[0] = 1;\r\n m.elements[5] = 1;\r\n m.elements[10] = 1;\r\n m.elements[15] = 1;\r\n return m;\r\n }\r\n\r\n static fromTranslation(v: Vec3): Mat4 {\r\n const m = Mat4.identity();\r\n m.elements[12] = v.x;\r\n m.elements[13] = v.y;\r\n m.elements[14] = v.z;\r\n return m;\r\n }\r\n\r\n static fromRotation(q: Quat): Mat4 {\r\n const m = new Mat4();\r\n const e = m.elements;\r\n\r\n const x2 = q.x + q.x;\r\n const y2 = q.y + q.y;\r\n const z2 = q.z + q.z;\r\n const xx = q.x * x2;\r\n const xy = q.x * y2;\r\n const xz = q.x * z2;\r\n const yy = q.y * y2;\r\n const yz = q.y * z2;\r\n const zz = q.z * z2;\r\n const wx = q.w * x2;\r\n const wy = q.w * y2;\r\n const wz = q.w * z2;\r\n\r\n e[0] = 1 - (yy + zz);\r\n e[1] = xy + wz;\r\n e[2] = xz - wy;\r\n e[3] = 0;\r\n\r\n e[4] = xy - wz;\r\n e[5] = 1 - (xx + zz);\r\n e[6] = yz + wx;\r\n e[7] = 0;\r\n\r\n e[8] = xz + wy;\r\n e[9] = yz - wx;\r\n e[10] = 1 - (xx + yy);\r\n e[11] = 0;\r\n\r\n e[12] = 0;\r\n e[13] = 0;\r\n e[14] = 0;\r\n e[15] = 1;\r\n\r\n return m;\r\n }\r\n\r\n static fromScale(v: Vec3): Mat4 {\r\n const m = new Mat4();\r\n m.elements[0] = v.x;\r\n m.elements[5] = v.y;\r\n m.elements[10] = v.z;\r\n m.elements[15] = 1;\r\n return m;\r\n }\r\n\r\n static compose(position: Vec3, rotation: Quat, scale: Vec3): Mat4 {\r\n const m = Mat4.fromRotation(rotation);\r\n const e = m.elements;\r\n\r\n // Apply scale\r\n e[0] *= scale.x;\r\n e[1] *= scale.x;\r\n e[2] *= scale.x;\r\n e[4] *= scale.y;\r\n e[5] *= scale.y;\r\n e[6] *= scale.y;\r\n e[8] *= scale.z;\r\n e[9] *= scale.z;\r\n e[10] *= scale.z;\r\n\r\n // Apply translation\r\n e[12] = position.x;\r\n e[13] = position.y;\r\n e[14] = position.z;\r\n\r\n return m;\r\n }\r\n\r\n // Operations\r\n multiply(m: Mat4): Mat4 {\r\n const result = new Mat4();\r\n const a = this.elements;\r\n const b = m.elements;\r\n const r = result.elements;\r\n\r\n for (let i = 0; i < 4; i++) {\r\n for (let j = 0; j < 4; j++) {\r\n r[i * 4 + j] =\r\n a[j] * b[i * 4] +\r\n a[j + 4] * b[i * 4 + 1] +\r\n a[j + 8] * b[i * 4 + 2] +\r\n a[j + 12] * b[i * 4 + 3];\r\n }\r\n }\r\n\r\n return result;\r\n }\r\n\r\n multiplyInPlace(m: Mat4): Mat4 {\r\n const temp = this.multiply(m);\r\n this.elements.set(temp.elements);\r\n return this;\r\n }\r\n\r\n inverse(): Mat4 | null {\r\n const e = this.elements;\r\n const inv = new Float32Array(16);\r\n\r\n inv[0] =\r\n e[5] * e[10] * e[15] -\r\n e[5] * e[11] * e[14] -\r\n e[9] * e[6] * e[15] +\r\n e[9] * e[7] * e[14] +\r\n e[13] * e[6] * e[11] -\r\n e[13] * e[7] * e[10];\r\n inv[4] =\r\n -e[4] * e[10] * e[15] +\r\n e[4] * e[11] * e[14] +\r\n e[8] * e[6] * e[15] -\r\n e[8] * e[7] * e[14] -\r\n e[12] * e[6] * e[11] +\r\n e[12] * e[7] * e[10];\r\n inv[8] =\r\n e[4] * e[9] * e[15] -\r\n e[4] * e[11] * e[13] -\r\n e[8] * e[5] * e[15] +\r\n e[8] * e[7] * e[13] +\r\n e[12] * e[5] * e[11] -\r\n e[12] * e[7] * e[9];\r\n inv[12] =\r\n -e[4] * e[9] * e[14] +\r\n e[4] * e[10] * e[13] +\r\n e[8] * e[5] * e[14] -\r\n e[8] * e[6] * e[13] -\r\n e[12] * e[5] * e[10] +\r\n e[12] * e[6] * e[9];\r\n\r\n inv[1] =\r\n -e[1] * e[10] * e[15] +\r\n e[1] * e[11] * e[14] +\r\n e[9] * e[2] * e[15] -\r\n e[9] * e[3] * e[14] -\r\n e[13] * e[2] * e[11] +\r\n e[13] * e[3] * e[10];\r\n inv[5] =\r\n e[0] * e[10] * e[15] -\r\n e[0] * e[11] * e[14] -\r\n e[8] * e[2] * e[15] +\r\n e[8] * e[3] * e[14] +\r\n e[12] * e[2] * e[11] -\r\n e[12] * e[3] * e[10];\r\n inv[9] =\r\n -e[0] * e[9] * e[15] +\r\n e[0] * e[11] * e[13] +\r\n e[8] * e[1] * e[15] -\r\n e[8] * e[3] * e[13] -\r\n e[12] * e[1] * e[11] +\r\n e[12] * e[3] * e[9];\r\n inv[13] =\r\n e[0] * e[9] * e[14] -\r\n e[0] * e[10] * e[13] -\r\n e[8] * e[1] * e[14] +\r\n e[8] * e[2] * e[13] +\r\n e[12] * e[1] * e[10] -\r\n e[12] * e[2] * e[9];\r\n\r\n inv[2] =\r\n e[1] * e[6] * e[15] -\r\n e[1] * e[7] * e[14] -\r\n e[5] * e[2] * e[15] +\r\n e[5] * e[3] * e[14] +\r\n e[13] * e[2] * e[7] -\r\n e[13] * e[3] * e[6];\r\n inv[6] =\r\n -e[0] * e[6] * e[15] +\r\n e[0] * e[7] * e[14] +\r\n e[4] * e[2] * e[15] -\r\n e[4] * e[3] * e[14] -\r\n e[12] * e[2] * e[7] +\r\n e[12] * e[3] * e[6];\r\n inv[10] =\r\n e[0] * e[5] * e[15] -\r\n e[0] * e[7] * e[13] -\r\n e[4] * e[1] * e[15] +\r\n e[4] * e[3] * e[13] +\r\n e[12] * e[1] * e[7] -\r\n e[12] * e[3] * e[5];\r\n inv[14] =\r\n -e[0] * e[5] * e[14] +\r\n e[0] * e[6] * e[13] +\r\n e[4] * e[1] * e[14] -\r\n e[4] * e[2] * e[13] -\r\n e[12] * e[1] * e[6] +\r\n e[12] * e[2] * e[5];\r\n\r\n inv[3] =\r\n -e[1] * e[6] * e[11] +\r\n e[1] * e[7] * e[10] +\r\n e[5] * e[2] * e[11] -\r\n e[5] * e[3] * e[10] -\r\n e[9] * e[2] * e[7] +\r\n e[9] * e[3] * e[6];\r\n inv[7] =\r\n e[0] * e[6] * e[11] -\r\n e[0] * e[7] * e[10] -\r\n e[4] * e[2] * e[11] +\r\n e[4] * e[3] * e[10] +\r\n e[8] * e[2] * e[7] -\r\n e[8] * e[3] * e[6];\r\n inv[11] =\r\n -e[0] * e[5] * e[11] +\r\n e[0] * e[7] * e[9] +\r\n e[4] * e[1] * e[11] -\r\n e[4] * e[3] * e[9] -\r\n e[8] * e[1] * e[7] +\r\n e[8] * e[3] * e[5];\r\n inv[15] =\r\n e[0] * e[5] * e[10] -\r\n e[0] * e[6] * e[9] -\r\n e[4] * e[1] * e[10] +\r\n e[4] * e[2] * e[9] +\r\n e[8] * e[1] * e[6] -\r\n e[8] * e[2] * e[5];\r\n\r\n const det = e[0] * inv[0] + e[1] * inv[4] + e[2] * inv[8] + e[3] * inv[12];\r\n\r\n if (Math.abs(det) < 1e-10) {\r\n return null; // Matrix is singular\r\n }\r\n\r\n const invDet = 1.0 / det;\r\n const result = new Mat4();\r\n for (let i = 0; i < 16; i++) {\r\n result.elements[i] = inv[i] * invDet;\r\n }\r\n\r\n return result;\r\n }\r\n\r\n transpose(): Mat4 {\r\n const m = new Mat4();\r\n const e = this.elements;\r\n const r = m.elements;\r\n\r\n r[0] = e[0];\r\n r[1] = e[4];\r\n r[2] = e[8];\r\n r[3] = e[12];\r\n r[4] = e[1];\r\n r[5] = e[5];\r\n r[6] = e[9];\r\n r[7] = e[13];\r\n r[8] = e[2];\r\n r[9] = e[6];\r\n r[10] = e[10];\r\n r[11] = e[14];\r\n r[12] = e[3];\r\n r[13] = e[7];\r\n r[14] = e[11];\r\n r[15] = e[15];\r\n\r\n return m;\r\n }\r\n\r\n // Decomposition\r\n decompose(): { position: Vec3; rotation: Quat; scale: Vec3 } | null {\r\n const e = this.elements;\r\n\r\n // Extract translation\r\n const position = new Vec3(e[12], e[13], e[14]);\r\n\r\n // Extract scale\r\n const sx = Math.sqrt(e[0] * e[0] + e[1] * e[1] + e[2] * e[2]);\r\n const sy = Math.sqrt(e[4] * e[4] + e[5] * e[5] + e[6] * e[6]);\r\n const sz = Math.sqrt(e[8] * e[8] + e[9] * e[9] + e[10] * e[10]);\r\n\r\n const scale = new Vec3(sx, sy, sz);\r\n\r\n // Check for zero scale\r\n if (sx < 1e-10 || sy < 1e-10 || sz < 1e-10) {\r\n return null;\r\n }\r\n\r\n // Extract rotation by removing scale\r\n const m11 = e[0] / sx;\r\n const m12 = e[1] / sx;\r\n const m13 = e[2] / sx;\r\n const m21 = e[4] / sy;\r\n const m22 = e[5] / sy;\r\n const m23 = e[6] / sy;\r\n const m31 = e[8] / sz;\r\n const m32 = e[9] / sz;\r\n const m33 = e[10] / sz;\r\n\r\n // Convert rotation matrix to quaternion\r\n const trace = m11 + m22 + m33;\r\n let rotation: Quat;\r\n\r\n if (trace > 0) {\r\n const s = 0.5 / Math.sqrt(trace + 1.0);\r\n rotation = new Quat(\r\n (m23 - m32) * s,\r\n (m31 - m13) * s,\r\n (m12 - m21) * s,\r\n 0.25 / s,\r\n );\r\n } else if (m11 > m22 && m11 > m33) {\r\n const s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33);\r\n rotation = new Quat(\r\n 0.25 * s,\r\n (m21 + m12) / s,\r\n (m31 + m13) / s,\r\n (m23 - m32) / s,\r\n );\r\n } else if (m22 > m33) {\r\n const s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33);\r\n rotation = new Quat(\r\n (m21 + m12) / s,\r\n 0.25 * s,\r\n (m32 + m23) / s,\r\n (m31 - m13) / s,\r\n );\r\n } else {\r\n const s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22);\r\n rotation = new Quat(\r\n (m31 + m13) / s,\r\n (m32 + m23) / s,\r\n 0.25 * s,\r\n (m12 - m21) / s,\r\n );\r\n }\r\n\r\n return { position, rotation, scale };\r\n }\r\n\r\n // Transformation\r\n transformPoint(v: Vec3): Vec3 {\r\n const e = this.elements;\r\n const x = v.x;\r\n const y = v.y;\r\n const z = v.z;\r\n const w = e[3] * x + e[7] * y + e[11] * z + e[15];\r\n\r\n return new Vec3(\r\n (e[0] * x + e[4] * y + e[8] * z + e[12]) / w,\r\n (e[1] * x + e[5] * y + e[9] * z + e[13]) / w,\r\n (e[2] * x + e[6] * y + e[10] * z + e[14]) / w,\r\n );\r\n }\r\n\r\n transformDirection(v: Vec3): Vec3 {\r\n const e = this.elements;\r\n return new Vec3(\r\n e[0] * v.x + e[4] * v.y + e[8] * v.z,\r\n e[1] * v.x + e[5] * v.y + e[9] * v.z,\r\n e[2] * v.x + e[6] * v.y + e[10] * v.z,\r\n );\r\n }\r\n\r\n /**\r\n * Transform a vector (direction) by this matrix (ignores translation)\r\n * Alias for transformDirection for compatibility\r\n */\r\n transformVector(v: Vec3): Vec3 {\r\n return this.transformDirection(v);\r\n }\r\n\r\n /**\r\n * Returns the inverse of this matrix\r\n * Returns identity matrix if singular (non-invertible)\r\n */\r\n invert(): Mat4 {\r\n const result = this.inverse();\r\n if (result === null) {\r\n return Mat4.identity();\r\n }\r\n return result;\r\n }\r\n\r\n // Utility\r\n clone(): Mat4 {\r\n const m = new Mat4();\r\n m.elements.set(this.elements);\r\n return m;\r\n }\r\n}\r\n","import { Vec3 } from \"../math/Vec3\";\r\nimport { Ray } from \"../math/Ray\";\r\nimport { Mat4 } from \"../math/Mat4\";\r\nimport { Quat } from \"../math/Quat\";\r\n\r\n/**\r\n * GizmoAxis - 轴类型枚举\r\n */\r\nexport type GizmoAxisType = 'x' | 'y' | 'z' | 'xy' | 'xz' | 'yz' | 'xyz';\r\n\r\n/**\r\n * Vec4Color - 带透明度的颜色\r\n */\r\nexport interface Vec4Color {\r\n r: number;\r\n g: number;\r\n b: number;\r\n a: number;\r\n}\r\n\r\n/**\r\n * ShapeConfig - Shape 配置\r\n */\r\nexport interface ShapeConfig {\r\n axis: GizmoAxisType;\r\n defaultColor: Vec3;\r\n hoverColor: Vec3;\r\n disabledColor: Vec3;\r\n defaultAlpha?: number; // 默认透明度\r\n hoverAlpha?: number; // hover 透明度\r\n rotation?: Vec3; // 欧拉角旋转\r\n}\r\n\r\n/**\r\n * TriData - 三角形碰撞数据\r\n */\r\nexport interface TriData {\r\n vertices: Float32Array; // 三角形顶点\r\n indices: Uint16Array; // 索引\r\n transform: Mat4; // 局部变换矩阵\r\n priority: number; // 碰撞优先级(越高越优先)\r\n}\r\n\r\n/**\r\n * Shape - Gizmo 形状基类\r\n * 参考 PlayCanvas 引擎的 Shape 实现\r\n */\r\nexport abstract class Shape {\r\n axis: GizmoAxisType;\r\n \r\n // 状态\r\n protected _disabled: boolean = false;\r\n protected _visible: boolean = true;\r\n protected _hovered: boolean = false;\r\n protected _interactable: boolean = true; // 是否可交互(碰撞检测)\r\n \r\n // 颜色\r\n protected _defaultColor: Vec3;\r\n protected _hoverColor: Vec3;\r\n protected _disabledColor: Vec3;\r\n protected _defaultAlpha: number = 1.0;\r\n protected _hoverAlpha: number = 1.0;\r\n \r\n // 变换\r\n protected _position: Vec3 = new Vec3(0, 0, 0);\r\n protected _rotation: Vec3 = new Vec3(0, 0, 0); // 欧拉角\r\n protected _scale: Vec3 = new Vec3(1, 1, 1);\r\n \r\n // 碰撞数据\r\n triData: TriData[] = [];\r\n \r\n // GPU 资源\r\n protected device: GPUDevice | null = null;\r\n protected vertexBuffer: GPUBuffer | null = null;\r\n protected indexBuffer: GPUBuffer | null = null;\r\n protected vertexCount: number = 0;\r\n protected indexCount: number = 0;\r\n \r\n // 翻转状态(用于平面)\r\n flipped: Vec3 = new Vec3(0, 0, 0);\r\n \r\n constructor(config: ShapeConfig) {\r\n this.axis = config.axis;\r\n this._defaultColor = config.defaultColor.clone();\r\n this._hoverColor = config.hoverColor.clone();\r\n this._disabledColor = config.disabledColor.clone();\r\n this._defaultAlpha = config.defaultAlpha ?? 1.0;\r\n this._hoverAlpha = config.hoverAlpha ?? 1.0;\r\n \r\n if (config.rotation) {\r\n this._rotation = config.rotation.clone();\r\n }\r\n }\r\n \r\n get disabled(): boolean {\r\n return this._disabled;\r\n }\r\n \r\n set disabled(value: boolean) {\r\n this._disabled = value;\r\n if (value) {\r\n this._hovered = false;\r\n }\r\n }\r\n \r\n get visible(): boolean {\r\n return this._visible;\r\n }\r\n \r\n set visible(value: boolean) {\r\n this._visible = value;\r\n // 默认情况下,visible 也控制 interactable\r\n // 但可以单独设置 interactable 来覆盖\r\n }\r\n \r\n get interactable(): boolean {\r\n return this._interactable;\r\n }\r\n \r\n set interactable(value: boolean) {\r\n this._interactable = value;\r\n }\r\n \r\n /**\r\n * 设置 hover 状态\r\n */\r\n hover(state: boolean): void {\r\n if (this._disabled) {\r\n this._hovered = false;\r\n return;\r\n }\r\n this._hovered = state;\r\n }\r\n \r\n /**\r\n * 获取当前颜色(包含透明度)\r\n */\r\n getColor(): Vec4Color {\r\n if (this._disabled) {\r\n return { r: this._disabledColor.x, g: this._disabledColor.y, b: this._disabledColor.z, a: 1.0 };\r\n }\r\n if (this._hovered) {\r\n return { r: this._hoverColor.x, g: this._hoverColor.y, b: this._hoverColor.z, a: this._hoverAlpha };\r\n }\r\n return { r: this._defaultColor.x, g: this._defaultColor.y, b: this._defaultColor.z, a: this._defaultAlpha };\r\n }\r\n \r\n /**\r\n * 获取局部变换矩阵\r\n */\r\n getLocalTransform(): Mat4 {\r\n // 将欧拉角(度)转换为弧度\r\n const rotX = this._rotation.x * Math.PI / 180;\r\n const rotY = this._rotation.y * Math.PI / 180;\r\n const rotZ = this._rotation.z * Math.PI / 180;\r\n \r\n const rotation = Quat.fromEuler(rotX, rotY, rotZ);\r\n \r\n // Debug: 输出旋转信息\r\n // console.log(`Shape ${this.axis} rotation (deg):`, this._rotation);\r\n // console.log(`Shape ${this.axis} rotation (rad):`, rotX, rotY, rotZ);\r\n // console.log(`Shape ${this.axis} quaternion:`, rotation);\r\n \r\n return Mat4.compose(this._position, rotation, this._scale);\r\n }\r\n \r\n /**\r\n * 射线碰撞检测\r\n * @param ray - 世界空间射线\r\n * @param parentTransform - 父级变换矩阵(gizmo 的世界变换)\r\n * @returns 碰撞距离,null 表示未碰撞\r\n */\r\n intersect(ray: Ray, parentTransform: Mat4): number | null {\r\n // disabled 或 不可交互时跳过碰撞检测\r\n if (this._disabled || !this._interactable) {\r\n return null;\r\n }\r\n \r\n let closestDist: number | null = null;\r\n let highestPriority = -Infinity;\r\n \r\n for (const tri of this.triData) {\r\n // 计算完整变换:parent * local * triTransform\r\n const localTransform = this.getLocalTransform();\r\n const worldTransform = parentTransform.multiply(localTransform).multiply(tri.transform);\r\n const invTransform = worldTransform.invert();\r\n \r\n // 将射线变换到局部空间\r\n const localRay = ray.transform(invTransform);\r\n \r\n // 与三角形进行碰撞检测\r\n const dist = this.intersectTriangles(localRay, tri.vertices, tri.indices);\r\n \r\n if (dist !== null) {\r\n // 考虑优先级\r\n if (tri.priority > highestPriority || \r\n (tri.priority === highestPriority && (closestDist === null || dist < closestDist))) {\r\n closestDist = dist;\r\n highestPriority = tri.priority;\r\n }\r\n }\r\n }\r\n \r\n return closestDist;\r\n }\r\n \r\n /**\r\n * 射线与三角形列表碰撞检测\r\n */\r\n protected intersectTriangles(\r\n ray: Ray, \r\n vertices: Float32Array, \r\n indices: Uint16Array\r\n ): number | null {\r\n let closestDist: number | null = null;\r\n \r\n for (let i = 0; i < indices.length; i += 3) {\r\n const i0 = indices[i] * 3;\r\n const i1 = indices[i + 1] * 3;\r\n const i2 = indices[i + 2] * 3;\r\n \r\n const v0 = new Vec3(vertices[i0], vertices[i0 + 1], vertices[i0 + 2]);\r\n const v1 = new Vec3(vertices[i1], vertices[i1 + 1], vertices[i1 + 2]);\r\n const v2 = new Vec3(vertices[i2], vertices[i2 + 1], vertices[i2 + 2]);\r\n \r\n const dist = ray.intersectTriangle(v0, v1, v2);\r\n \r\n if (dist !== null && dist > 0) {\r\n if (closestDist === null || dist < closestDist) {\r\n closestDist = dist;\r\n }\r\n }\r\n }\r\n \r\n return closestDist;\r\n }\r\n \r\n /**\r\n * 创建 GPU 资源\r\n */\r\n abstract createGeometry(device: GPUDevice): void;\r\n \r\n /**\r\n * 获取顶点缓冲区\r\n */\r\n getVertexBuffer(): GPUBuffer | null {\r\n return this.vertexBuffer;\r\n }\r\n \r\n /**\r\n * 获取索引缓冲区\r\n */\r\n getIndexBuffer(): GPUBuffer | null {\r\n return this.indexBuffer;\r\n }\r\n \r\n /**\r\n * 获取索引数量\r\n */\r\n getIndexCount(): number {\r\n return this.indexCount;\r\n }\r\n \r\n /**\r\n * 销毁 GPU 资源\r\n */\r\n destroy(): void {\r\n if (this.vertexBuffer) {\r\n this.vertexBuffer.destroy();\r\n this.vertexBuffer = null;\r\n }\r\n if (this.indexBuffer) {\r\n this.indexBuffer.destroy();\r\n this.indexBuffer = null;\r\n }\r\n }\r\n \r\n /**\r\n * 创建 GPU 缓冲区\r\n */\r\n protected createBuffers(\r\n device: GPUDevice,\r\n vertices: Float32Array,\r\n indices: Uint16Array\r\n ): void {\r\n this.device = device;\r\n this.vertexCount = vertices.length / 6;\r\n this.indexCount = indices.length;\r\n \r\n // 创建顶点缓冲区\r\n this.vertexBuffer = device.createBuffer({\r\n size: vertices.byteLength,\r\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\r\n mappedAtCreation: true,\r\n });\r\n new Float32Array(this.vertexBuffer.getMappedRange()).set(vertices);\r\n this.vertexBuffer.unmap();\r\n \r\n // 创建索引缓冲区\r\n this.indexBuffer = device.createBuffer({\r\n size: indices.byteLength,\r\n usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,\r\n mappedAtCreation: true,\r\n });\r\n new Uint16Array(this.indexBuffer.getMappedRange()).set(indices);\r\n this.indexBuffer.unmap();\r\n }\r\n}\r\n","import { Vec3 } from \"../math/Vec3\";\r\nimport { Mat4 } from \"../math/Mat4\";\r\nimport { Quat } from \"../math/Quat\";\r\nimport { Shape, ShapeConfig, TriData } from \"./Shape\";\r\n\r\n/**\r\n * ArrowShapeConfig - 箭头形状配置\r\n */\r\nexport interface ArrowShapeConfig extends ShapeConfig {\r\n gap?: number; // 箭头起点与中心的间距\r\n lineThickness?: number; // 线条粗细\r\n lineLength?: number; // 线条长度\r\n arrowThickness?: number;// 箭头粗细\r\n arrowLength?: number; // 箭头长度\r\n tolerance?: number; // 碰撞检测容差\r\n}\r\n\r\n/**\r\n * ArrowShape - 箭头形状(用于平移 Gizmo)\r\n * 参考 PlayCanvas 引擎的 ArrowShape 实现\r\n */\r\nexport class ArrowShape extends Shape {\r\n private _gap: number = 0;\r\n private _lineThickness: number = 0.02;\r\n private _lineLength: number = 0.5;\r\n private _arrowThickness: number = 0.12;\r\n private _arrowLength: number = 0.18;\r\n private _tolerance: number = 0.1;\r\n \r\n constructor(config: ArrowShapeConfig) {\r\n super(config);\r\n \r\n this._gap = config.gap ?? this._gap;\r\n this._lineThickness = config.lineThickness ?? this._lineThickness;\r\n this._lineLength = config.lineLength ?? this._lineLength;\r\n this._arrowThickness = config.arrowThickness ?? this._arrowThickness;\r\n this._arrowLength = config.arrowLength ?? this._arrowLength;\r\n this._tolerance = config.tolerance ?? this._tolerance;\r\n \r\n this._updateTriData();\r\n }\r\n \r\n get gap(): number { return this._gap; }\r\n set gap(value: number) { this._gap = value; this._updateTriData(); }\r\n \r\n get lineThickness(): number { return this._lineThickness; }\r\n set lineThickness(value: number) { this._lineThickness = value; this._updateTriData(); }\r\n \r\n get lineLength(): number { return this._lineLength; }\r\n set lineLength(value: number) { this._lineLength = value; this._updateTriData(); }\r\n \r\n get arrowThickness(): number { return this._arrowThickness; }\r\n set arrowThickness(value: number) { this._arrowThickness = value; this._updateTriData(); }\r\n \r\n get arrowLength(): number { return this._arrowLength; }\r\n set arrowLength(value: number) { this._arrowLength = value; this._updateTriData(); }\r\n \r\n /**\r\n * 更新碰撞数据\r\n */\r\n private _updateTriData(): void {\r\n // 箭头锥体碰撞数据\r\n const conePos = new Vec3(0, this._gap + this._arrowLength * 0.5 + this._lineLength, 0);\r\n const coneScale = new Vec3(this._arrowThickness, this._arrowLength, this._arrowThickness);\r\n const coneTransform = Mat4.compose(conePos, Quat.identity(), coneScale);\r\n \r\n // 线条圆柱碰撞数据(加上容差)\r\n const linePos = new Vec3(0, this._gap + this._lineLength * 0.5, 0);\r\n const lineScale = new Vec3(\r\n this._lineThickness + this._tolerance,\r\n this._lineLength,\r\n this._lineThickness + this._tolerance\r\n );\r\n const lineTransform = Mat4.compose(linePos, Quat.identity(), lineScale);\r\n \r\n // 生成单位锥体和圆柱的三角形数据\r\n const coneGeo = this.createConeGeometry();\r\n const cylinderGeo = this.createCylinderGeometry();\r\n \r\n this.triData = [\r\n {\r\n vertices: coneGeo.vertices,\r\n indices: coneGeo.indices,\r\n transform: coneTransform,\r\n priority: 0\r\n },\r\n {\r\n vertices: cylinderGeo.vertices,\r\n indices: cylinderGeo.indices,\r\n transform: lineTransform,\r\n priority: 1 // 线条优先级更高,更容易选中\r\n }\r\n ];\r\n }\r\n \r\n /**\r\n * 创建单位锥体几何体(底面半径1,高度1,底面在y=0,顶点在y=1)\r\n */\r\n private createConeGeometry(): { vertices: Float32Array; indices: Uint16Array } {\r\n const segments = 12;\r\n const vertices: number[] = [];\r\n const indices: number[] = [];\r\n \r\n // 底面中心\r\n vertices.push(0, -0.5, 0);\r\n \r\n // 底面顶点\r\n for (let i = 0; i <= segments; i++) {\r\n const angle = (i / segments) * Math.PI * 2;\r\n vertices.push(Math.cos(angle) * 0.5, -0.5, Math.sin(angle) * 0.5);\r\n }\r\n \r\n // 顶点\r\n const tipIndex = vertices.length / 3;\r\n vertices.push(0, 0.5, 0);\r\n \r\n // 底面三角形\r\n for (let i = 1; i <= segments; i++) {\r\n indices.push(0, i + 1, i);\r\n }\r\n \r\n // 侧面三角形\r\n for (let i = 1; i <= segments; i++) {\r\n indices.push(i, i + 1, tipIndex);\r\n }\r\n \r\n return {\r\n vertices: new Float32Array(vertices),\r\n indices: new Uint16Array(indices)\r\n };\r\n }\r\n \r\n /**\r\n * 创建单位圆柱几何体(半径1,高度1,中心在原点)\r\n */\r\n private createCylinderGeometry(): { vertices: Float32Array; indices: Uint16Array } {\r\n const segments = 12;\r\n const vertices: number[] = [];\r\n const indices: number[] = [];\r\n \r\n // 底面和顶面顶点\r\n for (let i = 0; i <= segments; i++) {\r\n const angle = (i / segments) * Math.PI * 2;\r\n const x = Math.cos(angle) * 0.5;\r\n const z = Math.sin(angle) * 0.5;\r\n \r\n // 底面\r\n vertices.push(x, -0.5, z);\r\n // 顶面\r\n vertices.push(x, 0.5, z);\r\n }\r\n \r\n // 侧面三角形\r\n for (let i = 0; i < segments; i++) {\r\n const base = i * 2;\r\n indices.push(base, base + 1, base + 2);\r\n indices.push(base + 1, base + 3, base + 2);\r\n }\r\n \r\n return {\r\n vertices: new Float32Array(vertices),\r\n indices: new Uint16Array(indices)\r\n };\r\n }\r\n \r\n /**\r\n * 创建渲染用的 GPU 几何体\r\n */\r\n createGeometry(device: GPUDevice): void {\r\n const segments = 12;\r\n const vertices: number[] = [];\r\n const indices: number[] = [];\r\n \r\n const color = this._defaultColor;\r\n \r\n // 辅助函数:添加顶点\r\n const addVertex = (x: number, y: number, z: number) => {\r\n vertices.push(x, y, z, color.x, color.y, color.z);\r\n };\r\n \r\n // 计算垂直向量\r\n let perpX: Vec3, perpY: Vec3;\r\n const dir = new Vec3(0, 1, 0); // 默认沿 Y 轴\r\n perpX = new Vec3(1, 0, 0);\r\n perpY = new Vec3(0, 0, 1);\r\n \r\n // 生成圆柱(线条部分)\r\n const cylinderStart = this._gap;\r\n const cylinderEnd = this._gap + this._lineLength;\r\n \r\n for (let i = 0; i <= segments; i++) {\r\n const angle = (i / segments) * Math.PI * 2;\r\n const cos = Math.cos(angle);\r\n const sin = Math.sin(angle);\r\n \r\n const offsetX = cos * this._lineThickness;\r\n const offsetZ = sin * this._lineThickness;\r\n \r\n // 底部顶点\r\n addVertex(offsetX, cylinderStart, offsetZ);\r\n // 顶部顶点\r\n addVertex(offsetX, cylinderEnd, offsetZ);\r\n }\r\n \r\n // 圆柱索引\r\n for (let i = 0; i < segments; i++) {\r\n const base = i * 2;\r\n indices.push(base, base + 1, base + 2);\r\n indices.push(base + 1, base + 3, base + 2);\r\n }\r\n \r\n // 生成锥体(箭头部分)\r\n const coneBase = this._gap + this._lineLength;\r\n const coneTip = coneBase + this._arrowLength;\r\n \r\n const coneBaseIndex = vertices.length / 6;\r\n \r\n // 锥体底面顶点\r\n for (let i = 0; i <= segments; i++) {\r\n const angle = (i / segments) * Math.PI * 2;\r\n const cos = Math.cos(angle);\r\n const sin = Math.sin(angle);\r\n \r\n const offsetX = cos * this._arrowThickness;\r\n const offsetZ = sin * this._arrowThickness;\r\n \r\n addVertex(offsetX, coneBase, offsetZ);\r\n }\r\n \r\n // 锥体顶点\r\n const coneTipIndex = vertices.length / 6;\r\n addVertex(0, coneTip, 0);\r\n \r\n // 锥体索引\r\n for (let i = 0; i < segments; i++) {\r\n indices.push(coneBaseIndex + i, coneTipIndex, coneBaseIndex + i + 1);\r\n }\r\n \r\n this.createBuffers(device, new Float32Array(vertices), new Uint16Array(indices));\r\n }\r\n}\r\n","import { Vec3 } from \"../math/Vec3\";\r\nimport { Mat4 } from \"../math/Mat4\";\r\nimport { Quat } from \"../math/Quat\";\r\nimport { Shape, ShapeConfig } from \"./Shape\";\r\n\r\n/**\r\n * PlaneShapeConfig - 平面形状配置\r\n */\r\nexport interface PlaneShapeConfig extends ShapeConfig {\r\n size?: number; // 平面大小\r\n gap?: number; // 与中心的间距\r\n}\r\n\r\n/**\r\n * PlaneShape - 平面形状(用于双轴平移)\r\n * 参考 PlayCanvas 引擎的 PlaneShape 实现\r\n */\r\nexport class PlaneShape extends Shape {\r\n private _size: number = 0.2;\r\n private _gap: number = 0.1;\r\n \r\n // 缓存不同翻转状态的几何数据\r\n private _geometryCache: Map<string, { vertexBuffer: GPUBuffer; indexBuffer: GPUBuffer }> = new Map();\r\n private _currentFlipKey: string = '0,0,0';\r\n \r\n constructor(config: PlaneShapeConfig) {\r\n super(config);\r\n \r\n this._size = config.size ?? this._size;\r\n this._gap = config.gap ?? this._gap;\r\n \r\n this._updateTriData();\r\n }\r\n \r\n get size(): number { return this._size; }\r\n set size(value: number) { \r\n this._size = value; \r\n this._updateTriData(); \r\n this._clearGeometryCache();\r\n }\r\n \r\n get gap(): number { return this._gap; }\r\n set gap(value: number) { \r\n this._gap = value; \r\n this._updateTriData(); \r\n this._clearGeometryCache();\r\n }\r\n \r\n /**\r\n * 更新碰撞数据\r\n */\r\n private _updateTriData(): void {\r\n const planeGeo = this.createPlaneGeometry();\r\n \r\n // 平面位置(考虑翻转)\r\n const pos = new Vec3(\r\n (this._gap + this._size * 0.5) * (1 - this.flipped.x * 2),\r\n 0,\r\n (this._gap + this._size * 0.5) * (1 - this.flipped.z * 2)\r\n );\r\n \r\n const scale = new Vec3(this._size, 1, this._size);\r\n const transform = Mat4.compose(pos, Quat.identity(), scale);\r\n \r\n this.triData = [{\r\n vertices: planeGeo.vertices,\r\n indices: planeGeo.indices,\r\n transform: transform,\r\n priority: 2\r\n }];\r\n }\r\n \r\n /**\r\n * 创建单位平面几何体\r\n */\r\n private createPlaneGeometry(): { vertices: Float32Array; indices: Uint16Array } {\r\n const vertices = new Float32Array([\r\n -0.5, 0, -0.5,\r\n 0.5, 0, -0.5,\r\n 0.5, 0, 0.5,\r\n -0.5, 0, 0.5\r\n ]);\r\n \r\n const indices = new Uint16Array([\r\n 0, 1, 2,\r\n 0, 2, 3,\r\n 0, 2, 1,\r\n 0, 3, 2\r\n ]);\r\n \r\n return { vertices, indices };\r\n }\r\n \r\n /**\r\n * 创建渲染用的 GPU 几何体\r\n */\r\n createGeometry(device: GPUDevice): void {\r\n this.device = device;\r\n this._currentFlipKey = `${this.flipped.x},${this.flipped.y},${this.flipped.z}`;\r\n \r\n // 检查缓存\r\n if (this._geometryCache.has(this._currentFlipKey)) {\r\n const cached = this._geometryCache.get(this._currentFlipKey)!;\r\n this.vertexBuffer = cached.vertexBuffer;\r\n this.indexBuffer = cached.indexBuffer;\r\n this.indexCount = 12;\r\n return;\r\n }\r\n \r\n const color = this._defaultColor;\r\n \r\n // 计算平面位置(考虑翻转)\r\n const offsetX = (this._gap + this._size * 0.5) * (1 - this.flipped.x * 2);\r\n const offsetZ = (this._gap + this._size * 0.5) * (1 - this.flipped.z * 2);\r\n const halfSize = this._size * 0.5;\r\n \r\n const vertices = new Float32Array([\r\n offsetX - halfSize, 0, offsetZ - halfSize, color.x, color.y, color.z,\r\n offsetX + halfSize, 0, offsetZ - halfSize, color.x, color.y, color.z,\r\n offsetX + halfSize, 0, offsetZ + halfSize, color.x, color.y, color.z,\r\n offsetX - halfSize, 0, offsetZ + halfSize, color.x, color.y, color.z,\r\n ]);\r\n \r\n const indices = new Uint16Array([\r\n 0, 1, 2,\r\n 0, 2, 3,\r\n 0, 2, 1,\r\n 0, 3, 2\r\n ]);\r\n \r\n this.createBuffers(device, vertices, indices);\r\n \r\n // 缓存\r\n if (this.vertexBuffer && this.indexBuffer) {\r\n this._geometryCache.set(this._currentFlipKey, {\r\n vertexBuffer: this.vertexBuffer,\r\n indexBuffer: this.indexBuffer\r\n });\r\n }\r\n }\r\n \r\n /**\r\n * 清除几何缓存\r\n */\r\n private _clearGeometryCache(): void {\r\n for (const cached of this._geometryCache.values()) {\r\n cached.vertexBuffer.destroy();\r\n cached.indexBuffer.destroy();\r\n }\r\n this._geometryCache.clear();\r\n this.vertexBuffer = null;\r\n this.indexBuffer = null;\r\n }\r\n \r\n /**\r\n * 更新翻转状态\r\n */\r\n setFlipped(newFlipped: Vec3): void {\r\n const newKey = `${newFlipped.x},${newFlipped.y},${newFlipped.z}`;\r\n if (this._currentFlipKey === newKey) return;\r\n \r\n this.flipped = newFlipped.clone();\r\n this._updateTriData();\r\n \r\n if (this.device) {\r\n this._currentFlipKey = newKey;\r\n \r\n // 检查缓存\r\n if (this._geometryCache.has(newKey)) {\r\n const cached = this._geometryCache.get(newKey)!;\r\n this.vertexBuffer = cached.vertexBuffer;\r\n this.indexBuffer = cached.indexBuffer;\r\n } else {\r\n // 创建新的几何体\r\n this.createGeometry(this.device);\r\n }\r\n }\r\n }\r\n \r\n /**\r\n * 销毁 GPU 资源\r\n */\r\n override destroy(): void {\r\n this._clearGeometryCache();\r\n super.destroy();\r\n }\r\n}\r\n","import { Vec3 } from \"../math/Vec3\";\r\nimport { Mat4 } from \"../math/Mat4\";\r\nimport { Quat } from \"../math/Quat\";\r\nimport { Shape, ShapeConfig, TriData } from \"./Shape\";\r\n\r\n/**\r\n * SphereShapeConfig - 球体形状配置\r\n */\r\nexport interface SphereShapeConfig extends ShapeConfig {\r\n radius?: number; // 球体半径\r\n}\r\n\r\n/**\r\n * SphereShape - 球体形状(用于统一缩放/中心选择)\r\n * 参考 PlayCanvas 引擎的 SphereShape 实现\r\n */\r\nexport class SphereShape extends Shape {\r\n private _radius: number = 0.1;\r\n \r\n constructor(config: SphereShapeConfig) {\r\n super(config);\r\n \r\n this._radius = config.radius ?? this._radius;\r\n \r\n this._updateTriData();\r\n }\r\n \r\n get radius(): number { return this._radius; }\r\n set radius(value: number) { this._radius = value; this._updateTriData(); }\r\n \r\n /**\r\n * 更新碰撞数据\r\n */\r\n private _updateTriData(): void {\r\n const sphereGeo = this.createSphereGeometry(8, 6);\r\n \r\n const scale = new Vec3(this._radius * 2, this._radius * 2, this._radius * 2);\r\n const transform = Mat4.compose(new Vec3(0, 0, 0), Quat.identity(), scale);\r\n \r\n this.triData = [{\r\n vertices: sphereGeo.vertices,\r\n indices: sphereGeo.indices,\r\n transform: transform,\r\n priority: 3 // 中心球优先级最高\r\n }];\r\n }\r\n \r\n /**\r\n * 创建单位球体几何体(半径0.5,中心在原点)\r\n */\r\n private createSphereGeometry(\r\n segments: number, \r\n rings: number\r\n ): { vertices: Float32Array; indices: Uint16Array } {\r\n const vertices: number[] = [];\r\n const indices: number[] = [];\r\n \r\n // 生成球体顶点\r\n for (let ring = 0; ring <= rings; ring++) {\r\n const phi = (ring / rings) * Math.PI;\r\n const sinPhi = Math.sin(phi);\r\n const cosPhi = Math.cos(phi);\r\n \r\n for (let seg = 0; seg <= segments; seg++) {\r\n const theta = (seg / segments) * Math.PI * 2;\r\n const sinTheta = Math.sin(theta);\r\n const cosTheta = Math.cos(theta);\r\n \r\n const x = 0.5 * sinPhi * cosTheta;\r\n const y = 0.5 * cosPhi;\r\n const z = 0.5 * sinPhi * sinTheta;\r\n \r\n vertices.push(x, y, z);\r\n }\r\n }\r\n \r\n // 生成球体索引\r\n for (let ring = 0; ring < rings; ring++) {\r\n for (let seg = 0; seg < segments; seg++) {\r\n const a = ring * (segments + 1) + seg;\r\n const b = a + segments + 1;\r\n const c = a + 1;\r\n const d = b + 1;\r\n \r\n indices.push(a, b, c);\r\n indices.push(b, d, c);\r\n }\r\n }\r\n \r\n return {\r\n vertices: new Float32Array(vertices),\r\n indices: new Uint16Array(indices)\r\n };\r\n }\r\n \r\n /**\r\n * 创建渲染用的 GPU 几何体\r\n */\r\n createGeometry(device: GPUDevice): void {\r\n const segments = 16;\r\n const rings = 12;\r\n const vertices: number[] = [];\r\n const indices: number[] = [];\r\n \r\n const color = this._defaultColor;\r\n \r\n // 生成球体顶点\r\n for (let ring = 0; ring <= rings; ring++) {\r\n const phi = (ring / rings) * Math.PI;\r\n const sinPhi = Math.sin(phi);\r\n const cosPhi = Math.cos(phi);\r\n \r\n for (let seg = 0; seg <= segments; seg++) {\r\n const theta = (seg / segments) * Math.PI * 2;\r\n const sinTheta = Math.sin(theta);\r\n const cosTheta = Math.cos(theta);\r\n \r\n const x = this._radius * sinPhi * cosTheta;\r\n const y = this._radius * cosPhi;\r\n const z = this._radius * sinPhi * sinTheta;\r\n \r\n vertices.push(x, y, z, color.x, color.y, color.z);\r\n }\r\n }\r\n \r\n // 生成球体索引\r\n for (let ring = 0; ring < rings; ring++) {\r\n for (let seg = 0; seg < segments; seg++) {\r\n const a = ring * (segments + 1) + seg;\r\n const b = a + segments + 1;\r\n const c = a + 1;\r\n const d = b + 1;\r\n \r\n indices.push(a, b, c);\r\n indices.push(b, d, c);\r\n }\r\n }\r\n \r\n this.createBuffers(device, new Float32Array(vertices), new Uint16Array(indices));\r\n }\r\n}\r\n","import { Vec3 } from \"../math/Vec3\";\r\nimport { Mat4 } from \"../math/Mat4\";\r\nimport { Quat } from \"../math/Quat\";\r\nimport { Shape, ShapeConfig } from \"./Shape\";\r\n\r\n/**\r\n * ArcShapeConfig - 圆弧形状配置\r\n */\r\nexport interface ArcShapeConfig extends ShapeConfig {\r\n tubeRadius?: number; // 管道半径(粗细)\r\n ringRadius?: number; // 圆环半径\r\n sectorAngle?: number; // 扇形角度(度)\r\n tolerance?: number; // 碰撞检测容差\r\n}\r\n\r\n/**\r\n * ArcDisplayMode - 圆弧显示模式\r\n */\r\nexport type ArcDisplayMode = 'sector' | 'ring' | 'none';\r\n\r\n/**\r\n * ArcShape - 圆弧形状(用于旋转 Gizmo)\r\n * 参考 PlayCanvas 引擎的 ArcShape 实现\r\n * \r\n * 圆环默认在 XZ 平面上(绕 Y 轴),通过 rotation 旋转到正确的轴\r\n */\r\nexport class ArcShape extends Shape {\r\n private _tubeRadius: number = 0.02;\r\n private _ringRadius: number = 0.5;\r\n private _sectorAngle: number = 180; // 默认半圆弧\r\n private _tolerance: number = 0.05;\r\n \r\n // 显示模式\r\n private _displayMode: ArcDisplayMode = 'sector';\r\n \r\n // 双缓冲:sector 和 ring 的几何数据\r\n private _sectorVertexBuffer: GPUBuffer | null = null;\r\n private _sectorIndexBuffer: GPUBuffer | null = null;\r\n private _sectorIndexCount: number = 0;\r\n private _ringVertexBuffer: GPUBuffer | null = null;\r\n private _ringIndexBuffer: GPUBuffer | null = null;\r\n private _ringIndexCount: number = 0;\r\n \r\n // 动态旋转角度(用于面向相机)\r\n private _dynamicRotation: Vec3 = new Vec3(0, 0, 0);\r\n \r\n constructor(config: ArcShapeConfig) {\r\n super(config);\r\n \r\n this._tubeRadius = config.tubeRadius ?? this._tubeRadius;\r\n this._ringRadius = config.ringRadius ?? this._ringRadius;\r\n this._sectorAngle = config.sectorAngle ?? this._sectorAngle;\r\n this._tolerance = config.tolerance ?? this._tolerance;\r\n \r\n this._updateTriData();\r\n }\r\n \r\n get tubeRadius(): number { return this._tubeRadius; }\r\n set tubeRadius(value: number) { this._tubeRadius = value; this._updateTriData(); }\r\n \r\n get ringRadius(): number { return this._ringRadius; }\r\n set ringRadius(value: number) { this._ringRadius = value; this._updateTriData(); }\r\n \r\n get sectorAngle(): number { return this._sectorAngle; }\r\n set sectorAngle(value: number) { this._sectorAngle = value; this._updateTriData(); }\r\n \r\n get tolerance(): number { return this._tolerance; }\r\n set tolerance(value: number) { this._tolerance = value; this._updateTriData(); }\r\n \r\n get displayMode(): ArcDisplayMode { return this._displayMode; }\r\n \r\n /**\r\n * 设置动态旋转(用于面向相机)\r\n */\r\n setDynamicRotation(rotation: Vec3): void {\r\n this._dynamicRotation = rotation.clone();\r\n }\r\n \r\n /**\r\n * 获取局部变换矩阵(包含动态旋转)\r\n */\r\n override getLocalTransform(): Mat4 {\r\n // 基础旋转\r\n const baseRotX = this._rotation.x * Math.PI / 180;\r\n const baseRotY = this._rotation.y * Math.PI / 180;\r\n const baseRotZ = this._rotation.z * Math.PI / 180;\r\n const baseQuat = Quat.fromEuler(baseRotX, baseRotY, baseRotZ);\r\n \r\n // 动态旋转\r\n const dynRotX = this._dynamicRotation.x * Math.PI / 180;\r\n const dynRotY = this._dynamicRotation.y * Math.PI / 180;\r\n const dynRotZ = this._dynamicRotation.z * Math.PI / 180;\r\n const dynQuat = Quat.fromEuler(dynRotX, dynRotY, dynRotZ);\r\n \r\n // 组合旋转:先应用基础旋转,再应用动态旋转\r\n const finalQuat = baseQuat.multiply(dynQuat);\r\n \r\n return Mat4.compose(this._position, finalQuat, this._scale);\r\n }\r\n \r\n /**\r\n * 更新碰撞数据\r\n */\r\n private _updateTriData(): void {\r\n // 根据显示模式创建碰撞几何体\r\n const angle = this._displayMode === 'ring' ? 360 : this._sectorAngle;\r\n const torusGeo = this.createTorusGeometry(20, 8, this._tubeRadius + this._tolerance, angle);\r\n \r\n const transform = Mat4.identity();\r\n \r\n this.triData = [{\r\n vertices: torusGeo.vertices,\r\n indices: torusGeo.indices,\r\n transform: transform,\r\n priority: 0\r\n }];\r\n }\r\n \r\n /**\r\n * 切换显示模式\r\n */\r\n show(mode: ArcDisplayMode): void {\r\n if (this._displayMode === mode) return;\r\n \r\n this._displayMode = mode;\r\n this._visible = mode !== 'none';\r\n \r\n // 更新碰撞数据\r\n this._updateTriData();\r\n }\r\n \r\n /**\r\n * 创建圆环几何体\r\n */\r\n private createTorusGeometry(\r\n segments: number,\r\n tubeSegments: number,\r\n tubeRadius?: number,\r\n sectorAngle?: number\r\n ): { vertices: Float32Array; indices: Uint16Array } {\r\n const vertices: number[] = [];\r\n const indices: number[] = [];\r\n \r\n const tube = tubeRadius ?? this._tubeRadius;\r\n const ring = this._ringRadius;\r\n const angle = sectorAngle ?? this._sectorAngle;\r\n const sectorRad = (angle * Math.PI) / 180;\r\n \r\n for (let i = 0; i <= segments; i++) {\r\n const u = (i / segments) * sectorRad;\r\n const cosU = Math.cos(u);\r\n const sinU = Math.sin(u);\r\n \r\n for (let j = 0; j <= tubeSegments; j++) {\r\n const v = (j / tubeSegments) * Math.PI * 2;\r\n const cosV = Math.cos(v);\r\n const sinV = Math.sin(v);\r\n \r\n const x = (ring + tube * cosV) * cosU;\r\n const y = tube * sinV;\r\n const z = (ring + tube * cosV) * sinU;\r\n \r\n vertices.push(x, y, z);\r\n }\r\n }\r\n \r\n for (let i = 0; i < segments; i++) {\r\n for (let j = 0; j < tubeSegments; j++) {\r\n const a = i * (tubeSegments + 1) + j;\r\n const b = a + tubeSegments + 1;\r\n const c = a + 1;\r\n const d = b + 1;\r\n \r\n indices.push(a, b, c);\r\n indices.push(b, d, c);\r\n }\r\n }\r\n \r\n return {\r\n vertices: new Float32Array(vertices),\r\n indices: new Uint16Array(indices)\r\n };\r\n }\r\n \r\n /**\r\n * 创建渲染用的 GPU 几何体(同时创建 sector 和 ring 两种)\r\n */\r\n createGeometry(device: GPUDevice): void {\r\n this.device = device;\r\n \r\n // 创建 sector 几何体\r\n const sectorData = this._createTorusMeshData(this._sectorAngle);\r\n this._sectorVertexBuffer = device.createBuffer({\r\n size: sectorData.vertices.byteLength,\r\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\r\n mappedAtCreation: true,\r\n });\r\n new Float32Array(this._sectorVertexBuffer.getMappedRange()).set(sectorData.vertices);\r\n this._sectorVertexBuffer.unmap();\r\n \r\n this._sectorIndexBuffer = device.createBuffer({\r\n size: sectorData.indices.byteLength,\r\n usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,\r\n mappedAtCreation: true,\r\n });\r\n new Uint16Array(this._sectorIndexBuffer.getMappedRange()).set(sectorData.indices);\r\n this._sectorIndexBuffer.unmap();\r\n this._sectorIndexCount = sectorData.indices.length;\r\n \r\n // 创建 ring 几何体(完整圆环)\r\n const ringData = this._createTorusMeshData(360);\r\n this._ringVertexBuffer = device.createBuffer({\r\n size: ringData.vertices.byteLength,\r\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\r\n mappedAtCreation: true,\r\n });\r\n new Float32Array(this._ringVertexBuffer.getMappedRange()).set(ringData.vertices);\r\n this._ringVertexBuffer.unmap();\r\n \r\n this._ringIndexBuffer = device.createBuffer({\r\n size: ringData.indices.byteLength,\r\n usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,\r\n mappedAtCreation: true,\r\n });\r\n new Uint16Array(this._ringIndexBuffer.getMappedRange()).set(ringData.indices);\r\n this._ringIndexBuffer.unmap();\r\n this._ringIndexCount = ringData.indices.length;\r\n \r\n // 默认使用 sector\r\n this.vertexBuffer = this._sectorVertexBuffer;\r\n this.indexBuffer = this._sectorIndexBuffer;\r\n this.indexCount = this._sectorIndexCount;\r\n }\r\n \r\n /**\r\n * 创建圆环网格数据\r\n */\r\n private _createTorusMeshData(sectorAngle: number): { vertices: Float32Array; indices: Uint16Array } {\r\n const segments = 64;\r\n const tubeSegments = 12;\r\n const vertices: number[] = [];\r\n const indices: number[] = [];\r\n \r\n const color = this._defaultColor;\r\n const tube = this._tubeRadius;\r\n const ring = this._ringRadius;\r\n const sectorRad = (sectorAngle * Math.PI) / 180;\r\n \r\n for (let i = 0; i <= segments; i++) {\r\n const u = (i / segments) * sectorRad;\r\n const cosU = Math.cos(u);\r\n const sinU = Math.sin(u);\r\n \r\n for (let j = 0; j <= tubeSegments; j++) {\r\n const v = (j / tubeSegments) * Math.PI * 2;\r\n const cosV = Math.cos(v);\r\n const sinV = Math.sin(v);\r\n \r\n const x = (ring + tube * cosV) * cosU;\r\n const y = tube * sinV;\r\n const z = (ring + tube * cosV) * sinU;\r\n \r\n vertices.push(x, y, z, color.x, color.y, color.z);\r\n }\r\n }\r\n \r\n for (let i = 0; i < segments; i++) {\r\n for (let j = 0; j < tubeSegments; j++) {\r\n const a = i * (tubeSegments + 1) + j;\r\n const b = a + tubeSegments + 1;\r\n const c = a + 1;\r\n const d = b + 1;\r\n \r\n indices.push(a, b, c);\r\n indices.push(b, d, c);\r\n }\r\n }\r\n \r\n return {\r\n vertices: new Float32Array(vertices),\r\n indices: new Uint16Array(indices)\r\n };\r\n }\r\n \r\n /**\r\n * 获取顶点缓冲区(根据显示模式)\r\n */\r\n override getVertexBuffer(): GPUBuffer | null {\r\n if (this._displayMode === 'ring') {\r\n return this._ringVertexBuffer;\r\n }\r\n return this._sectorVertexBuffer;\r\n }\r\n \r\n /**\r\n * 获取索引缓冲区(根据显示模式)\r\n */\r\n override getIndexBuffer(): GPUBuffer | null {\r\n if (this._displayMode === 'ring') {\r\n return this._ringIndexBuffer;\r\n }\r\n return this._sectorIndexBuffer;\r\n }\r\n \r\n /**\r\n * 获取索引数量(根据显示模式)\r\n */\r\n override getIndexCount(): number {\r\n if (this._displayMode === 'ring') {\r\n return this._ringIndexCount;\r\n }\r\n return this._sectorIndexCount;\r\n }\r\n \r\n /**\r\n * 销毁 GPU 资源\r\n */\r\n override destroy(): void {\r\n if (this._sectorVertexBuffer) {\r\n this._sectorVertexBuffer.destroy();\r\n this._sectorVertexBuffer = null;\r\n }\r\n if (this._sectorIndexBuffer) {\r\n this._sectorIndexBuffer.destroy();\r\n this._sectorIndexBuffer = null;\r\n }\r\n if (this._ringVertexBuffer) {\r\n this._ringVertexBuffer.destroy();\r\n this._ringVertexBuffer = null;\r\n }\r\n if (this._ringIndexBuffer) {\r\n this._ringIndexBuffer.destroy();\r\n this._ringIndexBuffer = null;\r\n }\r\n super.destroy();\r\n }\r\n}\r\n","import { Vec3 } from \"../math/Vec3\";\r\nimport { Mat4 } from \"../math/Mat4\";\r\nimport { Quat } from \"../math/Quat\";\r\nimport { Shape, ShapeConfig, TriData } from \"./Shape\";\r\n\r\n/**\r\n * BoxLineShapeConfig - 方块线形状配置(用于缩放 Gizmo)\r\n */\r\nexport interface BoxLineShapeConfig extends ShapeConfig {\r\n gap?: number; // 起点与中心的间距\r\n lineThickness?: number; // 线条粗细\r\n lineLength?: number; // 线条长度\r\n boxSize?: number; // 末端方块大小\r\n tolerance?: number; // 碰撞检测容差\r\n}\r\n\r\n/**\r\n * BoxLineShape - 方块线形状(用于缩放 Gizmo)\r\n * 参考 PlayCanvas 引擎的 BoxLineShape 实现\r\n */\r\nexport class BoxLineShape extends Shape {\r\n private _gap: number = 0;\r\n private _lineThickness: number = 0.02;\r\n private _lineLength: number = 0.5;\r\n private _boxSize: number = 0.1;\r\n private _tolerance: number = 0.1;\r\n \r\n constructor(config: BoxLineShapeConfig) {\r\n super(config);\r\n \r\n this._gap = config.gap ?? this._gap;\r\n this._lineThickness = config.lineThickness ?? this._lineThickness;\r\n this._lineLength = config.lineLength ?? this._lineLength;\r\n this._boxSize = config.boxSize ?? this._boxSize;\r\n this._tolerance = config.tolerance ?? this._tolerance;\r\n \r\n this._updateTriData();\r\n }\r\n \r\n get gap(): number { return this._gap; }\r\n set gap(value: number) { this._gap = value; this._updateTriData(); }\r\n \r\n get lineThickness(): number { return this._lineThickness; }\r\n set lineThickness(value: number) { this._lineThickness = value; this._updateTriData(); }\r\n \r\n get lineLength(): number { return this._lineLength; }\r\n set lineLength(value: number) { this._lineLength = value; this._updateTriData(); }\r\n \r\n get boxSize(): number { return this._boxSize; }\r\n set boxSize(value: number) { this._boxSize = value; this._updateTriData(); }\r\n \r\n /**\r\n * 更新碰撞数据\r\n */\r\n private _updateTriData(): void {\r\n // 方块碰撞数据\r\n const boxPos = new Vec3(0, this._gap + this._lineLength + this._boxSize * 0.5, 0);\r\n const boxScale = new Vec3(this._boxSize, this._boxSize, this._boxSize);\r\n const boxTransform = Mat4.compose(boxPos, Quat.identity(), boxScale);\r\n \r\n // 线条圆柱碰撞数据(加上容差)\r\n const linePos = new Vec3(0, this._gap + this._lineLength * 0.5, 0);\r\n const lineScale = new Vec3(\r\n this._lineThickness + this._tolerance,\r\n this._lineLength,\r\n this._lineThickness + this._tolerance\r\n );\r\n const lineTransform = Mat4.compose(linePos, Quat.identity(), lineScale);\r\n \r\n // 生成单位方块和圆柱的三角形数据\r\n const boxGeo = this.createBoxGeometry();\r\n const cylinderGeo = this.createCylinderGeometry();\r\n \r\n this.triData = [\r\n {\r\n vertices: boxGeo.vertices,\r\n indices: boxGeo.indices,\r\n transform: boxTransform,\r\n priority: 0\r\n },\r\n {\r\n vertices: cylinderGeo.vertices,\r\n indices: cylinderGeo.indices,\r\n transform: lineTransform,\r\n priority: 1\r\n }\r\n ];\r\n }\r\n \r\n /**\r\n * 创建单位方块几何体(边长1,中心在原点)\r\n */\r\n private createBoxGeometry(): { vertices: Float32Array; indices: Uint16Array } {\r\n const h = 0.5;\r\n const vertices = new Float32Array([\r\n // 前面\r\n -h, -h, h,\r\n h, -h, h,\r\n h, h, h,\r\n -h, h, h,\r\n // 后面\r\n -h, -h, -h,\r\n -h, h, -h,\r\n h, h, -h,\r\n h, -h, -h,\r\n ]);\r\n \r\n const indices = new Uint16Array([\r\n // 前\r\n 0, 1, 2, 0, 2, 3,\r\n // 后\r\n 4, 5, 6, 4, 6, 7,\r\n // 上\r\n 3, 2, 6, 3, 6, 5,\r\n // 下\r\n 4, 7, 1, 4, 1, 0,\r\n // 右\r\n 1, 7, 6, 1, 6, 2,\r\n // 左\r\n 4, 0, 3, 4, 3, 5\r\n ]);\r\n \r\n return { vertices, indices };\r\n }\r\n \r\n /**\r\n * 创建单位圆柱几何体\r\n */\r\n private createCylinderGeometry(): { vertices: Float32Array; indices: Uint16Array } {\r\n const segments = 12;\r\n const vertices: number[] = [];\r\n const indices: number[] = [];\r\n \r\n for (let i = 0; i <= segments; i++) {\r\n const angle = (i / segments) * Math.PI * 2;\r\n const x = Math.cos(angle) * 0.5;\r\n const z = Math.sin(angle) * 0.5;\r\n \r\n vertices.push(x, -0.5, z);\r\n vertices.push(x, 0.5, z);\r\n }\r\n \r\n for (let i = 0; i < segments; i++) {\r\n const base = i * 2;\r\n indices.push(base, base + 1, base + 2);\r\n indices.push(base + 1, base + 3, base + 2);\r\n }\r\n \r\n return {\r\n vertices: new Float32Array(vertices),\r\n indices: new Uint16Array(indices)\r\n };\r\n }\r\n \r\n /**\r\n * 创建渲染用的 GPU 几何体\r\n */\r\n createGeometry(device: GPUDevice): void {\r\n const segments = 12;\r\n const vertices: number[] = [];\r\n const indices: number[] = [];\r\n \r\n const color = this._defaultColor;\r\n \r\n // 辅助函数:添加顶点\r\n const addVertex = (x: number, y: number, z: number) => {\r\n vertices.push(x, y, z, color.x, color.y, color.z);\r\n };\r\n \r\n // 生成圆柱(线条部分)\r\n const cylinderStart = this._gap;\r\n const cylinderEnd = this._gap + this._lineLength;\r\n \r\n for (let i = 0; i <= segments; i++) {\r\n const angle = (i / segments) * Math.PI * 2;\r\n const cos = Math.cos(angle);\r\n const sin = Math.sin(angle);\r\n \r\n const offsetX = cos * this._lineThickness;\r\n const offsetZ = sin * this._lineThickness;\r\n \r\n addVertex(offsetX, cylinderStart, offsetZ);\r\n addVertex(offsetX, cylinderEnd, offsetZ);\r\n }\r\n \r\n // 圆柱索引\r\n for (let i = 0; i < segments; i++) {\r\n const base = i * 2;\r\n indices.push(base, base + 1, base + 2);\r\n indices.push(base + 1, base + 3, base + 2);\r\n }\r\n \r\n // 生成方块\r\n const boxCenter = this._gap + this._lineLength + this._boxSize * 0.5;\r\n const h = this._boxSize * 0.5;\r\n \r\n const boxBaseIndex = vertices.length / 6;\r\n \r\n // 方块8个顶点\r\n addVertex(-h, boxCenter - h, h);\r\n addVertex( h, boxCenter - h, h);\r\n addVertex( h, boxCenter + h, h);\r\n addVertex(-h, boxCenter + h, h);\r\n addVertex(-h, boxCenter - h, -h);\r\n addVertex(-h, boxCenter + h, -h);\r\n addVertex( h, boxCenter + h, -h);\r\n addVertex( h, boxCenter - h, -h);\r\n \r\n // 方块索引\r\n const boxIndices = [\r\n 0, 1, 2, 0, 2, 3, // 前\r\n 4, 5, 6, 4, 6, 7, // 后\r\n 3, 2, 6, 3, 6, 5, // 上\r\n 4, 7, 1, 4, 1, 0, // 下\r\n 1, 7, 6, 1, 6, 2, // 右\r\n 4, 0, 3, 4, 3, 5 // 左\r\n ];\r\n \r\n for (const idx of boxIndices) {\r\n indices.push(boxBaseIndex + idx);\r\n }\r\n \r\n this.createBuffers(device, new Float32Array(vertices), new Uint16Array(indices));\r\n }\r\n}\r\n","import { Renderer } from \"../Renderer\";\r\nimport { Camera } from \"../Camera\";\r\nimport { Vec3 } from \"../math/Vec3\";\r\nimport { Ray } from \"../math/Ray\";\r\nimport { Mat4 } from \"../math/Mat4\";\r\nimport { Quat } from \"../math/Quat\";\r\nimport { Shape, GizmoAxisType } from \"./Shape\";\r\nimport { ArrowShape } from \"./ArrowShape\";\r\nimport { PlaneShape } from \"./PlaneShape\";\r\nimport { SphereShape } from \"./SphereShape\";\r\nimport { ArcShape, ArcDisplayMode } from \"./ArcShape\";\r\nimport { BoxLineShape } from \"./BoxLineShape\";\r\n\r\n/**\r\n * GizmoMode - Gizmo 操作模式\r\n */\r\nexport enum GizmoMode {\r\n Translate = 'translate',\r\n Rotate = 'rotate',\r\n Scale = 'scale',\r\n}\r\n\r\n/**\r\n * GizmoSpace - 坐标空间\r\n */\r\nexport type GizmoSpace = 'world' | 'local';\r\n\r\n/**\r\n * GizmoDragMode - 拖拽时的显示模式\r\n */\r\nexport type GizmoDragMode = 'show' | 'hide' | 'selected';\r\n\r\n/**\r\n * TransformableObject - 可变换对象接口\r\n */\r\nexport interface TransformableObject {\r\n position: [number, number, number] | Float32Array;\r\n rotation: [number, number, number] | Float32Array;\r\n scale: [number, number, number] | Float32Array;\r\n setPosition(x: number, y: number, z: number): void;\r\n setRotation(x: number, y: number, z: number): void;\r\n setScale(x: number, y: number, z: number): void;\r\n}\r\n\r\n/**\r\n * GizmoTheme - Gizmo 主题颜色\r\n */\r\nexport interface GizmoTheme {\r\n shapeBase: {\r\n x: Vec3;\r\n y: Vec3;\r\n z: Vec3;\r\n xyz: Vec3;\r\n f: Vec3; // 面向相机的轴\r\n };\r\n shapeHover: {\r\n x: Vec3;\r\n y: Vec3;\r\n z: Vec3;\r\n xyz: Vec3;\r\n f: Vec3;\r\n };\r\n guideBase: {\r\n x: Vec3;\r\n y: Vec3;\r\n z: Vec3;\r\n };\r\n disabled: Vec3;\r\n}\r\n\r\n// 默认主题\r\nconst DEFAULT_THEME: GizmoTheme = {\r\n shapeBase: {\r\n x: new Vec3(0.9, 0.2, 0.2),\r\n y: new Vec3(0.2, 0.9, 0.2),\r\n z: new Vec3(0.2, 0.4, 0.9),\r\n xyz: new Vec3(0.8, 0.8, 0.8),\r\n f: new Vec3(0.8, 0.8, 0.8),\r\n },\r\n shapeHover: {\r\n x: new Vec3(1.0, 0.6, 0.6),\r\n y: new Vec3(0.6, 1.0, 0.6),\r\n z: new Vec3(0.6, 0.6, 1.0),\r\n xyz: new Vec3(1.0, 1.0, 1.0),\r\n f: new Vec3(1.0, 1.0, 1.0),\r\n },\r\n guideBase: {\r\n x: new Vec3(0.9, 0.2, 0.2),\r\n y: new Vec3(0.2, 0.9, 0.2),\r\n z: new Vec3(0.2, 0.4, 0.9),\r\n },\r\n disabled: new Vec3(0.5, 0.5, 0.5),\r\n};\r\n\r\n// 常量\r\nconst GLANCE_EPSILON = 0.01;\r\nconst RING_FACING_EPSILON = 1e-4;\r\nconst PERS_SCALE_RATIO = 0.3;\r\nconst MIN_SCALE = 1e-4;\r\nconst RAD_TO_DEG = 180 / Math.PI;\r\n\r\n/**\r\n * TransformGizmoConfig - Gizmo 配置\r\n */\r\nexport interface TransformGizmoConfig {\r\n renderer: Renderer;\r\n camera: Camera;\r\n canvas: HTMLCanvasElement;\r\n size?: number;\r\n snap?: boolean;\r\n snapIncrement?: number;\r\n}\r\n\r\n/**\r\n * TransformGizmoV2 - 重构后的变换 Gizmo\r\n * 参考 PlayCanvas 引擎的 TransformGizmo 实现\r\n */\r\nexport class TransformGizmoV2 {\r\n private renderer: Renderer;\r\n private camera: Camera;\r\n private canvas: HTMLCanvasElement;\r\n \r\n // 配置\r\n private _size: number = 1.0;\r\n private _scale: number = 1.0;\r\n private _mode: GizmoMode = GizmoMode.Translate;\r\n private _coordSpace: GizmoSpace = 'world';\r\n private _theme: GizmoTheme = DEFAULT_THEME;\r\n \r\n // Snap 功能\r\n snap: boolean = false;\r\n snapIncrement: number = 1;\r\n \r\n // 拖拽模式\r\n dragMode: GizmoDragMode = 'selected';\r\n \r\n // 平面翻转\r\n flipPlanes: boolean = true;\r\n \r\n // 目标对象\r\n private _target: TransformableObject | null = null;\r\n \r\n // 形状\r\n private _shapes: Map<GizmoAxisType | 'f', Shape> = new Map();\r\n \r\n // 交互状态\r\n private _hoverAxis: GizmoAxisType | 'f' | '' = '';\r\n private _selectedAxis: GizmoAxisType | 'f' | '' = '';\r\n private _hoverIsPlane: boolean = false;\r\n private _selectedIsPlane: boolean = false;\r\n private _dragging: boolean = false;\r\n\r\n // 拖拽起始状态\r\n private _rootStartPos: Vec3 = new Vec3();\r\n private _rootStartRot: Quat = Quat.identity();\r\n private _selectionStartPoint: Vec3 = new Vec3();\r\n private _dragStartTransform: {\r\n position: Vec3;\r\n rotation: Vec3;\r\n scale: Vec3;\r\n } | null = null;\r\n \r\n // 面向相机的方向缓存\r\n private _facingDir: Vec3 = new Vec3();\r\n \r\n // 回调\r\n private _onDragStateChange: ((isDragging: boolean) => void) | null = null;\r\n \r\n // GPU 资源\r\n private pipeline: GPURenderPipeline | null = null;\r\n private linePipeline: GPURenderPipeline | null = null;\r\n private uniformBuffer: GPUBuffer | null = null;\r\n private bindGroup: GPUBindGroup | null = null;\r\n private bindGroupLayout: GPUBindGroupLayout | null = null;\r\n \r\n // 每个 Shape 的 uniform buffer 和 bind group\r\n private shapeUniformBuffers: Map<GizmoAxisType | 'f', GPUBuffer> = new Map();\r\n private shapeBindGroups: Map<GizmoAxisType | 'f', GPUBindGroup> = new Map();\r\n \r\n // 辅助线资源\r\n private guideLineBuffer: GPUBuffer | null = null;\r\n private guideLineBindGroup: GPUBindGroup | null = null;\r\n \r\n constructor(config: TransformGizmoConfig) {\r\n this.renderer = config.renderer;\r\n this.camera = config.camera;\r\n this.canvas = config.canvas;\r\n \r\n if (config.size !== undefined) this._size = config.size;\r\n if (config.snap !== undefined) this.snap = config.snap;\r\n if (config.snapIncrement !== undefined) this.snapIncrement = config.snapIncrement;\r\n }\r\n\r\n /**\r\n * 初始化 Gizmo\r\n */\r\n init(): void {\r\n this.createPipeline();\r\n this.createLinePipeline();\r\n this.createShapes();\r\n }\r\n\r\n /**\r\n * 创建 WebGPU 渲染管线\r\n */\r\n private createPipeline(): void {\r\n const device = this.renderer.device;\r\n \r\n // Shader 支持从 uniform 读取颜色(包含透明度)\r\n const shaderCode = `\r\n struct Uniforms {\r\n viewProjection: mat4x4<f32>,\r\n model: mat4x4<f32>,\r\n color: vec4<f32>,\r\n }\r\n\r\n @group(0) @binding(0) var<uniform> uniforms: Uniforms;\r\n\r\n struct VertexInput {\r\n @location(0) position: vec3<f32>,\r\n @location(1) vertexColor: vec3<f32>,\r\n }\r\n\r\n struct VertexOutput {\r\n @builtin(position) position: vec4<f32>,\r\n @location(0) color: vec4<f32>,\r\n }\r\n\r\n @vertex\r\n fn vertexMain(input: VertexInput) -> VertexOutput {\r\n var output: VertexOutput;\r\n let worldPos = uniforms.model * vec4<f32>(input.position, 1.0);\r\n output.position = uniforms.viewProjection * worldPos;\r\n // 使用 uniform 中的颜色,忽略顶点颜色\r\n output.color = uniforms.color;\r\n return output;\r\n }\r\n\r\n @fragment\r\n fn fragmentMain(input: VertexOutput) -> @location(0) vec4<f32> {\r\n // 如果透明度为 0,丢弃片元\r\n if (input.color.a < 0.01) {\r\n discard;\r\n }\r\n return input.color;\r\n }\r\n `;\r\n \r\n const shaderModule = device.createShaderModule({ code: shaderCode });\r\n \r\n this.uniformBuffer = device.createBuffer({\r\n size: 144, // 64 + 64 + 16 = 144 bytes (viewProj + model + color)\r\n usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\r\n });\r\n \r\n this.bindGroupLayout = device.createBindGroupLayout({\r\n entries: [{\r\n binding: 0,\r\n visibility: GPUShaderStage.VERTEX,\r\n buffer: { type: \"uniform\" },\r\n }],\r\n });\r\n\r\n this.bindGroup = device.createBindGroup({\r\n layout: this.bindGroupLayout,\r\n entries: [{ binding: 0, resource: { buffer: this.uniformBuffer } }],\r\n });\r\n \r\n const pipelineLayout = device.createPipelineLayout({\r\n bindGroupLayouts: [this.bindGroupLayout],\r\n });\r\n\r\n this.pipeline = device.createRenderPipeline({\r\n layout: pipelineLayout,\r\n vertex: {\r\n module: shaderModule,\r\n entryPoint: \"vertexMain\",\r\n buffers: [{\r\n arrayStride: 24,\r\n attributes: [\r\n { shaderLocation: 0, offset: 0, format: \"float32x3\" },\r\n { shaderLocation: 1, offset: 12, format: \"float32x3\" },\r\n ],\r\n }],\r\n },\r\n fragment: {\r\n module: shaderModule,\r\n entryPoint: \"fragmentMain\",\r\n targets: [{\r\n format: this.renderer.format,\r\n blend: {\r\n color: { srcFactor: \"src-alpha\", dstFactor: \"one-minus-src-alpha\", operation: \"add\" },\r\n alpha: { srcFactor: \"one\", dstFactor: \"one-minus-src-alpha\", operation: \"add\" },\r\n },\r\n }],\r\n },\r\n primitive: { topology: \"triangle-list\", cullMode: \"none\" },\r\n depthStencil: {\r\n format: this.renderer.depthFormat,\r\n depthWriteEnabled: false,\r\n depthCompare: \"always\",\r\n },\r\n });\r\n }\r\n\r\n /**\r\n * 创建辅助线渲染管线\r\n */\r\n private createLinePipeline(): void {\r\n const device = this.renderer.device;\r\n \r\n const shaderCode = `\r\n struct Uniforms {\r\n viewProjection: mat4x4<f32>,\r\n model: mat4x4<f32>,\r\n }\r\n\r\n @group(0) @binding(0) var<uniform> uniforms: Uniforms;\r\n\r\n struct VertexInput {\r\n @location(0) position: vec3<f32>,\r\n @location(1) color: vec3<f32>,\r\n }\r\n\r\n struct VertexOutput {\r\n @builtin(position) position: vec4<f32>,\r\n @location(0) color: vec3<f32>,\r\n }\r\n\r\n @vertex\r\n fn vertexMain(input: VertexInput) -> VertexOutput {\r\n var output: VertexOutput;\r\n let worldPos = uniforms.model * vec4<f32>(input.position, 1.0);\r\n output.position = uniforms.viewProjection * worldPos;\r\n output.color = input.color;\r\n return output;\r\n }\r\n\r\n @fragment\r\n fn fragmentMain(input: VertexOutput) -> @location(0) vec4<f32> {\r\n return vec4<f32>(input.color, 0.6);\r\n }\r\n `;\r\n \r\n const shaderModule = device.createShaderModule({ code: shaderCode });\r\n \r\n if (!this.bindGroupLayout) return;\r\n \r\n const pipelineLayout = device.createPipelineLayout({\r\n bindGroupLayouts: [this.bindGroupLayout],\r\n });\r\n\r\n this.linePipeline = device.createRenderPipeline({\r\n layout: pipelineLayout,\r\n vertex: {\r\n module: shaderModule,\r\n entryPoint: \"vertexMain\",\r\n buffers: [{\r\n arrayStride: 24,\r\n attributes: [\r\n { shaderLocation: 0, offset: 0, format: \"float32x3\" },\r\n { shaderLocation: 1, offset: 12, format: \"float32x3\" },\r\n ],\r\n }],\r\n },\r\n fragment: {\r\n module: shaderModule,\r\n entryPoint: \"fragmentMain\",\r\n targets: [{\r\n format: this.renderer.format,\r\n blend: {\r\n color: { srcFactor: \"src-alpha\", dstFactor: \"one-minus-src-alpha\", operation: \"add\" },\r\n alpha: { srcFactor: \"one\", dstFactor: \"one-minus-src-alpha\", operation: \"add\" },\r\n },\r\n }],\r\n },\r\n primitive: { topology: \"line-list\", cullMode: \"none\" },\r\n depthStencil: {\r\n format: this.renderer.depthFormat,\r\n depthWriteEnabled: false,\r\n depthCompare: \"always\",\r\n },\r\n });\r\n \r\n // 创建辅助线缓冲区\r\n this.guideLineBuffer = device.createBuffer({\r\n size: 128,\r\n usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\r\n });\r\n \r\n this.guideLineBindGroup = device.createBindGroup({\r\n layout: this.bindGroupLayout,\r\n entries: [{ binding: 0, resource: { buffer: this.guideLineBuffer } }],\r\n });\r\n }\r\n\r\n /**\r\n * 创建形状\r\n */\r\n private createShapes(): void {\r\n const device = this.renderer.device;\r\n \r\n // 清除现有形状和资源\r\n for (const shape of this._shapes.values()) {\r\n shape.destroy();\r\n }\r\n this._shapes.clear();\r\n \r\n for (const buffer of this.shapeUniformBuffers.values()) {\r\n buffer.destroy();\r\n }\r\n this.shapeUniformBuffers.clear();\r\n this.shapeBindGroups.clear();\r\n \r\n if (this._mode === GizmoMode.Translate) {\r\n this.createTranslateShapes(device);\r\n } else if (this._mode === GizmoMode.Rotate) {\r\n this.createRotateShapes(device);\r\n } else if (this._mode === GizmoMode.Scale) {\r\n this.createScaleShapes(device);\r\n }\r\n \r\n this.createShapeUniformBuffers(device);\r\n }\r\n \r\n /**\r\n * 为每个 Shape 创建独立的 uniform buffer 和 bind group\r\n */\r\n private createShapeUniformBuffers(device: GPUDevice): void {\r\n if (!this.bindGroupLayout) return;\r\n \r\n for (const [axis] of this._shapes) {\r\n const uniformBuffer = device.createBuffer({\r\n size: 144, // 64 + 64 + 16 = 144 bytes (viewProj + model + color)\r\n usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\r\n });\r\n \r\n const bindGroup = device.createBindGroup({\r\n layout: this.bindGroupLayout,\r\n entries: [{ binding: 0, resource: { buffer: uniformBuffer } }],\r\n });\r\n \r\n this.shapeUniformBuffers.set(axis, uniformBuffer);\r\n this.shapeBindGroups.set(axis, bindGroup);\r\n }\r\n }\r\n\r\n /**\r\n * 创建平移模式的形状\r\n */\r\n private createTranslateShapes(device: GPUDevice): void {\r\n const theme = this._theme;\r\n \r\n // 中心球\r\n const center = new SphereShape({\r\n axis: 'xyz',\r\n defaultColor: theme.shapeBase.xyz,\r\n hoverColor: theme.shapeHover.xyz,\r\n disabledColor: theme.disabled,\r\n radius: 0.1,\r\n });\r\n center.createGeometry(device);\r\n this._shapes.set('xyz', center);\r\n \r\n // X 轴箭头\r\n const xArrow = new ArrowShape({\r\n axis: 'x',\r\n defaultColor: theme.shapeBase.x,\r\n hoverColor: theme.shapeHover.x,\r\n disabledColor: theme.disabled,\r\n rotation: new Vec3(0, 0, -90),\r\n });\r\n xArrow.createGeometry(device);\r\n this._shapes.set('x', xArrow);\r\n \r\n // Y 轴箭头\r\n const yArrow = new ArrowShape({\r\n axis: 'y',\r\n defaultColor: theme.shapeBase.y,\r\n hoverColor: theme.shapeHover.y,\r\n disabledColor: theme.disabled,\r\n rotation: new Vec3(0, 0, 0),\r\n });\r\n yArrow.createGeometry(device);\r\n this._shapes.set('y', yArrow);\r\n \r\n // Z 轴箭头\r\n const zArrow = new ArrowShape({\r\n axis: 'z',\r\n defaultColor: theme.shapeBase.z,\r\n hoverColor: theme.shapeHover.z,\r\n disabledColor: theme.disabled,\r\n rotation: new Vec3(90, 0, 0),\r\n });\r\n zArrow.createGeometry(device);\r\n this._shapes.set('z', zArrow);\r\n \r\n // YZ 平面 (X 轴方向)\r\n const yzPlane = new PlaneShape({\r\n axis: 'yz',\r\n defaultColor: theme.shapeBase.x,\r\n hoverColor: theme.shapeHover.x,\r\n disabledColor: theme.disabled,\r\n rotation: new Vec3(0, 0, -90),\r\n });\r\n yzPlane.createGeometry(device);\r\n this._shapes.set('yz', yzPlane);\r\n \r\n // XZ 平面 (Y 轴方向)\r\n const xzPlane = new PlaneShape({\r\n axis: 'xz',\r\n defaultColor: theme.shapeBase.y,\r\n hoverColor: theme.shapeHover.y,\r\n disabledColor: theme.disabled,\r\n });\r\n xzPlane.createGeometry(device);\r\n this._shapes.set('xz', xzPlane);\r\n \r\n // XY 平面 (Z 轴方向)\r\n const xyPlane = new PlaneShape({\r\n axis: 'xy',\r\n defaultColor: theme.shapeBase.z,\r\n hoverColor: theme.shapeHover.z,\r\n disabledColor: theme.disabled,\r\n rotation: new Vec3(90, 0, 0),\r\n });\r\n xyPlane.createGeometry(device);\r\n this._shapes.set('xy', xyPlane);\r\n }\r\n\r\n /**\r\n * 创建旋转模式的形状\r\n */\r\n private createRotateShapes(device: GPUDevice): void {\r\n const theme = this._theme;\r\n \r\n // X 轴旋转环\r\n const xArc = new ArcShape({\r\n axis: 'x',\r\n defaultColor: theme.shapeBase.x,\r\n hoverColor: theme.shapeHover.x,\r\n disabledColor: theme.disabled,\r\n rotation: new Vec3(0, 0, -90),\r\n ringRadius: 0.5,\r\n tubeRadius: 0.015,\r\n sectorAngle: 180,\r\n tolerance: 0.05,\r\n });\r\n xArc.createGeometry(device);\r\n this._shapes.set('x', xArc);\r\n \r\n // Y 轴旋转环\r\n const yArc = new ArcShape({\r\n axis: 'y',\r\n defaultColor: theme.shapeBase.y,\r\n hoverColor: theme.shapeHover.y,\r\n disabledColor: theme.disabled,\r\n rotation: new Vec3(0, 0, 0),\r\n ringRadius: 0.5,\r\n tubeRadius: 0.015,\r\n sectorAngle: 180,\r\n tolerance: 0.05,\r\n });\r\n yArc.createGeometry(device);\r\n this._shapes.set('y', yArc);\r\n \r\n // Z 轴旋转环\r\n const zArc = new ArcShape({\r\n axis: 'z',\r\n defaultColor: theme.shapeBase.z,\r\n hoverColor: theme.shapeHover.z,\r\n disabledColor: theme.disabled,\r\n rotation: new Vec3(90, 0, 90),\r\n ringRadius: 0.5,\r\n tubeRadius: 0.015,\r\n sectorAngle: 180,\r\n tolerance: 0.05,\r\n });\r\n zArc.createGeometry(device);\r\n this._shapes.set('z', zArc);\r\n \r\n // 面向相机的旋转环\r\n const fArc = new ArcShape({\r\n axis: 'xyz', // 使用 xyz 作为内部 axis\r\n defaultColor: theme.shapeBase.f,\r\n hoverColor: theme.shapeHover.f,\r\n disabledColor: theme.disabled,\r\n ringRadius: 0.55,\r\n tubeRadius: 0.015,\r\n sectorAngle: 360,\r\n tolerance: 0.05,\r\n });\r\n fArc.createGeometry(device);\r\n this._shapes.set('f', fArc);\r\n \r\n // 中心球(用于自由旋转)- 默认半透明,hover 时更明显\r\n const center = new SphereShape({\r\n axis: 'xyz',\r\n defaultColor: theme.shapeBase.xyz,\r\n hoverColor: theme.shapeHover.xyz,\r\n disabledColor: theme.disabled,\r\n defaultAlpha: 0.0, // 默认完全透明\r\n hoverAlpha: 0.3, // hover 时半透明\r\n radius: 0.45,\r\n });\r\n center.createGeometry(device);\r\n this._shapes.set('xyz', center);\r\n }\r\n\r\n /**\r\n * 创建缩放模式的形状\r\n */\r\n private createScaleShapes(device: GPUDevice): void {\r\n const theme = this._theme;\r\n \r\n // 中心球(统一缩放)\r\n const center = new SphereShape({\r\n axis: 'xyz',\r\n defaultColor: theme.shapeBase.xyz,\r\n hoverColor: theme.shapeHover.xyz,\r\n disabledColor: theme.disabled,\r\n radius: 0.1,\r\n });\r\n center.createGeometry(device);\r\n this._shapes.set('xyz', center);\r\n \r\n // X 轴缩放\r\n const xBox = new BoxLineShape({\r\n axis: 'x',\r\n defaultColor: theme.shapeBase.x,\r\n hoverColor: theme.shapeHover.x,\r\n disabledColor: theme.disabled,\r\n rotation: new Vec3(0, 0, -90),\r\n });\r\n xBox.createGeometry(device);\r\n this._shapes.set('x', xBox);\r\n \r\n // Y 轴缩放\r\n const yBox = new BoxLineShape({\r\n axis: 'y',\r\n defaultColor: theme.shapeBase.y,\r\n hoverColor: theme.shapeHover.y,\r\n disabledColor: theme.disabled,\r\n });\r\n yBox.createGeometry(device);\r\n this._shapes.set('y', yBox);\r\n \r\n // Z 轴缩放\r\n const zBox = new BoxLineShape({\r\n axis: 'z',\r\n defaultColor: theme.shapeBase.z,\r\n hoverColor: theme.shapeHover.z,\r\n disabledColor: theme.disabled,\r\n rotation: new Vec3(90, 0, 0),\r\n });\r\n zBox.createGeometry(device);\r\n this._shapes.set('z', zBox);\r\n }\r\n\r\n // ==================== 属性访问器 ====================\r\n \r\n get mode(): GizmoMode { return this._mode; }\r\n set mode(value: GizmoMode) {\r\n if (this._mode !== value) {\r\n this._mode = value;\r\n this.createShapes();\r\n this._clearInteractionState();\r\n }\r\n }\r\n \r\n get coordSpace(): GizmoSpace { return this._coordSpace; }\r\n set coordSpace(value: GizmoSpace) { this._coordSpace = value; }\r\n \r\n get size(): number { return this._size; }\r\n set size(value: number) { this._size = value; }\r\n \r\n get target(): TransformableObject | null { return this._target; }\r\n \r\n get isDragging(): boolean { return this._dragging; }\r\n\r\n // ==================== 目标管理 ====================\r\n \r\n setTarget(object: TransformableObject | null): void {\r\n this._target = object;\r\n this._clearInteractionState();\r\n }\r\n \r\n setOnDragStateChange(callback: ((isDragging: boolean) => void) | null): void {\r\n this._onDragStateChange = callback;\r\n }\r\n \r\n private _clearInteractionState(): void {\r\n this._hoverAxis = '';\r\n this._selectedAxis = '';\r\n this._hoverIsPlane = false;\r\n this._selectedIsPlane = false;\r\n this._dragging = false;\r\n this._dragStartTransform = null;\r\n \r\n for (const shape of this._shapes.values()) {\r\n shape.hover(false);\r\n }\r\n }\r\n\r\n // ==================== 缩放计算 ====================\r\n \r\n private getGizmoPosition(): Vec3 | null {\r\n if (!this._target) return null;\r\n return new Vec3(\r\n this._target.position[0],\r\n this._target.position[1],\r\n this._target.position[2]\r\n );\r\n }\r\n \r\n /**\r\n * 获取 Gizmo 的视觉旋转(用于渲染形状)\r\n * - 平移模式:根据 coordSpace 设置\r\n * - 旋转模式:始终使用世界坐标(三个轴保持正交)\r\n * - 缩放模式:始终使用本地坐标(跟随物体旋转)\r\n */\r\n private getGizmoRotation(): Quat {\r\n if (this._mode === GizmoMode.Rotate) {\r\n // 旋转模式始终使用世界坐标,保持三个轴正交\r\n return Quat.identity();\r\n }\r\n if (this._mode === GizmoMode.Scale && this._target) {\r\n // 缩放模式始终使用本地坐标\r\n return Quat.fromEuler(\r\n this._target.rotation[0],\r\n this._target.rotation[1],\r\n this._target.rotation[2]\r\n );\r\n }\r\n // 平移模式根据 coordSpace 设置\r\n if (this._coordSpace === 'local' && this._target) {\r\n return Quat.fromEuler(\r\n this._target.rotation[0],\r\n this._target.rotation[1],\r\n this._target.rotation[2]\r\n );\r\n }\r\n return Quat.identity();\r\n }\r\n \r\n private updateScale(): void {\r\n const gizmoPos = this.getGizmoPosition();\r\n if (!gizmoPos) return;\r\n \r\n const cameraPos = new Vec3(\r\n this.camera.position[0],\r\n this.camera.position[1],\r\n this.camera.position[2]\r\n );\r\n \r\n const dist = gizmoPos.distance(cameraPos);\r\n this._scale = Math.tan(0.5 * this.camera.fov) * dist * PERS_SCALE_RATIO;\r\n this._scale = Math.max(this._scale * this._size, MIN_SCALE);\r\n }\r\n \r\n /**\r\n * 获取面向相机的方向(从 Gizmo 指向相机)\r\n */\r\n private getFacingDir(): Vec3 {\r\n const gizmoPos = this.getGizmoPosition();\r\n if (!gizmoPos) return new Vec3(0, 0, 1);\r\n \r\n const cameraPos = new Vec3(\r\n this.camera.position[0],\r\n this.camera.position[1],\r\n this.camera.position[2]\r\n );\r\n \r\n return cameraPos.subtract(gizmoPos).normalize();\r\n }\r\n\r\n // ==================== 动态形状调整 ====================\r\n \r\n /**\r\n * 更新形状以面向相机\r\n * 参考 PlayCanvas 的 _shapesLookAtCamera\r\n */\r\n private _shapesLookAtCamera(): void {\r\n if (this._mode === GizmoMode.Translate) {\r\n this._updateTranslateShapesForCamera();\r\n } else if (this._mode === GizmoMode.Rotate) {\r\n this._updateRotateShapesForCamera();\r\n }\r\n }\r\n \r\n /**\r\n * 更新平移形状\r\n */\r\n private _updateTranslateShapesForCamera(): void {\r\n const facingDir = this.getFacingDir();\r\n const gizmoRot = this.getGizmoRotation();\r\n \r\n // 计算 Gizmo 的三个轴方向\r\n const right = gizmoRot.transformVector(new Vec3(1, 0, 0));\r\n const up = gizmoRot.transformVector(new Vec3(0, 1, 0));\r\n const forward = gizmoRot.transformVector(new Vec3(0, 0, 1));\r\n \r\n // 轴可见性\r\n const xShape = this._shapes.get('x');\r\n if (xShape) {\r\n const dot = Math.abs(facingDir.dot(right));\r\n xShape.visible = (1 - dot) > GLANCE_EPSILON;\r\n }\r\n \r\n const yShape = this._shapes.get('y');\r\n if (yShape) {\r\n const dot = Math.abs(facingDir.dot(up));\r\n yShape.visible = (1 - dot) > GLANCE_EPSILON;\r\n }\r\n \r\n const zShape = this._shapes.get('z');\r\n if (zShape) {\r\n const dot = Math.abs(facingDir.dot(forward));\r\n zShape.visible = (1 - dot) > GLANCE_EPSILON;\r\n }\r\n \r\n // 平面可见性和翻转\r\n const yzPlane = this._shapes.get('yz') as PlaneShape | undefined;\r\n if (yzPlane) {\r\n const cross = facingDir.cross(right);\r\n yzPlane.visible = (1 - cross.length()) > GLANCE_EPSILON;\r\n if (this.flipPlanes) {\r\n const flipped = new Vec3(\r\n 0,\r\n cross.dot(forward) < 0 ? 1 : 0,\r\n cross.dot(up) < 0 ? 1 : 0\r\n );\r\n yzPlane.setFlipped(flipped);\r\n }\r\n }\r\n \r\n const xzPlane = this._shapes.get('xz') as PlaneShape | undefined;\r\n if (xzPlane) {\r\n const cross = facingDir.cross(up);\r\n xzPlane.visible = (1 - cross.length()) > GLANCE_EPSILON;\r\n if (this.flipPlanes) {\r\n const flipped = new Vec3(\r\n cross.dot(forward) > 0 ? 1 : 0,\r\n 0,\r\n cross.dot(right) > 0 ? 1 : 0\r\n );\r\n xzPlane.setFlipped(flipped);\r\n }\r\n }\r\n \r\n const xyPlane = this._shapes.get('xy') as PlaneShape | undefined;\r\n if (xyPlane) {\r\n const cross = facingDir.cross(forward);\r\n xyPlane.visible = (1 - cross.length()) > GLANCE_EPSILON;\r\n if (this.flipPlanes) {\r\n const flipped = new Vec3(\r\n cross.dot(up) < 0 ? 1 : 0,\r\n cross.dot(right) > 0 ? 1 : 0,\r\n 0\r\n );\r\n xyPlane.setFlipped(flipped);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * 更新旋转形状\r\n */\r\n private _updateRotateShapesForCamera(): void {\r\n const facingDir = this.getFacingDir();\r\n const gizmoRot = this.getGizmoRotation();\r\n \r\n // 将面向方向转换到 Gizmo 局部空间\r\n const invRot = gizmoRot.inverse();\r\n const localFacingDir = invRot.transformVector(facingDir.clone());\r\n \r\n // 计算 Gizmo 的三个轴方向\r\n const right = gizmoRot.transformVector(new Vec3(1, 0, 0));\r\n const up = gizmoRot.transformVector(new Vec3(0, 1, 0));\r\n const forward = gizmoRot.transformVector(new Vec3(0, 0, 1));\r\n \r\n // X 轴旋转环\r\n const xArc = this._shapes.get('x') as ArcShape | undefined;\r\n if (xArc) {\r\n const angle = Math.atan2(localFacingDir.z, localFacingDir.y) * RAD_TO_DEG;\r\n xArc.setDynamicRotation(new Vec3(0, angle - 90, 0));\r\n \r\n const dot = facingDir.dot(right);\r\n if (!this._dragging) {\r\n const showSector = 1 - Math.abs(dot) > RING_FACING_EPSILON;\r\n xArc.show(showSector ? 'sector' : 'ring');\r\n }\r\n }\r\n \r\n // Y 轴旋转环\r\n const yArc = this._shapes.get('y') as ArcShape | undefined;\r\n if (yArc) {\r\n const angle = Math.atan2(localFacingDir.x, localFacingDir.z) * RAD_TO_DEG;\r\n yArc.setDynamicRotation(new Vec3(0, angle, 0));\r\n \r\n const dot = facingDir.dot(up);\r\n if (!this._dragging) {\r\n const showSector = 1 - Math.abs(dot) > RING_FACING_EPSILON;\r\n yArc.show(showSector ? 'sector' : 'ring');\r\n }\r\n }\r\n \r\n // Z 轴旋转环\r\n const zArc = this._shapes.get('z') as ArcShape | undefined;\r\n if (zArc) {\r\n const angle = Math.atan2(localFacingDir.y, localFacingDir.x) * RAD_TO_DEG;\r\n zArc.setDynamicRotation(new Vec3(0, 0, angle));\r\n \r\n const dot = facingDir.dot(forward);\r\n if (!this._dragging) {\r\n const showSector = 1 - Math.abs(dot) > RING_FACING_EPSILON;\r\n zArc.show(showSector ? 'sector' : 'ring');\r\n }\r\n }\r\n \r\n // 面向相机的旋转环\r\n const fArc = this._shapes.get('f') as ArcShape | undefined;\r\n if (fArc) {\r\n // 计算面向相机的旋转\r\n const cameraPos = new Vec3(\r\n this.camera.position[0],\r\n this.camera.position[1],\r\n this.camera.position[2]\r\n );\r\n const gizmoPos = this.getGizmoPosition()!;\r\n const dir = cameraPos.subtract(gizmoPos).normalize();\r\n \r\n const elev = Math.atan2(-dir.y, Math.sqrt(dir.x * dir.x + dir.z * dir.z)) * RAD_TO_DEG;\r\n const azim = Math.atan2(-dir.x, -dir.z) * RAD_TO_DEG;\r\n \r\n // 设置面向相机的旋转(覆盖基础旋转)\r\n fArc.setDynamicRotation(new Vec3(-elev + 90, azim, 0));\r\n }\r\n \r\n this._facingDir = facingDir;\r\n }\r\n\r\n /**\r\n * 拖拽时更新形状显示\r\n */\r\n private _updateDragVisibility(isDragging: boolean): void {\r\n if (this._mode === GizmoMode.Rotate) {\r\n this._updateRotateDragVisibility(isDragging);\r\n } else if (this._mode === GizmoMode.Translate) {\r\n this._updateTranslateDragVisibility(isDragging);\r\n } else if (this._mode === GizmoMode.Scale) {\r\n this._updateScaleDragVisibility(isDragging);\r\n }\r\n }\r\n \r\n private _updateRotateDragVisibility(isDragging: boolean): void {\r\n for (const [axis, shape] of this._shapes) {\r\n if (!(shape instanceof ArcShape)) continue;\r\n \r\n switch (this.dragMode) {\r\n case 'show':\r\n break;\r\n case 'hide':\r\n if (isDragging) {\r\n (shape as ArcShape).show(axis === this._selectedAxis ? 'ring' : 'none');\r\n } else {\r\n (shape as ArcShape).show('sector');\r\n }\r\n break;\r\n case 'selected':\r\n if (isDragging) {\r\n (shape as ArcShape).show(axis === this._selectedAxis ? 'ring' : 'sector');\r\n }\r\n break;\r\n }\r\n }\r\n }\r\n \r\n private _updateTranslateDragVisibility(isDragging: boolean): void {\r\n for (const [axis, shape] of this._shapes) {\r\n switch (this.dragMode) {\r\n case 'show':\r\n break;\r\n case 'hide':\r\n shape.visible = !isDragging;\r\n break;\r\n case 'selected':\r\n if (this._selectedAxis === 'xyz') {\r\n shape.visible = isDragging ? axis.length === 1 : true;\r\n } else if (this._selectedIsPlane) {\r\n shape.visible = isDragging ? axis.length === 1 && !axis.includes(this._selectedAxis) : true;\r\n } else {\r\n shape.visible = isDragging ? axis === this._selectedAxis : true;\r\n }\r\n break;\r\n }\r\n }\r\n }\r\n \r\n private _updateScaleDragVisibility(isDragging: boolean): void {\r\n for (const [axis, shape] of this._shapes) {\r\n switch (this.dragMode) {\r\n case 'show':\r\n break;\r\n case 'hide':\r\n shape.visible = !isDragging;\r\n break;\r\n case 'selected':\r\n if (this._selectedAxis === 'xyz') {\r\n shape.visible = isDragging ? axis.length === 1 : true;\r\n } else {\r\n shape.visible = isDragging ? axis === this._selectedAxis : true;\r\n }\r\n break;\r\n }\r\n }\r\n }\r\n\r\n // ==================== 射线转换 ====================\r\n \r\n private screenToRay(screenX: number, screenY: number): Ray {\r\n const rect = this.canvas.getBoundingClientRect();\r\n const canvasX = screenX - rect.left;\r\n const canvasY = screenY - rect.top;\r\n \r\n const scaleX = this.canvas.width / rect.width;\r\n const scaleY = this.canvas.height / rect.height;\r\n \r\n const pixelX = canvasX * scaleX;\r\n const pixelY = canvasY * scaleY;\r\n \r\n return Ray.fromScreenPoint(\r\n pixelX,\r\n pixelY,\r\n this.canvas.width,\r\n this.canvas.height,\r\n this.camera\r\n );\r\n }\r\n \r\n private performHitTest(ray: Ray): { axis: GizmoAxisType | 'f' | ''; isPlane: boolean } {\r\n if (!this._target) {\r\n return { axis: '', isPlane: false };\r\n }\r\n \r\n const gizmoPos = this.getGizmoPosition()!;\r\n const gizmoRot = this.getGizmoRotation();\r\n \r\n const scale = new Vec3(this._scale, this._scale, this._scale);\r\n const worldTransform = Mat4.compose(gizmoPos, gizmoRot, scale);\r\n \r\n let closestDist: number | null = null;\r\n let closestAxis: GizmoAxisType | 'f' | '' = '';\r\n let closestIsPlane = false;\r\n \r\n for (const [axis, shape] of this._shapes) {\r\n // 不检查 visible,让 Shape.intersect() 通过 interactable 属性控制\r\n // 这样透明但可交互的形状(如旋转模式的中心球)仍然可以被选中\r\n if (shape.disabled) continue;\r\n \r\n const dist = shape.intersect(ray, worldTransform);\r\n if (dist !== null && (closestDist === null || dist < closestDist)) {\r\n closestDist = dist;\r\n closestAxis = axis;\r\n closestIsPlane = axis.length === 2;\r\n }\r\n }\r\n \r\n return { axis: closestAxis, isPlane: closestIsPlane };\r\n }\r\n\r\n // ==================== 事件处理 ====================\r\n \r\n onPointerMove(event: PointerEvent): void {\r\n if (!this._target) return;\r\n \r\n const ray = this.screenToRay(event.clientX, event.clientY);\r\n \r\n if (!this._dragging) {\r\n const hit = this.performHitTest(ray);\r\n this._updateHover(hit.axis, hit.isPlane);\r\n } else {\r\n const point = this._screenToPoint(event.clientX, event.clientY);\r\n this._applyTransform(point);\r\n }\r\n }\r\n \r\n onPointerDown(event: PointerEvent): void {\r\n if (!this._target) return;\r\n \r\n const ray = this.screenToRay(event.clientX, event.clientY);\r\n const hit = this.performHitTest(ray);\r\n \r\n if (hit.axis) {\r\n this._selectedAxis = hit.axis;\r\n this._selectedIsPlane = hit.isPlane;\r\n this._dragging = true;\r\n \r\n this._rootStartPos = this.getGizmoPosition()!.clone();\r\n this._rootStartRot = this.getGizmoRotation();\r\n \r\n const point = this._screenToPoint(event.clientX, event.clientY);\r\n this._selectionStartPoint = point.clone();\r\n \r\n this._dragStartTransform = {\r\n position: new Vec3(\r\n this._target.position[0],\r\n this._target.position[1],\r\n this._target.position[2]\r\n ),\r\n rotation: new Vec3(\r\n this._target.rotation[0],\r\n this._target.rotation[1],\r\n this._target.rotation[2]\r\n ),\r\n scale: new Vec3(\r\n this._target.scale[0],\r\n this._target.scale[1],\r\n this._target.scale[2]\r\n ),\r\n };\r\n \r\n this.canvas.setPointerCapture(event.pointerId);\r\n \r\n // 更新拖拽时的形状显示\r\n this._updateDragVisibility(true);\r\n \r\n if (this._onDragStateChange) {\r\n this._onDragStateChange(true);\r\n }\r\n }\r\n }\r\n \r\n onPointerUp(event: PointerEvent): void {\r\n const wasDragging = this._dragging;\r\n \r\n this._dragging = false;\r\n this._selectedAxis = '';\r\n this._selectedIsPlane = false;\r\n this._dragStartTransform = null;\r\n \r\n if (this.canvas.hasPointerCapture(event.pointerId)) {\r\n this.canvas.releasePointerCapture(event.pointerId);\r\n }\r\n \r\n // 恢复形状显示\r\n this._updateDragVisibility(false);\r\n \r\n if (wasDragging && this._onDragStateChange) {\r\n this._onDragStateChange(false);\r\n }\r\n }\r\n\r\n private _updateHover(axis: GizmoAxisType | 'f' | '', isPlane: boolean): void {\r\n if (this._dragging) return;\r\n \r\n if (this._hoverAxis !== axis) {\r\n // 清除之前的 hover 状态\r\n for (const shape of this._shapes.values()) {\r\n shape.hover(false);\r\n }\r\n \r\n this._hoverAxis = axis;\r\n this._hoverIsPlane = isPlane;\r\n \r\n if (axis) {\r\n if (axis === 'xyz') {\r\n this._shapes.get('x')?.hover(true);\r\n this._shapes.get('y')?.hover(true);\r\n this._shapes.get('z')?.hover(true);\r\n this._shapes.get('xyz')?.hover(true);\r\n } else if (axis === 'f') {\r\n this._shapes.get('f')?.hover(true);\r\n } else if (isPlane) {\r\n const shape = this._shapes.get(axis);\r\n shape?.hover(true);\r\n for (const char of axis) {\r\n this._shapes.get(char as GizmoAxisType)?.hover(true);\r\n }\r\n } else {\r\n this._shapes.get(axis)?.hover(true);\r\n }\r\n }\r\n }\r\n }\r\n\r\n // ==================== 变换计算 ====================\r\n \r\n private _screenToPoint(x: number, y: number): Vec3 {\r\n const ray = this.screenToRay(x, y);\r\n const axis = this._selectedAxis;\r\n const isPlane = this._selectedIsPlane;\r\n \r\n // 旋转模式使用不同的平面\r\n if (this._mode === GizmoMode.Rotate && axis && axis !== 'xyz' && axis.length === 1) {\r\n const plane = this._createRotationPlane(axis);\r\n const dist = ray.intersectPlane(plane.point, plane.normal);\r\n if (dist === null) {\r\n return this._rootStartPos.clone();\r\n }\r\n return ray.at(dist);\r\n }\r\n \r\n // 面向相机的旋转\r\n if (this._mode === GizmoMode.Rotate && axis === 'f') {\r\n const plane = this._createFacingPlane();\r\n const dist = ray.intersectPlane(plane.point, plane.normal);\r\n if (dist === null) {\r\n return this._rootStartPos.clone();\r\n }\r\n return ray.at(dist);\r\n }\r\n \r\n const plane = this._createInteractionPlane(axis as GizmoAxisType, isPlane);\r\n \r\n const dist = ray.intersectPlane(plane.point, plane.normal);\r\n if (dist === null) {\r\n return new Vec3(0, 0, 0);\r\n }\r\n \r\n const point = ray.at(dist);\r\n \r\n const localPoint = point.subtract(this._rootStartPos);\r\n const invRot = this._rootStartRot.inverse();\r\n const rotatedPoint = invRot.transformVector(localPoint);\r\n \r\n if (!isPlane && axis !== 'xyz' && axis !== 'f' && axis.length === 1) {\r\n this._projectToAxis(rotatedPoint, axis);\r\n }\r\n \r\n return rotatedPoint;\r\n }\r\n\r\n private _createRotationPlane(axis: string): { point: Vec3; normal: Vec3 } {\r\n const point = this._rootStartPos.clone();\r\n let normal = new Vec3(0, 1, 0);\r\n \r\n if (this._coordSpace === 'local') {\r\n if (axis === 'x') normal = this._rootStartRot.transformVector(new Vec3(1, 0, 0));\r\n else if (axis === 'y') normal = this._rootStartRot.transformVector(new Vec3(0, 1, 0));\r\n else if (axis === 'z') normal = this._rootStartRot.transformVector(new Vec3(0, 0, 1));\r\n } else {\r\n if (axis === 'x') normal = new Vec3(1, 0, 0);\r\n else if (axis === 'y') normal = new Vec3(0, 1, 0);\r\n else if (axis === 'z') normal = new Vec3(0, 0, 1);\r\n }\r\n \r\n return { point, normal };\r\n }\r\n \r\n private _createFacingPlane(): { point: Vec3; normal: Vec3 } {\r\n const point = this._rootStartPos.clone();\r\n const normal = this.getFacingDir().multiply(-1);\r\n return { point, normal };\r\n }\r\n \r\n private _createInteractionPlane(\r\n axis: GizmoAxisType | '', \r\n isPlane: boolean\r\n ): { point: Vec3; normal: Vec3 } {\r\n const point = this._rootStartPos.clone();\r\n let normal = new Vec3(0, 1, 0);\r\n \r\n if (axis === 'xyz' || isPlane) {\r\n const facingDir = this.getFacingDir();\r\n normal = facingDir.multiply(-1);\r\n } else if (axis === 'x') {\r\n const axisDir = this._rootStartRot.transformVector(new Vec3(1, 0, 0));\r\n const cameraDir = this.getFacingDir();\r\n const cross = axisDir.cross(cameraDir);\r\n if (cross.lengthSquared() > 1e-6) {\r\n normal = cross.cross(axisDir).normalize();\r\n } else {\r\n normal = this._rootStartRot.transformVector(new Vec3(0, 1, 0));\r\n }\r\n } else if (axis === 'y') {\r\n const axisDir = this._rootStartRot.transformVector(new Vec3(0, 1, 0));\r\n const cameraDir = this.getFacingDir();\r\n const cross = axisDir.cross(cameraDir);\r\n if (cross.lengthSquared() > 1e-6) {\r\n normal = cross.cross(axisDir).normalize();\r\n } else {\r\n normal = this._rootStartRot.transformVector(new Vec3(1, 0, 0));\r\n }\r\n } else if (axis === 'z') {\r\n const axisDir = this._rootStartRot.transformVector(new Vec3(0, 0, 1));\r\n const cameraDir = this.getFacingDir();\r\n const cross = axisDir.cross(cameraDir);\r\n if (cross.lengthSquared() > 1e-6) {\r\n normal = cross.cross(axisDir).normalize();\r\n } else {\r\n normal = this._rootStartRot.transformVector(new Vec3(0, 1, 0));\r\n }\r\n }\r\n \r\n return { point, normal };\r\n }\r\n \r\n private _projectToAxis(point: Vec3, axis: string): void {\r\n if (axis === 'x') {\r\n point.y = 0;\r\n point.z = 0;\r\n } else if (axis === 'y') {\r\n point.x = 0;\r\n point.z = 0;\r\n } else if (axis === 'z') {\r\n point.x = 0;\r\n point.y = 0;\r\n }\r\n }\r\n\r\n private _applyTransform(point: Vec3): void {\r\n if (!this._target || !this._dragStartTransform) return;\r\n \r\n const delta = point.subtract(this._selectionStartPoint);\r\n \r\n if (this._mode === GizmoMode.Translate) {\r\n this._applyTranslation(delta);\r\n } else if (this._mode === GizmoMode.Rotate) {\r\n this._applyRotation(point);\r\n } else if (this._mode === GizmoMode.Scale) {\r\n this._applyScale(delta);\r\n }\r\n }\r\n \r\n private _applyTranslation(delta: Vec3): void {\r\n if (!this._target || !this._dragStartTransform) return;\r\n \r\n if (this.snap) {\r\n delta.x = Math.round(delta.x / this.snapIncrement) * this.snapIncrement;\r\n delta.y = Math.round(delta.y / this.snapIncrement) * this.snapIncrement;\r\n delta.z = Math.round(delta.z / this.snapIncrement) * this.snapIncrement;\r\n }\r\n \r\n const worldDelta = this._rootStartRot.transformVector(delta);\r\n \r\n const newPos = this._dragStartTransform.position.add(worldDelta);\r\n this._target.setPosition(newPos.x, newPos.y, newPos.z);\r\n }\r\n \r\n private _applyRotation(point: Vec3): void {\r\n if (!this._target || !this._dragStartTransform) return;\r\n \r\n const axis = this._selectedAxis;\r\n if (!axis) return;\r\n \r\n // 面向相机的旋转\r\n if (axis === 'f') {\r\n this._applyFacingRotation(point);\r\n return;\r\n }\r\n \r\n if (axis === 'xyz') return;\r\n \r\n const gizmoPos = this._rootStartPos;\r\n \r\n const startVec = this._selectionStartPoint.subtract(gizmoPos);\r\n const currentVec = point.subtract(gizmoPos);\r\n \r\n let rotAxis = new Vec3(0, 1, 0);\r\n if (this._coordSpace === 'local') {\r\n if (axis === 'x') rotAxis = this._rootStartRot.transformVector(new Vec3(1, 0, 0));\r\n else if (axis === 'y') rotAxis = this._rootStartRot.transformVector(new Vec3(0, 1, 0));\r\n else if (axis === 'z') rotAxis = this._rootStartRot.transformVector(new Vec3(0, 0, 1));\r\n } else {\r\n if (axis === 'x') rotAxis = new Vec3(1, 0, 0);\r\n else if (axis === 'y') rotAxis = new Vec3(0, 1, 0);\r\n else if (axis === 'z') rotAxis = new Vec3(0, 0, 1);\r\n }\r\n \r\n if (startVec.lengthSquared() < 1e-6 || currentVec.lengthSquared() < 1e-6) {\r\n return;\r\n }\r\n \r\n const startNorm = startVec.normalize();\r\n const currentNorm = currentVec.normalize();\r\n \r\n const cosAngle = Math.max(-1, Math.min(1, startNorm.dot(currentNorm)));\r\n const crossVec = startNorm.cross(currentNorm);\r\n const sinAngle = crossVec.dot(rotAxis);\r\n let angleDelta = Math.atan2(sinAngle, cosAngle);\r\n \r\n if (this.snap) {\r\n const snapAngle = this.snapIncrement * Math.PI / 180;\r\n angleDelta = Math.round(angleDelta / snapAngle) * snapAngle;\r\n }\r\n \r\n const deltaQuat = Quat.fromAxisAngle(rotAxis, angleDelta);\r\n \r\n const startRot = Quat.fromEuler(\r\n this._dragStartTransform.rotation.x,\r\n this._dragStartTransform.rotation.y,\r\n this._dragStartTransform.rotation.z\r\n );\r\n \r\n const newRot = deltaQuat.multiply(startRot);\r\n \r\n const euler = newRot.toEuler();\r\n this._target.setRotation(euler.x, euler.y, euler.z);\r\n }\r\n\r\n private _applyFacingRotation(point: Vec3): void {\r\n if (!this._target || !this._dragStartTransform) return;\r\n \r\n const gizmoPos = this._rootStartPos;\r\n \r\n const startVec = this._selectionStartPoint.subtract(gizmoPos);\r\n const currentVec = point.subtract(gizmoPos);\r\n \r\n // 旋转轴是面向相机的方向\r\n const rotAxis = this.getFacingDir();\r\n \r\n if (startVec.lengthSquared() < 1e-6 || currentVec.lengthSquared() < 1e-6) {\r\n return;\r\n }\r\n \r\n const startNorm = startVec.normalize();\r\n const currentNorm = currentVec.normalize();\r\n \r\n const cosAngle = Math.max(-1, Math.min(1, startNorm.dot(currentNorm)));\r\n const crossVec = startNorm.cross(currentNorm);\r\n const sinAngle = crossVec.dot(rotAxis);\r\n let angleDelta = Math.atan2(sinAngle, cosAngle);\r\n \r\n if (this.snap) {\r\n const snapAngle = this.snapIncrement * Math.PI / 180;\r\n angleDelta = Math.round(angleDelta / snapAngle) * snapAngle;\r\n }\r\n \r\n const deltaQuat = Quat.fromAxisAngle(rotAxis, angleDelta);\r\n \r\n const startRot = Quat.fromEuler(\r\n this._dragStartTransform.rotation.x,\r\n this._dragStartTransform.rotation.y,\r\n this._dragStartTransform.rotation.z\r\n );\r\n \r\n const newRot = deltaQuat.multiply(startRot);\r\n \r\n const euler = newRot.toEuler();\r\n this._target.setRotation(euler.x, euler.y, euler.z);\r\n }\r\n \r\n private _applyScale(delta: Vec3): void {\r\n if (!this._target || !this._dragStartTransform) return;\r\n \r\n const axis = this._selectedAxis;\r\n \r\n let scaleFactor = 1.0;\r\n if (axis === 'x') scaleFactor = 1.0 + delta.x;\r\n else if (axis === 'y') scaleFactor = 1.0 + delta.y;\r\n else if (axis === 'z') scaleFactor = 1.0 + delta.z;\r\n else if (axis === 'xyz') scaleFactor = 1.0 + (delta.x + delta.y + delta.z) / 3;\r\n \r\n scaleFactor = Math.max(0.001, scaleFactor);\r\n \r\n if (this.snap) {\r\n scaleFactor = Math.round(scaleFactor / this.snapIncrement) * this.snapIncrement;\r\n scaleFactor = Math.max(0.001, scaleFactor);\r\n }\r\n \r\n const newScale = this._dragStartTransform.scale.clone();\r\n if (axis === 'x') newScale.x *= scaleFactor;\r\n else if (axis === 'y') newScale.y *= scaleFactor;\r\n else if (axis === 'z') newScale.z *= scaleFactor;\r\n else if (axis === 'xyz') {\r\n newScale.x *= scaleFactor;\r\n newScale.y *= scaleFactor;\r\n newScale.z *= scaleFactor;\r\n }\r\n \r\n this._target.setScale(newScale.x, newScale.y, newScale.z);\r\n }\r\n\r\n // ==================== 渲染 ====================\r\n \r\n private getShapeModelMatrix(shape: Shape): Mat4 {\r\n const gizmoPos = this.getGizmoPosition()!;\r\n const gizmoRot = this.getGizmoRotation();\r\n const scale = new Vec3(this._scale, this._scale, this._scale);\r\n \r\n const gizmoMatrix = Mat4.compose(gizmoPos, gizmoRot, scale);\r\n const shapeLocalMatrix = shape.getLocalTransform();\r\n \r\n return gizmoMatrix.multiply(shapeLocalMatrix);\r\n }\r\n \r\n private updateUniformsForShape(axis: GizmoAxisType | 'f', shape: Shape): void {\r\n const uniformBuffer = this.shapeUniformBuffers.get(axis);\r\n if (!uniformBuffer) return;\r\n \r\n const device = this.renderer.device;\r\n \r\n const modelMatrix = this.getShapeModelMatrix(shape);\r\n \r\n const viewProjectionMatrix = new Mat4();\r\n viewProjectionMatrix.elements.set(this.camera.viewProjectionMatrix);\r\n \r\n // 获取当前颜色(包含透明度)\r\n const color = shape.getColor();\r\n \r\n // uniform 数据:viewProj(64) + model(64) + color(16) = 144 bytes\r\n // 但需要 16 字节对齐,所以用 36 个 float\r\n const uniformData = new Float32Array(36);\r\n uniformData.set(viewProjectionMatrix.elements, 0);\r\n uniformData.set(modelMatrix.elements, 16);\r\n uniformData[32] = color.r;\r\n uniformData[33] = color.g;\r\n uniformData[34] = color.b;\r\n uniformData[35] = color.a;\r\n \r\n device.queue.writeBuffer(uniformBuffer, 0, uniformData);\r\n }\r\n \r\n render(pass: GPURenderPassEncoder): void {\r\n if (!this._target) return;\r\n if (!this.pipeline) return;\r\n \r\n this.updateScale();\r\n this._shapesLookAtCamera();\r\n \r\n pass.setPipeline(this.pipeline);\r\n \r\n for (const [axis, shape] of this._shapes) {\r\n if (!shape.visible) continue;\r\n this.updateUniformsForShape(axis, shape);\r\n }\r\n \r\n for (const [axis, shape] of this._shapes) {\r\n if (!shape.visible) continue;\r\n \r\n const vertexBuffer = shape.getVertexBuffer();\r\n const indexBuffer = shape.getIndexBuffer();\r\n const indexCount = shape.getIndexCount();\r\n const bindGroup = this.shapeBindGroups.get(axis);\r\n \r\n if (!vertexBuffer || !indexBuffer || indexCount === 0 || !bindGroup) {\r\n continue;\r\n }\r\n \r\n pass.setBindGroup(0, bindGroup);\r\n pass.setVertexBuffer(0, vertexBuffer);\r\n pass.setIndexBuffer(indexBuffer, \"uint16\");\r\n pass.drawIndexed(indexCount);\r\n }\r\n }\r\n\r\n destroy(): void {\r\n for (const shape of this._shapes.values()) {\r\n shape.destroy();\r\n }\r\n this._shapes.clear();\r\n \r\n for (const buffer of this.shapeUniformBuffers.values()) {\r\n buffer.destroy();\r\n }\r\n this.shapeUniformBuffers.clear();\r\n this.shapeBindGroups.clear();\r\n \r\n if (this.uniformBuffer) {\r\n this.uniformBuffer.destroy();\r\n this.uniformBuffer = null;\r\n }\r\n \r\n if (this.guideLineBuffer) {\r\n this.guideLineBuffer.destroy();\r\n this.guideLineBuffer = null;\r\n }\r\n \r\n this.pipeline = null;\r\n this.linePipeline = null;\r\n this.bindGroup = null;\r\n this.guideLineBindGroup = null;\r\n this.bindGroupLayout = null;\r\n }\r\n}\r\n","/**\r\n * GizmoManager - Gizmo 交互管理器\r\n * \r\n * 负责管理所有 Gizmo 相关的交互:\r\n * - TransformGizmo(变换控制)\r\n * - ViewportGizmo(视口坐标轴)\r\n * - BoundingBoxRenderer(选中对象包围盒)\r\n */\r\n\r\nimport { Renderer } from \"../core/Renderer\";\r\nimport { Camera } from \"../core/Camera\";\r\nimport { OrbitControls } from \"../core/OrbitControls\";\r\nimport { ViewportGizmo } from \"../core/ViewportGizmo\";\r\nimport { TransformGizmoV2, TransformableObject, GizmoMode } from \"../core/gizmo/TransformGizmoV2\";\r\nimport { BoundingBoxRenderer, BoundingBox as RendererBoundingBox, BoundingBoxProvider } from \"../core/BoundingBoxRenderer\";\r\nimport { IGSSplatRenderer, BoundingBox as GSBoundingBox } from \"../gs/IGSSplatRenderer\";\r\nimport { Mesh } from \"../mesh/Mesh\";\r\n\r\n/**\r\n * SplatTransformProxy - PLY/Splat 变换代理对象\r\n * 实现类似 Mesh 的接口,让 TransformGizmo 可以操作 PLY 模型\r\n */\r\nexport class SplatTransformProxy implements TransformableObject {\r\n position: [number, number, number];\r\n rotation: [number, number, number];\r\n scale: [number, number, number];\r\n\r\n private renderer: IGSSplatRenderer;\r\n private center: [number, number, number];\r\n\r\n constructor(renderer: IGSSplatRenderer, center: [number, number, number]) {\r\n this.renderer = renderer;\r\n this.center = [...center];\r\n\r\n // 设置渲染器的 pivot 为包围盒中心\r\n renderer.setPivot(center[0], center[1], center[2]);\r\n\r\n // 初始化为当前渲染器的变换状态\r\n const pos = renderer.getPosition();\r\n const rot = renderer.getRotation();\r\n const scl = renderer.getScale();\r\n\r\n // Gizmo 位置 = 渲染器位置 + 中心点\r\n this.position = [\r\n pos[0] + center[0],\r\n pos[1] + center[1],\r\n pos[2] + center[2],\r\n ];\r\n this.rotation = [...rot];\r\n this.scale = [...scl];\r\n }\r\n\r\n setPosition(x: number, y: number, z: number): void {\r\n this.position = [x, y, z];\r\n // 渲染器位置 = Gizmo 位置 - 中心点\r\n this.renderer.setPosition(\r\n x - this.center[0],\r\n y - this.center[1],\r\n z - this.center[2]\r\n );\r\n }\r\n\r\n setRotation(x: number, y: number, z: number): void {\r\n this.rotation = [x, y, z];\r\n this.renderer.setRotation(x, y, z);\r\n }\r\n\r\n setScale(x: number, y: number, z: number): void {\r\n this.scale = [x, y, z];\r\n this.renderer.setScale(x, y, z);\r\n }\r\n}\r\n\r\n/**\r\n * MeshGroupProxy - 多 Mesh 组变换代理对象\r\n * 让 TransformGizmo 可以同时操作多个 Mesh\r\n */\r\nexport class MeshGroupProxy implements TransformableObject {\r\n position: [number, number, number];\r\n rotation: [number, number, number];\r\n scale: [number, number, number];\r\n\r\n private meshes: Mesh[];\r\n\r\n constructor(meshes: Mesh[]) {\r\n this.meshes = meshes;\r\n\r\n if (meshes.length > 0) {\r\n const firstMesh = meshes[0];\r\n this.position = [\r\n firstMesh.position[0],\r\n firstMesh.position[1],\r\n firstMesh.position[2],\r\n ];\r\n this.rotation = [\r\n firstMesh.rotation[0],\r\n firstMesh.rotation[1],\r\n firstMesh.rotation[2],\r\n ];\r\n this.scale = [\r\n firstMesh.scale[0],\r\n firstMesh.scale[1],\r\n firstMesh.scale[2],\r\n ];\r\n } else {\r\n this.position = [0, 0, 0];\r\n this.rotation = [0, 0, 0];\r\n this.scale = [1, 1, 1];\r\n }\r\n }\r\n\r\n setPosition(x: number, y: number, z: number): void {\r\n this.position = [x, y, z];\r\n for (const mesh of this.meshes) {\r\n mesh.setPosition(x, y, z);\r\n }\r\n }\r\n\r\n setRotation(x: number, y: number, z: number): void {\r\n this.rotation = [x, y, z];\r\n for (const mesh of this.meshes) {\r\n mesh.setRotation(x, y, z);\r\n }\r\n }\r\n\r\n setScale(x: number, y: number, z: number): void {\r\n this.scale = [x, y, z];\r\n for (const mesh of this.meshes) {\r\n mesh.setScale(x, y, z);\r\n }\r\n }\r\n\r\n getBoundingBox(): RendererBoundingBox | null {\r\n if (this.meshes.length === 0) return null;\r\n\r\n let combinedMin: [number, number, number] | null = null;\r\n let combinedMax: [number, number, number] | null = null;\r\n\r\n for (const mesh of this.meshes) {\r\n const bbox = mesh.getWorldBoundingBox();\r\n if (!bbox) continue;\r\n\r\n if (combinedMin === null || combinedMax === null) {\r\n combinedMin = [...bbox.min];\r\n combinedMax = [...bbox.max];\r\n } else {\r\n combinedMin[0] = Math.min(combinedMin[0], bbox.min[0]);\r\n combinedMin[1] = Math.min(combinedMin[1], bbox.min[1]);\r\n combinedMin[2] = Math.min(combinedMin[2], bbox.min[2]);\r\n combinedMax[0] = Math.max(combinedMax[0], bbox.max[0]);\r\n combinedMax[1] = Math.max(combinedMax[1], bbox.max[1]);\r\n combinedMax[2] = Math.max(combinedMax[2], bbox.max[2]);\r\n }\r\n }\r\n\r\n if (combinedMin === null || combinedMax === null) return null;\r\n\r\n return { min: combinedMin, max: combinedMax };\r\n }\r\n}\r\n\r\n/**\r\n * SplatBoundingBoxProvider - PLY/Splat 包围盒提供者\r\n * 动态获取 PLY 的包围盒(考虑变换)\r\n */\r\nexport class SplatBoundingBoxProvider implements BoundingBoxProvider {\r\n private renderer: IGSSplatRenderer;\r\n\r\n constructor(renderer: IGSSplatRenderer) {\r\n this.renderer = renderer;\r\n }\r\n\r\n getBoundingBox(): RendererBoundingBox | null {\r\n const bbox = this.renderer.getBoundingBox();\r\n if (!bbox) return null;\r\n\r\n // 获取变换参数\r\n const position = this.renderer.getPosition();\r\n const rotation = this.renderer.getRotation();\r\n const scale = this.renderer.getScale();\r\n const pivot = this.renderer.getPivot();\r\n\r\n // 获取本地包围盒的 8 个角点\r\n const corners: [number, number, number][] = [\r\n [bbox.min[0], bbox.min[1], bbox.min[2]],\r\n [bbox.max[0], bbox.min[1], bbox.min[2]],\r\n [bbox.min[0], bbox.max[1], bbox.min[2]],\r\n [bbox.max[0], bbox.max[1], bbox.min[2]],\r\n [bbox.min[0], bbox.min[1], bbox.max[2]],\r\n [bbox.max[0], bbox.min[1], bbox.max[2]],\r\n [bbox.min[0], bbox.max[1], bbox.max[2]],\r\n [bbox.max[0], bbox.max[1], bbox.max[2]],\r\n ];\r\n\r\n // 计算变换矩阵\r\n const [sx, sy, sz] = scale;\r\n const [rx, ry, rz] = rotation;\r\n const [tx, ty, tz] = position;\r\n const [px, py, pz] = pivot;\r\n\r\n const cx = Math.cos(rx), sx1 = Math.sin(rx);\r\n const cy = Math.cos(ry), sy1 = Math.sin(ry);\r\n const cz = Math.cos(rz), sz1 = Math.sin(rz);\r\n\r\n // 组合旋转矩阵 R = Rz * Ry * Rx\r\n const r00 = cy * cz;\r\n const r01 = sx1 * sy1 * cz - cx * sz1;\r\n const r02 = cx * sy1 * cz + sx1 * sz1;\r\n const r10 = cy * sz1;\r\n const r11 = sx1 * sy1 * sz1 + cx * cz;\r\n const r12 = cx * sy1 * sz1 - sx1 * cz;\r\n const r20 = -sy1;\r\n const r21 = sx1 * cy;\r\n const r22 = cx * cy;\r\n\r\n // RS 矩阵 (旋转 * 缩放)\r\n const rs00 = r00 * sx, rs01 = r01 * sy, rs02 = r02 * sz;\r\n const rs10 = r10 * sx, rs11 = r11 * sy, rs12 = r12 * sz;\r\n const rs20 = r20 * sx, rs21 = r21 * sy, rs22 = r22 * sz;\r\n\r\n // 计算 (I - RS) * pivot\r\n const dpx = px - (rs00 * px + rs01 * py + rs02 * pz);\r\n const dpy = py - (rs10 * px + rs11 * py + rs12 * pz);\r\n const dpz = pz - (rs20 * px + rs21 * py + rs22 * pz);\r\n\r\n // 最终平移 = position + (I - RS) * pivot\r\n const finalTx = tx + dpx;\r\n const finalTy = ty + dpy;\r\n const finalTz = tz + dpz;\r\n\r\n // 变换所有角点\r\n let minX = Infinity, minY = Infinity, minZ = Infinity;\r\n let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;\r\n\r\n for (const [x, y, z] of corners) {\r\n const wx = rs00 * x + rs01 * y + rs02 * z + finalTx;\r\n const wy = rs10 * x + rs11 * y + rs12 * z + finalTy;\r\n const wz = rs20 * x + rs21 * y + rs22 * z + finalTz;\r\n\r\n minX = Math.min(minX, wx);\r\n minY = Math.min(minY, wy);\r\n minZ = Math.min(minZ, wz);\r\n maxX = Math.max(maxX, wx);\r\n maxY = Math.max(maxY, wy);\r\n maxZ = Math.max(maxZ, wz);\r\n }\r\n\r\n return {\r\n min: [minX, minY, minZ],\r\n max: [maxX, maxY, maxZ],\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * GizmoManager - Gizmo 交互管理器\r\n */\r\nexport class GizmoManager {\r\n private renderer: Renderer;\r\n private camera: Camera;\r\n private canvas: HTMLCanvasElement;\r\n private controls: OrbitControls;\r\n\r\n private viewportGizmo: ViewportGizmo;\r\n private transformGizmo: TransformGizmoV2;\r\n private boundingBoxRenderer: BoundingBoxRenderer;\r\n\r\n // 事件处理函数引用(用于移除监听器)\r\n private boundOnClick: (e: MouseEvent) => void;\r\n private boundOnPointerMove: (e: PointerEvent) => void;\r\n private boundOnPointerDown: (e: PointerEvent) => void;\r\n private boundOnPointerUp: (e: PointerEvent) => void;\r\n\r\n constructor(\r\n renderer: Renderer,\r\n camera: Camera,\r\n canvas: HTMLCanvasElement,\r\n controls: OrbitControls\r\n ) {\r\n this.renderer = renderer;\r\n this.camera = camera;\r\n this.canvas = canvas;\r\n this.controls = controls;\r\n\r\n // 初始化 Gizmo\r\n this.viewportGizmo = new ViewportGizmo(renderer, camera, canvas);\r\n this.transformGizmo = new TransformGizmoV2({ renderer, camera, canvas });\r\n this.transformGizmo.init();\r\n this.boundingBoxRenderer = new BoundingBoxRenderer(renderer, camera);\r\n\r\n // 设置 Gizmo 拖拽时禁用 OrbitControls\r\n this.transformGizmo.setOnDragStateChange((isDragging) => {\r\n this.controls.enabled = !isDragging;\r\n });\r\n\r\n // 绑定事件处理函数\r\n this.boundOnClick = this.onCanvasClick.bind(this);\r\n this.boundOnPointerMove = this.onPointerMove.bind(this);\r\n this.boundOnPointerDown = this.onPointerDown.bind(this);\r\n this.boundOnPointerUp = this.onPointerUp.bind(this);\r\n\r\n this.setupEventListeners();\r\n }\r\n\r\n /**\r\n * 设置事件监听器\r\n */\r\n private setupEventListeners(): void {\r\n // 设置视口 Gizmo 轴点击回调\r\n this.viewportGizmo.setOnAxisClick((axis, positive) => {\r\n this.controls.setViewAxis(axis, positive, true);\r\n });\r\n\r\n // 监听点击事件\r\n this.canvas.addEventListener(\"click\", this.boundOnClick);\r\n\r\n // 添加变换 Gizmo 的指针事件监听器\r\n this.canvas.addEventListener(\"pointermove\", this.boundOnPointerMove);\r\n this.canvas.addEventListener(\"pointerdown\", this.boundOnPointerDown);\r\n this.canvas.addEventListener(\"pointerup\", this.boundOnPointerUp);\r\n }\r\n\r\n private onCanvasClick(e: MouseEvent): void {\r\n this.viewportGizmo.handleClick(e.clientX, e.clientY);\r\n }\r\n\r\n private onPointerMove(e: PointerEvent): void {\r\n this.transformGizmo.onPointerMove(e);\r\n }\r\n\r\n private onPointerDown(e: PointerEvent): void {\r\n this.transformGizmo.onPointerDown(e);\r\n }\r\n\r\n private onPointerUp(e: PointerEvent): void {\r\n this.transformGizmo.onPointerUp(e);\r\n }\r\n\r\n // ============================================\r\n // 渲染\r\n // ============================================\r\n\r\n /**\r\n * 渲染所有 Gizmo\r\n */\r\n render(pass: GPURenderPassEncoder): void {\r\n // 渲染包围盒\r\n this.boundingBoxRenderer.render(pass);\r\n // 渲染变换 Gizmo\r\n this.transformGizmo.render(pass);\r\n // 渲染视口 Gizmo\r\n this.viewportGizmo.render(pass);\r\n }\r\n\r\n // ============================================\r\n // Transform Gizmo\r\n // ============================================\r\n\r\n /**\r\n * 获取变换 Gizmo\r\n */\r\n getTransformGizmo(): TransformGizmoV2 {\r\n return this.transformGizmo;\r\n }\r\n\r\n /**\r\n * 设置 Gizmo 模式\r\n */\r\n setGizmoMode(mode: GizmoMode): void {\r\n this.transformGizmo.mode = mode;\r\n }\r\n\r\n /**\r\n * 设置 Gizmo 目标对象\r\n */\r\n setGizmoTarget(object: TransformableObject | null): void {\r\n this.transformGizmo.setTarget(object);\r\n }\r\n\r\n // ============================================\r\n // Viewport Gizmo\r\n // ============================================\r\n\r\n /**\r\n * 获取视口 Gizmo\r\n */\r\n getViewportGizmo(): ViewportGizmo {\r\n return this.viewportGizmo;\r\n }\r\n\r\n // ============================================\r\n // Bounding Box\r\n // ============================================\r\n\r\n /**\r\n * 获取包围盒渲染器\r\n */\r\n getBoundingBoxRenderer(): BoundingBoxRenderer {\r\n return this.boundingBoxRenderer;\r\n }\r\n\r\n /**\r\n * 设置选中对象的包围盒(静态模式)\r\n */\r\n setSelectionBoundingBox(box: RendererBoundingBox | null): void {\r\n this.boundingBoxRenderer.setBoundingBox(box);\r\n }\r\n\r\n /**\r\n * 设置选中对象的包围盒提供者(动态模式)\r\n */\r\n setSelectionBoundingBoxProvider(provider: BoundingBoxProvider | null): void {\r\n this.boundingBoxRenderer.setProvider(provider);\r\n }\r\n\r\n /**\r\n * 清除选中对象的包围盒\r\n */\r\n clearSelectionBoundingBox(): void {\r\n this.boundingBoxRenderer.clear();\r\n }\r\n\r\n // ============================================\r\n // 代理对象创建\r\n // ============================================\r\n\r\n /**\r\n * 创建 Splat 变换代理\r\n */\r\n createSplatTransformProxy(renderer: IGSSplatRenderer): SplatTransformProxy | null {\r\n const bbox = renderer.getBoundingBox();\r\n if (!bbox) return null;\r\n return new SplatTransformProxy(renderer, bbox.center);\r\n }\r\n\r\n /**\r\n * 创建 Mesh 组变换代理\r\n */\r\n createMeshGroupProxy(meshes: Mesh[]): MeshGroupProxy | null {\r\n if (meshes.length === 0) return null;\r\n return new MeshGroupProxy(meshes);\r\n }\r\n\r\n /**\r\n * 创建 Splat 包围盒提供者\r\n */\r\n createSplatBoundingBoxProvider(renderer: IGSSplatRenderer): SplatBoundingBoxProvider {\r\n return new SplatBoundingBoxProvider(renderer);\r\n }\r\n\r\n /**\r\n * 销毁\r\n */\r\n destroy(): void {\r\n // 移除事件监听器\r\n this.canvas.removeEventListener(\"click\", this.boundOnClick);\r\n this.canvas.removeEventListener(\"pointermove\", this.boundOnPointerMove);\r\n this.canvas.removeEventListener(\"pointerdown\", this.boundOnPointerDown);\r\n this.canvas.removeEventListener(\"pointerup\", this.boundOnPointerUp);\r\n\r\n // 销毁 Gizmo\r\n this.transformGizmo.destroy();\r\n this.boundingBoxRenderer.destroy();\r\n }\r\n}\r\n","/**\r\n * App - 统一调度入口\r\n * \r\n * 重构后的职责:\r\n * - 初始化和协调各子系统\r\n * - 渲染循环管理\r\n * - 模型加载(委托给加载器)\r\n * - 对外提供简洁的 API\r\n * \r\n * 场景管理委托给 SceneManager\r\n * Gizmo 交互委托给 GizmoManager\r\n */\r\n\r\nimport { Renderer } from \"./core/Renderer\";\r\nimport { Camera } from \"./core/Camera\";\r\nimport { OrbitControls } from \"./core/OrbitControls\";\r\nimport { MeshRenderer } from \"./mesh/MeshRenderer\";\r\nimport { GLBLoader } from \"./loaders/GLBLoader\";\r\nimport { OBJLoader } from \"./loaders/OBJLoader\";\r\nimport { Mesh } from \"./mesh/Mesh\";\r\nimport { GSSplatRenderer, PerformanceTier } from \"./gs/GSSplatRenderer\";\r\nimport { GSSplatRendererMobile } from \"./gs/GSSplatRendererMobile\";\r\nimport { IGSSplatRenderer, BoundingBox } from \"./gs/IGSSplatRenderer\";\r\nimport { deserializeSplat } from \"./gs/SplatLoader\";\r\nimport { SceneManager } from \"./scene/SceneManager\";\r\nimport { \r\n GizmoManager, \r\n SplatTransformProxy, \r\n MeshGroupProxy, \r\n SplatBoundingBoxProvider \r\n} from \"./interaction/GizmoManager\";\r\nimport { TransformableObject, GizmoMode } from \"./core/gizmo/TransformGizmoV2\";\r\nimport { BoundingBoxProvider } from \"./core/BoundingBoxRenderer\";\r\n\r\n// 重新导出代理类以保持向后兼容\r\nexport { SplatTransformProxy, MeshGroupProxy, SplatBoundingBoxProvider };\r\n\r\n/**\r\n * 统一进度回调类型\r\n * @param progress 进度值 0-100\r\n * @param stage 当前阶段: 'download' | 'parse' | 'upload'\r\n */\r\nexport type ProgressCallback = (progress: number, stage: 'download' | 'parse' | 'upload') => void;\r\n\r\n/**\r\n * 检测是否为移动设备\r\n */\r\nfunction isMobileDevice(): boolean {\r\n if (typeof navigator === \"undefined\") return false;\r\n const ua = navigator.userAgent || navigator.vendor || (window as any).opera || \"\";\r\n const isMobileUA = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(ua.toLowerCase());\r\n const hasTouch = \"ontouchstart\" in window || navigator.maxTouchPoints > 0;\r\n const isSmallScreen = window.innerWidth <= 768;\r\n const isIPadAsMac = navigator.platform === \"MacIntel\" && navigator.maxTouchPoints > 1;\r\n return isMobileUA || isIPadAsMac || (hasTouch && isSmallScreen);\r\n}\r\n\r\n/**\r\n * App - 统一调度入口\r\n */\r\nexport class App {\r\n private canvas: HTMLCanvasElement;\r\n private renderer!: Renderer;\r\n private camera!: Camera;\r\n private controls!: OrbitControls;\r\n private meshRenderer!: MeshRenderer;\r\n private glbLoader!: GLBLoader;\r\n private objLoader!: OBJLoader;\r\n\r\n // 子系统管理器\r\n private sceneManager!: SceneManager;\r\n private gizmoManager!: GizmoManager;\r\n\r\n private isRunning: boolean = false;\r\n private animationId: number = 0;\r\n\r\n // 是否使用移动端渲染器\r\n private useMobileRenderer: boolean = false;\r\n\r\n // 绑定的事件处理函数\r\n private boundOnResize: () => void;\r\n\r\n constructor(canvas: HTMLCanvasElement) {\r\n this.canvas = canvas;\r\n this.boundOnResize = this.onResize.bind(this);\r\n }\r\n\r\n /**\r\n * 初始化应用\r\n */\r\n async init(): Promise<void> {\r\n // 初始化渲染器\r\n this.renderer = new Renderer(this.canvas);\r\n await this.renderer.init();\r\n\r\n // 初始化相机\r\n this.camera = new Camera();\r\n this.camera.setAspect(this.renderer.getAspectRatio());\r\n\r\n // 初始化控制器\r\n this.controls = new OrbitControls(this.camera, this.canvas);\r\n\r\n // 初始化网格渲染器\r\n this.meshRenderer = new MeshRenderer(this.renderer, this.camera);\r\n\r\n // 初始化加载器\r\n this.glbLoader = new GLBLoader(this.renderer.device);\r\n this.objLoader = new OBJLoader(this.renderer.device);\r\n\r\n // 初始化场景管理器\r\n this.sceneManager = new SceneManager(this.meshRenderer);\r\n\r\n // 初始化 Gizmo 管理器\r\n this.gizmoManager = new GizmoManager(\r\n this.renderer,\r\n this.camera,\r\n this.canvas,\r\n this.controls\r\n );\r\n\r\n // 监听窗口大小变化\r\n window.addEventListener(\"resize\", this.boundOnResize);\r\n }\r\n\r\n // ============================================\r\n // 模型加载\r\n // ============================================\r\n\r\n /**\r\n * 加载 GLB 文件\r\n */\r\n async addGLB(url: string): Promise<number> {\r\n try {\r\n const loadedMeshes = await this.glbLoader.load(url);\r\n for (const { mesh, material } of loadedMeshes) {\r\n this.meshRenderer.addMesh(mesh, material);\r\n }\r\n return loadedMeshes.length;\r\n } catch (error) {\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * 加载 OBJ 文件\r\n */\r\n async addOBJ(url: string): Promise<Mesh[]> {\r\n try {\r\n const loadedMeshes = await this.objLoader.load(url);\r\n const meshes: Mesh[] = [];\r\n for (const { mesh, material } of loadedMeshes) {\r\n this.meshRenderer.addMesh(mesh, material);\r\n meshes.push(mesh);\r\n }\r\n return meshes;\r\n } catch (error) {\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * 加载 PLY 文件 (3D Gaussian Splatting)\r\n */\r\n async addPLY(\r\n urlOrBuffer: string | ArrayBuffer,\r\n onProgress?: ProgressCallback,\r\n isLocalFile: boolean = false,\r\n ): Promise<number> {\r\n try {\r\n const isMobile = isMobileDevice();\r\n let buffer: ArrayBuffer;\r\n\r\n // 下载阶段 (0-50%)\r\n if (typeof urlOrBuffer === 'string') {\r\n buffer = await this.fetchWithProgress(urlOrBuffer, (downloadProgress) => {\r\n if (onProgress) {\r\n onProgress(downloadProgress * 0.5, 'download');\r\n }\r\n });\r\n } else {\r\n buffer = urlOrBuffer;\r\n if (onProgress && isLocalFile) {\r\n onProgress(50, 'download');\r\n }\r\n }\r\n\r\n // 解析阶段 (50-90%)\r\n const parseProgressCallback = (loaded: number, total: number) => {\r\n if (onProgress) {\r\n const parseProgress = (loaded / total) * 40;\r\n onProgress(50 + parseProgress, 'parse');\r\n }\r\n };\r\n\r\n let gsRenderer: IGSSplatRenderer;\r\n\r\n if (isMobile) {\r\n gsRenderer = new GSSplatRendererMobile(this.renderer, this.camera);\r\n this.useMobileRenderer = true;\r\n\r\n const compactData = await this.parsePLYBuffer(buffer, {\r\n maxSplats: Infinity,\r\n loadSH: false,\r\n onProgress: parseProgressCallback,\r\n });\r\n\r\n if (onProgress) onProgress(90, 'upload');\r\n gsRenderer.setCompactData(compactData);\r\n if (onProgress) onProgress(100, 'upload');\r\n\r\n this.sceneManager.setGSRenderer(gsRenderer);\r\n return compactData.count;\r\n } else {\r\n gsRenderer = new GSSplatRenderer(this.renderer, this.camera);\r\n this.useMobileRenderer = false;\r\n\r\n const tier = (gsRenderer as GSSplatRenderer).getPerformanceTier();\r\n\r\n const compactData = await this.parsePLYBuffer(buffer, {\r\n maxSplats: Infinity,\r\n loadSH: true,\r\n onProgress: parseProgressCallback,\r\n });\r\n\r\n if (onProgress) onProgress(90, 'upload');\r\n gsRenderer.setCompactData(compactData);\r\n if (onProgress) onProgress(100, 'upload');\r\n\r\n this.sceneManager.setGSRenderer(gsRenderer);\r\n return compactData.count;\r\n }\r\n } catch (error) {\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * 加载 Splat 文件\r\n */\r\n async addSplat(\r\n urlOrBuffer: string | ArrayBuffer,\r\n onProgress?: ProgressCallback,\r\n isLocalFile: boolean = false,\r\n ): Promise<number> {\r\n try {\r\n let buffer: ArrayBuffer;\r\n\r\n if (typeof urlOrBuffer === 'string') {\r\n buffer = await this.fetchWithProgress(urlOrBuffer, (downloadProgress) => {\r\n if (onProgress) {\r\n onProgress(downloadProgress * 0.5, 'download');\r\n }\r\n });\r\n } else {\r\n buffer = urlOrBuffer;\r\n if (onProgress && isLocalFile) {\r\n onProgress(50, 'download');\r\n }\r\n }\r\n\r\n if (onProgress) onProgress(50, 'parse');\r\n const splats = deserializeSplat(buffer);\r\n if (onProgress) onProgress(90, 'parse');\r\n\r\n if (onProgress) onProgress(90, 'upload');\r\n const gsRenderer = new GSSplatRenderer(this.renderer, this.camera);\r\n gsRenderer.setData(splats);\r\n this.sceneManager.setGSRenderer(gsRenderer);\r\n this.useMobileRenderer = false;\r\n if (onProgress) onProgress(100, 'upload');\r\n\r\n return splats.length;\r\n } catch (error) {\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * 添加测试立方体\r\n */\r\n addTestCube(): void {\r\n const { mesh, material } = this.glbLoader.createTestCube();\r\n this.meshRenderer.addMesh(mesh, material);\r\n }\r\n\r\n /**\r\n * 添加测试球体\r\n */\r\n addTestSphere(): void {\r\n const { mesh, material } = this.glbLoader.createTestSphere();\r\n this.meshRenderer.addMesh(mesh, material);\r\n }\r\n\r\n // ============================================\r\n // 渲染循环\r\n // ============================================\r\n\r\n /**\r\n * 开始渲染循环\r\n */\r\n start(): void {\r\n if (this.isRunning) return;\r\n this.isRunning = true;\r\n this.animate();\r\n }\r\n\r\n /**\r\n * 停止渲染循环\r\n */\r\n stop(): void {\r\n this.isRunning = false;\r\n if (this.animationId) {\r\n cancelAnimationFrame(this.animationId);\r\n this.animationId = 0;\r\n }\r\n }\r\n\r\n private animate(): void {\r\n if (!this.isRunning) return;\r\n this.render();\r\n this.animationId = requestAnimationFrame(this.animate.bind(this));\r\n }\r\n\r\n private render(): void {\r\n this.camera.setAspect(this.renderer.getAspectRatio());\r\n this.camera.updateMatrix();\r\n\r\n const pass = this.renderer.beginFrame();\r\n\r\n // 渲染 3D Gaussian Splatting\r\n const gsRenderer = this.sceneManager.getGSRenderer();\r\n if (gsRenderer) {\r\n gsRenderer.render(pass);\r\n }\r\n\r\n // 渲染网格\r\n this.meshRenderer.render(pass);\r\n\r\n // 渲染 Gizmo\r\n this.gizmoManager.render(pass);\r\n\r\n this.renderer.endFrame();\r\n }\r\n\r\n private onResize(): void {\r\n this.camera.setAspect(this.renderer.getAspectRatio());\r\n this.camera.updateMatrix();\r\n }\r\n\r\n // ============================================\r\n // 场景管理(委托给 SceneManager)\r\n // ============================================\r\n\r\n getMeshCount(): number {\r\n return this.sceneManager.getMeshCount();\r\n }\r\n\r\n getMeshByIndex(index: number): Mesh | null {\r\n return this.sceneManager.getMeshByIndex(index);\r\n }\r\n\r\n getMeshRange(startIndex: number, count: number): Mesh[] {\r\n return this.sceneManager.getMeshRange(startIndex, count);\r\n }\r\n\r\n clearMeshes(): void {\r\n this.sceneManager.clearMeshes();\r\n }\r\n\r\n removeMeshByIndex(index: number): boolean {\r\n const result = this.sceneManager.removeMeshByIndex(index);\r\n return result;\r\n }\r\n\r\n getSplatCount(): number {\r\n return this.sceneManager.getSplatCount();\r\n }\r\n\r\n clearSplats(): void {\r\n this.sceneManager.clearSplats();\r\n this.useMobileRenderer = false;\r\n }\r\n\r\n // ============================================\r\n // Splat 变换(委托给 SceneManager)\r\n // ============================================\r\n\r\n setSplatPosition(x: number, y: number, z: number): void {\r\n this.sceneManager.setSplatPosition(x, y, z);\r\n }\r\n\r\n getSplatPosition(): [number, number, number] | null {\r\n return this.sceneManager.getSplatPosition();\r\n }\r\n\r\n setSplatRotation(x: number, y: number, z: number): void {\r\n this.sceneManager.setSplatRotation(x, y, z);\r\n }\r\n\r\n getSplatRotation(): [number, number, number] | null {\r\n return this.sceneManager.getSplatRotation();\r\n }\r\n\r\n setSplatScale(x: number, y: number, z: number): void {\r\n this.sceneManager.setSplatScale(x, y, z);\r\n }\r\n\r\n getSplatScale(): [number, number, number] | null {\r\n return this.sceneManager.getSplatScale();\r\n }\r\n\r\n // ============================================\r\n // SH 模式\r\n // ============================================\r\n\r\n setSHMode(mode: 0 | 1 | 2 | 3): void {\r\n this.sceneManager.setSHMode(mode);\r\n }\r\n\r\n getSHMode(): number {\r\n return this.sceneManager.getSHMode();\r\n }\r\n\r\n // ============================================\r\n // Bounding Box\r\n // ============================================\r\n\r\n getSplatBoundingBox(): BoundingBox | null {\r\n return this.sceneManager.getSplatBoundingBox();\r\n }\r\n\r\n getMeshRangeBoundingBox(startIndex: number, count: number): BoundingBox | null {\r\n return this.sceneManager.getMeshRangeBoundingBox(startIndex, count);\r\n }\r\n\r\n // ============================================\r\n // 材质颜色\r\n // ============================================\r\n\r\n getMeshColor(index: number): [number, number, number, number] | null {\r\n return this.sceneManager.getMeshColor(index);\r\n }\r\n\r\n setMeshColor(index: number, r: number, g: number, b: number, a: number = 1): boolean {\r\n return this.sceneManager.setMeshColor(index, r, g, b, a);\r\n }\r\n\r\n setMeshRangeColor(startIndex: number, count: number, r: number, g: number, b: number, a: number = 1): number {\r\n return this.sceneManager.setMeshRangeColor(startIndex, count, r, g, b, a);\r\n }\r\n\r\n // ============================================\r\n // 相机控制\r\n // ============================================\r\n\r\n frameCurrentModel(animate: boolean = true): boolean {\r\n const bbox = this.sceneManager.getSceneBoundingBox();\r\n if (!bbox) {\r\n return false;\r\n }\r\n\r\n this.controls.frameModel(bbox.center, bbox.radius, animate);\r\n return true;\r\n }\r\n\r\n // ============================================\r\n // Gizmo(委托给 GizmoManager)\r\n // ============================================\r\n\r\n getTransformGizmo() {\r\n return this.gizmoManager.getTransformGizmo();\r\n }\r\n\r\n getViewportGizmo() {\r\n return this.gizmoManager.getViewportGizmo();\r\n }\r\n\r\n getBoundingBoxRenderer() {\r\n return this.gizmoManager.getBoundingBoxRenderer();\r\n }\r\n\r\n setGizmoMode(mode: GizmoMode): void {\r\n this.gizmoManager.setGizmoMode(mode);\r\n }\r\n\r\n setGizmoTarget(object: TransformableObject | null): void {\r\n this.gizmoManager.setGizmoTarget(object);\r\n }\r\n\r\n setSelectionBoundingBox(box: BoundingBox | null): void {\r\n this.gizmoManager.setSelectionBoundingBox(box);\r\n }\r\n\r\n setSelectionBoundingBoxProvider(provider: BoundingBoxProvider | null): void {\r\n this.gizmoManager.setSelectionBoundingBoxProvider(provider);\r\n }\r\n\r\n clearSelectionBoundingBox(): void {\r\n this.gizmoManager.clearSelectionBoundingBox();\r\n }\r\n\r\n /**\r\n * 创建 Mesh 组的变换代理\r\n */\r\n createMeshGroupProxy(startIndex: number, count: number): MeshGroupProxy | null {\r\n const meshes = this.sceneManager.getMeshRange(startIndex, count);\r\n return this.gizmoManager.createMeshGroupProxy(meshes);\r\n }\r\n\r\n /**\r\n * 获取 Splat 的变换代理\r\n */\r\n getSplatTransformProxy(): SplatTransformProxy | null {\r\n const gsRenderer = this.sceneManager.getGSRenderer();\r\n if (!gsRenderer) return null;\r\n return this.gizmoManager.createSplatTransformProxy(gsRenderer);\r\n }\r\n\r\n /**\r\n * 创建 Splat 包围盒提供者\r\n */\r\n createSplatBoundingBoxProvider(): SplatBoundingBoxProvider | null {\r\n const gsRenderer = this.sceneManager.getGSRenderer();\r\n if (!gsRenderer) return null;\r\n return this.gizmoManager.createSplatBoundingBoxProvider(gsRenderer);\r\n }\r\n\r\n // ============================================\r\n // 子系统访问\r\n // ============================================\r\n\r\n getRenderer(): Renderer {\r\n return this.renderer;\r\n }\r\n\r\n getCamera(): Camera {\r\n return this.camera;\r\n }\r\n\r\n getControls(): OrbitControls {\r\n return this.controls;\r\n }\r\n\r\n getMeshRenderer(): MeshRenderer {\r\n return this.meshRenderer;\r\n }\r\n\r\n getGSRenderer(): GSSplatRenderer | undefined {\r\n const renderer = this.sceneManager.getGSRenderer();\r\n if (renderer && !this.useMobileRenderer) {\r\n return renderer as GSSplatRenderer;\r\n }\r\n return undefined;\r\n }\r\n\r\n getGSRendererMobile(): GSSplatRendererMobile | undefined {\r\n const renderer = this.sceneManager.getGSRenderer();\r\n if (renderer && this.useMobileRenderer) {\r\n return renderer as GSSplatRendererMobile;\r\n }\r\n return undefined;\r\n }\r\n\r\n isUsingMobileRenderer(): boolean {\r\n return this.useMobileRenderer;\r\n }\r\n\r\n // ============================================\r\n // 内部方法\r\n // ============================================\r\n\r\n private async fetchWithProgress(\r\n url: string,\r\n onProgress?: (progress: number) => void\r\n ): Promise<ArrayBuffer> {\r\n const response = await fetch(url);\r\n if (!response.ok) {\r\n throw new Error(`无法加载文件: ${url}`);\r\n }\r\n\r\n const contentLength = response.headers.get('content-length');\r\n if (!contentLength || !response.body) {\r\n const buffer = await response.arrayBuffer();\r\n if (onProgress) onProgress(100);\r\n return buffer;\r\n }\r\n\r\n const total = parseInt(contentLength, 10);\r\n const reader = response.body.getReader();\r\n const chunks: Uint8Array[] = [];\r\n let loaded = 0;\r\n\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n\r\n chunks.push(value);\r\n loaded += value.length;\r\n\r\n if (onProgress) {\r\n onProgress((loaded / total) * 100);\r\n }\r\n }\r\n\r\n const buffer = new ArrayBuffer(loaded);\r\n const view = new Uint8Array(buffer);\r\n let offset = 0;\r\n for (const chunk of chunks) {\r\n view.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n\r\n return buffer;\r\n }\r\n\r\n private async parsePLYBuffer(\r\n buffer: ArrayBuffer,\r\n options: { maxSplats?: number; loadSH?: boolean; onProgress?: (loaded: number, total: number) => void }\r\n ): Promise<import('./gs/PLYLoaderMobile').CompactSplatData> {\r\n const { parsePLYBuffer } = await import('./gs/PLYLoaderMobile');\r\n return parsePLYBuffer(buffer, options);\r\n }\r\n\r\n /**\r\n * 销毁应用及所有资源\r\n */\r\n destroy(): void {\r\n this.stop();\r\n window.removeEventListener(\"resize\", this.boundOnResize);\r\n\r\n this.sceneManager.destroy();\r\n this.gizmoManager.destroy();\r\n\r\n if (this.meshRenderer) {\r\n this.meshRenderer.destroy();\r\n }\r\n\r\n if (this.controls) {\r\n this.controls.destroy();\r\n }\r\n\r\n if (this.renderer) {\r\n this.renderer.destroy();\r\n }\r\n }\r\n}\r\n"],"names":["TYPE_SIZES","parseHeader","validatePLYMagic","extractHeader","readProperty","sigmoid","SH_C0","DEFAULT_NUM_BUCKETS","IOS_NUM_BUCKETS","WORKGROUP_SIZE","isIOSDevice","generateCullingShaderCode","generatePrefixSumShaderCode","generateScatterShaderCode","SHMode","isMobileDevice","PerformanceTier","module","ISHMode","GizmoMode","plane","dist","buffer","parsePLYBuffer"],"mappings":"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CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE;AAG3D,YAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;AAC3D,YAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;AAC3D,YAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE;AAG3D,YAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;AAC3D,YAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;AAC3D,YAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE;AAG3D,YAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;AAC3D,YAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;AAC3D,YAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE;AAG3D,YAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;AAC3D,YAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4KhC,MAAM;AAAA;AAAA,EAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsMhC,MAAM;AAAA;AAAA,EAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoNhC,MAAM;AAAA;AAAA,EAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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"}
|