mirror of
https://github.com/JannisX11/blockbench.git
synced 2025-01-18 15:26:19 +08:00
421 lines
13 KiB
JavaScript
421 lines
13 KiB
JavaScript
class MeshFace {
|
|
constructor(mesh, data) {
|
|
this.mesh = mesh;
|
|
//this.vertices = [];
|
|
//this.normal = [0, 1, 0];
|
|
this.texture = false;
|
|
this.uv = {};
|
|
for (var key in MeshFace.properties) {
|
|
MeshFace.properties[key].reset(this);
|
|
}
|
|
this.extend(data);
|
|
}
|
|
extend(data) {
|
|
for (var key in MeshFace.properties) {
|
|
MeshFace.properties[key].merge(this, data)
|
|
}
|
|
if (data.texture === null) {
|
|
this.texture = null;
|
|
} else if (data.texture === false) {
|
|
this.texture = false;
|
|
} else if (Texture.all.includes(data.texture)) {
|
|
this.texture = data.texture.uuid;
|
|
} else if (typeof data.texture === 'string') {
|
|
Merge.string(this, data, 'texture')
|
|
}
|
|
return this;
|
|
}
|
|
getSaveCopy() {
|
|
var copy = {
|
|
uv: this.uv
|
|
};
|
|
for (var key in MeshFace.properties) {
|
|
if (this[key] != MeshFace.properties[key].default) MeshFace.properties[key].copy(this, copy);
|
|
}
|
|
var tex = this.getTexture()
|
|
if (tex === null) {
|
|
copy.texture = null;
|
|
} else if (tex instanceof Texture) {
|
|
copy.texture = Texture.all.indexOf(tex)
|
|
}
|
|
return copy;
|
|
}
|
|
getUndoCopy() {
|
|
var copy = new MeshFace(this.mesh, this);
|
|
delete copy.mesh;
|
|
return copy;
|
|
}
|
|
reset() {
|
|
for (var key in Mesh.properties) {
|
|
Mesh.properties[key].reset(this);
|
|
}
|
|
this.texture = false;
|
|
return this;
|
|
}
|
|
getTexture() {
|
|
if (Format.single_texture) {
|
|
return Texture.getDefault();
|
|
}
|
|
if (typeof this.texture === 'string') {
|
|
return Texture.all.findInArray('uuid', this.texture)
|
|
} else {
|
|
return this.texture;
|
|
}
|
|
}
|
|
}
|
|
new Property(MeshFace, 'array', 'vertices', {default: 0});
|
|
new Property(MeshFace, 'vector', 'normal', {default: 0});
|
|
|
|
|
|
class Mesh extends OutlinerElement {
|
|
constructor(data, uuid) {
|
|
super(data, uuid)
|
|
|
|
this.vertices = {};
|
|
this.faces = [];
|
|
|
|
if (!data.vertices) {
|
|
this.addVertices([1, 1, 1], [1, 1, 0], [1, 0, 1], [1, 0, 0], [0, 1, 1], [0, 1, 0], [0, 0, 1], [0, 0, 0]);
|
|
let vertex_keys = Object.keys(this.vertices);
|
|
this.faces.push(new MeshFace( this, {vertices: [vertex_keys[0], vertex_keys[1], vertex_keys[2], vertex_keys[3]]} )); // East
|
|
this.faces.push(new MeshFace( this, {vertices: [vertex_keys[4], vertex_keys[5], vertex_keys[6], vertex_keys[7]]} )); // West
|
|
this.faces.push(new MeshFace( this, {vertices: [vertex_keys[0], vertex_keys[1], vertex_keys[4], vertex_keys[5]]} )); // Up
|
|
this.faces.push(new MeshFace( this, {vertices: [vertex_keys[2], vertex_keys[3], vertex_keys[6], vertex_keys[7]]} )); // Down
|
|
this.faces.push(new MeshFace( this, {vertices: [vertex_keys[0], vertex_keys[2], vertex_keys[4], vertex_keys[6]]} )); // South
|
|
this.faces.push(new MeshFace( this, {vertices: [vertex_keys[1], vertex_keys[3], vertex_keys[5], vertex_keys[7]]} )); // North
|
|
}
|
|
for (var key in Mesh.properties) {
|
|
Mesh.properties[key].reset(this);
|
|
}
|
|
if (data && typeof data === 'object') {
|
|
this.extend(data)
|
|
}
|
|
}
|
|
get from() {
|
|
return this.origin;
|
|
}
|
|
get vertice_list() {
|
|
return Object.keys(this.vertices).map(key => this.vertices[key]);
|
|
}
|
|
getWorldCenter() {
|
|
var m = this.mesh;
|
|
var pos = new THREE.Vector3()
|
|
|
|
let vertice_list = this.vertice_list;
|
|
vertice_list.forEach(vector => {
|
|
pos.x += vector[0];
|
|
pos.y += vector[1];
|
|
pos.z += vector[2];
|
|
})
|
|
pos.x /= vertice_list.length;
|
|
pos.y /= vertice_list.length;
|
|
pos.z /= vertice_list.length;
|
|
|
|
if (m) {
|
|
var r = m.getWorldQuaternion(new THREE.Quaternion())
|
|
pos.applyQuaternion(r)
|
|
pos.add(THREE.fastWorldPosition(m, new THREE.Vector3()))
|
|
}
|
|
return pos;
|
|
}
|
|
addVertices(...vectors) {
|
|
vectors.forEach(vector => {
|
|
let key;
|
|
while (!key || this.vertices[key]) {
|
|
key = bbuid(4);
|
|
}
|
|
this.vertices[key] = [...vector];
|
|
})
|
|
}
|
|
extend(object) {
|
|
for (var key in Mesh.properties) {
|
|
Mesh.properties[key].merge(this, object)
|
|
}
|
|
this.sanitizeName();
|
|
return this;
|
|
}
|
|
get mesh() {
|
|
return Project.nodes_3d[this.uuid];
|
|
}
|
|
getUndoCopy() {
|
|
var copy = new Mesh(this)
|
|
copy.uuid = this.uuid;
|
|
delete copy.parent;
|
|
return copy;
|
|
}
|
|
getSaveCopy() {
|
|
var el = {}
|
|
for (var key in Mesh.properties) {
|
|
Mesh.properties[key].copy(this, el)
|
|
}
|
|
el.uuid = this.uuid
|
|
return el;
|
|
}
|
|
}
|
|
Mesh.prototype.title = tl('data.mesh');
|
|
Mesh.prototype.type = 'mesh';
|
|
Mesh.prototype.icon = 'fa far fa-gem';
|
|
Mesh.prototype.movable = true;
|
|
Mesh.prototype.resizable = false;
|
|
Mesh.prototype.rotatable = true;
|
|
Mesh.prototype.needsUniqueName = false;
|
|
Mesh.prototype.menu = new Menu([
|
|
'group_elements',
|
|
'_',
|
|
'copy',
|
|
'paste',
|
|
'duplicate',
|
|
'_',
|
|
'rename',
|
|
{name: 'menu.cube.color', icon: 'color_lens', children: [
|
|
{icon: 'bubble_chart', color: markerColors[0].standard, name: 'cube.color.'+markerColors[0].name, click: function(cube) {cube.forSelected(function(obj){obj.setColor(0)}, 'change color')}},
|
|
{icon: 'bubble_chart', color: markerColors[1].standard, name: 'cube.color.'+markerColors[1].name, click: function(cube) {cube.forSelected(function(obj){obj.setColor(1)}, 'change color')}},
|
|
{icon: 'bubble_chart', color: markerColors[2].standard, name: 'cube.color.'+markerColors[2].name, click: function(cube) {cube.forSelected(function(obj){obj.setColor(2)}, 'change color')}},
|
|
{icon: 'bubble_chart', color: markerColors[3].standard, name: 'cube.color.'+markerColors[3].name, click: function(cube) {cube.forSelected(function(obj){obj.setColor(3)}, 'change color')}},
|
|
{icon: 'bubble_chart', color: markerColors[4].standard, name: 'cube.color.'+markerColors[4].name, click: function(cube) {cube.forSelected(function(obj){obj.setColor(4)}, 'change color')}},
|
|
{icon: 'bubble_chart', color: markerColors[5].standard, name: 'cube.color.'+markerColors[5].name, click: function(cube) {cube.forSelected(function(obj){obj.setColor(5)}, 'change color')}},
|
|
{icon: 'bubble_chart', color: markerColors[6].standard, name: 'cube.color.'+markerColors[6].name, click: function(cube) {cube.forSelected(function(obj){obj.setColor(6)}, 'change color')}},
|
|
{icon: 'bubble_chart', color: markerColors[7].standard, name: 'cube.color.'+markerColors[7].name, click: function(cube) {cube.forSelected(function(obj){obj.setColor(7)}, 'change color')}}
|
|
]},
|
|
{name: 'menu.cube.texture', icon: 'collections', condition: () => !Project.single_texture, children: function() {
|
|
var arr = [
|
|
{icon: 'crop_square', name: 'menu.cube.texture.blank', click: function(cube) {
|
|
cube.forSelected(function(obj) {
|
|
obj.applyTexture(false, true)
|
|
}, 'texture blank')
|
|
}},
|
|
{icon: 'clear', name: 'menu.cube.texture.transparent', click: function(cube) {
|
|
cube.forSelected(function(obj) {
|
|
obj.applyTexture(null, true)
|
|
}, 'texture transparent')
|
|
}}
|
|
]
|
|
Texture.all.forEach(function(t) {
|
|
arr.push({
|
|
name: t.name,
|
|
icon: (t.mode === 'link' ? t.img : t.source),
|
|
click: function(cube) {
|
|
cube.forSelected(function(obj) {
|
|
obj.applyTexture(t, true)
|
|
}, 'apply texture')
|
|
}
|
|
})
|
|
})
|
|
return arr;
|
|
}},
|
|
'toggle_visibility',
|
|
'delete'
|
|
]);
|
|
Mesh.prototype.buttons = [
|
|
Outliner.buttons.export,
|
|
Outliner.buttons.locked,
|
|
Outliner.buttons.visibility,
|
|
];
|
|
|
|
new Property(Mesh, 'string', 'name', {default: 'mesh'})
|
|
new Property(Mesh, 'number', 'color', {default: Math.floor(Math.random()*8)});
|
|
new Property(Mesh, 'vector', 'origin');
|
|
new Property(Mesh, 'vector', 'rotation');
|
|
new Property(Mesh, 'boolean', 'visibility', {default: true});
|
|
|
|
OutlinerElement.registerType(Mesh, 'mesh');
|
|
|
|
new NodePreviewController(Mesh, {
|
|
setup(element) {
|
|
var mesh = new THREE.Mesh(new THREE.BufferGeometry(1, 1, 1), emptyMaterials[0]);
|
|
Project.nodes_3d[element.uuid] = mesh;
|
|
mesh.name = element.uuid;
|
|
mesh.type = element.type;
|
|
mesh.isElement = true;
|
|
|
|
mesh.geometry.setAttribute('highlight', new THREE.BufferAttribute(new Uint8Array(24).fill(1), 1));
|
|
|
|
this.updateTransform(element);
|
|
this.updateGeometry(element);
|
|
this.updateFaces(element);
|
|
|
|
if (Prop.view_mode === 'textured') {
|
|
this.updateUV(element);
|
|
}
|
|
mesh.visible = element.visibility;
|
|
|
|
let material = new THREE.PointsMaterial({size: 5, sizeAttenuation: false});
|
|
let points = new THREE.Points(mesh.geometry, material)
|
|
mesh.add(points);
|
|
//Canvas.buildOutline(element);
|
|
},
|
|
updateGeometry(element) {
|
|
|
|
let {mesh} = element;
|
|
let position_array = [];
|
|
let position_indices = [];
|
|
let indices = [];
|
|
|
|
for (let key in element.vertices) {
|
|
let vector = element.vertices[key];
|
|
position_indices.push(key);
|
|
position_array.push(...vector);
|
|
}
|
|
|
|
element.faces.forEach(face => {
|
|
if (face.vertices.length == 3) {
|
|
// Tri
|
|
face.vertices.forEach(key => {
|
|
let index = position_indices.indexOf(key);
|
|
indices.push(index);
|
|
})
|
|
} else if (face.vertices.length == 4) {
|
|
// Quad
|
|
indices.push(position_indices.indexOf(face.vertices[0]));
|
|
indices.push(position_indices.indexOf(face.vertices[1]));
|
|
indices.push(position_indices.indexOf(face.vertices[2]));
|
|
|
|
indices.push(position_indices.indexOf(face.vertices[1]));
|
|
indices.push(position_indices.indexOf(face.vertices[2]));
|
|
indices.push(position_indices.indexOf(face.vertices[3]));
|
|
}
|
|
})
|
|
|
|
mesh.geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(position_array), 3));
|
|
mesh.geometry.setIndex( indices );
|
|
|
|
//Canvas.getOutlineMesh(mesh, mesh.outline)
|
|
mesh.geometry.computeBoundingBox()
|
|
mesh.geometry.computeBoundingSphere()
|
|
},
|
|
updateFaces(element) {
|
|
let {mesh} = element;
|
|
let {geometry} = mesh;
|
|
|
|
/*
|
|
if (!geometry.all_faces) geometry.all_faces = geometry.groups.slice();
|
|
geometry.groups.empty();
|
|
|
|
geometry.all_faces.forEach(face => {
|
|
let bb_face = element.faces[Canvas.face_order[face.materialIndex]];
|
|
|
|
if (bb_face && bb_face.texture === null && geometry.groups.includes(face)) {
|
|
geometry.groups.remove(face);
|
|
} else
|
|
if (bb_face && bb_face.texture !== null && !geometry.groups.includes(face)) {
|
|
geometry.groups.push(face);
|
|
}
|
|
})
|
|
if (geometry.groups.length == 0) {
|
|
// Keep down face if no faces enabled
|
|
geometry.groups.push(geometry.all_faces[6], geometry.all_faces[7]);
|
|
}
|
|
|
|
|
|
|
|
if (Prop.view_mode === 'solid') {
|
|
mesh.material = Canvas.solidMaterial
|
|
|
|
} else if (Prop.view_mode === 'wireframe') {
|
|
mesh.material = Canvas.wireframeMaterial
|
|
|
|
} else if (Format.single_texture && Texture.all.length >= 2 && Texture.all.find(t => t.render_mode == 'layered')) {
|
|
mesh.material = Canvas.getLayeredMaterial();
|
|
|
|
} else if (Format.single_texture) {
|
|
let tex = Texture.getDefault();
|
|
mesh.material = tex ? tex.getMaterial() : emptyMaterials[element.color];
|
|
|
|
} else {
|
|
var materials = []
|
|
Canvas.face_order.forEach(function(face) {
|
|
|
|
if (cube.faces[face].texture === null) {
|
|
materials.push(Canvas.transparentMaterial)
|
|
|
|
} else {
|
|
var tex = cube.faces[face].getTexture()
|
|
if (tex && tex.uuid) {
|
|
materials.push(Project.materials[tex.uuid])
|
|
} else {
|
|
materials.push(emptyMaterials[cube.color])
|
|
}
|
|
}
|
|
})
|
|
if (materials.allEqual(materials[0])) materials = materials[0];
|
|
mesh.material = materials
|
|
}*/
|
|
},
|
|
updateUV(cube, animation = true) {
|
|
if (Prop.view_mode !== 'textured') return;
|
|
var mesh = cube.mesh
|
|
if (mesh === undefined || !mesh.geometry) return;
|
|
return;
|
|
|
|
|
|
var stretch = 1
|
|
var frame = 0
|
|
|
|
Canvas.face_order.forEach((face, fIndex) => {
|
|
|
|
if (cube.faces[face].texture == null) return;
|
|
|
|
stretch = 1;
|
|
frame = 0;
|
|
let tex = cube.faces[face].getTexture();
|
|
if (tex instanceof Texture && tex.frameCount !== 1) {
|
|
stretch = tex.frameCount
|
|
if (animation === true && tex.currentFrame) {
|
|
frame = tex.currentFrame
|
|
}
|
|
}
|
|
Canvas.updateUVFace(mesh.geometry.attributes.uv, fIndex, cube.faces[face], frame, stretch)
|
|
})
|
|
|
|
mesh.geometry.attributes.uv.needsUpdate = true;
|
|
return mesh.geometry;
|
|
}
|
|
})
|
|
|
|
BARS.defineActions(function() {
|
|
new Action({
|
|
id: 'add_mesh',
|
|
icon: 'fa-gem',
|
|
category: 'edit',
|
|
keybind: new Keybind({key: 'n', ctrl: true}),
|
|
condition: () => (Modes.edit && Format.meshes),
|
|
click: function () {
|
|
|
|
Undo.initEdit({outliner: true, elements: [], selection: true});
|
|
var base_mesh = new Mesh({
|
|
autouv: (settings.autouv.value ? 1 : 0)
|
|
}).init()
|
|
var group = getCurrentGroup();
|
|
base_mesh.addTo(group)
|
|
|
|
if (Texture.all.length && Format.single_texture) {
|
|
for (var face in base_mesh.faces) {
|
|
base_mesh.faces[face].texture = Texture.getDefault().uuid
|
|
}
|
|
main_uv.loadData()
|
|
}
|
|
if (Format.bone_rig) {
|
|
if (group) {
|
|
var pos1 = group.origin.slice()
|
|
base_mesh.extend({
|
|
from:[ pos1[0]-0, pos1[1]-0, pos1[2]-0 ],
|
|
to:[ pos1[0]+1, pos1[1]+1, pos1[2]+1 ],
|
|
origin: pos1.slice()
|
|
})
|
|
}
|
|
}
|
|
|
|
if (Group.selected) Group.selected.unselect()
|
|
base_mesh.select()
|
|
Undo.finishEdit('Add mesh', {outliner: true, elements: selected, selection: true});
|
|
Blockbench.dispatchEvent( 'add_mesh', {object: base_mesh} )
|
|
|
|
Vue.nextTick(function() {
|
|
if (settings.create_rename.value) {
|
|
base_mesh.rename()
|
|
}
|
|
})
|
|
return base_mesh
|
|
}
|
|
})
|
|
}) |