minecraft-renderer 0.1.21 → 0.1.22

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minecraft-renderer",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "description": "The most Modular Minecraft world renderer with Three.js WebGL backend",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -74,6 +74,7 @@
74
74
  "@types/three": "0.154.0",
75
75
  "@zardoy/react-util": "^0.2.7",
76
76
  "@zardoy/tsconfig": "^1.5.1",
77
+ "contro-max": "*",
77
78
  "esbuild": "^0.19.3",
78
79
  "esbuild-plugin-polyfill-node": "^0.3.0",
79
80
  "fs-extra": "^11.0.0",
@@ -92,11 +93,15 @@
92
93
  "vitest": "^4.0.14"
93
94
  },
94
95
  "peerDependencies": {
96
+ "contro-max": "*",
95
97
  "mc-assets": ">=0.2.0",
96
98
  "minecraft-data": ">=3.0.0",
97
99
  "three": ">=0.150.0"
98
100
  },
99
101
  "peerDependenciesMeta": {
102
+ "contro-max": {
103
+ "optional": true
104
+ },
100
105
  "mc-assets": {
101
106
  "optional": true
102
107
  },
@@ -20,7 +20,7 @@ export const defaultWorldRendererConfig = {
20
20
  // Debug settings
21
21
  showChunkBorders: false,
22
22
  enableDebugOverlay: false,
23
- debugModelVariant: undefined,
23
+ debugModelVariant: undefined as undefined | number[],
24
24
  futuristicReveal: false,
25
25
 
26
26
  // Performance settings
@@ -46,7 +46,7 @@ export const defaultWorldRendererConfig = {
46
46
  showHand: false,
47
47
  viewBobbing: false,
48
48
  renderEars: true,
49
- highlightBlockColor: 'blue' as 'blue' | 'classic' | undefined,
49
+ highlightBlockColor: 'blue' as 'blue' | 'classic' | 'auto' | undefined,
50
50
 
51
51
  // Player models
52
52
  fetchPlayerSkins: true,
@@ -95,7 +95,7 @@ export const getDefaultRendererState = (): {
95
95
  reactive: proxy({
96
96
  world: {
97
97
  chunksLoaded: new Set<string>(),
98
- heightmaps: new Map<string, Uint8Array>(),
98
+ heightmaps: new Map<string, Int16Array>(),
99
99
  allChunksLoaded: false,
100
100
  mesherWork: false,
101
101
  intersectMedia: null
@@ -5,11 +5,11 @@
5
5
  * Core types for the graphics backend system.
6
6
  */
7
7
 
8
- import { WorldRendererConfig } from '../lib/worldrendererCommon'
9
8
  import { PlayerStateReactive } from '../playerState/playerState'
10
9
  import { ResourcesManagerTransferred } from '../resourcesManager'
11
10
  import { WorldViewWorker } from '../worldView'
12
11
  import { Vec3 } from 'vec3'
12
+ import { WorldRendererConfig } from './config'
13
13
 
14
14
  // ============================================================================
15
15
  // Graphics Backend Configuration
@@ -69,7 +69,7 @@ export interface NonReactiveState {
69
69
  export interface RendererReactiveState {
70
70
  world: {
71
71
  chunksLoaded: Set<string>
72
- heightmaps: Map<string, Uint8Array>
72
+ heightmaps: Map<string, Int16Array>
73
73
  allChunksLoaded: boolean
74
74
  mesherWork: boolean
75
75
  intersectMedia: any | null
@@ -64,7 +64,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
64
64
  dirty(pos: Vec3, value: boolean): void
65
65
  update(/* pos: Vec3, value: boolean */): void
66
66
  chunkFinished(key: string): void
67
- heightmap(key: string, heightmap: Uint8Array): void
67
+ heightmap(key: string, heightmap: Int16Array): void
68
68
  }>
69
69
  customTexturesDataUrl = undefined as string | undefined
70
70
  workers: any[] = []
@@ -428,7 +428,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
428
428
  }
429
429
 
430
430
  if (data.type === 'heightmap') {
431
- this.reactiveState.world.heightmaps.set(data.key, new Uint8Array(data.heightmap))
431
+ this.reactiveState.world.heightmaps.set(data.key, new Int16Array(data.heightmap))
432
432
  }
433
433
  }
434
434
 
@@ -622,11 +622,11 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
622
622
  customBlockModels: customBlockModels || undefined
623
623
  })
624
624
  }
625
- // this.workers[0].postMessage({
626
- // type: 'getHeightmap',
627
- // x,
628
- // z,
629
- // })
625
+ this.workers[0].postMessage({
626
+ type: 'getHeightmap',
627
+ x,
628
+ z,
629
+ })
630
630
  this.logWorkerWork(() => `-> chunk ${JSON.stringify({ x, z, chunkLength: chunk.length, customBlockModelsLength: customBlockModels ? Object.keys(customBlockModels).length : 0 })}`)
631
631
  this.mesherLogReader?.chunkReceived(x, z, chunk.length)
632
632
  const sectionHeight = this.getSectionHeight()
@@ -687,6 +687,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
687
687
  }
688
688
  }
689
689
  this.highestBlocksByChunks.delete(`${x},${z}`)
690
+ this.reactiveState.world.heightmaps.delete(`${Math.floor(x / 16)},${Math.floor(z / 16)}`)
690
691
 
691
692
  this.updateChunksStats()
692
693
 
@@ -848,6 +849,10 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
848
849
  customBlockModels
849
850
  })
