minerender 2.1.0-beta.6 → 2.1.0-beta.8

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.
Files changed (128) hide show
  1. package/README.md +10 -1
  2. package/dist/browser/esm/Materials.mjs +3 -1
  3. package/dist/browser/esm/Materials.mjs.map +1 -1
  4. package/dist/browser/esm/Ticker.mjs +3 -1
  5. package/dist/browser/esm/Ticker.mjs.map +1 -1
  6. package/dist/browser/esm/UVMapper.mjs +9 -9
  7. package/dist/browser/esm/UVMapper.mjs.map +1 -1
  8. package/dist/browser/esm/armor/Armor.mjs +84 -0
  9. package/dist/browser/esm/armor/Armor.mjs.map +1 -0
  10. package/dist/browser/esm/armor/index.mjs +3 -0
  11. package/dist/browser/esm/armor/index.mjs.map +1 -0
  12. package/dist/browser/esm/assets/AssetDefaults.mjs +8 -0
  13. package/dist/browser/esm/assets/AssetDefaults.mjs.map +1 -0
  14. package/dist/browser/esm/assets/AssetKey.mjs +1 -1
  15. package/dist/browser/esm/assets/AssetKey.mjs.map +1 -1
  16. package/dist/browser/esm/assets/AssetLoader.mjs +5 -5
  17. package/dist/browser/esm/assets/AssetLoader.mjs.map +1 -1
  18. package/dist/browser/esm/assets/Assets.mjs +1 -5
  19. package/dist/browser/esm/assets/Assets.mjs.map +1 -1
  20. package/dist/browser/esm/assets/BlockEntities.mjs +5 -1
  21. package/dist/browser/esm/assets/BlockEntities.mjs.map +1 -1
  22. package/dist/browser/esm/assets/BlockStates.mjs +1 -1
  23. package/dist/browser/esm/assets/BlockStates.mjs.map +1 -1
  24. package/dist/browser/esm/assets/Entities.mjs +40 -12
  25. package/dist/browser/esm/assets/Entities.mjs.map +1 -1
  26. package/dist/browser/esm/assets/MineRenderData.mjs +8 -17
  27. package/dist/browser/esm/assets/MineRenderData.mjs.map +1 -1
  28. package/dist/browser/esm/assets/ModelTextures.mjs +13 -7
  29. package/dist/browser/esm/assets/ModelTextures.mjs.map +1 -1
  30. package/dist/browser/esm/assets/Models.mjs +1 -1
  31. package/dist/browser/esm/assets/Models.mjs.map +1 -1
  32. package/dist/browser/esm/assets/_fonts/Unihex.mjs +5 -3
  33. package/dist/browser/esm/assets/_fonts/Unihex.mjs.map +1 -1
  34. package/dist/browser/esm/assets/_versions/DatasetVersions.mjs +22 -0
  35. package/dist/browser/esm/assets/_versions/DatasetVersions.mjs.map +1 -0
  36. package/dist/browser/esm/assets/index.mjs +1 -0
  37. package/dist/browser/esm/assets/index.mjs.map +1 -1
  38. package/dist/browser/esm/assets/source/HostedAssetSource.mjs +7 -8
  39. package/dist/browser/esm/assets/source/HostedAssetSource.mjs.map +1 -1
  40. package/dist/browser/esm/cache/Caching.mjs +8 -0
  41. package/dist/browser/esm/cache/Caching.mjs.map +1 -1
  42. package/dist/browser/esm/entity/scene/EntityObject.mjs +31 -5
  43. package/dist/browser/esm/entity/scene/EntityObject.mjs.map +1 -1
  44. package/dist/browser/esm/env/browser/BrowserCache.mjs +3 -3
  45. package/dist/browser/esm/env/browser/BrowserCache.mjs.map +1 -1
  46. package/dist/browser/esm/env/browser/BrowserEnv.mjs +11 -4
  47. package/dist/browser/esm/env/browser/BrowserEnv.mjs.map +1 -1
  48. package/dist/browser/esm/image/ImageLoader.mjs +3 -3
  49. package/dist/browser/esm/image/ImageLoader.mjs.map +1 -1
  50. package/dist/browser/esm/index.browser.d.mts +191 -22
  51. package/dist/browser/esm/index.browser.mjs.map +1 -1
  52. package/dist/browser/esm/index.mjs +1 -0
  53. package/dist/browser/esm/index.mjs.map +1 -1
  54. package/dist/browser/esm/inspector/SceneInspector.mjs +3 -3
  55. package/dist/browser/esm/inspector/SceneInspector.mjs.map +1 -1
  56. package/dist/browser/esm/instance/InstanceManager.mjs +4 -4
  57. package/dist/browser/esm/instance/InstanceManager.mjs.map +1 -1
  58. package/dist/browser/esm/model/ItemGlint.mjs +7 -6
  59. package/dist/browser/esm/model/ItemGlint.mjs.map +1 -1
  60. package/dist/browser/esm/model/block/scene/BlockObject.mjs +19 -6
  61. package/dist/browser/esm/model/block/scene/BlockObject.mjs.map +1 -1
  62. package/dist/browser/esm/model/multiblock/StructureParser.mjs +7 -4
  63. package/dist/browser/esm/model/multiblock/StructureParser.mjs.map +1 -1
  64. package/dist/browser/esm/model/scene/ModelObject.mjs +4 -4
  65. package/dist/browser/esm/model/scene/ModelObject.mjs.map +1 -1
  66. package/dist/browser/esm/nbt/NBTHelper.mjs +5 -2
  67. package/dist/browser/esm/nbt/NBTHelper.mjs.map +1 -1
  68. package/dist/browser/esm/renderer/Renderer.mjs +4 -2
  69. package/dist/browser/esm/renderer/Renderer.mjs.map +1 -1
  70. package/dist/browser/esm/renderer/SceneObject.mjs +0 -6
  71. package/dist/browser/esm/renderer/SceneObject.mjs.map +1 -1
  72. package/dist/browser/esm/request/Requests.mjs +3 -3
  73. package/dist/browser/esm/request/Requests.mjs.map +1 -1
  74. package/dist/browser/esm/scene/SceneDocument.mjs +35 -5
  75. package/dist/browser/esm/scene/SceneDocument.mjs.map +1 -1
  76. package/dist/browser/esm/scene/SceneDocumentLoader.mjs +14 -3
  77. package/dist/browser/esm/scene/SceneDocumentLoader.mjs.map +1 -1
  78. package/dist/browser/esm/skin/SkinGeometries.mjs +4 -1
  79. package/dist/browser/esm/skin/SkinGeometries.mjs.map +1 -1
  80. package/dist/browser/esm/skin/SkinPart.mjs +2 -0
  81. package/dist/browser/esm/skin/SkinPart.mjs.map +1 -1
  82. package/dist/browser/esm/skin/SkinTextureCoordinates.mjs +11 -1
  83. package/dist/browser/esm/skin/SkinTextureCoordinates.mjs.map +1 -1
  84. package/dist/browser/esm/skin/SkinTextures.mjs +1 -1
  85. package/dist/browser/esm/skin/SkinTextures.mjs.map +1 -1
  86. package/dist/browser/esm/skin/Skins.mjs +6 -4
  87. package/dist/browser/esm/skin/Skins.mjs.map +1 -1
  88. package/dist/browser/esm/skin/scene/SkinObject.mjs +119 -7
  89. package/dist/browser/esm/skin/scene/SkinObject.mjs.map +1 -1
  90. package/dist/browser/esm/texture/PalettedTextures.mjs +46 -0
  91. package/dist/browser/esm/texture/PalettedTextures.mjs.map +1 -0
  92. package/dist/browser/esm/texture/TextureLoader.mjs +4 -2
  93. package/dist/browser/esm/texture/TextureLoader.mjs.map +1 -1
  94. package/dist/browser/esm/texture/index.mjs +1 -0
  95. package/dist/browser/esm/texture/index.mjs.map +1 -1
  96. package/dist/browser/esm/util/log.mjs +75 -0
  97. package/dist/browser/esm/util/log.mjs.map +1 -1
  98. package/dist/browser/esm/util/performance.mjs.map +1 -1
  99. package/dist/browser/esm/util/util.mjs +0 -2
  100. package/dist/browser/esm/util/util.mjs.map +1 -1
  101. package/dist/browser/esm/world/Chunk.mjs +70 -38
  102. package/dist/browser/esm/world/Chunk.mjs.map +1 -1
  103. package/dist/browser/esm/world/MineRenderWorld.mjs +187 -60
  104. package/dist/browser/esm/world/MineRenderWorld.mjs.map +1 -1
  105. package/dist/browser/esm/world/SectionMesh.mjs +65 -9
  106. package/dist/browser/esm/world/SectionMesh.mjs.map +1 -1
  107. package/dist/browser/esm/world/SectionModels.mjs +21 -15
  108. package/dist/browser/esm/world/SectionModels.mjs.map +1 -1
  109. package/dist/browser/esm/world/SectionWorker.mjs +8 -2
  110. package/dist/browser/esm/world/SectionWorker.mjs.map +1 -1
  111. package/dist/browser/esm/world/WorldStreamer.mjs +129 -34
  112. package/dist/browser/esm/world/WorldStreamer.mjs.map +1 -1
  113. package/dist/browser/esm/world/_culling/BlockCulling.mjs +1 -1
  114. package/dist/browser/esm/world/_culling/BlockCulling.mjs.map +1 -1
  115. package/dist/browser/esm/world/_entities/WorldEntities.mjs +3 -1
  116. package/dist/browser/esm/world/_entities/WorldEntities.mjs.map +1 -1
  117. package/dist/browser/index.d.ts +191 -22
  118. package/dist/browser/index.js +4461 -3648
  119. package/dist/browser/index.js.map +1 -1
  120. package/dist/bundle.js +13482 -12651
  121. package/dist/bundle.js.map +1 -1
  122. package/dist/node/index.d.mts +179 -21
  123. package/dist/node/index.d.ts +179 -21
  124. package/dist/node/index.js +4495 -3666
  125. package/dist/node/index.js.map +1 -1
  126. package/dist/node/index.mjs +4412 -3586
  127. package/dist/node/index.mjs.map +1 -1
  128. package/package.json +2 -1
