@@ -8,6 +8,25 @@ 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 (
20+ `Detected Chrome ${ chromeVersion } , using ${ chromeVersion >= CHROME_VERSION_FOR_INSTANCED_MESH ? "InstancedMesh" : "BatchedMesh" } `
21+ )
22+ return chromeVersion >= CHROME_VERSION_FOR_INSTANCED_MESH
23+ }
24+
25+ console . log ( `Non-Chrome browser detected (${ userAgent } ), using BatchedMesh` )
26+ return false
27+ }
28+
29+ const USE_INSTANCED_MESH = detectInstancedMeshSupport ( )
1130
1231export enum MaterialStyle {
1332 REGULAR = 0 ,
@@ -93,8 +112,8 @@ const transformGeometry = (geometry: THREE.BufferGeometry, mesh: mirabuf.IMesh)
93112class MirabufInstance {
94113 private _mirabufParser : MirabufParser
95114 private _materials : Map < string , THREE . Material >
96- private _meshes : Map < MirabufPartInstanceGUID , Array < [ THREE . BatchedMesh , number ] > >
97- private _batches : Array < THREE . BatchedMesh >
115+ private _meshes : Map < MirabufPartInstanceGUID , Array < [ THREE . InstancedMesh | THREE . BatchedMesh , number ] > >
116+ private _batches : Array < THREE . InstancedMesh | THREE . BatchedMesh >
98117
99118 public get parser ( ) {
100119 return this . _mirabufParser
@@ -160,6 +179,63 @@ class MirabufInstance {
160179 * Creates ThreeJS meshes from the parsed mirabuf file.
161180 */
162181 private createMeshes ( ) {
182+ if ( USE_INSTANCED_MESH ) {
183+ this . createInstancedMeshes ( )
184+ } else {
185+ this . createBatchedMeshes ( )
186+ }
187+ }
188+
189+ /**
190+ * Creates InstancedMesh objects, as newer version of Chrome break with BatchedMesh
191+ */
192+ private createInstancedMeshes ( ) {
193+ const assembly = this . _mirabufParser . assembly
194+ const instances = assembly . data ! . parts ! . partInstances !
195+
196+ Object . values ( instances ) . forEach ( instance => {
197+ const definition = assembly . data ! . parts ! . partDefinitions ! [ instance . partDefinitionReference ! ]
198+ const bodies = definition ?. bodies ?? [ ]
199+
200+ bodies . forEach ( body => {
201+ const mesh = body ?. triangleMesh ?. mesh
202+ if ( ! mesh ?. verts || ! mesh . normals || ! mesh . uv || ! mesh . indices ) return
203+
204+ const appearanceOverride = body . appearanceOverride
205+ const material = WIREFRAME
206+ ? new THREE . MeshStandardMaterial ( { wireframe : true , color : 0x000000 } )
207+ : appearanceOverride && this . _materials . has ( appearanceOverride )
208+ ? this . _materials . get ( appearanceOverride ) !
209+ : fillerMaterials [ nextFillerMaterial ++ % fillerMaterials . length ]
210+
211+ const geometry = new THREE . BufferGeometry ( )
212+ transformGeometry ( geometry , mesh )
213+
214+ // Create InstancedMesh with count of 1 for this body
215+ const instancedMesh = new THREE . InstancedMesh ( geometry , material , 1 )
216+ instancedMesh . castShadow = true
217+ instancedMesh . receiveShadow = true
218+
219+ const mat = this . _mirabufParser . globalTransforms . get ( instance . info ! . GUID ! ) !
220+ instancedMesh . setMatrixAt ( 0 , mat )
221+ instancedMesh . instanceMatrix . needsUpdate = true
222+
223+ this . _batches . push ( instancedMesh )
224+
225+ let bodies = this . _meshes . get ( instance . info ! . GUID ! )
226+ if ( ! bodies ) {
227+ bodies = [ ]
228+ this . _meshes . set ( instance . info ! . GUID ! , bodies )
229+ }
230+ bodies . push ( [ instancedMesh , 0 ] )
231+ } )
232+ } )
233+ }
234+
235+ /**
236+ * Creates BatchedMesh, more efficient, but broken in newer versions of Chrome
237+ */
238+ private createBatchedMeshes ( ) {
163239 const assembly = this . _mirabufParser . assembly
164240 const instances = assembly . data ! . parts ! . partInstances !
165241
@@ -171,6 +247,7 @@ class MirabufInstance {
171247
172248 const batchMap = new Map < THREE . Material , Map < string , [ mirabuf . IBody , Array < mirabuf . IPartInstance > ] > > ( )
173249 const countMap = new Map < THREE . Material , BatchCounts > ( )
250+
174251 // Filter all instances by first material, then body
175252 Object . values ( instances ) . forEach ( instance => {
176253 const definition = assembly . data ! . parts ! . partDefinitions ! [ instance . partDefinitionReference ! ]
@@ -180,7 +257,6 @@ class MirabufInstance {
180257 if ( ! mesh ?. verts || ! mesh . normals || ! mesh . uv || ! mesh . indices ) return
181258
182259 const appearanceOverride = body . appearanceOverride
183-
184260 const material = WIREFRAME
185261 ? new THREE . MeshStandardMaterial ( { wireframe : true , color : 0x000000 } )
186262 : appearanceOverride && this . _materials . has ( appearanceOverride )
0 commit comments