2021-08-11 05:42:15 +08:00
class TextureMesh extends OutlinerElement {
constructor ( data , uuid ) {
super ( data , uuid )
for ( var key in TextureMesh . properties ) {
TextureMesh . properties [ key ] . reset ( this ) ;
}
if ( data && typeof data === 'object' ) {
this . extend ( data )
}
}
get from ( ) {
return this . origin ;
}
getWorldCenter ( ) {
2021-08-11 23:37:07 +08:00
let m = this . mesh ;
let pos = new THREE . Vector3 ( ) . fromArray ( this . local _pivot ) ;
if ( m ) {
let r = m . getWorldQuaternion ( new THREE . Quaternion ( ) ) ;
pos . applyQuaternion ( r ) ;
pos . add ( THREE . fastWorldPosition ( m , new THREE . Vector3 ( ) ) ) ;
}
2021-08-11 05:42:15 +08:00
return pos ;
}
extend ( object ) {
for ( var key in TextureMesh . properties ) {
TextureMesh . properties [ key ] . merge ( this , object )
}
if ( typeof object . vertices == 'object' ) {
for ( let key in object . vertices ) {
this . vertices [ key ] = object . vertices [ key ] . slice ( ) ;
}
}
this . sanitizeName ( ) ;
return this ;
}
getUndoCopy ( ) {
var copy = new TextureMesh ( this )
copy . uuid = this . uuid ;
delete copy . parent ;
return copy ;
}
getSaveCopy ( ) {
var el = { }
for ( var key in TextureMesh . properties ) {
TextureMesh . properties [ key ] . copy ( this , el )
}
el . type = 'texture_mesh' ;
el . uuid = this . uuid
return el ;
}
}
TextureMesh . prototype . title = tl ( 'data.texture_mesh' ) ;
TextureMesh . prototype . type = 'texture_mesh' ;
TextureMesh . prototype . icon = 'fa fa-puzzle-piece' ;
TextureMesh . prototype . movable = true ;
TextureMesh . prototype . scalable = true ;
TextureMesh . prototype . rotatable = true ;
TextureMesh . prototype . needsUniqueName = false ;
TextureMesh . prototype . menu = new Menu ( [
'group_elements' ,
'_' ,
'copy' ,
'paste' ,
'duplicate' ,
'_' ,
'rename' ,
2021-08-11 23:37:07 +08:00
{ name : 'menu.texture_mesh.texture_name' , icon : 'collections' , condition : ( ) => ! Project . single _texture , click ( context ) {
Blockbench . textPrompt ( 'menu.texture_mesh.texture_name' , context . texture _name , value => {
Undo . initEdit ( { elements : TextureMesh . all } ) ,
TextureMesh . all . forEach ( element => {
element . texture _name = value ;
} ) ;
Undo . finishEdit ( 'Change texture mesh texture name' )
2021-08-11 05:42:15 +08:00
} )
} } ,
'toggle_visibility' ,
'delete'
] ) ;
TextureMesh . prototype . buttons = [
Outliner . buttons . export ,
Outliner . buttons . locked ,
Outliner . buttons . visibility ,
] ;
new Property ( TextureMesh , 'string' , 'name' , { default : 'texture_mesh' } )
2021-08-11 23:37:07 +08:00
new Property ( TextureMesh , 'string' , 'texture_name' )
2021-08-11 05:42:15 +08:00
new Property ( TextureMesh , 'vector' , 'origin' ) ;
2021-08-11 23:37:07 +08:00
new Property ( TextureMesh , 'vector' , 'local_pivot' ) ;
2021-08-11 05:42:15 +08:00
new Property ( TextureMesh , 'vector' , 'rotation' ) ;
new Property ( TextureMesh , 'vector' , 'scale' , { default : [ 1 , 1 , 1 ] } ) ;
new Property ( TextureMesh , 'boolean' , 'visibility' , { default : true } ) ;
OutlinerElement . registerType ( TextureMesh , 'texture_mesh' ) ;
new NodePreviewController ( TextureMesh , {
setup ( element ) {
var mesh = new THREE . Mesh ( new THREE . BoxGeometry ( 1 , 1 , 1 ) , Canvas . emptyMaterials [ 0 ] ) ; // BoxGeometry because BufferGeometry would render black, TODO: investigate
Project . nodes _3d [ element . uuid ] = mesh ;
mesh . name = element . uuid ;
mesh . type = element . type ;
mesh . isElement = true ;
2021-08-13 04:15:47 +08:00
mesh . geometry . setAttribute ( 'highlight' , new THREE . BufferAttribute ( new Uint8Array ( 4 ) , 1 ) ) ;
2021-08-11 05:42:15 +08:00
// Outline
2021-08-13 04:15:47 +08:00
let outline = new THREE . LineSegments ( new THREE . BufferGeometry ( ) , Canvas . outlineMaterial ) ;
2021-08-11 05:42:15 +08:00
outline . no _export = true ;
outline . name = element . uuid + '_outline' ;
outline . visible = element . selected ;
outline . renderOrder = 2 ;
outline . frustumCulled = false ;
mesh . outline = outline ;
mesh . add ( outline ) ;
// Update
this . updateTransform ( element ) ;
this . updateGeometry ( element ) ;
2021-08-13 04:15:47 +08:00
this . updateFaces ( element ) ;
2021-08-11 05:42:15 +08:00
mesh . visible = element . visibility ;
} ,
updateGeometry ( element ) {
let { mesh } = element ;
let position _array = [ ] ;
let indices = [ ] ;
let outline _positions = [ ] ;
2021-08-13 04:15:47 +08:00
let uvs = [ 1 , 1 , 1 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 1 ] ;
let normals = [ ] ;
function addNormal ( x , y , z ) {
normals . push ( x , y , z ) ;
normals . push ( x , y , z ) ;
normals . push ( x , y , z ) ;
normals . push ( x , y , z ) ;
}
let corners = [
[ - Project . texture _width , 0 , 0 ] ,
[ - Project . texture _width , 0 , Project . texture _height ] ,
[ 0 , 0 , Project . texture _height ] ,
[ 0 , 0 , 0 ] ,
]
corners . push ( ... corners . map ( corner => {
return [ corner [ 0 ] , - 1 , corner [ 2 ] ]
} ) )
2021-08-11 05:42:15 +08:00
2021-08-13 04:15:47 +08:00
corners . forEach ( corner => {
position _array . push ( ... corner ) ;
} )
2021-08-11 05:42:15 +08:00
indices . push ( 0 , 1 , 2 , 0 , 2 , 3 ) ;
2021-08-13 04:15:47 +08:00
indices . push ( 4 + 0 , 4 + 2 , 4 + 1 , 4 + 0 , 4 + 3 , 4 + 2 ) ;
addNormal ( 0 , 1 , 0 ) ;
addNormal ( 0 , - 1 , 0 ) ;
outline _positions . push (
... corners [ 0 ] , ... corners [ 1 ] ,
... corners [ 1 ] , ... corners [ 2 ] ,
... corners [ 2 ] , ... corners [ 3 ] ,
... corners [ 3 ] , ... corners [ 0 ] ,
... corners [ 4 ] , ... corners [ 5 ] ,
... corners [ 5 ] , ... corners [ 6 ] ,
... corners [ 6 ] , ... corners [ 7 ] ,
... corners [ 7 ] , ... corners [ 4 ] ,
... corners [ 0 ] , ... corners [ 4 + 0 ] ,
... corners [ 1 ] , ... corners [ 4 + 1 ] ,
... corners [ 2 ] , ... corners [ 4 + 2 ] ,
... corners [ 3 ] , ... corners [ 4 + 3 ]
)
let texture = Texture . getDefault ( ) ;
if ( texture && texture . width ) {
let canvas = document . createElement ( 'canvas' ) ;
let ctx = canvas . getContext ( '2d' ) ;
canvas . width = texture . width ;
canvas . height = texture . height ;
ctx . drawImage ( texture . img , 0 , 0 ) ;
function addFace ( sx , sy , ex , ey , dir ) {
let s = position _array . length / 3 ;
position _array . push ( - sx * Project . texture _width / texture . width , 0 , sy * Project . texture _height / texture . height ) ;
position _array . push ( - sx * Project . texture _width / texture . width , - 1 , sy * Project . texture _height / texture . height ) ;
position _array . push ( - ex * Project . texture _width / texture . width , - 1 , ey * Project . texture _height / texture . height ) ;
position _array . push ( - ex * Project . texture _width / texture . width , 0 , ey * Project . texture _height / texture . height ) ;
if ( dir == 1 ) {
indices . push ( s + 0 , s + 1 , s + 2 , s + 0 , s + 2 , s + 3 ) ;
} else {
indices . push ( s + 0 , s + 2 , s + 1 , s + 0 , s + 3 , s + 2 ) ;
}
if ( sx == ex ) {
sx += 0.1 * - dir ;
ex += 0.4 * - dir ;
sy += 0.1 ;
ey -= 0.1 ;
addNormal ( dir , 0 , 0 ) ;
}
if ( sy == ey ) {
sy += 0.1 * dir ;
ey += 0.4 * dir ;
sx += 0.1 ;
ex -= 0.1 ;
addNormal ( 0 , 0 , dir ) ;
}
uvs . push (
ex / canvas . width , 1 - ( sy / canvas . height ) ,
ex / canvas . width , 1 - ( ey / canvas . height ) ,
sx / canvas . width , 1 - ( ey / canvas . height ) ,
sx / canvas . width , 1 - ( sy / canvas . height ) ,
)
}
let result = ctx . getImageData ( 0 , 0 , canvas . width , canvas . height ) ;
let matrix _1 = [ ] ;
for ( let i = 0 ; i < result . data . length ; i += 4 ) {
matrix _1 . push ( result . data [ i + 3 ] > 140 ? 1 : 0 ) ;
}
let matrix _2 = matrix _1 . slice ( ) ;
for ( var y = 0 ; y < canvas . height ; y ++ ) {
for ( var x = 0 ; x <= canvas . width ; x ++ ) {
let px0 = x == 0 ? 0 : matrix _1 [ y * canvas . width + x - 1 ] ;
let px1 = x == canvas . width ? 0 : matrix _1 [ y * canvas . width + x ] ;
if ( ! px0 !== ! px1 ) {
addFace ( x , y , x , y + 1 , px0 ? 1 : - 1 ) ;
}
}
}
for ( var x = 0 ; x < canvas . width ; x ++ ) {
for ( var y = 0 ; y <= canvas . height ; y ++ ) {
let px0 = y == 0 ? 0 : matrix _2 [ ( y - 1 ) * canvas . width + x ] ;
let px1 = y == canvas . height ? 0 : matrix _2 [ y * canvas . width + x ] ;
if ( ! px0 !== ! px1 ) {
addFace ( x , y , x + 1 , y , px0 ? - 1 : 1 ) ;
}
}
}
}
2021-08-11 05:42:15 +08:00
2021-08-11 23:37:07 +08:00
position _array . forEach ( ( n , i ) => {
let axis = i % 3 ;
position _array [ i ] = n * element . scale [ axis ] + element . local _pivot [ axis ] ;
} )
outline _positions . forEach ( ( n , i ) => {
let axis = i % 3 ;
outline _positions [ i ] = n * element . scale [ axis ] + element . local _pivot [ axis ] ;
} )
2021-08-11 05:42:15 +08:00
mesh . geometry . setAttribute ( 'position' , new THREE . BufferAttribute ( new Float32Array ( position _array ) , 3 ) ) ;
2021-08-13 04:15:47 +08:00
mesh . geometry . setAttribute ( 'highlight' , new THREE . BufferAttribute ( new Uint8Array ( mesh . geometry . attributes . position . count ) , 1 ) ) ;
2021-08-11 05:42:15 +08:00
mesh . geometry . setIndex ( indices ) ;
2021-08-13 04:15:47 +08:00
mesh . geometry . setAttribute ( 'uv' , new THREE . BufferAttribute ( new Float32Array ( uvs ) , 2 ) ) ;
mesh . geometry . setAttribute ( 'normal' , new THREE . BufferAttribute ( new Float32Array ( normals ) , 3 ) ) ;
mesh . geometry . attributes . normal . needsUpdate = true ;
2021-08-11 05:42:15 +08:00
mesh . outline . geometry . setAttribute ( 'position' , new THREE . BufferAttribute ( new Float32Array ( outline _positions ) , 3 ) ) ;
mesh . geometry . computeBoundingBox ( ) ;
mesh . geometry . computeBoundingSphere ( ) ;
} ,
updateFaces ( element ) {
let { mesh } = element ;
if ( Prop . view _mode === 'solid' ) {
mesh . material = Canvas . solidMaterial
} else if ( Prop . view _mode === 'wireframe' ) {
mesh . material = Canvas . wireframeMaterial
} else {
var tex = Texture . getDefault ( ) ;
if ( tex && tex . uuid ) {
mesh . material = Project . materials [ tex . uuid ]
} else {
mesh . material = Canvas . emptyMaterials [ 0 ]
}
}
TextureMesh . preview _controller . updateGeometry ( element ) ;
2021-08-11 23:37:07 +08:00
} ,
updateTransform ( element ) {
let { mesh } = element ;
NodePreviewController . prototype . updateTransform ( element ) ;
mesh . scale . set ( 1 , 1 , 1 ) ;
2021-08-11 05:42:15 +08:00
}
} )
BARS . defineActions ( function ( ) {
new Action ( {
id : 'add_texture_mesh' ,
icon : 'fa-puzzle-piece' ,
category : 'edit' ,
keybind : new Keybind ( { key : 'n' , ctrl : true } ) ,
2021-08-13 04:15:47 +08:00
condition : ( ) => ( Modes . edit && Format . texture _meshes ) ,
2021-08-11 05:42:15 +08:00
click : function ( ) {
Undo . initEdit ( { outliner : true , elements : [ ] , selection : true } ) ;
var base _texture _mesh = new TextureMesh ( ) . init ( )
var group = getCurrentGroup ( ) ;
base _texture _mesh . addTo ( group )
if ( Format . bone _rig ) {
if ( group ) {
var pos1 = group . origin . slice ( )
base _texture _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 _texture _mesh . select ( )
Undo . finishEdit ( 'Add texture mesh' , { outliner : true , elements : selected , selection : true } ) ;
Blockbench . dispatchEvent ( 'add_texture_mesh' , { object : base _texture _mesh } )
Vue . nextTick ( function ( ) {
if ( settings . create _rename . value ) {
base _texture _mesh . rename ( )
}
} )
return base _texture _mesh
}
} )
} )