@@ -0,0 +1,46 @@
1
+ // src/texture/PalettedTextures.ts
2
+ import { ModelTextures } from "../assets/ModelTextures.mjs";
3
+ import { Caching } from "../cache/Caching.mjs";
4
+ import { CanvasImage } from "../canvas/CanvasImage.mjs";
5
+ import { MineRenderError } from "../error/MineRenderError.mjs";
6
+ var PalettedTextures = class {
7
+ /** Loads a texture with a palette permutation, sharing the resulting canvas between callers. */
8
+ static async get(texture, key, permutation) {
9
+ return Caching.canvasImageDataCache.get(`palette:${texture.serialize()}:${key.serialize()}:${permutation.serialize()}`, async () => {
10
+ const source = await ModelTextures.get(texture);
11
+ if (!source) return void 0;
12
+ const [palette, replacement] = await Promise.all([ModelTextures.get(key), ModelTextures.get(permutation)]);
13
+ if (!palette) throw new MineRenderError(`Missing palette ${key.toNamespacedString()}`);
14
+ if (!replacement) throw new MineRenderError(`Missing palette ${permutation.toNamespacedString()}`);
15
+ const pixels = source.data.getImageData(0, 0, source.width, source.height);
16
+ const colors = palette.data.getImageData(0, 0, palette.width, palette.height).data;
17
+ const replacements = replacement.data.getImageData(0, 0, replacement.width, replacement.height).data;
18
+ const image = new CanvasImage(source.width, source.height);
19
+ const output = image.context.createImageData(source.width, source.height);
20
+ output.data.set(this.permute(pixels.data, colors, replacements));
21
+ image.putData(output);
22
+ return { data: image.context, width: image.width, height: image.height };
23
+ });
24
+ }
25
+ /** @internal */
26
+ static permute(pixels, key, permutation) {
27
+ if (key.length !== permutation.length) throw new MineRenderError("Palette images must have the same pixel count");
28
+ const mapping = /* @__PURE__ */ new Map();
29
+ for (let i = 0; i < key.length; i += 4) {
30
+ if (key[i + 3] !== 0) mapping.set(key[i] << 16 | key[i + 1] << 8 | key[i + 2], i);
31
+ }
32
+ const output = new Uint8ClampedArray(pixels);
33
+ for (let i = 0; i < pixels.length; i += 4) {
34
+ if (pixels[i + 3] === 0) continue;
35
+ const mapped = mapping.get(pixels[i] << 16 | pixels[i + 1] << 8 | pixels[i + 2]);
36
+ if (mapped === void 0) continue;
37
+ output.set(permutation.subarray(mapped, mapped + 3), i);
38
+ output[i + 3] = Math.floor(pixels[i + 3] * permutation[mapped + 3] / 255);
39
+ }
40
+ return output;
41
+ }
42
+ };
43
+ export {
44
+ PalettedTextures
45
+ };
46
+ //# sourceMappingURL=PalettedTextures.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/texture/PalettedTextures.ts"],"sourcesContent":["import { AssetKey } from \"../assets/AssetKey\";\nimport { ModelTextures } from \"../assets/ModelTextures\";\nimport { Caching } from \"../cache/Caching\";\nimport { CanvasImage } from \"../canvas/CanvasImage\";\nimport { MineRenderError } from \"../error/MineRenderError\";\nimport type { ExtractableImageData } from \"../ExtractableImageData\";\nimport type { Maybe } from \"../util/util\";\n\n/** Recolours textures using indexed pairs of palette images. */\nexport class PalettedTextures {\n /** Loads a texture with a palette permutation, sharing the resulting canvas between callers. */\n public static async get(texture: AssetKey, key: AssetKey, permutation: AssetKey): Promise<Maybe<ExtractableImageData>> {\n return Caching.canvasImageDataCache.get(`palette:${texture.serialize()}:${key.serialize()}:${permutation.serialize()}`, async () => {\n const source = await ModelTextures.get(texture);\n if (!source) return undefined;\n const [palette, replacement] = await Promise.all([ModelTextures.get(key), ModelTextures.get(permutation)]);\n if (!palette) throw new MineRenderError(`Missing palette ${key.toNamespacedString()}`);\n if (!replacement) throw new MineRenderError(`Missing palette ${permutation.toNamespacedString()}`);\n const pixels = source.data.getImageData(0, 0, source.width, source.height);\n const colors = palette.data.getImageData(0, 0, palette.width, palette.height).data;\n const replacements = replacement.data.getImageData(0, 0, replacement.width, replacement.height).data;\n const image = new CanvasImage(source.width, source.height);\n const output = image.context.createImageData(source.width, source.height);\n output.data.set(this.permute(pixels.data, colors, replacements));\n image.putData(output);\n return { data: image.context as unknown as CanvasRenderingContext2D, width: image.width, height: image.height };\n });\n }\n\n /** @internal */\n public static permute(pixels: Uint8ClampedArray, key: Uint8ClampedArray, permutation: Uint8ClampedArray): Uint8ClampedArray {\n if (key.length !== permutation.length) throw new MineRenderError(\"Palette images must have the same pixel count\");\n const mapping = new Map<number, number>();\n for (let i = 0; i < key.length; i += 4) {\n if (key[i + 3] !== 0) mapping.set((key[i] << 16) | (key[i + 1] << 8) | key[i + 2], i);\n }\n const output = new Uint8ClampedArray(pixels);\n // PalettedPermutations.createPaletteMapping matches RGB and multiplies source and replacement alpha.\n for (let i = 0; i < pixels.length; i += 4) {\n if (pixels[i + 3] === 0) continue;\n const mapped = mapping.get((pixels[i] << 16) | (pixels[i + 1] << 8) | pixels[i + 2]);\n if (mapped === undefined) continue;\n output.set(permutation.subarray(mapped, mapped + 3), i);\n output[i + 3] = Math.floor(pixels[i + 3] * permutation[mapped + 3] / 255);\n }\n return output;\n }\n}\n"],"mappings":";AACA,SAAS,qBAAqB;AAC9B,SAAS,eAAe;AACxB,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAKzB,IAAM,mBAAN,MAAuB;AAAA;AAAA,EAE1B,aAAoB,IAAI,SAAmB,KAAe,aAA6D;AACnH,WAAO,QAAQ,qBAAqB,IAAI,WAAW,QAAQ,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,IAAI,YAAY,UAAU,CAAC,IAAI,YAAY;AAChI,YAAM,SAAS,MAAM,cAAc,IAAI,OAAO;AAC9C,UAAI,CAAC,OAAQ,QAAO;AACpB,YAAM,CAAC,SAAS,WAAW,IAAI,MAAM,QAAQ,IAAI,CAAC,cAAc,IAAI,GAAG,GAAG,cAAc,IAAI,WAAW,CAAC,CAAC;AACzG,UAAI,CAAC,QAAS,OAAM,IAAI,gBAAgB,mBAAmB,IAAI,mBAAmB,CAAC,EAAE;AACrF,UAAI,CAAC,YAAa,OAAM,IAAI,gBAAgB,mBAAmB,YAAY,mBAAmB,CAAC,EAAE;AACjG,YAAM,SAAS,OAAO,KAAK,aAAa,GAAG,GAAG,OAAO,OAAO,OAAO,MAAM;AACzE,YAAM,SAAS,QAAQ,KAAK,aAAa,GAAG,GAAG,QAAQ,OAAO,QAAQ,MAAM,EAAE;AAC9E,YAAM,eAAe,YAAY,KAAK,aAAa,GAAG,GAAG,YAAY,OAAO,YAAY,MAAM,EAAE;AAChG,YAAM,QAAQ,IAAI,YAAY,OAAO,OAAO,OAAO,MAAM;AACzD,YAAM,SAAS,MAAM,QAAQ,gBAAgB,OAAO,OAAO,OAAO,MAAM;AACxE,aAAO,KAAK,IAAI,KAAK,QAAQ,OAAO,MAAM,QAAQ,YAAY,CAAC;AAC/D,YAAM,QAAQ,MAAM;AACpB,aAAO,EAAE,MAAM,MAAM,SAAgD,OAAO,MAAM,OAAO,QAAQ,MAAM,OAAO;AAAA,IAClH,CAAC;AAAA,EACL;AAAA;AAAA,EAGA,OAAc,QAAQ,QAA2B,KAAwB,aAAmD;AACxH,QAAI,IAAI,WAAW,YAAY,OAAQ,OAAM,IAAI,gBAAgB,+CAA+C;AAChH,UAAM,UAAU,oBAAI,IAAoB;AACxC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK,GAAG;AACpC,UAAI,IAAI,IAAI,CAAC,MAAM,EAAG,SAAQ,IAAK,IAAI,CAAC,KAAK,KAAO,IAAI,IAAI,CAAC,KAAK,IAAK,IAAI,IAAI,CAAC,GAAG,CAAC;AAAA,IACxF;AACA,UAAM,SAAS,IAAI,kBAAkB,MAAM;AAE3C,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,GAAG;AACvC,UAAI,OAAO,IAAI,CAAC,MAAM,EAAG;AACzB,YAAM,SAAS,QAAQ,IAAK,OAAO,CAAC,KAAK,KAAO,OAAO,IAAI,CAAC,KAAK,IAAK,OAAO,IAAI,CAAC,CAAC;AACnF,UAAI,WAAW,OAAW;AAC1B,aAAO,IAAI,YAAY,SAAS,QAAQ,SAAS,CAAC,GAAG,CAAC;AACtD,aAAO,IAAI,CAAC,IAAI,KAAK,MAAM,OAAO,IAAI,CAAC,IAAI,YAAY,SAAS,CAAC,IAAI,GAAG;AAAA,IAC5E;AACA,WAAO;AAAA,EACX;AACJ;","names":[]}
@@ -3,6 +3,8 @@ import { RGBAFormat, Texture } from "three";
3
3
  import * as THREE from "three";
