minecraft-renderer 0.1.45 → 0.1.47

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.
@@ -0,0 +1,10 @@
1
+ //@ts-nocheck
2
+ export type { FramePerformanceSample, PerformanceInstabilityFactors } from './types'
3
+ export { defaultPerformanceInstabilityFactors } from './types'
4
+ export {
5
+ LONG_RENDER_TIME_MS,
6
+ LOW_FPS_THRESHOLD,
7
+ HIGH_TEXTURE_COUNT,
8
+ } from './constants'
9
+ export { PerformanceMonitor } from './PerformanceMonitor'
10
+ export { formatPerformanceFactorsDebug } from './formatPerformanceFactorsDebug'
@@ -0,0 +1,27 @@
1
+ //@ts-nocheck
2
+ /** Low-FPS / instability factors written to reactive renderer state. */
3
+ export interface PerformanceInstabilityFactors {
4
+ longRenderTime: boolean
5
+ constantLongRenderTime: boolean
6
+ tooManyEntities: boolean
7
+ tooManyTextures: boolean
8
+ unknownReason: boolean
9
+ }
10
+
11
+ export const defaultPerformanceInstabilityFactors = (): PerformanceInstabilityFactors => ({
12
+ longRenderTime: false,
13
+ constantLongRenderTime: false,
14
+ tooManyEntities: false,
15
+ tooManyTextures: false,
16
+ unknownReason: false,
17
+ })
18
+
19
+ export interface FramePerformanceSample {
20
+ /** Full `WorldRendererThree.render()` duration in ms. */
21
+ totalMs: number
22
+ /** Time spent in `entities.render()` this frame (0 if skipped). */
23
+ entitiesMs: number
24
+ loadedTextureCount: number
25
+ /** FPS from the last completed 1s window (0 before first sample). */
26
+ fps: number
27
+ }
@@ -13,20 +13,15 @@ import * as THREE from 'three'
13
13
  import Stats from 'stats.js'
14
14
  import StatsGl from 'stats-gl'
15
15
  import * as tween from '@tweenjs/tween.js'
16
- import type { GraphicsInitOptions } from '../graphicsBackend/types'
16
+ import type { GraphicsBackendConfig, GraphicsInitOptions } from '../graphicsBackend/types'
17
+ import { gpuPreferenceToWebGLPowerPreference } from '../three/menuBackground/defaultOptions'
17
18
  import { WorldRendererConfig } from '../graphicsBackend'
18
19
 
19
20
  // ============================================================================
20
21
  // Types (co-located with implementation)
21
22
  // ============================================================================
22
23
 
23
- export interface GraphicsBackendConfig {
24
- fpsLimit?: number
25
- powerPreference?: 'high-performance' | 'low-power'
26
- statsVisible?: number
27
- sceneBackground: string
28
- timeoutRendering?: boolean
29
- }
24
+ export type { GraphicsBackendConfig }
30
25
 
