这段代码花了我近一周时间学习。
球体之间的相互碰撞,球体与墙面之间的相互碰撞,球体与木桩之间的相互碰撞。小键盘+/-控制球体的timestep,也就是速度。UP/DOWN/LEFT/RIGHT控制camera,W/S/A/D控制位移。
F2控制camera跟进某个小球,可观察小球的碰撞路径。
一、碰撞检测
- 平面
- 圆柱
- 移动的球体
二、基础物理模型
- 碰撞应答
- 模拟在重力下(Using Euler Equations)
三、特效
- 爆炸模型(Using A Fin-Tree Billboard Method)
- 音效(Using The Windows Multimedia Library)
四、文件
- 分为五个文件
demo.cpp : Main code
Image.cpp, Image.h : Code to load bitmaps
Tmatrix.cpp, Tmatrix.h : Classes to handle rotations
Tray.cpp, Tray.h : Classes to handle ray operation
Tvector.cpp, Tvector.h : Classes to handle vector opertation
一、碰撞检测
碰撞检测在这里使用光线追踪算法,定义的‘光线’是一个有方向的标准化向量,其方程为:
(1)、平面---与平面碰撞:
(点积)Xn是平面的法线向量,X是表面的点向量,d是来自并行系统中心的一个浮点数,表示法线的距离。
如果光线相交于平面上的一些点,那么这些点满足:
转换,
替换d,
最终,
由TestIntersectionPlane()函数实现。
(2)、光线---与圆柱碰撞:
由TestIntersectionCylinder
()函数实现。
(3)、球---与球碰撞:
球体的定义只有一个点和半径。由FindBallCol
()函数实现。
计算每次开始碰撞之间的时间,可用Dst(球体从开始点到结束点的距离),Dsc(球体从碰撞点到下一个碰撞点的距离),T(时间步长),Tc(每次碰撞之间的时间)来表示,即公式
二、基础物理模型
(1)、碰撞应答
计算碰撞点后的向量R,需要I(碰撞前的向量),N(碰撞点处的法线)。即公式,
(2)、球体相互碰撞
U1和U2是两个球体碰撞前的速率方向,U1x,U1y是球1在x轴和y轴方向的速率投影,同样U2x,U2y是球2在x轴和y轴方向的速率投影。在接触瞬间,两个球心的距离为X_Axis。
V1和V2是两个球体碰撞后的速率方向,V1X,V2X,V1Y,V2Y都是在x轴和y轴方向的速率投影。M1和M2是两个球体的质量。
(3)、模拟重力下(欧拉方程)
三、特效
(1)、爆炸
每次碰撞在碰撞点上都有爆炸效果。
(2)、声音
每次爆炸调用。
四、文件
Image.cpp中bmp图片的格式标准http://www.martinreddy.net/gfx/2d/BMP.txt。
Tmatrix.cpp给出了矩阵的转置,求行列式,相加相减,相乘etc。
Tray.cpp说明球体运动的轨迹,是否碰撞etc。
Tvector.cpp说明球体轨迹的具体向量以及各种运算。
下面为代码,修改部分位于双行星号内。
1 #ifndef mathex_h 2 3 #define mathex_h 4 5 #include <math.h> 6 7 #define EPSILON 1.0e-8 8 9 #define ZERO EPSILON 10 11 #define M_PI 3.1415926535 12 13 #define VC 1 14 15 #if VC 16 17 #define bool int 18 #define false 0 19 #define true !false 20 21 #endif 22 23 template <class T> inline T limit(const T &x, const T &lower, const T &upper) 24 { 25 if (x < lower) 26 return lower; 27 if (x > upper) 28 return upper; 29 return x; 30 } 31 32 template <class T> inline T sqr(const T &x) 33 { 34 return x * x; 35 } 36 37 template <class T> inline T RadToDeg(const T &rad) 38 { 39 return (rad * 180.0f) / M_PI; 40 } 41 42 template <class T> inline T DegToRad(const T °) 43 { 44 return (deg * M_PI) / 180.0f; 45 } 46 47 #endif
1 #ifndef image_h 2 3 #define image_h 4 5 struct Image { 6 unsigned long sizeX; 7 unsigned long sizeY; 8 char* data; 9 }; 10 11 int ImageLoad(char* filename, Image* image); 12 13 #endif
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include "image.h" 4 5 static unsigned int getint(FILE *fp) 6 { 7 int c, c1, c2, c3; 8 9 // Get 4 bytes 10 c = getc(fp); 11 c1 = getc(fp); 12 c2 = getc(fp); 13 c3 = getc(fp); 14 15 return ((unsigned int) c) + 16 (((unsigned int) c1) << 8) + 17 (((unsigned int) c2) << 16) + 18 (((unsigned int) c3) << 24); 19 } 20 21 static unsigned int getshort(FILE *fp) 22 { 23 int c, c1; 24 // Get 2 bytes 25 c = getc(fp); 26 c1 = getc(fp); 27 return ((unsigned int) c) + (((unsigned int) c1) << 8); 28 29 } 30 31 int ImageLoad(char *filename, Image *image) { 32 FILE *file; 33 unsigned long size; // size of the image in bytes. 34 unsigned long i; // standard counter. 35 unsigned short int planes; // number of planes in image (must be 1) 36 unsigned short int bpp; // number of bits per pixel (must be 24) 37 char temp; // temporary color storage for bgr-rgb conversion 38 39 /* make sure the file is there.*/ 40 if ((file = fopen(filename, "rb"))==NULL) { 41 printf("File Not Found : %s\n",filename); 42 return 0; 43 } 44 fseek(file, 18, SEEK_CUR); 45 46 image->sizeX = getint (file); 47 printf("Width of %s: %lu\n", filename, image->sizeX); 48 49 image->sizeY = getint (file); 50 printf("Height of %s: %lu\n", filename, image->sizeY); 51 52 size = image->sizeX * image->sizeY * 3; 53 54 // Read the planes 55 planes = getshort(file); 56 if (planes != 1) { 57 printf("Planes from %s is not 1: %u\n", filename, planes); 58 return 0; 59 } 60 61 // Read the bpp 62 bpp = getshort(file); 63 if (bpp != 24) { 64 printf("Bpp from %s is not 24: %u\n", filename, bpp); 65 return 0; 66 } 67 68 fseek(file, 24, SEEK_CUR); 69 70 image->data = (char*) malloc(size); 71 if (image->data == NULL) { 72 printf("Error allocating memory for color-corrected image data"); 73 return 0; 74 } 75 76 if ((i = fread(image->data, size, 1, file)) != 1) { 77 printf("Error reading image data from %s.\n", filename); 78 return 0; 79 } 80 81 for (i = 0; i < size; i += 3) { 82 temp = image->data[i]; 83 image->data[i] = image->data[i + 2]; 84 image->data[i + 2] = temp; 85 } 86 return 1; 87 }
1 #ifndef matrix_h 2 3 #define matrix_h 4 5 #include <iostream> 6 #include "Mathex.h" 7 8 class TVector; 9 10 class TMatrix33 { 11 // Stream 12 friend std::ostream& operator<<(std::ostream& out, const TMatrix33& o) { return o.write(out); } 13 friend std::istream& operator>>(std::istream& in, TMatrix33& o) { return o.read(in); } 14 15 private: 16 double _Mx[3][3]; 17 18 // Input output 19 std::ostream& write(std::ostream& out) const; 20 std::istream& read(std::istream& in); 21 22 public: 23 // Constructors 24 TMatrix33(); 25 TMatrix33(double Phi, double Theta, double Psi); 26 TMatrix33(double mx00, double mx01, double mx02, double mx10, double mx11, double mx12, 27 double mx20, double mx21, double mx22); 28 29 // Selectors 30 double operator()(int Row, int Column) const { return _Mx[Row][Column]; } 31 double& operator()(int Row, int Column) { return _Mx[Row][Column]; } 32 33 // Optimised artimtric methods 34 static TMatrix33& add(const TMatrix33& m1, const TMatrix33& m2, TMatrix33& result); 35 static TMatrix33& subtract(const TMatrix33& m1, const TMatrix33& m2, TMatrix33& result); 36 static TMatrix33& multiply(const TMatrix33& m1, const TMatrix33& m2, TMatrix33& result); 37 static TMatrix33& multiply(const TMatrix33& m1, const double& scale, TMatrix33& result); 38 static TVector& multiply(const TMatrix33& m1, const TVector& v, TVector& result); 39 40 // Matrix arithmetic 41 TMatrix33& operator+=(const TMatrix33& m) { return add(*this, m, *this); } 42 TMatrix33& operator-=(const TMatrix33& m) { return subtract(*this, m, *this); } 43 TMatrix33& operator*=(const TMatrix33& m) { TMatrix33 tm(*this); return multiply(tm, m, *this); } 44 TMatrix33& operator*=(const double& scale) { return multiply(*this, scale, *this); } 45 46 TMatrix33& operator+(const TMatrix33& m) { TMatrix33 tm; return add(*this, m, tm); } 47 TMatrix33& operator-(const TMatrix33& m) { TMatrix33 tm; return subtract(*this, m, tm); } 48 TMatrix33& operator*(const TMatrix33& m) { TMatrix33 tm; return multiply(*this, m, tm); } 49 TMatrix33& operator*(const double& scale) { TMatrix33 tm; return multiply(*this, scale, tm); } 50 51 TVector operator*(const TVector& v) const; 52 53 // Determinants 54 double determinant() const; 55 static double determinant(const TMatrix33& m) { return m.determinant(); } 56 57 // Transpose 58 TMatrix33& transpose(); 59 static TMatrix33& transpose(const TMatrix33& m, TMatrix33& result) { result = m; return result.transpose(); } 60 static TMatrix33 transpose(const TMatrix33& m) { return TMatrix33(m).transpose(); } 61 62 // Inverse 63 static TMatrix33& TMatrix33::inverse(const TMatrix33& m1, TMatrix33& result); 64 static TMatrix33 TMatrix33::inverse(const TMatrix33& m1) { TMatrix33 tm; return inverse(m1, tm); } 65 TMatrix33 inverse() const { TMatrix33 tm; return inverse(*this, tm); } 66 }; 67 68 #endif
1 #include "Tmatrix.h" 2 #include "Tvector.h" 3 4 TMatrix33::TMatrix33() 5 { 6 _Mx[0][0] = 1.0; _Mx[0][1] = 0.0; _Mx[0][2] = 0.0; 7 _Mx[1][0] = 0.0; _Mx[1][1] = 1.0; _Mx[1][2] = 0.0; 8 _Mx[2][0] = 0.0; _Mx[2][1] = 0.0; _Mx[2][2] = 1.0; 9 } 10 11 TMatrix33::TMatrix33(double mx00, double mx01, double mx02, double mx10, 12 double mx11, double mx12, double mx20, double mx21, double mx22) 13 { 14 _Mx[0][0] = mx00; _Mx[0][1] = mx01; _Mx[0][2] = mx02; 15 _Mx[1][0] = mx10; _Mx[1][1] = mx11; _Mx[1][2] = mx12; 16 _Mx[2][0] = mx20; _Mx[2][1] = mx21; _Mx[2][2] = mx22; 17 } 18 19 TMatrix33::TMatrix33(double Phi, double Theta, double Psi) 20 { 21 double c1 = cos(Phi), s1 = sin(Phi), c2 = cos(Theta), s2 = sin(Theta), c3 = cos(Psi), s3 = sin(Psi); 22 23 _Mx[0][0] = c2 * c3; _Mx[0][1] = -c2 * s3; _Mx[0][2] = s2; 24 _Mx[1][0] = s1 * s2 * c3 + c1 * s3; _Mx[1][1] = -s1 * s2 * s3 + c1 * c3; _Mx[1][2] = -s1 * c2; 25 _Mx[2][0] = -c1 * s2 * c3 + s1 * s3; _Mx[2][1] = c1 * s2 * s3 + s1 * c3; _Mx[2][2] = c1 * c2; 26 } 27 28 TMatrix33& TMatrix33::add(const TMatrix33& m1, const TMatrix33& m2, TMatrix33& result) 29 { 30 result._Mx[0][0] = m1._Mx[0][0] + m2._Mx[0][0]; 31 result._Mx[0][1] = m1._Mx[0][1] + m2._Mx[0][1]; 32 result._Mx[0][2] = m1._Mx[0][2] + m2._Mx[0][2]; 33 34 result._Mx[1][0] = m1._Mx[1][0] + m2._Mx[1][0]; 35 result._Mx[1][1] = m1._Mx[1][1] + m2._Mx[1][1]; 36 result._Mx[1][2] = m1._Mx[1][2] + m2._Mx[1][2]; 37 38 result._Mx[2][0] = m1._Mx[2][0] + m2._Mx[2][0]; 39 result._Mx[2][1] = m1._Mx[2][1] + m2._Mx[2][1]; 40 result._Mx[2][2] = m1._Mx[2][2] + m2._Mx[2][2]; 41 return result; 42 } 43 44 TMatrix33& TMatrix33::subtract(const TMatrix33& m1, const TMatrix33& m2, TMatrix33& result) 45 { 46 result._Mx[0][0] = m1._Mx[0][0] - m2._Mx[0][0]; 47 result._Mx[0][1] = m1._Mx[0][1] - m2._Mx[0][1]; 48 result._Mx[0][2] = m1._Mx[0][2] - m2._Mx[0][2]; 49 50 result._Mx[1][0] = m1._Mx[1][0] - m2._Mx[1][0]; 51 result._Mx[1][1] = m1._Mx[1][1] - m2._Mx[1][1]; 52 result._Mx[1][2] = m1._Mx[1][2] - m2._Mx[1][2]; 53 54 result._Mx[2][0] = m1._Mx[2][0] - m2._Mx[2][0]; 55 result._Mx[2][1] = m1._Mx[2][1] - m2._Mx[2][1]; 56 result._Mx[2][2] = m1._Mx[2][2] - m2._Mx[2][2]; 57 return result; 58 } 59 60 TMatrix33& TMatrix33::multiply(const TMatrix33& m1, const TMatrix33& m2, TMatrix33& result) 61 { 62 result._Mx[0][0] = m1._Mx[0][0] * m2._Mx[0][0] + m1._Mx[0][1] * m2._Mx[1][0] + m1._Mx[0][2] * m2._Mx[2][0]; 63 result._Mx[1][0] = m1._Mx[1][0] * m2._Mx[0][0] + m1._Mx[1][1] * m2._Mx[1][0] + m1._Mx[1][2] * m2._Mx[2][0]; 64 result._Mx[2][0] = m1._Mx[2][0] * m2._Mx[0][0] + m1._Mx[2][1] * m2._Mx[1][0] + m1._Mx[2][2] * m2._Mx[2][0]; 65 66 result._Mx[0][1] = m1._Mx[0][0] * m2._Mx[0][1] + m1._Mx[0][1] * m2._Mx[1][1] + m1._Mx[0][2] * m2._Mx[2][1]; 67 result._Mx[1][1] = m1._Mx[1][0] * m2._Mx[0][1] + m1._Mx[1][1] * m2._Mx[1][1] + m1._Mx[1][2] * m2._Mx[2][1]; 68 result._Mx[2][1] = m1._Mx[2][0] * m2._Mx[0][1] + m1._Mx[2][1] * m2._Mx[1][1] + m1._Mx[2][2] * m2._Mx[2][1]; 69 70 result._Mx[0][2] = m1._Mx[0][0] * m2._Mx[0][2] + m1._Mx[0][1] * m2._Mx[1][2] + m1._Mx[0][2] * m2._Mx[2][2]; 71 result._Mx[1][2] = m1._Mx[1][0] * m2._Mx[0][2] + m1._Mx[1][1] * m2._Mx[1][2] + m1._Mx[1][2] * m2._Mx[2][2]; 72 result._Mx[2][2] = m1._Mx[2][0] * m2._Mx[0][2] + m1._Mx[2][1] * m2._Mx[1][2] + m1._Mx[2][2] * m2._Mx[2][2]; 73 return result; 74 } 75 76 TMatrix33& TMatrix33::multiply(const TMatrix33& m1, const double& scale, TMatrix33& result) 77 { 78 result._Mx[0][0] = m1._Mx[0][0] * scale; 79 result._Mx[0][1] = m1._Mx[0][1] * scale; 80 result._Mx[0][2] = m1._Mx[0][2] * scale; 81 82 result._Mx[1][0] = m1._Mx[1][0] * scale; 83 result._Mx[1][1] = m1._Mx[1][1] * scale; 84 result._Mx[1][2] = m1._Mx[1][2] * scale; 85 86 result._Mx[2][0] = m1._Mx[2][0] * scale; 87 result._Mx[2][1] = m1._Mx[2][1] * scale; 88 result._Mx[2][2] = m1._Mx[2][2] * scale; 89 return result; 90 } 91 92 TVector& TMatrix33::multiply(const TMatrix33& m1, const TVector& v, TVector& result) 93 { 94 result = TVector( 95 m1._Mx[0][0] * v.X() + m1._Mx[0][1] * v.Y() + m1._Mx[0][2] * v.Z(), 96 m1._Mx[1][0] * v.X() + m1._Mx[1][1] * v.Y() + m1._Mx[1][2] * v.Z(), 97 m1._Mx[2][0] * v.X() + m1._Mx[2][1] * v.Y() + m1._Mx[2][2] * v.Z()); 98 return result; 99 } 100 101 double TMatrix33::determinant() const { 102 return _Mx[0][0] * (_Mx[1][1] * _Mx[2][2] - _Mx[1][2] * _Mx[2][1]) 103 - _Mx[0][1] * (_Mx[1][0] * _Mx[2][2] - _Mx[1][2] * _Mx[2][0]) 104 + _Mx[0][2] * (_Mx[1][0] * _Mx[2][1] - _Mx[1][1] * _Mx[2][0]); 105 } 106 107 TMatrix33& TMatrix33::transpose() { 108 double t; 109 t = _Mx[0][2]; _Mx[0][2] = _Mx[2][0]; _Mx[2][0] = t; 110 t = _Mx[0][1]; _Mx[0][1] = _Mx[1][0]; _Mx[1][0] = t; 111 t = _Mx[1][2]; _Mx[1][2] = _Mx[2][1]; _Mx[2][1] = t; 112 return *this; 113 } 114 115 TMatrix33& TMatrix33::inverse(const TMatrix33& m1, TMatrix33& result) 116 { 117 double det = m1.determinant(); 118 119 if (fabs(det) < EPSILON) { 120 result = TMatrix33(); 121 return result; 122 } 123 else { 124 result._Mx[0][0] = m1._Mx[1][1] * m1._Mx[2][2] - m1._Mx[1][2] * m1._Mx[2][1]; 125 result._Mx[0][1] = m1._Mx[2][1] * m1._Mx[0][2] - m1._Mx[2][2] * m1._Mx[0][1]; 126 result._Mx[0][2] = m1._Mx[0][1] * m1._Mx[1][2] - m1._Mx[0][2] * m1._Mx[1][1]; 127 128 result._Mx[1][0] = m1._Mx[1][2] * m1._Mx[2][0] - m1._Mx[1][0] * m1._Mx[2][2]; 129 result._Mx[1][1] = m1._Mx[2][2] * m1._Mx[0][0] - m1._Mx[2][0] * m1._Mx[0][2]; 130 result._Mx[1][2] = m1._Mx[0][2] * m1._Mx[1][0] - m1._Mx[0][0] * m1._Mx[1][2]; 131 132 result._Mx[2][0] = m1._Mx[1][0] * m1._Mx[2][1] - m1._Mx[1][1] * m1._Mx[2][0]; 133 result._Mx[2][1] = m1._Mx[2][0] * m1._Mx[0][1] - m1._Mx[2][1] * m1._Mx[0][0]; 134 result._Mx[2][2] = m1._Mx[0][0] * m1._Mx[1][1] - m1._Mx[0][1] * m1._Mx[1][0]; 135 return multiply(result, 1.0 / det, result); 136 } 137 } 138 139 TVector TMatrix33::operator*(const TVector& v) const 140 { 141 TVector tv; 142 return multiply(*this, v, tv); 143 } 144 145 std::ostream& TMatrix33::write(std::ostream& out) const 146 { 147 return out << "(" << _Mx[0][0] << "," << _Mx[0][1] << "," << _Mx[0][2] << ")" << std::endl 148 << "(" << _Mx[1][0] << "," << _Mx[1][1] << "," << _Mx[1][2] << ")" << std::endl 149 << "(" << _Mx[2][0] << "," << _Mx[2][1] << "," << _Mx[2][2] << ")" << std::endl; 150 } 151 152 std::istream& TMatrix33::read(std::istream& in) 153 { 154 char ch; 155 return in >> ch >> _Mx[0][0] >> ch >> _Mx[0][1] >> ch >> _Mx[0][2] >> ch 156 >> ch >> _Mx[1][0] >> ch >> _Mx[1][1] >> ch >> _Mx[1][2] >> ch 157 >> ch >> _Mx[2][0] >> ch >> _Mx[2][1] >> ch >> _Mx[2][2] >> ch; 158 }
1 #ifndef tray_h 2 3 #define tray_h 4 5 #include <iostream> 6 #include "Mathex.h" 7 #include "Tvector.h" 8 9 class TRay { 10 // Streaming 11 friend std::ostream& operator<<(std::ostream& out, const TRay& o) { return o.write(out); } 12 friend std::istream& operator>>(std::istream& in, TRay& o) { return o.read(in); } 13 14 private: 15 TVector _P; // Any point on the line 16 TVector _V; // Direction of the line 17 18 // Input and output 19 std::ostream& write(std::ostream& out) const; 20 std::istream& read(std::istream& in); 21 22 public: 23 // Constructors 24 TRay() {} 25 26 // Line between two points OR point and a direction 27 TRay(const TVector& point1, const TVector& point2); 28 29 // Adjacent points on both lines 30 bool adjacentPoints(const TRay& ray, TVector& point1, TVector& point2) const; 31 32 //Unary operator 33 static TRay& invert(const TRay& r, TRay& result) { 34 result._P = r._P; TVector::invert(r._V, result._V); return result; 35 } 36 TRay operator-() const { return invert(*this, TRay()); } 37 38 // Selectors 39 TVector P() const { return _P; } 40 TVector V() const { return _V; } 41 int isValid() const { return V().isUnit() && P().isValid(); } 42 43 // Distance 44 double dist(const TRay& ray) const; 45 double dist(const TVector& point) const; 46 }; 47 48 #endif
1 #include <math.h> 2 #include "Tray.h" 3 #include "Mathex.h" 4 5 // Line between two point OR point and direction 6 TRay::TRay(const TVector& point1, const TVector& point2) : _P(point1) 7 { 8 _V = (point2.isUnit() ? point2 : TVector::unit(point2 - point1)); 9 } 10 11 bool TRay::adjacentPoints(const TRay& ray, TVector& point1, TVector& point2) const 12 { 13 if (isValid() && ray.isValid()) { 14 double temp = TVector::dot(_V, ray._V); 15 double temp2 = 1.0 - sqr(temp); 16 TVector tv; // Temporary vector to enable of optimised routines 17 18 // Check for parallel rays 19 if (fabs(temp2) < EPSILON) { 20 double mu = TVector::dot(_V, _P - ray._P) / temp; 21 point1 = _P; 22 23 TVector::add(ray._P, TVector::multiply(ray._V, mu, tv), point2); 24 } 25 else { 26 double a = TVector::dot(_V, TVector::subtract(ray._P, _P, tv)); 27 double b = TVector::dot(ray._V, TVector::subtract(_P, ray._P, tv)); 28 double mu = (b + temp * a) / temp2; 29 double lambda = (a + temp * b) / temp2; 30 TVector::add(_P, TVector::multiply(_V, lambda, tv), point1); 31 TVector::add(ray._P, TVector::multiply(ray._V, mu, tv), point2); 32 } 33 return true; 34 } 35 return false; 36 } 37 38 // Distance between two rays 39 double TRay::dist(const TRay& ray) const 40 { 41 TVector point1, point2; 42 43 if (adjacentPoints(ray, point1, point2)) 44 return point1.dist(point2); 45 else 46 return 0.0; 47 } 48 49 // Distance between a ray and a point 50 double TRay::dist(const TVector& point) const 51 { 52 if (isValid() && point.isValid()) { 53 TVector tv, point2; 54 double lambda = TVector::dot(_V, point - _P); 55 56 TVector::add(_P, TVector::multiply(_V, lambda, tv), point2); 57 return point.dist(point2); 58 } 59 return 0.0; 60 } 61 62 // Streaming 63 std::ostream& TRay::write(std::ostream& out) const 64 { 65 return isValid() ? out << "(p=" << _P << ",V=" << _V << ")" : out << "Invalid"; 66 } 67 68 std::istream& TRay::read(std::istream& in) 69 { 70 char buf[20], ch; 71 72 in >> ch; 73 if (ch != 'I') 74 in >> ch >> ch >> _P >> ch >> ch >> _V; 75 else { 76 *this = TRay(); 77 in.get(buf, 20, 'd'); 78 } 79 return in >> ch; 80 }
1 #ifndef tvector_h 2 3 #define tvector_h 4 5 #include <iostream> 6 #include <math.h> 7 #include "Mathex.h" 8 9 class TRay; 10 11 class TVector { 12 // Streaming 13 14 friend std::ostream& operator<<(std::ostream& out, const TVector& o) { return o.write(out); } 15 friend std::istream& operator>>(std::istream& in, TVector& o) { return o.read(in); } 16 17 public: 18 enum TStatus { INVALID, DEFAULT, UNIT }; 19 20 private: 21 double _x, _y, _z; 22 TStatus _Status; 23 24 // Constructors 25 TVector(double x, double y, double z, TStatus s) : _x(x), _y(y), _z(z), _Status(s) {} 26 27 // Input and output 28 std::ostream& write(std::ostream& out) const; 29 std::istream& read(std::istream& in); 30 31 public: 32 // Constructors 33 TVector() : _x(0.0), _y(0.0), _z(0.0), _Status(INVALID) {} 34 TVector(double x, double y, double z) : _x(x), _y(y), _z(z), _Status(DEFAULT) {} 35 36 // Mid Point between two lines 37 TVector(const TRay& line1, const TRay& line2); 38 39 // Selectors 40 double X() const { return _x; } 41 double Y() const { return _y; } 42 double Z() const { return _z; } 43 44 int isUnit() const { return _Status == UNIT; } 45 int isDefault() const { return _Status == DEFAULT; } 46 int isValid() const { return _Status != INVALID; } 47 48 // Change the status of a vector 49 TVector& unit(); 50 static TVector& unit(const TVector& v, TVector& result) { result = v; return result.unit(); } 51 static TVector unit(const TVector& v) { return TVector(v).unit(); } 52 53 TVector& Default(); 54 static TVector Default(const TVector& v, TVector& result) { result = v; return result.Default(); } 55 static TVector Default(const TVector& v) { return TVector(v).Default(); } 56 57 // Magnitude 58 double mag() const { return (isValid() ? (isUnit() ? 1.0 : sqrt(sqr(X()) + sqr(Y()) + sqr(Z()))) : 0.0); } 59 double magSqr() const { return (isValid() ? (isUnit() ? 1.0 : sqr(X()) + sqr(Y()) + sqr(Z())) : 0.0); } 60 61 // Dot or scalar product 62 double dot(const TVector& v) const { 63 return ((isValid() && isValid()) ? (X() * v.X() + Y() * v.Y() + Z() * v.Z()) : 0.0); 64 } 65 static double dot(const TVector& v1, const TVector& v2) { return v1.dot(v2); } 66 67 //Distance between two vector 68 double dist(const TVector& v) const { return (*this - v).mag(); } 69 double sidtSqr(const TVector& v) const { return (*this - v).magSqr(); } 70 71 // Optimised arithmetic methods 72 static TVector& invert(const TVector& v1, TVector& result); 73 static TVector& add(const TVector& v1, const TVector& v2, TVector& result); 74 static TVector& subtract(const TVector& v1, const TVector& v2, TVector& result); 75 static TVector& cross(const TVector& v1, const TVector& v2, TVector& result); 76 static TVector& multiply(const TVector& v1, const double& scale, TVector& result); 77 78 // Vector arithmetic, addition, subtraction and vector product 79 TVector operator-(const TVector& v) const { TVector tv; return subtract(*this, v, tv); } 80 TVector operator+(const TVector& v) const { TVector tv; return add(*this, v, tv); } 81 TVector operator*(const TVector& v) const { TVector tv; return cross(*this, v, tv); } 82 TVector operator*(const double& scale) const { TVector tv; return multiply(*this, scale, tv); } 83 TVector& operator+=(const TVector& v) { return add(*this, v, *this); } 84 TVector& operator-=(const TVector& v) { return subtract(*this, v, *this); } 85 TVector& operator*=(const TVector& v) { TVector tv(*this); return cross(tv, v, *this); } 86 TVector& operator*=(const double& scale) { return multiply(*this, scale, *this); } 87 TVector operator-() const { return invert(*this, TVector()); } 88 }; 89 #endif
1 #include "Tvector.h" 2 #include "Tray.h" 3 4 // Mid point between two rays 5 TVector::TVector(const TRay& ray1, const TRay& ray2) 6 { 7 TVector point1, point2; 8 9 if (ray1.adjacentPoints(ray2, point1, point2)) 10 *this = (point1 + point2) * 0.5; 11 else 12 *this = TVector(); 13 } 14 15 // Make a unit vector 16 TVector& TVector::unit() 17 { 18 if (isDefault()) { 19 double rep = mag(); 20 if (rep < EPSILON) { 21 _x = 0.0; 22 _y = 0.0; 23 _z = 0.0; 24 } 25 else { 26 double temp = 1.0 / rep; 27 _x *= temp; 28 _y *= temp; 29 _z *= temp; 30 } 31 _Status = UNIT; 32 } 33 return *this; 34 } 35 36 // Make a default vector 37 TVector& TVector::Default() 38 { 39 if (isUnit()) 40 _Status = DEFAULT; 41 return *this; 42 } 43 44 TVector& TVector::invert(const TVector& v1, TVector& result) 45 { 46 if (v1.isValid()) { 47 result._x = -v1._x; 48 result._y = -v1._y; 49 result._z = -v1._z; 50 result._Status = v1._Status; 51 } 52 else { 53 result = TVector(); 54 } 55 return result; 56 } 57 58 TVector& TVector::add(const TVector& v1, const TVector& v2, TVector& result) 59 { 60 if (v1.isValid() && v2.isValid()) { 61 result._x = v1._x + v2._x; 62 result._y = v1._y + v2._y; 63 result._z = v1._z + v2._z; 64 result._Status = DEFAULT; 65 } 66 else { 67 result = TVector(); 68 } 69 return result; 70 } 71 72 TVector& TVector::subtract(const TVector& v1, const TVector& v2, TVector& result) 73 { 74 if (v1.isValid() && v2.isValid()) { 75 result._x = v1._x - v2._x; 76 result._y = v1._y - v2._y; 77 result._z = v1._z - v2._z; 78 result._Status = DEFAULT; 79 } 80 else { 81 result = TVector(); 82 } 83 return result; 84 } 85 86 TVector& TVector::cross(const TVector& v1, const TVector& v2, TVector& result) 87 { 88 if (v1.isValid() && v2.isValid()) { 89 result._x = v1._y * v2._z - v1._z * v2._y; 90 result._y = v1._z * v2._x - v1._x * v2._z; 91 result._z = v1._x * v2._y - v1._y * v2._x; 92 result._Status = DEFAULT; 93 } 94 else { 95 result = TVector(); 96 } 97 return result; 98 } 99 100 TVector& TVector::multiply(const TVector& v1, const double& scale, TVector& result) 101 { 102 if (v1.isValid()) { 103 result._x = v1._x * scale; 104 result._y = v1._y * scale; 105 result._z = v1._z * scale; 106 result._Status = DEFAULT; 107 } 108 else { 109 result = TVector(); 110 } 111 return result; 112 } 113 114 // Streaming 115 std:: ostream& TVector::write(std::ostream& out) const 116 { 117 if (isValid()) { 118 if (isUnit()) 119 return out << "<" << X() << "," << Y() << "," << Z() << ">"; 120 else 121 return out << "[" << X() << "," << Y() << "," << Z() << "]"; 122 } 123 return out << "Invalid"; 124 } 125 126 std::istream& TVector::read(std::istream& in) 127 { 128 char buf[20], ch, ch2; 129 130 in >> ch2; 131 if (ch2 != 'I') { 132 double x, y, z; 133 in >> x >> ch >> y >> ch >> z; 134 *this = TVector(x, y, z); 135 136 if (ch2 == '<') 137 unit(); 138 } 139 else { 140 *this = TVector(); 141 in.get(buf, 20, 'd'); // Character stream, characters and character with Stop sign 142 } 143 return in >> ch; 144 }
1 /******************************************************************************************************************************************/ 2 /******************************************************************************************************************************************/ 3 #include <windows.h> 4 #include <math.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <gl/glew.h> 8 #include <GL\glut.h> 9 #include "Tvector.h" 10 #include "Tray.h" 11 #include "Tmatrix.h" 12 #include "Image.h" 13 #include <mmsystem.h> 14 15 #pragma comment(lib, "legacy_stdio_definitions.lib") 16 17 /* 18 * Every OpenGL program is linked to a Rendering Context. 19 * A Rendering Context is what links OpenGL calls to the Device Context. 20 * In order for your program to draw to a Window you need to create a Device Context. 21 * The DC connects the Window to the GDI (Graphics Device Interface). 22 */ 23 24 HGLRC hRC = NULL; // Permanent rendering context 25 HDC hDC = NULL; // Private GDI device context 26 HWND hWnd = NULL; // Holds our window handle 27 HINSTANCE hInstance; // Holds the instance of the application 28 29 /* 30 * It's important to make this global so that each procedure knows if 31 * the program is running in fullscreen mode or not. 32 */ 33 34 bool keys[256]; // Array used for the keyboard routine 35 bool active = TRUE; // Window active flag set to TRUE by default 36 bool fullscreen = TRUE; // Fullscreen flag set to fullscreen mode by default 37 38 DEVMODE DMsaved; // Saves the previous sacreen settings 39 40 GLfloat spec[] = { 1.0f, 1.0f, 1.0f, 1.0f }; // Specular highlight of balls 41 GLfloat posl[] = { 0, 400, 0, 1 }; // Position of light 42 GLfloat amb[] = { 0.2f, 0.2f, 0.2f, 1.0f }; // Global ambient 43 GLfloat amb2[] = { 0.3f, 0.3f, 0.3f, 1.0f }; // Ambient of lightsource 44 45 GLfloat rotx; 46 GLfloat position_x; 47 48 TVector dir(0, 0, -10); // Initial direction of camera 49 TVector pos(0, -50, 1000); // Initial position of camera 50 float camera_rotation = 0; // Holds rotation around the Y aixs 51 52 TVector veloc(0.5, -0.1, 0.5); // Initial velocity of balls 53 TVector accel(0, -0.05, 0); // acceleration ie. gravity of balls 54 55 TVector ArrayVel[10]; // Holds velocity of balls 56 TVector ArrayPos[10]; // Position of balls 57 TVector OldPos[10]; // Old position f balls 58 int NrOfBalls; // Numbers of balls 59 double Time = 0.6; // Timestep of simulation 60 int hook_toball1 = 0; // Hook camera on ball 61 int sounds = 1; // Sound on/off 62 63 struct Plane { 64 TVector _Position; 65 TVector _Normal; 66 }; 67 68 struct Cylinder { 69 TVector _Position; 70 TVector _Axis; 71 double _Radius; 72 }; 73 74 struct Explosion { 75 TVector _Position; 76 float _Alpha; 77 float _Scale; 78 }; 79 80 Plane pl1, pl2, pl3, pl4, pl5; // Room 81 Cylinder cyl1, cyl2, cyl3; // The 3 cylinder of the room 82 GLUquadricObj* cylinder_obj; // Quadratic object 83 GLuint texture[4]; // Stores texture objects 84 GLuint displayList; // Display list 85 Explosion ExplosionArray[20]; // Holds max 20 explosion ay once 86 87 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Declaration for WndProc 88 89 /* Init variables */ 90 void InitVars() 91 { 92 // Create planes 93 pl1._Position = TVector(0, -300, 0); 94 pl1._Normal = TVector(0, 1, 0); 95 pl2._Position = TVector(300, 0, 0); 96 pl2._Normal = TVector(-1, 0, 0); 97 pl3._Position = TVector(-300, 0, 0); 98 pl3._Normal = TVector(1, 0, 0); 99 pl4._Position = TVector(0, 0, 300); 100 pl4._Normal = TVector(0, 0, -1); 101 pl5._Position = TVector(0, 0, -300); 102 pl5._Normal = TVector(0, 0, 1); 103 104 // Create cylinders 105 cyl1._Position = TVector(0, 0, 0); 106 cyl1._Axis = TVector(0, 1, 0); 107 cyl1._Radius = 60 + 20; 108 cyl2._Position = TVector(200, -300, 0); 109 cyl2._Axis = TVector(0, 0, 1); 110 cyl2._Radius = 60 + 20; 111 cyl3._Position = TVector(-200, 0, 0); 112 cyl3._Axis = TVector(0, 1, 1); 113 cyl3._Axis.unit(); 114 cyl3._Radius = 30 + 20; 115 116 // Create quadratic object 117 cylinder_obj = gluNewQuadric(); 118 gluQuadricTexture(cylinder_obj, GL_TRUE); 119 120 // Set initial position and velocities of balls 121 // alse initialize array which holds explosions 122 NrOfBalls = 10; 123 124 ArrayVel[0] = veloc; 125 ArrayPos[0] = TVector(199, 180, 10); 126 ExplosionArray[0]._Alpha = 0; 127 ExplosionArray[0]._Scale = 1; 128 129 ArrayVel[1] = veloc; 130 ArrayPos[1] = TVector(0, 150, 100); 131 ExplosionArray[1]._Alpha = 0; 132 ExplosionArray[1]._Scale = 1; 133 134 ArrayVel[2] = veloc; 135 ArrayPos[2] = TVector(-100, 180, -100); 136 ExplosionArray[2]._Alpha = 0; 137 ExplosionArray[2]._Scale = 1; 138 139 for (int i = 3; i<10; i++) 140 { 141 ArrayVel[i] = veloc; 142 ArrayPos[i] = TVector(-500 + i * 75, 300, -500 + i * 50); 143 ExplosionArray[i]._Alpha = 0; 144 ExplosionArray[i]._Scale = 1; 145 } 146 for (int i = 10; i<20; i++) 147 { 148 ExplosionArray[i]._Alpha = 0; 149 ExplosionArray[i]._Scale = 1; 150 } 151 } 152 153 /* Find if any of the current balls intersect with each other in the current timestep*/ 154 /* Return the index of the 2 intersecting balls, the point and time of intersecting. */ 155 int FindBallCol(TVector& point, double& TimePoint, double& Time2, int& BallNr1, int& BallNr2) 156 { 157 TVector RelativeV; 158 TRay rays; 159 double MyTime = 0.0; 160 double Add = Time2 / 150.0; 161 double Timedummy = 10000; 162 double Timedummy2 = -1; 163 TVector posi; 164 165 // Test all balls against each other in 150 small steps 166 for (int i = 0; i < NrOfBalls - 1; ++i) { 167 for (int j = i + 1; j < NrOfBalls; ++j) { 168 RelativeV = ArrayVel[i] - ArrayVel[j]; 169 rays = TRay(OldPos[i], TVector::unit(RelativeV)); 170 MyTime = 0.0; 171 172 if ((rays.dist(OldPos[j])) > 40) 173 continue; 174 while (MyTime < Time2) { 175 MyTime += Add; 176 posi = OldPos[i] + RelativeV * MyTime; 177 if (posi.dist(OldPos[j]) <= 40) { 178 point = posi; 179 if ((MyTime - Add) < Timedummy) 180 Timedummy = MyTime - Add; 181 BallNr1 = i; 182 BallNr2 = j; 183 break; 184 } 185 } 186 } 187 } 188 if (Timedummy != 10000) { 189 TimePoint = Timedummy; 190 return 1; 191 } 192 return 0; 193 } 194 195 // Fast intersection function between ray/plane 196 int TestIntersectionPlane(const Plane& plane, const TVector& position, const TVector& direction, 197 double& lambda, TVector& pNormal) 198 { 199 double DotProduct = direction.dot(plane._Normal); 200 double l2; 201 202 // Determine if ray paralle to plane 203 if ((DotProduct < ZERO) && (DotProduct > -ZERO)) 204 return 0; 205 206 l2 = (plane._Normal.dot(plane._Position - position)) / DotProduct; 207 208 if (l2 < -ZERO) 209 return 0; 210 pNormal = plane._Normal; 211 lambda = l2; 212 return 1; 213 } 214 215 /* Fast intersection function between ray/cylinder */ 216 int TestIntersectionCylinder(const Cylinder& cylinder, const TVector& position, const TVector& direction, 217 double& lambda, TVector& pNormal, TVector& newposition) 218 { 219 TVector RC; 220 double d; 221 double t, s; 222 TVector n, D, O; 223 double ln; 224 double in, out; 225 226 TVector::subtract(position, cylinder._Position, RC); 227 TVector::cross(direction, cylinder._Axis, n); 228 229 ln = n.mag(); 230 if ((ln < ZERO) && (ln > -ZERO)) 231 return 0; 232 233 n.unit(); 234 d = fabs(RC.dot(n)); 235 236 if (d <= cylinder._Radius) { 237 TVector::cross(RC, cylinder._Axis, O); 238 t = -O.dot(n) / ln; 239 TVector::cross(n, cylinder._Axis, O); 240 O.unit(); 241 s = fabs(sqrt(cylinder._Radius*cylinder._Radius - d * d) / direction.dot(O)); 242 243 in = t - s; 244 out = t + s; 245 if (in < -ZERO) { 246 if (out < -ZERO) 247 return 0; 248 else 249 lambda = out; 250 } 251 else { 252 if (out<-ZERO) { 253 lambda = in; 254 } 255 else { 256 if (in<out) 257 lambda = in; 258 else 259 lambda = out; 260 } 261 } 262 newposition = position + direction * lambda; 263 TVector HB = newposition - cylinder._Position; 264 pNormal = HB - cylinder._Axis * (HB.dot(cylinder._Axis)); 265 pNormal.unit(); 266 return 1; 267 } 268 return 0; 269 } 270 271 /* Main loop of simulation moves. finds the collisions */ 272 /* and responses of the objects in the current time step */ 273 void idle() 274 { 275 double rt, rt2, rt4, lambda = 10000; 276 TVector norm, uveloc; 277 TVector normal, point, time; 278 double RestTime, BallTime; 279 TVector Pos2; 280 int BallNr = 0; 281 int dummy = 0; 282 int BallColNr1, BallColNr2; 283 TVector Nc; 284 285 RestTime = Time; 286 lambda = 10000; 287 288 // Compute velocity for next tiemstep using Euler equation 289 for (int j = 0; j < NrOfBalls; ++j) 290 ArrayVel[j] += accel * RestTime; 291 292 // While timestep not over 293 while (RestTime > ZERO) { 294 lambda = 10000; 295 // For all the balls find closest intersection between balls and planes/cylinders 296 for (int i = 0; i < NrOfBalls; ++i) { 297 // Compute new position and distance 298 OldPos[i] = ArrayPos[i]; 299 TVector::unit(ArrayVel[i], uveloc); 300 ArrayPos[i] = ArrayPos[i] + ArrayVel[i] * RestTime; 301 rt2 = OldPos[i].dist(ArrayPos[i]); 302 303 // Test if collision occured between ball and ball 5 planes 304 if (TestIntersectionPlane(pl1, OldPos[i], uveloc, rt, norm)) { 305 // Find intersection time 306 rt4 = rt * RestTime / rt2; 307 // If smaller than the one already stored replace and in tiemstep 308 if (rt4 <= lambda) { 309 if (rt4 <= RestTime + ZERO) { 310 if (!((rt < ZERO) && (uveloc.dot(norm) > ZERO))) { 311 normal = norm; 312 point = OldPos[i] + uveloc * rt; 313 lambda = rt4; 314 BallNr = i; 315 } 316 } 317 } 318 } 319 if (TestIntersectionPlane(pl2, OldPos[i], uveloc, rt, norm)) { 320 rt4 = rt * RestTime / rt2; 321 if (rt4 <= lambda) { 322 if (rt4 <= RestTime + ZERO) { 323 if (!((rt <= ZERO) && (uveloc.dot(norm)>ZERO))) 324 { 325 normal = norm; 326 point = OldPos[i] + uveloc * rt; 327 lambda = rt4; 328 BallNr = i; 329 dummy = 1; 330 } 331 } 332 } 333 } 334 if (TestIntersectionPlane(pl3, OldPos[i], uveloc, rt, norm)) { 335 rt4 = rt*RestTime / rt2; 336 if (rt4 <= lambda) { 337 if (rt4 <= RestTime + ZERO) { 338 if (!((rt <= ZERO) && (uveloc.dot(norm)>ZERO))) { 339 normal = norm; 340 point = OldPos[i] + uveloc * rt; 341 lambda = rt4; 342 BallNr = i; 343 } 344 } 345 } 346 } 347 if (TestIntersectionPlane(pl4, OldPos[i], uveloc, rt, norm)) { 348 rt4 = rt*RestTime / rt2; 349 if (rt4 <= lambda) { 350 if (rt4 <= RestTime + ZERO) { 351 if (!((rt <= ZERO) && (uveloc.dot(norm)>ZERO))) { 352 normal = norm; 353 point = OldPos[i] + uveloc * rt; 354 lambda = rt4; 355 BallNr = i; 356 } 357 } 358 } 359 } 360 if (TestIntersectionPlane(pl5, OldPos[i], uveloc, rt, norm)) { 361 rt4 = rt*RestTime / rt2; 362 if (rt4 <= lambda) { 363 if (rt4 <= RestTime + ZERO) { 364 if (!((rt <= ZERO) && (uveloc.dot(norm)>ZERO))) { 365 normal = norm; 366 point = OldPos[i] + uveloc * rt; 367 lambda = rt4; 368 BallNr = i; 369 } 370 } 371 } 372 } 373 // Now test intersection with the 3 cylinders 374 if (TestIntersectionCylinder(cyl1, OldPos[i], uveloc, rt, norm, Nc)) { 375 rt4 = rt * RestTime / rt2; 376 if (rt4 <= lambda) { 377 if (rt4 <= RestTime + ZERO) { 378 if (!((rt <= ZERO) && (uveloc.dot(norm)>ZERO))) { 379 normal = norm; 380 point = Nc; 381 lambda = rt4; 382 BallNr = i; 383 } 384 } 385 } 386 } 387 if (TestIntersectionCylinder(cyl2, OldPos[i], uveloc, rt, norm, Nc)) { 388 rt4 = rt * RestTime / rt2; 389 if (rt4 <= lambda) { 390 if (rt4 <= RestTime + ZERO) { 391 if (!((rt <= ZERO) && (uveloc.dot(norm)>ZERO))) { 392 normal = norm; 393 point = Nc; 394 lambda = rt4; 395 BallNr = i; 396 } 397 } 398 } 399 } 400 if (TestIntersectionCylinder(cyl3, OldPos[i], uveloc, rt, norm, Nc)) { 401 rt4 = rt * RestTime / rt2; 402 if (rt4 <= lambda) { 403 if (rt4 <= RestTime + ZERO) { 404 if (!((rt <= ZERO) && (uveloc.dot(norm)>ZERO))) { 405 normal = norm; 406 point = Nc; 407 lambda = rt4; 408 BallNr = i; 409 } 410 } 411 } 412 } 413 } 414 // After all balls were teste with planes/cylinder test for collision 415 // between them and replace if collision time smaller 416 if (FindBallCol(Pos2, BallTime, RestTime, BallColNr1, BallColNr2)) { 417 if (sounds) 418 PlaySound("Data/Explode.wav", NULL, SND_FILENAME | SND_ASYNC); 419 if ((lambda == 10000) || (lambda > BallTime)) { 420 RestTime = RestTime - BallTime; 421 422 TVector pb1, pb2, xaxis, U1x, U1y, U2x, U2y, V1x, V1y, V2x, V2y; 423 double a, b; 424 425 pb1 = OldPos[BallColNr1] + ArrayVel[BallColNr1] * BallTime; 426 pb2 = OldPos[BallColNr2] + ArrayVel[BallColNr2] * BallTime; 427 xaxis = (pb2 - pb1).unit(); 428 429 a = xaxis.dot(ArrayVel[BallColNr1]); 430 U1x = xaxis * a; 431 U1y = ArrayVel[BallColNr1] - U1x; 432 433 xaxis = (pb1 - pb2).unit(); 434 b = xaxis.dot(ArrayVel[BallColNr2]); 435 U2x = xaxis * b; 436 U2y = ArrayVel[BallColNr2] - U2x; 437 438 V1x = (U1x + U2x - (U1x - U2x)) * 0.5; 439 V2x = (U1x + U2x - (U2x - U1x)) * 0.5; 440 V1y = U1y; 441 V2y = U2y; 442 443 for (int j = 0; j < NrOfBalls; j++) 444 ArrayPos[j] = OldPos[j] + ArrayVel[j] * BallTime; 445 446 ArrayVel[BallColNr1] = V1x + V1y; 447 ArrayVel[BallColNr2] = V2x + V2y; 448 449 // Update explosion array 450 for (int j = 0; j < 20; j++) { 451 if (ExplosionArray[j]._Alpha <= 0) { 452 ExplosionArray[j]._Alpha = 1; 453 ExplosionArray[j]._Position = ArrayPos[BallColNr1]; 454 ExplosionArray[j]._Scale = 1; 455 break; 456 } 457 } 458 continue; 459 } 460 } 461 // End of tests 462 // If test occured move simulation for the correct timestep 463 // and compute response for the colliding ball 464 if (lambda != 10000) 465 { 466 RestTime -= lambda; 467 468 for (int j = 0; j < NrOfBalls; j++) 469 ArrayPos[j] = OldPos[j] + ArrayVel[j] * lambda; 470 471 rt2 = ArrayVel[BallNr].mag(); 472 ArrayVel[BallNr].unit(); 473 ArrayVel[BallNr] = TVector::unit((normal * (2 * normal.dot(-ArrayVel[BallNr]))) + ArrayVel[BallNr]); 474 ArrayVel[BallNr] = ArrayVel[BallNr] * rt2; 475 476 // Update explosion array 477 for (int j = 0; j < 20; j++) { 478 if (ExplosionArray[j]._Alpha <= 0) { 479 ExplosionArray[j]._Alpha = 1; 480 ExplosionArray[j]._Position = point; 481 ExplosionArray[j]._Scale = 1; 482 break; 483 } 484 } 485 } 486 else { 487 RestTime = 0; 488 } 489 } 490 } 491 492 /* Load bitmaps and convert to textures */ 493 void LoadGLTextures() 494 { 495 Image *image1, *image2, *image3, *image4; 496 497 // allocate space for texture 498 image1 = (Image *)malloc(sizeof(Image)); 499 image2 = (Image *)malloc(sizeof(Image)); 500 image3 = (Image *)malloc(sizeof(Image)); 501 image4 = (Image *)malloc(sizeof(Image)); 502 if (image1 == NULL || image2 == NULL || image3 == NULL || image4 == NULL) { 503 printf("Error allocating space for image"); 504 exit(0); 505 } 506 507 if (!ImageLoad("data/marble.bmp", image1) || !ImageLoad("data/spark.bmp", image2) || 508 !ImageLoad("data/boden.bmp", image3) || !ImageLoad("data/wand.bmp", image4)) { 509 exit(1); 510 } 511 512 glGenTextures(2, &texture[0]); 513 glBindTexture(GL_TEXTURE_2D, texture[0]); 514 515 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 516 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 517 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 518 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 519 520 glTexImage2D(GL_TEXTURE_2D, 0, 3, image1->sizeX, image1->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, image1->data); 521 522 glBindTexture(GL_TEXTURE_2D, texture[1]); 523 524 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 525 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 526 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 527 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 528 529 glTexImage2D(GL_TEXTURE_2D, 0, 3, image2->sizeX, image2->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, image2->data); 530 531 532 glGenTextures(2, &texture[2]); 533 glBindTexture(GL_TEXTURE_2D, texture[2]); 534 535 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 536 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 537 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 538 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 539 540 glTexImage2D(GL_TEXTURE_2D, 0, 3, image3->sizeX, image3->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, image3->data); 541 542 glBindTexture(GL_TEXTURE_2D, texture[3]); 543 544 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 545 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 546 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 547 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 548 549 glTexImage2D(GL_TEXTURE_2D, 0, 3, image4->sizeX, image4->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, image4->data); 550 551 free(image1->data); 552 free(image1); 553 free(image2->data); 554 free(image2); 555 free(image3->data); 556 free(image3); 557 free(image4->data); 558 free(image4); 559 } 560 561 int ProcessKeys() 562 { 563 if (keys['W']) 564 pos += TVector(0, 0, -1); 565 if (keys['S']) 566 pos += TVector(0, 0, 1); 567 if (keys['A']) 568 position_x += 1; 569 if (keys['D']) 570 position_x -= 1; 571 if (keys[VK_UP]) 572 rotx += 0.1; 573 if (keys[VK_DOWN]) 574 rotx -= 0.1; 575 if (keys[VK_LEFT]) 576 camera_rotation += 0.1; 577 if (keys[VK_RIGHT]) 578 camera_rotation -= 0.1; 579 if (keys[VK_ADD]) { 580 Time += 0.1; 581 if (Time >= 10.0) 582 Time = 10.0; 583 keys[VK_ADD] = FALSE; 584 } 585 if (keys[VK_SUBTRACT]) { 586 Time -= 0.1; 587 if (Time <= 0.1) 588 Time = 0.0; 589 keys[VK_SUBTRACT] = FALSE; 590 } 591 if (keys[VK_F3]) { 592 sounds ^= 1; 593 keys[VK_F3] = FALSE; 594 } 595 if (keys[VK_F2]) { 596 hook_toball1 ^= 1; 597 camera_rotation = 0; 598 keys[VK_F2] = FALSE; 599 } 600 return 1; 601 } 602 603 604 /******************************************************************************************************************************************/ 605 /******************************************************************************************************************************************/ 606 GLvoid ReSizeGLScene(GLsizei width, GLsizei height) // Resize and initialize the GL window 607 { 608 if (height == 0) { // Prevent a divide by zero by 609 height = 1; // Making height equal one 610 } 611 612 glViewport(0, 0, width, height); // Reset the current viewport 613 614 /* 615 * The following lines set the screen up for a perspective view. 616 * Meaning things in the distance get smaller. This creates a realistic looking scene. 617 * The perspective is calculated with a 45 degree viewing angle based on 618 * the windows width and height. The 0.1f, 100.0f is the starting point and 619 * ending point for how deep we can draw into the screen. 620 * 621 * The projection matrix is responsible for adding perspective to our scene. 622 * glLoadIdentity() restores the selected matrix to it's original state. 623 * The modelview matrix is where our object information is stored. 624 * Lastly we reset the modelview matrix. 625 */ 626 627 glMatrixMode(GL_PROJECTION); // Select the projection matrix 628 glLoadIdentity(); // Reset the projection matrix 629 630 // Calculate the aspect ratio of the window 631 gluPerspective(50.0f, (GLfloat)width / (GLfloat)height, 10.0f, 1700.0f); 632 // glOrtho(0.0f, width, height, 0.0f, -1.0f, 1.0f); // Create orhto 640X480 view (0, 0, at the top) 633 634 glMatrixMode(GL_MODELVIEW); // Seclet the modelview matrix 635 glLoadIdentity(); // Reset the modelview matrix 636 } 637 /******************************************************************************************************************************************/ 638 /******************************************************************************************************************************************/ 639 640 int InitGL(GLvoid) // All setup for OpenGL goes here 641 { 642 float df = 100.0f; 643 644 glClearDepth(1.0f); // Depth buffer setup 645 glEnable(GL_DEPTH_TEST); 646 glDepthFunc(GL_LEQUAL); 647 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); 648 649 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Background 650 glMatrixMode(GL_MODELVIEW); 651 glLoadIdentity(); 652 653 glShadeModel(GL_SMOOTH); 654 glEnable(GL_CULL_FACE); 655 glEnable(GL_DEPTH_TEST); 656 657 glMaterialfv(GL_FRONT, GL_SPECULAR, spec); 658 glMaterialfv(GL_FRONT, GL_SHININESS, &df); 659 660 glEnable(GL_LIGHTING); 661 glLightfv(GL_LIGHT0, GL_POSITION, posl); 662 glLightfv(GL_LIGHT0, GL_AMBIENT, amb2); 663 glEnable(GL_LIGHT0); 664 665 glLightModelfv(GL_LIGHT_MODEL_AMBIENT, amb); 666 glEnable(GL_COLOR_MATERIAL); 667 glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); 668 669 glEnable(GL_BLEND); 670 glBlendFunc(GL_SRC_ALPHA, GL_ONE); 671 672 glEnable(GL_TEXTURE_2D); 673 LoadGLTextures(); 674 675 // Construct billboared explosion primitive as display list 676 // 4 quads at right angles to each other 677 glNewList(displayList = glGenLists(1), GL_COMPILE); 678 glBegin(GL_QUADS); 679 glRotatef(-45, 0, 1, 0); 680 glNormal3f(0, 0, 1); 681 glTexCoord2f(0.0f, 0.0f); glVertex3f(-50, -40, 0); 682 glTexCoord2f(0.0f, 1.0f); glVertex3f(50, -40, 0); 683 glTexCoord2f(1.0f, 1.0f); glVertex3f(50, 40, 0); 684 glTexCoord2f(1.0f, 0.0f); glVertex3f(-50, 40, 0); 685 glNormal3f(0, 0, -1); 686 glTexCoord2f(0.0f, 0.0f); glVertex3f(-50, 40, 0); 687 glTexCoord2f(0.0f, 1.0f); glVertex3f(50, 40, 0); 688 glTexCoord2f(1.0f, 1.0f); glVertex3f(50, -40, 0); 689 glTexCoord2f(1.0f, 0.0f); glVertex3f(-50, -40, 0); 690 691 glNormal3f(1, 0, 0); 692 glTexCoord2f(0.0f, 0.0f); glVertex3f(0, -40, 50); 693 glTexCoord2f(0.0f, 1.0f); glVertex3f(0, -40, -50); 694 glTexCoord2f(1.0f, 1.0f); glVertex3f(0, 40, -50); 695 glTexCoord2f(1.0f, 0.0f); glVertex3f(0, 40, 50); 696 glNormal3f(-1, 0, 0); 697 glTexCoord2f(0.0f, 0.0f); glVertex3f(0, 40, 50); 698 glTexCoord2f(0.0f, 1.0f); glVertex3f(0, 40, -50); 699 glTexCoord2f(1.0f, 1.0f); glVertex3f(0, -40, -50); 700 glTexCoord2f(1.0f, 0.0f); glVertex3f(0, -40, 50); 701 glEnd(); 702 glEndList(); 703 704 return TRUE; 705 } 706 707 /* 708 * For now all we will do is clear the screen to the color we previously decided on, 709 * clear the depth buffer and reset the scene. We wont draw anything yet. 710 */ 711 bool DrawGLScene(GLvoid) // Here's where we do all the drawing 712 { 713 glMatrixMode(GL_MODELVIEW); 714 glLoadIdentity(); 715 716 // Set camera in hookhmode 717 if (hook_toball1) { 718 TVector unit_followvector = ArrayVel[0]; 719 unit_followvector.unit(); 720 gluLookAt(ArrayPos[0].X() + 250, ArrayPos[0].Y() + 250, ArrayPos[0].Z(), 721 ArrayPos[0].X() + ArrayVel[0].X(), ArrayPos[0].Y() + ArrayVel[0].Y(), 722 ArrayPos[0].Z() + ArrayVel[0].Z(), 0, 1, 0); 723 } 724 else { 725 gluLookAt(pos.X(), pos.Y(), pos.Z(), pos.X() + dir.X(), pos.Y() + dir.Y(), 726 pos.Z() + dir.Z(), 0.0, 1.0, 0.0); 727 } 728 729 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 730 glRotatef(camera_rotation, 0, 1, 0); 731 glRotatef(rotx, 1, 0, 0); 732 glTranslatef(position_x, 0, 0); 733 // Render balls 734 for (int i = 0; i < NrOfBalls; ++i) { 735 switch (i) { 736 case 1: glColor3f(1.0f, 1.0f, 1.0f); break; 737 case 2: glColor3f(1.0f, 1.0f, 0.0f); break; 738 case 3: glColor3f(0.0f, 1.0f, 1.0f); break; 739 case 4: glColor3f(0.0f, 1.0f, 0.0f); break; 740 case 5: glColor3f(0.0f, 0.0f, 1.0f); break; 741 case 6: glColor3f(0.65f, 0.2f, 0.3f); break; 742 case 7: glColor3f(1.0f, 0.0f, 1.0f); break; 743 case 8: glColor3f(0.0f, 0.7f, 0.4f); break; 744 default: glColor3f(1.0f, 0, 0); 745 } 746 glPushMatrix(); 747 glTranslated(ArrayPos[i].X(), ArrayPos[i].Y(), ArrayPos[i].Z()); 748 gluSphere(cylinder_obj, 20, 20, 20); 749 glPopMatrix(); 750 } 751 752 glEnable(GL_TEXTURE_2D); 753 754 // Render walls with texture 755 glBindTexture(GL_TEXTURE_2D, texture[3]); 756 glColor3f(1, 1, 1); 757 glBegin(GL_QUADS); 758 glTexCoord2f(1.0f, 0.0f); glVertex3f(320, 320, 320); 759 glTexCoord2f(1.0f, 1.0f); glVertex3f(320, -320, 320); 760 glTexCoord2f(0.0f, 1.0f); glVertex3f(-320, -320, 320); 761 glTexCoord2f(0.0f, 0.0f); glVertex3f(-320, 320, 320); 762 763 glTexCoord2f(1.0f, 0.0f); glVertex3f(-320, 320, -320); 764 glTexCoord2f(1.0f, 1.0f); glVertex3f(-320, -320, -320); 765 glTexCoord2f(0.0f, 1.0f); glVertex3f(320, -320, -320); 766 glTexCoord2f(0.0f, 0.0f); glVertex3f(320, 320, -320); 767 768 glTexCoord2f(1.0f, 0.0f); glVertex3f(320, 320, -320); 769 glTexCoord2f(1.0f, 1.0f); glVertex3f(320, -320, -320); 770 glTexCoord2f(0.0f, 1.0f); glVertex3f(320, -320, 320); 771 glTexCoord2f(0.0f, 0.0f); glVertex3f(320, 320, 320); 772 773 glTexCoord2f(1.0f, 0.0f); glVertex3f(-320, 320, 320); 774 glTexCoord2f(1.0f, 1.0f); glVertex3f(-320, -320, 320); 775 glTexCoord2f(0.0f, 1.0f); glVertex3f(-320, -320, -320); 776 glTexCoord2f(0.0f, 0.0f); glVertex3f(-320, 320, -320); 777 glEnd(); 778 779 // Render floor with color 780 glBindTexture(GL_TEXTURE_2D, texture[2]); 781 glBegin(GL_QUADS); 782 glTexCoord2f(1.0f, 0.0f); glVertex3f(-320, -320, 320); 783 glTexCoord2f(1.0f, 1.0f); glVertex3f(320, -320, 320); 784 glTexCoord2f(0.0f, 1.0f); glVertex3f(320, -320, -320); 785 glTexCoord2f(0.0f, 0.0f); glVertex3f(-320, -320, -320); 786 glEnd(); 787 788 789 // Render columns 790 glBindTexture(GL_TEXTURE_2D, texture[0]); 791 glColor3f(0.5, 0.5, 0.5); 792 glPushMatrix(); 793 glRotatef(90, 1, 0, 0); 794 glTranslatef(0, 0, -500); 795 gluCylinder(cylinder_obj, 60, 60, 1000, 20, 2); 796 glPopMatrix(); 797 798 glPushMatrix(); 799 glTranslatef(200, -300, -500); 800 gluCylinder(cylinder_obj, 60, 60, 1000, 20, 2); 801 glPopMatrix(); 802 803 glPushMatrix(); 804 glTranslatef(-200, 0, 0); 805 glRotatef(135, 1, 0, 0); 806 glTranslatef(0, 0, -500); 807 gluCylinder(cylinder_obj, 30, 30, 1000, 20, 2); 808 glPopMatrix(); 809 810 // Render explosions 811 glEnable(GL_BLEND); 812 glDepthMask(GL_FALSE); 813 glBindTexture(GL_TEXTURE_2D, texture[1]); 814 for (int i = 0; i < 20; i++) 815 { 816 if (ExplosionArray[i]._Alpha >= 0) 817 { 818 glPushMatrix(); 819 ExplosionArray[i]._Alpha -= 0.01f; 820 ExplosionArray[i]._Scale += 0.03f; 821 glColor4f(1, 1, 0, ExplosionArray[i]._Alpha); 822 glScalef(ExplosionArray[i]._Scale, ExplosionArray[i]._Scale, ExplosionArray[i]._Scale); 823 glTranslatef((float)ExplosionArray[i]._Position.X() / ExplosionArray[i]._Scale, 824 (float)ExplosionArray[i]._Position.Y() / ExplosionArray[i]._Scale, 825 (float)ExplosionArray[i]._Position.Z() / ExplosionArray[i]._Scale); 826 glCallList(displayList); 827 glPopMatrix(); 828 } 829 } 830 glDepthMask(GL_TRUE); 831 glDisable(GL_BLEND); 832 glDisable(GL_TEXTURE_2D); 833 return true; 834 } 835 //******************************************************************************************************************************************/ 836 //******************************************************************************************************************************************/ 837 /* 838 * The job of KillGLWindow() is to release the Rendering Context, 839 * the Device Context and finally the Window Handle. 840 */ 841 842 GLvoid KillGLWindow(GLvoid) // Properly kill the window 843 { 844 if (fullscreen) { // Are we in fullscreen mode 845 if (!ChangeDisplaySettings(NULL, CDS_TEST)) { // If the shortcut doesn't work 846 ChangeDisplaySettings(NULL, CDS_RESET); // Do it anyway (to get the values out of the registry) 847 ChangeDisplaySettings(&DMsaved, CDS_RESET); 848 } 849 else { 850 ChangeDisplaySettings(NULL, CDS_RESET); // If so switch back to the desktop 851 } 852 /* 853 * We use ChangeDisplaySettings(NULL,0) to return us to our original desktop. 854 * After we've switched back to the desktop we make the cursor visible again. 855 */ 856 ShowCursor(TRUE); // Show mouse pointer 857 } 858 859 if (hRC) { // Do we have a rendering context 860 if (!wglMakeCurrent(NULL, NULL)) { // Are we able to release the DC and RC contexts 861 MessageBox(NULL, "Release of DC and RC failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); 862 } 863 864 if (!wglDeleteContext(hRC)) { // Are we able to delete the RC 865 MessageBox(NULL, "Release rendering context failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); 866 hRC = NULL; // Set RC to NULL 867 } 868 869 if (hDC && !ReleaseDC(hWnd, hDC)) { // Are we able to release the DC 870 MessageBox(NULL, "Release device context failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); 871 hDC = NULL; // Set DC to NULL 872 } 873 if (hWnd && !DestroyWindow(hWnd)) { // Are we able to destroy the window 874 MessageBox(NULL, "Could not release hWnd.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); 875 hWnd = NULL; // Set hWnd to NULL 876 } 877 878 if (!UnregisterClass("OpenGL", hInstance)) { // Are we able to unregister class 879 MessageBox(NULL, "Could not register class.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); 880 hInstance = NULL; // Set hInstance to NULL 881 } 882 } 883 } 884 /* 885 * The next section of code creates our OpenGL Window. 886 */ 887 888 BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag) 889 { 890 /* 891 * Find a pixel format that matches the one we want 892 */ 893 GLuint PixelFormat; // Holds the result after serching for a match 894 895 /* 896 * Before you create a window, you MUST register a Class for the window 897 */ 898 WNDCLASS wc; // Windows class structure 899 900 /* 901 * dwExStyle and dwStyle will store the Extended and normal Window Style Information. 902 */ 903 DWORD dwExStyle; // Window extend style 904 DWORD dwStyle; // Window style 905 906 907 RECT WindowRect; // Grabs rectangle upper left/lower right values 908 WindowRect.left = (long)0; // Set left value to 0 909 WindowRect.right = (long)width; // Set right value to requested width 910 WindowRect.top = (long)0; // Set top value to 0 911 WindowRect.bottom = (long)height; // Set bottom value to requested height 912 913 fullscreen = fullscreenflag; // Set the global fullscreen flag 914 915 /* 916 * The style CS_HREDRAW and CS_VREDRAW force the Window to redraw whenever it is resized. 917 * CS_OWNDC creates a private DC for the Window. Meaning the DC is not shared across applications. 918 * WndProc is the procedure that watches for messages in our program. 919 * No extra Window data is used so we zero the two fields. Then we set the instance. 920 * Next we set hIcon to NULL meaning we don't want an ICON in the Window, 921 * and for a mouse pointer we use the standard arrow. The background color doesn't matter 922 * (we set that in GL). We don't want a menu in this Window so we set it to NULL, 923 * and the class name can be any name you want. I'll use "OpenGL" for simplicity. 924 */ 925 hInstance = GetModuleHandle(NULL); // Grab an instance for our window 926 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Redraw on move, and own DC for window 927 wc.lpfnWndProc = (WNDPROC)WndProc; // WndProc handles message 928 wc.cbClsExtra = 0; // No extra window date 929 wc.cbWndExtra = 0; // No extra window date 930 wc.hInstance = hInstance; // set the instance 931 wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); // Load the default icon 932 wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Load the arrow pointer 933 wc.hbrBackground = NULL; // No background requried for GL 934 wc.lpszMenuName = NULL; // We don't want a menu 935 wc.lpszClassName = "OpenGL"; // set the class name 936 937 EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &DMsaved); // Save the current display state 938 939 if (!RegisterClass(&wc)) { // Attempt to register the window class 940 MessageBox(NULL, "Failed to register the window class.", "ERROR", MB_OK | MB_ICONEXCLAMATION); 941 return FALSE; // Exit and return false 942 } 943 944 if (fullscreen) { // attempt fullsreen model 945 946 /* 947 * There are a few very important things you should keep in mind when switching to full screen mode. 948 * Make sure the width and height that you use in fullscreen mode is the same as 949 * the width and height you plan to use for your window, and most importantly, 950 * set fullscreen mode BEFORE you create your window. 951 */ 952 DEVMODE dmScreenSettings; // Device mode 953 memset(&dmScreenSettings, 0, sizeof(dmScreenSettings)); // Make sure memory's cleared 954 dmScreenSettings.dmSize = sizeof(dmScreenSettings); // Size of devmode structure 955 dmScreenSettings.dmPelsWidth = width; // Select window width 956 dmScreenSettings.dmPelsHeight = height; // Select window height 957 dmScreenSettings.dmBitsPerPel = bits; // Select bits per pixel 958 dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; 959 960 /* 961 * In the line below ChangeDisplaySettings tries to switch to a mode that matches 962 * what we stored in dmScreenSettings. I use the parameter CDS_FULLSCREEN when switching modes, 963 * because it's supposed to remove the start bar at the bottom of the screen, 964 * plus it doesn't move or resize the windows on your desktop when you switch to 965 * fullscreen mode and back. 966 */ 967 //Try to set selected mode and get results. Note: CDS_FULLSCREEN gets rid of start bar 968 if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { 969 //If the mode fails, offer two options. Quit or run in a window 970 if (MessageBox(NULL, "The requested fullscreen mode is not supported by\n your video card. Use" 971 "windowed mode instead?", "GL", MB_YESNO | MB_ICONEXCLAMATION) == IDYES) 972 { 973 fullscreen = FALSE; // Select windowed mode (fullscreen=FLASE) 974 } 975 else { 976 // Pop up a message box letting user know the programe is closing. 977 MessageBox(NULL, "Program will now close.", "ERROR", MB_OK | MB_ICONSTOP); 978 return FALSE; // Exit and return FALSE 979 } 980 } 981 } 982 983 if (fullscreen) { // Are we still in fullscreen mode 984 985 /* 986 * If we are still in fullscreen mode we'll set the extended style to WS_EX_APPWINDOW, 987 * which force a top level window down to the taskbar once our window is visible. 988 * For the window style we'll create a WS_POPUP window. 989 * This type of window has no border around it, making it perfect for fullscreen mode. 990 991 * Finally, we disable the mouse pointer. If your program is not interactive, 992 * it's usually nice to disable the mouse pointer when in fullscreen mode. It's up to you though. 993 */ 994 dwExStyle = WS_EX_APPWINDOW; // Window extended style 995 dwStyle = WS_POPUP; // Window style 996 ShowCursor(FALSE); // Hide mosue pointer 997 } 998 else { 999 1000 /* 1001 * If we're using a window instead of fullscreen mode, 1002 * we'll add WS_EX_WINDOWEDGE to the extended style. This gives the window a more 3D look. 1003 * For style we'll use WS_OVERLAPPEDWINDOW instead of WS_POPUP. 1004 * WS_OVERLAPPEDWINDOW creates a window with a title bar, sizing border, 1005 * window menu, and minimize / maximize buttons. 1006 */ 1007 dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // Window extended style 1008 dwStyle = WS_OVERLAPPEDWINDOW; // Window style 1009 } 1010 1011 /* 1012 * By using the AdjustWindowRectEx command none of our OpenGL scene will be covered up by the borders, 1013 * instead, the window will be made larger to account for the pixels needed to draw the window border. 1014 * In fullscreen mode, this command has no effect. 1015 */ 1016 AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); // Adjust window to true resqusted 1017 1018 /* 1019 * WS_CLIPSIBLINGS and WS_CLIPCHILDREN are both REQUIRED for OpenGL to work properly. 1020 * These styles prevent other windows from drawing over or into our OpenGL Window. 1021 */ 1022 if (!(hWnd = CreateWindowEx(dwExStyle, // Extended style for the window 1023 "OpenGL", // Class name 1024 title, // Window title 1025 WS_CLIPSIBLINGS | // Requried window style 1026 WS_CLIPCHILDREN | // Requried window style 1027 dwStyle, // Select window style 1028 0, 0, // Window position 1029 WindowRect.right - WindowRect.left, // Calculate adjusted window width 1030 WindowRect.bottom - WindowRect.top, // Calculate adjusted window height 1031 NULL, // No parent window 1032 NULL, // No menu 1033 hInstance, // Instance 1034 NULL))) // Don't pass anything to WM_CREATE 1035 { 1036 KillGLWindow(); //Reset the display 1037 MessageBox(NULL, "Window creation error.", "ERROR", MB_OK | MB_ICONEXCLAMATION); 1038 return FALSE; // Retrurn FALSE; 1039 } 1040 1041 /* 1042 * aside from the stencil buffer and the (slow) accumulation buffer 1043 */ 1044 static PIXELFORMATDESCRIPTOR pfd = // pfd tells windows how we want things to be 1045 { 1046 sizeof(PIXELFORMATDESCRIPTOR), // Size of this pixel format descriptor 1047 1, // Version number 1048 PFD_DRAW_TO_WINDOW | // Format must support window 1049 PFD_SUPPORT_OPENGL | // Format must support OpenGL 1050 PFD_DOUBLEBUFFER, // Must support double buffer 1051 PFD_TYPE_RGBA, // Request an RGBA format 1052 bits, // Select our color depth 1053 0, 0, 0, 0, 0, 0, // Color bits ignored 1054 0, // No alpha buffer 1055 0, // shift bit ignored 1056 0, // No accumulation buffer 1057 0, 0, 0, 0, // Accumulation bits ignored 1058 16, // 16Bits Z_Buffer (depth buffer) 1059 0, // No stencil buffer 1060 0, // No auxiliary buffer 1061 PFD_MAIN_PLANE, // Main drawing layer 1062 0, // Reserved 1063 0, 0, 0 // Layer makes ignored 1064 }; 1065 1066 if (!(hDC = GetDC(hWnd))) { // Did we get a device context 1067 KillGLWindow(); // Reset the display 1068 MessageBox(NULL, "Can't create a GL device context.", "ERROR", MB_OK | MB_ICONEXCLAMATION); 1069 return FALSE; // Return FALSE 1070 } 1071 1072 if (!(PixelFormat = ChoosePixelFormat(hDC, &pfd))) { // Did window find a matching pixel format 1073 KillGLWindow(); // Reset the display 1074 MessageBox(NULL, "Can't find a suitable pixelformat.", "ERROR", MB_OK | MB_ICONEXCLAMATION); 1075 return FALSE; // Return FALSE; 1076 } 1077 1078 if (!SetPixelFormat(hDC, PixelFormat, &pfd)) { // Are we able to set the pixel format 1079 KillGLWindow(); // Reset the display 1080 MessageBox(NULL, "Can't set the pixelformat.", "ERROR", MB_OK | MB_ICONEXCLAMATION); 1081 return FALSE; // Return FALSE; 1082 } 1083 1084 if (!(hRC = wglCreateContext(hDC))) { // Are we able to rendering context 1085 KillGLWindow(); // Reset the display 1086 MessageBox(NULL, "Can't create a GL rendering context.", "ERROR", MB_OK | MB_ICONEXCLAMATION); 1087 return FALSE; // Return FASLE; 1088 } 1089 1090 if (!wglMakeCurrent(hDC, hRC)) { // Try to activate the rendering context 1091 KillGLWindow(); // Reset the display 1092 MessageBox(NULL, "Can't activate the GL rendering context.", "ERROR", MB_OK | MB_ICONEXCLAMATION); 1093 return FALSE; // Return FALSE 1094 } 1095 1096 /* 1097 * ReSizeGLScene passing the screen width and height to set up our perspective OpenGL screen. 1098 */ 1099 ShowWindow(hWnd, SW_SHOW); // Show the window 1100 SetForegroundWindow(hWnd); // slightly higher priority 1101 SetFocus(hWnd); // Sets keyboard focus to the window 1102 ReSizeGLScene(width, height); // Set up our perspective GL screen 1103 1104 /* 1105 * we can set up lighting, textures, and anything else that needs to be setup in InitGL(). 1106 */ 1107 if (!InitGL()) { // Initialize our newly created GL window 1108 KillGLWindow(); // Reset the display 1109 MessageBox(NULL, "Initialize Failed.", "ERROR", MB_OK | MB_ICONEXCLAMATION); 1110 return FALSE; // Return FALSE 1111 } 1112 return TRUE; 1113 } 1114 1115 LRESULT CALLBACK WndProc(HWND hWnd, // Handle for this window 1116 UINT uMsg, // Message for this window 1117 WPARAM wParam, // Additional message information 1118 LPARAM lParam) // Additional message information 1119 { 1120 switch (uMsg) { // Check for window message 1121 case WM_ACTIVATE: { // Check minimization state 1122 if (!HIWORD(wParam)) { 1123 active = TRUE; // Program is active 1124 } 1125 else { 1126 active = FALSE; // Program is no longer active 1127 } 1128 return 0; // Return to the message loop 1129 } 1130 case WM_SYSCOMMAND: { // Intercept system commands 1131 switch (wParam) { // Check system calls 1132 case SC_SCREENSAVE: // Screensaver trying to start 1133 case SC_MONITORPOWER: // Monitor trying to enter powersave 1134 return 0; // Prevent form happening 1135 } 1136 break; // Exit 1137 } 1138 case WM_CLOSE: { // Did we receive a close message 1139 PostQuitMessage(0); // Send a quit message 1140 return 0; 1141 } 1142 case WM_KEYDOWN: { // Is a key being held down 1143 keys[wParam] = TRUE; // if so, mark it as TRUE 1144 return 0; // Jump back 1145 } 1146 case WM_KEYUP: { // Has a key been released 1147 keys[wParam] = FALSE; // if so, mark it as FALSE 1148 return 0; // Jump back 1149 } 1150 case WM_SIZE: { // Resize the OpenGL window 1151 ReSizeGLScene(LOWORD(lParam), HIWORD(lParam)); // LoWord = width HiWord = height 1152 return 0; // Jump back 1153 } 1154 } 1155 return DefWindowProc(hWnd, uMsg, wParam, lParam); // Pass all unhandled message to DefWindwProc 1156 } 1157 1158 int WINAPI WinMain(HINSTANCE hInstance, // Instance 1159 HINSTANCE hPrevInstance, // Previous instance 1160 LPSTR lpCmdLine, // Command line parameters 1161 int nCmdShow) // Window show state 1162 { 1163 MSG msg; // Window message structure 1164 BOOL done = FALSE; // Bool variable to exit loop 1165 // Ask the user which screen mode they prefer 1166 if (MessageBox(NULL, "Would you like to run in fullscreen mode?", 1167 "Start fullscreen?", MB_YESNO | MB_ICONQUESTION) == IDNO) 1168 { 1169 fullscreen = FALSE; // Window mode 1170 } 1171 //******************************************************************************************************************************************/ 1172 //******************************************************************************************************************************************/ 1173 InitVars(); // Initialize variables 1174 1175 // Create our OpenGL window 1176 if (!CreateGLWindow("3D Shapes", 800, 600, 32, fullscreen)) { // (Modified) 1177 return 0; // Quit if window was not create 1178 } 1179 while (!done) { // Loop that runs until donw = TRUE 1180 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // Is there a message wating 1181 if (msg.message == WM_QUIT) { // Havw we received a quit message 1182 done = TRUE; // if so done = TRUE 1183 } 1184 else { // If not, deal with window message 1185 TranslateMessage(&msg); // Translate message 1186 DispatchMessage(&msg); // Dispatch message 1187 } 1188 } 1189 else { 1190 // Draw the scene. Watch for ESC key and quit message from DrawGLScene() 1191 if (active) { // Program active 1192 if (keys[VK_ESCAPE]) { // Was ESC pressed 1193 done = TRUE; // ESC signalled a quit 1194 } 1195 else { // Not time to quit, update screen 1196 idle(); // Advance Simulation 1197 DrawGLScene(); // Draw scene 1198 SwapBuffers(hDC); // Swap buffers (double buffering) 1199 } 1200 } 1201 //******************************************************************************************************************************************/ 1202 //******************************************************************************************************************************************/ 1203 if (!ProcessKeys()) 1204 return 0; 1205 /* 1206 * It allows us to press the F1 key to switch from fullscreen mode to 1207 * windowed mode or windowed mode to fullscreen mode. 1208 */ 1209 if (keys[VK_F1]) { // Is F1 being pressed 1210 keys[VK_F1] = FALSE; // If so make key FASLE 1211 KillGLWindow(); // Kill our current window 1212 fullscreen = !fullscreen; // Toggle fullscreen / window mode 1213 //Recreate our OpenGL window(modified) 1214 if (!CreateGLWindow("3D Shapes", 640, 480, 16, fullscreen)) { 1215 return 0; // Quit if window was not create 1216 } 1217 } 1218 } 1219 } 1220 // Shutdown 1221 KillGLWindow(); // Kill the window 1222 glDeleteTextures(4, texture); 1223 //******************************************************************************************************************************************/ 1224 //******************************************************************************************************************************************/ 1225 return (msg.wParam); // Exit the program 1226 }
Thanks for Nehe's tutorials, this is his home.
来源:https://www.cnblogs.com/clairvoyant/p/5795128.html