Add custom theme restore button

Fix copy paste tool
Add Sketchfab categories
Fix #1054
Fix #1067 Start menu bottom graphic icon offset
Fix template generator face rotation accuracy
Update meta data
[ci-build]
This commit is contained in:
JannisX11 2021-09-23 21:35:36 +02:00
parent dd6ad7387a
commit fa9d8db29d
14 changed files with 150 additions and 54 deletions

View File

@ -1,7 +1,7 @@
# Blockbench
Blockbench is a free, modern model editor for boxy models and pixel art textures.
Models can be exported for Minecraft Java and Bedrock Edition as well as most game engines and other 3D applications.
Blockbench is a free, modern model editor for low-poly and boxy models with pixel art textures.
Models can be exported into standardized formats, to be shared, rendered, 3D-printed, or used in game engines. There are also multiple dedicated formats for Minecraft Java and Bedrock Edition with format-specific features.
Blockbench features a modern and intuitive UI, plugin support and innovative features. It is the industry standard for creating custom 3D models for the Minecraft Marketplace.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 108 KiB

View File

@ -670,6 +670,23 @@
.theme_preview.borders .theme_preview_sidebar:last-child {
border-left: 2px solid var(--color-border);
}
.theme_backup_bar {
padding: 2px 6px;
border: 2px solid var(--color-accent);
margin-bottom: 7px;
cursor: pointer;
}
.theme_backup_bar:hover {
color: var(--color-light);
}
.theme_backup_bar > i {
padding: 1px;
color: var(--color-text);
float: right;
}
.theme_backup_bar > i:hover {
color: var(--color-light);
}
/*About*/
#about_page_title {

View File

@ -595,6 +595,8 @@
}
#start_screen h3 {
margin: 0;
}
#start_screen #start-files h3 {
padding-bottom: 0;
height: auto;
font-size: 18px;
@ -730,16 +732,16 @@
max-width: 100%;
flex-grow: 0;
}
#start_screen div.start_screen_left.graphic {
#start_screen div.graphic:not(.graphic_icon) {
background-size: cover;
position: relative;
padding: 0;
}
#start_screen div.start_screen_left.graphic p {
#start_screen div.graphic p {
position: absolute;
font-size: 0.96em;
}
#start_screen div.start_screen_left i.graphic_icon {
#start_screen div.graphic.graphic_icon i {
font-size: 40px;
width: 12px;
margin-top: 6px;

View File

