minecraft-renderer 0.1.46 → 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.
- package/README.md +22 -0
- package/dist/mesher.js +17 -17
- package/dist/mesher.js.map +4 -4
- package/dist/mesherWasm.js +18 -18
- package/dist/minecraft-renderer.js +54 -54
- package/dist/minecraft-renderer.js.meta.json +1 -1
- package/dist/threeWorker.js +384 -384
- package/package.json +1 -1
- package/src/graphicsBackend/appViewer.ts +10 -0
- package/src/graphicsBackend/config.ts +0 -1
- package/src/graphicsBackend/index.ts +1 -0
- package/src/graphicsBackend/rendererOptionsSync.ts +243 -0
- package/src/graphicsBackend/types.ts +6 -1
- package/src/mesher-shared/blockPropertiesForMeshing.ts +79 -0
- package/src/mesher-shared/exportedGeometryTypes.ts +26 -0
- package/src/mesher-shared/models.ts +3 -79
- package/src/three/documentRenderer.ts +5 -9
- package/src/three/graphicsBackendBase.ts +1 -2
- package/src/three/menuBackground/defaultOptions.ts +188 -30
- package/src/three/menuBackground/index.ts +10 -2
- package/src/three/worldGeometryExport.ts +3 -24
- package/src/wasm-mesher/bridge/render-from-wasm.ts +5 -10
package/package.json
CHANGED
|
@@ -28,6 +28,7 @@ import { PlayerStateReactive } from '../playerState/playerState'
|
|
|
28
28
|
import { ResourcesManager, ResourcesManagerTransferred } from '../resourcesManager'
|
|
29
29
|
import { preloadMesherWorkerScript } from './preloadWorkers'
|
|
30
30
|
import type { MenuBackgroundOptions } from '../three/menuBackground/types'
|
|
31
|
+
import type { RendererStorageOptions } from '../three/menuBackground/defaultOptions'
|
|
31
32
|
|
|
32
33
|
export interface AppViewerOptions {
|
|
33
34
|
config?: Partial<GraphicsBackendConfig>
|
|
@@ -81,6 +82,9 @@ export class AppViewer {
|
|
|
81
82
|
// Timing
|
|
82
83
|
lastCamUpdate = 0
|
|
83
84
|
|
|
85
|
+
/** Bound by `subscribeRendererOptions` / `bindRendererOptions` — source of truth for renderer-owned settings. */
|
|
86
|
+
private getRendererOptions?: () => RendererStorageOptions
|
|
87
|
+
|
|
84
88
|
constructor(options: AppViewerOptions = {}, public resourcesManager: ResourcesManager = new ResourcesManager()) {
|
|
85
89
|
this.config = {
|
|
86
90
|
...defaultGraphicsBackendConfig,
|
|
@@ -117,6 +121,11 @@ export class AppViewer {
|
|
|
117
121
|
return preloadMesherWorkerScript({ script })
|
|
118
122
|
}
|
|
119
123
|
|
|
124
|
+
/** Wire app options storage (valtio proxy) for backend init (WebGL gpuPreference, etc.). */
|
|
125
|
+
bindRendererOptions(getOptions: () => RendererStorageOptions): void {
|
|
126
|
+
this.getRendererOptions = getOptions
|
|
127
|
+
}
|
|
128
|
+
|
|
120
129
|
/**
|
|
121
130
|
* Load a graphics backend.
|
|
122
131
|
*/
|
|
@@ -132,6 +141,7 @@ export class AppViewer {
|
|
|
132
141
|
|
|
133
142
|
const loaderOptions: GraphicsInitOptions = {
|
|
134
143
|
config: this.config,
|
|
144
|
+
getRendererOptions: this.getRendererOptions,
|
|
135
145
|
callbacks: {
|
|
136
146
|
displayCriticalError: (error) => {
|
|
137
147
|
console.error('[AppViewer] Critical error:', error)
|
|
@@ -89,7 +89,6 @@ export type WorldRendererConfig = typeof defaultWorldRendererConfig
|
|
|
89
89
|
*/
|
|
90
90
|
export const defaultGraphicsBackendConfig: GraphicsBackendConfig = {
|
|
91
91
|
fpsLimit: undefined,
|
|
92
|
-
powerPreference: undefined,
|
|
93
92
|
sceneBackground: 'lightblue',
|
|
94
93
|
timeoutRendering: false
|
|
95
94
|
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Maps app options storage → AppViewer runtime (in-world config, graphics config, menu background).
|
|
4
|
+
* Call `subscribeRendererOptions` once after viewer init; keep volume sync in the app.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { subscribe } from 'valtio/vanilla'
|
|
8
|
+
import type { AppViewer } from './appViewer'
|
|
9
|
+
import type { RendererStorageOptions } from '../three/menuBackground/defaultOptions'
|
|
10
|
+
import type { MenuBackgroundOptions } from '../three/menuBackground/types'
|
|
11
|
+
import type { MenuBackgroundRenderer } from '../three/menuBackground/renderer'
|
|
12
|
+
import { menuBackgroundSpeedToMultiplier } from '../three/menuBackground/config'
|
|
13
|
+
import type { FuturisticCameraId, FuturisticSceneId, MinecraftBlockGroupId } from '../three/menuBackground/futuristic'
|
|
14
|
+
import { setSkinsConfig } from '../lib/utils/skins'
|
|
15
|
+
|
|
16
|
+
export type { RendererStorageOptions } from '../three/menuBackground/defaultOptions'
|
|
17
|
+
|
|
18
|
+
export interface ApplyRendererOptionsContext {
|
|
19
|
+
isSafari?: boolean
|
|
20
|
+
isCypress?: boolean
|
|
21
|
+
windowFocused?: boolean
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface RendererOptionsSubscribeHooks {
|
|
25
|
+
isSafari?: boolean
|
|
26
|
+
isCypress?: boolean
|
|
27
|
+
getWindowFocused?: () => boolean
|
|
28
|
+
onRegisterFocusHandlers?: (handlers: { onFocus: () => void, onBlur: () => void }) => void
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface RendererWorldViewLike {
|
|
32
|
+
keepChunksDistance: number
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function menuBackgroundOptionsFromStorage(o: Pick<
|
|
36
|
+
RendererStorageOptions,
|
|
37
|
+
| 'menuBackgroundMode'
|
|
38
|
+
| 'menuBackgroundMinecraftTextures'
|
|
39
|
+
| 'menuBackgroundFuturisticScene'
|
|
40
|
+
| 'menuBackgroundFuturisticCamera'
|
|
41
|
+
| 'menuBackgroundFuturisticBlockGroup'
|
|
42
|
+
| 'menuBackgroundFuturisticCameraSpeed'
|
|
43
|
+
| 'menuBackgroundFuturisticBlockSpeed'
|
|
44
|
+
>): MenuBackgroundOptions {
|
|
45
|
+
return {
|
|
46
|
+
mode: o.menuBackgroundMode as MenuBackgroundOptions['mode'],
|
|
47
|
+
useMinecraftTextures: o.menuBackgroundMinecraftTextures,
|
|
48
|
+
futuristicScene: o.menuBackgroundFuturisticScene as FuturisticSceneId,
|
|
49
|
+
futuristicCamera: o.menuBackgroundFuturisticCamera as FuturisticCameraId,
|
|
50
|
+
futuristicBlockGroup: o.menuBackgroundFuturisticBlockGroup as MinecraftBlockGroupId,
|
|
51
|
+
futuristicCameraSpeed: menuBackgroundSpeedToMultiplier(o.menuBackgroundFuturisticCameraSpeed),
|
|
52
|
+
futuristicBlockSpeed: menuBackgroundSpeedToMultiplier(o.menuBackgroundFuturisticBlockSpeed),
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function applyMenuBackgroundLiveOptions(
|
|
57
|
+
menu: MenuBackgroundRenderer,
|
|
58
|
+
o: Pick<
|
|
59
|
+
RendererStorageOptions,
|
|
60
|
+
| 'menuBackgroundFuturisticScene'
|
|
61
|
+
| 'menuBackgroundFuturisticCamera'
|
|
62
|
+
| 'menuBackgroundFuturisticBlockGroup'
|
|
63
|
+
| 'menuBackgroundFuturisticCameraSpeed'
|
|
64
|
+
| 'menuBackgroundFuturisticBlockSpeed'
|
|
65
|
+
>
|
|
66
|
+
): void {
|
|
67
|
+
const futuristic = menu.futuristic
|
|
68
|
+
if (!futuristic) return
|
|
69
|
+
futuristic.setScene?.(o.menuBackgroundFuturisticScene)
|
|
70
|
+
futuristic.setCamera?.(o.menuBackgroundFuturisticCamera)
|
|
71
|
+
void futuristic.setBlockGroup?.(o.menuBackgroundFuturisticBlockGroup)
|
|
72
|
+
futuristic.setCameraSpeed?.(menuBackgroundSpeedToMultiplier(o.menuBackgroundFuturisticCameraSpeed))
|
|
73
|
+
futuristic.setBlockSpeed?.(menuBackgroundSpeedToMultiplier(o.menuBackgroundFuturisticBlockSpeed))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function resolveWasmMesherActive(o: RendererStorageOptions): boolean {
|
|
77
|
+
return o.rendererMesher !== 'legacy-js'
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function applyMesherWorkersPreset(
|
|
81
|
+
appViewer: AppViewer,
|
|
82
|
+
o: RendererStorageOptions,
|
|
83
|
+
wasmActive: boolean
|
|
84
|
+
): void {
|
|
85
|
+
const cfg = appViewer.inWorldRenderingConfig
|
|
86
|
+
const override = o.rendererMeshersCountOverride
|
|
87
|
+
const applyMesherWorkers = (workers: number) => {
|
|
88
|
+
cfg.mesherWorkers = override ?? workers
|
|
89
|
+
}
|
|
90
|
+
switch (o.rendererWorldPerformance) {
|
|
91
|
+
case 'low-energy':
|
|
92
|
+
applyMesherWorkers(1)
|
|
93
|
+
cfg.dedicatedChangeWorker = false
|
|
94
|
+
break
|
|
95
|
+
case 'normal':
|
|
96
|
+
applyMesherWorkers(2)
|
|
97
|
+
cfg.dedicatedChangeWorker = !wasmActive
|
|
98
|
+
break
|
|
99
|
+
case 'maximum':
|
|
100
|
+
applyMesherWorkers(Math.max(3, Math.min(navigator.hardwareConcurrency ?? 0, 8)))
|
|
101
|
+
cfg.dedicatedChangeWorker = !wasmActive
|
|
102
|
+
break
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function applyFpsLimit(
|
|
107
|
+
appViewer: AppViewer,
|
|
108
|
+
o: RendererStorageOptions,
|
|
109
|
+
windowFocused: boolean
|
|
110
|
+
): void {
|
|
111
|
+
const backgroundFpsLimit = o.backgroundRendering
|
|
112
|
+
const normalFpsLimit = o.frameLimit
|
|
113
|
+
|
|
114
|
+
if (windowFocused) {
|
|
115
|
+
appViewer.config.fpsLimit = normalFpsLimit || undefined
|
|
116
|
+
} else if (backgroundFpsLimit === '5fps') {
|
|
117
|
+
appViewer.config.fpsLimit = 5
|
|
118
|
+
} else if (backgroundFpsLimit === '20fps') {
|
|
119
|
+
appViewer.config.fpsLimit = 20
|
|
120
|
+
} else {
|
|
121
|
+
appViewer.config.fpsLimit = undefined
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function applyStatsVisible(
|
|
126
|
+
appViewer: AppViewer,
|
|
127
|
+
o: RendererStorageOptions,
|
|
128
|
+
ctx: ApplyRendererOptionsContext
|
|
129
|
+
): void {
|
|
130
|
+
const { renderDebug } = o
|
|
131
|
+
if (renderDebug === 'none' || ctx.isCypress) {
|
|
132
|
+
appViewer.config.statsVisible = 0
|
|
133
|
+
} else if (renderDebug === 'basic') {
|
|
134
|
+
appViewer.config.statsVisible = 1
|
|
135
|
+
} else if (renderDebug === 'advanced') {
|
|
136
|
+
appViewer.config.statsVisible = 2
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ensure no object assigns to the config
|
|
141
|
+
export function applyRendererOptions(
|
|
142
|
+
appViewer: AppViewer,
|
|
143
|
+
o: RendererStorageOptions,
|
|
144
|
+
ctx: ApplyRendererOptionsContext = {}
|
|
145
|
+
): void {
|
|
146
|
+
const cfg = appViewer.inWorldRenderingConfig
|
|
147
|
+
const wasmActive = resolveWasmMesherActive(o)
|
|
148
|
+
|
|
149
|
+
cfg.showChunkBorders = o.showChunkBorders
|
|
150
|
+
cfg.futuristicReveal = o.rendererFuturisticReveal
|
|
151
|
+
applyMesherWorkersPreset(appViewer, o, wasmActive)
|
|
152
|
+
cfg.renderEntities = o.renderEntities
|
|
153
|
+
applyStatsVisible(appViewer, o, ctx)
|
|
154
|
+
applyFpsLimit(appViewer, o, ctx.windowFocused !== false)
|
|
155
|
+
|
|
156
|
+
cfg.vrSupport = o.vrSupport
|
|
157
|
+
cfg.vrPageGameRendering = o.vrPageGameRendering
|
|
158
|
+
cfg.enableDebugOverlay = o.rendererPerfDebugOverlay
|
|
159
|
+
|
|
160
|
+
cfg.clipWorldBelowY = o.clipWorldBelowY
|
|
161
|
+
cfg.extraBlockRenderers = !o.disableBlockEntityTextures
|
|
162
|
+
cfg.fetchPlayerSkins = o.loadPlayerSkins
|
|
163
|
+
cfg.highlightBlockColor = o.highlightBlockColor
|
|
164
|
+
cfg.wasmMesher = wasmActive
|
|
165
|
+
cfg.disableMesherConversionCache = !!ctx.isSafari
|
|
166
|
+
|
|
167
|
+
setSkinsConfig({ apiEnabled: o.loadPlayerSkins })
|
|
168
|
+
|
|
169
|
+
cfg.smoothLighting = o.smoothLighting
|
|
170
|
+
cfg.shadingTheme = o.vanillaLook ? 'vanilla' : 'high-contrast'
|
|
171
|
+
cfg.starfield = o.starfieldRendering
|
|
172
|
+
cfg.defaultSkybox = o.defaultSkybox
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** World-view + hand/camera options (call when WorldView is ready). */
|
|
176
|
+
export function applyRendererWorldViewOptions(
|
|
177
|
+
appViewer: AppViewer,
|
|
178
|
+
worldView: RendererWorldViewLike,
|
|
179
|
+
o: Pick<
|
|
180
|
+
RendererStorageOptions,
|
|
181
|
+
'keepChunksDistance' | 'renderEars' | 'showHand' | 'viewBobbing' | 'dayCycleAndLighting'
|
|
182
|
+
>
|
|
183
|
+
): void {
|
|
184
|
+
worldView.keepChunksDistance = o.keepChunksDistance
|
|
185
|
+
const cfg = appViewer.inWorldRenderingConfig
|
|
186
|
+
cfg.renderEars = o.renderEars
|
|
187
|
+
cfg.showHand = o.showHand
|
|
188
|
+
cfg.viewBobbing = o.viewBobbing
|
|
189
|
+
cfg.dayCycle = o.dayCycleAndLighting
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Subscribe to options changes and sync renderer runtime.
|
|
194
|
+
* Returns unsubscribe. Volume is intentionally excluded — wire it in the app.
|
|
195
|
+
*/
|
|
196
|
+
export function subscribeRendererOptions<T extends RendererStorageOptions>(
|
|
197
|
+
appViewer: AppViewer,
|
|
198
|
+
optionsProxy: T,
|
|
199
|
+
hooks: RendererOptionsSubscribeHooks = {}
|
|
200
|
+
): () => void {
|
|
201
|
+
appViewer.bindRendererOptions(() => optionsProxy as RendererStorageOptions)
|
|
202
|
+
|
|
203
|
+
let windowFocused = hooks.getWindowFocused?.() ?? true
|
|
204
|
+
|
|
205
|
+
const run = () => {
|
|
206
|
+
const snapshot = optionsProxy as RendererStorageOptions
|
|
207
|
+
applyRendererOptions(appViewer, snapshot, {
|
|
208
|
+
isSafari: hooks.isSafari,
|
|
209
|
+
isCypress: hooks.isCypress,
|
|
210
|
+
windowFocused,
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
if (appViewer.currentDisplay === 'menu') {
|
|
214
|
+
const menu = appViewer.backend?.getMenuBackground?.()
|
|
215
|
+
if (menu) applyMenuBackgroundLiveOptions(menu, snapshot)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
run()
|
|
220
|
+
|
|
221
|
+
hooks.onRegisterFocusHandlers?.({
|
|
222
|
+
onFocus: () => {
|
|
223
|
+
windowFocused = true
|
|
224
|
+
run()
|
|
225
|
+
},
|
|
226
|
+
onBlur: () => {
|
|
227
|
+
windowFocused = false
|
|
228
|
+
run()
|
|
229
|
+
},
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
return subscribe(optionsProxy, run)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Call when mineflayer bot is created (lighting depends on protocol features). */
|
|
236
|
+
export function applyRendererEnableLighting(
|
|
237
|
+
appViewer: AppViewer,
|
|
238
|
+
newVersionsLighting: boolean,
|
|
239
|
+
blockStateIdSupported: boolean
|
|
240
|
+
): void {
|
|
241
|
+
appViewer.inWorldRenderingConfig.enableLighting =
|
|
242
|
+
!blockStateIdSupported || newVersionsLighting
|
|
243
|
+
}
|
|
@@ -23,12 +23,13 @@ export interface SoundSystem {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
import type { MenuBackgroundOptions } from '../three/menuBackground/types'
|
|
26
|
+
import type { RendererStorageOptions } from '../three/menuBackground/defaultOptions'
|
|
27
|
+
import type { MenuBackgroundRenderer } from '../three/menuBackground/renderer'
|
|
26
28
|
import type { PerformanceInstabilityFactors } from '../performanceMonitor'
|
|
27
29
|
|
|
28
30
|
/** Graphics backend configuration */
|
|
29
31
|
export interface GraphicsBackendConfig {
|
|
30
32
|
fpsLimit?: number
|
|
31
|
-
powerPreference?: 'high-performance' | 'low-power'
|
|
32
33
|
statsVisible?: number
|
|
33
34
|
sceneBackground: string
|
|
34
35
|
timeoutRendering?: boolean
|
|
@@ -96,6 +97,8 @@ export interface RendererReactiveState {
|
|
|
96
97
|
/** Graphics initialization options */
|
|
97
98
|
export interface GraphicsInitOptions<S = any> {
|
|
98
99
|
config: GraphicsBackendConfig
|
|
100
|
+
/** Live app options (e.g. valtio proxy); used for WebGL `gpuPreference` at context creation. */
|
|
101
|
+
getRendererOptions?: () => RendererStorageOptions
|
|
99
102
|
rendererSpecificSettings: S
|
|
100
103
|
callbacks: {
|
|
101
104
|
displayCriticalError: (error: Error) => void
|
|
@@ -127,6 +130,8 @@ export interface GraphicsBackend {
|
|
|
127
130
|
soundSystem?: any
|
|
128
131
|
backendMethods?: any
|
|
129
132
|
getDebugOverlay?(): { entitiesString?: string, left?: Record<string, string>, right?: Record<string, string> }
|
|
133
|
+
/** Active main-menu background, when `currentDisplay === 'menu'`. */
|
|
134
|
+
getMenuBackground?(): MenuBackgroundRenderer | undefined
|
|
130
135
|
}
|
|
131
136
|
|
|
132
137
|
/** Graphics backend loader function type */
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
import { Vec3 } from 'vec3'
|
|
3
|
+
import type { WorldBlockProvider } from 'mc-assets/dist/worldBlockProvider'
|
|
4
|
+
import legacyJson from '../lib/preflatMap.json'
|
|
5
|
+
import type { World, WorldBlock as Block } from './world'
|
|
6
|
+
|
|
7
|
+
const calculatedBlocksEntries = Object.entries(legacyJson.clientCalculatedBlocks)
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Block name + properties for model lookup. Only runs neighbor/preflat work when
|
|
11
|
+
* `world.preflat` (legacy); modern block-state worlds use `fromStateId` only.
|
|
12
|
+
*/
|
|
13
|
+
export function resolveBlockPropertiesForMeshing(
|
|
14
|
+
world: World | undefined,
|
|
15
|
+
cursor: Vec3,
|
|
16
|
+
blockProvider: WorldBlockProvider,
|
|
17
|
+
blockStateId: number,
|
|
18
|
+
PrismarineBlockCtor: { fromStateId: (id: number, biome: number) => Block }
|
|
19
|
+
): { name: string, properties: Record<string, unknown> } {
|
|
20
|
+
if (world?.preflat) {
|
|
21
|
+
const block = world.getBlock(cursor, blockProvider, {})
|
|
22
|
+
if (block) {
|
|
23
|
+
let properties: Record<string, unknown> = { ...block.getProperties() }
|
|
24
|
+
const patch = preflatBlockCalculation(block, world, cursor)
|
|
25
|
+
if (patch) properties = { ...properties, ...patch }
|
|
26
|
+
return { name: block.name, properties }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const fromState = PrismarineBlockCtor.fromStateId(blockStateId, 1)
|
|
30
|
+
return { name: fromState.name, properties: fromState.getProperties() }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function preflatBlockCalculation(block: Block, world: World, position: Vec3) {
|
|
34
|
+
const type = calculatedBlocksEntries.find(([name, blocks]) => blocks.includes(block.name))?.[0]
|
|
35
|
+
if (!type) return
|
|
36
|
+
switch (type) {
|
|
37
|
+
case 'directional': {
|
|
38
|
+
const isSolidConnection = !block.name.includes('redstone') && !block.name.includes('tripwire')
|
|
39
|
+
const neighbors = [
|
|
40
|
+
world.getBlock(position.offset(0, 0, 1)),
|
|
41
|
+
world.getBlock(position.offset(0, 0, -1)),
|
|
42
|
+
world.getBlock(position.offset(1, 0, 0)),
|
|
43
|
+
world.getBlock(position.offset(-1, 0, 0))
|
|
44
|
+
]
|
|
45
|
+
const props = {}
|
|
46
|
+
let changed = false
|
|
47
|
+
for (const [i, neighbor] of neighbors.entries()) {
|
|
48
|
+
const isConnectedToSolid = isSolidConnection ? (neighbor && !neighbor.transparent) : false
|
|
49
|
+
if (isConnectedToSolid || neighbor?.name === block.name) {
|
|
50
|
+
props[['south', 'north', 'east', 'west'][i]] = 'true'
|
|
51
|
+
changed = true
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return changed ? props : undefined
|
|
55
|
+
}
|
|
56
|
+
case 'block_snowy': {
|
|
57
|
+
const aboveIsSnow = world.getBlock(position.offset(0, 1, 0))?.name === 'snow'
|
|
58
|
+
if (aboveIsSnow) {
|
|
59
|
+
return {
|
|
60
|
+
snowy: `${aboveIsSnow}`
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
case 'door': {
|
|
67
|
+
const { half } = block.getProperties()
|
|
68
|
+
if (half === 'upper') {
|
|
69
|
+
const lower = world.getBlock(position.offset(0, -1, 0))
|
|
70
|
+
if (lower?.name === block.name) {
|
|
71
|
+
return {
|
|
72
|
+
...lower.getProperties(),
|
|
73
|
+
half: 'upper'
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
/** Shared geometry export shapes (worker bridge + main-thread viewer). */
|
|
3
|
+
|
|
4
|
+
export interface ExportedSection {
|
|
5
|
+
key: string
|
|
6
|
+
position: { x: number, y: number, z: number }
|
|
7
|
+
geometry: {
|
|
8
|
+
positions: number[]
|
|
9
|
+
normals: number[]
|
|
10
|
+
colors: number[]
|
|
11
|
+
uvs: number[]
|
|
12
|
+
indices: number[]
|
|
13
|
+
}
|
|
14
|
+
shaderCubes?: unknown
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ExportedWorldGeometry {
|
|
18
|
+
version: string
|
|
19
|
+
exportedAt: string
|
|
20
|
+
camera: {
|
|
21
|
+
position: { x: number, y: number, z: number }
|
|
22
|
+
rotation: { pitch: number, yaw: number }
|
|
23
|
+
}
|
|
24
|
+
sections: ExportedSection[]
|
|
25
|
+
textureAtlasDataUrl?: string
|
|
26
|
+
}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { Vec3 } from 'vec3'
|
|
3
3
|
import worldBlockProvider, { WorldBlockProvider } from 'mc-assets/dist/worldBlockProvider'
|
|
4
4
|
import moreBlockDataGeneratedJson from '../lib/moreBlockDataGenerated.json'
|
|
5
|
-
import legacyJson from '../lib/preflatMap.json'
|
|
6
5
|
import { BlockType } from '../playground/shared'
|
|
7
6
|
import { World, BlockModelPartsResolved, WorldBlock as Block, WorldBlock, worldColumnKey } from './world'
|
|
8
7
|
import { BlockElement, buildRotationMatrix, elemFaces, matmul3, matmulmat3, vecadd3, vecsub3 } from './modelsGeometryCommon'
|
|
@@ -10,6 +9,9 @@ import { getSideShading, vertexLightFromAo } from './vertexShading'
|
|
|
10
9
|
import { INVISIBLE_BLOCKS } from './worldConstants'
|
|
11
10
|
import { MesherGeometryOutput, HighestBlockInfo } from './shared'
|
|
12
11
|
import { collectBlockEntityMetadata } from './blockEntityMetadata'
|
|
12
|
+
import { preflatBlockCalculation, resolveBlockPropertiesForMeshing } from './blockPropertiesForMeshing'
|
|
13
|
+
|
|
14
|
+
export { preflatBlockCalculation, resolveBlockPropertiesForMeshing } from './blockPropertiesForMeshing'
|
|
13
15
|
|
|
14
16
|
// Log function disabled by default for zero overhead in production hot loops
|
|
15
17
|
const ENABLE_TS_LOGS = false
|
|
@@ -51,84 +53,6 @@ function prepareTints(tints) {
|
|
|
51
53
|
})
|
|
52
54
|
}
|
|
53
55
|
|
|
54
|
-
const calculatedBlocksEntries = Object.entries(legacyJson.clientCalculatedBlocks)
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Block name + properties for model lookup. Only runs neighbor/preflat work when
|
|
58
|
-
* `world.preflat` (legacy); modern block-state worlds use `fromStateId` only.
|
|
59
|
-
*/
|
|
60
|
-
export function resolveBlockPropertiesForMeshing(
|
|
61
|
-
world: World | undefined,
|
|
62
|
-
cursor: Vec3,
|
|
63
|
-
blockProvider: WorldBlockProvider,
|
|
64
|
-
blockStateId: number,
|
|
65
|
-
PrismarineBlockCtor: { fromStateId: (id: number, biome: number) => Block }
|
|
66
|
-
): { name: string, properties: Record<string, unknown> } {
|
|
67
|
-
if (world?.preflat) {
|
|
68
|
-
const block = world.getBlock(cursor, blockProvider, {})
|
|
69
|
-
if (block) {
|
|
70
|
-
let properties: Record<string, unknown> = { ...block.getProperties() }
|
|
71
|
-
const patch = preflatBlockCalculation(block, world, cursor)
|
|
72
|
-
if (patch) properties = { ...properties, ...patch }
|
|
73
|
-
return { name: block.name, properties }
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
const fromState = PrismarineBlockCtor.fromStateId(blockStateId, 1)
|
|
77
|
-
return { name: fromState.name, properties: fromState.getProperties() }
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export function preflatBlockCalculation(block: Block, world: World, position: Vec3) {
|
|
81
|
-
const type = calculatedBlocksEntries.find(([name, blocks]) => blocks.includes(block.name))?.[0]
|
|
82
|
-
if (!type) return
|
|
83
|
-
switch (type) {
|
|
84
|
-
case 'directional': {
|
|
85
|
-
const isSolidConnection = !block.name.includes('redstone') && !block.name.includes('tripwire')
|
|
86
|
-
const neighbors = [
|
|
87
|
-
world.getBlock(position.offset(0, 0, 1)),
|
|
88
|
-
world.getBlock(position.offset(0, 0, -1)),
|
|
89
|
-
world.getBlock(position.offset(1, 0, 0)),
|
|
90
|
-
world.getBlock(position.offset(-1, 0, 0))
|
|
91
|
-
]
|
|
92
|
-
// set needed props to true: east:'false',north:'false',south:'false',west:'false'
|
|
93
|
-
const props = {}
|
|
94
|
-
let changed = false
|
|
95
|
-
for (const [i, neighbor] of neighbors.entries()) {
|
|
96
|
-
const isConnectedToSolid = isSolidConnection ? (neighbor && !neighbor.transparent) : false
|
|
97
|
-
if (isConnectedToSolid || neighbor?.name === block.name) {
|
|
98
|
-
props[['south', 'north', 'east', 'west'][i]] = 'true'
|
|
99
|
-
changed = true
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return changed ? props : undefined
|
|
103
|
-
}
|
|
104
|
-
// case 'gate_in_wall': {}
|
|
105
|
-
case 'block_snowy': {
|
|
106
|
-
const aboveIsSnow = world.getBlock(position.offset(0, 1, 0))?.name === 'snow'
|
|
107
|
-
if (aboveIsSnow) {
|
|
108
|
-
return {
|
|
109
|
-
snowy: `${aboveIsSnow}`
|
|
110
|
-
}
|
|
111
|
-
} else {
|
|
112
|
-
return
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
case 'door': {
|
|
116
|
-
// upper half matches lower in
|
|
117
|
-
const { half } = block.getProperties()
|
|
118
|
-
if (half === 'upper') {
|
|
119
|
-
// copy other properties
|
|
120
|
-
const lower = world.getBlock(position.offset(0, -1, 0))
|
|
121
|
-
if (lower?.name === block.name) {
|
|
122
|
-
return {
|
|
123
|
-
...lower.getProperties(),
|
|
124
|
-
half: 'upper'
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
56
|
function tintToGl(tint) {
|
|
133
57
|
const r = (tint >> 16) & 0xff
|
|
134
58
|
const g = (tint >> 8) & 0xff
|
|
@@ -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
|
|
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:
|
|
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()
|