The Perl and Raku Conference 2025: Greenville, South Carolina - June 27-29 Learn more

# Ported by: Brian Medley <freesoftware@bmedley.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
use strict;
use OpenGL qw/ :all /;
my ($gear1, $gear2, $gear3);
my $angle = 0;
my $view_rotx = 20.0;
my $view_roty = 30.0;
my $view_rotz = 0.0;
# Window and texture IDs, window width and height.
my $Window_ID;
my $Window_Width = 640;
my $Window_Height = 480;
use constant PROGRAM_TITLE => "glxgears.pl";
use constant M_PI => 3.14159265;
# Inits OpenGL. Calls our own init function, then passes control onto OpenGL.
MAIN:
{
glutInit();
print("CTRL-C to quit\n");
# To see OpenGL drawing, take out the GLUT_DOUBLE request.
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_ALPHA);
# glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_ALPHA);
# skip these MODE checks on win32, they don't work
if ($^O ne 'MSWin32' and $OpenGL::Config->{DEFINE} !~ /-DHAVE_W32API/) {
if (not glutGet(GLUT_DISPLAY_MODE_POSSIBLE))
{
warn "glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_ALPHA) not possible";
warn "...trying without GLUT_ALPHA";
# try without GLUT_ALPHA
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
if (not glutGet(GLUT_DISPLAY_MODE_POSSIBLE))
{
warn "glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH) not possible, exiting quietly";
exit 0;
}
}
}
glutInitWindowSize($Window_Width, $Window_Height);
$Window_ID = glutCreateWindow( PROGRAM_TITLE );
# Register the callback function to do the drawing.
glutDisplayFunc(\&draw);
# If there's nothing to do, draw.
glutIdleFunc(\&draw);
# It's a good idea to know when our window's resized.
glutReshapeFunc(\&reshape);
# OK, OpenGL's ready to go. Let's call our own init function.
ourInit($Window_Width, $Window_Height);
glutMainLoop();
exit(0);
}
sub ourInit
{
my ($Width, $Height) = @_;
my @pos = ( 5.0, 5.0, 10.0, 0.0 );
my @red = ( 0.8, 0.1, 0.0, 1.0 );
my @green = ( 0.0, 0.8, 0.2, 1.0 );
my @blue = ( 0.2, 0.2, 1.0, 1.0 );
glLightfv_p(GL_LIGHT0, GL_POSITION, @pos);
glEnable(GL_CULL_FACE);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
$gear1 = glGenLists(1);
glNewList($gear1, GL_COMPILE);
glMaterialfv_s(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, pack("f4", @red));
gear(1.0, 4.0, 1.0, 20, 0.7);
glEndList();
$gear2 = glGenLists(1);
glNewList($gear2, GL_COMPILE);
glMaterialfv_s(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, pack("f4", @green));
gear(0.5, 2.0, 2.0, 10, 0.7);
glEndList();
$gear3 = glGenLists(1);
glNewList($gear3, GL_COMPILE);
glMaterialfv_s(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, pack("f4", @blue));
gear(1.3, 2.0, 0.5, 10, 0.7);
glEndList();
glEnable(GL_NORMALIZE);
reshape($Width, $Height);
}
sub gear
{
my ($inner_radius, $outer_radius, $width, $teeth, $tooth_depth) = @_;
my $i;
my ($r0, $r1, $r2);
my ($angle, $da);
my ($u, $v, $len);
$r0 = $inner_radius;
$r1 = $outer_radius - $tooth_depth / 2.0;
$r2 = $outer_radius + $tooth_depth / 2.0;
$da = 2.0 * M_PI / $teeth / 4.0;
glShadeModel(GL_FLAT);
glNormal3f(0.0, 0.0, 1.0);
# /* draw front face */
glBegin(GL_QUAD_STRIP);
for ($i = 0; $i <= $teeth; $i++) {
$angle = $i * 2.0 * M_PI / $teeth;
glVertex3f($r0 * cos($angle), $r0 * sin($angle), $width * 0.5);
glVertex3f($r1 * cos($angle), $r1 * sin($angle), $width * 0.5);
if ($i < $teeth) {
glVertex3f($r0 * cos($angle), $r0 * sin($angle), $width * 0.5);
glVertex3f($r1 * cos($angle + 3 * $da), $r1 * sin($angle + 3 * $da),
$width * 0.5);
}
}
glEnd();
# /* draw front sides of teeth */
glBegin(GL_QUADS);
$da = 2.0 * M_PI / $teeth / 4.0;
for ($i = 0; $i < $teeth; $i++) {
$angle = $i * 2.0 * M_PI / $teeth;
glVertex3f($r1 * cos($angle), $r1 * sin($angle), $width * 0.5);
glVertex3f($r2 * cos($angle + $da), $r2 * sin($angle + $da), $width * 0.5);
glVertex3f($r2 * cos($angle + 2 * $da), $r2 * sin($angle + 2 * $da),
$width * 0.5);
glVertex3f($r1 * cos($angle + 3 * $da), $r1 * sin($angle + 3 * $da),
$width * 0.5);
}
glEnd();
glNormal3f(0.0, 0.0, -1.0);
# /* draw back face */
glBegin(GL_QUAD_STRIP);
for ($i = 0; $i <= $teeth; $i++) {
$angle = $i * 2.0 * M_PI / $teeth;
glVertex3f($r1 * cos($angle), $r1 * sin($angle), -$width * 0.5);
glVertex3f($r0 * cos($angle), $r0 * sin($angle), -$width * 0.5);
if ($i < $teeth) {
glVertex3f($r1 * cos($angle + 3 * $da), $r1 * sin($angle + 3 * $da),
-$width * 0.5);
glVertex3f($r0 * cos($angle), $r0 * sin($angle), -$width * 0.5);
}
}
glEnd();
# /* draw back sides of teeth */
glBegin(GL_QUADS);
$da = 2.0 * M_PI / $teeth / 4.0;
for ($i = 0; $i < $teeth; $i++) {
$angle = $i * 2.0 * M_PI / $teeth;
glVertex3f($r1 * cos($angle + 3 * $da), $r1 * sin($angle + 3 * $da),
-$width * 0.5);
glVertex3f($r2 * cos($angle + 2 * $da), $r2 * sin($angle + 2 * $da),
-$width * 0.5);
glVertex3f($r2 * cos($angle + $da), $r2 * sin($angle + $da), -$width * 0.5);
glVertex3f($r1 * cos($angle), $r1 * sin($angle), -$width * 0.5);
}
glEnd();
# /* draw outward faces of teeth */
glBegin(GL_QUAD_STRIP);
for ($i = 0; $i < $teeth; $i++) {
$angle = $i * 2.0 * M_PI / $teeth;
glVertex3f($r1 * cos($angle), $r1 * sin($angle), $width * 0.5);
glVertex3f($r1 * cos($angle), $r1 * sin($angle), -$width * 0.5);
$u = $r2 * cos($angle + $da) - $r1 * cos($angle);
$v = $r2 * sin($angle + $da) - $r1 * sin($angle);
$len = sqrt($u * $u + $v * $v);
$u /= $len;
$v /= $len;
glNormal3f($v, -$u, 0.0);
glVertex3f($r2 * cos($angle + $da), $r2 * sin($angle + $da), $width * 0.5);
glVertex3f($r2 * cos($angle + $da), $r2 * sin($angle + $da), -$width * 0.5);
glNormal3f(cos($angle), sin($angle), 0.0);
glVertex3f($r2 * cos($angle + 2 * $da), $r2 * sin($angle + 2 * $da),
$width * 0.5);
glVertex3f($r2 * cos($angle + 2 * $da), $r2 * sin($angle + 2 * $da),
-$width * 0.5);
$u = $r1 * cos($angle + 3 * $da) - $r2 * cos($angle + 2 * $da);
$v = $r1 * sin($angle + 3 * $da) - $r2 * sin($angle + 2 * $da);
glNormal3f($v, -$u, 0.0);
glVertex3f($r1 * cos($angle + 3 * $da), $r1 * sin($angle + 3 * $da),
$width * 0.5);
glVertex3f($r1 * cos($angle + 3 * $da), $r1 * sin($angle + 3 * $da),
-$width * 0.5);
glNormal3f(cos($angle), sin($angle), 0.0);
}
glVertex3f($r1 * cos(0), $r1 * sin(0), $width * 0.5);
glVertex3f($r1 * cos(0), $r1 * sin(0), -$width * 0.5);
glEnd();
glShadeModel(GL_SMOOTH);
# /* draw inside radius cylinder */
glBegin(GL_QUAD_STRIP);
for ($i = 0; $i <= $teeth; $i++) {
$angle = $i * 2.0 * M_PI / $teeth;
glNormal3f(-cos($angle), -sin($angle), 0.0);
glVertex3f($r0 * cos($angle), $r0 * sin($angle), -$width * 0.5);
glVertex3f($r0 * cos($angle), $r0 * sin($angle), $width * 0.5);
}
glEnd();
}
sub draw
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
$angle += 2.0;
glPushMatrix();
glRotatef($view_rotx, 1.0, 0.0, 0.0);
glRotatef($view_roty, 0.0, 1.0, 0.0);
glRotatef($view_rotz, 0.0, 0.0, 1.0);
glPushMatrix();
glTranslatef(-3.0, -2.0, 0.0);
glRotatef($angle, 0.0, 0.0, 1.0);
glCallList($gear1);
glPopMatrix();
glPushMatrix();
glTranslatef(3.1, -2.0, 0.0);
glRotatef(-2.0 * $angle - 9.0, 0.0, 0.0, 1.0);
glCallList($gear2);
glPopMatrix();
glPushMatrix();
glTranslatef(-3.1, 4.2, 0.0);
glRotatef(-2.0 * $angle - 25.0, 0.0, 0.0, 1.0);
glCallList($gear3);
glPopMatrix();
glPopMatrix();
# Double-buffer and done
glutSwapBuffers();
}
sub reshape
{
my ($width, $height) = @_;
my $h = $height / $width;
glViewport(0, 0, $width, $height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-1.0, 1.0, -$h, $h, 5.0, 60.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0, 0.0, -40.0);
}