mirror of
https://github.com/JannisX11/blockbench.git
synced 2024-12-09 04:50:29 +08:00
1293 lines
33 KiB
JavaScript
1293 lines
33 KiB
JavaScript
|
|
function getRescalingFactor(angle) {
|
|
switch (Math.abs(angle)) {
|
|
case 0:
|
|
return 1.4142
|
|
break;
|
|
case 22.5:
|
|
return 1.0824
|
|
break;
|
|
case 67.5:
|
|
return 1.0824
|
|
break;
|
|
case 45:
|
|
return 1.4142
|
|
break;
|
|
default:
|
|
return 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
const Reusable = {
|
|
vec1: new THREE.Vector3(),
|
|
vec2: new THREE.Vector3(),
|
|
vec3: new THREE.Vector3(),
|
|
vec4: new THREE.Vector3(),
|
|
vec5: new THREE.Vector3(),
|
|
vec6: new THREE.Vector3(),
|
|
vec7: new THREE.Vector3(),
|
|
vec8: new THREE.Vector3(),
|
|
|
|
quat1: new THREE.Quaternion(),
|
|
quat2: new THREE.Quaternion(),
|
|
|
|
euler1: new THREE.Euler(),
|
|
euler2: new THREE.Euler(),
|
|
}
|
|
|
|
const Canvas = {
|
|
// Stores various colors for the 3D scene
|
|
gizmo_colors,
|
|
// Main Blockbench 3D scene
|
|
scene,
|
|
// Pivot marker
|
|
pivot_marker: rot_origin,
|
|
gizmos: [rot_origin],
|
|
outlineMaterial: new THREE.LineBasicMaterial({
|
|
linewidth: 2,
|
|
transparent: true,
|
|
color: gizmo_colors.outline
|
|
}),
|
|
meshOutlineMaterial: new THREE.LineBasicMaterial({
|
|
linewidth: 2,
|
|
//color: gizmo_colors.outline,
|
|
vertexColors: true
|
|
}),
|
|
meshVertexMaterial: new THREE.PointsMaterial({size: 7, sizeAttenuation: false, vertexColors: true}),
|
|
wireframeMaterial: new THREE.MeshBasicMaterial({
|
|
wireframe: true
|
|
}),
|
|
solidMaterial: (function() {
|
|
var vertShader = `
|
|
attribute float highlight;
|
|
|
|
uniform bool SHADE;
|
|
|
|
varying float light;
|
|
varying float lift;
|
|
|
|
float AMBIENT = 0.1;
|
|
float XFAC = -0.05;
|
|
float ZFAC = 0.05;
|
|
|
|
void main()
|
|
{
|
|
|
|
if (SHADE) {
|
|
|
|
vec3 N = normalize( vec3( modelViewMatrix * vec4(normal, 0.0) ) );
|
|
|
|
light = (0.2 + abs(N.z) * 0.8) * (1.0-AMBIENT) + N.x*N.x * XFAC + N.y*N.y * ZFAC + AMBIENT;
|
|
|
|
} else {
|
|
|
|
light = 1.0;
|
|
|
|
}
|
|
|
|
if (highlight == 2.0) {
|
|
lift = 0.3;
|
|
} else if (highlight == 1.0) {
|
|
lift = 0.12;
|
|
} else {
|
|
lift = 0.0;
|
|
}
|
|
|
|
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
|
|
gl_Position = projectionMatrix * mvPosition;
|
|
}`
|
|
var fragShader = `
|
|
#ifdef GL_ES
|
|
precision ${isApp ? 'highp' : 'mediump'} float;
|
|
#endif
|
|
|
|
uniform bool SHADE;
|
|
uniform float BRIGHTNESS;
|
|
uniform vec3 base;
|
|
|
|
varying float light;
|
|
varying float lift;
|
|
|
|
void main(void)
|
|
{
|
|
|
|
gl_FragColor = vec4(lift + base * light * BRIGHTNESS, 1.0);
|
|
|
|
if (lift > 0.1) {
|
|
gl_FragColor.b = gl_FragColor.b * 1.16;
|
|
gl_FragColor.g = gl_FragColor.g * 1.04;
|
|
}
|
|
if (lift > 0.2) {
|
|
gl_FragColor.r = gl_FragColor.r * 0.6;
|
|
gl_FragColor.g = gl_FragColor.g * 0.7;
|
|
}
|
|
|
|
}`
|
|
|
|
return new THREE.ShaderMaterial({
|
|
uniforms: {
|
|
SHADE: {type: 'bool', value: settings.shading.value},
|
|
BRIGHTNESS: {type: 'bool', value: settings.brightness.value / 50},
|
|
base: {value: gizmo_colors.solid}
|
|
},
|
|
vertexShader: vertShader,
|
|
fragmentShader: fragShader,
|
|
side: THREE.DoubleSide
|
|
});
|
|
})(),
|
|
normalHelperMaterial: (function() {
|
|
var vertShader = `
|
|
attribute float highlight;
|
|
|
|
uniform bool SHADE;
|
|
|
|
varying float light;
|
|
varying float lift;
|
|
|
|
float AMBIENT = 0.1;
|
|
float XFAC = -0.05;
|
|
float ZFAC = 0.05;
|
|
|
|
void main()
|
|
{
|
|
|
|
if (SHADE) {
|
|
|
|
vec3 N = normalize( vec3( modelViewMatrix * vec4(normal, 0.0) ) );
|
|
light = (0.2 + abs(N.z) * 0.8) * (1.0-AMBIENT) + N.x*N.x * XFAC + N.y*N.y * ZFAC + AMBIENT;
|
|
|
|
} else {
|
|
|
|
light = 1.0;
|
|
}
|
|
|
|
|
|
if (highlight == 2.0) {
|
|
lift = 0.3;
|
|
} else if (highlight == 1.0) {
|
|
lift = 0.12;
|
|
} else {
|
|
lift = 0.0;
|
|
}
|
|
|
|
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
|
|
gl_Position = projectionMatrix * mvPosition;
|
|
}`
|
|
var fragShader = `
|
|
#ifdef GL_ES
|
|
precision ${isApp ? 'highp' : 'mediump'} float;
|
|
#endif
|
|
|
|
varying float light;
|
|
varying float lift;
|
|
|
|
void main(void)
|
|
{
|
|
if (gl_FrontFacing) {
|
|
gl_FragColor = vec4(vec3(0.20, 0.68, 0.32) * light, 1.0);
|
|
} else {
|
|
gl_FragColor = vec4(vec3(0.76, 0.21, 0.20) * light, 1.0);
|
|
}
|
|
|
|
if (lift > 0.1) {
|
|
gl_FragColor.r = gl_FragColor.r * 1.16;
|
|
gl_FragColor.g = gl_FragColor.g * 1.16;
|
|
gl_FragColor.b = gl_FragColor.b * 1.16;
|
|
}
|
|
if (lift > 0.2) {
|
|
if (gl_FrontFacing) {
|
|
gl_FragColor.r = gl_FragColor.r * 0.8;
|
|
gl_FragColor.g = gl_FragColor.g * 0.9;
|
|
gl_FragColor.b = gl_FragColor.g * 1.5;
|
|
} else {
|
|
gl_FragColor.r = gl_FragColor.r * 0.9;
|
|
gl_FragColor.g = gl_FragColor.g * 2.0;
|
|
gl_FragColor.b = gl_FragColor.g * 3.0;
|
|
}
|
|
}
|
|
|
|
}`
|
|
|
|
return new THREE.ShaderMaterial({
|
|
uniforms: {
|
|
SHADE: {type: 'bool', value: settings.shading.value}
|
|
},
|
|
vertexShader: vertShader,
|
|
fragmentShader: fragShader,
|
|
side: THREE.DoubleSide
|
|
});
|
|
})(),
|
|
uvHelperMaterial: (function() {
|
|
var img = new Image()
|
|
img.src = 'assets/uv_preview.png'
|
|
var tex = new THREE.Texture(img)
|
|
img.tex = tex;
|
|
img.tex.magFilter = THREE.NearestFilter
|
|
img.tex.minFilter = THREE.NearestFilter
|
|
img.tex.wrapS = img.tex.wrapT = THREE.RepeatWrapping;
|
|
img.onload = function() {
|
|
this.tex.needsUpdate = true;
|
|
}
|
|
var vertShader = `
|
|
attribute float highlight;
|
|
|
|
uniform bool SHADE;
|
|
uniform float DENSITY;
|
|
|
|
varying vec2 vUv;
|
|
varying float light;
|
|
varying float lift;
|
|
|
|
float AMBIENT = 0.1;
|
|
float XFAC = -0.05;
|
|
float ZFAC = 0.05;
|
|
|
|
void main()
|
|
{
|
|
|
|
if (SHADE) {
|
|
|
|
vec3 N = normalize( vec3( modelViewMatrix * vec4(normal, 0.0) ) );
|
|
|
|
light = (0.2 + abs(N.z) * 0.8) * (1.0-AMBIENT) + N.x*N.x * XFAC + N.y*N.y * ZFAC + AMBIENT;
|
|
|
|
} else {
|
|
|
|
light = 1.0;
|
|
|
|
}
|
|
|
|
if (highlight == 2.0) {
|
|
lift = 0.3;
|
|
} else if (highlight == 1.0) {
|
|
lift = 0.12;
|
|
} else {
|
|
lift = 0.0;
|
|
}
|
|
|
|
vUv = uv;
|
|
vUv.x = vUv.x * DENSITY;
|
|
vUv.y = vUv.y * DENSITY;
|
|
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
|
|
gl_Position = projectionMatrix * mvPosition;
|
|
}`
|
|
var fragShader = `
|
|
#ifdef GL_ES
|
|
precision ${isApp ? 'highp' : 'mediump'} float;
|
|
#endif
|
|
|
|
uniform sampler2D map;
|
|
|
|
uniform bool SHADE;
|
|
|
|
varying vec2 vUv;
|
|
varying float light;
|
|
varying float lift;
|
|
|
|
void main(void)
|
|
{
|
|
|
|
vec4 color = texture2D(map, vUv);
|
|
|
|
if (color.a < 0.01) discard;
|
|
|
|
gl_FragColor = vec4(lift + color.rgb * light, color.a);
|
|
|
|
|
|
if (lift > 0.2) {
|
|
gl_FragColor.r = gl_FragColor.r * 0.6;
|
|
gl_FragColor.g = gl_FragColor.g * 0.7;
|
|
}
|
|
|
|
}`
|
|
|
|
return new THREE.ShaderMaterial({
|
|
uniforms: {
|
|
map: {type: 't', value: tex},
|
|
SHADE: {type: 'bool', value: settings.shading.value},
|
|
DENSITY: {type: 'float', value: 4}
|
|
},
|
|
vertexShader: vertShader,
|
|
fragmentShader: fragShader,
|
|
side: THREE.DoubleSide,
|
|
})
|
|
})(),
|
|
emptyMaterials: [],
|
|
updateMarkerColorMaterials() {
|
|
var img = new Image()
|
|
img.src = 'assets/missing.png'
|
|
var tex = new THREE.Texture(img)
|
|
img.tex = tex;
|
|
img.tex.magFilter = THREE.NearestFilter
|
|
img.tex.minFilter = THREE.NearestFilter
|
|
img.tex.wrapS = img.tex.wrapT = THREE.RepeatWrapping;
|
|
img.onload = function() {
|
|
this.tex.needsUpdate = true;
|
|
}
|
|
var vertShader = `
|
|
attribute float highlight;
|
|
|
|
uniform bool SHADE;
|
|
|
|
varying vec2 vUv;
|
|
varying float light;
|
|
varying float lift;
|
|
|
|
float AMBIENT = 0.5;
|
|
float XFAC = -0.15;
|
|
float ZFAC = 0.05;
|
|
|
|
void main()
|
|
{
|
|
|
|
if (SHADE) {
|
|
|
|
vec3 N = normalize( vec3( modelMatrix * vec4(normal, 0.0) ) );
|
|
|
|
float yLight = (1.0+N.y) * 0.5;
|
|
light = yLight * (1.0-AMBIENT) + N.x*N.x * XFAC + N.z*N.z * ZFAC + AMBIENT;
|
|
|
|
} else {
|
|
|
|
light = 1.0;
|
|
|
|
}
|
|
|
|
if (highlight == 2.0) {
|
|
lift = 0.22;
|
|
} else if (highlight == 1.0) {
|
|
lift = 0.1;
|
|
} else {
|
|
lift = 0.0;
|
|
}
|
|
|
|
vUv = uv;
|
|
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
|
|
gl_Position = projectionMatrix * mvPosition;
|
|
}`
|
|
var fragShader = `
|
|
#ifdef GL_ES
|
|
precision ${isApp ? 'highp' : 'mediump'} float;
|
|
#endif
|
|
|
|
uniform sampler2D map;
|
|
|
|
uniform bool SHADE;
|
|
uniform float BRIGHTNESS;
|
|
uniform vec3 base;
|
|
|
|
varying vec2 vUv;
|
|
varying float light;
|
|
varying float lift;
|
|
|
|
void main(void)
|
|
{
|
|
vec4 color = texture2D(map, vUv);
|
|
|
|
gl_FragColor = vec4(lift + color.rgb * base * light * BRIGHTNESS, 1.0);
|
|
|
|
if (lift > 0.2) {
|
|
gl_FragColor.r = gl_FragColor.r * 0.6;
|
|
gl_FragColor.g = gl_FragColor.g * 0.7;
|
|
}
|
|
|
|
}`
|
|
|
|
|
|
markerColors.forEach(function(color, i) {
|
|
if (Canvas.emptyMaterials[i]) return;
|
|
Canvas.emptyMaterials[i] = new THREE.ShaderMaterial({
|
|
uniforms: {
|
|
map: {type: 't', value: tex},
|
|
SHADE: {type: 'bool', value: settings.shading.value},
|
|
BRIGHTNESS: {type: 'bool', value: settings.brightness.value / 50},
|
|
base: {value: new THREE.Color().set(color.pastel)}
|
|
},
|
|
vertexShader: vertShader,
|
|
fragmentShader: fragShader,
|
|
side: THREE.DoubleSide,
|
|
})
|
|
})
|
|
},
|
|
transparentMaterial: new THREE.MeshBasicMaterial({visible: false, name: 'invisible'}),
|
|
global_light_color: new THREE.Color(0xffffff),
|
|
global_light_side: 0,
|
|
|
|
gridMaterial: new THREE.LineBasicMaterial({color: gizmo_colors.grid}),
|
|
buildGrid() {
|
|
three_grid.children.length = 0;
|
|
if (Canvas.side_grids) {
|
|
Canvas.side_grids.x.children.length = 0;
|
|
Canvas.side_grids.z.children.length = 0;
|
|
}
|
|
if (Modes.display) return;
|
|
|
|
three_grid.name = 'grid_group'
|
|
gizmo_colors.grid.set(parseInt('0x'+CustomTheme.data.colors.grid.replace('#', ''), 16));
|
|
|
|
Canvas.northMarkMaterial.color = gizmo_colors.grid
|
|
|
|
function setupAxisLine(origin, length, axis) {
|
|
var color = 'rgb'[getAxisNumber(axis)]
|
|
var material = new THREE.LineBasicMaterial({color: gizmo_colors[color]});
|
|
var dest = new THREE.Vector3().copy(origin);
|
|
dest[axis] += length;
|
|
let points = [
|
|
origin,
|
|
dest
|
|
];
|
|
let geometry = new THREE.BufferGeometry().setFromPoints(points)
|
|
|
|
|
|
//geometry.vertices.push(origin)
|
|
//geometry.vertices.push(dest)
|
|
|
|
var line = new THREE.Line(geometry, material);
|
|
line.name = 'axis_line_'+axis;
|
|
three_grid.add(line)
|
|
}
|
|
//Axis Lines
|
|
if (settings.base_grid.value) {
|
|
var length = Format.centered_grid
|
|
? (settings.full_grid.value ? 24 : 8)
|
|
: 16
|
|
setupAxisLine(new THREE.Vector3( 0, 0.01, 0), length, 'x')
|
|
setupAxisLine(new THREE.Vector3( 0, 0.01, 0), length, 'z')
|
|
|
|
}
|
|
|
|
var side_grid = new THREE.Object3D()
|
|
|
|
if (settings.full_grid.value === true) {
|
|
//Grid
|
|
let size = settings.large_grid_size.value*16;
|
|
var grid = new THREE.GridHelper(size, size/canvasGridSize(), Canvas.gridMaterial);
|
|
if (Format.centered_grid) {
|
|
grid.position.set(0,0,0)
|
|
} else {
|
|
grid.position.set(8,0,8)
|
|
}
|
|
grid.name = 'grid'
|
|
three_grid.add(grid)
|
|
side_grid.add(grid.clone())
|
|
|
|
//North
|
|
geometry = new THREE.PlaneGeometry(5, 5)
|
|
var north_mark = new THREE.Mesh(geometry, Canvas.northMarkMaterial)
|
|
if (Format.centered_grid) {
|
|
north_mark.position.set(0,0, -3 - size/2)
|
|
} else {
|
|
north_mark.position.set(8, 0, 5 - size/2)
|
|
}
|
|
north_mark.rotation.x = Math.PI / -2
|
|
three_grid.add(north_mark)
|
|
|
|
} else {
|
|
if (settings.large_grid.value === true) {
|
|
//Grid
|
|
let size = settings.large_grid_size.value
|
|
var grid = new THREE.GridHelper(size*16, size, Canvas.gridMaterial);
|
|
if (Format.centered_grid) {
|
|
grid.position.set(0,0,0)
|
|
} else {
|
|
grid.position.set(8,0,8)
|
|
}
|
|
grid.name = 'grid'
|
|
three_grid.add(grid)
|
|
side_grid.add(grid.clone())
|
|
}
|
|
|
|
if (settings.base_grid.value === true) {
|
|
//Grid
|
|
var grid = new THREE.GridHelper(16, 16/canvasGridSize(), Canvas.gridMaterial);
|
|
|
|
if (Format.centered_grid) {
|
|
grid.position.set(0,0,0)
|
|
} else {
|
|
grid.position.set(8,0,8)
|
|
}
|
|
grid.name = 'grid'
|
|
three_grid.add(grid)
|
|
side_grid.add(grid.clone())
|
|
|
|
//North
|
|
geometry = new THREE.PlaneGeometry(2.4, 2.4)
|
|
var north_mark = new THREE.Mesh(geometry, Canvas.northMarkMaterial)
|
|
if (Format.centered_grid) {
|
|
north_mark.position.set(0,0,-9.5)
|
|
} else {
|
|
north_mark.position.set(8,0,-1.5)
|
|
}
|
|
north_mark.rotation.x = Math.PI / -2
|
|
three_grid.add(north_mark)
|
|
}
|
|
}
|
|
if (settings.large_box.value === true) {
|
|
let size = Format.cube_size_limiter?.box_marker_size || [48, 48, 48];
|
|
var geometry_box = new THREE.EdgesGeometry(new THREE.BoxBufferGeometry(...size));
|
|
|
|
var line_material = new THREE.LineBasicMaterial({color: gizmo_colors.grid});
|
|
var large_box = new THREE.LineSegments( geometry_box, line_material);
|
|
if (Format.centered_grid) {
|
|
large_box.position.set(0,8,0)
|
|
} else {
|
|
large_box.position.set(8,8,8)
|
|
}
|
|
large_box.name = 'grid'
|
|
three_grid.add(large_box)
|
|
three_grid.size_limit_box = large_box;
|
|
if (Format.cube_size_limiter?.updateBoxMarker) Format.cube_size_limiter.updateBoxMarker();
|
|
}
|
|
scene.add(three_grid)
|
|
|
|
Canvas.side_grids = {
|
|
x: side_grid,
|
|
z: side_grid.clone()
|
|
}
|
|
three_grid.add(Canvas.side_grids.x)
|
|
Canvas.side_grids.x.name = 'side_grid_x'
|
|
Canvas.side_grids.x.visible = !Modes.display;
|
|
Canvas.side_grids.x.rotation.z = Math.PI/2;
|
|
Canvas.side_grids.x.position.y = Format.centered_grid ? 8 : 0;
|
|
Canvas.side_grids.z.position.z = 0
|
|
Canvas.side_grids.x.children.forEach(el => {
|
|
el.layers.set(1)
|
|
});
|
|
|
|
three_grid.add(Canvas.side_grids.z)
|
|
Canvas.side_grids.z.name = 'side_grid_z'
|
|
Canvas.side_grids.z.visible = !Modes.display;
|
|
Canvas.side_grids.z.rotation.z = Math.PI/2;
|
|
Canvas.side_grids.z.rotation.y = Math.PI/2
|
|
Canvas.side_grids.z.position.y = Format.centered_grid ? 8 : 0;
|
|
Canvas.side_grids.z.position.z = 0
|
|
Canvas.side_grids.z.children.forEach(el => {
|
|
el.layers.set(3)
|
|
});
|
|
},
|
|
updateShading,
|
|
|
|
face_order: ['east', 'west', 'up', 'down', 'south', 'north'],
|
|
temp_vectors: [new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3()],
|
|
|
|
setup() {
|
|
Canvas.updateMarkerColorMaterials();
|
|
|
|
//Light
|
|
Sun = new THREE.AmbientLight( 0xffffff );
|
|
Sun.name = 'sun'
|
|
scene.add(Sun);
|
|
Sun.intensity = 0.5
|
|
|
|
lights = new THREE.Object3D()
|
|
lights.name = 'lights'
|
|
|
|
lights.top = new THREE.DirectionalLight();
|
|
lights.top.name = 'light_top'
|
|
lights.top.position.set(0, 100, 0)
|
|
lights.add(lights.top);
|
|
|
|
lights.top.intensity = 0.41
|
|
|
|
lights.bottom = new THREE.DirectionalLight();
|
|
lights.bottom.name = 'light_bottom'
|
|
lights.bottom.position.set(0, -100, 0)
|
|
lights.add(lights.bottom);
|
|
|
|
lights.bottom.intensity = -0.02
|
|
|
|
lights.north = new THREE.DirectionalLight();
|
|
lights.north.name = 'light_north'
|
|
lights.north.position.set(0, 0, -100)
|
|
lights.add(lights.north);
|
|
|
|
lights.south = new THREE.DirectionalLight();
|
|
lights.south.name = 'light_south'
|
|
lights.south.position.set(0, 0, 100)
|
|
lights.add(lights.south);
|
|
|
|
lights.north.intensity = lights.south.intensity = 0.3
|
|
|
|
lights.west = new THREE.DirectionalLight();
|
|
lights.west.name = 'light_west'
|
|
lights.west.position.set(-100, 0, 0)
|
|
lights.add(lights.west);
|
|
|
|
lights.east = new THREE.DirectionalLight();
|
|
lights.east.name = 'light_east'
|
|
lights.east.position.set(100, 0, 0)
|
|
lights.add(lights.east);
|
|
|
|
lights.west.intensity = lights.east.intensity = 0.1
|
|
|
|
Canvas.updateShading()
|
|
|
|
var img = new Image();
|
|
img.src = 'assets/north.png';
|
|
var tex = new THREE.Texture(img);
|
|
img.tex = tex;
|
|
img.tex.magFilter = THREE.NearestFilter;
|
|
img.tex.minFilter = THREE.NearestFilter;
|
|
img.onload = function() {
|
|
this.tex.needsUpdate = true;
|
|
}
|
|
Canvas.northMarkMaterial = new THREE.MeshBasicMaterial({
|
|
map: tex,
|
|
transparent: true,
|
|
side: THREE.DoubleSide,
|
|
alphaTest: 0.2
|
|
})
|
|
|
|
/*
|
|
// Vertex gizmos
|
|
var vertex_img = new Image();
|
|
vertex_img.src = 'assets/vertex.png';
|
|
vertex_img.tex = new THREE.Texture(vertex_img);
|
|
vertex_img.tex.magFilter = THREE.NearestFilter;
|
|
vertex_img.tex.minFilter = THREE.NearestFilter;
|
|
vertex_img.onload = function() {
|
|
this.tex.needsUpdate = true;
|
|
}
|
|
Canvas.meshVertexMaterial.map = vertex_img.tex;
|
|
Canvas.meshVertexMaterial.transparent = true;
|
|
*/
|
|
|
|
//Rotation Pivot
|
|
var helper1 = new THREE.AxesHelper(2)
|
|
var helper2 = new THREE.AxesHelper(2)
|
|
helper1.rotation.x = Math.PI / 1
|
|
|
|
helper2.rotation.x = Math.PI / -1
|
|
helper2.rotation.y = Math.PI / 1
|
|
helper2.scale.y = -1
|
|
|
|
Canvas.pivot_marker.add(helper1)
|
|
Canvas.pivot_marker.add(helper2)
|
|
|
|
Canvas.pivot_marker.name = 'pivot_marker';
|
|
Canvas.pivot_marker.rotation.order = 'ZYX';
|
|
Canvas.pivot_marker.base_scale = new THREE.Vector3(1, 1, 1);
|
|
Canvas.pivot_marker.no_export = true;
|
|
|
|
Canvas.groundPlaneMaterial = new THREE.MeshBasicMaterial({
|
|
map: Canvas.emptyMaterials[0].uniforms.map.value,
|
|
color: CustomTheme.data.colors.back,
|
|
side: THREE.DoubleSide,
|
|
alphaTest: 0.2
|
|
})
|
|
let size = 4096;
|
|
Canvas.ground_plane = new THREE.Mesh(new THREE.PlaneGeometry(size, size), Canvas.groundPlaneMaterial);
|
|
Canvas.ground_plane.rotation.x = Math.PI/2;
|
|
Canvas.ground_plane.position.y = -0.025;
|
|
Canvas.ground_plane.geometry.attributes.uv.set([0, 4096/16, 4096/16, 4096/16, 0, 0, 4096/16, 0]);
|
|
Canvas.ground_plane.geometry.attributes.uv.needsUpdate = true;
|
|
Canvas.ground_plane.visible = settings.ground_plane.value;
|
|
scene.add(Canvas.ground_plane);
|
|
Canvas.gizmos.push(Canvas.ground_plane);
|
|
|
|
setupGrid = true;
|
|
},
|
|
//Misc
|
|
raycast(event) {
|
|
var preview = Canvas.getHoveredPreview()
|
|
if (preview) {
|
|
return preview.raycast(event)
|
|
} else {
|
|
return false
|
|
}
|
|
},
|
|
getHoveredPreview() {
|
|
var canvas = $('.preview canvas:hover').get(0);
|
|
return canvas ? canvas.preview : Preview.selected;
|
|
},
|
|
withoutGizmos(cb) {
|
|
|
|
function editVis(edit) {
|
|
Canvas.gizmos.forEach(object => {
|
|
edit(object);
|
|
})
|
|
edit(three_grid)
|
|
edit(Canvas.side_grids.x)
|
|
edit(Canvas.side_grids.z)
|
|
Outliner.elements.forEach(element => {
|
|
let {mesh} = element;
|
|
if (element.selected && mesh.outline) edit(mesh.outline);
|
|
if (mesh.grid_box) edit(mesh.grid_box);
|
|
if (element instanceof Locator) edit(mesh.children[0]);
|
|
if (element instanceof NullObject) edit(mesh);
|
|
})
|
|
}
|
|
editVis(obj => {
|
|
obj.was_visible = obj.visible
|
|
obj.visible = false
|
|
})
|
|
var ground_anim_before = ground_animation
|
|
if (display_mode && ground_animation) {
|
|
ground_animation = false
|
|
}
|
|
updateCubeHighlights(null, true);
|
|
|
|
try {
|
|
cb()
|
|
} catch(err) {
|
|
console.error(err)
|
|
}
|
|
|
|
editVis(obj => {
|
|
obj.visible = obj.was_visible
|
|
delete obj.was_visible
|
|
})
|
|
if (display_mode && ground_anim_before) {
|
|
ground_animation = ground_anim_before
|
|
}
|
|
updateCubeHighlights();
|
|
},
|
|
|
|
/**
|
|
*
|
|
* @param {object} options
|
|
* @param {array} options.elements Elements to update
|
|
* @param {object} options.element_aspects
|
|
* @param {boolean} options.element_aspects.transform Update the element transformation
|
|
* @param {boolean} options.element_aspects.geometry Update the element geometry
|
|
* @param {boolean} options.element_aspects.faces Update the element faces and texture
|
|
* @param {boolean} options.element_aspects.uv Update the element UV mapping
|
|
* @param {boolean} options.element_aspects.visibility Update the element visibility
|
|
* @param {boolean} options.element_aspects.painting_grid Update the painting grid
|
|
* @param {array} options.groups Groups to update
|
|
* @param {object} options.group_aspects Which parts of the group to update
|
|
* @param {boolean} options.group_aspects.transform Update the group transformation and geometry
|
|
* @param {boolean} options.selection Update the selection
|
|
*/
|
|
updateView(options) {
|
|
if (options.elements) {
|
|
let aspects = options.element_aspects || {};
|
|
options.elements.forEach(element => {
|
|
let update_all = !options.element_aspects || (aspects.visibility && element.visibility && !element.mesh.visible);
|
|
let controller = element.constructor.preview_controller
|
|
|
|
if (aspects.transform || update_all) {
|
|
controller.updateTransform(element);
|
|
}
|
|
if (aspects.geometry || update_all) {
|
|
if (controller.updateGeometry) controller.updateGeometry(element);
|
|
}
|
|
if (aspects.faces || update_all) {
|
|
if (controller.updateFaces) controller.updateFaces(element);
|
|
}
|
|
if ((aspects.uv || update_all)) {
|
|
if (controller.updateUV) controller.updateUV(element);
|
|
}
|
|
if ((aspects.painting_grid || aspects.geometry || aspects.transform || update_all) && Modes.paint && settings.painting_grid.value) {
|
|
if (controller.updatePaintingGrid) controller.updatePaintingGrid(element);
|
|
}
|
|
if (aspects.visibility || update_all) {
|
|
if (controller.updateVisibility) controller.updateVisibility(element);
|
|
}
|
|
})
|
|
}
|
|
if (options.groups) {
|
|
Canvas.updateAllBones(options.groups)
|
|
}
|
|
if (options.selection) {
|
|
updateSelection();
|
|
}
|
|
Blockbench.dispatchEvent('update_view', options);
|
|
},
|
|
//Main updaters
|
|
clear() {
|
|
var objects = []
|
|
scene.traverse(function(s) {
|
|
if (s.isElement === true || s.isGroup === true) {
|
|
objects.push(s)
|
|
}
|
|
})
|
|
for (var uuid in Project.nodes_3d) {
|
|
var mesh = Project.nodes_3d[uuid];
|
|
objects.safePush(mesh);
|
|
}
|
|
objects.forEach(function(s) {
|
|
if (s.parent) {
|
|
s.parent.remove(s)
|
|
}
|
|
if (s.geometry) s.geometry.dispose()
|
|
if (s.outline && s.outline.geometry) s.outline.geometry.dispose()
|
|
delete Project.nodes_3d[s.name]
|
|
})
|
|
},
|
|
updateAll() {
|
|
updateNslideValues()
|
|
Canvas.updateView({
|
|
elements: Outliner.elements,
|
|
groups: Group.all,
|
|
selection: true,
|
|
})
|
|
},
|
|
updateAllPositions(leave_selection) {
|
|
updateNslideValues()
|
|
Canvas.updateView({
|
|
elements: Outliner.elements,
|
|
element_aspects: {
|
|
transform: true,
|
|
geometry: true,
|
|
}
|
|
})
|
|
if (leave_selection !== true) {
|
|
updateSelection()
|
|
}
|
|
},
|
|
updateVisibility() {
|
|
Canvas.updateView({elements: Outliner.elements, element_aspects: {visibility: true}})
|
|
},
|
|
updateAllFaces(texture) {
|
|
Outliner.elements.forEach(function(obj) {
|
|
if (obj.faces || obj instanceof TextureMesh) {
|
|
var used = true;
|
|
if (texture && obj.faces && !Format.single_texture) {
|
|
used = false;
|
|
for (var face in obj.faces) {
|
|
if (obj.faces[face].getTexture() == texture) {
|
|
used = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (used === true) {
|
|
obj.preview_controller.updateFaces(obj);
|
|
if (obj.preview_controller.updateUV) {
|
|
obj.preview_controller.updateUV(obj);
|
|
}
|
|
}
|
|
}
|
|
})
|
|
},
|
|
updateAllUVs() {
|
|
if (Project.view_mode !== 'textured') return;
|
|
Canvas.updateView({elements: Outliner.elements, element_aspects: {uv: true}});
|
|
return;
|
|
},
|
|
getRenderSide(texture) {
|
|
if (texture instanceof Texture) {
|
|
if (texture.render_sides == 'front') return THREE.FrontSide;
|
|
if (texture.render_sides == 'double') return THREE.DoubleSide;
|
|
}
|
|
if (settings.render_sides.value == 'auto') {
|
|
if (Format && Format.render_sides) {
|
|
let value = typeof Format.render_sides == 'function' ? Format.render_sides() : Format.render_sides;
|
|
if (value == 'front') return THREE.FrontSide;
|
|
if (value == 'double') return THREE.DoubleSide;
|
|
if (value == 'back') return THREE.BackSide;
|
|
}
|
|
return THREE.DoubleSide;
|
|
} else if (settings.render_sides.value == 'front') {
|
|
return THREE.FrontSide;
|
|
} else {
|
|
return THREE.DoubleSide;
|
|
}
|
|
},
|
|
updateRenderSides() {
|
|
let side = Canvas.getRenderSide();
|
|
ModelProject.all.forEach(project => {
|
|
project.textures.forEach((tex) => {
|
|
var mat = project.materials[tex.uuid];
|
|
if (!mat) return;
|
|
mat.side = Canvas.getRenderSide(tex);
|
|
})
|
|
})
|
|
if (Canvas.layered_material) {
|
|
Canvas.layered_material.side = side;
|
|
}
|
|
if (Canvas.solidMaterial) {
|
|
Canvas.solidMaterial.side = side;
|
|
}
|
|
Canvas.emptyMaterials.forEach(function(mat) {
|
|
mat.side = side
|
|
})
|
|
},
|
|
updatePositions(leave_selection) {
|
|
updateNslideValues()
|
|
var arr = selected.slice()
|
|
if (Format.bone_rig && Group.selected) {
|
|
Group.selected.forEachChild(obj => {
|
|
if (obj instanceof OutlinerElement) {
|
|
arr.safePush(obj)
|
|
}
|
|
})
|
|
if (arr.length === selected.length) {
|
|
Canvas.updateAllBones()
|
|
}
|
|
}
|
|
Canvas.updateView({elements: arr, element_aspects: {transform: true, geometry: true}})
|
|
if (leave_selection !== true) {
|
|
TickUpdates.selection = true;
|
|
}
|
|
},
|
|
updateSelectedFaces() {
|
|
Cube.selected.forEach(function(obj) {
|
|
if (obj.visibility == true) {
|
|
obj.preview_controller.updateFaces(obj);
|
|
obj.preview_controller.updateUV(obj);
|
|
}
|
|
})
|
|
},
|
|
updateUVs() {
|
|
Outliner.selected.forEach(function(obj) {
|
|
if (obj.preview_controller.updateUV) {
|
|
obj.preview_controller.updateUV(obj);
|
|
}
|
|
})
|
|
},
|
|
outlineObjects(arr) {
|
|
arr.forEach(function(obj) {
|
|
if (!obj.visibility) return;
|
|
var mesh = obj.mesh;
|
|
if (!mesh || !mesh.geometry || !mesh.outline) return;
|
|
|
|
var copy = mesh.outline.clone();
|
|
copy.geometry = mesh.outline.geometry.clone();
|
|
|
|
THREE.fastWorldPosition(mesh, copy.position);
|
|
copy.position.sub(scene.position);
|
|
copy.rotation.setFromQuaternion(mesh.getWorldQuaternion(new THREE.Quaternion()));
|
|
mesh.getWorldScale(copy.scale);
|
|
|
|
copy.name = obj.uuid+'_ghost_outline';
|
|
Canvas.outlines.add(copy);
|
|
})
|
|
},
|
|
updateAllBones(bones = Group.all) {
|
|
if (Project) Project.model_3d.scale.set(1, 1, 1);
|
|
bones.forEach((obj) => {
|
|
let bone = obj.mesh
|
|
if (bone && obj instanceof Group) {
|
|
|
|
bone.rotation.order = 'ZYX';
|
|
bone.rotation.setFromDegreeArray(obj.rotation);
|
|
bone.position.fromArray(obj.origin);
|
|
bone.scale.x = bone.scale.y = bone.scale.z = 1;
|
|
|
|
if (obj.parent.type === 'group') {
|
|
|
|
bone.position.x -= obj.parent.origin[0];
|
|
bone.position.y -= obj.parent.origin[1];
|
|
bone.position.z -= obj.parent.origin[2];
|
|
|
|
var parent_bone = obj.parent.mesh;
|
|
parent_bone.add(bone);
|
|
} else {
|
|
Project.model_3d.add(bone);
|
|
}
|
|
|
|
bone.fix_position = bone.position.clone();
|
|
bone.fix_rotation = bone.rotation.clone();
|
|
}
|
|
})
|
|
if (bones == Group.all) {
|
|
scene.updateMatrixWorld();
|
|
} else {
|
|
bones.forEach(bone => {
|
|
bone.mesh.updateMatrixWorld();
|
|
})
|
|
}
|
|
},
|
|
updatePivotMarker() {
|
|
if (Canvas.pivot_marker.parent) {
|
|
Canvas.pivot_marker.parent.remove(Canvas.pivot_marker)
|
|
}
|
|
if (settings.origin_size.value > 0) {
|
|
if (Group.selected && Format.bone_rig) {
|
|
if (Group.selected.visibility) {
|
|
Group.selected.mesh.add(Canvas.pivot_marker)
|
|
}
|
|
} else if ((Cube.selected.length && Format.rotate_cubes) || Mesh.selected.length) {
|
|
let selected_elements = [...Cube.selected, ...Mesh.selected];
|
|
if (selected_elements.length === 1) {
|
|
let mesh = selected_elements[0].mesh
|
|
if (mesh) {
|
|
mesh.add(Canvas.pivot_marker)
|
|
}
|
|
} else {
|
|
var origin = null;
|
|
var first_visible = null;
|
|
var i = 0;
|
|
while (i < selected_elements.length) {
|
|
if (selected_elements[i].visibility) {
|
|
|
|
if (first_visible === null) {
|
|
first_visible = selected_elements[i]
|
|
}
|
|
if (origin === null) {
|
|
origin = selected_elements[i].origin
|
|
} else if (!origin.equals(selected_elements[i].origin)) {
|
|
origin = false;
|
|
i = Infinity;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
if (first_visible && typeof origin === 'object') {
|
|
let mesh = first_visible.mesh
|
|
if (mesh) {
|
|
mesh.add(Canvas.pivot_marker)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return !!Canvas.pivot_marker.parent;
|
|
},
|
|
adaptObjectPosition(object, mesh) {
|
|
Canvas.updateView({
|
|
elements: [object],
|
|
element_aspects: {geometry: true, transform: true}
|
|
})
|
|
},
|
|
adaptObjectFaceGeo(cube) {
|
|
let {mesh} = cube;
|
|
let {geometry} = mesh;
|
|
if (!geometry.all_faces) geometry.all_faces = geometry.groups.slice();
|
|
geometry.groups.empty()
|
|
|
|
geometry.all_faces.forEach(face => {
|
|
let bb_face = cube.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]);
|
|
}
|
|
},
|
|
getLayeredMaterial(layers) {
|
|
if (Canvas.layered_material && !layers) return Canvas.layered_material;
|
|
// https://codepen.io/Fyrestar/pen/YmpXYr
|
|
var vertShader = `
|
|
attribute float highlight;
|
|
|
|
uniform bool SHADE;
|
|
|
|
varying vec2 vUv;
|
|
varying float light;
|
|
varying float lift;
|
|
|
|
float AMBIENT = 0.5;
|
|
float XFAC = -0.15;
|
|
float ZFAC = 0.05;
|
|
|
|
void main()
|
|
{
|
|
|
|
if (SHADE) {
|
|
|
|
vec3 N = normalize( vec3( modelMatrix * vec4(normal, 0.0) ) );
|
|
|
|
|
|
float yLight = (1.0+N.y) * 0.5;
|
|
light = yLight * (1.0-AMBIENT) + N.x*N.x * XFAC + N.z*N.z * ZFAC + AMBIENT;
|
|
|
|
} else {
|
|
|
|
light = 1.0;
|
|
|
|
}
|
|
|
|
if (highlight == 2.0) {
|
|
lift = 0.22;
|
|
} else if (highlight == 1.0) {
|
|
lift = 0.1;
|
|
} else {
|
|
lift = 0.0;
|
|
}
|
|
|
|
vUv = uv;
|
|
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
|
|
gl_Position = projectionMatrix * mvPosition;
|
|
}`
|
|
var fragShader = `
|
|
#ifdef GL_ES
|
|
precision ${isApp ? 'highp' : 'mediump'} float;
|
|
#endif
|
|
|
|
uniform sampler2D t0;
|
|
uniform sampler2D t1;
|
|
uniform sampler2D t2;
|
|
|
|
uniform bool SHADE;
|
|
|
|
varying vec2 vUv;
|
|
varying float light;
|
|
varying float lift;
|
|
|
|
void main(void)
|
|
{
|
|
vec4 Ca = texture2D(t0, vUv);
|
|
vec4 Cb = texture2D(t1, vUv);
|
|
vec4 Cc = texture2D(t2, vUv);
|
|
|
|
vec3 ctemp = Ca.rgb * Ca.a + Cb.rgb * Cb.a * (1.0 - Ca.a);
|
|
vec4 ctemp4 = vec4(ctemp, Ca.a + (1.0 - Ca.a) * Cb.a);
|
|
|
|
vec3 c = ctemp4.rgb + Cc.rgb * Cc.a * (1.0 - ctemp4.a);
|
|
gl_FragColor= vec4(lift + c * light, ctemp4.a + (1.0 - ctemp4.a) * Cc.a);
|
|
|
|
if (lift > 0.2) {
|
|
gl_FragColor.r = gl_FragColor.r * 0.6;
|
|
gl_FragColor.g = gl_FragColor.g * 0.7;
|
|
}
|
|
|
|
if (gl_FragColor.a < 0.05) discard;
|
|
}`
|
|
|
|
var uniforms = {
|
|
SHADE: {type: 'bool', value: settings.shading.value},
|
|
t0: {type: 't', value: null},
|
|
t1: {type: 't', value: null},
|
|
t2: {type: 't', value: null}
|
|
};
|
|
let i = 0;
|
|
if (layers instanceof Array == false) layers = Texture.all;
|
|
layers.forEachReverse(texture => {
|
|
if (texture.visible && i < 3) {
|
|
uniforms[`t${i}`].value = texture.getMaterial().map;
|
|
i++;
|
|
}
|
|
})
|
|
|
|
var material_shh = new THREE.ShaderMaterial({
|
|
uniforms: uniforms,
|
|
vertexShader: vertShader,
|
|
fragmentShader: fragShader,
|
|
side: Canvas.getRenderSide(),
|
|
transparent: true
|
|
});
|
|
Canvas.layered_material = material_shh;
|
|
return material_shh;
|
|
},
|
|
updateLayeredTextures() {
|
|
delete Canvas.layered_material;
|
|
if (Format.single_texture && Texture.all.length >= 2) {
|
|
Canvas.updateAllFaces();
|
|
}
|
|
},
|
|
adaptObjectFaces(cube, mesh) {
|
|
if (!mesh) mesh = cube.mesh
|
|
if (!mesh) return;
|
|
|
|
Canvas.adaptObjectFaceGeo(cube);
|
|
|
|
if (Project.view_mode === 'solid') {
|
|
mesh.material = Canvas.solidMaterial
|
|
|
|
} else if (Project.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() : Canvas.emptyMaterials[cube.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(Canvas.emptyMaterials[cube.color])
|
|
}
|
|
}
|
|
})
|
|
if (materials.allEqual(materials[0])) materials = materials[0];
|
|
mesh.material = materials
|
|
}
|
|
},
|
|
updateUV(cube, animation = true) {
|
|
// Deprecated
|
|
return Cube.preview_controller.updateUV(cube, animation);
|
|
},
|
|
updateUVFace(vertex_uvs, index, face, frame = 0, stretch = 1) {
|
|
stretch *= -1;
|
|
var pw = Project.texture_width;
|
|
var ph = Project.texture_height;
|
|
var arr = [
|
|
[face.uv[0]/pw, (face.uv[1]/ph)/stretch+1],
|
|
[face.uv[2]/pw, (face.uv[1]/ph)/stretch+1],
|
|
[face.uv[0]/pw, (face.uv[3]/ph)/stretch+1],
|
|
[face.uv[2]/pw, (face.uv[3]/ph)/stretch+1],
|
|
]
|
|
if (frame > 0 && stretch !== -1) {
|
|
//Animate
|
|
var offset = (1/stretch) * frame
|
|
arr[0][1] += offset
|
|
arr[1][1] += offset
|
|
arr[2][1] += offset
|
|
arr[3][1] += offset
|
|
}
|
|
var rot = (face.rotation+0)
|
|
while (rot > 0) {
|
|
let a = arr[0];
|
|
arr[0] = arr[2];
|
|
arr[2] = arr[3];
|
|
arr[3] = arr[1];
|
|
arr[1] = a;
|
|
rot = rot-90;
|
|
}
|
|
vertex_uvs.array.set(arr[0], index*8 + 0); //0,1
|
|
vertex_uvs.array.set(arr[1], index*8 + 2); //1,1
|
|
vertex_uvs.array.set(arr[2], index*8 + 4); //0,0
|
|
vertex_uvs.array.set(arr[3], index*8 + 6); //1,0
|
|
},
|
|
updatePaintingGrid() {
|
|
Outliner.elements.forEach(element => {
|
|
if (element.preview_controller.updatePaintingGrid) {
|
|
element.preview_controller.updatePaintingGrid(element);
|
|
}
|
|
})
|
|
},
|
|
|
|
getModelSize() {
|
|
var visible_box = new THREE.Box3()
|
|
Canvas.withoutGizmos(() => {
|
|
Outliner.elements.forEach(element => {
|
|
if (element.export != false && element.mesh && element.mesh.geometry) {
|
|
visible_box.expandByObject(element.mesh);
|
|
}
|
|
})
|
|
})
|
|
|
|
var offset = new THREE.Vector3(8,8,8);
|
|
visible_box.max.add(offset);
|
|
visible_box.min.add(offset);
|
|
|
|
// Width
|
|
var radius = Math.max(
|
|
visible_box.max.x,
|
|
visible_box.max.z,
|
|
-visible_box.min.x,
|
|
-visible_box.min.z
|
|
)
|
|
if (Math.abs(radius) === Infinity) {
|
|
radius = 0
|
|
}
|
|
let width = radius*2
|
|
let height = Math.abs(visible_box.max.y - visible_box.min.y)
|
|
if (height === Infinity) height = 0;
|
|
|
|
return [width, height]
|
|
}
|
|
}
|
|
var buildGrid = Canvas.buildGrid;
|