/* $Id: draw.cc,v 1.8 2000/01/13 20:11:22 mac Exp $ */
/*
* glbiff -- A Mesa/OpenGL-based `xbiff' substitute
* Copyright (C) 2000 Maciej Kalisiak
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#include <time.h>
#include <iostream.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include "draw.h"
#include "glbiff.h"
#include "astro.h"
#include "rgb.h"
#include "cfg.h"
// some angles (in degrees)
const int ANG_FLAG_UP = 90;
const int ANG_FLAG_DOWN = 0;
const int ANG_DOOR_OPEN = 90;
const int ANG_DOOR_CLOSED = 0;
const int ANG_CAM_INI = 0; // INIt = "tilted/corner" view
const int ANG_CAM_FIN = 1; // FInal = head on box view
// some step sizes
const double STEP_FLAG = (ANG_FLAG_UP-ANG_FLAG_DOWN)
/(double)flag_up_frames;
const double STEP_DOOR = (ANG_DOOR_OPEN-ANG_DOOR_CLOSED)
/ (double)door_open_frames;
const double STEP_CAM = (ANG_CAM_FIN-ANG_CAM_INI)
/ (double)cam_swing_frames;
////////// globals
double flag_position=0;
double door_position=0;
double xform_factor=0;
bool fDoorOpen = false;
bool fLookHeadOn = false;
/*
* set_colour()
*
* generic routine to set current colour; uses some reasonable default
* values. Requires that you pass in the RGB of the desired colour.
*/
void set_colour(float r, float g, float b)
{
float ambient = 0.2;
float diffuse = 0.7;
float specular = 0.4;
GLfloat mat[4];
/**** set ambient lighting parameters ****/
mat[0] = ambient*r;
mat[1] = ambient*g;
mat[2] = ambient*b;
mat[3] = 1.0;
glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, mat);
/**** set diffuse lighting parameters ******/
mat[0] = diffuse*r;
mat[1] = diffuse*g;
mat[2] = diffuse*b;
mat[3] = 1.0;
glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, mat);
/**** set specular lighting parameters *****/
mat[0] = specular*r;
mat[1] = specular*g;
mat[2] = specular*b;
mat[3] = 1.0;
glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, mat);
glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, 0.5);
}
void draw_cube( double d )
{
double v = 0.5 * d;
// NOTE: the spurious braces are here so that XEmacs does nice indentation...
glBegin(GL_QUAD_STRIP); {
glVertex3f(-v,-v,v);
glVertex3f(-v,-v,-v);
glVertex3f(v,-v,v);
glVertex3f(v,-v,-v);
glVertex3f(v,v,v);
glVertex3f(v,v,-v);
glVertex3f(-v,v,v);
glVertex3f(-v,v,-v);
glVertex3f(-v,-v,v);
glVertex3f(-v,-v,-v);
}
glEnd();
glBegin(GL_QUADS); {
glVertex3f(-v,-v,v); // front face
glVertex3f(v,-v,v);
glVertex3f(v,v,v);
glVertex3f(-v,v,v);
glVertex3f(-v,v,-v); // back face
glVertex3f(v,v,-v);
glVertex3f(v,-v,-v);
glVertex3f(-v,-v,-v);
}
glEnd();
}
void outlined_cube( double d )
{
draw_cube( d );
set_colour(0,0,0);
double v = 0.5 * d;
glBegin(GL_LINE_LOOP); {
glVertex3f(-v,-v,-v);
glVertex3f(v,-v,-v);
glVertex3f(v,v,-v);
glVertex3f(-v,v,-v);
}
glEnd();
glBegin(GL_LINE_LOOP); {
glVertex3f(-v,-v,v);
glVertex3f(v,-v,v);
glVertex3f(v,v,v);
glVertex3f(-v,v,v);
}
glEnd();
glBegin(GL_LINES); {
glVertex3f(-v,-v,-v);
glVertex3f(-v,-v,v);
glVertex3f(-v,v,-v);
glVertex3f(-v,v,v);
glVertex3f(v,v,-v);
glVertex3f(v,v,v);
glVertex3f(v,-v,-v);
glVertex3f(v,-v,v);
}
glEnd();
}
/*
* draw_mbox_top()
*
* draws the Bezier patch which makes the top of mailbox
*/
void draw_mbox_top() {
glEnable(GL_MAP2_VERTEX_3);
glEvalMesh2(GL_FILL, 0, DOME_SEGS, 0, DOME_SEGS);
glDisable(GL_MAP2_VERTEX_3);
}
/*
* draw_mbox_side()
*
* draw a single side of a mailbox; (nx,ny,nz) is the normal vector for
* the side.
*/
void draw_mbox_side( float nx, float ny, float nz ) {
glBegin(GL_POLYGON);
glNormal3f(nx,ny,nz);
glVertex3f(0,0,0);
glVertex3f(0,0,SIDE_HEIGHT);
glVertex3f(0,1,SIDE_HEIGHT);
glVertex3f(0,1,0);
glEnd();
}
/*
* draw_mbox_door()
*
* draws the door, using a Bezier curve, among other things. This is used
* both for the front, and the back doors.
*/
void draw_mbox_door() {
// glTranslatef(-0.5,-0.5*1.6,0);
glEnable(GL_MAP1_VERTEX_3);
glPushMatrix();
glTranslatef(0,0,SIDE_HEIGHT);
glBegin(GL_POLYGON);
glNormal3f(0,-1,0);
glVertex3f(0,0,-SIDE_HEIGHT);
for( int i=0; i<=DOME_SEGS; i++ )
glEvalCoord1f(((float)i)/((GLfloat)DOME_SEGS));
glVertex3f(1,0,-SIDE_HEIGHT);
glEnd();
glPopMatrix();
glDisable(GL_MAP1_VERTEX_3);
}
/*
* draw_mbox_flag()
*
* draws the flag, taking 'flag_position' into account.
*/
void draw_mbox_flag() {
glPushMatrix();
glScalef(FLAG_SCALE,FLAG_SCALE,FLAG_SCALE*1.3);
glRotatef(90-flag_position,0,1,0);
glTranslatef(0.05,0,0.5);
glPushMatrix();
glScalef(0.1,0.05,1);
draw_cube(1.0);
glPopMatrix();
glPushMatrix();
glTranslatef(0.2,0,0.35);
glScalef(0.3,0.05,0.3);
draw_cube(1.0);
glPopMatrix();
glPopMatrix();
}
/*
* draw_env()
*
* draws a single small envelope: stands for 1 email
*/
void draw_env() {
set_colour(1,1,1);
glPushMatrix();
glRotatef(-10,0,1,0);
glTranslatef(0.01,0.5+0.1,0.2);
glScalef(0.02,1,0.4);
outlined_cube(1.0);
glPopMatrix();
}
/*
* draw_big_env()
*
* draws a bigger envelope: stands for 5 emails
*/
void draw_big_env() {
set_colour(0.8,0.6,0);
glPushMatrix();
glRotatef(-5,0,1,0);
glTranslatef(0.01,0.5+0.1,0.25);
glScalef(0.02,1,0.5);
outlined_cube(1.0);
glPopMatrix();
};
/*
* draw_box()
*
* draws a small package: stands for 25 emails
*/
void draw_box() {
set_colour(0.4,0.3,0.1);
glPushMatrix();
glTranslatef(0.2,0.5+0.1,0.25);
glScalef(0.4,1,0.5);
outlined_cube(1.0);
glPopMatrix();
};
/*
* draw_big_box()
*
* draws a big package: stands for 50 emails
*/
void draw_big_box() {
set_colour(0.5,0.5,0.1);
glPushMatrix();
glTranslatef(0.2,0.5+0.1,0.37);
glScalef(0.4,1,0.74);
outlined_cube(1.0);
glPopMatrix();
};
/*
* draw_mail()
*
* draws the representation of 'mail_count' emails in the mailbox
*/
void draw_mail() {
// determine number of each object
int bbox, box, benv, env;
if( mail_count>=MAX_MAIL_DRAWABLE ) {
bbox=2;
box=benv=env=0;
} else {
int mc=mail_count;
bbox=mc/50;
mc-=bbox*50;
box=mc/25;
mc-=box*25;
benv=mc/5;
mc-=benv*5;
env=mc;
}
bool fLL, fLR, fUL, fUR;
fLL=fLR=fUL=fUR=true;
if( bbox ) {
glPushMatrix();
glTranslatef(0.09,0,0);
draw_big_box();
glPopMatrix();
fLL=fUL=false;
if( bbox==2 ) {
glPushMatrix();
glTranslatef(0.51,0,0);
draw_big_box();
glPopMatrix();
fLR=fUR=false;
}
}
if( box ) {
glPushMatrix();
if( fLL ) {
glTranslatef(0.02,0,0);
fLL=false;
} else {
glTranslatef(0.52,0,0);
fLR=false;
}
draw_box();
glPopMatrix();
}
if( benv || env ) {
glPushMatrix();
if( fLL ) {
fLL=false;
} else if( fLR ) {
glTranslatef(0.5,0,0);
fLR=false;
} else if( fUR ) {
glTranslatef(0.5,0,0.5);
fUR=false;
}
for( int i=0; i<benv; i++ ) {
glTranslatef( 0.05,0,0 );
draw_big_env();
}
for( int i=0; i<env; i++ ) {
glTranslatef( 0.05,0,0 );
draw_env();
}
glPopMatrix();
}
}
/*
* draw_mbox()
*
* draws the complete mailbox; post, flag and contained email included
*/
void draw_mbox() {
set_colour(0.5,0.5,0.5);
/* scale the whole box, w/o flag or front door */
glPushMatrix();
glTranslatef(-0.5,-0.5*1.6,0); // center the box along x and y
glScalef(1,1.6,1);
/* draw the top */
glPushMatrix();
glTranslatef(0,0,SIDE_HEIGHT);
draw_mbox_top();
glPopMatrix();
draw_mbox_side( -1, 0, 0 );
glPushMatrix();
glTranslatef(1,0,0);
draw_mbox_side( 1, 0, 0 );
glPopMatrix();
/* draw the bottom */
glBegin(GL_POLYGON);
glNormal3f(0,0,1);
glVertex3f(0,0,0);
glVertex3f(1,0,0);
glVertex3f(1,1,0);
glVertex3f(0,1,0);
glEnd();
// draw the back door
glPushMatrix();
glTranslatef(0,1,0);
draw_mbox_door();
glPopMatrix();
glPopMatrix(); /* end of box w/o flag, front door xform */
// draw the front door
glPushMatrix();
glTranslatef(-0.5,-0.5*1.6,0); // center the box along x and y
glRotatef(door_position,1,0,0);
draw_mbox_door();
glPopMatrix();
// draw the flag in red
set_colour(1.0,0,0);
glPushMatrix();
glTranslatef(-0.5,-0.5*1.6,0); // center the box along x and y
glScalef(1.0,1.2,1.0);
glTranslatef(1.1,0.2,0.5);
glRotatef(90,0,0,1);
draw_mbox_flag();
glPopMatrix();
// draw the mail count
glPushMatrix();
glTranslatef(-0.5,-0.5*1.6,0); // center the box along x and y
draw_mail();
glPopMatrix();
}
/*
* draw_land()
*
* draws the square which represents the ground
*/
void draw_land() {
// texture stuff (enable)
glEnable(GL_TEXTURE_2D);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glBindTexture(GL_TEXTURE_2D, texName);
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST); // correct perspective
glPushMatrix();
glRotatef( 45, 0, 0, 1 );
set_colour(0.0,0.6,0.0);
glBegin(GL_POLYGON);
glNormal3f(0,0,1);
glTexCoord2f(0,0); glVertex3f(-LAND_WIDTH/2,-LAND_WIDTH/2,-3);
glTexCoord2f(0,3); glVertex3f(-LAND_WIDTH/2,LAND_WIDTH/2,-3);
glTexCoord2f(3,3); glVertex3f(LAND_WIDTH/2,LAND_WIDTH/2,-3);
glTexCoord2f(3,0); glVertex3f(LAND_WIDTH/2,-LAND_WIDTH/2,-3);
glEnd();
glPopMatrix();
glFlush();
glDisable(GL_TEXTURE_2D);
}
/*
* draw_post()
*
* draws the post on which the mailbox is sitting
*/
void draw_post() {
set_colour(0.4,0.4,0);
glPushMatrix();
glTranslatef(-0.5,-0.5*1.6,0); // the whole box moved to be centered on x,y
glTranslatef(0.5,0.5*1.6,-1.51);
glScalef(0.2,0.2,3);
draw_cube(1.0);
glPopMatrix();
}
void set_sky_colour(void) {
// get time and date
time_t tt = time( NULL );
tm* ptm = localtime( &tt );
Date today(ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday);
double hour = ptm->tm_hour + ptm->tm_min/60.0 + ptm->tm_sec/3600.0;
// static double hour = 7;
// hour += 0.05;
// if( hour > 24 )
// hour -= 24;
coord rs = sun_rise_set( today, my_long_lat );
// cerr << "rs == " << rs.a << ", " << rs.b << endl;
// cerr << "time is " << ptm->tm_hour << ":" << ptm->tm_min << ":"
// << ptm->tm_sec << " ; tm_isdst == " << ptm->tm_isdst << endl;
// cerr << "hour is " << hour << endl;
if( hour <= rs.a || hour >= rs.b )
glClearColor(0,0,0,1.0); // pitch dark
else {
double s = (hour - rs.a)/(rs.b-rs.a);
list<Rgb_kf>::iterator it = sky_keyframes.begin();
list<Rgb_kf>::iterator it_after;
while( (*it).s < s )
++it;
it_after = it;
--it;
double f = 1 - (s - (*it).s)/
((*it_after).s-(*it).s);
// cerr << "i == " << i << " : s == " << s << " : f == " << f << endl;
glClearColor( (*it).rgb.r*f + (*it_after).rgb.r*(1-f),
(*it).rgb.g*f + (*it_after).rgb.g*(1-f),
(*it).rgb.b*f + (*it_after).rgb.b*(1-f),
1.0 );
}
}
/*
* redraw()
*
* this is the callback that handles drawing of the OpenGL window
*/
void redraw( Display *dpy, Window w )
{
if( !dpy || !w )
return;
bool call_me_again = false;
// "paint" the sky
set_sky_colour();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// setup the viewing transformation (assume the models are already setup)
glPushMatrix();
// --camera/viewing transformations
// read these backwards using the camera local coordinate system
// (or forwards using the "Grand, Fixed Coordinate System")
glTranslatef( 0,0,-3.0+1.0*xform_factor ); // dolly out a bit
glRotatef( -90+35-35.0*xform_factor,1,0,0 );
glRotatef( -45+45*xform_factor,0,0,1 ); // controls rot along z
glTranslatef( 0,0,-0.25-0.25*xform_factor ); // move camera to the vertical center of box
// glScalef(2,2,2);
// paint the origin as a cube
// draw_cube(0.1);
/////// perform any motions necessary for this frame
// check the flag, ...
if( unreadmail && flag_position<ANG_FLAG_UP ) {
flag_position += STEP_FLAG;
call_me_again = true;
}
if( !unreadmail && flag_position>ANG_FLAG_DOWN ) {
flag_position -= STEP_FLAG;
call_me_again = true;
}
// door, ...
if( fDoorOpen && door_position<ANG_DOOR_OPEN ) {
door_position += STEP_DOOR;
call_me_again = true;
}
if( !fDoorOpen && door_position>ANG_DOOR_CLOSED ) {
door_position -= STEP_DOOR;
call_me_again = true;
}
// and camera swing
if( fLookHeadOn && xform_factor<ANG_CAM_FIN ) {
xform_factor += STEP_CAM;
call_me_again = true;
}
if( !fLookHeadOn && xform_factor>ANG_CAM_INI ) {
xform_factor -= STEP_CAM;
call_me_again = true;
}
// render the land and mailbox
draw_land();
draw_post();
draw_mbox();
// clean up and swap buffers
glPopMatrix();
glFinish();
glXSwapBuffers(dpy, w);
// setup a callback if any motions are not yet finished
if(call_me_again) {
timed_callback( refresh, refresh_period );
}
}
////////////////////////////////////////////////////////////
// texturing stuff
GLubyte check_image[check_image_h][check_image_w][4];
GLuint texName;
void make_check_image(void) {
int i, j, c;
for( i=0; i<check_image_h; i++ ) {
for( j=0; j<check_image_w; j++ ) {
c = ((((i&0x8)==0)^((j&0x8))==0))*55+200;
check_image[i][j][0] = c;
check_image[i][j][1] = c;
check_image[i][j][2] = c;
check_image[i][j][3] = 255;
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1