@ -25,7 +25,7 @@
<script>
if (typeof module === 'object') {window.module = module; module = undefined;}//jQuery Fix
const isApp = typeof require !== 'undefined';
const appVersion = '4.0.0-beta.2';
const appVersion = '4.0.0-beta.3';
if (localStorage.getItem('theme')) {

View File

@ -30,7 +30,7 @@ const Clipbench = {
if (Animator.open && Timeline.animators.length && (Timeline.selected.length || mode === 2) && ['keyframe', 'timeline', 'preview'].includes(p)) {
return Clipbench.types.keyframe
}
if (Modes.edit && p == 'preview' && Mesh.selected[0] && Mesh.selected[0].getSelectedVertices().length) {
if (Modes.edit && p == 'preview' && Mesh.selected[0] && Mesh.selected[0].getSelectedVertices().length && (mode !== 2 || Clipbench.vertices)) {
return Clipbench.types.mesh_selection;
}
if (mode == 2 && Modes.edit && Format.meshes && Clipbench.last_copied == 'mesh_selection' && (p == 'preview' || p == 'outliner')) {

View File

@ -553,7 +553,7 @@ function addStartScreenSection(id, data) {
if (data.graphic.type === 'icon') {
var icon = Blockbench.getIconNode(data.graphic.icon)
$(icon).addClass('graphic_icon')
left.addClass('graphic_icon')
left.append(icon)
} else {
left.css('background-image', `url('${data.graphic.source}')`)
@ -704,7 +704,7 @@ function addStartScreenSection(id, data) {
color: 'var(--color-back)',
graphic: {type: 'icon', icon: 'fa-archive'},
text: [
{type: 'h1', text: tl('message.recover_backup.title')},
{type: 'h2', text: tl('message.recover_backup.title')},
{text: tl('message.recover_backup.message')},
{type: 'button', text: tl('dialog.ok'), click: (e) => {
loadModelFile({content: backup_model, path: 'backup.bbmodel', no_file: true})
@ -725,7 +725,7 @@ function addStartScreenSection(id, data) {
text_color: '#ffffff',
graphic: {type: 'icon', icon: 'fab.fa-twitter'},
text: [
{type: 'h1', text: 'Blockbench on Twitter'},
{type: 'h2', text: 'Blockbench on Twitter'},
{text: 'Follow Blockbench on Twitter for the latest news as well as cool models from the community! [twitter.com/blockbench](https://twitter.com/blockbench/)'}
],
last: true
@ -738,7 +738,7 @@ function addStartScreenSection(id, data) {
text_color: '#ffffff',
graphic: {type: 'icon', icon: 'fab.fa-discord'},
text: [
{type: 'h1', text: 'Discord Server'},
{type: 'h2', text: 'Discord Server'},
{text: 'You need help with modeling or you want to chat about Blockbench? Join the official [Blockbench Discord](https://discord.gg/WVHg5kH)!'}
],
last: true

View File

@ -113,6 +113,7 @@ const CustomTheme = {
},
component: {
data: {
backup: '',
data: CustomTheme.data,
open_category: 'select',
themes: CustomTheme.themes
@ -154,6 +155,15 @@ const CustomTheme = {
CustomTheme.loadTheme(theme);
saveChanges();
},
loadBackup() {
CustomTheme.loadTheme(JSON.parse(CustomTheme.backup_data));
CustomTheme.data.customized = true;
this.clearBackup();
},
clearBackup() {
this.backup = '';
CustomTheme.backup_data = null;
},
customizeTheme() {
CustomTheme.customizeTheme();
},
@ -193,6 +203,10 @@ const CustomTheme = {
template: `
<div id="theme_editor">
<div v-if="open_category == 'select'">
<div v-if="backup" class="theme_backup_bar" @click.stop="loadBackup()">
{{ tl('layout.restore_backup', [backup]) }}
<i class="material-icons" @click.stop="clearBackup()">clear</i>
</div>
<h2 class="i_b">${tl('layout.select')}</h2>
<div id="theme_list">
@ -375,6 +389,13 @@ const CustomTheme = {
},
loadTheme(theme) {
var app = CustomTheme.data;
if (app.customized && app.name) {
// Backup
CustomTheme.dialog.content_vue.backup = app.name;
CustomTheme.backup_data = JSON.stringify(app);
}
app.id = '';
app.name = '';
app.author = '';
@ -382,6 +403,7 @@ const CustomTheme = {
app.headline_font = '';
app.code_font = '';
app.borders = false;
app.customized = false;
Merge.string(app, theme, 'id')
Merge.string(app, theme, 'name')
Merge.string(app, theme, 'author')
@ -397,7 +419,6 @@ const CustomTheme = {
}
}
Merge.string(app, theme, 'css');
app.customized = false;
this.updateColors();
this.updateSettings();
},

View File

@ -297,6 +297,28 @@ function uploadSketchfabModel() {
if (Project.name) tag_suggestions.push(clean_project_name);
if (clean_project_name.includes('-')) tag_suggestions.safePush(...clean_project_name.split('-').filter(s => s.length > 2 && s != 'geo').reverse());
let categories = {
"": "-",
"animals-pets": "Animals & Pets",
"architecture": "Architecture",
"art-abstract": "Art & Abstract",
"cars-vehicles": "Cars & Vehicles",
"characters-creatures": "Characters & Creatures",
"cultural-heritage-history": "Cultural Heritage & History",
"electronics-gadgets": "Electronics & Gadgets",
"fashion-style": "Fashion & Style",
"food-drink": "Food & Drink",
"furniture-home": "Furniture & Home",
"music": "Music",
"nature-plants": "Nature & Plants",
"news-politics": "News & Politics",
"people": "People",
"places-travel": "Places & Travel",
"science-technology": "Science & Technology",
"sports-fitness": "Sports & Fitness",
"weapons-military": "Weapons & Military",
}
var dialog = new Dialog({
id: 'sketchfab_uploader',
title: 'dialog.sketchfab_uploader.title',
@ -304,8 +326,10 @@ function uploadSketchfabModel() {
form: {
token: {label: 'dialog.sketchfab_uploader.token', value: settings.sketchfab_token.value, type: 'password'},
about_token: {type: 'info', text: tl('dialog.sketchfab_uploader.about_token', ['[sketchfab.com/settings/password](https://sketchfab.com/settings/password)'])},
name: {label: 'dialog.sketchfab_uploader.name'},
name: {label: 'dialog.sketchfab_uploader.name', value: capitalizeFirstLetter(Project.name.replace(/\..+/, '').replace(/[_.-]/g, ' '))},
description: {label: 'dialog.sketchfab_uploader.description', type: 'textarea'},
category1: {label: 'dialog.sketchfab_uploader.category', type: 'select', options: categories, value: ''},
category2: {label: 'dialog.sketchfab_uploader.category2', type: 'select', options: categories, value: ''},
tags: {label: 'dialog.sketchfab_uploader.tags', placeholder: 'Tag1 Tag2'},
tag_suggestions: {label: 'dialog.sketchfab_uploader.suggested_tags', type: 'buttons', buttons: tag_suggestions, click(index) {
let {tags} = dialog.getFormResult();
@ -316,9 +340,7 @@ function uploadSketchfabModel() {
}
}},
animations: {label: 'dialog.sketchfab_uploader.animations', value: true, type: 'checkbox', condition: (Format.animation_mode && Animator.animations.length)},
//color: {type: 'color', label: 'dialog.sketchfab_uploader.color'},
draft: {label: 'dialog.sketchfab_uploader.draft', type: 'checkbox'},
// Category
draft: {label: 'dialog.sketchfab_uploader.draft', type: 'checkbox', value: true},
divider: '_',
private: {label: 'dialog.sketchfab_uploader.private', type: 'checkbox'},
password: {label: 'dialog.sketchfab_uploader.password'},
@ -338,11 +360,18 @@ function uploadSketchfabModel() {
data.append('description', formResult.description)
data.append('tags', formResult.tags)
data.append('isPublished', !formResult.draft)
//data.append('background', JSON.stringify({color: formResult.color.toHexString()}))
//data.append('background', JSON.stringify({color: '#00ff00'}))
data.append('private', formResult.private)
data.append('password', formResult.password)
data.append('source', 'blockbench')
if (formResult.category1 || formResult.category2) {
let selected_categories = [];
if (formResult.category1) selected_categories.push(formResult.category1);
if (formResult.category2) selected_categories.push(formResult.category2);
data.append('categories', selected_categories);
}
settings.sketchfab_token.value = formResult.token
Codecs.gltf.compile({animations: formResult.animations}, (content) => {

View File

@ -680,7 +680,16 @@ const TextureGenerator = {
)
let snap = 2;
rot = (Math.radToDeg(rot) + 360) % 90;
let rounded = Math.round(rot / snap) * snap;
let rounded
let last_difference = snap;
for (let rounded_angle in precise_rotation_angle) {
let precise = precise_rotation_angle[rounded_angle];
if (Math.abs(rot - precise) < last_difference) {
last_difference = Math.abs(rot - precise);
rounded = rounded_angle;
}
}
if (!rounded) rounded = Math.round(rot / snap) * snap;
if (rotation_angles[rounded]) {
rotation_angles[rounded]++;
} else {
@ -1093,7 +1102,6 @@ const TextureGenerator = {
face.uv[vertices[1]] = [x+0.25, y+0.25];
face.uv[vertices[2]] = [x+0.25, y+0.75];
if (vertices[3]) face.uv[vertices[3]] = [x+0.75, y+0.75];
console.log(vertices, face.uv)
}
})
var dataUrl = canvas.toDataURL()

View File

@ -277,8 +277,7 @@ const UVEditor = {
$(UVEditor.vue.$refs.frame).append(overlay)
Painter.selection.x = Math.clamp(Painter.selection.x, 0, this.texture.width-Painter.selection.canvas.width)
Painter.selection.y = Math.clamp(Painter.selection.y, 0, this.texture.height-Painter.selection.canvas.height)
this.updateSize()
UVEditor.updatePastingOverlay()
function clickElsewhere(event) {
if (!Painter.selection.overlay) {
@ -286,13 +285,6 @@ const UVEditor = {
} else if (Painter.selection.overlay.has(event.target).length == 0) {
open_interface.confirm()
}
/*
if (!Painter.selection.overlay) {
removeEventListeners(document, 'mousedown touchstart', clickElsewhere)
} else if (Painter.selection.overlay.has(event.target).length == 0) {
open_interface.confirm()
}
*/
}
addEventListeners(document, 'mousedown touchstart', clickElsewhere)
},
@ -1315,18 +1307,7 @@ const UVEditor = {
BARS.defineActions(function() {
/*
new Action('UVEditor', {
icon: 'view_module',
category: 'blockbench',
condition: () => !Project.box_uv && Cube.selected.length,
click: function () {UVEditor.openAll()}
})
new Action('UVEditor_full', {
icon: 'web_asset',
category: 'blockbench',
click: function () {UVEditor.openFull()}
})*/
new BarSlider('uv_rotation', {
@ -1362,14 +1343,6 @@ BARS.defineActions(function() {
UVEditor.setGrid(value);
}
})
/*
new Action('uv_select_all', {
icon: 'view_module',
category: 'uv',
condition: () => open_dialog === 'UVEditor',
click: UVEditor.selectAll
})
*/
new Action('uv_maximize', {
icon: 'zoom_out_map',
category: 'uv',
@ -1596,6 +1569,9 @@ Interface.definePanels(function() {
height: 0,
active: false
},
copy_overlay: {
},
project_resolution: [16, 16],
elements: [],
@ -1751,6 +1727,16 @@ Interface.definePanels(function() {
let {selection_rect} = this;
let scope = this;
let old_faces = this.selected_faces.slice();
let old_selected_vertices = {};
Mesh.selected.forEach(mesh => {
old_selected_vertices[mesh.uuid] = mesh.getSelectedVertices().slice();
})
let old_elements;
if (Project.box_uv) {
old_elements = scope.mappable_elements.slice();
}
function drag(e1) {
selection_rect.active = true;
@ -1767,11 +1753,21 @@ Interface.definePanels(function() {
if (!e1.shiftKey) {
scope.selected_faces.empty();
if (old_elements) Outliner.selected.empty();
} else {
scope.selected_faces.replace(old_faces);
if (old_elements) Outliner.selected.replace(old_elements);
}
let elements;
if (Project.box_uv) {
elements = Cube.all.slice();
elements.safePush(scope.mappable_elements);
} else {
elements = scope.mappable_elements;
}
scope.mappable_elements.forEach(element => {
elements.forEach(element => {
if (element instanceof Cube && !Project.box_uv) {
for (let fkey in element.faces) {
let face_rect = getRectangle(...element.faces[fkey].uv);
@ -1780,8 +1776,24 @@ Interface.definePanels(function() {
}
}
} else if (element instanceof Cube) {
let overlaps = false;
for (let fkey in element.faces) {
let face_rect = getRectangle(...element.faces[fkey].uv);
if (doRectanglesOverlap(rect, face_rect)) {
overlaps = true;
break;
}
}
if (overlaps) {
Outliner.selected.safePush(element);
}
} else if (element instanceof Mesh) {
let selected_vertices = element.getSelectedVertices(true);
if (!e1.shiftKey) {
selected_vertices.empty();
} else {
selected_vertices.replace(old_selected_vertices[element.uuid]);
}
for (let fkey in element.faces) {
let face = element.faces[fkey];
let vertices = face.getSortedVertices();
@ -1792,13 +1804,16 @@ Interface.definePanels(function() {
let vkey2 = vertices[i] || vertices[0];
if (lineIntersectsReactangle(face.uv[vkey], face.uv[vkey2], [rect.ax, rect.ay], [rect.bx, rect.by])) {
scope.selected_faces.safePush(fkey);
break;
}
if (pointInRectangle(face.uv[vkey], [rect.ax, rect.ay], [rect.bx, rect.by])) {
selected_vertices.safePush(vkey);
}
}
}
}
}
})
if (old_elements) updateSelection();
}
function stop() {
removeEventListeners(document, 'mousemove touchmove', drag);
@ -1858,6 +1873,7 @@ Interface.definePanels(function() {
UVEditor.vue.updateTexture()
},
reverseSelect(event) {
if (this.mode !== 'uv') return;
var offset = $(this.$refs.frame).offset();
event.offsetX = event.clientX - offset.left;
event.offsetY = event.clientY - offset.top;

File diff suppressed because one or more lines are too long

View File

@ -441,6 +441,8 @@
"dialog.sketchfab_uploader.animations": "Animations",
"dialog.sketchfab_uploader.tags": "Tags",
"dialog.sketchfab_uploader.suggested_tags": "Suggested Tags",
"dialog.sketchfab_uploader.category": "Category",
"dialog.sketchfab_uploader.category2": "Category 2",
"dialog.sketchfab_uploader.draft": "Draft",
"dialog.sketchfab_uploader.private": "Private (Pro)",
"dialog.sketchfab_uploader.password": "Password (Pro)",
@ -489,6 +491,7 @@
"layout.color": "Color Scheme",
"layout.css": "Custom CSS",
"layout.documentation": "Documentation",
"layout.restore_backup": "Your customized theme \"%0\" was overwritten. Click here to restore it.",
"layout.color.back": "Back",
"layout.color.back.desc": "Backgrounds and input fields",
"layout.color.dark": "Dark",

View File

@ -1,7 +1,7 @@
{
"name": "Blockbench",
"description": "Model editing and animation software",
"version": "4.0.0-beta.2",
"description": "Low-poly modeling and animation software",
"version": "4.0.0-beta.3",
"license": "GPL-3.0-or-later",
"author": {
"name": "JannisX11",