ugly-game 0.5.11 → 0.5.13

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.
@@ -107,7 +107,7 @@ export declare class GpuShadowRenderer {
107
107
  depth: GPUTextureView;
108
108
  w: number;
109
109
  h: number;
110
- }, tint?: [number, number, number, number], extraShadow?: (sp: GPURenderPassEncoder, lightVP: Float32Array) => void): void;
110
+ }, tint?: [number, number, number, number], extraShadow?: (sp: GPURenderPassEncoder, lightVP: Float32Array) => void, up?: [number, number, number]): void;
111
111
  /** Read back the last frame's GPU cull result (camera-visible, light-visible). Does its OWN copy+submit so it's
112
112
  * safe to call from a live render loop (no per-frame readback cost, no race with the ongoing frames). IR/tests. */
113
113
  readCullStats(): Promise<{
package/dist/gpuShadow.js CHANGED
@@ -51,6 +51,7 @@ struct Cam {
51
51
  sunCol: vec4<f32>, // xyz = sun colour, w = shadow map size (texels)
52
52
  tint: vec4<f32>, // rgb = per-planet tint · w = tint strength (0 = none)
53
53
  atlas: vec4<f32>, // x = useAtlas (1/0)
54
+ up: vec4<f32>, // xyz = local gravity up (for hemisphere face-shading readability); w unused
54
55
  };
55
56
  struct Inst { pos: vec4<f32>, scl: vec4<f32>, rot: vec4<f32>, aux: vec4<f32> }; // pos.w = hue (<0 ⇒ ground) · scl.w = emissive/alpha · rot = quaternion · aux.x = atlas tile (<0 ⇒ hue) · aux.y = up-face code
56
57
  @group(0) @binding(0) var<uniform> cam: Cam;
@@ -260,14 +261,21 @@ fn blockShade(fx: f32, fz: f32) -> f32 {
260
261
  var variant = 1u; // side
261
262
  if (d > 0.5) { variant = 0u; } else if (d < -0.5) { variant = 2u; }
262
263
  let rect = tileRects[u32(i.tile) * 3u + variant];
263
- let uv = rect.xy + fract(vec2(fx, fz)) * rect.zw;
264
+ // INSET the per-block UV so it never samples the tile's edge texels — otherwise linear filtering bleeds into
265
+ // the neighbouring atlas tile at every block boundary, drawing a hard seam GRID. 4% inset kills the seam.
266
+ let uv = rect.xy + (fract(vec2(fx, fz)) * 0.92 + 0.04) * rect.zw;
264
267
  let texc = textureSampleLevel(atlasTex, atlasSamp, uv, 0.0).rgb; // explicit LOD (early-return above makes control flow non-uniform)
265
268
  base = mix(texc, texc * cam.tint.rgb, cam.tint.w); // per-planet tint
266
269
  }
267
270
  // per-block edge AO makes each voxel visible on FLAT-hue cubes, but reads as a hard GRID over textures — so the
268
- // atlas path uses a soft, mostly-flat AO (texture already supplies surface detail) instead of the strong grid.
269
- let ao = select(blockShade(fx, fz), mix(blockShade(fx, fz), 1.0, 0.8), cam.atlas.x > 0.5 && i.tile >= 0.0);
270
- let lit = base * (cam.sun.w + cam.sunCol.xyz * ndl * sh) * ao;
271
+ // atlas path drops it entirely (the texture already supplies surface detail).
272
+ let ao = select(blockShade(fx, fz), 1.0, cam.atlas.x > 0.5 && i.tile >= 0.0);
273
+ // HEMISPHERE FACE-SHADING: brighten faces pointing toward the local up (sky), darken side/down faces — so every
274
+ // block's orientation reads at a glance (edges/corners legible) WITHOUT a per-block grid. Modulates the ambient
275
+ // sky term (not the direct sun), so sunlit faces keep full key light while shaded areas still show 3D form.
276
+ let hemi = dot(N, cam.up.xyz) * 0.5 + 0.5; // 0 = down-facing, 0.5 = side, 1 = up-facing
277
+ let amb = cam.sun.w * mix(0.55, 1.15, hemi);
278
+ let lit = base * (amb + cam.sunCol.xyz * ndl * sh) * ao;
271
279
  return vec4(pow(clamp(lit, vec3(0.0), vec3(1.0)), vec3(0.4545)), 1.0); // gamma
272
280
  }
273
281
  `;
@@ -302,7 +310,7 @@ export class GpuShadowRenderer {
302
310
  mainBGL;
303
311
  shadowSampler;
304
312
  count = 0;
305
- cam = new Float32Array(48); // 2×mat4 + sun + sunCol + tint + atlas
313
+ cam = new Float32Array(52); // 2×mat4 + sun + sunCol + tint + atlas + up
306
314
  atlasView;
307
315
  atlasSampler;
308
316
  tileRectBuf;
@@ -583,7 +591,7 @@ export class GpuShadowRenderer {
583
591
  /** camVP/lightVP: column-major Float32Array(16). sunDir: travel direction. Pass `occlusion` (the depth target +
584
592
  * its size) to enable Hi-Z occlusion culling on the camera pass — the renderer rebuilds a coarse Hi-Z from that
585
593
  * depth each frame and the NEXT frame's cull uses it (hole-free: the first frame + any resize skip occlusion). */
586
- frame(enc, colorView, sceneDepth, camVP, lightVP, sunDir, sunCol, ambient, load = false, occlusion, tint = [1, 1, 1, 0], extraShadow) {
594
+ frame(enc, colorView, sceneDepth, camVP, lightVP, sunDir, sunCol, ambient, load = false, occlusion, tint = [1, 1, 1, 0], extraShadow, up = [0, 1, 0]) {
587
595
  this.cam.set(camVP, 0);
588
596
  this.cam.set(lightVP, 16);
589
597
  this.cam[32] = sunDir[0];
@@ -599,6 +607,9 @@ export class GpuShadowRenderer {
599
607
  this.cam[42] = tint[2];
600
608
  this.cam[43] = tint[3];
601
609
  this.cam[44] = this.useAtlas;
610
+ this.cam[48] = up[0];
611
+ this.cam[49] = up[1];
612
+ this.cam[50] = up[2];
602
613
  this.device.queue.writeBuffer(this.camBuf, 0, this.cam);
603
614
  if (occlusion)
604
615
  this.ensureHiZ(occlusion.w, occlusion.h);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugly-game",
3
- "version": "0.5.11",
3
+ "version": "0.5.13",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {