mirror of
https://github.com/JannisX11/blockbench.git
synced 2025-04-06 17:31:09 +08:00
Billboard editing
This commit is contained in:
parent
43c34a8db5
commit
9ccd5409ba
@ -109,6 +109,10 @@ interface FormatOptions {
|
||||
* Enable texture meshes
|
||||
*/
|
||||
texture_meshes: boolean
|
||||
/**
|
||||
* Enable billboards
|
||||
*/
|
||||
billboards: boolean
|
||||
/**
|
||||
* Enable locators
|
||||
*/
|
||||
@ -244,6 +248,7 @@ export class ModelFormat implements FormatOptions {
|
||||
integer_size: boolean
|
||||
meshes: boolean
|
||||
texture_meshes: boolean
|
||||
billboards: boolean
|
||||
locators: boolean
|
||||
rotation_limit: boolean
|
||||
rotation_snap: boolean
|
||||
@ -377,7 +382,7 @@ export class ModelFormat implements FormatOptions {
|
||||
Project.export_path = '';
|
||||
Project.unhandled_root_fields = {};
|
||||
|
||||
var old_format = Format;
|
||||
var old_format = Blockbench.Format as ModelFormat;
|
||||
this.select();
|
||||
Modes.options.edit.select()
|
||||
|
||||
@ -389,14 +394,14 @@ export class ModelFormat implements FormatOptions {
|
||||
})
|
||||
}
|
||||
|
||||
if (!Format.per_texture_uv_size && old_format.per_texture_uv_size) {
|
||||
if (!this.per_texture_uv_size && old_format.per_texture_uv_size) {
|
||||
let tex = Texture.getDefault();
|
||||
if (tex) {
|
||||
Project.texture_width = tex.uv_width;
|
||||
Project.texture_height = tex.uv_height;
|
||||
}
|
||||
}
|
||||
if (Format.per_texture_uv_size && !old_format.per_texture_uv_size) {
|
||||
if (this.per_texture_uv_size && !old_format.per_texture_uv_size) {
|
||||
Texture.all.forEach(tex => {
|
||||
tex.uv_width = Project.texture_width;
|
||||
tex.uv_height = Project.texture_height;
|
||||
@ -404,12 +409,12 @@ export class ModelFormat implements FormatOptions {
|
||||
}
|
||||
|
||||
//Bone Rig
|
||||
if (!Format.bone_rig && old_format.bone_rig) {
|
||||
if (!this.bone_rig && old_format.bone_rig) {
|
||||
Group.all.forEach(group => {
|
||||
group.rotation.V3_set(0, 0, 0);
|
||||
})
|
||||
}
|
||||
if (Format.bone_rig && !old_format.bone_rig) {
|
||||
if (this.bone_rig && !old_format.bone_rig) {
|
||||
var loose_stuff = []
|
||||
Outliner.root.forEach(el => {
|
||||
if (el instanceof Group == false) {
|
||||
@ -428,13 +433,13 @@ export class ModelFormat implements FormatOptions {
|
||||
Project.geometry_name = Project.name;
|
||||
}
|
||||
}
|
||||
if (Format.bone_rig) {
|
||||
if (this.bone_rig) {
|
||||
Group.all.forEach(group => {
|
||||
group.createUniqueName();
|
||||
})
|
||||
}
|
||||
if (Format.centered_grid != old_format.centered_grid) {
|
||||
let offset = Format.centered_grid ? -8 : 8;
|
||||
if (this.centered_grid != old_format.centered_grid) {
|
||||
let offset = this.centered_grid ? -8 : 8;
|
||||
Cube.all.forEach(cube => {
|
||||
for (let axis of [0, 2]) {
|
||||
cube.from[axis] += offset;
|
||||
@ -448,7 +453,7 @@ export class ModelFormat implements FormatOptions {
|
||||
})
|
||||
}
|
||||
|
||||
if (!Format.single_texture && old_format.single_texture && Texture.all.length) {
|
||||
if (!this.single_texture && old_format.single_texture && Texture.all.length) {
|
||||
let texture = Texture.getDefault();
|
||||
Outliner.elements.filter((el: OutlinerElement) => 'applyTexture' in el).forEach(el => {
|
||||
// @ts-ignore
|
||||
@ -457,49 +462,57 @@ export class ModelFormat implements FormatOptions {
|
||||
}
|
||||
|
||||
//Rotate Cubes
|
||||
if (!Format.rotate_cubes && old_format.rotate_cubes) {
|
||||
if (!this.rotate_cubes && old_format.rotate_cubes) {
|
||||
Cube.all.forEach(cube => {
|
||||
cube.rotation.V3_set(0, 0, 0)
|
||||
})
|
||||
}
|
||||
|
||||
//Meshes
|
||||
if (!Format.meshes && old_format.meshes) {
|
||||
if (!this.meshes && old_format.meshes) {
|
||||
Mesh.all.slice().forEach(mesh => {
|
||||
mesh.remove()
|
||||
})
|
||||
}
|
||||
|
||||
//Locators
|
||||
if (!Format.locators && old_format.locators) {
|
||||
if (!this.locators && old_format.locators) {
|
||||
Locator.all.slice().forEach(locator => {
|
||||
locator.remove()
|
||||
})
|
||||
}
|
||||
|
||||
//Texture Meshes
|
||||
if (!Format.texture_meshes && old_format.texture_meshes) {
|
||||
if (!this.texture_meshes && old_format.texture_meshes) {
|
||||
TextureMesh.all.slice().forEach(tm => {
|
||||
tm.remove()
|
||||
})
|
||||
}
|
||||
|
||||
//Billboards
|
||||
if (!this.billboards && old_format.billboards) {
|
||||
// @ts-ignore
|
||||
Billboard.all.slice().forEach(b => {
|
||||
b.remove()
|
||||
})
|
||||
}
|
||||
|
||||
//Canvas Limit
|
||||
if (Format.cube_size_limiter && !old_format.cube_size_limiter && !settings.deactivate_size_limit.value) {
|
||||
if (this.cube_size_limiter && !old_format.cube_size_limiter && !settings.deactivate_size_limit.value) {
|
||||
Cube.all.forEach(cube => {
|
||||
Format.cube_size_limiter.move(cube);
|
||||
this.cube_size_limiter.move(cube);
|
||||
})
|
||||
Cube.all.forEach(cube => {
|
||||
Format.cube_size_limiter.clamp(cube);
|
||||
this.cube_size_limiter.clamp(cube);
|
||||
})
|
||||
}
|
||||
|
||||
//Rotation Limit
|
||||
if (Format.rotation_limit && !old_format.rotation_limit && Format.rotate_cubes) {
|
||||
if (this.rotation_limit && !old_format.rotation_limit && this.rotate_cubes) {
|
||||
Cube.all.forEach(cube => {
|
||||
if (!cube.rotation.allEqual(0)) {
|
||||
var axis = (getAxisNumber(cube.rotationAxis())) || 0;
|
||||
var cube_rotation = Format.rotation_snap ? Math.round(cube.rotation[axis]/22.5)*22.5 : cube.rotation[axis];
|
||||
var cube_rotation = this.rotation_snap ? Math.round(cube.rotation[axis]/22.5)*22.5 : cube.rotation[axis];
|
||||
var angle = limitNumber( cube_rotation, -45, 45 );
|
||||
cube.rotation.V3_set(0, 0, 0)
|
||||
cube.rotation[axis] = angle;
|
||||
@ -508,7 +521,7 @@ export class ModelFormat implements FormatOptions {
|
||||
}
|
||||
|
||||
//Animation Mode
|
||||
if (!Format.animation_mode && old_format.animation_mode) {
|
||||
if (!this.animation_mode && old_format.animation_mode) {
|
||||
Animator.animations.length = 0;
|
||||
}
|
||||
|
||||
@ -554,6 +567,7 @@ new Property(ModelFormat, 'boolean', 'stretch_cubes');
|
||||
new Property(ModelFormat, 'boolean', 'integer_size');
|
||||
new Property(ModelFormat, 'boolean', 'meshes');
|
||||
new Property(ModelFormat, 'boolean', 'texture_meshes');
|
||||
new Property(ModelFormat, 'boolean', 'billboards');
|
||||
new Property(ModelFormat, 'boolean', 'locators');
|
||||
new Property(ModelFormat, 'boolean', 'rotation_limit');
|
||||
new Property(ModelFormat, 'boolean', 'rotation_snap');
|
||||
|
@ -15,6 +15,7 @@ new ModelFormat({
|
||||
]
|
||||
},
|
||||
meshes: true,
|
||||
billboards: true,
|
||||
rotate_cubes: true,
|
||||
bone_rig: true,
|
||||
centered_grid: true,
|
||||
|
@ -739,6 +739,13 @@ export function getRotationObjects() {
|
||||
})
|
||||
if (elements.length) return elements;
|
||||
}
|
||||
export function getPivotObjects() {
|
||||
if (Format.bone_rig && Group.first_selected) return Group.multi_selected;
|
||||
let elements = Outliner.selected.filter(element => {
|
||||
return (element.getTypeBehavior('has_pivot')) && (element instanceof Cube == false || Format.rotate_cubes);
|
||||
})
|
||||
if (elements.length) return elements;
|
||||
}
|
||||
export function rotateOnAxis(modify, axis, slider) {
|
||||
var things = getRotationObjects();
|
||||
if (!things) return;
|
||||
@ -1418,7 +1425,7 @@ BARS.defineActions(function() {
|
||||
|
||||
//Origin
|
||||
function moveOriginOnAxis(modify, axis) {
|
||||
var rotation_objects = getRotationObjects()
|
||||
var rotation_objects = getPivotObjects()
|
||||
|
||||
if (rotation_objects && rotation_objects[0] instanceof Group) {
|
||||
let elements_to_update = [];
|
||||
@ -1453,7 +1460,7 @@ BARS.defineActions(function() {
|
||||
description: tl('action.slider_origin.desc', ['X']),
|
||||
color: 'x',
|
||||
category: 'transform',
|
||||
condition: () => (Modes.edit || Modes.animate || Modes.pose) && getRotationObjects() && (Group.first_selected || Outliner.selected.length > Locator.selected.length),
|
||||
condition: () => (Modes.edit || Modes.animate || Modes.pose) && getPivotObjects() && (Group.first_selected || Outliner.selected.length > Locator.selected.length),
|
||||
getInterval: getSpatialInterval,
|
||||
get: function() {
|
||||
if (Format.bone_rig && Group.first_selected) {
|
||||
@ -1480,7 +1487,7 @@ BARS.defineActions(function() {
|
||||
description: tl('action.slider_origin.desc', ['Y']),
|
||||
color: 'y',
|
||||
category: 'transform',
|
||||
condition: () => (Modes.edit || Modes.animate || Modes.pose) && getRotationObjects() && (Group.first_selected || Outliner.selected.length > Locator.selected.length),
|
||||
condition: () => (Modes.edit || Modes.animate || Modes.pose) && getPivotObjects() && (Group.first_selected || Outliner.selected.length > Locator.selected.length),
|
||||
getInterval: getSpatialInterval,
|
||||
get: function() {
|
||||
if (Format.bone_rig && Group.first_selected) {
|
||||
@ -1507,7 +1514,7 @@ BARS.defineActions(function() {
|
||||
description: tl('action.slider_origin.desc', ['Z']),
|
||||
color: 'z',
|
||||
category: 'transform',
|
||||
condition: () => (Modes.edit || Modes.animate || Modes.pose) && getRotationObjects() && (Group.first_selected || Outliner.selected.length > Locator.selected.length),
|
||||
condition: () => (Modes.edit || Modes.animate || Modes.pose) && getPivotObjects() && (Group.first_selected || Outliner.selected.length > Locator.selected.length),
|
||||
getInterval: getSpatialInterval,
|
||||
get: function() {
|
||||
if (Format.bone_rig && Group.first_selected) {
|
||||
@ -2041,6 +2048,7 @@ Object.assign(window, {
|
||||
getSpatialInterval,
|
||||
getRotationInterval,
|
||||
getRotationObjects,
|
||||
getPivotObjects,
|
||||
rotateOnAxis,
|
||||
afterRotateOnAxis
|
||||
});
|
||||
|
@ -3,6 +3,8 @@
|
||||
* modified for Blockbench by jannisx11
|
||||
*/
|
||||
|
||||
import { getPivotObjects, getRotationObjects } from "./transform";
|
||||
|
||||
( function () {
|
||||
|
||||
'use strict';
|
||||
@ -896,8 +898,21 @@
|
||||
if (!scope.dragging) Transformer.rotation_selection.set(0, 0, 0);
|
||||
if (Modes.edit || Modes.pose || Toolbox.selected.id == 'pivot_tool') {
|
||||
if (Transformer.visible) {
|
||||
let rotation_tool = Toolbox.selected.id === 'rotate_tool' || Toolbox.selected.id === 'pivot_tool'
|
||||
let rotation_object = getRotationObjects()
|
||||
let rotation_tool = false;
|
||||
let rotation_object;
|
||||
switch (Toolbox.selected.id) {
|
||||
case 'rotate_tool': {
|
||||
rotation_tool = true;
|
||||
rotation_object = getRotationObjects();
|
||||
break;
|
||||
}
|
||||
case 'pivot_tool': {
|
||||
rotation_tool = true;
|
||||
rotation_object = getPivotObjects();
|
||||
break;
|
||||
}
|
||||
}
|
||||
console.log(rotation_object)
|
||||
if (rotation_object instanceof Array || (!rotation_object && !rotation_tool)) {
|
||||
let arr = rotation_object instanceof Array ? rotation_object : Outliner.selected;
|
||||
rotation_object = undefined;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { THREE } from "../../lib/libs";
|
||||
|
||||
class BillboardFace extends CubeFace {
|
||||
export class BillboardFace extends CubeFace {
|
||||
constructor(data, billboard) {
|
||||
super();
|
||||
this.texture = false;
|
||||
@ -44,15 +44,10 @@ class BillboardFace extends CubeFace {
|
||||
new Property(BillboardFace, 'number', 'rotation', {default: 0});
|
||||
|
||||
|
||||
class Billboard extends OutlinerElement {
|
||||
export class Billboard extends OutlinerElement {
|
||||
constructor(data, uuid) {
|
||||
super(data, uuid)
|
||||
let size = Settings.get('default_cube_size');
|
||||
this.position = [0, 0, 0];
|
||||
this.size = [size, size];
|
||||
this.shade = true;
|
||||
this.mirror_uv = false;
|
||||
this.color = Math.floor(Math.random()*markerColors.length)
|
||||
this.visibility = true;
|
||||
this.autouv = 0;
|
||||
|
||||
@ -60,6 +55,12 @@ class Billboard extends OutlinerElement {
|
||||
Billboard.properties[key].reset(this);
|
||||
}
|
||||
|
||||
let size = Settings.get('default_cube_size');
|
||||
this.position = [0, 0, 0];
|
||||
this.size = [size, size];
|
||||
this.offset = [0, 0];
|
||||
this.color = Math.floor(Math.random()*markerColors.length)
|
||||
|
||||
this.faces = {
|
||||
front: new BillboardFace(null, this),
|
||||
}
|
||||
@ -182,34 +183,6 @@ class Billboard extends OutlinerElement {
|
||||
this.preview_controller.updateFaces(this);
|
||||
this.preview_controller.updateUV(this);
|
||||
}
|
||||
moveVector(arr, axis, update = true) {
|
||||
if (typeof arr == 'number') {
|
||||
let n = arr;
|
||||
arr = [0, 0, 0];
|
||||
arr[axis||0] = n;
|
||||
} else if (arr instanceof THREE.Vector3) {
|
||||
arr = arr.toArray();
|
||||
}
|
||||
let scope = this;
|
||||
let in_box = true;
|
||||
arr.forEach((val, i) => {
|
||||
|
||||
val += scope.from[i];
|
||||
|
||||
let val_before = val;
|
||||
if (Math.abs(val_before - val) >= 1e-4) in_box = false;
|
||||
val -= scope.from[i]
|
||||
|
||||
scope.position[i] += val;
|
||||
})
|
||||
if (update) {
|
||||
this.mapAutoUV()
|
||||
this.preview_controller.updateTransform(this);
|
||||
this.preview_controller.updateGeometry(this);
|
||||
}
|
||||
TickUpdates.selection = true;
|
||||
return in_box;
|
||||
}
|
||||
resize(val, axis, negative) {
|
||||
if (axis == 2) return;
|
||||
if (negative) val = -val;
|
||||
@ -292,6 +265,20 @@ class Billboard extends OutlinerElement {
|
||||
|
||||
new Property(Billboard, 'string', 'name', {default: 'billboard'});
|
||||
new Property(Billboard, 'vector', 'position');
|
||||
new Property(Billboard, 'vector2', 'size', {default: [2, 2]});
|
||||
new Property(Billboard, 'vector2', 'offset', {
|
||||
inputs: {
|
||||
element_panel: {
|
||||
input: {label: 'Offset', type: 'vector', dimensions: 2},
|
||||
onChange() {
|
||||
for (let billboard of Billboard.selected) {
|
||||
Billboard.preview_controller.updateGeometry(billboard);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
new Property(Billboard, 'number', 'color')
|
||||
new Property(Billboard, 'boolean', 'visibility', {default: true});
|
||||
new Property(Billboard, 'boolean', 'locked');
|
||||
new Property(Billboard, 'enum', 'facing_mode', {
|
||||
@ -360,24 +347,25 @@ new NodePreviewController(Billboard, {
|
||||
updateGeometry(element) {
|
||||
let mesh = element.mesh;
|
||||
let half_size = [element.size[0]/2, element.size[1]/2];
|
||||
let offset = element.offset;
|
||||
|
||||
let corners = [
|
||||
-half_size[0], half_size[1], 0,
|
||||
half_size[0], half_size[1], 0,
|
||||
-half_size[0], -half_size[1], 0,
|
||||
half_size[0], -half_size[1], 0,
|
||||
offset[0] - half_size[0], offset[1] + half_size[1], 0,
|
||||
offset[0] + half_size[0], offset[1] + half_size[1], 0,
|
||||
offset[0] - half_size[0], offset[1] - half_size[1], 0,
|
||||
offset[0] + half_size[0], offset[1] - half_size[1], 0,
|
||||
];
|
||||
mesh.geometry.attributes.position.array.set(corners, 0);
|
||||
mesh.geometry.attributes.position.needsUpdate = true;
|
||||
mesh.geometry.computeBoundingBox()
|
||||
mesh.geometry.computeBoundingSphere()
|
||||
mesh.geometry.computeBoundingBox();
|
||||
mesh.geometry.computeBoundingSphere();
|
||||
|
||||
let outline_corners = [
|
||||
-half_size[0], half_size[1], 0,
|
||||
half_size[0], half_size[1], 0,
|
||||
half_size[0], -half_size[1], 0,
|
||||
-half_size[0], -half_size[1], 0,
|
||||
-half_size[0], half_size[1], 0,
|
||||
offset[0] - half_size[0], offset[1] + half_size[1], 0,
|
||||
offset[0] + half_size[0], offset[1] + half_size[1], 0,
|
||||
offset[0] + half_size[0], offset[1] - half_size[1], 0,
|
||||
offset[0] - half_size[0], offset[1] - half_size[1], 0,
|
||||
offset[0] - half_size[0], offset[1] + half_size[1], 0,
|
||||
];
|
||||
mesh.outline.geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(outline_corners), 3));
|
||||
|
||||
@ -687,7 +675,7 @@ BARS.defineActions(function() {
|
||||
id: 'add_billboard',
|
||||
icon: 'bookmark_add',
|
||||
category: 'edit',
|
||||
condition: () => Modes.edit,
|
||||
condition: {features: ['billboards']},
|
||||
click: function () {
|
||||
|
||||
Undo.initEdit({outliner: true, elements: [], selection: true});
|
||||
|
@ -947,6 +947,7 @@ export class Cube extends OutlinerElement {
|
||||
rotatable: true,
|
||||
movable: true,
|
||||
resizable: true,
|
||||
has_pivot: true,
|
||||
cube_rotation_limit: true,
|
||||
cube_size_limit: true,
|
||||
unique_name: false
|
||||
|
@ -437,6 +437,7 @@ export class Group extends OutlinerNode {
|
||||
static behavior = {
|
||||
unique_name: () => Format.bone_rig,
|
||||
rotatable: true,
|
||||
has_pivot: true,
|
||||
}
|
||||
}
|
||||
Group.prototype.title = tl('data.group');
|
||||
|
@ -904,6 +904,7 @@ export class Mesh extends OutlinerElement {
|
||||
movable: true,
|
||||
resizable: true,
|
||||
rotatable: true,
|
||||
has_pivot: true,
|
||||
}
|
||||
}
|
||||
Mesh.prototype.title = tl('data.mesh');
|
||||
|
@ -635,7 +635,7 @@ export class NodePreviewController extends EventSystem {
|
||||
mesh.rotation.z = Math.degToRad(element.rotation[2]);
|
||||
}
|
||||
|
||||
if (element.scalable) {
|
||||
if (element.getTypeBehavior('scalable')) {
|
||||
mesh.scale.x = element.scale[0] || 1e-7;
|
||||
mesh.scale.y = element.scale[1] || 1e-7;
|
||||
mesh.scale.z = element.scale[2] || 1e-7;
|
||||
|
Loading…
x
Reference in New Issue
Block a user