/*
* Solar.cpp
用于演示变换和坐标系
画了一个简单的太阳系,包括一个太阳,一个地球和一个月球
使用说明:
* 按"r"键来启动和停止动画
* 按"s"键单步执行动画
* 向上和向下箭头用于控制动画中每一帧的时间间隔,每次按键时间间隔乘2或除2
* 按ESC键退出)
*/
#include <stdlib.h>
#include <GL/glut.h>
GLenum spinMode = GL_TRUE; // 控制动画运行和暂停
GLenum singleStep = GL_FALSE; // 控制单步执行模式开启和关闭
// 这3个变量控制动画的状态和速度
float HourOfDay = 0.0; // 一天中的小时数,初始为0
float DayOfYear = 0.0; // 一年中的天数,初始为0
float AnimateIncrement = 12.0; // 动画步长变量,表示时间间隔, 以小时为单位
float HourOfDayX=0.0;
float DayOfYearX=0.0;
// 显示回调函数
void Animate(void)
{
// 用背景色清空颜色缓存,将深度缓存恢复为初始值
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_NORMALIZE); //归一化法向量
if (spinMode) { // 如果动画开启
// 更新动画状态
HourOfDay += AnimateIncrement;
DayOfYear += AnimateIncrement/24.0;
HourOfDayX+=AnimateIncrement;
DayOfYearX+=AnimateIncrement/36.0;
// 防止溢出
HourOfDay = HourOfDay - ((int)(HourOfDay/24))*24;
DayOfYear = DayOfYear - ((int)(DayOfYear/365))*365;
HourOfDayX=HourOfDayX-((int)(HourOfDayX/36))*36;
DayOfYearX=DayOfYearX-((int)(DayOfYearX/300))*300;
}
// 将当前矩阵(模-视矩阵)置为恒等矩阵
glLoadIdentity();
// 在观察坐标系(照相机坐标系)下思考,定位整个场景(第一种观点)或世界坐标系(第二种观点)
// 向负z轴方向平移8个单位
//glTranslatef ( 0.0, 0.0, -8.0 );
//// 将模型平面绕x轴逆时针旋转15度
//glRotatef( 90.0, 1.0, 0.0, 0.0 );
gluLookAt(0.0,3.0,12.0,0.0,0.0,0.0,0.0,1.0,0.0);
// 下面开始构建整个3D世界,在世界坐标系下考虑问题
// 太阳直接画在原点,无须变换,用一个黄色的线框球体表示
glColor3f( 1.0, 1.0, 0.0 );
glPushMatrix();
glRotatef(2*360.0*DayOfYear/365.0,0.0,1.0,0.0);
glTranslatef(-1.0,0.0,0.0);
glRotatef(-90,1.0,0.0,0.0);
glutWireSphere( 0.8, 15, 15 );
glPopMatrix();
glPushMatrix();
glRotatef(2*360.0*DayOfYear/365.0,0.0,1.0,0.0);
glTranslatef(1.0,0.0,0.0);
glRotatef(-90,1.0,0.0,0.0);
glutWireSphere( 0.8, 15, 15 );
glPopMatrix();
//绘制地球运动轨迹
glPushMatrix();
glColor3f(0.6,0.5,0.5);
glRotatef(90,1.0,0.0,0.0);
glutWireTorus(0.0,4.0,1,50);
glPopMatrix();
// 对地球系统定位
// 用DayOfYear来控制其绕太阳的旋转
glPushMatrix();
glRotatef( 360.0*DayOfYear/365.0, 0.0, 1.0, 0.0 );
glTranslatef( 4.0, 0.0, 0.0 );
glRotatef( 360.0*DayOfYear/365.0, 0.0, -1.0, 0.0 );
glRotatef(40,0.0,0.0,-1.0);
glColor3f(0.6,0.5,0.5);
glBegin(GL_LINES);
glVertex3f(0.0,0.6,0.0);
glVertex3f(0.0,-0.6,0.0);
glEnd();
// 下面开始在地球系统的小世界坐标系下考虑问题
// 绘制地球,地球的自转不应该影响月球
glPushMatrix(); // 保存矩阵状态
// 地球自转,用HourOfDay进行控制
glRotatef( 360.0*HourOfDay/24.0, 0.0, 1.0, 0.0 );
// 画一个线框球体用以表示地球
glColor3f( 0.2, 0.2, 1.0 );
glRotatef(90,-1.0,0.0,0.0);
glutWireSphere( 0.4, 10, 10);
glPopMatrix(); // 恢复矩阵状态
// 画月球
// 用DayOfYear来控制其绕地球的旋转
glPushMatrix(); //绘制月球运动轨迹
glColor3f(0.6,0.5,0.5);
glRotatef(90,1.0,0.0,0.0);
glutWireTorus(0.0,0.7,1,50);
glPopMatrix();
glRotatef( 360.0*12.0*DayOfYear/365.0, 0.0, 1.0, 0.0 );
glTranslatef( 0.7, 0.0, 0.0 );
glColor3f( 1.0,1.0,1.0 );
glutWireSphere( 0.2, 10, 10 );
//绘制月球的卫星
glRotatef( 4*360.0*12.0*DayOfYear/365.0, 0.0, 1.0, 0.0 );
glTranslatef(0.5,0.0,0.0);
glColor3f(0.3,0.7,0.3);
glutWireSphere(0.1,5,5);
glPopMatrix();
//绘制X行星
glPushMatrix();
glRotatef(360.0*DayOfYearX/300.0,0.0,1.0,0.0);
glTranslatef(-4.0,0.0,0.0);
glPushMatrix();
glRotatef(360.0*HourOfDayX/36.0,0.0,1.0,0.0);
glColor3f(0.5,0.5,0.5);
glRotatef(90,-1.0,0.0,0.0);
glutWireSphere(0.5,10,10);
//同步卫星
glColor3f(1.0,0.0,0.0);
glTranslatef(0.6,0.0,0.0);
glutWireSphere(0.2,10,10);
glPopMatrix();
//逆时针旋转卫星
glPushMatrix();
glRotatef(8*360.0*DayOfYearX/300.0,0.0,-1.0,0.0);
glColor3f(1.0,0.0,1.0);
glTranslatef(1.0,0.0,0.0);
glRotatef(90,-1.0,0.0,0.0);
glutWireSphere(0.2,10,10);
glPopMatrix();
glPopMatrix();
glutSwapBuffers(); // 交换缓存
if ( singleStep ) { // 如果是单步执行,则关闭动画
spinMode = GL_FALSE;
}
}
// 初始化OpenGL的状态
void OpenGLInit(void)
{
glClearColor( 0.0, 0.0, 0.0, 1.0 ); // 背景为黑色
glEnable( GL_DEPTH_TEST ); // 开启深度检测
}
// 窗口调整回调函数,当窗口大小发生变化时被调用
void ResizeWindow(int w, int h)
{
h = (h == 0) ? 1 : h;
w = (w == 0) ? 1 : w;
//glViewport( 0, 0, w, h ); // 视口占满整个窗口
float aspectRatio = (float)600/(float)360;
if((float)w/h<aspectRatio){
int height=w/aspectRatio;
glViewport(0,(h-height)/2,w,height);
}
else{
int width=h*aspectRatio;
glViewport((w-width)/2,0,width,h);
}
// 设置投影矩阵
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( 30.0, aspectRatio, 1.0, 30.0 );
// 选择模-视矩阵为当前矩阵,这样在显示回调函数中就不用再选择当前矩阵了
glMatrixMode( GL_MODELVIEW );
}
// r键处理
void Key_r(void)
{
if ( singleStep ) { // 如果之前为单步执行模式
singleStep = GL_FALSE; // 结束单步执行
spinMode = GL_TRUE; // 重启动画
}
else {
spinMode = !spinMode; // 切换动画开关状态
}
}
// s键处理(单步显示)
void Key_s(void)
{
singleStep = GL_TRUE;
spinMode = GL_TRUE;
}
// 普通按键回调函数
void KeyPressFunc( unsigned char Key, int x, int y )
{
switch ( Key ) {
case 'R':
case 'r':
Key_r();
break;
case 's':
case 'S':
Key_s();
break;
case 27: // Esc键
exit(1);
}
}
// 方向键上处理
void Key_up(void)
{
AnimateIncrement *= 2.0; // 动画时间间隔翻倍,即加快动画速度
}
void Key_down(void)
{
AnimateIncrement /= 2.0; // 动画时间间隔减半,即减慢动画速度
}
// 特殊按键回调函数
void SpecialKeyFunc( int Key, int x, int y )
{
switch ( Key ) {
case GLUT_KEY_UP:
Key_up();
break;
case GLUT_KEY_DOWN:
Key_down();
break;
}
}
// 定时器,实现每秒绘制60帧
void myTimer(int value)
{
glutPostRedisplay(); // 刷新显示
glutTimerFunc(1000/60, myTimer, value); // 循环计时
}
//设置光照
void SetupLights()
{
GLfloat ambientLight[]={0.2f,0.2f,0.2f,1.0f}; //环境光
GLfloat diffuseLight[]={0.9f,0.9f,0.9f,1.0f}; //漫反射
GLfloat specularLight[]={1.0f,1.0f,1.0f,1.0f}; //镜面光
GLfloat lightsPos[]={50.0f,100.0f,80.0f,1.0f}; //光源位置
glEnable(GL_LIGHTING); //打开光照
glLightfv(GL_LIGHT0,GL_AMBIENT,ambientLight); //设置环境光源
glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseLight); //设置漫反射光源
glLightfv(GL_LIGHT0,GL_SPECULAR,specularLight); //设置镜面光源
glLightfv(GL_LIGHT0,GL_POSITION,lightsPos); //设置灯光位置
glEnable(GL_LIGHT0); //打开第一个灯光
glEnable(GL_COLOR_MATERIAL); //使能跟踪材料的颜色
glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE); //指定材料作色的面
glMaterialfv(GL_FRONT,GL_SPECULAR,specularLight); //指定材料对镜面光的反应
glMateriali(GL_FRONT,GL_SHININESS,100); //指定反射系数
}
// Main函数:初始化OpenGL,设置回调函数,启动消息循环
int main( int argc, char** argv )
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH ); // 需要双缓存以用于动画
// 创建和定位图形窗口
glutInitWindowPosition( 0, 0 );
glutInitWindowSize( 600, 360 );
glutCreateWindow( "Solar System Demo" );
// 初始化OpenGL状态
OpenGLInit();
// 设置键盘按键的回调函数
glutKeyboardFunc( KeyPressFunc );
glutSpecialFunc( SpecialKeyFunc );
// 设置窗口调整的回调函数
glutReshapeFunc( ResizeWindow );
// 设置显示回调函数
glutDisplayFunc( Animate );
// 设置定时器
glutTimerFunc(100, myTimer, 0);
SetupLights();
// 启动事件循环.glutMain