1/*******************************************************************************************
2*
3* raylib [shaders] example - Simple shader mask
4*
5* This example has been created using raylib 2.5 (www.raylib.com)
6* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
7*
8* Example contributed by Chris Camacho (@codifies - http://bedroomcoders.co.uk/)
9* and reviewed by Ramon Santamaria (@raysan5)
10*
11* Copyright (c) 2019 Chris Camacho (@codifies) and Ramon Santamaria (@raysan5)
12*
13********************************************************************************************
14*
15* The shader makes alpha holes in the forground to give the apearance of a top
16* down look at a spotlight casting a pool of light...
17*
18* The right hand side of the screen there is just enough light to see whats
19* going on without the spot light, great for a stealth type game where you
20* have to avoid the spotlights.
21*
22* The left hand side of the screen is in pitch dark except for where the spotlights are.
23*
24* Although this example doesn't scale like the letterbox example, you could integrate
25* the two techniques, but by scaling the actual colour of the render texture rather
26* than using alpha as a mask.
27*
28********************************************************************************************/
29
30#include "raylib.h"
31#include "raymath.h"
32
33#include <stddef.h>
34#include <stdint.h>
35
36#if defined(PLATFORM_DESKTOP)
37 #define GLSL_VERSION 330
38#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB
39 #define GLSL_VERSION 100
40#endif
41
42
43#define MAXSPOT 3 // NB must be the same as define in shader
44#define numStars 400
45
46
47// Spot data
48typedef struct {
49 Vector2 pos;
50 Vector2 vel;
51 float inner;
52 float radius;
53
54 // Shader locations
55 unsigned int posLoc;
56 unsigned int innerLoc;
57 unsigned int radiusLoc;
58} Spot;
59
60// Stars in the star field have a position and velocity
61typedef struct Star {
62 Vector2 pos;
63 Vector2 vel;
64} Star;
65
66void UpdateStar(Star *s);
67void ResetStar(Star *s);
68
69int main(void)
70{
71 // Initialization
72 //--------------------------------------------------------------------------------------
73 const int screenWidth = 800;
74 const int screenHeight = 450;
75
76 InitWindow(screenWidth, screenHeight, "raylib - shader spotlight");
77 HideCursor();
78
79 Texture texRay = LoadTexture("resources/raysan.png");
80
81 Star stars[numStars] = { 0 };
82
83 for (int n = 0; n < numStars; n++) ResetStar(&stars[n]);
84
85 // Progress all the stars on, so they don't all start in the centre
86 for (int m = 0; m < screenWidth/2.0; m++)
87 {
88 for (int n = 0; n < numStars; n++) UpdateStar(&stars[n]);
89 }
90
91 int frameCounter = 0;
92
93
94
95 // Use default vert shader
96 Shader spotShader = LoadShader(0, FormatText("resources/shaders/glsl%i/spotlight.fs", GLSL_VERSION));
97
98 // Get the locations of spots in the shader
99 Spot spots[MAXSPOT];
100/*
101 unsigned int posLoc;
102 unsigned int innerLoc;
103 unsigned int radiusLoc;
104*/
105 for (int i = 0; i < MAXSPOT; i++)
106 {
107 char posName[32] = "spots[x].pos\0";
108 char innerName[32] = "spots[x].inner\0";
109 char radiusName[32] = "spots[x].radius\0";
110
111 posName[6] = '0' + i;
112 innerName[6] = '0' + i;
113 radiusName[6] = '0' + i;
114
115 spots[i].posLoc = GetShaderLocation(spotShader, posName);
116 spots[i].innerLoc = GetShaderLocation(spotShader, innerName);
117 spots[i].radiusLoc = GetShaderLocation(spotShader, radiusName);
118
119 }
120
121 // tell the shader how wide the screen is so we can have
122 // a pitch black half and a dimly lit half.
123 {
124 unsigned int wLoc = GetShaderLocation(spotShader, "screenWidth");
125 float sw = (float)GetScreenWidth();
126 SetShaderValue(spotShader, wLoc, &sw, UNIFORM_FLOAT);
127 }
128
129 // randomise the locations and velocities of the spotlights
130 // and initialise the shader locations
131 for (int i = 0; i < MAXSPOT; i++)
132 {
133
134 spots[i].pos.x = GetRandomValue(64, screenWidth - 64);
135 spots[i].pos.y = GetRandomValue(64, screenHeight - 64);
136 spots[i].vel = (Vector2){ 0, 0 };
137
138 while ((fabs(spots[i].vel.x) + fabs(spots[i].vel.y)) < 2)
139 {
140 spots[i].vel.x = GetRandomValue(-40, 40)/10.0;
141 spots[i].vel.y = GetRandomValue(-40, 40)/10.0;
142 }
143
144 spots[i].inner = 28 * (i + 1);
145 spots[i].radius = 48 * (i + 1);
146
147 SetShaderValue(spotShader, spots[i].posLoc, &spots[i].pos.x, UNIFORM_VEC2);
148 SetShaderValue(spotShader, spots[i].innerLoc, &spots[i].inner, UNIFORM_FLOAT);
149 SetShaderValue(spotShader, spots[i].radiusLoc, &spots[i].radius, UNIFORM_FLOAT);
150 }
151
152 SetTargetFPS(60); // Set to run at 60 frames-per-second
153 //--------------------------------------------------------------------------------------
154
155 // Main game loop
156 while (!WindowShouldClose()) // Detect window close button or ESC key
157 {
158 // Update
159 //----------------------------------------------------------------------------------
160 frameCounter++;
161
162 // Move the stars, resetting them if the go offscreen
163 for (int n = 0; n < numStars; n++) UpdateStar(&stars[n]);
164
165 // Update the spots, send them to the shader
166 for (int i = 0; i < MAXSPOT; i++)
167 {
168 if ( i == 0 ) {
169 Vector2 mp = GetMousePosition();
170 spots[i].pos.x = mp.x;
171 spots[i].pos.y = screenHeight - mp.y;
172 } else {
173 spots[i].pos.x += spots[i].vel.x;
174 spots[i].pos.y += spots[i].vel.y;
175
176 if (spots[i].pos.x < 64) spots[i].vel.x = -spots[i].vel.x;
177 if (spots[i].pos.x > screenWidth - 64) spots[i].vel.x = -spots[i].vel.x;
178 if (spots[i].pos.y < 64) spots[i].vel.y = -spots[i].vel.y;
179 if (spots[i].pos.y > screenHeight - 64) spots[i].vel.y = -spots[i].vel.y;
180 }
181
182 SetShaderValue(spotShader, spots[i].posLoc, &spots[i].pos.x, UNIFORM_VEC2);
183 }
184
185 // Draw
186 //----------------------------------------------------------------------------------
187 BeginDrawing();
188
189 ClearBackground(DARKBLUE);
190
191 // Draw stars and bobs
192 for (int n = 0; n < numStars; n++)
193 {
194 // Single pixel is just too small these days!
195 DrawRectangle(stars[n].pos.x, stars[n].pos.y, 2, 2, WHITE);
196 }
197
198 for (int i = 0; i < 16; i++)
199 {
200 DrawTexture(texRay,
201 (screenWidth/2.0) + cos((frameCounter + i*8)/51.45f)*(screenWidth/2.2) - 32,
202 (screenHeight/2.0) + sin((frameCounter + i*8)/17.87f)*(screenHeight/4.2),
203 WHITE);
204 }
205
206 // Draw spot lights
207 BeginShaderMode(spotShader);
208 // instead of a blank rectangle you could render here
209 // a render texture of the full screen used to do screen
210 // scaling (slight adjustment to shader would be required
211 // to actually pay attention to the colour!)
212 DrawRectangle(0,0,screenWidth,screenHeight,WHITE);
213 EndShaderMode();
214
215 DrawFPS(10, 10);
216
217 DrawText("Move the mouse!", 10, 30, 20, GREEN);
218 DrawText("Pitch Black", screenWidth * .2, screenHeight / 2, 20, GREEN);
219 DrawText("Dark", screenWidth * .66, screenHeight / 2, 20, GREEN);
220
221
222 EndDrawing();
223 //----------------------------------------------------------------------------------
224 }
225
226 // De-Initialization
227 //--------------------------------------------------------------------------------------
228 UnloadTexture(texRay);
229
230 CloseWindow(); // Close window and OpenGL context
231 //--------------------------------------------------------------------------------------
232
233 return 0;
234}
235
236
237void ResetStar(Star *s)
238{
239 s->pos = (Vector2){ GetScreenWidth()/2.0f, GetScreenHeight()/2.0f };
240
241 do
242 {
243 s->vel.x = (float)GetRandomValue(-1000, 1000)/100.0f;
244 s->vel.y = (float)GetRandomValue(-1000, 1000)/100.0f;
245
246 } while (!(fabs(s->vel.x) + fabs(s->vel.y) > 1));
247
248 s->pos = Vector2Add(s->pos, Vector2MultiplyV(s->vel, (Vector2){ 8, 8 }));
249}
250
251void UpdateStar(Star *s)
252{
253 s->pos = Vector2Add(s->pos, s->vel);
254
255 if (s->pos.x < 0 || s->pos.x > GetScreenWidth() ||
256 s->pos.y < 0 || s->pos.y > GetScreenHeight())
257 {
258 ResetStar(s);
259 }
260}
261
262
263