Skip to content

Commit b2596dc

Browse files
committed
Batched Mesh For Chrome Versions < 139
1 parent 3e4328d commit b2596dc

File tree

2 files changed

+135
-5
lines changed

2 files changed

+135
-5
lines changed

fission/src/mirabuf/MirabufInstance.ts

Lines changed: 129 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,23 @@ import { ParseErrorSeverity } from "./MirabufParser.ts"
88
type MirabufPartInstanceGUID = string
99

1010
const WIREFRAME = false
11+
const CHROME_VERSION_FOR_INSTANCED_MESH = 139
12+
13+
const detectInstancedMeshSupport = (): boolean => {
14+
const userAgent = navigator.userAgent
15+
const chromeMatch = userAgent.match(/Chrome\/(\d+)/)
16+
17+
if (chromeMatch) {
18+
const chromeVersion = parseInt(chromeMatch[1], 10)
19+
console.log(`Detected Chrome ${chromeVersion}, using ${chromeVersion >= CHROME_VERSION_FOR_INSTANCED_MESH ? 'InstancedMesh' : 'BatchedMesh'}`)
20+
return chromeVersion >= CHROME_VERSION_FOR_INSTANCED_MESH
21+
}
22+
23+
console.log(`Non-Chrome browser detected (${userAgent}), using BatchedMesh`)
24+
return false
25+
}
26+
27+
const USE_INSTANCED_MESH = detectInstancedMeshSupport()
1128

