minecraft-renderer 0.1.98 → 0.1.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minecraft-renderer",
3
- "version": "0.1.98",
3
+ "version": "0.1.100",
4
4
  "description": "The most Modular Minecraft world renderer with Three.js WebGL backend",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -240,12 +240,18 @@ function getUsernameTexture(
240
240
  const iconSize = fontSize * 0.85
241
241
  const iconAreaWidth = voiceState ? iconSize + padding * 2.5 : 0
242
242
 
243
- const canvas = createCanvas(iconAreaWidth + textWidth, height)
243
+ // The nametag sprite is anchored (and thus centered above the player) at the
244
+ // midpoint of this canvas. Mirroring the icon's width as a second, unpainted
245
+ // margin on the right moves that midpoint to align with the *text's* own
246
+ // center instead of the icon+text bounding box's center — otherwise the
247
+ // icon's extra width on the left alone drags the whole sprite's anchor left,
248
+ // which visually reads as the text drifting right of the player.
249
+ const canvas = createCanvas(iconAreaWidth * 2 + textWidth, height)
244
250
  const ctx = canvas.getContext('2d')
245
251
  if (!ctx) throw new Error('Could not get 2d context')
246
252
 
247
253
  ctx.fillStyle = nameTagBackgroundColor
248
- ctx.fillRect(0, 0, canvas.width, canvas.height)
254
+ ctx.fillRect(0, 0, iconAreaWidth + textWidth, canvas.height)
249
255
  if (voiceState) {
250
256
  drawVoiceStateIcon(ctx, voiceState, iconAreaWidth / 2, canvas.height / 2, iconSize)
251
257
  }
@@ -0,0 +1,17 @@
1
+ //@ts-nocheck
2
+ export function createGeometryIndexArray(indices: readonly number[]): Uint16Array | Uint32Array {
3
+ let using32Array = false
4
+ for (let index = 0; index < indices.length; index++) {
5
+ if (indices[index] > 65535) {
6
+ using32Array = true
7
+ break
8
+ }
9
+ }
10
+ return using32Array ? new Uint32Array(indices) : new Uint16Array(indices)
11
+ }
12
+
13
+ export function appendGeometryIndices(target: number[], source: readonly number[], vertexOffset: number): void {
14
+ for (let index = 0; index < source.length; index++) {
15
+ target.push(source[index] + vertexOffset)
16
+ }
17
+ }
@@ -19,6 +19,7 @@ import { resolveBlockPropertiesForMeshing } from '../../mesher-shared/blockPrope
19
19
  import { isSemiTransparentBlockName } from '../../mesher-shared/models'
20
20
  import { faceIsCulled } from '../../mesher-shared/faceOcclusion'
21
21
  import { buildShaderCubesFromWords, getShaderCubeResources, tryBuildShaderCubeInstances } from './shaderCubeBridge'
22
+ import { appendGeometryIndices } from './geometryIndices'
22
23
  import { getSideShading, vertexLightFromAo } from '../../mesher-shared/vertexShading'
23
24
  import tintsJson from 'minecraft-data/minecraft-data/data/pc/1.16.2/tints.json'
24
25
 
@@ -986,10 +987,8 @@ export function renderWasmOutputToGeometry(
986
987
  blendSkyLights = liquidSkyLights.concat(blockBlendSkyLights)
987
988
  blendBlockLights = liquidBlockLights.concat(blockBlendBlockLights)
988
989
  blendUvs = liquidUvs.concat(blockBlendUvs)
989
- blendIndices.push(...liquidIndices)
990
- for (const idx of blockBlendIndices) {
991
- blendIndices.push(idx + liquidVertexCount)
992
- }
990
+ appendGeometryIndices(blendIndices, liquidIndices, 0)
991
+ appendGeometryIndices(blendIndices, blockBlendIndices, liquidVertexCount)
993
992
  }
994
993
 
995
994
  const shaderCubes = buildShaderCubesFromWords(shaderWordBuffer)
@@ -0,0 +1,71 @@
1
+ //@ts-nocheck
2
+ import { expect, test } from 'vitest'
3
+ import { appendGeometryIndices, createGeometryIndexArray } from '../bridge/geometryIndices'
4
+
5
+ function expectSameValues(actual: ArrayLike<number>, expected: ArrayLike<number>): void {
6
+ expect(actual.length).toBe(expected.length)
7
+ let firstMismatch = -1
8
+ for (let index = 0; index < expected.length; index++) {
9
+ if (actual[index] !== expected[index]) {
10
+ firstMismatch = index
11
+ break
12
+ }
13
+ }
14
+ expect(firstMismatch, 'First differing index; -1 means all values match').toBe(-1)
15
+ }
16
+
17
+ test('creates lossless index buffers without depending on argument count', () => {
18
+ const cases: Array<{ input: number[]; using32Array: boolean }> = [
19
+ { input: [], using32Array: false },
20
+ { input: [0, 65535, 7], using32Array: false },
21
+ { input: [0, 65536, 7], using32Array: true }
22
+ ]
23
+
24
+ for (const { input, using32Array } of cases) {
25
+ const inputBefore = input.slice()
26
+ const result = createGeometryIndexArray(input)
27
+ expect(result).toBeInstanceOf(using32Array ? Uint32Array : Uint16Array)
28
+ expect(Array.from(result)).toEqual(inputBefore)
29
+ expect(input).toEqual(inputBefore)
30
+ }
31
+
32
+ const source = new Array<number>(1_000_000).fill(65535)
33
+ source[0] = 0
34
+ source[500_000] = 123
35
+ const sourceBeforeSmall = source.slice()
36
+ const smallValues = createGeometryIndexArray(source)
37
+ expect(smallValues).toBeInstanceOf(Uint16Array)
38
+ expectSameValues(smallValues, sourceBeforeSmall)
39
+ expectSameValues(source, sourceBeforeSmall)
40
+
41
+ source[source.length - 1] = 65536
42
+ const sourceBeforeLarge = source.slice()
43
+ const largeValue = createGeometryIndexArray(source)
44
+ expect(largeValue).toBeInstanceOf(Uint32Array)
45
+ expectSameValues(largeValue, sourceBeforeLarge)
46
+ expectSameValues(source, sourceBeforeLarge)
47
+ })
48
+
49
+ test('appends large liquid indices before rebased block indices', () => {
50
+ const liquidIndices = new Array<number>(1_000_000).fill(0)
51
+ liquidIndices[0] = 2
52
+ liquidIndices[liquidIndices.length - 1] = 1
53
+ const blockBlendIndices = [0, 2, 1]
54
+ const liquidVertexCount = 3
55
+ const blendIndices: number[] = []
56
+ const liquidIndicesBefore = liquidIndices.slice()
57
+ const blockBlendIndicesBefore = blockBlendIndices.slice()
58
+
59
+ appendGeometryIndices(blendIndices, liquidIndices, 0)
60
+ appendGeometryIndices(blendIndices, blockBlendIndices, liquidVertexCount)
61
+
62
+ expect(blendIndices.length).toBe(liquidIndicesBefore.length + blockBlendIndicesBefore.length)
63
+ expectSameValues(blendIndices.slice(0, liquidIndicesBefore.length), liquidIndicesBefore)
64
+ expect(blendIndices.slice(liquidIndicesBefore.length)).toEqual([3, 5, 4])
65
+ expect(blockBlendIndices).toEqual(blockBlendIndicesBefore)
66
+ expectSameValues(liquidIndices, liquidIndicesBefore)
67
+
68
+ const snapshot = blendIndices.slice()
69
+ appendGeometryIndices(blendIndices, [], liquidVertexCount)
70
+ expectSameValues(blendIndices, snapshot)
71
+ })
@@ -2,6 +2,7 @@
2
2
  import { Vec3 } from 'vec3'
3
3
  import { convertChunkToWasm, getBlockMeta, type ChunkConversionResult } from '../bridge/convertChunk'
4
4
  import { extractColumnHeightmap, splitColumnWasmOutputToSections } from '../bridge/render-from-wasm'
5
+ import { createGeometryIndexArray } from '../bridge/geometryIndices'
5
6
  import { setBlockStatesData as setMesherData } from '../../mesher-shared/models'
6
7
  import { defaultMesherConfig, type MesherGeometryOutput, SECTION_HEIGHT } from '../../mesher-shared/shared'
7
8
  import { SHADER_CUBES_FORMAT_VERSION } from '../../mesher-shared/shaderCubeFormat'
@@ -1493,8 +1494,8 @@ function processColumnTick() {
1493
1494
  const hasLegacyMesh = hasOpaqueMesh || hasBlendMesh
1494
1495
  const hasShaderCubes = (exported?.shaderCubes?.count ?? 0) > 0
1495
1496
  if (exported && (hasLegacyMesh || hasShaderCubes)) {
1496
- const maxIndex = exported.geometry.indices.length > 0 ? Math.max(...exported.geometry.indices) : 0
1497
- const using32Array = maxIndex > 65535
1497
+ const indices = createGeometryIndexArray(exported.geometry.indices)
1498
+ const using32Array = indices instanceof Uint32Array
1498
1499
  geometry = {
1499
1500
  sectionYNumber: (sy - (config?.worldMinY || 0)) >> 4,
1500
1501
  chunkKey: worldColumnKey(sx, sz),
@@ -1513,7 +1514,7 @@ function processColumnTick() {
1513
1514
  skyLights: new Float32Array(exported.geometry.skyLights),
1514
1515
  blockLights: new Float32Array(exported.geometry.blockLights),
1515
1516
  uvs: new Float32Array(exported.geometry.uvs),
1516
- indices: using32Array ? new Uint32Array(exported.geometry.indices) : new Uint16Array(exported.geometry.indices),
1517
+ indices,
1517
1518
  indicesCount: exported.geometry.indices.length,
1518
1519
  using32Array,
1519
1520
  tiles: {},
@@ -1537,7 +1538,6 @@ function processColumnTick() {
1537
1538
  }
1538
1539
  }
1539
1540
  if (exported.blendGeometry && hasBlendMesh) {
1540
- const blendMax = Math.max(...exported.blendGeometry.indices)
1541
1541
  geometry.blend = {
1542
1542
  positions: new Float32Array(exported.blendGeometry.positions),
1543
1543
  normals: new Float32Array(exported.blendGeometry.normals),
@@ -1545,7 +1545,7 @@ function processColumnTick() {
1545
1545
  skyLights: new Float32Array(exported.blendGeometry.skyLights),
1546
1546
  blockLights: new Float32Array(exported.blendGeometry.blockLights),
1547
1547
  uvs: new Float32Array(exported.blendGeometry.uvs),
1548
- indices: blendMax > 65535 ? new Uint32Array(exported.blendGeometry.indices) : new Uint16Array(exported.blendGeometry.indices)
1548
+ indices: createGeometryIndexArray(exported.blendGeometry.indices)
1549
1549
  }
1550
1550
  }
1551
1551
  transferable = [