improvements

This commit is contained in:
2026-09-29 00:10:46 +02:00
parent d04f26465f
commit 8cfdf0ecec
2 changed files with 114 additions and 29 deletions
+80 -29
View File
@@ -1304,9 +1304,7 @@
return width > 0 && height > 0 && width <= 8192 && height <= 8192 ? { width, height } : null;
}
function tryLoadImportedActorTexture(json, geo, modelPath, actorIndex) {
let io;
try { io = desktopFiles(); } catch (_) { return; }
function actorTextureCandidates(json, geo, modelPath) {
const candidateIds = [];
const addId = (id) => { if (importResourceId(id) && !candidateIds.includes(id)) candidateIds.push(id); };
if (typeof json.afw_texture === 'string') addId(json.afw_texture);
@@ -1323,6 +1321,25 @@
addId(`minecraft:textures/entity/${entity}.png`);
}
}
return candidateIds;
}
function importTextureDimensions(bytes, metadata = null) {
let dimensions = pngDimensions(bytes);
if (!dimensions) return null;
const animation = metadata?.animation;
if (animation) {
const width = validUvDimension(animation.width, dimensions.width);
const height = validUvDimension(animation.height, width);
if (width <= dimensions.width && height <= dimensions.height) dimensions = { width, height };
}
return dimensions;
}
function tryLoadImportedActorTexture(json, geo, modelPath, actorIndex) {
let io;
try { io = desktopFiles(); } catch (_) { return; }
const candidateIds = actorTextureCandidates(json, geo, modelPath);
if (!candidateIds.length) return;
const sourcePaths = [];
try { sourcePaths.push(findImportPack(modelPath, io).root); } catch (_) { /* Standalone model. */ }
@@ -1342,12 +1359,7 @@
let dimensions = pngDimensions(bytes);
if (!dimensions) continue;
try {
const animation = propJson(source, `${relative}.mcmeta`)?.animation;
if (animation) {
const width = validUvDimension(animation.width, dimensions.width);
const height = validUvDimension(animation.height, width);
if (width <= dimensions.width && height <= dimensions.height) dimensions = { width, height };
}
dimensions = importTextureDimensions(bytes, propJson(source, `${relative}.mcmeta`));
} catch (_) { /* Keep the PNG dimensions when metadata is invalid. */ }
const edit = { elements: Cube.all.filter((cube) => cubeBelongsToActor(cube, actorIndex)),
textures: Texture.all.slice(), uv_mode: true, actor_uv_edit: true, actor_texture_edit: true };
@@ -5020,22 +5032,53 @@
}
function loadImportTextures(row, packs, io) {
const ids = new Set(Object.values(row.json.afw_bone_textures || {}).filter((value) => typeof value === 'string'));
if (typeof row.json.afw_texture === 'string') ids.add(row.json.afw_texture);
else {
const entity = importResourceId(row.entity);
for (const resource of [`${entity.namespace}:textures/entity/${entity.resource}/${entity.resource}.png`,
`${entity.namespace}:textures/entity/${entity.resource}.png`]) {
if (findImportResource(packs, resource, '', '', io)) { row.defaultTexture = resource; ids.add(resource); break; }
}
}
if (typeof row.json.afw_texture === 'string') row.defaultTexture = row.json.afw_texture;
const candidates = actorTextureCandidates(row.json, row.geo, row.resolved.file);
const ids = new Set([...candidates, ...Object.values(row.json.afw_bone_textures || {})
.filter((value) => typeof value === 'string')]);
row.textures = new Map();
let cachedSources;
let modelPack;
try { modelPack = findImportPack(row.resolved.file, io); } catch (_) { /* Standalone model. */ }
const searchPacks = modelPack && !packs.some((pack) => pack.root === modelPack.root)
? [modelPack, ...packs] : packs;
for (const resource of ids) {
const id = resource.includes(':') ? resource : `minecraft:${resource}`;
const texture = findImportResource(packs, id, '', '', io);
if (texture) row.textures.set(resource, texture);
const texture = findImportResource(searchPacks, id, '', '', io);
if (texture) {
let dimensions = null;
try {
if (io.fs.statSync(texture.file).size <= 4 * 1024 * 1024) {
const bytes = io.fs.readFileSync(texture.file);
let metadata = null;
try { metadata = readImportJson(`${texture.file}.mcmeta`, io); } catch (_) { /* Static texture. */ }
dimensions = importTextureDimensions(bytes, metadata);
}
} catch (_) { /* Let Blockbench report unreadable texture files. */ }
row.textures.set(resource, { ...texture, dimensions });
continue;
}
if (!cachedSources) {
cachedSources = [];
for (const entry of discoverPropSources(io)) {
try { cachedSources.push(openPropSource(entry.file, io)); } catch (_) { /* Try another asset source. */ }
}
}
const parsed = importResourceId(id);
if (!parsed) continue;
const relative = `assets/${parsed.namespace}/${parsed.resource}`;
for (const source of cachedSources) {
let bytes;
try { bytes = source.read(relative); } catch (_) { continue; }
if (!bytes || bytes.length > 4 * 1024 * 1024) continue;
let dimensions = pngDimensions(bytes);
if (!dimensions) continue;
try { dimensions = importTextureDimensions(bytes, propJson(source, `${relative}.mcmeta`)); }
catch (_) { /* Keep the PNG dimensions when metadata is invalid. */ }
row.textures.set(resource, { bytes, relative, dimensions });
break;
}
}
row.defaultTexture = candidates.find((id) => row.textures.has(id));
}
function commitAutomaticImport(plan) {
@@ -5059,22 +5102,30 @@
metadata[index] = plan.definition.actors[offset];
importGeometryIntoCurrentProject(row.geo, index, false, false);
for (const [name, resource] of row.textures) {
if (!loadedTextures.has(resource.file)) {
const texture = new Texture({ name, uv_width: row.geo.description?.texture_width || 64,
uv_height: row.geo.description?.texture_height || 64 });
const key = resource.file || `${resource.relative}:${name}`;
if (!loadedTextures.has(key)) {
const texture = new Texture({ name, uv_width: resource.dimensions?.width || row.geo.description?.texture_width || 64,
uv_height: resource.dimensions?.height || row.geo.description?.texture_height || 64 });
texture.keep_size = true;
texture.load_callback = () => { if (Project?.uuid === projectId) scheduleActorTexturePreviewApply(); };
texture.fromPath(resource.file);
if (resource.file) texture.fromPath(resource.file);
else texture.fromDataURL(`data:image/png;base64,${resource.bytes.toString('base64')}`);
texture.name = name;
if (resource.relative) texture.relative_path = resource.relative;
texture.add(false);
loadedTextures.set(resource.file, name);
loadedTextures.set(key, name);
}
}
if (row.textures.has(row.defaultTexture)) textures[index] = loadedTextures.get(row.textures.get(row.defaultTexture).file);
const base = row.textures.get(row.defaultTexture);
if (base) {
textures[index] = loadedTextures.get(base.file || `${base.relative}:${row.defaultTexture}`);
if (base.dimensions) normalizeActorUvs(index, base.dimensions.width, base.dimensions.height);
}
for (const [bone, textureName] of Object.entries(row.json.afw_bone_textures || {})) {
const group = Group.all.find((candidate) => candidate.name === `actor${index}_${bone}`);
if (group && row.textures.has(textureName)) overrides[group.uuid] = { uuid: group.uuid, name: group.name,
texture: loadedTextures.get(row.textures.get(textureName).file) };
const boneTexture = row.textures.get(textureName);
if (group && boneTexture) overrides[group.uuid] = { uuid: group.uuid, name: group.name,
texture: loadedTextures.get(boneTexture.file || `${boneTexture.relative}:${textureName}`) };
}
}
setProjectActorNames(names);