1229
export enum MaterialStyle {
1330
REGULAR = 0,
@@ -93,8 +110,8 @@ const transformGeometry = (geometry: THREE.BufferGeometry, mesh: mirabuf.IMesh)
93110
class MirabufInstance {
94111
private _mirabufParser: MirabufParser
95112
private _materials: Map<string, THREE.Material>
96-
private _meshes: Map<MirabufPartInstanceGUID, Array<[THREE.InstancedMesh, number]>>
97-
private _batches: Array<THREE.InstancedMesh>
113+
private _meshes: Map<MirabufPartInstanceGUID, Array<[THREE.InstancedMesh | THREE.BatchedMesh, number]>>
114+
private _batches: Array<THREE.InstancedMesh | THREE.BatchedMesh>
98115

99116
public get parser() {
100117
return this._mirabufParser
@@ -160,6 +177,17 @@ class MirabufInstance {
160177
* Creates ThreeJS meshes from the parsed mirabuf file.
161178
*/
162179
private createMeshes() {
180+
if (USE_INSTANCED_MESH) {
181+
this.createInstancedMeshes()
182+
} else {
183+
this.createBatchedMeshes()
184+
}
185+
}
186+
187+
/**
188+
* Creates InstancedMesh objects, as newer version of Chrome break with BatchedMesh
189+
*/
190+
private createInstancedMeshes() {
163191
const assembly = this._mirabufParser.assembly
164192
const instances = assembly.data!.parts!.partInstances!
165193

@@ -202,6 +230,105 @@ class MirabufInstance {
202230
})
203231
}
204232

233+
/**
234+
* Creates BatchedMesh, more efficient, but broken in newer versions of Chrome
235+
*/
236+
private createBatchedMeshes() {
237+
const assembly = this._mirabufParser.assembly
238+
const instances = assembly.data!.parts!.partInstances!
239+
240+
interface BatchCounts {
241+
maxInstances: number
242+
maxVertices: number
243+
maxIndices: number
244+
}
245+
246+
const batchMap = new Map<THREE.Material, Map<string, [mirabuf.IBody, Array<mirabuf.IPartInstance>]>>()
247+
const countMap = new Map<THREE.Material, BatchCounts>()
248+
249+
// Filter all instances by first material, then body
250+
Object.values(instances).forEach(instance => {
251+
const definition = assembly.data!.parts!.partDefinitions![instance.partDefinitionReference!]
252+
const bodies = definition?.bodies ?? []
253+
bodies.forEach(body => {
254+
const mesh = body?.triangleMesh?.mesh
255+
if (!mesh?.verts || !mesh.normals || !mesh.uv || !mesh.indices) return
256+
257+
const appearanceOverride = body.appearanceOverride
258+
const material = WIREFRAME
259+
? new THREE.MeshStandardMaterial({ wireframe: true, color: 0x000000 })
260+
: appearanceOverride && this._materials.has(appearanceOverride)
261+
? this._materials.get(appearanceOverride)!
262+
: fillerMaterials[nextFillerMaterial++ % fillerMaterials.length]
263+
264+
let materialBodyMap = batchMap.get(material)
265+
if (!materialBodyMap) {
266+
materialBodyMap = new Map<string, [mirabuf.IBody, Array<mirabuf.IPartInstance>]>()
267+
batchMap.set(material, materialBodyMap)
268+
}
269+
270+
const partBodyGuid = this.getPartBodyGuid(definition, body)
271+
let bodyInstances = materialBodyMap.get(partBodyGuid)
272+
if (!bodyInstances) {
273+
bodyInstances = [body, []]
274+
materialBodyMap.set(partBodyGuid, bodyInstances)
275+
}
276+
bodyInstances[1].push(instance)
277+
278+
if (countMap.has(material)) {
279+
const count = countMap.get(material)!
280+
count.maxInstances += 1
281+
count.maxVertices += mesh.verts.length / 3
282+
count.maxIndices += mesh.indices.length
283+
return
284+
}
285+
286+
const count: BatchCounts = {
287+
maxInstances: 1,
288+
maxVertices: mesh.verts.length / 3,
289+
maxIndices: mesh.indices.length,
290+
}
291+
countMap.set(material, count)
292+
})
293+
})
294+
295+
// Construct batched meshes
296+
batchMap.forEach((materialBodyMap, material) => {
297+
const count = countMap.get(material)!
298+
const batchedMesh = new THREE.BatchedMesh(count.maxInstances, count.maxVertices, count.maxIndices)
299+
this._batches.push(batchedMesh)
300+
301+
batchedMesh.material = material
302+
batchedMesh.castShadow = true
303+
batchedMesh.receiveShadow = true
304+
305+
materialBodyMap.forEach(instances => {
306+
const body = instances[0]
307+
instances[1].forEach(instance => {
308+
const mat = this._mirabufParser.globalTransforms.get(instance.info!.GUID!)!
309+
310+
const geometry = new THREE.BufferGeometry()
311+
transformGeometry(geometry, body.triangleMesh!.mesh!)
312+
const geoId = batchedMesh.addGeometry(geometry)
313+
const instanceId = batchedMesh.addInstance(geoId)
314+
batchedMesh.setMatrixAt(instanceId, mat)
315+
316+
let bodies = this._meshes.get(instance.info!.GUID!)
317+
if (!bodies) {
318+
bodies = []
319+
this._meshes.set(instance.info!.GUID!, bodies)
320+
}
321+
322+
bodies.push([batchedMesh, geoId])
323+
})
324+
})
325+
})
326+
}
327+
328+
private getPartBodyGuid(partDef: mirabuf.IPartDefinition, body: mirabuf.IPartDefinition) {
329+
return `${partDef.info!.GUID!}_BODY_${body.info!.GUID!}`
330+
}
331+
205332
/**
206333
* Adds all the meshes to the ThreeJs scene.
207334
*

fission/src/mirabuf/MirabufSceneObject.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -471,9 +471,12 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier {
471471
.clone()
472472
.premultiply(transform)
473473
const meshes = this._mirabufInstance.meshes.get(part) ?? []
474-
meshes.forEach(([instancedMesh, instanceIndex]) => {
475-
instancedMesh.setMatrixAt(instanceIndex, partTransform)
476-
instancedMesh.instanceMatrix.needsUpdate = true
474+
meshes.forEach(([mesh, index]) => {
475+
mesh.setMatrixAt(index, partTransform)
476+
// Only update instanceMatrix for InstancedMesh
477+
if ('instanceMatrix' in mesh) {
478+
mesh.instanceMatrix.needsUpdate = true
479+
}
477480
})
478481
})
479482
}

0 commit comments

Comments
 (0)