Sunday, November 23, 2008

Well I've started to write some Processing code for Chapter 33. I'm thinking that it might be useful to the reader to have a template project that sets up a 3-d environment ready for experimenting with. This is my first stab at it.

The code draws a grid, marks the origin and draws the x, y and z-axis. It also sets up some lighting. The view can be rotated using the cursor keys but mouse control would be much better... I'll add that in tomorrow. Code needs tidying up too. One thing that might be a bit confusing is that Processing uses a left hand coordinate system whereas OpenGL uses a right hand system. I've added a reflection transform so that the scene uses a right hand system, but I'm wondering if this just confuses things and I should really just stick with Processing's choice.

Here's the code in its rough state:-






import processing.opengl.*;

//
// Spherical coordiantes of camera.
//
float cameraR = 100.0 ;
float cameraPhi = PI / 4 ;
float cameraTheta = 0.0 ;


void setup()
{
//
// Enable OpenGL mode.
//
size(800, 600, OPENGL ) ;


colorMode( RGB, 1 ) ;

}

/**
* Handle a keyboard key press.
* At the moment, cursor keys control the camera.
*/
void keyPressed()
{
if ( key == CODED )
switch( keyCode )
{
case UP :
cameraPhi = cameraPhi > 0.1 ? cameraPhi - 0.01 : 0.1 ;
break ;

case DOWN :
cameraPhi = cameraPhi < PI / 4 - 0.1 ? cameraPhi + 0.01 : PI / 4 - 0.1 ;
break ;

case RIGHT :
cameraTheta = cameraTheta + 0.02 ;
break ;

case LEFT :
cameraTheta = cameraTheta - 0.02 ;
break ;
}

cameraTheta = cameraTheta < 2*PI ? cameraTheta : cameraTheta - 2*PI ;

}

/**
* Draw the scene.
*/
void draw()
{
//
// Initialise the view.
//
background( 0.0 ) ;

setCameraSpherical( cameraR, cameraPhi, -cameraTheta ) ;

//
// Reference page states that in OpenGL mode smooth defaults to on, but that's not the case!
//
smooth() ;

//
// Processing seems to use a left hand coordinate system.
// I prefer right as this is the norm for OpenGL.
//
scale( 1.0, -1.0, 1.0 ) ;

//
// Set up the lighting for the scene.
// This is done early on so that the lighting is fixed relative to the scene.
//
setupLights() ;

//
// Draw the coordinate centre and axis, and a grid.
// This will help when trying to work out the effect of transformations etc.
//
drawAxis( 10.0 ) ;
drawXYGrid( 100.0, 10, 4 ) ;




// cameraTheta += 0.01 ;
}

void setupLights()
{
ambientLight( 0.1, 0.1, 0.1 ) ;
directionalLight( 51.0, 102.0, 126.0, -1.0, -1.0, -1.0 ) ;
}

/**
* Set up the camera using spherical coordinates.
* @param r the distance from the pov to the camera
* @param phi the polar angle with the z-axis
* @param theta the azimuthal angle in the x/y-plane
*/
void setCameraSpherical( float r, float phi, float theta )
{
//
// Work out the x/y-z-coordinates of the camera.
//
float x = r * cos( theta )*sin( phi ) ;
float y = r * sin( theta ) * sin( phi ) ;
float z = r * cos( phi ) ;

//
// Need to work out the up vector.
// This is simply the image of the point ( 1, 0, 0 ) under the
// the given spherical map.
//
float nx = cos( theta ) * cos( phi ) ;
float ny = sin( theta ) * cos( phi ) ;
float nz = - sin( phi ) ;

beginCamera() ;
camera( x, y, z, 0.0, 0.0, 0.0, nx, ny, nz ) ;
endCamera() ;
}

/**
* Draw the unit x, y, z vectors.
*/

void drawAxis( float length )
{
pushMatrix();

//
// Highlight the origin with a white sphere.
//
noStroke() ;
fill( 1.0, 1.0, 1.0 ) ;
sphere( 1.5 ) ;

//
// Draw the x, y and z axis.
// The axis are actually drawn as unit length lines
// and we make use of a scaling transform to get them to the desired length.
//
scale( length, length, length ) ;


strokeWeight( 2 ) ;

beginShape( LINES ) ;

//
// x-axis in red.
//
stroke( 1.0, 0.0, 0.0 ) ;
vertex( 0.0, 0.0, 0.0 ) ;
vertex( 1.0, 0.0, 0.0 ) ;

//
// y-axis in green.
//
stroke( 0.0, 1.0, 0.0 ) ;
vertex( 0.0, 0.0, 0.0 ) ;
vertex( 0.0, 1.0, 0.0 ) ;

//
// z-axis in blue.
//
stroke( 0.0, 0.0, 1.0 ) ;
vertex( 0.0, 0.0, 0.0 ) ;
vertex( 0.0, 0.0, 1.0 ) ;

endShape() ;

popMatrix() ;

}

/**
* Draw a grid in the x-y plane.
*/
void drawXYGrid( float scale, int divisions, int minorDivisions )
{

pushMatrix();

scale( scale, scale, 0 ) ;

translate( -0.5, -0.5, 0 ) ;

noFill() ;
strokeWeight( 1 ) ;

float dw = 1.0 / divisions ;
float dm = dw / minorDivisions ;

beginShape( LINES ) ;

for ( int i = 0 ; i <= divisions ; ++i )
{
//
// Draw the minor divisions
//
strokeWeight( 0.5 ) ;
stroke( 0.6, 0.6, 0.6 ) ;

if ( i < divisions )
for ( int j = 1 ; j < minorDivisions ; ++j )
{
vertex( 0, i*dw + j*dm, 0 ) ;
vertex( 1, i*dw + j*dm, 0 ) ;
vertex( i*dw + j*dm, 0, 0 ) ;
vertex( i*dw + j*dm, 1, 0 ) ;
}

if ( i == divisions / 2 )
stroke( 0.9, 0.9, 0.9 ) ;
else
stroke( 0.8, 0.8, 0.8 ) ;

strokeWeight( 1.0 ) ;
vertex( 0, i*dw, 0 ) ;
vertex( 1, i*dw, 0 ) ;
vertex( i*dw, 0, 0 ) ;
vertex( i*dw, 1, 0 ) ;
}

endShape() ;


popMatrix();
}

3 comments:

monkstone said...

I also found the left-hand coordinate system to be confusing, however think adding the translation is more confusing. I have argued previously against working in degrees, and then making an unnecessary translation to radians. I think such conventions/usages should be made clear at the beginning of the book.

monkstone said...

Darrel does cover the weird coordinate system of processing/java in Fragment 24, it is is something might need to reiterated throughout the book

lazydog said...

Thanks Martin. I am going to have to dig in a bit deeper anyway as I've been playing around with calling OpenGL functions from within Processing and I'm getting odd behaviour because of the mixture of coordinate systems. Definitely going to lump it and stick with the one, ie Processing's left hand system.