minerender 2.1.0-alpha.73 → 2.1.0-alpha.75

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.
@@ -1031,7 +1031,7 @@ var init_Materials = __esm({
1031
1031
  };
1032
1032
  return material;
1033
1033
  }
1034
- static createShadedCanvasMaterial(canvas, transparent = false, shade = false) {
1034
+ static createShadedCanvasMaterial(canvas, transparent = false, shade = false, boundUvs = false) {
1035
1035
  const vertShader = `
1036
1036
  uniform bool SHADE;
1037
1037
 
@@ -1041,6 +1041,10 @@ var init_Materials = __esm({
1041
1041
  #else
1042
1042
  varying vec2 vUv;
1043
1043
  #endif
1044
+ #ifdef BOUND_UVS
1045
+ attribute vec4 uvBounds;
1046
+ flat out vec4 vUvBounds;
1047
+ #endif
1044
1048
  varying vec3 vTint;
1045
1049
  varying float light;
1046
1050
  varying float lift;
@@ -1076,13 +1080,17 @@ var init_Materials = __esm({
1076
1080
  }
1077
1081
 
1078
1082
  vUv = uv;
1083
+ #ifdef BOUND_UVS
1084
+ vUvBounds = uvBounds;
1085
+ #endif
1079
1086
  vTint = color;
1080
1087
 
1088
+ vec4 mvPosition = vec4(position, 1.0);
1081
1089
  #ifdef USE_INSTANCING
1082
- gl_Position = projectionMatrix * viewMatrix * modelMatrix * instanceMatrix * vec4(position, 1.0);
1083
- #else
1084
- gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
1090
+ mvPosition = instanceMatrix * mvPosition;
1085
1091
  #endif
1092
+ mvPosition = modelViewMatrix * mvPosition;
1093
+ gl_Position = projectionMatrix * mvPosition;
1086
1094
 
1087
1095
  }
1088
1096
  `;
@@ -1102,13 +1110,22 @@ var init_Materials = __esm({
1102
1110
  #else
1103
1111
  varying vec2 vUv;
1104
1112
  #endif
1113
+ #ifdef BOUND_UVS
1114
+ flat in vec4 vUvBounds;
1115
+ #endif
1105
1116
  varying vec3 vTint;
1106
1117
  varying float light;
1107
1118
  varying float lift;
1108
1119
 
1109
1120
  void main(void)
1110
1121
  {
1111
- vec4 color = texture2D(map, vUv);
1122
+ #ifdef BOUND_UVS
1123
+ // Keep edge samples inside their texture even without centroid interpolation.
1124
+ vec2 mapUv = clamp(vUv, vUvBounds.xy, vUvBounds.zw);
1125
+ #else
1126
+ vec2 mapUv = vUv;
1127
+ #endif
1128
+ vec4 color = texture2D(map, mapUv);
1112
1129
  color.rgb *= vTint;
1113
1130
 
1114
1131
  if (color.a < 0.01) discard;
@@ -1129,7 +1146,8 @@ var init_Materials = __esm({
1129
1146
  }
1130
1147
  `;
1131
1148
  try {
1132
- return new ShaderMaterial({
1149
+ const material = new ShaderMaterial({
1150
+ defines: boundUvs ? { BOUND_UVS: true } : {},
1133
1151
  uniforms: {
1134
1152
  SHADE: { value: true },
1135
1153
  BRIGHTNESS: { value: 1 },
@@ -1147,6 +1165,8 @@ var init_Materials = __esm({
1147
1165
  side: transparent ? DoubleSide : FrontSide,
1148
1166
  alphaTest: 0.5
1149
1167
  });
1168
+ Object.assign(material.defaultAttributeValues, { uvBounds: [0, 0, 1, 1] });
1169
+ return material;
1150
1170
  } catch (e) {
1151
1171
  console.warn(e);
1152
1172
  }
@@ -3066,6 +3086,37 @@ var init_UVMapper = __esm({
3066
3086
  static facesToUv(faces, originalTextureSize, actualTextureSize) {
3067
3087
  return new Float32BufferAttribute2(this.facesToUvArray(faces, originalTextureSize, actualTextureSize), 2);
3068
3088
  }
3089
+ /** Bounds texture sampling to texel centres without changing cropped face UVs. */
3090
+ static setAtlasUvBounds(geometry, faces, atlas) {
3091
+ const uv = geometry.getAttribute("uv");
3092
+ const bounds = new Float32Array(uv.count * 4);
3093
+ for (const [faceIndex, faceName] of CUBE_FACES.entries()) {
3094
+ const texture = faces[faceName]?.texture?.substring(1);
3095
+ const position = texture && atlas.positions[texture];
3096
+ const size = texture && atlas.sizes[texture];
3097
+ let minU = Infinity, minV = Infinity, maxU = -Infinity, maxV = -Infinity;
3098
+ if (position && size) {
3099
+ minU = position[0] / atlas.image.width;
3100
+ maxU = (position[0] + size[0]) / atlas.image.width;
3101
+ minV = 1 - (position[1] + size[1]) / atlas.image.height;
3102
+ maxV = 1 - position[1] / atlas.image.height;
3103
+ } else {
3104
+ for (let corner = 0; corner < 4; corner++) {
3105
+ const index = faceIndex * 4 + corner;
3106
+ minU = Math.min(minU, uv.getX(index));
3107
+ minV = Math.min(minV, uv.getY(index));
3108
+ maxU = Math.max(maxU, uv.getX(index));
3109
+ maxV = Math.max(maxV, uv.getY(index));
3110
+ }
3111
+ }
3112
+ const insetU = Math.min(0.5 / atlas.image.width, (maxU - minU) / 2);
3113
+ const insetV = Math.min(0.5 / atlas.image.height, (maxV - minV) / 2);
3114
+ for (let corner = 0; corner < 4; corner++) {
3115
+ bounds.set([minU + insetU, minV + insetV, maxU - insetU, maxV - insetV], (faceIndex * 4 + corner) * 4);
3116
+ }
3117
+ }
3118
+ geometry.setAttribute("uvBounds", new Float32BufferAttribute2(bounds, 4));
3119
+ }
3069
3120
  static lockUvs(geometry, faces, atlas, rotation) {
3070
3121
  const uv = geometry.getAttribute("uv");
3071
3122
  for (let faceIndex = 0; faceIndex < CUBE_FACES.length; faceIndex++) {
@@ -5477,6 +5528,7 @@ var init_ModelObject = __esm({
5477
5528
  if (this.options.uvLockRotation) {
5478
5529
  UVMapper.lockUvs(elGeo, el.faces, this.atlas, new Euler7(...this.options.uvLockRotation));
5479
5530
  }
5531
+ UVMapper.setAtlasUvBounds(elGeo, el.faces, this.atlas);
5480
5532
  if (this.options.tints) {
5481
5533
  const colors = new Float32Array(elGeo.getAttribute("position").count * 3).fill(1);
5482
5534
  for (const [faceIndex, faceName] of CUBE_FACES.entries()) {
@@ -5542,7 +5594,7 @@ var init_ModelObject = __esm({
5542
5594
  }
5543
5595
  applyTextures() {
5544
5596
  if (this.atlas) {
5545
- const mat = Materials.createShadedCanvasMaterial(this.atlas.image.canvas, this.atlas.hasTransparency, false);
5597
+ const mat = Materials.createShadedCanvasMaterial(this.atlas.image.canvas, this.atlas.hasTransparency, false, true);
5546
5598
  this.atlasMaterial = mat;
5547
5599
  this.atlasTexture = mat.uniforms?.map?.value ?? mat.map;
5548
5600
  this.iterateAllMeshes((mesh) => {
@@ -15284,13 +15336,7 @@ var MineRenderWorld = class {
15284
15336
  async placeChunk(chunk, executor) {
15285
15337
  const changes = /* @__PURE__ */ new Map();
15286
15338
  try {
15287
- const previous = [...this._chunks.entries()].filter(([, section]) => section.x === chunk.x && section.z === chunk.z);
15288
- for (const [key, section] of previous) {
15289
- this._chunks.delete(key);
15290
- await section.clear(async (positions) => {
15291
- for (const pos of positions) changes.set(pos.toArray().join(","), pos);
15292
- });
15293
- }
15339
+ await this.clearChunkColumn(chunk.x, chunk.z, changes);
15294
15340
  for (const section of chunk.sections) {
15295
15341
  const blocks = [];
15296
15342
  for (let index = 0; index < 4096; index++) {
@@ -15310,6 +15356,31 @@ var MineRenderWorld = class {
15310
15356
  await this.updateCulling([...changes.values()]);
15311
15357
  }
15312
15358
  }
15359
+ /**
15360
+ * Removes all sections at integer chunk-column coordinates, then refreshes neighboring faces and fluids.
15361
+ * Coordinates match AnvilChunk.x/z, not block positions. Missing columns are ignored.
15362
+ */
15363
+ async unloadChunkColumn(x, z) {
15364
+ if (!Number.isInteger(x) || !Number.isInteger(z)) {
15365
+ throw new RangeError("Chunk column coordinates must be integers");
15366
+ }
15367
+ await this.culling;
15368
+ const changes = /* @__PURE__ */ new Map();
15369
+ try {
15370
+ await this.clearChunkColumn(x, z, changes);
15371
+ } finally {
15372
+ await this.updateCulling([...changes.values()]);
15373
+ }
15374
+ }
15375
+ async clearChunkColumn(x, z, changes) {
15376
+ const previous = [...this._chunks.entries()].filter(([, section]) => section.x === x && section.z === z);
15377
+ for (const [key, section] of previous) {
15378
+ this._chunks.delete(key);
15379
+ await section.clear(async (positions) => {
15380
+ for (const pos of positions) changes.set(pos.toArray().join(","), pos);
15381
+ });
15382
+ }
15383
+ }
15313
15384
  async clear() {
15314
15385
  await this.culling;
15315
15386
  const chunks = [...this._chunks.values()];
@@ -15478,14 +15549,17 @@ var SectionMesh = class _SectionMesh extends Group2 {
15478
15549
  for (const { atlas, x, y } of page.placements) {
15479
15550
  context.drawImage(atlas.image.canvas, x, y);
15480
15551
  }
15481
- const positions = [], normals = [], uvs = [], colors = [], indices = [];
15552
+ const positions = [], normals = [], uvs = [], uvBounds = [], colors = [], indices = [];
15482
15553
  for (const { index, template, cullMask } of page.entries) {
15483
15554
  const position = template.geometry.getAttribute("position");
15484
15555
  const normal = template.geometry.getAttribute("normal");
15485
15556
  const uv = template.geometry.getAttribute("uv");
15557
+ const bounds = template.geometry.getAttribute("uvBounds");
15486
15558
  const color = template.geometry.getAttribute("color");
15487
15559
  const sourceIndices = template.geometry.getIndex();
15488
15560
  const placement = locations.get(template.atlas).placement;
15561
+ const mapU = (u) => (placement.x + u * template.atlas.image.width) / page.width;
15562
+ const mapV = (v) => 1 - (placement.y + (1 - v) * template.atlas.image.height) / page.height;
15489
15563
  const offsetX = index % 16 * 16;
15490
15564
  const offsetY = Math.floor(index / 256) * 16;
15491
15565
  const offsetZ = Math.floor(index / 16) % 16 * 16;
@@ -15496,9 +15570,12 @@ var SectionMesh = class _SectionMesh extends Group2 {
15496
15570
  const vertex = face * 4 + corner;
15497
15571
  positions.push(position.getX(vertex) + offsetX, position.getY(vertex) + offsetY, position.getZ(vertex) + offsetZ);
15498
15572
  normals.push(normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex));
15499
- uvs.push(
15500
- (placement.x + uv.getX(vertex) * template.atlas.image.width) / page.width,
15501
- 1 - (placement.y + (1 - uv.getY(vertex)) * template.atlas.image.height) / page.height
15573
+ uvs.push(mapU(uv.getX(vertex)), mapV(uv.getY(vertex)));
15574
+ uvBounds.push(
15575
+ mapU(bounds?.getX(vertex) ?? 0),
15576
+ mapV(bounds?.getY(vertex) ?? 0),
15577
+ mapU(bounds?.getZ(vertex) ?? 1),
15578
+ mapV(bounds?.getW(vertex) ?? 1)
15502
15579
  );
15503
15580
  colors.push(color?.getX(vertex) ?? 1, color?.getY(vertex) ?? 1, color?.getZ(vertex) ?? 1);
15504
15581
  }
@@ -15509,11 +15586,12 @@ var SectionMesh = class _SectionMesh extends Group2 {
15509
15586
  geometry.setAttribute("position", new Float32BufferAttribute8(positions, 3));
15510
15587
  geometry.setAttribute("normal", new Float32BufferAttribute8(normals, 3));
15511
15588
  geometry.setAttribute("uv", new Float32BufferAttribute8(uvs, 2));
15589
+ geometry.setAttribute("uvBounds", new Float32BufferAttribute8(uvBounds, 4));
15512
15590
  geometry.setAttribute("color", new Float32BufferAttribute8(colors, 3));
15513
15591
  geometry.setIndex(indices);
15514
15592
  geometry.computeBoundingBox();
15515
15593
  geometry.computeBoundingSphere();
15516
- const material = Materials.createShadedCanvasMaterial(canvas, false);
15594
+ const material = Materials.createShadedCanvasMaterial(canvas, false, false, true);
15517
15595
  material.vertexColors = true;
15518
15596
  const texture = material.uniforms?.map?.value ?? material.map;
15519
15597
  const mesh = new Mesh9(geometry, material);