- [Outline] Add option to crop overflow pixel for inside outline.

This commit is contained in:
MakhamDev 2023-10-23 16:39:41 +07:00
parent 70d6876fe7
commit 8e6d37c7cd
17 changed files with 390 additions and 169 deletions

View file

@ -224,6 +224,7 @@
{"name":"s_node_corner","order":16,"path":"sprites/s_node_corner/s_node_corner.yy",},
{"name":"sh_cell_noise_crystal","order":1,"path":"shaders/sh_cell_noise_crystal/sh_cell_noise_crystal.yy",},
{"name":"panel_function","order":2,"path":"scripts/panel_function/panel_function.yy",},
{"name":"sh_shape_normalize","order":36,"path":"shaders/sh_shape_normalize/sh_shape_normalize.yy",},
{"name":"node_time_remap","order":3,"path":"scripts/node_time_remap/node_time_remap.yy",},
{"name":"sh_perlin","order":4,"path":"shaders/sh_perlin/sh_perlin.yy",},
{"name":"sh_normal_light","order":1,"path":"shaders/sh_normal_light/sh_normal_light.yy",},

View file

@ -774,6 +774,7 @@
{"id":{"name":"s_node_corner","path":"sprites/s_node_corner/s_node_corner.yy",},},
{"id":{"name":"sh_cell_noise_crystal","path":"shaders/sh_cell_noise_crystal/sh_cell_noise_crystal.yy",},},
{"id":{"name":"panel_function","path":"scripts/panel_function/panel_function.yy",},},
{"id":{"name":"sh_shape_normalize","path":"shaders/sh_shape_normalize/sh_shape_normalize.yy",},},
{"id":{"name":"node_time_remap","path":"scripts/node_time_remap/node_time_remap.yy",},},
{"id":{"name":"sh_perlin","path":"shaders/sh_perlin/sh_perlin.yy",},},
{"id":{"name":"sh_normal_light","path":"shaders/sh_normal_light/sh_normal_light.yy",},},

View file

@ -86,6 +86,11 @@
addHotkey("", "Close file", "Q", MOD_KEY.ctrl, function() { PANEL_GRAPH.close(); });
addHotkey("", "Close program", vk_f4, MOD_KEY.alt, window_close);
addHotkey("", "Reload theme", vk_f10, MOD_KEY.ctrl | MOD_KEY.shift, function() {
loadGraphic(PREF_MAP[? "theme"]);
resetPanel();
} );
globalvar HOTKEY_MOD, HOTKEY_BLOCK;
HOTKEY_MOD = 0;
HOTKEY_BLOCK = false;

View file

@ -229,4 +229,12 @@ function array_spread(arr, _arr = []) {
array_spread(arr[i], _arr);
return _arr;
}
function array_verify(arr, length) {
if(!is_array(arr)) return array_create(length);
if(array_length(arr) == length) return arr;
array_resize(arr, length);
return arr;
}

View file

