2
0

NoiseGenerator.hx 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. package hrt.prefab.l2d;
  2. enum abstract NoiseMode(String) {
  3. var Perlin;
  4. var Ridged;
  5. }
  6. enum abstract RepeatMode(String) {
  7. var Both;
  8. var X;
  9. var Y;
  10. var None;
  11. }
  12. class NoiseGenerator extends Prefab {
  13. @:s public var seed : Int;
  14. @:s public var mode : NoiseMode = Perlin;
  15. @:s public var scale : Float = 1.;
  16. @:s public var channels : Int = 1;
  17. @:s public var normals : Bool = false;
  18. @:s public var contrast : Float = 0.;
  19. @:s public var brightness : Float = 0.;
  20. @:s public var repeat : RepeatMode = Both;
  21. @:s public var size : Int = 512;
  22. @:s public var octaves : Int = 1;
  23. @:s public var persist : Float = 0.5;
  24. @:s public var lacunarity : Float = 2.;
  25. @:s public var gain : Float = 2.0;
  26. @:s public var offset : Float = 0.5;
  27. @:s public var turbulence : Float = 0.;
  28. @:s public var turbulenceScale : Float = 1.;
  29. @:s public var inverse : Bool;
  30. var tex : h3d.mat.Texture;
  31. function new(?parent) {
  32. super(parent);
  33. seed = Std.random(100);
  34. }
  35. public function updateTexture( t : h3d.mat.Texture ) {
  36. var e = h3d.Engine.getCurrent();
  37. e.pushTarget(t);
  38. @:privateAccess e.flushTarget();
  39. var pass = new h3d.pass.ScreenFx(new NoiseShader());
  40. pass.shader.seed = seed;
  41. pass.shader.channels = normals ? 0 : channels;
  42. pass.shader.octaves = octaves;
  43. var scale = size * scale * scale / 16;
  44. pass.shader.scale = Math.round(scale*0.5) * 2;
  45. pass.shader.persist = persist;
  46. pass.shader.lacunarity = lacunarity;
  47. pass.shader.mode = switch( mode ) {
  48. case Perlin: 0;
  49. case Ridged :
  50. pass.shader.gain = gain;
  51. pass.shader.offset = offset;
  52. 1;
  53. }
  54. pass.shader.contrast = contrast;
  55. pass.shader.brightness = brightness;
  56. pass.shader.normals = normals;
  57. pass.shader.inverse = inverse ? 1 : 0;
  58. pass.shader.repeat = switch repeat {
  59. case Both: 0;
  60. case X: 1;
  61. case Y: 2;
  62. case None: 3;
  63. }
  64. pass.shader.turbulence = turbulence * 16 / size;
  65. pass.shader.turbulenceScale = turbulenceScale;
  66. pass.render();
  67. pass.dispose();
  68. e.popTarget();
  69. }
  70. public function toTexture() {
  71. if( tex != null )
  72. return tex;
  73. tex = new h3d.mat.Texture(size, size, [Target]);
  74. if( !tex.flags.has(IsNPOT) ) tex.wrap = Repeat;
  75. updateTexture(tex);
  76. var e = h3d.Engine.getCurrent();
  77. tex.realloc = function() haxe.Timer.delay(function() {
  78. e.setCurrent();
  79. updateTexture(tex);
  80. }, 0);
  81. return tex;
  82. }
  83. override function reload(p:Dynamic) {
  84. if( tex != null ) {
  85. tex.dispose();
  86. tex = null;
  87. }
  88. super.reload(p);
  89. }
  90. function makeTile( tex : h3d.mat.Texture ) {
  91. var t = h2d.Tile.fromTexture(tex);
  92. if( tex.flags.has(IsNPOT) )
  93. return t;
  94. // make wrapping artefacts apparent, if any
  95. return t.sub(repeat == Both || repeat == X ? tex.width >> 1 : 0, repeat == Both || repeat == Y ? tex.height >> 1 : 0, tex.width, tex.height);
  96. }
  97. override function makeInstance( ctx : Context ) {
  98. var tex = new h3d.mat.Texture(size, size, [Target]);
  99. updateTexture(tex);
  100. ctx = ctx.clone(this);
  101. var bmp = new h2d.Bitmap(makeTile(tex), ctx.local2d);
  102. bmp.tileWrap = !tex.flags.has(IsNPOT);
  103. bmp.visible = false;
  104. bmp.x = -size >> 1;
  105. bmp.y = -size >> 1;
  106. ctx.local2d = bmp;
  107. ctx.cleanup = function() tex.dispose();
  108. return ctx;
  109. }
  110. #if editor
  111. override function getHideProps() : HideProps {
  112. return { icon : "cloud", name : "Noise Generator" };
  113. }
  114. override function edit( ctx : EditContext ) {
  115. var e = ctx.properties.add(new hide.Element('
  116. <dl>
  117. <dt>Mode</dt><dd><select field="mode">
  118. <option value="Perlin">Perlin</option>
  119. <option value="Ridged">Ridged</option>
  120. </select>
  121. </dd>
  122. <dt>Size</dt><dd><input type="range" min="16" max="2048" step="16" field="size"/></dd>
  123. <dt>Scale</dt><dd><input type="range" min="0" max="2" field="scale"/></dd>
  124. <dt>Channels</dt><dd><input type="range" min="1" max="4" step="1" field="channels"/></dd>
  125. <dt>NormalMap</dt><dd><input type="checkbox" field="normals"/></dd>
  126. <dt>Repeat</dt><dd>
  127. <select field="repeat">
  128. <option value="Both">Both</option>
  129. <option value="X">X</option>
  130. <option value="Y">Y</option>
  131. <option value="None">None</option>
  132. </select>
  133. </dd>
  134. </dl>
  135. <br/>
  136. <dl>
  137. <dt>Octaves</dt><dd><input type="range" min="1" max="8" step="1" field="octaves"/></dd>
  138. <dt>Persistence</dt><dd><input type="range" min="0.01" max="1" field="persist"/></dd>
  139. <dt>Lacunarity</dt><dd><input type="range" min="1" max="5" field="lacunarity"/></dd>
  140. </dl>
  141. <br/>
  142. ${mode == Ridged ? '
  143. <dl>
  144. <dt>Offset</dt><dd><input type="range" min="-1" max="1" field="offset"/></dd>
  145. <dt>Gain</dt><dd><input type="range" min="0.01" max="5" field="gain"/></dd>
  146. </dl>
  147. <br/>
  148. ' : ''}
  149. <dl>
  150. <dt>Turbulence</dt><dd><input type="range" min="0" max="1" field="turbulence"/></dd>
  151. <dt>Scale</dt><dd><input type="range" min="0" max="10" field="turbulenceScale"/></dd>
  152. </dl>
  153. <br/>
  154. <dl>
  155. <dt>Contrast</dt><dd><input type="range" min="-1" max="1" field="contrast"/></dd>
  156. <dt>Brightness</dt><dd><input type="range" min="-1" max="1" field="brightness"/></dd>
  157. <dt>Inverse</dt><dd><input type="checkbox" field="inverse"/></dd>
  158. </dl>
  159. <br/>
  160. <dl>
  161. <dt>Seed</dt><dd><input type="range" step="1" min="0" max="100" field="seed"/></dd>
  162. <dt>&nbsp;</dt><dd><input type="button" value="Download" name="dl"/></dd>
  163. </dl>
  164. '),this,function(pname) {
  165. var bmp = cast(ctx.getContext(this).local2d, h2d.Bitmap);
  166. var tex = bmp.tile.getTexture();
  167. if( tex.width != size ) {
  168. tex.resize(size, size);
  169. bmp.tile = makeTile(tex);
  170. bmp.tileWrap = !tex.flags.has(IsNPOT);
  171. bmp.x = -size >> 1;
  172. bmp.y = -size >> 1;
  173. }
  174. updateTexture(tex);
  175. ctx.onChange(this, pname);
  176. });
  177. var bmp = cast(ctx.getContext(this).local2d, h2d.Bitmap);
  178. e.find("[name=dl]").click(function(_) {
  179. ctx.ide.chooseFileSave("noise.png", function(f) {
  180. try {
  181. var data = cast(ctx.getContext(this).local2d, h2d.Bitmap).tile.getTexture().capturePixels().toPNG();
  182. sys.io.File.saveBytes(ctx.ide.getPath(f), data);
  183. } catch( e : Dynamic ) {
  184. ctx.ide.error(e);
  185. }
  186. });
  187. });
  188. bmp.visible = true;
  189. ctx.cleanups.push(function() bmp.visible = false);
  190. }
  191. #end
  192. static var _ = Library.register("noise", NoiseGenerator);
  193. }
  194. class NoiseShader extends h3d.shader.ScreenShader {
  195. static var SRC = {
  196. @:import h3d.shader.NoiseLib;
  197. @const(5) var channels : Int = 1;
  198. @const(64) var octaves : Int = 1;
  199. @const(4) var mode : Int;
  200. @const var normals : Bool;
  201. @const(4) var repeat : Int;
  202. @param var seed : Int;
  203. @param var scale : Float = 8;
  204. @param var persist : Float = 0.5;
  205. @param var lacunarity : Float = 2.0;
  206. @param var gain : Float;
  207. @param var offset : Float;
  208. @param var inverse : Float;
  209. @param var contrast : Float;
  210. @param var brightness : Float;
  211. @param var turbulence : Float;
  212. @param var turbulenceScale : Float;
  213. function makeRepeat( scale : Float ) : Vec2 {
  214. var s = int(scale * 0.5) * 2.;
  215. return if( repeat == 0 ) vec2(s) else if( repeat == 1 ) vec2(s,scale) else if( repeat == 2 ) vec2(scale,s) else vec2(scale);
  216. }
  217. function makePeriod( scale : Vec2 ) : Vec2 {
  218. // TODO : the period is unbound for no-repeat but psrnoise
  219. // still exhibits rounding behaviors with low scales
  220. if( repeat == 0 )
  221. return scale;
  222. if( repeat == 1 )
  223. return vec2(scale.x, 1e9);
  224. if( repeat == 2 )
  225. return vec2(1e9, scale.y);
  226. return vec2(1e9);
  227. }
  228. function perturb( uv : Vec2, scale : Float, seed : Int ) : Vec2 {
  229. if( turbulence > 0. ) {
  230. var turbScaleRepeat = makeRepeat(scale * turbulenceScale);
  231. noiseSeed = channels * octaves + 1 + seed;
  232. uv.x += psnoise(calculatedUV * turbScaleRepeat, makePeriod(turbScaleRepeat)) * turbulence;
  233. noiseSeed = channels * octaves + 1025 + seed;
  234. uv.y += psnoise(calculatedUV * turbScaleRepeat, makePeriod(turbScaleRepeat)) * turbulence;
  235. }
  236. return uv;
  237. }
  238. function noise( seed : Int, scale : Float ) : Float {
  239. var scaleRepeat = makeRepeat(scale);
  240. var uv = perturb(calculatedUV,scale,seed);
  241. noiseSeed = seed;
  242. return psnoise(uv * scaleRepeat, makePeriod(scaleRepeat));
  243. }
  244. function noiseNormal( seed : Int, scale : Float ) : Vec3 {
  245. var scaleRepeat = makeRepeat(scale);
  246. var uv = perturb(calculatedUV, scale,seed);
  247. noiseSeed = seed;
  248. return psrdnoise(uv * scaleRepeat, makePeriod(scaleRepeat), 0.).yzx;
  249. }
  250. function calc( channel : Int ) : Float {
  251. var v = 0.;
  252. var seed = seed + channel * octaves;
  253. var scale = scale;
  254. switch( mode ) {
  255. case 0: // perlin
  256. var k = 1.;
  257. for( i in 0...octaves ) {
  258. v += noise(seed + i, scale) * k;
  259. k *= persist;
  260. scale *= lacunarity;
  261. }
  262. case 1: // ridged
  263. var k = 1.;
  264. var s = lacunarity;
  265. var weight = 1.;
  266. var tot = 0.;
  267. for( i in 0...octaves ) {
  268. var g = noise(seed + i, scale) * k;
  269. g = offset - abs(g);
  270. g *= g;
  271. g *= weight;
  272. v += g * s;
  273. tot += k;
  274. weight = g * gain;
  275. if( weight < 0 ) weight = 0 else if( weight > 1 ) weight = 1;
  276. k *= persist;
  277. scale *= lacunarity;
  278. }
  279. v /= tot;
  280. }
  281. v = (v * (1 + contrast) + 1) * 0.5 + brightness;
  282. if( inverse > 0 ) v = 1 - v;
  283. return v;
  284. }
  285. function calcNormal() : Vec3 {
  286. var v = vec3(0.);
  287. var scale = scale;
  288. switch( mode ) {
  289. case 0:
  290. var k = 1.;
  291. for( i in 0...octaves ) {
  292. v += noiseNormal(seed + i, scale) * k;
  293. k *= persist;
  294. scale *= lacunarity;
  295. }
  296. default:
  297. // TODO
  298. v.z = 1.;
  299. }
  300. v.z = (v.z + 1) * 0.5 + 10;
  301. v = v.normalize();
  302. v.xy *= pow(2., 1. + contrast * 4.);
  303. return v.normalize();
  304. }
  305. function fragment() {
  306. var out = vec4(0, 0, 0, 1);
  307. if( normals ) {
  308. out = packNormal(calcNormal());
  309. } else {
  310. if( channels >= 1 ) {
  311. out.r = calc(0);
  312. if( channels == 1 ) out.gb = out.rr;
  313. }
  314. if( channels >= 2 )
  315. out.g = calc(1);
  316. if( channels >= 3 )
  317. out.b = calc(2);
  318. if( channels >= 4 )
  319. out.a = calc(3);
  320. }
  321. output.color = out;
  322. }
  323. }
  324. }