@minecraft/creator-tools
Version:
Minecraft Creator Tools command line and libraries.
302 lines (301 loc) • 12.6 kB
JavaScript
;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const ModelGeometryDefinition_1 = __importDefault(require("./ModelGeometryDefinition"));
const EntityTypeResourceDefinition_1 = __importDefault(require("./EntityTypeResourceDefinition"));
const VanillaProjectManager_1 = __importDefault(require("./VanillaProjectManager"));
const ProjectItemUtilities_1 = __importDefault(require("../app/ProjectItemUtilities"));
const IProjectItemData_1 = require("../app/IProjectItemData");
const Log_1 = __importDefault(require("../core/Log"));
class EntityTextureResolver {
/**
* Resolve all assets (geometry + texture) for an entity.
* This is the main entry point for asset resolution.
*/
static async resolveEntityAssets(options) {
const result = {
source: "none",
warnings: [],
};
// If direct data is provided, use it
if (options.geometry) {
result.geometry = options.geometry;
result.source = "data";
}
if (options.modelDefinition) {
result.modelDefinition = options.modelDefinition;
if (!result.geometry && options.modelDefinition.defaultGeometry) {
result.geometry = options.modelDefinition.defaultGeometry;
}
result.source = "data";
}
if (options.textureData) {
result.textureData = options.textureData;
result.source = "data";
}
if (options.textureUrl) {
result.textureUrl = options.textureUrl;
result.source = result.source === "none" ? "url" : result.source;
}
// If we have all we need from direct data, extract dimensions and return
if (result.geometry && (result.textureData || result.textureUrl)) {
this._extractTextureDimensions(result);
return result;
}
// Try project lookup if entityTypeId is provided and project is available
if (options.entityTypeId && options.project && !options.skipVanilla) {
const projectAssets = await this._resolveFromProject(options.entityTypeId, options.project, options.geometryId, options.variantKey);
if (projectAssets.geometry) {
result.geometry = projectAssets.geometry;
result.modelDefinition = projectAssets.modelDefinition;
result.geometryId = projectAssets.geometryId;
result.source = "project";
}
if (projectAssets.textureData) {
result.textureData = projectAssets.textureData;
result.texturePath = projectAssets.texturePath;
}
else if (projectAssets.textureUrl) {
result.textureUrl = projectAssets.textureUrl;
result.texturePath = projectAssets.texturePath;
}
}
// Fall back to vanilla lookup if needed
if (options.entityTypeId && !options.skipVanilla && (!result.geometry || !result.textureData)) {
const vanillaAssets = await this._resolveFromVanilla(options.entityTypeId, options.geometryId, options.variantKey);
if (!result.geometry && vanillaAssets.geometry) {
result.geometry = vanillaAssets.geometry;
result.modelDefinition = vanillaAssets.modelDefinition;
result.geometryId = vanillaAssets.geometryId;
result.source = "vanilla";
}
if (!result.textureData && !result.textureUrl) {
if (vanillaAssets.textureData) {
result.textureData = vanillaAssets.textureData;
}
else if (vanillaAssets.textureUrl) {
result.textureUrl = vanillaAssets.textureUrl;
}
result.texturePath = vanillaAssets.texturePath;
}
}
// Extract texture dimensions from geometry
this._extractTextureDimensions(result);
return result;
}
/**
* Resolve texture from a project item.
* Finds the related texture file for a model or entity definition.
*/
static async resolveTextureForProjectItem(projectItem, project) {
const result = {};
// Try to find a cousin texture item
const textureItem = ProjectItemUtilities_1.default.getCousinOfType(projectItem, IProjectItemData_1.ProjectItemType.texture);
if (textureItem) {
if (!textureItem.isContentLoaded) {
await textureItem.loadContent();
}
const textureFile = textureItem.primaryFile;
if (textureFile) {
if (!textureFile.isContentLoaded) {
await textureFile.loadContent();
}
if (textureFile.content instanceof Uint8Array) {
result.textureData = textureFile.content;
result.texturePath = textureItem.projectPath ?? undefined;
}
}
}
return result;
}
/**
* Load texture data from a URL.
*/
static async loadTextureFromUrl(url) {
try {
const response = await fetch(url);
if (response.ok) {
const buffer = await response.arrayBuffer();
return new Uint8Array(buffer);
}
}
catch (error) {
Log_1.default.debug(`Failed to load texture from URL ${url}: ${error}`);
}
return null;
}
/**
* Load texture data from a file.
*/
static async loadTextureFromFile(file) {
try {
if (!file.isContentLoaded) {
await file.loadContent();
}
if (file.content instanceof Uint8Array) {
return file.content;
}
}
catch (error) {
Log_1.default.debug(`Failed to load texture from file: ${error}`);
}
return null;
}
/**
* Canonicalize a texture path by removing extensions and normalizing slashes.
*/
static canonicalizeTexturePath(path) {
// Remove .png, .tga extensions
let normalized = path;
if (normalized.endsWith(".png") || normalized.endsWith(".tga")) {
normalized = normalized.substring(0, normalized.lastIndexOf("."));
}
// Normalize slashes
normalized = normalized.replace(/\\/g, "/");
// Ensure it starts with textures/ if it's a relative path
if (!normalized.startsWith("/") && !normalized.startsWith("textures/")) {
normalized = "textures/" + normalized;
}
return normalized;
}
/**
* Build a URL for a vanilla texture path.
*/
static buildVanillaTextureUrl(texturePath) {
const canonical = this.canonicalizeTexturePath(texturePath);
return `/res/latest/van/serve/resource_pack/${canonical}.png`;
}
/**
* Resolve assets from a project.
*/
static async _resolveFromProject(entityTypeId, project, geometryId, variantKey) {
const result = {};
// Normalize entity ID
const shortId = entityTypeId.replace("minecraft:", "");
// Find entity resource definition in project
const entityItems = project.getItemsByType(IProjectItemData_1.ProjectItemType.entityTypeResource);
for (const item of entityItems) {
if (!item.isContentLoaded) {
await item.loadContent();
}
if (item.primaryFile) {
const etrd = await EntityTypeResourceDefinition_1.default.ensureOnFile(item.primaryFile);
if (etrd && (etrd.id === entityTypeId || etrd.id === `minecraft:${shortId}` || etrd.id === shortId)) {
// Found the entity - get geometry and texture
const matched = etrd.getMatchedGeometryAndTexture(variantKey || "default");
if (matched.geometryId) {
result.geometryId = geometryId || matched.geometryId;
// Find the geometry file in project
const geoItem = await this._findGeometryInProject(project, result.geometryId);
if (geoItem) {
result.geometry = geoItem.geometry;
result.modelDefinition = geoItem.definition;
}
}
if (matched.texturePath) {
result.texturePath = matched.texturePath;
// Find the texture file in project
const textureData = await this._findTextureInProject(project, matched.texturePath);
if (textureData) {
result.textureData = textureData;
}
}
break;
}
}
}
return result;
}
/**
* Find geometry in a project by geometry ID.
*/
static async _findGeometryInProject(project, geometryId) {
const modelItems = project.getItemsByType(IProjectItemData_1.ProjectItemType.modelGeometryJson);
for (const item of modelItems) {
if (!item.isContentLoaded) {
await item.loadContent();
}
if (item.primaryFile) {
const modelDef = await ModelGeometryDefinition_1.default.ensureOnFile(item.primaryFile);
if (modelDef) {
const geometry = modelDef.getById(geometryId);
if (geometry) {
return { geometry, definition: modelDef };
}
}
}
}
return null;
}
/**
* Find texture in a project by texture path.
*/
static async _findTextureInProject(project, texturePath) {
const canonical = this.canonicalizeTexturePath(texturePath);
const textureItems = project.getItemsByType(IProjectItemData_1.ProjectItemType.texture);
for (const item of textureItems) {
if (item.projectPath && item.projectPath.includes(canonical)) {
if (!item.isContentLoaded) {
await item.loadContent();
}
if (item.primaryFile) {
return this.loadTextureFromFile(item.primaryFile);
}
}
}
return null;
}
/**
* Resolve assets from vanilla resources.
*/
static async _resolveFromVanilla(entityTypeId, geometryId, variantKey) {
const modelData = await VanillaProjectManager_1.default.getVanillaEntityModelData(entityTypeId, variantKey || "default");
if (!modelData) {
return {};
}
const result = {
geometry: modelData.geometry,
modelDefinition: modelData.modelDefinition,
geometryId: geometryId || modelData.geometryId,
texturePath: modelData.texturePath,
textureData: modelData.textureData,
textureUrl: modelData.textureUrl,
};
// If geometryId is specified and different from default, try to find it
if (geometryId && geometryId !== modelData.geometryId && modelData.modelDefinition) {
const specificGeometry = modelData.modelDefinition.getById(geometryId);
if (specificGeometry) {
result.geometry = specificGeometry;
result.geometryId = geometryId;
}
}
return result;
}
/**
* Extract texture dimensions from geometry description.
*/
static _extractTextureDimensions(result) {
if (!result.geometry)
return;
const desc = result.geometry.description;
if (desc) {
result.textureWidth = desc.texture_width;
result.textureHeight = desc.texture_height;
}
else {
// Try legacy properties
result.textureWidth = result.geometry.texturewidth;
result.textureHeight = result.geometry.textureheight;
}
// Defaults
if (!result.textureWidth)
result.textureWidth = 64;
if (!result.textureHeight)
result.textureHeight = 64;
}
}
exports.default = EntityTextureResolver;