Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/light manip UI #159

Merged
merged 9 commits into from
Jan 30, 2024
14 changes: 10 additions & 4 deletions agave_app/AppearanceDockWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

#include <QScrollArea>

QAppearanceWidget::QAppearanceWidget(QWidget* pParent, QRenderSettings* qrs, RenderSettings* rs)
QAppearanceWidget::QAppearanceWidget(QWidget* pParent,
QRenderSettings* qrs,
RenderSettings* rs,
QAction* pLightRotationAction)
: QWidget(pParent)
, m_MainLayout()
, m_AppearanceSettingsWidget(nullptr, qrs, rs)
, m_AppearanceSettingsWidget(nullptr, qrs, rs, pLightRotationAction)
{
// Create main layout
m_MainLayout.setAlignment(Qt::AlignTop);
Expand All @@ -18,9 +21,12 @@ QAppearanceWidget::QAppearanceWidget(QWidget* pParent, QRenderSettings* qrs, Ren
m_MainLayout.addWidget(scrollArea, 1, 0);
}

QAppearanceDockWidget::QAppearanceDockWidget(QWidget* parent, QRenderSettings* qrs, RenderSettings* rs)
QAppearanceDockWidget::QAppearanceDockWidget(QWidget* parent,
QRenderSettings* qrs,
RenderSettings* rs,
QAction* pLightRotationAction)
: QDockWidget(parent)
, m_VolumeAppearanceWidget(nullptr, qrs, rs)
, m_VolumeAppearanceWidget(nullptr, qrs, rs, pLightRotationAction)
{
setWindowTitle("Appearance");

Expand Down
10 changes: 8 additions & 2 deletions agave_app/AppearanceDockWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ class QAppearanceWidget : public QWidget
Q_OBJECT

public:
QAppearanceWidget(QWidget* pParent = NULL, QRenderSettings* qrs = nullptr, RenderSettings* rs = nullptr);
QAppearanceWidget(QWidget* pParent = NULL,
QRenderSettings* qrs = nullptr,
RenderSettings* rs = nullptr,
QAction* pLightRotationAction = nullptr);

void onNewImage(Scene* s) { m_AppearanceSettingsWidget.onNewImage(s); }

Expand All @@ -26,7 +29,10 @@ class QAppearanceDockWidget : public QDockWidget
Q_OBJECT

public:
QAppearanceDockWidget(QWidget* pParent = NULL, QRenderSettings* qrs = nullptr, RenderSettings* rs = nullptr);
QAppearanceDockWidget(QWidget* pParent = NULL,
QRenderSettings* qrs = nullptr,
RenderSettings* rs = nullptr,
QAction* pLightRotationAction = nullptr);

void onNewImage(Scene* s) { m_VolumeAppearanceWidget.onNewImage(s); }

Expand Down
47 changes: 44 additions & 3 deletions agave_app/AppearanceSettingsWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@
#include "tfeditor/gradients.h"

#include <QFormLayout>
#include <QFrame>
#include <QLinearGradient>

static const int MAX_CHANNELS_CHECKED = 4;

QAppearanceSettingsWidget::QAppearanceSettingsWidget(QWidget* pParent, QRenderSettings* qrs, RenderSettings* rs)
QAppearanceSettingsWidget::QAppearanceSettingsWidget(QWidget* pParent,
QRenderSettings* qrs,
RenderSettings* rs,
QAction* pLightRotationAction)
: QGroupBox(pParent)
, m_MainLayout()
, m_DensityScaleSlider()
Expand Down Expand Up @@ -198,7 +202,7 @@ QAppearanceSettingsWidget::QAppearanceSettingsWidget(QWidget* pParent, QRenderSe
m_clipRoiSection->setContentLayout(*roiSectionLayout);
m_MainLayout.addRow(m_clipRoiSection);

Section* section = createLightingControls();
Section* section = createLightingControls(pLightRotationAction);
m_MainLayout.addRow(section);

QFrame* lineA = new QFrame();
Expand All @@ -214,11 +218,22 @@ QAppearanceSettingsWidget::QAppearanceSettingsWidget(QWidget* pParent, QRenderSe
}

Section*
QAppearanceSettingsWidget::createLightingControls()
QAppearanceSettingsWidget::createLightingControls(QAction* pLightRotationAction)
{
Section* section = new Section("Lighting", 0);
auto* sectionLayout = Controls::createFormLayout();

m_lt0gui.m_enableControlsCheckBox = new QCheckBox();
m_lt0gui.m_enableControlsCheckBox->setStatusTip(
tr("Show interactive controls in viewport for area light rotation angle"));
m_lt0gui.m_enableControlsCheckBox->setToolTip(
tr("Show interactive controls in viewport for area light rotation angle"));
sectionLayout->addRow("Viewport Controls", m_lt0gui.m_enableControlsCheckBox);
QObject::connect(m_lt0gui.m_enableControlsCheckBox, &QCheckBox::clicked, pLightRotationAction, &QAction::trigger);
QObject::connect(pLightRotationAction, &QAction::triggered, [this](bool toggled) {
this->m_lt0gui.m_enableControlsCheckBox->setChecked(toggled);
});

m_lt0gui.m_thetaSlider = new QNumericSlider();
m_lt0gui.m_thetaSlider->setStatusTip(tr("Set angle theta for area light"));
m_lt0gui.m_thetaSlider->setToolTip(tr("Set angle theta for area light"));
Expand Down Expand Up @@ -279,6 +294,12 @@ QAppearanceSettingsWidget::createLightingControls()
this->OnSetAreaLightColor(v, this->m_lt0gui.m_areaLightColorButton->GetColor());
});

// separator
QFrame* line = new QFrame();
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
sectionLayout->addRow(line);

auto* skylightTopLayout = new QHBoxLayout();
m_lt1gui.m_stintensitySlider = new QNumericSlider();
m_lt1gui.m_stintensitySlider->setStatusTip(tr("Set intensity for top of skylight sphere"));
Expand Down Expand Up @@ -739,6 +760,26 @@ QAppearanceSettingsWidget::initLightingControls(Scene* scene)
m_lt0gui.m_intensitySlider->setValue(i * scene->m_lighting.m_Lights[1].m_ColorIntensity);
m_lt0gui.m_areaLightColorButton->SetColor(c);

// attach light observer to scene's area light source, to receive updates from viewport controls
// TODO FIXME clean this up - it's not removed anywhere so if light(i.e. scene) outlives "this" then we have problems.
// Currently in AGAVE this is not an issue..
scene->m_lighting.m_sceneLights[1].m_observers.push_back([this](const Light& light) {
// update gui controls

// bring theta into 0..2pi
m_lt0gui.m_thetaSlider->setValue(light.m_Theta < 0 ? light.m_Theta + TWO_PI_F : light.m_Theta);
// bring phi into 0..pi
m_lt0gui.m_phiSlider->setValue(light.m_Phi < 0 ? light.m_Phi + PI_F : light.m_Phi);
m_lt0gui.m_sizeSlider->setValue(light.m_Width);
m_lt0gui.m_distSlider->setValue(light.m_Distance);
// split color into color and intensity.
QColor c;
float i;
normalizeColorForGui(light.m_Color, c, i);
m_lt0gui.m_intensitySlider->setValue(i * light.m_ColorIntensity);
m_lt0gui.m_areaLightColorButton->SetColor(c);
});

normalizeColorForGui(scene->m_lighting.m_Lights[0].m_ColorTop, c, i);
m_lt1gui.m_stintensitySlider->setValue(i * scene->m_lighting.m_Lights[0].m_ColorTopIntensity);
m_lt1gui.m_stColorButton->SetColor(c);
Expand Down
8 changes: 6 additions & 2 deletions agave_app/AppearanceSettingsWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ class QAppearanceSettingsWidget : public QGroupBox
Q_OBJECT

public:
QAppearanceSettingsWidget(QWidget* pParent = NULL, QRenderSettings* qrs = nullptr, RenderSettings* rs = nullptr);
QAppearanceSettingsWidget(QWidget* pParent = NULL,
QRenderSettings* qrs = nullptr,
RenderSettings* rs = nullptr,
QAction* pLightRotationAction = nullptr);

void onNewImage(Scene* scene);

Expand Down Expand Up @@ -102,6 +105,7 @@ public slots:

struct lt0
{
QCheckBox* m_enableControlsCheckBox;
QNumericSlider* m_thetaSlider;
QNumericSlider* m_phiSlider;
QNumericSlider* m_sizeSlider;
Expand All @@ -120,6 +124,6 @@ public slots:
QColorPushButton* m_sbColorButton;
} m_lt1gui;

Section* createLightingControls();
Section* createLightingControls(QAction* pLightRotationAction);
void initLightingControls(Scene* scene);
};
64 changes: 38 additions & 26 deletions agave_app/GLView3D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,41 +287,53 @@ GLView3D::FitToScene()
}

