Pixel-Composer/scripts/__3D/__3D.gml
2024-06-14 17:46:25 +07:00

627 lines
No EOL
16 KiB
Text

enum CAMERA_PROJ {
ortho,
perspective
}
#region setup
globalvar PRIMITIVES, FORMAT_P, FORMAT_PT, FORMAT_PNT, FORMAT_2PC;
PRIMITIVES = ds_map_create();
vertex_format_begin();
vertex_format_add_position_3d();
FORMAT_P = vertex_format_end();
vertex_format_begin();
vertex_format_add_position_3d();
vertex_format_add_texcoord();
FORMAT_PT = vertex_format_end();
vertex_format_begin();
vertex_format_add_position_3d();
vertex_format_add_normal();
vertex_format_add_texcoord();
FORMAT_PNT = vertex_format_end();
vertex_format_begin();
vertex_format_add_position();
vertex_format_add_color();
FORMAT_2PC = vertex_format_end();
#endregion
#region 3d obj
function VertexObject() constructor {
positions = [];
textures = [];
normals = [];
faces = [];
buffer = noone;
renderSurface = noone;
renderTexture = noone;
static addPosition = function(_pos, _merge = false) {
if(!_merge) {
array_push(positions, _pos);
return array_length(positions) - 1;
}
var ind = array_find(positions, _pos);
if(ind == -1) {
array_push(positions, _pos);
return array_length(positions) - 1;
}
return ind;
}
static addNormal = function(_nor, _merge = false) {
if(!_merge) {
array_push(normals, _nor);
return array_length(normals) - 1;
}
var ind = array_find(normals, _nor);
if(ind == -1) {
array_push(normals, _nor);
return array_length(normals) - 1;
}
return ind;
}
static addTexture = function(_tex, _merge = false) {
if(!_merge) {
array_push(textures, _tex);
return array_length(textures) - 1;
}
var ind = array_find(textures, _tex);
if(ind == -1) {
array_push(textures, _tex);
return array_length(textures) - 1;
}
return ind;
}
static addFace = function(v1 = [0, 0, 0], n1 = [0, 0, 0], t1 = [0, 0],
v2 = [0, 0, 0], n2 = [0, 0, 0], t2 = [0, 0],
v3 = [0, 0, 0], n3 = [0, 0, 0], t3 = [0, 0], _merge = false) {
var pi0 = addPosition(v1, _merge);
var pi1 = addPosition(v2, _merge);
var pi2 = addPosition(v3, _merge);
var ni0 = addNormal(n1, _merge);
var ni1 = addNormal(n2, _merge);
var ni2 = addNormal(n3, _merge);
var ti0 = addTexture(t1, _merge);
var ti1 = addTexture(t2, _merge);
var ti2 = addTexture(t3, _merge);
array_append(faces, [
[pi0, ni0, ti0],
[pi1, ni1, ti1],
[pi2, ni2, ti2],
]);
}
static createBuffer = function() {
if(buffer != noone) vertex_delete_buffer(buffer);
var VB = vertex_create_buffer();
vertex_begin(VB, FORMAT_PNT);
for( var i = 0, n = array_length(faces); i < n; i++ ) {
var face = faces[i];
var _pos = positions[face[0]];
var _nor = normals [face[1]];
var _tex = textures [face[2]];
vertex_add_pnt(VB, _pos, _nor, _tex);
}
vertex_end(VB);
vertex_freeze(VB);
buffer = VB;
return VB;
}
static submit = function(surface = noone) {
if(!is_surface(surface)) {
__submit();
return;
}
renderSurface = surface;
submitTexture(surface_get_texture(surface));
}
static submitTexture = function(texture) {
renderTexture = texture;
__submit();
}
static __submit = function() {
if(renderTexture == noone) return;
if(buffer == noone) return;
vertex_submit(buffer, pr_trianglelist, renderTexture);
}
static clone = function(_submit = true) {
var v = new VertexObject();
v.positions = array_clone(positions);
v.textures = array_clone(textures);
v.normals = array_clone(normals);
v.faces = array_clone(faces);
v.renderTexture = renderTexture;
if(_submit) v.createBuffer();
return v;
}
static destroy = function() {
vertex_delete_buffer(buffer);
}
}
#endregion
#region primitives
var _0 = -.5;
var _1 = .5;
var v = new VertexObject();
v.addFace( [_1, _0, 0], [0, 0, 1], [1, 0],
[_0, _0, 0], [0, 0, 1], [0, 0],
[_1, _1, 0], [0, 0, 1], [1, 1], );
v.addFace( [_1, _1, 0], [0, 0, 1], [1, 1],
[_0, _0, 0], [0, 0, 1], [0, 0],
[_0, _1, 0], [0, 0, 1], [0, 1], );
PRIMITIVES[? "plane"] = v;
var v = [];
v[0] = new VertexObject();
v[0].addFace( [_1, _0, _0], [0, 0, -1], [1, 0],
[_0, _0, _0], [0, 0, -1], [0, 0],
[_1, _1, _0], [0, 0, -1], [1, 1], );
v[0].addFace( [_1, _1, _0], [0, 0, -1], [1, 1],
[_0, _0, _0], [0, 0, -1], [0, 0],
[_0, _1, _0], [0, 0, -1], [0, 1], );
v[1] = new VertexObject();
v[1].addFace( [_1, _0, _1], [0, 0, 1], [0, 0],
[_0, _0, _1], [0, 0, 1], [1, 0],
[_1, _1, _1], [0, 0, 1], [0, 1], );
v[1].addFace( [_1, _1, _1], [0, 0, 1], [0, 1],
[_0, _0, _1], [0, 0, 1], [1, 0],
[_0, _1, _1], [0, 0, 1], [1, 1], );
v[2] = new VertexObject();
v[2].addFace( [_1, _0, _0], [0, -1, 0], [1, 0],
[_0, _0, _0], [0, -1, 0], [0, 0],
[_1, _0, _1], [0, -1, 0], [1, 1], );
v[2].addFace( [_1, _0, _1], [0, -1, 0], [1, 1],
[_0, _0, _0], [0, -1, 0], [0, 0],
[_0, _0, _1], [0, -1, 0], [0, 1], );
v[3] = new VertexObject();
v[3].addFace( [_1, _1, _0], [0, 1, 0], [1, 0],
[_0, _1, _0], [0, 1, 0], [0, 0],
[_1, _1, _1], [0, 1, 0], [1, 1], );
v[3].addFace( [_1, _1, _1], [0, 1, 0], [1, 1],
[_0, _1, _0], [0, 1, 0], [0, 0],
[_0, _1, _1], [0, 1, 0], [0, 1], );
v[4] = new VertexObject();
v[4].addFace( [_0, _1, _0], [-1, 0, 0], [1, 1],
[_0, _0, _0], [-1, 0, 0], [1, 0],
[_0, _1, _1], [-1, 0, 0], [0, 1], );
v[4].addFace( [_0, _1, _1], [-1, 0, 0], [0, 1],
[_0, _0, _0], [-1, 0, 0], [1, 0],
[_0, _0, _1], [-1, 0, 0], [0, 0], );
v[5] = new VertexObject();
v[5].addFace( [_1, _1, _0], [1, 0, 0], [0, 1],
[_1, _0, _0], [1, 0, 0], [0, 0],
[_1, _1, _1], [1, 0, 0], [1, 1], );
v[5].addFace( [_1, _1, _1], [1, 0, 0], [1, 1],
[_1, _0, _0], [1, 0, 0], [0, 0],
[_1, _0, _1], [1, 0, 0], [1, 0], );
PRIMITIVES[? "cube"] = v;
#endregion
#region helper
enum GIZMO_3D_TYPE {
move,
rotate,
scale
}
function _3d_node_init(iDim, gPos, gSca, iPos, iRot, iSca) {
VB = [];
use_normal = true;
TM = matrix_build(0, 0, 0, 0, 0, 0, 1, 1, 1);
cam = camera_create();
cam_view = matrix_build_lookat(0, 0, 1, 0, 0, 0, 0, 1, 0);
cam_proj = matrix_build_projection_ortho(1, 1, 1, 100);
camera_set_view_mat(cam, cam_view);
camera_set_proj_mat(cam, cam_proj);
drag_index = noone;
drag_sv = 0;
drag_delta = 0;
drag_prev = 0;
drag_mx = 0;
drag_my = 0;
input_dim = iDim;
global_pos = gPos;
global_sca = gSca;
input_pos = iPos;
input_rot = iRot;
input_sca = iSca;
gizmo_hover = noone;
tools = [
new NodeTool( "Transform", THEME.tools_3d_transform ),
new NodeTool( "Rotate", THEME.tools_3d_rotate ),
new NodeTool( "Scale", THEME.tools_3d_scale ),
];
}
function _3d_gizmo(active, _x, _y, _s, _mx, _my, _snx, _sny) { #region
var _gpos = getInputData(global_pos);
var _gsca = getInputData(global_sca);
var _pos = getInputData(input_pos);
var _rot = getInputData(input_rot);
var _sca = getInputData(input_sca);
var cx = _x + _gpos[0] * _s;
var cy = _y + _gpos[1] * _s;
var _qrot = new BBMOD_Quaternion().FromEuler(_rot[0], -_rot[1], -_rot[2]);
var _hover = noone;
var _hoverDist = 10;
if(isUsingTool(0)) {
var ga = [];
var dir = 0;
ga[0] = new BBMOD_Vec3(64, 0, 0);
ga[1] = new BBMOD_Vec3( 0, -64, 0);
ga[2] = new BBMOD_Vec3( 0, 0, 64);
for( var i = 0; i < 3; i++ ) {
ga[i] = _qrot.Rotate(ga[i]);
var th = 2 + (gizmo_hover == i);
if(drag_index == i) {
th = 3;
dir = point_direction(0, 0, ga[i].X, ga[i].Y);
} else if(drag_index != noone)
th = 1;
draw_set_color(COLORS.axis[i]);
draw_line_round_arrow(cx, cy, cx + ga[i].X, cy + ga[i].Y, th);
var _d = distance_to_line(_mx, _my, cx, cy, cx + ga[i].X, cy + ga[i].Y);
if(_d < _hoverDist) {
_hover = i;
_hoverDist = _d;
}
}
gizmo_hover = _hover;
if(drag_index != noone) {
var mAng = point_direction(cx, cy, _mx, _my);
var _n = BBMOD_VEC3_FORWARD;
var _mmx = _mx - cx;
var _mmy = _my - cy;
var _max = ga[drag_index].X;
var _may = ga[drag_index].Y;
var mAdj = dot_product(_mmx, _mmy, _max, _may);
if(drag_prev != undefined) {
_pos[drag_index] += (mAdj - drag_prev) / 8000;
if(inputs[| input_pos].setValue(_pos))
UNDO_HOLDING = true;
}
drag_prev = mAdj;
}
} else if(isUsingTool(1)) {
var _pa = [], pa = [];
var drx, dry, drz;
var _sub = 64;
for( var i = 0; i <= _sub; i++ ) {
var ang = i * 360 / _sub;
pa[0] = new BBMOD_Vec3(0, lengthdir_x(64, ang), lengthdir_y(64, ang));
pa[0] = _qrot.Rotate(pa[0]);
pa[1] = new BBMOD_Vec3(lengthdir_x(64, ang), 0, lengthdir_y(64, ang));
pa[1] = _qrot.Rotate(pa[1]);
pa[2] = new BBMOD_Vec3(lengthdir_x(64, ang), lengthdir_y(64, ang), 0);
pa[2] = _qrot.Rotate(pa[2]);
if(i) {
for( var j = 0; j < 3; j++ ) {
draw_set_color(COLORS.axis[j]);
var th = (_pa[j].Z >= 0) + 1 + (gizmo_hover == j);
if(drag_index == j)
th = 3;
else if(drag_index != noone)
th = 1;
if(_pa[j].Z >= 0 || i % 2 || drag_index == j) {
draw_line_round(cx + _pa[j].X, cy + _pa[j].Y, cx + pa[j].X, cy + pa[j].Y, th);
drx = point_direction(cx + _pa[j].X, cy + _pa[j].Y, cx + pa[j].X, cy + pa[j].Y);
}
var _d = distance_to_line(_mx, _my, cx + _pa[j].X, cy + _pa[j].Y, cx + pa[j].X, cy + pa[j].Y);
if(_d < _hoverDist) {
_hover = j;
_hoverDist = _d;
}
}
}
for( var j = 0; j < 3; j++ )
_pa[j] = pa[j];
}
gizmo_hover = _hover;
if(drag_index != noone) {
var mAng = point_direction(cx, cy, _mx, _my);
var _n = BBMOD_VEC3_FORWARD;
switch(drag_index) {
case 0 : _n = new BBMOD_Vec3(1.0, 0.0, 0.0); break;
case 1 : _n = new BBMOD_Vec3(0.0, 1.0, 0.0); break;
case 2 : _n = new BBMOD_Vec3(0.0, 0.0, 1.0); break;
}
if(drag_prev != undefined) {
var _currQ = new BBMOD_Quaternion().FromEuler(_rot[0], _rot[1], _rot[2]);
var _currR = new BBMOD_Quaternion().FromAxisAngle(_n, (mAng - drag_prev) * (_currQ.Rotate(_n).Z > 0? -1 : 1));
var _mulp = _currQ.Mul(_currR);
var _Nrot = new BBMOD_Matrix(_mulp.ToMatrix()).ToEuler();
if(inputs[| input_rot].setValue(_Nrot))
UNDO_HOLDING = true;
}
draw_set_color(COLORS._main_accent);
draw_line_dashed(cx, cy, _mx, _my, 2, 8);
drag_prev = mAng;
}
} else if(isUsingTool(2)) {
var ga = [];
ga[0] = new BBMOD_Vec3(64, 0, 0);
ga[1] = new BBMOD_Vec3( 0, -64, 0);
ga[2] = new BBMOD_Vec3( 0, 0, 64);
for( var i = 0; i < 3; i++ ) {
ga[i] = _qrot.Rotate(ga[i]);
var th = 2 + (gizmo_hover == i);
if(drag_index == i)
th = 3;
else if(drag_index != noone)
th = 1;
draw_set_color(COLORS.axis[i]);
draw_line_round_arrow_scale(cx, cy, cx + ga[i].X, cy + ga[i].Y, th);
var _d = distance_to_line(_mx, _my, cx, cy, cx + ga[i].X, cy + ga[i].Y);
if(_d < _hoverDist) {
_hover = i;
_hoverDist = _d;
}
}
gizmo_hover = _hover;
if(drag_index != noone) {
var mAng = point_direction(cx, cy, _mx, _my);
var _n = BBMOD_VEC3_FORWARD;
var _mmx = _mx - cx;
var _mmy = _my - cy;
var _max = ga[drag_index].X;
var _may = ga[drag_index].Y;
var mAdj = dot_product(_mmx, _mmy, _max, _may);
if(drag_prev != undefined) {
_sca[drag_index] += (mAdj - drag_prev) / 8000;
if(inputs[| input_sca].setValue(_sca))
UNDO_HOLDING = true;
}
drag_prev = mAdj;
}
}
if(drag_index != noone && mouse_release(mb_left)) {
drag_index = noone;
UNDO_HOLDING = false;
}
if(_hover != noone && mouse_press(mb_left, active)) {
drag_index = _hover;
drag_prev = undefined;
drag_mx = _mx;
drag_my = _my;
}
inputs[| global_pos].drawOverlay(hover, active, _x, _y, _s, _mx, _my, _snx, _sny);
#endregion
}
function _3d_local_transform(_lpos, _lrot, _lsca) {
matrix_stack_push(matrix_build(0, 0, 0, _lrot[0], _lrot[1], _lrot[2], 1, 1, 1));
matrix_stack_push(matrix_build(-_lpos[0], -_lpos[1], _lpos[2], 0, 0, 0, 1, 1, 1));
matrix_stack_push(matrix_build(0, 0, 0, 0, 0, 0, _lsca[0], _lsca[1], _lsca[2]));
matrix_set(matrix_world, matrix_stack_top());
}
function _3d_clear_local_transform() {
matrix_stack_pop();
matrix_stack_pop();
matrix_stack_pop();
}
function __3d_transform(pos = 0, rot = 0, sca = 0, lpos = 0, lrot = 0, lsca = 0, apply_local = true, sdim = true) constructor {
self.pos = pos;
self.rot = rot;
self.sca = sca;
self.local_pos = lpos;
self.local_rot = lrot;
self.local_sca = lsca;
self.apply_local = apply_local;
self.scaleDimension = sdim;
}
function __3d_light(dir = 0, height = 0, intensity = 0, color = c_white, ambient = c_white) constructor {
self.dir = dir;
self.height = height;
self.intensity = intensity;
self.color = color;
self.ambient = ambient;
}
function __3d_camera(proj, fov) constructor {
self.projection = proj;
self.fov = fov;
}
function _3d_pre_setup(_outSurf, _dim, _transform, _light, _cam, _pass = "diff") {
_outSurf = surface_verify(_outSurf, _dim[0], _dim[1]);
var _pos = _transform.pos;
var _sca = _transform.sca;
var _lpos = _transform.local_pos;
var _lrot = _transform.local_rot;
var _lsca = _transform.local_sca;
var _ldir = _light.dir;
var _lhgt = _light.height;
var _lint = _light.intensity;
var _lclr = _light.color;
var _aclr = _light.ambient;
var _proj = _cam.projection;
var _fov = _cam.fov;
var _applyLocal = _transform.apply_local;
var scaleDimension = _transform.scaleDimension;
var lightFor = [ -cos(degtorad(_ldir)), -_lhgt, -sin(degtorad(_ldir)) ];
gpu_set_ztestenable(true);
surface_set_target(_outSurf);
DRAW_CLEAR
var shader = sh_vertex_pnt_light;
if(_pass == "diff") shader = sh_vertex_pnt_light;
else if(_pass == "norm") shader = sh_vertex_normal_pass;
else if(_pass == "dept") shader = sh_vertex_depth_pass;
uniVertex_lightFor = shader_get_uniform(shader, "u_LightForward");
uniLightAmb = shader_get_uniform(shader, "u_AmbientLight");
uniLightClr = shader_get_uniform(shader, "u_LightColor");
uniLightInt = shader_get_uniform(shader, "u_LightIntensity");
uniLightNrm = shader_get_uniform(shader, "useNormal");
shader_set(shader);
shader_set_uniform_f_array_safe(uniVertex_lightFor, lightFor);
shader_set_uniform_f_array_safe(uniLightAmb, colorToArray(_aclr));
shader_set_uniform_f_array_safe(uniLightClr, colorToArray(_lclr));
shader_set_uniform_f(uniLightInt, _lint);
shader_set_uniform_i(uniLightNrm, use_normal);
var cam_view, cam_proj;
var dw = array_safe_get_fast(_dim, 0);
var dh = array_safe_get_fast(_dim, 1);
if(_proj == CAMERA_PROJ.ortho) {
cam_view = matrix_build_lookat(0, 0, 128, 0, 0, 0, 0, 1, 0);
cam_proj = matrix_build_projection_ortho(dw, dh, 0.1, 256);
} else {
var _adjFov = power(_fov / 90, 1 / 4) * 90;
var dist = _dim[0] / 2 * dtan(90 - _adjFov);
cam_view = matrix_build_lookat(0, 0, 1 + dist, 0, 0, 0, 0, 1, 0);
cam_proj = matrix_build_projection_perspective(dw, dh, dist, dist + 256);
}
var cam = camera_get_active();
camera_set_view_size(cam, dw, dh);
camera_set_view_mat(cam, cam_view);
camera_set_proj_mat(cam, cam_proj);
camera_apply(cam);
if(_proj == CAMERA_PROJ.ortho)
matrix_stack_push(matrix_build(dw / 2 - _pos[0], _pos[1] - dh / 2, 0, 0, 0, 0, (scaleDimension? dw : 1) * _sca[0], (scaleDimension? dh : 1) * _sca[1], 1));
else
matrix_stack_push(matrix_build(dw / 2 - _pos[0], _pos[1] - dh / 2, 0, 0, 0, 0, (scaleDimension? dw : 1) * _sca[0], (scaleDimension? dh : 1) * _sca[1], 1));
//matrix_stack_push(matrix_build(0, 0, 0, 0, 0, 0, 1, 1, 1));
if(_applyLocal) _3d_local_transform(_lpos, _lrot, _lsca);
matrix_set(matrix_world, matrix_stack_top());
return _outSurf;
}
function _3d_post_setup() {
shader_reset();
matrix_stack_clear();
matrix_set(matrix_world, MATRIX_IDENTITY);
gpu_set_ztestenable(false);
var cam = camera_get_active();
camera_set_view_mat(cam, matrix_build_lookat(0, 0, 1, 0, 0, 0, 0, 1, 0));
camera_set_proj_mat(cam, matrix_build_projection_ortho(1, 1, 0.1, 256));
camera_apply(cam);
surface_reset_target();
}
#endregion