mirror of
https://github.com/Ttanasart-pt/Pixel-Composer.git
synced 2025-01-24 20:08:04 +01:00
Partial rendering for animation.
This commit is contained in:
parent
387105fb53
commit
abffaecb0d
23 changed files with 96 additions and 68 deletions
|
@ -40,7 +40,9 @@
|
|||
TOOLTIP = "";
|
||||
DRAGGING = noone;
|
||||
KEYBOARD_STRING = "";
|
||||
|
||||
RENDER_QUEUE = new Queue();
|
||||
RENDER_ORDER = [];
|
||||
|
||||
globalvar AUTO_SAVE_TIMER;
|
||||
AUTO_SAVE_TIMER = 0;
|
||||
|
@ -78,7 +80,7 @@
|
|||
});
|
||||
|
||||
addHotkey("", "Render all", vk_f5, MOD_KEY.none, function() {
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
RENDER_ALL_REORDER
|
||||
});
|
||||
|
||||
addHotkey("", "Close file", "Q", MOD_KEY.ctrl, function() { PANEL_GRAPH.close(); });
|
||||
|
|
|
@ -80,6 +80,9 @@ _HOVERING_ELEMENT = noone;
|
|||
//physics_pause_enable(true);
|
||||
DEF_SURFACE_RESET();
|
||||
|
||||
if(UPDATE_RENDER_ORDER) ResetAllNodesRender();
|
||||
UPDATE_RENDER_ORDER = false;
|
||||
|
||||
if(PROJECT.active) {
|
||||
var _k = ds_map_find_first(PROJECT.nodeMap);
|
||||
var _a = ds_map_size(PROJECT.nodeMap);
|
||||
|
@ -91,7 +94,11 @@ _HOVERING_ELEMENT = noone;
|
|||
if(PROJECT.animator.is_playing || PROJECT.animator.rendering) {
|
||||
if(PROJECT.animator.frame_progress) {
|
||||
__addon_preAnim();
|
||||
Render();
|
||||
|
||||
if(PROJECT.animator.current_frame == 0)
|
||||
ResetAllNodesRender();
|
||||
Render(true);
|
||||
|
||||
__addon_postAnim();
|
||||
}
|
||||
PROJECT.animator.frame_progress = false;
|
||||
|
|
|
@ -403,7 +403,7 @@ function __Node_3D_Extrude(_x, _y, _group = noone) : Node_Processor(_x, _y, _gro
|
|||
mesh_generating = false;
|
||||
mesh_genetated = true;
|
||||
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
RENDER_ALL
|
||||
outputs[| 3].setValue(vertexObjects);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
frame_progress = true;
|
||||
if(resetTime)
|
||||
time_since_last_frame = 0;
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
RENDER_ALL
|
||||
} else
|
||||
frame_progress = false;
|
||||
}
|
||||
|
|
|
@ -130,7 +130,8 @@ function __APPEND_MAP(_map, context = PANEL_GRAPH.getCurrentContext()) {
|
|||
|
||||
APPENDING = false;
|
||||
PANEL_ANIMATION.updatePropertyList();
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
|
||||
RENDER_ALL_REORDER
|
||||
|
||||
if(struct_has(_map, "metadata")) {
|
||||
var meta = _map.metadata;
|
||||
|
|
|
@ -119,7 +119,6 @@
|
|||
HOTKEY_CONTEXT[| 0] = "";
|
||||
|
||||
globalvar CURSOR, TOOLTIP, DRAGGING, DIALOG_DEPTH_HOVER;
|
||||
globalvar UPDATE, RENDER_QUEUE;
|
||||
#endregion
|
||||
|
||||
#region inputs
|
||||
|
|
|
@ -236,7 +236,7 @@ function __LOAD_PATH(path, readonly = false, safe_mode = false, override = false
|
|||
log_warning("LOAD, connect", exception_print(e));
|
||||
}
|
||||
|
||||
Render();
|
||||
RENDER_ALL_REORDER
|
||||
|
||||
LOADING = false;
|
||||
PROJECT.modified = false;
|
||||
|
|
|
@ -408,13 +408,16 @@ function Node(_x, _y, _group = PANEL_GRAPH.getCurrentContext()) : __Node_Base(_x
|
|||
static focusStep = function() {}
|
||||
static inspectorStep = function() {}
|
||||
|
||||
static getInputData = function(index, def = 0) { return array_safe_get(inputs_data, index, def); }
|
||||
static getInputData = function(index, def = 0) { #region
|
||||
gml_pragma("forceinline");
|
||||
|
||||
return array_safe_get(inputs_data, index, def);
|
||||
} #endregion
|
||||
|
||||
static getInputs = function() { #region
|
||||
inputs_data = array_create(ds_list_size(inputs));
|
||||
|
||||
inputs_data = array_create(ds_list_size(inputs), undefined);
|
||||
for(var i = 0; i < ds_list_size(inputs); i++)
|
||||
inputs_data[i] = inputs[| i].getValue();
|
||||
inputs_data[i] = inputs[| i].getValue(,,, true);
|
||||
} #endregion
|
||||
|
||||
static doUpdate = function() { #region
|
||||
|
@ -425,24 +428,17 @@ function Node(_x, _y, _group = PANEL_GRAPH.getCurrentContext()) : __Node_Base(_x
|
|||
LOG_BLOCK_START();
|
||||
LOG_IF(global.FLAG.render, $">>>>>>>>>> DoUpdate called from {internalName} <<<<<<<<<<");
|
||||
|
||||
var t = get_timer();
|
||||
var _hash = input_hash;
|
||||
getInputs();
|
||||
input_hash = md5_string_unicode(json_stringify(inputs_data));
|
||||
input_hash = md5_string_unicode(string(inputs_data));
|
||||
anim_last_step = isAnimated() || _hash != input_hash;
|
||||
|
||||
if(name == "Shape") print($"{isAnimated()} - {_hash != input_hash}");
|
||||
|
||||
try {
|
||||
var t = get_timer();
|
||||
|
||||
if(!is_instanceof(self, Node_Collection))
|
||||
setRenderStatus(true);
|
||||
|
||||
if(anim_last_step)
|
||||
update(); ///Update only if input hash differs from previous.
|
||||
|
||||
if(!is_instanceof(self, Node_Collection))
|
||||
render_time = get_timer() - t;
|
||||
if(anim_last_step) update(); ///Update only if input hash differs from previous.
|
||||
} catch(exception) {
|
||||
var sCurr = surface_get_target();
|
||||
while(surface_get_target() != sBase)
|
||||
|
@ -451,6 +447,9 @@ function Node(_x, _y, _group = PANEL_GRAPH.getCurrentContext()) : __Node_Base(_x
|
|||
log_warning("RENDER", exception_print(exception), self);
|
||||
}
|
||||
|
||||
if(!is_instanceof(self, Node_Collection))
|
||||
render_time = get_timer() - t;
|
||||
|
||||
if(!use_cache && PROJECT.onion_skin) {
|
||||
for( var i = 0; i < ds_list_size(outputs); i++ ) {
|
||||
if(outputs[| i].type != VALUE_TYPE.surface) continue;
|
||||
|
@ -1078,7 +1077,8 @@ function Node(_x, _y, _group = PANEL_GRAPH.getCurrentContext()) : __Node_Base(_x
|
|||
outputs[| i].destroy();
|
||||
|
||||
onDestroy();
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
|
||||
RENDER_ALL_REORDER
|
||||
} #endregion
|
||||
|
||||
static restore = function() { #region
|
||||
|
@ -1513,8 +1513,7 @@ function Node(_x, _y, _group = PANEL_GRAPH.getCurrentContext()) : __Node_Base(_x
|
|||
|
||||
static attributeDeserialize = function(attr) { #region
|
||||
struct_override(attributes, attr);
|
||||
}
|
||||
#endregion
|
||||
} #endregion
|
||||
static postDeserialize = function() {}
|
||||
static processDeserialize = function() {}
|
||||
|
||||
|
@ -1631,4 +1630,6 @@ function Node(_x, _y, _group = PANEL_GRAPH.getCurrentContext()) : __Node_Base(_x
|
|||
return surface_rgba8unorm;
|
||||
return surface_get_format(_s);
|
||||
} #endregion
|
||||
|
||||
static toString = function() { return $"PixelComposerNode: {node_id}[{internalName}] {input_hash}"; }
|
||||
}
|
|
@ -44,7 +44,7 @@ function Node_DynaSurf(_x, _y, _group = noone) : Node_Collection(_x, _y, _group)
|
|||
_surH.inputs[| 0].setFrom(_input.outputs[| 0]);
|
||||
_outH.inputs[| 0].setFrom(_surH.outputs[| 0]);
|
||||
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
RENDER_ALL
|
||||
} #endregion
|
||||
|
||||
static setRenderStatus = function(result) { #region
|
||||
|
|
|
@ -570,7 +570,7 @@ function Node_Export(_x, _y, _group = noone) : Node(_x, _y, _group) constructor
|
|||
insp2UpdateIcon = [ THEME.play_all, 0, COLORS._main_value_positive ];
|
||||
|
||||
static onInspector1Update = function() { #region
|
||||
if(isInLoop()) UPDATE |= RENDER_TYPE.full;
|
||||
if(isInLoop()) RENDER_ALL
|
||||
else doInspectorAction();
|
||||
} #endregion
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ function Node_Feedback(_x, _y, _group = noone) : Node_Collection(_x, _y, _group)
|
|||
static doStepBegin = function() {
|
||||
if(!PROJECT.animator.frame_progress) return;
|
||||
setRenderStatus(false);
|
||||
UPDATE |= RENDER_TYPE.full; //force full render
|
||||
RENDER_ALL //force full render
|
||||
}
|
||||
|
||||
static getNextNodes = function() {
|
||||
|
|
|
@ -21,7 +21,7 @@ function Node_Fluid_Group(_x, _y, _group = noone) : Node_Collection(_x, _y, _gro
|
|||
RETURN_ON_REST
|
||||
|
||||
setRenderStatus(false);
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
RENDER_ALL
|
||||
}
|
||||
|
||||
PATCH_STATIC
|
||||
|
|
|
@ -20,7 +20,7 @@ function variable_editor(nodeVal) constructor {
|
|||
|
||||
if(string_pos(" ", value.name))
|
||||
noti_warning("Global variable name can't have space.");
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
RENDER_ALL
|
||||
});
|
||||
|
||||
vb_range = new vectorBox(2, function(index, val) {
|
||||
|
@ -39,7 +39,7 @@ function variable_editor(nodeVal) constructor {
|
|||
disp_index = 0;
|
||||
refreshInput();
|
||||
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
RENDER_ALL
|
||||
} );
|
||||
sc_type.update_hover = false;
|
||||
|
||||
|
@ -47,7 +47,7 @@ function variable_editor(nodeVal) constructor {
|
|||
disp_index = val;
|
||||
refreshInput();
|
||||
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
RENDER_ALL
|
||||
} );
|
||||
sc_disp.update_hover = false;
|
||||
|
||||
|
@ -190,7 +190,7 @@ function Node_Global(_x = 0, _y = 0) : __Node_Base(_x, _y) constructor {
|
|||
anim_priority = -999;
|
||||
|
||||
static valueUpdate = function(index) {
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
RENDER_ALL
|
||||
}
|
||||
|
||||
static createValue = function() {
|
||||
|
|
|
@ -290,7 +290,7 @@ function Node_Path(_x, _y, _group = noone) : Node(_x, _y, _group) constructor {
|
|||
|
||||
if(mouse_release(mb_left)) {
|
||||
transform_type = 0;
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
RENDER_ALL
|
||||
UNDO_HOLDING = false;
|
||||
}
|
||||
} else if(drag_point > -1) {
|
||||
|
@ -550,7 +550,7 @@ function Node_Path(_x, _y, _group = noone) : Node(_x, _y, _group) constructor {
|
|||
|
||||
if(mouse_release(mb_left)) {
|
||||
drag_point = -1;
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
RENDER_ALL
|
||||
UNDO_HOLDING = false;
|
||||
}
|
||||
}
|
||||
|
@ -812,7 +812,7 @@ function Node_Path(_x, _y, _group = noone) : Node(_x, _y, _group) constructor {
|
|||
drag_point_sx = (_mx - _x) / _s;
|
||||
drag_point_sy = (_my - _y) / _s;
|
||||
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
RENDER_ALL
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ function Node_Pixel_Builder(_x, _y, _group = noone) : Node_Collection(_x, _y, _g
|
|||
|
||||
if(!LOADING && !APPENDING && !CLONING) {
|
||||
var input = nodeBuild("Node_PB_Layer", -256, -32, self);
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
RENDER_ALL
|
||||
}
|
||||
|
||||
static getNextNodes = function() {
|
||||
|
|
|
@ -21,7 +21,7 @@ function Node_Strand_Group(_x, _y, _group = noone) : Node_Collection(_x, _y, _gr
|
|||
RETURN_ON_REST
|
||||
|
||||
setRenderStatus(false);
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
RENDER_ALL
|
||||
}
|
||||
|
||||
PATCH_STATIC
|
||||
|
|
|
@ -102,7 +102,7 @@ function Node_Tunnel_In(_x, _y, _group = noone) : Node(_x, _y, _group) construct
|
|||
k = ds_map_find_next(TUNNELS_IN_MAP, k);
|
||||
}
|
||||
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
RENDER_ALL
|
||||
}
|
||||
|
||||
static step = function() {
|
||||
|
|
|
@ -60,7 +60,7 @@ function Node_Tunnel_Out(_x, _y, _group = noone) : Node(_x, _y, _group) construc
|
|||
|
||||
TUNNELS_OUT[? node_id] = _key;
|
||||
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
RENDER_ALL
|
||||
}
|
||||
|
||||
static step = function() {
|
||||
|
|
|
@ -1283,7 +1283,7 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
|
|||
global.cache_call++;
|
||||
if(useCache && use_cache) {
|
||||
var cache_hit = cache_value[0];
|
||||
cache_hit &= (!is_anim && value_from == noone) || cache_value[1] == _time;
|
||||
cache_hit &= !isAnimated() || cache_value[1] == _time;
|
||||
cache_hit &= cache_value[2] != undefined;
|
||||
cache_hit &= cache_value[3] == applyUnit;
|
||||
cache_hit &= connect_type == JUNCTION_CONNECT.input;
|
||||
|
@ -1592,8 +1592,8 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
|
|||
if(_update) node.valueUpdate(self.index);
|
||||
node.clearCacheForward();
|
||||
|
||||
if(fullUpdate) UPDATE |= RENDER_TYPE.full;
|
||||
else UPDATE |= RENDER_TYPE.partial;
|
||||
if(fullUpdate) RENDER_ALL
|
||||
else RENDER_PARTIAL
|
||||
|
||||
if(!LOADING) PROJECT.modified = true;
|
||||
}
|
||||
|
@ -1702,6 +1702,7 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
|
|||
PROJECT.modified = true;
|
||||
}
|
||||
|
||||
UPDATE_RENDER_ORDER = true;
|
||||
return true;
|
||||
} #endregion
|
||||
|
||||
|
@ -1716,6 +1717,8 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
|
|||
node.clearCacheForward();
|
||||
|
||||
PROJECT.modified = true;
|
||||
|
||||
UPDATE_RENDER_ORDER = true;
|
||||
return false;
|
||||
} #endregion
|
||||
|
||||
|
|
|
@ -208,7 +208,7 @@ function Node_WAV_File_Read(_x, _y, _group = noone) : Node(_x, _y, _group) const
|
|||
readSoundComplete();
|
||||
checkPreview(true);
|
||||
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
RENDER_ALL
|
||||
}
|
||||
|
||||
insp2UpdateIcon[1] = attributes.play;
|
||||
|
|
|
@ -141,9 +141,7 @@ function Panel_Menu() : PanelContent() constructor {
|
|||
]],
|
||||
[ __txt("Rendering"), [
|
||||
menuItem(__txtx("panel_menu_render_all_nodes", "Render all nodes"), function() {
|
||||
for(var i = 0; i < ds_list_size(PROJECT.nodes); i++)
|
||||
PROJECT.nodes[| i].triggerRender();
|
||||
UPDATE |= RENDER_TYPE.full;
|
||||
RENDER_ALL_REORDER
|
||||
}, [ THEME.sequence_control, 1 ], ["", "Render all"]),
|
||||
menuItem(__txtx("panel_menu_execute_exports", "Execute all export nodes"), function() {
|
||||
var key = ds_map_find_first(PROJECT.nodeMap);
|
||||
|
|
|
@ -5,11 +5,18 @@ enum RENDER_TYPE {
|
|||
}
|
||||
|
||||
#region globalvar
|
||||
global.FLAG.render = false;
|
||||
globalvar UPDATE, RENDER_QUEUE, RENDER_ORDER, UPDATE_RENDER_ORDER;
|
||||
UPDATE_RENDER_ORDER = false;
|
||||
|
||||
global.FLAG.render = true;
|
||||
global.group_inputs = [ "Node_Group_Input", "Node_Feedback_Input", "Node_Iterator_Input", "Node_Iterator_Each_Input" ];
|
||||
|
||||
#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;
|
||||
#endregion
|
||||
|
||||
function __nodeLeafList(_list) {
|
||||
function __nodeLeafList(_list) { #region
|
||||
var nodes = [];
|
||||
var nodeNames = [];
|
||||
|
||||
|
@ -27,9 +34,9 @@ function __nodeLeafList(_list) {
|
|||
|
||||
LOG_LINE_IF(global.FLAG.render, $"Push node {nodeNames} to stack");
|
||||
return nodes;
|
||||
}
|
||||
} #endregion
|
||||
|
||||
function __nodeIsLoop(_node) {
|
||||
function __nodeIsLoop(_node) { #region
|
||||
switch(instanceof(_node)) {
|
||||
case "Node_Iterate" :
|
||||
case "Node_Iterate_Each" :
|
||||
|
@ -38,19 +45,30 @@ function __nodeIsLoop(_node) {
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} #endregion
|
||||
|
||||
function __nodeInLoop(_node) {
|
||||
function __nodeInLoop(_node) { #region
|
||||
var gr = _node.group;
|
||||
while(gr != noone) {
|
||||
if(__nodeIsLoop(gr)) return true;
|
||||
gr = gr.group;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} #endregion
|
||||
|
||||
function Render(partial = false, runAction = false) {
|
||||
var t = current_time;
|
||||
function ResetAllNodesRender() { #region
|
||||
var _key = ds_map_find_first(PROJECT.nodeMap);
|
||||
var amo = ds_map_size(PROJECT.nodeMap);
|
||||
|
||||
repeat(amo) {
|
||||
var _node = PROJECT.nodeMap[? _key];
|
||||
_node.setRenderStatus(false);
|
||||
_key = ds_map_find_next(PROJECT.nodeMap, _key);
|
||||
}
|
||||
} #endregion
|
||||
|
||||
function Render(partial = false, runAction = false) { #region
|
||||
var t = get_timer();
|
||||
LOG_BLOCK_START();
|
||||
LOG_IF(global.FLAG.render, $"============================== RENDER START [frame {PROJECT.animator.current_frame}] ==============================");
|
||||
|
||||
|
@ -69,7 +87,7 @@ function Render(partial = false, runAction = false) {
|
|||
_key = ds_map_find_next(PROJECT.nodeMap, _key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// get leaf node
|
||||
RENDER_QUEUE.clear();
|
||||
var key = ds_map_find_first(PROJECT.nodeMap);
|
||||
|
@ -122,8 +140,9 @@ function Render(partial = false, runAction = false) {
|
|||
rendering.doUpdate();
|
||||
|
||||
var nextNodes = rendering.getNextNodes();
|
||||
for( var i = 0, n = array_length(nextNodes); i < n; i++ )
|
||||
for( var i = 0, n = array_length(nextNodes); i < n; i++ ) {
|
||||
RENDER_QUEUE.enqueue(nextNodes[i]);
|
||||
}
|
||||
|
||||
if(runAction && rendering.hasInspector1Update())
|
||||
rendering.inspector1Update();
|
||||
|
@ -135,20 +154,20 @@ function Render(partial = false, runAction = false) {
|
|||
noti_warning(exception_print(e));
|
||||
}
|
||||
|
||||
LOG_IF(global.FLAG.render, "=== RENDER COMPLETE IN {" + string(current_time - t) + "ms} ===\n");
|
||||
LOG_IF(global.FLAG.render, $"=== RENDER COMPLETE IN {(get_timer() - t) / 1000} ms ===\n");
|
||||
LOG_END();
|
||||
}
|
||||
} #endregion
|
||||
|
||||
function __renderListReset(list) {
|
||||
function __renderListReset(list) { #region
|
||||
for( var i = 0; i < ds_list_size(list); i++ ) {
|
||||
list[| i].setRenderStatus(false);
|
||||
|
||||
if(struct_has(list[| i], "nodes"))
|
||||
__renderListReset(list[| i].nodes);
|
||||
}
|
||||
}
|
||||
} #endregion
|
||||
|
||||
function RenderList(list) {
|
||||
function RenderList(list) { #region
|
||||
LOG_BLOCK_START();
|
||||
LOG_IF(global.FLAG.render, "=============== RENDER LIST START ===============");
|
||||
var queue = ds_queue_create();
|
||||
|
@ -212,9 +231,9 @@ function RenderList(list) {
|
|||
LOG_END();
|
||||
|
||||
ds_queue_destroy(queue);
|
||||
}
|
||||
} #endregion
|
||||
|
||||
function RenderListAction(list, context = PANEL_GRAPH.getCurrentContext()) {
|
||||
function RenderListAction(list, context = PANEL_GRAPH.getCurrentContext()) { #region
|
||||
printIf(global.FLAG.render, "=== RENDER LIST ACTION START [frame " + string(PROJECT.animator.current_frame) + "] ===");
|
||||
|
||||
try {
|
||||
|
@ -272,4 +291,4 @@ function RenderListAction(list, context = PANEL_GRAPH.getCurrentContext()) {
|
|||
} catch(e) {
|
||||
noti_warning(exception_print(e));
|
||||
}
|
||||
}
|
||||
} #endregion
|
|
@ -1,14 +1,12 @@
|
|||
#macro struct_has variable_struct_exists
|
||||
|
||||
function struct_override(original, override, excludes = []) {
|
||||
function struct_override(original, override) {
|
||||
var args = variable_struct_get_names(override);
|
||||
|
||||
for( var i = 0, n = array_length(args); i < n; i++ ) {
|
||||
var _key = args[i];
|
||||
|
||||
if(!struct_has(original, _key)) continue;
|
||||
if(is_callable(original[$ _key])) continue;
|
||||
if(array_exists(excludes, _key)) continue;
|
||||
|
||||
original[$ _key] = override[$ _key];
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue