File: stencil.cpp

package info (click to toggle)
clanlib 0.8.1-1
  • links: PTS, VCS
  • area: main
  • in suites: lenny
  • size: 28,372 kB
  • ctags: 16,520
  • sloc: cpp: 101,145; sh: 8,752; xml: 6,410; makefile: 1,740; ansic: 463; perl: 424; php: 247
file content (220 lines) | stat: -rw-r--r-- 6,886 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#include <ClanLib/application.h>
#include <ClanLib/core.h>
#include <ClanLib/display.h>
#include <ClanLib/gl.h>

class Application : public CL_ClanApplication
{
public:
	virtual int main(int argc, char** argv)
	{
		CL_ConsoleWindow debug_console("Debug console");
		debug_console.redirect_stdio();

		try
		{
			CL_SetupCore setup_core;
			CL_SetupDisplay setup_display;
			CL_SetupGL setup_gl;

			CL_DisplayWindow window("ClanLib Stencil Demo", 800,600, false);

			stencil_test(window);
		}
		catch (CL_Error error)
		{
			std::cout << "Uncaught error: " << error.message.c_str() << std::endl;
			debug_console.display_close_message();
		}

		return 0;
	}

	void stencil_test(CL_DisplayWindow &window)
	{
		// load surfaces
		CL_Surface wheel("images/wheel.png");
		CL_Surface text("images/stencil_text.png");
		CL_Surface highlight("images/clouds.png");
		CL_Surface spot("images/spot.png");

		// set alignment of surfaces to centered.
		text.set_alignment(origin_center);		
		wheel.set_alignment(origin_center);		
		highlight.set_alignment(origin_center);		

		// set rotation hotspots to center.
		wheel.set_rotation_hotspot(origin_center);		
		highlight.set_rotation_hotspot(origin_center);		

		// change blendfunc of highlight to additive
		highlight.set_blend_func( blend_src_alpha, blend_one );

		spot.set_alignment(origin_bottom_center);
		spot.set_rotation_hotspot(origin_bottom_center);

		// get graphics context handle
		CL_GraphicContext *gc = window.get_gc();

		int width = CL_Display::get_width();
		int height = CL_Display::get_height();
		
		float sin_count = 0;
		int angle = 0;
		int highlight_angle = 0;

		while(!CL_Keyboard::get_keycode(CL_KEY_ESCAPE))
		{
			sin_count += (float) (M_PI/180.0);
			angle += 2;
			highlight_angle += 8;

			int a=int(255.f*fabs(sin(sin_count))) - 30;
			int b=int(255.f*fabs(sin(sin_count+(0.25*M_PI)))) - 30;
			int c=int(255.f*fabs(sin(sin_count+(0.5*M_PI)))) - 30;

			if( a < 0 ) a = 0;
			if( b < 0 ) b = 0;
			if( c < 0 ) c = 0;

			// draw screen background: a blue shifting gradient.
			CL_Display::fill_rect(CL_Rect(0,0,width,height), 
					CL_Gradient( 
						CL_Color(0,0,a,255),
						CL_Color(0,0,c,255),
						CL_Color(0,0,b,255),
						CL_Color(0,0,a,255)));
			
			/* Lower Text:
			 *
			 * This demonstrates the simplest use of stenciling.
			 *
			 * 1. Create the stencil mask.
			 * 	1.1 Stencil buffer writing is enabled
			 * 	1.2 A surface (or other graphics primitive) is drawn as usual
			 * 	1.3 Stencil buffer writing is disabled
			 * 2. Draw, testing against the mask.
			 * 	2.1 Stencil buffer testing is enabled
			 * 	2.2 A surface is drawn as usual.
			 * 	2.3 Stencil testing is disabled.
			 *  
			 *  This will clip the surface drawn in step 2.2 to the area covered by the surface(s)
			 *  drawn to the stencil buffer in step 1.2.
			*/

			// erase old contents from the stencil buffer.
			gc->clear_stencil();

			// Enable drawing to the stencil buffer, disable color buffer writing
			gc->enable_stencil_write(true, false); 

			// Draw into the stencil buffer.
			text.set_scale(1.0f, 1.0f); // reset text size.
			text.draw(width/2,height/2+height/4);

			// Disable stencil writing.
			gc->enable_stencil_write(false);

			// Enable stencil testing.
			gc->enable_stencil_test(true);

			// Draw, testing each pixel against the stencil mask.
			spot.set_angle(angle);
			spot.draw(width/2,height);

			// Disable stencil testing.
			gc->enable_stencil_test(false);


			/* Upper Text:
			 *
			 * Here is a bit more complicated example: 
			 *
			 * Three spinning wheels are drawn inside the text. Each wheel
			 * is overlayed with 2 highlighting surfaces (a grayscale "cloud").
			 * 
			 * here are the steps:
			 *  
			 * 1. The text is drawn into the stencil buffer with color buf write off.
			 *      - Where something is drawn the stencil gets the value 1. 
			 * 2. The wheels are drawn on top of the text into the stencil (not color buf).
			 *      - Stencil testing is also enabled. Only where the stencil
			 *        contains a 1, do we modify the value. Each pixel, successfully drawn
			 *        into the stencil buffer will increase the corresponding value in the stencil.
			 *        Because there are overlapping parts, the parts overlapping the text
			 *        will get the value 2, and the rest remain 1 or 0.
			 * 3. Enable Stencil testing and fill the text with a gradient.
			 *		- the parameters specify that only stecil values equalling 1 get drawn over.
			 *        That means the text, excluding the wheels.
			 * 4. Draw the wheels and their highlight surfaces
			 *      - Stencil is tested for == 2, the value the wheels on top of text have.
			 *        
			 * For further reading about stencil buffer operations search the web
			 * for "OpenGL red book", and the opengl man pages, which are also available
			 * online.
			 */
	
			wheel.set_angle(angle);
			
			gc->clear_stencil();

			text.set_scale(1.2f,1.9f);

			// 1. write text into stencil.
			gc->enable_stencil_write(true, false); // enable stencil write, disable color buf write
			text.draw(width/2,height/2);
			gc->enable_stencil_write(false);

			// 2. draw stencil of wheels clipping to stencil of the text.
			gc->set_stencil_operation( stencil_incr, stencil_keep, stencil_equal, 1);
			gc->enable_stencil_write(true, false); // enable stencil write, disable color buf write
			
			wheel.draw(width/2,height/2);     // overdraw -> value in stencil is incremented
			wheel.draw(width/2-256,height/2); // to 2 for the wheels over text
			wheel.draw(width/2+256,height/2);
			
			// reset to original settings.
			gc->set_stencil_operation( stencil_incr, stencil_keep, stencil_always, 0 );

			// disable stencil writing.
			gc->enable_stencil_write(false);

			// 3. Enable stencil testing and fill the font.
			gc->enable_stencil_test(true, stencil_equal, 1); // pass stencil test if stencil == 1

			CL_Display::fill_rect(CL_Rect(0,0,width,height), 
				CL_Gradient( 
					CL_Color(a,b,c,a),
					CL_Color(b,c,a,b),
					CL_Color(c,b,a,c),
					CL_Color(c,a,b,a)));

			gc->enable_stencil_test(false); 

			// 4. Draw wheels (where overlapping text). 
			gc->enable_stencil_test(true, stencil_equal, 2); // pass if stencil == 2

			// Draw the wheels using stencil testing
			wheel.draw(width/2,height/2);
			wheel.draw(width/2-256,height/2);
			wheel.draw(width/2+256,height/2);

			// Draw highlight texture over wheels.
			highlight.set_angle(angle);
			highlight.draw(width/2,height/2);
			highlight.draw(width/2+256,height/2);
			highlight.draw(width/2-256,height/2);

			highlight.set_angle(highlight_angle);
			highlight.set_color(a,b,c,1.0f);
			highlight.draw(width/2,height/2);
			highlight.draw(width/2+256,height/2);
			highlight.draw(width/2-256,height/2);

			gc->enable_stencil_test(false);

			window.flip();
			CL_System::keep_alive(20);
		}
	}
} app;