Skip to content

Commit

Permalink
rebuild the head demo in the reorganized framework
Browse files Browse the repository at this point in the history
  • Loading branch information
TTimo committed Jul 4, 2013
1 parent 9fa60d0 commit 385614a
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 153 deletions.
16 changes: 11 additions & 5 deletions SConstruct
Expand Up @@ -44,12 +44,18 @@ else:

template_env = env.Clone()
template_env.Append( CPPPATH = [ 'template_src' ] )
template = template_env.Program( 'template', source + [ 'template_src/game.cpp', 'template_src/render.cpp' ] )
template_env.VariantDir( 'build/template_src', '.' )
template = template_env.Program( 'template', [ os.path.join( 'build/template_src', s ) for s in source + [ 'template_src/game.cpp', 'template_src/render.cpp' ] ] )

#core = env.Program( 'core', source + [ 'input_test/game.cpp', 'input_test/render.cpp' ] )
#
#env.Append( CPPPATH = [ 'mesh_loader' ] )
#mesh = env.Program( 'mesh', source + [ 'mesh_loader/game.cpp', 'mesh_loader/render.cpp' ] )
scene_env = env.Clone()
scene_env.Append( CPPPATH = [ 'scene_load_src' ] )
scene_env.VariantDir( 'build/scene_load_src', '.' )
scene = scene_env.Program( 'scene', [ os.path.join( 'build/scene_load_src', s ) for s in source + [ 'scene_load_src/game.cpp', 'scene_load_src/render.cpp' ] ] )

head_env = env.Clone()
head_env.Append( CPPPATH = [ 'head_src' ] )
head_env.VariantDir( 'build/head_src', '.' )
head = head_env.Program( 'head', [ os.path.join( 'build/head_src', s ) for s in source + [ 'head_src/game.cpp', 'head_src/render.cpp' ] ] )

# FIXME: the OSX build and binary distribution strategy needs redone
#if ( system == 'Darwin' ):
Expand Down
3 changes: 0 additions & 3 deletions game_main.cpp
Expand Up @@ -45,9 +45,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "shared_render_state.h"
#include "game.h"

const int GAME_DELAY = 16;
const float GAME_TICK_FLOAT = (float)GAME_DELAY / 1000.0f;

