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.js
CHANGED
|
@@ -69,6 +69,7 @@ var init_PersistentCache = __esm({
|
|
|
69
69
|
/** Bump to invalidate every persisted entry. */
|
|
70
70
|
this.VERSION = 2;
|
|
71
71
|
}
|
|
72
|
+
/** Opens a named store in the active environment using the current cache schema version. */
|
|
72
73
|
static open(name) {
|
|
73
74
|
return Env.provider.openCache(name, _PersistentCache.VERSION);
|
|
74
75
|
}
|
|
@@ -300,6 +301,9 @@ var init_Caching = __esm({
|
|
|
300
301
|
static {
|
|
301
302
|
this.fontCache = _Caching.createAssetCache();
|
|
302
303
|
}
|
|
304
|
+
static {
|
|
305
|
+
this.bannerPatternCache = _Caching.createAssetCache();
|
|
306
|
+
}
|
|
303
307
|
static createAssetCache() {
|
|
304
308
|
return import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
|
|
305
309
|
}
|
|
@@ -327,9 +331,11 @@ var init_Caching = __esm({
|
|
|
327
331
|
this.entityTextureCache,
|
|
328
332
|
this.entityAnimationCache,
|
|
329
333
|
this.blockEntityIndexCache,
|
|
330
|
-
this.fontCache
|
|
334
|
+
this.fontCache,
|
|
335
|
+
this.bannerPatternCache
|
|
331
336
|
];
|
|
332
337
|
}
|
|
338
|
+
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
333
339
|
static clear() {
|
|
334
340
|
for (const cache of this.all) {
|
|
335
341
|
cache.invalidateAll();
|
|
@@ -372,6 +378,7 @@ var init_Geometries = __esm({
|
|
|
372
378
|
}
|
|
373
379
|
return geometry;
|
|
374
380
|
}
|
|
381
|
+
/** Returns shared geometry. Use {@link createBox} when the caller needs to modify or dispose it independently. */
|
|
375
382
|
static getBox(key) {
|
|
376
383
|
const keyStr = serializeBoxGeometryKey(key);
|
|
377
384
|
return Caching.boxGeometryCache.get(keyStr, (k) => {
|
|
@@ -555,12 +562,15 @@ var init_Requests = __esm({
|
|
|
555
562
|
static {
|
|
556
563
|
this.mcAssetRequestQueue = new RequestQueue();
|
|
557
564
|
}
|
|
565
|
+
/** Queues a general request. Set `request.signal` to cancel it and `request.timeout` to limit each attempt. */
|
|
558
566
|
static genericRequest(request) {
|
|
559
567
|
return this.genericQueue.request(request);
|
|
560
568
|
}
|
|
569
|
+
/** Queues a request separately from general traffic, using the configured asset base URL for relative paths. */
|
|
561
570
|
static mcAssetRequest(request) {
|
|
562
571
|
return this.mcAssetRequestQueue.request(request);
|
|
563
572
|
}
|
|
573
|
+
/** Sets the base URL for relative asset requests. Use {@link AssetLoader.setVersion} to change Minecraft versions. */
|
|
564
574
|
static setMcAssetRoot(root) {
|
|
565
575
|
this.mcAssetRequestQueue.baseURL = root;
|
|
566
576
|
}
|
|
@@ -613,6 +623,7 @@ var init_WrappedImage = __esm({
|
|
|
613
623
|
}
|
|
614
624
|
return false;
|
|
615
625
|
}
|
|
626
|
+
/** Whether the dimensions form a vertical strip of square frames, without consulting `.mcmeta`. */
|
|
616
627
|
get animated() {
|
|
617
628
|
return this.height > this.width && this.height % this.width === 0;
|
|
618
629
|
}
|
|
@@ -659,6 +670,7 @@ var init_ImageLoader = __esm({
|
|
|
659
670
|
static _createImage() {
|
|
660
671
|
return createImage();
|
|
661
672
|
}
|
|
673
|
+
/** Resolves when an image URL has decoded, or rejects if decoding fails. */
|
|
662
674
|
static async loadAsync(src) {
|
|
663
675
|
return new Promise((resolve, reject) => {
|
|
664
676
|
const image = this._createImage();
|
|
@@ -667,6 +679,7 @@ var init_ImageLoader = __esm({
|
|
|
667
679
|
image.src = src;
|
|
668
680
|
});
|
|
669
681
|
}
|
|
682
|
+
/** Returns an image immediately. Use the callbacks to observe loading, or await {@link loadAsync}. */
|
|
670
683
|
static loadElement(src, onload, onerr) {
|
|
671
684
|
const image = this._createImage();
|
|
672
685
|
if (onload)
|
|
@@ -694,6 +707,7 @@ var init_ImageLoader = __esm({
|
|
|
694
707
|
throw err;
|
|
695
708
|
}
|
|
696
709
|
}
|
|
710
|
+
/** Decodes the supplied bytes into a canvas without fetching the source URL again. */
|
|
697
711
|
static async infoToCanvasData(info) {
|
|
698
712
|
const type = info.type === "jpg" ? "jpeg" : info.type ?? "png";
|
|
699
713
|
const image = await _ImageLoader.loadAsync(`data:image/${type};base64,${info.data.toString("base64")}`);
|
|
@@ -710,12 +724,14 @@ var init_ImageLoader = __esm({
|
|
|
710
724
|
const image = await this.infoToCanvasData(info);
|
|
711
725
|
return image.data.getImageData(0, 0, image.width, image.height);
|
|
712
726
|
}
|
|
727
|
+
/** Returns cached RGBA pixels for an image URL or data URL. Failed loads can be retried. */
|
|
713
728
|
static async getData(src) {
|
|
714
729
|
const keyStr = serializeImageKey({ src });
|
|
715
730
|
return await Caching.imageDataCache.get(keyStr, (k) => {
|
|
716
731
|
return _ImageLoader.loadData(src);
|
|
717
732
|
});
|
|
718
733
|
}
|
|
734
|
+
/** Returns a cached canvas context and dimensions for an image URL or data URL. */
|
|
719
735
|
static async getCanvasData(src) {
|
|
720
736
|
const keyStr = serializeImageKey({ src });
|
|
721
737
|
return await Caching.canvasImageDataCache.get(keyStr, (k) => {
|
|
@@ -801,7 +817,7 @@ var init_TextureLoader = __esm({
|
|
|
801
817
|
static createTexture() {
|
|
802
818
|
return new import_three3.Texture();
|
|
803
819
|
}
|
|
804
|
-
static loadInBackground(src,
|
|
820
|
+
static loadInBackground(src, format2 = import_three3.RGBAFormat, rotation = 0) {
|
|
805
821
|
const texture = this.createTexture();
|
|
806
822
|
texture.colorSpace = THREE.SRGBColorSpace;
|
|
807
823
|
const image = ImageLoader.loadElement(src, () => {
|
|
@@ -811,11 +827,12 @@ var init_TextureLoader = __esm({
|
|
|
811
827
|
console.warn("Failed to load texture", src, error);
|
|
812
828
|
});
|
|
813
829
|
texture.image = image;
|
|
814
|
-
texture.format =
|
|
830
|
+
texture.format = format2;
|
|
815
831
|
texture.rotation = rotation;
|
|
816
832
|
return Textures.initTextureProps(texture);
|
|
817
833
|
}
|
|
818
|
-
|
|
834
|
+
/** Loads image pixels in the background. `rotation` is in radians. Check {@link hasFailed} for load failures. */
|
|
835
|
+
static load(src, format2 = import_three3.RGBAFormat, rotation = 0) {
|
|
819
836
|
const texture = new import_three3.Texture();
|
|
820
837
|
texture.colorSpace = THREE.SRGBColorSpace;
|
|
821
838
|
ImageLoader.getData(src).then((image) => {
|
|
@@ -825,7 +842,7 @@ var init_TextureLoader = __esm({
|
|
|
825
842
|
this.failedTextures.add(texture);
|
|
826
843
|
console.warn("Failed to load texture", src, error);
|
|
827
844
|
});
|
|
828
|
-
texture.format =
|
|
845
|
+
texture.format = format2;
|
|
829
846
|
texture.rotation = rotation;
|
|
830
847
|
return Textures.initTextureProps(texture);
|
|
831
848
|
}
|
|
@@ -851,8 +868,8 @@ var init_Textures = __esm({
|
|
|
851
868
|
for (let y = 0; y < size; y++) {
|
|
852
869
|
for (let x = 0; x < size; x++) {
|
|
853
870
|
const offset = (y * size + x) * 4;
|
|
854
|
-
const
|
|
855
|
-
data[offset] = data[offset + 2] =
|
|
871
|
+
const color2 = (x + y) % 2 === 0 ? 255 : 0;
|
|
872
|
+
data[offset] = data[offset + 2] = color2;
|
|
856
873
|
data[offset + 3] = 255;
|
|
857
874
|
}
|
|
858
875
|
}
|
|
@@ -863,6 +880,7 @@ var init_Textures = __esm({
|
|
|
863
880
|
return this.initTextureProps(texture);
|
|
864
881
|
});
|
|
865
882
|
}
|
|
883
|
+
/** Sets nearest-neighbor filters without changing the texture's color space. Returns the same texture. */
|
|
866
884
|
static initTextureProps(texture) {
|
|
867
885
|
texture.magFilter = THREE2.NearestFilter;
|
|
868
886
|
texture.minFilter = THREE2.NearestFilter;
|
|
@@ -875,11 +893,13 @@ var init_Textures = __esm({
|
|
|
875
893
|
static createAsset(key) {
|
|
876
894
|
return new import_three4.Texture();
|
|
877
895
|
}
|
|
896
|
+
/** Creates an sRGB color texture from a canvas. */
|
|
878
897
|
static createCanvasTexture(canvas) {
|
|
879
898
|
const texture = new import_three4.CanvasTexture(canvas);
|
|
880
899
|
texture.colorSpace = THREE2.SRGBColorSpace;
|
|
881
900
|
return this.initTextureProps(texture);
|
|
882
901
|
}
|
|
902
|
+
/** Returns a shared texture immediately and loads its image asynchronously. Failed cached loads are retried. */
|
|
883
903
|
static getImage(key) {
|
|
884
904
|
const keyStr = serializeTextureKey(key);
|
|
885
905
|
const cached2 = Caching.textureCache.peek(keyStr);
|
|
@@ -904,6 +924,7 @@ var init_Materials = __esm({
|
|
|
904
924
|
init_TextureLoader();
|
|
905
925
|
init_Caching();
|
|
906
926
|
Materials = class _Materials {
|
|
927
|
+
/** Shared checkerboard material, created on first access. */
|
|
907
928
|
static get MISSING_TEXTURE() {
|
|
908
929
|
return Caching.materialCache.get("builtin:missing-texture", () => new import_three5.MeshBasicMaterial({
|
|
909
930
|
map: Textures.getMissing(),
|
|
@@ -975,6 +996,7 @@ var init_Materials = __esm({
|
|
|
975
996
|
material.depthWrite = false;
|
|
976
997
|
break;
|
|
977
998
|
case "eyes":
|
|
999
|
+
case "no_outline":
|
|
978
1000
|
material.transparent = true;
|
|
979
1001
|
material.alphaTest = 0;
|
|
980
1002
|
material.depthWrite = false;
|
|
@@ -1007,6 +1029,7 @@ var init_Materials = __esm({
|
|
|
1007
1029
|
if (tint !== void 0) material.color.multiply(new import_three5.Color(tint));
|
|
1008
1030
|
return material;
|
|
1009
1031
|
}
|
|
1032
|
+
/** Creates an unlit, transparent GUI material without depth writes or tone mapping. */
|
|
1010
1033
|
static createGuiCanvasMaterial(canvas) {
|
|
1011
1034
|
const material = new import_three5.MeshBasicMaterial({
|
|
1012
1035
|
map: Textures.createCanvasTexture(canvas),
|
|
@@ -1032,7 +1055,7 @@ var init_Materials = __esm({
|
|
|
1032
1055
|
};
|
|
1033
1056
|
return material;
|
|
1034
1057
|
}
|
|
1035
|
-
static createShadedCanvasMaterial(canvas, transparent = false, shade = false) {
|
|
1058
|
+
static createShadedCanvasMaterial(canvas, transparent = false, shade = false, boundUvs = false) {
|
|
1036
1059
|
const vertShader = `
|
|
1037
1060
|
uniform bool SHADE;
|
|
1038
1061
|
|
|
@@ -1042,6 +1065,10 @@ var init_Materials = __esm({
|
|
|
1042
1065
|
#else
|
|
1043
1066
|
varying vec2 vUv;
|
|
1044
1067
|
#endif
|
|
1068
|
+
#ifdef BOUND_UVS
|
|
1069
|
+
attribute vec4 uvBounds;
|
|
1070
|
+
flat out vec4 vUvBounds;
|
|
1071
|
+
#endif
|
|
1045
1072
|
varying vec3 vTint;
|
|
1046
1073
|
varying float light;
|
|
1047
1074
|
varying float lift;
|
|
@@ -1077,13 +1104,17 @@ var init_Materials = __esm({
|
|
|
1077
1104
|
}
|
|
1078
1105
|
|
|
1079
1106
|
vUv = uv;
|
|
1107
|
+
#ifdef BOUND_UVS
|
|
1108
|
+
vUvBounds = uvBounds;
|
|
1109
|
+
#endif
|
|
1080
1110
|
vTint = color;
|
|
1081
1111
|
|
|
1112
|
+
vec4 mvPosition = vec4(position, 1.0);
|
|
1082
1113
|
#ifdef USE_INSTANCING
|
|
1083
|
-
|
|
1084
|
-
#else
|
|
1085
|
-
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
|
1114
|
+
mvPosition = instanceMatrix * mvPosition;
|
|
1086
1115
|
#endif
|
|
1116
|
+
mvPosition = modelViewMatrix * mvPosition;
|
|
1117
|
+
gl_Position = projectionMatrix * mvPosition;
|
|
1087
1118
|
|
|
1088
1119
|
}
|
|
1089
1120
|
`;
|
|
@@ -1103,13 +1134,22 @@ var init_Materials = __esm({
|
|
|
1103
1134
|
#else
|
|
1104
1135
|
varying vec2 vUv;
|
|
1105
1136
|
#endif
|
|
1137
|
+
#ifdef BOUND_UVS
|
|
1138
|
+
flat in vec4 vUvBounds;
|
|
1139
|
+
#endif
|
|
1106
1140
|
varying vec3 vTint;
|
|
1107
1141
|
varying float light;
|
|
1108
1142
|
varying float lift;
|
|
1109
1143
|
|
|
1110
1144
|
void main(void)
|
|
1111
1145
|
{
|
|
1112
|
-
|
|
1146
|
+
#ifdef BOUND_UVS
|
|
1147
|
+
// Keep edge samples inside their texture even without centroid interpolation.
|
|
1148
|
+
vec2 mapUv = clamp(vUv, vUvBounds.xy, vUvBounds.zw);
|
|
1149
|
+
#else
|
|
1150
|
+
vec2 mapUv = vUv;
|
|
1151
|
+
#endif
|
|
1152
|
+
vec4 color = texture2D(map, mapUv);
|
|
1113
1153
|
color.rgb *= vTint;
|
|
1114
1154
|
|
|
1115
1155
|
if (color.a < 0.01) discard;
|
|
@@ -1130,7 +1170,8 @@ var init_Materials = __esm({
|
|
|
1130
1170
|
}
|
|
1131
1171
|
`;
|
|
1132
1172
|
try {
|
|
1133
|
-
|
|
1173
|
+
const material = new import_three5.ShaderMaterial({
|
|
1174
|
+
defines: boundUvs ? { BOUND_UVS: true } : {},
|
|
1134
1175
|
uniforms: {
|
|
1135
1176
|
SHADE: { value: true },
|
|
1136
1177
|
BRIGHTNESS: { value: 1 },
|
|
@@ -1148,11 +1189,14 @@ var init_Materials = __esm({
|
|
|
1148
1189
|
side: transparent ? import_three5.DoubleSide : import_three5.FrontSide,
|
|
1149
1190
|
alphaTest: 0.5
|
|
1150
1191
|
});
|
|
1192
|
+
Object.assign(material.defaultAttributeValues, { uvBounds: [0, 0, 1, 1] });
|
|
1193
|
+
return material;
|
|
1151
1194
|
} catch (e) {
|
|
1152
1195
|
console.warn(e);
|
|
1153
1196
|
}
|
|
1154
1197
|
return this.createBasicCanvasMaterial(canvas, transparent, shade);
|
|
1155
1198
|
}
|
|
1199
|
+
/** Returns a shared image material. Use {@link createImage} for a separate material instance. */
|
|
1156
1200
|
static getImage(key) {
|
|
1157
1201
|
const keyStr = serializeMaterialKey(key);
|
|
1158
1202
|
const map = Caching.materialCache.peek(keyStr)?.map;
|
|
@@ -1327,6 +1371,14 @@ var init_AssetKey = __esm({
|
|
|
1327
1371
|
}
|
|
1328
1372
|
};
|
|
1329
1373
|
AssetKey = class _AssetKey extends BasicAssetKey {
|
|
1374
|
+
/**
|
|
1375
|
+
* Creates a key from separate path components. Use {@link parse} for a combined reference.
|
|
1376
|
+
*
|
|
1377
|
+
* @param assetType - Asset directory, such as `models`, `textures`, or `blockstates`.
|
|
1378
|
+
* @param type - Subdirectory, such as `block` or `item`.
|
|
1379
|
+
* @param rootType - Top-level directory, usually `assets` or `data`.
|
|
1380
|
+
* @param root - Base URL override for hosted asset sources.
|
|
1381
|
+
*/
|
|
1330
1382
|
constructor(namespace, path, assetType, type, rootType = "assets", extension = ".json", root) {
|
|
1331
1383
|
super(namespace, path);
|
|
1332
1384
|
this.namespace = namespace;
|
|
@@ -1338,6 +1390,16 @@ var init_AssetKey = __esm({
|
|
|
1338
1390
|
this.root = root;
|
|
1339
1391
|
this.isAssetKey = true;
|
|
1340
1392
|
}
|
|
1393
|
+
/**
|
|
1394
|
+
* Parses an asset reference such as `minecraft:block/stone`.
|
|
1395
|
+
* A missing namespace defaults to the origin's namespace or `minecraft`.
|
|
1396
|
+
*
|
|
1397
|
+
* @param assetType - Asset category, such as `models` or `textures`.
|
|
1398
|
+
* @param str - Path with an optional namespace and file extension.
|
|
1399
|
+
* @param origin - Supplies the asset root, default namespace, and non-texture extension.
|
|
1400
|
+
* @returns A key with the matching extension removed from its path. Textures use `.png`;
|
|
1401
|
+
* other assets use the origin's extension or `.json`.
|
|
1402
|
+
*/
|
|
1341
1403
|
static parse(assetType, str, origin) {
|
|
1342
1404
|
let namespace = origin?.namespace || DEFAULT_NAMESPACE;
|
|
1343
1405
|
if (str.includes(":")) {
|
|
@@ -1369,6 +1431,7 @@ var init_AssetKey = __esm({
|
|
|
1369
1431
|
];
|
|
1370
1432
|
return a.join("/");
|
|
1371
1433
|
}
|
|
1434
|
+
/** Joins the type subdirectory and path without adding a namespace or file extension. */
|
|
1372
1435
|
getFullPath() {
|
|
1373
1436
|
let p10 = [];
|
|
1374
1437
|
if (this.type) {
|
|
@@ -1380,6 +1443,7 @@ var init_AssetKey = __esm({
|
|
|
1380
1443
|
toNamespacedString() {
|
|
1381
1444
|
return this.namespace + ":" + this.getFullPath();
|
|
1382
1445
|
}
|
|
1446
|
+
/** Returns a cache identifier that includes the explicit root or {@link AssetLoader.ROOT}. */
|
|
1383
1447
|
serialize() {
|
|
1384
1448
|
return this.toString();
|
|
1385
1449
|
}
|
|
@@ -1391,17 +1455,17 @@ var init_AssetKey = __esm({
|
|
|
1391
1455
|
var DisplayPosition, DISPLAY_POSITIONS;
|
|
1392
1456
|
var init_DisplayPosition = __esm({
|
|
1393
1457
|
"src/model/DisplayPosition.ts"() {
|
|
1394
|
-
DisplayPosition = /* @__PURE__ */ ((
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
return
|
|
1458
|
+
DisplayPosition = /* @__PURE__ */ ((DisplayPosition3) => {
|
|
1459
|
+
DisplayPosition3["THIRDPERSON_RIGHTHAND"] = "thirdperson_righthand";
|
|
1460
|
+
DisplayPosition3["THIRDPERSON_LEFTHAND"] = "thirdperson_lefthand";
|
|
1461
|
+
DisplayPosition3["FIRSTPERSON_RIGHTHAND"] = "firstperson_righthand";
|
|
1462
|
+
DisplayPosition3["FIRSTPERSON_LEFTHAND"] = "firstperson_lefthand";
|
|
1463
|
+
DisplayPosition3["GUI"] = "gui";
|
|
1464
|
+
DisplayPosition3["HEAD"] = "head";
|
|
1465
|
+
DisplayPosition3["GROUND"] = "ground";
|
|
1466
|
+
DisplayPosition3["FIXED"] = "fixed";
|
|
1467
|
+
DisplayPosition3["ON_SHELF"] = "on_shelf";
|
|
1468
|
+
return DisplayPosition3;
|
|
1405
1469
|
})(DisplayPosition || {});
|
|
1406
1470
|
DISPLAY_POSITIONS = Object.values(DisplayPosition);
|
|
1407
1471
|
}
|
|
@@ -1439,6 +1503,7 @@ var init_DisplayTransforms = __esm({
|
|
|
1439
1503
|
const scale = new import_three7.Vector3(...transform.scale ?? [1, 1, 1]).clampScalar(-4, 4);
|
|
1440
1504
|
return new import_three7.Matrix4().compose(translation, rotation, scale);
|
|
1441
1505
|
}
|
|
1506
|
+
/** Transforms geometry in place and reverses triangle winding when the matrix reflects it. */
|
|
1442
1507
|
static apply(geometry, matrix) {
|
|
1443
1508
|
geometry.applyMatrix4(matrix);
|
|
1444
1509
|
if (matrix.determinant() < 0) {
|
|
@@ -1466,7 +1531,10 @@ var init_ModelMerger = __esm({
|
|
|
1466
1531
|
init_AssetKey();
|
|
1467
1532
|
init_DisplayTransforms();
|
|
1468
1533
|
ModelMerger = class {
|
|
1534
|
+
/** Loads the parent chain and returns a merged model without modifying the supplied definition. */
|
|
1469
1535
|
static async mergeWithParents(model) {
|
|
1536
|
+
const parts = model.parts;
|
|
1537
|
+
if (parts) return { ...model, parts: await Promise.all(parts.map((part) => this.mergeWithParents(part))) };
|
|
1470
1538
|
const models = await this.collectAllParents(model);
|
|
1471
1539
|
let merged = {};
|
|
1472
1540
|
for (const source of [...models, model]) {
|
|
@@ -1478,6 +1546,7 @@ var init_ModelMerger = __esm({
|
|
|
1478
1546
|
}
|
|
1479
1547
|
merged.hierarchy = models.map((m) => m.parent).filter((p10) => `${p10}`);
|
|
1480
1548
|
merged.hierarchy.push(`${merged.parent}`);
|
|
1549
|
+
if (model.components) merged.components = model.components;
|
|
1481
1550
|
return merged;
|
|
1482
1551
|
}
|
|
1483
1552
|
static async collectAllParents(model) {
|
|
@@ -1516,6 +1585,79 @@ var init_AssetParsers = __esm({
|
|
|
1516
1585
|
}
|
|
1517
1586
|
});
|
|
1518
1587
|
|
|
1588
|
+
// src/assets/BannerPatterns.ts
|
|
1589
|
+
var DYE_COLORS, BannerPatterns;
|
|
1590
|
+
var init_BannerPatterns = __esm({
|
|
1591
|
+
"src/assets/BannerPatterns.ts"() {
|
|
1592
|
+
init_AssetKey();
|
|
1593
|
+
init_AssetLoader();
|
|
1594
|
+
init_AssetParsers();
|
|
1595
|
+
init_Caching();
|
|
1596
|
+
DYE_COLORS = Object.freeze({
|
|
1597
|
+
white: 16383998,
|
|
1598
|
+
orange: 16351261,
|
|
1599
|
+
magenta: 13061821,
|
|
1600
|
+
light_blue: 3847130,
|
|
1601
|
+
yellow: 16701501,
|
|
1602
|
+
lime: 8439583,
|
|
1603
|
+
pink: 15961002,
|
|
1604
|
+
gray: 4673362,
|
|
1605
|
+
light_gray: 10329495,
|
|
1606
|
+
cyan: 1481884,
|
|
1607
|
+
purple: 8991416,
|
|
1608
|
+
blue: 3949738,
|
|
1609
|
+
brown: 8606770,
|
|
1610
|
+
green: 6192150,
|
|
1611
|
+
red: 11546150,
|
|
1612
|
+
black: 1908001
|
|
1613
|
+
});
|
|
1614
|
+
BannerPatterns = class {
|
|
1615
|
+
/** Lists namespaced pattern IDs from the selected namespace's data directory. */
|
|
1616
|
+
static async getList(namespace = "minecraft", root) {
|
|
1617
|
+
const collect = async (path) => {
|
|
1618
|
+
const prefix2 = path ? `${path}/` : "";
|
|
1619
|
+
const key = new AssetKey(namespace, `${prefix2}_list`, "banner_pattern", void 0, "data", ".json", root);
|
|
1620
|
+
const list2 = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
|
|
1621
|
+
if (!list2) return [];
|
|
1622
|
+
const files = list2.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${namespace}:${prefix2}${file.slice(0, -5)}`);
|
|
1623
|
+
const children = await Promise.all(list2.directories.map((directory) => collect(`${prefix2}${directory}`)));
|
|
1624
|
+
return [...files, ...children.flat()];
|
|
1625
|
+
};
|
|
1626
|
+
return collect("");
|
|
1627
|
+
}
|
|
1628
|
+
/** Returns the named dye's sRGB value, rejecting unknown colors. */
|
|
1629
|
+
static getColor(color2) {
|
|
1630
|
+
if (typeof color2 !== "string" || !Object.prototype.hasOwnProperty.call(DYE_COLORS, color2)) throw new Error(`Unsupported dye color ${color2}`);
|
|
1631
|
+
return DYE_COLORS[color2];
|
|
1632
|
+
}
|
|
1633
|
+
/** Resolves the first 16 layers of a `banner_patterns` component, preserving their order. */
|
|
1634
|
+
static async getLayers(value, target, root) {
|
|
1635
|
+
if (value === void 0) return [];
|
|
1636
|
+
if (!Array.isArray(value)) throw new Error("banner_patterns must be an array");
|
|
1637
|
+
return Promise.all(value.slice(0, 16).map(async (layer) => {
|
|
1638
|
+
if (!layer || typeof layer !== "object" || Array.isArray(layer)) throw new Error("Each banner pattern requires a pattern and dye color");
|
|
1639
|
+
const color2 = this.getColor(layer.color);
|
|
1640
|
+
let pattern2 = layer.pattern;
|
|
1641
|
+
if (typeof pattern2 === "string") {
|
|
1642
|
+
const id = this.identifier(pattern2);
|
|
1643
|
+
const key = new AssetKey(id.namespace, id.getFullPath(), "banner_pattern", void 0, "data", ".json", root);
|
|
1644
|
+
pattern2 = await Caching.bannerPatternCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
|
|
1645
|
+
if (!pattern2) throw new Error(`Missing banner pattern ${layer.pattern}`);
|
|
1646
|
+
}
|
|
1647
|
+
if (!pattern2 || typeof pattern2 !== "object" || Array.isArray(pattern2) || typeof pattern2.translation_key !== "string") throw new Error("Banner patterns require asset_id and translation_key");
|
|
1648
|
+
const asset2 = this.identifier(pattern2.asset_id);
|
|
1649
|
+
return { color: color2, texture: new AssetKey(asset2.namespace, `${target}/${asset2.getFullPath()}`, "textures", "entity", "assets", ".png", root) };
|
|
1650
|
+
}));
|
|
1651
|
+
}
|
|
1652
|
+
static identifier(value) {
|
|
1653
|
+
if (typeof value !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(value)) throw new Error(`Invalid banner-pattern identifier ${value}`);
|
|
1654
|
+
const [namespace, path] = value.includes(":") ? value.split(":") : ["minecraft", value];
|
|
1655
|
+
return new AssetKey(namespace, path);
|
|
1656
|
+
}
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
});
|
|
1660
|
+
|
|
1519
1661
|
// src/assets/Models.ts
|
|
1520
1662
|
var Models;
|
|
1521
1663
|
var init_Models = __esm({
|
|
@@ -1527,12 +1669,15 @@ var init_Models = __esm({
|
|
|
1527
1669
|
init_PersistentCache();
|
|
1528
1670
|
init_AssetKey();
|
|
1529
1671
|
init_AssetParsers();
|
|
1672
|
+
init_DisplayPosition();
|
|
1673
|
+
init_BannerPatterns();
|
|
1530
1674
|
Models = class _Models {
|
|
1531
1675
|
// opened lazily: touching the store at import time would hit IndexedDB/disk just for loading
|
|
1532
1676
|
// the library, and would require an EnvProvider before any entry has had a chance to register
|
|
1533
1677
|
static get PERSISTENT_CACHE() {
|
|
1534
1678
|
return this._persistentCache ??= PersistentCache.open("minerender-models");
|
|
1535
1679
|
}
|
|
1680
|
+
/** Lists item filenames from the asset source's `_list.json`, with a legacy model-directory fallback. */
|
|
1536
1681
|
static async getItemList() {
|
|
1537
1682
|
const key = new AssetKey(DEFAULT_NAMESPACE, "_list", "items");
|
|
1538
1683
|
const legacyKey = new AssetKey(DEFAULT_NAMESPACE, "_list", "models", "item");
|
|
@@ -1540,34 +1685,174 @@ var init_Models = __esm({
|
|
|
1540
1685
|
return (await AssetLoader.getFirst([key, legacyKey], "list" /* LIST */))?.asset;
|
|
1541
1686
|
}).then((r) => r?.files ?? []);
|
|
1542
1687
|
}
|
|
1543
|
-
static async loadAndMerge(key) {
|
|
1544
|
-
const model = key.type === "item" ? await this.getItemModel(key) : await this.getRaw(key);
|
|
1688
|
+
static async loadAndMerge(key, context = {}) {
|
|
1689
|
+
const model = key.type === "item" ? await this.getItemModel(key, context) : await this.getRaw(key);
|
|
1545
1690
|
if (!model) {
|
|
1546
1691
|
return void 0;
|
|
1547
1692
|
}
|
|
1548
1693
|
return ModelMerger.mergeWithParents(model);
|
|
1549
1694
|
}
|
|
1550
|
-
static async getItemModel(key) {
|
|
1695
|
+
static async getItemModel(key, context = {}) {
|
|
1696
|
+
const preview = this.snapshotContext(key, context);
|
|
1551
1697
|
const itemKey = new AssetKey(key.namespace, key.path, "items", void 0, key.rootType, ".json", key.root);
|
|
1552
|
-
const
|
|
1698
|
+
const cacheKey = itemKey.serialize() + this.contextKey(preview);
|
|
1699
|
+
const model = await this.PERSISTENT_CACHE.getOrLoad(`item-v4:${AssetLoader.persistentKey(cacheKey)}`, async () => {
|
|
1553
1700
|
const result = await AssetLoader.getFirst([itemKey, key], "json" /* JSON */);
|
|
1554
1701
|
if (!result) return void 0;
|
|
1555
|
-
if (result.key.assetType !== "items") return { ...result.asset, key };
|
|
1556
|
-
const
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1702
|
+
if (result.key.assetType !== "items") return { ...result.asset, key, components: preview.components };
|
|
1703
|
+
const load = async (selected) => {
|
|
1704
|
+
if ("parts" in selected) {
|
|
1705
|
+
return { key, parts: await Promise.all(selected.parts.map(load)) };
|
|
1706
|
+
}
|
|
1707
|
+
if ("item" in selected) {
|
|
1708
|
+
const model3 = await this.getItemModel(selected.item, { displayContext: preview.displayContext });
|
|
1709
|
+
if (!model3) throw new Error(`Item ${key.toNamespacedString()} references missing item ${selected.item.toNamespacedString()}`);
|
|
1710
|
+
return model3;
|
|
1711
|
+
}
|
|
1712
|
+
const modelKey = AssetKey.parse("models", selected.model);
|
|
1713
|
+
modelKey.root = key.root;
|
|
1714
|
+
const model2 = await this.getRaw(modelKey);
|
|
1715
|
+
if (!model2) throw new Error(`Item ${key.toNamespacedString()} references missing model ${selected.model}`);
|
|
1716
|
+
return { ...model2, components: preview.components, ...selected.special && { special: selected.special }, ...selected.tints && { tints: selected.tints } };
|
|
1717
|
+
};
|
|
1718
|
+
return load(this.selectItemModel(result.asset.model, key, preview));
|
|
1719
|
+
});
|
|
1720
|
+
const restore = (model2) => ({
|
|
1721
|
+
...model2,
|
|
1722
|
+
key: Object.assign(new AssetKey("", ""), model2.key),
|
|
1723
|
+
...model2.parts && { parts: model2.parts.map(restore) }
|
|
1562
1724
|
});
|
|
1563
|
-
return model ?
|
|
1725
|
+
return model ? restore(model) : void 0;
|
|
1726
|
+
}
|
|
1727
|
+
static contextIdentifier(id) {
|
|
1728
|
+
if (typeof id !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(id)) {
|
|
1729
|
+
throw new Error(`Invalid item-preview identifier: ${id}`);
|
|
1730
|
+
}
|
|
1731
|
+
return id.includes(":") ? id : `minecraft:${id}`;
|
|
1732
|
+
}
|
|
1733
|
+
static snapshotContext(key, context) {
|
|
1734
|
+
const displayContext = context.displayContext ?? "gui" /* GUI */;
|
|
1735
|
+
if (displayContext !== "none" && !Object.values(DisplayPosition).includes(displayContext)) {
|
|
1736
|
+
throw new Error(`Unsupported item display context: ${displayContext}`);
|
|
1737
|
+
}
|
|
1738
|
+
for (const map of [context.properties, context.components, context.itemReferences]) {
|
|
1739
|
+
if (map !== void 0 && (!map || typeof map !== "object" || Array.isArray(map))) {
|
|
1740
|
+
throw new Error("Item-preview properties, components, and references must be objects keyed by identifier");
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
const count = context.count === void 0 ? 1 : context.count;
|
|
1744
|
+
if (!Number.isSafeInteger(count) || count < 0) throw new Error("Item-preview count must be a nonnegative safe integer");
|
|
1745
|
+
const properties = {};
|
|
1746
|
+
for (const [id, value] of Object.entries(context.properties ?? {})) {
|
|
1747
|
+
const property = this.contextIdentifier(id);
|
|
1748
|
+
if (!["boolean", "string", "number"].includes(typeof value) || typeof value === "number" && !Number.isFinite(value)) {
|
|
1749
|
+
throw new Error(`Item-preview property ${property} must be a boolean, string, or finite number`);
|
|
1750
|
+
}
|
|
1751
|
+
if (Object.prototype.hasOwnProperty.call(properties, property)) throw new Error(`Duplicate item-preview property: ${property}`);
|
|
1752
|
+
properties[property] = value;
|
|
1753
|
+
}
|
|
1754
|
+
const components = {};
|
|
1755
|
+
for (const [id, value] of Object.entries(context.components ?? {})) {
|
|
1756
|
+
const component = this.contextIdentifier(id);
|
|
1757
|
+
if (Object.prototype.hasOwnProperty.call(components, component)) throw new Error(`Duplicate item-preview component: ${component}`);
|
|
1758
|
+
components[component] = this.snapshotJson(value);
|
|
1759
|
+
}
|
|
1760
|
+
const itemReferences = {};
|
|
1761
|
+
for (const [id, value] of Object.entries(context.itemReferences ?? {})) {
|
|
1762
|
+
const reference = this.contextIdentifier(id);
|
|
1763
|
+
if (!isAssetKey(value) || value.assetType !== "models" || value.type !== "item") {
|
|
1764
|
+
throw new Error(`Item-preview reference ${reference} must be an item-model AssetKey`);
|
|
1765
|
+
}
|
|
1766
|
+
this.contextIdentifier(`${value.namespace}:${value.path}`);
|
|
1767
|
+
if (Object.prototype.hasOwnProperty.call(itemReferences, reference)) throw new Error(`Duplicate item-preview reference: ${reference}`);
|
|
1768
|
+
itemReferences[reference] = Object.assign(new AssetKey("", ""), value, { root: value.root ?? key.root });
|
|
1769
|
+
}
|
|
1770
|
+
return { displayContext, properties, components, count, itemReferences };
|
|
1771
|
+
}
|
|
1772
|
+
static contextKey(context) {
|
|
1773
|
+
const itemReferences = Object.fromEntries(Object.entries(context.itemReferences).map(([id, key]) => [id, key.serialize()]));
|
|
1774
|
+
return `|item-v4:${JSON.stringify(this.snapshotJson({ ...context, itemReferences }))}`;
|
|
1564
1775
|
}
|
|
1565
|
-
|
|
1566
|
-
|
|
1776
|
+
static snapshotJson(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
1777
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return value;
|
|
1778
|
+
if (typeof value !== "object" || ancestors.has(value) || !Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
|
|
1779
|
+
throw new Error("Item-preview components must contain finite JSON values without circular references");
|
|
1780
|
+
}
|
|
1781
|
+
ancestors.add(value);
|
|
1782
|
+
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)]));
|
|
1783
|
+
ancestors.delete(value);
|
|
1784
|
+
return copy;
|
|
1785
|
+
}
|
|
1786
|
+
static propertyValue(node, context) {
|
|
1787
|
+
const kind = node.type.replace(/^minecraft:/, "");
|
|
1788
|
+
const property = node.property ? this.contextIdentifier(node.property) : void 0;
|
|
1789
|
+
if (kind === "select" && property === "minecraft:display_context") return context.displayContext;
|
|
1790
|
+
if (property && Object.prototype.hasOwnProperty.call(context.properties, property)) return context.properties[property];
|
|
1791
|
+
const fallback = kind === "condition" ? false : kind === "range_dispatch" ? 0 : void 0;
|
|
1792
|
+
const component = (id) => context.components[this.contextIdentifier(id)];
|
|
1793
|
+
if (property === "minecraft:custom_model_data") {
|
|
1794
|
+
const index = node.index ?? 0;
|
|
1795
|
+
if (!Number.isSafeInteger(index) || index < 0) throw new Error("Item-model custom_model_data index must be a nonnegative integer");
|
|
1796
|
+
const data = component("custom_model_data");
|
|
1797
|
+
if (data === void 0) return fallback;
|
|
1798
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) throw new Error("Item-preview custom_model_data must be an object");
|
|
1799
|
+
const field = kind === "condition" ? "flags" : kind === "select" ? "strings" : "floats";
|
|
1800
|
+
const values = data[field];
|
|
1801
|
+
if (values === void 0) return fallback;
|
|
1802
|
+
if (!Array.isArray(values)) throw new Error(`Item-preview custom_model_data.${field} must be an array`);
|
|
1803
|
+
return index < values.length ? values[index] : fallback;
|
|
1804
|
+
}
|
|
1805
|
+
if (kind === "condition" && property === "minecraft:has_component") {
|
|
1806
|
+
if (node.ignore_default) throw new Error("Item-preview has_component with ignore_default requires an explicit properties override; item defaults are unavailable");
|
|
1807
|
+
return Object.prototype.hasOwnProperty.call(context.components, this.contextIdentifier(node.component));
|
|
1808
|
+
}
|
|
1809
|
+
if (kind === "select") {
|
|
1810
|
+
switch (property) {
|
|
1811
|
+
case "minecraft:component":
|
|
1812
|
+
return component(node.component);
|
|
1813
|
+
case "minecraft:block_state": {
|
|
1814
|
+
const state = component("block_state");
|
|
1815
|
+
if (state === void 0) return void 0;
|
|
1816
|
+
if (!state || typeof state !== "object" || Array.isArray(state)) throw new Error("Item-preview block_state must be an object");
|
|
1817
|
+
if (typeof node.block_state_property !== "string") throw new Error("Item-model block_state requires block_state_property");
|
|
1818
|
+
return Object.prototype.hasOwnProperty.call(state, node.block_state_property) ? state[node.block_state_property] : void 0;
|
|
1819
|
+
}
|
|
1820
|
+
case "minecraft:charge_type": {
|
|
1821
|
+
const projectiles = component("charged_projectiles");
|
|
1822
|
+
if (projectiles === void 0) return "none";
|
|
1823
|
+
if (!Array.isArray(projectiles)) throw new Error("Item-preview charged_projectiles must be an array of item stacks");
|
|
1824
|
+
const ids = projectiles.map((stack) => {
|
|
1825
|
+
if (!stack || typeof stack !== "object" || Array.isArray(stack)) throw new Error("Item-preview charged_projectiles must contain item-stack objects");
|
|
1826
|
+
return this.contextIdentifier(stack.id);
|
|
1827
|
+
});
|
|
1828
|
+
return !ids.length ? "none" : ids.includes("minecraft:firework_rocket") ? "rocket" : "arrow";
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
if (kind === "range_dispatch" && (property === "minecraft:damage" || property === "minecraft:count")) {
|
|
1833
|
+
const damage = property === "minecraft:damage";
|
|
1834
|
+
const maximumComponent = component(damage ? "max_damage" : "max_stack_size");
|
|
1835
|
+
const maximum = maximumComponent === void 0 ? damage ? 0 : 1 : maximumComponent;
|
|
1836
|
+
const damageComponent = component("damage");
|
|
1837
|
+
const value = damage ? damageComponent === void 0 ? 0 : damageComponent : context.count;
|
|
1838
|
+
if (typeof value !== "number" || typeof maximum !== "number" || maximum < 0) throw new Error(`Item-preview ${property} requires numeric components and a nonnegative maximum`);
|
|
1839
|
+
const clamped = Math.max(0, Math.min(maximum, value));
|
|
1840
|
+
return node.normalize === false ? clamped : clamped / maximum;
|
|
1841
|
+
}
|
|
1842
|
+
return fallback;
|
|
1843
|
+
}
|
|
1844
|
+
static selectItemModel(node, key, context) {
|
|
1567
1845
|
if (!node || typeof node.type !== "string") {
|
|
1568
1846
|
throw new Error(`Unsupported item model definition for ${key.toNamespacedString()}`);
|
|
1569
1847
|
}
|
|
1570
1848
|
switch (node.type.replace(/^minecraft:/, "")) {
|
|
1849
|
+
case "composite":
|
|
1850
|
+
if (Array.isArray(node.models)) return { parts: node.models.map((child) => this.selectItemModel(child, key, context)) };
|
|
1851
|
+
break;
|
|
1852
|
+
case "bundle/selected_item": {
|
|
1853
|
+
const item = context.itemReferences["minecraft:bundle/selected_item"];
|
|
1854
|
+
return item ? { item } : { parts: [] };
|
|
1855
|
+
}
|
|
1571
1856
|
case "model":
|
|
1572
1857
|
if (typeof node.model === "string" && node.model) return { model: node.model, tints: node.tints };
|
|
1573
1858
|
break;
|
|
@@ -1575,6 +1860,23 @@ var init_Models = __esm({
|
|
|
1575
1860
|
if (!node.base || !node.model || typeof node.model !== "object") break;
|
|
1576
1861
|
const special = node.model;
|
|
1577
1862
|
switch (special.type) {
|
|
1863
|
+
case "banner":
|
|
1864
|
+
case "minecraft:banner":
|
|
1865
|
+
if (typeof special.color === "string" && Object.prototype.hasOwnProperty.call(DYE_COLORS, special.color)) return { model: node.base, special };
|
|
1866
|
+
break;
|
|
1867
|
+
case "shield":
|
|
1868
|
+
case "minecraft:shield":
|
|
1869
|
+
case "trident":
|
|
1870
|
+
case "minecraft:trident":
|
|
1871
|
+
case "conduit":
|
|
1872
|
+
case "minecraft:conduit":
|
|
1873
|
+
return { model: node.base, special };
|
|
1874
|
+
case "shulker_box":
|
|
1875
|
+
case "minecraft:shulker_box":
|
|
1876
|
+
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))) {
|
|
1877
|
+
return { model: node.base, special };
|
|
1878
|
+
}
|
|
1879
|
+
break;
|
|
1578
1880
|
case "chest":
|
|
1579
1881
|
case "minecraft:chest":
|
|
1580
1882
|
case "bed":
|
|
@@ -1588,21 +1890,34 @@ var init_Models = __esm({
|
|
|
1588
1890
|
}
|
|
1589
1891
|
throw new Error(`Unsupported special item renderer ${special.type} for ${key.toNamespacedString()}`);
|
|
1590
1892
|
}
|
|
1591
|
-
case "condition":
|
|
1592
|
-
|
|
1893
|
+
case "condition": {
|
|
1894
|
+
const value = this.propertyValue(node, context);
|
|
1895
|
+
if (typeof value !== "boolean") throw new Error(`Item-preview condition ${node.property} requires a boolean`);
|
|
1896
|
+
return this.selectItemModel(value ? node.on_true : node.on_false, key, context);
|
|
1897
|
+
}
|
|
1593
1898
|
case "select": {
|
|
1594
|
-
const
|
|
1595
|
-
|
|
1899
|
+
const value = this.propertyValue(node, context);
|
|
1900
|
+
const component = node.property && this.contextIdentifier(node.property) === "minecraft:component";
|
|
1901
|
+
if (!component && value !== void 0 && typeof value !== "string") throw new Error(`Item-preview selector ${node.property} requires a string`);
|
|
1902
|
+
const matches = (candidate) => component ? JSON.stringify(this.snapshotJson(candidate)) === JSON.stringify(this.snapshotJson(value)) : candidate === value;
|
|
1903
|
+
const selected = value === void 0 ? void 0 : node.cases?.find((entry) => matches(entry.when) || Array.isArray(entry.when) && entry.when.some(matches))?.model;
|
|
1904
|
+
return this.selectItemModel(selected ?? node.fallback, key, context);
|
|
1596
1905
|
}
|
|
1597
1906
|
case "range_dispatch": {
|
|
1907
|
+
const value = this.propertyValue(node, context);
|
|
1908
|
+
if (typeof value !== "number") throw new Error(`Item-preview range ${node.property} requires a number`);
|
|
1909
|
+
if (Number.isNaN(value)) return this.selectItemModel(node.fallback, key, context);
|
|
1910
|
+
const scaled = value * (node.scale ?? 1);
|
|
1911
|
+
if (!Number.isFinite(scaled)) throw new Error(`Item-preview range ${node.property} requires a finite value and scale`);
|
|
1598
1912
|
const selected = node.entries?.reduce((match, entry) => {
|
|
1599
|
-
return entry.threshold <=
|
|
1913
|
+
return entry.threshold <= scaled && (!match || entry.threshold >= match.threshold) ? entry : match;
|
|
1600
1914
|
}, void 0);
|
|
1601
|
-
return this.
|
|
1915
|
+
return this.selectItemModel(selected?.model ?? node.fallback, key, context);
|
|
1602
1916
|
}
|
|
1603
1917
|
}
|
|
1604
1918
|
throw new Error(`Unsupported item model ${node?.type ?? "definition"} for ${key.toNamespacedString()}`);
|
|
1605
1919
|
}
|
|
1920
|
+
/** Loads one model file without resolving its parents. Returns `undefined` when the file is missing. */
|
|
1606
1921
|
static async getRaw(key) {
|
|
1607
1922
|
if (!key.assetType) {
|
|
1608
1923
|
key.assetType = "models";
|
|
@@ -1615,28 +1930,39 @@ var init_Models = __esm({
|
|
|
1615
1930
|
return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
|
|
1616
1931
|
return AssetLoader.get(key, "model" /* MODEL */);
|
|
1617
1932
|
});
|
|
1618
|
-
}).then((
|
|
1619
|
-
if (
|
|
1620
|
-
|
|
1933
|
+
}).then((asset2) => {
|
|
1934
|
+
if (asset2) {
|
|
1935
|
+
asset2.key = key;
|
|
1621
1936
|
}
|
|
1622
|
-
return
|
|
1937
|
+
return asset2;
|
|
1623
1938
|
});
|
|
1624
1939
|
}
|
|
1625
|
-
|
|
1940
|
+
/**
|
|
1941
|
+
* Loads a model and resolves its parent chain. Returns `undefined` when the model is missing.
|
|
1942
|
+
* Item keys default to GUI context and a stack count of 1. Unresolved conditions are false and numeric properties are zero.
|
|
1943
|
+
* Pass `context` to supply component values, property overrides, and item references for a preview.
|
|
1944
|
+
* Composite items retain independently merged children in `ItemModel.parts`.
|
|
1945
|
+
*
|
|
1946
|
+
* @param key - Model key, for example `AssetKey.parse("models", "minecraft:item/diamond_sword")`.
|
|
1947
|
+
*/
|
|
1948
|
+
static async getMerged(key, context = {}) {
|
|
1626
1949
|
if (!key.assetType) {
|
|
1627
1950
|
key.assetType = "models";
|
|
1628
1951
|
}
|
|
1629
1952
|
if (!key.extension) {
|
|
1630
1953
|
key.extension = ".json";
|
|
1631
1954
|
}
|
|
1632
|
-
const
|
|
1955
|
+
const preview = this.snapshotContext(key, context);
|
|
1956
|
+
const keyStr = key.serialize() + (key.type === "item" ? this.contextKey(preview) : "");
|
|
1633
1957
|
return Caching.mergedModelCache.get(keyStr, (k) => {
|
|
1634
|
-
return _Models.loadAndMerge(key);
|
|
1958
|
+
return _Models.loadAndMerge(key, preview);
|
|
1635
1959
|
});
|
|
1636
1960
|
}
|
|
1637
|
-
|
|
1638
|
-
|
|
1961
|
+
/** Alias for {@link getMerged}. */
|
|
1962
|
+
static async get(key, context = {}) {
|
|
1963
|
+
return this.getMerged(key, context);
|
|
1639
1964
|
}
|
|
1965
|
+
/** Clears persisted models. Use {@link Caching.clear} to also discard in-memory assets. */
|
|
1640
1966
|
static async clearCache() {
|
|
1641
1967
|
return this.PERSISTENT_CACHE.clear();
|
|
1642
1968
|
}
|
|
@@ -1659,6 +1985,7 @@ var init_BlockStates = __esm({
|
|
|
1659
1985
|
return this._persistentCache ??= PersistentCache.open("minerender-blockstates");
|
|
1660
1986
|
}
|
|
1661
1987
|
// BlockState names are hardcoded
|
|
1988
|
+
/** Returns blockstate filenames from the selected version's `_list.json`, or an empty list. */
|
|
1662
1989
|
static async getList() {
|
|
1663
1990
|
const key = new AssetKey(
|
|
1664
1991
|
DEFAULT_NAMESPACE,
|
|
@@ -1679,6 +2006,7 @@ var init_BlockStates = __esm({
|
|
|
1679
2006
|
return AssetLoader.get(key, "json" /* JSON */);
|
|
1680
2007
|
});
|
|
1681
2008
|
}
|
|
2009
|
+
/** Looks up vanilla property definitions for the key's block path, or returns `undefined`. */
|
|
1682
2010
|
static async getDefaultState(key) {
|
|
1683
2011
|
const defaultStates = await this.getDefaultStates();
|
|
1684
2012
|
if (!defaultStates) {
|
|
@@ -1686,6 +2014,7 @@ var init_BlockStates = __esm({
|
|
|
1686
2014
|
}
|
|
1687
2015
|
return defaultStates["minecraft:" + key.path];
|
|
1688
2016
|
}
|
|
2017
|
+
/** Loads a cached blockstate definition for {@link MineRenderScene.addBlock}, or returns `undefined`. */
|
|
1689
2018
|
static async get(key) {
|
|
1690
2019
|
if (!key.assetType) {
|
|
1691
2020
|
key.assetType = "blockstates";
|
|
@@ -1698,11 +2027,11 @@ var init_BlockStates = __esm({
|
|
|
1698
2027
|
return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
|
|
1699
2028
|
return AssetLoader.get(key, "blockstate" /* BLOCKSTATE */);
|
|
1700
2029
|
});
|
|
1701
|
-
}).then((
|
|
1702
|
-
if (
|
|
1703
|
-
|
|
2030
|
+
}).then((asset2) => {
|
|
2031
|
+
if (asset2) {
|
|
2032
|
+
asset2.key = key;
|
|
1704
2033
|
}
|
|
1705
|
-
return
|
|
2034
|
+
return asset2;
|
|
1706
2035
|
});
|
|
1707
2036
|
}
|
|
1708
2037
|
static getAll(keys) {
|
|
@@ -1712,6 +2041,7 @@ var init_BlockStates = __esm({
|
|
|
1712
2041
|
}
|
|
1713
2042
|
return Promise.all(promises);
|
|
1714
2043
|
}
|
|
2044
|
+
/** Clears persisted blockstates. Use {@link Caching.clear} to also discard in-memory assets. */
|
|
1715
2045
|
static async clearCache() {
|
|
1716
2046
|
await this.PERSISTENT_CACHE.clear();
|
|
1717
2047
|
}
|
|
@@ -1737,9 +2067,11 @@ var init_Assets = __esm({
|
|
|
1737
2067
|
static parseAssetKey(assetType, str, origin) {
|
|
1738
2068
|
return AssetKey.parse(assetType, str, origin);
|
|
1739
2069
|
}
|
|
2070
|
+
/** Loads a model with its parent chain resolved. See {@link Models.getMerged}. */
|
|
1740
2071
|
static async getModel(key) {
|
|
1741
2072
|
return Models.getMerged(key);
|
|
1742
2073
|
}
|
|
2074
|
+
/** Loads a blockstate definition. See {@link BlockStates.get}. */
|
|
1743
2075
|
static async getBlockState(key) {
|
|
1744
2076
|
return BlockStates.get(key);
|
|
1745
2077
|
}
|
|
@@ -1754,6 +2086,10 @@ var init_AssetSource = __esm({
|
|
|
1754
2086
|
AssetSource = class {
|
|
1755
2087
|
constructor() {
|
|
1756
2088
|
}
|
|
2089
|
+
/** Whether this source prevents lower-priority sources from supplying the key. Its own assets remain available. */
|
|
2090
|
+
blocks(key) {
|
|
2091
|
+
return false;
|
|
2092
|
+
}
|
|
1757
2093
|
/**
|
|
1758
2094
|
* Identifies this source's content across sessions for persistent caching. Sources that
|
|
1759
2095
|
* cannot tell whether their content changed (an unnamed archive, for example) return
|
|
@@ -1782,6 +2118,10 @@ var init_BatchedExecutor = __esm({
|
|
|
1782
2118
|
"src/util/BatchedExecutor.ts"() {
|
|
1783
2119
|
import_jobqu2 = require("jobqu");
|
|
1784
2120
|
BatchedExecutor = class {
|
|
2121
|
+
/**
|
|
2122
|
+
* @param interval - Delay between queue runs, in milliseconds.
|
|
2123
|
+
* @param batch - Maximum tasks started per run and maximum tasks active at once.
|
|
2124
|
+
*/
|
|
1785
2125
|
constructor(interval = 1, batch = 30) {
|
|
1786
2126
|
if (!Number.isFinite(interval) || interval < 0) throw new RangeError("Batch interval must be finite and nonnegative");
|
|
1787
2127
|
if (!Number.isInteger(batch) || batch < 1) throw new RangeError("Batch size must be a positive integer");
|
|
@@ -1789,9 +2129,11 @@ var init_BatchedExecutor = __esm({
|
|
|
1789
2129
|
this.batch = batch;
|
|
1790
2130
|
this.queue = new import_jobqu2.JobQueue(async (task) => task(), { interval, maxPerRun: batch, maxActive: batch });
|
|
1791
2131
|
}
|
|
2132
|
+
/** Queues a task and resolves with its result, or rejects with its error. */
|
|
1792
2133
|
submit(task) {
|
|
1793
2134
|
return this.queue.add(() => task());
|
|
1794
2135
|
}
|
|
2136
|
+
/** Rejects queued and future tasks. Tasks already running are allowed to finish. */
|
|
1795
2137
|
stop() {
|
|
1796
2138
|
this.queue.end();
|
|
1797
2139
|
}
|
|
@@ -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 import_three8.Vector3();
|
|
1843
2185
|
box.getSize(size);
|
|
1844
2186
|
let boxGeo = new import_three8.BoxGeometry(size.x, size.y, size.z, 1, 1, 1);
|
|
1845
2187
|
let wireGeo = new import_three8.EdgesGeometry(boxGeo);
|
|
1846
|
-
let wireMat = new import_three8.LineBasicMaterial({ color, linewidth: w });
|
|
2188
|
+
let wireMat = new import_three8.LineBasicMaterial({ color: color2, linewidth: w });
|
|
1847
2189
|
let wireframe = new import_three8.LineSegments(wireGeo, wireMat);
|
|
1848
2190
|
obj.add(wireframe);
|
|
1849
2191
|
let axes = new import_three8.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 import_three8.BoxGeometry(16, 16, 16, 1, 1, 1);
|
|
1854
2196
|
let wireGeo = new import_three8.EdgesGeometry(boxGeo);
|
|
1855
|
-
let wireMat = new import_three8.LineBasicMaterial({ color, linewidth: w });
|
|
2197
|
+
let wireMat = new import_three8.LineBasicMaterial({ color: color2, linewidth: w });
|
|
1856
2198
|
let wireframe = new import_three8.LineSegments(wireGeo, wireMat);
|
|
1857
2199
|
obj.add(wireframe);
|
|
1858
2200
|
let axes = new import_three8.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 import_three8.EdgesGeometry(geo);
|
|
1863
|
-
let wireMat = new import_three8.LineBasicMaterial({ color, linewidth: w });
|
|
2205
|
+
let wireMat = new import_three8.LineBasicMaterial({ color: color2, linewidth: w });
|
|
1864
2206
|
let wireframe = new import_three8.LineSegments(wireGeo, wireMat);
|
|
1865
2207
|
mesh.add(wireframe);
|
|
1866
2208
|
let axes = new import_three8.AxesHelper(1);
|
|
@@ -1934,10 +2276,11 @@ var init_NBTHelper = __esm({
|
|
|
1934
2276
|
"src/nbt/NBTHelper.ts"() {
|
|
1935
2277
|
import_buffer2 = require("buffer");
|
|
1936
2278
|
NBTHelper = class {
|
|
1937
|
-
|
|
2279
|
+
/** Parses NBT bytes and preserves format and compression metadata. Omit `format` for automatic detection. */
|
|
2280
|
+
static async fromBuffer(data, format2) {
|
|
1938
2281
|
const buffer = import_buffer2.Buffer.from(data instanceof Uint8Array ? data : new Uint8Array(data));
|
|
1939
2282
|
const prismarineNbt = await import("prismarine-nbt");
|
|
1940
|
-
const { parsed, type, metadata } = await prismarineNbt.parse(buffer,
|
|
2283
|
+
const { parsed, type, metadata } = await prismarineNbt.parse(buffer, format2);
|
|
1941
2284
|
return Object.assign(parsed, {
|
|
1942
2285
|
format: type,
|
|
1943
2286
|
metadata,
|
|
@@ -2078,6 +2421,7 @@ var init_HostedAssetSource = __esm({
|
|
|
2078
2421
|
["json" /* JSON */, _HostedAssetSource.JSON]
|
|
2079
2422
|
]);
|
|
2080
2423
|
}
|
|
2424
|
+
/** Retries missing assets with the `minecraft` namespace and default root. Other failures reject. */
|
|
2081
2425
|
async loadOrRetryWithDefaults(key, parser) {
|
|
2082
2426
|
const direct = await this.load(key, parser);
|
|
2083
2427
|
if (typeof direct !== "undefined") {
|
|
@@ -2135,6 +2479,10 @@ var init_HostedAssetSource = __esm({
|
|
|
2135
2479
|
assetBasePath(key) {
|
|
2136
2480
|
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 + "/" : ""}`;
|
|
2137
2481
|
}
|
|
2482
|
+
/**
|
|
2483
|
+
* Creates a hosted source for {@link AssetLoader.addSource}.
|
|
2484
|
+
* Set `options.retryDefaults` to `false` to restrict lookups to the requested namespace and root.
|
|
2485
|
+
*/
|
|
2138
2486
|
constructor(root, options) {
|
|
2139
2487
|
super();
|
|
2140
2488
|
this._root = root;
|
|
@@ -2166,6 +2514,11 @@ var init_BrowserArchiveProxy = __esm({
|
|
|
2166
2514
|
"src/assets/source/archive/BrowserArchiveProxy.ts"() {
|
|
2167
2515
|
import_zip = require("@zip.js/zip.js");
|
|
2168
2516
|
BrowserArchiveProxy = class {
|
|
2517
|
+
/**
|
|
2518
|
+
* Opens a ZIP for asset lookup.
|
|
2519
|
+
* @param id - Cache identity for this content. Change it when the pack changes.
|
|
2520
|
+
* A File supplies an identity from its name, size, and modification time when omitted.
|
|
2521
|
+
*/
|
|
2169
2522
|
constructor(blob, id) {
|
|
2170
2523
|
this._blob = blob;
|
|
2171
2524
|
this._reader = new import_zip.ZipReader(new import_zip.BlobReader(this._blob));
|
|
@@ -2186,6 +2539,164 @@ var init_BrowserArchiveProxy = __esm({
|
|
|
2186
2539
|
}
|
|
2187
2540
|
});
|
|
2188
2541
|
|
|
2542
|
+
// src/assets/source/archive/PackMetadata.ts
|
|
2543
|
+
function object(value, field) {
|
|
2544
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
2545
|
+
throw new Error(`Invalid ${field}: expected an object`);
|
|
2546
|
+
}
|
|
2547
|
+
return value;
|
|
2548
|
+
}
|
|
2549
|
+
function integer(value, field) {
|
|
2550
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
2551
|
+
throw new Error(`Invalid ${field}: expected a nonnegative integer`);
|
|
2552
|
+
}
|
|
2553
|
+
return value;
|
|
2554
|
+
}
|
|
2555
|
+
function format(value, field, upper = false) {
|
|
2556
|
+
const parts = Array.isArray(value) ? value : [value];
|
|
2557
|
+
if (parts.length < 1 || parts.length > 2) {
|
|
2558
|
+
throw new Error(`Invalid ${field}: expected a major version or [major, minor]`);
|
|
2559
|
+
}
|
|
2560
|
+
return [integer(parts[0], field), parts.length === 2 ? integer(parts[1], field) : upper ? Infinity : 0];
|
|
2561
|
+
}
|
|
2562
|
+
function compare(a, b) {
|
|
2563
|
+
return a[0] === b[0] ? a[1] - b[1] : a[0] - b[0];
|
|
2564
|
+
}
|
|
2565
|
+
function overlay(value, field) {
|
|
2566
|
+
const entry = object(value, field);
|
|
2567
|
+
const directory = entry.directory;
|
|
2568
|
+
if (typeof directory !== "string" || !directory || /[^-_a-zA-Z0-9.]/.test(directory) || directory === "." || directory === "..") {
|
|
2569
|
+
throw new Error(`Invalid ${field}.directory: expected a directory name without path separators`);
|
|
2570
|
+
}
|
|
2571
|
+
let min, max;
|
|
2572
|
+
if ("min_format" in entry || "max_format" in entry) {
|
|
2573
|
+
min = format(entry.min_format, `${field}.min_format`);
|
|
2574
|
+
max = format(entry.max_format, `${field}.max_format`, true);
|
|
2575
|
+
} else {
|
|
2576
|
+
const range = entry.formats;
|
|
2577
|
+
let lower, upper;
|
|
2578
|
+
if (typeof range === "number") {
|
|
2579
|
+
lower = upper = range;
|
|
2580
|
+
} else if (Array.isArray(range)) {
|
|
2581
|
+
if (range.length !== 2) throw new Error(`Invalid ${field}.formats: expected [min, max]`);
|
|
2582
|
+
[lower, upper] = range;
|
|
2583
|
+
} else {
|
|
2584
|
+
const bounds = object(range, `${field}.formats`);
|
|
2585
|
+
lower = bounds.min_inclusive;
|
|
2586
|
+
upper = bounds.max_inclusive;
|
|
2587
|
+
}
|
|
2588
|
+
min = [integer(lower, `${field}.formats.min_inclusive`), 0];
|
|
2589
|
+
max = [integer(upper, `${field}.formats.max_inclusive`), Infinity];
|
|
2590
|
+
}
|
|
2591
|
+
if (compare(min, max) > 0) throw new Error(`Invalid ${field}: minimum format exceeds maximum format`);
|
|
2592
|
+
return { directory, min, max };
|
|
2593
|
+
}
|
|
2594
|
+
function pattern(value, field) {
|
|
2595
|
+
if (value === void 0) return void 0;
|
|
2596
|
+
if (typeof value !== "string") throw new Error(`Invalid ${field}: expected a regular expression string`);
|
|
2597
|
+
try {
|
|
2598
|
+
return new RegExp(value);
|
|
2599
|
+
} catch (cause) {
|
|
2600
|
+
throw new Error(`Invalid ${field}: ${String(cause)}`);
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
var PackMetadata;
|
|
2604
|
+
var init_PackMetadata = __esm({
|
|
2605
|
+
"src/assets/source/archive/PackMetadata.ts"() {
|
|
2606
|
+
PackMetadata = class _PackMetadata {
|
|
2607
|
+
constructor(overlays, filters) {
|
|
2608
|
+
this.overlays = overlays;
|
|
2609
|
+
this.filters = filters;
|
|
2610
|
+
this.hasOverlays = overlays.length > 0;
|
|
2611
|
+
}
|
|
2612
|
+
static parse(value) {
|
|
2613
|
+
const metadata = object(value, "pack.mcmeta");
|
|
2614
|
+
let overlays = [];
|
|
2615
|
+
if ("overlays" in metadata) {
|
|
2616
|
+
const entries = object(metadata.overlays, "overlays").entries;
|
|
2617
|
+
if (!Array.isArray(entries)) throw new Error("Invalid overlays.entries: expected an array");
|
|
2618
|
+
overlays = entries.map((entry, index) => overlay(entry, `overlays.entries[${index}]`));
|
|
2619
|
+
}
|
|
2620
|
+
let filters = [];
|
|
2621
|
+
if ("filter" in metadata) {
|
|
2622
|
+
const entries = object(metadata.filter, "filter").block;
|
|
2623
|
+
if (!Array.isArray(entries)) throw new Error("Invalid filter.block: expected an array");
|
|
2624
|
+
filters = entries.map((value2, index) => {
|
|
2625
|
+
const field = `filter.block[${index}]`;
|
|
2626
|
+
const entry = object(value2, field);
|
|
2627
|
+
return { namespace: pattern(entry.namespace, `${field}.namespace`), path: pattern(entry.path, `${field}.path`) };
|
|
2628
|
+
});
|
|
2629
|
+
}
|
|
2630
|
+
return new _PackMetadata(overlays, filters);
|
|
2631
|
+
}
|
|
2632
|
+
/** Returns applicable directories in lookup order, with the last declared overlay first. */
|
|
2633
|
+
overlayDirectories(target) {
|
|
2634
|
+
const selected = format(target, "target format");
|
|
2635
|
+
return this.overlays.filter((entry) => compare(selected, entry.min) >= 0 && compare(selected, entry.max) <= 0).map((entry) => entry.directory).reverse();
|
|
2636
|
+
}
|
|
2637
|
+
blocks(namespace, path) {
|
|
2638
|
+
return this.filters.some((entry) => entry.namespace?.test(namespace) ?? true) && this.filters.some((entry) => entry.path?.test(path) ?? true);
|
|
2639
|
+
}
|
|
2640
|
+
};
|
|
2641
|
+
}
|
|
2642
|
+
});
|
|
2643
|
+
|
|
2644
|
+
// src/assets/source/archive/PackFormats.ts
|
|
2645
|
+
var PackFormats;
|
|
2646
|
+
var init_PackFormats = __esm({
|
|
2647
|
+
"src/assets/source/archive/PackFormats.ts"() {
|
|
2648
|
+
init_Requests();
|
|
2649
|
+
PackFormats = class {
|
|
2650
|
+
static {
|
|
2651
|
+
this.versions = /* @__PURE__ */ new Map();
|
|
2652
|
+
}
|
|
2653
|
+
/** Resolves pack formats from Mojang's client metadata hosted on assets.mcasset.cloud. */
|
|
2654
|
+
static async get(version2, type) {
|
|
2655
|
+
let pending = this.versions.get(version2);
|
|
2656
|
+
if (!pending || pending.expires <= Date.now()) {
|
|
2657
|
+
pending = {
|
|
2658
|
+
formats: this.load(version2),
|
|
2659
|
+
expires: ["latest", "release", "snapshot"].includes(version2) ? Date.now() + 3e5 : Infinity
|
|
2660
|
+
};
|
|
2661
|
+
this.versions.set(version2, pending);
|
|
2662
|
+
}
|
|
2663
|
+
try {
|
|
2664
|
+
return (await pending.formats)[type];
|
|
2665
|
+
} catch (cause) {
|
|
2666
|
+
if (this.versions.get(version2) === pending) this.versions.delete(version2);
|
|
2667
|
+
const option = type === "assets" ? "resourcePackFormat" : "dataPackFormat";
|
|
2668
|
+
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
2669
|
+
throw new Error(`Could not resolve the pack format for Minecraft ${version2}. Set ArchiveAssetSource ${option} explicitly. ${reason}`);
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
static async load(version2) {
|
|
2673
|
+
const response = await Requests.genericRequest({
|
|
2674
|
+
url: `https://assets.mcasset.cloud/${encodeURIComponent(version2)}/game-version.json`,
|
|
2675
|
+
responseType: "json"
|
|
2676
|
+
});
|
|
2677
|
+
const data = response.data;
|
|
2678
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
2679
|
+
throw new Error("Expected version metadata to be an object");
|
|
2680
|
+
}
|
|
2681
|
+
const packs = data.pack_version;
|
|
2682
|
+
if (typeof packs !== "number" && (!packs || typeof packs !== "object" || Array.isArray(packs))) {
|
|
2683
|
+
throw new Error("Expected pack_version to be a number or an object");
|
|
2684
|
+
}
|
|
2685
|
+
const format2 = (type) => {
|
|
2686
|
+
const modern = typeof packs === "object" && ["resource_major", "resource_minor", "data_major", "data_minor"].some((key) => key in packs);
|
|
2687
|
+
const major = typeof packs === "number" ? packs : packs[modern ? `${type}_major` : type];
|
|
2688
|
+
const minor = modern ? packs[`${type}_minor`] : 0;
|
|
2689
|
+
if (typeof major !== "number" || !Number.isSafeInteger(major) || major < 0 || typeof minor !== "number" || !Number.isSafeInteger(minor) || minor < 0) {
|
|
2690
|
+
throw new Error(`Invalid ${type} pack format in version metadata`);
|
|
2691
|
+
}
|
|
2692
|
+
return Object.freeze([major, minor]);
|
|
2693
|
+
};
|
|
2694
|
+
return { assets: format2("resource"), data: format2("data") };
|
|
2695
|
+
}
|
|
2696
|
+
};
|
|
2697
|
+
}
|
|
2698
|
+
});
|
|
2699
|
+
|
|
2189
2700
|
// src/assets/source/archive/ArchiveAssetSource.ts
|
|
2190
2701
|
var ArchiveAssetSource;
|
|
2191
2702
|
var init_ArchiveAssetSource = __esm({
|
|
@@ -2194,35 +2705,78 @@ var init_ArchiveAssetSource = __esm({
|
|
|
2194
2705
|
init_request();
|
|
2195
2706
|
init_HostedAssetSource();
|
|
2196
2707
|
init_BrowserArchiveProxy();
|
|
2708
|
+
init_AssetLoader();
|
|
2709
|
+
init_PackMetadata();
|
|
2710
|
+
init_PackFormats();
|
|
2197
2711
|
ArchiveAssetSource = class _ArchiveAssetSource extends AssetSource {
|
|
2198
|
-
constructor(archiveProxy) {
|
|
2712
|
+
constructor(archiveProxy, options = {}) {
|
|
2199
2713
|
super();
|
|
2200
|
-
console.log(archiveProxy);
|
|
2201
2714
|
this._archiveProxy = archiveProxy;
|
|
2715
|
+
const copyFormat = (format2) => typeof format2 === "object" ? [format2[0], format2[1]] : format2;
|
|
2716
|
+
this.options = {
|
|
2717
|
+
resourcePackFormat: copyFormat(options.resourcePackFormat),
|
|
2718
|
+
dataPackFormat: copyFormat(options.dataPackFormat)
|
|
2719
|
+
};
|
|
2202
2720
|
}
|
|
2203
|
-
|
|
2204
|
-
|
|
2721
|
+
/** Creates a browser ZIP source from a Blob or File. */
|
|
2722
|
+
static blob(blob, options) {
|
|
2723
|
+
return new _ArchiveAssetSource(new BrowserArchiveProxy(blob), options);
|
|
2205
2724
|
}
|
|
2206
2725
|
get cacheId() {
|
|
2207
2726
|
const id = this._archiveProxy.id;
|
|
2208
|
-
|
|
2727
|
+
const formats = [this.options.resourcePackFormat ?? AssetLoader.version, this.options.dataPackFormat ?? AssetLoader.version];
|
|
2728
|
+
return id === void 0 ? void 0 : `archive-metadata:1:${id}:${JSON.stringify(formats)}`;
|
|
2209
2729
|
}
|
|
2210
2730
|
async getEntries() {
|
|
2211
|
-
return this._archiveProxy.getEntries()
|
|
2731
|
+
return this.entries ??= this._archiveProxy.getEntries().catch((error) => {
|
|
2732
|
+
this.entries = void 0;
|
|
2733
|
+
throw error;
|
|
2734
|
+
});
|
|
2212
2735
|
}
|
|
2213
2736
|
async getEntry(path) {
|
|
2214
2737
|
const entries = await this.getEntries();
|
|
2215
2738
|
return entries.find((e) => e.filename === path);
|
|
2216
2739
|
}
|
|
2740
|
+
async getMetadata(key) {
|
|
2741
|
+
try {
|
|
2742
|
+
return await (this.metadata ??= this.getEntry("pack.mcmeta").then(async (entry) => PackMetadata.parse(entry ? JSON.parse(await (await entry.getData()).text()) : {})).catch((error) => {
|
|
2743
|
+
this.metadata = void 0;
|
|
2744
|
+
throw error;
|
|
2745
|
+
}));
|
|
2746
|
+
} catch (cause) {
|
|
2747
|
+
throw new AssetLoadError(this, key, "pack.mcmeta", cause);
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
async blocks(key) {
|
|
2751
|
+
if (key.rootType !== "assets" && key.rootType !== "data") return false;
|
|
2752
|
+
const metadata = await this.getMetadata(key);
|
|
2753
|
+
const path = `${key.assetType !== void 0 ? key.assetType + "/" : ""}${key.getFullPath()}${key.extension}`;
|
|
2754
|
+
return metadata.blocks(key.namespace, path);
|
|
2755
|
+
}
|
|
2217
2756
|
async get(key, parser) {
|
|
2218
2757
|
const responseParser = HostedAssetSource.PARSER_MAP.get(parser);
|
|
2219
2758
|
return this.load(key, responseParser);
|
|
2220
2759
|
}
|
|
2221
2760
|
async load(key, parser) {
|
|
2222
2761
|
const path = `${this.assetBasePath(key)}${key.type !== void 0 ? key.type + "/" : ""}${key.path}${key.extension}`;
|
|
2762
|
+
const version2 = AssetLoader.version;
|
|
2763
|
+
let selectedPath = path;
|
|
2223
2764
|
let url;
|
|
2224
2765
|
try {
|
|
2225
|
-
|
|
2766
|
+
let directories = [];
|
|
2767
|
+
if (key.rootType === "assets" || key.rootType === "data") {
|
|
2768
|
+
const metadata = await this.getMetadata(key);
|
|
2769
|
+
if (metadata.hasOverlays) {
|
|
2770
|
+
const format2 = (key.rootType === "assets" ? this.options.resourcePackFormat : this.options.dataPackFormat) ?? await PackFormats.get(version2, key.rootType);
|
|
2771
|
+
directories = metadata.overlayDirectories(format2);
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
let entry;
|
|
2775
|
+
for (const directory of [...directories, ""]) {
|
|
2776
|
+
selectedPath = directory ? `${directory}/${path}` : path;
|
|
2777
|
+
entry = await this.getEntry(selectedPath);
|
|
2778
|
+
if (entry) break;
|
|
2779
|
+
}
|
|
2226
2780
|
if (!entry) {
|
|
2227
2781
|
return void 0;
|
|
2228
2782
|
}
|
|
@@ -2235,7 +2789,8 @@ var init_ArchiveAssetSource = __esm({
|
|
|
2235
2789
|
}
|
|
2236
2790
|
return await parser.parse(response);
|
|
2237
2791
|
} catch (cause) {
|
|
2238
|
-
|
|
2792
|
+
if (cause instanceof AssetLoadError) throw cause;
|
|
2793
|
+
throw new AssetLoadError(this, key, selectedPath, cause);
|
|
2239
2794
|
} finally {
|
|
2240
2795
|
if (url !== void 0) {
|
|
2241
2796
|
URL.revokeObjectURL(url);
|
|
@@ -2268,6 +2823,8 @@ var init_archive = __esm({
|
|
|
2268
2823
|
init_ArchiveEntry();
|
|
2269
2824
|
init_ArchiveProxy();
|
|
2270
2825
|
init_BrowserArchiveProxy();
|
|
2826
|
+
init_PackFormats();
|
|
2827
|
+
init_PackMetadata();
|
|
2271
2828
|
}
|
|
2272
2829
|
});
|
|
2273
2830
|
|
|
@@ -2294,6 +2851,7 @@ var init_AssetLoader = __esm({
|
|
|
2294
2851
|
FALLBACK_ROOT = "https://raw.githubusercontent.com/InventivetalentDev/minerender-fallback-assets/master";
|
|
2295
2852
|
AssetLoader = class {
|
|
2296
2853
|
static {
|
|
2854
|
+
/** Default asset base URL. Use {@link setVersion} to select a Minecraft version. */
|
|
2297
2855
|
this.ROOT = DEFAULT_ROOT;
|
|
2298
2856
|
}
|
|
2299
2857
|
static {
|
|
@@ -2306,8 +2864,9 @@ var init_AssetLoader = __esm({
|
|
|
2306
2864
|
static get version() {
|
|
2307
2865
|
return this.ROOT.substring(this.ROOT.lastIndexOf("/") + 1);
|
|
2308
2866
|
}
|
|
2309
|
-
|
|
2310
|
-
|
|
2867
|
+
/** Selects an mcasset.cloud version, updates the vanilla source, and clears in-memory caches. */
|
|
2868
|
+
static setVersion(version2) {
|
|
2869
|
+
this.ROOT = `https://assets.mcasset.cloud/${version2}`;
|
|
2311
2870
|
const source = new HostedAssetSource(this.ROOT, { retryDefaults: false });
|
|
2312
2871
|
const index = this._SOURCES.findIndex((s) => s.key === "mcassets");
|
|
2313
2872
|
if (index !== -1) {
|
|
@@ -2339,6 +2898,13 @@ var init_AssetLoader = __esm({
|
|
|
2339
2898
|
return scope ? `${scope}
|
|
2340
2899
|
${key}` : key;
|
|
2341
2900
|
}
|
|
2901
|
+
/**
|
|
2902
|
+
* Registers a source at the highest priority.
|
|
2903
|
+
* Call {@link Caching.clear} after changing sources to discard previously loaded assets.
|
|
2904
|
+
*
|
|
2905
|
+
* @param key - Name used to replace or remove this source.
|
|
2906
|
+
* @param override - Removes the first source with this name before adding the new one.
|
|
2907
|
+
*/
|
|
2342
2908
|
static addSource(key, source, override = true) {
|
|
2343
2909
|
if (override) {
|
|
2344
2910
|
const existing = this.removeSource(key);
|
|
@@ -2349,6 +2915,7 @@ ${key}` : key;
|
|
|
2349
2915
|
this._SOURCES.unshift({ key, source });
|
|
2350
2916
|
console.log(p4, "Added AssetSource", key);
|
|
2351
2917
|
}
|
|
2918
|
+
/** Removes and returns the first source with this name. Call {@link Caching.clear} to reload assets. */
|
|
2352
2919
|
static removeSource(key) {
|
|
2353
2920
|
const index = this._SOURCES.findIndex((s) => s.key === key);
|
|
2354
2921
|
if (index != -1) {
|
|
@@ -2363,14 +2930,16 @@ ${key}` : key;
|
|
|
2363
2930
|
this.addSource("mcassets-fallback", new HostedAssetSource(FALLBACK_ROOT, { retryDefaults: false }));
|
|
2364
2931
|
this.addSource("mcassets", new HostedAssetSource(this.ROOT, { retryDefaults: false }));
|
|
2365
2932
|
}
|
|
2933
|
+
/** Loads from all sources and returns defined results in priority order. Any source failure rejects the call. */
|
|
2366
2934
|
static async getAll(key, parser) {
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2935
|
+
const sources = [...this._SOURCES];
|
|
2936
|
+
const results = [];
|
|
2937
|
+
for (const { source } of sources) {
|
|
2938
|
+
const asset2 = await source.get(key, parser);
|
|
2939
|
+
if (asset2 != void 0) results.push(asset2);
|
|
2940
|
+
if (await source.blocks(key)) break;
|
|
2370
2941
|
}
|
|
2371
|
-
return
|
|
2372
|
-
return results.filter((r) => r != void 0);
|
|
2373
|
-
});
|
|
2942
|
+
return results;
|
|
2374
2943
|
}
|
|
2375
2944
|
/** Returns the first defined result in source-priority order, without merging assets. */
|
|
2376
2945
|
static async get(key, parser) {
|
|
@@ -2391,13 +2960,17 @@ ${key}` : key;
|
|
|
2391
2960
|
/** Tries each path within a source before considering lower-priority sources. */
|
|
2392
2961
|
static async getFirst(keys, parser) {
|
|
2393
2962
|
const sources = [...this._SOURCES];
|
|
2963
|
+
let remaining = [...keys];
|
|
2394
2964
|
for (const source of sources) {
|
|
2395
|
-
for (const key of
|
|
2965
|
+
for (const key of remaining) {
|
|
2396
2966
|
const result = await source.source.get(key, parser);
|
|
2397
2967
|
if (result !== void 0) {
|
|
2398
2968
|
return { key, asset: result };
|
|
2399
2969
|
}
|
|
2400
2970
|
}
|
|
2971
|
+
const blocked = await Promise.all(remaining.map((key) => source.source.blocks(key)));
|
|
2972
|
+
remaining = remaining.filter((_, index) => !blocked[index]);
|
|
2973
|
+
if (!remaining.length) break;
|
|
2401
2974
|
}
|
|
2402
2975
|
return void 0;
|
|
2403
2976
|
}
|
|
@@ -2419,14 +2992,15 @@ var init_ModelTextures = __esm({
|
|
|
2419
2992
|
static get PERSISTENT_META_CACHE() {
|
|
2420
2993
|
return this._persistentMetaCache ??= PersistentCache.open("minerender-texturemeta");
|
|
2421
2994
|
}
|
|
2995
|
+
/** Loads and decodes a texture into a readable canvas, or returns `undefined` when missing. */
|
|
2422
2996
|
static async get(key) {
|
|
2423
2997
|
const keyStr = key.serialize();
|
|
2424
2998
|
const pending = this.preload(key);
|
|
2425
2999
|
const cached2 = Caching.textureAssetCache.getIfPresent(keyStr);
|
|
2426
3000
|
try {
|
|
2427
|
-
const
|
|
2428
|
-
if (
|
|
2429
|
-
return await ImageLoader.infoToCanvasData(
|
|
3001
|
+
const asset2 = await pending;
|
|
3002
|
+
if (asset2) {
|
|
3003
|
+
return await ImageLoader.infoToCanvasData(asset2);
|
|
2430
3004
|
}
|
|
2431
3005
|
return void 0;
|
|
2432
3006
|
} catch (err) {
|
|
@@ -2436,16 +3010,18 @@ var init_ModelTextures = __esm({
|
|
|
2436
3010
|
throw err;
|
|
2437
3011
|
}
|
|
2438
3012
|
}
|
|
3013
|
+
/** Fetches and caches encoded texture bytes without decoding the pixels. */
|
|
2439
3014
|
static async preload(key) {
|
|
2440
3015
|
const keyStr = key.serialize();
|
|
2441
3016
|
return Caching.textureAssetCache.get(keyStr, (k) => {
|
|
2442
|
-
return AssetLoader.get(key, "image" /* IMAGE */).then((
|
|
2443
|
-
if (
|
|
2444
|
-
|
|
2445
|
-
return
|
|
3017
|
+
return AssetLoader.get(key, "image" /* IMAGE */).then((asset2) => {
|
|
3018
|
+
if (asset2)
|
|
3019
|
+
asset2.key = key;
|
|
3020
|
+
return asset2;
|
|
2446
3021
|
});
|
|
2447
3022
|
});
|
|
2448
3023
|
}
|
|
3024
|
+
/** Loads a texture's `.mcmeta` file, or returns `undefined` when no metadata file exists. */
|
|
2449
3025
|
static async getMeta(key) {
|
|
2450
3026
|
if (!key.extension || !key.extension.endsWith(".mcmeta")) {
|
|
2451
3027
|
key = Object.assign(new AssetKey("", ""), key);
|
|
@@ -2454,14 +3030,15 @@ var init_ModelTextures = __esm({
|
|
|
2454
3030
|
const keyStr = key.serialize();
|
|
2455
3031
|
return Caching.textureMetaCache.get(keyStr, (k) => {
|
|
2456
3032
|
return this.PERSISTENT_META_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
|
|
2457
|
-
return AssetLoader.get(key, "meta" /* META */).then((
|
|
2458
|
-
if (
|
|
2459
|
-
|
|
2460
|
-
return
|
|
3033
|
+
return AssetLoader.get(key, "meta" /* META */).then((asset2) => {
|
|
3034
|
+
if (asset2)
|
|
3035
|
+
asset2.key = key;
|
|
3036
|
+
return asset2;
|
|
2461
3037
|
});
|
|
2462
3038
|
});
|
|
2463
3039
|
});
|
|
2464
3040
|
}
|
|
3041
|
+
/** Clears persisted texture metadata. Use {@link Caching.clear} to discard in-memory textures and metadata. */
|
|
2465
3042
|
static async clearCache() {
|
|
2466
3043
|
await this.PERSISTENT_META_CACHE.clear();
|
|
2467
3044
|
}
|
|
@@ -2549,6 +3126,7 @@ var init_Ticker = __esm({
|
|
|
2549
3126
|
}
|
|
2550
3127
|
return handle;
|
|
2551
3128
|
}
|
|
3129
|
+
/** Starts or resumes the shared tick loop. Repeated calls have no effect. */
|
|
2552
3130
|
static start() {
|
|
2553
3131
|
if (this.interval) return;
|
|
2554
3132
|
this.interval = this.schedule(() => {
|
|
@@ -2571,6 +3149,7 @@ var init_Ticker = __esm({
|
|
|
2571
3149
|
_Ticker.fiveSecondTicks = 0;
|
|
2572
3150
|
}, 1e3 * 5);
|
|
2573
3151
|
}
|
|
3152
|
+
/** Pauses the loop while retaining callbacks for a later {@link start}. */
|
|
2574
3153
|
static stop() {
|
|
2575
3154
|
if (this.interval) clearInterval(this.interval);
|
|
2576
3155
|
if (this.oneSecondTicksTracker) clearInterval(this.oneSecondTicksTracker);
|
|
@@ -2579,12 +3158,14 @@ var init_Ticker = __esm({
|
|
|
2579
3158
|
this.oneSecondTicksTracker = void 0;
|
|
2580
3159
|
this.fiveSecondTicksTracker = void 0;
|
|
2581
3160
|
}
|
|
3161
|
+
/** Registers a callback, starts the loop if needed, and returns an ID for {@link remove}. */
|
|
2582
3162
|
static add(tick) {
|
|
2583
3163
|
this.start();
|
|
2584
3164
|
const c = this.counter++;
|
|
2585
3165
|
this._tickers.set(c, tick);
|
|
2586
3166
|
return c;
|
|
2587
3167
|
}
|
|
3168
|
+
/** Removes a callback by ID and stops the timers when no callbacks remain. */
|
|
2588
3169
|
static remove(c) {
|
|
2589
3170
|
if (typeof c === "number") this._tickers.delete(c);
|
|
2590
3171
|
if (!this._tickers.size) this.stop();
|
|
@@ -2598,6 +3179,7 @@ var init_Ticker = __esm({
|
|
|
2598
3179
|
static get tpsFiveSeconds() {
|
|
2599
3180
|
return this.fiveSecondTps;
|
|
2600
3181
|
}
|
|
3182
|
+
/** Stops the loop and removes all registered callbacks. */
|
|
2601
3183
|
static dispose() {
|
|
2602
3184
|
this.stop();
|
|
2603
3185
|
this._tickers.clear();
|
|
@@ -2644,6 +3226,7 @@ var init_TextureAtlas = __esm({
|
|
|
2644
3226
|
}
|
|
2645
3227
|
};
|
|
2646
3228
|
}
|
|
3229
|
+
/** Copies pixels for a texture variable from its current atlas rectangle. */
|
|
2647
3230
|
getData(texture) {
|
|
2648
3231
|
const pos = this.positions[texture];
|
|
2649
3232
|
const size = this.sizes[texture];
|
|
@@ -2856,6 +3439,37 @@ var init_UVMapper = __esm({
|
|
|
2856
3439
|
static facesToUv(faces, originalTextureSize, actualTextureSize) {
|
|
2857
3440
|
return new import_three9.Float32BufferAttribute(this.facesToUvArray(faces, originalTextureSize, actualTextureSize), 2);
|
|
2858
3441
|
}
|
|
3442
|
+
/** Bounds texture sampling to texel centres without changing cropped face UVs. */
|
|
3443
|
+
static setAtlasUvBounds(geometry, faces, atlas) {
|
|
3444
|
+
const uv = geometry.getAttribute("uv");
|
|
3445
|
+
const bounds = new Float32Array(uv.count * 4);
|
|
3446
|
+
for (const [faceIndex, faceName] of CUBE_FACES.entries()) {
|
|
3447
|
+
const texture = faces[faceName]?.texture?.substring(1);
|
|
3448
|
+
const position = texture && atlas.positions[texture];
|
|
3449
|
+
const size = texture && atlas.sizes[texture];
|
|
3450
|
+
let minU = Infinity, minV = Infinity, maxU = -Infinity, maxV = -Infinity;
|
|
3451
|
+
if (position && size) {
|
|
3452
|
+
minU = position[0] / atlas.image.width;
|
|
3453
|
+
maxU = (position[0] + size[0]) / atlas.image.width;
|
|
3454
|
+
minV = 1 - (position[1] + size[1]) / atlas.image.height;
|
|
3455
|
+
maxV = 1 - position[1] / atlas.image.height;
|
|
3456
|
+
} else {
|
|
3457
|
+
for (let corner = 0; corner < 4; corner++) {
|
|
3458
|
+
const index = faceIndex * 4 + corner;
|
|
3459
|
+
minU = Math.min(minU, uv.getX(index));
|
|
3460
|
+
minV = Math.min(minV, uv.getY(index));
|
|
3461
|
+
maxU = Math.max(maxU, uv.getX(index));
|
|
3462
|
+
maxV = Math.max(maxV, uv.getY(index));
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
const insetU = Math.min(0.5 / atlas.image.width, (maxU - minU) / 2);
|
|
3466
|
+
const insetV = Math.min(0.5 / atlas.image.height, (maxV - minV) / 2);
|
|
3467
|
+
for (let corner = 0; corner < 4; corner++) {
|
|
3468
|
+
bounds.set([minU + insetU, minV + insetV, maxU - insetU, maxV - insetV], (faceIndex * 4 + corner) * 4);
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
geometry.setAttribute("uvBounds", new import_three9.Float32BufferAttribute(bounds, 4));
|
|
3472
|
+
}
|
|
2859
3473
|
static lockUvs(geometry, faces, atlas, rotation) {
|
|
2860
3474
|
const uv = geometry.getAttribute("uv");
|
|
2861
3475
|
for (let faceIndex = 0; faceIndex < CUBE_FACES.length; faceIndex++) {
|
|
@@ -2892,6 +3506,7 @@ var init_UVMapper = __esm({
|
|
|
2892
3506
|
}
|
|
2893
3507
|
uv.needsUpdate = true;
|
|
2894
3508
|
}
|
|
3509
|
+
/** Returns the shared atlas for a keyed, merged model, creating it on first use. */
|
|
2895
3510
|
static async getAtlas(model) {
|
|
2896
3511
|
const keyStr = model.key.serialize();
|
|
2897
3512
|
return Caching.modelTextureAtlasCache.get(keyStr, (k) => {
|
|
@@ -2962,6 +3577,11 @@ var init_UVMapper = __esm({
|
|
|
2962
3577
|
}
|
|
2963
3578
|
return [tl, tr, bl, br];
|
|
2964
3579
|
}
|
|
3580
|
+
/**
|
|
3581
|
+
* Combines model textures and animation metadata into an atlas.
|
|
3582
|
+
* Mapped UVs are stored on copies of the model's elements, available through the returned atlas.
|
|
3583
|
+
* Returns `undefined` for models without a texture map.
|
|
3584
|
+
*/
|
|
2965
3585
|
static async createAtlas(originalModel) {
|
|
2966
3586
|
const textureMap = {};
|
|
2967
3587
|
const metaMap = {};
|
|
@@ -2982,12 +3602,12 @@ var init_UVMapper = __esm({
|
|
|
2982
3602
|
} else {
|
|
2983
3603
|
uniqueTextureNames.push(textureKey);
|
|
2984
3604
|
const assetKey = AssetKey.parse("textures", textureValue, model.key);
|
|
2985
|
-
promises.push(ModelTextures.get(assetKey).then((
|
|
2986
|
-
if (!
|
|
3605
|
+
promises.push(ModelTextures.get(assetKey).then((asset2) => {
|
|
3606
|
+
if (!asset2) {
|
|
2987
3607
|
console.warn(p5, "Missing texture", assetKey);
|
|
2988
3608
|
return;
|
|
2989
3609
|
}
|
|
2990
|
-
textureMap[textureKey] = new WrappedImage(
|
|
3610
|
+
textureMap[textureKey] = new WrappedImage(asset2);
|
|
2991
3611
|
}));
|
|
2992
3612
|
promises.push(ModelTextures.getMeta(assetKey).then((meta) => {
|
|
2993
3613
|
metaMap[textureKey] = meta;
|
|
@@ -3326,14 +3946,15 @@ var init_Entities = __esm({
|
|
|
3326
3946
|
init_MineRenderError();
|
|
3327
3947
|
init_ModelTextures();
|
|
3328
3948
|
Entities = class {
|
|
3949
|
+
/** Lists model paths in the `minecraft` namespace, including subdirectories and excluding `.json`. */
|
|
3329
3950
|
static async getEntityList() {
|
|
3330
3951
|
const collect = async (path) => {
|
|
3331
3952
|
const prefix2 = path ? `${path}/` : "";
|
|
3332
3953
|
const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json");
|
|
3333
|
-
const
|
|
3334
|
-
if (!
|
|
3335
|
-
const files =
|
|
3336
|
-
const children = await Promise.all(
|
|
3954
|
+
const list2 = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
|
|
3955
|
+
if (!list2) return [];
|
|
3956
|
+
const files = list2.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
|
|
3957
|
+
const children = await Promise.all(list2.directories.map((directory) => collect(`${prefix2}${directory}`)));
|
|
3337
3958
|
return [...files, ...children.flat()];
|
|
3338
3959
|
};
|
|
3339
3960
|
return collect("");
|
|
@@ -3343,6 +3964,7 @@ var init_Entities = __esm({
|
|
|
3343
3964
|
const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
|
|
3344
3965
|
return Caching.entityModelCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
|
|
3345
3966
|
}
|
|
3967
|
+
/** Returns selectable geometry-layer names, or an empty list when the model is missing. */
|
|
3346
3968
|
static async getLayerList(modelKey) {
|
|
3347
3969
|
return Object.keys((await this.getModelFile(modelKey))?.layers ?? {});
|
|
3348
3970
|
}
|
|
@@ -3350,6 +3972,15 @@ var init_Entities = __esm({
|
|
|
3350
3972
|
static async getPassList(modelKey) {
|
|
3351
3973
|
return (await this.getModelFile(modelKey))?.passes ?? [];
|
|
3352
3974
|
}
|
|
3975
|
+
/**
|
|
3976
|
+
* Loads the layers and textures to pass to {@link MineRenderScene.addEntity}.
|
|
3977
|
+
* By default, selects `main`, unconditional passes, and passes enabled by `options.when`.
|
|
3978
|
+
*
|
|
3979
|
+
* @param modelKey - Dataset model, such as `new BasicAssetKey("minecraft", "zombie")`.
|
|
3980
|
+
* @param textureKey - Texture override for the first selected layer.
|
|
3981
|
+
* @param options - Layer selection, conditional passes, and per-layer texture overrides.
|
|
3982
|
+
* @returns The selected model, or `undefined` when its dataset file is missing.
|
|
3983
|
+
*/
|
|
3353
3984
|
static async getEntity(modelKey, textureKey, options) {
|
|
3354
3985
|
const model = await this.getModelFile(modelKey);
|
|
3355
3986
|
if (!model) return void 0;
|
|
@@ -3395,6 +4026,7 @@ var init_Entities = __esm({
|
|
|
3395
4026
|
const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
|
|
3396
4027
|
return file?.animations;
|
|
3397
4028
|
}
|
|
4029
|
+
/** Finds a fallback texture using the model path, its directory, and vanilla variant data. */
|
|
3398
4030
|
static async resolveTexture(modelKey) {
|
|
3399
4031
|
const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
|
|
3400
4032
|
const root = isAssetKey(modelKey) ? modelKey.root : void 0;
|
|
@@ -3406,8 +4038,8 @@ var init_Entities = __esm({
|
|
|
3406
4038
|
if (await ModelTextures.get(texture2)) return texture2;
|
|
3407
4039
|
}
|
|
3408
4040
|
const listKey = new AssetKey(modelKey.namespace, "_list", `${name}_variant`, void 0, "data", ".json", root);
|
|
3409
|
-
const
|
|
3410
|
-
const files =
|
|
4041
|
+
const list2 = await AssetLoader.get(listKey, "list" /* LIST */);
|
|
4042
|
+
const files = list2?.files.filter((file2) => file2.endsWith(".json") && file2 !== "_list.json");
|
|
3411
4043
|
const file = files?.find((file2) => file2 === "temperate.json") ?? files?.[0];
|
|
3412
4044
|
if (!file) return void 0;
|
|
3413
4045
|
const variantKey = new AssetKey(modelKey.namespace, file.slice(0, -5), `${name}_variant`, void 0, "data", ".json", root);
|
|
@@ -3512,27 +4144,27 @@ var init_EntityAnimationPlayer = __esm({
|
|
|
3512
4144
|
for (const layer of layers) {
|
|
3513
4145
|
const objects = [];
|
|
3514
4146
|
if (poses) {
|
|
3515
|
-
layer.traverse((
|
|
3516
|
-
if (poses.has(
|
|
4147
|
+
layer.traverse((object3) => {
|
|
4148
|
+
if (poses.has(object3)) objects.push(object3);
|
|
3517
4149
|
});
|
|
3518
4150
|
} else {
|
|
3519
4151
|
for (const bone of Object.keys(animation.bones)) {
|
|
3520
|
-
const
|
|
3521
|
-
if (
|
|
4152
|
+
const object3 = layer.getObjectByName(`group:${bone}`);
|
|
4153
|
+
if (object3) objects.push(object3);
|
|
3522
4154
|
}
|
|
3523
4155
|
}
|
|
3524
|
-
for (const
|
|
3525
|
-
const bone =
|
|
3526
|
-
const pose = poses?.get(
|
|
4156
|
+
for (const object3 of objects) {
|
|
4157
|
+
const bone = object3.name.slice("group:".length);
|
|
4158
|
+
const pose = poses?.get(object3);
|
|
3527
4159
|
this.parts.push({
|
|
3528
4160
|
bone,
|
|
3529
|
-
object,
|
|
3530
|
-
position:
|
|
3531
|
-
rotation:
|
|
3532
|
-
scale:
|
|
3533
|
-
basePosition: pose ? new import_three11.Vector3(...pose.offset) :
|
|
3534
|
-
baseRotation: pose ? new import_three11.Euler(...pose.rotation, "ZYX") :
|
|
3535
|
-
baseScale: pose ? new import_three11.Vector3(...pose.scale ?? [1, 1, 1]) :
|
|
4161
|
+
object: object3,
|
|
4162
|
+
position: object3.position.clone(),
|
|
4163
|
+
rotation: object3.rotation.clone(),
|
|
4164
|
+
scale: object3.scale.clone(),
|
|
4165
|
+
basePosition: pose ? new import_three11.Vector3(...pose.offset) : object3.position.clone(),
|
|
4166
|
+
baseRotation: pose ? new import_three11.Euler(...pose.rotation, "ZYX") : object3.rotation.clone(),
|
|
4167
|
+
baseScale: pose ? new import_three11.Vector3(...pose.scale ?? [1, 1, 1]) : object3.scale.clone()
|
|
3536
4168
|
});
|
|
3537
4169
|
}
|
|
3538
4170
|
}
|
|
@@ -3555,12 +4187,14 @@ var init_EntityAnimationPlayer = __esm({
|
|
|
3555
4187
|
this._animation = void 0;
|
|
3556
4188
|
this._time = 0;
|
|
3557
4189
|
}
|
|
4190
|
+
/** Applies the pose at an absolute playback time. Returns `false` when no clip is active. */
|
|
3558
4191
|
setTime(seconds) {
|
|
3559
4192
|
if (!this._animation) return false;
|
|
3560
4193
|
this._time = seconds;
|
|
3561
4194
|
this.apply();
|
|
3562
4195
|
return true;
|
|
3563
4196
|
}
|
|
4197
|
+
/** Advances playback by elapsed seconds multiplied by the speed passed to {@link play}. */
|
|
3564
4198
|
advance(deltaSeconds) {
|
|
3565
4199
|
return this.setTime(this._time + deltaSeconds * this.speed);
|
|
3566
4200
|
}
|
|
@@ -3598,6 +4232,7 @@ var init_InstanceReference = __esm({
|
|
|
3598
4232
|
if (!this.instanceable.isInstanceActive(this.index, this)) throw new MineRenderError("Instance has been removed");
|
|
3599
4233
|
return this.instanceable;
|
|
3600
4234
|
}
|
|
4235
|
+
/** Releases this placement's slot for reuse. Repeated calls have no effect. */
|
|
3601
4236
|
removeFromScene() {
|
|
3602
4237
|
if (this.instanceable.isInstanceActive(this.index, this)) this.instanceable.removeInstanceAt(this.index);
|
|
3603
4238
|
}
|
|
@@ -3610,9 +4245,11 @@ var init_InstanceReference = __esm({
|
|
|
3610
4245
|
setMatrix(matrix) {
|
|
3611
4246
|
this.activeInstanceable.setMatrixAt(this.index, matrix);
|
|
3612
4247
|
}
|
|
4248
|
+
/** Hides or restores this placement while preserving its transform and allocated slot. */
|
|
3613
4249
|
setVisible(visible) {
|
|
3614
4250
|
this.activeInstanceable.setInstanceVisibleAt(this.index, visible);
|
|
3615
4251
|
}
|
|
4252
|
+
/** Updates the supplied transform components. Positions use scene units and rotations use radians. */
|
|
3616
4253
|
setPositionRotationScale(position, rotation, scale) {
|
|
3617
4254
|
this.activeInstanceable.setPositionRotationScaleAt(this.index, position, rotation, scale);
|
|
3618
4255
|
}
|
|
@@ -3689,6 +4326,7 @@ var init_SceneObject = __esm({
|
|
|
3689
4326
|
}
|
|
3690
4327
|
async init() {
|
|
3691
4328
|
}
|
|
4329
|
+
/** Marks ancestor scenes for redraw and emits a `change` event. */
|
|
3692
4330
|
notifyDirty() {
|
|
3693
4331
|
this.traverseAncestors((parent) => {
|
|
3694
4332
|
const scene = parent;
|
|
@@ -3724,15 +4362,17 @@ var init_SceneObject = __esm({
|
|
|
3724
4362
|
return obj;
|
|
3725
4363
|
}
|
|
3726
4364
|
/**
|
|
3727
|
-
*
|
|
4365
|
+
* Finds a named part, such as `head`, without the internal `group:` prefix.
|
|
4366
|
+
* Returns `undefined` when the group does not exist.
|
|
3728
4367
|
*/
|
|
3729
4368
|
getGroupByName(name) {
|
|
3730
4369
|
return this.getObjectByName(`group:${name}`);
|
|
3731
4370
|
}
|
|
3732
4371
|
/**
|
|
3733
|
-
*
|
|
3734
|
-
* @param name name
|
|
3735
|
-
* @param visible
|
|
4372
|
+
* Changes a named group's visibility and requests a redraw.
|
|
4373
|
+
* @param name - Part name without the `group:` prefix.
|
|
4374
|
+
* @param visible - Visibility to assign. Omit it to toggle the current value.
|
|
4375
|
+
* @returns The resulting visibility, or `false` if the group does not exist.
|
|
3736
4376
|
*/
|
|
3737
4377
|
toggleGroupVisibility(name, visible) {
|
|
3738
4378
|
return this.toggleObjectVisibility(this.getGroupByName(name), visible);
|
|
@@ -3783,15 +4423,16 @@ var init_SceneObject = __esm({
|
|
|
3783
4423
|
return mesh;
|
|
3784
4424
|
}
|
|
3785
4425
|
/**
|
|
3786
|
-
*
|
|
4426
|
+
* Finds a named mesh without the internal `mesh:` prefix, or returns `undefined`.
|
|
3787
4427
|
*/
|
|
3788
4428
|
getMeshByName(name) {
|
|
3789
4429
|
return this.getObjectByName(`mesh:${name}`);
|
|
3790
4430
|
}
|
|
3791
4431
|
/**
|
|
3792
|
-
*
|
|
3793
|
-
* @param name name
|
|
3794
|
-
* @param visible
|
|
4432
|
+
* Changes a named mesh's visibility and requests a redraw.
|
|
4433
|
+
* @param name - Mesh name without the `mesh:` prefix.
|
|
4434
|
+
* @param visible - Visibility to assign. Omit it to toggle the current value.
|
|
4435
|
+
* @returns The resulting visibility, or `false` if the mesh does not exist.
|
|
3795
4436
|
*/
|
|
3796
4437
|
toggleMeshVisibility(name, visible) {
|
|
3797
4438
|
return this.toggleObjectVisibility(this.getMeshByName(name), visible);
|
|
@@ -3837,12 +4478,14 @@ var init_SceneObject = __esm({
|
|
|
3837
4478
|
get isInstanced() {
|
|
3838
4479
|
return this._isInstanced;
|
|
3839
4480
|
}
|
|
4481
|
+
/** Number of live placements, excluding released instance slots. */
|
|
3840
4482
|
get instanceCounter() {
|
|
3841
4483
|
return this._instanceCounter;
|
|
3842
4484
|
}
|
|
3843
4485
|
constructInstanceReference(i) {
|
|
3844
4486
|
return new InstanceReference(this, i);
|
|
3845
4487
|
}
|
|
4488
|
+
/** Allocates a placement of this instanced object, reusing a released slot when available. */
|
|
3846
4489
|
nextInstance() {
|
|
3847
4490
|
const mesh = this.instanceMesh;
|
|
3848
4491
|
if (!mesh) throw new MineRenderError("Object is not instanced");
|
|
@@ -3887,10 +4530,10 @@ var init_SceneObject = __esm({
|
|
|
3887
4530
|
return new import_three12.InstancedBufferAttribute(array, attribute.itemSize, attribute.normalized, attribute.meshPerAttribute).setUsage(attribute.usage);
|
|
3888
4531
|
};
|
|
3889
4532
|
const matrix = grow(mesh.instanceMatrix, 0);
|
|
3890
|
-
const
|
|
4533
|
+
const color2 = mesh.instanceColor ? grow(mesh.instanceColor, 1) : null;
|
|
3891
4534
|
mesh.dispose();
|
|
3892
4535
|
mesh.instanceMatrix = matrix;
|
|
3893
|
-
mesh.instanceColor =
|
|
4536
|
+
mesh.instanceColor = color2;
|
|
3894
4537
|
}
|
|
3895
4538
|
*activeInstanceIndices() {
|
|
3896
4539
|
if (this.instanceMesh) {
|
|
@@ -3989,6 +4632,7 @@ var init_SceneObject = __esm({
|
|
|
3989
4632
|
vector.setFromMatrixScale(matrix);
|
|
3990
4633
|
return vector;
|
|
3991
4634
|
}
|
|
4635
|
+
/** Updates supplied transform components. For a shared model, affects every live instance. */
|
|
3992
4636
|
setPositionRotationScale(position, rotation, scale) {
|
|
3993
4637
|
if (this.isInstanced) {
|
|
3994
4638
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -4007,6 +4651,7 @@ var init_SceneObject = __esm({
|
|
|
4007
4651
|
}
|
|
4008
4652
|
this.notifyDirty();
|
|
4009
4653
|
}
|
|
4654
|
+
/** Sets position in scene units. For a shared model, updates every live instance. */
|
|
4010
4655
|
setPosition(position) {
|
|
4011
4656
|
if (this.isInstanced) {
|
|
4012
4657
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -4024,6 +4669,7 @@ var init_SceneObject = __esm({
|
|
|
4024
4669
|
return this.position;
|
|
4025
4670
|
}
|
|
4026
4671
|
}
|
|
4672
|
+
/** Sets rotation in radians. For a shared model, updates every live instance. */
|
|
4027
4673
|
setRotation(rotation) {
|
|
4028
4674
|
if (this.isInstanced) {
|
|
4029
4675
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -4041,6 +4687,7 @@ var init_SceneObject = __esm({
|
|
|
4041
4687
|
return this.rotation;
|
|
4042
4688
|
}
|
|
4043
4689
|
}
|
|
4690
|
+
/** Sets scale factors. For a shared model, updates every live instance. */
|
|
4044
4691
|
setScale(scale) {
|
|
4045
4692
|
if (this.isInstanced) {
|
|
4046
4693
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -4059,15 +4706,15 @@ var init_SceneObject = __esm({
|
|
|
4059
4706
|
}
|
|
4060
4707
|
}
|
|
4061
4708
|
//</editor-fold>
|
|
4062
|
-
toggleObjectVisibility(
|
|
4063
|
-
if (
|
|
4709
|
+
toggleObjectVisibility(object3, visible) {
|
|
4710
|
+
if (object3) {
|
|
4064
4711
|
if (typeof visible !== "undefined") {
|
|
4065
|
-
|
|
4712
|
+
object3.visible = visible;
|
|
4066
4713
|
} else {
|
|
4067
|
-
|
|
4714
|
+
object3.visible = !object3.visible;
|
|
4068
4715
|
}
|
|
4069
4716
|
this.notifyDirty();
|
|
4070
|
-
return
|
|
4717
|
+
return object3.visible;
|
|
4071
4718
|
}
|
|
4072
4719
|
this.notifyDirty();
|
|
4073
4720
|
return false;
|
|
@@ -4078,6 +4725,7 @@ var init_SceneObject = __esm({
|
|
|
4078
4725
|
this.notifyDirty();
|
|
4079
4726
|
super.dispose();
|
|
4080
4727
|
}
|
|
4728
|
+
/** Releases instance slots and removes children, disposing children that implement Disposable. */
|
|
4081
4729
|
disposeAndRemoveAllChildren() {
|
|
4082
4730
|
if (this.instanceMesh) {
|
|
4083
4731
|
if (this._scene) this._scene.stats.instanceCount -= this.instanceReferences.size;
|
|
@@ -4100,6 +4748,7 @@ var init_SceneObject = __esm({
|
|
|
4100
4748
|
}
|
|
4101
4749
|
this.notifyDirty();
|
|
4102
4750
|
}
|
|
4751
|
+
/** Detaches the object from its parent and requests a redraw. */
|
|
4103
4752
|
removeFromScene() {
|
|
4104
4753
|
this.notifyDirty();
|
|
4105
4754
|
this.removeFromParent();
|
|
@@ -4251,12 +4900,15 @@ var init_EntityObject = __esm({
|
|
|
4251
4900
|
if (this.animationPlayers.length) this.notifyDirty();
|
|
4252
4901
|
}
|
|
4253
4902
|
//</editor-fold>
|
|
4903
|
+
/** Finds a selected draw by its layer key, such as `main` or `main#2`, or returns `undefined`. */
|
|
4254
4904
|
getLayerGroup(name) {
|
|
4255
4905
|
return super.getGroupByName(`layer:${name}`);
|
|
4256
4906
|
}
|
|
4907
|
+
/** Finds a part name without `group:`. Supply `layerName` to restrict the search to one draw. */
|
|
4257
4908
|
getGroupByName(name, layerName) {
|
|
4258
4909
|
return layerName === void 0 ? super.getGroupByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`group:${name}`);
|
|
4259
4910
|
}
|
|
4911
|
+
/** Finds a mesh name without `mesh:`. Supply `layerName` to restrict the search to one draw. */
|
|
4260
4912
|
getMeshByName(name, layerName) {
|
|
4261
4913
|
return layerName === void 0 ? super.getMeshByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`mesh:${name}`);
|
|
4262
4914
|
}
|
|
@@ -4358,8 +5010,8 @@ var init_EntityObject = __esm({
|
|
|
4358
5010
|
this.scrollMaterials.push({ material: mat, offset: mat.map.offset, speed: scroll });
|
|
4359
5011
|
}
|
|
4360
5012
|
}
|
|
4361
|
-
this.getLayerGroup(name)?.traverse((
|
|
4362
|
-
if (isMesh(
|
|
5013
|
+
this.getLayerGroup(name)?.traverse((object3) => {
|
|
5014
|
+
if (isMesh(object3)) object3.material = mat;
|
|
4363
5015
|
});
|
|
4364
5016
|
}));
|
|
4365
5017
|
if (!this.children.length) {
|
|
@@ -4687,8 +5339,8 @@ function toTrianglesDrawMode(geometry, drawMode) {
|
|
|
4687
5339
|
return geometry;
|
|
4688
5340
|
}
|
|
4689
5341
|
}
|
|
4690
|
-
function computeMorphedAttributes(
|
|
4691
|
-
if (
|
|
5342
|
+
function computeMorphedAttributes(object3) {
|
|
5343
|
+
if (object3.geometry.isBufferGeometry !== true) {
|
|
4692
5344
|
console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
|
|
4693
5345
|
return null;
|
|
4694
5346
|
}
|
|
@@ -4701,11 +5353,11 @@ function computeMorphedAttributes(object) {
|
|
|
4701
5353
|
const _morphA = new import_three22.Vector3();
|
|
4702
5354
|
const _morphB = new import_three22.Vector3();
|
|
4703
5355
|
const _morphC = new import_three22.Vector3();
|
|
4704
|
-
function _calculateMorphedAttributeData(
|
|
5356
|
+
function _calculateMorphedAttributeData(object4, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
|
|
4705
5357
|
_vA.fromBufferAttribute(attribute, a2);
|
|
4706
5358
|
_vB.fromBufferAttribute(attribute, b2);
|
|
4707
5359
|
_vC.fromBufferAttribute(attribute, c2);
|
|
4708
|
-
const morphInfluences =
|
|
5360
|
+
const morphInfluences = object4.morphTargetInfluences;
|
|
4709
5361
|
if (material2.morphTargets && morphAttribute && morphInfluences) {
|
|
4710
5362
|
_morphA.set(0, 0, 0);
|
|
4711
5363
|
_morphB.set(0, 0, 0);
|
|
@@ -4731,10 +5383,10 @@ function computeMorphedAttributes(object) {
|
|
|
4731
5383
|
_vB.add(_morphB);
|
|
4732
5384
|
_vC.add(_morphC);
|
|
4733
5385
|
}
|
|
4734
|
-
if (
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
5386
|
+
if (object4.isSkinnedMesh) {
|
|
5387
|
+
object4.boneTransform(a2, _vA);
|
|
5388
|
+
object4.boneTransform(b2, _vB);
|
|
5389
|
+
object4.boneTransform(c2, _vC);
|
|
4738
5390
|
}
|
|
4739
5391
|
modifiedAttributeArray[a2 * 3 + 0] = _vA.x;
|
|
4740
5392
|
modifiedAttributeArray[a2 * 3 + 1] = _vA.y;
|
|
@@ -4746,8 +5398,8 @@ function computeMorphedAttributes(object) {
|
|
|
4746
5398
|
modifiedAttributeArray[c2 * 3 + 1] = _vC.y;
|
|
4747
5399
|
modifiedAttributeArray[c2 * 3 + 2] = _vC.z;
|
|
4748
5400
|
}
|
|
4749
|
-
const geometry =
|
|
4750
|
-
const material =
|
|
5401
|
+
const geometry = object3.geometry;
|
|
5402
|
+
const material = object3.material;
|
|
4751
5403
|
let a, b, c;
|
|
4752
5404
|
const index = geometry.index;
|
|
4753
5405
|
const positionAttribute = geometry.attributes.position;
|
|
@@ -4774,7 +5426,7 @@ function computeMorphedAttributes(object) {
|
|
|
4774
5426
|
b = index.getX(j + 1);
|
|
4775
5427
|
c = index.getX(j + 2);
|
|
4776
5428
|
_calculateMorphedAttributeData(
|
|
4777
|
-
|
|
5429
|
+
object3,
|
|
4778
5430
|
groupMaterial,
|
|
4779
5431
|
positionAttribute,
|
|
4780
5432
|
morphPosition,
|
|
@@ -4785,7 +5437,7 @@ function computeMorphedAttributes(object) {
|
|
|
4785
5437
|
modifiedPosition
|
|
4786
5438
|
);
|
|
4787
5439
|
_calculateMorphedAttributeData(
|
|
4788
|
-
|
|
5440
|
+
object3,
|
|
4789
5441
|
groupMaterial,
|
|
4790
5442
|
normalAttribute,
|
|
4791
5443
|
morphNormal,
|
|
@@ -4805,7 +5457,7 @@ function computeMorphedAttributes(object) {
|
|
|
4805
5457
|
b = index.getX(i + 1);
|
|
4806
5458
|
c = index.getX(i + 2);
|
|
4807
5459
|
_calculateMorphedAttributeData(
|
|
4808
|
-
|
|
5460
|
+
object3,
|
|
4809
5461
|
material,
|
|
4810
5462
|
positionAttribute,
|
|
4811
5463
|
morphPosition,
|
|
@@ -4816,7 +5468,7 @@ function computeMorphedAttributes(object) {
|
|
|
4816
5468
|
modifiedPosition
|
|
4817
5469
|
);
|
|
4818
5470
|
_calculateMorphedAttributeData(
|
|
4819
|
-
|
|
5471
|
+
object3,
|
|
4820
5472
|
material,
|
|
4821
5473
|
normalAttribute,
|
|
4822
5474
|
morphNormal,
|
|
@@ -4840,7 +5492,7 @@ function computeMorphedAttributes(object) {
|
|
|
4840
5492
|
b = j + 1;
|
|
4841
5493
|
c = j + 2;
|
|
4842
5494
|
_calculateMorphedAttributeData(
|
|
4843
|
-
|
|
5495
|
+
object3,
|
|
4844
5496
|
groupMaterial,
|
|
4845
5497
|
positionAttribute,
|
|
4846
5498
|
morphPosition,
|
|
@@ -4851,7 +5503,7 @@ function computeMorphedAttributes(object) {
|
|
|
4851
5503
|
modifiedPosition
|
|
4852
5504
|
);
|
|
4853
5505
|
_calculateMorphedAttributeData(
|
|
4854
|
-
|
|
5506
|
+
object3,
|
|
4855
5507
|
groupMaterial,
|
|
4856
5508
|
normalAttribute,
|
|
4857
5509
|
morphNormal,
|
|
@@ -4871,7 +5523,7 @@ function computeMorphedAttributes(object) {
|
|
|
4871
5523
|
b = i + 1;
|
|
4872
5524
|
c = i + 2;
|
|
4873
5525
|
_calculateMorphedAttributeData(
|
|
4874
|
-
|
|
5526
|
+
object3,
|
|
4875
5527
|
material,
|
|
4876
5528
|
positionAttribute,
|
|
4877
5529
|
morphPosition,
|
|
@@ -4882,7 +5534,7 @@ function computeMorphedAttributes(object) {
|
|
|
4882
5534
|
modifiedPosition
|
|
4883
5535
|
);
|
|
4884
5536
|
_calculateMorphedAttributeData(
|
|
4885
|
-
|
|
5537
|
+
object3,
|
|
4886
5538
|
material,
|
|
4887
5539
|
normalAttribute,
|
|
4888
5540
|
morphNormal,
|
|
@@ -4918,6 +5570,7 @@ var init_ModelCulling = __esm({
|
|
|
4918
5570
|
import_three23 = require("three");
|
|
4919
5571
|
init_CubeFace();
|
|
4920
5572
|
ModelCulling = class {
|
|
5573
|
+
/** Rotates a world-direction bit mask into model space. Bits follow {@link CUBE_FACES}. */
|
|
4921
5574
|
static toLocalMask(worldMask, rotation) {
|
|
4922
5575
|
let localMask = 0;
|
|
4923
5576
|
for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
|
|
@@ -4928,6 +5581,7 @@ var init_ModelCulling = __esm({
|
|
|
4928
5581
|
}
|
|
4929
5582
|
return localMask;
|
|
4930
5583
|
}
|
|
5584
|
+
/** Whether a model fills one block with opaque faces and can hide adjacent block faces. */
|
|
4931
5585
|
static isOpaqueFullCube(atlas) {
|
|
4932
5586
|
if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
|
|
4933
5587
|
const element = atlas.model.elements[0];
|
|
@@ -4960,9 +5614,11 @@ var init_SpecialItems = __esm({
|
|
|
4960
5614
|
import_three24 = require("three");
|
|
4961
5615
|
init_AssetKey();
|
|
4962
5616
|
init_Entities();
|
|
5617
|
+
init_BannerPatterns();
|
|
5618
|
+
init_ModelTextures();
|
|
4963
5619
|
SpecialItems = class {
|
|
4964
5620
|
/** Resolves the static entity parts drawn by a special item renderer, in model units. */
|
|
4965
|
-
static async getParts(special, root) {
|
|
5621
|
+
static async getParts(special, root, components = {}) {
|
|
4966
5622
|
const texture = (id, directory) => {
|
|
4967
5623
|
const key = AssetKey.parse("textures", id);
|
|
4968
5624
|
return new AssetKey(
|
|
@@ -4975,13 +5631,77 @@ var init_SpecialItems = __esm({
|
|
|
4975
5631
|
root
|
|
4976
5632
|
);
|
|
4977
5633
|
};
|
|
4978
|
-
const load = async (id, textureKey, transform, rotations = {}) => {
|
|
5634
|
+
const load = async (id, textureKey, transform, rotations = {}, layers = ["main"]) => {
|
|
4979
5635
|
const key = new AssetKey("minecraft", id, void 0, void 0, "entity-models", ".json", root);
|
|
4980
|
-
const model = await Entities.getEntity(key, textureKey, { layer:
|
|
5636
|
+
const model = await Entities.getEntity(key, textureKey, layers.length === 1 ? { layer: layers[0] } : {
|
|
5637
|
+
layers,
|
|
5638
|
+
textures: textureKey ? Object.fromEntries(layers.map((name) => [name, textureKey])) : void 0
|
|
5639
|
+
});
|
|
4981
5640
|
if (!model) throw new Error(`Missing entity model minecraft:${id} for special item ${special.type}`);
|
|
4982
5641
|
return { model, transform, rotations };
|
|
4983
5642
|
};
|
|
4984
5643
|
switch (special.type) {
|
|
5644
|
+
case "minecraft:trident":
|
|
5645
|
+
case "trident":
|
|
5646
|
+
case "minecraft:conduit":
|
|
5647
|
+
case "conduit": {
|
|
5648
|
+
const trident = special.type === "trident" || special.type === "minecraft:trident";
|
|
5649
|
+
const part = await load(
|
|
5650
|
+
trident ? "trident" : "conduit",
|
|
5651
|
+
texture(trident ? "trident" : "conduit/base", ""),
|
|
5652
|
+
trident ? new import_three24.Matrix4().makeScale(1, -1, -1) : new import_three24.Matrix4().makeTranslation(8, 8, 8),
|
|
5653
|
+
{},
|
|
5654
|
+
trident ? ["main"] : ["shell"]
|
|
5655
|
+
);
|
|
5656
|
+
part.model = { ...part.model, render: "solid", layers: part.model.layers && Object.fromEntries(
|
|
5657
|
+
Object.entries(part.model.layers).map(([name, layer]) => [name, { ...layer, render: "solid" }])
|
|
5658
|
+
) };
|
|
5659
|
+
return [part];
|
|
5660
|
+
}
|
|
5661
|
+
case "minecraft:banner":
|
|
5662
|
+
case "banner":
|
|
5663
|
+
case "minecraft:shield":
|
|
5664
|
+
case "shield": {
|
|
5665
|
+
const banner = special.type === "banner" || special.type === "minecraft:banner";
|
|
5666
|
+
const color2 = banner ? BannerPatterns.getColor(special.color) : components["minecraft:base_color"] === void 0 ? void 0 : BannerPatterns.getColor(components["minecraft:base_color"]);
|
|
5667
|
+
const patterns = await BannerPatterns.getLayers(components["minecraft:banner_patterns"], banner ? "banner" : "shield", root);
|
|
5668
|
+
const patterned = banner || color2 !== void 0 || patterns.length > 0;
|
|
5669
|
+
const baseTexture = texture(banner ? "banner_base" : patterned ? "shield_base" : "shield_base_nopattern", "");
|
|
5670
|
+
const transform = banner ? new import_three24.Matrix4().makeTranslation(8, 0, 8).multiply(new import_three24.Matrix4().makeScale(2 / 3, -2 / 3, -2 / 3)) : new import_three24.Matrix4().makeScale(1, -1, -1);
|
|
5671
|
+
const part = await load(banner ? "standing_banner" : "shield", baseTexture, transform, {}, banner ? ["main", "flag"] : ["main"]);
|
|
5672
|
+
const layers = Object.fromEntries(Object.entries(part.model.layers ?? { main: part.model }).map(([name, layer]) => [name, { ...layer, render: "solid" }]));
|
|
5673
|
+
let overlay2;
|
|
5674
|
+
if (banner) {
|
|
5675
|
+
const flagLayer = layers.flag;
|
|
5676
|
+
const flag = flagLayer?.layer.root.children.flag;
|
|
5677
|
+
if (!flag) throw new Error("Banner entity model is missing its flag layer");
|
|
5678
|
+
overlay2 = { ...flagLayer.layer, root: { ...flagLayer.layer.root, children: {
|
|
5679
|
+
...flagLayer.layer.root.children,
|
|
5680
|
+
flag: { ...flag, pose: { ...flag.pose, rotation: [-25e-4 * Math.PI, flag.pose.rotation[1], flag.pose.rotation[2]] } }
|
|
5681
|
+
} } };
|
|
5682
|
+
layers.flag = { ...flagLayer, layer: overlay2 };
|
|
5683
|
+
} else {
|
|
5684
|
+
const main = layers.main.layer;
|
|
5685
|
+
const plate = main.root.children.plate;
|
|
5686
|
+
if (!plate) throw new Error("Shield entity model is missing its plate");
|
|
5687
|
+
overlay2 = { ...main, root: { ...main.root, cubes: [], children: { plate } } };
|
|
5688
|
+
}
|
|
5689
|
+
const tints = {};
|
|
5690
|
+
if (patterned) {
|
|
5691
|
+
const draws = [{ texture: texture(`${banner ? "banner" : "shield"}/base`, ""), color: color2 ?? DYE_COLORS.white }, ...patterns];
|
|
5692
|
+
draws.forEach((draw, index) => {
|
|
5693
|
+
const name = index === 0 ? "pattern_base" : `pattern_${index - 1}`;
|
|
5694
|
+
layers[name] = { key: draw.texture, texture: draw.texture, layer: overlay2, render: "no_outline", tint: name };
|
|
5695
|
+
tints[name] = draw.color;
|
|
5696
|
+
});
|
|
5697
|
+
}
|
|
5698
|
+
await Promise.all([...new Map(Object.values(layers).map((layer) => [layer.texture.serialize(), layer.texture])).values()].map(async (key) => {
|
|
5699
|
+
if (!await ModelTextures.preload(key)) throw new Error(`Missing special item texture ${key.toNamespacedString()}`);
|
|
5700
|
+
}));
|
|
5701
|
+
part.model = { ...part.model, ...layers.main, layers };
|
|
5702
|
+
part.tints = tints;
|
|
5703
|
+
return [part];
|
|
5704
|
+
}
|
|
4985
5705
|
case "minecraft:chest":
|
|
4986
5706
|
case "chest": {
|
|
4987
5707
|
const angle = -(special.openness ?? 0) * Math.PI / 2;
|
|
@@ -4992,6 +5712,26 @@ var init_SpecialItems = __esm({
|
|
|
4992
5712
|
chest.model = { ...chest.model, render: "solid", layers: void 0 };
|
|
4993
5713
|
return [chest];
|
|
4994
5714
|
}
|
|
5715
|
+
case "minecraft:shulker_box":
|
|
5716
|
+
case "shulker_box": {
|
|
5717
|
+
const openness = special.openness === void 0 ? 0 : special.openness;
|
|
5718
|
+
const orientation = special.orientation === void 0 ? "up" : special.orientation;
|
|
5719
|
+
const directions = {
|
|
5720
|
+
down: [Math.PI, 0, 0],
|
|
5721
|
+
up: [0, 0, 0],
|
|
5722
|
+
north: [Math.PI / 2, 0, Math.PI],
|
|
5723
|
+
south: [Math.PI / 2, 0, 0],
|
|
5724
|
+
west: [Math.PI / 2, 0, Math.PI / 2],
|
|
5725
|
+
east: [Math.PI / 2, 0, -Math.PI / 2]
|
|
5726
|
+
};
|
|
5727
|
+
if (!Object.prototype.hasOwnProperty.call(directions, orientation)) throw new Error(`Unsupported shulker-box orientation ${orientation}`);
|
|
5728
|
+
if (typeof openness !== "number" || !Number.isFinite(openness)) throw new Error("Shulker-box openness must be finite");
|
|
5729
|
+
if (typeof special.texture !== "string" || !special.texture) throw new Error("Shulker-box texture is required");
|
|
5730
|
+
const transform = new import_three24.Matrix4().makeTranslation(8, 8, 8).multiply(new import_three24.Matrix4().makeScale(0.9995, 0.9995, 0.9995)).multiply(new import_three24.Matrix4().makeRotationFromEuler(new import_three24.Euler(...directions[orientation], "XYZ"))).multiply(new import_three24.Matrix4().makeScale(1, -1, -1)).multiply(new import_three24.Matrix4().makeTranslation(0, -16, 0));
|
|
5731
|
+
const box = await load("shulker_box", texture(special.texture, "shulker"), transform, { lid: [0, openness * Math.PI * 1.5, 0] });
|
|
5732
|
+
box.positions = { lid: [0, 24 - openness * 8, 0] };
|
|
5733
|
+
return [box];
|
|
5734
|
+
}
|
|
4995
5735
|
case "minecraft:bed":
|
|
4996
5736
|
case "bed": {
|
|
4997
5737
|
const transform = new import_three24.Matrix4().makeTranslation(0, 9, 0).multiply(new import_three24.Matrix4().makeRotationX(Math.PI / 2)).multiply(new import_three24.Matrix4().makeTranslation(8, 8, 8)).multiply(new import_three24.Matrix4().makeRotationZ(Math.PI)).multiply(new import_three24.Matrix4().makeTranslation(-8, -8, -8));
|
|
@@ -5106,6 +5846,7 @@ var init_ItemTints = __esm({
|
|
|
5106
5846
|
static async get(model, tints) {
|
|
5107
5847
|
const sources = model.tints;
|
|
5108
5848
|
if (!sources?.length) return tints;
|
|
5849
|
+
const components = model.components ?? {};
|
|
5109
5850
|
const colors = { ...tints };
|
|
5110
5851
|
await Promise.all(sources.map(async (source, index) => {
|
|
5111
5852
|
if (colors[index] !== void 0) return;
|
|
@@ -5120,27 +5861,71 @@ var init_ItemTints = __esm({
|
|
|
5120
5861
|
break;
|
|
5121
5862
|
case "dye":
|
|
5122
5863
|
case "minecraft:dye":
|
|
5864
|
+
colors[index] = this.colorOrDefault(components["minecraft:dyed_color"], source.default, true);
|
|
5865
|
+
break;
|
|
5123
5866
|
case "potion":
|
|
5124
|
-
case "minecraft:potion":
|
|
5867
|
+
case "minecraft:potion": {
|
|
5868
|
+
const value = typeof components["minecraft:potion_contents"] === "string" ? void 0 : this.componentField(components, "potion_contents", "custom_color");
|
|
5869
|
+
colors[index] = this.colorOrDefault(value, source.default);
|
|
5870
|
+
break;
|
|
5871
|
+
}
|
|
5125
5872
|
case "map_color":
|
|
5126
5873
|
case "minecraft:map_color":
|
|
5874
|
+
colors[index] = this.colorOrDefault(components["minecraft:map_color"], source.default);
|
|
5875
|
+
break;
|
|
5127
5876
|
case "firework":
|
|
5128
|
-
case "minecraft:firework":
|
|
5877
|
+
case "minecraft:firework": {
|
|
5878
|
+
const values = this.componentField(components, "firework_explosion", "colors");
|
|
5879
|
+
if (values !== void 0 && !Array.isArray(values)) throw new MineRenderError("Item tint firework_explosion.colors must be an array");
|
|
5880
|
+
if (!values?.length) {
|
|
5881
|
+
colors[index] = this.rgb(source.default);
|
|
5882
|
+
break;
|
|
5883
|
+
}
|
|
5884
|
+
const rgb = values.map((value) => this.componentColor(value));
|
|
5885
|
+
const red = Math.floor(rgb.reduce((sum, color2) => sum + (color2 >> 16 & 255), 0) / rgb.length);
|
|
5886
|
+
const green = Math.floor(rgb.reduce((sum, color2) => sum + (color2 >> 8 & 255), 0) / rgb.length);
|
|
5887
|
+
const blue = Math.floor(rgb.reduce((sum, color2) => sum + (color2 & 255), 0) / rgb.length);
|
|
5888
|
+
colors[index] = red << 16 | green << 8 | blue;
|
|
5889
|
+
break;
|
|
5890
|
+
}
|
|
5129
5891
|
case "team":
|
|
5130
5892
|
case "minecraft:team":
|
|
5131
|
-
case "custom_model_data":
|
|
5132
|
-
case "minecraft:custom_model_data":
|
|
5133
5893
|
colors[index] = this.rgb(source.default);
|
|
5134
5894
|
break;
|
|
5895
|
+
case "custom_model_data":
|
|
5896
|
+
case "minecraft:custom_model_data": {
|
|
5897
|
+
const colorIndex = source.index ?? 0;
|
|
5898
|
+
if (!Number.isSafeInteger(colorIndex) || colorIndex < 0) throw new MineRenderError("Item tint custom_model_data index must be a nonnegative integer");
|
|
5899
|
+
const values = this.componentField(components, "custom_model_data", "colors");
|
|
5900
|
+
if (values !== void 0 && !Array.isArray(values)) throw new MineRenderError("Item tint custom_model_data.colors must be an array");
|
|
5901
|
+
colors[index] = this.colorOrDefault(values?.[colorIndex], source.default, true);
|
|
5902
|
+
break;
|
|
5903
|
+
}
|
|
5135
5904
|
default:
|
|
5136
5905
|
throw new MineRenderError(`Unsupported item tint source ${source.type}`);
|
|
5137
5906
|
}
|
|
5138
5907
|
}));
|
|
5139
5908
|
return colors;
|
|
5140
5909
|
}
|
|
5141
|
-
static
|
|
5142
|
-
|
|
5143
|
-
|
|
5910
|
+
static componentField(components, id, field) {
|
|
5911
|
+
const value = components[`minecraft:${id}`];
|
|
5912
|
+
if (value === void 0) return void 0;
|
|
5913
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new MineRenderError(`Item tint ${id} must be an object`);
|
|
5914
|
+
return value[field];
|
|
5915
|
+
}
|
|
5916
|
+
static colorOrDefault(value, fallback, allowTriple = false) {
|
|
5917
|
+
return value === void 0 ? this.rgb(fallback) : this.componentColor(value, allowTriple);
|
|
5918
|
+
}
|
|
5919
|
+
static componentColor(value, allowTriple = false) {
|
|
5920
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= -2147483648 && value <= 2147483647) return this.rgb(value);
|
|
5921
|
+
if (allowTriple && Array.isArray(value) && value.length === 3 && value.every((channel) => typeof channel === "number" && Number.isFinite(channel))) {
|
|
5922
|
+
return this.rgb(value) & 16777215;
|
|
5923
|
+
}
|
|
5924
|
+
throw new MineRenderError(`Item tint component color must be a packed integer${allowTriple ? " or RGB triple" : ""}`);
|
|
5925
|
+
}
|
|
5926
|
+
static rgb(color2) {
|
|
5927
|
+
if (!Array.isArray(color2)) return color2 & 16777215;
|
|
5928
|
+
const [red, green, blue] = color2.map((channel) => Math.floor(Math.fround(Math.fround(channel) * 255)) & 255);
|
|
5144
5929
|
return red << 16 | green << 8 | blue;
|
|
5145
5930
|
}
|
|
5146
5931
|
};
|
|
@@ -5177,9 +5962,10 @@ var init_ModelObject = __esm({
|
|
|
5177
5962
|
this.originalModel = originalModel;
|
|
5178
5963
|
this.isModelObject = true;
|
|
5179
5964
|
this.specialMaterials = /* @__PURE__ */ new Map();
|
|
5965
|
+
this.geometries = /* @__PURE__ */ new Set();
|
|
5180
5966
|
this.meshesCreated = false;
|
|
5181
5967
|
this.options = (0, import_ts_deepmerge5.default)({}, _ModelObject.DEFAULT_OPTIONS, options ?? {});
|
|
5182
|
-
if (originalModel.special) this.options.instanceMeshes = false;
|
|
5968
|
+
if (originalModel.special || originalModel.parts) this.options.instanceMeshes = false;
|
|
5183
5969
|
if (this.options.tints) this.options.tints = { ...this.options.tints };
|
|
5184
5970
|
this.addEventListener("added", () => this.updateAnimationSubscription());
|
|
5185
5971
|
this.addEventListener("removed", () => this.updateAnimationSubscription());
|
|
@@ -5188,21 +5974,43 @@ var init_ModelObject = __esm({
|
|
|
5188
5974
|
this.DEFAULT_OPTIONS = (0, import_ts_deepmerge5.default)({}, SceneObject.DEFAULT_OPTIONS, {});
|
|
5189
5975
|
}
|
|
5190
5976
|
async init() {
|
|
5977
|
+
const parts = this.originalModel.parts;
|
|
5978
|
+
if (parts) {
|
|
5979
|
+
try {
|
|
5980
|
+
for (const part of parts) {
|
|
5981
|
+
const object3 = new _ModelObject(part, { ...this.options, instanceMeshes: false });
|
|
5982
|
+
this.add(object3);
|
|
5983
|
+
await object3.init();
|
|
5984
|
+
}
|
|
5985
|
+
let order = 0;
|
|
5986
|
+
this.iterateAllMeshes((mesh) => {
|
|
5987
|
+
mesh.renderOrder = order++;
|
|
5988
|
+
for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
|
|
5989
|
+
material.transparent = true;
|
|
5990
|
+
}
|
|
5991
|
+
});
|
|
5992
|
+
} catch (error) {
|
|
5993
|
+
this.disposeAndRemoveAllChildren();
|
|
5994
|
+
throw error;
|
|
5995
|
+
}
|
|
5996
|
+
this.notifyDirty();
|
|
5997
|
+
return;
|
|
5998
|
+
}
|
|
5191
5999
|
this.options.tints = await ItemTints.get(this.originalModel, this.options.tints);
|
|
5192
6000
|
const special = this.originalModel.special;
|
|
5193
6001
|
if (special) {
|
|
5194
|
-
const
|
|
6002
|
+
const parts2 = await SpecialItems.getParts(special, this.originalModel.key?.root, this.originalModel.components);
|
|
5195
6003
|
const transform = this.options.displayPosition ? DisplayTransforms.getMatrix(this.originalModel.display, this.options.displayPosition) : new import_three25.Matrix4();
|
|
5196
6004
|
transform.multiply(new import_three25.Matrix4().makeTranslation(-8, -8, -8));
|
|
5197
6005
|
try {
|
|
5198
|
-
for (const part of
|
|
5199
|
-
const
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
this.add(
|
|
5204
|
-
await
|
|
5205
|
-
|
|
6006
|
+
for (const part of parts2) {
|
|
6007
|
+
const object3 = new EntityObject(part.model, { flip: false, wireframe: this.options.wireframe, tints: part.tints });
|
|
6008
|
+
object3.matrix.copy(transform).multiply(part.transform);
|
|
6009
|
+
object3.matrixWorldNeedsUpdate = true;
|
|
6010
|
+
object3.matrixAutoUpdate = false;
|
|
6011
|
+
this.add(object3);
|
|
6012
|
+
await object3.init();
|
|
6013
|
+
object3.iterateAllMeshes((mesh) => {
|
|
5206
6014
|
const source = mesh.material;
|
|
5207
6015
|
let material = this.specialMaterials.get(source);
|
|
5208
6016
|
if (!material) {
|
|
@@ -5213,7 +6021,10 @@ var init_ModelObject = __esm({
|
|
|
5213
6021
|
mesh.material = material;
|
|
5214
6022
|
});
|
|
5215
6023
|
for (const [name, rotation] of Object.entries(part.rotations)) {
|
|
5216
|
-
|
|
6024
|
+
object3.getGroupByName(name)?.rotation.set(...rotation, "ZYX");
|
|
6025
|
+
}
|
|
6026
|
+
for (const [name, position] of Object.entries(part.positions ?? {})) {
|
|
6027
|
+
object3.getGroupByName(name)?.position.set(...position);
|
|
5217
6028
|
}
|
|
5218
6029
|
}
|
|
5219
6030
|
} catch (error) {
|
|
@@ -5246,6 +6057,7 @@ var init_ModelObject = __esm({
|
|
|
5246
6057
|
if (this.atlas.model.elements) {
|
|
5247
6058
|
this.atlas.model.elements?.forEach((el) => {
|
|
5248
6059
|
const elGeo = this._getBoxGeometryFromElement(el).clone();
|
|
6060
|
+
this.geometries.add(elGeo);
|
|
5249
6061
|
if (this.options.cullMask) {
|
|
5250
6062
|
const indices = Array.from(elGeo.getIndex().array);
|
|
5251
6063
|
elGeo.setIndex(indices.filter((_, index) => {
|
|
@@ -5258,6 +6070,7 @@ var init_ModelObject = __esm({
|
|
|
5258
6070
|
if (this.options.uvLockRotation) {
|
|
5259
6071
|
UVMapper.lockUvs(elGeo, el.faces, this.atlas, new import_three25.Euler(...this.options.uvLockRotation));
|
|
5260
6072
|
}
|
|
6073
|
+
UVMapper.setAtlasUvBounds(elGeo, el.faces, this.atlas);
|
|
5261
6074
|
if (this.options.tints) {
|
|
5262
6075
|
const colors = new Float32Array(elGeo.getAttribute("position").count * 3).fill(1);
|
|
5263
6076
|
for (const [faceIndex, faceName] of CUBE_FACES.entries()) {
|
|
@@ -5265,9 +6078,9 @@ var init_ModelObject = __esm({
|
|
|
5265
6078
|
if (tintIndex === void 0 || tintIndex < 0) continue;
|
|
5266
6079
|
const tint = this.options.tints[tintIndex];
|
|
5267
6080
|
if (tint === void 0) continue;
|
|
5268
|
-
const
|
|
6081
|
+
const color2 = new import_three25.Color(tint);
|
|
5269
6082
|
for (let vertex = 0; vertex < 4; vertex++) {
|
|
5270
|
-
|
|
6083
|
+
color2.toArray(colors, (faceIndex * 4 + vertex) * 3);
|
|
5271
6084
|
}
|
|
5272
6085
|
}
|
|
5273
6086
|
elGeo.setAttribute("color", new import_three25.BufferAttribute(colors, 3));
|
|
@@ -5298,10 +6111,14 @@ var init_ModelObject = __esm({
|
|
|
5298
6111
|
let combinedGeo;
|
|
5299
6112
|
if (allGeos.length > 0) {
|
|
5300
6113
|
combinedGeo = mergeBufferGeometries(allGeos);
|
|
6114
|
+
for (const geometry of allGeos) {
|
|
6115
|
+
this.geometries.delete(geometry);
|
|
6116
|
+
}
|
|
5301
6117
|
} else {
|
|
5302
6118
|
combinedGeo = new import_three25.BoxGeometry(16, 16, 16);
|
|
5303
6119
|
if (displayTransform) DisplayTransforms.apply(combinedGeo, displayTransform);
|
|
5304
6120
|
}
|
|
6121
|
+
this.geometries.add(combinedGeo);
|
|
5305
6122
|
combinedGeo.computeBoundingBox();
|
|
5306
6123
|
let mesh;
|
|
5307
6124
|
if (this.options.instanceMeshes) {
|
|
@@ -5323,9 +6140,12 @@ var init_ModelObject = __esm({
|
|
|
5323
6140
|
}
|
|
5324
6141
|
applyTextures() {
|
|
5325
6142
|
if (this.atlas) {
|
|
5326
|
-
const mat = Materials.createShadedCanvasMaterial(this.atlas.image.canvas, this.atlas.hasTransparency, false);
|
|
6143
|
+
const mat = Materials.createShadedCanvasMaterial(this.atlas.image.canvas, this.atlas.hasTransparency, false, true);
|
|
5327
6144
|
this.atlasMaterial = mat;
|
|
5328
6145
|
this.atlasTexture = mat.uniforms?.map?.value ?? mat.map;
|
|
6146
|
+
if (this.options.displayPosition === "gui" /* GUI */ && this.originalModel.gui_light === "front" /* FRONT */ && mat.uniforms?.SHADE) {
|
|
6147
|
+
mat.uniforms.SHADE.value = false;
|
|
6148
|
+
}
|
|
5329
6149
|
this.iterateAllMeshes((mesh) => {
|
|
5330
6150
|
if (mesh.geometry.hasAttribute("color")) mat.vertexColors = true;
|
|
5331
6151
|
mesh.material = mat;
|
|
@@ -5334,7 +6154,9 @@ var init_ModelObject = __esm({
|
|
|
5334
6154
|
}
|
|
5335
6155
|
}
|
|
5336
6156
|
updateAnimationSubscription() {
|
|
5337
|
-
|
|
6157
|
+
let root = this;
|
|
6158
|
+
while (root.parent && isModelObject(root.parent)) root = root.parent;
|
|
6159
|
+
const active = !!root.parent && (!this.isInstanced || this.instanceCounter > 0);
|
|
5338
6160
|
if (active && this.atlas?.hasAnimation && this.atlasTexture) {
|
|
5339
6161
|
if (!this.unsubscribeAtlas) {
|
|
5340
6162
|
this.atlasTexture.needsUpdate = true;
|
|
@@ -5350,6 +6172,9 @@ var init_ModelObject = __esm({
|
|
|
5350
6172
|
this.unsubscribeAtlas?.();
|
|
5351
6173
|
this.unsubscribeAtlas = void 0;
|
|
5352
6174
|
}
|
|
6175
|
+
for (const child of this.children) {
|
|
6176
|
+
if (isModelObject(child)) child.updateAnimationSubscription();
|
|
6177
|
+
}
|
|
5353
6178
|
}
|
|
5354
6179
|
nextInstance() {
|
|
5355
6180
|
const reference = super.nextInstance();
|
|
@@ -5369,6 +6194,8 @@ var init_ModelObject = __esm({
|
|
|
5369
6194
|
this.atlasMaterial = void 0;
|
|
5370
6195
|
for (const material of this.specialMaterials.values()) material.dispose();
|
|
5371
6196
|
this.specialMaterials.clear();
|
|
6197
|
+
for (const geometry of this.geometries) geometry.dispose();
|
|
6198
|
+
this.geometries.clear();
|
|
5372
6199
|
super.disposeAndRemoveAllChildren();
|
|
5373
6200
|
}
|
|
5374
6201
|
};
|
|
@@ -5582,9 +6409,9 @@ var init_FluidModelObject = __esm({
|
|
|
5582
6409
|
}
|
|
5583
6410
|
}
|
|
5584
6411
|
geometry.clearGroups();
|
|
5585
|
-
const
|
|
6412
|
+
const color2 = new import_three31.Color(this.options.tints?.[0] ?? (this.kind === "water" ? 4159204 : 16777215));
|
|
5586
6413
|
const colors = new Float32Array(geometry.getAttribute("position").count * 3);
|
|
5587
|
-
for (let i = 0; i < colors.length; i += 3)
|
|
6414
|
+
for (let i = 0; i < colors.length; i += 3) color2.toArray(colors, i);
|
|
5588
6415
|
geometry.setAttribute("color", new import_three31.BufferAttribute(colors, 3));
|
|
5589
6416
|
geometry.computeBoundingBox();
|
|
5590
6417
|
geometry.computeBoundingSphere();
|
|
@@ -5628,6 +6455,7 @@ __export(index_node_exports, {
|
|
|
5628
6455
|
Axis: () => Axis,
|
|
5629
6456
|
BUILTIN_ENTITY: () => BUILTIN_ENTITY,
|
|
5630
6457
|
BUILTIN_GENERATED: () => BUILTIN_GENERATED,
|
|
6458
|
+
BannerPatterns: () => BannerPatterns,
|
|
5631
6459
|
BasicAssetKey: () => BasicAssetKey,
|
|
5632
6460
|
BatchedExecutor: () => BatchedExecutor,
|
|
5633
6461
|
BlockEntities: () => BlockEntities,
|
|
@@ -5650,6 +6478,7 @@ __export(index_node_exports, {
|
|
|
5650
6478
|
DEFAULT_ROOT: () => DEFAULT_ROOT,
|
|
5651
6479
|
DEFAULT_UV: () => DEFAULT_UV,
|
|
5652
6480
|
DISPLAY_POSITIONS: () => DISPLAY_POSITIONS,
|
|
6481
|
+
DYE_COLORS: () => DYE_COLORS,
|
|
5653
6482
|
DefaultBlockStates: () => DefaultBlockStates,
|
|
5654
6483
|
DisplayPosition: () => DisplayPosition,
|
|
5655
6484
|
DisplayTransforms: () => DisplayTransforms,
|
|
@@ -5660,6 +6489,7 @@ __export(index_node_exports, {
|
|
|
5660
6489
|
Env: () => Env,
|
|
5661
6490
|
FluidModelObject: () => FluidModelObject,
|
|
5662
6491
|
Fonts: () => Fonts,
|
|
6492
|
+
GUI_CONTAINER_LAYOUTS: () => GUI_CONTAINER_LAYOUTS,
|
|
5663
6493
|
Geometries: () => Geometries,
|
|
5664
6494
|
GuiHelper: () => GuiHelper,
|
|
5665
6495
|
GuiLight: () => GuiLight,
|
|
@@ -5688,11 +6518,14 @@ __export(index_node_exports, {
|
|
|
5688
6518
|
NodeCache: () => NodeCache,
|
|
5689
6519
|
NodeEnv: () => NodeEnv,
|
|
5690
6520
|
OrbitControls: () => OrbitControls,
|
|
6521
|
+
PackFormats: () => PackFormats,
|
|
6522
|
+
PackMetadata: () => PackMetadata,
|
|
5691
6523
|
PersistentCache: () => PersistentCache,
|
|
5692
6524
|
Renderer: () => Renderer,
|
|
5693
6525
|
RequestError: () => RequestError,
|
|
5694
6526
|
Requests: () => Requests,
|
|
5695
6527
|
SKIN_PARTS: () => SKIN_PARTS,
|
|
6528
|
+
SceneDocumentLoader: () => SceneDocumentLoader,
|
|
5696
6529
|
SceneExporter: () => SceneExporter,
|
|
5697
6530
|
SceneInspector: () => SceneInspector,
|
|
5698
6531
|
SceneObject: () => SceneObject,
|
|
@@ -5768,6 +6601,7 @@ __export(index_node_exports, {
|
|
|
5768
6601
|
mergeBufferAttributes: () => mergeBufferAttributes,
|
|
5769
6602
|
mergeBufferGeometries: () => mergeBufferGeometries,
|
|
5770
6603
|
mergeVertices: () => mergeVertices,
|
|
6604
|
+
parseSceneDocument: () => parseSceneDocument,
|
|
5771
6605
|
prefix: () => prefix,
|
|
5772
6606
|
registerNodeEnv: () => registerNodeEnv,
|
|
5773
6607
|
sampleEntityAnimation: () => sampleEntityAnimation,
|
|
@@ -5858,9 +6692,9 @@ var NodeEnv = class {
|
|
|
5858
6692
|
imageSize(data) {
|
|
5859
6693
|
return (0, import_image_size.default)(Buffer.isBuffer(data) ? data : Buffer.from(data));
|
|
5860
6694
|
}
|
|
5861
|
-
openCache(name,
|
|
6695
|
+
openCache(name, version2) {
|
|
5862
6696
|
return new NodeCache(import_node_persist.default.create({
|
|
5863
|
-
dir: name +
|
|
6697
|
+
dir: name + version2,
|
|
5864
6698
|
encoding: "utf8"
|
|
5865
6699
|
}));
|
|
5866
6700
|
}
|
|
@@ -5892,6 +6726,7 @@ var Meshes = class _Meshes {
|
|
|
5892
6726
|
static createBox(key) {
|
|
5893
6727
|
return new import_three6.Mesh(Geometries.getBox(key.geometry), Materials.getImage(key.material));
|
|
5894
6728
|
}
|
|
6729
|
+
/** Returns a shared Mesh object. Use {@link createBox} for a mesh with its own transform and parent. */
|
|
5895
6730
|
static getBox(key) {
|
|
5896
6731
|
const keyStr = serializeMeshKey(key);
|
|
5897
6732
|
return Caching.boxMeshCache.get(keyStr, (k) => {
|
|
@@ -5941,6 +6776,7 @@ function shutdown() {
|
|
|
5941
6776
|
init_AssetKey();
|
|
5942
6777
|
init_AssetLoader();
|
|
5943
6778
|
init_Assets();
|
|
6779
|
+
init_BannerPatterns();
|
|
5944
6780
|
|
|
5945
6781
|
// src/assets/BlockEntities.ts
|
|
5946
6782
|
var import_three10 = require("three");
|
|
@@ -6004,6 +6840,7 @@ init_AssetParsers();
|
|
|
6004
6840
|
init_ModelTextures();
|
|
6005
6841
|
init_Caching();
|
|
6006
6842
|
var Fonts = class {
|
|
6843
|
+
/** Combines font definitions in source-priority order. Unsupported Unihex and TrueType providers are skipped. */
|
|
6007
6844
|
static async get(key = "minecraft:default") {
|
|
6008
6845
|
const fontKey = typeof key === "string" ? AssetKey.parse("font", key) : key;
|
|
6009
6846
|
return await Caching.fontCache.get(fontKey.serialize(), () => this.load(fontKey, []));
|
|
@@ -6019,8 +6856,8 @@ var Fonts = class {
|
|
|
6019
6856
|
const stack = await AssetLoader.getAll(key, "json" /* JSON */);
|
|
6020
6857
|
if (stack.length === 0) throw new Error(`Could not load font ${key.toNamespacedString()}`);
|
|
6021
6858
|
const glyphs = /* @__PURE__ */ new Map();
|
|
6022
|
-
for (const
|
|
6023
|
-
for (const provider of
|
|
6859
|
+
for (const asset2 of stack) {
|
|
6860
|
+
for (const provider of asset2.providers) {
|
|
6024
6861
|
if (provider.filter?.uniform || provider.filter?.jp) continue;
|
|
6025
6862
|
let provided;
|
|
6026
6863
|
switch (provider.type) {
|
|
@@ -6159,13 +6996,13 @@ var OBJExporter = class {
|
|
|
6159
6996
|
* @param {Object3D} object - The 3D object to export.
|
|
6160
6997
|
* @return {string} The exported OBJ.
|
|
6161
6998
|
*/
|
|
6162
|
-
parse(
|
|
6999
|
+
parse(object3) {
|
|
6163
7000
|
let output = "";
|
|
6164
7001
|
let indexVertex = 0;
|
|
6165
7002
|
let indexVertexUvs = 0;
|
|
6166
7003
|
let indexNormals = 0;
|
|
6167
7004
|
const vertex = new import_three16.Vector3();
|
|
6168
|
-
const
|
|
7005
|
+
const color2 = new import_three16.Color();
|
|
6169
7006
|
const normal = new import_three16.Vector3();
|
|
6170
7007
|
const uv = new import_three16.Vector2();
|
|
6171
7008
|
const face = [];
|
|
@@ -6264,9 +7101,9 @@ var OBJExporter = class {
|
|
|
6264
7101
|
vertex.applyMatrix4(points.matrixWorld);
|
|
6265
7102
|
output += "v " + vertex.x + " " + vertex.y + " " + vertex.z;
|
|
6266
7103
|
if (colors !== void 0) {
|
|
6267
|
-
|
|
6268
|
-
import_three16.ColorManagement.workingToColorSpace(
|
|
6269
|
-
output += " " +
|
|
7104
|
+
color2.fromBufferAttribute(colors, i);
|
|
7105
|
+
import_three16.ColorManagement.workingToColorSpace(color2, import_three16.SRGBColorSpace);
|
|
7106
|
+
output += " " + color2.r + " " + color2.g + " " + color2.b;
|
|
6270
7107
|
}
|
|
6271
7108
|
output += "\n";
|
|
6272
7109
|
}
|
|
@@ -6278,7 +7115,7 @@ var OBJExporter = class {
|
|
|
6278
7115
|
}
|
|
6279
7116
|
indexVertex += nbVertex;
|
|
6280
7117
|
}
|
|
6281
|
-
|
|
7118
|
+
object3.traverse(function(child) {
|
|
6282
7119
|
if (child.isMesh === true) {
|
|
6283
7120
|
parseMesh(child);
|
|
6284
7121
|
}
|
|
@@ -6306,9 +7143,9 @@ var PLYExporter = class {
|
|
|
6306
7143
|
* @param {PLYExporter~Options} options - The export options.
|
|
6307
7144
|
* @return {?(string|ArrayBuffer)} The exported PLY.
|
|
6308
7145
|
*/
|
|
6309
|
-
parse(
|
|
7146
|
+
parse(object3, onDone, options = {}) {
|
|
6310
7147
|
function traverseMeshes(cb) {
|
|
6311
|
-
|
|
7148
|
+
object3.traverse(function(child) {
|
|
6312
7149
|
if (child.isMesh === true || child.isPoints) {
|
|
6313
7150
|
const mesh = child;
|
|
6314
7151
|
const geometry = mesh.geometry;
|
|
@@ -6341,7 +7178,7 @@ var PLYExporter = class {
|
|
|
6341
7178
|
for (const name of customAttributeNames) customTypes[name] = "float";
|
|
6342
7179
|
let vertexCount = 0;
|
|
6343
7180
|
let faceCount = 0;
|
|
6344
|
-
|
|
7181
|
+
object3.traverse(function(child) {
|
|
6345
7182
|
if (child.isMesh === true) {
|
|
6346
7183
|
const mesh = child;
|
|
6347
7184
|
const geometry = mesh.geometry;
|
|
@@ -6935,8 +7772,8 @@ function equalArray(array1, array2) {
|
|
|
6935
7772
|
return element === array2[index];
|
|
6936
7773
|
});
|
|
6937
7774
|
}
|
|
6938
|
-
function stringToArrayBuffer(
|
|
6939
|
-
return new TextEncoder().encode(
|
|
7775
|
+
function stringToArrayBuffer(text2) {
|
|
7776
|
+
return new TextEncoder().encode(text2).buffer;
|
|
6940
7777
|
}
|
|
6941
7778
|
function isIdentityMatrix(matrix) {
|
|
6942
7779
|
return equalArray(matrix.elements, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
|
|
@@ -7129,12 +7966,12 @@ var GLTFWriter = class {
|
|
|
7129
7966
|
* @param {THREE.Object3D|THREE.Material|THREE.BufferGeometry|THREE.AnimationClip} object
|
|
7130
7967
|
* @param {Object} objectDef
|
|
7131
7968
|
*/
|
|
7132
|
-
serializeUserData(
|
|
7133
|
-
if (Object.keys(
|
|
7969
|
+
serializeUserData(object3, objectDef) {
|
|
7970
|
+
if (Object.keys(object3.userData).length === 0) return;
|
|
7134
7971
|
const options = this.options;
|
|
7135
7972
|
const extensionsUsed = this.extensionsUsed;
|
|
7136
7973
|
try {
|
|
7137
|
-
const json = JSON.parse(JSON.stringify(
|
|
7974
|
+
const json = JSON.parse(JSON.stringify(object3.userData));
|
|
7138
7975
|
if (options.includeCustomExtensions && json.gltfExtensions) {
|
|
7139
7976
|
if (objectDef.extensions === void 0) objectDef.extensions = {};
|
|
7140
7977
|
for (const extensionName in json.gltfExtensions) {
|
|
@@ -7145,7 +7982,7 @@ var GLTFWriter = class {
|
|
|
7145
7982
|
}
|
|
7146
7983
|
if (Object.keys(json).length > 0) objectDef.extras = json;
|
|
7147
7984
|
} catch (error) {
|
|
7148
|
-
console.warn("THREE.GLTFExporter: userData of '" +
|
|
7985
|
+
console.warn("THREE.GLTFExporter: userData of '" + object3.name + "' won't be serialized because of JSON.stringify error - " + error.message);
|
|
7149
7986
|
}
|
|
7150
7987
|
}
|
|
7151
7988
|
/**
|
|
@@ -7518,7 +8355,7 @@ var GLTFWriter = class {
|
|
|
7518
8355
|
* @param {string} mimeType export format
|
|
7519
8356
|
* @return {number} Index of the processed texture in the "images" array
|
|
7520
8357
|
*/
|
|
7521
|
-
processImage(image,
|
|
8358
|
+
processImage(image, format2, flipY, mimeType = "image/png") {
|
|
7522
8359
|
if (image !== null) {
|
|
7523
8360
|
const writer = this;
|
|
7524
8361
|
const cache = writer.cache;
|
|
@@ -7542,8 +8379,8 @@ var GLTFWriter = class {
|
|
|
7542
8379
|
ctx.scale(1, -1);
|
|
7543
8380
|
}
|
|
7544
8381
|
if (image.data !== void 0) {
|
|
7545
|
-
if (
|
|
7546
|
-
console.error("GLTFExporter: Only RGBAFormat is supported.",
|
|
8382
|
+
if (format2 !== import_three18.RGBAFormat) {
|
|
8383
|
+
console.error("GLTFExporter: Only RGBAFormat is supported.", format2);
|
|
7547
8384
|
}
|
|
7548
8385
|
if (image.width > options.maxTextureSize || image.height > options.maxTextureSize) {
|
|
7549
8386
|
console.warn("GLTFExporter: Image size is bigger than maxTextureSize", image);
|
|
@@ -7654,9 +8491,9 @@ var GLTFWriter = class {
|
|
|
7654
8491
|
if (material.isMeshStandardMaterial !== true && material.isMeshBasicMaterial !== true) {
|
|
7655
8492
|
console.warn("GLTFExporter: Use MeshStandardMaterial or MeshBasicMaterial for best results.");
|
|
7656
8493
|
}
|
|
7657
|
-
const
|
|
7658
|
-
if (!equalArray(
|
|
7659
|
-
materialDef.pbrMetallicRoughness.baseColorFactor =
|
|
8494
|
+
const color2 = material.color.toArray().concat([material.opacity]);
|
|
8495
|
+
if (!equalArray(color2, [1, 1, 1, 1])) {
|
|
8496
|
+
materialDef.pbrMetallicRoughness.baseColorFactor = color2;
|
|
7660
8497
|
}
|
|
7661
8498
|
if (material.isMeshStandardMaterial) {
|
|
7662
8499
|
materialDef.pbrMetallicRoughness.metallicFactor = material.metalness;
|
|
@@ -8071,13 +8908,13 @@ var GLTFWriter = class {
|
|
|
8071
8908
|
* @param {THREE.Object3D} object
|
|
8072
8909
|
* @return {?number}
|
|
8073
8910
|
*/
|
|
8074
|
-
processSkin(
|
|
8911
|
+
processSkin(object3) {
|
|
8075
8912
|
const json = this.json;
|
|
8076
8913
|
const nodeMap = this.nodeMap;
|
|
8077
|
-
const node = json.nodes[nodeMap.get(
|
|
8078
|
-
const skeleton =
|
|
8914
|
+
const node = json.nodes[nodeMap.get(object3)];
|
|
8915
|
+
const skeleton = object3.skeleton;
|
|
8079
8916
|
if (skeleton === void 0) return null;
|
|
8080
|
-
const rootJoint =
|
|
8917
|
+
const rootJoint = object3.skeleton.bones[0];
|
|
8081
8918
|
if (rootJoint === void 0) return null;
|
|
8082
8919
|
const joints = [];
|
|
8083
8920
|
const inverseBindMatrices = new Float32Array(skeleton.bones.length * 16);
|
|
@@ -8085,7 +8922,7 @@ var GLTFWriter = class {
|
|
|
8085
8922
|
for (let i = 0; i < skeleton.bones.length; ++i) {
|
|
8086
8923
|
joints.push(nodeMap.get(skeleton.bones[i]));
|
|
8087
8924
|
temporaryBoneInverse.copy(skeleton.boneInverses[i]);
|
|
8088
|
-
temporaryBoneInverse.multiply(
|
|
8925
|
+
temporaryBoneInverse.multiply(object3.bindMatrix).toArray(inverseBindMatrices, i * 16);
|
|
8089
8926
|
}
|
|
8090
8927
|
if (json.skins === void 0) json.skins = [];
|
|
8091
8928
|
json.skins.push({
|
|
@@ -8101,19 +8938,19 @@ var GLTFWriter = class {
|
|
|
8101
8938
|
* @param {THREE.Object3D} object Object3D to processNodeAsync
|
|
8102
8939
|
* @return {Promise<number>} Index of the node in the nodes list
|
|
8103
8940
|
*/
|
|
8104
|
-
async processNodeAsync(
|
|
8941
|
+
async processNodeAsync(object3) {
|
|
8105
8942
|
const json = this.json;
|
|
8106
8943
|
const options = this.options;
|
|
8107
8944
|
const nodeMap = this.nodeMap;
|
|
8108
8945
|
if (!json.nodes) json.nodes = [];
|
|
8109
|
-
if (
|
|
8110
|
-
return await this._processNodeWithPivotAsync(
|
|
8946
|
+
if (object3.pivot !== null) {
|
|
8947
|
+
return await this._processNodeWithPivotAsync(object3);
|
|
8111
8948
|
}
|
|
8112
8949
|
const nodeDef = {};
|
|
8113
8950
|
if (options.trs) {
|
|
8114
|
-
const rotation =
|
|
8115
|
-
const position =
|
|
8116
|
-
const scale =
|
|
8951
|
+
const rotation = object3.quaternion.toArray();
|
|
8952
|
+
const position = object3.position.toArray();
|
|
8953
|
+
const scale = object3.scale.toArray();
|
|
8117
8954
|
if (!equalArray(rotation, [0, 0, 0, 1])) {
|
|
8118
8955
|
nodeDef.rotation = rotation;
|
|
8119
8956
|
}
|
|
@@ -8124,28 +8961,28 @@ var GLTFWriter = class {
|
|
|
8124
8961
|
nodeDef.scale = scale;
|
|
8125
8962
|
}
|
|
8126
8963
|
} else {
|
|
8127
|
-
if (
|
|
8128
|
-
|
|
8964
|
+
if (object3.matrixAutoUpdate) {
|
|
8965
|
+
object3.updateMatrix();
|
|
8129
8966
|
}
|
|
8130
|
-
if (isIdentityMatrix(
|
|
8131
|
-
nodeDef.matrix =
|
|
8967
|
+
if (isIdentityMatrix(object3.matrix) === false) {
|
|
8968
|
+
nodeDef.matrix = object3.matrix.elements;
|
|
8132
8969
|
}
|
|
8133
8970
|
}
|
|
8134
|
-
if (
|
|
8135
|
-
this.serializeUserData(
|
|
8136
|
-
if (
|
|
8137
|
-
const meshIndex = await this.processMeshAsync(
|
|
8971
|
+
if (object3.name !== "") nodeDef.name = String(object3.name);
|
|
8972
|
+
this.serializeUserData(object3, nodeDef);
|
|
8973
|
+
if (object3.isMesh || object3.isLine || object3.isPoints) {
|
|
8974
|
+
const meshIndex = await this.processMeshAsync(object3);
|
|
8138
8975
|
if (meshIndex !== null) nodeDef.mesh = meshIndex;
|
|
8139
|
-
} else if (
|
|
8140
|
-
nodeDef.camera = this.processCamera(
|
|
8976
|
+
} else if (object3.isCamera) {
|
|
8977
|
+
nodeDef.camera = this.processCamera(object3);
|
|
8141
8978
|
}
|
|
8142
|
-
if (
|
|
8979
|
+
if (object3.isSkinnedMesh) this.skins.push(object3);
|
|
8143
8980
|
const nodeIndex = json.nodes.push(nodeDef) - 1;
|
|
8144
|
-
nodeMap.set(
|
|
8145
|
-
if (
|
|
8981
|
+
nodeMap.set(object3, nodeIndex);
|
|
8982
|
+
if (object3.children.length > 0) {
|
|
8146
8983
|
const children = [];
|
|
8147
|
-
for (let i = 0, l =
|
|
8148
|
-
const child =
|
|
8984
|
+
for (let i = 0, l = object3.children.length; i < l; i++) {
|
|
8985
|
+
const child = object3.children[i];
|
|
8149
8986
|
if (child.visible || options.onlyVisible === false) {
|
|
8150
8987
|
const childNodeIndex = await this.processNodeAsync(child);
|
|
8151
8988
|
if (childNodeIndex !== null) children.push(childNodeIndex);
|
|
@@ -8154,7 +8991,7 @@ var GLTFWriter = class {
|
|
|
8154
8991
|
if (children.length > 0) nodeDef.children = children;
|
|
8155
8992
|
}
|
|
8156
8993
|
await this._invokeAllAsync(function(ext) {
|
|
8157
|
-
ext.writeNode && ext.writeNode(
|
|
8994
|
+
ext.writeNode && ext.writeNode(object3, nodeDef);
|
|
8158
8995
|
});
|
|
8159
8996
|
return nodeIndex;
|
|
8160
8997
|
}
|
|
@@ -8163,19 +9000,19 @@ var GLTFWriter = class {
|
|
|
8163
9000
|
* @param {THREE.Object3D} object Object3D with pivot
|
|
8164
9001
|
* @return {Promise<number>} Index of the container node
|
|
8165
9002
|
*/
|
|
8166
|
-
async _processNodeWithPivotAsync(
|
|
9003
|
+
async _processNodeWithPivotAsync(object3) {
|
|
8167
9004
|
const json = this.json;
|
|
8168
9005
|
const options = this.options;
|
|
8169
9006
|
const nodeMap = this.nodeMap;
|
|
8170
|
-
const pivot =
|
|
9007
|
+
const pivot = object3.pivot;
|
|
8171
9008
|
const containerDef = {};
|
|
8172
|
-
const rotation =
|
|
9009
|
+
const rotation = object3.quaternion.toArray();
|
|
8173
9010
|
const position = [
|
|
8174
|
-
|
|
8175
|
-
|
|
8176
|
-
|
|
9011
|
+
object3.position.x + pivot.x,
|
|
9012
|
+
object3.position.y + pivot.y,
|
|
9013
|
+
object3.position.z + pivot.z
|
|
8177
9014
|
];
|
|
8178
|
-
const scale =
|
|
9015
|
+
const scale = object3.scale.toArray();
|
|
8179
9016
|
if (!equalArray(rotation, [0, 0, 0, 1])) {
|
|
8180
9017
|
containerDef.rotation = rotation;
|
|
8181
9018
|
}
|
|
@@ -8186,28 +9023,28 @@ var GLTFWriter = class {
|
|
|
8186
9023
|
containerDef.scale = scale;
|
|
8187
9024
|
}
|
|
8188
9025
|
containerDef.extras = { pivot: pivot.toArray() };
|
|
8189
|
-
if (
|
|
8190
|
-
this.serializeUserData(
|
|
9026
|
+
if (object3.name !== "") containerDef.name = String(object3.name);
|
|
9027
|
+
this.serializeUserData(object3, containerDef);
|
|
8191
9028
|
const containerIndex = json.nodes.push(containerDef) - 1;
|
|
8192
|
-
nodeMap.set(
|
|
9029
|
+
nodeMap.set(object3, containerIndex);
|
|
8193
9030
|
const childDef = {};
|
|
8194
9031
|
const childPosition = [-pivot.x, -pivot.y, -pivot.z];
|
|
8195
9032
|
if (!equalArray(childPosition, [0, 0, 0])) {
|
|
8196
9033
|
childDef.translation = childPosition;
|
|
8197
9034
|
}
|
|
8198
|
-
if (
|
|
8199
|
-
const meshIndex = await this.processMeshAsync(
|
|
9035
|
+
if (object3.isMesh || object3.isLine || object3.isPoints) {
|
|
9036
|
+
const meshIndex = await this.processMeshAsync(object3);
|
|
8200
9037
|
if (meshIndex !== null) childDef.mesh = meshIndex;
|
|
8201
|
-
} else if (
|
|
8202
|
-
childDef.camera = this.processCamera(
|
|
9038
|
+
} else if (object3.isCamera) {
|
|
9039
|
+
childDef.camera = this.processCamera(object3);
|
|
8203
9040
|
}
|
|
8204
|
-
if (
|
|
9041
|
+
if (object3.isSkinnedMesh) this.skins.push(object3);
|
|
8205
9042
|
const childIndex = json.nodes.push(childDef) - 1;
|
|
8206
9043
|
const containerChildren = [childIndex];
|
|
8207
|
-
if (
|
|
9044
|
+
if (object3.children.length > 0) {
|
|
8208
9045
|
const grandchildren = [];
|
|
8209
|
-
for (let i = 0, l =
|
|
8210
|
-
const child =
|
|
9046
|
+
for (let i = 0, l = object3.children.length; i < l; i++) {
|
|
9047
|
+
const child = object3.children[i];
|
|
8211
9048
|
if (child.visible || options.onlyVisible === false) {
|
|
8212
9049
|
const childNodeIndex = await this.processNodeAsync(child);
|
|
8213
9050
|
if (childNodeIndex !== null) grandchildren.push(childNodeIndex);
|
|
@@ -8217,7 +9054,7 @@ var GLTFWriter = class {
|
|
|
8217
9054
|
}
|
|
8218
9055
|
containerDef.children = containerChildren;
|
|
8219
9056
|
await this._invokeAllAsync(function(ext) {
|
|
8220
|
-
ext.writeNode && ext.writeNode(
|
|
9057
|
+
ext.writeNode && ext.writeNode(object3, containerDef);
|
|
8221
9058
|
});
|
|
8222
9059
|
return containerIndex;
|
|
8223
9060
|
}
|
|
@@ -8660,10 +9497,10 @@ var GLTFMeshGpuInstancing = class {
|
|
|
8660
9497
|
this.writer = writer;
|
|
8661
9498
|
this.name = "EXT_mesh_gpu_instancing";
|
|
8662
9499
|
}
|
|
8663
|
-
writeNode(
|
|
8664
|
-
if (!
|
|
9500
|
+
writeNode(object3, nodeDef) {
|
|
9501
|
+
if (!object3.isInstancedMesh) return;
|
|
8665
9502
|
const writer = this.writer;
|
|
8666
|
-
const mesh =
|
|
9503
|
+
const mesh = object3;
|
|
8667
9504
|
const translationAttr = new Float32Array(mesh.count * 3);
|
|
8668
9505
|
const rotationAttr = new Float32Array(mesh.count * 4);
|
|
8669
9506
|
const scaleAttr = new Float32Array(mesh.count * 3);
|
|
@@ -8853,8 +9690,8 @@ var SceneExporter = class {
|
|
|
8853
9690
|
}
|
|
8854
9691
|
}
|
|
8855
9692
|
};
|
|
8856
|
-
function localMatrix(
|
|
8857
|
-
return
|
|
9693
|
+
function localMatrix(object3) {
|
|
9694
|
+
return object3.matrixAutoUpdate ? new import_three19.Matrix4().compose(object3.position, object3.quaternion, object3.scale) : object3.matrix.clone();
|
|
8858
9695
|
}
|
|
8859
9696
|
function createSnapshot(root, gltf, bakeColors = false) {
|
|
8860
9697
|
const scene = new import_three19.Scene();
|
|
@@ -8958,24 +9795,24 @@ function createSnapshot(root, gltf, bakeColors = false) {
|
|
|
8958
9795
|
scene.add(copy);
|
|
8959
9796
|
}
|
|
8960
9797
|
};
|
|
8961
|
-
const visit = (
|
|
8962
|
-
if (!
|
|
8963
|
-
const world = parentWorld.clone().multiply(localMatrix(
|
|
8964
|
-
const mesh =
|
|
9798
|
+
const visit = (object3, parentWorld) => {
|
|
9799
|
+
if (!object3.visible) return;
|
|
9800
|
+
const world = parentWorld.clone().multiply(localMatrix(object3));
|
|
9801
|
+
const mesh = object3;
|
|
8965
9802
|
if (mesh.isMesh) {
|
|
8966
9803
|
const instanced = mesh;
|
|
8967
9804
|
if (instanced.isInstancedMesh) {
|
|
8968
9805
|
for (let i = 0; i < instanced.count; i++) {
|
|
8969
9806
|
const matrix = new import_three19.Matrix4();
|
|
8970
9807
|
instanced.getMatrixAt(i, matrix);
|
|
8971
|
-
const
|
|
8972
|
-
addMesh(mesh, world.clone().multiply(matrix),
|
|
9808
|
+
const color2 = instanced.instanceColor ? new import_three19.Color().fromBufferAttribute(instanced.instanceColor, i) : void 0;
|
|
9809
|
+
addMesh(mesh, world.clone().multiply(matrix), color2);
|
|
8973
9810
|
}
|
|
8974
9811
|
} else {
|
|
8975
9812
|
addMesh(mesh, world);
|
|
8976
9813
|
}
|
|
8977
9814
|
}
|
|
8978
|
-
for (const child of
|
|
9815
|
+
for (const child of object3.children) visit(child, world);
|
|
8979
9816
|
};
|
|
8980
9817
|
try {
|
|
8981
9818
|
const ancestors = [];
|
|
@@ -9048,16 +9885,17 @@ function copyDataTexture(source) {
|
|
|
9048
9885
|
|
|
9049
9886
|
// src/gui/GuiHelper.ts
|
|
9050
9887
|
init_AssetKey();
|
|
9888
|
+
init_ModelTextures();
|
|
9051
9889
|
|
|
9052
9890
|
// src/gui/GuiText.ts
|
|
9053
9891
|
var import_three20 = require("three");
|
|
9054
|
-
async function layoutGuiText(
|
|
9892
|
+
async function layoutGuiText(text2, options = {}) {
|
|
9055
9893
|
const maxWidth = options.maxWidth ?? Infinity, lineHeight = options.lineHeight ?? 9;
|
|
9056
9894
|
if (!(maxWidth > 0) || !Number.isFinite(lineHeight) || lineHeight <= 0) {
|
|
9057
9895
|
throw new Error("Text wrap width and line height must be positive");
|
|
9058
9896
|
}
|
|
9059
9897
|
const font = await Fonts.get(options.font);
|
|
9060
|
-
const runs = typeof
|
|
9898
|
+
const runs = typeof text2 === "string" ? [{ text: text2 }] : text2;
|
|
9061
9899
|
const glyphs = [], lineWidths = [];
|
|
9062
9900
|
let line = [], width = 0;
|
|
9063
9901
|
let trailingNewline = false;
|
|
@@ -9111,7 +9949,7 @@ function createGuiTextGeometry(layout, shadow = true) {
|
|
|
9111
9949
|
batch = { image: glyph?.image, positions: [], uvs: [], colors: [], indices: [] };
|
|
9112
9950
|
batches.push(batch);
|
|
9113
9951
|
}
|
|
9114
|
-
const
|
|
9952
|
+
const color2 = new import_three20.Color(isShadow ? (style.color & 16579836) >> 2 : style.color);
|
|
9115
9953
|
const offset = isShadow ? 1 : 0;
|
|
9116
9954
|
const top = glyph ? 7 - glyph.ascent : 0;
|
|
9117
9955
|
const height = glyph ? glyph.height * glyph.scale : 8;
|
|
@@ -9139,7 +9977,7 @@ function createGuiTextGeometry(layout, shadow = true) {
|
|
|
9139
9977
|
const v0 = glyph ? 1 - glyph.y / glyph.image.height : 1;
|
|
9140
9978
|
const v1 = glyph ? 1 - (glyph.y + glyph.height) / glyph.image.height : 0;
|
|
9141
9979
|
batch.uvs.push(u0, v0, u1, v0, u0, v1, u1, v1);
|
|
9142
|
-
for (let i = 0; i < 4; i++) batch.colors.push(
|
|
9980
|
+
for (let i = 0; i < 4; i++) batch.colors.push(color2.r, color2.g, color2.b);
|
|
9143
9981
|
batch.indices.push(base, base + 2, base + 1, base + 2, base + 3, base + 1);
|
|
9144
9982
|
};
|
|
9145
9983
|
for (const boldOffset of style.bold ? [0, 1] : [0]) {
|
|
@@ -9157,7 +9995,128 @@ function createGuiTextGeometry(layout, shadow = true) {
|
|
|
9157
9995
|
}
|
|
9158
9996
|
|
|
9159
9997
|
// src/gui/GuiHelper.ts
|
|
9998
|
+
var GUI_CONTAINER_LAYOUTS = {
|
|
9999
|
+
generic_54: {
|
|
10000
|
+
texture: "minecraft:gui/container/generic_54",
|
|
10001
|
+
crop: [0, 0, 176, 222],
|
|
10002
|
+
slotOrigin: [8, 18],
|
|
10003
|
+
slotOffset: [18, 18],
|
|
10004
|
+
rowSize: 9,
|
|
10005
|
+
slotCount: 54
|
|
10006
|
+
},
|
|
10007
|
+
crafting_table: {
|
|
10008
|
+
texture: "minecraft:gui/container/crafting_table",
|
|
10009
|
+
crop: [0, 0, 176, 166],
|
|
10010
|
+
slotOrigin: [30, 17],
|
|
10011
|
+
slotOffset: [18, 18],
|
|
10012
|
+
rowSize: 3,
|
|
10013
|
+
slotCount: 9,
|
|
10014
|
+
resultPosition: [124, 35]
|
|
10015
|
+
}
|
|
10016
|
+
};
|
|
9160
10017
|
var GuiHelper = class {
|
|
10018
|
+
/**
|
|
10019
|
+
* Creates a 182×5 boss bar using the active resource pack. Finite progress is clamped to 0–1.
|
|
10020
|
+
* The fill crops the source image; explicit crops do not apply `.mcmeta` sprite scaling.
|
|
10021
|
+
*/
|
|
10022
|
+
static async bossBar(options = {}) {
|
|
10023
|
+
const { color: color2 = "pink", progress = 1, position = [0, 0] } = options;
|
|
10024
|
+
this.validatePosition(position);
|
|
10025
|
+
if (!["pink", "blue", "red", "green", "yellow", "purple", "white"].includes(color2)) {
|
|
10026
|
+
throw new Error(`Unsupported boss bar color: ${color2}`);
|
|
10027
|
+
}
|
|
10028
|
+
if (!Number.isFinite(progress)) throw new Error("Boss bar progress must be finite");
|
|
10029
|
+
const value = Math.max(0, Math.min(1, progress));
|
|
10030
|
+
const width = value > 0 ? Math.floor(value * 181) + 1 : 0;
|
|
10031
|
+
const texture = `minecraft:gui/sprites/boss_bar/${color2}`;
|
|
10032
|
+
const layers = [{
|
|
10033
|
+
name: "boss-bar-background",
|
|
10034
|
+
texture: `${texture}_background`,
|
|
10035
|
+
position: [...position],
|
|
10036
|
+
size: [182, 5]
|
|
10037
|
+
}];
|
|
10038
|
+
if (width > 0) {
|
|
10039
|
+
layers.push({
|
|
10040
|
+
...await this.cropTexture(`${texture}_progress`, [0, 0, width, 5], [182, 5], position),
|
|
10041
|
+
name: "boss-bar-progress"
|
|
10042
|
+
});
|
|
10043
|
+
}
|
|
10044
|
+
return layers;
|
|
10045
|
+
}
|
|
10046
|
+
/**
|
|
10047
|
+
* Creates a 192×192 book page with optional arrows and supplied text. Buttons are hidden by default.
|
|
10048
|
+
* Callers choose the page content and button states; this helper does not paginate or handle clicks.
|
|
10049
|
+
*/
|
|
10050
|
+
static async book(options = {}) {
|
|
10051
|
+
const {
|
|
10052
|
+
position = [0, 0],
|
|
10053
|
+
previous = "hidden",
|
|
10054
|
+
next = "hidden",
|
|
10055
|
+
text: text2,
|
|
10056
|
+
color: color2 = 0,
|
|
10057
|
+
shadow = false,
|
|
10058
|
+
maxWidth = 114,
|
|
10059
|
+
lineHeight = 9,
|
|
10060
|
+
...style
|
|
10061
|
+
} = options;
|
|
10062
|
+
this.validatePosition(position);
|
|
10063
|
+
for (const state of [previous, next]) {
|
|
10064
|
+
if (!["hidden", "normal", "hover"].includes(state)) throw new Error(`Unsupported book button state: ${state}`);
|
|
10065
|
+
}
|
|
10066
|
+
const layers = [{
|
|
10067
|
+
...await this.cropTexture("minecraft:gui/book", [0, 0, 192, 192], [256, 256], position),
|
|
10068
|
+
name: "book-background"
|
|
10069
|
+
}];
|
|
10070
|
+
for (const [name, direction, state, x] of [
|
|
10071
|
+
["previous", "backward", previous, 43],
|
|
10072
|
+
["next", "forward", next, 116]
|
|
10073
|
+
]) {
|
|
10074
|
+
if (state === "hidden") continue;
|
|
10075
|
+
layers.push({
|
|
10076
|
+
name: `book-${name}`,
|
|
10077
|
+
texture: `minecraft:gui/sprites/widget/page_${direction}${state === "hover" ? "_highlighted" : ""}`,
|
|
10078
|
+
position: [position[0] + x, position[1] + 157],
|
|
10079
|
+
size: [23, 13]
|
|
10080
|
+
});
|
|
10081
|
+
}
|
|
10082
|
+
if (text2 !== void 0) {
|
|
10083
|
+
layers.push({
|
|
10084
|
+
name: "book-text",
|
|
10085
|
+
text: text2,
|
|
10086
|
+
position: [position[0] + 36, position[1] + 30],
|
|
10087
|
+
color: color2,
|
|
10088
|
+
shadow,
|
|
10089
|
+
maxWidth,
|
|
10090
|
+
lineHeight,
|
|
10091
|
+
...style
|
|
10092
|
+
});
|
|
10093
|
+
}
|
|
10094
|
+
return layers;
|
|
10095
|
+
}
|
|
10096
|
+
/** Creates a container background. Use {@link GUI_CONTAINER_LAYOUTS} with {@link inventorySlot} to place its items. */
|
|
10097
|
+
static async container(type, position = [0, 0]) {
|
|
10098
|
+
this.validatePosition(position);
|
|
10099
|
+
if (type !== "generic_54" && type !== "crafting_table") throw new Error(`Unsupported GUI container: ${type}`);
|
|
10100
|
+
const layout = GUI_CONTAINER_LAYOUTS[type];
|
|
10101
|
+
return [{ ...await this.cropTexture(layout.texture, layout.crop, [256, 256], position), name: "background" }];
|
|
10102
|
+
}
|
|
10103
|
+
static validatePosition(position) {
|
|
10104
|
+
if (!Array.isArray(position) || position.length !== 2 || !position.every(Number.isFinite)) {
|
|
10105
|
+
throw new Error("GUI position must contain two finite coordinates");
|
|
10106
|
+
}
|
|
10107
|
+
}
|
|
10108
|
+
static async cropTexture(texture, crop, referenceSize, position) {
|
|
10109
|
+
const image = await ModelTextures.preload(AssetKey.parse("textures", texture));
|
|
10110
|
+
if (!image) throw new Error(`Could not load GUI texture ${texture}`);
|
|
10111
|
+
const [x, y, width, height] = crop;
|
|
10112
|
+
const scaleX = image.width / referenceSize[0], scaleY = image.height / referenceSize[1];
|
|
10113
|
+
return {
|
|
10114
|
+
texture,
|
|
10115
|
+
crop: [x * scaleX, y * scaleY, width * scaleX, height * scaleY],
|
|
10116
|
+
position: [...position],
|
|
10117
|
+
size: [width, height]
|
|
10118
|
+
};
|
|
10119
|
+
}
|
|
9161
10120
|
/** Sizes a tooltip around supplied text; the first entry is its title. */
|
|
9162
10121
|
static async tooltip(lines, options = {}) {
|
|
9163
10122
|
if (!lines.length) return [];
|
|
@@ -9173,20 +10132,28 @@ var GuiHelper = class {
|
|
|
9173
10132
|
size: [width + 24, height + 24]
|
|
9174
10133
|
}));
|
|
9175
10134
|
let y = position[1];
|
|
9176
|
-
lines.forEach((
|
|
9177
|
-
layers.push({ ...textOptions, name: `tooltip-line-${index}`, text, position: [position[0], y] });
|
|
10135
|
+
lines.forEach((text2, index) => {
|
|
10136
|
+
layers.push({ ...textOptions, name: `tooltip-line-${index}`, text: text2, position: [position[0], y] });
|
|
9178
10137
|
y += layouts[index].height + (index === 0 ? titleGap : 0);
|
|
9179
10138
|
});
|
|
9180
10139
|
return layers;
|
|
9181
10140
|
}
|
|
10141
|
+
/**
|
|
10142
|
+
* Calculates a slot's top-left position in GUI pixels.
|
|
10143
|
+
* @param slot - Zero-based slot index, or `[column, row]`.
|
|
10144
|
+
* @param origin - Position of the first slot.
|
|
10145
|
+
* @param offset - Horizontal and vertical spacing between slot origins, in pixels.
|
|
10146
|
+
* @param rowSize - Slots per row when `slot` is an index.
|
|
10147
|
+
*/
|
|
9182
10148
|
static inventorySlot(slot, origin = [0, 0], offset = [18, 18], rowSize = 9) {
|
|
9183
10149
|
const [column, row] = typeof slot === "number" ? [slot % rowSize, Math.floor(slot / rowSize)] : slot;
|
|
9184
10150
|
return [origin[0] + column * offset[0], origin[1] + row * offset[1]];
|
|
9185
10151
|
}
|
|
9186
10152
|
/** Creates crafting-table layers for ingredients and the result; stack counts are not drawn. */
|
|
9187
10153
|
static recipe(recipe, options = {}) {
|
|
10154
|
+
const layout = GUI_CONTAINER_LAYOUTS.crafting_table;
|
|
9188
10155
|
const layers = [
|
|
9189
|
-
{ name: "background", texture:
|
|
10156
|
+
{ name: "background", texture: layout.texture, crop: [...layout.crop] }
|
|
9190
10157
|
];
|
|
9191
10158
|
const itemKey = (ingredient) => {
|
|
9192
10159
|
const item = typeof ingredient === "string" && !ingredient.startsWith("#") ? ingredient : typeof ingredient === "object" && "item" in ingredient ? ingredient.item : options.resolveIngredient?.(ingredient);
|
|
@@ -9201,17 +10168,17 @@ var GuiHelper = class {
|
|
|
9201
10168
|
layers.push({
|
|
9202
10169
|
name: `ingredient-${slot}`,
|
|
9203
10170
|
item: itemKey(ingredient),
|
|
9204
|
-
position: this.inventorySlot(slot,
|
|
10171
|
+
position: this.inventorySlot(slot, layout.slotOrigin, layout.slotOffset, layout.rowSize)
|
|
9205
10172
|
});
|
|
9206
10173
|
};
|
|
9207
10174
|
switch (recipe.type) {
|
|
9208
10175
|
case "minecraft:crafting_shaped":
|
|
9209
10176
|
case "crafting_shaped": {
|
|
9210
|
-
const
|
|
9211
|
-
if (!
|
|
10177
|
+
const pattern2 = recipe.pattern;
|
|
10178
|
+
if (!pattern2.length || pattern2.length > 3 || !pattern2[0].length || pattern2[0].length > 3 || pattern2.some((row) => row.length !== pattern2[0].length)) {
|
|
9212
10179
|
throw new Error("Crafting patterns must be rectangular, with 1\u20133 rows and columns");
|
|
9213
10180
|
}
|
|
9214
|
-
const cells =
|
|
10181
|
+
const cells = pattern2.flatMap((row, y) => row.split("").map((symbol, x) => ({ symbol, x, y }))).filter((cell) => cell.symbol !== " ");
|
|
9215
10182
|
if (!cells.length) throw new Error("Crafting patterns must contain an ingredient");
|
|
9216
10183
|
const minX = Math.min(...cells.map((cell) => cell.x)), maxX = Math.max(...cells.map((cell) => cell.x));
|
|
9217
10184
|
const minY = Math.min(...cells.map((cell) => cell.y)), maxY = Math.max(...cells.map((cell) => cell.y));
|
|
@@ -9236,7 +10203,7 @@ var GuiHelper = class {
|
|
|
9236
10203
|
layers.push({
|
|
9237
10204
|
name: "result",
|
|
9238
10205
|
item: itemKey("id" in recipe.result ? recipe.result.id : recipe.result.item),
|
|
9239
|
-
position: [
|
|
10206
|
+
position: [...layout.resultPosition]
|
|
9240
10207
|
});
|
|
9241
10208
|
return layers;
|
|
9242
10209
|
}
|
|
@@ -9331,7 +10298,6 @@ init_SceneObject();
|
|
|
9331
10298
|
init_Materials();
|
|
9332
10299
|
init_ModelObject();
|
|
9333
10300
|
init_DisplayPosition();
|
|
9334
|
-
init_GuiLight();
|
|
9335
10301
|
var GuiObject = class extends SceneObject {
|
|
9336
10302
|
constructor(textureLayers, options) {
|
|
9337
10303
|
super({ ...options, instanceMeshes: false, mergeMeshes: false });
|
|
@@ -9399,12 +10365,16 @@ var GuiObject = class extends SceneObject {
|
|
|
9399
10365
|
mesh2.geometry.computeBoundingBox();
|
|
9400
10366
|
bounds.union(mesh2.geometry.boundingBox.clone().applyMatrix4(new import_three26.Matrix4().multiplyMatrices(inverse, mesh2.matrixWorld)));
|
|
9401
10367
|
});
|
|
9402
|
-
item.position.z = depth
|
|
9403
|
-
|
|
10368
|
+
item.position.z = depth;
|
|
10369
|
+
if (!bounds.isEmpty()) {
|
|
10370
|
+
item.position.z -= bounds.min.z;
|
|
10371
|
+
depth += bounds.max.z - bounds.min.z;
|
|
10372
|
+
this.bounds.expandByPoint(new import_three26.Vector2(bounds.min.x, -bounds.max.y));
|
|
10373
|
+
this.bounds.expandByPoint(new import_three26.Vector2(bounds.max.x, -bounds.min.y));
|
|
10374
|
+
}
|
|
10375
|
+
depth += 0.01;
|
|
9404
10376
|
this.bounds.expandByPoint(new import_three26.Vector2(x, y));
|
|
9405
10377
|
this.bounds.expandByPoint(new import_three26.Vector2(x + width2, y + height2));
|
|
9406
|
-
this.bounds.expandByPoint(new import_three26.Vector2(bounds.min.x, -bounds.max.y));
|
|
9407
|
-
this.bounds.expandByPoint(new import_three26.Vector2(bounds.max.x, -bounds.min.y));
|
|
9408
10378
|
continue;
|
|
9409
10379
|
}
|
|
9410
10380
|
const key = typeof layer.texture === "string" ? AssetKey.parse("textures", layer.texture) : layer.texture;
|
|
@@ -9433,7 +10403,7 @@ var GuiObject = class extends SceneObject {
|
|
|
9433
10403
|
}
|
|
9434
10404
|
async createItem(layer, index) {
|
|
9435
10405
|
const key = typeof layer.item === "string" ? AssetKey.parse("models", layer.item) : layer.item;
|
|
9436
|
-
const model = await Models.getMerged(key);
|
|
10406
|
+
const model = await Models.getMerged(key, { ...layer.context, displayContext: "gui" /* GUI */ });
|
|
9437
10407
|
if (!model) throw new Error(`Could not load GUI item ${key.toNamespacedString()}`);
|
|
9438
10408
|
const item = new ModelObject(model, {
|
|
9439
10409
|
displayPosition: "gui" /* GUI */,
|
|
@@ -9444,15 +10414,13 @@ var GuiObject = class extends SceneObject {
|
|
|
9444
10414
|
item.name = `group:${layer.name ?? index}`;
|
|
9445
10415
|
this.add(item);
|
|
9446
10416
|
await item.init();
|
|
9447
|
-
|
|
9448
|
-
|
|
10417
|
+
const meshes = [];
|
|
10418
|
+
item.iterateAllMeshes((mesh) => meshes.push(mesh));
|
|
10419
|
+
meshes.forEach((mesh, partIndex) => {
|
|
9449
10420
|
mesh.name = `mesh:${layer.name ?? index}`;
|
|
9450
|
-
mesh.renderOrder = index;
|
|
10421
|
+
mesh.renderOrder = index + partIndex / (meshes.length + 1) * 0.5;
|
|
9451
10422
|
for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
|
|
9452
10423
|
material.transparent = true;
|
|
9453
|
-
if (model.gui_light === "front" /* FRONT */ && material.uniforms?.SHADE) {
|
|
9454
|
-
material.uniforms.SHADE.value = false;
|
|
9455
|
-
}
|
|
9456
10424
|
}
|
|
9457
10425
|
});
|
|
9458
10426
|
return item;
|
|
@@ -9623,29 +10591,29 @@ var SceneInspector = class {
|
|
|
9623
10591
|
this.objectControlsContainer.innerHTML = "";
|
|
9624
10592
|
this.addControls(targetObject, targetIntersection, instance);
|
|
9625
10593
|
}
|
|
9626
|
-
addControls(
|
|
10594
|
+
addControls(object3, intersection, instance) {
|
|
9627
10595
|
const container = document.createElement("div");
|
|
9628
10596
|
container.append(this.separator("Select Parent/Child"));
|
|
9629
|
-
if (
|
|
9630
|
-
container.append(this.buttonControl("Select Parent " +
|
|
9631
|
-
this.selectObject(
|
|
10597
|
+
if (object3.parent) {
|
|
10598
|
+
container.append(this.buttonControl("Select Parent " + object3.parent.constructor.name + " " + object3.parent.name, "P", () => {
|
|
10599
|
+
this.selectObject(object3.parent, intersection, instance);
|
|
9632
10600
|
}));
|
|
9633
10601
|
}
|
|
9634
|
-
if (
|
|
10602
|
+
if (object3.children.length > 0) {
|
|
9635
10603
|
let i = 1;
|
|
9636
|
-
for (let child of
|
|
10604
|
+
for (let child of object3.children) {
|
|
9637
10605
|
container.append(this.buttonControl("Select Child " + child.constructor.name + " " + child.name, "C" + i++, () => {
|
|
9638
10606
|
this.selectObject(child, intersection, instance);
|
|
9639
10607
|
}));
|
|
9640
10608
|
}
|
|
9641
10609
|
}
|
|
9642
10610
|
container.append(this.separator());
|
|
9643
|
-
const selectedInstance = instance && (
|
|
10611
|
+
const selectedInstance = instance && (object3 === instance.instanceable || object3 === intersection?.object) ? instance : void 0;
|
|
9644
10612
|
if (!selectedInstance) {
|
|
9645
|
-
container.append(this.toggleControl("Visibility", "V",
|
|
10613
|
+
container.append(this.toggleControl("Visibility", "V", object3.visible, (v) => object3.visible = v));
|
|
9646
10614
|
}
|
|
9647
10615
|
container.append(this.separator("Mesh"));
|
|
9648
|
-
this.addObjectControls(
|
|
10616
|
+
this.addObjectControls(object3, intersection, container, selectedInstance);
|
|
9649
10617
|
container.append(this.separator());
|
|
9650
10618
|
this.objectControlsContainer.append(container);
|
|
9651
10619
|
}
|
|
@@ -9836,6 +10804,7 @@ var SceneStatsDisplay = class {
|
|
|
9836
10804
|
appendTo(el) {
|
|
9837
10805
|
el.append(this.statsContainer);
|
|
9838
10806
|
}
|
|
10807
|
+
/** Stops the refresh timer and removes the panel from the page. */
|
|
9839
10808
|
dispose() {
|
|
9840
10809
|
clearInterval(this.interval);
|
|
9841
10810
|
this.interval = void 0;
|
|
@@ -9860,6 +10829,7 @@ var InstanceManager = class {
|
|
|
9860
10829
|
this.isInstanceManager = true;
|
|
9861
10830
|
this.instanceCache = {};
|
|
9862
10831
|
}
|
|
10832
|
+
/** Allocates a new placement of a cached model, or returns `undefined` if the key is unknown. */
|
|
9863
10833
|
async get(key) {
|
|
9864
10834
|
if (key in this.instanceCache) {
|
|
9865
10835
|
console.debug(p9, "key in cache", key);
|
|
@@ -9867,6 +10837,7 @@ var InstanceManager = class {
|
|
|
9867
10837
|
}
|
|
9868
10838
|
return void 0;
|
|
9869
10839
|
}
|
|
10840
|
+
/** Allocates a placement, calling the supplier once when the shared model is first needed. */
|
|
9870
10841
|
async getOrCreate(key, supplier) {
|
|
9871
10842
|
if (key in this.instanceCache) {
|
|
9872
10843
|
return await this.get(key);
|
|
@@ -9883,6 +10854,7 @@ var InstanceManager = class {
|
|
|
9883
10854
|
throw error;
|
|
9884
10855
|
}
|
|
9885
10856
|
}
|
|
10857
|
+
/** Forgets cached models without disposing them or releasing their existing placements. */
|
|
9886
10858
|
reset() {
|
|
9887
10859
|
for (let instanceCacheKey in this.instanceCache) {
|
|
9888
10860
|
delete this.instanceCache[instanceCacheKey];
|
|
@@ -9928,6 +10900,7 @@ function matchesCondition(condition, state) {
|
|
|
9928
10900
|
});
|
|
9929
10901
|
}
|
|
9930
10902
|
var BlockStateResolver = class {
|
|
10903
|
+
/** Loads vanilla property defaults, falling back to preview values inferred from the blockstate file. */
|
|
9931
10904
|
static async defaults(blockState) {
|
|
9932
10905
|
const defaults = blockState.key ? await BlockStates.getDefaultState(blockState.key) : void 0;
|
|
9933
10906
|
const state = {};
|
|
@@ -9947,9 +10920,11 @@ var BlockStateResolver = class {
|
|
|
9947
10920
|
}
|
|
9948
10921
|
return state;
|
|
9949
10922
|
}
|
|
10923
|
+
/** Selects matching models. Supply `choose` to control selection within each alternatives array. */
|
|
9950
10924
|
static select(blockState, state, choose = this.choose) {
|
|
9951
10925
|
return this.matching(blockState, state).map(choose);
|
|
9952
10926
|
}
|
|
10927
|
+
/** Returns matching model groups before choosing weighted alternatives. */
|
|
9953
10928
|
static matching(blockState, state) {
|
|
9954
10929
|
const out = [];
|
|
9955
10930
|
if (blockState.variants) {
|
|
@@ -9973,6 +10948,7 @@ var BlockStateResolver = class {
|
|
|
9973
10948
|
}
|
|
9974
10949
|
return out;
|
|
9975
10950
|
}
|
|
10951
|
+
/** Selects a random alternative using positive integer weights, each defaulting to 1. */
|
|
9976
10952
|
static choose(variants) {
|
|
9977
10953
|
if (!Array.isArray(variants)) return variants;
|
|
9978
10954
|
if (!variants.length) throw new MineRenderError("Blockstate variant arrays must not be empty");
|
|
@@ -9990,6 +10966,7 @@ var BlockStateResolver = class {
|
|
|
9990
10966
|
}
|
|
9991
10967
|
return variants[variants.length - 1];
|
|
9992
10968
|
}
|
|
10969
|
+
/** Converts blockstate rotations in degrees into the Euler rotation used by model rendering. */
|
|
9993
10970
|
static rotation(variant) {
|
|
9994
10971
|
const rotation = new import_three29.Euler();
|
|
9995
10972
|
if (typeof variant.x !== "undefined") {
|
|
@@ -10052,29 +11029,29 @@ var BlockTints = class {
|
|
|
10052
11029
|
}
|
|
10053
11030
|
}
|
|
10054
11031
|
if (!indices.size) return tints;
|
|
10055
|
-
let
|
|
11032
|
+
let color2;
|
|
10056
11033
|
switch (rule.source) {
|
|
10057
11034
|
case "constant":
|
|
10058
|
-
|
|
11035
|
+
color2 = rule.color;
|
|
10059
11036
|
break;
|
|
10060
11037
|
case "grass":
|
|
10061
|
-
|
|
11038
|
+
color2 = await Colormaps.grassColor(key);
|
|
10062
11039
|
break;
|
|
10063
11040
|
case "redstone": {
|
|
10064
11041
|
const power = Number(state.power ?? 0) / 15;
|
|
10065
11042
|
const red = power * 0.6 + (power > 0 ? 0.4 : 0.3);
|
|
10066
11043
|
const green = Math.max(0, power * power * 0.7 - 0.5);
|
|
10067
11044
|
const blue = Math.max(0, power * power * 0.6 - 0.7);
|
|
10068
|
-
|
|
11045
|
+
color2 = Math.floor(red * 255) << 16 | Math.floor(green * 255) << 8 | Math.floor(blue * 255);
|
|
10069
11046
|
break;
|
|
10070
11047
|
}
|
|
10071
11048
|
case "stem": {
|
|
10072
11049
|
const age = Number(state.age ?? 0);
|
|
10073
|
-
|
|
11050
|
+
color2 = age * 32 << 16 | 255 - age * 8 << 8 | age * 4;
|
|
10074
11051
|
break;
|
|
10075
11052
|
}
|
|
10076
11053
|
}
|
|
10077
|
-
return { ...Object.fromEntries([...indices].map((index) => [index,
|
|
11054
|
+
return { ...Object.fromEntries([...indices].map((index) => [index, color2])), ...tints };
|
|
10078
11055
|
}
|
|
10079
11056
|
};
|
|
10080
11057
|
|
|
@@ -10216,8 +11193,8 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
10216
11193
|
return false;
|
|
10217
11194
|
}
|
|
10218
11195
|
return this._models.some((model) => {
|
|
10219
|
-
const
|
|
10220
|
-
return
|
|
11196
|
+
const object3 = isInstanceReference(model) ? model.instanceable : model;
|
|
11197
|
+
return object3.isOpaqueFullCube && this.getModelCullMask(model, 63) === 63;
|
|
10221
11198
|
});
|
|
10222
11199
|
}
|
|
10223
11200
|
getModelMatrix(model) {
|
|
@@ -10226,8 +11203,8 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
10226
11203
|
return model.matrix.clone();
|
|
10227
11204
|
}
|
|
10228
11205
|
getModelCullMask(model, worldMask) {
|
|
10229
|
-
const
|
|
10230
|
-
if (
|
|
11206
|
+
const object3 = isInstanceReference(model) ? model.instanceable : model;
|
|
11207
|
+
if (object3.options.displayPosition) return 0;
|
|
10231
11208
|
const position = new import_three32.Vector3();
|
|
10232
11209
|
const rotation = new import_three32.Quaternion();
|
|
10233
11210
|
const scale = new import_three32.Vector3();
|
|
@@ -10244,14 +11221,14 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
10244
11221
|
replacements.push(model);
|
|
10245
11222
|
continue;
|
|
10246
11223
|
}
|
|
10247
|
-
const
|
|
11224
|
+
const object3 = isInstanceReference(model) ? model.instanceable : model;
|
|
10248
11225
|
const cullMask = this.getModelCullMask(model, worldMask);
|
|
10249
|
-
if (cullMask === (
|
|
11226
|
+
if (cullMask === (object3.options.cullMask ?? 0)) {
|
|
10250
11227
|
replacements.push(model);
|
|
10251
11228
|
continue;
|
|
10252
11229
|
}
|
|
10253
11230
|
const matrix = this.getModelMatrix(model);
|
|
10254
|
-
const replacement = await this.scene.addModel(
|
|
11231
|
+
const replacement = await this.scene.addModel(object3.originalModel, { ...object3.options, cullMask });
|
|
10255
11232
|
replacements.push(replacement);
|
|
10256
11233
|
if (isInstanceReference(replacement)) {
|
|
10257
11234
|
replacement.setMatrix(matrix);
|
|
@@ -10259,8 +11236,8 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
10259
11236
|
replacement.matrix.copy(matrix);
|
|
10260
11237
|
replacement.matrixWorldNeedsUpdate = true;
|
|
10261
11238
|
matrix.decompose(replacement.position, replacement.quaternion, replacement.scale);
|
|
10262
|
-
replacement.matrixAutoUpdate =
|
|
10263
|
-
replacement.visible =
|
|
11239
|
+
replacement.matrixAutoUpdate = object3.matrixAutoUpdate;
|
|
11240
|
+
replacement.visible = object3.visible;
|
|
10264
11241
|
}
|
|
10265
11242
|
}
|
|
10266
11243
|
} catch (error) {
|
|
@@ -10276,6 +11253,7 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
10276
11253
|
}
|
|
10277
11254
|
this.notifyDirty();
|
|
10278
11255
|
}
|
|
11256
|
+
/** Selected property values. Use {@link setState} to change properties and rebuild the rendered models. */
|
|
10279
11257
|
get state() {
|
|
10280
11258
|
return this._state;
|
|
10281
11259
|
}
|
|
@@ -10375,6 +11353,7 @@ var BlockObject = class _BlockObject extends SceneObject {
|
|
|
10375
11353
|
obj.setPosition(this.position);
|
|
10376
11354
|
return obj;
|
|
10377
11355
|
}
|
|
11356
|
+
/** Clears selected properties and rebuilds models without reapplying property defaults. */
|
|
10378
11357
|
async resetState() {
|
|
10379
11358
|
this._previousState = this._state;
|
|
10380
11359
|
this._state = {};
|
|
@@ -12178,9 +13157,9 @@ var SchematicParser = class {
|
|
|
12178
13157
|
* Parses a legacy schematic using custom mappings before the bundled defaults.
|
|
12179
13158
|
* Keys are `id:metadata`; values are block states such as `minecraft:oak_log[axis=x]`.
|
|
12180
13159
|
*/
|
|
12181
|
-
static async parse(nbt, customMappings = {}) {
|
|
13160
|
+
static async parse(nbt, customMappings = {}, options = {}) {
|
|
12182
13161
|
const tags = nbt.value;
|
|
12183
|
-
if (tags.Materials?.type !== "string" || tags.Materials.value !== "Alpha") {
|
|
13162
|
+
if (!options.lenient && (tags.Materials?.type !== "string" || tags.Materials.value !== "Alpha")) {
|
|
12184
13163
|
throw new MineRenderError("Schematic Materials must be Alpha");
|
|
12185
13164
|
}
|
|
12186
13165
|
const size = ["Width", "Height", "Length"].map((name) => {
|
|
@@ -12221,8 +13200,16 @@ var SchematicParser = class {
|
|
|
12221
13200
|
const id = high << 8 | ids.value[index] & 255;
|
|
12222
13201
|
const metadata = data.value[index] & 255;
|
|
12223
13202
|
const key = `${id}:${metadata}`;
|
|
12224
|
-
|
|
12225
|
-
if (!mapped
|
|
13203
|
+
let mapped = customMappings[key] ?? mapping[key];
|
|
13204
|
+
if (!mapped && options.lenient) {
|
|
13205
|
+
mapped = customMappings[`${id}:0`] ?? mapping[`${id}:0`];
|
|
13206
|
+
}
|
|
13207
|
+
if (!mapped) {
|
|
13208
|
+
if (options.lenient) {
|
|
13209
|
+
continue;
|
|
13210
|
+
}
|
|
13211
|
+
throw new MineRenderError(`Unsupported legacy block ${id}:${metadata} at schematic index ${index}`);
|
|
13212
|
+
}
|
|
12226
13213
|
const [type, state] = mapped.split("[");
|
|
12227
13214
|
if (type === "minecraft:air") continue;
|
|
12228
13215
|
const properties = {};
|
|
@@ -12266,6 +13253,11 @@ function compoundList(tags, name) {
|
|
|
12266
13253
|
// src/model/multiblock/StructureParser.ts
|
|
12267
13254
|
init_MineRenderError();
|
|
12268
13255
|
var StructureParser = class {
|
|
13256
|
+
/**
|
|
13257
|
+
* Parses decoded NBT, omitting air blocks.
|
|
13258
|
+
* @param _nbt - Structure data decoded by {@link NBTHelper.fromBuffer}.
|
|
13259
|
+
* @param paletteIndex - Palette to select when the structure has several. Defaults to 0.
|
|
13260
|
+
*/
|
|
12269
13261
|
static async parse(_nbt, paletteIndex) {
|
|
12270
13262
|
const nbt = _nbt.value;
|
|
12271
13263
|
let palette;
|
|
@@ -13100,6 +14092,7 @@ var SkinImage = class {
|
|
|
13100
14092
|
// src/skin/SkinTextures.ts
|
|
13101
14093
|
init_Textures();
|
|
13102
14094
|
var SkinTextures = class {
|
|
14095
|
+
/** Loads a skin URL. Pass `legacy` to override layout detection from the image dimensions. */
|
|
13103
14096
|
static async get(src, legacy) {
|
|
13104
14097
|
const image = await SkinImage.getData(src);
|
|
13105
14098
|
const scale = image.width / 64;
|
|
@@ -13273,6 +14266,14 @@ var SkinObject = class extends SceneObject {
|
|
|
13273
14266
|
}
|
|
13274
14267
|
}
|
|
13275
14268
|
}
|
|
14269
|
+
/**
|
|
14270
|
+
* Loads a skin texture, detecting slim arms and legacy layouts by default.
|
|
14271
|
+
* To override detection, set `slim` or `legacy` in the options passed to
|
|
14272
|
+
* `new SkinObject(options)` or `scene.addSkin(src, options)`.
|
|
14273
|
+
* Use {@link setSlim} or {@link setLegacy} to change these settings later.
|
|
14274
|
+
*
|
|
14275
|
+
* @param src - URL of the skin texture.
|
|
14276
|
+
*/
|
|
13276
14277
|
async setSkinTexture(src) {
|
|
13277
14278
|
if (typeof src === "undefined") return;
|
|
13278
14279
|
this.skinTextureSrc = src;
|
|
@@ -13383,59 +14384,61 @@ var MineRenderScene = class _MineRenderScene extends import_three34.Scene {
|
|
|
13383
14384
|
this.DEFAULT_OPTIONS = (0, import_ts_deepmerge8.default)({}, {});
|
|
13384
14385
|
}
|
|
13385
14386
|
add(...objects) {
|
|
13386
|
-
for (const
|
|
14387
|
+
for (const object3 of objects) {
|
|
13387
14388
|
this.dirty = true;
|
|
13388
14389
|
try {
|
|
13389
|
-
super.add(
|
|
14390
|
+
super.add(object3);
|
|
13390
14391
|
} finally {
|
|
13391
|
-
if (
|
|
13392
|
-
this.updateObjectRegistration(
|
|
14392
|
+
if (object3?.isObject3D && object3 !== this) {
|
|
14393
|
+
this.updateObjectRegistration(object3, object3.parent === this);
|
|
13393
14394
|
}
|
|
13394
14395
|
}
|
|
13395
14396
|
}
|
|
13396
14397
|
return this;
|
|
13397
14398
|
}
|
|
13398
14399
|
remove(...objects) {
|
|
13399
|
-
for (const
|
|
13400
|
-
if (!this.children.includes(
|
|
14400
|
+
for (const object3 of objects) {
|
|
14401
|
+
if (!this.children.includes(object3)) continue;
|
|
13401
14402
|
this.dirty = true;
|
|
13402
|
-
this.updateObjectRegistration(
|
|
14403
|
+
this.updateObjectRegistration(object3, false);
|
|
13403
14404
|
try {
|
|
13404
|
-
super.remove(
|
|
14405
|
+
super.remove(object3);
|
|
13405
14406
|
} finally {
|
|
13406
|
-
this.updateObjectRegistration(
|
|
14407
|
+
this.updateObjectRegistration(object3, object3.parent === this);
|
|
13407
14408
|
}
|
|
13408
14409
|
}
|
|
13409
14410
|
return this;
|
|
13410
14411
|
}
|
|
13411
|
-
updateObjectRegistration(
|
|
13412
|
-
if (this.observedObjects.has(
|
|
13413
|
-
const observable =
|
|
14412
|
+
updateObjectRegistration(object3, attached) {
|
|
14413
|
+
if (this.observedObjects.has(object3) === attached) return;
|
|
14414
|
+
const observable = object3;
|
|
13414
14415
|
if (attached) {
|
|
13415
|
-
this.observedObjects.add(
|
|
14416
|
+
this.observedObjects.add(object3);
|
|
13416
14417
|
observable.addEventListener("change", this.onObjectChange);
|
|
13417
14418
|
} else {
|
|
13418
|
-
this.observedObjects.delete(
|
|
14419
|
+
this.observedObjects.delete(object3);
|
|
13419
14420
|
observable.removeEventListener("change", this.onObjectChange);
|
|
13420
14421
|
}
|
|
13421
14422
|
const change = attached ? 1 : -1;
|
|
13422
14423
|
this.stats.objectCount += change;
|
|
13423
|
-
if (isSceneObject(
|
|
14424
|
+
if (isSceneObject(object3)) {
|
|
13424
14425
|
this.stats.sceneObjectCount += change;
|
|
13425
14426
|
}
|
|
13426
14427
|
}
|
|
13427
|
-
|
|
14428
|
+
/** Initializes each object before attaching it to the scene. */
|
|
14429
|
+
async initAndAdd(...object3) {
|
|
13428
14430
|
this.dirty = true;
|
|
13429
|
-
for (let obj of
|
|
14431
|
+
for (let obj of object3) {
|
|
13430
14432
|
await obj.init();
|
|
13431
14433
|
}
|
|
13432
|
-
return this.add(...
|
|
14434
|
+
return this.add(...object3);
|
|
13433
14435
|
}
|
|
13434
|
-
async addSceneObject(
|
|
14436
|
+
async addSceneObject(asset2, objectSupplier, _options, parent = this) {
|
|
13435
14437
|
this.dirty = true;
|
|
13436
14438
|
const obj = await objectSupplier();
|
|
13437
|
-
if (obj
|
|
13438
|
-
|
|
14439
|
+
if (isModelObject(obj)) obj.options.tints = await ItemTints.get(obj.originalModel, obj.options.tints);
|
|
14440
|
+
if (obj?.options?.instanceMeshes && asset2.key && asset2.key?.assetType === "models") {
|
|
14441
|
+
let key = asset2.key.serialize();
|
|
13439
14442
|
if (isModelObject(obj)) {
|
|
13440
14443
|
const { displayPosition, uvLockRotation, tints, cullMask } = obj.options;
|
|
13441
14444
|
if (displayPosition) key += `|display:${displayPosition}`;
|
|
@@ -13459,13 +14462,32 @@ var MineRenderScene = class _MineRenderScene extends import_three34.Scene {
|
|
|
13459
14462
|
return obj;
|
|
13460
14463
|
}
|
|
13461
14464
|
}
|
|
14465
|
+
/**
|
|
14466
|
+
* Adds a model loaded with {@link Models.getMerged}.
|
|
14467
|
+
* Set `options.instanceMeshes` to `false` to create a separate ModelObject.
|
|
14468
|
+
*
|
|
14469
|
+
* @param parent - Object to attach the model to. Defaults to this scene.
|
|
14470
|
+
* @returns The model, or a reference to one placement of a shared model.
|
|
14471
|
+
*/
|
|
13462
14472
|
async addModel(model, options, parent = this) {
|
|
13463
|
-
options = { ...options, tints: await ItemTints.get(model, options?.tints) };
|
|
13464
14473
|
return this.addSceneObject(model, () => new ModelObject(model, options), options, parent);
|
|
13465
14474
|
}
|
|
14475
|
+
/**
|
|
14476
|
+
* Adds the models selected by a blockstate loaded with {@link BlockStates.get}.
|
|
14477
|
+
* Set `options.initialState` to choose block properties such as `facing` or `axis`.
|
|
14478
|
+
* Use the returned object's `setPosition` method to move all of its model parts.
|
|
14479
|
+
*/
|
|
13466
14480
|
async addBlock(blockState, options, parent = this) {
|
|
13467
14481
|
return this.addSceneObject(blockState, () => new BlockObject(blockState, options), options, parent);
|
|
13468
14482
|
}
|
|
14483
|
+
/**
|
|
14484
|
+
* Loads a skin and adds an initialized player model.
|
|
14485
|
+
*
|
|
14486
|
+
* @param skin - Texture URL, such as one returned by {@link Skins.fromUuidOrUsername}.
|
|
14487
|
+
* Omit it to assign a texture later with {@link SkinObject.setSkinTexture}.
|
|
14488
|
+
* @param options - Player settings, including `slim` and `legacy` detection overrides.
|
|
14489
|
+
* @param parent - Object to attach the player to. Defaults to this scene.
|
|
14490
|
+
*/
|
|
13469
14491
|
async addSkin(skin, options, parent = this) {
|
|
13470
14492
|
this.dirty = true;
|
|
13471
14493
|
const obj = new SkinObject(options);
|
|
@@ -13477,9 +14499,14 @@ var MineRenderScene = class _MineRenderScene extends import_three34.Scene {
|
|
|
13477
14499
|
parent.add(obj);
|
|
13478
14500
|
return obj;
|
|
13479
14501
|
}
|
|
14502
|
+
/** Adds an entity loaded with {@link Entities.getEntity}, using its selected layers and textures. */
|
|
13480
14503
|
async addEntity(entity, options, parent = this) {
|
|
13481
14504
|
return this.addSceneObject(entity, () => new EntityObject(entity, options), options, parent);
|
|
13482
14505
|
}
|
|
14506
|
+
/**
|
|
14507
|
+
* Adds GUI layers in draw order. One GUI pixel equals one scene unit.
|
|
14508
|
+
* Layer positions start at the top left, with positive GUI y pointing down.
|
|
14509
|
+
*/
|
|
13483
14510
|
async addGui(layers, options, parent = this) {
|
|
13484
14511
|
const obj = new GuiObject(layers, options);
|
|
13485
14512
|
obj.scene = this;
|
|
@@ -13508,11 +14535,11 @@ var _changeEvent = { type: "change" };
|
|
|
13508
14535
|
var _startEvent = { type: "start" };
|
|
13509
14536
|
var _endEvent = { type: "end" };
|
|
13510
14537
|
var OrbitControls = class extends import_three35.EventDispatcher {
|
|
13511
|
-
constructor(
|
|
14538
|
+
constructor(object3, domElement) {
|
|
13512
14539
|
super();
|
|
13513
14540
|
if (domElement === void 0) console.warn('THREE.OrbitControls: The second parameter "domElement" is now mandatory.');
|
|
13514
14541
|
if (domElement === document) console.error('THREE.OrbitControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.');
|
|
13515
|
-
this.object =
|
|
14542
|
+
this.object = object3;
|
|
13516
14543
|
this.domElement = domElement;
|
|
13517
14544
|
this.domElement.style.touchAction = "none";
|
|
13518
14545
|
this.enabled = true;
|
|
@@ -13569,7 +14596,7 @@ var OrbitControls = class extends import_three35.EventDispatcher {
|
|
|
13569
14596
|
};
|
|
13570
14597
|
this.update = function() {
|
|
13571
14598
|
const offset = new import_three35.Vector3();
|
|
13572
|
-
const quat = new import_three35.Quaternion().setFromUnitVectors(
|
|
14599
|
+
const quat = new import_three35.Quaternion().setFromUnitVectors(object3.up, new import_three35.Vector3(0, 1, 0));
|
|
13573
14600
|
const quatInverse = quat.clone().invert();
|
|
13574
14601
|
const lastPosition = new import_three35.Vector3();
|
|
13575
14602
|
const lastQuaternion = new import_three35.Quaternion();
|
|
@@ -14134,8 +15161,8 @@ var OrbitControls = class extends import_three35.EventDispatcher {
|
|
|
14134
15161
|
}
|
|
14135
15162
|
};
|
|
14136
15163
|
var MapControls = class extends OrbitControls {
|
|
14137
|
-
constructor(
|
|
14138
|
-
super(
|
|
15164
|
+
constructor(object3, domElement) {
|
|
15165
|
+
super(object3, domElement);
|
|
14139
15166
|
this.screenSpacePanning = false;
|
|
14140
15167
|
this.mouseButtons.LEFT = import_three35.MOUSE.PAN;
|
|
14141
15168
|
this.mouseButtons.RIGHT = import_three35.MOUSE.ROTATE;
|
|
@@ -14334,6 +15361,7 @@ var Renderer = class _Renderer {
|
|
|
14334
15361
|
get element() {
|
|
14335
15362
|
return this._element;
|
|
14336
15363
|
}
|
|
15364
|
+
/** Attaches the canvas to an element and sizes it to that element's dimensions. */
|
|
14337
15365
|
appendTo(element) {
|
|
14338
15366
|
if (this._disposed) return;
|
|
14339
15367
|
this._element = element;
|
|
@@ -14361,6 +15389,7 @@ var Renderer = class _Renderer {
|
|
|
14361
15389
|
events.add(changeEvent2);
|
|
14362
15390
|
dispatcher.addEventListener(changeEvent2, this._changeListener);
|
|
14363
15391
|
}
|
|
15392
|
+
/** Updates the camera and canvas size. Width and height are in CSS pixels. */
|
|
14364
15393
|
resize(width, height) {
|
|
14365
15394
|
if (this._disposed) return;
|
|
14366
15395
|
if (isPerspectiveCamera(this.camera)) {
|
|
@@ -14399,12 +15428,14 @@ var Renderer = class _Renderer {
|
|
|
14399
15428
|
if (this._frameCallbacks.get(callback) === subscription) this._frameCallbacks.delete(callback);
|
|
14400
15429
|
};
|
|
14401
15430
|
}
|
|
15431
|
+
/** Starts or resumes the animation loop and registered frame callbacks. */
|
|
14402
15432
|
start() {
|
|
14403
15433
|
if (this._disposed) return;
|
|
14404
15434
|
this.stop();
|
|
14405
15435
|
this._running = true;
|
|
14406
15436
|
if (!this._inAnimationLoop) this.renderer.setAnimationLoop(this._animationLoop);
|
|
14407
15437
|
}
|
|
15438
|
+
/** Pauses rendering and frame callbacks. Call {@link start} to resume. */
|
|
14408
15439
|
stop() {
|
|
14409
15440
|
if (this._disposed) return;
|
|
14410
15441
|
this._running = false;
|
|
@@ -14513,12 +15544,15 @@ var Renderer = class _Renderer {
|
|
|
14513
15544
|
const canvas = trim ? trimCanvas(this.renderer.domElement) : this.renderer.domElement;
|
|
14514
15545
|
return canvas.toDataURL(mime, quality);
|
|
14515
15546
|
}
|
|
15547
|
+
/** Exports visible scene meshes as OBJ text, without texture images. */
|
|
14516
15548
|
toObj() {
|
|
14517
15549
|
return SceneExporter.toObj(this.scene);
|
|
14518
15550
|
}
|
|
15551
|
+
/** Exports visible scene meshes as PLY. Set `options.binary` for an ArrayBuffer. */
|
|
14519
15552
|
toPLY(options) {
|
|
14520
15553
|
return SceneExporter.toPLY(this.scene, options);
|
|
14521
15554
|
}
|
|
15555
|
+
/** Exports a static textured scene in the browser. Set `options.binary` for GLB output. */
|
|
14522
15556
|
toGLTF(options) {
|
|
14523
15557
|
return SceneExporter.toGLTF(this.scene, options);
|
|
14524
15558
|
}
|
|
@@ -14557,16 +15591,244 @@ init_SceneObject();
|
|
|
14557
15591
|
// src/index.ts
|
|
14558
15592
|
init_request();
|
|
14559
15593
|
|
|
15594
|
+
// src/scene/SceneDocument.ts
|
|
15595
|
+
init_DisplayPosition();
|
|
15596
|
+
function fail(path, message) {
|
|
15597
|
+
throw new Error(`${path}: ${message}`);
|
|
15598
|
+
}
|
|
15599
|
+
function object2(value, path) {
|
|
15600
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail(path, "expected a JSON object");
|
|
15601
|
+
for (const key of Object.keys(value)) {
|
|
15602
|
+
if (["__proto__", "prototype", "constructor"].includes(key)) fail(`${path}.${key}`, "unsupported property");
|
|
15603
|
+
}
|
|
15604
|
+
return value;
|
|
15605
|
+
}
|
|
15606
|
+
function fields(value, path, allowed, required = []) {
|
|
15607
|
+
const input = object2(value, path);
|
|
15608
|
+
for (const key of required) if (input[key] === void 0) fail(`${path}.${key}`, "required");
|
|
15609
|
+
for (const [key, entry] of Object.entries(input)) {
|
|
15610
|
+
if (!Object.prototype.hasOwnProperty.call(allowed, key)) fail(`${path}.${key}`, "unsupported property");
|
|
15611
|
+
if (entry !== void 0) allowed[key](entry, `${path}.${key}`);
|
|
15612
|
+
}
|
|
15613
|
+
return input;
|
|
15614
|
+
}
|
|
15615
|
+
var text = (value, path) => {
|
|
15616
|
+
if (typeof value !== "string" || !value.trim()) fail(path, "expected a nonempty string");
|
|
15617
|
+
};
|
|
15618
|
+
var literalText = (value, path) => {
|
|
15619
|
+
if (typeof value !== "string") fail(path, "expected a string");
|
|
15620
|
+
};
|
|
15621
|
+
var boolean = (value, path) => {
|
|
15622
|
+
if (typeof value !== "boolean") fail(path, "expected a boolean");
|
|
15623
|
+
};
|
|
15624
|
+
var number = (value, path) => {
|
|
15625
|
+
if (typeof value !== "number" || !Number.isFinite(value)) fail(path, "expected a finite number");
|
|
15626
|
+
};
|
|
15627
|
+
var positive = (value, path) => {
|
|
15628
|
+
number(value, path);
|
|
15629
|
+
if (value <= 0) fail(path, "expected a positive number");
|
|
15630
|
+
};
|
|
15631
|
+
var nonnegative = (value, path) => {
|
|
15632
|
+
number(value, path);
|
|
15633
|
+
if (value < 0) fail(path, "expected a nonnegative number");
|
|
15634
|
+
};
|
|
15635
|
+
var color = (value, path) => {
|
|
15636
|
+
number(value, path);
|
|
15637
|
+
if (!Number.isInteger(value) || value < 0 || value > 16777215) {
|
|
15638
|
+
fail(path, "expected an RGB integer from 0 to 16777215");
|
|
15639
|
+
}
|
|
15640
|
+
};
|
|
15641
|
+
var asset = (value, path) => {
|
|
15642
|
+
text(value, path);
|
|
15643
|
+
if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(value) || value.split(/[:/]/).some((part) => !part || part === "." || part === "..")) {
|
|
15644
|
+
fail(path, "expected a resource ID such as minecraft:stone");
|
|
15645
|
+
}
|
|
15646
|
+
};
|
|
15647
|
+
var version = (value, path) => {
|
|
15648
|
+
text(value, path);
|
|
15649
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(value)) fail(path, "expected a Minecraft version without slashes");
|
|
15650
|
+
};
|
|
15651
|
+
var tuple = (size, item = number) => (value, path) => {
|
|
15652
|
+
if (!Array.isArray(value) || value.length !== size) fail(path, `expected ${size} numbers`);
|
|
15653
|
+
for (let index = 0; index < size; index++) item(value[index], `${path}[${index}]`);
|
|
15654
|
+
};
|
|
15655
|
+
var list = (item) => (value, path) => {
|
|
15656
|
+
if (!Array.isArray(value)) fail(path, "expected an array");
|
|
15657
|
+
for (let index = 0; index < value.length; index++) item(value[index], `${path}[${index}]`);
|
|
15658
|
+
};
|
|
15659
|
+
var dictionary = (item, numericKeys = false) => (value, path) => {
|
|
15660
|
+
for (const [key, entry] of Object.entries(object2(value, path))) {
|
|
15661
|
+
if (numericKeys && !/^\d+$/.test(key)) fail(`${path}.${key}`, "expected a nonnegative tint index");
|
|
15662
|
+
item(entry, `${path}.${key}`);
|
|
15663
|
+
}
|
|
15664
|
+
};
|
|
15665
|
+
var enumeration = (values) => (value, path) => {
|
|
15666
|
+
if (!values.includes(value)) fail(path, `expected one of: ${values.join(", ")}`);
|
|
15667
|
+
};
|
|
15668
|
+
var textStyle = { color, bold: boolean, italic: boolean };
|
|
15669
|
+
function guiLayer(value, path) {
|
|
15670
|
+
const layer = object2(value, path);
|
|
15671
|
+
if (["texture", "item", "text"].filter((key) => layer[key] !== void 0).length !== 1) {
|
|
15672
|
+
fail(path, "expected exactly one of texture, item, or text");
|
|
15673
|
+
}
|
|
15674
|
+
const layout = { name: text, position: tuple(2), size: tuple(2, positive) };
|
|
15675
|
+
if (layer.texture !== void 0) {
|
|
15676
|
+
fields(value, path, { ...layout, texture: asset, crop: (value2, path2) => {
|
|
15677
|
+
tuple(4)(value2, path2);
|
|
15678
|
+
const [x, y, width, height] = value2;
|
|
15679
|
+
nonnegative(x, `${path2}[0]`);
|
|
15680
|
+
nonnegative(y, `${path2}[1]`);
|
|
15681
|
+
positive(width, `${path2}[2]`);
|
|
15682
|
+
positive(height, `${path2}[3]`);
|
|
15683
|
+
} });
|
|
15684
|
+
} else if (layer.item !== void 0) {
|
|
15685
|
+
fields(value, path, { ...layout, item: asset, tints: dictionary(color, true) });
|
|
15686
|
+
} else {
|
|
15687
|
+
fields(value, path, {
|
|
15688
|
+
...layout,
|
|
15689
|
+
...textStyle,
|
|
15690
|
+
font: asset,
|
|
15691
|
+
shadow: boolean,
|
|
15692
|
+
maxWidth: positive,
|
|
15693
|
+
lineHeight: positive,
|
|
15694
|
+
text: (value2, path2) => {
|
|
15695
|
+
if (typeof value2 === "string") return;
|
|
15696
|
+
list((run, path3) => {
|
|
15697
|
+
fields(run, path3, { text: literalText, ...textStyle }, ["text"]);
|
|
15698
|
+
})(value2, path2);
|
|
15699
|
+
}
|
|
15700
|
+
});
|
|
15701
|
+
}
|
|
15702
|
+
}
|
|
15703
|
+
function validateObject(value, path) {
|
|
15704
|
+
const input = object2(value, path);
|
|
15705
|
+
const common = { id: text, name: text, type: text, position: tuple(3), rotation: tuple(3), scale: tuple(3), visible: boolean };
|
|
15706
|
+
const required = ["id", "type"];
|
|
15707
|
+
const renderOptions = { wireframe: boolean };
|
|
15708
|
+
switch (input.type) {
|
|
15709
|
+
case "skin":
|
|
15710
|
+
fields(value, path, {
|
|
15711
|
+
...common,
|
|
15712
|
+
skin: text,
|
|
15713
|
+
cape: (value2, path2) => {
|
|
15714
|
+
fields(value2, path2, { texture: text, layout: enumeration(["minecraft", "optifine", "labymod"]) }, ["texture"]);
|
|
15715
|
+
},
|
|
15716
|
+
hiddenParts: list(enumeration(SKIN_PARTS)),
|
|
15717
|
+
pose: (value2, path2) => {
|
|
15718
|
+
fields(value2, path2, {
|
|
15719
|
+
head: tuple(3),
|
|
15720
|
+
body: tuple(3),
|
|
15721
|
+
rightArm: tuple(3),
|
|
15722
|
+
leftArm: tuple(3),
|
|
15723
|
+
rightLeg: tuple(3),
|
|
15724
|
+
leftLeg: tuple(3),
|
|
15725
|
+
cape: tuple(3)
|
|
15726
|
+
});
|
|
15727
|
+
},
|
|
15728
|
+
options: (value2, path2) => {
|
|
15729
|
+
fields(value2, path2, { slim: boolean, legacy: boolean });
|
|
15730
|
+
}
|
|
15731
|
+
}, required);
|
|
15732
|
+
break;
|
|
15733
|
+
case "block":
|
|
15734
|
+
fields(value, path, {
|
|
15735
|
+
...common,
|
|
15736
|
+
asset,
|
|
15737
|
+
state: dictionary(text),
|
|
15738
|
+
options: (value2, path2) => {
|
|
15739
|
+
fields(value2, path2, { ...renderOptions, tints: dictionary(color, true) });
|
|
15740
|
+
}
|
|
15741
|
+
}, [...required, "asset"]);
|
|
15742
|
+
break;
|
|
15743
|
+
case "item":
|
|
15744
|
+
case "model":
|
|
15745
|
+
fields(value, path, {
|
|
15746
|
+
...common,
|
|
15747
|
+
asset,
|
|
15748
|
+
options: (value2, path2) => {
|
|
15749
|
+
fields(value2, path2, { ...renderOptions, displayPosition: enumeration(DISPLAY_POSITIONS), tints: dictionary(color, true) });
|
|
15750
|
+
}
|
|
15751
|
+
}, [...required, "asset"]);
|
|
15752
|
+
break;
|
|
15753
|
+
case "entity":
|
|
15754
|
+
fields(value, path, {
|
|
15755
|
+
...common,
|
|
15756
|
+
asset,
|
|
15757
|
+
texture: asset,
|
|
15758
|
+
layers: list(text),
|
|
15759
|
+
when: list(text),
|
|
15760
|
+
textures: dictionary(asset),
|
|
15761
|
+
options: (value2, path2) => {
|
|
15762
|
+
fields(value2, path2, {
|
|
15763
|
+
...renderOptions,
|
|
15764
|
+
flip: boolean,
|
|
15765
|
+
tints: dictionary((value3, path3) => {
|
|
15766
|
+
typeof value3 === "string" ? text(value3, path3) : color(value3, path3);
|
|
15767
|
+
})
|
|
15768
|
+
});
|
|
15769
|
+
},
|
|
15770
|
+
animation: (value2, path2) => {
|
|
15771
|
+
fields(value2, path2, { name: text, loop: boolean, speed: nonnegative, time: nonnegative, paused: boolean }, ["name"]);
|
|
15772
|
+
}
|
|
15773
|
+
}, [...required, "asset"]);
|
|
15774
|
+
if (Array.isArray(input.layers) && !input.layers.length) fail(`${path}.layers`, "expected at least one layer");
|
|
15775
|
+
break;
|
|
15776
|
+
case "gui":
|
|
15777
|
+
fields(value, path, { ...common, layers: list(guiLayer) }, [...required, "layers"]);
|
|
15778
|
+
break;
|
|
15779
|
+
default:
|
|
15780
|
+
fail(`${path}.type`, "expected skin, block, item, entity, model, or gui");
|
|
15781
|
+
}
|
|
15782
|
+
}
|
|
15783
|
+
function parseSceneDocument(value) {
|
|
15784
|
+
if (typeof value === "string") {
|
|
15785
|
+
try {
|
|
15786
|
+
value = JSON.parse(value);
|
|
15787
|
+
} catch {
|
|
15788
|
+
fail("scene", "invalid JSON");
|
|
15789
|
+
}
|
|
15790
|
+
}
|
|
15791
|
+
const input = fields(value, "scene", {
|
|
15792
|
+
format: enumeration(["minerender-scene"]),
|
|
15793
|
+
version: (value2, path) => {
|
|
15794
|
+
if (value2 !== 1) fail(path, "only scene document version 1 is supported");
|
|
15795
|
+
},
|
|
15796
|
+
minecraftVersion: version,
|
|
15797
|
+
camera: (value2, path) => {
|
|
15798
|
+
fields(value2, path, { position: tuple(3), target: tuple(3) }, ["position", "target"]);
|
|
15799
|
+
},
|
|
15800
|
+
objects: list(validateObject)
|
|
15801
|
+
}, ["format", "version", "objects"]);
|
|
15802
|
+
const ids = /* @__PURE__ */ new Set();
|
|
15803
|
+
for (const [index, entry] of input.objects.entries()) {
|
|
15804
|
+
if (ids.has(entry.id)) fail(`scene.objects[${index}].id`, `duplicate object ID "${entry.id}"`);
|
|
15805
|
+
ids.add(entry.id);
|
|
15806
|
+
}
|
|
15807
|
+
return JSON.parse(JSON.stringify(input));
|
|
15808
|
+
}
|
|
15809
|
+
|
|
15810
|
+
// src/scene/SceneDocumentLoader.ts
|
|
15811
|
+
var import_three38 = require("three");
|
|
15812
|
+
init_AssetKey();
|
|
15813
|
+
init_AssetLoader();
|
|
15814
|
+
init_BlockStates();
|
|
15815
|
+
init_Entities();
|
|
15816
|
+
init_Models();
|
|
15817
|
+
init_DisplayPosition();
|
|
15818
|
+
init_SceneObject();
|
|
15819
|
+
|
|
14560
15820
|
// src/skin/Skins.ts
|
|
14561
15821
|
init_Requests();
|
|
14562
15822
|
var Skins = class {
|
|
14563
15823
|
//<editor-fold desc="Skins">
|
|
15824
|
+
/** Returns a skin URL for {@link MineRenderScene.addSkin}, or `undefined` if name lookup fails. */
|
|
14564
15825
|
static async fromUuidOrUsername(uuidOrUsername) {
|
|
14565
15826
|
if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
|
|
14566
15827
|
return this.fromUuid(uuidOrUsername);
|
|
14567
15828
|
}
|
|
14568
15829
|
return this.fromUsername(uuidOrUsername);
|
|
14569
15830
|
}
|
|
15831
|
+
/** Builds a proxy URL for a UUID without downloading or checking the skin. */
|
|
14570
15832
|
static async fromUuid(uuid) {
|
|
14571
15833
|
return this.getMcProxySkinUrl(uuid);
|
|
14572
15834
|
}
|
|
@@ -14578,6 +15840,7 @@ var Skins = class {
|
|
|
14578
15840
|
return void 0;
|
|
14579
15841
|
});
|
|
14580
15842
|
}
|
|
15843
|
+
/** Looks up a saved MineSkin skin and returns its texture URL, or `undefined` on lookup failure. */
|
|
14581
15844
|
static async fromMineSkin(mineskinId) {
|
|
14582
15845
|
return this.getMineSkin(mineskinId).then((mineskin) => {
|
|
14583
15846
|
if (mineskin) {
|
|
@@ -14588,12 +15851,14 @@ var Skins = class {
|
|
|
14588
15851
|
}
|
|
14589
15852
|
//</editor-fold>
|
|
14590
15853
|
//<editor-fold desc="Capes">
|
|
15854
|
+
/** Resolves a player's Minecraft cape URL for {@link SkinObject.setCapeTexture}. */
|
|
14591
15855
|
static async capeFromUuidOrUsername(uuidOrUsername) {
|
|
14592
15856
|
if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
|
|
14593
15857
|
return this.capeFromUuid(uuidOrUsername);
|
|
14594
15858
|
}
|
|
14595
15859
|
return this.capeFromUsername(uuidOrUsername);
|
|
14596
15860
|
}
|
|
15861
|
+
/** Builds a proxy URL without checking whether the player has a cape. */
|
|
14597
15862
|
static async capeFromUuid(uuid) {
|
|
14598
15863
|
return this.getMcProxyCapeUrl(uuid);
|
|
14599
15864
|
}
|
|
@@ -14722,6 +15987,195 @@ var Skins = class {
|
|
|
14722
15987
|
//</editor-fold>
|
|
14723
15988
|
};
|
|
14724
15989
|
|
|
15990
|
+
// src/scene/SceneDocumentLoader.ts
|
|
15991
|
+
var DocumentObjectScene = class extends MineRenderScene {
|
|
15992
|
+
async addSceneObject(_asset, supplier, _options, parent = this) {
|
|
15993
|
+
const object3 = await supplier();
|
|
15994
|
+
return this.initialize(object3, parent);
|
|
15995
|
+
}
|
|
15996
|
+
async initialize(object3, parent = this) {
|
|
15997
|
+
object3.scene = this;
|
|
15998
|
+
try {
|
|
15999
|
+
await object3.init();
|
|
16000
|
+
parent.add(object3);
|
|
16001
|
+
return object3;
|
|
16002
|
+
} catch (error) {
|
|
16003
|
+
object3.dispose();
|
|
16004
|
+
object3.removeFromParent();
|
|
16005
|
+
throw error;
|
|
16006
|
+
}
|
|
16007
|
+
}
|
|
16008
|
+
dispose() {
|
|
16009
|
+
for (const child of [...this.children]) {
|
|
16010
|
+
if (child.parent !== this) continue;
|
|
16011
|
+
if (isSceneObject(child)) child.dispose();
|
|
16012
|
+
child.removeFromParent();
|
|
16013
|
+
}
|
|
16014
|
+
}
|
|
16015
|
+
};
|
|
16016
|
+
async function skinTexture(value, cape = false) {
|
|
16017
|
+
if (!/^[a-zA-Z0-9_-]{1,36}$/.test(value)) return value;
|
|
16018
|
+
const texture = await (cape ? Skins.capeFromUuidOrUsername(value) : Skins.fromUuidOrUsername(value));
|
|
16019
|
+
if (!texture) throw new Error(`Could not find ${cape ? "cape" : "skin"} for "${value}"`);
|
|
16020
|
+
return texture;
|
|
16021
|
+
}
|
|
16022
|
+
var SceneDocumentLoader = class {
|
|
16023
|
+
static parse(value) {
|
|
16024
|
+
return parseSceneDocument(value);
|
|
16025
|
+
}
|
|
16026
|
+
/**
|
|
16027
|
+
* Loads objects concurrently and attaches them in document order after every load succeeds.
|
|
16028
|
+
* A failure waits for all loads, disposes staged objects, and reports the first error in document order.
|
|
16029
|
+
*/
|
|
16030
|
+
static async load(scene, value, parent = scene) {
|
|
16031
|
+
const definition = this.parse(value);
|
|
16032
|
+
if (definition.minecraftVersion !== void 0 && definition.minecraftVersion !== AssetLoader.version) {
|
|
16033
|
+
throw new Error(`Scene requires Minecraft ${definition.minecraftVersion}; call AssetLoader.setVersion("${definition.minecraftVersion}") before loading it`);
|
|
16034
|
+
}
|
|
16035
|
+
const root = new import_three38.Group();
|
|
16036
|
+
root.name = "MineRender scene document";
|
|
16037
|
+
const objects = [];
|
|
16038
|
+
let disposed = false;
|
|
16039
|
+
const loaded = {
|
|
16040
|
+
root,
|
|
16041
|
+
objects,
|
|
16042
|
+
definition,
|
|
16043
|
+
advanceAnimations: (delta) => {
|
|
16044
|
+
if (!disposed) for (const object3 of objects) object3.advanceAnimation(delta);
|
|
16045
|
+
},
|
|
16046
|
+
dispose: () => {
|
|
16047
|
+
if (disposed) return;
|
|
16048
|
+
disposed = true;
|
|
16049
|
+
root.removeFromParent();
|
|
16050
|
+
for (const object3 of objects) object3.dispose();
|
|
16051
|
+
root.clear();
|
|
16052
|
+
scene.dirty = true;
|
|
16053
|
+
}
|
|
16054
|
+
};
|
|
16055
|
+
try {
|
|
16056
|
+
const results = await Promise.allSettled(definition.objects.map((object3) => this.loadValidatedObject(scene, object3)));
|
|
16057
|
+
for (const result of results) if (result.status === "fulfilled") objects.push(result.value);
|
|
16058
|
+
const failure = results.find((result) => result.status === "rejected");
|
|
16059
|
+
if (failure) throw failure.reason;
|
|
16060
|
+
for (const object3 of objects) root.add(object3.root);
|
|
16061
|
+
parent.add(root);
|
|
16062
|
+
scene.dirty = true;
|
|
16063
|
+
return loaded;
|
|
16064
|
+
} catch (error) {
|
|
16065
|
+
loaded.dispose();
|
|
16066
|
+
throw error;
|
|
16067
|
+
}
|
|
16068
|
+
}
|
|
16069
|
+
/** Loads an independently editable object. Its root owns the document's position, rotation, and scale. */
|
|
16070
|
+
static async loadObject(scene, value, parent = scene) {
|
|
16071
|
+
const definition = this.parse({ format: "minerender-scene", version: 1, objects: [value] }).objects[0];
|
|
16072
|
+
return this.loadValidatedObject(scene, definition, parent);
|
|
16073
|
+
}
|
|
16074
|
+
static async loadValidatedObject(scene, definition, parent) {
|
|
16075
|
+
const root = new import_three38.Group();
|
|
16076
|
+
root.name = definition.name ?? definition.id;
|
|
16077
|
+
root.userData.sceneObjectId = definition.id;
|
|
16078
|
+
root.position.fromArray(definition.position ?? [0, 0, 0]);
|
|
16079
|
+
root.rotation.set(...definition.rotation ?? [0, 0, 0]);
|
|
16080
|
+
root.scale.fromArray(definition.scale ?? [1, 1, 1]);
|
|
16081
|
+
root.visible = definition.visible ?? true;
|
|
16082
|
+
const content = new DocumentObjectScene();
|
|
16083
|
+
root.add(content);
|
|
16084
|
+
let object3;
|
|
16085
|
+
try {
|
|
16086
|
+
switch (definition.type) {
|
|
16087
|
+
case "skin": {
|
|
16088
|
+
const skin = new SkinObject({ ...definition.options, instanceMeshes: false });
|
|
16089
|
+
object3 = skin;
|
|
16090
|
+
const texture = definition.skin ? await skinTexture(definition.skin) : `${AssetLoader.ROOT}/assets/minecraft/textures/entity/player/${definition.options?.slim ? "slim/alex" : "wide/steve"}.png`;
|
|
16091
|
+
await skin.setSkinTexture(texture);
|
|
16092
|
+
await content.initialize(skin);
|
|
16093
|
+
if (definition.cape) await skin.setCapeTexture(await skinTexture(definition.cape.texture, true), definition.cape.layout);
|
|
16094
|
+
for (const [part, rotation] of Object.entries(definition.pose ?? {})) {
|
|
16095
|
+
skin.getGroupByName(part)?.rotation.set(...rotation, "XYZ");
|
|
16096
|
+
}
|
|
16097
|
+
for (const part of definition.hiddenParts ?? []) {
|
|
16098
|
+
const mesh = skin.getMeshByName(part);
|
|
16099
|
+
if (mesh) mesh.visible = false;
|
|
16100
|
+
}
|
|
16101
|
+
break;
|
|
16102
|
+
}
|
|
16103
|
+
case "block": {
|
|
16104
|
+
const state = await BlockStates.get(AssetKey.parse("blockstates", definition.asset));
|
|
16105
|
+
if (!state) throw new Error(`Could not load block ${definition.asset}`);
|
|
16106
|
+
object3 = await content.addBlock(state, { ...definition.options, initialState: definition.state, instanceMeshes: false });
|
|
16107
|
+
break;
|
|
16108
|
+
}
|
|
16109
|
+
case "item":
|
|
16110
|
+
case "model": {
|
|
16111
|
+
let key = AssetKey.parse("models", definition.asset);
|
|
16112
|
+
if (definition.type === "item" && key.type !== "item") {
|
|
16113
|
+
key = new AssetKey(key.namespace, key.getFullPath(), "models", "item");
|
|
16114
|
+
}
|
|
16115
|
+
const model = await Models.getMerged(key);
|
|
16116
|
+
if (!model) throw new Error(`Could not load ${definition.type} ${definition.asset}`);
|
|
16117
|
+
object3 = await content.addModel(model, {
|
|
16118
|
+
...definition.type === "item" ? { displayPosition: "gui" /* GUI */ } : {},
|
|
16119
|
+
...definition.options,
|
|
16120
|
+
instanceMeshes: false
|
|
16121
|
+
});
|
|
16122
|
+
break;
|
|
16123
|
+
}
|
|
16124
|
+
case "entity": {
|
|
16125
|
+
const key = AssetKey.parse("entity-models", definition.asset);
|
|
16126
|
+
const model = await Entities.getEntity(key, definition.texture ? AssetKey.parse("textures", definition.texture) : void 0, {
|
|
16127
|
+
layers: definition.layers,
|
|
16128
|
+
when: definition.when,
|
|
16129
|
+
textures: definition.textures && Object.fromEntries(Object.entries(definition.textures).map(([name, texture]) => [name, AssetKey.parse("textures", texture)]))
|
|
16130
|
+
});
|
|
16131
|
+
if (!model) throw new Error(`Could not load entity ${definition.asset}`);
|
|
16132
|
+
const entity = await content.addEntity(model, { ...definition.options, instanceMeshes: false });
|
|
16133
|
+
object3 = entity;
|
|
16134
|
+
if (definition.animation) {
|
|
16135
|
+
const animations = await Entities.getAnimations(key);
|
|
16136
|
+
const clip = animations?.[definition.animation.name];
|
|
16137
|
+
if (!clip) throw new Error(`Entity ${definition.asset} has no animation "${definition.animation.name}"`);
|
|
16138
|
+
entity.playAnimation(clip, definition.animation);
|
|
16139
|
+
}
|
|
16140
|
+
break;
|
|
16141
|
+
}
|
|
16142
|
+
case "gui":
|
|
16143
|
+
object3 = await content.initialize(new GuiObject(definition.layers));
|
|
16144
|
+
break;
|
|
16145
|
+
}
|
|
16146
|
+
let disposed = false;
|
|
16147
|
+
const loaded = {
|
|
16148
|
+
root,
|
|
16149
|
+
object: object3,
|
|
16150
|
+
definition,
|
|
16151
|
+
advanceAnimation: (delta) => {
|
|
16152
|
+
if (!disposed && definition.type === "entity" && !definition.animation?.paused) object3.advanceAnimation(delta);
|
|
16153
|
+
},
|
|
16154
|
+
dispose: () => {
|
|
16155
|
+
if (disposed) return;
|
|
16156
|
+
disposed = true;
|
|
16157
|
+
root.removeFromParent();
|
|
16158
|
+
object3.dispose();
|
|
16159
|
+
object3.removeFromParent();
|
|
16160
|
+
content.dispose();
|
|
16161
|
+
root.clear();
|
|
16162
|
+
scene.dirty = true;
|
|
16163
|
+
}
|
|
16164
|
+
};
|
|
16165
|
+
parent?.add(root);
|
|
16166
|
+
scene.dirty = true;
|
|
16167
|
+
return loaded;
|
|
16168
|
+
} catch (error) {
|
|
16169
|
+
object3?.dispose();
|
|
16170
|
+
object3?.removeFromParent();
|
|
16171
|
+
content.dispose();
|
|
16172
|
+
root.removeFromParent();
|
|
16173
|
+
root.clear();
|
|
16174
|
+
throw new Error(`Scene object "${definition.name ?? definition.id}": ${error instanceof Error ? error.message : String(error)}`);
|
|
16175
|
+
}
|
|
16176
|
+
}
|
|
16177
|
+
};
|
|
16178
|
+
|
|
14725
16179
|
// src/texture/index.ts
|
|
14726
16180
|
init_Colormaps();
|
|
14727
16181
|
init_TextureAtlas();
|
|
@@ -14748,16 +16202,19 @@ var ChunkData = class _ChunkData {
|
|
|
14748
16202
|
this.freeIds = [];
|
|
14749
16203
|
this.nbt = /* @__PURE__ */ new Map();
|
|
14750
16204
|
}
|
|
16205
|
+
/** Returns a detached copy of a cell, or `undefined` for air. Indices range from 0 to 4095. */
|
|
14751
16206
|
get(index) {
|
|
14752
16207
|
_ChunkData.validateIndex(index);
|
|
14753
16208
|
const entry = this.palette[this.indices[index]];
|
|
14754
16209
|
return entry ? _ChunkData.copyBlock(entry.state, this.nbt.get(index)) : void 0;
|
|
14755
16210
|
}
|
|
16211
|
+
/** Captures a cell's data and returns a copy-producing reader that survives later cell changes. */
|
|
14756
16212
|
snapshot(index) {
|
|
14757
16213
|
_ChunkData.validateIndex(index);
|
|
14758
16214
|
const entry = this.palette[this.indices[index]];
|
|
14759
16215
|
return entry ? _ChunkData.copyBlock.bind(void 0, entry.state, this.nbt.get(index)) : void 0;
|
|
14760
16216
|
}
|
|
16217
|
+
/** Copies block data into a cell. An omitted block or an air block clears the cell. */
|
|
14761
16218
|
set(index, block) {
|
|
14762
16219
|
_ChunkData.validateIndex(index);
|
|
14763
16220
|
let state;
|
|
@@ -14822,15 +16279,23 @@ var ChunkData = class _ChunkData {
|
|
|
14822
16279
|
|
|
14823
16280
|
// src/world/AnvilParser.ts
|
|
14824
16281
|
var AnvilParser = class {
|
|
16282
|
+
/** Lists occupied chunk positions within the region, using local coordinates from 0 to 31. */
|
|
14825
16283
|
static getChunkList(data) {
|
|
14826
16284
|
return this.locations(this.bytes(data)).map(({ x, z }) => ({ x, z }));
|
|
14827
16285
|
}
|
|
16286
|
+
/** Decodes all stored chunks. Gzip, zlib, and uncompressed payloads are supported. */
|
|
14828
16287
|
static async parse(data) {
|
|
14829
16288
|
const bytes = this.bytes(data);
|
|
14830
16289
|
const chunks = [];
|
|
14831
16290
|
for (const location of this.locations(bytes)) chunks.push(await this.readChunk(bytes, location));
|
|
14832
16291
|
return { chunks };
|
|
14833
16292
|
}
|
|
16293
|
+
/**
|
|
16294
|
+
* Decodes one chunk for {@link MineRenderWorld.placeChunk}.
|
|
16295
|
+
* @param localX - Chunk x within the region, from 0 to 31.
|
|
16296
|
+
* @param localZ - Chunk z within the region, from 0 to 31.
|
|
16297
|
+
* @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
|
|
16298
|
+
*/
|
|
14834
16299
|
static async parseChunk(data, localX, localZ) {
|
|
14835
16300
|
if (![localX, localZ].every((value) => Number.isInteger(value) && value >= 0 && value < 32)) {
|
|
14836
16301
|
throw new RangeError("Region-local chunk coordinates must be integers from 0 to 31");
|
|
@@ -14992,12 +16457,12 @@ var AnvilParser = class {
|
|
|
14992
16457
|
};
|
|
14993
16458
|
|
|
14994
16459
|
// src/world/Chunk.ts
|
|
14995
|
-
var
|
|
16460
|
+
var import_three42 = require("three");
|
|
14996
16461
|
init_BlockStates();
|
|
14997
16462
|
init_AssetKey();
|
|
14998
16463
|
|
|
14999
16464
|
// src/world/MineRenderWorld.ts
|
|
15000
|
-
var
|
|
16465
|
+
var import_three40 = require("three");
|
|
15001
16466
|
init_Model();
|
|
15002
16467
|
init_BatchedExecutor();
|
|
15003
16468
|
init_AssetKey();
|
|
@@ -15005,7 +16470,7 @@ init_BlockStates();
|
|
|
15005
16470
|
init_CubeFace();
|
|
15006
16471
|
|
|
15007
16472
|
// src/world/SectionModels.ts
|
|
15008
|
-
var
|
|
16473
|
+
var import_three39 = require("three");
|
|
15009
16474
|
init_AssetKey();
|
|
15010
16475
|
init_Models();
|
|
15011
16476
|
init_CubeFace();
|
|
@@ -15035,6 +16500,7 @@ var SectionModels = class {
|
|
|
15035
16500
|
this.templates = /* @__PURE__ */ new Set();
|
|
15036
16501
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
|
|
15037
16502
|
}
|
|
16503
|
+
/** Returns a cube template, or `undefined` when the block requires an individual render object. */
|
|
15038
16504
|
async get(blockState, properties = {}) {
|
|
15039
16505
|
if (!blockState.variants || blockState.multipart) return void 0;
|
|
15040
16506
|
let states = this.states.get(blockState);
|
|
@@ -15063,20 +16529,20 @@ var SectionModels = class {
|
|
|
15063
16529
|
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
15064
16530
|
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
15065
16531
|
const template = await cached(models, key, async () => {
|
|
15066
|
-
const
|
|
16532
|
+
const object3 = new SectionModel(model, {
|
|
15067
16533
|
instanceMeshes: false,
|
|
15068
16534
|
mergeMeshes: false,
|
|
15069
16535
|
tints,
|
|
15070
16536
|
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
15071
16537
|
});
|
|
15072
|
-
await
|
|
15073
|
-
const geometry =
|
|
15074
|
-
geometry.applyMatrix4(new
|
|
15075
|
-
|
|
16538
|
+
await object3.init();
|
|
16539
|
+
const geometry = object3.children[0].geometry;
|
|
16540
|
+
geometry.applyMatrix4(new import_three39.Matrix4().makeRotationFromEuler(rotation));
|
|
16541
|
+
object3.clear();
|
|
15076
16542
|
const cullFaces = CUBE_FACES.map((face) => {
|
|
15077
16543
|
const local = CUBE_FACES.findIndex((name) => name === atlas.model.elements[0].faces[face]?.cullface);
|
|
15078
16544
|
if (local < 0) return 0;
|
|
15079
|
-
const normal = new
|
|
16545
|
+
const normal = new import_three39.Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
|
|
15080
16546
|
const world = CUBE_FACE_OFFSETS.findIndex(([x, y, z]) => Math.abs(normal.x - x) < 1e-6 && Math.abs(normal.y - y) < 1e-6 && Math.abs(normal.z - z) < 1e-6);
|
|
15081
16547
|
return 1 << world;
|
|
15082
16548
|
});
|
|
@@ -15088,6 +16554,7 @@ var SectionModels = class {
|
|
|
15088
16554
|
}
|
|
15089
16555
|
return { variants, templates };
|
|
15090
16556
|
}
|
|
16557
|
+
/** Disposes prepared geometry and clears this world's template caches. */
|
|
15091
16558
|
clear() {
|
|
15092
16559
|
for (const template of this.templates) template.geometry.dispose();
|
|
15093
16560
|
this.templates.clear();
|
|
@@ -15106,20 +16573,20 @@ var MineRenderWorld = class {
|
|
|
15106
16573
|
}
|
|
15107
16574
|
getBlockAt(posOrX, y, z) {
|
|
15108
16575
|
if (typeof posOrX == "number") {
|
|
15109
|
-
return this.getBlockAt(new
|
|
16576
|
+
return this.getBlockAt(new import_three40.Vector3(posOrX, y, z));
|
|
15110
16577
|
}
|
|
15111
16578
|
if (isTripleArray(posOrX)) {
|
|
15112
|
-
return this.getBlockAt(new
|
|
16579
|
+
return this.getBlockAt(new import_three40.Vector3(posOrX[0], posOrX[1], posOrX[2]));
|
|
15113
16580
|
}
|
|
15114
16581
|
this.validatePosBounds(posOrX);
|
|
15115
16582
|
return this.getChunkAt(posOrX)?.getBlockAt(posOrX);
|
|
15116
16583
|
}
|
|
15117
16584
|
async setBlockAt(posOrX, yOrBlock, z, block) {
|
|
15118
16585
|
if (typeof posOrX == "number") {
|
|
15119
|
-
return this.setBlockAt(new
|
|
16586
|
+
return this.setBlockAt(new import_three40.Vector3(posOrX, yOrBlock, z), block);
|
|
15120
16587
|
}
|
|
15121
16588
|
if (isTripleArray(posOrX)) {
|
|
15122
|
-
return this.setBlockAt(new
|
|
16589
|
+
return this.setBlockAt(new import_three40.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
|
|
15123
16590
|
}
|
|
15124
16591
|
return this.placeBlock(posOrX, yOrBlock);
|
|
15125
16592
|
}
|
|
@@ -15130,14 +16597,21 @@ var MineRenderWorld = class {
|
|
|
15130
16597
|
}
|
|
15131
16598
|
async setBlockVisibleAt(posOrX, yOrVisible, z, visible) {
|
|
15132
16599
|
if (typeof posOrX === "number") {
|
|
15133
|
-
return this.setBlockVisibleAt(new
|
|
16600
|
+
return this.setBlockVisibleAt(new import_three40.Vector3(posOrX, yOrVisible, z), visible);
|
|
15134
16601
|
}
|
|
15135
16602
|
if (isTripleArray(posOrX)) {
|
|
15136
|
-
return this.setBlockVisibleAt(new
|
|
16603
|
+
return this.setBlockVisibleAt(new import_three40.Vector3(...posOrX), yOrVisible);
|
|
15137
16604
|
}
|
|
15138
16605
|
this.validatePosBounds(posOrX);
|
|
15139
16606
|
await this.getChunkAt(posOrX)?.setBlockVisibleAt(posOrX, yOrVisible);
|
|
15140
16607
|
}
|
|
16608
|
+
/**
|
|
16609
|
+
* Places a structure's blocks at their stored positions, then refreshes neighboring faces.
|
|
16610
|
+
* Entity NBT is retained by the structure but is not rendered.
|
|
16611
|
+
*
|
|
16612
|
+
* @param useBatches - Uses bounded batches by default. Set to `false` for sequential placement.
|
|
16613
|
+
* @param executor - Optional queue for batched placement. The caller remains responsible for stopping it.
|
|
16614
|
+
*/
|
|
15141
16615
|
async placeMultiBlock(multiblock, useBatches = true, executor) {
|
|
15142
16616
|
const changes = /* @__PURE__ */ new Map();
|
|
15143
16617
|
try {
|
|
@@ -15150,7 +16624,7 @@ var MineRenderWorld = class {
|
|
|
15150
16624
|
const collect = async (positions2) => {
|
|
15151
16625
|
for (const pos of positions2) changes.set(pos.toArray().join(","), pos);
|
|
15152
16626
|
};
|
|
15153
|
-
const place = (block) => this.placeBlock(new
|
|
16627
|
+
const place = (block) => this.placeBlock(new import_three40.Vector3(...block.position), block, collect);
|
|
15154
16628
|
const keys = /* @__PURE__ */ new Map();
|
|
15155
16629
|
for (const block of multiblock.blocks) {
|
|
15156
16630
|
if (Chunk.isAir(block)) continue;
|
|
@@ -15189,13 +16663,7 @@ var MineRenderWorld = class {
|
|
|
15189
16663
|
async placeChunk(chunk, executor) {
|
|
15190
16664
|
const changes = /* @__PURE__ */ new Map();
|
|
15191
16665
|
try {
|
|
15192
|
-
|
|
15193
|
-
for (const [key, section] of previous) {
|
|
15194
|
-
this._chunks.delete(key);
|
|
15195
|
-
await section.clear(async (positions) => {
|
|
15196
|
-
for (const pos of positions) changes.set(pos.toArray().join(","), pos);
|
|
15197
|
-
});
|
|
15198
|
-
}
|
|
16666
|
+
await this.clearChunkColumn(chunk.x, chunk.z, changes);
|
|
15199
16667
|
for (const section of chunk.sections) {
|
|
15200
16668
|
const blocks = [];
|
|
15201
16669
|
for (let index = 0; index < 4096; index++) {
|
|
@@ -15215,6 +16683,32 @@ var MineRenderWorld = class {
|
|
|
15215
16683
|
await this.updateCulling([...changes.values()]);
|
|
15216
16684
|
}
|
|
15217
16685
|
}
|
|
16686
|
+
/**
|
|
16687
|
+
* Removes all sections at integer chunk-column coordinates, then refreshes neighboring faces and fluids.
|
|
16688
|
+
* Coordinates match AnvilChunk.x/z, not block positions. Missing columns are ignored.
|
|
16689
|
+
*/
|
|
16690
|
+
async unloadChunkColumn(x, z) {
|
|
16691
|
+
if (!Number.isInteger(x) || !Number.isInteger(z)) {
|
|
16692
|
+
throw new RangeError("Chunk column coordinates must be integers");
|
|
16693
|
+
}
|
|
16694
|
+
await this.culling;
|
|
16695
|
+
const changes = /* @__PURE__ */ new Map();
|
|
16696
|
+
try {
|
|
16697
|
+
await this.clearChunkColumn(x, z, changes);
|
|
16698
|
+
} finally {
|
|
16699
|
+
await this.updateCulling([...changes.values()]);
|
|
16700
|
+
}
|
|
16701
|
+
}
|
|
16702
|
+
async clearChunkColumn(x, z, changes) {
|
|
16703
|
+
const previous = [...this._chunks.entries()].filter(([, section]) => section.x === x && section.z === z);
|
|
16704
|
+
for (const [key, section] of previous) {
|
|
16705
|
+
this._chunks.delete(key);
|
|
16706
|
+
await section.clear(async (positions) => {
|
|
16707
|
+
for (const pos of positions) changes.set(pos.toArray().join(","), pos);
|
|
16708
|
+
});
|
|
16709
|
+
}
|
|
16710
|
+
}
|
|
16711
|
+
/** Removes all chunks and releases their block placements and section meshes. */
|
|
15218
16712
|
async clear() {
|
|
15219
16713
|
await this.culling;
|
|
15220
16714
|
const chunks = [...this._chunks.values()];
|
|
@@ -15228,13 +16722,13 @@ var MineRenderWorld = class {
|
|
|
15228
16722
|
for (const pos of positions) {
|
|
15229
16723
|
this.pendingCulling.set(pos.toArray().join(","), pos.clone());
|
|
15230
16724
|
for (const offset of CUBE_FACE_OFFSETS) {
|
|
15231
|
-
const neighbor = pos.clone().add(new
|
|
16725
|
+
const neighbor = pos.clone().add(new import_three40.Vector3(...offset));
|
|
15232
16726
|
this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
|
|
15233
16727
|
}
|
|
15234
16728
|
for (let y = -1; y <= 1; y++) {
|
|
15235
16729
|
for (let z = -1; z <= 1; z++) {
|
|
15236
16730
|
for (let x = -1; x <= 1; x++) {
|
|
15237
|
-
const neighbor = pos.clone().add(new
|
|
16731
|
+
const neighbor = pos.clone().add(new import_three40.Vector3(x, y, z));
|
|
15238
16732
|
if (this.getBlockAt(neighbor)?.object?.fluidKind) {
|
|
15239
16733
|
this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
|
|
15240
16734
|
}
|
|
@@ -15258,19 +16752,19 @@ var MineRenderWorld = class {
|
|
|
15258
16752
|
}
|
|
15259
16753
|
if (block.object?.fluidKind) {
|
|
15260
16754
|
await block.object.updateFluid((x, y, z) => {
|
|
15261
|
-
const neighbor = pos.clone().add(new
|
|
16755
|
+
const neighbor = pos.clone().add(new import_three40.Vector3(x, y, z));
|
|
15262
16756
|
const section = this.getChunkAt(neighbor);
|
|
15263
|
-
const
|
|
16757
|
+
const object3 = section?.isBlockVisibleAt(neighbor) ? section.getBlockAt(neighbor)?.object : void 0;
|
|
15264
16758
|
return {
|
|
15265
|
-
fluid:
|
|
15266
|
-
level:
|
|
16759
|
+
fluid: object3?.fluidKind,
|
|
16760
|
+
level: object3?.fluidLevel,
|
|
15267
16761
|
solid: section?.isOccludingAt(neighbor) ?? false
|
|
15268
16762
|
};
|
|
15269
16763
|
});
|
|
15270
16764
|
}
|
|
15271
16765
|
let mask = 0;
|
|
15272
16766
|
for (const [face, offset] of CUBE_FACE_OFFSETS.entries()) {
|
|
15273
|
-
const neighbor = pos.clone().add(new
|
|
16767
|
+
const neighbor = pos.clone().add(new import_three40.Vector3(...offset));
|
|
15274
16768
|
if (this.getChunkAt(neighbor)?.isOccludingAt(neighbor)) {
|
|
15275
16769
|
mask |= 1 << face;
|
|
15276
16770
|
}
|
|
@@ -15300,18 +16794,21 @@ var MineRenderWorld = class {
|
|
|
15300
16794
|
}
|
|
15301
16795
|
return chunk;
|
|
15302
16796
|
}
|
|
16797
|
+
/** Returns the loaded chunk containing a world block position, without creating a chunk. */
|
|
15303
16798
|
getChunkAt(pos) {
|
|
15304
16799
|
return this._chunks.get(this.worldPosToChunkKey(pos));
|
|
15305
16800
|
}
|
|
16801
|
+
/** Converts block coordinates to scene units by multiplying each component by 16. */
|
|
15306
16802
|
static worldToScenePosition(pos) {
|
|
15307
|
-
return new
|
|
16803
|
+
return new import_three40.Vector3(
|
|
15308
16804
|
pos.x * 16,
|
|
15309
16805
|
pos.y * 16,
|
|
15310
16806
|
pos.z * 16
|
|
15311
16807
|
);
|
|
15312
16808
|
}
|
|
16809
|
+
/** Converts scene units to block coordinates without rounding to an integer block. */
|
|
15313
16810
|
static sceneToWorldPosition(pos) {
|
|
15314
|
-
return new
|
|
16811
|
+
return new import_three40.Vector3(
|
|
15315
16812
|
pos.x / 16,
|
|
15316
16813
|
pos.y / 16,
|
|
15317
16814
|
pos.z / 16
|
|
@@ -15334,14 +16831,15 @@ var MineRenderWorld = class {
|
|
|
15334
16831
|
init_Model();
|
|
15335
16832
|
|
|
15336
16833
|
// src/world/SectionMesh.ts
|
|
15337
|
-
var
|
|
16834
|
+
var import_three41 = require("three");
|
|
15338
16835
|
init_CanvasCompat();
|
|
15339
16836
|
init_Materials();
|
|
15340
|
-
var SectionMesh = class _SectionMesh extends
|
|
16837
|
+
var SectionMesh = class _SectionMesh extends import_three41.Group {
|
|
15341
16838
|
constructor() {
|
|
15342
16839
|
super(...arguments);
|
|
15343
16840
|
this.ownedMeshes = [];
|
|
15344
16841
|
}
|
|
16842
|
+
/** Builds section-local meshes from visible cube faces. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
15345
16843
|
static build(entries, maxAtlasSize = 2048) {
|
|
15346
16844
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
15347
16845
|
const visible = entries.filter((entry) => entry.template.cullFaces.some((direction) => !(entry.cullMask & direction)));
|
|
@@ -15383,14 +16881,17 @@ var SectionMesh = class _SectionMesh extends import_three40.Group {
|
|
|
15383
16881
|
for (const { atlas, x, y } of page.placements) {
|
|
15384
16882
|
context.drawImage(atlas.image.canvas, x, y);
|
|
15385
16883
|
}
|
|
15386
|
-
const positions = [], normals = [], uvs = [], colors = [], indices = [];
|
|
16884
|
+
const positions = [], normals = [], uvs = [], uvBounds = [], colors = [], indices = [];
|
|
15387
16885
|
for (const { index, template, cullMask } of page.entries) {
|
|
15388
16886
|
const position = template.geometry.getAttribute("position");
|
|
15389
16887
|
const normal = template.geometry.getAttribute("normal");
|
|
15390
16888
|
const uv = template.geometry.getAttribute("uv");
|
|
15391
|
-
const
|
|
16889
|
+
const bounds = template.geometry.getAttribute("uvBounds");
|
|
16890
|
+
const color2 = template.geometry.getAttribute("color");
|
|
15392
16891
|
const sourceIndices = template.geometry.getIndex();
|
|
15393
16892
|
const placement = locations.get(template.atlas).placement;
|
|
16893
|
+
const mapU = (u) => (placement.x + u * template.atlas.image.width) / page.width;
|
|
16894
|
+
const mapV = (v) => 1 - (placement.y + (1 - v) * template.atlas.image.height) / page.height;
|
|
15394
16895
|
const offsetX = index % 16 * 16;
|
|
15395
16896
|
const offsetY = Math.floor(index / 256) * 16;
|
|
15396
16897
|
const offsetZ = Math.floor(index / 16) % 16 * 16;
|
|
@@ -15401,27 +16902,31 @@ var SectionMesh = class _SectionMesh extends import_three40.Group {
|
|
|
15401
16902
|
const vertex = face * 4 + corner;
|
|
15402
16903
|
positions.push(position.getX(vertex) + offsetX, position.getY(vertex) + offsetY, position.getZ(vertex) + offsetZ);
|
|
15403
16904
|
normals.push(normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex));
|
|
15404
|
-
uvs.push(
|
|
15405
|
-
|
|
15406
|
-
|
|
16905
|
+
uvs.push(mapU(uv.getX(vertex)), mapV(uv.getY(vertex)));
|
|
16906
|
+
uvBounds.push(
|
|
16907
|
+
mapU(bounds?.getX(vertex) ?? 0),
|
|
16908
|
+
mapV(bounds?.getY(vertex) ?? 0),
|
|
16909
|
+
mapU(bounds?.getZ(vertex) ?? 1),
|
|
16910
|
+
mapV(bounds?.getW(vertex) ?? 1)
|
|
15407
16911
|
);
|
|
15408
|
-
colors.push(
|
|
16912
|
+
colors.push(color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1);
|
|
15409
16913
|
}
|
|
15410
16914
|
for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
|
|
15411
16915
|
}
|
|
15412
16916
|
}
|
|
15413
|
-
const geometry = new
|
|
15414
|
-
geometry.setAttribute("position", new
|
|
15415
|
-
geometry.setAttribute("normal", new
|
|
15416
|
-
geometry.setAttribute("uv", new
|
|
15417
|
-
geometry.setAttribute("
|
|
16917
|
+
const geometry = new import_three41.BufferGeometry();
|
|
16918
|
+
geometry.setAttribute("position", new import_three41.Float32BufferAttribute(positions, 3));
|
|
16919
|
+
geometry.setAttribute("normal", new import_three41.Float32BufferAttribute(normals, 3));
|
|
16920
|
+
geometry.setAttribute("uv", new import_three41.Float32BufferAttribute(uvs, 2));
|
|
16921
|
+
geometry.setAttribute("uvBounds", new import_three41.Float32BufferAttribute(uvBounds, 4));
|
|
16922
|
+
geometry.setAttribute("color", new import_three41.Float32BufferAttribute(colors, 3));
|
|
15418
16923
|
geometry.setIndex(indices);
|
|
15419
16924
|
geometry.computeBoundingBox();
|
|
15420
16925
|
geometry.computeBoundingSphere();
|
|
15421
|
-
const material = Materials.createShadedCanvasMaterial(canvas, false);
|
|
16926
|
+
const material = Materials.createShadedCanvasMaterial(canvas, false, false, true);
|
|
15422
16927
|
material.vertexColors = true;
|
|
15423
16928
|
const texture = material.uniforms?.map?.value ?? material.map;
|
|
15424
|
-
const mesh = new
|
|
16929
|
+
const mesh = new import_three41.Mesh(geometry, material);
|
|
15425
16930
|
section.add(mesh);
|
|
15426
16931
|
section.ownedMeshes.push({ mesh, texture });
|
|
15427
16932
|
}
|
|
@@ -15457,10 +16962,10 @@ var Chunk = class _Chunk {
|
|
|
15457
16962
|
}
|
|
15458
16963
|
getBlockAt(posOrX, y, z) {
|
|
15459
16964
|
if (typeof posOrX == "number") {
|
|
15460
|
-
return this.getBlockAt(new
|
|
16965
|
+
return this.getBlockAt(new import_three42.Vector3(posOrX, y, z));
|
|
15461
16966
|
}
|
|
15462
16967
|
if (isTripleArray(posOrX)) {
|
|
15463
|
-
return this.getBlockAt(new
|
|
16968
|
+
return this.getBlockAt(new import_three42.Vector3(posOrX[0], posOrX[1], posOrX[2]));
|
|
15464
16969
|
}
|
|
15465
16970
|
const pos = this.worldPosToChunkPos(posOrX);
|
|
15466
16971
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
@@ -15468,10 +16973,10 @@ var Chunk = class _Chunk {
|
|
|
15468
16973
|
}
|
|
15469
16974
|
async setBlockAt(posOrX, yOrBlock, z, block) {
|
|
15470
16975
|
if (typeof posOrX == "number") {
|
|
15471
|
-
return this.setBlockAt(new
|
|
16976
|
+
return this.setBlockAt(new import_three42.Vector3(posOrX, yOrBlock, z), block);
|
|
15472
16977
|
}
|
|
15473
16978
|
if (isTripleArray(posOrX)) {
|
|
15474
|
-
return this.setBlockAt(new
|
|
16979
|
+
return this.setBlockAt(new import_three42.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
|
|
15475
16980
|
}
|
|
15476
16981
|
const worldPos = posOrX;
|
|
15477
16982
|
const pos = this.worldPosToChunkPos(worldPos);
|
|
@@ -15479,7 +16984,7 @@ var Chunk = class _Chunk {
|
|
|
15479
16984
|
return this.setBlockInChunkAt(pos, block, worldPos);
|
|
15480
16985
|
}
|
|
15481
16986
|
/**
|
|
15482
|
-
*
|
|
16987
|
+
* Places a block at integer chunk-local coordinates from 0 to 15.
|
|
15483
16988
|
*/
|
|
15484
16989
|
async setBlockInChunkAt(pos, block, worldPos, onBlocksChanged = this.onBlocksChanged) {
|
|
15485
16990
|
if (typeof worldPos === "undefined") {
|
|
@@ -15492,7 +16997,7 @@ var Chunk = class _Chunk {
|
|
|
15492
16997
|
this.renderedBlocks.get(index)?.object?.removeFromScene();
|
|
15493
16998
|
if (this.sectionBlocks.delete(index)) this.meshDirty = true;
|
|
15494
16999
|
this.renderedBlocks.delete(index);
|
|
15495
|
-
let
|
|
17000
|
+
let object3;
|
|
15496
17001
|
try {
|
|
15497
17002
|
if (!readBlock) return void 0;
|
|
15498
17003
|
const stored = readBlock();
|
|
@@ -15507,27 +17012,28 @@ var Chunk = class _Chunk {
|
|
|
15507
17012
|
this.sectionBlocks.set(index, { index, template, cullMask: 0 });
|
|
15508
17013
|
this.meshDirty = true;
|
|
15509
17014
|
} else {
|
|
15510
|
-
|
|
17015
|
+
object3 = await this.scene.addBlock(blockState, {
|
|
15511
17016
|
mergeMeshes: true,
|
|
15512
17017
|
instanceMeshes: true,
|
|
15513
17018
|
maxInstanceCount: 2e3,
|
|
15514
17019
|
initialState: stored.properties
|
|
15515
17020
|
});
|
|
15516
|
-
|
|
17021
|
+
object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
15517
17022
|
}
|
|
15518
17023
|
const info = { get block() {
|
|
15519
17024
|
return readBlock();
|
|
15520
|
-
}, object };
|
|
17025
|
+
}, object: object3 };
|
|
15521
17026
|
this.renderedBlocks.set(index, info);
|
|
15522
17027
|
return info;
|
|
15523
17028
|
} catch (error) {
|
|
15524
17029
|
this.data.set(index, void 0);
|
|
15525
|
-
|
|
17030
|
+
object3?.removeFromScene();
|
|
15526
17031
|
throw error;
|
|
15527
17032
|
} finally {
|
|
15528
17033
|
await onBlocksChanged?.([worldPos]);
|
|
15529
17034
|
}
|
|
15530
17035
|
}
|
|
17036
|
+
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
15531
17037
|
async setBlockVisibleAt(pos, visible) {
|
|
15532
17038
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
15533
17039
|
const block = this.renderedBlocks.get(index);
|
|
@@ -15581,9 +17087,10 @@ var Chunk = class _Chunk {
|
|
|
15581
17087
|
this.scene.dirty = true;
|
|
15582
17088
|
this.meshDirty = false;
|
|
15583
17089
|
}
|
|
17090
|
+
/** Removes stored blocks and their render objects, then notifies the owning world of changed positions. */
|
|
15584
17091
|
async clear(onBlocksChanged = this.onBlocksChanged) {
|
|
15585
17092
|
const positions = [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
|
|
15586
|
-
new
|
|
17093
|
+
new import_three42.Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
|
|
15587
17094
|
));
|
|
15588
17095
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
15589
17096
|
this.sectionBlocks.clear();
|
|
@@ -15601,6 +17108,7 @@ var Chunk = class _Chunk {
|
|
|
15601
17108
|
static isAir(block) {
|
|
15602
17109
|
return ChunkData.isAir(block);
|
|
15603
17110
|
}
|
|
17111
|
+
/** Maps chunk-local coordinates from 0 to 15 to a storage index, with x varying fastest, then z, then y. */
|
|
15604
17112
|
static chunkPosToBlockIndex(pos) {
|
|
15605
17113
|
if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
|
|
15606
17114
|
throw new RangeError("Block coordinates must be integers from 0 to 15 within a chunk");
|
|
@@ -15608,14 +17116,14 @@ var Chunk = class _Chunk {
|
|
|
15608
17116
|
return pos.y * 16 * 16 + pos.z * 16 + pos.x;
|
|
15609
17117
|
}
|
|
15610
17118
|
worldPosToChunkPos(pos) {
|
|
15611
|
-
return new
|
|
17119
|
+
return new import_three42.Vector3(
|
|
15612
17120
|
pos.x - this.x * 16,
|
|
15613
17121
|
pos.y - this.y * 16,
|
|
15614
17122
|
pos.z - this.z * 16
|
|
15615
17123
|
);
|
|
15616
17124
|
}
|
|
15617
17125
|
chunkPosToWorldPos(pos) {
|
|
15618
|
-
return new
|
|
17126
|
+
return new import_three42.Vector3(
|
|
15619
17127
|
this.x * 16 + pos.x,
|
|
15620
17128
|
this.y * 16 + pos.y,
|
|
15621
17129
|
this.z * 16 + pos.z
|
|
@@ -15636,6 +17144,7 @@ var Chunk = class _Chunk {
|
|
|
15636
17144
|
Axis,
|
|
15637
17145
|
BUILTIN_ENTITY,
|
|
15638
17146
|
BUILTIN_GENERATED,
|
|
17147
|
+
BannerPatterns,
|
|
15639
17148
|
BasicAssetKey,
|
|
15640
17149
|
BatchedExecutor,
|
|
15641
17150
|
BlockEntities,
|
|
@@ -15658,6 +17167,7 @@ var Chunk = class _Chunk {
|
|
|
15658
17167
|
DEFAULT_ROOT,
|
|
15659
17168
|
DEFAULT_UV,
|
|
15660
17169
|
DISPLAY_POSITIONS,
|
|
17170
|
+
DYE_COLORS,
|
|
15661
17171
|
DefaultBlockStates,
|
|
15662
17172
|
DisplayPosition,
|
|
15663
17173
|
DisplayTransforms,
|
|
@@ -15668,6 +17178,7 @@ var Chunk = class _Chunk {
|
|
|
15668
17178
|
Env,
|
|
15669
17179
|
FluidModelObject,
|
|
15670
17180
|
Fonts,
|
|
17181
|
+
GUI_CONTAINER_LAYOUTS,
|
|
15671
17182
|
Geometries,
|
|
15672
17183
|
GuiHelper,
|
|
15673
17184
|
GuiLight,
|
|
@@ -15696,11 +17207,14 @@ var Chunk = class _Chunk {
|
|
|
15696
17207
|
NodeCache,
|
|
15697
17208
|
NodeEnv,
|
|
15698
17209
|
OrbitControls,
|
|
17210
|
+
PackFormats,
|
|
17211
|
+
PackMetadata,
|
|
15699
17212
|
PersistentCache,
|
|
15700
17213
|
Renderer,
|
|
15701
17214
|
RequestError,
|
|
15702
17215
|
Requests,
|
|
15703
17216
|
SKIN_PARTS,
|
|
17217
|
+
SceneDocumentLoader,
|
|
15704
17218
|
SceneExporter,
|
|
15705
17219
|
SceneInspector,
|
|
15706
17220
|
SceneObject,
|
|
@@ -15776,6 +17290,7 @@ var Chunk = class _Chunk {
|
|
|
15776
17290
|
mergeBufferAttributes,
|
|
15777
17291
|
mergeBufferGeometries,
|
|
15778
17292
|
mergeVertices,
|
|
17293
|
+
parseSceneDocument,
|
|
15779
17294
|
prefix,
|
|
15780
17295
|
registerNodeEnv,
|
|
15781
17296
|
sampleEntityAnimation,
|