void
GLView3D::keyPressEvent(QKeyEvent* event)
GLView3D::toggleAreaLightRotateControls()
{
static enum MODE { NONE, ROT, TRANS } mode = MODE::NONE;
// toggle rotate tool
if (m_areaLightMode == AREALIGHT_MODE::NONE || m_areaLightMode == AREALIGHT_MODE::TRANS) {
m_viewerWindow->showAreaLightGizmo(true);
m_viewerWindow->setTool(
new RotateTool(m_viewerWindow->m_toolsUseLocalSpace, ManipulationTool::s_manipulatorSize * devicePixelRatioF()));
m_viewerWindow->forEachTool(
[this](ManipulationTool* tool) { tool->setUseLocalSpace(m_viewerWindow->m_toolsUseLocalSpace); });
m_areaLightMode = AREALIGHT_MODE::ROT;
} else {
m_viewerWindow->showAreaLightGizmo(false);
m_viewerWindow->setTool(nullptr);
m_areaLightMode = AREALIGHT_MODE::NONE;
}
}

// TODO currently this function is not wired up to any gui at all.
// This is because translation of area light source still needs work.
// (Currently rotation is sufficient.)
void
GLView3D::toggleAreaLightTranslateControls()
{
// toggle translate tool
if (m_areaLightMode == AREALIGHT_MODE::NONE || m_areaLightMode == AREALIGHT_MODE::ROT) {
m_viewerWindow->showAreaLightGizmo(true);
m_viewerWindow->setTool(
new MoveTool(m_viewerWindow->m_toolsUseLocalSpace, ManipulationTool::s_manipulatorSize * devicePixelRatioF()));
m_viewerWindow->forEachTool(
[this](ManipulationTool* tool) { tool->setUseLocalSpace(m_viewerWindow->m_toolsUseLocalSpace); });
m_areaLightMode = AREALIGHT_MODE::TRANS;
} else {
m_viewerWindow->showAreaLightGizmo(false);
m_viewerWindow->setTool(nullptr);
m_areaLightMode = AREALIGHT_MODE::NONE;
}
}
Comment on lines +292 to +326
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A note for the future, there's some duplicated boilerplate between these two methods. Might be worth seeing if they can be combined or reduced whenever the translation controls go in.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's something for later. There's a big TODO at the top of the translate function and it may be completely changed when it's time to wire it up to something. This code is here to sort of remind us that it's similar to the rotate code but they can work together with the mode variable. Think I should just delete it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine to keep in, it's a good reminder.

