这段代码花了我近一周时间学习。
球体之间的相互碰撞,球体与墙面之间的相互碰撞,球体与木桩之间的相互碰撞。小键盘+/-控制球体的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
