minerender 2.1.0-alpha.75 → 2.1.0-alpha.77
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 +57 -0
- package/dist/browser/index.d.mts +594 -14
- package/dist/browser/index.d.ts +594 -14
- package/dist/browser/index.js +965 -312
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.mjs +933 -280
- package/dist/browser/index.mjs.map +1 -1
- package/dist/bundle.js +1708 -1044
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.d.mts +594 -14
- package/dist/node/index.d.ts +594 -14
- package/dist/node/index.js +966 -311
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +932 -279
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/browser/index.mjs
CHANGED
|
@@ -64,6 +64,7 @@ var init_PersistentCache = __esm({
|
|
|
64
64
|
constructor(backing) {
|
|
65
65
|
this.backing = backing;
|
|
66
66
|
}
|
|
67
|
+
/** Opens a named store in the active environment using the current cache schema version. */
|
|
67
68
|
static open(name) {
|
|
68
69
|
return Env.provider.openCache(name, _PersistentCache.VERSION);
|
|
69
70
|
}
|
|
@@ -262,6 +263,7 @@ var init_Caching = __esm({
|
|
|
262
263
|
this.fontCache
|
|
263
264
|
];
|
|
264
265
|
}
|
|
266
|
+
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
265
267
|
static clear() {
|
|
266
268
|
for (const cache of this.all) {
|
|
267
269
|
cache.invalidateAll();
|
|
@@ -327,6 +329,7 @@ var init_Geometries = __esm({
|
|
|
327
329
|
}
|
|
328
330
|
return geometry;
|
|
329
331
|
}
|
|
332
|
+
/** Returns shared geometry. Use {@link createBox} when the caller needs to modify or dispose it independently. */
|
|
330
333
|
static getBox(key) {
|
|
331
334
|
const keyStr = serializeBoxGeometryKey(key);
|
|
332
335
|
return Caching.boxGeometryCache.get(keyStr, (k) => {
|
|
@@ -504,12 +507,15 @@ var init_Requests = __esm({
|
|
|
504
507
|
}
|
|
505
508
|
};
|
|
506
509
|
Requests = class {
|
|
510
|
+
/** Queues a general request. Set `request.signal` to cancel it and `request.timeout` to limit each attempt. */
|
|
507
511
|
static genericRequest(request) {
|
|
508
512
|
return this.genericQueue.request(request);
|
|
509
513
|
}
|
|
514
|
+
/** Queues a request separately from general traffic, using the configured asset base URL for relative paths. */
|
|
510
515
|
static mcAssetRequest(request) {
|
|
511
516
|
return this.mcAssetRequestQueue.request(request);
|
|
512
517
|
}
|
|
518
|
+
/** Sets the base URL for relative asset requests. Use {@link AssetLoader.setVersion} to change Minecraft versions. */
|
|
513
519
|
static setMcAssetRoot(root) {
|
|
514
520
|
this.mcAssetRequestQueue.baseURL = root;
|
|
515
521
|
}
|
|
@@ -564,6 +570,7 @@ var init_WrappedImage = __esm({
|
|
|
564
570
|
}
|
|
565
571
|
return false;
|
|
566
572
|
}
|
|
573
|
+
/** Whether the dimensions form a vertical strip of square frames, without consulting `.mcmeta`. */
|
|
567
574
|
get animated() {
|
|
568
575
|
return this.height > this.width && this.height % this.width === 0;
|
|
569
576
|
}
|
|
@@ -610,6 +617,7 @@ var init_ImageLoader = __esm({
|
|
|
610
617
|
static _createImage() {
|
|
611
618
|
return createImage();
|
|
612
619
|
}
|
|
620
|
+
/** Resolves when an image URL has decoded, or rejects if decoding fails. */
|
|
613
621
|
static async loadAsync(src) {
|
|
614
622
|
return new Promise((resolve, reject) => {
|
|
615
623
|
const image = this._createImage();
|
|
@@ -618,6 +626,7 @@ var init_ImageLoader = __esm({
|
|
|
618
626
|
image.src = src;
|
|
619
627
|
});
|
|
620
628
|
}
|
|
629
|
+
/** Returns an image immediately. Use the callbacks to observe loading, or await {@link loadAsync}. */
|
|
621
630
|
static loadElement(src, onload, onerr) {
|
|
622
631
|
const image = this._createImage();
|
|
623
632
|
if (onload)
|
|
@@ -645,6 +654,7 @@ var init_ImageLoader = __esm({
|
|
|
645
654
|
throw err;
|
|
646
655
|
}
|
|
647
656
|
}
|
|
657
|
+
/** Decodes the supplied bytes into a canvas without fetching the source URL again. */
|
|
648
658
|
static async infoToCanvasData(info) {
|
|
649
659
|
const type = info.type === "jpg" ? "jpeg" : info.type ?? "png";
|
|
650
660
|
const image = await _ImageLoader.loadAsync(`data:image/${type};base64,${info.data.toString("base64")}`);
|
|
@@ -661,12 +671,14 @@ var init_ImageLoader = __esm({
|
|
|
661
671
|
const image = await this.infoToCanvasData(info);
|
|
662
672
|
return image.data.getImageData(0, 0, image.width, image.height);
|
|
663
673
|
}
|
|
674
|
+
/** Returns cached RGBA pixels for an image URL or data URL. Failed loads can be retried. */
|
|
664
675
|
static async getData(src) {
|
|
665
676
|
const keyStr = serializeImageKey({ src });
|
|
666
677
|
return await Caching.imageDataCache.get(keyStr, (k) => {
|
|
667
678
|
return _ImageLoader.loadData(src);
|
|
668
679
|
});
|
|
669
680
|
}
|
|
681
|
+
/** Returns a cached canvas context and dimensions for an image URL or data URL. */
|
|
670
682
|
static async getCanvasData(src) {
|
|
671
683
|
const keyStr = serializeImageKey({ src });
|
|
672
684
|
return await Caching.canvasImageDataCache.get(keyStr, (k) => {
|
|
@@ -763,6 +775,7 @@ var init_TextureLoader = __esm({
|
|
|
763
775
|
texture.rotation = rotation;
|
|
764
776
|
return Textures.initTextureProps(texture);
|
|
765
777
|
}
|
|
778
|
+
/** Loads image pixels in the background. `rotation` is in radians. Check {@link hasFailed} for load failures. */
|
|
766
779
|
static load(src, format2 = RGBAFormat, rotation = 0) {
|
|
767
780
|
const texture = new Texture();
|
|
768
781
|
texture.colorSpace = THREE.SRGBColorSpace;
|
|
@@ -800,8 +813,8 @@ var init_Textures = __esm({
|
|
|
800
813
|
for (let y = 0; y < size; y++) {
|
|
801
814
|
for (let x = 0; x < size; x++) {
|
|
802
815
|
const offset = (y * size + x) * 4;
|
|
803
|
-
const
|
|
804
|
-
data[offset] = data[offset + 2] =
|
|
816
|
+
const color2 = (x + y) % 2 === 0 ? 255 : 0;
|
|
817
|
+
data[offset] = data[offset + 2] = color2;
|
|
805
818
|
data[offset + 3] = 255;
|
|
806
819
|
}
|
|
807
820
|
}
|
|
@@ -812,6 +825,7 @@ var init_Textures = __esm({
|
|
|
812
825
|
return this.initTextureProps(texture);
|
|
813
826
|
});
|
|
814
827
|
}
|
|
828
|
+
/** Sets nearest-neighbor filters without changing the texture's color space. Returns the same texture. */
|
|
815
829
|
static initTextureProps(texture) {
|
|
816
830
|
texture.magFilter = THREE2.NearestFilter;
|
|
817
831
|
texture.minFilter = THREE2.NearestFilter;
|
|
@@ -824,11 +838,13 @@ var init_Textures = __esm({
|
|
|
824
838
|
static createAsset(key) {
|
|
825
839
|
return new Texture2();
|
|
826
840
|
}
|
|
841
|
+
/** Creates an sRGB color texture from a canvas. */
|
|
827
842
|
static createCanvasTexture(canvas) {
|
|
828
843
|
const texture = new CanvasTexture(canvas);
|
|
829
844
|
texture.colorSpace = THREE2.SRGBColorSpace;
|
|
830
845
|
return this.initTextureProps(texture);
|
|
831
846
|
}
|
|
847
|
+
/** Returns a shared texture immediately and loads its image asynchronously. Failed cached loads are retried. */
|
|
832
848
|
static getImage(key) {
|
|
833
849
|
const keyStr = serializeTextureKey(key);
|
|
834
850
|
const cached2 = Caching.textureCache.peek(keyStr);
|
|
@@ -853,6 +869,7 @@ var init_Materials = __esm({
|
|
|
853
869
|
init_TextureLoader();
|
|
854
870
|
init_Caching();
|
|
855
871
|
Materials = class _Materials {
|
|
872
|
+
/** Shared checkerboard material, created on first access. */
|
|
856
873
|
static get MISSING_TEXTURE() {
|
|
857
874
|
return Caching.materialCache.get("builtin:missing-texture", () => new MeshBasicMaterial({
|
|
858
875
|
map: Textures.getMissing(),
|
|
@@ -956,6 +973,7 @@ var init_Materials = __esm({
|
|
|
956
973
|
if (tint !== void 0) material.color.multiply(new Color(tint));
|
|
957
974
|
return material;
|
|
958
975
|
}
|
|
976
|
+
/** Creates an unlit, transparent GUI material without depth writes or tone mapping. */
|
|
959
977
|
static createGuiCanvasMaterial(canvas) {
|
|
960
978
|
const material = new MeshBasicMaterial({
|
|
961
979
|
map: Textures.createCanvasTexture(canvas),
|
|
@@ -1122,6 +1140,7 @@ var init_Materials = __esm({
|
|
|
1122
1140
|
}
|
|
1123
1141
|
return this.createBasicCanvasMaterial(canvas, transparent, shade);
|
|
1124
1142
|
}
|
|
1143
|
+
/** Returns a shared image material. Use {@link createImage} for a separate material instance. */
|
|
1125
1144
|
static getImage(key) {
|
|
1126
1145
|
const keyStr = serializeMaterialKey(key);
|
|
1127
1146
|
const map = Caching.materialCache.peek(keyStr)?.map;
|
|
@@ -1296,6 +1315,14 @@ var init_AssetKey = __esm({
|
|
|
1296
1315
|
}
|
|
1297
1316
|
};
|
|
1298
1317
|
AssetKey = class _AssetKey extends BasicAssetKey {
|
|
1318
|
+
/**
|
|
1319
|
+
* Creates a key from separate path components. Use {@link parse} for a combined reference.
|
|
1320
|
+
*
|
|
1321
|
+
* @param assetType - Asset directory, such as `models`, `textures`, or `blockstates`.
|
|
1322
|
+
* @param type - Subdirectory, such as `block` or `item`.
|
|
1323
|
+
* @param rootType - Top-level directory, usually `assets` or `data`.
|
|
1324
|
+
* @param root - Base URL override for hosted asset sources.
|
|
1325
|
+
*/
|
|
1299
1326
|
constructor(namespace, path, assetType, type, rootType = "assets", extension = ".json", root) {
|
|
1300
1327
|
super(namespace, path);
|
|
1301
1328
|
this.namespace = namespace;
|
|
@@ -1307,6 +1334,16 @@ var init_AssetKey = __esm({
|
|
|
1307
1334
|
this.root = root;
|
|
1308
1335
|
this.isAssetKey = true;
|
|
1309
1336
|
}
|
|
1337
|
+
/**
|
|
1338
|
+
* Parses an asset reference such as `minecraft:block/stone`.
|
|
1339
|
+
* A missing namespace defaults to the origin's namespace or `minecraft`.
|
|
1340
|
+
*
|
|
1341
|
+
* @param assetType - Asset category, such as `models` or `textures`.
|
|
1342
|
+
* @param str - Path with an optional namespace and file extension.
|
|
1343
|
+
* @param origin - Supplies the asset root, default namespace, and non-texture extension.
|
|
1344
|
+
* @returns A key with the matching extension removed from its path. Textures use `.png`;
|
|
1345
|
+
* other assets use the origin's extension or `.json`.
|
|
1346
|
+
*/
|
|
1310
1347
|
static parse(assetType, str, origin) {
|
|
1311
1348
|
let namespace = origin?.namespace || DEFAULT_NAMESPACE;
|
|
1312
1349
|
if (str.includes(":")) {
|
|
@@ -1338,6 +1375,7 @@ var init_AssetKey = __esm({
|
|
|
1338
1375
|
];
|
|
1339
1376
|
return a.join("/");
|
|
1340
1377
|
}
|
|
1378
|
+
/** Joins the type subdirectory and path without adding a namespace or file extension. */
|
|
1341
1379
|
getFullPath() {
|
|
1342
1380
|
let p10 = [];
|
|
1343
1381
|
if (this.type) {
|
|
@@ -1349,6 +1387,7 @@ var init_AssetKey = __esm({
|
|
|
1349
1387
|
toNamespacedString() {
|
|
1350
1388
|
return this.namespace + ":" + this.getFullPath();
|
|
1351
1389
|
}
|
|
1390
|
+
/** Returns a cache identifier that includes the explicit root or {@link AssetLoader.ROOT}. */
|
|
1352
1391
|
serialize() {
|
|
1353
1392
|
return this.toString();
|
|
1354
1393
|
}
|
|
@@ -1360,17 +1399,17 @@ var init_AssetKey = __esm({
|
|
|
1360
1399
|
var DisplayPosition, DISPLAY_POSITIONS;
|
|
1361
1400
|
var init_DisplayPosition = __esm({
|
|
1362
1401
|
"src/model/DisplayPosition.ts"() {
|
|
1363
|
-
DisplayPosition = /* @__PURE__ */ ((
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
return
|
|
1402
|
+
DisplayPosition = /* @__PURE__ */ ((DisplayPosition3) => {
|
|
1403
|
+
DisplayPosition3["THIRDPERSON_RIGHTHAND"] = "thirdperson_righthand";
|
|
1404
|
+
DisplayPosition3["THIRDPERSON_LEFTHAND"] = "thirdperson_lefthand";
|
|
1405
|
+
DisplayPosition3["FIRSTPERSON_RIGHTHAND"] = "firstperson_righthand";
|
|
1406
|
+
DisplayPosition3["FIRSTPERSON_LEFTHAND"] = "firstperson_lefthand";
|
|
1407
|
+
DisplayPosition3["GUI"] = "gui";
|
|
1408
|
+
DisplayPosition3["HEAD"] = "head";
|
|
1409
|
+
DisplayPosition3["GROUND"] = "ground";
|
|
1410
|
+
DisplayPosition3["FIXED"] = "fixed";
|
|
1411
|
+
DisplayPosition3["ON_SHELF"] = "on_shelf";
|
|
1412
|
+
return DisplayPosition3;
|
|
1374
1413
|
})(DisplayPosition || {});
|
|
1375
1414
|
DISPLAY_POSITIONS = Object.values(DisplayPosition);
|
|
1376
1415
|
}
|
|
@@ -1408,6 +1447,7 @@ var init_DisplayTransforms = __esm({
|
|
|
1408
1447
|
const scale = new Vector32(...transform.scale ?? [1, 1, 1]).clampScalar(-4, 4);
|
|
1409
1448
|
return new Matrix4().compose(translation, rotation, scale);
|
|
1410
1449
|
}
|
|
1450
|
+
/** Transforms geometry in place and reverses triangle winding when the matrix reflects it. */
|
|
1411
1451
|
static apply(geometry, matrix) {
|
|
1412
1452
|
geometry.applyMatrix4(matrix);
|
|
1413
1453
|
if (matrix.determinant() < 0) {
|
|
@@ -1435,6 +1475,7 @@ var init_ModelMerger = __esm({
|
|
|
1435
1475
|
init_AssetKey();
|
|
1436
1476
|
init_DisplayTransforms();
|
|
1437
1477
|
ModelMerger = class {
|
|
1478
|
+
/** Loads the parent chain and returns a merged model without modifying the supplied definition. */
|
|
1438
1479
|
static async mergeWithParents(model) {
|
|
1439
1480
|
const models = await this.collectAllParents(model);
|
|
1440
1481
|
let merged = {};
|
|
@@ -1502,6 +1543,7 @@ var init_Models = __esm({
|
|
|
1502
1543
|
static get PERSISTENT_CACHE() {
|
|
1503
1544
|
return this._persistentCache ?? (this._persistentCache = PersistentCache.open("minerender-models"));
|
|
1504
1545
|
}
|
|
1546
|
+
/** Lists item filenames from the asset source's `_list.json`, with a legacy model-directory fallback. */
|
|
1505
1547
|
static async getItemList() {
|
|
1506
1548
|
const key = new AssetKey(DEFAULT_NAMESPACE, "_list", "items");
|
|
1507
1549
|
const legacyKey = new AssetKey(DEFAULT_NAMESPACE, "_list", "models", "item");
|
|
@@ -1572,6 +1614,7 @@ var init_Models = __esm({
|
|
|
1572
1614
|
}
|
|
1573
1615
|
throw new Error(`Unsupported item model ${node?.type ?? "definition"} for ${key.toNamespacedString()}`);
|
|
1574
1616
|
}
|
|
1617
|
+
/** Loads one model file without resolving its parents. Returns `undefined` when the file is missing. */
|
|
1575
1618
|
static async getRaw(key) {
|
|
1576
1619
|
if (!key.assetType) {
|
|
1577
1620
|
key.assetType = "models";
|
|
@@ -1584,13 +1627,19 @@ var init_Models = __esm({
|
|
|
1584
1627
|
return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
|
|
1585
1628
|
return AssetLoader.get(key, "model" /* MODEL */);
|
|
1586
1629
|
});
|
|
1587
|
-
}).then((
|
|
1588
|
-
if (
|
|
1589
|
-
|
|
1630
|
+
}).then((asset2) => {
|
|
1631
|
+
if (asset2) {
|
|
1632
|
+
asset2.key = key;
|
|
1590
1633
|
}
|
|
1591
|
-
return
|
|
1634
|
+
return asset2;
|
|
1592
1635
|
});
|
|
1593
1636
|
}
|
|
1637
|
+
/**
|
|
1638
|
+
* Loads a model and resolves its parent chain. Returns `undefined` when the model is missing.
|
|
1639
|
+
* Item keys use GUI context, false conditions, and zero numeric properties.
|
|
1640
|
+
*
|
|
1641
|
+
* @param key - Model key, for example `AssetKey.parse("models", "minecraft:item/diamond_sword")`.
|
|
1642
|
+
*/
|
|
1594
1643
|
static async getMerged(key) {
|
|
1595
1644
|
if (!key.assetType) {
|
|
1596
1645
|
key.assetType = "models";
|
|
@@ -1603,9 +1652,11 @@ var init_Models = __esm({
|
|
|
1603
1652
|
return _Models.loadAndMerge(key);
|
|
1604
1653
|
});
|
|
1605
1654
|
}
|
|
1655
|
+
/** Alias for {@link getMerged}. */
|
|
1606
1656
|
static async get(key) {
|
|
1607
1657
|
return this.getMerged(key);
|
|
1608
1658
|
}
|
|
1659
|
+
/** Clears persisted models. Use {@link Caching.clear} to also discard in-memory assets. */
|
|
1609
1660
|
static async clearCache() {
|
|
1610
1661
|
return this.PERSISTENT_CACHE.clear();
|
|
1611
1662
|
}
|
|
@@ -1628,6 +1679,7 @@ var init_BlockStates = __esm({
|
|
|
1628
1679
|
return this._persistentCache ?? (this._persistentCache = PersistentCache.open("minerender-blockstates"));
|
|
1629
1680
|
}
|
|
1630
1681
|
// BlockState names are hardcoded
|
|
1682
|
+
/** Returns blockstate filenames from the selected version's `_list.json`, or an empty list. */
|
|
1631
1683
|
static async getList() {
|
|
1632
1684
|
const key = new AssetKey(
|
|
1633
1685
|
DEFAULT_NAMESPACE,
|
|
@@ -1648,6 +1700,7 @@ var init_BlockStates = __esm({
|
|
|
1648
1700
|
return AssetLoader.get(key, "json" /* JSON */);
|
|
1649
1701
|
});
|
|
1650
1702
|
}
|
|
1703
|
+
/** Looks up vanilla property definitions for the key's block path, or returns `undefined`. */
|
|
1651
1704
|
static async getDefaultState(key) {
|
|
1652
1705
|
const defaultStates = await this.getDefaultStates();
|
|
1653
1706
|
if (!defaultStates) {
|
|
@@ -1655,6 +1708,7 @@ var init_BlockStates = __esm({
|
|
|
1655
1708
|
}
|
|
1656
1709
|
return defaultStates["minecraft:" + key.path];
|
|
1657
1710
|
}
|
|
1711
|
+
/** Loads a cached blockstate definition for {@link MineRenderScene.addBlock}, or returns `undefined`. */
|
|
1658
1712
|
static async get(key) {
|
|
1659
1713
|
if (!key.assetType) {
|
|
1660
1714
|
key.assetType = "blockstates";
|
|
@@ -1667,11 +1721,11 @@ var init_BlockStates = __esm({
|
|
|
1667
1721
|
return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
|
|
1668
1722
|
return AssetLoader.get(key, "blockstate" /* BLOCKSTATE */);
|
|
1669
1723
|
});
|
|
1670
|
-
}).then((
|
|
1671
|
-
if (
|
|
1672
|
-
|
|
1724
|
+
}).then((asset2) => {
|
|
1725
|
+
if (asset2) {
|
|
1726
|
+
asset2.key = key;
|
|
1673
1727
|
}
|
|
1674
|
-
return
|
|
1728
|
+
return asset2;
|
|
1675
1729
|
});
|
|
1676
1730
|
}
|
|
1677
1731
|
static getAll(keys) {
|
|
@@ -1681,6 +1735,7 @@ var init_BlockStates = __esm({
|
|
|
1681
1735
|
}
|
|
1682
1736
|
return Promise.all(promises);
|
|
1683
1737
|
}
|
|
1738
|
+
/** Clears persisted blockstates. Use {@link Caching.clear} to also discard in-memory assets. */
|
|
1684
1739
|
static async clearCache() {
|
|
1685
1740
|
await this.PERSISTENT_CACHE.clear();
|
|
1686
1741
|
}
|
|
@@ -1706,9 +1761,11 @@ var init_Assets = __esm({
|
|
|
1706
1761
|
static parseAssetKey(assetType, str, origin) {
|
|
1707
1762
|
return AssetKey.parse(assetType, str, origin);
|
|
1708
1763
|
}
|
|
1764
|
+
/** Loads a model with its parent chain resolved. See {@link Models.getMerged}. */
|
|
1709
1765
|
static async getModel(key) {
|
|
1710
1766
|
return Models.getMerged(key);
|
|
1711
1767
|
}
|
|
1768
|
+
/** Loads a blockstate definition. See {@link BlockStates.get}. */
|
|
1712
1769
|
static async getBlockState(key) {
|
|
1713
1770
|
return BlockStates.get(key);
|
|
1714
1771
|
}
|
|
@@ -1755,6 +1812,10 @@ var BatchedExecutor;
|
|
|
1755
1812
|
var init_BatchedExecutor = __esm({
|
|
1756
1813
|
"src/util/BatchedExecutor.ts"() {
|
|
1757
1814
|
BatchedExecutor = class {
|
|
1815
|
+
/**
|
|
1816
|
+
* @param interval - Delay between queue runs, in milliseconds.
|
|
1817
|
+
* @param batch - Maximum tasks started per run and maximum tasks active at once.
|
|
1818
|
+
*/
|
|
1758
1819
|
constructor(interval = 1, batch = 30) {
|
|
1759
1820
|
if (!Number.isFinite(interval) || interval < 0) throw new RangeError("Batch interval must be finite and nonnegative");
|
|
1760
1821
|
if (!Number.isInteger(batch) || batch < 1) throw new RangeError("Batch size must be a positive integer");
|
|
@@ -1762,9 +1823,11 @@ var init_BatchedExecutor = __esm({
|
|
|
1762
1823
|
this.batch = batch;
|
|
1763
1824
|
this.queue = new JobQueue2(async (task) => task(), { interval, maxPerRun: batch, maxActive: batch });
|
|
1764
1825
|
}
|
|
1826
|
+
/** Queues a task and resolves with its result, or rejects with its error. */
|
|
1765
1827
|
submit(task) {
|
|
1766
1828
|
return this.queue.add(() => task());
|
|
1767
1829
|
}
|
|
1830
|
+
/** Rejects queued and future tasks. Tasks already running are allowed to finish. */
|
|
1768
1831
|
stop() {
|
|
1769
1832
|
this.queue.end();
|
|
1770
1833
|
}
|
|
@@ -1812,29 +1875,29 @@ function applyGenericRotation(axis, rotation, obj) {
|
|
|
1812
1875
|
break;
|
|
1813
1876
|
}
|
|
1814
1877
|
}
|
|
1815
|
-
function addBox3WireframeToObject(box, obj,
|
|
1878
|
+
function addBox3WireframeToObject(box, obj, color2 = 16777215, w = 2) {
|
|
1816
1879
|
let size = new Vector33();
|
|
1817
1880
|
box.getSize(size);
|
|
1818
1881
|
let boxGeo = new BoxGeometry2(size.x, size.y, size.z, 1, 1, 1);
|
|
1819
1882
|
let wireGeo = new EdgesGeometry(boxGeo);
|
|
1820
|
-
let wireMat = new LineBasicMaterial({ color, linewidth: w });
|
|
1883
|
+
let wireMat = new LineBasicMaterial({ color: color2, linewidth: w });
|
|
1821
1884
|
let wireframe = new LineSegments(wireGeo, wireMat);
|
|
1822
1885
|
obj.add(wireframe);
|
|
1823
1886
|
let axes = new AxesHelper(1);
|
|
1824
1887
|
obj.add(axes);
|
|
1825
1888
|
}
|
|
1826
|
-
function addWireframeToObject(obj,
|
|
1889
|
+
function addWireframeToObject(obj, color2 = 16777215, w = 2) {
|
|
1827
1890
|
let boxGeo = new BoxGeometry2(16, 16, 16, 1, 1, 1);
|
|
1828
1891
|
let wireGeo = new EdgesGeometry(boxGeo);
|
|
1829
|
-
let wireMat = new LineBasicMaterial({ color, linewidth: w });
|
|
1892
|
+
let wireMat = new LineBasicMaterial({ color: color2, linewidth: w });
|
|
1830
1893
|
let wireframe = new LineSegments(wireGeo, wireMat);
|
|
1831
1894
|
obj.add(wireframe);
|
|
1832
1895
|
let axes = new AxesHelper(1);
|
|
1833
1896
|
obj.add(axes);
|
|
1834
1897
|
}
|
|
1835
|
-
function addWireframeToMesh(geo, mesh,
|
|
1898
|
+
function addWireframeToMesh(geo, mesh, color2 = 16777215, w = 2) {
|
|
1836
1899
|
let wireGeo = new EdgesGeometry(geo);
|
|
1837
|
-
let wireMat = new LineBasicMaterial({ color, linewidth: w });
|
|
1900
|
+
let wireMat = new LineBasicMaterial({ color: color2, linewidth: w });
|
|
1838
1901
|
let wireframe = new LineSegments(wireGeo, wireMat);
|
|
1839
1902
|
mesh.add(wireframe);
|
|
1840
1903
|
let axes = new AxesHelper(1);
|
|
@@ -1906,6 +1969,7 @@ var NBTHelper;
|
|
|
1906
1969
|
var init_NBTHelper = __esm({
|
|
1907
1970
|
"src/nbt/NBTHelper.ts"() {
|
|
1908
1971
|
NBTHelper = class {
|
|
1972
|
+
/** Parses NBT bytes and preserves format and compression metadata. Omit `format` for automatic detection. */
|
|
1909
1973
|
static async fromBuffer(data, format2) {
|
|
1910
1974
|
const buffer = Buffer3.from(data instanceof Uint8Array ? data : new Uint8Array(data));
|
|
1911
1975
|
const prismarineNbt = await import("prismarine-nbt");
|
|
@@ -1974,6 +2038,7 @@ var init_HostedAssetSource = __esm({
|
|
|
1974
2038
|
retryDefaults: true
|
|
1975
2039
|
};
|
|
1976
2040
|
_HostedAssetSource = class _HostedAssetSource extends AssetSource {
|
|
2041
|
+
/** Retries missing assets with the `minecraft` namespace and default root. Other failures reject. */
|
|
1977
2042
|
async loadOrRetryWithDefaults(key, parser) {
|
|
1978
2043
|
const direct = await this.load(key, parser);
|
|
1979
2044
|
if (typeof direct !== "undefined") {
|
|
@@ -2031,6 +2096,10 @@ var init_HostedAssetSource = __esm({
|
|
|
2031
2096
|
assetBasePath(key) {
|
|
2032
2097
|
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 + "/" : ""}`;
|
|
2033
2098
|
}
|
|
2099
|
+
/**
|
|
2100
|
+
* Creates a hosted source for {@link AssetLoader.addSource}.
|
|
2101
|
+
* Set `options.retryDefaults` to `false` to restrict lookups to the requested namespace and root.
|
|
2102
|
+
*/
|
|
2034
2103
|
constructor(root, options) {
|
|
2035
2104
|
super();
|
|
2036
2105
|
this._root = root;
|
|
@@ -2123,6 +2192,11 @@ var BrowserArchiveProxy;
|
|
|
2123
2192
|
var init_BrowserArchiveProxy = __esm({
|
|
2124
2193
|
"src/assets/source/archive/BrowserArchiveProxy.ts"() {
|
|
2125
2194
|
BrowserArchiveProxy = class {
|
|
2195
|
+
/**
|
|
2196
|
+
* Opens a ZIP for asset lookup.
|
|
2197
|
+
* @param id - Cache identity for this content. Change it when the pack changes.
|
|
2198
|
+
* A File supplies an identity from its name, size, and modification time when omitted.
|
|
2199
|
+
*/
|
|
2126
2200
|
constructor(blob, id) {
|
|
2127
2201
|
this._blob = blob;
|
|
2128
2202
|
this._reader = new ZipReader(new BlobReader(this._blob));
|
|
@@ -2252,27 +2326,27 @@ var init_PackFormats = __esm({
|
|
|
2252
2326
|
init_Requests();
|
|
2253
2327
|
PackFormats = class {
|
|
2254
2328
|
/** Resolves pack formats from Mojang's client metadata hosted on assets.mcasset.cloud. */
|
|
2255
|
-
static async get(
|
|
2256
|
-
let pending = this.versions.get(
|
|
2329
|
+
static async get(version2, type) {
|
|
2330
|
+
let pending = this.versions.get(version2);
|
|
2257
2331
|
if (!pending || pending.expires <= Date.now()) {
|
|
2258
2332
|
pending = {
|
|
2259
|
-
formats: this.load(
|
|
2260
|
-
expires: ["latest", "release", "snapshot"].includes(
|
|
2333
|
+
formats: this.load(version2),
|
|
2334
|
+
expires: ["latest", "release", "snapshot"].includes(version2) ? Date.now() + 3e5 : Infinity
|
|
2261
2335
|
};
|
|
2262
|
-
this.versions.set(
|
|
2336
|
+
this.versions.set(version2, pending);
|
|
2263
2337
|
}
|
|
2264
2338
|
try {
|
|
2265
2339
|
return (await pending.formats)[type];
|
|
2266
2340
|
} catch (cause) {
|
|
2267
|
-
if (this.versions.get(
|
|
2341
|
+
if (this.versions.get(version2) === pending) this.versions.delete(version2);
|
|
2268
2342
|
const option = type === "assets" ? "resourcePackFormat" : "dataPackFormat";
|
|
2269
2343
|
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
2270
|
-
throw new Error(`Could not resolve the pack format for Minecraft ${
|
|
2344
|
+
throw new Error(`Could not resolve the pack format for Minecraft ${version2}. Set ArchiveAssetSource ${option} explicitly. ${reason}`);
|
|
2271
2345
|
}
|
|
2272
2346
|
}
|
|
2273
|
-
static async load(
|
|
2347
|
+
static async load(version2) {
|
|
2274
2348
|
const response = await Requests.genericRequest({
|
|
2275
|
-
url: `https://assets.mcasset.cloud/${encodeURIComponent(
|
|
2349
|
+
url: `https://assets.mcasset.cloud/${encodeURIComponent(version2)}/game-version.json`,
|
|
2276
2350
|
responseType: "json"
|
|
2277
2351
|
});
|
|
2278
2352
|
const data = response.data;
|
|
@@ -2320,6 +2394,7 @@ var init_ArchiveAssetSource = __esm({
|
|
|
2320
2394
|
dataPackFormat: copyFormat(options.dataPackFormat)
|
|
2321
2395
|
};
|
|
2322
2396
|
}
|
|
2397
|
+
/** Creates a browser ZIP source from a Blob or File. */
|
|
2323
2398
|
static blob(blob, options) {
|
|
2324
2399
|
return new _ArchiveAssetSource(new BrowserArchiveProxy(blob), options);
|
|
2325
2400
|
}
|
|
@@ -2360,7 +2435,7 @@ var init_ArchiveAssetSource = __esm({
|
|
|
2360
2435
|
}
|
|
2361
2436
|
async load(key, parser) {
|
|
2362
2437
|
const path = `${this.assetBasePath(key)}${key.type !== void 0 ? key.type + "/" : ""}${key.path}${key.extension}`;
|
|
2363
|
-
const
|
|
2438
|
+
const version2 = AssetLoader.version;
|
|
2364
2439
|
let selectedPath = path;
|
|
2365
2440
|
let url;
|
|
2366
2441
|
try {
|
|
@@ -2368,7 +2443,7 @@ var init_ArchiveAssetSource = __esm({
|
|
|
2368
2443
|
if (key.rootType === "assets" || key.rootType === "data") {
|
|
2369
2444
|
const metadata = await this.getMetadata(key);
|
|
2370
2445
|
if (metadata.hasOverlays) {
|
|
2371
|
-
const format2 = (key.rootType === "assets" ? this.options.resourcePackFormat : this.options.dataPackFormat) ?? await PackFormats.get(
|
|
2446
|
+
const format2 = (key.rootType === "assets" ? this.options.resourcePackFormat : this.options.dataPackFormat) ?? await PackFormats.get(version2, key.rootType);
|
|
2372
2447
|
directories = metadata.overlayDirectories(format2);
|
|
2373
2448
|
}
|
|
2374
2449
|
}
|
|
@@ -2454,8 +2529,9 @@ var init_AssetLoader = __esm({
|
|
|
2454
2529
|
static get version() {
|
|
2455
2530
|
return this.ROOT.substring(this.ROOT.lastIndexOf("/") + 1);
|
|
2456
2531
|
}
|
|
2457
|
-
|
|
2458
|
-
|
|
2532
|
+
/** Selects an mcasset.cloud version, updates the vanilla source, and clears in-memory caches. */
|
|
2533
|
+
static setVersion(version2) {
|
|
2534
|
+
this.ROOT = `https://assets.mcasset.cloud/${version2}`;
|
|
2459
2535
|
const source = new HostedAssetSource(this.ROOT, { retryDefaults: false });
|
|
2460
2536
|
const index = this._SOURCES.findIndex((s) => s.key === "mcassets");
|
|
2461
2537
|
if (index !== -1) {
|
|
@@ -2487,6 +2563,13 @@ var init_AssetLoader = __esm({
|
|
|
2487
2563
|
return scope ? `${scope}
|
|
2488
2564
|
${key}` : key;
|
|
2489
2565
|
}
|
|
2566
|
+
/**
|
|
2567
|
+
* Registers a source at the highest priority.
|
|
2568
|
+
* Call {@link Caching.clear} after changing sources to discard previously loaded assets.
|
|
2569
|
+
*
|
|
2570
|
+
* @param key - Name used to replace or remove this source.
|
|
2571
|
+
* @param override - Removes the first source with this name before adding the new one.
|
|
2572
|
+
*/
|
|
2490
2573
|
static addSource(key, source, override = true) {
|
|
2491
2574
|
if (override) {
|
|
2492
2575
|
const existing = this.removeSource(key);
|
|
@@ -2497,6 +2580,7 @@ ${key}` : key;
|
|
|
2497
2580
|
this._SOURCES.unshift({ key, source });
|
|
2498
2581
|
console.log(p4, "Added AssetSource", key);
|
|
2499
2582
|
}
|
|
2583
|
+
/** Removes and returns the first source with this name. Call {@link Caching.clear} to reload assets. */
|
|
2500
2584
|
static removeSource(key) {
|
|
2501
2585
|
const index = this._SOURCES.findIndex((s) => s.key === key);
|
|
2502
2586
|
if (index != -1) {
|
|
@@ -2507,12 +2591,13 @@ ${key}` : key;
|
|
|
2507
2591
|
}
|
|
2508
2592
|
return void 0;
|
|
2509
2593
|
}
|
|
2594
|
+
/** Loads from all sources and returns defined results in priority order. Any source failure rejects the call. */
|
|
2510
2595
|
static async getAll(key, parser) {
|
|
2511
2596
|
const sources = [...this._SOURCES];
|
|
2512
2597
|
const results = [];
|
|
2513
2598
|
for (const { source } of sources) {
|
|
2514
|
-
const
|
|
2515
|
-
if (
|
|
2599
|
+
const asset2 = await source.get(key, parser);
|
|
2600
|
+
if (asset2 != void 0) results.push(asset2);
|
|
2516
2601
|
if (await source.blocks(key)) break;
|
|
2517
2602
|
}
|
|
2518
2603
|
return results;
|
|
@@ -2551,6 +2636,7 @@ ${key}` : key;
|
|
|
2551
2636
|
return void 0;
|
|
2552
2637
|
}
|
|
2553
2638
|
};
|
|
2639
|
+
/** Default asset base URL. Use {@link setVersion} to select a Minecraft version. */
|
|
2554
2640
|
_AssetLoader.ROOT = DEFAULT_ROOT;
|
|
2555
2641
|
_AssetLoader._SOURCES = [];
|
|
2556
2642
|
/** Used when a registered source cannot identify its content, so nothing persists past this session. */
|
|
@@ -2575,14 +2661,15 @@ var init_ModelTextures = __esm({
|
|
|
2575
2661
|
static get PERSISTENT_META_CACHE() {
|
|
2576
2662
|
return this._persistentMetaCache ?? (this._persistentMetaCache = PersistentCache.open("minerender-texturemeta"));
|
|
2577
2663
|
}
|
|
2664
|
+
/** Loads and decodes a texture into a readable canvas, or returns `undefined` when missing. */
|
|
2578
2665
|
static async get(key) {
|
|
2579
2666
|
const keyStr = key.serialize();
|
|
2580
2667
|
const pending = this.preload(key);
|
|
2581
2668
|
const cached2 = Caching.textureAssetCache.getIfPresent(keyStr);
|
|
2582
2669
|
try {
|
|
2583
|
-
const
|
|
2584
|
-
if (
|
|
2585
|
-
return await ImageLoader.infoToCanvasData(
|
|
2670
|
+
const asset2 = await pending;
|
|
2671
|
+
if (asset2) {
|
|
2672
|
+
return await ImageLoader.infoToCanvasData(asset2);
|
|
2586
2673
|
}
|
|
2587
2674
|
return void 0;
|
|
2588
2675
|
} catch (err) {
|
|
@@ -2592,16 +2679,18 @@ var init_ModelTextures = __esm({
|
|
|
2592
2679
|
throw err;
|
|
2593
2680
|
}
|
|
2594
2681
|
}
|
|
2682
|
+
/** Fetches and caches encoded texture bytes without decoding the pixels. */
|
|
2595
2683
|
static async preload(key) {
|
|
2596
2684
|
const keyStr = key.serialize();
|
|
2597
2685
|
return Caching.textureAssetCache.get(keyStr, (k) => {
|
|
2598
|
-
return AssetLoader.get(key, "image" /* IMAGE */).then((
|
|
2599
|
-
if (
|
|
2600
|
-
|
|
2601
|
-
return
|
|
2686
|
+
return AssetLoader.get(key, "image" /* IMAGE */).then((asset2) => {
|
|
2687
|
+
if (asset2)
|
|
2688
|
+
asset2.key = key;
|
|
2689
|
+
return asset2;
|
|
2602
2690
|
});
|
|
2603
2691
|
});
|
|
2604
2692
|
}
|
|
2693
|
+
/** Loads a texture's `.mcmeta` file, or returns `undefined` when no metadata file exists. */
|
|
2605
2694
|
static async getMeta(key) {
|
|
2606
2695
|
if (!key.extension || !key.extension.endsWith(".mcmeta")) {
|
|
2607
2696
|
key = Object.assign(new AssetKey("", ""), key);
|
|
@@ -2610,14 +2699,15 @@ var init_ModelTextures = __esm({
|
|
|
2610
2699
|
const keyStr = key.serialize();
|
|
2611
2700
|
return Caching.textureMetaCache.get(keyStr, (k) => {
|
|
2612
2701
|
return this.PERSISTENT_META_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
|
|
2613
|
-
return AssetLoader.get(key, "meta" /* META */).then((
|
|
2614
|
-
if (
|
|
2615
|
-
|
|
2616
|
-
return
|
|
2702
|
+
return AssetLoader.get(key, "meta" /* META */).then((asset2) => {
|
|
2703
|
+
if (asset2)
|
|
2704
|
+
asset2.key = key;
|
|
2705
|
+
return asset2;
|
|
2617
2706
|
});
|
|
2618
2707
|
});
|
|
2619
2708
|
});
|
|
2620
2709
|
}
|
|
2710
|
+
/** Clears persisted texture metadata. Use {@link Caching.clear} to discard in-memory textures and metadata. */
|
|
2621
2711
|
static async clearCache() {
|
|
2622
2712
|
await this.PERSISTENT_META_CACHE.clear();
|
|
2623
2713
|
}
|
|
@@ -2684,6 +2774,7 @@ var init_Ticker = __esm({
|
|
|
2684
2774
|
}
|
|
2685
2775
|
return handle;
|
|
2686
2776
|
}
|
|
2777
|
+
/** Starts or resumes the shared tick loop. Repeated calls have no effect. */
|
|
2687
2778
|
static start() {
|
|
2688
2779
|
if (this.interval) return;
|
|
2689
2780
|
this.interval = this.schedule(() => {
|
|
@@ -2706,6 +2797,7 @@ var init_Ticker = __esm({
|
|
|
2706
2797
|
_Ticker.fiveSecondTicks = 0;
|
|
2707
2798
|
}, 1e3 * 5);
|
|
2708
2799
|
}
|
|
2800
|
+
/** Pauses the loop while retaining callbacks for a later {@link start}. */
|
|
2709
2801
|
static stop() {
|
|
2710
2802
|
if (this.interval) clearInterval(this.interval);
|
|
2711
2803
|
if (this.oneSecondTicksTracker) clearInterval(this.oneSecondTicksTracker);
|
|
@@ -2714,12 +2806,14 @@ var init_Ticker = __esm({
|
|
|
2714
2806
|
this.oneSecondTicksTracker = void 0;
|
|
2715
2807
|
this.fiveSecondTicksTracker = void 0;
|
|
2716
2808
|
}
|
|
2809
|
+
/** Registers a callback, starts the loop if needed, and returns an ID for {@link remove}. */
|
|
2717
2810
|
static add(tick) {
|
|
2718
2811
|
this.start();
|
|
2719
2812
|
const c = this.counter++;
|
|
2720
2813
|
this._tickers.set(c, tick);
|
|
2721
2814
|
return c;
|
|
2722
2815
|
}
|
|
2816
|
+
/** Removes a callback by ID and stops the timers when no callbacks remain. */
|
|
2723
2817
|
static remove(c) {
|
|
2724
2818
|
if (typeof c === "number") this._tickers.delete(c);
|
|
2725
2819
|
if (!this._tickers.size) this.stop();
|
|
@@ -2733,6 +2827,7 @@ var init_Ticker = __esm({
|
|
|
2733
2827
|
static get tpsFiveSeconds() {
|
|
2734
2828
|
return this.fiveSecondTps;
|
|
2735
2829
|
}
|
|
2830
|
+
/** Stops the loop and removes all registered callbacks. */
|
|
2736
2831
|
static dispose() {
|
|
2737
2832
|
this.stop();
|
|
2738
2833
|
this._tickers.clear();
|
|
@@ -2787,6 +2882,7 @@ var init_TextureAtlas = __esm({
|
|
|
2787
2882
|
}
|
|
2788
2883
|
};
|
|
2789
2884
|
}
|
|
2885
|
+
/** Copies pixels for a texture variable from its current atlas rectangle. */
|
|
2790
2886
|
getData(texture) {
|
|
2791
2887
|
const pos = this.positions[texture];
|
|
2792
2888
|
const size = this.sizes[texture];
|
|
@@ -3064,6 +3160,7 @@ var init_UVMapper = __esm({
|
|
|
3064
3160
|
}
|
|
3065
3161
|
uv.needsUpdate = true;
|
|
3066
3162
|
}
|
|
3163
|
+
/** Returns the shared atlas for a keyed, merged model, creating it on first use. */
|
|
3067
3164
|
static async getAtlas(model) {
|
|
3068
3165
|
const keyStr = model.key.serialize();
|
|
3069
3166
|
return Caching.modelTextureAtlasCache.get(keyStr, (k) => {
|
|
@@ -3134,6 +3231,11 @@ var init_UVMapper = __esm({
|
|
|
3134
3231
|
}
|
|
3135
3232
|
return [tl, tr, bl, br];
|
|
3136
3233
|
}
|
|
3234
|
+
/**
|
|
3235
|
+
* Combines model textures and animation metadata into an atlas.
|
|
3236
|
+
* Mapped UVs are stored on copies of the model's elements, available through the returned atlas.
|
|
3237
|
+
* Returns `undefined` for models without a texture map.
|
|
3238
|
+
*/
|
|
3137
3239
|
static async createAtlas(originalModel) {
|
|
3138
3240
|
const textureMap = {};
|
|
3139
3241
|
const metaMap = {};
|
|
@@ -3154,12 +3256,12 @@ var init_UVMapper = __esm({
|
|
|
3154
3256
|
} else {
|
|
3155
3257
|
uniqueTextureNames.push(textureKey);
|
|
3156
3258
|
const assetKey = AssetKey.parse("textures", textureValue, model.key);
|
|
3157
|
-
promises.push(ModelTextures.get(assetKey).then((
|
|
3158
|
-
if (!
|
|
3259
|
+
promises.push(ModelTextures.get(assetKey).then((asset2) => {
|
|
3260
|
+
if (!asset2) {
|
|
3159
3261
|
console.warn(p5, "Missing texture", assetKey);
|
|
3160
3262
|
return;
|
|
3161
3263
|
}
|
|
3162
|
-
textureMap[textureKey] = new WrappedImage(
|
|
3264
|
+
textureMap[textureKey] = new WrappedImage(asset2);
|
|
3163
3265
|
}));
|
|
3164
3266
|
promises.push(ModelTextures.getMeta(assetKey).then((meta) => {
|
|
3165
3267
|
metaMap[textureKey] = meta;
|
|
@@ -3498,14 +3600,15 @@ var init_Entities = __esm({
|
|
|
3498
3600
|
init_MineRenderError();
|
|
3499
3601
|
init_ModelTextures();
|
|
3500
3602
|
Entities = class {
|
|
3603
|
+
/** Lists model paths in the `minecraft` namespace, including subdirectories and excluding `.json`. */
|
|
3501
3604
|
static async getEntityList() {
|
|
3502
3605
|
const collect = async (path) => {
|
|
3503
3606
|
const prefix2 = path ? `${path}/` : "";
|
|
3504
3607
|
const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json");
|
|
3505
|
-
const
|
|
3506
|
-
if (!
|
|
3507
|
-
const files =
|
|
3508
|
-
const children = await Promise.all(
|
|
3608
|
+
const list2 = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
|
|
3609
|
+
if (!list2) return [];
|
|
3610
|
+
const files = list2.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
|
|
3611
|
+
const children = await Promise.all(list2.directories.map((directory) => collect(`${prefix2}${directory}`)));
|
|
3509
3612
|
return [...files, ...children.flat()];
|
|
3510
3613
|
};
|
|
3511
3614
|
return collect("");
|
|
@@ -3515,6 +3618,7 @@ var init_Entities = __esm({
|
|
|
3515
3618
|
const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
|
|
3516
3619
|
return Caching.entityModelCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
|
|
3517
3620
|
}
|
|
3621
|
+
/** Returns selectable geometry-layer names, or an empty list when the model is missing. */
|
|
3518
3622
|
static async getLayerList(modelKey) {
|
|
3519
3623
|
return Object.keys((await this.getModelFile(modelKey))?.layers ?? {});
|
|
3520
3624
|
}
|
|
@@ -3522,6 +3626,15 @@ var init_Entities = __esm({
|
|
|
3522
3626
|
static async getPassList(modelKey) {
|
|
3523
3627
|
return (await this.getModelFile(modelKey))?.passes ?? [];
|
|
3524
3628
|
}
|
|
3629
|
+
/**
|
|
3630
|
+
* Loads the layers and textures to pass to {@link MineRenderScene.addEntity}.
|
|
3631
|
+
* By default, selects `main`, unconditional passes, and passes enabled by `options.when`.
|
|
3632
|
+
*
|
|
3633
|
+
* @param modelKey - Dataset model, such as `new BasicAssetKey("minecraft", "zombie")`.
|
|
3634
|
+
* @param textureKey - Texture override for the first selected layer.
|
|
3635
|
+
* @param options - Layer selection, conditional passes, and per-layer texture overrides.
|
|
3636
|
+
* @returns The selected model, or `undefined` when its dataset file is missing.
|
|
3637
|
+
*/
|
|
3525
3638
|
static async getEntity(modelKey, textureKey, options) {
|
|
3526
3639
|
const model = await this.getModelFile(modelKey);
|
|
3527
3640
|
if (!model) return void 0;
|
|
@@ -3567,6 +3680,7 @@ var init_Entities = __esm({
|
|
|
3567
3680
|
const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
|
|
3568
3681
|
return file?.animations;
|
|
3569
3682
|
}
|
|
3683
|
+
/** Finds a fallback texture using the model path, its directory, and vanilla variant data. */
|
|
3570
3684
|
static async resolveTexture(modelKey) {
|
|
3571
3685
|
const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
|
|
3572
3686
|
const root = isAssetKey(modelKey) ? modelKey.root : void 0;
|
|
@@ -3578,8 +3692,8 @@ var init_Entities = __esm({
|
|
|
3578
3692
|
if (await ModelTextures.get(texture2)) return texture2;
|
|
3579
3693
|
}
|
|
3580
3694
|
const listKey = new AssetKey(modelKey.namespace, "_list", `${name}_variant`, void 0, "data", ".json", root);
|
|
3581
|
-
const
|
|
3582
|
-
const files =
|
|
3695
|
+
const list2 = await AssetLoader.get(listKey, "list" /* LIST */);
|
|
3696
|
+
const files = list2?.files.filter((file2) => file2.endsWith(".json") && file2 !== "_list.json");
|
|
3583
3697
|
const file = files?.find((file2) => file2 === "temperate.json") ?? files?.[0];
|
|
3584
3698
|
if (!file) return void 0;
|
|
3585
3699
|
const variantKey = new AssetKey(modelKey.namespace, file.slice(0, -5), `${name}_variant`, void 0, "data", ".json", root);
|
|
@@ -3684,27 +3798,27 @@ var init_EntityAnimationPlayer = __esm({
|
|
|
3684
3798
|
for (const layer of layers) {
|
|
3685
3799
|
const objects = [];
|
|
3686
3800
|
if (poses) {
|
|
3687
|
-
layer.traverse((
|
|
3688
|
-
if (poses.has(
|
|
3801
|
+
layer.traverse((object3) => {
|
|
3802
|
+
if (poses.has(object3)) objects.push(object3);
|
|
3689
3803
|
});
|
|
3690
3804
|
} else {
|
|
3691
3805
|
for (const bone of Object.keys(animation.bones)) {
|
|
3692
|
-
const
|
|
3693
|
-
if (
|
|
3806
|
+
const object3 = layer.getObjectByName(`group:${bone}`);
|
|
3807
|
+
if (object3) objects.push(object3);
|
|
3694
3808
|
}
|
|
3695
3809
|
}
|
|
3696
|
-
for (const
|
|
3697
|
-
const bone =
|
|
3698
|
-
const pose = poses?.get(
|
|
3810
|
+
for (const object3 of objects) {
|
|
3811
|
+
const bone = object3.name.slice("group:".length);
|
|
3812
|
+
const pose = poses?.get(object3);
|
|
3699
3813
|
this.parts.push({
|
|
3700
3814
|
bone,
|
|
3701
|
-
object:
|
|
3702
|
-
position:
|
|
3703
|
-
rotation:
|
|
3704
|
-
scale:
|
|
3705
|
-
basePosition: pose ? new Vector35(...pose.offset) :
|
|
3706
|
-
baseRotation: pose ? new Euler3(...pose.rotation, "ZYX") :
|
|
3707
|
-
baseScale: pose ? new Vector35(...pose.scale ?? [1, 1, 1]) :
|
|
3815
|
+
object: object3,
|
|
3816
|
+
position: object3.position.clone(),
|
|
3817
|
+
rotation: object3.rotation.clone(),
|
|
3818
|
+
scale: object3.scale.clone(),
|
|
3819
|
+
basePosition: pose ? new Vector35(...pose.offset) : object3.position.clone(),
|
|
3820
|
+
baseRotation: pose ? new Euler3(...pose.rotation, "ZYX") : object3.rotation.clone(),
|
|
3821
|
+
baseScale: pose ? new Vector35(...pose.scale ?? [1, 1, 1]) : object3.scale.clone()
|
|
3708
3822
|
});
|
|
3709
3823
|
}
|
|
3710
3824
|
}
|
|
@@ -3727,12 +3841,14 @@ var init_EntityAnimationPlayer = __esm({
|
|
|
3727
3841
|
this._animation = void 0;
|
|
3728
3842
|
this._time = 0;
|
|
3729
3843
|
}
|
|
3844
|
+
/** Applies the pose at an absolute playback time. Returns `false` when no clip is active. */
|
|
3730
3845
|
setTime(seconds) {
|
|
3731
3846
|
if (!this._animation) return false;
|
|
3732
3847
|
this._time = seconds;
|
|
3733
3848
|
this.apply();
|
|
3734
3849
|
return true;
|
|
3735
3850
|
}
|
|
3851
|
+
/** Advances playback by elapsed seconds multiplied by the speed passed to {@link play}. */
|
|
3736
3852
|
advance(deltaSeconds) {
|
|
3737
3853
|
return this.setTime(this._time + deltaSeconds * this.speed);
|
|
3738
3854
|
}
|
|
@@ -3770,6 +3886,7 @@ var init_InstanceReference = __esm({
|
|
|
3770
3886
|
if (!this.instanceable.isInstanceActive(this.index, this)) throw new MineRenderError("Instance has been removed");
|
|
3771
3887
|
return this.instanceable;
|
|
3772
3888
|
}
|
|
3889
|
+
/** Releases this placement's slot for reuse. Repeated calls have no effect. */
|
|
3773
3890
|
removeFromScene() {
|
|
3774
3891
|
if (this.instanceable.isInstanceActive(this.index, this)) this.instanceable.removeInstanceAt(this.index);
|
|
3775
3892
|
}
|
|
@@ -3782,9 +3899,11 @@ var init_InstanceReference = __esm({
|
|
|
3782
3899
|
setMatrix(matrix) {
|
|
3783
3900
|
this.activeInstanceable.setMatrixAt(this.index, matrix);
|
|
3784
3901
|
}
|
|
3902
|
+
/** Hides or restores this placement while preserving its transform and allocated slot. */
|
|
3785
3903
|
setVisible(visible) {
|
|
3786
3904
|
this.activeInstanceable.setInstanceVisibleAt(this.index, visible);
|
|
3787
3905
|
}
|
|
3906
|
+
/** Updates the supplied transform components. Positions use scene units and rotations use radians. */
|
|
3788
3907
|
setPositionRotationScale(position, rotation, scale) {
|
|
3789
3908
|
this.activeInstanceable.setPositionRotationScaleAt(this.index, position, rotation, scale);
|
|
3790
3909
|
}
|
|
@@ -3853,6 +3972,7 @@ var init_SceneObject = __esm({
|
|
|
3853
3972
|
}
|
|
3854
3973
|
async init() {
|
|
3855
3974
|
}
|
|
3975
|
+
/** Marks ancestor scenes for redraw and emits a `change` event. */
|
|
3856
3976
|
notifyDirty() {
|
|
3857
3977
|
this.traverseAncestors((parent) => {
|
|
3858
3978
|
const scene = parent;
|
|
@@ -3888,15 +4008,17 @@ var init_SceneObject = __esm({
|
|
|
3888
4008
|
return obj;
|
|
3889
4009
|
}
|
|
3890
4010
|
/**
|
|
3891
|
-
*
|
|
4011
|
+
* Finds a named part, such as `head`, without the internal `group:` prefix.
|
|
4012
|
+
* Returns `undefined` when the group does not exist.
|
|
3892
4013
|
*/
|
|
3893
4014
|
getGroupByName(name) {
|
|
3894
4015
|
return this.getObjectByName(`group:${name}`);
|
|
3895
4016
|
}
|
|
3896
4017
|
/**
|
|
3897
|
-
*
|
|
3898
|
-
* @param name name
|
|
3899
|
-
* @param visible
|
|
4018
|
+
* Changes a named group's visibility and requests a redraw.
|
|
4019
|
+
* @param name - Part name without the `group:` prefix.
|
|
4020
|
+
* @param visible - Visibility to assign. Omit it to toggle the current value.
|
|
4021
|
+
* @returns The resulting visibility, or `false` if the group does not exist.
|
|
3900
4022
|
*/
|
|
3901
4023
|
toggleGroupVisibility(name, visible) {
|
|
3902
4024
|
return this.toggleObjectVisibility(this.getGroupByName(name), visible);
|
|
@@ -3947,15 +4069,16 @@ var init_SceneObject = __esm({
|
|
|
3947
4069
|
return mesh;
|
|
3948
4070
|
}
|
|
3949
4071
|
/**
|
|
3950
|
-
*
|
|
4072
|
+
* Finds a named mesh without the internal `mesh:` prefix, or returns `undefined`.
|
|
3951
4073
|
*/
|
|
3952
4074
|
getMeshByName(name) {
|
|
3953
4075
|
return this.getObjectByName(`mesh:${name}`);
|
|
3954
4076
|
}
|
|
3955
4077
|
/**
|
|
3956
|
-
*
|
|
3957
|
-
* @param name name
|
|
3958
|
-
* @param visible
|
|
4078
|
+
* Changes a named mesh's visibility and requests a redraw.
|
|
4079
|
+
* @param name - Mesh name without the `mesh:` prefix.
|
|
4080
|
+
* @param visible - Visibility to assign. Omit it to toggle the current value.
|
|
4081
|
+
* @returns The resulting visibility, or `false` if the mesh does not exist.
|
|
3959
4082
|
*/
|
|
3960
4083
|
toggleMeshVisibility(name, visible) {
|
|
3961
4084
|
return this.toggleObjectVisibility(this.getMeshByName(name), visible);
|
|
@@ -4001,12 +4124,14 @@ var init_SceneObject = __esm({
|
|
|
4001
4124
|
get isInstanced() {
|
|
4002
4125
|
return this._isInstanced;
|
|
4003
4126
|
}
|
|
4127
|
+
/** Number of live placements, excluding released instance slots. */
|
|
4004
4128
|
get instanceCounter() {
|
|
4005
4129
|
return this._instanceCounter;
|
|
4006
4130
|
}
|
|
4007
4131
|
constructInstanceReference(i) {
|
|
4008
4132
|
return new InstanceReference(this, i);
|
|
4009
4133
|
}
|
|
4134
|
+
/** Allocates a placement of this instanced object, reusing a released slot when available. */
|
|
4010
4135
|
nextInstance() {
|
|
4011
4136
|
const mesh = this.instanceMesh;
|
|
4012
4137
|
if (!mesh) throw new MineRenderError("Object is not instanced");
|
|
@@ -4051,10 +4176,10 @@ var init_SceneObject = __esm({
|
|
|
4051
4176
|
return new InstancedBufferAttribute(array, attribute.itemSize, attribute.normalized, attribute.meshPerAttribute).setUsage(attribute.usage);
|
|
4052
4177
|
};
|
|
4053
4178
|
const matrix = grow(mesh.instanceMatrix, 0);
|
|
4054
|
-
const
|
|
4179
|
+
const color2 = mesh.instanceColor ? grow(mesh.instanceColor, 1) : null;
|
|
4055
4180
|
mesh.dispose();
|
|
4056
4181
|
mesh.instanceMatrix = matrix;
|
|
4057
|
-
mesh.instanceColor =
|
|
4182
|
+
mesh.instanceColor = color2;
|
|
4058
4183
|
}
|
|
4059
4184
|
*activeInstanceIndices() {
|
|
4060
4185
|
if (this.instanceMesh) {
|
|
@@ -4153,6 +4278,7 @@ var init_SceneObject = __esm({
|
|
|
4153
4278
|
vector.setFromMatrixScale(matrix);
|
|
4154
4279
|
return vector;
|
|
4155
4280
|
}
|
|
4281
|
+
/** Updates supplied transform components. For a shared model, affects every live instance. */
|
|
4156
4282
|
setPositionRotationScale(position, rotation, scale) {
|
|
4157
4283
|
if (this.isInstanced) {
|
|
4158
4284
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -4171,6 +4297,7 @@ var init_SceneObject = __esm({
|
|
|
4171
4297
|
}
|
|
4172
4298
|
this.notifyDirty();
|
|
4173
4299
|
}
|
|
4300
|
+
/** Sets position in scene units. For a shared model, updates every live instance. */
|
|
4174
4301
|
setPosition(position) {
|
|
4175
4302
|
if (this.isInstanced) {
|
|
4176
4303
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -4188,6 +4315,7 @@ var init_SceneObject = __esm({
|
|
|
4188
4315
|
return this.position;
|
|
4189
4316
|
}
|
|
4190
4317
|
}
|
|
4318
|
+
/** Sets rotation in radians. For a shared model, updates every live instance. */
|
|
4191
4319
|
setRotation(rotation) {
|
|
4192
4320
|
if (this.isInstanced) {
|
|
4193
4321
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -4205,6 +4333,7 @@ var init_SceneObject = __esm({
|
|
|
4205
4333
|
return this.rotation;
|
|
4206
4334
|
}
|
|
4207
4335
|
}
|
|
4336
|
+
/** Sets scale factors. For a shared model, updates every live instance. */
|
|
4208
4337
|
setScale(scale) {
|
|
4209
4338
|
if (this.isInstanced) {
|
|
4210
4339
|
for (const i of this.activeInstanceIndices()) {
|
|
@@ -4223,15 +4352,15 @@ var init_SceneObject = __esm({
|
|
|
4223
4352
|
}
|
|
4224
4353
|
}
|
|
4225
4354
|
//</editor-fold>
|
|
4226
|
-
toggleObjectVisibility(
|
|
4227
|
-
if (
|
|
4355
|
+
toggleObjectVisibility(object3, visible) {
|
|
4356
|
+
if (object3) {
|
|
4228
4357
|
if (typeof visible !== "undefined") {
|
|
4229
|
-
|
|
4358
|
+
object3.visible = visible;
|
|
4230
4359
|
} else {
|
|
4231
|
-
|
|
4360
|
+
object3.visible = !object3.visible;
|
|
4232
4361
|
}
|
|
4233
4362
|
this.notifyDirty();
|
|
4234
|
-
return
|
|
4363
|
+
return object3.visible;
|
|
4235
4364
|
}
|
|
4236
4365
|
this.notifyDirty();
|
|
4237
4366
|
return false;
|
|
@@ -4242,6 +4371,7 @@ var init_SceneObject = __esm({
|
|
|
4242
4371
|
this.notifyDirty();
|
|
4243
4372
|
super.dispose();
|
|
4244
4373
|
}
|
|
4374
|
+
/** Releases instance slots and removes children, disposing children that implement Disposable. */
|
|
4245
4375
|
disposeAndRemoveAllChildren() {
|
|
4246
4376
|
if (this.instanceMesh) {
|
|
4247
4377
|
if (this._scene) this._scene.stats.instanceCount -= this.instanceReferences.size;
|
|
@@ -4264,6 +4394,7 @@ var init_SceneObject = __esm({
|
|
|
4264
4394
|
}
|
|
4265
4395
|
this.notifyDirty();
|
|
4266
4396
|
}
|
|
4397
|
+
/** Detaches the object from its parent and requests a redraw. */
|
|
4267
4398
|
removeFromScene() {
|
|
4268
4399
|
this.notifyDirty();
|
|
4269
4400
|
this.removeFromParent();
|
|
@@ -4419,12 +4550,15 @@ var init_EntityObject = __esm({
|
|
|
4419
4550
|
if (this.animationPlayers.length) this.notifyDirty();
|
|
4420
4551
|
}
|
|
4421
4552
|
//</editor-fold>
|
|
4553
|
+
/** Finds a selected draw by its layer key, such as `main` or `main#2`, or returns `undefined`. */
|
|
4422
4554
|
getLayerGroup(name) {
|
|
4423
4555
|
return super.getGroupByName(`layer:${name}`);
|
|
4424
4556
|
}
|
|
4557
|
+
/** Finds a part name without `group:`. Supply `layerName` to restrict the search to one draw. */
|
|
4425
4558
|
getGroupByName(name, layerName) {
|
|
4426
4559
|
return layerName === void 0 ? super.getGroupByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`group:${name}`);
|
|
4427
4560
|
}
|
|
4561
|
+
/** Finds a mesh name without `mesh:`. Supply `layerName` to restrict the search to one draw. */
|
|
4428
4562
|
getMeshByName(name, layerName) {
|
|
4429
4563
|
return layerName === void 0 ? super.getMeshByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`mesh:${name}`);
|
|
4430
4564
|
}
|
|
@@ -4526,8 +4660,8 @@ var init_EntityObject = __esm({
|
|
|
4526
4660
|
this.scrollMaterials.push({ material: mat, offset: mat.map.offset, speed: scroll });
|
|
4527
4661
|
}
|
|
4528
4662
|
}
|
|
4529
|
-
this.getLayerGroup(name)?.traverse((
|
|
4530
|
-
if (isMesh(
|
|
4663
|
+
this.getLayerGroup(name)?.traverse((object3) => {
|
|
4664
|
+
if (isMesh(object3)) object3.material = mat;
|
|
4531
4665
|
});
|
|
4532
4666
|
}));
|
|
4533
4667
|
if (!this.children.length) {
|
|
@@ -4868,8 +5002,8 @@ function toTrianglesDrawMode(geometry, drawMode) {
|
|
|
4868
5002
|
return geometry;
|
|
4869
5003
|
}
|
|
4870
5004
|
}
|
|
4871
|
-
function computeMorphedAttributes(
|
|
4872
|
-
if (
|
|
5005
|
+
function computeMorphedAttributes(object3) {
|
|
5006
|
+
if (object3.geometry.isBufferGeometry !== true) {
|
|
4873
5007
|
console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
|
|
4874
5008
|
return null;
|
|
4875
5009
|
}
|
|
@@ -4882,11 +5016,11 @@ function computeMorphedAttributes(object2) {
|
|
|
4882
5016
|
const _morphA = new Vector310();
|
|
4883
5017
|
const _morphB = new Vector310();
|
|
4884
5018
|
const _morphC = new Vector310();
|
|
4885
|
-
function _calculateMorphedAttributeData(
|
|
5019
|
+
function _calculateMorphedAttributeData(object4, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
|
|
4886
5020
|
_vA.fromBufferAttribute(attribute, a2);
|
|
4887
5021
|
_vB.fromBufferAttribute(attribute, b2);
|
|
4888
5022
|
_vC.fromBufferAttribute(attribute, c2);
|
|
4889
|
-
const morphInfluences =
|
|
5023
|
+
const morphInfluences = object4.morphTargetInfluences;
|
|
4890
5024
|
if (material2.morphTargets && morphAttribute && morphInfluences) {
|
|
4891
5025
|
_morphA.set(0, 0, 0);
|
|
4892
5026
|
_morphB.set(0, 0, 0);
|
|
@@ -4912,10 +5046,10 @@ function computeMorphedAttributes(object2) {
|
|
|
4912
5046
|
_vB.add(_morphB);
|
|
4913
5047
|
_vC.add(_morphC);
|
|
4914
5048
|
}
|
|
4915
|
-
if (
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
5049
|
+
if (object4.isSkinnedMesh) {
|
|
5050
|
+
object4.boneTransform(a2, _vA);
|
|
5051
|
+
object4.boneTransform(b2, _vB);
|
|
5052
|
+
object4.boneTransform(c2, _vC);
|
|
4919
5053
|
}
|
|
4920
5054
|
modifiedAttributeArray[a2 * 3 + 0] = _vA.x;
|
|
4921
5055
|
modifiedAttributeArray[a2 * 3 + 1] = _vA.y;
|
|
@@ -4927,8 +5061,8 @@ function computeMorphedAttributes(object2) {
|
|
|
4927
5061
|
modifiedAttributeArray[c2 * 3 + 1] = _vC.y;
|
|
4928
5062
|
modifiedAttributeArray[c2 * 3 + 2] = _vC.z;
|
|
4929
5063
|
}
|
|
4930
|
-
const geometry =
|
|
4931
|
-
const material =
|
|
5064
|
+
const geometry = object3.geometry;
|
|
5065
|
+
const material = object3.material;
|
|
4932
5066
|
let a, b, c;
|
|
4933
5067
|
const index = geometry.index;
|
|
4934
5068
|
const positionAttribute = geometry.attributes.position;
|
|
@@ -4955,7 +5089,7 @@ function computeMorphedAttributes(object2) {
|
|
|
4955
5089
|
b = index.getX(j + 1);
|
|
4956
5090
|
c = index.getX(j + 2);
|
|
4957
5091
|
_calculateMorphedAttributeData(
|
|
4958
|
-
|
|
5092
|
+
object3,
|
|
4959
5093
|
groupMaterial,
|
|
4960
5094
|
positionAttribute,
|
|
4961
5095
|
morphPosition,
|
|
@@ -4966,7 +5100,7 @@ function computeMorphedAttributes(object2) {
|
|
|
4966
5100
|
modifiedPosition
|
|
4967
5101
|
);
|
|
4968
5102
|
_calculateMorphedAttributeData(
|
|
4969
|
-
|
|
5103
|
+
object3,
|
|
4970
5104
|
groupMaterial,
|
|
4971
5105
|
normalAttribute,
|
|
4972
5106
|
morphNormal,
|
|
@@ -4986,7 +5120,7 @@ function computeMorphedAttributes(object2) {
|
|
|
4986
5120
|
b = index.getX(i + 1);
|
|
4987
5121
|
c = index.getX(i + 2);
|
|
4988
5122
|
_calculateMorphedAttributeData(
|
|
4989
|
-
|
|
5123
|
+
object3,
|
|
4990
5124
|
material,
|
|
4991
5125
|
positionAttribute,
|
|
4992
5126
|
morphPosition,
|
|
@@ -4997,7 +5131,7 @@ function computeMorphedAttributes(object2) {
|
|
|
4997
5131
|
modifiedPosition
|
|
4998
5132
|
);
|
|
4999
5133
|
_calculateMorphedAttributeData(
|
|
5000
|
-
|
|
5134
|
+
object3,
|
|
5001
5135
|
material,
|
|
5002
5136
|
normalAttribute,
|
|
5003
5137
|
morphNormal,
|
|
@@ -5021,7 +5155,7 @@ function computeMorphedAttributes(object2) {
|
|
|
5021
5155
|
b = j + 1;
|
|
5022
5156
|
c = j + 2;
|
|
5023
5157
|
_calculateMorphedAttributeData(
|
|
5024
|
-
|
|
5158
|
+
object3,
|
|
5025
5159
|
groupMaterial,
|
|
5026
5160
|
positionAttribute,
|
|
5027
5161
|
morphPosition,
|
|
@@ -5032,7 +5166,7 @@ function computeMorphedAttributes(object2) {
|
|
|
5032
5166
|
modifiedPosition
|
|
5033
5167
|
);
|
|
5034
5168
|
_calculateMorphedAttributeData(
|
|
5035
|
-
|
|
5169
|
+
object3,
|
|
5036
5170
|
groupMaterial,
|
|
5037
5171
|
normalAttribute,
|
|
5038
5172
|
morphNormal,
|
|
@@ -5052,7 +5186,7 @@ function computeMorphedAttributes(object2) {
|
|
|
5052
5186
|
b = i + 1;
|
|
5053
5187
|
c = i + 2;
|
|
5054
5188
|
_calculateMorphedAttributeData(
|
|
5055
|
-
|
|
5189
|
+
object3,
|
|
5056
5190
|
material,
|
|
5057
5191
|
positionAttribute,
|
|
5058
5192
|
morphPosition,
|
|
@@ -5063,7 +5197,7 @@ function computeMorphedAttributes(object2) {
|
|
|
5063
5197
|
modifiedPosition
|
|
5064
5198
|
);
|
|
5065
5199
|
_calculateMorphedAttributeData(
|
|
5066
|
-
|
|
5200
|
+
object3,
|
|
5067
5201
|
material,
|
|
5068
5202
|
normalAttribute,
|
|
5069
5203
|
morphNormal,
|
|
@@ -5097,6 +5231,7 @@ var init_ModelCulling = __esm({
|
|
|
5097
5231
|
"src/model/ModelCulling.ts"() {
|
|
5098
5232
|
init_CubeFace();
|
|
5099
5233
|
ModelCulling = class {
|
|
5234
|
+
/** Rotates a world-direction bit mask into model space. Bits follow {@link CUBE_FACES}. */
|
|
5100
5235
|
static toLocalMask(worldMask, rotation) {
|
|
5101
5236
|
let localMask = 0;
|
|
5102
5237
|
for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
|
|
@@ -5107,6 +5242,7 @@ var init_ModelCulling = __esm({
|
|
|
5107
5242
|
}
|
|
5108
5243
|
return localMask;
|
|
5109
5244
|
}
|
|
5245
|
+
/** Whether a model fills one block with opaque faces and can hide adjacent block faces. */
|
|
5110
5246
|
static isOpaqueFullCube(atlas) {
|
|
5111
5247
|
if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
|
|
5112
5248
|
const element = atlas.model.elements[0];
|
|
@@ -5317,9 +5453,9 @@ var init_ItemTints = __esm({
|
|
|
5317
5453
|
}));
|
|
5318
5454
|
return colors;
|
|
5319
5455
|
}
|
|
5320
|
-
static rgb(
|
|
5321
|
-
if (!Array.isArray(
|
|
5322
|
-
const [red, green, blue] =
|
|
5456
|
+
static rgb(color2) {
|
|
5457
|
+
if (!Array.isArray(color2)) return color2 & 16777215;
|
|
5458
|
+
const [red, green, blue] = color2.map((channel) => Math.floor(Math.fround(Math.fround(channel) * 255)));
|
|
5323
5459
|
return red << 16 | green << 8 | blue;
|
|
5324
5460
|
}
|
|
5325
5461
|
};
|
|
@@ -5372,13 +5508,13 @@ var init_ModelObject = __esm({
|
|
|
5372
5508
|
transform.multiply(new Matrix49().makeTranslation(-8, -8, -8));
|
|
5373
5509
|
try {
|
|
5374
5510
|
for (const part of parts) {
|
|
5375
|
-
const
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
this.add(
|
|
5380
|
-
await
|
|
5381
|
-
|
|
5511
|
+
const object3 = new EntityObject(part.model, { flip: false, wireframe: this.options.wireframe });
|
|
5512
|
+
object3.matrix.copy(transform).multiply(part.transform);
|
|
5513
|
+
object3.matrixWorldNeedsUpdate = true;
|
|
5514
|
+
object3.matrixAutoUpdate = false;
|
|
5515
|
+
this.add(object3);
|
|
5516
|
+
await object3.init();
|
|
5517
|
+
object3.iterateAllMeshes((mesh) => {
|
|
5382
5518
|
const source = mesh.material;
|
|
5383
5519
|
let material = this.specialMaterials.get(source);
|
|
5384
5520
|
if (!material) {
|
|
@@ -5389,7 +5525,7 @@ var init_ModelObject = __esm({
|
|
|
5389
5525
|
mesh.material = material;
|
|
5390
5526
|
});
|
|
5391
5527
|
for (const [name, rotation] of Object.entries(part.rotations)) {
|
|
5392
|
-
|
|
5528
|
+
object3.getGroupByName(name)?.rotation.set(...rotation, "ZYX");
|
|
5393
5529
|
}
|
|
5394
5530
|
}
|
|
5395
5531
|
} catch (error) {
|
|
@@ -5442,9 +5578,9 @@ var init_ModelObject = __esm({
|
|
|
5442
5578
|
if (tintIndex === void 0 || tintIndex < 0) continue;
|
|
5443
5579
|
const tint = this.options.tints[tintIndex];
|
|
5444
5580
|
if (tint === void 0) continue;
|
|
5445
|
-
const
|
|
5581
|
+
const color2 = new Color9(tint);
|
|
5446
5582
|
for (let vertex = 0; vertex < 4; vertex++) {
|
|
5447
|
-
|
|
5583
|
+
color2.toArray(colors, (faceIndex * 4 + vertex) * 3);
|
|
5448
5584
|
}
|
|
5449
5585
|
}
|
|
5450
5586
|
elGeo.setAttribute("color", new BufferAttribute5(colors, 3));
|
|
@@ -5761,9 +5897,9 @@ var init_FluidModelObject = __esm({
|
|
|
5761
5897
|
}
|
|
5762
5898
|
}
|
|
5763
5899
|
geometry.clearGroups();
|
|
5764
|
-
const
|
|
5900
|
+
const color2 = new Color10(this.options.tints?.[0] ?? (this.kind === "water" ? 4159204 : 16777215));
|
|
5765
5901
|
const colors = new Float32Array(geometry.getAttribute("position").count * 3);
|
|
5766
|
-
for (let i = 0; i < colors.length; i += 3)
|
|
5902
|
+
for (let i = 0; i < colors.length; i += 3) color2.toArray(colors, i);
|
|
5767
5903
|
geometry.setAttribute("color", new BufferAttribute6(colors, 3));
|
|
5768
5904
|
geometry.computeBoundingBox();
|
|
5769
5905
|
geometry.computeBoundingSphere();
|
|
@@ -5893,11 +6029,11 @@ var BrowserEnv = class {
|
|
|
5893
6029
|
imageSize(data) {
|
|
5894
6030
|
return probeImageSize(data);
|
|
5895
6031
|
}
|
|
5896
|
-
openCache(name,
|
|
6032
|
+
openCache(name, version2) {
|
|
5897
6033
|
return new BrowserCache(localforage.createInstance({
|
|
5898
6034
|
// IndexedDB schema upgrades retain records; each cache generation needs its own database.
|
|
5899
|
-
name: name +
|
|
5900
|
-
version
|
|
6035
|
+
name: name + version2,
|
|
6036
|
+
version: version2
|
|
5901
6037
|
}));
|
|
5902
6038
|
}
|
|
5903
6039
|
};
|
|
@@ -5928,6 +6064,7 @@ var Meshes = class _Meshes {
|
|
|
5928
6064
|
static createBox(key) {
|
|
5929
6065
|
return new Mesh(Geometries.getBox(key.geometry), Materials.getImage(key.material));
|
|
5930
6066
|
}
|
|
6067
|
+
/** Returns a shared Mesh object. Use {@link createBox} for a mesh with its own transform and parent. */
|
|
5931
6068
|
static getBox(key) {
|
|
5932
6069
|
const keyStr = serializeMeshKey(key);
|
|
5933
6070
|
return Caching.boxMeshCache.get(keyStr, (k) => {
|
|
@@ -6040,6 +6177,7 @@ init_AssetParsers();
|
|
|
6040
6177
|
init_ModelTextures();
|
|
6041
6178
|
init_Caching();
|
|
6042
6179
|
var Fonts = class {
|
|
6180
|
+
/** Combines font definitions in source-priority order. Unsupported Unihex and TrueType providers are skipped. */
|
|
6043
6181
|
static async get(key = "minecraft:default") {
|
|
6044
6182
|
const fontKey = typeof key === "string" ? AssetKey.parse("font", key) : key;
|
|
6045
6183
|
return await Caching.fontCache.get(fontKey.serialize(), () => this.load(fontKey, []));
|
|
@@ -6055,8 +6193,8 @@ var Fonts = class {
|
|
|
6055
6193
|
const stack = await AssetLoader.getAll(key, "json" /* JSON */);
|
|
6056
6194
|
if (stack.length === 0) throw new Error(`Could not load font ${key.toNamespacedString()}`);
|
|
6057
6195
|
const glyphs = /* @__PURE__ */ new Map();
|
|
6058
|
-
for (const
|
|
6059
|
-
for (const provider of
|
|
6196
|
+
for (const asset2 of stack) {
|
|
6197
|
+
for (const provider of asset2.providers) {
|
|
6060
6198
|
if (provider.filter?.uniform || provider.filter?.jp) continue;
|
|
6061
6199
|
let provided;
|
|
6062
6200
|
switch (provider.type) {
|
|
@@ -6202,13 +6340,13 @@ var OBJExporter = class {
|
|
|
6202
6340
|
* @param {Object3D} object - The 3D object to export.
|
|
6203
6341
|
* @return {string} The exported OBJ.
|
|
6204
6342
|
*/
|
|
6205
|
-
parse(
|
|
6343
|
+
parse(object3) {
|
|
6206
6344
|
let output = "";
|
|
6207
6345
|
let indexVertex = 0;
|
|
6208
6346
|
let indexVertexUvs = 0;
|
|
6209
6347
|
let indexNormals = 0;
|
|
6210
6348
|
const vertex = new Vector37();
|
|
6211
|
-
const
|
|
6349
|
+
const color2 = new Color4();
|
|
6212
6350
|
const normal = new Vector37();
|
|
6213
6351
|
const uv = new Vector22();
|
|
6214
6352
|
const face = [];
|
|
@@ -6307,9 +6445,9 @@ var OBJExporter = class {
|
|
|
6307
6445
|
vertex.applyMatrix4(points.matrixWorld);
|
|
6308
6446
|
output += "v " + vertex.x + " " + vertex.y + " " + vertex.z;
|
|
6309
6447
|
if (colors !== void 0) {
|
|
6310
|
-
|
|
6311
|
-
ColorManagement.workingToColorSpace(
|
|
6312
|
-
output += " " +
|
|
6448
|
+
color2.fromBufferAttribute(colors, i);
|
|
6449
|
+
ColorManagement.workingToColorSpace(color2, SRGBColorSpace3);
|
|
6450
|
+
output += " " + color2.r + " " + color2.g + " " + color2.b;
|
|
6313
6451
|
}
|
|
6314
6452
|
output += "\n";
|
|
6315
6453
|
}
|
|
@@ -6321,7 +6459,7 @@ var OBJExporter = class {
|
|
|
6321
6459
|
}
|
|
6322
6460
|
indexVertex += nbVertex;
|
|
6323
6461
|
}
|
|
6324
|
-
|
|
6462
|
+
object3.traverse(function(child) {
|
|
6325
6463
|
if (child.isMesh === true) {
|
|
6326
6464
|
parseMesh(child);
|
|
6327
6465
|
}
|
|
@@ -6355,9 +6493,9 @@ var PLYExporter = class {
|
|
|
6355
6493
|
* @param {PLYExporter~Options} options - The export options.
|
|
6356
6494
|
* @return {?(string|ArrayBuffer)} The exported PLY.
|
|
6357
6495
|
*/
|
|
6358
|
-
parse(
|
|
6496
|
+
parse(object3, onDone, options = {}) {
|
|
6359
6497
|
function traverseMeshes(cb) {
|
|
6360
|
-
|
|
6498
|
+
object3.traverse(function(child) {
|
|
6361
6499
|
if (child.isMesh === true || child.isPoints) {
|
|
6362
6500
|
const mesh = child;
|
|
6363
6501
|
const geometry = mesh.geometry;
|
|
@@ -6390,7 +6528,7 @@ var PLYExporter = class {
|
|
|
6390
6528
|
for (const name of customAttributeNames) customTypes[name] = "float";
|
|
6391
6529
|
let vertexCount = 0;
|
|
6392
6530
|
let faceCount = 0;
|
|
6393
|
-
|
|
6531
|
+
object3.traverse(function(child) {
|
|
6394
6532
|
if (child.isMesh === true) {
|
|
6395
6533
|
const mesh = child;
|
|
6396
6534
|
const geometry = mesh.geometry;
|
|
@@ -7012,8 +7150,8 @@ function equalArray(array1, array2) {
|
|
|
7012
7150
|
return element === array2[index];
|
|
7013
7151
|
});
|
|
7014
7152
|
}
|
|
7015
|
-
function stringToArrayBuffer(
|
|
7016
|
-
return new TextEncoder().encode(
|
|
7153
|
+
function stringToArrayBuffer(text2) {
|
|
7154
|
+
return new TextEncoder().encode(text2).buffer;
|
|
7017
7155
|
}
|
|
7018
7156
|
function isIdentityMatrix(matrix) {
|
|
7019
7157
|
return equalArray(matrix.elements, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
|
|
@@ -7206,12 +7344,12 @@ var GLTFWriter = class {
|
|
|
7206
7344
|
* @param {THREE.Object3D|THREE.Material|THREE.BufferGeometry|THREE.AnimationClip} object
|
|
7207
7345
|
* @param {Object} objectDef
|
|
7208
7346
|
*/
|
|
7209
|
-
serializeUserData(
|
|
7210
|
-
if (Object.keys(
|
|
7347
|
+
serializeUserData(object3, objectDef) {
|
|
7348
|
+
if (Object.keys(object3.userData).length === 0) return;
|
|
7211
7349
|
const options = this.options;
|
|
7212
7350
|
const extensionsUsed = this.extensionsUsed;
|
|
7213
7351
|
try {
|
|
7214
|
-
const json = JSON.parse(JSON.stringify(
|
|
7352
|
+
const json = JSON.parse(JSON.stringify(object3.userData));
|
|
7215
7353
|
if (options.includeCustomExtensions && json.gltfExtensions) {
|
|
7216
7354
|
if (objectDef.extensions === void 0) objectDef.extensions = {};
|
|
7217
7355
|
for (const extensionName in json.gltfExtensions) {
|
|
@@ -7222,7 +7360,7 @@ var GLTFWriter = class {
|
|
|
7222
7360
|
}
|
|
7223
7361
|
if (Object.keys(json).length > 0) objectDef.extras = json;
|
|
7224
7362
|
} catch (error) {
|
|
7225
|
-
console.warn("THREE.GLTFExporter: userData of '" +
|
|
7363
|
+
console.warn("THREE.GLTFExporter: userData of '" + object3.name + "' won't be serialized because of JSON.stringify error - " + error.message);
|
|
7226
7364
|
}
|
|
7227
7365
|
}
|
|
7228
7366
|
/**
|
|
@@ -7731,9 +7869,9 @@ var GLTFWriter = class {
|
|
|
7731
7869
|
if (material.isMeshStandardMaterial !== true && material.isMeshBasicMaterial !== true) {
|
|
7732
7870
|
console.warn("GLTFExporter: Use MeshStandardMaterial or MeshBasicMaterial for best results.");
|
|
7733
7871
|
}
|
|
7734
|
-
const
|
|
7735
|
-
if (!equalArray(
|
|
7736
|
-
materialDef.pbrMetallicRoughness.baseColorFactor =
|
|
7872
|
+
const color2 = material.color.toArray().concat([material.opacity]);
|
|
7873
|
+
if (!equalArray(color2, [1, 1, 1, 1])) {
|
|
7874
|
+
materialDef.pbrMetallicRoughness.baseColorFactor = color2;
|
|
7737
7875
|
}
|
|
7738
7876
|
if (material.isMeshStandardMaterial) {
|
|
7739
7877
|
materialDef.pbrMetallicRoughness.metallicFactor = material.metalness;
|
|
@@ -8148,13 +8286,13 @@ var GLTFWriter = class {
|
|
|
8148
8286
|
* @param {THREE.Object3D} object
|
|
8149
8287
|
* @return {?number}
|
|
8150
8288
|
*/
|
|
8151
|
-
processSkin(
|
|
8289
|
+
processSkin(object3) {
|
|
8152
8290
|
const json = this.json;
|
|
8153
8291
|
const nodeMap = this.nodeMap;
|
|
8154
|
-
const node = json.nodes[nodeMap.get(
|
|
8155
|
-
const skeleton =
|
|
8292
|
+
const node = json.nodes[nodeMap.get(object3)];
|
|
8293
|
+
const skeleton = object3.skeleton;
|
|
8156
8294
|
if (skeleton === void 0) return null;
|
|
8157
|
-
const rootJoint =
|
|
8295
|
+
const rootJoint = object3.skeleton.bones[0];
|
|
8158
8296
|
if (rootJoint === void 0) return null;
|
|
8159
8297
|
const joints = [];
|
|
8160
8298
|
const inverseBindMatrices = new Float32Array(skeleton.bones.length * 16);
|
|
@@ -8162,7 +8300,7 @@ var GLTFWriter = class {
|
|
|
8162
8300
|
for (let i = 0; i < skeleton.bones.length; ++i) {
|
|
8163
8301
|
joints.push(nodeMap.get(skeleton.bones[i]));
|
|
8164
8302
|
temporaryBoneInverse.copy(skeleton.boneInverses[i]);
|
|
8165
|
-
temporaryBoneInverse.multiply(
|
|
8303
|
+
temporaryBoneInverse.multiply(object3.bindMatrix).toArray(inverseBindMatrices, i * 16);
|
|
8166
8304
|
}
|
|
8167
8305
|
if (json.skins === void 0) json.skins = [];
|
|
8168
8306
|
json.skins.push({
|
|
@@ -8178,19 +8316,19 @@ var GLTFWriter = class {
|
|
|
8178
8316
|
* @param {THREE.Object3D} object Object3D to processNodeAsync
|
|
8179
8317
|
* @return {Promise<number>} Index of the node in the nodes list
|
|
8180
8318
|
*/
|
|
8181
|
-
async processNodeAsync(
|
|
8319
|
+
async processNodeAsync(object3) {
|
|
8182
8320
|
const json = this.json;
|
|
8183
8321
|
const options = this.options;
|
|
8184
8322
|
const nodeMap = this.nodeMap;
|
|
8185
8323
|
if (!json.nodes) json.nodes = [];
|
|
8186
|
-
if (
|
|
8187
|
-
return await this._processNodeWithPivotAsync(
|
|
8324
|
+
if (object3.pivot !== null) {
|
|
8325
|
+
return await this._processNodeWithPivotAsync(object3);
|
|
8188
8326
|
}
|
|
8189
8327
|
const nodeDef = {};
|
|
8190
8328
|
if (options.trs) {
|
|
8191
|
-
const rotation =
|
|
8192
|
-
const position =
|
|
8193
|
-
const scale =
|
|
8329
|
+
const rotation = object3.quaternion.toArray();
|
|
8330
|
+
const position = object3.position.toArray();
|
|
8331
|
+
const scale = object3.scale.toArray();
|
|
8194
8332
|
if (!equalArray(rotation, [0, 0, 0, 1])) {
|
|
8195
8333
|
nodeDef.rotation = rotation;
|
|
8196
8334
|
}
|
|
@@ -8201,28 +8339,28 @@ var GLTFWriter = class {
|
|
|
8201
8339
|
nodeDef.scale = scale;
|
|
8202
8340
|
}
|
|
8203
8341
|
} else {
|
|
8204
|
-
if (
|
|
8205
|
-
|
|
8342
|
+
if (object3.matrixAutoUpdate) {
|
|
8343
|
+
object3.updateMatrix();
|
|
8206
8344
|
}
|
|
8207
|
-
if (isIdentityMatrix(
|
|
8208
|
-
nodeDef.matrix =
|
|
8345
|
+
if (isIdentityMatrix(object3.matrix) === false) {
|
|
8346
|
+
nodeDef.matrix = object3.matrix.elements;
|
|
8209
8347
|
}
|
|
8210
8348
|
}
|
|
8211
|
-
if (
|
|
8212
|
-
this.serializeUserData(
|
|
8213
|
-
if (
|
|
8214
|
-
const meshIndex = await this.processMeshAsync(
|
|
8349
|
+
if (object3.name !== "") nodeDef.name = String(object3.name);
|
|
8350
|
+
this.serializeUserData(object3, nodeDef);
|
|
8351
|
+
if (object3.isMesh || object3.isLine || object3.isPoints) {
|
|
8352
|
+
const meshIndex = await this.processMeshAsync(object3);
|
|
8215
8353
|
if (meshIndex !== null) nodeDef.mesh = meshIndex;
|
|
8216
|
-
} else if (
|
|
8217
|
-
nodeDef.camera = this.processCamera(
|
|
8354
|
+
} else if (object3.isCamera) {
|
|
8355
|
+
nodeDef.camera = this.processCamera(object3);
|
|
8218
8356
|
}
|
|
8219
|
-
if (
|
|
8357
|
+
if (object3.isSkinnedMesh) this.skins.push(object3);
|
|
8220
8358
|
const nodeIndex = json.nodes.push(nodeDef) - 1;
|
|
8221
|
-
nodeMap.set(
|
|
8222
|
-
if (
|
|
8359
|
+
nodeMap.set(object3, nodeIndex);
|
|
8360
|
+
if (object3.children.length > 0) {
|
|
8223
8361
|
const children = [];
|
|
8224
|
-
for (let i = 0, l =
|
|
8225
|
-
const child =
|
|
8362
|
+
for (let i = 0, l = object3.children.length; i < l; i++) {
|
|
8363
|
+
const child = object3.children[i];
|
|
8226
8364
|
if (child.visible || options.onlyVisible === false) {
|
|
8227
8365
|
const childNodeIndex = await this.processNodeAsync(child);
|
|
8228
8366
|
if (childNodeIndex !== null) children.push(childNodeIndex);
|
|
@@ -8231,7 +8369,7 @@ var GLTFWriter = class {
|
|
|
8231
8369
|
if (children.length > 0) nodeDef.children = children;
|
|
8232
8370
|
}
|
|
8233
8371
|
await this._invokeAllAsync(function(ext) {
|
|
8234
|
-
ext.writeNode && ext.writeNode(
|
|
8372
|
+
ext.writeNode && ext.writeNode(object3, nodeDef);
|
|
8235
8373
|
});
|
|
8236
8374
|
return nodeIndex;
|
|
8237
8375
|
}
|
|
@@ -8240,19 +8378,19 @@ var GLTFWriter = class {
|
|
|
8240
8378
|
* @param {THREE.Object3D} object Object3D with pivot
|
|
8241
8379
|
* @return {Promise<number>} Index of the container node
|
|
8242
8380
|
*/
|
|
8243
|
-
async _processNodeWithPivotAsync(
|
|
8381
|
+
async _processNodeWithPivotAsync(object3) {
|
|
8244
8382
|
const json = this.json;
|
|
8245
8383
|
const options = this.options;
|
|
8246
8384
|
const nodeMap = this.nodeMap;
|
|
8247
|
-
const pivot =
|
|
8385
|
+
const pivot = object3.pivot;
|
|
8248
8386
|
const containerDef = {};
|
|
8249
|
-
const rotation =
|
|
8387
|
+
const rotation = object3.quaternion.toArray();
|
|
8250
8388
|
const position = [
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8389
|
+
object3.position.x + pivot.x,
|
|
8390
|
+
object3.position.y + pivot.y,
|
|
8391
|
+
object3.position.z + pivot.z
|
|
8254
8392
|
];
|
|
8255
|
-
const scale =
|
|
8393
|
+
const scale = object3.scale.toArray();
|
|
8256
8394
|
if (!equalArray(rotation, [0, 0, 0, 1])) {
|
|
8257
8395
|
containerDef.rotation = rotation;
|
|
8258
8396
|
}
|
|
@@ -8263,28 +8401,28 @@ var GLTFWriter = class {
|
|
|
8263
8401
|
containerDef.scale = scale;
|
|
8264
8402
|
}
|
|
8265
8403
|
containerDef.extras = { pivot: pivot.toArray() };
|
|
8266
|
-
if (
|
|
8267
|
-
this.serializeUserData(
|
|
8404
|
+
if (object3.name !== "") containerDef.name = String(object3.name);
|
|
8405
|
+
this.serializeUserData(object3, containerDef);
|
|
8268
8406
|
const containerIndex = json.nodes.push(containerDef) - 1;
|
|
8269
|
-
nodeMap.set(
|
|
8407
|
+
nodeMap.set(object3, containerIndex);
|
|
8270
8408
|
const childDef = {};
|
|
8271
8409
|
const childPosition = [-pivot.x, -pivot.y, -pivot.z];
|
|
8272
8410
|
if (!equalArray(childPosition, [0, 0, 0])) {
|
|
8273
8411
|
childDef.translation = childPosition;
|
|
8274
8412
|
}
|
|
8275
|
-
if (
|
|
8276
|
-
const meshIndex = await this.processMeshAsync(
|
|
8413
|
+
if (object3.isMesh || object3.isLine || object3.isPoints) {
|
|
8414
|
+
const meshIndex = await this.processMeshAsync(object3);
|
|
8277
8415
|
if (meshIndex !== null) childDef.mesh = meshIndex;
|
|
8278
|
-
} else if (
|
|
8279
|
-
childDef.camera = this.processCamera(
|
|
8416
|
+
} else if (object3.isCamera) {
|
|
8417
|
+
childDef.camera = this.processCamera(object3);
|
|
8280
8418
|
}
|
|
8281
|
-
if (
|
|
8419
|
+
if (object3.isSkinnedMesh) this.skins.push(object3);
|
|
8282
8420
|
const childIndex = json.nodes.push(childDef) - 1;
|
|
8283
8421
|
const containerChildren = [childIndex];
|
|
8284
|
-
if (
|
|
8422
|
+
if (object3.children.length > 0) {
|
|
8285
8423
|
const grandchildren = [];
|
|
8286
|
-
for (let i = 0, l =
|
|
8287
|
-
const child =
|
|
8424
|
+
for (let i = 0, l = object3.children.length; i < l; i++) {
|
|
8425
|
+
const child = object3.children[i];
|
|
8288
8426
|
if (child.visible || options.onlyVisible === false) {
|
|
8289
8427
|
const childNodeIndex = await this.processNodeAsync(child);
|
|
8290
8428
|
if (childNodeIndex !== null) grandchildren.push(childNodeIndex);
|
|
@@ -8294,7 +8432,7 @@ var GLTFWriter = class {
|
|
|
8294
8432
|
}
|
|
8295
8433
|
containerDef.children = containerChildren;
|
|
8296
8434
|
await this._invokeAllAsync(function(ext) {
|
|
8297
|
-
ext.writeNode && ext.writeNode(
|
|
8435
|
+
ext.writeNode && ext.writeNode(object3, containerDef);
|
|
8298
8436
|
});
|
|
8299
8437
|
return containerIndex;
|
|
8300
8438
|
}
|
|
@@ -8737,10 +8875,10 @@ var GLTFMeshGpuInstancing = class {
|
|
|
8737
8875
|
this.writer = writer;
|
|
8738
8876
|
this.name = "EXT_mesh_gpu_instancing";
|
|
8739
8877
|
}
|
|
8740
|
-
writeNode(
|
|
8741
|
-
if (!
|
|
8878
|
+
writeNode(object3, nodeDef) {
|
|
8879
|
+
if (!object3.isInstancedMesh) return;
|
|
8742
8880
|
const writer = this.writer;
|
|
8743
|
-
const mesh =
|
|
8881
|
+
const mesh = object3;
|
|
8744
8882
|
const translationAttr = new Float32Array(mesh.count * 3);
|
|
8745
8883
|
const rotationAttr = new Float32Array(mesh.count * 4);
|
|
8746
8884
|
const scaleAttr = new Float32Array(mesh.count * 3);
|
|
@@ -8930,8 +9068,8 @@ var SceneExporter = class {
|
|
|
8930
9068
|
}
|
|
8931
9069
|
}
|
|
8932
9070
|
};
|
|
8933
|
-
function localMatrix(
|
|
8934
|
-
return
|
|
9071
|
+
function localMatrix(object3) {
|
|
9072
|
+
return object3.matrixAutoUpdate ? new Matrix47().compose(object3.position, object3.quaternion, object3.scale) : object3.matrix.clone();
|
|
8935
9073
|
}
|
|
8936
9074
|
function createSnapshot(root, gltf, bakeColors = false) {
|
|
8937
9075
|
const scene = new Scene3();
|
|
@@ -9035,24 +9173,24 @@ function createSnapshot(root, gltf, bakeColors = false) {
|
|
|
9035
9173
|
scene.add(copy);
|
|
9036
9174
|
}
|
|
9037
9175
|
};
|
|
9038
|
-
const visit = (
|
|
9039
|
-
if (!
|
|
9040
|
-
const world = parentWorld.clone().multiply(localMatrix(
|
|
9041
|
-
const mesh =
|
|
9176
|
+
const visit = (object3, parentWorld) => {
|
|
9177
|
+
if (!object3.visible) return;
|
|
9178
|
+
const world = parentWorld.clone().multiply(localMatrix(object3));
|
|
9179
|
+
const mesh = object3;
|
|
9042
9180
|
if (mesh.isMesh) {
|
|
9043
9181
|
const instanced = mesh;
|
|
9044
9182
|
if (instanced.isInstancedMesh) {
|
|
9045
9183
|
for (let i = 0; i < instanced.count; i++) {
|
|
9046
9184
|
const matrix = new Matrix47();
|
|
9047
9185
|
instanced.getMatrixAt(i, matrix);
|
|
9048
|
-
const
|
|
9049
|
-
addMesh(mesh, world.clone().multiply(matrix),
|
|
9186
|
+
const color2 = instanced.instanceColor ? new Color7().fromBufferAttribute(instanced.instanceColor, i) : void 0;
|
|
9187
|
+
addMesh(mesh, world.clone().multiply(matrix), color2);
|
|
9050
9188
|
}
|
|
9051
9189
|
} else {
|
|
9052
9190
|
addMesh(mesh, world);
|
|
9053
9191
|
}
|
|
9054
9192
|
}
|
|
9055
|
-
for (const child of
|
|
9193
|
+
for (const child of object3.children) visit(child, world);
|
|
9056
9194
|
};
|
|
9057
9195
|
try {
|
|
9058
9196
|
const ancestors = [];
|
|
@@ -9128,13 +9266,13 @@ init_AssetKey();
|
|
|
9128
9266
|
|
|
9129
9267
|
// src/gui/GuiText.ts
|
|
9130
9268
|
import { BufferGeometry as BufferGeometry2, Color as Color8, Float32BufferAttribute as Float32BufferAttribute4 } from "three";
|
|
9131
|
-
async function layoutGuiText(
|
|
9269
|
+
async function layoutGuiText(text2, options = {}) {
|
|
9132
9270
|
const maxWidth = options.maxWidth ?? Infinity, lineHeight = options.lineHeight ?? 9;
|
|
9133
9271
|
if (!(maxWidth > 0) || !Number.isFinite(lineHeight) || lineHeight <= 0) {
|
|
9134
9272
|
throw new Error("Text wrap width and line height must be positive");
|
|
9135
9273
|
}
|
|
9136
9274
|
const font = await Fonts.get(options.font);
|
|
9137
|
-
const runs = typeof
|
|
9275
|
+
const runs = typeof text2 === "string" ? [{ text: text2 }] : text2;
|
|
9138
9276
|
const glyphs = [], lineWidths = [];
|
|
9139
9277
|
let line = [], width = 0;
|
|
9140
9278
|
let trailingNewline = false;
|
|
@@ -9188,7 +9326,7 @@ function createGuiTextGeometry(layout, shadow = true) {
|
|
|
9188
9326
|
batch = { image: glyph?.image, positions: [], uvs: [], colors: [], indices: [] };
|
|
9189
9327
|
batches.push(batch);
|
|
9190
9328
|
}
|
|
9191
|
-
const
|
|
9329
|
+
const color2 = new Color8(isShadow ? (style.color & 16579836) >> 2 : style.color);
|
|
9192
9330
|
const offset = isShadow ? 1 : 0;
|
|
9193
9331
|
const top = glyph ? 7 - glyph.ascent : 0;
|
|
9194
9332
|
const height = glyph ? glyph.height * glyph.scale : 8;
|
|
@@ -9216,7 +9354,7 @@ function createGuiTextGeometry(layout, shadow = true) {
|
|
|
9216
9354
|
const v0 = glyph ? 1 - glyph.y / glyph.image.height : 1;
|
|
9217
9355
|
const v1 = glyph ? 1 - (glyph.y + glyph.height) / glyph.image.height : 0;
|
|
9218
9356
|
batch.uvs.push(u0, v0, u1, v0, u0, v1, u1, v1);
|
|
9219
|
-
for (let i = 0; i < 4; i++) batch.colors.push(
|
|
9357
|
+
for (let i = 0; i < 4; i++) batch.colors.push(color2.r, color2.g, color2.b);
|
|
9220
9358
|
batch.indices.push(base, base + 2, base + 1, base + 2, base + 3, base + 1);
|
|
9221
9359
|
};
|
|
9222
9360
|
for (const boldOffset of style.bold ? [0, 1] : [0]) {
|
|
@@ -9250,12 +9388,19 @@ var GuiHelper = class {
|
|
|
9250
9388
|
size: [width + 24, height + 24]
|
|
9251
9389
|
}));
|
|
9252
9390
|
let y = position[1];
|
|
9253
|
-
lines.forEach((
|
|
9254
|
-
layers.push({ ...textOptions, name: `tooltip-line-${index}`, text, position: [position[0], y] });
|
|
9391
|
+
lines.forEach((text2, index) => {
|
|
9392
|
+
layers.push({ ...textOptions, name: `tooltip-line-${index}`, text: text2, position: [position[0], y] });
|
|
9255
9393
|
y += layouts[index].height + (index === 0 ? titleGap : 0);
|
|
9256
9394
|
});
|
|
9257
9395
|
return layers;
|
|
9258
9396
|
}
|
|
9397
|
+
/**
|
|
9398
|
+
* Calculates a slot's top-left position in GUI pixels.
|
|
9399
|
+
* @param slot - Zero-based slot index, or `[column, row]`.
|
|
9400
|
+
* @param origin - Position of the first slot.
|
|
9401
|
+
* @param offset - Horizontal and vertical spacing between slot origins, in pixels.
|
|
9402
|
+
* @param rowSize - Slots per row when `slot` is an index.
|
|
9403
|
+
*/
|
|
9259
9404
|
static inventorySlot(slot, origin = [0, 0], offset = [18, 18], rowSize = 9) {
|
|
9260
9405
|
const [column, row] = typeof slot === "number" ? [slot % rowSize, Math.floor(slot / rowSize)] : slot;
|
|
9261
9406
|
return [origin[0] + column * offset[0], origin[1] + row * offset[1]];
|
|
@@ -9700,29 +9845,29 @@ var SceneInspector = class {
|
|
|
9700
9845
|
this.objectControlsContainer.innerHTML = "";
|
|
9701
9846
|
this.addControls(targetObject, targetIntersection, instance);
|
|
9702
9847
|
}
|
|
9703
|
-
addControls(
|
|
9848
|
+
addControls(object3, intersection, instance) {
|
|
9704
9849
|
const container = document.createElement("div");
|
|
9705
9850
|
container.append(this.separator("Select Parent/Child"));
|
|
9706
|
-
if (
|
|
9707
|
-
container.append(this.buttonControl("Select Parent " +
|
|
9708
|
-
this.selectObject(
|
|
9851
|
+
if (object3.parent) {
|
|
9852
|
+
container.append(this.buttonControl("Select Parent " + object3.parent.constructor.name + " " + object3.parent.name, "P", () => {
|
|
9853
|
+
this.selectObject(object3.parent, intersection, instance);
|
|
9709
9854
|
}));
|
|
9710
9855
|
}
|
|
9711
|
-
if (
|
|
9856
|
+
if (object3.children.length > 0) {
|
|
9712
9857
|
let i = 1;
|
|
9713
|
-
for (let child of
|
|
9858
|
+
for (let child of object3.children) {
|
|
9714
9859
|
container.append(this.buttonControl("Select Child " + child.constructor.name + " " + child.name, "C" + i++, () => {
|
|
9715
9860
|
this.selectObject(child, intersection, instance);
|
|
9716
9861
|
}));
|
|
9717
9862
|
}
|
|
9718
9863
|
}
|
|
9719
9864
|
container.append(this.separator());
|
|
9720
|
-
const selectedInstance = instance && (
|
|
9865
|
+
const selectedInstance = instance && (object3 === instance.instanceable || object3 === intersection?.object) ? instance : void 0;
|
|
9721
9866
|
if (!selectedInstance) {
|
|
9722
|
-
container.append(this.toggleControl("Visibility", "V",
|
|
9867
|
+
container.append(this.toggleControl("Visibility", "V", object3.visible, (v) => object3.visible = v));
|
|
9723
9868
|
}
|
|
9724
9869
|
container.append(this.separator("Mesh"));
|
|
9725
|
-
this.addObjectControls(
|
|
9870
|
+
this.addObjectControls(object3, intersection, container, selectedInstance);
|
|
9726
9871
|
container.append(this.separator());
|
|
9727
9872
|
this.objectControlsContainer.append(container);
|
|
9728
9873
|
}
|
|
@@ -9913,6 +10058,7 @@ var SceneStatsDisplay = class {
|
|
|
9913
10058
|
appendTo(el) {
|
|
9914
10059
|
el.append(this.statsContainer);
|
|
9915
10060
|
}
|
|
10061
|
+
/** Stops the refresh timer and removes the panel from the page. */
|
|
9916
10062
|
dispose() {
|
|
9917
10063
|
clearInterval(this.interval);
|
|
9918
10064
|
this.interval = void 0;
|
|
@@ -9937,6 +10083,7 @@ var InstanceManager = class {
|
|
|
9937
10083
|
this.isInstanceManager = true;
|
|
9938
10084
|
this.instanceCache = {};
|
|
9939
10085
|
}
|
|
10086
|
+
/** Allocates a new placement of a cached model, or returns `undefined` if the key is unknown. */
|
|
9940
10087
|
async get(key) {
|
|
9941
10088
|
if (key in this.instanceCache) {
|
|
9942
10089
|
console.debug(p9, "key in cache", key);
|
|
@@ -9944,6 +10091,7 @@ var InstanceManager = class {
|
|
|
9944
10091
|
}
|
|
9945
10092
|
return void 0;
|
|
9946
10093
|
}
|
|
10094
|
+
/** Allocates a placement, calling the supplier once when the shared model is first needed. */
|
|
9947
10095
|
async getOrCreate(key, supplier) {
|
|
9948
10096
|
if (key in this.instanceCache) {
|
|
9949
10097
|
return await this.get(key);
|
|
@@ -9960,6 +10108,7 @@ var InstanceManager = class {
|
|
|
9960
10108
|
throw error;
|
|
9961
10109
|
}
|
|
9962
10110
|
}
|
|
10111
|
+
/** Forgets cached models without disposing them or releasing their existing placements. */
|
|
9963
10112
|
reset() {
|
|
9964
10113
|
for (let instanceCacheKey in this.instanceCache) {
|
|
9965
10114
|
delete this.instanceCache[instanceCacheKey];
|
|
@@ -10005,6 +10154,7 @@ function matchesCondition(condition, state) {
|
|
|
10005
10154
|
});
|
|
10006
10155
|
}
|
|
10007
10156
|
var BlockStateResolver = class {
|
|
10157
|
+
/** Loads vanilla property defaults, falling back to preview values inferred from the blockstate file. */
|
|
10008
10158
|
static async defaults(blockState) {
|
|
10009
10159
|
const defaults = blockState.key ? await BlockStates.getDefaultState(blockState.key) : void 0;
|
|
10010
10160
|
const state = {};
|
|
@@ -10024,9 +10174,11 @@ var BlockStateResolver = class {
|
|
|
10024
10174
|
}
|
|
10025
10175
|
return state;
|
|
10026
10176
|
}
|
|
10177
|
+
/** Selects matching models. Supply `choose` to control selection within each alternatives array. */
|
|
10027
10178
|
static select(blockState, state, choose = this.choose) {
|
|
10028
10179
|
return this.matching(blockState, state).map(choose);
|
|
10029
10180
|
}
|
|
10181
|
+
/** Returns matching model groups before choosing weighted alternatives. */
|
|
10030
10182
|
static matching(blockState, state) {
|
|
10031
10183
|
const out = [];
|
|
10032
10184
|
if (blockState.variants) {
|
|
@@ -10050,6 +10202,7 @@ var BlockStateResolver = class {
|
|
|
10050
10202
|
}
|
|
10051
10203
|
return out;
|
|
10052
10204
|
}
|
|
10205
|
+
/** Selects a random alternative using positive integer weights, each defaulting to 1. */
|
|
10053
10206
|
static choose(variants) {
|
|
10054
10207
|
if (!Array.isArray(variants)) return variants;
|
|
10055
10208
|
if (!variants.length) throw new MineRenderError("Blockstate variant arrays must not be empty");
|
|
@@ -10067,6 +10220,7 @@ var BlockStateResolver = class {
|
|
|
10067
10220
|
}
|
|
10068
10221
|
return variants[variants.length - 1];
|
|
10069
10222
|
}
|
|
10223
|
+
/** Converts blockstate rotations in degrees into the Euler rotation used by model rendering. */
|
|
10070
10224
|
static rotation(variant) {
|
|
10071
10225
|
const rotation = new Euler8();
|
|
10072
10226
|
if (typeof variant.x !== "undefined") {
|
|
@@ -10129,29 +10283,29 @@ var BlockTints = class {
|
|
|
10129
10283
|
}
|
|
10130
10284
|
}
|
|
10131
10285
|
if (!indices.size) return tints;
|
|
10132
|
-
let
|
|
10286
|
+
let color2;
|
|
10133
10287
|
switch (rule.source) {
|
|
10134
10288
|
case "constant":
|
|
10135
|
-
|
|
10289
|
+
color2 = rule.color;
|
|
10136
10290
|
break;
|
|
10137
10291
|
case "grass":
|
|
10138
|
-
|
|
10292
|
+
color2 = await Colormaps.grassColor(key);
|
|
10139
10293
|
break;
|
|
10140
10294
|
case "redstone": {
|
|
10141
10295
|
const power = Number(state.power ?? 0) / 15;
|
|
10142
10296
|
const red = power * 0.6 + (power > 0 ? 0.4 : 0.3);
|
|
10143
10297
|
const green = Math.max(0, power * power * 0.7 - 0.5);
|
|
10144
10298
|
const blue = Math.max(0, power * power * 0.6 - 0.7);
|
|
10145
|
-
|
|
10299
|
+
color2 = Math.floor(red * 255) << 16 | Math.floor(green * 255) << 8 | Math.floor(blue * 255);
|
|
10146
10300
|
break;
|
|
10147
10301
|
}
|
|
10148
10302
|
case "stem": {
|
|
10149
10303
|
const age = Number(state.age ?? 0);
|
|
10150
|
-
|
|
10304
|
+
color2 = age * 32 << 16 | 255 - age * 8 << 8 | age * 4;
|
|
10151
10305
|
break;
|
|
10152
10306
|
}
|
|
10153
10307
|
}
|
|
10154
|
-
return { ...Object.fromEntries([...indices].map((index) => [index,
|
|
10308
|
+
return { ...Object.fromEntries([...indices].map((index) => [index, color2])), ...tints };
|
|
10155
10309
|
}
|
|
10156
10310
|
};
|
|
10157
10311
|
|
|
@@ -10288,8 +10442,8 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
10288
10442
|
return false;
|
|
10289
10443
|
}
|
|
10290
10444
|
return this._models.some((model) => {
|
|
10291
|
-
const
|
|
10292
|
-
return
|
|
10445
|
+
const object3 = isInstanceReference(model) ? model.instanceable : model;
|
|
10446
|
+
return object3.isOpaqueFullCube && this.getModelCullMask(model, 63) === 63;
|
|
10293
10447
|
});
|
|
10294
10448
|
}
|
|
10295
10449
|
getModelMatrix(model) {
|
|
@@ -10298,8 +10452,8 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
10298
10452
|
return model.matrix.clone();
|
|
10299
10453
|
}
|
|
10300
10454
|
getModelCullMask(model, worldMask) {
|
|
10301
|
-
const
|
|
10302
|
-
if (
|
|
10455
|
+
const object3 = isInstanceReference(model) ? model.instanceable : model;
|
|
10456
|
+
if (object3.options.displayPosition) return 0;
|
|
10303
10457
|
const position = new Vector312();
|
|
10304
10458
|
const rotation = new Quaternion5();
|
|
10305
10459
|
const scale = new Vector312();
|
|
@@ -10316,14 +10470,14 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
10316
10470
|
replacements.push(model);
|
|
10317
10471
|
continue;
|
|
10318
10472
|
}
|
|
10319
|
-
const
|
|
10473
|
+
const object3 = isInstanceReference(model) ? model.instanceable : model;
|
|
10320
10474
|
const cullMask = this.getModelCullMask(model, worldMask);
|
|
10321
|
-
if (cullMask === (
|
|
10475
|
+
if (cullMask === (object3.options.cullMask ?? 0)) {
|
|
10322
10476
|
replacements.push(model);
|
|
10323
10477
|
continue;
|
|
10324
10478
|
}
|
|
10325
10479
|
const matrix = this.getModelMatrix(model);
|
|
10326
|
-
const replacement = await this.scene.addModel(
|
|
10480
|
+
const replacement = await this.scene.addModel(object3.originalModel, { ...object3.options, cullMask });
|
|
10327
10481
|
replacements.push(replacement);
|
|
10328
10482
|
if (isInstanceReference(replacement)) {
|
|
10329
10483
|
replacement.setMatrix(matrix);
|
|
@@ -10331,8 +10485,8 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
10331
10485
|
replacement.matrix.copy(matrix);
|
|
10332
10486
|
replacement.matrixWorldNeedsUpdate = true;
|
|
10333
10487
|
matrix.decompose(replacement.position, replacement.quaternion, replacement.scale);
|
|
10334
|
-
replacement.matrixAutoUpdate =
|
|
10335
|
-
replacement.visible =
|
|
10488
|
+
replacement.matrixAutoUpdate = object3.matrixAutoUpdate;
|
|
10489
|
+
replacement.visible = object3.visible;
|
|
10336
10490
|
}
|
|
10337
10491
|
}
|
|
10338
10492
|
} catch (error) {
|
|
@@ -10348,6 +10502,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
10348
10502
|
}
|
|
10349
10503
|
this.notifyDirty();
|
|
10350
10504
|
}
|
|
10505
|
+
/** Selected property values. Use {@link setState} to change properties and rebuild the rendered models. */
|
|
10351
10506
|
get state() {
|
|
10352
10507
|
return this._state;
|
|
10353
10508
|
}
|
|
@@ -10447,6 +10602,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
|
|
|
10447
10602
|
obj.setPosition(this.position);
|
|
10448
10603
|
return obj;
|
|
10449
10604
|
}
|
|
10605
|
+
/** Clears selected properties and rebuilds models without reapplying property defaults. */
|
|
10450
10606
|
async resetState() {
|
|
10451
10607
|
this._previousState = this._state;
|
|
10452
10608
|
this._state = {};
|
|
@@ -12350,6 +12506,11 @@ function compoundList(tags, name) {
|
|
|
12350
12506
|
// src/model/multiblock/StructureParser.ts
|
|
12351
12507
|
init_MineRenderError();
|
|
12352
12508
|
var StructureParser = class {
|
|
12509
|
+
/**
|
|
12510
|
+
* Parses decoded NBT, omitting air blocks.
|
|
12511
|
+
* @param _nbt - Structure data decoded by {@link NBTHelper.fromBuffer}.
|
|
12512
|
+
* @param paletteIndex - Palette to select when the structure has several. Defaults to 0.
|
|
12513
|
+
*/
|
|
12353
12514
|
static async parse(_nbt, paletteIndex) {
|
|
12354
12515
|
const nbt = _nbt.value;
|
|
12355
12516
|
let palette;
|
|
@@ -13184,6 +13345,7 @@ var SkinImage = class {
|
|
|
13184
13345
|
// src/skin/SkinTextures.ts
|
|
13185
13346
|
init_Textures();
|
|
13186
13347
|
var SkinTextures = class {
|
|
13348
|
+
/** Loads a skin URL. Pass `legacy` to override layout detection from the image dimensions. */
|
|
13187
13349
|
static async get(src, legacy) {
|
|
13188
13350
|
const image = await SkinImage.getData(src);
|
|
13189
13351
|
const scale = image.width / 64;
|
|
@@ -13357,6 +13519,14 @@ var SkinObject = class extends SceneObject {
|
|
|
13357
13519
|
}
|
|
13358
13520
|
}
|
|
13359
13521
|
}
|
|
13522
|
+
/**
|
|
13523
|
+
* Loads a skin texture, detecting slim arms and legacy layouts by default.
|
|
13524
|
+
* To override detection, set `slim` or `legacy` in the options passed to
|
|
13525
|
+
* `new SkinObject(options)` or `scene.addSkin(src, options)`.
|
|
13526
|
+
* Use {@link setSlim} or {@link setLegacy} to change these settings later.
|
|
13527
|
+
*
|
|
13528
|
+
* @param src - URL of the skin texture.
|
|
13529
|
+
*/
|
|
13360
13530
|
async setSkinTexture(src) {
|
|
13361
13531
|
if (typeof src === "undefined") return;
|
|
13362
13532
|
this.skinTextureSrc = src;
|
|
@@ -13464,59 +13634,60 @@ var _MineRenderScene = class _MineRenderScene extends Scene4 {
|
|
|
13464
13634
|
this.options = (0, import_ts_deepmerge8.default)({}, _MineRenderScene.DEFAULT_OPTIONS, options ?? {});
|
|
13465
13635
|
}
|
|
13466
13636
|
add(...objects) {
|
|
13467
|
-
for (const
|
|
13637
|
+
for (const object3 of objects) {
|
|
13468
13638
|
this.dirty = true;
|
|
13469
13639
|
try {
|
|
13470
|
-
super.add(
|
|
13640
|
+
super.add(object3);
|
|
13471
13641
|
} finally {
|
|
13472
|
-
if (
|
|
13473
|
-
this.updateObjectRegistration(
|
|
13642
|
+
if (object3?.isObject3D && object3 !== this) {
|
|
13643
|
+
this.updateObjectRegistration(object3, object3.parent === this);
|
|
13474
13644
|
}
|
|
13475
13645
|
}
|
|
13476
13646
|
}
|
|
13477
13647
|
return this;
|
|
13478
13648
|
}
|
|
13479
13649
|
remove(...objects) {
|
|
13480
|
-
for (const
|
|
13481
|
-
if (!this.children.includes(
|
|
13650
|
+
for (const object3 of objects) {
|
|
13651
|
+
if (!this.children.includes(object3)) continue;
|
|
13482
13652
|
this.dirty = true;
|
|
13483
|
-
this.updateObjectRegistration(
|
|
13653
|
+
this.updateObjectRegistration(object3, false);
|
|
13484
13654
|
try {
|
|
13485
|
-
super.remove(
|
|
13655
|
+
super.remove(object3);
|
|
13486
13656
|
} finally {
|
|
13487
|
-
this.updateObjectRegistration(
|
|
13657
|
+
this.updateObjectRegistration(object3, object3.parent === this);
|
|
13488
13658
|
}
|
|
13489
13659
|
}
|
|
13490
13660
|
return this;
|
|
13491
13661
|
}
|
|
13492
|
-
updateObjectRegistration(
|
|
13493
|
-
if (this.observedObjects.has(
|
|
13494
|
-
const observable =
|
|
13662
|
+
updateObjectRegistration(object3, attached) {
|
|
13663
|
+
if (this.observedObjects.has(object3) === attached) return;
|
|
13664
|
+
const observable = object3;
|
|
13495
13665
|
if (attached) {
|
|
13496
|
-
this.observedObjects.add(
|
|
13666
|
+
this.observedObjects.add(object3);
|
|
13497
13667
|
observable.addEventListener("change", this.onObjectChange);
|
|
13498
13668
|
} else {
|
|
13499
|
-
this.observedObjects.delete(
|
|
13669
|
+
this.observedObjects.delete(object3);
|
|
13500
13670
|
observable.removeEventListener("change", this.onObjectChange);
|
|
13501
13671
|
}
|
|
13502
13672
|
const change = attached ? 1 : -1;
|
|
13503
13673
|
this.stats.objectCount += change;
|
|
13504
|
-
if (isSceneObject(
|
|
13674
|
+
if (isSceneObject(object3)) {
|
|
13505
13675
|
this.stats.sceneObjectCount += change;
|
|
13506
13676
|
}
|
|
13507
13677
|
}
|
|
13508
|
-
|
|
13678
|
+
/** Initializes each object before attaching it to the scene. */
|
|
13679
|
+
async initAndAdd(...object3) {
|
|
13509
13680
|
this.dirty = true;
|
|
13510
|
-
for (let obj of
|
|
13681
|
+
for (let obj of object3) {
|
|
13511
13682
|
await obj.init();
|
|
13512
13683
|
}
|
|
13513
|
-
return this.add(...
|
|
13684
|
+
return this.add(...object3);
|
|
13514
13685
|
}
|
|
13515
|
-
async addSceneObject(
|
|
13686
|
+
async addSceneObject(asset2, objectSupplier, _options, parent = this) {
|
|
13516
13687
|
this.dirty = true;
|
|
13517
13688
|
const obj = await objectSupplier();
|
|
13518
|
-
if (obj?.options?.instanceMeshes &&
|
|
13519
|
-
let key =
|
|
13689
|
+
if (obj?.options?.instanceMeshes && asset2.key && asset2.key?.assetType === "models") {
|
|
13690
|
+
let key = asset2.key.serialize();
|
|
13520
13691
|
if (isModelObject(obj)) {
|
|
13521
13692
|
const { displayPosition, uvLockRotation, tints, cullMask } = obj.options;
|
|
13522
13693
|
if (displayPosition) key += `|display:${displayPosition}`;
|
|
@@ -13540,13 +13711,33 @@ var _MineRenderScene = class _MineRenderScene extends Scene4 {
|
|
|
13540
13711
|
return obj;
|
|
13541
13712
|
}
|
|
13542
13713
|
}
|
|
13714
|
+
/**
|
|
13715
|
+
* Adds a model loaded with {@link Models.getMerged}.
|
|
13716
|
+
* Set `options.instanceMeshes` to `false` to create a separate ModelObject.
|
|
13717
|
+
*
|
|
13718
|
+
* @param parent - Object to attach the model to. Defaults to this scene.
|
|
13719
|
+
* @returns The model, or a reference to one placement of a shared model.
|
|
13720
|
+
*/
|
|
13543
13721
|
async addModel(model, options, parent = this) {
|
|
13544
13722
|
options = { ...options, tints: await ItemTints.get(model, options?.tints) };
|
|
13545
13723
|
return this.addSceneObject(model, () => new ModelObject(model, options), options, parent);
|
|
13546
13724
|
}
|
|
13725
|
+
/**
|
|
13726
|
+
* Adds the models selected by a blockstate loaded with {@link BlockStates.get}.
|
|
13727
|
+
* Set `options.initialState` to choose block properties such as `facing` or `axis`.
|
|
13728
|
+
* Use the returned object's `setPosition` method to move all of its model parts.
|
|
13729
|
+
*/
|
|
13547
13730
|
async addBlock(blockState, options, parent = this) {
|
|
13548
13731
|
return this.addSceneObject(blockState, () => new BlockObject(blockState, options), options, parent);
|
|
13549
13732
|
}
|
|
13733
|
+
/**
|
|
13734
|
+
* Loads a skin and adds an initialized player model.
|
|
13735
|
+
*
|
|
13736
|
+
* @param skin - Texture URL, such as one returned by {@link Skins.fromUuidOrUsername}.
|
|
13737
|
+
* Omit it to assign a texture later with {@link SkinObject.setSkinTexture}.
|
|
13738
|
+
* @param options - Player settings, including `slim` and `legacy` detection overrides.
|
|
13739
|
+
* @param parent - Object to attach the player to. Defaults to this scene.
|
|
13740
|
+
*/
|
|
13550
13741
|
async addSkin(skin, options, parent = this) {
|
|
13551
13742
|
this.dirty = true;
|
|
13552
13743
|
const obj = new SkinObject(options);
|
|
@@ -13558,9 +13749,14 @@ var _MineRenderScene = class _MineRenderScene extends Scene4 {
|
|
|
13558
13749
|
parent.add(obj);
|
|
13559
13750
|
return obj;
|
|
13560
13751
|
}
|
|
13752
|
+
/** Adds an entity loaded with {@link Entities.getEntity}, using its selected layers and textures. */
|
|
13561
13753
|
async addEntity(entity, options, parent = this) {
|
|
13562
13754
|
return this.addSceneObject(entity, () => new EntityObject(entity, options), options, parent);
|
|
13563
13755
|
}
|
|
13756
|
+
/**
|
|
13757
|
+
* Adds GUI layers in draw order. One GUI pixel equals one scene unit.
|
|
13758
|
+
* Layer positions start at the top left, with positive GUI y pointing down.
|
|
13759
|
+
*/
|
|
13564
13760
|
async addGui(layers, options, parent = this) {
|
|
13565
13761
|
const obj = new GuiObject(layers, options);
|
|
13566
13762
|
obj.scene = this;
|
|
@@ -13599,11 +13795,11 @@ var _changeEvent = { type: "change" };
|
|
|
13599
13795
|
var _startEvent = { type: "start" };
|
|
13600
13796
|
var _endEvent = { type: "end" };
|
|
13601
13797
|
var OrbitControls = class extends EventDispatcher {
|
|
13602
|
-
constructor(
|
|
13798
|
+
constructor(object3, domElement) {
|
|
13603
13799
|
super();
|
|
13604
13800
|
if (domElement === void 0) console.warn('THREE.OrbitControls: The second parameter "domElement" is now mandatory.');
|
|
13605
13801
|
if (domElement === document) console.error('THREE.OrbitControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.');
|
|
13606
|
-
this.object =
|
|
13802
|
+
this.object = object3;
|
|
13607
13803
|
this.domElement = domElement;
|
|
13608
13804
|
this.domElement.style.touchAction = "none";
|
|
13609
13805
|
this.enabled = true;
|
|
@@ -13660,7 +13856,7 @@ var OrbitControls = class extends EventDispatcher {
|
|
|
13660
13856
|
};
|
|
13661
13857
|
this.update = function() {
|
|
13662
13858
|
const offset = new Vector313();
|
|
13663
|
-
const quat = new Quaternion6().setFromUnitVectors(
|
|
13859
|
+
const quat = new Quaternion6().setFromUnitVectors(object3.up, new Vector313(0, 1, 0));
|
|
13664
13860
|
const quatInverse = quat.clone().invert();
|
|
13665
13861
|
const lastPosition = new Vector313();
|
|
13666
13862
|
const lastQuaternion = new Quaternion6();
|
|
@@ -14225,8 +14421,8 @@ var OrbitControls = class extends EventDispatcher {
|
|
|
14225
14421
|
}
|
|
14226
14422
|
};
|
|
14227
14423
|
var MapControls = class extends OrbitControls {
|
|
14228
|
-
constructor(
|
|
14229
|
-
super(
|
|
14424
|
+
constructor(object3, domElement) {
|
|
14425
|
+
super(object3, domElement);
|
|
14230
14426
|
this.screenSpacePanning = false;
|
|
14231
14427
|
this.mouseButtons.LEFT = MOUSE.PAN;
|
|
14232
14428
|
this.mouseButtons.RIGHT = MOUSE.ROTATE;
|
|
@@ -14385,6 +14581,7 @@ var _Renderer = class _Renderer {
|
|
|
14385
14581
|
get element() {
|
|
14386
14582
|
return this._element;
|
|
14387
14583
|
}
|
|
14584
|
+
/** Attaches the canvas to an element and sizes it to that element's dimensions. */
|
|
14388
14585
|
appendTo(element) {
|
|
14389
14586
|
if (this._disposed) return;
|
|
14390
14587
|
this._element = element;
|
|
@@ -14412,6 +14609,7 @@ var _Renderer = class _Renderer {
|
|
|
14412
14609
|
events.add(changeEvent2);
|
|
14413
14610
|
dispatcher.addEventListener(changeEvent2, this._changeListener);
|
|
14414
14611
|
}
|
|
14612
|
+
/** Updates the camera and canvas size. Width and height are in CSS pixels. */
|
|
14415
14613
|
resize(width, height) {
|
|
14416
14614
|
if (this._disposed) return;
|
|
14417
14615
|
if (isPerspectiveCamera(this.camera)) {
|
|
@@ -14450,12 +14648,14 @@ var _Renderer = class _Renderer {
|
|
|
14450
14648
|
if (this._frameCallbacks.get(callback) === subscription) this._frameCallbacks.delete(callback);
|
|
14451
14649
|
};
|
|
14452
14650
|
}
|
|
14651
|
+
/** Starts or resumes the animation loop and registered frame callbacks. */
|
|
14453
14652
|
start() {
|
|
14454
14653
|
if (this._disposed) return;
|
|
14455
14654
|
this.stop();
|
|
14456
14655
|
this._running = true;
|
|
14457
14656
|
if (!this._inAnimationLoop) this.renderer.setAnimationLoop(this._animationLoop);
|
|
14458
14657
|
}
|
|
14658
|
+
/** Pauses rendering and frame callbacks. Call {@link start} to resume. */
|
|
14459
14659
|
stop() {
|
|
14460
14660
|
if (this._disposed) return;
|
|
14461
14661
|
this._running = false;
|
|
@@ -14564,12 +14764,15 @@ var _Renderer = class _Renderer {
|
|
|
14564
14764
|
const canvas = trim ? trimCanvas(this.renderer.domElement) : this.renderer.domElement;
|
|
14565
14765
|
return canvas.toDataURL(mime, quality);
|
|
14566
14766
|
}
|
|
14767
|
+
/** Exports visible scene meshes as OBJ text, without texture images. */
|
|
14567
14768
|
toObj() {
|
|
14568
14769
|
return SceneExporter.toObj(this.scene);
|
|
14569
14770
|
}
|
|
14771
|
+
/** Exports visible scene meshes as PLY. Set `options.binary` for an ArrayBuffer. */
|
|
14570
14772
|
toPLY(options) {
|
|
14571
14773
|
return SceneExporter.toPLY(this.scene, options);
|
|
14572
14774
|
}
|
|
14775
|
+
/** Exports a static textured scene in the browser. Set `options.binary` for GLB output. */
|
|
14573
14776
|
toGLTF(options) {
|
|
14574
14777
|
return SceneExporter.toGLTF(this.scene, options);
|
|
14575
14778
|
}
|
|
@@ -14647,16 +14850,244 @@ init_SceneObject();
|
|
|
14647
14850
|
// src/index.ts
|
|
14648
14851
|
init_request();
|
|
14649
14852
|
|
|
14853
|
+
// src/scene/SceneDocument.ts
|
|
14854
|
+
init_DisplayPosition();
|
|
14855
|
+
function fail(path, message) {
|
|
14856
|
+
throw new Error(`${path}: ${message}`);
|
|
14857
|
+
}
|
|
14858
|
+
function object2(value, path) {
|
|
14859
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail(path, "expected a JSON object");
|
|
14860
|
+
for (const key of Object.keys(value)) {
|
|
14861
|
+
if (["__proto__", "prototype", "constructor"].includes(key)) fail(`${path}.${key}`, "unsupported property");
|
|
14862
|
+
}
|
|
14863
|
+
return value;
|
|
14864
|
+
}
|
|
14865
|
+
function fields(value, path, allowed, required = []) {
|
|
14866
|
+
const input = object2(value, path);
|
|
14867
|
+
for (const key of required) if (input[key] === void 0) fail(`${path}.${key}`, "required");
|
|
14868
|
+
for (const [key, entry] of Object.entries(input)) {
|
|
14869
|
+
if (!Object.prototype.hasOwnProperty.call(allowed, key)) fail(`${path}.${key}`, "unsupported property");
|
|
14870
|
+
if (entry !== void 0) allowed[key](entry, `${path}.${key}`);
|
|
14871
|
+
}
|
|
14872
|
+
return input;
|
|
14873
|
+
}
|
|
14874
|
+
var text = (value, path) => {
|
|
14875
|
+
if (typeof value !== "string" || !value.trim()) fail(path, "expected a nonempty string");
|
|
14876
|
+
};
|
|
14877
|
+
var literalText = (value, path) => {
|
|
14878
|
+
if (typeof value !== "string") fail(path, "expected a string");
|
|
14879
|
+
};
|
|
14880
|
+
var boolean = (value, path) => {
|
|
14881
|
+
if (typeof value !== "boolean") fail(path, "expected a boolean");
|
|
14882
|
+
};
|
|
14883
|
+
var number = (value, path) => {
|
|
14884
|
+
if (typeof value !== "number" || !Number.isFinite(value)) fail(path, "expected a finite number");
|
|
14885
|
+
};
|
|
14886
|
+
var positive = (value, path) => {
|
|
14887
|
+
number(value, path);
|
|
14888
|
+
if (value <= 0) fail(path, "expected a positive number");
|
|
14889
|
+
};
|
|
14890
|
+
var nonnegative = (value, path) => {
|
|
14891
|
+
number(value, path);
|
|
14892
|
+
if (value < 0) fail(path, "expected a nonnegative number");
|
|
14893
|
+
};
|
|
14894
|
+
var color = (value, path) => {
|
|
14895
|
+
number(value, path);
|
|
14896
|
+
if (!Number.isInteger(value) || value < 0 || value > 16777215) {
|
|
14897
|
+
fail(path, "expected an RGB integer from 0 to 16777215");
|
|
14898
|
+
}
|
|
14899
|
+
};
|
|
14900
|
+
var asset = (value, path) => {
|
|
14901
|
+
text(value, path);
|
|
14902
|
+
if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(value) || value.split(/[:/]/).some((part) => !part || part === "." || part === "..")) {
|
|
14903
|
+
fail(path, "expected a resource ID such as minecraft:stone");
|
|
14904
|
+
}
|
|
14905
|
+
};
|
|
14906
|
+
var version = (value, path) => {
|
|
14907
|
+
text(value, path);
|
|
14908
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(value)) fail(path, "expected a Minecraft version without slashes");
|
|
14909
|
+
};
|
|
14910
|
+
var tuple = (size, item = number) => (value, path) => {
|
|
14911
|
+
if (!Array.isArray(value) || value.length !== size) fail(path, `expected ${size} numbers`);
|
|
14912
|
+
for (let index = 0; index < size; index++) item(value[index], `${path}[${index}]`);
|
|
14913
|
+
};
|
|
14914
|
+
var list = (item) => (value, path) => {
|
|
14915
|
+
if (!Array.isArray(value)) fail(path, "expected an array");
|
|
14916
|
+
for (let index = 0; index < value.length; index++) item(value[index], `${path}[${index}]`);
|
|
14917
|
+
};
|
|
14918
|
+
var dictionary = (item, numericKeys = false) => (value, path) => {
|
|
14919
|
+
for (const [key, entry] of Object.entries(object2(value, path))) {
|
|
14920
|
+
if (numericKeys && !/^\d+$/.test(key)) fail(`${path}.${key}`, "expected a nonnegative tint index");
|
|
14921
|
+
item(entry, `${path}.${key}`);
|
|
14922
|
+
}
|
|
14923
|
+
};
|
|
14924
|
+
var enumeration = (values) => (value, path) => {
|
|
14925
|
+
if (!values.includes(value)) fail(path, `expected one of: ${values.join(", ")}`);
|
|
14926
|
+
};
|
|
14927
|
+
var textStyle = { color, bold: boolean, italic: boolean };
|
|
14928
|
+
function guiLayer(value, path) {
|
|
14929
|
+
const layer = object2(value, path);
|
|
14930
|
+
if (["texture", "item", "text"].filter((key) => layer[key] !== void 0).length !== 1) {
|
|
14931
|
+
fail(path, "expected exactly one of texture, item, or text");
|
|
14932
|
+
}
|
|
14933
|
+
const layout = { name: text, position: tuple(2), size: tuple(2, positive) };
|
|
14934
|
+
if (layer.texture !== void 0) {
|
|
14935
|
+
fields(value, path, { ...layout, texture: asset, crop: (value2, path2) => {
|
|
14936
|
+
tuple(4)(value2, path2);
|
|
14937
|
+
const [x, y, width, height] = value2;
|
|
14938
|
+
nonnegative(x, `${path2}[0]`);
|
|
14939
|
+
nonnegative(y, `${path2}[1]`);
|
|
14940
|
+
positive(width, `${path2}[2]`);
|
|
14941
|
+
positive(height, `${path2}[3]`);
|
|
14942
|
+
} });
|
|
14943
|
+
} else if (layer.item !== void 0) {
|
|
14944
|
+
fields(value, path, { ...layout, item: asset, tints: dictionary(color, true) });
|
|
14945
|
+
} else {
|
|
14946
|
+
fields(value, path, {
|
|
14947
|
+
...layout,
|
|
14948
|
+
...textStyle,
|
|
14949
|
+
font: asset,
|
|
14950
|
+
shadow: boolean,
|
|
14951
|
+
maxWidth: positive,
|
|
14952
|
+
lineHeight: positive,
|
|
14953
|
+
text: (value2, path2) => {
|
|
14954
|
+
if (typeof value2 === "string") return;
|
|
14955
|
+
list((run, path3) => {
|
|
14956
|
+
fields(run, path3, { text: literalText, ...textStyle }, ["text"]);
|
|
14957
|
+
})(value2, path2);
|
|
14958
|
+
}
|
|
14959
|
+
});
|
|
14960
|
+
}
|
|
14961
|
+
}
|
|
14962
|
+
function validateObject(value, path) {
|
|
14963
|
+
const input = object2(value, path);
|
|
14964
|
+
const common = { id: text, name: text, type: text, position: tuple(3), rotation: tuple(3), scale: tuple(3), visible: boolean };
|
|
14965
|
+
const required = ["id", "type"];
|
|
14966
|
+
const renderOptions = { wireframe: boolean };
|
|
14967
|
+
switch (input.type) {
|
|
14968
|
+
case "skin":
|
|
14969
|
+
fields(value, path, {
|
|
14970
|
+
...common,
|
|
14971
|
+
skin: text,
|
|
14972
|
+
cape: (value2, path2) => {
|
|
14973
|
+
fields(value2, path2, { texture: text, layout: enumeration(["minecraft", "optifine", "labymod"]) }, ["texture"]);
|
|
14974
|
+
},
|
|
14975
|
+
hiddenParts: list(enumeration(SKIN_PARTS)),
|
|
14976
|
+
pose: (value2, path2) => {
|
|
14977
|
+
fields(value2, path2, {
|
|
14978
|
+
head: tuple(3),
|
|
14979
|
+
body: tuple(3),
|
|
14980
|
+
rightArm: tuple(3),
|
|
14981
|
+
leftArm: tuple(3),
|
|
14982
|
+
rightLeg: tuple(3),
|
|
14983
|
+
leftLeg: tuple(3),
|
|
14984
|
+
cape: tuple(3)
|
|
14985
|
+
});
|
|
14986
|
+
},
|
|
14987
|
+
options: (value2, path2) => {
|
|
14988
|
+
fields(value2, path2, { slim: boolean, legacy: boolean });
|
|
14989
|
+
}
|
|
14990
|
+
}, required);
|
|
14991
|
+
break;
|
|
14992
|
+
case "block":
|
|
14993
|
+
fields(value, path, {
|
|
14994
|
+
...common,
|
|
14995
|
+
asset,
|
|
14996
|
+
state: dictionary(text),
|
|
14997
|
+
options: (value2, path2) => {
|
|
14998
|
+
fields(value2, path2, { ...renderOptions, tints: dictionary(color, true) });
|
|
14999
|
+
}
|
|
15000
|
+
}, [...required, "asset"]);
|
|
15001
|
+
break;
|
|
15002
|
+
case "item":
|
|
15003
|
+
case "model":
|
|
15004
|
+
fields(value, path, {
|
|
15005
|
+
...common,
|
|
15006
|
+
asset,
|
|
15007
|
+
options: (value2, path2) => {
|
|
15008
|
+
fields(value2, path2, { ...renderOptions, displayPosition: enumeration(DISPLAY_POSITIONS), tints: dictionary(color, true) });
|
|
15009
|
+
}
|
|
15010
|
+
}, [...required, "asset"]);
|
|
15011
|
+
break;
|
|
15012
|
+
case "entity":
|
|
15013
|
+
fields(value, path, {
|
|
15014
|
+
...common,
|
|
15015
|
+
asset,
|
|
15016
|
+
texture: asset,
|
|
15017
|
+
layers: list(text),
|
|
15018
|
+
when: list(text),
|
|
15019
|
+
textures: dictionary(asset),
|
|
15020
|
+
options: (value2, path2) => {
|
|
15021
|
+
fields(value2, path2, {
|
|
15022
|
+
...renderOptions,
|
|
15023
|
+
flip: boolean,
|
|
15024
|
+
tints: dictionary((value3, path3) => {
|
|
15025
|
+
typeof value3 === "string" ? text(value3, path3) : color(value3, path3);
|
|
15026
|
+
})
|
|
15027
|
+
});
|
|
15028
|
+
},
|
|
15029
|
+
animation: (value2, path2) => {
|
|
15030
|
+
fields(value2, path2, { name: text, loop: boolean, speed: nonnegative, time: nonnegative, paused: boolean }, ["name"]);
|
|
15031
|
+
}
|
|
15032
|
+
}, [...required, "asset"]);
|
|
15033
|
+
if (Array.isArray(input.layers) && !input.layers.length) fail(`${path}.layers`, "expected at least one layer");
|
|
15034
|
+
break;
|
|
15035
|
+
case "gui":
|
|
15036
|
+
fields(value, path, { ...common, layers: list(guiLayer) }, [...required, "layers"]);
|
|
15037
|
+
break;
|
|
15038
|
+
default:
|
|
15039
|
+
fail(`${path}.type`, "expected skin, block, item, entity, model, or gui");
|
|
15040
|
+
}
|
|
15041
|
+
}
|
|
15042
|
+
function parseSceneDocument(value) {
|
|
15043
|
+
if (typeof value === "string") {
|
|
15044
|
+
try {
|
|
15045
|
+
value = JSON.parse(value);
|
|
15046
|
+
} catch {
|
|
15047
|
+
fail("scene", "invalid JSON");
|
|
15048
|
+
}
|
|
15049
|
+
}
|
|
15050
|
+
const input = fields(value, "scene", {
|
|
15051
|
+
format: enumeration(["minerender-scene"]),
|
|
15052
|
+
version: (value2, path) => {
|
|
15053
|
+
if (value2 !== 1) fail(path, "only scene document version 1 is supported");
|
|
15054
|
+
},
|
|
15055
|
+
minecraftVersion: version,
|
|
15056
|
+
camera: (value2, path) => {
|
|
15057
|
+
fields(value2, path, { position: tuple(3), target: tuple(3) }, ["position", "target"]);
|
|
15058
|
+
},
|
|
15059
|
+
objects: list(validateObject)
|
|
15060
|
+
}, ["format", "version", "objects"]);
|
|
15061
|
+
const ids = /* @__PURE__ */ new Set();
|
|
15062
|
+
for (const [index, entry] of input.objects.entries()) {
|
|
15063
|
+
if (ids.has(entry.id)) fail(`scene.objects[${index}].id`, `duplicate object ID "${entry.id}"`);
|
|
15064
|
+
ids.add(entry.id);
|
|
15065
|
+
}
|
|
15066
|
+
return JSON.parse(JSON.stringify(input));
|
|
15067
|
+
}
|
|
15068
|
+
|
|
15069
|
+
// src/scene/SceneDocumentLoader.ts
|
|
15070
|
+
init_AssetKey();
|
|
15071
|
+
init_AssetLoader();
|
|
15072
|
+
init_BlockStates();
|
|
15073
|
+
init_Entities();
|
|
15074
|
+
init_Models();
|
|
15075
|
+
init_DisplayPosition();
|
|
15076
|
+
import { Group as Group2 } from "three";
|
|
15077
|
+
init_SceneObject();
|
|
15078
|
+
|
|
14650
15079
|
// src/skin/Skins.ts
|
|
14651
15080
|
init_Requests();
|
|
14652
15081
|
var Skins = class {
|
|
14653
15082
|
//<editor-fold desc="Skins">
|
|
15083
|
+
/** Returns a skin URL for {@link MineRenderScene.addSkin}, or `undefined` if name lookup fails. */
|
|
14654
15084
|
static async fromUuidOrUsername(uuidOrUsername) {
|
|
14655
15085
|
if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
|
|
14656
15086
|
return this.fromUuid(uuidOrUsername);
|
|
14657
15087
|
}
|
|
14658
15088
|
return this.fromUsername(uuidOrUsername);
|
|
14659
15089
|
}
|
|
15090
|
+
/** Builds a proxy URL for a UUID without downloading or checking the skin. */
|
|
14660
15091
|
static async fromUuid(uuid) {
|
|
14661
15092
|
return this.getMcProxySkinUrl(uuid);
|
|
14662
15093
|
}
|
|
@@ -14668,6 +15099,7 @@ var Skins = class {
|
|
|
14668
15099
|
return void 0;
|
|
14669
15100
|
});
|
|
14670
15101
|
}
|
|
15102
|
+
/** Looks up a saved MineSkin skin and returns its texture URL, or `undefined` on lookup failure. */
|
|
14671
15103
|
static async fromMineSkin(mineskinId) {
|
|
14672
15104
|
return this.getMineSkin(mineskinId).then((mineskin) => {
|
|
14673
15105
|
if (mineskin) {
|
|
@@ -14678,12 +15110,14 @@ var Skins = class {
|
|
|
14678
15110
|
}
|
|
14679
15111
|
//</editor-fold>
|
|
14680
15112
|
//<editor-fold desc="Capes">
|
|
15113
|
+
/** Resolves a player's Minecraft cape URL for {@link SkinObject.setCapeTexture}. */
|
|
14681
15114
|
static async capeFromUuidOrUsername(uuidOrUsername) {
|
|
14682
15115
|
if (uuidOrUsername.length === 32 || uuidOrUsername.length === 36) {
|
|
14683
15116
|
return this.capeFromUuid(uuidOrUsername);
|
|
14684
15117
|
}
|
|
14685
15118
|
return this.capeFromUsername(uuidOrUsername);
|
|
14686
15119
|
}
|
|
15120
|
+
/** Builds a proxy URL without checking whether the player has a cape. */
|
|
14687
15121
|
static async capeFromUuid(uuid) {
|
|
14688
15122
|
return this.getMcProxyCapeUrl(uuid);
|
|
14689
15123
|
}
|
|
@@ -14812,6 +15246,195 @@ var Skins = class {
|
|
|
14812
15246
|
//</editor-fold>
|
|
14813
15247
|
};
|
|
14814
15248
|
|
|
15249
|
+
// src/scene/SceneDocumentLoader.ts
|
|
15250
|
+
var DocumentObjectScene = class extends MineRenderScene {
|
|
15251
|
+
async addSceneObject(_asset, supplier, _options, parent = this) {
|
|
15252
|
+
const object3 = await supplier();
|
|
15253
|
+
return this.initialize(object3, parent);
|
|
15254
|
+
}
|
|
15255
|
+
async initialize(object3, parent = this) {
|
|
15256
|
+
object3.scene = this;
|
|
15257
|
+
try {
|
|
15258
|
+
await object3.init();
|
|
15259
|
+
parent.add(object3);
|
|
15260
|
+
return object3;
|
|
15261
|
+
} catch (error) {
|
|
15262
|
+
object3.dispose();
|
|
15263
|
+
object3.removeFromParent();
|
|
15264
|
+
throw error;
|
|
15265
|
+
}
|
|
15266
|
+
}
|
|
15267
|
+
dispose() {
|
|
15268
|
+
for (const child of [...this.children]) {
|
|
15269
|
+
if (child.parent !== this) continue;
|
|
15270
|
+
if (isSceneObject(child)) child.dispose();
|
|
15271
|
+
child.removeFromParent();
|
|
15272
|
+
}
|
|
15273
|
+
}
|
|
15274
|
+
};
|
|
15275
|
+
async function skinTexture(value, cape = false) {
|
|
15276
|
+
if (!/^[a-zA-Z0-9_-]{1,36}$/.test(value)) return value;
|
|
15277
|
+
const texture = await (cape ? Skins.capeFromUuidOrUsername(value) : Skins.fromUuidOrUsername(value));
|
|
15278
|
+
if (!texture) throw new Error(`Could not find ${cape ? "cape" : "skin"} for "${value}"`);
|
|
15279
|
+
return texture;
|
|
15280
|
+
}
|
|
15281
|
+
var SceneDocumentLoader = class {
|
|
15282
|
+
static parse(value) {
|
|
15283
|
+
return parseSceneDocument(value);
|
|
15284
|
+
}
|
|
15285
|
+
/**
|
|
15286
|
+
* Loads objects concurrently and attaches them in document order after every load succeeds.
|
|
15287
|
+
* A failure waits for all loads, disposes staged objects, and reports the first error in document order.
|
|
15288
|
+
*/
|
|
15289
|
+
static async load(scene, value, parent = scene) {
|
|
15290
|
+
const definition = this.parse(value);
|
|
15291
|
+
if (definition.minecraftVersion !== void 0 && definition.minecraftVersion !== AssetLoader.version) {
|
|
15292
|
+
throw new Error(`Scene requires Minecraft ${definition.minecraftVersion}; call AssetLoader.setVersion("${definition.minecraftVersion}") before loading it`);
|
|
15293
|
+
}
|
|
15294
|
+
const root = new Group2();
|
|
15295
|
+
root.name = "MineRender scene document";
|
|
15296
|
+
const objects = [];
|
|
15297
|
+
let disposed = false;
|
|
15298
|
+
const loaded = {
|
|
15299
|
+
root,
|
|
15300
|
+
objects,
|
|
15301
|
+
definition,
|
|
15302
|
+
advanceAnimations: (delta) => {
|
|
15303
|
+
if (!disposed) for (const object3 of objects) object3.advanceAnimation(delta);
|
|
15304
|
+
},
|
|
15305
|
+
dispose: () => {
|
|
15306
|
+
if (disposed) return;
|
|
15307
|
+
disposed = true;
|
|
15308
|
+
root.removeFromParent();
|
|
15309
|
+
for (const object3 of objects) object3.dispose();
|
|
15310
|
+
root.clear();
|
|
15311
|
+
scene.dirty = true;
|
|
15312
|
+
}
|
|
15313
|
+
};
|
|
15314
|
+
try {
|
|
15315
|
+
const results = await Promise.allSettled(definition.objects.map((object3) => this.loadValidatedObject(scene, object3)));
|
|
15316
|
+
for (const result of results) if (result.status === "fulfilled") objects.push(result.value);
|
|
15317
|
+
const failure = results.find((result) => result.status === "rejected");
|
|
15318
|
+
if (failure) throw failure.reason;
|
|
15319
|
+
for (const object3 of objects) root.add(object3.root);
|
|
15320
|
+
parent.add(root);
|
|
15321
|
+
scene.dirty = true;
|
|
15322
|
+
return loaded;
|
|
15323
|
+
} catch (error) {
|
|
15324
|
+
loaded.dispose();
|
|
15325
|
+
throw error;
|
|
15326
|
+
}
|
|
15327
|
+
}
|
|
15328
|
+
/** Loads an independently editable object. Its root owns the document's position, rotation, and scale. */
|
|
15329
|
+
static async loadObject(scene, value, parent = scene) {
|
|
15330
|
+
const definition = this.parse({ format: "minerender-scene", version: 1, objects: [value] }).objects[0];
|
|
15331
|
+
return this.loadValidatedObject(scene, definition, parent);
|
|
15332
|
+
}
|
|
15333
|
+
static async loadValidatedObject(scene, definition, parent) {
|
|
15334
|
+
const root = new Group2();
|
|
15335
|
+
root.name = definition.name ?? definition.id;
|
|
15336
|
+
root.userData.sceneObjectId = definition.id;
|
|
15337
|
+
root.position.fromArray(definition.position ?? [0, 0, 0]);
|
|
15338
|
+
root.rotation.set(...definition.rotation ?? [0, 0, 0]);
|
|
15339
|
+
root.scale.fromArray(definition.scale ?? [1, 1, 1]);
|
|
15340
|
+
root.visible = definition.visible ?? true;
|
|
15341
|
+
const content = new DocumentObjectScene();
|
|
15342
|
+
root.add(content);
|
|
15343
|
+
let object3;
|
|
15344
|
+
try {
|
|
15345
|
+
switch (definition.type) {
|
|
15346
|
+
case "skin": {
|
|
15347
|
+
const skin = new SkinObject({ ...definition.options, instanceMeshes: false });
|
|
15348
|
+
object3 = skin;
|
|
15349
|
+
const texture = definition.skin ? await skinTexture(definition.skin) : `${AssetLoader.ROOT}/assets/minecraft/textures/entity/player/${definition.options?.slim ? "slim/alex" : "wide/steve"}.png`;
|
|
15350
|
+
await skin.setSkinTexture(texture);
|
|
15351
|
+
await content.initialize(skin);
|
|
15352
|
+
if (definition.cape) await skin.setCapeTexture(await skinTexture(definition.cape.texture, true), definition.cape.layout);
|
|
15353
|
+
for (const [part, rotation] of Object.entries(definition.pose ?? {})) {
|
|
15354
|
+
skin.getGroupByName(part)?.rotation.set(...rotation, "XYZ");
|
|
15355
|
+
}
|
|
15356
|
+
for (const part of definition.hiddenParts ?? []) {
|
|
15357
|
+
const mesh = skin.getMeshByName(part);
|
|
15358
|
+
if (mesh) mesh.visible = false;
|
|
15359
|
+
}
|
|
15360
|
+
break;
|
|
15361
|
+
}
|
|
15362
|
+
case "block": {
|
|
15363
|
+
const state = await BlockStates.get(AssetKey.parse("blockstates", definition.asset));
|
|
15364
|
+
if (!state) throw new Error(`Could not load block ${definition.asset}`);
|
|
15365
|
+
object3 = await content.addBlock(state, { ...definition.options, initialState: definition.state, instanceMeshes: false });
|
|
15366
|
+
break;
|
|
15367
|
+
}
|
|
15368
|
+
case "item":
|
|
15369
|
+
case "model": {
|
|
15370
|
+
let key = AssetKey.parse("models", definition.asset);
|
|
15371
|
+
if (definition.type === "item" && key.type !== "item") {
|
|
15372
|
+
key = new AssetKey(key.namespace, key.getFullPath(), "models", "item");
|
|
15373
|
+
}
|
|
15374
|
+
const model = await Models.getMerged(key);
|
|
15375
|
+
if (!model) throw new Error(`Could not load ${definition.type} ${definition.asset}`);
|
|
15376
|
+
object3 = await content.addModel(model, {
|
|
15377
|
+
...definition.type === "item" ? { displayPosition: "gui" /* GUI */ } : {},
|
|
15378
|
+
...definition.options,
|
|
15379
|
+
instanceMeshes: false
|
|
15380
|
+
});
|
|
15381
|
+
break;
|
|
15382
|
+
}
|
|
15383
|
+
case "entity": {
|
|
15384
|
+
const key = AssetKey.parse("entity-models", definition.asset);
|
|
15385
|
+
const model = await Entities.getEntity(key, definition.texture ? AssetKey.parse("textures", definition.texture) : void 0, {
|
|
15386
|
+
layers: definition.layers,
|
|
15387
|
+
when: definition.when,
|
|
15388
|
+
textures: definition.textures && Object.fromEntries(Object.entries(definition.textures).map(([name, texture]) => [name, AssetKey.parse("textures", texture)]))
|
|
15389
|
+
});
|
|
15390
|
+
if (!model) throw new Error(`Could not load entity ${definition.asset}`);
|
|
15391
|
+
const entity = await content.addEntity(model, { ...definition.options, instanceMeshes: false });
|
|
15392
|
+
object3 = entity;
|
|
15393
|
+
if (definition.animation) {
|
|
15394
|
+
const animations = await Entities.getAnimations(key);
|
|
15395
|
+
const clip = animations?.[definition.animation.name];
|
|
15396
|
+
if (!clip) throw new Error(`Entity ${definition.asset} has no animation "${definition.animation.name}"`);
|
|
15397
|
+
entity.playAnimation(clip, definition.animation);
|
|
15398
|
+
}
|
|
15399
|
+
break;
|
|
15400
|
+
}
|
|
15401
|
+
case "gui":
|
|
15402
|
+
object3 = await content.initialize(new GuiObject(definition.layers));
|
|
15403
|
+
break;
|
|
15404
|
+
}
|
|
15405
|
+
let disposed = false;
|
|
15406
|
+
const loaded = {
|
|
15407
|
+
root,
|
|
15408
|
+
object: object3,
|
|
15409
|
+
definition,
|
|
15410
|
+
advanceAnimation: (delta) => {
|
|
15411
|
+
if (!disposed && definition.type === "entity" && !definition.animation?.paused) object3.advanceAnimation(delta);
|
|
15412
|
+
},
|
|
15413
|
+
dispose: () => {
|
|
15414
|
+
if (disposed) return;
|
|
15415
|
+
disposed = true;
|
|
15416
|
+
root.removeFromParent();
|
|
15417
|
+
object3.dispose();
|
|
15418
|
+
object3.removeFromParent();
|
|
15419
|
+
content.dispose();
|
|
15420
|
+
root.clear();
|
|
15421
|
+
scene.dirty = true;
|
|
15422
|
+
}
|
|
15423
|
+
};
|
|
15424
|
+
parent?.add(root);
|
|
15425
|
+
scene.dirty = true;
|
|
15426
|
+
return loaded;
|
|
15427
|
+
} catch (error) {
|
|
15428
|
+
object3?.dispose();
|
|
15429
|
+
object3?.removeFromParent();
|
|
15430
|
+
content.dispose();
|
|
15431
|
+
root.removeFromParent();
|
|
15432
|
+
root.clear();
|
|
15433
|
+
throw new Error(`Scene object "${definition.name ?? definition.id}": ${error instanceof Error ? error.message : String(error)}`);
|
|
15434
|
+
}
|
|
15435
|
+
}
|
|
15436
|
+
};
|
|
15437
|
+
|
|
14815
15438
|
// src/texture/index.ts
|
|
14816
15439
|
init_Colormaps();
|
|
14817
15440
|
init_TextureAtlas();
|
|
@@ -14838,16 +15461,19 @@ var ChunkData = class _ChunkData {
|
|
|
14838
15461
|
this.freeIds = [];
|
|
14839
15462
|
this.nbt = /* @__PURE__ */ new Map();
|
|
14840
15463
|
}
|
|
15464
|
+
/** Returns a detached copy of a cell, or `undefined` for air. Indices range from 0 to 4095. */
|
|
14841
15465
|
get(index) {
|
|
14842
15466
|
_ChunkData.validateIndex(index);
|
|
14843
15467
|
const entry = this.palette[this.indices[index]];
|
|
14844
15468
|
return entry ? _ChunkData.copyBlock(entry.state, this.nbt.get(index)) : void 0;
|
|
14845
15469
|
}
|
|
15470
|
+
/** Captures a cell's data and returns a copy-producing reader that survives later cell changes. */
|
|
14846
15471
|
snapshot(index) {
|
|
14847
15472
|
_ChunkData.validateIndex(index);
|
|
14848
15473
|
const entry = this.palette[this.indices[index]];
|
|
14849
15474
|
return entry ? _ChunkData.copyBlock.bind(void 0, entry.state, this.nbt.get(index)) : void 0;
|
|
14850
15475
|
}
|
|
15476
|
+
/** Copies block data into a cell. An omitted block or an air block clears the cell. */
|
|
14851
15477
|
set(index, block) {
|
|
14852
15478
|
_ChunkData.validateIndex(index);
|
|
14853
15479
|
let state;
|
|
@@ -14912,15 +15538,23 @@ var ChunkData = class _ChunkData {
|
|
|
14912
15538
|
|
|
14913
15539
|
// src/world/AnvilParser.ts
|
|
14914
15540
|
var AnvilParser = class {
|
|
15541
|
+
/** Lists occupied chunk positions within the region, using local coordinates from 0 to 31. */
|
|
14915
15542
|
static getChunkList(data) {
|
|
14916
15543
|
return this.locations(this.bytes(data)).map(({ x, z }) => ({ x, z }));
|
|
14917
15544
|
}
|
|
15545
|
+
/** Decodes all stored chunks. Gzip, zlib, and uncompressed payloads are supported. */
|
|
14918
15546
|
static async parse(data) {
|
|
14919
15547
|
const bytes = this.bytes(data);
|
|
14920
15548
|
const chunks = [];
|
|
14921
15549
|
for (const location of this.locations(bytes)) chunks.push(await this.readChunk(bytes, location));
|
|
14922
15550
|
return { chunks };
|
|
14923
15551
|
}
|
|
15552
|
+
/**
|
|
15553
|
+
* Decodes one chunk for {@link MineRenderWorld.placeChunk}.
|
|
15554
|
+
* @param localX - Chunk x within the region, from 0 to 31.
|
|
15555
|
+
* @param localZ - Chunk z within the region, from 0 to 31.
|
|
15556
|
+
* @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
|
|
15557
|
+
*/
|
|
14924
15558
|
static async parseChunk(data, localX, localZ) {
|
|
14925
15559
|
if (![localX, localZ].every((value) => Number.isInteger(value) && value >= 0 && value < 32)) {
|
|
14926
15560
|
throw new RangeError("Region-local chunk coordinates must be integers from 0 to 31");
|
|
@@ -15125,6 +15759,7 @@ var SectionModels = class {
|
|
|
15125
15759
|
this.templates = /* @__PURE__ */ new Set();
|
|
15126
15760
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
|
|
15127
15761
|
}
|
|
15762
|
+
/** Returns a cube template, or `undefined` when the block requires an individual render object. */
|
|
15128
15763
|
async get(blockState, properties = {}) {
|
|
15129
15764
|
if (!blockState.variants || blockState.multipart) return void 0;
|
|
15130
15765
|
let states = this.states.get(blockState);
|
|
@@ -15153,16 +15788,16 @@ var SectionModels = class {
|
|
|
15153
15788
|
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
15154
15789
|
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
15155
15790
|
const template = await cached(models, key, async () => {
|
|
15156
|
-
const
|
|
15791
|
+
const object3 = new SectionModel(model, {
|
|
15157
15792
|
instanceMeshes: false,
|
|
15158
15793
|
mergeMeshes: false,
|
|
15159
15794
|
tints,
|
|
15160
15795
|
uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
|
|
15161
15796
|
});
|
|
15162
|
-
await
|
|
15163
|
-
const geometry =
|
|
15797
|
+
await object3.init();
|
|
15798
|
+
const geometry = object3.children[0].geometry;
|
|
15164
15799
|
geometry.applyMatrix4(new Matrix412().makeRotationFromEuler(rotation));
|
|
15165
|
-
|
|
15800
|
+
object3.clear();
|
|
15166
15801
|
const cullFaces = CUBE_FACES.map((face) => {
|
|
15167
15802
|
const local = CUBE_FACES.findIndex((name) => name === atlas.model.elements[0].faces[face]?.cullface);
|
|
15168
15803
|
if (local < 0) return 0;
|
|
@@ -15178,6 +15813,7 @@ var SectionModels = class {
|
|
|
15178
15813
|
}
|
|
15179
15814
|
return { variants, templates };
|
|
15180
15815
|
}
|
|
15816
|
+
/** Disposes prepared geometry and clears this world's template caches. */
|
|
15181
15817
|
clear() {
|
|
15182
15818
|
for (const template of this.templates) template.geometry.dispose();
|
|
15183
15819
|
this.templates.clear();
|
|
@@ -15228,6 +15864,13 @@ var MineRenderWorld = class {
|
|
|
15228
15864
|
this.validatePosBounds(posOrX);
|
|
15229
15865
|
await this.getChunkAt(posOrX)?.setBlockVisibleAt(posOrX, yOrVisible);
|
|
15230
15866
|
}
|
|
15867
|
+
/**
|
|
15868
|
+
* Places a structure's blocks at their stored positions, then refreshes neighboring faces.
|
|
15869
|
+
* Entity NBT is retained by the structure but is not rendered.
|
|
15870
|
+
*
|
|
15871
|
+
* @param useBatches - Uses bounded batches by default. Set to `false` for sequential placement.
|
|
15872
|
+
* @param executor - Optional queue for batched placement. The caller remains responsible for stopping it.
|
|
15873
|
+
*/
|
|
15231
15874
|
async placeMultiBlock(multiblock, useBatches = true, executor) {
|
|
15232
15875
|
const changes = /* @__PURE__ */ new Map();
|
|
15233
15876
|
try {
|
|
@@ -15324,6 +15967,7 @@ var MineRenderWorld = class {
|
|
|
15324
15967
|
});
|
|
15325
15968
|
}
|
|
15326
15969
|
}
|
|
15970
|
+
/** Removes all chunks and releases their block placements and section meshes. */
|
|
15327
15971
|
async clear() {
|
|
15328
15972
|
await this.culling;
|
|
15329
15973
|
const chunks = [...this._chunks.values()];
|
|
@@ -15369,10 +16013,10 @@ var MineRenderWorld = class {
|
|
|
15369
16013
|
await block.object.updateFluid((x, y, z) => {
|
|
15370
16014
|
const neighbor = pos.clone().add(new Vector316(x, y, z));
|
|
15371
16015
|
const section = this.getChunkAt(neighbor);
|
|
15372
|
-
const
|
|
16016
|
+
const object3 = section?.isBlockVisibleAt(neighbor) ? section.getBlockAt(neighbor)?.object : void 0;
|
|
15373
16017
|
return {
|
|
15374
|
-
fluid:
|
|
15375
|
-
level:
|
|
16018
|
+
fluid: object3?.fluidKind,
|
|
16019
|
+
level: object3?.fluidLevel,
|
|
15376
16020
|
solid: section?.isOccludingAt(neighbor) ?? false
|
|
15377
16021
|
};
|
|
15378
16022
|
});
|
|
@@ -15409,9 +16053,11 @@ var MineRenderWorld = class {
|
|
|
15409
16053
|
}
|
|
15410
16054
|
return chunk;
|
|
15411
16055
|
}
|
|
16056
|
+
/** Returns the loaded chunk containing a world block position, without creating a chunk. */
|
|
15412
16057
|
getChunkAt(pos) {
|
|
15413
16058
|
return this._chunks.get(this.worldPosToChunkKey(pos));
|
|
15414
16059
|
}
|
|
16060
|
+
/** Converts block coordinates to scene units by multiplying each component by 16. */
|
|
15415
16061
|
static worldToScenePosition(pos) {
|
|
15416
16062
|
return new Vector316(
|
|
15417
16063
|
pos.x * 16,
|
|
@@ -15419,6 +16065,7 @@ var MineRenderWorld = class {
|
|
|
15419
16065
|
pos.z * 16
|
|
15420
16066
|
);
|
|
15421
16067
|
}
|
|
16068
|
+
/** Converts scene units to block coordinates without rounding to an integer block. */
|
|
15422
16069
|
static sceneToWorldPosition(pos) {
|
|
15423
16070
|
return new Vector316(
|
|
15424
16071
|
pos.x / 16,
|
|
@@ -15445,12 +16092,13 @@ init_Model();
|
|
|
15445
16092
|
// src/world/SectionMesh.ts
|
|
15446
16093
|
init_CanvasCompat();
|
|
15447
16094
|
init_Materials();
|
|
15448
|
-
import { BufferGeometry as BufferGeometry8, Float32BufferAttribute as Float32BufferAttribute8, Group as
|
|
15449
|
-
var SectionMesh = class _SectionMesh extends
|
|
16095
|
+
import { BufferGeometry as BufferGeometry8, Float32BufferAttribute as Float32BufferAttribute8, Group as Group3, Mesh as Mesh9 } from "three";
|
|
16096
|
+
var SectionMesh = class _SectionMesh extends Group3 {
|
|
15450
16097
|
constructor() {
|
|
15451
16098
|
super(...arguments);
|
|
15452
16099
|
this.ownedMeshes = [];
|
|
15453
16100
|
}
|
|
16101
|
+
/** Builds section-local meshes from visible cube faces. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
15454
16102
|
static build(entries, maxAtlasSize = 2048) {
|
|
15455
16103
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
15456
16104
|
const visible = entries.filter((entry) => entry.template.cullFaces.some((direction) => !(entry.cullMask & direction)));
|
|
@@ -15498,7 +16146,7 @@ var SectionMesh = class _SectionMesh extends Group2 {
|
|
|
15498
16146
|
const normal = template.geometry.getAttribute("normal");
|
|
15499
16147
|
const uv = template.geometry.getAttribute("uv");
|
|
15500
16148
|
const bounds = template.geometry.getAttribute("uvBounds");
|
|
15501
|
-
const
|
|
16149
|
+
const color2 = template.geometry.getAttribute("color");
|
|
15502
16150
|
const sourceIndices = template.geometry.getIndex();
|
|
15503
16151
|
const placement = locations.get(template.atlas).placement;
|
|
15504
16152
|
const mapU = (u) => (placement.x + u * template.atlas.image.width) / page.width;
|
|
@@ -15520,7 +16168,7 @@ var SectionMesh = class _SectionMesh extends Group2 {
|
|
|
15520
16168
|
mapU(bounds?.getZ(vertex) ?? 1),
|
|
15521
16169
|
mapV(bounds?.getW(vertex) ?? 1)
|
|
15522
16170
|
);
|
|
15523
|
-
colors.push(
|
|
16171
|
+
colors.push(color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1);
|
|
15524
16172
|
}
|
|
15525
16173
|
for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
|
|
15526
16174
|
}
|
|
@@ -15595,7 +16243,7 @@ var Chunk = class _Chunk {
|
|
|
15595
16243
|
return this.setBlockInChunkAt(pos, block, worldPos);
|
|
15596
16244
|
}
|
|
15597
16245
|
/**
|
|
15598
|
-
*
|
|
16246
|
+
* Places a block at integer chunk-local coordinates from 0 to 15.
|
|
15599
16247
|
*/
|
|
15600
16248
|
async setBlockInChunkAt(pos, block, worldPos, onBlocksChanged = this.onBlocksChanged) {
|
|
15601
16249
|
if (typeof worldPos === "undefined") {
|
|
@@ -15608,7 +16256,7 @@ var Chunk = class _Chunk {
|
|
|
15608
16256
|
this.renderedBlocks.get(index)?.object?.removeFromScene();
|
|
15609
16257
|
if (this.sectionBlocks.delete(index)) this.meshDirty = true;
|
|
15610
16258
|
this.renderedBlocks.delete(index);
|
|
15611
|
-
let
|
|
16259
|
+
let object3;
|
|
15612
16260
|
try {
|
|
15613
16261
|
if (!readBlock) return void 0;
|
|
15614
16262
|
const stored = readBlock();
|
|
@@ -15623,27 +16271,28 @@ var Chunk = class _Chunk {
|
|
|
15623
16271
|
this.sectionBlocks.set(index, { index, template, cullMask: 0 });
|
|
15624
16272
|
this.meshDirty = true;
|
|
15625
16273
|
} else {
|
|
15626
|
-
|
|
16274
|
+
object3 = await this.scene.addBlock(blockState, {
|
|
15627
16275
|
mergeMeshes: true,
|
|
15628
16276
|
instanceMeshes: true,
|
|
15629
16277
|
maxInstanceCount: 2e3,
|
|
15630
16278
|
initialState: stored.properties
|
|
15631
16279
|
});
|
|
15632
|
-
|
|
16280
|
+
object3.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
|
|
15633
16281
|
}
|
|
15634
16282
|
const info = { get block() {
|
|
15635
16283
|
return readBlock();
|
|
15636
|
-
}, object:
|
|
16284
|
+
}, object: object3 };
|
|
15637
16285
|
this.renderedBlocks.set(index, info);
|
|
15638
16286
|
return info;
|
|
15639
16287
|
} catch (error) {
|
|
15640
16288
|
this.data.set(index, void 0);
|
|
15641
|
-
|
|
16289
|
+
object3?.removeFromScene();
|
|
15642
16290
|
throw error;
|
|
15643
16291
|
} finally {
|
|
15644
16292
|
await onBlocksChanged?.([worldPos]);
|
|
15645
16293
|
}
|
|
15646
16294
|
}
|
|
16295
|
+
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
15647
16296
|
async setBlockVisibleAt(pos, visible) {
|
|
15648
16297
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
15649
16298
|
const block = this.renderedBlocks.get(index);
|
|
@@ -15697,6 +16346,7 @@ var Chunk = class _Chunk {
|
|
|
15697
16346
|
this.scene.dirty = true;
|
|
15698
16347
|
this.meshDirty = false;
|
|
15699
16348
|
}
|
|
16349
|
+
/** Removes stored blocks and their render objects, then notifies the owning world of changed positions. */
|
|
15700
16350
|
async clear(onBlocksChanged = this.onBlocksChanged) {
|
|
15701
16351
|
const positions = [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
|
|
15702
16352
|
new Vector317(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
|
|
@@ -15717,6 +16367,7 @@ var Chunk = class _Chunk {
|
|
|
15717
16367
|
static isAir(block) {
|
|
15718
16368
|
return ChunkData.isAir(block);
|
|
15719
16369
|
}
|
|
16370
|
+
/** Maps chunk-local coordinates from 0 to 15 to a storage index, with x varying fastest, then z, then y. */
|
|
15720
16371
|
static chunkPosToBlockIndex(pos) {
|
|
15721
16372
|
if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
|
|
15722
16373
|
throw new RangeError("Block coordinates must be integers from 0 to 15 within a chunk");
|
|
@@ -15818,6 +16469,7 @@ export {
|
|
|
15818
16469
|
RequestError,
|
|
15819
16470
|
Requests,
|
|
15820
16471
|
SKIN_PARTS,
|
|
16472
|
+
SceneDocumentLoader,
|
|
15821
16473
|
SceneExporter,
|
|
15822
16474
|
SceneInspector,
|
|
15823
16475
|
SceneObject,
|
|
@@ -15893,6 +16545,7 @@ export {
|
|
|
15893
16545
|
mergeBufferAttributes,
|
|
15894
16546
|
mergeBufferGeometries,
|
|
15895
16547
|
mergeVertices,
|
|
16548
|
+
parseSceneDocument,
|
|
15896
16549
|
prefix,
|
|
15897
16550
|
probeImageSize,
|
|
15898
16551
|
registerBrowserEnv,
|