Gradient.hx 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. package hrt.impl;
  2. import haxe.Int32;
  3. import h3d.mat.Texture;
  4. import h3d.Vector4;
  5. typedef ColorStop = {position : Float, color : Int};
  6. enum abstract GradientInterpolation(String) from String to String {
  7. var Linear;
  8. var Cubic;
  9. var Constant;
  10. }
  11. typedef GradientData = {
  12. var stops : Array<ColorStop>;
  13. var resolution : Int;
  14. var isVertical : Bool;
  15. var interpolation: GradientInterpolation;
  16. var colorMode: Int;
  17. };
  18. #if editor
  19. typedef EditorCacheData = {
  20. var tex : Texture;
  21. };
  22. #end
  23. class Gradient {
  24. public var data : GradientData = {
  25. stops: new Array<ColorStop>(),
  26. resolution: 32,
  27. isVertical: false,
  28. interpolation: Linear,
  29. colorMode: 0,
  30. };
  31. public function new () {
  32. }
  33. public static function getDefaultGradientData() : GradientData {
  34. var data : GradientData = {stops: [{position: 0.0, color:0xFF000000}, {position: 1.0, color:0xFFFFFFFF}], resolution: 64, isVertical : false, interpolation: Linear, colorMode: 0};
  35. return data;
  36. }
  37. public static function evalData(data : GradientData, position : Float, ?outVector : Vector4) : Vector4 {
  38. if (outVector == null) outVector = new Vector4();
  39. var i : Int = 0;
  40. while(i < data.stops.length && data.stops[i].position < position) {
  41. i += 1;
  42. }
  43. var firstStopIdx : Int = hxd.Math.iclamp(i-1, 0, data.stops.length-1);
  44. var secondStopIdx : Int = hxd.Math.iclamp(i, 0, data.stops.length-1);
  45. var firstStop = data.stops[firstStopIdx];
  46. var secondStop = data.stops[secondStopIdx];
  47. var c1 : Int = firstStop.color;
  48. var c2 : Int = secondStop.color;
  49. var distance = secondStop.position - firstStop.position;
  50. var offsetFromSecondStop = secondStop.position - position;
  51. var blend = if (distance != 0.0) 1.0 - (offsetFromSecondStop / distance) else 0.0;
  52. blend = hxd.Math.clamp(blend, 0.0, 1.0);
  53. var func = ColorSpace.colorModes[data.colorMode];
  54. var start = func.ARGBToValue(ColorSpace.Color.fromInt(c1), null);
  55. var end = func.ARGBToValue(ColorSpace.Color.fromInt(c2), null);
  56. inline function lerp_angle(a:Float,b:Float,t:Float) : Float {
  57. var diff = (b - a) % 1.0;
  58. var dist = ((2.0 * diff) % 1.0) - diff;
  59. return outVector.x = (a + dist * t + 1.0) % 1.0;
  60. }
  61. switch (data.interpolation) {
  62. case Linear:
  63. outVector.lerp(start, end, blend);
  64. // Patch hue values that need to be lerped around the cercle
  65. if (func.name.charAt(0) == "H") {
  66. outVector.x = lerp_angle(start.x, end.x, blend);
  67. }
  68. case Constant:
  69. outVector.load(start);
  70. case Cubic:
  71. // Honteusement copié de https://github.com/godotengine/godot/blob/c241f1c52386b21cf2df936ee927740a06970db6/scene/resources/gradient.h#L159
  72. var i0 = firstStopIdx-1;
  73. var i3 = secondStopIdx+1;
  74. if (i0 < 0) {
  75. i0 = firstStopIdx;
  76. }
  77. if (i3 >= data.stops.length) {
  78. i3 = data.stops.length-1;
  79. }
  80. var c0 = func.ARGBToValue(ColorSpace.Color.fromInt(data.stops[i0].color), null);
  81. var c3 = func.ARGBToValue(ColorSpace.Color.fromInt(data.stops[i3].color), null);
  82. inline function cubicInterpolate(p_from: Float, p_to: Float, p_pre: Float, p_post: Float, p_weight: Float) {
  83. return 0.5 *
  84. ((p_from * 2.0) +
  85. (-p_pre + p_to) * p_weight +
  86. (2.0 * p_pre - 5.0 * p_from + 4.0 * p_to - p_post) * (p_weight * p_weight) +
  87. (-p_pre + 3.0 * p_from - 3.0 * p_to + p_post) * (p_weight * p_weight * p_weight));
  88. }
  89. outVector.r = cubicInterpolate(start.r, end.r, c0.r, c3.r, blend);
  90. outVector.g = cubicInterpolate(start.g, end.g, c0.g, c3.g, blend);
  91. outVector.b = cubicInterpolate(start.b, end.b, c0.b, c3.b, blend);
  92. outVector.a = cubicInterpolate(start.a, end.a, c0.a, c3.a, blend);
  93. default:
  94. throw "Unknown interpolation mode";
  95. }
  96. var tmp = func.valueToARGB(outVector, null);
  97. ColorSpace.iRGBtofRGB(tmp, outVector);
  98. return outVector;
  99. }
  100. public function eval(position : Float, ?outVector : Vector4) : Vector4 {
  101. return evalData(data, position, outVector);
  102. }
  103. #if !editor
  104. static function getCache() : Map<Int32, h3d.mat.Texture> {
  105. var engine = h3d.Engine.getCurrent();
  106. var cache : Map<Int32, h3d.mat.Texture> = @:privateAccess engine.resCache.get(Gradient);
  107. if(cache == null) {
  108. cache = new Map<Int32, h3d.mat.Texture>();
  109. @:privateAccess engine.resCache.set(Gradient, cache);
  110. }
  111. return cache;
  112. }
  113. #end
  114. #if editor
  115. public static function getEditorCache() : Map<{}, EditorCacheData> {
  116. var engine = h3d.Engine.getCurrent();
  117. var cache : Map<{}, EditorCacheData> = @:privateAccess engine.resCache.get(Gradient);
  118. if(cache == null) {
  119. cache = new Map<{}, EditorCacheData>();
  120. @:privateAccess engine.resCache.set(Gradient, cache);
  121. }
  122. return cache;
  123. }
  124. public static function purgeEditorCache() {
  125. var cache = getEditorCache();
  126. for (c in cache) {
  127. if (c.tex != null) {
  128. c.tex.dispose();
  129. }
  130. }
  131. cache.clear();
  132. }
  133. #end
  134. public static function getDataHash(data : GradientData) : Int32 {
  135. var hash = hxd.Rand.hash(data.resolution);
  136. hash = hxd.Rand.hash(data.isVertical ? 0 : 1, hash);
  137. // Vieux hack nul
  138. hash = hxd.Rand.hash((data.interpolation:String).charCodeAt(0), hash);
  139. hash = hxd.Rand.hash((data.interpolation:String).charCodeAt(1), hash);
  140. hash = hxd.Rand.hash(data.colorMode, hash);
  141. for (stop in data.stops) {
  142. hash = hxd.Rand.hash(stop.color, hash);
  143. hash = hxd.Rand.hash(Std.int(stop.position * 0x7FFFFFFF), hash);
  144. }
  145. return hash;
  146. }
  147. #if castle
  148. public static function textureFromCDB( g : cdb.Types.Gradient, size : Int ) {
  149. if( g == null ) return null;
  150. var d : GradientData = {
  151. stops : [for( i in 0...g.data.colors.length ) { position : g.data.positions[i], color : g.data.colors[i] }],
  152. resolution : size,
  153. isVertical : false,
  154. interpolation: Linear,
  155. colorMode: 0,
  156. };
  157. return textureFromData(d);
  158. }
  159. #end
  160. public static function textureFromData(data : GradientData) : h3d.mat.Texture {
  161. function genPixels() {
  162. var xScale = data.isVertical ? 0 : 1;
  163. var yScale = 1 - xScale;
  164. var pixels = hxd.Pixels.alloc(data.resolution * xScale + 1 * yScale,1 * xScale + data.resolution * yScale, ARGB);
  165. var vec = new Vector4();
  166. for (x in 0...data.resolution) {
  167. evalData(data, x / (data.resolution-1), vec);
  168. pixels.setPixelF(x * xScale,x*yScale, vec);
  169. }
  170. return pixels;
  171. }
  172. #if !editor
  173. var hash = getDataHash(data);
  174. var cache = getCache();
  175. var entry = cache.get(hash);
  176. if (entry != null)
  177. {
  178. return entry;
  179. }
  180. #else
  181. var cache = getEditorCache();
  182. var entry = cache.get(data);
  183. if (entry != null)
  184. {
  185. var texture = entry.tex;
  186. var pixels = genPixels();
  187. if (pixels.width != texture.width || pixels.height != texture.height) {
  188. texture.resize(pixels.width, pixels.height);
  189. }
  190. texture.uploadPixels(pixels);
  191. return texture;
  192. }
  193. #end
  194. var texture = Texture.fromPixels(genPixels(), RGBA);
  195. texture.realloc = function() {
  196. texture.uploadPixels(genPixels());
  197. }
  198. #if !editor
  199. cache.set(hash, texture);
  200. #else
  201. cache.set(data, {tex: texture});
  202. #end
  203. return texture;
  204. }
  205. public function toTexture() : h3d.mat.Texture {
  206. return textureFromData(data);
  207. }
  208. }