/**************************************************************************** Copyright (C) 2002-2006 Gilles Debunne (Gilles.Debunne@imag.fr) This file is part of the QGLViewer library. Version 2.2.4-1, released on December 12, 2006. http://artis.imag.fr/Members/Gilles.Debunne/QGLViewer libQGLViewer 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. libQGLViewer 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 libQGLViewer; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ #include "drawer.h" #include #include #include #include using namespace std; using namespace dvonn; using namespace qglviewer; namespace { static const float whiteColor[3] = {1.0f,1.0f,1.0f}; static const float blackColor[3] = {0.2f,0.2f,0.2f}; static const float redColor[3] = {1.0f,0.0f,0.0f}; static const float boardColor[3] = {1.0f,1.0f,1.0f}; static const float boardBorderColor[3] = {0.0f,0.0f,0.0f}; // Dimension (D=widht=height) of a case and of the inner border of the // case (B) const float caseD = 1.0f; const float caseB = 0.2f; // Dimension of a piece const float pieceRMax = (caseD-2.0f*caseB)/sqrtf(2.0f); const float pieceRMin = pieceRMax/3.0f; const float pieceH = 0.14f; const float pieceC = 0.25f; // curvature of normal on vring part const float pieceT = 0.5f; // piece transparence // Dimension of the label case float hLabelW = caseD; // must be caseD float hLabelH = caseD/3.0f; float vLabelW = caseD/3.0f; float vLabelH = caseD; // must be caseD // Dimension of the board const float boardB = 0.1f; const float boardW = Board::nbSpacesMaxOnRow()*caseD+2*boardB+2*vLabelW; const float boardH = Board::nbRows()*caseD+2*boardB+2*hLabelH; const float poolB = 0.1f; void tessAndTexture(float x0,float y0,float x2,float y2, float u0=0.0f,float v0=0.0f, float u2=1.0f,float v2=1.0f) { if (x0 > x2) swap(x0,x2); if (y0 > y2) swap(y0,y2); if (x2-x0>caseD) { float x1 = (x0+x2)/2.0f; float u1 = (u0+u2)/2.0f; tessAndTexture(x0,y0,x1,y2, u0,v0,u1,v2); tessAndTexture(x1,y0,x2,y2, u1,v0,u2,v2); } else if (y2-y0>caseD) { float y1 = (y0+y2)/2.0f; float v1 = (v0+v2)/2.0f; tessAndTexture(x0,y0,x2,y1, u0,v0,u2,v1); tessAndTexture(x0,y1,x2,y2, u0,v1,u2,v2); } else { glMultiTexCoord2f(GL_TEXTURE0,x0/caseD,y0/caseD); glMultiTexCoord2f(GL_TEXTURE1,u0,v0); glVertex2f(x0,y0); glMultiTexCoord2f(GL_TEXTURE0,x2/caseD,y0/caseD); glMultiTexCoord2f(GL_TEXTURE1,u2,v0); glVertex2f(x2,y0); glMultiTexCoord2f(GL_TEXTURE0,x2/caseD,y2/caseD); glMultiTexCoord2f(GL_TEXTURE1,u2,v2); glVertex2f(x2,y2); glMultiTexCoord2f(GL_TEXTURE0,x0/caseD,y2/caseD); glMultiTexCoord2f(GL_TEXTURE1,u0,v2); glVertex2f(x0,y2); } } const Vec normal[5] = { Vec(-1.0, 0.0, 0.0), Vec( 0.0, 1.0, 0.0), Vec( 1.0, 0.0, 0.0), Vec( 0.0, -1.0, 0.0), Vec(-1.0, 0.0, 0.0) }; void drawHRing(const float rMin, const float rMax, const float height) { // TODO : tabulate the cos and sin tables const int nbSteps = 24; glNormal3f(0.0, 0.0, 1.0); glBegin(GL_QUAD_STRIP); glVertex3f(rMin, 0.0, height); glVertex3f(rMax, 0.0, height); for (int i=1; i<=nbSteps; ++i) { const float angle = i * 2.0 * M_PI / nbSteps; const float cosine = cos(angle); const float sine = sin(angle); glVertex3f(rMin*cosine, rMin*sine, height); glVertex3f(rMax*cosine, rMax*sine, height); } glEnd(); } void drawVRing(const float hMin, const float hMax, const float r, bool in) { const int nbSteps = 24; const float normalSign = (in ? -1.0 : 1.0); float thMin = hMin; float thMax = hMax; if (in) swap(thMax,thMin); glBegin(GL_QUAD_STRIP); glNormal3f(normalSign, 0.0, 0.0); for (int i=0; i<=nbSteps; ++i) { const float angle = i * 2.0 * M_PI / nbSteps; const float cosine = cos(angle); const float sine = sin(angle); glNormal3fv(Vec(normalSign*cosine, normalSign*sine, +pieceC).unit()); glVertex3f(r*cosine, r*sine, thMax); glNormal3fv(Vec(normalSign*cosine, normalSign*sine, -pieceC).unit()); glVertex3f(r*cosine, r*sine, thMin); } glEnd(); } void drawPiece(const Color& p,float a=1.0f) { static const float* colors[3] = { redColor, whiteColor, blackColor }; const float* c = colors[static_cast(p)]; glColor4f(c[0]*a,c[1]*a,c[2]*a,a); drawHRing(pieceRMin, pieceRMax, pieceH); drawVRing(0.0f, pieceH, pieceRMax, false); drawVRing(0.0f, pieceH, pieceRMin, true); } void drawHLabel(unsigned int i) { glBegin(GL_QUADS); unsigned int d = Board::nbRows()/2; if (i=d) { float x = boardB+vLabelW-(d+1)*(caseD/2.0f)+i*hLabelW; float y = boardB+hLabelH+Board::nbRows()*caseD; tessAndTexture(x,y, x+hLabelW-hLabelH,y+hLabelH, 2.0f,2.0f, 2.0f,2.0f); tessAndTexture(x+hLabelW-hLabelH,y, x+hLabelW,y+hLabelH, 0.0f,0.0f, 1.0f,1.0f); } glEnd(); } void drawVLabel(int i) { glBegin(GL_QUADS); int d = static_cast(Board::nbRows()/2); int e = abs(d-i); float shift = e*caseD/2.0f; float x = boardB+shift; float y = boardB+hLabelH+i*vLabelH; tessAndTexture(x,y, x+vLabelW,y+vLabelH/3.0f, 2.0f,2.0f, 2.0f,2.0f); tessAndTexture(x,y+vLabelH/3.0f, x+vLabelW,y+2.0f*vLabelH/3.0f, 0.0f,0.0f, 1.0f,1.0f); tessAndTexture(x,y+2.0f*vLabelH/3.0f, x+vLabelW,y+vLabelH, 2.0f,2.0f, 2.0f,2.0f); x = boardB+shift+vLabelW+(Board::nbSpacesMaxOnRow()-e)*caseD; y = boardB+hLabelH+i*vLabelH; tessAndTexture(x,y, x+vLabelW,y+vLabelH/3.0f, 2.0f,2.0f, 2.0f,2.0f); tessAndTexture(x,y+vLabelH/3.0f, x+vLabelW,y+2.0f*vLabelH/3.0f, 0.0f,0.0f, 1.0f,1.0f); tessAndTexture(x,y+2.0f*vLabelH/3.0f, x+vLabelW,y+vLabelH, 2.0f,2.0f, 2.0f,2.0f); glEnd(); } GLuint loadTexture(const QString fileName) { QImage img("images/"+fileName); if (img.isNull()) { QMessageBox::critical(NULL, "Image not found", "Unable to load texture from "+fileName); exit(1); // TODO: be nicer! } // 1E-3 needed. Just try with width=128 and see ! if ( ( img.width() != 1<<(int)(1+log(img.width() -1+1E-3) / log(2.0)) ) || ( img.height() != 1<<(int)(1+log(img.height()-1+1E-3) / log(2.0))) ) { QMessageBox::critical(NULL, "Wrong image dimensions", "Texture dimensions are not powers of 2 in "+fileName); exit(1); // TODO: be nicer! } // cout << "Loaded "<begin(),(*s)->end(),(*s).stackCoord()); Board::Ghost* g = NULL; d.drawTransparentPieces(g->stack.begin(),g->stack.end(),g->coord); } } //************************************************************ // Implementation of Drawer //************************************************************ Drawer::Drawer() : showTextures_(true) { } void Drawer::toggleTexture(bool b) { showTextures_ = b; } Drawer::~Drawer() { } void Drawer::init() { static const QStringList texFilenames = QStringList()<<"board.png" <<"case.png"; for (QStringList::const_iterator it = texFilenames.begin(); it != texFilenames.end();++it) { textures_[*it] = loadTexture(*it); } for (unsigned int i=0;i::const_iterator fter = textures_.find(t); if (showTextures_ && fter != textures_.end()) { glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,fter->second); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); } else { glDisable(GL_TEXTURE_2D); } glActiveTexture(GL_TEXTURE1); glDisable(GL_TEXTURE_2D); } void Drawer::startTexture(const QString& t,GLuint to) const { glPushAttrib(GL_ENABLE_BIT); glActiveTexture(GL_TEXTURE0); map::const_iterator fter = textures_.find(t); if (showTextures_ && fter != textures_.end()) { glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,fter->second); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); } else { glDisable(GL_TEXTURE_2D); } glActiveTexture(GL_TEXTURE1); if (showTextures_) { glBindTexture(GL_TEXTURE_2D,to); glEnable(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); } else { glDisable(GL_TEXTURE_2D); } } void Drawer::startTexture() const { glPushAttrib(GL_ENABLE_BIT); glActiveTexture(GL_TEXTURE0); glDisable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE1); glDisable(GL_TEXTURE_2D); } void Drawer::endTexture() const { glPopAttrib(); } void translateTo(Board::Coord c,float h = 0.0f) { static const float shifts[5] = { 1.0f,0.5f,0.0f,-0.5f,-1.0f }; glTranslatef(boardB+vLabelW+c.x()*caseD+shifts[c.y()]*caseD, boardB+hLabelH+c.y()*caseD, h); } void Drawer::drawPieces(const Board::ConstStackHandle& s) const { startTexture(); glPushMatrix(); translateTo(s.stackCoord()); glTranslatef(0.5f*caseD,0.5f*caseD,0.0f); for (Stack::const_iterator iter = s->begin(), istop= s->end(); iter != istop;++iter) { drawPiece((*iter)->color()); glTranslatef(0.0f,0.0f,pieceH); } glPopMatrix(); endTexture(); } void Drawer::drawStatus(const Board::ConstStackHandle& s,QGLViewer* v) const { startTexture(); glPushMatrix(); translateTo(s.stackCoord(),s->height()*pieceH+pieceH/2.0f); glColor3f(1.0f,1.0f,0.0f); v->renderText(0.5f*caseD,0.5f*caseD,0.0f,QString("%1").arg(s.stackStatus())); glPopMatrix(); endTexture(); } void Drawer::drawTransparentPiece(Color col, const Board::ConstStackHandle& c,float a) const { startTexture(); glPushMatrix(); translateTo(c.stackCoord(),c->height()*pieceH); glTranslatef(0.5f*caseD,0.5f*caseD,0.0f); drawPiece(col,a); glPopMatrix(); endTexture(); } template void Drawer::drawTransparentPieces(O first,O last,const Board::Coord& c,float h,float a) const { startTexture(); glPushMatrix(); translateTo(c,h*pieceH); glTranslatef(0.5f*caseD,0.5f*caseD,0.0f); while (first != last) { drawPiece((*first++)->color(),a); glTranslatef(0.0f,0.0f,pieceH); } glPopMatrix(); endTexture(); } void Drawer::draw(const Board::ConstStackHandle& c) const { glPushMatrix(); translateTo(c.stackCoord()); startTexture("case.png"); glColor3fv(boardColor); glNormal3fv(boardUpVector()); glBegin(GL_QUADS); tessAndTexture(0.0f ,0.0f,caseD,caseD); glEnd(); endTexture(); glPopMatrix(); } void Drawer::drawComplement(bool showLabels) const { glPushAttrib(GL_ALL_ATTRIB_BITS); glNormal3fv(boardUpVector()); glColor3fv(boardColor); for (unsigned int i=0;i 0.0f) { glColor3fv(boardBorderColor); tessAndTexture(0.0f ,0.0f, boardW,boardB); tessAndTexture(0.0f ,boardH-boardB, boardW,boardH); tessAndTexture(0.0f,boardB, boardB,boardH-boardB); tessAndTexture(boardW-boardB,boardB, boardW,boardH-boardB); } glColor3fv(boardColor); int d = Board::nbRows()/2; tessAndTexture(boardB, boardB, boardB+vLabelW+(d+1)*caseD/2.0f, boardB+hLabelH); tessAndTexture(boardB, boardH-boardB-hLabelH, boardB+vLabelW+(d+1)*caseD/2.0f-hLabelW, boardH-boardB); tessAndTexture(boardW-(boardB+vLabelW+(d+1)*caseD/2.0f)+hLabelW, boardB, boardW-boardB, boardB+hLabelH); tessAndTexture(boardW-(boardB+vLabelW+(d+1)*caseD/2.0f), boardH-boardB-hLabelH, boardW-boardB, boardH-boardB); for (int i=0;iheight())*pieceH/L; const float t1 = 1.0f-(b.heightMax()+1-b.stackAt(m.dst)->height())*pieceH/L; Board::ConstStackHandle src = b.stackAt(m.src); glPushMatrix(); startTexture(); if (t<=t0) { t /= t0; float srcH = src->height()*pieceH; float dstH = b.heightMax()*pieceH+pieceH; translateTo(m.src); glTranslatef(0.5f*caseD,0.5f*caseD, (1-t)*srcH+t*dstH); for (Stack::const_iterator iter = src->begin(), istop= src->end(); iter != istop;++iter) { drawPiece((*iter)->color()); glTranslatef(0.0f,0.0f,pieceH); } } else if (t<=t1) { t = (t-t0)/(t1-t0); static const float shifts[5] = { 1.0f,0.5f,0.0f,-0.5f,-1.0f }; float srcX = boardB+vLabelW+m.src.x()*caseD+shifts[m.src.y()]*caseD; float srcY = boardB+hLabelH+m.src.y()*caseD; float dstX = boardB+vLabelW+m.dst.x()*caseD+shifts[m.dst.y()]*caseD; float dstY = boardB+hLabelH+m.dst.y()*caseD; glTranslatef((1.0f-t)*srcX+t*dstX+0.5f*caseD, (1.0f-t)*srcY+t*dstY+0.5f*caseD, b.heightMax()*pieceH+pieceH); for (Stack::const_iterator iter = src->begin(), istop= src->end(); iter != istop;++iter) { drawPiece((*iter)->color()); glTranslatef(0.0f,0.0f,pieceH); } } else { t = (t-t1)/(1.0f-t1); float srcH = b.heightMax()*pieceH+pieceH; float dstH = b.stackAt(m.dst)->height()*pieceH; translateTo(m.dst); glTranslatef(0.5f*caseD,0.5f*caseD, (1-t)*srcH+t*dstH); for (Stack::const_iterator iter = src->begin(), istop= src->end(); iter != istop;++iter) { drawPiece((*iter)->color()); glTranslatef(0.0f,0.0f,pieceH); } } endTexture(); glPopMatrix(); } float Drawer::estimateDrawMoveLength(const Board& b,const Game::Move m) const { static const float shifts[5] = { 1.0f,0.5f,0.0f,-0.5f,-1.0f }; float srcX = boardB+vLabelW+m.src.x()*caseD+shifts[m.src.y()]*caseD; float srcY = boardB+hLabelH+m.src.y()*caseD; float dstX = boardB+vLabelW+m.dst.x()*caseD+shifts[m.dst.y()]*caseD; float dstY = boardB+hLabelH+m.dst.y()*caseD; return (Vec(dstX-srcX,dstY-srcY,0.0f).norm()+ (b.heightMax()+1-b.stackAt(m.src)->height())*pieceH+ (b.heightMax()+1-b.stackAt(m.dst)->height())*pieceH)/caseD; } Vec Drawer::boardCenter() const { return Vec(boardW/2.0f,boardH/2.0f,0.0f); } Vec Drawer::boardUpVector() const { return Vec(0.0f,0.0f,1.0f); } float Drawer::boardRadius() const { return Vec(boardW/2.0f,boardH/2.0f,0.0).norm(); } Vec Drawer::defaultEyePosition() const { return boardCenter()+boardRadius()*Vec(0.0, -1.7, 1.0); }