Updates to cmodview:

- Use better values for near and far camera planes to prevent clipping
- Added selection capability
- Improved zoom function
sensor-dev
Chris Laurel 2010-02-11 04:05:35 +00:00
parent f9260386b2
commit c83622222c
7 changed files with 457 additions and 38 deletions

View File

@ -11,14 +11,16 @@ HEADERS = \
mainwindow.h \
modelviewwidget.h \
convert3ds.h \
cmodops.h
cmodops.h \
materialwidget.h
SOURCES = \
mainwindow.cpp \
cmodview.cpp \
modelviewwidget.cpp \
convert3ds.cpp \
cmodops.cpp
cmodops.cpp \
materialwidget.cpp
#### CMOD Mesh library ####

View File

@ -10,6 +10,7 @@
#include <QtGui>
#include "mainwindow.h"
#include "materialwidget.h"
#include "convert3ds.h"
#include "cmodops.h"
#include <cel3ds/3dsread.h>
@ -68,11 +69,13 @@ MainWindow::MainWindow() :
QAction* generateTangentsAction = new QAction(tr("Generate &Tangents..."), this);
QAction* uniquifyVerticesAction = new QAction(tr("&Uniquify Vertices"), this);
QAction* mergeMeshesAction = new QAction(tr("&Merge Meshes"), this);
QAction* editMaterialAction = new QAction(tr("&Edit Material"), this);
operationsMenu->addAction(generateNormalsAction);
operationsMenu->addAction(generateTangentsAction);
operationsMenu->addAction(uniquifyVerticesAction);
operationsMenu->addAction(mergeMeshesAction);
operationsMenu->addAction(editMaterialAction);
menuBar->addMenu(operationsMenu);
setMenuBar(menuBar);
@ -100,6 +103,7 @@ MainWindow::MainWindow() :
connect(generateTangentsAction, SIGNAL(triggered()), this, SLOT(generateTangents()));
connect(uniquifyVerticesAction, SIGNAL(triggered()), this, SLOT(uniquifyVertices()));
connect(mergeMeshesAction, SIGNAL(triggered()), this, SLOT(mergeMeshes()));
connect(editMaterialAction, SIGNAL(triggered()), this, SLOT(editMaterial()));
}
@ -564,3 +568,30 @@ MainWindow::mergeMeshes()
setModel(modelFileName(), newModel);
}
void
MainWindow::editMaterial()
{
QDialog dialog(this);
MaterialWidget* materialWidget = new MaterialWidget(&dialog);
QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(materialWidget);
if (!m_modelView->selection().isEmpty())
{
QSetIterator<Mesh::PrimitiveGroup*> iter(m_modelView->selection());
Mesh::PrimitiveGroup* selectedGroup = iter.next();
const Material* material = m_modelView->model()->getMaterial(selectedGroup->materialIndex);
if (material)
{
materialWidget->setMaterial(*material);
}
}
dialog.setLayout(layout);
dialog.resize(350, 250);
dialog.show();
dialog.exec();
}

View File

@ -49,6 +49,7 @@ public slots:
void generateTangents();
void uniquifyVertices();
void mergeMeshes();
void editMaterial();
private:
ModelViewWidget* m_modelView;

View File