@ -2,6 +2,7 @@
#macro __draw_sprite_ext draw_sprite_ext
function draw_sprite_ext_override(spr, ind, _x, _y, xscale = 1, yscale = 1, rot = 0, color = c_white, alpha = 1) {
gml_pragma("forceinline");
__draw_sprite_ext(spr, ind, round(_x), round(_y), xscale, yscale, rot, color, alpha);
}
@ -9,6 +10,7 @@ function draw_sprite_ext_override(spr, ind, _x, _y, xscale = 1, yscale = 1, rot
#macro __draw_sprite_stretched_ext draw_sprite_stretched_ext
function draw_sprite_stretched_ext_override(spr, ind, _x, _y, w = 1, h = 1, color = c_white, alpha = 1) {
gml_pragma("forceinline");
__draw_sprite_stretched_ext(spr, ind, round(_x), round(_y), round(w), round(h), color, alpha);
}
@ -16,16 +18,20 @@ function draw_sprite_stretched_ext_override(spr, ind, _x, _y, w = 1, h = 1, colo
#macro __draw_sprite_stretched draw_sprite_stretched
function draw_sprite_stretched_override(spr, ind, _x, _y, w = 1, h = 1) {
gml_pragma("forceinline");
__draw_sprite_stretched(spr, ind, round(_x), round(_y), round(w), round(h));
}
function draw_sprite_ext_add(spr, ind, _x, _y, xscale = 1, yscale = 1, rot = 0, color = c_white, alpha = 1) {
gml_pragma("forceinline");
BLEND_ADD
__draw_sprite_ext(spr, ind, round(_x), round(_y), xscale, yscale, rot, color, alpha);
BLEND_NORMAL
}
function draw_sprite_stretched_points(spr, ind, _x0, _y0, _x1, _y1) {
gml_pragma("forceinline");
var _xs = round(min(_x0, _x1));
var _ys = round(min(_y0, _y1));
var _w = round(max(_x0, _x1) - _xs);
@ -35,15 +41,18 @@ function draw_sprite_stretched_points(spr, ind, _x0, _y0, _x1, _y1) {
}
function draw_sprite_bbox(spr, ind, _bbox) {
gml_pragma("forceinline");
if(_bbox == noone) return;
__draw_sprite_stretched(spr, ind, _bbox.x0, _bbox.y0, _bbox.w, _bbox.h);
}
function draw_sprite_uniform(spr, ind, _x, _y, scale, color = c_white) {
gml_pragma("forceinline");
draw_sprite_ext(spr, ind, round(_x), round(_y), scale, scale, 0, color, 1);
}
function draw_sprite_ui(spr, ind, _x, _y, xscale = 1, yscale = 1, rot = 0, color = c_white, alpha = 1) {
gml_pragma("forceinline");
static UI_SPRITE_SCALE = 1;
var xscale_ui = ui(xscale) / UI_SPRITE_SCALE;
@ -53,10 +62,12 @@ function draw_sprite_ui(spr, ind, _x, _y, xscale = 1, yscale = 1, rot = 0, color
}
function draw_sprite_ui_uniform(spr, ind, _x, _y, scale = 1, color = c_white, alpha = 1, rot = 0) {
gml_pragma("forceinline");
draw_sprite_ui(spr, ind, round(_x), round(_y), scale, scale, rot, color, alpha);
}
function draw_sprite_colored(spr, ind, _x, _y, scale = 1, rot = 0) {
gml_pragma("forceinline");
var num = sprite_get_number(spr);
draw_sprite_ui(spr, ind, _x, _y, scale, scale, rot, c_white);

View file

@ -494,7 +494,7 @@ function Node(_x, _y, _group = PANEL_GRAPH.getCurrentContext()) : __Node_Base(_x
} #endregion
static getInputs = function(frame = CURRENT_FRAME) { #region
inputs_data = array_create(ds_list_size(inputs), undefined);
inputs_data = array_verify(inputs_data, ds_list_size(inputs));
for(var i = 0; i < ds_list_size(inputs); i++) {
if(!is_instanceof(inputs[| i], NodeValue)) continue;

View file

@ -1,21 +1,6 @@
function Node_Outline(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) constructor {
name = "Outline";
shader = sh_outline;
uniform_dim = shader_get_uniform(shader, "dimension");
uniform_border_start = shader_get_uniform(shader, "borderStart");
uniform_border_size = shader_get_uniform(shader, "borderSize");
uniform_border_color = shader_get_uniform(shader, "borderColor");
uniform_blend = shader_get_uniform(shader, "is_blend");
uniform_blend_alpha = shader_get_uniform(shader, "blend_alpha");
uniform_side = shader_get_uniform(shader, "side");
uniform_aa = shader_get_uniform(shader, "is_aa");
uniform_out_only = shader_get_uniform(shader, "outline_only");
uniform_sam = shader_get_uniform(shader, "sampleMode");
inputs[| 0] = nodeValue("Surface in", self, JUNCTION_CONNECT.input, VALUE_TYPE.surface, 0);
inputs[| 1] = nodeValue("Width", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 0);
inputs[| 2] = nodeValue("Color", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, c_white);
@ -43,20 +28,30 @@ function Node_Outline(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) c
inputs[| 11] = nodeValue("Active", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, true);
active_index = 11;
inputs[| 12] = nodeValue("Crop border", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, false);
outputs[| 0] = nodeValue("Surface out", self, JUNCTION_CONNECT.output, VALUE_TYPE.surface, noone);
outputs[| 1] = nodeValue("Outline", self, JUNCTION_CONNECT.output, VALUE_TYPE.surface, noone);
input_display_list = [ 11,
["Output", true], 0, 9, 10,
["Outline", false], 1, 5, 8,
["Outline", false], 1, 5, 8, 12,
["Render", false], 2, 3, 4, 6,
];
attribute_surface_depth();
attribute_oversample();
static processData = function(_outSurf, _data, _output_index, _array_index) {
static step = function() { #region
var blend = getInputData(3);
var _side = getInputData(5);
inputs[| 4].setVisible(blend);
inputs[| 12].setVisible(_side == 0);
} #endregion
static processData = function(_outSurf, _data, _output_index, _array_index) { #region
var ww = surface_get_width_safe(_data[0]);
var hh = surface_get_height_safe(_data[0]);
var wd = _data[1];
@ -68,37 +63,27 @@ function Node_Outline(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) c
var aa = _data[6];
var sam = struct_try_get(attributes, "oversample");
var bst = _data[8];
var _crop = _data[12];
surface_set_target(_outSurf);
DRAW_CLEAR
BLEND_OVERRIDE;
shader_set(shader);
shader_set_uniform_f_array_safe(uniform_dim, [ww, hh]);
shader_set_uniform_f(uniform_border_start, bst);
shader_set_uniform_f(uniform_border_size, wd);
shader_set_uniform_f_array_safe(uniform_border_color, [color_get_red(cl) / 255, color_get_green(cl) / 255, color_get_blue(cl) / 255, 1.0]);
surface_set_shader(_outSurf, sh_outline);
shader_set_f("dimension", ww, hh);
shader_set_f("borderStart", bst);
shader_set_f("borderSize", wd);
shader_set_color("borderColor", cl);
shader_set_uniform_i(uniform_side, side);
shader_set_uniform_i(uniform_aa, aa);
shader_set_uniform_i(uniform_out_only, _output_index);
shader_set_uniform_i(uniform_blend, blend);
shader_set_uniform_f(uniform_blend_alpha, alpha);
shader_set_uniform_i(uniform_sam, sam);
shader_set_i("side", side);
shader_set_i("is_aa", aa);
shader_set_i("outline_only", _output_index);
shader_set_i("is_blend", blend);
shader_set_f("blend_alpha", alpha);
shader_set_i("sampleMode", sam);
shader_set_i("crop_border", _crop);
draw_surface_safe(_data[0], 0, 0);
shader_reset();
BLEND_NORMAL;
surface_reset_target();
surface_reset_shader();
_outSurf = mask_apply(_data[0], _outSurf, _data[9], _data[10]);
return _outSurf;
}
static step = function() {
var blend = getInputData(3);
inputs[| 4].setVisible(blend);
}
} #endregion
}

View file

@ -44,16 +44,19 @@ function Node_Processor(_x, _y, _group = noone) : Node(_x, _y, _group) construct
var _l = output? outputs : inputs;
var _n = _l[| _index];
var _in = output? _n.getValue() : getInputData(_index);
//print($"Getting value {name}: {_index}: {_arr}: {_n.isArray()}: {_in}");
if(!_n.isArray()) return _in;
var _aIndex = _arr;
switch(attributes.array_process) {
case ARRAY_PROCESS.loop : _index = safe_mod(_arr, array_length(_in)); break;
case ARRAY_PROCESS.hold : _index = min(_arr, array_length(_in) - 1); break;
case ARRAY_PROCESS.expand : _index = floor(_arr / process_length[_index][1]) % process_length[_index][0]; break;
case ARRAY_PROCESS.expand_inv : _index = floor(_arr / process_length[ds_list_size(_l) - 1 - _index][1]) % process_length[_index][0]; break;
case ARRAY_PROCESS.loop : _aIndex = safe_mod(_arr, array_length(_in)); break;
case ARRAY_PROCESS.hold : _aIndex = min(_arr, array_length(_in) - 1); break;
case ARRAY_PROCESS.expand : _aIndex = floor(_arr / process_length[_index][1]) % process_length[_index][0]; break;
case ARRAY_PROCESS.expand_inv : _aIndex = floor(_arr / process_length[ds_list_size(_l) - 1 - _index][1]) % process_length[_index][0]; break;
}
return array_safe_get(_in, _index);
return array_safe_get(_in, _aIndex);
} #endregion
static getDimension = function(arr = 0) { #region
@ -253,9 +256,9 @@ function Node_Processor(_x, _y, _group = noone) : Node(_x, _y, _group) construct
preGetInputs();
process_amount = 1;
inputs_data = array_create(ds_list_size(inputs));
inputs_is_array = array_create(ds_list_size(inputs));
process_length = array_create(ds_list_size(inputs));
inputs_data = array_verify(inputs_data, ds_list_size(inputs));
inputs_is_array = array_verify(inputs_is_array, ds_list_size(inputs));
process_length = array_verify(process_length, ds_list_size(inputs));
for(var i = 0; i < ds_list_size(inputs); i++) {
var val = inputs[| i].getValue();

View file

@ -56,7 +56,7 @@ function Node_Render_Sprite_Sheet(_x, _y, _group = noone) : Node(_x, _y, _group)
attribute_surface_depth();
static step = function() {
static step = function() { #region
var grup = getInputData(1);
var pack = getInputData(3);
@ -66,9 +66,11 @@ function Node_Render_Sprite_Sheet(_x, _y, _group = noone) : Node(_x, _y, _group)
inputs[| 2].setVisible(grup == SPRITE_ANIM_GROUP.animation);
inputs[| 4].setVisible(pack == SPRITE_STACK.grid);
inputs[| 5].setVisible(pack != SPRITE_STACK.grid);
}
update_on_frame = grup == 0;
} #endregion
static update = function(frame = CURRENT_FRAME) {
static update = function(frame = CURRENT_FRAME) { #region
var inpt = getInputData(0);
var grup = getInputData(1);
var skip = getInputData(2);
@ -178,7 +180,11 @@ function Node_Render_Sprite_Sheet(_x, _y, _group = noone) : Node(_x, _y, _group)
var py = padd[1];
for(var i = 0; i < array_length(inpt); i++) {
if(!is_surface(inpt[i])) break;
if(!is_surface(inpt[i])) {
_atl[i] = noone;
break;
}
var oo = noone;
if(!is_array(oupt)) oo = oupt;
else oo = oupt[i];
@ -206,7 +212,6 @@ function Node_Render_Sprite_Sheet(_x, _y, _group = noone) : Node(_x, _y, _group)
_atl[i] = array_push_create(_atl[i], new SurfaceAtlas(inpt[i], _sx, _sy));
draw_surface_safe(inpt[i], _sx, _sy);
break;
case SPRITE_STACK.vertical :
var py = padd[1] + _frame * _h + max(0, _frame) * spac;
@ -242,9 +247,9 @@ function Node_Render_Sprite_Sheet(_x, _y, _group = noone) : Node(_x, _y, _group)
if(drawn) array_safe_set(anim_drawn, CURRENT_FRAME, true);
outputs[| 1].setValue(_atl);
}
} #endregion
static onInspector1Update = function(updateAll = true) {
static onInspector1Update = function(updateAll = true) { #region
var key = ds_map_find_first(PROJECT.nodeMap);
repeat(ds_map_size(PROJECT.nodeMap)) {
@ -256,9 +261,9 @@ function Node_Render_Sprite_Sheet(_x, _y, _group = noone) : Node(_x, _y, _group)
node.initRender();
}
}
} #endregion
static initRender = function() {
static initRender = function() { #region
for(var i = 0; i < array_length(anim_drawn); i++) anim_drawn[i] = false;
var inpt = getInputData(0);
@ -427,5 +432,5 @@ function Node_Render_Sprite_Sheet(_x, _y, _group = noone) : Node(_x, _y, _group)
outputs[| 0].setValue(_surf);
outputs[| 1].setValue(_atl);
}
} #endregion
}

View file

@ -12,24 +12,6 @@ enum NODE_SHAPE_TYPE {
function Node_Shape(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) constructor {
name = "Shape";
shader = sh_shape;
uniform_shape = shader_get_uniform(shader, "shape");
uniform_cent = shader_get_uniform(shader, "center");
uniform_scal = shader_get_uniform(shader, "scale");
uniform_side = shader_get_uniform(shader, "sides");
uniform_angle = shader_get_uniform(shader, "angle");
uniform_inner = shader_get_uniform(shader, "inner");
uniform_outer = shader_get_uniform(shader, "outer");
uniform_corner = shader_get_uniform(shader, "corner");
uniform_arange = shader_get_uniform(shader, "angle_range");
uniform_aa = shader_get_uniform(shader, "aa");
uniform_dim = shader_get_uniform(shader, "dimension");
uniform_bgCol = shader_get_uniform(shader, "bgColor");
uniform_drawDF = shader_get_uniform(shader, "drawDF");
uniform_stRad = shader_get_uniform(shader, "stRad");
uniform_edRad = shader_get_uniform(shader, "edRad");
inputs[| 0] = nodeValue("Dimension", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, DEF_SURF )
.setDisplay(VALUE_DISPLAY.vector);
@ -81,15 +63,17 @@ function Node_Shape(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) con
["Render", true], 10, 1, 11, 12
];
temp_surface = [ noone ];
attribute_surface_depth();
static drawOverlay = function(active, _x, _y, _s, _mx, _my, _snx, _sny) {
static drawOverlay = function(active, _x, _y, _s, _mx, _my, _snx, _sny) { #region
var _path = getInputData(14);
if(_path != noone && struct_has(_path, "getPointRatio")) return;
inputs[| 3].drawOverlay(active, _x, _y, _s, _mx, _my, _snx, _sny);
}
} #endregion
static processData = function(_outSurf, _data, _output_index, _array_index) {
static processData = function(_outSurf, _data, _output_index, _array_index) { #region
var _dim = _data[0];
var _bg = _data[1];
var _shape = _data[2];
@ -99,6 +83,7 @@ function Node_Shape(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) con
var _color = _data[10];
var _df = _data[12];
var _path = _data[14];
var _bgC = _data[11];
var _bgcol = _bg? colToVec4(_data[11]) : [0, 0, 0, 0];
inputs[| 3].setVisible(true);
@ -113,7 +98,7 @@ function Node_Shape(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) con
_outSurf = surface_verify(_outSurf, _dim[0], _dim[1], attrDepth());
if(_path != noone && struct_has(_path, "getPointRatio")) {
if(_path != noone && struct_has(_path, "getPointRatio")) { #region
inputs[| 3].setVisible(false);
inputs[| 4].setVisible(false);
inputs[| 5].setVisible(false);
@ -158,22 +143,20 @@ function Node_Shape(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) con
surface_reset_target();
return _outSurf;
}
} #endregion
surface_set_target(_outSurf);
surface_set_shader(_outSurf, sh_shape);
if(_bg) draw_clear_alpha(0, 1);
else DRAW_CLEAR
shader_set(shader);
inputs[| 4].setVisible(false);
inputs[| 5].setVisible(false);
inputs[| 7].setVisible(false);
inputs[| 8].setVisible(false);
inputs[| 9].setVisible(false);
inputs[| 4].setVisible(false);
inputs[| 5].setVisible(false);
inputs[| 7].setVisible(false);
inputs[| 8].setVisible(false);
inputs[| 9].setVisible(false);
inputs[| 13].setVisible(false);
switch(_shape) {
switch(_shape) { #region
case NODE_SHAPE_TYPE.rectangle :
inputs[| 9].setVisible(true);
break;
@ -184,20 +167,20 @@ function Node_Shape(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) con
inputs[| 7].setVisible(true);
inputs[| 9].setVisible(true);
shader_set_uniform_i(uniform_side, _data[4]);
shader_set_uniform_f(uniform_angle, degtorad(_data[7]));
shader_set_i("sides", _data[4]);
shader_set_f("angle", degtorad(_data[7]));
break;
case NODE_SHAPE_TYPE.star :
inputs[| 4].setVisible(true);
inputs[| 5].setVisible(true);
inputs[| 7].setVisible(true);
inputs[| 9].setVisible(true);
inputs[| 5].name = "Inner radius";
shader_set_uniform_i(uniform_side, _data[4]);
shader_set_uniform_f(uniform_angle, degtorad(_data[7]));
shader_set_uniform_f(uniform_inner, _data[5]);
shader_set_i("sides", _data[4]);
shader_set_f("angle", degtorad(_data[7]));
shader_set_f("inner", _data[5]);
break;
case NODE_SHAPE_TYPE.arc :
inputs[| 5].setVisible(true);
@ -208,54 +191,53 @@ function Node_Shape(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) con
var ar = _data[8];
var center = degtorad(ar[0] + ar[1]) / 2;
var range = degtorad(ar[0] - ar[1]) / 2;
shader_set_uniform_f(uniform_angle, center);
shader_set_uniform_f_array_safe(uniform_arange, [ sin(range), cos(range) ] );
shader_set_uniform_f(uniform_inner, _data[5] / 2);
shader_set_f("angle", center);
shader_set_f("angle_range", [ sin(range), cos(range) ] );
shader_set_f("inner", _data[5] / 2);
break;
case NODE_SHAPE_TYPE.teardrop :
inputs[| 5].setVisible(true);
inputs[| 5].setVisible(true);
inputs[| 13].setVisible(true);
inputs[| 5].name = "End radius";
inputs[| 5].name = "End radius";
inputs[| 13].name = "Start radius";
shader_set_uniform_f(uniform_edRad, _data[5]);
shader_set_uniform_f(uniform_stRad, _data[13]);
shader_set_f("edRad", _data[ 5]);
shader_set_f("stRad", _data[13]);
break;
case NODE_SHAPE_TYPE.cross :
inputs[| 9].setVisible(true);
inputs[| 9].setVisible(true);
inputs[| 13].setVisible(true);
inputs[| 13].name = "Outer radius";
shader_set_uniform_f(uniform_outer, _data[13]);
shader_set_f("outer", _data[13]);
break;
case NODE_SHAPE_TYPE.leaf :
inputs[| 5].setVisible(true);
inputs[| 13].setVisible(true);
inputs[| 5].name = "Inner radius";
inputs[| 13].name = "Outer radius";
shader_set_uniform_f(uniform_inner, _data[5]);
shader_set_uniform_f(uniform_outer, _data[13]);
shader_set_f("inner", _data[ 5]);
shader_set_f("outer", _data[13]);
break;
}
} #endregion
shader_set_uniform_f_array_safe(uniform_dim, _dim);
shader_set_uniform_i(uniform_shape, _shape);
shader_set_uniform_f_array_safe(uniform_bgCol, _bgcol);
shader_set_uniform_i(uniform_aa, _aa);
shader_set_uniform_i(uniform_drawDF, _df);
shader_set_uniform_f(uniform_corner, _corner);
shader_set_uniform_f_array_safe(uniform_cent, [ _posit[0] / _dim[0], _posit[1] / _dim[1] ]);
shader_set_uniform_f_array_safe(uniform_scal, [ _posit[2] / _dim[0], _posit[3] / _dim[1] ]);
shader_set_f("dimension", _dim);
shader_set_i("shape", _shape);
shader_set_f("bgColor", _bgcol);
shader_set_i("aa", _aa);
shader_set_i("drawDF", _df);
shader_set_f("corner", _corner);
draw_sprite_ext(s_fx_pixel, 0, 0, 0, _dim[0], _dim[1], 0, _color, 1);
shader_reset();
surface_reset_target();
shader_set_f("center", [ _posit[0] / _dim[0], _posit[1] / _dim[1] ]);
shader_set_f("scale", [ _posit[2] / _dim[0], _posit[3] / _dim[1] ]);
draw_sprite_stretched_ext(s_fx_pixel, 0, 0, 0, _dim[0], _dim[1], _color, 1);
surface_reset_shader();
return _outSurf;
}
} #endregion
}

View file

@ -38,7 +38,7 @@ function Node_Transform(_x, _y, _group = noone) : Node_Processor(_x, _y, _group)
.setDisplay(VALUE_DISPLAY.slider);
inputs[| 9] = nodeValue("Output dimension type", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, OUTPUT_SCALING.same_as_input)
.setDisplay(VALUE_DISPLAY.enum_scroll, [ "Same as input", "Constant", "Relative to input", "Scale" ]);
.setDisplay(VALUE_DISPLAY.enum_scroll, [ "Same as input", "Constant", "Relative to input", "Transformed" ]);
inputs[| 10] = nodeValue("Round position", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, false, "Round position to the closest integer value to avoid jittering.");
@ -64,25 +64,42 @@ function Node_Transform(_x, _y, _group = noone) : Node_Processor(_x, _y, _group)
var _surf = getSingleValue(0, arr);
var _out_type = getSingleValue(9, arr);
var _out = getSingleValue(1, arr);
var _rotate = getSingleValue(5, arr);
var _scale = getSingleValue(6, arr);
var ww, hh;
var sw = surface_get_width_safe(_surf);
var sh = surface_get_height_safe(_surf);
switch(_out_type) {
case OUTPUT_SCALING.same_as_input :
ww = surface_get_width_safe(_surf);
hh = surface_get_height_safe(_surf);
ww = sw;
hh = sh;
break;
case OUTPUT_SCALING.relative :
ww = surface_get_width_safe(_surf) * _out[0];
hh = surface_get_height_safe(_surf) * _out[1];
ww = sw * _out[0];
hh = sh * _out[1];
break;
case OUTPUT_SCALING.constant :
ww = _out[0];
hh = _out[1];
ww = _out[0];
hh = _out[1];
break;
case OUTPUT_SCALING.scale :
ww = surface_get_width_safe(_surf) * _scale[0];
hh = surface_get_height_safe(_surf) * _scale[1];
ww = sw * _scale[0];
hh = sh * _scale[1];
var p0 = point_rotate( 0, 0, ww / 2, hh / 2, _rotate);
var p1 = point_rotate(ww, 0, ww / 2, hh / 2, _rotate);
var p2 = point_rotate( 0, hh, ww / 2, hh / 2, _rotate);
var p3 = point_rotate(ww, hh, ww / 2, hh / 2, _rotate);
var minx = min(p0[0], p1[0], p2[0], p3[0]);
var maxx = max(p0[0], p1[0], p2[0], p3[0]);
var miny = min(p0[1], p1[1], p2[1], p3[1]);
var maxy = max(p0[1], p1[1], p2[1], p3[1]);
ww = maxx - minx;
hh = maxy - miny;
break;
}
@ -167,6 +184,19 @@ function Node_Transform(_x, _y, _group = noone) : Node_Processor(_x, _y, _group)
inputs[| 1].setVisible(false);
_ww = ww * sca[0];
_hh = hh * sca[1];
var p0 = point_rotate( 0, 0, _ww / 2, _hh / 2, rot);
var p1 = point_rotate(_ww, 0, _ww / 2, _hh / 2, rot);
var p2 = point_rotate( 0, _hh, _ww / 2, _hh / 2, rot);
var p3 = point_rotate(_ww, _hh, _ww / 2, _hh / 2, rot);
var minx = min(p0[0], p1[0], p2[0], p3[0]);
var maxx = max(p0[0], p1[0], p2[0], p3[0]);
var miny = min(p0[1], p1[1], p2[1], p3[1]);
var maxy = max(p0[1], p1[1], p2[1], p3[1]);
_ww = maxx - minx;
_hh = maxy - miny;
break;
}
if(_ww <= 0 || _hh <= 0) return;

View file

@ -4,10 +4,8 @@ function __initTheme() {
directory_create(root);
var _l = root + "/version";
if(file_exists(_l)) {
if(file_exists(_l))
var res = json_load_struct(_l);
//if(res.version == BUILD_NUMBER) return;
}
json_save_struct(_l, { version: BUILD_NUMBER });
log_message("THEME", $"unzipping default theme to {root}.");
@ -15,12 +13,17 @@ function __initTheme() {
}
function _sprite_path(rel, theme) {
return DIRECTORY + "themes/" + theme + "/graphics/" + string_replace_all(rel, "./", "");
gml_pragma("forceinline");
return $"{DIRECTORY}themes/{theme}/graphics/{string_replace_all(rel, "./", "")}";
}
function _sprite_load_from_struct(str, theme, key) {
gml_pragma("forceinline");
var path = _sprite_path(str.path, theme);
var s = sprite_add(path, str.subimages, false, true, str.xorigin, str.yorigin);
var s = sprite_add(path, str.subimages, false, true, str.xorigin, str.yorigin);
if(str.slice) {
var slice = sprite_nineslice_create();
slice.enabled = str.slice.enabled;
@ -39,6 +42,8 @@ function _sprite_load_from_struct(str, theme, key) {
}
function __getGraphicList() {
gml_pragma("forceinline");
var path = _sprite_path("./graphics.json", "default");
var s = file_text_read_all(path);
return json_try_parse(s);
@ -48,26 +53,33 @@ function loadGraphic(theme = "default") {
var sprDef = __getGraphicList();
var path = _sprite_path("./graphics.json", theme);
print($"Loading theme {theme}");
if(!file_exists(path)) {
noti_status("Theme not defined at " + path + ", rollback to default theme.");
return;
}
var s = file_text_read_all(path);
var s = file_text_read_all(path);
var graphics = variable_struct_get_names(sprDef);
var sprStr = json_try_parse(s);
var sprStr = json_try_parse(s);
var str;
for( var i = 0, n = array_length(graphics); i < n; i++ ) {
var key = graphics[i];
if(variable_struct_exists(sprStr, key)) {
var str = variable_struct_get(sprStr, key);
variable_struct_set(THEME, key, _sprite_load_from_struct(str, theme, key));
} else {
noti_status("Graphic resource for " + string(key) + " not found. Rollback to default directory.");
if(struct_has(THEME, key) && sprite_exists(THEME[$ key]))
sprite_delete(THEME[$ key]);
var str = variable_struct_get(sprDef, key);
variable_struct_set(THEME, key, _sprite_load_from_struct(str, "default", key));
if(struct_has(sprStr, key)) {
str = sprStr[$ key];
THEME[$ key] = _sprite_load_from_struct(str, theme, key);
} else {
noti_status($"Graphic resource for {key} not found. Rollback to default directory.");
str = sprDef[$ key];
THEME[$ key] = _sprite_load_from_struct(str, "default", key);
}
//print($"{key}: {THEME[$ key]} [{sprite_exists(THEME[$ key])}]");
}
}

View file

@ -297,7 +297,7 @@ function surface_size_lim(surface, width, height) {
if(sw <= width && sh <= height) return surface;
var ss = min(width / sw, height / sh);
var s = surface_create(sw * ss, sh * ss);
var s = surface_create(max(1, sw * ss), max(1, sh * ss));
surface_set_target(s);
DRAW_CLEAR;
draw_surface_ext_safe(surface, 0, 0, ss, ss, 0, c_white, 1);

View file

@ -10,6 +10,7 @@ uniform float borderSize;
uniform vec4 borderColor;
uniform int side;
uniform int crop_border;
uniform int is_aa;
uniform int is_blend;
@ -54,16 +55,16 @@ void main() {
bool closetCollected = false;
vec4 closetColor;
bool isBorder = false;
if(side == 0)
isBorder = point.a == 1.;
else if(side == 1)
isBorder = point.a < 1.;
#region filter out filled ot empty pixel
bool isBorder = false;
if(side == 0) isBorder = point.a == 1.;
else if(side == 1) isBorder = point.a < 1.;
if(!isBorder) {
gl_FragColor = col;
return;
}
if(!isBorder) {
gl_FragColor = col;
return;
}
#endregion
if(borderSize + borderStart > 0.) {
outline_alpha = 0.;
@ -79,14 +80,13 @@ void main() {
top = 1.;
base *= 2.;
}
vec2 pxs = (pixelPosition + vec2( cos(ang), sin(ang)) * i) / dimension;
vec4 sam = sampleTexture( pxs );
if(side == 0 && sam.a > 0.)
continue;
if(side == 1 && sam.a < 1.)
continue;
vec2 pxs = (pixelPosition + vec2( cos(ang), sin(ang)) * i) / dimension;
if(side == 0 && crop_border == 1 && (pxs.x < 0. || pxs.x > 1. || pxs.y < 0. || pxs.y > 1.)) continue;
vec4 sam = sampleTexture( pxs );
if(side == 0 && sam.a > 0.) continue; //inside border, skip if current pixel is filled
if(side == 1 && sam.a < 1.) continue; //outside border, skip if current pixel is empty
if(i < borderStart) {
i = 9999.;
@ -109,6 +109,8 @@ void main() {
for(float j = 0.; j < 4.; j++) {
float ang = j * tauDiv;
vec2 pxs = (pixelPosition + vec2( cos(ang), sin(ang)) ) / dimension;
if(side == 0 && crop_border == 1 && (pxs.x < 0. || pxs.x > 1. || pxs.y < 0. || pxs.y > 1.)) continue;
vec4 sam = sampleTexture( pxs );
if((side == 0 && sam.a == 0.) || (side == 1 && sam.a > 0.)) {

View file

@ -0,0 +1,147 @@
#extension GL_OES_standard_derivatives : require
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
uniform int shape;
uniform int bg;
uniform int aa;
uniform int sides;
uniform int drawDF;
uniform float angle;
uniform float inner;
uniform float outer;
uniform float corner;
uniform float stRad;
uniform float edRad;
uniform vec2 angle_range;
uniform vec2 dimension;
uniform vec2 center;
uniform vec2 scale;
uniform vec4 bgColor;
#define PI 3.14159265359
#define TAU 6.283185307179586
float sdRegularPolygon(in vec2 p, in float r, in int n, in float ang ) {
// these 4 lines can be precomputed for a given shape
float an = PI / float(n);
vec2 acs = vec2(cos(an), sin(an));
// reduce to first sector
float bn = mod(atan(p.x, p.y) + PI - ang, 2.0 * an) - an;
p = length(p) * vec2(cos(bn), abs(sin(bn)));
// line sdf
p -= r * acs;
p.y += clamp( -p.y, 0.0, r * acs.y);
return length(p) * sign(p.x);
}
// signed distance to a n-star polygon with external angle en
float sdStar(in vec2 p, in float r, in int n, in float m, in float ang) { // m=[2,n]
// these 4 lines can be precomputed for a given shape
float an = PI / float(n);
float en = PI / m;
vec2 acs = vec2(cos(an), sin(an));
vec2 ecs = vec2(cos(en), sin(en)); // ecs=vec2(0,1) and simplify, for regular polygon,
// reduce to first sector
float bn = mod( atan(p.x, p.y) + PI - ang, 2.0 * an) - an;
p = length(p) * vec2(cos(bn), abs(sin(bn)));
// line sdf
p -= r * acs;
p += ecs * clamp( -dot(p, ecs), 0.0, r * acs.y / ecs.y);
return length(p)*sign(p.x);
}
// sca is the sin/cos of the orientation
// scb is the sin/cos of the aperture
float sdArc( in vec2 p, in vec2 sca, in vec2 scb, in float ra, in float rb ) {
p *= mat2(sca.x, sca.y, -sca.y, sca.x);
p.x = abs(p.x);
float k = (scb.y * p.x > scb.x * p.y) ? dot(p.xy,scb) : length(p);
return sqrt( dot(p, p) + ra * ra - 2.0 * ra * k ) - rb;
}
float sdRoundBox( in vec2 p, in vec2 b, in vec4 r ) {
r.xy = (p.x > 0.0)? r.xy : r.zw;
r.x = (p.y > 0.0)? r.x : r.y;
vec2 q = abs(p) - b + r.x;
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r.x;
}
float sdBox( in vec2 p, in vec2 b ) {
vec2 d = abs(p) - b;
return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
}
float sdTearDrop( vec2 p, float r1, float r2, float h ) {
p.x = abs(p.x);
float b = (r1-r2)/h;
float a = sqrt(1.0-b*b);
float k = dot(p,vec2(-b,a));
if( k < 0.0 ) return length(p) - r1;
if( k > a*h ) return length(p-vec2(0.0,h)) - r2;
return dot(p, vec2(a,b) ) - r1;
}
float sdCross( in vec2 p, in vec2 b, float r ) {
p = abs(p); p = (p.y>p.x) ? p.yx : p.xy;
vec2 q = p - b;
float k = max(q.y,q.x);
vec2 w = (k>0.0) ? q : vec2(b.y-p.x,-k);
return sign(k)*length(max(w,0.0)) + r;
}
float sdVesica(vec2 p, float r, float d) {
p = abs(p);
float b = sqrt(r*r-d*d); // can delay this sqrt by rewriting the comparison
return ((p.y-b)*d > p.x*b) ? length(p-vec2(0.0,b))*sign(d)
: length(p-vec2(-d,0.0))-r;
}
void main() {
float color = 0.;
vec2 cen = (v_vTexcoord - center) / scale;
vec2 ratio = dimension / dimension.y;
float d;
if(shape == 0) {
d = sdBox( (v_vTexcoord - center) * ratio, (scale * ratio - corner));
d -= corner;
} else if(shape == 1) {
d = length(cen) - 1.;
} else if(shape == 2) {
d = sdRegularPolygon( cen, 0.9 - corner, sides, angle );
d -= corner;
} else if(shape == 3) {
d = sdStar( cen, 0.9 - corner, sides, 2. + inner * (float(sides) - 2.), angle );
d -= corner;
} else if(shape == 4) {
d = sdArc( cen, vec2(sin(angle), cos(angle)), angle_range, 0.9 - inner, inner );
} else if(shape == 5) {
d = sdTearDrop( cen + vec2(0., 0.5), stRad, edRad, 1. );
} else if(shape == 6) {
d = sdCross( cen, vec2(1. + corner, outer), corner );
} else if(shape == 7) {
d = sdVesica( cen, inner, outer );
}
//d = d;
if(drawDF == 1)
color = -d;
else if(aa == 0)
color = step(d, 0.0);
else
color = smoothstep(0.02, -0.02, d);
gl_FragColor = mix(bgColor, v_vColour, color);
}

View file

@ -0,0 +1,19 @@
//
// Simple passthrough vertex shader
//
attribute vec3 in_Position; // (x,y,z)
//attribute vec3 in_Normal; // (x,y,z) unused in this shader.
attribute vec4 in_Colour; // (r,g,b,a)
attribute vec2 in_TextureCoord; // (u,v)
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
void main()
{
vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;
v_vColour = in_Colour;
v_vTexcoord = in_TextureCoord;
}

View file

@ -0,0 +1,10 @@
{
"resourceType": "GMShader",
"resourceVersion": "1.0",
"name": "sh_shape_normalize",
"parent": {
"name": "generator",
"path": "folders/shader/generator.yy",
},
"type": 1,
}