#!/usr/bin/perl -w
#
# Draws a gear.
#
# This code is originally from TQt-1.44, by Troll Tech
#
# Portions of this code have been borrowed from Brian Paul's Mesa
# distribution.
#

package GearWidget;
use OpenGL qw(:all);

use TQt;
use TQt::attributes qw(
    gear1
    gear2
    gear3
    view_rotx
    view_roty
    view_rotz
    angle
);

use TQt::isa qw(TQt::GLWidget);

#
# Draw a gear wheel.  You'll probably want to call this function when
# building a display list since we do a lot of trig here.
#
# Input:  inner_radius - radius of hole at center
#         outer_radius - radius at center of teeth
#         width - width of gear
#         teeth - number of teeth
#         tooth_depth - depth of tooth
#

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;

    my $pi = 3.141592654;
    $da = 2.0*$pi / $teeth / 4.0;

    glShadeModel(GL_FLAT);

    glNormal3f(0.0, 0.0, 1.0);

    # draw front face
    glBegin(GL_QUAD_STRIP);
    for $i (0 .. $teeth) {
	$angle = $i * 2.0*$pi / $teeth;
        glVertex3f($r0*cos($angle), $r0*sin($angle), $width*0.5);
        glVertex3f($r1*cos($angle), $r1*sin($angle), $width*0.5);
        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*$pi / $teeth / 4.0;
    for $i (0 .. $teeth-1) {
       $angle = $i * 2.0*$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 .. $teeth) {
        $angle = $i * 2.0*$pi / $teeth;
        glVertex3f($r1*cos($angle), $r1*sin($angle), -$width*0.5);
        glVertex3f($r0*cos($angle), $r0*sin($angle), -$width*0.5);
        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*$pi / $teeth / 4.0;
    for $i (0 .. $teeth-1) {
        $angle = $i * 2.0*$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 .. $teeth-1) {
        $angle = $i * 2.0*$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.0), $r1*sin(0.0),  $width*0.5);
    glVertex3f($r1*cos(0.0), $r1*sin(0.0), -$width*0.5);

    glEnd();


    glShadeModel(GL_SMOOTH);

    # draw inside radius cylinder
    glBegin(GL_QUAD_STRIP);
    for $i (0 .. $teeth) {
        $angle = $i * 2.0*$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 {
    angle += 2.0;
    view_roty += 1.0;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    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, 2.2, -1.8);
    glRotatef(90.0, 1.0, 0.0, 0.0);
    glRotatef(2.0*angle-2.0, 0.0, 0.0, 1.0);
    glCallList(gear3);
    glPopMatrix();

    glPopMatrix();
}

sub NEW {
    shift->SUPER::NEW(@_);
    this->startTimer(10);
    view_rotx = 20.0;
    view_roty = 30.0; 
    view_rotz = 0.0;
    angle = 0.0;
}

sub initializeGL {
    my $pos = [ 5.0, 5.0, 10.0, 1.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);

    # make the gears
    gear1 = glGenLists(1);
    glNewList(gear1, GL_COMPILE);
    glMaterialfv_p(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, @$red);
    gear(1.0, 4.0, 1.0, 20, 0.7);
    glEndList();

    gear2 = glGenLists(1);
    glNewList(gear2, GL_COMPILE);
    glMaterialfv_p(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, @$green);
    gear(0.5, 2.0, 2.0, 10, 0.7);
    glEndList();

    gear3 = glGenLists(1);
    glNewList(gear3, GL_COMPILE);
    glMaterialfv_p(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, @$blue);
    gear(1.3, 2.0, 0.5, 10, 0.7);
    glEndList();

    glEnable(GL_NORMALIZE);
}

sub resizeGL {
    my($width, $height) = @_;
    my $w = $width / $height;
    my $h = 1.0;

    glViewport(0, 0, $width, $height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(-$w, $w, -$h, $h, 5.0, 60.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0, 0.0, -40.0);
}

sub paintGL {
    draw();
}

sub timerEvent {
    updateGL();
}

package main;

use TQt;
use GearWidget;

$app = TQt::Application(\@ARGV);

if(!TQt::GLFormat::hasOpenGL()) {
    warn("This system has no OpenGL support. Exiting.");
    exit -1;
}

$w = GearWidget;
$app->setMainWidget($w);
$w->show;
exit $app->exec;