@ -0,0 +1,187 @@
// materialwidget.cpp
//
// Copyright (C) 2010, Chris Laurel <claurel@gmail.com>
//
// 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.
#include "materialwidget.h"
#include <celmodel/material.h>
#include <QFormLayout>
#include <QPushButton>
#include <QColorDialog>
using namespace cmod;
static QColor toQtColor(const Material::Color& color)
{
return QColor((int) (color.red()) * 255.99f, (int) (color.green() * 255.99f), (int) (color.blue() * 255.99f));
}
static Material::Color fromQtColor(const QColor& color)
{
return Material::Color(color.redF(), color.greenF(), color.blueF());
}
static void setWidgetColor(QLabel* widget, const Material::Color& color)
{
widget->setPalette(QPalette(toQtColor(color)));
widget->setAutoFillBackground(true);
widget->setText(QString("%1, %2, %3").arg(color.red(), 0, 'g', 3).arg(color.green(), 0, 'g', 3).arg(color.blue(), 0, 'g', 3));
}
MaterialWidget::MaterialWidget(QWidget* parent) :
QWidget(parent)
{
QGridLayout *layout = new QGridLayout;
layout->setColumnStretch(1, 1);
int frameStyle = QFrame::Sunken | QFrame::Panel;
m_diffuseColor = new QLabel(this);
m_specularColor = new QLabel(this);
m_emissiveColor = new QLabel(this);
m_opacity = new QLineEdit(this);
m_specularPower = new QLineEdit(this);
m_baseTexture = new QLabel(this);
m_specularMap = new QLabel(this);
m_emissiveMap = new QLabel(this);
m_normalMap = new QLabel(this);
m_diffuseColor->setFrameStyle(frameStyle);
m_specularColor->setFrameStyle(frameStyle);
m_emissiveColor->setFrameStyle(frameStyle);
m_baseTexture->setFrameStyle(frameStyle);
m_specularMap->setFrameStyle(frameStyle);
m_emissiveMap->setFrameStyle(frameStyle);
m_normalMap->setFrameStyle(frameStyle);
QPushButton* changeDiffuse = new QPushButton(tr("Change..."), this);
QPushButton* changeSpecular = new QPushButton(tr("Change..."), this);
QPushButton* changeEmissive = new QPushButton(tr("Change..."), this);
QPushButton* changeBaseTexture = new QPushButton(tr("Change..."), this);
QPushButton* changeSpecularMap = new QPushButton(tr("Change..."), this);
QPushButton* changeEmissiveMap = new QPushButton(tr("Change..."), this);
QPushButton* changeNormalMap = new QPushButton(tr("Change..."), this);
layout->addWidget(new QLabel(tr("Diffuse")), 0, 0);
layout->addWidget(m_diffuseColor, 0, 1);
layout->addWidget(changeDiffuse, 0, 2);
layout->addWidget(new QLabel(tr("Specular")), 1, 0);
layout->addWidget(m_specularColor, 1, 1);
layout->addWidget(changeSpecular, 1, 2);
layout->addWidget(new QLabel(tr("Emissive")), 2, 0);
layout->addWidget(m_emissiveColor, 2, 1);
layout->addWidget(changeEmissive, 2, 2);
layout->addWidget(new QLabel(tr("Opacity")), 3, 0);
layout->addWidget(m_opacity, 3, 1);
layout->addWidget(new QLabel(tr("Shininess")), 4, 0);
layout->addWidget(m_specularPower, 4, 1);
layout->addWidget(new QLabel(tr("Base Texture")), 5, 0);
layout->addWidget(m_baseTexture, 5, 1);
layout->addWidget(changeBaseTexture, 5, 2);
layout->addWidget(new QLabel(tr("Specular Map")), 6, 0);
layout->addWidget(m_specularMap, 6, 1);
layout->addWidget(changeSpecularMap, 6, 2);
layout->addWidget(new QLabel(tr("Emissive Map")), 7, 0);
layout->addWidget(m_emissiveMap, 7, 1);
layout->addWidget(changeEmissiveMap, 7, 2);
layout->addWidget(new QLabel(tr("Normal Map")), 8, 0);
layout->addWidget(m_normalMap, 8, 1);
layout->addWidget(changeNormalMap, 8, 2);
connect(changeDiffuse, SIGNAL(clicked()), this, SLOT(editDiffuse()));
connect(changeSpecular, SIGNAL(clicked()), this, SLOT(editSpecular()));
connect(changeEmissive, SIGNAL(clicked()), this, SLOT(editEmissive()));
setMaterial(Material());
this->setLayout(layout);
}
MaterialWidget::~MaterialWidget()
{
}
void
MaterialWidget::setMaterial(const Material& material)
{
m_material = material;
setWidgetColor(m_diffuseColor, m_material.diffuse);
setWidgetColor(m_specularColor, m_material.specular);
setWidgetColor(m_emissiveColor, m_material.emissive);
m_opacity->setText(QString::number(m_material.opacity));
m_specularPower->setText(QString::number(m_material.specularPower));
emit materialChanged();
}
void
MaterialWidget::editDiffuse()
{
QColor color = QColorDialog::getColor(toQtColor(m_material.diffuse), this);
m_material.diffuse = fromQtColor(color);
setWidgetColor(m_diffuseColor, m_material.diffuse);
emit materialChanged();
}
void
MaterialWidget::editSpecular()
{
QColor color = QColorDialog::getColor(toQtColor(m_material.specular), this);
m_material.specular = fromQtColor(color);
setWidgetColor(m_specularColor, m_material.specular);
emit materialChanged();
}
void
MaterialWidget::editEmissive()
{
QColor color = QColorDialog::getColor(toQtColor(m_material.emissive), this);
m_material.emissive = fromQtColor(color);
setWidgetColor(m_emissiveColor, m_material.emissive);
emit materialChanged();
}
void
MaterialWidget::editBaseTexture()
{
}
void
MaterialWidget::editSpecularMap()
{
}
void
MaterialWidget::editEmissiveMap()
{
}
void
MaterialWidget::editNormalMap()
{
}

