Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Branch: master
Fetching contributors…

Cannot retrieve contributors at this time

1727 lines (1447 sloc) 55.425 kB
#include <QtGui>
#include <QtOpenGL>
#include <map>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <ImathLine.h>
#include <ImathBoxAlgo.h>
#include "GLModelWidget.h"
#define DEBUG_ME (0)
Imath::Box3d fakeBounds(Imath::V3d(-50, -50, -50), Imath::V3d(50, 50, 50));
GLModelWidget::GLModelWidget(QWidget* parent, QSettings* appSettings, UndoManager *undoManager, VoxelGridGroupPtr sprite)
: QGLWidget(parent),
m_cam(),
m_cameraSnapStep(45.0),
m_cameraSnapDelta(m_cameraSnapStep/2.0, m_cameraSnapStep/2.0),
m_gvg(sprite),
m_previews(),
p_undoManager(undoManager),
m_activeVoxel(-1,-1,-1),
m_activeColor(1.0f, 1.0f, 1.0f, 1.0f),
m_activeIndex(-1),
m_lastMouse(),
m_drawGrid(true),
m_drawVoxelGrid(true),
m_drawBoundingBox(false),
m_drawSpriteBounds(true),
m_shiftWrap(true),
m_currAxis(Y_AXIS),
m_activeTool(NULL),
p_appSettings(appSettings),
m_minBoundOffset(0),
m_maxBoundOffset(0)
{
m_drawGrid=p_appSettings->value("GLModelWidget/drawGrid", true).toBool();
m_drawVoxelGrid=p_appSettings->value("GLModelWidget/drawVoxelGrid", false).toBool();
m_drawBoundingBox=p_appSettings->value("GLModelWidget/drawBoundingBox", false).toBool();
m_drawSpriteBounds=p_appSettings->value("GLModelWidget/drawSpriteBounds", true).toBool();
// Default empty grid
//centerGrid();
// Always shoot rays through the scene - even when a mouse button isn't pressed
setMouseTracking(true);
connect(p_undoManager, SIGNAL(spriteChanged(VoxelGridGroupPtr)),
this, SLOT(onSpriteChanged(VoxelGridGroupPtr)));
connect(p_undoManager, SIGNAL(paletteChanged(ColorPalettePtr)),
this, SLOT(onPaletteChanged(ColorPalettePtr)));
}
GLModelWidget::~GLModelWidget()
{
//makeCurrent();
//glDeleteLists(object, 1);
delete m_activeTool;
}
void GLModelWidget::saveSettings()
{
p_appSettings->setValue("GLModelWidget/drawGrid", m_drawGrid);
p_appSettings->setValue("GLModelWidget/drawVoxelGrid", m_drawVoxelGrid);
p_appSettings->setValue("GLModelWidget/drawBoundingBox", m_drawBoundingBox);
p_appSettings->setValue("GLModelWidget/drawSpriteBounds", m_drawSpriteBounds);
}
Imath::Box3i GLModelWidget::editBounds()
{
Imath::Box3i box=m_gvg->bounds();
switch (m_currAxis)
{
case X_AXIS:
box.min.x+=m_minBoundOffset.x;
box.max.x-=m_maxBoundOffset.x;
break;
case Y_AXIS:
box.min.y+=m_minBoundOffset.y;
box.max.y-=m_maxBoundOffset.y;
break;
case Z_AXIS:
box.min.z+=m_minBoundOffset.z;
box.max.z-=m_maxBoundOffset.z;
break;
default:
box.min+=m_minBoundOffset;
box.max-=m_maxBoundOffset;
}
return box;
}
void GLModelWidget::setSprite(VoxelGridGroupPtr sprite)
{
m_gvg=sprite;
//centerGrid();
updateGL();
}
void GLModelWidget::setCurrentAxis(const SproxelAxis val)
{
m_currAxis = val;
int mino, maxo, range;
switch (m_currAxis)
{
case X_AXIS: mino=m_minBoundOffset.x; maxo=m_maxBoundOffset.x; range=m_gvg->bounds().size().x; break;
case Y_AXIS: mino=m_minBoundOffset.y; maxo=m_maxBoundOffset.y; range=m_gvg->bounds().size().y; break;
case Z_AXIS: mino=m_minBoundOffset.z; maxo=m_maxBoundOffset.z; range=m_gvg->bounds().size().z; break;
default: mino=0; maxo=0; range=0;
}
emit sliceChanged(mino, maxo, range);
// TODO: Change tool as well.
updateGL();
}
void GLModelWidget::setMinSlice(int o)
{
switch (m_currAxis)
{
case X_AXIS: m_minBoundOffset.x=o; break;
case Y_AXIS: m_minBoundOffset.y=o; break;
case Z_AXIS: m_minBoundOffset.z=o; break;
}
updateGL();
}
void GLModelWidget::setMaxSlice(int o)
{
switch (m_currAxis)
{
case X_AXIS: m_maxBoundOffset.x=o; break;
case Y_AXIS: m_maxBoundOffset.y=o; break;
case Z_AXIS: m_maxBoundOffset.z=o; break;
}
updateGL();
}
void GLModelWidget::resizeAndClearVoxelGrid(const Imath::V3i& size)
{
VoxelGridLayerPtr layer(new VoxelGridLayer());
layer->resize(Imath::Box3i(Imath::V3i(0), size-Imath::V3i(1)));
layer->setName("main layer");
m_gvg->clear();
m_gvg->insertLayerAbove(0, layer);
centerGrid();
updateGL();
}
void GLModelWidget::resizeAndShiftVoxelGrid(const Imath::V3i& sizeInc,
const Imath::V3i& shift)
{
Imath::Box3i box=m_gvg->bounds();
Imath::V3i newSize = box.size() + Imath::V3i(1) + sizeInc;
//== FIXME: current implementation collapses all layers, should be per-layer operation
VoxelGridGroupPtr newGridPtr(new VoxelGridGroup(newSize, ColorPalettePtr()));
VoxelGridGroup &newGrid = *newGridPtr;
for (int x = box.min.x; x <= box.max.x; x++)
{
const int xDest = x + shift.x;
for (int y = box.min.y; y <= box.max.y; y++)
{
const int yDest = y + shift.y;
for (int z = box.min.z; z <= box.max.z; z++)
{
const int zDest = z + shift.z;
if ((xDest < 0 || xDest >= newSize.x) ||
(yDest < 0 || yDest >= newSize.y) ||
(zDest < 0 || zDest >= newSize.z))
continue;
newGrid.set(Imath::V3i(xDest, yDest, zDest),
m_gvg->get(Imath::V3i(x, y, z)));
}
}
}
p_undoManager->changeEntireVoxelGrid(m_gvg, newGridPtr);
centerGrid();
updateGL();
}
void GLModelWidget::reresVoxelGrid(const float scale)
{
//== FIXME: make it work with layers
Imath::V3i newDims;
const Imath::V3i oldDims = m_gvg->bounds().size()+Imath::V3i(1);
newDims.x = (int)((float)oldDims.x * scale);
newDims.y = (int)((float)oldDims.y * scale);
newDims.z = (int)((float)oldDims.z * scale);
if (newDims.x <= 0 && newDims.y <= 0 && newDims.z <= 0)
return;
VoxelGridGroupPtr newGridPtr(new VoxelGridGroup(newDims, ColorPalettePtr()));
VoxelGridGroup &newGrid = *newGridPtr;
newGrid.setTransform(m_gvg->transform());
Imath::M44d transform;
Imath::V3d oldTranslation = m_gvg->transform().translation();
transform[3][0] = oldTranslation.x * scale;
transform[3][1] = oldTranslation.y * scale;
transform[3][2] = oldTranslation.z * scale;
newGrid.setTransform(transform);
// TODO: Maybe scaling the world or the gvg would be fun
// Move the stuff over, "changing resolution"
// TODO: Make this less naieve. Right now, when down-res'ing, you get a filled
// voxel if there is *anything* in its up-res'ed version.
// Could also take neighbors into account (round corners) when up-res'ing, etc.
for (int x = 0; x < oldDims.x; x++)
{
for (int y = 0; y < oldDims.y; y++)
{
for (int z = 0; z < oldDims.z; z++)
{
const Imath::Color4f curCol = m_gvg->get(Imath::V3i(x,y,z));
if (curCol.a != 0.0f)
{
for (int xx = 0; xx < scale; xx++)
{
for (int yy = 0; yy < scale; yy++)
{
for (int zz = 0; zz < scale; zz++)
{
const Imath::V3i lowIndex((int)((float)x*scale)+xx,
(int)((float)y*scale)+yy,
(int)((float)z*scale)+zz);
newGrid.set(lowIndex, curCol);
}
}
}
}
}
}
}
p_undoManager->changeEntireVoxelGrid(m_gvg, newGridPtr);
updateGL();
}
QSize GLModelWidget::minimumSizeHint() const
{
return QSize(50, 50);
}
QSize GLModelWidget::sizeHint() const
{
return QSize(400, 400);
}
void GLModelWidget::setActiveTool(const SproxelTool tool)
{
delete m_activeTool;
switch (tool)
{
case TOOL_RAY: break; // TODO: Add icon and implement!
case TOOL_SPLAT: m_activeTool = new SplatToolState(p_undoManager); break;
case TOOL_FLOOD: m_activeTool = new FloodToolState(p_undoManager); break;
case TOOL_DROPPER: m_activeTool = new DropperToolState(p_undoManager); break;
case TOOL_ERASER: m_activeTool = new EraserToolState(p_undoManager); break;
case TOOL_REPLACE: m_activeTool = new ReplaceToolState(p_undoManager); break;
case TOOL_SLAB: m_activeTool = new SlabToolState(p_undoManager); break;
case TOOL_LINE: m_activeTool = new LineToolState(p_undoManager); break;
case TOOL_BOX: m_activeTool = new BoxToolState(p_undoManager); break;
case TOOL_EXTRUDE: m_activeTool = new ExtrudeToolState(p_undoManager); break;
}
}
void GLModelWidget::initializeGL()
{
QColor bg = p_appSettings->value("GLModelWidget/backgroundColor", QColor(161,161,161)).value<QColor>();
glClearColor(bg.redF(), bg.greenF(), bg.blueF(), 0.0);
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE); // Not sure if i should let these go or not.
m_cam.setSize(400, 400);
m_cam.lookAt(Imath::V3d(28, 21, 28), Imath::V3d(0.0, 0.0, 0.0));
m_cam.setFovy(37.849289);
}
void GLModelWidget::resizeGL(int width, int height)
{
m_cam.setSize(width, height);
m_cam.autoSetClippingPlanes(fakeBounds);
}
void GLModelWidget::glDrawBounds(const Imath::Box3i &bounds, QColor color)
{
if (bounds.isEmpty()) return;
Imath::Box3d box(bounds.min, bounds.max+Imath::V3i(1));
box=Imath::transform(box, m_gvg->transform());
const Imath::V3d &min=box.min;
const Imath::V3d &max=box.max;
glColor3f(color.redF(), color.greenF(), color.blueF());
glLineWidth(2);
glDisable(GL_DEPTH_TEST);
glBegin(GL_LINE_LOOP);
glVertex3f(min.x, min.y, min.z);
glVertex3f(max.x, min.y, min.z);
glVertex3f(max.x, min.y, max.z);
glVertex3f(min.x, min.y, max.z);
glEnd();
glBegin(GL_LINE_LOOP);
glVertex3f(min.x, max.y, min.z);
glVertex3f(max.x, max.y, min.z);
glVertex3f(max.x, max.y, max.z);
glVertex3f(min.x, max.y, max.z);
glEnd();
glBegin(GL_LINES);
glVertex3f(min.x, min.y, min.z);
glVertex3f(min.x, max.y, min.z);
glVertex3f(max.x, min.y, min.z);
glVertex3f(max.x, max.y, min.z);
glVertex3f(min.x, min.y, max.z);
glVertex3f(min.x, max.y, max.z);
glVertex3f(max.x, min.y, max.z);
glVertex3f(max.x, max.y, max.z);
glEnd();
glLineWidth(1);
glEnable(GL_DEPTH_TEST);
}
void GLModelWidget::paintGL()
{
QColor bg = p_appSettings->value("GLModelWidget/backgroundColor", QColor(161,161,161)).value<QColor>();
glClearColor(bg.redF(), bg.greenF(), bg.blueF(), 0.0);
m_cam.apply();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (m_drawGrid)
{
// Shift the grid to the floor of the voxel grid
// TODO: Eventually stop moving this to the floor and just keep it at 0,0,0
Imath::Box3d worldBox = m_gvg->worldBounds();
glPushMatrix();
glTranslatef(0, worldBox.min.y, 0);
// Grid drawing with color conversion
QColor tempG = p_appSettings->value("GLModelWidget/gridColor", QColor(0,0,0)).value<QColor>();
QColor tempBG = p_appSettings->value("GLModelWidget/backgroundColor", QColor(161,161,161)).value<QColor>();
glDrawGrid(p_appSettings->value("GLModelWidget/gridSize", 16).toInt(),
p_appSettings->value("GLModelWidget/gridCellSize", 1).toInt(),
Imath::Color4f(tempG.redF(), tempG.greenF(), tempG.blueF(), 1.0f),
Imath::Color4f(tempBG.redF(), tempBG.greenF(), tempBG.blueF(), 1.0f));
glPopMatrix();
}
if (m_drawSpriteBounds)
{
Imath::Box3i bounds=m_gvg->bounds(), sliced=editBounds();
if (sliced!=bounds)
{
QColor tempS = p_appSettings->value("GLModelWidget/slicingColor", QColor(255,0,0)).value<QColor>();
glDrawBounds(sliced, tempS);
}
QColor tempG = p_appSettings->value("GLModelWidget/gridColor", QColor(0,0,0)).value<QColor>();
glDrawBounds(bounds, tempG);
}
glDrawAxes();
// Draw colored centers
//glEnable(GL_BLEND);
//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_LIGHT0);
GLfloat ambient[] = {0.0, 0.0, 0.0, 1.0};
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
Imath::V3f camPos = m_cam.translation();
GLfloat lightDir[4];
lightDir[0] = camPos.x;
lightDir[1] = camPos.y;
lightDir[2] = camPos.z;
lightDir[3] = 0.0; // w=0.0 means directional
glLightfv(GL_LIGHT0, GL_POSITION, lightDir);
GLfloat diffuse[] = {0.8f, 0.8f, 0.8f, 1.0f};
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
bool drawOutlines=p_appSettings->value("GLModelWidget/drawVoxelOutlines", 0).toBool();
bool drawSmoothCubes=p_appSettings->value("GLModelWidget/drawSmoothVoxels", 0).toBool();
const Imath::Box3i dim = editBounds();
for (int x = dim.min.x; x <= dim.max.x; x++)
{
for (int y = dim.min.y; y <= dim.max.y; y++)
{
for (int z = dim.min.z; z <= dim.max.z; z++)
{
const Imath::V3i index = Imath::V3i(x,y,z);
Imath::Color4f cellColor = m_gvg->get(index);
const Imath::M44d mat = m_gvg->voxelTransform(index);
if (cellColor.a != 0.0)
{
glColor4f(cellColor.r, cellColor.g, cellColor.b, 0.2f);
glPushMatrix();
glMultMatrixd(glMatrix(mat));
CubeFaceMask mask = computeVoxelFaceMask(index, dim);
if (drawOutlines)
{
// TODO: Make line width a setting
// TODO: Learn how to fix these polygon offset values to work properly.
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.0, 1.0);
glDrawCubePoly(mask, drawSmoothCubes);
glDisable(GL_POLYGON_OFFSET_FILL);
//glLineWidth(1.5);
glDisable(GL_LIGHTING);
glEnable(GL_POLYGON_OFFSET_LINE);
glPolygonOffset(1.0, -5.0);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glColor3f(1.0f - cellColor.r,
1.0f - cellColor.g,
1.0f - cellColor.b);
glDrawCubePoly(mask, false);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDisable(GL_POLYGON_OFFSET_LINE);
glEnable(GL_LIGHTING);
//glLineWidth(1.0);
}
else
{
glDrawCubePoly(mask, drawSmoothCubes);
}
glPopMatrix();
}
}
}
}
glDisable(GL_LIGHT0);
glDisable(GL_COLOR_MATERIAL);
glDisable(GL_LIGHTING);
//glDisable(GL_BLEND);
// DRAW UNPROJECTED LINE
if (DEBUG_ME)
{
const Imath::Line3d& lastRay = m_activeTool->ray();
glColor4f(1.0f, 1.0f, 0.0f, 1.0f);
glBegin(GL_LINES);
glVertex3d(lastRay.pos.x, lastRay.pos.y, lastRay.pos.z);
glVertex3f(lastRay.pos.x + lastRay.dir.x * 100.0,
lastRay.pos.y + lastRay.dir.y * 100.0,
lastRay.pos.z + lastRay.dir.z * 100.0);
glEnd();
}
// Grid stuff
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (m_drawVoxelGrid)
{
glColor4f(1.0f, 1.0f, 1.0f, 0.2f);
glDrawVoxelGrid();
}
if (p_appSettings->value("GLModelWidget/previewEnabled", 1).toBool())
{
glColor4f(1.0f, 0.0f, 0.0f, 0.2f);
glDrawPreviewVoxels();
}
if (m_activeVoxel != Imath::V3i(-1,-1,-1))
{
glColor4f(0.0f, 0.0f, 1.0f, 0.2f);
glDrawActiveVoxel();
}
glDisable(GL_BLEND);
// Draw text stuff
QFont font;
font.setPointSize(10);
glColor3f(1.0f, 1.0f, 1.0f);
const char *sliceName[3] = { "Axis X, Slice YZ",
"Axis Y, Slice XZ",
"Axis Z, Slice XY" };
renderText(10, 20, QString(sliceName[m_currAxis]), font);
//renderText(10, 32, QString("%1, %2, %3")
// .arg(m_activeVoxel.x)
// .arg(m_activeVoxel.y)
// .arg(m_activeVoxel.z));
// DRAW BOUNDING BOX
if (m_drawBoundingBox)
{
Imath::Box3d ext = dataBounds();
Imath::V3d& min = ext.min;
Imath::V3d& max = ext.max;
if (!ext.isEmpty())
{
glColor3f(1.0f, 0.0f, 0.0f);
glBegin(GL_LINE_LOOP);
glVertex3f(min.x, min.y, min.z);
glVertex3f(max.x, min.y, min.z);
glVertex3f(max.x, min.y, max.z);
glVertex3f(min.x, min.y, max.z);
glEnd();
glBegin(GL_LINE_LOOP);
glVertex3f(min.x, max.y, min.z);
glVertex3f(max.x, max.y, min.z);
glVertex3f(max.x, max.y, max.z);
glVertex3f(min.x, max.y, max.z);
glEnd();
glBegin(GL_LINES);
glVertex3f(min.x, min.y, min.z);
glVertex3f(min.x, max.y, min.z);
glVertex3f(max.x, min.y, min.z);
glVertex3f(max.x, max.y, min.z);
glVertex3f(min.x, min.y, max.z);
glVertex3f(min.x, max.y, max.z);
glVertex3f(max.x, min.y, max.z);
glVertex3f(max.x, max.y, max.z);
glEnd();
}
}
glLoadIdentity();
}
// Transform an Imath::M44d into a matrix usable by OpenGL.
double* GLModelWidget::glMatrix(const Imath::M44d& m)
{
return (double*)(&m);
}
void GLModelWidget::glDrawGrid(const int size,
const int gridCellSize,
const Imath::Color4f& gridColor,
const Imath::Color4f& bgColor)
{
// TODO: Query and restore depth test
glDisable(GL_DEPTH_TEST);
// Lighter grid lines
const Imath::Color4f lightColor = ((bgColor - gridColor) * 0.80f) + gridColor;
Imath::Box3i ext=m_gvg->bounds();
if (ext.isEmpty() || !m_drawSpriteBounds)
{
ext=Imath::Box3i(Imath::V3i(-size), Imath::V3i(size));
}
else
{
ext.min-=Imath::V3i(1);
ext.max+=Imath::V3i(2);
}
glBegin(GL_LINES);
glColor4f(lightColor.r, lightColor.g, lightColor.b, 1.0f);
for (int i = ext.min.x; i <= ext.max.x; i++)
{
if (i == 0) continue;
if (i % gridCellSize) continue;
glVertex3f(i, 0, ext.max.z);
glVertex3f(i, 0, ext.min.z);
}
for (int i = ext.min.z; i <= ext.max.z; i++)
{
if (i == 0) continue;
if (i % gridCellSize) continue;
glVertex3f(ext.max.x, 0, i);
glVertex3f(ext.min.x, 0, i);
}
glEnd();
// Darker main lines
// TODO: Query and restore line width
glLineWidth(2);
glBegin(GL_LINES);
glColor4f(gridColor.r, gridColor.g, gridColor.b, 1.0f);
glVertex3f(ext.min.x, 0, 0);
glVertex3f(ext.max.x, 0, 0);
glVertex3f(0, 0, ext.min.z);
glVertex3f(0, 0, ext.max.z);
glEnd();
glLineWidth(1);
glEnable(GL_DEPTH_TEST);
}
GLModelWidget::CubeFaceMask GLModelWidget::computeVoxelFaceMask(const Imath::V3i& index, const Imath::Box3i &bounds)
{
CubeFaceMask returnMask = FACE_NONE;
const Imath::Color4f& cellColor = m_gvg->get(index);
if (cellColor.a == 0.0f)
return returnMask;
Imath::V3i at;
// Positive
at=index; at.x++;
if (!bounds.intersects(at) || m_gvg->get(at).a == 0.0)
returnMask = (CubeFaceMask)(returnMask | FACE_POSX);
at=index; at.y++;
if (!bounds.intersects(at) || m_gvg->get(at).a == 0.0)
returnMask = (CubeFaceMask)(returnMask | FACE_POSY);
at=index; at.z++;
if (!bounds.intersects(at) || m_gvg->get(at).a == 0.0)
returnMask = (CubeFaceMask)(returnMask | FACE_POSZ);
// Negative
at=index; at.x--;
if (!bounds.intersects(at) || m_gvg->get(at).a == 0.0)
returnMask = (CubeFaceMask)(returnMask | FACE_NEGX);
at=index; at.y--;
if (!bounds.intersects(at) || m_gvg->get(at).a == 0.0)
returnMask = (CubeFaceMask)(returnMask | FACE_NEGY);
at=index; at.z--;
if (!bounds.intersects(at) || m_gvg->get(at).a == 0.0)
returnMask = (CubeFaceMask)(returnMask | FACE_NEGZ);
return returnMask;
}
void GLModelWidget::glDrawAxes()
{
// A little heavy-handed, but it gets the job done.
GLCamera localCam;
localCam.setSize(50, 50);
localCam.setFovy(15);
localCam.setRotation(m_cam.rotation());
const Imath::V3d distance = (m_cam.translation() - m_cam.pointOfInterest()).normalized();
localCam.setTranslation(distance*15.0);
localCam.setCenterOfInterest(m_cam.centerOfInterest());
// Set the new camera
glLoadIdentity();
localCam.apply();
// Draw the axes
glDisable(GL_DEPTH_TEST);
glBegin(GL_LINES);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 0.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 1.0f);
glEnd();
glEnable(GL_DEPTH_TEST);
// Restore old camera
glLoadIdentity();
m_cam.apply();
}
void GLModelWidget::glDrawCubeWire()
{
glBegin(GL_LINE_LOOP);
glVertex3f(-0.5, -0.5, -0.5);
glVertex3f( 0.5, -0.5, -0.5);
glVertex3f( 0.5, -0.5, 0.5);
glVertex3f(-0.5, -0.5, 0.5);
glEnd();
glBegin(GL_LINE_LOOP);
glVertex3f(-0.5, 0.5, -0.5);
glVertex3f( 0.5, 0.5, -0.5);
glVertex3f( 0.5, 0.5, 0.5);
glVertex3f(-0.5, 0.5, 0.5);
glEnd();
glBegin(GL_LINES);
glVertex3f(-0.5, -0.5, -0.5);
glVertex3f(-0.5, 0.5, -0.5);
glVertex3f( 0.5, -0.5, -0.5);
glVertex3f( 0.5, 0.5, -0.5);
glVertex3f( 0.5, -0.5, 0.5);
glVertex3f( 0.5, 0.5, 0.5);
glVertex3f(-0.5, -0.5, 0.5);
glVertex3f(-0.5, 0.5, 0.5);
glEnd();
}
void GLModelWidget::glDrawCubePoly(const CubeFaceMask mask, bool smooth)
{
glBegin(GL_QUADS);
if (smooth)
{
float ns=0.07f, nf=sqrtf(1.0f-ns*ns*2);
if (mask & FACE_POSY)
{
glNormal3f( ns, nf,- ns);
glVertex3f( 0.5f, 0.5f,-0.5f);
glNormal3f(- ns, nf,- ns);
glVertex3f(-0.5f, 0.5f,-0.5f);
glNormal3f(- ns, nf, ns);
glVertex3f(-0.5f, 0.5f, 0.5f);
glNormal3f( ns, nf, ns);
glVertex3f( 0.5f, 0.5f, 0.5f);
}
if (mask & FACE_NEGY)
{
glNormal3f( ns,- nf, ns);
glVertex3f( 0.5f,-0.5f, 0.5f);
glNormal3f(- ns,- nf, ns);
glVertex3f(-0.5f,-0.5f, 0.5f);
glNormal3f(- ns,- nf,- ns);
glVertex3f(-0.5f,-0.5f,-0.5f);
glNormal3f( ns,- nf,- ns);
glVertex3f( 0.5f,-0.5f,-0.5f);
}
if (mask & FACE_POSZ)
{
glNormal3f( ns, ns, nf);
glVertex3f( 0.5f, 0.5f, 0.5f);
glNormal3f(- ns, ns, nf);
glVertex3f(-0.5f, 0.5f, 0.5f);
glNormal3f(- ns,- ns, nf);
glVertex3f(-0.5f,-0.5f, 0.5f);
glNormal3f( ns,- ns, nf);
glVertex3f( 0.5f,-0.5f, 0.5f);
}
if (mask & FACE_NEGZ)
{
glNormal3f( ns,- ns,- nf);
glVertex3f( 0.5f,-0.5f,-0.5f);
glNormal3f(- ns,- ns,- nf);
glVertex3f(-0.5f,-0.5f,-0.5f);
glNormal3f(- ns, ns,- nf);
glVertex3f(-0.5f, 0.5f,-0.5f);
glNormal3f( ns, ns,- nf);
glVertex3f( 0.5f, 0.5f,-0.5f);
}
if (mask & FACE_POSX)
{
glNormal3f( nf, ns,- ns);
glVertex3f( 0.5f, 0.5f,-0.5f);
glNormal3f( nf, ns, ns);
glVertex3f( 0.5f, 0.5f, 0.5f);
glNormal3f( nf,- ns, ns);
glVertex3f( 0.5f,-0.5f, 0.5f);
glNormal3f( nf,- ns,- ns);
glVertex3f( 0.5f,-0.5f,-0.5f);
}
if (mask & FACE_NEGX)
{
glNormal3f(- nf, ns, ns);
glVertex3f(-0.5f, 0.5f, 0.5f);
glNormal3f(- nf, ns,- ns);
glVertex3f(-0.5f, 0.5f,-0.5f);
glNormal3f(- nf,- ns,- ns);
glVertex3f(-0.5f,-0.5f,-0.5f);
glNormal3f(- nf,- ns, ns);
glVertex3f(-0.5f,-0.5f, 0.5f);
}
}
else
{
if (mask & FACE_POSY)
{
glNormal3f( 0.0f, 1.0f, 0.0f);
glVertex3f( 0.5f, 0.5f,-0.5f);
glVertex3f(-0.5f, 0.5f,-0.5f);
glVertex3f(-0.5f, 0.5f, 0.5f);
glVertex3f( 0.5f, 0.5f, 0.5f);
}
if (mask & FACE_NEGY)
{
glNormal3f( 0.0f,-1.0f, 0.0f);
glVertex3f( 0.5f,-0.5f, 0.5f);
glVertex3f(-0.5f,-0.5f, 0.5f);
glVertex3f(-0.5f,-0.5f,-0.5f);
glVertex3f( 0.5f,-0.5f,-0.5f);
}
if (mask & FACE_POSZ)
{
glNormal3f( 0.0f, 0.0f, 1.0f);
glVertex3f( 0.5f, 0.5f, 0.5f);
glVertex3f(-0.5f, 0.5f, 0.5f);
glVertex3f(-0.5f,-0.5f, 0.5f);
glVertex3f( 0.5f,-0.5f, 0.5f);
}
if (mask & FACE_NEGZ)
{
glNormal3f( 0.0f, 0.0f,-1.0f);
glVertex3f( 0.5f,-0.5f,-0.5f);
glVertex3f(-0.5f,-0.5f,-0.5f);
glVertex3f(-0.5f, 0.5f,-0.5f);
glVertex3f( 0.5f, 0.5f,-0.5f);
}
if (mask & FACE_POSX)
{
glNormal3f( 1.0f, 0.0f, 0.0f);
glVertex3f( 0.5f, 0.5f,-0.5f);
glVertex3f( 0.5f, 0.5f, 0.5f);
glVertex3f( 0.5f,-0.5f, 0.5f);
glVertex3f( 0.5f,-0.5f,-0.5f);
}
if (mask & FACE_NEGX)
{
glNormal3f(-1.0f, 0.0f, 0.0f);
glVertex3f(-0.5f, 0.5f, 0.5f);
glVertex3f(-0.5f, 0.5f,-0.5f);
glVertex3f(-0.5f,-0.5f,-0.5f);
glVertex3f(-0.5f,-0.5f, 0.5f);
}
}
glEnd();
}
void GLModelWidget::glDrawVoxelGrid()
{
const Imath::Box3i dim = m_gvg->bounds();
// TODO: Optimize the intersects stuff (or just ignore it)
for (int x = dim.min.x; x <= dim.max.x; x++)
{
for (int y = dim.min.y; y <= dim.max.y+1; y++)
{
for (int z = dim.min.z; z <= dim.max.z+1; z++)
{
if (p_appSettings->value("GLModelWidget/previewEnabled", 1).toBool())
{
if (std::find(m_previews.begin(), m_previews.end(), Imath::V3i(x,y,z)) != m_previews.end())
continue;
if (std::find(m_previews.begin(), m_previews.end(), Imath::V3i(x,y-1,z)) != m_previews.end())
continue;
if (std::find(m_previews.begin(), m_previews.end(), Imath::V3i(x,y,z-1)) != m_previews.end())
continue;
if (std::find(m_previews.begin(), m_previews.end(), Imath::V3i(x,y-1,z-1)) != m_previews.end())
continue;
}
if (Imath::V3i(x,y,z) == m_activeVoxel || Imath::V3i(x,y-1,z) == m_activeVoxel ||
Imath::V3i(x,y,z-1) == m_activeVoxel || Imath::V3i(x,y-1,z-1) == m_activeVoxel)
{
continue;
}
const Imath::M44d mat = m_gvg->voxelTransform(Imath::V3i(x,y,z));
glPushMatrix();
glMultMatrixd(glMatrix(mat));
glBegin(GL_LINES);
glVertex3f(-0.5, -0.5, -0.5);
glVertex3f( 0.5, -0.5, -0.5);
glEnd();
glPopMatrix();
}
}
}
for (int x = dim.min.x; x <= dim.max.x+1; x++)
{
for (int y = dim.min.y; y <= dim.max.y; y++)
{
for (int z = dim.min.z; z <= dim.max.z+1; z++)
{
if (p_appSettings->value("GLModelWidget/previewEnabled", 1).toBool())
{
if (std::find(m_previews.begin(), m_previews.end(), Imath::V3i(x,y,z)) != m_previews.end())
continue;
if (std::find(m_previews.begin(), m_previews.end(), Imath::V3i(x-1,y,z)) != m_previews.end())
continue;
if (std::find(m_previews.begin(), m_previews.end(), Imath::V3i(x,y,z-1)) != m_previews.end())
continue;
if (std::find(m_previews.begin(), m_previews.end(), Imath::V3i(x-1,y,z-1)) != m_previews.end())
continue;
}
if (Imath::V3i(x,y,z) == m_activeVoxel || Imath::V3i(x-1,y,z) == m_activeVoxel ||
Imath::V3i(x,y,z-1) == m_activeVoxel || Imath::V3i(x-1,y,z-1) == m_activeVoxel)
{
continue;
}
const Imath::M44d mat = m_gvg->voxelTransform(Imath::V3i(x,y,z));
glPushMatrix();
glMultMatrixd(glMatrix(mat));
glBegin(GL_LINES);
glVertex3f(-0.5, -0.5, -0.5);
glVertex3f(-0.5, 0.5, -0.5);
glEnd();
glPopMatrix();
}
}
}
for (int x = dim.min.x; x <= dim.max.x+1; x++)
{
for (int y = dim.min.y; y <= dim.max.y+1; y++)
{
for (int z = dim.min.z; z <= dim.max.z; z++)
{
if (p_appSettings->value("GLModelWidget/previewEnabled", 1).toBool())
{
if (std::find(m_previews.begin(), m_previews.end(), Imath::V3i(x,y,z)) != m_previews.end())
continue;
if (std::find(m_previews.begin(), m_previews.end(), Imath::V3i(x-1,y,z)) != m_previews.end())
continue;
if (std::find(m_previews.begin(), m_previews.end(), Imath::V3i(x,y-1,z)) != m_previews.end())
continue;
if (std::find(m_previews.begin(), m_previews.end(), Imath::V3i(x-1,y-1,z)) != m_previews.end())
continue;
}
if (Imath::V3i(x,y,z) == m_activeVoxel || Imath::V3i(x-1,y,z) == m_activeVoxel ||
Imath::V3i(x,y-1,z) == m_activeVoxel || Imath::V3i(x-1,y-1,z) == m_activeVoxel)
{
continue;
}
const Imath::M44d mat = m_gvg->voxelTransform(Imath::V3i(x,y,z));
glPushMatrix();
glMultMatrixd(glMatrix(mat));
glBegin(GL_LINES);
glVertex3f(-0.5, -0.5, -0.5);
glVertex3f(-0.5, -0.5, 0.5);
glEnd();
glPopMatrix();
}
}
}
}
void GLModelWidget::glDrawActiveVoxel()
{
const Imath::M44d mat = m_gvg->voxelTransform(m_activeVoxel);
glPushMatrix();
glMultMatrixd(glMatrix(mat));
glDrawCubeWire();
glPopMatrix();
}
void GLModelWidget::glDrawPreviewVoxels()
{
Imath::Color4f curColor;
glGetFloatv(GL_CURRENT_COLOR, (float*)&curColor);
for (unsigned int i = 0; i < m_previews.size(); i++)
{
float scalar = 1.0f - ((float)i / (float)(m_previews.size()));
curColor.r = curColor.r * scalar;
curColor.g = curColor.g * scalar;
curColor.b = curColor.b * scalar;
const Imath::M44d mat = m_gvg->voxelTransform(m_previews[i]);
glPushMatrix();
glMultMatrixd(glMatrix(mat));
glColor4f(curColor.r, curColor.g, curColor.b, curColor.a);
glDrawCubeWire();
glPopMatrix();
}
}
void GLModelWidget::glDrawVoxelCenter(const size_t x, const size_t y, const size_t z)
{
const Imath::V3d location = m_gvg->voxelTransform(Imath::V3i(x,y,z)).translation();
glPointSize(5);
glBegin(GL_POINTS);
glVertex3f(location.x, location.y, location.z);
glEnd();
glPointSize(1);
}
void GLModelWidget::mousePressEvent(QMouseEvent *event)
{
const bool altDown = event->modifiers() & Qt::AltModifier;
const bool ctrlDown = event->modifiers() & Qt::ControlModifier;
//const bool shiftDown = event->modifiers() & Qt::ShiftModifier;
// If you click on the GLModelWidget window, bring it forward.
setFocus();
if (altDown)
{
m_lastMouse = event->pos();
}
else if (ctrlDown)
{
if (event->buttons() & Qt::LeftButton)
{
const Imath::Line3d localLine =
m_cam.unproject(Imath::V2d(event->pos().x(), height() - event->pos().y()));
// TODO: Figure out how to restore old tool properly in ReleaseEvent
// CTRL+LMB is always replace - switch tools and execute
SproxelTool currentTool = m_activeTool->type();
setActiveTool(TOOL_REPLACE);
m_activeTool->set(m_gvg, editBounds(), localLine, m_activeColor, m_activeIndex);
m_activeTool->execute();
setActiveTool(currentTool);
updateGL();
}
}
else
{
Imath::Line3d localLine =
m_cam.unproject(Imath::V2d(event->pos().x(), height() - event->pos().y()));
if (event->buttons() & Qt::LeftButton)
{
bool draggingEnabled = p_appSettings->value("GLModelWidget/dragEnabled", 1).toBool();
m_activeTool->setDragSupport(draggingEnabled);
m_activeTool->set(m_gvg, editBounds(), localLine, m_activeColor, m_activeIndex);
if (m_activeTool->type() == TOOL_SLAB)
dynamic_cast<SlabToolState*>(m_activeTool)->setAxis(currentAxis());
else if (m_activeTool->type() == TOOL_DROPPER)
{
// TODO: Coalesce this dropper code so i don't repeat it everywhere
std::vector<Imath::V3i> ints = m_activeTool->voxelsAffected();
if (ints.size() != 0)
{
Imath::Color4f result = m_gvg->get(ints[0]);
emit colorSampled(result, m_gvg->getInd(ints[0]));
}
return;
}
m_activeTool->execute();
updateGL();
}
else if (event->buttons() & Qt::MidButton)
{
// Middle button is always the color picker
// TODO: Restore old tool properly in ReleaseEvent
SproxelTool currentTool = m_activeTool->type();
setActiveTool(TOOL_DROPPER);
m_activeTool->set(m_gvg, editBounds(), localLine, m_activeColor, m_activeIndex);
// TODO: Coalesce this dropper code so i don't repeat it everywhere
std::vector<Imath::V3i> ints = m_activeTool->voxelsAffected();
if (ints.size() != 0)
{
Imath::Color4f result = m_gvg->get(ints[0]);
emit colorSampled(result, m_gvg->getInd(ints[0]));
}
setActiveTool(currentTool);
m_activeTool->setDragSupport(p_appSettings->value("GLModelWidget/dragEnabled", 1).toInt());
}
else if (event->buttons() & Qt::RightButton)
{
// Right button is always delete
// TODO: Restore old tool properly in ReleaseEvent
SproxelTool currentTool = m_activeTool->type();
if (currentTool==TOOL_EXTRUDE)
{
m_activeTool->set(m_gvg, editBounds(), localLine, m_activeColor, m_activeIndex);
m_activeTool->executeErase();
}
else
{
setActiveTool(TOOL_ERASER);
m_activeTool->set(m_gvg, editBounds(), localLine, m_activeColor, m_activeIndex);
m_activeTool->execute();
setActiveTool(currentTool);
m_activeTool->setDragSupport(p_appSettings->value("GLModelWidget/dragEnabled", 1).toInt());
}
updateGL();
}
return;
}
}
void GLModelWidget::mouseMoveEvent(QMouseEvent *event)
{
const bool altDown = event->modifiers() & Qt::AltModifier;
const bool shiftDown = event->modifiers() & Qt::ShiftModifier;
if (altDown)
{
// Camera movement
const int dx = event->pos().x() - m_lastMouse.x();
const int dy = event->pos().y() - m_lastMouse.y();
m_lastMouse = event->pos();
if ((event->buttons() & (Qt::LeftButton | Qt::MidButton)) == (Qt::LeftButton | Qt::MidButton) ||
(event->buttons() & (Qt::RightButton)))
{
m_cam.dolly(Imath::V2d(dx, dy));
m_cam.autoSetClippingPlanes(fakeBounds);
}
else if (event->buttons() & Qt::LeftButton)
{
if (shiftDown)
{
// Camera snap
if (m_cameraSnapDelta.x <= 0)
{
double fm = fmod(m_cam.rotation().y, m_cameraSnapStep);
if (fm > 0.0)
m_cam.rotateAngle(Imath::V2d(m_cameraSnapStep-fm, 0.0));
else if (fm < 0.0)
m_cam.rotateAngle(Imath::V2d(-fm, 0.0));
else
m_cam.rotateAngle(Imath::V2d(45.0, 0.0));
m_cameraSnapDelta.x = m_cameraSnapStep/2.0;
}
else if (m_cameraSnapDelta.x >= 45.0)
{
double fm = fmod(m_cam.rotation().y, m_cameraSnapStep);
if (fm > 0.0)
m_cam.rotateAngle(Imath::V2d(-fm, 0.0));
else if (fm < 0.0)
m_cam.rotateAngle(Imath::V2d(-(m_cameraSnapStep+fm), 0.0));
else
m_cam.rotateAngle(Imath::V2d(-45.0, 0.0));
m_cameraSnapDelta.x = m_cameraSnapStep/2.0;
}
if (m_cameraSnapDelta.y <= 0)
{
double fm = fmod(m_cam.rotation().x, m_cameraSnapStep);
if (fm > 0.0)
m_cam.rotateAngle(Imath::V2d(0.0, m_cameraSnapStep-fm));
else if (fm < 0.0)
m_cam.rotateAngle(Imath::V2d(0.0, -fm));
else
m_cam.rotateAngle(Imath::V2d(0.0, 45.0));
m_cameraSnapDelta.y = m_cameraSnapStep/2.0;
}
else if (m_cameraSnapDelta.y >= 45.0)
{
double fm = fmod(m_cam.rotation().x, m_cameraSnapStep);
if (fm > 0.0)
m_cam.rotateAngle(Imath::V2d(0.0, -fm));
else if (fm < 0.0)
m_cam.rotateAngle(Imath::V2d(0.0, -(m_cameraSnapStep+fm)));
else
m_cam.rotateAngle(Imath::V2d(0.0, -45.0));
m_cameraSnapDelta.y = m_cameraSnapStep/2.0;
}
m_cameraSnapDelta.x += (double)dx / 4.0;
m_cameraSnapDelta.y += (double)dy / 4.0;
}
else
{
// Standard rotation
m_cam.rotate(Imath::V2d(dx, dy));
m_cam.autoSetClippingPlanes(fakeBounds);
// Reset the snap
m_cameraSnapDelta.x = m_cameraSnapDelta.y = m_cameraSnapStep/2.0;
}
}
else if (event->buttons() & Qt::MidButton)
{
m_cam.track(Imath::V2d(dx, dy));
m_cam.autoSetClippingPlanes(fakeBounds);
}
updateGL();
}
else
{
const Imath::Line3d localLine =
m_cam.unproject(Imath::V2d(event->pos().x(), height() - event->pos().y()));
m_activeTool->set(m_gvg, editBounds(), localLine, m_activeColor, m_activeIndex);
// Left click means you're tooling.
if (event->buttons() & Qt::LeftButton)
{
// If your active tool supports drag, tool away
if (m_activeTool->supportsDrag())
{
// You want your preview to update even when you're tooling
m_previews = m_activeTool->voxelsAffected();
// Tool execution
m_activeTool->execute();
// TODO: Coalesce this dropper code so i don't repeat it everywhere
if (m_activeTool->type() == TOOL_DROPPER)
{
std::vector<Imath::V3i> ints = m_activeTool->voxelsAffected();
if (ints.size() != 0)
{
Imath::Color4f result = m_gvg->get(ints[0]);
emit colorSampled(result, m_gvg->getInd(ints[0]));
}
}
updateGL();
}
}
else
{
// Tool preview
if (p_appSettings->value("GLModelWidget/previewEnabled", 1).toBool())
{
if (m_activeTool->type() == TOOL_SLAB)
dynamic_cast<SlabToolState*>(m_activeTool)->setAxis(currentAxis());
m_previews = m_activeTool->voxelsAffected();
updateGL();
}
}
}
}
void GLModelWidget::mouseReleaseEvent(QMouseEvent*)
{
m_cameraSnapDelta.x = m_cameraSnapDelta.y = m_cameraSnapStep/2.0;
//toRight = 0.0;
}
Imath::Box3d GLModelWidget::dataBounds()
{
Imath::Box3d retBox;
Imath::Box3i dim=m_gvg->bounds();
for (int x = dim.min.x; x <= dim.max.x; x++)
{
for (int y = dim.min.y; y <= dim.max.y; y++)
{
for (int z = dim.min.z; z <= dim.max.z; z++)
{
if (m_gvg->get(Imath::V3i(x,y,z)).a != 0.0f)
{
retBox.extendBy(Imath::V3d(x, y, z));
retBox.extendBy(Imath::V3d(x+1,y+1,z+1));
}
}
}
}
// (ImathBoxAlgo) This properly computes the world bounding box
return Imath::transform(retBox, m_gvg->transform());
}
void GLModelWidget::centerGrid()
{
Imath::M44d transform;
Imath::Box3i dim = m_gvg->bounds();
transform.setTranslation(Imath::V3d(-(dim.min.x+dim.max.x+1)/2.0, 0, -(dim.min.z+dim.max.z+1)/2.0));
m_gvg->setTransform(transform);
}
void GLModelWidget::frame(bool fullExtents)
{
// Frame on data extents if they're present. Otherwise grid world bounds
Imath::Box3d ext = dataBounds();
if (ext.isEmpty() || fullExtents)
ext = m_gvg->worldBounds();
m_cam.frame(ext);
m_cam.autoSetClippingPlanes(fakeBounds);
updateGL();
}
void GLModelWidget::handleArrows(QKeyEvent *event)
{
const bool altDown = event->modifiers() & Qt::AltModifier;
const bool ctrlDown = event->modifiers() & Qt::ControlModifier;
// If you're holding alt, you're moving the camera
if (altDown)
{
// TODO: Movement speed - inverse axes - multiple keys
if (event->key() == Qt::Key_Left) m_cam.rotate(Imath::V2d(-19, 0));
if (event->key() == Qt::Key_Right) m_cam.rotate(Imath::V2d( 19, 0));
if (event->key() == Qt::Key_Up) m_cam.rotate(Imath::V2d( 0, -19));
if (event->key() == Qt::Key_Down) m_cam.rotate(Imath::V2d( 0, 19));
updateGL();
return;
}
// If you hit an arrow key and you're invisible, make visible
if (m_activeVoxel == Imath::V3i(-1,-1,-1))
m_activeVoxel = Imath::V3i(0,0,0);
// Which way does camera up go?
int udInc = 0;
int* camUD = NULL;
Imath::V3d camYVec;
m_cam.transform().multDirMatrix(Imath::V3d(0.0, 1.0, 0.0), camYVec);
// TODO: Optimize since these are all obvious dot product results
Imath::V3d objectXVec; m_gvg->transform().multDirMatrix(Imath::V3d(1.0, 0.0, 0.0), objectXVec);
Imath::V3d objectYVec; m_gvg->transform().multDirMatrix(Imath::V3d(0.0, 1.0, 0.0), objectYVec);
Imath::V3d objectZVec; m_gvg->transform().multDirMatrix(Imath::V3d(0.0, 0.0, 1.0), objectZVec);
double xDot = camYVec.dot(objectXVec);
double yDot = camYVec.dot(objectYVec);
double zDot = camYVec.dot(objectZVec);
if (fabs(xDot) > fabs(yDot) && fabs(xDot) > fabs(zDot))
{
camUD = &m_activeVoxel.x;
if (xDot > 0) udInc = 1;
else udInc = -1;
}
else if (fabs(zDot) > fabs(yDot) && fabs(zDot) > fabs(xDot))
{
camUD = &m_activeVoxel.z;
if (zDot > 0) udInc = 1;
else udInc = -1;
}
else if (fabs(yDot) > fabs(xDot) && fabs(yDot) > fabs(zDot))
{
camUD = &m_activeVoxel.y;
if (yDot > 0) udInc = 1;
else udInc = -1;
}
// Which way does camera right go?
int rlInc = 0;
int* camRL = NULL;
Imath::V3d camXVec; m_cam.transform().multDirMatrix(Imath::V3d(1.0, 0.0, 0.0), camXVec);
xDot = camXVec.dot(objectXVec);
yDot = camXVec.dot(objectYVec);
zDot = camXVec.dot(objectZVec);
if (fabs(xDot) >= fabs(yDot) && fabs(xDot) >= fabs(zDot))
{
camRL = &m_activeVoxel.x;
if (xDot > 0) rlInc = 1;
else rlInc = -1;
}
else if (fabs(zDot) >= fabs(yDot) && fabs(zDot) >= fabs(xDot))
{
camRL = &m_activeVoxel.z;
if (zDot > 0) rlInc = 1;
else rlInc = -1;
}
else if (fabs(yDot) >= fabs(xDot) && fabs(yDot) >= fabs(zDot))
{
camRL = &m_activeVoxel.y;
if (yDot > 0) rlInc = 1;
else rlInc = -1;
}
// Which way does camera depth go?
int fbInc = 0;
int* camFB = NULL;
Imath::V3d camZVec; m_cam.transform().multDirMatrix(Imath::V3d(0.0, 0.0, -1.0), camZVec);
xDot = camZVec.dot(objectXVec);
yDot = camZVec.dot(objectYVec);
zDot = camZVec.dot(objectZVec);
if (fabs(xDot) >= fabs(yDot) && fabs(xDot) >= fabs(zDot))
{
camFB = &m_activeVoxel.x;
if (xDot > 0) fbInc = 1;
else fbInc = -1;
}
else if (fabs(zDot) >= fabs(yDot) && fabs(zDot) >= fabs(xDot))
{
camFB = &m_activeVoxel.z;
if (zDot > 0) fbInc = 1;
else fbInc = -1;
}
else if (fabs(yDot) >= fabs(xDot) && fabs(yDot) >= fabs(zDot))
{
camFB = &m_activeVoxel.y;
if (yDot > 0) fbInc = 1;
else fbInc = -1;
}
// Apply the results
switch (event->key())
{
case Qt::Key_Left: *camRL += -rlInc; break;
case Qt::Key_Right: *camRL += rlInc; break;
case Qt::Key_Up: if (ctrlDown) *camFB += fbInc; else *camUD += udInc; break;
case Qt::Key_Down: if (ctrlDown) *camFB += -fbInc; else *camUD += -udInc; break;
default: break;
}
// Clamp on the edges
Imath::Box3i dim=m_gvg->bounds();
if (m_activeVoxel.x > dim.max.x) m_activeVoxel.x = dim.max.x;
if (m_activeVoxel.y > dim.max.y) m_activeVoxel.y = dim.max.y;
if (m_activeVoxel.z > dim.max.z) m_activeVoxel.z = dim.max.z;
if (m_activeVoxel.x < dim.min.x) m_activeVoxel.x = dim.min.x;
if (m_activeVoxel.y < dim.min.y) m_activeVoxel.y = dim.min.y;
if (m_activeVoxel.z < dim.min.z) m_activeVoxel.z = dim.min.z;
updateGL();
}
void GLModelWidget::shiftVoxels(const SproxelAxis axis, const bool up, const bool wrap)
{
//== FIXME: current implementation only operates on the current layer
// maybe it should work for all the layers at once. Or be an option.
// Simplifiers for which way to shift
size_t tan0AxisDim = 0;
size_t tan1AxisDim = 0;
size_t primaryAxisDim = 0;
switch (axis)
{
case X_AXIS: primaryAxisDim = m_gvg->curLayer()->size().x;
tan0AxisDim = m_gvg->curLayer()->size().y;
tan1AxisDim = m_gvg->curLayer()->size().z; break;
case Y_AXIS: primaryAxisDim = m_gvg->curLayer()->size().y;
tan0AxisDim = m_gvg->curLayer()->size().x;
tan1AxisDim = m_gvg->curLayer()->size().z; break;
case Z_AXIS: primaryAxisDim = m_gvg->curLayer()->size().z;
tan0AxisDim = m_gvg->curLayer()->size().x;
tan1AxisDim = m_gvg->curLayer()->size().y; break;
}
// Simplifiers for wrapping
size_t backupIndex = 0;
size_t clearIndex = primaryAxisDim - 1;
if (up) std::swap(backupIndex, clearIndex);
p_undoManager->beginMacro("Shift");
// Backup the necessary slice
Imath::Color4f* sliceBackup = NULL;
int* sliceBackupInd = NULL;
if (wrap)
{
sliceBackup = new Imath::Color4f[tan0AxisDim * tan1AxisDim];
sliceBackupInd = new int[tan0AxisDim * tan1AxisDim];
for (size_t a = 0; a < tan0AxisDim; a++)
{
for (size_t b = 0; b < tan1AxisDim; b++)
{
Imath::V3i index(-1, -1, -1);
switch (axis)
{
case X_AXIS: index = Imath::V3i(backupIndex, a, b); break;
case Y_AXIS: index = Imath::V3i(a, backupIndex, b); break;
case Z_AXIS: index = Imath::V3i(a, b, backupIndex); break;
}
sliceBackup [a + (b * tan0AxisDim)] = m_gvg->get (index);
sliceBackupInd[a + (b * tan0AxisDim)] = m_gvg->getInd(index);
}
}
}
// Shift everyone over
if (up)
{
for (size_t a = backupIndex; a > clearIndex; a--)
{
for (size_t b = 0; b < tan0AxisDim; b++)
{
for (size_t c = 0; c < tan1AxisDim; c++)
{
Imath::V3i nextIndex(-1, -1, -1);
switch (axis)
{
case X_AXIS: setVoxelColor(Imath::V3i(a, b, c), m_gvg->get(Imath::V3i(a-1, b, c)), m_gvg->getInd(Imath::V3i(a-1, b, c))); break;
case Y_AXIS: setVoxelColor(Imath::V3i(b, a, c), m_gvg->get(Imath::V3i(b, a-1, c)), m_gvg->getInd(Imath::V3i(b, a-1, c))); break;
case Z_AXIS: setVoxelColor(Imath::V3i(b, c, a), m_gvg->get(Imath::V3i(b, c, a-1)), m_gvg->getInd(Imath::V3i(b, c, a-1))); break;
}
}
}
}
}
else
{
for (size_t a = backupIndex; a < clearIndex; a++)
{
for (size_t b = 0; b < tan0AxisDim; b++)
{
for (size_t c = 0; c < tan1AxisDim; c++)
{
Imath::V3i nextIndex(-1, -1, -1);
switch (axis)
{
case X_AXIS: setVoxelColor(Imath::V3i(a, b, c), m_gvg->get(Imath::V3i(a+1, b, c)), m_gvg->getInd(Imath::V3i(a+1, b, c))); break;
case Y_AXIS: setVoxelColor(Imath::V3i(b, a, c), m_gvg->get(Imath::V3i(b, a+1, c)), m_gvg->getInd(Imath::V3i(b, a+1, c))); break;
case Z_AXIS: setVoxelColor(Imath::V3i(b, c, a), m_gvg->get(Imath::V3i(b, c, a+1)), m_gvg->getInd(Imath::V3i(b, c, a+1))); break;
}
}
}
}
}
// Either clear or set the wrap-to slice
for (size_t a = 0; a < tan0AxisDim; a++)
{
for (size_t b = 0; b < tan1AxisDim; b++)
{
Imath::V3i workIndex(-1, -1, -1);
switch (axis)
{
case X_AXIS: workIndex = Imath::V3i(clearIndex, a, b); break;
case Y_AXIS: workIndex = Imath::V3i(a, clearIndex, b); break;
case Z_AXIS: workIndex = Imath::V3i(a, b, clearIndex); break;
}
if (wrap)
setVoxelColor(workIndex, sliceBackup[a + (b * tan0AxisDim)], sliceBackupInd[a + (b * tan0AxisDim)]);
else
setVoxelColor(workIndex, Imath::Color4f(0.0f, 0.0f, 0.0f, 0.0f), 0);
}
}
if (sliceBackup ) delete[] sliceBackup ;
if (sliceBackupInd) delete[] sliceBackupInd;
p_undoManager->endMacro();
updateGL();
}
void GLModelWidget::rotateVoxels(const SproxelAxis axis, const int dir)
{
//== FIXME: current implementation flattens the layers
// Set some new dimensions
Imath::V3i newDim(0,0,0);
Imath::V3i oldDim = m_gvg->bounds().size()+Imath::V3i(1);
switch (axis)
{
case X_AXIS: newDim.x = oldDim.x; newDim.y = oldDim.z; newDim.z = oldDim.y; break;
case Y_AXIS: newDim.x = oldDim.z; newDim.y = oldDim.y; newDim.z = oldDim.x; break;
case Z_AXIS: newDim.x = oldDim.y; newDim.y = oldDim.x; newDim.z = oldDim.z; break;
}
VoxelGridGroupPtr newGridPtr(new VoxelGridGroup(newDim, ColorPalettePtr()));
VoxelGridGroup &newGrid = *newGridPtr;
newGrid.setTransform(m_gvg->transform());
// Do the rotation
for (int x = 0; x < newDim.x; x++)
{
for (int y = 0; y < newDim.y; y++)
{
for (int z = 0; z < newDim.z; z++)
{
Imath::V3i oldLocation(0,0,0);
switch (axis)
{
case X_AXIS: if (dir > 0) oldLocation = Imath::V3i(x, oldDim.y-1-z, y);
else oldLocation = Imath::V3i(x, z, oldDim.z-1-y);
break;
case Y_AXIS: if (dir > 0) oldLocation = Imath::V3i(z, y, oldDim.z-1-x);
else oldLocation = Imath::V3i(oldDim.x-1-z, y, x);
break;
case Z_AXIS: if (dir > 0) oldLocation = Imath::V3i(oldDim.x-1-y, x, z);
else oldLocation = Imath::V3i(y, oldDim.y-1-x, z);
break;
}
newGrid.set(Imath::V3i(x,y,z), m_gvg->get(oldLocation));
}
}
}
p_undoManager->changeEntireVoxelGrid(m_gvg, newGridPtr);
centerGrid();
updateGL();
}
void GLModelWidget::mirrorVoxels(const SproxelAxis axis)
{
//== FIXME: current implementation collapses all layers, should be per-layer operation
VoxelGridGroupPtr backup(new VoxelGridGroup(*m_gvg));
p_undoManager->beginMacro("Mirror");
Imath::Box3i dim=m_gvg->bounds();
for (int x = dim.min.x; x <= dim.max.x; x++)
{
for (int y = dim.min.y; y <= dim.max.y; y++)
{
for (int z = dim.min.z; z <= dim.max.z; z++)
{
Imath::V3i oldLocation(-1, -1, -1);
switch (axis)
{
case X_AXIS: oldLocation = Imath::V3i(dim.max.x+dim.min.x-x, y, z); break;
case Y_AXIS: oldLocation = Imath::V3i(x, dim.max.y+dim.min.y-y, z); break;
case Z_AXIS: oldLocation = Imath::V3i(x, y, dim.max.z+dim.min.z-z); break;
}
setVoxelColor(Imath::V3i(x,y,z), backup->get(oldLocation), backup->getInd(oldLocation));
}
}
}
p_undoManager->endMacro();
updateGL();
}
// This is only here for the MainWindow now. Should be changed.
void GLModelWidget::setVoxelColor(const Imath::V3i& index, const Imath::Color4f color, int ind)
{
// Validity check
/*
const Imath::V3i& cd = m_gvg->cellDimensions();
if (index.x < 0 || index.y < 0 || index.z < 0 ||
index.x >= cd.x || index.y >= cd.y || index.z >= cd.z)
return;
*/
p_undoManager->setVoxelColor(m_gvg, index, color, ind);
}
Jump to Line
Something went wrong with that request. Please try again.