850
851
  }
852
+ // Re-request heightmap for the affected chunk after block change
853
+ const chunkCornerX = Math.floor(pos.x / CHUNK_SIZE) * CHUNK_SIZE
854
+ const chunkCornerZ = Math.floor(pos.z / CHUNK_SIZE) * CHUNK_SIZE
855
+ this.workers[0].postMessage({ type: 'getHeightmap', x: chunkCornerX, z: chunkCornerZ })
851
856
  this.logWorkerWork(`-> blockUpdate ${JSON.stringify({ pos, stateId, customBlockModels })}`)
852
857
  this.setSectionDirty(pos, true, true)
853
858
  if (this.neighborChunkUpdates) {
@@ -153,7 +153,7 @@ const handleMessage = data => {
153
153
  break
154
154
  }
155
155
  case 'getHeightmap': {
156
- const heightmap = new Uint8Array(256)
156
+ const heightmap = new Int16Array(256)
157
157
 
158
158
  const blockPos = new Vec3(0, 0, 0)
159
159
  for (let z = 0; z < 16; z++) {
@@ -169,7 +169,7 @@ const handleMessage = data => {
169
169
  block = world.getBlock(blockPos)
170
170
  }
171
171
  const index = z * 16 + x
172
- heightmap[index] = block ? blockPos.y : 0
172
+ heightmap[index] = block ? blockPos.y : -32768
173
173
  }
174
174
  }
175
175
  postMessage({ type: 'heightmap', key: `${Math.floor(data.x / 16)},${Math.floor(data.z / 16)}`, heightmap })
@@ -65,7 +65,7 @@ export interface MesherMainEvents {
65
65
  geometry: { type: 'geometry'; key: string; geometry: MesherGeometryOutput; workerIndex: number };
66
66
  sectionFinished: { type: 'sectionFinished'; key: string; workerIndex: number; processTime?: number };
67
67
  blockStateModelInfo: { type: 'blockStateModelInfo'; info: Record<string, BlockStateModelInfo> };
68
- heightmap: { type: 'heightmap'; key: string; heightmap: Uint8Array };
68
+ heightmap: { type: 'heightmap'; key: string; heightmap: Int16Array };
69
69
  }
70
70
 
71
71
  export type MesherMainEvent = MesherMainEvents[keyof MesherMainEvents]
@@ -1,7 +1,10 @@
1
1
  //@ts-nocheck
2
+ import { IndexedData } from 'minecraft-data'
2
3
  import { EntityMesh, rendererSpecialHandled, EntityDebugFlags } from '../three/entity/EntityMesh'
3
4
 
4
- export const displayEntitiesDebugList = (version: string) => {
5
+ export const displayEntitiesDebugList = (mcData: IndexedData) => {
6
+ const version = mcData.version.minecraftVersion!
7
+
5
8
  // Create results container
6
9
  const container = document.createElement('div')
7
10
  container.style.cssText = `
@@ -36,7 +39,6 @@ export const displayEntitiesDebugList = (version: string) => {
36
39
  textureMap?: boolean;
37
40
  errors?: string[];
38
41
  }> = []
39
- const { mcData } = window
40
42
  const entityNames = Object.keys(mcData.entitiesArray.reduce((acc, entity) => {
41
43
  acc[entity.name] = true
42
44
  return acc
@@ -64,8 +66,8 @@ export const displayEntitiesDebugList = (version: string) => {
64
66
 
65
67
  const { mesh: entityMesh } = new EntityMesh(version, entity, undefined, {}, debugFlags)
66
68
  // find the most distant pos child
67
- window.objects ??= {}
68
- window.objects[entity] = entityMesh
69
+ globalThis.objects ??= {}
70
+ globalThis.objects[entity] = entityMesh
69
71
 
70
72
  results.push({
71
73
  entity,
@@ -8,6 +8,6 @@ export default class AllEntities extends BasePlaygroundScene {
8
8
 
9
9
  async initData() {
10
10
  await super.initData()
11
- displayEntitiesDebugList(this.version)
11
+ displayEntitiesDebugList(this.mcData)
12
12
  }
13
13
  }
@@ -27,7 +27,7 @@ export default class RailsCobwebScene extends BasePlaygroundScene {
27
27
  for (let x = -squareSize; x <= squareSize; x++) {
28
28
  for (let z = -squareSize; z <= squareSize; z++) {
29
29
  const i = Math.abs(x + z) * squareSize
30
- this.worldView!.world.setBlock(this.targetPos.offset(x, 0, z), this.Block.fromStateId(fullBlocks[i % fullBlocks.length].defaultState, 0))
30
+ this.worldView!.setBlockStateId(this.targetPos.offset(x, 0, z), this.mcData.blocksByName[fullBlocks[i % fullBlocks.length].name]!.defaultState)
31
31
  }
32
32
  }
33
33
  }
@@ -25,8 +25,8 @@ export const getItemUv = (item: Record<string, any>, specificProps: ItemSpecific
25
25
  try {
26
26
  const name =
27
27
  blockState
28
- ? loadedData.blocksByStateId[blockState]?.name
29
- : typeof idOrName === 'number' ? loadedData.items[idOrName]?.name : idOrName
28
+ ? globalThis.loadedData.blocksByStateId[blockState]?.name
29
+ : typeof idOrName === 'number' ? globalThis.loadedData.items[idOrName]?.name : idOrName
30
30
  if (!name) throw new Error(`Item not found: ${idOrName}`)
31
31
 
32
32
  const model = getItemModelName({
@@ -13,8 +13,8 @@ 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 { WorldRendererConfig } from '../lib/worldrendererCommon'
17
16
  import type { GraphicsInitOptions } from '../graphicsBackend/types'
17
+ import { WorldRendererConfig } from '../graphicsBackend'
18
18
 
19
19
  // ============================================================================
20
20
  // Types (co-located with implementation)
@@ -208,7 +208,7 @@ export class DocumentRenderer {
208
208
 
209
209
  try {
210
210
  this.renderer = new THREE.WebGLRenderer({
211
- canvas: this.canvas,
211
+ canvas: this.canvas as HTMLCanvasElement,
212
212
  preserveDrawingBuffer: true,
213
213
  logarithmicDepthBuffer: true,
214
214
  powerPreference: this.config.powerPreference
@@ -66,6 +66,8 @@ export const getBackendMethods = (worldRenderer: WorldRendererThree): any => {
66
66
  launchFirework: worldRenderer.fireworks.launchFirework.bind(worldRenderer.fireworks),
67
67
  // New method for updating skybox
68
68
  setSkyboxImage: worldRenderer.skyboxRenderer.setSkyboxImage.bind(worldRenderer.skyboxRenderer),
69
+ // Rain methods
70
+ setRain: (newState: boolean) => worldRenderer.toggleModule('rain', newState),
69
71
  async loadGeometryExport(exportData: any) {
70
72
  // Import dynamically to avoid circular dependencies
71
73
  const { applyWorldGeometryExport } = await import('./worldGeometryExport')
@@ -7,7 +7,6 @@ import { BlockModel } from 'mc-assets'
7
7
  import { DebugGui } from '../lib/DebugGui'
8
8
  import { SmoothSwitcher } from '../lib/smoothSwitcher'
9
9
  import { watchProperty } from '../lib/utils/proxy'
10
- import { WorldRendererConfig } from '../lib/worldrendererCommon'
11
10
  import { getMyHand } from './hand'
12
11
  import { WorldRendererThree } from './worldRendererThree'
13
12
  import { disposeObject } from './threeJsUtils'
@@ -16,6 +15,7 @@ import { PlayerStateRenderer } from '../playerState/playerState'
16
15
  import { getThreeBlockModelGroup } from '../mesher/standaloneRenderer'
17
16
  import { IndexedData } from 'minecraft-data'
18
17
  import type { ResourcesManagerTransferred } from '../resourcesManager'
18
+ import { WorldRendererConfig } from '../graphicsBackend'
19
19
 
20
20
  const rotationPositionData = {
21
21
  itemRight: {
@@ -1,8 +1,10 @@
1
1
  //@ts-nocheck
2
+ import { rainManifest } from './rain'
2
3
  import { sciFiWorldRevealManifest } from './sciFiWorldReveal'
3
4
  import { starfieldManifest } from './starfield'
4
5
 
5
6
  export const BUILTIN_MODULES = {
6
7
  starfield: starfieldManifest,
7
8
  futuristicReveal: sciFiWorldRevealManifest,
9
+ rain: rainManifest,
8
10
  }
@@ -0,0 +1,181 @@
1
+ //@ts-nocheck
2
+ import * as THREE from 'three'
3
+ import type { WorldRendererThree } from '../worldRendererThree'
4
+ import type { RendererModuleController, RendererModuleManifest } from '../rendererModuleSystem'
5
+
6
+ interface RainParticleData {
7
+ velocity: THREE.Vector3
8
+ age: number
9
+ despawnOffset: number
10
+ }
11
+
12
+ const PARTICLE_COUNT = 2000
13
+ const RANGE = 32
14
+ const HEIGHT = 32
15
+ const FALL_SPEED_MIN = 0.2
16
+ const FALL_SPEED_MAX = 0.4
17
+ const HORIZONTAL_DRIFT = 0.02
18
+ const RESPAWN_BELOW = -5
19
+
20
+ export class RainModule implements RendererModuleController {
21
+ private instancedMesh?: THREE.InstancedMesh
22
+ private geometry?: THREE.BoxGeometry
23
+ private material?: THREE.MeshBasicMaterial
24
+ private particles: RainParticleData[] = []
25
+ private enabled = false
26
+ private readonly dummy = new THREE.Matrix4()
27
+ private readonly tempPosition = new THREE.Vector3()
28
+ private readonly tempQuaternion = new THREE.Quaternion()
29
+ private readonly tempScale = new THREE.Vector3()
30
+
31
+ constructor(private readonly worldRenderer: WorldRendererThree) { }
32
+
33
+ enable(): void {
34
+ if (this.enabled) return
35
+ this.enabled = true
36
+ if (!this.instancedMesh) {
37
+ this.createRain()
38
+ } else {
39
+ this.instancedMesh.visible = true
40
+ }
41
+ }
42
+
43
+ disable(): void {
44
+ if (!this.enabled) return
45
+ this.enabled = false
46
+ if (this.instancedMesh) {
47
+ this.instancedMesh.visible = false
48
+ }
49
+ }
50
+
51
+ render?: () => void = () => {
52
+ if (!this.enabled || !this.instancedMesh) return
53
+
54
+ const cameraPos = this.worldRenderer.getCameraPosition()
55
+ this.instancedMesh.position.copy(cameraPos)
56
+
57
+ const heightmaps = this.worldRenderer.reactiveState.world.heightmaps
58
+
59
+ const { dummy, tempPosition: position, tempQuaternion: quaternion, tempScale: scale } = this
60
+
61
+ // Cache chunk key lookup to avoid redundant Map.get and string allocation
62
+ let prevChunkX = NaN
63
+ let prevChunkZ = NaN
64
+ let cachedHeightmap: Int16Array | undefined
65
+
66
+ for (let i = 0; i < PARTICLE_COUNT; i++) {
67
+ const particle = this.particles[i]
68
+ this.instancedMesh.getMatrixAt(i, dummy)
69
+ dummy.decompose(position, quaternion, scale)
70
+
71
+ position.add(particle.velocity)
72
+
73
+ const relativeY = position.y
74
+ const horizontalDist = Math.sqrt(position.x * position.x + position.z * position.z)
75
+
76
+ // Convert camera-relative position to world coordinates
77
+ const worldX = cameraPos.x + position.x
78
+ const worldY = cameraPos.y + position.y
79
+ const worldZ = cameraPos.z + position.z
80
+
81
+ // Look up heightmap for this world position (cached per chunk)
82
+ const chunkX = Math.floor(worldX / 16)
83
+ const chunkZ = Math.floor(worldZ / 16)
84
+ if (chunkX !== prevChunkX || chunkZ !== prevChunkZ) {
85
+ cachedHeightmap = heightmaps.get(`${chunkX},${chunkZ}`)
86
+ prevChunkX = chunkX
87
+ prevChunkZ = chunkZ
88
+ }
89
+
90
+ const localX = ((Math.floor(worldX) % 16) + 16) % 16
91
+ const localZ = ((Math.floor(worldZ) % 16) + 16) % 16
92
+ const heightY = cachedHeightmap?.[localZ * 16 + localX]
93
+
94
+ // Respawn when: out of range, hit heightmap surface (heightY + 1 = block top face), or fell too far
95
+ const shouldRespawn = horizontalDist > RANGE ||
96
+ (heightY !== undefined && heightY !== -32768 && worldY <= heightY + 1 + particle.despawnOffset) ||
97
+ relativeY < RESPAWN_BELOW
98
+
99
+ if (shouldRespawn) {
100
+ this.respawnParticle(position)
101
+ const speed = FALL_SPEED_MIN + Math.random() * (FALL_SPEED_MAX - FALL_SPEED_MIN)
102
+ particle.velocity.set(
103
+ (Math.random() - 0.5) * HORIZONTAL_DRIFT,
104
+ -speed,
105
+ (Math.random() - 0.5) * HORIZONTAL_DRIFT,
106
+ )
107
+ particle.despawnOffset = Math.random() * 0.5
108
+ }
109
+
110
+ dummy.compose(position, quaternion, scale)
111
+ this.instancedMesh.setMatrixAt(i, dummy)
112
+ }
113
+
114
+ this.instancedMesh.instanceMatrix.needsUpdate = true
115
+ }
116
+
117
+ dispose(): void {
118
+ if (this.instancedMesh) {
119
+ this.worldRenderer.scene.remove(this.instancedMesh)
120
+ }
121
+ this.geometry?.dispose()
122
+ this.material?.dispose()
123
+ this.instancedMesh = undefined
124
+ this.geometry = undefined
125
+ this.material = undefined
126
+ this.particles = []
127
+ }
128
+
129
+ private createRain(): void {
130
+ this.geometry = new THREE.BoxGeometry(0.03, 0.3, 0.03)
131
+ this.material = new THREE.MeshBasicMaterial({
132
+ color: 0x44_66_99,
133
+ transparent: true,
134
+ opacity: 0.6,
135
+ })
136
+
137
+ this.instancedMesh = new THREE.InstancedMesh(this.geometry, this.material, PARTICLE_COUNT)
138
+ this.instancedMesh.name = 'rain-particles'
139
+
140
+ const dummy = new THREE.Matrix4()
141
+ const position = new THREE.Vector3()
142
+
143
+ for (let i = 0; i < PARTICLE_COUNT; i++) {
144
+ this.respawnParticle(position)
145
+ position.y = Math.random() * HEIGHT
146
+ dummy.setPosition(position)
147
+ this.instancedMesh.setMatrixAt(i, dummy)
148
+
149
+ const speed = FALL_SPEED_MIN + Math.random() * (FALL_SPEED_MAX - FALL_SPEED_MIN)
150
+ this.particles.push({
151
+ velocity: new THREE.Vector3(
152
+ (Math.random() - 0.5) * HORIZONTAL_DRIFT,
153
+ -speed,
154
+ (Math.random() - 0.5) * HORIZONTAL_DRIFT,
155
+ ),
156
+ age: 0,
157
+ despawnOffset: Math.random() * 0.5,
158
+ })
159
+ }
160
+
161
+ this.instancedMesh.instanceMatrix.needsUpdate = true
162
+ this.worldRenderer.scene.add(this.instancedMesh)
163
+ }
164
+
165
+ private respawnParticle(position: THREE.Vector3): void {
166
+ const angle = Math.random() * Math.PI * 2
167
+ const distance = Math.random() * RANGE
168
+ position.set(
169
+ Math.cos(angle) * distance,
170
+ HEIGHT,
171
+ Math.sin(angle) * distance,
172
+ )
173
+ }
174
+ }
175
+
176
+ export const rainManifest: RendererModuleManifest = {
177
+ id: 'rain',
178
+ controller: RainModule,
179
+ enabledDefault: false,
180
+ requiresHeightmap: true,
181
+ }
@@ -99,6 +99,15 @@ export class SciFiWorldRevealModule implements RendererModuleController {
99
99
  this.reset()
100
100
  }
101
101
 
102
+ toggle(): boolean {
103
+ if (this.enabled) {
104
+ this.disable()
105
+ } else {
106
+ this.enable()
107
+ }
108
+ return this.enabled
109
+ }
110
+
102
111
  render?: () => void = () => {
103
112
  if (!this.enabled) return
104
113
  this.update(16)
@@ -56,6 +56,15 @@ export class StarfieldModule implements RendererModuleController {
56
56
  this.removeStars()
57
57
  }
58
58
 
59
+ toggle(): boolean {
60
+ if (this.enabled) {
61
+ this.disable()
62
+ } else {
63
+ this.enable()
64
+ }
65
+ return this.enabled
66
+ }
67
+
59
68
  enablementCheck?: () => boolean = () => {
60
69
  if (!this.currentTime) return false
61
70
  const nightTime = 13_500
@@ -17,10 +17,10 @@ export const renderSlot = (model: ResolvedItemModelRender, resourcesManager: Res
17
17
  modelName: string | null,
18
18
  } => {
19
19
  let itemModelName = model.modelName
20
- const isItem = loadedData.itemsByName[itemModelName]
20
+ const isItem = globalThis.loadedData.itemsByName[itemModelName]
21
21
 
22
22
  // #region normalize item name
23
- if (versionToNumber(bot.version) < versionToNumber('1.13')) itemModelName = getRenamedData(isItem ? 'items' : 'blocks', itemModelName, bot.version, '1.13.1') as string
23
+ if (versionToNumber(resourcesManager.currentResources!.version) < versionToNumber('1.13')) itemModelName = getRenamedData(isItem ? 'items' : 'blocks', itemModelName, resourcesManager.currentResources!.version, '1.13.1') as string
24
24
  // #endregion
25
25
 
26
26
 
@@ -47,18 +47,18 @@ export const renderSlot = (model: ResolvedItemModelRender, resourcesManager: Res
47
47
  const blockToTopTexture = (r) => r.top ?? r
48
48
 
49
49
  try {
50
- if (!appViewer.resourcesManager.currentResources?.itemsRenderer) throw new Error('Items renderer is not available')
50
+ if (!resourcesManager.currentResources?.itemsRenderer) throw new Error('Items renderer is not available')
51
51
  itemTexture =
52
- appViewer.resourcesManager.currentResources.itemsRenderer.getItemTexture(itemModelName, {}, false, fullBlockModelSupport)
53
- ?? (model.originalItemName ? appViewer.resourcesManager.currentResources.itemsRenderer.getItemTexture(model.originalItemName, {}, false, fullBlockModelSupport) : undefined)
54
- ?? appViewer.resourcesManager.currentResources.itemsRenderer.getItemTexture('item/missing_texture')!
52
+ resourcesManager.currentResources.itemsRenderer.getItemTexture(itemModelName, {}, false, fullBlockModelSupport)
53
+ ?? (model.originalItemName ? resourcesManager.currentResources.itemsRenderer.getItemTexture(model.originalItemName, {}, false, fullBlockModelSupport) : undefined)
54
+ ?? resourcesManager.currentResources.itemsRenderer.getItemTexture('item/missing_texture')!
55
55
  } catch (err) {
56
56
  // get resourcepack from resource manager
57
- reportError?.(`Failed to render item ${itemModelName} (original: ${model.originalItemName}) on ${bot.version} (resourcepack: TODO!): ${err.stack}`)
58
- itemTexture = blockToTopTexture(appViewer.resourcesManager.currentResources!.itemsRenderer!.getItemTexture('errored')!)
57
+ reportError?.(`Failed to render item ${itemModelName} (original: ${model.originalItemName}) on ${resourcesManager.currentResources!.version} (resourcepack: TODO!): ${err.stack}`)
58
+ itemTexture = blockToTopTexture(resourcesManager.currentResources!.itemsRenderer!.getItemTexture('errored')!)
59
59
  }
60
60
 
61
- itemTexture ??= blockToTopTexture(appViewer.resourcesManager.currentResources!.itemsRenderer!.getItemTexture('unknown')!)
61
+ itemTexture ??= blockToTopTexture(resourcesManager.currentResources!.itemsRenderer!.getItemTexture('unknown')!)
62
62
 
63
63
 
64
64
  if ('type' in itemTexture) {
@@ -29,10 +29,13 @@ export interface RendererModuleManifest {
29
29
  cannotBeDisabled?: boolean
30
30
  slowSystemAutoDisable?: boolean
31
31
  userSettingsSchema?: Record<string, any>
32
+
33
+ requiresHeightmap?: boolean
32
34
  }
33
35
 
34
36
  export interface RegisteredModule {
35
37
  manifest: RendererModuleManifest
36
38
  controller: RendererModuleController
37
39
  enabled: boolean
40
+ toggle: () => boolean
38
41
  }