/*************************************************************************** * 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 #include #include #include #include #include template static T clip(T min, T max, T val) { if (val < min) return min; if (val > max) return max; return val; } template 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 &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 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(m_originX + event->x() - m_mouseLastX, m_textureSize); m_originY = rotate(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(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 moves; markMoves(QList()); 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(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(m_skyOffset + 0.001, 1); if (m_boardMode == TORUS) updateGL(); } }