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.mjs
CHANGED
|
@@ -64,6 +64,7 @@ var init_PersistentCache = __esm({
|
|
|
64
64
|
constructor(backing) {
|
|
65
65
|
this.backing = backing;
|
|
66
66
|
}
|
|
67
|
+
/** Opens a named store in the active environment using the current cache schema version. */
|
|
67
68
|
static open(name) {
|
|
68
69
|
return Env.provider.openCache(name, _PersistentCache.VERSION);
|
|
69
70
|
}
|
|
@@ -259,9 +260,11 @@ var init_Caching = __esm({
|
|
|
259
260
|
this.entityTextureCache,
|
|
260
261
|
this.entityAnimationCache,
|
|
261
262
|
this.blockEntityIndexCache,
|
|
262
|
-
this.fontCache
|
|
263
|
+
this.fontCache,
|
|
264
|
+
this.bannerPatternCache
|
|
263
265
|
];
|
|
264
266
|
}
|
|
267
|
+
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
265
268
|
static clear() {
|
|
266
269
|
for (const cache of this.all) {
|
|
267
270
|
cache.invalidateAll();
|
|
@@ -307,6 +310,7 @@ var init_Caching = __esm({
|
|
|
307
310
|
_Caching.entityAnimationCache = _Caching.createAssetCache();
|
|
308
311
|
_Caching.blockEntityIndexCache = _Caching.createAssetCache();
|
|
309
312
|
_Caching.fontCache = _Caching.createAssetCache();
|
|
313
|
+
_Caching.bannerPatternCache = _Caching.createAssetCache();
|
|
310
314
|
Caching = _Caching;
|
|
311
315
|
}
|
|
312
316
|
});
|
|
@@ -327,6 +331,7 @@ var init_Geometries = __esm({
|
|
|
327
331
|
}
|
|
328
332
|
return geometry;
|
|
329
333
|
}
|
|
334
|
+
/** Returns shared geometry. Use {@link createBox} when the caller needs to modify or dispose it independently. */
|
|
330
335
|
static getBox(key) {
|
|
331
336
|
const keyStr = serializeBoxGeometryKey(key);
|
|
332
337
|
return Caching.boxGeometryCache.get(keyStr, (k) => {
|
|
@@ -504,12 +509,15 @@ var init_Requests = __esm({
|
|
|
504
509
|
}
|
|
505
510
|
};
|
|
506
511
|
Requests = class {
|
|
512
|
+
/** Queues a general request. Set `request.signal` to cancel it and `request.timeout` to limit each attempt. */
|
|
507
513
|
static genericRequest(request) {
|
|
508
514
|
return this.genericQueue.request(request);
|
|
509
515
|
}
|
|
516
|
+
/** Queues a request separately from general traffic, using the configured asset base URL for relative paths. */
|
|
510
517
|
static mcAssetRequest(request) {
|
|
511
518
|
return this.mcAssetRequestQueue.request(request);
|
|
512
519
|
}
|
|
520
|
+
/** Sets the base URL for relative asset requests. Use {@link AssetLoader.setVersion} to change Minecraft versions. */
|
|
513
521
|
static setMcAssetRoot(root) {
|
|
514
522
|
this.mcAssetRequestQueue.baseURL = root;
|
|
515
523
|
}
|
|
@@ -564,6 +572,7 @@ var init_WrappedImage = __esm({
|
|
|
564
572
|
}
|
|
565
573
|
return false;
|
|
566
574
|
}
|
|
575
|
+
/** Whether the dimensions form a vertical strip of square frames, without consulting `.mcmeta`. */
|
|
567
576
|
get animated() {
|
|
568
577
|
return this.height > this.width && this.height % this.width === 0;
|
|
569
578
|
}
|
|
@@ -610,6 +619,7 @@ var init_ImageLoader = __esm({
|
|
|
610
619
|
static _createImage() {
|
|
611
620
|
return createImage();
|
|
612
621
|
}
|
|
622
|
+
/** Resolves when an image URL has decoded, or rejects if decoding fails. */
|
|
613
623
|
static async loadAsync(src) {
|
|
614
624
|
return new Promise((resolve, reject) => {
|
|
615
625
|
const image = this._createImage();
|
|
@@ -618,6 +628,7 @@ var init_ImageLoader = __esm({
|
|
|
618
628
|
image.src = src;
|
|
619
629
|
});
|
|
620
630
|
}
|
|
631
|
+
/** Returns an image immediately. Use the callbacks to observe loading, or await {@link loadAsync}. */
|
|
621
632
|
static loadElement(src, onload, onerr) {
|
|
622
633
|
const image = this._createImage();
|
|
623
634
|
if (onload)
|
|
@@ -645,6 +656,7 @@ var init_ImageLoader = __esm({
|
|
|
645
656
|
throw err;
|
|
646
657
|
}
|
|
647
658
|
}
|
|
659
|
+
/** Decodes the supplied bytes into a canvas without fetching the source URL again. */
|
|
648
660
|
static async infoToCanvasData(info) {
|
|
649
661
|
const type = info.type === "jpg" ? "jpeg" : info.type ?? "png";
|
|
650
662
|
const image = await _ImageLoader.loadAsync(`data:image/${type};base64,${info.data.toString("base64")}`);
|
|
@@ -661,12 +673,14 @@ var init_ImageLoader = __esm({
|
|
|
661
673
|
const image = await this.infoToCanvasData(info);
|
|
662
674
|
return image.data.getImageData(0, 0, image.width, image.height);
|
|
663
675
|
}
|
|
676
|
+
/** Returns cached RGBA pixels for an image URL or data URL. Failed loads can be retried. */
|
|
664
677
|
static async getData(src) {
|
|
665
678
|
const keyStr = serializeImageKey({ src });
|
|
666
679
|
return await Caching.imageDataCache.get(keyStr, (k) => {
|
|
667
680
|
return _ImageLoader.loadData(src);
|
|
668
681
|
});
|
|
669
682
|
}
|
|
683
|
+
/** Returns a cached canvas context and dimensions for an image URL or data URL. */
|
|
670
684
|
static async getCanvasData(src) {
|
|
671
685
|
const keyStr = serializeImageKey({ src });
|
|
672
686
|
return await Caching.canvasImageDataCache.get(keyStr, (k) => {
|
|
@@ -749,7 +763,7 @@ var init_TextureLoader = __esm({
|
|
|
749
763
|
static createTexture() {
|
|
750
764
|
return new Texture();
|
|
751
765
|
}
|
|
752
|
-
static loadInBackground(src,
|
|
766
|
+
static loadInBackground(src, format2 = RGBAFormat, rotation = 0) {
|
|
753
767
|
const texture = this.createTexture();
|
|
754
768
|
texture.colorSpace = THREE.SRGBColorSpace;
|
|
755
769
|
const image = ImageLoader.loadElement(src, () => {
|
|
@@ -759,11 +773,12 @@ var init_TextureLoader = __esm({
|
|
|
759
773
|
console.warn("Failed to load texture", src, error);
|
|
760
774
|
});
|
|
761
775
|
texture.image = image;
|
|
762
|
-
texture.format =
|
|
776
|
+
texture.format = format2;
|
|
763
777
|
texture.rotation = rotation;
|
|
764
778
|
return Textures.initTextureProps(texture);
|
|
765
779
|
}
|
|
766
|
-
|
|
780
|
+
/** Loads image pixels in the background. `rotation` is in radians. Check {@link hasFailed} for load failures. */
|
|
781
|
+
static load(src, format2 = RGBAFormat, rotation = 0) {
|
|
767
782
|
const texture = new Texture();
|
|
768
783
|
texture.colorSpace = THREE.SRGBColorSpace;
|
|
769
784
|
ImageLoader.getData(src).then((image) => {
|
|
@@ -773,7 +788,7 @@ var init_TextureLoader = __esm({
|
|
|
773
788
|
this.failedTextures.add(texture);
|
|
774
789
|
console.warn("Failed to load texture", src, error);
|
|
775
790
|
});
|
|
776
|
-
texture.format =
|
|
791
|
+
texture.format = format2;
|
|
777
792
|
texture.rotation = rotation;
|
|
778
793
|
return Textures.initTextureProps(texture);
|
|
779
794
|
}
|
|
@@ -800,8 +815,8 @@ var init_Textures = __esm({
|
|
|
800
815
|
for (let y = 0; y < size; y++) {
|
|
801
816
|
for (let x = 0; x < size; x++) {
|
|
802
817
|
const offset = (y * size + x) * 4;
|
|
803
|
-
const
|
|
804
|
-
data[offset] = data[offset + 2] =
|
|
818
|
+
const color2 = (x + y) % 2 === 0 ? 255 : 0;
|
|
819
|
+
data[offset] = data[offset + 2] = color2;
|
|
805
820
|
data[offset + 3] = 255;
|
|
806
821
|
}
|
|
807
822
|
}
|
|
@@ -812,6 +827,7 @@ var init_Textures = __esm({
|
|
|
812
827
|
return this.initTextureProps(texture);
|
|
813
828
|
});
|
|
814
829
|
}
|
|
830
|
+
/** Sets nearest-neighbor filters without changing the texture's color space. Returns the same texture. */
|
|
815
831
|
static initTextureProps(texture) {
|
|
816
832
|
texture.magFilter = THREE2.NearestFilter;
|
|
817
833
|
texture.minFilter = THREE2.NearestFilter;
|
|
@@ -824,11 +840,13 @@ var init_Textures = __esm({
|
|
|
824
840
|
static createAsset(key) {
|
|
825
841
|
return new Texture2();
|
|
826
842
|
}
|
|
843
|
+
/** Creates an sRGB color texture from a canvas. */
|
|
827
844
|
static createCanvasTexture(canvas) {
|
|
828
845
|
const texture = new CanvasTexture(canvas);
|
|
829
846
|
texture.colorSpace = THREE2.SRGBColorSpace;
|
|
830
847
|
return this.initTextureProps(texture);
|
|
831
848
|
}
|
|
849
|
+
/** Returns a shared texture immediately and loads its image asynchronously. Failed cached loads are retried. */
|
|
832
850
|
static getImage(key) {
|
|
833
851
|
const keyStr = serializeTextureKey(key);
|
|
834
852
|
const cached2 = Caching.textureCache.peek(keyStr);
|
|
@@ -853,6 +871,7 @@ var init_Materials = __esm({
|
|
|
853
871
|
init_TextureLoader();
|
|
854
872
|
init_Caching();
|
|
855
873
|
Materials = class _Materials {
|
|
874
|
+
/** Shared checkerboard material, created on first access. */
|
|
856
875
|
static get MISSING_TEXTURE() {
|
|
857
876
|
return Caching.materialCache.get("builtin:missing-texture", () => new MeshBasicMaterial({
|
|
858
877
|
map: Textures.getMissing(),
|
|
@@ -924,6 +943,7 @@ var init_Materials = __esm({
|
|
|
924
943
|
material.depthWrite = false;
|
|
925
944
|
break;
|
|
926
945
|
case "eyes":
|
|
946
|
+
case "no_outline":
|
|
927
947
|
material.transparent = true;
|
|
928
948
|
material.alphaTest = 0;
|
|
929
949
|
material.depthWrite = false;
|
|
@@ -956,6 +976,7 @@ var init_Materials = __esm({
|
|
|
956
976
|
if (tint !== void 0) material.color.multiply(new Color(tint));
|
|
957
977
|
return material;
|
|
958
978
|
}
|
|
979
|
+
/** Creates an unlit, transparent GUI material without depth writes or tone mapping. */
|
|
959
980
|
static createGuiCanvasMaterial(canvas) {
|
|
960
981
|
const material = new MeshBasicMaterial({
|
|
961
982
|
map: Textures.createCanvasTexture(canvas),
|
|
@@ -981,7 +1002,7 @@ var init_Materials = __esm({
|
|
|
981
1002
|
};
|
|
982
1003
|
return material;
|
|
983
1004
|
}
|
|
984
|
-
static createShadedCanvasMaterial(canvas, transparent = false, shade = false) {
|
|
1005
|
+
static createShadedCanvasMaterial(canvas, transparent = false, shade = false, boundUvs = false) {
|
|
985
1006
|
const vertShader = `
|
|
986
1007
|
uniform bool SHADE;
|
|
987
1008
|
|
|
@@ -991,6 +1012,10 @@ var init_Materials = __esm({
|
|
|
991
1012
|
#else
|
|
992
1013
|
varying vec2 vUv;
|
|
993
1014
|
#endif
|
|
1015
|
+
#ifdef BOUND_UVS
|
|
1016
|
+
attribute vec4 uvBounds;
|
|
1017
|
+
flat out vec4 vUvBounds;
|
|
1018
|
+
#endif
|
|
994
1019
|
varying vec3 vTint;
|
|
995
1020
|
varying float light;
|
|
996
1021
|
varying float lift;
|
|
@@ -1026,13 +1051,17 @@ var init_Materials = __esm({
|
|
|
1026
1051
|
}
|
|
1027
1052
|
|
|
1028
1053
|
vUv = uv;
|
|
1054
|
+
#ifdef BOUND_UVS
|
|
1055
|
+
vUvBounds = uvBounds;
|
|
1056
|
+
#endif
|
|
1029
1057
|
vTint = color;
|
|
1030
1058
|
|
|
1059
|
+
vec4 mvPosition = vec4(position, 1.0);
|
|
1031
1060
|
#ifdef USE_INSTANCING
|
|
1032
|
-
|
|
1033
|
-
#else
|
|
1034
|
-
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
|
1061
|
+
mvPosition = instanceMatrix * mvPosition;
|
|
1035
1062
|
#endif
|
|
1063
|
+
mvPosition = modelViewMatrix * mvPosition;
|
|
1064
|
+
gl_Position = projectionMatrix * mvPosition;
|
|
1036
1065
|
|
|
1037
1066
|
}
|
|
1038
1067
|
`;
|
|
@@ -1052,13 +1081,22 @@ var init_Materials = __esm({
|
|
|
1052
1081
|
#else
|
|
1053
1082
|
varying vec2 vUv;
|
|
1054
1083
|
#endif
|
|
1084
|
+
#ifdef BOUND_UVS
|
|
1085
|
+
flat in vec4 vUvBounds;
|
|
1086
|
+
#endif
|
|
1055
1087
|
varying vec3 vTint;
|
|
1056
1088
|
varying float light;
|
|
1057
1089
|
varying float lift;
|
|
1058
1090
|
|
|
1059
1091
|
void main(void)
|
|
1060
1092
|
{
|
|
1061
|
-
|
|
1093
|
+
#ifdef BOUND_UVS
|
|
1094
|
+
// Keep edge samples inside their texture even without centroid interpolation.
|
|
1095
|
+
vec2 mapUv = clamp(vUv, vUvBounds.xy, vUvBounds.zw);
|
|
1096
|
+
#else
|
|
1097
|
+
vec2 mapUv = vUv;
|
|
1098
|
+
#endif
|
|
1099
|
+
vec4 color = texture2D(map, mapUv);
|
|
1062
1100
|
color.rgb *= vTint;
|
|
1063
1101
|
|
|
1064
1102
|
if (color.a < 0.01) discard;
|
|
@@ -1079,7 +1117,8 @@ var init_Materials = __esm({
|
|
|
1079
1117
|
}
|
|
1080
1118
|
`;
|
|
1081
1119
|
try {
|
|
1082
|
-
|
|
1120
|
+
const material = new ShaderMaterial({
|
|
1121
|
+
defines: boundUvs ? { BOUND_UVS: true } : {},
|
|
1083
1122
|
uniforms: {
|
|
1084
1123
|
SHADE: { value: true },
|
|
1085
1124
|
BRIGHTNESS: { value: 1 },
|
|
@@ -1097,11 +1136,14 @@ var init_Materials = __esm({
|
|
|
1097
1136
|
side: transparent ? DoubleSide : FrontSide,
|
|
1098
1137
|
alphaTest: 0.5
|
|
1099
1138
|
});
|
|
1139
|
+
Object.assign(material.defaultAttributeValues, { uvBounds: [0, 0, 1, 1] });
|
|
1140
|
+
return material;
|
|
1100
1141
|
} catch (e) {
|
|
1101
1142
|
console.warn(e);
|
|
1102
1143
|
}
|
|
1103
1144
|
return this.createBasicCanvasMaterial(canvas, transparent, shade);
|
|
1104
1145
|
}
|
|
1146
|
+
/** Returns a shared image material. Use {@link createImage} for a separate material instance. */
|
|
1105
1147
|
static getImage(key) {
|
|
1106
1148
|
const keyStr = serializeMaterialKey(key);
|
|
1107
1149
|
const map = Caching.materialCache.peek(keyStr)?.map;
|
|
@@ -1276,6 +1318,14 @@ var init_AssetKey = __esm({
|
|
|
1276
1318
|
}
|
|
1277
1319
|
};
|
|
1278
1320
|
AssetKey = class _AssetKey extends BasicAssetKey {
|
|
1321
|
+
/**
|
|
1322
|
+
* Creates a key from separate path components. Use {@link parse} for a combined reference.
|
|
1323
|
+
*
|
|
1324
|
+
* @param assetType - Asset directory, such as `models`, `textures`, or `blockstates`.
|
|
1325
|
+
* @param type - Subdirectory, such as `block` or `item`.
|
|
1326
|
+
* @param rootType - Top-level directory, usually `assets` or `data`.
|
|
1327
|
+
* @param root - Base URL override for hosted asset sources.
|
|
1328
|
+
*/
|
|
1279
1329
|
constructor(namespace, path, assetType, type, rootType = "assets", extension = ".json", root) {
|
|
1280
1330
|
super(namespace, path);
|
|
1281
1331
|
this.namespace = namespace;
|
|
@@ -1287,6 +1337,16 @@ var init_AssetKey = __esm({
|
|
|
1287
1337
|
this.root = root;
|
|
1288
1338
|
this.isAssetKey = true;
|
|
1289
1339
|
}
|
|
1340
|
+
/**
|
|
1341
|
+
* Parses an asset reference such as `minecraft:block/stone`.
|
|
1342
|
+
* A missing namespace defaults to the origin's namespace or `minecraft`.
|
|
1343
|
+
*
|
|
1344
|
+
* @param assetType - Asset category, such as `models` or `textures`.
|
|
1345
|
+
* @param str - Path with an optional namespace and file extension.
|
|
1346
|
+
* @param origin - Supplies the asset root, default namespace, and non-texture extension.
|
|
1347
|
+
* @returns A key with the matching extension removed from its path. Textures use `.png`;
|
|
1348
|
+
* other assets use the origin's extension or `.json`.
|
|
1349
|
+
*/
|
|
1290
1350
|
static parse(assetType, str, origin) {
|
|
1291
1351
|
let namespace = origin?.namespace || DEFAULT_NAMESPACE;
|
|
1292
1352
|
if (str.includes(":")) {
|
|
@@ -1318,6 +1378,7 @@ var init_AssetKey = __esm({
|
|
|
1318
1378
|
];
|
|
1319
1379
|
return a.join("/");
|
|
1320
1380
|
}
|
|
1381
|
+
/** Joins the type subdirectory and path without adding a namespace or file extension. */
|
|
1321
1382
|
getFullPath() {
|
|
1322
1383
|
let p10 = [];
|
|
1323
1384
|
if (this.type) {
|
|
@@ -1329,6 +1390,7 @@ var init_AssetKey = __esm({
|
|
|
1329
1390
|
toNamespacedString() {
|
|
1330
1391
|
return this.namespace + ":" + this.getFullPath();
|
|
1331
1392
|
}
|
|
1393
|
+
/** Returns a cache identifier that includes the explicit root or {@link AssetLoader.ROOT}. */
|
|
1332
1394
|
serialize() {
|
|
1333
1395
|
return this.toString();
|
|
1334
1396
|
}
|
|
@@ -1340,17 +1402,17 @@ var init_AssetKey = __esm({
|
|
|
1340
1402
|
var DisplayPosition, DISPLAY_POSITIONS;
|
|
1341
1403
|
var init_DisplayPosition = __esm({
|
|
1342
1404
|
"src/model/DisplayPosition.ts"() {
|
|
1343
|
-
DisplayPosition = /* @__PURE__ */ ((
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
return
|
|
1405
|
+
DisplayPosition = /* @__PURE__ */ ((DisplayPosition3) => {
|
|
1406
|
+
DisplayPosition3["THIRDPERSON_RIGHTHAND"] = "thirdperson_righthand";
|
|
1407
|
+
DisplayPosition3["THIRDPERSON_LEFTHAND"] = "thirdperson_lefthand";
|
|
1408
|
+
DisplayPosition3["FIRSTPERSON_RIGHTHAND"] = "firstperson_righthand";
|
|
1409
|
+
DisplayPosition3["FIRSTPERSON_LEFTHAND"] = "firstperson_lefthand";
|
|
1410
|
+
DisplayPosition3["GUI"] = "gui";
|
|
1411
|
+
DisplayPosition3["HEAD"] = "head";
|
|
1412
|
+
DisplayPosition3["GROUND"] = "ground";
|
|
1413
|
+
DisplayPosition3["FIXED"] = "fixed";
|
|
1414
|
+
DisplayPosition3["ON_SHELF"] = "on_shelf";
|
|
1415
|
+
return DisplayPosition3;
|
|
1354
1416
|
})(DisplayPosition || {});
|
|
1355
1417
|
DISPLAY_POSITIONS = Object.values(DisplayPosition);
|
|
1356
1418
|
}
|
|
@@ -1388,6 +1450,7 @@ var init_DisplayTransforms = __esm({
|
|
|
1388
1450
|
const scale = new Vector32(...transform.scale ?? [1, 1, 1]).clampScalar(-4, 4);
|
|
1389
1451
|
return new Matrix4().compose(translation, rotation, scale);
|
|
1390
1452
|
}
|
|
1453
|
+
/** Transforms geometry in place and reverses triangle winding when the matrix reflects it. */
|
|
1391
1454
|
static apply(geometry, matrix) {
|
|
1392
1455
|
geometry.applyMatrix4(matrix);
|
|
1393
1456
|
if (matrix.determinant() < 0) {
|
|
@@ -1415,7 +1478,10 @@ var init_ModelMerger = __esm({
|
|
|
1415
1478
|
init_AssetKey();
|
|
1416
1479
|
init_DisplayTransforms();
|
|
1417
1480
|
ModelMerger = class {
|
|
1481
|
+
/** Loads the parent chain and returns a merged model without modifying the supplied definition. */
|
|
1418
1482
|
static async mergeWithParents(model) {
|
|
1483
|
+
const parts = model.parts;
|
|
1484
|
+
if (parts) return { ...model, parts: await Promise.all(parts.map((part) => this.mergeWithParents(part))) };
|
|
1419
1485
|
const models = await this.collectAllParents(model);
|
|
1420
1486
|
let merged = {};
|
|
1421
1487
|
for (const source of [...models, model]) {
|
|
@@ -1427,6 +1493,7 @@ var init_ModelMerger = __esm({
|
|
|
1427
1493
|
}
|
|
1428
1494
|
merged.hierarchy = models.map((m) => m.parent).filter((p10) => `${p10}`);
|
|
1429
1495
|
merged.hierarchy.push(`${merged.parent}`);
|
|
1496
|
+
if (model.components) merged.components = model.components;
|
|
1430
1497
|
return merged;
|
|
1431
1498
|
}
|
|
1432
1499
|
static async collectAllParents(model) {
|
|
@@ -1465,6 +1532,79 @@ var init_AssetParsers = __esm({
|
|
|
1465
1532
|
}
|
|
1466
1533
|
});
|
|
1467
1534
|
|
|
1535
|
+
// src/assets/BannerPatterns.ts
|
|
1536
|
+
var DYE_COLORS, BannerPatterns;
|
|
1537
|
+
var init_BannerPatterns = __esm({
|
|
1538
|
+
"src/assets/BannerPatterns.ts"() {
|
|
1539
|
+
init_AssetKey();
|
|
1540
|
+
init_AssetLoader();
|
|
1541
|
+
init_AssetParsers();
|
|
1542
|
+
init_Caching();
|
|
1543
|
+
DYE_COLORS = Object.freeze({
|
|
1544
|
+
white: 16383998,
|
|
1545
|
+
orange: 16351261,
|
|
1546
|
+
magenta: 13061821,
|
|
1547
|
+
light_blue: 3847130,
|
|
1548
|
+
yellow: 16701501,
|
|
1549
|
+
lime: 8439583,
|
|
1550
|
+
pink: 15961002,
|
|
1551
|
+
gray: 4673362,
|
|
1552
|
+
light_gray: 10329495,
|
|
1553
|
+
cyan: 1481884,
|
|
1554
|
+
purple: 8991416,
|
|
1555
|
+
blue: 3949738,
|
|
1556
|
+
brown: 8606770,
|
|
1557
|
+
green: 6192150,
|
|
1558
|
+
red: 11546150,
|
|
1559
|
+
black: 1908001
|
|
1560
|
+
});
|
|
1561
|
+
BannerPatterns = class {
|
|
1562
|
+
/** Lists namespaced pattern IDs from the selected namespace's data directory. */
|
|
1563
|
+
static async getList(namespace = "minecraft", root) {
|
|
1564
|
+
const collect = async (path) => {
|
|
1565
|
+
const prefix2 = path ? `${path}/` : "";
|
|
1566
|
+
const key = new AssetKey(namespace, `${prefix2}_list`, "banner_pattern", void 0, "data", ".json", root);
|
|
1567
|
+
const list2 = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
|
|
1568
|
+
if (!list2) return [];
|
|
1569
|
+
const files = list2.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${namespace}:${prefix2}${file.slice(0, -5)}`);
|
|
1570
|
+
const children = await Promise.all(list2.directories.map((directory) => collect(`${prefix2}${directory}`)));
|
|
1571
|
+
return [...files, ...children.flat()];
|
|
1572
|
+
};
|
|
1573
|
+
return collect("");
|
|
1574
|
+
}
|
|
1575
|
+
/** Returns the named dye's sRGB value, rejecting unknown colors. */
|
|
1576
|
+
static getColor(color2) {
|
|
1577
|
+
if (typeof color2 !== "string" || !Object.prototype.hasOwnProperty.call(DYE_COLORS, color2)) throw new Error(`Unsupported dye color ${color2}`);
|
|
1578
|
+
return DYE_COLORS[color2];
|
|
1579
|
+
}
|
|
1580
|
+
/** Resolves the first 16 layers of a `banner_patterns` component, preserving their order. */
|
|
1581
|
+
static async getLayers(value, target, root) {
|
|
1582
|
+
if (value === void 0) return [];
|
|
1583
|
+
if (!Array.isArray(value)) throw new Error("banner_patterns must be an array");
|
|
1584
|
+
return Promise.all(value.slice(0, 16).map(async (layer) => {
|
|
1585
|
+
if (!layer || typeof layer !== "object" || Array.isArray(layer)) throw new Error("Each banner pattern requires a pattern and dye color");
|
|
1586
|
+
const color2 = this.getColor(layer.color);
|
|
1587
|
+
let pattern2 = layer.pattern;
|
|
1588
|
+
if (typeof pattern2 === "string") {
|
|
1589
|
+
const id = this.identifier(pattern2);
|
|
1590
|
+
const key = new AssetKey(id.namespace, id.getFullPath(), "banner_pattern", void 0, "data", ".json", root);
|
|
1591
|
+
pattern2 = await Caching.bannerPatternCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
|
|
1592
|
+
if (!pattern2) throw new Error(`Missing banner pattern ${layer.pattern}`);
|
|
1593
|
+
}
|
|
1594
|
+
if (!pattern2 || typeof pattern2 !== "object" || Array.isArray(pattern2) || typeof pattern2.translation_key !== "string") throw new Error("Banner patterns require asset_id and translation_key");
|
|
1595
|
+
const asset2 = this.identifier(pattern2.asset_id);
|
|
1596
|
+
return { color: color2, texture: new AssetKey(asset2.namespace, `${target}/${asset2.getFullPath()}`, "textures", "entity", "assets", ".png", root) };
|
|
1597
|
+
}));
|
|
1598
|
+
}
|
|
1599
|
+
static identifier(value) {
|
|
1600
|
+
if (typeof value !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(value)) throw new Error(`Invalid banner-pattern identifier ${value}`);
|
|
1601
|
+
const [namespace, path] = value.includes(":") ? value.split(":") : ["minecraft", value];
|
|
1602
|
+
return new AssetKey(namespace, path);
|
|
1603
|
+
}
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
1607
|
+
|
|
1468
1608
|
// src/assets/Models.ts
|
|
1469
1609
|
var Models;
|
|
1470
1610
|
var init_Models = __esm({
|
|
@@ -1476,12 +1616,15 @@ var init_Models = __esm({
|
|
|
1476
1616
|
init_PersistentCache();
|
|
1477
1617
|
init_AssetKey();
|
|
1478
1618
|
init_AssetParsers();
|
|
1619
|
+
init_DisplayPosition();
|
|
1620
|
+
init_BannerPatterns();
|
|
1479
1621
|
Models = class _Models {
|
|
1480
1622
|
// opened lazily: touching the store at import time would hit IndexedDB/disk just for loading
|
|
1481
1623
|
// the library, and would require an EnvProvider before any entry has had a chance to register
|
|
1482
1624
|
static get PERSISTENT_CACHE() {
|
|
1483
1625
|
return this._persistentCache ?? (this._persistentCache = PersistentCache.open("minerender-models"));
|
|
1484
1626
|
}
|
|
1627
|
+
/** Lists item filenames from the asset source's `_list.json`, with a legacy model-directory fallback. */
|
|
1485
1628
|
static async getItemList() {
|
|
1486
1629
|
const key = new AssetKey(DEFAULT_NAMESPACE, "_list", "items");
|
|
1487
1630
|
const legacyKey = new AssetKey(DEFAULT_NAMESPACE, "_list", "models", "item");
|
|
@@ -1489,34 +1632,174 @@ var init_Models = __esm({
|
|
|
1489
1632
|
return (await AssetLoader.getFirst([key, legacyKey], "list" /* LIST */))?.asset;
|
|
1490
1633
|
}).then((r) => r?.files ?? []);
|
|
1491
1634
|
}
|
|
1492
|
-
static async loadAndMerge(key) {
|
|
1493
|
-
const model = key.type === "item" ? await this.getItemModel(key) : await this.getRaw(key);
|
|
1635
|
+
static async loadAndMerge(key, context = {}) {
|
|
1636
|
+
const model = key.type === "item" ? await this.getItemModel(key, context) : await this.getRaw(key);
|
|
1494
1637
|
if (!model) {
|
|
1495
1638
|
return void 0;
|
|
1496
1639
|
}
|
|
1497
1640
|
return ModelMerger.mergeWithParents(model);
|
|
1498
1641
|
}
|
|
1499
|
-
static async getItemModel(key) {
|
|
1642
|
+
static async getItemModel(key, context = {}) {
|
|
1643
|
+
const preview = this.snapshotContext(key, context);
|
|
1500
1644
|
const itemKey = new AssetKey(key.namespace, key.path, "items", void 0, key.rootType, ".json", key.root);
|
|
1501
|
-
const
|
|
1645
|
+
const cacheKey = itemKey.serialize() + this.contextKey(preview);
|
|
1646
|
+
const model = await this.PERSISTENT_CACHE.getOrLoad(`item-v4:${AssetLoader.persistentKey(cacheKey)}`, async () => {
|
|
1502
1647
|
const result = await AssetLoader.getFirst([itemKey, key], "json" /* JSON */);
|
|
1503
1648
|
if (!result) return void 0;
|
|
1504
|
-
if (result.key.assetType !== "items") return { ...result.asset, key };
|
|
1505
|
-
const
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1649
|
+
if (result.key.assetType !== "items") return { ...result.asset, key, components: preview.components };
|
|
1650
|
+
const load = async (selected) => {
|
|
1651
|
+
if ("parts" in selected) {
|
|
1652
|
+
return { key, parts: await Promise.all(selected.parts.map(load)) };
|
|
1653
|
+
}
|
|
1654
|
+
if ("item" in selected) {
|
|
1655
|
+
const model3 = await this.getItemModel(selected.item, { displayContext: preview.displayContext });
|
|
1656
|
+
if (!model3) throw new Error(`Item ${key.toNamespacedString()} references missing item ${selected.item.toNamespacedString()}`);
|
|
1657
|
+
return model3;
|
|
1658
|
+
}
|
|
1659
|
+
const modelKey = AssetKey.parse("models", selected.model);
|
|
1660
|
+
modelKey.root = key.root;
|
|
1661
|
+
const model2 = await this.getRaw(modelKey);
|
|
1662
|
+
if (!model2) throw new Error(`Item ${key.toNamespacedString()} references missing model ${selected.model}`);
|
|
1663
|
+
return { ...model2, components: preview.components, ...selected.special && { special: selected.special }, ...selected.tints && { tints: selected.tints } };
|
|
1664
|
+
};
|
|
1665
|
+
return load(this.selectItemModel(result.asset.model, key, preview));
|
|
1666
|
+
});
|
|
1667
|
+
const restore = (model2) => ({
|
|
1668
|
+
...model2,
|
|
1669
|
+
key: Object.assign(new AssetKey("", ""), model2.key),
|
|
1670
|
+
...model2.parts && { parts: model2.parts.map(restore) }
|
|
1511
1671
|
});
|
|
1512
|
-
return model ?
|
|
1672
|
+
return model ? restore(model) : void 0;
|
|
1673
|
+
}
|
|
1674
|
+
static contextIdentifier(id) {
|
|
1675
|
+
if (typeof id !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(id)) {
|
|
1676
|
+
throw new Error(`Invalid item-preview identifier: ${id}`);
|
|
1677
|
+
}
|
|
1678
|
+
return id.includes(":") ? id : `minecraft:${id}`;
|
|
1679
|
+
}
|
|
1680
|
+
static snapshotContext(key, context) {
|
|
1681
|
+
const displayContext = context.displayContext ?? "gui" /* GUI */;
|
|
1682
|
+
if (displayContext !== "none" && !Object.values(DisplayPosition).includes(displayContext)) {
|
|
1683
|
+
throw new Error(`Unsupported item display context: ${displayContext}`);
|
|
1684
|
+
}
|
|
1685
|
+
for (const map of [context.properties, context.components, context.itemReferences]) {
|
|
1686
|
+
if (map !== void 0 && (!map || typeof map !== "object" || Array.isArray(map))) {
|
|
1687
|
+
throw new Error("Item-preview properties, components, and references must be objects keyed by identifier");
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
const count = context.count === void 0 ? 1 : context.count;
|
|
1691
|
+
if (!Number.isSafeInteger(count) || count < 0) throw new Error("Item-preview count must be a nonnegative safe integer");
|
|
1692
|
+
const properties = {};
|
|
1693
|
+
for (const [id, value] of Object.entries(context.properties ?? {})) {
|
|
1694
|
+
const property = this.contextIdentifier(id);
|
|
1695
|
+
if (!["boolean", "string", "number"].includes(typeof value) || typeof value === "number" && !Number.isFinite(value)) {
|
|
1696
|
+
throw new Error(`Item-preview property ${property} must be a boolean, string, or finite number`);
|
|
1697
|
+
}
|
|
1698
|
+
if (Object.prototype.hasOwnProperty.call(properties, property)) throw new Error(`Duplicate item-preview property: ${property}`);
|
|
1699
|
+
properties[property] = value;
|
|
1700
|
+
}
|
|
1701
|
+
const components = {};
|
|
1702
|
+
for (const [id, value] of Object.entries(context.components ?? {})) {
|
|
1703
|
+
const component = this.contextIdentifier(id);
|
|
1704
|
+
if (Object.prototype.hasOwnProperty.call(components, component)) throw new Error(`Duplicate item-preview component: ${component}`);
|
|
1705
|
+
components[component] = this.snapshotJson(value);
|
|
1706
|
+
}
|
|
1707
|
+
const itemReferences = {};
|
|
1708
|
+
for (const [id, value] of Object.entries(context.itemReferences ?? {})) {
|
|
1709
|
+
const reference = this.contextIdentifier(id);
|
|
1710
|
+
if (!isAssetKey(value) || value.assetType !== "models" || value.type !== "item") {
|
|
1711
|
+
throw new Error(`Item-preview reference ${reference} must be an item-model AssetKey`);
|
|
1712
|
+
}
|
|
1713
|
+
this.contextIdentifier(`${value.namespace}:${value.path}`);
|
|
1714
|
+
if (Object.prototype.hasOwnProperty.call(itemReferences, reference)) throw new Error(`Duplicate item-preview reference: ${reference}`);
|
|
1715
|
+
itemReferences[reference] = Object.assign(new AssetKey("", ""), value, { root: value.root ?? key.root });
|
|
1716
|
+
}
|
|
1717
|
+
return { displayContext, properties, components, count, itemReferences };
|
|
1718
|
+
}
|
|
1719
|
+
static contextKey(context) {
|
|
1720
|
+
const itemReferences = Object.fromEntries(Object.entries(context.itemReferences).map(([id, key]) => [id, key.serialize()]));
|
|
1721
|
+
return `|item-v4:${JSON.stringify(this.snapshotJson({ ...context, itemReferences }))}`;
|
|
1513
1722
|
}
|
|
1514
|
-
|
|
1515
|
-
|
|
1723
|
+
static snapshotJson(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
1724
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return value;
|
|
1725
|
+
if (typeof value !== "object" || ancestors.has(value) || !Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
|
|
1726
|
+
throw new Error("Item-preview components must contain finite JSON values without circular references");
|
|
1727
|
+
}
|
|
1728
|
+
ancestors.add(value);
|
|
1729
|
+
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)]));
|
|
1730
|
+
ancestors.delete(value);
|
|
1731
|
+
return copy;
|
|
1732
|
+
}
|
|
1733
|
+
static propertyValue(node, context) {
|
|
1734
|
+
const kind = node.type.replace(/^minecraft:/, "");
|
|
1735
|
+
const property = node.property ? this.contextIdentifier(node.property) : void 0;
|
|
1736
|
+
if (kind === "select" && property === "minecraft:display_context") return context.displayContext;
|
|
1737
|
+
if (property && Object.prototype.hasOwnProperty.call(context.properties, property)) return context.properties[property];
|
|
1738
|
+
const fallback = kind === "condition" ? false : kind === "range_dispatch" ? 0 : void 0;
|
|
1739
|
+
const component = (id) => context.components[this.contextIdentifier(id)];
|
|
1740
|
+
if (property === "minecraft:custom_model_data") {
|
|
1741
|
+
const index = node.index ?? 0;
|
|
1742
|
+
if (!Number.isSafeInteger(index) || index < 0) throw new Error("Item-model custom_model_data index must be a nonnegative integer");
|
|
1743
|
+
const data = component("custom_model_data");
|
|
1744
|
+
if (data === void 0) return fallback;
|
|
1745
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) throw new Error("Item-preview custom_model_data must be an object");
|
|
1746
|
+
const field = kind === "condition" ? "flags" : kind === "select" ? "strings" : "floats";
|
|
1747
|
+
const values = data[field];
|
|
1748
|
+
if (values === void 0) return fallback;
|
|
1749
|
+
if (!Array.isArray(values)) throw new Error(`Item-preview custom_model_data.${field} must be an array`);
|
|
1750
|
+
return index < values.length ? values[index] : fallback;
|
|
1751
|
+
}
|
|
1752
|
+
if (kind === "condition" && property === "minecraft:has_component") {
|
|
1753
|
+
if (node.ignore_default) throw new Error("Item-preview has_component with ignore_default requires an explicit properties override; item defaults are unavailable");
|
|
1754
|
+
return Object.prototype.hasOwnProperty.call(context.components, this.contextIdentifier(node.component));
|
|
1755
|
+
}
|
|
1756
|
+
if (kind === "select") {
|
|
1757
|
+
switch (property) {
|
|
1758
|
+
case "minecraft:component":
|
|
1759
|
+
return component(node.component);
|
|
1760
|
+
case "minecraft:block_state": {
|
|
1761
|
+
const state = component("block_state");
|
|
1762
|
+
if (state === void 0) return void 0;
|
|
1763
|
+
if (!state || typeof state !== "object" || Array.isArray(state)) throw new Error("Item-preview block_state must be an object");
|
|
1764
|
+
if (typeof node.block_state_property !== "string") throw new Error("Item-model block_state requires block_state_property");
|
|
1765
|
+
return Object.prototype.hasOwnProperty.call(state, node.block_state_property) ? state[node.block_state_property] : void 0;
|
|
1766
|
+
}
|
|
1767
|
+
case "minecraft:charge_type": {
|
|
1768
|
+
const projectiles = component("charged_projectiles");
|
|
1769
|
+
if (projectiles === void 0) return "none";
|
|
1770
|
+
if (!Array.isArray(projectiles)) throw new Error("Item-preview charged_projectiles must be an array of item stacks");
|
|
1771
|
+
const ids = projectiles.map((stack) => {
|
|
1772
|
+
if (!stack || typeof stack !== "object" || Array.isArray(stack)) throw new Error("Item-preview charged_projectiles must contain item-stack objects");
|
|
1773
|
+
return this.contextIdentifier(stack.id);
|
|
1774
|
+
});
|
|
1775
|
+
return !ids.length ? "none" : ids.includes("minecraft:firework_rocket") ? "rocket" : "arrow";
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
if (kind === "range_dispatch" && (property === "minecraft:damage" || property === "minecraft:count")) {
|
|
1780
|
+
const damage = property === "minecraft:damage";
|
|
1781
|
+
const maximumComponent = component(damage ? "max_damage" : "max_stack_size");
|
|
1782
|
+
const maximum = maximumComponent === void 0 ? damage ? 0 : 1 : maximumComponent;
|
|
1783
|
+
const damageComponent = component("damage");
|
|
1784
|
+
const value = damage ? damageComponent === void 0 ? 0 : damageComponent : context.count;
|
|
1785
|
+
if (typeof value !== "number" || typeof maximum !== "number" || maximum < 0) throw new Error(`Item-preview ${property} requires numeric components and a nonnegative maximum`);
|
|
1786
|
+
const clamped = Math.max(0, Math.min(maximum, value));
|
|
1787
|
+
return node.normalize === false ? clamped : clamped / maximum;
|
|
1788
|
+
}
|
|
1789
|
+
return fallback;
|
|
1790
|
+
}
|
|
1791
|
+
static selectItemModel(node, key, context) {
|
|
1516
1792
|
if (!node || typeof node.type !== "string") {
|
|
1517
1793
|
throw new Error(`Unsupported item model definition for ${key.toNamespacedString()}`);
|
|
1518
1794
|
}
|
|
1519
1795
|
switch (node.type.replace(/^minecraft:/, "")) {
|
|
1796
|
+
case "composite":
|
|
1797
|
+
if (Array.isArray(node.models)) return { parts: node.models.map((child) => this.selectItemModel(child, key, context)) };
|
|
1798
|
+
break;
|
|
1799
|
+
case "bundle/selected_item": {
|
|
1800
|
+
const item = context.itemReferences["minecraft:bundle/selected_item"];
|
|
1801
|
+
return item ? { item } : { parts: [] };
|
|
1802
|
+
}
|
|
1520
1803
|
case "model":
|
|
1521
1804
|
if (typeof node.model === "string" && node.model) return { model: node.model, tints: node.tints };
|
|
1522
1805
|
break;
|
|
@@ -1524,6 +1807,23 @@ var init_Models = __esm({
|
|
|
1524
1807
|
if (!node.base || !node.model || typeof node.model !== "object") break;
|
|
1525
1808
|
const special = node.model;
|
|
1526
1809
|
switch (special.type) {
|
|
1810
|
+
case "banner":
|
|
1811
|
+
case "minecraft:banner":
|
|
1812
|
+
if (typeof special.color === "string" && Object.prototype.hasOwnProperty.call(DYE_COLORS, special.color)) return { model: node.base, special };
|
|
1813
|
+
break;
|
|
1814
|
+
case "shield":
|
|
1815
|
+
case "minecraft:shield":
|
|
1816
|
+
case "trident":
|
|
1817
|
+
case "minecraft:trident":
|
|
1818
|
+
case "conduit":
|
|
1819
|
+
case "minecraft:conduit":
|
|
1820
|
+
return { model: node.base, special };
|
|
1821
|
+
case "shulker_box":
|
|
1822
|
+
case "minecraft:shulker_box":
|
|
1823
|
+
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))) {
|
|
1824
|
+
return { model: node.base, special };
|
|
1825
|
+
}
|
|
1826
|
+
break;
|
|
1527
1827
|
case "chest":
|
|
1528
1828
|
case "minecraft:chest":
|
|
1529
1829
|
case "bed":
|
|
@@ -1537,21 +1837,34 @@ var init_Models = __esm({
|
|
|
1537
1837
|
}
|
|
1538
1838
|
throw new Error(`Unsupported special item renderer ${special.type} for ${key.toNamespacedString()}`);
|
|
1539
1839
|
}
|
|
1540
|
-
case "condition":
|
|
1541
|
-
|
|
1840
|
+
case "condition": {
|
|
1841
|
+
const value = this.propertyValue(node, context);
|
|
1842
|
+
if (typeof value !== "boolean") throw new Error(`Item-preview condition ${node.property} requires a boolean`);
|
|
1843
|
+
return this.selectItemModel(value ? node.on_true : node.on_false, key, context);
|
|
1844
|
+
}
|
|
1542
1845
|
case "select": {
|
|
1543
|
-
const
|
|
1544
|
-
|
|
1846
|
+
const value = this.propertyValue(node, context);
|
|
1847
|
+
const component = node.property && this.contextIdentifier(node.property) === "minecraft:component";
|
|
1848
|
+
if (!component && value !== void 0 && typeof value !== "string") throw new Error(`Item-preview selector ${node.property} requires a string`);
|
|
1849
|
+
const matches = (candidate) => component ? JSON.stringify(this.snapshotJson(candidate)) === JSON.stringify(this.snapshotJson(value)) : candidate === value;
|
|
1850
|
+
const selected = value === void 0 ? void 0 : node.cases?.find((entry) => matches(entry.when) || Array.isArray(entry.when) && entry.when.some(matches))?.model;
|
|
1851
|
+
return this.selectItemModel(selected ?? node.fallback, key, context);
|
|
1545
1852
|
}
|
|
1546
1853
|
case "range_dispatch": {
|
|
1854
|
+
const value = this.propertyValue(node, context);
|
|
1855
|
+
if (typeof value !== "number") throw new Error(`Item-preview range ${node.property} requires a number`);
|
|
1856
|
+
if (Number.isNaN(value)) return this.selectItemModel(node.fallback, key, context);
|
|
1857
|
+
const scaled = value * (node.scale ?? 1);
|
|
1858
|
+
if (!Number.isFinite(scaled)) throw new Error(`Item-preview range ${node.property} requires a finite value and scale`);
|
|
1547
1859
|
const selected = node.entries?.reduce((match, entry) => {
|
|
1548
|
-
return entry.threshold <=
|
|
1860
|
+
return entry.threshold <= scaled && (!match || entry.threshold >= match.threshold) ? entry : match;
|
|
1549
1861
|
}, void 0);
|
|
1550
|
-
return this.
|
|
1862
|
+
return this.selectItemModel(selected?.model ?? node.fallback, key, context);
|
|
1551
1863
|
}
|
|
1552
1864
|
}
|
|
1553
1865
|
throw new Error(`Unsupported item model ${node?.type ?? "definition"} for ${key.toNamespacedString()}`);
|
|
1554
1866
|
}
|
|
1867
|
+
/** Loads one model file without resolving its parents. Returns `undefined` when the file is missing. */
|
|
1555
1868
|
static async getRaw(key) {
|
|
1556
1869
|
if (!key.assetType) {
|
|
1557
1870
|
key.assetType = "models";
|
|
@@ -1564,28 +1877,39 @@ var init_Models = __esm({
|
|
|
1564
1877
|
return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
|
|
1565
1878
|
return AssetLoader.get(key, "model" /* MODEL */);
|
|
1566
1879
|
});
|
|
1567
|
-
}).then((
|
|
1568
|
-
if (
|
|
1569
|
-
|
|
1880
|
+
}).then((asset2) => {
|
|
1881
|
+
if (asset2) {
|
|
1882
|
+
asset2.key = key;
|
|
1570
1883
|
}
|
|
1571
|
-
return
|
|
1884
|
+
return asset2;
|
|
1572
1885
|
});
|
|
1573
1886
|
}
|
|
1574
|
-
|
|
1887
|
+
/**
|
|
1888
|
+
* Loads a model and resolves its parent chain. Returns `undefined` when the model is missing.
|
|
1889
|
+
* Item keys default to GUI context and a stack count of 1. Unresolved conditions are false and numeric properties are zero.
|
|
1890
|
+
* Pass `context` to supply component values, property overrides, and item references for a preview.
|
|
1891
|
+
* Composite items retain independently merged children in `ItemModel.parts`.
|
|
1892
|
+
*
|
|
1893
|
+
* @param key - Model key, for example `AssetKey.parse("models", "minecraft:item/diamond_sword")`.
|
|
1894
|
+
*/
|
|
1895
|
+
static async getMerged(key, context = {}) {
|
|
1575
1896
|
if (!key.assetType) {
|
|
1576
1897
|
key.assetType = "models";
|
|
1577
1898
|
}
|
|
1578
1899
|
if (!key.extension) {
|
|
1579
1900
|
key.extension = ".json";
|
|
1580
1901
|
}
|
|
1581
|
-
const
|
|
1902
|
+
const preview = this.snapshotContext(key, context);
|
|
1903
|
+
const keyStr = key.serialize() + (key.type === "item" ? this.contextKey(preview) : "");
|
|
1582
1904
|
return Caching.mergedModelCache.get(keyStr, (k) => {
|
|
1583
|
-
return _Models.loadAndMerge(key);
|
|
1905
|
+
return _Models.loadAndMerge(key, preview);
|
|
1584
1906
|
});
|
|
1585
1907
|
}
|
|
1586
|
-
|
|
1587
|
-
|
|
1908
|
+
/** Alias for {@link getMerged}. */
|
|
1909
|
+
static async get(key, context = {}) {
|
|
1910
|
+
return this.getMerged(key, context);
|
|
1588
1911
|
}
|
|
1912
|
+
/** Clears persisted models. Use {@link Caching.clear} to also discard in-memory assets. */
|
|
1589
1913
|
static async clearCache() {
|
|
1590
1914
|
return this.PERSISTENT_CACHE.clear();
|
|
1591
1915
|
}
|
|
@@ -1608,6 +1932,7 @@ var init_BlockStates = __esm({
|
|
|
1608
1932
|
return this._persistentCache ?? (this._persistentCache = PersistentCache.open("minerender-blockstates"));
|
|
1609
1933
|
}
|
|
1610
1934
|
// BlockState names are hardcoded
|
|
1935
|
+
/** Returns blockstate filenames from the selected version's `_list.json`, or an empty list. */
|
|
1611
1936
|
static async getList() {
|
|
1612
1937
|
const key = new AssetKey(
|
|
1613
1938
|
DEFAULT_NAMESPACE,
|
|
@@ -1628,6 +1953,7 @@ var init_BlockStates = __esm({
|
|
|
1628
1953
|
return AssetLoader.get(key, "json" /* JSON */);
|
|
1629
1954
|
});
|
|
1630
1955
|
}
|
|
1956
|
+
/** Looks up vanilla property definitions for the key's block path, or returns `undefined`. */
|
|
1631
1957
|
static async getDefaultState(key) {
|
|
1632
1958
|
const defaultStates = await this.getDefaultStates();
|
|
1633
1959
|
if (!defaultStates) {
|
|
@@ -1635,6 +1961,7 @@ var init_BlockStates = __esm({
|
|
|
1635
1961
|
}
|
|
1636
1962
|
return defaultStates["minecraft:" + key.path];
|
|
1637
1963
|
}
|
|
1964
|
+
/** Loads a cached blockstate definition for {@link MineRenderScene.addBlock}, or returns `undefined`. */
|
|
1638
1965
|
static async get(key) {
|
|
1639
1966
|
if (!key.assetType) {
|
|
1640
1967
|
key.assetType = "blockstates";
|
|
@@ -1647,11 +1974,11 @@ var init_BlockStates = __esm({
|
|
|
1647
1974
|
return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
|
|
1648
1975
|
return AssetLoader.get(key, "blockstate" /* BLOCKSTATE */);
|
|
1649
1976
|
});
|
|
1650
|
-
}).then((
|
|
1651
|
-
if (
|
|
1652
|
-
|
|
1977
|
+
}).then((asset2) => {
|
|
1978
|
+
if (asset2) {
|
|
1979
|
+
asset2.key = key;
|
|
1653
1980
|
}
|
|
1654
|
-
return
|
|
1981
|
+
return asset2;
|
|
1655
1982
|
});
|
|
1656
1983
|
}
|
|
1657
1984
|
static getAll(keys) {
|
|
@@ -1661,6 +1988,7 @@ var init_BlockStates = __esm({
|
|
|
1661
1988
|
}
|
|
1662
1989
|
return Promise.all(promises);
|
|
1663
1990
|
}
|
|
1991
|
+
/** Clears persisted blockstates. Use {@link Caching.clear} to also discard in-memory assets. */
|
|
1664
1992
|
static async clearCache() {
|
|
1665
1993
|
await this.PERSISTENT_CACHE.clear();
|
|
1666
1994
|
}
|
|
@@ -1686,9 +2014,11 @@ var init_Assets = __esm({
|
|
|
1686
2014
|
static parseAssetKey(assetType, str, origin) {
|
|
1687
2015
|
return AssetKey.parse(assetType, str, origin);
|
|
1688
2016
|
}
|
|
2017
|
+
/** Loads a model with its parent chain resolved. See {@link Models.getMerged}. */
|
|
1689
2018
|
static async getModel(key) {
|
|
1690
2019
|
return Models.getMerged(key);
|
|
1691
2020
|
}
|
|
2021
|
+
/** Loads a blockstate definition. See {@link BlockStates.get}. */
|
|
1692
2022
|
static async getBlockState(key) {
|
|
1693
2023
|
return BlockStates.get(key);
|
|
1694
2024
|
}
|
|
@@ -1703,6 +2033,10 @@ var init_AssetSource = __esm({
|
|
|
1703
2033
|
AssetSource = class {
|
|
1704
2034
|
constructor() {
|
|
1705
2035
|
}
|
|
2036
|
+
/** Whether this source prevents lower-priority sources from supplying the key. Its own assets remain available. */
|
|
2037
|
+
blocks(key) {
|
|
2038
|
+
return false;
|
|
2039
|
+
}
|
|
1706
2040
|
/**
|
|
1707
2041
|
* Identifies this source's content across sessions for persistent caching. Sources that
|
|
1708
2042
|
* cannot tell whether their content changed (an unnamed archive, for example) return
|
|
@@ -1731,6 +2065,10 @@ var BatchedExecutor;
|
|
|
1731
2065
|
var init_BatchedExecutor = __esm({
|
|
1732
2066
|
"src/util/BatchedExecutor.ts"() {
|
|
1733
2067
|
BatchedExecutor = class {
|
|
2068
|
+
/**
|
|
2069
|
+
* @param interval - Delay between queue runs, in milliseconds.
|
|
2070
|
+
* @param batch - Maximum tasks started per run and maximum tasks active at once.
|
|
2071
|
+
*/
|
|
1734
2072
|
constructor(interval = 1, batch = 30) {
|
|
1735
2073
|
if (!Number.isFinite(interval) || interval < 0) throw new RangeError("Batch interval must be finite and nonnegative");
|
|
1736
2074
|
if (!Number.isInteger(batch) || batch < 1) throw new RangeError("Batch size must be a positive integer");
|
|
@@ -1738,9 +2076,11 @@ var init_BatchedExecutor = __esm({
|
|
|
1738
2076
|
this.batch = batch;
|
|
1739
2077
|
this.queue = new JobQueue2(async (task) => task(), { interval, maxPerRun: batch, maxActive: batch });
|
|
1740
2078
|
}
|
|
2079
|
+
/** Queues a task and resolves with its result, or rejects with its error. */
|
|
1741
2080
|
submit(task) {
|
|
1742
2081
|
return this.queue.add(() => task());
|
|
1743
2082
|
}
|
|
2083
|
+
/** Rejects queued and future tasks. Tasks already running are allowed to finish. */
|
|
1744
2084
|
stop() {
|
|
1745
2085
|
this.queue.end();
|
|
1746
2086
|
}
|
|
@@ -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 Vector33();
|
|
1793
2133
|
box.getSize(size);
|
|
1794
2134
|
let boxGeo = new BoxGeometry2(size.x, size.y, size.z, 1, 1, 1);
|
|
1795
2135
|
let wireGeo = new EdgesGeometry(boxGeo);
|
|
1796
|
-
let wireMat = new LineBasicMaterial({ color, linewidth: w });
|
|
2136
|
+
let wireMat = new LineBasicMaterial({ color: color2, linewidth: w });
|
|
1797
2137
|
let wireframe = new LineSegments(wireGeo, wireMat);
|
|
1798
2138
|
obj.add(wireframe);
|
|
1799
2139
|
let axes = new 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 BoxGeometry2(16, 16, 16, 1, 1, 1);
|
|
1804
2144
|
let wireGeo = new EdgesGeometry(boxGeo);
|
|
1805
|
-
let wireMat = new LineBasicMaterial({ color, linewidth: w });
|
|
2145
|
+
let wireMat = new LineBasicMaterial({ color: color2, linewidth: w });
|
|
1806
2146
|
let wireframe = new LineSegments(wireGeo, wireMat);
|
|
1807
2147
|
obj.add(wireframe);
|
|
1808
2148
|
let axes = new 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 EdgesGeometry(geo);
|
|
1813
|
-
let wireMat = new LineBasicMaterial({ color, linewidth: w });
|
|
2153
|
+
let wireMat = new LineBasicMaterial({ color: color2, linewidth: w });
|
|
1814
2154
|
let wireframe = new LineSegments(wireGeo, wireMat);
|
|
1815
2155
|
mesh.add(wireframe);
|
|
1816
2156
|
let axes = new AxesHelper(1);
|
|
@@ -1882,10 +2222,11 @@ var NBTHelper;
|
|
|
1882
2222
|
var init_NBTHelper = __esm({
|
|
1883
2223
|
"src/nbt/NBTHelper.ts"() {
|
|
1884
2224
|
NBTHelper = class {
|
|
1885
|
-
|
|
2225
|
+
/** Parses NBT bytes and preserves format and compression metadata. Omit `format` for automatic detection. */
|
|
2226
|
+
static async fromBuffer(data, format2) {
|
|
1886
2227
|
const buffer = Buffer3.from(data instanceof Uint8Array ? data : new Uint8Array(data));
|
|
1887
2228
|
const prismarineNbt = await import("prismarine-nbt");
|
|
1888
|
-
const { parsed, type, metadata } = await prismarineNbt.parse(buffer,
|
|
2229
|
+
const { parsed, type, metadata } = await prismarineNbt.parse(buffer, format2);
|
|
1889
2230
|
return Object.assign(parsed, {
|
|
1890
2231
|
format: type,
|
|
1891
2232
|
metadata,
|
|
@@ -1950,6 +2291,7 @@ var init_HostedAssetSource = __esm({
|
|
|
1950
2291
|
retryDefaults: true
|
|
1951
2292
|
};
|
|
1952
2293
|
_HostedAssetSource = class _HostedAssetSource extends AssetSource {
|
|
2294
|
+
/** Retries missing assets with the `minecraft` namespace and default root. Other failures reject. */
|
|
1953
2295
|
async loadOrRetryWithDefaults(key, parser) {
|
|
1954
2296
|
const direct = await this.load(key, parser);
|
|
1955
2297
|
if (typeof direct !== "undefined") {
|
|
@@ -2007,6 +2349,10 @@ var init_HostedAssetSource = __esm({
|
|
|
2007
2349
|
assetBasePath(key) {
|
|
2008
2350
|
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 + "/" : ""}`;
|
|
2009
2351
|
}
|
|
2352
|
+
/**
|
|
2353
|
+
* Creates a hosted source for {@link AssetLoader.addSource}.
|
|
2354
|
+
* Set `options.retryDefaults` to `false` to restrict lookups to the requested namespace and root.
|
|
2355
|
+
*/
|
|
2010
2356
|
constructor(root, options) {
|
|
2011
2357
|
super();
|
|
2012
2358
|
this._root = root;
|
|
@@ -2099,6 +2445,11 @@ var BrowserArchiveProxy;
|
|
|
2099
2445
|
var init_BrowserArchiveProxy = __esm({
|
|
2100
2446
|
"src/assets/source/archive/BrowserArchiveProxy.ts"() {
|
|
2101
2447
|
BrowserArchiveProxy = class {
|
|
2448
|
+
/**
|
|
2449
|
+
* Opens a ZIP for asset lookup.
|
|
2450
|
+
* @param id - Cache identity for this content. Change it when the pack changes.
|
|
2451
|
+
* A File supplies an identity from its name, size, and modification time when omitted.
|
|
2452
|
+
*/
|
|
2102
2453
|
constructor(blob, id) {
|
|
2103
2454
|
this._blob = blob;
|
|
2104
2455
|
this._reader = new ZipReader(new BlobReader(this._blob));
|
|
@@ -2119,6 +2470,162 @@ var init_BrowserArchiveProxy = __esm({
|
|
|
2119
2470
|
}
|
|
2120
2471
|
});
|
|
2121
2472
|
|
|
2473
|
+
// src/assets/source/archive/PackMetadata.ts
|
|
2474
|
+
function object(value, field) {
|
|
2475
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
2476
|
+
throw new Error(`Invalid ${field}: expected an object`);
|
|
2477
|
+
}
|
|
2478
|
+
return value;
|
|
2479
|
+
}
|
|
2480
|
+
function integer(value, field) {
|
|
2481
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
2482
|
+
throw new Error(`Invalid ${field}: expected a nonnegative integer`);
|
|
2483
|
+
}
|
|
2484
|
+
return value;
|
|
2485
|
+
}
|
|
2486
|
+
function format(value, field, upper = false) {
|
|
2487
|
+
const parts = Array.isArray(value) ? value : [value];
|
|
2488
|
+
if (parts.length < 1 || parts.length > 2) {
|
|
2489
|
+
throw new Error(`Invalid ${field}: expected a major version or [major, minor]`);
|
|
2490
|
+
}
|
|
2491
|
+
return [integer(parts[0], field), parts.length === 2 ? integer(parts[1], field) : upper ? Infinity : 0];
|
|
2492
|
+
}
|
|
2493
|
+
function compare(a, b) {
|
|
2494
|
+
return a[0] === b[0] ? a[1] - b[1] : a[0] - b[0];
|
|
2495
|
+
}
|
|
2496
|
+
function overlay(value, field) {
|
|
2497
|
+
const entry = object(value, field);
|
|
2498
|
+
const directory = entry.directory;
|
|
2499
|
+
if (typeof directory !== "string" || !directory || /[^-_a-zA-Z0-9.]/.test(directory) || directory === "." || directory === "..") {
|
|
2500
|
+
throw new Error(`Invalid ${field}.directory: expected a directory name without path separators`);
|
|
2501
|
+
}
|
|
2502
|
+
let min, max;
|
|
2503
|
+
if ("min_format" in entry || "max_format" in entry) {
|
|
2504
|
+
min = format(entry.min_format, `${field}.min_format`);
|
|
2505
|
+
max = format(entry.max_format, `${field}.max_format`, true);
|
|
2506
|
+
} else {
|
|
2507
|
+
const range = entry.formats;
|
|
2508
|
+
let lower, upper;
|
|
2509
|
+
if (typeof range === "number") {
|
|
2510
|
+
lower = upper = range;
|
|
2511
|
+
} else if (Array.isArray(range)) {
|
|
2512
|
+
if (range.length !== 2) throw new Error(`Invalid ${field}.formats: expected [min, max]`);
|
|
2513
|
+
[lower, upper] = range;
|
|
2514
|
+
} else {
|
|
2515
|
+
const bounds = object(range, `${field}.formats`);
|
|
2516
|
+
lower = bounds.min_inclusive;
|
|
2517
|
+
upper = bounds.max_inclusive;
|
|
2518
|
+
}
|
|
2519
|
+
min = [integer(lower, `${field}.formats.min_inclusive`), 0];
|
|
2520
|
+
max = [integer(upper, `${field}.formats.max_inclusive`), Infinity];
|
|
2521
|
+
}
|
|
2522
|
+
if (compare(min, max) > 0) throw new Error(`Invalid ${field}: minimum format exceeds maximum format`);
|
|
2523
|
+
return { directory, min, max };
|
|
2524
|
+
}
|
|
2525
|
+
function pattern(value, field) {
|
|
2526
|
+
if (value === void 0) return void 0;
|
|
2527
|
+
if (typeof value !== "string") throw new Error(`Invalid ${field}: expected a regular expression string`);
|
|
2528
|
+
try {
|
|
2529
|
+
return new RegExp(value);
|
|
2530
|
+
} catch (cause) {
|
|
2531
|
+
throw new Error(`Invalid ${field}: ${String(cause)}`);
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
var PackMetadata;
|
|
2535
|
+
var init_PackMetadata = __esm({
|
|
2536
|
+
"src/assets/source/archive/PackMetadata.ts"() {
|
|
2537
|
+
PackMetadata = class _PackMetadata {
|
|
2538
|
+
constructor(overlays, filters) {
|
|
2539
|
+
this.overlays = overlays;
|
|
2540
|
+
this.filters = filters;
|
|
2541
|
+
this.hasOverlays = overlays.length > 0;
|
|
2542
|
+
}
|
|
2543
|
+
static parse(value) {
|
|
2544
|
+
const metadata = object(value, "pack.mcmeta");
|
|
2545
|
+
let overlays = [];
|
|
2546
|
+
if ("overlays" in metadata) {
|
|
2547
|
+
const entries = object(metadata.overlays, "overlays").entries;
|
|
2548
|
+
if (!Array.isArray(entries)) throw new Error("Invalid overlays.entries: expected an array");
|
|
2549
|
+
overlays = entries.map((entry, index) => overlay(entry, `overlays.entries[${index}]`));
|
|
2550
|
+
}
|
|
2551
|
+
let filters = [];
|
|
2552
|
+
if ("filter" in metadata) {
|
|
2553
|
+
const entries = object(metadata.filter, "filter").block;
|
|
2554
|
+
if (!Array.isArray(entries)) throw new Error("Invalid filter.block: expected an array");
|
|
2555
|
+
filters = entries.map((value2, index) => {
|
|
2556
|
+
const field = `filter.block[${index}]`;
|
|
2557
|
+
const entry = object(value2, field);
|
|
2558
|
+
return { namespace: pattern(entry.namespace, `${field}.namespace`), path: pattern(entry.path, `${field}.path`) };
|
|
2559
|
+
});
|
|
2560
|
+
}
|
|
2561
|
+
return new _PackMetadata(overlays, filters);
|
|
2562
|
+
}
|
|
2563
|
+
/** Returns applicable directories in lookup order, with the last declared overlay first. */
|
|
2564
|
+
overlayDirectories(target) {
|
|
2565
|
+
const selected = format(target, "target format");
|
|
2566
|
+
return this.overlays.filter((entry) => compare(selected, entry.min) >= 0 && compare(selected, entry.max) <= 0).map((entry) => entry.directory).reverse();
|
|
2567
|
+
}
|
|
2568
|
+
blocks(namespace, path) {
|
|
2569
|
+
return this.filters.some((entry) => entry.namespace?.test(namespace) ?? true) && this.filters.some((entry) => entry.path?.test(path) ?? true);
|
|
2570
|
+
}
|
|
2571
|
+
};
|
|
2572
|
+
}
|
|
2573
|
+
});
|
|
2574
|
+
|
|
2575
|
+
// src/assets/source/archive/PackFormats.ts
|
|
2576
|
+
var PackFormats;
|
|
2577
|
+
var init_PackFormats = __esm({
|
|
2578
|
+
"src/assets/source/archive/PackFormats.ts"() {
|
|
2579
|
+
init_Requests();
|
|
2580
|
+
PackFormats = class {
|
|
2581
|
+
/** Resolves pack formats from Mojang's client metadata hosted on assets.mcasset.cloud. */
|
|
2582
|
+
static async get(version2, type) {
|
|
2583
|
+
let pending = this.versions.get(version2);
|
|
2584
|
+
if (!pending || pending.expires <= Date.now()) {
|
|
2585
|
+
pending = {
|
|
2586
|
+
formats: this.load(version2),
|
|
2587
|
+
expires: ["latest", "release", "snapshot"].includes(version2) ? Date.now() + 3e5 : Infinity
|
|
2588
|
+
};
|
|
2589
|
+
this.versions.set(version2, pending);
|
|
2590
|
+
}
|
|
2591
|
+
try {
|
|
2592
|
+
return (await pending.formats)[type];
|
|
2593
|
+
} catch (cause) {
|
|
2594
|
+
if (this.versions.get(version2) === pending) this.versions.delete(version2);
|
|
2595
|
+
const option = type === "assets" ? "resourcePackFormat" : "dataPackFormat";
|
|
2596
|
+
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
2597
|
+
throw new Error(`Could not resolve the pack format for Minecraft ${version2}. Set ArchiveAssetSource ${option} explicitly. ${reason}`);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
static async load(version2) {
|
|
2601
|
+
const response = await Requests.genericRequest({
|
|
2602
|
+
url: `https://assets.mcasset.cloud/${encodeURIComponent(version2)}/game-version.json`,
|
|
2603
|
+
responseType: "json"
|
|
2604
|
+
});
|
|
2605
|
+
const data = response.data;
|
|
2606
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
2607
|
+
throw new Error("Expected version metadata to be an object");
|
|
2608
|
+
}
|
|
2609
|
+
const packs = data.pack_version;
|
|
2610
|
+
if (typeof packs !== "number" && (!packs || typeof packs !== "object" || Array.isArray(packs))) {
|
|
2611
|
+
throw new Error("Expected pack_version to be a number or an object");
|
|
2612
|
+
}
|
|
2613
|
+
const format2 = (type) => {
|
|
2614
|
+
const modern = typeof packs === "object" && ["resource_major", "resource_minor", "data_major", "data_minor"].some((key) => key in packs);
|
|
2615
|
+
const major = typeof packs === "number" ? packs : packs[modern ? `${type}_major` : type];
|
|
2616
|
+
const minor = modern ? packs[`${type}_minor`] : 0;
|
|
2617
|
+
if (typeof major !== "number" || !Number.isSafeInteger(major) || major < 0 || typeof minor !== "number" || !Number.isSafeInteger(minor) || minor < 0) {
|
|
2618
|
+
throw new Error(`Invalid ${type} pack format in version metadata`);
|
|
2619
|
+
}
|
|
2620
|
+
return Object.freeze([major, minor]);
|
|
2621
|
+
};
|
|
2622
|
+
return { assets: format2("resource"), data: format2("data") };
|
|
2623
|
+
}
|
|
2624
|
+
};
|
|
2625
|
+
PackFormats.versions = /* @__PURE__ */ new Map();
|
|
2626
|
+
}
|
|
2627
|
+
});
|
|
2628
|
+
|
|
2122
2629
|
// src/assets/source/archive/ArchiveAssetSource.ts
|
|
2123
2630
|
var ArchiveAssetSource;
|
|
2124
2631
|
var init_ArchiveAssetSource = __esm({
|
|
@@ -2127,35 +2634,78 @@ var init_ArchiveAssetSource = __esm({
|
|
|
2127
2634
|
init_request();
|
|
2128
2635
|
init_HostedAssetSource();
|
|
2129
2636
|
init_BrowserArchiveProxy();
|
|
2637
|
+
init_AssetLoader();
|
|
2638
|
+
init_PackMetadata();
|
|
2639
|
+
init_PackFormats();
|
|
2130
2640
|
ArchiveAssetSource = class _ArchiveAssetSource extends AssetSource {
|
|
2131
|
-
constructor(archiveProxy) {
|
|
2641
|
+
constructor(archiveProxy, options = {}) {
|
|
2132
2642
|
super();
|
|
2133
|
-
console.log(archiveProxy);
|
|
2134
2643
|
this._archiveProxy = archiveProxy;
|
|
2644
|
+
const copyFormat = (format2) => typeof format2 === "object" ? [format2[0], format2[1]] : format2;
|
|
2645
|
+
this.options = {
|
|
2646
|
+
resourcePackFormat: copyFormat(options.resourcePackFormat),
|
|
2647
|
+
dataPackFormat: copyFormat(options.dataPackFormat)
|
|
2648
|
+
};
|
|
2135
2649
|
}
|
|
2136
|
-
|
|
2137
|
-
|
|
2650
|
+
/** Creates a browser ZIP source from a Blob or File. */
|
|
2651
|
+
static blob(blob, options) {
|
|
2652
|
+
return new _ArchiveAssetSource(new BrowserArchiveProxy(blob), options);
|
|
2138
2653
|
}
|
|
2139
2654
|
get cacheId() {
|
|
2140
2655
|
const id = this._archiveProxy.id;
|
|
2141
|
-
|
|
2656
|
+
const formats = [this.options.resourcePackFormat ?? AssetLoader.version, this.options.dataPackFormat ?? AssetLoader.version];
|
|
2657
|
+
return id === void 0 ? void 0 : `archive-metadata:1:${id}:${JSON.stringify(formats)}`;
|
|
2142
2658
|
}
|
|
2143
2659
|
async getEntries() {
|
|
2144
|
-
return this._archiveProxy.getEntries()
|
|
2660
|
+
return this.entries ?? (this.entries = this._archiveProxy.getEntries().catch((error) => {
|
|
2661
|
+
this.entries = void 0;
|
|
2662
|
+
throw error;
|
|
2663
|
+
}));
|
|
2145
2664
|
}
|
|
2146
2665
|
async getEntry(path) {
|
|
2147
2666
|
const entries = await this.getEntries();
|
|
2148
2667
|
return entries.find((e) => e.filename === path);
|
|
2149
2668
|
}
|
|
2669
|
+
async getMetadata(key) {
|
|
2670
|
+
try {
|
|
2671
|
+
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) => {
|
|
2672
|
+
this.metadata = void 0;
|
|
2673
|
+
throw error;
|
|
2674
|
+
})));
|
|
2675
|
+
} catch (cause) {
|
|
2676
|
+
throw new AssetLoadError(this, key, "pack.mcmeta", cause);
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
async blocks(key) {
|
|
2680
|
+
if (key.rootType !== "assets" && key.rootType !== "data") return false;
|
|
2681
|
+
const metadata = await this.getMetadata(key);
|
|
2682
|
+
const path = `${key.assetType !== void 0 ? key.assetType + "/" : ""}${key.getFullPath()}${key.extension}`;
|
|
2683
|
+
return metadata.blocks(key.namespace, path);
|
|
2684
|
+
}
|
|
2150
2685
|
async get(key, parser) {
|
|
2151
2686
|
const responseParser = HostedAssetSource.PARSER_MAP.get(parser);
|
|
2152
2687
|
return this.load(key, responseParser);
|
|
2153
2688
|
}
|
|
2154
2689
|
async load(key, parser) {
|
|
2155
2690
|
const path = `${this.assetBasePath(key)}${key.type !== void 0 ? key.type + "/" : ""}${key.path}${key.extension}`;
|
|
2691
|
+
const version2 = AssetLoader.version;
|
|
2692
|
+
let selectedPath = path;
|
|
2156
2693
|
let url;
|
|
2157
2694
|
try {
|
|
2158
|
-
|
|
2695
|
+
let directories = [];
|
|
2696
|
+
if (key.rootType === "assets" || key.rootType === "data") {
|
|
2697
|
+
const metadata = await this.getMetadata(key);
|
|
2698
|
+
if (metadata.hasOverlays) {
|
|
2699
|
+
const format2 = (key.rootType === "assets" ? this.options.resourcePackFormat : this.options.dataPackFormat) ?? await PackFormats.get(version2, key.rootType);
|
|
2700
|
+
directories = metadata.overlayDirectories(format2);
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
let entry;
|
|
2704
|
+
for (const directory of [...directories, ""]) {
|
|
2705
|
+
selectedPath = directory ? `${directory}/${path}` : path;
|
|
2706
|
+
entry = await this.getEntry(selectedPath);
|
|
2707
|
+
if (entry) break;
|
|
2708
|
+
}
|
|
2159
2709
|
if (!entry) {
|
|
2160
2710
|
return void 0;
|
|
2161
2711
|
}
|
|
@@ -2168,7 +2718,8 @@ var init_ArchiveAssetSource = __esm({
|
|
|
2168
2718
|
}
|
|
2169
2719
|
return await parser.parse(response);
|
|
2170
2720
|
} catch (cause) {
|
|
2171
|
-
|
|
2721
|
+
if (cause instanceof AssetLoadError) throw cause;
|
|
2722
|
+
throw new AssetLoadError(this, key, selectedPath, cause);
|
|
2172
2723
|
} finally {
|
|
2173
2724
|
if (url !== void 0) {
|
|
2174
2725
|
URL.revokeObjectURL(url);
|
|
@@ -2201,6 +2752,8 @@ var init_archive = __esm({
|
|
|
2201
2752
|
init_ArchiveEntry();
|
|
2202
2753
|
init_ArchiveProxy();
|
|
2203
2754
|
init_BrowserArchiveProxy();
|
|
2755
|
+
init_PackFormats();
|
|
2756
|
+
init_PackMetadata();
|
|
2204
2757
|
}
|
|
2205
2758
|
});
|
|
2206
2759
|
|
|
@@ -2229,8 +2782,9 @@ var init_AssetLoader = __esm({
|
|
|
2229
2782
|
static get version() {
|
|
2230
2783
|
return this.ROOT.substring(this.ROOT.lastIndexOf("/") + 1);
|
|
2231
2784
|
}
|
|
2232
|
-
|
|
2233
|
-
|
|
2785
|
+
/** Selects an mcasset.cloud version, updates the vanilla source, and clears in-memory caches. */
|
|
2786
|
+
static setVersion(version2) {
|
|
2787
|
+
this.ROOT = `https://assets.mcasset.cloud/${version2}`;
|
|
2234
2788
|
const source = new HostedAssetSource(this.ROOT, { retryDefaults: false });
|
|
2235
2789
|
const index = this._SOURCES.findIndex((s) => s.key === "mcassets");
|
|
2236
2790
|
if (index !== -1) {
|
|
@@ -2262,6 +2816,13 @@ var init_AssetLoader = __esm({
|
|
|
2262
2816
|
return scope ? `${scope}
|
|
2263
2817
|
${key}` : key;
|
|
2264
2818
|
}
|
|
2819
|
+
/**
|
|
2820
|
+
* Registers a source at the highest priority.
|
|
2821
|
+
* Call {@link Caching.clear} after changing sources to discard previously loaded assets.
|
|
2822
|
+
*
|
|
2823
|
+
* @param key - Name used to replace or remove this source.
|
|
2824
|
+
* @param override - Removes the first source with this name before adding the new one.
|
|
2825
|
+
*/
|
|
2265
2826
|
static addSource(key, source, override = true) {
|
|
2266
2827
|
if (override) {
|
|
2267
2828
|
const existing = this.removeSource(key);
|
|
@@ -2272,6 +2833,7 @@ ${key}` : key;
|
|
|
2272
2833
|
this._SOURCES.unshift({ key, source });
|
|
2273
2834
|
console.log(p4, "Added AssetSource", key);
|
|
2274
2835
|
}
|
|
2836
|
+
/** Removes and returns the first source with this name. Call {@link Caching.clear} to reload assets. */
|
|
2275
2837
|
static removeSource(key) {
|
|
2276
2838
|
const index = this._SOURCES.findIndex((s) => s.key === key);
|
|
2277
2839
|
if (index != -1) {
|
|
@@ -2282,14 +2844,16 @@ ${key}` : key;
|
|
|
2282
2844
|
}
|
|
2283
2845
|
return void 0;
|
|
2284
2846
|
}
|
|
2847
|
+
/** Loads from all sources and returns defined results in priority order. Any source failure rejects the call. */
|
|
2285
2848
|
static async getAll(key, parser) {
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2849
|
+
const sources = [...this._SOURCES];
|
|
2850
|
+
const results = [];
|
|
2851
|
+
for (const { source } of sources) {
|
|
2852
|
+
const asset2 = await source.get(key, parser);
|
|
2853
|
+
if (asset2 != void 0) results.push(asset2);
|
|
2854
|
+
if (await source.blocks(key)) break;
|
|
2289
2855
|
}
|
|
2290
|
-
return
|
|
2291
|
-
return results.filter((r) => r != void 0);
|
|
2292
|
-
});
|
|
2856
|
+
return results;
|
|
2293
2857
|
}
|
|
2294
2858
|
/** Returns the first defined result in source-priority order, without merging assets. */
|
|
2295
2859
|
static async get(key, parser) {
|
|
@@ -2310,17 +2874,22 @@ ${key}` : key;
|
|
|
2310
2874
|
/** Tries each path within a source before considering lower-priority sources. */
|
|
2311
2875
|
static async getFirst(keys, parser) {
|
|
2312
2876
|
const sources = [...this._SOURCES];
|
|
2877
|
+
let remaining = [...keys];
|
|
2313
2878
|
for (const source of sources) {
|
|
2314
|
-
for (const key of
|
|
2879
|
+
for (const key of remaining) {
|
|
2315
2880
|
const result = await source.source.get(key, parser);
|
|
2316
2881
|
if (result !== void 0) {
|
|
2317
2882
|
return { key, asset: result };
|
|
2318
2883
|
}
|
|
2319
2884
|
}
|
|
2885
|
+
const blocked = await Promise.all(remaining.map((key) => source.source.blocks(key)));
|
|
2886
|
+
remaining = remaining.filter((_, index) => !blocked[index]);
|
|
2887
|
+
if (!remaining.length) break;
|
|
2320
2888
|
}
|
|
2321
2889
|
return void 0;
|
|
2322
2890
|
}
|
|
2323
2891
|
};
|
|
2892
|
+
/** Default asset base URL. Use {@link setVersion} to select a Minecraft version. */
|
|
2324
2893
|
_AssetLoader.ROOT = DEFAULT_ROOT;
|
|
2325
2894
|
_AssetLoader._SOURCES = [];
|
|
2326
2895
|
/** Used when a registered source cannot identify its content, so nothing persists past this session. */
|
|
@@ -2345,14 +2914,15 @@ var init_ModelTextures = __esm({
|
|
|
2345
2914
|
static get PERSISTENT_META_CACHE() {
|
|
2346
2915
|
return this._persistentMetaCache ?? (this._persistentMetaCache = PersistentCache.open("minerender-texturemeta"));
|
|
2347
2916
|
}
|
|
2917
|
+
/** Loads and decodes a texture into a readable canvas, or returns `undefined` when missing. */
|
|
2348
2918
|
static async get(key) {
|
|
2349
2919
|
const keyStr = key.serialize();
|
|
2350
2920
|
const pending = this.preload(key);
|
|
2351
2921
|
const cached2 = Caching.textureAssetCache.getIfPresent(keyStr);
|
|
2352
2922
|
try {
|
|
2353
|
-
const
|
|
2354
|
-
if (
|
|
2355
|
-
return await ImageLoader.infoToCanvasData(
|
|
2923
|
+
const asset2 = await pending;
|
|
2924
|
+
if (asset2) {
|
|
2925
|
+
return await ImageLoader.infoToCanvasData(asset2);
|
|
2356
2926
|
}
|
|
2357
2927
|
return void 0;
|
|
2358
2928
|
} catch (err) {
|
|
@@ -2362,16 +2932,18 @@ var init_ModelTextures = __esm({
|
|
|
2362
2932
|
throw err;
|
|
2363
2933
|
}
|
|
2364
2934
|
}
|
|
2935
|
+
/** Fetches and caches encoded texture bytes without decoding the pixels. */
|
|
2365
2936
|
static async preload(key) {
|
|
2366
2937
|
const keyStr = key.serialize();
|
|
2367
2938
|
return Caching.textureAssetCache.get(keyStr, (k) => {
|
|
2368
|
-
return AssetLoader.get(key, "image" /* IMAGE */).then((
|
|
2369
|
-
if (
|
|
2370
|
-
|
|
2371
|
-
return
|
|
2939
|
+
return AssetLoader.get(key, "image" /* IMAGE */).then((asset2) => {
|
|
2940
|
+
if (asset2)
|
|
2941
|
+
asset2.key = key;
|
|
2942
|
+
return asset2;
|
|
2372
2943
|
});
|
|
2373
2944
|
});
|
|
2374
2945
|
}
|
|
2946
|
+
/** Loads a texture's `.mcmeta` file, or returns `undefined` when no metadata file exists. */
|
|
2375
2947
|
static async getMeta(key) {
|
|
2376
2948
|
if (!key.extension || !key.extension.endsWith(".mcmeta")) {
|
|
2377
2949
|
key = Object.assign(new AssetKey("", ""), key);
|
|
@@ -2380,14 +2952,15 @@ var init_ModelTextures = __esm({
|
|
|
2380
2952
|
const keyStr = key.serialize();
|
|
2381
2953
|
return Caching.textureMetaCache.get(keyStr, (k) => {
|
|
2382
2954
|
return this.PERSISTENT_META_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
|
|
2383
|
-
return AssetLoader.get(key, "meta" /* META */).then((
|
|
2384
|
-
if (
|
|
2385
|
-
|
|
2386
|
-
return
|
|
2955
|
+
return AssetLoader.get(key, "meta" /* META */).then((asset2) => {
|
|
2956
|
+
if (asset2)
|
|
2957
|
+
asset2.key = key;
|
|
2958
|
+
return asset2;
|
|
2387
2959
|
});
|
|
2388
2960
|
});
|
|
2389
2961
|
});
|
|
2390
2962
|
}
|
|
2963
|
+
/** Clears persisted texture metadata. Use {@link Caching.clear} to discard in-memory textures and metadata. */
|
|
2391
2964
|
static async clearCache() {
|
|
2392
2965
|
await this.PERSISTENT_META_CACHE.clear();
|
|
2393
2966
|
}
|
|
@@ -2454,6 +3027,7 @@ var init_Ticker = __esm({
|
|
|
2454
3027
|
}
|
|
2455
3028
|
return handle;
|
|
2456
3029
|
}
|
|
3030
|
+
/** Starts or resumes the shared tick loop. Repeated calls have no effect. */
|
|
2457
3031
|
static start() {
|
|
2458
3032
|
if (this.interval) return;
|
|
2459
3033
|
this.interval = this.schedule(() => {
|
|
@@ -2476,6 +3050,7 @@ var init_Ticker = __esm({
|
|
|
2476
3050
|
_Ticker.fiveSecondTicks = 0;
|
|
2477
3051
|
}, 1e3 * 5);
|
|
2478
3052
|
}
|
|
3053
|
+
/** Pauses the loop while retaining callbacks for a later {@link start}. */
|
|
2479
3054
|
static stop() {
|
|
2480
3055
|
if (this.interval) clearInterval(this.interval);
|
|
2481
3056
|
if (this.oneSecondTicksTracker) clearInterval(this.oneSecondTicksTracker);
|
|
@@ -2484,12 +3059,14 @@ var init_Ticker = __esm({
|
|
|
2484
3059
|
this.oneSecondTicksTracker = void 0;
|
|
2485
3060
|
this.fiveSecondTicksTracker = void 0;
|
|
2486
3061
|
}
|
|
3062
|
+
/** Registers a callback, starts the loop if needed, and returns an ID for {@link remove}. */
|
|
2487
3063
|
static add(tick) {
|
|
2488
3064
|
this.start();
|
|
2489
3065
|
const c = this.counter++;
|
|
2490
3066
|
this._tickers.set(c, tick);
|
|
2491
3067
|
return c;
|
|
2492
3068
|
}
|
|
3069
|
+
/** Removes a callback by ID and stops the timers when no callbacks remain. */
|
|
2493
3070
|
static remove(c) {
|
|
2494
3071
|
if (typeof c === "number") this._tickers.delete(c);
|
|
2495
3072
|
if (!this._tickers.size) this.stop();
|
|
@@ -2503,6 +3080,7 @@ var init_Ticker = __esm({
|
|
|
2503
3080
|
static get tpsFiveSeconds() {
|
|
2504
3081
|
return this.fiveSecondTps;
|
|
2505
3082
|
}
|
|
3083
|
+
/** Stops the loop and removes all registered callbacks. */
|
|
2506
3084
|
static dispose() {
|
|
2507
3085
|
this.stop();
|
|
2508
3086
|
this._tickers.clear();
|
|
@@ -2557,6 +3135,7 @@ var init_TextureAtlas = __esm({
|
|
|
2557
3135
|
}
|
|
2558
3136
|
};
|
|
2559
3137
|
}
|
|
3138
|
+
/** Copies pixels for a texture variable from its current atlas rectangle. */
|
|
2560
3139
|
getData(texture) {
|
|
2561
3140
|
const pos = this.positions[texture];
|
|
2562
3141
|
const size = this.sizes[texture];
|
|
@@ -2767,6 +3346,37 @@ var init_UVMapper = __esm({
|
|
|
2767
3346
|
static facesToUv(faces, originalTextureSize, actualTextureSize) {
|
|
2768
3347
|
return new Float32BufferAttribute2(this.facesToUvArray(faces, originalTextureSize, actualTextureSize), 2);
|
|
2769
3348
|
}
|
|
3349
|
+
/** Bounds texture sampling to texel centres without changing cropped face UVs. */
|
|
3350
|
+
static setAtlasUvBounds(geometry, faces, atlas) {
|
|
3351
|
+
const uv = geometry.getAttribute("uv");
|
|
3352
|
+
const bounds = new Float32Array(uv.count * 4);
|
|
3353
|
+
for (const [faceIndex, faceName] of CUBE_FACES.entries()) {
|
|
3354
|
+
const texture = faces[faceName]?.texture?.substring(1);
|
|
3355
|
+
const position = texture && atlas.positions[texture];
|
|
3356
|
+
const size = texture && atlas.sizes[texture];
|
|
3357
|
+
let minU = Infinity, minV = Infinity, maxU = -Infinity, maxV = -Infinity;
|
|
3358
|
+
if (position && size) {
|
|
3359
|
+
minU = position[0] / atlas.image.width;
|
|
3360
|
+
maxU = (position[0] + size[0]) / atlas.image.width;
|
|
3361
|
+
minV = 1 - (position[1] + size[1]) / atlas.image.height;
|
|
3362
|
+
maxV = 1 - position[1] / atlas.image.height;
|
|
3363
|
+
} else {
|
|
3364
|
+
for (let corner = 0; corner < 4; corner++) {
|
|
3365
|
+
const index = faceIndex * 4 + corner;
|
|
3366
|
+
minU = Math.min(minU, uv.getX(index));
|
|
3367
|
+
minV = Math.min(minV, uv.getY(index));
|
|
3368
|
+
maxU = Math.max(maxU, uv.getX(index));
|
|
3369
|
+
maxV = Math.max(maxV, uv.getY(index));
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
const insetU = Math.min(0.5 / atlas.image.width, (maxU - minU) / 2);
|
|
3373
|
+
const insetV = Math.min(0.5 / atlas.image.height, (maxV - minV) / 2);
|
|
3374
|
+
for (let corner = 0; corner < 4; corner++) {
|
|
3375
|
+
bounds.set([minU + insetU, minV + insetV, maxU - insetU, maxV - insetV], (faceIndex * 4 + corner) * 4);
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
geometry.setAttribute("uvBounds", new Float32BufferAttribute2(bounds, 4));
|
|
3379
|
+
}
|
|
2770
3380
|
static lockUvs(geometry, faces, atlas, rotation) {
|
|
2771
3381
|
const uv = geometry.getAttribute("uv");
|
|
2772
3382
|
for (let faceIndex = 0; faceIndex < CUBE_FACES.length; faceIndex++) {
|
|
@@ -2803,6 +3413,7 @@ var init_UVMapper = __esm({
|
|
|
2803
3413
|
}
|
|
2804
3414
|
uv.needsUpdate = true;
|
|
2805
3415
|
}
|
|
3416
|
+
/** Returns the shared atlas for a keyed, merged model, creating it on first use. */
|
|
2806
3417
|
static async getAtlas(model) {
|
|
2807
3418
|
const keyStr = model.key.serialize();
|
|
2808
3419
|
return Caching.modelTextureAtlasCache.get(keyStr, (k) => {
|
|
@@ -2873,6 +3484,11 @@ var init_UVMapper = __esm({
|
|
|
2873
3484
|
}
|
|
2874
3485
|
return [tl, tr, bl, br];
|
|
2875
3486
|
}
|
|
3487
|
+
/**
|
|
3488
|
+
* Combines model textures and animation metadata into an atlas.
|
|
3489
|
+
* Mapped UVs are stored on copies of the model's elements, available through the returned atlas.
|
|
3490
|
+
* Returns `undefined` for models without a texture map.
|
|
3491
|
+
*/
|
|
2876
3492
|
static async createAtlas(originalModel) {
|
|
2877
3493
|
const textureMap = {};
|
|
2878
3494
|
const metaMap = {};
|
|
@@ -2893,12 +3509,12 @@ var init_UVMapper = __esm({
|
|
|
2893
3509
|
} else {
|
|
2894
3510
|
uniqueTextureNames.push(textureKey);
|
|
2895
3511
|
const assetKey = AssetKey.parse("textures", textureValue, model.key);
|
|
2896
|
-
promises.push(ModelTextures.get(assetKey).then((
|
|
2897
|
-
if (!
|
|
3512
|
+
promises.push(ModelTextures.get(assetKey).then((asset2) => {
|
|
3513
|
+
if (!asset2) {
|
|
2898
3514
|
console.warn(p5, "Missing texture", assetKey);
|
|
2899
3515
|
return;
|
|
2900
3516
|
}
|
|
2901
|
-
textureMap[textureKey] = new WrappedImage(
|
|
3517
|
+
textureMap[textureKey] = new WrappedImage(asset2);
|
|
2902
3518
|
}));
|
|
2903
3519
|
promises.push(ModelTextures.getMeta(assetKey).then((meta) => {
|
|
2904
3520
|
metaMap[textureKey] = meta;
|
|
@@ -3237,14 +3853,15 @@ var init_Entities = __esm({
|
|
|
3237
3853
|
init_MineRenderError();
|
|
3238
3854
|
init_ModelTextures();
|
|
3239
3855
|
Entities = class {
|
|
3856
|
+
/** Lists model paths in the `minecraft` namespace, including subdirectories and excluding `.json`. */
|
|
3240
3857
|
static async getEntityList() {
|
|
3241
3858
|
const collect = async (path) => {
|
|
3242
3859
|
const prefix2 = path ? `${path}/` : "";
|
|
3243
3860
|
const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json");
|
|
3244
|
-
const
|
|
3245
|
-
if (!
|
|
3246
|
-
const files =
|
|
3247
|
-
const children = await Promise.all(
|
|
3861
|
+
const list2 = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
|
|
3862
|
+
if (!list2) return [];
|
|
3863
|
+
const files = list2.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
|
|
3864
|
+
const children = await Promise.all(list2.directories.map((directory) => collect(`${prefix2}${directory}`)));
|
|
3248
3865
|
return [...files, ...children.flat()];
|
|
3249
3866
|
};
|
|
3250
3867
|
return collect("");
|
|
@@ -3254,6 +3871,7 @@ var init_Entities = __esm({
|
|
|
3254
3871
|
const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
|
|
3255
3872
|
return Caching.entityModelCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
|
|
3256
3873
|
}
|
|
3874
|
+
/** Returns selectable geometry-layer names, or an empty list when the model is missing. */
|
|
3257
3875
|
static async getLayerList(modelKey) {
|
|
3258
3876
|
return Object.keys((await this.getModelFile(modelKey))?.layers ?? {});
|
|
3259
3877
|
}
|
|
@@ -3261,6 +3879,15 @@ var init_Entities = __esm({
|
|
|
3261
3879
|
static async getPassList(modelKey) {
|
|
3262
3880
|
return (await this.getModelFile(modelKey))?.passes ?? [];
|
|
3263
3881
|
}
|
|
3882
|
+
/**
|
|
3883
|
+
* Loads the layers and textures to pass to {@link MineRenderScene.addEntity}.
|
|
3884
|
+
* By default, selects `main`, unconditional passes, and passes enabled by `options.when`.
|
|
3885
|
+
*
|
|
3886
|
+
* @param modelKey - Dataset model, such as `new BasicAssetKey("minecraft", "zombie")`.
|
|
3887
|
+
* @param textureKey - Texture override for the first selected layer.
|
|
3888
|
+
* @param options - Layer selection, conditional passes, and per-layer texture overrides.
|
|
3889
|
+
* @returns The selected model, or `undefined` when its dataset file is missing.
|
|
3890
|
+
*/
|
|
3264
3891
|
static async getEntity(modelKey, textureKey, options) {
|
|
3265
3892
|
const model = await this.getModelFile(modelKey);
|
|
3266
3893
|
if (!model) return void 0;
|
|
@@ -3306,6 +3933,7 @@ var init_Entities = __esm({
|
|
|
3306
3933
|
const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
|
|
3307
3934
|
return file?.animations;
|
|
3308
3935
|
}
|
|
3936
|
+
/** Finds a fallback texture using the model path, its directory, and vanilla variant data. */
|
|
3309
3937
|
static async resolveTexture(modelKey) {
|
|
3310
3938
|
const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
|
|
3311
3939
|
const root = isAssetKey(modelKey) ? modelKey.root : void 0;
|
|
@@ -3317,8 +3945,8 @@ var init_Entities = __esm({
|
|
|
3317
3945
|
if (await ModelTextures.get(texture2)) return texture2;
|
|
3318
3946
|
}
|
|
3319
3947
|
const listKey = new AssetKey(modelKey.namespace, "_list", `${name}_variant`, void 0, "data", ".json", root);
|
|
3320
|
-
const
|
|
3321
|
-
const files =
|
|
3948
|
+
const list2 = await AssetLoader.get(listKey, "list" /* LIST */);
|
|
3949
|
+
const files = list2?.files.filter((file2) => file2.endsWith(".json") && file2 !== "_list.json");
|
|
3322
3950
|
const file = files?.find((file2) => file2 === "temperate.json") ?? files?.[0];
|
|
3323
3951
|
if (!file) return void 0;
|
|
3324
3952
|
const variantKey = new AssetKey(modelKey.namespace, file.slice(0, -5), `${name}_variant`, void 0, "data", ".json", root);
|
|
@@ -3423,27 +4051,27 @@ var init_EntityAnimationPlayer = __esm({
|
|
|
3423
4051
|
for (const layer of layers) {
|
|
3424
4052
|
const objects = [];
|
|
3425
4053
|
if (poses) {
|
|
3426
|
-
layer.traverse((
|
|
3427
|
-
if (poses.has(
|
|
4054
|
+
layer.traverse((object3) => {
|
|
4055
|
+
if (poses.has(object3)) objects.push(object3);
|
|
3428
4056
|
});
|
|
3429
4057
|
} else {
|
|
3430
4058
|
for (const bone of Object.keys(animation.bones)) {
|
|
3431
|
-
const
|
|
3432
|
-
if (
|
|
4059
|
+
const object3 = layer.getObjectByName(`group:${bone}`);
|
|
4060
|
+
if (object3) objects.push(object3);
|
|
3433
4061
|
}
|
|
3434
4062
|
}
|
|
3435
|
-
for (const
|
|
3436
|
-
const bone =
|
|
3437
|
-
const pose = poses?.get(
|
|
4063
|
+
for (const object3 of objects) {
|
|
4064
|
+
const bone = object3.name.slice("group:".length);
|
|
4065
|
+
const pose = poses?.get(object3);
|
|
3438
4066
|
this.parts.push({
|
|
3439
4067
|
bone,
|
|
3440
|
-
object,
|
|
3441
|
-
position:
|
|
3442
|
-
rotation:
|
|
3443
|
-
scale:
|
|
3444
|
-
basePosition: pose ? new Vector35(...pose.offset) :
|
|
3445
|
-
baseRotation: pose ? new Euler3(...pose.rotation, "ZYX") :
|
|
3446
|
-
baseScale: pose ? new Vector35(...pose.scale ?? [1, 1, 1]) :
|
|
4068
|
+
object: object3,
|
|
4069
|
+
position: object3.position.clone(),
|
|
4070
|
+
rotation: object3.rotation.clone(),
|
|
4071
|
+
scale: object3.scale.clone(),
|
|
4072
|
+
basePosition: pose ? new Vector35(...pose.offset) : object3.position.clone(),
|
|
4073
|
+
baseRotation: pose ? new Euler3(...pose.rotation, "ZYX") : object3.rotation.clone(),
|
|
4074
|
+
baseScale: pose ? new Vector35(...pose.scale ?? [1, 1, 1]) : object3.scale.clone()
|
|
3447
4075
|
});
|
|
3448
4076
|
}
|
|
3449
4077
|
}
|
|
@@ -3466,12 +4094,14 @@ var init_EntityAnimationPlayer = __esm({
|
|
|
3466
4094
|
this._animation = void 0;
|
|
3467
4095
|
this._time = 0;
|
|
3468
4096
|
}
|
|
4097
|
+
/** Applies the pose at an absolute playback time. Returns `false` when no clip is active. */
|
|
3469
4098
|
setTime(seconds) {
|
|
3470
4099
|
if (!this._animation) return false;
|
|
3471
4100
|
this._time = seconds;
|
|
3472
4101
|
this.apply();
|
|
3473
4102
|
return true;
|
|
3474
4103
|
}
|
|
4104
|
+
/** Advances playback by elapsed seconds multiplied by the speed passed to {@link play}. */
|
|
3475
4105
|
advance(deltaSeconds) {
|
|
3476
4106
|
return this.setTime(this._time + deltaSeconds * this.speed);
|
|
3477
4107
|
}
|
|
@@ -3509,6 +4139,7 @@ var init_InstanceReference = __esm({
|
|
|
3509
4139
|
if (!this.instanceable.isInstanceActive(this.index, this)) throw new MineRenderError("Instance has been removed");
|
|
3510
4140
|
return this.instanceable;
|
|
3511
4141
|
}
|
|
4142
|
+
/** Releases this placement's slot for reuse. Repeated calls have no effect. */
|
|
3512
4143
|
removeFromScene() {
|
|
3513
4144
|
if (this.instanceable.isInstanceActive(this.index, this)) this.instanceable.removeInstanceAt(this.index);
|
|
3514
4145
|
}
|
|
@@ -3521,9 +4152,11 @@ var init_InstanceReference = __esm({
|
|
|
3521
4152
|
setMatrix(matrix) {
|
|
3522
4153
|
this.activeInstanceable.setMatrixAt(this.index, matrix);
|
|
3523
4154
|
}
|
|
4155
|
+
/** Hides or restores this placement while preserving its transform and allocated slot. */
|
|
3524
4156
|
setVisible(visible) {
|
|
3525
4157
|
this.activeInstanceable.setInstanceVisibleAt(this.index, visible);
|
|
3526
4158
|
}
|
|
4159
|
+
/** Updates the supplied transform components. Positions use scene units and rotations use radians. */
|
|
3527
4160
|
setPositionRotationScale(position, rotation, scale) {
|
|
3528
4161
|
this.activeInstanceable.setPositionRotationScaleAt(this.index, position, rotation, scale);
|
|
3529
4162
|
}
|
|
@@ -3592,6 +4225,7 @@ var init_SceneObject = __esm({
|
|
|
3592
4225
|
}
|
|
3593
4226
|
async init() {
|
|
3594
4227
|
}
|
|
4228
|
+
/** Marks ancestor scenes for redraw and emits a `change` event. */
|
|
3595
4229
|
notifyDirty() {
|
|
3596
4230
|
this.traverseAncestors((parent) => {
|
|
3597
4231
|
const scene = parent;
|
|
@@ -3627,15 +4261,17 @@ var init_SceneObject = __esm({
|
|
|
3627
4261
|
return obj;
|
|
3628
4262
|
}
|
|
3629
4263
|
/**
|
|
3630
|
-
*
|
|
4264
|
+
* Finds a named part, such as `head`, without the internal `group:` prefix.
|
|
4265
|
+
* Returns `undefined` when the group does not exist.
|
|
3631
4266
|
*/
|
|
3632
4267
|
getGroupByName(name) {
|
|
3633
4268
|
return this.getObjectByName(`group:${name}`);
|
|
3634
4269
|
}
|
|
3635
4270
|
/**
|
|
3636
|
-
*
|
|
3637
|
-
* @param name name
|
|
3638
|
-
* @param visible
|
|
4271
|
+
* Changes a named group's visibility and requests a redraw.
|
|
4272
|
+
* @param name - Part name without the `group:` prefix.
|
|
4273
|
+
* @param visible - Visibility to assign. Omit it to toggle the current value.
|
|
4274
|
+
* @returns The resulting visibility, or `false` if the group does not exist.
|
|
3639
4275
|
*/
|
|
3640
4276
|
toggleGroupVisibility(name, visible) {
|
|
3641
4277
|
return this.toggleObjectVisibility(this.getGroupByName(name), visible);
|
|
@@ -3686,15 +4322,16 @@ var init_SceneObject = __esm({
|
|
|
3686
4322
|
return mesh;
|
|
3687
4323
|
}
|
|
3688
4324
|
/**
|
|
3689
|
-
*
|
|
4325
|
+
* Finds a named mesh without the internal `mesh:` prefix, or returns `undefined`.
|
|
3690
4326
|
*/
|
|
3691
4327
|
getMeshByName(name) {
|
|
3692
4328
|
return this.getObjectByName(`mesh:${name}`);
|
|
3693
4329
|
}
|
|
3694
4330
|
/**
|
|
3695
|
-
*
|
|
3696
|
-
* @param name name
|
|
3697
|
-
* @param visible
|
|
4331
|
+
* Changes a named mesh's visibility and requests a redraw.
|
|
4332
|
+
* @param name - Mesh name without the `mesh:` prefix.
|
|
4333
|
+
* @param visible - Visibility to assign. Omit it to toggle the current value.
|
|
4334
|
+
* @returns The resulting visibility, or `false` if the mesh does not exist.
|
|
3698
4335
|
*/
|
|
3699
4336
|
toggleMeshVisibility(name, visible) {
|
|
3700
4337
|
return this.toggleObjectVisibility(this.getMeshByName(name), visible);
|
|
@@ -3740,12 +4377,14 @@ var init_SceneObject = __esm({
|
|
|
3740
4377
|
get isInstanced() {
|
|
3741
4378
|
return this._isInstanced;
|
|
3742
4379
|
}
|
|
4380
|
+
/** Number of live placements, excluding released instance slots. */
|
|
3743
4381
|
get instanceCounter() {
|
|
3744
4382
|
return this._instanceCounter;
|
|
3745
4383
|
}
|
|
3746
4384
|
constructInstanceReference(i) {
|
|
3747
4385
|
return new InstanceReference(this, i);
|
|
3748
4386
|
}
|
|
4387
|
+
/** Allocates a placement of this instanced object, reusing a released slot when available. */
|
|
3749
4388
|
nextInstance() {
|
|
3750
4389
|
const mesh = this.instanceMesh;
|
|
3751
4390
|
if (!mesh) throw new MineRenderError("Object is not instanced");
|
|
@@ -3790,10 +4429,10 @@ var init_SceneObject = __esm({
|
|
|
3790
4429
|
return new InstancedBufferAttribute(array, attribute.itemSize, attribute.normalized, attribute.meshPerAttribute).setUsage(attribute.usage);
|
|
3791
4430
|
};
|
|
3792
4431
|
const matrix = grow(mesh.instanceMatrix, 0);
|
|
3793
|
-
const
|
|
4432
|
+
const color2 = mesh.instanceColor ? grow(mesh.instanceColor, 1) : null;
|
|
3794
4433
|
mesh.dispose();
|
|
3795
4434
|
mesh.instanceMatrix = matrix;
|
|
3796
|
-
mesh.instanceColor =
|
|
4435
|
+
mesh.instanceColor = color2;
|
|
3797
4436
|
}
|
|
3798
4437
|
*activeInstanceIndices() {
|
|
3799
4438
|
if (this.instanceMesh) {
|
|
@@ -3892,6 +4531,7 @@ var init_SceneObject = __esm({
|
|
|
3892
4531
|
vector.setFromMatrixScale(matrix);
|
|
3893
4532
|
return vector;
|
|
3894
4533
|
}
|
|
4534
|
+
/** Updates supplied transform components. For a shared model, affects every live instance. */
|
|
3895
4535
|
setPositionRotationScale(position, rotation, scale) {
|
|
3896
4536
|
if (this.isInstanced) {
|
|
3897
4537
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -3910,6 +4550,7 @@ var init_SceneObject = __esm({
|
|
|
3910
4550
|
}
|
|
3911
4551
|
this.notifyDirty();
|
|
3912
4552
|
}
|
|
4553
|
+
/** Sets position in scene units. For a shared model, updates every live instance. */
|
|
3913
4554
|
setPosition(position) {
|
|
3914
4555
|
if (this.isInstanced) {
|
|
3915
4556
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -3927,6 +4568,7 @@ var init_SceneObject = __esm({
|
|
|
3927
4568
|
return this.position;
|
|
3928
4569
|
}
|
|
3929
4570
|
}
|
|
4571
|
+
/** Sets rotation in radians. For a shared model, updates every live instance. */
|
|
3930
4572
|
setRotation(rotation) {
|
|
3931
4573
|
if (this.isInstanced) {
|
|
3932
4574
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -3944,6 +4586,7 @@ var init_SceneObject = __esm({
|
|
|
3944
4586
|
return this.rotation;
|
|
3945
4587
|
}
|
|
3946
4588
|
}
|
|
4589
|
+
/** Sets scale factors. For a shared model, updates every live instance. */
|
|
3947
4590
|
setScale(scale) {
|
|
3948
4591
|
if (this.isInstanced) {
|
|
3949
4592
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -3962,15 +4605,15 @@ var init_SceneObject = __esm({
|
|
|
3962
4605
|
}
|
|
3963
4606
|
}
|
|
3964
4607
|
//</editor-fold>
|
|
3965
|
-
toggleObjectVisibility(
|
|
3966
|
-
if (
|
|
4608
|
+
toggleObjectVisibility(object3, visible) {
|
|
4609
|
+
if (object3) {
|
|
3967
4610
|
if (typeof visible !== "undefined") {
|
|
3968
|
-
|
|
4611
|
+
object3.visible = visible;
|
|
3969
4612
|
} else {
|
|
3970
|
-
|
|
4613
|
+
object3.visible = !object3.visible;
|
|
3971
4614
|
}
|
|
3972
4615
|
this.notifyDirty();
|
|
3973
|
-
return
|
|
4616
|
+
return object3.visible;
|
|
3974
4617
|
}
|
|
3975
4618
|
this.notifyDirty();
|
|
3976
4619
|
return false;
|
|
@@ -3981,6 +4624,7 @@ var init_SceneObject = __esm({
|
|
|
3981
4624
|
this.notifyDirty();
|
|
3982
4625
|
super.dispose();
|
|
3983
4626
|
}
|
|
4627
|
+
/** Releases instance slots and removes children, disposing children that implement Disposable. */
|
|
3984
4628
|
disposeAndRemoveAllChildren() {
|
|
3985
4629
|
if (this.instanceMesh) {
|
|
3986
4630
|
if (this._scene) this._scene.stats.instanceCount -= this.instanceReferences.size;
|
|
@@ -4003,6 +4647,7 @@ var init_SceneObject = __esm({
|
|
|
4003
4647
|
}
|
|
4004
4648
|
this.notifyDirty();
|
|
4005
4649
|
}
|
|
4650
|
+
/** Detaches the object from its parent and requests a redraw. */
|
|
4006
4651
|
removeFromScene() {
|
|
4007
4652
|
this.notifyDirty();
|
|
4008
4653
|
this.removeFromParent();
|
|
@@ -4158,12 +4803,15 @@ var init_EntityObject = __esm({
|
|
|
4158
4803
|
if (this.animationPlayers.length) this.notifyDirty();
|
|
4159
4804
|
}
|
|
4160
4805
|
//</editor-fold>
|
|
4806
|
+
/** Finds a selected draw by its layer key, such as `main` or `main#2`, or returns `undefined`. */
|
|
4161
4807
|
getLayerGroup(name) {
|
|
4162
4808
|
return super.getGroupByName(`layer:${name}`);
|
|
4163
4809
|
}
|
|
4810
|
+
/** Finds a part name without `group:`. Supply `layerName` to restrict the search to one draw. */
|
|
4164
4811
|
getGroupByName(name, layerName) {
|
|
4165
4812
|
return layerName === void 0 ? super.getGroupByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`group:${name}`);
|
|
4166
4813
|
}
|
|
4814
|
+
/** Finds a mesh name without `mesh:`. Supply `layerName` to restrict the search to one draw. */
|
|
4167
4815
|
getMeshByName(name, layerName) {
|
|
4168
4816
|
return layerName === void 0 ? super.getMeshByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`mesh:${name}`);
|
|
4169
4817
|
}
|
|
@@ -4265,8 +4913,8 @@ var init_EntityObject = __esm({
|
|
|
4265
4913
|
this.scrollMaterials.push({ material: mat, offset: mat.map.offset, speed: scroll });
|
|
4266
4914
|
}
|
|
4267
4915
|
}
|
|
4268
|
-
this.getLayerGroup(name)?.traverse((
|
|
4269
|
-
if (isMesh(
|
|
4916
|
+
this.getLayerGroup(name)?.traverse((object3) => {
|
|
4917
|
+
if (isMesh(object3)) object3.material = mat;
|
|
4270
4918
|
});
|
|
4271
4919
|
}));
|
|
4272
4920
|
if (!this.children.length) {
|
|
@@ -4607,8 +5255,8 @@ function toTrianglesDrawMode(geometry, drawMode) {
|
|
|
4607
5255
|
return geometry;
|
|
4608
5256
|
}
|
|
4609
5257
|
}
|
|
4610
|
-
function computeMorphedAttributes(
|
|
4611
|
-
if (
|
|
5258
|
+
function computeMorphedAttributes(object3) {
|
|
5259
|
+
if (object3.geometry.isBufferGeometry !== true) {
|
|
4612
5260
|
console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
|
|
4613
5261
|
return null;
|
|
4614
5262
|
}
|
|
@@ -4621,11 +5269,11 @@ function computeMorphedAttributes(object) {
|
|
|
4621
5269
|
const _morphA = new Vector310();
|
|
4622
5270
|
const _morphB = new Vector310();
|
|
4623
5271
|
const _morphC = new Vector310();
|
|
4624
|
-
function _calculateMorphedAttributeData(
|
|
5272
|
+
function _calculateMorphedAttributeData(object4, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
|
|
4625
5273
|
_vA.fromBufferAttribute(attribute, a2);
|
|
4626
5274
|
_vB.fromBufferAttribute(attribute, b2);
|
|
4627
5275
|
_vC.fromBufferAttribute(attribute, c2);
|
|
4628
|
-
const morphInfluences =
|
|
5276
|
+
const morphInfluences = object4.morphTargetInfluences;
|
|
4629
5277
|
if (material2.morphTargets && morphAttribute && morphInfluences) {
|
|
4630
5278
|
_morphA.set(0, 0, 0);
|
|
4631
5279
|
_morphB.set(0, 0, 0);
|
|
@@ -4651,10 +5299,10 @@ function computeMorphedAttributes(object) {
|
|
|
4651
5299
|
_vB.add(_morphB);
|
|
4652
5300
|
_vC.add(_morphC);
|
|
4653
5301
|
}
|
|
4654
|
-
if (
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
5302
|
+
if (object4.isSkinnedMesh) {
|
|
5303
|
+
object4.boneTransform(a2, _vA);
|
|
5304
|
+
object4.boneTransform(b2, _vB);
|
|
5305
|
+
object4.boneTransform(c2, _vC);
|
|
4658
5306
|
}
|
|
4659
5307
|
modifiedAttributeArray[a2 * 3 + 0] = _vA.x;
|
|
4660
5308
|
modifiedAttributeArray[a2 * 3 + 1] = _vA.y;
|
|
@@ -4666,8 +5314,8 @@ function computeMorphedAttributes(object) {
|
|
|
4666
5314
|
modifiedAttributeArray[c2 * 3 + 1] = _vC.y;
|
|
4667
5315
|
modifiedAttributeArray[c2 * 3 + 2] = _vC.z;
|
|
4668
5316
|
}
|
|
4669
|
-
const geometry =
|
|
4670
|
-
const material =
|
|
5317
|
+
const geometry = object3.geometry;
|
|
5318
|
+
const material = object3.material;
|
|
4671
5319
|
let a, b, c;
|
|
4672
5320
|
const index = geometry.index;
|
|
4673
5321
|
const positionAttribute = geometry.attributes.position;
|
|
@@ -4694,7 +5342,7 @@ function computeMorphedAttributes(object) {
|
|
|
4694
5342
|
b = index.getX(j + 1);
|
|
4695
5343
|
c = index.getX(j + 2);
|
|
4696
5344
|
_calculateMorphedAttributeData(
|
|
4697
|
-
|
|
5345
|
+
object3,
|
|
4698
5346
|
groupMaterial,
|
|
4699
5347
|
positionAttribute,
|
|
4700
5348
|
morphPosition,
|
|
@@ -4705,7 +5353,7 @@ function computeMorphedAttributes(object) {
|
|
|
4705
5353
|
modifiedPosition
|
|
4706
5354
|
);
|
|
4707
5355
|
_calculateMorphedAttributeData(
|
|
4708
|
-
|
|
5356
|
+
object3,
|
|
4709
5357
|
groupMaterial,
|
|
4710
5358
|
normalAttribute,
|
|
4711
5359
|
morphNormal,
|
|
@@ -4725,7 +5373,7 @@ function computeMorphedAttributes(object) {
|
|
|
4725
5373
|
b = index.getX(i + 1);
|
|
4726
5374
|
c = index.getX(i + 2);
|
|
4727
5375
|
_calculateMorphedAttributeData(
|
|
4728
|
-
|
|
5376
|
+
object3,
|
|
4729
5377
|
material,
|
|
4730
5378
|
positionAttribute,
|
|
4731
5379
|
morphPosition,
|
|
@@ -4736,7 +5384,7 @@ function computeMorphedAttributes(object) {
|
|
|
4736
5384
|
modifiedPosition
|
|
4737
5385
|
);
|
|
4738
5386
|
_calculateMorphedAttributeData(
|
|
4739
|
-
|
|
5387
|
+
object3,
|
|
4740
5388
|
material,
|
|
4741
5389
|
normalAttribute,
|
|
4742
5390
|
morphNormal,
|
|
@@ -4760,7 +5408,7 @@ function computeMorphedAttributes(object) {
|
|
|
4760
5408
|
b = j + 1;
|
|
4761
5409
|
c = j + 2;
|
|
4762
5410
|
_calculateMorphedAttributeData(
|
|
4763
|
-
|
|
5411
|
+
object3,
|
|
4764
5412
|
groupMaterial,
|
|
4765
5413
|
positionAttribute,
|
|
4766
5414
|
morphPosition,
|
|
@@ -4771,7 +5419,7 @@ function computeMorphedAttributes(object) {
|
|
|
4771
5419
|
modifiedPosition
|
|
4772
5420
|
);
|
|
4773
5421
|
_calculateMorphedAttributeData(
|
|
4774
|
-
|
|
5422
|
+
object3,
|
|
4775
5423
|
groupMaterial,
|
|
4776
5424
|
normalAttribute,
|
|
4777
5425
|
morphNormal,
|
|
@@ -4791,7 +5439,7 @@ function computeMorphedAttributes(object) {
|
|
|
4791
5439
|
b = i + 1;
|
|
4792
5440
|
c = i + 2;
|
|
4793
5441
|
_calculateMorphedAttributeData(
|
|
4794
|
-
|
|
5442
|
+
object3,
|
|
4795
5443
|
material,
|
|
4796
5444
|
positionAttribute,
|
|
4797
5445
|
morphPosition,
|
|
@@ -4802,7 +5450,7 @@ function computeMorphedAttributes(object) {
|
|
|
4802
5450
|
modifiedPosition
|
|
4803
5451
|
);
|
|
4804
5452
|
_calculateMorphedAttributeData(
|
|
4805
|
-
|
|
5453
|
+
object3,
|
|
4806
5454
|
material,
|
|
4807
5455
|
normalAttribute,
|
|
4808
5456
|
morphNormal,
|
|
@@ -4836,6 +5484,7 @@ var init_ModelCulling = __esm({
|
|
|
4836
5484
|
"src/model/ModelCulling.ts"() {
|
|
4837
5485
|
init_CubeFace();
|
|
4838
5486
|
ModelCulling = class {
|
|
5487
|
+
/** Rotates a world-direction bit mask into model space. Bits follow {@link CUBE_FACES}. */
|
|
4839
5488
|
static toLocalMask(worldMask, rotation) {
|
|
4840
5489
|
let localMask = 0;
|
|
4841
5490
|
for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
|
|
@@ -4846,6 +5495,7 @@ var init_ModelCulling = __esm({
|
|
|
4846
5495
|
}
|
|
4847
5496
|
return localMask;
|
|
4848
5497
|
}
|
|
5498
|
+
/** Whether a model fills one block with opaque faces and can hide adjacent block faces. */
|
|
4849
5499
|
static isOpaqueFullCube(atlas) {
|
|
4850
5500
|
if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
|
|
4851
5501
|
const element = atlas.model.elements[0];
|
|
@@ -4872,15 +5522,17 @@ var init_ModelCulling = __esm({
|
|
|
4872
5522
|
});
|
|
4873
5523
|
|
|
4874
5524
|
// src/model/SpecialItems.ts
|
|
4875
|
-
import { Matrix4 as Matrix48 } from "three";
|
|
5525
|
+
import { Euler as Euler7, Matrix4 as Matrix48 } from "three";
|
|
4876
5526
|
var SpecialItems;
|
|
4877
5527
|
var init_SpecialItems = __esm({
|
|
4878
5528
|
"src/model/SpecialItems.ts"() {
|
|
4879
5529
|
init_AssetKey();
|
|
4880
5530
|
init_Entities();
|
|
5531
|
+
init_BannerPatterns();
|
|
5532
|
+
init_ModelTextures();
|
|
4881
5533
|
SpecialItems = class {
|
|
4882
5534
|
/** Resolves the static entity parts drawn by a special item renderer, in model units. */
|
|
4883
|
-
static async getParts(special, root) {
|
|
5535
|
+
static async getParts(special, root, components = {}) {
|
|
4884
5536
|
const texture = (id, directory) => {
|
|
4885
5537
|
const key = AssetKey.parse("textures", id);
|
|
4886
5538
|
return new AssetKey(
|
|
@@ -4893,13 +5545,77 @@ var init_SpecialItems = __esm({
|
|
|
4893
5545
|
root
|
|
4894
5546
|
);
|
|
4895
5547
|
};
|
|
4896
|
-
const load = async (id, textureKey, transform, rotations = {}) => {
|
|
5548
|
+
const load = async (id, textureKey, transform, rotations = {}, layers = ["main"]) => {
|
|
4897
5549
|
const key = new AssetKey("minecraft", id, void 0, void 0, "entity-models", ".json", root);
|
|
4898
|
-
const model = await Entities.getEntity(key, textureKey, { layer:
|
|
5550
|
+
const model = await Entities.getEntity(key, textureKey, layers.length === 1 ? { layer: layers[0] } : {
|
|
5551
|
+
layers,
|
|
5552
|
+
textures: textureKey ? Object.fromEntries(layers.map((name) => [name, textureKey])) : void 0
|
|
5553
|
+
});
|
|
4899
5554
|
if (!model) throw new Error(`Missing entity model minecraft:${id} for special item ${special.type}`);
|
|
4900
5555
|
return { model, transform, rotations };
|
|
4901
5556
|
};
|
|
4902
5557
|
switch (special.type) {
|
|
5558
|
+
case "minecraft:trident":
|
|
5559
|
+
case "trident":
|
|
5560
|
+
case "minecraft:conduit":
|
|
5561
|
+
case "conduit": {
|
|
5562
|
+
const trident = special.type === "trident" || special.type === "minecraft:trident";
|
|
5563
|
+
const part = await load(
|
|
5564
|
+
trident ? "trident" : "conduit",
|
|
5565
|
+
texture(trident ? "trident" : "conduit/base", ""),
|
|
5566
|
+
trident ? new Matrix48().makeScale(1, -1, -1) : new Matrix48().makeTranslation(8, 8, 8),
|
|
5567
|
+
{},
|
|
5568
|
+
trident ? ["main"] : ["shell"]
|
|
5569
|
+
);
|
|
5570
|
+
part.model = { ...part.model, render: "solid", layers: part.model.layers && Object.fromEntries(
|
|
5571
|
+
Object.entries(part.model.layers).map(([name, layer]) => [name, { ...layer, render: "solid" }])
|
|
5572
|
+
) };
|
|
5573
|
+
return [part];
|
|
5574
|
+
}
|
|
5575
|
+
case "minecraft:banner":
|
|
5576
|
+
case "banner":
|
|
5577
|
+
case "minecraft:shield":
|
|
5578
|
+
case "shield": {
|
|
5579
|
+
const banner = special.type === "banner" || special.type === "minecraft:banner";
|
|
5580
|
+
const color2 = banner ? BannerPatterns.getColor(special.color) : components["minecraft:base_color"] === void 0 ? void 0 : BannerPatterns.getColor(components["minecraft:base_color"]);
|
|
5581
|
+
const patterns = await BannerPatterns.getLayers(components["minecraft:banner_patterns"], banner ? "banner" : "shield", root);
|
|
5582
|
+
const patterned = banner || color2 !== void 0 || patterns.length > 0;
|
|
5583
|
+
const baseTexture = texture(banner ? "banner_base" : patterned ? "shield_base" : "shield_base_nopattern", "");
|
|
5584
|
+
const transform = banner ? new Matrix48().makeTranslation(8, 0, 8).multiply(new Matrix48().makeScale(2 / 3, -2 / 3, -2 / 3)) : new Matrix48().makeScale(1, -1, -1);
|
|
5585
|
+
const part = await load(banner ? "standing_banner" : "shield", baseTexture, transform, {}, banner ? ["main", "flag"] : ["main"]);
|
|
5586
|
+
const layers = Object.fromEntries(Object.entries(part.model.layers ?? { main: part.model }).map(([name, layer]) => [name, { ...layer, render: "solid" }]));
|
|
5587
|
+
let overlay2;
|
|
5588
|
+
if (banner) {
|
|
5589
|
+
const flagLayer = layers.flag;
|
|
5590
|
+
const flag = flagLayer?.layer.root.children.flag;
|
|
5591
|
+
if (!flag) throw new Error("Banner entity model is missing its flag layer");
|
|
5592
|
+
overlay2 = { ...flagLayer.layer, root: { ...flagLayer.layer.root, children: {
|
|
5593
|
+
...flagLayer.layer.root.children,
|
|
5594
|
+
flag: { ...flag, pose: { ...flag.pose, rotation: [-25e-4 * Math.PI, flag.pose.rotation[1], flag.pose.rotation[2]] } }
|
|
5595
|
+
} } };
|
|
5596
|
+
layers.flag = { ...flagLayer, layer: overlay2 };
|
|
5597
|
+
} else {
|
|
5598
|
+
const main = layers.main.layer;
|
|
5599
|
+
const plate = main.root.children.plate;
|
|
5600
|
+
if (!plate) throw new Error("Shield entity model is missing its plate");
|
|
5601
|
+
overlay2 = { ...main, root: { ...main.root, cubes: [], children: { plate } } };
|
|
5602
|
+
}
|
|
5603
|
+
const tints = {};
|
|
5604
|
+
if (patterned) {
|
|
5605
|
+
const draws = [{ texture: texture(`${banner ? "banner" : "shield"}/base`, ""), color: color2 ?? DYE_COLORS.white }, ...patterns];
|
|
5606
|
+
draws.forEach((draw, index) => {
|
|
5607
|
+
const name = index === 0 ? "pattern_base" : `pattern_${index - 1}`;
|
|
5608
|
+
layers[name] = { key: draw.texture, texture: draw.texture, layer: overlay2, render: "no_outline", tint: name };
|
|
5609
|
+
tints[name] = draw.color;
|
|
5610
|
+
});
|
|
5611
|
+
}
|
|
5612
|
+
await Promise.all([...new Map(Object.values(layers).map((layer) => [layer.texture.serialize(), layer.texture])).values()].map(async (key) => {
|
|
5613
|
+
if (!await ModelTextures.preload(key)) throw new Error(`Missing special item texture ${key.toNamespacedString()}`);
|
|
5614
|
+
}));
|
|
5615
|
+
part.model = { ...part.model, ...layers.main, layers };
|
|
5616
|
+
part.tints = tints;
|
|
5617
|
+
return [part];
|
|
5618
|
+
}
|
|
4903
5619
|
case "minecraft:chest":
|
|
4904
5620
|
case "chest": {
|
|
4905
5621
|
const angle = -(special.openness ?? 0) * Math.PI / 2;
|
|
@@ -4910,6 +5626,26 @@ var init_SpecialItems = __esm({
|
|
|
4910
5626
|
chest.model = { ...chest.model, render: "solid", layers: void 0 };
|
|
4911
5627
|
return [chest];
|
|
4912
5628
|
}
|
|
5629
|
+
case "minecraft:shulker_box":
|
|
5630
|
+
case "shulker_box": {
|
|
5631
|
+
const openness = special.openness === void 0 ? 0 : special.openness;
|
|
5632
|
+
const orientation = special.orientation === void 0 ? "up" : special.orientation;
|
|
5633
|
+
const directions = {
|
|
5634
|
+
down: [Math.PI, 0, 0],
|
|
5635
|
+
up: [0, 0, 0],
|
|
5636
|
+
north: [Math.PI / 2, 0, Math.PI],
|
|
5637
|
+
south: [Math.PI / 2, 0, 0],
|
|
5638
|
+
west: [Math.PI / 2, 0, Math.PI / 2],
|
|
5639
|
+
east: [Math.PI / 2, 0, -Math.PI / 2]
|
|
5640
|
+
};
|
|
5641
|
+
if (!Object.prototype.hasOwnProperty.call(directions, orientation)) throw new Error(`Unsupported shulker-box orientation ${orientation}`);
|
|
5642
|
+
if (typeof openness !== "number" || !Number.isFinite(openness)) throw new Error("Shulker-box openness must be finite");
|
|
5643
|
+
if (typeof special.texture !== "string" || !special.texture) throw new Error("Shulker-box texture is required");
|
|
5644
|
+
const transform = new Matrix48().makeTranslation(8, 8, 8).multiply(new Matrix48().makeScale(0.9995, 0.9995, 0.9995)).multiply(new Matrix48().makeRotationFromEuler(new Euler7(...directions[orientation], "XYZ"))).multiply(new Matrix48().makeScale(1, -1, -1)).multiply(new Matrix48().makeTranslation(0, -16, 0));
|
|
5645
|
+
const box = await load("shulker_box", texture(special.texture, "shulker"), transform, { lid: [0, openness * Math.PI * 1.5, 0] });
|
|
5646
|
+
box.positions = { lid: [0, 24 - openness * 8, 0] };
|
|
5647
|
+
return [box];
|
|
5648
|
+
}
|
|
4913
5649
|
case "minecraft:bed":
|
|
4914
5650
|
case "bed": {
|
|
4915
5651
|
const transform = new Matrix48().makeTranslation(0, 9, 0).multiply(new Matrix48().makeRotationX(Math.PI / 2)).multiply(new Matrix48().makeTranslation(8, 8, 8)).multiply(new Matrix48().makeRotationZ(Math.PI)).multiply(new Matrix48().makeTranslation(-8, -8, -8));
|
|
@@ -5024,6 +5760,7 @@ var init_ItemTints = __esm({
|
|
|
5024
5760
|
static async get(model, tints) {
|
|
5025
5761
|
const sources = model.tints;
|
|
5026
5762
|
if (!sources?.length) return tints;
|
|
5763
|
+
const components = model.components ?? {};
|
|
5027
5764
|
const colors = { ...tints };
|
|
5028
5765
|
await Promise.all(sources.map(async (source, index) => {
|
|
5029
5766
|
if (colors[index] !== void 0) return;
|
|
@@ -5038,27 +5775,71 @@ var init_ItemTints = __esm({
|
|
|
5038
5775
|
break;
|
|
5039
5776
|
case "dye":
|
|
5040
5777
|
case "minecraft:dye":
|
|
5778
|
+
colors[index] = this.colorOrDefault(components["minecraft:dyed_color"], source.default, true);
|
|
5779
|
+
break;
|
|
5041
5780
|
case "potion":
|
|
5042
|
-
case "minecraft:potion":
|
|
5781
|
+
case "minecraft:potion": {
|
|
5782
|
+
const value = typeof components["minecraft:potion_contents"] === "string" ? void 0 : this.componentField(components, "potion_contents", "custom_color");
|
|
5783
|
+
colors[index] = this.colorOrDefault(value, source.default);
|
|
5784
|
+
break;
|
|
5785
|
+
}
|
|
5043
5786
|
case "map_color":
|
|
5044
5787
|
case "minecraft:map_color":
|
|
5788
|
+
colors[index] = this.colorOrDefault(components["minecraft:map_color"], source.default);
|
|
5789
|
+
break;
|
|
5045
5790
|
case "firework":
|
|
5046
|
-
case "minecraft:firework":
|
|
5791
|
+
case "minecraft:firework": {
|
|
5792
|
+
const values = this.componentField(components, "firework_explosion", "colors");
|
|
5793
|
+
if (values !== void 0 && !Array.isArray(values)) throw new MineRenderError("Item tint firework_explosion.colors must be an array");
|
|
5794
|
+
if (!values?.length) {
|
|
5795
|
+
colors[index] = this.rgb(source.default);
|
|
5796
|
+
break;
|
|
5797
|
+
}
|
|
5798
|
+
const rgb = values.map((value) => this.componentColor(value));
|
|
5799
|
+
const red = Math.floor(rgb.reduce((sum, color2) => sum + (color2 >> 16 & 255), 0) / rgb.length);
|
|
5800
|
+
const green = Math.floor(rgb.reduce((sum, color2) => sum + (color2 >> 8 & 255), 0) / rgb.length);
|
|
5801
|
+
const blue = Math.floor(rgb.reduce((sum, color2) => sum + (color2 & 255), 0) / rgb.length);
|
|
5802
|
+
colors[index] = red << 16 | green << 8 | blue;
|
|
5803
|
+
break;
|
|
5804
|
+
}
|
|
5047
5805
|
case "team":
|
|
5048
5806
|
case "minecraft:team":
|
|
5049
|
-
case "custom_model_data":
|
|
5050
|
-
case "minecraft:custom_model_data":
|
|
5051
5807
|
colors[index] = this.rgb(source.default);
|
|
5052
5808
|
break;
|
|
5809
|
+
case "custom_model_data":
|
|
5810
|
+
case "minecraft:custom_model_data": {
|
|
5811
|
+
const colorIndex = source.index ?? 0;
|
|
5812
|
+
if (!Number.isSafeInteger(colorIndex) || colorIndex < 0) throw new MineRenderError("Item tint custom_model_data index must be a nonnegative integer");
|
|
5813
|
+
const values = this.componentField(components, "custom_model_data", "colors");
|
|
5814
|
+
if (values !== void 0 && !Array.isArray(values)) throw new MineRenderError("Item tint custom_model_data.colors must be an array");
|
|
5815
|
+
colors[index] = this.colorOrDefault(values?.[colorIndex], source.default, true);
|
|
5816
|
+
break;
|
|
5817
|
+
}
|
|
5053
5818
|
default:
|
|
5054
5819
|
throw new MineRenderError(`Unsupported item tint source ${source.type}`);
|
|
5055
5820
|
}
|
|
5056
5821
|
}));
|
|
5057
5822
|
return colors;
|
|
5058
5823
|
}
|
|
5059
|
-
static
|
|
5060
|
-
|
|
5061
|
-
|
|
5824
|
+
static componentField(components, id, field) {
|
|
5825
|
+
const value = components[`minecraft:${id}`];
|
|
5826
|
+
if (value === void 0) return void 0;
|
|
5827
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new MineRenderError(`Item tint ${id} must be an object`);
|
|
5828
|
+
return value[field];
|
|
5829
|
+
}
|
|
5830
|
+
static colorOrDefault(value, fallback, allowTriple = false) {
|
|
5831
|
+
return value === void 0 ? this.rgb(fallback) : this.componentColor(value, allowTriple);
|
|
5832
|
+
}
|
|
5833
|
+
static componentColor(value, allowTriple = false) {
|
|
5834
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= -2147483648 && value <= 2147483647) return this.rgb(value);
|
|
5835
|
+
if (allowTriple && Array.isArray(value) && value.length === 3 && value.every((channel) => typeof channel === "number" && Number.isFinite(channel))) {
|
|
5836
|
+
return this.rgb(value) & 16777215;
|
|
5837
|
+
}
|
|
5838
|
+
throw new MineRenderError(`Item tint component color must be a packed integer${allowTriple ? " or RGB triple" : ""}`);
|
|
5839
|
+
}
|
|
5840
|
+
static rgb(color2) {
|
|
5841
|
+
if (!Array.isArray(color2)) return color2 & 16777215;
|
|
5842
|
+
const [red, green, blue] = color2.map((channel) => Math.floor(Math.fround(Math.fround(channel) * 255)) & 255);
|
|
5062
5843
|
return red << 16 | green << 8 | blue;
|
|
5063
5844
|
}
|
|
5064
5845
|
};
|
|
@@ -5066,7 +5847,7 @@ var init_ItemTints = __esm({
|
|
|
5066
5847
|
});
|
|
5067
5848
|
|
|
5068
5849
|
// src/model/scene/ModelObject.ts
|
|
5069
|
-
import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute5, Color as Color9, Euler as
|
|
5850
|
+
import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute5, Color as Color9, Euler as Euler8, Matrix4 as Matrix49 } from "three";
|
|
5070
5851
|
function isModelObject(obj) {
|
|
5071
5852
|
return obj.isModelObject;
|
|
5072
5853
|
}
|
|
@@ -5095,29 +5876,52 @@ var init_ModelObject = __esm({
|
|
|
5095
5876
|
this.originalModel = originalModel;
|
|
5096
5877
|
this.isModelObject = true;
|
|
5097
5878
|
this.specialMaterials = /* @__PURE__ */ new Map();
|
|
5879
|
+
this.geometries = /* @__PURE__ */ new Set();
|
|
5098
5880
|
this.meshesCreated = false;
|
|
5099
5881
|
this.options = (0, import_ts_deepmerge5.default)({}, _ModelObject.DEFAULT_OPTIONS, options ?? {});
|
|
5100
|
-
if (originalModel.special) this.options.instanceMeshes = false;
|
|
5882
|
+
if (originalModel.special || originalModel.parts) this.options.instanceMeshes = false;
|
|
5101
5883
|
if (this.options.tints) this.options.tints = { ...this.options.tints };
|
|
5102
5884
|
this.addEventListener("added", () => this.updateAnimationSubscription());
|
|
5103
5885
|
this.addEventListener("removed", () => this.updateAnimationSubscription());
|
|
5104
5886
|
}
|
|
5105
5887
|
async init() {
|
|
5888
|
+
const parts = this.originalModel.parts;
|
|
5889
|
+
if (parts) {
|
|
5890
|
+
try {
|
|
5891
|
+
for (const part of parts) {
|
|
5892
|
+
const object3 = new _ModelObject(part, { ...this.options, instanceMeshes: false });
|
|
5893
|
+
this.add(object3);
|
|
5894
|
+
await object3.init();
|
|
5895
|
+
}
|
|
5896
|
+
let order = 0;
|
|
5897
|
+
this.iterateAllMeshes((mesh) => {
|
|
5898
|
+
mesh.renderOrder = order++;
|
|
5899
|
+
for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
|
|
5900
|
+
material.transparent = true;
|
|
5901
|
+
}
|
|
5902
|
+
});
|
|
5903
|
+
} catch (error) {
|
|
5904
|
+
this.disposeAndRemoveAllChildren();
|
|
5905
|
+
throw error;
|
|
5906
|
+
}
|
|
5907
|
+
this.notifyDirty();
|
|
5908
|
+
return;
|
|
5909
|
+
}
|
|
5106
5910
|
this.options.tints = await ItemTints.get(this.originalModel, this.options.tints);
|
|
5107
5911
|
const special = this.originalModel.special;
|
|
5108
5912
|
if (special) {
|
|
5109
|
-
const
|
|
5913
|
+
const parts2 = await SpecialItems.getParts(special, this.originalModel.key?.root, this.originalModel.components);
|
|
5110
5914
|
const transform = this.options.displayPosition ? DisplayTransforms.getMatrix(this.originalModel.display, this.options.displayPosition) : new Matrix49();
|
|
5111
5915
|
transform.multiply(new Matrix49().makeTranslation(-8, -8, -8));
|
|
5112
5916
|
try {
|
|
5113
|
-
for (const part of
|
|
5114
|
-
const
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
this.add(
|
|
5119
|
-
await
|
|
5120
|
-
|
|
5917
|
+
for (const part of parts2) {
|
|
5918
|
+
const object3 = new EntityObject(part.model, { flip: false, wireframe: this.options.wireframe, tints: part.tints });
|
|
5919
|
+
object3.matrix.copy(transform).multiply(part.transform);
|
|
5920
|
+
object3.matrixWorldNeedsUpdate = true;
|
|
5921
|
+
object3.matrixAutoUpdate = false;
|
|
5922
|
+
this.add(object3);
|
|
5923
|
+
await object3.init();
|
|
5924
|
+
object3.iterateAllMeshes((mesh) => {
|
|
5121
5925
|
const source = mesh.material;
|
|
5122
5926
|
let material = this.specialMaterials.get(source);
|
|
5123
5927
|
if (!material) {
|
|
@@ -5128,7 +5932,10 @@ var init_ModelObject = __esm({
|
|
|
5128
5932
|
mesh.material = material;
|
|
5129
5933
|
});
|
|
5130
5934
|
for (const [name, rotation] of Object.entries(part.rotations)) {
|
|
5131
|
-
|
|
5935
|
+
object3.getGroupByName(name)?.rotation.set(...rotation, "ZYX");
|
|
5936
|
+
}
|
|
5937
|
+
for (const [name, position] of Object.entries(part.positions ?? {})) {
|
|
5938
|
+
object3.getGroupByName(name)?.position.set(...position);
|
|
5132
5939
|
}
|
|
5133
5940
|
}
|
|
5134
5941
|
} catch (error) {
|
|
@@ -5161,6 +5968,7 @@ var init_ModelObject = __esm({
|
|
|
5161
5968
|
if (this.atlas.model.elements) {
|
|
5162
5969
|
this.atlas.model.elements?.forEach((el) => {
|
|
5163
5970
|
const elGeo = this._getBoxGeometryFromElement(el).clone();
|
|
5971
|
+
this.geometries.add(elGeo);
|
|
5164
5972
|
if (this.options.cullMask) {
|
|
5165
5973
|
const indices = Array.from(elGeo.getIndex().array);
|
|
5166
5974
|
elGeo.setIndex(indices.filter((_, index) => {
|
|
@@ -5171,8 +5979,9 @@ var init_ModelObject = __esm({
|
|
|
5171
5979
|
elGeo.clearGroups();
|
|
5172
5980
|
}
|
|
5173
5981
|
if (this.options.uvLockRotation) {
|
|
5174
|
-
UVMapper.lockUvs(elGeo, el.faces, this.atlas, new
|
|
5982
|
+
UVMapper.lockUvs(elGeo, el.faces, this.atlas, new Euler8(...this.options.uvLockRotation));
|
|
5175
5983
|
}
|
|
5984
|
+
UVMapper.setAtlasUvBounds(elGeo, el.faces, this.atlas);
|
|
5176
5985
|
if (this.options.tints) {
|
|
5177
5986
|
const colors = new Float32Array(elGeo.getAttribute("position").count * 3).fill(1);
|
|
5178
5987
|
for (const [faceIndex, faceName] of CUBE_FACES.entries()) {
|
|
@@ -5180,9 +5989,9 @@ var init_ModelObject = __esm({
|
|
|
5180
5989
|
if (tintIndex === void 0 || tintIndex < 0) continue;
|
|
5181
5990
|
const tint = this.options.tints[tintIndex];
|
|
5182
5991
|
if (tint === void 0) continue;
|
|
5183
|
-
const
|
|
5992
|
+
const color2 = new Color9(tint);
|
|
5184
5993
|
for (let vertex = 0; vertex < 4; vertex++) {
|
|
5185
|
-
|
|
5994
|
+
color2.toArray(colors, (faceIndex * 4 + vertex) * 3);
|
|
5186
5995
|
}
|
|
5187
5996
|
}
|
|
5188
5997
|
elGeo.setAttribute("color", new BufferAttribute5(colors, 3));
|
|
@@ -5213,10 +6022,14 @@ var init_ModelObject = __esm({
|
|
|
5213
6022
|
let combinedGeo;
|
|
5214
6023
|
if (allGeos.length > 0) {
|
|
5215
6024
|
combinedGeo = mergeBufferGeometries(allGeos);
|
|
6025
|
+
for (const geometry of allGeos) {
|
|
6026
|
+
this.geometries.delete(geometry);
|
|
6027
|
+
}
|
|
5216
6028
|
} else {
|
|
5217
6029
|
combinedGeo = new BoxGeometry5(16, 16, 16);
|
|
5218
6030
|
if (displayTransform) DisplayTransforms.apply(combinedGeo, displayTransform);
|
|
5219
6031
|
}
|
|
6032
|
+
this.geometries.add(combinedGeo);
|
|
5220
6033
|
combinedGeo.computeBoundingBox();
|
|
5221
6034
|
let mesh;
|
|
5222
6035
|
if (this.options.instanceMeshes) {
|
|
@@ -5238,9 +6051,12 @@ var init_ModelObject = __esm({
|
|
|
5238
6051
|
}
|
|
5239
6052
|
applyTextures() {
|
|
5240
6053
|
if (this.atlas) {
|
|
5241
|
-
const mat = Materials.createShadedCanvasMaterial(this.atlas.image.canvas, this.atlas.hasTransparency, false);
|
|
6054
|
+
const mat = Materials.createShadedCanvasMaterial(this.atlas.image.canvas, this.atlas.hasTransparency, false, true);
|
|
5242
6055
|
this.atlasMaterial = mat;
|
|
5243
6056
|
this.atlasTexture = mat.uniforms?.map?.value ?? mat.map;
|
|
6057
|
+
if (this.options.displayPosition === "gui" /* GUI */ && this.originalModel.gui_light === "front" /* FRONT */ && mat.uniforms?.SHADE) {
|
|
6058
|
+
mat.uniforms.SHADE.value = false;
|
|
6059
|
+
}
|
|
5244
6060
|
this.iterateAllMeshes((mesh) => {
|
|
5245
6061
|
if (mesh.geometry.hasAttribute("color")) mat.vertexColors = true;
|
|
5246
6062
|
mesh.material = mat;
|
|
@@ -5249,7 +6065,9 @@ var init_ModelObject = __esm({
|
|
|
5249
6065
|
}
|
|
5250
6066
|
}
|
|
5251
6067
|
updateAnimationSubscription() {
|
|
5252
|
-
|
|
6068
|
+
let root = this;
|
|
6069
|
+
while (root.parent && isModelObject(root.parent)) root = root.parent;
|
|
6070
|
+
const active = !!root.parent && (!this.isInstanced || this.instanceCounter > 0);
|
|
5253
6071
|
if (active && this.atlas?.hasAnimation && this.atlasTexture) {
|
|
5254
6072
|
if (!this.unsubscribeAtlas) {
|
|
5255
6073
|
this.atlasTexture.needsUpdate = true;
|
|
@@ -5265,6 +6083,9 @@ var init_ModelObject = __esm({
|
|
|
5265
6083
|
this.unsubscribeAtlas?.();
|
|
5266
6084
|
this.unsubscribeAtlas = void 0;
|
|
5267
6085
|
}
|
|
6086
|
+
for (const child of this.children) {
|
|
6087
|
+
if (isModelObject(child)) child.updateAnimationSubscription();
|
|
6088
|
+
}
|
|
5268
6089
|
}
|
|
5269
6090
|
nextInstance() {
|
|
5270
6091
|
const reference = super.nextInstance();
|
|
@@ -5284,6 +6105,8 @@ var init_ModelObject = __esm({
|
|
|
5284
6105
|
this.atlasMaterial = void 0;
|
|
5285
6106
|
for (const material of this.specialMaterials.values()) material.dispose();
|
|
5286
6107
|
this.specialMaterials.clear();
|
|
6108
|
+
for (const geometry of this.geometries) geometry.dispose();
|
|
6109
|
+
this.geometries.clear();
|
|
5287
6110
|
super.disposeAndRemoveAllChildren();
|
|
5288
6111
|
}
|
|
5289
6112
|
};
|
|
@@ -5499,9 +6322,9 @@ var init_FluidModelObject = __esm({
|
|
|
5499
6322
|
}
|
|
5500
6323
|
}
|
|
5501
6324
|
geometry.clearGroups();
|
|
5502
|
-
const
|
|
6325
|
+
const color2 = new Color10(this.options.tints?.[0] ?? (this.kind === "water" ? 4159204 : 16777215));
|
|
5503
6326
|
const colors = new Float32Array(geometry.getAttribute("position").count * 3);
|
|
5504
|
-
for (let i = 0; i < colors.length; i += 3)
|
|
6327
|
+
for (let i = 0; i < colors.length; i += 3) color2.toArray(colors, i);
|
|
5505
6328
|
geometry.setAttribute("color", new BufferAttribute6(colors, 3));
|
|
5506
6329
|
geometry.computeBoundingBox();
|
|
5507
6330
|
geometry.computeBoundingSphere();
|
|
@@ -5631,11 +6454,11 @@ var BrowserEnv = class {
|
|
|
5631
6454
|
imageSize(data) {
|
|
5632
6455
|
return probeImageSize(data);
|
|
5633
6456
|
}
|
|
5634
|
-
openCache(name,
|
|
6457
|
+
openCache(name, version2) {
|
|
5635
6458
|
return new BrowserCache(localforage.createInstance({
|
|
5636
6459
|
// IndexedDB schema upgrades retain records; each cache generation needs its own database.
|
|
5637
|
-
name: name +
|
|
5638
|
-
version
|
|
6460
|
+
name: name + version2,
|
|
6461
|
+
version: version2
|
|
5639
6462
|
}));
|
|
5640
6463
|
}
|
|
5641
6464
|
};
|
|
@@ -5666,6 +6489,7 @@ var Meshes = class _Meshes {
|
|
|
5666
6489
|
static createBox(key) {
|
|
5667
6490
|
return new Mesh(Geometries.getBox(key.geometry), Materials.getImage(key.material));
|
|
5668
6491
|
}
|
|
6492
|
+
/** Returns a shared Mesh object. Use {@link createBox} for a mesh with its own transform and parent. */
|
|
5669
6493
|
static getBox(key) {
|
|
5670
6494
|
const keyStr = serializeMeshKey(key);
|
|
5671
6495
|
return Caching.boxMeshCache.get(keyStr, (k) => {
|
|
@@ -5715,6 +6539,7 @@ function shutdown() {
|
|
|
5715
6539
|
init_AssetKey();
|
|
5716
6540
|
init_AssetLoader();
|
|
5717
6541
|
init_Assets();
|
|
6542
|
+
init_BannerPatterns();
|
|
5718
6543
|
|
|
5719
6544
|
// src/assets/BlockEntities.ts
|
|
5720
6545
|
init_AssetKey();
|
|
@@ -5778,6 +6603,7 @@ init_AssetParsers();
|
|
|
5778
6603
|
init_ModelTextures();
|
|
5779
6604
|
init_Caching();
|
|
5780
6605
|
var Fonts = class {
|
|
6606
|
+
/** Combines font definitions in source-priority order. Unsupported Unihex and TrueType providers are skipped. */
|
|
5781
6607
|
static async get(key = "minecraft:default") {
|
|
5782
6608
|
const fontKey = typeof key === "string" ? AssetKey.parse("font", key) : key;
|
|
5783
6609
|
return await Caching.fontCache.get(fontKey.serialize(), () => this.load(fontKey, []));
|
|
@@ -5793,8 +6619,8 @@ var Fonts = class {
|
|
|
5793
6619
|
const stack = await AssetLoader.getAll(key, "json" /* JSON */);
|
|
5794
6620
|
if (stack.length === 0) throw new Error(`Could not load font ${key.toNamespacedString()}`);
|
|
5795
6621
|
const glyphs = /* @__PURE__ */ new Map();
|
|
5796
|
-
for (const
|
|
5797
|
-
for (const provider of
|
|
6622
|
+
for (const asset2 of stack) {
|
|
6623
|
+
for (const provider of asset2.providers) {
|
|
5798
6624
|
if (provider.filter?.uniform || provider.filter?.jp) continue;
|
|
5799
6625
|
let provided;
|
|
5800
6626
|
switch (provider.type) {
|
|
@@ -5940,13 +6766,13 @@ var OBJExporter = class {
|
|
|
5940
6766
|
* @param {Object3D} object - The 3D object to export.
|
|
5941
6767
|
* @return {string} The exported OBJ.
|
|
5942
6768
|
*/
|
|
5943
|
-
parse(
|
|
6769
|
+
parse(object3) {
|
|
5944
6770
|
let output = "";
|
|
5945
6771
|
let indexVertex = 0;
|
|
5946
6772
|
let indexVertexUvs = 0;
|
|
5947
6773
|
let indexNormals = 0;
|
|
5948
6774
|
const vertex = new Vector37();
|
|
5949
|
-
const
|
|
6775
|
+
const color2 = new Color4();
|
|
5950
6776
|
const normal = new Vector37();
|
|
5951
6777
|
const uv = new Vector22();
|
|
5952
6778
|
const face = [];
|
|
@@ -6045,9 +6871,9 @@ var OBJExporter = class {
|
|
|
6045
6871
|
vertex.applyMatrix4(points.matrixWorld);
|
|
6046
6872
|
output += "v " + vertex.x + " " + vertex.y + " " + vertex.z;
|
|
6047
6873
|
if (colors !== void 0) {
|
|
6048
|
-
|
|
6049
|
-
ColorManagement.workingToColorSpace(
|
|
6050
|
-
output += " " +
|
|
6874
|
+
color2.fromBufferAttribute(colors, i);
|
|
6875
|
+
ColorManagement.workingToColorSpace(color2, SRGBColorSpace3);
|
|
6876
|
+
output += " " + color2.r + " " + color2.g + " " + color2.b;
|
|
6051
6877
|
}
|
|
6052
6878
|
output += "\n";
|
|
6053
6879
|
}
|
|
@@ -6059,7 +6885,7 @@ var OBJExporter = class {
|
|
|
6059
6885
|
}
|
|
6060
6886
|
indexVertex += nbVertex;
|
|
6061
6887
|
}
|
|
6062
|
-
|
|
6888
|
+
object3.traverse(function(child) {
|
|
6063
6889
|
if (child.isMesh === true) {
|
|
6064
6890
|
parseMesh(child);
|
|
6065
6891
|
}
|
|
@@ -6093,9 +6919,9 @@ var PLYExporter = class {
|
|
|
6093
6919
|
* @param {PLYExporter~Options} options - The export options.
|
|
6094
6920
|
* @return {?(string|ArrayBuffer)} The exported PLY.
|
|
6095
6921
|
*/
|
|
6096
|
-
parse(
|
|
6922
|
+
parse(object3, onDone, options = {}) {
|
|
6097
6923
|
function traverseMeshes(cb) {
|
|
6098
|
-
|
|
6924
|
+
object3.traverse(function(child) {
|
|
6099
6925
|
if (child.isMesh === true || child.isPoints) {
|
|
6100
6926
|
const mesh = child;
|
|
6101
6927
|
const geometry = mesh.geometry;
|
|
@@ -6128,7 +6954,7 @@ var PLYExporter = class {
|
|
|
6128
6954
|
for (const name of customAttributeNames) customTypes[name] = "float";
|
|
6129
6955
|
let vertexCount = 0;
|
|
6130
6956
|
let faceCount = 0;
|
|
6131
|
-
|
|
6957
|
+
object3.traverse(function(child) {
|
|
6132
6958
|
if (child.isMesh === true) {
|
|
6133
6959
|
const mesh = child;
|
|
6134
6960
|
const geometry = mesh.geometry;
|
|
@@ -6750,8 +7576,8 @@ function equalArray(array1, array2) {
|
|
|
6750
7576
|
return element === array2[index];
|
|
6751
7577
|
});
|
|
6752
7578
|
}
|
|
6753
|
-
function stringToArrayBuffer(
|
|
6754
|
-
return new TextEncoder().encode(
|
|
7579
|
+
function stringToArrayBuffer(text2) {
|
|
7580
|
+
return new TextEncoder().encode(text2).buffer;
|
|
6755
7581
|
}
|
|
6756
7582
|
function isIdentityMatrix(matrix) {
|
|
6757
7583
|
return equalArray(matrix.elements, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
|
|
@@ -6944,12 +7770,12 @@ var GLTFWriter = class {
|
|
|
6944
7770
|
* @param {THREE.Object3D|THREE.Material|THREE.BufferGeometry|THREE.AnimationClip} object
|
|
6945
7771
|
* @param {Object} objectDef
|
|
6946
7772
|
*/
|
|
6947
|
-
serializeUserData(
|
|
6948
|
-
if (Object.keys(
|
|
7773
|
+
serializeUserData(object3, objectDef) {
|
|
7774
|
+
if (Object.keys(object3.userData).length === 0) return;
|
|
6949
7775
|
const options = this.options;
|
|
6950
7776
|
const extensionsUsed = this.extensionsUsed;
|
|
6951
7777
|
try {
|
|
6952
|
-
const json = JSON.parse(JSON.stringify(
|
|
7778
|
+
const json = JSON.parse(JSON.stringify(object3.userData));
|
|
6953
7779
|
if (options.includeCustomExtensions && json.gltfExtensions) {
|
|
6954
7780
|
if (objectDef.extensions === void 0) objectDef.extensions = {};
|
|
6955
7781
|
for (const extensionName in json.gltfExtensions) {
|
|
@@ -6960,7 +7786,7 @@ var GLTFWriter = class {
|
|
|
6960
7786
|
}
|
|
6961
7787
|
if (Object.keys(json).length > 0) objectDef.extras = json;
|
|
6962
7788
|
} catch (error) {
|
|
6963
|
-
console.warn("THREE.GLTFExporter: userData of '" +
|
|
7789
|
+
console.warn("THREE.GLTFExporter: userData of '" + object3.name + "' won't be serialized because of JSON.stringify error - " + error.message);
|
|
6964
7790
|
}
|
|
6965
7791
|
}
|
|
6966
7792
|
/**
|
|
@@ -7333,7 +8159,7 @@ var GLTFWriter = class {
|
|
|
7333
8159
|
* @param {string} mimeType export format
|
|
7334
8160
|
* @return {number} Index of the processed texture in the "images" array
|
|
7335
8161
|
*/
|
|
7336
|
-
processImage(image,
|
|
8162
|
+
processImage(image, format2, flipY, mimeType = "image/png") {
|
|
7337
8163
|
if (image !== null) {
|
|
7338
8164
|
const writer = this;
|
|
7339
8165
|
const cache = writer.cache;
|
|
@@ -7357,8 +8183,8 @@ var GLTFWriter = class {
|
|
|
7357
8183
|
ctx.scale(1, -1);
|
|
7358
8184
|
}
|
|
7359
8185
|
if (image.data !== void 0) {
|
|
7360
|
-
if (
|
|
7361
|
-
console.error("GLTFExporter: Only RGBAFormat is supported.",
|
|
8186
|
+
if (format2 !== RGBAFormat2) {
|
|
8187
|
+
console.error("GLTFExporter: Only RGBAFormat is supported.", format2);
|
|
7362
8188
|
}
|
|
7363
8189
|
if (image.width > options.maxTextureSize || image.height > options.maxTextureSize) {
|
|
7364
8190
|
console.warn("GLTFExporter: Image size is bigger than maxTextureSize", image);
|
|
@@ -7469,9 +8295,9 @@ var GLTFWriter = class {
|
|
|
7469
8295
|
if (material.isMeshStandardMaterial !== true && material.isMeshBasicMaterial !== true) {
|
|
7470
8296
|
console.warn("GLTFExporter: Use MeshStandardMaterial or MeshBasicMaterial for best results.");
|
|
7471
8297
|
}
|
|
7472
|
-
const
|
|
7473
|
-
if (!equalArray(
|
|
7474
|
-
materialDef.pbrMetallicRoughness.baseColorFactor =
|
|
8298
|
+
const color2 = material.color.toArray().concat([material.opacity]);
|
|
8299
|
+
if (!equalArray(color2, [1, 1, 1, 1])) {
|
|
8300
|
+
materialDef.pbrMetallicRoughness.baseColorFactor = color2;
|
|
7475
8301
|
}
|
|
7476
8302
|
if (material.isMeshStandardMaterial) {
|
|
7477
8303
|
materialDef.pbrMetallicRoughness.metallicFactor = material.metalness;
|
|
@@ -7886,13 +8712,13 @@ var GLTFWriter = class {
|
|
|
7886
8712
|
* @param {THREE.Object3D} object
|
|
7887
8713
|
* @return {?number}
|
|
7888
8714
|
*/
|
|
7889
|
-
processSkin(
|
|
8715
|
+
processSkin(object3) {
|
|
7890
8716
|
const json = this.json;
|
|
7891
8717
|
const nodeMap = this.nodeMap;
|
|
7892
|
-
const node = json.nodes[nodeMap.get(
|
|
7893
|
-
const skeleton =
|
|
8718
|
+
const node = json.nodes[nodeMap.get(object3)];
|
|
8719
|
+
const skeleton = object3.skeleton;
|
|
7894
8720
|
if (skeleton === void 0) return null;
|
|
7895
|
-
const rootJoint =
|
|
8721
|
+
const rootJoint = object3.skeleton.bones[0];
|
|
7896
8722
|
if (rootJoint === void 0) return null;
|
|
7897
8723
|
const joints = [];
|
|
7898
8724
|
const inverseBindMatrices = new Float32Array(skeleton.bones.length * 16);
|
|
@@ -7900,7 +8726,7 @@ var GLTFWriter = class {
|
|
|
7900
8726
|
for (let i = 0; i < skeleton.bones.length; ++i) {
|
|
7901
8727
|
joints.push(nodeMap.get(skeleton.bones[i]));
|
|
7902
8728
|
temporaryBoneInverse.copy(skeleton.boneInverses[i]);
|
|
7903
|
-
temporaryBoneInverse.multiply(
|
|
8729
|
+
temporaryBoneInverse.multiply(object3.bindMatrix).toArray(inverseBindMatrices, i * 16);
|
|
7904
8730
|
}
|
|
7905
8731
|
if (json.skins === void 0) json.skins = [];
|
|
7906
8732
|
json.skins.push({
|
|
@@ -7916,19 +8742,19 @@ var GLTFWriter = class {
|
|
|
7916
8742
|
* @param {THREE.Object3D} object Object3D to processNodeAsync
|
|
7917
8743
|
* @return {Promise<number>} Index of the node in the nodes list
|
|
7918
8744
|
*/
|
|
7919
|
-
async processNodeAsync(
|
|
8745
|
+
async processNodeAsync(object3) {
|
|
7920
8746
|
const json = this.json;
|
|
7921
8747
|
const options = this.options;
|
|
7922
8748
|
const nodeMap = this.nodeMap;
|
|
7923
8749
|
if (!json.nodes) json.nodes = [];
|
|
7924
|
-
if (
|
|
7925
|
-
return await this._processNodeWithPivotAsync(
|
|
8750
|
+
if (object3.pivot !== null) {
|
|
8751
|
+
return await this._processNodeWithPivotAsync(object3);
|
|
7926
8752
|
}
|
|
7927
8753
|
const nodeDef = {};
|
|
7928
8754
|
if (options.trs) {
|
|
7929
|
-
const rotation =
|
|
7930
|
-
const position =
|
|
7931
|
-
const scale =
|
|
8755
|
+
const rotation = object3.quaternion.toArray();
|
|
8756
|
+
const position = object3.position.toArray();
|
|
8757
|
+
const scale = object3.scale.toArray();
|
|
7932
8758
|
if (!equalArray(rotation, [0, 0, 0, 1])) {
|
|
7933
8759
|
nodeDef.rotation = rotation;
|
|
7934
8760
|
}
|
|
@@ -7939,28 +8765,28 @@ var GLTFWriter = class {
|
|
|
7939
8765
|
nodeDef.scale = scale;
|
|
7940
8766
|
}
|
|
7941
8767
|
} else {
|
|
7942
|
-
if (
|
|
7943
|
-
|
|
8768
|
+
if (object3.matrixAutoUpdate) {
|
|
8769
|
+
object3.updateMatrix();
|
|
7944
8770
|
}
|
|
7945
|
-
if (isIdentityMatrix(
|
|
7946
|
-
nodeDef.matrix =
|
|
8771
|
+
if (isIdentityMatrix(object3.matrix) === false) {
|
|
8772
|
+
nodeDef.matrix = object3.matrix.elements;
|
|
7947
8773
|
}
|
|
7948
8774
|
}
|
|
7949
|
-
if (
|
|
7950
|
-
this.serializeUserData(
|
|
7951
|
-
if (
|
|
7952
|
-
const meshIndex = await this.processMeshAsync(
|
|
8775
|
+
if (object3.name !== "") nodeDef.name = String(object3.name);
|
|
8776
|
+
this.serializeUserData(object3, nodeDef);
|
|
8777
|
+
if (object3.isMesh || object3.isLine || object3.isPoints) {
|
|
8778
|
+
const meshIndex = await this.processMeshAsync(object3);
|
|
7953
8779
|
if (meshIndex !== null) nodeDef.mesh = meshIndex;
|
|
7954
|
-
} else if (
|
|
7955
|
-
nodeDef.camera = this.processCamera(
|
|
8780
|
+
} else if (object3.isCamera) {
|
|
8781
|
+
nodeDef.camera = this.processCamera(object3);
|
|
7956
8782
|
}
|
|
7957
|
-
if (
|
|
8783
|
+
if (object3.isSkinnedMesh) this.skins.push(object3);
|
|
7958
8784
|
const nodeIndex = json.nodes.push(nodeDef) - 1;
|
|
7959
|
-
nodeMap.set(
|
|
7960
|
-
if (
|
|
8785
|
+
nodeMap.set(object3, nodeIndex);
|
|
8786
|
+
if (object3.children.length > 0) {
|
|
7961
8787
|
const children = [];
|
|
7962
|
-
for (let i = 0, l =
|
|
7963
|
-
const child =
|
|
8788
|
+
for (let i = 0, l = object3.children.length; i < l; i++) {
|
|
8789
|
+
const child = object3.children[i];
|
|
7964
8790
|
if (child.visible || options.onlyVisible === false) {
|
|
7965
8791
|
const childNodeIndex = await this.processNodeAsync(child);
|
|
7966
8792
|
if (childNodeIndex !== null) children.push(childNodeIndex);
|
|
@@ -7969,7 +8795,7 @@ var GLTFWriter = class {
|
|
|
7969
8795
|
if (children.length > 0) nodeDef.children = children;
|
|
7970
8796
|
}
|
|
7971
8797
|
await this._invokeAllAsync(function(ext) {
|
|
7972
|
-
ext.writeNode && ext.writeNode(
|
|
8798
|
+
ext.writeNode && ext.writeNode(object3, nodeDef);
|
|
7973
8799
|
});
|
|
7974
8800
|
return nodeIndex;
|
|
7975
8801
|
}
|
|
@@ -7978,19 +8804,19 @@ var GLTFWriter = class {
|
|
|
7978
8804
|
* @param {THREE.Object3D} object Object3D with pivot
|
|
7979
8805
|
* @return {Promise<number>} Index of the container node
|
|
7980
8806
|
*/
|
|
7981
|
-
async _processNodeWithPivotAsync(
|
|
8807
|
+
async _processNodeWithPivotAsync(object3) {
|
|
7982
8808
|
const json = this.json;
|
|
7983
8809
|
const options = this.options;
|
|
7984
8810
|
const nodeMap = this.nodeMap;
|
|
7985
|
-
const pivot =
|
|
8811
|
+
const pivot = object3.pivot;
|
|
7986
8812
|
const containerDef = {};
|
|
7987
|
-
const rotation =
|
|
8813
|
+
const rotation = object3.quaternion.toArray();
|
|
7988
8814
|
const position = [
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
|
|
8815
|
+
object3.position.x + pivot.x,
|
|
8816
|
+
object3.position.y + pivot.y,
|
|
8817
|
+
object3.position.z + pivot.z
|
|
7992
8818
|
];
|
|
7993
|
-
const scale =
|
|
8819
|
+
const scale = object3.scale.toArray();
|
|
7994
8820
|
if (!equalArray(rotation, [0, 0, 0, 1])) {
|
|
7995
8821
|
containerDef.rotation = rotation;
|
|
7996
8822
|
}
|
|
@@ -8001,28 +8827,28 @@ var GLTFWriter = class {
|
|
|
8001
8827
|
containerDef.scale = scale;
|
|
8002
8828
|
}
|
|
8003
8829
|
containerDef.extras = { pivot: pivot.toArray() };
|
|
8004
|
-
if (
|
|
8005
|
-
this.serializeUserData(
|
|
8830
|
+
if (object3.name !== "") containerDef.name = String(object3.name);
|
|
8831
|
+
this.serializeUserData(object3, containerDef);
|
|
8006
8832
|
const containerIndex = json.nodes.push(containerDef) - 1;
|
|
8007
|
-
nodeMap.set(
|
|
8833
|
+
nodeMap.set(object3, containerIndex);
|
|
8008
8834
|
const childDef = {};
|
|
8009
8835
|
const childPosition = [-pivot.x, -pivot.y, -pivot.z];
|
|
8010
8836
|
if (!equalArray(childPosition, [0, 0, 0])) {
|
|
8011
8837
|
childDef.translation = childPosition;
|
|
8012
8838
|
}
|
|
8013
|
-
if (
|
|
8014
|
-
const meshIndex = await this.processMeshAsync(
|
|
8839
|
+
if (object3.isMesh || object3.isLine || object3.isPoints) {
|
|
8840
|
+
const meshIndex = await this.processMeshAsync(object3);
|
|
8015
8841
|
if (meshIndex !== null) childDef.mesh = meshIndex;
|
|
8016
|
-
} else if (
|
|
8017
|
-
childDef.camera = this.processCamera(
|
|
8842
|
+
} else if (object3.isCamera) {
|
|
8843
|
+
childDef.camera = this.processCamera(object3);
|
|
8018
8844
|
}
|
|
8019
|
-
if (
|
|
8845
|
+
if (object3.isSkinnedMesh) this.skins.push(object3);
|
|
8020
8846
|
const childIndex = json.nodes.push(childDef) - 1;
|
|
8021
8847
|
const containerChildren = [childIndex];
|
|
8022
|
-
if (
|
|
8848
|
+
if (object3.children.length > 0) {
|
|
8023
8849
|
const grandchildren = [];
|
|
8024
|
-
for (let i = 0, l =
|
|
8025
|
-
const child =
|
|
8850
|
+
for (let i = 0, l = object3.children.length; i < l; i++) {
|
|
8851
|
+
const child = object3.children[i];
|
|
8026
8852
|
if (child.visible || options.onlyVisible === false) {
|
|
8027
8853
|
const childNodeIndex = await this.processNodeAsync(child);
|
|
8028
8854
|
if (childNodeIndex !== null) grandchildren.push(childNodeIndex);
|
|
@@ -8032,7 +8858,7 @@ var GLTFWriter = class {
|
|
|
8032
8858
|
}
|
|
8033
8859
|
containerDef.children = containerChildren;
|
|
8034
8860
|
await this._invokeAllAsync(function(ext) {
|
|
8035
|
-
ext.writeNode && ext.writeNode(
|
|
8861
|
+
ext.writeNode && ext.writeNode(object3, containerDef);
|
|
8036
8862
|
});
|
|
8037
8863
|
return containerIndex;
|
|
8038
8864
|
}
|
|
@@ -8475,10 +9301,10 @@ var GLTFMeshGpuInstancing = class {
|
|
|
8475
9301
|
this.writer = writer;
|
|
8476
9302
|
this.name = "EXT_mesh_gpu_instancing";
|
|
8477
9303
|
}
|
|
8478
|
-
writeNode(
|
|
8479
|
-
if (!
|
|
9304
|
+
writeNode(object3, nodeDef) {
|
|
9305
|
+
if (!object3.isInstancedMesh) return;
|
|
8480
9306
|
const writer = this.writer;
|
|
8481
|
-
const mesh =
|
|
9307
|
+
const mesh = object3;
|
|
8482
9308
|
const translationAttr = new Float32Array(mesh.count * 3);
|
|
8483
9309
|
const rotationAttr = new Float32Array(mesh.count * 4);
|
|
8484
9310
|
const scaleAttr = new Float32Array(mesh.count * 3);
|
|
@@ -8668,8 +9494,8 @@ var SceneExporter = class {
|
|
|
8668
9494
|
}
|
|
8669
9495
|
}
|
|
8670
9496
|
};
|
|
8671
|
-
function localMatrix(
|
|
8672
|
-
return
|
|
9497
|
+
function localMatrix(object3) {
|
|
9498
|
+
return object3.matrixAutoUpdate ? new Matrix47().compose(object3.position, object3.quaternion, object3.scale) : object3.matrix.clone();
|
|
8673
9499
|
}
|
|
8674
9500
|
function createSnapshot(root, gltf, bakeColors = false) {
|
|
8675
9501
|
const scene = new Scene3();
|
|
@@ -8773,24 +9599,24 @@ function createSnapshot(root, gltf, bakeColors = false) {
|
|
|
8773
9599
|
scene.add(copy);
|
|
8774
9600
|
}
|
|
8775
9601
|
};
|
|
8776
|
-
const visit = (
|
|
8777
|
-
if (!
|
|
8778
|
-
const world = parentWorld.clone().multiply(localMatrix(
|
|
8779
|
-
const mesh =
|
|
9602
|
+
const visit = (object3, parentWorld) => {
|
|
9603
|
+
if (!object3.visible) return;
|
|
9604
|
+
const world = parentWorld.clone().multiply(localMatrix(object3));
|
|
9605
|
+
const mesh = object3;
|
|
8780
9606
|
if (mesh.isMesh) {
|
|
8781
9607
|
const instanced = mesh;
|
|
8782
9608
|
if (instanced.isInstancedMesh) {
|
|
8783
9609
|
for (let i = 0; i < instanced.count; i++) {
|
|
8784
9610
|
const matrix = new Matrix47();
|
|
8785
9611
|
instanced.getMatrixAt(i, matrix);
|
|
8786
|
-
const
|
|
8787
|
-
addMesh(mesh, world.clone().multiply(matrix),
|
|
9612
|
+
const color2 = instanced.instanceColor ? new Color7().fromBufferAttribute(instanced.instanceColor, i) : void 0;
|
|
9613
|
+
addMesh(mesh, world.clone().multiply(matrix), color2);
|
|
8788
9614
|
}
|
|
8789
9615
|
} else {
|
|
8790
9616
|
addMesh(mesh, world);
|
|
8791
9617
|
}
|
|
8792
9618
|
}
|
|
8793
|
-
for (const child of
|
|
9619
|
+
for (const child of object3.children) visit(child, world);
|
|
8794
9620
|
};
|
|
8795
9621
|
try {
|
|
8796
9622
|
const ancestors = [];
|
|
@@ -8863,16 +9689,17 @@ function copyDataTexture(source) {
|
|
|
8863
9689
|
|
|
8864
9690
|
// src/gui/GuiHelper.ts
|
|
8865
9691
|
init_AssetKey();
|
|
9692
|
+
init_ModelTextures();
|
|
8866
9693
|
|
|
8867
9694
|
// src/gui/GuiText.ts
|
|
8868
9695
|
import { BufferGeometry as BufferGeometry2, Color as Color8, Float32BufferAttribute as Float32BufferAttribute4 } from "three";
|
|
8869
|
-
async function layoutGuiText(
|
|
9696
|
+
async function layoutGuiText(text2, options = {}) {
|
|
8870
9697
|
const maxWidth = options.maxWidth ?? Infinity, lineHeight = options.lineHeight ?? 9;
|
|
8871
9698
|
if (!(maxWidth > 0) || !Number.isFinite(lineHeight) || lineHeight <= 0) {
|
|
8872
9699
|
throw new Error("Text wrap width and line height must be positive");
|
|
8873
9700
|
}
|
|
8874
9701
|
const font = await Fonts.get(options.font);
|
|
8875
|
-
const runs = typeof
|
|
9702
|
+
const runs = typeof text2 === "string" ? [{ text: text2 }] : text2;
|
|
8876
9703
|
const glyphs = [], lineWidths = [];
|
|
8877
9704
|
let line = [], width = 0;
|
|
8878
9705
|
let trailingNewline = false;
|
|
@@ -8926,7 +9753,7 @@ function createGuiTextGeometry(layout, shadow = true) {
|
|
|
8926
9753
|
batch = { image: glyph?.image, positions: [], uvs: [], colors: [], indices: [] };
|
|
8927
9754
|
batches.push(batch);
|
|
8928
9755
|
}
|
|
8929
|
-
const
|
|
9756
|
+
const color2 = new Color8(isShadow ? (style.color & 16579836) >> 2 : style.color);
|
|
8930
9757
|
const offset = isShadow ? 1 : 0;
|
|
8931
9758
|
const top = glyph ? 7 - glyph.ascent : 0;
|
|
8932
9759
|
const height = glyph ? glyph.height * glyph.scale : 8;
|
|
@@ -8954,7 +9781,7 @@ function createGuiTextGeometry(layout, shadow = true) {
|
|
|
8954
9781
|
const v0 = glyph ? 1 - glyph.y / glyph.image.height : 1;
|
|
8955
9782
|
const v1 = glyph ? 1 - (glyph.y + glyph.height) / glyph.image.height : 0;
|
|
8956
9783
|
batch.uvs.push(u0, v0, u1, v0, u0, v1, u1, v1);
|
|
8957
|
-
for (let i = 0; i < 4; i++) batch.colors.push(
|
|
9784
|
+
for (let i = 0; i < 4; i++) batch.colors.push(color2.r, color2.g, color2.b);
|
|
8958
9785
|
batch.indices.push(base, base + 2, base + 1, base + 2, base + 3, base + 1);
|
|
8959
9786
|
};
|
|
8960
9787
|
for (const boldOffset of style.bold ? [0, 1] : [0]) {
|
|
@@ -8972,7 +9799,128 @@ function createGuiTextGeometry(layout, shadow = true) {
|
|
|
8972
9799
|
}
|
|
8973
9800
|
|
|
8974
9801
|
// src/gui/GuiHelper.ts
|
|
9802
|
+
var GUI_CONTAINER_LAYOUTS = {
|
|
9803
|
+
generic_54: {
|
|
9804
|
+
texture: "minecraft:gui/container/generic_54",
|
|
9805
|
+
crop: [0, 0, 176, 222],
|
|
9806
|
+
slotOrigin: [8, 18],
|
|
9807
|
+
slotOffset: [18, 18],
|
|
9808
|
+
rowSize: 9,
|
|
9809
|
+
slotCount: 54
|
|
9810
|
+
},
|
|
9811
|
+
crafting_table: {
|
|
9812
|
+
texture: "minecraft:gui/container/crafting_table",
|
|
9813
|
+
crop: [0, 0, 176, 166],
|
|
9814
|
+
slotOrigin: [30, 17],
|
|
9815
|
+
slotOffset: [18, 18],
|
|
9816
|
+
rowSize: 3,
|
|
9817
|
+
slotCount: 9,
|
|
9818
|
+
resultPosition: [124, 35]
|
|
9819
|
+
}
|
|
9820
|
+
};
|
|
8975
9821
|
var GuiHelper = class {
|
|
9822
|
+
/**
|
|
9823
|
+
* Creates a 182×5 boss bar using the active resource pack. Finite progress is clamped to 0–1.
|
|
9824
|
+
* The fill crops the source image; explicit crops do not apply `.mcmeta` sprite scaling.
|
|
9825
|
+
*/
|
|
9826
|
+
static async bossBar(options = {}) {
|
|
9827
|
+
const { color: color2 = "pink", progress = 1, position = [0, 0] } = options;
|
|
9828
|
+
this.validatePosition(position);
|
|
9829
|
+
if (!["pink", "blue", "red", "green", "yellow", "purple", "white"].includes(color2)) {
|
|
9830
|
+
throw new Error(`Unsupported boss bar color: ${color2}`);
|
|
9831
|
+
}
|
|
9832
|
+
if (!Number.isFinite(progress)) throw new Error("Boss bar progress must be finite");
|
|
9833
|
+
const value = Math.max(0, Math.min(1, progress));
|
|
9834
|
+
const width = value > 0 ? Math.floor(value * 181) + 1 : 0;
|
|
9835
|
+
const texture = `minecraft:gui/sprites/boss_bar/${color2}`;
|
|
9836
|
+
const layers = [{
|
|
9837
|
+
name: "boss-bar-background",
|
|
9838
|
+
texture: `${texture}_background`,
|
|
9839
|
+
position: [...position],
|
|
9840
|
+
size: [182, 5]
|
|
9841
|
+
}];
|
|
9842
|
+
if (width > 0) {
|
|
9843
|
+
layers.push({
|
|
9844
|
+
...await this.cropTexture(`${texture}_progress`, [0, 0, width, 5], [182, 5], position),
|
|
9845
|
+
name: "boss-bar-progress"
|
|
9846
|
+
});
|
|
9847
|
+
}
|
|
9848
|
+
return layers;
|
|
9849
|
+
}
|
|
9850
|
+
/**
|
|
9851
|
+
* Creates a 192×192 book page with optional arrows and supplied text. Buttons are hidden by default.
|
|
9852
|
+
* Callers choose the page content and button states; this helper does not paginate or handle clicks.
|
|
9853
|
+
*/
|
|
9854
|
+
static async book(options = {}) {
|
|
9855
|
+
const {
|
|
9856
|
+
position = [0, 0],
|
|
9857
|
+
previous = "hidden",
|
|
9858
|
+
next = "hidden",
|
|
9859
|
+
text: text2,
|
|
9860
|
+
color: color2 = 0,
|
|
9861
|
+
shadow = false,
|
|
9862
|
+
maxWidth = 114,
|
|
9863
|
+
lineHeight = 9,
|
|
9864
|
+
...style
|
|
9865
|
+
} = options;
|
|
9866
|
+
this.validatePosition(position);
|
|
9867
|
+
for (const state of [previous, next]) {
|
|
9868
|
+
if (!["hidden", "normal", "hover"].includes(state)) throw new Error(`Unsupported book button state: ${state}`);
|
|
9869
|
+
}
|
|
9870
|
+
const layers = [{
|
|
9871
|
+
...await this.cropTexture("minecraft:gui/book", [0, 0, 192, 192], [256, 256], position),
|
|
9872
|
+
name: "book-background"
|
|
9873
|
+
}];
|
|
9874
|
+
for (const [name, direction, state, x] of [
|
|
9875
|
+
["previous", "backward", previous, 43],
|
|
9876
|
+
["next", "forward", next, 116]
|
|
9877
|
+
]) {
|
|
9878
|
+
if (state === "hidden") continue;
|
|
9879
|
+
layers.push({
|
|
9880
|
+
name: `book-${name}`,
|
|
9881
|
+
texture: `minecraft:gui/sprites/widget/page_${direction}${state === "hover" ? "_highlighted" : ""}`,
|
|
9882
|
+
position: [position[0] + x, position[1] + 157],
|
|
9883
|
+
size: [23, 13]
|
|
9884
|
+
});
|
|
9885
|
+
}
|
|
9886
|
+
if (text2 !== void 0) {
|
|
9887
|
+
layers.push({
|
|
9888
|
+
name: "book-text",
|
|
9889
|
+
text: text2,
|
|
9890
|
+
position: [position[0] + 36, position[1] + 30],
|
|
9891
|
+
color: color2,
|
|
9892
|
+
shadow,
|
|
9893
|
+
maxWidth,
|
|
9894
|
+
lineHeight,
|
|
9895
|
+
...style
|
|
9896
|
+
});
|
|
9897
|
+
}
|
|
9898
|
+
return layers;
|
|
9899
|
+
}
|
|
9900
|
+
/** Creates a container background. Use {@link GUI_CONTAINER_LAYOUTS} with {@link inventorySlot} to place its items. */
|
|
9901
|
+
static async container(type, position = [0, 0]) {
|
|
9902
|
+
this.validatePosition(position);
|
|
9903
|
+
if (type !== "generic_54" && type !== "crafting_table") throw new Error(`Unsupported GUI container: ${type}`);
|
|
9904
|
+
const layout = GUI_CONTAINER_LAYOUTS[type];
|
|
9905
|
+
return [{ ...await this.cropTexture(layout.texture, layout.crop, [256, 256], position), name: "background" }];
|
|
9906
|
+
}
|
|
9907
|
+
static validatePosition(position) {
|
|
9908
|
+
if (!Array.isArray(position) || position.length !== 2 || !position.every(Number.isFinite)) {
|
|
9909
|
+
throw new Error("GUI position must contain two finite coordinates");
|
|
9910
|
+
}
|
|
9911
|
+
}
|
|
9912
|
+
static async cropTexture(texture, crop, referenceSize, position) {
|
|
9913
|
+
const image = await ModelTextures.preload(AssetKey.parse("textures", texture));
|
|
9914
|
+
if (!image) throw new Error(`Could not load GUI texture ${texture}`);
|
|
9915
|
+
const [x, y, width, height] = crop;
|
|
9916
|
+
const scaleX = image.width / referenceSize[0], scaleY = image.height / referenceSize[1];
|
|
9917
|
+
return {
|
|
9918
|
+
texture,
|
|
9919
|
+
crop: [x * scaleX, y * scaleY, width * scaleX, height * scaleY],
|
|
9920
|
+
position: [...position],
|
|
9921
|
+
size: [width, height]
|
|
9922
|
+
};
|
|
9923
|
+
}
|
|
8976
9924
|
/** Sizes a tooltip around supplied text; the first entry is its title. */
|
|
8977
9925
|
static async tooltip(lines, options = {}) {
|
|
8978
9926
|
if (!lines.length) return [];
|
|
@@ -8988,20 +9936,28 @@ var GuiHelper = class {
|
|
|
8988
9936
|
size: [width + 24, height + 24]
|
|
8989
9937
|
}));
|
|
8990
9938
|
let y = position[1];
|
|
8991
|
-
lines.forEach((
|
|
8992
|
-
layers.push({ ...textOptions, name: `tooltip-line-${index}`, text, position: [position[0], y] });
|
|
9939
|
+
lines.forEach((text2, index) => {
|
|
9940
|
+
layers.push({ ...textOptions, name: `tooltip-line-${index}`, text: text2, position: [position[0], y] });
|
|
8993
9941
|
y += layouts[index].height + (index === 0 ? titleGap : 0);
|
|
8994
9942
|
});
|
|
8995
9943
|
return layers;
|
|
8996
9944
|
}
|
|
9945
|
+
/**
|
|
9946
|
+
* Calculates a slot's top-left position in GUI pixels.
|
|
9947
|
+
* @param slot - Zero-based slot index, or `[column, row]`.
|
|
9948
|
+
* @param origin - Position of the first slot.
|
|
9949
|
+
* @param offset - Horizontal and vertical spacing between slot origins, in pixels.
|
|
9950
|
+
* @param rowSize - Slots per row when `slot` is an index.
|
|
9951
|
+
*/
|
|
8997
9952
|
static inventorySlot(slot, origin = [0, 0], offset = [18, 18], rowSize = 9) {
|
|
8998
9953
|
const [column, row] = typeof slot === "number" ? [slot % rowSize, Math.floor(slot / rowSize)] : slot;
|
|
8999
9954
|
return [origin[0] + column * offset[0], origin[1] + row * offset[1]];
|
|
9000
9955
|
}
|
|
9001
9956
|
/** Creates crafting-table layers for ingredients and the result; stack counts are not drawn. */
|
|
9002
9957
|
static recipe(recipe, options = {}) {
|
|
9958
|
+
const layout = GUI_CONTAINER_LAYOUTS.crafting_table;
|
|
9003
9959
|
const layers = [
|
|
9004
|
-
{ name: "background", texture:
|
|
9960
|
+
{ name: "background", texture: layout.texture, crop: [...layout.crop] }
|
|
9005
9961
|
];
|
|
9006
9962
|
const itemKey = (ingredient) => {
|
|
9007
9963
|
const item = typeof ingredient === "string" && !ingredient.startsWith("#") ? ingredient : typeof ingredient === "object" && "item" in ingredient ? ingredient.item : options.resolveIngredient?.(ingredient);
|
|
@@ -9016,17 +9972,17 @@ var GuiHelper = class {
|
|
|
9016
9972
|
layers.push({
|
|
9017
9973
|
name: `ingredient-${slot}`,
|
|
9018
9974
|
item: itemKey(ingredient),
|
|
9019
|
-
position: this.inventorySlot(slot,
|
|
9975
|
+
position: this.inventorySlot(slot, layout.slotOrigin, layout.slotOffset, layout.rowSize)
|
|
9020
9976
|
});
|
|
9021
9977
|
};
|
|
9022
9978
|
switch (recipe.type) {
|
|
9023
9979
|
case "minecraft:crafting_shaped":
|
|
9024
9980
|
case "crafting_shaped": {
|
|
9025
|
-
const
|
|
9026
|
-
if (!
|
|
9981
|
+
const pattern2 = recipe.pattern;
|
|
9982
|
+
if (!pattern2.length || pattern2.length > 3 || !pattern2[0].length || pattern2[0].length > 3 || pattern2.some((row) => row.length !== pattern2[0].length)) {
|
|
9027
9983
|
throw new Error("Crafting patterns must be rectangular, with 1\u20133 rows and columns");
|
|
9028
9984
|
}
|
|
9029
|
-
const cells =
|
|
9985
|
+
const cells = pattern2.flatMap((row, y) => row.split("").map((symbol, x) => ({ symbol, x, y }))).filter((cell) => cell.symbol !== " ");
|
|
9030
9986
|
if (!cells.length) throw new Error("Crafting patterns must contain an ingredient");
|
|
9031
9987
|
const minX = Math.min(...cells.map((cell) => cell.x)), maxX = Math.max(...cells.map((cell) => cell.x));
|
|
9032
9988
|
const minY = Math.min(...cells.map((cell) => cell.y)), maxY = Math.max(...cells.map((cell) => cell.y));
|
|
@@ -9051,7 +10007,7 @@ var GuiHelper = class {
|
|
|
9051
10007
|
layers.push({
|
|
9052
10008
|
name: "result",
|
|
9053
10009
|
item: itemKey("id" in recipe.result ? recipe.result.id : recipe.result.item),
|
|
9054
|
-
position: [
|
|
10010
|
+
position: [...layout.resultPosition]
|
|
9055
10011
|
});
|
|
9056
10012
|
return layers;
|
|
9057
10013
|
}
|
|
@@ -9145,7 +10101,6 @@ init_SceneObject();
|
|
|
9145
10101
|
init_Materials();
|
|
9146
10102
|
init_ModelObject();
|
|
9147
10103
|
init_DisplayPosition();
|
|
9148
|
-
init_GuiLight();
|
|
9149
10104
|
import { Box2, Box3 as Box32, DoubleSide as DoubleSide3, Group, Matrix4 as Matrix410, Mesh as Mesh6, MeshBasicMaterial as MeshBasicMaterial5, Vector2 as Vector23 } from "three";
|
|
9150
10105
|
var GuiObject = class extends SceneObject {
|
|
9151
10106
|
constructor(textureLayers, options) {
|
|
@@ -9214,12 +10169,16 @@ var GuiObject = class extends SceneObject {
|
|
|
9214
10169
|
mesh2.geometry.computeBoundingBox();
|
|
9215
10170
|
bounds.union(mesh2.geometry.boundingBox.clone().applyMatrix4(new Matrix410().multiplyMatrices(inverse, mesh2.matrixWorld)));
|
|
9216
10171
|
});
|
|
9217
|
-
item.position.z = depth
|
|
9218
|
-
|
|
10172
|
+
item.position.z = depth;
|
|
10173
|
+
if (!bounds.isEmpty()) {
|
|
10174
|
+
item.position.z -= bounds.min.z;
|
|
10175
|
+
depth += bounds.max.z - bounds.min.z;
|
|
10176
|
+
this.bounds.expandByPoint(new Vector23(bounds.min.x, -bounds.max.y));
|
|
10177
|
+
this.bounds.expandByPoint(new Vector23(bounds.max.x, -bounds.min.y));
|
|
10178
|
+
}
|
|
10179
|
+
depth += 0.01;
|
|
9219
10180
|
this.bounds.expandByPoint(new Vector23(x, y));
|
|
9220
10181
|
this.bounds.expandByPoint(new Vector23(x + width2, y + height2));
|
|
9221
|
-
this.bounds.expandByPoint(new Vector23(bounds.min.x, -bounds.max.y));
|
|
9222
|
-
this.bounds.expandByPoint(new Vector23(bounds.max.x, -bounds.min.y));
|
|
9223
10182
|
continue;
|
|
9224
10183
|
}
|
|
9225
10184
|
const key = typeof layer.texture === "string" ? AssetKey.parse("textures", layer.texture) : layer.texture;
|
|
@@ -9248,7 +10207,7 @@ var GuiObject = class extends SceneObject {
|
|
|
9248
10207
|
}
|
|
9249
10208
|
async createItem(layer, index) {
|
|
9250
10209
|
const key = typeof layer.item === "string" ? AssetKey.parse("models", layer.item) : layer.item;
|
|
9251
|
-
const model = await Models.getMerged(key);
|
|
10210
|
+
const model = await Models.getMerged(key, { ...layer.context, displayContext: "gui" /* GUI */ });
|
|
9252
10211
|
if (!model) throw new Error(`Could not load GUI item ${key.toNamespacedString()}`);
|
|
9253
10212
|
const item = new ModelObject(model, {
|
|
9254
10213
|
displayPosition: "gui" /* GUI */,
|
|
@@ -9259,15 +10218,13 @@ var GuiObject = class extends SceneObject {
|
|
|
9259
10218
|
item.name = `group:${layer.name ?? index}`;
|
|
9260
10219
|
this.add(item);
|
|
9261
10220
|
await item.init();
|
|
9262
|
-
|
|
9263
|
-
|
|
10221
|
+
const meshes = [];
|
|
10222
|
+
item.iterateAllMeshes((mesh) => meshes.push(mesh));
|
|
10223
|
+
meshes.forEach((mesh, partIndex) => {
|
|
9264
10224
|
mesh.name = `mesh:${layer.name ?? index}`;
|
|
9265
|
-
mesh.renderOrder = index;
|
|
10225
|
+
mesh.renderOrder = index + partIndex / (meshes.length + 1) * 0.5;
|
|
9266
10226
|
for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
|
|
9267
10227
|
material.transparent = true;
|
|
9268
|
-
if (model.gui_light === "front" /* FRONT */ && material.uniforms?.SHADE) {
|
|
9269
|
-
material.uniforms.SHADE.value = false;
|
|
9270
|
-
}
|
|
9271
10228
|
}
|
|
9272
10229
|
});
|
|
9273
10230
|
return item;
|
|
@@ -9438,29 +10395,29 @@ var SceneInspector = class {
|
|
|
9438
10395
|
this.objectControlsContainer.innerHTML = "";
|
|
9439
10396
|
this.addControls(targetObject, targetIntersection, instance);
|
|
9440
10397
|
}
|
|
9441
|
-
addControls(
|
|
10398
|
+
addControls(object3, intersection, instance) {
|
|
9442
10399
|
const container = document.createElement("div");
|
|
9443
10400
|
container.append(this.separator("Select Parent/Child"));
|
|
9444
|
-
if (
|
|
9445
|
-
container.append(this.buttonControl("Select Parent " +
|
|
9446
|
-
this.selectObject(
|
|
10401
|
+
if (object3.parent) {
|
|
10402
|
+
container.append(this.buttonControl("Select Parent " + object3.parent.constructor.name + " " + object3.parent.name, "P", () => {
|
|
10403
|
+
this.selectObject(object3.parent, intersection, instance);
|
|
9447
10404
|
}));
|
|
9448
10405
|
}
|
|
9449
|
-
if (
|
|
10406
|
+
if (object3.children.length > 0) {
|
|
9450
10407
|
let i = 1;
|
|
9451
|
-
for (let child of
|
|
10408
|
+
for (let child of object3.children) {
|
|
9452
10409
|
container.append(this.buttonControl("Select Child " + child.constructor.name + " " + child.name, "C" + i++, () => {
|
|
9453
10410
|
this.selectObject(child, intersection, instance);
|
|
9454
10411
|
}));
|
|
9455
10412
|
}
|
|
9456
10413
|
}
|
|
9457
10414
|
container.append(this.separator());
|
|
9458
|
-
const selectedInstance = instance && (
|
|
10415
|
+
const selectedInstance = instance && (object3 === instance.instanceable || object3 === intersection?.object) ? instance : void 0;
|
|
9459
10416
|
if (!selectedInstance) {
|
|
9460
|
-
container.append(this.toggleControl("Visibility", "V",
|
|
10417
|
+
container.append(this.toggleControl("Visibility", "V", object3.visible, (v) => object3.visible = v));
|
|
9461
10418
|
}
|
|
9462
10419
|
container.append(this.separator("Mesh"));
|
|
9463
|
-
this.addObjectControls(
|
|
10420
|
+
this.addObjectControls(object3, intersection, container, selectedInstance);
|
|
9464
10421
|
container.append(this.separator());
|
|
9465
10422
|
this.objectControlsContainer.append(container);
|
|
9466
10423
|
}
|
|
@@ -9651,6 +10608,7 @@ var SceneStatsDisplay = class {
|
|
|
9651
10608
|
appendTo(el) {
|
|
9652
10609
|
el.append(this.statsContainer);
|
|
9653
10610
|
}
|
|
10611
|
+
/** Stops the refresh timer and removes the panel from the page. */
|
|
9654
10612
|
dispose() {
|
|
9655
10613
|
clearInterval(this.interval);
|
|
9656
10614
|
this.interval = void 0;
|
|
@@ -9675,6 +10633,7 @@ var InstanceManager = class {
|
|
|
9675
10633
|
this.isInstanceManager = true;
|
|
9676
10634
|
this.instanceCache = {};
|
|
9677
10635
|
}
|
|
10636
|
+
/** Allocates a new placement of a cached model, or returns `undefined` if the key is unknown. */
|
|
9678
10637
|
async get(key) {
|
|
9679
10638
|
if (key in this.instanceCache) {
|
|
9680
10639
|
console.debug(p9, "key in cache", key);
|
|
@@ -9682,6 +10641,7 @@ var InstanceManager = class {
|
|
|
9682
10641
|
}
|
|
9683
10642
|
return void 0;
|
|
9684
10643
|
}
|
|
10644
|
+
/** Allocates a placement, calling the supplier once when the shared model is first needed. */
|
|
9685
10645
|
async getOrCreate(key, supplier) {
|
|
9686
10646
|
if (key in this.instanceCache) {
|
|
9687
10647
|
return await this.get(key);
|
|
@@ -9698,6 +10658,7 @@ var InstanceManager = class {
|
|
|
9698
10658
|
throw error;
|
|
9699
10659
|
}
|
|
9700
10660
|
}
|
|
10661
|
+
/** Forgets cached models without disposing them or releasing their existing placements. */
|
|
9701
10662
|
reset() {
|
|
9702
10663
|
for (let instanceCacheKey in this.instanceCache) {
|
|
9703
10664
|
delete this.instanceCache[instanceCacheKey];
|
|
@@ -9731,7 +10692,7 @@ init_SpecialItems();
|
|
|
9731
10692
|
init_BlockStates();
|
|
9732
10693
|
init_MineRenderError();
|
|
9733
10694
|
init_util();
|
|
9734
|
-
import { Euler as
|
|
10695
|
+
import { Euler as Euler9 } from "three";
|
|
9735
10696
|
function matchesCondition(condition, state) {
|
|
9736
10697
|
return Object.entries(condition).every(([key, value]) => {
|
|
9737
10698
|
if (Array.isArray(value)) {
|
|
@@ -9743,6 +10704,7 @@ function matchesCondition(condition, state) {
|
|
|
9743
10704
|
});
|
|
9744
10705
|
}
|
|
9745
10706
|
var BlockStateResolver = class {
|
|
10707
|
+
/** Loads vanilla property defaults, falling back to preview values inferred from the blockstate file. */
|
|
9746
10708
|
static async defaults(blockState) {
|
|
9747
10709
|
const defaults = blockState.key ? await BlockStates.getDefaultState(blockState.key) : void 0;
|
|
9748
10710
|
const state = {};
|
|
@@ -9762,9 +10724,11 @@ var BlockStateResolver = class {
|
|
|
9762
10724
|
}
|
|
9763
10725
|
return state;
|
|
9764
10726
|
}
|
|
10727
|
+
/** Selects matching models. Supply `choose` to control selection within each alternatives array. */
|
|
9765
10728
|
static select(blockState, state, choose = this.choose) {
|
|
9766
10729
|
return this.matching(blockState, state).map(choose);
|
|
9767
10730
|
}
|
|
10731
|
+
/** Returns matching model groups before choosing weighted alternatives. */
|
|
9768
10732
|
static matching(blockState, state) {
|
|
9769
10733
|
const out = [];
|
|
9770
10734
|
if (blockState.variants) {
|
|
@@ -9788,6 +10752,7 @@ var BlockStateResolver = class {
|
|
|
9788
10752
|
}
|
|
9789
10753
|
return out;
|
|
9790
10754
|
}
|
|
10755
|
+
/** Selects a random alternative using positive integer weights, each defaulting to 1. */
|
|
9791
10756
|
static choose(variants) {
|
|
9792
10757
|
if (!Array.isArray(variants)) return variants;
|
|
9793
10758
|
if (!variants.length) throw new MineRenderError("Blockstate variant arrays must not be empty");
|
|
@@ -9805,8 +10770,9 @@ var BlockStateResolver = class {
|
|
|
9805
10770
|
}
|
|
9806
10771
|
return variants[variants.length - 1];
|
|
9807
10772
|
}
|
|
10773
|
+
/** Converts blockstate rotations in degrees into the Euler rotation used by model rendering. */
|
|
9808
10774
|
static rotation(variant) {
|
|
9809
|
-
const rotation = new
|
|
10775
|
+
const rotation = new Euler9();
|
|
9810
10776
|
if (typeof variant.x !== "undefined") {
|
|
9811
10777
|
rotation.x = toRadians(clampRotationDegrees(variant.x));
|
|
9812
10778
|
}
|
|
@@ -9867,29 +10833,29 @@ var BlockTints = class {
|
|
|
9867
10833
|
}
|
|
9868
10834
|
}
|
|
9869
10835
|
if (!indices.size) return tints;
|
|
9870
|
-
let
|
|
10836
|
+
let color2;
|
|
9871
10837
|
switch (rule.source) {
|
|
9872
10838
|
case "constant":
|
|
9873
|
-
|
|
10839
|
+
color2 = rule.color;
|
|
9874
10840
|
break;
|
|
9875
10841
|
case "grass":
|
|
9876
|
-
|
|
10842
|
+
color2 = await Colormaps.grassColor(key);
|
|
9877
10843
|
break;
|
|
9878
10844
|
case "redstone": {
|
|
9879
10845
|
const power = Number(state.power ?? 0) / 15;
|
|
9880
10846
|
const red = power * 0.6 + (power > 0 ? 0.4 : 0.3);
|
|
9881
10847
|
const green = Math.max(0, power * power * 0.7 - 0.5);
|
|
9882
10848
|
const blue = Math.max(0, power * power * 0.6 - 0.7);
|
|
9883
|
-
|
|
10849
|
+
color2 = Math.floor(red * 255) << 16 | Math.floor(green * 255) << 8 | Math.floor(blue * 255);
|
|
9884
10850
|
break;
|
|
9885
10851
|
}
|
|
9886
10852
|
case "stem": {
|
|
9887
10853
|
const age = Number(state.age ?? 0);
|
|
9888
|
-
|
|
10854
|
+
color2 = age * 32 << 16 | 255 - age * 8 << 8 | age * 4;
|
|
9889
10855
|
break;
|
|
9890
10856
|
}
|
|
9891
10857
|
}
|
|
9892
|
-
return { ...Object.fromEntries([...indices].map((index) => [index,
|
|
10858
|
+
return { ...Object.fromEntries([...indices].map((index) => [index, color2])), ...tints };
|
|
9893
10859
|
}
|
|
9894
10860
|
};
|
|
9895
10861
|
|
|
@@ -9901,7 +10867,7 @@ var import_ts_deepmerge6 = __toESM(require_dist());
|
|
|
9901
10867
|
init_MineRenderError();
|
|
9902
10868
|
init_InstanceReference();
|
|
9903
10869
|
init_AssetKey();
|
|
9904
|
-
import { Euler as
|
|
10870
|
+
import { Euler as Euler10, Matrix4 as Matrix411, Quaternion as Quaternion5, Vector3 as Vector312 } from "three";
|
|
9905
10871
|
init_ModelCulling();
|
|
9906
10872
|
init_FluidGeometry();
|
|
9907
10873
|
init_Entities();
|
|
@@ -10026,8 +10992,8 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
10026
10992
|
return false;
|
|
10027
10993
|
}
|
|
10028
10994
|
return this._models.some((model) => {
|
|
10029
|
-
const
|
|
10030
|
-
return
|
|
10995
|
+
const object3 = isInstanceReference(model) ? model.instanceable : model;
|
|
10996
|
+
return object3.isOpaqueFullCube && this.getModelCullMask(model, 63) === 63;
|
|
10031
10997
|
});
|
|
10032
10998
|
}
|
|
10033
10999
|
getModelMatrix(model) {
|
|
@@ -10036,14 +11002,14 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
10036
11002
|
return model.matrix.clone();
|
|
10037
11003
|
}
|
|
10038
11004
|
getModelCullMask(model, worldMask) {
|
|
10039
|
-
const
|
|
10040
|
-
if (
|
|
11005
|
+
const object3 = isInstanceReference(model) ? model.instanceable : model;
|
|
11006
|
+
if (object3.options.displayPosition) return 0;
|
|
10041
11007
|
const position = new Vector312();
|
|
10042
11008
|
const rotation = new Quaternion5();
|
|
10043
11009
|
const scale = new Vector312();
|
|
10044
11010
|
this.getModelMatrix(model).decompose(position, rotation, scale);
|
|
10045
11011
|
if (position.distanceToSquared(this.position) > 1e-10 || scale.distanceToSquared(new Vector312(1, 1, 1)) > 1e-10) return 0;
|
|
10046
|
-
return ModelCulling.toLocalMask(worldMask, new
|
|
11012
|
+
return ModelCulling.toLocalMask(worldMask, new Euler10().setFromQuaternion(rotation));
|
|
10047
11013
|
}
|
|
10048
11014
|
async setCullMask(worldMask) {
|
|
10049
11015
|
worldMask &= 63;
|
|
@@ -10054,14 +11020,14 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
10054
11020
|
replacements.push(model);
|
|
10055
11021
|
continue;
|
|
10056
11022
|
}
|
|
10057
|
-
const
|
|
11023
|
+
const object3 = isInstanceReference(model) ? model.instanceable : model;
|
|
10058
11024
|
const cullMask = this.getModelCullMask(model, worldMask);
|
|
10059
|
-
if (cullMask === (
|
|
11025
|
+
if (cullMask === (object3.options.cullMask ?? 0)) {
|
|
10060
11026
|
replacements.push(model);
|
|
10061
11027
|
continue;
|
|
10062
11028
|
}
|
|
10063
11029
|
const matrix = this.getModelMatrix(model);
|
|
10064
|
-
const replacement = await this.scene.addModel(
|
|
11030
|
+
const replacement = await this.scene.addModel(object3.originalModel, { ...object3.options, cullMask });
|
|
10065
11031
|
replacements.push(replacement);
|
|
10066
11032
|
if (isInstanceReference(replacement)) {
|
|
10067
11033
|
replacement.setMatrix(matrix);
|
|
@@ -10069,8 +11035,8 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
10069
11035
|
replacement.matrix.copy(matrix);
|
|
10070
11036
|
replacement.matrixWorldNeedsUpdate = true;
|
|
10071
11037
|
matrix.decompose(replacement.position, replacement.quaternion, replacement.scale);
|
|
10072
|
-
replacement.matrixAutoUpdate =
|
|
10073
|
-
replacement.visible =
|
|
11038
|
+
replacement.matrixAutoUpdate = object3.matrixAutoUpdate;
|
|
11039
|
+
replacement.visible = object3.visible;
|
|
10074
11040
|
}
|
|
10075
11041
|
}
|
|
10076
11042
|
} catch (error) {
|
|
@@ -10086,6 +11052,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
10086
11052
|
}
|
|
10087
11053
|
this.notifyDirty();
|
|
10088
11054
|
}
|
|
11055
|
+
/** Selected property values. Use {@link setState} to change properties and rebuild the rendered models. */
|
|
10089
11056
|
get state() {
|
|
10090
11057
|
return this._state;
|
|
10091
11058
|
}
|
|
@@ -10185,6 +11152,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
10185
11152
|
obj.setPosition(this.position);
|
|
10186
11153
|
return obj;
|
|
10187
11154
|
}
|
|
11155
|
+
/** Clears selected properties and rebuilds models without reapplying property defaults. */
|
|
10188
11156
|
async resetState() {
|
|
10189
11157
|
this._previousState = this._state;
|
|
10190
11158
|
this._state = {};
|
|
@@ -11992,9 +12960,9 @@ var SchematicParser = class {
|
|
|
11992
12960
|
* Parses a legacy schematic using custom mappings before the bundled defaults.
|
|
11993
12961
|
* Keys are `id:metadata`; values are block states such as `minecraft:oak_log[axis=x]`.
|
|
11994
12962
|
*/
|
|
11995
|
-
static async parse(nbt, customMappings = {}) {
|
|
12963
|
+
static async parse(nbt, customMappings = {}, options = {}) {
|
|
11996
12964
|
const tags = nbt.value;
|
|
11997
|
-
if (tags.Materials?.type !== "string" || tags.Materials.value !== "Alpha") {
|
|
12965
|
+
if (!options.lenient && (tags.Materials?.type !== "string" || tags.Materials.value !== "Alpha")) {
|
|
11998
12966
|
throw new MineRenderError("Schematic Materials must be Alpha");
|
|
11999
12967
|
}
|
|
12000
12968
|
const size = ["Width", "Height", "Length"].map((name) => {
|
|
@@ -12035,8 +13003,16 @@ var SchematicParser = class {
|
|
|
12035
13003
|
const id = high << 8 | ids.value[index] & 255;
|
|
12036
13004
|
const metadata = data.value[index] & 255;
|
|
12037
13005
|
const key = `${id}:${metadata}`;
|
|
12038
|
-
|
|
12039
|
-
if (!mapped
|
|
13006
|
+
let mapped = customMappings[key] ?? mapping[key];
|
|
13007
|
+
if (!mapped && options.lenient) {
|
|
13008
|
+
mapped = customMappings[`${id}:0`] ?? mapping[`${id}:0`];
|
|
13009
|
+
}
|
|
13010
|
+
if (!mapped) {
|
|
13011
|
+
if (options.lenient) {
|
|
13012
|
+
continue;
|
|
13013
|
+
}
|
|
13014
|
+
throw new MineRenderError(`Unsupported legacy block ${id}:${metadata} at schematic index ${index}`);
|
|
13015
|
+
}
|
|
12040
13016
|
const [type, state] = mapped.split("[");
|
|
12041
13017
|
if (type === "minecraft:air") continue;
|
|
12042
13018
|
const properties = {};
|
|
@@ -12080,6 +13056,11 @@ function compoundList(tags, name) {
|
|
|
12080
13056
|
// src/model/multiblock/StructureParser.ts
|
|
12081
13057
|
init_MineRenderError();
|
|
12082
13058
|
var StructureParser = class {
|
|
13059
|
+
/**
|
|
13060
|
+
* Parses decoded NBT, omitting air blocks.
|
|
13061
|
+
* @param _nbt - Structure data decoded by {@link NBTHelper.fromBuffer}.
|
|
13062
|
+
* @param paletteIndex - Palette to select when the structure has several. Defaults to 0.
|
|
13063
|
+
*/
|
|
12083
13064
|
static async parse(_nbt, paletteIndex) {
|
|
12084
13065
|
const nbt = _nbt.value;
|
|
12085
13066
|
let palette;
|
|
@@ -12914,6 +13895,7 @@ var SkinImage = class {
|
|
|
12914
13895
|
// src/skin/SkinTextures.ts
|
|
12915
13896
|
init_Textures();
|
|
12916
13897
|
var SkinTextures = class {
|
|
13898
|
+
/** Loads a skin URL. Pass `legacy` to override layout detection from the image dimensions. */
|
|
12917
13899
|
static async get(src, legacy) {
|
|
12918
13900
|
const image = await SkinImage.getData(src);
|
|
12919
13901
|
const scale = image.width / 64;
|
|
@@ -13087,6 +14069,14 @@ var SkinObject = class extends SceneObject {
|
|
|
13087
14069
|
}
|
|
13088
14070
|
}
|
|
13089
14071
|
}
|
|
14072
|
+
/**
|
|
14073
|
+
* Loads a skin texture, detecting slim arms and legacy layouts by default.
|
|
14074
|
+
* To override detection, set `slim` or `legacy` in the options passed to
|
|
14075
|
+
* `new SkinObject(options)` or `scene.addSkin(src, options)`.
|
|
14076
|
+
* Use {@link setSlim} or {@link setLegacy} to change these settings later.
|
|
14077
|
+
*
|
|
14078
|
+
* @param src - URL of the skin texture.
|
|
14079
|
+
*/
|
|
13090
14080
|
async setSkinTexture(src) {
|
|
13091
14081
|
if (typeof src === "undefined") return;
|
|
13092
14082
|
this.skinTextureSrc = src;
|
|
@@ -13194,59 +14184,61 @@ var _MineRenderScene = class _MineRenderScene extends Scene4 {
|
|
|
13194
14184
|
this.options = (0, import_ts_deepmerge8.default)({}, _MineRenderScene.DEFAULT_OPTIONS, options ?? {});
|
|
13195
14185
|
}
|
|
13196
14186
|
add(...objects) {
|
|
13197
|
-
for (const
|
|
14187
|
+
for (const object3 of objects) {
|
|
13198
14188
|
this.dirty = true;
|
|
13199
14189
|
try {
|
|
13200
|
-
super.add(
|
|
14190
|
+
super.add(object3);
|
|
13201
14191
|
} finally {
|
|
13202
|
-
if (
|
|
13203
|
-
this.updateObjectRegistration(
|
|
14192
|
+
if (object3?.isObject3D && object3 !== this) {
|
|
14193
|
+
this.updateObjectRegistration(object3, object3.parent === this);
|
|
13204
14194
|
}
|
|
13205
14195
|
}
|
|
13206
14196
|
}
|
|
13207
14197
|
return this;
|
|
13208
14198
|
}
|
|
13209
14199
|
remove(...objects) {
|
|
13210
|
-
for (const
|
|
13211
|
-
if (!this.children.includes(
|
|
14200
|
+
for (const object3 of objects) {
|
|
14201
|
+
if (!this.children.includes(object3)) continue;
|
|
13212
14202
|
this.dirty = true;
|
|
13213
|
-
this.updateObjectRegistration(
|
|
14203
|
+
this.updateObjectRegistration(object3, false);
|
|
13214
14204
|
try {
|
|
13215
|
-
super.remove(
|
|
14205
|
+
super.remove(object3);
|
|
13216
14206
|
} finally {
|
|
13217
|
-
this.updateObjectRegistration(
|
|
14207
|
+
this.updateObjectRegistration(object3, object3.parent === this);
|
|
13218
14208
|
}
|
|
13219
14209
|
}
|
|
13220
14210
|
return this;
|
|
13221
14211
|
}
|
|
13222
|
-
updateObjectRegistration(
|
|
13223
|
-
if (this.observedObjects.has(
|
|
13224
|
-
const observable =
|
|
14212
|
+
updateObjectRegistration(object3, attached) {
|
|
14213
|
+
if (this.observedObjects.has(object3) === attached) return;
|
|
14214
|
+
const observable = object3;
|
|
13225
14215
|
if (attached) {
|
|
13226
|
-
this.observedObjects.add(
|
|
14216
|
+
this.observedObjects.add(object3);
|
|
13227
14217
|
observable.addEventListener("change", this.onObjectChange);
|
|
13228
14218
|
} else {
|
|
13229
|
-
this.observedObjects.delete(
|
|
14219
|
+
this.observedObjects.delete(object3);
|
|
13230
14220
|
observable.removeEventListener("change", this.onObjectChange);
|
|
13231
14221
|
}
|
|
13232
14222
|
const change = attached ? 1 : -1;
|
|
13233
14223
|
this.stats.objectCount += change;
|
|
13234
|
-
if (isSceneObject(
|
|
14224
|
+
if (isSceneObject(object3)) {
|
|
13235
14225
|
this.stats.sceneObjectCount += change;
|
|
13236
14226
|
}
|
|
13237
14227
|
}
|
|
13238
|
-
|
|
14228
|
+
/** Initializes each object before attaching it to the scene. */
|
|
14229
|
+
async initAndAdd(...object3) {
|
|
13239
14230
|
this.dirty = true;
|
|
13240
|
-
for (let obj of
|
|
14231
|
+
for (let obj of object3) {
|
|
13241
14232
|
await obj.init();
|
|
13242
14233
|
}
|
|
13243
|
-
return this.add(...
|
|
14234
|
+
return this.add(...object3);
|
|
13244
14235
|
}
|
|
13245
|
-
async addSceneObject(
|
|
14236
|
+
async addSceneObject(asset2, objectSupplier, _options, parent = this) {
|
|
13246
14237
|
this.dirty = true;
|
|
13247
14238
|
const obj = await objectSupplier();
|
|
13248
|
-
if (obj
|
|
13249
|
-
|
|
14239
|
+
if (isModelObject(obj)) obj.options.tints = await ItemTints.get(obj.originalModel, obj.options.tints);
|
|
14240
|
+
if (obj?.options?.instanceMeshes && asset2.key && asset2.key?.assetType === "models") {
|
|
14241
|
+
let key = asset2.key.serialize();
|
|
13250
14242
|
if (isModelObject(obj)) {
|
|
13251
14243
|
const { displayPosition, uvLockRotation, tints, cullMask } = obj.options;
|
|
13252
14244
|
if (displayPosition) key += `|display:${displayPosition}`;
|
|
@@ -13270,13 +14262,32 @@ var _MineRenderScene = class _MineRenderScene extends Scene4 {
|
|
|
13270
14262
|
return obj;
|
|
13271
14263
|
}
|
|
13272
14264
|
}
|
|
14265
|
+
/**
|
|
14266
|
+
* Adds a model loaded with {@link Models.getMerged}.
|
|
14267
|
+
* Set `options.instanceMeshes` to `false` to create a separate ModelObject.
|
|
14268
|
+
*
|
|
14269
|
+
* @param parent - Object to attach the model to. Defaults to this scene.
|
|
14270
|
+
* @returns The model, or a reference to one placement of a shared model.
|
|
14271
|
+
*/
|
|
13273
14272
|
async addModel(model, options, parent = this) {
|
|
13274
|
-
options = { ...options, tints: await ItemTints.get(model, options?.tints) };
|
|
13275
14273
|
return this.addSceneObject(model, () => new ModelObject(model, options), options, parent);
|
|
13276
14274
|
}
|
|
14275
|
+
/**
|
|
14276
|
+
* Adds the models selected by a blockstate loaded with {@link BlockStates.get}.
|
|
14277
|
+
* Set `options.initialState` to choose block properties such as `facing` or `axis`.
|
|
14278
|
+
* Use the returned object's `setPosition` method to move all of its model parts.
|
|
14279
|
+
*/
|
|
13277
14280
|
async addBlock(blockState, options, parent = this) {
|
|
13278
14281
|
return this.addSceneObject(blockState, () => new BlockObject(blockState, options), options, parent);
|
|
13279
14282
|
}
|
|
14283
|
+
/**
|
|
14284
|
+
* Loads a skin and adds an initialized player model.
|
|
14285
|
+
*
|
|
14286
|
+
* @param skin - Texture URL, such as one returned by {@link Skins.fromUuidOrUsername}.
|
|
14287
|
+
* Omit it to assign a texture later with {@link SkinObject.setSkinTexture}.
|
|
14288
|
+
* @param options - Player settings, including `slim` and `legacy` detection overrides.
|
|
14289
|
+
* @param parent - Object to attach the player to. Defaults to this scene.
|
|
14290
|
+
*/
|
|
13280
14291
|
async addSkin(skin, options, parent = this) {
|
|
13281
14292
|
this.dirty = true;
|
|
13282
14293
|
const obj = new SkinObject(options);
|
|
@@ -13288,9 +14299,14 @@ var _MineRenderScene = class _MineRenderScene extends Scene4 {
|
|
|
13288
14299
|
parent.add(obj);
|
|
13289
14300
|
return obj;
|
|
13290
14301
|
}
|
|
14302
|
+
/** Adds an entity loaded with {@link Entities.getEntity}, using its selected layers and textures. */
|
|
13291
14303
|
async addEntity(entity, options, parent = this) {
|
|
13292
14304
|
return this.addSceneObject(entity, () => new EntityObject(entity, options), options, parent);
|
|
13293
14305
|
}
|
|
14306
|
+
/**
|
|
14307
|
+
* Adds GUI layers in draw order. One GUI pixel equals one scene unit.
|
|
14308
|
+
* Layer positions start at the top left, with positive GUI y pointing down.
|
|
14309
|
+
*/
|
|
13294
14310
|
async addGui(layers, options, parent = this) {
|
|
13295
14311
|
const obj = new GuiObject(layers, options);
|
|
13296
14312
|
obj.scene = this;
|
|
@@ -13329,11 +14345,11 @@ var _changeEvent = { type: "change" };
|
|
|
13329
14345
|
var _startEvent = { type: "start" };
|
|
13330
14346
|
var _endEvent = { type: "end" };
|
|
13331
14347
|
var OrbitControls = class extends EventDispatcher {
|
|
13332
|
-
constructor(
|
|
14348
|
+
constructor(object3, domElement) {
|
|
13333
14349
|
super();
|
|
13334
14350
|
if (domElement === void 0) console.warn('THREE.OrbitControls: The second parameter "domElement" is now mandatory.');
|
|
13335
14351
|
if (domElement === document) console.error('THREE.OrbitControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.');
|
|
13336
|
-
this.object =
|
|
14352
|
+
this.object = object3;
|
|
13337
14353
|
this.domElement = domElement;
|
|
13338
14354
|
this.domElement.style.touchAction = "none";
|
|
13339
14355
|
this.enabled = true;
|
|
@@ -13390,7 +14406,7 @@ var OrbitControls = class extends EventDispatcher {
|
|
|
13390
14406
|
};
|
|
13391
14407
|
this.update = function() {
|
|
13392
14408
|
const offset = new Vector313();
|
|
13393
|
-
const quat = new Quaternion6().setFromUnitVectors(
|
|
14409
|
+
const quat = new Quaternion6().setFromUnitVectors(object3.up, new Vector313(0, 1, 0));
|
|
13394
14410
|
const quatInverse = quat.clone().invert();
|
|
13395
14411
|
const lastPosition = new Vector313();
|
|
13396
14412
|
const lastQuaternion = new Quaternion6();
|
|
@@ -13955,8 +14971,8 @@ var OrbitControls = class extends EventDispatcher {
|
|
|
13955
14971
|
}
|
|
13956
14972
|
};
|
|
13957
14973
|
var MapControls = class extends OrbitControls {
|
|
13958
|
-
constructor(
|
|
13959
|
-
super(
|
|
14974
|
+
constructor(object3, domElement) {
|
|
14975
|
+
super(object3, domElement);
|
|
13960
14976
|
this.screenSpacePanning = false;
|
|
13961
14977
|
this.mouseButtons.LEFT = MOUSE.PAN;
|
|
13962
14978
|
this.mouseButtons.RIGHT = MOUSE.ROTATE;
|
|
@@ -14115,6 +15131,7 @@ var _Renderer = class _Renderer {
|
|
|
14115
15131
|
get element() {
|
|
14116
15132
|
return this._element;
|
|
14117
15133
|
}
|
|
15134
|
+
/** Attaches the canvas to an element and sizes it to that element's dimensions. */
|
|
14118
15135
|
appendTo(element) {
|
|
14119
15136
|
if (this._disposed) return;
|
|
14120
15137
|
this._element = element;
|
|
@@ -14142,6 +15159,7 @@ var _Renderer = class _Renderer {
|
|
|
14142
15159
|
events.add(changeEvent2);
|
|
14143
15160
|
dispatcher.addEventListener(changeEvent2, this._changeListener);
|
|
14144
15161
|
}
|
|
15162
|
+
/** Updates the camera and canvas size. Width and height are in CSS pixels. */
|
|
14145
15163
|
resize(width, height) {
|
|
14146
15164
|
if (this._disposed) return;
|
|
14147
15165
|
if (isPerspectiveCamera(this.camera)) {
|
|
@@ -14180,12 +15198,14 @@ var _Renderer = class _Renderer {
|
|
|
14180
15198
|
if (this._frameCallbacks.get(callback) === subscription) this._frameCallbacks.delete(callback);
|
|
14181
15199
|
};
|
|
14182
15200
|
}
|
|
15201
|
+
/** Starts or resumes the animation loop and registered frame callbacks. */
|
|
14183
15202
|
start() {
|
|
14184
15203
|
if (this._disposed) return;
|
|
14185
15204
|
this.stop();
|
|
14186
15205
|
this._running = true;
|
|
14187
15206
|
if (!this._inAnimationLoop) this.renderer.setAnimationLoop(this._animationLoop);
|
|
14188
15207
|
}
|
|
15208
|
+
/** Pauses rendering and frame callbacks. Call {@link start} to resume. */
|
|
14189
15209
|
stop() {
|
|
14190
15210
|
if (this._disposed) return;
|
|
14191
15211
|
this._running = false;
|
|
@@ -14294,12 +15314,15 @@ var _Renderer = class _Renderer {
|
|
|
14294
15314
|
const canvas = trim ? trimCanvas(this.renderer.domElement) : this.renderer.domElement;
|
|
14295
15315
|
return canvas.toDataURL(mime, quality);
|
|
14296
15316
|
}
|
|
15317
|
+
/** Exports visible scene meshes as OBJ text, without texture images. */
|
|
14297
15318
|
toObj() {
|
|
14298
15319
|
return SceneExporter.toObj(this.scene);
|
|
14299
15320
|
}
|
|
15321
|
+
/** Exports visible scene meshes as PLY. Set `options.binary` for an ArrayBuffer. */
|
|
14300
15322
|
toPLY(options) {
|
|
14301
15323
|
return SceneExporter.toPLY(this.scene, options);
|
|
14302
15324
|
}
|
|
15325
|
+
/** Exports a static textured scene in the browser. Set `options.binary` for GLB output. */
|
|
14303
15326
|
toGLTF(options) {
|
|
14304
15327
|
return SceneExporter.toGLTF(this.scene, options);
|
|
14305
15328
|
}
|
|
@@ -14377,16 +15400,244 @@ init_SceneObject();
|
|
|
14377
15400
|
// src/index.ts
|
|
14378
15401
|
init_request();
|
|
14379
15402
|
|
|
15403
|
+
// src/scene/SceneDocument.ts
|
|
15404
|
+
init_DisplayPosition();
|
|
15405
|
+
function fail(path, message) {
|
|
15406
|
+
throw new Error(`${path}: ${message}`);
|
|
15407
|
+
}
|
|
15408
|
+
function object2(value, path) {
|
|
15409
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail(path, "expected a JSON object");
|
|
15410
|
+
for (const key of Object.keys(value)) {
|
|
15411
|
+
if (["__proto__", "prototype", "constructor"].includes(key)) fail(`${path}.${key}`, "unsupported property");
|
|
15412
|
+
}
|
|
15413
|
+
return value;
|
|
15414
|
+
}
|
|
15415
|
+
function fields(value, path, allowed, required = []) {
|
|
15416
|
+
const input = object2(value, path);
|
|
15417
|
+
for (const key of required) if (input[key] === void 0) fail(`${path}.${key}`, "required");
|
|
15418
|
+
for (const [key, entry] of Object.entries(input)) {
|
|
15419
|
+
if (!Object.prototype.hasOwnProperty.call(allowed, key)) fail(`${path}.${key}`, "unsupported property");
|
|
15420
|
+
if (entry !== void 0) allowed[key](entry, `${path}.${key}`);
|
|
15421
|
+
}
|
|
15422
|
+
return input;
|
|
15423
|
+
}
|
|
15424
|
+
var text = (value, path) => {
|
|
15425
|
+
if (typeof value !== "string" || !value.trim()) fail(path, "expected a nonempty string");
|
|
15426
|
+
};
|
|
15427
|
+
var literalText = (value, path) => {
|
|
15428
|
+
if (typeof value !== "string") fail(path, "expected a string");
|
|
15429
|
+
};
|
|
15430
|
+
var boolean = (value, path) => {
|
|
15431
|
+
if (typeof value !== "boolean") fail(path, "expected a boolean");
|
|
15432
|
+
};
|
|
15433
|
+
var number = (value, path) => {
|
|
15434
|
+
if (typeof value !== "number" || !Number.isFinite(value)) fail(path, "expected a finite number");
|
|
15435
|
+
};
|
|
15436
|
+
var positive = (value, path) => {
|
|
15437
|
+
number(value, path);
|
|
15438
|
+
if (value <= 0) fail(path, "expected a positive number");
|
|
15439
|
+
};
|
|
15440
|
+
var nonnegative = (value, path) => {
|
|
15441
|
+
number(value, path);
|
|
15442
|
+
if (value < 0) fail(path, "expected a nonnegative number");
|
|
15443
|
+
};
|
|
15444
|
+
var color = (value, path) => {
|
|
15445
|
+
number(value, path);
|
|
15446
|
+
if (!Number.isInteger(value) || value < 0 || value > 16777215) {
|
|
15447
|
+
fail(path, "expected an RGB integer from 0 to 16777215");
|
|
15448
|
+
}
|
|
15449
|
+
};
|
|
15450
|
+
var asset = (value, path) => {
|
|
15451
|
+
text(value, path);
|
|
15452
|
+
if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(value) || value.split(/[:/]/).some((part) => !part || part === "." || part === "..")) {
|
|
15453
|
+
fail(path, "expected a resource ID such as minecraft:stone");
|
|
15454
|
+
}
|
|
15455
|
+
};
|
|
15456
|
+
var version = (value, path) => {
|
|
15457
|
+
text(value, path);
|
|
15458
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(value)) fail(path, "expected a Minecraft version without slashes");
|
|
15459
|
+
};
|
|
15460
|
+
var tuple = (size, item = number) => (value, path) => {
|
|
15461
|
+
if (!Array.isArray(value) || value.length !== size) fail(path, `expected ${size} numbers`);
|
|
15462
|
+
for (let index = 0; index < size; index++) item(value[index], `${path}[${index}]`);
|
|
15463
|
+
};
|
|
15464
|
+
var list = (item) => (value, path) => {
|
|
15465
|
+
if (!Array.isArray(value)) fail(path, "expected an array");
|
|
15466
|
+
for (let index = 0; index < value.length; index++) item(value[index], `${path}[${index}]`);
|
|
15467
|
+
};
|
|
15468
|
+
var dictionary = (item, numericKeys = false) => (value, path) => {
|
|
15469
|
+
for (const [key, entry] of Object.entries(object2(value, path))) {
|
|
15470
|
+
if (numericKeys && !/^\d+$/.test(key)) fail(`${path}.${key}`, "expected a nonnegative tint index");
|
|
15471
|
+
item(entry, `${path}.${key}`);
|
|
15472
|
+
}
|
|
15473
|
+
};
|
|
15474
|
+
var enumeration = (values) => (value, path) => {
|
|
15475
|
+
if (!values.includes(value)) fail(path, `expected one of: ${values.join(", ")}`);
|
|
15476
|
+
};
|
|
15477
|
+
var textStyle = { color, bold: boolean, italic: boolean };
|
|
15478
|
+
function guiLayer(value, path) {
|
|
15479
|
+
const layer = object2(value, path);
|
|
15480
|
+
if (["texture", "item", "text"].filter((key) => layer[key] !== void 0).length !== 1) {
|
|
15481
|
+
fail(path, "expected exactly one of texture, item, or text");
|
|
15482
|
+
}
|
|
15483
|
+
const layout = { name: text, position: tuple(2), size: tuple(2, positive) };
|
|
15484
|
+
if (layer.texture !== void 0) {
|
|
15485
|
+
fields(value, path, { ...layout, texture: asset, crop: (value2, path2) => {
|
|
15486
|
+
tuple(4)(value2, path2);
|
|
15487
|
+
const [x, y, width, height] = value2;
|
|
15488
|
+
nonnegative(x, `${path2}[0]`);
|
|
15489
|
+
nonnegative(y, `${path2}[1]`);
|
|
15490
|
+
positive(width, `${path2}[2]`);
|
|
15491
|
+
positive(height, `${path2}[3]`);
|
|
15492
|
+
} });
|
|
15493
|
+
} else if (layer.item !== void 0) {
|
|
15494
|
+
fields(value, path, { ...layout, item: asset, tints: dictionary(color, true) });
|
|
15495
|
+
} else {
|
|
15496
|
+
fields(value, path, {
|
|
15497
|
+
...layout,
|
|
15498
|
+
...textStyle,
|
|
15499
|
+
font: asset,
|
|
15500
|
+
shadow: boolean,
|
|
15501
|
+
maxWidth: positive,
|
|
15502
|
+
lineHeight: positive,
|
|
15503
|
+
text: (value2, path2) => {
|
|
15504
|
+
if (typeof value2 === "string") return;
|
|
15505
|
+
list((run, path3) => {
|
|
15506
|
+
fields(run, path3, { text: literalText, ...textStyle }, ["text"]);
|
|
15507
|
+
})(value2, path2);
|
|
15508
|
+
}
|
|
15509
|
+
});
|
|
15510
|
+
}
|
|
15511
|
+
}
|
|
15512
|
+
function validateObject(value, path) {
|
|
15513
|
+
const input = object2(value, path);
|
|
15514
|
+
const common = { id: text, name: text, type: text, position: tuple(3), rotation: tuple(3), scale: tuple(3), visible: boolean };
|
|
15515
|
+
const required = ["id", "type"];
|
|
15516
|
+
const renderOptions = { wireframe: boolean };
|
|
15517
|
+
switch (input.type) {
|
|
15518
|
+
case "skin":
|
|
15519
|
+
fields(value, path, {
|
|
15520
|
+
...common,
|
|
15521
|
+
skin: text,
|
|
15522
|
+
cape: (value2, path2) => {
|
|
15523
|
+
fields(value2, path2, { texture: text, layout: enumeration(["minecraft", "optifine", "labymod"]) }, ["texture"]);
|
|
15524
|
+
},
|
|
15525
|
+
hiddenParts: list(enumeration(SKIN_PARTS)),
|
|
15526
|
+
pose: (value2, path2) => {
|
|
15527
|
+
fields(value2, path2, {
|
|
15528
|
+
head: tuple(3),
|
|
15529
|
+
body: tuple(3),
|
|
15530
|
+
rightArm: tuple(3),
|
|
15531
|
+
leftArm: tuple(3),
|
|
15532
|
+
rightLeg: tuple(3),
|
|
15533
|
+
leftLeg: tuple(3),
|
|
15534
|
+
cape: tuple(3)
|
|
15535
|
+
});
|
|
15536
|
+
},
|
|
15537
|
+
options: (value2, path2) => {
|
|
15538
|
+
fields(value2, path2, { slim: boolean, legacy: boolean });
|
|
15539
|
+
}
|
|
15540
|
+
}, required);
|
|
15541
|
+
break;
|
|
15542
|
+
case "block":
|
|
15543
|
+
fields(value, path, {
|
|
15544
|
+
...common,
|
|
15545
|
+
asset,
|
|
15546
|
+
state: dictionary(text),
|
|
15547
|
+
options: (value2, path2) => {
|
|
15548
|
+
fields(value2, path2, { ...renderOptions, tints: dictionary(color, true) });
|
|
15549
|
+
}
|
|
15550
|
+
}, [...required, "asset"]);
|
|
15551
|
+
break;
|
|
15552
|
+
case "item":
|
|
15553
|
+
case "model":
|
|
15554
|
+
fields(value, path, {
|
|
15555
|
+
...common,
|
|
15556
|
+
asset,
|
|
15557
|
+
options: (value2, path2) => {
|
|
15558
|
+
fields(value2, path2, { ...renderOptions, displayPosition: enumeration(DISPLAY_POSITIONS), tints: dictionary(color, true) });
|
|
15559
|
+
}
|
|
15560
|
+
}, [...required, "asset"]);
|
|
15561
|
+
break;
|
|
15562
|
+
case "entity":
|
|
15563
|
+
fields(value, path, {
|
|
15564
|
+
...common,
|
|
15565
|
+
asset,
|
|
15566
|
+
texture: asset,
|
|
15567
|
+
layers: list(text),
|
|
15568
|
+
when: list(text),
|
|
15569
|
+
textures: dictionary(asset),
|
|
15570
|
+
options: (value2, path2) => {
|
|
15571
|
+
fields(value2, path2, {
|
|
15572
|
+
...renderOptions,
|
|
15573
|
+
flip: boolean,
|
|
15574
|
+
tints: dictionary((value3, path3) => {
|
|
15575
|
+
typeof value3 === "string" ? text(value3, path3) : color(value3, path3);
|
|
15576
|
+
})
|
|
15577
|
+
});
|
|
15578
|
+
},
|
|
15579
|
+
animation: (value2, path2) => {
|
|
15580
|
+
fields(value2, path2, { name: text, loop: boolean, speed: nonnegative, time: nonnegative, paused: boolean }, ["name"]);
|
|
15581
|
+
}
|
|
15582
|
+
}, [...required, "asset"]);
|
|
15583
|
+
if (Array.isArray(input.layers) && !input.layers.length) fail(`${path}.layers`, "expected at least one layer");
|
|
15584
|
+
break;
|
|
15585
|
+
case "gui":
|
|
15586
|
+
fields(value, path, { ...common, layers: list(guiLayer) }, [...required, "layers"]);
|
|
15587
|
+
break;
|
|
15588
|
+
default:
|
|
15589
|
+
fail(`${path}.type`, "expected skin, block, item, entity, model, or gui");
|
|
15590
|
+
}
|
|
15591
|
+
}
|
|
15592
|
+
function parseSceneDocument(value) {
|
|
15593
|
+
if (typeof value === "string") {
|
|
15594
|
+
try {
|
|
15595
|
+
value = JSON.parse(value);
|
|
15596
|
+
} catch {
|
|
15597
|
+
fail("scene", "invalid JSON");
|
|
15598
|
+
}
|
|
15599
|
+
}
|
|
15600
|
+
const input = fields(value, "scene", {
|
|
15601
|
+
format: enumeration(["minerender-scene"]),
|
|
15602
|
+
version: (value2, path) => {
|
|
15603
|
+
if (value2 !== 1) fail(path, "only scene document version 1 is supported");
|
|
15604
|
+
},
|
|
15605
|
+
minecraftVersion: version,
|
|
15606
|
+
camera: (value2, path) => {
|
|
15607
|
+
fields(value2, path, { position: tuple(3), target: tuple(3) }, ["position", "target"]);
|
|
15608
|
+
},
|
|
15609
|
+
objects: list(validateObject)
|
|
15610
|
+
}, ["format", "version", "objects"]);
|
|
15611
|
+
const ids = /* @__PURE__ */ new Set();
|
|
15612
|
+
for (const [index, entry] of input.objects.entries()) {
|
|
15613
|
+
if (ids.has(entry.id)) fail(`scene.objects[${index}].id`, `duplicate object ID "${entry.id}"`);
|
|
15614
|
+
ids.add(entry.id);
|
|
15615
|
+
}
|
|
15616
|
+
return JSON.parse(JSON.stringify(input));
|
|
15617
|
+
}
|
|
15618
|
+
|
|
15619
|
+
// src/scene/SceneDocumentLoader.ts
|
|
15620
|
+
init_AssetKey();
|
|
15621
|
+
init_AssetLoader();
|
|
15622
|
+
init_BlockStates();
|
|
15623
|
+
init_Entities();
|
|
15624
|
+
init_Models();
|
|
15625
|
+
init_DisplayPosition();
|
|
15626
|
+
import { Group as Group2 } from "three";
|
|
15627
|
+
init_SceneObject();
|
|
15628
|
+
|
|
14380
15629
|
// src/skin/Skins.ts
|
|
14381
15630
|
init_Requests();
|
|
14382
15631
|
var Skins = class {
|
|
14383
15632
|
//<editor-fold desc="Skins">
|
|
15633
|
+
/** Returns a skin URL for {@link MineRenderScene.addSkin}, or `undefined` if name lookup fails. */
|
|
14384
15634
|
static async fromUuidOrUsername(uuidOrUsername) {
|
|
14385
15635
|
if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
|
|
14386
15636
|
return this.fromUuid(uuidOrUsername);
|
|
14387
15637
|
}
|
|
14388
15638
|
return this.fromUsername(uuidOrUsername);
|
|
14389
15639
|
}
|
|
15640
|
+
/** Builds a proxy URL for a UUID without downloading or checking the skin. */
|
|
14390
15641
|
static async fromUuid(uuid) {
|
|
14391
15642
|
return this.getMcProxySkinUrl(uuid);
|
|
14392
15643
|
}
|
|
@@ -14398,6 +15649,7 @@ var Skins = class {
|
|
|
14398
15649
|
return void 0;
|
|
14399
15650
|
});
|
|
14400
15651
|
}
|
|
15652
|
+
/** Looks up a saved MineSkin skin and returns its texture URL, or `undefined` on lookup failure. */
|
|
14401
15653
|
static async fromMineSkin(mineskinId) {
|
|
14402
15654
|
return this.getMineSkin(mineskinId).then((mineskin) => {
|
|
14403
15655
|
if (mineskin) {
|
|
@@ -14408,12 +15660,14 @@ var Skins = class {
|
|
|
14408
15660
|
}
|
|
14409
15661
|
//</editor-fold>
|
|
14410
15662
|
//<editor-fold desc="Capes">
|
|
15663
|
+
/** Resolves a player's Minecraft cape URL for {@link SkinObject.setCapeTexture}. */
|
|
14411
15664
|
static async capeFromUuidOrUsername(uuidOrUsername) {
|
|
14412
15665
|
if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
|
|
14413
15666
|
return this.capeFromUuid(uuidOrUsername);
|
|
14414
15667
|
}
|
|
14415
15668
|
return this.capeFromUsername(uuidOrUsername);
|
|
14416
15669
|
}
|
|
15670
|
+
/** Builds a proxy URL without checking whether the player has a cape. */
|
|
14417
15671
|
static async capeFromUuid(uuid) {
|
|
14418
15672
|
return this.getMcProxyCapeUrl(uuid);
|
|
14419
15673
|
}
|
|
@@ -14542,6 +15796,195 @@ var Skins = class {
|
|
|
14542
15796
|
//</editor-fold>
|
|
14543
15797
|
};
|
|
14544
15798
|
|
|
15799
|
+
// src/scene/SceneDocumentLoader.ts
|
|
15800
|
+
var DocumentObjectScene = class extends MineRenderScene {
|
|
15801
|
+
async addSceneObject(_asset, supplier, _options, parent = this) {
|
|
15802
|
+
const object3 = await supplier();
|
|
15803
|
+
return this.initialize(object3, parent);
|
|
15804
|
+
}
|
|
15805
|
+
async initialize(object3, parent = this) {
|
|
15806
|
+
object3.scene = this;
|
|
15807
|
+
try {
|
|
15808
|
+
await object3.init();
|
|
15809
|
+
parent.add(object3);
|
|
15810
|
+
return object3;
|
|
15811
|
+
} catch (error) {
|
|
15812
|
+
object3.dispose();
|
|
15813
|
+
object3.removeFromParent();
|
|
15814
|
+
throw error;
|
|
15815
|
+
}
|
|
15816
|
+
}
|
|
15817
|
+
dispose() {
|
|
15818
|
+
for (const child of [...this.children]) {
|
|
15819
|
+
if (child.parent !== this) continue;
|
|
15820
|
+
if (isSceneObject(child)) child.dispose();
|
|
15821
|
+
child.removeFromParent();
|
|
15822
|
+
}
|
|
15823
|
+
}
|
|
15824
|
+
};
|
|
15825
|
+
async function skinTexture(value, cape = false) {
|
|
15826
|
+
if (!/^[a-zA-Z0-9_-]{1,36}$/.test(value)) return value;
|
|
15827
|
+
const texture = await (cape ? Skins.capeFromUuidOrUsername(value) : Skins.fromUuidOrUsername(value));
|
|
15828
|
+
if (!texture) throw new Error(`Could not find ${cape ? "cape" : "skin"} for "${value}"`);
|
|
15829
|
+
return texture;
|
|
15830
|
+
}
|
|
15831
|
+
var SceneDocumentLoader = class {
|
|
15832
|
+
static parse(value) {
|
|
15833
|
+
return parseSceneDocument(value);
|
|
15834
|
+
}
|
|
15835
|
+
/**
|
|
15836
|
+
* Loads objects concurrently and attaches them in document order after every load succeeds.
|
|
15837
|
+
* A failure waits for all loads, disposes staged objects, and reports the first error in document order.
|
|
15838
|
+
*/
|
|
15839
|
+
static async load(scene, value, parent = scene) {
|
|
15840
|
+
const definition = this.parse(value);
|
|
15841
|
+
if (definition.minecraftVersion !== void 0 && definition.minecraftVersion !== AssetLoader.version) {
|
|
15842
|
+
throw new Error(`Scene requires Minecraft ${definition.minecraftVersion}; call AssetLoader.setVersion("${definition.minecraftVersion}") before loading it`);
|
|
15843
|
+
}
|
|
15844
|
+
const root = new Group2();
|
|
15845
|
+
root.name = "MineRender scene document";
|
|
15846
|
+
const objects = [];
|
|
15847
|
+
let disposed = false;
|
|
15848
|
+
const loaded = {
|
|
15849
|
+
root,
|
|
15850
|
+
objects,
|
|
15851
|
+
definition,
|
|
15852
|
+
advanceAnimations: (delta) => {
|
|
15853
|
+
if (!disposed) for (const object3 of objects) object3.advanceAnimation(delta);
|
|
15854
|
+
},
|
|
15855
|
+
dispose: () => {
|
|
15856
|
+
if (disposed) return;
|
|
15857
|
+
disposed = true;
|
|
15858
|
+
root.removeFromParent();
|
|
15859
|
+
for (const object3 of objects) object3.dispose();
|
|
15860
|
+
root.clear();
|
|
15861
|
+
scene.dirty = true;
|
|
15862
|
+
}
|
|
15863
|
+
};
|
|
15864
|
+
try {
|
|
15865
|
+
const results = await Promise.allSettled(definition.objects.map((object3) => this.loadValidatedObject(scene, object3)));
|
|
15866
|
+
for (const result of results) if (result.status === "fulfilled") objects.push(result.value);
|
|
15867
|
+
const failure = results.find((result) => result.status === "rejected");
|
|
15868
|
+
if (failure) throw failure.reason;
|
|
15869
|
+
for (const object3 of objects) root.add(object3.root);
|
|
15870
|
+
parent.add(root);
|
|
15871
|
+
scene.dirty = true;
|
|
15872
|
+
return loaded;
|
|
15873
|
+
} catch (error) {
|
|
15874
|
+
loaded.dispose();
|
|
15875
|
+
throw error;
|
|
15876
|
+
}
|
|
15877
|
+
}
|
|
15878
|
+
/** Loads an independently editable object. Its root owns the document's position, rotation, and scale. */
|
|
15879
|
+
static async loadObject(scene, value, parent = scene) {
|
|
15880
|
+
const definition = this.parse({ format: "minerender-scene", version: 1, objects: [value] }).objects[0];
|
|
15881
|
+
return this.loadValidatedObject(scene, definition, parent);
|
|
15882
|
+
}
|
|
15883
|
+
static async loadValidatedObject(scene, definition, parent) {
|
|
15884
|
+
const root = new Group2();
|
|
15885
|
+
root.name = definition.name ?? definition.id;
|
|
15886
|
+
root.userData.sceneObjectId = definition.id;
|
|
15887
|
+
root.position.fromArray(definition.position ?? [0, 0, 0]);
|
|
15888
|
+
root.rotation.set(...definition.rotation ?? [0, 0, 0]);
|
|
15889
|
+
root.scale.fromArray(definition.scale ?? [1, 1, 1]);
|
|
15890
|
+
root.visible = definition.visible ?? true;
|
|
15891
|
+
const content = new DocumentObjectScene();
|
|
15892
|
+
root.add(content);
|
|
15893
|
+
let object3;
|
|
15894
|
+
try {
|
|
15895
|
+
switch (definition.type) {
|
|
15896
|
+
case "skin": {
|
|
15897
|
+
const skin = new SkinObject({ ...definition.options, instanceMeshes: false });
|
|
15898
|
+
object3 = skin;
|
|
15899
|
+
const texture = definition.skin ? await skinTexture(definition.skin) : `${AssetLoader.ROOT}/assets/minecraft/textures/entity/player/${definition.options?.slim ? "slim/alex" : "wide/steve"}.png`;
|
|
15900
|
+
await skin.setSkinTexture(texture);
|
|
15901
|
+
await content.initialize(skin);
|
|
15902
|
+
if (definition.cape) await skin.setCapeTexture(await skinTexture(definition.cape.texture, true), definition.cape.layout);
|
|
15903
|
+
for (const [part, rotation] of Object.entries(definition.pose ?? {})) {
|
|
15904
|
+
skin.getGroupByName(part)?.rotation.set(...rotation, "XYZ");
|
|
15905
|
+
}
|
|
15906
|
+
for (const part of definition.hiddenParts ?? []) {
|
|
15907
|
+
const mesh = skin.getMeshByName(part);
|
|
15908
|
+
if (mesh) mesh.visible = false;
|
|
15909
|
+
}
|
|
15910
|
+
break;
|
|
15911
|
+
}
|
|
15912
|
+
case "block": {
|
|
15913
|
+
const state = await BlockStates.get(AssetKey.parse("blockstates", definition.asset));
|
|
15914
|
+
if (!state) throw new Error(`Could not load block ${definition.asset}`);
|
|
15915
|
+
object3 = await content.addBlock(state, { ...definition.options, initialState: definition.state, instanceMeshes: false });
|
|
15916
|
+
break;
|
|
15917
|
+
}
|
|
15918
|
+
case "item":
|
|
15919
|
+
case "model": {
|
|
15920
|
+
let key = AssetKey.parse("models", definition.asset);
|
|
15921
|
+
if (definition.type === "item" && key.type !== "item") {
|
|
15922
|
+
key = new AssetKey(key.namespace, key.getFullPath(), "models", "item");
|
|
15923
|
+
}
|
|
15924
|
+
const model = await Models.getMerged(key);
|
|
15925
|
+
if (!model) throw new Error(`Could not load ${definition.type} ${definition.asset}`);
|
|
15926
|
+
object3 = await content.addModel(model, {
|
|
15927
|
+
...definition.type === "item" ? { displayPosition: "gui" /* GUI */ } : {},
|
|
15928
|
+
...definition.options,
|
|
15929
|
+
instanceMeshes: false
|
|
15930
|
+
});
|
|
15931
|
+
break;
|
|
15932
|
+
}
|
|
15933
|
+
case "entity": {
|
|
15934
|
+
const key = AssetKey.parse("entity-models", definition.asset);
|
|
15935
|
+
const model = await Entities.getEntity(key, definition.texture ? AssetKey.parse("textures", definition.texture) : void 0, {
|
|
15936
|
+
layers: definition.layers,
|
|
15937
|
+
when: definition.when,
|
|
15938
|
+
textures: definition.textures && Object.fromEntries(Object.entries(definition.textures).map(([name, texture]) => [name, AssetKey.parse("textures", texture)]))
|
|
15939
|
+
});
|
|
15940
|
+
if (!model) throw new Error(`Could not load entity ${definition.asset}`);
|
|
15941
|
+
const entity = await content.addEntity(model, { ...definition.options, instanceMeshes: false });
|
|
15942
|
+
object3 = entity;
|
|
15943
|
+
if (definition.animation) {
|
|
15944
|
+
const animations = await Entities.getAnimations(key);
|
|
15945
|
+
const clip = animations?.[definition.animation.name];
|
|
15946
|
+
if (!clip) throw new Error(`Entity ${definition.asset} has no animation "${definition.animation.name}"`);
|
|
15947
|
+
entity.playAnimation(clip, definition.animation);
|
|
15948
|
+
}
|
|
15949
|
+
break;
|
|
15950
|
+
}
|
|
15951
|
+
case "gui":
|
|
15952
|
+
object3 = await content.initialize(new GuiObject(definition.layers));
|
|
15953
|
+
break;
|
|
15954
|
+
}
|
|
15955
|
+
let disposed = false;
|
|
15956
|
+
const loaded = {
|
|
15957
|
+
root,
|
|
15958
|
+
object: object3,
|
|
15959
|
+
definition,
|
|
15960
|
+
advanceAnimation: (delta) => {
|
|
15961
|
+
if (!disposed && definition.type === "entity" && !definition.animation?.paused) object3.advanceAnimation(delta);
|
|
15962
|
+
},
|
|
15963
|
+
dispose: () => {
|
|
15964
|
+
if (disposed) return;
|
|
15965
|
+
disposed = true;
|
|
15966
|
+
root.removeFromParent();
|
|
15967
|
+
object3.dispose();
|
|
15968
|
+
object3.removeFromParent();
|
|
15969
|
+
content.dispose();
|
|
15970
|
+
root.clear();
|
|
15971
|
+
scene.dirty = true;
|
|
15972
|
+
}
|
|
15973
|
+
};
|
|
15974
|
+
parent?.add(root);
|
|
15975
|
+
scene.dirty = true;
|
|
15976
|
+
return loaded;
|
|
15977
|
+
} catch (error) {
|
|
15978
|
+
object3?.dispose();
|
|
15979
|
+
object3?.removeFromParent();
|
|
15980
|
+
content.dispose();
|
|
15981
|
+
root.removeFromParent();
|
|
15982
|
+
root.clear();
|
|
15983
|
+
throw new Error(`Scene object "${definition.name ?? definition.id}": ${error instanceof Error ? error.message : String(error)}`);
|
|
15984
|
+
}
|
|
15985
|
+
}
|
|
15986
|
+
};
|
|
15987
|
+
|
|
14545
15988
|
// src/texture/index.ts
|
|
14546
15989
|
init_Colormaps();
|
|
14547
15990
|
init_TextureAtlas();
|
|
@@ -14568,16 +16011,19 @@ var ChunkData = class _ChunkData {
|
|
|
14568
16011
|
this.freeIds = [];
|
|
14569
16012
|
this.nbt = /* @__PURE__ */ new Map();
|
|
14570
16013
|
}
|
|
16014
|
+
/** Returns a detached copy of a cell, or `undefined` for air. Indices range from 0 to 4095. */
|
|
14571
16015
|
get(index) {
|
|
14572
16016
|
_ChunkData.validateIndex(index);
|
|
14573
16017
|
const entry = this.palette[this.indices[index]];
|
|
14574
16018
|
return entry ? _ChunkData.copyBlock(entry.state, this.nbt.get(index)) : void 0;
|
|
14575
16019
|
}
|
|
16020
|
+
/** Captures a cell's data and returns a copy-producing reader that survives later cell changes. */
|
|
14576
16021
|
snapshot(index) {
|
|
14577
16022
|
_ChunkData.validateIndex(index);
|
|
14578
16023
|
const entry = this.palette[this.indices[index]];
|
|
14579
16024
|
return entry ? _ChunkData.copyBlock.bind(void 0, entry.state, this.nbt.get(index)) : void 0;
|
|
14580
16025
|
}
|
|
16026
|
+
/** Copies block data into a cell. An omitted block or an air block clears the cell. */
|
|
14581
16027
|
set(index, block) {
|
|
14582
16028
|
_ChunkData.validateIndex(index);
|
|
14583
16029
|
let state;
|
|
@@ -14642,15 +16088,23 @@ var ChunkData = class _ChunkData {
|
|
|
14642
16088
|
|
|
14643
16089
|
// src/world/AnvilParser.ts
|
|
14644
16090
|
var AnvilParser = class {
|
|
16091
|
+
/** Lists occupied chunk positions within the region, using local coordinates from 0 to 31. */
|
|
14645
16092
|
static getChunkList(data) {
|
|
14646
16093
|
return this.locations(this.bytes(data)).map(({ x, z }) => ({ x, z }));
|
|
14647
16094
|
}
|
|
16095
|
+
/** Decodes all stored chunks. Gzip, zlib, and uncompressed payloads are supported. */
|
|
14648
16096
|
static async parse(data) {
|
|
14649
16097
|
const bytes = this.bytes(data);
|
|
14650
16098
|
const chunks = [];
|
|
14651
16099
|
for (const location of this.locations(bytes)) chunks.push(await this.readChunk(bytes, location));
|
|
14652
16100
|
return { chunks };
|
|
14653
16101
|
}
|
|
16102
|
+
/**
|
|
16103
|
+
* Decodes one chunk for {@link MineRenderWorld.placeChunk}.
|
|
16104
|
+
* @param localX - Chunk x within the region, from 0 to 31.
|
|
16105
|
+
* @param localZ - Chunk z within the region, from 0 to 31.
|
|
16106
|
+
* @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
|
|
16107
|
+
*/
|
|
14654
16108
|
static async parseChunk(data, localX, localZ) {
|
|
14655
16109
|
if (![localX, localZ].every((value) => Number.isInteger(value) && value >= 0 && value < 32)) {
|
|
14656
16110
|
throw new RangeError("Region-local chunk coordinates must be integers from 0 to 31");
|
|
@@ -14855,6 +16309,7 @@ var SectionModels = class {
|
|
|
14855
16309
|
this.templates = /* @__PURE__ */ new Set();
|
|
14856
16310
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
|
|
14857
16311
|
}
|
|
16312
|
+
/** Returns a cube template, or `undefined` when the block requires an individual render object. */
|
|
14858
16313
|
async get(blockState, properties = {}) {
|
|
14859
16314
|
if (!blockState.variants || blockState.multipart) return void 0;
|
|
14860
16315
|
let states = this.states.get(blockState);
|
|
@@ -14883,16 +16338,16 @@ var SectionModels = class {
|
|
|
14883
16338
|
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
14884
16339
|
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
14885
16340
|
const template = await cached(models, key, async () => {
|
|
14886
|
-
const
|
|
16341
|
+
const object3 = new SectionModel(model, {
|
|
14887
16342
|
instanceMeshes: false,
|
|
14888
16343
|
mergeMeshes: false,
|
|
14889
16344
|
tints,
|
|
14890
16345
|
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
14891
16346
|
});
|
|
14892
|
-
await
|
|
14893
|
-
const geometry =
|
|
16347
|
+
await object3.init();
|
|
16348
|
+
const geometry = object3.children[0].geometry;
|
|
14894
16349
|
geometry.applyMatrix4(new Matrix412().makeRotationFromEuler(rotation));
|
|
14895
|
-
|
|
16350
|
+
object3.clear();
|
|
14896
16351
|
const cullFaces = CUBE_FACES.map((face) => {
|
|
14897
16352
|
const local = CUBE_FACES.findIndex((name) => name === atlas.model.elements[0].faces[face]?.cullface);
|
|
14898
16353
|
if (local < 0) return 0;
|
|
@@ -14908,6 +16363,7 @@ var SectionModels = class {
|
|
|
14908
16363
|
}
|
|
14909
16364
|
return { variants, templates };
|
|
14910
16365
|
}
|
|
16366
|
+
/** Disposes prepared geometry and clears this world's template caches. */
|
|
14911
16367
|
clear() {
|
|
14912
16368
|
for (const template of this.templates) template.geometry.dispose();
|
|
14913
16369
|
this.templates.clear();
|
|
@@ -14958,6 +16414,13 @@ var MineRenderWorld = class {
|
|
|
14958
16414
|
this.validatePosBounds(posOrX);
|
|
14959
16415
|
await this.getChunkAt(posOrX)?.setBlockVisibleAt(posOrX, yOrVisible);
|
|
14960
16416
|
}
|
|
16417
|
+
/**
|
|
16418
|
+
* Places a structure's blocks at their stored positions, then refreshes neighboring faces.
|
|
16419
|
+
* Entity NBT is retained by the structure but is not rendered.
|
|
16420
|
+
*
|
|
16421
|
+
* @param useBatches - Uses bounded batches by default. Set to `false` for sequential placement.
|
|
16422
|
+
* @param executor - Optional queue for batched placement. The caller remains responsible for stopping it.
|
|
16423
|
+
*/
|
|
14961
16424
|
async placeMultiBlock(multiblock, useBatches = true, executor) {
|
|
14962
16425
|
const changes = /* @__PURE__ */ new Map();
|
|
14963
16426
|
try {
|
|
@@ -15009,13 +16472,7 @@ var MineRenderWorld = class {
|
|
|
15009
16472
|
async placeChunk(chunk, executor) {
|
|
15010
16473
|
const changes = /* @__PURE__ */ new Map();
|
|
15011
16474
|
try {
|
|
15012
|
-
|
|
15013
|
-
for (const [key, section] of previous) {
|
|
15014
|
-
this._chunks.delete(key);
|
|
15015
|
-
await section.clear(async (positions) => {
|
|
15016
|
-
for (const pos of positions) changes.set(pos.toArray().join(","), pos);
|
|
15017
|
-
});
|
|
15018
|
-
}
|
|
16475
|
+
await this.clearChunkColumn(chunk.x, chunk.z, changes);
|
|
15019
16476
|
for (const section of chunk.sections) {
|
|
15020
16477
|
const blocks = [];
|
|
15021
16478
|
for (let index = 0; index < 4096; index++) {
|
|
@@ -15035,6 +16492,32 @@ var MineRenderWorld = class {
|
|
|
15035
16492
|
await this.updateCulling([...changes.values()]);
|
|
15036
16493
|
}
|
|
15037
16494
|
}
|
|
16495
|
+
/**
|
|
16496
|
+
* Removes all sections at integer chunk-column coordinates, then refreshes neighboring faces and fluids.
|
|
16497
|
+
* Coordinates match AnvilChunk.x/z, not block positions. Missing columns are ignored.
|
|
16498
|
+
*/
|
|
16499
|
+
async unloadChunkColumn(x, z) {
|
|
16500
|
+
if (!Number.isInteger(x) || !Number.isInteger(z)) {
|
|
16501
|
+
throw new RangeError("Chunk column coordinates must be integers");
|
|
16502
|
+
}
|
|
16503
|
+
await this.culling;
|
|
16504
|
+
const changes = /* @__PURE__ */ new Map();
|
|
16505
|
+
try {
|
|
16506
|
+
await this.clearChunkColumn(x, z, changes);
|
|
16507
|
+
} finally {
|
|
16508
|
+
await this.updateCulling([...changes.values()]);
|
|
16509
|
+
}
|
|
16510
|
+
}
|
|
16511
|
+
async clearChunkColumn(x, z, changes) {
|
|
16512
|
+
const previous = [...this._chunks.entries()].filter(([, section]) => section.x === x && section.z === z);
|
|
16513
|
+
for (const [key, section] of previous) {
|
|
16514
|
+
this._chunks.delete(key);
|
|
16515
|
+
await section.clear(async (positions) => {
|
|
16516
|
+
for (const pos of positions) changes.set(pos.toArray().join(","), pos);
|
|
16517
|
+
});
|
|
16518
|
+
}
|
|
16519
|
+
}
|
|
16520
|
+
/** Removes all chunks and releases their block placements and section meshes. */
|
|
15038
16521
|
async clear() {
|
|
15039
16522
|
await this.culling;
|
|
15040
16523
|
const chunks = [...this._chunks.values()];
|
|
@@ -15080,10 +16563,10 @@ var MineRenderWorld = class {
|
|
|
15080
16563
|
await block.object.updateFluid((x, y, z) => {
|
|
15081
16564
|
const neighbor = pos.clone().add(new Vector316(x, y, z));
|
|
15082
16565
|
const section = this.getChunkAt(neighbor);
|
|
15083
|
-
const
|
|
16566
|
+
const object3 = section?.isBlockVisibleAt(neighbor) ? section.getBlockAt(neighbor)?.object : void 0;
|
|
15084
16567
|
return {
|
|
15085
|
-
fluid:
|
|
15086
|
-
level:
|
|
16568
|
+
fluid: object3?.fluidKind,
|
|
16569
|
+
level: object3?.fluidLevel,
|
|
15087
16570
|
solid: section?.isOccludingAt(neighbor) ?? false
|
|
15088
16571
|
};
|
|
15089
16572
|
});
|
|
@@ -15120,9 +16603,11 @@ var MineRenderWorld = class {
|
|
|
15120
16603
|
}
|
|
15121
16604
|
return chunk;
|
|
15122
16605
|
}
|
|
16606
|
+
/** Returns the loaded chunk containing a world block position, without creating a chunk. */
|
|
15123
16607
|
getChunkAt(pos) {
|
|
15124
16608
|
return this._chunks.get(this.worldPosToChunkKey(pos));
|
|
15125
16609
|
}
|
|
16610
|
+
/** Converts block coordinates to scene units by multiplying each component by 16. */
|
|
15126
16611
|
static worldToScenePosition(pos) {
|
|
15127
16612
|
return new Vector316(
|
|
15128
16613
|
pos.x * 16,
|
|
@@ -15130,6 +16615,7 @@ var MineRenderWorld = class {
|
|
|
15130
16615
|
pos.z * 16
|
|
15131
16616
|
);
|
|
15132
16617
|
}
|
|
16618
|
+
/** Converts scene units to block coordinates without rounding to an integer block. */
|
|
15133
16619
|
static sceneToWorldPosition(pos) {
|
|
15134
16620
|
return new Vector316(
|
|
15135
16621
|
pos.x / 16,
|
|
@@ -15156,12 +16642,13 @@ init_Model();
|
|
|
15156
16642
|
// src/world/SectionMesh.ts
|
|
15157
16643
|
init_CanvasCompat();
|
|
15158
16644
|
init_Materials();
|
|
15159
|
-
import { BufferGeometry as BufferGeometry8, Float32BufferAttribute as Float32BufferAttribute8, Group as
|
|
15160
|
-
var SectionMesh = class _SectionMesh extends
|
|
16645
|
+
import { BufferGeometry as BufferGeometry8, Float32BufferAttribute as Float32BufferAttribute8, Group as Group3, Mesh as Mesh9 } from "three";
|
|
16646
|
+
var SectionMesh = class _SectionMesh extends Group3 {
|
|
15161
16647
|
constructor() {
|
|
15162
16648
|
super(...arguments);
|
|
15163
16649
|
this.ownedMeshes = [];
|
|
15164
16650
|
}
|
|
16651
|
+
/** Builds section-local meshes from visible cube faces. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
15165
16652
|
static build(entries, maxAtlasSize = 2048) {
|
|
15166
16653
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
15167
16654
|
const visible = entries.filter((entry) => entry.template.cullFaces.some((direction) => !(entry.cullMask & direction)));
|
|
@@ -15203,14 +16690,17 @@ var SectionMesh = class _SectionMesh extends Group2 {
|
|
|
15203
16690
|
for (const { atlas, x, y } of page.placements) {
|
|
15204
16691
|
context.drawImage(atlas.image.canvas, x, y);
|
|
15205
16692
|
}
|
|
15206
|
-
const positions = [], normals = [], uvs = [], colors = [], indices = [];
|
|
16693
|
+
const positions = [], normals = [], uvs = [], uvBounds = [], colors = [], indices = [];
|
|
15207
16694
|
for (const { index, template, cullMask } of page.entries) {
|
|
15208
16695
|
const position = template.geometry.getAttribute("position");
|
|
15209
16696
|
const normal = template.geometry.getAttribute("normal");
|
|
15210
16697
|
const uv = template.geometry.getAttribute("uv");
|
|
15211
|
-
const
|
|
16698
|
+
const bounds = template.geometry.getAttribute("uvBounds");
|
|
16699
|
+
const color2 = template.geometry.getAttribute("color");
|
|
15212
16700
|
const sourceIndices = template.geometry.getIndex();
|
|
15213
16701
|
const placement = locations.get(template.atlas).placement;
|
|
16702
|
+
const mapU = (u) => (placement.x + u * template.atlas.image.width) / page.width;
|
|
16703
|
+
const mapV = (v) => 1 - (placement.y + (1 - v) * template.atlas.image.height) / page.height;
|
|
15214
16704
|
const offsetX = index % 16 * 16;
|
|
15215
16705
|
const offsetY = Math.floor(index / 256) * 16;
|
|
15216
16706
|
const offsetZ = Math.floor(index / 16) % 16 * 16;
|
|
@@ -15221,11 +16711,14 @@ var SectionMesh = class _SectionMesh extends Group2 {
|
|
|
15221
16711
|
const vertex = face * 4 + corner;
|
|
15222
16712
|
positions.push(position.getX(vertex) + offsetX, position.getY(vertex) + offsetY, position.getZ(vertex) + offsetZ);
|
|
15223
16713
|
normals.push(normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex));
|
|
15224
|
-
uvs.push(
|
|
15225
|
-
|
|
15226
|
-
|
|
16714
|
+
uvs.push(mapU(uv.getX(vertex)), mapV(uv.getY(vertex)));
|
|
16715
|
+
uvBounds.push(
|
|
16716
|
+
mapU(bounds?.getX(vertex) ?? 0),
|
|
16717
|
+
mapV(bounds?.getY(vertex) ?? 0),
|
|
16718
|
+
mapU(bounds?.getZ(vertex) ?? 1),
|
|
16719
|
+
mapV(bounds?.getW(vertex) ?? 1)
|
|
15227
16720
|
);
|
|
15228
|
-
colors.push(
|
|
16721
|
+
colors.push(color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1);
|
|
15229
16722
|
}
|
|
15230
16723
|
for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
|
|
15231
16724
|
}
|
|
@@ -15234,11 +16727,12 @@ var SectionMesh = class _SectionMesh extends Group2 {
|
|
|
15234
16727
|
geometry.setAttribute("position", new Float32BufferAttribute8(positions, 3));
|
|
15235
16728
|
geometry.setAttribute("normal", new Float32BufferAttribute8(normals, 3));
|
|
15236
16729
|
geometry.setAttribute("uv", new Float32BufferAttribute8(uvs, 2));
|
|
16730
|
+
geometry.setAttribute("uvBounds", new Float32BufferAttribute8(uvBounds, 4));
|
|
15237
16731
|
geometry.setAttribute("color", new Float32BufferAttribute8(colors, 3));
|
|
15238
16732
|
geometry.setIndex(indices);
|
|
15239
16733
|
geometry.computeBoundingBox();
|
|
15240
16734
|
geometry.computeBoundingSphere();
|
|
15241
|
-
const material = Materials.createShadedCanvasMaterial(canvas, false);
|
|
16735
|
+
const material = Materials.createShadedCanvasMaterial(canvas, false, false, true);
|
|
15242
16736
|
material.vertexColors = true;
|
|
15243
16737
|
const texture = material.uniforms?.map?.value ?? material.map;
|
|
15244
16738
|
const mesh = new Mesh9(geometry, material);
|
|
@@ -15299,7 +16793,7 @@ var Chunk = class _Chunk {
|
|
|
15299
16793
|
return this.setBlockInChunkAt(pos, block, worldPos);
|
|
15300
16794
|
}
|
|
15301
16795
|
/**
|
|
15302
|
-
*
|
|
16796
|
+
* Places a block at integer chunk-local coordinates from 0 to 15.
|
|
15303
16797
|
*/
|
|
15304
16798
|
async setBlockInChunkAt(pos, block, worldPos, onBlocksChanged = this.onBlocksChanged) {
|
|
15305
16799
|
if (typeof worldPos === "undefined") {
|
|
@@ -15312,7 +16806,7 @@ var Chunk = class _Chunk {
|
|
|
15312
16806
|
this.renderedBlocks.get(index)?.object?.removeFromScene();
|
|
15313
16807
|
if (this.sectionBlocks.delete(index)) this.meshDirty = true;
|
|
15314
16808
|
this.renderedBlocks.delete(index);
|
|
15315
|
-
let
|
|
16809
|
+
let object3;
|
|
15316
16810
|
try {
|
|
15317
16811
|
if (!readBlock) return void 0;
|
|
15318
16812
|
const stored = readBlock();
|
|
@@ -15327,27 +16821,28 @@ var Chunk = class _Chunk {
|
|
|
15327
16821
|
this.sectionBlocks.set(index, { index, template, cullMask: 0 });
|
|
15328
16822
|
this.meshDirty = true;
|
|
15329
16823
|
} else {
|
|
15330
|
-
|
|
16824
|
+
object3 = await this.scene.addBlock(blockState, {
|
|
15331
16825
|
mergeMeshes: true,
|
|
15332
16826
|
instanceMeshes: true,
|
|
15333
16827
|
maxInstanceCount: 2e3,
|
|
15334
16828
|
initialState: stored.properties
|
|
15335
16829
|
});
|
|
15336
|
-
|
|
16830
|
+
object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
15337
16831
|
}
|
|
15338
16832
|
const info = { get block() {
|
|
15339
16833
|
return readBlock();
|
|
15340
|
-
}, object };
|
|
16834
|
+
}, object: object3 };
|
|
15341
16835
|
this.renderedBlocks.set(index, info);
|
|
15342
16836
|
return info;
|
|
15343
16837
|
} catch (error) {
|
|
15344
16838
|
this.data.set(index, void 0);
|
|
15345
|
-
|
|
16839
|
+
object3?.removeFromScene();
|
|
15346
16840
|
throw error;
|
|
15347
16841
|
} finally {
|
|
15348
16842
|
await onBlocksChanged?.([worldPos]);
|
|
15349
16843
|
}
|
|
15350
16844
|
}
|
|
16845
|
+
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
15351
16846
|
async setBlockVisibleAt(pos, visible) {
|
|
15352
16847
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
15353
16848
|
const block = this.renderedBlocks.get(index);
|
|
@@ -15401,6 +16896,7 @@ var Chunk = class _Chunk {
|
|
|
15401
16896
|
this.scene.dirty = true;
|
|
15402
16897
|
this.meshDirty = false;
|
|
15403
16898
|
}
|
|
16899
|
+
/** Removes stored blocks and their render objects, then notifies the owning world of changed positions. */
|
|
15404
16900
|
async clear(onBlocksChanged = this.onBlocksChanged) {
|
|
15405
16901
|
const positions = [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
|
|
15406
16902
|
new Vector317(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
|
|
@@ -15421,6 +16917,7 @@ var Chunk = class _Chunk {
|
|
|
15421
16917
|
static isAir(block) {
|
|
15422
16918
|
return ChunkData.isAir(block);
|
|
15423
16919
|
}
|
|
16920
|
+
/** Maps chunk-local coordinates from 0 to 15 to a storage index, with x varying fastest, then z, then y. */
|
|
15424
16921
|
static chunkPosToBlockIndex(pos) {
|
|
15425
16922
|
if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
|
|
15426
16923
|
throw new RangeError("Block coordinates must be integers from 0 to 15 within a chunk");
|
|
@@ -15455,6 +16952,7 @@ export {
|
|
|
15455
16952
|
Axis,
|
|
15456
16953
|
BUILTIN_ENTITY,
|
|
15457
16954
|
BUILTIN_GENERATED,
|
|
16955
|
+
BannerPatterns,
|
|
15458
16956
|
BasicAssetKey,
|
|
15459
16957
|
BatchedExecutor,
|
|
15460
16958
|
BlockEntities,
|
|
@@ -15479,6 +16977,7 @@ export {
|
|
|
15479
16977
|
DEFAULT_ROOT,
|
|
15480
16978
|
DEFAULT_UV,
|
|
15481
16979
|
DISPLAY_POSITIONS,
|
|
16980
|
+
DYE_COLORS,
|
|
15482
16981
|
DefaultBlockStates,
|
|
15483
16982
|
DisplayPosition,
|
|
15484
16983
|
DisplayTransforms,
|
|
@@ -15489,6 +16988,7 @@ export {
|
|
|
15489
16988
|
Env,
|
|
15490
16989
|
FluidModelObject,
|
|
15491
16990
|
Fonts,
|
|
16991
|
+
GUI_CONTAINER_LAYOUTS,
|
|
15492
16992
|
Geometries,
|
|
15493
16993
|
GuiHelper,
|
|
15494
16994
|
GuiLight,
|
|
@@ -15515,11 +17015,14 @@ export {
|
|
|
15515
17015
|
Models,
|
|
15516
17016
|
NBTHelper,
|
|
15517
17017
|
OrbitControls,
|
|
17018
|
+
PackFormats,
|
|
17019
|
+
PackMetadata,
|
|
15518
17020
|
PersistentCache,
|
|
15519
17021
|
Renderer,
|
|
15520
17022
|
RequestError,
|
|
15521
17023
|
Requests,
|
|
15522
17024
|
SKIN_PARTS,
|
|
17025
|
+
SceneDocumentLoader,
|
|
15523
17026
|
SceneExporter,
|
|
15524
17027
|
SceneInspector,
|
|
15525
17028
|
SceneObject,
|
|
@@ -15595,6 +17098,7 @@ export {
|
|
|
15595
17098
|
mergeBufferAttributes,
|
|
15596
17099
|
mergeBufferGeometries,
|
|
15597
17100
|
mergeVertices,
|
|
17101
|
+
parseSceneDocument,
|
|
15598
17102
|
prefix,
|
|
15599
17103
|
probeImageSize,
|
|
15600
17104
|
registerBrowserEnv,
|