31
26
  export interface FrameTimingEvent {
32
27
  type: 'frameStart' | 'frameEnd' | 'cameraUpdate' | 'frameDisplay'
@@ -207,11 +202,12 @@ export class DocumentRenderer {
207
202
  }
208
203
 
209
204
  try {
205
+ const gpuPreference = initOptions.getRendererOptions?.()?.gpuPreference ?? 'default'
210
206
  this.renderer = new THREE.WebGLRenderer({
211
207
  canvas: this.canvas as HTMLCanvasElement,
212
208
  preserveDrawingBuffer: true,
213
209
  logarithmicDepthBuffer: true,
214
- powerPreference: this.config.powerPreference
210
+ powerPreference: gpuPreferenceToWebGLPowerPreference(gpuPreference)
215
211
  })
216
212
  } catch (err: any) {
217
213
  initOptions.callbacks.displayCriticalError(
@@ -162,8 +162,6 @@ export const createGraphicsBackendBase = () => {
162
162
  mergedOptions,
163
163
  !!process.env.SINGLE_FILE_BUILD_MODE
164
164
  )
165
- ; (globalThis as any).menuBackgroundRenderer = menuBackgroundRenderer
166
-
167
165
  callModsMethod('menuBackgroundCreated', menuBackgroundRenderer)
168
166
  await menuBackgroundRenderer.start(mergedOptions)
169
167
  callModsMethod('menuBackgroundReady', menuBackgroundRenderer)
@@ -240,6 +238,7 @@ export const createGraphicsBackendBase = () => {
240
238
  documentRenderer!.setPaused(!rendering)
241
239
  if (worldRenderer) worldRenderer.renderingActive = rendering
242
240
  },
241
+ getMenuBackground: () => menuBackgroundRenderer ?? undefined,
243
242
  getDebugOverlay: () => ({
244
243
  get entitiesString() {
245
244
  return worldRenderer?.entities.getDebugString()
@@ -22,6 +22,19 @@ export type RendererOptionMeta = {
22
22
 
23
23
  export type RendererDefaultOptionKey = keyof typeof RENDERER_DEFAULT_OPTIONS
24
24
 
25
+ export type RendererMesherPipeline = 'wasm' | 'legacy-js'
26
+
27
+ export type RendererGpuPreference = 'default' | 'high-performance' | 'low-power'
28
+
29
+ /** Maps stored `gpuPreference` to WebGL `powerPreference` (undefined = browser default). */
30
+ export function gpuPreferenceToWebGLPowerPreference(
31
+ preference: RendererGpuPreference
32
+ ): 'high-performance' | 'low-power' | undefined {
33
+ if (preference === 'high-performance') return 'high-performance'
34
+ if (preference === 'low-power') return 'low-power'
35
+ return undefined
36
+ }
37
+
25
38
  const MB = MENU_BACKGROUND_OPTION_DEFAULTS
26
39
 
27
40
  /** Default values for options owned by minecraft-renderer (spread into app `defaultOptions`). */
@@ -39,9 +52,52 @@ export const RENDERER_DEFAULT_OPTIONS = {
39
52
  menuBackgroundFuturisticBlockSpeed: MB.futuristicBlockSpeedPercent,
40
53
  rendererFuturisticReveal: false,
41
54
  rendererPerfDebugOverlay: false,
42
- disableBlockEntityTextures: false
55
+ disableBlockEntityTextures: false,
56
+ rendererMesher: 'wasm' as RendererMesherPipeline,
57
+ showChunkBorders: false,
58
+ renderEntities: true,
59
+ renderDebug: 'basic' as 'none' | 'basic' | 'advanced',
60
+ frameLimit: false as number | false,
61
+ backgroundRendering: '20fps' as 'full' | '20fps' | '5fps',
62
+ vanillaLook: false,
63
+ smoothLighting: true,
64
+ newVersionsLighting: false,
65
+ vrSupport: true,
66
+ vrPageGameRendering: false,
67
+ clipWorldBelowY: undefined as number | undefined,
68
+ highlightBlockColor: 'auto' as 'auto' | 'blue' | 'classic',
69
+ loadPlayerSkins: true,
70
+ renderEars: true,
71
+ showHand: true,
72
+ viewBobbing: true,
73
+ dayCycleAndLighting: true,
74
+ keepChunksDistance: 1,
75
+ gpuPreference: 'default' as RendererGpuPreference
43
76
  } as const
44
77
 
78
+ /** App options storage shape for renderer-owned keys. */
79
+ export type RendererStorageOptions = typeof RENDERER_DEFAULT_OPTIONS
80
+
81
+ /**
82
+ * Migrate persisted / legacy option keys into current {@link RENDERER_DEFAULT_OPTIONS} shape.
83
+ * Call when loading saved settings (safe to run on every load).
84
+ */
85
+ export function migrateRendererOptions(saved: Record<string, unknown>): void {
86
+ if (saved.highPerformanceGpu) {
87
+ saved.gpuPreference = 'high-performance'
88
+ delete saved.highPerformanceGpu
89
+ }
90
+ if (saved.rendererMesher !== 'wasm' && saved.rendererMesher !== 'legacy-js') {
91
+ if (typeof saved.rendererWasmMesher === 'boolean') {
92
+ saved.rendererMesher = saved.rendererWasmMesher ? 'wasm' : 'legacy-js'
93
+ } else if (typeof saved.wasmExperimentalMesher === 'boolean') {
94
+ saved.rendererMesher = saved.wasmExperimentalMesher ? 'wasm' : 'legacy-js'
95
+ }
96
+ }
97
+ delete saved.wasmExperimentalMesher
98
+ delete saved.rendererWasmMesher
99
+ }
100
+
45
101
  /** Settings UI metadata for {@link RENDERER_DEFAULT_OPTIONS} keys. */
46
102
  export const RENDERER_OPTIONS_META: Partial<Record<RendererDefaultOptionKey, RendererOptionMeta>> = {
47
103
  menuBackgroundMode: {
@@ -102,6 +158,83 @@ export const RENDERER_OPTIONS_META: Partial<Record<RendererDefaultOptionKey, Ren
102
158
  disableBlockEntityTextures: {
103
159
  text: 'Disable block entity textures',
104
160
  tooltip: 'Skips signs, banners, heads, maps, etc.'
161
+ },
162
+ rendererMesher: {
163
+ possibleValues: [['wasm', 'WASM'], ['legacy-js', 'Legacy JS']],
164
+ text: 'Mesher pipeline',
165
+ tooltip: 'WASM is faster. Use JS if WASM is not working. Requires reload.',
166
+ requiresRestart: true
167
+ },
168
+ showChunkBorders: {
169
+ text: 'Chunk borders'
170
+ },
171
+ renderEntities: {
172
+ text: 'Render entities'
173
+ },
174
+ renderDebug: {
175
+ possibleValues: ['advanced', 'basic', 'none']
176
+ },
177
+ frameLimit: {
178
+ text: 'Frame limit',
179
+ tooltip: 'false = VSync / unlimited when focused'
180
+ },
181
+ backgroundRendering: {
182
+ text: 'Background FPS limit',
183
+ possibleValues: [
184
+ ['full', 'NO'],
185
+ ['5fps', '5 FPS'],
186
+ ['20fps', '20 FPS']
187
+ ]
188
+ },
189
+ vanillaLook: {
190
+ text: 'Vanilla shading',
191
+ tooltip: 'On: Minecraft-style face shading. Off: higher-contrast client shading.'
192
+ },
193
+ smoothLighting: {},
194
+ newVersionsLighting: {
195
+ text: 'Lighting in newer versions'
196
+ },
197
+ vrSupport: {
198
+ text: 'VR support',
199
+ tooltip: 'Shows VR entry; does not force VR on.'
200
+ },
201
+ vrPageGameRendering: {
202
+ text: 'VR page game rendering'
203
+ },
204
+ clipWorldBelowY: {
205
+ text: 'Clip world below Y'
206
+ },
207
+ highlightBlockColor: {
208
+ possibleValues: [
209
+ ['auto', 'Auto'],
210
+ ['blue', 'Blue'],
211
+ ['classic', 'Classic']
212
+ ]
213
+ },
214
+ loadPlayerSkins: {},
215
+ renderEars: {
216
+ tooltip: 'Deadmau5 ears when the skin texture includes them'
217
+ },
218
+ showHand: {},
219
+ viewBobbing: {},
220
+ dayCycleAndLighting: {
221
+ text: 'Day cycle'
222
+ },
223
+ keepChunksDistance: {
224
+ text: 'Keep chunks distance',
225
+ tooltip: 'Extra distance before unloading chunks',
226
+ max: 5,
227
+ unit: ''
228
+ },
229
+ gpuPreference: {
230
+ text: 'GPU preference',
231
+ tooltip: 'WebGL power preference. Requires reload / backend restart to apply.',
232
+ requiresRestart: true,
233
+ possibleValues: [
234
+ ['default', 'Auto'],
235
+ ['high-performance', 'Dedicated'],
236
+ ['low-power', 'Low power']
237
+ ]
105
238
  }
106
239
  }
107
240
 
@@ -110,32 +243,57 @@ export const RENDERER_RENDER_GUI_SECTIONS: ReadonlyArray<{
110
243
  title: string
111
244
  keys: readonly RendererDefaultOptionKey[]
112
245
  }> = [
113
- {
114
- title: 'Menu background',
115
- keys: [
116
- 'menuBackgroundMode',
117
- 'menuBackgroundMinecraftTextures',
118
- 'menuBackgroundFuturisticScene',
119
- 'menuBackgroundFuturisticCamera',
120
- 'menuBackgroundFuturisticBlockGroup',
121
- 'menuBackgroundFuturisticCameraSpeed',
122
- 'menuBackgroundFuturisticBlockSpeed'
123
- ]
124
- },
125
- {
126
- title: 'World rendering',
127
- keys: [
128
- 'rendererWorldPerformance',
129
- 'starfieldRendering',
130
- 'defaultSkybox',
131
- 'disableBlockEntityTextures'
132
- ]
133
- },
134
- {
135
- title: 'Renderer debug',
136
- keys: [
137
- 'rendererFuturisticReveal',
138
- 'rendererPerfDebugOverlay'
139
- ]
140
- }
141
- ]
246
+ {
247
+ title: 'World rendering',
248
+ keys: [
249
+ 'rendererWorldPerformance',
250
+ 'starfieldRendering',
251
+ 'defaultSkybox',
252
+ 'disableBlockEntityTextures',
253
+ 'showChunkBorders',
254
+ 'renderEntities',
255
+ 'smoothLighting',
256
+ 'vanillaLook',
257
+ 'newVersionsLighting',
258
+ 'dayCycleAndLighting',
259
+ 'loadPlayerSkins',
260
+ 'renderEars',
261
+ 'showHand',
262
+ 'viewBobbing',
263
+ 'keepChunksDistance',
264
+ 'highlightBlockColor',
265
+ 'clipWorldBelowY'
266
+ ]
267
+ },
268
+ {
269
+ title: 'Frame pacing',
270
+ keys: ['frameLimit', 'backgroundRendering', 'renderDebug', 'gpuPreference']
271
+ },
272
+ {
273
+ title: 'VR',
274
+ keys: ['vrSupport', 'vrPageGameRendering']
275
+ },
276
+ {
277
+ title: 'Menu background',
278
+ keys: [
279
+ 'menuBackgroundMode',
280
+ 'menuBackgroundMinecraftTextures',
281
+ 'menuBackgroundFuturisticScene',
282
+ 'menuBackgroundFuturisticCamera',
283
+ 'menuBackgroundFuturisticBlockGroup',
284
+ 'menuBackgroundFuturisticCameraSpeed',
285
+ 'menuBackgroundFuturisticBlockSpeed'
286
+ ]
287
+ },
288
+ {
289
+ title: 'Mesher',
290
+ keys: ['rendererMesher']
291
+ },
292
+ {
293
+ title: 'Renderer debug',
294
+ keys: [
295
+ 'rendererFuturisticReveal',
296
+ 'rendererPerfDebugOverlay'
297
+ ]
298
+ }
299
+ ]
@@ -31,6 +31,14 @@ export {
31
31
  export {
32
32
  RENDERER_DEFAULT_OPTIONS,
33
33
  RENDERER_OPTIONS_META,
34
- RENDERER_RENDER_GUI_SECTIONS
34
+ RENDERER_RENDER_GUI_SECTIONS,
35
+ migrateRendererOptions
35
36
  } from './defaultOptions'
36
- export type { RendererDefaultOptionKey, RendererOptionMeta } from './defaultOptions'
37
+ export type {
38
+ RendererDefaultOptionKey,
39
+ RendererGpuPreference,
40
+ RendererMesherPipeline,
41
+ RendererOptionMeta,
42
+ RendererStorageOptions
43
+ } from './defaultOptions'
44
+ export { gpuPreferenceToWebGLPowerPreference } from './defaultOptions'
@@ -1,32 +1,11 @@
1
1
  //@ts-nocheck
2
2
  import * as THREE from 'three'
3
3
  import type { WorldRendererThree } from './worldRendererThree'
4
+ import type { ExportedSection, ExportedWorldGeometry } from '../mesher-shared/exportedGeometryTypes'
4
5
 
5
- const GEOMETRY_EXPORT_GROUP_NAME = 'geometry-export-root'
6
-
7
- // Format for exported world geometry
8
- export interface ExportedWorldGeometry {
9
- version: string
10
- exportedAt: string
11
- camera: {
12
- position: { x: number, y: number, z: number }
13
- rotation: { pitch: number, yaw: number }
14
- }
15
- sections: ExportedSection[]
16
- textureAtlasDataUrl?: string
17
- }
6
+ export type { ExportedSection, ExportedWorldGeometry } from '../mesher-shared/exportedGeometryTypes'
18
7
 
19
- export interface ExportedSection {
20
- key: string
21
- position: { x: number, y: number, z: number }
22
- geometry: {
23
- positions: number[]
24
- normals: number[]
25
- colors: number[]
26
- uvs: number[]
27
- indices: number[]
28
- }
29
- }
8
+ const GEOMETRY_EXPORT_GROUP_NAME = 'geometry-export-root'
30
9
 
31
10
  /**
32
11
  * Export world geometry to a downloadable file
@@ -36,6 +36,7 @@ import { downloadWorldGeometry } from './worldGeometryExport'
36
36
  import { ChunkMeshManager } from './chunkMeshManager'
37
37
  import type { RendererModuleManifest, RegisteredModule, RendererModuleController } from './rendererModuleSystem'
38
38
  import { BUILTIN_MODULES } from './modules/index'
39
+ import { formatPerformanceFactorsDebug, PerformanceMonitor } from '../performanceMonitor'
39
40
 
40
41
  type SectionKey = string
41
42
 
@@ -57,6 +58,7 @@ export class WorldRendererThree extends WorldRendererCommon {
57
58
  ambientLight = new THREE.AmbientLight(0xcc_cc_cc)
58
59
  directionalLight = new THREE.DirectionalLight(0xff_ff_ff, 0.5)
59
60
  entities = new Entities(this, (globalThis as any).mcData)
61
+ performanceMonitor!: PerformanceMonitor
60
62
  cameraGroupVr?: THREE.Object3D
61
63
  material = new THREE.MeshBasicMaterial({ vertexColors: true, transparent: true, alphaTest: 0.1 })
62
64
  itemsTexture!: THREE.Texture
@@ -154,6 +156,8 @@ export class WorldRendererThree extends WorldRendererCommon {
154
156
  if (!displayOptions.resourcesManager) throw new Error('resourcesManager is required in displayOptions')
155
157
  super(displayOptions.resourcesManager, displayOptions, initOptions)
156
158
 
159
+ this.performanceMonitor = new PerformanceMonitor(this.reactiveState.world.instabilityFactors)
160
+
157
161
  this.renderer = renderer
158
162
  displayOptions.rendererState.renderer = WorldRendererThree.getRendererInfo(renderer) ?? '...'
159
163
 
@@ -713,7 +717,10 @@ export class WorldRendererThree extends WorldRendererCommon {
713
717
  text += `B: ${formatCompact(this.blocksRendered)} `
714
718
  text += `MEM: ${this.chunkMeshManager.getEstimatedMemoryUsage().total} `
715
719
  const poolStats = this.chunkMeshManager.getStats()
716
- text += `POOL: ${poolStats.activeCount}/${poolStats.poolSize}`
720
+ text += `POOL: ${poolStats.activeCount}/${poolStats.poolSize} `
721
+ const pf = formatPerformanceFactorsDebug(this.reactiveState.world.instabilityFactors)
722
+ if (pf) text += `PF: ${pf} `
723
+ // entities can be seen in F3
717
724
  pane.updateText(text)
718
725
  this.backendInfoReport = text
719
726
  }
@@ -1201,8 +1208,11 @@ export class WorldRendererThree extends WorldRendererCommon {
1201
1208
  this.camera.updateProjectionMatrix()
1202
1209
  }
1203
1210
 
1211
+ let entitiesRenderMs = 0
1204
1212
  if (!this.reactiveDebugParams.disableEntities) {
1213
+ const entitiesStart = performance.now()
1205
1214
  this.entities.render()
1215
+ entitiesRenderMs = performance.now() - entitiesStart
1206
1216
  }
1207
1217
 
1208
1218
  // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style
@@ -1243,6 +1253,13 @@ export class WorldRendererThree extends WorldRendererCommon {
1243
1253
  this.renderTimeAvgCount++
1244
1254
  this.renderTimeAvg = ((this.renderTimeAvg * (this.renderTimeAvgCount - 1)) + totalTime) / this.renderTimeAvgCount
1245
1255
  this.renderTimeMax = Math.max(this.renderTimeMax, totalTime)
1256
+
1257
+ this.performanceMonitor.onFrame({
1258
+ totalMs: totalTime,
1259
+ entitiesMs: entitiesRenderMs,
1260
+ loadedTextureCount: this.renderer.info.memory.textures,
1261
+ fps: this.lastFps,
1262
+ })
1246
1263
  }
1247
1264
 
1248
1265
  renderHead(position: Vec3, rotation: number, isWall: boolean, blockEntity) {
@@ -1450,6 +1467,7 @@ export class WorldRendererThree extends WorldRendererCommon {
1450
1467
  }
1451
1468
 
1452
1469
  destroy(): void {
1470
+ this.performanceMonitor?.reset()
1453
1471
  this.pendingSectionUpdates.clear()
1454
1472
  this.pendingSectionBufferStartTimes.clear()
1455
1473
  this.chunkMeshManager.dispose()
@@ -10,11 +10,12 @@ import MinecraftData from 'minecraft-data'
10
10
  import PrismarineBlockLoader from 'prismarine-block'
11
11
  import { Vec3 } from 'vec3'
12
12
  import { elemFaces, buildRotationMatrix, matmul3, matmulmat3, vecadd3, vecsub3 } from '../../mesher-shared/modelsGeometryCommon'
13
- import type { ExportedWorldGeometry, ExportedSection } from '../../three/worldGeometryExport'
13
+ import type { ExportedWorldGeometry, ExportedSection } from '../../mesher-shared/exportedGeometryTypes'
14
14
  import type { MesherGeometryOutput } from '../../mesher-shared/shared'
15
15
  import type { World } from '../../mesher-shared/world'
16
- import { resolveBlockPropertiesForMeshing } from '../../mesher-shared/models'
16
+ import { resolveBlockPropertiesForMeshing } from '../../mesher-shared/blockPropertiesForMeshing'
17
17
  import { getSideShading, vertexLightFromAo } from '../../mesher-shared/vertexShading'
18
+ import tintsJson from 'minecraft-data/minecraft-data/data/pc/1.16.2/tints.json'
18
19
 
19
20
  // Handle both default and named export
20
21
  const worldBlockProvider = (worldBlockProviderModule as any).default || worldBlockProviderModule
@@ -25,14 +26,8 @@ let tintsInitialized = false
25
26
 
26
27
  function initializeTints() {
27
28
  if (tintsInitialized) return
28
- let tintsData
29
- try {
30
- tintsData = require('esbuild-data').tints
31
- } catch (err) {
32
- tintsData = require('minecraft-data/minecraft-data/data/pc/1.16.2/tints.json')
33
- }
34
- for (const key of Object.keys(tintsData)) {
35
- tints[key] = prepareTints(tintsData[key])
29
+ for (const key of Object.keys(tintsJson as Record<string, unknown>)) {
30
+ tints[key] = prepareTints((tintsJson as Record<string, unknown>)[key])
36
31
  }
37
32
  tintsInitialized = true
38
33
  }