minerender 2.1.0-beta.1 → 2.1.0-beta.2
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/README.md +95 -0
- package/dist/browser/index.d.mts +795 -25
- package/dist/browser/index.d.ts +795 -25
- package/dist/browser/index.js +1905 -401
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.mjs +1881 -377
- package/dist/browser/index.mjs.map +1 -1
- package/dist/bundle.js +2729 -1205
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.d.mts +795 -25
- package/dist/node/index.d.ts +795 -25
- package/dist/node/index.js +1915 -400
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +1884 -376
- package/dist/node/index.mjs.map +1 -1
- package/package.json +12 -4
package/dist/browser/index.js
CHANGED
|
@@ -65,6 +65,7 @@ var init_PersistentCache = __esm({
|
|
|
65
65
|
constructor(backing) {
|
|
66
66
|
this.backing = backing;
|
|
67
67
|
}
|
|
68
|
+
/** Opens a named store in the active environment using the current cache schema version. */
|
|
68
69
|
static open(name) {
|
|
69
70
|
return Env.provider.openCache(name, _PersistentCache.VERSION);
|
|
70
71
|
}
|
|
@@ -260,9 +261,11 @@ var init_Caching = __esm({
|
|
|
260
261
|
this.entityTextureCache,
|
|
261
262
|
this.entityAnimationCache,
|
|
262
263
|
this.blockEntityIndexCache,
|
|
263
|
-
this.fontCache
|
|
264
|
+
this.fontCache,
|
|
265
|
+
this.bannerPatternCache
|
|
264
266
|
];
|
|
265
267
|
}
|
|
268
|
+
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
266
269
|
static clear() {
|
|
267
270
|
for (const cache of this.all) {
|
|
268
271
|
cache.invalidateAll();
|
|
@@ -308,6 +311,7 @@ var init_Caching = __esm({
|
|
|
308
311
|
_Caching.entityAnimationCache = _Caching.createAssetCache();
|
|
309
312
|
_Caching.blockEntityIndexCache = _Caching.createAssetCache();
|
|
310
313
|
_Caching.fontCache = _Caching.createAssetCache();
|
|
314
|
+
_Caching.bannerPatternCache = _Caching.createAssetCache();
|
|
311
315
|
Caching = _Caching;
|
|
312
316
|
}
|
|
313
317
|
});
|
|
@@ -328,6 +332,7 @@ var init_Geometries = __esm({
|
|
|
328
332
|
}
|
|
329
333
|
return geometry;
|
|
330
334
|
}
|
|
335
|
+
/** Returns shared geometry. Use {@link createBox} when the caller needs to modify or dispose it independently. */
|
|
331
336
|
static getBox(key) {
|
|
332
337
|
const keyStr = serializeBoxGeometryKey(key);
|
|
333
338
|
return Caching.boxGeometryCache.get(keyStr, (k) => {
|
|
@@ -505,12 +510,15 @@ var init_Requests = __esm({
|
|
|
505
510
|
}
|
|
506
511
|
};
|
|
507
512
|
Requests = class {
|
|
513
|
+
/** Queues a general request. Set `request.signal` to cancel it and `request.timeout` to limit each attempt. */
|
|
508
514
|
static genericRequest(request) {
|
|
509
515
|
return this.genericQueue.request(request);
|
|
510
516
|
}
|
|
517
|
+
/** Queues a request separately from general traffic, using the configured asset base URL for relative paths. */
|
|
511
518
|
static mcAssetRequest(request) {
|
|
512
519
|
return this.mcAssetRequestQueue.request(request);
|
|
513
520
|
}
|
|
521
|
+
/** Sets the base URL for relative asset requests. Use {@link AssetLoader.setVersion} to change Minecraft versions. */
|
|
514
522
|
static setMcAssetRoot(root) {
|
|
515
523
|
this.mcAssetRequestQueue.baseURL = root;
|
|
516
524
|
}
|
|
@@ -565,6 +573,7 @@ var init_WrappedImage = __esm({
|
|
|
565
573
|
}
|
|
566
574
|
return false;
|
|
567
575
|
}
|
|
576
|
+
/** Whether the dimensions form a vertical strip of square frames, without consulting `.mcmeta`. */
|
|
568
577
|
get animated() {
|
|
569
578
|
return this.height > this.width && this.height % this.width === 0;
|
|
570
579
|
}
|
|
@@ -611,6 +620,7 @@ var init_ImageLoader = __esm({
|
|
|
611
620
|
static _createImage() {
|
|
612
621
|
return createImage();
|
|
613
622
|
}
|
|
623
|
+
/** Resolves when an image URL has decoded, or rejects if decoding fails. */
|
|
614
624
|
static async loadAsync(src) {
|
|
615
625
|
return new Promise((resolve, reject) => {
|
|
616
626
|
const image = this._createImage();
|
|
@@ -619,6 +629,7 @@ var init_ImageLoader = __esm({
|
|
|
619
629
|
image.src = src;
|
|
620
630
|
});
|
|
621
631
|
}
|
|
632
|
+
/** Returns an image immediately. Use the callbacks to observe loading, or await {@link loadAsync}. */
|
|
622
633
|
static loadElement(src, onload, onerr) {
|
|
623
634
|
const image = this._createImage();
|
|
624
635
|
if (onload)
|
|
@@ -646,6 +657,7 @@ var init_ImageLoader = __esm({
|
|
|
646
657
|
throw err;
|
|
647
658
|
}
|
|
648
659
|
}
|
|
660
|
+
/** Decodes the supplied bytes into a canvas without fetching the source URL again. */
|
|
649
661
|
static async infoToCanvasData(info) {
|
|
650
662
|
const type = info.type === "jpg" ? "jpeg" : info.type ?? "png";
|
|
651
663
|
const image = await _ImageLoader.loadAsync(`data:image/${type};base64,${info.data.toString("base64")}`);
|
|
@@ -662,12 +674,14 @@ var init_ImageLoader = __esm({
|
|
|
662
674
|
const image = await this.infoToCanvasData(info);
|
|
663
675
|
return image.data.getImageData(0, 0, image.width, image.height);
|
|
664
676
|
}
|
|
677
|
+
/** Returns cached RGBA pixels for an image URL or data URL. Failed loads can be retried. */
|
|
665
678
|
static async getData(src) {
|
|
666
679
|
const keyStr = serializeImageKey({ src });
|
|
667
680
|
return await Caching.imageDataCache.get(keyStr, (k) => {
|
|
668
681
|
return _ImageLoader.loadData(src);
|
|
669
682
|
});
|
|
670
683
|
}
|
|
684
|
+
/** Returns a cached canvas context and dimensions for an image URL or data URL. */
|
|
671
685
|
static async getCanvasData(src) {
|
|
672
686
|
const keyStr = serializeImageKey({ src });
|
|
673
687
|
return await Caching.canvasImageDataCache.get(keyStr, (k) => {
|
|
@@ -750,7 +764,7 @@ var init_TextureLoader = __esm({
|
|
|
750
764
|
static createTexture() {
|
|
751
765
|
return new import_three3.Texture();
|
|
752
766
|
}
|
|
753
|
-
static loadInBackground(src,
|
|
767
|
+
static loadInBackground(src, format2 = import_three3.RGBAFormat, rotation = 0) {
|
|
754
768
|
const texture = this.createTexture();
|
|
755
769
|
texture.colorSpace = THREE.SRGBColorSpace;
|
|
756
770
|
const image = ImageLoader.loadElement(src, () => {
|
|
@@ -760,11 +774,12 @@ var init_TextureLoader = __esm({
|
|
|
760
774
|
console.warn("Failed to load texture", src, error);
|
|
761
775
|
});
|
|
762
776
|
texture.image = image;
|
|
763
|
-
texture.format =
|
|
777
|
+
texture.format = format2;
|
|
764
778
|
texture.rotation = rotation;
|
|
765
779
|
return Textures.initTextureProps(texture);
|
|
766
780
|
}
|
|
767
|
-
|
|
781
|
+
/** Loads image pixels in the background. `rotation` is in radians. Check {@link hasFailed} for load failures. */
|
|
782
|
+
static load(src, format2 = import_three3.RGBAFormat, rotation = 0) {
|
|
768
783
|
const texture = new import_three3.Texture();
|
|
769
784
|
texture.colorSpace = THREE.SRGBColorSpace;
|
|
770
785
|
ImageLoader.getData(src).then((image) => {
|
|
@@ -774,7 +789,7 @@ var init_TextureLoader = __esm({
|
|
|
774
789
|
this.failedTextures.add(texture);
|
|
775
790
|
console.warn("Failed to load texture", src, error);
|
|
776
791
|
});
|
|
777
|
-
texture.format =
|
|
792
|
+
texture.format = format2;
|
|
778
793
|
texture.rotation = rotation;
|
|
779
794
|
return Textures.initTextureProps(texture);
|
|
780
795
|
}
|
|
@@ -801,8 +816,8 @@ var init_Textures = __esm({
|
|
|
801
816
|
for (let y = 0; y < size; y++) {
|
|
802
817
|
for (let x = 0; x < size; x++) {
|
|
803
818
|
const offset = (y * size + x) * 4;
|
|
804
|
-
const
|
|
805
|
-
data[offset] = data[offset + 2] =
|
|
819
|
+
const color2 = (x + y) % 2 === 0 ? 255 : 0;
|
|
820
|
+
data[offset] = data[offset + 2] = color2;
|
|
806
821
|
data[offset + 3] = 255;
|
|
807
822
|
}
|
|
808
823
|
}
|
|
@@ -813,6 +828,7 @@ var init_Textures = __esm({
|
|
|
813
828
|
return this.initTextureProps(texture);
|
|
814
829
|
});
|
|
815
830
|
}
|
|
831
|
+
/** Sets nearest-neighbor filters without changing the texture's color space. Returns the same texture. */
|
|
816
832
|
static initTextureProps(texture) {
|
|
817
833
|
texture.magFilter = THREE2.NearestFilter;
|
|
818
834
|
texture.minFilter = THREE2.NearestFilter;
|
|
@@ -825,11 +841,13 @@ var init_Textures = __esm({
|
|
|
825
841
|
static createAsset(key) {
|
|
826
842
|
return new import_three4.Texture();
|
|
827
843
|
}
|
|
844
|
+
/** Creates an sRGB color texture from a canvas. */
|
|
828
845
|
static createCanvasTexture(canvas) {
|
|
829
846
|
const texture = new import_three4.CanvasTexture(canvas);
|
|
830
847
|
texture.colorSpace = THREE2.SRGBColorSpace;
|
|
831
848
|
return this.initTextureProps(texture);
|
|
832
849
|
}
|
|
850
|
+
/** Returns a shared texture immediately and loads its image asynchronously. Failed cached loads are retried. */
|
|
833
851
|
static getImage(key) {
|
|
834
852
|
const keyStr = serializeTextureKey(key);
|
|
835
853
|
const cached2 = Caching.textureCache.peek(keyStr);
|
|
@@ -854,6 +872,7 @@ var init_Materials = __esm({
|
|
|
854
872
|
init_TextureLoader();
|
|
855
873
|
init_Caching();
|
|
856
874
|
Materials = class _Materials {
|
|
875
|
+
/** Shared checkerboard material, created on first access. */
|
|
857
876
|
static get MISSING_TEXTURE() {
|
|
858
877
|
return Caching.materialCache.get("builtin:missing-texture", () => new import_three5.MeshBasicMaterial({
|
|
859
878
|
map: Textures.getMissing(),
|
|
@@ -925,6 +944,7 @@ var init_Materials = __esm({
|
|
|
925
944
|
material.depthWrite = false;
|
|
926
945
|
break;
|
|
927
946
|
case "eyes":
|
|
947
|
+
case "no_outline":
|
|
928
948
|
material.transparent = true;
|
|
929
949
|
material.alphaTest = 0;
|
|
930
950
|
material.depthWrite = false;
|
|
@@ -957,6 +977,7 @@ var init_Materials = __esm({
|
|
|
957
977
|
if (tint !== void 0) material.color.multiply(new import_three5.Color(tint));
|
|
958
978
|
return material;
|
|
959
979
|
}
|
|
980
|
+
/** Creates an unlit, transparent GUI material without depth writes or tone mapping. */
|
|
960
981
|
static createGuiCanvasMaterial(canvas) {
|
|
961
982
|
const material = new import_three5.MeshBasicMaterial({
|
|
962
983
|
map: Textures.createCanvasTexture(canvas),
|
|
@@ -982,7 +1003,7 @@ var init_Materials = __esm({
|
|
|
982
1003
|
};
|
|
983
1004
|
return material;
|
|
984
1005
|
}
|
|
985
|
-
static createShadedCanvasMaterial(canvas, transparent = false, shade = false) {
|
|
1006
|
+
static createShadedCanvasMaterial(canvas, transparent = false, shade = false, boundUvs = false) {
|
|
986
1007
|
const vertShader = `
|
|
987
1008
|
uniform bool SHADE;
|
|
988
1009
|
|
|
@@ -992,6 +1013,10 @@ var init_Materials = __esm({
|
|
|
992
1013
|
#else
|
|
993
1014
|
varying vec2 vUv;
|
|
994
1015
|
#endif
|
|
1016
|
+
#ifdef BOUND_UVS
|
|
1017
|
+
attribute vec4 uvBounds;
|
|
1018
|
+
flat out vec4 vUvBounds;
|
|
1019
|
+
#endif
|
|
995
1020
|
varying vec3 vTint;
|
|
996
1021
|
varying float light;
|
|
997
1022
|
varying float lift;
|
|
@@ -1027,13 +1052,17 @@ var init_Materials = __esm({
|
|
|
1027
1052
|
}
|
|
1028
1053
|
|
|
1029
1054
|
vUv = uv;
|
|
1055
|
+
#ifdef BOUND_UVS
|
|
1056
|
+
vUvBounds = uvBounds;
|
|
1057
|
+
#endif
|
|
1030
1058
|
vTint = color;
|
|
1031
1059
|
|
|
1060
|
+
vec4 mvPosition = vec4(position, 1.0);
|
|
1032
1061
|
#ifdef USE_INSTANCING
|
|
1033
|
-
|
|
1034
|
-
#else
|
|
1035
|
-
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
|
1062
|
+
mvPosition = instanceMatrix * mvPosition;
|
|
1036
1063
|
#endif
|
|
1064
|
+
mvPosition = modelViewMatrix * mvPosition;
|
|
1065
|
+
gl_Position = projectionMatrix * mvPosition;
|
|
1037
1066
|
|
|
1038
1067
|
}
|
|
1039
1068
|
`;
|
|
@@ -1053,13 +1082,22 @@ var init_Materials = __esm({
|
|
|
1053
1082
|
#else
|
|
1054
1083
|
varying vec2 vUv;
|
|
1055
1084
|
#endif
|
|
1085
|
+
#ifdef BOUND_UVS
|
|
1086
|
+
flat in vec4 vUvBounds;
|
|
1087
|
+
#endif
|
|
1056
1088
|
varying vec3 vTint;
|
|
1057
1089
|
varying float light;
|
|
1058
1090
|
varying float lift;
|
|
1059
1091
|
|
|
1060
1092
|
void main(void)
|
|
1061
1093
|
{
|
|
1062
|
-
|
|
1094
|
+
#ifdef BOUND_UVS
|
|
1095
|
+
// Keep edge samples inside their texture even without centroid interpolation.
|
|
1096
|
+
vec2 mapUv = clamp(vUv, vUvBounds.xy, vUvBounds.zw);
|
|
1097
|
+
#else
|
|
1098
|
+
vec2 mapUv = vUv;
|
|
1099
|
+
#endif
|
|
1100
|
+
vec4 color = texture2D(map, mapUv);
|
|
1063
1101
|
color.rgb *= vTint;
|
|
1064
1102
|
|
|
1065
1103
|
if (color.a < 0.01) discard;
|
|
@@ -1080,7 +1118,8 @@ var init_Materials = __esm({
|
|
|
1080
1118
|
}
|
|
1081
1119
|
`;
|
|
1082
1120
|
try {
|
|
1083
|
-
|
|
1121
|
+
const material = new import_three5.ShaderMaterial({
|
|
1122
|
+
defines: boundUvs ? { BOUND_UVS: true } : {},
|
|
1084
1123
|
uniforms: {
|
|
1085
1124
|
SHADE: { value: true },
|
|
1086
1125
|
BRIGHTNESS: { value: 1 },
|
|
@@ -1098,11 +1137,14 @@ var init_Materials = __esm({
|
|
|
1098
1137
|
side: transparent ? import_three5.DoubleSide : import_three5.FrontSide,
|
|
1099
1138
|
alphaTest: 0.5
|
|
1100
1139
|
});
|
|
1140
|
+
Object.assign(material.defaultAttributeValues, { uvBounds: [0, 0, 1, 1] });
|
|
1141
|
+
return material;
|
|
1101
1142
|
} catch (e) {
|
|
1102
1143
|
console.warn(e);
|
|
1103
1144
|
}
|
|
1104
1145
|
return this.createBasicCanvasMaterial(canvas, transparent, shade);
|
|
1105
1146
|
}
|
|
1147
|
+
/** Returns a shared image material. Use {@link createImage} for a separate material instance. */
|
|
1106
1148
|
static getImage(key) {
|
|
1107
1149
|
const keyStr = serializeMaterialKey(key);
|
|
1108
1150
|
const map = Caching.materialCache.peek(keyStr)?.map;
|
|
@@ -1277,6 +1319,14 @@ var init_AssetKey = __esm({
|
|
|
1277
1319
|
}
|
|
1278
1320
|
};
|
|
1279
1321
|
AssetKey = class _AssetKey extends BasicAssetKey {
|
|
1322
|
+
/**
|
|
1323
|
+
* Creates a key from separate path components. Use {@link parse} for a combined reference.
|
|
1324
|
+
*
|
|
1325
|
+
* @param assetType - Asset directory, such as `models`, `textures`, or `blockstates`.
|
|
1326
|
+
* @param type - Subdirectory, such as `block` or `item`.
|
|
1327
|
+
* @param rootType - Top-level directory, usually `assets` or `data`.
|
|
1328
|
+
* @param root - Base URL override for hosted asset sources.
|
|
1329
|
+
*/
|
|
1280
1330
|
constructor(namespace, path, assetType, type, rootType = "assets", extension = ".json", root) {
|
|
1281
1331
|
super(namespace, path);
|
|
1282
1332
|
this.namespace = namespace;
|
|
@@ -1288,6 +1338,16 @@ var init_AssetKey = __esm({
|
|
|
1288
1338
|
this.root = root;
|
|
1289
1339
|
this.isAssetKey = true;
|
|
1290
1340
|
}
|
|
1341
|
+
/**
|
|
1342
|
+
* Parses an asset reference such as `minecraft:block/stone`.
|
|
1343
|
+
* A missing namespace defaults to the origin's namespace or `minecraft`.
|
|
1344
|
+
*
|
|
1345
|
+
* @param assetType - Asset category, such as `models` or `textures`.
|
|
1346
|
+
* @param str - Path with an optional namespace and file extension.
|
|
1347
|
+
* @param origin - Supplies the asset root, default namespace, and non-texture extension.
|
|
1348
|
+
* @returns A key with the matching extension removed from its path. Textures use `.png`;
|
|
1349
|
+
* other assets use the origin's extension or `.json`.
|
|
1350
|
+
*/
|
|
1291
1351
|
static parse(assetType, str, origin) {
|
|
1292
1352
|
let namespace = origin?.namespace || DEFAULT_NAMESPACE;
|
|
1293
1353
|
if (str.includes(":")) {
|
|
@@ -1319,6 +1379,7 @@ var init_AssetKey = __esm({
|
|
|
1319
1379
|
];
|
|
1320
1380
|
return a.join("/");
|
|
1321
1381
|
}
|
|
1382
|
+
/** Joins the type subdirectory and path without adding a namespace or file extension. */
|
|
1322
1383
|
getFullPath() {
|
|
1323
1384
|
let p10 = [];
|
|
1324
1385
|
if (this.type) {
|
|
@@ -1330,6 +1391,7 @@ var init_AssetKey = __esm({
|
|
|
1330
1391
|
toNamespacedString() {
|
|
1331
1392
|
return this.namespace + ":" + this.getFullPath();
|
|
1332
1393
|
}
|
|
1394
|
+
/** Returns a cache identifier that includes the explicit root or {@link AssetLoader.ROOT}. */
|
|
1333
1395
|
serialize() {
|
|
1334
1396
|
return this.toString();
|
|
1335
1397
|
}
|
|
@@ -1341,17 +1403,17 @@ var init_AssetKey = __esm({
|
|
|
1341
1403
|
var DisplayPosition, DISPLAY_POSITIONS;
|
|
1342
1404
|
var init_DisplayPosition = __esm({
|
|
1343
1405
|
"src/model/DisplayPosition.ts"() {
|
|
1344
|
-
DisplayPosition = /* @__PURE__ */ ((
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
return
|
|
1406
|
+
DisplayPosition = /* @__PURE__ */ ((DisplayPosition3) => {
|
|
1407
|
+
DisplayPosition3["THIRDPERSON_RIGHTHAND"] = "thirdperson_righthand";
|
|
1408
|
+
DisplayPosition3["THIRDPERSON_LEFTHAND"] = "thirdperson_lefthand";
|
|
1409
|
+
DisplayPosition3["FIRSTPERSON_RIGHTHAND"] = "firstperson_righthand";
|
|
1410
|
+
DisplayPosition3["FIRSTPERSON_LEFTHAND"] = "firstperson_lefthand";
|
|
1411
|
+
DisplayPosition3["GUI"] = "gui";
|
|
1412
|
+
DisplayPosition3["HEAD"] = "head";
|
|
1413
|
+
DisplayPosition3["GROUND"] = "ground";
|
|
1414
|
+
DisplayPosition3["FIXED"] = "fixed";
|
|
1415
|
+
DisplayPosition3["ON_SHELF"] = "on_shelf";
|
|
1416
|
+
return DisplayPosition3;
|
|
1355
1417
|
})(DisplayPosition || {});
|
|
1356
1418
|
DISPLAY_POSITIONS = Object.values(DisplayPosition);
|
|
1357
1419
|
}
|
|
@@ -1389,6 +1451,7 @@ var init_DisplayTransforms = __esm({
|
|
|
1389
1451
|
const scale = new import_three7.Vector3(...transform.scale ?? [1, 1, 1]).clampScalar(-4, 4);
|
|
1390
1452
|
return new import_three7.Matrix4().compose(translation, rotation, scale);
|
|
1391
1453
|
}
|
|
1454
|
+
/** Transforms geometry in place and reverses triangle winding when the matrix reflects it. */
|
|
1392
1455
|
static apply(geometry, matrix) {
|
|
1393
1456
|
geometry.applyMatrix4(matrix);
|
|
1394
1457
|
if (matrix.determinant() < 0) {
|
|
@@ -1416,7 +1479,10 @@ var init_ModelMerger = __esm({
|
|
|
1416
1479
|
init_AssetKey();
|
|
1417
1480
|
init_DisplayTransforms();
|
|
1418
1481
|
ModelMerger = class {
|
|
1482
|
+
/** Loads the parent chain and returns a merged model without modifying the supplied definition. */
|
|
1419
1483
|
static async mergeWithParents(model) {
|
|
1484
|
+
const parts = model.parts;
|
|
1485
|
+
if (parts) return { ...model, parts: await Promise.all(parts.map((part) => this.mergeWithParents(part))) };
|
|
1420
1486
|
const models = await this.collectAllParents(model);
|
|
1421
1487
|
let merged = {};
|
|
1422
1488
|
for (const source of [...models, model]) {
|
|
@@ -1428,6 +1494,7 @@ var init_ModelMerger = __esm({
|
|
|
1428
1494
|
}
|
|
1429
1495
|
merged.hierarchy = models.map((m) => m.parent).filter((p10) => `${p10}`);
|
|
1430
1496
|
merged.hierarchy.push(`${merged.parent}`);
|
|
1497
|
+
if (model.components) merged.components = model.components;
|
|
1431
1498
|
return merged;
|
|
1432
1499
|
}
|
|
1433
1500
|
static async collectAllParents(model) {
|
|
@@ -1466,6 +1533,79 @@ var init_AssetParsers = __esm({
|
|
|
1466
1533
|
}
|
|
1467
1534
|
});
|
|
1468
1535
|
|
|
1536
|
+
// src/assets/BannerPatterns.ts
|
|
1537
|
+
var DYE_COLORS, BannerPatterns;
|
|
1538
|
+
var init_BannerPatterns = __esm({
|
|
1539
|
+
"src/assets/BannerPatterns.ts"() {
|
|
1540
|
+
init_AssetKey();
|
|
1541
|
+
init_AssetLoader();
|
|
1542
|
+
init_AssetParsers();
|
|
1543
|
+
init_Caching();
|
|
1544
|
+
DYE_COLORS = Object.freeze({
|
|
1545
|
+
white: 16383998,
|
|
1546
|
+
orange: 16351261,
|
|
1547
|
+
magenta: 13061821,
|
|
1548
|
+
light_blue: 3847130,
|
|
1549
|
+
yellow: 16701501,
|
|
1550
|
+
lime: 8439583,
|
|
1551
|
+
pink: 15961002,
|
|
1552
|
+
gray: 4673362,
|
|
1553
|
+
light_gray: 10329495,
|
|
1554
|
+
cyan: 1481884,
|
|
1555
|
+
purple: 8991416,
|
|
1556
|
+
blue: 3949738,
|
|
1557
|
+
brown: 8606770,
|
|
1558
|
+
green: 6192150,
|
|
1559
|
+
red: 11546150,
|
|
1560
|
+
black: 1908001
|
|
1561
|
+
});
|
|
1562
|
+
BannerPatterns = class {
|
|
1563
|
+
/** Lists namespaced pattern IDs from the selected namespace's data directory. */
|
|
1564
|
+
static async getList(namespace = "minecraft", root) {
|
|
1565
|
+
const collect = async (path) => {
|
|
1566
|
+
const prefix2 = path ? `${path}/` : "";
|
|
1567
|
+
const key = new AssetKey(namespace, `${prefix2}_list`, "banner_pattern", void 0, "data", ".json", root);
|
|
1568
|
+
const list2 = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
|
|
1569
|
+
if (!list2) return [];
|
|
1570
|
+
const files = list2.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${namespace}:${prefix2}${file.slice(0, -5)}`);
|
|
1571
|
+
const children = await Promise.all(list2.directories.map((directory) => collect(`${prefix2}${directory}`)));
|
|
1572
|
+
return [...files, ...children.flat()];
|
|
1573
|
+
};
|
|
1574
|
+
return collect("");
|
|
1575
|
+
}
|
|
1576
|
+
/** Returns the named dye's sRGB value, rejecting unknown colors. */
|
|
1577
|
+
static getColor(color2) {
|
|
1578
|
+
if (typeof color2 !== "string" || !Object.prototype.hasOwnProperty.call(DYE_COLORS, color2)) throw new Error(`Unsupported dye color ${color2}`);
|
|
1579
|
+
return DYE_COLORS[color2];
|
|
1580
|
+
}
|
|
1581
|
+
/** Resolves the first 16 layers of a `banner_patterns` component, preserving their order. */
|
|
1582
|
+
static async getLayers(value, target, root) {
|
|
1583
|
+
if (value === void 0) return [];
|
|
1584
|
+
if (!Array.isArray(value)) throw new Error("banner_patterns must be an array");
|
|
1585
|
+
return Promise.all(value.slice(0, 16).map(async (layer) => {
|
|
1586
|
+
if (!layer || typeof layer !== "object" || Array.isArray(layer)) throw new Error("Each banner pattern requires a pattern and dye color");
|
|
1587
|
+
const color2 = this.getColor(layer.color);
|
|
1588
|
+
let pattern2 = layer.pattern;
|
|
1589
|
+
if (typeof pattern2 === "string") {
|
|
1590
|
+
const id = this.identifier(pattern2);
|
|
1591
|
+
const key = new AssetKey(id.namespace, id.getFullPath(), "banner_pattern", void 0, "data", ".json", root);
|
|
1592
|
+
pattern2 = await Caching.bannerPatternCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
|
|
1593
|
+
if (!pattern2) throw new Error(`Missing banner pattern ${layer.pattern}`);
|
|
1594
|
+
}
|
|
1595
|
+
if (!pattern2 || typeof pattern2 !== "object" || Array.isArray(pattern2) || typeof pattern2.translation_key !== "string") throw new Error("Banner patterns require asset_id and translation_key");
|
|
1596
|
+
const asset2 = this.identifier(pattern2.asset_id);
|
|
1597
|
+
return { color: color2, texture: new AssetKey(asset2.namespace, `${target}/${asset2.getFullPath()}`, "textures", "entity", "assets", ".png", root) };
|
|
1598
|
+
}));
|
|
1599
|
+
}
|
|
1600
|
+
static identifier(value) {
|
|
1601
|
+
if (typeof value !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(value)) throw new Error(`Invalid banner-pattern identifier ${value}`);
|
|
1602
|
+
const [namespace, path] = value.includes(":") ? value.split(":") : ["minecraft", value];
|
|
1603
|
+
return new AssetKey(namespace, path);
|
|
1604
|
+
}
|
|
1605
|
+
};
|
|
1606
|
+
}
|
|
1607
|
+
});
|
|
1608
|
+
|
|
1469
1609
|
// src/assets/Models.ts
|
|
1470
1610
|
var Models;
|
|
1471
1611
|
var init_Models = __esm({
|
|
@@ -1477,12 +1617,15 @@ var init_Models = __esm({
|
|
|
1477
1617
|
init_PersistentCache();
|
|
1478
1618
|
init_AssetKey();
|
|
1479
1619
|
init_AssetParsers();
|
|
1620
|
+
init_DisplayPosition();
|
|
1621
|
+
init_BannerPatterns();
|
|
1480
1622
|
Models = class _Models {
|
|
1481
1623
|
// opened lazily: touching the store at import time would hit IndexedDB/disk just for loading
|
|
1482
1624
|
// the library, and would require an EnvProvider before any entry has had a chance to register
|
|
1483
1625
|
static get PERSISTENT_CACHE() {
|
|
1484
1626
|
return this._persistentCache ?? (this._persistentCache = PersistentCache.open("minerender-models"));
|
|
1485
1627
|
}
|
|
1628
|
+
/** Lists item filenames from the asset source's `_list.json`, with a legacy model-directory fallback. */
|
|
1486
1629
|
static async getItemList() {
|
|
1487
1630
|
const key = new AssetKey(DEFAULT_NAMESPACE, "_list", "items");
|
|
1488
1631
|
const legacyKey = new AssetKey(DEFAULT_NAMESPACE, "_list", "models", "item");
|
|
@@ -1490,34 +1633,174 @@ var init_Models = __esm({
|
|
|
1490
1633
|
return (await AssetLoader.getFirst([key, legacyKey], "list" /* LIST */))?.asset;
|
|
1491
1634
|
}).then((r) => r?.files ?? []);
|
|
1492
1635
|
}
|
|
1493
|
-
static async loadAndMerge(key) {
|
|
1494
|
-
const model = key.type === "item" ? await this.getItemModel(key) : await this.getRaw(key);
|
|
1636
|
+
static async loadAndMerge(key, context = {}) {
|
|
1637
|
+
const model = key.type === "item" ? await this.getItemModel(key, context) : await this.getRaw(key);
|
|
1495
1638
|
if (!model) {
|
|
1496
1639
|
return void 0;
|
|
1497
1640
|
}
|
|
1498
1641
|
return ModelMerger.mergeWithParents(model);
|
|
1499
1642
|
}
|
|
1500
|
-
static async getItemModel(key) {
|
|
1643
|
+
static async getItemModel(key, context = {}) {
|
|
1644
|
+
const preview = this.snapshotContext(key, context);
|
|
1501
1645
|
const itemKey = new AssetKey(key.namespace, key.path, "items", void 0, key.rootType, ".json", key.root);
|
|
1502
|
-
const
|
|
1646
|
+
const cacheKey = itemKey.serialize() + this.contextKey(preview);
|
|
1647
|
+
const model = await this.PERSISTENT_CACHE.getOrLoad(`item-v4:${AssetLoader.persistentKey(cacheKey)}`, async () => {
|
|
1503
1648
|
const result = await AssetLoader.getFirst([itemKey, key], "json" /* JSON */);
|
|
1504
1649
|
if (!result) return void 0;
|
|
1505
|
-
if (result.key.assetType !== "items") return { ...result.asset, key };
|
|
1506
|
-
const
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1650
|
+
if (result.key.assetType !== "items") return { ...result.asset, key, components: preview.components };
|
|
1651
|
+
const load = async (selected) => {
|
|
1652
|
+
if ("parts" in selected) {
|
|
1653
|
+
return { key, parts: await Promise.all(selected.parts.map(load)) };
|
|
1654
|
+
}
|
|
1655
|
+
if ("item" in selected) {
|
|
1656
|
+
const model3 = await this.getItemModel(selected.item, { displayContext: preview.displayContext });
|
|
1657
|
+
if (!model3) throw new Error(`Item ${key.toNamespacedString()} references missing item ${selected.item.toNamespacedString()}`);
|
|
1658
|
+
return model3;
|
|
1659
|
+
}
|
|
1660
|
+
const modelKey = AssetKey.parse("models", selected.model);
|
|
1661
|
+
modelKey.root = key.root;
|
|
1662
|
+
const model2 = await this.getRaw(modelKey);
|
|
1663
|
+
if (!model2) throw new Error(`Item ${key.toNamespacedString()} references missing model ${selected.model}`);
|
|
1664
|
+
return { ...model2, components: preview.components, ...selected.special && { special: selected.special }, ...selected.tints && { tints: selected.tints } };
|
|
1665
|
+
};
|
|
1666
|
+
return load(this.selectItemModel(result.asset.model, key, preview));
|
|
1667
|
+
});
|
|
1668
|
+
const restore = (model2) => ({
|
|
1669
|
+
...model2,
|
|
1670
|
+
key: Object.assign(new AssetKey("", ""), model2.key),
|
|
1671
|
+
...model2.parts && { parts: model2.parts.map(restore) }
|
|
1512
1672
|
});
|
|
1513
|
-
return model ?
|
|
1673
|
+
return model ? restore(model) : void 0;
|
|
1674
|
+
}
|
|
1675
|
+
static contextIdentifier(id) {
|
|
1676
|
+
if (typeof id !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(id)) {
|
|
1677
|
+
throw new Error(`Invalid item-preview identifier: ${id}`);
|
|
1678
|
+
}
|
|
1679
|
+
return id.includes(":") ? id : `minecraft:${id}`;
|
|
1680
|
+
}
|
|
1681
|
+
static snapshotContext(key, context) {
|
|
1682
|
+
const displayContext = context.displayContext ?? "gui" /* GUI */;
|
|
1683
|
+
if (displayContext !== "none" && !Object.values(DisplayPosition).includes(displayContext)) {
|
|
1684
|
+
throw new Error(`Unsupported item display context: ${displayContext}`);
|
|
1685
|
+
}
|
|
1686
|
+
for (const map of [context.properties, context.components, context.itemReferences]) {
|
|
1687
|
+
if (map !== void 0 && (!map || typeof map !== "object" || Array.isArray(map))) {
|
|
1688
|
+
throw new Error("Item-preview properties, components, and references must be objects keyed by identifier");
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
const count = context.count === void 0 ? 1 : context.count;
|
|
1692
|
+
if (!Number.isSafeInteger(count) || count < 0) throw new Error("Item-preview count must be a nonnegative safe integer");
|
|
1693
|
+
const properties = {};
|
|
1694
|
+
for (const [id, value] of Object.entries(context.properties ?? {})) {
|
|
1695
|
+
const property = this.contextIdentifier(id);
|
|
1696
|
+
if (!["boolean", "string", "number"].includes(typeof value) || typeof value === "number" && !Number.isFinite(value)) {
|
|
1697
|
+
throw new Error(`Item-preview property ${property} must be a boolean, string, or finite number`);
|
|
1698
|
+
}
|
|
1699
|
+
if (Object.prototype.hasOwnProperty.call(properties, property)) throw new Error(`Duplicate item-preview property: ${property}`);
|
|
1700
|
+
properties[property] = value;
|
|
1701
|
+
}
|
|
1702
|
+
const components = {};
|
|
1703
|
+
for (const [id, value] of Object.entries(context.components ?? {})) {
|
|
1704
|
+
const component = this.contextIdentifier(id);
|
|
1705
|
+
if (Object.prototype.hasOwnProperty.call(components, component)) throw new Error(`Duplicate item-preview component: ${component}`);
|
|
1706
|
+
components[component] = this.snapshotJson(value);
|
|
1707
|
+
}
|
|
1708
|
+
const itemReferences = {};
|
|
1709
|
+
for (const [id, value] of Object.entries(context.itemReferences ?? {})) {
|
|
1710
|
+
const reference = this.contextIdentifier(id);
|
|
1711
|
+
if (!isAssetKey(value) || value.assetType !== "models" || value.type !== "item") {
|
|
1712
|
+
throw new Error(`Item-preview reference ${reference} must be an item-model AssetKey`);
|
|
1713
|
+
}
|
|
1714
|
+
this.contextIdentifier(`${value.namespace}:${value.path}`);
|
|
1715
|
+
if (Object.prototype.hasOwnProperty.call(itemReferences, reference)) throw new Error(`Duplicate item-preview reference: ${reference}`);
|
|
1716
|
+
itemReferences[reference] = Object.assign(new AssetKey("", ""), value, { root: value.root ?? key.root });
|
|
1717
|
+
}
|
|
1718
|
+
return { displayContext, properties, components, count, itemReferences };
|
|
1719
|
+
}
|
|
1720
|
+
static contextKey(context) {
|
|
1721
|
+
const itemReferences = Object.fromEntries(Object.entries(context.itemReferences).map(([id, key]) => [id, key.serialize()]));
|
|
1722
|
+
return `|item-v4:${JSON.stringify(this.snapshotJson({ ...context, itemReferences }))}`;
|
|
1514
1723
|
}
|
|
1515
|
-
|
|
1516
|
-
|
|
1724
|
+
static snapshotJson(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
1725
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return value;
|
|
1726
|
+
if (typeof value !== "object" || ancestors.has(value) || !Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
|
|
1727
|
+
throw new Error("Item-preview components must contain finite JSON values without circular references");
|
|
1728
|
+
}
|
|
1729
|
+
ancestors.add(value);
|
|
1730
|
+
const copy = Array.isArray(value) ? Array.from(value, (entry) => this.snapshotJson(entry, ancestors)) : Object.fromEntries(Object.keys(value).sort().map((key) => [key, this.snapshotJson(value[key], ancestors)]));
|
|
1731
|
+
ancestors.delete(value);
|
|
1732
|
+
return copy;
|
|
1733
|
+
}
|
|
1734
|
+
static propertyValue(node, context) {
|
|
1735
|
+
const kind = node.type.replace(/^minecraft:/, "");
|
|
1736
|
+
const property = node.property ? this.contextIdentifier(node.property) : void 0;
|
|
1737
|
+
if (kind === "select" && property === "minecraft:display_context") return context.displayContext;
|
|
1738
|
+
if (property && Object.prototype.hasOwnProperty.call(context.properties, property)) return context.properties[property];
|
|
1739
|
+
const fallback = kind === "condition" ? false : kind === "range_dispatch" ? 0 : void 0;
|
|
1740
|
+
const component = (id) => context.components[this.contextIdentifier(id)];
|
|
1741
|
+
if (property === "minecraft:custom_model_data") {
|
|
1742
|
+
const index = node.index ?? 0;
|
|
1743
|
+
if (!Number.isSafeInteger(index) || index < 0) throw new Error("Item-model custom_model_data index must be a nonnegative integer");
|
|
1744
|
+
const data = component("custom_model_data");
|
|
1745
|
+
if (data === void 0) return fallback;
|
|
1746
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) throw new Error("Item-preview custom_model_data must be an object");
|
|
1747
|
+
const field = kind === "condition" ? "flags" : kind === "select" ? "strings" : "floats";
|
|
1748
|
+
const values = data[field];
|
|
1749
|
+
if (values === void 0) return fallback;
|
|
1750
|
+
if (!Array.isArray(values)) throw new Error(`Item-preview custom_model_data.${field} must be an array`);
|
|
1751
|
+
return index < values.length ? values[index] : fallback;
|
|
1752
|
+
}
|
|
1753
|
+
if (kind === "condition" && property === "minecraft:has_component") {
|
|
1754
|
+
if (node.ignore_default) throw new Error("Item-preview has_component with ignore_default requires an explicit properties override; item defaults are unavailable");
|
|
1755
|
+
return Object.prototype.hasOwnProperty.call(context.components, this.contextIdentifier(node.component));
|
|
1756
|
+
}
|
|
1757
|
+
if (kind === "select") {
|
|
1758
|
+
switch (property) {
|
|
1759
|
+
case "minecraft:component":
|
|
1760
|
+
return component(node.component);
|
|
1761
|
+
case "minecraft:block_state": {
|
|
1762
|
+
const state = component("block_state");
|
|
1763
|
+
if (state === void 0) return void 0;
|
|
1764
|
+
if (!state || typeof state !== "object" || Array.isArray(state)) throw new Error("Item-preview block_state must be an object");
|
|
1765
|
+
if (typeof node.block_state_property !== "string") throw new Error("Item-model block_state requires block_state_property");
|
|
1766
|
+
return Object.prototype.hasOwnProperty.call(state, node.block_state_property) ? state[node.block_state_property] : void 0;
|
|
1767
|
+
}
|
|
1768
|
+
case "minecraft:charge_type": {
|
|
1769
|
+
const projectiles = component("charged_projectiles");
|
|
1770
|
+
if (projectiles === void 0) return "none";
|
|
1771
|
+
if (!Array.isArray(projectiles)) throw new Error("Item-preview charged_projectiles must be an array of item stacks");
|
|
1772
|
+
const ids = projectiles.map((stack) => {
|
|
1773
|
+
if (!stack || typeof stack !== "object" || Array.isArray(stack)) throw new Error("Item-preview charged_projectiles must contain item-stack objects");
|
|
1774
|
+
return this.contextIdentifier(stack.id);
|
|
1775
|
+
});
|
|
1776
|
+
return !ids.length ? "none" : ids.includes("minecraft:firework_rocket") ? "rocket" : "arrow";
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
if (kind === "range_dispatch" && (property === "minecraft:damage" || property === "minecraft:count")) {
|
|
1781
|
+
const damage = property === "minecraft:damage";
|
|
1782
|
+
const maximumComponent = component(damage ? "max_damage" : "max_stack_size");
|
|
1783
|
+
const maximum = maximumComponent === void 0 ? damage ? 0 : 1 : maximumComponent;
|
|
1784
|
+
const damageComponent = component("damage");
|
|
1785
|
+
const value = damage ? damageComponent === void 0 ? 0 : damageComponent : context.count;
|
|
1786
|
+
if (typeof value !== "number" || typeof maximum !== "number" || maximum < 0) throw new Error(`Item-preview ${property} requires numeric components and a nonnegative maximum`);
|
|
1787
|
+
const clamped = Math.max(0, Math.min(maximum, value));
|
|
1788
|
+
return node.normalize === false ? clamped : clamped / maximum;
|
|
1789
|
+
}
|
|
1790
|
+
return fallback;
|
|
1791
|
+
}
|
|
1792
|
+
static selectItemModel(node, key, context) {
|
|
1517
1793
|
if (!node || typeof node.type !== "string") {
|
|
1518
1794
|
throw new Error(`Unsupported item model definition for ${key.toNamespacedString()}`);
|
|
1519
1795
|
}
|
|
1520
1796
|
switch (node.type.replace(/^minecraft:/, "")) {
|
|
1797
|
+
case "composite":
|
|
1798
|
+
if (Array.isArray(node.models)) return { parts: node.models.map((child) => this.selectItemModel(child, key, context)) };
|
|
1799
|
+
break;
|
|
1800
|
+
case "bundle/selected_item": {
|
|
1801
|
+
const item = context.itemReferences["minecraft:bundle/selected_item"];
|
|
1802
|
+
return item ? { item } : { parts: [] };
|
|
1803
|
+
}
|
|
1521
1804
|
case "model":
|
|
1522
1805
|
if (typeof node.model === "string" && node.model) return { model: node.model, tints: node.tints };
|
|
1523
1806
|
break;
|
|
@@ -1525,6 +1808,23 @@ var init_Models = __esm({
|
|
|
1525
1808
|
if (!node.base || !node.model || typeof node.model !== "object") break;
|
|
1526
1809
|
const special = node.model;
|
|
1527
1810
|
switch (special.type) {
|
|
1811
|
+
case "banner":
|
|
1812
|
+
case "minecraft:banner":
|
|
1813
|
+
if (typeof special.color === "string" && Object.prototype.hasOwnProperty.call(DYE_COLORS, special.color)) return { model: node.base, special };
|
|
1814
|
+
break;
|
|
1815
|
+
case "shield":
|
|
1816
|
+
case "minecraft:shield":
|
|
1817
|
+
case "trident":
|
|
1818
|
+
case "minecraft:trident":
|
|
1819
|
+
case "conduit":
|
|
1820
|
+
case "minecraft:conduit":
|
|
1821
|
+
return { model: node.base, special };
|
|
1822
|
+
case "shulker_box":
|
|
1823
|
+
case "minecraft:shulker_box":
|
|
1824
|
+
if (typeof special.texture === "string" && special.texture && (special.openness === void 0 || typeof special.openness === "number" && Number.isFinite(special.openness)) && (special.orientation === void 0 || ["down", "up", "north", "south", "west", "east"].includes(special.orientation))) {
|
|
1825
|
+
return { model: node.base, special };
|
|
1826
|
+
}
|
|
1827
|
+
break;
|
|
1528
1828
|
case "chest":
|
|
1529
1829
|
case "minecraft:chest":
|
|
1530
1830
|
case "bed":
|
|
@@ -1538,21 +1838,34 @@ var init_Models = __esm({
|
|
|
1538
1838
|
}
|
|
1539
1839
|
throw new Error(`Unsupported special item renderer ${special.type} for ${key.toNamespacedString()}`);
|
|
1540
1840
|
}
|
|
1541
|
-
case "condition":
|
|
1542
|
-
|
|
1841
|
+
case "condition": {
|
|
1842
|
+
const value = this.propertyValue(node, context);
|
|
1843
|
+
if (typeof value !== "boolean") throw new Error(`Item-preview condition ${node.property} requires a boolean`);
|
|
1844
|
+
return this.selectItemModel(value ? node.on_true : node.on_false, key, context);
|
|
1845
|
+
}
|
|
1543
1846
|
case "select": {
|
|
1544
|
-
const
|
|
1545
|
-
|
|
1847
|
+
const value = this.propertyValue(node, context);
|
|
1848
|
+
const component = node.property && this.contextIdentifier(node.property) === "minecraft:component";
|
|
1849
|
+
if (!component && value !== void 0 && typeof value !== "string") throw new Error(`Item-preview selector ${node.property} requires a string`);
|
|
1850
|
+
const matches = (candidate) => component ? JSON.stringify(this.snapshotJson(candidate)) === JSON.stringify(this.snapshotJson(value)) : candidate === value;
|
|
1851
|
+
const selected = value === void 0 ? void 0 : node.cases?.find((entry) => matches(entry.when) || Array.isArray(entry.when) && entry.when.some(matches))?.model;
|
|
1852
|
+
return this.selectItemModel(selected ?? node.fallback, key, context);
|
|
1546
1853
|
}
|
|
1547
1854
|
case "range_dispatch": {
|
|
1855
|
+
const value = this.propertyValue(node, context);
|
|
1856
|
+
if (typeof value !== "number") throw new Error(`Item-preview range ${node.property} requires a number`);
|
|
1857
|
+
if (Number.isNaN(value)) return this.selectItemModel(node.fallback, key, context);
|
|
1858
|
+
const scaled = value * (node.scale ?? 1);
|
|
1859
|
+
if (!Number.isFinite(scaled)) throw new Error(`Item-preview range ${node.property} requires a finite value and scale`);
|
|
1548
1860
|
const selected = node.entries?.reduce((match, entry) => {
|
|
1549
|
-
return entry.threshold <=
|
|
1861
|
+
return entry.threshold <= scaled && (!match || entry.threshold >= match.threshold) ? entry : match;
|
|
1550
1862
|
}, void 0);
|
|
1551
|
-
return this.
|
|
1863
|
+
return this.selectItemModel(selected?.model ?? node.fallback, key, context);
|
|
1552
1864
|
}
|
|
1553
1865
|
}
|
|
1554
1866
|
throw new Error(`Unsupported item model ${node?.type ?? "definition"} for ${key.toNamespacedString()}`);
|
|
1555
1867
|
}
|
|
1868
|
+
/** Loads one model file without resolving its parents. Returns `undefined` when the file is missing. */
|
|
1556
1869
|
static async getRaw(key) {
|
|
1557
1870
|
if (!key.assetType) {
|
|
1558
1871
|
key.assetType = "models";
|
|
@@ -1565,28 +1878,39 @@ var init_Models = __esm({
|
|
|
1565
1878
|
return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
|
|
1566
1879
|
return AssetLoader.get(key, "model" /* MODEL */);
|
|
1567
1880
|
});
|
|
1568
|
-
}).then((
|
|
1569
|
-
if (
|
|
1570
|
-
|
|
1881
|
+
}).then((asset2) => {
|
|
1882
|
+
if (asset2) {
|
|
1883
|
+
asset2.key = key;
|
|
1571
1884
|
}
|
|
1572
|
-
return
|
|
1885
|
+
return asset2;
|
|
1573
1886
|
});
|
|
1574
1887
|
}
|
|
1575
|
-
|
|
1888
|
+
/**
|
|
1889
|
+
* Loads a model and resolves its parent chain. Returns `undefined` when the model is missing.
|
|
1890
|
+
* Item keys default to GUI context and a stack count of 1. Unresolved conditions are false and numeric properties are zero.
|
|
1891
|
+
* Pass `context` to supply component values, property overrides, and item references for a preview.
|
|
1892
|
+
* Composite items retain independently merged children in `ItemModel.parts`.
|
|
1893
|
+
*
|
|
1894
|
+
* @param key - Model key, for example `AssetKey.parse("models", "minecraft:item/diamond_sword")`.
|
|
1895
|
+
*/
|
|
1896
|
+
static async getMerged(key, context = {}) {
|
|
1576
1897
|
if (!key.assetType) {
|
|
1577
1898
|
key.assetType = "models";
|
|
1578
1899
|
}
|
|
1579
1900
|
if (!key.extension) {
|
|
1580
1901
|
key.extension = ".json";
|
|
1581
1902
|
}
|
|
1582
|
-
const
|
|
1903
|
+
const preview = this.snapshotContext(key, context);
|
|
1904
|
+
const keyStr = key.serialize() + (key.type === "item" ? this.contextKey(preview) : "");
|
|
1583
1905
|
return Caching.mergedModelCache.get(keyStr, (k) => {
|
|
1584
|
-
return _Models.loadAndMerge(key);
|
|
1906
|
+
return _Models.loadAndMerge(key, preview);
|
|
1585
1907
|
});
|
|
1586
1908
|
}
|
|
1587
|
-
|
|
1588
|
-
|
|
1909
|
+
/** Alias for {@link getMerged}. */
|
|
1910
|
+
static async get(key, context = {}) {
|
|
1911
|
+
return this.getMerged(key, context);
|
|
1589
1912
|
}
|
|
1913
|
+
/** Clears persisted models. Use {@link Caching.clear} to also discard in-memory assets. */
|
|
1590
1914
|
static async clearCache() {
|
|
1591
1915
|
return this.PERSISTENT_CACHE.clear();
|
|
1592
1916
|
}
|
|
@@ -1609,6 +1933,7 @@ var init_BlockStates = __esm({
|
|
|
1609
1933
|
return this._persistentCache ?? (this._persistentCache = PersistentCache.open("minerender-blockstates"));
|
|
1610
1934
|
}
|
|
1611
1935
|
// BlockState names are hardcoded
|
|
1936
|
+
/** Returns blockstate filenames from the selected version's `_list.json`, or an empty list. */
|
|
1612
1937
|
static async getList() {
|
|
1613
1938
|
const key = new AssetKey(
|
|
1614
1939
|
DEFAULT_NAMESPACE,
|
|
@@ -1629,6 +1954,7 @@ var init_BlockStates = __esm({
|
|
|
1629
1954
|
return AssetLoader.get(key, "json" /* JSON */);
|
|
1630
1955
|
});
|
|
1631
1956
|
}
|
|
1957
|
+
/** Looks up vanilla property definitions for the key's block path, or returns `undefined`. */
|
|
1632
1958
|
static async getDefaultState(key) {
|
|
1633
1959
|
const defaultStates = await this.getDefaultStates();
|
|
1634
1960
|
if (!defaultStates) {
|
|
@@ -1636,6 +1962,7 @@ var init_BlockStates = __esm({
|
|
|
1636
1962
|
}
|
|
1637
1963
|
return defaultStates["minecraft:" + key.path];
|
|
1638
1964
|
}
|
|
1965
|
+
/** Loads a cached blockstate definition for {@link MineRenderScene.addBlock}, or returns `undefined`. */
|
|
1639
1966
|
static async get(key) {
|
|
1640
1967
|
if (!key.assetType) {
|
|
1641
1968
|
key.assetType = "blockstates";
|
|
@@ -1648,11 +1975,11 @@ var init_BlockStates = __esm({
|
|
|
1648
1975
|
return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
|
|
1649
1976
|
return AssetLoader.get(key, "blockstate" /* BLOCKSTATE */);
|
|
1650
1977
|
});
|
|
1651
|
-
}).then((
|
|
1652
|
-
if (
|
|
1653
|
-
|
|
1978
|
+
}).then((asset2) => {
|
|
1979
|
+
if (asset2) {
|
|
1980
|
+
asset2.key = key;
|
|
1654
1981
|
}
|
|
1655
|
-
return
|
|
1982
|
+
return asset2;
|
|
1656
1983
|
});
|
|
1657
1984
|
}
|
|
1658
1985
|
static getAll(keys) {
|
|
@@ -1662,6 +1989,7 @@ var init_BlockStates = __esm({
|
|
|
1662
1989
|
}
|
|
1663
1990
|
return Promise.all(promises);
|
|
1664
1991
|
}
|
|
1992
|
+
/** Clears persisted blockstates. Use {@link Caching.clear} to also discard in-memory assets. */
|
|
1665
1993
|
static async clearCache() {
|
|
1666
1994
|
await this.PERSISTENT_CACHE.clear();
|
|
1667
1995
|
}
|
|
@@ -1687,9 +2015,11 @@ var init_Assets = __esm({
|
|
|
1687
2015
|
static parseAssetKey(assetType, str, origin) {
|
|
1688
2016
|
return AssetKey.parse(assetType, str, origin);
|
|
1689
2017
|
}
|
|
2018
|
+
/** Loads a model with its parent chain resolved. See {@link Models.getMerged}. */
|
|
1690
2019
|
static async getModel(key) {
|
|
1691
2020
|
return Models.getMerged(key);
|
|
1692
2021
|
}
|
|
2022
|
+
/** Loads a blockstate definition. See {@link BlockStates.get}. */
|
|
1693
2023
|
static async getBlockState(key) {
|
|
1694
2024
|
return BlockStates.get(key);
|
|
1695
2025
|
}
|
|
@@ -1704,6 +2034,10 @@ var init_AssetSource = __esm({
|
|
|
1704
2034
|
AssetSource = class {
|
|
1705
2035
|
constructor() {
|
|
1706
2036
|
}
|
|
2037
|
+
/** Whether this source prevents lower-priority sources from supplying the key. Its own assets remain available. */
|
|
2038
|
+
blocks(key) {
|
|
2039
|
+
return false;
|
|
2040
|
+
}
|
|
1707
2041
|
/**
|
|
1708
2042
|
* Identifies this source's content across sessions for persistent caching. Sources that
|
|
1709
2043
|
* cannot tell whether their content changed (an unnamed archive, for example) return
|
|
@@ -1732,6 +2066,10 @@ var init_BatchedExecutor = __esm({
|
|
|
1732
2066
|
"src/util/BatchedExecutor.ts"() {
|
|
1733
2067
|
import_jobqu2 = require("jobqu");
|
|
1734
2068
|
BatchedExecutor = class {
|
|
2069
|
+
/**
|
|
2070
|
+
* @param interval - Delay between queue runs, in milliseconds.
|
|
2071
|
+
* @param batch - Maximum tasks started per run and maximum tasks active at once.
|
|
2072
|
+
*/
|
|
1735
2073
|
constructor(interval = 1, batch = 30) {
|
|
1736
2074
|
if (!Number.isFinite(interval) || interval < 0) throw new RangeError("Batch interval must be finite and nonnegative");
|
|
1737
2075
|
if (!Number.isInteger(batch) || batch < 1) throw new RangeError("Batch size must be a positive integer");
|
|
@@ -1739,9 +2077,11 @@ var init_BatchedExecutor = __esm({
|
|
|
1739
2077
|
this.batch = batch;
|
|
1740
2078
|
this.queue = new import_jobqu2.JobQueue(async (task) => task(), { interval, maxPerRun: batch, maxActive: batch });
|
|
1741
2079
|
}
|
|
2080
|
+
/** Queues a task and resolves with its result, or rejects with its error. */
|
|
1742
2081
|
submit(task) {
|
|
1743
2082
|
return this.queue.add(() => task());
|
|
1744
2083
|
}
|
|
2084
|
+
/** Rejects queued and future tasks. Tasks already running are allowed to finish. */
|
|
1745
2085
|
stop() {
|
|
1746
2086
|
this.queue.end();
|
|
1747
2087
|
}
|
|
@@ -1788,29 +2128,29 @@ function applyGenericRotation(axis, rotation, obj) {
|
|
|
1788
2128
|
break;
|
|
1789
2129
|
}
|
|
1790
2130
|
}
|
|
1791
|
-
function addBox3WireframeToObject(box, obj,
|
|
2131
|
+
function addBox3WireframeToObject(box, obj, color2 = 16777215, w = 2) {
|
|
1792
2132
|
let size = new import_three8.Vector3();
|
|
1793
2133
|
box.getSize(size);
|
|
1794
2134
|
let boxGeo = new import_three8.BoxGeometry(size.x, size.y, size.z, 1, 1, 1);
|
|
1795
2135
|
let wireGeo = new import_three8.EdgesGeometry(boxGeo);
|
|
1796
|
-
let wireMat = new import_three8.LineBasicMaterial({ color, linewidth: w });
|
|
2136
|
+
let wireMat = new import_three8.LineBasicMaterial({ color: color2, linewidth: w });
|
|
1797
2137
|
let wireframe = new import_three8.LineSegments(wireGeo, wireMat);
|
|
1798
2138
|
obj.add(wireframe);
|
|
1799
2139
|
let axes = new import_three8.AxesHelper(1);
|
|
1800
2140
|
obj.add(axes);
|
|
1801
2141
|
}
|
|
1802
|
-
function addWireframeToObject(obj,
|
|
2142
|
+
function addWireframeToObject(obj, color2 = 16777215, w = 2) {
|
|
1803
2143
|
let boxGeo = new import_three8.BoxGeometry(16, 16, 16, 1, 1, 1);
|
|
1804
2144
|
let wireGeo = new import_three8.EdgesGeometry(boxGeo);
|
|
1805
|
-
let wireMat = new import_three8.LineBasicMaterial({ color, linewidth: w });
|
|
2145
|
+
let wireMat = new import_three8.LineBasicMaterial({ color: color2, linewidth: w });
|
|
1806
2146
|
let wireframe = new import_three8.LineSegments(wireGeo, wireMat);
|
|
1807
2147
|
obj.add(wireframe);
|
|
1808
2148
|
let axes = new import_three8.AxesHelper(1);
|
|
1809
2149
|
obj.add(axes);
|
|
1810
2150
|
}
|
|
1811
|
-
function addWireframeToMesh(geo, mesh,
|
|
2151
|
+
function addWireframeToMesh(geo, mesh, color2 = 16777215, w = 2) {
|
|
1812
2152
|
let wireGeo = new import_three8.EdgesGeometry(geo);
|
|
1813
|
-
let wireMat = new import_three8.LineBasicMaterial({ color, linewidth: w });
|
|
2153
|
+
let wireMat = new import_three8.LineBasicMaterial({ color: color2, linewidth: w });
|
|
1814
2154
|
let wireframe = new import_three8.LineSegments(wireGeo, wireMat);
|
|
1815
2155
|
mesh.add(wireframe);
|
|
1816
2156
|
let axes = new import_three8.AxesHelper(1);
|
|
@@ -1884,10 +2224,11 @@ var init_NBTHelper = __esm({
|
|
|
1884
2224
|
"src/nbt/NBTHelper.ts"() {
|
|
1885
2225
|
import_buffer2 = require("buffer");
|
|
1886
2226
|
NBTHelper = class {
|
|
1887
|
-
|
|
2227
|
+
/** Parses NBT bytes and preserves format and compression metadata. Omit `format` for automatic detection. */
|
|
2228
|
+
static async fromBuffer(data, format2) {
|
|
1888
2229
|
const buffer = import_buffer2.Buffer.from(data instanceof Uint8Array ? data : new Uint8Array(data));
|
|
1889
2230
|
const prismarineNbt = await import("prismarine-nbt");
|
|
1890
|
-
const { parsed, type, metadata } = await prismarineNbt.parse(buffer,
|
|
2231
|
+
const { parsed, type, metadata } = await prismarineNbt.parse(buffer, format2);
|
|
1891
2232
|
return Object.assign(parsed, {
|
|
1892
2233
|
format: type,
|
|
1893
2234
|
metadata,
|
|
@@ -1952,6 +2293,7 @@ var init_HostedAssetSource = __esm({
|
|
|
1952
2293
|
retryDefaults: true
|
|
1953
2294
|
};
|
|
1954
2295
|
_HostedAssetSource = class _HostedAssetSource extends AssetSource {
|
|
2296
|
+
/** Retries missing assets with the `minecraft` namespace and default root. Other failures reject. */
|
|
1955
2297
|
async loadOrRetryWithDefaults(key, parser) {
|
|
1956
2298
|
const direct = await this.load(key, parser);
|
|
1957
2299
|
if (typeof direct !== "undefined") {
|
|
@@ -2009,6 +2351,10 @@ var init_HostedAssetSource = __esm({
|
|
|
2009
2351
|
assetBasePath(key) {
|
|
2010
2352
|
return `${key.root ?? this.root ?? AssetLoader.ROOT}/${key.rootType !== void 0 ? key.rootType + "/" : ""}${key.namespace !== void 0 ? key.namespace + "/" : ""}${key.assetType !== void 0 ? key.assetType + "/" : ""}`;
|
|
2011
2353
|
}
|
|
2354
|
+
/**
|
|
2355
|
+
* Creates a hosted source for {@link AssetLoader.addSource}.
|
|
2356
|
+
* Set `options.retryDefaults` to `false` to restrict lookups to the requested namespace and root.
|
|
2357
|
+
*/
|
|
2012
2358
|
constructor(root, options) {
|
|
2013
2359
|
super();
|
|
2014
2360
|
this._root = root;
|
|
@@ -2101,6 +2447,11 @@ var init_BrowserArchiveProxy = __esm({
|
|
|
2101
2447
|
"src/assets/source/archive/BrowserArchiveProxy.ts"() {
|
|
2102
2448
|
import_zip = require("@zip.js/zip.js");
|
|
2103
2449
|
BrowserArchiveProxy = class {
|
|
2450
|
+
/**
|
|
2451
|
+
* Opens a ZIP for asset lookup.
|
|
2452
|
+
* @param id - Cache identity for this content. Change it when the pack changes.
|
|
2453
|
+
* A File supplies an identity from its name, size, and modification time when omitted.
|
|
2454
|
+
*/
|
|
2104
2455
|
constructor(blob, id) {
|
|
2105
2456
|
this._blob = blob;
|
|
2106
2457
|
this._reader = new import_zip.ZipReader(new import_zip.BlobReader(this._blob));
|
|
@@ -2121,6 +2472,162 @@ var init_BrowserArchiveProxy = __esm({
|
|
|
2121
2472
|
}
|
|
2122
2473
|
});
|
|
2123
2474
|
|
|
2475
|
+
// src/assets/source/archive/PackMetadata.ts
|
|
2476
|
+
function object(value, field) {
|
|
2477
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
2478
|
+
throw new Error(`Invalid ${field}: expected an object`);
|
|
2479
|
+
}
|
|
2480
|
+
return value;
|
|
2481
|
+
}
|
|
2482
|
+
function integer(value, field) {
|
|
2483
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
2484
|
+
throw new Error(`Invalid ${field}: expected a nonnegative integer`);
|
|
2485
|
+
}
|
|
2486
|
+
return value;
|
|
2487
|
+
}
|
|
2488
|
+
function format(value, field, upper = false) {
|
|
2489
|
+
const parts = Array.isArray(value) ? value : [value];
|
|
2490
|
+
if (parts.length < 1 || parts.length > 2) {
|
|
2491
|
+
throw new Error(`Invalid ${field}: expected a major version or [major, minor]`);
|
|
2492
|
+
}
|
|
2493
|
+
return [integer(parts[0], field), parts.length === 2 ? integer(parts[1], field) : upper ? Infinity : 0];
|
|
2494
|
+
}
|
|
2495
|
+
function compare(a, b) {
|
|
2496
|
+
return a[0] === b[0] ? a[1] - b[1] : a[0] - b[0];
|
|
2497
|
+
}
|
|
2498
|
+
function overlay(value, field) {
|
|
2499
|
+
const entry = object(value, field);
|
|
2500
|
+
const directory = entry.directory;
|
|
2501
|
+
if (typeof directory !== "string" || !directory || /[^-_a-zA-Z0-9.]/.test(directory) || directory === "." || directory === "..") {
|
|
2502
|
+
throw new Error(`Invalid ${field}.directory: expected a directory name without path separators`);
|
|
2503
|
+
}
|
|
2504
|
+
let min, max;
|
|
2505
|
+
if ("min_format" in entry || "max_format" in entry) {
|
|
2506
|
+
min = format(entry.min_format, `${field}.min_format`);
|
|
2507
|
+
max = format(entry.max_format, `${field}.max_format`, true);
|
|
2508
|
+
} else {
|
|
2509
|
+
const range = entry.formats;
|
|
2510
|
+
let lower, upper;
|
|
2511
|
+
if (typeof range === "number") {
|
|
2512
|
+
lower = upper = range;
|
|
2513
|
+
} else if (Array.isArray(range)) {
|
|
2514
|
+
if (range.length !== 2) throw new Error(`Invalid ${field}.formats: expected [min, max]`);
|
|
2515
|
+
[lower, upper] = range;
|
|
2516
|
+
} else {
|
|
2517
|
+
const bounds = object(range, `${field}.formats`);
|
|
2518
|
+
lower = bounds.min_inclusive;
|
|
2519
|
+
upper = bounds.max_inclusive;
|
|
2520
|
+
}
|
|
2521
|
+
min = [integer(lower, `${field}.formats.min_inclusive`), 0];
|
|
2522
|
+
max = [integer(upper, `${field}.formats.max_inclusive`), Infinity];
|
|
2523
|
+
}
|
|
2524
|
+
if (compare(min, max) > 0) throw new Error(`Invalid ${field}: minimum format exceeds maximum format`);
|
|
2525
|
+
return { directory, min, max };
|
|
2526
|
+
}
|
|
2527
|
+
function pattern(value, field) {
|
|
2528
|
+
if (value === void 0) return void 0;
|
|
2529
|
+
if (typeof value !== "string") throw new Error(`Invalid ${field}: expected a regular expression string`);
|
|
2530
|
+
try {
|
|
2531
|
+
return new RegExp(value);
|
|
2532
|
+
} catch (cause) {
|
|
2533
|
+
throw new Error(`Invalid ${field}: ${String(cause)}`);
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
var PackMetadata;
|
|
2537
|
+
var init_PackMetadata = __esm({
|
|
2538
|
+
"src/assets/source/archive/PackMetadata.ts"() {
|
|
2539
|
+
PackMetadata = class _PackMetadata {
|
|
2540
|
+
constructor(overlays, filters) {
|
|
2541
|
+
this.overlays = overlays;
|
|
2542
|
+
this.filters = filters;
|
|
2543
|
+
this.hasOverlays = overlays.length > 0;
|
|
2544
|
+
}
|
|
2545
|
+
static parse(value) {
|
|
2546
|
+
const metadata = object(value, "pack.mcmeta");
|
|
2547
|
+
let overlays = [];
|
|
2548
|
+
if ("overlays" in metadata) {
|
|
2549
|
+
const entries = object(metadata.overlays, "overlays").entries;
|
|
2550
|
+
if (!Array.isArray(entries)) throw new Error("Invalid overlays.entries: expected an array");
|
|
2551
|
+
overlays = entries.map((entry, index) => overlay(entry, `overlays.entries[${index}]`));
|
|
2552
|
+
}
|
|
2553
|
+
let filters = [];
|
|
2554
|
+
if ("filter" in metadata) {
|
|
2555
|
+
const entries = object(metadata.filter, "filter").block;
|
|
2556
|
+
if (!Array.isArray(entries)) throw new Error("Invalid filter.block: expected an array");
|
|
2557
|
+
filters = entries.map((value2, index) => {
|
|
2558
|
+
const field = `filter.block[${index}]`;
|
|
2559
|
+
const entry = object(value2, field);
|
|
2560
|
+
return { namespace: pattern(entry.namespace, `${field}.namespace`), path: pattern(entry.path, `${field}.path`) };
|
|
2561
|
+
});
|
|
2562
|
+
}
|
|
2563
|
+
return new _PackMetadata(overlays, filters);
|
|
2564
|
+
}
|
|
2565
|
+
/** Returns applicable directories in lookup order, with the last declared overlay first. */
|
|
2566
|
+
overlayDirectories(target) {
|
|
2567
|
+
const selected = format(target, "target format");
|
|
2568
|
+
return this.overlays.filter((entry) => compare(selected, entry.min) >= 0 && compare(selected, entry.max) <= 0).map((entry) => entry.directory).reverse();
|
|
2569
|
+
}
|
|
2570
|
+
blocks(namespace, path) {
|
|
2571
|
+
return this.filters.some((entry) => entry.namespace?.test(namespace) ?? true) && this.filters.some((entry) => entry.path?.test(path) ?? true);
|
|
2572
|
+
}
|
|
2573
|
+
};
|
|
2574
|
+
}
|
|
2575
|
+
});
|
|
2576
|
+
|
|
2577
|
+
// src/assets/source/archive/PackFormats.ts
|
|
2578
|
+
var PackFormats;
|
|
2579
|
+
var init_PackFormats = __esm({
|
|
2580
|
+
"src/assets/source/archive/PackFormats.ts"() {
|
|
2581
|
+
init_Requests();
|
|
2582
|
+
PackFormats = class {
|
|
2583
|
+
/** Resolves pack formats from Mojang's client metadata hosted on assets.mcasset.cloud. */
|
|
2584
|
+
static async get(version2, type) {
|
|
2585
|
+
let pending = this.versions.get(version2);
|
|
2586
|
+
if (!pending || pending.expires <= Date.now()) {
|
|
2587
|
+
pending = {
|
|
2588
|
+
formats: this.load(version2),
|
|
2589
|
+
expires: ["latest", "release", "snapshot"].includes(version2) ? Date.now() + 3e5 : Infinity
|
|
2590
|
+
};
|
|
2591
|
+
this.versions.set(version2, pending);
|
|
2592
|
+
}
|
|
2593
|
+
try {
|
|
2594
|
+
return (await pending.formats)[type];
|
|
2595
|
+
} catch (cause) {
|
|
2596
|
+
if (this.versions.get(version2) === pending) this.versions.delete(version2);
|
|
2597
|
+
const option = type === "assets" ? "resourcePackFormat" : "dataPackFormat";
|
|
2598
|
+
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
2599
|
+
throw new Error(`Could not resolve the pack format for Minecraft ${version2}. Set ArchiveAssetSource ${option} explicitly. ${reason}`);
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
static async load(version2) {
|
|
2603
|
+
const response = await Requests.genericRequest({
|
|
2604
|
+
url: `https://assets.mcasset.cloud/${encodeURIComponent(version2)}/game-version.json`,
|
|
2605
|
+
responseType: "json"
|
|
2606
|
+
});
|
|
2607
|
+
const data = response.data;
|
|
2608
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
2609
|
+
throw new Error("Expected version metadata to be an object");
|
|
2610
|
+
}
|
|
2611
|
+
const packs = data.pack_version;
|
|
2612
|
+
if (typeof packs !== "number" && (!packs || typeof packs !== "object" || Array.isArray(packs))) {
|
|
2613
|
+
throw new Error("Expected pack_version to be a number or an object");
|
|
2614
|
+
}
|
|
2615
|
+
const format2 = (type) => {
|
|
2616
|
+
const modern = typeof packs === "object" && ["resource_major", "resource_minor", "data_major", "data_minor"].some((key) => key in packs);
|
|
2617
|
+
const major = typeof packs === "number" ? packs : packs[modern ? `${type}_major` : type];
|
|
2618
|
+
const minor = modern ? packs[`${type}_minor`] : 0;
|
|
2619
|
+
if (typeof major !== "number" || !Number.isSafeInteger(major) || major < 0 || typeof minor !== "number" || !Number.isSafeInteger(minor) || minor < 0) {
|
|
2620
|
+
throw new Error(`Invalid ${type} pack format in version metadata`);
|
|
2621
|
+
}
|
|
2622
|
+
return Object.freeze([major, minor]);
|
|
2623
|
+
};
|
|
2624
|
+
return { assets: format2("resource"), data: format2("data") };
|
|
2625
|
+
}
|
|
2626
|
+
};
|
|
2627
|
+
PackFormats.versions = /* @__PURE__ */ new Map();
|
|
2628
|
+
}
|
|
2629
|
+
});
|
|
2630
|
+
|
|
2124
2631
|
// src/assets/source/archive/ArchiveAssetSource.ts
|
|
2125
2632
|
var ArchiveAssetSource;
|
|
2126
2633
|
var init_ArchiveAssetSource = __esm({
|
|
@@ -2129,35 +2636,78 @@ var init_ArchiveAssetSource = __esm({
|
|
|
2129
2636
|
init_request();
|
|
2130
2637
|
init_HostedAssetSource();
|
|
2131
2638
|
init_BrowserArchiveProxy();
|
|
2639
|
+
init_AssetLoader();
|
|
2640
|
+
init_PackMetadata();
|
|
2641
|
+
init_PackFormats();
|
|
2132
2642
|
ArchiveAssetSource = class _ArchiveAssetSource extends AssetSource {
|
|
2133
|
-
constructor(archiveProxy) {
|
|
2643
|
+
constructor(archiveProxy, options = {}) {
|
|
2134
2644
|
super();
|
|
2135
|
-
console.log(archiveProxy);
|
|
2136
2645
|
this._archiveProxy = archiveProxy;
|
|
2646
|
+
const copyFormat = (format2) => typeof format2 === "object" ? [format2[0], format2[1]] : format2;
|
|
2647
|
+
this.options = {
|
|
2648
|
+
resourcePackFormat: copyFormat(options.resourcePackFormat),
|
|
2649
|
+
dataPackFormat: copyFormat(options.dataPackFormat)
|
|
2650
|
+
};
|
|
2137
2651
|
}
|
|
2138
|
-
|
|
2139
|
-
|
|
2652
|
+
/** Creates a browser ZIP source from a Blob or File. */
|
|
2653
|
+
static blob(blob, options) {
|
|
2654
|
+
return new _ArchiveAssetSource(new BrowserArchiveProxy(blob), options);
|
|
2140
2655
|
}
|
|
2141
2656
|
get cacheId() {
|
|
2142
2657
|
const id = this._archiveProxy.id;
|
|
2143
|
-
|
|
2658
|
+
const formats = [this.options.resourcePackFormat ?? AssetLoader.version, this.options.dataPackFormat ?? AssetLoader.version];
|
|
2659
|
+
return id === void 0 ? void 0 : `archive-metadata:1:${id}:${JSON.stringify(formats)}`;
|
|
2144
2660
|
}
|
|
2145
2661
|
async getEntries() {
|
|
2146
|
-
return this._archiveProxy.getEntries()
|
|
2662
|
+
return this.entries ?? (this.entries = this._archiveProxy.getEntries().catch((error) => {
|
|
2663
|
+
this.entries = void 0;
|
|
2664
|
+
throw error;
|
|
2665
|
+
}));
|
|
2147
2666
|
}
|
|
2148
2667
|
async getEntry(path) {
|
|
2149
2668
|
const entries = await this.getEntries();
|
|
2150
2669
|
return entries.find((e) => e.filename === path);
|
|
2151
2670
|
}
|
|
2671
|
+
async getMetadata(key) {
|
|
2672
|
+
try {
|
|
2673
|
+
return await (this.metadata ?? (this.metadata = this.getEntry("pack.mcmeta").then(async (entry) => PackMetadata.parse(entry ? JSON.parse(await (await entry.getData()).text()) : {})).catch((error) => {
|
|
2674
|
+
this.metadata = void 0;
|
|
2675
|
+
throw error;
|
|
2676
|
+
})));
|
|
2677
|
+
} catch (cause) {
|
|
2678
|
+
throw new AssetLoadError(this, key, "pack.mcmeta", cause);
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
async blocks(key) {
|
|
2682
|
+
if (key.rootType !== "assets" && key.rootType !== "data") return false;
|
|
2683
|
+
const metadata = await this.getMetadata(key);
|
|
2684
|
+
const path = `${key.assetType !== void 0 ? key.assetType + "/" : ""}${key.getFullPath()}${key.extension}`;
|
|
2685
|
+
return metadata.blocks(key.namespace, path);
|
|
2686
|
+
}
|
|
2152
2687
|
async get(key, parser) {
|
|
2153
2688
|
const responseParser = HostedAssetSource.PARSER_MAP.get(parser);
|
|
2154
2689
|
return this.load(key, responseParser);
|
|
2155
2690
|
}
|
|
2156
2691
|
async load(key, parser) {
|
|
2157
2692
|
const path = `${this.assetBasePath(key)}${key.type !== void 0 ? key.type + "/" : ""}${key.path}${key.extension}`;
|
|
2693
|
+
const version2 = AssetLoader.version;
|
|
2694
|
+
let selectedPath = path;
|
|
2158
2695
|
let url;
|
|
2159
2696
|
try {
|
|
2160
|
-
|
|
2697
|
+
let directories = [];
|
|
2698
|
+
if (key.rootType === "assets" || key.rootType === "data") {
|
|
2699
|
+
const metadata = await this.getMetadata(key);
|
|
2700
|
+
if (metadata.hasOverlays) {
|
|
2701
|
+
const format2 = (key.rootType === "assets" ? this.options.resourcePackFormat : this.options.dataPackFormat) ?? await PackFormats.get(version2, key.rootType);
|
|
2702
|
+
directories = metadata.overlayDirectories(format2);
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
let entry;
|
|
2706
|
+
for (const directory of [...directories, ""]) {
|
|
2707
|
+
selectedPath = directory ? `${directory}/${path}` : path;
|
|
2708
|
+
entry = await this.getEntry(selectedPath);
|
|
2709
|
+
if (entry) break;
|
|
2710
|
+
}
|
|
2161
2711
|
if (!entry) {
|
|
2162
2712
|
return void 0;
|
|
2163
2713
|
}
|
|
@@ -2170,7 +2720,8 @@ var init_ArchiveAssetSource = __esm({
|
|
|
2170
2720
|
}
|
|
2171
2721
|
return await parser.parse(response);
|
|
2172
2722
|
} catch (cause) {
|
|
2173
|
-
|
|
2723
|
+
if (cause instanceof AssetLoadError) throw cause;
|
|
2724
|
+
throw new AssetLoadError(this, key, selectedPath, cause);
|
|
2174
2725
|
} finally {
|
|
2175
2726
|
if (url !== void 0) {
|
|
2176
2727
|
URL.revokeObjectURL(url);
|
|
@@ -2203,6 +2754,8 @@ var init_archive = __esm({
|
|
|
2203
2754
|
init_ArchiveEntry();
|
|
2204
2755
|
init_ArchiveProxy();
|
|
2205
2756
|
init_BrowserArchiveProxy();
|
|
2757
|
+
init_PackFormats();
|
|
2758
|
+
init_PackMetadata();
|
|
2206
2759
|
}
|
|
2207
2760
|
});
|
|
2208
2761
|
|
|
@@ -2231,8 +2784,9 @@ var init_AssetLoader = __esm({
|
|
|
2231
2784
|
static get version() {
|
|
2232
2785
|
return this.ROOT.substring(this.ROOT.lastIndexOf("/") + 1);
|
|
2233
2786
|
}
|
|
2234
|
-
|
|
2235
|
-
|
|
2787
|
+
/** Selects an mcasset.cloud version, updates the vanilla source, and clears in-memory caches. */
|
|
2788
|
+
static setVersion(version2) {
|
|
2789
|
+
this.ROOT = `https://assets.mcasset.cloud/${version2}`;
|
|
2236
2790
|
const source = new HostedAssetSource(this.ROOT, { retryDefaults: false });
|
|
2237
2791
|
const index = this._SOURCES.findIndex((s) => s.key === "mcassets");
|
|
2238
2792
|
if (index !== -1) {
|
|
@@ -2264,6 +2818,13 @@ var init_AssetLoader = __esm({
|
|
|
2264
2818
|
return scope ? `${scope}
|
|
2265
2819
|
${key}` : key;
|
|
2266
2820
|
}
|
|
2821
|
+
/**
|
|
2822
|
+
* Registers a source at the highest priority.
|
|
2823
|
+
* Call {@link Caching.clear} after changing sources to discard previously loaded assets.
|
|
2824
|
+
*
|
|
2825
|
+
* @param key - Name used to replace or remove this source.
|
|
2826
|
+
* @param override - Removes the first source with this name before adding the new one.
|
|
2827
|
+
*/
|
|
2267
2828
|
static addSource(key, source, override = true) {
|
|
2268
2829
|
if (override) {
|
|
2269
2830
|
const existing = this.removeSource(key);
|
|
@@ -2274,6 +2835,7 @@ ${key}` : key;
|
|
|
2274
2835
|
this._SOURCES.unshift({ key, source });
|
|
2275
2836
|
console.log(p4, "Added AssetSource", key);
|
|
2276
2837
|
}
|
|
2838
|
+
/** Removes and returns the first source with this name. Call {@link Caching.clear} to reload assets. */
|
|
2277
2839
|
static removeSource(key) {
|
|
2278
2840
|
const index = this._SOURCES.findIndex((s) => s.key === key);
|
|
2279
2841
|
if (index != -1) {
|
|
@@ -2284,14 +2846,16 @@ ${key}` : key;
|
|
|
2284
2846
|
}
|
|
2285
2847
|
return void 0;
|
|
2286
2848
|
}
|
|
2849
|
+
/** Loads from all sources and returns defined results in priority order. Any source failure rejects the call. */
|
|
2287
2850
|
static async getAll(key, parser) {
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2851
|
+
const sources = [...this._SOURCES];
|
|
2852
|
+
const results = [];
|
|
2853
|
+
for (const { source } of sources) {
|
|
2854
|
+
const asset2 = await source.get(key, parser);
|
|
2855
|
+
if (asset2 != void 0) results.push(asset2);
|
|
2856
|
+
if (await source.blocks(key)) break;
|
|
2291
2857
|
}
|
|
2292
|
-
return
|
|
2293
|
-
return results.filter((r) => r != void 0);
|
|
2294
|
-
});
|
|
2858
|
+
return results;
|
|
2295
2859
|
}
|
|
2296
2860
|
/** Returns the first defined result in source-priority order, without merging assets. */
|
|
2297
2861
|
static async get(key, parser) {
|
|
@@ -2312,17 +2876,22 @@ ${key}` : key;
|
|
|
2312
2876
|
/** Tries each path within a source before considering lower-priority sources. */
|
|
2313
2877
|
static async getFirst(keys, parser) {
|
|
2314
2878
|
const sources = [...this._SOURCES];
|
|
2879
|
+
let remaining = [...keys];
|
|
2315
2880
|
for (const source of sources) {
|
|
2316
|
-
for (const key of
|
|
2881
|
+
for (const key of remaining) {
|
|
2317
2882
|
const result = await source.source.get(key, parser);
|
|
2318
2883
|
if (result !== void 0) {
|
|
2319
2884
|
return { key, asset: result };
|
|
2320
2885
|
}
|
|
2321
2886
|
}
|
|
2887
|
+
const blocked = await Promise.all(remaining.map((key) => source.source.blocks(key)));
|
|
2888
|
+
remaining = remaining.filter((_, index) => !blocked[index]);
|
|
2889
|
+
if (!remaining.length) break;
|
|
2322
2890
|
}
|
|
2323
2891
|
return void 0;
|
|
2324
2892
|
}
|
|
2325
2893
|
};
|
|
2894
|
+
/** Default asset base URL. Use {@link setVersion} to select a Minecraft version. */
|
|
2326
2895
|
_AssetLoader.ROOT = DEFAULT_ROOT;
|
|
2327
2896
|
_AssetLoader._SOURCES = [];
|
|
2328
2897
|
/** Used when a registered source cannot identify its content, so nothing persists past this session. */
|
|
@@ -2347,14 +2916,15 @@ var init_ModelTextures = __esm({
|
|
|
2347
2916
|
static get PERSISTENT_META_CACHE() {
|
|
2348
2917
|
return this._persistentMetaCache ?? (this._persistentMetaCache = PersistentCache.open("minerender-texturemeta"));
|
|
2349
2918
|
}
|
|
2919
|
+
/** Loads and decodes a texture into a readable canvas, or returns `undefined` when missing. */
|
|
2350
2920
|
static async get(key) {
|
|
2351
2921
|
const keyStr = key.serialize();
|
|
2352
2922
|
const pending = this.preload(key);
|
|
2353
2923
|
const cached2 = Caching.textureAssetCache.getIfPresent(keyStr);
|
|
2354
2924
|
try {
|
|
2355
|
-
const
|
|
2356
|
-
if (
|
|
2357
|
-
return await ImageLoader.infoToCanvasData(
|
|
2925
|
+
const asset2 = await pending;
|
|
2926
|
+
if (asset2) {
|
|
2927
|
+
return await ImageLoader.infoToCanvasData(asset2);
|
|
2358
2928
|
}
|
|
2359
2929
|
return void 0;
|
|
2360
2930
|
} catch (err) {
|
|
@@ -2364,16 +2934,18 @@ var init_ModelTextures = __esm({
|
|
|
2364
2934
|
throw err;
|
|
2365
2935
|
}
|
|
2366
2936
|
}
|
|
2937
|
+
/** Fetches and caches encoded texture bytes without decoding the pixels. */
|
|
2367
2938
|
static async preload(key) {
|
|
2368
2939
|
const keyStr = key.serialize();
|
|
2369
2940
|
return Caching.textureAssetCache.get(keyStr, (k) => {
|
|
2370
|
-
return AssetLoader.get(key, "image" /* IMAGE */).then((
|
|
2371
|
-
if (
|
|
2372
|
-
|
|
2373
|
-
return
|
|
2941
|
+
return AssetLoader.get(key, "image" /* IMAGE */).then((asset2) => {
|
|
2942
|
+
if (asset2)
|
|
2943
|
+
asset2.key = key;
|
|
2944
|
+
return asset2;
|
|
2374
2945
|
});
|
|
2375
2946
|
});
|
|
2376
2947
|
}
|
|
2948
|
+
/** Loads a texture's `.mcmeta` file, or returns `undefined` when no metadata file exists. */
|
|
2377
2949
|
static async getMeta(key) {
|
|
2378
2950
|
if (!key.extension || !key.extension.endsWith(".mcmeta")) {
|
|
2379
2951
|
key = Object.assign(new AssetKey("", ""), key);
|
|
@@ -2382,14 +2954,15 @@ var init_ModelTextures = __esm({
|
|
|
2382
2954
|
const keyStr = key.serialize();
|
|
2383
2955
|
return Caching.textureMetaCache.get(keyStr, (k) => {
|
|
2384
2956
|
return this.PERSISTENT_META_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
|
|
2385
|
-
return AssetLoader.get(key, "meta" /* META */).then((
|
|
2386
|
-
if (
|
|
2387
|
-
|
|
2388
|
-
return
|
|
2957
|
+
return AssetLoader.get(key, "meta" /* META */).then((asset2) => {
|
|
2958
|
+
if (asset2)
|
|
2959
|
+
asset2.key = key;
|
|
2960
|
+
return asset2;
|
|
2389
2961
|
});
|
|
2390
2962
|
});
|
|
2391
2963
|
});
|
|
2392
2964
|
}
|
|
2965
|
+
/** Clears persisted texture metadata. Use {@link Caching.clear} to discard in-memory textures and metadata. */
|
|
2393
2966
|
static async clearCache() {
|
|
2394
2967
|
await this.PERSISTENT_META_CACHE.clear();
|
|
2395
2968
|
}
|
|
@@ -2456,6 +3029,7 @@ var init_Ticker = __esm({
|
|
|
2456
3029
|
}
|
|
2457
3030
|
return handle;
|
|
2458
3031
|
}
|
|
3032
|
+
/** Starts or resumes the shared tick loop. Repeated calls have no effect. */
|
|
2459
3033
|
static start() {
|
|
2460
3034
|
if (this.interval) return;
|
|
2461
3035
|
this.interval = this.schedule(() => {
|
|
@@ -2478,6 +3052,7 @@ var init_Ticker = __esm({
|
|
|
2478
3052
|
_Ticker.fiveSecondTicks = 0;
|
|
2479
3053
|
}, 1e3 * 5);
|
|
2480
3054
|
}
|
|
3055
|
+
/** Pauses the loop while retaining callbacks for a later {@link start}. */
|
|
2481
3056
|
static stop() {
|
|
2482
3057
|
if (this.interval) clearInterval(this.interval);
|
|
2483
3058
|
if (this.oneSecondTicksTracker) clearInterval(this.oneSecondTicksTracker);
|
|
@@ -2486,12 +3061,14 @@ var init_Ticker = __esm({
|
|
|
2486
3061
|
this.oneSecondTicksTracker = void 0;
|
|
2487
3062
|
this.fiveSecondTicksTracker = void 0;
|
|
2488
3063
|
}
|
|
3064
|
+
/** Registers a callback, starts the loop if needed, and returns an ID for {@link remove}. */
|
|
2489
3065
|
static add(tick) {
|
|
2490
3066
|
this.start();
|
|
2491
3067
|
const c = this.counter++;
|
|
2492
3068
|
this._tickers.set(c, tick);
|
|
2493
3069
|
return c;
|
|
2494
3070
|
}
|
|
3071
|
+
/** Removes a callback by ID and stops the timers when no callbacks remain. */
|
|
2495
3072
|
static remove(c) {
|
|
2496
3073
|
if (typeof c === "number") this._tickers.delete(c);
|
|
2497
3074
|
if (!this._tickers.size) this.stop();
|
|
@@ -2505,6 +3082,7 @@ var init_Ticker = __esm({
|
|
|
2505
3082
|
static get tpsFiveSeconds() {
|
|
2506
3083
|
return this.fiveSecondTps;
|
|
2507
3084
|
}
|
|
3085
|
+
/** Stops the loop and removes all registered callbacks. */
|
|
2508
3086
|
static dispose() {
|
|
2509
3087
|
this.stop();
|
|
2510
3088
|
this._tickers.clear();
|
|
@@ -2559,6 +3137,7 @@ var init_TextureAtlas = __esm({
|
|
|
2559
3137
|
}
|
|
2560
3138
|
};
|
|
2561
3139
|
}
|
|
3140
|
+
/** Copies pixels for a texture variable from its current atlas rectangle. */
|
|
2562
3141
|
getData(texture) {
|
|
2563
3142
|
const pos = this.positions[texture];
|
|
2564
3143
|
const size = this.sizes[texture];
|
|
@@ -2769,6 +3348,37 @@ var init_UVMapper = __esm({
|
|
|
2769
3348
|
static facesToUv(faces, originalTextureSize, actualTextureSize) {
|
|
2770
3349
|
return new import_three9.Float32BufferAttribute(this.facesToUvArray(faces, originalTextureSize, actualTextureSize), 2);
|
|
2771
3350
|
}
|
|
3351
|
+
/** Bounds texture sampling to texel centres without changing cropped face UVs. */
|
|
3352
|
+
static setAtlasUvBounds(geometry, faces, atlas) {
|
|
3353
|
+
const uv = geometry.getAttribute("uv");
|
|
3354
|
+
const bounds = new Float32Array(uv.count * 4);
|
|
3355
|
+
for (const [faceIndex, faceName] of CUBE_FACES.entries()) {
|
|
3356
|
+
const texture = faces[faceName]?.texture?.substring(1);
|
|
3357
|
+
const position = texture && atlas.positions[texture];
|
|
3358
|
+
const size = texture && atlas.sizes[texture];
|
|
3359
|
+
let minU = Infinity, minV = Infinity, maxU = -Infinity, maxV = -Infinity;
|
|
3360
|
+
if (position && size) {
|
|
3361
|
+
minU = position[0] / atlas.image.width;
|
|
3362
|
+
maxU = (position[0] + size[0]) / atlas.image.width;
|
|
3363
|
+
minV = 1 - (position[1] + size[1]) / atlas.image.height;
|
|
3364
|
+
maxV = 1 - position[1] / atlas.image.height;
|
|
3365
|
+
} else {
|
|
3366
|
+
for (let corner = 0; corner < 4; corner++) {
|
|
3367
|
+
const index = faceIndex * 4 + corner;
|
|
3368
|
+
minU = Math.min(minU, uv.getX(index));
|
|
3369
|
+
minV = Math.min(minV, uv.getY(index));
|
|
3370
|
+
maxU = Math.max(maxU, uv.getX(index));
|
|
3371
|
+
maxV = Math.max(maxV, uv.getY(index));
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
const insetU = Math.min(0.5 / atlas.image.width, (maxU - minU) / 2);
|
|
3375
|
+
const insetV = Math.min(0.5 / atlas.image.height, (maxV - minV) / 2);
|
|
3376
|
+
for (let corner = 0; corner < 4; corner++) {
|
|
3377
|
+
bounds.set([minU + insetU, minV + insetV, maxU - insetU, maxV - insetV], (faceIndex * 4 + corner) * 4);
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
geometry.setAttribute("uvBounds", new import_three9.Float32BufferAttribute(bounds, 4));
|
|
3381
|
+
}
|
|
2772
3382
|
static lockUvs(geometry, faces, atlas, rotation) {
|
|
2773
3383
|
const uv = geometry.getAttribute("uv");
|
|
2774
3384
|
for (let faceIndex = 0; faceIndex < CUBE_FACES.length; faceIndex++) {
|
|
@@ -2805,6 +3415,7 @@ var init_UVMapper = __esm({
|
|
|
2805
3415
|
}
|
|
2806
3416
|
uv.needsUpdate = true;
|
|
2807
3417
|
}
|
|
3418
|
+
/** Returns the shared atlas for a keyed, merged model, creating it on first use. */
|
|
2808
3419
|
static async getAtlas(model) {
|
|
2809
3420
|
const keyStr = model.key.serialize();
|
|
2810
3421
|
return Caching.modelTextureAtlasCache.get(keyStr, (k) => {
|
|
@@ -2875,6 +3486,11 @@ var init_UVMapper = __esm({
|
|
|
2875
3486
|
}
|
|
2876
3487
|
return [tl, tr, bl, br];
|
|
2877
3488
|
}
|
|
3489
|
+
/**
|
|
3490
|
+
* Combines model textures and animation metadata into an atlas.
|
|
3491
|
+
* Mapped UVs are stored on copies of the model's elements, available through the returned atlas.
|
|
3492
|
+
* Returns `undefined` for models without a texture map.
|
|
3493
|
+
*/
|
|
2878
3494
|
static async createAtlas(originalModel) {
|
|
2879
3495
|
const textureMap = {};
|
|
2880
3496
|
const metaMap = {};
|
|
@@ -2895,12 +3511,12 @@ var init_UVMapper = __esm({
|
|
|
2895
3511
|
} else {
|
|
2896
3512
|
uniqueTextureNames.push(textureKey);
|
|
2897
3513
|
const assetKey = AssetKey.parse("textures", textureValue, model.key);
|
|
2898
|
-
promises.push(ModelTextures.get(assetKey).then((
|
|
2899
|
-
if (!
|
|
3514
|
+
promises.push(ModelTextures.get(assetKey).then((asset2) => {
|
|
3515
|
+
if (!asset2) {
|
|
2900
3516
|
console.warn(p5, "Missing texture", assetKey);
|
|
2901
3517
|
return;
|
|
2902
3518
|
}
|
|
2903
|
-
textureMap[textureKey] = new WrappedImage(
|
|
3519
|
+
textureMap[textureKey] = new WrappedImage(asset2);
|
|
2904
3520
|
}));
|
|
2905
3521
|
promises.push(ModelTextures.getMeta(assetKey).then((meta) => {
|
|
2906
3522
|
metaMap[textureKey] = meta;
|
|
@@ -3239,14 +3855,15 @@ var init_Entities = __esm({
|
|
|
3239
3855
|
init_MineRenderError();
|
|
3240
3856
|
init_ModelTextures();
|
|
3241
3857
|
Entities = class {
|
|
3858
|
+
/** Lists model paths in the `minecraft` namespace, including subdirectories and excluding `.json`. */
|
|
3242
3859
|
static async getEntityList() {
|
|
3243
3860
|
const collect = async (path) => {
|
|
3244
3861
|
const prefix2 = path ? `${path}/` : "";
|
|
3245
3862
|
const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json");
|
|
3246
|
-
const
|
|
3247
|
-
if (!
|
|
3248
|
-
const files =
|
|
3249
|
-
const children = await Promise.all(
|
|
3863
|
+
const list2 = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
|
|
3864
|
+
if (!list2) return [];
|
|
3865
|
+
const files = list2.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
|
|
3866
|
+
const children = await Promise.all(list2.directories.map((directory) => collect(`${prefix2}${directory}`)));
|
|
3250
3867
|
return [...files, ...children.flat()];
|
|
3251
3868
|
};
|
|
3252
3869
|
return collect("");
|
|
@@ -3256,6 +3873,7 @@ var init_Entities = __esm({
|
|
|
3256
3873
|
const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
|
|
3257
3874
|
return Caching.entityModelCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
|
|
3258
3875
|
}
|
|
3876
|
+
/** Returns selectable geometry-layer names, or an empty list when the model is missing. */
|
|
3259
3877
|
static async getLayerList(modelKey) {
|
|
3260
3878
|
return Object.keys((await this.getModelFile(modelKey))?.layers ?? {});
|
|
3261
3879
|
}
|
|
@@ -3263,6 +3881,15 @@ var init_Entities = __esm({
|
|
|
3263
3881
|
static async getPassList(modelKey) {
|
|
3264
3882
|
return (await this.getModelFile(modelKey))?.passes ?? [];
|
|
3265
3883
|
}
|
|
3884
|
+
/**
|
|
3885
|
+
* Loads the layers and textures to pass to {@link MineRenderScene.addEntity}.
|
|
3886
|
+
* By default, selects `main`, unconditional passes, and passes enabled by `options.when`.
|
|
3887
|
+
*
|
|
3888
|
+
* @param modelKey - Dataset model, such as `new BasicAssetKey("minecraft", "zombie")`.
|
|
3889
|
+
* @param textureKey - Texture override for the first selected layer.
|
|
3890
|
+
* @param options - Layer selection, conditional passes, and per-layer texture overrides.
|
|
3891
|
+
* @returns The selected model, or `undefined` when its dataset file is missing.
|
|
3892
|
+
*/
|
|
3266
3893
|
static async getEntity(modelKey, textureKey, options) {
|
|
3267
3894
|
const model = await this.getModelFile(modelKey);
|
|
3268
3895
|
if (!model) return void 0;
|
|
@@ -3308,6 +3935,7 @@ var init_Entities = __esm({
|
|
|
3308
3935
|
const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
|
|
3309
3936
|
return file?.animations;
|
|
3310
3937
|
}
|
|
3938
|
+
/** Finds a fallback texture using the model path, its directory, and vanilla variant data. */
|
|
3311
3939
|
static async resolveTexture(modelKey) {
|
|
3312
3940
|
const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
|
|
3313
3941
|
const root = isAssetKey(modelKey) ? modelKey.root : void 0;
|
|
@@ -3319,8 +3947,8 @@ var init_Entities = __esm({
|
|
|
3319
3947
|
if (await ModelTextures.get(texture2)) return texture2;
|
|
3320
3948
|
}
|
|
3321
3949
|
const listKey = new AssetKey(modelKey.namespace, "_list", `${name}_variant`, void 0, "data", ".json", root);
|
|
3322
|
-
const
|
|
3323
|
-
const files =
|
|
3950
|
+
const list2 = await AssetLoader.get(listKey, "list" /* LIST */);
|
|
3951
|
+
const files = list2?.files.filter((file2) => file2.endsWith(".json") && file2 !== "_list.json");
|
|
3324
3952
|
const file = files?.find((file2) => file2 === "temperate.json") ?? files?.[0];
|
|
3325
3953
|
if (!file) return void 0;
|
|
3326
3954
|
const variantKey = new AssetKey(modelKey.namespace, file.slice(0, -5), `${name}_variant`, void 0, "data", ".json", root);
|
|
@@ -3425,27 +4053,27 @@ var init_EntityAnimationPlayer = __esm({
|
|
|
3425
4053
|
for (const layer of layers) {
|
|
3426
4054
|
const objects = [];
|
|
3427
4055
|
if (poses) {
|
|
3428
|
-
layer.traverse((
|
|
3429
|
-
if (poses.has(
|
|
4056
|
+
layer.traverse((object3) => {
|
|
4057
|
+
if (poses.has(object3)) objects.push(object3);
|
|
3430
4058
|
});
|
|
3431
4059
|
} else {
|
|
3432
4060
|
for (const bone of Object.keys(animation.bones)) {
|
|
3433
|
-
const
|
|
3434
|
-
if (
|
|
4061
|
+
const object3 = layer.getObjectByName(`group:${bone}`);
|
|
4062
|
+
if (object3) objects.push(object3);
|
|
3435
4063
|
}
|
|
3436
4064
|
}
|
|
3437
|
-
for (const
|
|
3438
|
-
const bone =
|
|
3439
|
-
const pose = poses?.get(
|
|
4065
|
+
for (const object3 of objects) {
|
|
4066
|
+
const bone = object3.name.slice("group:".length);
|
|
4067
|
+
const pose = poses?.get(object3);
|
|
3440
4068
|
this.parts.push({
|
|
3441
4069
|
bone,
|
|
3442
|
-
object,
|
|
3443
|
-
position:
|
|
3444
|
-
rotation:
|
|
3445
|
-
scale:
|
|
3446
|
-
basePosition: pose ? new import_three11.Vector3(...pose.offset) :
|
|
3447
|
-
baseRotation: pose ? new import_three11.Euler(...pose.rotation, "ZYX") :
|
|
3448
|
-
baseScale: pose ? new import_three11.Vector3(...pose.scale ?? [1, 1, 1]) :
|
|
4070
|
+
object: object3,
|
|
4071
|
+
position: object3.position.clone(),
|
|
4072
|
+
rotation: object3.rotation.clone(),
|
|
4073
|
+
scale: object3.scale.clone(),
|
|
4074
|
+
basePosition: pose ? new import_three11.Vector3(...pose.offset) : object3.position.clone(),
|
|
4075
|
+
baseRotation: pose ? new import_three11.Euler(...pose.rotation, "ZYX") : object3.rotation.clone(),
|
|
4076
|
+
baseScale: pose ? new import_three11.Vector3(...pose.scale ?? [1, 1, 1]) : object3.scale.clone()
|
|
3449
4077
|
});
|
|
3450
4078
|
}
|
|
3451
4079
|
}
|
|
@@ -3468,12 +4096,14 @@ var init_EntityAnimationPlayer = __esm({
|
|
|
3468
4096
|
this._animation = void 0;
|
|
3469
4097
|
this._time = 0;
|
|
3470
4098
|
}
|
|
4099
|
+
/** Applies the pose at an absolute playback time. Returns `false` when no clip is active. */
|
|
3471
4100
|
setTime(seconds) {
|
|
3472
4101
|
if (!this._animation) return false;
|
|
3473
4102
|
this._time = seconds;
|
|
3474
4103
|
this.apply();
|
|
3475
4104
|
return true;
|
|
3476
4105
|
}
|
|
4106
|
+
/** Advances playback by elapsed seconds multiplied by the speed passed to {@link play}. */
|
|
3477
4107
|
advance(deltaSeconds) {
|
|
3478
4108
|
return this.setTime(this._time + deltaSeconds * this.speed);
|
|
3479
4109
|
}
|
|
@@ -3511,6 +4141,7 @@ var init_InstanceReference = __esm({
|
|
|
3511
4141
|
if (!this.instanceable.isInstanceActive(this.index, this)) throw new MineRenderError("Instance has been removed");
|
|
3512
4142
|
return this.instanceable;
|
|
3513
4143
|
}
|
|
4144
|
+
/** Releases this placement's slot for reuse. Repeated calls have no effect. */
|
|
3514
4145
|
removeFromScene() {
|
|
3515
4146
|
if (this.instanceable.isInstanceActive(this.index, this)) this.instanceable.removeInstanceAt(this.index);
|
|
3516
4147
|
}
|
|
@@ -3523,9 +4154,11 @@ var init_InstanceReference = __esm({
|
|
|
3523
4154
|
setMatrix(matrix) {
|
|
3524
4155
|
this.activeInstanceable.setMatrixAt(this.index, matrix);
|
|
3525
4156
|
}
|
|
4157
|
+
/** Hides or restores this placement while preserving its transform and allocated slot. */
|
|
3526
4158
|
setVisible(visible) {
|
|
3527
4159
|
this.activeInstanceable.setInstanceVisibleAt(this.index, visible);
|
|
3528
4160
|
}
|
|
4161
|
+
/** Updates the supplied transform components. Positions use scene units and rotations use radians. */
|
|
3529
4162
|
setPositionRotationScale(position, rotation, scale) {
|
|
3530
4163
|
this.activeInstanceable.setPositionRotationScaleAt(this.index, position, rotation, scale);
|
|
3531
4164
|
}
|
|
@@ -3594,6 +4227,7 @@ var init_SceneObject = __esm({
|
|
|
3594
4227
|
}
|
|
3595
4228
|
async init() {
|
|
3596
4229
|
}
|
|
4230
|
+
/** Marks ancestor scenes for redraw and emits a `change` event. */
|
|
3597
4231
|
notifyDirty() {
|
|
3598
4232
|
this.traverseAncestors((parent) => {
|
|
3599
4233
|
const scene = parent;
|
|
@@ -3629,15 +4263,17 @@ var init_SceneObject = __esm({
|
|
|
3629
4263
|
return obj;
|
|
3630
4264
|
}
|
|
3631
4265
|
/**
|
|
3632
|
-
*
|
|
4266
|
+
* Finds a named part, such as `head`, without the internal `group:` prefix.
|
|
4267
|
+
* Returns `undefined` when the group does not exist.
|
|
3633
4268
|
*/
|
|
3634
4269
|
getGroupByName(name) {
|
|
3635
4270
|
return this.getObjectByName(`group:${name}`);
|
|
3636
4271
|
}
|
|
3637
4272
|
/**
|
|
3638
|
-
*
|
|
3639
|
-
* @param name name
|
|
3640
|
-
* @param visible
|
|
4273
|
+
* Changes a named group's visibility and requests a redraw.
|
|
4274
|
+
* @param name - Part name without the `group:` prefix.
|
|
4275
|
+
* @param visible - Visibility to assign. Omit it to toggle the current value.
|
|
4276
|
+
* @returns The resulting visibility, or `false` if the group does not exist.
|
|
3641
4277
|
*/
|
|
3642
4278
|
toggleGroupVisibility(name, visible) {
|
|
3643
4279
|
return this.toggleObjectVisibility(this.getGroupByName(name), visible);
|
|
@@ -3688,15 +4324,16 @@ var init_SceneObject = __esm({
|
|
|
3688
4324
|
return mesh;
|
|
3689
4325
|
}
|
|
3690
4326
|
/**
|
|
3691
|
-
*
|
|
4327
|
+
* Finds a named mesh without the internal `mesh:` prefix, or returns `undefined`.
|
|
3692
4328
|
*/
|
|
3693
4329
|
getMeshByName(name) {
|
|
3694
4330
|
return this.getObjectByName(`mesh:${name}`);
|
|
3695
4331
|
}
|
|
3696
4332
|
/**
|
|
3697
|
-
*
|
|
3698
|
-
* @param name name
|
|
3699
|
-
* @param visible
|
|
4333
|
+
* Changes a named mesh's visibility and requests a redraw.
|
|
4334
|
+
* @param name - Mesh name without the `mesh:` prefix.
|
|
4335
|
+
* @param visible - Visibility to assign. Omit it to toggle the current value.
|
|
4336
|
+
* @returns The resulting visibility, or `false` if the mesh does not exist.
|
|
3700
4337
|
*/
|
|
3701
4338
|
toggleMeshVisibility(name, visible) {
|
|
3702
4339
|
return this.toggleObjectVisibility(this.getMeshByName(name), visible);
|
|
@@ -3742,12 +4379,14 @@ var init_SceneObject = __esm({
|
|
|
3742
4379
|
get isInstanced() {
|
|
3743
4380
|
return this._isInstanced;
|
|
3744
4381
|
}
|
|
4382
|
+
/** Number of live placements, excluding released instance slots. */
|
|
3745
4383
|
get instanceCounter() {
|
|
3746
4384
|
return this._instanceCounter;
|
|
3747
4385
|
}
|
|
3748
4386
|
constructInstanceReference(i) {
|
|
3749
4387
|
return new InstanceReference(this, i);
|
|
3750
4388
|
}
|
|
4389
|
+
/** Allocates a placement of this instanced object, reusing a released slot when available. */
|
|
3751
4390
|
nextInstance() {
|
|
3752
4391
|
const mesh = this.instanceMesh;
|
|
3753
4392
|
if (!mesh) throw new MineRenderError("Object is not instanced");
|
|
@@ -3792,10 +4431,10 @@ var init_SceneObject = __esm({
|
|
|
3792
4431
|
return new import_three12.InstancedBufferAttribute(array, attribute.itemSize, attribute.normalized, attribute.meshPerAttribute).setUsage(attribute.usage);
|
|
3793
4432
|
};
|
|
3794
4433
|
const matrix = grow(mesh.instanceMatrix, 0);
|
|
3795
|
-
const
|
|
4434
|
+
const color2 = mesh.instanceColor ? grow(mesh.instanceColor, 1) : null;
|
|
3796
4435
|
mesh.dispose();
|
|
3797
4436
|
mesh.instanceMatrix = matrix;
|
|
3798
|
-
mesh.instanceColor =
|
|
4437
|
+
mesh.instanceColor = color2;
|
|
3799
4438
|
}
|
|
3800
4439
|
*activeInstanceIndices() {
|
|
3801
4440
|
if (this.instanceMesh) {
|
|
@@ -3894,6 +4533,7 @@ var init_SceneObject = __esm({
|
|
|
3894
4533
|
vector.setFromMatrixScale(matrix);
|
|
3895
4534
|
return vector;
|
|
3896
4535
|
}
|
|
4536
|
+
/** Updates supplied transform components. For a shared model, affects every live instance. */
|
|
3897
4537
|
setPositionRotationScale(position, rotation, scale) {
|
|
3898
4538
|
if (this.isInstanced) {
|
|
3899
4539
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -3912,6 +4552,7 @@ var init_SceneObject = __esm({
|
|
|
3912
4552
|
}
|
|
3913
4553
|
this.notifyDirty();
|
|
3914
4554
|
}
|
|
4555
|
+
/** Sets position in scene units. For a shared model, updates every live instance. */
|
|
3915
4556
|
setPosition(position) {
|
|
3916
4557
|
if (this.isInstanced) {
|
|
3917
4558
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -3929,6 +4570,7 @@ var init_SceneObject = __esm({
|
|
|
3929
4570
|
return this.position;
|
|
3930
4571
|
}
|
|
3931
4572
|
}
|
|
4573
|
+
/** Sets rotation in radians. For a shared model, updates every live instance. */
|
|
3932
4574
|
setRotation(rotation) {
|
|
3933
4575
|
if (this.isInstanced) {
|
|
3934
4576
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -3946,6 +4588,7 @@ var init_SceneObject = __esm({
|
|
|
3946
4588
|
return this.rotation;
|
|
3947
4589
|
}
|
|
3948
4590
|
}
|
|
4591
|
+
/** Sets scale factors. For a shared model, updates every live instance. */
|
|
3949
4592
|
setScale(scale) {
|
|
3950
4593
|
if (this.isInstanced) {
|
|
3951
4594
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -3964,15 +4607,15 @@ var init_SceneObject = __esm({
|
|
|
3964
4607
|
}
|
|
3965
4608
|
}
|
|
3966
4609
|
//</editor-fold>
|
|
3967
|
-
toggleObjectVisibility(
|
|
3968
|
-
if (
|
|
4610
|
+
toggleObjectVisibility(object3, visible) {
|
|
4611
|
+
if (object3) {
|
|
3969
4612
|
if (typeof visible !== "undefined") {
|
|
3970
|
-
|
|
4613
|
+
object3.visible = visible;
|
|
3971
4614
|
} else {
|
|
3972
|
-
|
|
4615
|
+
object3.visible = !object3.visible;
|
|
3973
4616
|
}
|
|
3974
4617
|
this.notifyDirty();
|
|
3975
|
-
return
|
|
4618
|
+
return object3.visible;
|
|
3976
4619
|
}
|
|
3977
4620
|
this.notifyDirty();
|
|
3978
4621
|
return false;
|
|
@@ -3983,6 +4626,7 @@ var init_SceneObject = __esm({
|
|
|
3983
4626
|
this.notifyDirty();
|
|
3984
4627
|
super.dispose();
|
|
3985
4628
|
}
|
|
4629
|
+
/** Releases instance slots and removes children, disposing children that implement Disposable. */
|
|
3986
4630
|
disposeAndRemoveAllChildren() {
|
|
3987
4631
|
if (this.instanceMesh) {
|
|
3988
4632
|
if (this._scene) this._scene.stats.instanceCount -= this.instanceReferences.size;
|
|
@@ -4005,6 +4649,7 @@ var init_SceneObject = __esm({
|
|
|
4005
4649
|
}
|
|
4006
4650
|
this.notifyDirty();
|
|
4007
4651
|
}
|
|
4652
|
+
/** Detaches the object from its parent and requests a redraw. */
|
|
4008
4653
|
removeFromScene() {
|
|
4009
4654
|
this.notifyDirty();
|
|
4010
4655
|
this.removeFromParent();
|
|
@@ -4160,12 +4805,15 @@ var init_EntityObject = __esm({
|
|
|
4160
4805
|
if (this.animationPlayers.length) this.notifyDirty();
|
|
4161
4806
|
}
|
|
4162
4807
|
//</editor-fold>
|
|
4808
|
+
/** Finds a selected draw by its layer key, such as `main` or `main#2`, or returns `undefined`. */
|
|
4163
4809
|
getLayerGroup(name) {
|
|
4164
4810
|
return super.getGroupByName(`layer:${name}`);
|
|
4165
4811
|
}
|
|
4812
|
+
/** Finds a part name without `group:`. Supply `layerName` to restrict the search to one draw. */
|
|
4166
4813
|
getGroupByName(name, layerName) {
|
|
4167
4814
|
return layerName === void 0 ? super.getGroupByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`group:${name}`);
|
|
4168
4815
|
}
|
|
4816
|
+
/** Finds a mesh name without `mesh:`. Supply `layerName` to restrict the search to one draw. */
|
|
4169
4817
|
getMeshByName(name, layerName) {
|
|
4170
4818
|
return layerName === void 0 ? super.getMeshByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`mesh:${name}`);
|
|
4171
4819
|
}
|
|
@@ -4267,8 +4915,8 @@ var init_EntityObject = __esm({
|
|
|
4267
4915
|
this.scrollMaterials.push({ material: mat, offset: mat.map.offset, speed: scroll });
|
|
4268
4916
|
}
|
|
4269
4917
|
}
|
|
4270
|
-
this.getLayerGroup(name)?.traverse((
|
|
4271
|
-
if (isMesh(
|
|
4918
|
+
this.getLayerGroup(name)?.traverse((object3) => {
|
|
4919
|
+
if (isMesh(object3)) object3.material = mat;
|
|
4272
4920
|
});
|
|
4273
4921
|
}));
|
|
4274
4922
|
if (!this.children.length) {
|
|
@@ -4598,8 +5246,8 @@ function toTrianglesDrawMode(geometry, drawMode) {
|
|
|
4598
5246
|
return geometry;
|
|
4599
5247
|
}
|
|
4600
5248
|
}
|
|
4601
|
-
function computeMorphedAttributes(
|
|
4602
|
-
if (
|
|
5249
|
+
function computeMorphedAttributes(object3) {
|
|
5250
|
+
if (object3.geometry.isBufferGeometry !== true) {
|
|
4603
5251
|
console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
|
|
4604
5252
|
return null;
|
|
4605
5253
|
}
|
|
@@ -4612,11 +5260,11 @@ function computeMorphedAttributes(object) {
|
|
|
4612
5260
|
const _morphA = new import_three22.Vector3();
|
|
4613
5261
|
const _morphB = new import_three22.Vector3();
|
|
4614
5262
|
const _morphC = new import_three22.Vector3();
|
|
4615
|
-
function _calculateMorphedAttributeData(
|
|
5263
|
+
function _calculateMorphedAttributeData(object4, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
|
|
4616
5264
|
_vA.fromBufferAttribute(attribute, a2);
|
|
4617
5265
|
_vB.fromBufferAttribute(attribute, b2);
|
|
4618
5266
|
_vC.fromBufferAttribute(attribute, c2);
|
|
4619
|
-
const morphInfluences =
|
|
5267
|
+
const morphInfluences = object4.morphTargetInfluences;
|
|
4620
5268
|
if (material2.morphTargets && morphAttribute && morphInfluences) {
|
|
4621
5269
|
_morphA.set(0, 0, 0);
|
|
4622
5270
|
_morphB.set(0, 0, 0);
|
|
@@ -4642,10 +5290,10 @@ function computeMorphedAttributes(object) {
|
|
|
4642
5290
|
_vB.add(_morphB);
|
|
4643
5291
|
_vC.add(_morphC);
|
|
4644
5292
|
}
|
|
4645
|
-
if (
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
5293
|
+
if (object4.isSkinnedMesh) {
|
|
5294
|
+
object4.boneTransform(a2, _vA);
|
|
5295
|
+
object4.boneTransform(b2, _vB);
|
|
5296
|
+
object4.boneTransform(c2, _vC);
|
|
4649
5297
|
}
|
|
4650
5298
|
modifiedAttributeArray[a2 * 3 + 0] = _vA.x;
|
|
4651
5299
|
modifiedAttributeArray[a2 * 3 + 1] = _vA.y;
|
|
@@ -4657,8 +5305,8 @@ function computeMorphedAttributes(object) {
|
|
|
4657
5305
|
modifiedAttributeArray[c2 * 3 + 1] = _vC.y;
|
|
4658
5306
|
modifiedAttributeArray[c2 * 3 + 2] = _vC.z;
|
|
4659
5307
|
}
|
|
4660
|
-
const geometry =
|
|
4661
|
-
const material =
|
|
5308
|
+
const geometry = object3.geometry;
|
|
5309
|
+
const material = object3.material;
|
|
4662
5310
|
let a, b, c;
|
|
4663
5311
|
const index = geometry.index;
|
|
4664
5312
|
const positionAttribute = geometry.attributes.position;
|
|
@@ -4685,7 +5333,7 @@ function computeMorphedAttributes(object) {
|
|
|
4685
5333
|
b = index.getX(j + 1);
|
|
4686
5334
|
c = index.getX(j + 2);
|
|
4687
5335
|
_calculateMorphedAttributeData(
|
|
4688
|
-
|
|
5336
|
+
object3,
|
|
4689
5337
|
groupMaterial,
|
|
4690
5338
|
positionAttribute,
|
|
4691
5339
|
morphPosition,
|
|
@@ -4696,7 +5344,7 @@ function computeMorphedAttributes(object) {
|
|
|
4696
5344
|
modifiedPosition
|
|
4697
5345
|
);
|
|
4698
5346
|
_calculateMorphedAttributeData(
|
|
4699
|
-
|
|
5347
|
+
object3,
|
|
4700
5348
|
groupMaterial,
|
|
4701
5349
|
normalAttribute,
|
|
4702
5350
|
morphNormal,
|
|
@@ -4716,7 +5364,7 @@ function computeMorphedAttributes(object) {
|
|
|
4716
5364
|
b = index.getX(i + 1);
|
|
4717
5365
|
c = index.getX(i + 2);
|
|
4718
5366
|
_calculateMorphedAttributeData(
|
|
4719
|
-
|
|
5367
|
+
object3,
|
|
4720
5368
|
material,
|
|
4721
5369
|
positionAttribute,
|
|
4722
5370
|
morphPosition,
|
|
@@ -4727,7 +5375,7 @@ function computeMorphedAttributes(object) {
|
|
|
4727
5375
|
modifiedPosition
|
|
4728
5376
|
);
|
|
4729
5377
|
_calculateMorphedAttributeData(
|
|
4730
|
-
|
|
5378
|
+
object3,
|
|
4731
5379
|
material,
|
|
4732
5380
|
normalAttribute,
|
|
4733
5381
|
morphNormal,
|
|
@@ -4751,7 +5399,7 @@ function computeMorphedAttributes(object) {
|
|
|
4751
5399
|
b = j + 1;
|
|
4752
5400
|
c = j + 2;
|
|
4753
5401
|
_calculateMorphedAttributeData(
|
|
4754
|
-
|
|
5402
|
+
object3,
|
|
4755
5403
|
groupMaterial,
|
|
4756
5404
|
positionAttribute,
|
|
4757
5405
|
morphPosition,
|
|
@@ -4762,7 +5410,7 @@ function computeMorphedAttributes(object) {
|
|
|
4762
5410
|
modifiedPosition
|
|
4763
5411
|
);
|
|
4764
5412
|
_calculateMorphedAttributeData(
|
|
4765
|
-
|
|
5413
|
+
object3,
|
|
4766
5414
|
groupMaterial,
|
|
4767
5415
|
normalAttribute,
|
|
4768
5416
|
morphNormal,
|
|
@@ -4782,7 +5430,7 @@ function computeMorphedAttributes(object) {
|
|
|
4782
5430
|
b = i + 1;
|
|
4783
5431
|
c = i + 2;
|
|
4784
5432
|
_calculateMorphedAttributeData(
|
|
4785
|
-
|
|
5433
|
+
object3,
|
|
4786
5434
|
material,
|
|
4787
5435
|
positionAttribute,
|
|
4788
5436
|
morphPosition,
|
|
@@ -4793,7 +5441,7 @@ function computeMorphedAttributes(object) {
|
|
|
4793
5441
|
modifiedPosition
|
|
4794
5442
|
);
|
|
4795
5443
|
_calculateMorphedAttributeData(
|
|
4796
|
-
|
|
5444
|
+
object3,
|
|
4797
5445
|
material,
|
|
4798
5446
|
normalAttribute,
|
|
4799
5447
|
morphNormal,
|
|
@@ -4829,6 +5477,7 @@ var init_ModelCulling = __esm({
|
|
|
4829
5477
|
import_three23 = require("three");
|
|
4830
5478
|
init_CubeFace();
|
|
4831
5479
|
ModelCulling = class {
|
|
5480
|
+
/** Rotates a world-direction bit mask into model space. Bits follow {@link CUBE_FACES}. */
|
|
4832
5481
|
static toLocalMask(worldMask, rotation) {
|
|
4833
5482
|
let localMask = 0;
|
|
4834
5483
|
for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
|
|
@@ -4839,6 +5488,7 @@ var init_ModelCulling = __esm({
|
|
|
4839
5488
|
}
|
|
4840
5489
|
return localMask;
|
|
4841
5490
|
}
|
|
5491
|
+
/** Whether a model fills one block with opaque faces and can hide adjacent block faces. */
|
|
4842
5492
|
static isOpaqueFullCube(atlas) {
|
|
4843
5493
|
if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
|
|
4844
5494
|
const element = atlas.model.elements[0];
|
|
@@ -4871,9 +5521,11 @@ var init_SpecialItems = __esm({
|
|
|
4871
5521
|
import_three24 = require("three");
|
|
4872
5522
|
init_AssetKey();
|
|
4873
5523
|
init_Entities();
|
|
5524
|
+
init_BannerPatterns();
|
|
5525
|
+
init_ModelTextures();
|
|
4874
5526
|
SpecialItems = class {
|
|
4875
5527
|
/** Resolves the static entity parts drawn by a special item renderer, in model units. */
|
|
4876
|
-
static async getParts(special, root) {
|
|
5528
|
+
static async getParts(special, root, components = {}) {
|
|
4877
5529
|
const texture = (id, directory) => {
|
|
4878
5530
|
const key = AssetKey.parse("textures", id);
|
|
4879
5531
|
return new AssetKey(
|
|
@@ -4886,13 +5538,77 @@ var init_SpecialItems = __esm({
|
|
|
4886
5538
|
root
|
|
4887
5539
|
);
|
|
4888
5540
|
};
|
|
4889
|
-
const load = async (id, textureKey, transform, rotations = {}) => {
|
|
5541
|
+
const load = async (id, textureKey, transform, rotations = {}, layers = ["main"]) => {
|
|
4890
5542
|
const key = new AssetKey("minecraft", id, void 0, void 0, "entity-models", ".json", root);
|
|
4891
|
-
const model = await Entities.getEntity(key, textureKey, { layer:
|
|
5543
|
+
const model = await Entities.getEntity(key, textureKey, layers.length === 1 ? { layer: layers[0] } : {
|
|
5544
|
+
layers,
|
|
5545
|
+
textures: textureKey ? Object.fromEntries(layers.map((name) => [name, textureKey])) : void 0
|
|
5546
|
+
});
|
|
4892
5547
|
if (!model) throw new Error(`Missing entity model minecraft:${id} for special item ${special.type}`);
|
|
4893
5548
|
return { model, transform, rotations };
|
|
4894
5549
|
};
|
|
4895
5550
|
switch (special.type) {
|
|
5551
|
+
case "minecraft:trident":
|
|
5552
|
+
case "trident":
|
|
5553
|
+
case "minecraft:conduit":
|
|
5554
|
+
case "conduit": {
|
|
5555
|
+
const trident = special.type === "trident" || special.type === "minecraft:trident";
|
|
5556
|
+
const part = await load(
|
|
5557
|
+
trident ? "trident" : "conduit",
|
|
5558
|
+
texture(trident ? "trident" : "conduit/base", ""),
|
|
5559
|
+
trident ? new import_three24.Matrix4().makeScale(1, -1, -1) : new import_three24.Matrix4().makeTranslation(8, 8, 8),
|
|
5560
|
+
{},
|
|
5561
|
+
trident ? ["main"] : ["shell"]
|
|
5562
|
+
);
|
|
5563
|
+
part.model = { ...part.model, render: "solid", layers: part.model.layers && Object.fromEntries(
|
|
5564
|
+
Object.entries(part.model.layers).map(([name, layer]) => [name, { ...layer, render: "solid" }])
|
|
5565
|
+
) };
|
|
5566
|
+
return [part];
|
|
5567
|
+
}
|
|
5568
|
+
case "minecraft:banner":
|
|
5569
|
+
case "banner":
|
|
5570
|
+
case "minecraft:shield":
|
|
5571
|
+
case "shield": {
|
|
5572
|
+
const banner = special.type === "banner" || special.type === "minecraft:banner";
|
|
5573
|
+
const color2 = banner ? BannerPatterns.getColor(special.color) : components["minecraft:base_color"] === void 0 ? void 0 : BannerPatterns.getColor(components["minecraft:base_color"]);
|
|
5574
|
+
const patterns = await BannerPatterns.getLayers(components["minecraft:banner_patterns"], banner ? "banner" : "shield", root);
|
|
5575
|
+
const patterned = banner || color2 !== void 0 || patterns.length > 0;
|
|
5576
|
+
const baseTexture = texture(banner ? "banner_base" : patterned ? "shield_base" : "shield_base_nopattern", "");
|
|
5577
|
+
const transform = banner ? new import_three24.Matrix4().makeTranslation(8, 0, 8).multiply(new import_three24.Matrix4().makeScale(2 / 3, -2 / 3, -2 / 3)) : new import_three24.Matrix4().makeScale(1, -1, -1);
|
|
5578
|
+
const part = await load(banner ? "standing_banner" : "shield", baseTexture, transform, {}, banner ? ["main", "flag"] : ["main"]);
|
|
5579
|
+
const layers = Object.fromEntries(Object.entries(part.model.layers ?? { main: part.model }).map(([name, layer]) => [name, { ...layer, render: "solid" }]));
|
|
5580
|
+
let overlay2;
|
|
5581
|
+
if (banner) {
|
|
5582
|
+
const flagLayer = layers.flag;
|
|
5583
|
+
const flag = flagLayer?.layer.root.children.flag;
|
|
5584
|
+
if (!flag) throw new Error("Banner entity model is missing its flag layer");
|
|
5585
|
+
overlay2 = { ...flagLayer.layer, root: { ...flagLayer.layer.root, children: {
|
|
5586
|
+
...flagLayer.layer.root.children,
|
|
5587
|
+
flag: { ...flag, pose: { ...flag.pose, rotation: [-25e-4 * Math.PI, flag.pose.rotation[1], flag.pose.rotation[2]] } }
|
|
5588
|
+
} } };
|
|
5589
|
+
layers.flag = { ...flagLayer, layer: overlay2 };
|
|
5590
|
+
} else {
|
|
5591
|
+
const main = layers.main.layer;
|
|
5592
|
+
const plate = main.root.children.plate;
|
|
5593
|
+
if (!plate) throw new Error("Shield entity model is missing its plate");
|
|
5594
|
+
overlay2 = { ...main, root: { ...main.root, cubes: [], children: { plate } } };
|
|
5595
|
+
}
|
|
5596
|
+
const tints = {};
|
|
5597
|
+
if (patterned) {
|
|
5598
|
+
const draws = [{ texture: texture(`${banner ? "banner" : "shield"}/base`, ""), color: color2 ?? DYE_COLORS.white }, ...patterns];
|
|
5599
|
+
draws.forEach((draw, index) => {
|
|
5600
|
+
const name = index === 0 ? "pattern_base" : `pattern_${index - 1}`;
|
|
5601
|
+
layers[name] = { key: draw.texture, texture: draw.texture, layer: overlay2, render: "no_outline", tint: name };
|
|
5602
|
+
tints[name] = draw.color;
|
|
5603
|
+
});
|
|
5604
|
+
}
|
|
5605
|
+
await Promise.all([...new Map(Object.values(layers).map((layer) => [layer.texture.serialize(), layer.texture])).values()].map(async (key) => {
|
|
5606
|
+
if (!await ModelTextures.preload(key)) throw new Error(`Missing special item texture ${key.toNamespacedString()}`);
|
|
5607
|
+
}));
|
|
5608
|
+
part.model = { ...part.model, ...layers.main, layers };
|
|
5609
|
+
part.tints = tints;
|
|
5610
|
+
return [part];
|
|
5611
|
+
}
|
|
4896
5612
|
case "minecraft:chest":
|
|
4897
5613
|
case "chest": {
|
|
4898
5614
|
const angle = -(special.openness ?? 0) * Math.PI / 2;
|
|
@@ -4903,6 +5619,26 @@ var init_SpecialItems = __esm({
|
|
|
4903
5619
|
chest.model = { ...chest.model, render: "solid", layers: void 0 };
|
|
4904
5620
|
return [chest];
|
|
4905
5621
|
}
|
|
5622
|
+
case "minecraft:shulker_box":
|
|
5623
|
+
case "shulker_box": {
|
|
5624
|
+
const openness = special.openness === void 0 ? 0 : special.openness;
|
|
5625
|
+
const orientation = special.orientation === void 0 ? "up" : special.orientation;
|
|
5626
|
+
const directions = {
|
|
5627
|
+
down: [Math.PI, 0, 0],
|
|
5628
|
+
up: [0, 0, 0],
|
|
5629
|
+
north: [Math.PI / 2, 0, Math.PI],
|
|
5630
|
+
south: [Math.PI / 2, 0, 0],
|
|
5631
|
+
west: [Math.PI / 2, 0, Math.PI / 2],
|
|
5632
|
+
east: [Math.PI / 2, 0, -Math.PI / 2]
|
|
5633
|
+
};
|
|
5634
|
+
if (!Object.prototype.hasOwnProperty.call(directions, orientation)) throw new Error(`Unsupported shulker-box orientation ${orientation}`);
|
|
5635
|
+
if (typeof openness !== "number" || !Number.isFinite(openness)) throw new Error("Shulker-box openness must be finite");
|
|
5636
|
+
if (typeof special.texture !== "string" || !special.texture) throw new Error("Shulker-box texture is required");
|
|
5637
|
+
const transform = new import_three24.Matrix4().makeTranslation(8, 8, 8).multiply(new import_three24.Matrix4().makeScale(0.9995, 0.9995, 0.9995)).multiply(new import_three24.Matrix4().makeRotationFromEuler(new import_three24.Euler(...directions[orientation], "XYZ"))).multiply(new import_three24.Matrix4().makeScale(1, -1, -1)).multiply(new import_three24.Matrix4().makeTranslation(0, -16, 0));
|
|
5638
|
+
const box = await load("shulker_box", texture(special.texture, "shulker"), transform, { lid: [0, openness * Math.PI * 1.5, 0] });
|
|
5639
|
+
box.positions = { lid: [0, 24 - openness * 8, 0] };
|
|
5640
|
+
return [box];
|
|
5641
|
+
}
|
|
4906
5642
|
case "minecraft:bed":
|
|
4907
5643
|
case "bed": {
|
|
4908
5644
|
const transform = new import_three24.Matrix4().makeTranslation(0, 9, 0).multiply(new import_three24.Matrix4().makeRotationX(Math.PI / 2)).multiply(new import_three24.Matrix4().makeTranslation(8, 8, 8)).multiply(new import_three24.Matrix4().makeRotationZ(Math.PI)).multiply(new import_three24.Matrix4().makeTranslation(-8, -8, -8));
|
|
@@ -5017,6 +5753,7 @@ var init_ItemTints = __esm({
|
|
|
5017
5753
|
static async get(model, tints) {
|
|
5018
5754
|
const sources = model.tints;
|
|
5019
5755
|
if (!sources?.length) return tints;
|
|
5756
|
+
const components = model.components ?? {};
|
|
5020
5757
|
const colors = { ...tints };
|
|
5021
5758
|
await Promise.all(sources.map(async (source, index) => {
|
|
5022
5759
|
if (colors[index] !== void 0) return;
|
|
@@ -5031,27 +5768,71 @@ var init_ItemTints = __esm({
|
|
|
5031
5768
|
break;
|
|
5032
5769
|
case "dye":
|
|
5033
5770
|
case "minecraft:dye":
|
|
5771
|
+
colors[index] = this.colorOrDefault(components["minecraft:dyed_color"], source.default, true);
|
|
5772
|
+
break;
|
|
5034
5773
|
case "potion":
|
|
5035
|
-
case "minecraft:potion":
|
|
5774
|
+
case "minecraft:potion": {
|
|
5775
|
+
const value = typeof components["minecraft:potion_contents"] === "string" ? void 0 : this.componentField(components, "potion_contents", "custom_color");
|
|
5776
|
+
colors[index] = this.colorOrDefault(value, source.default);
|
|
5777
|
+
break;
|
|
5778
|
+
}
|
|
5036
5779
|
case "map_color":
|
|
5037
5780
|
case "minecraft:map_color":
|
|
5781
|
+
colors[index] = this.colorOrDefault(components["minecraft:map_color"], source.default);
|
|
5782
|
+
break;
|
|
5038
5783
|
case "firework":
|
|
5039
|
-
case "minecraft:firework":
|
|
5784
|
+
case "minecraft:firework": {
|
|
5785
|
+
const values = this.componentField(components, "firework_explosion", "colors");
|
|
5786
|
+
if (values !== void 0 && !Array.isArray(values)) throw new MineRenderError("Item tint firework_explosion.colors must be an array");
|
|
5787
|
+
if (!values?.length) {
|
|
5788
|
+
colors[index] = this.rgb(source.default);
|
|
5789
|
+
break;
|
|
5790
|
+
}
|
|
5791
|
+
const rgb = values.map((value) => this.componentColor(value));
|
|
5792
|
+
const red = Math.floor(rgb.reduce((sum, color2) => sum + (color2 >> 16 & 255), 0) / rgb.length);
|
|
5793
|
+
const green = Math.floor(rgb.reduce((sum, color2) => sum + (color2 >> 8 & 255), 0) / rgb.length);
|
|
5794
|
+
const blue = Math.floor(rgb.reduce((sum, color2) => sum + (color2 & 255), 0) / rgb.length);
|
|
5795
|
+
colors[index] = red << 16 | green << 8 | blue;
|
|
5796
|
+
break;
|
|
5797
|
+
}
|
|
5040
5798
|
case "team":
|
|
5041
5799
|
case "minecraft:team":
|
|
5042
|
-
case "custom_model_data":
|
|
5043
|
-
case "minecraft:custom_model_data":
|
|
5044
5800
|
colors[index] = this.rgb(source.default);
|
|
5045
5801
|
break;
|
|
5802
|
+
case "custom_model_data":
|
|
5803
|
+
case "minecraft:custom_model_data": {
|
|
5804
|
+
const colorIndex = source.index ?? 0;
|
|
5805
|
+
if (!Number.isSafeInteger(colorIndex) || colorIndex < 0) throw new MineRenderError("Item tint custom_model_data index must be a nonnegative integer");
|
|
5806
|
+
const values = this.componentField(components, "custom_model_data", "colors");
|
|
5807
|
+
if (values !== void 0 && !Array.isArray(values)) throw new MineRenderError("Item tint custom_model_data.colors must be an array");
|
|
5808
|
+
colors[index] = this.colorOrDefault(values?.[colorIndex], source.default, true);
|
|
5809
|
+
break;
|
|
5810
|
+
}
|
|
5046
5811
|
default:
|
|
5047
5812
|
throw new MineRenderError(`Unsupported item tint source ${source.type}`);
|
|
5048
5813
|
}
|
|
5049
5814
|
}));
|
|
5050
5815
|
return colors;
|
|
5051
5816
|
}
|
|
5052
|
-
static
|
|
5053
|
-
|
|
5054
|
-
|
|
5817
|
+
static componentField(components, id, field) {
|
|
5818
|
+
const value = components[`minecraft:${id}`];
|
|
5819
|
+
if (value === void 0) return void 0;
|
|
5820
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new MineRenderError(`Item tint ${id} must be an object`);
|
|
5821
|
+
return value[field];
|
|
5822
|
+
}
|
|
5823
|
+
static colorOrDefault(value, fallback, allowTriple = false) {
|
|
5824
|
+
return value === void 0 ? this.rgb(fallback) : this.componentColor(value, allowTriple);
|
|
5825
|
+
}
|
|
5826
|
+
static componentColor(value, allowTriple = false) {
|
|
5827
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= -2147483648 && value <= 2147483647) return this.rgb(value);
|
|
5828
|
+
if (allowTriple && Array.isArray(value) && value.length === 3 && value.every((channel) => typeof channel === "number" && Number.isFinite(channel))) {
|
|
5829
|
+
return this.rgb(value) & 16777215;
|
|
5830
|
+
}
|
|
5831
|
+
throw new MineRenderError(`Item tint component color must be a packed integer${allowTriple ? " or RGB triple" : ""}`);
|
|
5832
|
+
}
|
|
5833
|
+
static rgb(color2) {
|
|
5834
|
+
if (!Array.isArray(color2)) return color2 & 16777215;
|
|
5835
|
+
const [red, green, blue] = color2.map((channel) => Math.floor(Math.fround(Math.fround(channel) * 255)) & 255);
|
|
5055
5836
|
return red << 16 | green << 8 | blue;
|
|
5056
5837
|
}
|
|
5057
5838
|
};
|
|
@@ -5088,29 +5869,52 @@ var init_ModelObject = __esm({
|
|
|
5088
5869
|
this.originalModel = originalModel;
|
|
5089
5870
|
this.isModelObject = true;
|
|
5090
5871
|
this.specialMaterials = /* @__PURE__ */ new Map();
|
|
5872
|
+
this.geometries = /* @__PURE__ */ new Set();
|
|
5091
5873
|
this.meshesCreated = false;
|
|
5092
5874
|
this.options = (0, import_ts_deepmerge5.default)({}, _ModelObject.DEFAULT_OPTIONS, options ?? {});
|
|
5093
|
-
if (originalModel.special) this.options.instanceMeshes = false;
|
|
5875
|
+
if (originalModel.special || originalModel.parts) this.options.instanceMeshes = false;
|
|
5094
5876
|
if (this.options.tints) this.options.tints = { ...this.options.tints };
|
|
5095
5877
|
this.addEventListener("added", () => this.updateAnimationSubscription());
|
|
5096
5878
|
this.addEventListener("removed", () => this.updateAnimationSubscription());
|
|
5097
5879
|
}
|
|
5098
5880
|
async init() {
|
|
5881
|
+
const parts = this.originalModel.parts;
|
|
5882
|
+
if (parts) {
|
|
5883
|
+
try {
|
|
5884
|
+
for (const part of parts) {
|
|
5885
|
+
const object3 = new _ModelObject(part, { ...this.options, instanceMeshes: false });
|
|
5886
|
+
this.add(object3);
|
|
5887
|
+
await object3.init();
|
|
5888
|
+
}
|
|
5889
|
+
let order = 0;
|
|
5890
|
+
this.iterateAllMeshes((mesh) => {
|
|
5891
|
+
mesh.renderOrder = order++;
|
|
5892
|
+
for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
|
|
5893
|
+
material.transparent = true;
|
|
5894
|
+
}
|
|
5895
|
+
});
|
|
5896
|
+
} catch (error) {
|
|
5897
|
+
this.disposeAndRemoveAllChildren();
|
|
5898
|
+
throw error;
|
|
5899
|
+
}
|
|
5900
|
+
this.notifyDirty();
|
|
5901
|
+
return;
|
|
5902
|
+
}
|
|
5099
5903
|
this.options.tints = await ItemTints.get(this.originalModel, this.options.tints);
|
|
5100
5904
|
const special = this.originalModel.special;
|
|
5101
5905
|
if (special) {
|
|
5102
|
-
const
|
|
5906
|
+
const parts2 = await SpecialItems.getParts(special, this.originalModel.key?.root, this.originalModel.components);
|
|
5103
5907
|
const transform = this.options.displayPosition ? DisplayTransforms.getMatrix(this.originalModel.display, this.options.displayPosition) : new import_three25.Matrix4();
|
|
5104
5908
|
transform.multiply(new import_three25.Matrix4().makeTranslation(-8, -8, -8));
|
|
5105
5909
|
try {
|
|
5106
|
-
for (const part of
|
|
5107
|
-
const
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
this.add(
|
|
5112
|
-
await
|
|
5113
|
-
|
|
5910
|
+
for (const part of parts2) {
|
|
5911
|
+
const object3 = new EntityObject(part.model, { flip: false, wireframe: this.options.wireframe, tints: part.tints });
|
|
5912
|
+
object3.matrix.copy(transform).multiply(part.transform);
|
|
5913
|
+
object3.matrixWorldNeedsUpdate = true;
|
|
5914
|
+
object3.matrixAutoUpdate = false;
|
|
5915
|
+
this.add(object3);
|
|
5916
|
+
await object3.init();
|
|
5917
|
+
object3.iterateAllMeshes((mesh) => {
|
|
5114
5918
|
const source = mesh.material;
|
|
5115
5919
|
let material = this.specialMaterials.get(source);
|
|
5116
5920
|
if (!material) {
|
|
@@ -5121,7 +5925,10 @@ var init_ModelObject = __esm({
|
|
|
5121
5925
|
mesh.material = material;
|
|
5122
5926
|
});
|
|
5123
5927
|
for (const [name, rotation] of Object.entries(part.rotations)) {
|
|
5124
|
-
|
|
5928
|
+
object3.getGroupByName(name)?.rotation.set(...rotation, "ZYX");
|
|
5929
|
+
}
|
|
5930
|
+
for (const [name, position] of Object.entries(part.positions ?? {})) {
|
|
5931
|
+
object3.getGroupByName(name)?.position.set(...position);
|
|
5125
5932
|
}
|
|
5126
5933
|
}
|
|
5127
5934
|
} catch (error) {
|
|
@@ -5154,6 +5961,7 @@ var init_ModelObject = __esm({
|
|
|
5154
5961
|
if (this.atlas.model.elements) {
|
|
5155
5962
|
this.atlas.model.elements?.forEach((el) => {
|
|
5156
5963
|
const elGeo = this._getBoxGeometryFromElement(el).clone();
|
|
5964
|
+
this.geometries.add(elGeo);
|
|
5157
5965
|
if (this.options.cullMask) {
|
|
5158
5966
|
const indices = Array.from(elGeo.getIndex().array);
|
|
5159
5967
|
elGeo.setIndex(indices.filter((_, index) => {
|
|
@@ -5166,6 +5974,7 @@ var init_ModelObject = __esm({
|
|
|
5166
5974
|
if (this.options.uvLockRotation) {
|
|
5167
5975
|
UVMapper.lockUvs(elGeo, el.faces, this.atlas, new import_three25.Euler(...this.options.uvLockRotation));
|
|
5168
5976
|
}
|
|
5977
|
+
UVMapper.setAtlasUvBounds(elGeo, el.faces, this.atlas);
|
|
5169
5978
|
if (this.options.tints) {
|
|
5170
5979
|
const colors = new Float32Array(elGeo.getAttribute("position").count * 3).fill(1);
|
|
5171
5980
|
for (const [faceIndex, faceName] of CUBE_FACES.entries()) {
|
|
@@ -5173,9 +5982,9 @@ var init_ModelObject = __esm({
|
|
|
5173
5982
|
if (tintIndex === void 0 || tintIndex < 0) continue;
|
|
5174
5983
|
const tint = this.options.tints[tintIndex];
|
|
5175
5984
|
if (tint === void 0) continue;
|
|
5176
|
-
const
|
|
5985
|
+
const color2 = new import_three25.Color(tint);
|
|
5177
5986
|
for (let vertex = 0; vertex < 4; vertex++) {
|
|
5178
|
-
|
|
5987
|
+
color2.toArray(colors, (faceIndex * 4 + vertex) * 3);
|
|
5179
5988
|
}
|
|
5180
5989
|
}
|
|
5181
5990
|
elGeo.setAttribute("color", new import_three25.BufferAttribute(colors, 3));
|
|
@@ -5206,10 +6015,14 @@ var init_ModelObject = __esm({
|
|
|
5206
6015
|
let combinedGeo;
|
|
5207
6016
|
if (allGeos.length > 0) {
|
|
5208
6017
|
combinedGeo = mergeBufferGeometries(allGeos);
|
|
6018
|
+
for (const geometry of allGeos) {
|
|
6019
|
+
this.geometries.delete(geometry);
|
|
6020
|
+
}
|
|
5209
6021
|
} else {
|
|
5210
6022
|
combinedGeo = new import_three25.BoxGeometry(16, 16, 16);
|
|
5211
6023
|
if (displayTransform) DisplayTransforms.apply(combinedGeo, displayTransform);
|
|
5212
6024
|
}
|
|
6025
|
+
this.geometries.add(combinedGeo);
|
|
5213
6026
|
combinedGeo.computeBoundingBox();
|
|
5214
6027
|
let mesh;
|
|
5215
6028
|
if (this.options.instanceMeshes) {
|
|
@@ -5231,9 +6044,12 @@ var init_ModelObject = __esm({
|
|
|
5231
6044
|
}
|
|
5232
6045
|
applyTextures() {
|
|
5233
6046
|
if (this.atlas) {
|
|
5234
|
-
const mat = Materials.createShadedCanvasMaterial(this.atlas.image.canvas, this.atlas.hasTransparency, false);
|
|
6047
|
+
const mat = Materials.createShadedCanvasMaterial(this.atlas.image.canvas, this.atlas.hasTransparency, false, true);
|
|
5235
6048
|
this.atlasMaterial = mat;
|
|
5236
6049
|
this.atlasTexture = mat.uniforms?.map?.value ?? mat.map;
|
|
6050
|
+
if (this.options.displayPosition === "gui" /* GUI */ && this.originalModel.gui_light === "front" /* FRONT */ && mat.uniforms?.SHADE) {
|
|
6051
|
+
mat.uniforms.SHADE.value = false;
|
|
6052
|
+
}
|
|
5237
6053
|
this.iterateAllMeshes((mesh) => {
|
|
5238
6054
|
if (mesh.geometry.hasAttribute("color")) mat.vertexColors = true;
|
|
5239
6055
|
mesh.material = mat;
|
|
@@ -5242,7 +6058,9 @@ var init_ModelObject = __esm({
|
|
|
5242
6058
|
}
|
|
5243
6059
|
}
|
|
5244
6060
|
updateAnimationSubscription() {
|
|
5245
|
-
|
|
6061
|
+
let root = this;
|
|
6062
|
+
while (root.parent && isModelObject(root.parent)) root = root.parent;
|
|
6063
|
+
const active = !!root.parent && (!this.isInstanced || this.instanceCounter > 0);
|
|
5246
6064
|
if (active && this.atlas?.hasAnimation && this.atlasTexture) {
|
|
5247
6065
|
if (!this.unsubscribeAtlas) {
|
|
5248
6066
|
this.atlasTexture.needsUpdate = true;
|
|
@@ -5258,6 +6076,9 @@ var init_ModelObject = __esm({
|
|
|
5258
6076
|
this.unsubscribeAtlas?.();
|
|
5259
6077
|
this.unsubscribeAtlas = void 0;
|
|
5260
6078
|
}
|
|
6079
|
+
for (const child of this.children) {
|
|
6080
|
+
if (isModelObject(child)) child.updateAnimationSubscription();
|
|
6081
|
+
}
|
|
5261
6082
|
}
|
|
5262
6083
|
nextInstance() {
|
|
5263
6084
|
const reference = super.nextInstance();
|
|
@@ -5277,6 +6098,8 @@ var init_ModelObject = __esm({
|
|
|
5277
6098
|
this.atlasMaterial = void 0;
|
|
5278
6099
|
for (const material of this.specialMaterials.values()) material.dispose();
|
|
5279
6100
|
this.specialMaterials.clear();
|
|
6101
|
+
for (const geometry of this.geometries) geometry.dispose();
|
|
6102
|
+
this.geometries.clear();
|
|
5280
6103
|
super.disposeAndRemoveAllChildren();
|
|
5281
6104
|
}
|
|
5282
6105
|
};
|
|
@@ -5492,9 +6315,9 @@ var init_FluidModelObject = __esm({
|
|
|
5492
6315
|
}
|
|
5493
6316
|
}
|
|
5494
6317
|
geometry.clearGroups();
|
|
5495
|
-
const
|
|
6318
|
+
const color2 = new import_three31.Color(this.options.tints?.[0] ?? (this.kind === "water" ? 4159204 : 16777215));
|
|
5496
6319
|
const colors = new Float32Array(geometry.getAttribute("position").count * 3);
|
|
5497
|
-
for (let i = 0; i < colors.length; i += 3)
|
|
6320
|
+
for (let i = 0; i < colors.length; i += 3) color2.toArray(colors, i);
|
|
5498
6321
|
geometry.setAttribute("color", new import_three31.BufferAttribute(colors, 3));
|
|
5499
6322
|
geometry.computeBoundingBox();
|
|
5500
6323
|
geometry.computeBoundingSphere();
|
|
@@ -5538,6 +6361,7 @@ __export(index_browser_exports, {
|
|
|
5538
6361
|
Axis: () => Axis,
|
|
5539
6362
|
BUILTIN_ENTITY: () => BUILTIN_ENTITY,
|
|
5540
6363
|
BUILTIN_GENERATED: () => BUILTIN_GENERATED,
|
|
6364
|
+
BannerPatterns: () => BannerPatterns,
|
|
5541
6365
|
BasicAssetKey: () => BasicAssetKey,
|
|
5542
6366
|
BatchedExecutor: () => BatchedExecutor,
|
|
5543
6367
|
BlockEntities: () => BlockEntities,
|
|
@@ -5562,6 +6386,7 @@ __export(index_browser_exports, {
|
|
|
5562
6386
|
DEFAULT_ROOT: () => DEFAULT_ROOT,
|
|
5563
6387
|
DEFAULT_UV: () => DEFAULT_UV,
|
|
5564
6388
|
DISPLAY_POSITIONS: () => DISPLAY_POSITIONS,
|
|
6389
|
+
DYE_COLORS: () => DYE_COLORS,
|
|
5565
6390
|
DefaultBlockStates: () => DefaultBlockStates,
|
|
5566
6391
|
DisplayPosition: () => DisplayPosition,
|
|
5567
6392
|
DisplayTransforms: () => DisplayTransforms,
|
|
@@ -5572,6 +6397,7 @@ __export(index_browser_exports, {
|
|
|
5572
6397
|
Env: () => Env,
|
|
5573
6398
|
FluidModelObject: () => FluidModelObject,
|
|
5574
6399
|
Fonts: () => Fonts,
|
|
6400
|
+
GUI_CONTAINER_LAYOUTS: () => GUI_CONTAINER_LAYOUTS,
|
|
5575
6401
|
Geometries: () => Geometries,
|
|
5576
6402
|
GuiHelper: () => GuiHelper,
|
|
5577
6403
|
GuiLight: () => GuiLight,
|
|
@@ -5598,11 +6424,14 @@ __export(index_browser_exports, {
|
|
|
5598
6424
|
Models: () => Models,
|
|
5599
6425
|
NBTHelper: () => NBTHelper,
|
|
5600
6426
|
OrbitControls: () => OrbitControls,
|
|
6427
|
+
PackFormats: () => PackFormats,
|
|
6428
|
+
PackMetadata: () => PackMetadata,
|
|
5601
6429
|
PersistentCache: () => PersistentCache,
|
|
5602
6430
|
Renderer: () => Renderer,
|
|
5603
6431
|
RequestError: () => RequestError,
|
|
5604
6432
|
Requests: () => Requests,
|
|
5605
6433
|
SKIN_PARTS: () => SKIN_PARTS,
|
|
6434
|
+
SceneDocumentLoader: () => SceneDocumentLoader,
|
|
5606
6435
|
SceneExporter: () => SceneExporter,
|
|
5607
6436
|
SceneInspector: () => SceneInspector,
|
|
5608
6437
|
SceneObject: () => SceneObject,
|
|
@@ -5678,6 +6507,7 @@ __export(index_browser_exports, {
|
|
|
5678
6507
|
mergeBufferAttributes: () => mergeBufferAttributes,
|
|
5679
6508
|
mergeBufferGeometries: () => mergeBufferGeometries,
|
|
5680
6509
|
mergeVertices: () => mergeVertices,
|
|
6510
|
+
parseSceneDocument: () => parseSceneDocument,
|
|
5681
6511
|
prefix: () => prefix,
|
|
5682
6512
|
probeImageSize: () => probeImageSize,
|
|
5683
6513
|
registerBrowserEnv: () => registerBrowserEnv,
|
|
@@ -5806,11 +6636,11 @@ var BrowserEnv = class {
|
|
|
5806
6636
|
imageSize(data) {
|
|
5807
6637
|
return probeImageSize(data);
|
|
5808
6638
|
}
|
|
5809
|
-
openCache(name,
|
|
6639
|
+
openCache(name, version2) {
|
|
5810
6640
|
return new BrowserCache(import_localforage.default.createInstance({
|
|
5811
6641
|
// IndexedDB schema upgrades retain records; each cache generation needs its own database.
|
|
5812
|
-
name: name +
|
|
5813
|
-
version
|
|
6642
|
+
name: name + version2,
|
|
6643
|
+
version: version2
|
|
5814
6644
|
}));
|
|
5815
6645
|
}
|
|
5816
6646
|
};
|
|
@@ -5841,6 +6671,7 @@ var Meshes = class _Meshes {
|
|
|
5841
6671
|
static createBox(key) {
|
|
5842
6672
|
return new import_three6.Mesh(Geometries.getBox(key.geometry), Materials.getImage(key.material));
|
|
5843
6673
|
}
|
|
6674
|
+
/** Returns a shared Mesh object. Use {@link createBox} for a mesh with its own transform and parent. */
|
|
5844
6675
|
static getBox(key) {
|
|
5845
6676
|
const keyStr = serializeMeshKey(key);
|
|
5846
6677
|
return Caching.boxMeshCache.get(keyStr, (k) => {
|
|
@@ -5890,6 +6721,7 @@ function shutdown() {
|
|
|
5890
6721
|
init_AssetKey();
|
|
5891
6722
|
init_AssetLoader();
|
|
5892
6723
|
init_Assets();
|
|
6724
|
+
init_BannerPatterns();
|
|
5893
6725
|
|
|
5894
6726
|
// src/assets/BlockEntities.ts
|
|
5895
6727
|
var import_three10 = require("three");
|
|
@@ -5953,6 +6785,7 @@ init_AssetParsers();
|
|
|
5953
6785
|
init_ModelTextures();
|
|
5954
6786
|
init_Caching();
|
|
5955
6787
|
var Fonts = class {
|
|
6788
|
+
/** Combines font definitions in source-priority order. Unsupported Unihex and TrueType providers are skipped. */
|
|
5956
6789
|
static async get(key = "minecraft:default") {
|
|
5957
6790
|
const fontKey = typeof key === "string" ? AssetKey.parse("font", key) : key;
|
|
5958
6791
|
return await Caching.fontCache.get(fontKey.serialize(), () => this.load(fontKey, []));
|
|
@@ -5968,8 +6801,8 @@ var Fonts = class {
|
|
|
5968
6801
|
const stack = await AssetLoader.getAll(key, "json" /* JSON */);
|
|
5969
6802
|
if (stack.length === 0) throw new Error(`Could not load font ${key.toNamespacedString()}`);
|
|
5970
6803
|
const glyphs = /* @__PURE__ */ new Map();
|
|
5971
|
-
for (const
|
|
5972
|
-
for (const provider of
|
|
6804
|
+
for (const asset2 of stack) {
|
|
6805
|
+
for (const provider of asset2.providers) {
|
|
5973
6806
|
if (provider.filter?.uniform || provider.filter?.jp) continue;
|
|
5974
6807
|
let provided;
|
|
5975
6808
|
switch (provider.type) {
|
|
@@ -6108,13 +6941,13 @@ var OBJExporter = class {
|
|
|
6108
6941
|
* @param {Object3D} object - The 3D object to export.
|
|
6109
6942
|
* @return {string} The exported OBJ.
|
|
6110
6943
|
*/
|
|
6111
|
-
parse(
|
|
6944
|
+
parse(object3) {
|
|
6112
6945
|
let output = "";
|
|
6113
6946
|
let indexVertex = 0;
|
|
6114
6947
|
let indexVertexUvs = 0;
|
|
6115
6948
|
let indexNormals = 0;
|
|
6116
6949
|
const vertex = new import_three16.Vector3();
|
|
6117
|
-
const
|
|
6950
|
+
const color2 = new import_three16.Color();
|
|
6118
6951
|
const normal = new import_three16.Vector3();
|
|
6119
6952
|
const uv = new import_three16.Vector2();
|
|
6120
6953
|
const face = [];
|
|
@@ -6213,9 +7046,9 @@ var OBJExporter = class {
|
|
|
6213
7046
|
vertex.applyMatrix4(points.matrixWorld);
|
|
6214
7047
|
output += "v " + vertex.x + " " + vertex.y + " " + vertex.z;
|
|
6215
7048
|
if (colors !== void 0) {
|
|
6216
|
-
|
|
6217
|
-
import_three16.ColorManagement.workingToColorSpace(
|
|
6218
|
-
output += " " +
|
|
7049
|
+
color2.fromBufferAttribute(colors, i);
|
|
7050
|
+
import_three16.ColorManagement.workingToColorSpace(color2, import_three16.SRGBColorSpace);
|
|
7051
|
+
output += " " + color2.r + " " + color2.g + " " + color2.b;
|
|
6219
7052
|
}
|
|
6220
7053
|
output += "\n";
|
|
6221
7054
|
}
|
|
@@ -6227,7 +7060,7 @@ var OBJExporter = class {
|
|
|
6227
7060
|
}
|
|
6228
7061
|
indexVertex += nbVertex;
|
|
6229
7062
|
}
|
|
6230
|
-
|
|
7063
|
+
object3.traverse(function(child) {
|
|
6231
7064
|
if (child.isMesh === true) {
|
|
6232
7065
|
parseMesh(child);
|
|
6233
7066
|
}
|
|
@@ -6255,9 +7088,9 @@ var PLYExporter = class {
|
|
|
6255
7088
|
* @param {PLYExporter~Options} options - The export options.
|
|
6256
7089
|
* @return {?(string|ArrayBuffer)} The exported PLY.
|
|
6257
7090
|
*/
|
|
6258
|
-
parse(
|
|
7091
|
+
parse(object3, onDone, options = {}) {
|
|
6259
7092
|
function traverseMeshes(cb) {
|
|
6260
|
-
|
|
7093
|
+
object3.traverse(function(child) {
|
|
6261
7094
|
if (child.isMesh === true || child.isPoints) {
|
|
6262
7095
|
const mesh = child;
|
|
6263
7096
|
const geometry = mesh.geometry;
|
|
@@ -6290,7 +7123,7 @@ var PLYExporter = class {
|
|
|
6290
7123
|
for (const name of customAttributeNames) customTypes[name] = "float";
|
|
6291
7124
|
let vertexCount = 0;
|
|
6292
7125
|
let faceCount = 0;
|
|
6293
|
-
|
|
7126
|
+
object3.traverse(function(child) {
|
|
6294
7127
|
if (child.isMesh === true) {
|
|
6295
7128
|
const mesh = child;
|
|
6296
7129
|
const geometry = mesh.geometry;
|
|
@@ -6884,8 +7717,8 @@ function equalArray(array1, array2) {
|
|
|
6884
7717
|
return element === array2[index];
|
|
6885
7718
|
});
|
|
6886
7719
|
}
|
|
6887
|
-
function stringToArrayBuffer(
|
|
6888
|
-
return new TextEncoder().encode(
|
|
7720
|
+
function stringToArrayBuffer(text2) {
|
|
7721
|
+
return new TextEncoder().encode(text2).buffer;
|
|
6889
7722
|
}
|
|
6890
7723
|
function isIdentityMatrix(matrix) {
|
|
6891
7724
|
return equalArray(matrix.elements, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
|
|
@@ -7078,12 +7911,12 @@ var GLTFWriter = class {
|
|
|
7078
7911
|
* @param {THREE.Object3D|THREE.Material|THREE.BufferGeometry|THREE.AnimationClip} object
|
|
7079
7912
|
* @param {Object} objectDef
|
|
7080
7913
|
*/
|
|
7081
|
-
serializeUserData(
|
|
7082
|
-
if (Object.keys(
|
|
7914
|
+
serializeUserData(object3, objectDef) {
|
|
7915
|
+
if (Object.keys(object3.userData).length === 0) return;
|
|
7083
7916
|
const options = this.options;
|
|
7084
7917
|
const extensionsUsed = this.extensionsUsed;
|
|
7085
7918
|
try {
|
|
7086
|
-
const json = JSON.parse(JSON.stringify(
|
|
7919
|
+
const json = JSON.parse(JSON.stringify(object3.userData));
|
|
7087
7920
|
if (options.includeCustomExtensions && json.gltfExtensions) {
|
|
7088
7921
|
if (objectDef.extensions === void 0) objectDef.extensions = {};
|
|
7089
7922
|
for (const extensionName in json.gltfExtensions) {
|
|
@@ -7094,7 +7927,7 @@ var GLTFWriter = class {
|
|
|
7094
7927
|
}
|
|
7095
7928
|
if (Object.keys(json).length > 0) objectDef.extras = json;
|
|
7096
7929
|
} catch (error) {
|
|
7097
|
-
console.warn("THREE.GLTFExporter: userData of '" +
|
|
7930
|
+
console.warn("THREE.GLTFExporter: userData of '" + object3.name + "' won't be serialized because of JSON.stringify error - " + error.message);
|
|
7098
7931
|
}
|
|
7099
7932
|
}
|
|
7100
7933
|
/**
|
|
@@ -7467,7 +8300,7 @@ var GLTFWriter = class {
|
|
|
7467
8300
|
* @param {string} mimeType export format
|
|
7468
8301
|
* @return {number} Index of the processed texture in the "images" array
|
|
7469
8302
|
*/
|
|
7470
|
-
processImage(image,
|
|
8303
|
+
processImage(image, format2, flipY, mimeType = "image/png") {
|
|
7471
8304
|
if (image !== null) {
|
|
7472
8305
|
const writer = this;
|
|
7473
8306
|
const cache = writer.cache;
|
|
@@ -7491,8 +8324,8 @@ var GLTFWriter = class {
|
|
|
7491
8324
|
ctx.scale(1, -1);
|
|
7492
8325
|
}
|
|
7493
8326
|
if (image.data !== void 0) {
|
|
7494
|
-
if (
|
|
7495
|
-
console.error("GLTFExporter: Only RGBAFormat is supported.",
|
|
8327
|
+
if (format2 !== import_three18.RGBAFormat) {
|
|
8328
|
+
console.error("GLTFExporter: Only RGBAFormat is supported.", format2);
|
|
7496
8329
|
}
|
|
7497
8330
|
if (image.width > options.maxTextureSize || image.height > options.maxTextureSize) {
|
|
7498
8331
|
console.warn("GLTFExporter: Image size is bigger than maxTextureSize", image);
|
|
@@ -7603,9 +8436,9 @@ var GLTFWriter = class {
|
|
|
7603
8436
|
if (material.isMeshStandardMaterial !== true && material.isMeshBasicMaterial !== true) {
|
|
7604
8437
|
console.warn("GLTFExporter: Use MeshStandardMaterial or MeshBasicMaterial for best results.");
|
|
7605
8438
|
}
|
|
7606
|
-
const
|
|
7607
|
-
if (!equalArray(
|
|
7608
|
-
materialDef.pbrMetallicRoughness.baseColorFactor =
|
|
8439
|
+
const color2 = material.color.toArray().concat([material.opacity]);
|
|
8440
|
+
if (!equalArray(color2, [1, 1, 1, 1])) {
|
|
8441
|
+
materialDef.pbrMetallicRoughness.baseColorFactor = color2;
|
|
7609
8442
|
}
|
|
7610
8443
|
if (material.isMeshStandardMaterial) {
|
|
7611
8444
|
materialDef.pbrMetallicRoughness.metallicFactor = material.metalness;
|
|
@@ -8020,13 +8853,13 @@ var GLTFWriter = class {
|
|
|
8020
8853
|
* @param {THREE.Object3D} object
|
|
8021
8854
|
* @return {?number}
|
|
8022
8855
|
*/
|
|
8023
|
-
processSkin(
|
|
8856
|
+
processSkin(object3) {
|
|
8024
8857
|
const json = this.json;
|
|
8025
8858
|
const nodeMap = this.nodeMap;
|
|
8026
|
-
const node = json.nodes[nodeMap.get(
|
|
8027
|
-
const skeleton =
|
|
8859
|
+
const node = json.nodes[nodeMap.get(object3)];
|
|
8860
|
+
const skeleton = object3.skeleton;
|
|
8028
8861
|
if (skeleton === void 0) return null;
|
|
8029
|
-
const rootJoint =
|
|
8862
|
+
const rootJoint = object3.skeleton.bones[0];
|
|
8030
8863
|
if (rootJoint === void 0) return null;
|
|
8031
8864
|
const joints = [];
|
|
8032
8865
|
const inverseBindMatrices = new Float32Array(skeleton.bones.length * 16);
|
|
@@ -8034,7 +8867,7 @@ var GLTFWriter = class {
|
|
|
8034
8867
|
for (let i = 0; i < skeleton.bones.length; ++i) {
|
|
8035
8868
|
joints.push(nodeMap.get(skeleton.bones[i]));
|
|
8036
8869
|
temporaryBoneInverse.copy(skeleton.boneInverses[i]);
|
|
8037
|
-
temporaryBoneInverse.multiply(
|
|
8870
|
+
temporaryBoneInverse.multiply(object3.bindMatrix).toArray(inverseBindMatrices, i * 16);
|
|
8038
8871
|
}
|
|
8039
8872
|
if (json.skins === void 0) json.skins = [];
|
|
8040
8873
|
json.skins.push({
|
|
@@ -8050,19 +8883,19 @@ var GLTFWriter = class {
|
|
|
8050
8883
|
* @param {THREE.Object3D} object Object3D to processNodeAsync
|
|
8051
8884
|
* @return {Promise<number>} Index of the node in the nodes list
|
|
8052
8885
|
*/
|
|
8053
|
-
async processNodeAsync(
|
|
8886
|
+
async processNodeAsync(object3) {
|
|
8054
8887
|
const json = this.json;
|
|
8055
8888
|
const options = this.options;
|
|
8056
8889
|
const nodeMap = this.nodeMap;
|
|
8057
8890
|
if (!json.nodes) json.nodes = [];
|
|
8058
|
-
if (
|
|
8059
|
-
return await this._processNodeWithPivotAsync(
|
|
8891
|
+
if (object3.pivot !== null) {
|
|
8892
|
+
return await this._processNodeWithPivotAsync(object3);
|
|
8060
8893
|
}
|
|
8061
8894
|
const nodeDef = {};
|
|
8062
8895
|
if (options.trs) {
|
|
8063
|
-
const rotation =
|
|
8064
|
-
const position =
|
|
8065
|
-
const scale =
|
|
8896
|
+
const rotation = object3.quaternion.toArray();
|
|
8897
|
+
const position = object3.position.toArray();
|
|
8898
|
+
const scale = object3.scale.toArray();
|
|
8066
8899
|
if (!equalArray(rotation, [0, 0, 0, 1])) {
|
|
8067
8900
|
nodeDef.rotation = rotation;
|
|
8068
8901
|
}
|
|
@@ -8073,28 +8906,28 @@ var GLTFWriter = class {
|
|
|
8073
8906
|
nodeDef.scale = scale;
|
|
8074
8907
|
}
|
|
8075
8908
|
} else {
|
|
8076
|
-
if (
|
|
8077
|
-
|
|
8909
|
+
if (object3.matrixAutoUpdate) {
|
|
8910
|
+
object3.updateMatrix();
|
|
8078
8911
|
}
|
|
8079
|
-
if (isIdentityMatrix(
|
|
8080
|
-
nodeDef.matrix =
|
|
8912
|
+
if (isIdentityMatrix(object3.matrix) === false) {
|
|
8913
|
+
nodeDef.matrix = object3.matrix.elements;
|
|
8081
8914
|
}
|
|
8082
8915
|
}
|
|
8083
|
-
if (
|
|
8084
|
-
this.serializeUserData(
|
|
8085
|
-
if (
|
|
8086
|
-
const meshIndex = await this.processMeshAsync(
|
|
8916
|
+
if (object3.name !== "") nodeDef.name = String(object3.name);
|
|
8917
|
+
this.serializeUserData(object3, nodeDef);
|
|
8918
|
+
if (object3.isMesh || object3.isLine || object3.isPoints) {
|
|
8919
|
+
const meshIndex = await this.processMeshAsync(object3);
|
|
8087
8920
|
if (meshIndex !== null) nodeDef.mesh = meshIndex;
|
|
8088
|
-
} else if (
|
|
8089
|
-
nodeDef.camera = this.processCamera(
|
|
8921
|
+
} else if (object3.isCamera) {
|
|
8922
|
+
nodeDef.camera = this.processCamera(object3);
|
|
8090
8923
|
}
|
|
8091
|
-
if (
|
|
8924
|
+
if (object3.isSkinnedMesh) this.skins.push(object3);
|
|
8092
8925
|
const nodeIndex = json.nodes.push(nodeDef) - 1;
|
|
8093
|
-
nodeMap.set(
|
|
8094
|
-
if (
|
|
8926
|
+
nodeMap.set(object3, nodeIndex);
|
|
8927
|
+
if (object3.children.length > 0) {
|
|
8095
8928
|
const children = [];
|
|
8096
|
-
for (let i = 0, l =
|
|
8097
|
-
const child =
|
|
8929
|
+
for (let i = 0, l = object3.children.length; i < l; i++) {
|
|
8930
|
+
const child = object3.children[i];
|
|
8098
8931
|
if (child.visible || options.onlyVisible === false) {
|
|
8099
8932
|
const childNodeIndex = await this.processNodeAsync(child);
|
|
8100
8933
|
if (childNodeIndex !== null) children.push(childNodeIndex);
|
|
@@ -8103,7 +8936,7 @@ var GLTFWriter = class {
|
|
|
8103
8936
|
if (children.length > 0) nodeDef.children = children;
|
|
8104
8937
|
}
|
|
8105
8938
|
await this._invokeAllAsync(function(ext) {
|
|
8106
|
-
ext.writeNode && ext.writeNode(
|
|
8939
|
+
ext.writeNode && ext.writeNode(object3, nodeDef);
|
|
8107
8940
|
});
|
|
8108
8941
|
return nodeIndex;
|
|
8109
8942
|
}
|
|
@@ -8112,19 +8945,19 @@ var GLTFWriter = class {
|
|
|
8112
8945
|
* @param {THREE.Object3D} object Object3D with pivot
|
|
8113
8946
|
* @return {Promise<number>} Index of the container node
|
|
8114
8947
|
*/
|
|
8115
|
-
async _processNodeWithPivotAsync(
|
|
8948
|
+
async _processNodeWithPivotAsync(object3) {
|
|
8116
8949
|
const json = this.json;
|
|
8117
8950
|
const options = this.options;
|
|
8118
8951
|
const nodeMap = this.nodeMap;
|
|
8119
|
-
const pivot =
|
|
8952
|
+
const pivot = object3.pivot;
|
|
8120
8953
|
const containerDef = {};
|
|
8121
|
-
const rotation =
|
|
8954
|
+
const rotation = object3.quaternion.toArray();
|
|
8122
8955
|
const position = [
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8956
|
+
object3.position.x + pivot.x,
|
|
8957
|
+
object3.position.y + pivot.y,
|
|
8958
|
+
object3.position.z + pivot.z
|
|
8126
8959
|
];
|
|
8127
|
-
const scale =
|
|
8960
|
+
const scale = object3.scale.toArray();
|
|
8128
8961
|
if (!equalArray(rotation, [0, 0, 0, 1])) {
|
|
8129
8962
|
containerDef.rotation = rotation;
|
|
8130
8963
|
}
|
|
@@ -8135,28 +8968,28 @@ var GLTFWriter = class {
|
|
|
8135
8968
|
containerDef.scale = scale;
|
|
8136
8969
|
}
|
|
8137
8970
|
containerDef.extras = { pivot: pivot.toArray() };
|
|
8138
|
-
if (
|
|
8139
|
-
this.serializeUserData(
|
|
8971
|
+
if (object3.name !== "") containerDef.name = String(object3.name);
|
|
8972
|
+
this.serializeUserData(object3, containerDef);
|
|
8140
8973
|
const containerIndex = json.nodes.push(containerDef) - 1;
|
|
8141
|
-
nodeMap.set(
|
|
8974
|
+
nodeMap.set(object3, containerIndex);
|
|
8142
8975
|
const childDef = {};
|
|
8143
8976
|
const childPosition = [-pivot.x, -pivot.y, -pivot.z];
|
|
8144
8977
|
if (!equalArray(childPosition, [0, 0, 0])) {
|
|
8145
8978
|
childDef.translation = childPosition;
|
|
8146
8979
|
}
|
|
8147
|
-
if (
|
|
8148
|
-
const meshIndex = await this.processMeshAsync(
|
|
8980
|
+
if (object3.isMesh || object3.isLine || object3.isPoints) {
|
|
8981
|
+
const meshIndex = await this.processMeshAsync(object3);
|
|
8149
8982
|
if (meshIndex !== null) childDef.mesh = meshIndex;
|
|
8150
|
-
} else if (
|
|
8151
|
-
childDef.camera = this.processCamera(
|
|
8983
|
+
} else if (object3.isCamera) {
|
|
8984
|
+
childDef.camera = this.processCamera(object3);
|
|
8152
8985
|
}
|
|
8153
|
-
if (
|
|
8986
|
+
if (object3.isSkinnedMesh) this.skins.push(object3);
|
|
8154
8987
|
const childIndex = json.nodes.push(childDef) - 1;
|
|
8155
8988
|
const containerChildren = [childIndex];
|
|
8156
|
-
if (
|
|
8989
|
+
if (object3.children.length > 0) {
|
|
8157
8990
|
const grandchildren = [];
|
|
8158
|
-
for (let i = 0, l =
|
|
8159
|
-
const child =
|
|
8991
|
+
for (let i = 0, l = object3.children.length; i < l; i++) {
|
|
8992
|
+
const child = object3.children[i];
|
|
8160
8993
|
if (child.visible || options.onlyVisible === false) {
|
|
8161
8994
|
const childNodeIndex = await this.processNodeAsync(child);
|
|
8162
8995
|
if (childNodeIndex !== null) grandchildren.push(childNodeIndex);
|
|
@@ -8166,7 +8999,7 @@ var GLTFWriter = class {
|
|
|
8166
8999
|
}
|
|
8167
9000
|
containerDef.children = containerChildren;
|
|
8168
9001
|
await this._invokeAllAsync(function(ext) {
|
|
8169
|
-
ext.writeNode && ext.writeNode(
|
|
9002
|
+
ext.writeNode && ext.writeNode(object3, containerDef);
|
|
8170
9003
|
});
|
|
8171
9004
|
return containerIndex;
|
|
8172
9005
|
}
|
|
@@ -8609,10 +9442,10 @@ var GLTFMeshGpuInstancing = class {
|
|
|
8609
9442
|
this.writer = writer;
|
|
8610
9443
|
this.name = "EXT_mesh_gpu_instancing";
|
|
8611
9444
|
}
|
|
8612
|
-
writeNode(
|
|
8613
|
-
if (!
|
|
9445
|
+
writeNode(object3, nodeDef) {
|
|
9446
|
+
if (!object3.isInstancedMesh) return;
|
|
8614
9447
|
const writer = this.writer;
|
|
8615
|
-
const mesh =
|
|
9448
|
+
const mesh = object3;
|
|
8616
9449
|
const translationAttr = new Float32Array(mesh.count * 3);
|
|
8617
9450
|
const rotationAttr = new Float32Array(mesh.count * 4);
|
|
8618
9451
|
const scaleAttr = new Float32Array(mesh.count * 3);
|
|
@@ -8802,8 +9635,8 @@ var SceneExporter = class {
|
|
|
8802
9635
|
}
|
|
8803
9636
|
}
|
|
8804
9637
|
};
|
|
8805
|
-
function localMatrix(
|
|
8806
|
-
return
|
|
9638
|
+
function localMatrix(object3) {
|
|
9639
|
+
return object3.matrixAutoUpdate ? new import_three19.Matrix4().compose(object3.position, object3.quaternion, object3.scale) : object3.matrix.clone();
|
|
8807
9640
|
}
|
|
8808
9641
|
function createSnapshot(root, gltf, bakeColors = false) {
|
|
8809
9642
|
const scene = new import_three19.Scene();
|
|
@@ -8907,24 +9740,24 @@ function createSnapshot(root, gltf, bakeColors = false) {
|
|
|
8907
9740
|
scene.add(copy);
|
|
8908
9741
|
}
|
|
8909
9742
|
};
|
|
8910
|
-
const visit = (
|
|
8911
|
-
if (!
|
|
8912
|
-
const world = parentWorld.clone().multiply(localMatrix(
|
|
8913
|
-
const mesh =
|
|
9743
|
+
const visit = (object3, parentWorld) => {
|
|
9744
|
+
if (!object3.visible) return;
|
|
9745
|
+
const world = parentWorld.clone().multiply(localMatrix(object3));
|
|
9746
|
+
const mesh = object3;
|
|
8914
9747
|
if (mesh.isMesh) {
|
|
8915
9748
|
const instanced = mesh;
|
|
8916
9749
|
if (instanced.isInstancedMesh) {
|
|
8917
9750
|
for (let i = 0; i < instanced.count; i++) {
|
|
8918
9751
|
const matrix = new import_three19.Matrix4();
|
|
8919
9752
|
instanced.getMatrixAt(i, matrix);
|
|
8920
|
-
const
|
|
8921
|
-
addMesh(mesh, world.clone().multiply(matrix),
|
|
9753
|
+
const color2 = instanced.instanceColor ? new import_three19.Color().fromBufferAttribute(instanced.instanceColor, i) : void 0;
|
|
9754
|
+
addMesh(mesh, world.clone().multiply(matrix), color2);
|
|
8922
9755
|
}
|
|
8923
9756
|
} else {
|
|
8924
9757
|
addMesh(mesh, world);
|
|
8925
9758
|
}
|
|
8926
9759
|
}
|
|
8927
|
-
for (const child of
|
|
9760
|
+
for (const child of object3.children) visit(child, world);
|
|
8928
9761
|
};
|
|
8929
9762
|
try {
|
|
8930
9763
|
const ancestors = [];
|
|
@@ -8997,16 +9830,17 @@ function copyDataTexture(source) {
|
|
|
8997
9830
|
|
|
8998
9831
|
// src/gui/GuiHelper.ts
|
|
8999
9832
|
init_AssetKey();
|
|
9833
|
+
init_ModelTextures();
|
|
9000
9834
|
|
|
9001
9835
|
// src/gui/GuiText.ts
|
|
9002
9836
|
var import_three20 = require("three");
|
|
9003
|
-
async function layoutGuiText(
|
|
9837
|
+
async function layoutGuiText(text2, options = {}) {
|
|
9004
9838
|
const maxWidth = options.maxWidth ?? Infinity, lineHeight = options.lineHeight ?? 9;
|
|
9005
9839
|
if (!(maxWidth > 0) || !Number.isFinite(lineHeight) || lineHeight <= 0) {
|
|
9006
9840
|
throw new Error("Text wrap width and line height must be positive");
|
|
9007
9841
|
}
|
|
9008
9842
|
const font = await Fonts.get(options.font);
|
|
9009
|
-
const runs = typeof
|
|
9843
|
+
const runs = typeof text2 === "string" ? [{ text: text2 }] : text2;
|
|
9010
9844
|
const glyphs = [], lineWidths = [];
|
|
9011
9845
|
let line = [], width = 0;
|
|
9012
9846
|
let trailingNewline = false;
|
|
@@ -9060,7 +9894,7 @@ function createGuiTextGeometry(layout, shadow = true) {
|
|
|
9060
9894
|
batch = { image: glyph?.image, positions: [], uvs: [], colors: [], indices: [] };
|
|
9061
9895
|
batches.push(batch);
|
|
9062
9896
|
}
|
|
9063
|
-
const
|
|
9897
|
+
const color2 = new import_three20.Color(isShadow ? (style.color & 16579836) >> 2 : style.color);
|
|
9064
9898
|
const offset = isShadow ? 1 : 0;
|
|
9065
9899
|
const top = glyph ? 7 - glyph.ascent : 0;
|
|
9066
9900
|
const height = glyph ? glyph.height * glyph.scale : 8;
|
|
@@ -9088,7 +9922,7 @@ function createGuiTextGeometry(layout, shadow = true) {
|
|
|
9088
9922
|
const v0 = glyph ? 1 - glyph.y / glyph.image.height : 1;
|
|
9089
9923
|
const v1 = glyph ? 1 - (glyph.y + glyph.height) / glyph.image.height : 0;
|
|
9090
9924
|
batch.uvs.push(u0, v0, u1, v0, u0, v1, u1, v1);
|
|
9091
|
-
for (let i = 0; i < 4; i++) batch.colors.push(
|
|
9925
|
+
for (let i = 0; i < 4; i++) batch.colors.push(color2.r, color2.g, color2.b);
|
|
9092
9926
|
batch.indices.push(base, base + 2, base + 1, base + 2, base + 3, base + 1);
|
|
9093
9927
|
};
|
|
9094
9928
|
for (const boldOffset of style.bold ? [0, 1] : [0]) {
|
|
@@ -9106,7 +9940,128 @@ function createGuiTextGeometry(layout, shadow = true) {
|
|
|
9106
9940
|
}
|
|
9107
9941
|
|
|
9108
9942
|
// src/gui/GuiHelper.ts
|
|
9943
|
+
var GUI_CONTAINER_LAYOUTS = {
|
|
9944
|
+
generic_54: {
|
|
9945
|
+
texture: "minecraft:gui/container/generic_54",
|
|
9946
|
+
crop: [0, 0, 176, 222],
|
|
9947
|
+
slotOrigin: [8, 18],
|
|
9948
|
+
slotOffset: [18, 18],
|
|
9949
|
+
rowSize: 9,
|
|
9950
|
+
slotCount: 54
|
|
9951
|
+
},
|
|
9952
|
+
crafting_table: {
|
|
9953
|
+
texture: "minecraft:gui/container/crafting_table",
|
|
9954
|
+
crop: [0, 0, 176, 166],
|
|
9955
|
+
slotOrigin: [30, 17],
|
|
9956
|
+
slotOffset: [18, 18],
|
|
9957
|
+
rowSize: 3,
|
|
9958
|
+
slotCount: 9,
|
|
9959
|
+
resultPosition: [124, 35]
|
|
9960
|
+
}
|
|
9961
|
+
};
|
|
9109
9962
|
var GuiHelper = class {
|
|
9963
|
+
/**
|
|
9964
|
+
* Creates a 182×5 boss bar using the active resource pack. Finite progress is clamped to 0–1.
|
|
9965
|
+
* The fill crops the source image; explicit crops do not apply `.mcmeta` sprite scaling.
|
|
9966
|
+
*/
|
|
9967
|
+
static async bossBar(options = {}) {
|
|
9968
|
+
const { color: color2 = "pink", progress = 1, position = [0, 0] } = options;
|
|
9969
|
+
this.validatePosition(position);
|
|
9970
|
+
if (!["pink", "blue", "red", "green", "yellow", "purple", "white"].includes(color2)) {
|
|
9971
|
+
throw new Error(`Unsupported boss bar color: ${color2}`);
|
|
9972
|
+
}
|
|
9973
|
+
if (!Number.isFinite(progress)) throw new Error("Boss bar progress must be finite");
|
|
9974
|
+
const value = Math.max(0, Math.min(1, progress));
|
|
9975
|
+
const width = value > 0 ? Math.floor(value * 181) + 1 : 0;
|
|
9976
|
+
const texture = `minecraft:gui/sprites/boss_bar/${color2}`;
|
|
9977
|
+
const layers = [{
|
|
9978
|
+
name: "boss-bar-background",
|
|
9979
|
+
texture: `${texture}_background`,
|
|
9980
|
+
position: [...position],
|
|
9981
|
+
size: [182, 5]
|
|
9982
|
+
}];
|
|
9983
|
+
if (width > 0) {
|
|
9984
|
+
layers.push({
|
|
9985
|
+
...await this.cropTexture(`${texture}_progress`, [0, 0, width, 5], [182, 5], position),
|
|
9986
|
+
name: "boss-bar-progress"
|
|
9987
|
+
});
|
|
9988
|
+
}
|
|
9989
|
+
return layers;
|
|
9990
|
+
}
|
|
9991
|
+
/**
|
|
9992
|
+
* Creates a 192×192 book page with optional arrows and supplied text. Buttons are hidden by default.
|
|
9993
|
+
* Callers choose the page content and button states; this helper does not paginate or handle clicks.
|
|
9994
|
+
*/
|
|
9995
|
+
static async book(options = {}) {
|
|
9996
|
+
const {
|
|
9997
|
+
position = [0, 0],
|
|
9998
|
+
previous = "hidden",
|
|
9999
|
+
next = "hidden",
|
|
10000
|
+
text: text2,
|
|
10001
|
+
color: color2 = 0,
|
|
10002
|
+
shadow = false,
|
|
10003
|
+
maxWidth = 114,
|
|
10004
|
+
lineHeight = 9,
|
|
10005
|
+
...style
|
|
10006
|
+
} = options;
|
|
10007
|
+
this.validatePosition(position);
|
|
10008
|
+
for (const state of [previous, next]) {
|
|
10009
|
+
if (!["hidden", "normal", "hover"].includes(state)) throw new Error(`Unsupported book button state: ${state}`);
|
|
10010
|
+
}
|
|
10011
|
+
const layers = [{
|
|
10012
|
+
...await this.cropTexture("minecraft:gui/book", [0, 0, 192, 192], [256, 256], position),
|
|
10013
|
+
name: "book-background"
|
|
10014
|
+
}];
|
|
10015
|
+
for (const [name, direction, state, x] of [
|
|
10016
|
+
["previous", "backward", previous, 43],
|
|
10017
|
+
["next", "forward", next, 116]
|
|
10018
|
+
]) {
|
|
10019
|
+
if (state === "hidden") continue;
|
|
10020
|
+
layers.push({
|
|
10021
|
+
name: `book-${name}`,
|
|
10022
|
+
texture: `minecraft:gui/sprites/widget/page_${direction}${state === "hover" ? "_highlighted" : ""}`,
|
|
10023
|
+
position: [position[0] + x, position[1] + 157],
|
|
10024
|
+
size: [23, 13]
|
|
10025
|
+
});
|
|
10026
|
+
}
|
|
10027
|
+
if (text2 !== void 0) {
|
|
10028
|
+
layers.push({
|
|
10029
|
+
name: "book-text",
|
|
10030
|
+
text: text2,
|
|
10031
|
+
position: [position[0] + 36, position[1] + 30],
|
|
10032
|
+
color: color2,
|
|
10033
|
+
shadow,
|
|
10034
|
+
maxWidth,
|
|
10035
|
+
lineHeight,
|
|
10036
|
+
...style
|
|
10037
|
+
});
|
|
10038
|
+
}
|
|
10039
|
+
return layers;
|
|
10040
|
+
}
|
|
10041
|
+
/** Creates a container background. Use {@link GUI_CONTAINER_LAYOUTS} with {@link inventorySlot} to place its items. */
|
|
10042
|
+
static async container(type, position = [0, 0]) {
|
|
10043
|
+
this.validatePosition(position);
|
|
10044
|
+
if (type !== "generic_54" && type !== "crafting_table") throw new Error(`Unsupported GUI container: ${type}`);
|
|
10045
|
+
const layout = GUI_CONTAINER_LAYOUTS[type];
|
|
10046
|
+
return [{ ...await this.cropTexture(layout.texture, layout.crop, [256, 256], position), name: "background" }];
|
|
10047
|
+
}
|
|
10048
|
+
static validatePosition(position) {
|
|
10049
|
+
if (!Array.isArray(position) || position.length !== 2 || !position.every(Number.isFinite)) {
|
|
10050
|
+
throw new Error("GUI position must contain two finite coordinates");
|
|
10051
|
+
}
|
|
10052
|
+
}
|
|
10053
|
+
static async cropTexture(texture, crop, referenceSize, position) {
|
|
10054
|
+
const image = await ModelTextures.preload(AssetKey.parse("textures", texture));
|
|
10055
|
+
if (!image) throw new Error(`Could not load GUI texture ${texture}`);
|
|
10056
|
+
const [x, y, width, height] = crop;
|
|
10057
|
+
const scaleX = image.width / referenceSize[0], scaleY = image.height / referenceSize[1];
|
|
10058
|
+
return {
|
|
10059
|
+
texture,
|
|
10060
|
+
crop: [x * scaleX, y * scaleY, width * scaleX, height * scaleY],
|
|
10061
|
+
position: [...position],
|
|
10062
|
+
size: [width, height]
|
|
10063
|
+
};
|
|
10064
|
+
}
|
|
9110
10065
|
/** Sizes a tooltip around supplied text; the first entry is its title. */
|
|
9111
10066
|
static async tooltip(lines, options = {}) {
|
|
9112
10067
|
if (!lines.length) return [];
|
|
@@ -9122,20 +10077,28 @@ var GuiHelper = class {
|
|
|
9122
10077
|
size: [width + 24, height + 24]
|
|
9123
10078
|
}));
|
|
9124
10079
|
let y = position[1];
|
|
9125
|
-
lines.forEach((
|
|
9126
|
-
layers.push({ ...textOptions, name: `tooltip-line-${index}`, text, position: [position[0], y] });
|
|
10080
|
+
lines.forEach((text2, index) => {
|
|
10081
|
+
layers.push({ ...textOptions, name: `tooltip-line-${index}`, text: text2, position: [position[0], y] });
|
|
9127
10082
|
y += layouts[index].height + (index === 0 ? titleGap : 0);
|
|
9128
10083
|
});
|
|
9129
10084
|
return layers;
|
|
9130
10085
|
}
|
|
10086
|
+
/**
|
|
10087
|
+
* Calculates a slot's top-left position in GUI pixels.
|
|
10088
|
+
* @param slot - Zero-based slot index, or `[column, row]`.
|
|
10089
|
+
* @param origin - Position of the first slot.
|
|
10090
|
+
* @param offset - Horizontal and vertical spacing between slot origins, in pixels.
|
|
10091
|
+
* @param rowSize - Slots per row when `slot` is an index.
|
|
10092
|
+
*/
|
|
9131
10093
|
static inventorySlot(slot, origin = [0, 0], offset = [18, 18], rowSize = 9) {
|
|
9132
10094
|
const [column, row] = typeof slot === "number" ? [slot % rowSize, Math.floor(slot / rowSize)] : slot;
|
|
9133
10095
|
return [origin[0] + column * offset[0], origin[1] + row * offset[1]];
|
|
9134
10096
|
}
|
|
9135
10097
|
/** Creates crafting-table layers for ingredients and the result; stack counts are not drawn. */
|
|
9136
10098
|
static recipe(recipe, options = {}) {
|
|
10099
|
+
const layout = GUI_CONTAINER_LAYOUTS.crafting_table;
|
|
9137
10100
|
const layers = [
|
|
9138
|
-
{ name: "background", texture:
|
|
10101
|
+
{ name: "background", texture: layout.texture, crop: [...layout.crop] }
|
|
9139
10102
|
];
|
|
9140
10103
|
const itemKey = (ingredient) => {
|
|
9141
10104
|
const item = typeof ingredient === "string" && !ingredient.startsWith("#") ? ingredient : typeof ingredient === "object" && "item" in ingredient ? ingredient.item : options.resolveIngredient?.(ingredient);
|
|
@@ -9150,17 +10113,17 @@ var GuiHelper = class {
|
|
|
9150
10113
|
layers.push({
|
|
9151
10114
|
name: `ingredient-${slot}`,
|
|
9152
10115
|
item: itemKey(ingredient),
|
|
9153
|
-
position: this.inventorySlot(slot,
|
|
10116
|
+
position: this.inventorySlot(slot, layout.slotOrigin, layout.slotOffset, layout.rowSize)
|
|
9154
10117
|
});
|
|
9155
10118
|
};
|
|
9156
10119
|
switch (recipe.type) {
|
|
9157
10120
|
case "minecraft:crafting_shaped":
|
|
9158
10121
|
case "crafting_shaped": {
|
|
9159
|
-
const
|
|
9160
|
-
if (!
|
|
10122
|
+
const pattern2 = recipe.pattern;
|
|
10123
|
+
if (!pattern2.length || pattern2.length > 3 || !pattern2[0].length || pattern2[0].length > 3 || pattern2.some((row) => row.length !== pattern2[0].length)) {
|
|
9161
10124
|
throw new Error("Crafting patterns must be rectangular, with 1\u20133 rows and columns");
|
|
9162
10125
|
}
|
|
9163
|
-
const cells =
|
|
10126
|
+
const cells = pattern2.flatMap((row, y) => row.split("").map((symbol, x) => ({ symbol, x, y }))).filter((cell) => cell.symbol !== " ");
|
|
9164
10127
|
if (!cells.length) throw new Error("Crafting patterns must contain an ingredient");
|
|
9165
10128
|
const minX = Math.min(...cells.map((cell) => cell.x)), maxX = Math.max(...cells.map((cell) => cell.x));
|
|
9166
10129
|
const minY = Math.min(...cells.map((cell) => cell.y)), maxY = Math.max(...cells.map((cell) => cell.y));
|
|
@@ -9185,7 +10148,7 @@ var GuiHelper = class {
|
|
|
9185
10148
|
layers.push({
|
|
9186
10149
|
name: "result",
|
|
9187
10150
|
item: itemKey("id" in recipe.result ? recipe.result.id : recipe.result.item),
|
|
9188
|
-
position: [
|
|
10151
|
+
position: [...layout.resultPosition]
|
|
9189
10152
|
});
|
|
9190
10153
|
return layers;
|
|
9191
10154
|
}
|
|
@@ -9280,7 +10243,6 @@ init_SceneObject();
|
|
|
9280
10243
|
init_Materials();
|
|
9281
10244
|
init_ModelObject();
|
|
9282
10245
|
init_DisplayPosition();
|
|
9283
|
-
init_GuiLight();
|
|
9284
10246
|
var GuiObject = class extends SceneObject {
|
|
9285
10247
|
constructor(textureLayers, options) {
|
|
9286
10248
|
super({ ...options, instanceMeshes: false, mergeMeshes: false });
|
|
@@ -9348,12 +10310,16 @@ var GuiObject = class extends SceneObject {
|
|
|
9348
10310
|
mesh2.geometry.computeBoundingBox();
|
|
9349
10311
|
bounds.union(mesh2.geometry.boundingBox.clone().applyMatrix4(new import_three26.Matrix4().multiplyMatrices(inverse, mesh2.matrixWorld)));
|
|
9350
10312
|
});
|
|
9351
|
-
item.position.z = depth
|
|
9352
|
-
|
|
10313
|
+
item.position.z = depth;
|
|
10314
|
+
if (!bounds.isEmpty()) {
|
|
10315
|
+
item.position.z -= bounds.min.z;
|
|
10316
|
+
depth += bounds.max.z - bounds.min.z;
|
|
10317
|
+
this.bounds.expandByPoint(new import_three26.Vector2(bounds.min.x, -bounds.max.y));
|
|
10318
|
+
this.bounds.expandByPoint(new import_three26.Vector2(bounds.max.x, -bounds.min.y));
|
|
10319
|
+
}
|
|
10320
|
+
depth += 0.01;
|
|
9353
10321
|
this.bounds.expandByPoint(new import_three26.Vector2(x, y));
|
|
9354
10322
|
this.bounds.expandByPoint(new import_three26.Vector2(x + width2, y + height2));
|
|
9355
|
-
this.bounds.expandByPoint(new import_three26.Vector2(bounds.min.x, -bounds.max.y));
|
|
9356
|
-
this.bounds.expandByPoint(new import_three26.Vector2(bounds.max.x, -bounds.min.y));
|
|
9357
10323
|
continue;
|
|
9358
10324
|
}
|
|
9359
10325
|
const key = typeof layer.texture === "string" ? AssetKey.parse("textures", layer.texture) : layer.texture;
|
|
@@ -9382,7 +10348,7 @@ var GuiObject = class extends SceneObject {
|
|
|
9382
10348
|
}
|
|
9383
10349
|
async createItem(layer, index) {
|
|
9384
10350
|
const key = typeof layer.item === "string" ? AssetKey.parse("models", layer.item) : layer.item;
|
|
9385
|
-
const model = await Models.getMerged(key);
|
|
10351
|
+
const model = await Models.getMerged(key, { ...layer.context, displayContext: "gui" /* GUI */ });
|
|
9386
10352
|
if (!model) throw new Error(`Could not load GUI item ${key.toNamespacedString()}`);
|
|
9387
10353
|
const item = new ModelObject(model, {
|
|
9388
10354
|
displayPosition: "gui" /* GUI */,
|
|
@@ -9393,15 +10359,13 @@ var GuiObject = class extends SceneObject {
|
|
|
9393
10359
|
item.name = `group:${layer.name ?? index}`;
|
|
9394
10360
|
this.add(item);
|
|
9395
10361
|
await item.init();
|
|
9396
|
-
|
|
9397
|
-
|
|
10362
|
+
const meshes = [];
|
|
10363
|
+
item.iterateAllMeshes((mesh) => meshes.push(mesh));
|
|
10364
|
+
meshes.forEach((mesh, partIndex) => {
|
|
9398
10365
|
mesh.name = `mesh:${layer.name ?? index}`;
|
|
9399
|
-
mesh.renderOrder = index;
|
|
10366
|
+
mesh.renderOrder = index + partIndex / (meshes.length + 1) * 0.5;
|
|
9400
10367
|
for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
|
|
9401
10368
|
material.transparent = true;
|
|
9402
|
-
if (model.gui_light === "front" /* FRONT */ && material.uniforms?.SHADE) {
|
|
9403
|
-
material.uniforms.SHADE.value = false;
|
|
9404
|
-
}
|
|
9405
10369
|
}
|
|
9406
10370
|
});
|
|
9407
10371
|
return item;
|
|
@@ -9572,29 +10536,29 @@ var SceneInspector = class {
|
|
|
9572
10536
|
this.objectControlsContainer.innerHTML = "";
|
|
9573
10537
|
this.addControls(targetObject, targetIntersection, instance);
|
|
9574
10538
|
}
|
|
9575
|
-
addControls(
|
|
10539
|
+
addControls(object3, intersection, instance) {
|
|
9576
10540
|
const container = document.createElement("div");
|
|
9577
10541
|
container.append(this.separator("Select Parent/Child"));
|
|
9578
|
-
if (
|
|
9579
|
-
container.append(this.buttonControl("Select Parent " +
|
|
9580
|
-
this.selectObject(
|
|
10542
|
+
if (object3.parent) {
|
|
10543
|
+
container.append(this.buttonControl("Select Parent " + object3.parent.constructor.name + " " + object3.parent.name, "P", () => {
|
|
10544
|
+
this.selectObject(object3.parent, intersection, instance);
|
|
9581
10545
|
}));
|
|
9582
10546
|
}
|
|
9583
|
-
if (
|
|
10547
|
+
if (object3.children.length > 0) {
|
|
9584
10548
|
let i = 1;
|
|
9585
|
-
for (let child of
|
|
10549
|
+
for (let child of object3.children) {
|
|
9586
10550
|
container.append(this.buttonControl("Select Child " + child.constructor.name + " " + child.name, "C" + i++, () => {
|
|
9587
10551
|
this.selectObject(child, intersection, instance);
|
|
9588
10552
|
}));
|
|
9589
10553
|
}
|
|
9590
10554
|
}
|
|
9591
10555
|
container.append(this.separator());
|
|
9592
|
-
const selectedInstance = instance && (
|
|
10556
|
+
const selectedInstance = instance && (object3 === instance.instanceable || object3 === intersection?.object) ? instance : void 0;
|
|
9593
10557
|
if (!selectedInstance) {
|
|
9594
|
-
container.append(this.toggleControl("Visibility", "V",
|
|
10558
|
+
container.append(this.toggleControl("Visibility", "V", object3.visible, (v) => object3.visible = v));
|
|
9595
10559
|
}
|
|
9596
10560
|
container.append(this.separator("Mesh"));
|
|
9597
|
-
this.addObjectControls(
|
|
10561
|
+
this.addObjectControls(object3, intersection, container, selectedInstance);
|
|
9598
10562
|
container.append(this.separator());
|
|
9599
10563
|
this.objectControlsContainer.append(container);
|
|
9600
10564
|
}
|
|
@@ -9785,6 +10749,7 @@ var SceneStatsDisplay = class {
|
|
|
9785
10749
|
appendTo(el) {
|
|
9786
10750
|
el.append(this.statsContainer);
|
|
9787
10751
|
}
|
|
10752
|
+
/** Stops the refresh timer and removes the panel from the page. */
|
|
9788
10753
|
dispose() {
|
|
9789
10754
|
clearInterval(this.interval);
|
|
9790
10755
|
this.interval = void 0;
|
|
@@ -9809,6 +10774,7 @@ var InstanceManager = class {
|
|
|
9809
10774
|
this.isInstanceManager = true;
|
|
9810
10775
|
this.instanceCache = {};
|
|
9811
10776
|
}
|
|
10777
|
+
/** Allocates a new placement of a cached model, or returns `undefined` if the key is unknown. */
|
|
9812
10778
|
async get(key) {
|
|
9813
10779
|
if (key in this.instanceCache) {
|
|
9814
10780
|
console.debug(p9, "key in cache", key);
|
|
@@ -9816,6 +10782,7 @@ var InstanceManager = class {
|
|
|
9816
10782
|
}
|
|
9817
10783
|
return void 0;
|
|
9818
10784
|
}
|
|
10785
|
+
/** Allocates a placement, calling the supplier once when the shared model is first needed. */
|
|
9819
10786
|
async getOrCreate(key, supplier) {
|
|
9820
10787
|
if (key in this.instanceCache) {
|
|
9821
10788
|
return await this.get(key);
|
|
@@ -9832,6 +10799,7 @@ var InstanceManager = class {
|
|
|
9832
10799
|
throw error;
|
|
9833
10800
|
}
|
|
9834
10801
|
}
|
|
10802
|
+
/** Forgets cached models without disposing them or releasing their existing placements. */
|
|
9835
10803
|
reset() {
|
|
9836
10804
|
for (let instanceCacheKey in this.instanceCache) {
|
|
9837
10805
|
delete this.instanceCache[instanceCacheKey];
|
|
@@ -9877,6 +10845,7 @@ function matchesCondition(condition, state) {
|
|
|
9877
10845
|
});
|
|
9878
10846
|
}
|
|
9879
10847
|
var BlockStateResolver = class {
|
|
10848
|
+
/** Loads vanilla property defaults, falling back to preview values inferred from the blockstate file. */
|
|
9880
10849
|
static async defaults(blockState) {
|
|
9881
10850
|
const defaults = blockState.key ? await BlockStates.getDefaultState(blockState.key) : void 0;
|
|
9882
10851
|
const state = {};
|
|
@@ -9896,9 +10865,11 @@ var BlockStateResolver = class {
|
|
|
9896
10865
|
}
|
|
9897
10866
|
return state;
|
|
9898
10867
|
}
|
|
10868
|
+
/** Selects matching models. Supply `choose` to control selection within each alternatives array. */
|
|
9899
10869
|
static select(blockState, state, choose = this.choose) {
|
|
9900
10870
|
return this.matching(blockState, state).map(choose);
|
|
9901
10871
|
}
|
|
10872
|
+
/** Returns matching model groups before choosing weighted alternatives. */
|
|
9902
10873
|
static matching(blockState, state) {
|
|
9903
10874
|
const out = [];
|
|
9904
10875
|
if (blockState.variants) {
|
|
@@ -9922,6 +10893,7 @@ var BlockStateResolver = class {
|
|
|
9922
10893
|
}
|
|
9923
10894
|
return out;
|
|
9924
10895
|
}
|
|
10896
|
+
/** Selects a random alternative using positive integer weights, each defaulting to 1. */
|
|
9925
10897
|
static choose(variants) {
|
|
9926
10898
|
if (!Array.isArray(variants)) return variants;
|
|
9927
10899
|
if (!variants.length) throw new MineRenderError("Blockstate variant arrays must not be empty");
|
|
@@ -9939,6 +10911,7 @@ var BlockStateResolver = class {
|
|
|
9939
10911
|
}
|
|
9940
10912
|
return variants[variants.length - 1];
|
|
9941
10913
|
}
|
|
10914
|
+
/** Converts blockstate rotations in degrees into the Euler rotation used by model rendering. */
|
|
9942
10915
|
static rotation(variant) {
|
|
9943
10916
|
const rotation = new import_three29.Euler();
|
|
9944
10917
|
if (typeof variant.x !== "undefined") {
|
|
@@ -10001,29 +10974,29 @@ var BlockTints = class {
|
|
|
10001
10974
|
}
|
|
10002
10975
|
}
|
|
10003
10976
|
if (!indices.size) return tints;
|
|
10004
|
-
let
|
|
10977
|
+
let color2;
|
|
10005
10978
|
switch (rule.source) {
|
|
10006
10979
|
case "constant":
|
|
10007
|
-
|
|
10980
|
+
color2 = rule.color;
|
|
10008
10981
|
break;
|
|
10009
10982
|
case "grass":
|
|
10010
|
-
|
|
10983
|
+
color2 = await Colormaps.grassColor(key);
|
|
10011
10984
|
break;
|
|
10012
10985
|
case "redstone": {
|
|
10013
10986
|
const power = Number(state.power ?? 0) / 15;
|
|
10014
10987
|
const red = power * 0.6 + (power > 0 ? 0.4 : 0.3);
|
|
10015
10988
|
const green = Math.max(0, power * power * 0.7 - 0.5);
|
|
10016
10989
|
const blue = Math.max(0, power * power * 0.6 - 0.7);
|
|
10017
|
-
|
|
10990
|
+
color2 = Math.floor(red * 255) << 16 | Math.floor(green * 255) << 8 | Math.floor(blue * 255);
|
|
10018
10991
|
break;
|
|
10019
10992
|
}
|
|
10020
10993
|
case "stem": {
|
|
10021
10994
|
const age = Number(state.age ?? 0);
|
|
10022
|
-
|
|
10995
|
+
color2 = age * 32 << 16 | 255 - age * 8 << 8 | age * 4;
|
|
10023
10996
|
break;
|
|
10024
10997
|
}
|
|
10025
10998
|
}
|
|
10026
|
-
return { ...Object.fromEntries([...indices].map((index) => [index,
|
|
10999
|
+
return { ...Object.fromEntries([...indices].map((index) => [index, color2])), ...tints };
|
|
10027
11000
|
}
|
|
10028
11001
|
};
|
|
10029
11002
|
|
|
@@ -10160,8 +11133,8 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
10160
11133
|
return false;
|
|
10161
11134
|
}
|
|
10162
11135
|
return this._models.some((model) => {
|
|
10163
|
-
const
|
|
10164
|
-
return
|
|
11136
|
+
const object3 = isInstanceReference(model) ? model.instanceable : model;
|
|
11137
|
+
return object3.isOpaqueFullCube && this.getModelCullMask(model, 63) === 63;
|
|
10165
11138
|
});
|
|
10166
11139
|
}
|
|
10167
11140
|
getModelMatrix(model) {
|
|
@@ -10170,8 +11143,8 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
10170
11143
|
return model.matrix.clone();
|
|
10171
11144
|
}
|
|
10172
11145
|
getModelCullMask(model, worldMask) {
|
|
10173
|
-
const
|
|
10174
|
-
if (
|
|
11146
|
+
const object3 = isInstanceReference(model) ? model.instanceable : model;
|
|
11147
|
+
if (object3.options.displayPosition) return 0;
|
|
10175
11148
|
const position = new import_three32.Vector3();
|
|
10176
11149
|
const rotation = new import_three32.Quaternion();
|
|
10177
11150
|
const scale = new import_three32.Vector3();
|
|
@@ -10188,14 +11161,14 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
10188
11161
|
replacements.push(model);
|
|
10189
11162
|
continue;
|
|
10190
11163
|
}
|
|
10191
|
-
const
|
|
11164
|
+
const object3 = isInstanceReference(model) ? model.instanceable : model;
|
|
10192
11165
|
const cullMask = this.getModelCullMask(model, worldMask);
|
|
10193
|
-
if (cullMask === (
|
|
11166
|
+
if (cullMask === (object3.options.cullMask ?? 0)) {
|
|
10194
11167
|
replacements.push(model);
|
|
10195
11168
|
continue;
|
|
10196
11169
|
}
|
|
10197
11170
|
const matrix = this.getModelMatrix(model);
|
|
10198
|
-
const replacement = await this.scene.addModel(
|
|
11171
|
+
const replacement = await this.scene.addModel(object3.originalModel, { ...object3.options, cullMask });
|
|
10199
11172
|
replacements.push(replacement);
|
|
10200
11173
|
if (isInstanceReference(replacement)) {
|
|
10201
11174
|
replacement.setMatrix(matrix);
|
|
@@ -10203,8 +11176,8 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
10203
11176
|
replacement.matrix.copy(matrix);
|
|
10204
11177
|
replacement.matrixWorldNeedsUpdate = true;
|
|
10205
11178
|
matrix.decompose(replacement.position, replacement.quaternion, replacement.scale);
|
|
10206
|
-
replacement.matrixAutoUpdate =
|
|
10207
|
-
replacement.visible =
|
|
11179
|
+
replacement.matrixAutoUpdate = object3.matrixAutoUpdate;
|
|
11180
|
+
replacement.visible = object3.visible;
|
|
10208
11181
|
}
|
|
10209
11182
|
}
|
|
10210
11183
|
} catch (error) {
|
|
@@ -10220,6 +11193,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
10220
11193
|
}
|
|
10221
11194
|
this.notifyDirty();
|
|
10222
11195
|
}
|
|
11196
|
+
/** Selected property values. Use {@link setState} to change properties and rebuild the rendered models. */
|
|
10223
11197
|
get state() {
|
|
10224
11198
|
return this._state;
|
|
10225
11199
|
}
|
|
@@ -10319,6 +11293,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
10319
11293
|
obj.setPosition(this.position);
|
|
10320
11294
|
return obj;
|
|
10321
11295
|
}
|
|
11296
|
+
/** Clears selected properties and rebuilds models without reapplying property defaults. */
|
|
10322
11297
|
async resetState() {
|
|
10323
11298
|
this._previousState = this._state;
|
|
10324
11299
|
this._state = {};
|
|
@@ -12126,9 +13101,9 @@ var SchematicParser = class {
|
|
|
12126
13101
|
* Parses a legacy schematic using custom mappings before the bundled defaults.
|
|
12127
13102
|
* Keys are `id:metadata`; values are block states such as `minecraft:oak_log[axis=x]`.
|
|
12128
13103
|
*/
|
|
12129
|
-
static async parse(nbt, customMappings = {}) {
|
|
13104
|
+
static async parse(nbt, customMappings = {}, options = {}) {
|
|
12130
13105
|
const tags = nbt.value;
|
|
12131
|
-
if (tags.Materials?.type !== "string" || tags.Materials.value !== "Alpha") {
|
|
13106
|
+
if (!options.lenient && (tags.Materials?.type !== "string" || tags.Materials.value !== "Alpha")) {
|
|
12132
13107
|
throw new MineRenderError("Schematic Materials must be Alpha");
|
|
12133
13108
|
}
|
|
12134
13109
|
const size = ["Width", "Height", "Length"].map((name) => {
|
|
@@ -12169,8 +13144,16 @@ var SchematicParser = class {
|
|
|
12169
13144
|
const id = high << 8 | ids.value[index] & 255;
|
|
12170
13145
|
const metadata = data.value[index] & 255;
|
|
12171
13146
|
const key = `${id}:${metadata}`;
|
|
12172
|
-
|
|
12173
|
-
if (!mapped
|
|
13147
|
+
let mapped = customMappings[key] ?? mapping[key];
|
|
13148
|
+
if (!mapped && options.lenient) {
|
|
13149
|
+
mapped = customMappings[`${id}:0`] ?? mapping[`${id}:0`];
|
|
13150
|
+
}
|
|
13151
|
+
if (!mapped) {
|
|
13152
|
+
if (options.lenient) {
|
|
13153
|
+
continue;
|
|
13154
|
+
}
|
|
13155
|
+
throw new MineRenderError(`Unsupported legacy block ${id}:${metadata} at schematic index ${index}`);
|
|
13156
|
+
}
|
|
12174
13157
|
const [type, state] = mapped.split("[");
|
|
12175
13158
|
if (type === "minecraft:air") continue;
|
|
12176
13159
|
const properties = {};
|
|
@@ -12214,6 +13197,11 @@ function compoundList(tags, name) {
|
|
|
12214
13197
|
// src/model/multiblock/StructureParser.ts
|
|
12215
13198
|
init_MineRenderError();
|
|
12216
13199
|
var StructureParser = class {
|
|
13200
|
+
/**
|
|
13201
|
+
* Parses decoded NBT, omitting air blocks.
|
|
13202
|
+
* @param _nbt - Structure data decoded by {@link NBTHelper.fromBuffer}.
|
|
13203
|
+
* @param paletteIndex - Palette to select when the structure has several. Defaults to 0.
|
|
13204
|
+
*/
|
|
12217
13205
|
static async parse(_nbt, paletteIndex) {
|
|
12218
13206
|
const nbt = _nbt.value;
|
|
12219
13207
|
let palette;
|
|
@@ -13048,6 +14036,7 @@ var SkinImage = class {
|
|
|
13048
14036
|
// src/skin/SkinTextures.ts
|
|
13049
14037
|
init_Textures();
|
|
13050
14038
|
var SkinTextures = class {
|
|
14039
|
+
/** Loads a skin URL. Pass `legacy` to override layout detection from the image dimensions. */
|
|
13051
14040
|
static async get(src, legacy) {
|
|
13052
14041
|
const image = await SkinImage.getData(src);
|
|
13053
14042
|
const scale = image.width / 64;
|
|
@@ -13221,6 +14210,14 @@ var SkinObject = class extends SceneObject {
|
|
|
13221
14210
|
}
|
|
13222
14211
|
}
|
|
13223
14212
|
}
|
|
14213
|
+
/**
|
|
14214
|
+
* Loads a skin texture, detecting slim arms and legacy layouts by default.
|
|
14215
|
+
* To override detection, set `slim` or `legacy` in the options passed to
|
|
14216
|
+
* `new SkinObject(options)` or `scene.addSkin(src, options)`.
|
|
14217
|
+
* Use {@link setSlim} or {@link setLegacy} to change these settings later.
|
|
14218
|
+
*
|
|
14219
|
+
* @param src - URL of the skin texture.
|
|
14220
|
+
*/
|
|
13224
14221
|
async setSkinTexture(src) {
|
|
13225
14222
|
if (typeof src === "undefined") return;
|
|
13226
14223
|
this.skinTextureSrc = src;
|
|
@@ -13328,59 +14325,61 @@ var _MineRenderScene = class _MineRenderScene extends import_three34.Scene {
|
|
|
13328
14325
|
this.options = (0, import_ts_deepmerge8.default)({}, _MineRenderScene.DEFAULT_OPTIONS, options ?? {});
|
|
13329
14326
|
}
|
|
13330
14327
|
add(...objects) {
|
|
13331
|
-
for (const
|
|
14328
|
+
for (const object3 of objects) {
|
|
13332
14329
|
this.dirty = true;
|
|
13333
14330
|
try {
|
|
13334
|
-
super.add(
|
|
14331
|
+
super.add(object3);
|
|
13335
14332
|
} finally {
|
|
13336
|
-
if (
|
|
13337
|
-
this.updateObjectRegistration(
|
|
14333
|
+
if (object3?.isObject3D && object3 !== this) {
|
|
14334
|
+
this.updateObjectRegistration(object3, object3.parent === this);
|
|
13338
14335
|
}
|
|
13339
14336
|
}
|
|
13340
14337
|
}
|
|
13341
14338
|
return this;
|
|
13342
14339
|
}
|
|
13343
14340
|
remove(...objects) {
|
|
13344
|
-
for (const
|
|
13345
|
-
if (!this.children.includes(
|
|
14341
|
+
for (const object3 of objects) {
|
|
14342
|
+
if (!this.children.includes(object3)) continue;
|
|
13346
14343
|
this.dirty = true;
|
|
13347
|
-
this.updateObjectRegistration(
|
|
14344
|
+
this.updateObjectRegistration(object3, false);
|
|
13348
14345
|
try {
|
|
13349
|
-
super.remove(
|
|
14346
|
+
super.remove(object3);
|
|
13350
14347
|
} finally {
|
|
13351
|
-
this.updateObjectRegistration(
|
|
14348
|
+
this.updateObjectRegistration(object3, object3.parent === this);
|
|
13352
14349
|
}
|
|
13353
14350
|
}
|
|
13354
14351
|
return this;
|
|
13355
14352
|
}
|
|
13356
|
-
updateObjectRegistration(
|
|
13357
|
-
if (this.observedObjects.has(
|
|
13358
|
-
const observable =
|
|
14353
|
+
updateObjectRegistration(object3, attached) {
|
|
14354
|
+
if (this.observedObjects.has(object3) === attached) return;
|
|
14355
|
+
const observable = object3;
|
|
13359
14356
|
if (attached) {
|
|
13360
|
-
this.observedObjects.add(
|
|
14357
|
+
this.observedObjects.add(object3);
|
|
13361
14358
|
observable.addEventListener("change", this.onObjectChange);
|
|
13362
14359
|
} else {
|
|
13363
|
-
this.observedObjects.delete(
|
|
14360
|
+
this.observedObjects.delete(object3);
|
|
13364
14361
|
observable.removeEventListener("change", this.onObjectChange);
|
|
13365
14362
|
}
|
|
13366
14363
|
const change = attached ? 1 : -1;
|
|
13367
14364
|
this.stats.objectCount += change;
|
|
13368
|
-
if (isSceneObject(
|
|
14365
|
+
if (isSceneObject(object3)) {
|
|
13369
14366
|
this.stats.sceneObjectCount += change;
|
|
13370
14367
|
}
|
|
13371
14368
|
}
|
|
13372
|
-
|
|
14369
|
+
/** Initializes each object before attaching it to the scene. */
|
|
14370
|
+
async initAndAdd(...object3) {
|
|
13373
14371
|
this.dirty = true;
|
|
13374
|
-
for (let obj of
|
|
14372
|
+
for (let obj of object3) {
|
|
13375
14373
|
await obj.init();
|
|
13376
14374
|
}
|
|
13377
|
-
return this.add(...
|
|
14375
|
+
return this.add(...object3);
|
|
13378
14376
|
}
|
|
13379
|
-
async addSceneObject(
|
|
14377
|
+
async addSceneObject(asset2, objectSupplier, _options, parent = this) {
|
|
13380
14378
|
this.dirty = true;
|
|
13381
14379
|
const obj = await objectSupplier();
|
|
13382
|
-
if (obj
|
|
13383
|
-
|
|
14380
|
+
if (isModelObject(obj)) obj.options.tints = await ItemTints.get(obj.originalModel, obj.options.tints);
|
|
14381
|
+
if (obj?.options?.instanceMeshes && asset2.key && asset2.key?.assetType === "models") {
|
|
14382
|
+
let key = asset2.key.serialize();
|
|
13384
14383
|
if (isModelObject(obj)) {
|
|
13385
14384
|
const { displayPosition, uvLockRotation, tints, cullMask } = obj.options;
|
|
13386
14385
|
if (displayPosition) key += `|display:${displayPosition}`;
|
|
@@ -13404,13 +14403,32 @@ var _MineRenderScene = class _MineRenderScene extends import_three34.Scene {
|
|
|
13404
14403
|
return obj;
|
|
13405
14404
|
}
|
|
13406
14405
|
}
|
|
14406
|
+
/**
|
|
14407
|
+
* Adds a model loaded with {@link Models.getMerged}.
|
|
14408
|
+
* Set `options.instanceMeshes` to `false` to create a separate ModelObject.
|
|
14409
|
+
*
|
|
14410
|
+
* @param parent - Object to attach the model to. Defaults to this scene.
|
|
14411
|
+
* @returns The model, or a reference to one placement of a shared model.
|
|
14412
|
+
*/
|
|
13407
14413
|
async addModel(model, options, parent = this) {
|
|
13408
|
-
options = { ...options, tints: await ItemTints.get(model, options?.tints) };
|
|
13409
14414
|
return this.addSceneObject(model, () => new ModelObject(model, options), options, parent);
|
|
13410
14415
|
}
|
|
14416
|
+
/**
|
|
14417
|
+
* Adds the models selected by a blockstate loaded with {@link BlockStates.get}.
|
|
14418
|
+
* Set `options.initialState` to choose block properties such as `facing` or `axis`.
|
|
14419
|
+
* Use the returned object's `setPosition` method to move all of its model parts.
|
|
14420
|
+
*/
|
|
13411
14421
|
async addBlock(blockState, options, parent = this) {
|
|
13412
14422
|
return this.addSceneObject(blockState, () => new BlockObject(blockState, options), options, parent);
|
|
13413
14423
|
}
|
|
14424
|
+
/**
|
|
14425
|
+
* Loads a skin and adds an initialized player model.
|
|
14426
|
+
*
|
|
14427
|
+
* @param skin - Texture URL, such as one returned by {@link Skins.fromUuidOrUsername}.
|
|
14428
|
+
* Omit it to assign a texture later with {@link SkinObject.setSkinTexture}.
|
|
14429
|
+
* @param options - Player settings, including `slim` and `legacy` detection overrides.
|
|
14430
|
+
* @param parent - Object to attach the player to. Defaults to this scene.
|
|
14431
|
+
*/
|
|
13414
14432
|
async addSkin(skin, options, parent = this) {
|
|
13415
14433
|
this.dirty = true;
|
|
13416
14434
|
const obj = new SkinObject(options);
|
|
@@ -13422,9 +14440,14 @@ var _MineRenderScene = class _MineRenderScene extends import_three34.Scene {
|
|
|
13422
14440
|
parent.add(obj);
|
|
13423
14441
|
return obj;
|
|
13424
14442
|
}
|
|
14443
|
+
/** Adds an entity loaded with {@link Entities.getEntity}, using its selected layers and textures. */
|
|
13425
14444
|
async addEntity(entity, options, parent = this) {
|
|
13426
14445
|
return this.addSceneObject(entity, () => new EntityObject(entity, options), options, parent);
|
|
13427
14446
|
}
|
|
14447
|
+
/**
|
|
14448
|
+
* Adds GUI layers in draw order. One GUI pixel equals one scene unit.
|
|
14449
|
+
* Layer positions start at the top left, with positive GUI y pointing down.
|
|
14450
|
+
*/
|
|
13428
14451
|
async addGui(layers, options, parent = this) {
|
|
13429
14452
|
const obj = new GuiObject(layers, options);
|
|
13430
14453
|
obj.scene = this;
|
|
@@ -13455,11 +14478,11 @@ var _changeEvent = { type: "change" };
|
|
|
13455
14478
|
var _startEvent = { type: "start" };
|
|
13456
14479
|
var _endEvent = { type: "end" };
|
|
13457
14480
|
var OrbitControls = class extends import_three35.EventDispatcher {
|
|
13458
|
-
constructor(
|
|
14481
|
+
constructor(object3, domElement) {
|
|
13459
14482
|
super();
|
|
13460
14483
|
if (domElement === void 0) console.warn('THREE.OrbitControls: The second parameter "domElement" is now mandatory.');
|
|
13461
14484
|
if (domElement === document) console.error('THREE.OrbitControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.');
|
|
13462
|
-
this.object =
|
|
14485
|
+
this.object = object3;
|
|
13463
14486
|
this.domElement = domElement;
|
|
13464
14487
|
this.domElement.style.touchAction = "none";
|
|
13465
14488
|
this.enabled = true;
|
|
@@ -13516,7 +14539,7 @@ var OrbitControls = class extends import_three35.EventDispatcher {
|
|
|
13516
14539
|
};
|
|
13517
14540
|
this.update = function() {
|
|
13518
14541
|
const offset = new import_three35.Vector3();
|
|
13519
|
-
const quat = new import_three35.Quaternion().setFromUnitVectors(
|
|
14542
|
+
const quat = new import_three35.Quaternion().setFromUnitVectors(object3.up, new import_three35.Vector3(0, 1, 0));
|
|
13520
14543
|
const quatInverse = quat.clone().invert();
|
|
13521
14544
|
const lastPosition = new import_three35.Vector3();
|
|
13522
14545
|
const lastQuaternion = new import_three35.Quaternion();
|
|
@@ -14081,8 +15104,8 @@ var OrbitControls = class extends import_three35.EventDispatcher {
|
|
|
14081
15104
|
}
|
|
14082
15105
|
};
|
|
14083
15106
|
var MapControls = class extends OrbitControls {
|
|
14084
|
-
constructor(
|
|
14085
|
-
super(
|
|
15107
|
+
constructor(object3, domElement) {
|
|
15108
|
+
super(object3, domElement);
|
|
14086
15109
|
this.screenSpacePanning = false;
|
|
14087
15110
|
this.mouseButtons.LEFT = import_three35.MOUSE.PAN;
|
|
14088
15111
|
this.mouseButtons.RIGHT = import_three35.MOUSE.ROTATE;
|
|
@@ -14241,6 +15264,7 @@ var _Renderer = class _Renderer {
|
|
|
14241
15264
|
get element() {
|
|
14242
15265
|
return this._element;
|
|
14243
15266
|
}
|
|
15267
|
+
/** Attaches the canvas to an element and sizes it to that element's dimensions. */
|
|
14244
15268
|
appendTo(element) {
|
|
14245
15269
|
if (this._disposed) return;
|
|
14246
15270
|
this._element = element;
|
|
@@ -14268,6 +15292,7 @@ var _Renderer = class _Renderer {
|
|
|
14268
15292
|
events.add(changeEvent2);
|
|
14269
15293
|
dispatcher.addEventListener(changeEvent2, this._changeListener);
|
|
14270
15294
|
}
|
|
15295
|
+
/** Updates the camera and canvas size. Width and height are in CSS pixels. */
|
|
14271
15296
|
resize(width, height) {
|
|
14272
15297
|
if (this._disposed) return;
|
|
14273
15298
|
if (isPerspectiveCamera(this.camera)) {
|
|
@@ -14306,12 +15331,14 @@ var _Renderer = class _Renderer {
|
|
|
14306
15331
|
if (this._frameCallbacks.get(callback) === subscription) this._frameCallbacks.delete(callback);
|
|
14307
15332
|
};
|
|
14308
15333
|
}
|
|
15334
|
+
/** Starts or resumes the animation loop and registered frame callbacks. */
|
|
14309
15335
|
start() {
|
|
14310
15336
|
if (this._disposed) return;
|
|
14311
15337
|
this.stop();
|
|
14312
15338
|
this._running = true;
|
|
14313
15339
|
if (!this._inAnimationLoop) this.renderer.setAnimationLoop(this._animationLoop);
|
|
14314
15340
|
}
|
|
15341
|
+
/** Pauses rendering and frame callbacks. Call {@link start} to resume. */
|
|
14315
15342
|
stop() {
|
|
14316
15343
|
if (this._disposed) return;
|
|
14317
15344
|
this._running = false;
|
|
@@ -14420,12 +15447,15 @@ var _Renderer = class _Renderer {
|
|
|
14420
15447
|
const canvas = trim ? trimCanvas(this.renderer.domElement) : this.renderer.domElement;
|
|
14421
15448
|
return canvas.toDataURL(mime, quality);
|
|
14422
15449
|
}
|
|
15450
|
+
/** Exports visible scene meshes as OBJ text, without texture images. */
|
|
14423
15451
|
toObj() {
|
|
14424
15452
|
return SceneExporter.toObj(this.scene);
|
|
14425
15453
|
}
|
|
15454
|
+
/** Exports visible scene meshes as PLY. Set `options.binary` for an ArrayBuffer. */
|
|
14426
15455
|
toPLY(options) {
|
|
14427
15456
|
return SceneExporter.toPLY(this.scene, options);
|
|
14428
15457
|
}
|
|
15458
|
+
/** Exports a static textured scene in the browser. Set `options.binary` for GLB output. */
|
|
14429
15459
|
toGLTF(options) {
|
|
14430
15460
|
return SceneExporter.toGLTF(this.scene, options);
|
|
14431
15461
|
}
|
|
@@ -14503,16 +15533,244 @@ init_SceneObject();
|
|
|
14503
15533
|
// src/index.ts
|
|
14504
15534
|
init_request();
|
|
14505
15535
|
|
|
15536
|
+
// src/scene/SceneDocument.ts
|
|
15537
|
+
init_DisplayPosition();
|
|
15538
|
+
function fail(path, message) {
|
|
15539
|
+
throw new Error(`${path}: ${message}`);
|
|
15540
|
+
}
|
|
15541
|
+
function object2(value, path) {
|
|
15542
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail(path, "expected a JSON object");
|
|
15543
|
+
for (const key of Object.keys(value)) {
|
|
15544
|
+
if (["__proto__", "prototype", "constructor"].includes(key)) fail(`${path}.${key}`, "unsupported property");
|
|
15545
|
+
}
|
|
15546
|
+
return value;
|
|
15547
|
+
}
|
|
15548
|
+
function fields(value, path, allowed, required = []) {
|
|
15549
|
+
const input = object2(value, path);
|
|
15550
|
+
for (const key of required) if (input[key] === void 0) fail(`${path}.${key}`, "required");
|
|
15551
|
+
for (const [key, entry] of Object.entries(input)) {
|
|
15552
|
+
if (!Object.prototype.hasOwnProperty.call(allowed, key)) fail(`${path}.${key}`, "unsupported property");
|
|
15553
|
+
if (entry !== void 0) allowed[key](entry, `${path}.${key}`);
|
|
15554
|
+
}
|
|
15555
|
+
return input;
|
|
15556
|
+
}
|
|
15557
|
+
var text = (value, path) => {
|
|
15558
|
+
if (typeof value !== "string" || !value.trim()) fail(path, "expected a nonempty string");
|
|
15559
|
+
};
|
|
15560
|
+
var literalText = (value, path) => {
|
|
15561
|
+
if (typeof value !== "string") fail(path, "expected a string");
|
|
15562
|
+
};
|
|
15563
|
+
var boolean = (value, path) => {
|
|
15564
|
+
if (typeof value !== "boolean") fail(path, "expected a boolean");
|
|
15565
|
+
};
|
|
15566
|
+
var number = (value, path) => {
|
|
15567
|
+
if (typeof value !== "number" || !Number.isFinite(value)) fail(path, "expected a finite number");
|
|
15568
|
+
};
|
|
15569
|
+
var positive = (value, path) => {
|
|
15570
|
+
number(value, path);
|
|
15571
|
+
if (value <= 0) fail(path, "expected a positive number");
|
|
15572
|
+
};
|
|
15573
|
+
var nonnegative = (value, path) => {
|
|
15574
|
+
number(value, path);
|
|
15575
|
+
if (value < 0) fail(path, "expected a nonnegative number");
|
|
15576
|
+
};
|
|
15577
|
+
var color = (value, path) => {
|
|
15578
|
+
number(value, path);
|
|
15579
|
+
if (!Number.isInteger(value) || value < 0 || value > 16777215) {
|
|
15580
|
+
fail(path, "expected an RGB integer from 0 to 16777215");
|
|
15581
|
+
}
|
|
15582
|
+
};
|
|
15583
|
+
var asset = (value, path) => {
|
|
15584
|
+
text(value, path);
|
|
15585
|
+
if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(value) || value.split(/[:/]/).some((part) => !part || part === "." || part === "..")) {
|
|
15586
|
+
fail(path, "expected a resource ID such as minecraft:stone");
|
|
15587
|
+
}
|
|
15588
|
+
};
|
|
15589
|
+
var version = (value, path) => {
|
|
15590
|
+
text(value, path);
|
|
15591
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(value)) fail(path, "expected a Minecraft version without slashes");
|
|
15592
|
+
};
|
|
15593
|
+
var tuple = (size, item = number) => (value, path) => {
|
|
15594
|
+
if (!Array.isArray(value) || value.length !== size) fail(path, `expected ${size} numbers`);
|
|
15595
|
+
for (let index = 0; index < size; index++) item(value[index], `${path}[${index}]`);
|
|
15596
|
+
};
|
|
15597
|
+
var list = (item) => (value, path) => {
|
|
15598
|
+
if (!Array.isArray(value)) fail(path, "expected an array");
|
|
15599
|
+
for (let index = 0; index < value.length; index++) item(value[index], `${path}[${index}]`);
|
|
15600
|
+
};
|
|
15601
|
+
var dictionary = (item, numericKeys = false) => (value, path) => {
|
|
15602
|
+
for (const [key, entry] of Object.entries(object2(value, path))) {
|
|
15603
|
+
if (numericKeys && !/^\d+$/.test(key)) fail(`${path}.${key}`, "expected a nonnegative tint index");
|
|
15604
|
+
item(entry, `${path}.${key}`);
|
|
15605
|
+
}
|
|
15606
|
+
};
|
|
15607
|
+
var enumeration = (values) => (value, path) => {
|
|
15608
|
+
if (!values.includes(value)) fail(path, `expected one of: ${values.join(", ")}`);
|
|
15609
|
+
};
|
|
15610
|
+
var textStyle = { color, bold: boolean, italic: boolean };
|
|
15611
|
+
function guiLayer(value, path) {
|
|
15612
|
+
const layer = object2(value, path);
|
|
15613
|
+
if (["texture", "item", "text"].filter((key) => layer[key] !== void 0).length !== 1) {
|
|
15614
|
+
fail(path, "expected exactly one of texture, item, or text");
|
|
15615
|
+
}
|
|
15616
|
+
const layout = { name: text, position: tuple(2), size: tuple(2, positive) };
|
|
15617
|
+
if (layer.texture !== void 0) {
|
|
15618
|
+
fields(value, path, { ...layout, texture: asset, crop: (value2, path2) => {
|
|
15619
|
+
tuple(4)(value2, path2);
|
|
15620
|
+
const [x, y, width, height] = value2;
|
|
15621
|
+
nonnegative(x, `${path2}[0]`);
|
|
15622
|
+
nonnegative(y, `${path2}[1]`);
|
|
15623
|
+
positive(width, `${path2}[2]`);
|
|
15624
|
+
positive(height, `${path2}[3]`);
|
|
15625
|
+
} });
|
|
15626
|
+
} else if (layer.item !== void 0) {
|
|
15627
|
+
fields(value, path, { ...layout, item: asset, tints: dictionary(color, true) });
|
|
15628
|
+
} else {
|
|
15629
|
+
fields(value, path, {
|
|
15630
|
+
...layout,
|
|
15631
|
+
...textStyle,
|
|
15632
|
+
font: asset,
|
|
15633
|
+
shadow: boolean,
|
|
15634
|
+
maxWidth: positive,
|
|
15635
|
+
lineHeight: positive,
|
|
15636
|
+
text: (value2, path2) => {
|
|
15637
|
+
if (typeof value2 === "string") return;
|
|
15638
|
+
list((run, path3) => {
|
|
15639
|
+
fields(run, path3, { text: literalText, ...textStyle }, ["text"]);
|
|
15640
|
+
})(value2, path2);
|
|
15641
|
+
}
|
|
15642
|
+
});
|
|
15643
|
+
}
|
|
15644
|
+
}
|
|
15645
|
+
function validateObject(value, path) {
|
|
15646
|
+
const input = object2(value, path);
|
|
15647
|
+
const common = { id: text, name: text, type: text, position: tuple(3), rotation: tuple(3), scale: tuple(3), visible: boolean };
|
|
15648
|
+
const required = ["id", "type"];
|
|
15649
|
+
const renderOptions = { wireframe: boolean };
|
|
15650
|
+
switch (input.type) {
|
|
15651
|
+
case "skin":
|
|
15652
|
+
fields(value, path, {
|
|
15653
|
+
...common,
|
|
15654
|
+
skin: text,
|
|
15655
|
+
cape: (value2, path2) => {
|
|
15656
|
+
fields(value2, path2, { texture: text, layout: enumeration(["minecraft", "optifine", "labymod"]) }, ["texture"]);
|
|
15657
|
+
},
|
|
15658
|
+
hiddenParts: list(enumeration(SKIN_PARTS)),
|
|
15659
|
+
pose: (value2, path2) => {
|
|
15660
|
+
fields(value2, path2, {
|
|
15661
|
+
head: tuple(3),
|
|
15662
|
+
body: tuple(3),
|
|
15663
|
+
rightArm: tuple(3),
|
|
15664
|
+
leftArm: tuple(3),
|
|
15665
|
+
rightLeg: tuple(3),
|
|
15666
|
+
leftLeg: tuple(3),
|
|
15667
|
+
cape: tuple(3)
|
|
15668
|
+
});
|
|
15669
|
+
},
|
|
15670
|
+
options: (value2, path2) => {
|
|
15671
|
+
fields(value2, path2, { slim: boolean, legacy: boolean });
|
|
15672
|
+
}
|
|
15673
|
+
}, required);
|
|
15674
|
+
break;
|
|
15675
|
+
case "block":
|
|
15676
|
+
fields(value, path, {
|
|
15677
|
+
...common,
|
|
15678
|
+
asset,
|
|
15679
|
+
state: dictionary(text),
|
|
15680
|
+
options: (value2, path2) => {
|
|
15681
|
+
fields(value2, path2, { ...renderOptions, tints: dictionary(color, true) });
|
|
15682
|
+
}
|
|
15683
|
+
}, [...required, "asset"]);
|
|
15684
|
+
break;
|
|
15685
|
+
case "item":
|
|
15686
|
+
case "model":
|
|
15687
|
+
fields(value, path, {
|
|
15688
|
+
...common,
|
|
15689
|
+
asset,
|
|
15690
|
+
options: (value2, path2) => {
|
|
15691
|
+
fields(value2, path2, { ...renderOptions, displayPosition: enumeration(DISPLAY_POSITIONS), tints: dictionary(color, true) });
|
|
15692
|
+
}
|
|
15693
|
+
}, [...required, "asset"]);
|
|
15694
|
+
break;
|
|
15695
|
+
case "entity":
|
|
15696
|
+
fields(value, path, {
|
|
15697
|
+
...common,
|
|
15698
|
+
asset,
|
|
15699
|
+
texture: asset,
|
|
15700
|
+
layers: list(text),
|
|
15701
|
+
when: list(text),
|
|
15702
|
+
textures: dictionary(asset),
|
|
15703
|
+
options: (value2, path2) => {
|
|
15704
|
+
fields(value2, path2, {
|
|
15705
|
+
...renderOptions,
|
|
15706
|
+
flip: boolean,
|
|
15707
|
+
tints: dictionary((value3, path3) => {
|
|
15708
|
+
typeof value3 === "string" ? text(value3, path3) : color(value3, path3);
|
|
15709
|
+
})
|
|
15710
|
+
});
|
|
15711
|
+
},
|
|
15712
|
+
animation: (value2, path2) => {
|
|
15713
|
+
fields(value2, path2, { name: text, loop: boolean, speed: nonnegative, time: nonnegative, paused: boolean }, ["name"]);
|
|
15714
|
+
}
|
|
15715
|
+
}, [...required, "asset"]);
|
|
15716
|
+
if (Array.isArray(input.layers) && !input.layers.length) fail(`${path}.layers`, "expected at least one layer");
|
|
15717
|
+
break;
|
|
15718
|
+
case "gui":
|
|
15719
|
+
fields(value, path, { ...common, layers: list(guiLayer) }, [...required, "layers"]);
|
|
15720
|
+
break;
|
|
15721
|
+
default:
|
|
15722
|
+
fail(`${path}.type`, "expected skin, block, item, entity, model, or gui");
|
|
15723
|
+
}
|
|
15724
|
+
}
|
|
15725
|
+
function parseSceneDocument(value) {
|
|
15726
|
+
if (typeof value === "string") {
|
|
15727
|
+
try {
|
|
15728
|
+
value = JSON.parse(value);
|
|
15729
|
+
} catch {
|
|
15730
|
+
fail("scene", "invalid JSON");
|
|
15731
|
+
}
|
|
15732
|
+
}
|
|
15733
|
+
const input = fields(value, "scene", {
|
|
15734
|
+
format: enumeration(["minerender-scene"]),
|
|
15735
|
+
version: (value2, path) => {
|
|
15736
|
+
if (value2 !== 1) fail(path, "only scene document version 1 is supported");
|
|
15737
|
+
},
|
|
15738
|
+
minecraftVersion: version,
|
|
15739
|
+
camera: (value2, path) => {
|
|
15740
|
+
fields(value2, path, { position: tuple(3), target: tuple(3) }, ["position", "target"]);
|
|
15741
|
+
},
|
|
15742
|
+
objects: list(validateObject)
|
|
15743
|
+
}, ["format", "version", "objects"]);
|
|
15744
|
+
const ids = /* @__PURE__ */ new Set();
|
|
15745
|
+
for (const [index, entry] of input.objects.entries()) {
|
|
15746
|
+
if (ids.has(entry.id)) fail(`scene.objects[${index}].id`, `duplicate object ID "${entry.id}"`);
|
|
15747
|
+
ids.add(entry.id);
|
|
15748
|
+
}
|
|
15749
|
+
return JSON.parse(JSON.stringify(input));
|
|
15750
|
+
}
|
|
15751
|
+
|
|
15752
|
+
// src/scene/SceneDocumentLoader.ts
|
|
15753
|
+
var import_three38 = require("three");
|
|
15754
|
+
init_AssetKey();
|
|
15755
|
+
init_AssetLoader();
|
|
15756
|
+
init_BlockStates();
|
|
15757
|
+
init_Entities();
|
|
15758
|
+
init_Models();
|
|
15759
|
+
init_DisplayPosition();
|
|
15760
|
+
init_SceneObject();
|
|
15761
|
+
|
|
14506
15762
|
// src/skin/Skins.ts
|
|
14507
15763
|
init_Requests();
|
|
14508
15764
|
var Skins = class {
|
|
14509
15765
|
//<editor-fold desc="Skins">
|
|
15766
|
+
/** Returns a skin URL for {@link MineRenderScene.addSkin}, or `undefined` if name lookup fails. */
|
|
14510
15767
|
static async fromUuidOrUsername(uuidOrUsername) {
|
|
14511
15768
|
if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
|
|
14512
15769
|
return this.fromUuid(uuidOrUsername);
|
|
14513
15770
|
}
|
|
14514
15771
|
return this.fromUsername(uuidOrUsername);
|
|
14515
15772
|
}
|
|
15773
|
+
/** Builds a proxy URL for a UUID without downloading or checking the skin. */
|
|
14516
15774
|
static async fromUuid(uuid) {
|
|
14517
15775
|
return this.getMcProxySkinUrl(uuid);
|
|
14518
15776
|
}
|
|
@@ -14524,6 +15782,7 @@ var Skins = class {
|
|
|
14524
15782
|
return void 0;
|
|
14525
15783
|
});
|
|
14526
15784
|
}
|
|
15785
|
+
/** Looks up a saved MineSkin skin and returns its texture URL, or `undefined` on lookup failure. */
|
|
14527
15786
|
static async fromMineSkin(mineskinId) {
|
|
14528
15787
|
return this.getMineSkin(mineskinId).then((mineskin) => {
|
|
14529
15788
|
if (mineskin) {
|
|
@@ -14534,12 +15793,14 @@ var Skins = class {
|
|
|
14534
15793
|
}
|
|
14535
15794
|
//</editor-fold>
|
|
14536
15795
|
//<editor-fold desc="Capes">
|
|
15796
|
+
/** Resolves a player's Minecraft cape URL for {@link SkinObject.setCapeTexture}. */
|
|
14537
15797
|
static async capeFromUuidOrUsername(uuidOrUsername) {
|
|
14538
15798
|
if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
|
|
14539
15799
|
return this.capeFromUuid(uuidOrUsername);
|
|
14540
15800
|
}
|
|
14541
15801
|
return this.capeFromUsername(uuidOrUsername);
|
|
14542
15802
|
}
|
|
15803
|
+
/** Builds a proxy URL without checking whether the player has a cape. */
|
|
14543
15804
|
static async capeFromUuid(uuid) {
|
|
14544
15805
|
return this.getMcProxyCapeUrl(uuid);
|
|
14545
15806
|
}
|
|
@@ -14668,6 +15929,195 @@ var Skins = class {
|
|
|
14668
15929
|
//</editor-fold>
|
|
14669
15930
|
};
|
|
14670
15931
|
|
|
15932
|
+
// src/scene/SceneDocumentLoader.ts
|
|
15933
|
+
var DocumentObjectScene = class extends MineRenderScene {
|
|
15934
|
+
async addSceneObject(_asset, supplier, _options, parent = this) {
|
|
15935
|
+
const object3 = await supplier();
|
|
15936
|
+
return this.initialize(object3, parent);
|
|
15937
|
+
}
|
|
15938
|
+
async initialize(object3, parent = this) {
|
|
15939
|
+
object3.scene = this;
|
|
15940
|
+
try {
|
|
15941
|
+
await object3.init();
|
|
15942
|
+
parent.add(object3);
|
|
15943
|
+
return object3;
|
|
15944
|
+
} catch (error) {
|
|
15945
|
+
object3.dispose();
|
|
15946
|
+
object3.removeFromParent();
|
|
15947
|
+
throw error;
|
|
15948
|
+
}
|
|
15949
|
+
}
|
|
15950
|
+
dispose() {
|
|
15951
|
+
for (const child of [...this.children]) {
|
|
15952
|
+
if (child.parent !== this) continue;
|
|
15953
|
+
if (isSceneObject(child)) child.dispose();
|
|
15954
|
+
child.removeFromParent();
|
|
15955
|
+
}
|
|
15956
|
+
}
|
|
15957
|
+
};
|
|
15958
|
+
async function skinTexture(value, cape = false) {
|
|
15959
|
+
if (!/^[a-zA-Z0-9_-]{1,36}$/.test(value)) return value;
|
|
15960
|
+
const texture = await (cape ? Skins.capeFromUuidOrUsername(value) : Skins.fromUuidOrUsername(value));
|
|
15961
|
+
if (!texture) throw new Error(`Could not find ${cape ? "cape" : "skin"} for "${value}"`);
|
|
15962
|
+
return texture;
|
|
15963
|
+
}
|
|
15964
|
+
var SceneDocumentLoader = class {
|
|
15965
|
+
static parse(value) {
|
|
15966
|
+
return parseSceneDocument(value);
|
|
15967
|
+
}
|
|
15968
|
+
/**
|
|
15969
|
+
* Loads objects concurrently and attaches them in document order after every load succeeds.
|
|
15970
|
+
* A failure waits for all loads, disposes staged objects, and reports the first error in document order.
|
|
15971
|
+
*/
|
|
15972
|
+
static async load(scene, value, parent = scene) {
|
|
15973
|
+
const definition = this.parse(value);
|
|
15974
|
+
if (definition.minecraftVersion !== void 0 && definition.minecraftVersion !== AssetLoader.version) {
|
|
15975
|
+
throw new Error(`Scene requires Minecraft ${definition.minecraftVersion}; call AssetLoader.setVersion("${definition.minecraftVersion}") before loading it`);
|
|
15976
|
+
}
|
|
15977
|
+
const root = new import_three38.Group();
|
|
15978
|
+
root.name = "MineRender scene document";
|
|
15979
|
+
const objects = [];
|
|
15980
|
+
let disposed = false;
|
|
15981
|
+
const loaded = {
|
|
15982
|
+
root,
|
|
15983
|
+
objects,
|
|
15984
|
+
definition,
|
|
15985
|
+
advanceAnimations: (delta) => {
|
|
15986
|
+
if (!disposed) for (const object3 of objects) object3.advanceAnimation(delta);
|
|
15987
|
+
},
|
|
15988
|
+
dispose: () => {
|
|
15989
|
+
if (disposed) return;
|
|
15990
|
+
disposed = true;
|
|
15991
|
+
root.removeFromParent();
|
|
15992
|
+
for (const object3 of objects) object3.dispose();
|
|
15993
|
+
root.clear();
|
|
15994
|
+
scene.dirty = true;
|
|
15995
|
+
}
|
|
15996
|
+
};
|
|
15997
|
+
try {
|
|
15998
|
+
const results = await Promise.allSettled(definition.objects.map((object3) => this.loadValidatedObject(scene, object3)));
|
|
15999
|
+
for (const result of results) if (result.status === "fulfilled") objects.push(result.value);
|
|
16000
|
+
const failure = results.find((result) => result.status === "rejected");
|
|
16001
|
+
if (failure) throw failure.reason;
|
|
16002
|
+
for (const object3 of objects) root.add(object3.root);
|
|
16003
|
+
parent.add(root);
|
|
16004
|
+
scene.dirty = true;
|
|
16005
|
+
return loaded;
|
|
16006
|
+
} catch (error) {
|
|
16007
|
+
loaded.dispose();
|
|
16008
|
+
throw error;
|
|
16009
|
+
}
|
|
16010
|
+
}
|
|
16011
|
+
/** Loads an independently editable object. Its root owns the document's position, rotation, and scale. */
|
|
16012
|
+
static async loadObject(scene, value, parent = scene) {
|
|
16013
|
+
const definition = this.parse({ format: "minerender-scene", version: 1, objects: [value] }).objects[0];
|
|
16014
|
+
return this.loadValidatedObject(scene, definition, parent);
|
|
16015
|
+
}
|
|
16016
|
+
static async loadValidatedObject(scene, definition, parent) {
|
|
16017
|
+
const root = new import_three38.Group();
|
|
16018
|
+
root.name = definition.name ?? definition.id;
|
|
16019
|
+
root.userData.sceneObjectId = definition.id;
|
|
16020
|
+
root.position.fromArray(definition.position ?? [0, 0, 0]);
|
|
16021
|
+
root.rotation.set(...definition.rotation ?? [0, 0, 0]);
|
|
16022
|
+
root.scale.fromArray(definition.scale ?? [1, 1, 1]);
|
|
16023
|
+
root.visible = definition.visible ?? true;
|
|
16024
|
+
const content = new DocumentObjectScene();
|
|
16025
|
+
root.add(content);
|
|
16026
|
+
let object3;
|
|
16027
|
+
try {
|
|
16028
|
+
switch (definition.type) {
|
|
16029
|
+
case "skin": {
|
|
16030
|
+
const skin = new SkinObject({ ...definition.options, instanceMeshes: false });
|
|
16031
|
+
object3 = skin;
|
|
16032
|
+
const texture = definition.skin ? await skinTexture(definition.skin) : `${AssetLoader.ROOT}/assets/minecraft/textures/entity/player/${definition.options?.slim ? "slim/alex" : "wide/steve"}.png`;
|
|
16033
|
+
await skin.setSkinTexture(texture);
|
|
16034
|
+
await content.initialize(skin);
|
|
16035
|
+
if (definition.cape) await skin.setCapeTexture(await skinTexture(definition.cape.texture, true), definition.cape.layout);
|
|
16036
|
+
for (const [part, rotation] of Object.entries(definition.pose ?? {})) {
|
|
16037
|
+
skin.getGroupByName(part)?.rotation.set(...rotation, "XYZ");
|
|
16038
|
+
}
|
|
16039
|
+
for (const part of definition.hiddenParts ?? []) {
|
|
16040
|
+
const mesh = skin.getMeshByName(part);
|
|
16041
|
+
if (mesh) mesh.visible = false;
|
|
16042
|
+
}
|
|
16043
|
+
break;
|
|
16044
|
+
}
|
|
16045
|
+
case "block": {
|
|
16046
|
+
const state = await BlockStates.get(AssetKey.parse("blockstates", definition.asset));
|
|
16047
|
+
if (!state) throw new Error(`Could not load block ${definition.asset}`);
|
|
16048
|
+
object3 = await content.addBlock(state, { ...definition.options, initialState: definition.state, instanceMeshes: false });
|
|
16049
|
+
break;
|
|
16050
|
+
}
|
|
16051
|
+
case "item":
|
|
16052
|
+
case "model": {
|
|
16053
|
+
let key = AssetKey.parse("models", definition.asset);
|
|
16054
|
+
if (definition.type === "item" && key.type !== "item") {
|
|
16055
|
+
key = new AssetKey(key.namespace, key.getFullPath(), "models", "item");
|
|
16056
|
+
}
|
|
16057
|
+
const model = await Models.getMerged(key);
|
|
16058
|
+
if (!model) throw new Error(`Could not load ${definition.type} ${definition.asset}`);
|
|
16059
|
+
object3 = await content.addModel(model, {
|
|
16060
|
+
...definition.type === "item" ? { displayPosition: "gui" /* GUI */ } : {},
|
|
16061
|
+
...definition.options,
|
|
16062
|
+
instanceMeshes: false
|
|
16063
|
+
});
|
|
16064
|
+
break;
|
|
16065
|
+
}
|
|
16066
|
+
case "entity": {
|
|
16067
|
+
const key = AssetKey.parse("entity-models", definition.asset);
|
|
16068
|
+
const model = await Entities.getEntity(key, definition.texture ? AssetKey.parse("textures", definition.texture) : void 0, {
|
|
16069
|
+
layers: definition.layers,
|
|
16070
|
+
when: definition.when,
|
|
16071
|
+
textures: definition.textures && Object.fromEntries(Object.entries(definition.textures).map(([name, texture]) => [name, AssetKey.parse("textures", texture)]))
|
|
16072
|
+
});
|
|
16073
|
+
if (!model) throw new Error(`Could not load entity ${definition.asset}`);
|
|
16074
|
+
const entity = await content.addEntity(model, { ...definition.options, instanceMeshes: false });
|
|
16075
|
+
object3 = entity;
|
|
16076
|
+
if (definition.animation) {
|
|
16077
|
+
const animations = await Entities.getAnimations(key);
|
|
16078
|
+
const clip = animations?.[definition.animation.name];
|
|
16079
|
+
if (!clip) throw new Error(`Entity ${definition.asset} has no animation "${definition.animation.name}"`);
|
|
16080
|
+
entity.playAnimation(clip, definition.animation);
|
|
16081
|
+
}
|
|
16082
|
+
break;
|
|
16083
|
+
}
|
|
16084
|
+
case "gui":
|
|
16085
|
+
object3 = await content.initialize(new GuiObject(definition.layers));
|
|
16086
|
+
break;
|
|
16087
|
+
}
|
|
16088
|
+
let disposed = false;
|
|
16089
|
+
const loaded = {
|
|
16090
|
+
root,
|
|
16091
|
+
object: object3,
|
|
16092
|
+
definition,
|
|
16093
|
+
advanceAnimation: (delta) => {
|
|
16094
|
+
if (!disposed && definition.type === "entity" && !definition.animation?.paused) object3.advanceAnimation(delta);
|
|
16095
|
+
},
|
|
16096
|
+
dispose: () => {
|
|
16097
|
+
if (disposed) return;
|
|
16098
|
+
disposed = true;
|
|
16099
|
+
root.removeFromParent();
|
|
16100
|
+
object3.dispose();
|
|
16101
|
+
object3.removeFromParent();
|
|
16102
|
+
content.dispose();
|
|
16103
|
+
root.clear();
|
|
16104
|
+
scene.dirty = true;
|
|
16105
|
+
}
|
|
16106
|
+
};
|
|
16107
|
+
parent?.add(root);
|
|
16108
|
+
scene.dirty = true;
|
|
16109
|
+
return loaded;
|
|
16110
|
+
} catch (error) {
|
|
16111
|
+
object3?.dispose();
|
|
16112
|
+
object3?.removeFromParent();
|
|
16113
|
+
content.dispose();
|
|
16114
|
+
root.removeFromParent();
|
|
16115
|
+
root.clear();
|
|
16116
|
+
throw new Error(`Scene object "${definition.name ?? definition.id}": ${error instanceof Error ? error.message : String(error)}`);
|
|
16117
|
+
}
|
|
16118
|
+
}
|
|
16119
|
+
};
|
|
16120
|
+
|
|
14671
16121
|
// src/texture/index.ts
|
|
14672
16122
|
init_Colormaps();
|
|
14673
16123
|
init_TextureAtlas();
|
|
@@ -14694,16 +16144,19 @@ var ChunkData = class _ChunkData {
|
|
|
14694
16144
|
this.freeIds = [];
|
|
14695
16145
|
this.nbt = /* @__PURE__ */ new Map();
|
|
14696
16146
|
}
|
|
16147
|
+
/** Returns a detached copy of a cell, or `undefined` for air. Indices range from 0 to 4095. */
|
|
14697
16148
|
get(index) {
|
|
14698
16149
|
_ChunkData.validateIndex(index);
|
|
14699
16150
|
const entry = this.palette[this.indices[index]];
|
|
14700
16151
|
return entry ? _ChunkData.copyBlock(entry.state, this.nbt.get(index)) : void 0;
|
|
14701
16152
|
}
|
|
16153
|
+
/** Captures a cell's data and returns a copy-producing reader that survives later cell changes. */
|
|
14702
16154
|
snapshot(index) {
|
|
14703
16155
|
_ChunkData.validateIndex(index);
|
|
14704
16156
|
const entry = this.palette[this.indices[index]];
|
|
14705
16157
|
return entry ? _ChunkData.copyBlock.bind(void 0, entry.state, this.nbt.get(index)) : void 0;
|
|
14706
16158
|
}
|
|
16159
|
+
/** Copies block data into a cell. An omitted block or an air block clears the cell. */
|
|
14707
16160
|
set(index, block) {
|
|
14708
16161
|
_ChunkData.validateIndex(index);
|
|
14709
16162
|
let state;
|
|
@@ -14768,15 +16221,23 @@ var ChunkData = class _ChunkData {
|
|
|
14768
16221
|
|
|
14769
16222
|
// src/world/AnvilParser.ts
|
|
14770
16223
|
var AnvilParser = class {
|
|
16224
|
+
/** Lists occupied chunk positions within the region, using local coordinates from 0 to 31. */
|
|
14771
16225
|
static getChunkList(data) {
|
|
14772
16226
|
return this.locations(this.bytes(data)).map(({ x, z }) => ({ x, z }));
|
|
14773
16227
|
}
|
|
16228
|
+
/** Decodes all stored chunks. Gzip, zlib, and uncompressed payloads are supported. */
|
|
14774
16229
|
static async parse(data) {
|
|
14775
16230
|
const bytes = this.bytes(data);
|
|
14776
16231
|
const chunks = [];
|
|
14777
16232
|
for (const location of this.locations(bytes)) chunks.push(await this.readChunk(bytes, location));
|
|
14778
16233
|
return { chunks };
|
|
14779
16234
|
}
|
|
16235
|
+
/**
|
|
16236
|
+
* Decodes one chunk for {@link MineRenderWorld.placeChunk}.
|
|
16237
|
+
* @param localX - Chunk x within the region, from 0 to 31.
|
|
16238
|
+
* @param localZ - Chunk z within the region, from 0 to 31.
|
|
16239
|
+
* @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
|
|
16240
|
+
*/
|
|
14780
16241
|
static async parseChunk(data, localX, localZ) {
|
|
14781
16242
|
if (![localX, localZ].every((value) => Number.isInteger(value) && value >= 0 && value < 32)) {
|
|
14782
16243
|
throw new RangeError("Region-local chunk coordinates must be integers from 0 to 31");
|
|
@@ -14938,12 +16399,12 @@ var AnvilParser = class {
|
|
|
14938
16399
|
};
|
|
14939
16400
|
|
|
14940
16401
|
// src/world/Chunk.ts
|
|
14941
|
-
var
|
|
16402
|
+
var import_three42 = require("three");
|
|
14942
16403
|
init_BlockStates();
|
|
14943
16404
|
init_AssetKey();
|
|
14944
16405
|
|
|
14945
16406
|
// src/world/MineRenderWorld.ts
|
|
14946
|
-
var
|
|
16407
|
+
var import_three40 = require("three");
|
|
14947
16408
|
init_Model();
|
|
14948
16409
|
init_BatchedExecutor();
|
|
14949
16410
|
init_AssetKey();
|
|
@@ -14951,7 +16412,7 @@ init_BlockStates();
|
|
|
14951
16412
|
init_CubeFace();
|
|
14952
16413
|
|
|
14953
16414
|
// src/world/SectionModels.ts
|
|
14954
|
-
var
|
|
16415
|
+
var import_three39 = require("three");
|
|
14955
16416
|
init_AssetKey();
|
|
14956
16417
|
init_Models();
|
|
14957
16418
|
init_CubeFace();
|
|
@@ -14981,6 +16442,7 @@ var SectionModels = class {
|
|
|
14981
16442
|
this.templates = /* @__PURE__ */ new Set();
|
|
14982
16443
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
|
|
14983
16444
|
}
|
|
16445
|
+
/** Returns a cube template, or `undefined` when the block requires an individual render object. */
|
|
14984
16446
|
async get(blockState, properties = {}) {
|
|
14985
16447
|
if (!blockState.variants || blockState.multipart) return void 0;
|
|
14986
16448
|
let states = this.states.get(blockState);
|
|
@@ -15009,20 +16471,20 @@ var SectionModels = class {
|
|
|
15009
16471
|
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
15010
16472
|
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
15011
16473
|
const template = await cached(models, key, async () => {
|
|
15012
|
-
const
|
|
16474
|
+
const object3 = new SectionModel(model, {
|
|
15013
16475
|
instanceMeshes: false,
|
|
15014
16476
|
mergeMeshes: false,
|
|
15015
16477
|
tints,
|
|
15016
16478
|
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
15017
16479
|
});
|
|
15018
|
-
await
|
|
15019
|
-
const geometry =
|
|
15020
|
-
geometry.applyMatrix4(new
|
|
15021
|
-
|
|
16480
|
+
await object3.init();
|
|
16481
|
+
const geometry = object3.children[0].geometry;
|
|
16482
|
+
geometry.applyMatrix4(new import_three39.Matrix4().makeRotationFromEuler(rotation));
|
|
16483
|
+
object3.clear();
|
|
15022
16484
|
const cullFaces = CUBE_FACES.map((face) => {
|
|
15023
16485
|
const local = CUBE_FACES.findIndex((name) => name === atlas.model.elements[0].faces[face]?.cullface);
|
|
15024
16486
|
if (local < 0) return 0;
|
|
15025
|
-
const normal = new
|
|
16487
|
+
const normal = new import_three39.Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
|
|
15026
16488
|
const world = CUBE_FACE_OFFSETS.findIndex(([x, y, z]) => Math.abs(normal.x - x) < 1e-6 && Math.abs(normal.y - y) < 1e-6 && Math.abs(normal.z - z) < 1e-6);
|
|
15027
16489
|
return 1 << world;
|
|
15028
16490
|
});
|
|
@@ -15034,6 +16496,7 @@ var SectionModels = class {
|
|
|
15034
16496
|
}
|
|
15035
16497
|
return { variants, templates };
|
|
15036
16498
|
}
|
|
16499
|
+
/** Disposes prepared geometry and clears this world's template caches. */
|
|
15037
16500
|
clear() {
|
|
15038
16501
|
for (const template of this.templates) template.geometry.dispose();
|
|
15039
16502
|
this.templates.clear();
|
|
@@ -15052,20 +16515,20 @@ var MineRenderWorld = class {
|
|
|
15052
16515
|
}
|
|
15053
16516
|
getBlockAt(posOrX, y, z) {
|
|
15054
16517
|
if (typeof posOrX == "number") {
|
|
15055
|
-
return this.getBlockAt(new
|
|
16518
|
+
return this.getBlockAt(new import_three40.Vector3(posOrX, y, z));
|
|
15056
16519
|
}
|
|
15057
16520
|
if (isTripleArray(posOrX)) {
|
|
15058
|
-
return this.getBlockAt(new
|
|
16521
|
+
return this.getBlockAt(new import_three40.Vector3(posOrX[0], posOrX[1], posOrX[2]));
|
|
15059
16522
|
}
|
|
15060
16523
|
this.validatePosBounds(posOrX);
|
|
15061
16524
|
return this.getChunkAt(posOrX)?.getBlockAt(posOrX);
|
|
15062
16525
|
}
|
|
15063
16526
|
async setBlockAt(posOrX, yOrBlock, z, block) {
|
|
15064
16527
|
if (typeof posOrX == "number") {
|
|
15065
|
-
return this.setBlockAt(new
|
|
16528
|
+
return this.setBlockAt(new import_three40.Vector3(posOrX, yOrBlock, z), block);
|
|
15066
16529
|
}
|
|
15067
16530
|
if (isTripleArray(posOrX)) {
|
|
15068
|
-
return this.setBlockAt(new
|
|
16531
|
+
return this.setBlockAt(new import_three40.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
|
|
15069
16532
|
}
|
|
15070
16533
|
return this.placeBlock(posOrX, yOrBlock);
|
|
15071
16534
|
}
|
|
@@ -15076,14 +16539,21 @@ var MineRenderWorld = class {
|
|
|
15076
16539
|
}
|
|
15077
16540
|
async setBlockVisibleAt(posOrX, yOrVisible, z, visible) {
|
|
15078
16541
|
if (typeof posOrX === "number") {
|
|
15079
|
-
return this.setBlockVisibleAt(new
|
|
16542
|
+
return this.setBlockVisibleAt(new import_three40.Vector3(posOrX, yOrVisible, z), visible);
|
|
15080
16543
|
}
|
|
15081
16544
|
if (isTripleArray(posOrX)) {
|
|
15082
|
-
return this.setBlockVisibleAt(new
|
|
16545
|
+
return this.setBlockVisibleAt(new import_three40.Vector3(...posOrX), yOrVisible);
|
|
15083
16546
|
}
|
|
15084
16547
|
this.validatePosBounds(posOrX);
|
|
15085
16548
|
await this.getChunkAt(posOrX)?.setBlockVisibleAt(posOrX, yOrVisible);
|
|
15086
16549
|
}
|
|
16550
|
+
/**
|
|
16551
|
+
* Places a structure's blocks at their stored positions, then refreshes neighboring faces.
|
|
16552
|
+
* Entity NBT is retained by the structure but is not rendered.
|
|
16553
|
+
*
|
|
16554
|
+
* @param useBatches - Uses bounded batches by default. Set to `false` for sequential placement.
|
|
16555
|
+
* @param executor - Optional queue for batched placement. The caller remains responsible for stopping it.
|
|
16556
|
+
*/
|
|
15087
16557
|
async placeMultiBlock(multiblock, useBatches = true, executor) {
|
|
15088
16558
|
const changes = /* @__PURE__ */ new Map();
|
|
15089
16559
|
try {
|
|
@@ -15096,7 +16566,7 @@ var MineRenderWorld = class {
|
|
|
15096
16566
|
const collect = async (positions2) => {
|
|
15097
16567
|
for (const pos of positions2) changes.set(pos.toArray().join(","), pos);
|
|
15098
16568
|
};
|
|
15099
|
-
const place = (block) => this.placeBlock(new
|
|
16569
|
+
const place = (block) => this.placeBlock(new import_three40.Vector3(...block.position), block, collect);
|
|
15100
16570
|
const keys = /* @__PURE__ */ new Map();
|
|
15101
16571
|
for (const block of multiblock.blocks) {
|
|
15102
16572
|
if (Chunk.isAir(block)) continue;
|
|
@@ -15135,13 +16605,7 @@ var MineRenderWorld = class {
|
|
|
15135
16605
|
async placeChunk(chunk, executor) {
|
|
15136
16606
|
const changes = /* @__PURE__ */ new Map();
|
|
15137
16607
|
try {
|
|
15138
|
-
|
|
15139
|
-
for (const [key, section] of previous) {
|
|
15140
|
-
this._chunks.delete(key);
|
|
15141
|
-
await section.clear(async (positions) => {
|
|
15142
|
-
for (const pos of positions) changes.set(pos.toArray().join(","), pos);
|
|
15143
|
-
});
|
|
15144
|
-
}
|
|
16608
|
+
await this.clearChunkColumn(chunk.x, chunk.z, changes);
|
|
15145
16609
|
for (const section of chunk.sections) {
|
|
15146
16610
|
const blocks = [];
|
|
15147
16611
|
for (let index = 0; index < 4096; index++) {
|
|
@@ -15161,6 +16625,32 @@ var MineRenderWorld = class {
|
|
|
15161
16625
|
await this.updateCulling([...changes.values()]);
|
|
15162
16626
|
}
|
|
15163
16627
|
}
|
|
16628
|
+
/**
|
|
16629
|
+
* Removes all sections at integer chunk-column coordinates, then refreshes neighboring faces and fluids.
|
|
16630
|
+
* Coordinates match AnvilChunk.x/z, not block positions. Missing columns are ignored.
|
|
16631
|
+
*/
|
|
16632
|
+
async unloadChunkColumn(x, z) {
|
|
16633
|
+
if (!Number.isInteger(x) || !Number.isInteger(z)) {
|
|
16634
|
+
throw new RangeError("Chunk column coordinates must be integers");
|
|
16635
|
+
}
|
|
16636
|
+
await this.culling;
|
|
16637
|
+
const changes = /* @__PURE__ */ new Map();
|
|
16638
|
+
try {
|
|
16639
|
+
await this.clearChunkColumn(x, z, changes);
|
|
16640
|
+
} finally {
|
|
16641
|
+
await this.updateCulling([...changes.values()]);
|
|
16642
|
+
}
|
|
16643
|
+
}
|
|
16644
|
+
async clearChunkColumn(x, z, changes) {
|
|
16645
|
+
const previous = [...this._chunks.entries()].filter(([, section]) => section.x === x && section.z === z);
|
|
16646
|
+
for (const [key, section] of previous) {
|
|
16647
|
+
this._chunks.delete(key);
|
|
16648
|
+
await section.clear(async (positions) => {
|
|
16649
|
+
for (const pos of positions) changes.set(pos.toArray().join(","), pos);
|
|
16650
|
+
});
|
|
16651
|
+
}
|
|
16652
|
+
}
|
|
16653
|
+
/** Removes all chunks and releases their block placements and section meshes. */
|
|
15164
16654
|
async clear() {
|
|
15165
16655
|
await this.culling;
|
|
15166
16656
|
const chunks = [...this._chunks.values()];
|
|
@@ -15174,13 +16664,13 @@ var MineRenderWorld = class {
|
|
|
15174
16664
|
for (const pos of positions) {
|
|
15175
16665
|
this.pendingCulling.set(pos.toArray().join(","), pos.clone());
|
|
15176
16666
|
for (const offset of CUBE_FACE_OFFSETS) {
|
|
15177
|
-
const neighbor = pos.clone().add(new
|
|
16667
|
+
const neighbor = pos.clone().add(new import_three40.Vector3(...offset));
|
|
15178
16668
|
this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
|
|
15179
16669
|
}
|
|
15180
16670
|
for (let y = -1; y <= 1; y++) {
|
|
15181
16671
|
for (let z = -1; z <= 1; z++) {
|
|
15182
16672
|
for (let x = -1; x <= 1; x++) {
|
|
15183
|
-
const neighbor = pos.clone().add(new
|
|
16673
|
+
const neighbor = pos.clone().add(new import_three40.Vector3(x, y, z));
|
|
15184
16674
|
if (this.getBlockAt(neighbor)?.object?.fluidKind) {
|
|
15185
16675
|
this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
|
|
15186
16676
|
}
|
|
@@ -15204,19 +16694,19 @@ var MineRenderWorld = class {
|
|
|
15204
16694
|
}
|
|
15205
16695
|
if (block.object?.fluidKind) {
|
|
15206
16696
|
await block.object.updateFluid((x, y, z) => {
|
|
15207
|
-
const neighbor = pos.clone().add(new
|
|
16697
|
+
const neighbor = pos.clone().add(new import_three40.Vector3(x, y, z));
|
|
15208
16698
|
const section = this.getChunkAt(neighbor);
|
|
15209
|
-
const
|
|
16699
|
+
const object3 = section?.isBlockVisibleAt(neighbor) ? section.getBlockAt(neighbor)?.object : void 0;
|
|
15210
16700
|
return {
|
|
15211
|
-
fluid:
|
|
15212
|
-
level:
|
|
16701
|
+
fluid: object3?.fluidKind,
|
|
16702
|
+
level: object3?.fluidLevel,
|
|
15213
16703
|
solid: section?.isOccludingAt(neighbor) ?? false
|
|
15214
16704
|
};
|
|
15215
16705
|
});
|
|
15216
16706
|
}
|
|
15217
16707
|
let mask = 0;
|
|
15218
16708
|
for (const [face, offset] of CUBE_FACE_OFFSETS.entries()) {
|
|
15219
|
-
const neighbor = pos.clone().add(new
|
|
16709
|
+
const neighbor = pos.clone().add(new import_three40.Vector3(...offset));
|
|
15220
16710
|
if (this.getChunkAt(neighbor)?.isOccludingAt(neighbor)) {
|
|
15221
16711
|
mask |= 1 << face;
|
|
15222
16712
|
}
|
|
@@ -15246,18 +16736,21 @@ var MineRenderWorld = class {
|
|
|
15246
16736
|
}
|
|
15247
16737
|
return chunk;
|
|
15248
16738
|
}
|
|
16739
|
+
/** Returns the loaded chunk containing a world block position, without creating a chunk. */
|
|
15249
16740
|
getChunkAt(pos) {
|
|
15250
16741
|
return this._chunks.get(this.worldPosToChunkKey(pos));
|
|
15251
16742
|
}
|
|
16743
|
+
/** Converts block coordinates to scene units by multiplying each component by 16. */
|
|
15252
16744
|
static worldToScenePosition(pos) {
|
|
15253
|
-
return new
|
|
16745
|
+
return new import_three40.Vector3(
|
|
15254
16746
|
pos.x * 16,
|
|
15255
16747
|
pos.y * 16,
|
|
15256
16748
|
pos.z * 16
|
|
15257
16749
|
);
|
|
15258
16750
|
}
|
|
16751
|
+
/** Converts scene units to block coordinates without rounding to an integer block. */
|
|
15259
16752
|
static sceneToWorldPosition(pos) {
|
|
15260
|
-
return new
|
|
16753
|
+
return new import_three40.Vector3(
|
|
15261
16754
|
pos.x / 16,
|
|
15262
16755
|
pos.y / 16,
|
|
15263
16756
|
pos.z / 16
|
|
@@ -15280,14 +16773,15 @@ var MineRenderWorld = class {
|
|
|
15280
16773
|
init_Model();
|
|
15281
16774
|
|
|
15282
16775
|
// src/world/SectionMesh.ts
|
|
15283
|
-
var
|
|
16776
|
+
var import_three41 = require("three");
|
|
15284
16777
|
init_CanvasCompat();
|
|
15285
16778
|
init_Materials();
|
|
15286
|
-
var SectionMesh = class _SectionMesh extends
|
|
16779
|
+
var SectionMesh = class _SectionMesh extends import_three41.Group {
|
|
15287
16780
|
constructor() {
|
|
15288
16781
|
super(...arguments);
|
|
15289
16782
|
this.ownedMeshes = [];
|
|
15290
16783
|
}
|
|
16784
|
+
/** Builds section-local meshes from visible cube faces. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
15291
16785
|
static build(entries, maxAtlasSize = 2048) {
|
|
15292
16786
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
15293
16787
|
const visible = entries.filter((entry) => entry.template.cullFaces.some((direction) => !(entry.cullMask & direction)));
|
|
@@ -15329,14 +16823,17 @@ var SectionMesh = class _SectionMesh extends import_three40.Group {
|
|
|
15329
16823
|
for (const { atlas, x, y } of page.placements) {
|
|
15330
16824
|
context.drawImage(atlas.image.canvas, x, y);
|
|
15331
16825
|
}
|
|
15332
|
-
const positions = [], normals = [], uvs = [], colors = [], indices = [];
|
|
16826
|
+
const positions = [], normals = [], uvs = [], uvBounds = [], colors = [], indices = [];
|
|
15333
16827
|
for (const { index, template, cullMask } of page.entries) {
|
|
15334
16828
|
const position = template.geometry.getAttribute("position");
|
|
15335
16829
|
const normal = template.geometry.getAttribute("normal");
|
|
15336
16830
|
const uv = template.geometry.getAttribute("uv");
|
|
15337
|
-
const
|
|
16831
|
+
const bounds = template.geometry.getAttribute("uvBounds");
|
|
16832
|
+
const color2 = template.geometry.getAttribute("color");
|
|
15338
16833
|
const sourceIndices = template.geometry.getIndex();
|
|
15339
16834
|
const placement = locations.get(template.atlas).placement;
|
|
16835
|
+
const mapU = (u) => (placement.x + u * template.atlas.image.width) / page.width;
|
|
16836
|
+
const mapV = (v) => 1 - (placement.y + (1 - v) * template.atlas.image.height) / page.height;
|
|
15340
16837
|
const offsetX = index % 16 * 16;
|
|
15341
16838
|
const offsetY = Math.floor(index / 256) * 16;
|
|
15342
16839
|
const offsetZ = Math.floor(index / 16) % 16 * 16;
|
|
@@ -15347,27 +16844,31 @@ var SectionMesh = class _SectionMesh extends import_three40.Group {
|
|
|
15347
16844
|
const vertex = face * 4 + corner;
|
|
15348
16845
|
positions.push(position.getX(vertex) + offsetX, position.getY(vertex) + offsetY, position.getZ(vertex) + offsetZ);
|
|
15349
16846
|
normals.push(normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex));
|
|
15350
|
-
uvs.push(
|
|
15351
|
-
|
|
15352
|
-
|
|
16847
|
+
uvs.push(mapU(uv.getX(vertex)), mapV(uv.getY(vertex)));
|
|
16848
|
+
uvBounds.push(
|
|
16849
|
+
mapU(bounds?.getX(vertex) ?? 0),
|
|
16850
|
+
mapV(bounds?.getY(vertex) ?? 0),
|
|
16851
|
+
mapU(bounds?.getZ(vertex) ?? 1),
|
|
16852
|
+
mapV(bounds?.getW(vertex) ?? 1)
|
|
15353
16853
|
);
|
|
15354
|
-
colors.push(
|
|
16854
|
+
colors.push(color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1);
|
|
15355
16855
|
}
|
|
15356
16856
|
for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
|
|
15357
16857
|
}
|
|
15358
16858
|
}
|
|
15359
|
-
const geometry = new
|
|
15360
|
-
geometry.setAttribute("position", new
|
|
15361
|
-
geometry.setAttribute("normal", new
|
|
15362
|
-
geometry.setAttribute("uv", new
|
|
15363
|
-
geometry.setAttribute("
|
|
16859
|
+
const geometry = new import_three41.BufferGeometry();
|
|
16860
|
+
geometry.setAttribute("position", new import_three41.Float32BufferAttribute(positions, 3));
|
|
16861
|
+
geometry.setAttribute("normal", new import_three41.Float32BufferAttribute(normals, 3));
|
|
16862
|
+
geometry.setAttribute("uv", new import_three41.Float32BufferAttribute(uvs, 2));
|
|
16863
|
+
geometry.setAttribute("uvBounds", new import_three41.Float32BufferAttribute(uvBounds, 4));
|
|
16864
|
+
geometry.setAttribute("color", new import_three41.Float32BufferAttribute(colors, 3));
|
|
15364
16865
|
geometry.setIndex(indices);
|
|
15365
16866
|
geometry.computeBoundingBox();
|
|
15366
16867
|
geometry.computeBoundingSphere();
|
|
15367
|
-
const material = Materials.createShadedCanvasMaterial(canvas, false);
|
|
16868
|
+
const material = Materials.createShadedCanvasMaterial(canvas, false, false, true);
|
|
15368
16869
|
material.vertexColors = true;
|
|
15369
16870
|
const texture = material.uniforms?.map?.value ?? material.map;
|
|
15370
|
-
const mesh = new
|
|
16871
|
+
const mesh = new import_three41.Mesh(geometry, material);
|
|
15371
16872
|
section.add(mesh);
|
|
15372
16873
|
section.ownedMeshes.push({ mesh, texture });
|
|
15373
16874
|
}
|
|
@@ -15403,10 +16904,10 @@ var Chunk = class _Chunk {
|
|
|
15403
16904
|
}
|
|
15404
16905
|
getBlockAt(posOrX, y, z) {
|
|
15405
16906
|
if (typeof posOrX == "number") {
|
|
15406
|
-
return this.getBlockAt(new
|
|
16907
|
+
return this.getBlockAt(new import_three42.Vector3(posOrX, y, z));
|
|
15407
16908
|
}
|
|
15408
16909
|
if (isTripleArray(posOrX)) {
|
|
15409
|
-
return this.getBlockAt(new
|
|
16910
|
+
return this.getBlockAt(new import_three42.Vector3(posOrX[0], posOrX[1], posOrX[2]));
|
|
15410
16911
|
}
|
|
15411
16912
|
const pos = this.worldPosToChunkPos(posOrX);
|
|
15412
16913
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
@@ -15414,10 +16915,10 @@ var Chunk = class _Chunk {
|
|
|
15414
16915
|
}
|
|
15415
16916
|
async setBlockAt(posOrX, yOrBlock, z, block) {
|
|
15416
16917
|
if (typeof posOrX == "number") {
|
|
15417
|
-
return this.setBlockAt(new
|
|
16918
|
+
return this.setBlockAt(new import_three42.Vector3(posOrX, yOrBlock, z), block);
|
|
15418
16919
|
}
|
|
15419
16920
|
if (isTripleArray(posOrX)) {
|
|
15420
|
-
return this.setBlockAt(new
|
|
16921
|
+
return this.setBlockAt(new import_three42.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
|
|
15421
16922
|
}
|
|
15422
16923
|
const worldPos = posOrX;
|
|
15423
16924
|
const pos = this.worldPosToChunkPos(worldPos);
|
|
@@ -15425,7 +16926,7 @@ var Chunk = class _Chunk {
|
|
|
15425
16926
|
return this.setBlockInChunkAt(pos, block, worldPos);
|
|
15426
16927
|
}
|
|
15427
16928
|
/**
|
|
15428
|
-
*
|
|
16929
|
+
* Places a block at integer chunk-local coordinates from 0 to 15.
|
|
15429
16930
|
*/
|
|
15430
16931
|
async setBlockInChunkAt(pos, block, worldPos, onBlocksChanged = this.onBlocksChanged) {
|
|
15431
16932
|
if (typeof worldPos === "undefined") {
|
|
@@ -15438,7 +16939,7 @@ var Chunk = class _Chunk {
|
|
|
15438
16939
|
this.renderedBlocks.get(index)?.object?.removeFromScene();
|
|
15439
16940
|
if (this.sectionBlocks.delete(index)) this.meshDirty = true;
|
|
15440
16941
|
this.renderedBlocks.delete(index);
|
|
15441
|
-
let
|
|
16942
|
+
let object3;
|
|
15442
16943
|
try {
|
|
15443
16944
|
if (!readBlock) return void 0;
|
|
15444
16945
|
const stored = readBlock();
|
|
@@ -15453,27 +16954,28 @@ var Chunk = class _Chunk {
|
|
|
15453
16954
|
this.sectionBlocks.set(index, { index, template, cullMask: 0 });
|
|
15454
16955
|
this.meshDirty = true;
|
|
15455
16956
|
} else {
|
|
15456
|
-
|
|
16957
|
+
object3 = await this.scene.addBlock(blockState, {
|
|
15457
16958
|
mergeMeshes: true,
|
|
15458
16959
|
instanceMeshes: true,
|
|
15459
16960
|
maxInstanceCount: 2e3,
|
|
15460
16961
|
initialState: stored.properties
|
|
15461
16962
|
});
|
|
15462
|
-
|
|
16963
|
+
object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
15463
16964
|
}
|
|
15464
16965
|
const info = { get block() {
|
|
15465
16966
|
return readBlock();
|
|
15466
|
-
}, object };
|
|
16967
|
+
}, object: object3 };
|
|
15467
16968
|
this.renderedBlocks.set(index, info);
|
|
15468
16969
|
return info;
|
|
15469
16970
|
} catch (error) {
|
|
15470
16971
|
this.data.set(index, void 0);
|
|
15471
|
-
|
|
16972
|
+
object3?.removeFromScene();
|
|
15472
16973
|
throw error;
|
|
15473
16974
|
} finally {
|
|
15474
16975
|
await onBlocksChanged?.([worldPos]);
|
|
15475
16976
|
}
|
|
15476
16977
|
}
|
|
16978
|
+
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
15477
16979
|
async setBlockVisibleAt(pos, visible) {
|
|
15478
16980
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
15479
16981
|
const block = this.renderedBlocks.get(index);
|
|
@@ -15527,9 +17029,10 @@ var Chunk = class _Chunk {
|
|
|
15527
17029
|
this.scene.dirty = true;
|
|
15528
17030
|
this.meshDirty = false;
|
|
15529
17031
|
}
|
|
17032
|
+
/** Removes stored blocks and their render objects, then notifies the owning world of changed positions. */
|
|
15530
17033
|
async clear(onBlocksChanged = this.onBlocksChanged) {
|
|
15531
17034
|
const positions = [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
|
|
15532
|
-
new
|
|
17035
|
+
new import_three42.Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
|
|
15533
17036
|
));
|
|
15534
17037
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
15535
17038
|
this.sectionBlocks.clear();
|
|
@@ -15547,6 +17050,7 @@ var Chunk = class _Chunk {
|
|
|
15547
17050
|
static isAir(block) {
|
|
15548
17051
|
return ChunkData.isAir(block);
|
|
15549
17052
|
}
|
|
17053
|
+
/** Maps chunk-local coordinates from 0 to 15 to a storage index, with x varying fastest, then z, then y. */
|
|
15550
17054
|
static chunkPosToBlockIndex(pos) {
|
|
15551
17055
|
if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
|
|
15552
17056
|
throw new RangeError("Block coordinates must be integers from 0 to 15 within a chunk");
|
|
@@ -15554,14 +17058,14 @@ var Chunk = class _Chunk {
|
|
|
15554
17058
|
return pos.y * 16 * 16 + pos.z * 16 + pos.x;
|
|
15555
17059
|
}
|
|
15556
17060
|
worldPosToChunkPos(pos) {
|
|
15557
|
-
return new
|
|
17061
|
+
return new import_three42.Vector3(
|
|
15558
17062
|
pos.x - this.x * 16,
|
|
15559
17063
|
pos.y - this.y * 16,
|
|
15560
17064
|
pos.z - this.z * 16
|
|
15561
17065
|
);
|
|
15562
17066
|
}
|
|
15563
17067
|
chunkPosToWorldPos(pos) {
|
|
15564
|
-
return new
|
|
17068
|
+
return new import_three42.Vector3(
|
|
15565
17069
|
this.x * 16 + pos.x,
|
|
15566
17070
|
this.y * 16 + pos.y,
|
|
15567
17071
|
this.z * 16 + pos.z
|