void
GLView3D::keyPressEvent(QKeyEvent* event)
{
if (event->key() == Qt::Key_A) {
FitToScene();
} else if (event->key() == Qt::Key_L) {
// toggle local/global coordinates for transforms
m_viewerWindow->m_toolsUseLocalSpace = !m_viewerWindow->m_toolsUseLocalSpace;
m_viewerWindow->forEachTool(
[this](ManipulationTool* tool) { tool->setUseLocalSpace(m_viewerWindow->m_toolsUseLocalSpace); });
} else if (event->key() == Qt::Key_R) {
// toggle rotate tool
if (mode == MODE::NONE || mode == MODE::TRANS) {
m_viewerWindow->setTool(new RotateTool(m_viewerWindow->m_toolsUseLocalSpace,
ManipulationTool::s_manipulatorSize * devicePixelRatioF()));
m_viewerWindow->forEachTool(
[this](ManipulationTool* tool) { tool->setUseLocalSpace(m_viewerWindow->m_toolsUseLocalSpace); });
mode = MODE::ROT;
} else {
m_viewerWindow->setTool(nullptr);
mode = MODE::NONE;
}
} else if (event->key() == Qt::Key_T) {
// toggle translate tool
if (mode == MODE::NONE || mode == MODE::ROT) {
m_viewerWindow->setTool(
new MoveTool(m_viewerWindow->m_toolsUseLocalSpace, ManipulationTool::s_manipulatorSize * devicePixelRatioF()));
m_viewerWindow->forEachTool(
[this](ManipulationTool* tool) { tool->setUseLocalSpace(m_viewerWindow->m_toolsUseLocalSpace); });
mode = MODE::TRANS;
} else {
m_viewerWindow->setTool(nullptr);
mode = MODE::NONE;
}
} else {
QOpenGLWidget::keyPressEvent(event);
}
Expand Down
9 changes: 9 additions & 0 deletions agave_app/GLView3D.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class GLView3D : public QOpenGLWidget

void initCameraFromImage(Scene* scene);
void toggleCameraProjection();
void toggleAreaLightRotateControls();
void toggleAreaLightTranslateControls();

void onNewImage(Scene* scene);

Expand Down Expand Up @@ -103,4 +105,11 @@ public slots:
QTimer* m_etimer;

ViewerWindow* m_viewerWindow;

enum AREALIGHT_MODE
{
NONE,
ROT,
TRANS
} m_areaLightMode = AREALIGHT_MODE::NONE;
};
14 changes: 13 additions & 1 deletion agave_app/agaveGui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ agaveGui::agaveGui(QWidget* parent)
setStyleSheet("QToolTip{padding:3px;}");
m_ui.setupUi(this);

