[Date Prev][Date Next][Thread Prev][Thread Next] [Search] [Date Index] [Thread Index]

[MacPerl] 3D Ball



#!perl -w

# 3D Ball v2.0
# by Kevin Reid
#
# W, A, S and D and/or click & drag to rotate.
# + and - or control-drag to change scale.
# Tab key toggles polygon borders. 
# Escape key quits.
#
# Version History
# 2.0
#   * Added perspective, rotation & scaling
#   * Window now covers screen
#   * Changed name
#   * Added frames-per-second counter
# 1.0
#   * Initial release as "Isometric Ball"

use strict;
use Mac::Windows;
use Mac::QuickDraw;
use Mac::Events;
use Mac::Fonts qw(systemFont);
use Math::Trig qw(deg2rad);

use vars qw(
  $ScrnRect
  $Mods
  $GWindow
  %pos %oldpos %vel
  $tick
  @Polygons
  $ObscureRgn
  $otime $otick $ptick
  %Multipliers
  $frame_size
  $rot $pitch
  $distort
  $mouse
  $oldmouse
  $downflag
  $scale
);

use constant GRAV => 0.8;
use constant LOSS => 0.99;
use constant BOOSTVEL => 23;
use constant DISTORT => 700;

use constant BOXSIZE => 100;
use constant SYSTIME => 100;

use constant ROTSTEP => 360 / 64;
use constant PITCHSTEP => 360 / 64;

use constant C_EMPTY => new RGBColor (50000, 50000, 65535);
use constant C_BACKL => new RGBColor ((47000)x3);
use constant C_BACKR => new RGBColor ((50000)x3);
use constant C_BACKB => new RGBColor ((49000)x3);
use constant C_LINES => new RGBColor ((40000)x3);

BEGIN {
  $ScrnRect = GetMainDevice()->gdRect;
  $ScrnRect->top($ScrnRect->top + 20);
}
use constant WINSIZEH => $ScrnRect->right - $ScrnRect->left; #BOXSIZE *
3.5 + 1;
use constant WINSIZEV => $ScrnRect->bottom - $ScrnRect->top; #BOXSIZE *
3.5 + 1;
use constant WINRECT => new Rect (0, 0, WINSIZEH, WINSIZEV);

sub CalcRot {
  my $rotr = deg2rad($rot);
  my $pitr = deg2rad($pitch);
  %Multipliers = (
    'x' => {'x' => sin($rotr), 'y' => 0, 'z' => cos($rotr)},
    'y' => {'x' => cos($pitr) * cos($rotr), 'y' => sin($pitr), 'z' =>
cos($pitr) * -sin($rotr)},
    'z' => {'x' => -sin($pitr) * cos($rotr), 'y' => cos($pitr), 'z' =>
-sin($pitr) * -sin($rotr)},
  );
  CalcPolys();
}

sub IsoFlatten {
  my ($x, $y, $z) = @_;

  my $rz = Rotate_Z(@_) + $distort;
 
  return (
    ($x * $Multipliers{'x'}{'x'} + $y * $Multipliers{'x'}{'y'} + $z *
$Multipliers{'x'}{'z'}) / $rz * $distort * $scale + WINSIZEH/2,
    ($x * $Multipliers{'y'}{'x'} + $y * $Multipliers{'y'}{'y'} + $z *
$Multipliers{'y'}{'z'}) / $rz * $distort * $scale + WINSIZEV/2,
  );
}

sub Rotate_Z {
  my ($x, $y, $z) = @_;

  return $x * $Multipliers{'z'}{'x'} + $y * $Multipliers{'z'}{'y'} + $z
* $Multipliers{'z'}{'z'};
}

sub DotAt {
  PenSize((2.5 * $scale) x 2);
  MoveTo(@_);
  LineTo(@_);
}

sub BallAt {
  my ($x, $y, $siz) = @_;

  $siz *= $scale;
  InvertOval(new Rect($x-$siz, $y-$siz, $x+$siz, $y+$siz));
}

