1 /*******************************************************************************
2 Copyright 2008 Ian Wadham <ianw2@optusnet.com.au>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *******************************************************************************/
20 #include "gameglview.h"
23 #include <QStringList>
27 #include <QMouseEvent>
32 GameGLView::GameGLView(Game
* g
, QWidget
* parent
)
33 : QGLWidget(QGLFormat (QGL::AlphaChannel
), parent
),
34 bgColor (QColor (0, 0, 35))
36 // Save a pointer to the Game object that controls everything.
39 this->setMinimumSize (450, 300);
41 // Set the amount of bevelling on a cubie's edge to its default value.
44 // Set colors to full intensity (no blinking).
47 // Note: Do not use OpenGL functions here.
48 // Initialize OpenGL in initializeGL() instead!
50 const GLubyte
* v
= glGetString(GL_VERSION
);
51 printf ("GL Version %s\n", v
);
55 void GameGLView::initializeGL()
57 // Make glClear() clear to a deep-blue background.
58 qglClearColor (bgColor
);
60 // Disable dithering which is usually not needed
63 // Set up fixed lighting.
64 glMatrixMode(GL_MODELVIEW
);
70 void GameGLView::resizeGL(int w
, int h
)
72 // Make use of the whole view.
73 glViewport(0, 0, w
, h
);
75 // Setup a 3D projection matrix
76 glMatrixMode(GL_PROJECTION
);
78 gluPerspective(viewAngle
, ((GLdouble
)w
)/((GLdouble
)h
), minZ
, maxZ
);
79 glMatrixMode(GL_MODELVIEW
);
81 // Re-position the view-labels.
82 game
->setSceneLabels ();
86 float GameGLView::colors
[7] [3] = {
87 {0.2, 0.2, 0.2}, // Dark grey.
88 {1.0, 0.5, 0.1}, // Orange.
89 {0.7, 0.0, 0.0}, // Red.
90 {1.0, 1.0, 0.0}, // Yellow.
91 {0.0, 0.5, 0.0}, // Green.
92 {0.0, 0.0, 0.8}, // Blue.
93 {0.9, 0.9, 0.8} // Off-white.
97 void GameGLView::paintGL()
100 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
102 // Reset the modelview matrix. By doing so we also reset the "camera" that
103 // is the eye position, so that it is now at (0,0,0), which is also the
104 // center of the screen. We are looking down the negative Z axis, towards
105 // (0,0,-1), with "up" being at (0,1,0).
108 if (checkGLError()) {
109 std::cerr
<< "OpenGL error detected before drawScene()" << std::endl
;
114 if (checkGLError()) {
115 std::cerr
<< "OpenGL error detected after drawScene()" << std::endl
;
120 void GameGLView::dumpExtensions()
122 // OpenGL Extension detection.
123 QString s
= (const char*)glGetString(GL_EXTENSIONS
);
125 s
+= (const char*)gluGetString(GLU_EXTENSIONS
);
126 QStringList extensions
= s
.split (" ", QString::SkipEmptyParts
);
127 for (int i
= 0; i
< extensions
.count(); i
++)
129 std::cout
<< extensions
[i
].toLatin1().data() << std::endl
;
133 static bool printed
= false;
135 static float ambient
[] = {0.0, 0.0, 0.0, 1.0};
136 static float diffuse
[] = {1.0, 1.0, 1.0, 1.0};
138 // Directional light, at infinity (parameter 4, w=0.0), shining
139 // towards -Z. No attenuation. Diffuse, no spotlight effect.
140 static float position0
[] = {0.0, 0.0, 1.0, 0.0};
142 // With lights off, 100% ambient gives a flat, dull, fairly well-lit picture.
143 // With lights on, it makes little difference to specular effect, but makes
144 // the strong colors of stickers look a bit washed out. So choose 60%.
145 static float lmodel_ambient
[] = {0.6, 0.6, 0.6, 1.0};
147 // Controls SPREAD of specular reflections (128.0 = small highlight).
148 static float material_shininess
= 60.0;
150 // Controls intensity and color of specular reflections.
151 static float material_specular
[] = {0.5, 0.6, 0.5, 1.0};
153 // Trying to light the insides of the cubies is a waste of time.
154 static float lmodel_twoside
[] = {GL_FALSE
};
156 void GameGLView::turnOnLighting ()
158 // There is just one light.
159 glLightfv (GL_LIGHT0
, GL_AMBIENT
, ambient
);
160 glLightfv (GL_LIGHT0
, GL_DIFFUSE
, diffuse
);
161 glLightfv (GL_LIGHT0
, GL_POSITION
, position0
);
163 glLightModelfv (GL_LIGHT_MODEL_AMBIENT
, lmodel_ambient
);
164 glLightModelfv (GL_LIGHT_MODEL_TWO_SIDE
, lmodel_twoside
);
166 // This gives a rolling-sheen effect to the specularity.
167 // Not all stickers on a face light up at once.
168 glLightModeli (GL_LIGHT_MODEL_LOCAL_VIEWER
, GL_TRUE
);
170 glEnable (GL_LIGHTING
);
171 glEnable (GL_LIGHT0
);
173 glEnable (GL_DEPTH_TEST
);
174 glEnable (GL_NORMALIZE
);
176 // Do not render the backs (interiors) of cubie and sticker faces. The
177 // "front" faces are those for which the order of drawing of the vertices
178 // is anti-clockwise, as seen from the outside looking towards the centre.
179 glEnable (GL_CULL_FACE
);
181 glShadeModel (GL_FLAT
);
182 glMaterialf (GL_FRONT
, GL_SHININESS
, material_shininess
);
183 glMaterialfv (GL_FRONT
, GL_SPECULAR
, material_specular
);
185 // This tracks color CHANGES and updates the corresponding glMaterial*.
186 glEnable (GL_COLOR_MATERIAL
);
187 glColorMaterial (GL_FRONT
, GL_AMBIENT_AND_DIFFUSE
);
191 QPoint
GameGLView::getMousePosition ()
194 p
= mapFromGlobal (QCursor::pos()); // Convert to window co-ordinates.
196 // Convert Y co-ordinate to OpenGL convention (zero at bottom of window).
197 p
.setY (height() - p
.y());
202 void GameGLView::pushGLMatrix ()
208 void GameGLView::moveGLView (float xChange
, float yChange
, float zChange
)
210 glTranslatef (xChange
, yChange
, zChange
);
214 void GameGLView::rotateGLView (float degrees
, float x
, float y
, float z
)
216 glRotatef (degrees
, x
, y
, z
);
220 void GameGLView::popGLMatrix ()
226 void GameGLView::setBevelAmount (int bevelPerCent
)
228 bevelAmount
= ((float) bevelPerCent
)/100.0;
232 void GameGLView::drawACubie (float size
, float centre
[], int axis
, int angle
)
234 float lenA
= 0.5 * size
; // Half of a cubie's outside dimension.
235 float bevel
= bevelAmount
* lenA
; // The size of the bevel at the edges.
236 float lenB
= lenA
- bevel
; // Half of a face's inside dimension.
238 float p
[nAxes
]; // The point to be drawn currently.
239 float normal
[nAxes
]; // The current normal vector (used in lighting).
240 // Needs to be of length 1 for best results.
241 float r2
= 0.7071067; // (1 / sqrt (2.0)), used for bevel normals.
242 float r3
= 0.5773502; // (1 / sqrt (3.0)), used for corner normals.
244 printed
= true; // Suppress printing of co-ordinates.
246 // If the cubie is moving, rotate it around the required axis of the cube.
248 GLfloat v
[nAxes
] = {0.0, 0.0, 0.0};
251 glRotatef ((GLfloat
) (-angle
), v
[0], v
[1], v
[2]);
254 glPushAttrib(GL_CURRENT_BIT
| GL_ENABLE_BIT
);
257 glColor3fv (colors
[0]); // The base color of a cubie, without stickers.
259 // Bring the centre to the origin during drawing.
260 glTranslatef ((GLfloat
) centre
[X
],
262 (GLfloat
) centre
[Z
]);
264 // Draw three pairs of faces of the cubie and twelve bevelled edges.
268 // The coordinate on the axis thru the face does not change, but we must
269 // work out what the other two coordinates are, in cyclical order: i.e.
270 // X-axis (0) --> Y,Z (coordinates 1 and 2 change),
271 // Y-axis (1) --> Z,X (coordinates 2 and 0 change),
272 // Z-axis (2) --> X,Y (coordinates 0 and 1 change).
277 // Draw two opposite faces of the cubie and two bevelled edges each.
280 // Draw one face of the cubie.
281 // For (i,j) = (0,0), (0,1), (1,0) and (1,1), we will get values
282 // (i,k) = (0,0), (0,1), (1,1) and (1,0) and will be drawing
283 // vertices going around a square face of the cubie.
285 normal
[coord1
] = 0.0;
286 normal
[coord2
] = 0.0;
287 normal
[axis
] = (2*face
-1) * 1.0; // -1.0 or +1.0.
288 glNormal3fv (normal
); // Used by lighting.
290 p
[axis
] = (2*face
-1) * lenA
; // Value -lenA or +lenA.
291 // The axes are chosen so that the vertices go in "GL front face"
292 // order (ie. anti-clockwise), as viewed from outside the cubie.
294 coord1
= (axis
+ 2) % 3;
295 coord2
= (axis
+ 1) % 3;
298 coord1
= (axis
+ 1) % 3;
299 coord2
= (axis
+ 2) % 3;
303 p
[coord1
] = (2*i
-1) * lenB
; // Value -lenB or +lenB.
305 int k
= (i
+ j
) % 2; // Takes values 0 1 1 0.
306 p
[coord2
] = (2*k
-1) * lenB
; // Value -lenB or +lenB.
307 glVertex3fv (p
); // Draw one of 4 vertices.
309 printf("Axis[%d] %5.2f %5.2f %5.2f\n",
310 axis
, p
[X
], p
[Y
], p
[Z
]);
314 // Draw two of this face's bevelled edges.
315 // Eventually, for 6 faces, all 12 edges will have been drawn.
316 coord1
= (axis
+ 1) % 3;
317 coord2
= (axis
+ 2) % 3;
318 normal
[axis
] = (2*face
-1) * r2
;
320 normal
[coord1
] = (2*edge
-1) * r2
;
321 normal
[coord2
] = 0.0;
322 glNormal3fv (normal
); // Used by lighting.
325 p
[axis
] = (2*face
-1) * (lenA
- ((edge
+ i
)%2) * bevel
);
326 p
[coord1
] = (2*edge
-1) * lenB
+ (edge
- i
) * bevel
;
328 int k
= (i
+ j
) % 2; // Takes values 0 1 1 0.
329 // p[coord2] = -lenB, +lenB, +lenB, -lenB on the + face
330 // and = +lenB, -lenB, -lenB, +lenB on the - face,
331 // thus ensuring anti-clockwise drawing of all 4 bevels
332 // as you view each face from the outside loooking in.
333 p
[coord2
] = (2*face
-1) * (2*k
-1) * lenB
;
334 glVertex3fv (p
); // Draw 1 of 4 vertices.
336 printf("Edge[%d] %5.2f %5.2f %5.2f\n",
337 edge
, p
[X
], p
[Y
], p
[Z
]);
348 // Draw the 8 chiselled corners on the back and front faces of the cubie.
349 glBegin(GL_TRIANGLES
);
351 float z
= ((float) 2*face
-1); // Z direction: +r3 or -r3.
353 float x
= ((float) 2*i
-1); // X direction: +r3 or -r3.
355 float y
= ((float) 2*j
-1); // Y direction: +r3 or -r3.
357 glNormal3f (x
*r3
, y
*r3
, z
*r3
); // Used by lighting.
359 // Draw the triangular facet at this corner of the face.
360 // The vertices must go in "GL front face" order (ie. anti-
361 // clockwise), as viewed from outside the cubie.
363 if ((x
* y
* z
) > 0.0) {
364 // If 0 or 2 negatives, go round the vertices one way.
365 glVertex3f (x
*lenA
, y
*lenB
, z
*lenB
); // A
366 glVertex3f (x
*lenB
, y
*lenA
, z
*lenB
); // B
367 glVertex3f (x
*lenB
, y
*lenB
, z
*lenA
); // C
370 // If 1 or 3 negatives, go round the vertices the other way.
371 glVertex3f (x
*lenA
, y
*lenB
, z
*lenB
); // A
372 glVertex3f (x
*lenB
, y
*lenB
, z
*lenA
); // C
373 glVertex3f (x
*lenB
, y
*lenA
, z
*lenB
); // B
376 // printf("Corner[%d] %5.2f %5.2f %5.2f\n",
391 void GameGLView::finishCubie ()
393 // If the cubie just drawn is moving, restore the OpenGL co-ordinate axes.
398 void GameGLView::drawASticker (float size
, int color
, bool blinking
,
399 int faceNormal
[], float faceCentre
[])
401 float lenA
= 0.5 * size
; // Half of a cubie's outside dimension.
402 float bevel
= bevelAmount
* lenA
; // The size of the bevel at the edges.
403 float lenB
= lenA
- bevel
; // Half the sticker's dimension.
405 float p
[nAxes
]; // The point to be drawn currently.
406 float normal
[nAxes
]; // The normal vector (used in lighting).
408 int axis1
= 0, axis2
= 1; // Two axes in the plane of the sticker.
412 glPushAttrib(GL_CURRENT_BIT
| GL_ENABLE_BIT
);
415 // Set the color of this sticker.
418 mColor
[i
] = blinkIntensity
* colors
[color
] [i
];
420 glColor3fv (mColor
); // Dimmed color.
423 glColor3fv (colors
[color
]); // Normal color.
426 // Bring the centre of the sticker to the origin during drawing.
427 glTranslatef ((GLfloat
) faceCentre
[X
],
428 (GLfloat
) faceCentre
[Y
],
429 (GLfloat
) faceCentre
[Z
]);
433 if (faceNormal
[axis
] != 0) {
434 // One component of the faceNormal vector is 1: the others are zero.
435 normal
[axis
] = (float) faceNormal
[axis
];
437 // The sticker is offset from the cubie's face, along the normal.
438 p
[axis
] = 0.01 * size
* normal
[axis
];
440 // The coordinate on the normal does not change, but we must work
441 // out what the other two coordinates are, in cyclical order: i.e.
442 // X-axis (0) --> Y,Z (coordinates 1 and 2 change),
443 // Y-axis (1) --> Z,X (coordinates 2 and 0 change),
444 // Z-axis (2) --> X,Y (coordinates 0 and 1 change).
446 // Ensure that the outside face of the sticker is drawn in
447 // anti-clockwise sequence, so GL_CULL_FACE culls the inside one.
449 if (faceNormal
[axis
] > 0) {
450 axis1
= (axis
+ 1) % 3;
451 axis2
= (axis
+ 2) % 3;
454 axis2
= (axis
+ 1) % 3;
455 axis1
= (axis
+ 2) % 3;
461 glNormal3fv (normal
); // Used by lighting.
463 p
[axis1
] = + lenB
; // Set the first vertex of the sticker.
465 glVertex3fv (p
); // Draw the first vertex, eg. (+X,+Y) quadrant.
467 glVertex3fv (p
); // Draw the second vertex, eg. (+X,-Y) quadrant.
469 glVertex3fv (p
); // Draw the third vertex, eg. (-X,-Y) quadrant.
471 glVertex3fv (p
); // Draw the fourth vertex, eg. (+X,-Y) quadrant.
481 void GameGLView::setBlinkIntensity (float intensity
)
483 blinkIntensity
= intensity
;
487 bool GameGLView::checkGLError()
489 GLenum error
= glGetError();
490 if (error
!= GL_NO_ERROR
)
492 // std::cerr << "An OpenGL error occurred" << std::endl;
499 void GameGLView::mousePressEvent(QMouseEvent
* e
)
501 // Convert Y co-ordinate to OpenGL convention (zero at bottom of window).
502 game
->handleMouseEvent (ButtonDown
, e
->button(),
503 e
->pos().x(), height() - e
->pos().y());
507 void GameGLView::mouseReleaseEvent(QMouseEvent
* e
)
509 // Convert Y co-ordinate to OpenGL convention (zero at bottom of window).
510 game
->handleMouseEvent (ButtonUp
, e
->button(),
511 e
->pos().x(), height() - e
->pos().y());
514 // End gameglview.cpp