minecraft-renderer 0.1.76 → 0.1.78

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.76",
3
+ "version": "0.1.78",
4
4
  "description": "The most Modular Minecraft world renderer with Three.js WebGL backend",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -0,0 +1,46 @@
1
+ //@ts-nocheck
2
+ import { describe, expect, it, vi } from 'vitest'
3
+
4
+ vi.mock('./utils/skins', () => ({
5
+ stevePngUrl: '',
6
+ loadSkinImage: vi.fn(),
7
+ }))
8
+
9
+ import { PlayerObject } from 'skinview3d'
10
+ import { configurePlayerSkinMaterials } from './createPlayerObject'
11
+
12
+ describe('configurePlayerSkinMaterials', () => {
13
+ it('configures cutout materials and log-depth bias on arm/leg mats only', () => {
14
+ const playerObject = new PlayerObject()
15
+ const skin = playerObject.skin as any
16
+
17
+ configurePlayerSkinMaterials(playerObject)
18
+
19
+ const allMaterials = [
20
+ skin.layer1Material,
21
+ skin.layer1MaterialBiased,
22
+ skin.layer2Material,
23
+ skin.layer2MaterialBiased,
24
+ ]
25
+ for (const mat of allMaterials) {
26
+ expect(mat.transparent).toBe(false)
27
+ expect(mat.alphaTest).toBe(0.1)
28
+ expect(mat.depthWrite).toBe(true)
29
+ }
30
+
31
+ expect(skin.layer1MaterialBiased.userData.logDepthBiasApplied).toBe(true)
32
+ expect(skin.layer2MaterialBiased.userData.logDepthBiasApplied).toBe(true)
33
+ expect(typeof skin.layer1MaterialBiased.onBeforeCompile).toBe('function')
34
+ expect(typeof skin.layer2MaterialBiased.onBeforeCompile).toBe('function')
35
+ expect(skin.layer1MaterialBiased.onBeforeCompile).toBe(skin.layer2MaterialBiased.onBeforeCompile)
36
+
37
+ expect(skin.layer1Material.userData.logDepthBiasApplied).toBeUndefined()
38
+ expect(skin.layer2Material.userData.logDepthBiasApplied).toBeUndefined()
39
+ expect(skin.layer1Material.onBeforeCompile).not.toBe(skin.layer1MaterialBiased.onBeforeCompile)
40
+ expect(skin.layer2Material.onBeforeCompile).not.toBe(skin.layer2MaterialBiased.onBeforeCompile)
41
+
42
+ const firstCompile = skin.layer1MaterialBiased.onBeforeCompile
43
+ configurePlayerSkinMaterials(playerObject)
44
+ expect(skin.layer1MaterialBiased.onBeforeCompile).toBe(firstCompile)
45
+ })
46
+ })
@@ -10,7 +10,30 @@ export type PlayerObjectType = PlayerObject & {
10
10
  realUsername: string
11
11
  }
12
12
 
13
- /** Starfield + log-depth world: cutout skin mats need alphaTest and depthWrite (not mesh traverse). */
13
+ const LOG_DEPTH_BIAS = -2e-4 // tune visually; negative polygonOffset “closer”
14
+
15
+ function patchLogDepthBiasShader (shader: THREE.WebGLProgramParametersWithUniforms): void {
16
+ shader.uniforms.uLogDepthBias = { value: LOG_DEPTH_BIAS }
17
+ shader.fragmentShader = shader.fragmentShader.replace(
18
+ '#include <logdepthbuf_pars_fragment>',
19
+ `#include <logdepthbuf_pars_fragment>\nuniform float uLogDepthBias;`
20
+ )
21
+ shader.fragmentShader = shader.fragmentShader.replace(
22
+ '#include <logdepthbuf_fragment>',
23
+ `#if defined( USE_LOGARITHMIC_DEPTH_BUFFER )
24
+ gl_FragDepth = log2( vFragDepth ) * logDepthBufFC * 0.5 + uLogDepthBias;
25
+ #endif`
26
+ )
27
+ }
28
+
29
+ function applyLogDepthBias (material: THREE.Material): void {
30
+ if (material.userData.logDepthBiasApplied) return
31
+ material.userData.logDepthBiasApplied = true
32
+ material.onBeforeCompile = patchLogDepthBiasShader
33
+ material.needsUpdate = true
34
+ }
35
+
36
+ /** Log-depth world: opaque cutout mats (alphaTest + depthWrite, not transparent sort). */
14
37
  export function configurePlayerSkinMaterials (playerObject: PlayerObject): void {
15
38
  const skin = playerObject.skin as any
16
39
  const materials = [
@@ -20,10 +43,12 @@ export function configurePlayerSkinMaterials (playerObject: PlayerObject): void
20
43
  skin.layer2MaterialBiased,
21
44
  ]
22
45
  for (const mat of materials) {
23
- mat.transparent = true
46
+ mat.transparent = false
24
47
  mat.alphaTest = 0.1
25
48
  mat.depthWrite = true
26
49
  }
50
+ applyLogDepthBias(skin.layer1MaterialBiased)
51
+ applyLogDepthBias(skin.layer2MaterialBiased)
27
52
  }