View File

@ -0,0 +1,59 @@
// materialwidget.h
//
// Copyright (C) 2010, Chris Laurel <claurel@gmail.com>
//
// 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.
#ifndef _CMODVIEW_MATERIAL_WIDGET_H_
#define _CMODVIEW_MATERIAL_WIDGET_H_
#include <celmodel/material.h>
#include <QWidget>
#include <QLabel>
#include <QLineEdit>
class MaterialWidget : public QWidget
{
Q_OBJECT
public:
MaterialWidget(QWidget* parent);
~MaterialWidget();
const cmod::Material& material() const
{
return m_material;
}
void setMaterial(const cmod::Material& material);
public slots:
void editDiffuse();
void editSpecular();
void editEmissive();
void editBaseTexture();
void editSpecularMap();
void editEmissiveMap();
void editNormalMap();
signals:
void materialChanged();
private:
QLabel* m_diffuseColor;
QLabel* m_specularColor;
QLabel* m_emissiveColor;
QLineEdit* m_opacity;
QLineEdit* m_specularPower;
QLabel* m_baseTexture;
QLabel* m_specularMap;
QLabel* m_emissiveMap;
QLabel* m_normalMap;
cmod::Material m_material;
};
#endif // _CMODVIEW_MATERIAL_WIDGET_H_

View File

