danabr75-ashton 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +0 -0
  3. data/LICENSE +21 -0
  4. data/README.md +95 -0
  5. data/Rakefile +42 -0
  6. data/examples/bloom_example.rb +59 -0
  7. data/examples/lighting_example.rb +127 -0
  8. data/examples/media/Earth.png +0 -0
  9. data/examples/media/LargeStar.png +0 -0
  10. data/examples/media/SmallStar.png +0 -0
  11. data/examples/media/Star.png +0 -0
  12. data/examples/media/Starfighter.bmp +0 -0
  13. data/examples/media/Starfighter.png +0 -0
  14. data/examples/media/simple.png +0 -0
  15. data/examples/noise_example.rb +94 -0
  16. data/examples/outline_example.rb +86 -0
  17. data/examples/particle_emitter_example.rb +114 -0
  18. data/examples/pixelate_example.rb +52 -0
  19. data/examples/pixelated_texture_example.rb +69 -0
  20. data/examples/radial_blur_example.rb +61 -0
  21. data/examples/shader_image_example.rb +75 -0
  22. data/examples/shockwave_example.rb +75 -0
  23. data/examples/stencil_shader_example.rb +104 -0
  24. data/examples/texture_render_example.rb +54 -0
  25. data/examples/tv_screen_and_static_example.rb +60 -0
  26. data/ext/ashton/GLee.c +18170 -0
  27. data/ext/ashton/GLee.h +17648 -0
  28. data/ext/ashton/ashton.c +42 -0
  29. data/ext/ashton/ashton.h +31 -0
  30. data/ext/ashton/color.c +45 -0
  31. data/ext/ashton/color.h +25 -0
  32. data/ext/ashton/common.h +41 -0
  33. data/ext/ashton/extconf.rb +45 -0
  34. data/ext/ashton/fast_math.c +30 -0
  35. data/ext/ashton/fast_math.h +30 -0
  36. data/ext/ashton/font.c +8 -0
  37. data/ext/ashton/font.h +16 -0
  38. data/ext/ashton/gosu.c +18 -0
  39. data/ext/ashton/gosu.h +19 -0
  40. data/ext/ashton/image.c +8 -0
  41. data/ext/ashton/image.h +16 -0
  42. data/ext/ashton/particle_emitter.c +788 -0
  43. data/ext/ashton/particle_emitter.h +171 -0
  44. data/ext/ashton/pixel_cache.c +237 -0
  45. data/ext/ashton/pixel_cache.h +58 -0
  46. data/ext/ashton/shader.c +9 -0
  47. data/ext/ashton/shader.h +16 -0
  48. data/ext/ashton/texture.c +442 -0
  49. data/ext/ashton/texture.h +63 -0
  50. data/ext/ashton/vendor/gl/include/GL/GL.H +1526 -0
  51. data/ext/ashton/window.c +8 -0
  52. data/ext/ashton/window.h +16 -0
  53. data/lib/ashton.rb +38 -0
  54. data/lib/ashton/gosu_ext/color.rb +25 -0
  55. data/lib/ashton/gosu_ext/font.rb +58 -0
  56. data/lib/ashton/gosu_ext/gosu_module.rb +16 -0
  57. data/lib/ashton/gosu_ext/image.rb +96 -0
  58. data/lib/ashton/gosu_ext/window.rb +79 -0
  59. data/lib/ashton/image_stub.rb +33 -0
  60. data/lib/ashton/lighting/light_source.rb +146 -0
  61. data/lib/ashton/lighting/manager.rb +98 -0
  62. data/lib/ashton/mixins/version_checking.rb +23 -0
  63. data/lib/ashton/particle_emitter.rb +87 -0
  64. data/lib/ashton/pixel_cache.rb +24 -0
  65. data/lib/ashton/shader.rb +386 -0
  66. data/lib/ashton/shaders/bloom.frag +41 -0
  67. data/lib/ashton/shaders/color_inversion.frag +11 -0
  68. data/lib/ashton/shaders/contrast.frag +16 -0
  69. data/lib/ashton/shaders/default.frag +22 -0
  70. data/lib/ashton/shaders/default.vert +14 -0
  71. data/lib/ashton/shaders/fade.frag +14 -0
  72. data/lib/ashton/shaders/grayscale.frag +15 -0
  73. data/lib/ashton/shaders/include/classicnoise2d.glsl +113 -0
  74. data/lib/ashton/shaders/include/classicnoise3d.glsl +177 -0
  75. data/lib/ashton/shaders/include/classicnoise4d.glsl +302 -0
  76. data/lib/ashton/shaders/include/noise2d.glsl +70 -0
  77. data/lib/ashton/shaders/include/noise3d.glsl +102 -0
  78. data/lib/ashton/shaders/include/noise4d.glsl +128 -0
  79. data/lib/ashton/shaders/include/rand.glsl +5 -0
  80. data/lib/ashton/shaders/lighting/distort.frag +57 -0
  81. data/lib/ashton/shaders/lighting/draw_shadows.frag +60 -0
  82. data/lib/ashton/shaders/lighting/shadow_blur.frag +60 -0
  83. data/lib/ashton/shaders/mezzotint.frag +22 -0
  84. data/lib/ashton/shaders/multitexture2.vert +19 -0
  85. data/lib/ashton/shaders/outline.frag +45 -0
  86. data/lib/ashton/shaders/pixelate.frag +48 -0
  87. data/lib/ashton/shaders/radial_blur.frag +63 -0
  88. data/lib/ashton/shaders/sepia.frag +26 -0
  89. data/lib/ashton/shaders/shockwave.frag +38 -0
  90. data/lib/ashton/shaders/signed_distance_field.frag +80 -0
  91. data/lib/ashton/shaders/static.frag +25 -0
  92. data/lib/ashton/shaders/stencil.frag +27 -0
  93. data/lib/ashton/shaders/tv_screen.frag +23 -0
  94. data/lib/ashton/signed_distance_field.rb +151 -0
  95. data/lib/ashton/texture.rb +186 -0
  96. data/lib/ashton/version.rb +3 -0
  97. data/lib/ashton/window_buffer.rb +16 -0
  98. data/spec/ashton/ashton_spec.rb +22 -0
  99. data/spec/ashton/gosu_ext/color_spec.rb +34 -0
  100. data/spec/ashton/gosu_ext/font_spec.rb +57 -0
  101. data/spec/ashton/gosu_ext/gosu_spec.rb +11 -0
  102. data/spec/ashton/gosu_ext/image_spec.rb +66 -0
  103. data/spec/ashton/gosu_ext/window_spec.rb +71 -0
  104. data/spec/ashton/image_stub_spec.rb +46 -0
  105. data/spec/ashton/particle_emitter_spec.rb +123 -0
  106. data/spec/ashton/pixel_cache_spec.rb +153 -0
  107. data/spec/ashton/shader_spec.rb +152 -0
  108. data/spec/ashton/signed_distance_field_spec.rb +163 -0
  109. data/spec/ashton/texture_spec.rb +347 -0
  110. data/spec/helper.rb +12 -0
  111. metadata +309 -0
