auto reapply textures when selecting texture

This commit is contained in:
2026-10-01 02:04:46 +02:00
parent 8aa71244b6
commit 9241b35291
3 changed files with 57 additions and 3 deletions
+21 -1
View File
@@ -72,7 +72,7 @@
let importAction, replaceActorAction, importConjoinedAnimationAction, exportConjoinedAction, createAnimationDefinitionAction, actorPreferencesAction, boneTextureOverridesAction, exportGeckoModelAction; let importAction, replaceActorAction, importConjoinedAnimationAction, exportConjoinedAction, createAnimationDefinitionAction, actorPreferencesAction, boneTextureOverridesAction, exportGeckoModelAction;
let actorNamesProjectProperty, actorTexturesProjectProperty, actorUvBasisProjectProperty, boneTextureOverridesProjectProperty, actorAnimdefMetadataProjectProperty, animdefAnimatorsProjectProperty, animdefDefinitionsProjectProperty; let actorNamesProjectProperty, actorTexturesProjectProperty, actorUvBasisProjectProperty, boneTextureOverridesProjectProperty, actorAnimdefMetadataProjectProperty, animdefAnimatorsProjectProperty, animdefDefinitionsProjectProperty;
let actorTexturePreviewProjectListener; let actorTexturePreviewProjectListener;
const ACTOR_TEXTURE_PREVIEW_EVENTS = ['load_project', 'select_project', 'setup_project', 'load_from_recent_project_data', 'add_texture', 'change_texture_path']; const ACTOR_TEXTURE_PREVIEW_EVENTS = ['load_project', 'select_project', 'setup_project', 'load_from_recent_project_data', 'add_texture', 'change_texture_path', 'select_texture'];
let actorTexturePreviewTimeouts = []; let actorTexturePreviewTimeouts = [];
// ---------------------------- // ----------------------------
@@ -1964,6 +1964,23 @@
return true; return true;
} }
function restoreActorTextureMaterial({ element } = {}) {
if (!Project || !element?.mesh || isBlockRequirementGuide(element)) return;
const override = getNearestBoneTextureOverride(element, getProjectBoneTextureOverrides());
let texture = override?.texture ? findTextureByActorTextureName(override.texture) : null;
if (!texture) {
let node = element;
let actorIndex;
while (node && !actorIndex) {
actorIndex = readActorIndexFromName(node.name);
node = node.parent;
}
if (actorIndex) texture = findTextureByActorTextureName(getProjectActorTextures()[actorIndex]);
}
// Only restore the preview material; do not rebuild faces or trigger another update.
if (texture) forceActorTextureMaterial(element, texture);
}
function applyTextureToCube(cube, texture) { function applyTextureToCube(cube, texture) {
if (!cube || !texture) return; if (!cube || !texture) return;
@@ -7221,6 +7238,7 @@ Features without a reference need no additional addon. Whole-animation import an
}); });
actorTexturePreviewProjectListener = scheduleActorTexturePreviewApply; actorTexturePreviewProjectListener = scheduleActorTexturePreviewApply;
Cube.preview_controller.on('setup update_faces', restoreActorTextureMaterial);
for (const eventName of ACTOR_TEXTURE_PREVIEW_EVENTS) { for (const eventName of ACTOR_TEXTURE_PREVIEW_EVENTS) {
Blockbench.on(eventName, actorTexturePreviewProjectListener); Blockbench.on(eventName, actorTexturePreviewProjectListener);
} }
@@ -7250,6 +7268,8 @@ Features without a reference need no additional addon. Whole-animation import an
if (propListener) for (const event of ['select_animation','select_project','load_project','unselect_project','undo','redo','render_frame']) Blockbench.removeListener(event, propListener); if (propListener) for (const event of ['select_animation','select_project','load_project','unselect_project','undo','redo','render_frame']) Blockbench.removeListener(event, propListener);
propDialog = undefined; propListener = undefined; propDialog = undefined; propListener = undefined;
clearScheduledActorTexturePreviewApplies(); clearScheduledActorTexturePreviewApplies();
Cube.preview_controller.removeListener('setup', restoreActorTextureMaterial);
Cube.preview_controller.removeListener('update_faces', restoreActorTextureMaterial);
if (actorTexturePreviewProjectListener) { if (actorTexturePreviewProjectListener) {
for (const eventName of ACTOR_TEXTURE_PREVIEW_EVENTS) { for (const eventName of ACTOR_TEXTURE_PREVIEW_EVENTS) {
Blockbench.removeListener(eventName, actorTexturePreviewProjectListener); Blockbench.removeListener(eventName, actorTexturePreviewProjectListener);
+6 -1
View File
@@ -34,7 +34,12 @@ function harness() {
class Cube { class Cube {
static all = []; static all = [];
static preview_controller = { static preview_controller = {
on(names, listener) { for (const name of names.split(' ')) previewEvents.set(name, listener); }, on(names, listener) {
for (const name of names.split(' ')) {
const previous = previewEvents.get(name);
previewEvents.set(name, (event) => { previous?.(event); listener(event); });
}
},
}; };
constructor(data) { constructor(data) {
Object.assign(this, data, { uuid: `cube-${++sequence}`, mesh: {}, Object.assign(this, data, { uuid: `cube-${++sequence}`, mesh: {},
+30 -1
View File
@@ -8,7 +8,7 @@ const vm = require('node:vm');
function harness() { function harness() {
const source = fs.readFileSync(path.join(__dirname, '..', 'AnimationDirector.js'), 'utf8').replace( const source = fs.readFileSync(path.join(__dirname, '..', 'AnimationDirector.js'), 'utf8').replace(
' Plugin.register(PLUGIN_ID, {', ' Plugin.register(PLUGIN_ID, {',
' globalThis.uvTest = { normalizeActorUvs, expandProjectUvs, scaleCubeUvs, getActorUvBasis, tryLoadImportedActorTexture, applyActorTexturePreview, importGeometryIntoCurrentProject, applyAllActorTexturePreviews, setProjectActorTextures, setProjectBoneTextureOverrides };\n Plugin.register(PLUGIN_ID, {' ' globalThis.uvTest = { normalizeActorUvs, expandProjectUvs, scaleCubeUvs, getActorUvBasis, tryLoadImportedActorTexture, applyActorTexturePreview, importGeometryIntoCurrentProject, applyAllActorTexturePreviews, setProjectActorTextures, setProjectBoneTextureOverrides, restoreActorTextureMaterial };\n Plugin.register(PLUGIN_ID, {'
); );
class Group { class Group {
static all = []; static all = [];
@@ -69,6 +69,35 @@ test('growing the project UV canvas preserves other actors and converts box UVs'
assert.deepEqual(Array.from(actor1.faces.north.uv), [0, 0, 64, 16], 'Repeated refreshes must not rescale UVs'); assert.deepEqual(Array.from(actor1.faces.north.uv), [0, 0, 64, 16], 'Repeated refreshes must not rescale UVs');
}); });
test('preview material rebuild restores actor overrides without modifying faces or UVs', () => {
const h = harness(), cube = new h.Cube('actor1_body');
const material = {}, replacement = {};
new h.Texture({ name: 'actor.png', material }).add();
h.setProjectActorTextures({ 1: 'actor.png' });
cube.mesh = { material: replacement };
const before = JSON.stringify(cube.faces);
h.restoreActorTextureMaterial({ element: cube });
assert.equal(cube.mesh.material, material);
assert.equal(JSON.stringify(cube.faces), before);
const other = new h.Cube('actor2_body');
other.mesh = { material: replacement };
h.restoreActorTextureMaterial({ element: other });
assert.equal(other.mesh.material, replacement);
});
test('folder overrides take priority when restoring preview materials', () => {
const h = harness(), cube = new h.Cube('actor1_body');
const actorMaterial = {}, folderMaterial = {};
new h.Texture({ name: 'actor.png', material: actorMaterial }).add();
new h.Texture({ name: 'folder.png', material: folderMaterial }).add();
cube.parent = new h.context.Group({ name: 'actor1_details', uuid: 'details' });
cube.mesh = { material: {} };
h.setProjectActorTextures({ 1: 'actor.png' });
h.setProjectBoneTextureOverrides({ details: { uuid: 'details', name: 'actor1_details', texture: 'folder.png' } });
h.restoreActorTextureMaterial({ element: cube });
assert.equal(cube.mesh.material, folderMaterial);
});
test('non-GeckoLib projects keep UVs, canvas dimensions and box UV mode while still assigning textures', () => { test('non-GeckoLib projects keep UVs, canvas dimensions and box UV mode while still assigning textures', () => {
for (const format of ['optifine_entity', 'optifine_part', 'bedrock', 'free', 'java_block']) { for (const format of ['optifine_entity', 'optifine_part', 'bedrock', 'free', 'java_block']) {
const h = harness(), cube = new h.Cube('actor1_body', true); const h = harness(), cube = new h.Cube('actor1_body', true);