4
4
  import { ImageLoader } from "../image/ImageLoader.mjs";
5
5
  import { Textures } from "./Textures.mjs";
6
+ import { logger } from "../util/log.mjs";
7
+ var log = logger("TextureLoader");
6
8
  var TextureLoader = class {
7
9
  /** Reports whether image loading failed, so cached textures can be retried. */
8
10
  static hasFailed(texture) {
@@ -18,7 +20,7 @@ var TextureLoader = class {
18
20
  texture.needsUpdate = true;
19
21
  }, (error) => {
20
22
  this.failedTextures.add(texture);
21
- console.warn("Failed to load texture", src, error);
23
+ log.warn("Failed to load texture", src, error);
22
24
  });
23
25
  texture.image = image;
24
26
  texture.format = format;
@@ -34,7 +36,7 @@ var TextureLoader = class {
34
36
  texture.needsUpdate = true;
35
37
  }).catch((error) => {
36
38
  this.failedTextures.add(texture);
37
- console.warn("Failed to load texture", src, error);
39
+ log.warn("Failed to load texture", src, error);
38
40
  });
39
41
  texture.format = format;
40
42
  texture.rotation = rotation;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/texture/TextureLoader.ts"],"sourcesContent":["import { PixelFormat, RGBAFormat, Texture } from \"three\";\nimport { Caching } from \"../cache/Caching\";\nimport * as THREE from \"three\";\nimport { ImageLoader } from \"../image/ImageLoader\";\nimport { createCanvas } from \"../canvas/CanvasCompat\";\nimport type { CanvasRenderingContext2D } from \"canvas\";\nimport { Textures } from \"./Textures\";\n\n/** Creates sRGB textures immediately and fills their pixels after asynchronous image loading. */\nexport class TextureLoader {\n\n private static readonly failedTextures = new WeakSet<Texture>();\n\n /** Reports whether image loading failed, so cached textures can be retried. */\n public static hasFailed(texture: Texture): boolean {\n return this.failedTextures.has(texture);\n }\n\n protected static createTexture(): Texture {\n return new Texture();\n }\n\n public static loadInBackground(src: string, format: PixelFormat = RGBAFormat, rotation: number = 0): Texture {\n const texture = this.createTexture();\n texture.colorSpace = THREE.SRGBColorSpace;\n const image = ImageLoader.loadElement(src, () => {\n texture.needsUpdate = true;\n }, error => {\n this.failedTextures.add(texture);\n console.warn(\"Failed to load texture\", src, error);\n });\n texture.image = image;\n texture.format = format;\n texture.rotation = rotation;\n\n return Textures.initTextureProps(texture);\n }\n\n /** Loads image pixels in the background. `rotation` is in radians. Check {@link hasFailed} for load failures. */\n public static load(src: string,format: PixelFormat = RGBAFormat, rotation: number = 0): Texture {\n const texture = new Texture();\n texture.colorSpace = THREE.SRGBColorSpace;\n ImageLoader.getData(src).then(image=>{\n texture.image = image;\n texture.needsUpdate = true;\n }).catch(error => {\n this.failedTextures.add(texture);\n console.warn(\"Failed to load texture\", src, error);\n });\n texture.format = format;\n texture.rotation = rotation;\n\n return Textures.initTextureProps(texture);\n }\n\n\n}\n"],"mappings":";AAAA,SAAsB,YAAY,eAAe;AAEjD,YAAY,WAAW;AACvB,SAAS,mBAAmB;AAG5B,SAAS,gBAAgB;AAGlB,IAAM,gBAAN,MAAoB;AAAA;AAAA,EAKvB,OAAc,UAAU,SAA2B;AAC/C,WAAO,KAAK,eAAe,IAAI,OAAO;AAAA,EAC1C;AAAA,EAEA,OAAiB,gBAAyB;AACtC,WAAO,IAAI,QAAQ;AAAA,EACvB;AAAA,EAEA,OAAc,iBAAiB,KAAa,SAAsB,YAAY,WAAmB,GAAY;AACzG,UAAM,UAAU,KAAK,cAAc;AACnC,YAAQ,aAAmB;AAC3B,UAAM,QAAQ,YAAY,YAAY,KAAK,MAAM;AAC7C,cAAQ,cAAc;AAAA,IAC1B,GAAG,WAAS;AACR,WAAK,eAAe,IAAI,OAAO;AAC/B,cAAQ,KAAK,0BAA0B,KAAK,KAAK;AAAA,IACrD,CAAC;AACD,YAAQ,QAAQ;AAChB,YAAQ,SAAS;AACjB,YAAQ,WAAW;AAEnB,WAAO,SAAS,iBAAiB,OAAO;AAAA,EAC5C;AAAA;AAAA,EAGA,OAAc,KAAK,KAAY,SAAsB,YAAY,WAAmB,GAAY;AAC5F,UAAM,UAAU,IAAI,QAAQ;AAC5B,YAAQ,aAAmB;AAC3B,gBAAY,QAAQ,GAAG,EAAE,KAAK,WAAO;AACjC,cAAQ,QAAQ;AAChB,cAAQ,cAAc;AAAA,IAC1B,CAAC,EAAE,MAAM,WAAS;AACd,WAAK,eAAe,IAAI,OAAO;AAC/B,cAAQ,KAAK,0BAA0B,KAAK,KAAK;AAAA,IACrD,CAAC;AACD,YAAQ,SAAS;AACjB,YAAQ,WAAW;AAEnB,WAAO,SAAS,iBAAiB,OAAO;AAAA,EAC5C;AAGJ;AA/Ca,cAEe,iBAAiB,oBAAI,QAAiB;","names":[]}
