636 lines
24 KiB
C++
636 lines
24 KiB
C++
/*
|
|
src/example1.cpp -- C++ version of an example application that shows
|
|
how to use the various widget classes. For a Python implementation, see
|
|
'../python/example1.py'.
|
|
|
|
NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>.
|
|
The widget drawing code is based on the NanoVG demo application
|
|
by Mikko Mononen.
|
|
|
|
All rights reserved. Use of this source code is governed by a
|
|
BSD-style license that can be found in the LICENSE.txt file.
|
|
*/
|
|
|
|
#include <nanogui/opengl.h>
|
|
#include <nanogui/glutil.h>
|
|
#include <nanogui/screen.h>
|
|
#include <nanogui/window.h>
|
|
#include <nanogui/layout.h>
|
|
#include <nanogui/label.h>
|
|
#include <nanogui/checkbox.h>
|
|
#include <nanogui/button.h>
|
|
#include <nanogui/toolbutton.h>
|
|
#include <nanogui/popupbutton.h>
|
|
#include <nanogui/combobox.h>
|
|
#include <nanogui/progressbar.h>
|
|
#include <nanogui/entypo.h>
|
|
#include <nanogui/messagedialog.h>
|
|
#include <nanogui/textbox.h>
|
|
#include <nanogui/slider.h>
|
|
#include <nanogui/imagepanel.h>
|
|
#include <nanogui/imageview.h>
|
|
#include <nanogui/vscrollpanel.h>
|
|
#include <nanogui/colorwheel.h>
|
|
#include <nanogui/colorpicker.h>
|
|
#include <nanogui/graph.h>
|
|
#include <nanogui/tabwidget.h>
|
|
#include <iostream>
|
|
#include <string>
|
|
|
|
// Includes for the GLTexture class.
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#if defined(__GNUC__)
|
|
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
|
#endif
|
|
#if defined(_WIN32)
|
|
# pragma warning(push)
|
|
# pragma warning(disable: 4457 4456 4005 4312)
|
|
#endif
|
|
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
#include <stb_image.h>
|
|
|
|
#if defined(_WIN32)
|
|
# pragma warning(pop)
|
|
#endif
|
|
#if defined(_WIN32)
|
|
# if defined(APIENTRY)
|
|
# undef APIENTRY
|
|
# endif
|
|
# include <windows.h>
|
|
#endif
|
|
|
|
using std::cout;
|
|
using std::cerr;
|
|
using std::endl;
|
|
using std::string;
|
|
using std::vector;
|
|
using std::pair;
|
|
using std::to_string;
|
|
|
|
class GLTexture {
|
|
public:
|
|
using handleType = std::unique_ptr<uint8_t[], void(*)(void*)>;
|
|
GLTexture() = default;
|
|
GLTexture(const std::string& textureName)
|
|
: mTextureName(textureName), mTextureId(0) {}
|
|
|
|
GLTexture(const std::string& textureName, GLint textureId)
|
|
: mTextureName(textureName), mTextureId(textureId) {}
|
|
|
|
GLTexture(const GLTexture& other) = delete;
|
|
GLTexture(GLTexture&& other) noexcept
|
|
: mTextureName(std::move(other.mTextureName)),
|
|
mTextureId(other.mTextureId) {
|
|
other.mTextureId = 0;
|
|
}
|
|
GLTexture& operator=(const GLTexture& other) = delete;
|
|
GLTexture& operator=(GLTexture&& other) noexcept {
|
|
mTextureName = std::move(other.mTextureName);
|
|
std::swap(mTextureId, other.mTextureId);
|
|
return *this;
|
|
}
|
|
~GLTexture() noexcept {
|
|
if (mTextureId)
|
|
glDeleteTextures(1, &mTextureId);
|
|
}
|
|
|
|
GLuint texture() const { return mTextureId; }
|
|
const std::string& textureName() const { return mTextureName; }
|
|
|
|
/**
|
|
* Load a file in memory and create an OpenGL texture.
|
|
* Returns a handle type (an std::unique_ptr) to the loaded pixels.
|
|
*/
|
|
handleType load(const std::string& fileName) {
|
|
if (mTextureId) {
|
|
glDeleteTextures(1, &mTextureId);
|
|
mTextureId = 0;
|
|
}
|
|
int force_channels = 0;
|
|
int w, h, n;
|
|
handleType textureData(stbi_load(fileName.c_str(), &w, &h, &n, force_channels), stbi_image_free);
|
|
if (!textureData)
|
|
throw std::invalid_argument("Could not load texture data from file " + fileName);
|
|
glGenTextures(1, &mTextureId);
|
|
glBindTexture(GL_TEXTURE_2D, mTextureId);
|
|
GLint internalFormat;
|
|
GLint format;
|
|
switch (n) {
|
|
case 1: internalFormat = GL_R8; format = GL_RED; break;
|
|
case 2: internalFormat = GL_RG8; format = GL_RG; break;
|
|
case 3: internalFormat = GL_RGB8; format = GL_RGB; break;
|
|
case 4: internalFormat = GL_RGBA8; format = GL_RGBA; break;
|
|
default: internalFormat = 0; format = 0; break;
|
|
}
|
|
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, format, GL_UNSIGNED_BYTE, textureData.get());
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
return textureData;
|
|
}
|
|
|
|
private:
|
|
std::string mTextureName;
|
|
GLuint mTextureId;
|
|
};
|
|
|
|
class ExampleApplication : public nanogui::Screen {
|
|
public:
|
|
ExampleApplication() : nanogui::Screen(Eigen::Vector2i(1024, 768), "NanoGUI Test") {
|
|
using namespace nanogui;
|
|
|
|
Window *window = new Window(this, "Button demo");
|
|
window->setPosition(Vector2i(15, 15));
|
|
window->setLayout(new GroupLayout());
|
|
|
|
/* No need to store a pointer, the data structure will be automatically
|
|
freed when the parent window is deleted */
|
|
new Label(window, "Push buttons", "sans-bold");
|
|
|
|
Button *b = new Button(window, "Plain button");
|
|
b->setCallback([] { cout << "pushed!" << endl; });
|
|
b->setTooltip("short tooltip");
|
|
|
|
/* Alternative construction notation using variadic template */
|
|
b = window->add<Button>("Styled", ENTYPO_ICON_ROCKET);
|
|
b->setBackgroundColor(Color(0, 0, 255, 25));
|
|
b->setCallback([] { cout << "pushed!" << endl; });
|
|
b->setTooltip("This button has a fairly long tooltip. It is so long, in "
|
|
"fact, that the shown text will span several lines.");
|
|
|
|
new Label(window, "Toggle buttons", "sans-bold");
|
|
b = new Button(window, "Toggle me");
|
|
b->setFlags(Button::ToggleButton);
|
|
b->setChangeCallback([](bool state) { cout << "Toggle button state: " << state << endl; });
|
|
|
|
new Label(window, "Radio buttons", "sans-bold");
|
|
b = new Button(window, "Radio button 1");
|
|
b->setFlags(Button::RadioButton);
|
|
b = new Button(window, "Radio button 2");
|
|
b->setFlags(Button::RadioButton);
|
|
|
|
new Label(window, "A tool palette", "sans-bold");
|
|
Widget *tools = new Widget(window);
|
|
tools->setLayout(new BoxLayout(Orientation::Horizontal,
|
|
Alignment::Middle, 0, 6));
|
|
|
|
b = new ToolButton(tools, ENTYPO_ICON_CLOUD);
|
|
b = new ToolButton(tools, ENTYPO_ICON_CONTROLLER_FAST_FORWARD);
|
|
b = new ToolButton(tools, ENTYPO_ICON_COMPASS);
|
|
b = new ToolButton(tools, ENTYPO_ICON_INSTALL);
|
|
|
|
new Label(window, "Popup buttons", "sans-bold");
|
|
PopupButton *popupBtn = new PopupButton(window, "Popup", ENTYPO_ICON_EXPORT);
|
|
Popup *popup = popupBtn->popup();
|
|
popup->setLayout(new GroupLayout());
|
|
new Label(popup, "Arbitrary widgets can be placed here");
|
|
new CheckBox(popup, "A check box");
|
|
// popup right
|
|
popupBtn = new PopupButton(popup, "Recursive popup", ENTYPO_ICON_FLASH);
|
|
Popup *popupRight = popupBtn->popup();
|
|
popupRight->setLayout(new GroupLayout());
|
|
new CheckBox(popupRight, "Another check box");
|
|
// popup left
|
|
popupBtn = new PopupButton(popup, "Recursive popup", ENTYPO_ICON_FLASH);
|
|
popupBtn->setSide(Popup::Side::Left);
|
|
Popup *popupLeft = popupBtn->popup();
|
|
popupLeft->setLayout(new GroupLayout());
|
|
new CheckBox(popupLeft, "Another check box");
|
|
|
|
window = new Window(this, "Basic widgets");
|
|
window->setPosition(Vector2i(200, 15));
|
|
window->setLayout(new GroupLayout());
|
|
|
|
new Label(window, "Message dialog", "sans-bold");
|
|
tools = new Widget(window);
|
|
tools->setLayout(new BoxLayout(Orientation::Horizontal,
|
|
Alignment::Middle, 0, 6));
|
|
b = new Button(tools, "Info");
|
|
b->setCallback([&] {
|
|
auto dlg = new MessageDialog(this, MessageDialog::Type::Information, "Title", "This is an information message");
|
|
dlg->setCallback([](int result) { cout << "Dialog result: " << result << endl; });
|
|
});
|
|
b = new Button(tools, "Warn");
|
|
b->setCallback([&] {
|
|
auto dlg = new MessageDialog(this, MessageDialog::Type::Warning, "Title", "This is a warning message");
|
|
dlg->setCallback([](int result) { cout << "Dialog result: " << result << endl; });
|
|
});
|
|
b = new Button(tools, "Ask");
|
|
b->setCallback([&] {
|
|
auto dlg = new MessageDialog(this, MessageDialog::Type::Warning, "Title", "This is a question message", "Yes", "No", true);
|
|
dlg->setCallback([](int result) { cout << "Dialog result: " << result << endl; });
|
|
});
|
|
|
|
vector<pair<int, string>>
|
|
icons = loadImageDirectory(mNVGContext, "icons");
|
|
#if defined(_WIN32)
|
|
string resourcesFolderPath("../resources/");
|
|
#else
|
|
string resourcesFolderPath("./");
|
|
#endif
|
|
|
|
new Label(window, "Image panel & scroll panel", "sans-bold");
|
|
PopupButton *imagePanelBtn = new PopupButton(window, "Image Panel");
|
|
imagePanelBtn->setIcon(ENTYPO_ICON_FOLDER);
|
|
popup = imagePanelBtn->popup();
|
|
VScrollPanel *vscroll = new VScrollPanel(popup);
|
|
ImagePanel *imgPanel = new ImagePanel(vscroll);
|
|
imgPanel->setImages(icons);
|
|
popup->setFixedSize(Vector2i(245, 150));
|
|
|
|
auto imageWindow = new Window(this, "Selected image");
|
|
imageWindow->setPosition(Vector2i(710, 15));
|
|
imageWindow->setLayout(new GroupLayout());
|
|
|
|
// Load all of the images by creating a GLTexture object and saving the pixel data.
|
|
for (auto& icon : icons) {
|
|
GLTexture texture(icon.second);
|
|
auto data = texture.load(resourcesFolderPath + icon.second + ".png");
|
|
mImagesData.emplace_back(std::move(texture), std::move(data));
|
|
}
|
|
|
|
// Set the first texture
|
|
auto imageView = new ImageView(imageWindow, mImagesData[0].first.texture());
|
|
mCurrentImage = 0;
|
|
// Change the active textures.
|
|
imgPanel->setCallback([this, imageView](int i) {
|
|
imageView->bindImage(mImagesData[i].first.texture());
|
|
mCurrentImage = i;
|
|
cout << "Selected item " << i << '\n';
|
|
});
|
|
imageView->setGridThreshold(20);
|
|
imageView->setPixelInfoThreshold(20);
|
|
imageView->setPixelInfoCallback(
|
|
[this, imageView](const Vector2i& index) -> pair<string, Color> {
|
|
auto& imageData = mImagesData[mCurrentImage].second;
|
|
auto& textureSize = imageView->imageSize();
|
|
string stringData;
|
|
uint16_t channelSum = 0;
|
|
for (int i = 0; i != 4; ++i) {
|
|
auto& channelData = imageData[4*index.y()*textureSize.x() + 4*index.x() + i];
|
|
channelSum += channelData;
|
|
stringData += (to_string(static_cast<int>(channelData)) + "\n");
|
|
}
|
|
float intensity = static_cast<float>(255 - (channelSum / 4)) / 255.0f;
|
|
float colorScale = intensity > 0.5f ? (intensity + 1) / 2 : intensity / 2;
|
|
Color textColor = Color(colorScale, 1.0f);
|
|
return { stringData, textColor };
|
|
});
|
|
|
|
new Label(window, "File dialog", "sans-bold");
|
|
tools = new Widget(window);
|
|
tools->setLayout(new BoxLayout(Orientation::Horizontal,
|
|
Alignment::Middle, 0, 6));
|
|
b = new Button(tools, "Open");
|
|
b->setCallback([&] {
|
|
cout << "File dialog result: " << file_dialog(
|
|
{ {"png", "Portable Network Graphics"}, {"txt", "Text file"} }, false) << endl;
|
|
});
|
|
b = new Button(tools, "Save");
|
|
b->setCallback([&] {
|
|
cout << "File dialog result: " << file_dialog(
|
|
{ {"png", "Portable Network Graphics"}, {"txt", "Text file"} }, true) << endl;
|
|
});
|
|
|
|
new Label(window, "Combo box", "sans-bold");
|
|
new ComboBox(window, { "Combo box item 1", "Combo box item 2", "Combo box item 3"});
|
|
new Label(window, "Check box", "sans-bold");
|
|
CheckBox *cb = new CheckBox(window, "Flag 1",
|
|
[](bool state) { cout << "Check box 1 state: " << state << endl; }
|
|
);
|
|
cb->setChecked(true);
|
|
cb = new CheckBox(window, "Flag 2",
|
|
[](bool state) { cout << "Check box 2 state: " << state << endl; }
|
|
);
|
|
new Label(window, "Progress bar", "sans-bold");
|
|
mProgress = new ProgressBar(window);
|
|
|
|
new Label(window, "Slider and text box", "sans-bold");
|
|
|
|
Widget *panel = new Widget(window);
|
|
panel->setLayout(new BoxLayout(Orientation::Horizontal,
|
|
Alignment::Middle, 0, 20));
|
|
|
|
Slider *slider = new Slider(panel);
|
|
slider->setValue(0.5f);
|
|
slider->setFixedWidth(80);
|
|
|
|
TextBox *textBox = new TextBox(panel);
|
|
textBox->setFixedSize(Vector2i(60, 25));
|
|
textBox->setValue("50");
|
|
textBox->setUnits("%");
|
|
slider->setCallback([textBox](float value) {
|
|
textBox->setValue(std::to_string((int) (value * 100)));
|
|
});
|
|
slider->setFinalCallback([&](float value) {
|
|
cout << "Final slider value: " << (int) (value * 100) << endl;
|
|
});
|
|
textBox->setFixedSize(Vector2i(60,25));
|
|
textBox->setFontSize(20);
|
|
textBox->setAlignment(TextBox::Alignment::Right);
|
|
|
|
window = new Window(this, "Misc. widgets");
|
|
window->setPosition(Vector2i(425,15));
|
|
window->setLayout(new GroupLayout());
|
|
|
|
TabWidget* tabWidget = window->add<TabWidget>();
|
|
|
|
Widget* layer = tabWidget->createTab("Color Wheel");
|
|
layer->setLayout(new GroupLayout());
|
|
|
|
// Use overloaded variadic add to fill the tab widget with Different tabs.
|
|
layer->add<Label>("Color wheel widget", "sans-bold");
|
|
layer->add<ColorWheel>();
|
|
|
|
layer = tabWidget->createTab("Function Graph");
|
|
layer->setLayout(new GroupLayout());
|
|
|
|
layer->add<Label>("Function graph widget", "sans-bold");
|
|
|
|
Graph *graph = layer->add<Graph>("Some Function");
|
|
|
|
graph->setHeader("E = 2.35e-3");
|
|
graph->setFooter("Iteration 89");
|
|
VectorXf &func = graph->values();
|
|
func.resize(100);
|
|
for (int i = 0; i < 100; ++i)
|
|
func[i] = 0.5f * (0.5f * std::sin(i / 10.f) +
|
|
0.5f * std::cos(i / 23.f) + 1);
|
|
|
|
// Dummy tab used to represent the last tab button.
|
|
tabWidget->createTab("+");
|
|
|
|
// A simple counter.
|
|
int counter = 1;
|
|
tabWidget->setCallback([tabWidget, this, counter] (int index) mutable {
|
|
if (index == (tabWidget->tabCount()-1)) {
|
|
// When the "+" tab has been clicked, simply add a new tab.
|
|
string tabName = "Dynamic " + to_string(counter);
|
|
Widget* layerDyn = tabWidget->createTab(index, tabName);
|
|
layerDyn->setLayout(new GroupLayout());
|
|
layerDyn->add<Label>("Function graph widget", "sans-bold");
|
|
Graph *graphDyn = layerDyn->add<Graph>("Dynamic function");
|
|
|
|
graphDyn->setHeader("E = 2.35e-3");
|
|
graphDyn->setFooter("Iteration " + to_string(index*counter));
|
|
VectorXf &funcDyn = graphDyn->values();
|
|
funcDyn.resize(100);
|
|
for (int i = 0; i < 100; ++i)
|
|
funcDyn[i] = 0.5f *
|
|
std::abs((0.5f * std::sin(i / 10.f + counter) +
|
|
0.5f * std::cos(i / 23.f + 1 + counter)));
|
|
++counter;
|
|
// We must invoke perform layout from the screen instance to keep everything in order.
|
|
// This is essential when creating tabs dynamically.
|
|
performLayout();
|
|
// Ensure that the newly added header is visible on screen
|
|
tabWidget->ensureTabVisible(index);
|
|
|
|
}
|
|
});
|
|
tabWidget->setActiveTab(0);
|
|
|
|
// A button to go back to the first tab and scroll the window.
|
|
panel = window->add<Widget>();
|
|
panel->add<Label>("Jump to tab: ");
|
|
panel->setLayout(new BoxLayout(Orientation::Horizontal,
|
|
Alignment::Middle, 0, 6));
|
|
|
|
auto ib = panel->add<IntBox<int>>();
|
|
ib->setEditable(true);
|
|
|
|
b = panel->add<Button>("", ENTYPO_ICON_FORWARD);
|
|
b->setFixedSize(Vector2i(22, 22));
|
|
ib->setFixedHeight(22);
|
|
b->setCallback([tabWidget, ib] {
|
|
int value = ib->value();
|
|
if (value >= 0 && value < tabWidget->tabCount()) {
|
|
tabWidget->setActiveTab(value);
|
|
tabWidget->ensureTabVisible(value);
|
|
}
|
|
});
|
|
|
|
window = new Window(this, "Grid of small widgets");
|
|
window->setPosition(Vector2i(425, 300));
|
|
GridLayout *layout =
|
|
new GridLayout(Orientation::Horizontal, 2,
|
|
Alignment::Middle, 15, 5);
|
|
layout->setColAlignment(
|
|
{ Alignment::Maximum, Alignment::Fill });
|
|
layout->setSpacing(0, 10);
|
|
window->setLayout(layout);
|
|
|
|
/* FP widget */ {
|
|
new Label(window, "Floating point :", "sans-bold");
|
|
textBox = new TextBox(window);
|
|
textBox->setEditable(true);
|
|
textBox->setFixedSize(Vector2i(100, 20));
|
|
textBox->setValue("50");
|
|
textBox->setUnits("GiB");
|
|
textBox->setDefaultValue("0.0");
|
|
textBox->setFontSize(16);
|
|
textBox->setFormat("[-]?[0-9]*\\.?[0-9]+");
|
|
}
|
|
|
|
/* Positive integer widget */ {
|
|
new Label(window, "Positive integer :", "sans-bold");
|
|
auto intBox = new IntBox<int>(window);
|
|
intBox->setEditable(true);
|
|
intBox->setFixedSize(Vector2i(100, 20));
|
|
intBox->setValue(50);
|
|
intBox->setUnits("Mhz");
|
|
intBox->setDefaultValue("0");
|
|
intBox->setFontSize(16);
|
|
intBox->setFormat("[1-9][0-9]*");
|
|
intBox->setSpinnable(true);
|
|
intBox->setMinValue(1);
|
|
intBox->setValueIncrement(2);
|
|
}
|
|
|
|
/* Checkbox widget */ {
|
|
new Label(window, "Checkbox :", "sans-bold");
|
|
|
|
cb = new CheckBox(window, "Check me");
|
|
cb->setFontSize(16);
|
|
cb->setChecked(true);
|
|
}
|
|
|
|
new Label(window, "Combo box :", "sans-bold");
|
|
ComboBox *cobo =
|
|
new ComboBox(window, { "Item 1", "Item 2", "Item 3" });
|
|
cobo->setFontSize(16);
|
|
cobo->setFixedSize(Vector2i(100,20));
|
|
|
|
new Label(window, "Color picker :", "sans-bold");
|
|
auto cp = new ColorPicker(window, {255, 120, 0, 255});
|
|
cp->setFixedSize({100, 20});
|
|
cp->setFinalCallback([](const Color &c) {
|
|
std::cout << "ColorPicker Final Callback: ["
|
|
<< c.r() << ", "
|
|
<< c.g() << ", "
|
|
<< c.b() << ", "
|
|
<< c.w() << "]" << std::endl;
|
|
});
|
|
// setup a fast callback for the color picker widget on a new window
|
|
// for demonstrative purposes
|
|
window = new Window(this, "Color Picker Fast Callback");
|
|
layout =
|
|
new GridLayout(Orientation::Horizontal, 2,
|
|
Alignment::Middle, 15, 5);
|
|
layout->setColAlignment(
|
|
{ Alignment::Maximum, Alignment::Fill });
|
|
layout->setSpacing(0, 10);
|
|
window->setLayout(layout);
|
|
window->setPosition(Vector2i(425, 500));
|
|
new Label(window, "Combined: ");
|
|
b = new Button(window, "ColorWheel", ENTYPO_ICON_500PX);
|
|
new Label(window, "Red: ");
|
|
auto redIntBox = new IntBox<int>(window);
|
|
redIntBox->setEditable(false);
|
|
new Label(window, "Green: ");
|
|
auto greenIntBox = new IntBox<int>(window);
|
|
greenIntBox->setEditable(false);
|
|
new Label(window, "Blue: ");
|
|
auto blueIntBox = new IntBox<int>(window);
|
|
blueIntBox->setEditable(false);
|
|
new Label(window, "Alpha: ");
|
|
auto alphaIntBox = new IntBox<int>(window);
|
|
cp->setCallback([b,redIntBox,blueIntBox,greenIntBox,alphaIntBox](const Color &c) {
|
|
b->setBackgroundColor(c);
|
|
b->setTextColor(c.contrastingColor());
|
|
int red = (int) (c.r() * 255.0f);
|
|
redIntBox->setValue(red);
|
|
int green = (int) (c.g() * 255.0f);
|
|
greenIntBox->setValue(green);
|
|
int blue = (int) (c.b() * 255.0f);
|
|
blueIntBox->setValue(blue);
|
|
int alpha = (int) (c.w() * 255.0f);
|
|
alphaIntBox->setValue(alpha);
|
|
|
|
});
|
|
|
|
performLayout();
|
|
|
|
/* All NanoGUI widgets are initialized at this point. Now
|
|
create an OpenGL shader to draw the main window contents.
|
|
|
|
NanoGUI comes with a simple Eigen-based wrapper around OpenGL 3,
|
|
which eliminates most of the tedious and error-prone shader and
|
|
buffer object management.
|
|
*/
|
|
|
|
mShader.init(
|
|
/* An identifying name */
|
|
"a_simple_shader",
|
|
|
|
/* Vertex shader */
|
|
"#version 330\n"
|
|
"uniform mat4 modelViewProj;\n"
|
|
"in vec3 position;\n"
|
|
"void main() {\n"
|
|
" gl_Position = modelViewProj * vec4(position, 1.0);\n"
|
|
"}",
|
|
|
|
/* Fragment shader */
|
|
"#version 330\n"
|
|
"out vec4 color;\n"
|
|
"uniform float intensity;\n"
|
|
"void main() {\n"
|
|
" color = vec4(vec3(intensity), 1.0);\n"
|
|
"}"
|
|
);
|
|
|
|
MatrixXu indices(3, 2); /* Draw 2 triangles */
|
|
indices.col(0) << 0, 1, 2;
|
|
indices.col(1) << 2, 3, 0;
|
|
|
|
MatrixXf positions(3, 4);
|
|
positions.col(0) << -1, -1, 0;
|
|
positions.col(1) << 1, -1, 0;
|
|
positions.col(2) << 1, 1, 0;
|
|
positions.col(3) << -1, 1, 0;
|
|
|
|
mShader.bind();
|
|
mShader.uploadIndices(indices);
|
|
mShader.uploadAttrib("position", positions);
|
|
mShader.setUniform("intensity", 0.5f);
|
|
}
|
|
|
|
~ExampleApplication() {
|
|
mShader.free();
|
|
}
|
|
|
|
virtual bool keyboardEvent(int key, int scancode, int action, int modifiers) {
|
|
if (Screen::keyboardEvent(key, scancode, action, modifiers))
|
|
return true;
|
|
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
|
|
setVisible(false);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
virtual void draw(NVGcontext *ctx) {
|
|
/* Animate the scrollbar */
|
|
mProgress->setValue(std::fmod((float) glfwGetTime() / 10, 1.0f));
|
|
|
|
/* Draw the user interface */
|
|
Screen::draw(ctx);
|
|
}
|
|
|
|
virtual void drawContents() {
|
|
using namespace nanogui;
|
|
|
|
/* Draw the window contents using OpenGL */
|
|
mShader.bind();
|
|
|
|
Matrix4f mvp;
|
|
mvp.setIdentity();
|
|
mvp.topLeftCorner<3,3>() = Matrix3f(Eigen::AngleAxisf((float) glfwGetTime(), Vector3f::UnitZ())) * 0.25f;
|
|
|
|
mvp.row(0) *= (float) mSize.y() / (float) mSize.x();
|
|
|
|
mShader.setUniform("modelViewProj", mvp);
|
|
|
|
/* Draw 2 triangles starting at index 0 */
|
|
mShader.drawIndexed(GL_TRIANGLES, 0, 2);
|
|
}
|
|
private:
|
|
nanogui::ProgressBar *mProgress;
|
|
nanogui::GLShader mShader;
|
|
|
|
using imagesDataType = vector<pair<GLTexture, GLTexture::handleType>>;
|
|
imagesDataType mImagesData;
|
|
int mCurrentImage;
|
|
};
|
|
|
|
int main(int /* argc */, char ** /* argv */) {
|
|
try {
|
|
nanogui::init();
|
|
|
|
/* scoped variables */ {
|
|
nanogui::ref<ExampleApplication> app = new ExampleApplication();
|
|
app->drawAll();
|
|
app->setVisible(true);
|
|
nanogui::mainloop();
|
|
}
|
|
|
|
nanogui::shutdown();
|
|
} catch (const std::runtime_error &e) {
|
|
std::string error_msg = std::string("Caught a fatal error: ") + std::string(e.what());
|
|
#if defined(_WIN32)
|
|
MessageBoxA(nullptr, error_msg.c_str(), NULL, MB_ICONERROR | MB_OK);
|
|
#else
|
|
std::cerr << error_msg << endl;
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|