diff --git a/datafiles/data/Collections.zip b/datafiles/data/Collections.zip index 9c1262b76..b477cc055 100644 Binary files a/datafiles/data/Collections.zip and b/datafiles/data/Collections.zip differ diff --git a/objects/o_main/Step_0.gml b/objects/o_main/Step_0.gml index bf21c196f..b3a9bb117 100644 --- a/objects/o_main/Step_0.gml +++ b/objects/o_main/Step_0.gml @@ -9,7 +9,11 @@ if(PROJECT.active && !PROJECT.safeMode) { #region try { if(PANEL_MAIN != 0) PANEL_MAIN.step(); - array_foreach(PROJECT.nodeArray, function(_node) { if(!_node.active) return; _node.triggerCheck(); _node.step(); }); + array_foreach(PROJECT.nodeArray, function(_node) { + if(!_node.active) return; + _node.triggerCheck(); + _node.step(); + }); } catch(e) { noti_warning("Step error: " + exception_print(e)); } diff --git a/scripts/node_data/node_data.gml b/scripts/node_data/node_data.gml index a4b297971..1abf4b4e5 100644 --- a/scripts/node_data/node_data.gml +++ b/scripts/node_data/node_data.gml @@ -196,7 +196,9 @@ function Node(_x, _y, _group = PANEL_GRAPH.getCurrentContext()) : __Node_Base(_x updated = false; passiveDynamic = false; topoSorted = false; - temp_surface = []; + temp_surface = []; + + is_group_io = false; #endregion #region ---- timeline ---- diff --git a/scripts/node_feedback_input/node_feedback_input.gml b/scripts/node_feedback_input/node_feedback_input.gml index 11e2742d6..3af27f837 100644 --- a/scripts/node_feedback_input/node_feedback_input.gml +++ b/scripts/node_feedback_input/node_feedback_input.gml @@ -1,6 +1,7 @@ function Node_Feedback_Input(_x, _y, _group = noone) : Node_Group_Input(_x, _y, _group) constructor { name = "Feedback Input"; color = COLORS.node_blend_feedback; + is_group_io = true; w = 96; h = 32 + 24 * 2; diff --git a/scripts/node_feedback_output/node_feedback_output.gml b/scripts/node_feedback_output/node_feedback_output.gml index bd1b77e55..3ec90b759 100644 --- a/scripts/node_feedback_output/node_feedback_output.gml +++ b/scripts/node_feedback_output/node_feedback_output.gml @@ -1,6 +1,7 @@ function Node_Feedback_Output(_x, _y, _group = noone) : Node_Group_Output(_x, _y, _group) constructor { name = "Feedback Output"; color = COLORS.node_blend_feedback; + is_group_io = true; w = 96; h = 32 + 24 * 2; diff --git a/scripts/node_group_input/node_group_input.gml b/scripts/node_group_input/node_group_input.gml index e6a477e18..6031dbb41 100644 --- a/scripts/node_group_input/node_group_input.gml +++ b/scripts/node_group_input/node_group_input.gml @@ -1,8 +1,9 @@ function Node_Group_Input(_x, _y, _group = noone) : Node(_x, _y, _group) constructor { name = "Group Input"; - destroy_when_upgroup = true; color = COLORS.node_blend_collection; previewable = false; + is_group_io = true; + destroy_when_upgroup = true; inParent = undefined; diff --git a/scripts/node_group_output/node_group_output.gml b/scripts/node_group_output/node_group_output.gml index 1f4c28503..72301ce60 100644 --- a/scripts/node_group_output/node_group_output.gml +++ b/scripts/node_group_output/node_group_output.gml @@ -2,6 +2,7 @@ function Node_Group_Output(_x, _y, _group = noone) : Node(_x, _y, _group) constr name = "Group Output"; color = COLORS.node_blend_collection; previewable = false; + is_group_io = true; destroy_when_upgroup = true; diff --git a/scripts/node_iterator_each_input/node_iterator_each_input.gml b/scripts/node_iterator_each_input/node_iterator_each_input.gml index 88299e1d9..888433a9a 100644 --- a/scripts/node_iterator_each_input/node_iterator_each_input.gml +++ b/scripts/node_iterator_each_input/node_iterator_each_input.gml @@ -1,6 +1,7 @@ function Node_Iterator_Each_Input(_x, _y, _group = noone) : Node(_x, _y, _group) constructor { name = "Loop Input"; color = COLORS.node_blend_loop; + is_group_io = true; manual_deletable = false; diff --git a/scripts/node_iterator_each_output/node_iterator_each_output.gml b/scripts/node_iterator_each_output/node_iterator_each_output.gml index 2bdb88885..cf2216f78 100644 --- a/scripts/node_iterator_each_output/node_iterator_each_output.gml +++ b/scripts/node_iterator_each_output/node_iterator_each_output.gml @@ -1,6 +1,7 @@ function Node_Iterator_Each_Output(_x, _y, _group = noone) : Node(_x, _y, _group) constructor { name = "Loop Output"; color = COLORS.node_blend_loop; + is_group_io = true; manual_deletable = false; diff --git a/scripts/node_iterator_filter_input/node_iterator_filter_input.gml b/scripts/node_iterator_filter_input/node_iterator_filter_input.gml index bf3575542..2e17c6147 100644 --- a/scripts/node_iterator_filter_input/node_iterator_filter_input.gml +++ b/scripts/node_iterator_filter_input/node_iterator_filter_input.gml @@ -1,6 +1,7 @@ function Node_Iterator_Filter_Input(_x, _y, _group = noone) : Node(_x, _y, _group) constructor { name = "Value"; color = COLORS.node_blend_loop; + is_group_io = true; manual_deletable = false; diff --git a/scripts/node_iterator_filter_output/node_iterator_filter_output.gml b/scripts/node_iterator_filter_output/node_iterator_filter_output.gml index 00315889c..76abddaeb 100644 --- a/scripts/node_iterator_filter_output/node_iterator_filter_output.gml +++ b/scripts/node_iterator_filter_output/node_iterator_filter_output.gml @@ -1,6 +1,7 @@ function Node_Iterator_Filter_Output(_x, _y, _group = noone) : Node(_x, _y, _group) constructor { name = "Filter Output"; color = COLORS.node_blend_loop; + is_group_io = true; manual_deletable = false; diff --git a/scripts/node_iterator_input/node_iterator_input.gml b/scripts/node_iterator_input/node_iterator_input.gml index 1d5e24034..0471d249b 100644 --- a/scripts/node_iterator_input/node_iterator_input.gml +++ b/scripts/node_iterator_input/node_iterator_input.gml @@ -1,6 +1,7 @@ function Node_Iterator_Input(_x, _y, _group = noone) : Node_Group_Input(_x, _y, _group) constructor { name = "Loop Input"; color = COLORS.node_blend_loop; + is_group_io = true; local_output = noone; diff --git a/scripts/node_iterator_output/node_iterator_output.gml b/scripts/node_iterator_output/node_iterator_output.gml index 8effadfbd..cc0add59c 100644 --- a/scripts/node_iterator_output/node_iterator_output.gml +++ b/scripts/node_iterator_output/node_iterator_output.gml @@ -1,6 +1,7 @@ function Node_Iterator_Output(_x, _y, _group = noone) : Node_Group_Output(_x, _y, _group) constructor { name = "Loop Output"; color = COLORS.node_blend_loop; + is_group_io = true; w = 96; h = 32 + 24 * 2; diff --git a/scripts/node_keyframe/node_keyframe.gml b/scripts/node_keyframe/node_keyframe.gml index 0b91c779b..de49e24ac 100644 --- a/scripts/node_keyframe/node_keyframe.gml +++ b/scripts/node_keyframe/node_keyframe.gml @@ -72,14 +72,19 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor { values = ds_list_create(); sep_axis = _sep_axis; - index = 0; - prop = _prop; - y = 0; + index = 0; + prop = _prop; + y = 0; + key_map = array_create(TOTAL_FRAMES); animate_frames = []; if(_prop.type != VALUE_TYPE.trigger) ds_list_add(values, new valueKey(0, _val, self)); + + process_cache = {}; + process_cache_type = -1; + process_cache_disp = -1; #endregion static refreshAnimation = function() { #region @@ -106,6 +111,32 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor { if(_fr) array_fill(animate_frames, _fr.time, TOTAL_FRAMES, 0); } #endregion + static updateKeyMap = function() { #region + if(!prop.is_anim && !LOADING && !APPENDING) return; + + if(array_length(key_map) != TOTAL_FRAMES) + array_resize(key_map, TOTAL_FRAMES); + + if(ds_list_size(values) < 2) { + array_fill(key_map, 0, TOTAL_FRAMES, 0); + return; + } + + //print($"Update key map {prop.node.name} - {prop.name}"); + + var _firstKey = values[| 0].time; + array_fill(key_map, 0, _firstKey, -1); + var _keyIndex = _firstKey; + + for( var i = 1, n = ds_list_size(values); i < n; i++ ) { + var _k1 = values[| i].time; + array_fill(key_map, _keyIndex, _k1, i - 1); + _keyIndex = _k1; + } + + array_fill(key_map, _keyIndex, TOTAL_FRAMES, 999_999); + } #endregion + static interpolate = function(from, to, rat) { #region if(prop.type == VALUE_TYPE.boolean) return 0; @@ -207,12 +238,15 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor { if(!prop.is_anim) return values[| 0].value; - for(var i = 0; i < ds_list_size(values); i++) { //Find trigger - var _key = values[| i]; - if(_key.time == _time) - return _key.value; - } - return false; + if(array_length(key_map) != TOTAL_FRAMES) updateKeyMap(); + + var _keyIndex = key_map[_time]; + + if(_keyIndex == -1 || _keyIndex == 999_999) + return false; + + var _key = values[| _keyIndex]; + return _key.time == _time? _key.value : false; } if(ds_list_size(values) == 0) return processTypeDefault(); @@ -220,12 +254,13 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor { if(prop.type == VALUE_TYPE.path) return processType(values[| 0].value); if(!prop.is_anim) return processType(values[| 0].value); + if(array_length(key_map) != TOTAL_FRAMES) updateKeyMap(); var _time_first = prop.loop_range == -1? values[| 0].time : values[| ds_list_size(values) - 1 - prop.loop_range].time; var _time_last = values[| ds_list_size(values) - 1].time; var _time_dura = _time_last - _time_first; - if(_time > _time_last) { //loop + if(_time > _time_last) { #region //loop switch(prop.on_end) { case KEYFRAME_END.loop : _time = _time_first + safe_mod(_time - _time_last, _time_dura + 1); @@ -238,9 +273,12 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor { _time = _time_first + _time_dura * 2 - time_in_loop; break; } - } + } #endregion - if(_time < values[| 0].time) { //Wrap begin + var _keyIndex = key_map[_time]; + //print(_keyIndex); + + if(_keyIndex == -1) { #region Before first key if(prop.on_end == KEYFRAME_END.wrap) { var from = values[| ds_list_size(values) - 1]; var to = values[| 0]; @@ -258,33 +296,32 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor { } return processType(values[| 0].value); //First frame - } + } #endregion + + if(_keyIndex == 999_999) { #region After last key + if(_keyIndex == KEYFRAME_END.wrap) { + var from = values[| ds_list_size(values) - 1]; + var to = values[| 0]; + var prog = _time - from.time; + var totl = TOTAL_FRAMES - from.time + to.time; + + var rat = prog / totl; + var _lrp = interpolate(from, to, rat); + + return lerpValue(from, to, _lrp); + } - for(var i = 0; i < ds_list_size(values); i++) { //In between - var _key = values[| i]; - if(_key.time <= _time) continue; - - var rat = (_time - values[| i - 1].time) / (values[| i].time - values[| i - 1].time); - var from = values[| i - 1]; - var to = values[| i]; + return processType(values[| ds_list_size(values) - 1].value); //First frame + } #endregion + + #region In between + var from = values[| _keyIndex]; + var to = values[| _keyIndex + 1]; + var rat = (_time - from.time) / (to.time - from.time); var _lrp = interpolate(from, to, rat); return lerpValue(from, to, _lrp); - } - - if(prop.on_end == KEYFRAME_END.wrap) { //Wrap end - var from = values[| ds_list_size(values) - 1]; - var to = values[| 0]; - var prog = _time - from.time; - var totl = TOTAL_FRAMES - from.time + to.time; - - var rat = prog / totl; - var _lrp = interpolate(from, to, rat); - - return lerpValue(from, to, _lrp); - } - - return processType(values[| ds_list_size(values) - 1].value); //Last frame + #endregion } #endregion static processTypeDefault = function() { #region @@ -292,13 +329,27 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor { return 0; } #endregion + static clearProcessCache = function(_val) { process_cache = {}; } + static processType = function(_val) { #region + if(process_cache_type != prop.type || process_cache_disp != prop.display_type) { + clearProcessCache(); + process_cache_type = prop.type; + process_cache_disp = prop.display_type; + } + + if(struct_has(process_cache, _val)) + return process_cache[$ _val]; + + var _res = _val; if(!sep_axis && typeArray(prop.display_type) && is_array(_val)) { for(var i = 0; i < array_length(_val); i++) - _val[i] = processValue(_val[i]); - return _val; - } - return processValue(_val); + _res[i] = processValue(_val[i]); + } else + _res = processValue(_val); + + process_cache[$ _val] = _res; + return _res; } #endregion static processValue = function(_val) { #region @@ -348,6 +399,7 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor { } values[| i] = _key; + updateKeyMap(); return 2; } @@ -361,6 +413,7 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor { }, { key : _key, time : _prevTime }); ds_list_insert(values, i, _key); + if(_replace) updateKeyMap(); return 1; } @@ -371,6 +424,7 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor { }, { key : _key, time : _prevTime }); ds_list_add(values, _key); + if(_replace) updateKeyMap(); return 1; } #endregion @@ -378,6 +432,7 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor { if(prop.type == VALUE_TYPE.trigger) { if(!prop.is_anim) { values[| 0] = new valueKey(0, _val, self); + updateKeyMap(); return true; } @@ -390,11 +445,13 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor { return false; } else if(_key.time > _time) { ds_list_insert(values, i, new valueKey(_time, _val, self)); + updateKeyMap(); return true; } } ds_list_add(values, new valueKey(_time, _val, self)); + updateKeyMap(); return true; } @@ -430,6 +487,7 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor { var k = new valueKey(_time, _val, self, ease_in, ease_out); ds_list_insert(values, i, k); if(_record) recordAction(ACTION_TYPE.list_insert, values, [k, i, $"add {prop.name} keyframe" ]); + updateKeyMap(); return true; } } @@ -437,6 +495,7 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor { var k = new valueKey(_time, _val, self, ease_in, ease_out); if(_record) recordAction(ACTION_TYPE.list_insert, values, [ k, ds_list_size(values), $"add {prop.name} keyframe" ]); ds_list_add(values, k); + updateKeyMap(); return true; } #endregion @@ -445,6 +504,7 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor { ds_list_remove(values, key); else prop.is_anim = false; + updateKeyMap(); } #endregion static serialize = function(scale = false) { #region @@ -506,6 +566,8 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor { var grad = new gradientObject(); grad.keys = _val; ds_list_add(values, new valueKey(0, grad, self)); + + updateKeyMap(); return; } @@ -554,6 +616,8 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor { vk.ease_y_lock = ease_y_lock; ds_list_add(values, vk); } + + updateKeyMap(); } #endregion static cleanUp = function() { #region diff --git a/scripts/node_tunnel_in/node_tunnel_in.gml b/scripts/node_tunnel_in/node_tunnel_in.gml index ee1731cb9..eac0f80bf 100644 --- a/scripts/node_tunnel_in/node_tunnel_in.gml +++ b/scripts/node_tunnel_in/node_tunnel_in.gml @@ -2,6 +2,7 @@ function Node_Tunnel_In(_x, _y, _group = noone) : Node(_x, _y, _group) construct name = "Tunnel In"; previewable = false; color = COLORS.node_blend_tunnel; + is_group_io = true; w = 96; diff --git a/scripts/node_tunnel_out/node_tunnel_out.gml b/scripts/node_tunnel_out/node_tunnel_out.gml index 55ce38703..d06c8a94d 100644 --- a/scripts/node_tunnel_out/node_tunnel_out.gml +++ b/scripts/node_tunnel_out/node_tunnel_out.gml @@ -1,7 +1,8 @@ function Node_Tunnel_Out(_x, _y, _group = noone) : Node(_x, _y, _group) constructor { name = "Tunnel Out"; - previewable = false; color = COLORS.node_blend_tunnel; + previewable = false; + is_group_io = true; w = 96; diff --git a/scripts/node_value/node_value.gml b/scripts/node_value/node_value.gml index c514a73c6..80c677e32 100644 --- a/scripts/node_value/node_value.gml +++ b/scripts/node_value/node_value.gml @@ -1638,21 +1638,25 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru if(ds_list_empty(animator.values)) ds_list_add(animator.values, new valueKey(CURRENT_FRAME, animator.getValue(), animator)); animator.values[| 0].time = CURRENT_FRAME; + animator.updateKeyMap(); for( var i = 0, n = array_length(animators); i < n; i++ ) { if(ds_list_empty(animators[i].values)) ds_list_add(animators[i].values, new valueKey(CURRENT_FRAME, animators[i].getValue(), animators[i])); animators[i].values[| 0].time = CURRENT_FRAME; + animators[i].updateKeyMap(); } } else { var _val = animator.getValue(); ds_list_clear(animator.values); animator.values[| 0] = new valueKey(0, _val, animator); + animator.updateKeyMap(); for( var i = 0, n = array_length(animators); i < n; i++ ) { var _val = animators[i].getValue(); ds_list_clear(animators[i].values); animators[i].values[| 0] = new valueKey(0, _val, animators[i]); + animators[i].updateKeyMap(); } } diff --git a/scripts/node_wiggler/node_wiggler.gml b/scripts/node_wiggler/node_wiggler.gml index 9a25ba056..1aa093f28 100644 --- a/scripts/node_wiggler/node_wiggler.gml +++ b/scripts/node_wiggler/node_wiggler.gml @@ -32,7 +32,7 @@ function Node_Wiggler(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) c var step = TOTAL_FRAMES / 64; for( var i = 0; i < 64; i++ ) - random_value[i] = getWiggle(ran[0], ran[1], TOTAL_FRAMES / fre, step * i, sed, 0, TOTAL_FRAMES); + random_value[i] = getWiggle(array_safe_get(ran, 0), array_safe_get(ran, 1), TOTAL_FRAMES / fre, step * i, sed, 0, TOTAL_FRAMES); } static processData = function(_output, _data, _output_index, _array_index = 0) { @@ -41,7 +41,7 @@ function Node_Wiggler(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) c var sed = _data[2]; var time = CURRENT_FRAME; - return getWiggle(ran[0], ran[1], TOTAL_FRAMES / fre, time, sed, 0, TOTAL_FRAMES); + return getWiggle(array_safe_get(ran, 0), array_safe_get(ran, 1), TOTAL_FRAMES / fre, time, sed, 0, TOTAL_FRAMES); } static onDrawNode = function(xx, yy, _mx, _my, _s, _hover, _focus) { @@ -52,6 +52,8 @@ function Node_Wiggler(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) c var time = CURRENT_FRAME; var total_time = TOTAL_FRAMES; + if(!is_array(ran)) return; + switch(disp) { case 0 : w = 96; diff --git a/scripts/render_data/render_data.gml b/scripts/render_data/render_data.gml index 9a279c26b..4e9a0df7a 100644 --- a/scripts/render_data/render_data.gml +++ b/scripts/render_data/render_data.gml @@ -10,15 +10,6 @@ enum RENDER_TYPE { global.FLAG.render = 0; global.FLAG.renderTime = false; - global.group_io = [ - "Node_Group_Input", "Node_Group_Output", - "Node_Feedback_Input", "Node_Feedback_Output", - "Node_Iterator_Input", "Node_Iterator_Output", - "Node_Iterator_Each_Input", "Node_Iterator_Each_Output", - "Node_Iterator_Filter_Input", "Node_Iterator_Filter_Output", - "Node_Tunnel_In", "Node_Tunnel_Out" - ]; - #macro RENDER_ALL_REORDER UPDATE_RENDER_ORDER = true; UPDATE |= RENDER_TYPE.full; #macro RENDER_ALL UPDATE |= RENDER_TYPE.full; #macro RENDER_PARTIAL UPDATE |= RENDER_TYPE.partial; @@ -133,7 +124,7 @@ function __nodeIsRenderLeaf(_node) { #region if(is_undefined(_node)) { LOG_IF(global.FLAG.render == 1, $"Skip undefiend [{_node}]"); return false; } if(!is_instanceof(_node, Node)) { LOG_IF(global.FLAG.render == 1, $"Skip non-node [{_node}]"); return false; } - if(array_exists(global.group_io, instanceof(_node))) { LOG_IF(global.FLAG.render == 1, $"Skip group IO [{_node.internalName}]"); return false; } + if(_node.is_group_io) { LOG_IF(global.FLAG.render == 1, $"Skip group IO [{_node.internalName}]"); return false; } if(!_node.active) { LOG_IF(global.FLAG.render == 1, $"Skip inactive [{_node.internalName}]"); return false; } if(!_node.isRenderActive()) { LOG_IF(global.FLAG.render == 1, $"Skip render inactive [{_node.internalName}]"); return false; } @@ -282,10 +273,7 @@ function RenderList(list, skipInLoop = true) { #region if(!is_instanceof(_node, Node)) { LOG_IF(global.FLAG.render == 1, $"Skip non-node {_node}"); continue; } _node.render_time = 0; - if(array_exists(global.group_io, instanceof(_node))) { - LOG_IF(global.FLAG.render == 1, $"Skip group IO {_node.internalName}"); - continue; - } + if(_node.is_group_io) { LOG_IF(global.FLAG.render == 1, $"Skip group IO {_node.internalName}"); continue; } if(!_node.active) { LOG_IF(global.FLAG.render == 1, $"Skip inactive {_node.internalName}"); continue; } if(!_node.isRenderActive()) { LOG_IF(global.FLAG.render == 1, $"Skip non-renderActive {_node.internalName}"); continue; }