package iron.object; #if lnx_morph_target import kha.arrays.Float32Array; import kha.Image; import kha.FastFloat; import iron.data.Data; import iron.data.SceneFormat; class MorphTarget { public var data: TMorphTarget; public var numMorphTargets: Int = 0; public var morphImageSize: Int = 0; public var morphBlockSize: Int = 0; public var scaling: FastFloat; public var offset: FastFloat; public var morphWeights: Float32Array; public var morphDataPos: Image; public var morphDataNor: Image; public var morphMap: Map = null; public var isDirty: Bool = true; var previousWeights: Float32Array; var changeThreshold: FastFloat = 0.001; // skip smaller var pendingUpdates: Map = null; var batchUpdateEnabled: Bool = true; var lastFlushFrame: Int = 0; public function new(data: TMorphTarget) { initWeights(data.morph_target_defaults); scaling = data.morph_scale; offset = data.morph_offset; numMorphTargets = data.num_morph_targets; morphImageSize = data.morph_img_size; morphBlockSize = data.morph_block_size; Data.getImage(data.morph_target_data_file + "_morph_pos.png", function(img: Image) { if (img != null) morphDataPos = img; }); Data.getImage(data.morph_target_data_file + "_morph_nor.png", function(img: Image) { if (img != null) morphDataNor = img; }); morphMap = new Map(); var i = 0; for (name in data.morph_target_ref) { morphMap.set(name, i); i++; } previousWeights = new Float32Array(morphWeights.length); for (i in 0...morphWeights.length) { previousWeights.set(i, morphWeights.get(i)); } // batch system pendingUpdates = new Map(); } inline function initWeights(defaults: Float32Array) { morphWeights = new Float32Array(defaults.length); for (i in 0...morphWeights.length) { morphWeights.set(i, defaults.get(i)); } } public function setMorphValue(name: String, value: Float) { var i = morphMap.get(name); if (i != null) { if (batchUpdateEnabled) { pendingUpdates.set(i, value); } else { setMorphValueDirect(i, value); } } } // faster indexed access public inline function setMorphValueDirect(index: Int, value: Float) { var current = morphWeights.get(index); // allow explicit zero values to reset if (value == 0.0 && current != 0.0) { morphWeights.set(index, value); isDirty = true; return; } var delta = value - current; if (delta < -changeThreshold || delta > changeThreshold) { morphWeights.set(index, value); isDirty = true; } } // flush pending batch public function flushBatchedUpdates() { if (pendingUpdates.keys().hasNext()) { var anyChanged = false; var hasZeros = false; for (index in pendingUpdates.keys()) { var value = pendingUpdates.get(index); if (value == null) continue; if (value == 0.0) hasZeros = true; var current = morphWeights.get(index); var delta = value - current; if (value == 0.0 && current != 0.0) { try{ morphWeights.set(index, cast value); }catch(e){ trace("ERROR: " + e); } anyChanged = true; } else if (delta < -changeThreshold || delta > changeThreshold) { morphWeights.set(index, cast value); anyChanged = true; } } pendingUpdates.clear(); if (anyChanged || hasZeros) { isDirty = true; } } } public inline function markClean() { isDirty = false; for (i in 0...morphWeights.length) { previousWeights.set(i, morphWeights.get(i)); } } public inline function markDirty() { isDirty = true; } // toggle batch mode public inline function setBatchMode(enabled: Bool) { if (!enabled && batchUpdateEnabled) { flushBatchedUpdates(); } batchUpdateEnabled = enabled; } public function resetAllWeights() { for (i in 0...morphWeights.length) { morphWeights.set(i, 0.0); } pendingUpdates.clear(); isDirty = true; } } #end