Author: japhb
Date: Mon Nov 10 23:41:45 2008
New Revision: 32521

Added:
   trunk/examples/opengl/shapes.p6
Modified:
   trunk/MANIFEST

Log:
[OpenGL] WIP: Conversion of shapes.pir to Perl 6

* So far, only about 1/3 done with conversion

Modified: trunk/MANIFEST
==============================================================================
--- trunk/MANIFEST      (original)
+++ trunk/MANIFEST      Mon Nov 10 23:41:45 2008
@@ -715,6 +715,7 @@
 examples/nci/xlibtest.p6                                    [main]doc
 examples/nci/xlibtest.pir                                   [main]doc
 examples/nci/xlibtest.rb                                    [main]doc
+examples/opengl/shapes.p6                                   [main]doc
 examples/opengl/shapes.pir                                  [main]doc
 examples/opengl/static-triangle.p6                          [main]doc
 examples/opengl/static-triangle.pir                         [main]doc

Added: trunk/examples/opengl/shapes.p6
==============================================================================
--- (empty file)
+++ trunk/examples/opengl/shapes.p6     Mon Nov 10 23:41:45 2008
@@ -0,0 +1,651 @@
+use v6;
+
+=begin pod
+
+=head1 TITLE
+
+shapes.p6 - Exercise basic OpenGL 1.1/GLUT 3 APIs by drawing animated shapes
+
+=head1 SYNOPSIS
+
+    $ cd parrot-home
+    $ make perl6
+    $ cd parrot-home/runtime/parrot/library
+    $ ../../../perl6 ../../../examples/opengl/shapes.p6
+
+=head1 DESCRIPTION
+
+NOTE: The conversion from F<shapes.pir> is still in progress; much is not
+working yet.
+
+This example is slightly more complex than F<triangle.p6>, and exercises more
+of the OpenGL 1.1 and GLUT 3 APIs.  It is also a better behaved application,
+correctly responding to reshape events, pausing on minimize, and so on.  It is
+a fairly direct translation of f<shapes.pir> to Perl 6.
+
+To quit the example, press C<Q> or the C<ESCAPE> key, or close the window using
+your window manager (using the X in the corner of the window title bar, for
+example).  To pause or restart the animation, press any other ASCII key.
+
+=end pod
+
+
+use OpenGL;
+use NCI::call_toolkit_init;
+
+# .include 'datatypes.pasm'
+
+# .include 'opengl_defines.pasm'
+our $GLUT_RGBA              = 0x0000;
+our $GLUT_DOUBLE            = 0x0002;
+our $GLUT_DEPTH             = 0x0010;
+our $GLUT_STENCIL           = 0x0020;
+
+our $GL_DEPTH_BUFFER_BIT    = 0x0100;
+our $GL_STENCIL_BUFFER_BIT  = 0x0400;
+our $GL_COLOR_BUFFER_BIT    = 0x4000;
+
+our $GL_FALSE               = 0x0000;
+our $GL_TRUE                = 0x0001;
+our $GL_TRIANGLES           = 0x0004;
+our $GL_EQUAL               = 0x0202;
+our $GL_ALWAYS              = 0x0207;
+our $GL_SRC_ALPHA           = 0x0302;
+our $GL_ONE_MINUS_SRC_ALPHA = 0x0303;
+our $GL_FRONT               = 0x0404;
+our $GL_BACK                = 0x0405;
+our $GL_DEPTH_TEST          = 0x0B71;
+our $GL_STENCIL_TEST        = 0x0B90;
+our $GL_NORMALIZE           = 0x0BA1;
+our $GL_BLEND               = 0x0BE2;
+our $GL_MODELVIEW           = 0x1700;
+our $GL_PROJECTION          = 0x1701;
+our $GL_KEEP                = 0x1E00;
+our $GL_REPLACE             = 0x1E01;
+
+
+our $mode        = $GLUT_DOUBLE +| $GLUT_RGBA +| $GLUT_DEPTH +| $GLUT_STENCIL;
+our $glut_window = init_glut($mode, 'Test', @*ARGS);
+
+our $aspect      = 1.0;
+our $frames      = 0;
+our $paused      = 0;
+
+our $now         = time();
+our $time_prev   = $now;
+our $time_curr   = $now;
+our $time_sim    = 0.0;
+our $time_sim_dt = 0.0;
+
+glutIdleFunc(     &idle     );
+glutDisplayFunc(  &draw     );
+# glutReshapeFunc(  &reshape  );
+# glutKeyboardFunc( &keyboard );
+
+glutMainLoop();
+
+
+sub init_glut($display_mode, $window_title, @args is rw) {
+    # Import OpenGL and GLUT functions
+    OpenGL::_export_all_functions();
+
+    # Set larger default window size
+    glutInitWindowSize(500, 500);
+
+    # Initialize GLUT, fixup command line args
+    @args = NCI::call_toolkit_init(&glutInit, @args);
+
+    # Set display mode, create GLUT window, return window handle
+    glutInitDisplayMode($GLUT_DOUBLE +| $GLUT_RGBA +| $GLUT_DEPTH +| 
$GLUT_STENCIL);
+
+    return glutCreateWindow($window_title);
+}
+
+sub idle {
+    $time_prev = $time_curr;
+    $time_curr = $now = time();
+
+    my $dt = $paused ?? 0 !! $now - $time_prev;
+
+    $time_sim_dt = $dt;
+    $time_sim    = $dt + $time_sim;
+
+    glutPostRedisplay() unless $paused;
+}
+
+sub reshape($width, $height is copy) {
+    $height = $height || 1;
+    $aspect = $width / $height;
+
+    # say "HELLO1";
+    glViewport(0, 0, $width, $height);
+    say "HELLO2";
+}
+
+sub keyboard($key, $x, $y) {
+    # For ESCAPE, 'Q', and 'q', exit program
+    if ($key == 27 | 81 | 113) {
+        glutDestroyWindow($glut_window);
+
+        say "FPS: { $time_sim / ($frames || 1) }";
+
+        exit();
+    }
+    # For all other keys, just toggle pause
+    else {
+        $paused = !$paused;
+    }
+}
+
+sub draw {
+    glClear($GL_COLOR_BUFFER_BIT +| $GL_DEPTH_BUFFER_BIT +| 
$GL_STENCIL_BUFFER_BIT);
+
+    set_3d_view();
+    update_particle_effect();
+    draw_reflected_scene();
+    draw_main_scene();
+    set_2d_view();
+
+    glutSwapBuffers();
+    $frames++;
+}
+
+sub set_3d_view {
+    # Simple 60 degree FOV perspective view
+    glMatrixMode($GL_PROJECTION);
+    glLoadIdentity();
+    gluPerspective(60, $aspect, 1, 100);
+
+    # Look at origin from (0,2,4), with +Y up
+    glMatrixMode($GL_MODELVIEW);
+    glLoadIdentity();
+    gluLookAt(0, 2, 4, 0, 0, 0, 0, 1, 0);
+
+    # Rotate view around origin, to see objects from all angles
+    my $angle = ($time_sim * -24) % 360;
+    glRotatef($angle, 0, 1, 0);
+}
+
+sub draw_reflected_scene {
+    # First, make a stencil of the floor, so that reflected scene
+    # doesn't "leak out" of the reflective area
+
+    # Turn off everything we don't need
+    glDisable($GL_DEPTH_TEST);
+    glColorMask($GL_FALSE, $GL_FALSE, $GL_FALSE, $GL_FALSE);
+
+    # Set stencil for just the reflecting area
+    glEnable($GL_STENCIL_TEST);
+    glStencilOp($GL_REPLACE, $GL_REPLACE, $GL_REPLACE);
+    glStencilFunc($GL_ALWAYS, 1, 0xffffffff);
+
+    # Draw the floor as the reflector
+    draw_floor();
+
+    # Now only draw where stencil is set
+    glStencilOp($GL_KEEP, $GL_KEEP, $GL_KEEP);
+    glStencilFunc($GL_EQUAL, 1, 0xffffffff);
+
+    # Turn back on the stuff we turned off
+    glEnable($GL_DEPTH_TEST);
+    glColorMask($GL_TRUE, $GL_TRUE, $GL_TRUE, $GL_TRUE);
+
+    # Flip reflection through the reflector
+    glPushMatrix();
+    glScalef(1, -1, 1);
+
+    # Account for the reversed normals
+    glEnable($GL_NORMALIZE);
+    glCullFace($GL_FRONT);
+
+    # Lights need to be reassigned after reflection
+    set_lights();
+
+    # Draw the reflected objects
+    draw_objects();
+
+    # Switch back to normal facing
+    glDisable($GL_NORMALIZE);
+    glCullFace($GL_BACK);
+    glPopMatrix();
+
+    # Done with stencil
+    glDisable($GL_STENCIL_TEST);
+}
+
+sub draw_main_scene {
+    #Draw floor blended over reflected scene
+    glEnable($GL_BLEND);
+    glBlendFunc($GL_SRC_ALPHA, $GL_ONE_MINUS_SRC_ALPHA);
+
+    draw_floor();
+
+    # Done with blending
+    glDisable($GL_BLEND);
+
+    # Set lights for upright view
+    set_lights();
+
+    # Draw objects (now in upright orientation)
+    draw_objects();
+}
+
+sub draw_floor {
+    # Partially transparent grey (so that reflection shows through)
+    glColor4f(.7, .7, .7, .7);
+
+    # Rotate quadric from +Z up to +Y up
+    glPushMatrix();
+    glRotatef(90, 1, 0, 0);
+
+    # Annulus floor (shapes sit in various spots above it)
+#     my $glu_quadric = gluNewQuadric();
+#     gluDisk($glu_quadric, 1, 2, 128, 1);
+#     gluDeleteQuadric($glu_quadric);
+
+    glPopMatrix();
+}
+
+sub set_lights {
+}
+
+sub draw_objects {
+    draw_rgb_triangle();
+    draw_lit_teapot();
+    draw_particle_effect();
+}
+
+sub draw_rgb_triangle {
+    # Unlit spinning RGB triangle at -Z
+
+    my $angle = ($time_sim * 45) % 360;
+
+    glPushMatrix();
+    glTranslatef(0, 0.04, -1.5);
+    glRotatef($angle, 0, 1, 0);
+
+    glBegin($GL_TRIANGLES);
+    glColor3f(1, 0, 0); glVertex3f(-.5, 0, 0);
+    glColor3f(0, 1, 0); glVertex3f( .5, 0, 0);
+    glColor3f(0, 0, 1); glVertex3f(0  , 1, 0);
+    glEnd();
+
+    glPopMatrix();
+}
+
+sub draw_lit_teapot {
+}
+
+sub update_particle_effect {
+}
+
+sub draw_particle_effect {
+}
+
+sub set_2d_view {
+    glMatrixMode($GL_PROJECTION);
+    glLoadIdentity();
+
+    glMatrixMode($GL_MODELVIEW);
+    glLoadIdentity();
+}
+
+
+=begin pod
+
+.sub main :main
+    # Init miscellaneous globals
+    init_globals()
+
+    # Create particle effect
+    init_particle_effect()
+.end
+
+.sub init_globals
+    # Create structure definition for float4 structure
+    .local pmc float4
+    float4 = new 'ResizablePMCArray'
+    push float4, .DATATYPE_FLOAT
+    push float4, 0
+    push float4, 0
+    push float4, .DATATYPE_FLOAT
+    push float4, 0
+    push float4, 0
+    push float4, .DATATYPE_FLOAT
+    push float4, 0
+    push float4, 0
+    push float4, .DATATYPE_FLOAT
+    push float4, 0
+    push float4, 0
+    set_global 'float4', float4
+.end
+
+.sub init_particle_effect
+    .local pmc pfx_pos, pfx_vel
+    pfx_pos = new 'ResizablePMCArray'
+    pfx_vel = new 'ResizablePMCArray'
+
+    new_particle(0, pfx_pos, pfx_vel)
+
+    set_global 'pfx_pos', pfx_pos
+    set_global 'pfx_vel', pfx_vel
+.end
+
+.sub set_lights
+    glEnable(.GL_LIGHT0)
+
+    # Light above origin
+    .local pmc float4, position
+    float4   = get_global 'float4'
+    position = new 'ManagedStruct', float4
+    position[0] = 0.0
+    position[1] = 2.0
+    position[2] = 0.0
+    position[3] = 1.0
+
+    glLightfv(.GL_LIGHT0, .GL_POSITION, position)
+.end
+
+.sub draw_lit_teapot
+    # Lit cyan teapot at +X
+
+    glPushMatrix()
+    glTranslatef(1.5, .4, 0)
+    glRotatef(90, 0, 1, 0)
+
+    glEnable(.GL_LIGHTING)
+
+    .local pmc float4, color
+    float4 = get_global 'float4'
+    color  = new 'ManagedStruct', float4
+    color[0] = 0.0
+    color[1] = 0.8
+    color[2] = 0.8
+    color[3] = 1.0
+    glMaterialfv(.GL_FRONT, .GL_AMBIENT_AND_DIFFUSE, color)
+
+    color[0] = 1.0
+    color[1] = 1.0
+    color[2] = 1.0
+    color[3] = 1.0
+    glMaterialfv(.GL_FRONT, .GL_SPECULAR,  color)
+    glMaterialf (.GL_FRONT, .GL_SHININESS, 64)
+
+    glutSolidTeapot(.5)
+
+    glDisable(.GL_LIGHTING)
+
+    glPopMatrix()
+.end
+
+.sub new_particle
+    .param int particle_num
+    .param pmc pfx_pos
+    .param pmc pfx_vel
+
+    .local num x, y, z, vx, vy, vz
+    x  = 4.0
+    y  = 0.0
+    z  = 0.0
+
+    vx = 0.0
+    vy = 0.135
+    vz = 0.0
+
+    .local pmc random
+    .local num rand
+    random = new 'Random'
+    rand  = random
+    rand *= .1
+    x    += rand
+    rand  = random
+    rand *= .1
+    y    += rand
+    rand  = random
+    rand *= .1
+    z    += rand
+
+    rand  = random
+    rand -= .5
+    rand *= .01
+    vx   += rand
+    rand  = random
+    rand -= .5
+    rand *= .01
+    vy   += rand
+    rand  = random
+    rand -= .5
+    rand *= .01
+    vz   += rand
+
+    .local pmc pos
+    pos = new 'FixedFloatArray'
+    pos = 3
+    pos[0] = x
+    pos[1] = y
+    pos[2] = z
+
+    .local pmc vel
+    vel = new 'FixedFloatArray'
+    vel = 3
+    vel[0] = vx
+    vel[1] = vy
+    vel[2] = vz
+
+    pfx_pos[particle_num] = pos
+    pfx_vel[particle_num] = vel
+.end
+
+.sub update_particle_effect
+    .param num dt
+
+    # "Black hole" particle effect at +Z
+
+    # Speed up time a little; this effect is *slow*
+    dt *= 30.0
+
+    # Global particle state arrays
+    .local pmc pfx_pos, pfx_vel
+    pfx_pos = get_global 'pfx_pos'
+    pfx_vel = get_global 'pfx_vel'
+
+    # Add a particle at random
+    .local int count
+    count = pfx_pos
+    if count > 1000 goto update_particles
+    .local pmc random
+    .local num rand
+    random = new 'Random'
+    rand   = random
+    rand  *= 4
+    if rand > dt goto update_particles
+    new_particle(count, pfx_pos, pfx_vel)
+
+    # Update all particles
+  update_particles:
+    dec count
+    if count < 0 goto update_particles_end
+
+    # Update particle states
+    update_particle(pfx_pos, pfx_vel, count, dt)
+
+    goto update_particles
+  update_particles_end:
+.end
+
+.sub update_particle
+    .param pmc pfx_pos
+    .param pmc pfx_vel
+    .param int particle_num
+    .param num dt
+
+    .local pmc pos, vel
+    pos = pfx_pos[particle_num]
+    vel = pfx_vel[particle_num]
+
+    # Constants
+    .const num G           = -.075   # Gravitational force constant
+    .const num Cd          = -.00033 # Coefficient of drag
+    .const num event_grav  = -.3     # Gravity at "event horizon"
+    .const num min_dist2   =  .001   # Minimum distance**2 before calc blows up
+    .const num escape_dist = 30      # Distance at which "escape" occurs
+
+    # Particle states
+    .local num x, y, z, vx, vy, vz
+    x  = pos[0]
+    y  = pos[1]
+    z  = pos[2]
+    vx = vel[0]
+    vy = vel[1]
+    vz = vel[2]
+
+    # Calculate distance and distance squared
+    .local num x2, y2, z2, dist2, dist
+    x2     = x * x
+    y2     = y * y
+    z2     = z * z
+    dist2  = x2 + y2
+    dist2 += z2
+    if dist2 >= min_dist2 goto dist2_ok
+    dist2  = min_dist2
+  dist2_ok:
+    dist   = sqrt dist2
+
+    # If distance is too great, particle has "escaped"; regenerate it
+    if dist < escape_dist goto dist_ok
+    new_particle(particle_num, pfx_pos, pfx_vel)
+    .return ()
+  dist_ok:
+
+    # Compute gravity force
+    .local num grav
+    grav = G / dist2
+
+    # If gravity is too strong, it has "passed the event horizon"; regenerate 
it
+    if grav > event_grav goto grav_ok
+    new_particle(particle_num, pfx_pos, pfx_vel)
+    .return ()
+  grav_ok:
+
+    # Calculate gravity vector (always directed toward center of "hole")
+    .local num gx, gy, gz
+    gx  = x / dist
+    gy  = y / dist
+    gz  = z / dist
+    gx *= grav
+    gy *= grav
+    gz *= grav
+
+    # Calculate drag vector (always directed opposite of velocity)
+    # NOTE: Using drag proportional to velocity, instead of velocity squared
+    .local num dragx, dragy, dragz
+    dragx = Cd * vx
+    dragy = Cd * vy
+    dragz = Cd * vz
+
+    # Acceleration is gravity + drag
+    .local num ax, ay, az
+    ax = gx + dragx
+    ay = gy + dragy
+    az = gz + dragz
+
+    # Update velocity and position with simple Euler integration
+    .local num dvx, dvy, dvz
+    .local num  dx,  dy,  dz
+    dvx  = ax * dt
+    dvy  = ay * dt
+    dvz  = az * dt
+    vx  += dvx
+    vy  += dvy
+    vz  += dvz
+    dx   = vx * dt
+    dy   = vy * dt
+    dz   = vz * dt
+    x   += dx
+    y   += dy
+    z   += dz
+
+    # Save new values back to particle state
+    vel[0] = vx
+    vel[1] = vy
+    vel[2] = vz
+    pos[0] = x
+    pos[1] = y
+    pos[2] = z
+.end
+
+.sub draw_particle_effect
+    # "Black hole" particle effect at +Z
+
+    # Make it visually interesting
+    glPushMatrix()
+    glTranslatef(0, .3, 1.5)
+    glRotatef(-20, 0, 0, 1)
+    glRotatef( 90, 1, 0, 0)
+    glScalef(.15, .15, .15)
+
+    # OpenGL state for "glowing transparent particles"
+    glEnable(.GL_BLEND)
+    glBlendFunc(.GL_SRC_ALPHA, .GL_ONE)
+    glDepthMask(.GL_FALSE)
+    glPointSize(4)
+
+    # XXXX: Disabled for now because it's broken on many systems;
+    #       will replace later with textured quads
+    # glEnable(.GL_POINT_SMOOTH)
+
+#     # Show plane of effect
+#     glColor4f(1, 1, 1, .2)
+#     glBegin(.GL_QUADS)
+#     glVertex3f(-2, -2, 0)
+#     glVertex3f( 2, -2, 0)
+#     glVertex3f( 2,  2, 0)
+#     glVertex3f(-2,  2, 0)
+#     glEnd()
+
+    # Start drawing particles
+    glColor4f(1, 1, 1, .5)
+    glBegin(.GL_POINTS)
+
+    # Global particle state arrays
+    .local pmc pfx_pos, pfx_vel
+    pfx_pos = get_global 'pfx_pos'
+    pfx_vel = get_global 'pfx_vel'
+
+    # Loop over all particles, updating and drawing them
+    .local int count
+    count = pfx_pos
+  draw_particle_loop:
+    dec count
+    if count < 0 goto draw_particle_loop_end
+
+    # Draw particle
+    .local pmc pos
+    .local num x, y, z
+    pos = pfx_pos[count]
+    x   = pos[0]
+    y   = pos[1]
+    z   = pos[2]
+    glVertex3f(x, y, z)
+
+    goto draw_particle_loop
+
+    # Finished with all particles
+  draw_particle_loop_end:
+    glEnd()
+
+    # Done, return to normal OpenGL state
+    glDepthMask(.GL_TRUE)
+    glDisable(.GL_BLEND)
+
+    glPopMatrix()
+.end
+
+
+=end pod
+
+# Local Variables:
+#   mode: pir
+#   fill-column: 100
+# End:
+# vim: expandtab shiftwidth=4 ft=perl6:

Reply via email to