3 # 3-D gear wheels. This program is in the public domain.
5 # Command line options:
6 # -info print GL implementation information
7 # -exit automatically exit after 30 seconds
9 # 2009-Mar-08 Modified Ruby version by Michael Brooks changed to demonstrate
10 # using GLSL shaders to implement velvet and shiny materials.
11 # 2005-May-01 Ruby version by Arto Bendiken based on gears.c rev 1.8.
12 # 2005-Jan-09 Original C version (gears.c) by Brian Paul et al.
13 # http://cvs.freedesktop.org/mesa/Mesa/progs/demos/gears.c?rev=1.8
23 LIGHT_POS = [100.0, 0.0, 0.0, 1.0]
24 RED = [0.8, 0.1, 0.0, 1.0]
25 GREEN = [0.0, 0.8, 0.2, 1.0]
26 BLUE = [0.2, 0.2, 1.0, 1.0]
28 def create_gear(inner_radius, outer_radius, width, teeth, tooth_depth)
29 # Draw a gear wheel based on the following inputs:
31 # inner_radius - radius of hole at center
32 # outer_radius - radius at center of teeth
33 # width - width of gear
34 # teeth - number of teeth
35 # tooth_depth - depth of tooth
38 r1 = outer_radius - tooth_depth / 2.0
39 r2 = outer_radius + tooth_depth / 2.0
41 da = 2.0 * PI / teeth / 4.0
43 GL.Normal(0.0, 0.0, 1.0)
46 GL.Begin(GL::QUAD_STRIP)
48 angle = i * 2.0 * PI / teeth
49 GL.Vertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5)
50 GL.Vertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5)
52 GL.Vertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5)
53 GL.Vertex3f(r1 * cos(angle + 3 * da),
54 r1 * sin(angle + 3 * da), width * 0.5)
59 # Draw front sides of teeth
62 angle = i * 2.0 * PI / teeth
63 GL.Vertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5)
64 GL.Vertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5)
65 GL.Vertex3f(r2 * cos(angle + 2 * da),
66 r2 * sin(angle + 2 * da), width * 0.5)
67 GL.Vertex3f(r1 * cos(angle + 3 * da),
68 r1 * sin(angle + 3 * da), width * 0.5)
72 GL.Normal(0.0, 0.0, -1.0)
75 GL.Begin(GL::QUAD_STRIP)
77 angle = i * 2.0 * PI / teeth
78 GL.Vertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5)
79 GL.Vertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5)
81 GL.Vertex3f(r1 * cos(angle + 3 * da),
82 r1 * sin(angle + 3 * da), -width * 0.5)
83 GL.Vertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5)
88 # Draw back sides of teeth
91 angle = i * 2.0 * PI / teeth
92 GL.Vertex3f(r1 * cos(angle + 3 * da),
93 r1 * sin(angle + 3 * da), -width * 0.5)
94 GL.Vertex3f(r2 * cos(angle + 2 * da),
95 r2 * sin(angle + 2 * da), -width * 0.5)
96 GL.Vertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5)
97 GL.Vertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5)
101 # Draw outward faces of teeth
102 GL.Begin(GL::QUAD_STRIP)
104 angle = i * 2.0 * PI / teeth
105 GL.Vertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5)
106 GL.Vertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5)
107 u = r2 * cos(angle + da) - r1 * cos(angle)
108 v = r2 * sin(angle + da) - r1 * sin(angle)
109 len = sqrt(u * u + v * v)
112 GL.Normal(v, -u, 0.0)
113 GL.Vertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5)
114 GL.Vertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5)
115 GL.Normal(cos(angle), sin(angle), 0.0)
116 GL.Vertex3f(r2 * cos(angle + 2 * da),
117 r2 * sin(angle + 2 * da), width * 0.5)
118 GL.Vertex3f(r2 * cos(angle + 2 * da),
119 r2 * sin(angle + 2 * da), -width * 0.5)
120 u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da)
121 v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da)
122 GL.Normal(v, -u, 0.0)
123 GL.Vertex3f(r1 * cos(angle + 3 * da),
124 r1 * sin(angle + 3 * da), width * 0.5)
125 GL.Vertex3f(r1 * cos(angle + 3 * da),
126 r1 * sin(angle + 3 * da), -width * 0.5)
127 GL.Normal(cos(angle), sin(angle), 0.0)
129 GL.Vertex3f(r1 * cos(0), r1 * sin(0), width * 0.5)
130 GL.Vertex3f(r1 * cos(0), r1 * sin(0), -width * 0.5)
133 # Draw inside radius cylinder
134 GL.Begin(GL::QUAD_STRIP)
136 angle = i * 2.0 * PI / teeth
137 GL.Normal(-cos(angle), -sin(angle), 0.0)
138 GL.Vertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5)
139 GL.Vertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5)
144 def create_shader_program(shader_base_file_name)
145 # Create GLSL program and shader objects
146 glsl_program = GL.CreateProgram()
147 glsl_vertex_shader = GL.CreateShader(GL_VERTEX_SHADER)
148 glsl_fragment_shader = GL.CreateShader(GL_FRAGMENT_SHADER)
150 # Load shader source into the shaders
151 GL.ShaderSource(glsl_vertex_shader,
152 File.read(File.join(Dir.getwd,
153 "#{shader_base_file_name}.vsh")))
154 GL.ShaderSource(glsl_fragment_shader,
155 File.read(File.join(Dir.getwd,
156 "#{shader_base_file_name}.fsh")))
159 GL.CompileShader(glsl_vertex_shader)
160 GL.CompileShader(glsl_fragment_shader)
162 # Attach the shaders to the program
163 GL.AttachShader(glsl_program, glsl_vertex_shader)
164 GL.AttachShader(glsl_program, glsl_fragment_shader)
167 GL.LinkProgram(glsl_program)
169 # Cleanup the shaders, the program copied them so they aren't needed anymore
170 GL.DeleteShader(glsl_vertex_shader)
171 GL.DeleteShader(glsl_fragment_shader)
173 # Pass the GLSL program back to the caller
178 # Setup rotation tracking variables
180 @view_rotx, @view_roty, @view_rotz = 20.0, 30.0, 0.0
182 # Setup OpenGL environment
183 GL.Enable(GL::DEPTH_TEST)
184 GL.Enable(GL::CULL_FACE)
185 GL.Lightfv(GL::LIGHT0, GL::POSITION, LIGHT_POS)
186 GL.Enable(GL::LIGHT0)
187 GL.ShadeModel(GL::SMOOTH)
188 GL.Enable(GL::NORMALIZE)
190 # Create the gear objects
191 # Note: Includes some material attributes which the GLSL shaders
192 # can use or ignore at their disgression.
193 @gear1 = GL.GenLists(1)
194 GL.NewList(@gear1, GL::COMPILE)
195 GL.Material(GL::FRONT, GL::AMBIENT_AND_DIFFUSE, RED)
196 GL.Material(GL::FRONT, GL::SPECULAR, [1.0, 1.0, 1.0]);
197 GL.Material(GL::FRONT, GL::SHININESS, 50);
198 create_gear(1.0, 4.0, 1.0, 20, 0.7)
201 @gear2 = GL.GenLists(1)
202 GL.NewList(@gear2, GL::COMPILE)
203 GL.Material(GL::FRONT, GL::AMBIENT_AND_DIFFUSE, GREEN)
204 GL.Material(GL::FRONT, GL::SPECULAR, [1.0, 1.0, 1.0]);
205 GL.Material(GL::FRONT, GL::SHININESS, 50);
206 create_gear(0.5, 2.0, 2.0, 10, 0.7)
209 @gear3 = GL.GenLists(1)
210 GL.NewList(@gear3, GL::COMPILE)
211 GL.Material(GL::FRONT, GL::AMBIENT_AND_DIFFUSE, BLUE)
212 GL.Material(GL::FRONT, GL::SPECULAR, [1.0, 1.0, 1.0]);
213 GL.Material(GL::FRONT, GL::SHININESS, 50);
214 create_gear(1.3, 2.0, 0.5, 10, 0.7)
217 # Create the shader programs
218 # Note: These shaders are based on code provided by others which has been
219 # slightly cleaned up or modified to better meet the needs of this
220 # demonstration program.
221 @glsl_program1 = create_shader_program('shiny')
222 @glsl_program2 = create_shader_program('velvet')
226 # Delete OpenGL objects that Ruby doesn't know to clean up for us.
227 # Note: This doesn't get called yet. See note in Gears.finalize method.
228 GL.DeleteProgram(@glsl_program1)
229 GL.DeleteProgram(@glsl_program2)
230 GL.DeleteLists(@gear1, 1)
231 GL.DeleteLists(@gear2, 1)
232 GL.DeleteLists(@gear3, 1)
237 GL.Clear(GL::COLOR_BUFFER_BIT | GL::DEPTH_BUFFER_BIT);
239 # Save current scene rotation
243 GL.Rotate(@view_rotx, 1.0, 0.0, 0.0)
244 GL.Rotate(@view_roty, 0.0, 1.0, 0.0)
245 GL.Rotate(@view_rotz, 0.0, 0.0, 1.0)
247 # Activate shader program 1
248 GL.UseProgram(@glsl_program1)
252 GL.Translate(-3.0, -2.0, 0.0)
253 GL.Rotate(@angle, 0.0, 0.0, 1.0)
257 # Activate shader program 2
258 GL.UseProgram(@glsl_program2)
262 GL.Translate(3.1, -2.0, 0.0)
263 GL.Rotate(-2.0 * @angle - 9.0, 0.0, 0.0, 1.0)
269 GL.Translate(-3.1, 4.2, 0.0)
270 GL.Rotate(-2.0 * @angle - 25.0, 0.0, 0.0, 1.0)
274 # Restore "saved" scene rotation
277 # Make the new scene visible in the window
280 # Calculate the drawing performance and display it every 5 seconds
281 @frames = 0 if not defined?(@frames)
282 @t0 = 0 if not defined?(@t0)
284 t = GLUT.Get(GLUT::ELAPSED_TIME)
286 seconds = (t - @t0) / 1000.0
287 fps = @frames / seconds
288 printf("%d frames in %6.3f seconds = %6.3f FPS\n", @frames, seconds, fps)
290 exit if defined?(@autoexit) and t >= 999.0 * @autoexit
294 def handle_window_resize(width, height)
295 # Handle new window size or exposure
296 h = height.to_f / width.to_f
297 GL.Viewport(0, 0, width, height)
298 GL.MatrixMode(GL::PROJECTION)
300 GL.Frustum(-1.0, 1.0, -h, h, 5.0, 60.0)
301 GL.MatrixMode(GL::MODELVIEW)
303 GL.Translate(0.0, 0.0, -40.0)
306 def handle_window_visibilitychange(visibility)
307 # Handle changes in the visibility of the window
308 GLUT.IdleFunc((visibility == GLUT::VISIBLE ? method(:handle_idling).to_proc : nil))
311 def handle_keypress(k, x, y)
312 # Handle keyboard requests for Z view angle change or exit
314 when ?z # pressed 'z', inc Z rotation
316 when ?Z # pressed 'z', dec Z rotation
318 when 27 # pressed escape, exit
324 def handle_special_keypress(k, x, y)
325 # Handle keyboard requests for X and Y view angle change
327 when GLUT::KEY_UP # pressed up-arrow, inc X rotation
329 when GLUT::KEY_DOWN # pressed down-arrow, dec X rotation
331 when GLUT::KEY_LEFT # pressed left-arrow, inc Y rotation
333 when GLUT::KEY_RIGHT # pressed right-arrow, dec Y rotation
339 def handle_mouse_click(button, state, x, y)
340 # Record mouse position when buttons are first clicked to support rotation calc.
345 def handle_mousedown_motion(x, y)
346 # Handle mouse requests for X and Y view angle changes.
347 if @mouse == GLUT::DOWN
348 @view_roty -= @x0 - x
349 @view_rotx -= @y0 - y
355 # Handle additonal tasks in between GLUT drawing events...
357 # - Record the current GLUT ticks for later use in perofmance calc
358 t = GLUT.Get(GLUT::ELAPSED_TIME) / 1000.0
359 @t0_idle = t if !defined?(@t0_idle)
361 # - Tell the gears to rotate 90 degrees per second
362 @angle += 70.0 * (t - @t0_idle)
367 def handle_command_line_parameters()
368 # Handle the following command line parameters:
370 # -info : Prints OpenGL driver information.
371 # -exit : Forces app to run for 30 seconds then exit.
376 printf("GL_RENDERER = %s\n", GL.GetString(GL::RENDERER))
377 printf("GL_VERSION = %s\n", GL.GetString(GL::VERSION))
378 printf("GL_VENDOR = %s\n", GL.GetString(GL::VENDOR))
379 printf("GL_EXTENSIONS = %s\n", GL.GetString(GL::EXTENSIONS))
382 printf("Auto Exit after %i seconds.\n", @autoexit);
388 # Exit if the environment can't handle the OpenGL 2.0 style
389 # GLSL shader api commands
390 if not (GL.respond_to?('CreateProgram') and
391 GL.respond_to?('CreateShader') and
392 GL.respond_to?('ShaderSource') and
393 GL.respond_to?('CompileShader') and
394 GL.respond_to?('AttachShader') and
395 GL.respond_to?('LinkProgram') and
396 GL.respond_to?('DeleteShader') and
397 GL.respond_to?('DeleteProgram'))
398 puts('Error - The program has terminated because the environment does ' +
399 'not support OpenGL 2.0 or greater GLSL shaders.')
403 # Setup OpenGL environment
405 GLUT.InitDisplayMode(GLUT::RGBA | GLUT::DEPTH | GLUT::DOUBLE)
406 GLUT.InitWindowPosition(0, 0)
407 GLUT.InitWindowSize(300, 300)
408 GLUT.CreateWindow('Gears')
410 # Handle command line parameters
411 handle_command_line_parameters()
416 # Setup GLUT event handlers
417 GLUT.DisplayFunc(method(:draw_scene).to_proc)
418 GLUT.ReshapeFunc(method(:handle_window_resize).to_proc)
419 GLUT.VisibilityFunc(method(:handle_window_visibilitychange).to_proc)
420 GLUT.KeyboardFunc(method(:handle_keypress).to_proc)
421 GLUT.SpecialFunc(method(:handle_special_keypress).to_proc)
422 GLUT.MouseFunc(method(:handle_mouse_click).to_proc)
423 GLUT.MotionFunc(method(:handle_mousedown_motion).to_proc)
427 # Cleanup the OpenGL objects
428 # Note: Haven't found a way to get this finalize method called in a
429 # standard GLUT environment when closing the applications.
434 # Pass control to GLUT to display and manage the scene
440 # *** Program execution starts here ***