@@ -0,0 +1,22 @@
1
+ #version 110
2
+
3
+ #include <rand>
4
+
5
+ uniform sampler2D in_Texture;
6
+ uniform int in_WindowWidth;
7
+
8
+ uniform int in_T;
9
+
10
+ varying vec2 var_TexCoord;
11
+
12
+ void main() {
13
+ vec4 color = texture2D(in_Texture, var_TexCoord);
14
+ vec3 mezzo = vec3(0.0);
15
+ float width = float(in_WindowWidth);
16
+
17
+ if(rand(var_TexCoord + float(in_T) / width) <= color.r) { mezzo.r = 1.0; }
18
+ if(rand(var_TexCoord + float(in_T) / width) <= color.g) { mezzo.g = 1.0; }
19
+ if(rand(var_TexCoord + float(in_T) / width) <= color.b) { mezzo.b = 1.0; }
20
+
21
+ gl_FragColor.rgb = mezzo;
22
+ }
@@ -0,0 +1,19 @@
1
+ #version 110
2
+
3
+ attribute vec4 in_Color;
4
+
5
+ varying vec4 var_Color;
6
+ varying vec2 var_TexCoord0;
7
+ varying vec2 var_TexCoord1;
8
+
9
+ void main()
10
+ {
11
+ gl_Position = ftransform();
12
+ var_Color = in_Color;
13
+
14
+ // Set the coordinates for TEXTURE0
15
+ var_TexCoord0 = gl_MultiTexCoord0.xy;
16
+
17
+ // Set the coordinates for TEXTURE1
18
+ var_TexCoord1 = gl_MultiTexCoord1.xy;
19
+ }
@@ -0,0 +1,45 @@
1
+ #version 110
2
+
3
+ const float ALPHA_THRESHOLD = 0.5;
4
+
5
+ const vec2 TextureSize = vec2(1024.0, 1024.0); // Gosu-specific!
6
+ const vec2 PixelSize = 1.0 / TextureSize;
7
+
8
+ // Automatically set by Ray (actually passed from the vertex shader).
9
+ uniform sampler2D in_Texture; // Original texture.
10
+ //uniform int in_WindowWidth;
11
+ //uniform int in_WindowHeight;
12
+
13
+ uniform vec4 in_OutlineColor;
14
+ uniform float in_OutlineWidth; // In pixels.
15
+
16
+ varying vec2 var_TexCoord; // Pixel to process on this pass
17
+ varying vec4 var_Color;
18
+
19
+ void main()
20
+ {
21
+ gl_FragColor = texture2D(in_Texture, var_TexCoord); // * var_Color;
22
+
23
+ if(gl_FragColor.a < ALPHA_THRESHOLD)
24
+ {
25
+ for(int i = -1; i < 2; i++)
26
+ {
27
+ for(int j = -1; j < 2; j++)
28
+ {
29
+ if(i != 0 && j != 0)
30
+ {
31
+ // Get the color of the adjacent pixel.
32
+ vec2 pos = var_TexCoord + vec2(i, j) * PixelSize * in_OutlineWidth;
33
+ if(texture2D(in_Texture, pos).a > ALPHA_THRESHOLD)
34
+ {
35
+ gl_FragColor = in_OutlineColor;
36
+
37
+ // Simplified return, since GLSL 1.10 hates `return`.
38
+ i = 2;
39
+ j = 2;
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,48 @@
1
+ #version 110
2
+
3
+ #define KERNEL_SIZE 9
4
+
5
+ uniform int in_WindowWidth;
6
+ uniform int in_WindowHeight;
7
+
8
+ uniform sampler2D in_Texture;
9
+ uniform int in_PixelSize;
10
+
11
+ varying vec2 var_TexCoord;
12
+
13
+ vec2 texCoords[KERNEL_SIZE];
14
+
15
+ void main(void)
16
+ {
17
+ vec4 avgColor;
18
+ vec2 texCoordsStep = 1.0 /
19
+ (vec2(float(in_WindowWidth), float(in_WindowHeight))/float(in_PixelSize));
20
+ vec2 pixelBin = floor(var_TexCoord / texCoordsStep);
21
+ vec2 inPixelStep = texCoordsStep / 3.0;
22
+ vec2 inPixelHalfStep = inPixelStep / 2.0;
23
+
24
+
25
+ texCoords[0] = vec2(inPixelHalfStep.x, inPixelStep.y*2.0 + inPixelHalfStep.y) + pixelBin * texCoordsStep;
26
+ texCoords[1] = vec2(inPixelStep.x + inPixelHalfStep.x, inPixelStep.y*2.0 + inPixelHalfStep.y) + pixelBin * texCoordsStep;
27
+ texCoords[2] = vec2(inPixelStep.x*2.0 + inPixelHalfStep.x, inPixelStep.y*2.0 + inPixelHalfStep.y) + pixelBin * texCoordsStep;
28
+ texCoords[3] = vec2(inPixelHalfStep.x, inPixelStep.y + inPixelHalfStep.y) + pixelBin * texCoordsStep;
29
+ texCoords[4] = vec2(inPixelStep.x + inPixelHalfStep.x, inPixelStep.y + inPixelHalfStep.y) + pixelBin * texCoordsStep;
30
+ texCoords[5] = vec2(inPixelStep.x*2.0 + inPixelHalfStep.x, inPixelStep.y + inPixelHalfStep.y) + pixelBin * texCoordsStep;
31
+ texCoords[6] = vec2(inPixelHalfStep.x, inPixelHalfStep.y) + pixelBin * texCoordsStep;
32
+ texCoords[7] = vec2(inPixelStep.x + inPixelHalfStep.x, inPixelHalfStep.y) + pixelBin * texCoordsStep;
33
+ texCoords[8] = vec2(inPixelStep.x*2.0 + inPixelHalfStep.x, inPixelHalfStep.y) + pixelBin * texCoordsStep;
34
+
35
+ avgColor = texture2D(in_Texture, texCoords[0]) +
36
+ texture2D(in_Texture, texCoords[1]) +
37
+ texture2D(in_Texture, texCoords[2]) +
38
+ texture2D(in_Texture, texCoords[3]) +
39
+ texture2D(in_Texture, texCoords[4]) +
40
+ texture2D(in_Texture, texCoords[5]) +
41
+ texture2D(in_Texture, texCoords[6]) +
42
+ texture2D(in_Texture, texCoords[7]) +
43
+ texture2D(in_Texture, texCoords[8]);
44
+
45
+ avgColor /= float(KERNEL_SIZE);
46
+
47
+ gl_FragColor = avgColor;
48
+ }
@@ -0,0 +1,63 @@
1
+ #version 110
2
+
3
+ // http://www.gamerendering.com/2008/12/20/radial-blur-filter/
4
+
5
+ uniform sampler2D in_Texture; // Texture to manipulate.
6
+ uniform int in_WindowWidth;
7
+ uniform int in_WindowHeight;
8
+
9
+ uniform float in_Spacing; // good value is 1.0;
10
+ uniform float in_Strength; // good value is 2.2;
11
+
12
+ varying vec2 var_TexCoord;
13
+
14
+ void main(void)
15
+ {
16
+ // some sample positions. GLSL 1.10 is painfully dumb with arrays :)
17
+ float samples[10];
18
+ samples[0] = -0.08;
19
+ samples[1] = -0.05;
20
+ samples[2] = -0.03;
21
+ samples[3] = -0.02;
22
+ samples[4] = -0.01;
23
+ samples[5] = 0.01;
24
+ samples[6] = 0.02;
25
+ samples[7] = 0.03;
26
+ samples[8] = 0.05;
27
+ samples[9] = 0.08;
28
+
29
+ // 0.5,0.5 is the center of the screen
30
+ // so substracting uv from it will result in
31
+ // a vector pointing to the middle of the screen
32
+ vec2 dir = 0.5 - var_TexCoord;
33
+
34
+ // calculate the distance to the center of the screen
35
+ float dist = sqrt(dir.x * dir.x + dir.y * dir.y);
36
+
37
+ // normalize the direction (reuse the distance)
38
+ dir /= dist;
39
+
40
+ // this is the original colour of this fragment
41
+ // using only this would result in a non-blurred version
42
+ vec4 color = texture2D(in_Texture, var_TexCoord);
43
+
44
+ vec4 sum = color;
45
+
46
+ // take 10 additional blur samples in the direction towards
47
+ // the center of the screen
48
+ for (int i = 0; i < 10; i++)
49
+ {
50
+ sum += texture2D(in_Texture, var_TexCoord + dir * samples[i] * in_Spacing);
51
+ }
52
+
53
+ // we have taken eleven samples
54
+ sum *= 1.0/11.0;
55
+
56
+ // weighten the blur effect with the distance to the
57
+ // center of the screen ( further out is blurred more)
58
+ float t = dist * in_Strength;
59
+ t = clamp(t, 0.0, 1.0); //0 &lt;= t &lt;= 1
60
+
61
+ // Blend the original color with the averaged pixels
62
+ gl_FragColor = mix(color, sum, t);
63
+ }
@@ -0,0 +1,26 @@
1
+ #version 110
2
+
3
+ uniform sampler2D in_Texture;
4
+
5
+ varying vec2 var_TexCoord;
6
+
7
+ void main()
8
+ {
9
+ vec4 Sepia1 = vec4( 0.2, 0.05, 0.0, 1.0 );
10
+ vec4 Sepia2 = vec4( 1.0, 0.9, 0.5, 1.0 );
11
+
12
+ vec4 Color = texture2D(in_Texture, vec2(var_TexCoord));
13
+
14
+ if(Color.a == 0.0)
15
+ {
16
+ gl_FragColor = Color;
17
+ }
18
+ else
19
+ {
20
+ float SepiaMix = dot(vec3(0.3, 0.59, 0.11), vec3(Color));
21
+ Color = mix(Color, vec4(SepiaMix), vec4(0.5));
22
+ vec4 Sepia = mix(Sepia1, Sepia2, SepiaMix);
23
+
24
+ gl_FragColor = mix(Color, Sepia, 1.0);
25
+ }
26
+ }
@@ -0,0 +1,38 @@
1
+ #version 110
2
+
3
+ // http://empire-defense.crystalin.fr/blog/2d_shock_wave_texture_with_shader
4
+ // http://www.geeks3d.com/20091116/shader-library-2d-shockwave-post-processing-filter-glsl/
5
+
6
+ uniform sampler2D in_Texture; // 0
7
+ uniform vec2 in_Center; // Mouse position
8
+ uniform float in_Time; // effect elapsed time. Multiply this to affect speed.
9
+
10
+ // Amplitude?, Refraction?, Width? e.g. 10.0, 0.8, 0.1
11
+ uniform vec3 in_ShockParams;
12
+
13
+ uniform int in_WindowWidth;
14
+ uniform int in_WindowHeight;
15
+
16
+ varying vec2 var_TexCoord;
17
+
18
+ void main()
19
+ {
20
+ vec2 uv = var_TexCoord;
21
+ vec2 texCoord = uv;
22
+ float x = in_Center.x / float(in_WindowWidth);
23
+ float y = (float(in_WindowHeight) - in_Center.y) / float(in_WindowHeight);
24
+ float distance = distance(uv, vec2(x, y));
25
+
26
+ if ( (distance <= (in_Time + in_ShockParams.z)) &&
27
+ (distance >= (in_Time - in_ShockParams.z)) )
28
+ {
29
+ float diff = (distance - in_Time);
30
+ float powDiff = 1.0 - pow(abs(diff * in_ShockParams.x),
31
+ in_ShockParams.y);
32
+ float diffTime = diff * powDiff;
33
+ vec2 diffUV = normalize(uv - in_Center);
34
+ texCoord = uv + (diffUV * diffTime);
35
+ }
36
+
37
+ gl_FragColor = texture2D(in_Texture, texCoord);
38
+ }
@@ -0,0 +1,80 @@
1
+ #version 110
2
+
3
+ // Calculates a signed distance field, based on how far any pixel is from an opaque pixel.
4
+ // In each pixel, the signed value is actually r/g/b - 128
5
+ // if r/g/b > 0 => distance from an opaque pixel.
6
+ // if r/g/b <= 0 => distance from an opaque pixel that is adjacent to a transparent pixel.
7
+
8
+ uniform sampler2D in_Texture; // Original texture.
9
+
10
+ uniform vec2 in_TextureSize; // Width and height of the texture, so we know how big pixels are.
11
+ uniform int in_StepSize; // Distance to check each time (larger steps will be faster, but less accurate).
12
+ uniform int in_MaxDistance; // Maximum distance to search out to. Cannot be more than 127!
13
+
14
+ varying vec2 var_TexCoord; // Pixel to process on this pass.
15
+
16
+ const float NUM_SPOKES = 36.0; // Number of radiating lines to check in.
17
+ const float ANGULAR_STEP = 360.0 / NUM_SPOKES;
18
+
19
+ const int ZERO_VALUE = 128; // Color channel containing 0 => -128, 128 => 0, 255 => +127
20
+
21
+ // Returns true an alpha value is found at this distance (any direction).
22
+ bool find_alpha_at_distance(in vec2 center, in vec2 distance, in float alpha)
23
+ {
24
+ bool found = false;
25
+
26
+ for(float angle = 0.0; angle < 360.0; angle += ANGULAR_STEP)
27
+ {
28
+ vec2 position = center + distance * vec2(cos(angle), sin(angle));
29
+
30
+ if(texture2D(in_Texture, position).a == alpha)
31
+ {
32
+ found = true;
33
+ angle = 361.0;
34
+ }
35
+ }
36
+
37
+ return found;
38
+ }
39
+
40
+ void main()
41
+ {
42
+ vec2 pixel_size = 1.0 / in_TextureSize;
43
+
44
+ int distance;
45
+
46
+ if(texture2D(in_Texture, var_TexCoord).a == 0.0)
47
+ {
48
+ // Texel is transparent, search for nearest opaque.
49
+ distance = ZERO_VALUE + 1;
50
+ for(int i = in_StepSize; i < in_MaxDistance; i += in_StepSize)
51
+ {
52
+ if(find_alpha_at_distance(var_TexCoord, float(i) * pixel_size, 1.0))
53
+ {
54
+ i = in_MaxDistance + 1; // BREAK!
55
+ }
56
+ else
57
+ {
58
+ distance = ZERO_VALUE + 1 + i;
59
+ }
60
+ }
61
+ }
62
+ else
63
+ {
64
+ // Texel is opaque, search for nearest transparent.
65
+ distance = ZERO_VALUE;
66
+ for(int i = in_StepSize; i <= in_MaxDistance; i += in_StepSize)
67
+ {
68
+ if(find_alpha_at_distance(var_TexCoord, float(i) * pixel_size, 0.0))
69
+ {
70
+ i = in_MaxDistance + 1; // BREAK!
71
+ }
72
+ else
73
+ {
74
+ distance = ZERO_VALUE - i;
75
+ }
76
+ }
77
+ }
78
+
79
+ gl_FragColor = vec4(vec3(float(distance) / 255.0), 1.0);
80
+ }
@@ -0,0 +1,25 @@
1
+ #version 110
2
+
3
+ #include <rand>
4
+
5
+ // Rather poor quality noise generation, but better than nothing and sort of looks like TV static a bit.
6
+
7
+ uniform sampler2D in_Texture;
8
+
9
+ uniform int in_WindowWidth; // Not used.
10
+ uniform int in_WindowHeight; // Not used.
11
+
12
+ uniform float in_Intensity;
13
+ uniform float in_T;
14
+
15
+ varying vec2 var_TexCoord;
16
+
17
+ void main() {
18
+ vec4 color = texture2D(in_Texture, var_TexCoord);
19
+
20
+ vec4 influence = min(color, 1.0 - color);
21
+
22
+ float noise = 1.0 - 2.0 * rand(var_TexCoord + float(in_T));
23
+
24
+ gl_FragColor = color + in_Intensity * influence * noise;
25
+ }
@@ -0,0 +1,27 @@
1
+ #version 110
2
+
3
+ // Use a stencil texture to selectively draw. Drawing of the image will occur
4
+ // where the stencil is transparent (or only where the stencil is opaque if
5
+ // inverted).
6
+ //
7
+ // Partial transparency in the stencil will allow the image to be drawn
8
+ // partially too.
9
+
10
+ varying vec4 var_Color;
11
+ varying vec2 var_TexCoord0; // The texture/image to draw.
12
+ varying vec2 var_TexCoord1; // The stencil (multitexture).
13
+
14
+ uniform sampler2D in_Texture0; // The texture/image to draw.
15
+ uniform sampler2D in_Texture1; // The stencil (multitexture).
16
+ uniform bool in_Inverted; // true to draw in opaque areas / false to draw in transparent areas.
17
+
18
+ void main()
19
+ {
20
+ vec4 texColor = texture2D(in_Texture0, var_TexCoord0);
21
+ vec4 maskColor = texture2D(in_Texture1, var_TexCoord1);
22
+
23
+ // Only draw the texture where the stencil is transparent (unless inverted).
24
+ float mask_alpha = in_Inverted ? (1.0 - maskColor.a) : maskColor.a;
25
+
26
+ gl_FragColor = vec4(texColor.r, texColor.g, texColor.b, texColor.a - mask_alpha);
27
+ }
@@ -0,0 +1,23 @@
1
+ #version 110
2
+
3
+ uniform sampler2D in_Texture;
4
+
5
+ uniform float in_ColumnWidth; // In pixels.
6
+ uniform int in_WindowWidth;
7
+ uniform int in_WindowHeight; // Not used in this shader.
8
+
9
+ varying vec2 var_TexCoord;
10
+
11
+ void main()
12
+ {
13
+ vec3 color = texture2D(in_Texture, var_TexCoord).rgb;
14
+
15
+ gl_FragColor = vec4(color * 0.25, 1.0);
16
+
17
+ float column_index = var_TexCoord.x * float(in_WindowWidth) / in_ColumnWidth;
18
+
19
+ int c = int(mod(column_index, 3.0));
20
+ if(c == 0) { gl_FragColor.r = color.r; }
21
+ else if(c == 1) { gl_FragColor.g = color.g; }
22
+ else if(c == 2) { gl_FragColor.b = color.b; }
23
+ }
@@ -0,0 +1,151 @@
1
+ module Ashton
2
+ class SignedDistanceField
3
+ ZERO_DISTANCE = 128 # color channel containing 0 => -128, 128 => 0, 129 => +1, 255 => +128
4
+
5
+ attr_reader :width, :height
6
+
7
+ # Creates a Signed Distance Field based on a given image.
8
+ # When drawing into the SDF, drawing should ONLY have alpha of 0 (clear) or 255 (solid)
9
+ #
10
+ # @param width [Integer]
11
+ # @param height [Integer]
12
+ # @param max_distance [Integer] Maximum distance to measure.
13
+ # @option options :step_size [Integer] (1) pixels to step out.
14
+ # @option options :scale [Integer] (1) Scale relative to the image.
15
+ def initialize(width, height, max_distance, options = {}, &block)
16
+ options = {
17
+ scale: 1,
18
+ step_size: 1,
19
+ }.merge! options
20
+
21
+ @width, @height = width, height
22
+ @scale = options[:scale].to_f
23
+
24
+ @shader = Shader.new fragment: :signed_distance_field, uniforms: {
25
+ max_distance: max_distance.ceil,
26
+ step_size: options[:step_size].floor, # One pixel.
27
+ texture_size: [width, height].map(&:to_f),
28
+ }
29
+
30
+ @field = Texture.new (width / @scale).ceil, (height / @scale).ceil
31
+ @mask = Texture.new @field.width, @field.height
32
+
33
+ if block_given?
34
+ render_field(&block)
35
+ else
36
+ @field.clear color: Gosu::Color.rgb(*([ZERO_DISTANCE + max_distance] * 3))
37
+ end
38
+ end
39
+
40
+ # Is the position clear for a given radius around it.
41
+ def position_clear?(x, y, radius)
42
+ sample_distance(x, y) >= radius
43
+ end
44
+
45
+ # If positive, distance, in pixels, to the nearest opaque pixel.
46
+ # If negative, distance in pixels to the nearest transparent pixel.
47
+ def sample_distance(x, y)
48
+ x = [[x, width - 1].min, 0].max
49
+ y = [[y, height - 1].min, 0].max
50
+ # Could be checking any of red/blue/green.
51
+ @field.red((x / @scale).round, (y / @scale).round) - ZERO_DISTANCE
52
+ end
53
+
54
+ # Gets the gradient of the field at a given point.
55
+ # @return [Float, Float] gradient_x, gradient_y
56
+ def sample_gradient(x, y)
57
+ d0 = sample_distance x, y - 1
58
+ d1 = sample_distance x - 1, y
59
+ d2 = sample_distance x + 1, y
60
+ d3 = sample_distance x, y + 1
61
+
62
+ [(d2 - d1) / @scale, (d3 - d0) / @scale]
63
+ end
64
+
65
+ # Get the normal at a given point.
66
+ # @return [Float, Float] normal_x, normal_y
67
+ def sample_normal(x, y)
68
+ gradient_x, gradient_y = sample_gradient x, y
69
+ length = Gosu::distance 0, 0, gradient_x, gradient_y
70
+ if length == 0
71
+ [0, 0] # This could be NaN in edge cases.
72
+ else
73
+ [gradient_x / length, gradient_y / length]
74
+ end
75
+ end
76
+
77
+ # Does the point x1, x2 have line of sight to x2, y2 (that is, no solid in the way).
78
+ def line_of_sight?(x1, y1, x2, y2)
79
+ !line_of_sight_blocked_at(x1, y1, x2, y2)
80
+ end
81
+
82
+ # Returns blocking position, else nil if line of sight isn't blocked.
83
+ def line_of_sight_blocked_at(x1, y1, x2, y2)
84
+ distance_to_travel = Gosu::distance x1, y1, x2, y2
85
+ distance_x, distance_y = x2 - x1, y2 - y1
86
+ distance_travelled = 0
87
+ x, y = x1, y1
88
+
89
+ loop do
90
+ distance = sample_distance x, y
91
+
92
+ # Blocked?
93
+ return [x, y] if distance <= 0
94
+
95
+ distance_travelled += distance
96
+
97
+ # Got to destination in the clear.
98
+ return nil if distance_travelled >= distance_to_travel
99
+
100
+ lerp = distance_travelled.fdiv distance_to_travel
101
+ x = x1 + distance_x * lerp
102
+ y = y1 + distance_y * lerp
103
+ end
104
+ end
105
+
106
+ # Update the SDF should the image have changed.
107
+ # Draw the mask in the passed block.
108
+ def render_field
109
+ raise ArgumentError, "Block required" unless block_given?
110
+
111
+ @mask.render do
112
+ @mask.clear
113
+ $window.scale 1.0 / @scale do
114
+ yield self
115
+ end
116
+ end
117
+
118
+ @shader.enable do
119
+ @field.render do
120
+ @mask.draw 0, 0, 0
121
+ end
122
+ end
123
+
124
+ nil
125
+ end
126
+
127
+ # Draw the field, usually for debugging purposes.
128
+ # @see Ashton::Texture#draw
129
+ def draw(x, y, z, options = {})
130
+ options = {
131
+ mode: :add,
132
+ }.merge! options
133
+
134
+ $window.scale @scale do
135
+ @field.draw x, y, z, options
136
+ end
137
+
138
+ nil
139
+ end
140
+
141
+ # Convert into a nested array of sample values.
142
+ # @return [Array<Array<Integer>>]
143
+ def to_a
144
+ width.times.map do |x|
145
+ height.times.map do |y|
146
+ sample_distance x, y
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end