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/node/index.mjs
CHANGED
|
@@ -68,6 +68,7 @@ var init_PersistentCache = __esm({
|
|
|
68
68
|
/** Bump to invalidate every persisted entry. */
|
|
69
69
|
this.VERSION = 2;
|
|
70
70
|
}
|
|
71
|
+
/** Opens a named store in the active environment using the current cache schema version. */
|
|
71
72
|
static open(name) {
|
|
72
73
|
return Env.provider.openCache(name, _PersistentCache.VERSION);
|
|
73
74
|
}
|
|
@@ -299,6 +300,9 @@ var init_Caching = __esm({
|
|
|
299
300
|
static {
|
|
300
301
|
this.fontCache = _Caching.createAssetCache();
|
|
301
302
|
}
|
|
303
|
+
static {
|
|
304
|
+
this.bannerPatternCache = _Caching.createAssetCache();
|
|
305
|
+
}
|
|
302
306
|
static createAssetCache() {
|
|
303
307
|
return Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
|
|
304
308
|
}
|
|
@@ -326,9 +330,11 @@ var init_Caching = __esm({
|
|
|
326
330
|
this.entityTextureCache,
|
|
327
331
|
this.entityAnimationCache,
|
|
328
332
|
this.blockEntityIndexCache,
|
|
329
|
-
this.fontCache
|
|
333
|
+
this.fontCache,
|
|
334
|
+
this.bannerPatternCache
|
|
330
335
|
];
|
|
331
336
|
}
|
|
337
|
+
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
332
338
|
static clear() {
|
|
333
339
|
for (const cache of this.all) {
|
|
334
340
|
cache.invalidateAll();
|
|
@@ -371,6 +377,7 @@ var init_Geometries = __esm({
|
|
|
371
377
|
}
|
|
372
378
|
return geometry;
|
|
373
379
|
}
|
|
380
|
+
/** Returns shared geometry. Use {@link createBox} when the caller needs to modify or dispose it independently. */
|
|
374
381
|
static getBox(key) {
|
|
375
382
|
const keyStr = serializeBoxGeometryKey(key);
|
|
376
383
|
return Caching.boxGeometryCache.get(keyStr, (k) => {
|
|
@@ -554,12 +561,15 @@ var init_Requests = __esm({
|
|
|
554
561
|
static {
|
|
555
562
|
this.mcAssetRequestQueue = new RequestQueue();
|
|
556
563
|
}
|
|
564
|
+
/** Queues a general request. Set `request.signal` to cancel it and `request.timeout` to limit each attempt. */
|
|
557
565
|
static genericRequest(request) {
|
|
558
566
|
return this.genericQueue.request(request);
|
|
559
567
|
}
|
|
568
|
+
/** Queues a request separately from general traffic, using the configured asset base URL for relative paths. */
|
|
560
569
|
static mcAssetRequest(request) {
|
|
561
570
|
return this.mcAssetRequestQueue.request(request);
|
|
562
571
|
}
|
|
572
|
+
/** Sets the base URL for relative asset requests. Use {@link AssetLoader.setVersion} to change Minecraft versions. */
|
|
563
573
|
static setMcAssetRoot(root) {
|
|
564
574
|
this.mcAssetRequestQueue.baseURL = root;
|
|
565
575
|
}
|
|
@@ -612,6 +622,7 @@ var init_WrappedImage = __esm({
|
|
|
612
622
|
}
|
|
613
623
|
return false;
|
|
614
624
|
}
|
|
625
|
+
/** Whether the dimensions form a vertical strip of square frames, without consulting `.mcmeta`. */
|
|
615
626
|
get animated() {
|
|
616
627
|
return this.height > this.width && this.height % this.width === 0;
|
|
617
628
|
}
|
|
@@ -658,6 +669,7 @@ var init_ImageLoader = __esm({
|
|
|
658
669
|
static _createImage() {
|
|
659
670
|
return createImage();
|
|
660
671
|
}
|
|
672
|
+
/** Resolves when an image URL has decoded, or rejects if decoding fails. */
|
|
661
673
|
static async loadAsync(src) {
|
|
662
674
|
return new Promise((resolve, reject) => {
|
|
663
675
|
const image = this._createImage();
|
|
@@ -666,6 +678,7 @@ var init_ImageLoader = __esm({
|
|
|
666
678
|
image.src = src;
|
|
667
679
|
});
|
|
668
680
|
}
|
|
681
|
+
/** Returns an image immediately. Use the callbacks to observe loading, or await {@link loadAsync}. */
|
|
669
682
|
static loadElement(src, onload, onerr) {
|
|
670
683
|
const image = this._createImage();
|
|
671
684
|
if (onload)
|
|
@@ -693,6 +706,7 @@ var init_ImageLoader = __esm({
|
|
|
693
706
|
throw err;
|
|
694
707
|
}
|
|
695
708
|
}
|
|
709
|
+
/** Decodes the supplied bytes into a canvas without fetching the source URL again. */
|
|
696
710
|
static async infoToCanvasData(info) {
|
|
697
711
|
const type = info.type === "jpg" ? "jpeg" : info.type ?? "png";
|
|
698
712
|
const image = await _ImageLoader.loadAsync(`data:image/${type};base64,${info.data.toString("base64")}`);
|
|
@@ -709,12 +723,14 @@ var init_ImageLoader = __esm({
|
|
|
709
723
|
const image = await this.infoToCanvasData(info);
|
|
710
724
|
return image.data.getImageData(0, 0, image.width, image.height);
|
|
711
725
|
}
|
|
726
|
+
/** Returns cached RGBA pixels for an image URL or data URL. Failed loads can be retried. */
|
|
712
727
|
static async getData(src) {
|
|
713
728
|
const keyStr = serializeImageKey({ src });
|
|
714
729
|
return await Caching.imageDataCache.get(keyStr, (k) => {
|
|
715
730
|
return _ImageLoader.loadData(src);
|
|
716
731
|
});
|
|
717
732
|
}
|
|
733
|
+
/** Returns a cached canvas context and dimensions for an image URL or data URL. */
|
|
718
734
|
static async getCanvasData(src) {
|
|
719
735
|
const keyStr = serializeImageKey({ src });
|
|
720
736
|
return await Caching.canvasImageDataCache.get(keyStr, (k) => {
|
|
@@ -800,7 +816,7 @@ var init_TextureLoader = __esm({
|
|
|
800
816
|
static createTexture() {
|
|
801
817
|
return new Texture();
|
|
802
818
|
}
|
|
803
|
-
static loadInBackground(src,
|
|
819
|
+
static loadInBackground(src, format2 = RGBAFormat, rotation = 0) {
|
|
804
820
|
const texture = this.createTexture();
|
|
805
821
|
texture.colorSpace = THREE.SRGBColorSpace;
|
|
806
822
|
const image = ImageLoader.loadElement(src, () => {
|
|
@@ -810,11 +826,12 @@ var init_TextureLoader = __esm({
|
|
|
810
826
|
console.warn("Failed to load texture", src, error);
|
|
811
827
|
});
|
|
812
828
|
texture.image = image;
|
|
813
|
-
texture.format =
|
|
829
|
+
texture.format = format2;
|
|
814
830
|
texture.rotation = rotation;
|
|
815
831
|
return Textures.initTextureProps(texture);
|
|
816
832
|
}
|
|
817
|
-
|
|
833
|
+
/** Loads image pixels in the background. `rotation` is in radians. Check {@link hasFailed} for load failures. */
|
|
834
|
+
static load(src, format2 = RGBAFormat, rotation = 0) {
|
|
818
835
|
const texture = new Texture();
|
|
819
836
|
texture.colorSpace = THREE.SRGBColorSpace;
|
|
820
837
|
ImageLoader.getData(src).then((image) => {
|
|
@@ -824,7 +841,7 @@ var init_TextureLoader = __esm({
|
|
|
824
841
|
this.failedTextures.add(texture);
|
|
825
842
|
console.warn("Failed to load texture", src, error);
|
|
826
843
|
});
|
|
827
|
-
texture.format =
|
|
844
|
+
texture.format = format2;
|
|
828
845
|
texture.rotation = rotation;
|
|
829
846
|
return Textures.initTextureProps(texture);
|
|
830
847
|
}
|
|
@@ -850,8 +867,8 @@ var init_Textures = __esm({
|
|
|
850
867
|
for (let y = 0; y < size; y++) {
|
|
851
868
|
for (let x = 0; x < size; x++) {
|
|
852
869
|
const offset = (y * size + x) * 4;
|
|
853
|
-
const
|
|
854
|
-
data[offset] = data[offset + 2] =
|
|
870
|
+
const color2 = (x + y) % 2 === 0 ? 255 : 0;
|
|
871
|
+
data[offset] = data[offset + 2] = color2;
|
|
855
872
|
data[offset + 3] = 255;
|
|
856
873
|
}
|
|
857
874
|
}
|
|
@@ -862,6 +879,7 @@ var init_Textures = __esm({
|
|
|
862
879
|
return this.initTextureProps(texture);
|
|
863
880
|
});
|
|
864
881
|
}
|
|
882
|
+
/** Sets nearest-neighbor filters without changing the texture's color space. Returns the same texture. */
|
|
865
883
|
static initTextureProps(texture) {
|
|
866
884
|
texture.magFilter = THREE2.NearestFilter;
|
|
867
885
|
texture.minFilter = THREE2.NearestFilter;
|
|
@@ -874,11 +892,13 @@ var init_Textures = __esm({
|
|
|
874
892
|
static createAsset(key) {
|
|
875
893
|
return new Texture2();
|
|
876
894
|
}
|
|
895
|
+
/** Creates an sRGB color texture from a canvas. */
|
|
877
896
|
static createCanvasTexture(canvas) {
|
|
878
897
|
const texture = new CanvasTexture(canvas);
|
|
879
898
|
texture.colorSpace = THREE2.SRGBColorSpace;
|
|
880
899
|
return this.initTextureProps(texture);
|
|
881
900
|
}
|
|
901
|
+
/** Returns a shared texture immediately and loads its image asynchronously. Failed cached loads are retried. */
|
|
882
902
|
static getImage(key) {
|
|
883
903
|
const keyStr = serializeTextureKey(key);
|
|
884
904
|
const cached2 = Caching.textureCache.peek(keyStr);
|
|
@@ -903,6 +923,7 @@ var init_Materials = __esm({
|
|
|
903
923
|
init_TextureLoader();
|
|
904
924
|
init_Caching();
|
|
905
925
|
Materials = class _Materials {
|
|
926
|
+
/** Shared checkerboard material, created on first access. */
|
|
906
927
|
static get MISSING_TEXTURE() {
|
|
907
928
|
return Caching.materialCache.get("builtin:missing-texture", () => new MeshBasicMaterial({
|
|
908
929
|
map: Textures.getMissing(),
|
|
@@ -974,6 +995,7 @@ var init_Materials = __esm({
|
|
|
974
995
|
material.depthWrite = false;
|
|
975
996
|
break;
|
|
976
997
|
case "eyes":
|
|
998
|
+
case "no_outline":
|
|
977
999
|
material.transparent = true;
|
|
978
1000
|
material.alphaTest = 0;
|
|
979
1001
|
material.depthWrite = false;
|
|
@@ -1006,6 +1028,7 @@ var init_Materials = __esm({
|
|
|
1006
1028
|
if (tint !== void 0) material.color.multiply(new Color(tint));
|
|
1007
1029
|
return material;
|
|
1008
1030
|
}
|
|
1031
|
+
/** Creates an unlit, transparent GUI material without depth writes or tone mapping. */
|
|
1009
1032
|
static createGuiCanvasMaterial(canvas) {
|
|
1010
1033
|
const material = new MeshBasicMaterial({
|
|
1011
1034
|
map: Textures.createCanvasTexture(canvas),
|
|
@@ -1031,7 +1054,7 @@ var init_Materials = __esm({
|
|
|
1031
1054
|
};
|
|
1032
1055
|
return material;
|
|
1033
1056
|
}
|
|
1034
|
-
static createShadedCanvasMaterial(canvas, transparent = false, shade = false) {
|
|
1057
|
+
static createShadedCanvasMaterial(canvas, transparent = false, shade = false, boundUvs = false) {
|
|
1035
1058
|
const vertShader = `
|
|
1036
1059
|
uniform bool SHADE;
|
|
1037
1060
|
|
|
@@ -1041,6 +1064,10 @@ var init_Materials = __esm({
|
|
|
1041
1064
|
#else
|
|
1042
1065
|
varying vec2 vUv;
|
|
1043
1066
|
#endif
|
|
1067
|
+
#ifdef BOUND_UVS
|
|
1068
|
+
attribute vec4 uvBounds;
|
|
1069
|
+
flat out vec4 vUvBounds;
|
|
1070
|
+
#endif
|
|
1044
1071
|
varying vec3 vTint;
|
|
1045
1072
|
varying float light;
|
|
1046
1073
|
varying float lift;
|
|
@@ -1076,13 +1103,17 @@ var init_Materials = __esm({
|
|
|
1076
1103
|
}
|
|
1077
1104
|
|
|
1078
1105
|
vUv = uv;
|
|
1106
|
+
#ifdef BOUND_UVS
|
|
1107
|
+
vUvBounds = uvBounds;
|
|
1108
|
+
#endif
|
|
1079
1109
|
vTint = color;
|
|
1080
1110
|
|
|
1111
|
+
vec4 mvPosition = vec4(position, 1.0);
|
|
1081
1112
|
#ifdef USE_INSTANCING
|
|
1082
|
-
|
|
1083
|
-
#else
|
|
1084
|
-
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
|
1113
|
+
mvPosition = instanceMatrix * mvPosition;
|
|
1085
1114
|
#endif
|
|
1115
|
+
mvPosition = modelViewMatrix * mvPosition;
|
|
1116
|
+
gl_Position = projectionMatrix * mvPosition;
|
|
1086
1117
|
|
|
1087
1118
|
}
|
|
1088
1119
|
`;
|
|
@@ -1102,13 +1133,22 @@ var init_Materials = __esm({
|
|
|
1102
1133
|
#else
|
|
1103
1134
|
varying vec2 vUv;
|
|
1104
1135
|
#endif
|
|
1136
|
+
#ifdef BOUND_UVS
|
|
1137
|
+
flat in vec4 vUvBounds;
|
|
1138
|
+
#endif
|
|
1105
1139
|
varying vec3 vTint;
|
|
1106
1140
|
varying float light;
|
|
1107
1141
|
varying float lift;
|
|
1108
1142
|
|
|
1109
1143
|
void main(void)
|
|
1110
1144
|
{
|
|
1111
|
-
|
|
1145
|
+
#ifdef BOUND_UVS
|
|
1146
|
+
// Keep edge samples inside their texture even without centroid interpolation.
|
|
1147
|
+
vec2 mapUv = clamp(vUv, vUvBounds.xy, vUvBounds.zw);
|
|
1148
|
+
#else
|
|
1149
|
+
vec2 mapUv = vUv;
|
|
1150
|
+
#endif
|
|
1151
|
+
vec4 color = texture2D(map, mapUv);
|
|
1112
1152
|
color.rgb *= vTint;
|
|
1113
1153
|
|
|
1114
1154
|
if (color.a < 0.01) discard;
|
|
@@ -1129,7 +1169,8 @@ var init_Materials = __esm({
|
|
|
1129
1169
|
}
|
|
1130
1170
|
`;
|
|
1131
1171
|
try {
|
|
1132
|
-
|
|
1172
|
+
const material = new ShaderMaterial({
|
|
1173
|
+
defines: boundUvs ? { BOUND_UVS: true } : {},
|
|
1133
1174
|
uniforms: {
|
|
1134
1175
|
SHADE: { value: true },
|
|
1135
1176
|
BRIGHTNESS: { value: 1 },
|
|
@@ -1147,11 +1188,14 @@ var init_Materials = __esm({
|
|
|
1147
1188
|
side: transparent ? DoubleSide : FrontSide,
|
|
1148
1189
|
alphaTest: 0.5
|
|
1149
1190
|
});
|
|
1191
|
+
Object.assign(material.defaultAttributeValues, { uvBounds: [0, 0, 1, 1] });
|
|
1192
|
+
return material;
|
|
1150
1193
|
} catch (e) {
|
|
1151
1194
|
console.warn(e);
|
|
1152
1195
|
}
|
|
1153
1196
|
return this.createBasicCanvasMaterial(canvas, transparent, shade);
|
|
1154
1197
|
}
|
|
1198
|
+
/** Returns a shared image material. Use {@link createImage} for a separate material instance. */
|
|
1155
1199
|
static getImage(key) {
|
|
1156
1200
|
const keyStr = serializeMaterialKey(key);
|
|
1157
1201
|
const map = Caching.materialCache.peek(keyStr)?.map;
|
|
@@ -1326,6 +1370,14 @@ var init_AssetKey = __esm({
|
|
|
1326
1370
|
}
|
|
1327
1371
|
};
|
|
1328
1372
|
AssetKey = class _AssetKey extends BasicAssetKey {
|
|
1373
|
+
/**
|
|
1374
|
+
* Creates a key from separate path components. Use {@link parse} for a combined reference.
|
|
1375
|
+
*
|
|
1376
|
+
* @param assetType - Asset directory, such as `models`, `textures`, or `blockstates`.
|
|
1377
|
+
* @param type - Subdirectory, such as `block` or `item`.
|
|
1378
|
+
* @param rootType - Top-level directory, usually `assets` or `data`.
|
|
1379
|
+
* @param root - Base URL override for hosted asset sources.
|
|
1380
|
+
*/
|
|
1329
1381
|
constructor(namespace, path, assetType, type, rootType = "assets", extension = ".json", root) {
|
|
1330
1382
|
super(namespace, path);
|
|
1331
1383
|
this.namespace = namespace;
|
|
@@ -1337,6 +1389,16 @@ var init_AssetKey = __esm({
|
|
|
1337
1389
|
this.root = root;
|
|
1338
1390
|
this.isAssetKey = true;
|
|
1339
1391
|
}
|
|
1392
|
+
/**
|
|
1393
|
+
* Parses an asset reference such as `minecraft:block/stone`.
|
|
1394
|
+
* A missing namespace defaults to the origin's namespace or `minecraft`.
|
|
1395
|
+
*
|
|
1396
|
+
* @param assetType - Asset category, such as `models` or `textures`.
|
|
1397
|
+
* @param str - Path with an optional namespace and file extension.
|
|
1398
|
+
* @param origin - Supplies the asset root, default namespace, and non-texture extension.
|
|
1399
|
+
* @returns A key with the matching extension removed from its path. Textures use `.png`;
|
|
1400
|
+
* other assets use the origin's extension or `.json`.
|
|
1401
|
+
*/
|
|
1340
1402
|
static parse(assetType, str, origin) {
|
|
1341
1403
|
let namespace = origin?.namespace || DEFAULT_NAMESPACE;
|
|
1342
1404
|
if (str.includes(":")) {
|
|
@@ -1368,6 +1430,7 @@ var init_AssetKey = __esm({
|
|
|
1368
1430
|
];
|
|
1369
1431
|
return a.join("/");
|
|
1370
1432
|
}
|
|
1433
|
+
/** Joins the type subdirectory and path without adding a namespace or file extension. */
|
|
1371
1434
|
getFullPath() {
|
|
1372
1435
|
let p10 = [];
|
|
1373
1436
|
if (this.type) {
|
|
@@ -1379,6 +1442,7 @@ var init_AssetKey = __esm({
|
|
|
1379
1442
|
toNamespacedString() {
|
|
1380
1443
|
return this.namespace + ":" + this.getFullPath();
|
|
1381
1444
|
}
|
|
1445
|
+
/** Returns a cache identifier that includes the explicit root or {@link AssetLoader.ROOT}. */
|
|
1382
1446
|
serialize() {
|
|
1383
1447
|
return this.toString();
|
|
1384
1448
|
}
|
|
@@ -1390,17 +1454,17 @@ var init_AssetKey = __esm({
|
|
|
1390
1454
|
var DisplayPosition, DISPLAY_POSITIONS;
|
|
1391
1455
|
var init_DisplayPosition = __esm({
|
|
1392
1456
|
"src/model/DisplayPosition.ts"() {
|
|
1393
|
-
DisplayPosition = /* @__PURE__ */ ((
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
return
|
|
1457
|
+
DisplayPosition = /* @__PURE__ */ ((DisplayPosition3) => {
|
|
1458
|
+
DisplayPosition3["THIRDPERSON_RIGHTHAND"] = "thirdperson_righthand";
|
|
1459
|
+
DisplayPosition3["THIRDPERSON_LEFTHAND"] = "thirdperson_lefthand";
|
|
1460
|
+
DisplayPosition3["FIRSTPERSON_RIGHTHAND"] = "firstperson_righthand";
|
|
1461
|
+
DisplayPosition3["FIRSTPERSON_LEFTHAND"] = "firstperson_lefthand";
|
|
1462
|
+
DisplayPosition3["GUI"] = "gui";
|
|
1463
|
+
DisplayPosition3["HEAD"] = "head";
|
|
1464
|
+
DisplayPosition3["GROUND"] = "ground";
|
|
1465
|
+
DisplayPosition3["FIXED"] = "fixed";
|
|
1466
|
+
DisplayPosition3["ON_SHELF"] = "on_shelf";
|
|
1467
|
+
return DisplayPosition3;
|
|
1404
1468
|
})(DisplayPosition || {});
|
|
1405
1469
|
DISPLAY_POSITIONS = Object.values(DisplayPosition);
|
|
1406
1470
|
}
|
|
@@ -1438,6 +1502,7 @@ var init_DisplayTransforms = __esm({
|
|
|
1438
1502
|
const scale = new Vector32(...transform.scale ?? [1, 1, 1]).clampScalar(-4, 4);
|
|
1439
1503
|
return new Matrix4().compose(translation, rotation, scale);
|
|
1440
1504
|
}
|
|
1505
|
+
/** Transforms geometry in place and reverses triangle winding when the matrix reflects it. */
|
|
1441
1506
|
static apply(geometry, matrix) {
|
|
1442
1507
|
geometry.applyMatrix4(matrix);
|
|
1443
1508
|
if (matrix.determinant() < 0) {
|
|
@@ -1465,7 +1530,10 @@ var init_ModelMerger = __esm({
|
|
|
1465
1530
|
init_AssetKey();
|
|
1466
1531
|
init_DisplayTransforms();
|
|
1467
1532
|
ModelMerger = class {
|
|
1533
|
+
/** Loads the parent chain and returns a merged model without modifying the supplied definition. */
|
|
1468
1534
|
static async mergeWithParents(model) {
|
|
1535
|
+
const parts = model.parts;
|
|
1536
|
+
if (parts) return { ...model, parts: await Promise.all(parts.map((part) => this.mergeWithParents(part))) };
|
|
1469
1537
|
const models = await this.collectAllParents(model);
|
|
1470
1538
|
let merged = {};
|
|
1471
1539
|
for (const source of [...models, model]) {
|
|
@@ -1477,6 +1545,7 @@ var init_ModelMerger = __esm({
|
|
|
1477
1545
|
}
|
|
1478
1546
|
merged.hierarchy = models.map((m) => m.parent).filter((p10) => `${p10}`);
|
|
1479
1547
|
merged.hierarchy.push(`${merged.parent}`);
|
|
1548
|
+
if (model.components) merged.components = model.components;
|
|
1480
1549
|
return merged;
|
|
1481
1550
|
}
|
|
1482
1551
|
static async collectAllParents(model) {
|
|
@@ -1515,6 +1584,79 @@ var init_AssetParsers = __esm({
|
|
|
1515
1584
|
}
|
|
1516
1585
|
});
|
|
1517
1586
|
|
|
1587
|
+
// src/assets/BannerPatterns.ts
|
|
1588
|
+
var DYE_COLORS, BannerPatterns;
|
|
1589
|
+
var init_BannerPatterns = __esm({
|
|
1590
|
+
"src/assets/BannerPatterns.ts"() {
|
|
1591
|
+
init_AssetKey();
|
|
1592
|
+
init_AssetLoader();
|
|
1593
|
+
init_AssetParsers();
|
|
1594
|
+
init_Caching();
|
|
1595
|
+
DYE_COLORS = Object.freeze({
|
|
1596
|
+
white: 16383998,
|
|
1597
|
+
orange: 16351261,
|
|
1598
|
+
magenta: 13061821,
|
|
1599
|
+
light_blue: 3847130,
|
|
1600
|
+
yellow: 16701501,
|
|
1601
|
+
lime: 8439583,
|
|
1602
|
+
pink: 15961002,
|
|
1603
|
+
gray: 4673362,
|
|
1604
|
+
light_gray: 10329495,
|
|
1605
|
+
cyan: 1481884,
|
|
1606
|
+
purple: 8991416,
|
|
1607
|
+
blue: 3949738,
|
|
1608
|
+
brown: 8606770,
|
|
1609
|
+
green: 6192150,
|
|
1610
|
+
red: 11546150,
|
|
1611
|
+
black: 1908001
|
|
1612
|
+
});
|
|
1613
|
+
BannerPatterns = class {
|
|
1614
|
+
/** Lists namespaced pattern IDs from the selected namespace's data directory. */
|
|
1615
|
+
static async getList(namespace = "minecraft", root) {
|
|
1616
|
+
const collect = async (path) => {
|
|
1617
|
+
const prefix2 = path ? `${path}/` : "";
|
|
1618
|
+
const key = new AssetKey(namespace, `${prefix2}_list`, "banner_pattern", void 0, "data", ".json", root);
|
|
1619
|
+
const list2 = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
|
|
1620
|
+
if (!list2) return [];
|
|
1621
|
+
const files = list2.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${namespace}:${prefix2}${file.slice(0, -5)}`);
|
|
1622
|
+
const children = await Promise.all(list2.directories.map((directory) => collect(`${prefix2}${directory}`)));
|
|
1623
|
+
return [...files, ...children.flat()];
|
|
1624
|
+
};
|
|
1625
|
+
return collect("");
|
|
1626
|
+
}
|
|
1627
|
+
/** Returns the named dye's sRGB value, rejecting unknown colors. */
|
|
1628
|
+
static getColor(color2) {
|
|
1629
|
+
if (typeof color2 !== "string" || !Object.prototype.hasOwnProperty.call(DYE_COLORS, color2)) throw new Error(`Unsupported dye color ${color2}`);
|
|
1630
|
+
return DYE_COLORS[color2];
|
|
1631
|
+
}
|
|
1632
|
+
/** Resolves the first 16 layers of a `banner_patterns` component, preserving their order. */
|
|
1633
|
+
static async getLayers(value, target, root) {
|
|
1634
|
+
if (value === void 0) return [];
|
|
1635
|
+
if (!Array.isArray(value)) throw new Error("banner_patterns must be an array");
|
|
1636
|
+
return Promise.all(value.slice(0, 16).map(async (layer) => {
|
|
1637
|
+
if (!layer || typeof layer !== "object" || Array.isArray(layer)) throw new Error("Each banner pattern requires a pattern and dye color");
|
|
1638
|
+
const color2 = this.getColor(layer.color);
|
|
1639
|
+
let pattern2 = layer.pattern;
|
|
1640
|
+
if (typeof pattern2 === "string") {
|
|
1641
|
+
const id = this.identifier(pattern2);
|
|
1642
|
+
const key = new AssetKey(id.namespace, id.getFullPath(), "banner_pattern", void 0, "data", ".json", root);
|
|
1643
|
+
pattern2 = await Caching.bannerPatternCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
|
|
1644
|
+
if (!pattern2) throw new Error(`Missing banner pattern ${layer.pattern}`);
|
|
1645
|
+
}
|
|
1646
|
+
if (!pattern2 || typeof pattern2 !== "object" || Array.isArray(pattern2) || typeof pattern2.translation_key !== "string") throw new Error("Banner patterns require asset_id and translation_key");
|
|
1647
|
+
const asset2 = this.identifier(pattern2.asset_id);
|
|
1648
|
+
return { color: color2, texture: new AssetKey(asset2.namespace, `${target}/${asset2.getFullPath()}`, "textures", "entity", "assets", ".png", root) };
|
|
1649
|
+
}));
|
|
1650
|
+
}
|
|
1651
|
+
static identifier(value) {
|
|
1652
|
+
if (typeof value !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(value)) throw new Error(`Invalid banner-pattern identifier ${value}`);
|
|
1653
|
+
const [namespace, path] = value.includes(":") ? value.split(":") : ["minecraft", value];
|
|
1654
|
+
return new AssetKey(namespace, path);
|
|
1655
|
+
}
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
});
|
|
1659
|
+
|
|
1518
1660
|
// src/assets/Models.ts
|
|
1519
1661
|
var Models;
|
|
1520
1662
|
var init_Models = __esm({
|
|
@@ -1526,12 +1668,15 @@ var init_Models = __esm({
|
|
|
1526
1668
|
init_PersistentCache();
|
|
1527
1669
|
init_AssetKey();
|
|
1528
1670
|
init_AssetParsers();
|
|
1671
|
+
init_DisplayPosition();
|
|
1672
|
+
init_BannerPatterns();
|
|
1529
1673
|
Models = class _Models {
|
|
1530
1674
|
// opened lazily: touching the store at import time would hit IndexedDB/disk just for loading
|
|
1531
1675
|
// the library, and would require an EnvProvider before any entry has had a chance to register
|
|
1532
1676
|
static get PERSISTENT_CACHE() {
|
|
1533
1677
|
return this._persistentCache ??= PersistentCache.open("minerender-models");
|
|
1534
1678
|
}
|
|
1679
|
+
/** Lists item filenames from the asset source's `_list.json`, with a legacy model-directory fallback. */
|
|
1535
1680
|
static async getItemList() {
|
|
1536
1681
|
const key = new AssetKey(DEFAULT_NAMESPACE, "_list", "items");
|
|
1537
1682
|
const legacyKey = new AssetKey(DEFAULT_NAMESPACE, "_list", "models", "item");
|
|
@@ -1539,34 +1684,174 @@ var init_Models = __esm({
|
|
|
1539
1684
|
return (await AssetLoader.getFirst([key, legacyKey], "list" /* LIST */))?.asset;
|
|
1540
1685
|
}).then((r) => r?.files ?? []);
|
|
1541
1686
|
}
|
|
1542
|
-
static async loadAndMerge(key) {
|
|
1543
|
-
const model = key.type === "item" ? await this.getItemModel(key) : await this.getRaw(key);
|
|
1687
|
+
static async loadAndMerge(key, context = {}) {
|
|
1688
|
+
const model = key.type === "item" ? await this.getItemModel(key, context) : await this.getRaw(key);
|
|
1544
1689
|
if (!model) {
|
|
1545
1690
|
return void 0;
|
|
1546
1691
|
}
|
|
1547
1692
|
return ModelMerger.mergeWithParents(model);
|
|
1548
1693
|
}
|
|
1549
|
-
static async getItemModel(key) {
|
|
1694
|
+
static async getItemModel(key, context = {}) {
|
|
1695
|
+
const preview = this.snapshotContext(key, context);
|
|
1550
1696
|
const itemKey = new AssetKey(key.namespace, key.path, "items", void 0, key.rootType, ".json", key.root);
|
|
1551
|
-
const
|
|
1697
|
+
const cacheKey = itemKey.serialize() + this.contextKey(preview);
|
|
1698
|
+
const model = await this.PERSISTENT_CACHE.getOrLoad(`item-v4:${AssetLoader.persistentKey(cacheKey)}`, async () => {
|
|
1552
1699
|
const result = await AssetLoader.getFirst([itemKey, key], "json" /* JSON */);
|
|
1553
1700
|
if (!result) return void 0;
|
|
1554
|
-
if (result.key.assetType !== "items") return { ...result.asset, key };
|
|
1555
|
-
const
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1701
|
+
if (result.key.assetType !== "items") return { ...result.asset, key, components: preview.components };
|
|
1702
|
+
const load = async (selected) => {
|
|
1703
|
+
if ("parts" in selected) {
|
|
1704
|
+
return { key, parts: await Promise.all(selected.parts.map(load)) };
|
|
1705
|
+
}
|
|
1706
|
+
if ("item" in selected) {
|
|
1707
|
+
const model3 = await this.getItemModel(selected.item, { displayContext: preview.displayContext });
|
|
1708
|
+
if (!model3) throw new Error(`Item ${key.toNamespacedString()} references missing item ${selected.item.toNamespacedString()}`);
|
|
1709
|
+
return model3;
|
|
1710
|
+
}
|
|
1711
|
+
const modelKey = AssetKey.parse("models", selected.model);
|
|
1712
|
+
modelKey.root = key.root;
|
|
1713
|
+
const model2 = await this.getRaw(modelKey);
|
|
1714
|
+
if (!model2) throw new Error(`Item ${key.toNamespacedString()} references missing model ${selected.model}`);
|
|
1715
|
+
return { ...model2, components: preview.components, ...selected.special && { special: selected.special }, ...selected.tints && { tints: selected.tints } };
|
|
1716
|
+
};
|
|
1717
|
+
return load(this.selectItemModel(result.asset.model, key, preview));
|
|
1561
1718
|
});
|
|
1562
|
-
|
|
1719
|
+
const restore = (model2) => ({
|
|
1720
|
+
...model2,
|
|
1721
|
+
key: Object.assign(new AssetKey("", ""), model2.key),
|
|
1722
|
+
...model2.parts && { parts: model2.parts.map(restore) }
|
|
1723
|
+
});
|
|
1724
|
+
return model ? restore(model) : void 0;
|
|
1725
|
+
}
|
|
1726
|
+
static contextIdentifier(id) {
|
|
1727
|
+
if (typeof id !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(id)) {
|
|
1728
|
+
throw new Error(`Invalid item-preview identifier: ${id}`);
|
|
1729
|
+
}
|
|
1730
|
+
return id.includes(":") ? id : `minecraft:${id}`;
|
|
1731
|
+
}
|
|
1732
|
+
static snapshotContext(key, context) {
|
|
1733
|
+
const displayContext = context.displayContext ?? "gui" /* GUI */;
|
|
1734
|
+
if (displayContext !== "none" && !Object.values(DisplayPosition).includes(displayContext)) {
|
|
1735
|
+
throw new Error(`Unsupported item display context: ${displayContext}`);
|
|
1736
|
+
}
|
|
1737
|
+
for (const map of [context.properties, context.components, context.itemReferences]) {
|
|
1738
|
+
if (map !== void 0 && (!map || typeof map !== "object" || Array.isArray(map))) {
|
|
1739
|
+
throw new Error("Item-preview properties, components, and references must be objects keyed by identifier");
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
const count = context.count === void 0 ? 1 : context.count;
|
|
1743
|
+
if (!Number.isSafeInteger(count) || count < 0) throw new Error("Item-preview count must be a nonnegative safe integer");
|
|
1744
|
+
const properties = {};
|
|
1745
|
+
for (const [id, value] of Object.entries(context.properties ?? {})) {
|
|
1746
|
+
const property = this.contextIdentifier(id);
|
|
1747
|
+
if (!["boolean", "string", "number"].includes(typeof value) || typeof value === "number" && !Number.isFinite(value)) {
|
|
1748
|
+
throw new Error(`Item-preview property ${property} must be a boolean, string, or finite number`);
|
|
1749
|
+
}
|
|
1750
|
+
if (Object.prototype.hasOwnProperty.call(properties, property)) throw new Error(`Duplicate item-preview property: ${property}`);
|
|
1751
|
+
properties[property] = value;
|
|
1752
|
+
}
|
|
1753
|
+
const components = {};
|
|
1754
|
+
for (const [id, value] of Object.entries(context.components ?? {})) {
|
|
1755
|
+
const component = this.contextIdentifier(id);
|
|
1756
|
+
if (Object.prototype.hasOwnProperty.call(components, component)) throw new Error(`Duplicate item-preview component: ${component}`);
|
|
1757
|
+
components[component] = this.snapshotJson(value);
|
|
1758
|
+
}
|
|
1759
|
+
const itemReferences = {};
|
|
1760
|
+
for (const [id, value] of Object.entries(context.itemReferences ?? {})) {
|
|
1761
|
+
const reference = this.contextIdentifier(id);
|
|
1762
|
+
if (!isAssetKey(value) || value.assetType !== "models" || value.type !== "item") {
|
|
1763
|
+
throw new Error(`Item-preview reference ${reference} must be an item-model AssetKey`);
|
|
1764
|
+
}
|
|
1765
|
+
this.contextIdentifier(`${value.namespace}:${value.path}`);
|
|
1766
|
+
if (Object.prototype.hasOwnProperty.call(itemReferences, reference)) throw new Error(`Duplicate item-preview reference: ${reference}`);
|
|
1767
|
+
itemReferences[reference] = Object.assign(new AssetKey("", ""), value, { root: value.root ?? key.root });
|
|
1768
|
+
}
|
|
1769
|
+
return { displayContext, properties, components, count, itemReferences };
|
|
1563
1770
|
}
|
|
1564
|
-
|
|
1565
|
-
|
|
1771
|
+
static contextKey(context) {
|
|
1772
|
+
const itemReferences = Object.fromEntries(Object.entries(context.itemReferences).map(([id, key]) => [id, key.serialize()]));
|
|
1773
|
+
return `|item-v4:${JSON.stringify(this.snapshotJson({ ...context, itemReferences }))}`;
|
|
1774
|
+
}
|
|
1775
|
+
static snapshotJson(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
1776
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return value;
|
|
1777
|
+
if (typeof value !== "object" || ancestors.has(value) || !Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
|
|
1778
|
+
throw new Error("Item-preview components must contain finite JSON values without circular references");
|
|
1779
|
+
}
|
|
1780
|
+
ancestors.add(value);
|
|
1781
|
+
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)]));
|
|
1782
|
+
ancestors.delete(value);
|
|
1783
|
+
return copy;
|
|
1784
|
+
}
|
|
1785
|
+
static propertyValue(node, context) {
|
|
1786
|
+
const kind = node.type.replace(/^minecraft:/, "");
|
|
1787
|
+
const property = node.property ? this.contextIdentifier(node.property) : void 0;
|
|
1788
|
+
if (kind === "select" && property === "minecraft:display_context") return context.displayContext;
|
|
1789
|
+
if (property && Object.prototype.hasOwnProperty.call(context.properties, property)) return context.properties[property];
|
|
1790
|
+
const fallback = kind === "condition" ? false : kind === "range_dispatch" ? 0 : void 0;
|
|
1791
|
+
const component = (id) => context.components[this.contextIdentifier(id)];
|
|
1792
|
+
if (property === "minecraft:custom_model_data") {
|
|
1793
|
+
const index = node.index ?? 0;
|
|
1794
|
+
if (!Number.isSafeInteger(index) || index < 0) throw new Error("Item-model custom_model_data index must be a nonnegative integer");
|
|
1795
|
+
const data = component("custom_model_data");
|
|
1796
|
+
if (data === void 0) return fallback;
|
|
1797
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) throw new Error("Item-preview custom_model_data must be an object");
|
|
1798
|
+
const field = kind === "condition" ? "flags" : kind === "select" ? "strings" : "floats";
|
|
1799
|
+
const values = data[field];
|
|
1800
|
+
if (values === void 0) return fallback;
|
|
1801
|
+
if (!Array.isArray(values)) throw new Error(`Item-preview custom_model_data.${field} must be an array`);
|
|
1802
|
+
return index < values.length ? values[index] : fallback;
|
|
1803
|
+
}
|
|
1804
|
+
if (kind === "condition" && property === "minecraft:has_component") {
|
|
1805
|
+
if (node.ignore_default) throw new Error("Item-preview has_component with ignore_default requires an explicit properties override; item defaults are unavailable");
|
|
1806
|
+
return Object.prototype.hasOwnProperty.call(context.components, this.contextIdentifier(node.component));
|
|
1807
|
+
}
|
|
1808
|
+
if (kind === "select") {
|
|
1809
|
+
switch (property) {
|
|
1810
|
+
case "minecraft:component":
|
|
1811
|
+
return component(node.component);
|
|
1812
|
+
case "minecraft:block_state": {
|
|
1813
|
+
const state = component("block_state");
|
|
1814
|
+
if (state === void 0) return void 0;
|
|
1815
|
+
if (!state || typeof state !== "object" || Array.isArray(state)) throw new Error("Item-preview block_state must be an object");
|
|
1816
|
+
if (typeof node.block_state_property !== "string") throw new Error("Item-model block_state requires block_state_property");
|
|
1817
|
+
return Object.prototype.hasOwnProperty.call(state, node.block_state_property) ? state[node.block_state_property] : void 0;
|
|
1818
|
+
}
|
|
1819
|
+
case "minecraft:charge_type": {
|
|
1820
|
+
const projectiles = component("charged_projectiles");
|
|
1821
|
+
if (projectiles === void 0) return "none";
|
|
1822
|
+
if (!Array.isArray(projectiles)) throw new Error("Item-preview charged_projectiles must be an array of item stacks");
|
|
1823
|
+
const ids = projectiles.map((stack) => {
|
|
1824
|
+
if (!stack || typeof stack !== "object" || Array.isArray(stack)) throw new Error("Item-preview charged_projectiles must contain item-stack objects");
|
|
1825
|
+
return this.contextIdentifier(stack.id);
|
|
1826
|
+
});
|
|
1827
|
+
return !ids.length ? "none" : ids.includes("minecraft:firework_rocket") ? "rocket" : "arrow";
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
if (kind === "range_dispatch" && (property === "minecraft:damage" || property === "minecraft:count")) {
|
|
1832
|
+
const damage = property === "minecraft:damage";
|
|
1833
|
+
const maximumComponent = component(damage ? "max_damage" : "max_stack_size");
|
|
1834
|
+
const maximum = maximumComponent === void 0 ? damage ? 0 : 1 : maximumComponent;
|
|
1835
|
+
const damageComponent = component("damage");
|
|
1836
|
+
const value = damage ? damageComponent === void 0 ? 0 : damageComponent : context.count;
|
|
1837
|
+
if (typeof value !== "number" || typeof maximum !== "number" || maximum < 0) throw new Error(`Item-preview ${property} requires numeric components and a nonnegative maximum`);
|
|
1838
|
+
const clamped = Math.max(0, Math.min(maximum, value));
|
|
1839
|
+
return node.normalize === false ? clamped : clamped / maximum;
|
|
1840
|
+
}
|
|
1841
|
+
return fallback;
|
|
1842
|
+
}
|
|
1843
|
+
static selectItemModel(node, key, context) {
|
|
1566
1844
|
if (!node || typeof node.type !== "string") {
|
|
1567
1845
|
throw new Error(`Unsupported item model definition for ${key.toNamespacedString()}`);
|
|
1568
1846
|
}
|
|
1569
1847
|
switch (node.type.replace(/^minecraft:/, "")) {
|
|
1848
|
+
case "composite":
|
|
1849
|
+
if (Array.isArray(node.models)) return { parts: node.models.map((child) => this.selectItemModel(child, key, context)) };
|
|
1850
|
+
break;
|
|
1851
|
+
case "bundle/selected_item": {
|
|
1852
|
+
const item = context.itemReferences["minecraft:bundle/selected_item"];
|
|
1853
|
+
return item ? { item } : { parts: [] };
|
|
1854
|
+
}
|
|
1570
1855
|
case "model":
|
|
1571
1856
|
if (typeof node.model === "string" && node.model) return { model: node.model, tints: node.tints };
|
|
1572
1857
|
break;
|
|
@@ -1574,6 +1859,23 @@ var init_Models = __esm({
|
|
|
1574
1859
|
if (!node.base || !node.model || typeof node.model !== "object") break;
|
|
1575
1860
|
const special = node.model;
|
|
1576
1861
|
switch (special.type) {
|
|
1862
|
+
case "banner":
|
|
1863
|
+
case "minecraft:banner":
|
|
1864
|
+
if (typeof special.color === "string" && Object.prototype.hasOwnProperty.call(DYE_COLORS, special.color)) return { model: node.base, special };
|
|
1865
|
+
break;
|
|
1866
|
+
case "shield":
|
|
1867
|
+
case "minecraft:shield":
|
|
1868
|
+
case "trident":
|
|
1869
|
+
case "minecraft:trident":
|
|
1870
|
+
case "conduit":
|
|
1871
|
+
case "minecraft:conduit":
|
|
1872
|
+
return { model: node.base, special };
|
|
1873
|
+
case "shulker_box":
|
|
1874
|
+
case "minecraft:shulker_box":
|
|
1875
|
+
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))) {
|
|
1876
|
+
return { model: node.base, special };
|
|
1877
|
+
}
|
|
1878
|
+
break;
|
|
1577
1879
|
case "chest":
|
|
1578
1880
|
case "minecraft:chest":
|
|
1579
1881
|
case "bed":
|
|
@@ -1587,21 +1889,34 @@ var init_Models = __esm({
|
|
|
1587
1889
|
}
|
|
1588
1890
|
throw new Error(`Unsupported special item renderer ${special.type} for ${key.toNamespacedString()}`);
|
|
1589
1891
|
}
|
|
1590
|
-
case "condition":
|
|
1591
|
-
|
|
1892
|
+
case "condition": {
|
|
1893
|
+
const value = this.propertyValue(node, context);
|
|
1894
|
+
if (typeof value !== "boolean") throw new Error(`Item-preview condition ${node.property} requires a boolean`);
|
|
1895
|
+
return this.selectItemModel(value ? node.on_true : node.on_false, key, context);
|
|
1896
|
+
}
|
|
1592
1897
|
case "select": {
|
|
1593
|
-
const
|
|
1594
|
-
|
|
1898
|
+
const value = this.propertyValue(node, context);
|
|
1899
|
+
const component = node.property && this.contextIdentifier(node.property) === "minecraft:component";
|
|
1900
|
+
if (!component && value !== void 0 && typeof value !== "string") throw new Error(`Item-preview selector ${node.property} requires a string`);
|
|
1901
|
+
const matches = (candidate) => component ? JSON.stringify(this.snapshotJson(candidate)) === JSON.stringify(this.snapshotJson(value)) : candidate === value;
|
|
1902
|
+
const selected = value === void 0 ? void 0 : node.cases?.find((entry) => matches(entry.when) || Array.isArray(entry.when) && entry.when.some(matches))?.model;
|
|
1903
|
+
return this.selectItemModel(selected ?? node.fallback, key, context);
|
|
1595
1904
|
}
|
|
1596
1905
|
case "range_dispatch": {
|
|
1906
|
+
const value = this.propertyValue(node, context);
|
|
1907
|
+
if (typeof value !== "number") throw new Error(`Item-preview range ${node.property} requires a number`);
|
|
1908
|
+
if (Number.isNaN(value)) return this.selectItemModel(node.fallback, key, context);
|
|
1909
|
+
const scaled = value * (node.scale ?? 1);
|
|
1910
|
+
if (!Number.isFinite(scaled)) throw new Error(`Item-preview range ${node.property} requires a finite value and scale`);
|
|
1597
1911
|
const selected = node.entries?.reduce((match, entry) => {
|
|
1598
|
-
return entry.threshold <=
|
|
1912
|
+
return entry.threshold <= scaled && (!match || entry.threshold >= match.threshold) ? entry : match;
|
|
1599
1913
|
}, void 0);
|
|
1600
|
-
return this.
|
|
1914
|
+
return this.selectItemModel(selected?.model ?? node.fallback, key, context);
|
|
1601
1915
|
}
|
|
1602
1916
|
}
|
|
1603
1917
|
throw new Error(`Unsupported item model ${node?.type ?? "definition"} for ${key.toNamespacedString()}`);
|
|
1604
1918
|
}
|
|
1919
|
+
/** Loads one model file without resolving its parents. Returns `undefined` when the file is missing. */
|
|
1605
1920
|
static async getRaw(key) {
|
|
1606
1921
|
if (!key.assetType) {
|
|
1607
1922
|
key.assetType = "models";
|
|
@@ -1614,28 +1929,39 @@ var init_Models = __esm({
|
|
|
1614
1929
|
return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
|
|
1615
1930
|
return AssetLoader.get(key, "model" /* MODEL */);
|
|
1616
1931
|
});
|
|
1617
|
-
}).then((
|
|
1618
|
-
if (
|
|
1619
|
-
|
|
1932
|
+
}).then((asset2) => {
|
|
1933
|
+
if (asset2) {
|
|
1934
|
+
asset2.key = key;
|
|
1620
1935
|
}
|
|
1621
|
-
return
|
|
1936
|
+
return asset2;
|
|
1622
1937
|
});
|
|
1623
1938
|
}
|
|
1624
|
-
|
|
1939
|
+
/**
|
|
1940
|
+
* Loads a model and resolves its parent chain. Returns `undefined` when the model is missing.
|
|
1941
|
+
* Item keys default to GUI context and a stack count of 1. Unresolved conditions are false and numeric properties are zero.
|
|
1942
|
+
* Pass `context` to supply component values, property overrides, and item references for a preview.
|
|
1943
|
+
* Composite items retain independently merged children in `ItemModel.parts`.
|
|
1944
|
+
*
|
|
1945
|
+
* @param key - Model key, for example `AssetKey.parse("models", "minecraft:item/diamond_sword")`.
|
|
1946
|
+
*/
|
|
1947
|
+
static async getMerged(key, context = {}) {
|
|
1625
1948
|
if (!key.assetType) {
|
|
1626
1949
|
key.assetType = "models";
|
|
1627
1950
|
}
|
|
1628
1951
|
if (!key.extension) {
|
|
1629
1952
|
key.extension = ".json";
|
|
1630
1953
|
}
|
|
1631
|
-
const
|
|
1954
|
+
const preview = this.snapshotContext(key, context);
|
|
1955
|
+
const keyStr = key.serialize() + (key.type === "item" ? this.contextKey(preview) : "");
|
|
1632
1956
|
return Caching.mergedModelCache.get(keyStr, (k) => {
|
|
1633
|
-
return _Models.loadAndMerge(key);
|
|
1957
|
+
return _Models.loadAndMerge(key, preview);
|
|
1634
1958
|
});
|
|
1635
1959
|
}
|
|
1636
|
-
|
|
1637
|
-
|
|
1960
|
+
/** Alias for {@link getMerged}. */
|
|
1961
|
+
static async get(key, context = {}) {
|
|
1962
|
+
return this.getMerged(key, context);
|
|
1638
1963
|
}
|
|
1964
|
+
/** Clears persisted models. Use {@link Caching.clear} to also discard in-memory assets. */
|
|
1639
1965
|
static async clearCache() {
|
|
1640
1966
|
return this.PERSISTENT_CACHE.clear();
|
|
1641
1967
|
}
|
|
@@ -1658,6 +1984,7 @@ var init_BlockStates = __esm({
|
|
|
1658
1984
|
return this._persistentCache ??= PersistentCache.open("minerender-blockstates");
|
|
1659
1985
|
}
|
|
1660
1986
|
// BlockState names are hardcoded
|
|
1987
|
+
/** Returns blockstate filenames from the selected version's `_list.json`, or an empty list. */
|
|
1661
1988
|
static async getList() {
|
|
1662
1989
|
const key = new AssetKey(
|
|
1663
1990
|
DEFAULT_NAMESPACE,
|
|
@@ -1678,6 +2005,7 @@ var init_BlockStates = __esm({
|
|
|
1678
2005
|
return AssetLoader.get(key, "json" /* JSON */);
|
|
1679
2006
|
});
|
|
1680
2007
|
}
|
|
2008
|
+
/** Looks up vanilla property definitions for the key's block path, or returns `undefined`. */
|
|
1681
2009
|
static async getDefaultState(key) {
|
|
1682
2010
|
const defaultStates = await this.getDefaultStates();
|
|
1683
2011
|
if (!defaultStates) {
|
|
@@ -1685,6 +2013,7 @@ var init_BlockStates = __esm({
|
|
|
1685
2013
|
}
|
|
1686
2014
|
return defaultStates["minecraft:" + key.path];
|
|
1687
2015
|
}
|
|
2016
|
+
/** Loads a cached blockstate definition for {@link MineRenderScene.addBlock}, or returns `undefined`. */
|
|
1688
2017
|
static async get(key) {
|
|
1689
2018
|
if (!key.assetType) {
|
|
1690
2019
|
key.assetType = "blockstates";
|
|
@@ -1697,11 +2026,11 @@ var init_BlockStates = __esm({
|
|
|
1697
2026
|
return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
|
|
1698
2027
|
return AssetLoader.get(key, "blockstate" /* BLOCKSTATE */);
|
|
1699
2028
|
});
|
|
1700
|
-
}).then((
|
|
1701
|
-
if (
|
|
1702
|
-
|
|
2029
|
+
}).then((asset2) => {
|
|
2030
|
+
if (asset2) {
|
|
2031
|
+
asset2.key = key;
|
|
1703
2032
|
}
|
|
1704
|
-
return
|
|
2033
|
+
return asset2;
|
|
1705
2034
|
});
|
|
1706
2035
|
}
|
|
1707
2036
|
static getAll(keys) {
|
|
@@ -1711,6 +2040,7 @@ var init_BlockStates = __esm({
|
|
|
1711
2040
|
}
|
|
1712
2041
|
return Promise.all(promises);
|
|
1713
2042
|
}
|
|
2043
|
+
/** Clears persisted blockstates. Use {@link Caching.clear} to also discard in-memory assets. */
|
|
1714
2044
|
static async clearCache() {
|
|
1715
2045
|
await this.PERSISTENT_CACHE.clear();
|
|
1716
2046
|
}
|
|
@@ -1736,9 +2066,11 @@ var init_Assets = __esm({
|
|
|
1736
2066
|
static parseAssetKey(assetType, str, origin) {
|
|
1737
2067
|
return AssetKey.parse(assetType, str, origin);
|
|
1738
2068
|
}
|
|
2069
|
+
/** Loads a model with its parent chain resolved. See {@link Models.getMerged}. */
|
|
1739
2070
|
static async getModel(key) {
|
|
1740
2071
|
return Models.getMerged(key);
|
|
1741
2072
|
}
|
|
2073
|
+
/** Loads a blockstate definition. See {@link BlockStates.get}. */
|
|
1742
2074
|
static async getBlockState(key) {
|
|
1743
2075
|
return BlockStates.get(key);
|
|
1744
2076
|
}
|
|
@@ -1753,6 +2085,10 @@ var init_AssetSource = __esm({
|
|
|
1753
2085
|
AssetSource = class {
|
|
1754
2086
|
constructor() {
|
|
1755
2087
|
}
|
|
2088
|
+
/** Whether this source prevents lower-priority sources from supplying the key. Its own assets remain available. */
|
|
2089
|
+
blocks(key) {
|
|
2090
|
+
return false;
|
|
2091
|
+
}
|
|
1756
2092
|
/**
|
|
1757
2093
|
* Identifies this source's content across sessions for persistent caching. Sources that
|
|
1758
2094
|
* cannot tell whether their content changed (an unnamed archive, for example) return
|
|
@@ -1781,6 +2117,10 @@ var BatchedExecutor;
|
|
|
1781
2117
|
var init_BatchedExecutor = __esm({
|
|
1782
2118
|
"src/util/BatchedExecutor.ts"() {
|
|
1783
2119
|
BatchedExecutor = class {
|
|
2120
|
+
/**
|
|
2121
|
+
* @param interval - Delay between queue runs, in milliseconds.
|
|
2122
|
+
* @param batch - Maximum tasks started per run and maximum tasks active at once.
|
|
2123
|
+
*/
|
|
1784
2124
|
constructor(interval = 1, batch = 30) {
|
|
1785
2125
|
if (!Number.isFinite(interval) || interval < 0) throw new RangeError("Batch interval must be finite and nonnegative");
|
|
1786
2126
|
if (!Number.isInteger(batch) || batch < 1) throw new RangeError("Batch size must be a positive integer");
|
|
@@ -1788,9 +2128,11 @@ var init_BatchedExecutor = __esm({
|
|
|
1788
2128
|
this.batch = batch;
|
|
1789
2129
|
this.queue = new JobQueue2(async (task) => task(), { interval, maxPerRun: batch, maxActive: batch });
|
|
1790
2130
|
}
|
|
2131
|
+
/** Queues a task and resolves with its result, or rejects with its error. */
|
|
1791
2132
|
submit(task) {
|
|
1792
2133
|
return this.queue.add(() => task());
|
|
1793
2134
|
}
|
|
2135
|
+
/** Rejects queued and future tasks. Tasks already running are allowed to finish. */
|
|
1794
2136
|
stop() {
|
|
1795
2137
|
this.queue.end();
|
|
1796
2138
|
}
|
|
@@ -1838,29 +2180,29 @@ function applyGenericRotation(axis, rotation, obj) {
|
|
|
1838
2180
|
break;
|
|
1839
2181
|
}
|
|
1840
2182
|
}
|
|
1841
|
-
function addBox3WireframeToObject(box, obj,
|
|
2183
|
+
function addBox3WireframeToObject(box, obj, color2 = 16777215, w = 2) {
|
|
1842
2184
|
let size = new Vector33();
|
|
1843
2185
|
box.getSize(size);
|
|
1844
2186
|
let boxGeo = new BoxGeometry2(size.x, size.y, size.z, 1, 1, 1);
|
|
1845
2187
|
let wireGeo = new EdgesGeometry(boxGeo);
|
|
1846
|
-
let wireMat = new LineBasicMaterial({ color, linewidth: w });
|
|
2188
|
+
let wireMat = new LineBasicMaterial({ color: color2, linewidth: w });
|
|
1847
2189
|
let wireframe = new LineSegments(wireGeo, wireMat);
|
|
1848
2190
|
obj.add(wireframe);
|
|
1849
2191
|
let axes = new AxesHelper(1);
|
|
1850
2192
|
obj.add(axes);
|
|
1851
2193
|
}
|
|
1852
|
-
function addWireframeToObject(obj,
|
|
2194
|
+
function addWireframeToObject(obj, color2 = 16777215, w = 2) {
|
|
1853
2195
|
let boxGeo = new BoxGeometry2(16, 16, 16, 1, 1, 1);
|
|
1854
2196
|
let wireGeo = new EdgesGeometry(boxGeo);
|
|
1855
|
-
let wireMat = new LineBasicMaterial({ color, linewidth: w });
|
|
2197
|
+
let wireMat = new LineBasicMaterial({ color: color2, linewidth: w });
|
|
1856
2198
|
let wireframe = new LineSegments(wireGeo, wireMat);
|
|
1857
2199
|
obj.add(wireframe);
|
|
1858
2200
|
let axes = new AxesHelper(1);
|
|
1859
2201
|
obj.add(axes);
|
|
1860
2202
|
}
|
|
1861
|
-
function addWireframeToMesh(geo, mesh,
|
|
2203
|
+
function addWireframeToMesh(geo, mesh, color2 = 16777215, w = 2) {
|
|
1862
2204
|
let wireGeo = new EdgesGeometry(geo);
|
|
1863
|
-
let wireMat = new LineBasicMaterial({ color, linewidth: w });
|
|
2205
|
+
let wireMat = new LineBasicMaterial({ color: color2, linewidth: w });
|
|
1864
2206
|
let wireframe = new LineSegments(wireGeo, wireMat);
|
|
1865
2207
|
mesh.add(wireframe);
|
|
1866
2208
|
let axes = new AxesHelper(1);
|
|
@@ -1932,10 +2274,11 @@ var NBTHelper;
|
|
|
1932
2274
|
var init_NBTHelper = __esm({
|
|
1933
2275
|
"src/nbt/NBTHelper.ts"() {
|
|
1934
2276
|
NBTHelper = class {
|
|
1935
|
-
|
|
2277
|
+
/** Parses NBT bytes and preserves format and compression metadata. Omit `format` for automatic detection. */
|
|
2278
|
+
static async fromBuffer(data, format2) {
|
|
1936
2279
|
const buffer = Buffer3.from(data instanceof Uint8Array ? data : new Uint8Array(data));
|
|
1937
2280
|
const prismarineNbt = await import("prismarine-nbt");
|
|
1938
|
-
const { parsed, type, metadata } = await prismarineNbt.parse(buffer,
|
|
2281
|
+
const { parsed, type, metadata } = await prismarineNbt.parse(buffer, format2);
|
|
1939
2282
|
return Object.assign(parsed, {
|
|
1940
2283
|
format: type,
|
|
1941
2284
|
metadata,
|
|
@@ -2076,6 +2419,7 @@ var init_HostedAssetSource = __esm({
|
|
|
2076
2419
|
["json" /* JSON */, _HostedAssetSource.JSON]
|
|
2077
2420
|
]);
|
|
2078
2421
|
}
|
|
2422
|
+
/** Retries missing assets with the `minecraft` namespace and default root. Other failures reject. */
|
|
2079
2423
|
async loadOrRetryWithDefaults(key, parser) {
|
|
2080
2424
|
const direct = await this.load(key, parser);
|
|
2081
2425
|
if (typeof direct !== "undefined") {
|
|
@@ -2133,6 +2477,10 @@ var init_HostedAssetSource = __esm({
|
|
|
2133
2477
|
assetBasePath(key) {
|
|
2134
2478
|
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 + "/" : ""}`;
|
|
2135
2479
|
}
|
|
2480
|
+
/**
|
|
2481
|
+
* Creates a hosted source for {@link AssetLoader.addSource}.
|
|
2482
|
+
* Set `options.retryDefaults` to `false` to restrict lookups to the requested namespace and root.
|
|
2483
|
+
*/
|
|
2136
2484
|
constructor(root, options) {
|
|
2137
2485
|
super();
|
|
2138
2486
|
this._root = root;
|
|
@@ -2164,6 +2512,11 @@ var BrowserArchiveProxy;
|
|
|
2164
2512
|
var init_BrowserArchiveProxy = __esm({
|
|
2165
2513
|
"src/assets/source/archive/BrowserArchiveProxy.ts"() {
|
|
2166
2514
|
BrowserArchiveProxy = class {
|
|
2515
|
+
/**
|
|
2516
|
+
* Opens a ZIP for asset lookup.
|
|
2517
|
+
* @param id - Cache identity for this content. Change it when the pack changes.
|
|
2518
|
+
* A File supplies an identity from its name, size, and modification time when omitted.
|
|
2519
|
+
*/
|
|
2167
2520
|
constructor(blob, id) {
|
|
2168
2521
|
this._blob = blob;
|
|
2169
2522
|
this._reader = new ZipReader(new BlobReader(this._blob));
|
|
@@ -2184,6 +2537,164 @@ var init_BrowserArchiveProxy = __esm({
|
|
|
2184
2537
|
}
|
|
2185
2538
|
});
|
|
2186
2539
|
|
|
2540
|
+
// src/assets/source/archive/PackMetadata.ts
|
|
2541
|
+
function object(value, field) {
|
|
2542
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
2543
|
+
throw new Error(`Invalid ${field}: expected an object`);
|
|
2544
|
+
}
|
|
2545
|
+
return value;
|
|
2546
|
+
}
|
|
2547
|
+
function integer(value, field) {
|
|
2548
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
2549
|
+
throw new Error(`Invalid ${field}: expected a nonnegative integer`);
|
|
2550
|
+
}
|
|
2551
|
+
return value;
|
|
2552
|
+
}
|
|
2553
|
+
function format(value, field, upper = false) {
|
|
2554
|
+
const parts = Array.isArray(value) ? value : [value];
|
|
2555
|
+
if (parts.length < 1 || parts.length > 2) {
|
|
2556
|
+
throw new Error(`Invalid ${field}: expected a major version or [major, minor]`);
|
|
2557
|
+
}
|
|
2558
|
+
return [integer(parts[0], field), parts.length === 2 ? integer(parts[1], field) : upper ? Infinity : 0];
|
|
2559
|
+
}
|
|
2560
|
+
function compare(a, b) {
|
|
2561
|
+
return a[0] === b[0] ? a[1] - b[1] : a[0] - b[0];
|
|
2562
|
+
}
|
|
2563
|
+
function overlay(value, field) {
|
|
2564
|
+
const entry = object(value, field);
|
|
2565
|
+
const directory = entry.directory;
|
|
2566
|
+
if (typeof directory !== "string" || !directory || /[^-_a-zA-Z0-9.]/.test(directory) || directory === "." || directory === "..") {
|
|
2567
|
+
throw new Error(`Invalid ${field}.directory: expected a directory name without path separators`);
|
|
2568
|
+
}
|
|
2569
|
+
let min, max;
|
|
2570
|
+
if ("min_format" in entry || "max_format" in entry) {
|
|
2571
|
+
min = format(entry.min_format, `${field}.min_format`);
|
|
2572
|
+
max = format(entry.max_format, `${field}.max_format`, true);
|
|
2573
|
+
} else {
|
|
2574
|
+
const range = entry.formats;
|
|
2575
|
+
let lower, upper;
|
|
2576
|
+
if (typeof range === "number") {
|
|
2577
|
+
lower = upper = range;
|
|
2578
|
+
} else if (Array.isArray(range)) {
|
|
2579
|
+
if (range.length !== 2) throw new Error(`Invalid ${field}.formats: expected [min, max]`);
|
|
2580
|
+
[lower, upper] = range;
|
|
2581
|
+
} else {
|
|
2582
|
+
const bounds = object(range, `${field}.formats`);
|
|
2583
|
+
lower = bounds.min_inclusive;
|
|
2584
|
+
upper = bounds.max_inclusive;
|
|
2585
|
+
}
|
|
2586
|
+
min = [integer(lower, `${field}.formats.min_inclusive`), 0];
|
|
2587
|
+
max = [integer(upper, `${field}.formats.max_inclusive`), Infinity];
|
|
2588
|
+
}
|
|
2589
|
+
if (compare(min, max) > 0) throw new Error(`Invalid ${field}: minimum format exceeds maximum format`);
|
|
2590
|
+
return { directory, min, max };
|
|
2591
|
+
}
|
|
2592
|
+
function pattern(value, field) {
|
|
2593
|
+
if (value === void 0) return void 0;
|
|
2594
|
+
if (typeof value !== "string") throw new Error(`Invalid ${field}: expected a regular expression string`);
|
|
2595
|
+
try {
|
|
2596
|
+
return new RegExp(value);
|
|
2597
|
+
} catch (cause) {
|
|
2598
|
+
throw new Error(`Invalid ${field}: ${String(cause)}`);
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
var PackMetadata;
|
|
2602
|
+
var init_PackMetadata = __esm({
|
|
2603
|
+
"src/assets/source/archive/PackMetadata.ts"() {
|
|
2604
|
+
PackMetadata = class _PackMetadata {
|
|
2605
|
+
constructor(overlays, filters) {
|
|
2606
|
+
this.overlays = overlays;
|
|
2607
|
+
this.filters = filters;
|
|
2608
|
+
this.hasOverlays = overlays.length > 0;
|
|
2609
|
+
}
|
|
2610
|
+
static parse(value) {
|
|
2611
|
+
const metadata = object(value, "pack.mcmeta");
|
|
2612
|
+
let overlays = [];
|
|
2613
|
+
if ("overlays" in metadata) {
|
|
2614
|
+
const entries = object(metadata.overlays, "overlays").entries;
|
|
2615
|
+
if (!Array.isArray(entries)) throw new Error("Invalid overlays.entries: expected an array");
|
|
2616
|
+
overlays = entries.map((entry, index) => overlay(entry, `overlays.entries[${index}]`));
|
|
2617
|
+
}
|
|
2618
|
+
let filters = [];
|
|
2619
|
+
if ("filter" in metadata) {
|
|
2620
|
+
const entries = object(metadata.filter, "filter").block;
|
|
2621
|
+
if (!Array.isArray(entries)) throw new Error("Invalid filter.block: expected an array");
|
|
2622
|
+
filters = entries.map((value2, index) => {
|
|
2623
|
+
const field = `filter.block[${index}]`;
|
|
2624
|
+
const entry = object(value2, field);
|
|
2625
|
+
return { namespace: pattern(entry.namespace, `${field}.namespace`), path: pattern(entry.path, `${field}.path`) };
|
|
2626
|
+
});
|
|
2627
|
+
}
|
|
2628
|
+
return new _PackMetadata(overlays, filters);
|
|
2629
|
+
}
|
|
2630
|
+
/** Returns applicable directories in lookup order, with the last declared overlay first. */
|
|
2631
|
+
overlayDirectories(target) {
|
|
2632
|
+
const selected = format(target, "target format");
|
|
2633
|
+
return this.overlays.filter((entry) => compare(selected, entry.min) >= 0 && compare(selected, entry.max) <= 0).map((entry) => entry.directory).reverse();
|
|
2634
|
+
}
|
|
2635
|
+
blocks(namespace, path) {
|
|
2636
|
+
return this.filters.some((entry) => entry.namespace?.test(namespace) ?? true) && this.filters.some((entry) => entry.path?.test(path) ?? true);
|
|
2637
|
+
}
|
|
2638
|
+
};
|
|
2639
|
+
}
|
|
2640
|
+
});
|
|
2641
|
+
|
|
2642
|
+
// src/assets/source/archive/PackFormats.ts
|
|
2643
|
+
var PackFormats;
|
|
2644
|
+
var init_PackFormats = __esm({
|
|
2645
|
+
"src/assets/source/archive/PackFormats.ts"() {
|
|
2646
|
+
init_Requests();
|
|
2647
|
+
PackFormats = class {
|
|
2648
|
+
static {
|
|
2649
|
+
this.versions = /* @__PURE__ */ new Map();
|
|
2650
|
+
}
|
|
2651
|
+
/** Resolves pack formats from Mojang's client metadata hosted on assets.mcasset.cloud. */
|
|
2652
|
+
static async get(version2, type) {
|
|
2653
|
+
let pending = this.versions.get(version2);
|
|
2654
|
+
if (!pending || pending.expires <= Date.now()) {
|
|
2655
|
+
pending = {
|
|
2656
|
+
formats: this.load(version2),
|
|
2657
|
+
expires: ["latest", "release", "snapshot"].includes(version2) ? Date.now() + 3e5 : Infinity
|
|
2658
|
+
};
|
|
2659
|
+
this.versions.set(version2, pending);
|
|
2660
|
+
}
|
|
2661
|
+
try {
|
|
2662
|
+
return (await pending.formats)[type];
|
|
2663
|
+
} catch (cause) {
|
|
2664
|
+
if (this.versions.get(version2) === pending) this.versions.delete(version2);
|
|
2665
|
+
const option = type === "assets" ? "resourcePackFormat" : "dataPackFormat";
|
|
2666
|
+
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
2667
|
+
throw new Error(`Could not resolve the pack format for Minecraft ${version2}. Set ArchiveAssetSource ${option} explicitly. ${reason}`);
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
static async load(version2) {
|
|
2671
|
+
const response = await Requests.genericRequest({
|
|
2672
|
+
url: `https://assets.mcasset.cloud/${encodeURIComponent(version2)}/game-version.json`,
|
|
2673
|
+
responseType: "json"
|
|
2674
|
+
});
|
|
2675
|
+
const data = response.data;
|
|
2676
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
2677
|
+
throw new Error("Expected version metadata to be an object");
|
|
2678
|
+
}
|
|
2679
|
+
const packs = data.pack_version;
|
|
2680
|
+
if (typeof packs !== "number" && (!packs || typeof packs !== "object" || Array.isArray(packs))) {
|
|
2681
|
+
throw new Error("Expected pack_version to be a number or an object");
|
|
2682
|
+
}
|
|
2683
|
+
const format2 = (type) => {
|
|
2684
|
+
const modern = typeof packs === "object" && ["resource_major", "resource_minor", "data_major", "data_minor"].some((key) => key in packs);
|
|
2685
|
+
const major = typeof packs === "number" ? packs : packs[modern ? `${type}_major` : type];
|
|
2686
|
+
const minor = modern ? packs[`${type}_minor`] : 0;
|
|
2687
|
+
if (typeof major !== "number" || !Number.isSafeInteger(major) || major < 0 || typeof minor !== "number" || !Number.isSafeInteger(minor) || minor < 0) {
|
|
2688
|
+
throw new Error(`Invalid ${type} pack format in version metadata`);
|
|
2689
|
+
}
|
|
2690
|
+
return Object.freeze([major, minor]);
|
|
2691
|
+
};
|
|
2692
|
+
return { assets: format2("resource"), data: format2("data") };
|
|
2693
|
+
}
|
|
2694
|
+
};
|
|
2695
|
+
}
|
|
2696
|
+
});
|
|
2697
|
+
|
|
2187
2698
|
// src/assets/source/archive/ArchiveAssetSource.ts
|
|
2188
2699
|
var ArchiveAssetSource;
|
|
2189
2700
|
var init_ArchiveAssetSource = __esm({
|
|
@@ -2192,35 +2703,78 @@ var init_ArchiveAssetSource = __esm({
|
|
|
2192
2703
|
init_request();
|
|
2193
2704
|
init_HostedAssetSource();
|
|
2194
2705
|
init_BrowserArchiveProxy();
|
|
2706
|
+
init_AssetLoader();
|
|
2707
|
+
init_PackMetadata();
|
|
2708
|
+
init_PackFormats();
|
|
2195
2709
|
ArchiveAssetSource = class _ArchiveAssetSource extends AssetSource {
|
|
2196
|
-
constructor(archiveProxy) {
|
|
2710
|
+
constructor(archiveProxy, options = {}) {
|
|
2197
2711
|
super();
|
|
2198
|
-
console.log(archiveProxy);
|
|
2199
2712
|
this._archiveProxy = archiveProxy;
|
|
2713
|
+
const copyFormat = (format2) => typeof format2 === "object" ? [format2[0], format2[1]] : format2;
|
|
2714
|
+
this.options = {
|
|
2715
|
+
resourcePackFormat: copyFormat(options.resourcePackFormat),
|
|
2716
|
+
dataPackFormat: copyFormat(options.dataPackFormat)
|
|
2717
|
+
};
|
|
2200
2718
|
}
|
|
2201
|
-
|
|
2202
|
-
|
|
2719
|
+
/** Creates a browser ZIP source from a Blob or File. */
|
|
2720
|
+
static blob(blob, options) {
|
|
2721
|
+
return new _ArchiveAssetSource(new BrowserArchiveProxy(blob), options);
|
|
2203
2722
|
}
|
|
2204
2723
|
get cacheId() {
|
|
2205
2724
|
const id = this._archiveProxy.id;
|
|
2206
|
-
|
|
2725
|
+
const formats = [this.options.resourcePackFormat ?? AssetLoader.version, this.options.dataPackFormat ?? AssetLoader.version];
|
|
2726
|
+
return id === void 0 ? void 0 : `archive-metadata:1:${id}:${JSON.stringify(formats)}`;
|
|
2207
2727
|
}
|
|
2208
2728
|
async getEntries() {
|
|
2209
|
-
return this._archiveProxy.getEntries()
|
|
2729
|
+
return this.entries ??= this._archiveProxy.getEntries().catch((error) => {
|
|
2730
|
+
this.entries = void 0;
|
|
2731
|
+
throw error;
|
|
2732
|
+
});
|
|
2210
2733
|
}
|
|
2211
2734
|
async getEntry(path) {
|
|
2212
2735
|
const entries = await this.getEntries();
|
|
2213
2736
|
return entries.find((e) => e.filename === path);
|
|
2214
2737
|
}
|
|
2738
|
+
async getMetadata(key) {
|
|
2739
|
+
try {
|
|
2740
|
+
return await (this.metadata ??= this.getEntry("pack.mcmeta").then(async (entry) => PackMetadata.parse(entry ? JSON.parse(await (await entry.getData()).text()) : {})).catch((error) => {
|
|
2741
|
+
this.metadata = void 0;
|
|
2742
|
+
throw error;
|
|
2743
|
+
}));
|
|
2744
|
+
} catch (cause) {
|
|
2745
|
+
throw new AssetLoadError(this, key, "pack.mcmeta", cause);
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
async blocks(key) {
|
|
2749
|
+
if (key.rootType !== "assets" && key.rootType !== "data") return false;
|
|
2750
|
+
const metadata = await this.getMetadata(key);
|
|
2751
|
+
const path = `${key.assetType !== void 0 ? key.assetType + "/" : ""}${key.getFullPath()}${key.extension}`;
|
|
2752
|
+
return metadata.blocks(key.namespace, path);
|
|
2753
|
+
}
|
|
2215
2754
|
async get(key, parser) {
|
|
2216
2755
|
const responseParser = HostedAssetSource.PARSER_MAP.get(parser);
|
|
2217
2756
|
return this.load(key, responseParser);
|
|
2218
2757
|
}
|
|
2219
2758
|
async load(key, parser) {
|
|
2220
2759
|
const path = `${this.assetBasePath(key)}${key.type !== void 0 ? key.type + "/" : ""}${key.path}${key.extension}`;
|
|
2760
|
+
const version2 = AssetLoader.version;
|
|
2761
|
+
let selectedPath = path;
|
|
2221
2762
|
let url;
|
|
2222
2763
|
try {
|
|
2223
|
-
|
|
2764
|
+
let directories = [];
|
|
2765
|
+
if (key.rootType === "assets" || key.rootType === "data") {
|
|
2766
|
+
const metadata = await this.getMetadata(key);
|
|
2767
|
+
if (metadata.hasOverlays) {
|
|
2768
|
+
const format2 = (key.rootType === "assets" ? this.options.resourcePackFormat : this.options.dataPackFormat) ?? await PackFormats.get(version2, key.rootType);
|
|
2769
|
+
directories = metadata.overlayDirectories(format2);
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
let entry;
|
|
2773
|
+
for (const directory of [...directories, ""]) {
|
|
2774
|
+
selectedPath = directory ? `${directory}/${path}` : path;
|
|
2775
|
+
entry = await this.getEntry(selectedPath);
|
|
2776
|
+
if (entry) break;
|
|
2777
|
+
}
|
|
2224
2778
|
if (!entry) {
|
|
2225
2779
|
return void 0;
|
|
2226
2780
|
}
|
|
@@ -2233,7 +2787,8 @@ var init_ArchiveAssetSource = __esm({
|
|
|
2233
2787
|
}
|
|
2234
2788
|
return await parser.parse(response);
|
|
2235
2789
|
} catch (cause) {
|
|
2236
|
-
|
|
2790
|
+
if (cause instanceof AssetLoadError) throw cause;
|
|
2791
|
+
throw new AssetLoadError(this, key, selectedPath, cause);
|
|
2237
2792
|
} finally {
|
|
2238
2793
|
if (url !== void 0) {
|
|
2239
2794
|
URL.revokeObjectURL(url);
|
|
@@ -2266,6 +2821,8 @@ var init_archive = __esm({
|
|
|
2266
2821
|
init_ArchiveEntry();
|
|
2267
2822
|
init_ArchiveProxy();
|
|
2268
2823
|
init_BrowserArchiveProxy();
|
|
2824
|
+
init_PackFormats();
|
|
2825
|
+
init_PackMetadata();
|
|
2269
2826
|
}
|
|
2270
2827
|
});
|
|
2271
2828
|
|
|
@@ -2292,6 +2849,7 @@ var init_AssetLoader = __esm({
|
|
|
2292
2849
|
FALLBACK_ROOT = "https://raw.githubusercontent.com/InventivetalentDev/minerender-fallback-assets/master";
|
|
2293
2850
|
AssetLoader = class {
|
|
2294
2851
|
static {
|
|
2852
|
+
/** Default asset base URL. Use {@link setVersion} to select a Minecraft version. */
|
|
2295
2853
|
this.ROOT = DEFAULT_ROOT;
|
|
2296
2854
|
}
|
|
2297
2855
|
static {
|
|
@@ -2304,8 +2862,9 @@ var init_AssetLoader = __esm({
|
|
|
2304
2862
|
static get version() {
|
|
2305
2863
|
return this.ROOT.substring(this.ROOT.lastIndexOf("/") + 1);
|
|
2306
2864
|
}
|
|
2307
|
-
|
|
2308
|
-
|
|
2865
|
+
/** Selects an mcasset.cloud version, updates the vanilla source, and clears in-memory caches. */
|
|
2866
|
+
static setVersion(version2) {
|
|
2867
|
+
this.ROOT = `https://assets.mcasset.cloud/${version2}`;
|
|
2309
2868
|
const source = new HostedAssetSource(this.ROOT, { retryDefaults: false });
|
|
2310
2869
|
const index = this._SOURCES.findIndex((s) => s.key === "mcassets");
|
|
2311
2870
|
if (index !== -1) {
|
|
@@ -2337,6 +2896,13 @@ var init_AssetLoader = __esm({
|
|
|
2337
2896
|
return scope ? `${scope}
|
|
2338
2897
|
${key}` : key;
|
|
2339
2898
|
}
|
|
2899
|
+
/**
|
|
2900
|
+
* Registers a source at the highest priority.
|
|
2901
|
+
* Call {@link Caching.clear} after changing sources to discard previously loaded assets.
|
|
2902
|
+
*
|
|
2903
|
+
* @param key - Name used to replace or remove this source.
|
|
2904
|
+
* @param override - Removes the first source with this name before adding the new one.
|
|
2905
|
+
*/
|
|
2340
2906
|
static addSource(key, source, override = true) {
|
|
2341
2907
|
if (override) {
|
|
2342
2908
|
const existing = this.removeSource(key);
|
|
@@ -2347,6 +2913,7 @@ ${key}` : key;
|
|
|
2347
2913
|
this._SOURCES.unshift({ key, source });
|
|
2348
2914
|
console.log(p4, "Added AssetSource", key);
|
|
2349
2915
|
}
|
|
2916
|
+
/** Removes and returns the first source with this name. Call {@link Caching.clear} to reload assets. */
|
|
2350
2917
|
static removeSource(key) {
|
|
2351
2918
|
const index = this._SOURCES.findIndex((s) => s.key === key);
|
|
2352
2919
|
if (index != -1) {
|
|
@@ -2361,14 +2928,16 @@ ${key}` : key;
|
|
|
2361
2928
|
this.addSource("mcassets-fallback", new HostedAssetSource(FALLBACK_ROOT, { retryDefaults: false }));
|
|
2362
2929
|
this.addSource("mcassets", new HostedAssetSource(this.ROOT, { retryDefaults: false }));
|
|
2363
2930
|
}
|
|
2931
|
+
/** Loads from all sources and returns defined results in priority order. Any source failure rejects the call. */
|
|
2364
2932
|
static async getAll(key, parser) {
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2933
|
+
const sources = [...this._SOURCES];
|
|
2934
|
+
const results = [];
|
|
2935
|
+
for (const { source } of sources) {
|
|
2936
|
+
const asset2 = await source.get(key, parser);
|
|
2937
|
+
if (asset2 != void 0) results.push(asset2);
|
|
2938
|
+
if (await source.blocks(key)) break;
|
|
2368
2939
|
}
|
|
2369
|
-
return
|
|
2370
|
-
return results.filter((r) => r != void 0);
|
|
2371
|
-
});
|
|
2940
|
+
return results;
|
|
2372
2941
|
}
|
|
2373
2942
|
/** Returns the first defined result in source-priority order, without merging assets. */
|
|
2374
2943
|
static async get(key, parser) {
|
|
@@ -2389,13 +2958,17 @@ ${key}` : key;
|
|
|
2389
2958
|
/** Tries each path within a source before considering lower-priority sources. */
|
|
2390
2959
|
static async getFirst(keys, parser) {
|
|
2391
2960
|
const sources = [...this._SOURCES];
|
|
2961
|
+
let remaining = [...keys];
|
|
2392
2962
|
for (const source of sources) {
|
|
2393
|
-
for (const key of
|
|
2963
|
+
for (const key of remaining) {
|
|
2394
2964
|
const result = await source.source.get(key, parser);
|
|
2395
2965
|
if (result !== void 0) {
|
|
2396
2966
|
return { key, asset: result };
|
|
2397
2967
|
}
|
|
2398
2968
|
}
|
|
2969
|
+
const blocked = await Promise.all(remaining.map((key) => source.source.blocks(key)));
|
|
2970
|
+
remaining = remaining.filter((_, index) => !blocked[index]);
|
|
2971
|
+
if (!remaining.length) break;
|
|
2399
2972
|
}
|
|
2400
2973
|
return void 0;
|
|
2401
2974
|
}
|
|
@@ -2417,14 +2990,15 @@ var init_ModelTextures = __esm({
|
|
|
2417
2990
|
static get PERSISTENT_META_CACHE() {
|
|
2418
2991
|
return this._persistentMetaCache ??= PersistentCache.open("minerender-texturemeta");
|
|
2419
2992
|
}
|
|
2993
|
+
/** Loads and decodes a texture into a readable canvas, or returns `undefined` when missing. */
|
|
2420
2994
|
static async get(key) {
|
|
2421
2995
|
const keyStr = key.serialize();
|
|
2422
2996
|
const pending = this.preload(key);
|
|
2423
2997
|
const cached2 = Caching.textureAssetCache.getIfPresent(keyStr);
|
|
2424
2998
|
try {
|
|
2425
|
-
const
|
|
2426
|
-
if (
|
|
2427
|
-
return await ImageLoader.infoToCanvasData(
|
|
2999
|
+
const asset2 = await pending;
|
|
3000
|
+
if (asset2) {
|
|
3001
|
+
return await ImageLoader.infoToCanvasData(asset2);
|
|
2428
3002
|
}
|
|
2429
3003
|
return void 0;
|
|
2430
3004
|
} catch (err) {
|
|
@@ -2434,16 +3008,18 @@ var init_ModelTextures = __esm({
|
|
|
2434
3008
|
throw err;
|
|
2435
3009
|
}
|
|
2436
3010
|
}
|
|
3011
|
+
/** Fetches and caches encoded texture bytes without decoding the pixels. */
|
|
2437
3012
|
static async preload(key) {
|
|
2438
3013
|
const keyStr = key.serialize();
|
|
2439
3014
|
return Caching.textureAssetCache.get(keyStr, (k) => {
|
|
2440
|
-
return AssetLoader.get(key, "image" /* IMAGE */).then((
|
|
2441
|
-
if (
|
|
2442
|
-
|
|
2443
|
-
return
|
|
3015
|
+
return AssetLoader.get(key, "image" /* IMAGE */).then((asset2) => {
|
|
3016
|
+
if (asset2)
|
|
3017
|
+
asset2.key = key;
|
|
3018
|
+
return asset2;
|
|
2444
3019
|
});
|
|
2445
3020
|
});
|
|
2446
3021
|
}
|
|
3022
|
+
/** Loads a texture's `.mcmeta` file, or returns `undefined` when no metadata file exists. */
|
|
2447
3023
|
static async getMeta(key) {
|
|
2448
3024
|
if (!key.extension || !key.extension.endsWith(".mcmeta")) {
|
|
2449
3025
|
key = Object.assign(new AssetKey("", ""), key);
|
|
@@ -2452,14 +3028,15 @@ var init_ModelTextures = __esm({
|
|
|
2452
3028
|
const keyStr = key.serialize();
|
|
2453
3029
|
return Caching.textureMetaCache.get(keyStr, (k) => {
|
|
2454
3030
|
return this.PERSISTENT_META_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
|
|
2455
|
-
return AssetLoader.get(key, "meta" /* META */).then((
|
|
2456
|
-
if (
|
|
2457
|
-
|
|
2458
|
-
return
|
|
3031
|
+
return AssetLoader.get(key, "meta" /* META */).then((asset2) => {
|
|
3032
|
+
if (asset2)
|
|
3033
|
+
asset2.key = key;
|
|
3034
|
+
return asset2;
|
|
2459
3035
|
});
|
|
2460
3036
|
});
|
|
2461
3037
|
});
|
|
2462
3038
|
}
|
|
3039
|
+
/** Clears persisted texture metadata. Use {@link Caching.clear} to discard in-memory textures and metadata. */
|
|
2463
3040
|
static async clearCache() {
|
|
2464
3041
|
await this.PERSISTENT_META_CACHE.clear();
|
|
2465
3042
|
}
|
|
@@ -2547,6 +3124,7 @@ var init_Ticker = __esm({
|
|
|
2547
3124
|
}
|
|
2548
3125
|
return handle;
|
|
2549
3126
|
}
|
|
3127
|
+
/** Starts or resumes the shared tick loop. Repeated calls have no effect. */
|
|
2550
3128
|
static start() {
|
|
2551
3129
|
if (this.interval) return;
|
|
2552
3130
|
this.interval = this.schedule(() => {
|
|
@@ -2569,6 +3147,7 @@ var init_Ticker = __esm({
|
|
|
2569
3147
|
_Ticker.fiveSecondTicks = 0;
|
|
2570
3148
|
}, 1e3 * 5);
|
|
2571
3149
|
}
|
|
3150
|
+
/** Pauses the loop while retaining callbacks for a later {@link start}. */
|
|
2572
3151
|
static stop() {
|
|
2573
3152
|
if (this.interval) clearInterval(this.interval);
|
|
2574
3153
|
if (this.oneSecondTicksTracker) clearInterval(this.oneSecondTicksTracker);
|
|
@@ -2577,12 +3156,14 @@ var init_Ticker = __esm({
|
|
|
2577
3156
|
this.oneSecondTicksTracker = void 0;
|
|
2578
3157
|
this.fiveSecondTicksTracker = void 0;
|
|
2579
3158
|
}
|
|
3159
|
+
/** Registers a callback, starts the loop if needed, and returns an ID for {@link remove}. */
|
|
2580
3160
|
static add(tick) {
|
|
2581
3161
|
this.start();
|
|
2582
3162
|
const c = this.counter++;
|
|
2583
3163
|
this._tickers.set(c, tick);
|
|
2584
3164
|
return c;
|
|
2585
3165
|
}
|
|
3166
|
+
/** Removes a callback by ID and stops the timers when no callbacks remain. */
|
|
2586
3167
|
static remove(c) {
|
|
2587
3168
|
if (typeof c === "number") this._tickers.delete(c);
|
|
2588
3169
|
if (!this._tickers.size) this.stop();
|
|
@@ -2596,6 +3177,7 @@ var init_Ticker = __esm({
|
|
|
2596
3177
|
static get tpsFiveSeconds() {
|
|
2597
3178
|
return this.fiveSecondTps;
|
|
2598
3179
|
}
|
|
3180
|
+
/** Stops the loop and removes all registered callbacks. */
|
|
2599
3181
|
static dispose() {
|
|
2600
3182
|
this.stop();
|
|
2601
3183
|
this._tickers.clear();
|
|
@@ -2642,6 +3224,7 @@ var init_TextureAtlas = __esm({
|
|
|
2642
3224
|
}
|
|
2643
3225
|
};
|
|
2644
3226
|
}
|
|
3227
|
+
/** Copies pixels for a texture variable from its current atlas rectangle. */
|
|
2645
3228
|
getData(texture) {
|
|
2646
3229
|
const pos = this.positions[texture];
|
|
2647
3230
|
const size = this.sizes[texture];
|
|
@@ -2854,6 +3437,37 @@ var init_UVMapper = __esm({
|
|
|
2854
3437
|
static facesToUv(faces, originalTextureSize, actualTextureSize) {
|
|
2855
3438
|
return new Float32BufferAttribute2(this.facesToUvArray(faces, originalTextureSize, actualTextureSize), 2);
|
|
2856
3439
|
}
|
|
3440
|
+
/** Bounds texture sampling to texel centres without changing cropped face UVs. */
|
|
3441
|
+
static setAtlasUvBounds(geometry, faces, atlas) {
|
|
3442
|
+
const uv = geometry.getAttribute("uv");
|
|
3443
|
+
const bounds = new Float32Array(uv.count * 4);
|
|
3444
|
+
for (const [faceIndex, faceName] of CUBE_FACES.entries()) {
|
|
3445
|
+
const texture = faces[faceName]?.texture?.substring(1);
|
|
3446
|
+
const position = texture && atlas.positions[texture];
|
|
3447
|
+
const size = texture && atlas.sizes[texture];
|
|
3448
|
+
let minU = Infinity, minV = Infinity, maxU = -Infinity, maxV = -Infinity;
|
|
3449
|
+
if (position && size) {
|
|
3450
|
+
minU = position[0] / atlas.image.width;
|
|
3451
|
+
maxU = (position[0] + size[0]) / atlas.image.width;
|
|
3452
|
+
minV = 1 - (position[1] + size[1]) / atlas.image.height;
|
|
3453
|
+
maxV = 1 - position[1] / atlas.image.height;
|
|
3454
|
+
} else {
|
|
3455
|
+
for (let corner = 0; corner < 4; corner++) {
|
|
3456
|
+
const index = faceIndex * 4 + corner;
|
|
3457
|
+
minU = Math.min(minU, uv.getX(index));
|
|
3458
|
+
minV = Math.min(minV, uv.getY(index));
|
|
3459
|
+
maxU = Math.max(maxU, uv.getX(index));
|
|
3460
|
+
maxV = Math.max(maxV, uv.getY(index));
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
const insetU = Math.min(0.5 / atlas.image.width, (maxU - minU) / 2);
|
|
3464
|
+
const insetV = Math.min(0.5 / atlas.image.height, (maxV - minV) / 2);
|
|
3465
|
+
for (let corner = 0; corner < 4; corner++) {
|
|
3466
|
+
bounds.set([minU + insetU, minV + insetV, maxU - insetU, maxV - insetV], (faceIndex * 4 + corner) * 4);
|
|
3467
|
+
}
|
|
3468
|
+
}
|
|
3469
|
+
geometry.setAttribute("uvBounds", new Float32BufferAttribute2(bounds, 4));
|
|
3470
|
+
}
|
|
2857
3471
|
static lockUvs(geometry, faces, atlas, rotation) {
|
|
2858
3472
|
const uv = geometry.getAttribute("uv");
|
|
2859
3473
|
for (let faceIndex = 0; faceIndex < CUBE_FACES.length; faceIndex++) {
|
|
@@ -2890,6 +3504,7 @@ var init_UVMapper = __esm({
|
|
|
2890
3504
|
}
|
|
2891
3505
|
uv.needsUpdate = true;
|
|
2892
3506
|
}
|
|
3507
|
+
/** Returns the shared atlas for a keyed, merged model, creating it on first use. */
|
|
2893
3508
|
static async getAtlas(model) {
|
|
2894
3509
|
const keyStr = model.key.serialize();
|
|
2895
3510
|
return Caching.modelTextureAtlasCache.get(keyStr, (k) => {
|
|
@@ -2960,6 +3575,11 @@ var init_UVMapper = __esm({
|
|
|
2960
3575
|
}
|
|
2961
3576
|
return [tl, tr, bl, br];
|
|
2962
3577
|
}
|
|
3578
|
+
/**
|
|
3579
|
+
* Combines model textures and animation metadata into an atlas.
|
|
3580
|
+
* Mapped UVs are stored on copies of the model's elements, available through the returned atlas.
|
|
3581
|
+
* Returns `undefined` for models without a texture map.
|
|
3582
|
+
*/
|
|
2963
3583
|
static async createAtlas(originalModel) {
|
|
2964
3584
|
const textureMap = {};
|
|
2965
3585
|
const metaMap = {};
|
|
@@ -2980,12 +3600,12 @@ var init_UVMapper = __esm({
|
|
|
2980
3600
|
} else {
|
|
2981
3601
|
uniqueTextureNames.push(textureKey);
|
|
2982
3602
|
const assetKey = AssetKey.parse("textures", textureValue, model.key);
|
|
2983
|
-
promises.push(ModelTextures.get(assetKey).then((
|
|
2984
|
-
if (!
|
|
3603
|
+
promises.push(ModelTextures.get(assetKey).then((asset2) => {
|
|
3604
|
+
if (!asset2) {
|
|
2985
3605
|
console.warn(p5, "Missing texture", assetKey);
|
|
2986
3606
|
return;
|
|
2987
3607
|
}
|
|
2988
|
-
textureMap[textureKey] = new WrappedImage(
|
|
3608
|
+
textureMap[textureKey] = new WrappedImage(asset2);
|
|
2989
3609
|
}));
|
|
2990
3610
|
promises.push(ModelTextures.getMeta(assetKey).then((meta) => {
|
|
2991
3611
|
metaMap[textureKey] = meta;
|
|
@@ -3324,14 +3944,15 @@ var init_Entities = __esm({
|
|
|
3324
3944
|
init_MineRenderError();
|
|
3325
3945
|
init_ModelTextures();
|
|
3326
3946
|
Entities = class {
|
|
3947
|
+
/** Lists model paths in the `minecraft` namespace, including subdirectories and excluding `.json`. */
|
|
3327
3948
|
static async getEntityList() {
|
|
3328
3949
|
const collect = async (path) => {
|
|
3329
3950
|
const prefix2 = path ? `${path}/` : "";
|
|
3330
3951
|
const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json");
|
|
3331
|
-
const
|
|
3332
|
-
if (!
|
|
3333
|
-
const files =
|
|
3334
|
-
const children = await Promise.all(
|
|
3952
|
+
const list2 = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
|
|
3953
|
+
if (!list2) return [];
|
|
3954
|
+
const files = list2.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
|
|
3955
|
+
const children = await Promise.all(list2.directories.map((directory) => collect(`${prefix2}${directory}`)));
|
|
3335
3956
|
return [...files, ...children.flat()];
|
|
3336
3957
|
};
|
|
3337
3958
|
return collect("");
|
|
@@ -3341,6 +3962,7 @@ var init_Entities = __esm({
|
|
|
3341
3962
|
const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
|
|
3342
3963
|
return Caching.entityModelCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
|
|
3343
3964
|
}
|
|
3965
|
+
/** Returns selectable geometry-layer names, or an empty list when the model is missing. */
|
|
3344
3966
|
static async getLayerList(modelKey) {
|
|
3345
3967
|
return Object.keys((await this.getModelFile(modelKey))?.layers ?? {});
|
|
3346
3968
|
}
|
|
@@ -3348,6 +3970,15 @@ var init_Entities = __esm({
|
|
|
3348
3970
|
static async getPassList(modelKey) {
|
|
3349
3971
|
return (await this.getModelFile(modelKey))?.passes ?? [];
|
|
3350
3972
|
}
|
|
3973
|
+
/**
|
|
3974
|
+
* Loads the layers and textures to pass to {@link MineRenderScene.addEntity}.
|
|
3975
|
+
* By default, selects `main`, unconditional passes, and passes enabled by `options.when`.
|
|
3976
|
+
*
|
|
3977
|
+
* @param modelKey - Dataset model, such as `new BasicAssetKey("minecraft", "zombie")`.
|
|
3978
|
+
* @param textureKey - Texture override for the first selected layer.
|
|
3979
|
+
* @param options - Layer selection, conditional passes, and per-layer texture overrides.
|
|
3980
|
+
* @returns The selected model, or `undefined` when its dataset file is missing.
|
|
3981
|
+
*/
|
|
3351
3982
|
static async getEntity(modelKey, textureKey, options) {
|
|
3352
3983
|
const model = await this.getModelFile(modelKey);
|
|
3353
3984
|
if (!model) return void 0;
|
|
@@ -3393,6 +4024,7 @@ var init_Entities = __esm({
|
|
|
3393
4024
|
const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
|
|
3394
4025
|
return file?.animations;
|
|
3395
4026
|
}
|
|
4027
|
+
/** Finds a fallback texture using the model path, its directory, and vanilla variant data. */
|
|
3396
4028
|
static async resolveTexture(modelKey) {
|
|
3397
4029
|
const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
|
|
3398
4030
|
const root = isAssetKey(modelKey) ? modelKey.root : void 0;
|
|
@@ -3404,8 +4036,8 @@ var init_Entities = __esm({
|
|
|
3404
4036
|
if (await ModelTextures.get(texture2)) return texture2;
|
|
3405
4037
|
}
|
|
3406
4038
|
const listKey = new AssetKey(modelKey.namespace, "_list", `${name}_variant`, void 0, "data", ".json", root);
|
|
3407
|
-
const
|
|
3408
|
-
const files =
|
|
4039
|
+
const list2 = await AssetLoader.get(listKey, "list" /* LIST */);
|
|
4040
|
+
const files = list2?.files.filter((file2) => file2.endsWith(".json") && file2 !== "_list.json");
|
|
3409
4041
|
const file = files?.find((file2) => file2 === "temperate.json") ?? files?.[0];
|
|
3410
4042
|
if (!file) return void 0;
|
|
3411
4043
|
const variantKey = new AssetKey(modelKey.namespace, file.slice(0, -5), `${name}_variant`, void 0, "data", ".json", root);
|
|
@@ -3510,27 +4142,27 @@ var init_EntityAnimationPlayer = __esm({
|
|
|
3510
4142
|
for (const layer of layers) {
|
|
3511
4143
|
const objects = [];
|
|
3512
4144
|
if (poses) {
|
|
3513
|
-
layer.traverse((
|
|
3514
|
-
if (poses.has(
|
|
4145
|
+
layer.traverse((object3) => {
|
|
4146
|
+
if (poses.has(object3)) objects.push(object3);
|
|
3515
4147
|
});
|
|
3516
4148
|
} else {
|
|
3517
4149
|
for (const bone of Object.keys(animation.bones)) {
|
|
3518
|
-
const
|
|
3519
|
-
if (
|
|
4150
|
+
const object3 = layer.getObjectByName(`group:${bone}`);
|
|
4151
|
+
if (object3) objects.push(object3);
|
|
3520
4152
|
}
|
|
3521
4153
|
}
|
|
3522
|
-
for (const
|
|
3523
|
-
const bone =
|
|
3524
|
-
const pose = poses?.get(
|
|
4154
|
+
for (const object3 of objects) {
|
|
4155
|
+
const bone = object3.name.slice("group:".length);
|
|
4156
|
+
const pose = poses?.get(object3);
|
|
3525
4157
|
this.parts.push({
|
|
3526
4158
|
bone,
|
|
3527
|
-
object,
|
|
3528
|
-
position:
|
|
3529
|
-
rotation:
|
|
3530
|
-
scale:
|
|
3531
|
-
basePosition: pose ? new Vector35(...pose.offset) :
|
|
3532
|
-
baseRotation: pose ? new Euler3(...pose.rotation, "ZYX") :
|
|
3533
|
-
baseScale: pose ? new Vector35(...pose.scale ?? [1, 1, 1]) :
|
|
4159
|
+
object: object3,
|
|
4160
|
+
position: object3.position.clone(),
|
|
4161
|
+
rotation: object3.rotation.clone(),
|
|
4162
|
+
scale: object3.scale.clone(),
|
|
4163
|
+
basePosition: pose ? new Vector35(...pose.offset) : object3.position.clone(),
|
|
4164
|
+
baseRotation: pose ? new Euler3(...pose.rotation, "ZYX") : object3.rotation.clone(),
|
|
4165
|
+
baseScale: pose ? new Vector35(...pose.scale ?? [1, 1, 1]) : object3.scale.clone()
|
|
3534
4166
|
});
|
|
3535
4167
|
}
|
|
3536
4168
|
}
|
|
@@ -3553,12 +4185,14 @@ var init_EntityAnimationPlayer = __esm({
|
|
|
3553
4185
|
this._animation = void 0;
|
|
3554
4186
|
this._time = 0;
|
|
3555
4187
|
}
|
|
4188
|
+
/** Applies the pose at an absolute playback time. Returns `false` when no clip is active. */
|
|
3556
4189
|
setTime(seconds) {
|
|
3557
4190
|
if (!this._animation) return false;
|
|
3558
4191
|
this._time = seconds;
|
|
3559
4192
|
this.apply();
|
|
3560
4193
|
return true;
|
|
3561
4194
|
}
|
|
4195
|
+
/** Advances playback by elapsed seconds multiplied by the speed passed to {@link play}. */
|
|
3562
4196
|
advance(deltaSeconds) {
|
|
3563
4197
|
return this.setTime(this._time + deltaSeconds * this.speed);
|
|
3564
4198
|
}
|
|
@@ -3596,6 +4230,7 @@ var init_InstanceReference = __esm({
|
|
|
3596
4230
|
if (!this.instanceable.isInstanceActive(this.index, this)) throw new MineRenderError("Instance has been removed");
|
|
3597
4231
|
return this.instanceable;
|
|
3598
4232
|
}
|
|
4233
|
+
/** Releases this placement's slot for reuse. Repeated calls have no effect. */
|
|
3599
4234
|
removeFromScene() {
|
|
3600
4235
|
if (this.instanceable.isInstanceActive(this.index, this)) this.instanceable.removeInstanceAt(this.index);
|
|
3601
4236
|
}
|
|
@@ -3608,9 +4243,11 @@ var init_InstanceReference = __esm({
|
|
|
3608
4243
|
setMatrix(matrix) {
|
|
3609
4244
|
this.activeInstanceable.setMatrixAt(this.index, matrix);
|
|
3610
4245
|
}
|
|
4246
|
+
/** Hides or restores this placement while preserving its transform and allocated slot. */
|
|
3611
4247
|
setVisible(visible) {
|
|
3612
4248
|
this.activeInstanceable.setInstanceVisibleAt(this.index, visible);
|
|
3613
4249
|
}
|
|
4250
|
+
/** Updates the supplied transform components. Positions use scene units and rotations use radians. */
|
|
3614
4251
|
setPositionRotationScale(position, rotation, scale) {
|
|
3615
4252
|
this.activeInstanceable.setPositionRotationScaleAt(this.index, position, rotation, scale);
|
|
3616
4253
|
}
|
|
@@ -3687,6 +4324,7 @@ var init_SceneObject = __esm({
|
|
|
3687
4324
|
}
|
|
3688
4325
|
async init() {
|
|
3689
4326
|
}
|
|
4327
|
+
/** Marks ancestor scenes for redraw and emits a `change` event. */
|
|
3690
4328
|
notifyDirty() {
|
|
3691
4329
|
this.traverseAncestors((parent) => {
|
|
3692
4330
|
const scene = parent;
|
|
@@ -3722,15 +4360,17 @@ var init_SceneObject = __esm({
|
|
|
3722
4360
|
return obj;
|
|
3723
4361
|
}
|
|
3724
4362
|
/**
|
|
3725
|
-
*
|
|
4363
|
+
* Finds a named part, such as `head`, without the internal `group:` prefix.
|
|
4364
|
+
* Returns `undefined` when the group does not exist.
|
|
3726
4365
|
*/
|
|
3727
4366
|
getGroupByName(name) {
|
|
3728
4367
|
return this.getObjectByName(`group:${name}`);
|
|
3729
4368
|
}
|
|
3730
4369
|
/**
|
|
3731
|
-
*
|
|
3732
|
-
* @param name name
|
|
3733
|
-
* @param visible
|
|
4370
|
+
* Changes a named group's visibility and requests a redraw.
|
|
4371
|
+
* @param name - Part name without the `group:` prefix.
|
|
4372
|
+
* @param visible - Visibility to assign. Omit it to toggle the current value.
|
|
4373
|
+
* @returns The resulting visibility, or `false` if the group does not exist.
|
|
3734
4374
|
*/
|
|
3735
4375
|
toggleGroupVisibility(name, visible) {
|
|
3736
4376
|
return this.toggleObjectVisibility(this.getGroupByName(name), visible);
|
|
@@ -3781,15 +4421,16 @@ var init_SceneObject = __esm({
|
|
|
3781
4421
|
return mesh;
|
|
3782
4422
|
}
|
|
3783
4423
|
/**
|
|
3784
|
-
*
|
|
4424
|
+
* Finds a named mesh without the internal `mesh:` prefix, or returns `undefined`.
|
|
3785
4425
|
*/
|
|
3786
4426
|
getMeshByName(name) {
|
|
3787
4427
|
return this.getObjectByName(`mesh:${name}`);
|
|
3788
4428
|
}
|
|
3789
4429
|
/**
|
|
3790
|
-
*
|
|
3791
|
-
* @param name name
|
|
3792
|
-
* @param visible
|
|
4430
|
+
* Changes a named mesh's visibility and requests a redraw.
|
|
4431
|
+
* @param name - Mesh name without the `mesh:` prefix.
|
|
4432
|
+
* @param visible - Visibility to assign. Omit it to toggle the current value.
|
|
4433
|
+
* @returns The resulting visibility, or `false` if the mesh does not exist.
|
|
3793
4434
|
*/
|
|
3794
4435
|
toggleMeshVisibility(name, visible) {
|
|
3795
4436
|
return this.toggleObjectVisibility(this.getMeshByName(name), visible);
|
|
@@ -3835,12 +4476,14 @@ var init_SceneObject = __esm({
|
|
|
3835
4476
|
get isInstanced() {
|
|
3836
4477
|
return this._isInstanced;
|
|
3837
4478
|
}
|
|
4479
|
+
/** Number of live placements, excluding released instance slots. */
|
|
3838
4480
|
get instanceCounter() {
|
|
3839
4481
|
return this._instanceCounter;
|
|
3840
4482
|
}
|
|
3841
4483
|
constructInstanceReference(i) {
|
|
3842
4484
|
return new InstanceReference(this, i);
|
|
3843
4485
|
}
|
|
4486
|
+
/** Allocates a placement of this instanced object, reusing a released slot when available. */
|
|
3844
4487
|
nextInstance() {
|
|
3845
4488
|
const mesh = this.instanceMesh;
|
|
3846
4489
|
if (!mesh) throw new MineRenderError("Object is not instanced");
|
|
@@ -3885,10 +4528,10 @@ var init_SceneObject = __esm({
|
|
|
3885
4528
|
return new InstancedBufferAttribute(array, attribute.itemSize, attribute.normalized, attribute.meshPerAttribute).setUsage(attribute.usage);
|
|
3886
4529
|
};
|
|
3887
4530
|
const matrix = grow(mesh.instanceMatrix, 0);
|
|
3888
|
-
const
|
|
4531
|
+
const color2 = mesh.instanceColor ? grow(mesh.instanceColor, 1) : null;
|
|
3889
4532
|
mesh.dispose();
|
|
3890
4533
|
mesh.instanceMatrix = matrix;
|
|
3891
|
-
mesh.instanceColor =
|
|
4534
|
+
mesh.instanceColor = color2;
|
|
3892
4535
|
}
|
|
3893
4536
|
*activeInstanceIndices() {
|
|
3894
4537
|
if (this.instanceMesh) {
|
|
@@ -3987,6 +4630,7 @@ var init_SceneObject = __esm({
|
|
|
3987
4630
|
vector.setFromMatrixScale(matrix);
|
|
3988
4631
|
return vector;
|
|
3989
4632
|
}
|
|
4633
|
+
/** Updates supplied transform components. For a shared model, affects every live instance. */
|
|
3990
4634
|
setPositionRotationScale(position, rotation, scale) {
|
|
3991
4635
|
if (this.isInstanced) {
|
|
3992
4636
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -4005,6 +4649,7 @@ var init_SceneObject = __esm({
|
|
|
4005
4649
|
}
|
|
4006
4650
|
this.notifyDirty();
|
|
4007
4651
|
}
|
|
4652
|
+
/** Sets position in scene units. For a shared model, updates every live instance. */
|
|
4008
4653
|
setPosition(position) {
|
|
4009
4654
|
if (this.isInstanced) {
|
|
4010
4655
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -4022,6 +4667,7 @@ var init_SceneObject = __esm({
|
|
|
4022
4667
|
return this.position;
|
|
4023
4668
|
}
|
|
4024
4669
|
}
|
|
4670
|
+
/** Sets rotation in radians. For a shared model, updates every live instance. */
|
|
4025
4671
|
setRotation(rotation) {
|
|
4026
4672
|
if (this.isInstanced) {
|
|
4027
4673
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -4039,6 +4685,7 @@ var init_SceneObject = __esm({
|
|
|
4039
4685
|
return this.rotation;
|
|
4040
4686
|
}
|
|
4041
4687
|
}
|
|
4688
|
+
/** Sets scale factors. For a shared model, updates every live instance. */
|
|
4042
4689
|
setScale(scale) {
|
|
4043
4690
|
if (this.isInstanced) {
|
|
4044
4691
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -4057,15 +4704,15 @@ var init_SceneObject = __esm({
|
|
|
4057
4704
|
}
|
|
4058
4705
|
}
|
|
4059
4706
|
//</editor-fold>
|
|
4060
|
-
toggleObjectVisibility(
|
|
4061
|
-
if (
|
|
4707
|
+
toggleObjectVisibility(object3, visible) {
|
|
4708
|
+
if (object3) {
|
|
4062
4709
|
if (typeof visible !== "undefined") {
|
|
4063
|
-
|
|
4710
|
+
object3.visible = visible;
|
|
4064
4711
|
} else {
|
|
4065
|
-
|
|
4712
|
+
object3.visible = !object3.visible;
|
|
4066
4713
|
}
|
|
4067
4714
|
this.notifyDirty();
|
|
4068
|
-
return
|
|
4715
|
+
return object3.visible;
|
|
4069
4716
|
}
|
|
4070
4717
|
this.notifyDirty();
|
|
4071
4718
|
return false;
|
|
@@ -4076,6 +4723,7 @@ var init_SceneObject = __esm({
|
|
|
4076
4723
|
this.notifyDirty();
|
|
4077
4724
|
super.dispose();
|
|
4078
4725
|
}
|
|
4726
|
+
/** Releases instance slots and removes children, disposing children that implement Disposable. */
|
|
4079
4727
|
disposeAndRemoveAllChildren() {
|
|
4080
4728
|
if (this.instanceMesh) {
|
|
4081
4729
|
if (this._scene) this._scene.stats.instanceCount -= this.instanceReferences.size;
|
|
@@ -4098,6 +4746,7 @@ var init_SceneObject = __esm({
|
|
|
4098
4746
|
}
|
|
4099
4747
|
this.notifyDirty();
|
|
4100
4748
|
}
|
|
4749
|
+
/** Detaches the object from its parent and requests a redraw. */
|
|
4101
4750
|
removeFromScene() {
|
|
4102
4751
|
this.notifyDirty();
|
|
4103
4752
|
this.removeFromParent();
|
|
@@ -4249,12 +4898,15 @@ var init_EntityObject = __esm({
|
|
|
4249
4898
|
if (this.animationPlayers.length) this.notifyDirty();
|
|
4250
4899
|
}
|
|
4251
4900
|
//</editor-fold>
|
|
4901
|
+
/** Finds a selected draw by its layer key, such as `main` or `main#2`, or returns `undefined`. */
|
|
4252
4902
|
getLayerGroup(name) {
|
|
4253
4903
|
return super.getGroupByName(`layer:${name}`);
|
|
4254
4904
|
}
|
|
4905
|
+
/** Finds a part name without `group:`. Supply `layerName` to restrict the search to one draw. */
|
|
4255
4906
|
getGroupByName(name, layerName) {
|
|
4256
4907
|
return layerName === void 0 ? super.getGroupByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`group:${name}`);
|
|
4257
4908
|
}
|
|
4909
|
+
/** Finds a mesh name without `mesh:`. Supply `layerName` to restrict the search to one draw. */
|
|
4258
4910
|
getMeshByName(name, layerName) {
|
|
4259
4911
|
return layerName === void 0 ? super.getMeshByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`mesh:${name}`);
|
|
4260
4912
|
}
|
|
@@ -4356,8 +5008,8 @@ var init_EntityObject = __esm({
|
|
|
4356
5008
|
this.scrollMaterials.push({ material: mat, offset: mat.map.offset, speed: scroll });
|
|
4357
5009
|
}
|
|
4358
5010
|
}
|
|
4359
|
-
this.getLayerGroup(name)?.traverse((
|
|
4360
|
-
if (isMesh(
|
|
5011
|
+
this.getLayerGroup(name)?.traverse((object3) => {
|
|
5012
|
+
if (isMesh(object3)) object3.material = mat;
|
|
4361
5013
|
});
|
|
4362
5014
|
}));
|
|
4363
5015
|
if (!this.children.length) {
|
|
@@ -4696,8 +5348,8 @@ function toTrianglesDrawMode(geometry, drawMode) {
|
|
|
4696
5348
|
return geometry;
|
|
4697
5349
|
}
|
|
4698
5350
|
}
|
|
4699
|
-
function computeMorphedAttributes(
|
|
4700
|
-
if (
|
|
5351
|
+
function computeMorphedAttributes(object3) {
|
|
5352
|
+
if (object3.geometry.isBufferGeometry !== true) {
|
|
4701
5353
|
console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
|
|
4702
5354
|
return null;
|
|
4703
5355
|
}
|
|
@@ -4710,11 +5362,11 @@ function computeMorphedAttributes(object) {
|
|
|
4710
5362
|
const _morphA = new Vector310();
|
|
4711
5363
|
const _morphB = new Vector310();
|
|
4712
5364
|
const _morphC = new Vector310();
|
|
4713
|
-
function _calculateMorphedAttributeData(
|
|
5365
|
+
function _calculateMorphedAttributeData(object4, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
|
|
4714
5366
|
_vA.fromBufferAttribute(attribute, a2);
|
|
4715
5367
|
_vB.fromBufferAttribute(attribute, b2);
|
|
4716
5368
|
_vC.fromBufferAttribute(attribute, c2);
|
|
4717
|
-
const morphInfluences =
|
|
5369
|
+
const morphInfluences = object4.morphTargetInfluences;
|
|
4718
5370
|
if (material2.morphTargets && morphAttribute && morphInfluences) {
|
|
4719
5371
|
_morphA.set(0, 0, 0);
|
|
4720
5372
|
_morphB.set(0, 0, 0);
|
|
@@ -4740,10 +5392,10 @@ function computeMorphedAttributes(object) {
|
|
|
4740
5392
|
_vB.add(_morphB);
|
|
4741
5393
|
_vC.add(_morphC);
|
|
4742
5394
|
}
|
|
4743
|
-
if (
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
5395
|
+
if (object4.isSkinnedMesh) {
|
|
5396
|
+
object4.boneTransform(a2, _vA);
|
|
5397
|
+
object4.boneTransform(b2, _vB);
|
|
5398
|
+
object4.boneTransform(c2, _vC);
|
|
4747
5399
|
}
|
|
4748
5400
|
modifiedAttributeArray[a2 * 3 + 0] = _vA.x;
|
|
4749
5401
|
modifiedAttributeArray[a2 * 3 + 1] = _vA.y;
|
|
@@ -4755,8 +5407,8 @@ function computeMorphedAttributes(object) {
|
|
|
4755
5407
|
modifiedAttributeArray[c2 * 3 + 1] = _vC.y;
|
|
4756
5408
|
modifiedAttributeArray[c2 * 3 + 2] = _vC.z;
|
|
4757
5409
|
}
|
|
4758
|
-
const geometry =
|
|
4759
|
-
const material =
|
|
5410
|
+
const geometry = object3.geometry;
|
|
5411
|
+
const material = object3.material;
|
|
4760
5412
|
let a, b, c;
|
|
4761
5413
|
const index = geometry.index;
|
|
4762
5414
|
const positionAttribute = geometry.attributes.position;
|
|
@@ -4783,7 +5435,7 @@ function computeMorphedAttributes(object) {
|
|
|
4783
5435
|
b = index.getX(j + 1);
|
|
4784
5436
|
c = index.getX(j + 2);
|
|
4785
5437
|
_calculateMorphedAttributeData(
|
|
4786
|
-
|
|
5438
|
+
object3,
|
|
4787
5439
|
groupMaterial,
|
|
4788
5440
|
positionAttribute,
|
|
4789
5441
|
morphPosition,
|
|
@@ -4794,7 +5446,7 @@ function computeMorphedAttributes(object) {
|
|
|
4794
5446
|
modifiedPosition
|
|
4795
5447
|
);
|
|
4796
5448
|
_calculateMorphedAttributeData(
|
|
4797
|
-
|
|
5449
|
+
object3,
|
|
4798
5450
|
groupMaterial,
|
|
4799
5451
|
normalAttribute,
|
|
4800
5452
|
morphNormal,
|
|
@@ -4814,7 +5466,7 @@ function computeMorphedAttributes(object) {
|
|
|
4814
5466
|
b = index.getX(i + 1);
|
|
4815
5467
|
c = index.getX(i + 2);
|
|
4816
5468
|
_calculateMorphedAttributeData(
|
|
4817
|
-
|
|
5469
|
+
object3,
|
|
4818
5470
|
material,
|
|
4819
5471
|
positionAttribute,
|
|
4820
5472
|
morphPosition,
|
|
@@ -4825,7 +5477,7 @@ function computeMorphedAttributes(object) {
|
|
|
4825
5477
|
modifiedPosition
|
|
4826
5478
|
);
|
|
4827
5479
|
_calculateMorphedAttributeData(
|
|
4828
|
-
|
|
5480
|
+
object3,
|
|
4829
5481
|
material,
|
|
4830
5482
|
normalAttribute,
|
|
4831
5483
|
morphNormal,
|
|
@@ -4849,7 +5501,7 @@ function computeMorphedAttributes(object) {
|
|
|
4849
5501
|
b = j + 1;
|
|
4850
5502
|
c = j + 2;
|
|
4851
5503
|
_calculateMorphedAttributeData(
|
|
4852
|
-
|
|
5504
|
+
object3,
|
|
4853
5505
|
groupMaterial,
|
|
4854
5506
|
positionAttribute,
|
|
4855
5507
|
morphPosition,
|
|
@@ -4860,7 +5512,7 @@ function computeMorphedAttributes(object) {
|
|
|
4860
5512
|
modifiedPosition
|
|
4861
5513
|
);
|
|
4862
5514
|
_calculateMorphedAttributeData(
|
|
4863
|
-
|
|
5515
|
+
object3,
|
|
4864
5516
|
groupMaterial,
|
|
4865
5517
|
normalAttribute,
|
|
4866
5518
|
morphNormal,
|
|
@@ -4880,7 +5532,7 @@ function computeMorphedAttributes(object) {
|
|
|
4880
5532
|
b = i + 1;
|
|
4881
5533
|
c = i + 2;
|
|
4882
5534
|
_calculateMorphedAttributeData(
|
|
4883
|
-
|
|
5535
|
+
object3,
|
|
4884
5536
|
material,
|
|
4885
5537
|
positionAttribute,
|
|
4886
5538
|
morphPosition,
|
|
@@ -4891,7 +5543,7 @@ function computeMorphedAttributes(object) {
|
|
|
4891
5543
|
modifiedPosition
|
|
4892
5544
|
);
|
|
4893
5545
|
_calculateMorphedAttributeData(
|
|
4894
|
-
|
|
5546
|
+
object3,
|
|
4895
5547
|
material,
|
|
4896
5548
|
normalAttribute,
|
|
4897
5549
|
morphNormal,
|
|
@@ -4925,6 +5577,7 @@ var init_ModelCulling = __esm({
|
|
|
4925
5577
|
"src/model/ModelCulling.ts"() {
|
|
4926
5578
|
init_CubeFace();
|
|
4927
5579
|
ModelCulling = class {
|
|
5580
|
+
/** Rotates a world-direction bit mask into model space. Bits follow {@link CUBE_FACES}. */
|
|
4928
5581
|
static toLocalMask(worldMask, rotation) {
|
|
4929
5582
|
let localMask = 0;
|
|
4930
5583
|
for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
|
|
@@ -4935,6 +5588,7 @@ var init_ModelCulling = __esm({
|
|
|
4935
5588
|
}
|
|
4936
5589
|
return localMask;
|
|
4937
5590
|
}
|
|
5591
|
+
/** Whether a model fills one block with opaque faces and can hide adjacent block faces. */
|
|
4938
5592
|
static isOpaqueFullCube(atlas) {
|
|
4939
5593
|
if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
|
|
4940
5594
|
const element = atlas.model.elements[0];
|
|
@@ -4961,15 +5615,17 @@ var init_ModelCulling = __esm({
|
|
|
4961
5615
|
});
|
|
4962
5616
|
|
|
4963
5617
|
// src/model/SpecialItems.ts
|
|
4964
|
-
import { Matrix4 as Matrix48 } from "three";
|
|
5618
|
+
import { Euler as Euler7, Matrix4 as Matrix48 } from "three";
|
|
4965
5619
|
var SpecialItems;
|
|
4966
5620
|
var init_SpecialItems = __esm({
|
|
4967
5621
|
"src/model/SpecialItems.ts"() {
|
|
4968
5622
|
init_AssetKey();
|
|
4969
5623
|
init_Entities();
|
|
5624
|
+
init_BannerPatterns();
|
|
5625
|
+
init_ModelTextures();
|
|
4970
5626
|
SpecialItems = class {
|
|
4971
5627
|
/** Resolves the static entity parts drawn by a special item renderer, in model units. */
|
|
4972
|
-
static async getParts(special, root) {
|
|
5628
|
+
static async getParts(special, root, components = {}) {
|
|
4973
5629
|
const texture = (id, directory) => {
|
|
4974
5630
|
const key = AssetKey.parse("textures", id);
|
|
4975
5631
|
return new AssetKey(
|
|
@@ -4982,13 +5638,77 @@ var init_SpecialItems = __esm({
|
|
|
4982
5638
|
root
|
|
4983
5639
|
);
|
|
4984
5640
|
};
|
|
4985
|
-
const load = async (id, textureKey, transform, rotations = {}) => {
|
|
5641
|
+
const load = async (id, textureKey, transform, rotations = {}, layers = ["main"]) => {
|
|
4986
5642
|
const key = new AssetKey("minecraft", id, void 0, void 0, "entity-models", ".json", root);
|
|
4987
|
-
const model = await Entities.getEntity(key, textureKey, { layer:
|
|
5643
|
+
const model = await Entities.getEntity(key, textureKey, layers.length === 1 ? { layer: layers[0] } : {
|
|
5644
|
+
layers,
|
|
5645
|
+
textures: textureKey ? Object.fromEntries(layers.map((name) => [name, textureKey])) : void 0
|
|
5646
|
+
});
|
|
4988
5647
|
if (!model) throw new Error(`Missing entity model minecraft:${id} for special item ${special.type}`);
|
|
4989
5648
|
return { model, transform, rotations };
|
|
4990
5649
|
};
|
|
4991
5650
|
switch (special.type) {
|
|
5651
|
+
case "minecraft:trident":
|
|
5652
|
+
case "trident":
|
|
5653
|
+
case "minecraft:conduit":
|
|
5654
|
+
case "conduit": {
|
|
5655
|
+
const trident = special.type === "trident" || special.type === "minecraft:trident";
|
|
5656
|
+
const part = await load(
|
|
5657
|
+
trident ? "trident" : "conduit",
|
|
5658
|
+
texture(trident ? "trident" : "conduit/base", ""),
|
|
5659
|
+
trident ? new Matrix48().makeScale(1, -1, -1) : new Matrix48().makeTranslation(8, 8, 8),
|
|
5660
|
+
{},
|
|
5661
|
+
trident ? ["main"] : ["shell"]
|
|
5662
|
+
);
|
|
5663
|
+
part.model = { ...part.model, render: "solid", layers: part.model.layers && Object.fromEntries(
|
|
5664
|
+
Object.entries(part.model.layers).map(([name, layer]) => [name, { ...layer, render: "solid" }])
|
|
5665
|
+
) };
|
|
5666
|
+
return [part];
|
|
5667
|
+
}
|
|
5668
|
+
case "minecraft:banner":
|
|
5669
|
+
case "banner":
|
|
5670
|
+
case "minecraft:shield":
|
|
5671
|
+
case "shield": {
|
|
5672
|
+
const banner = special.type === "banner" || special.type === "minecraft:banner";
|
|
5673
|
+
const color2 = banner ? BannerPatterns.getColor(special.color) : components["minecraft:base_color"] === void 0 ? void 0 : BannerPatterns.getColor(components["minecraft:base_color"]);
|
|
5674
|
+
const patterns = await BannerPatterns.getLayers(components["minecraft:banner_patterns"], banner ? "banner" : "shield", root);
|
|
5675
|
+
const patterned = banner || color2 !== void 0 || patterns.length > 0;
|
|
5676
|
+
const baseTexture = texture(banner ? "banner_base" : patterned ? "shield_base" : "shield_base_nopattern", "");
|
|
5677
|
+
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);
|
|
5678
|
+
const part = await load(banner ? "standing_banner" : "shield", baseTexture, transform, {}, banner ? ["main", "flag"] : ["main"]);
|
|
5679
|
+
const layers = Object.fromEntries(Object.entries(part.model.layers ?? { main: part.model }).map(([name, layer]) => [name, { ...layer, render: "solid" }]));
|
|
5680
|
+
let overlay2;
|
|
5681
|
+
if (banner) {
|
|
5682
|
+
const flagLayer = layers.flag;
|
|
5683
|
+
const flag = flagLayer?.layer.root.children.flag;
|
|
5684
|
+
if (!flag) throw new Error("Banner entity model is missing its flag layer");
|
|
5685
|
+
overlay2 = { ...flagLayer.layer, root: { ...flagLayer.layer.root, children: {
|
|
5686
|
+
...flagLayer.layer.root.children,
|
|
5687
|
+
flag: { ...flag, pose: { ...flag.pose, rotation: [-25e-4 * Math.PI, flag.pose.rotation[1], flag.pose.rotation[2]] } }
|
|
5688
|
+
} } };
|
|
5689
|
+
layers.flag = { ...flagLayer, layer: overlay2 };
|
|
5690
|
+
} else {
|
|
5691
|
+
const main = layers.main.layer;
|
|
5692
|
+
const plate = main.root.children.plate;
|
|
5693
|
+
if (!plate) throw new Error("Shield entity model is missing its plate");
|
|
5694
|
+
overlay2 = { ...main, root: { ...main.root, cubes: [], children: { plate } } };
|
|
5695
|
+
}
|
|
5696
|
+
const tints = {};
|
|
5697
|
+
if (patterned) {
|
|
5698
|
+
const draws = [{ texture: texture(`${banner ? "banner" : "shield"}/base`, ""), color: color2 ?? DYE_COLORS.white }, ...patterns];
|
|
5699
|
+
draws.forEach((draw, index) => {
|
|
5700
|
+
const name = index === 0 ? "pattern_base" : `pattern_${index - 1}`;
|
|
5701
|
+
layers[name] = { key: draw.texture, texture: draw.texture, layer: overlay2, render: "no_outline", tint: name };
|
|
5702
|
+
tints[name] = draw.color;
|
|
5703
|
+
});
|
|
5704
|
+
}
|
|
5705
|
+
await Promise.all([...new Map(Object.values(layers).map((layer) => [layer.texture.serialize(), layer.texture])).values()].map(async (key) => {
|
|
5706
|
+
if (!await ModelTextures.preload(key)) throw new Error(`Missing special item texture ${key.toNamespacedString()}`);
|
|
5707
|
+
}));
|
|
5708
|
+
part.model = { ...part.model, ...layers.main, layers };
|
|
5709
|
+
part.tints = tints;
|
|
5710
|
+
return [part];
|
|
5711
|
+
}
|
|
4992
5712
|
case "minecraft:chest":
|
|
4993
5713
|
case "chest": {
|
|
4994
5714
|
const angle = -(special.openness ?? 0) * Math.PI / 2;
|
|
@@ -4999,6 +5719,26 @@ var init_SpecialItems = __esm({
|
|
|
4999
5719
|
chest.model = { ...chest.model, render: "solid", layers: void 0 };
|
|
5000
5720
|
return [chest];
|
|
5001
5721
|
}
|
|
5722
|
+
case "minecraft:shulker_box":
|
|
5723
|
+
case "shulker_box": {
|
|
5724
|
+
const openness = special.openness === void 0 ? 0 : special.openness;
|
|
5725
|
+
const orientation = special.orientation === void 0 ? "up" : special.orientation;
|
|
5726
|
+
const directions = {
|
|
5727
|
+
down: [Math.PI, 0, 0],
|
|
5728
|
+
up: [0, 0, 0],
|
|
5729
|
+
north: [Math.PI / 2, 0, Math.PI],
|
|
5730
|
+
south: [Math.PI / 2, 0, 0],
|
|
5731
|
+
west: [Math.PI / 2, 0, Math.PI / 2],
|
|
5732
|
+
east: [Math.PI / 2, 0, -Math.PI / 2]
|
|
5733
|
+
};
|
|
5734
|
+
if (!Object.prototype.hasOwnProperty.call(directions, orientation)) throw new Error(`Unsupported shulker-box orientation ${orientation}`);
|
|
5735
|
+
if (typeof openness !== "number" || !Number.isFinite(openness)) throw new Error("Shulker-box openness must be finite");
|
|
5736
|
+
if (typeof special.texture !== "string" || !special.texture) throw new Error("Shulker-box texture is required");
|
|
5737
|
+
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));
|
|
5738
|
+
const box = await load("shulker_box", texture(special.texture, "shulker"), transform, { lid: [0, openness * Math.PI * 1.5, 0] });
|
|
5739
|
+
box.positions = { lid: [0, 24 - openness * 8, 0] };
|
|
5740
|
+
return [box];
|
|
5741
|
+
}
|
|
5002
5742
|
case "minecraft:bed":
|
|
5003
5743
|
case "bed": {
|
|
5004
5744
|
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));
|
|
@@ -5113,6 +5853,7 @@ var init_ItemTints = __esm({
|
|
|
5113
5853
|
static async get(model, tints) {
|
|
5114
5854
|
const sources = model.tints;
|
|
5115
5855
|
if (!sources?.length) return tints;
|
|
5856
|
+
const components = model.components ?? {};
|
|
5116
5857
|
const colors = { ...tints };
|
|
5117
5858
|
await Promise.all(sources.map(async (source, index) => {
|
|
5118
5859
|
if (colors[index] !== void 0) return;
|
|
@@ -5127,27 +5868,71 @@ var init_ItemTints = __esm({
|
|
|
5127
5868
|
break;
|
|
5128
5869
|
case "dye":
|
|
5129
5870
|
case "minecraft:dye":
|
|
5871
|
+
colors[index] = this.colorOrDefault(components["minecraft:dyed_color"], source.default, true);
|
|
5872
|
+
break;
|
|
5130
5873
|
case "potion":
|
|
5131
|
-
case "minecraft:potion":
|
|
5874
|
+
case "minecraft:potion": {
|
|
5875
|
+
const value = typeof components["minecraft:potion_contents"] === "string" ? void 0 : this.componentField(components, "potion_contents", "custom_color");
|
|
5876
|
+
colors[index] = this.colorOrDefault(value, source.default);
|
|
5877
|
+
break;
|
|
5878
|
+
}
|
|
5132
5879
|
case "map_color":
|
|
5133
5880
|
case "minecraft:map_color":
|
|
5881
|
+
colors[index] = this.colorOrDefault(components["minecraft:map_color"], source.default);
|
|
5882
|
+
break;
|
|
5134
5883
|
case "firework":
|
|
5135
|
-
case "minecraft:firework":
|
|
5884
|
+
case "minecraft:firework": {
|
|
5885
|
+
const values = this.componentField(components, "firework_explosion", "colors");
|
|
5886
|
+
if (values !== void 0 && !Array.isArray(values)) throw new MineRenderError("Item tint firework_explosion.colors must be an array");
|
|
5887
|
+
if (!values?.length) {
|
|
5888
|
+
colors[index] = this.rgb(source.default);
|
|
5889
|
+
break;
|
|
5890
|
+
}
|
|
5891
|
+
const rgb = values.map((value) => this.componentColor(value));
|
|
5892
|
+
const red = Math.floor(rgb.reduce((sum, color2) => sum + (color2 >> 16 & 255), 0) / rgb.length);
|
|
5893
|
+
const green = Math.floor(rgb.reduce((sum, color2) => sum + (color2 >> 8 & 255), 0) / rgb.length);
|
|
5894
|
+
const blue = Math.floor(rgb.reduce((sum, color2) => sum + (color2 & 255), 0) / rgb.length);
|
|
5895
|
+
colors[index] = red << 16 | green << 8 | blue;
|
|
5896
|
+
break;
|
|
5897
|
+
}
|
|
5136
5898
|
case "team":
|
|
5137
5899
|
case "minecraft:team":
|
|
5138
|
-
case "custom_model_data":
|
|
5139
|
-
case "minecraft:custom_model_data":
|
|
5140
5900
|
colors[index] = this.rgb(source.default);
|
|
5141
5901
|
break;
|
|
5902
|
+
case "custom_model_data":
|
|
5903
|
+
case "minecraft:custom_model_data": {
|
|
5904
|
+
const colorIndex = source.index ?? 0;
|
|
5905
|
+
if (!Number.isSafeInteger(colorIndex) || colorIndex < 0) throw new MineRenderError("Item tint custom_model_data index must be a nonnegative integer");
|
|
5906
|
+
const values = this.componentField(components, "custom_model_data", "colors");
|
|
5907
|
+
if (values !== void 0 && !Array.isArray(values)) throw new MineRenderError("Item tint custom_model_data.colors must be an array");
|
|
5908
|
+
colors[index] = this.colorOrDefault(values?.[colorIndex], source.default, true);
|
|
5909
|
+
break;
|
|
5910
|
+
}
|
|
5142
5911
|
default:
|
|
5143
5912
|
throw new MineRenderError(`Unsupported item tint source ${source.type}`);
|
|
5144
5913
|
}
|
|
5145
5914
|
}));
|
|
5146
5915
|
return colors;
|
|
5147
5916
|
}
|
|
5148
|
-
static
|
|
5149
|
-
|
|
5150
|
-
|
|
5917
|
+
static componentField(components, id, field) {
|
|
5918
|
+
const value = components[`minecraft:${id}`];
|
|
5919
|
+
if (value === void 0) return void 0;
|
|
5920
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new MineRenderError(`Item tint ${id} must be an object`);
|
|
5921
|
+
return value[field];
|
|
5922
|
+
}
|
|
5923
|
+
static colorOrDefault(value, fallback, allowTriple = false) {
|
|
5924
|
+
return value === void 0 ? this.rgb(fallback) : this.componentColor(value, allowTriple);
|
|
5925
|
+
}
|
|
5926
|
+
static componentColor(value, allowTriple = false) {
|
|
5927
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= -2147483648 && value <= 2147483647) return this.rgb(value);
|
|
5928
|
+
if (allowTriple && Array.isArray(value) && value.length === 3 && value.every((channel) => typeof channel === "number" && Number.isFinite(channel))) {
|
|
5929
|
+
return this.rgb(value) & 16777215;
|
|
5930
|
+
}
|
|
5931
|
+
throw new MineRenderError(`Item tint component color must be a packed integer${allowTriple ? " or RGB triple" : ""}`);
|
|
5932
|
+
}
|
|
5933
|
+
static rgb(color2) {
|
|
5934
|
+
if (!Array.isArray(color2)) return color2 & 16777215;
|
|
5935
|
+
const [red, green, blue] = color2.map((channel) => Math.floor(Math.fround(Math.fround(channel) * 255)) & 255);
|
|
5151
5936
|
return red << 16 | green << 8 | blue;
|
|
5152
5937
|
}
|
|
5153
5938
|
};
|
|
@@ -5155,7 +5940,7 @@ var init_ItemTints = __esm({
|
|
|
5155
5940
|
});
|
|
5156
5941
|
|
|
5157
5942
|
// src/model/scene/ModelObject.ts
|
|
5158
|
-
import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute5, Color as Color9, Euler as
|
|
5943
|
+
import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute5, Color as Color9, Euler as Euler8, Matrix4 as Matrix49 } from "three";
|
|
5159
5944
|
function isModelObject(obj) {
|
|
5160
5945
|
return obj.isModelObject;
|
|
5161
5946
|
}
|
|
@@ -5184,9 +5969,10 @@ var init_ModelObject = __esm({
|
|
|
5184
5969
|
this.originalModel = originalModel;
|
|
5185
5970
|
this.isModelObject = true;
|
|
5186
5971
|
this.specialMaterials = /* @__PURE__ */ new Map();
|
|
5972
|
+
this.geometries = /* @__PURE__ */ new Set();
|
|
5187
5973
|
this.meshesCreated = false;
|
|
5188
5974
|
this.options = (0, import_ts_deepmerge5.default)({}, _ModelObject.DEFAULT_OPTIONS, options ?? {});
|
|
5189
|
-
if (originalModel.special) this.options.instanceMeshes = false;
|
|
5975
|
+
if (originalModel.special || originalModel.parts) this.options.instanceMeshes = false;
|
|
5190
5976
|
if (this.options.tints) this.options.tints = { ...this.options.tints };
|
|
5191
5977
|
this.addEventListener("added", () => this.updateAnimationSubscription());
|
|
5192
5978
|
this.addEventListener("removed", () => this.updateAnimationSubscription());
|
|
@@ -5195,21 +5981,43 @@ var init_ModelObject = __esm({
|
|
|
5195
5981
|
this.DEFAULT_OPTIONS = (0, import_ts_deepmerge5.default)({}, SceneObject.DEFAULT_OPTIONS, {});
|
|
5196
5982
|
}
|
|
5197
5983
|
async init() {
|
|
5984
|
+
const parts = this.originalModel.parts;
|
|
5985
|
+
if (parts) {
|
|
5986
|
+
try {
|
|
5987
|
+
for (const part of parts) {
|
|
5988
|
+
const object3 = new _ModelObject(part, { ...this.options, instanceMeshes: false });
|
|
5989
|
+
this.add(object3);
|
|
5990
|
+
await object3.init();
|
|
5991
|
+
}
|
|
5992
|
+
let order = 0;
|
|
5993
|
+
this.iterateAllMeshes((mesh) => {
|
|
5994
|
+
mesh.renderOrder = order++;
|
|
5995
|
+
for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
|
|
5996
|
+
material.transparent = true;
|
|
5997
|
+
}
|
|
5998
|
+
});
|
|
5999
|
+
} catch (error) {
|
|
6000
|
+
this.disposeAndRemoveAllChildren();
|
|
6001
|
+
throw error;
|
|
6002
|
+
}
|
|
6003
|
+
this.notifyDirty();
|
|
6004
|
+
return;
|
|
6005
|
+
}
|
|
5198
6006
|
this.options.tints = await ItemTints.get(this.originalModel, this.options.tints);
|
|
5199
6007
|
const special = this.originalModel.special;
|
|
5200
6008
|
if (special) {
|
|
5201
|
-
const
|
|
6009
|
+
const parts2 = await SpecialItems.getParts(special, this.originalModel.key?.root, this.originalModel.components);
|
|
5202
6010
|
const transform = this.options.displayPosition ? DisplayTransforms.getMatrix(this.originalModel.display, this.options.displayPosition) : new Matrix49();
|
|
5203
6011
|
transform.multiply(new Matrix49().makeTranslation(-8, -8, -8));
|
|
5204
6012
|
try {
|
|
5205
|
-
for (const part of
|
|
5206
|
-
const
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
this.add(
|
|
5211
|
-
await
|
|
5212
|
-
|
|
6013
|
+
for (const part of parts2) {
|
|
6014
|
+
const object3 = new EntityObject(part.model, { flip: false, wireframe: this.options.wireframe, tints: part.tints });
|
|
6015
|
+
object3.matrix.copy(transform).multiply(part.transform);
|
|
6016
|
+
object3.matrixWorldNeedsUpdate = true;
|
|
6017
|
+
object3.matrixAutoUpdate = false;
|
|
6018
|
+
this.add(object3);
|
|
6019
|
+
await object3.init();
|
|
6020
|
+
object3.iterateAllMeshes((mesh) => {
|
|
5213
6021
|
const source = mesh.material;
|
|
5214
6022
|
let material = this.specialMaterials.get(source);
|
|
5215
6023
|
if (!material) {
|
|
@@ -5220,7 +6028,10 @@ var init_ModelObject = __esm({
|
|
|
5220
6028
|
mesh.material = material;
|
|
5221
6029
|
});
|
|
5222
6030
|
for (const [name, rotation] of Object.entries(part.rotations)) {
|
|
5223
|
-
|
|
6031
|
+
object3.getGroupByName(name)?.rotation.set(...rotation, "ZYX");
|
|
6032
|
+
}
|
|
6033
|
+
for (const [name, position] of Object.entries(part.positions ?? {})) {
|
|
6034
|
+
object3.getGroupByName(name)?.position.set(...position);
|
|
5224
6035
|
}
|
|
5225
6036
|
}
|
|
5226
6037
|
} catch (error) {
|
|
@@ -5253,6 +6064,7 @@ var init_ModelObject = __esm({
|
|
|
5253
6064
|
if (this.atlas.model.elements) {
|
|
5254
6065
|
this.atlas.model.elements?.forEach((el) => {
|
|
5255
6066
|
const elGeo = this._getBoxGeometryFromElement(el).clone();
|
|
6067
|
+
this.geometries.add(elGeo);
|
|
5256
6068
|
if (this.options.cullMask) {
|
|
5257
6069
|
const indices = Array.from(elGeo.getIndex().array);
|
|
5258
6070
|
elGeo.setIndex(indices.filter((_, index) => {
|
|
@@ -5263,8 +6075,9 @@ var init_ModelObject = __esm({
|
|
|
5263
6075
|
elGeo.clearGroups();
|
|
5264
6076
|
}
|
|
5265
6077
|
if (this.options.uvLockRotation) {
|
|
5266
|
-
UVMapper.lockUvs(elGeo, el.faces, this.atlas, new
|
|
6078
|
+
UVMapper.lockUvs(elGeo, el.faces, this.atlas, new Euler8(...this.options.uvLockRotation));
|
|
5267
6079
|
}
|
|
6080
|
+
UVMapper.setAtlasUvBounds(elGeo, el.faces, this.atlas);
|
|
5268
6081
|
if (this.options.tints) {
|
|
5269
6082
|
const colors = new Float32Array(elGeo.getAttribute("position").count * 3).fill(1);
|
|
5270
6083
|
for (const [faceIndex, faceName] of CUBE_FACES.entries()) {
|
|
@@ -5272,9 +6085,9 @@ var init_ModelObject = __esm({
|
|
|
5272
6085
|
if (tintIndex === void 0 || tintIndex < 0) continue;
|
|
5273
6086
|
const tint = this.options.tints[tintIndex];
|
|
5274
6087
|
if (tint === void 0) continue;
|
|
5275
|
-
const
|
|
6088
|
+
const color2 = new Color9(tint);
|
|
5276
6089
|
for (let vertex = 0; vertex < 4; vertex++) {
|
|
5277
|
-
|
|
6090
|
+
color2.toArray(colors, (faceIndex * 4 + vertex) * 3);
|
|
5278
6091
|
}
|
|
5279
6092
|
}
|
|
5280
6093
|
elGeo.setAttribute("color", new BufferAttribute5(colors, 3));
|
|
@@ -5305,10 +6118,14 @@ var init_ModelObject = __esm({
|
|
|
5305
6118
|
let combinedGeo;
|
|
5306
6119
|
if (allGeos.length > 0) {
|
|
5307
6120
|
combinedGeo = mergeBufferGeometries(allGeos);
|
|
6121
|
+
for (const geometry of allGeos) {
|
|
6122
|
+
this.geometries.delete(geometry);
|
|
6123
|
+
}
|
|
5308
6124
|
} else {
|
|
5309
6125
|
combinedGeo = new BoxGeometry5(16, 16, 16);
|
|
5310
6126
|
if (displayTransform) DisplayTransforms.apply(combinedGeo, displayTransform);
|
|
5311
6127
|
}
|
|
6128
|
+
this.geometries.add(combinedGeo);
|
|
5312
6129
|
combinedGeo.computeBoundingBox();
|
|
5313
6130
|
let mesh;
|
|
5314
6131
|
if (this.options.instanceMeshes) {
|
|
@@ -5330,9 +6147,12 @@ var init_ModelObject = __esm({
|
|
|
5330
6147
|
}
|
|
5331
6148
|
applyTextures() {
|
|
5332
6149
|
if (this.atlas) {
|
|
5333
|
-
const mat = Materials.createShadedCanvasMaterial(this.atlas.image.canvas, this.atlas.hasTransparency, false);
|
|
6150
|
+
const mat = Materials.createShadedCanvasMaterial(this.atlas.image.canvas, this.atlas.hasTransparency, false, true);
|
|
5334
6151
|
this.atlasMaterial = mat;
|
|
5335
6152
|
this.atlasTexture = mat.uniforms?.map?.value ?? mat.map;
|
|
6153
|
+
if (this.options.displayPosition === "gui" /* GUI */ && this.originalModel.gui_light === "front" /* FRONT */ && mat.uniforms?.SHADE) {
|
|
6154
|
+
mat.uniforms.SHADE.value = false;
|
|
6155
|
+
}
|
|
5336
6156
|
this.iterateAllMeshes((mesh) => {
|
|
5337
6157
|
if (mesh.geometry.hasAttribute("color")) mat.vertexColors = true;
|
|
5338
6158
|
mesh.material = mat;
|
|
@@ -5341,7 +6161,9 @@ var init_ModelObject = __esm({
|
|
|
5341
6161
|
}
|
|
5342
6162
|
}
|
|
5343
6163
|
updateAnimationSubscription() {
|
|
5344
|
-
|
|
6164
|
+
let root = this;
|
|
6165
|
+
while (root.parent && isModelObject(root.parent)) root = root.parent;
|
|
6166
|
+
const active = !!root.parent && (!this.isInstanced || this.instanceCounter > 0);
|
|
5345
6167
|
if (active && this.atlas?.hasAnimation && this.atlasTexture) {
|
|
5346
6168
|
if (!this.unsubscribeAtlas) {
|
|
5347
6169
|
this.atlasTexture.needsUpdate = true;
|
|
@@ -5357,6 +6179,9 @@ var init_ModelObject = __esm({
|
|
|
5357
6179
|
this.unsubscribeAtlas?.();
|
|
5358
6180
|
this.unsubscribeAtlas = void 0;
|
|
5359
6181
|
}
|
|
6182
|
+
for (const child of this.children) {
|
|
6183
|
+
if (isModelObject(child)) child.updateAnimationSubscription();
|
|
6184
|
+
}
|
|
5360
6185
|
}
|
|
5361
6186
|
nextInstance() {
|
|
5362
6187
|
const reference = super.nextInstance();
|
|
@@ -5376,6 +6201,8 @@ var init_ModelObject = __esm({
|
|
|
5376
6201
|
this.atlasMaterial = void 0;
|
|
5377
6202
|
for (const material of this.specialMaterials.values()) material.dispose();
|
|
5378
6203
|
this.specialMaterials.clear();
|
|
6204
|
+
for (const geometry of this.geometries) geometry.dispose();
|
|
6205
|
+
this.geometries.clear();
|
|
5379
6206
|
super.disposeAndRemoveAllChildren();
|
|
5380
6207
|
}
|
|
5381
6208
|
};
|
|
@@ -5589,9 +6416,9 @@ var init_FluidModelObject = __esm({
|
|
|
5589
6416
|
}
|
|
5590
6417
|
}
|
|
5591
6418
|
geometry.clearGroups();
|
|
5592
|
-
const
|
|
6419
|
+
const color2 = new Color10(this.options.tints?.[0] ?? (this.kind === "water" ? 4159204 : 16777215));
|
|
5593
6420
|
const colors = new Float32Array(geometry.getAttribute("position").count * 3);
|
|
5594
|
-
for (let i = 0; i < colors.length; i += 3)
|
|
6421
|
+
for (let i = 0; i < colors.length; i += 3) color2.toArray(colors, i);
|
|
5595
6422
|
geometry.setAttribute("color", new BufferAttribute6(colors, 3));
|
|
5596
6423
|
geometry.computeBoundingBox();
|
|
5597
6424
|
geometry.computeBoundingSphere();
|
|
@@ -5684,9 +6511,9 @@ var NodeEnv = class {
|
|
|
5684
6511
|
imageSize(data) {
|
|
5685
6512
|
return imageSize(Buffer.isBuffer(data) ? data : Buffer.from(data));
|
|
5686
6513
|
}
|
|
5687
|
-
openCache(name,
|
|
6514
|
+
openCache(name, version2) {
|
|
5688
6515
|
return new NodeCache(NodePersist.create({
|
|
5689
|
-
dir: name +
|
|
6516
|
+
dir: name + version2,
|
|
5690
6517
|
encoding: "utf8"
|
|
5691
6518
|
}));
|
|
5692
6519
|
}
|
|
@@ -5718,6 +6545,7 @@ var Meshes = class _Meshes {
|
|
|
5718
6545
|
static createBox(key) {
|
|
5719
6546
|
return new Mesh(Geometries.getBox(key.geometry), Materials.getImage(key.material));
|
|
5720
6547
|
}
|
|
6548
|
+
/** Returns a shared Mesh object. Use {@link createBox} for a mesh with its own transform and parent. */
|
|
5721
6549
|
static getBox(key) {
|
|
5722
6550
|
const keyStr = serializeMeshKey(key);
|
|
5723
6551
|
return Caching.boxMeshCache.get(keyStr, (k) => {
|
|
@@ -5767,6 +6595,7 @@ function shutdown() {
|
|
|
5767
6595
|
init_AssetKey();
|
|
5768
6596
|
init_AssetLoader();
|
|
5769
6597
|
init_Assets();
|
|
6598
|
+
init_BannerPatterns();
|
|
5770
6599
|
|
|
5771
6600
|
// src/assets/BlockEntities.ts
|
|
5772
6601
|
init_AssetKey();
|
|
@@ -5830,6 +6659,7 @@ init_AssetParsers();
|
|
|
5830
6659
|
init_ModelTextures();
|
|
5831
6660
|
init_Caching();
|
|
5832
6661
|
var Fonts = class {
|
|
6662
|
+
/** Combines font definitions in source-priority order. Unsupported Unihex and TrueType providers are skipped. */
|
|
5833
6663
|
static async get(key = "minecraft:default") {
|
|
5834
6664
|
const fontKey = typeof key === "string" ? AssetKey.parse("font", key) : key;
|
|
5835
6665
|
return await Caching.fontCache.get(fontKey.serialize(), () => this.load(fontKey, []));
|
|
@@ -5845,8 +6675,8 @@ var Fonts = class {
|
|
|
5845
6675
|
const stack = await AssetLoader.getAll(key, "json" /* JSON */);
|
|
5846
6676
|
if (stack.length === 0) throw new Error(`Could not load font ${key.toNamespacedString()}`);
|
|
5847
6677
|
const glyphs = /* @__PURE__ */ new Map();
|
|
5848
|
-
for (const
|
|
5849
|
-
for (const provider of
|
|
6678
|
+
for (const asset2 of stack) {
|
|
6679
|
+
for (const provider of asset2.providers) {
|
|
5850
6680
|
if (provider.filter?.uniform || provider.filter?.jp) continue;
|
|
5851
6681
|
let provided;
|
|
5852
6682
|
switch (provider.type) {
|
|
@@ -5992,13 +6822,13 @@ var OBJExporter = class {
|
|
|
5992
6822
|
* @param {Object3D} object - The 3D object to export.
|
|
5993
6823
|
* @return {string} The exported OBJ.
|
|
5994
6824
|
*/
|
|
5995
|
-
parse(
|
|
6825
|
+
parse(object3) {
|
|
5996
6826
|
let output = "";
|
|
5997
6827
|
let indexVertex = 0;
|
|
5998
6828
|
let indexVertexUvs = 0;
|
|
5999
6829
|
let indexNormals = 0;
|
|
6000
6830
|
const vertex = new Vector37();
|
|
6001
|
-
const
|
|
6831
|
+
const color2 = new Color4();
|
|
6002
6832
|
const normal = new Vector37();
|
|
6003
6833
|
const uv = new Vector22();
|
|
6004
6834
|
const face = [];
|
|
@@ -6097,9 +6927,9 @@ var OBJExporter = class {
|
|
|
6097
6927
|
vertex.applyMatrix4(points.matrixWorld);
|
|
6098
6928
|
output += "v " + vertex.x + " " + vertex.y + " " + vertex.z;
|
|
6099
6929
|
if (colors !== void 0) {
|
|
6100
|
-
|
|
6101
|
-
ColorManagement.workingToColorSpace(
|
|
6102
|
-
output += " " +
|
|
6930
|
+
color2.fromBufferAttribute(colors, i);
|
|
6931
|
+
ColorManagement.workingToColorSpace(color2, SRGBColorSpace3);
|
|
6932
|
+
output += " " + color2.r + " " + color2.g + " " + color2.b;
|
|
6103
6933
|
}
|
|
6104
6934
|
output += "\n";
|
|
6105
6935
|
}
|
|
@@ -6111,7 +6941,7 @@ var OBJExporter = class {
|
|
|
6111
6941
|
}
|
|
6112
6942
|
indexVertex += nbVertex;
|
|
6113
6943
|
}
|
|
6114
|
-
|
|
6944
|
+
object3.traverse(function(child) {
|
|
6115
6945
|
if (child.isMesh === true) {
|
|
6116
6946
|
parseMesh(child);
|
|
6117
6947
|
}
|
|
@@ -6145,9 +6975,9 @@ var PLYExporter = class {
|
|
|
6145
6975
|
* @param {PLYExporter~Options} options - The export options.
|
|
6146
6976
|
* @return {?(string|ArrayBuffer)} The exported PLY.
|
|
6147
6977
|
*/
|
|
6148
|
-
parse(
|
|
6978
|
+
parse(object3, onDone, options = {}) {
|
|
6149
6979
|
function traverseMeshes(cb) {
|
|
6150
|
-
|
|
6980
|
+
object3.traverse(function(child) {
|
|
6151
6981
|
if (child.isMesh === true || child.isPoints) {
|
|
6152
6982
|
const mesh = child;
|
|
6153
6983
|
const geometry = mesh.geometry;
|
|
@@ -6180,7 +7010,7 @@ var PLYExporter = class {
|
|
|
6180
7010
|
for (const name of customAttributeNames) customTypes[name] = "float";
|
|
6181
7011
|
let vertexCount = 0;
|
|
6182
7012
|
let faceCount = 0;
|
|
6183
|
-
|
|
7013
|
+
object3.traverse(function(child) {
|
|
6184
7014
|
if (child.isMesh === true) {
|
|
6185
7015
|
const mesh = child;
|
|
6186
7016
|
const geometry = mesh.geometry;
|
|
@@ -6802,8 +7632,8 @@ function equalArray(array1, array2) {
|
|
|
6802
7632
|
return element === array2[index];
|
|
6803
7633
|
});
|
|
6804
7634
|
}
|
|
6805
|
-
function stringToArrayBuffer(
|
|
6806
|
-
return new TextEncoder().encode(
|
|
7635
|
+
function stringToArrayBuffer(text2) {
|
|
7636
|
+
return new TextEncoder().encode(text2).buffer;
|
|
6807
7637
|
}
|
|
6808
7638
|
function isIdentityMatrix(matrix) {
|
|
6809
7639
|
return equalArray(matrix.elements, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
|
|
@@ -6996,12 +7826,12 @@ var GLTFWriter = class {
|
|
|
6996
7826
|
* @param {THREE.Object3D|THREE.Material|THREE.BufferGeometry|THREE.AnimationClip} object
|
|
6997
7827
|
* @param {Object} objectDef
|
|
6998
7828
|
*/
|
|
6999
|
-
serializeUserData(
|
|
7000
|
-
if (Object.keys(
|
|
7829
|
+
serializeUserData(object3, objectDef) {
|
|
7830
|
+
if (Object.keys(object3.userData).length === 0) return;
|
|
7001
7831
|
const options = this.options;
|
|
7002
7832
|
const extensionsUsed = this.extensionsUsed;
|
|
7003
7833
|
try {
|
|
7004
|
-
const json = JSON.parse(JSON.stringify(
|
|
7834
|
+
const json = JSON.parse(JSON.stringify(object3.userData));
|
|
7005
7835
|
if (options.includeCustomExtensions && json.gltfExtensions) {
|
|
7006
7836
|
if (objectDef.extensions === void 0) objectDef.extensions = {};
|
|
7007
7837
|
for (const extensionName in json.gltfExtensions) {
|
|
@@ -7012,7 +7842,7 @@ var GLTFWriter = class {
|
|
|
7012
7842
|
}
|
|
7013
7843
|
if (Object.keys(json).length > 0) objectDef.extras = json;
|
|
7014
7844
|
} catch (error) {
|
|
7015
|
-
console.warn("THREE.GLTFExporter: userData of '" +
|
|
7845
|
+
console.warn("THREE.GLTFExporter: userData of '" + object3.name + "' won't be serialized because of JSON.stringify error - " + error.message);
|
|
7016
7846
|
}
|
|
7017
7847
|
}
|
|
7018
7848
|
/**
|
|
@@ -7385,7 +8215,7 @@ var GLTFWriter = class {
|
|
|
7385
8215
|
* @param {string} mimeType export format
|
|
7386
8216
|
* @return {number} Index of the processed texture in the "images" array
|
|
7387
8217
|
*/
|
|
7388
|
-
processImage(image,
|
|
8218
|
+
processImage(image, format2, flipY, mimeType = "image/png") {
|
|
7389
8219
|
if (image !== null) {
|
|
7390
8220
|
const writer = this;
|
|
7391
8221
|
const cache = writer.cache;
|
|
@@ -7409,8 +8239,8 @@ var GLTFWriter = class {
|
|
|
7409
8239
|
ctx.scale(1, -1);
|
|
7410
8240
|
}
|
|
7411
8241
|
if (image.data !== void 0) {
|
|
7412
|
-
if (
|
|
7413
|
-
console.error("GLTFExporter: Only RGBAFormat is supported.",
|
|
8242
|
+
if (format2 !== RGBAFormat2) {
|
|
8243
|
+
console.error("GLTFExporter: Only RGBAFormat is supported.", format2);
|
|
7414
8244
|
}
|
|
7415
8245
|
if (image.width > options.maxTextureSize || image.height > options.maxTextureSize) {
|
|
7416
8246
|
console.warn("GLTFExporter: Image size is bigger than maxTextureSize", image);
|
|
@@ -7521,9 +8351,9 @@ var GLTFWriter = class {
|
|
|
7521
8351
|
if (material.isMeshStandardMaterial !== true && material.isMeshBasicMaterial !== true) {
|
|
7522
8352
|
console.warn("GLTFExporter: Use MeshStandardMaterial or MeshBasicMaterial for best results.");
|
|
7523
8353
|
}
|
|
7524
|
-
const
|
|
7525
|
-
if (!equalArray(
|
|
7526
|
-
materialDef.pbrMetallicRoughness.baseColorFactor =
|
|
8354
|
+
const color2 = material.color.toArray().concat([material.opacity]);
|
|
8355
|
+
if (!equalArray(color2, [1, 1, 1, 1])) {
|
|
8356
|
+
materialDef.pbrMetallicRoughness.baseColorFactor = color2;
|
|
7527
8357
|
}
|
|
7528
8358
|
if (material.isMeshStandardMaterial) {
|
|
7529
8359
|
materialDef.pbrMetallicRoughness.metallicFactor = material.metalness;
|
|
@@ -7938,13 +8768,13 @@ var GLTFWriter = class {
|
|
|
7938
8768
|
* @param {THREE.Object3D} object
|
|
7939
8769
|
* @return {?number}
|
|
7940
8770
|
*/
|
|
7941
|
-
processSkin(
|
|
8771
|
+
processSkin(object3) {
|
|
7942
8772
|
const json = this.json;
|
|
7943
8773
|
const nodeMap = this.nodeMap;
|
|
7944
|
-
const node = json.nodes[nodeMap.get(
|
|
7945
|
-
const skeleton =
|
|
8774
|
+
const node = json.nodes[nodeMap.get(object3)];
|
|
8775
|
+
const skeleton = object3.skeleton;
|
|
7946
8776
|
if (skeleton === void 0) return null;
|
|
7947
|
-
const rootJoint =
|
|
8777
|
+
const rootJoint = object3.skeleton.bones[0];
|
|
7948
8778
|
if (rootJoint === void 0) return null;
|
|
7949
8779
|
const joints = [];
|
|
7950
8780
|
const inverseBindMatrices = new Float32Array(skeleton.bones.length * 16);
|
|
@@ -7952,7 +8782,7 @@ var GLTFWriter = class {
|
|
|
7952
8782
|
for (let i = 0; i < skeleton.bones.length; ++i) {
|
|
7953
8783
|
joints.push(nodeMap.get(skeleton.bones[i]));
|
|
7954
8784
|
temporaryBoneInverse.copy(skeleton.boneInverses[i]);
|
|
7955
|
-
temporaryBoneInverse.multiply(
|
|
8785
|
+
temporaryBoneInverse.multiply(object3.bindMatrix).toArray(inverseBindMatrices, i * 16);
|
|
7956
8786
|
}
|
|
7957
8787
|
if (json.skins === void 0) json.skins = [];
|
|
7958
8788
|
json.skins.push({
|
|
@@ -7968,19 +8798,19 @@ var GLTFWriter = class {
|
|
|
7968
8798
|
* @param {THREE.Object3D} object Object3D to processNodeAsync
|
|
7969
8799
|
* @return {Promise<number>} Index of the node in the nodes list
|
|
7970
8800
|
*/
|
|
7971
|
-
async processNodeAsync(
|
|
8801
|
+
async processNodeAsync(object3) {
|
|
7972
8802
|
const json = this.json;
|
|
7973
8803
|
const options = this.options;
|
|
7974
8804
|
const nodeMap = this.nodeMap;
|
|
7975
8805
|
if (!json.nodes) json.nodes = [];
|
|
7976
|
-
if (
|
|
7977
|
-
return await this._processNodeWithPivotAsync(
|
|
8806
|
+
if (object3.pivot !== null) {
|
|
8807
|
+
return await this._processNodeWithPivotAsync(object3);
|
|
7978
8808
|
}
|
|
7979
8809
|
const nodeDef = {};
|
|
7980
8810
|
if (options.trs) {
|
|
7981
|
-
const rotation =
|
|
7982
|
-
const position =
|
|
7983
|
-
const scale =
|
|
8811
|
+
const rotation = object3.quaternion.toArray();
|
|
8812
|
+
const position = object3.position.toArray();
|
|
8813
|
+
const scale = object3.scale.toArray();
|
|
7984
8814
|
if (!equalArray(rotation, [0, 0, 0, 1])) {
|
|
7985
8815
|
nodeDef.rotation = rotation;
|
|
7986
8816
|
}
|
|
@@ -7991,28 +8821,28 @@ var GLTFWriter = class {
|
|
|
7991
8821
|
nodeDef.scale = scale;
|
|
7992
8822
|
}
|
|
7993
8823
|
} else {
|
|
7994
|
-
if (
|
|
7995
|
-
|
|
8824
|
+
if (object3.matrixAutoUpdate) {
|
|
8825
|
+
object3.updateMatrix();
|
|
7996
8826
|
}
|
|
7997
|
-
if (isIdentityMatrix(
|
|
7998
|
-
nodeDef.matrix =
|
|
8827
|
+
if (isIdentityMatrix(object3.matrix) === false) {
|
|
8828
|
+
nodeDef.matrix = object3.matrix.elements;
|
|
7999
8829
|
}
|
|
8000
8830
|
}
|
|
8001
|
-
if (
|
|
8002
|
-
this.serializeUserData(
|
|
8003
|
-
if (
|
|
8004
|
-
const meshIndex = await this.processMeshAsync(
|
|
8831
|
+
if (object3.name !== "") nodeDef.name = String(object3.name);
|
|
8832
|
+
this.serializeUserData(object3, nodeDef);
|
|
8833
|
+
if (object3.isMesh || object3.isLine || object3.isPoints) {
|
|
8834
|
+
const meshIndex = await this.processMeshAsync(object3);
|
|
8005
8835
|
if (meshIndex !== null) nodeDef.mesh = meshIndex;
|
|
8006
|
-
} else if (
|
|
8007
|
-
nodeDef.camera = this.processCamera(
|
|
8836
|
+
} else if (object3.isCamera) {
|
|
8837
|
+
nodeDef.camera = this.processCamera(object3);
|
|
8008
8838
|
}
|
|
8009
|
-
if (
|
|
8839
|
+
if (object3.isSkinnedMesh) this.skins.push(object3);
|
|
8010
8840
|
const nodeIndex = json.nodes.push(nodeDef) - 1;
|
|
8011
|
-
nodeMap.set(
|
|
8012
|
-
if (
|
|
8841
|
+
nodeMap.set(object3, nodeIndex);
|
|
8842
|
+
if (object3.children.length > 0) {
|
|
8013
8843
|
const children = [];
|
|
8014
|
-
for (let i = 0, l =
|
|
8015
|
-
const child =
|
|
8844
|
+
for (let i = 0, l = object3.children.length; i < l; i++) {
|
|
8845
|
+
const child = object3.children[i];
|
|
8016
8846
|
if (child.visible || options.onlyVisible === false) {
|
|
8017
8847
|
const childNodeIndex = await this.processNodeAsync(child);
|
|
8018
8848
|
if (childNodeIndex !== null) children.push(childNodeIndex);
|
|
@@ -8021,7 +8851,7 @@ var GLTFWriter = class {
|
|
|
8021
8851
|
if (children.length > 0) nodeDef.children = children;
|
|
8022
8852
|
}
|
|
8023
8853
|
await this._invokeAllAsync(function(ext) {
|
|
8024
|
-
ext.writeNode && ext.writeNode(
|
|
8854
|
+
ext.writeNode && ext.writeNode(object3, nodeDef);
|
|
8025
8855
|
});
|
|
8026
8856
|
return nodeIndex;
|
|
8027
8857
|
}
|
|
@@ -8030,19 +8860,19 @@ var GLTFWriter = class {
|
|
|
8030
8860
|
* @param {THREE.Object3D} object Object3D with pivot
|
|
8031
8861
|
* @return {Promise<number>} Index of the container node
|
|
8032
8862
|
*/
|
|
8033
|
-
async _processNodeWithPivotAsync(
|
|
8863
|
+
async _processNodeWithPivotAsync(object3) {
|
|
8034
8864
|
const json = this.json;
|
|
8035
8865
|
const options = this.options;
|
|
8036
8866
|
const nodeMap = this.nodeMap;
|
|
8037
|
-
const pivot =
|
|
8867
|
+
const pivot = object3.pivot;
|
|
8038
8868
|
const containerDef = {};
|
|
8039
|
-
const rotation =
|
|
8869
|
+
const rotation = object3.quaternion.toArray();
|
|
8040
8870
|
const position = [
|
|
8041
|
-
|
|
8042
|
-
|
|
8043
|
-
|
|
8871
|
+
object3.position.x + pivot.x,
|
|
8872
|
+
object3.position.y + pivot.y,
|
|
8873
|
+
object3.position.z + pivot.z
|
|
8044
8874
|
];
|
|
8045
|
-
const scale =
|
|
8875
|
+
const scale = object3.scale.toArray();
|
|
8046
8876
|
if (!equalArray(rotation, [0, 0, 0, 1])) {
|
|
8047
8877
|
containerDef.rotation = rotation;
|
|
8048
8878
|
}
|
|
@@ -8053,28 +8883,28 @@ var GLTFWriter = class {
|
|
|
8053
8883
|
containerDef.scale = scale;
|
|
8054
8884
|
}
|
|
8055
8885
|
containerDef.extras = { pivot: pivot.toArray() };
|
|
8056
|
-
if (
|
|
8057
|
-
this.serializeUserData(
|
|
8886
|
+
if (object3.name !== "") containerDef.name = String(object3.name);
|
|
8887
|
+
this.serializeUserData(object3, containerDef);
|
|
8058
8888
|
const containerIndex = json.nodes.push(containerDef) - 1;
|
|
8059
|
-
nodeMap.set(
|
|
8889
|
+
nodeMap.set(object3, containerIndex);
|
|
8060
8890
|
const childDef = {};
|
|
8061
8891
|
const childPosition = [-pivot.x, -pivot.y, -pivot.z];
|
|
8062
8892
|
if (!equalArray(childPosition, [0, 0, 0])) {
|
|
8063
8893
|
childDef.translation = childPosition;
|
|
8064
8894
|
}
|
|
8065
|
-
if (
|
|
8066
|
-
const meshIndex = await this.processMeshAsync(
|
|
8895
|
+
if (object3.isMesh || object3.isLine || object3.isPoints) {
|
|
8896
|
+
const meshIndex = await this.processMeshAsync(object3);
|
|
8067
8897
|
if (meshIndex !== null) childDef.mesh = meshIndex;
|
|
8068
|
-
} else if (
|
|
8069
|
-
childDef.camera = this.processCamera(
|
|
8898
|
+
} else if (object3.isCamera) {
|
|
8899
|
+
childDef.camera = this.processCamera(object3);
|
|
8070
8900
|
}
|
|
8071
|
-
if (
|
|
8901
|
+
if (object3.isSkinnedMesh) this.skins.push(object3);
|
|
8072
8902
|
const childIndex = json.nodes.push(childDef) - 1;
|
|
8073
8903
|
const containerChildren = [childIndex];
|
|
8074
|
-
if (
|
|
8904
|
+
if (object3.children.length > 0) {
|
|
8075
8905
|
const grandchildren = [];
|
|
8076
|
-
for (let i = 0, l =
|
|
8077
|
-
const child =
|
|
8906
|
+
for (let i = 0, l = object3.children.length; i < l; i++) {
|
|
8907
|
+
const child = object3.children[i];
|
|
8078
8908
|
if (child.visible || options.onlyVisible === false) {
|
|
8079
8909
|
const childNodeIndex = await this.processNodeAsync(child);
|
|
8080
8910
|
if (childNodeIndex !== null) grandchildren.push(childNodeIndex);
|
|
@@ -8084,7 +8914,7 @@ var GLTFWriter = class {
|
|
|
8084
8914
|
}
|
|
8085
8915
|
containerDef.children = containerChildren;
|
|
8086
8916
|
await this._invokeAllAsync(function(ext) {
|
|
8087
|
-
ext.writeNode && ext.writeNode(
|
|
8917
|
+
ext.writeNode && ext.writeNode(object3, containerDef);
|
|
8088
8918
|
});
|
|
8089
8919
|
return containerIndex;
|
|
8090
8920
|
}
|
|
@@ -8527,10 +9357,10 @@ var GLTFMeshGpuInstancing = class {
|
|
|
8527
9357
|
this.writer = writer;
|
|
8528
9358
|
this.name = "EXT_mesh_gpu_instancing";
|
|
8529
9359
|
}
|
|
8530
|
-
writeNode(
|
|
8531
|
-
if (!
|
|
9360
|
+
writeNode(object3, nodeDef) {
|
|
9361
|
+
if (!object3.isInstancedMesh) return;
|
|
8532
9362
|
const writer = this.writer;
|
|
8533
|
-
const mesh =
|
|
9363
|
+
const mesh = object3;
|
|
8534
9364
|
const translationAttr = new Float32Array(mesh.count * 3);
|
|
8535
9365
|
const rotationAttr = new Float32Array(mesh.count * 4);
|
|
8536
9366
|
const scaleAttr = new Float32Array(mesh.count * 3);
|
|
@@ -8720,8 +9550,8 @@ var SceneExporter = class {
|
|
|
8720
9550
|
}
|
|
8721
9551
|
}
|
|
8722
9552
|
};
|
|
8723
|
-
function localMatrix(
|
|
8724
|
-
return
|
|
9553
|
+
function localMatrix(object3) {
|
|
9554
|
+
return object3.matrixAutoUpdate ? new Matrix47().compose(object3.position, object3.quaternion, object3.scale) : object3.matrix.clone();
|
|
8725
9555
|
}
|
|
8726
9556
|
function createSnapshot(root, gltf, bakeColors = false) {
|
|
8727
9557
|
const scene = new Scene3();
|
|
@@ -8825,24 +9655,24 @@ function createSnapshot(root, gltf, bakeColors = false) {
|
|
|
8825
9655
|
scene.add(copy);
|
|
8826
9656
|
}
|
|
8827
9657
|
};
|
|
8828
|
-
const visit = (
|
|
8829
|
-
if (!
|
|
8830
|
-
const world = parentWorld.clone().multiply(localMatrix(
|
|
8831
|
-
const mesh =
|
|
9658
|
+
const visit = (object3, parentWorld) => {
|
|
9659
|
+
if (!object3.visible) return;
|
|
9660
|
+
const world = parentWorld.clone().multiply(localMatrix(object3));
|
|
9661
|
+
const mesh = object3;
|
|
8832
9662
|
if (mesh.isMesh) {
|
|
8833
9663
|
const instanced = mesh;
|
|
8834
9664
|
if (instanced.isInstancedMesh) {
|
|
8835
9665
|
for (let i = 0; i < instanced.count; i++) {
|
|
8836
9666
|
const matrix = new Matrix47();
|
|
8837
9667
|
instanced.getMatrixAt(i, matrix);
|
|
8838
|
-
const
|
|
8839
|
-
addMesh(mesh, world.clone().multiply(matrix),
|
|
9668
|
+
const color2 = instanced.instanceColor ? new Color7().fromBufferAttribute(instanced.instanceColor, i) : void 0;
|
|
9669
|
+
addMesh(mesh, world.clone().multiply(matrix), color2);
|
|
8840
9670
|
}
|
|
8841
9671
|
} else {
|
|
8842
9672
|
addMesh(mesh, world);
|
|
8843
9673
|
}
|
|
8844
9674
|
}
|
|
8845
|
-
for (const child of
|
|
9675
|
+
for (const child of object3.children) visit(child, world);
|
|
8846
9676
|
};
|
|
8847
9677
|
try {
|
|
8848
9678
|
const ancestors = [];
|
|
@@ -8915,16 +9745,17 @@ function copyDataTexture(source) {
|
|
|
8915
9745
|
|
|
8916
9746
|
// src/gui/GuiHelper.ts
|
|
8917
9747
|
init_AssetKey();
|
|
9748
|
+
init_ModelTextures();
|
|
8918
9749
|
|
|
8919
9750
|
// src/gui/GuiText.ts
|
|
8920
9751
|
import { BufferGeometry as BufferGeometry2, Color as Color8, Float32BufferAttribute as Float32BufferAttribute4 } from "three";
|
|
8921
|
-
async function layoutGuiText(
|
|
9752
|
+
async function layoutGuiText(text2, options = {}) {
|
|
8922
9753
|
const maxWidth = options.maxWidth ?? Infinity, lineHeight = options.lineHeight ?? 9;
|
|
8923
9754
|
if (!(maxWidth > 0) || !Number.isFinite(lineHeight) || lineHeight <= 0) {
|
|
8924
9755
|
throw new Error("Text wrap width and line height must be positive");
|
|
8925
9756
|
}
|
|
8926
9757
|
const font = await Fonts.get(options.font);
|
|
8927
|
-
const runs = typeof
|
|
9758
|
+
const runs = typeof text2 === "string" ? [{ text: text2 }] : text2;
|
|
8928
9759
|
const glyphs = [], lineWidths = [];
|
|
8929
9760
|
let line = [], width = 0;
|
|
8930
9761
|
let trailingNewline = false;
|
|
@@ -8978,7 +9809,7 @@ function createGuiTextGeometry(layout, shadow = true) {
|
|
|
8978
9809
|
batch = { image: glyph?.image, positions: [], uvs: [], colors: [], indices: [] };
|
|
8979
9810
|
batches.push(batch);
|
|
8980
9811
|
}
|
|
8981
|
-
const
|
|
9812
|
+
const color2 = new Color8(isShadow ? (style.color & 16579836) >> 2 : style.color);
|
|
8982
9813
|
const offset = isShadow ? 1 : 0;
|
|
8983
9814
|
const top = glyph ? 7 - glyph.ascent : 0;
|
|
8984
9815
|
const height = glyph ? glyph.height * glyph.scale : 8;
|
|
@@ -9006,7 +9837,7 @@ function createGuiTextGeometry(layout, shadow = true) {
|
|
|
9006
9837
|
const v0 = glyph ? 1 - glyph.y / glyph.image.height : 1;
|
|
9007
9838
|
const v1 = glyph ? 1 - (glyph.y + glyph.height) / glyph.image.height : 0;
|
|
9008
9839
|
batch.uvs.push(u0, v0, u1, v0, u0, v1, u1, v1);
|
|
9009
|
-
for (let i = 0; i < 4; i++) batch.colors.push(
|
|
9840
|
+
for (let i = 0; i < 4; i++) batch.colors.push(color2.r, color2.g, color2.b);
|
|
9010
9841
|
batch.indices.push(base, base + 2, base + 1, base + 2, base + 3, base + 1);
|
|
9011
9842
|
};
|
|
9012
9843
|
for (const boldOffset of style.bold ? [0, 1] : [0]) {
|
|
@@ -9024,7 +9855,128 @@ function createGuiTextGeometry(layout, shadow = true) {
|
|
|
9024
9855
|
}
|
|
9025
9856
|
|
|
9026
9857
|
// src/gui/GuiHelper.ts
|
|
9858
|
+
var GUI_CONTAINER_LAYOUTS = {
|
|
9859
|
+
generic_54: {
|
|
9860
|
+
texture: "minecraft:gui/container/generic_54",
|
|
9861
|
+
crop: [0, 0, 176, 222],
|
|
9862
|
+
slotOrigin: [8, 18],
|
|
9863
|
+
slotOffset: [18, 18],
|
|
9864
|
+
rowSize: 9,
|
|
9865
|
+
slotCount: 54
|
|
9866
|
+
},
|
|
9867
|
+
crafting_table: {
|
|
9868
|
+
texture: "minecraft:gui/container/crafting_table",
|
|
9869
|
+
crop: [0, 0, 176, 166],
|
|
9870
|
+
slotOrigin: [30, 17],
|
|
9871
|
+
slotOffset: [18, 18],
|
|
9872
|
+
rowSize: 3,
|
|
9873
|
+
slotCount: 9,
|
|
9874
|
+
resultPosition: [124, 35]
|
|
9875
|
+
}
|
|
9876
|
+
};
|
|
9027
9877
|
var GuiHelper = class {
|
|
9878
|
+
/**
|
|
9879
|
+
* Creates a 182×5 boss bar using the active resource pack. Finite progress is clamped to 0–1.
|
|
9880
|
+
* The fill crops the source image; explicit crops do not apply `.mcmeta` sprite scaling.
|
|
9881
|
+
*/
|
|
9882
|
+
static async bossBar(options = {}) {
|
|
9883
|
+
const { color: color2 = "pink", progress = 1, position = [0, 0] } = options;
|
|
9884
|
+
this.validatePosition(position);
|
|
9885
|
+
if (!["pink", "blue", "red", "green", "yellow", "purple", "white"].includes(color2)) {
|
|
9886
|
+
throw new Error(`Unsupported boss bar color: ${color2}`);
|
|
9887
|
+
}
|
|
9888
|
+
if (!Number.isFinite(progress)) throw new Error("Boss bar progress must be finite");
|
|
9889
|
+
const value = Math.max(0, Math.min(1, progress));
|
|
9890
|
+
const width = value > 0 ? Math.floor(value * 181) + 1 : 0;
|
|
9891
|
+
const texture = `minecraft:gui/sprites/boss_bar/${color2}`;
|
|
9892
|
+
const layers = [{
|
|
9893
|
+
name: "boss-bar-background",
|
|
9894
|
+
texture: `${texture}_background`,
|
|
9895
|
+
position: [...position],
|
|
9896
|
+
size: [182, 5]
|
|
9897
|
+
}];
|
|
9898
|
+
if (width > 0) {
|
|
9899
|
+
layers.push({
|
|
9900
|
+
...await this.cropTexture(`${texture}_progress`, [0, 0, width, 5], [182, 5], position),
|
|
9901
|
+
name: "boss-bar-progress"
|
|
9902
|
+
});
|
|
9903
|
+
}
|
|
9904
|
+
return layers;
|
|
9905
|
+
}
|
|
9906
|
+
/**
|
|
9907
|
+
* Creates a 192×192 book page with optional arrows and supplied text. Buttons are hidden by default.
|
|
9908
|
+
* Callers choose the page content and button states; this helper does not paginate or handle clicks.
|
|
9909
|
+
*/
|
|
9910
|
+
static async book(options = {}) {
|
|
9911
|
+
const {
|
|
9912
|
+
position = [0, 0],
|
|
9913
|
+
previous = "hidden",
|
|
9914
|
+
next = "hidden",
|
|
9915
|
+
text: text2,
|
|
9916
|
+
color: color2 = 0,
|
|
9917
|
+
shadow = false,
|
|
9918
|
+
maxWidth = 114,
|
|
9919
|
+
lineHeight = 9,
|
|
9920
|
+
...style
|
|
9921
|
+
} = options;
|
|
9922
|
+
this.validatePosition(position);
|
|
9923
|
+
for (const state of [previous, next]) {
|
|
9924
|
+
if (!["hidden", "normal", "hover"].includes(state)) throw new Error(`Unsupported book button state: ${state}`);
|
|
9925
|
+
}
|
|
9926
|
+
const layers = [{
|
|
9927
|
+
...await this.cropTexture("minecraft:gui/book", [0, 0, 192, 192], [256, 256], position),
|
|
9928
|
+
name: "book-background"
|
|
9929
|
+
}];
|
|
9930
|
+
for (const [name, direction, state, x] of [
|
|
9931
|
+
["previous", "backward", previous, 43],
|
|
9932
|
+
["next", "forward", next, 116]
|
|
9933
|
+
]) {
|
|
9934
|
+
if (state === "hidden") continue;
|
|
9935
|
+
layers.push({
|
|
9936
|
+
name: `book-${name}`,
|
|
9937
|
+
texture: `minecraft:gui/sprites/widget/page_${direction}${state === "hover" ? "_highlighted" : ""}`,
|
|
9938
|
+
position: [position[0] + x, position[1] + 157],
|
|
9939
|
+
size: [23, 13]
|
|
9940
|
+
});
|
|
9941
|
+
}
|
|
9942
|
+
if (text2 !== void 0) {
|
|
9943
|
+
layers.push({
|
|
9944
|
+
name: "book-text",
|
|
9945
|
+
text: text2,
|
|
9946
|
+
position: [position[0] + 36, position[1] + 30],
|
|
9947
|
+
color: color2,
|
|
9948
|
+
shadow,
|
|
9949
|
+
maxWidth,
|
|
9950
|
+
lineHeight,
|
|
9951
|
+
...style
|
|
9952
|
+
});
|
|
9953
|
+
}
|
|
9954
|
+
return layers;
|
|
9955
|
+
}
|
|
9956
|
+
/** Creates a container background. Use {@link GUI_CONTAINER_LAYOUTS} with {@link inventorySlot} to place its items. */
|
|
9957
|
+
static async container(type, position = [0, 0]) {
|
|
9958
|
+
this.validatePosition(position);
|
|
9959
|
+
if (type !== "generic_54" && type !== "crafting_table") throw new Error(`Unsupported GUI container: ${type}`);
|
|
9960
|
+
const layout = GUI_CONTAINER_LAYOUTS[type];
|
|
9961
|
+
return [{ ...await this.cropTexture(layout.texture, layout.crop, [256, 256], position), name: "background" }];
|
|
9962
|
+
}
|
|
9963
|
+
static validatePosition(position) {
|
|
9964
|
+
if (!Array.isArray(position) || position.length !== 2 || !position.every(Number.isFinite)) {
|
|
9965
|
+
throw new Error("GUI position must contain two finite coordinates");
|
|
9966
|
+
}
|
|
9967
|
+
}
|
|
9968
|
+
static async cropTexture(texture, crop, referenceSize, position) {
|
|
9969
|
+
const image = await ModelTextures.preload(AssetKey.parse("textures", texture));
|
|
9970
|
+
if (!image) throw new Error(`Could not load GUI texture ${texture}`);
|
|
9971
|
+
const [x, y, width, height] = crop;
|
|
9972
|
+
const scaleX = image.width / referenceSize[0], scaleY = image.height / referenceSize[1];
|
|
9973
|
+
return {
|
|
9974
|
+
texture,
|
|
9975
|
+
crop: [x * scaleX, y * scaleY, width * scaleX, height * scaleY],
|
|
9976
|
+
position: [...position],
|
|
9977
|
+
size: [width, height]
|
|
9978
|
+
};
|
|
9979
|
+
}
|
|
9028
9980
|
/** Sizes a tooltip around supplied text; the first entry is its title. */
|
|
9029
9981
|
static async tooltip(lines, options = {}) {
|
|
9030
9982
|
if (!lines.length) return [];
|
|
@@ -9040,20 +9992,28 @@ var GuiHelper = class {
|
|
|
9040
9992
|
size: [width + 24, height + 24]
|
|
9041
9993
|
}));
|
|
9042
9994
|
let y = position[1];
|
|
9043
|
-
lines.forEach((
|
|
9044
|
-
layers.push({ ...textOptions, name: `tooltip-line-${index}`, text, position: [position[0], y] });
|
|
9995
|
+
lines.forEach((text2, index) => {
|
|
9996
|
+
layers.push({ ...textOptions, name: `tooltip-line-${index}`, text: text2, position: [position[0], y] });
|
|
9045
9997
|
y += layouts[index].height + (index === 0 ? titleGap : 0);
|
|
9046
9998
|
});
|
|
9047
9999
|
return layers;
|
|
9048
10000
|
}
|
|
10001
|
+
/**
|
|
10002
|
+
* Calculates a slot's top-left position in GUI pixels.
|
|
10003
|
+
* @param slot - Zero-based slot index, or `[column, row]`.
|
|
10004
|
+
* @param origin - Position of the first slot.
|
|
10005
|
+
* @param offset - Horizontal and vertical spacing between slot origins, in pixels.
|
|
10006
|
+
* @param rowSize - Slots per row when `slot` is an index.
|
|
10007
|
+
*/
|
|
9049
10008
|
static inventorySlot(slot, origin = [0, 0], offset = [18, 18], rowSize = 9) {
|
|
9050
10009
|
const [column, row] = typeof slot === "number" ? [slot % rowSize, Math.floor(slot / rowSize)] : slot;
|
|
9051
10010
|
return [origin[0] + column * offset[0], origin[1] + row * offset[1]];
|
|
9052
10011
|
}
|
|
9053
10012
|
/** Creates crafting-table layers for ingredients and the result; stack counts are not drawn. */
|
|
9054
10013
|
static recipe(recipe, options = {}) {
|
|
10014
|
+
const layout = GUI_CONTAINER_LAYOUTS.crafting_table;
|
|
9055
10015
|
const layers = [
|
|
9056
|
-
{ name: "background", texture:
|
|
10016
|
+
{ name: "background", texture: layout.texture, crop: [...layout.crop] }
|
|
9057
10017
|
];
|
|
9058
10018
|
const itemKey = (ingredient) => {
|
|
9059
10019
|
const item = typeof ingredient === "string" && !ingredient.startsWith("#") ? ingredient : typeof ingredient === "object" && "item" in ingredient ? ingredient.item : options.resolveIngredient?.(ingredient);
|
|
@@ -9068,17 +10028,17 @@ var GuiHelper = class {
|
|
|
9068
10028
|
layers.push({
|
|
9069
10029
|
name: `ingredient-${slot}`,
|
|
9070
10030
|
item: itemKey(ingredient),
|
|
9071
|
-
position: this.inventorySlot(slot,
|
|
10031
|
+
position: this.inventorySlot(slot, layout.slotOrigin, layout.slotOffset, layout.rowSize)
|
|
9072
10032
|
});
|
|
9073
10033
|
};
|
|
9074
10034
|
switch (recipe.type) {
|
|
9075
10035
|
case "minecraft:crafting_shaped":
|
|
9076
10036
|
case "crafting_shaped": {
|
|
9077
|
-
const
|
|
9078
|
-
if (!
|
|
10037
|
+
const pattern2 = recipe.pattern;
|
|
10038
|
+
if (!pattern2.length || pattern2.length > 3 || !pattern2[0].length || pattern2[0].length > 3 || pattern2.some((row) => row.length !== pattern2[0].length)) {
|
|
9079
10039
|
throw new Error("Crafting patterns must be rectangular, with 1\u20133 rows and columns");
|
|
9080
10040
|
}
|
|
9081
|
-
const cells =
|
|
10041
|
+
const cells = pattern2.flatMap((row, y) => row.split("").map((symbol, x) => ({ symbol, x, y }))).filter((cell) => cell.symbol !== " ");
|
|
9082
10042
|
if (!cells.length) throw new Error("Crafting patterns must contain an ingredient");
|
|
9083
10043
|
const minX = Math.min(...cells.map((cell) => cell.x)), maxX = Math.max(...cells.map((cell) => cell.x));
|
|
9084
10044
|
const minY = Math.min(...cells.map((cell) => cell.y)), maxY = Math.max(...cells.map((cell) => cell.y));
|
|
@@ -9103,7 +10063,7 @@ var GuiHelper = class {
|
|
|
9103
10063
|
layers.push({
|
|
9104
10064
|
name: "result",
|
|
9105
10065
|
item: itemKey("id" in recipe.result ? recipe.result.id : recipe.result.item),
|
|
9106
|
-
position: [
|
|
10066
|
+
position: [...layout.resultPosition]
|
|
9107
10067
|
});
|
|
9108
10068
|
return layers;
|
|
9109
10069
|
}
|
|
@@ -9197,7 +10157,6 @@ init_SceneObject();
|
|
|
9197
10157
|
init_Materials();
|
|
9198
10158
|
init_ModelObject();
|
|
9199
10159
|
init_DisplayPosition();
|
|
9200
|
-
init_GuiLight();
|
|
9201
10160
|
import { Box2, Box3 as Box32, DoubleSide as DoubleSide3, Group, Matrix4 as Matrix410, Mesh as Mesh6, MeshBasicMaterial as MeshBasicMaterial5, Vector2 as Vector23 } from "three";
|
|
9202
10161
|
var GuiObject = class extends SceneObject {
|
|
9203
10162
|
constructor(textureLayers, options) {
|
|
@@ -9266,12 +10225,16 @@ var GuiObject = class extends SceneObject {
|
|
|
9266
10225
|
mesh2.geometry.computeBoundingBox();
|
|
9267
10226
|
bounds.union(mesh2.geometry.boundingBox.clone().applyMatrix4(new Matrix410().multiplyMatrices(inverse, mesh2.matrixWorld)));
|
|
9268
10227
|
});
|
|
9269
|
-
item.position.z = depth
|
|
9270
|
-
|
|
10228
|
+
item.position.z = depth;
|
|
10229
|
+
if (!bounds.isEmpty()) {
|
|
10230
|
+
item.position.z -= bounds.min.z;
|
|
10231
|
+
depth += bounds.max.z - bounds.min.z;
|
|
10232
|
+
this.bounds.expandByPoint(new Vector23(bounds.min.x, -bounds.max.y));
|
|
10233
|
+
this.bounds.expandByPoint(new Vector23(bounds.max.x, -bounds.min.y));
|
|
10234
|
+
}
|
|
10235
|
+
depth += 0.01;
|
|
9271
10236
|
this.bounds.expandByPoint(new Vector23(x, y));
|
|
9272
10237
|
this.bounds.expandByPoint(new Vector23(x + width2, y + height2));
|
|
9273
|
-
this.bounds.expandByPoint(new Vector23(bounds.min.x, -bounds.max.y));
|
|
9274
|
-
this.bounds.expandByPoint(new Vector23(bounds.max.x, -bounds.min.y));
|
|
9275
10238
|
continue;
|
|
9276
10239
|
}
|
|
9277
10240
|
const key = typeof layer.texture === "string" ? AssetKey.parse("textures", layer.texture) : layer.texture;
|
|
@@ -9300,7 +10263,7 @@ var GuiObject = class extends SceneObject {
|
|
|
9300
10263
|
}
|
|
9301
10264
|
async createItem(layer, index) {
|
|
9302
10265
|
const key = typeof layer.item === "string" ? AssetKey.parse("models", layer.item) : layer.item;
|
|
9303
|
-
const model = await Models.getMerged(key);
|
|
10266
|
+
const model = await Models.getMerged(key, { ...layer.context, displayContext: "gui" /* GUI */ });
|
|
9304
10267
|
if (!model) throw new Error(`Could not load GUI item ${key.toNamespacedString()}`);
|
|
9305
10268
|
const item = new ModelObject(model, {
|
|
9306
10269
|
displayPosition: "gui" /* GUI */,
|
|
@@ -9311,15 +10274,13 @@ var GuiObject = class extends SceneObject {
|
|
|
9311
10274
|
item.name = `group:${layer.name ?? index}`;
|
|
9312
10275
|
this.add(item);
|
|
9313
10276
|
await item.init();
|
|
9314
|
-
|
|
9315
|
-
|
|
10277
|
+
const meshes = [];
|
|
10278
|
+
item.iterateAllMeshes((mesh) => meshes.push(mesh));
|
|
10279
|
+
meshes.forEach((mesh, partIndex) => {
|
|
9316
10280
|
mesh.name = `mesh:${layer.name ?? index}`;
|
|
9317
|
-
mesh.renderOrder = index;
|
|
10281
|
+
mesh.renderOrder = index + partIndex / (meshes.length + 1) * 0.5;
|
|
9318
10282
|
for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
|
|
9319
10283
|
material.transparent = true;
|
|
9320
|
-
if (model.gui_light === "front" /* FRONT */ && material.uniforms?.SHADE) {
|
|
9321
|
-
material.uniforms.SHADE.value = false;
|
|
9322
|
-
}
|
|
9323
10284
|
}
|
|
9324
10285
|
});
|
|
9325
10286
|
return item;
|
|
@@ -9490,29 +10451,29 @@ var SceneInspector = class {
|
|
|
9490
10451
|
this.objectControlsContainer.innerHTML = "";
|
|
9491
10452
|
this.addControls(targetObject, targetIntersection, instance);
|
|
9492
10453
|
}
|
|
9493
|
-
addControls(
|
|
10454
|
+
addControls(object3, intersection, instance) {
|
|
9494
10455
|
const container = document.createElement("div");
|
|
9495
10456
|
container.append(this.separator("Select Parent/Child"));
|
|
9496
|
-
if (
|
|
9497
|
-
container.append(this.buttonControl("Select Parent " +
|
|
9498
|
-
this.selectObject(
|
|
10457
|
+
if (object3.parent) {
|
|
10458
|
+
container.append(this.buttonControl("Select Parent " + object3.parent.constructor.name + " " + object3.parent.name, "P", () => {
|
|
10459
|
+
this.selectObject(object3.parent, intersection, instance);
|
|
9499
10460
|
}));
|
|
9500
10461
|
}
|
|
9501
|
-
if (
|
|
10462
|
+
if (object3.children.length > 0) {
|
|
9502
10463
|
let i = 1;
|
|
9503
|
-
for (let child of
|
|
10464
|
+
for (let child of object3.children) {
|
|
9504
10465
|
container.append(this.buttonControl("Select Child " + child.constructor.name + " " + child.name, "C" + i++, () => {
|
|
9505
10466
|
this.selectObject(child, intersection, instance);
|
|
9506
10467
|
}));
|
|
9507
10468
|
}
|
|
9508
10469
|
}
|
|
9509
10470
|
container.append(this.separator());
|
|
9510
|
-
const selectedInstance = instance && (
|
|
10471
|
+
const selectedInstance = instance && (object3 === instance.instanceable || object3 === intersection?.object) ? instance : void 0;
|
|
9511
10472
|
if (!selectedInstance) {
|
|
9512
|
-
container.append(this.toggleControl("Visibility", "V",
|
|
10473
|
+
container.append(this.toggleControl("Visibility", "V", object3.visible, (v) => object3.visible = v));
|
|
9513
10474
|
}
|
|
9514
10475
|
container.append(this.separator("Mesh"));
|
|
9515
|
-
this.addObjectControls(
|
|
10476
|
+
this.addObjectControls(object3, intersection, container, selectedInstance);
|
|
9516
10477
|
container.append(this.separator());
|
|
9517
10478
|
this.objectControlsContainer.append(container);
|
|
9518
10479
|
}
|
|
@@ -9703,6 +10664,7 @@ var SceneStatsDisplay = class {
|
|
|
9703
10664
|
appendTo(el) {
|
|
9704
10665
|
el.append(this.statsContainer);
|
|
9705
10666
|
}
|
|
10667
|
+
/** Stops the refresh timer and removes the panel from the page. */
|
|
9706
10668
|
dispose() {
|
|
9707
10669
|
clearInterval(this.interval);
|
|
9708
10670
|
this.interval = void 0;
|
|
@@ -9727,6 +10689,7 @@ var InstanceManager = class {
|
|
|
9727
10689
|
this.isInstanceManager = true;
|
|
9728
10690
|
this.instanceCache = {};
|
|
9729
10691
|
}
|
|
10692
|
+
/** Allocates a new placement of a cached model, or returns `undefined` if the key is unknown. */
|
|
9730
10693
|
async get(key) {
|
|
9731
10694
|
if (key in this.instanceCache) {
|
|
9732
10695
|
console.debug(p9, "key in cache", key);
|
|
@@ -9734,6 +10697,7 @@ var InstanceManager = class {
|
|
|
9734
10697
|
}
|
|
9735
10698
|
return void 0;
|
|
9736
10699
|
}
|
|
10700
|
+
/** Allocates a placement, calling the supplier once when the shared model is first needed. */
|
|
9737
10701
|
async getOrCreate(key, supplier) {
|
|
9738
10702
|
if (key in this.instanceCache) {
|
|
9739
10703
|
return await this.get(key);
|
|
@@ -9750,6 +10714,7 @@ var InstanceManager = class {
|
|
|
9750
10714
|
throw error;
|
|
9751
10715
|
}
|
|
9752
10716
|
}
|
|
10717
|
+
/** Forgets cached models without disposing them or releasing their existing placements. */
|
|
9753
10718
|
reset() {
|
|
9754
10719
|
for (let instanceCacheKey in this.instanceCache) {
|
|
9755
10720
|
delete this.instanceCache[instanceCacheKey];
|
|
@@ -9783,7 +10748,7 @@ init_SpecialItems();
|
|
|
9783
10748
|
init_BlockStates();
|
|
9784
10749
|
init_MineRenderError();
|
|
9785
10750
|
init_util();
|
|
9786
|
-
import { Euler as
|
|
10751
|
+
import { Euler as Euler9 } from "three";
|
|
9787
10752
|
function matchesCondition(condition, state) {
|
|
9788
10753
|
return Object.entries(condition).every(([key, value]) => {
|
|
9789
10754
|
if (Array.isArray(value)) {
|
|
@@ -9795,6 +10760,7 @@ function matchesCondition(condition, state) {
|
|
|
9795
10760
|
});
|
|
9796
10761
|
}
|
|
9797
10762
|
var BlockStateResolver = class {
|
|
10763
|
+
/** Loads vanilla property defaults, falling back to preview values inferred from the blockstate file. */
|
|
9798
10764
|
static async defaults(blockState) {
|
|
9799
10765
|
const defaults = blockState.key ? await BlockStates.getDefaultState(blockState.key) : void 0;
|
|
9800
10766
|
const state = {};
|
|
@@ -9814,9 +10780,11 @@ var BlockStateResolver = class {
|
|
|
9814
10780
|
}
|
|
9815
10781
|
return state;
|
|
9816
10782
|
}
|
|
10783
|
+
/** Selects matching models. Supply `choose` to control selection within each alternatives array. */
|
|
9817
10784
|
static select(blockState, state, choose = this.choose) {
|
|
9818
10785
|
return this.matching(blockState, state).map(choose);
|
|
9819
10786
|
}
|
|
10787
|
+
/** Returns matching model groups before choosing weighted alternatives. */
|
|
9820
10788
|
static matching(blockState, state) {
|
|
9821
10789
|
const out = [];
|
|
9822
10790
|
if (blockState.variants) {
|
|
@@ -9840,6 +10808,7 @@ var BlockStateResolver = class {
|
|
|
9840
10808
|
}
|
|
9841
10809
|
return out;
|
|
9842
10810
|
}
|
|
10811
|
+
/** Selects a random alternative using positive integer weights, each defaulting to 1. */
|
|
9843
10812
|
static choose(variants) {
|
|
9844
10813
|
if (!Array.isArray(variants)) return variants;
|
|
9845
10814
|
if (!variants.length) throw new MineRenderError("Blockstate variant arrays must not be empty");
|
|
@@ -9857,8 +10826,9 @@ var BlockStateResolver = class {
|
|
|
9857
10826
|
}
|
|
9858
10827
|
return variants[variants.length - 1];
|
|
9859
10828
|
}
|
|
10829
|
+
/** Converts blockstate rotations in degrees into the Euler rotation used by model rendering. */
|
|
9860
10830
|
static rotation(variant) {
|
|
9861
|
-
const rotation = new
|
|
10831
|
+
const rotation = new Euler9();
|
|
9862
10832
|
if (typeof variant.x !== "undefined") {
|
|
9863
10833
|
rotation.x = toRadians(clampRotationDegrees(variant.x));
|
|
9864
10834
|
}
|
|
@@ -9919,29 +10889,29 @@ var BlockTints = class {
|
|
|
9919
10889
|
}
|
|
9920
10890
|
}
|
|
9921
10891
|
if (!indices.size) return tints;
|
|
9922
|
-
let
|
|
10892
|
+
let color2;
|
|
9923
10893
|
switch (rule.source) {
|
|
9924
10894
|
case "constant":
|
|
9925
|
-
|
|
10895
|
+
color2 = rule.color;
|
|
9926
10896
|
break;
|
|
9927
10897
|
case "grass":
|
|
9928
|
-
|
|
10898
|
+
color2 = await Colormaps.grassColor(key);
|
|
9929
10899
|
break;
|
|
9930
10900
|
case "redstone": {
|
|
9931
10901
|
const power = Number(state.power ?? 0) / 15;
|
|
9932
10902
|
const red = power * 0.6 + (power > 0 ? 0.4 : 0.3);
|
|
9933
10903
|
const green = Math.max(0, power * power * 0.7 - 0.5);
|
|
9934
10904
|
const blue = Math.max(0, power * power * 0.6 - 0.7);
|
|
9935
|
-
|
|
10905
|
+
color2 = Math.floor(red * 255) << 16 | Math.floor(green * 255) << 8 | Math.floor(blue * 255);
|
|
9936
10906
|
break;
|
|
9937
10907
|
}
|
|
9938
10908
|
case "stem": {
|
|
9939
10909
|
const age = Number(state.age ?? 0);
|
|
9940
|
-
|
|
10910
|
+
color2 = age * 32 << 16 | 255 - age * 8 << 8 | age * 4;
|
|
9941
10911
|
break;
|
|
9942
10912
|
}
|
|
9943
10913
|
}
|
|
9944
|
-
return { ...Object.fromEntries([...indices].map((index) => [index,
|
|
10914
|
+
return { ...Object.fromEntries([...indices].map((index) => [index, color2])), ...tints };
|
|
9945
10915
|
}
|
|
9946
10916
|
};
|
|
9947
10917
|
|
|
@@ -9953,7 +10923,7 @@ var import_ts_deepmerge6 = __toESM(require_dist());
|
|
|
9953
10923
|
init_MineRenderError();
|
|
9954
10924
|
init_InstanceReference();
|
|
9955
10925
|
init_AssetKey();
|
|
9956
|
-
import { Euler as
|
|
10926
|
+
import { Euler as Euler10, Matrix4 as Matrix411, Quaternion as Quaternion5, Vector3 as Vector312 } from "three";
|
|
9957
10927
|
init_ModelCulling();
|
|
9958
10928
|
init_FluidGeometry();
|
|
9959
10929
|
init_Entities();
|
|
@@ -10083,8 +11053,8 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
10083
11053
|
return false;
|
|
10084
11054
|
}
|
|
10085
11055
|
return this._models.some((model) => {
|
|
10086
|
-
const
|
|
10087
|
-
return
|
|
11056
|
+
const object3 = isInstanceReference(model) ? model.instanceable : model;
|
|
11057
|
+
return object3.isOpaqueFullCube && this.getModelCullMask(model, 63) === 63;
|
|
10088
11058
|
});
|
|
10089
11059
|
}
|
|
10090
11060
|
getModelMatrix(model) {
|
|
@@ -10093,14 +11063,14 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
10093
11063
|
return model.matrix.clone();
|
|
10094
11064
|
}
|
|
10095
11065
|
getModelCullMask(model, worldMask) {
|
|
10096
|
-
const
|
|
10097
|
-
if (
|
|
11066
|
+
const object3 = isInstanceReference(model) ? model.instanceable : model;
|
|
11067
|
+
if (object3.options.displayPosition) return 0;
|
|
10098
11068
|
const position = new Vector312();
|
|
10099
11069
|
const rotation = new Quaternion5();
|
|
10100
11070
|
const scale = new Vector312();
|
|
10101
11071
|
this.getModelMatrix(model).decompose(position, rotation, scale);
|
|
10102
11072
|
if (position.distanceToSquared(this.position) > 1e-10 || scale.distanceToSquared(new Vector312(1, 1, 1)) > 1e-10) return 0;
|
|
10103
|
-
return ModelCulling.toLocalMask(worldMask, new
|
|
11073
|
+
return ModelCulling.toLocalMask(worldMask, new Euler10().setFromQuaternion(rotation));
|
|
10104
11074
|
}
|
|
10105
11075
|
async setCullMask(worldMask) {
|
|
10106
11076
|
worldMask &= 63;
|
|
@@ -10111,14 +11081,14 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
10111
11081
|
replacements.push(model);
|
|
10112
11082
|
continue;
|
|
10113
11083
|
}
|
|
10114
|
-
const
|
|
11084
|
+
const object3 = isInstanceReference(model) ? model.instanceable : model;
|
|
10115
11085
|
const cullMask = this.getModelCullMask(model, worldMask);
|
|
10116
|
-
if (cullMask === (
|
|
11086
|
+
if (cullMask === (object3.options.cullMask ?? 0)) {
|
|
10117
11087
|
replacements.push(model);
|
|
10118
11088
|
continue;
|
|
10119
11089
|
}
|
|
10120
11090
|
const matrix = this.getModelMatrix(model);
|
|
10121
|
-
const replacement = await this.scene.addModel(
|
|
11091
|
+
const replacement = await this.scene.addModel(object3.originalModel, { ...object3.options, cullMask });
|
|
10122
11092
|
replacements.push(replacement);
|
|
10123
11093
|
if (isInstanceReference(replacement)) {
|
|
10124
11094
|
replacement.setMatrix(matrix);
|
|
@@ -10126,8 +11096,8 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
10126
11096
|
replacement.matrix.copy(matrix);
|
|
10127
11097
|
replacement.matrixWorldNeedsUpdate = true;
|
|
10128
11098
|
matrix.decompose(replacement.position, replacement.quaternion, replacement.scale);
|
|
10129
|
-
replacement.matrixAutoUpdate =
|
|
10130
|
-
replacement.visible =
|
|
11099
|
+
replacement.matrixAutoUpdate = object3.matrixAutoUpdate;
|
|
11100
|
+
replacement.visible = object3.visible;
|
|
10131
11101
|
}
|
|
10132
11102
|
}
|
|
10133
11103
|
} catch (error) {
|
|
@@ -10143,6 +11113,7 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
10143
11113
|
}
|
|
10144
11114
|
this.notifyDirty();
|
|
10145
11115
|
}
|
|
11116
|
+
/** Selected property values. Use {@link setState} to change properties and rebuild the rendered models. */
|
|
10146
11117
|
get state() {
|
|
10147
11118
|
return this._state;
|
|
10148
11119
|
}
|
|
@@ -10242,6 +11213,7 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
10242
11213
|
obj.setPosition(this.position);
|
|
10243
11214
|
return obj;
|
|
10244
11215
|
}
|
|
11216
|
+
/** Clears selected properties and rebuilds models without reapplying property defaults. */
|
|
10245
11217
|
async resetState() {
|
|
10246
11218
|
this._previousState = this._state;
|
|
10247
11219
|
this._state = {};
|
|
@@ -12045,9 +13017,9 @@ var SchematicParser = class {
|
|
|
12045
13017
|
* Parses a legacy schematic using custom mappings before the bundled defaults.
|
|
12046
13018
|
* Keys are `id:metadata`; values are block states such as `minecraft:oak_log[axis=x]`.
|
|
12047
13019
|
*/
|
|
12048
|
-
static async parse(nbt, customMappings = {}) {
|
|
13020
|
+
static async parse(nbt, customMappings = {}, options = {}) {
|
|
12049
13021
|
const tags = nbt.value;
|
|
12050
|
-
if (tags.Materials?.type !== "string" || tags.Materials.value !== "Alpha") {
|
|
13022
|
+
if (!options.lenient && (tags.Materials?.type !== "string" || tags.Materials.value !== "Alpha")) {
|
|
12051
13023
|
throw new MineRenderError("Schematic Materials must be Alpha");
|
|
12052
13024
|
}
|
|
12053
13025
|
const size = ["Width", "Height", "Length"].map((name) => {
|
|
@@ -12088,8 +13060,16 @@ var SchematicParser = class {
|
|
|
12088
13060
|
const id = high << 8 | ids.value[index] & 255;
|
|
12089
13061
|
const metadata = data.value[index] & 255;
|
|
12090
13062
|
const key = `${id}:${metadata}`;
|
|
12091
|
-
|
|
12092
|
-
if (!mapped
|
|
13063
|
+
let mapped = customMappings[key] ?? mapping[key];
|
|
13064
|
+
if (!mapped && options.lenient) {
|
|
13065
|
+
mapped = customMappings[`${id}:0`] ?? mapping[`${id}:0`];
|
|
13066
|
+
}
|
|
13067
|
+
if (!mapped) {
|
|
13068
|
+
if (options.lenient) {
|
|
13069
|
+
continue;
|
|
13070
|
+
}
|
|
13071
|
+
throw new MineRenderError(`Unsupported legacy block ${id}:${metadata} at schematic index ${index}`);
|
|
13072
|
+
}
|
|
12093
13073
|
const [type, state] = mapped.split("[");
|
|
12094
13074
|
if (type === "minecraft:air") continue;
|
|
12095
13075
|
const properties = {};
|
|
@@ -12133,6 +13113,11 @@ function compoundList(tags, name) {
|
|
|
12133
13113
|
// src/model/multiblock/StructureParser.ts
|
|
12134
13114
|
init_MineRenderError();
|
|
12135
13115
|
var StructureParser = class {
|
|
13116
|
+
/**
|
|
13117
|
+
* Parses decoded NBT, omitting air blocks.
|
|
13118
|
+
* @param _nbt - Structure data decoded by {@link NBTHelper.fromBuffer}.
|
|
13119
|
+
* @param paletteIndex - Palette to select when the structure has several. Defaults to 0.
|
|
13120
|
+
*/
|
|
12136
13121
|
static async parse(_nbt, paletteIndex) {
|
|
12137
13122
|
const nbt = _nbt.value;
|
|
12138
13123
|
let palette;
|
|
@@ -12967,6 +13952,7 @@ var SkinImage = class {
|
|
|
12967
13952
|
// src/skin/SkinTextures.ts
|
|
12968
13953
|
init_Textures();
|
|
12969
13954
|
var SkinTextures = class {
|
|
13955
|
+
/** Loads a skin URL. Pass `legacy` to override layout detection from the image dimensions. */
|
|
12970
13956
|
static async get(src, legacy) {
|
|
12971
13957
|
const image = await SkinImage.getData(src);
|
|
12972
13958
|
const scale = image.width / 64;
|
|
@@ -13140,6 +14126,14 @@ var SkinObject = class extends SceneObject {
|
|
|
13140
14126
|
}
|
|
13141
14127
|
}
|
|
13142
14128
|
}
|
|
14129
|
+
/**
|
|
14130
|
+
* Loads a skin texture, detecting slim arms and legacy layouts by default.
|
|
14131
|
+
* To override detection, set `slim` or `legacy` in the options passed to
|
|
14132
|
+
* `new SkinObject(options)` or `scene.addSkin(src, options)`.
|
|
14133
|
+
* Use {@link setSlim} or {@link setLegacy} to change these settings later.
|
|
14134
|
+
*
|
|
14135
|
+
* @param src - URL of the skin texture.
|
|
14136
|
+
*/
|
|
13143
14137
|
async setSkinTexture(src) {
|
|
13144
14138
|
if (typeof src === "undefined") return;
|
|
13145
14139
|
this.skinTextureSrc = src;
|
|
@@ -13250,59 +14244,61 @@ var MineRenderScene = class _MineRenderScene extends Scene4 {
|
|
|
13250
14244
|
this.DEFAULT_OPTIONS = (0, import_ts_deepmerge8.default)({}, {});
|
|
13251
14245
|
}
|
|
13252
14246
|
add(...objects) {
|
|
13253
|
-
for (const
|
|
14247
|
+
for (const object3 of objects) {
|
|
13254
14248
|
this.dirty = true;
|
|
13255
14249
|
try {
|
|
13256
|
-
super.add(
|
|
14250
|
+
super.add(object3);
|
|
13257
14251
|
} finally {
|
|
13258
|
-
if (
|
|
13259
|
-
this.updateObjectRegistration(
|
|
14252
|
+
if (object3?.isObject3D && object3 !== this) {
|
|
14253
|
+
this.updateObjectRegistration(object3, object3.parent === this);
|
|
13260
14254
|
}
|
|
13261
14255
|
}
|
|
13262
14256
|
}
|
|
13263
14257
|
return this;
|
|
13264
14258
|
}
|
|
13265
14259
|
remove(...objects) {
|
|
13266
|
-
for (const
|
|
13267
|
-
if (!this.children.includes(
|
|
14260
|
+
for (const object3 of objects) {
|
|
14261
|
+
if (!this.children.includes(object3)) continue;
|
|
13268
14262
|
this.dirty = true;
|
|
13269
|
-
this.updateObjectRegistration(
|
|
14263
|
+
this.updateObjectRegistration(object3, false);
|
|
13270
14264
|
try {
|
|
13271
|
-
super.remove(
|
|
14265
|
+
super.remove(object3);
|
|
13272
14266
|
} finally {
|
|
13273
|
-
this.updateObjectRegistration(
|
|
14267
|
+
this.updateObjectRegistration(object3, object3.parent === this);
|
|
13274
14268
|
}
|
|
13275
14269
|
}
|
|
13276
14270
|
return this;
|
|
13277
14271
|
}
|
|
13278
|
-
updateObjectRegistration(
|
|
13279
|
-
if (this.observedObjects.has(
|
|
13280
|
-
const observable =
|
|
14272
|
+
updateObjectRegistration(object3, attached) {
|
|
14273
|
+
if (this.observedObjects.has(object3) === attached) return;
|
|
14274
|
+
const observable = object3;
|
|
13281
14275
|
if (attached) {
|
|
13282
|
-
this.observedObjects.add(
|
|
14276
|
+
this.observedObjects.add(object3);
|
|
13283
14277
|
observable.addEventListener("change", this.onObjectChange);
|
|
13284
14278
|
} else {
|
|
13285
|
-
this.observedObjects.delete(
|
|
14279
|
+
this.observedObjects.delete(object3);
|
|
13286
14280
|
observable.removeEventListener("change", this.onObjectChange);
|
|
13287
14281
|
}
|
|
13288
14282
|
const change = attached ? 1 : -1;
|
|
13289
14283
|
this.stats.objectCount += change;
|
|
13290
|
-
if (isSceneObject(
|
|
14284
|
+
if (isSceneObject(object3)) {
|
|
13291
14285
|
this.stats.sceneObjectCount += change;
|
|
13292
14286
|
}
|
|
13293
14287
|
}
|
|
13294
|
-
|
|
14288
|
+
/** Initializes each object before attaching it to the scene. */
|
|
14289
|
+
async initAndAdd(...object3) {
|
|
13295
14290
|
this.dirty = true;
|
|
13296
|
-
for (let obj of
|
|
14291
|
+
for (let obj of object3) {
|
|
13297
14292
|
await obj.init();
|
|
13298
14293
|
}
|
|
13299
|
-
return this.add(...
|
|
14294
|
+
return this.add(...object3);
|
|
13300
14295
|
}
|
|
13301
|
-
async addSceneObject(
|
|
14296
|
+
async addSceneObject(asset2, objectSupplier, _options, parent = this) {
|
|
13302
14297
|
this.dirty = true;
|
|
13303
14298
|
const obj = await objectSupplier();
|
|
13304
|
-
if (obj
|
|
13305
|
-
|
|
14299
|
+
if (isModelObject(obj)) obj.options.tints = await ItemTints.get(obj.originalModel, obj.options.tints);
|
|
14300
|
+
if (obj?.options?.instanceMeshes && asset2.key && asset2.key?.assetType === "models") {
|
|
14301
|
+
let key = asset2.key.serialize();
|
|
13306
14302
|
if (isModelObject(obj)) {
|
|
13307
14303
|
const { displayPosition, uvLockRotation, tints, cullMask } = obj.options;
|
|
13308
14304
|
if (displayPosition) key += `|display:${displayPosition}`;
|
|
@@ -13326,13 +14322,32 @@ var MineRenderScene = class _MineRenderScene extends Scene4 {
|
|
|
13326
14322
|
return obj;
|
|
13327
14323
|
}
|
|
13328
14324
|
}
|
|
14325
|
+
/**
|
|
14326
|
+
* Adds a model loaded with {@link Models.getMerged}.
|
|
14327
|
+
* Set `options.instanceMeshes` to `false` to create a separate ModelObject.
|
|
14328
|
+
*
|
|
14329
|
+
* @param parent - Object to attach the model to. Defaults to this scene.
|
|
14330
|
+
* @returns The model, or a reference to one placement of a shared model.
|
|
14331
|
+
*/
|
|
13329
14332
|
async addModel(model, options, parent = this) {
|
|
13330
|
-
options = { ...options, tints: await ItemTints.get(model, options?.tints) };
|
|
13331
14333
|
return this.addSceneObject(model, () => new ModelObject(model, options), options, parent);
|
|
13332
14334
|
}
|
|
14335
|
+
/**
|
|
14336
|
+
* Adds the models selected by a blockstate loaded with {@link BlockStates.get}.
|
|
14337
|
+
* Set `options.initialState` to choose block properties such as `facing` or `axis`.
|
|
14338
|
+
* Use the returned object's `setPosition` method to move all of its model parts.
|
|
14339
|
+
*/
|
|
13333
14340
|
async addBlock(blockState, options, parent = this) {
|
|
13334
14341
|
return this.addSceneObject(blockState, () => new BlockObject(blockState, options), options, parent);
|
|
13335
14342
|
}
|
|
14343
|
+
/**
|
|
14344
|
+
* Loads a skin and adds an initialized player model.
|
|
14345
|
+
*
|
|
14346
|
+
* @param skin - Texture URL, such as one returned by {@link Skins.fromUuidOrUsername}.
|
|
14347
|
+
* Omit it to assign a texture later with {@link SkinObject.setSkinTexture}.
|
|
14348
|
+
* @param options - Player settings, including `slim` and `legacy` detection overrides.
|
|
14349
|
+
* @param parent - Object to attach the player to. Defaults to this scene.
|
|
14350
|
+
*/
|
|
13336
14351
|
async addSkin(skin, options, parent = this) {
|
|
13337
14352
|
this.dirty = true;
|
|
13338
14353
|
const obj = new SkinObject(options);
|
|
@@ -13344,9 +14359,14 @@ var MineRenderScene = class _MineRenderScene extends Scene4 {
|
|
|
13344
14359
|
parent.add(obj);
|
|
13345
14360
|
return obj;
|
|
13346
14361
|
}
|
|
14362
|
+
/** Adds an entity loaded with {@link Entities.getEntity}, using its selected layers and textures. */
|
|
13347
14363
|
async addEntity(entity, options, parent = this) {
|
|
13348
14364
|
return this.addSceneObject(entity, () => new EntityObject(entity, options), options, parent);
|
|
13349
14365
|
}
|
|
14366
|
+
/**
|
|
14367
|
+
* Adds GUI layers in draw order. One GUI pixel equals one scene unit.
|
|
14368
|
+
* Layer positions start at the top left, with positive GUI y pointing down.
|
|
14369
|
+
*/
|
|
13350
14370
|
async addGui(layers, options, parent = this) {
|
|
13351
14371
|
const obj = new GuiObject(layers, options);
|
|
13352
14372
|
obj.scene = this;
|
|
@@ -13383,11 +14403,11 @@ var _changeEvent = { type: "change" };
|
|
|
13383
14403
|
var _startEvent = { type: "start" };
|
|
13384
14404
|
var _endEvent = { type: "end" };
|
|
13385
14405
|
var OrbitControls = class extends EventDispatcher {
|
|
13386
|
-
constructor(
|
|
14406
|
+
constructor(object3, domElement) {
|
|
13387
14407
|
super();
|
|
13388
14408
|
if (domElement === void 0) console.warn('THREE.OrbitControls: The second parameter "domElement" is now mandatory.');
|
|
13389
14409
|
if (domElement === document) console.error('THREE.OrbitControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.');
|
|
13390
|
-
this.object =
|
|
14410
|
+
this.object = object3;
|
|
13391
14411
|
this.domElement = domElement;
|
|
13392
14412
|
this.domElement.style.touchAction = "none";
|
|
13393
14413
|
this.enabled = true;
|
|
@@ -13444,7 +14464,7 @@ var OrbitControls = class extends EventDispatcher {
|
|
|
13444
14464
|
};
|
|
13445
14465
|
this.update = function() {
|
|
13446
14466
|
const offset = new Vector313();
|
|
13447
|
-
const quat = new Quaternion6().setFromUnitVectors(
|
|
14467
|
+
const quat = new Quaternion6().setFromUnitVectors(object3.up, new Vector313(0, 1, 0));
|
|
13448
14468
|
const quatInverse = quat.clone().invert();
|
|
13449
14469
|
const lastPosition = new Vector313();
|
|
13450
14470
|
const lastQuaternion = new Quaternion6();
|
|
@@ -14009,8 +15029,8 @@ var OrbitControls = class extends EventDispatcher {
|
|
|
14009
15029
|
}
|
|
14010
15030
|
};
|
|
14011
15031
|
var MapControls = class extends OrbitControls {
|
|
14012
|
-
constructor(
|
|
14013
|
-
super(
|
|
15032
|
+
constructor(object3, domElement) {
|
|
15033
|
+
super(object3, domElement);
|
|
14014
15034
|
this.screenSpacePanning = false;
|
|
14015
15035
|
this.mouseButtons.LEFT = MOUSE.PAN;
|
|
14016
15036
|
this.mouseButtons.RIGHT = MOUSE.ROTATE;
|
|
@@ -14209,6 +15229,7 @@ var Renderer = class _Renderer {
|
|
|
14209
15229
|
get element() {
|
|
14210
15230
|
return this._element;
|
|
14211
15231
|
}
|
|
15232
|
+
/** Attaches the canvas to an element and sizes it to that element's dimensions. */
|
|
14212
15233
|
appendTo(element) {
|
|
14213
15234
|
if (this._disposed) return;
|
|
14214
15235
|
this._element = element;
|
|
@@ -14236,6 +15257,7 @@ var Renderer = class _Renderer {
|
|
|
14236
15257
|
events.add(changeEvent2);
|
|
14237
15258
|
dispatcher.addEventListener(changeEvent2, this._changeListener);
|
|
14238
15259
|
}
|
|
15260
|
+
/** Updates the camera and canvas size. Width and height are in CSS pixels. */
|
|
14239
15261
|
resize(width, height) {
|
|
14240
15262
|
if (this._disposed) return;
|
|
14241
15263
|
if (isPerspectiveCamera(this.camera)) {
|
|
@@ -14274,12 +15296,14 @@ var Renderer = class _Renderer {
|
|
|
14274
15296
|
if (this._frameCallbacks.get(callback) === subscription) this._frameCallbacks.delete(callback);
|
|
14275
15297
|
};
|
|
14276
15298
|
}
|
|
15299
|
+
/** Starts or resumes the animation loop and registered frame callbacks. */
|
|
14277
15300
|
start() {
|
|
14278
15301
|
if (this._disposed) return;
|
|
14279
15302
|
this.stop();
|
|
14280
15303
|
this._running = true;
|
|
14281
15304
|
if (!this._inAnimationLoop) this.renderer.setAnimationLoop(this._animationLoop);
|
|
14282
15305
|
}
|
|
15306
|
+
/** Pauses rendering and frame callbacks. Call {@link start} to resume. */
|
|
14283
15307
|
stop() {
|
|
14284
15308
|
if (this._disposed) return;
|
|
14285
15309
|
this._running = false;
|
|
@@ -14388,12 +15412,15 @@ var Renderer = class _Renderer {
|
|
|
14388
15412
|
const canvas = trim ? trimCanvas(this.renderer.domElement) : this.renderer.domElement;
|
|
14389
15413
|
return canvas.toDataURL(mime, quality);
|
|
14390
15414
|
}
|
|
15415
|
+
/** Exports visible scene meshes as OBJ text, without texture images. */
|
|
14391
15416
|
toObj() {
|
|
14392
15417
|
return SceneExporter.toObj(this.scene);
|
|
14393
15418
|
}
|
|
15419
|
+
/** Exports visible scene meshes as PLY. Set `options.binary` for an ArrayBuffer. */
|
|
14394
15420
|
toPLY(options) {
|
|
14395
15421
|
return SceneExporter.toPLY(this.scene, options);
|
|
14396
15422
|
}
|
|
15423
|
+
/** Exports a static textured scene in the browser. Set `options.binary` for GLB output. */
|
|
14397
15424
|
toGLTF(options) {
|
|
14398
15425
|
return SceneExporter.toGLTF(this.scene, options);
|
|
14399
15426
|
}
|
|
@@ -14432,16 +15459,244 @@ init_SceneObject();
|
|
|
14432
15459
|
// src/index.ts
|
|
14433
15460
|
init_request();
|
|
14434
15461
|
|
|
15462
|
+
// src/scene/SceneDocument.ts
|
|
15463
|
+
init_DisplayPosition();
|
|
15464
|
+
function fail(path, message) {
|
|
15465
|
+
throw new Error(`${path}: ${message}`);
|
|
15466
|
+
}
|
|
15467
|
+
function object2(value, path) {
|
|
15468
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail(path, "expected a JSON object");
|
|
15469
|
+
for (const key of Object.keys(value)) {
|
|
15470
|
+
if (["__proto__", "prototype", "constructor"].includes(key)) fail(`${path}.${key}`, "unsupported property");
|
|
15471
|
+
}
|
|
15472
|
+
return value;
|
|
15473
|
+
}
|
|
15474
|
+
function fields(value, path, allowed, required = []) {
|
|
15475
|
+
const input = object2(value, path);
|
|
15476
|
+
for (const key of required) if (input[key] === void 0) fail(`${path}.${key}`, "required");
|
|
15477
|
+
for (const [key, entry] of Object.entries(input)) {
|
|
15478
|
+
if (!Object.prototype.hasOwnProperty.call(allowed, key)) fail(`${path}.${key}`, "unsupported property");
|
|
15479
|
+
if (entry !== void 0) allowed[key](entry, `${path}.${key}`);
|
|
15480
|
+
}
|
|
15481
|
+
return input;
|
|
15482
|
+
}
|
|
15483
|
+
var text = (value, path) => {
|
|
15484
|
+
if (typeof value !== "string" || !value.trim()) fail(path, "expected a nonempty string");
|
|
15485
|
+
};
|
|
15486
|
+
var literalText = (value, path) => {
|
|
15487
|
+
if (typeof value !== "string") fail(path, "expected a string");
|
|
15488
|
+
};
|
|
15489
|
+
var boolean = (value, path) => {
|
|
15490
|
+
if (typeof value !== "boolean") fail(path, "expected a boolean");
|
|
15491
|
+
};
|
|
15492
|
+
var number = (value, path) => {
|
|
15493
|
+
if (typeof value !== "number" || !Number.isFinite(value)) fail(path, "expected a finite number");
|
|
15494
|
+
};
|
|
15495
|
+
var positive = (value, path) => {
|
|
15496
|
+
number(value, path);
|
|
15497
|
+
if (value <= 0) fail(path, "expected a positive number");
|
|
15498
|
+
};
|
|
15499
|
+
var nonnegative = (value, path) => {
|
|
15500
|
+
number(value, path);
|
|
15501
|
+
if (value < 0) fail(path, "expected a nonnegative number");
|
|
15502
|
+
};
|
|
15503
|
+
var color = (value, path) => {
|
|
15504
|
+
number(value, path);
|
|
15505
|
+
if (!Number.isInteger(value) || value < 0 || value > 16777215) {
|
|
15506
|
+
fail(path, "expected an RGB integer from 0 to 16777215");
|
|
15507
|
+
}
|
|
15508
|
+
};
|
|
15509
|
+
var asset = (value, path) => {
|
|
15510
|
+
text(value, path);
|
|
15511
|
+
if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(value) || value.split(/[:/]/).some((part) => !part || part === "." || part === "..")) {
|
|
15512
|
+
fail(path, "expected a resource ID such as minecraft:stone");
|
|
15513
|
+
}
|
|
15514
|
+
};
|
|
15515
|
+
var version = (value, path) => {
|
|
15516
|
+
text(value, path);
|
|
15517
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(value)) fail(path, "expected a Minecraft version without slashes");
|
|
15518
|
+
};
|
|
15519
|
+
var tuple = (size, item = number) => (value, path) => {
|
|
15520
|
+
if (!Array.isArray(value) || value.length !== size) fail(path, `expected ${size} numbers`);
|
|
15521
|
+
for (let index = 0; index < size; index++) item(value[index], `${path}[${index}]`);
|
|
15522
|
+
};
|
|
15523
|
+
var list = (item) => (value, path) => {
|
|
15524
|
+
if (!Array.isArray(value)) fail(path, "expected an array");
|
|
15525
|
+
for (let index = 0; index < value.length; index++) item(value[index], `${path}[${index}]`);
|
|
15526
|
+
};
|
|
15527
|
+
var dictionary = (item, numericKeys = false) => (value, path) => {
|
|
15528
|
+
for (const [key, entry] of Object.entries(object2(value, path))) {
|
|
15529
|
+
if (numericKeys && !/^\d+$/.test(key)) fail(`${path}.${key}`, "expected a nonnegative tint index");
|
|
15530
|
+
item(entry, `${path}.${key}`);
|
|
15531
|
+
}
|
|
15532
|
+
};
|
|
15533
|
+
var enumeration = (values) => (value, path) => {
|
|
15534
|
+
if (!values.includes(value)) fail(path, `expected one of: ${values.join(", ")}`);
|
|
15535
|
+
};
|
|
15536
|
+
var textStyle = { color, bold: boolean, italic: boolean };
|
|
15537
|
+
function guiLayer(value, path) {
|
|
15538
|
+
const layer = object2(value, path);
|
|
15539
|
+
if (["texture", "item", "text"].filter((key) => layer[key] !== void 0).length !== 1) {
|
|
15540
|
+
fail(path, "expected exactly one of texture, item, or text");
|
|
15541
|
+
}
|
|
15542
|
+
const layout = { name: text, position: tuple(2), size: tuple(2, positive) };
|
|
15543
|
+
if (layer.texture !== void 0) {
|
|
15544
|
+
fields(value, path, { ...layout, texture: asset, crop: (value2, path2) => {
|
|
15545
|
+
tuple(4)(value2, path2);
|
|
15546
|
+
const [x, y, width, height] = value2;
|
|
15547
|
+
nonnegative(x, `${path2}[0]`);
|
|
15548
|
+
nonnegative(y, `${path2}[1]`);
|
|
15549
|
+
positive(width, `${path2}[2]`);
|
|
15550
|
+
positive(height, `${path2}[3]`);
|
|
15551
|
+
} });
|
|
15552
|
+
} else if (layer.item !== void 0) {
|
|
15553
|
+
fields(value, path, { ...layout, item: asset, tints: dictionary(color, true) });
|
|
15554
|
+
} else {
|
|
15555
|
+
fields(value, path, {
|
|
15556
|
+
...layout,
|
|
15557
|
+
...textStyle,
|
|
15558
|
+
font: asset,
|
|
15559
|
+
shadow: boolean,
|
|
15560
|
+
maxWidth: positive,
|
|
15561
|
+
lineHeight: positive,
|
|
15562
|
+
text: (value2, path2) => {
|
|
15563
|
+
if (typeof value2 === "string") return;
|
|
15564
|
+
list((run, path3) => {
|
|
15565
|
+
fields(run, path3, { text: literalText, ...textStyle }, ["text"]);
|
|
15566
|
+
})(value2, path2);
|
|
15567
|
+
}
|
|
15568
|
+
});
|
|
15569
|
+
}
|
|
15570
|
+
}
|
|
15571
|
+
function validateObject(value, path) {
|
|
15572
|
+
const input = object2(value, path);
|
|
15573
|
+
const common = { id: text, name: text, type: text, position: tuple(3), rotation: tuple(3), scale: tuple(3), visible: boolean };
|
|
15574
|
+
const required = ["id", "type"];
|
|
15575
|
+
const renderOptions = { wireframe: boolean };
|
|
15576
|
+
switch (input.type) {
|
|
15577
|
+
case "skin":
|
|
15578
|
+
fields(value, path, {
|
|
15579
|
+
...common,
|
|
15580
|
+
skin: text,
|
|
15581
|
+
cape: (value2, path2) => {
|
|
15582
|
+
fields(value2, path2, { texture: text, layout: enumeration(["minecraft", "optifine", "labymod"]) }, ["texture"]);
|
|
15583
|
+
},
|
|
15584
|
+
hiddenParts: list(enumeration(SKIN_PARTS)),
|
|
15585
|
+
pose: (value2, path2) => {
|
|
15586
|
+
fields(value2, path2, {
|
|
15587
|
+
head: tuple(3),
|
|
15588
|
+
body: tuple(3),
|
|
15589
|
+
rightArm: tuple(3),
|
|
15590
|
+
leftArm: tuple(3),
|
|
15591
|
+
rightLeg: tuple(3),
|
|
15592
|
+
leftLeg: tuple(3),
|
|
15593
|
+
cape: tuple(3)
|
|
15594
|
+
});
|
|
15595
|
+
},
|
|
15596
|
+
options: (value2, path2) => {
|
|
15597
|
+
fields(value2, path2, { slim: boolean, legacy: boolean });
|
|
15598
|
+
}
|
|
15599
|
+
}, required);
|
|
15600
|
+
break;
|
|
15601
|
+
case "block":
|
|
15602
|
+
fields(value, path, {
|
|
15603
|
+
...common,
|
|
15604
|
+
asset,
|
|
15605
|
+
state: dictionary(text),
|
|
15606
|
+
options: (value2, path2) => {
|
|
15607
|
+
fields(value2, path2, { ...renderOptions, tints: dictionary(color, true) });
|
|
15608
|
+
}
|
|
15609
|
+
}, [...required, "asset"]);
|
|
15610
|
+
break;
|
|
15611
|
+
case "item":
|
|
15612
|
+
case "model":
|
|
15613
|
+
fields(value, path, {
|
|
15614
|
+
...common,
|
|
15615
|
+
asset,
|
|
15616
|
+
options: (value2, path2) => {
|
|
15617
|
+
fields(value2, path2, { ...renderOptions, displayPosition: enumeration(DISPLAY_POSITIONS), tints: dictionary(color, true) });
|
|
15618
|
+
}
|
|
15619
|
+
}, [...required, "asset"]);
|
|
15620
|
+
break;
|
|
15621
|
+
case "entity":
|
|
15622
|
+
fields(value, path, {
|
|
15623
|
+
...common,
|
|
15624
|
+
asset,
|
|
15625
|
+
texture: asset,
|
|
15626
|
+
layers: list(text),
|
|
15627
|
+
when: list(text),
|
|
15628
|
+
textures: dictionary(asset),
|
|
15629
|
+
options: (value2, path2) => {
|
|
15630
|
+
fields(value2, path2, {
|
|
15631
|
+
...renderOptions,
|
|
15632
|
+
flip: boolean,
|
|
15633
|
+
tints: dictionary((value3, path3) => {
|
|
15634
|
+
typeof value3 === "string" ? text(value3, path3) : color(value3, path3);
|
|
15635
|
+
})
|
|
15636
|
+
});
|
|
15637
|
+
},
|
|
15638
|
+
animation: (value2, path2) => {
|
|
15639
|
+
fields(value2, path2, { name: text, loop: boolean, speed: nonnegative, time: nonnegative, paused: boolean }, ["name"]);
|
|
15640
|
+
}
|
|
15641
|
+
}, [...required, "asset"]);
|
|
15642
|
+
if (Array.isArray(input.layers) && !input.layers.length) fail(`${path}.layers`, "expected at least one layer");
|
|
15643
|
+
break;
|
|
15644
|
+
case "gui":
|
|
15645
|
+
fields(value, path, { ...common, layers: list(guiLayer) }, [...required, "layers"]);
|
|
15646
|
+
break;
|
|
15647
|
+
default:
|
|
15648
|
+
fail(`${path}.type`, "expected skin, block, item, entity, model, or gui");
|
|
15649
|
+
}
|
|
15650
|
+
}
|
|
15651
|
+
function parseSceneDocument(value) {
|
|
15652
|
+
if (typeof value === "string") {
|
|
15653
|
+
try {
|
|
15654
|
+
value = JSON.parse(value);
|
|
15655
|
+
} catch {
|
|
15656
|
+
fail("scene", "invalid JSON");
|
|
15657
|
+
}
|
|
15658
|
+
}
|
|
15659
|
+
const input = fields(value, "scene", {
|
|
15660
|
+
format: enumeration(["minerender-scene"]),
|
|
15661
|
+
version: (value2, path) => {
|
|
15662
|
+
if (value2 !== 1) fail(path, "only scene document version 1 is supported");
|
|
15663
|
+
},
|
|
15664
|
+
minecraftVersion: version,
|
|
15665
|
+
camera: (value2, path) => {
|
|
15666
|
+
fields(value2, path, { position: tuple(3), target: tuple(3) }, ["position", "target"]);
|
|
15667
|
+
},
|
|
15668
|
+
objects: list(validateObject)
|
|
15669
|
+
}, ["format", "version", "objects"]);
|
|
15670
|
+
const ids = /* @__PURE__ */ new Set();
|
|
15671
|
+
for (const [index, entry] of input.objects.entries()) {
|
|
15672
|
+
if (ids.has(entry.id)) fail(`scene.objects[${index}].id`, `duplicate object ID "${entry.id}"`);
|
|
15673
|
+
ids.add(entry.id);
|
|
15674
|
+
}
|
|
15675
|
+
return JSON.parse(JSON.stringify(input));
|
|
15676
|
+
}
|
|
15677
|
+
|
|
15678
|
+
// src/scene/SceneDocumentLoader.ts
|
|
15679
|
+
init_AssetKey();
|
|
15680
|
+
init_AssetLoader();
|
|
15681
|
+
init_BlockStates();
|
|
15682
|
+
init_Entities();
|
|
15683
|
+
init_Models();
|
|
15684
|
+
init_DisplayPosition();
|
|
15685
|
+
import { Group as Group2 } from "three";
|
|
15686
|
+
init_SceneObject();
|
|
15687
|
+
|
|
14435
15688
|
// src/skin/Skins.ts
|
|
14436
15689
|
init_Requests();
|
|
14437
15690
|
var Skins = class {
|
|
14438
15691
|
//<editor-fold desc="Skins">
|
|
15692
|
+
/** Returns a skin URL for {@link MineRenderScene.addSkin}, or `undefined` if name lookup fails. */
|
|
14439
15693
|
static async fromUuidOrUsername(uuidOrUsername) {
|
|
14440
15694
|
if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
|
|
14441
15695
|
return this.fromUuid(uuidOrUsername);
|
|
14442
15696
|
}
|
|
14443
15697
|
return this.fromUsername(uuidOrUsername);
|
|
14444
15698
|
}
|
|
15699
|
+
/** Builds a proxy URL for a UUID without downloading or checking the skin. */
|
|
14445
15700
|
static async fromUuid(uuid) {
|
|
14446
15701
|
return this.getMcProxySkinUrl(uuid);
|
|
14447
15702
|
}
|
|
@@ -14453,6 +15708,7 @@ var Skins = class {
|
|
|
14453
15708
|
return void 0;
|
|
14454
15709
|
});
|
|
14455
15710
|
}
|
|
15711
|
+
/** Looks up a saved MineSkin skin and returns its texture URL, or `undefined` on lookup failure. */
|
|
14456
15712
|
static async fromMineSkin(mineskinId) {
|
|
14457
15713
|
return this.getMineSkin(mineskinId).then((mineskin) => {
|
|
14458
15714
|
if (mineskin) {
|
|
@@ -14463,12 +15719,14 @@ var Skins = class {
|
|
|
14463
15719
|
}
|
|
14464
15720
|
//</editor-fold>
|
|
14465
15721
|
//<editor-fold desc="Capes">
|
|
15722
|
+
/** Resolves a player's Minecraft cape URL for {@link SkinObject.setCapeTexture}. */
|
|
14466
15723
|
static async capeFromUuidOrUsername(uuidOrUsername) {
|
|
14467
15724
|
if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
|
|
14468
15725
|
return this.capeFromUuid(uuidOrUsername);
|
|
14469
15726
|
}
|
|
14470
15727
|
return this.capeFromUsername(uuidOrUsername);
|
|
14471
15728
|
}
|
|
15729
|
+
/** Builds a proxy URL without checking whether the player has a cape. */
|
|
14472
15730
|
static async capeFromUuid(uuid) {
|
|
14473
15731
|
return this.getMcProxyCapeUrl(uuid);
|
|
14474
15732
|
}
|
|
@@ -14597,6 +15855,195 @@ var Skins = class {
|
|
|
14597
15855
|
//</editor-fold>
|
|
14598
15856
|
};
|
|
14599
15857
|
|
|
15858
|
+
// src/scene/SceneDocumentLoader.ts
|
|
15859
|
+
var DocumentObjectScene = class extends MineRenderScene {
|
|
15860
|
+
async addSceneObject(_asset, supplier, _options, parent = this) {
|
|
15861
|
+
const object3 = await supplier();
|
|
15862
|
+
return this.initialize(object3, parent);
|
|
15863
|
+
}
|
|
15864
|
+
async initialize(object3, parent = this) {
|
|
15865
|
+
object3.scene = this;
|
|
15866
|
+
try {
|
|
15867
|
+
await object3.init();
|
|
15868
|
+
parent.add(object3);
|
|
15869
|
+
return object3;
|
|
15870
|
+
} catch (error) {
|
|
15871
|
+
object3.dispose();
|
|
15872
|
+
object3.removeFromParent();
|
|
15873
|
+
throw error;
|
|
15874
|
+
}
|
|
15875
|
+
}
|
|
15876
|
+
dispose() {
|
|
15877
|
+
for (const child of [...this.children]) {
|
|
15878
|
+
if (child.parent !== this) continue;
|
|
15879
|
+
if (isSceneObject(child)) child.dispose();
|
|
15880
|
+
child.removeFromParent();
|
|
15881
|
+
}
|
|
15882
|
+
}
|
|
15883
|
+
};
|
|
15884
|
+
async function skinTexture(value, cape = false) {
|
|
15885
|
+
if (!/^[a-zA-Z0-9_-]{1,36}$/.test(value)) return value;
|
|
15886
|
+
const texture = await (cape ? Skins.capeFromUuidOrUsername(value) : Skins.fromUuidOrUsername(value));
|
|
15887
|
+
if (!texture) throw new Error(`Could not find ${cape ? "cape" : "skin"} for "${value}"`);
|
|
15888
|
+
return texture;
|
|
15889
|
+
}
|
|
15890
|
+
var SceneDocumentLoader = class {
|
|
15891
|
+
static parse(value) {
|
|
15892
|
+
return parseSceneDocument(value);
|
|
15893
|
+
}
|
|
15894
|
+
/**
|
|
15895
|
+
* Loads objects concurrently and attaches them in document order after every load succeeds.
|
|
15896
|
+
* A failure waits for all loads, disposes staged objects, and reports the first error in document order.
|
|
15897
|
+
*/
|
|
15898
|
+
static async load(scene, value, parent = scene) {
|
|
15899
|
+
const definition = this.parse(value);
|
|
15900
|
+
if (definition.minecraftVersion !== void 0 && definition.minecraftVersion !== AssetLoader.version) {
|
|
15901
|
+
throw new Error(`Scene requires Minecraft ${definition.minecraftVersion}; call AssetLoader.setVersion("${definition.minecraftVersion}") before loading it`);
|
|
15902
|
+
}
|
|
15903
|
+
const root = new Group2();
|
|
15904
|
+
root.name = "MineRender scene document";
|
|
15905
|
+
const objects = [];
|
|
15906
|
+
let disposed = false;
|
|
15907
|
+
const loaded = {
|
|
15908
|
+
root,
|
|
15909
|
+
objects,
|
|
15910
|
+
definition,
|
|
15911
|
+
advanceAnimations: (delta) => {
|
|
15912
|
+
if (!disposed) for (const object3 of objects) object3.advanceAnimation(delta);
|
|
15913
|
+
},
|
|
15914
|
+
dispose: () => {
|
|
15915
|
+
if (disposed) return;
|
|
15916
|
+
disposed = true;
|
|
15917
|
+
root.removeFromParent();
|
|
15918
|
+
for (const object3 of objects) object3.dispose();
|
|
15919
|
+
root.clear();
|
|
15920
|
+
scene.dirty = true;
|
|
15921
|
+
}
|
|
15922
|
+
};
|
|
15923
|
+
try {
|
|
15924
|
+
const results = await Promise.allSettled(definition.objects.map((object3) => this.loadValidatedObject(scene, object3)));
|
|
15925
|
+
for (const result of results) if (result.status === "fulfilled") objects.push(result.value);
|
|
15926
|
+
const failure = results.find((result) => result.status === "rejected");
|
|
15927
|
+
if (failure) throw failure.reason;
|
|
15928
|
+
for (const object3 of objects) root.add(object3.root);
|
|
15929
|
+
parent.add(root);
|
|
15930
|
+
scene.dirty = true;
|
|
15931
|
+
return loaded;
|
|
15932
|
+
} catch (error) {
|
|
15933
|
+
loaded.dispose();
|
|
15934
|
+
throw error;
|
|
15935
|
+
}
|
|
15936
|
+
}
|
|
15937
|
+
/** Loads an independently editable object. Its root owns the document's position, rotation, and scale. */
|
|
15938
|
+
static async loadObject(scene, value, parent = scene) {
|
|
15939
|
+
const definition = this.parse({ format: "minerender-scene", version: 1, objects: [value] }).objects[0];
|
|
15940
|
+
return this.loadValidatedObject(scene, definition, parent);
|
|
15941
|
+
}
|
|
15942
|
+
static async loadValidatedObject(scene, definition, parent) {
|
|
15943
|
+
const root = new Group2();
|
|
15944
|
+
root.name = definition.name ?? definition.id;
|
|
15945
|
+
root.userData.sceneObjectId = definition.id;
|
|
15946
|
+
root.position.fromArray(definition.position ?? [0, 0, 0]);
|
|
15947
|
+
root.rotation.set(...definition.rotation ?? [0, 0, 0]);
|
|
15948
|
+
root.scale.fromArray(definition.scale ?? [1, 1, 1]);
|
|
15949
|
+
root.visible = definition.visible ?? true;
|
|
15950
|
+
const content = new DocumentObjectScene();
|
|
15951
|
+
root.add(content);
|
|
15952
|
+
let object3;
|
|
15953
|
+
try {
|
|
15954
|
+
switch (definition.type) {
|
|
15955
|
+
case "skin": {
|
|
15956
|
+
const skin = new SkinObject({ ...definition.options, instanceMeshes: false });
|
|
15957
|
+
object3 = skin;
|
|
15958
|
+
const texture = definition.skin ? await skinTexture(definition.skin) : `${AssetLoader.ROOT}/assets/minecraft/textures/entity/player/${definition.options?.slim ? "slim/alex" : "wide/steve"}.png`;
|
|
15959
|
+
await skin.setSkinTexture(texture);
|
|
15960
|
+
await content.initialize(skin);
|
|
15961
|
+
if (definition.cape) await skin.setCapeTexture(await skinTexture(definition.cape.texture, true), definition.cape.layout);
|
|
15962
|
+
for (const [part, rotation] of Object.entries(definition.pose ?? {})) {
|
|
15963
|
+
skin.getGroupByName(part)?.rotation.set(...rotation, "XYZ");
|
|
15964
|
+
}
|
|
15965
|
+
for (const part of definition.hiddenParts ?? []) {
|
|
15966
|
+
const mesh = skin.getMeshByName(part);
|
|
15967
|
+
if (mesh) mesh.visible = false;
|
|
15968
|
+
}
|
|
15969
|
+
break;
|
|
15970
|
+
}
|
|
15971
|
+
case "block": {
|
|
15972
|
+
const state = await BlockStates.get(AssetKey.parse("blockstates", definition.asset));
|
|
15973
|
+
if (!state) throw new Error(`Could not load block ${definition.asset}`);
|
|
15974
|
+
object3 = await content.addBlock(state, { ...definition.options, initialState: definition.state, instanceMeshes: false });
|
|
15975
|
+
break;
|
|
15976
|
+
}
|
|
15977
|
+
case "item":
|
|
15978
|
+
case "model": {
|
|
15979
|
+
let key = AssetKey.parse("models", definition.asset);
|
|
15980
|
+
if (definition.type === "item" && key.type !== "item") {
|
|
15981
|
+
key = new AssetKey(key.namespace, key.getFullPath(), "models", "item");
|
|
15982
|
+
}
|
|
15983
|
+
const model = await Models.getMerged(key);
|
|
15984
|
+
if (!model) throw new Error(`Could not load ${definition.type} ${definition.asset}`);
|
|
15985
|
+
object3 = await content.addModel(model, {
|
|
15986
|
+
...definition.type === "item" ? { displayPosition: "gui" /* GUI */ } : {},
|
|
15987
|
+
...definition.options,
|
|
15988
|
+
instanceMeshes: false
|
|
15989
|
+
});
|
|
15990
|
+
break;
|
|
15991
|
+
}
|
|
15992
|
+
case "entity": {
|
|
15993
|
+
const key = AssetKey.parse("entity-models", definition.asset);
|
|
15994
|
+
const model = await Entities.getEntity(key, definition.texture ? AssetKey.parse("textures", definition.texture) : void 0, {
|
|
15995
|
+
layers: definition.layers,
|
|
15996
|
+
when: definition.when,
|
|
15997
|
+
textures: definition.textures && Object.fromEntries(Object.entries(definition.textures).map(([name, texture]) => [name, AssetKey.parse("textures", texture)]))
|
|
15998
|
+
});
|
|
15999
|
+
if (!model) throw new Error(`Could not load entity ${definition.asset}`);
|
|
16000
|
+
const entity = await content.addEntity(model, { ...definition.options, instanceMeshes: false });
|
|
16001
|
+
object3 = entity;
|
|
16002
|
+
if (definition.animation) {
|
|
16003
|
+
const animations = await Entities.getAnimations(key);
|
|
16004
|
+
const clip = animations?.[definition.animation.name];
|
|
16005
|
+
if (!clip) throw new Error(`Entity ${definition.asset} has no animation "${definition.animation.name}"`);
|
|
16006
|
+
entity.playAnimation(clip, definition.animation);
|
|
16007
|
+
}
|
|
16008
|
+
break;
|
|
16009
|
+
}
|
|
16010
|
+
case "gui":
|
|
16011
|
+
object3 = await content.initialize(new GuiObject(definition.layers));
|
|
16012
|
+
break;
|
|
16013
|
+
}
|
|
16014
|
+
let disposed = false;
|
|
16015
|
+
const loaded = {
|
|
16016
|
+
root,
|
|
16017
|
+
object: object3,
|
|
16018
|
+
definition,
|
|
16019
|
+
advanceAnimation: (delta) => {
|
|
16020
|
+
if (!disposed && definition.type === "entity" && !definition.animation?.paused) object3.advanceAnimation(delta);
|
|
16021
|
+
},
|
|
16022
|
+
dispose: () => {
|
|
16023
|
+
if (disposed) return;
|
|
16024
|
+
disposed = true;
|
|
16025
|
+
root.removeFromParent();
|
|
16026
|
+
object3.dispose();
|
|
16027
|
+
object3.removeFromParent();
|
|
16028
|
+
content.dispose();
|
|
16029
|
+
root.clear();
|
|
16030
|
+
scene.dirty = true;
|
|
16031
|
+
}
|
|
16032
|
+
};
|
|
16033
|
+
parent?.add(root);
|
|
16034
|
+
scene.dirty = true;
|
|
16035
|
+
return loaded;
|
|
16036
|
+
} catch (error) {
|
|
16037
|
+
object3?.dispose();
|
|
16038
|
+
object3?.removeFromParent();
|
|
16039
|
+
content.dispose();
|
|
16040
|
+
root.removeFromParent();
|
|
16041
|
+
root.clear();
|
|
16042
|
+
throw new Error(`Scene object "${definition.name ?? definition.id}": ${error instanceof Error ? error.message : String(error)}`);
|
|
16043
|
+
}
|
|
16044
|
+
}
|
|
16045
|
+
};
|
|
16046
|
+
|
|
14600
16047
|
// src/texture/index.ts
|
|
14601
16048
|
init_Colormaps();
|
|
14602
16049
|
init_TextureAtlas();
|
|
@@ -14623,16 +16070,19 @@ var ChunkData = class _ChunkData {
|
|
|
14623
16070
|
this.freeIds = [];
|
|
14624
16071
|
this.nbt = /* @__PURE__ */ new Map();
|
|
14625
16072
|
}
|
|
16073
|
+
/** Returns a detached copy of a cell, or `undefined` for air. Indices range from 0 to 4095. */
|
|
14626
16074
|
get(index) {
|
|
14627
16075
|
_ChunkData.validateIndex(index);
|
|
14628
16076
|
const entry = this.palette[this.indices[index]];
|
|
14629
16077
|
return entry ? _ChunkData.copyBlock(entry.state, this.nbt.get(index)) : void 0;
|
|
14630
16078
|
}
|
|
16079
|
+
/** Captures a cell's data and returns a copy-producing reader that survives later cell changes. */
|
|
14631
16080
|
snapshot(index) {
|
|
14632
16081
|
_ChunkData.validateIndex(index);
|
|
14633
16082
|
const entry = this.palette[this.indices[index]];
|
|
14634
16083
|
return entry ? _ChunkData.copyBlock.bind(void 0, entry.state, this.nbt.get(index)) : void 0;
|
|
14635
16084
|
}
|
|
16085
|
+
/** Copies block data into a cell. An omitted block or an air block clears the cell. */
|
|
14636
16086
|
set(index, block) {
|
|
14637
16087
|
_ChunkData.validateIndex(index);
|
|
14638
16088
|
let state;
|
|
@@ -14697,15 +16147,23 @@ var ChunkData = class _ChunkData {
|
|
|
14697
16147
|
|
|
14698
16148
|
// src/world/AnvilParser.ts
|
|
14699
16149
|
var AnvilParser = class {
|
|
16150
|
+
/** Lists occupied chunk positions within the region, using local coordinates from 0 to 31. */
|
|
14700
16151
|
static getChunkList(data) {
|
|
14701
16152
|
return this.locations(this.bytes(data)).map(({ x, z }) => ({ x, z }));
|
|
14702
16153
|
}
|
|
16154
|
+
/** Decodes all stored chunks. Gzip, zlib, and uncompressed payloads are supported. */
|
|
14703
16155
|
static async parse(data) {
|
|
14704
16156
|
const bytes = this.bytes(data);
|
|
14705
16157
|
const chunks = [];
|
|
14706
16158
|
for (const location of this.locations(bytes)) chunks.push(await this.readChunk(bytes, location));
|
|
14707
16159
|
return { chunks };
|
|
14708
16160
|
}
|
|
16161
|
+
/**
|
|
16162
|
+
* Decodes one chunk for {@link MineRenderWorld.placeChunk}.
|
|
16163
|
+
* @param localX - Chunk x within the region, from 0 to 31.
|
|
16164
|
+
* @param localZ - Chunk z within the region, from 0 to 31.
|
|
16165
|
+
* @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
|
|
16166
|
+
*/
|
|
14709
16167
|
static async parseChunk(data, localX, localZ) {
|
|
14710
16168
|
if (![localX, localZ].every((value) => Number.isInteger(value) && value >= 0 && value < 32)) {
|
|
14711
16169
|
throw new RangeError("Region-local chunk coordinates must be integers from 0 to 31");
|
|
@@ -14910,6 +16368,7 @@ var SectionModels = class {
|
|
|
14910
16368
|
this.templates = /* @__PURE__ */ new Set();
|
|
14911
16369
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
|
|
14912
16370
|
}
|
|
16371
|
+
/** Returns a cube template, or `undefined` when the block requires an individual render object. */
|
|
14913
16372
|
async get(blockState, properties = {}) {
|
|
14914
16373
|
if (!blockState.variants || blockState.multipart) return void 0;
|
|
14915
16374
|
let states = this.states.get(blockState);
|
|
@@ -14938,16 +16397,16 @@ var SectionModels = class {
|
|
|
14938
16397
|
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
14939
16398
|
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
14940
16399
|
const template = await cached(models, key, async () => {
|
|
14941
|
-
const
|
|
16400
|
+
const object3 = new SectionModel(model, {
|
|
14942
16401
|
instanceMeshes: false,
|
|
14943
16402
|
mergeMeshes: false,
|
|
14944
16403
|
tints,
|
|
14945
16404
|
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
14946
16405
|
});
|
|
14947
|
-
await
|
|
14948
|
-
const geometry =
|
|
16406
|
+
await object3.init();
|
|
16407
|
+
const geometry = object3.children[0].geometry;
|
|
14949
16408
|
geometry.applyMatrix4(new Matrix412().makeRotationFromEuler(rotation));
|
|
14950
|
-
|
|
16409
|
+
object3.clear();
|
|
14951
16410
|
const cullFaces = CUBE_FACES.map((face) => {
|
|
14952
16411
|
const local = CUBE_FACES.findIndex((name) => name === atlas.model.elements[0].faces[face]?.cullface);
|
|
14953
16412
|
if (local < 0) return 0;
|
|
@@ -14963,6 +16422,7 @@ var SectionModels = class {
|
|
|
14963
16422
|
}
|
|
14964
16423
|
return { variants, templates };
|
|
14965
16424
|
}
|
|
16425
|
+
/** Disposes prepared geometry and clears this world's template caches. */
|
|
14966
16426
|
clear() {
|
|
14967
16427
|
for (const template of this.templates) template.geometry.dispose();
|
|
14968
16428
|
this.templates.clear();
|
|
@@ -15013,6 +16473,13 @@ var MineRenderWorld = class {
|
|
|
15013
16473
|
this.validatePosBounds(posOrX);
|
|
15014
16474
|
await this.getChunkAt(posOrX)?.setBlockVisibleAt(posOrX, yOrVisible);
|
|
15015
16475
|
}
|
|
16476
|
+
/**
|
|
16477
|
+
* Places a structure's blocks at their stored positions, then refreshes neighboring faces.
|
|
16478
|
+
* Entity NBT is retained by the structure but is not rendered.
|
|
16479
|
+
*
|
|
16480
|
+
* @param useBatches - Uses bounded batches by default. Set to `false` for sequential placement.
|
|
16481
|
+
* @param executor - Optional queue for batched placement. The caller remains responsible for stopping it.
|
|
16482
|
+
*/
|
|
15016
16483
|
async placeMultiBlock(multiblock, useBatches = true, executor) {
|
|
15017
16484
|
const changes = /* @__PURE__ */ new Map();
|
|
15018
16485
|
try {
|
|
@@ -15064,13 +16531,7 @@ var MineRenderWorld = class {
|
|
|
15064
16531
|
async placeChunk(chunk, executor) {
|
|
15065
16532
|
const changes = /* @__PURE__ */ new Map();
|
|
15066
16533
|
try {
|
|
15067
|
-
|
|
15068
|
-
for (const [key, section] of previous) {
|
|
15069
|
-
this._chunks.delete(key);
|
|
15070
|
-
await section.clear(async (positions) => {
|
|
15071
|
-
for (const pos of positions) changes.set(pos.toArray().join(","), pos);
|
|
15072
|
-
});
|
|
15073
|
-
}
|
|
16534
|
+
await this.clearChunkColumn(chunk.x, chunk.z, changes);
|
|
15074
16535
|
for (const section of chunk.sections) {
|
|
15075
16536
|
const blocks = [];
|
|
15076
16537
|
for (let index = 0; index < 4096; index++) {
|
|
@@ -15090,6 +16551,32 @@ var MineRenderWorld = class {
|
|
|
15090
16551
|
await this.updateCulling([...changes.values()]);
|
|
15091
16552
|
}
|
|
15092
16553
|
}
|
|
16554
|
+
/**
|
|
16555
|
+
* Removes all sections at integer chunk-column coordinates, then refreshes neighboring faces and fluids.
|
|
16556
|
+
* Coordinates match AnvilChunk.x/z, not block positions. Missing columns are ignored.
|
|
16557
|
+
*/
|
|
16558
|
+
async unloadChunkColumn(x, z) {
|
|
16559
|
+
if (!Number.isInteger(x) || !Number.isInteger(z)) {
|
|
16560
|
+
throw new RangeError("Chunk column coordinates must be integers");
|
|
16561
|
+
}
|
|
16562
|
+
await this.culling;
|
|
16563
|
+
const changes = /* @__PURE__ */ new Map();
|
|
16564
|
+
try {
|
|
16565
|
+
await this.clearChunkColumn(x, z, changes);
|
|
16566
|
+
} finally {
|
|
16567
|
+
await this.updateCulling([...changes.values()]);
|
|
16568
|
+
}
|
|
16569
|
+
}
|
|
16570
|
+
async clearChunkColumn(x, z, changes) {
|
|
16571
|
+
const previous = [...this._chunks.entries()].filter(([, section]) => section.x === x && section.z === z);
|
|
16572
|
+
for (const [key, section] of previous) {
|
|
16573
|
+
this._chunks.delete(key);
|
|
16574
|
+
await section.clear(async (positions) => {
|
|
16575
|
+
for (const pos of positions) changes.set(pos.toArray().join(","), pos);
|
|
16576
|
+
});
|
|
16577
|
+
}
|
|
16578
|
+
}
|
|
16579
|
+
/** Removes all chunks and releases their block placements and section meshes. */
|
|
15093
16580
|
async clear() {
|
|
15094
16581
|
await this.culling;
|
|
15095
16582
|
const chunks = [...this._chunks.values()];
|
|
@@ -15135,10 +16622,10 @@ var MineRenderWorld = class {
|
|
|
15135
16622
|
await block.object.updateFluid((x, y, z) => {
|
|
15136
16623
|
const neighbor = pos.clone().add(new Vector316(x, y, z));
|
|
15137
16624
|
const section = this.getChunkAt(neighbor);
|
|
15138
|
-
const
|
|
16625
|
+
const object3 = section?.isBlockVisibleAt(neighbor) ? section.getBlockAt(neighbor)?.object : void 0;
|
|
15139
16626
|
return {
|
|
15140
|
-
fluid:
|
|
15141
|
-
level:
|
|
16627
|
+
fluid: object3?.fluidKind,
|
|
16628
|
+
level: object3?.fluidLevel,
|
|
15142
16629
|
solid: section?.isOccludingAt(neighbor) ?? false
|
|
15143
16630
|
};
|
|
15144
16631
|
});
|
|
@@ -15175,9 +16662,11 @@ var MineRenderWorld = class {
|
|
|
15175
16662
|
}
|
|
15176
16663
|
return chunk;
|
|
15177
16664
|
}
|
|
16665
|
+
/** Returns the loaded chunk containing a world block position, without creating a chunk. */
|
|
15178
16666
|
getChunkAt(pos) {
|
|
15179
16667
|
return this._chunks.get(this.worldPosToChunkKey(pos));
|
|
15180
16668
|
}
|
|
16669
|
+
/** Converts block coordinates to scene units by multiplying each component by 16. */
|
|
15181
16670
|
static worldToScenePosition(pos) {
|
|
15182
16671
|
return new Vector316(
|
|
15183
16672
|
pos.x * 16,
|
|
@@ -15185,6 +16674,7 @@ var MineRenderWorld = class {
|
|
|
15185
16674
|
pos.z * 16
|
|
15186
16675
|
);
|
|
15187
16676
|
}
|
|
16677
|
+
/** Converts scene units to block coordinates without rounding to an integer block. */
|
|
15188
16678
|
static sceneToWorldPosition(pos) {
|
|
15189
16679
|
return new Vector316(
|
|
15190
16680
|
pos.x / 16,
|
|
@@ -15211,12 +16701,13 @@ init_Model();
|
|
|
15211
16701
|
// src/world/SectionMesh.ts
|
|
15212
16702
|
init_CanvasCompat();
|
|
15213
16703
|
init_Materials();
|
|
15214
|
-
import { BufferGeometry as BufferGeometry8, Float32BufferAttribute as Float32BufferAttribute8, Group as
|
|
15215
|
-
var SectionMesh = class _SectionMesh extends
|
|
16704
|
+
import { BufferGeometry as BufferGeometry8, Float32BufferAttribute as Float32BufferAttribute8, Group as Group3, Mesh as Mesh9 } from "three";
|
|
16705
|
+
var SectionMesh = class _SectionMesh extends Group3 {
|
|
15216
16706
|
constructor() {
|
|
15217
16707
|
super(...arguments);
|
|
15218
16708
|
this.ownedMeshes = [];
|
|
15219
16709
|
}
|
|
16710
|
+
/** Builds section-local meshes from visible cube faces. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
15220
16711
|
static build(entries, maxAtlasSize = 2048) {
|
|
15221
16712
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
15222
16713
|
const visible = entries.filter((entry) => entry.template.cullFaces.some((direction) => !(entry.cullMask & direction)));
|
|
@@ -15258,14 +16749,17 @@ var SectionMesh = class _SectionMesh extends Group2 {
|
|
|
15258
16749
|
for (const { atlas, x, y } of page.placements) {
|
|
15259
16750
|
context.drawImage(atlas.image.canvas, x, y);
|
|
15260
16751
|
}
|
|
15261
|
-
const positions = [], normals = [], uvs = [], colors = [], indices = [];
|
|
16752
|
+
const positions = [], normals = [], uvs = [], uvBounds = [], colors = [], indices = [];
|
|
15262
16753
|
for (const { index, template, cullMask } of page.entries) {
|
|
15263
16754
|
const position = template.geometry.getAttribute("position");
|
|
15264
16755
|
const normal = template.geometry.getAttribute("normal");
|
|
15265
16756
|
const uv = template.geometry.getAttribute("uv");
|
|
15266
|
-
const
|
|
16757
|
+
const bounds = template.geometry.getAttribute("uvBounds");
|
|
16758
|
+
const color2 = template.geometry.getAttribute("color");
|
|
15267
16759
|
const sourceIndices = template.geometry.getIndex();
|
|
15268
16760
|
const placement = locations.get(template.atlas).placement;
|
|
16761
|
+
const mapU = (u) => (placement.x + u * template.atlas.image.width) / page.width;
|
|
16762
|
+
const mapV = (v) => 1 - (placement.y + (1 - v) * template.atlas.image.height) / page.height;
|
|
15269
16763
|
const offsetX = index % 16 * 16;
|
|
15270
16764
|
const offsetY = Math.floor(index / 256) * 16;
|
|
15271
16765
|
const offsetZ = Math.floor(index / 16) % 16 * 16;
|
|
@@ -15276,11 +16770,14 @@ var SectionMesh = class _SectionMesh extends Group2 {
|
|
|
15276
16770
|
const vertex = face * 4 + corner;
|
|
15277
16771
|
positions.push(position.getX(vertex) + offsetX, position.getY(vertex) + offsetY, position.getZ(vertex) + offsetZ);
|
|
15278
16772
|
normals.push(normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex));
|
|
15279
|
-
uvs.push(
|
|
15280
|
-
|
|
15281
|
-
|
|
16773
|
+
uvs.push(mapU(uv.getX(vertex)), mapV(uv.getY(vertex)));
|
|
16774
|
+
uvBounds.push(
|
|
16775
|
+
mapU(bounds?.getX(vertex) ?? 0),
|
|
16776
|
+
mapV(bounds?.getY(vertex) ?? 0),
|
|
16777
|
+
mapU(bounds?.getZ(vertex) ?? 1),
|
|
16778
|
+
mapV(bounds?.getW(vertex) ?? 1)
|
|
15282
16779
|
);
|
|
15283
|
-
colors.push(
|
|
16780
|
+
colors.push(color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1);
|
|
15284
16781
|
}
|
|
15285
16782
|
for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
|
|
15286
16783
|
}
|
|
@@ -15289,11 +16786,12 @@ var SectionMesh = class _SectionMesh extends Group2 {
|
|
|
15289
16786
|
geometry.setAttribute("position", new Float32BufferAttribute8(positions, 3));
|
|
15290
16787
|
geometry.setAttribute("normal", new Float32BufferAttribute8(normals, 3));
|
|
15291
16788
|
geometry.setAttribute("uv", new Float32BufferAttribute8(uvs, 2));
|
|
16789
|
+
geometry.setAttribute("uvBounds", new Float32BufferAttribute8(uvBounds, 4));
|
|
15292
16790
|
geometry.setAttribute("color", new Float32BufferAttribute8(colors, 3));
|
|
15293
16791
|
geometry.setIndex(indices);
|
|
15294
16792
|
geometry.computeBoundingBox();
|
|
15295
16793
|
geometry.computeBoundingSphere();
|
|
15296
|
-
const material = Materials.createShadedCanvasMaterial(canvas, false);
|
|
16794
|
+
const material = Materials.createShadedCanvasMaterial(canvas, false, false, true);
|
|
15297
16795
|
material.vertexColors = true;
|
|
15298
16796
|
const texture = material.uniforms?.map?.value ?? material.map;
|
|
15299
16797
|
const mesh = new Mesh9(geometry, material);
|
|
@@ -15354,7 +16852,7 @@ var Chunk = class _Chunk {
|
|
|
15354
16852
|
return this.setBlockInChunkAt(pos, block, worldPos);
|
|
15355
16853
|
}
|
|
15356
16854
|
/**
|
|
15357
|
-
*
|
|
16855
|
+
* Places a block at integer chunk-local coordinates from 0 to 15.
|
|
15358
16856
|
*/
|
|
15359
16857
|
async setBlockInChunkAt(pos, block, worldPos, onBlocksChanged = this.onBlocksChanged) {
|
|
15360
16858
|
if (typeof worldPos === "undefined") {
|
|
@@ -15367,7 +16865,7 @@ var Chunk = class _Chunk {
|
|
|
15367
16865
|
this.renderedBlocks.get(index)?.object?.removeFromScene();
|
|
15368
16866
|
if (this.sectionBlocks.delete(index)) this.meshDirty = true;
|
|
15369
16867
|
this.renderedBlocks.delete(index);
|
|
15370
|
-
let
|
|
16868
|
+
let object3;
|
|
15371
16869
|
try {
|
|
15372
16870
|
if (!readBlock) return void 0;
|
|
15373
16871
|
const stored = readBlock();
|
|
@@ -15382,27 +16880,28 @@ var Chunk = class _Chunk {
|
|
|
15382
16880
|
this.sectionBlocks.set(index, { index, template, cullMask: 0 });
|
|
15383
16881
|
this.meshDirty = true;
|
|
15384
16882
|
} else {
|
|
15385
|
-
|
|
16883
|
+
object3 = await this.scene.addBlock(blockState, {
|
|
15386
16884
|
mergeMeshes: true,
|
|
15387
16885
|
instanceMeshes: true,
|
|
15388
16886
|
maxInstanceCount: 2e3,
|
|
15389
16887
|
initialState: stored.properties
|
|
15390
16888
|
});
|
|
15391
|
-
|
|
16889
|
+
object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
15392
16890
|
}
|
|
15393
16891
|
const info = { get block() {
|
|
15394
16892
|
return readBlock();
|
|
15395
|
-
}, object };
|
|
16893
|
+
}, object: object3 };
|
|
15396
16894
|
this.renderedBlocks.set(index, info);
|
|
15397
16895
|
return info;
|
|
15398
16896
|
} catch (error) {
|
|
15399
16897
|
this.data.set(index, void 0);
|
|
15400
|
-
|
|
16898
|
+
object3?.removeFromScene();
|
|
15401
16899
|
throw error;
|
|
15402
16900
|
} finally {
|
|
15403
16901
|
await onBlocksChanged?.([worldPos]);
|
|
15404
16902
|
}
|
|
15405
16903
|
}
|
|
16904
|
+
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
15406
16905
|
async setBlockVisibleAt(pos, visible) {
|
|
15407
16906
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
15408
16907
|
const block = this.renderedBlocks.get(index);
|
|
@@ -15456,6 +16955,7 @@ var Chunk = class _Chunk {
|
|
|
15456
16955
|
this.scene.dirty = true;
|
|
15457
16956
|
this.meshDirty = false;
|
|
15458
16957
|
}
|
|
16958
|
+
/** Removes stored blocks and their render objects, then notifies the owning world of changed positions. */
|
|
15459
16959
|
async clear(onBlocksChanged = this.onBlocksChanged) {
|
|
15460
16960
|
const positions = [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
|
|
15461
16961
|
new Vector317(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
|
|
@@ -15476,6 +16976,7 @@ var Chunk = class _Chunk {
|
|
|
15476
16976
|
static isAir(block) {
|
|
15477
16977
|
return ChunkData.isAir(block);
|
|
15478
16978
|
}
|
|
16979
|
+
/** Maps chunk-local coordinates from 0 to 15 to a storage index, with x varying fastest, then z, then y. */
|
|
15479
16980
|
static chunkPosToBlockIndex(pos) {
|
|
15480
16981
|
if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
|
|
15481
16982
|
throw new RangeError("Block coordinates must be integers from 0 to 15 within a chunk");
|
|
@@ -15510,6 +17011,7 @@ export {
|
|
|
15510
17011
|
Axis,
|
|
15511
17012
|
BUILTIN_ENTITY,
|
|
15512
17013
|
BUILTIN_GENERATED,
|
|
17014
|
+
BannerPatterns,
|
|
15513
17015
|
BasicAssetKey,
|
|
15514
17016
|
BatchedExecutor,
|
|
15515
17017
|
BlockEntities,
|
|
@@ -15532,6 +17034,7 @@ export {
|
|
|
15532
17034
|
DEFAULT_ROOT,
|
|
15533
17035
|
DEFAULT_UV,
|
|
15534
17036
|
DISPLAY_POSITIONS,
|
|
17037
|
+
DYE_COLORS,
|
|
15535
17038
|
DefaultBlockStates,
|
|
15536
17039
|
DisplayPosition,
|
|
15537
17040
|
DisplayTransforms,
|
|
@@ -15542,6 +17045,7 @@ export {
|
|
|
15542
17045
|
Env,
|
|
15543
17046
|
FluidModelObject,
|
|
15544
17047
|
Fonts,
|
|
17048
|
+
GUI_CONTAINER_LAYOUTS,
|
|
15545
17049
|
Geometries,
|
|
15546
17050
|
GuiHelper,
|
|
15547
17051
|
GuiLight,
|
|
@@ -15570,11 +17074,14 @@ export {
|
|
|
15570
17074
|
NodeCache,
|
|
15571
17075
|
NodeEnv,
|
|
15572
17076
|
OrbitControls,
|
|
17077
|
+
PackFormats,
|
|
17078
|
+
PackMetadata,
|
|
15573
17079
|
PersistentCache,
|
|
15574
17080
|
Renderer,
|
|
15575
17081
|
RequestError,
|
|
15576
17082
|
Requests,
|
|
15577
17083
|
SKIN_PARTS,
|
|
17084
|
+
SceneDocumentLoader,
|
|
15578
17085
|
SceneExporter,
|
|
15579
17086
|
SceneInspector,
|
|
15580
17087
|
SceneObject,
|
|
@@ -15650,6 +17157,7 @@ export {
|
|
|
15650
17157
|
mergeBufferAttributes,
|
|
15651
17158
|
mergeBufferGeometries,
|
|
15652
17159
|
mergeVertices,
|
|
17160
|
+
parseSceneDocument,
|
|
15653
17161
|
prefix,
|
|
15654
17162
|
registerNodeEnv,
|
|
15655
17163
|
sampleEntityAnimation,
|