@@ -8,6 +8,23 @@ import { ParseErrorSeverity } from "./MirabufParser.ts"
88type MirabufPartInstanceGUID = string
99
1010const 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 ( / C h r o m e \/ ( \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
1229export enum MaterialStyle {
1330 REGULAR = 0 ,
@@ -93,8 +110,8 @@ const transformGeometry = (geometry: THREE.BufferGeometry, mesh: mirabuf.IMesh)
93110class 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 *
0 commit comments