LOG750-LAB2/src/viewer/simpleViewer.cpp

553 lines
19 KiB
C++

/****************************************************************************
Copyright (C) 2002-2008 Gilles Debunne. All rights reserved.
This file is part of the QGLViewer library version 2.3.6.
http://www.libqglviewer.com - contact@libqglviewer.com
This file may be used under the terms of the GNU General Public License
versions 2.0 or 3.0 as published by the Free Software Foundation and
appearing in the LICENSE file included in the packaging of this file.
In addition, as a special exception, Gilles Debunne gives you certain
additional rights, described in the file GPL_EXCEPTION in this package.
libQGLViewer uses dual licensing. Commercial/proprietary software must
purchase a libQGLViewer Commercial License.
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*****************************************************************************/
#include "simpleViewer.h"
#include "../interfaces/visitor.h"
#include "../glnodes/scenegroup.h"
#include "../glnodes/shapes.h"
#include <qopengl.h>
#include <QOpenGLShaderProgram>
#include <qopengl.h>
#include <iostream>
#include <stack>
#include <QMouseEvent>
#include <typeinfo>
using namespace std;
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
#define GRID_SIZE 10;
namespace
{
const int numVerticesCube = 12*3;
const double frame_limit = 5;
const double inc_mult = 5;
const double inc_offset = 1.05;
enum Buffer_IDs { ArrayBuffer, NumBuffers };
int vUVLocation;
GLuint Buffers[NumBuffers];
int m_lDirectionLocation;
int m_vNormalLocation;
int m_normalMatrixLoc;
}
class SkyboxCamera : public qglviewer::Camera
{
virtual qreal zNear() const { return 0.01; }
virtual qreal zFar() const { return 100.0; }
};
Viewer::Viewer()
{
activeColor = new QColor(255, 255, 255, 255);
activeCell = nullptr;
activeShape = 0;
}
Viewer::~Viewer()
{
cleanup();
}
void Viewer::cleanup()
{
makeCurrent();
// Delete shaders
delete m_program;
m_program = 0;
// Delete buffers
glDeleteBuffers(NumBuffers, m_Buffers);
glDeleteVertexArrays(NumVAOs, m_VAOs);
doneCurrent();
}
void Viewer::drawSkybox()
{
// Use the Skybox Shaders
skyboxRenderShaderProgram->bind();
s_texture->bind();
// Get projection and camera transformations
QMatrix4x4 projectionMatrix;
QMatrix4x4 modelViewMatrix;
camera()->getProjectionMatrix(projectionMatrix);
camera()->getModelViewMatrix(modelViewMatrix);
// Increase size of skybox
modelViewMatrix.scale(100);
skyboxRenderShaderProgram->setUniformValue(s_projMatrixLocation, projectionMatrix);
skyboxRenderShaderProgram->setUniformValue(s_mvMatrixLocation, modelViewMatrix);
// skyboxRenderShaderProgram->setAttributeValue(s_colorLocation, );
int faces = floor(numVerticesCube/6);
for(int i = 0; i < faces; i++){ // 6 vertexes par face
glBindVertexArray(m_VAOs[VAO_Cube]);
glDrawArrays(GL_TRIANGLES, i*6, 6);
}
}
void Viewer::draw()
{
drawSkybox();
// Bind our vertex/fragment shaders
m_program->bind();
// Get projection and camera transformations
QMatrix4x4 projectionMatrix;
QMatrix4x4 modelViewMatrix;
camera()->getProjectionMatrix(projectionMatrix);
camera()->getModelViewMatrix(modelViewMatrix);
// Prepare a transformation stack
// stack<QMatrix4x4> modelStack;
//modelViewMatrix.rotate(45, 0, 1, 0);
// uncomment this and the cube translation in init()
// to see the cube spin
/*rot = QQuaternion::fromAxisAndAngle(1, 0, 1, 90/60 * frame);
modelViewMatrix.rotate(rot);
projectionMatrix.translate(0, -5, 0);
projectionMatrix.rotate(15, 1, 0, 0);//*/
m_program->setUniformValue(m_projMatrixLocation, projectionMatrix);
m_program->setUniformValue(m_mvMatrixLocation, modelViewMatrix);
// Traverse the Scene in order to draw its components
modelStack.push(modelViewMatrix);
root.accept(*this);
frame++;
update();
}
void Viewer::mouseMoveEvent(QMouseEvent* e) {
//cout << "Viewer::mouseMoveEvent(QMouseEvent* e)" << endl;
// Normal QGLViewer behavior.
QGLViewer::mouseMoveEvent(e);
}
void Viewer::mousePressEvent(QMouseEvent* e) {
// Auto Return, but old code left in as reference
// TODO: figure out how to get this weird offset ↓↓ frame position maybe?
int x = this->x() + e->x() + 10;
int y = this->parentWidget()->height() - e->y() + 21;
std::cout << "--------------------------------------------------\nPicking shape at " << x << " (" << this->x() << " + " << e->x() << "), " << y << endl;
std::cout << "Window geom: " << this->window()->size().width() << "w, " << this->window()->size().height() << "h" << endl;
Shape* selectedShape = pickGeom(x, y);
// OpenGL coords start at the window's bottom-left, not the frame's. ugh.
QGLViewer::mousePressEvent(e);
}
void Viewer::deselect(){
std::cout << "Deselecting cell " << activeCell << endl;
if(activeCell != nullptr && activeCell->getChildren()->size()){
std::cout << "Cell has children..." << endl;
Shape* shape = dynamic_cast<Shape*> (activeCell->childAt(0));
QColor newColor = shape->getColor();
std::cout << newColor.Rgb << endl;
newColor.setAlpha(180);
shape->setColor(newColor);
}else{
std::cout << "Cell has no children, moving on" << endl;
}
this->update();
}
void Viewer::mouseReleaseEvent(QMouseEvent* e) {
//cout << "Viewer::mouseReleaseEvent(QMouseEvent* e)" << endl;
QGLViewer::mouseReleaseEvent(e);
}
void Viewer::init()
{
SkyboxCamera *_cam = new SkyboxCamera();
setCamera(_cam);
//camera()->setType(qglviewer::Camera::PERSPECTIVE);
//setMouseBinding(Qt::NoModifier, Qt::LeftButton, CAMERA, SCREEN_ROTATE);
//setMouseBinding(Qt::AltModifier, Qt::LeftButton, CAMERA, NO_MOUSE_ACTION);
//setMouseBinding(Qt::NoModifier, Qt::MouseButton(Qt::LeftButton + Qt::MidButton), CAMERA, NO_MOUSE_ACTION);
//setMouseBinding(Qt::ControlModifier, Qt::MouseButton(Qt::LeftButton + Qt::MidButton), CAMERA, NO_MOUSE_ACTION);
//setMouseBinding(Qt::ShiftModifier, Qt::MouseButton(Qt::LeftButton + Qt::MidButton), CAMERA, NO_MOUSE_ACTION);
// Our scene will be from -5 to 5 in X and Y (the grid will be 10x10).
setSceneRadius(5);
showEntireScene();
// Init OpenGL objects
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &Viewer::cleanup);
initializeOpenGLFunctions();
// Init shaders & geometry
initShaders();
initGeometries();
{
Shape* cube = new Cube();
cube->setColor(*activeColor);
// uncomment this and the quaternion transformation in draw()
// to see the cube rotate. This is how we can easily make the sun.
// cube->transform.translate(3, 0, 0);
SceneGroup *c = new SceneGroup();
c->addChild(cube);
root.addChild(c);
}
}
void Viewer::initShaders()
{
// Load vertex and fragment shaders
m_program = new QOpenGLShaderProgram;
colorPickerShaderProgram = m_program;
if (!m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, "src/shaders/basicShader.vert")) {
cerr << "Unable to load Shader" << endl
<< "Log file:" << endl;
qDebug() << m_program->log();
}
if (!m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, "src/shaders/basicShader.frag")) {
cerr << "Unable to load Shader" << endl
<< "Log file:" << endl;
qDebug() << m_program->log();
}
m_program->link();
m_program->bind(); // Note: This is equivalent to glUseProgram(programId());
// Specify shader input paramters
// The strings "vPosition", "mvMatrix", etc. have to match an attribute name in the vertex shader.
if ((m_vPositionLocation = m_program->attributeLocation("vPosition")) < 0)
qDebug() << "Unable to find shader location for " << "vPosition";
if ((m_colorLocation = m_program->uniformLocation("color")) < 0)
qDebug() << "Unable to find shader location for " << "color";
if ((m_mvMatrixLocation = m_program->uniformLocation("mvMatrix")) < 0)
qDebug() << "Unable to find shader location for " << "mvMatrix";
if ((m_projMatrixLocation = m_program->uniformLocation("projMatrix")) < 0)
qDebug() << "Unable to find shader location for " << "projMatrix";
if ((m_lDirectionLocation = m_program->uniformLocation("lDirection")) < 0)
qDebug() << "Unable to find m_shader location for" << "lDirection";
if ((m_normalMatrixLoc = m_program->uniformLocation("normalMatrix")) < 0)
qDebug() << "Unable to find m_shader location for" << "normalMatrix";
if ((m_vNormalLocation = m_program->attributeLocation("vNormal")) < 0)
qDebug() << "Unable to find m_shader location for" << "vNormal";
/*
* Adding Texture Shader
*/
/*
textureRenderShaderprogram = new QOpenGLShaderProgram;
if (!textureRenderShaderprogram->addShaderFromSourceFile(QOpenGLShader::Vertex, "src/shaders/textureShader.vert")) {
cerr << "Unable to load Shader" << endl
<< "Log file:" << endl;
qDebug() << textureRenderShaderprogram->log();
}
if (!textureRenderShaderprogram->addShaderFromSourceFile(QOpenGLShader::Fragment, "src/shaders/textureShader.frag")) {
cerr << "Unable to load Shader" << endl
<< "Log file:" << endl;
qDebug() << textureRenderShaderprogram->log();
}
textureRenderShaderprogram->link();
*/
/*
* Adding Skybox Shaders
*
*/
skyboxRenderShaderProgram = new QOpenGLShaderProgram;
if (!skyboxRenderShaderProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, "src/shaders/skyboxShader.vert")) {
cerr << "Unable to load Shader" << endl
<< "Log file:" << endl;
qDebug() << skyboxRenderShaderProgram->log();
}
if (!skyboxRenderShaderProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, "src/shaders/skyboxShader.frag")) {
cerr << "Unable to load Shader" << endl
<< "Log file:" << endl;
qDebug() << skyboxRenderShaderProgram->log();
}
skyboxRenderShaderProgram->link();
skyboxRenderShaderProgram->bind();
// if ((s_texCoordsLocation = m_program->attributeLocation("texCoords")) < 0)
// qDebug() << "Unable to find shader location for " << "vPosition";
if ((s_vPositionLocation = skyboxRenderShaderProgram->attributeLocation("vPosition")) < 0)
qDebug() << "Unable to find shader location for " << "vPosition";
if ((s_projMatrixLocation = skyboxRenderShaderProgram->uniformLocation("projMatrix")) < 0)
qDebug() << "Unable to find shader location for " << "projMatrix";
if ((s_mvMatrixLocation = skyboxRenderShaderProgram->uniformLocation("mvMatrix")) < 0)
qDebug() << "Unable to find shader location for " << "mvMatrix";
if ((s_vUvLocation = skyboxRenderShaderProgram->attributeLocation("vUv")) < 0)
qDebug() << "Unable to find shader location for " << "vUv";
s_texture = new QOpenGLTexture(QImage("src/data/skybox.jpg"));/*/
s_texture = new QOpenGLTexture(QImage("src/data/uvLayoutGrid.png"));//*/
s_texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
s_texture->setMagnificationFilter(QOpenGLTexture::Linear);
}
// Creates the basic shapes in memory. We only have 3, so we just prep them all in advance.
void Viewer::initGeometries()
{
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable( GL_BLEND );
//glEnable( GL_CULL_FACE );
glFrontFace( GL_CCW );
//glCullFace( GL_BACK );
glClearColor(0.0,0.0,0.0,0.0);
// Create our VertexArrays Objects and VertexBuffer Objects
glGenVertexArrays(NumVAOs, m_VAOs);
glGenBuffers(NumBuffers, m_Buffers);
// Create our pentagone object, store its vertices on the graphic card, and
// bind the data to the vPosition attribute of the shader
GLfloat verticesCube[numVerticesCube][3] = { // 12 triangles == 6 squares
//Front, if Z is towards us
{ -0.5, 0.5, 0.5 }, { -0.5, -0.5, 0.5 }, { 0.5, -0.5, 0.5 },
{ 0.5, -0.5, 0.5 }, { 0.5, 0.5, 0.5 }, { -0.5, 0.5, 0.5 },
//Back
{ -0.5, 0.5, -0.5 }, { 0.5, 0.5, -0.5 }, { 0.5, -0.5, -0.5 },
{ 0.5, -0.5, -0.5 }, { -0.5, -0.5, -0.5 }, { -0.5, 0.5, -0.5 },
//Right
{ -0.5, 0.5, 0.5 }, { -0.5, 0.5, -0.5 }, { -0.5, -0.5, -0.5 },
{ -0.5, -0.5, -0.5 }, { -0.5, -0.5, 0.5 }, { -0.5, 0.5, 0.5 },
//Left
{ 0.5, 0.5, 0.5 }, { 0.5, -0.5, -0.5 }, { 0.5, 0.5, -0.5 },
{ 0.5, -0.5, -0.5 }, { 0.5, 0.5, 0.5 }, { 0.5, -0.5, 0.5 },
//Top
{ -0.5, 0.5, 0.5 }, { 0.5, 0.5, 0.5 }, { 0.5, 0.5, -0.5 },
{ 0.5, 0.5, -0.5 }, { -0.5, 0.5, -0.5 }, { -0.5, 0.5, 0.5 },
//Bottom
{ -0.5, -0.5, 0.5 }, { 0.5, -0.5, -0.5 }, { 0.5, -0.5, 0.5 },
{ 0.5, -0.5, -0.5 }, { -0.5, -0.5, 0.5 }, { -0.5, -0.5, -0.5 }
};
GLfloat uvs[numVerticesCube][2] = {
{ 1.0, .25 }, { 1.0, .50 }, { .75, .50 },
{ .75, .50 }, { .75, .25 }, { 1.0, .25 },
{ .25, .25 }, { .50, .25 }, { .50, .50 },
{ .50, .50 }, { .25, .50 }, { .25, .25 },
{ .00, .25}, { .25, .25}, { .25, .50},
{ .25, .50}, { .00, .50}, { .00, .25},
{ .75, .25}, { .50, .50}, { .50, .25},
{ .50, .50}, { .75, .25}, { .75, .50},
{ .25, .00 }, { .49, .00 }, { .49, .25 },
{ .49, .25 }, { .25, .25 }, { .25, .00 },
{ .25, .50 }, { .49, .75 }, { .49, .50 },
{ .49, .75 }, { .25, .50 }, { .25, .75 },
};
GLfloat normals[numVerticesCube][3] = {
{ 0, 0, 1 }, {0, 0, 1}, {0, 0, 1},
{ 0, 0, 1 }, {0, 0, 1}, {0, 0, 1},
{ 0, 0, -1 }, {0, 0, -1}, {0, 0, -1},
{ 0, 0, -1 }, {0, 0, -1}, {0, 0, -1},
{ 1, 0, 0 }, {1, 0, 0}, {1, 0, 0},
{ 1, 0, 0 }, {1, 0, 0}, {1, 0, 0},
{ -1, 0, 0 }, {-1, 0, 0}, {-1, 0, 0},
{ -1, 0, 0 }, {-1, 0, 0}, {-1, 0, 0},
{ 0, 1, 0 }, {0, 1, 0}, {0, 1, 0},
{ 0, 1, 0 }, {0, 1, 0}, {0, 1, 0},
{ 0, -1, 0 }, {0, -1, 0}, {0, -1, 0},
{ 0, -1, 0 }, {0, -1, 0}, {0, -1, 0},
};
glBindVertexArray(m_VAOs[VAO_Cube]);
glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[VBO_Cube]);
glGenBuffers(NumBuffers, Buffers);
glBindBuffer(GL_ARRAY_BUFFER, Buffers[ArrayBuffer]);
glBufferData(GL_ARRAY_BUFFER, sizeof(verticesCube) + sizeof(uvs) + sizeof(normals),
NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(verticesCube), verticesCube);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(verticesCube), sizeof(uvs), uvs);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(verticesCube)+sizeof(uvs), sizeof(normals), normals);
// glBindVertexArray(m_VAOs[VAO_Cube]);
// glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[VBO_Cube]);
// glBufferData(GL_ARRAY_BUFFER, sizeof(verticesCube), verticesCube, GL_STATIC_DRAW);
glVertexAttribPointer(m_vPositionLocation, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glEnableVertexAttribArray(m_vPositionLocation);
glVertexAttribPointer(s_vUvLocation, 2, GL_FLOAT,
GL_FALSE, 0, BUFFER_OFFSET(sizeof(verticesCube)));
glEnableVertexAttribArray(s_vUvLocation);
glVertexAttribPointer(m_vNormalLocation, 3, GL_FLOAT,
GL_FALSE, 0, BUFFER_OFFSET(sizeof(verticesCube) + sizeof(uvs)));
glEnableVertexAttribArray(m_vNormalLocation);
}
void Viewer::visit(Cube &s)
{
QMatrix4x4 modelViewMatrix = modelStack.top() * QMatrix4x4(s.transform);
QMatrix4x4 lookAt;
camera()->getProjectionMatrix(lookAt);
int faces = floor(numVerticesCube/6);
for(int i = 0; i < faces; i++){ // 6 vertexes par face
glBindVertexArray(m_VAOs[VAO_Cube]);
m_program->setUniformValue(m_mvMatrixLocation, modelViewMatrix);
m_program->setUniformValue(m_normalMatrixLoc, lookAt.normalMatrix());
m_program->setUniformValue(m_colorLocation, s.getColor());
glDrawArrays(GL_TRIANGLES, i*6, 6);
}
}
void Viewer::visit(SceneGroup &s)
{
// Build compound transformation matrix
QMatrix4x4 currentMatrix = modelStack.top() * QMatrix4x4(s.transform);
modelStack.push(currentMatrix);
while(s.hasNext()) {
// Get next leaf
GlNode* current = s.getChild();
// Draw/Traverse child
current->accept(*this);
}
// Return model matrix to previous state
modelStack.pop();
}
void Viewer::changeColor(QColor c){
if(activeCell->getChildren()->size()){
Shape* shape = dynamic_cast<Shape*> (activeCell->childAt(0));
shape->setColor(c);
}
activeColor = new QColor(c);
this->update();
}
Shape* Viewer::pickGeom(int x, int y){
QMap<QRgb, Shape*> mapColorToShape;
QColor c;
QRgb startColor = 0xFF0001; // alpha must be 100%, glReadPixels doesn't resturn alpha
unsigned char pixelData[3];
// Traverse tree
/* TODO: Make this recurse through SceneGroups, with like "populateMap(map, root, color)"
* Right now it's fine because we have simple Minecraft rules
* but could be important in the future
*/
QOpenGLShaderProgram *lastshader = m_program;
//colorPickerShaderProgram->bind();
std::cout << "Iterating through " << root.getChildren()->size() << "items"<<endl;
for(int i = 0, l = root.getChildren()->size(); i<l; i++)
{
std::cout << " iterating... " << i << endl;
SceneGroup* current = dynamic_cast<SceneGroup*>(root.childAt(i));
Shape* currentCube;
if(current->getChildren()->size())
{
currentCube = dynamic_cast<Shape*>(current->childAt(0));
}
if(currentCube)
{
c.setRgb(startColor);
mapColorToShape.insert(c.rgb(), currentCube);
std::cout << "Setting " << currentCube << " to " << c.red() << " " << c.green() << " " << c.blue() << " " << c.alpha() << endl;
currentCube->setColor(c);
startColor+=6;
}
}
update();
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// for debugging purposes
/*QImage *im = new QImage(128,128, QImage::Format_RGB32);
for(int i = 0; i< 128; i++){
for(int j = 0; j< 128; j++){
glReadPixels(x-64+i, y+64-j, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixelData);
QColor* pickedColor = new QColor(pixelData[0], pixelData[1], pixelData[2]);
if(i==64&&j==64) pickedColor->setRgba(0xFFFFFFFF);
im->setPixelColor(i, j, pickedColor->rgb());
}
}
im->save("./screenshot.bmp");//*/
glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixelData);
QColor* pickedColor = new QColor(pixelData[0], pixelData[1], pixelData[2]);
unsigned int pickedInt = pickedColor->rgb();
Shape* pickedShape = mapColorToShape.value(pickedInt - (pickedInt % 6) + 1);
std::cout << "Picked Color: " << pickedColor->red() << " " << pickedColor->green() << " " << pickedColor->blue() << " " << pickedColor->alpha() << endl;
std::cout << "Picked Shape: " << pickedShape << endl;
//lastshader->bind();
return pickedShape;
}