blockbench/js/io/project.js

570 lines
14 KiB
JavaScript
Raw Normal View History

2020-07-16 09:32:59 +02:00
class ModelProject {
2021-05-05 13:54:41 +02:00
constructor(options = {}) {
2020-07-16 09:32:59 +02:00
for (var key in ModelProject.properties) {
ModelProject.properties[key].reset(this);
}
2021-05-05 13:54:41 +02:00
this.uuid = guid();
this.selected = false;
2020-07-16 09:32:59 +02:00
this._box_uv = false;
this._texture_width = 16;
this._texture_height = 16;
2021-05-05 13:54:41 +02:00
this._name = '';
this.saved = true;
this.save_path = '';
this.export_path = '';
this.undo = new UndoSystem();
this.format = options.format || Formats.free;
ModelProject.all.push(this);
2020-07-16 09:32:59 +02:00
}
extend() {
for (var key in ModelProject.properties) {
ModelProject.properties[key].merge(this, object)
}
}
get box_uv() {return Project._box_uv}
2020-04-25 20:25:07 +02:00
set box_uv(v) {
if (Project._box_uv != v) {
Project._box_uv = v;
switchBoxUV(v);
}
2020-07-16 09:32:59 +02:00
}
get texture_width() {return this._texture_width}
get texture_height() {return this._texture_height}
2020-04-25 20:25:07 +02:00
set texture_width(n) {
n = parseInt(n)||16
Vue.nextTick(updateProjectResolution)
2020-07-16 09:32:59 +02:00
this._texture_width = n;
}
2021-05-05 13:54:41 +02:00
get optional_box_uv() {
return Format.optional_box_uv;
}
2020-04-25 20:25:07 +02:00
set texture_height(n) {
n = parseInt(n)||16
Vue.nextTick(updateProjectResolution)
2020-07-16 09:32:59 +02:00
this._texture_height = n;
}
2021-05-05 13:54:41 +02:00
get name() {
return this._name;
}
set name(name) {
this._name = name;
setProjectTitle(this._name);
2020-04-25 20:25:07 +02:00
}
2020-07-16 09:32:59 +02:00
reset() {
2021-05-05 13:54:41 +02:00
return;
if (isApp) updateRecentProjectThumbnail();
2020-07-16 09:32:59 +02:00
Blockbench.dispatchEvent('reset_project');
if (isApp) BedrockEntityManager.reset();
2020-07-16 09:32:59 +02:00
if (Toolbox.selected.id !== 'move_tool') BarItems.move_tool.select();
Screencam.stopTimelapse();
Format = 0;
for (var uuid in OutlinerNode.uuids) {
delete OutlinerNode.uuids[uuid];
2020-10-04 11:13:38 +02:00
}
2020-07-16 09:32:59 +02:00
Outliner.elements.empty();
Outliner.root.purge();
2020-12-31 15:08:35 +01:00
for (var key in Canvas.materials) {
delete Canvas.materials[key];
}
for (var key in Canvas.bones) {
delete Canvas.bones[key];
}
2020-07-16 09:32:59 +02:00
selected.empty();
Group.all.empty();
Group.selected = undefined;
Cube.all.empty();
Cube.selected.empty();
Locator.all.empty();
Locator.selected.empty();
Texture.all.forEach(tex => tex.stopWatcher());
2020-07-16 09:32:59 +02:00
Texture.all.empty();
Texture.selected = undefined;
for (var key in ModelProject.properties) {
ModelProject.properties[key].reset(this)
}
this.texture_width = this.texture_height = 16;
this.overrides = null;
Blockbench.display_settings = display = {};
2021-05-05 13:54:41 +02:00
Project.save_path = Project.export_path = Project.name = '';
Project.saved = true;
2020-07-16 09:32:59 +02:00
Prop.added_models = 0;
Canvas.updateAll();
Outliner.vue.$forceUpdate();
2020-09-21 23:23:42 +02:00
Interface.Panels.textures.inside_vue.$forceUpdate();
2020-07-16 09:32:59 +02:00
Undo.history.empty();
Undo.index = 0;
Undo.current_save = null;
Painter.current = {};
Animator.animations.purge();
Timeline.animators.purge();
Animation.selected = undefined;
delete Animator.motion_trail_lock;
2020-07-16 09:32:59 +02:00
$('#var_placeholder_area').val('');
}
2021-05-05 13:54:41 +02:00
/*
----- THINGS TO SAVE --------
textures
elements
groups
selection
display settings
format
----- SAVE EXTERNALLY --------
scene
materials,
bones,
3d elements
*/
openSettings() {
BarItems.project_window.click();
}
select() {
if (this.selected) return;
if (Project) {
Project.unselect()
} else {
Interface.tab_bar.new_tab.visible = false;
2021-05-05 13:54:41 +02:00
}
Project = this;
Undo = this.undo;
this.selected = true;
this.format.select();
2021-07-07 13:08:56 +02:00
setStartScreen(!Project)
2021-05-05 13:54:41 +02:00
}
unselect() {
this.selected = false;
}
async close(force) {
if (force || showSaveDialog()) {
if (isApp) await updateRecentProjectThumbnail();
Blockbench.dispatchEvent('close_project');
ModelProject.all.remove(this);
ModelProject.all[0].select();
return true;
} else {
return false;
}
}
2020-04-25 20:25:07 +02:00
}
2020-07-16 09:32:59 +02:00
new Property(ModelProject, 'string', 'name', {
label: 'dialog.project.name'
});
new Property(ModelProject, 'string', 'parent', {
label: 'dialog.project.parent',
condition: {formats: ['java_block']
}});
new Property(ModelProject, 'string', 'geometry_name', {
label: 'dialog.project.geoname',
condition: () => Format.bone_rig
});
new Property(ModelProject, 'string', 'modded_entity_version', {
label: 'dialog.project.modded_entity_version',
default: '1.15',
condition: {formats: ['modded_entity']},
options() {
let options = {}
for (var key in Codecs.modded_entity.templates) {
if (Codecs.modded_entity.templates[key] instanceof Function == false) {
options[key] = Codecs.modded_entity.templates[key].name;
}
}
return options;
}
});
new Property(ModelProject, 'boolean', 'ambientocclusion', {
label: 'dialog.project.ao',
default: true,
condition: {formats: ['java_block']}
});
new Property(ModelProject, 'boolean', 'front_gui_light', {
exposed: false,
condition: () => Format.display_mode});
new Property(ModelProject, 'vector', 'visible_box', {
exposed: false,
default: [1, 1, 0]
});
2021-05-05 13:54:41 +02:00
ModelProject.all = [];
let Project = 0;// = new ModelProject();
2020-04-25 20:25:07 +02:00
2021-05-05 13:54:41 +02:00
function newProject(format) {
new ModelProject({format}).select();
Modes.options.edit.select();
Blockbench.dispatchEvent('new_project');
return true;
2020-04-25 20:25:07 +02:00
}
2021-01-23 23:24:14 +01:00
// Resolution
function setProjectResolution(width, height, modify_uv) {
if (Project.texture_width / width != Project.texture_width / height) {
modify_uv = false;
}
Undo.initEdit({uv_mode: true, elements: Cube.all, uv_only: true})
let old_res = {
x: Project.texture_width,
y: Project.texture_height
}
Project.texture_width = width;
Project.texture_height = height;
if (modify_uv) {
var multiplier = [
Project.texture_width/old_res.x,
Project.texture_height/old_res.y
]
function shiftCube(cube, axis) {
if (Project.box_uv) {
cube.uv_offset[axis] *= multiplier[axis];
} else {
for (var face in cube.faces) {
var uv = cube.faces[face];
uv[axis] *= multiplier[axis];
uv[axis+2] *= multiplier[axis];
}
}
}
if (old_res.x != Project.texture_width && Math.areMultiples(old_res.x, Project.texture_width)) {
Cube.all.forEach(cube => shiftCube(cube, 0));
}
if (old_res.y != Project.texture_height && Math.areMultiples(old_res.x, Project.texture_width)) {
Cube.all.forEach(cube => shiftCube(cube, 1));
}
}
Undo.finishEdit('Changed project resolution')
Canvas.updateAllUVs()
if (selected.length) {
main_uv.loadData()
}
}
function updateProjectResolution() {
document.querySelector('#project_resolution_status').textContent = `${Project.texture_width}${Project.texture_height}`;
if (Texture.selected) {
// Update animated textures
Texture.selected.height++;
Texture.selected.height--;
}
2021-01-23 23:24:14 +01:00
}
2021-07-07 13:08:56 +02:00
function setStartScreen(state) {
document.getElementById('start_screen').style.display = state ? 'block' : 'none';
document.getElementById('work_screen').style.display = state ? 'none' : 'grid';
}
2021-05-05 13:54:41 +02:00
onVueSetup(() => {
Interface.tab_bar = new Vue({
el: '#tab_bar',
data: {
projects: ModelProject.all,
2021-05-08 16:52:49 +02:00
drag_target_index: null,
drag_position_index: null,
new_tab: {
2021-07-07 13:08:56 +02:00
name: tl('projects.new_tab'),
saved: true,
selected: true,
uuid: guid(),
visible: true,
2021-07-07 13:08:56 +02:00
is_new_tab: true,
close: () => {
Interface.tab_bar.new_tab.visible = false;
},
select() {
if (Project) {
Project.unselect()
}
Project = 0;
Interface.tab_bar.new_tab.selected = true;
},
openSettings() {}
}
},
computed: {
tabs() {
let tabs = this.projects.slice();
if (this.new_tab.visible) {
tabs.push(this.new_tab);
}
return tabs;
}
},
methods: {
openNewTab() {
this.new_tab.visible = true;
this.new_tab.select();
2021-07-07 13:08:56 +02:00
setStartScreen(true);
2021-05-08 16:52:49 +02:00
},
dragTab(tab, e1) {
convertTouchEvent(e1);
let scope = this;
let active = false;
let timeout;
let last_event = e1;
let tab_node = e1.target;
if (!tab_node.classList.contains('project_tab') || ModelProject.all.indexOf(tab) < 0) return;
let activate = () => {
this.drag_target_index = ModelProject.all.indexOf(tab);
this.drag_position_index = 0;
if (open_menu) open_menu.hide();
active = true;
}
function move(e2) {
convertTouchEvent(e2);
let offset = e2.clientX - e1.clientX;
if (!active) {
let distance = Math.abs(offset);
if (Blockbench.isTouch) {
if (distance > 14 && timeout) {
clearTimeout(timeout);
timeout = null;
} else {
document.getElementById('tab_bar').scrollLeft += last_event.clientX - e2.clientX;
}
} else if (distance > 5) {
activate();
}
} else {
if (e2) e2.preventDefault();
tab_node.style.left = `${offset}px`;
let index_offset = Math.trunc((e2.clientX - e1.clientX) / tab_node.clientWidth);
scope.drag_position_index = scope.drag_target_index + index_offset;
console.log('drag_target_index', scope.drag_target_index, 'drag_position_index', scope.drag_position_index)
}
last_event = e2;
}
function off(e2) {
let {drag_target_index} = scope;
removeEventListeners(document, 'mousemove touchmove', move);
removeEventListeners(document, 'mouseup touchend', off);
tab_node.style.left = null;
scope.drag_target_index = null;
scope.drag_position_index = null;
if (Blockbench.isTouch) clearTimeout(timeout);
if (active && !open_menu) {
convertTouchEvent(e2);
let index_offset = Math.trunc((e2.clientX - e1.clientX) / tab_node.clientWidth);
if (index_offset) {
ModelProject.all.splice(drag_target_index, 1);
ModelProject.all.splice(drag_target_index + index_offset, 0, tab);
}
}
}
if (Blockbench.isTouch) {
timeout = setTimeout(() => {
active = true;
move(e1);
}, 320)
}
addEventListeners(document, 'mousemove touchmove', move, {passive: false});
addEventListeners(document, 'mouseup touchend', off, {passive: false});
},
mouseUp(tab, e1) {
if (e1.button === 1) {
tab.close()
}
}
2021-05-05 13:54:41 +02:00
}
})
})
2020-04-25 20:25:07 +02:00
BARS.defineActions(function() {
new Action('project_window', {
icon: 'featured_play_list',
category: 'file',
condition: () => Format,
click: function () {
2020-07-16 09:32:59 +02:00
let form = {
format: {type: 'info', label: 'data.format', text: Format.name||'unknown'}
}
for (var key in ModelProject.properties) {
let property = ModelProject.properties[key];
if (property.exposed == false || !Condition(property.condition)) continue;
let entry = form[property.name] = {
label: property.label,
2020-12-30 22:27:12 +01:00
description: property.description,
2020-07-16 09:32:59 +02:00
value: Project[property.name],
type: property.type
}
if (property.type == 'boolean') entry.type = 'checkbox';
if (property.type == 'string') entry.type = 'text';
if (property.options) {
entry.options = typeof property.options == 'function' ? property.options() : property.options;
entry.type = 'select';
2020-04-25 20:25:07 +02:00
}
}
2020-07-16 09:32:59 +02:00
form.uv_mode = {
label: 'dialog.project.uv_mode',
type: 'select',
condition: Format.optional_box_uv,
options: {
face_uv: 'dialog.project.uv_mode.face_uv',
box_uv: 'dialog.project.uv_mode.box_uv',
},
value: Project.box_uv ? 'box_uv' : 'face_uv',
2020-07-16 09:32:59 +02:00
};
form.texture_size = {
label: 'dialog.project.texture_size',
type: 'vector',
dimensions: 2,
value: [Project.texture_width, Project.texture_height],
2020-07-16 09:32:59 +02:00
min: 1
};
2020-04-25 20:25:07 +02:00
var dialog = new Dialog({
id: 'project',
title: 'dialog.project.title',
2020-07-16 09:32:59 +02:00
width: 500,
form,
2020-04-25 20:25:07 +02:00
onConfirm: function(formResult) {
var save;
let box_uv = formResult.uv_mode == 'box_uv';
2020-12-02 11:32:08 +01:00
let texture_width = Math.clamp(formResult.texture_size[0], 1, Infinity);
let texture_height = Math.clamp(formResult.texture_size[1], 1, Infinity);
if (Project.box_uv != box_uv ||
Project.texture_width != texture_width ||
Project.texture_height != texture_height
2020-04-25 20:25:07 +02:00
) {
if (!Project.box_uv && !box_uv
&& (Project.texture_width != texture_width
|| Project.texture_height != texture_height)
2020-04-25 20:25:07 +02:00
) {
save = Undo.initEdit({uv_only: true, elements: Cube.all, uv_mode: true})
Cube.all.forEach(cube => {
for (var key in cube.faces) {
var uv = cube.faces[key].uv;
uv[0] *= texture_width / Project.texture_width;
uv[2] *= texture_width / Project.texture_width;
uv[1] *= texture_height / Project.texture_height;
uv[3] *= texture_height / Project.texture_height;
2020-04-25 20:25:07 +02:00
}
})
} else {
save = Undo.initEdit({uv_mode: true})
}
Project.texture_width = texture_width;
Project.texture_height = texture_height;
2020-04-25 20:25:07 +02:00
if (Format.optional_box_uv) Project.box_uv = box_uv;
2020-04-25 20:25:07 +02:00
Canvas.updateAllUVs()
updateSelection()
}
2020-07-16 09:32:59 +02:00
for (var key in ModelProject.properties) {
ModelProject.properties[key].merge(Project, formResult);
}
2020-04-25 20:25:07 +02:00
if (save) {
2021-06-06 09:28:22 +02:00
Undo.finishEdit('Change project UV settings')
2020-04-25 20:25:07 +02:00
}
Blockbench.dispatchEvent('update_project_settings', formResult);
2020-04-25 20:25:07 +02:00
BARS.updateConditions()
if (EditSession.active) {
EditSession.sendAll('change_project_meta', JSON.stringify(Project));
}
2020-04-25 20:25:07 +02:00
dialog.hide()
}
})
dialog.show()
}
})
/*
2020-04-25 20:25:07 +02:00
new Action('close_project', {
icon: 'cancel_presentation',
category: 'file',
condition: () => (!EditSession.active || EditSession.hosting) && Format,
click: function () {
if (showSaveDialog()) {
resetProject()
Modes.options.start.select()
Modes.vue.$forceUpdate()
Blockbench.dispatchEvent('close_project');
}
}
})*/
2020-04-25 20:25:07 +02:00
new Action('convert_project', {
icon: 'fas.fa-file-import',
category: 'file',
condition: () => (!EditSession.active || EditSession.hosting),
click: function () {
var options = {};
for (var key in Formats) {
if (key !== Format.id && key !== 'skin') {
options[key] = Formats[key].name;
}
}
var dialog = new Dialog({
id: 'convert_project',
title: 'dialog.convert_project.title',
width: 540,
form: {
text: {type: 'info', text: 'dialog.convert_project.text'},
current: {type: 'info', label: 'dialog.convert_project.current_format', text: Format.name || '-'},
format: {
2020-04-25 20:25:07 +02:00
label: 'data.format',
type: 'select',
default: Format.id,
options,
},
},
onConfirm: function(formResult) {
var format = Formats[formResult.format]
if (format && format != Format) {
format.convertTo()
}
dialog.hide()
}
})
dialog.show()
}
})
})