sub DrawObject {
  my (%cd) = @_;
  SetClip($ObscureRgn);

  if ($cd{z} == 0) {
    BallAt(IsoFlatten(@cd{qw(x y z)}), 8);
  } else {
    BallAt(IsoFlatten(@cd{qw(x y z)}), 8 * $distort / (Rotate_Z(@cd{qw(x
y z)}) + $distort));
  }

  PenSize(2,2);
  DotAt(IsoFlatten(@cd{qw(x y)}, - BOXSIZE * .98));
  DotAt(IsoFlatten(- BOXSIZE * .98, (@cd{qw(y z)})));
  DotAt(IsoFlatten($cd{'x'}, - BOXSIZE * .98, $cd{z}));
  ClipRect(WINRECT);
}

sub GenPoly {
}

sub CalcPolys {

  my $poly;
  foreach (@Polygons) {
    KillPoly $_->{poly};
  }
  @Polygons = ();

  $poly = OpenPoly; # left poly
  MoveTo(IsoFlatten(- BOXSIZE, - BOXSIZE, - BOXSIZE));
  LineTo(IsoFlatten(- BOXSIZE, - BOXSIZE,   BOXSIZE));
  LineTo(IsoFlatten(- BOXSIZE,   BOXSIZE,   BOXSIZE));
  LineTo(IsoFlatten(- BOXSIZE,   BOXSIZE, - BOXSIZE));
  LineTo(IsoFlatten(- BOXSIZE, - BOXSIZE, - BOXSIZE));
  ClosePoly;
  push @Polygons, {poly => $poly, depth => Rotate_Z(- BOXSIZE, 0, 0),
color => C_BACKL};

  $poly = OpenPoly; # right poly
  MoveTo(IsoFlatten(- BOXSIZE, - BOXSIZE, - BOXSIZE));
  LineTo(IsoFlatten(- BOXSIZE,   BOXSIZE, - BOXSIZE));
  LineTo(IsoFlatten(  BOXSIZE,   BOXSIZE, - BOXSIZE));
  LineTo(IsoFlatten(  BOXSIZE, - BOXSIZE, - BOXSIZE));
  LineTo(IsoFlatten(- BOXSIZE, - BOXSIZE, - BOXSIZE));
  ClosePoly;
  push @Polygons, {poly => $poly, depth => Rotate_Z(0, 0, - BOXSIZE),
color => C_BACKR};

  $poly = OpenPoly; # bottom poly
  MoveTo(IsoFlatten(- BOXSIZE, - BOXSIZE, - BOXSIZE));
  LineTo(IsoFlatten(- BOXSIZE, - BOXSIZE,   BOXSIZE));
  LineTo(IsoFlatten(  BOXSIZE, - BOXSIZE,   BOXSIZE));
  LineTo(IsoFlatten(  BOXSIZE, - BOXSIZE, - BOXSIZE));
  LineTo(IsoFlatten(- BOXSIZE, - BOXSIZE, - BOXSIZE));
  ClosePoly;
  push @Polygons, {poly => $poly, depth => Rotate_Z(0, - BOXSIZE, 0),
color => C_BACKB};
  
  # use Data::Dumper; print Dumper \@Polygons;

  @Polygons = sort {$b->{depth} <=> $a->{depth}} @Polygons;
  DisposeRgn $ObscureRgn if $ObscureRgn;
  OpenRgn;
  FrameRect(WINRECT);
  foreach (@Polygons) {
    FramePoly($_->{poly}) if (!$_->{non}) and $_->{depth} < -15;
  }
  $ObscureRgn = CloseRgn;
  InsetRgn($ObscureRgn, 0, 0);

  InvalRect(WINRECT);
}

%pos    = ('x' => 0, 'y' => BOXSIZE - 10, 'z' => 0),
%oldpos = ('x' => 0, 'y' => BOXSIZE - 10, 'z' => 0),
%vel    = ('x' => rand 10, 'y' => 0, 'z' => rand 10),
$otime = 0; $otick = 0; $ptick = '';

$rot = 135;
$pitch = 225;
$frame_size = 1;
$distort = DISTORT;
$scale = 1;

CalcRot();
$mouse = GetMouse();

