minecraft-renderer 0.1.42 → 0.1.44
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 +52 -52
- package/dist/mesher.js.map +3 -3
- package/dist/mesherWasm.js +51 -51
- package/dist/minecraft-renderer.js +59 -59
- package/dist/minecraft-renderer.js.meta.json +1 -1
- package/dist/threeWorker.js +458 -458
- package/package.json +1 -1
- package/src/graphicsBackend/appViewer.ts +19 -7
- package/src/graphicsBackend/types.ts +5 -1
- package/src/index.ts +33 -0
- package/src/lib/ui/newStats.ts +16 -2
- package/src/lib/worldrendererCommon.ts +3 -2
- package/src/mesher-legacy/mesher.ts +10 -1
- package/src/mesher-shared/models.ts +41 -0
- package/src/mesher-shared/shared.ts +2 -0
- package/src/three/entities.ts +22 -6
- package/src/three/graphicsBackendBase.ts +28 -20
- package/src/three/graphicsBackendOffThread.ts +1 -2
- package/src/three/menuBackground/activeView.ts +19 -0
- package/src/three/menuBackground/classic.ts +148 -0
- package/src/three/menuBackground/config.ts +23 -0
- package/src/three/menuBackground/defaultOptions.ts +141 -0
- package/src/three/menuBackground/futuristic.ts +859 -0
- package/src/three/menuBackground/index.ts +36 -0
- package/src/three/menuBackground/renderer.ts +97 -0
- package/src/three/menuBackground/shared.ts +3 -0
- package/src/three/menuBackground/types.ts +37 -0
- package/src/three/menuBackground/worldBlocks.ts +144 -0
- package/src/three/modules/sciFiWorldReveal.ts +6 -0
- package/src/three/waypointSprite.ts +108 -106
- package/src/three/worldRendererThree.ts +10 -13
- package/src/wasm-mesher/runtime-build/wasm_mesher.d.ts +25 -0
- package/src/wasm-mesher/runtime-build/wasm_mesher.js +68 -0
- package/src/wasm-mesher/runtime-build/wasm_mesher_bg.wasm +0 -0
- package/src/wasm-mesher/runtime-build/wasm_mesher_bg.wasm.d.ts +2 -0
- package/src/wasm-mesher/worker/mesherWasm.ts +14 -0
- package/src/three/panorama.ts +0 -312
- package/src/three/panoramaShared.ts +0 -2
package/package.json
CHANGED
|
@@ -27,10 +27,12 @@ import { defaultWorldRendererConfig, defaultGraphicsBackendConfig, getDefaultRen
|
|
|
27
27
|
import { PlayerStateReactive } from '../playerState/playerState'
|
|
28
28
|
import { ResourcesManager, ResourcesManagerTransferred } from '../resourcesManager'
|
|
29
29
|
import { preloadMesherWorkerScript } from './preloadWorkers'
|
|
30
|
+
import type { MenuBackgroundOptions } from '../three/menuBackground/types'
|
|
30
31
|
|
|
31
32
|
export interface AppViewerOptions {
|
|
32
33
|
config?: Partial<GraphicsBackendConfig>
|
|
33
34
|
rendererConfig?: Partial<WorldRendererConfig>
|
|
35
|
+
menuBackground?: MenuBackgroundOptions
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
/**
|
|
@@ -49,6 +51,7 @@ export class AppViewer {
|
|
|
49
51
|
|
|
50
52
|
// Configuration
|
|
51
53
|
readonly config: GraphicsBackendConfig
|
|
54
|
+
readonly menuBackgroundOptions: MenuBackgroundOptions
|
|
52
55
|
readonly inWorldRenderingConfig: WorldRendererConfig
|
|
53
56
|
|
|
54
57
|
// Backend
|
|
@@ -83,7 +86,10 @@ export class AppViewer {
|
|
|
83
86
|
...defaultGraphicsBackendConfig,
|
|
84
87
|
...options.config
|
|
85
88
|
}
|
|
86
|
-
|
|
89
|
+
this.menuBackgroundOptions = {
|
|
90
|
+
...options.config?.menuBackground,
|
|
91
|
+
...options.menuBackground
|
|
92
|
+
}
|
|
87
93
|
this.inWorldRenderingConfig = proxy({
|
|
88
94
|
...defaultWorldRendererConfig,
|
|
89
95
|
...options.rendererConfig
|
|
@@ -145,8 +151,8 @@ export class AppViewer {
|
|
|
145
151
|
|
|
146
152
|
// Execute queued action if exists
|
|
147
153
|
if (this.currentState) {
|
|
148
|
-
if (this.currentState.method === '
|
|
149
|
-
this.
|
|
154
|
+
if (this.currentState.method === 'startMenuBackground') {
|
|
155
|
+
this.startMenuBackground(...this.currentState.args)
|
|
150
156
|
} else {
|
|
151
157
|
const { method, args } = this.currentState
|
|
152
158
|
; (this.backend as any)[method](...args)
|
|
@@ -202,17 +208,23 @@ export class AppViewer {
|
|
|
202
208
|
}
|
|
203
209
|
|
|
204
210
|
/**
|
|
205
|
-
* Start
|
|
211
|
+
* Start the main-menu background (3D scene behind UI).
|
|
206
212
|
*/
|
|
207
|
-
|
|
213
|
+
startMenuBackground(menuBackgroundOptions?: MenuBackgroundOptions): void {
|
|
208
214
|
if (this.currentDisplay === 'menu') return
|
|
209
215
|
|
|
216
|
+
const merged: MenuBackgroundOptions = {
|
|
217
|
+
...this.menuBackgroundOptions,
|
|
218
|
+
...menuBackgroundOptions,
|
|
219
|
+
resourcesManager: menuBackgroundOptions?.resourcesManager ?? this.resourcesManager
|
|
220
|
+
}
|
|
221
|
+
|
|
210
222
|
if (this.backend) {
|
|
211
223
|
this.currentDisplay = 'menu'
|
|
212
|
-
this.backend.
|
|
224
|
+
this.backend.startMenuBackground(merged)
|
|
213
225
|
}
|
|
214
226
|
|
|
215
|
-
this.currentState = { method: '
|
|
227
|
+
this.currentState = { method: 'startMenuBackground', args: [merged] }
|
|
216
228
|
}
|
|
217
229
|
|
|
218
230
|
/**
|
|
@@ -22,6 +22,8 @@ export interface SoundSystem {
|
|
|
22
22
|
destroy: () => void
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
import type { MenuBackgroundOptions } from '../three/menuBackground/types'
|
|
26
|
+
|
|
25
27
|
/** Graphics backend configuration */
|
|
26
28
|
export interface GraphicsBackendConfig {
|
|
27
29
|
fpsLimit?: number
|
|
@@ -29,6 +31,8 @@ export interface GraphicsBackendConfig {
|
|
|
29
31
|
statsVisible?: number
|
|
30
32
|
sceneBackground: string
|
|
31
33
|
timeoutRendering?: boolean
|
|
34
|
+
/** Default options when `startMenuBackground()` is called without arguments */
|
|
35
|
+
menuBackground?: MenuBackgroundOptions
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
// ============================================================================
|
|
@@ -112,7 +116,7 @@ export interface DisplayWorldOptions {
|
|
|
112
116
|
export interface GraphicsBackend {
|
|
113
117
|
id: string
|
|
114
118
|
displayName: string
|
|
115
|
-
|
|
119
|
+
startMenuBackground(options?: MenuBackgroundOptions): Promise<void>
|
|
116
120
|
startWorld(options: DisplayWorldOptions): Promise<void>
|
|
117
121
|
disconnect(): void
|
|
118
122
|
setRendering(rendering: boolean): void
|
package/src/index.ts
CHANGED
|
@@ -78,3 +78,36 @@ export {
|
|
|
78
78
|
addCanvasForWorker,
|
|
79
79
|
isWebWorker
|
|
80
80
|
} from './three/documentRenderer'
|
|
81
|
+
export { MC_RENDERER_DEBUG_OVERLAY_CLASS } from './lib/ui/newStats'
|
|
82
|
+
|
|
83
|
+
// Main-menu background (title screen backdrop)
|
|
84
|
+
export type {
|
|
85
|
+
MenuBackgroundMode,
|
|
86
|
+
MenuBackgroundOptions,
|
|
87
|
+
MenuBackgroundView,
|
|
88
|
+
FuturisticSceneId,
|
|
89
|
+
FuturisticCameraId,
|
|
90
|
+
FuturisticMenuBackgroundOptions,
|
|
91
|
+
MinecraftBlockGroupId
|
|
92
|
+
} from './three/menuBackground'
|
|
93
|
+
export {
|
|
94
|
+
MenuBackgroundRenderer,
|
|
95
|
+
ClassicMenuBackground,
|
|
96
|
+
FuturisticMenuBackground,
|
|
97
|
+
WorldBlocksMenuBackground,
|
|
98
|
+
MENU_BACKGROUND_MC_VERSION,
|
|
99
|
+
FUTURISTIC_SCENE_IDS,
|
|
100
|
+
FUTURISTIC_CAMERA_IDS,
|
|
101
|
+
FUTURISTIC_SCENE_LABELS,
|
|
102
|
+
FUTURISTIC_CAMERA_LABELS,
|
|
103
|
+
MINECRAFT_BLOCK_GROUPS,
|
|
104
|
+
MINECRAFT_BLOCK_GROUP_IDS,
|
|
105
|
+
MINECRAFT_BLOCK_GROUP_LABELS,
|
|
106
|
+
RENDERER_DEFAULT_OPTIONS,
|
|
107
|
+
RENDERER_OPTIONS_META,
|
|
108
|
+
RENDERER_RENDER_GUI_SECTIONS,
|
|
109
|
+
MENU_BACKGROUND_OPTION_DEFAULTS,
|
|
110
|
+
MENU_BACKGROUND_MOTION_DEFAULTS,
|
|
111
|
+
menuBackgroundSpeedToMultiplier
|
|
112
|
+
} from './three/menuBackground'
|
|
113
|
+
export type { RendererDefaultOptionKey, RendererOptionMeta } from './three/menuBackground'
|
package/src/lib/ui/newStats.ts
CHANGED
|
@@ -7,7 +7,17 @@ const rightOffset = 0
|
|
|
7
7
|
const stats = {}
|
|
8
8
|
|
|
9
9
|
let lastY = 40
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
/** Class for advanced stats pane; host app should set z-index (see integrating app global CSS). */
|
|
12
|
+
export const MC_RENDERER_DEBUG_OVERLAY_CLASS = 'mc-renderer-debug-overlay'
|
|
13
|
+
|
|
14
|
+
export const addNewStat = (
|
|
15
|
+
id: string,
|
|
16
|
+
width = 80,
|
|
17
|
+
x = rightOffset,
|
|
18
|
+
y = lastY,
|
|
19
|
+
opts?: { className?: string },
|
|
20
|
+
) => {
|
|
11
21
|
if (isWebWorker) return { updateText() { }, setVisibility() { } }
|
|
12
22
|
|
|
13
23
|
const pane = document.createElement('div')
|
|
@@ -20,7 +30,11 @@ export const addNewStat = (id: string, width = 80, x = rightOffset, y = lastY) =
|
|
|
20
30
|
pane.style.padding = '2px'
|
|
21
31
|
pane.style.fontFamily = 'monospace'
|
|
22
32
|
pane.style.fontSize = '12px'
|
|
23
|
-
|
|
33
|
+
if (opts?.className) {
|
|
34
|
+
pane.className = opts.className
|
|
35
|
+
} else {
|
|
36
|
+
pane.style.zIndex = '100'
|
|
37
|
+
}
|
|
24
38
|
pane.style.pointerEvents = 'none'
|
|
25
39
|
document.body.appendChild(pane)
|
|
26
40
|
stats[id] = pane
|
|
@@ -11,7 +11,7 @@ import { dynamicMcDataFiles } from './buildSharedConfig.mjs'
|
|
|
11
11
|
import { DisplayWorldOptions, GraphicsInitOptions, RendererReactiveState, SoundSystem } from '../graphicsBackend/types'
|
|
12
12
|
import { HighestBlockInfo, CustomBlockModels, BlockStateModelInfo, getBlockAssetsCacheKey, MesherConfig, MesherMainEvent, SECTION_HEIGHT } from '../mesher-shared/shared'
|
|
13
13
|
import { chunkPos } from './simpleUtils'
|
|
14
|
-
import { addNewStat, removeAllStats, updatePanesVisibility, updateStatText } from './ui/newStats'
|
|
14
|
+
import { addNewStat, MC_RENDERER_DEBUG_OVERLAY_CLASS, removeAllStats, updatePanesVisibility, updateStatText } from './ui/newStats'
|
|
15
15
|
import { getPlayerStateUtils } from '../graphicsBackend/playerState'
|
|
16
16
|
// TODO: Fix PlayerStateRenderer and PlayerStateUtils imports
|
|
17
17
|
type PlayerStateUtils = ReturnType<typeof getPlayerStateUtils>
|
|
@@ -194,7 +194,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
194
194
|
updateStatText('loaded-chunks', `${loadedChunks}/${this.chunksLength} chunks (${this.lastChunkDistance}/${this.viewDistance})`)
|
|
195
195
|
})
|
|
196
196
|
|
|
197
|
-
addNewStat('downloaded-chunks', 100, 140, 20)
|
|
197
|
+
addNewStat('downloaded-chunks', 100, 140, 20, { className: MC_RENDERER_DEBUG_OVERLAY_CLASS })
|
|
198
198
|
|
|
199
199
|
this.connect(this.displayOptions.worldView as any)
|
|
200
200
|
|
|
@@ -628,6 +628,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
628
628
|
worldMinY: this.worldMinYRender,
|
|
629
629
|
worldMaxY: this.worldMinYRender + this.worldSizeParams.worldHeight,
|
|
630
630
|
disableConversionCache: this.worldRendererConfig.disableMesherConversionCache,
|
|
631
|
+
computeWireframeEdges: this.worldRendererConfig.futuristicReveal === true,
|
|
631
632
|
}
|
|
632
633
|
}
|
|
633
634
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//@ts-nocheck
|
|
2
2
|
import { Vec3 } from 'vec3'
|
|
3
3
|
import { World } from '../mesher-shared/world'
|
|
4
|
-
import { getSectionGeometry, setBlockStatesData as setMesherData } from '../mesher-shared/models'
|
|
4
|
+
import { getSectionGeometry, setBlockStatesData as setMesherData, computeWireframeEdgesJS } from '../mesher-shared/models'
|
|
5
5
|
import { BlockStateModelInfo } from '../mesher-shared/shared'
|
|
6
6
|
import { handleGetHeightmap, EMPTY_COLUMN_HEIGHTMAP_SENTINEL } from '../mesher-shared/computeHeightmap'
|
|
7
7
|
|
|
@@ -208,7 +208,16 @@ setInterval(() => {
|
|
|
208
208
|
if (chunk?.getSection(new Vec3(x, y, z))) {
|
|
209
209
|
const start = performance.now()
|
|
210
210
|
const geometry = getSectionGeometry(x, y, z, world)
|
|
211
|
+
if (geometry.positions.length > 0 && geometry.indices.length > 0 && world.config.computeWireframeEdges) {
|
|
212
|
+
const wireframeF32 = computeWireframeEdgesJS(geometry.positions as number[], geometry.indices as number[])
|
|
213
|
+
if (wireframeF32.length > 0) {
|
|
214
|
+
geometry.wireframePositions = wireframeF32
|
|
215
|
+
}
|
|
216
|
+
}
|
|
211
217
|
const transferable = [geometry.positions?.buffer, geometry.normals?.buffer, geometry.colors?.buffer, geometry.uvs?.buffer].filter(Boolean)
|
|
218
|
+
if (geometry.wireframePositions) {
|
|
219
|
+
transferable.push(geometry.wireframePositions.buffer)
|
|
220
|
+
}
|
|
212
221
|
//@ts-expect-error
|
|
213
222
|
postMessage({ type: 'geometry', key, geometry, workerIndex }, transferable)
|
|
214
223
|
processTime = performance.now() - start
|
|
@@ -794,3 +794,44 @@ export const setBlockStatesData = (blockstatesModels, blocksAtlas: any, _needTil
|
|
|
794
794
|
}
|
|
795
795
|
}
|
|
796
796
|
}
|
|
797
|
+
|
|
798
|
+
export function computeWireframeEdgesJS(positions: Float32Array | number[], indices: Uint32Array | Uint16Array | number[]): Float32Array {
|
|
799
|
+
const pos = positions instanceof Float32Array ? positions : new Float32Array(positions as number[])
|
|
800
|
+
const idx = indices instanceof Uint32Array ? indices : (indices instanceof Uint16Array ? new Uint32Array(indices) : new Uint32Array(indices as number[]))
|
|
801
|
+
|
|
802
|
+
const linePositions: number[] = []
|
|
803
|
+
const edgeSet = new Set<number>()
|
|
804
|
+
|
|
805
|
+
for (let i = 0; i < idx.length; i += 3) {
|
|
806
|
+
const i0 = idx[i]!
|
|
807
|
+
const i1 = idx[i + 1]!
|
|
808
|
+
const i2 = idx[i + 2]!
|
|
809
|
+
|
|
810
|
+
addEdgeJS(pos, i0, i1, linePositions, edgeSet)
|
|
811
|
+
addEdgeJS(pos, i1, i2, linePositions, edgeSet)
|
|
812
|
+
addEdgeJS(pos, i2, i0, linePositions, edgeSet)
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
return new Float32Array(linePositions)
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
function addEdgeJS(
|
|
819
|
+
positions: Float32Array,
|
|
820
|
+
i0: number,
|
|
821
|
+
i1: number,
|
|
822
|
+
linePositions: number[],
|
|
823
|
+
edgeSet: Set<number>
|
|
824
|
+
): void {
|
|
825
|
+
const minI = i0 < i1 ? i0 : i1
|
|
826
|
+
const maxI = i0 < i1 ? i1 : i0
|
|
827
|
+
// Pack two indices into a single number (safe while indices < 2^24 — far above per-section vertex counts).
|
|
828
|
+
const key = minI * 0x1000000 + maxI
|
|
829
|
+
|
|
830
|
+
if (edgeSet.has(key)) return
|
|
831
|
+
edgeSet.add(key)
|
|
832
|
+
|
|
833
|
+
linePositions.push(
|
|
834
|
+
positions[i0 * 3]!, positions[i0 * 3 + 1]!, positions[i0 * 3 + 2]!,
|
|
835
|
+
positions[i1 * 3]!, positions[i1 * 3 + 1]!, positions[i1 * 3 + 2]!
|
|
836
|
+
)
|
|
837
|
+
}
|
|
@@ -19,6 +19,7 @@ export const defaultMesherConfig = {
|
|
|
19
19
|
clipWorldBelowY: undefined as undefined | number,
|
|
20
20
|
disableBlockEntityTextures: false,
|
|
21
21
|
disableConversionCache: false,
|
|
22
|
+
computeWireframeEdges: false,
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export type CustomBlockModels = {
|
|
@@ -60,6 +61,7 @@ export type MesherGeometryOutput = {
|
|
|
60
61
|
// isFull: boolean
|
|
61
62
|
hadErrors: boolean
|
|
62
63
|
blocksCount: number
|
|
64
|
+
wireframePositions?: Float32Array
|
|
63
65
|
customBlockModels?: CustomBlockModels
|
|
64
66
|
}
|
|
65
67
|
|
package/src/three/entities.ts
CHANGED
|
@@ -108,6 +108,14 @@ function poseToEuler(pose: any, defaultValue?: THREE.Euler) {
|
|
|
108
108
|
return defaultValue ?? new THREE.Euler()
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
const TAU_YAW = Math.PI * 2
|
|
112
|
+
|
|
113
|
+
/** Prismarine yaw in radians → shortest delta from→to in (-π, π]. */
|
|
114
|
+
function shortestYawRadians(fromYawRad: number, toYawRad: number): number {
|
|
115
|
+
const norm = ((toYawRad - fromYawRad) % TAU_YAW + TAU_YAW) % TAU_YAW
|
|
116
|
+
return norm > Math.PI ? norm - TAU_YAW : norm
|
|
117
|
+
}
|
|
118
|
+
|
|
111
119
|
function getUsernameTexture({
|
|
112
120
|
username,
|
|
113
121
|
nameTagBackgroundColor = 'rgba(0, 0, 0, 0.3)',
|
|
@@ -1366,17 +1374,25 @@ export class Entities {
|
|
|
1366
1374
|
})
|
|
1367
1375
|
.start()
|
|
1368
1376
|
}
|
|
1369
|
-
if (entity.yaw) {
|
|
1370
|
-
const
|
|
1371
|
-
const dy = 2 * da % (Math.PI * 2) - da
|
|
1377
|
+
if (typeof entity.yaw === 'number' && Number.isFinite(entity.yaw)) {
|
|
1378
|
+
const dy = shortestYawRadians(e.rotation.y, entity.yaw)
|
|
1372
1379
|
new TWEEN.Tween(e.rotation).to({ y: e.rotation.y + dy }, ANIMATION_DURATION).start()
|
|
1373
1380
|
}
|
|
1374
1381
|
|
|
1375
1382
|
if (e?.playerObject && overrides?.rotation?.head) {
|
|
1376
1383
|
const { playerObject } = e
|
|
1377
|
-
const
|
|
1378
|
-
|
|
1379
|
-
|
|
1384
|
+
const hy = overrides.rotation.head.y
|
|
1385
|
+
const headYawWorld =
|
|
1386
|
+
typeof hy === 'number' && Number.isFinite(hy) ? hy : entity.yaw
|
|
1387
|
+
const headYawOffset =
|
|
1388
|
+
typeof headYawWorld === 'number' && typeof entity.yaw === 'number' && Number.isFinite(headYawWorld) && Number.isFinite(entity.yaw)
|
|
1389
|
+
? shortestYawRadians(entity.yaw, headYawWorld)
|
|
1390
|
+
: 0
|
|
1391
|
+
playerObject.skin.head.rotation.y = headYawOffset
|
|
1392
|
+
|
|
1393
|
+
const hp = overrides.rotation.head.x
|
|
1394
|
+
playerObject.skin.head.rotation.x =
|
|
1395
|
+
typeof hp === 'number' && Number.isFinite(hp) ? -hp : 0
|
|
1380
1396
|
}
|
|
1381
1397
|
}
|
|
1382
1398
|
|
|
@@ -14,7 +14,8 @@ import { ResourcesManager } from '../resourcesManager'
|
|
|
14
14
|
import { FrameTimingCollector } from '../lib/frameTimingCollector'
|
|
15
15
|
import { WorldRendererThree } from './worldRendererThree'
|
|
16
16
|
import { DocumentRenderer, isWebWorker, ThreeRendererMainData } from './documentRenderer'
|
|
17
|
-
import {
|
|
17
|
+
import { MenuBackgroundRenderer } from './menuBackground'
|
|
18
|
+
import type { MenuBackgroundOptions } from './menuBackground/types'
|
|
18
19
|
import { WorldViewWorker } from '../worldView'
|
|
19
20
|
import type { FeedChunkPacketPayload } from '../worldView/types'
|
|
20
21
|
|
|
@@ -127,7 +128,7 @@ export const createGraphicsBackendBase = () => {
|
|
|
127
128
|
// Private state
|
|
128
129
|
let initOptions!: GraphicsInitOptions
|
|
129
130
|
let documentRenderer: DocumentRenderer | null = null
|
|
130
|
-
let
|
|
131
|
+
let menuBackgroundRenderer: MenuBackgroundRenderer | null = null
|
|
131
132
|
let worldRenderer: WorldRendererThree | null = null
|
|
132
133
|
let frameTimingCollector: FrameTimingCollector | null = null
|
|
133
134
|
|
|
@@ -146,19 +147,26 @@ export const createGraphicsBackendBase = () => {
|
|
|
146
147
|
callModsMethod('default', backend)
|
|
147
148
|
}
|
|
148
149
|
|
|
149
|
-
const
|
|
150
|
+
const startMenuBackground = async (menuBackgroundStartOptions?: MenuBackgroundOptions) => {
|
|
150
151
|
if (!documentRenderer) throw new Error('Document renderer not initialized')
|
|
151
152
|
if (worldRenderer) return
|
|
152
153
|
|
|
153
|
-
if (!
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
154
|
+
if (!menuBackgroundRenderer) {
|
|
155
|
+
const mergedOptions: MenuBackgroundOptions = {
|
|
156
|
+
...initOptions.config.menuBackground,
|
|
157
|
+
...menuBackgroundStartOptions
|
|
158
|
+
}
|
|
159
|
+
menuBackgroundRenderer = new MenuBackgroundRenderer(
|
|
160
|
+
documentRenderer,
|
|
161
|
+
{ ...initOptions },
|
|
162
|
+
mergedOptions,
|
|
163
|
+
!!process.env.SINGLE_FILE_BUILD_MODE
|
|
164
|
+
)
|
|
165
|
+
; (globalThis as any).menuBackgroundRenderer = menuBackgroundRenderer
|
|
166
|
+
|
|
167
|
+
callModsMethod('menuBackgroundCreated', menuBackgroundRenderer)
|
|
168
|
+
await menuBackgroundRenderer.start(mergedOptions)
|
|
169
|
+
callModsMethod('menuBackgroundReady', menuBackgroundRenderer)
|
|
162
170
|
}
|
|
163
171
|
}
|
|
164
172
|
|
|
@@ -172,9 +180,9 @@ export const createGraphicsBackendBase = () => {
|
|
|
172
180
|
// Set resourcesManager globally for world rendering
|
|
173
181
|
; (globalThis as any).resourcesManager = displayOptions.resourcesManager
|
|
174
182
|
|
|
175
|
-
if (
|
|
176
|
-
|
|
177
|
-
|
|
183
|
+
if (menuBackgroundRenderer) {
|
|
184
|
+
menuBackgroundRenderer.dispose()
|
|
185
|
+
menuBackgroundRenderer = null
|
|
178
186
|
}
|
|
179
187
|
|
|
180
188
|
worldRenderer = new WorldRendererThree(documentRenderer.renderer, initOptions, displayOptions)
|
|
@@ -206,9 +214,9 @@ export const createGraphicsBackendBase = () => {
|
|
|
206
214
|
}
|
|
207
215
|
|
|
208
216
|
const disconnect = () => {
|
|
209
|
-
if (
|
|
210
|
-
|
|
211
|
-
|
|
217
|
+
if (menuBackgroundRenderer) {
|
|
218
|
+
menuBackgroundRenderer.dispose()
|
|
219
|
+
menuBackgroundRenderer = null
|
|
212
220
|
}
|
|
213
221
|
|
|
214
222
|
if (documentRenderer) {
|
|
@@ -225,7 +233,7 @@ export const createGraphicsBackendBase = () => {
|
|
|
225
233
|
const backend: GraphicsBackend = {
|
|
226
234
|
id: 'threejs',
|
|
227
235
|
displayName: `three.js ${THREE.REVISION}`,
|
|
228
|
-
|
|
236
|
+
startMenuBackground,
|
|
229
237
|
startWorld,
|
|
230
238
|
disconnect,
|
|
231
239
|
setRendering(rendering) {
|
|
@@ -269,7 +277,7 @@ export const createGraphicsBackendBase = () => {
|
|
|
269
277
|
updateSizeExternal(width: number, height: number, pixelRatio: number) {
|
|
270
278
|
documentRenderer?.updateSizeExternal(width, height, pixelRatio)
|
|
271
279
|
},
|
|
272
|
-
|
|
280
|
+
startMenuBackground,
|
|
273
281
|
startWorld,
|
|
274
282
|
disconnect,
|
|
275
283
|
setRendering: backend.setRendering,
|
|
@@ -58,8 +58,7 @@ export const createGraphicsBackendOffThread: GraphicsBackendLoader = async (init
|
|
|
58
58
|
const backend: GraphicsBackend = {
|
|
59
59
|
id: 'threejs',
|
|
60
60
|
displayName: `three.js ${THREE.REVISION}`,
|
|
61
|
-
|
|
62
|
-
async startPanorama() { },
|
|
61
|
+
async startMenuBackground() { },
|
|
63
62
|
async startWorld(options) {
|
|
64
63
|
const workerThreeSendData = {
|
|
65
64
|
...dynamicMcDataFiles,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
import * as THREE from 'three'
|
|
3
|
+
|
|
4
|
+
/** Contract for a main-menu background implementation (classic cubemap, futuristic scene, etc.). */
|
|
5
|
+
export interface MenuBackgroundView {
|
|
6
|
+
readonly scene: THREE.Scene
|
|
7
|
+
readonly camera: THREE.PerspectiveCamera
|
|
8
|
+
init(): Promise<void>
|
|
9
|
+
update(dt: number, sizeChanged: boolean): void
|
|
10
|
+
dispose(): void
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function resizeMenuBackgroundCamera(
|
|
14
|
+
camera: THREE.PerspectiveCamera,
|
|
15
|
+
canvas: { width: number, height: number }
|
|
16
|
+
) {
|
|
17
|
+
camera.aspect = canvas.width / canvas.height
|
|
18
|
+
camera.updateProjectionMatrix()
|
|
19
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
import { join } from 'path'
|
|
3
|
+
import * as THREE from 'three'
|
|
4
|
+
import { EntityMesh } from '../entity/EntityMesh'
|
|
5
|
+
import type { DocumentRenderer } from '../documentRenderer'
|
|
6
|
+
import { loadThreeJsTextureFromUrl, loadThreeJsTextureFromUrlSync } from '../threeJsUtils'
|
|
7
|
+
import type { MenuBackgroundView } from './activeView'
|
|
8
|
+
import { resizeMenuBackgroundCamera } from './activeView'
|
|
9
|
+
|
|
10
|
+
const date = new Date()
|
|
11
|
+
const isChristmas = date.getMonth() === 11 && date.getDate() >= 24 && date.getDate() <= 26
|
|
12
|
+
|
|
13
|
+
const panoramaFiles = [
|
|
14
|
+
'panorama_3.webp', // right (+x)
|
|
15
|
+
'panorama_1.webp', // left (-x)
|
|
16
|
+
'panorama_4.webp', // top (+y)
|
|
17
|
+
'panorama_5.webp', // bottom (-y)
|
|
18
|
+
'panorama_0.webp', // front (+z)
|
|
19
|
+
'panorama_2.webp', // back (-z)
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
const FADE_IN_DURATION_MS = 200
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Vanilla-style rotating cubemap (Minecraft title-screen style) with optional squids.
|
|
26
|
+
*/
|
|
27
|
+
export class ClassicMenuBackground implements MenuBackgroundView {
|
|
28
|
+
readonly scene: THREE.Scene
|
|
29
|
+
readonly camera: THREE.PerspectiveCamera
|
|
30
|
+
|
|
31
|
+
private readonly startTimes = new Map<THREE.MeshBasicMaterial, number>()
|
|
32
|
+
private time = 0
|
|
33
|
+
private panoramaGroup: THREE.Object3D | null = null
|
|
34
|
+
|
|
35
|
+
constructor(private readonly documentRenderer: DocumentRenderer) {
|
|
36
|
+
this.scene = new THREE.Scene()
|
|
37
|
+
this.scene.background = new THREE.Color(0x32_45_68)
|
|
38
|
+
|
|
39
|
+
const ambient = new THREE.AmbientLight(0xcc_cc_cc)
|
|
40
|
+
this.scene.add(ambient)
|
|
41
|
+
const directional = new THREE.DirectionalLight(0xff_ff_ff, 0.5)
|
|
42
|
+
directional.position.set(1, 1, 0.5).normalize()
|
|
43
|
+
directional.castShadow = true
|
|
44
|
+
this.scene.add(directional)
|
|
45
|
+
|
|
46
|
+
this.camera = new THREE.PerspectiveCamera(
|
|
47
|
+
85,
|
|
48
|
+
documentRenderer.canvas.width / documentRenderer.canvas.height,
|
|
49
|
+
0.05,
|
|
50
|
+
1000
|
|
51
|
+
)
|
|
52
|
+
this.camera.position.set(0, 0, 0)
|
|
53
|
+
this.camera.rotation.set(0, 0, 0)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async init() {
|
|
57
|
+
this.buildCubemap()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
update(_dt: number, sizeChanged: boolean) {
|
|
61
|
+
if (sizeChanged) {
|
|
62
|
+
resizeMenuBackgroundCamera(this.camera, this.documentRenderer.canvas)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
dispose() {
|
|
67
|
+
this.scene.clear()
|
|
68
|
+
this.panoramaGroup = null
|
|
69
|
+
this.startTimes.clear()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private buildCubemap() {
|
|
73
|
+
const panorGeo = new THREE.BoxGeometry(1000, 1000, 1000)
|
|
74
|
+
const panorMaterials: THREE.MeshBasicMaterial[] = []
|
|
75
|
+
|
|
76
|
+
for (const file of panoramaFiles) {
|
|
77
|
+
const load = async () => {
|
|
78
|
+
const { texture } = loadThreeJsTextureFromUrlSync(join('background', isChristmas ? 'christmas' : '', file))
|
|
79
|
+
|
|
80
|
+
texture.matrixAutoUpdate = false
|
|
81
|
+
texture.matrix.set(-1, 0, 1, 0, 1, 0, 0, 0, 1)
|
|
82
|
+
texture.wrapS = THREE.ClampToEdgeWrapping
|
|
83
|
+
texture.wrapT = THREE.ClampToEdgeWrapping
|
|
84
|
+
texture.minFilter = THREE.LinearFilter
|
|
85
|
+
texture.magFilter = THREE.LinearFilter
|
|
86
|
+
|
|
87
|
+
const material = new THREE.MeshBasicMaterial({
|
|
88
|
+
map: texture,
|
|
89
|
+
transparent: true,
|
|
90
|
+
side: THREE.DoubleSide,
|
|
91
|
+
depthWrite: false,
|
|
92
|
+
opacity: 0
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
this.startTimes.set(material, Date.now())
|
|
96
|
+
panorMaterials.push(material)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
void load()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const panoramaBox = new THREE.Mesh(panorGeo, panorMaterials)
|
|
103
|
+
panoramaBox.onBeforeRender = () => {
|
|
104
|
+
this.time += 0.01
|
|
105
|
+
panoramaBox.rotation.y = Math.PI + this.time * 0.01
|
|
106
|
+
panoramaBox.rotation.z = Math.sin(-this.time * 0.001) * 0.001
|
|
107
|
+
|
|
108
|
+
for (const material of panorMaterials) {
|
|
109
|
+
const startTime = this.startTimes.get(material)
|
|
110
|
+
if (startTime) {
|
|
111
|
+
const elapsed = Date.now() - startTime
|
|
112
|
+
material.opacity = Math.min(1, elapsed / FADE_IN_DURATION_MS)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const group = new THREE.Object3D()
|
|
118
|
+
group.add(panoramaBox)
|
|
119
|
+
|
|
120
|
+
if (!isChristmas) {
|
|
121
|
+
for (let i = 0; i < 20; i++) {
|
|
122
|
+
const m = new EntityMesh('1.16.4', 'squid').mesh
|
|
123
|
+
m.position.set(Math.random() * 30 - 15, Math.random() * 20 - 10, Math.random() * 10 - 17)
|
|
124
|
+
m.rotation.set(0, Math.PI + Math.random(), -Math.PI / 4, 'ZYX')
|
|
125
|
+
const v = Math.random() * 0.01
|
|
126
|
+
m.children[0].onBeforeRender = () => {
|
|
127
|
+
m.rotation.y += v
|
|
128
|
+
m.rotation.z = Math.cos(panoramaBox.rotation.y * 3) * Math.PI / 4 - Math.PI / 2
|
|
129
|
+
}
|
|
130
|
+
group.add(m)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this.scene.add(group)
|
|
135
|
+
this.panoramaGroup = group
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Debug helper: flat cubemap face in front of the camera. */
|
|
139
|
+
async debugImageInFrontOfCamera() {
|
|
140
|
+
const image = await loadThreeJsTextureFromUrl(join('background', 'panorama_0.webp'))
|
|
141
|
+
const mesh = new THREE.Mesh(
|
|
142
|
+
new THREE.PlaneGeometry(1000, 1000),
|
|
143
|
+
new THREE.MeshBasicMaterial({ map: image })
|
|
144
|
+
)
|
|
145
|
+
mesh.position.set(0, 0, -500)
|
|
146
|
+
this.scene.add(mesh)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
import type { MenuBackgroundMode } from './types'
|
|
3
|
+
import type { FuturisticCameraId, FuturisticSceneId, MinecraftBlockGroupId } from './futuristic'
|
|
4
|
+
|
|
5
|
+
/** Single source of truth for menu-background defaults (settings + runtime fallbacks). */
|
|
6
|
+
export const MENU_BACKGROUND_OPTION_DEFAULTS = {
|
|
7
|
+
mode: 'futuristic' as MenuBackgroundMode,
|
|
8
|
+
minecraftTextures: true,
|
|
9
|
+
futuristicScene: 'light' as FuturisticSceneId,
|
|
10
|
+
futuristicCamera: 'dive' as FuturisticCameraId,
|
|
11
|
+
futuristicBlockGroup: 'stainedGlass' as MinecraftBlockGroupId,
|
|
12
|
+
/** 0–200 (%). 100 = 1× motion. */
|
|
13
|
+
futuristicCameraSpeedPercent: 80,
|
|
14
|
+
futuristicBlockSpeedPercent: 40
|
|
15
|
+
} as const
|
|
16
|
+
|
|
17
|
+
export const menuBackgroundSpeedToMultiplier = (percent: number) => percent / 100
|
|
18
|
+
|
|
19
|
+
/** Default camera / block motion multipliers (1 = 100%). */
|
|
20
|
+
export const MENU_BACKGROUND_MOTION_DEFAULTS = {
|
|
21
|
+
camera: menuBackgroundSpeedToMultiplier(MENU_BACKGROUND_OPTION_DEFAULTS.futuristicCameraSpeedPercent),
|
|
22
|
+
block: menuBackgroundSpeedToMultiplier(MENU_BACKGROUND_OPTION_DEFAULTS.futuristicBlockSpeedPercent)
|
|
23
|
+
} as const
|