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/node/index.js
CHANGED
|
@@ -1528,6 +1528,8 @@ var init_ModelMerger = __esm({
|
|
|
1528
1528
|
ModelMerger = class {
|
|
1529
1529
|
/** Loads the parent chain and returns a merged model without modifying the supplied definition. */
|
|
1530
1530
|
static async mergeWithParents(model) {
|
|
1531
|
+
const parts = model.parts;
|
|
1532
|
+
if (parts) return { ...model, parts: await Promise.all(parts.map((part) => this.mergeWithParents(part))) };
|
|
1531
1533
|
const models = await this.collectAllParents(model);
|
|
1532
1534
|
let merged = {};
|
|
1533
1535
|
for (const source of [...models, model]) {
|
|
@@ -1588,6 +1590,7 @@ var init_Models = __esm({
|
|
|
1588
1590
|
init_PersistentCache();
|
|
1589
1591
|
init_AssetKey();
|
|
1590
1592
|
init_AssetParsers();
|
|
1593
|
+
init_DisplayPosition();
|
|
1591
1594
|
Models = class _Models {
|
|
1592
1595
|
// opened lazily: touching the store at import time would hit IndexedDB/disk just for loading
|
|
1593
1596
|
// the library, and would require an EnvProvider before any entry has had a chance to register
|
|
@@ -1602,34 +1605,174 @@ var init_Models = __esm({
|
|
|
1602
1605
|
return (await AssetLoader.getFirst([key, legacyKey], "list" /* LIST */))?.asset;
|
|
1603
1606
|
}).then((r) => r?.files ?? []);
|
|
1604
1607
|
}
|
|
1605
|
-
static async loadAndMerge(key) {
|
|
1606
|
-
const model = key.type === "item" ? await this.getItemModel(key) : await this.getRaw(key);
|
|
1608
|
+
static async loadAndMerge(key, context = {}) {
|
|
1609
|
+
const model = key.type === "item" ? await this.getItemModel(key, context) : await this.getRaw(key);
|
|
1607
1610
|
if (!model) {
|
|
1608
1611
|
return void 0;
|
|
1609
1612
|
}
|
|
1610
1613
|
return ModelMerger.mergeWithParents(model);
|
|
1611
1614
|
}
|
|
1612
|
-
static async getItemModel(key) {
|
|
1615
|
+
static async getItemModel(key, context = {}) {
|
|
1616
|
+
const preview = this.snapshotContext(key, context);
|
|
1613
1617
|
const itemKey = new AssetKey(key.namespace, key.path, "items", void 0, key.rootType, ".json", key.root);
|
|
1614
|
-
const
|
|
1618
|
+
const cacheKey = itemKey.serialize() + this.contextKey(preview);
|
|
1619
|
+
const model = await this.PERSISTENT_CACHE.getOrLoad(`item-v3:${AssetLoader.persistentKey(cacheKey)}`, async () => {
|
|
1615
1620
|
const result = await AssetLoader.getFirst([itemKey, key], "json" /* JSON */);
|
|
1616
1621
|
if (!result) return void 0;
|
|
1617
1622
|
if (result.key.assetType !== "items") return { ...result.asset, key };
|
|
1618
|
-
const
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1623
|
+
const load = async (selected) => {
|
|
1624
|
+
if ("parts" in selected) {
|
|
1625
|
+
return { key, parts: await Promise.all(selected.parts.map(load)) };
|
|
1626
|
+
}
|
|
1627
|
+
if ("item" in selected) {
|
|
1628
|
+
const model3 = await this.getItemModel(selected.item, { displayContext: preview.displayContext });
|
|
1629
|
+
if (!model3) throw new Error(`Item ${key.toNamespacedString()} references missing item ${selected.item.toNamespacedString()}`);
|
|
1630
|
+
return model3;
|
|
1631
|
+
}
|
|
1632
|
+
const modelKey = AssetKey.parse("models", selected.model);
|
|
1633
|
+
modelKey.root = key.root;
|
|
1634
|
+
const model2 = await this.getRaw(modelKey);
|
|
1635
|
+
if (!model2) throw new Error(`Item ${key.toNamespacedString()} references missing model ${selected.model}`);
|
|
1636
|
+
return { ...model2, ...selected.special && { special: selected.special }, ...selected.tints && { tints: selected.tints } };
|
|
1637
|
+
};
|
|
1638
|
+
return load(this.selectItemModel(result.asset.model, key, preview));
|
|
1639
|
+
});
|
|
1640
|
+
const restore = (model2) => ({
|
|
1641
|
+
...model2,
|
|
1642
|
+
key: Object.assign(new AssetKey("", ""), model2.key),
|
|
1643
|
+
...model2.parts && { parts: model2.parts.map(restore) }
|
|
1624
1644
|
});
|
|
1625
|
-
return model ?
|
|
1645
|
+
return model ? restore(model) : void 0;
|
|
1646
|
+
}
|
|
1647
|
+
static contextIdentifier(id) {
|
|
1648
|
+
if (typeof id !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(id)) {
|
|
1649
|
+
throw new Error(`Invalid item-preview identifier: ${id}`);
|
|
1650
|
+
}
|
|
1651
|
+
return id.includes(":") ? id : `minecraft:${id}`;
|
|
1652
|
+
}
|
|
1653
|
+
static snapshotContext(key, context) {
|
|
1654
|
+
const displayContext = context.displayContext ?? "gui" /* GUI */;
|
|
1655
|
+
if (displayContext !== "none" && !Object.values(DisplayPosition).includes(displayContext)) {
|
|
1656
|
+
throw new Error(`Unsupported item display context: ${displayContext}`);
|
|
1657
|
+
}
|
|
1658
|
+
for (const map of [context.properties, context.components, context.itemReferences]) {
|
|
1659
|
+
if (map !== void 0 && (!map || typeof map !== "object" || Array.isArray(map))) {
|
|
1660
|
+
throw new Error("Item-preview properties, components, and references must be objects keyed by identifier");
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
const count = context.count === void 0 ? 1 : context.count;
|
|
1664
|
+
if (!Number.isSafeInteger(count) || count < 0) throw new Error("Item-preview count must be a nonnegative safe integer");
|
|
1665
|
+
const properties = {};
|
|
1666
|
+
for (const [id, value] of Object.entries(context.properties ?? {})) {
|
|
1667
|
+
const property = this.contextIdentifier(id);
|
|
1668
|
+
if (!["boolean", "string", "number"].includes(typeof value) || typeof value === "number" && !Number.isFinite(value)) {
|
|
1669
|
+
throw new Error(`Item-preview property ${property} must be a boolean, string, or finite number`);
|
|
1670
|
+
}
|
|
1671
|
+
if (Object.prototype.hasOwnProperty.call(properties, property)) throw new Error(`Duplicate item-preview property: ${property}`);
|
|
1672
|
+
properties[property] = value;
|
|
1673
|
+
}
|
|
1674
|
+
const components = {};
|
|
1675
|
+
for (const [id, value] of Object.entries(context.components ?? {})) {
|
|
1676
|
+
const component = this.contextIdentifier(id);
|
|
1677
|
+
if (Object.prototype.hasOwnProperty.call(components, component)) throw new Error(`Duplicate item-preview component: ${component}`);
|
|
1678
|
+
components[component] = this.snapshotJson(value);
|
|
1679
|
+
}
|
|
1680
|
+
const itemReferences = {};
|
|
1681
|
+
for (const [id, value] of Object.entries(context.itemReferences ?? {})) {
|
|
1682
|
+
const reference = this.contextIdentifier(id);
|
|
1683
|
+
if (!isAssetKey(value) || value.assetType !== "models" || value.type !== "item") {
|
|
1684
|
+
throw new Error(`Item-preview reference ${reference} must be an item-model AssetKey`);
|
|
1685
|
+
}
|
|
1686
|
+
this.contextIdentifier(`${value.namespace}:${value.path}`);
|
|
1687
|
+
if (Object.prototype.hasOwnProperty.call(itemReferences, reference)) throw new Error(`Duplicate item-preview reference: ${reference}`);
|
|
1688
|
+
itemReferences[reference] = Object.assign(new AssetKey("", ""), value, { root: value.root ?? key.root });
|
|
1689
|
+
}
|
|
1690
|
+
return { displayContext, properties, components, count, itemReferences };
|
|
1691
|
+
}
|
|
1692
|
+
static contextKey(context) {
|
|
1693
|
+
const itemReferences = Object.fromEntries(Object.entries(context.itemReferences).map(([id, key]) => [id, key.serialize()]));
|
|
1694
|
+
return `|item-v3:${JSON.stringify(this.snapshotJson({ ...context, itemReferences }))}`;
|
|
1695
|
+
}
|
|
1696
|
+
static snapshotJson(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
1697
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return value;
|
|
1698
|
+
if (typeof value !== "object" || ancestors.has(value) || !Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
|
|
1699
|
+
throw new Error("Item-preview components must contain finite JSON values without circular references");
|
|
1700
|
+
}
|
|
1701
|
+
ancestors.add(value);
|
|
1702
|
+
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)]));
|
|
1703
|
+
ancestors.delete(value);
|
|
1704
|
+
return copy;
|
|
1705
|
+
}
|
|
1706
|
+
static propertyValue(node, context) {
|
|
1707
|
+
const kind = node.type.replace(/^minecraft:/, "");
|
|
1708
|
+
const property = node.property ? this.contextIdentifier(node.property) : void 0;
|
|
1709
|
+
if (kind === "select" && property === "minecraft:display_context") return context.displayContext;
|
|
1710
|
+
if (property && Object.prototype.hasOwnProperty.call(context.properties, property)) return context.properties[property];
|
|
1711
|
+
const fallback = kind === "condition" ? false : kind === "range_dispatch" ? 0 : void 0;
|
|
1712
|
+
const component = (id) => context.components[this.contextIdentifier(id)];
|
|
1713
|
+
if (property === "minecraft:custom_model_data") {
|
|
1714
|
+
const index = node.index ?? 0;
|
|
1715
|
+
if (!Number.isSafeInteger(index) || index < 0) throw new Error("Item-model custom_model_data index must be a nonnegative integer");
|
|
1716
|
+
const data = component("custom_model_data");
|
|
1717
|
+
if (data === void 0) return fallback;
|
|
1718
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) throw new Error("Item-preview custom_model_data must be an object");
|
|
1719
|
+
const field = kind === "condition" ? "flags" : kind === "select" ? "strings" : "floats";
|
|
1720
|
+
const values = data[field];
|
|
1721
|
+
if (values === void 0) return fallback;
|
|
1722
|
+
if (!Array.isArray(values)) throw new Error(`Item-preview custom_model_data.${field} must be an array`);
|
|
1723
|
+
return index < values.length ? values[index] : fallback;
|
|
1724
|
+
}
|
|
1725
|
+
if (kind === "condition" && property === "minecraft:has_component") {
|
|
1726
|
+
if (node.ignore_default) throw new Error("Item-preview has_component with ignore_default requires an explicit properties override; item defaults are unavailable");
|
|
1727
|
+
return Object.prototype.hasOwnProperty.call(context.components, this.contextIdentifier(node.component));
|
|
1728
|
+
}
|
|
1729
|
+
if (kind === "select") {
|
|
1730
|
+
switch (property) {
|
|
1731
|
+
case "minecraft:component":
|
|
1732
|
+
return component(node.component);
|
|
1733
|
+
case "minecraft:block_state": {
|
|
1734
|
+
const state = component("block_state");
|
|
1735
|
+
if (state === void 0) return void 0;
|
|
1736
|
+
if (!state || typeof state !== "object" || Array.isArray(state)) throw new Error("Item-preview block_state must be an object");
|
|
1737
|
+
if (typeof node.block_state_property !== "string") throw new Error("Item-model block_state requires block_state_property");
|
|
1738
|
+
return Object.prototype.hasOwnProperty.call(state, node.block_state_property) ? state[node.block_state_property] : void 0;
|
|
1739
|
+
}
|
|
1740
|
+
case "minecraft:charge_type": {
|
|
1741
|
+
const projectiles = component("charged_projectiles");
|
|
1742
|
+
if (projectiles === void 0) return "none";
|
|
1743
|
+
if (!Array.isArray(projectiles)) throw new Error("Item-preview charged_projectiles must be an array of item stacks");
|
|
1744
|
+
const ids = projectiles.map((stack) => {
|
|
1745
|
+
if (!stack || typeof stack !== "object" || Array.isArray(stack)) throw new Error("Item-preview charged_projectiles must contain item-stack objects");
|
|
1746
|
+
return this.contextIdentifier(stack.id);
|
|
1747
|
+
});
|
|
1748
|
+
return !ids.length ? "none" : ids.includes("minecraft:firework_rocket") ? "rocket" : "arrow";
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
if (kind === "range_dispatch" && (property === "minecraft:damage" || property === "minecraft:count")) {
|
|
1753
|
+
const damage = property === "minecraft:damage";
|
|
1754
|
+
const maximumComponent = component(damage ? "max_damage" : "max_stack_size");
|
|
1755
|
+
const maximum = maximumComponent === void 0 ? damage ? 0 : 1 : maximumComponent;
|
|
1756
|
+
const damageComponent = component("damage");
|
|
1757
|
+
const value = damage ? damageComponent === void 0 ? 0 : damageComponent : context.count;
|
|
1758
|
+
if (typeof value !== "number" || typeof maximum !== "number" || maximum < 0) throw new Error(`Item-preview ${property} requires numeric components and a nonnegative maximum`);
|
|
1759
|
+
const clamped = Math.max(0, Math.min(maximum, value));
|
|
1760
|
+
return node.normalize === false ? clamped : clamped / maximum;
|
|
1761
|
+
}
|
|
1762
|
+
return fallback;
|
|
1626
1763
|
}
|
|
1627
|
-
|
|
1628
|
-
static defaultItemModel(node, key) {
|
|
1764
|
+
static selectItemModel(node, key, context) {
|
|
1629
1765
|
if (!node || typeof node.type !== "string") {
|
|
1630
1766
|
throw new Error(`Unsupported item model definition for ${key.toNamespacedString()}`);
|
|
1631
1767
|
}
|
|
1632
1768
|
switch (node.type.replace(/^minecraft:/, "")) {
|
|
1769
|
+
case "composite":
|
|
1770
|
+
if (Array.isArray(node.models)) return { parts: node.models.map((child) => this.selectItemModel(child, key, context)) };
|
|
1771
|
+
break;
|
|
1772
|
+
case "bundle/selected_item": {
|
|
1773
|
+
const item = context.itemReferences["minecraft:bundle/selected_item"];
|
|
1774
|
+
return item ? { item } : { parts: [] };
|
|
1775
|
+
}
|
|
1633
1776
|
case "model":
|
|
1634
1777
|
if (typeof node.model === "string" && node.model) return { model: node.model, tints: node.tints };
|
|
1635
1778
|
break;
|
|
@@ -1650,17 +1793,29 @@ var init_Models = __esm({
|
|
|
1650
1793
|
}
|
|
1651
1794
|
throw new Error(`Unsupported special item renderer ${special.type} for ${key.toNamespacedString()}`);
|
|
1652
1795
|
}
|
|
1653
|
-
case "condition":
|
|
1654
|
-
|
|
1796
|
+
case "condition": {
|
|
1797
|
+
const value = this.propertyValue(node, context);
|
|
1798
|
+
if (typeof value !== "boolean") throw new Error(`Item-preview condition ${node.property} requires a boolean`);
|
|
1799
|
+
return this.selectItemModel(value ? node.on_true : node.on_false, key, context);
|
|
1800
|
+
}
|
|
1655
1801
|
case "select": {
|
|
1656
|
-
const
|
|
1657
|
-
|
|
1802
|
+
const value = this.propertyValue(node, context);
|
|
1803
|
+
const component = node.property && this.contextIdentifier(node.property) === "minecraft:component";
|
|
1804
|
+
if (!component && value !== void 0 && typeof value !== "string") throw new Error(`Item-preview selector ${node.property} requires a string`);
|
|
1805
|
+
const matches = (candidate) => component ? JSON.stringify(this.snapshotJson(candidate)) === JSON.stringify(this.snapshotJson(value)) : candidate === value;
|
|
1806
|
+
const selected = value === void 0 ? void 0 : node.cases?.find((entry) => matches(entry.when) || Array.isArray(entry.when) && entry.when.some(matches))?.model;
|
|
1807
|
+
return this.selectItemModel(selected ?? node.fallback, key, context);
|
|
1658
1808
|
}
|
|
1659
1809
|
case "range_dispatch": {
|
|
1810
|
+
const value = this.propertyValue(node, context);
|
|
1811
|
+
if (typeof value !== "number") throw new Error(`Item-preview range ${node.property} requires a number`);
|
|
1812
|
+
if (Number.isNaN(value)) return this.selectItemModel(node.fallback, key, context);
|
|
1813
|
+
const scaled = value * (node.scale ?? 1);
|
|
1814
|
+
if (!Number.isFinite(scaled)) throw new Error(`Item-preview range ${node.property} requires a finite value and scale`);
|
|
1660
1815
|
const selected = node.entries?.reduce((match, entry) => {
|
|
1661
|
-
return entry.threshold <=
|
|
1816
|
+
return entry.threshold <= scaled && (!match || entry.threshold >= match.threshold) ? entry : match;
|
|
1662
1817
|
}, void 0);
|
|
1663
|
-
return this.
|
|
1818
|
+
return this.selectItemModel(selected?.model ?? node.fallback, key, context);
|
|
1664
1819
|
}
|
|
1665
1820
|
}
|
|
1666
1821
|
throw new Error(`Unsupported item model ${node?.type ?? "definition"} for ${key.toNamespacedString()}`);
|
|
@@ -1687,25 +1842,28 @@ var init_Models = __esm({
|
|
|
1687
1842
|
}
|
|
1688
1843
|
/**
|
|
1689
1844
|
* Loads a model and resolves its parent chain. Returns `undefined` when the model is missing.
|
|
1690
|
-
* Item keys
|
|
1845
|
+
* Item keys default to GUI context and a stack count of 1. Unresolved conditions are false and numeric properties are zero.
|
|
1846
|
+
* Pass `context` to supply component values, property overrides, and item references for a preview.
|
|
1847
|
+
* Composite items retain independently merged children in `ItemModel.parts`.
|
|
1691
1848
|
*
|
|
1692
1849
|
* @param key - Model key, for example `AssetKey.parse("models", "minecraft:item/diamond_sword")`.
|
|
1693
1850
|
*/
|
|
1694
|
-
static async getMerged(key) {
|
|
1851
|
+
static async getMerged(key, context = {}) {
|
|
1695
1852
|
if (!key.assetType) {
|
|
1696
1853
|
key.assetType = "models";
|
|
1697
1854
|
}
|
|
1698
1855
|
if (!key.extension) {
|
|
1699
1856
|
key.extension = ".json";
|
|
1700
1857
|
}
|
|
1701
|
-
const
|
|
1858
|
+
const preview = this.snapshotContext(key, context);
|
|
1859
|
+
const keyStr = key.serialize() + (key.type === "item" ? this.contextKey(preview) : "");
|
|
1702
1860
|
return Caching.mergedModelCache.get(keyStr, (k) => {
|
|
1703
|
-
return _Models.loadAndMerge(key);
|
|
1861
|
+
return _Models.loadAndMerge(key, preview);
|
|
1704
1862
|
});
|
|
1705
1863
|
}
|
|
1706
1864
|
/** Alias for {@link getMerged}. */
|
|
1707
|
-
static async get(key) {
|
|
1708
|
-
return this.getMerged(key);
|
|
1865
|
+
static async get(key, context = {}) {
|
|
1866
|
+
return this.getMerged(key, context);
|
|
1709
1867
|
}
|
|
1710
1868
|
/** Clears persisted models. Use {@link Caching.clear} to also discard in-memory assets. */
|
|
1711
1869
|
static async clearCache() {
|
|
@@ -5576,9 +5734,10 @@ var init_ModelObject = __esm({
|
|
|
5576
5734
|
this.originalModel = originalModel;
|
|
5577
5735
|
this.isModelObject = true;
|
|
5578
5736
|
this.specialMaterials = /* @__PURE__ */ new Map();
|
|
5737
|
+
this.geometries = /* @__PURE__ */ new Set();
|
|
5579
5738
|
this.meshesCreated = false;
|
|
5580
5739
|
this.options = (0, import_ts_deepmerge5.default)({}, _ModelObject.DEFAULT_OPTIONS, options ?? {});
|
|
5581
|
-
if (originalModel.special) this.options.instanceMeshes = false;
|
|
5740
|
+
if (originalModel.special || originalModel.parts) this.options.instanceMeshes = false;
|
|
5582
5741
|
if (this.options.tints) this.options.tints = { ...this.options.tints };
|
|
5583
5742
|
this.addEventListener("added", () => this.updateAnimationSubscription());
|
|
5584
5743
|
this.addEventListener("removed", () => this.updateAnimationSubscription());
|
|
@@ -5587,14 +5746,36 @@ var init_ModelObject = __esm({
|
|
|
5587
5746
|
this.DEFAULT_OPTIONS = (0, import_ts_deepmerge5.default)({}, SceneObject.DEFAULT_OPTIONS, {});
|
|
5588
5747
|
}
|
|
5589
5748
|
async init() {
|
|
5749
|
+
const parts = this.originalModel.parts;
|
|
5750
|
+
if (parts) {
|
|
5751
|
+
try {
|
|
5752
|
+
for (const part of parts) {
|
|
5753
|
+
const object3 = new _ModelObject(part, { ...this.options, instanceMeshes: false });
|
|
5754
|
+
this.add(object3);
|
|
5755
|
+
await object3.init();
|
|
5756
|
+
}
|
|
5757
|
+
let order = 0;
|
|
5758
|
+
this.iterateAllMeshes((mesh) => {
|
|
5759
|
+
mesh.renderOrder = order++;
|
|
5760
|
+
for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
|
|
5761
|
+
material.transparent = true;
|
|
5762
|
+
}
|
|
5763
|
+
});
|
|
5764
|
+
} catch (error) {
|
|
5765
|
+
this.disposeAndRemoveAllChildren();
|
|
5766
|
+
throw error;
|
|
5767
|
+
}
|
|
5768
|
+
this.notifyDirty();
|
|
5769
|
+
return;
|
|
5770
|
+
}
|
|
5590
5771
|
this.options.tints = await ItemTints.get(this.originalModel, this.options.tints);
|
|
5591
5772
|
const special = this.originalModel.special;
|
|
5592
5773
|
if (special) {
|
|
5593
|
-
const
|
|
5774
|
+
const parts2 = await SpecialItems.getParts(special, this.originalModel.key?.root);
|
|
5594
5775
|
const transform = this.options.displayPosition ? DisplayTransforms.getMatrix(this.originalModel.display, this.options.displayPosition) : new import_three25.Matrix4();
|
|
5595
5776
|
transform.multiply(new import_three25.Matrix4().makeTranslation(-8, -8, -8));
|
|
5596
5777
|
try {
|
|
5597
|
-
for (const part of
|
|
5778
|
+
for (const part of parts2) {
|
|
5598
5779
|
const object3 = new EntityObject(part.model, { flip: false, wireframe: this.options.wireframe });
|
|
5599
5780
|
object3.matrix.copy(transform).multiply(part.transform);
|
|
5600
5781
|
object3.matrixWorldNeedsUpdate = true;
|
|
@@ -5645,6 +5826,7 @@ var init_ModelObject = __esm({
|
|
|
5645
5826
|
if (this.atlas.model.elements) {
|
|
5646
5827
|
this.atlas.model.elements?.forEach((el) => {
|
|
5647
5828
|
const elGeo = this._getBoxGeometryFromElement(el).clone();
|
|
5829
|
+
this.geometries.add(elGeo);
|
|
5648
5830
|
if (this.options.cullMask) {
|
|
5649
5831
|
const indices = Array.from(elGeo.getIndex().array);
|
|
5650
5832
|
elGeo.setIndex(indices.filter((_, index) => {
|
|
@@ -5698,10 +5880,14 @@ var init_ModelObject = __esm({
|
|
|
5698
5880
|
let combinedGeo;
|
|
5699
5881
|
if (allGeos.length > 0) {
|
|
5700
5882
|
combinedGeo = mergeBufferGeometries(allGeos);
|
|
5883
|
+
for (const geometry of allGeos) {
|
|
5884
|
+
this.geometries.delete(geometry);
|
|
5885
|
+
}
|
|
5701
5886
|
} else {
|
|
5702
5887
|
combinedGeo = new import_three25.BoxGeometry(16, 16, 16);
|
|
5703
5888
|
if (displayTransform) DisplayTransforms.apply(combinedGeo, displayTransform);
|
|
5704
5889
|
}
|
|
5890
|
+
this.geometries.add(combinedGeo);
|
|
5705
5891
|
combinedGeo.computeBoundingBox();
|
|
5706
5892
|
let mesh;
|
|
5707
5893
|
if (this.options.instanceMeshes) {
|
|
@@ -5726,6 +5912,9 @@ var init_ModelObject = __esm({
|
|
|
5726
5912
|
const mat = Materials.createShadedCanvasMaterial(this.atlas.image.canvas, this.atlas.hasTransparency, false, true);
|
|
5727
5913
|
this.atlasMaterial = mat;
|
|
5728
5914
|
this.atlasTexture = mat.uniforms?.map?.value ?? mat.map;
|
|
5915
|
+
if (this.options.displayPosition === "gui" /* GUI */ && this.originalModel.gui_light === "front" /* FRONT */ && mat.uniforms?.SHADE) {
|
|
5916
|
+
mat.uniforms.SHADE.value = false;
|
|
5917
|
+
}
|
|
5729
5918
|
this.iterateAllMeshes((mesh) => {
|
|
5730
5919
|
if (mesh.geometry.hasAttribute("color")) mat.vertexColors = true;
|
|
5731
5920
|
mesh.material = mat;
|
|
@@ -5734,7 +5923,9 @@ var init_ModelObject = __esm({
|
|
|
5734
5923
|
}
|
|
5735
5924
|
}
|
|
5736
5925
|
updateAnimationSubscription() {
|
|
5737
|
-
|
|
5926
|
+
let root = this;
|
|
5927
|
+
while (root.parent && isModelObject(root.parent)) root = root.parent;
|
|
5928
|
+
const active = !!root.parent && (!this.isInstanced || this.instanceCounter > 0);
|
|
5738
5929
|
if (active && this.atlas?.hasAnimation && this.atlasTexture) {
|
|
5739
5930
|
if (!this.unsubscribeAtlas) {
|
|
5740
5931
|
this.atlasTexture.needsUpdate = true;
|
|
@@ -5750,6 +5941,9 @@ var init_ModelObject = __esm({
|
|
|
5750
5941
|
this.unsubscribeAtlas?.();
|
|
5751
5942
|
this.unsubscribeAtlas = void 0;
|
|
5752
5943
|
}
|
|
5944
|
+
for (const child of this.children) {
|
|
5945
|
+
if (isModelObject(child)) child.updateAnimationSubscription();
|
|
5946
|
+
}
|
|
5753
5947
|
}
|
|
5754
5948
|
nextInstance() {
|
|
5755
5949
|
const reference = super.nextInstance();
|
|
@@ -5769,6 +5963,8 @@ var init_ModelObject = __esm({
|
|
|
5769
5963
|
this.atlasMaterial = void 0;
|
|
5770
5964
|
for (const material of this.specialMaterials.values()) material.dispose();
|
|
5771
5965
|
this.specialMaterials.clear();
|
|
5966
|
+
for (const geometry of this.geometries) geometry.dispose();
|
|
5967
|
+
this.geometries.clear();
|
|
5772
5968
|
super.disposeAndRemoveAllChildren();
|
|
5773
5969
|
}
|
|
5774
5970
|
};
|
|
@@ -6060,6 +6256,7 @@ __export(index_node_exports, {
|
|
|
6060
6256
|
Env: () => Env,
|
|
6061
6257
|
FluidModelObject: () => FluidModelObject,
|
|
6062
6258
|
Fonts: () => Fonts,
|
|
6259
|
+
GUI_CONTAINER_LAYOUTS: () => GUI_CONTAINER_LAYOUTS,
|
|
6063
6260
|
Geometries: () => Geometries,
|
|
6064
6261
|
GuiHelper: () => GuiHelper,
|
|
6065
6262
|
GuiLight: () => GuiLight,
|
|
@@ -9454,6 +9651,7 @@ function copyDataTexture(source) {
|
|
|
9454
9651
|
|
|
9455
9652
|
// src/gui/GuiHelper.ts
|
|
9456
9653
|
init_AssetKey();
|
|
9654
|
+
init_ModelTextures();
|
|
9457
9655
|
|
|
9458
9656
|
// src/gui/GuiText.ts
|
|
9459
9657
|
var import_three20 = require("three");
|
|
@@ -9563,7 +9761,128 @@ function createGuiTextGeometry(layout, shadow = true) {
|
|
|
9563
9761
|
}
|
|
9564
9762
|
|
|
9565
9763
|
// src/gui/GuiHelper.ts
|
|
9764
|
+
var GUI_CONTAINER_LAYOUTS = {
|
|
9765
|
+
generic_54: {
|
|
9766
|
+
texture: "minecraft:gui/container/generic_54",
|
|
9767
|
+
crop: [0, 0, 176, 222],
|
|
9768
|
+
slotOrigin: [8, 18],
|
|
9769
|
+
slotOffset: [18, 18],
|
|
9770
|
+
rowSize: 9,
|
|
9771
|
+
slotCount: 54
|
|
9772
|
+
},
|
|
9773
|
+
crafting_table: {
|
|
9774
|
+
texture: "minecraft:gui/container/crafting_table",
|
|
9775
|
+
crop: [0, 0, 176, 166],
|
|
9776
|
+
slotOrigin: [30, 17],
|
|
9777
|
+
slotOffset: [18, 18],
|
|
9778
|
+
rowSize: 3,
|
|
9779
|
+
slotCount: 9,
|
|
9780
|
+
resultPosition: [124, 35]
|
|
9781
|
+
}
|
|
9782
|
+
};
|
|
9566
9783
|
var GuiHelper = class {
|
|
9784
|
+
/**
|
|
9785
|
+
* Creates a 182×5 boss bar using the active resource pack. Finite progress is clamped to 0–1.
|
|
9786
|
+
* The fill crops the source image; explicit crops do not apply `.mcmeta` sprite scaling.
|
|
9787
|
+
*/
|
|
9788
|
+
static async bossBar(options = {}) {
|
|
9789
|
+
const { color: color2 = "pink", progress = 1, position = [0, 0] } = options;
|
|
9790
|
+
this.validatePosition(position);
|
|
9791
|
+
if (!["pink", "blue", "red", "green", "yellow", "purple", "white"].includes(color2)) {
|
|
9792
|
+
throw new Error(`Unsupported boss bar color: ${color2}`);
|
|
9793
|
+
}
|
|
9794
|
+
if (!Number.isFinite(progress)) throw new Error("Boss bar progress must be finite");
|
|
9795
|
+
const value = Math.max(0, Math.min(1, progress));
|
|
9796
|
+
const width = value > 0 ? Math.floor(value * 181) + 1 : 0;
|
|
9797
|
+
const texture = `minecraft:gui/sprites/boss_bar/${color2}`;
|
|
9798
|
+
const layers = [{
|
|
9799
|
+
name: "boss-bar-background",
|
|
9800
|
+
texture: `${texture}_background`,
|
|
9801
|
+
position: [...position],
|
|
9802
|
+
size: [182, 5]
|
|
9803
|
+
}];
|
|
9804
|
+
if (width > 0) {
|
|
9805
|
+
layers.push({
|
|
9806
|
+
...await this.cropTexture(`${texture}_progress`, [0, 0, width, 5], [182, 5], position),
|
|
9807
|
+
name: "boss-bar-progress"
|
|
9808
|
+
});
|
|
9809
|
+
}
|
|
9810
|
+
return layers;
|
|
9811
|
+
}
|
|
9812
|
+
/**
|
|
9813
|
+
* Creates a 192×192 book page with optional arrows and supplied text. Buttons are hidden by default.
|
|
9814
|
+
* Callers choose the page content and button states; this helper does not paginate or handle clicks.
|
|
9815
|
+
*/
|
|
9816
|
+
static async book(options = {}) {
|
|
9817
|
+
const {
|
|
9818
|
+
position = [0, 0],
|
|
9819
|
+
previous = "hidden",
|
|
9820
|
+
next = "hidden",
|
|
9821
|
+
text: text2,
|
|
9822
|
+
color: color2 = 0,
|
|
9823
|
+
shadow = false,
|
|
9824
|
+
maxWidth = 114,
|
|
9825
|
+
lineHeight = 9,
|
|
9826
|
+
...style
|
|
9827
|
+
} = options;
|
|
9828
|
+
this.validatePosition(position);
|
|
9829
|
+
for (const state of [previous, next]) {
|
|
9830
|
+
if (!["hidden", "normal", "hover"].includes(state)) throw new Error(`Unsupported book button state: ${state}`);
|
|
9831
|
+
}
|
|
9832
|
+
const layers = [{
|
|
9833
|
+
...await this.cropTexture("minecraft:gui/book", [0, 0, 192, 192], [256, 256], position),
|
|
9834
|
+
name: "book-background"
|
|
9835
|
+
}];
|
|
9836
|
+
for (const [name, direction, state, x] of [
|
|
9837
|
+
["previous", "backward", previous, 43],
|
|
9838
|
+
["next", "forward", next, 116]
|
|
9839
|
+
]) {
|
|
9840
|
+
if (state === "hidden") continue;
|
|
9841
|
+
layers.push({
|
|
9842
|
+
name: `book-${name}`,
|
|
9843
|
+
texture: `minecraft:gui/sprites/widget/page_${direction}${state === "hover" ? "_highlighted" : ""}`,
|
|
9844
|
+
position: [position[0] + x, position[1] + 157],
|
|
9845
|
+
size: [23, 13]
|
|
9846
|
+
});
|
|
9847
|
+
}
|
|
9848
|
+
if (text2 !== void 0) {
|
|
9849
|
+
layers.push({
|
|
9850
|
+
name: "book-text",
|
|
9851
|
+
text: text2,
|
|
9852
|
+
position: [position[0] + 36, position[1] + 30],
|
|
9853
|
+
color: color2,
|
|
9854
|
+
shadow,
|
|
9855
|
+
maxWidth,
|
|
9856
|
+
lineHeight,
|
|
9857
|
+
...style
|
|
9858
|
+
});
|
|
9859
|
+
}
|
|
9860
|
+
return layers;
|
|
9861
|
+
}
|
|
9862
|
+
/** Creates a container background. Use {@link GUI_CONTAINER_LAYOUTS} with {@link inventorySlot} to place its items. */
|
|
9863
|
+
static async container(type, position = [0, 0]) {
|
|
9864
|
+
this.validatePosition(position);
|
|
9865
|
+
if (type !== "generic_54" && type !== "crafting_table") throw new Error(`Unsupported GUI container: ${type}`);
|
|
9866
|
+
const layout = GUI_CONTAINER_LAYOUTS[type];
|
|
9867
|
+
return [{ ...await this.cropTexture(layout.texture, layout.crop, [256, 256], position), name: "background" }];
|
|
9868
|
+
}
|
|
9869
|
+
static validatePosition(position) {
|
|
9870
|
+
if (!Array.isArray(position) || position.length !== 2 || !position.every(Number.isFinite)) {
|
|
9871
|
+
throw new Error("GUI position must contain two finite coordinates");
|
|
9872
|
+
}
|
|
9873
|
+
}
|
|
9874
|
+
static async cropTexture(texture, crop, referenceSize, position) {
|
|
9875
|
+
const image = await ModelTextures.preload(AssetKey.parse("textures", texture));
|
|
9876
|
+
if (!image) throw new Error(`Could not load GUI texture ${texture}`);
|
|
9877
|
+
const [x, y, width, height] = crop;
|
|
9878
|
+
const scaleX = image.width / referenceSize[0], scaleY = image.height / referenceSize[1];
|
|
9879
|
+
return {
|
|
9880
|
+
texture,
|
|
9881
|
+
crop: [x * scaleX, y * scaleY, width * scaleX, height * scaleY],
|
|
9882
|
+
position: [...position],
|
|
9883
|
+
size: [width, height]
|
|
9884
|
+
};
|
|
9885
|
+
}
|
|
9567
9886
|
/** Sizes a tooltip around supplied text; the first entry is its title. */
|
|
9568
9887
|
static async tooltip(lines, options = {}) {
|
|
9569
9888
|
if (!lines.length) return [];
|
|
@@ -9598,8 +9917,9 @@ var GuiHelper = class {
|
|
|
9598
9917
|
}
|
|
9599
9918
|
/** Creates crafting-table layers for ingredients and the result; stack counts are not drawn. */
|
|
9600
9919
|
static recipe(recipe, options = {}) {
|
|
9920
|
+
const layout = GUI_CONTAINER_LAYOUTS.crafting_table;
|
|
9601
9921
|
const layers = [
|
|
9602
|
-
{ name: "background", texture:
|
|
9922
|
+
{ name: "background", texture: layout.texture, crop: [...layout.crop] }
|
|
9603
9923
|
];
|
|
9604
9924
|
const itemKey = (ingredient) => {
|
|
9605
9925
|
const item = typeof ingredient === "string" && !ingredient.startsWith("#") ? ingredient : typeof ingredient === "object" && "item" in ingredient ? ingredient.item : options.resolveIngredient?.(ingredient);
|
|
@@ -9614,7 +9934,7 @@ var GuiHelper = class {
|
|
|
9614
9934
|
layers.push({
|
|
9615
9935
|
name: `ingredient-${slot}`,
|
|
9616
9936
|
item: itemKey(ingredient),
|
|
9617
|
-
position: this.inventorySlot(slot,
|
|
9937
|
+
position: this.inventorySlot(slot, layout.slotOrigin, layout.slotOffset, layout.rowSize)
|
|
9618
9938
|
});
|
|
9619
9939
|
};
|
|
9620
9940
|
switch (recipe.type) {
|
|
@@ -9649,7 +9969,7 @@ var GuiHelper = class {
|
|
|
9649
9969
|
layers.push({
|
|
9650
9970
|
name: "result",
|
|
9651
9971
|
item: itemKey("id" in recipe.result ? recipe.result.id : recipe.result.item),
|
|
9652
|
-
position: [
|
|
9972
|
+
position: [...layout.resultPosition]
|
|
9653
9973
|
});
|
|
9654
9974
|
return layers;
|
|
9655
9975
|
}
|
|
@@ -9744,7 +10064,6 @@ init_SceneObject();
|
|
|
9744
10064
|
init_Materials();
|
|
9745
10065
|
init_ModelObject();
|
|
9746
10066
|
init_DisplayPosition();
|
|
9747
|
-
init_GuiLight();
|
|
9748
10067
|
var GuiObject = class extends SceneObject {
|
|
9749
10068
|
constructor(textureLayers, options) {
|
|
9750
10069
|
super({ ...options, instanceMeshes: false, mergeMeshes: false });
|
|
@@ -9812,12 +10131,16 @@ var GuiObject = class extends SceneObject {
|
|
|
9812
10131
|
mesh2.geometry.computeBoundingBox();
|
|
9813
10132
|
bounds.union(mesh2.geometry.boundingBox.clone().applyMatrix4(new import_three26.Matrix4().multiplyMatrices(inverse, mesh2.matrixWorld)));
|
|
9814
10133
|
});
|
|
9815
|
-
item.position.z = depth
|
|
9816
|
-
|
|
10134
|
+
item.position.z = depth;
|
|
10135
|
+
if (!bounds.isEmpty()) {
|
|
10136
|
+
item.position.z -= bounds.min.z;
|
|
10137
|
+
depth += bounds.max.z - bounds.min.z;
|
|
10138
|
+
this.bounds.expandByPoint(new import_three26.Vector2(bounds.min.x, -bounds.max.y));
|
|
10139
|
+
this.bounds.expandByPoint(new import_three26.Vector2(bounds.max.x, -bounds.min.y));
|
|
10140
|
+
}
|
|
10141
|
+
depth += 0.01;
|
|
9817
10142
|
this.bounds.expandByPoint(new import_three26.Vector2(x, y));
|
|
9818
10143
|
this.bounds.expandByPoint(new import_three26.Vector2(x + width2, y + height2));
|
|
9819
|
-
this.bounds.expandByPoint(new import_three26.Vector2(bounds.min.x, -bounds.max.y));
|
|
9820
|
-
this.bounds.expandByPoint(new import_three26.Vector2(bounds.max.x, -bounds.min.y));
|
|
9821
10144
|
continue;
|
|
9822
10145
|
}
|
|
9823
10146
|
const key = typeof layer.texture === "string" ? AssetKey.parse("textures", layer.texture) : layer.texture;
|
|
@@ -9857,15 +10180,13 @@ var GuiObject = class extends SceneObject {
|
|
|
9857
10180
|
item.name = `group:${layer.name ?? index}`;
|
|
9858
10181
|
this.add(item);
|
|
9859
10182
|
await item.init();
|
|
9860
|
-
|
|
9861
|
-
|
|
10183
|
+
const meshes = [];
|
|
10184
|
+
item.iterateAllMeshes((mesh) => meshes.push(mesh));
|
|
10185
|
+
meshes.forEach((mesh, partIndex) => {
|
|
9862
10186
|
mesh.name = `mesh:${layer.name ?? index}`;
|
|
9863
|
-
mesh.renderOrder = index;
|
|
10187
|
+
mesh.renderOrder = index + partIndex / (meshes.length + 1) * 0.5;
|
|
9864
10188
|
for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
|
|
9865
10189
|
material.transparent = true;
|
|
9866
|
-
if (model.gui_light === "front" /* FRONT */ && material.uniforms?.SHADE) {
|
|
9867
|
-
material.uniforms.SHADE.value = false;
|
|
9868
|
-
}
|
|
9869
10190
|
}
|
|
9870
10191
|
});
|
|
9871
10192
|
return item;
|
|
@@ -16621,6 +16942,7 @@ var Chunk = class _Chunk {
|
|
|
16621
16942
|
Env,
|
|
16622
16943
|
FluidModelObject,
|
|
16623
16944
|
Fonts,
|
|
16945
|
+
GUI_CONTAINER_LAYOUTS,
|
|
16624
16946
|
Geometries,
|
|
16625
16947
|
GuiHelper,
|
|
16626
16948
|
GuiLight,
|