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.
@@ -981,7 +981,7 @@ var init_Materials = __esm({
981
981
  };
982
982
  return material;
983
983
  }
984
- static createShadedCanvasMaterial(canvas, transparent = false, shade = false) {
984
+ static createShadedCanvasMaterial(canvas, transparent = false, shade = false, boundUvs = false) {
985
985
  const vertShader = `
986
986
  uniform bool SHADE;
987
987
 
@@ -991,6 +991,10 @@ var init_Materials = __esm({
991
991
  #else
992
992
  varying vec2 vUv;
993
993
  #endif
994
+ #ifdef BOUND_UVS
995
+ attribute vec4 uvBounds;
996
+ flat out vec4 vUvBounds;
997
+ #endif
994
998
  varying vec3 vTint;
995
999
  varying float light;
996
1000
  varying float lift;
@@ -1026,13 +1030,17 @@ var init_Materials = __esm({
1026
1030
  }
1027
1031
 
1028
1032
  vUv = uv;
1033
+ #ifdef BOUND_UVS
1034
+ vUvBounds = uvBounds;
1035
+ #endif
1029
1036
  vTint = color;
1030
1037
 
1038
+ vec4 mvPosition = vec4(position, 1.0);
1031
1039
  #ifdef USE_INSTANCING
1032
- gl_Position = projectionMatrix * viewMatrix * modelMatrix * instanceMatrix * vec4(position, 1.0);
1033
- #else
1034
- gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
1040
+ mvPosition = instanceMatrix * mvPosition;
1035
1041
  #endif
1042
+ mvPosition = modelViewMatrix * mvPosition;
1043
+ gl_Position = projectionMatrix * mvPosition;
1036
1044
 
1037
1045
  }
1038
1046
  `;
@@ -1052,13 +1060,22 @@ var init_Materials = __esm({
1052
1060
  #else
1053
1061
  varying vec2 vUv;
1054
1062
  #endif
1063
+ #ifdef BOUND_UVS
1064
+ flat in vec4 vUvBounds;
1065
+ #endif
1055
1066
  varying vec3 vTint;
1056
1067
  varying float light;
1057
1068
  varying float lift;
1058
1069
 
1059
1070
  void main(void)
1060
1071
  {
1061
- vec4 color = texture2D(map, vUv);
1072
+ #ifdef BOUND_UVS
1073
+ // Keep edge samples inside their texture even without centroid interpolation.
1074
+ vec2 mapUv = clamp(vUv, vUvBounds.xy, vUvBounds.zw);
1075
+ #else
1076
+ vec2 mapUv = vUv;
1077
+ #endif
1078
+ vec4 color = texture2D(map, mapUv);
1062
1079
  color.rgb *= vTint;
1063
1080
 
1064
1081
  if (color.a < 0.01) discard;
@@ -1079,7 +1096,8 @@ var init_Materials = __esm({
1079
1096
  }
1080
1097
  `;
1081
1098
  try {
1082
- return new ShaderMaterial({
1099
+ const material = new ShaderMaterial({
1100
+ defines: boundUvs ? { BOUND_UVS: true } : {},
1083
1101
  uniforms: {
1084
1102
  SHADE: { value: true },
1085
1103
  BRIGHTNESS: { value: 1 },
@@ -1097,6 +1115,8 @@ var init_Materials = __esm({
1097
1115
  side: transparent ? DoubleSide : FrontSide,
1098
1116
  alphaTest: 0.5
1099
1117
  });
1118
+ Object.assign(material.defaultAttributeValues, { uvBounds: [0, 0, 1, 1] });
1119
+ return material;
1100
1120
  } catch (e) {
1101
1121
  console.warn(e);
1102
1122
  }
@@ -2977,6 +2997,37 @@ var init_UVMapper = __esm({
2977
2997
  static facesToUv(faces, originalTextureSize, actualTextureSize) {
2978
2998
  return new Float32BufferAttribute2(this.facesToUvArray(faces, originalTextureSize, actualTextureSize), 2);
2979
2999
  }
3000
+ /** Bounds texture sampling to texel centres without changing cropped face UVs. */
3001
+ static setAtlasUvBounds(geometry, faces, atlas) {
3002
+ const uv = geometry.getAttribute("uv");
3003
+ const bounds = new Float32Array(uv.count * 4);
3004
+ for (const [faceIndex, faceName] of CUBE_FACES.entries()) {
3005
+ const texture = faces[faceName]?.texture?.substring(1);
3006
+ const position = texture && atlas.positions[texture];
3007
+ const size = texture && atlas.sizes[texture];
3008
+ let minU = Infinity, minV = Infinity, maxU = -Infinity, maxV = -Infinity;
3009
+ if (position && size) {
3010
+ minU = position[0] / atlas.image.width;
3011
+ maxU = (position[0] + size[0]) / atlas.image.width;
3012
+ minV = 1 - (position[1] + size[1]) / atlas.image.height;
3013
+ maxV = 1 - position[1] / atlas.image.height;
3014
+ } else {
3015
+ for (let corner = 0; corner < 4; corner++) {
3016
+ const index = faceIndex * 4 + corner;
3017
+ minU = Math.min(minU, uv.getX(index));
3018
+ minV = Math.min(minV, uv.getY(index));
3019
+ maxU = Math.max(maxU, uv.getX(index));
3020
+ maxV = Math.max(maxV, uv.getY(index));
3021
+ }
3022
+ }
3023
+ const insetU = Math.min(0.5 / atlas.image.width, (maxU - minU) / 2);
3024
+ const insetV = Math.min(0.5 / atlas.image.height, (maxV - minV) / 2);
3025
+ for (let corner = 0; corner < 4; corner++) {
3026
+ bounds.set([minU + insetU, minV + insetV, maxU - insetU, maxV - insetV], (faceIndex * 4 + corner) * 4);
3027
+ }
3028
+ }
3029
+ geometry.setAttribute("uvBounds", new Float32BufferAttribute2(bounds, 4));
3030
+ }
2980
3031
  static lockUvs(geometry, faces, atlas, rotation) {
2981
3032
  const uv = geometry.getAttribute("uv");
2982
3033
  for (let faceIndex = 0; faceIndex < CUBE_FACES.length; faceIndex++) {
@@ -5383,6 +5434,7 @@ var init_ModelObject = __esm({
5383
5434
  if (this.options.uvLockRotation) {
5384
5435
  UVMapper.lockUvs(elGeo, el.faces, this.atlas, new Euler7(...this.options.uvLockRotation));
5385
5436
  }
5437
+ UVMapper.setAtlasUvBounds(elGeo, el.faces, this.atlas);
5386
5438
  if (this.options.tints) {
5387
5439
  const colors = new Float32Array(elGeo.getAttribute("position").count * 3).fill(1);
5388
5440
  for (const [faceIndex, faceName] of CUBE_FACES.entries()) {
@@ -5448,7 +5500,7 @@ var init_ModelObject = __esm({
5448
5500
  }
5449
5501
  applyTextures() {
5450
5502
  if (this.atlas) {
5451
- const mat = Materials.createShadedCanvasMaterial(this.atlas.image.canvas, this.atlas.hasTransparency, false);
5503
+ const mat = Materials.createShadedCanvasMaterial(this.atlas.image.canvas, this.atlas.hasTransparency, false, true);
5452
5504
  this.atlasMaterial = mat;
5453
5505
  this.atlasTexture = mat.uniforms?.map?.value ?? mat.map;
5454
5506
  this.iterateAllMeshes((mesh) => {
@@ -15227,13 +15279,7 @@ var MineRenderWorld = class {
15227
15279
  async placeChunk(chunk, executor) {
15228
15280
  const changes = /* @__PURE__ */ new Map();
15229
15281
  try {
15230
- const previous = [...this._chunks.entries()].filter(([, section]) => section.x === chunk.x && section.z === chunk.z);
15231
- for (const [key, section] of previous) {
15232
- this._chunks.delete(key);
15233
- await section.clear(async (positions) => {
15234
- for (const pos of positions) changes.set(pos.toArray().join(","), pos);
15235
- });
15236
- }
15282
+ await this.clearChunkColumn(chunk.x, chunk.z, changes);
15237
15283
  for (const section of chunk.sections) {
15238
15284
  const blocks = [];
15239
15285
  for (let index = 0; index < 4096; index++) {
@@ -15253,6 +15299,31 @@ var MineRenderWorld = class {
15253
15299
  await this.updateCulling([...changes.values()]);
15254
15300
  }
15255
15301
  }
15302
+ /**
15303
+ * Removes all sections at integer chunk-column coordinates, then refreshes neighboring faces and fluids.
15304
+ * Coordinates match AnvilChunk.x/z, not block positions. Missing columns are ignored.
15305
+ */
15306
+ async unloadChunkColumn(x, z) {
15307
+ if (!Number.isInteger(x) || !Number.isInteger(z)) {
15308
+ throw new RangeError("Chunk column coordinates must be integers");
15309
+ }
15310
+ await this.culling;
15311
+ const changes = /* @__PURE__ */ new Map();
15312
+ try {
15313
+ await this.clearChunkColumn(x, z, changes);
15314
+ } finally {
15315
+ await this.updateCulling([...changes.values()]);
15316
+ }
15317
+ }
15318
+ async clearChunkColumn(x, z, changes) {
15319
+ const previous = [...this._chunks.entries()].filter(([, section]) => section.x === x && section.z === z);
15320
+ for (const [key, section] of previous) {
15321
+ this._chunks.delete(key);
15322
+ await section.clear(async (positions) => {
15323
+ for (const pos of positions) changes.set(pos.toArray().join(","), pos);
15324
+ });
15325
+ }
15326
+ }
15256
15327
  async clear() {
15257
15328
  await this.culling;
15258
15329
  const chunks = [...this._chunks.values()];
@@ -15421,14 +15492,17 @@ var SectionMesh = class _SectionMesh extends Group2 {
15421
15492
  for (const { atlas, x, y } of page.placements) {
15422
15493
  context.drawImage(atlas.image.canvas, x, y);
15423
15494
  }
15424
- const positions = [], normals = [], uvs = [], colors = [], indices = [];
15495
+ const positions = [], normals = [], uvs = [], uvBounds = [], colors = [], indices = [];
15425
15496
  for (const { index, template, cullMask } of page.entries) {
15426
15497
  const position = template.geometry.getAttribute("position");
15427
15498
  const normal = template.geometry.getAttribute("normal");
15428
15499
  const uv = template.geometry.getAttribute("uv");
15500
+ const bounds = template.geometry.getAttribute("uvBounds");
15429
15501
  const color = template.geometry.getAttribute("color");
15430
15502
  const sourceIndices = template.geometry.getIndex();
15431
15503
  const placement = locations.get(template.atlas).placement;
15504
+ const mapU = (u) => (placement.x + u * template.atlas.image.width) / page.width;
15505
+ const mapV = (v) => 1 - (placement.y + (1 - v) * template.atlas.image.height) / page.height;
15432
15506
  const offsetX = index % 16 * 16;
15433
15507
  const offsetY = Math.floor(index / 256) * 16;
15434
15508
  const offsetZ = Math.floor(index / 16) % 16 * 16;
@@ -15439,9 +15513,12 @@ var SectionMesh = class _SectionMesh extends Group2 {
15439
15513
  const vertex = face * 4 + corner;
15440
15514
  positions.push(position.getX(vertex) + offsetX, position.getY(vertex) + offsetY, position.getZ(vertex) + offsetZ);
15441
15515
  normals.push(normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex));
15442
- uvs.push(
15443
- (placement.x + uv.getX(vertex) * template.atlas.image.width) / page.width,
15444
- 1 - (placement.y + (1 - uv.getY(vertex)) * template.atlas.image.height) / page.height
15516
+ uvs.push(mapU(uv.getX(vertex)), mapV(uv.getY(vertex)));
15517
+ uvBounds.push(
15518
+ mapU(bounds?.getX(vertex) ?? 0),
15519
+ mapV(bounds?.getY(vertex) ?? 0),
15520
+ mapU(bounds?.getZ(vertex) ?? 1),
15521
+ mapV(bounds?.getW(vertex) ?? 1)
15445
15522
  );
15446
15523
  colors.push(color?.getX(vertex) ?? 1, color?.getY(vertex) ?? 1, color?.getZ(vertex) ?? 1);
15447
15524
  }
@@ -15452,11 +15529,12 @@ var SectionMesh = class _SectionMesh extends Group2 {
15452
15529
  geometry.setAttribute("position", new Float32BufferAttribute8(positions, 3));
15453
15530
  geometry.setAttribute("normal", new Float32BufferAttribute8(normals, 3));
15454
15531
  geometry.setAttribute("uv", new Float32BufferAttribute8(uvs, 2));
15532
+ geometry.setAttribute("uvBounds", new Float32BufferAttribute8(uvBounds, 4));
15455
15533
  geometry.setAttribute("color", new Float32BufferAttribute8(colors, 3));
15456
15534
  geometry.setIndex(indices);
15457
15535
  geometry.computeBoundingBox();
15458
15536
  geometry.computeBoundingSphere();
15459
- const material = Materials.createShadedCanvasMaterial(canvas, false);
15537
+ const material = Materials.createShadedCanvasMaterial(canvas, false, false, true);
15460
15538
  material.vertexColors = true;
15461
15539
  const texture = material.uniforms?.map?.value ?? material.map;
15462
15540
  const mesh = new Mesh9(geometry, material);