429 lines
13 KiB
C++
429 lines
13 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2009 by Stefan Bühler *
|
|
* stbuehler@web.de *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
***************************************************************************/
|
|
#include "field3d.h"
|
|
|
|
#include <QPainter>
|
|
#include <QMouseEvent>
|
|
#include <QWheelEvent>
|
|
|
|
#include <GL/gl.h>
|
|
|
|
#include <QGLPixelBuffer>
|
|
|
|
#include <math.h>
|
|
|
|
template<typename T>
|
|
static T clip(T min, T max, T val) {
|
|
if (val < min) return min;
|
|
if (val > max) return max;
|
|
return val;
|
|
}
|
|
|
|
template<typename T>
|
|
static T rotate(T angle, T bound = 360) {
|
|
while (angle < 0) angle += bound;
|
|
while (angle > bound) angle -= bound;
|
|
return angle;
|
|
}
|
|
|
|
namespace toruschess {
|
|
|
|
Field3D::Field3D(Game *game, QWidget *parent)
|
|
: QGLWidget(QGLFormat(QGL::DoubleBuffer | QGL::DepthBuffer | QGL::Rgba), parent), m_game(game), m_lib(new PieceLibrary(this)),
|
|
m_boardMode(PLANE), m_figureMode(FIG3D),
|
|
m_textureBuffer(0), m_textureID(0),
|
|
m_camDist(5) ,m_camRotX(45), m_camRotY(0), m_camRotZ(0),
|
|
m_originX(0), m_originY(0), m_mouseLastX(0), m_mouseLastY(0),
|
|
m_skySphere(0), m_skyTextureID(0), m_skyTimerID(0), m_skyOffset(0),
|
|
m_pickTextureID(0) {
|
|
for (int x = 0; x < 8; x++) for (int y = 0; y < 8; y++) m_marked[x][y] = false;
|
|
m_lib->setSize(m_textureSize / 8, m_textureSize / 8);
|
|
}
|
|
|
|
Field3D::~Field3D() {
|
|
freeTexture();
|
|
|
|
if (m_skySphere) gluDeleteQuadric(m_skySphere);
|
|
}
|
|
|
|
void Field3D::markMoves(const QList<Move> &moves) {
|
|
foreach(Move m, m_markedMoves) m_marked[m.to().x()][m.to().y()] = false;
|
|
m_markedMoves = moves;
|
|
foreach(Move m, m_markedMoves) m_marked[m.to().x()][m.to().y()] = true;
|
|
|
|
updateTexture();
|
|
updateGL();
|
|
}
|
|
|
|
QSize Field3D::sizeHint() const {
|
|
return QSize(640, 480);
|
|
}
|
|
|
|
void Field3D::initializeGL() {
|
|
glClearColor(0.0, 0.0, 0.0, 0.0);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
glEnable(GL_LIGHTING);
|
|
glEnable(GL_LIGHT0);
|
|
glDisable(GL_LIGHT1);
|
|
glEnable(GL_COLOR_MATERIAL);
|
|
{
|
|
const GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 },
|
|
inv[] = { 0.0, 0.0, 0.0, 0.0 };
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, white);
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, white);
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, inv);
|
|
glLightfv(GL_LIGHT0, GL_AMBIENT, white);
|
|
glLightfv(GL_LIGHT0, GL_DIFFUSE, inv);
|
|
glLightfv(GL_LIGHT1, GL_AMBIENT, inv);
|
|
glLightfv(GL_LIGHT1, GL_DIFFUSE, white);
|
|
}
|
|
|
|
glFogi(GL_FOG_MODE, GL_EXP2);
|
|
glFogf(GL_FOG_DENSITY, 1/50.0);
|
|
|
|
m_skySphere = gluNewQuadric();
|
|
gluQuadricTexture(m_skySphere, GL_TRUE);
|
|
|
|
createTexture();
|
|
connect(m_game, SIGNAL(updated()), this, SLOT(fieldUpdated()));
|
|
|
|
m_pickBuffer = new QGLPixelBuffer(QSize(1024, 1024));
|
|
|
|
m_skyTimerID = startTimer(20);
|
|
}
|
|
|
|
void Field3D::resizeGL(int w, int h) {
|
|
glViewport(0, 0, (GLint)w, (GLint)h);
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
gluPerspective(90.0, double(w)/h, 0.01, 1000);
|
|
|
|
updateCam();
|
|
}
|
|
|
|
void Field3D::paintGL() {
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
glEnable(GL_TEXTURE_2D);
|
|
glEnable(GL_LIGHT0);
|
|
glDisable(GL_LIGHT1);
|
|
|
|
if (m_boardMode == TORUS) {
|
|
glDisable(GL_FOG);
|
|
glDisable(GL_LIGHTING);
|
|
glBindTexture(GL_TEXTURE_2D, m_skyTextureID);
|
|
glMatrixMode(GL_TEXTURE);
|
|
glPushMatrix();
|
|
glTranslatef(m_skyOffset, 0, 0);
|
|
gluSphere(m_skySphere, 256, 64, 64);
|
|
glMatrixMode(GL_TEXTURE);
|
|
glPopMatrix();
|
|
glEnable(GL_LIGHTING);
|
|
|
|
double ox = 2.0*(m_originX/(double)m_textureSize), oy = 2.0*(m_originY/(double)m_textureSize);
|
|
glBindTexture(GL_TEXTURE_2D, m_textureID);
|
|
{
|
|
const int numc = 64, numt = 64;
|
|
const double rad1 = 4.0, rad2 = 2.0;
|
|
int i, j, k;
|
|
double s, t, x, y, z, twopi;
|
|
|
|
twopi = 2 * M_PI;
|
|
for (i = 0; i < numc; i++) {
|
|
glBegin(GL_QUAD_STRIP);
|
|
for (j = 0; j <= numt; j++) {
|
|
for (k = 1; k >= 0; k--) {
|
|
s = (i + k) % numc + 0.5;
|
|
t = j % numt;
|
|
|
|
x = (rad1+rad2*cos(s*twopi/numc))*cos(t*twopi/numt);
|
|
y = (rad1+rad2*cos(s*twopi/numc))*sin(t*twopi/numt);
|
|
z = rad2 * sin(s * twopi / numc);
|
|
glTexCoord2f(oy+double(i+k)/numc, ox+double(j)/numt);
|
|
glVertex3f(x, z, y);
|
|
}
|
|
}
|
|
glEnd();
|
|
}
|
|
}
|
|
} else {
|
|
const int repeat = 10;
|
|
double ox = -2.0*(m_originX/(double)m_textureSize), oy = 2.0*(m_originY/(double)m_textureSize);
|
|
glEnable(GL_FOG);
|
|
glEnable(GL_LIGHTING);
|
|
glBindTexture(GL_TEXTURE_2D, m_textureID);
|
|
glBegin(GL_QUADS);
|
|
glTexCoord2f(-repeat+ox, repeat+oy); glVertex3f(-10*repeat, 0, -10*repeat);
|
|
glTexCoord2f(-repeat+ox, -repeat+oy); glVertex3f(-10*repeat, 0, 10*repeat);
|
|
glTexCoord2f( repeat+ox, -repeat+oy); glVertex3f( 10*repeat, 0, 10*repeat);
|
|
glTexCoord2f( repeat+ox, repeat+oy); glVertex3f( 10*repeat, 0, -10*repeat);
|
|
glEnd();
|
|
|
|
#if 0
|
|
glDisable(GL_TEXTURE_2D);
|
|
glEnable(GL_LIGHT1);
|
|
glDisable(GL_LIGHT0);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
ox *= -10.0; oy *= 10.0;
|
|
ox += 10.0/16; oy += 10.0/16;
|
|
for (int i = 0; i < 8; i++) for (int j = 0; j < 8; j++) {
|
|
if (m_game->field()->piece(Pos(i, j)) == PAWN) {
|
|
if (m_game->field()->player(Pos(i, j)) == WHITE) {
|
|
glColor3f(1.0, 1.0, 1.0);
|
|
} else {
|
|
glColor3f(.1, .1, .1);
|
|
}
|
|
for (int x = -10; x <= 10; x++) for (int y = -10; y <= 10; y++) {
|
|
glPushMatrix();
|
|
glTranslatef(ox + 10.0/8.0*i + 10*x, 0, oy + 10.0/8.0*j + 10*y);
|
|
glRotatef(-90, 1, 0, 0);
|
|
m_lib->paint_pawn();
|
|
glPopMatrix();
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
glDisable(GL_TEXTURE_2D);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
bool Field3D::findPos(int mx, int my, Pos &p) {
|
|
glDisable(GL_FOG);
|
|
glDisable(GL_LIGHTING);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
if (m_boardMode == TORUS) {
|
|
double ox = 2.0*(m_originX/(double)m_textureSize), oy = 2.0*(m_originY/(double)m_textureSize);
|
|
glBindTexture(GL_TEXTURE_2D, m_pickTextureID);
|
|
{
|
|
const int numc = 64, numt = 64;
|
|
const double rad1 = 4.0, rad2 = 2.0;
|
|
int i, j, k;
|
|
double s, t, x, y, z, twopi;
|
|
|
|
twopi = 2 * M_PI;
|
|
for (i = 0; i < numc; i++) {
|
|
glBegin(GL_QUAD_STRIP);
|
|
for (j = 0; j <= numt; j++) {
|
|
for (k = 1; k >= 0; k--) {
|
|
s = (i + k) % numc + 0.5;
|
|
t = j % numt;
|
|
|
|
x = (rad1+rad2*cos(s*twopi/numc))*cos(t*twopi/numt);
|
|
y = (rad1+rad2*cos(s*twopi/numc))*sin(t*twopi/numt);
|
|
z = rad2 * sin(s * twopi / numc);
|
|
glTexCoord2f(oy+double(i+k)/numc, ox+double(j)/numt);
|
|
glVertex3f(x, z, y);
|
|
}
|
|
}
|
|
glEnd();
|
|
}
|
|
}
|
|
} else {
|
|
const int repeat = 10;
|
|
double ox = -2.0*(m_originX/(double)m_textureSize), oy = 2.0*(m_originY/(double)m_textureSize);
|
|
glBindTexture(GL_TEXTURE_2D, m_pickTextureID);
|
|
glBegin(GL_QUADS);
|
|
glTexCoord2f(-repeat+ox, repeat+oy); glVertex3f(-10*repeat, 0, -10*repeat);
|
|
glTexCoord2f(-repeat+ox, -repeat+oy); glVertex3f(-10*repeat, 0, 10*repeat);
|
|
glTexCoord2f( repeat+ox, -repeat+oy); glVertex3f( 10*repeat, 0, 10*repeat);
|
|
glTexCoord2f( repeat+ox, repeat+oy); glVertex3f( 10*repeat, 0, -10*repeat);
|
|
glEnd();
|
|
}
|
|
glDisable(GL_TEXTURE_2D);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
unsigned char pxbuf[3];
|
|
glReadPixels(mx, height() - 1 - my, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &pxbuf);
|
|
paintGL();
|
|
p = Pos(pxbuf[0] - 50, pxbuf[1] - 50);
|
|
return pxbuf[0] >= 50;
|
|
}
|
|
|
|
|
|
void Field3D::updateCam() {
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
glTranslatef(0, 0, -m_camDist);
|
|
glRotatef(m_camRotX,1,0,0);
|
|
glRotatef(m_camRotY,0,1,0);
|
|
glRotatef(m_camRotZ,0,0,1);
|
|
|
|
updateGL();
|
|
}
|
|
|
|
|
|
void Field3D::createTexture() {
|
|
freeTexture();
|
|
m_textureBuffer = new QImage(QSize(m_textureSize, m_textureSize), QImage::Format_ARGB32_Premultiplied);
|
|
|
|
updateTexture();
|
|
|
|
{
|
|
QImage *pickimage = new QImage(QSize(m_textureSize, m_textureSize), QImage::Format_ARGB32_Premultiplied);
|
|
pickimage->fill(Qt::black);
|
|
QPainter pt(pickimage);
|
|
int pieceSize = m_textureSize / 8;
|
|
for (int x = 0; x < 8; x++)
|
|
for (int y = 0; y < 8; y++) {
|
|
QRect prect(x * pieceSize, y * pieceSize, pieceSize - 1, pieceSize - 1);
|
|
pt.fillRect(prect, QBrush(QColor(x+50,y+50,50)));
|
|
}
|
|
m_pickTextureID = bindTexture(*pickimage);
|
|
// delete pickimage;
|
|
}
|
|
|
|
/* glBindTexture(GL_TEXTURE_2D, m_textureID);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);*/
|
|
|
|
QImage *skyimg = new QImage(":/media/skybox.jpg");
|
|
m_skyTextureID = bindTexture(*skyimg);
|
|
delete skyimg;
|
|
|
|
/* glBindTexture(GL_TEXTURE_2D, m_skyTextureID);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);*/
|
|
}
|
|
|
|
void Field3D::freeTexture() {
|
|
if (m_textureID != 0) {
|
|
deleteTexture(m_textureID);
|
|
m_textureID = 0;
|
|
}
|
|
if (m_skyTextureID != 0) {
|
|
deleteTexture(m_skyTextureID);
|
|
m_skyTextureID = 0;
|
|
}
|
|
if (m_pickTextureID != 0) {
|
|
deleteTexture(m_pickTextureID);
|
|
m_textureID = 0;
|
|
}
|
|
if (m_textureBuffer) {
|
|
delete m_textureBuffer;
|
|
m_textureBuffer = 0;
|
|
}
|
|
}
|
|
|
|
void Field3D::updateTexture() {
|
|
QPainter pt(m_textureBuffer);
|
|
pt.fillRect(QRect(0, 0, m_textureSize, m_textureSize), Qt::black);
|
|
|
|
int pieceSize = m_textureSize / 8;
|
|
for (int x = 0; x < 8; x++)
|
|
for (int y = 0; y < 8; y++) {
|
|
bool marked = m_marked[x][y];
|
|
int place = m_game->field()->place(Pos(x, y));
|
|
|
|
QRect prect(x * pieceSize, y * pieceSize, pieceSize - 1, pieceSize - 1);
|
|
m_lib->paint_board(pt, (x+y) % 2 == 1, prect);
|
|
m_lib->paint(pt, place, prect);
|
|
if (marked) {
|
|
int r = qMax(1, (pieceSize + pieceSize) / 36);
|
|
pt.setBrush(place != 0 ? Qt::green : Qt::white);
|
|
pt.setPen(Qt::red);
|
|
pt.drawEllipse(QPoint(prect.x() + pieceSize / 2, prect.y() + pieceSize / 2), r, r);
|
|
}
|
|
}
|
|
pt.setCompositionMode(QPainter::CompositionMode_Plus);
|
|
pt.fillRect(0, 0, m_textureSize, m_textureSize, Qt::black);
|
|
pt.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
|
if (m_textureID) deleteTexture(m_textureID);
|
|
m_textureID = bindTexture(*m_textureBuffer);
|
|
}
|
|
|
|
void Field3D::mousePressEvent(QMouseEvent *event) {
|
|
if (event->button() == Qt::LeftButton) {
|
|
QList<Move> moves;
|
|
Pos p;
|
|
if (findPos(event->x(), event->y(), p))
|
|
moves = m_game->field()->validMoves(p);
|
|
markMoves(moves);
|
|
updateTexture();
|
|
updateGL();
|
|
}
|
|
m_mouseLastX = event->x();
|
|
m_mouseLastY = event->y();
|
|
}
|
|
|
|
void Field3D::mouseMoveEvent(QMouseEvent *event) {
|
|
if (event->buttons() & Qt::RightButton) {
|
|
m_originX = rotate<int>(m_originX + event->x() - m_mouseLastX, m_textureSize);
|
|
m_originY = rotate<int>(m_originY + event->y() - m_mouseLastY, m_textureSize);
|
|
updateCam();
|
|
} else if (event->buttons() & Qt::MidButton) {
|
|
m_camRotY = rotate(m_camRotY + 0.5*(event->x() - m_mouseLastX));
|
|
// m_camRotX = clip<double>(10, 170, m_camRotX + 0.5*(event->y() - m_mouseLastY));
|
|
m_camRotX = rotate(m_camRotX + 0.5*(event->y() - m_mouseLastY));
|
|
updateCam();
|
|
}
|
|
m_mouseLastX = event->x();
|
|
m_mouseLastY = event->y();
|
|
}
|
|
|
|
void Field3D::mouseReleaseEvent(QMouseEvent *event) {
|
|
m_mouseLastX = event->x();
|
|
m_mouseLastY = event->y();
|
|
if (event->button() == Qt::LeftButton) {
|
|
if (m_markedMoves.size() == 0) return;
|
|
Pos from = m_markedMoves[0].from();
|
|
QList<Move> moves;
|
|
markMoves(QList<Move>());
|
|
Pos p;
|
|
if (findPos(event->x(), event->y(), p)) {
|
|
Move m(m_game->field(), from, p);
|
|
m_game->move(m);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Field3D::wheelEvent(QWheelEvent *event) {
|
|
double zoom = pow(1.2, - event->delta() / 120);
|
|
m_camDist = clip<double>(1.0, 40.0, m_camDist * zoom);
|
|
updateCam();
|
|
}
|
|
|
|
void Field3D::fieldUpdated() {
|
|
updateTexture();
|
|
updateGL();
|
|
}
|
|
|
|
void Field3D::setFigureMode(FigureMode m) {
|
|
m_figureMode = m;
|
|
updateTexture();
|
|
updateGL();
|
|
}
|
|
|
|
void Field3D::timerEvent(QTimerEvent *) {
|
|
m_skyOffset = rotate<double>(m_skyOffset + 0.001, 1);
|
|
if (m_boardMode == TORUS)
|
|
updateGL();
|
|
}
|
|
|
|
}
|