minecraft-renderer 0.1.18 → 0.1.20
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 +87 -87
- package/dist/mesher.js.map +3 -3
- package/dist/mesherWasm.js +246 -0
- package/dist/minecraft-renderer.js +55 -55
- package/dist/threeWorker.js +395 -395
- package/package.json +3 -2
- package/public/wasm_mesher_bg.wasm +0 -0
- package/src/graphicsBackend/config.ts +4 -3
- package/src/lib/worldrendererCommon.ts +98 -48
- package/src/mesher/mesherWasm.ts +404 -0
- package/src/mesher/models.ts +53 -18
- package/src/mesher/shared.ts +3 -0
- package/src/mesher/test/run/chunk.ts +18 -0
- package/src/mesher/test/run/test-js.ts +26 -0
- package/src/mesher/test/snapshotUtils.ts +93 -0
- package/src/playground/baseScene.ts +4 -3
- package/src/resourcesManager/resourcesManager.ts +1 -2
- package/src/three/modules/sciFiWorldReveal.ts +5 -3
- package/src/three/worldBlockGeometry.ts +29 -4
- package/src/three/worldRendererThree.ts +7 -4
- package/src/wasm-lib/convertChunk.ts +192 -0
- package/src/wasm-lib/render-from-wasm.ts +1000 -0
- package/dist/metafile.json +0 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs'
|
|
3
|
+
import { join } from 'path'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Serialize any output object for snapshot comparison
|
|
7
|
+
* Converts TypedArrays to regular arrays for JSON serialization
|
|
8
|
+
*/
|
|
9
|
+
export function serializeOutput(output: any): any {
|
|
10
|
+
const serialized: any = {}
|
|
11
|
+
for (const [key, value] of Object.entries(output)) {
|
|
12
|
+
if (value instanceof Float32Array || value instanceof Uint32Array || value instanceof Uint16Array || value instanceof Uint8Array) {
|
|
13
|
+
serialized[key] = Array.from(value as any)
|
|
14
|
+
} else if (value instanceof ArrayBuffer) {
|
|
15
|
+
serialized[key] = Array.from(new Uint8Array(value))
|
|
16
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
17
|
+
serialized[key] = serializeOutput(value)
|
|
18
|
+
} else {
|
|
19
|
+
serialized[key] = value
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return serialized
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Deep equality comparison for snapshot testing
|
|
27
|
+
*/
|
|
28
|
+
export function deepEqual(obj1: any, obj2: any): boolean {
|
|
29
|
+
if (obj1 === obj2) return true
|
|
30
|
+
if (obj1 == null || obj2 == null) return false
|
|
31
|
+
if (typeof obj1 !== typeof obj2) return false
|
|
32
|
+
|
|
33
|
+
if (Array.isArray(obj1) && Array.isArray(obj2)) {
|
|
34
|
+
if (obj1.length !== obj2.length) return false
|
|
35
|
+
for (let i = 0; i < obj1.length; i++) {
|
|
36
|
+
if (!deepEqual(obj1[i], obj2[i])) return false
|
|
37
|
+
}
|
|
38
|
+
return true
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (typeof obj1 === 'object') {
|
|
42
|
+
const keys1 = Object.keys(obj1)
|
|
43
|
+
const keys2 = Object.keys(obj2)
|
|
44
|
+
if (keys1.length !== keys2.length) return false
|
|
45
|
+
for (const key of keys1) {
|
|
46
|
+
if (!keys2.includes(key)) return false
|
|
47
|
+
if (!deepEqual(obj1[key], obj2[key])) return false
|
|
48
|
+
}
|
|
49
|
+
return true
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Compare output with snapshot file, or write snapshot if it doesn't exist
|
|
57
|
+
* @param output - The output object to compare/write
|
|
58
|
+
* @param snapshotPath - Path to snapshot file (absolute or relative to __dirname)
|
|
59
|
+
* @param baseDir - Base directory for relative paths (defaults to __dirname, ignored if snapshotPath is absolute)
|
|
60
|
+
* @returns true if snapshot matches or was created, throws error if mismatch
|
|
61
|
+
*/
|
|
62
|
+
export function compareOrWriteSnapshot(
|
|
63
|
+
output: any,
|
|
64
|
+
snapshotPath: string,
|
|
65
|
+
baseDir?: string
|
|
66
|
+
): boolean {
|
|
67
|
+
// If path is absolute, use it directly; otherwise join with baseDir or __dirname
|
|
68
|
+
const fullPath = snapshotPath.startsWith('/') || snapshotPath.includes(':')
|
|
69
|
+
? snapshotPath
|
|
70
|
+
: baseDir ? join(baseDir, snapshotPath) : join(__dirname, snapshotPath)
|
|
71
|
+
const serialized = serializeOutput(output)
|
|
72
|
+
|
|
73
|
+
if (!existsSync(fullPath)) {
|
|
74
|
+
// Write snapshot if file doesn't exist
|
|
75
|
+
writeFileSync(fullPath, JSON.stringify(serialized, null, 2), 'utf-8')
|
|
76
|
+
console.log('Snapshot file created:', fullPath)
|
|
77
|
+
return true
|
|
78
|
+
} else {
|
|
79
|
+
// Compare with existing snapshot
|
|
80
|
+
const snapshotContent = readFileSync(fullPath, 'utf-8')
|
|
81
|
+
const snapshotData = JSON.parse(snapshotContent)
|
|
82
|
+
|
|
83
|
+
if (!deepEqual(serialized, snapshotData)) {
|
|
84
|
+
console.error('Snapshot mismatch! Current output differs from snapshot.')
|
|
85
|
+
// console.error('Expected (snapshot):', JSON.stringify(snapshotData, null, 2))
|
|
86
|
+
// console.error('Actual (current):', JSON.stringify(serialized, null, 2))
|
|
87
|
+
throw new Error('Snapshot comparison failed - output has changed')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
console.log('Snapshot comparison passed - no changes detected')
|
|
91
|
+
return true
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -13,7 +13,7 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
|
|
|
13
13
|
// eslint-disable-next-line import/no-named-as-default
|
|
14
14
|
import GUI from 'lil-gui'
|
|
15
15
|
import _ from 'lodash'
|
|
16
|
-
import {
|
|
16
|
+
import { WorldRendererConfig } from '../graphicsBackend/config'
|
|
17
17
|
import { getSyncWorld } from './shared'
|
|
18
18
|
import { AppViewer, getInitialPlayerState } from '../graphicsBackend'
|
|
19
19
|
import { WorldView } from '../worldView'
|
|
@@ -35,8 +35,8 @@ export interface PlaygroundSceneConfig {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
const appGraphicBackends = [
|
|
38
|
-
|
|
39
|
-
createGraphicsBackendOffThread
|
|
38
|
+
createGraphicsBackendSingleThread,
|
|
39
|
+
// createGraphicsBackendOffThread
|
|
40
40
|
]
|
|
41
41
|
|
|
42
42
|
const includedVersions = globalThis.includedVersions
|
|
@@ -130,6 +130,7 @@ export class BasePlaygroundScene {
|
|
|
130
130
|
Object.assign(this.appViewer.inWorldRenderingConfig, config.worldConfig)
|
|
131
131
|
}
|
|
132
132
|
this.appViewer.inWorldRenderingConfig.showHand = false
|
|
133
|
+
this.appViewer.inWorldRenderingConfig.showChunkBorders = true
|
|
133
134
|
this.appViewer.inWorldRenderingConfig.isPlayground = true
|
|
134
135
|
this.appViewer.inWorldRenderingConfig.instantCameraUpdate = this.enableCameraOrbitControl
|
|
135
136
|
this.appViewer.config.statsVisible = 2
|
|
@@ -308,8 +308,7 @@ export class ResourcesManager extends (EventEmitter as new () => TypedEmitter<Re
|
|
|
308
308
|
}
|
|
309
309
|
|
|
310
310
|
async generateGuiTextures() {
|
|
311
|
-
//
|
|
312
|
-
// await generateGuiAtlas()
|
|
311
|
+
// todo-low handled now in the client
|
|
313
312
|
}
|
|
314
313
|
|
|
315
314
|
async downloadDebugAtlas(isItems = false) {
|
|
@@ -250,9 +250,11 @@ export class SciFiWorldRevealModule implements RendererModuleController {
|
|
|
250
250
|
// Section keys are in format "x,y,z" where x, y, z are section coordinates
|
|
251
251
|
// Mesh position is at geometry.sx, geometry.sy, geometry.sz
|
|
252
252
|
const pos = mesh.position
|
|
253
|
-
const
|
|
254
|
-
const
|
|
255
|
-
const
|
|
253
|
+
const CHUNK_SIZE = 16
|
|
254
|
+
const sectionHeight = this.worldRenderer.getSectionHeight()
|
|
255
|
+
const sectionX = Math.floor(pos.x / CHUNK_SIZE) * CHUNK_SIZE
|
|
256
|
+
const sectionY = Math.floor(pos.y / sectionHeight) * sectionHeight
|
|
257
|
+
const sectionZ = Math.floor(pos.z / CHUNK_SIZE) * CHUNK_SIZE
|
|
256
258
|
const derivedKey = `${sectionX},${sectionY},${sectionZ}`
|
|
257
259
|
|
|
258
260
|
// Verify this key exists in sectionObjects
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import * as THREE from 'three'
|
|
3
3
|
import { Vec3 } from 'vec3'
|
|
4
4
|
import nbt from 'prismarine-nbt'
|
|
5
|
-
import { MesherGeometryOutput } from '../mesher/shared'
|
|
5
|
+
import { MesherGeometryOutput, IS_FULL_WORLD_SECTION } from '../mesher/shared'
|
|
6
6
|
import { getBannerTexture, createBannerMesh, releaseBannerTexture } from './bannerRenderer'
|
|
7
7
|
import { disposeObject } from './threeJsUtils'
|
|
8
8
|
import type { WorldRendererThree } from './worldRendererThree'
|
|
@@ -70,9 +70,11 @@ export class WorldBlockGeometry {
|
|
|
70
70
|
mesh.name = 'mesh'
|
|
71
71
|
object = new THREE.Group()
|
|
72
72
|
object.add(mesh)
|
|
73
|
-
// mesh with static dimensions:
|
|
73
|
+
// mesh with static dimensions: 16x16xsectionHeight
|
|
74
|
+
const sectionHeight = data.geometry.sectionEndY - data.geometry.sectionStartY
|
|
75
|
+
const CHUNK_SIZE = 16
|
|
74
76
|
const staticChunkMesh = new THREE.Mesh(
|
|
75
|
-
new THREE.BoxGeometry(
|
|
77
|
+
new THREE.BoxGeometry(CHUNK_SIZE, sectionHeight, CHUNK_SIZE),
|
|
76
78
|
new THREE.MeshBasicMaterial({ color: 0x00_00_00, transparent: true, opacity: 0 })
|
|
77
79
|
)
|
|
78
80
|
staticChunkMesh.position.set(data.geometry.sx, data.geometry.sy, data.geometry.sz)
|
|
@@ -230,7 +232,11 @@ export class WorldBlockGeometry {
|
|
|
230
232
|
}
|
|
231
233
|
|
|
232
234
|
removeColumn(x: number, z: number) {
|
|
233
|
-
|
|
235
|
+
const sectionHeight = this.worldRenderer.getSectionHeight()
|
|
236
|
+
const worldMinY = this.worldRenderer.worldMinYRender
|
|
237
|
+
if (IS_FULL_WORLD_SECTION) {
|
|
238
|
+
// Only one section per chunk when full world section
|
|
239
|
+
const y = worldMinY
|
|
234
240
|
this.worldRenderer.setSectionDirty(new Vec3(x, y, z), false)
|
|
235
241
|
const key = `${x},${y},${z}`
|
|
236
242
|
const mesh = this.sectionObjects[key]
|
|
@@ -247,6 +253,25 @@ export class WorldBlockGeometry {
|
|
|
247
253
|
disposeObject(mesh)
|
|
248
254
|
}
|
|
249
255
|
delete this.sectionObjects[key]
|
|
256
|
+
} else {
|
|
257
|
+
for (let y = worldMinY; y < this.worldRenderer.worldSizeParams.worldHeight; y += sectionHeight) {
|
|
258
|
+
this.worldRenderer.setSectionDirty(new Vec3(x, y, z), false)
|
|
259
|
+
const key = `${x},${y},${z}`
|
|
260
|
+
const mesh = this.sectionObjects[key]
|
|
261
|
+
if (mesh) {
|
|
262
|
+
// Track memory usage removal
|
|
263
|
+
this.removeSectionMemoryUsage(mesh)
|
|
264
|
+
// Cleanup banner textures before disposing
|
|
265
|
+
mesh.traverse((child) => {
|
|
266
|
+
if ((child as any).bannerTexture) {
|
|
267
|
+
releaseBannerTexture((child as any).bannerTexture)
|
|
268
|
+
}
|
|
269
|
+
})
|
|
270
|
+
this.scene.remove(mesh)
|
|
271
|
+
disposeObject(mesh)
|
|
272
|
+
}
|
|
273
|
+
delete this.sectionObjects[key]
|
|
274
|
+
}
|
|
250
275
|
}
|
|
251
276
|
}
|
|
252
277
|
}
|
|
@@ -796,7 +796,8 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
796
796
|
debugChunksVisibilityOverride() {
|
|
797
797
|
const { chunksRenderAboveOverride, chunksRenderBelowOverride, chunksRenderDistanceOverride, chunksRenderAboveEnabled, chunksRenderBelowEnabled, chunksRenderDistanceEnabled } = this.reactiveDebugParams
|
|
798
798
|
|
|
799
|
-
const
|
|
799
|
+
const sectionHeight = this.getSectionHeight()
|
|
800
|
+
const baseY = this.cameraSectionPos.y * sectionHeight
|
|
800
801
|
|
|
801
802
|
if (
|
|
802
803
|
this.displayOptions.inWorldRenderingConfig.enableDebugOverlay &&
|
|
@@ -1077,9 +1078,11 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
1077
1078
|
|
|
1078
1079
|
shouldObjectVisible(object: THREE.Object3D) {
|
|
1079
1080
|
// Get chunk coordinates
|
|
1080
|
-
const
|
|
1081
|
-
const
|
|
1082
|
-
const
|
|
1081
|
+
const CHUNK_SIZE = 16
|
|
1082
|
+
const sectionHeight = this.getSectionHeight()
|
|
1083
|
+
const chunkX = Math.floor(object.position.x / CHUNK_SIZE) * CHUNK_SIZE
|
|
1084
|
+
const chunkZ = Math.floor(object.position.z / CHUNK_SIZE) * CHUNK_SIZE
|
|
1085
|
+
const sectionY = Math.floor(object.position.y / sectionHeight) * sectionHeight
|
|
1083
1086
|
|
|
1084
1087
|
const chunkKey = `${chunkX},${chunkZ}`
|
|
1085
1088
|
const sectionKey = `${chunkX},${sectionY},${chunkZ}`
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
import { Vec3 } from 'vec3'
|
|
3
|
+
import MinecraftData from 'minecraft-data'
|
|
4
|
+
import PrismarineBlockLoader from 'prismarine-block'
|
|
5
|
+
import moreBlockDataGeneratedJson from '../lib/moreBlockDataGenerated.json'
|
|
6
|
+
|
|
7
|
+
type BlockMeta = {
|
|
8
|
+
invisibleBlocks: Uint16Array
|
|
9
|
+
transparentBlocks: Uint16Array
|
|
10
|
+
noAoBlocks: Uint16Array
|
|
11
|
+
cullIdenticalBlocks: Uint16Array
|
|
12
|
+
occludingBlocks: Uint16Array
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const metaCache = new Map<string, BlockMeta>()
|
|
16
|
+
|
|
17
|
+
const blockToIds = (block: { minStateId: number, maxStateId: number }) => {
|
|
18
|
+
const ids: number[] = []
|
|
19
|
+
for (let i = block.minStateId; i <= block.maxStateId; i++) {
|
|
20
|
+
ids.push(i)
|
|
21
|
+
}
|
|
22
|
+
return ids
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const isCube = (shapes: any) => {
|
|
26
|
+
if (!shapes || shapes.length !== 1) return false
|
|
27
|
+
const s = shapes[0]
|
|
28
|
+
return s[0] === 0 && s[1] === 0 && s[2] === 0 && s[3] === 1 && s[4] === 1 && s[5] === 1
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const isLikelyFullCubeBlockName = (name: string) => {
|
|
32
|
+
if (!name) return false
|
|
33
|
+
if (name.includes('stairs')) return false
|
|
34
|
+
if (name.includes('slab')) return false
|
|
35
|
+
if (name.includes('fence')) return false
|
|
36
|
+
if (name.includes('gate')) return false
|
|
37
|
+
if (name.includes('pane')) return false
|
|
38
|
+
if (name.includes('wall')) return false
|
|
39
|
+
if (name.includes('door')) return false
|
|
40
|
+
if (name.includes('trapdoor')) return false
|
|
41
|
+
if (name.includes('sign')) return false
|
|
42
|
+
if (name.includes('banner')) return false
|
|
43
|
+
if (name.includes('rail')) return false
|
|
44
|
+
if (name.includes('torch')) return false
|
|
45
|
+
if (name.includes('lantern')) return false
|
|
46
|
+
if (name.includes('button')) return false
|
|
47
|
+
if (name.includes('lever')) return false
|
|
48
|
+
if (name.includes('pressure_plate')) return false
|
|
49
|
+
if (name.includes('carpet')) return false
|
|
50
|
+
if (name.includes('flower')) return false
|
|
51
|
+
if (name.includes('sapling')) return false
|
|
52
|
+
if (name.includes('tall_grass')) return false
|
|
53
|
+
if (name === 'grass' || name === 'short_grass' || name === 'tall_grass') return false
|
|
54
|
+
return true
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const getBlockMeta = (version: string): BlockMeta => {
|
|
58
|
+
const cached = metaCache.get(version)
|
|
59
|
+
if (cached) return cached
|
|
60
|
+
|
|
61
|
+
const mcData = MinecraftData(version)
|
|
62
|
+
|
|
63
|
+
const invisibleBlocks = new Uint16Array(mcData.blocksArray.filter(x => moreBlockDataGeneratedJson.invisibleBlocks[x.name]).flatMap(blockToIds))
|
|
64
|
+
const transparentBlocks = new Uint16Array(mcData.blocksArray.filter(x => x.transparent).flatMap(blockToIds))
|
|
65
|
+
const noAoBlocks = new Uint16Array(mcData.blocksArray.filter(x => moreBlockDataGeneratedJson.noOcclusions[x.name]).flatMap(blockToIds))
|
|
66
|
+
const cullIdenticalBlocks = new Uint16Array(mcData.blocksArray.filter(x => x.name.includes('glass') || x.name.includes('ice')).flatMap(blockToIds))
|
|
67
|
+
const Block = PrismarineBlockLoader(version)
|
|
68
|
+
const noOcclusionsSet = new Set(Object.keys(moreBlockDataGeneratedJson.noOcclusions))
|
|
69
|
+
|
|
70
|
+
const occludingBlockIds: number[] = []
|
|
71
|
+
for (const idStr of Object.keys((mcData as any).blocksByStateId)) {
|
|
72
|
+
const id = Number(idStr)
|
|
73
|
+
if (!id) continue
|
|
74
|
+
const b = (Block as any).fromStateId(id, 0)
|
|
75
|
+
if (!b) continue
|
|
76
|
+
if (b.transparent) continue
|
|
77
|
+
if (b.boundingBox !== 'block') continue
|
|
78
|
+
if (noOcclusionsSet.has(b.name)) continue
|
|
79
|
+
if (!isCube(b.shapes)) continue
|
|
80
|
+
occludingBlockIds.push(id)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const occludingBlocks = new Uint16Array(occludingBlockIds)
|
|
84
|
+
|
|
85
|
+
const meta = {
|
|
86
|
+
invisibleBlocks,
|
|
87
|
+
transparentBlocks,
|
|
88
|
+
noAoBlocks,
|
|
89
|
+
cullIdenticalBlocks,
|
|
90
|
+
occludingBlocks
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
metaCache.set(version, meta)
|
|
94
|
+
return meta
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface ChunkConversionResult {
|
|
98
|
+
blockStates: Uint16Array
|
|
99
|
+
blockLight: Uint8Array
|
|
100
|
+
skyLight: Uint8Array
|
|
101
|
+
biomesArray: Uint8Array
|
|
102
|
+
invisibleBlocks: Uint16Array
|
|
103
|
+
transparentBlocks: Uint16Array
|
|
104
|
+
noAoBlocks: Uint16Array
|
|
105
|
+
cullIdenticalBlocks: Uint16Array
|
|
106
|
+
occludingBlocks: Uint16Array
|
|
107
|
+
blockCount: number
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Convert a prismarine chunk to WASM format
|
|
112
|
+
*/
|
|
113
|
+
export function convertChunkToWasm(
|
|
114
|
+
chunk: any,
|
|
115
|
+
version: string,
|
|
116
|
+
chunkX: number = 0,
|
|
117
|
+
chunkZ: number = 0,
|
|
118
|
+
worldMinY: number = 0,
|
|
119
|
+
worldMaxY: number = 256,
|
|
120
|
+
sectionY?: number,
|
|
121
|
+
sectionHeight?: number
|
|
122
|
+
): ChunkConversionResult {
|
|
123
|
+
const CHUNK_SIZE = 16
|
|
124
|
+
|
|
125
|
+
// If sectionY and sectionHeight are provided, only convert that section
|
|
126
|
+
// Otherwise convert the full chunk
|
|
127
|
+
const startY = sectionY !== undefined ? sectionY : worldMinY
|
|
128
|
+
const endY = sectionHeight !== undefined ? startY + sectionHeight : worldMaxY
|
|
129
|
+
const totalBlocks = CHUNK_SIZE * CHUNK_SIZE * (endY - startY)
|
|
130
|
+
|
|
131
|
+
const blockStates = new Uint16Array(totalBlocks)
|
|
132
|
+
const blockLight = new Uint8Array(totalBlocks)
|
|
133
|
+
const skyLight = new Uint8Array(totalBlocks)
|
|
134
|
+
const biomesArray = new Uint8Array(totalBlocks)
|
|
135
|
+
|
|
136
|
+
// Traverse chunk and extract data
|
|
137
|
+
let blockCount = 0
|
|
138
|
+
|
|
139
|
+
for (let y = startY; y < endY; y++) {
|
|
140
|
+
for (let z = 0; z < CHUNK_SIZE; z++) {
|
|
141
|
+
for (let x = 0; x < CHUNK_SIZE; x++) {
|
|
142
|
+
const pos = new Vec3(x, y, z)
|
|
143
|
+
const idx = x + z * CHUNK_SIZE + (y - startY) * CHUNK_SIZE * CHUNK_SIZE
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
// Get block state ID
|
|
147
|
+
const stateId = chunk.getBlockStateId(pos)
|
|
148
|
+
blockStates[idx] = stateId || 0
|
|
149
|
+
|
|
150
|
+
// Get light values
|
|
151
|
+
const bl = chunk.getBlockLight(pos)
|
|
152
|
+
const sl = chunk.getSkyLight(pos)
|
|
153
|
+
blockLight[idx] = bl !== undefined ? bl : 0
|
|
154
|
+
skyLight[idx] = sl !== undefined ? sl : 15
|
|
155
|
+
|
|
156
|
+
// Get biome
|
|
157
|
+
const biome = chunk.getBiome ? chunk.getBiome(pos) : 1
|
|
158
|
+
biomesArray[idx] = biome || 1
|
|
159
|
+
|
|
160
|
+
if (stateId && stateId !== 0) blockCount++
|
|
161
|
+
} catch (err) {
|
|
162
|
+
// If position is out of bounds, set to air
|
|
163
|
+
blockStates[idx] = 0
|
|
164
|
+
blockLight[idx] = 0
|
|
165
|
+
skyLight[idx] = 15
|
|
166
|
+
biomesArray[idx] = 1
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const {
|
|
173
|
+
invisibleBlocks,
|
|
174
|
+
transparentBlocks,
|
|
175
|
+
noAoBlocks,
|
|
176
|
+
cullIdenticalBlocks,
|
|
177
|
+
occludingBlocks
|
|
178
|
+
} = getBlockMeta(version)
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
blockStates,
|
|
182
|
+
blockLight,
|
|
183
|
+
skyLight,
|
|
184
|
+
biomesArray,
|
|
185
|
+
invisibleBlocks,
|
|
186
|
+
transparentBlocks,
|
|
187
|
+
noAoBlocks,
|
|
188
|
+
cullIdenticalBlocks,
|
|
189
|
+
occludingBlocks,
|
|
190
|
+
blockCount,
|
|
191
|
+
}
|
|
192
|
+
}
|