Add face properties editor

This commit is contained in:
JannisX11 2022-10-10 20:38:15 +02:00
parent 6debfd32db
commit 7912894a95
6 changed files with 248 additions and 13 deletions

View File

@ -1439,6 +1439,99 @@
padding-top: 2px;
}
.face_properties_toggle {
width: 32px;
flex-grow: 0 !important;
}
.face_properties_toggle > i {
margin: 1px auto;
}
#face_properties_header_bar {
display: flex;
height: 28px;
color: var(--color-subtle_text);
}
#face_properties_header_bar li:first-child {
flex-grow: 1;
text-align: center;
}
#uv_face_properties > ul {
overflow-y: auto;
margin-bottom: 8px;
}
.uv_face_properties_labels {
height: 28px;
display: flex;
align-items: center;
padding: 0 6px;
gap: 2px;
text-align: center;
color: var(--color-subtle_text);
}
.uv_face_properties_labels > .flexible {
flex-grow: 1;
flex-shrink: 1;
width: 50px;
}
.uv_face_properties_line {
height: 44px;
display: flex;
align-items: center;
padding: 0 6px;
gap: 2px;
}
.uv_face_properties_line:nth-child(even) {
background-color: var(--color-back);
}
.uv_face_properties_line:nth-child(even) .dark_bordered {
background-color: var(--color-ui);
}
.uv_face_properties_line.disabled {
color: var(--color-subtle_text);
opacity: 0.64;
}
.uv_face_properties_line > * {
flex-grow: 0;
flex-shrink: 0;
}
.uv_face_properties_line > .flexible {
flex-grow: 1;
flex-shrink: 1;
width: 50px;
}
.uv_face_properties_line label {
width: 48px;
}
.uv_face_properties_line input[type=checkbox] {
width: 28px;
text-align: center;
}
.face_properties_texture {
width: 120px;
flex-grow: 1;
overflow: hidden;
white-space: nowrap;
cursor: pointer;
color: var(--color-subtle_text);
}
.face_properties_texture:hover {
color: var(--color-text);
}
.face_properties_texture img {
vertical-align: middle;
pointer-events: none;
margin-right: 2px;
}
.face_properties_texture .texture_dummy_icon {
vertical-align: middle;
pointer-events: none;
margin-right: 2px;
display: inline-block;
width: 32px;
height: 32px;
background-color: var(--color-dark);
}
.cube_box_uv {
position: absolute;
z-index: 2;

View File

@ -301,7 +301,7 @@ var codec = new Codec('java_block', {
link = texture_arr[link.substring(1)];
}
let texture = new Texture({id: key}).fromJavaLink(texture_arr[key], path_arr.slice()).add();
texture_paths[texture_arr[key]] = texture_ids[key] = texture;
texture_paths[texture_arr[key].replace(/^minecraft:/, '')] = texture_ids[key] = texture;
new_textures.push(texture);
}
}
@ -310,11 +310,11 @@ var codec = new Codec('java_block', {
if (link.startsWith('#') && texture_arr[link.substring(1)]) {
link = texture_arr[link.substring(1)];
}
if (texture_paths[link]) {
texture_paths[link].enableParticle()
if (texture_paths[link.replace(/^minecraft:/, '')]) {
texture_paths[link.replace(/^minecraft:/, '')].enableParticle()
} else {
let texture = new Texture({id: 'particle'}).fromJavaLink(link, path_arr.slice()).enableParticle().add();
texture_paths[link] = texture_ids.particle = texture;
texture_paths[link.replace(/^minecraft:/, '')] = texture_ids.particle = texture;
new_textures.push(texture);
}
}

View File

@ -163,6 +163,11 @@ function updateSelection(options = {}) {
document.querySelectorAll('.selection_only#panel_uv').forEach(node => node.style.setProperty('visibility', 'visible'));
}
}
if (UVEditor.vue.mode == 'face_properties' && Outliner.selected.length) {
if (!Outliner.selected[0] || Outliner.selected[0].type !== 'cube' || Outliner.selected[0].box_uv) {
UVEditor.vue.mode = 'uv';
}
}
if (Outliner.selected.length || (Format.single_texture && Modes.paint)) {
UVEditor.selected_faces.forEachReverse((fkey, i) => {
if (!UVEditor.getMappableElements().find(el => el.faces[fkey])) {

View File

@ -1021,7 +1021,6 @@ const UVEditor = {
Canvas.updateSelectedFaces()
},
switchCullface(event) {
var scope = this;
Undo.initEdit({elements: Cube.selected, uv_only: true})
var val = BarItems.cullface.get()
if (val === 'off') val = false
@ -1399,7 +1398,7 @@ const UVEditor = {
UVEditor.message('uv_editor.reset')
Undo.finishEdit('Apply blank texture')
}},
{icon: 'clear', name: 'menu.cube.texture.transparent', condition: () => UVEditor.getReferenceFace() instanceof CubeFace, click: function() {UVEditor.clear(event)}},
{icon: 'clear', name: 'menu.cube.texture.transparent', condition: () => UVEditor.getReferenceFace() instanceof CubeFace, click: function(event) {UVEditor.clear(event)}},
]
Texture.all.forEach(function(t) {
arr.push({
@ -1799,6 +1798,15 @@ Interface.definePanels(function() {
east: tl('face.east'),
up: tl('face.up'),
down: tl('face.down'),
},
cullface_options: {
'': tl('uv_editor.no_faces'),
north: tl('face.north'),
south: tl('face.south'),
west: tl('face.west'),
east: tl('face.east'),
up: tl('face.up'),
down: tl('face.down'),
}
}},
computed: {
@ -2897,6 +2905,67 @@ Interface.definePanels(function() {
Math.floor(gap_y + this.height/2),
];
return style ? `${margin[1]}px ${margin[0]}px` : margin;
},
checkFormat(values) {
for (let key in values) {
if (Format[key] != values[key]) return false;
}
return true;
},
toggleFaceEnabled(key, event) {
let value = this.mappable_elements[0].faces[key].texture === null;
Undo.initEdit({elements: Cube.selected, uv_only: true})
UVEditor.forCubes(obj => {
UVEditor.getFaces(obj, event).forEach(function(side) {
if (value) {
if (obj.faces[side].texture === null) obj.faces[side].texture = false;
} else {
obj.faces[side].texture = null;
}
})
obj.preview_controller.updateFaces(obj);
})
UVEditor.loadData()
Undo.finishEdit('Toggle face')
Canvas.updateSelectedFaces()
},
openFaceTextureMenu(event) {
let menu = new Menu(Texture.all.map(tex => {
return {
name: tex.name,
icon: tex.img,
click(event) {
UVEditor.applyTexture(tex);
}
}
}))
menu.open(event.target);
},
toggleFaceTint(key, event) {
Undo.initEdit({elements: Cube.selected, uv_only: true})
UVEditor.switchTint(event)
Undo.finishEdit('Toggle face tint')
},
changeFaceTint(key, event) {
Undo.initEdit({elements: Cube.selected, uv_only: true})
UVEditor.setTint(event, parseInt(event.target.value));
Undo.finishEdit('Toggle face tint');
},
setCullface(key, value) {
Undo.initEdit({elements: Cube.selected, uv_only: true})
UVEditor.forCubes(obj => {
UVEditor.selected_faces.forEach(face => {
obj.faces[face].cullface = value;
})
})
Undo.finishEdit(value ? `Set cullface to ${value}` : 'Disable cullface');
},
startInputMaterialInstance(event) {
Undo.initEdit({elements: Cube.selected, uv_only: true})
},
endInputMaterialInstance(event) {
Undo.finishEdit('Change material instances');
}
},
template: `
@ -2912,8 +2981,78 @@ Interface.definePanels(function() {
<li v-for="(face, key) in mappable_elements[0].faces" :face="key" :class="{selected: selected_faces.includes(key), disabled: mappable_elements[0].faces[key].texture === null}" @mousedown="selectFace(key, $event, false, true)">
{{ face_names[key] }}
</li>
<li @click="mode = 'face_properties'" class="tool face_properties_toggle">
<div class="tooltip">${tl('uv_editor.face_properties')}</div>
<i class="material-icons">checklist</i>
</li>
</div>
<div id="uv_face_properties" v-if="mode === 'face_properties' && mappable_elements[0] && mappable_elements[0].type == 'cube'">
<div class="bar" id="face_properties_header_bar">
<li></li>
<li @click="mode = 'uv'" class="tool face_properties_toggle">
<i class="material-icons">clear</i>
</li>
</div>
<div class="uv_face_properties_labels">
<label style="width: 76px;">Enabled</label>
<label style="width: 120px;" class="flexible">Texture</label>
<template v-if="checkFormat({java_face_properties: true})">
<label style="width: 58px;">${tl('action.face_tint')}</label>
<label style="width: 50px;" class="flexible">${tl('action.cullface')}</label>
</template>
<template v-if="checkFormat({id: 'bedrock_block'})">
<label style="width: 100px;" class="flexible">${tl('uv_editor.face_properties.material_instance')}</label>
</template>
</div>
<ul>
<li v-for="(face, key) in mappable_elements[0].faces" :face="key"
class="uv_face_properties_line"
:class="{selected: selected_faces.includes(key), disabled: mappable_elements[0].faces[key].texture === null}"
@mousedown="selectFace(key, $event, false, true)"
>
<input type="checkbox" :checked="mappable_elements[0].faces[key].texture !== null" @change="toggleFaceEnabled(key, $event)">
<label>{{ face_names[key] }}</label>
<div class="face_properties_texture" :face_texture="face_texture = mappable_elements[0].faces[key].getTexture()" @click="openFaceTextureMenu($event)">
<img :src="face_texture.source" class="texture_icon" width="32px" height="32px" alt="" v-if="face_texture && face_texture.show_icon" />
<div class="texture_dummy_icon" v-else></div>
{{ face_texture ? face_texture.name : '${tl('menu.cube.texture.blank')}' }}
</div>
<template v-if="checkFormat({java_face_properties: true})">
<div style="width: 58px; display: flex; justify-content: center;">
<input type="checkbox" title="${tl('action.face_tint')}" :checked="mappable_elements[0].faces[key].tint > -1" @change="toggleFaceTint(key, $event)">
<input type="number" title="${tl('action.face_tint')}" style="width: 30px;" :value="mappable_elements[0].faces[key].tint" min="0" step="1" @input="changeFaceTint(key, $event)" v-if="mappable_elements[0].faces[key].tint > -1">
</div>
<select-input class="flexible" title="${tl('action.cullface')}" :value="mappable_elements[0].faces[key].cullface" @input="setCullface(key, $event)" :options="cullface_options" />
</template>
<template v-if="checkFormat({id: 'bedrock_block'})">
<input type="text" style="width: 100px;" class="flexible dark_bordered"
title="${tl('uv_editor.face_properties.material_instance')}"
v-model="mappable_elements[0].faces[key].material_name"
@focus="startInputMaterialInstance($event)"
@focusout="endInputMaterialInstance($event)"
>
</template>
</li>
</ul>
</div>
<div id="uv_viewport"
@contextmenu="contextMenu($event)"
@mousedown="onMouseDown($event)"
@ -2921,7 +3060,7 @@ Interface.definePanels(function() {
@mousewheel="onMouseWheel($event)"
class="checkerboard_target"
ref="viewport"
v-if="!hidden"
v-if="!hidden && mode !== 'face_properties'"
:style="{width: (width+8) + 'px', height: (height+8) + 'px', overflowX: (zoom > 1) ? 'scroll' : 'hidden', overflowY: (inner_height > height) ? 'scroll' : 'hidden'}"
>
@ -3075,11 +3214,7 @@ Interface.definePanels(function() {
<div v-show="copy_overlay.state !== 'move'" id="toggle_uv_overlay_anchor"></div>
</div>
<div v-if="mode == 'properties'">
</div>
<div :class="{joined_uv_bar: width >= 720}" ref="uv_toolbars">
<div v-show="mode == 'uv'" class="bar uv_editor_sliders" ref="slider_bar" style="margin-left: 2px;"></div>
<div v-show="mode == 'uv'" class="toolbar_wrapper uv_editor"></div>

File diff suppressed because one or more lines are too long

View File

@ -1717,6 +1717,8 @@
"panel.bone.ik": "Inverse Kinematics (Experimental)",
"uv_editor.title": "UV Editor",
"uv_editor.face_properties": "Face Froperties",
"uv_editor.face_properties.material_instance": "Material Instance",
"uv_editor.show_all_faces": "Show All Faces",
"uv_editor.show_selected_faces": "Show Selected Faces",
"uv_editor.all_faces": "All",