/// LSU EE 4702-1 (Fall 2017), GPU Programming // /// Demonstration of transforms. // /// Purpose // // Demonstrate use of basic transforms. /// What Code Does // Simulates a ball bouncing over a platform. The platform consists of // tiles, some are purple-tinted mirrors (showing a reflection of the // ball), the others show the course syllabus. The ball can be // manipulated by the user. // Scene also shows syllabi arranged to from a box and a tower of prisms. /// Keyboard Commands // /// Object (Eye, Light, Ball) Location or Push // Arrows, Page Up, Page Down // Will move object or push ball, depending on mode: // 'e': Move eye. // 'l': Move light. // 'b': Move ball. (Change position but not velocity.) // 'B': Push ball. (Add velocity.) // /// Eye Direction // Home, End, Delete, Insert // Turn the eye direction. // Home should rotate eye direction up, End should rotate eye // down, Delete should rotate eye left, Insert should rotate eye // right. The eye direction vector is displayed in the upper left. /// Text Zoom and Screenshot // 'Ctrl' '+' or 'Ctrl' '=', and 'Ctrl' '-' or 'Ctrl' '_', // Increase and decrease green text size. // 'F12' Write screenshot to file. /// Simulation Options // (Also see variables below.) // // 'p' Pause simulation. (Press again to resume.) // 's' Stop ball. // 'S' Freeze ball. (Set velocity of all vertices to zero.) // 'g' Turn gravity on and off. /// Variables // Selected program variables can be modified using the keyboard. // Use "Tab" to cycle through the variable to be modified, the // name of the variable is displayed next to "VAR" on the bottom // line of green text. // 'Tab' Cycle to next variable. // '`' Cycle to previous variable. // '+' Increase variable value. // '-' Decrease variable value. // // VAR Light Intensity - The light intensity. // VAR Gravity - Gravitational acceleration. (Turn on/off using 'g'.) // VAR Viscosity - Viscosity of air. Modifies opt_air_viscosity. // Include handy classes for coordinates (pCoor), vectors (pVect), etc. // #include <gp/coord.h> // Object Holding Ball State // class Ball { public: // Note: pCoor is a coordinate, pVect is a vector. pCoor position; // Ball Position. pVect velocity; // Ball Velocity. // Member functions to manipulate state of ball. // void push(pVect amt); void translate(pVect amt); void stop(); }; // // Include file containing graphical code for ball, something to be // skipped in this simple example. // #include "demo-1d-objects-graphics.cc" // Return a new prism. Group* new_prism(int num_sides, int texid); // Return a new tower. Group* new_tower(int num_sides, int num_layers, int texid); void World::init() { /// /// Physical Model Initialization /// // Set initial position to a visibly interesting point. // ball.position = pCoor( 12.1, 22, 11.4 ); ball.velocity = pVect(0,0,0); // Time in our simulated world. // world_time = 0; // // Set acceleration vector based on scalar. // // A user-adjustable scalar. // opt_gravity_accel = 9.8; // The vector needed for calculations. // gravity_accel = pVect( 0, -opt_gravity_accel, 0 ); // Make gravity adjustable using user interface. // variable_control.insert(opt_gravity_accel,"Gravity"); // User interface control for turning gravity on and off. // opt_gravity = true; // If false, gravity doesn't act on ball. // For Homework 1: Air Viscosity // opt_air_viscosity = 0.1; // Note: 0 means no air resistance. variable_control.insert(opt_air_viscosity,"Air Viscosity"); // Position of platform that the ball bounces on. // platform_xmin = -40; platform_xmax = 40; platform_zmin = -40; platform_zmax = 40; /// /// Graphical Model Initialization /// init_graphics(); // Our own code for initializing graphics model. /// Instantiate A Prism of Cards // Group* const prism = new_prism(4,texid_syl); groups += prism; // Add to list of objects to displayed. // Below: Use transforms to position prism. pMatrix_Translate prism_move(pVect(12.1,0,11.4)); pMatrix_Rotation prism_rot(pVect(0,1,0),M_PI/5); pMatrix_Scale prism_scale(15); prism->transform = prism_move * prism_rot * prism_scale; /// Instantiate and Position A Tower of Prisms of Cards // Group* const tower = new_tower(5,15,texid_syl); groups += tower; // Add to list of objects to display. // Specify transform. The code below is in a more compact form // than the code setting prism->transform, above. tower->transform = pMatrix_Translate(pVect(6.8,0,21.8)) * pMatrix_Scale(10); texid_a = pBuild_Texture_File("mult.png", false,-1); texid_b = pBuild_Texture_File("mult.png", false,-1); texid_c = pBuild_Texture_File("mult.png", false,-1); } Group* new_prism(int num_sides, int texid) { // Arrange cards to form a prism. Group* const prism = new Group(); const double card_width = 8.5 * 0.0254; // Convert 8.5 inches to meters. const double card_height = 11 * 0.0254; // Convert 11 inches to meters. const double theta = 2 * M_PI / num_sides; const double theta_2 = theta / 2; const double radius = card_width / ( 2 * sin( theta_2 ) ); const float a = radius * cos( theta_2 ); // Note that the lower left of the card is at the origin and lower // edge on positive x-axis. // pMatrix_Translate center_card(pVect(-card_width/2,0,0)); pMatrix_Translate card_to_circle(pVect(0,0,a)); pMatrix_Rotation rotate(pVect(0,1,0),theta); pMatrix position_side_i = card_to_circle * center_card; for ( int side = 0; side < num_sides; side++ ) { Card2* const c = new Card2(card_width,card_height); c->texid = texid; // Specify texture used to decorate card. c->color = pColor(0.9,0.9,0.9); // Specify transform from card's coordinate system to prism's system. // c->transform = position_side_i; prism->contents.push_back( c ); // Add card to prism's contents. position_side_i = rotate * position_side_i; } return prism; } Group* new_tower (int num_sides, int layers, int texid) { // Arrange prisms for form a tower. Group* const tower = new Group(); const double card_height = 11 * 0.0254; for ( int i=0; i<layers; i++ ) { Group* const p = new_prism(num_sides, texid); tower->contents.push_back( p ); p->transform = pMatrix_Translate(0,card_height*i,0) * pMatrix_Rotation(pVect(0,1,0),i*M_PI/10); } return tower; } /// /// Physical Simulation Code /// /// Advance Simulation State by delta_t Seconds // void World::time_step_cpu_v0(double delta_t) { /// Update Position and Velocity of Ball // // Advance physical state by delta_t seconds. // New ball position, accounting for current speed and acceleration, // and assuming no collision. // ball.position += ball.velocity * delta_t + 0.5 * gravity_accel * delta_t * delta_t; // New velocity, assuming no collision. // ball.velocity += gravity_accel * delta_t; /// Possible Collision with Platform // Position and velocity computation simplified: they do not account // for the exact time of collision. (In this particular case the // exact time of collision could have been easily found.) // Return quickly if collision impossible. // if ( !platform_collision_possible(ball.position) ) return; if ( ball.position.y < 0 ) { const float yval = ball.position.y; // Snap ball position to surface. // ball.position.y = 0; ball.velocity.y += sqrt( -2.0 * yval * opt_gravity_accel ); // Reflect y (vertical) component of velocity, with a reduction // due to energy lost in the collision. // if ( ball.velocity.y < 0 ) ball.velocity.y = - 0.9 * ball.velocity.y; } } bool World::platform_collision_possible(pCoor pos) { // Assuming no motion in x or z axes. // return pos.x >= platform_xmin && pos.x <= platform_xmax && pos.z >= platform_zmin && pos.z <= platform_zmax; } /// External Modifications to State // // These allow the user to play with state while simulation // running. // Move the ball. // void Ball::translate(pVect amt) { position += amt; } // Add velocity to the ball. // void Ball::push(pVect amt) { velocity += amt; } // Set the velocity to zero. // void Ball::stop() { velocity = pVect(0,0,0); } void World::frame_callback() { // This routine called whenever window needs to be updated. const double time_now = time_wall_fp(); if ( opt_pause || world_time == 0 ) { /// Don't change simulation state. // world_time = time_now; } else { /// Advance simulation state by wall clock time. // const double delta_t = time_now - world_time; time_step_cpu_v0(delta_t); world_time += delta_t; } // Emit graphical representation of the just-updated physical state. // render(); // Code in demo-1-simple-graphics.cc } int main(int argv, char **argc) { // A class that initializes graphics and provides handy // functionality; we'll call it the helper. Something written for // this class. // pOpenGL_Helper popengl_helper(argv,argc); // Instantiate our bouncing-ball world. // This calls World::init(). // World world(popengl_helper); // Tell our helper to call our frame code 30 times per second, when // the time comes. // popengl_helper.rate_set(30); // Tell our helper code that we are done setting up and that // whenever we need to paint a frame to call world.frame_callback_w. // (It will be called 30 times per second.) // popengl_helper.display_cb_set(world.frame_callback_w,&world); // This point will only be reached when the program exits normally. }