mirror of
https://github.com/JannisX11/blockbench.git
synced 2025-03-25 17:11:15 +08:00
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:
parent
dd6ad7387a
commit
fa9d8db29d
@ -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 |
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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')) {
|
||||
|
@ -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')) {
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
},
|
||||
|
39
js/io/io.js
39
js/io/io.js
@ -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) => {
|
||||
|
@ -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()
|
||||
|
@ -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
@ -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",
|
||||
|
@ -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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user