P3-02 P5-02: integrate Bitbybit OCCT geometry runtime

This commit is contained in:
2026-08-02 06:45:49 -04:00
parent feb2eb8be6
commit 9f2d11e151
14 changed files with 454 additions and 12 deletions

View File

@@ -1,5 +1,5 @@
import * as THREE from 'three'
import type { BitBybitViewportAdapter } from './types'
import type { BitBybitViewportAdapter, MeshAsset } from './types'
/** Internal renderer boundary. React receives only the adapter contract. */
export class ThreeViewportAdapter implements BitBybitViewportAdapter {
@@ -20,6 +20,7 @@ export class ThreeViewportAdapter implements BitBybitViewportAdapter {
this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false, powerPreference: 'high-performance' })
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2))
this.renderer.setSize(host.clientWidth || 1, host.clientHeight || 1, false)
this.renderer.domElement.dataset.geometrySource = 'three-fallback'
host.appendChild(this.renderer.domElement)
const grid = new THREE.GridHelper(8, 16, 0x31555a, 0x1e3439)
grid.rotation.x = 0
@@ -35,7 +36,6 @@ export class ThreeViewportAdapter implements BitBybitViewportAdapter {
this.scene.add(key)
const render = () => {
if (!this.renderer || !this.scene || !this.camera) return
this.selection && (this.selection.rotation.y += 0.002)
this.renderer.render(this.scene, this.camera)
this.frame = requestAnimationFrame(render)
}
@@ -43,6 +43,32 @@ export class ThreeViewportAdapter implements BitBybitViewportAdapter {
render()
}
setMesh(mesh: MeshAsset | null) {
if (!this.selection) return
if (!mesh) {
this.selection.visible = false
if (this.renderer) {
this.renderer.domElement.dataset.geometrySource = 'none'
delete this.renderer.domElement.dataset.triangleCount
}
return
}
const geometry = new THREE.BufferGeometry()
geometry.setAttribute('position', new THREE.BufferAttribute(mesh.positions, 3))
geometry.setAttribute('normal', new THREE.BufferAttribute(mesh.normals, 3))
geometry.setIndex(new THREE.BufferAttribute(mesh.indices, 1))
if (!mesh.normals.some((component) => component !== 0)) geometry.computeVertexNormals()
geometry.computeBoundingSphere()
this.selection.geometry.dispose()
this.selection.geometry = geometry
this.selection.visible = true
this.selection.position.set(0, -mesh.bounds.min[1], 0)
if (this.renderer) {
this.renderer.domElement.dataset.geometrySource = 'bitbybit-occt'
this.renderer.domElement.dataset.triangleCount = String(mesh.indices.length / 3)
}
}
resize(width: number, height: number, devicePixelRatio = Math.min(window.devicePixelRatio || 1, 2)) {
if (!this.renderer || !this.camera) return
this.camera.aspect = Math.max(width, 1) / Math.max(height, 1)