Billboard editing

This commit is contained in:
JannisX11 2025-03-15 18:55:06 +01:00
parent 43c34a8db5
commit 9ccd5409ba
9 changed files with 102 additions and 73 deletions

View File

@ -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');

View File

@ -15,6 +15,7 @@ new ModelFormat({
]
},
meshes: true,
billboards: true,
rotate_cubes: true,
bone_rig: true,
centered_grid: true,

View File

@ -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
});

View File

@ -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;

View File

@ -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});

View File

@ -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

View File

@ -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');

View File

@ -904,6 +904,7 @@ export class Mesh extends OutlinerElement {
movable: true,
resizable: true,
rotatable: true,
has_pivot: true,
}
}
Mesh.prototype.title = tl('data.mesh');

View File

@ -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;