// create actions first so they can be deposited in other gui elements
createActions();
createMenus();
createToolbars();
Expand Down Expand Up @@ -168,6 +169,16 @@ agaveGui::createActions()
m_citationAction = new QAction(tr("&Cite AGAVE"), this);
m_citationAction->setStatusTip(tr("Cite AGAVE in your research"));
connect(m_citationAction, SIGNAL(triggered()), this, SLOT(onCitationAction()));

m_toggleRotateControlsAction = new QAction(tr("&Rotate controls"), this);
m_toggleRotateControlsAction->setStatusTip(tr("Show interactive controls in viewport for area light rotation angle"));
m_toggleRotateControlsAction->setCheckable(true);
m_toggleRotateControlsAction->setShortcut(QKeySequence(Qt::Key_R));
// tie the action to the main app window.
addAction(m_toggleRotateControlsAction);
connect(m_toggleRotateControlsAction, &QAction::triggered, [this](bool checked) {
this->m_glView->toggleAreaLightRotateControls();
});
}

void
Expand Down Expand Up @@ -246,7 +257,8 @@ agaveGui::createDockWindows()
addDockWidget(Qt::RightDockWidgetArea, m_timelinedock);
m_timelinedock->setVisible(false); // hide by default

m_appearanceDockWidget = new QAppearanceDockWidget(this, &m_qrendersettings, &m_renderSettings);
m_appearanceDockWidget =
new QAppearanceDockWidget(this, &m_qrendersettings, &m_renderSettings, m_toggleRotateControlsAction);
m_appearanceDockWidget->setAllowedAreas(Qt::AllDockWidgetAreas);
addDockWidget(Qt::LeftDockWidgetArea, m_appearanceDockWidget);

Expand Down
1 change: 1 addition & 0 deletions agave_app/agaveGui.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ private slots:
QAction* m_reportBugAction = nullptr;
QAction* m_sourceCodeAction = nullptr;
QAction* m_citationAction = nullptr;
QAction* m_toggleRotateControlsAction = nullptr;

QSlider* createAngleSlider();
QSlider* createRangeSlider();
Expand Down
21 changes: 0 additions & 21 deletions renderlib/AppScene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,24 +150,3 @@ Scene::getFirst4EnabledChannels(uint32_t& c0, uint32_t& c1, uint32_t& c2, uint32
c2 = ch[2];
c3 = ch[3];
}

void
SceneLight::updateTransform()
{
// update from transform position and rotation.

// calculate the new direction for the light to point in
glm::vec3 normdir = m_transform.m_rotation * glm::vec3(0, 0, 1);

// compute the phi and theta for the new direction
// because they will be used in future light updates
float phi, theta;
Light::cartesianToSpherical(normdir, phi, theta);
m_light->m_Phi = phi;
m_light->m_Theta = theta;

// get m_P in world space:
m_light->m_P = m_light->m_Distance * normdir + m_light->m_Target;

m_light->updateBasisFrame();
}
8 changes: 0 additions & 8 deletions renderlib/AppScene.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,6 @@ struct VolumeDisplay
GradientData m_gradientData[MAX_CPU_CHANNELS];
};

// MUST NOT OUTLIVE ITS LIGHT
class SceneLight : public SceneObject
{
public:
void updateTransform();
Light* m_light;
};

#define MAX_NO_LIGHTS 4
class Lighting
{
Expand Down
26 changes: 26 additions & 0 deletions renderlib/Light.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,29 @@ Light::cartesianToSpherical(glm::vec3 v, float& phi, float& theta)
phi = acosf(v.y);
theta = atan2f(v.x, v.z);
}

void
SceneLight::updateTransform()
{
// update from transform position and rotation.

// calculate the new direction for the light to point in
glm::vec3 normdir = m_transform.m_rotation * glm::vec3(0, 0, 1);

// compute the phi and theta for the new direction
// because they will be used in future light updates
float phi, theta;
Light::cartesianToSpherical(normdir, phi, theta);
m_light->m_Phi = phi;
m_light->m_Theta = theta;

// get m_P in world space:
m_light->m_P = m_light->m_Distance * normdir + m_light->m_Target;

m_light->updateBasisFrame();

// this lets the GUI have a chance to update in an abstract way
for (auto it = m_observers.begin(); it != m_observers.end(); ++it) {
(*it)(*m_light);
}
}
Loading
Loading