int game_thread( void * _parms ) {
GameThreadParms * parms = (GameThreadParms*)_parms;
GameThreadSockets gsockets;
Expand Down
3 changes: 3 additions & 0 deletions game_main.h
Expand Up @@ -28,6 +28,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef _GAME_MAIN_H_
#define _GAME_MAIN_H_

const int GAME_DELAY = 16;
const float GAME_TICK_FLOAT = (float)GAME_DELAY / 1000.0f;

typedef struct GameThreadParms_s {
zctx_t * zmq_context;
} GameThreadParms;
Expand Down
83 changes: 32 additions & 51 deletions input_test/game.cpp → head_src/game.cpp
Expand Up @@ -25,34 +25,18 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

const int ORIENTATION_LOG = 10;

typedef struct OrientationHistory_s {
Uint32 t;
Ogre::Quaternion o;
} OrientationHistory;

// only visible to the game logic
typedef struct GameState_s {
void * zmq_control_socket;
void * zmq_input_req;
void * zmq_render_socket;

float bounce; // limits of the bounce area
float speed; // picked a speed to bounce around at startup
bool mouse_pressed; // go from mouse is pressed to click each time to change the control scheme
Ogre::Vector2 direction; // direction the head is moving on the plance
Ogre::Vector3 rotation; // rotation axis of the head
Ogre::Degree rotation_speed; // rotation speed of the head

// use the last few frames of mouse input to build a smoothed angular velocity
unsigned int orientation_index;
OrientationHistory orientation_history[ ORIENTATION_LOG ];
Ogre::Vector3 smoothed_angular;
Ogre::Degree smoothed_angular_velocity;
} GameState;

void parse_mouse_state( char * mouse_state, Ogre::Quaternion & orientation, Uint8 & buttons ) {
#include "SDL.h"

#include "OgreRoot.h"

#include "czmq.h"

#include "../game_main.h"

#include "shared_render_state.h"
#include "game.h"

void parse_mouse_state( char * mouse_state, Ogre::Quaternion & orientation, uint8_t & buttons ) {
Ogre::Vector3 rotation_vector; // skipped over
char * start = mouse_state;
char * end = strchr( start, ' ' );
Expand All @@ -75,9 +59,6 @@ void parse_mouse_state( char * mouse_state, Ogre::Quaternion & orientation, Uint
}

void game_init( GameState & gs, SharedRenderState & rs ) {
gs.zmq_control_socket = NULL;
gs.zmq_input_req = NULL;
gs.zmq_render_socket = NULL;
time_t now;
time( &now );
srandom( now );
Expand All @@ -94,11 +75,11 @@ void game_init( GameState & gs, SharedRenderState & rs ) {
memset( gs.orientation_history, 0, sizeof( gs.orientation_history ) );
}

void game_tick( unsigned int now, GameState & gs, SharedRenderState & rs ) {
void game_tick( GameThreadSockets & gsockets, GameState & gs, SharedRenderState & srs, unsigned int now ) {
// get the latest mouse buttons state and orientation
zstr_send( gs.zmq_input_req, "mouse_state" );
char * mouse_state = zstr_recv( gs.zmq_input_req );
Uint8 buttons;
zstr_send( gsockets.zmq_input_req, "mouse_state" );
char * mouse_state = zstr_recv( gsockets.zmq_input_req );
uint8_t buttons;
Ogre::Quaternion orientation;
parse_mouse_state( mouse_state, orientation, buttons );
free( mouse_state );
Expand All @@ -116,22 +97,22 @@ void game_tick( unsigned int now, GameState & gs, SharedRenderState & rs ) {
// NOTE: uncomment the following line to use the full history, notice the 'flip' happens at much lower speed
q1_index = ( q1_index + ORIENTATION_LOG - 2 ) % ORIENTATION_LOG;
Ogre::Quaternion q1 = gs.orientation_history[ q1_index ].o;
Uint32 q1_t = gs.orientation_history[ q1_index ].t;
uint32_t q1_t = gs.orientation_history[ q1_index ].t;
Ogre::Quaternion omega = 2.0f * ( orientation - q1 ) * q1.UnitInverse() * ( 1000.0f / (float)( now - q1_t ) );
omega.ToAngleAxis( gs.smoothed_angular_velocity, gs.smoothed_angular );
// printf( "%f %f %f - %f\n", gs.smoothed_angular.x, gs.smoothed_angular.y, gs.smoothed_angular.z, gs.smoothed_angular_velocity.valueDegrees() );
rs.smoothed_angular = gs.smoothed_angular;
srs.smoothed_angular = gs.smoothed_angular;

if ( ( buttons & SDL_BUTTON( 1 ) ) != 0 ) {
if ( !gs.mouse_pressed ) {
gs.mouse_pressed = true;
// changing the control scheme: the player is now driving the orientation of the head directly with the mouse
// tell the input logic to reset the orientation to match the current orientation of the head
zstr_sendf( gs.zmq_input_req, "mouse_reset %f %f %f %f", rs.orientation.w, rs.orientation.x, rs.orientation.y, rs.orientation.z );
zstr_recv( gs.zmq_input_req ); // wait for ack from input
zstr_sendf( gsockets.zmq_input_req, "mouse_reset %f %f %f %f", srs.orientation.w, srs.orientation.x, srs.orientation.y, srs.orientation.z );
zstr_recv( gsockets.zmq_input_req ); // wait for ack from input
// IF RENDER TICK HAPPENS HERE: render will not know that it should grab the orientation directly from the mouse,
// but the orientation coming from game should still be ok?
zstr_sendf( gs.zmq_render_socket, "# %s", "1" );
zstr_sendf( gsockets.zmq_render_socket, "# %s", "1" );
// IF RENDER TICK HAPPENS HERE (before a new gamestate):
// the now reset input orientation will combine with the old game state, that's bad
}
Expand All @@ -141,25 +122,25 @@ void game_tick( unsigned int now, GameState & gs, SharedRenderState & rs ) {
// changing the control scheme: the head will free spin and slow down for a bit, then it will resume bouncing around
// the player looses mouse control, the game grabs latest orientation and angular velocity
// the input thread was authoritative on orientation until now, so accept that as our starting orientation
rs.orientation = orientation;
srs.orientation = orientation;
gs.rotation_speed = gs.smoothed_angular_velocity;
gs.rotation = gs.smoothed_angular;
zstr_sendf( gs.zmq_render_socket, "# %s", "0" );
zstr_sendf( gsockets.zmq_render_socket, "# %s", "0" );
// IF RENDER TICK HAPPENS HERE (before a new gamestate): render will pull the head orientation from the game state rather than input, but game state won't have the fixed orientation yet
}
}

if ( rs.position.x > gs.bounce || rs.position.x < -gs.bounce ) {
if ( srs.position.x > gs.bounce || srs.position.x < -gs.bounce ) {
gs.direction.x *= -1.0f;
}
if ( rs.position.y > gs.bounce || rs.position.y < -gs.bounce ) {
if ( srs.position.y > gs.bounce || srs.position.y < -gs.bounce ) {
gs.direction.y *= -1.0f;
}
Ogre::Vector2 delta = gs.speed * ( (float)GAME_DELAY / 1000.0f ) * gs.direction;
if ( !gs.mouse_pressed ) {
if ( gs.rotation_speed.valueDegrees() == 0.0f ) {
rs.position.x += delta.x;
rs.position.y += delta.y;
srs.position.x += delta.x;
srs.position.y += delta.y;
}
// printf( "game tick position: %f %f\n", rs.position.x, rs.position.y );

Expand All @@ -177,15 +158,15 @@ void game_tick( unsigned int now, GameState & gs, SharedRenderState & rs ) {
factor * gs.rotation.x,
factor * gs.rotation.y,
factor * gs.rotation.z );
rs.orientation = rotation_tick * rs.orientation;
srs.orientation = rotation_tick * srs.orientation;
} else {
// keep updating the orientation in the render state, even while the render thread is ignoring it:
// when the game thread resumes control of the head orientation, it will interpolate from one of these states,
// so we keep updating the orientation to avoid a short glitch at the discontinuity
rs.orientation = orientation;
srs.orientation = orientation;
}
}

// emit render state ..
zstr_sendf( gsockets.zmq_render_socket, "%d %f %f %f %f %f %f %f %f %f", baseline + framenum * GAME_DELAY, rs.position.x, rs.position.y, rs.orientation.w, rs.orientation.x, rs.orientation.y, rs.orientation.z, rs.smoothed_angular.x, rs.smoothed_angular.y, rs.smoothed_angular.z );

void emit_render_state( void * socket, unsigned int time, SharedRenderState & srs ) {
zstr_sendf( socket, "%d %f %f %f %f %f %f %f %f %f", time, srs.position.x, srs.position.y, srs.orientation.w, srs.orientation.x, srs.orientation.y, srs.orientation.z, srs.smoothed_angular.x, srs.smoothed_angular.y, srs.smoothed_angular.z );
}
162 changes: 72 additions & 90 deletions input_test/render.cpp → head_src/render.cpp
Expand Up @@ -25,13 +25,22 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// support functions, internal render state
#include "SDL.h"

typedef struct RenderState_s {
bool mouse_control;
} RenderState;
#include "OgreRoot.h"
#include "OgreRenderWindow.h"
#include "OgreViewport.h"
#include "OgreEntity.h"
#include "OgreManualObject.h"

void parse_mouse_state( char * mouse_state, Ogre::Quaternion & orientation ) { //, Ogre::Vector3 & rotation_vector ) {
#include "czmq.h"

#include "../render_main.h"

#include "shared_render_state.h"
#include "render.h"

void parse_mouse_state( char * mouse_state, Ogre::Quaternion & orientation ) {
char * start = mouse_state;
char * end = strchr( start, ' ' );
end[0] = '\0';
Expand All @@ -51,7 +60,7 @@ void parse_mouse_state( char * mouse_state, Ogre::Quaternion & orientation ) { /
// NOTE: skipping the button state
}

void parse_render_state( char * render_state, int & tick_time, float & x, float & y, Ogre::Quaternion & orientation, Ogre::Vector3 & smoothed_angular ) {
void _parse_render_state( char * render_state, unsigned int & tick_time, float & x, float & y, Ogre::Quaternion & orientation, Ogre::Vector3 & smoothed_angular ) {
char * start = render_state;
char * end = strchr( start, ' ' );
end[0] = '\0';
Expand Down Expand Up @@ -92,8 +101,7 @@ void parse_render_state( char * render_state, int & tick_time, float & x, float
smoothed_angular.z = atof( start );
}

// init and scene setup block

void render_init( RenderThreadParms * parms, RenderState & rs, SharedRenderState & srs ) {
rs.mouse_control = false;

// create a test scene
Expand All @@ -109,8 +117,8 @@ void parse_render_state( char * render_state, int & tick_time, float & x, float
Ogre::SceneManager * scene = parms->root->createSceneManager( Ogre::ST_GENERIC, "SimpleStaticCheck" );
scene->setAmbientLight( Ogre::ColourValue( 0.5f, 0.5f, 0.5f ) );
Ogre::Entity * head = scene->createEntity( "head", "ogrehead.mesh" );
Ogre::SceneNode * head_node = scene->getRootSceneNode()->createChildSceneNode( "head_node" );
head_node->attachObject( head );
rs.head_node = scene->getRootSceneNode()->createChildSceneNode( "head_node" );
rs.head_node->attachObject( head );

Ogre::Light * light = scene->createLight( "light" );
light->setPosition( 20.0f, 80.0f, 50.0f );
Expand All @@ -124,85 +132,59 @@ void parse_render_state( char * render_state, int & tick_time, float & x, float
viewport->setBackgroundColour( Ogre::ColourValue( 0, 0, 0 ) );
camera->setAspectRatio( Ogre::Real( viewport->getActualWidth() ) / Ogre::Real( viewport->getActualHeight() ) );

Ogre::ManualObject * rotation_vector_obj = scene->createManualObject( "rotation_vector" );
rotation_vector_obj->setDynamic( true );
rotation_vector_obj->begin( "BaseWhiteNoLighting", Ogre::RenderOperation::OT_LINE_LIST );
rotation_vector_obj->position( 0.0f, 0.0f, 0.0f );
rotation_vector_obj->position( 0.0f, 0.0f, 0.0f );
rotation_vector_obj->end();
Ogre::SceneNode * rotation_vector_node = scene->getRootSceneNode()->createChildSceneNode( "rotation_vector_node" );
rotation_vector_node->attachObject( rotation_vector_obj );
rotation_vector_node->setVisible( false );

// parse render state

if ( game_tick[0] == '#' ) {
// using '#' to decorate the head tracking trigger
if ( game_tick[2] == '0' ) {
rs.mouse_control = false;
rotation_vector_node->setVisible( false );
} else {
assert( game_tick[2] == '1' );
rs.mouse_control = true;
// NOTE: uncomment to visualize the rotation vector
// rotation_vector_node->setVisible( true );
}
// wait for the next game state before continuing so we don't do renders with an inconsistent state
free( game_tick );
game_tick = zstr_recv( zmq_game_socket );
}

int tick_time;
float x, y;
Ogre::Quaternion orientation;
Ogre::Vector3 smoothed_angular;
parse_render_state( game_tick, tick_time, x, y, orientation, smoothed_angular );
//printf( "render state head orientation: %f %f %f %f\n", orientation.w, orientation.x, orientation.y, orientation.z );
free( game_tick );
if ( previous_game_time == 0 ) {
previous_game_time = tick_time;
previous_render.position.x = x;
previous_render.position.y = y;
previous_render.position.z = 0.0f;
previous_render.orientation = orientation;
previous_render.smoothed_angular = smoothed_angular;
} else {
if ( next_game_time != 0 ) {
previous_game_time = next_game_time;
previous_render = next_render;
}
next_game_time = tick_time;
next_render.position.x = x;
next_render.position.y = y;
next_render.position.z = 0.0f;
next_render.orientation = orientation;
next_render.smoothed_angular = smoothed_angular;
}

// interpolate the render state


interpolated_render.position = ( 1.0f - ratio ) * previous_render.position + ratio * next_render.position;
interpolated_render.orientation = Ogre::Quaternion::Slerp( ratio, previous_render.orientation, next_render.orientation );
interpolated_render.smoothed_angular = ( 1.0f - ratio ) * previous_render.smoothed_angular + ratio * next_render.smoothed_angular;
head_node->setPosition( interpolated_render.position );
if ( rs.mouse_control ) {
zstr_send( zmq_input_req, "mouse_state" );
char * mouse_state = zstr_recv( zmq_input_req );
Ogre::Quaternion orientation;
parse_mouse_state( mouse_state, orientation );

// orient the head according to latest input data
head_node->setOrientation( orientation );

// update the rotation axis of the head (smoothed over a few frames in the game thread)
rotation_vector_obj->beginUpdate( 0 );
rotation_vector_obj->position( interpolated_render.position );
Ogre::Vector3 rotation_vector_end = interpolated_render.position + 40.0f * interpolated_render.smoothed_angular;
rotation_vector_obj->position( rotation_vector_end );
rotation_vector_obj->end();

free( mouse_state );
rs.rotation_vector_obj = scene->createManualObject( "rotation_vector" );
rs.rotation_vector_obj->setDynamic( true );
rs.rotation_vector_obj->begin( "BaseWhiteNoLighting", Ogre::RenderOperation::OT_LINE_LIST );
rs.rotation_vector_obj->position( 0.0f, 0.0f, 0.0f );
rs.rotation_vector_obj->position( 0.0f, 0.0f, 0.0f );
rs.rotation_vector_obj->end();
rs.rotation_vector_node = scene->getRootSceneNode()->createChildSceneNode( "rotation_vector_node" );
rs.rotation_vector_node->attachObject( rs.rotation_vector_obj );
rs.rotation_vector_node->setVisible( false );
}

void parse_render_state( RenderState & rs, SharedRenderState & srs, char * game_tick ) {
if ( game_tick[0] == '#' ) {
// using '#' to decorate the head tracking trigger
if ( game_tick[2] == '0' ) {
rs.mouse_control = false;
rs.rotation_vector_node->setVisible( false );
} else {
head_node->setOrientation( interpolated_render.orientation );
assert( game_tick[2] == '1' );
rs.mouse_control = true;
// NOTE: uncomment to visualize the rotation vector
rs.rotation_vector_node->setVisible( true );
}
// resume updating render state on the next loop
return;
}

float x, y;
_parse_render_state( game_tick, srs.game_time, x, y, srs.orientation, srs.smoothed_angular );
srs.position.x = x;
srs.position.y = y;
srs.position.z = 0.0f;
}

void interpolate_and_render( RenderThreadSockets & rsockets, RenderState & rs, float ratio, SharedRenderState & previous_render, SharedRenderState & next_render ) {
Ogre::Vector3 interp_position = ( 1.0f - ratio ) * previous_render.position + ratio * next_render.position;
rs.head_node->setPosition( interp_position );
if ( rs.mouse_control ) {
zstr_send( rsockets.zmq_input_req, "mouse_state" );
char * mouse_state = zstr_recv( rsockets.zmq_input_req );
Ogre::Quaternion orientation;
parse_mouse_state( mouse_state, orientation );
free( mouse_state );
// use latest mouse data to orient the head
rs.head_node->setOrientation( orientation );
// update the rotation axis of the head (smoothed over a few frames in the game thread)
rs.rotation_vector_obj->beginUpdate( 0 );
rs.rotation_vector_obj->position( interp_position );
Ogre::Vector3 interp_smoothed_angular = ( 1.0f - ratio ) * previous_render.smoothed_angular + ratio * next_render.smoothed_angular;
Ogre::Vector3 rotation_vector_end = interp_position + 40.0f * interp_smoothed_angular;
rs.rotation_vector_obj->position( rotation_vector_end );
rs.rotation_vector_obj->end();
} else {
rs.head_node->setOrientation( Ogre::Quaternion::Slerp( ratio, previous_render.orientation, next_render.orientation ) );
}
}

0 comments on commit 385614a

Please sign in to comment.