1
+ {"version":3,"sources":["../../../../src/texture/TextureLoader.ts"],"sourcesContent":["import { PixelFormat, RGBAFormat, Texture } from \"three\";\nimport { Caching } from \"../cache/Caching\";\nimport * as THREE from \"three\";\nimport { ImageLoader } from \"../image/ImageLoader\";\nimport { createCanvas } from \"../canvas/CanvasCompat\";\nimport type { CanvasRenderingContext2D } from \"canvas\";\nimport { Textures } from \"./Textures\";\nimport { logger } from \"../util/log\";\n\nconst log = logger(\"TextureLoader\");\n\n/** Creates sRGB textures immediately and fills their pixels after asynchronous image loading. */\nexport class TextureLoader {\n\n private static readonly failedTextures = new WeakSet<Texture>();\n\n /** Reports whether image loading failed, so cached textures can be retried. */\n public static hasFailed(texture: Texture): boolean {\n return this.failedTextures.has(texture);\n }\n\n protected static createTexture(): Texture {\n return new Texture();\n }\n\n public static loadInBackground(src: string, format: PixelFormat = RGBAFormat, rotation: number = 0): Texture {\n const texture = this.createTexture();\n texture.colorSpace = THREE.SRGBColorSpace;\n const image = ImageLoader.loadElement(src, () => {\n texture.needsUpdate = true;\n }, error => {\n this.failedTextures.add(texture);\n log.warn(\"Failed to load texture\", src, error);\n });\n texture.image = image;\n texture.format = format;\n texture.rotation = rotation;\n\n return Textures.initTextureProps(texture);\n }\n\n /** Loads image pixels in the background. `rotation` is in radians. Check {@link hasFailed} for load failures. */\n public static load(src: string,format: PixelFormat = RGBAFormat, rotation: number = 0): Texture {\n const texture = new Texture();\n texture.colorSpace = THREE.SRGBColorSpace;\n ImageLoader.getData(src).then(image=>{\n texture.image = image;\n texture.needsUpdate = true;\n }).catch(error => {\n this.failedTextures.add(texture);\n log.warn(\"Failed to load texture\", src, error);\n });\n texture.format = format;\n texture.rotation = rotation;\n\n return Textures.initTextureProps(texture);\n }\n\n\n}\n"],"mappings":";AAAA,SAAsB,YAAY,eAAe;AAEjD,YAAY,WAAW;AACvB,SAAS,mBAAmB;AAG5B,SAAS,gBAAgB;AACzB,SAAS,cAAc;AAEvB,IAAM,MAAM,OAAO,eAAe;AAG3B,IAAM,gBAAN,MAAoB;AAAA;AAAA,EAKvB,OAAc,UAAU,SAA2B;AAC/C,WAAO,KAAK,eAAe,IAAI,OAAO;AAAA,EAC1C;AAAA,EAEA,OAAiB,gBAAyB;AACtC,WAAO,IAAI,QAAQ;AAAA,EACvB;AAAA,EAEA,OAAc,iBAAiB,KAAa,SAAsB,YAAY,WAAmB,GAAY;AACzG,UAAM,UAAU,KAAK,cAAc;AACnC,YAAQ,aAAmB;AAC3B,UAAM,QAAQ,YAAY,YAAY,KAAK,MAAM;AAC7C,cAAQ,cAAc;AAAA,IAC1B,GAAG,WAAS;AACR,WAAK,eAAe,IAAI,OAAO;AAC/B,UAAI,KAAK,0BAA0B,KAAK,KAAK;AAAA,IACjD,CAAC;AACD,YAAQ,QAAQ;AAChB,YAAQ,SAAS;AACjB,YAAQ,WAAW;AAEnB,WAAO,SAAS,iBAAiB,OAAO;AAAA,EAC5C;AAAA;AAAA,EAGA,OAAc,KAAK,KAAY,SAAsB,YAAY,WAAmB,GAAY;AAC5F,UAAM,UAAU,IAAI,QAAQ;AAC5B,YAAQ,aAAmB;AAC3B,gBAAY,QAAQ,GAAG,EAAE,KAAK,WAAO;AACjC,cAAQ,QAAQ;AAChB,cAAQ,cAAc;AAAA,IAC1B,CAAC,EAAE,MAAM,WAAS;AACd,WAAK,eAAe,IAAI,OAAO;AAC/B,UAAI,KAAK,0BAA0B,KAAK,KAAK;AAAA,IACjD,CAAC;AACD,YAAQ,SAAS;AACjB,YAAQ,WAAW;AAEnB,WAAO,SAAS,iBAAiB,OAAO;AAAA,EAC5C;AAGJ;AA/Ca,cAEe,iBAAiB,oBAAI,QAAiB;","names":[]}
@@ -1,5 +1,6 @@
1
1
  // src/texture/index.ts
2
2
  export * from "./Colormaps.mjs";
3
+ export * from "./PalettedTextures.mjs";
3
4
  export * from "./TextureAtlas.mjs";
4
5
  export * from "./TextureLoader.mjs";
