mirror of
https://github.com/lucaspalomodevelop/meshlab.git
synced 2026-03-20 11:26:11 +00:00
1461 lines
42 KiB
C++
1461 lines
42 KiB
C++
/****************************************************************************
|
|
* MeshLab o o *
|
|
* A versatile mesh processing toolbox o o *
|
|
* _ O _ *
|
|
* Copyright(C) 2005 \/)\/ *
|
|
* Visual Computing Lab /\/| *
|
|
* ISTI - Italian National Research Council | *
|
|
* \ *
|
|
* All rights reserved. *
|
|
* *
|
|
* 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 (http://www.gnu.org/licenses/gpl.txt) *
|
|
* for more details. *
|
|
* *
|
|
****************************************************************************/
|
|
#include "edit_paint.h"
|
|
#include <common/GLExtensionsManager.h>
|
|
|
|
#include <vcg/math/perlin_noise.h>
|
|
|
|
using namespace std;
|
|
using namespace vcg;
|
|
|
|
EditPaintPlugin::EditPaintPlugin()
|
|
{
|
|
zbuffer = NULL;
|
|
color_buffer = NULL;
|
|
clone_zbuffer = NULL;
|
|
generateCircle(circle);
|
|
generateCircle(dense_circle, 64);
|
|
generateSquare(square);
|
|
generateSquare(dense_square, 16);
|
|
}
|
|
|
|
EditPaintPlugin::~EditPaintPlugin() {}
|
|
|
|
const QString EditPaintPlugin::info() {
|
|
return tr("Improved Painting");
|
|
}
|
|
|
|
void EditPaintPlugin::suggestedRenderingData(MeshModel & m, MLRenderingData& dt)
|
|
{
|
|
if (m.cm.VN() == 0)
|
|
return;
|
|
MLRenderingData::PRIMITIVE_MODALITY pr = MLRenderingData::PR_POINTS;
|
|
if (m.cm.FN() > 0)
|
|
pr = MLRenderingData::PR_SOLID;
|
|
|
|
MLRenderingData::RendAtts atts;
|
|
atts[MLRenderingData::ATT_NAMES::ATT_VERTPOSITION] = true;
|
|
atts[MLRenderingData::ATT_NAMES::ATT_VERTNORMAL] = true;
|
|
atts[MLRenderingData::ATT_NAMES::ATT_VERTCOLOR] = true;
|
|
|
|
MLPerViewGLOptions opts;
|
|
dt.get(opts);
|
|
opts._sel_enabled = true;
|
|
opts._face_sel = true;
|
|
opts._vertex_sel = true;
|
|
dt.set(opts);
|
|
|
|
dt.set(pr, atts);
|
|
}
|
|
|
|
bool EditPaintPlugin::startEdit(MeshModel & m, GLArea * parent, MLSceneGLSharedDataContext* /*cont*/)
|
|
{
|
|
if (!GLExtensionsManager::initializeGLextensions_notThrowing())
|
|
return false;
|
|
dock = new QDockWidget(parent->window());
|
|
paintbox = new Paintbox(dock);
|
|
dock->setAllowedAreas(Qt::NoDockWidgetArea);
|
|
dock->setWidget(paintbox);
|
|
QPoint p = parent->mapToGlobal(QPoint(0, 0));
|
|
dock->setGeometry(5 + p.x(), p.y() + 5, paintbox->width(), parent->height() - 10);
|
|
dock->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Minimum);
|
|
dock->setFloating(true);
|
|
dock->setVisible(true);
|
|
|
|
tri::UpdateBounding<CMeshO>::Box(m.cm);
|
|
|
|
m.updateDataMask(MeshModel::MM_VERTFACETOPO | MeshModel::MM_FACEMARK | MeshModel::MM_VERTMARK);
|
|
|
|
if (!m.hasDataMask(MeshModel::MM_VERTCOLOR))
|
|
{
|
|
m.updateDataMask(MeshModel::MM_VERTCOLOR);
|
|
tri::UpdateColor<CMeshO>::PerVertexConstant(m.cm, Color4b(150, 150, 150, 255));
|
|
}
|
|
tri::InitFaceIMark(m.cm);
|
|
tri::InitVertexIMark(m.cm);
|
|
|
|
QObject::connect(paintbox, SIGNAL(undo()), this, SLOT(update()));
|
|
QObject::connect(paintbox, SIGNAL(redo()), this, SLOT(update()));
|
|
QObject::connect(paintbox, SIGNAL(typeChange(ToolType)), this, SLOT(setToolType(ToolType)));
|
|
|
|
parent->update();
|
|
|
|
selection = new vector<CMeshO::FacePointer>();
|
|
latest_event.pressure = 0.0;
|
|
|
|
setToolType(COLOR_PAINT);
|
|
|
|
glarea = parent;
|
|
buffer_width = glarea->width();
|
|
buffer_height = glarea->height();
|
|
glarea->setMouseTracking(true);
|
|
|
|
//connect(this, SIGNAL(setSelectionRendering(bool)), glarea, SLOT(setSelectFaceRendering(bool)));
|
|
|
|
parent->setCursor(QCursor(QPixmap(":/images/cursor_paint.png"), 1, 1));
|
|
|
|
// initialize once and for all the radius (will remain the same all the time)
|
|
current_brush.radius = (paintbox->getRadius() * m.cm.bbox.Diag() * 0.5);
|
|
|
|
if (glarea->mvc() == NULL)
|
|
return false;
|
|
|
|
MLSceneGLSharedDataContext* shared = glarea->mvc()->sharedDataContext();
|
|
updateColorBuffer(m, shared);
|
|
shared->manageBuffers(m.id());
|
|
return true;
|
|
}
|
|
|
|
void EditPaintPlugin::endEdit(MeshModel &/* m*/, GLArea * /*parent*/, MLSceneGLSharedDataContext* /*cont*/)
|
|
{
|
|
QObject::disconnect(paintbox, SIGNAL(undo()), this, SLOT(update()));
|
|
QObject::disconnect(paintbox, SIGNAL(redo()), this, SLOT(update()));
|
|
glarea->setMouseTracking(false);
|
|
if (zbuffer != NULL) {
|
|
delete zbuffer;
|
|
zbuffer = NULL;
|
|
}
|
|
delete paintbox;
|
|
delete selection;
|
|
delete dock;
|
|
}
|
|
|
|
void EditPaintPlugin::mousePressEvent(QMouseEvent * event, MeshModel &, GLArea * gla)
|
|
{
|
|
// start a new stroke: init zbuffer and update brush
|
|
if (zbuffer != NULL) {
|
|
delete zbuffer;
|
|
zbuffer = NULL;
|
|
}
|
|
|
|
current_brush.size = paintbox->getSize();
|
|
current_brush.opacity = paintbox->getOpacity();
|
|
current_brush.hardness = paintbox->getHardness();
|
|
|
|
pushInputEvent(event->type(), event->pos(), event->modifiers(), 1, event->button(), gla);
|
|
gla->update();
|
|
}
|
|
|
|
void EditPaintPlugin::mouseMoveEvent(QMouseEvent* event, MeshModel & /*m*/, GLArea * gla)
|
|
{
|
|
if (gla == NULL)
|
|
return;
|
|
pushInputEvent(event->type(), event->pos(), event->modifiers(), latest_event.pressure, latest_event.button, gla);
|
|
|
|
gla->update();
|
|
}
|
|
|
|
void EditPaintPlugin::mouseReleaseEvent(QMouseEvent * event, MeshModel &, GLArea * gla)
|
|
{
|
|
pushInputEvent(event->type(), event->pos(), event->modifiers(), 0, event->button(), gla);
|
|
gla->updateAllSiblingsGLAreas();
|
|
}
|
|
|
|
void EditPaintPlugin::tabletEvent(QTabletEvent * event, MeshModel &, GLArea * gla)
|
|
{
|
|
if (!(paintbox->getPressureFrameEnabled()))
|
|
paintbox->enablePressureFrame();
|
|
|
|
event->accept();
|
|
|
|
// if event is down, start a new stroke: clean zbuff
|
|
if (event->type() == QEvent::TabletPress)
|
|
{
|
|
if (zbuffer != NULL) {
|
|
delete zbuffer;
|
|
zbuffer = NULL;
|
|
}
|
|
current_brush.size = paintbox->getSize();
|
|
current_brush.opacity = paintbox->getOpacity();
|
|
current_brush.hardness = paintbox->getHardness();
|
|
}
|
|
|
|
pushInputEvent(event->type(), event->pos(), event->modifiers(), event->pressure(), (event->pointerType() == QTabletEvent::Eraser) ? Qt::RightButton : Qt::LeftButton, gla);
|
|
gla->update();
|
|
}
|
|
|
|
void EditPaintPlugin::setToolType(ToolType t)
|
|
{
|
|
current_type = t;
|
|
|
|
switch (current_type)
|
|
{
|
|
case MESH_SELECT:
|
|
current_options = EPP_PICK_FACES | EPP_DRAW_CURSOR;
|
|
emit setSelectionRendering(true);
|
|
break;
|
|
|
|
case COLOR_PAINT:
|
|
case COLOR_NOISE:
|
|
case COLOR_CLONE:
|
|
case COLOR_SMOOTH:
|
|
case MESH_SMOOTH:
|
|
current_options = EPP_PICK_VERTICES | EPP_DRAW_CURSOR;
|
|
break;
|
|
|
|
case MESH_PULL:
|
|
case MESH_PUSH:
|
|
current_options = EPP_PICK_VERTICES | EPP_AVG_NORMAL | EPP_DRAW_CURSOR;
|
|
break;
|
|
|
|
case COLOR_FILL:
|
|
case COLOR_GRADIENT:
|
|
case COLOR_PICK:
|
|
default:
|
|
current_options = EPP_NONE;
|
|
}
|
|
}
|
|
|
|
void EditPaintPlugin::setBrushSettings(int size, int opacity, int hardness)
|
|
{
|
|
current_brush.size = size;
|
|
current_brush.opacity = opacity;
|
|
current_brush.hardness = hardness;
|
|
}
|
|
|
|
/**
|
|
* Since only on a Decorate call it is possible to obtain correct values
|
|
* from OpenGL, all operations are performed during the execution of this
|
|
* method and not where mouse events are processed.
|
|
*
|
|
*/
|
|
void EditPaintPlugin::decorate(MeshModel &m, GLArea * gla)
|
|
{
|
|
glarea = gla;
|
|
if (gla->mvc() == NULL)
|
|
return;
|
|
|
|
MLSceneGLSharedDataContext* shared = gla->mvc()->sharedDataContext();
|
|
if (shared == NULL)
|
|
return;
|
|
|
|
if (!latest_event.valid || latest_event.processed) return;
|
|
latest_event.processed = true;
|
|
glPushAttrib(GL_TRANSFORM_BIT);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPushMatrix();
|
|
glMultMatrix(m.cm.Tr);
|
|
glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix);
|
|
glPopMatrix();
|
|
glPopAttrib();
|
|
glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix);
|
|
viewport[0] = viewport[1] = 0;
|
|
viewport[2] = gla->width(); viewport[3] = gla->height();
|
|
|
|
if (zbuffer == NULL)
|
|
{
|
|
zbuffer = new GLfloat[gla->width()*gla->height()];
|
|
glReadPixels(0, 0, gla->width(), gla->height(), GL_DEPTH_COMPONENT, GL_FLOAT, zbuffer);
|
|
}
|
|
|
|
if (current_options & EPP_DRAW_CURSOR)
|
|
{
|
|
|
|
current_brush.radius = (paintbox->getRadius() * m.cm.bbox.Diag() * 0.5);
|
|
if ((paintbox->getPressureFrameEnabled()))
|
|
{
|
|
if (paintbox->getPressureSize())
|
|
current_brush.size = paintbox->getSize() * latest_event.pressure;
|
|
|
|
if (paintbox->getPressureOpacity())
|
|
current_brush.opacity = paintbox->getOpacity() * latest_event.pressure;
|
|
|
|
if (paintbox->getPressureHardness())
|
|
current_brush.hardness = paintbox->getHardness() * latest_event.pressure;
|
|
}
|
|
|
|
if (paintbox->getSizeUnit() == 0)
|
|
drawSimplePolyLine(gla, latest_event.position, paintbox->getSize(),
|
|
(paintbox->getBrush() == CIRCLE) ? &circle : &square);
|
|
else
|
|
drawPercentualPolyLine(glarea, latest_event.gl_position, m, zbuffer, modelview_matrix, projection_matrix, viewport, current_brush.radius,
|
|
(paintbox->getBrush() == CIRCLE) ? &dense_circle : &dense_square);
|
|
}
|
|
|
|
if (latest_event.pressure > 0)
|
|
{
|
|
if (current_options & EPP_PICK_VERTICES)
|
|
{
|
|
vertices.clear();
|
|
updateSelection(m, &vertices);
|
|
}
|
|
else if (current_options & EPP_PICK_FACES) updateSelection(m);
|
|
|
|
if (previous_event.pressure == 0)
|
|
{
|
|
/*************************
|
|
* ON BRUSH DOWN *
|
|
*************************/
|
|
|
|
paintbox->setUndoStack(glarea);
|
|
|
|
switch (current_type)
|
|
{
|
|
case COLOR_PAINT:
|
|
{
|
|
painted_vertices.clear();
|
|
QColor newcol;
|
|
|
|
if (latest_event.button == Qt::LeftButton)
|
|
newcol = paintbox->getForegroundColor();
|
|
else
|
|
newcol = paintbox->getBackgroundColor();
|
|
color[0] = newcol.red(); color[1] = newcol.green();
|
|
color[2] = newcol.blue(); color[3] = newcol.alpha();
|
|
paintbox->getUndoStack()->beginMacro("Color Paint");
|
|
paint(&vertices);
|
|
}
|
|
break;
|
|
|
|
case COLOR_CLONE:
|
|
|
|
//Clone Source Acquisition
|
|
if (latest_event.modifiers & Qt::ControlModifier ||
|
|
latest_event.button == Qt::RightButton)
|
|
{
|
|
if (color_buffer != NULL) delete color_buffer;
|
|
if (clone_zbuffer != NULL) delete clone_zbuffer;
|
|
color_buffer = NULL, clone_zbuffer = NULL;
|
|
|
|
/*QMap<int, RenderMode>::iterator it = glarea->rendermodemap.find(m.id());
|
|
if (it == glarea->rendermodemap.end())
|
|
return;
|
|
it.value().lighting = false;*/
|
|
|
|
current_options &= ~EPP_DRAW_CURSOR;
|
|
glarea->update();
|
|
}
|
|
|
|
//Clone Painting
|
|
else {
|
|
//There's a new image to get
|
|
if (paintbox->isNewPixmapAvailable())
|
|
{
|
|
paintbox->getPixmapBuffer(color_buffer, clone_zbuffer, buffer_width, buffer_height);
|
|
// source_delta.setX(buffer_width/2);
|
|
// source_delta.setY(buffer_height/2);
|
|
source_delta = paintbox->getPixmapDelta();
|
|
paintbox->setPixmapOffset(0, 0);
|
|
apply_start = latest_event.position;
|
|
painted_vertices.clear();
|
|
paintbox->getUndoStack()->beginMacro("Color Clone");
|
|
|
|
paint(&vertices);
|
|
}
|
|
else if (color_buffer != NULL)
|
|
//There's still something in the buffer
|
|
{
|
|
painted_vertices.clear();
|
|
source_delta = paintbox->getPixmapDelta();
|
|
paintbox->setPixmapOffset(0, 0);
|
|
apply_start = latest_event.position;
|
|
paintbox->getUndoStack()->beginMacro("Color Clone");
|
|
paint(&vertices);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case COLOR_NOISE:
|
|
painted_vertices.clear();
|
|
noise_scale = paintbox->getNoiseSize() * 10 / m.cm.bbox.Diag();
|
|
paintbox->getUndoStack()->beginMacro("Color Noise");
|
|
break;
|
|
|
|
case COLOR_GRADIENT:
|
|
gradient_start = latest_event.position;
|
|
break;
|
|
|
|
case MESH_PULL:
|
|
case MESH_PUSH:
|
|
displaced_vertices.clear();
|
|
paintbox->getUndoStack()->beginMacro("Mesh Sculpting");
|
|
sculpt(m, &vertices);
|
|
break;
|
|
|
|
case COLOR_SMOOTH:
|
|
paintbox->getUndoStack()->beginMacro("Color Smooth");
|
|
smoothed_vertices.clear();
|
|
tri::UnMarkAll(m.cm);
|
|
break;
|
|
case MESH_SMOOTH:
|
|
paintbox->getUndoStack()->beginMacro("Mesh Smooth");
|
|
smoothed_vertices.clear();
|
|
tri::UnMarkAll(m.cm);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*************************
|
|
* ON BRUSH MOVE *
|
|
*************************/
|
|
switch (current_type)
|
|
{
|
|
|
|
case COLOR_CLONE:
|
|
{
|
|
paintbox->setPixmapOffset(latest_event.position.x() - apply_start.x(), latest_event.position.y() - apply_start.y());
|
|
if (color_buffer != NULL)
|
|
{
|
|
paint(&vertices);
|
|
updateColorBuffer(m, shared);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case COLOR_PAINT:
|
|
case COLOR_NOISE:
|
|
{
|
|
paint(&vertices);
|
|
updateColorBuffer(m, shared);
|
|
}
|
|
break;
|
|
|
|
case COLOR_GRADIENT:
|
|
{
|
|
drawLine(glarea, gradient_start, latest_event.position);
|
|
updateColorBuffer(m, shared);
|
|
}
|
|
break;
|
|
|
|
case MESH_SELECT:
|
|
{
|
|
for (vector<CMeshO::FacePointer>::iterator fpi = selection->begin(); fpi != selection->end(); ++fpi)
|
|
{
|
|
if (latest_event.button == Qt::LeftButton)(*fpi)->SetS();
|
|
else (*fpi)->ClearS();
|
|
}
|
|
glarea->updateSelection(m.id(), false, true);
|
|
}
|
|
break;
|
|
|
|
case MESH_PUSH:
|
|
case MESH_PULL:
|
|
{
|
|
sculpt(m, &vertices);
|
|
updateGeometryBuffers(m, shared);
|
|
}
|
|
break;
|
|
|
|
case COLOR_SMOOTH:
|
|
{
|
|
smooth(&vertices);
|
|
updateColorBuffer(m, shared);
|
|
}
|
|
break;
|
|
case MESH_SMOOTH:
|
|
{
|
|
smooth(&vertices);
|
|
updateGeometryBuffers(m, shared);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if (previous_event.pressure > 0)
|
|
{
|
|
/*************************
|
|
* ON BRUSH UP *
|
|
*************************/
|
|
|
|
switch (current_type)
|
|
{
|
|
case COLOR_FILL:
|
|
{
|
|
//CFaceO * face;
|
|
std::vector<CFaceO*> res;
|
|
int nface = GLPickTri<CMeshO>::PickVisibleFace(latest_event.gl_position.x(), latest_event.gl_position.y(), m.cm, res, 2, 2);
|
|
//if (GLPickTri<CMeshO>::PickClosestFace(latest_event.gl_position.x(), latest_event.gl_position.y(), m.cm, face, 2, 2))
|
|
if ((nface > 0) && (res[0] != NULL) && !(res[0]->IsD()))
|
|
{
|
|
fill(m, res[0]);
|
|
updateColorBuffer(m, shared);
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case COLOR_PICK:
|
|
{
|
|
QColor color;
|
|
CVertexO * temp_vert = NULL;
|
|
if (paintbox->getPickMode() == 0) {
|
|
if (getVertexAtMouse(m, temp_vert, latest_event.gl_position, modelview_matrix, projection_matrix, viewport))
|
|
{
|
|
color.setRgb(temp_vert->C()[0], temp_vert->C()[1], temp_vert->C()[2], 255);
|
|
(latest_event.button == Qt::LeftButton) ? paintbox->setForegroundColor(color) : paintbox->setBackgroundColor(color);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GLubyte pixel[3];
|
|
glReadPixels(latest_event.gl_position.x(), latest_event.gl_position.y(), 1, 1, GL_RGB, GL_UNSIGNED_BYTE, (void *)pixel);
|
|
color.setRgb(pixel[0], pixel[1], pixel[2], 255);
|
|
(latest_event.button == Qt::LeftButton) ? paintbox->setForegroundColor(color) : paintbox->setBackgroundColor(color);
|
|
}
|
|
paintbox->restorePreviousType();
|
|
}
|
|
break;
|
|
|
|
case COLOR_GRADIENT:
|
|
gradient(m, gla);
|
|
updateColorBuffer(m, shared);
|
|
break;
|
|
|
|
case COLOR_CLONE:
|
|
if (latest_event.modifiers & Qt::ControlModifier || latest_event.button == Qt::RightButton) {
|
|
capture(); break;
|
|
}
|
|
else
|
|
paintbox->movePixmapDelta(-latest_event.position.x() + apply_start.x(), -latest_event.position.y() + apply_start.y());
|
|
case COLOR_SMOOTH:
|
|
case COLOR_NOISE:
|
|
case COLOR_PAINT:
|
|
case MESH_SMOOTH:
|
|
case MESH_PUSH:
|
|
case MESH_PULL:
|
|
paintbox->getUndoStack()->endMacro();
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
shared->manageBuffers(m.id());
|
|
}
|
|
|
|
|
|
inline void EditPaintPlugin::smooth(vector< pair<CVertexO *, PickingData> > * vertices)
|
|
{
|
|
QHash <CVertexO *, pair<Point3f, Color4b> > originals;
|
|
|
|
Color4b newcol, destCol;
|
|
int strength = paintbox->getSmoothPercentual();
|
|
int decrease_pos = paintbox->getHardness();
|
|
|
|
int c_r, c_g, c_b;
|
|
float p_x, p_y, p_z;
|
|
float newpos[3];
|
|
|
|
if (paintbox->getPressureDisplacement())
|
|
strength *= latest_event.pressure;
|
|
|
|
for (unsigned int k = 0; k < vertices->size(); k++) //forach selected vertices
|
|
{
|
|
pair<CVertexO *, PickingData> data = vertices->at(k);
|
|
|
|
CVertexO * v = data.first;
|
|
PickingData * vd = &data.second;
|
|
|
|
pair<Point3f, Color4b> pc_data; //save its color and position
|
|
|
|
for (int k = 0; k < 4; k++) pc_data.second[k] = v->C()[k];
|
|
for (int k = 0; k < 3; k++) pc_data.first[k] = v->P()[k];
|
|
|
|
if ((!smoothed_vertices.contains(v)) && (glarea != NULL) && (glarea->mvc() != NULL) && (glarea->md() != NULL) && (glarea->md()->mm() != NULL))
|
|
{
|
|
if (paintbox->getCurrentType() == COLOR_SMOOTH)
|
|
paintbox->getUndoStack()->push(new SingleColorUndo(v, v->C()));
|
|
else
|
|
paintbox->getUndoStack()->push(new SinglePositionUndo(v, v->P(), v->N()));
|
|
smoothed_vertices.insert(v, v);
|
|
}
|
|
|
|
if (!originals.contains(v)) originals.insert(v, pc_data); //save original color/position data
|
|
|
|
CFaceO * one_face = v->VFp(); //one of the faces adjacent to the vertex
|
|
int pos = v->VFi(); //index of vertex v on face one_face
|
|
c_r = c_g = c_b = 0;
|
|
p_x = p_y = p_z = 0;
|
|
int count_me = 0;
|
|
CFaceO * f = one_face;
|
|
|
|
do
|
|
{ /// calc the sum of the surrounding vertices pos or and vertices color
|
|
CFaceO * temp = one_face->VFp(pos); //next face in VF list
|
|
if (one_face != 0 && !one_face->IsD())
|
|
{
|
|
for (int k = 0; k < 3; k++)
|
|
{
|
|
if (pos != k)
|
|
{
|
|
CVertexO * v = one_face->V(k);
|
|
Color4b tco = v->C();
|
|
if (originals.contains(v))
|
|
{
|
|
pair<Point3f, Color4b> pc_data_k = originals[v];
|
|
|
|
tco = pc_data_k.second;
|
|
|
|
p_x += pc_data_k.first[0]; p_y += pc_data_k.first[1]; p_z += pc_data_k.first[2];
|
|
}
|
|
else if (paintbox->getCurrentType() == MESH_SMOOTH)
|
|
{
|
|
p_x += v->P()[0]; p_y += v->P()[1]; p_z += v->P()[2];
|
|
}
|
|
c_r += tco[0]; c_g += tco[1]; c_b += tco[2];
|
|
}
|
|
}
|
|
|
|
pos = one_face->VFi(pos);
|
|
count_me += 2;
|
|
}
|
|
one_face = temp;
|
|
} while (one_face != f && one_face != 0);
|
|
|
|
if (count_me > 0) /// calc the new color or pos
|
|
{
|
|
float op = brush(paintbox->getBrush(), vd->distance, vd->rel_position.x(), vd->rel_position.y(), decrease_pos);
|
|
|
|
if (paintbox->getCurrentType() == COLOR_SMOOTH)
|
|
{
|
|
newcol[0] = c_r / count_me;
|
|
newcol[1] = c_g / count_me;
|
|
newcol[2] = c_b / count_me;
|
|
mergeColors((float)(op*strength) / 100.0, newcol, v->C(), &destCol);
|
|
|
|
v->C()[0] = destCol[0];
|
|
v->C()[1] = destCol[1];
|
|
v->C()[2] = destCol[2];
|
|
}
|
|
else
|
|
{
|
|
newpos[0] = p_x / (float)count_me;
|
|
newpos[1] = p_y / (float)count_me;
|
|
newpos[2] = p_z / (float)count_me;
|
|
float po[3]; for (int lauf = 0; lauf < 3; lauf++) po[lauf] = v->P()[lauf];
|
|
mergePositions((float)(op*strength) / 100.0, newpos, po, newpos);
|
|
|
|
for (int lauf = 0; lauf < 3; lauf++) v->P()[lauf] = newpos[lauf];
|
|
}
|
|
}
|
|
|
|
if (paintbox->getCurrentType() == MESH_SMOOTH)
|
|
{
|
|
updateNormal(v);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
inline void EditPaintPlugin::sculpt(MeshModel & /*m*/, vector< pair<CVertexO *, PickingData> > * vertices)
|
|
{
|
|
// int opac = 1.0;
|
|
float decrease_pos = paintbox->getHardness() / 100.0;
|
|
float strength = paintbox->getDisplacement() / 100.0;
|
|
|
|
if (paintbox->getPressureDisplacement())
|
|
strength *= latest_event.pressure;
|
|
|
|
if (latest_event.button == Qt::RightButton) strength = -strength;
|
|
|
|
if (normal[0] == normal[1] && normal[1] == normal[2] && normal[2] == 0) {
|
|
return;
|
|
}
|
|
|
|
for (unsigned int k = 0; k < vertices->size(); k++)
|
|
{
|
|
pair<CVertexO *, PickingData> data = vertices->at(k);
|
|
|
|
float op = brush(paintbox->getBrush(), data.second.distance, data.second.rel_position.x(), data.second.rel_position.y(), decrease_pos * 100);
|
|
|
|
//TODO Precalculate this monster!
|
|
float gauss = (strength * exp(-(op - 1.0)*(op - 1.0) * 8.0));
|
|
|
|
bool useAverageNormals = paintbox->getDirection() == 0; //0 is average normal, 1 is per-vertex normal
|
|
|
|
if (!displaced_vertices.contains(data.first))
|
|
{
|
|
displaced_vertices.insert(data.first, pair<Point3f, float>(
|
|
Point3f(data.first->P()[0], data.first->P()[1], data.first->P()[2]),
|
|
gauss));
|
|
|
|
paintbox->getUndoStack()->push(new SinglePositionUndo(data.first, data.first->P(), data.first->N()));
|
|
|
|
if (useAverageNormals)
|
|
displaceAlongVector(data.first, normal, gauss);
|
|
else
|
|
displaceAlongVector(data.first, data.first->N(), gauss);
|
|
|
|
updateNormal(data.first);
|
|
}
|
|
else if ((latest_event.button == Qt::RightButton)
|
|
? displaced_vertices[data.first].second > gauss
|
|
: displaced_vertices[data.first].second < gauss)
|
|
{
|
|
displaced_vertices[data.first].second = gauss;
|
|
Point3f temp = displaced_vertices[data.first].first;
|
|
data.first->P()[0] = temp[0]; data.first->P()[1] = temp[1]; data.first->P()[2] = temp[2];
|
|
if (useAverageNormals)
|
|
displaceAlongVector(data.first, normal, gauss);
|
|
else
|
|
displaceAlongVector(data.first, data.first->N(), gauss);
|
|
updateNormal(data.first);
|
|
}
|
|
|
|
delete zbuffer; zbuffer = NULL;
|
|
}
|
|
}
|
|
|
|
inline void EditPaintPlugin::capture()
|
|
{
|
|
color_buffer = new GLubyte[glarea->width()*glarea->height() * 4];
|
|
clone_zbuffer = new GLfloat[glarea->width()*glarea->height()];
|
|
glReadPixels(0, 0, glarea->width(), glarea->height(), GL_RGBA, GL_UNSIGNED_BYTE, color_buffer);
|
|
glReadPixels(0, 0, glarea->width(), glarea->height(), GL_DEPTH_COMPONENT, GL_FLOAT, clone_zbuffer);
|
|
buffer_height = glarea->height();
|
|
buffer_width = glarea->width();
|
|
|
|
source_delta = latest_event.position;
|
|
|
|
QImage image(glarea->width(), glarea->height(), QImage::Format_ARGB32);
|
|
for (int x = 0; x < glarea->width(); x++) {
|
|
for (int y = 0; y < glarea->height(); y++) {
|
|
int index = (y * glarea->width() + x) * 4;
|
|
image.setPixel(x, glarea->height() - y - 1, qRgba((int)color_buffer[index], (int)color_buffer[index + 1], (int)color_buffer[index + 2], (int)color_buffer[index + 3]));
|
|
}
|
|
}
|
|
//if (glarea->getCurrentRenderMode() == NULL)
|
|
// return;
|
|
//glarea->getCurrentRenderMode()->lighting = true;
|
|
current_options |= EPP_DRAW_CURSOR;
|
|
paintbox->setClonePixmap(image);
|
|
paintbox->setPixmapDelta(source_delta.x(), source_delta.y());
|
|
|
|
// paintbox->setPixmapCenter(-source_delta.x(), -source_delta.y());
|
|
glarea->update();
|
|
}
|
|
|
|
inline bool EditPaintPlugin::accessCloneBuffer(int vertex_x, int vertex_y, vcg::Color4b & color)
|
|
{
|
|
int y = buffer_height - source_delta.y() + (vertex_y + apply_start.y() - glarea->height());
|
|
int x = source_delta.x() + (vertex_x - apply_start.x());
|
|
|
|
int index = y * buffer_width + x;
|
|
|
|
if (x < buffer_width && y < buffer_height && x >= 0 && y >= 0)
|
|
{
|
|
if (clone_zbuffer[index] < 1.0)
|
|
{
|
|
index *= 4;
|
|
color[0] = color_buffer[index]; color[1] = color_buffer[index + 1]; color[2] = color_buffer[index + 2], color[3] = color_buffer[index + 3];
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Painting, Cloning and Noise!
|
|
*/
|
|
inline void EditPaintPlugin::paint(vector< pair<CVertexO *, PickingData> > * vertices)
|
|
{
|
|
int opac = current_brush.opacity; //TODO legacy assignment
|
|
int decrease_pos = current_brush.hardness; //TODO legacy assignment
|
|
|
|
for (unsigned int k = 0; k < vertices->size(); k++)
|
|
{
|
|
pair<CVertexO *, PickingData> data = vertices->at(k);
|
|
|
|
float op = brush(paintbox->getBrush(), data.second.distance, data.second.rel_position.x(), data.second.rel_position.y(), decrease_pos);
|
|
|
|
if ((glarea != NULL) && (glarea->mvc() != NULL) && (glarea->md() != NULL) && (glarea->md()->mm() != NULL))
|
|
{
|
|
if (!painted_vertices.contains(data.first))
|
|
{
|
|
if (paintbox->getCurrentType() == COLOR_CLONE)
|
|
if (!accessCloneBuffer(data.second.position.x(), data.second.position.y(), color)) return;
|
|
|
|
if (paintbox->getCurrentType() == COLOR_NOISE)
|
|
computeNoiseColor(data.first, color);
|
|
|
|
painted_vertices.insert(data.first, pair<Color4b, int>(
|
|
Color4b(data.first->C()[0], data.first->C()[1], data.first->C()[2], data.first->C()[3]),
|
|
(int)(op*opac)));
|
|
|
|
paintbox->getUndoStack()->push(new SingleColorUndo(data.first, data.first->C()));
|
|
|
|
applyColor(data.first, color, (int)(op * opac));
|
|
|
|
}
|
|
else if (painted_vertices[data.first].second < (int)(op * opac))
|
|
{
|
|
painted_vertices[data.first].second = (int)(op * opac);
|
|
Color4b temp = painted_vertices[data.first].first;
|
|
data.first->C()[0] = temp[0]; data.first->C()[1] = temp[1]; data.first->C()[2] = temp[2];
|
|
|
|
if (paintbox->getCurrentType() == COLOR_CLONE)
|
|
if (!accessCloneBuffer(data.second.position.x(), data.second.position.y(), color)) return;
|
|
|
|
if (paintbox->getCurrentType() == COLOR_NOISE)
|
|
computeNoiseColor(data.first, color);
|
|
|
|
paintbox->getUndoStack()->push(new SingleColorUndo(data.first, data.first->C()));
|
|
|
|
applyColor(data.first, color, (int)(op * opac));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void EditPaintPlugin::computeNoiseColor(CVertexO * vert, vcg::Color4b & col)
|
|
{
|
|
float scaler = noise_scale; //parameter TODO to be cached
|
|
|
|
double noise;
|
|
//if "veins on"
|
|
// noise = vcg::math::Abs(vcg::math::Perlin::Noise(vert->P()[0] * scaler, vert->P()[1] * scaler, vert->P()[2] * scaler));
|
|
//else
|
|
noise = (vcg::math::Perlin::Noise(vert->P()[0] * scaler, vert->P()[1] * scaler, vert->P()[2] * scaler) + 1) / 2;
|
|
|
|
Color4b forecolor(paintbox->getForegroundColor().red(), paintbox->getForegroundColor().green(), paintbox->getForegroundColor().blue(), paintbox->getForegroundColor().alpha());
|
|
|
|
//TODO test code to be refactored
|
|
if (paintbox->getGradientType() == 0)
|
|
{
|
|
Color4b backcolor(paintbox->getBackgroundColor().red(), paintbox->getBackgroundColor().green(), paintbox->getBackgroundColor().blue(), paintbox->getBackgroundColor().alpha());
|
|
for (int i = 0; i < 4; i++)
|
|
col[i] = forecolor[i] * noise + backcolor[i] * (1.0 - noise);
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
col[i] = forecolor[i] * noise + vert->C()[i] * (1.0 - noise);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* fills the mesh starting from face.
|
|
* If face is selected, it will fill only the selected area,
|
|
* otherwise only the non selected area
|
|
*/
|
|
inline void EditPaintPlugin::fill(MeshModel &, CFaceO * face)
|
|
{
|
|
QHash <CFaceO *, CFaceO *> visited;
|
|
QHash <CVertexO *, CVertexO *> temp;
|
|
vector <CFaceO *>temp_po;
|
|
bool who = face->IsS();
|
|
temp_po.push_back(face);
|
|
visited.insert(face, face);
|
|
int opac = paintbox->getOpacity();
|
|
QColor newcol = (latest_event.button == Qt::LeftButton) ? paintbox->getForegroundColor() : paintbox->getBackgroundColor();
|
|
|
|
Color4b color(newcol.red(), newcol.green(), newcol.blue(), newcol.alpha());
|
|
|
|
paintbox->getUndoStack()->beginMacro("Fill Color");
|
|
// MLSceneGLSharedDataContext* shared = NULL;
|
|
// int meshid;
|
|
// if ((glarea != NULL) && (glarea->mvc() != NULL) && (glarea->md() != NULL) && (glarea->md()->mm() != NULL))
|
|
// {
|
|
// shared = glarea->mvc()->sharedDataContext();
|
|
// meshid = glarea->md()->mm()->id();
|
|
// }
|
|
for (unsigned int lauf2 = 0; lauf2 < temp_po.size(); lauf2++) {
|
|
CFaceO * fac = temp_po.at(lauf2);
|
|
if (who == fac->IsS()) {
|
|
for (int lauf = 0; lauf < 3; lauf++) {
|
|
if (!temp.contains(fac->V(lauf))) {
|
|
temp.insert(fac->V(lauf), fac->V(lauf));
|
|
|
|
paintbox->getUndoStack()->push(new SingleColorUndo(fac->V(lauf), fac->V(lauf)->C()));
|
|
|
|
applyColor(fac->V(lauf), color, opac);
|
|
}
|
|
}
|
|
|
|
vector <CFaceO *> surround;
|
|
for (int lauf = 0; lauf < 3; lauf++) getSurroundingFacesVF(fac, lauf, &surround);
|
|
|
|
for (unsigned int lauf3 = 0; lauf3 < surround.size(); lauf3++) {
|
|
if (!visited.contains(surround[lauf3])) {
|
|
temp_po.push_back(surround[lauf3]);
|
|
visited.insert(surround[lauf3], surround[lauf3]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
paintbox->getUndoStack()->endMacro();
|
|
}
|
|
|
|
inline void EditPaintPlugin::gradient(MeshModel & m, GLArea * gla) {
|
|
|
|
QPoint p = gradient_start - latest_event.position;
|
|
|
|
QHash <CVertexO *, CVertexO *> temp;
|
|
|
|
int opac = paintbox->getOpacity();
|
|
QColor qc1 = paintbox->getForegroundColor();
|
|
QColor qc2 = paintbox->getBackgroundColor();
|
|
|
|
Color4b c1(qc1.red(), qc1.green(), qc1.blue(), qc1.alpha());
|
|
Color4b c2(qc2.red(), qc2.green(), qc2.blue(), qc2.alpha());
|
|
|
|
QPointF p1(gradient_start.x(), gla->height() - gradient_start.y());
|
|
QPointF p0(latest_event.gl_position);
|
|
|
|
float x2 = (p1.x() - p0.x());
|
|
float y2 = (p1.y() - p0.y());
|
|
|
|
float l_square = x2*x2 + y2*y2;
|
|
|
|
CVertexO * vertex;
|
|
CMeshO::FaceIterator fi;
|
|
double dx, dy, dz;
|
|
Color4b merger;
|
|
bool tutti = !hasSelected(m);
|
|
float radius = sqrt((float)(p.x()*p.x() + p.y()*p.y()));
|
|
|
|
paintbox->getUndoStack()->beginMacro("Gradient");
|
|
// MLSceneGLSharedDataContext* shared = NULL;
|
|
// int meshid;
|
|
|
|
// if ((glarea != NULL) && (glarea->mvc() != NULL) && (glarea->md() != NULL) && (glarea->md()->mm() != NULL))
|
|
// {
|
|
// shared = glarea->mvc()->sharedDataContext();
|
|
// meshid = glarea->md()->mm()->id();
|
|
// }
|
|
|
|
|
|
int gradient_type = paintbox->getGradientType();
|
|
int gradient_form = paintbox->getGradientForm();
|
|
for (fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi) {
|
|
if (!(*fi).IsD() && (tutti || (*fi).IsS())){
|
|
for (int lauf = 0; lauf < 3; lauf++) {
|
|
if (!temp.contains((*fi).V(lauf))) {
|
|
vertex = (*fi).V(lauf);
|
|
temp.insert(vertex, vertex);
|
|
gluProject(vertex->P()[0], vertex->P()[1], vertex->P()[2], modelview_matrix, projection_matrix, viewport, &dx, &dy, &dz);
|
|
|
|
paintbox->getUndoStack()->push(new SingleColorUndo(vertex, vertex->C()));
|
|
|
|
if (gradient_form == 0) {
|
|
double r = (dx - p0.x())*(p1.x() - p0.x()) + (dy - p0.y())*(p1.y() - p0.y());
|
|
r = r / l_square;
|
|
|
|
float px = p0.x() + r*(p1.x() - p0.x());
|
|
float py = p0.y() + r*(p1.y() - p0.y());
|
|
|
|
px = px - dx;
|
|
py = py - dy;
|
|
|
|
if (gradient_type == 0) {
|
|
if (r >= 0 && r <= 1) {
|
|
mergeColors(r, c1, c2, &merger);
|
|
applyColor(vertex, merger, opac);
|
|
}
|
|
else if (r > 1) {
|
|
applyColor(vertex, c1, opac);
|
|
}
|
|
else if (r < 0) {
|
|
applyColor(vertex, c2, opac);
|
|
}
|
|
}
|
|
else {
|
|
if (r >= 0 && r <= 1) {
|
|
applyColor(vertex, c1, (int)((opac * 0.01) * r * 100.0));
|
|
}
|
|
else if (r > 1) {
|
|
applyColor(vertex, c1, opac);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
float x0 = (dx - p1.x());
|
|
float y0 = (dy - p1.y());
|
|
float bla0 = x0*x0 + y0*y0;
|
|
if (bla0 < radius*radius && radius>0) {
|
|
float r = 1.0 - sqrt(bla0) / sqrt(radius*radius);
|
|
if (gradient_type == 0) {
|
|
mergeColors(r, c1, c2, &merger);
|
|
applyColor(vertex, merger, opac);
|
|
}
|
|
else {
|
|
applyColor(vertex, c1, (int)((opac * 0.01) * r * 100.0));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
paintbox->getUndoStack()->endMacro();
|
|
}
|
|
|
|
|
|
/**
|
|
* Updates currently selected faces by using one of the
|
|
* two methods. Just a new version of computeBrushedFaces
|
|
*
|
|
* It's inlined because it's only called once, inside Decorate.
|
|
* Should it be called in other places, the inlining must be removed!
|
|
*/
|
|
inline void EditPaintPlugin::updateSelection(MeshModel &m, vector< pair<CVertexO *, PickingData> > * vertex_result)
|
|
{
|
|
vector<CMeshO::FacePointer>::iterator fpi;
|
|
vector<CMeshO::FacePointer> temp; //TODO maybe temp can be placed inside the class for better performance
|
|
|
|
vector <CFaceO *> surround; /*< surrounding faces of a given face*/
|
|
surround.reserve(6);
|
|
|
|
if (current_options & EPP_AVG_NORMAL) normal = Point3m(0.0, 0.0, 0.0);
|
|
|
|
tri::UnMarkAll(m.cm);
|
|
|
|
if (selection->size() == 0) {
|
|
CMeshO::FaceIterator fi;
|
|
temp.reserve(m.cm.fn); //avoid unnecessary expansions
|
|
for (fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi) {
|
|
if (!(*fi).IsD()) {
|
|
temp.push_back((&*fi));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
temp.reserve(selection->size()); //avoid unnecessary expansions
|
|
vertex_result->reserve(selection->size());
|
|
for (fpi = selection->begin(); fpi != selection->end(); ++fpi)
|
|
{
|
|
temp.push_back(*fpi);
|
|
}
|
|
}
|
|
|
|
selection->clear();
|
|
|
|
QPointF gl_cursorf = QPointF(latest_event.gl_position);
|
|
QPointF gl_prev_cursorf = QPointF(previous_event.gl_position);
|
|
|
|
QPointF p[3], z[3]; //p stores vertex coordinates in screen space, z the corresponding depth value
|
|
double tx, ty, tz;
|
|
|
|
bool backface = paintbox->getPaintBackFace();
|
|
bool invisible = paintbox->getPaintInvisible();
|
|
bool percentual = paintbox->getSizeUnit() == 1;
|
|
|
|
double EPSILON = 0.003;
|
|
|
|
double scale_fac = 1.0;
|
|
float fov = 1.0;
|
|
double distance[3];
|
|
|
|
if (percentual)
|
|
{
|
|
double dX, dY, dZ;
|
|
|
|
fastMultiply(0, 0, 0, modelview_matrix, &dX, &dY, &dZ);
|
|
fastMultiply(0, 1, 0, modelview_matrix, &tx, &ty, &tz);
|
|
|
|
scale_fac = sqrt((ty - dY)*(ty - dY) + (tx - dX)*(tx - dX) + (tz - dZ)*(tz - dZ));
|
|
|
|
/** to get the correct radius depending on distance but on fov too */
|
|
float fo = glarea->getFov()*0.5;
|
|
fov = 1.0 / tan(fo*M_PI / 180.0)*0.5;
|
|
}
|
|
|
|
for (unsigned int k = 0; k < temp.size(); k++) //for each face
|
|
{
|
|
CFaceO * fac = temp.at(k);
|
|
bool intern = false;
|
|
int checkable = 0; /*< counts how many valid vertices there are in this face*/
|
|
|
|
for (int i = 0; i < 3; i++) //for each vertex defining the face
|
|
{
|
|
if (gluProject((fac)->V(i)->P()[0], (fac)->V(i)->P()[1], (fac)->V(i)->P()[2],
|
|
modelview_matrix, projection_matrix, viewport, &tx, &ty, &tz) == GL_TRUE) checkable++; //if gluProjection works
|
|
|
|
if (tz < 0 || tz > 1) checkable--; //but if outside depth bounds
|
|
|
|
p[i].setX(tx); p[i].setY(ty); //store the position of the vertex on the screen
|
|
|
|
//if the vertex is projected within the screen (i.e. it's visible)
|
|
if (tx >= 0 && tx < viewport[2] && ty >= 0 && ty < viewport[3])
|
|
{
|
|
z[i].setX(tz); //the screen depth of the point
|
|
z[i].setY((float)zbuffer[(int)(((int)ty)*viewport[2] + (int)tx)]); //the screen depth of the closest point at the same coors
|
|
}
|
|
else
|
|
{
|
|
z[i].setX(1); //far
|
|
z[i].setY(0); //near
|
|
}
|
|
|
|
if (percentual) {
|
|
double dx, dy, dz; // distance
|
|
fastMultiply((fac)->V(i)->P()[0], (fac)->V(i)->P()[1], (fac)->V(i)->P()[2], modelview_matrix, &dx, &dy, &dz);
|
|
distance[i] = dz;
|
|
}
|
|
}
|
|
|
|
if (backface || isFront(p[0], p[1], p[2]))
|
|
{
|
|
for (int j = 0; j < 3; j++) if (invisible || (z[j].x() <= z[j].y() + EPSILON))
|
|
{
|
|
PickingData vd; //TODO make it a pointer
|
|
|
|
float radius = percentual ? vcg::math::Abs(current_brush.radius * scale_fac * viewport[3] * fov / distance[j]) : current_brush.size;
|
|
|
|
if (isIn(gl_cursorf, gl_prev_cursorf, p[j].x(), p[j].y(), radius, &vd.distance, vd.rel_position))
|
|
{
|
|
intern = true;
|
|
if (vertex_result == NULL) continue;
|
|
else if (!tri::IsMarked(m.cm, fac->V(j)))
|
|
{
|
|
vd.position.setX((int)p[j].x()); vd.position.setY((int)p[j].y());
|
|
pair<CVertexO *, PickingData> data(fac->V(j), vd);
|
|
vertex_result->push_back(data);
|
|
|
|
if (current_options & EPP_AVG_NORMAL) normal += fac->V(j)->N();
|
|
|
|
tri::Mark(m.cm, fac->V(j));
|
|
}
|
|
}
|
|
|
|
if (vertex_result == NULL && !intern && lineHitsCircle(p[j], p[(j + 1) % 3], gl_cursorf, radius))
|
|
{
|
|
intern = true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (checkable == 3 && !intern && (pointInTriangle(gl_cursorf, p[0], p[1], p[2]) || pointInTriangle(gl_prev_cursorf, p[0], p[1], p[2])))
|
|
{
|
|
intern = true;
|
|
}
|
|
}
|
|
|
|
if (intern && !tri::IsMarked(m.cm, fac))
|
|
{
|
|
tri::Mark(m.cm, fac);
|
|
selection->push_back(fac);
|
|
surround.clear();
|
|
for (int lauf = 0; lauf < 3; lauf++) getSurroundingFacesVF(fac, lauf, &surround);
|
|
|
|
for (unsigned int lauf3 = 0; lauf3 < surround.size(); lauf3++)
|
|
{
|
|
if (!tri::IsMarked(m.cm, surround[lauf3]))
|
|
{
|
|
temp.push_back(surround[lauf3]);
|
|
}
|
|
}
|
|
}
|
|
} //end of for each face loop
|
|
|
|
if (current_options & EPP_AVG_NORMAL) normal /= vertex_result->size();
|
|
}
|
|
|
|
void EditPaintPlugin::updateColorBuffer(MeshModel& m, MLSceneGLSharedDataContext* shared)
|
|
{
|
|
if (shared != NULL)
|
|
{
|
|
MLRenderingData::RendAtts atts;
|
|
atts[MLRenderingData::ATT_NAMES::ATT_VERTCOLOR] = true;
|
|
shared->meshAttributesUpdated(m.id(), false, atts);
|
|
}
|
|
}
|
|
|
|
void EditPaintPlugin::updateGeometryBuffers(MeshModel & m, MLSceneGLSharedDataContext* shared)
|
|
{
|
|
if (shared != NULL)
|
|
{
|
|
MLRenderingData::RendAtts atts;
|
|
atts[MLRenderingData::ATT_NAMES::ATT_VERTPOSITION] = true;
|
|
atts[MLRenderingData::ATT_NAMES::ATT_VERTNORMAL] = true;
|
|
atts[MLRenderingData::ATT_NAMES::ATT_FACENORMAL] = true;
|
|
shared->meshAttributesUpdated(m.id(), false, atts);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Request an async repainting of the glarea
|
|
*
|
|
* This slot is connected to undo and redo
|
|
*/
|
|
void EditPaintPlugin::update()
|
|
{
|
|
if ((glarea != NULL) && (glarea->mvc() != NULL) && (glarea->md() != NULL) && (glarea->md()->mm() != NULL))
|
|
{
|
|
updateColorBuffer(*(glarea->md()->mm()),glarea->mvc()->sharedDataContext());
|
|
updateGeometryBuffers(*(glarea->md()->mm()), glarea->mvc()->sharedDataContext());
|
|
glarea->mvc()->sharedDataContext()->manageBuffers(glarea->md()->mm()->id());
|
|
}
|
|
glarea->updateAllSiblingsGLAreas();
|
|
}
|
|
|
|
|
|
/*********OpenGL Drawing Routines************/
|
|
|
|
|
|
/**
|
|
* draws the xor-line
|
|
*/
|
|
void drawLine(GLArea * gla, QPoint & start, QPoint & cur) {
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
glOrtho(0, gla->width(), gla->height(), 0, -1, 1);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
glPushAttrib(GL_ENABLE_BIT);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDisable(GL_LIGHTING);
|
|
glDisable(GL_TEXTURE_2D);
|
|
glEnable(GL_COLOR_LOGIC_OP);
|
|
float wi;
|
|
glGetFloatv(GL_LINE_WIDTH, &wi);
|
|
glLineWidth(4);
|
|
glLogicOp(GL_XOR);
|
|
glColor3f(1, 1, 1);
|
|
glBegin(GL_LINES);
|
|
glVertex2f(start.x(), start.y());
|
|
glVertex2f(cur.x(), cur.y());
|
|
glEnd();
|
|
glPopAttrib();
|
|
glPopMatrix(); // restore modelview
|
|
glLineWidth(wi);
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPopMatrix();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
}
|
|
|
|
void drawSimplePolyLine(GLArea * gla, QPoint & cur, float scale, vector<QPointF> * points)
|
|
{
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
glOrtho(0, gla->width(), gla->height(), 0, -1, 1);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
glPushAttrib(GL_ENABLE_BIT);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDisable(GL_LIGHTING);
|
|
glDisable(GL_TEXTURE_2D);
|
|
glEnable(GL_COLOR_LOGIC_OP);
|
|
glLogicOp(GL_XOR);
|
|
glColor3f(1, 1, 1);
|
|
|
|
glBegin(GL_LINE_LOOP);
|
|
|
|
for (unsigned int k = 0; k < points->size(); k++)
|
|
{
|
|
glVertex2f(cur.x() + (points->at(k).x() * scale), cur.y() + (points->at(k).y() * scale));
|
|
}
|
|
|
|
glEnd();
|
|
|
|
glDisable(GL_LOGIC_OP);
|
|
glPopAttrib();
|
|
glPopMatrix(); // restore modelview
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPopMatrix();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
}
|
|
|
|
void drawPercentualPolyLine(GLArea * gla, QPoint &mid, MeshModel &m, GLfloat* pixels,
|
|
double* modelview_matrix, double* projection_matrix, GLint* viewport, float scale, vector<QPointF> * points)
|
|
{
|
|
double dX, dY, dZ; //near
|
|
double dX2, dY2, dZ2; //far
|
|
|
|
gluUnProject((double)mid.x(), mid.y(), 0.0, modelview_matrix, projection_matrix, viewport, &dX, &dY, &dZ);
|
|
gluUnProject((double)mid.x(), mid.y(), 1.0, modelview_matrix, projection_matrix, viewport, &dX2, &dY2, &dZ2);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
gluLookAt(dX, dY, dZ, dX2, dY2, dZ2, 1, 0, 0);
|
|
|
|
double mvmatrix2[16];
|
|
glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix2);
|
|
glPopMatrix();
|
|
|
|
double tx, ty, tz;
|
|
double tx2, ty2, tz2;
|
|
|
|
double inv_mvmatrix[16];
|
|
|
|
Matrix44d temp(mvmatrix2);
|
|
temp = Inverse(temp);
|
|
|
|
for (int k = 0; k < 16; k++)
|
|
inv_mvmatrix[k] = temp[k / 4][k % 4];
|
|
|
|
double a, b, c;
|
|
double a2, b2, c2;
|
|
|
|
int STEPS = 30;
|
|
int DSCALER = -7;
|
|
|
|
float diag = m.cm.bbox.Diag() * DSCALER;
|
|
|
|
float radius = scale; //TODO leftover
|
|
|
|
QVector<QPointF> proj_points(int(points->size()));
|
|
|
|
for (size_t i = 0; i < points->size(); i++)
|
|
{
|
|
/** calcs the far point of the line */
|
|
fastMultiply(points->at(i).x() * radius, points->at(i).y() * radius, diag, inv_mvmatrix, &tx, &ty, &tz);
|
|
|
|
gluProject(tx, ty, tz, modelview_matrix, projection_matrix, viewport, &a, &b, &c);
|
|
|
|
/** calcs the near point of the line */
|
|
fastMultiply(points->at(i).x() * radius, points->at(i).y() * radius, 0, inv_mvmatrix, &tx2, &ty2, &tz2);
|
|
|
|
gluProject(tx2, ty2, tz2, modelview_matrix, projection_matrix, viewport, &a2, &b2, &c2);
|
|
double da = (a - a2);// /(double)STEPS;
|
|
double db = (b - b2);// /(double)STEPS;
|
|
double dc = (c - c2);// /(double)STEPS;
|
|
double pix_x = a2;
|
|
double pix_y = b2;
|
|
double pix_z = c2;
|
|
|
|
/** to search with quicksearch the nearest zbuffer value in the line */
|
|
for (int k = 0; k < STEPS; k++)
|
|
{
|
|
double inv_yy = gla->height() - pix_y;
|
|
double zz = 999;
|
|
|
|
if ((int)pix_x >= 0 && (int)pix_x < gla->width() && (int)pix_y >= 0 && (int)pix_y < gla->height())
|
|
zz = (GLfloat)pixels[(int)(((int)pix_y)*gla->width() + (int)pix_x)];
|
|
da = da / 2.0;
|
|
db = db / 2.0;
|
|
dc = dc / 2.0;
|
|
if (std::abs(zz - pix_z) < 0.001)
|
|
{
|
|
proj_points[i] = QPointF(pix_x, inv_yy);
|
|
break;
|
|
}
|
|
else if (zz > pix_z)
|
|
{
|
|
pix_x += da;
|
|
pix_y += db;
|
|
pix_z += dc;
|
|
}
|
|
else
|
|
{
|
|
pix_x -= da;
|
|
pix_y -= db;
|
|
pix_z -= dc;
|
|
}
|
|
|
|
if (k == STEPS - 1)
|
|
{
|
|
proj_points[i] = QPointF(pix_x, inv_yy);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
glOrtho(0, gla->width(), gla->height(), 0, -1, 1);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
glPushAttrib(GL_ENABLE_BIT);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDisable(GL_LIGHTING);
|
|
glDisable(GL_TEXTURE_2D);
|
|
glEnable(GL_COLOR_LOGIC_OP);
|
|
glLogicOp(GL_XOR);
|
|
glColor3f(1, 1, 1);
|
|
|
|
/** draws the circle */
|
|
glBegin(GL_LINE_LOOP);
|
|
|
|
for (unsigned int i = 0; i < points->size(); i++)
|
|
{
|
|
glVertex2f(proj_points[i].x(), proj_points[i].y());
|
|
}
|
|
|
|
glEnd();
|
|
|
|
glDisable(GL_COLOR_LOGIC_OP);
|
|
glPopAttrib();
|
|
glPopMatrix(); // restore modelview
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPopMatrix();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
}
|
|
|
|
//each side is divided in given segments
|
|
void generatePolygon(std::vector<QPointF> & vertices, int sides, int segments)
|
|
{
|
|
float step = sides / 2.0;
|
|
|
|
float start_angle = M_PI / sides;
|
|
|
|
for (int k = 0; k < sides; k++)
|
|
{
|
|
vertices.push_back(QPointF(sin((M_PI * (float)k / step) + start_angle), cos((M_PI * (float)k / step) + start_angle)));
|
|
}
|
|
|
|
if (segments > 1)
|
|
{
|
|
int sk;
|
|
for (int k = 0; k < sides; k++)
|
|
{
|
|
sk = (k + 1) % sides;
|
|
QPointF kv = vertices.at(k);
|
|
QPointF skv = vertices.at(sk);
|
|
QPointF d = (skv - kv) / segments;
|
|
vertices.push_back(kv);
|
|
for (int j = 1; j < segments; j++)
|
|
{
|
|
vertices.push_back(kv + d * j);
|
|
}
|
|
}
|
|
vertices.erase(vertices.begin(), vertices.begin() + sides);
|
|
}
|
|
}
|
|
|
|
//TODO This should be done statically
|
|
|
|
/**
|
|
* Generates the same circle points that Gfrei's algorithm does
|
|
*/
|
|
void generateCircle(std::vector<QPointF> & vertices, int segments)
|
|
{
|
|
return generatePolygon(vertices, segments, 1);
|
|
}
|
|
|
|
void generateSquare(std::vector<QPointF> & vertices, int segments)
|
|
{
|
|
return generatePolygon(vertices, 4, segments);
|
|
}
|
|
|