$GWindow = new MacColorWindow (
  $ScrnRect,
  'Ball',
  1, plainDBox, 1,
);
$GWindow->sethook(drawgrowicon => sub {});
$GWindow->sethook(click => sub {$Mods =
$Mac::Events::CurrentEvent->modifiers; $downflag = 1});
$GWindow->sethook(redraw => sub {
  PenMode(srcCopy);
  PenSize($frame_size,$frame_size);

  foreach (@Polygons) {
    RGBForeColor($_->{color});
    PaintPoly($_->{poly});
    RGBForeColor(C_LINES);
    FramePoly($_->{poly});
  }

  PenMode(srcXor);

  MoveTo(1, 10);
  DrawString($ptick);
  MoveTo(1, 22);
  DrawString("Rot: $rot");
  MoveTo(1, 34);
  DrawString("Tilt: $pitch");

  DrawObject(%pos);
});
$GWindow->sethook(key => sub {
  my ($win, $key) = @_;
  $key = lc chr $key;
  my $updt = 1;

  my $rs = ((abs $pitch % 360) > 180) ? ROTSTEP / $scale : - ROTSTEP /
$scale;
  &{{
    'a' => sub {$rot += $rs},
    'd' => sub {$rot -= $rs},
    'w' => sub {$pitch += PITCHSTEP / $scale},
    's' => sub {$pitch -= PITCHSTEP / $scale},
    "\t" => sub {$frame_size *= -1},
    "\c[" => sub {$GWindow->dispose},
    '+' => sub {$scale *= 1.1},
    '-' => sub {$scale /= 1.1},
  }->{$key} || sub {$updt = 0}};
  if ($updt) {
    CalcRot();
    $rot = $rot - 360 if $rot >= 360;
    $rot = $rot + 360 if $rot < 0;
    $pitch = $pitch - 360 if $pitch >= 360;
    $pitch = $pitch + 360 if $pitch < 0;
  }
});
SetPort $GWindow->window;
PenNormal;
RGBBackColor(C_EMPTY);
TextMode(srcXor);
TextFont(systemFont);

WaitNextEvent;
WaitNextEvent;
WaitNextEvent;

$tick = 0;
while ($tick++, $GWindow->window) {

  SetPort $GWindow->window;

  $oldmouse = $mouse;
  $mouse = GetMouse;
  if ($downflag) {
    if ($oldmouse->h != $mouse->h or $oldmouse->v != $mouse->v) {
      my $xdiff = $mouse->h - $oldmouse->h;
      my $ydiff = $mouse->v - $oldmouse->v;
      if ($Mods & controlKey) {
        $scale += $ydiff / 60;
        $scale = .1 if $scale < .1;
        $scale = 4 if $scale > 4;
      } else {
        if ((abs $pitch % 360) > 180) {$rot -= ($xdiff) * .5}
                                 else {$rot += ($xdiff) * .5}
        $pitch -= ($ydiff) * .5;
      }
      $rot = $rot - 360 if $rot >= 360;
      $rot = $rot + 360 if $rot < 0;
      $pitch = $pitch - 360 if $pitch >= 360;
      $pitch = $pitch + 360 if $pitch < 0;
      CalcRot();
    }
    $downflag = Button();
  }

  foreach (qw(x y z)) {
    my $did = 0;

    $oldpos{$_} = $pos{$_};
    $pos{$_} += $vel{$_};
    if ($pos{$_} < - BOXSIZE +9) {
      $vel{$_} *= - LOSS;
      $pos{$_} = - BOXSIZE +9;
      $did = 1;
    }
    if ($pos{$_} > BOXSIZE -9) {
      $vel{$_} *= - LOSS;
      $pos{$_} = BOXSIZE -9;
    #  $did = 1;
    }
    if ($did and abs($vel{$_}) < 1) {
      $vel{$_} = BOOSTVEL;
    } 
  }

  DrawObject(%oldpos);
  DrawObject(%pos);

  $vel{'y'} -= GRAV;

  if ($otime != time) {
    $otime = time;
    MoveTo(1, 10);
    DrawString($ptick);
    $ptick = 'FPS: ' . ($tick - $otick);
    MoveTo(1, 10);
    DrawString($ptick);
    $otick = $tick;
  }

  WaitNextEvent unless $tick % (101 - SYSTIME);
}

END {
  $GWindow->dispose if $GWindow;
}
__END__

-- 
 Kevin Reid: |    Macintosh:      
  "I'm me."  | Think different.
    

===== Want to unsubscribe from this list?
===== Send mail with body "unsubscribe" to macperl-request@macperl.org