5
6
  export * from "./Textures.mjs";
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/texture/index.ts"],"sourcesContent":["// autogenerated\nexport * from './Colormaps';\nexport * from './TextureAtlas';\nexport * from './TextureLoader';\nexport * from './Textures';\n"],"mappings":";AACA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
1
+ {"version":3,"sources":["../../../../src/texture/index.ts"],"sourcesContent":["// autogenerated\nexport * from './Colormaps';\nexport * from './PalettedTextures';\nexport * from './TextureAtlas';\nexport * from './TextureLoader';\nexport * from './Textures';\n"],"mappings":";AACA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
@@ -1,4 +1,77 @@
1
1
  // src/util/log.ts
2
+ var runtime = globalThis;
3
+ var levels = { debug: 0, info: 1, warn: 2, error: 3, silent: 4 };
4
+ var colors = { debug: 90, info: 36, warn: 33, error: 31 };
5
+ var threshold = "warn";
6
+ var namespaces;
7
+ var logHandler;
8
+ var tagStyle;
9
+ var switchRead = false;
10
+ var switchThreshold;
11
+ var switchNamespaces;
12
+ function readSwitch() {
13
+ switchRead = true;
14
+ let value;
15
+ if (typeof runtime.process?.versions?.node === "string") {
16
+ value = runtime.process.env?.MINERENDER_LOG;
17
+ } else {
18
+ try {
19
+ value = runtime.localStorage?.getItem("minerender:log");
20
+ } catch {
21
+ }
22
+ }
23
+ if (value) {
24
+ for (const entry of value.split(",")) {
25
+ const separator = entry.indexOf("=");
26
+ const name = separator < 0 ? void 0 : entry.slice(0, separator).trim();
27
+ const setting = (separator < 0 ? entry : entry.slice(separator + 1)).trim();
28
+ if (!Object.prototype.hasOwnProperty.call(levels, setting) || name === "") continue;
29
+ if (name === void 0) {
30
+ switchThreshold = setting;
31
+ } else {
32
+ switchNamespaces ?? (switchNamespaces = /* @__PURE__ */ new Map());
33
+ switchNamespaces.set(name, setting);
34
+ }
35
+ }
36
+ }
37
+ }
38
+ var Logging = class {
39
+ /** Updates the given settings and keeps the others. */
40
+ static configure(options) {
41
+ if (options.level !== void 0) threshold = options.level;
42
+ if (options.namespaces !== void 0) namespaces = new Map(Object.entries(options.namespaces));
43
+ }
44
+ /** Sends messages to `handler` instead of the console. Call without an argument to restore console output. */
45
+ static setHandler(handler) {
46
+ logHandler = handler;
47
+ }
48
+ };
49
+ function logger(namespace) {
50
+ const write = (level, args) => {
51
+ if (!switchRead) readSwitch();
52
+ if (levels[level] < levels[switchNamespaces?.get(namespace) ?? switchThreshold ?? namespaces?.get(namespace) ?? threshold]) return;
53
+ if (logHandler) {
54
+ logHandler(level, namespace, args);
55
+ return;
56
+ }
57
+ if (tagStyle === void 0) {
58
+ tagStyle = typeof runtime.process?.versions?.node === "string" ? runtime.process.stdout?.isTTY === true ? "ansi" : "plain" : "browser";
59
+ }
60
+ if (tagStyle === "browser") {
61
+ console[level](`%cMineRender%c ${namespace}`, "background:#3c8527;color:#fff;border-radius:3px;padding:0 4px", "font-weight:bold", ...args);
62
+ } else {
63
+ const color = colors[level];
64
+ const tag = `[minerender:${namespace}]`;
65
+ console[level](tagStyle === "ansi" ? `\x1B[${color}m${tag}\x1B[0m` : tag, ...args);
66
+ }
67
+ };
68
+ return {
69
+ debug: (...args) => write("debug", args),
70
+ info: (...args) => write("info", args),
71
+ warn: (...args) => write("warn", args),
72
+ error: (...args) => write("error", args)
73
+ };
74
+ }
2
75
  var Reset = "\x1B[0m";
3
76
  var FgGreen = "\x1B[32m";
4
77
  var BgBlack = "\x1B[40m";
@@ -6,6 +79,8 @@ function prefix(prefix2) {
6
79
  return `${FgGreen}MR${BgBlack}${prefix2 ?? ""}${Reset}`;
7
80
  }
8
81
  export {
82
+ Logging,
83
+ logger,
9
84
  prefix
10
85
  };
11
86
  //# sourceMappingURL=log.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/util/log.ts"],"sourcesContent":["// https://stackoverflow.com/a/41407246/6257838\nconst Reset = \"\\x1b[0m\"\nconst Bright = \"\\x1b[1m\"\nconst Dim = \"\\x1b[2m\"\nconst Underscore = \"\\x1b[4m\"\nconst Blink = \"\\x1b[5m\"\nconst Reverse = \"\\x1b[7m\"\nconst Hidden = \"\\x1b[8m\"\n\nconst FgBlack = \"\\x1b[30m\"\nconst FgRed = \"\\x1b[31m\"\nconst FgGreen = \"\\x1b[32m\"\nconst FgYellow = \"\\x1b[33m\"\nconst FgBlue = \"\\x1b[34m\"\nconst FgMagenta = \"\\x1b[35m\"\nconst FgCyan = \"\\x1b[36m\"\nconst FgWhite = \"\\x1b[37m\"\n\nconst BgBlack = \"\\x1b[40m\"\nconst BgRed = \"\\x1b[41m\"\nconst BgGreen = \"\\x1b[42m\"\nconst BgYellow = \"\\x1b[43m\"\nconst BgBlue = \"\\x1b[44m\"\nconst BgMagenta = \"\\x1b[45m\"\nconst BgCyan = \"\\x1b[46m\"\nconst BgWhite = \"\\x1b[47m\"\n\nexport function prefix(prefix?: string): string {\n return `${ FgGreen }MR${ BgBlack }${ prefix ?? '' }${ Reset }`\n}\n"],"mappings":";AACA,IAAM,QAAQ;AAUd,IAAM,UAAU;AAOhB,IAAM,UAAU;AAST,SAAS,OAAOA,SAAyB;AAC5C,SAAO,GAAI,OAAQ,KAAM,OAAQ,GAAIA,WAAU,EAAG,GAAI,KAAM;AAChE;","names":["prefix"]}
1
+ {"version":3,"sources":["../../../../src/util/log.ts"],"sourcesContent":["/** Severity of a log message, from most to least verbose. */\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\n/** The lowest level that is printed, or `\"silent\"` to print nothing. */\nexport type LogThreshold = LogLevel | \"silent\";\n\n/** Receives each message that passes the threshold. `args` are the logger call's arguments, without a tag. */\nexport type LogHandler = (level: LogLevel, namespace: string, args: unknown[]) => void;\n\nexport interface LoggingOptions {\n /** Threshold for namespaces without an override. Starts at `\"warn\"`. */\n level?: LogThreshold;\n /** Thresholds by logger name, such as `{ UVMapper: \"debug\" }`. Replaces earlier overrides. */\n namespaces?: Record<string, LogThreshold>;\n}\n\nexport interface Logger {\n debug(...args: unknown[]): void;\n info(...args: unknown[]): void;\n warn(...args: unknown[]): void;\n error(...args: unknown[]): void;\n}\n\ntype Runtime = {\n process?: { versions?: { node?: string }; stdout?: { isTTY?: boolean }; env?: Record<string, string | undefined> };\n localStorage?: { getItem(key: string): string | null };\n};\n\nconst runtime = globalThis as Runtime;\nconst levels: Record<LogThreshold, number> = { debug: 0, info: 1, warn: 2, error: 3, silent: 4 };\nconst colors: Record<LogLevel, number> = { debug: 90, info: 36, warn: 33, error: 31 };\n\nlet threshold: LogThreshold = \"warn\";\nlet namespaces: Map<string, LogThreshold> | undefined;\nlet logHandler: LogHandler | undefined;\nlet tagStyle: \"plain\" | \"ansi\" | \"browser\" | undefined;\nlet switchRead = false;\nlet switchThreshold: LogThreshold | undefined;\nlet switchNamespaces: Map<string, LogThreshold> | undefined;\n\nfunction readSwitch(): void {\n switchRead = true;\n let value: string | null | undefined;\n if (typeof runtime.process?.versions?.node === \"string\") {\n value = runtime.process.env?.MINERENDER_LOG;\n } else {\n try {\n value = runtime.localStorage?.getItem(\"minerender:log\");\n } catch {\n // Sandboxed pages can deny access to storage.\n }\n }\n if (value) {\n for (const entry of value.split(\",\")) {\n const separator = entry.indexOf(\"=\");\n const name = separator < 0 ? undefined : entry.slice(0, separator).trim();\n const setting = (separator < 0 ? entry : entry.slice(separator + 1)).trim();\n if (!Object.prototype.hasOwnProperty.call(levels, setting) || name === \"\") continue;\n if (name === undefined) {\n switchThreshold = setting as LogThreshold;\n } else {\n switchNamespaces ??= new Map();\n switchNamespaces.set(name, setting as LogThreshold);\n }\n }\n }\n}\n\n/** Global log settings for every MineRender logger. */\nexport class Logging {\n /** Updates the given settings and keeps the others. */\n static configure(options: LoggingOptions): void {\n if (options.level !== undefined) threshold = options.level;\n if (options.namespaces !== undefined) namespaces = new Map(Object.entries(options.namespaces));\n }\n\n /** Sends messages to `handler` instead of the console. Call without an argument to restore console output. */\n static setHandler(handler?: LogHandler): void {\n logHandler = handler;\n }\n}\n\n/** Creates a logger whose messages are tagged and filtered as `namespace`. */\nexport function logger(namespace: string): Logger {\n const write = (level: LogLevel, args: unknown[]): void => {\n if (!switchRead) readSwitch();\n if (levels[level] < levels[switchNamespaces?.get(namespace) ?? switchThreshold ?? namespaces?.get(namespace) ?? threshold]) return;\n if (logHandler) {\n logHandler(level, namespace, args);\n return;\n }\n if (tagStyle === undefined) {\n tagStyle = typeof runtime.process?.versions?.node === \"string\"\n ? runtime.process.stdout?.isTTY === true ? \"ansi\" : \"plain\"\n : \"browser\";\n }\n if (tagStyle === \"browser\") {\n console[level](`%cMineRender%c ${namespace}`, \"background:#3c8527;color:#fff;border-radius:3px;padding:0 4px\", \"font-weight:bold\", ...args);\n } else {\n const color = colors[level];\n const tag = `[minerender:${namespace}]`;\n console[level](tagStyle === \"ansi\" ? `\\x1b[${color}m${tag}\\x1b[0m` : tag, ...args);\n }\n };\n return {\n debug: (...args) => write(\"debug\", args),\n info: (...args) => write(\"info\", args),\n warn: (...args) => write(\"warn\", args),\n error: (...args) => write(\"error\", args),\n };\n}\n\nconst Reset = \"\\x1b[0m\"\nconst FgGreen = \"\\x1b[32m\"\nconst BgBlack = \"\\x1b[40m\"\n\n/** @deprecated Use {@link logger}. */\nexport function prefix(prefix?: string): string {\n return `${ FgGreen }MR${ BgBlack }${ prefix ?? '' }${ Reset }`\n}\n"],"mappings":";AA4BA,IAAM,UAAU;AAChB,IAAM,SAAuC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,EAAE;AAC/F,IAAM,SAAmC,EAAE,OAAO,IAAI,MAAM,IAAI,MAAM,IAAI,OAAO,GAAG;AAEpF,IAAI,YAA0B;AAC9B,IAAI;AACJ,IAAI;AACJ,IAAI;AACJ,IAAI,aAAa;AACjB,IAAI;AACJ,IAAI;AAEJ,SAAS,aAAmB;AACxB,eAAa;AACb,MAAI;AACJ,MAAI,OAAO,QAAQ,SAAS,UAAU,SAAS,UAAU;AACrD,YAAQ,QAAQ,QAAQ,KAAK;AAAA,EACjC,OAAO;AACH,QAAI;AACA,cAAQ,QAAQ,cAAc,QAAQ,gBAAgB;AAAA,IAC1D,QAAQ;AAAA,IAER;AAAA,EACJ;AACA,MAAI,OAAO;AACP,eAAW,SAAS,MAAM,MAAM,GAAG,GAAG;AAClC,YAAM,YAAY,MAAM,QAAQ,GAAG;AACnC,YAAM,OAAO,YAAY,IAAI,SAAY,MAAM,MAAM,GAAG,SAAS,EAAE,KAAK;AACxE,YAAM,WAAW,YAAY,IAAI,QAAQ,MAAM,MAAM,YAAY,CAAC,GAAG,KAAK;AAC1E,UAAI,CAAC,OAAO,UAAU,eAAe,KAAK,QAAQ,OAAO,KAAK,SAAS,GAAI;AAC3E,UAAI,SAAS,QAAW;AACpB,0BAAkB;AAAA,MACtB,OAAO;AACH,gDAAqB,oBAAI,IAAI;AAC7B,yBAAiB,IAAI,MAAM,OAAuB;AAAA,MACtD;AAAA,IACJ;AAAA,EACJ;AACJ;AAGO,IAAM,UAAN,MAAc;AAAA;AAAA,EAEjB,OAAO,UAAU,SAA+B;AAC5C,QAAI,QAAQ,UAAU,OAAW,aAAY,QAAQ;AACrD,QAAI,QAAQ,eAAe,OAAW,cAAa,IAAI,IAAI,OAAO,QAAQ,QAAQ,UAAU,CAAC;AAAA,EACjG;AAAA;AAAA,EAGA,OAAO,WAAW,SAA4B;AAC1C,iBAAa;AAAA,EACjB;AACJ;AAGO,SAAS,OAAO,WAA2B;AAC9C,QAAM,QAAQ,CAAC,OAAiB,SAA0B;AACtD,QAAI,CAAC,WAAY,YAAW;AAC5B,QAAI,OAAO,KAAK,IAAI,OAAO,kBAAkB,IAAI,SAAS,KAAK,mBAAmB,YAAY,IAAI,SAAS,KAAK,SAAS,EAAG;AAC5H,QAAI,YAAY;AACZ,iBAAW,OAAO,WAAW,IAAI;AACjC;AAAA,IACJ;AACA,QAAI,aAAa,QAAW;AACxB,iBAAW,OAAO,QAAQ,SAAS,UAAU,SAAS,WAChD,QAAQ,QAAQ,QAAQ,UAAU,OAAO,SAAS,UAClD;AAAA,IACV;AACA,QAAI,aAAa,WAAW;AACxB,cAAQ,KAAK,EAAE,kBAAkB,SAAS,IAAI,iEAAiE,oBAAoB,GAAG,IAAI;AAAA,IAC9I,OAAO;AACH,YAAM,QAAQ,OAAO,KAAK;AAC1B,YAAM,MAAM,eAAe,SAAS;AACpC,cAAQ,KAAK,EAAE,aAAa,SAAS,QAAQ,KAAK,IAAI,GAAG,YAAY,KAAK,GAAG,IAAI;AAAA,IACrF;AAAA,EACJ;AACA,SAAO;AAAA,IACH,OAAO,IAAI,SAAS,MAAM,SAAS,IAAI;AAAA,IACvC,MAAM,IAAI,SAAS,MAAM,QAAQ,IAAI;AAAA,IACrC,MAAM,IAAI,SAAS,MAAM,QAAQ,IAAI;AAAA,IACrC,OAAO,IAAI,SAAS,MAAM,SAAS,IAAI;AAAA,EAC3C;AACJ;AAEA,IAAM,QAAQ;AACd,IAAM,UAAU;AAChB,IAAM,UAAU;AAGT,SAAS,OAAOA,SAAyB;AAC5C,SAAO,GAAI,OAAQ,KAAM,OAAQ,GAAIA,WAAU,EAAG,GAAI,KAAM;AAChE;","names":["prefix"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/util/performance.ts"],"sourcesContent":["export function captureException(e: any, ...extra: any[]) {\n console.error(e, extra);\n //TODO\n}\n\nexport function timingStart(marker: string) {\n console.time(marker);\n //TODO\n}\n\nexport function timingEnd(marker: string) {\n console.timeEnd(marker);\n //TODO\n}\n"],"mappings":";AAAO,SAAS,iBAAiB,MAAW,OAAc;AACtD,UAAQ,MAAM,GAAG,KAAK;AAE1B;AAEO,SAAS,YAAY,QAAgB;AACxC,UAAQ,KAAK,MAAM;AAEvB;AAEO,SAAS,UAAU,QAAgB;AACtC,UAAQ,QAAQ,MAAM;AAE1B;","names":[]}
1
+ {"version":3,"sources":["../../../../src/util/performance.ts"],"sourcesContent":["/** @deprecated Unused by MineRender. */\nexport function captureException(e: any, ...extra: any[]) {\n console.error(e, extra);\n //TODO\n}\n\n/** @deprecated Unused by MineRender. */\nexport function timingStart(marker: string) {\n console.time(marker);\n //TODO\n}\n\n/** @deprecated Unused by MineRender. */\nexport function timingEnd(marker: string) {\n console.timeEnd(marker);\n //TODO\n}\n"],"mappings":";AACO,SAAS,iBAAiB,MAAW,OAAc;AACtD,UAAQ,MAAM,GAAG,KAAK;AAE1B;AAGO,SAAS,YAAY,QAAgB;AACxC,UAAQ,KAAK,MAAM;AAEvB;AAGO,SAAS,UAAU,QAAgB;AACtC,UAAQ,QAAQ,MAAM;AAE1B;","names":[]}
@@ -28,8 +28,6 @@ async function sleep(timeout) {
28
28
  });
29
29
  }
30
30
  function yieldToEventLoop() {
31
- const scheduler = globalThis.scheduler;
32
- if (scheduler?.yield) return scheduler.yield();
33
31
  return new Promise((resolve) => {
34
32
  if (typeof MessageChannel !== "undefined") {
35
33
  const channel = new MessageChannel();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/util/util.ts"],"sourcesContent":["// Node ESM needs the explicit .js extension for the CommonJS hash module.\nimport MD5 from \"crypto-js/md5.js\";\nimport { Vector3 } from \"three\";\n\nexport const changeEvent = { type: 'change' as const };\n\nexport type Maybe<T> = T | undefined;\n\n// MD5 runs in both environments without pulling Node's crypto into the browser graph.\nexport function md5(str: string): string {\n return MD5(str).toString();\n}\n\nexport function base64encode(str: string): string {\n if (typeof btoa !== \"undefined\") {\n return btoa(str);\n }\n return Buffer.from(str).toString(\"base64\");\n}\n\nexport function base64decode(str: string): string {\n if (typeof atob !== \"undefined\") {\n return atob(str);\n }\n return Buffer.from(str, \"base64\").toString(\"ascii\");\n}\n\nexport function toRadians(degrees: number): number {\n return degrees * Math.PI / 180;\n}\n\nexport function toDegrees(radians: number): number {\n return radians * 180 / Math.PI;\n}\n\n/** Resolves after a delay in milliseconds. */\nexport async function sleep(timeout: number): Promise<void> {\n return new Promise(resolve => {\n setTimeout(() => resolve(), timeout);\n });\n}\n\n/** Yields to the next macrotask without timer clamping when the platform supports it. */\nexport function yieldToEventLoop(): Promise<void> {\n const scheduler = (globalThis as typeof globalThis & { scheduler?: { yield?: () => Promise<void> } }).scheduler;\n if (scheduler?.yield) return scheduler.yield();\n return new Promise(resolve => {\n if (typeof MessageChannel !== \"undefined\") {\n const channel = new MessageChannel();\n channel.port1.onmessage = () => {\n channel.port1.close();\n channel.port2.close();\n resolve();\n };\n channel.port2.postMessage(undefined);\n } else {\n setTimeout(resolve, 0);\n }\n });\n}\n\n/** Reduces an angle modulo 360, retaining a negative sign for negative inputs. */\nexport function clampRotationDegrees(deg: number): number {\n return deg % 360;\n}\n\nexport function isVector3(obj: any): obj is Vector3 {\n return (<Vector3>obj).isVector3;\n}\n\nexport type DeepPartial<T> = {\n [P in keyof T]?: DeepPartial<T[P]>;\n};\n"],"mappings":";AACA,OAAO,SAAS;AAGT,IAAM,cAAc,EAAE,MAAM,SAAkB;AAK9C,SAAS,IAAI,KAAqB;AACrC,SAAO,IAAI,GAAG,EAAE,SAAS;AAC7B;AAEO,SAAS,aAAa,KAAqB;AAC9C,MAAI,OAAO,SAAS,aAAa;AAC7B,WAAO,KAAK,GAAG;AAAA,EACnB;AACA,SAAO,OAAO,KAAK,GAAG,EAAE,SAAS,QAAQ;AAC7C;AAEO,SAAS,aAAa,KAAqB;AAC9C,MAAI,OAAO,SAAS,aAAa;AAC7B,WAAO,KAAK,GAAG;AAAA,EACnB;AACA,SAAO,OAAO,KAAK,KAAK,QAAQ,EAAE,SAAS,OAAO;AACtD;AAEO,SAAS,UAAU,SAAyB;AAC/C,SAAO,UAAU,KAAK,KAAK;AAC/B;AAEO,SAAS,UAAU,SAAyB;AAC/C,SAAO,UAAU,MAAM,KAAK;AAChC;AAGA,eAAsB,MAAM,SAAgC;AACxD,SAAO,IAAI,QAAQ,aAAW;AAC1B,eAAW,MAAM,QAAQ,GAAG,OAAO;AAAA,EACvC,CAAC;AACL;AAGO,SAAS,mBAAkC;AAC9C,QAAM,YAAa,WAAmF;AACtG,MAAI,WAAW,MAAO,QAAO,UAAU,MAAM;AAC7C,SAAO,IAAI,QAAQ,aAAW;AAC1B,QAAI,OAAO,mBAAmB,aAAa;AACvC,YAAM,UAAU,IAAI,eAAe;AACnC,cAAQ,MAAM,YAAY,MAAM;AAC5B,gBAAQ,MAAM,MAAM;AACpB,gBAAQ,MAAM,MAAM;AACpB,gBAAQ;AAAA,MACZ;AACA,cAAQ,MAAM,YAAY,MAAS;AAAA,IACvC,OAAO;AACH,iBAAW,SAAS,CAAC;AAAA,IACzB;AAAA,EACJ,CAAC;AACL;AAGO,SAAS,qBAAqB,KAAqB;AACtD,SAAO,MAAM;AACjB;AAEO,SAAS,UAAU,KAA0B;AAChD,SAAiB,IAAK;AAC1B;","names":[]}
1
+ {"version":3,"sources":["../../../../src/util/util.ts"],"sourcesContent":["// Node ESM needs the explicit .js extension for the CommonJS hash module.\nimport MD5 from \"crypto-js/md5.js\";\nimport { Vector3 } from \"three\";\n\nexport const changeEvent = { type: 'change' as const };\n\nexport type Maybe<T> = T | undefined;\n\n// MD5 runs in both environments without pulling Node's crypto into the browser graph.\nexport function md5(str: string): string {\n return MD5(str).toString();\n}\n\nexport function base64encode(str: string): string {\n if (typeof btoa !== \"undefined\") {\n return btoa(str);\n }\n return Buffer.from(str).toString(\"base64\");\n}\n\nexport function base64decode(str: string): string {\n if (typeof atob !== \"undefined\") {\n return atob(str);\n }\n return Buffer.from(str, \"base64\").toString(\"ascii\");\n}\n\nexport function toRadians(degrees: number): number {\n return degrees * Math.PI / 180;\n}\n\nexport function toDegrees(radians: number): number {\n return radians * 180 / Math.PI;\n}\n\n/** Resolves after a delay in milliseconds. */\nexport async function sleep(timeout: number): Promise<void> {\n return new Promise(resolve => {\n setTimeout(() => resolve(), timeout);\n });\n}\n\n/**\n * Yields to the next macrotask without timer clamping when the platform supports it.\n * Not `scheduler.yield()`: Chrome runs its continuations ahead of rendering, so chained yields delay frames.\n */\nexport function yieldToEventLoop(): Promise<void> {\n return new Promise(resolve => {\n if (typeof MessageChannel !== \"undefined\") {\n const channel = new MessageChannel();\n channel.port1.onmessage = () => {\n channel.port1.close();\n channel.port2.close();\n resolve();\n };\n channel.port2.postMessage(undefined);\n } else {\n setTimeout(resolve, 0);\n }\n });\n}\n\n/** Reduces an angle modulo 360, retaining a negative sign for negative inputs. */\nexport function clampRotationDegrees(deg: number): number {\n return deg % 360;\n}\n\nexport function isVector3(obj: any): obj is Vector3 {\n return (<Vector3>obj).isVector3;\n}\n\nexport type DeepPartial<T> = {\n [P in keyof T]?: DeepPartial<T[P]>;\n};\n"],"mappings":";AACA,OAAO,SAAS;AAGT,IAAM,cAAc,EAAE,MAAM,SAAkB;AAK9C,SAAS,IAAI,KAAqB;AACrC,SAAO,IAAI,GAAG,EAAE,SAAS;AAC7B;AAEO,SAAS,aAAa,KAAqB;AAC9C,MAAI,OAAO,SAAS,aAAa;AAC7B,WAAO,KAAK,GAAG;AAAA,EACnB;AACA,SAAO,OAAO,KAAK,GAAG,EAAE,SAAS,QAAQ;AAC7C;AAEO,SAAS,aAAa,KAAqB;AAC9C,MAAI,OAAO,SAAS,aAAa;AAC7B,WAAO,KAAK,GAAG;AAAA,EACnB;AACA,SAAO,OAAO,KAAK,KAAK,QAAQ,EAAE,SAAS,OAAO;AACtD;AAEO,SAAS,UAAU,SAAyB;AAC/C,SAAO,UAAU,KAAK,KAAK;AAC/B;AAEO,SAAS,UAAU,SAAyB;AAC/C,SAAO,UAAU,MAAM,KAAK;AAChC;AAGA,eAAsB,MAAM,SAAgC;AACxD,SAAO,IAAI,QAAQ,aAAW;AAC1B,eAAW,MAAM,QAAQ,GAAG,OAAO;AAAA,EACvC,CAAC;AACL;AAMO,SAAS,mBAAkC;AAC9C,SAAO,IAAI,QAAQ,aAAW;AAC1B,QAAI,OAAO,mBAAmB,aAAa;AACvC,YAAM,UAAU,IAAI,eAAe;AACnC,cAAQ,MAAM,YAAY,MAAM;AAC5B,gBAAQ,MAAM,MAAM;AACpB,gBAAQ,MAAM,MAAM;AACpB,gBAAQ;AAAA,MACZ;AACA,cAAQ,MAAM,YAAY,MAAS;AAAA,IACvC,OAAO;AACH,iBAAW,SAAS,CAAC;AAAA,IACzB;AAAA,EACJ,CAAC;AACL;AAGO,SAAS,qBAAqB,KAAqB;AACtD,SAAO,MAAM;AACjB;AAEO,SAAS,UAAU,KAA0B;AAChD,SAAiB,IAAK;AAC1B;","names":[]}
@@ -1,4 +1,5 @@
1
1
  // src/world/Chunk.ts
2
+ import { BlockObject } from "../model/block/scene/BlockObject.mjs";
2
3
  import { Color, Vector3 } from "three";
3
4
  import { ChunkData } from "./ChunkData.mjs";
4
5
  import { BlockStates } from "../assets/BlockStates.mjs";
@@ -134,11 +135,14 @@ var Chunk = class _Chunk {
134
135
  this.biomes = copy;
135
136
  return this.refreshBiomes();
136
137
  }
137
- /** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
138
- refreshBiomes(columns) {
139
- return this.changeBlocks(() => this.refreshBiomeBlocks(columns), true);
138
+ /**
139
+ * Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects.
140
+ * The owning world can defer neighbor culling until all affected chunks are refreshed.
141
+ */
142
+ refreshBiomes(columns, deferCulling = false) {
143
+ return this.changeBlocks(() => this.refreshBiomeBlocks(columns, deferCulling), true);
140
144
  }
141
- async refreshBiomeBlocks(columns) {
145
+ async refreshBiomeBlocks(columns, deferCulling = false) {
142
146
  const blend = columns && this.biomeBlend?.();
143
147
  const ownColumn = columns?.some((column) => column.x === this.x && column.z === this.z);
144
148
  const affected = /* @__PURE__ */ new Map();
@@ -195,7 +199,7 @@ var Chunk = class _Chunk {
195
199
  this.renderedBlocks.get(index)?.object?.setVisible(false);
196
200
  }
197
201
  const positions = blocks.map(({ index }) => new Vector3(...this.positionAt(index)));
198
- if (this.onBlocksChanged) await this.onBlocksChanged(positions);
202
+ if (this.onBlocksChanged) await this.onBlocksChanged(positions, deferCulling);
199
203
  else await this.rebuildSectionMesh();
200
204
  }
201
205
  }
@@ -266,8 +270,10 @@ var Chunk = class _Chunk {
266
270
  if (rejected?.status === "rejected") throw rejected.reason;
267
271
  }
268
272
  const positions = [];
269
- let failed = false, failure;
270
- for (const { index, key, block } of placements) {
273
+ const jobs = /* @__PURE__ */ new Map();
274
+ let failureOrder = Infinity, failure;
275
+ for (const [order, { index, key, block }] of placements.entries()) {
276
+ jobs.delete(index);
271
277
  this.hiddenBlocks.delete(index);
272
278
  this.data.assign(index, this.data.intern(block), block?.nbt);
273
279
  const readBlock = this.data.snapshot(index);
@@ -283,7 +289,6 @@ var Chunk = class _Chunk {
283
289
  this.z * 16 + Math.floor(index / 16) % 16
284
290
  );
285
291
  positions.push(worldPos);
286
- let object;
287
292
  try {
288
293
  if (!readBlock) continue;
289
294
  const resolved = resolutions.get(key);
@@ -305,7 +310,7 @@ var Chunk = class _Chunk {
305
310
  this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
306
311
  this.meshDirty = true;
307
312
  } else {
308
- object = await this.scene.addBlock(blockState, {
313
+ jobs.set(index, { order, readBlock, blockState, position: MineRenderWorld.worldToScenePosition(worldPos), options: {
309
314
  mergeMeshes: true,
310
315
  instanceMeshes: true,
311
316
  maxInstanceCount: 2e3,
@@ -313,13 +318,12 @@ var Chunk = class _Chunk {
313
318
  variantPosition,
314
319
  biome: context?.biome,
315
320
  biomePosition: this.biomePosition(index, blockState, stored)
316
- });
317
- object.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
321
+ } });
322
+ continue;
318
323
  }
319
324
  this.renderedBlocks.set(index, { get block() {
320
325
  return readBlock();
321
- }, object });
322
- if (object) this.objectBlocks.add(index);
326
+ }, object: void 0 });
323
327
  } catch (error) {
324
328
  if (preserveDataOnFailure && readBlock) {
325
329
  this.renderedBlocks.set(index, { get block() {
@@ -327,12 +331,35 @@ var Chunk = class _Chunk {
327
331
  }, object: void 0 });
328
332
  } else this.data.assign(index, 0);
329
333
  this.fluidCells[index] = 0;
330
- object?.removeFromScene();
331
- if (!failed) failure = error;
332
- failed = true;
334
+ if (order < failureOrder) {
335
+ failureOrder = order;
336
+ failure = error;
337
+ }
333
338
  }
334
339
  }
335
- if (failed) throw failure;
340
+ const pending = [...jobs];
341
+ const objects = await Promise.allSettled(pending.map(([, job]) => this.createBlockObject(job.blockState, job.position, job.options)));
342
+ for (const [i, [index, { order, readBlock }]] of pending.entries()) {
343
+ const result = objects[i];
344
+ if (result.status === "fulfilled") {
345
+ this.renderedBlocks.set(index, { get block() {
346
+ return readBlock();
347
+ }, object: result.value });
348
+ this.objectBlocks.add(index);
349
+ } else {
350
+ if (preserveDataOnFailure) {
351
+ this.renderedBlocks.set(index, { get block() {
352
+ return readBlock();
353
+ }, object: void 0 });
354
+ } else this.data.assign(index, 0);
355
+ this.fluidCells[index] = 0;
356
+ if (order < failureOrder) {
357
+ failureOrder = order;
358
+ failure = result.reason;
359
+ }
360
+ }
361
+ }
362
+ if (failureOrder < Infinity) throw failure;
336
363
  return positions;
337
364
  }
338
365
  /** Replaces every cell from parsed section data. The caller refreshes neighbor culling. */
@@ -403,37 +430,42 @@ var Chunk = class _Chunk {
403
430
  failed = true;
404
431
  }
405
432
  }
406
- for (const { index, blockState, biome } of perBlocks) {
407
- let object;
408
- try {
409
- object = await this.scene.addBlock(blockState, {
410
- mergeMeshes: true,
411
- instanceMeshes: true,
412
- maxInstanceCount: 2e3,
413
- initialState: this.data.stateAt(this.data.idAt(index)).properties,
414
- variantPosition: this.positionAt(index),
415
- biome,
416
- biomePosition: this.biomePosition(index, blockState, this.data.stateAt(this.data.idAt(index)))
417
- });
418
- object.setPosition(new Vector3(
419
- this.x * 256 + index % 16 * 16,
420
- this.y * 256 + Math.floor(index / 256) * 16,
421
- this.z * 256 + Math.floor(index / 16) % 16 * 16
422
- ));
423
- this.renderedBlocks.get(index).object = object;
433
+ const objects = await Promise.allSettled(perBlocks.map(({ index, blockState, biome }) => this.createBlockObject(blockState, new Vector3(
434
+ this.x * 256 + index % 16 * 16,
435
+ this.y * 256 + Math.floor(index / 256) * 16,
436
+ this.z * 256 + Math.floor(index / 16) % 16 * 16
437
+ ), {
438
+ mergeMeshes: true,
439
+ instanceMeshes: true,
440
+ maxInstanceCount: 2e3,
441
+ initialState: this.data.stateAt(this.data.idAt(index)).properties,
442
+ variantPosition: this.positionAt(index),
443
+ biome,
444
+ biomePosition: this.biomePosition(index, blockState, this.data.stateAt(this.data.idAt(index)))
445
+ })));
446
+ for (const [i, { index }] of perBlocks.entries()) {
447
+ const result = objects[i];
448
+ if (result.status === "fulfilled") {
449
+ this.renderedBlocks.get(index).object = result.value;
424
450
  this.objectBlocks.add(index);
425
- } catch (error) {
451
+ } else {
426
452
  this.data.assign(index, 0);
427
453
  this.fluidCells[index] = 0;
428
454
  this.renderedBlocks.delete(index);
429
455
  this.objectBlocks.delete(index);
430
- object?.removeFromScene();
431
- if (!failed) failure = error;
456
+ if (!failed) failure = result.reason;
432
457
  failed = true;
433
458
  }
434
459
  }
435
460
  if (failed) throw failure;
436
461
  }
462
+ createBlockObject(blockState, position, options) {
463
+ return this.scene.addSceneObject(blockState, () => {
464
+ const object = new BlockObject(blockState, options);
465
+ object.position.copy(position);
466
+ return object;
467
+ }, options);
468
+ }
437
469
  positionAt(index) {
438
470
  return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
439
471
  }