28
53
 
29
54
  export function createPlayerObject (options: {
@@ -4,7 +4,7 @@ import * as nbt from 'prismarine-nbt'
4
4
  import { Vec3 } from 'vec3'
5
5
  import { MesherGeometryOutput } from '../mesher-shared/shared'
6
6
  import { getShaderCubeResources, SHADER_CUBES_WORDS_PER_FACE } from '../wasm-mesher/bridge/shaderCubeBridge'
7
- import { createCubeBlockMaterial, computeSectionOriginRel, setCubeSkyLevel, setCubeLightmapParams, type BlockLightmapParams } from './shaders/cubeBlockShader'
7
+ import { createCubeBlockMaterial, computeSectionOriginRel, setCubeSkyLevel, setCubeShadingTheme, setCubeLightmapParams, type BlockLightmapParams } from './shaders/cubeBlockShader'
8
8
  import { computeCameraRelativeUniforms, createGlobalLegacyBlendMaterial, createGlobalLegacyBlockMaterial, createLegacyBlockMaterial, setLegacyCameraOrigin, setLegacySkyLevel, setLegacyLightmapParams, type RenderOrigin } from './shaders/legacyBlockShader'
9
9
  import { LEGACY_SECTION_HALF_EXTENT, sectionIntersectsFrustum, setupLegacySectionMatrix, updateLegacySectionCullState } from './legacySectionCull'
10
10
  import { createShaderCubeMesh, disposeShaderCubeMesh } from './shaderCubeMesh'
@@ -266,6 +266,11 @@ export class ChunkMeshManager {
266
266
  this.blockEntityLightRegistry.setSkyLevel(value)
267
267
  }
268
268
 
269
+ setShadingTheme (theme: 'vanilla' | 'high-contrast', cardinalLight: string): void {
270
+ const cube = this.cubeShaderMaterial ?? (this.isShaderCubesGpuEnabled() ? this.getCubeShaderMaterial() : null)
271
+ if (cube) setCubeShadingTheme(cube, theme, cardinalLight)
272
+ }
273
+
269
274
  /** Vanilla-like lightmap curve params (live tuning via window.setBlockLightmap). */
270
275
  setBlockLightmapParams (params: BlockLightmapParams): void {
271
276
  const cube = this.cubeShaderMaterial ?? (this.isShaderCubesGpuEnabled() ? this.getCubeShaderMaterial() : null)
@@ -531,6 +536,8 @@ export class ChunkMeshManager {
531
536
  if (!this.cubeShaderMaterial) {
532
537
  this.cubeShaderMaterial = createCubeBlockMaterial()
533
538
  this.syncCubeShaderUniforms()
539
+ const cfg = this.worldRenderer.worldRendererConfig
540
+ setCubeShadingTheme(this.cubeShaderMaterial, cfg.shadingTheme, cfg.cardinalLight)
534
541
  }
535
542
  return this.cubeShaderMaterial
536
543
  }
@@ -199,6 +199,8 @@ uniform float u_skyLevel;
199
199
  uniform float u_lightCurve;
200
200
  uniform float u_minBrightness;
201
201
  uniform float u_lightGamma;
202
+ uniform float u_shadingTheme; // 0 = vanilla, 1 = high-contrast
203
+ uniform float u_cardinalLight; // 0 = default, 1 = nether
202
204
 
203
205
  in float v_blockLight;
204
206
  in float v_skyLight;
@@ -248,6 +250,24 @@ void applyFog() {
248
250
 
249
251
  ${APPLY_LIGHTMAP_GLSL}
250
252
 
253
+ const vec3 FACE_NORMAL[6] = vec3[6](
254
+ vec3(0.0, 1.0, 0.0), // UP
255
+ vec3(0.0,-1.0, 0.0), // DOWN
256
+ vec3(1.0, 0.0, 0.0), // EAST
257
+ vec3(-1.0,0.0, 0.0), // WEST
258
+ vec3(0.0, 0.0, 1.0), // SOUTH
259
+ vec3(0.0, 0.0,-1.0) // NORTH
260
+ );
261
+
262
+ float sideShadingFromFaceId(int faceId, float theme, float cardinal) {
263
+ vec3 n = FACE_NORMAL[faceId];
264
+ float hc = 0.8 + 0.5 * max(0.0, 0.66*n.x + 0.66*n.y + 0.33*n.z);
265
+ float nether = 0.5 + abs(0.1*n.x + 0.4*n.y + 0.3*n.z);
266
+ float vanilla = 0.75 + 0.25*n.y + 0.05*(abs(n.z) - 3.0*abs(n.x));
267
+ float nonHc = mix(vanilla, nether, cardinal);
268
+ return mix(nonHc, hc, theme);
269
+ }
270
+
251
271
  void main() {
252
272
  // Atlas sample (pixelated, no filtering)
253
273
  ivec2 atlasSize = textureSize(u_atlas, 0);
@@ -294,7 +314,9 @@ void main() {
294
314
 
295
315
  float L = max(v_blockLight, min(v_skyLight, u_skyLevel));
296
316
  float Lm = applyLightmap(L);
297
- float brightness = Lm * v_ao;
317
+ float aoFactor = mix(0.8 * v_ao + 0.2, v_ao, u_shadingTheme);
318
+ float side = sideShadingFromFaceId(v_faceId, u_shadingTheme, u_cardinalLight);
319
+ float brightness = Lm * aoFactor * side;
298
320
 
299
321
  // Opaque full cubes: always alpha 1 (legacy uses cutout material; avoids seeing blocks behind)
300
322
  FragColor = vec4(baseColor.rgb * tint * brightness, 1.0);
@@ -317,6 +339,8 @@ export function createCubeBlockMaterial(): THREE.ShaderMaterial {
317
339
  u_tintPalette: { value: null },
318
340
  u_debugMode: { value: 0 },
319
341
  u_skyLevel: { value: 1.0 },
342
+ u_shadingTheme: { value: 1.0 },
343
+ u_cardinalLight: { value: 0.0 },
320
344
  u_lightCurve: { value: DEFAULT_LIGHTMAP_PARAMS.curve },
321
345
  u_minBrightness: { value: DEFAULT_LIGHTMAP_PARAMS.minBrightness },
322
346
  u_lightGamma: { value: DEFAULT_LIGHTMAP_PARAMS.gamma },
@@ -344,6 +368,17 @@ export function setCubeSkyLevel (material: THREE.ShaderMaterial, value: number):
344
368
  if (u) u.value = value
345
369
  }
346
370
 
371
+ export function setCubeShadingTheme (
372
+ material: THREE.ShaderMaterial,
373
+ theme: 'vanilla' | 'high-contrast',
374
+ cardinalLight: string,
375
+ ): void {
376
+ const t = material.uniforms.u_shadingTheme
377
+ if (t) t.value = theme === 'high-contrast' ? 1.0 : 0.0
378
+ const c = material.uniforms.u_cardinalLight
379
+ if (c) c.value = cardinalLight === 'nether' ? 1.0 : 0.0
380
+ }
381
+
347
382
  export function setCubeLightmapParams (
348
383
  material: THREE.ShaderMaterial,
349
384
  params: BlockLightmapParams,
@@ -549,6 +549,12 @@ export class WorldRendererThree extends WorldRendererCommon {
549
549
  this.chunkMeshManager.syncCubeShaderUniforms()
550
550
  this.chunkMeshManager.syncLegacyShaderUniforms()
551
551
  })
552
+ const pushShading = () => {
553
+ const cfg = this.worldRendererConfig
554
+ this.chunkMeshManager.setShadingTheme(cfg.shadingTheme, cfg.cardinalLight)
555
+ }
556
+ this.onReactiveConfigUpdated('shadingTheme', pushShading)
557
+ this.onReactiveConfigUpdated('cardinalLight', pushShading)
552
558
  this.onReactiveConfigUpdated('futuristicReveal', () => {
553
559
  this.updateModulesFromConfig()
554
560
  })
@@ -17,7 +17,7 @@ import {
17
17
  } from '../bridge/shaderCubeBridge'
18
18
  import { GlobalBlockBuffer } from '../../three/globalBlockBuffer'
19
19
  import { buildVisibleCubeSpans } from '../../three/cubeDrawSpans'
20
- import { createCubeBlockMaterial, computeSectionOriginRel } from '../../three/shaders/cubeBlockShader'
20
+ import { createCubeBlockMaterial, computeSectionOriginRel, setCubeShadingTheme } from '../../three/shaders/cubeBlockShader'
21
21
  import * as THREE from 'three'
22
22
  import { renderWasmOutputToGeometry } from '../bridge/render-from-wasm'
23
23
 
@@ -36,6 +36,17 @@ beforeEach(() => {
36
36
  resetShaderCubeResources()
37
37
  })
38
38
 
39
+ test('setCubeShadingTheme: maps theme and cardinalLight to shader uniforms', () => {
40
+ const mat = createCubeBlockMaterial()
41
+ setCubeShadingTheme(mat, 'vanilla', 'default')
42
+ expect(mat.uniforms.u_shadingTheme.value).toBe(0)
43
+ expect(mat.uniforms.u_cardinalLight.value).toBe(0)
44
+ setCubeShadingTheme(mat, 'high-contrast', 'nether')
45
+ expect(mat.uniforms.u_shadingTheme.value).toBe(1)
46
+ expect(mat.uniforms.u_cardinalLight.value).toBe(1)
47
+ mat.dispose()
48
+ })
49
+
39
50
  test('packWord2: AO diagonal flip sets bit 12', () => {
40
51
  const words: number[] = []
41
52
  const block = {