minerender 2.1.0-alpha.77 → 2.1.0-alpha.79
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/dist/browser/index.d.mts +85 -7
- package/dist/browser/index.d.ts +85 -7
- package/dist/browser/index.js +364 -43
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.mjs +364 -43
- package/dist/browser/index.mjs.map +1 -1
- package/dist/bundle.js +364 -43
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.d.mts +85 -7
- package/dist/node/index.d.ts +85 -7
- package/dist/node/index.js +365 -43
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +364 -43
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/browser/index.mjs
CHANGED
|
@@ -1477,6 +1477,8 @@ var init_ModelMerger = __esm({
|
|
|
1477
1477
|
ModelMerger = class {
|
|
1478
1478
|
/** Loads the parent chain and returns a merged model without modifying the supplied definition. */
|
|
1479
1479
|
static async mergeWithParents(model) {
|
|
1480
|
+
const parts = model.parts;
|
|
1481
|
+
if (parts) return { ...model, parts: await Promise.all(parts.map((part) => this.mergeWithParents(part))) };
|
|
1480
1482
|
const models = await this.collectAllParents(model);
|
|
1481
1483
|
let merged = {};
|
|
1482
1484
|
for (const source of [...models, model]) {
|
|
@@ -1537,6 +1539,7 @@ var init_Models = __esm({
|
|
|
1537
1539
|
init_PersistentCache();
|
|
1538
1540
|
init_AssetKey();
|
|
1539
1541
|
init_AssetParsers();
|
|
1542
|
+
init_DisplayPosition();
|
|
1540
1543
|
Models = class _Models {
|
|
1541
1544
|
// opened lazily: touching the store at import time would hit IndexedDB/disk just for loading
|
|
1542
1545
|
// the library, and would require an EnvProvider before any entry has had a chance to register
|
|
@@ -1551,34 +1554,174 @@ var init_Models = __esm({
|
|
|
1551
1554
|
return (await AssetLoader.getFirst([key, legacyKey], "list" /* LIST */))?.asset;
|
|
1552
1555
|
}).then((r) => r?.files ?? []);
|
|
1553
1556
|
}
|
|
1554
|
-
static async loadAndMerge(key) {
|
|
1555
|
-
const model = key.type === "item" ? await this.getItemModel(key) : await this.getRaw(key);
|
|
1557
|
+
static async loadAndMerge(key, context = {}) {
|
|
1558
|
+
const model = key.type === "item" ? await this.getItemModel(key, context) : await this.getRaw(key);
|
|
1556
1559
|
if (!model) {
|
|
1557
1560
|
return void 0;
|
|
1558
1561
|
}
|
|
1559
1562
|
return ModelMerger.mergeWithParents(model);
|
|
1560
1563
|
}
|
|
1561
|
-
static async getItemModel(key) {
|
|
1564
|
+
static async getItemModel(key, context = {}) {
|
|
1565
|
+
const preview = this.snapshotContext(key, context);
|
|
1562
1566
|
const itemKey = new AssetKey(key.namespace, key.path, "items", void 0, key.rootType, ".json", key.root);
|
|
1563
|
-
const
|
|
1567
|
+
const cacheKey = itemKey.serialize() + this.contextKey(preview);
|
|
1568
|
+
const model = await this.PERSISTENT_CACHE.getOrLoad(`item-v3:${AssetLoader.persistentKey(cacheKey)}`, async () => {
|
|
1564
1569
|
const result = await AssetLoader.getFirst([itemKey, key], "json" /* JSON */);
|
|
1565
1570
|
if (!result) return void 0;
|
|
1566
1571
|
if (result.key.assetType !== "items") return { ...result.asset, key };
|
|
1567
|
-
const
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1572
|
+
const load = async (selected) => {
|
|
1573
|
+
if ("parts" in selected) {
|
|
1574
|
+
return { key, parts: await Promise.all(selected.parts.map(load)) };
|
|
1575
|
+
}
|
|
1576
|
+
if ("item" in selected) {
|
|
1577
|
+
const model3 = await this.getItemModel(selected.item, { displayContext: preview.displayContext });
|
|
1578
|
+
if (!model3) throw new Error(`Item ${key.toNamespacedString()} references missing item ${selected.item.toNamespacedString()}`);
|
|
1579
|
+
return model3;
|
|
1580
|
+
}
|
|
1581
|
+
const modelKey = AssetKey.parse("models", selected.model);
|
|
1582
|
+
modelKey.root = key.root;
|
|
1583
|
+
const model2 = await this.getRaw(modelKey);
|
|
1584
|
+
if (!model2) throw new Error(`Item ${key.toNamespacedString()} references missing model ${selected.model}`);
|
|
1585
|
+
return { ...model2, ...selected.special && { special: selected.special }, ...selected.tints && { tints: selected.tints } };
|
|
1586
|
+
};
|
|
1587
|
+
return load(this.selectItemModel(result.asset.model, key, preview));
|
|
1588
|
+
});
|
|
1589
|
+
const restore = (model2) => ({
|
|
1590
|
+
...model2,
|
|
1591
|
+
key: Object.assign(new AssetKey("", ""), model2.key),
|
|
1592
|
+
...model2.parts && { parts: model2.parts.map(restore) }
|
|
1573
1593
|
});
|
|
1574
|
-
return model ?
|
|
1594
|
+
return model ? restore(model) : void 0;
|
|
1595
|
+
}
|
|
1596
|
+
static contextIdentifier(id) {
|
|
1597
|
+
if (typeof id !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(id)) {
|
|
1598
|
+
throw new Error(`Invalid item-preview identifier: ${id}`);
|
|
1599
|
+
}
|
|
1600
|
+
return id.includes(":") ? id : `minecraft:${id}`;
|
|
1601
|
+
}
|
|
1602
|
+
static snapshotContext(key, context) {
|
|
1603
|
+
const displayContext = context.displayContext ?? "gui" /* GUI */;
|
|
1604
|
+
if (displayContext !== "none" && !Object.values(DisplayPosition).includes(displayContext)) {
|
|
1605
|
+
throw new Error(`Unsupported item display context: ${displayContext}`);
|
|
1606
|
+
}
|
|
1607
|
+
for (const map of [context.properties, context.components, context.itemReferences]) {
|
|
1608
|
+
if (map !== void 0 && (!map || typeof map !== "object" || Array.isArray(map))) {
|
|
1609
|
+
throw new Error("Item-preview properties, components, and references must be objects keyed by identifier");
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
const count = context.count === void 0 ? 1 : context.count;
|
|
1613
|
+
if (!Number.isSafeInteger(count) || count < 0) throw new Error("Item-preview count must be a nonnegative safe integer");
|
|
1614
|
+
const properties = {};
|
|
1615
|
+
for (const [id, value] of Object.entries(context.properties ?? {})) {
|
|
1616
|
+
const property = this.contextIdentifier(id);
|
|
1617
|
+
if (!["boolean", "string", "number"].includes(typeof value) || typeof value === "number" && !Number.isFinite(value)) {
|
|
1618
|
+
throw new Error(`Item-preview property ${property} must be a boolean, string, or finite number`);
|
|
1619
|
+
}
|
|
1620
|
+
if (Object.prototype.hasOwnProperty.call(properties, property)) throw new Error(`Duplicate item-preview property: ${property}`);
|
|
1621
|
+
properties[property] = value;
|
|
1622
|
+
}
|
|
1623
|
+
const components = {};
|
|
1624
|
+
for (const [id, value] of Object.entries(context.components ?? {})) {
|
|
1625
|
+
const component = this.contextIdentifier(id);
|
|
1626
|
+
if (Object.prototype.hasOwnProperty.call(components, component)) throw new Error(`Duplicate item-preview component: ${component}`);
|
|
1627
|
+
components[component] = this.snapshotJson(value);
|
|
1628
|
+
}
|
|
1629
|
+
const itemReferences = {};
|
|
1630
|
+
for (const [id, value] of Object.entries(context.itemReferences ?? {})) {
|
|
1631
|
+
const reference = this.contextIdentifier(id);
|
|
1632
|
+
if (!isAssetKey(value) || value.assetType !== "models" || value.type !== "item") {
|
|
1633
|
+
throw new Error(`Item-preview reference ${reference} must be an item-model AssetKey`);
|
|
1634
|
+
}
|
|
1635
|
+
this.contextIdentifier(`${value.namespace}:${value.path}`);
|
|
1636
|
+
if (Object.prototype.hasOwnProperty.call(itemReferences, reference)) throw new Error(`Duplicate item-preview reference: ${reference}`);
|
|
1637
|
+
itemReferences[reference] = Object.assign(new AssetKey("", ""), value, { root: value.root ?? key.root });
|
|
1638
|
+
}
|
|
1639
|
+
return { displayContext, properties, components, count, itemReferences };
|
|
1640
|
+
}
|
|
1641
|
+
static contextKey(context) {
|
|
1642
|
+
const itemReferences = Object.fromEntries(Object.entries(context.itemReferences).map(([id, key]) => [id, key.serialize()]));
|
|
1643
|
+
return `|item-v3:${JSON.stringify(this.snapshotJson({ ...context, itemReferences }))}`;
|
|
1644
|
+
}
|
|
1645
|
+
static snapshotJson(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
1646
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return value;
|
|
1647
|
+
if (typeof value !== "object" || ancestors.has(value) || !Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
|
|
1648
|
+
throw new Error("Item-preview components must contain finite JSON values without circular references");
|
|
1649
|
+
}
|
|
1650
|
+
ancestors.add(value);
|
|
1651
|
+
const copy = Array.isArray(value) ? Array.from(value, (entry) => this.snapshotJson(entry, ancestors)) : Object.fromEntries(Object.keys(value).sort().map((key) => [key, this.snapshotJson(value[key], ancestors)]));
|
|
1652
|
+
ancestors.delete(value);
|
|
1653
|
+
return copy;
|
|
1654
|
+
}
|
|
1655
|
+
static propertyValue(node, context) {
|
|
1656
|
+
const kind = node.type.replace(/^minecraft:/, "");
|
|
1657
|
+
const property = node.property ? this.contextIdentifier(node.property) : void 0;
|
|
1658
|
+
if (kind === "select" && property === "minecraft:display_context") return context.displayContext;
|
|
1659
|
+
if (property && Object.prototype.hasOwnProperty.call(context.properties, property)) return context.properties[property];
|
|
1660
|
+
const fallback = kind === "condition" ? false : kind === "range_dispatch" ? 0 : void 0;
|
|
1661
|
+
const component = (id) => context.components[this.contextIdentifier(id)];
|
|
1662
|
+
if (property === "minecraft:custom_model_data") {
|
|
1663
|
+
const index = node.index ?? 0;
|
|
1664
|
+
if (!Number.isSafeInteger(index) || index < 0) throw new Error("Item-model custom_model_data index must be a nonnegative integer");
|
|
1665
|
+
const data = component("custom_model_data");
|
|
1666
|
+
if (data === void 0) return fallback;
|
|
1667
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) throw new Error("Item-preview custom_model_data must be an object");
|
|
1668
|
+
const field = kind === "condition" ? "flags" : kind === "select" ? "strings" : "floats";
|
|
1669
|
+
const values = data[field];
|
|
1670
|
+
if (values === void 0) return fallback;
|
|
1671
|
+
if (!Array.isArray(values)) throw new Error(`Item-preview custom_model_data.${field} must be an array`);
|
|
1672
|
+
return index < values.length ? values[index] : fallback;
|
|
1673
|
+
}
|
|
1674
|
+
if (kind === "condition" && property === "minecraft:has_component") {
|
|
1675
|
+
if (node.ignore_default) throw new Error("Item-preview has_component with ignore_default requires an explicit properties override; item defaults are unavailable");
|
|
1676
|
+
return Object.prototype.hasOwnProperty.call(context.components, this.contextIdentifier(node.component));
|
|
1677
|
+
}
|
|
1678
|
+
if (kind === "select") {
|
|
1679
|
+
switch (property) {
|
|
1680
|
+
case "minecraft:component":
|
|
1681
|
+
return component(node.component);
|
|
1682
|
+
case "minecraft:block_state": {
|
|
1683
|
+
const state = component("block_state");
|
|
1684
|
+
if (state === void 0) return void 0;
|
|
1685
|
+
if (!state || typeof state !== "object" || Array.isArray(state)) throw new Error("Item-preview block_state must be an object");
|
|
1686
|
+
if (typeof node.block_state_property !== "string") throw new Error("Item-model block_state requires block_state_property");
|
|
1687
|
+
return Object.prototype.hasOwnProperty.call(state, node.block_state_property) ? state[node.block_state_property] : void 0;
|
|
1688
|
+
}
|
|
1689
|
+
case "minecraft:charge_type": {
|
|
1690
|
+
const projectiles = component("charged_projectiles");
|
|
1691
|
+
if (projectiles === void 0) return "none";
|
|
1692
|
+
if (!Array.isArray(projectiles)) throw new Error("Item-preview charged_projectiles must be an array of item stacks");
|
|
1693
|
+
const ids = projectiles.map((stack) => {
|
|
1694
|
+
if (!stack || typeof stack !== "object" || Array.isArray(stack)) throw new Error("Item-preview charged_projectiles must contain item-stack objects");
|
|
1695
|
+
return this.contextIdentifier(stack.id);
|
|
1696
|
+
});
|
|
1697
|
+
return !ids.length ? "none" : ids.includes("minecraft:firework_rocket") ? "rocket" : "arrow";
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
if (kind === "range_dispatch" && (property === "minecraft:damage" || property === "minecraft:count")) {
|
|
1702
|
+
const damage = property === "minecraft:damage";
|
|
1703
|
+
const maximumComponent = component(damage ? "max_damage" : "max_stack_size");
|
|
1704
|
+
const maximum = maximumComponent === void 0 ? damage ? 0 : 1 : maximumComponent;
|
|
1705
|
+
const damageComponent = component("damage");
|
|
1706
|
+
const value = damage ? damageComponent === void 0 ? 0 : damageComponent : context.count;
|
|
1707
|
+
if (typeof value !== "number" || typeof maximum !== "number" || maximum < 0) throw new Error(`Item-preview ${property} requires numeric components and a nonnegative maximum`);
|
|
1708
|
+
const clamped = Math.max(0, Math.min(maximum, value));
|
|
1709
|
+
return node.normalize === false ? clamped : clamped / maximum;
|
|
1710
|
+
}
|
|
1711
|
+
return fallback;
|
|
1575
1712
|
}
|
|
1576
|
-
|
|
1577
|
-
static defaultItemModel(node, key) {
|
|
1713
|
+
static selectItemModel(node, key, context) {
|
|
1578
1714
|
if (!node || typeof node.type !== "string") {
|
|
1579
1715
|
throw new Error(`Unsupported item model definition for ${key.toNamespacedString()}`);
|
|
1580
1716
|
}
|
|
1581
1717
|
switch (node.type.replace(/^minecraft:/, "")) {
|
|
1718
|
+
case "composite":
|
|
1719
|
+
if (Array.isArray(node.models)) return { parts: node.models.map((child) => this.selectItemModel(child, key, context)) };
|
|
1720
|
+
break;
|
|
1721
|
+
case "bundle/selected_item": {
|
|
1722
|
+
const item = context.itemReferences["minecraft:bundle/selected_item"];
|
|
1723
|
+
return item ? { item } : { parts: [] };
|
|
1724
|
+
}
|
|
1582
1725
|
case "model":
|
|
1583
1726
|
if (typeof node.model === "string" && node.model) return { model: node.model, tints: node.tints };
|
|
1584
1727
|
break;
|
|
@@ -1599,17 +1742,29 @@ var init_Models = __esm({
|
|
|
1599
1742
|
}
|
|
1600
1743
|
throw new Error(`Unsupported special item renderer ${special.type} for ${key.toNamespacedString()}`);
|
|
1601
1744
|
}
|
|
1602
|
-
case "condition":
|
|
1603
|
-
|
|
1745
|
+
case "condition": {
|
|
1746
|
+
const value = this.propertyValue(node, context);
|
|
1747
|
+
if (typeof value !== "boolean") throw new Error(`Item-preview condition ${node.property} requires a boolean`);
|
|
1748
|
+
return this.selectItemModel(value ? node.on_true : node.on_false, key, context);
|
|
1749
|
+
}
|
|
1604
1750
|
case "select": {
|
|
1605
|
-
const
|
|
1606
|
-
|
|
1751
|
+
const value = this.propertyValue(node, context);
|
|
1752
|
+
const component = node.property && this.contextIdentifier(node.property) === "minecraft:component";
|
|
1753
|
+
if (!component && value !== void 0 && typeof value !== "string") throw new Error(`Item-preview selector ${node.property} requires a string`);
|
|
1754
|
+
const matches = (candidate) => component ? JSON.stringify(this.snapshotJson(candidate)) === JSON.stringify(this.snapshotJson(value)) : candidate === value;
|
|
1755
|
+
const selected = value === void 0 ? void 0 : node.cases?.find((entry) => matches(entry.when) || Array.isArray(entry.when) && entry.when.some(matches))?.model;
|
|
1756
|
+
return this.selectItemModel(selected ?? node.fallback, key, context);
|
|
1607
1757
|
}
|
|
1608
1758
|
case "range_dispatch": {
|
|
1759
|
+
const value = this.propertyValue(node, context);
|
|
1760
|
+
if (typeof value !== "number") throw new Error(`Item-preview range ${node.property} requires a number`);
|
|
1761
|
+
if (Number.isNaN(value)) return this.selectItemModel(node.fallback, key, context);
|
|
1762
|
+
const scaled = value * (node.scale ?? 1);
|
|
1763
|
+
if (!Number.isFinite(scaled)) throw new Error(`Item-preview range ${node.property} requires a finite value and scale`);
|
|
1609
1764
|
const selected = node.entries?.reduce((match, entry) => {
|
|
1610
|
-
return entry.threshold <=
|
|
1765
|
+
return entry.threshold <= scaled && (!match || entry.threshold >= match.threshold) ? entry : match;
|
|
1611
1766
|
}, void 0);
|
|
1612
|
-
return this.
|
|
1767
|
+
return this.selectItemModel(selected?.model ?? node.fallback, key, context);
|
|
1613
1768
|
}
|
|
1614
1769
|
}
|
|
1615
1770
|
throw new Error(`Unsupported item model ${node?.type ?? "definition"} for ${key.toNamespacedString()}`);
|
|
@@ -1636,25 +1791,28 @@ var init_Models = __esm({
|
|
|
1636
1791
|
}
|
|
1637
1792
|
/**
|
|
1638
1793
|
* Loads a model and resolves its parent chain. Returns `undefined` when the model is missing.
|
|
1639
|
-
* Item keys
|
|
1794
|
+
* Item keys default to GUI context and a stack count of 1. Unresolved conditions are false and numeric properties are zero.
|
|
1795
|
+
* Pass `context` to supply component values, property overrides, and item references for a preview.
|
|
1796
|
+
* Composite items retain independently merged children in `ItemModel.parts`.
|
|
1640
1797
|
*
|
|
1641
1798
|
* @param key - Model key, for example `AssetKey.parse("models", "minecraft:item/diamond_sword")`.
|
|
1642
1799
|
*/
|
|
1643
|
-
static async getMerged(key) {
|
|
1800
|
+
static async getMerged(key, context = {}) {
|
|
1644
1801
|
if (!key.assetType) {
|
|
1645
1802
|
key.assetType = "models";
|
|
1646
1803
|
}
|
|
1647
1804
|
if (!key.extension) {
|
|
1648
1805
|
key.extension = ".json";
|
|
1649
1806
|
}
|
|
1650
|
-
const
|
|
1807
|
+
const preview = this.snapshotContext(key, context);
|
|
1808
|
+
const keyStr = key.serialize() + (key.type === "item" ? this.contextKey(preview) : "");
|
|
1651
1809
|
return Caching.mergedModelCache.get(keyStr, (k) => {
|
|
1652
|
-
return _Models.loadAndMerge(key);
|
|
1810
|
+
return _Models.loadAndMerge(key, preview);
|
|
1653
1811
|
});
|
|
1654
1812
|
}
|
|
1655
1813
|
/** Alias for {@link getMerged}. */
|
|
1656
|
-
static async get(key) {
|
|
1657
|
-
return this.getMerged(key);
|
|
1814
|
+
static async get(key, context = {}) {
|
|
1815
|
+
return this.getMerged(key, context);
|
|
1658
1816
|
}
|
|
1659
1817
|
/** Clears persisted models. Use {@link Caching.clear} to also discard in-memory assets. */
|
|
1660
1818
|
static async clearCache() {
|
|
@@ -5492,22 +5650,45 @@ var init_ModelObject = __esm({
|
|
|
5492
5650
|
this.originalModel = originalModel;
|
|
5493
5651
|
this.isModelObject = true;
|
|
5494
5652
|
this.specialMaterials = /* @__PURE__ */ new Map();
|
|
5653
|
+
this.geometries = /* @__PURE__ */ new Set();
|
|
5495
5654
|
this.meshesCreated = false;
|
|
5496
5655
|
this.options = (0, import_ts_deepmerge5.default)({}, _ModelObject.DEFAULT_OPTIONS, options ?? {});
|
|
5497
|
-
if (originalModel.special) this.options.instanceMeshes = false;
|
|
5656
|
+
if (originalModel.special || originalModel.parts) this.options.instanceMeshes = false;
|
|
5498
5657
|
if (this.options.tints) this.options.tints = { ...this.options.tints };
|
|
5499
5658
|
this.addEventListener("added", () => this.updateAnimationSubscription());
|
|
5500
5659
|
this.addEventListener("removed", () => this.updateAnimationSubscription());
|
|
5501
5660
|
}
|
|
5502
5661
|
async init() {
|
|
5662
|
+
const parts = this.originalModel.parts;
|
|
5663
|
+
if (parts) {
|
|
5664
|
+
try {
|
|
5665
|
+
for (const part of parts) {
|
|
5666
|
+
const object3 = new _ModelObject(part, { ...this.options, instanceMeshes: false });
|
|
5667
|
+
this.add(object3);
|
|
5668
|
+
await object3.init();
|
|
5669
|
+
}
|
|
5670
|
+
let order = 0;
|
|
5671
|
+
this.iterateAllMeshes((mesh) => {
|
|
5672
|
+
mesh.renderOrder = order++;
|
|
5673
|
+
for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
|
|
5674
|
+
material.transparent = true;
|
|
5675
|
+
}
|
|
5676
|
+
});
|
|
5677
|
+
} catch (error) {
|
|
5678
|
+
this.disposeAndRemoveAllChildren();
|
|
5679
|
+
throw error;
|
|
5680
|
+
}
|
|
5681
|
+
this.notifyDirty();
|
|
5682
|
+
return;
|
|
5683
|
+
}
|
|
5503
5684
|
this.options.tints = await ItemTints.get(this.originalModel, this.options.tints);
|
|
5504
5685
|
const special = this.originalModel.special;
|
|
5505
5686
|
if (special) {
|
|
5506
|
-
const
|
|
5687
|
+
const parts2 = await SpecialItems.getParts(special, this.originalModel.key?.root);
|
|
5507
5688
|
const transform = this.options.displayPosition ? DisplayTransforms.getMatrix(this.originalModel.display, this.options.displayPosition) : new Matrix49();
|
|
5508
5689
|
transform.multiply(new Matrix49().makeTranslation(-8, -8, -8));
|
|
5509
5690
|
try {
|
|
5510
|
-
for (const part of
|
|
5691
|
+
for (const part of parts2) {
|
|
5511
5692
|
const object3 = new EntityObject(part.model, { flip: false, wireframe: this.options.wireframe });
|
|
5512
5693
|
object3.matrix.copy(transform).multiply(part.transform);
|
|
5513
5694
|
object3.matrixWorldNeedsUpdate = true;
|
|
@@ -5558,6 +5739,7 @@ var init_ModelObject = __esm({
|
|
|
5558
5739
|
if (this.atlas.model.elements) {
|
|
5559
5740
|
this.atlas.model.elements?.forEach((el) => {
|
|
5560
5741
|
const elGeo = this._getBoxGeometryFromElement(el).clone();
|
|
5742
|
+
this.geometries.add(elGeo);
|
|
5561
5743
|
if (this.options.cullMask) {
|
|
5562
5744
|
const indices = Array.from(elGeo.getIndex().array);
|
|
5563
5745
|
elGeo.setIndex(indices.filter((_, index) => {
|
|
@@ -5611,10 +5793,14 @@ var init_ModelObject = __esm({
|
|
|
5611
5793
|
let combinedGeo;
|
|
5612
5794
|
if (allGeos.length > 0) {
|
|
5613
5795
|
combinedGeo = mergeBufferGeometries(allGeos);
|
|
5796
|
+
for (const geometry of allGeos) {
|
|
5797
|
+
this.geometries.delete(geometry);
|
|
5798
|
+
}
|
|
5614
5799
|
} else {
|
|
5615
5800
|
combinedGeo = new BoxGeometry5(16, 16, 16);
|
|
5616
5801
|
if (displayTransform) DisplayTransforms.apply(combinedGeo, displayTransform);
|
|
5617
5802
|
}
|
|
5803
|
+
this.geometries.add(combinedGeo);
|
|
5618
5804
|
combinedGeo.computeBoundingBox();
|
|
5619
5805
|
let mesh;
|
|
5620
5806
|
if (this.options.instanceMeshes) {
|
|
@@ -5639,6 +5825,9 @@ var init_ModelObject = __esm({
|
|
|
5639
5825
|
const mat = Materials.createShadedCanvasMaterial(this.atlas.image.canvas, this.atlas.hasTransparency, false, true);
|
|
5640
5826
|
this.atlasMaterial = mat;
|
|
5641
5827
|
this.atlasTexture = mat.uniforms?.map?.value ?? mat.map;
|
|
5828
|
+
if (this.options.displayPosition === "gui" /* GUI */ && this.originalModel.gui_light === "front" /* FRONT */ && mat.uniforms?.SHADE) {
|
|
5829
|
+
mat.uniforms.SHADE.value = false;
|
|
5830
|
+
}
|
|
5642
5831
|
this.iterateAllMeshes((mesh) => {
|
|
5643
5832
|
if (mesh.geometry.hasAttribute("color")) mat.vertexColors = true;
|
|
5644
5833
|
mesh.material = mat;
|
|
@@ -5647,7 +5836,9 @@ var init_ModelObject = __esm({
|
|
|
5647
5836
|
}
|
|
5648
5837
|
}
|
|
5649
5838
|
updateAnimationSubscription() {
|
|
5650
|
-
|
|
5839
|
+
let root = this;
|
|
5840
|
+
while (root.parent && isModelObject(root.parent)) root = root.parent;
|
|
5841
|
+
const active = !!root.parent && (!this.isInstanced || this.instanceCounter > 0);
|
|
5651
5842
|
if (active && this.atlas?.hasAnimation && this.atlasTexture) {
|
|
5652
5843
|
if (!this.unsubscribeAtlas) {
|
|
5653
5844
|
this.atlasTexture.needsUpdate = true;
|
|
@@ -5663,6 +5854,9 @@ var init_ModelObject = __esm({
|
|
|
5663
5854
|
this.unsubscribeAtlas?.();
|
|
5664
5855
|
this.unsubscribeAtlas = void 0;
|
|
5665
5856
|
}
|
|
5857
|
+
for (const child of this.children) {
|
|
5858
|
+
if (isModelObject(child)) child.updateAnimationSubscription();
|
|
5859
|
+
}
|
|
5666
5860
|
}
|
|
5667
5861
|
nextInstance() {
|
|
5668
5862
|
const reference = super.nextInstance();
|
|
@@ -5682,6 +5876,8 @@ var init_ModelObject = __esm({
|
|
|
5682
5876
|
this.atlasMaterial = void 0;
|
|
5683
5877
|
for (const material of this.specialMaterials.values()) material.dispose();
|
|
5684
5878
|
this.specialMaterials.clear();
|
|
5879
|
+
for (const geometry of this.geometries) geometry.dispose();
|
|
5880
|
+
this.geometries.clear();
|
|
5685
5881
|
super.disposeAndRemoveAllChildren();
|
|
5686
5882
|
}
|
|
5687
5883
|
};
|
|
@@ -9263,6 +9459,7 @@ function copyDataTexture(source) {
|
|
|
9263
9459
|
|
|
9264
9460
|
// src/gui/GuiHelper.ts
|
|
9265
9461
|
init_AssetKey();
|
|
9462
|
+
init_ModelTextures();
|
|
9266
9463
|
|
|
9267
9464
|
// src/gui/GuiText.ts
|
|
9268
9465
|
import { BufferGeometry as BufferGeometry2, Color as Color8, Float32BufferAttribute as Float32BufferAttribute4 } from "three";
|
|
@@ -9372,7 +9569,128 @@ function createGuiTextGeometry(layout, shadow = true) {
|
|
|
9372
9569
|
}
|
|
9373
9570
|
|
|
9374
9571
|
// src/gui/GuiHelper.ts
|
|
9572
|
+
var GUI_CONTAINER_LAYOUTS = {
|
|
9573
|
+
generic_54: {
|
|
9574
|
+
texture: "minecraft:gui/container/generic_54",
|
|
9575
|
+
crop: [0, 0, 176, 222],
|
|
9576
|
+
slotOrigin: [8, 18],
|
|
9577
|
+
slotOffset: [18, 18],
|
|
9578
|
+
rowSize: 9,
|
|
9579
|
+
slotCount: 54
|
|
9580
|
+
},
|
|
9581
|
+
crafting_table: {
|
|
9582
|
+
texture: "minecraft:gui/container/crafting_table",
|
|
9583
|
+
crop: [0, 0, 176, 166],
|
|
9584
|
+
slotOrigin: [30, 17],
|
|
9585
|
+
slotOffset: [18, 18],
|
|
9586
|
+
rowSize: 3,
|
|
9587
|
+
slotCount: 9,
|
|
9588
|
+
resultPosition: [124, 35]
|
|
9589
|
+
}
|
|
9590
|
+
};
|
|
9375
9591
|
var GuiHelper = class {
|
|
9592
|
+
/**
|
|
9593
|
+
* Creates a 182×5 boss bar using the active resource pack. Finite progress is clamped to 0–1.
|
|
9594
|
+
* The fill crops the source image; explicit crops do not apply `.mcmeta` sprite scaling.
|
|
9595
|
+
*/
|
|
9596
|
+
static async bossBar(options = {}) {
|
|
9597
|
+
const { color: color2 = "pink", progress = 1, position = [0, 0] } = options;
|
|
9598
|
+
this.validatePosition(position);
|
|
9599
|
+
if (!["pink", "blue", "red", "green", "yellow", "purple", "white"].includes(color2)) {
|
|
9600
|
+
throw new Error(`Unsupported boss bar color: ${color2}`);
|
|
9601
|
+
}
|
|
9602
|
+
if (!Number.isFinite(progress)) throw new Error("Boss bar progress must be finite");
|
|
9603
|
+
const value = Math.max(0, Math.min(1, progress));
|
|
9604
|
+
const width = value > 0 ? Math.floor(value * 181) + 1 : 0;
|
|
9605
|
+
const texture = `minecraft:gui/sprites/boss_bar/${color2}`;
|
|
9606
|
+
const layers = [{
|
|
9607
|
+
name: "boss-bar-background",
|
|
9608
|
+
texture: `${texture}_background`,
|
|
9609
|
+
position: [...position],
|
|
9610
|
+
size: [182, 5]
|
|
9611
|
+
}];
|
|
9612
|
+
if (width > 0) {
|
|
9613
|
+
layers.push({
|
|
9614
|
+
...await this.cropTexture(`${texture}_progress`, [0, 0, width, 5], [182, 5], position),
|
|
9615
|
+
name: "boss-bar-progress"
|
|
9616
|
+
});
|
|
9617
|
+
}
|
|
9618
|
+
return layers;
|
|
9619
|
+
}
|
|
9620
|
+
/**
|
|
9621
|
+
* Creates a 192×192 book page with optional arrows and supplied text. Buttons are hidden by default.
|
|
9622
|
+
* Callers choose the page content and button states; this helper does not paginate or handle clicks.
|
|
9623
|
+
*/
|
|
9624
|
+
static async book(options = {}) {
|
|
9625
|
+
const {
|
|
9626
|
+
position = [0, 0],
|
|
9627
|
+
previous = "hidden",
|
|
9628
|
+
next = "hidden",
|
|
9629
|
+
text: text2,
|
|
9630
|
+
color: color2 = 0,
|
|
9631
|
+
shadow = false,
|
|
9632
|
+
maxWidth = 114,
|
|
9633
|
+
lineHeight = 9,
|
|
9634
|
+
...style
|
|
9635
|
+
} = options;
|
|
9636
|
+
this.validatePosition(position);
|
|
9637
|
+
for (const state of [previous, next]) {
|
|
9638
|
+
if (!["hidden", "normal", "hover"].includes(state)) throw new Error(`Unsupported book button state: ${state}`);
|
|
9639
|
+
}
|
|
9640
|
+
const layers = [{
|
|
9641
|
+
...await this.cropTexture("minecraft:gui/book", [0, 0, 192, 192], [256, 256], position),
|
|
9642
|
+
name: "book-background"
|
|
9643
|
+
}];
|
|
9644
|
+
for (const [name, direction, state, x] of [
|
|
9645
|
+
["previous", "backward", previous, 43],
|
|
9646
|
+
["next", "forward", next, 116]
|
|
9647
|
+
]) {
|
|
9648
|
+
if (state === "hidden") continue;
|
|
9649
|
+
layers.push({
|
|
9650
|
+
name: `book-${name}`,
|
|
9651
|
+
texture: `minecraft:gui/sprites/widget/page_${direction}${state === "hover" ? "_highlighted" : ""}`,
|
|
9652
|
+
position: [position[0] + x, position[1] + 157],
|
|
9653
|
+
size: [23, 13]
|
|
9654
|
+
});
|
|
9655
|
+
}
|
|
9656
|
+
if (text2 !== void 0) {
|
|
9657
|
+
layers.push({
|
|
9658
|
+
name: "book-text",
|
|
9659
|
+
text: text2,
|
|
9660
|
+
position: [position[0] + 36, position[1] + 30],
|
|
9661
|
+
color: color2,
|
|
9662
|
+
shadow,
|
|
9663
|
+
maxWidth,
|
|
9664
|
+
lineHeight,
|
|
9665
|
+
...style
|
|
9666
|
+
});
|
|
9667
|
+
}
|
|
9668
|
+
return layers;
|
|
9669
|
+
}
|
|
9670
|
+
/** Creates a container background. Use {@link GUI_CONTAINER_LAYOUTS} with {@link inventorySlot} to place its items. */
|
|
9671
|
+
static async container(type, position = [0, 0]) {
|
|
9672
|
+
this.validatePosition(position);
|
|
9673
|
+
if (type !== "generic_54" && type !== "crafting_table") throw new Error(`Unsupported GUI container: ${type}`);
|
|
9674
|
+
const layout = GUI_CONTAINER_LAYOUTS[type];
|
|
9675
|
+
return [{ ...await this.cropTexture(layout.texture, layout.crop, [256, 256], position), name: "background" }];
|
|
9676
|
+
}
|
|
9677
|
+
static validatePosition(position) {
|
|
9678
|
+
if (!Array.isArray(position) || position.length !== 2 || !position.every(Number.isFinite)) {
|
|
9679
|
+
throw new Error("GUI position must contain two finite coordinates");
|
|
9680
|
+
}
|
|
9681
|
+
}
|
|
9682
|
+
static async cropTexture(texture, crop, referenceSize, position) {
|
|
9683
|
+
const image = await ModelTextures.preload(AssetKey.parse("textures", texture));
|
|
9684
|
+
if (!image) throw new Error(`Could not load GUI texture ${texture}`);
|
|
9685
|
+
const [x, y, width, height] = crop;
|
|
9686
|
+
const scaleX = image.width / referenceSize[0], scaleY = image.height / referenceSize[1];
|
|
9687
|
+
return {
|
|
9688
|
+
texture,
|
|
9689
|
+
crop: [x * scaleX, y * scaleY, width * scaleX, height * scaleY],
|
|
9690
|
+
position: [...position],
|
|
9691
|
+
size: [width, height]
|
|
9692
|
+
};
|
|
9693
|
+
}
|
|
9376
9694
|
/** Sizes a tooltip around supplied text; the first entry is its title. */
|
|
9377
9695
|
static async tooltip(lines, options = {}) {
|
|
9378
9696
|
if (!lines.length) return [];
|
|
@@ -9407,8 +9725,9 @@ var GuiHelper = class {
|
|
|
9407
9725
|
}
|
|
9408
9726
|
/** Creates crafting-table layers for ingredients and the result; stack counts are not drawn. */
|
|
9409
9727
|
static recipe(recipe, options = {}) {
|
|
9728
|
+
const layout = GUI_CONTAINER_LAYOUTS.crafting_table;
|
|
9410
9729
|
const layers = [
|
|
9411
|
-
{ name: "background", texture:
|
|
9730
|
+
{ name: "background", texture: layout.texture, crop: [...layout.crop] }
|
|
9412
9731
|
];
|
|
9413
9732
|
const itemKey = (ingredient) => {
|
|
9414
9733
|
const item = typeof ingredient === "string" && !ingredient.startsWith("#") ? ingredient : typeof ingredient === "object" && "item" in ingredient ? ingredient.item : options.resolveIngredient?.(ingredient);
|
|
@@ -9423,7 +9742,7 @@ var GuiHelper = class {
|
|
|
9423
9742
|
layers.push({
|
|
9424
9743
|
name: `ingredient-${slot}`,
|
|
9425
9744
|
item: itemKey(ingredient),
|
|
9426
|
-
position: this.inventorySlot(slot,
|
|
9745
|
+
position: this.inventorySlot(slot, layout.slotOrigin, layout.slotOffset, layout.rowSize)
|
|
9427
9746
|
});
|
|
9428
9747
|
};
|
|
9429
9748
|
switch (recipe.type) {
|
|
@@ -9458,7 +9777,7 @@ var GuiHelper = class {
|
|
|
9458
9777
|
layers.push({
|
|
9459
9778
|
name: "result",
|
|
9460
9779
|
item: itemKey("id" in recipe.result ? recipe.result.id : recipe.result.item),
|
|
9461
|
-
position: [
|
|
9780
|
+
position: [...layout.resultPosition]
|
|
9462
9781
|
});
|
|
9463
9782
|
return layers;
|
|
9464
9783
|
}
|
|
@@ -9552,7 +9871,6 @@ init_SceneObject();
|
|
|
9552
9871
|
init_Materials();
|
|
9553
9872
|
init_ModelObject();
|
|
9554
9873
|
init_DisplayPosition();
|
|
9555
|
-
init_GuiLight();
|
|
9556
9874
|
import { Box2, Box3 as Box32, DoubleSide as DoubleSide3, Group, Matrix4 as Matrix410, Mesh as Mesh6, MeshBasicMaterial as MeshBasicMaterial5, Vector2 as Vector23 } from "three";
|
|
9557
9875
|
var GuiObject = class extends SceneObject {
|
|
9558
9876
|
constructor(textureLayers, options) {
|
|
@@ -9621,12 +9939,16 @@ var GuiObject = class extends SceneObject {
|
|
|
9621
9939
|
mesh2.geometry.computeBoundingBox();
|
|
9622
9940
|
bounds.union(mesh2.geometry.boundingBox.clone().applyMatrix4(new Matrix410().multiplyMatrices(inverse, mesh2.matrixWorld)));
|
|
9623
9941
|
});
|
|
9624
|
-
item.position.z = depth
|
|
9625
|
-
|
|
9942
|
+
item.position.z = depth;
|
|
9943
|
+
if (!bounds.isEmpty()) {
|
|
9944
|
+
item.position.z -= bounds.min.z;
|
|
9945
|
+
depth += bounds.max.z - bounds.min.z;
|
|
9946
|
+
this.bounds.expandByPoint(new Vector23(bounds.min.x, -bounds.max.y));
|
|
9947
|
+
this.bounds.expandByPoint(new Vector23(bounds.max.x, -bounds.min.y));
|
|
9948
|
+
}
|
|
9949
|
+
depth += 0.01;
|
|
9626
9950
|
this.bounds.expandByPoint(new Vector23(x, y));
|
|
9627
9951
|
this.bounds.expandByPoint(new Vector23(x + width2, y + height2));
|
|
9628
|
-
this.bounds.expandByPoint(new Vector23(bounds.min.x, -bounds.max.y));
|
|
9629
|
-
this.bounds.expandByPoint(new Vector23(bounds.max.x, -bounds.min.y));
|
|
9630
9952
|
continue;
|
|
9631
9953
|
}
|
|
9632
9954
|
const key = typeof layer.texture === "string" ? AssetKey.parse("textures", layer.texture) : layer.texture;
|
|
@@ -9666,15 +9988,13 @@ var GuiObject = class extends SceneObject {
|
|
|
9666
9988
|
item.name = `group:${layer.name ?? index}`;
|
|
9667
9989
|
this.add(item);
|
|
9668
9990
|
await item.init();
|
|
9669
|
-
|
|
9670
|
-
|
|
9991
|
+
const meshes = [];
|
|
9992
|
+
item.iterateAllMeshes((mesh) => meshes.push(mesh));
|
|
9993
|
+
meshes.forEach((mesh, partIndex) => {
|
|
9671
9994
|
mesh.name = `mesh:${layer.name ?? index}`;
|
|
9672
|
-
mesh.renderOrder = index;
|
|
9995
|
+
mesh.renderOrder = index + partIndex / (meshes.length + 1) * 0.5;
|
|
9673
9996
|
for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
|
|
9674
9997
|
material.transparent = true;
|
|
9675
|
-
if (model.gui_light === "front" /* FRONT */ && material.uniforms?.SHADE) {
|
|
9676
|
-
material.uniforms.SHADE.value = false;
|
|
9677
|
-
}
|
|
9678
9998
|
}
|
|
9679
9999
|
});
|
|
9680
10000
|
return item;
|
|
@@ -16436,6 +16756,7 @@ export {
|
|
|
16436
16756
|
Env,
|
|
16437
16757
|
FluidModelObject,
|
|
16438
16758
|
Fonts,
|
|
16759
|
+
GUI_CONTAINER_LAYOUTS,
|
|
16439
16760
|
Geometries,
|
|
16440
16761
|
GuiHelper,
|
|
16441
16762
|
GuiLight,
|