@ -11,10 +11,13 @@
#include <GL/glew.h>
#include "modelviewwidget.h"
#include <QtOpenGL>
#include <Eigen/LU>
using namespace cmod;
using namespace Eigen;
static const float VIEWPORT_FOV = 45.0;
static const double PI = 3.1415926535897932;
class MaterialLibrary
{
@ -36,7 +39,12 @@ public:
QString ext = QFileInfo(fileName).suffix().toLower();
if (ext == "dds")
{
return m_glWidget->bindTexture(fileName);
GLuint texId = m_glWidget->bindTexture(fileName);
// Qt doesn't seem to enable mipmap filtering automatically
// TODO: Check whether the texture has mipmaps:
// glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, &maxLevel);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
return texId;
}
else
{
@ -125,6 +133,8 @@ ModelViewWidget::setModel(cmod::Model* model, const QString& modelDirPath)
}
m_materialLibrary = new MaterialLibrary(this, modelDirPath);
m_selection.clear();
// Load materials
if (m_model != NULL)
{
@ -154,7 +164,7 @@ ModelViewWidget::resetCamera()
}
}
m_modelBoundingRadius = bbox.max().norm();
m_modelBoundingRadius = std::max(bbox.max().norm(), bbox.min().norm());
m_cameraPosition = m_modelBoundingRadius * Vector3d::UnitZ() * 2.0;
m_cameraOrientation = Quaterniond::Identity();
}
@ -174,6 +184,20 @@ void
ModelViewWidget::mousePressEvent(QMouseEvent *event)
{
m_lastMousePosition = event->pos();
m_mouseDownPosition = event->pos();
}
void
ModelViewWidget::mouseReleaseEvent(QMouseEvent* event)
{
int moveDistance = (event->pos() - m_mouseDownPosition).manhattanLength();
if (moveDistance < 3)
{
float x = (float) event->pos().x() / (float) size().width() * 2.0f - 1.0f;
float y = (float) event->pos().y() / (float) size().height() * -2.0f + 1.0f;
select(Vector2f(x, y));
}
}
@ -212,14 +236,60 @@ ModelViewWidget::wheelEvent(QWheelEvent* event)
}
// Mouse wheel controls camera dolly
#if LINEAR_DOLLY
double adjust = m_modelBoundingRadius * event->delta() / 1000.0;
double newDistance = m_cameraPosition.norm() + adjust;
m_cameraPosition = m_cameraPosition.normalized() * newDistance;
#else
double adjust = std::pow(2.0, event->delta() / 1000.0);
double newDistance = m_cameraPosition.norm() * adjust;
m_cameraPosition = m_cameraPosition.normalized() * newDistance;
#endif
update();
}
void
ModelViewWidget::select(const Vector2f& viewportPoint)
{
if (!m_model)
{
return;
}
float aspectRatio = (float) size().width() / (float) size().height();
float fovRad = float(VIEWPORT_FOV * PI / 180.0f);
float h = (float) tan(fovRad / 2.0f);
Vector3d direction(h * aspectRatio * viewportPoint.x(), h * viewportPoint.y(), -1.0f);
direction.normalize();
Vector3d origin = Vector3d::Zero();
Transform3d camera(cameraTransform().inverse());
Mesh::PickResult pickResult;
bool hit = m_model->pick(camera * origin, camera.linear() * direction, &pickResult);
if (hit)
{
m_selection.clear();
m_selection.insert(pickResult.group);
update();
}
else
{
m_selection.clear();
update();
}
}
Transform3d
ModelViewWidget::cameraTransform() const
{
Transform3d t(m_cameraOrientation.conjugate());
t.translate(-m_cameraPosition);
return t;
}
void
ModelViewWidget::initializeGL()
{
@ -233,12 +303,14 @@ ModelViewWidget::paintGL()
glDepthMask(GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
double distanceToOrigin = m_cameraPosition.norm();
double nearDistance = std::max(m_modelBoundingRadius * 0.001, distanceToOrigin - m_modelBoundingRadius);
double farDistance = m_modelBoundingRadius + distanceToOrigin;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
double aspectRatio = (double) size().width() / (double) size().height();
double nearDistance = m_modelBoundingRadius * 0.05;
double farDistance = m_modelBoundingRadius * 20.0;
gluPerspective(45.0, aspectRatio, nearDistance, farDistance);
gluPerspective(VIEWPORT_FOV, aspectRatio, nearDistance, farDistance);
glEnable(GL_LIGHTING);
@ -263,10 +335,7 @@ ModelViewWidget::paintGL()
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
Transform3d cameraRotation(m_cameraOrientation.conjugate());
glMultMatrixd(cameraRotation.data());
glTranslated(-m_cameraPosition.x(), -m_cameraPosition.y(), -m_cameraPosition.z());
glMultMatrixd(cameraTransform().data());
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
@ -274,6 +343,12 @@ ModelViewWidget::paintGL()
if (m_model)
{
renderModel(m_model);
if (!m_selection.isEmpty())
{
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(-0.0f, -1.0f);
renderSelection(m_model);
}
}
GLenum errorCode = glGetError();
@ -309,6 +384,7 @@ static int GLComponentCounts[Mesh::FormatMax] =
4, // UByte4
};
static void
setVertexArrays(const Mesh::VertexDescription& desc, const void* vertexData)
{
@ -377,6 +453,50 @@ setVertexArrays(const Mesh::VertexDescription& desc, const void* vertexData)
}
// Set just the vertex pointer
void
setVertexPointer(const Mesh::VertexDescription& desc, const void* vertexData)
{
const Mesh::VertexAttribute& position = desc.getAttribute(Mesh::Position);
// Can't render anything unless we have positions
if (position.format != Mesh::Float3)
return;
// Set up the vertex arrays
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, desc.stride,
reinterpret_cast<const char*>(vertexData) + position.offset);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
static GLenum
getGLMode(Mesh::PrimitiveGroupType primitive)
{
switch (primitive)
{
case Mesh::TriList:
return GL_TRIANGLES;
case Mesh::TriStrip:
return GL_TRIANGLE_STRIP;
case Mesh::TriFan:
return GL_TRIANGLE_FAN;
case Mesh::LineList:
return GL_LINES;
case Mesh::LineStrip:
return GL_LINE_STRIP;
case Mesh::PointList:
return GL_POINTS;
default:
return GL_POINTS;
}
}
void
ModelViewWidget::bindMaterial(const Material* material)
{
@ -460,37 +580,42 @@ ModelViewWidget::renderModel(Model* model)
}
bindMaterial(material);
GLenum primitiveMode = 0;
bool validMode = true;
switch (group->prim)
{
case Mesh::TriList:
primitiveMode = GL_TRIANGLES;
break;
case Mesh::TriStrip:
primitiveMode = GL_TRIANGLE_STRIP;
break;
case Mesh::TriFan:
primitiveMode = GL_TRIANGLE_FAN;
break;
case Mesh::LineList:
primitiveMode = GL_LINES;
break;
case Mesh::LineStrip:
primitiveMode = GL_LINE_STRIP;
break;
case Mesh::PointList:
primitiveMode = GL_POINTS;
break;
default:
validMode = false;
break;
}
GLenum primitiveMode = getGLMode(group->prim);
glDrawElements(primitiveMode, group->nIndices, GL_UNSIGNED_INT, group->indices);
}
}
}
if (validMode)
void
ModelViewWidget::renderSelection(Model* model)
{
glEnable(GL_CULL_FACE);
glPolygonMode(GL_FRONT, GL_LINE);
glDisable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
glColor4f(0.0f, 1.0f, 0.0f, 0.5f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(GL_FALSE);
for (unsigned int meshIndex = 0; meshIndex < model->getMeshCount(); ++meshIndex)
{
Mesh* mesh = model->getMesh(meshIndex);
setVertexPointer(mesh->getVertexDescription(), mesh->getVertexData());
for (unsigned int groupIndex = 0; groupIndex < mesh->getGroupCount(); ++groupIndex)
{
Mesh::PrimitiveGroup* group = mesh->getGroup(groupIndex);
if (m_selection.contains(group))
{
GLenum primitiveMode = getGLMode(group->prim);
glDrawElements(primitiveMode, group->nIndices, GL_UNSIGNED_INT, group->indices);
}
}
}
glPolygonMode(GL_FRONT, GL_FILL);
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
}

View File

@ -12,6 +12,7 @@
#define _CMODVIEW_MODEL_VIEW_WIDGET_H_
#include <QGLWidget>
#include <QSet>
#include <celmodel/model.h>
#include <Eigen/Core>
#include <Eigen/Geometry>
@ -43,15 +44,24 @@ public:
};
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void wheelEvent(QWheelEvent* event);
void select(const Eigen::Vector2f& point);
QSet<cmod::Mesh::PrimitiveGroup*> selection()
{
return m_selection;
}
void setRenderStyle(RenderStyle style);
RenderStyle renderStyle() const
{
return m_renderStyle;
}
Eigen::Transform3d cameraTransform() const;
protected:
void initializeGL();
void paintGL();
@ -59,6 +69,7 @@ protected:
private:
void renderModel(cmod::Model* model);
void renderSelection(cmod::Model* model);
void bindMaterial(const cmod::Material* material);
private:
@ -67,9 +78,12 @@ private:
Eigen::Vector3d m_cameraPosition;
Eigen::Quaterniond m_cameraOrientation;
QPoint m_lastMousePosition;
QPoint m_mouseDownPosition;
RenderStyle m_renderStyle;
MaterialLibrary* m_materialLibrary;
QSet<cmod::Mesh::PrimitiveGroup*> m_selection;
};
#endif // _CMODVIEW_MODEL_VIEW_WIDGET_H_