Created
July 23, 2012 12:43
-
-
Save zpmorgan/3163442 to your computer and use it in GitHub Desktop.
double pendulum simulation in box2d & sdl spewed to gst-launch with videoparse
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This example uses b2DistanceJoint to simulate a pendulum. | |
use strict; | |
use warnings; | |
use Box2D; | |
use SDL; | |
use SDL::Event; | |
use SDL::Video; | |
use SDLx::App; | |
my $gst_handle; | |
#video/x-raw-rgb,format=RGBA,depth=32,width=450,height=450,framerate=30 ! \ | |
#format=rgba also works for this; it just swaps r & b color channels. | |
open( $gst_handle, | |
'| gst-launch fdsrc ! \ | |
videoparse format=bgra width=450 height=450 ! \ | |
ffmpegcolorspace ! \ | |
theoraenc bitrate=256 ! oggmux ! \ | |
filesink location=video.ogg ', | |
); | |
binmode($gst_handle); | |
sub spew_frame{ | |
my $app = shift; | |
#warn length ${$app->get_pixels_ptr}; | |
# my $buf = pixels_to_string($app->get_pixels_ptr, 450,450); | |
print $gst_handle ${$app->get_pixels_ptr}; | |
} | |
my $segments = $ARGV[0] || 2; | |
# The number of steps to apply to each frame, | |
# This seems to make joint correction lose less energy. | |
my $precision = 40; | |
# pixels | |
my $width = 450; | |
my $height = 450; | |
# pixels per meter | |
my $ppm = 30; | |
# meters per pixel | |
my $mpp = 1.0 / $ppm; | |
# frames per second | |
my $fps = 20.0; | |
my $timestep = 1.0 / $fps; | |
# velocity iterations | |
my $vIters = 8; | |
# position iterations | |
my $pIters = 8; | |
my $gravity = Box2D::b2Vec2->new( 0, -8.0 / $precision**2 ); | |
# no sleep. don't lose energy. | |
my $world = Box2D::b2World->new( $gravity, 0 ); | |
my $rodColor = 0x00CC70FF; | |
my $pathColor = 0x0000CFFF; | |
my $pivot = { | |
x0 => s2w( $width / 2 ), | |
y0 => s2w( $height / 2 ), | |
radius => s2w(4), | |
color => 0x5CCC00FF, | |
}; | |
$pivot->{body} = make_static_circle( @$pivot{qw( x0 y0 radius )} ); | |
$pivot->{anchor} = Box2D::b2Vec2->new( @$pivot{qw( x0 y0 )} ); | |
my $prev_pivot = $pivot; | |
my @bobs; | |
for ( 1 .. $segments ) { | |
my $bob = { | |
y0 => s2w( $height / 2 + .9 * $height * $_ / ( 2 * $segments ) ), | |
x0 => s2w( $width / 2 + 1 ), | |
radius => s2w(6), | |
color => 0xCC005CFF, | |
}; | |
$bob->{body} = make_dynamic_circle( @$bob{qw( x0 y0 radius )} ); | |
$bob->{anchor} = Box2D::b2Vec2->new( @$bob{qw( x0 y0 )} ); | |
my $jointDef = Box2D::b2DistanceJointDef->new(); | |
$jointDef->Initialize( | |
$prev_pivot->{body}, $bob->{body}, | |
$prev_pivot->{anchor}, $bob->{anchor} | |
); | |
# high frequency means less energy lost from joint correction | |
$jointDef->frequencyHz( 1 / $timestep ); | |
$jointDef->dampingRatio(0); | |
$world->CreateJoint($jointDef); | |
$prev_pivot = $bob; | |
push @bobs, $bob; | |
} | |
my $app = SDLx::App->new( | |
width => $width, | |
height => $height, | |
depth => 32, | |
dt => $timestep, | |
min_t => $timestep / 2, | |
flags => SDL_DOUBLEBUF | SDL_HWSURFACE | SDL_RLEACCEL, | |
eoq => 1, | |
); | |
#die $app->format->BytesPerPixel;; | |
my $bg = $app->duplicate; | |
$bg->draw_rect( [ 0, 0, $width, $height ], [ 0, 0, 0, 255 ] ); | |
my $realFps = $fps; | |
my $frames = 1; | |
my $ticks = SDL::get_ticks(); | |
# last location of end of pendulum | |
my $prev_path_pos; | |
$app->add_show_handler( | |
sub { | |
for (1..$precision){ | |
$world->Step( $timestep, $vIters, $pIters ); | |
$world->ClearForces(); | |
} | |
my $endpoint = $bobs[$#bobs]{body}->GetPosition(); | |
my $current_path_pos | |
= [ w2s( $endpoint->x ), w2s( s2w($height) - $endpoint->y ) ]; | |
# trace path on bg | |
$bg->draw_line( $prev_path_pos, $current_path_pos, $pathColor ) | |
if $prev_path_pos; | |
$prev_path_pos = $current_path_pos; | |
# draw bg | |
$bg->blit( $app, [ 0, 0, $width, $height ] ); | |
# draw 1st pendulum | |
my $p1 = $pivot->{body}->GetPosition(); | |
my $p2 = $bobs[0]->{body}->GetPosition(); | |
$app->draw_line( [ w2s( $p1->x ), w2s( s2w($height) - $p1->y ) ], | |
[ w2s( $p2->x ), w2s( s2w($height) - $p2->y ) ], $rodColor ); | |
# draw the other pendulums | |
for ( 0 .. $#bobs - 1 ) { | |
my $b1 = $bobs[$_]->{body}->GetPosition(); | |
my $b2 = $bobs[ $_ + 1 ]->{body}->GetPosition(); | |
$app->draw_line( [ w2s( $b1->x ), w2s( s2w($height) - $b1->y ) ], | |
[ w2s( $b2->x ), w2s( s2w($height) - $b2->y ) ], $rodColor ); | |
} | |
draw_circle($pivot); | |
draw_circle($_) for @bobs; | |
if ( $frames % $fps == 0 ) { | |
my $t = SDL::get_ticks(); | |
$realFps = $fps / ( $t - $ticks ) * 1000; | |
$ticks = $t; | |
} | |
$app->draw_gfx_text( [ 10, 10 ], | |
0xFFFFFFFF, sprintf( "FPS: %0.2f", $realFps ) ); | |
$app->update(); | |
spew_frame($app); | |
$frames++; | |
} | |
); | |
$app->run(); | |
# screen to world | |
sub s2w { return $_[0] * $mpp } | |
# world to screen | |
sub w2s { return $_[0] * $ppm } | |
sub make_static_circle { | |
my ( $x, $y, $r ) = @_; | |
my $bodyDef = Box2D::b2BodyDef->new(); | |
$bodyDef->position->Set( $x, $y ); | |
my $body = $world->CreateBody($bodyDef); | |
my $circle = Box2D::b2CircleShape->new(); | |
$circle->m_radius($r); | |
return $body; | |
} | |
sub make_dynamic_circle { | |
my ( $x, $y, $r ) = @_; | |
my $bodyDef = Box2D::b2BodyDef->new(); | |
$bodyDef->type(Box2D::b2_dynamicBody); | |
$bodyDef->position->Set( $x, $y ); | |
my $body = $world->CreateBody($bodyDef); | |
my $dynamicCircle = Box2D::b2CircleShape->new(); | |
$dynamicCircle->m_radius($r); | |
return $body; | |
} | |
sub draw_circle { | |
my ($circle) = @_; | |
my $p = $circle->{body}->GetPosition(); | |
my ( $x, $y ) = ( $p->x, $p->y ); | |
$y = s2w($height) - $y; | |
my ( $r, $c ) = @$circle{qw( radius color )}; | |
$app->draw_circle_filled( [ w2s($x), w2s($y) ], w2s($r), $c ); | |
} | |
$app->add_event_handler( | |
sub{ | |
if ($_[0]->type == SDL_QUIT){ | |
close($gst_handle); | |
exit(0); | |
} | |
return 1 | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment