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/dist/mesher.js +1 -1
- package/dist/mesher.js.map +2 -2
- package/dist/minecraft-renderer.js +53 -53
- package/dist/threeWorker.js +397 -397
- package/package.json +6 -1
- package/src/graphicsBackend/config.ts +3 -3
- package/src/graphicsBackend/types.ts +2 -2
- package/src/lib/worldrendererCommon.ts +12 -7
- package/src/mesher/mesher.ts +2 -2
- package/src/mesher/shared.ts +1 -1
- package/src/playground/allEntitiesDebug.ts +6 -4
- package/src/playground/scenes/allEntities.ts +1 -1
- package/src/playground/scenes/floorRandom.ts +1 -1
- package/src/three/appShared.ts +2 -2
- package/src/three/documentRenderer.ts +2 -2
- package/src/three/graphicsBackendBase.ts +2 -0
- package/src/three/holdingBlock.ts +1 -1
- package/src/three/modules/index.ts +2 -0
- package/src/three/modules/rain.ts +181 -0
- package/src/three/modules/sciFiWorldReveal.ts +9 -0
- package/src/three/modules/starfield.ts +9 -0
- package/src/three/renderSlot.ts +9 -9
- package/src/three/rendererModuleSystem.ts +3 -0
- package/src/three/worldRendererThree.ts +54 -19
- package/src/wasm-lib/render-from-wasm.ts +161 -161
- package/src/worldView/worldView.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "minecraft-renderer",
|
|
3
|
-
"version": "0.1.
|
|
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,
|
|
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,
|
|
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:
|
|
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
|
|
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
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
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) {
|
package/src/mesher/mesher.ts
CHANGED
|
@@ -153,7 +153,7 @@ const handleMessage = data => {
|
|
|
153
153
|
break
|
|
154
154
|
}
|
|
155
155
|
case 'getHeightmap': {
|
|
156
|
-
const heightmap = new
|
|
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 :
|
|
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 })
|
package/src/mesher/shared.ts
CHANGED
|
@@ -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:
|
|
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 = (
|
|
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
|
-
|
|
68
|
-
|
|
69
|
+
globalThis.objects ??= {}
|
|
70
|
+
globalThis.objects[entity] = entityMesh
|
|
69
71
|
|
|
70
72
|
results.push({
|
|
71
73
|
entity,
|
|
@@ -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!.
|
|
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
|
}
|
package/src/three/appShared.ts
CHANGED
|
@@ -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
|
package/src/three/renderSlot.ts
CHANGED
|
@@ -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(
|
|
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 (!
|
|
50
|
+
if (!resourcesManager.currentResources?.itemsRenderer) throw new Error('Items renderer is not available')
|
|
51
51
|
itemTexture =
|
|
52
|
-
|
|
53
|
-
?? (model.originalItemName ?
|
|
54
|
-
??
|
|
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 ${
|
|
58
|
-
itemTexture = blockToTopTexture(
|
|
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(
|
|
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
|
}
|