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.mjs
CHANGED
|
@@ -1527,6 +1527,8 @@ var init_ModelMerger = __esm({
|
|
|
1527
1527
|
ModelMerger = class {
|
|
1528
1528
|
/** Loads the parent chain and returns a merged model without modifying the supplied definition. */
|
|
1529
1529
|
static async mergeWithParents(model) {
|
|
1530
|
+
const parts = model.parts;
|
|
1531
|
+
if (parts) return { ...model, parts: await Promise.all(parts.map((part) => this.mergeWithParents(part))) };
|
|
1530
1532
|
const models = await this.collectAllParents(model);
|
|
1531
1533
|
let merged = {};
|
|
1532
1534
|
for (const source of [...models, model]) {
|
|
@@ -1587,6 +1589,7 @@ var init_Models = __esm({
|
|
|
1587
1589
|
init_PersistentCache();
|
|
1588
1590
|
init_AssetKey();
|
|
1589
1591
|
init_AssetParsers();
|
|
1592
|
+
init_DisplayPosition();
|
|
1590
1593
|
Models = class _Models {
|
|
1591
1594
|
// opened lazily: touching the store at import time would hit IndexedDB/disk just for loading
|
|
1592
1595
|
// the library, and would require an EnvProvider before any entry has had a chance to register
|
|
@@ -1601,34 +1604,174 @@ var init_Models = __esm({
|
|
|
1601
1604
|
return (await AssetLoader.getFirst([key, legacyKey], "list" /* LIST */))?.asset;
|
|
1602
1605
|
}).then((r) => r?.files ?? []);
|
|
1603
1606
|
}
|
|
1604
|
-
static async loadAndMerge(key) {
|
|
1605
|
-
const model = key.type === "item" ? await this.getItemModel(key) : await this.getRaw(key);
|
|
1607
|
+
static async loadAndMerge(key, context = {}) {
|
|
1608
|
+
const model = key.type === "item" ? await this.getItemModel(key, context) : await this.getRaw(key);
|
|
1606
1609
|
if (!model) {
|
|
1607
1610
|
return void 0;
|
|
1608
1611
|
}
|
|
1609
1612
|
return ModelMerger.mergeWithParents(model);
|
|
1610
1613
|
}
|
|
1611
|
-
static async getItemModel(key) {
|
|
1614
|
+
static async getItemModel(key, context = {}) {
|
|
1615
|
+
const preview = this.snapshotContext(key, context);
|
|
1612
1616
|
const itemKey = new AssetKey(key.namespace, key.path, "items", void 0, key.rootType, ".json", key.root);
|
|
1613
|
-
const
|
|
1617
|
+
const cacheKey = itemKey.serialize() + this.contextKey(preview);
|
|
1618
|
+
const model = await this.PERSISTENT_CACHE.getOrLoad(`item-v3:${AssetLoader.persistentKey(cacheKey)}`, async () => {
|
|
1614
1619
|
const result = await AssetLoader.getFirst([itemKey, key], "json" /* JSON */);
|
|
1615
1620
|
if (!result) return void 0;
|
|
1616
1621
|
if (result.key.assetType !== "items") return { ...result.asset, key };
|
|
1617
|
-
const
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1622
|
+
const load = async (selected) => {
|
|
1623
|
+
if ("parts" in selected) {
|
|
1624
|
+
return { key, parts: await Promise.all(selected.parts.map(load)) };
|
|
1625
|
+
}
|
|
1626
|
+
if ("item" in selected) {
|
|
1627
|
+
const model3 = await this.getItemModel(selected.item, { displayContext: preview.displayContext });
|
|
1628
|
+
if (!model3) throw new Error(`Item ${key.toNamespacedString()} references missing item ${selected.item.toNamespacedString()}`);
|
|
1629
|
+
return model3;
|
|
1630
|
+
}
|
|
1631
|
+
const modelKey = AssetKey.parse("models", selected.model);
|
|
1632
|
+
modelKey.root = key.root;
|
|
1633
|
+
const model2 = await this.getRaw(modelKey);
|
|
1634
|
+
if (!model2) throw new Error(`Item ${key.toNamespacedString()} references missing model ${selected.model}`);
|
|
1635
|
+
return { ...model2, ...selected.special && { special: selected.special }, ...selected.tints && { tints: selected.tints } };
|
|
1636
|
+
};
|
|
1637
|
+
return load(this.selectItemModel(result.asset.model, key, preview));
|
|
1638
|
+
});
|
|
1639
|
+
const restore = (model2) => ({
|
|
1640
|
+
...model2,
|
|
1641
|
+
key: Object.assign(new AssetKey("", ""), model2.key),
|
|
1642
|
+
...model2.parts && { parts: model2.parts.map(restore) }
|
|
1623
1643
|
});
|
|
1624
|
-
return model ?
|
|
1644
|
+
return model ? restore(model) : void 0;
|
|
1645
|
+
}
|
|
1646
|
+
static contextIdentifier(id) {
|
|
1647
|
+
if (typeof id !== "string" || !/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(id)) {
|
|
1648
|
+
throw new Error(`Invalid item-preview identifier: ${id}`);
|
|
1649
|
+
}
|
|
1650
|
+
return id.includes(":") ? id : `minecraft:${id}`;
|
|
1651
|
+
}
|
|
1652
|
+
static snapshotContext(key, context) {
|
|
1653
|
+
const displayContext = context.displayContext ?? "gui" /* GUI */;
|
|
1654
|
+
if (displayContext !== "none" && !Object.values(DisplayPosition).includes(displayContext)) {
|
|
1655
|
+
throw new Error(`Unsupported item display context: ${displayContext}`);
|
|
1656
|
+
}
|
|
1657
|
+
for (const map of [context.properties, context.components, context.itemReferences]) {
|
|
1658
|
+
if (map !== void 0 && (!map || typeof map !== "object" || Array.isArray(map))) {
|
|
1659
|
+
throw new Error("Item-preview properties, components, and references must be objects keyed by identifier");
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
const count = context.count === void 0 ? 1 : context.count;
|
|
1663
|
+
if (!Number.isSafeInteger(count) || count < 0) throw new Error("Item-preview count must be a nonnegative safe integer");
|
|
1664
|
+
const properties = {};
|
|
1665
|
+
for (const [id, value] of Object.entries(context.properties ?? {})) {
|
|
1666
|
+
const property = this.contextIdentifier(id);
|
|
1667
|
+
if (!["boolean", "string", "number"].includes(typeof value) || typeof value === "number" && !Number.isFinite(value)) {
|
|
1668
|
+
throw new Error(`Item-preview property ${property} must be a boolean, string, or finite number`);
|
|
1669
|
+
}
|
|
1670
|
+
if (Object.prototype.hasOwnProperty.call(properties, property)) throw new Error(`Duplicate item-preview property: ${property}`);
|
|
1671
|
+
properties[property] = value;
|
|
1672
|
+
}
|
|
1673
|
+
const components = {};
|
|
1674
|
+
for (const [id, value] of Object.entries(context.components ?? {})) {
|
|
1675
|
+
const component = this.contextIdentifier(id);
|
|
1676
|
+
if (Object.prototype.hasOwnProperty.call(components, component)) throw new Error(`Duplicate item-preview component: ${component}`);
|
|
1677
|
+
components[component] = this.snapshotJson(value);
|
|
1678
|
+
}
|
|
1679
|
+
const itemReferences = {};
|
|
1680
|
+
for (const [id, value] of Object.entries(context.itemReferences ?? {})) {
|
|
1681
|
+
const reference = this.contextIdentifier(id);
|
|
1682
|
+
if (!isAssetKey(value) || value.assetType !== "models" || value.type !== "item") {
|
|
1683
|
+
throw new Error(`Item-preview reference ${reference} must be an item-model AssetKey`);
|
|
1684
|
+
}
|
|
1685
|
+
this.contextIdentifier(`${value.namespace}:${value.path}`);
|
|
1686
|
+
if (Object.prototype.hasOwnProperty.call(itemReferences, reference)) throw new Error(`Duplicate item-preview reference: ${reference}`);
|
|
1687
|
+
itemReferences[reference] = Object.assign(new AssetKey("", ""), value, { root: value.root ?? key.root });
|
|
1688
|
+
}
|
|
1689
|
+
return { displayContext, properties, components, count, itemReferences };
|
|
1690
|
+
}
|
|
1691
|
+
static contextKey(context) {
|
|
1692
|
+
const itemReferences = Object.fromEntries(Object.entries(context.itemReferences).map(([id, key]) => [id, key.serialize()]));
|
|
1693
|
+
return `|item-v3:${JSON.stringify(this.snapshotJson({ ...context, itemReferences }))}`;
|
|
1694
|
+
}
|
|
1695
|
+
static snapshotJson(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
1696
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return value;
|
|
1697
|
+
if (typeof value !== "object" || ancestors.has(value) || !Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
|
|
1698
|
+
throw new Error("Item-preview components must contain finite JSON values without circular references");
|
|
1699
|
+
}
|
|
1700
|
+
ancestors.add(value);
|
|
1701
|
+
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)]));
|
|
1702
|
+
ancestors.delete(value);
|
|
1703
|
+
return copy;
|
|
1704
|
+
}
|
|
1705
|
+
static propertyValue(node, context) {
|
|
1706
|
+
const kind = node.type.replace(/^minecraft:/, "");
|
|
1707
|
+
const property = node.property ? this.contextIdentifier(node.property) : void 0;
|
|
1708
|
+
if (kind === "select" && property === "minecraft:display_context") return context.displayContext;
|
|
1709
|
+
if (property && Object.prototype.hasOwnProperty.call(context.properties, property)) return context.properties[property];
|
|
1710
|
+
const fallback = kind === "condition" ? false : kind === "range_dispatch" ? 0 : void 0;
|
|
1711
|
+
const component = (id) => context.components[this.contextIdentifier(id)];
|
|
1712
|
+
if (property === "minecraft:custom_model_data") {
|
|
1713
|
+
const index = node.index ?? 0;
|
|
1714
|
+
if (!Number.isSafeInteger(index) || index < 0) throw new Error("Item-model custom_model_data index must be a nonnegative integer");
|
|
1715
|
+
const data = component("custom_model_data");
|
|
1716
|
+
if (data === void 0) return fallback;
|
|
1717
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) throw new Error("Item-preview custom_model_data must be an object");
|
|
1718
|
+
const field = kind === "condition" ? "flags" : kind === "select" ? "strings" : "floats";
|
|
1719
|
+
const values = data[field];
|
|
1720
|
+
if (values === void 0) return fallback;
|
|
1721
|
+
if (!Array.isArray(values)) throw new Error(`Item-preview custom_model_data.${field} must be an array`);
|
|
1722
|
+
return index < values.length ? values[index] : fallback;
|
|
1723
|
+
}
|
|
1724
|
+
if (kind === "condition" && property === "minecraft:has_component") {
|
|
1725
|
+
if (node.ignore_default) throw new Error("Item-preview has_component with ignore_default requires an explicit properties override; item defaults are unavailable");
|
|
1726
|
+
return Object.prototype.hasOwnProperty.call(context.components, this.contextIdentifier(node.component));
|
|
1727
|
+
}
|
|
1728
|
+
if (kind === "select") {
|
|
1729
|
+
switch (property) {
|
|
1730
|
+
case "minecraft:component":
|
|
1731
|
+
return component(node.component);
|
|
1732
|
+
case "minecraft:block_state": {
|
|
1733
|
+
const state = component("block_state");
|
|
1734
|
+
if (state === void 0) return void 0;
|
|
1735
|
+
if (!state || typeof state !== "object" || Array.isArray(state)) throw new Error("Item-preview block_state must be an object");
|
|
1736
|
+
if (typeof node.block_state_property !== "string") throw new Error("Item-model block_state requires block_state_property");
|
|
1737
|
+
return Object.prototype.hasOwnProperty.call(state, node.block_state_property) ? state[node.block_state_property] : void 0;
|
|
1738
|
+
}
|
|
1739
|
+
case "minecraft:charge_type": {
|
|
1740
|
+
const projectiles = component("charged_projectiles");
|
|
1741
|
+
if (projectiles === void 0) return "none";
|
|
1742
|
+
if (!Array.isArray(projectiles)) throw new Error("Item-preview charged_projectiles must be an array of item stacks");
|
|
1743
|
+
const ids = projectiles.map((stack) => {
|
|
1744
|
+
if (!stack || typeof stack !== "object" || Array.isArray(stack)) throw new Error("Item-preview charged_projectiles must contain item-stack objects");
|
|
1745
|
+
return this.contextIdentifier(stack.id);
|
|
1746
|
+
});
|
|
1747
|
+
return !ids.length ? "none" : ids.includes("minecraft:firework_rocket") ? "rocket" : "arrow";
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
if (kind === "range_dispatch" && (property === "minecraft:damage" || property === "minecraft:count")) {
|
|
1752
|
+
const damage = property === "minecraft:damage";
|
|
1753
|
+
const maximumComponent = component(damage ? "max_damage" : "max_stack_size");
|
|
1754
|
+
const maximum = maximumComponent === void 0 ? damage ? 0 : 1 : maximumComponent;
|
|
1755
|
+
const damageComponent = component("damage");
|
|
1756
|
+
const value = damage ? damageComponent === void 0 ? 0 : damageComponent : context.count;
|
|
1757
|
+
if (typeof value !== "number" || typeof maximum !== "number" || maximum < 0) throw new Error(`Item-preview ${property} requires numeric components and a nonnegative maximum`);
|
|
1758
|
+
const clamped = Math.max(0, Math.min(maximum, value));
|
|
1759
|
+
return node.normalize === false ? clamped : clamped / maximum;
|
|
1760
|
+
}
|
|
1761
|
+
return fallback;
|
|
1625
1762
|
}
|
|
1626
|
-
|
|
1627
|
-
static defaultItemModel(node, key) {
|
|
1763
|
+
static selectItemModel(node, key, context) {
|
|
1628
1764
|
if (!node || typeof node.type !== "string") {
|
|
1629
1765
|
throw new Error(`Unsupported item model definition for ${key.toNamespacedString()}`);
|
|
1630
1766
|
}
|
|
1631
1767
|
switch (node.type.replace(/^minecraft:/, "")) {
|
|
1768
|
+
case "composite":
|
|
1769
|
+
if (Array.isArray(node.models)) return { parts: node.models.map((child) => this.selectItemModel(child, key, context)) };
|
|
1770
|
+
break;
|
|
1771
|
+
case "bundle/selected_item": {
|
|
1772
|
+
const item = context.itemReferences["minecraft:bundle/selected_item"];
|
|
1773
|
+
return item ? { item } : { parts: [] };
|
|
1774
|
+
}
|
|
1632
1775
|
case "model":
|
|
1633
1776
|
if (typeof node.model === "string" && node.model) return { model: node.model, tints: node.tints };
|
|
1634
1777
|
break;
|
|
@@ -1649,17 +1792,29 @@ var init_Models = __esm({
|
|
|
1649
1792
|
}
|
|
1650
1793
|
throw new Error(`Unsupported special item renderer ${special.type} for ${key.toNamespacedString()}`);
|
|
1651
1794
|
}
|
|
1652
|
-
case "condition":
|
|
1653
|
-
|
|
1795
|
+
case "condition": {
|
|
1796
|
+
const value = this.propertyValue(node, context);
|
|
1797
|
+
if (typeof value !== "boolean") throw new Error(`Item-preview condition ${node.property} requires a boolean`);
|
|
1798
|
+
return this.selectItemModel(value ? node.on_true : node.on_false, key, context);
|
|
1799
|
+
}
|
|
1654
1800
|
case "select": {
|
|
1655
|
-
const
|
|
1656
|
-
|
|
1801
|
+
const value = this.propertyValue(node, context);
|
|
1802
|
+
const component = node.property && this.contextIdentifier(node.property) === "minecraft:component";
|
|
1803
|
+
if (!component && value !== void 0 && typeof value !== "string") throw new Error(`Item-preview selector ${node.property} requires a string`);
|
|
1804
|
+
const matches = (candidate) => component ? JSON.stringify(this.snapshotJson(candidate)) === JSON.stringify(this.snapshotJson(value)) : candidate === value;
|
|
1805
|
+
const selected = value === void 0 ? void 0 : node.cases?.find((entry) => matches(entry.when) || Array.isArray(entry.when) && entry.when.some(matches))?.model;
|
|
1806
|
+
return this.selectItemModel(selected ?? node.fallback, key, context);
|
|
1657
1807
|
}
|
|
1658
1808
|
case "range_dispatch": {
|
|
1809
|
+
const value = this.propertyValue(node, context);
|
|
1810
|
+
if (typeof value !== "number") throw new Error(`Item-preview range ${node.property} requires a number`);
|
|
1811
|
+
if (Number.isNaN(value)) return this.selectItemModel(node.fallback, key, context);
|
|
1812
|
+
const scaled = value * (node.scale ?? 1);
|
|
1813
|
+
if (!Number.isFinite(scaled)) throw new Error(`Item-preview range ${node.property} requires a finite value and scale`);
|
|
1659
1814
|
const selected = node.entries?.reduce((match, entry) => {
|
|
1660
|
-
return entry.threshold <=
|
|
1815
|
+
return entry.threshold <= scaled && (!match || entry.threshold >= match.threshold) ? entry : match;
|
|
1661
1816
|
}, void 0);
|
|
1662
|
-
return this.
|
|
1817
|
+
return this.selectItemModel(selected?.model ?? node.fallback, key, context);
|
|
1663
1818
|
}
|
|
1664
1819
|
}
|
|
1665
1820
|
throw new Error(`Unsupported item model ${node?.type ?? "definition"} for ${key.toNamespacedString()}`);
|
|
@@ -1686,25 +1841,28 @@ var init_Models = __esm({
|
|
|
1686
1841
|
}
|
|
1687
1842
|
/**
|
|
1688
1843
|
* Loads a model and resolves its parent chain. Returns `undefined` when the model is missing.
|
|
1689
|
-
* Item keys
|
|
1844
|
+
* Item keys default to GUI context and a stack count of 1. Unresolved conditions are false and numeric properties are zero.
|
|
1845
|
+
* Pass `context` to supply component values, property overrides, and item references for a preview.
|
|
1846
|
+
* Composite items retain independently merged children in `ItemModel.parts`.
|
|
1690
1847
|
*
|
|
1691
1848
|
* @param key - Model key, for example `AssetKey.parse("models", "minecraft:item/diamond_sword")`.
|
|
1692
1849
|
*/
|
|
1693
|
-
static async getMerged(key) {
|
|
1850
|
+
static async getMerged(key, context = {}) {
|
|
1694
1851
|
if (!key.assetType) {
|
|
1695
1852
|
key.assetType = "models";
|
|
1696
1853
|
}
|
|
1697
1854
|
if (!key.extension) {
|
|
1698
1855
|
key.extension = ".json";
|
|
1699
1856
|
}
|
|
1700
|
-
const
|
|
1857
|
+
const preview = this.snapshotContext(key, context);
|
|
1858
|
+
const keyStr = key.serialize() + (key.type === "item" ? this.contextKey(preview) : "");
|
|
1701
1859
|
return Caching.mergedModelCache.get(keyStr, (k) => {
|
|
1702
|
-
return _Models.loadAndMerge(key);
|
|
1860
|
+
return _Models.loadAndMerge(key, preview);
|
|
1703
1861
|
});
|
|
1704
1862
|
}
|
|
1705
1863
|
/** Alias for {@link getMerged}. */
|
|
1706
|
-
static async get(key) {
|
|
1707
|
-
return this.getMerged(key);
|
|
1864
|
+
static async get(key, context = {}) {
|
|
1865
|
+
return this.getMerged(key, context);
|
|
1708
1866
|
}
|
|
1709
1867
|
/** Clears persisted models. Use {@link Caching.clear} to also discard in-memory assets. */
|
|
1710
1868
|
static async clearCache() {
|
|
@@ -5583,9 +5741,10 @@ var init_ModelObject = __esm({
|
|
|
5583
5741
|
this.originalModel = originalModel;
|
|
5584
5742
|
this.isModelObject = true;
|
|
5585
5743
|
this.specialMaterials = /* @__PURE__ */ new Map();
|
|
5744
|
+
this.geometries = /* @__PURE__ */ new Set();
|
|
5586
5745
|
this.meshesCreated = false;
|
|
5587
5746
|
this.options = (0, import_ts_deepmerge5.default)({}, _ModelObject.DEFAULT_OPTIONS, options ?? {});
|
|
5588
|
-
if (originalModel.special) this.options.instanceMeshes = false;
|
|
5747
|
+
if (originalModel.special || originalModel.parts) this.options.instanceMeshes = false;
|
|
5589
5748
|
if (this.options.tints) this.options.tints = { ...this.options.tints };
|
|
5590
5749
|
this.addEventListener("added", () => this.updateAnimationSubscription());
|
|
5591
5750
|
this.addEventListener("removed", () => this.updateAnimationSubscription());
|
|
@@ -5594,14 +5753,36 @@ var init_ModelObject = __esm({
|
|
|
5594
5753
|
this.DEFAULT_OPTIONS = (0, import_ts_deepmerge5.default)({}, SceneObject.DEFAULT_OPTIONS, {});
|
|
5595
5754
|
}
|
|
5596
5755
|
async init() {
|
|
5756
|
+
const parts = this.originalModel.parts;
|
|
5757
|
+
if (parts) {
|
|
5758
|
+
try {
|
|
5759
|
+
for (const part of parts) {
|
|
5760
|
+
const object3 = new _ModelObject(part, { ...this.options, instanceMeshes: false });
|
|
5761
|
+
this.add(object3);
|
|
5762
|
+
await object3.init();
|
|
5763
|
+
}
|
|
5764
|
+
let order = 0;
|
|
5765
|
+
this.iterateAllMeshes((mesh) => {
|
|
5766
|
+
mesh.renderOrder = order++;
|
|
5767
|
+
for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
|
|
5768
|
+
material.transparent = true;
|
|
5769
|
+
}
|
|
5770
|
+
});
|
|
5771
|
+
} catch (error) {
|
|
5772
|
+
this.disposeAndRemoveAllChildren();
|
|
5773
|
+
throw error;
|
|
5774
|
+
}
|
|
5775
|
+
this.notifyDirty();
|
|
5776
|
+
return;
|
|
5777
|
+
}
|
|
5597
5778
|
this.options.tints = await ItemTints.get(this.originalModel, this.options.tints);
|
|
5598
5779
|
const special = this.originalModel.special;
|
|
5599
5780
|
if (special) {
|
|
5600
|
-
const
|
|
5781
|
+
const parts2 = await SpecialItems.getParts(special, this.originalModel.key?.root);
|
|
5601
5782
|
const transform = this.options.displayPosition ? DisplayTransforms.getMatrix(this.originalModel.display, this.options.displayPosition) : new Matrix49();
|
|
5602
5783
|
transform.multiply(new Matrix49().makeTranslation(-8, -8, -8));
|
|
5603
5784
|
try {
|
|
5604
|
-
for (const part of
|
|
5785
|
+
for (const part of parts2) {
|
|
5605
5786
|
const object3 = new EntityObject(part.model, { flip: false, wireframe: this.options.wireframe });
|
|
5606
5787
|
object3.matrix.copy(transform).multiply(part.transform);
|
|
5607
5788
|
object3.matrixWorldNeedsUpdate = true;
|
|
@@ -5652,6 +5833,7 @@ var init_ModelObject = __esm({
|
|
|
5652
5833
|
if (this.atlas.model.elements) {
|
|
5653
5834
|
this.atlas.model.elements?.forEach((el) => {
|
|
5654
5835
|
const elGeo = this._getBoxGeometryFromElement(el).clone();
|
|
5836
|
+
this.geometries.add(elGeo);
|
|
5655
5837
|
if (this.options.cullMask) {
|
|
5656
5838
|
const indices = Array.from(elGeo.getIndex().array);
|
|
5657
5839
|
elGeo.setIndex(indices.filter((_, index) => {
|
|
@@ -5705,10 +5887,14 @@ var init_ModelObject = __esm({
|
|
|
5705
5887
|
let combinedGeo;
|
|
5706
5888
|
if (allGeos.length > 0) {
|
|
5707
5889
|
combinedGeo = mergeBufferGeometries(allGeos);
|
|
5890
|
+
for (const geometry of allGeos) {
|
|
5891
|
+
this.geometries.delete(geometry);
|
|
5892
|
+
}
|
|
5708
5893
|
} else {
|
|
5709
5894
|
combinedGeo = new BoxGeometry5(16, 16, 16);
|
|
5710
5895
|
if (displayTransform) DisplayTransforms.apply(combinedGeo, displayTransform);
|
|
5711
5896
|
}
|
|
5897
|
+
this.geometries.add(combinedGeo);
|
|
5712
5898
|
combinedGeo.computeBoundingBox();
|
|
5713
5899
|
let mesh;
|
|
5714
5900
|
if (this.options.instanceMeshes) {
|
|
@@ -5733,6 +5919,9 @@ var init_ModelObject = __esm({
|
|
|
5733
5919
|
const mat = Materials.createShadedCanvasMaterial(this.atlas.image.canvas, this.atlas.hasTransparency, false, true);
|
|
5734
5920
|
this.atlasMaterial = mat;
|
|
5735
5921
|
this.atlasTexture = mat.uniforms?.map?.value ?? mat.map;
|
|
5922
|
+
if (this.options.displayPosition === "gui" /* GUI */ && this.originalModel.gui_light === "front" /* FRONT */ && mat.uniforms?.SHADE) {
|
|
5923
|
+
mat.uniforms.SHADE.value = false;
|
|
5924
|
+
}
|
|
5736
5925
|
this.iterateAllMeshes((mesh) => {
|
|
5737
5926
|
if (mesh.geometry.hasAttribute("color")) mat.vertexColors = true;
|
|
5738
5927
|
mesh.material = mat;
|
|
@@ -5741,7 +5930,9 @@ var init_ModelObject = __esm({
|
|
|
5741
5930
|
}
|
|
5742
5931
|
}
|
|
5743
5932
|
updateAnimationSubscription() {
|
|
5744
|
-
|
|
5933
|
+
let root = this;
|
|
5934
|
+
while (root.parent && isModelObject(root.parent)) root = root.parent;
|
|
5935
|
+
const active = !!root.parent && (!this.isInstanced || this.instanceCounter > 0);
|
|
5745
5936
|
if (active && this.atlas?.hasAnimation && this.atlasTexture) {
|
|
5746
5937
|
if (!this.unsubscribeAtlas) {
|
|
5747
5938
|
this.atlasTexture.needsUpdate = true;
|
|
@@ -5757,6 +5948,9 @@ var init_ModelObject = __esm({
|
|
|
5757
5948
|
this.unsubscribeAtlas?.();
|
|
5758
5949
|
this.unsubscribeAtlas = void 0;
|
|
5759
5950
|
}
|
|
5951
|
+
for (const child of this.children) {
|
|
5952
|
+
if (isModelObject(child)) child.updateAnimationSubscription();
|
|
5953
|
+
}
|
|
5760
5954
|
}
|
|
5761
5955
|
nextInstance() {
|
|
5762
5956
|
const reference = super.nextInstance();
|
|
@@ -5776,6 +5970,8 @@ var init_ModelObject = __esm({
|
|
|
5776
5970
|
this.atlasMaterial = void 0;
|
|
5777
5971
|
for (const material of this.specialMaterials.values()) material.dispose();
|
|
5778
5972
|
this.specialMaterials.clear();
|
|
5973
|
+
for (const geometry of this.geometries) geometry.dispose();
|
|
5974
|
+
this.geometries.clear();
|
|
5779
5975
|
super.disposeAndRemoveAllChildren();
|
|
5780
5976
|
}
|
|
5781
5977
|
};
|
|
@@ -9317,6 +9513,7 @@ function copyDataTexture(source) {
|
|
|
9317
9513
|
|
|
9318
9514
|
// src/gui/GuiHelper.ts
|
|
9319
9515
|
init_AssetKey();
|
|
9516
|
+
init_ModelTextures();
|
|
9320
9517
|
|
|
9321
9518
|
// src/gui/GuiText.ts
|
|
9322
9519
|
import { BufferGeometry as BufferGeometry2, Color as Color8, Float32BufferAttribute as Float32BufferAttribute4 } from "three";
|
|
@@ -9426,7 +9623,128 @@ function createGuiTextGeometry(layout, shadow = true) {
|
|
|
9426
9623
|
}
|
|
9427
9624
|
|
|
9428
9625
|
// src/gui/GuiHelper.ts
|
|
9626
|
+
var GUI_CONTAINER_LAYOUTS = {
|
|
9627
|
+
generic_54: {
|
|
9628
|
+
texture: "minecraft:gui/container/generic_54",
|
|
9629
|
+
crop: [0, 0, 176, 222],
|
|
9630
|
+
slotOrigin: [8, 18],
|
|
9631
|
+
slotOffset: [18, 18],
|
|
9632
|
+
rowSize: 9,
|
|
9633
|
+
slotCount: 54
|
|
9634
|
+
},
|
|
9635
|
+
crafting_table: {
|
|
9636
|
+
texture: "minecraft:gui/container/crafting_table",
|
|
9637
|
+
crop: [0, 0, 176, 166],
|
|
9638
|
+
slotOrigin: [30, 17],
|
|
9639
|
+
slotOffset: [18, 18],
|
|
9640
|
+
rowSize: 3,
|
|
9641
|
+
slotCount: 9,
|
|
9642
|
+
resultPosition: [124, 35]
|
|
9643
|
+
}
|
|
9644
|
+
};
|
|
9429
9645
|
var GuiHelper = class {
|
|
9646
|
+
/**
|
|
9647
|
+
* Creates a 182×5 boss bar using the active resource pack. Finite progress is clamped to 0–1.
|
|
9648
|
+
* The fill crops the source image; explicit crops do not apply `.mcmeta` sprite scaling.
|
|
9649
|
+
*/
|
|
9650
|
+
static async bossBar(options = {}) {
|
|
9651
|
+
const { color: color2 = "pink", progress = 1, position = [0, 0] } = options;
|
|
9652
|
+
this.validatePosition(position);
|
|
9653
|
+
if (!["pink", "blue", "red", "green", "yellow", "purple", "white"].includes(color2)) {
|
|
9654
|
+
throw new Error(`Unsupported boss bar color: ${color2}`);
|
|
9655
|
+
}
|
|
9656
|
+
if (!Number.isFinite(progress)) throw new Error("Boss bar progress must be finite");
|
|
9657
|
+
const value = Math.max(0, Math.min(1, progress));
|
|
9658
|
+
const width = value > 0 ? Math.floor(value * 181) + 1 : 0;
|
|
9659
|
+
const texture = `minecraft:gui/sprites/boss_bar/${color2}`;
|
|
9660
|
+
const layers = [{
|
|
9661
|
+
name: "boss-bar-background",
|
|
9662
|
+
texture: `${texture}_background`,
|
|
9663
|
+
position: [...position],
|
|
9664
|
+
size: [182, 5]
|
|
9665
|
+
}];
|
|
9666
|
+
if (width > 0) {
|
|
9667
|
+
layers.push({
|
|
9668
|
+
...await this.cropTexture(`${texture}_progress`, [0, 0, width, 5], [182, 5], position),
|
|
9669
|
+
name: "boss-bar-progress"
|
|
9670
|
+
});
|
|
9671
|
+
}
|
|
9672
|
+
return layers;
|
|
9673
|
+
}
|
|
9674
|
+
/**
|
|
9675
|
+
* Creates a 192×192 book page with optional arrows and supplied text. Buttons are hidden by default.
|
|
9676
|
+
* Callers choose the page content and button states; this helper does not paginate or handle clicks.
|
|
9677
|
+
*/
|
|
9678
|
+
static async book(options = {}) {
|
|
9679
|
+
const {
|
|
9680
|
+
position = [0, 0],
|
|
9681
|
+
previous = "hidden",
|
|
9682
|
+
next = "hidden",
|
|
9683
|
+
text: text2,
|
|
9684
|
+
color: color2 = 0,
|
|
9685
|
+
shadow = false,
|
|
9686
|
+
maxWidth = 114,
|
|
9687
|
+
lineHeight = 9,
|
|
9688
|
+
...style
|
|
9689
|
+
} = options;
|
|
9690
|
+
this.validatePosition(position);
|
|
9691
|
+
for (const state of [previous, next]) {
|
|
9692
|
+
if (!["hidden", "normal", "hover"].includes(state)) throw new Error(`Unsupported book button state: ${state}`);
|
|
9693
|
+
}
|
|
9694
|
+
const layers = [{
|
|
9695
|
+
...await this.cropTexture("minecraft:gui/book", [0, 0, 192, 192], [256, 256], position),
|
|
9696
|
+
name: "book-background"
|
|
9697
|
+
}];
|
|
9698
|
+
for (const [name, direction, state, x] of [
|
|
9699
|
+
["previous", "backward", previous, 43],
|
|
9700
|
+
["next", "forward", next, 116]
|
|
9701
|
+
]) {
|
|
9702
|
+
if (state === "hidden") continue;
|
|
9703
|
+
layers.push({
|
|
9704
|
+
name: `book-${name}`,
|
|
9705
|
+
texture: `minecraft:gui/sprites/widget/page_${direction}${state === "hover" ? "_highlighted" : ""}`,
|
|
9706
|
+
position: [position[0] + x, position[1] + 157],
|
|
9707
|
+
size: [23, 13]
|
|
9708
|
+
});
|
|
9709
|
+
}
|
|
9710
|
+
if (text2 !== void 0) {
|
|
9711
|
+
layers.push({
|
|
9712
|
+
name: "book-text",
|
|
9713
|
+
text: text2,
|
|
9714
|
+
position: [position[0] + 36, position[1] + 30],
|
|
9715
|
+
color: color2,
|
|
9716
|
+
shadow,
|
|
9717
|
+
maxWidth,
|
|
9718
|
+
lineHeight,
|
|
9719
|
+
...style
|
|
9720
|
+
});
|
|
9721
|
+
}
|
|
9722
|
+
return layers;
|
|
9723
|
+
}
|
|
9724
|
+
/** Creates a container background. Use {@link GUI_CONTAINER_LAYOUTS} with {@link inventorySlot} to place its items. */
|
|
9725
|
+
static async container(type, position = [0, 0]) {
|
|
9726
|
+
this.validatePosition(position);
|
|
9727
|
+
if (type !== "generic_54" && type !== "crafting_table") throw new Error(`Unsupported GUI container: ${type}`);
|
|
9728
|
+
const layout = GUI_CONTAINER_LAYOUTS[type];
|
|
9729
|
+
return [{ ...await this.cropTexture(layout.texture, layout.crop, [256, 256], position), name: "background" }];
|
|
9730
|
+
}
|
|
9731
|
+
static validatePosition(position) {
|
|
9732
|
+
if (!Array.isArray(position) || position.length !== 2 || !position.every(Number.isFinite)) {
|
|
9733
|
+
throw new Error("GUI position must contain two finite coordinates");
|
|
9734
|
+
}
|
|
9735
|
+
}
|
|
9736
|
+
static async cropTexture(texture, crop, referenceSize, position) {
|
|
9737
|
+
const image = await ModelTextures.preload(AssetKey.parse("textures", texture));
|
|
9738
|
+
if (!image) throw new Error(`Could not load GUI texture ${texture}`);
|
|
9739
|
+
const [x, y, width, height] = crop;
|
|
9740
|
+
const scaleX = image.width / referenceSize[0], scaleY = image.height / referenceSize[1];
|
|
9741
|
+
return {
|
|
9742
|
+
texture,
|
|
9743
|
+
crop: [x * scaleX, y * scaleY, width * scaleX, height * scaleY],
|
|
9744
|
+
position: [...position],
|
|
9745
|
+
size: [width, height]
|
|
9746
|
+
};
|
|
9747
|
+
}
|
|
9430
9748
|
/** Sizes a tooltip around supplied text; the first entry is its title. */
|
|
9431
9749
|
static async tooltip(lines, options = {}) {
|
|
9432
9750
|
if (!lines.length) return [];
|
|
@@ -9461,8 +9779,9 @@ var GuiHelper = class {
|
|
|
9461
9779
|
}
|
|
9462
9780
|
/** Creates crafting-table layers for ingredients and the result; stack counts are not drawn. */
|
|
9463
9781
|
static recipe(recipe, options = {}) {
|
|
9782
|
+
const layout = GUI_CONTAINER_LAYOUTS.crafting_table;
|
|
9464
9783
|
const layers = [
|
|
9465
|
-
{ name: "background", texture:
|
|
9784
|
+
{ name: "background", texture: layout.texture, crop: [...layout.crop] }
|
|
9466
9785
|
];
|
|
9467
9786
|
const itemKey = (ingredient) => {
|
|
9468
9787
|
const item = typeof ingredient === "string" && !ingredient.startsWith("#") ? ingredient : typeof ingredient === "object" && "item" in ingredient ? ingredient.item : options.resolveIngredient?.(ingredient);
|
|
@@ -9477,7 +9796,7 @@ var GuiHelper = class {
|
|
|
9477
9796
|
layers.push({
|
|
9478
9797
|
name: `ingredient-${slot}`,
|
|
9479
9798
|
item: itemKey(ingredient),
|
|
9480
|
-
position: this.inventorySlot(slot,
|
|
9799
|
+
position: this.inventorySlot(slot, layout.slotOrigin, layout.slotOffset, layout.rowSize)
|
|
9481
9800
|
});
|
|
9482
9801
|
};
|
|
9483
9802
|
switch (recipe.type) {
|
|
@@ -9512,7 +9831,7 @@ var GuiHelper = class {
|
|
|
9512
9831
|
layers.push({
|
|
9513
9832
|
name: "result",
|
|
9514
9833
|
item: itemKey("id" in recipe.result ? recipe.result.id : recipe.result.item),
|
|
9515
|
-
position: [
|
|
9834
|
+
position: [...layout.resultPosition]
|
|
9516
9835
|
});
|
|
9517
9836
|
return layers;
|
|
9518
9837
|
}
|
|
@@ -9606,7 +9925,6 @@ init_SceneObject();
|
|
|
9606
9925
|
init_Materials();
|
|
9607
9926
|
init_ModelObject();
|
|
9608
9927
|
init_DisplayPosition();
|
|
9609
|
-
init_GuiLight();
|
|
9610
9928
|
import { Box2, Box3 as Box32, DoubleSide as DoubleSide3, Group, Matrix4 as Matrix410, Mesh as Mesh6, MeshBasicMaterial as MeshBasicMaterial5, Vector2 as Vector23 } from "three";
|
|
9611
9929
|
var GuiObject = class extends SceneObject {
|
|
9612
9930
|
constructor(textureLayers, options) {
|
|
@@ -9675,12 +9993,16 @@ var GuiObject = class extends SceneObject {
|
|
|
9675
9993
|
mesh2.geometry.computeBoundingBox();
|
|
9676
9994
|
bounds.union(mesh2.geometry.boundingBox.clone().applyMatrix4(new Matrix410().multiplyMatrices(inverse, mesh2.matrixWorld)));
|
|
9677
9995
|
});
|
|
9678
|
-
item.position.z = depth
|
|
9679
|
-
|
|
9996
|
+
item.position.z = depth;
|
|
9997
|
+
if (!bounds.isEmpty()) {
|
|
9998
|
+
item.position.z -= bounds.min.z;
|
|
9999
|
+
depth += bounds.max.z - bounds.min.z;
|
|
10000
|
+
this.bounds.expandByPoint(new Vector23(bounds.min.x, -bounds.max.y));
|
|
10001
|
+
this.bounds.expandByPoint(new Vector23(bounds.max.x, -bounds.min.y));
|
|
10002
|
+
}
|
|
10003
|
+
depth += 0.01;
|
|
9680
10004
|
this.bounds.expandByPoint(new Vector23(x, y));
|
|
9681
10005
|
this.bounds.expandByPoint(new Vector23(x + width2, y + height2));
|
|
9682
|
-
this.bounds.expandByPoint(new Vector23(bounds.min.x, -bounds.max.y));
|
|
9683
|
-
this.bounds.expandByPoint(new Vector23(bounds.max.x, -bounds.min.y));
|
|
9684
10006
|
continue;
|
|
9685
10007
|
}
|
|
9686
10008
|
const key = typeof layer.texture === "string" ? AssetKey.parse("textures", layer.texture) : layer.texture;
|
|
@@ -9720,15 +10042,13 @@ var GuiObject = class extends SceneObject {
|
|
|
9720
10042
|
item.name = `group:${layer.name ?? index}`;
|
|
9721
10043
|
this.add(item);
|
|
9722
10044
|
await item.init();
|
|
9723
|
-
|
|
9724
|
-
|
|
10045
|
+
const meshes = [];
|
|
10046
|
+
item.iterateAllMeshes((mesh) => meshes.push(mesh));
|
|
10047
|
+
meshes.forEach((mesh, partIndex) => {
|
|
9725
10048
|
mesh.name = `mesh:${layer.name ?? index}`;
|
|
9726
|
-
mesh.renderOrder = index;
|
|
10049
|
+
mesh.renderOrder = index + partIndex / (meshes.length + 1) * 0.5;
|
|
9727
10050
|
for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
|
|
9728
10051
|
material.transparent = true;
|
|
9729
|
-
if (model.gui_light === "front" /* FRONT */ && material.uniforms?.SHADE) {
|
|
9730
|
-
material.uniforms.SHADE.value = false;
|
|
9731
|
-
}
|
|
9732
10052
|
}
|
|
9733
10053
|
});
|
|
9734
10054
|
return item;
|
|
@@ -16491,6 +16811,7 @@ export {
|
|
|
16491
16811
|
Env,
|
|
16492
16812
|
FluidModelObject,
|
|
16493
16813
|
Fonts,
|
|
16814
|
+
GUI_CONTAINER_LAYOUTS,
|
|
16494
16815
|
Geometries,
|
|
16495
16816
|
GuiHelper,
|
|
16496
16817
|
GuiLight,
|