mirror of
https://github.com/JannisX11/blockbench.git
synced 2024-11-27 04:21:46 +08:00
Merge branch 'texture-groups' into next
This commit is contained in:
commit
b740c56376
@ -47,8 +47,10 @@
|
||||
font-weight: normal;
|
||||
display: inline-block;
|
||||
}
|
||||
.code {
|
||||
code, .code {
|
||||
font-family: var(--font-code);
|
||||
}
|
||||
.code {
|
||||
font-size: 16px;
|
||||
}
|
||||
.small_text {
|
||||
|
@ -471,8 +471,7 @@
|
||||
bottom: 4px;
|
||||
width: 4px;
|
||||
margin-left: 10px;
|
||||
border-left: 2px solid var(--color-text);
|
||||
opacity: 0.2;
|
||||
border-left: 2px solid var(--color-guidelines);
|
||||
pointer-events: none;
|
||||
}
|
||||
.drag_hover[order]::before {
|
||||
@ -639,6 +638,21 @@
|
||||
z-index: 100;
|
||||
border: 2px solid var(--color-accent);
|
||||
box-shadow: 0 0 16px black;
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
.texture_group_drag_helper {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
min-height: 24px;
|
||||
min-width: 120px;
|
||||
padding: 4px;
|
||||
border: 2px solid var(--color-accent);
|
||||
background-color: var(--color-ui);
|
||||
box-shadow: 0 0 16px black;
|
||||
pointer-events: none;
|
||||
}
|
||||
.icon_placeholder {
|
||||
width: 48px;
|
||||
@ -711,6 +725,63 @@
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
.texture_group {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
.texture_group_head {
|
||||
height: 32px;
|
||||
padding: 4px;
|
||||
padding-right: 8px;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
color: var(--color-subtle_text);
|
||||
}
|
||||
.texture_group_head:hover {
|
||||
color: var(--color-text);
|
||||
}
|
||||
.texture_group_head > .icon-open-state {
|
||||
text-align: center;
|
||||
width: 21px;
|
||||
margin-top: 4px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.texture_group_head > label {
|
||||
flex-shrink: 1;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.texture_group_head.folded > label {
|
||||
max-width: calc(60% - 50px);
|
||||
min-width: 30px;
|
||||
}
|
||||
.texture_group_head > .in_list_button {
|
||||
margin-left: auto;
|
||||
}
|
||||
.texture_group_mini_icon_list {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
margin-right: auto;
|
||||
margin-left: 4px;
|
||||
max-width: 36%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.texture_group_mini_icon_list > .texture_mini_icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
background-color: var(--color-ui);
|
||||
flex-shrink: 0;
|
||||
margin-right: -8px;
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
.texture_group_list {
|
||||
margin-left: 14px;
|
||||
padding-left: 6px;
|
||||
border-left: 2px solid var(--color-guidelines);
|
||||
}
|
||||
|
||||
#texture_animation_playback {
|
||||
display: flex;
|
||||
}
|
||||
|
@ -343,6 +343,7 @@
|
||||
--color-checkerboard: #1c2026;
|
||||
|
||||
--color-menu_separator: #b0afba;
|
||||
--color-guidelines: rgba(136, 150, 157, 0.35);
|
||||
|
||||
--color-close: #d62e3f;
|
||||
--color-confirm: #90ee90;
|
||||
|
@ -146,6 +146,7 @@
|
||||
<script src="js/modeling/mirror_modeling.js"></script>
|
||||
<script src="js/texturing/layers.js"></script>
|
||||
<script src="js/texturing/textures.js"></script>
|
||||
<script src="js/texturing/texture_groups.js"></script>
|
||||
<script src="js/texturing/texture_flipbook.js"></script>
|
||||
<script src="js/texturing/uv.js"></script>
|
||||
<script src="js/texturing/painter.js"></script>
|
||||
|
@ -538,8 +538,6 @@ function setupInterface() {
|
||||
reference.select();
|
||||
});
|
||||
|
||||
document.getElementById('texture_list').addEventListener('click', e => unselectTextures());
|
||||
|
||||
$(Panels.timeline.node).mousedown((event) => {
|
||||
setActivePanel('timeline');
|
||||
})
|
||||
|
@ -189,6 +189,11 @@ var codec = new Codec('project', {
|
||||
if (options.absolute_paths == false) delete t.path;
|
||||
model.textures.push(t);
|
||||
})
|
||||
for (let texture_group of TextureGroup.all) {
|
||||
if (!model.texture_groups) model.texture_groups = [];
|
||||
let copy = texture_group.getSaveCopy();
|
||||
model.texture_groups.push(copy);
|
||||
}
|
||||
|
||||
if (Animation.all.length) {
|
||||
model.animations = [];
|
||||
@ -328,6 +333,11 @@ var codec = new Codec('project', {
|
||||
Project.texture_height = model.resolution.height;
|
||||
}
|
||||
|
||||
if (model.texture_groups) {
|
||||
model.texture_groups.forEach(tex_group => {
|
||||
new TextureGroup(tex_group, tex_group.uuid).add(false);
|
||||
})
|
||||
}
|
||||
if (model.textures) {
|
||||
model.textures.forEach(tex => {
|
||||
var tex_copy = new Texture(tex, tex.uuid).add(false);
|
||||
@ -533,6 +543,11 @@ var codec = new Codec('project', {
|
||||
}
|
||||
}
|
||||
|
||||
if (model.texture_groups) {
|
||||
model.texture_groups.forEach(tex_group => {
|
||||
new TextureGroup(tex_group, tex_group.uuid).add(false);
|
||||
})
|
||||
}
|
||||
if (model.textures && (!Format.single_texture || Texture.all.length == 0)) {
|
||||
new_textures.replace(model.textures.map(loadTexture))
|
||||
}
|
||||
|
@ -730,7 +730,6 @@ function calculateVisibleBox() {
|
||||
|
||||
codec.dispatchEvent('parsed', {model: data.object});
|
||||
|
||||
loadTextureDraggable()
|
||||
Canvas.updateAllBones()
|
||||
setProjectTitle()
|
||||
if (isApp && Project.geometry_name) {
|
||||
|
@ -114,7 +114,6 @@ function parseGeometry(data) {
|
||||
|
||||
codec.dispatchEvent('parsed', {model: data.object});
|
||||
|
||||
loadTextureDraggable()
|
||||
Canvas.updateAllBones()
|
||||
setProjectTitle()
|
||||
if (isApp && Project.geometry_name && Project.BedrockEntityManager) {
|
||||
|
@ -149,7 +149,6 @@ const codec = new Codec('skin_model', {
|
||||
if (data.camera_angle) {
|
||||
main_preview.loadAnglePreset(DefaultCameraPresets.find(p => p.id == data.camera_angle))
|
||||
}
|
||||
loadTextureDraggable()
|
||||
Canvas.updateAllBones()
|
||||
Canvas.updateVisibility()
|
||||
setProjectTitle()
|
||||
|
@ -59,6 +59,7 @@ class ModelProject {
|
||||
this.mesh_selection = {};
|
||||
this.textures = [];
|
||||
this.selected_texture = null;
|
||||
this.texture_groups = [];
|
||||
this.outliner = [];
|
||||
this.animations = [];
|
||||
this.animation_controllers = [];
|
||||
@ -207,6 +208,7 @@ class ModelProject {
|
||||
BarItems.edit_mode_uv_overlay.updateEnabledState();
|
||||
|
||||
Panels.textures.inside_vue.textures = Texture.all;
|
||||
Panels.textures.inside_vue.texture_groups = TextureGroup.all;
|
||||
Panels.layers.inside_vue.layers = Texture.selected ? Texture.selected.layers : [];
|
||||
scene.add(this.model_3d);
|
||||
|
||||
@ -279,8 +281,6 @@ class ModelProject {
|
||||
updateProjectResolution();
|
||||
Validator.validate();
|
||||
Vue.nextTick(() => {
|
||||
loadTextureDraggable();
|
||||
|
||||
if (this.on_next_upen instanceof Array) {
|
||||
this.on_next_upen.forEach(callback => callback());
|
||||
delete this.on_next_upen;
|
||||
@ -607,6 +607,7 @@ function selectNoProject() {
|
||||
UVEditor.vue.all_elements = [];
|
||||
|
||||
Interface.Panels.textures.inside_vue.textures = [];
|
||||
Interface.Panels.textures.inside_vue.texture_groups = [];
|
||||
|
||||
Panels.animations.inside_vue.animations = [];
|
||||
Panels.animations.inside_vue.animation_controllers = [];
|
||||
|
@ -395,10 +395,6 @@ const TickUpdates = {
|
||||
delete TickUpdates.UVEditor;
|
||||
UVEditor.loadData()
|
||||
}
|
||||
if (TickUpdates.texture_list) {
|
||||
delete TickUpdates.texture_list;
|
||||
loadTextureDraggable();
|
||||
}
|
||||
if (TickUpdates.keyframe_selection) {
|
||||
delete TickUpdates.keyframe_selection;
|
||||
Vue.nextTick(updateKeyframeSelection)
|
||||
|
@ -1433,7 +1433,8 @@ Interface.definePanels(function() {
|
||||
depth: Number
|
||||
},
|
||||
data() {return {
|
||||
outliner_colors: settings.outliner_colors
|
||||
outliner_colors: settings.outliner_colors,
|
||||
markerColors
|
||||
}},
|
||||
computed: {
|
||||
indentation() {
|
||||
|
135
js/texturing/texture_groups.js
Normal file
135
js/texturing/texture_groups.js
Normal file
@ -0,0 +1,135 @@
|
||||
|
||||
class TextureGroup {
|
||||
constructor(data, uuid) {
|
||||
this.uuid = uuid ?? guid();
|
||||
this.folded = false;
|
||||
if (data) this.extend(data);
|
||||
}
|
||||
extend(data) {
|
||||
for (let key in TextureGroup.properties) {
|
||||
TextureGroup.properties[key].merge(this, data)
|
||||
}
|
||||
return this;
|
||||
}
|
||||
add() {
|
||||
TextureGroup.all.push(this);
|
||||
return this;
|
||||
}
|
||||
select() {
|
||||
let textures = this.getTextures();
|
||||
if (textures[0]) textures[0].select();
|
||||
for (let texture of textures) {
|
||||
if (!texture.selected) texture.multi_selected = true;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
remove() {
|
||||
TextureGroup.all.remove(this);
|
||||
}
|
||||
showContextMenu(event) {
|
||||
Prop.active_panel = 'textures';
|
||||
TextureGroup.active_menu_group = this;
|
||||
this.menu.open(event, this);
|
||||
}
|
||||
rename() {
|
||||
Blockbench.textPrompt('generic.rename', this.name, (name) => {
|
||||
if (name && name !== this.name) {
|
||||
Undo.initEdit({texture_groups: [this]});
|
||||
this.name = name;
|
||||
Undo.finishEdit('Rename texture group');
|
||||
}
|
||||
})
|
||||
return this;
|
||||
}
|
||||
getTextures() {
|
||||
return Texture.all.filter(texture => texture.group == this.uuid);
|
||||
}
|
||||
getUndoCopy() {
|
||||
let copy = {
|
||||
uuid: this.uuid,
|
||||
index: TextureGroup.all.indexOf(this)
|
||||
};
|
||||
for (let key in TextureGroup.properties) {
|
||||
TextureGroup.properties[key].copy(this, copy)
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
getSaveCopy() {
|
||||
let copy = {
|
||||
uuid: this.uuid
|
||||
};
|
||||
for (let key in TextureGroup.properties) {
|
||||
TextureGroup.properties[key].copy(this, copy)
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
Object.defineProperty(TextureGroup, 'all', {
|
||||
get() {
|
||||
return Project.texture_groups || [];
|
||||
},
|
||||
set(arr) {
|
||||
Project.texture_groups.replace(arr);
|
||||
}
|
||||
})
|
||||
new Property(TextureGroup, 'string', 'name', {default: tl('data.texture_group')});
|
||||
|
||||
TextureGroup.prototype.menu = new Menu('texture_group', [
|
||||
new MenuSeparator('manage'),
|
||||
'rename',
|
||||
{
|
||||
icon: 'fa-leaf',
|
||||
name: 'menu.texture_group.resolve',
|
||||
click(texture_group) {
|
||||
let textures = texture_group.getTextures();
|
||||
Undo.initEdit({textures, texture_groups: [texture_group]});
|
||||
texture_group.remove();
|
||||
textures.forEach(texture => {
|
||||
texture.group = '';
|
||||
})
|
||||
Undo.finishEdit('Resolve texture group', {textures, texture_groups: []});
|
||||
}
|
||||
},
|
||||
], {
|
||||
onClose() {
|
||||
setTimeout(() => {
|
||||
TextureGroup.active_menu_group = null;
|
||||
}, 10);
|
||||
}
|
||||
})
|
||||
/**
|
||||
ToDo:
|
||||
- Auto-generate groups
|
||||
- Grid view?
|
||||
- Search
|
||||
*/
|
||||
|
||||
SharedActions.add('rename', {
|
||||
condition: () => Prop.active_panel == 'textures' && TextureGroup.active_menu_group,
|
||||
run() {
|
||||
TextureGroup.active_menu_group.rename();
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
BARS.defineActions(function() {
|
||||
new Action('create_texture_group', {
|
||||
icon: 'perm_media',
|
||||
category: 'textures',
|
||||
click() {
|
||||
let texture_group = new TextureGroup();
|
||||
texture_group.name = 'Texture Group ' + (TextureGroup.all.length+1);
|
||||
let textures_to_add = Texture.all.filter(tex => tex.selected || tex.multi_selected);
|
||||
Undo.initEdit({texture_groups: [], textures: textures_to_add});
|
||||
if (textures_to_add.length) {
|
||||
for (let texture of textures_to_add) {
|
||||
texture.group = texture_group.uuid;
|
||||
}
|
||||
let first = Texture.selected || textures_to_add[0];
|
||||
texture_group.name = first.name.replace(/\.\w+$/, '') + ' Group';
|
||||
}
|
||||
texture_group.add(false);
|
||||
Undo.finishEdit('Add texture group', {texture_groups: [texture_group], textures: textures_to_add});
|
||||
}
|
||||
})
|
||||
});
|
@ -2,9 +2,9 @@
|
||||
//Textures
|
||||
class Texture {
|
||||
constructor(data, uuid) {
|
||||
var scope = this;
|
||||
let scope = this;
|
||||
//Info
|
||||
for (var key in Texture.properties) {
|
||||
for (let key in Texture.properties) {
|
||||
Texture.properties[key].reset(this);
|
||||
}
|
||||
//meta
|
||||
@ -342,6 +342,15 @@ class Texture {
|
||||
case 3: return tl('texture.error.parent'); break;
|
||||
}
|
||||
}
|
||||
getGroup() {
|
||||
if (!this.group) return;
|
||||
let group = TextureGroup.all.find(group => group.uuid == this.group);
|
||||
if (group) {
|
||||
return group;
|
||||
} else {
|
||||
this.group = '';
|
||||
}
|
||||
}
|
||||
getUndoCopy(bitmap) {
|
||||
var copy = {};
|
||||
for (var key in Texture.properties) {
|
||||
@ -900,6 +909,10 @@ class Texture {
|
||||
if (this.layers_enabled && !this.selected_layer && this.layers[0]) {
|
||||
this.layers[0].select();
|
||||
}
|
||||
if (this.group) {
|
||||
let group = this.getGroup();
|
||||
if (group) group.folded = false;
|
||||
}
|
||||
this.scrollTo();
|
||||
if (this.render_mode == 'layered') {
|
||||
Canvas.updatePixelGrid()
|
||||
@ -935,7 +948,6 @@ class Texture {
|
||||
Project.textures.push(this)
|
||||
}
|
||||
Blockbench.dispatchEvent( 'add_texture', {texture: this})
|
||||
loadTextureDraggable()
|
||||
|
||||
if ((Format.single_texture || Format.single_texture_default) && Cube.all.length) {
|
||||
Canvas.updateAllFaces()
|
||||
@ -1400,7 +1412,7 @@ class Texture {
|
||||
return this;
|
||||
}
|
||||
scrollTo() {
|
||||
var el = $(`#texture_list > li[texid=${this.uuid}]`)
|
||||
var el = $(`#texture_list li.texture[texid=${this.uuid}]`)
|
||||
if (el.length === 0 || Texture.all.length < 2) return;
|
||||
|
||||
var outliner_pos = $('#texture_list').offset().top
|
||||
@ -2052,6 +2064,7 @@ class Texture {
|
||||
new Property(Texture, 'string', 'folder')
|
||||
new Property(Texture, 'string', 'namespace')
|
||||
new Property(Texture, 'string', 'id')
|
||||
new Property(Texture, 'string', 'group')
|
||||
new Property(Texture, 'number', 'width')
|
||||
new Property(Texture, 'number', 'height')
|
||||
new Property(Texture, 'number', 'uv_width')
|
||||
@ -2094,142 +2107,7 @@ function saveTextures(lazy = false) {
|
||||
})
|
||||
}
|
||||
function loadTextureDraggable() {
|
||||
Vue.nextTick(function() {
|
||||
setTimeout(function() {
|
||||
$('li.texture:not(.ui-draggable)').draggable({
|
||||
revertDuration: 0,
|
||||
cursorAt: { left: 2, top: -5 },
|
||||
revert: 'invalid',
|
||||
appendTo: 'body',
|
||||
zIndex: 19,
|
||||
distance: 12,
|
||||
delay: 120,
|
||||
helper: function(e) {
|
||||
var t = $(e.target)
|
||||
if (!t.hasClass('texture')) t = t.parent()
|
||||
if (!t.hasClass('texture')) t = t.parent()
|
||||
return t.find('.texture_icon_wrapper').clone().addClass('texture_drag_helper').attr('texid', t.attr('texid'))
|
||||
},
|
||||
drag: function(event, ui) {
|
||||
|
||||
$('.outliner_node[order]').attr('order', null);
|
||||
$('.drag_hover').removeClass('drag_hover');
|
||||
$('.texture[order]').attr('order', null)
|
||||
if ($('#cubes_list li.outliner_node:hover').length) {
|
||||
var tar = $('#cubes_list li.outliner_node:hover').last()
|
||||
tar.addClass('drag_hover').attr('order', '0');
|
||||
/*
|
||||
var element = Outliner.root.findRecursive('uuid', tar.attr('id'))
|
||||
if (element) {
|
||||
tar.attr('order', '0')
|
||||
}*/
|
||||
} else if ($('#texture_list li:hover').length) {
|
||||
let node = $('#texture_list > .texture:hover')
|
||||
if (node.length) {
|
||||
var target_tex = Texture.all.findInArray('uuid', node.attr('texid'));
|
||||
index = Texture.all.indexOf(target_tex);
|
||||
let offset = event.clientY - node[0].offsetTop;
|
||||
if (offset > 24) {
|
||||
node.attr('order', '1')
|
||||
} else {
|
||||
node.attr('order', '-1')
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
stop: function(event, ui) {
|
||||
setTimeout(function() {
|
||||
$('.texture[order]').attr('order', null);
|
||||
$('.outliner_node[order]').attr('order', null);
|
||||
var tex = Texture.all.findInArray('uuid', ui.helper.attr('texid'));
|
||||
if (!tex) return;
|
||||
if ($('.preview:hover').length > 0) {
|
||||
var data = Canvas.raycast(event)
|
||||
if (data.element && data.face) {
|
||||
var elements = data.element.selected ? UVEditor.getMappableElements() : [data.element];
|
||||
|
||||
if (Format.per_group_texture) {
|
||||
elements = [];
|
||||
let groups = Group.selected ? [Group.selected] : [];
|
||||
Outliner.selected.forEach(el => {
|
||||
if (el.faces && el.parent instanceof Group) groups.safePush(el.parent);
|
||||
});
|
||||
Undo.initEdit({outliner: true});
|
||||
groups.forEach(group => {
|
||||
group.texture = '';
|
||||
group.forEachChild(child => {
|
||||
if (child.preview_controller?.updateFaces) child.preview_controller.updateFaces(child);
|
||||
})
|
||||
})
|
||||
} else {
|
||||
Undo.initEdit({elements})
|
||||
elements.forEach(element => {
|
||||
element.applyTexture(tex, event.shiftKey || Pressing.overrides.shift || [data.face])
|
||||
})
|
||||
}
|
||||
Undo.finishEdit('Apply texture')
|
||||
}
|
||||
} else if ($('#texture_list:hover').length > 0) {
|
||||
let index = Texture.all.length-1
|
||||
let node = $('#texture_list > .texture:hover')
|
||||
if (node.length) {
|
||||
var target_tex = Texture.all.findInArray('uuid', node.attr('texid'));
|
||||
index = Texture.all.indexOf(target_tex);
|
||||
let own_index = Texture.all.indexOf(tex)
|
||||
if (own_index == index) return;
|
||||
if (own_index < index) index--;
|
||||
if (event.clientY - node[0].offsetTop > 24) index++;
|
||||
}
|
||||
Undo.initEdit({texture_order: true})
|
||||
Texture.all.remove(tex)
|
||||
Texture.all.splice(index, 0, tex)
|
||||
Canvas.updateLayeredTextures()
|
||||
Undo.finishEdit('Reorder textures')
|
||||
} else if ($('#cubes_list:hover').length) {
|
||||
|
||||
let target_node = $('#cubes_list li.outliner_node.drag_hover').last().get(0);
|
||||
$('.drag_hover').removeClass('drag_hover');
|
||||
if (!target_node) return;
|
||||
let uuid = target_node.id;
|
||||
let target = OutlinerNode.uuids[uuid];
|
||||
|
||||
let array = [];
|
||||
if (target.type === 'group') {
|
||||
target.forEachChild((element) => {
|
||||
array.push(element);
|
||||
})
|
||||
} else {
|
||||
array = selected.includes(target) ? selected.slice() : [target];
|
||||
}
|
||||
array = array.filter(element => element.applyTexture);
|
||||
|
||||
if (Format.per_group_texture) {
|
||||
let group = target.type === 'group' ? target : null;
|
||||
if (!group) group = target.parent;
|
||||
|
||||
array = [];
|
||||
Undo.initEdit({group});
|
||||
group.texture = tex.uuid;
|
||||
group.forEachChild(child => {
|
||||
if (child.preview_controller?.updateFaces) child.preview_controller.updateFaces(child);
|
||||
})
|
||||
} else {
|
||||
Undo.initEdit({elements: array, uv_only: true})
|
||||
array.forEach(element => {
|
||||
element.applyTexture(tex, true);
|
||||
});
|
||||
}
|
||||
Undo.finishEdit('Apply texture');
|
||||
UVEditor.loadData();
|
||||
|
||||
} else if ($('#uv_viewport:hover').length) {
|
||||
UVEditor.applyTexture(tex);
|
||||
}
|
||||
}, 10)
|
||||
}
|
||||
})
|
||||
}, 42)
|
||||
})
|
||||
console.warn('loadTextureDraggable no longer exists');
|
||||
}
|
||||
function unselectTextures() {
|
||||
Texture.all.forEach(function(s) {
|
||||
@ -2330,15 +2208,15 @@ BARS.defineActions(function() {
|
||||
icon: 'library_add',
|
||||
category: 'textures',
|
||||
keybind: new Keybind({key: 't', ctrl: true}),
|
||||
click() {
|
||||
var start_path;
|
||||
click(event, context) {
|
||||
let start_path;
|
||||
if (!isApp) {} else
|
||||
if (Texture.all.length > 0) {
|
||||
var arr = Texture.all[0].path.split(osfs)
|
||||
let arr = Texture.all[0].path.split(osfs)
|
||||
arr.splice(-1)
|
||||
start_path = arr.join(osfs)
|
||||
} else if (Project.export_path) {
|
||||
var arr = Project.export_path.split(osfs)
|
||||
let arr = Project.export_path.split(osfs)
|
||||
arr.splice(-3)
|
||||
arr.push('textures')
|
||||
start_path = arr.join(osfs)
|
||||
@ -2351,11 +2229,15 @@ BARS.defineActions(function() {
|
||||
multiple: true,
|
||||
startpath: start_path
|
||||
}, function(results) {
|
||||
var new_textures = []
|
||||
Undo.initEdit({textures: new_textures})
|
||||
let new_textures = [];
|
||||
let texture_group = context instanceof TextureGroup ? context : Texture.selected?.getGroup();
|
||||
Undo.initEdit({textures: new_textures});
|
||||
results.forEach(function(f) {
|
||||
var t = new Texture({name: f.name}).fromFile(f).add(false).fillParticle()
|
||||
new_textures.push(t)
|
||||
let t = new Texture({name: f.name}).fromFile(f).add(false).fillParticle();
|
||||
new_textures.push(t);
|
||||
if (texture_group) {
|
||||
t.group = texture_group.uuid;
|
||||
}
|
||||
})
|
||||
Undo.finishEdit('Add texture')
|
||||
})
|
||||
@ -2421,6 +2303,298 @@ BARS.defineActions(function() {
|
||||
|
||||
Interface.definePanels(function() {
|
||||
|
||||
let texture_component = Vue.extend({
|
||||
props: {
|
||||
texture: Texture
|
||||
},
|
||||
methods: {
|
||||
getDescription(texture) {
|
||||
if (texture.error) {
|
||||
return texture.getErrorMessage()
|
||||
} else {
|
||||
let message = texture.width + ' x ' + texture.height + 'px';
|
||||
if (!Format.image_editor) {
|
||||
let uv_size = texture.width / texture.getUVWidth() * 16;
|
||||
message += ` (${trimFloatNumber(uv_size, 2)}x)`;
|
||||
}
|
||||
if (texture.frameCount > 1) {
|
||||
message += ` - ${texture.currentFrame+1}/${texture.frameCount}`
|
||||
}
|
||||
return message;
|
||||
}
|
||||
},
|
||||
getTextureIconOffset(texture) {
|
||||
if (!texture.currentFrame) return;
|
||||
let val = texture.currentFrame * -48 * (texture.display_height / texture.width);
|
||||
return `${val}px`;
|
||||
},
|
||||
dragTexture(e1) {
|
||||
if (e1.button == 1) return;
|
||||
if (getFocusedTextInput()) return;
|
||||
convertTouchEvent(e1);
|
||||
|
||||
let texture = this.texture;
|
||||
let active = false;
|
||||
let helper;
|
||||
let timeout;
|
||||
let last_event = e1;
|
||||
let vue_scope = this;
|
||||
|
||||
// scrolling
|
||||
let list = document.getElementById('texture_list');
|
||||
let list_offset = $(list).offset();
|
||||
let scrollInterval = function() {
|
||||
if (!active) return;
|
||||
if (mouse_pos.y < list_offset.top) {
|
||||
list.scrollTop += (mouse_pos.y - list_offset.top) / 7 - 3;
|
||||
} else if (mouse_pos.y > list_offset.top + list.clientHeight) {
|
||||
list.scrollTop += (mouse_pos.y - (list_offset.top + list.clientHeight)) / 6 + 3;
|
||||
}
|
||||
}
|
||||
let scrollIntervalID;
|
||||
|
||||
function move(e2) {
|
||||
convertTouchEvent(e2);
|
||||
let offset = [
|
||||
e2.clientX - e1.clientX,
|
||||
e2.clientY - e1.clientY,
|
||||
]
|
||||
if (!active) {
|
||||
let distance = Math.sqrt(Math.pow(offset[0], 2) + Math.pow(offset[1], 2))
|
||||
if (Blockbench.isTouch) {
|
||||
if (distance > 20 && timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
} else {
|
||||
document.getElementById('texture_list').scrollTop += last_event.clientY - e2.clientY;
|
||||
}
|
||||
} else if (distance > 6) {
|
||||
active = true;
|
||||
}
|
||||
}
|
||||
if (!active) return;
|
||||
|
||||
if (e2) e2.preventDefault();
|
||||
|
||||
if (open_menu) open_menu.hide();
|
||||
|
||||
if (!helper) {
|
||||
helper = vue_scope.$el.cloneNode();
|
||||
helper.classList.add('texture_drag_helper');
|
||||
helper.setAttribute('texid', texture.uuid);
|
||||
|
||||
document.body.append(helper);
|
||||
|
||||
scrollIntervalID = setInterval(scrollInterval, 1000/60)
|
||||
|
||||
Blockbench.addFlag('dragging_textures');
|
||||
}
|
||||
helper.style.left = `${e2.clientX}px`;
|
||||
helper.style.top = `${e2.clientY}px`;
|
||||
|
||||
// drag
|
||||
$('.outliner_node[order]').attr('order', null);
|
||||
$('.drag_hover').removeClass('drag_hover');
|
||||
$('.texture[order]').attr('order', null)
|
||||
|
||||
let target = $('#cubes_list li.outliner_node:hover').last();
|
||||
if (target.length) {
|
||||
tar.addClass('drag_hover').attr('order', '0');
|
||||
return;
|
||||
}
|
||||
target = document.querySelector('#texture_list li.texture:hover');
|
||||
if (target) {
|
||||
let offset = e2.clientY - $(target).offset().top;
|
||||
target.setAttribute('order', offset > 24 ? '1' : '-1');
|
||||
return;
|
||||
}
|
||||
target = document.querySelector('#texture_list .texture_group_head:hover');
|
||||
if (target) {
|
||||
target.classList.add('drag_hover');
|
||||
target.setAttribute('order', '0');
|
||||
return;
|
||||
}
|
||||
if (document.querySelector('#texture_list:hover')) {
|
||||
let nodes = document.querySelectorAll('#texture_list > li');
|
||||
if (nodes.length) {
|
||||
let target = nodes[nodes.length-1];
|
||||
target.setAttribute('order', '1');
|
||||
target.classList.add('drag_hover');
|
||||
}
|
||||
}
|
||||
last_event = e2;
|
||||
}
|
||||
async function off(e2) {
|
||||
if (helper) helper.remove();
|
||||
clearInterval(scrollIntervalID);
|
||||
removeEventListeners(document, 'mousemove touchmove', move);
|
||||
removeEventListeners(document, 'mouseup touchend', off);
|
||||
e2.stopPropagation();
|
||||
|
||||
$('.outliner_node[order]').attr('order', null);
|
||||
$('.drag_hover').removeClass('drag_hover');
|
||||
$('.texture[order]').attr('order', null)
|
||||
if (Blockbench.isTouch) clearTimeout(timeout);
|
||||
|
||||
if (!active || Menu.open) return;
|
||||
|
||||
await new Promise(r => setTimeout(r, 10));
|
||||
|
||||
Blockbench.removeFlag('dragging_textures');
|
||||
|
||||
if ($('.preview:hover').length > 0) {
|
||||
var data = Canvas.raycast(e2)
|
||||
if (data.element && data.face) {
|
||||
var elements = data.element.selected ? UVEditor.getMappableElements() : [data.element];
|
||||
|
||||
if (Format.per_group_texture) {
|
||||
elements = [];
|
||||
let groups = Group.selected ? [Group.selected] : [];
|
||||
Outliner.selected.forEach(el => {
|
||||
if (el.faces && el.parent instanceof Group) groups.safePush(el.parent);
|
||||
});
|
||||
Undo.initEdit({outliner: true});
|
||||
groups.forEach(group => {
|
||||
group.texture = texture.uuid;
|
||||
group.forEachChild(child => {
|
||||
if (child.preview_controller?.updateFaces) child.preview_controller.updateFaces(child);
|
||||
})
|
||||
})
|
||||
} else {
|
||||
Undo.initEdit({elements});
|
||||
elements.forEach(element => {
|
||||
element.applyTexture(texture, e2.shiftKey || Pressing.overrides.shift || [data.face])
|
||||
})
|
||||
}
|
||||
Undo.finishEdit('Apply texture')
|
||||
}
|
||||
} else if ($('#texture_list:hover').length > 0) {
|
||||
let index = Texture.all.length-1;
|
||||
let texture_node = document.querySelector('#texture_list li.texture:hover');
|
||||
let target_group_head = document.querySelector('#texture_list .texture_group_head:hover');
|
||||
let new_group = '';
|
||||
if (target_group_head) {
|
||||
new_group = target_group_head.parentNode.id;
|
||||
|
||||
} else if (texture_node) {
|
||||
let target_tex = Texture.all.findInArray('uuid', texture_node.getAttribute('texid'));
|
||||
index = Texture.all.indexOf(target_tex);
|
||||
let own_index = Texture.all.indexOf(texture)
|
||||
if (own_index == index) return;
|
||||
let offset = e2.clientY - $(texture_node).offset().top;
|
||||
if (own_index < index) index--;
|
||||
if (offset > 24) index++;
|
||||
new_group = target_tex.group;
|
||||
}
|
||||
Undo.initEdit({texture_order: true, textures: texture.group != new_group ? [texture] : null});
|
||||
Texture.all.remove(texture);
|
||||
Texture.all.splice(index, 0, texture);
|
||||
texture.group = new_group;
|
||||
Canvas.updateLayeredTextures();
|
||||
Undo.finishEdit('Rearrange textures');
|
||||
|
||||
} else if ($('#cubes_list:hover').length) {
|
||||
|
||||
let target_node = $('#cubes_list li.outliner_node.drag_hover').last().get(0);
|
||||
$('.drag_hover').removeClass('drag_hover');
|
||||
if (!target_node) return;
|
||||
let uuid = target_node.id;
|
||||
let target = OutlinerNode.uuids[uuid];
|
||||
|
||||
let array = [];
|
||||
if (target.type === 'group') {
|
||||
target.forEachChild((element) => {
|
||||
array.push(element);
|
||||
})
|
||||
} else {
|
||||
array = selected.includes(target) ? selected.slice() : [target];
|
||||
}
|
||||
array = array.filter(element => element.applyTexture);
|
||||
|
||||
if (Format.per_group_texture) {
|
||||
let group = target.type === 'group' ? target : null;
|
||||
if (!group) group = target.parent;
|
||||
|
||||
array = [];
|
||||
Undo.initEdit({group});
|
||||
group.texture = texture.uuid;
|
||||
group.forEachChild(child => {
|
||||
if (child.preview_controller?.updateFaces) child.preview_controller.updateFaces(child);
|
||||
})
|
||||
} else {
|
||||
Undo.initEdit({elements: array, uv_only: true})
|
||||
array.forEach(element => {
|
||||
element.applyTexture(texture, true);
|
||||
});
|
||||
}
|
||||
Undo.finishEdit('Apply texture');
|
||||
UVEditor.loadData();
|
||||
|
||||
} else if ($('#uv_viewport:hover').length) {
|
||||
UVEditor.applyTexture(texture);
|
||||
}
|
||||
|
||||
|
||||
/*convertTouchEvent(e2);
|
||||
let target = document.elementFromPoint(e2.clientX, e2.clientY);
|
||||
[drop_target] = eventTargetToNode(target);
|
||||
if (drop_target) {
|
||||
moveOutlinerSelectionTo(item, drop_target, e2, order);
|
||||
} else if ($('#texture_list').is(':hover')) {
|
||||
moveOutlinerSelectionTo(item, undefined, e2);*/
|
||||
}
|
||||
|
||||
if (Blockbench.isTouch) {
|
||||
timeout = setTimeout(() => {
|
||||
active = true;
|
||||
move(e1);
|
||||
}, 320)
|
||||
}
|
||||
|
||||
addEventListeners(document, 'mousemove touchmove', move, {passive: false});
|
||||
addEventListeners(document, 'mouseup touchend', off, {passive: false});
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<li
|
||||
v-bind:class="{ selected: texture.selected, multi_selected: texture.multi_selected, particle: texture.particle, use_as_default: texture.use_as_default}"
|
||||
v-bind:texid="texture.uuid"
|
||||
class="texture"
|
||||
@click.stop="texture.select($event)"
|
||||
@dblclick="texture.openMenu($event)"
|
||||
@mousedown.stop="dragTexture($event)" @touchstart.stop="dragTexture($event)"
|
||||
@contextmenu.prevent.stop="texture.showContextMenu($event)"
|
||||
>
|
||||
<div class="texture_icon_wrapper">
|
||||
<img v-bind:texid="texture.id" v-bind:src="texture.source" class="texture_icon" width="48px" alt="" v-if="texture.show_icon" :style="{marginTop: getTextureIconOffset(texture)}" />
|
||||
<i class="material-icons texture_error" v-bind:title="texture.getErrorMessage()" v-if="texture.error">error_outline</i>
|
||||
<i class="texture_movie fa fa_big fa-film" title="Animated Texture" v-if="texture.frameCount > 1"></i>
|
||||
</div>
|
||||
<div class="texture_description_wrapper">
|
||||
<div class="texture_name">{{ texture.name }}</div>
|
||||
<div class="texture_res">{{ getDescription(texture) }}</div>
|
||||
</div>
|
||||
<i class="material-icons texture_multi_select_icon" v-if="texture.multi_selected">check</i>
|
||||
<template v-else>
|
||||
<i class="material-icons texture_particle_icon" v-if="texture.particle">bubble_chart</i>
|
||||
<i class="material-icons texture_particle_icon" v-if="texture.use_as_default">star</i>
|
||||
<i class="material-icons texture_visibility_icon clickable"
|
||||
v-bind:class="{icon_off: !texture.visible}"
|
||||
v-if="texture.render_mode == 'layered'"
|
||||
@click.stop="texture.toggleVisibility()"
|
||||
@dblclick.stop
|
||||
>
|
||||
{{ texture.visible ? 'visibility' : 'visibility_off' }}
|
||||
</i>
|
||||
<i class="material-icons texture_save_icon" v-bind:class="{clickable: !texture.saved}" @click.stop="texture.save()">
|
||||
<template v-if="texture.saved">check_circle</template>
|
||||
<template v-else>save</template>
|
||||
</i>
|
||||
</template>
|
||||
</li>
|
||||
`
|
||||
})
|
||||
|
||||
new Panel('textures', {
|
||||
icon: 'fas.fa-images',
|
||||
growable: true,
|
||||
@ -2437,6 +2611,7 @@ Interface.definePanels(function() {
|
||||
children: [
|
||||
'import_texture',
|
||||
'create_texture',
|
||||
'create_texture_group',
|
||||
'append_to_template',
|
||||
]
|
||||
})
|
||||
@ -2449,26 +2624,16 @@ Interface.definePanels(function() {
|
||||
name: 'panel-textures',
|
||||
data() { return {
|
||||
textures: Texture.all,
|
||||
texture_groups: TextureGroup.all,
|
||||
currentFrame: 0,
|
||||
}},
|
||||
components: {'Texture': texture_component},
|
||||
methods: {
|
||||
openMenu(event) {
|
||||
Interface.Panels.textures.menu.show(event)
|
||||
},
|
||||
getDescription(texture) {
|
||||
if (texture.error) {
|
||||
return texture.getErrorMessage()
|
||||
} else {
|
||||
let message = texture.width + ' x ' + texture.height + 'px';
|
||||
if (!Format.image_editor) {
|
||||
let uv_size = texture.width / texture.getUVWidth() * 16;
|
||||
message += ` (${trimFloatNumber(uv_size, 2)}x)`;
|
||||
}
|
||||
if (texture.frameCount > 1) {
|
||||
message += ` - ${texture.currentFrame+1}/${texture.frameCount}`
|
||||
}
|
||||
return message;
|
||||
}
|
||||
addTextureToGroup(texture_group) {
|
||||
BarItems.import_texture.click(0, texture_group);
|
||||
},
|
||||
slideTimelinePointer(e1) {
|
||||
let scope = this;
|
||||
@ -2510,53 +2675,183 @@ Interface.definePanels(function() {
|
||||
});
|
||||
return count;
|
||||
},
|
||||
getTextureIconOffset(texture) {
|
||||
if (!texture.currentFrame) return;
|
||||
let val = texture.currentFrame * -48 * (texture.display_height / texture.width);
|
||||
return `${val}px`;
|
||||
unselect(event) {
|
||||
if (Blockbench.hasFlag('dragging_textures')) return;
|
||||
unselectTextures();
|
||||
},
|
||||
getUngroupedTextures() {
|
||||
return this.textures.filter(tex => !(tex.group && TextureGroup.all.find(g => g.uuid == tex.group)));
|
||||
},
|
||||
dragTextureGroup(texture_group, e1) {
|
||||
if (e1.button == 1) return;
|
||||
convertTouchEvent(e1);
|
||||
|
||||
let active = false;
|
||||
let helper;
|
||||
let timeout;
|
||||
let last_event = e1;
|
||||
let texture_group_target_node;
|
||||
let order = 0;
|
||||
|
||||
// scrolling
|
||||
let list = document.getElementById('texture_list');
|
||||
let list_offset = $(list).offset();
|
||||
let scrollInterval = function() {
|
||||
if (!active) return;
|
||||
if (mouse_pos.y < list_offset.top) {
|
||||
list.scrollTop += (mouse_pos.y - list_offset.top) / 7 - 3;
|
||||
} else if (mouse_pos.y > list_offset.top + list.clientHeight) {
|
||||
list.scrollTop += (mouse_pos.y - (list_offset.top + list.clientHeight)) / 6 + 3;
|
||||
}
|
||||
}
|
||||
let scrollIntervalID;
|
||||
|
||||
function move(e2) {
|
||||
convertTouchEvent(e2);
|
||||
let offset = [
|
||||
e2.clientX - e1.clientX,
|
||||
e2.clientY - e1.clientY,
|
||||
]
|
||||
if (!active) {
|
||||
let distance = Math.sqrt(Math.pow(offset[0], 2) + Math.pow(offset[1], 2))
|
||||
if (Blockbench.isTouch) {
|
||||
if (distance > 20 && timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
} else {
|
||||
document.getElementById('texture_list').scrollTop += last_event.clientY - e2.clientY;
|
||||
}
|
||||
} else if (distance > 6) {
|
||||
active = true;
|
||||
}
|
||||
}
|
||||
if (!active) return;
|
||||
|
||||
if (e2) e2.preventDefault();
|
||||
|
||||
if (open_menu) open_menu.hide();
|
||||
|
||||
if (!helper) {
|
||||
helper = Interface.createElement('div', {class: 'texture_group_drag_helper'}, texture_group.name);
|
||||
document.body.append(helper);
|
||||
scrollIntervalID = setInterval(scrollInterval, 1000/60)
|
||||
}
|
||||
helper.style.left = `${e2.clientX}px`;
|
||||
helper.style.top = `${e2.clientY}px`;
|
||||
|
||||
// drag
|
||||
$('.drag_hover').removeClass('drag_hover');
|
||||
$('.texture_group[order]').attr('order', null);
|
||||
|
||||
let target = document.querySelector('#texture_list .texture_group:hover');
|
||||
if (target) {
|
||||
target.classList.add('drag_hover');
|
||||
let offset = e2.clientY - $(target).offset().top;
|
||||
order = offset > (target.clientHeight/2) ? 1 : -1;
|
||||
target.setAttribute('order', order.toString());
|
||||
texture_group_target_node = target;
|
||||
|
||||
} else if (document.querySelector('#texture_list:hover')) {
|
||||
let nodes = document.querySelectorAll('#texture_list > li.texture_group');
|
||||
if (nodes.length) {
|
||||
let target = nodes[nodes.length-1];
|
||||
order = 1;
|
||||
target.setAttribute('order', '1');
|
||||
target.classList.add('drag_hover');
|
||||
texture_group_target_node = target;
|
||||
}
|
||||
}
|
||||
last_event = e2;
|
||||
}
|
||||
async function off(e2) {
|
||||
if (helper) helper.remove();
|
||||
clearInterval(scrollIntervalID);
|
||||
removeEventListeners(document, 'mousemove touchmove', move);
|
||||
removeEventListeners(document, 'mouseup touchend', off);
|
||||
e2.stopPropagation();
|
||||
|
||||
$('.drag_hover').removeClass('drag_hover');
|
||||
$('.texture_group[order]').attr('order', null);
|
||||
if (Blockbench.isTouch) clearTimeout(timeout);
|
||||
|
||||
if (!active || Menu.open) return;
|
||||
|
||||
if (texture_group_target_node) {
|
||||
let index = TextureGroup.all.length-1;
|
||||
let texture_group_target = TextureGroup.all.find(tg => tg.uuid == texture_group_target_node.id);
|
||||
if (texture_group_target) {
|
||||
index = TextureGroup.all.indexOf(texture_group_target)
|
||||
let own_index = TextureGroup.all.indexOf(texture_group)
|
||||
if (own_index == index) return;
|
||||
if (own_index < index) index--;
|
||||
if (order == 1) index++;
|
||||
}
|
||||
Undo.initEdit({texture_groups: [texture_group]});
|
||||
TextureGroup.all.remove(texture_group);
|
||||
TextureGroup.all.splice(index, 0, texture_group);
|
||||
Undo.finishEdit('Rearrange texture groups');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (Blockbench.isTouch) {
|
||||
timeout = setTimeout(() => {
|
||||
active = true;
|
||||
move(e1);
|
||||
}, 320)
|
||||
}
|
||||
|
||||
addEventListeners(document, 'mousemove touchmove', move, {passive: false});
|
||||
addEventListeners(document, 'mouseup touchend', off, {passive: false});
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<ul id="texture_list" class="list mobile_scrollbar" @contextmenu.stop.prevent="openMenu($event)">
|
||||
<ul id="texture_list" class="list mobile_scrollbar" @contextmenu.stop.prevent="openMenu($event)" @click.stop="unselect($event)">
|
||||
<li
|
||||
v-for="texture in textures"
|
||||
v-bind:class="{ selected: texture.selected, multi_selected: texture.multi_selected, particle: texture.particle, use_as_default: texture.use_as_default}"
|
||||
v-bind:texid="texture.uuid"
|
||||
v-for="texture_group in texture_groups" :key="texture_group.uuid" :id="texture_group.uuid"
|
||||
class="texture_group"
|
||||
>
|
||||
<div class="texture_group_head" :class="{folded: texture_group.folded}"
|
||||
@dblclick.stop="texture_group.select()"
|
||||
@click.stop="texture_group.folded = !texture_group.folded"
|
||||
@contextmenu.prevent.stop="texture_group.showContextMenu($event)"
|
||||
@mousedown.stop="dragTextureGroup(texture_group, $event)" @touchstart.stop="dragTextureGroup(texture_group, $event)"
|
||||
>
|
||||
<i
|
||||
@click.stop="texture_group.folded = !texture_group.folded"
|
||||
class="icon-open-state fa"
|
||||
:class=\'{"fa-angle-right": texture_group.folded, "fa-angle-down": !texture_group.folded}\'
|
||||
></i>
|
||||
<label :title="texture_group.name">{{ texture_group.name }}</label>
|
||||
<ul class="texture_group_mini_icon_list" v-if="texture_group.folded">
|
||||
<li
|
||||
v-for="texture in texture_group.getTextures()"
|
||||
:key="texture.uuid"
|
||||
class="texture"
|
||||
v-on:click.stop="texture.select($event)"
|
||||
v-on:dblclick="texture.openMenu($event)"
|
||||
@contextmenu.prevent.stop="texture.showContextMenu($event)"
|
||||
class="texture_mini_icon"
|
||||
:title="texture.name"
|
||||
>
|
||||
<div class="texture_icon_wrapper">
|
||||
<img v-bind:texid="texture.id" v-bind:src="texture.source" class="texture_icon" width="48px" alt="" v-if="texture.show_icon" :style="{marginTop: getTextureIconOffset(texture)}" />
|
||||
<i class="material-icons texture_error" v-bind:title="texture.getErrorMessage()" v-if="texture.error">error_outline</i>
|
||||
<i class="texture_movie fa fa_big fa-film" title="Animated Texture" v-if="texture.frameCount > 1"></i>
|
||||
</div>
|
||||
<div class="texture_description_wrapper">
|
||||
<div class="texture_name">{{ texture.name }}</div>
|
||||
<div class="texture_res">{{ getDescription(texture) }}</div>
|
||||
</div>
|
||||
<i class="material-icons texture_multi_select_icon" v-if="texture.multi_selected">check</i>
|
||||
<template v-else>
|
||||
<i class="material-icons texture_particle_icon" v-if="texture.particle">bubble_chart</i>
|
||||
<i class="material-icons texture_particle_icon" v-if="texture.use_as_default">star</i>
|
||||
<i class="material-icons texture_visibility_icon clickable"
|
||||
v-bind:class="{icon_off: !texture.visible}"
|
||||
v-if="texture.render_mode == 'layered'"
|
||||
@click.stop="texture.toggleVisibility()"
|
||||
@dblclick.stop
|
||||
>
|
||||
{{ texture.visible ? 'visibility' : 'visibility_off' }}
|
||||
</i>
|
||||
<i class="material-icons texture_save_icon" v-bind:class="{clickable: !texture.saved}" @click="texture.save()">
|
||||
<template v-if="texture.saved">check_circle</template>
|
||||
<template v-else>save</template>
|
||||
</i>
|
||||
</template>
|
||||
<img :src="texture.source" class="texture_icon" width="24px" height="24px" alt="" v-if="texture.show_icon" />
|
||||
</li>
|
||||
</ul>
|
||||
<div class="in_list_button" @click.stop="addTextureToGroup(texture_group)" v-if="!texture_group.folded">
|
||||
<i class="material-icons">add</i>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="texture_group_list" v-if="!texture_group.folded">
|
||||
<Texture
|
||||
v-for="texture in texture_group.getTextures()"
|
||||
:key="texture.uuid"
|
||||
:texture="texture"
|
||||
></Texture>
|
||||
</ul>
|
||||
</li>
|
||||
<Texture
|
||||
v-for="texture in getUngroupedTextures()"
|
||||
:key="texture.uuid"
|
||||
:texture="texture"
|
||||
></Texture>
|
||||
</ul>
|
||||
<div id="texture_animation_playback" class="bar" v-show="maxFrameCount()">
|
||||
<div class="tool_wrapper"></div>
|
||||
<div id="texture_animation_timeline" ref="timeline" @mousedown="slideTimelinePointer">
|
||||
|
36
js/undo.js
36
js/undo.js
@ -323,6 +323,34 @@ class UndoSystem {
|
||||
}
|
||||
}
|
||||
|
||||
if (save.texture_groups) {
|
||||
for (let uuid in save.texture_groups) {
|
||||
let group;
|
||||
let data = save.texture_groups[uuid];
|
||||
if (reference.texture_groups[uuid]) {
|
||||
group = TextureGroup.all.find(tg => tg.uuid == uuid);
|
||||
if (group) {
|
||||
group.extend(data);
|
||||
}
|
||||
} else {
|
||||
group = new TextureGroup(data, uuid).add(false);
|
||||
}
|
||||
//order
|
||||
let index = TextureGroup.all.indexOf(group);
|
||||
if (index != -1 && index != data.index && typeof data.index == 'number') {
|
||||
TextureGroup.all.remove(group);
|
||||
TextureGroup.all.splice(data.index, 0, group);
|
||||
}
|
||||
}
|
||||
for (let uuid in reference.texture_groups) {
|
||||
if (!save.texture_groups[uuid]) {
|
||||
let group = TextureGroup.all.find(tg => tg.uuid == uuid);
|
||||
if (group) {
|
||||
TextureGroup.all.remove(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (save.textures) {
|
||||
Painter.current = {}
|
||||
for (var uuid in save.textures) {
|
||||
@ -591,6 +619,14 @@ UndoSystem.save = class {
|
||||
})
|
||||
}
|
||||
|
||||
if (aspects.texture_groups) {
|
||||
this.texture_groups = {};
|
||||
aspects.texture_groups.forEach(tg => {
|
||||
let copy = tg.getUndoCopy()
|
||||
this.texture_groups[tg.uuid] = copy;
|
||||
})
|
||||
}
|
||||
|
||||
if (aspects.layers) {
|
||||
this.layers = {};
|
||||
aspects.layers.forEach(layer => {
|
||||
|
@ -26,6 +26,7 @@
|
||||
"data.texture_mesh": "Texture Mesh",
|
||||
"data.group": "Group",
|
||||
"data.texture": "Texture",
|
||||
"data.texture_group": "Texture Group",
|
||||
"data.layer": "Layer",
|
||||
"data.origin": "Pivot",
|
||||
"data.plugin": "Plugin",
|
||||
@ -2020,6 +2021,8 @@
|
||||
"menu.texture.save": "Save",
|
||||
"menu.texture.properties": "Properties...",
|
||||
|
||||
"menu.texture_group.resolve": "Resolve",
|
||||
|
||||
"menu.preview.background": "Background",
|
||||
"menu.preview.background.load": "Load",
|
||||
"menu.preview.background.clipboard": "Load from Clipboard",
|
||||
|
Loading…
Reference in New Issue
Block a user