1 | /* |
2 | src/example1.cpp -- C++ version of an example application that shows |
3 | how to use the various widget classes. For a Python implementation, see |
4 | '../python/example1.py'. |
5 | |
6 | NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>. |
7 | The widget drawing code is based on the NanoVG demo application |
8 | by Mikko Mononen. |
9 | |
10 | All rights reserved. Use of this source code is governed by a |
11 | BSD-style license that can be found in the LICENSE.txt file. |
12 | */ |
13 | |
14 | #include <nanogui/opengl.h> |
15 | #include <nanogui/glutil.h> |
16 | #include <nanogui/screen.h> |
17 | #include <nanogui/window.h> |
18 | #include <nanogui/layout.h> |
19 | #include <nanogui/label.h> |
20 | #include <nanogui/checkbox.h> |
21 | #include <nanogui/button.h> |
22 | #include <nanogui/toolbutton.h> |
23 | #include <nanogui/popupbutton.h> |
24 | #include <nanogui/combobox.h> |
25 | #include <nanogui/progressbar.h> |
26 | #include <nanogui/entypo.h> |
27 | #include <nanogui/messagedialog.h> |
28 | #include <nanogui/textbox.h> |
29 | #include <nanogui/slider.h> |
30 | #include <nanogui/imagepanel.h> |
31 | #include <nanogui/imageview.h> |
32 | #include <nanogui/vscrollpanel.h> |
33 | #include <nanogui/colorwheel.h> |
34 | #include <nanogui/colorpicker.h> |
35 | #include <nanogui/graph.h> |
36 | #include <nanogui/tabwidget.h> |
37 | #include <iostream> |
38 | #include <string> |
39 | |
40 | // Includes for the GLTexture class. |
41 | #include <cstdint> |
42 | #include <memory> |
43 | #include <utility> |
44 | |
45 | #if defined(__GNUC__) |
46 | # pragma GCC diagnostic ignored "-Wmissing-field-initializers" |
47 | #endif |
48 | #if defined(_WIN32) |
49 | # pragma warning(push) |
50 | # pragma warning(disable: 4457 4456 4005 4312) |
51 | #endif |
52 | |
53 | #define STB_IMAGE_STATIC |
54 | #define STB_IMAGE_IMPLEMENTATION |
55 | #include <stb_image.h> |
56 | |
57 | #if defined(_WIN32) |
58 | # pragma warning(pop) |
59 | #endif |
60 | #if defined(_WIN32) |
61 | # if defined(APIENTRY) |
62 | # undef APIENTRY |
63 | # endif |
64 | # include <windows.h> |
65 | #endif |
66 | |
67 | using std::cout; |
68 | using std::cerr; |
69 | using std::endl; |
70 | using std::string; |
71 | using std::vector; |
72 | using std::pair; |
73 | using std::to_string; |
74 | |
75 | class GLTexture { |
76 | public: |
77 | using handleType = std::unique_ptr<uint8_t[], void(*)(void*)>; |
78 | GLTexture() = default; |
79 | GLTexture(const std::string& textureName) |
80 | : mTextureName(textureName), mTextureId(0) {} |
81 | |
82 | GLTexture(const std::string& textureName, GLint textureId) |
83 | : mTextureName(textureName), mTextureId(textureId) {} |
84 | |
85 | GLTexture(const GLTexture& other) = delete; |
86 | GLTexture(GLTexture&& other) noexcept |
87 | : mTextureName(std::move(other.mTextureName)), |
88 | mTextureId(other.mTextureId) { |
89 | other.mTextureId = 0; |
90 | } |
91 | GLTexture& operator=(const GLTexture& other) = delete; |
92 | GLTexture& operator=(GLTexture&& other) noexcept { |
93 | mTextureName = std::move(other.mTextureName); |
94 | std::swap(mTextureId, other.mTextureId); |
95 | return *this; |
96 | } |
97 | ~GLTexture() noexcept { |
98 | if (mTextureId) |
99 | glDeleteTextures(1, &mTextureId); |
100 | } |
101 | |
102 | GLuint texture() const { return mTextureId; } |
103 | const std::string& textureName() const { return mTextureName; } |
104 | |
105 | /** |
106 | * Load a file in memory and create an OpenGL texture. |
107 | * Returns a handle type (an std::unique_ptr) to the loaded pixels. |
108 | */ |
109 | handleType load(const std::string& fileName) { |
110 | if (mTextureId) { |
111 | glDeleteTextures(1, &mTextureId); |
112 | mTextureId = 0; |
113 | } |
114 | int force_channels = 0; |
115 | int w, h, n; |
116 | handleType textureData(stbi_load(fileName.c_str(), &w, &h, &n, force_channels), stbi_image_free); |
117 | if (!textureData) |
118 | throw std::invalid_argument("Could not load texture data from file " + fileName); |
119 | glGenTextures(1, &mTextureId); |
120 | glBindTexture(GL_TEXTURE_2D, mTextureId); |
121 | GLint internalFormat; |
122 | GLint format; |
123 | switch (n) { |
124 | case 1: internalFormat = GL_R8; format = GL_RED; break; |
125 | case 2: internalFormat = GL_RG8; format = GL_RG; break; |
126 | case 3: internalFormat = GL_RGB8; format = GL_RGB; break; |
127 | case 4: internalFormat = GL_RGBA8; format = GL_RGBA; break; |
128 | default: internalFormat = 0; format = 0; break; |
129 | } |
130 | glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, format, GL_UNSIGNED_BYTE, textureData.get()); |
131 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
132 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
133 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
134 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
135 | return textureData; |
136 | } |
137 | |
138 | private: |
139 | std::string mTextureName; |
140 | GLuint mTextureId; |
141 | }; |
142 | |
143 | class ExampleApplication : public nanogui::Screen { |
144 | public: |
145 | ExampleApplication() : nanogui::Screen(Eigen::Vector2i(1024, 768), "NanoGUI Test" ) { |
146 | using namespace nanogui; |
147 | |
148 | Window *window = new Window(this, "Button demo" ); |
149 | window->setPosition(Vector2i(15, 15)); |
150 | window->setLayout(new GroupLayout()); |
151 | |
152 | /* No need to store a pointer, the data structure will be automatically |
153 | freed when the parent window is deleted */ |
154 | new Label(window, "Push buttons" , "sans-bold" ); |
155 | |
156 | Button *b = new Button(window, "Plain button" ); |
157 | b->setCallback([] { cout << "pushed!" << endl; }); |
158 | b->setTooltip("short tooltip" ); |
159 | |
160 | /* Alternative construction notation using variadic template */ |
161 | b = window->add<Button>("Styled" , ENTYPO_ICON_ROCKET); |
162 | b->setBackgroundColor(Color(0, 0, 255, 25)); |
163 | b->setCallback([] { cout << "pushed!" << endl; }); |
164 | b->setTooltip("This button has a fairly long tooltip. It is so long, in " |
165 | "fact, that the shown text will span several lines." ); |
166 | |
167 | new Label(window, "Toggle buttons" , "sans-bold" ); |
168 | b = new Button(window, "Toggle me" ); |
169 | b->setFlags(Button::ToggleButton); |
170 | b->setChangeCallback([](bool state) { cout << "Toggle button state: " << state << endl; }); |
171 | |
172 | new Label(window, "Radio buttons" , "sans-bold" ); |
173 | b = new Button(window, "Radio button 1" ); |
174 | b->setFlags(Button::RadioButton); |
175 | b = new Button(window, "Radio button 2" ); |
176 | b->setFlags(Button::RadioButton); |
177 | |
178 | new Label(window, "A tool palette" , "sans-bold" ); |
179 | Widget *tools = new Widget(window); |
180 | tools->setLayout(new BoxLayout(Orientation::Horizontal, |
181 | Alignment::Middle, 0, 6)); |
182 | |
183 | b = new ToolButton(tools, ENTYPO_ICON_CLOUD); |
184 | b = new ToolButton(tools, ENTYPO_ICON_CONTROLLER_FAST_FORWARD); |
185 | b = new ToolButton(tools, ENTYPO_ICON_COMPASS); |
186 | b = new ToolButton(tools, ENTYPO_ICON_INSTALL); |
187 | |
188 | new Label(window, "Popup buttons" , "sans-bold" ); |
189 | PopupButton * = new PopupButton(window, "Popup" , ENTYPO_ICON_EXPORT); |
190 | Popup * = popupBtn->popup(); |
191 | popup->setLayout(new GroupLayout()); |
192 | new Label(popup, "Arbitrary widgets can be placed here" ); |
193 | new CheckBox(popup, "A check box" ); |
194 | // popup right |
195 | popupBtn = new PopupButton(popup, "Recursive popup" , ENTYPO_ICON_FLASH); |
196 | Popup * = popupBtn->popup(); |
197 | popupRight->setLayout(new GroupLayout()); |
198 | new CheckBox(popupRight, "Another check box" ); |
199 | // popup left |
200 | popupBtn = new PopupButton(popup, "Recursive popup" , ENTYPO_ICON_FLASH); |
201 | popupBtn->setSide(Popup::Side::Left); |
202 | Popup * = popupBtn->popup(); |
203 | popupLeft->setLayout(new GroupLayout()); |
204 | new CheckBox(popupLeft, "Another check box" ); |
205 | |
206 | window = new Window(this, "Basic widgets" ); |
207 | window->setPosition(Vector2i(200, 15)); |
208 | window->setLayout(new GroupLayout()); |
209 | |
210 | new Label(window, "Message dialog" , "sans-bold" ); |
211 | tools = new Widget(window); |
212 | tools->setLayout(new BoxLayout(Orientation::Horizontal, |
213 | Alignment::Middle, 0, 6)); |
214 | b = new Button(tools, "Info" ); |
215 | b->setCallback([&] { |
216 | auto dlg = new MessageDialog(this, MessageDialog::Type::Information, "Title" , "This is an information message" ); |
217 | dlg->setCallback([](int result) { cout << "Dialog result: " << result << endl; }); |
218 | }); |
219 | b = new Button(tools, "Warn" ); |
220 | b->setCallback([&] { |
221 | auto dlg = new MessageDialog(this, MessageDialog::Type::Warning, "Title" , "This is a warning message" ); |
222 | dlg->setCallback([](int result) { cout << "Dialog result: " << result << endl; }); |
223 | }); |
224 | b = new Button(tools, "Ask" ); |
225 | b->setCallback([&] { |
226 | auto dlg = new MessageDialog(this, MessageDialog::Type::Warning, "Title" , "This is a question message" , "Yes" , "No" , true); |
227 | dlg->setCallback([](int result) { cout << "Dialog result: " << result << endl; }); |
228 | }); |
229 | |
230 | vector<pair<int, string>> |
231 | icons = loadImageDirectory(mNVGContext, "icons" ); |
232 | #if defined(_WIN32) |
233 | string resourcesFolderPath("../resources/" ); |
234 | #else |
235 | string resourcesFolderPath("./" ); |
236 | #endif |
237 | |
238 | new Label(window, "Image panel & scroll panel" , "sans-bold" ); |
239 | PopupButton *imagePanelBtn = new PopupButton(window, "Image Panel" ); |
240 | imagePanelBtn->setIcon(ENTYPO_ICON_FOLDER); |
241 | popup = imagePanelBtn->popup(); |
242 | VScrollPanel *vscroll = new VScrollPanel(popup); |
243 | ImagePanel *imgPanel = new ImagePanel(vscroll); |
244 | imgPanel->setImages(icons); |
245 | popup->setFixedSize(Vector2i(245, 150)); |
246 | |
247 | auto imageWindow = new Window(this, "Selected image" ); |
248 | imageWindow->setPosition(Vector2i(710, 15)); |
249 | imageWindow->setLayout(new GroupLayout()); |
250 | |
251 | // Load all of the images by creating a GLTexture object and saving the pixel data. |
252 | for (auto& icon : icons) { |
253 | GLTexture texture(icon.second); |
254 | auto data = texture.load(resourcesFolderPath + icon.second + ".png" ); |
255 | mImagesData.emplace_back(std::move(texture), std::move(data)); |
256 | } |
257 | |
258 | // Set the first texture |
259 | auto imageView = new ImageView(imageWindow, mImagesData[0].first.texture()); |
260 | mCurrentImage = 0; |
261 | // Change the active textures. |
262 | imgPanel->setCallback([this, imageView](int i) { |
263 | imageView->bindImage(mImagesData[i].first.texture()); |
264 | mCurrentImage = i; |
265 | cout << "Selected item " << i << '\n'; |
266 | }); |
267 | imageView->setGridThreshold(20); |
268 | imageView->setPixelInfoThreshold(20); |
269 | imageView->setPixelInfoCallback( |
270 | [this, imageView](const Vector2i& index) -> pair<string, Color> { |
271 | auto& imageData = mImagesData[mCurrentImage].second; |
272 | auto& textureSize = imageView->imageSize(); |
273 | string stringData; |
274 | uint16_t channelSum = 0; |
275 | for (int i = 0; i != 4; ++i) { |
276 | auto& channelData = imageData[4*index.y()*textureSize.x() + 4*index.x() + i]; |
277 | channelSum += channelData; |
278 | stringData += (to_string(static_cast<int>(channelData)) + "\n" ); |
279 | } |
280 | float intensity = static_cast<float>(255 - (channelSum / 4)) / 255.0f; |
281 | float colorScale = intensity > 0.5f ? (intensity + 1) / 2 : intensity / 2; |
282 | Color textColor = Color(colorScale, 1.0f); |
283 | return { stringData, textColor }; |
284 | }); |
285 | |
286 | new Label(window, "File dialog" , "sans-bold" ); |
287 | tools = new Widget(window); |
288 | tools->setLayout(new BoxLayout(Orientation::Horizontal, |
289 | Alignment::Middle, 0, 6)); |
290 | b = new Button(tools, "Open" ); |
291 | b->setCallback([&] { |
292 | cout << "File dialog result: " << file_dialog( |
293 | { {"png" , "Portable Network Graphics" }, {"txt" , "Text file" } }, false) << endl; |
294 | }); |
295 | b = new Button(tools, "Save" ); |
296 | b->setCallback([&] { |
297 | cout << "File dialog result: " << file_dialog( |
298 | { {"png" , "Portable Network Graphics" }, {"txt" , "Text file" } }, true) << endl; |
299 | }); |
300 | |
301 | new Label(window, "Combo box" , "sans-bold" ); |
302 | new ComboBox(window, { "Combo box item 1" , "Combo box item 2" , "Combo box item 3" }); |
303 | new Label(window, "Check box" , "sans-bold" ); |
304 | CheckBox *cb = new CheckBox(window, "Flag 1" , |
305 | [](bool state) { cout << "Check box 1 state: " << state << endl; } |
306 | ); |
307 | cb->setChecked(true); |
308 | cb = new CheckBox(window, "Flag 2" , |
309 | [](bool state) { cout << "Check box 2 state: " << state << endl; } |
310 | ); |
311 | new Label(window, "Progress bar" , "sans-bold" ); |
312 | mProgress = new ProgressBar(window); |
313 | |
314 | new Label(window, "Slider and text box" , "sans-bold" ); |
315 | |
316 | Widget *panel = new Widget(window); |
317 | panel->setLayout(new BoxLayout(Orientation::Horizontal, |
318 | Alignment::Middle, 0, 20)); |
319 | |
320 | Slider *slider = new Slider(panel); |
321 | slider->setValue(0.5f); |
322 | slider->setFixedWidth(80); |
323 | |
324 | TextBox *textBox = new TextBox(panel); |
325 | textBox->setFixedSize(Vector2i(60, 25)); |
326 | textBox->setValue("50" ); |
327 | textBox->setUnits("%" ); |
328 | slider->setCallback([textBox](float value) { |
329 | textBox->setValue(std::to_string((int) (value * 100))); |
330 | }); |
331 | slider->setFinalCallback([&](float value) { |
332 | cout << "Final slider value: " << (int) (value * 100) << endl; |
333 | }); |
334 | textBox->setFixedSize(Vector2i(60,25)); |
335 | textBox->setFontSize(20); |
336 | textBox->setAlignment(TextBox::Alignment::Right); |
337 | |
338 | window = new Window(this, "Misc. widgets" ); |
339 | window->setPosition(Vector2i(425,15)); |
340 | window->setLayout(new GroupLayout()); |
341 | |
342 | TabWidget* tabWidget = window->add<TabWidget>(); |
343 | |
344 | Widget* layer = tabWidget->createTab("Color Wheel" ); |
345 | layer->setLayout(new GroupLayout()); |
346 | |
347 | // Use overloaded variadic add to fill the tab widget with Different tabs. |
348 | layer->add<Label>("Color wheel widget" , "sans-bold" ); |
349 | layer->add<ColorWheel>(); |
350 | |
351 | layer = tabWidget->createTab("Function Graph" ); |
352 | layer->setLayout(new GroupLayout()); |
353 | |
354 | layer->add<Label>("Function graph widget" , "sans-bold" ); |
355 | |
356 | Graph *graph = layer->add<Graph>("Some Function" ); |
357 | |
358 | graph->setHeader("E = 2.35e-3" ); |
359 | graph->setFooter("Iteration 89" ); |
360 | VectorXf &func = graph->values(); |
361 | func.resize(100); |
362 | for (int i = 0; i < 100; ++i) |
363 | func[i] = 0.5f * (0.5f * std::sin(i / 10.f) + |
364 | 0.5f * std::cos(i / 23.f) + 1); |
365 | |
366 | // Dummy tab used to represent the last tab button. |
367 | tabWidget->createTab("+" ); |
368 | |
369 | // A simple counter. |
370 | int counter = 1; |
371 | tabWidget->setCallback([tabWidget, this, counter] (int index) mutable { |
372 | if (index == (tabWidget->tabCount()-1)) { |
373 | // When the "+" tab has been clicked, simply add a new tab. |
374 | string tabName = "Dynamic " + to_string(counter); |
375 | Widget* layerDyn = tabWidget->createTab(index, tabName); |
376 | layerDyn->setLayout(new GroupLayout()); |
377 | layerDyn->add<Label>("Function graph widget" , "sans-bold" ); |
378 | Graph *graphDyn = layerDyn->add<Graph>("Dynamic function" ); |
379 | |
380 | graphDyn->setHeader("E = 2.35e-3" ); |
381 | graphDyn->setFooter("Iteration " + to_string(index*counter)); |
382 | VectorXf &funcDyn = graphDyn->values(); |
383 | funcDyn.resize(100); |
384 | for (int i = 0; i < 100; ++i) |
385 | funcDyn[i] = 0.5f * |
386 | std::abs((0.5f * std::sin(i / 10.f + counter) + |
387 | 0.5f * std::cos(i / 23.f + 1 + counter))); |
388 | ++counter; |
389 | // We must invoke perform layout from the screen instance to keep everything in order. |
390 | // This is essential when creating tabs dynamically. |
391 | performLayout(); |
392 | // Ensure that the newly added header is visible on screen |
393 | tabWidget->ensureTabVisible(index); |
394 | |
395 | } |
396 | }); |
397 | tabWidget->setActiveTab(0); |
398 | |
399 | // A button to go back to the first tab and scroll the window. |
400 | panel = window->add<Widget>(); |
401 | panel->add<Label>("Jump to tab: " ); |
402 | panel->setLayout(new BoxLayout(Orientation::Horizontal, |
403 | Alignment::Middle, 0, 6)); |
404 | |
405 | auto ib = panel->add<IntBox<int>>(); |
406 | ib->setEditable(true); |
407 | |
408 | b = panel->add<Button>("" , ENTYPO_ICON_FORWARD); |
409 | b->setFixedSize(Vector2i(22, 22)); |
410 | ib->setFixedHeight(22); |
411 | b->setCallback([tabWidget, ib] { |
412 | int value = ib->value(); |
413 | if (value >= 0 && value < tabWidget->tabCount()) { |
414 | tabWidget->setActiveTab(value); |
415 | tabWidget->ensureTabVisible(value); |
416 | } |
417 | }); |
418 | |
419 | window = new Window(this, "Grid of small widgets" ); |
420 | window->setPosition(Vector2i(425, 300)); |
421 | GridLayout *layout = |
422 | new GridLayout(Orientation::Horizontal, 2, |
423 | Alignment::Middle, 15, 5); |
424 | layout->setColAlignment( |
425 | { Alignment::Maximum, Alignment::Fill }); |
426 | layout->setSpacing(0, 10); |
427 | window->setLayout(layout); |
428 | |
429 | /* FP widget */ { |
430 | new Label(window, "Floating point :" , "sans-bold" ); |
431 | textBox = new TextBox(window); |
432 | textBox->setEditable(true); |
433 | textBox->setFixedSize(Vector2i(100, 20)); |
434 | textBox->setValue("50" ); |
435 | textBox->setUnits("GiB" ); |
436 | textBox->setDefaultValue("0.0" ); |
437 | textBox->setFontSize(16); |
438 | textBox->setFormat("[-]?[0-9]*\\.?[0-9]+" ); |
439 | } |
440 | |
441 | /* Positive integer widget */ { |
442 | new Label(window, "Positive integer :" , "sans-bold" ); |
443 | auto intBox = new IntBox<int>(window); |
444 | intBox->setEditable(true); |
445 | intBox->setFixedSize(Vector2i(100, 20)); |
446 | intBox->setValue(50); |
447 | intBox->setUnits("Mhz" ); |
448 | intBox->setDefaultValue("0" ); |
449 | intBox->setFontSize(16); |
450 | intBox->setFormat("[1-9][0-9]*" ); |
451 | intBox->setSpinnable(true); |
452 | intBox->setMinValue(1); |
453 | intBox->setValueIncrement(2); |
454 | } |
455 | |
456 | /* Checkbox widget */ { |
457 | new Label(window, "Checkbox :" , "sans-bold" ); |
458 | |
459 | cb = new CheckBox(window, "Check me" ); |
460 | cb->setFontSize(16); |
461 | cb->setChecked(true); |
462 | } |
463 | |
464 | new Label(window, "Combo box :" , "sans-bold" ); |
465 | ComboBox *cobo = |
466 | new ComboBox(window, { "Item 1" , "Item 2" , "Item 3" }); |
467 | cobo->setFontSize(16); |
468 | cobo->setFixedSize(Vector2i(100,20)); |
469 | |
470 | new Label(window, "Color picker :" , "sans-bold" ); |
471 | auto cp = new ColorPicker(window, {255, 120, 0, 255}); |
472 | cp->setFixedSize({100, 20}); |
473 | cp->setFinalCallback([](const Color &c) { |
474 | std::cout << "ColorPicker Final Callback: [" |
475 | << c.r() << ", " |
476 | << c.g() << ", " |
477 | << c.b() << ", " |
478 | << c.w() << "]" << std::endl; |
479 | }); |
480 | // setup a fast callback for the color picker widget on a new window |
481 | // for demonstrative purposes |
482 | window = new Window(this, "Color Picker Fast Callback" ); |
483 | layout = |
484 | new GridLayout(Orientation::Horizontal, 2, |
485 | Alignment::Middle, 15, 5); |
486 | layout->setColAlignment( |
487 | { Alignment::Maximum, Alignment::Fill }); |
488 | layout->setSpacing(0, 10); |
489 | window->setLayout(layout); |
490 | window->setPosition(Vector2i(425, 500)); |
491 | new Label(window, "Combined: " ); |
492 | b = new Button(window, "ColorWheel" , ENTYPO_ICON_500PX); |
493 | new Label(window, "Red: " ); |
494 | auto redIntBox = new IntBox<int>(window); |
495 | redIntBox->setEditable(false); |
496 | new Label(window, "Green: " ); |
497 | auto greenIntBox = new IntBox<int>(window); |
498 | greenIntBox->setEditable(false); |
499 | new Label(window, "Blue: " ); |
500 | auto blueIntBox = new IntBox<int>(window); |
501 | blueIntBox->setEditable(false); |
502 | new Label(window, "Alpha: " ); |
503 | auto alphaIntBox = new IntBox<int>(window); |
504 | cp->setCallback([b,redIntBox,blueIntBox,greenIntBox,alphaIntBox](const Color &c) { |
505 | b->setBackgroundColor(c); |
506 | b->setTextColor(c.contrastingColor()); |
507 | int red = (int) (c.r() * 255.0f); |
508 | redIntBox->setValue(red); |
509 | int green = (int) (c.g() * 255.0f); |
510 | greenIntBox->setValue(green); |
511 | int blue = (int) (c.b() * 255.0f); |
512 | blueIntBox->setValue(blue); |
513 | int alpha = (int) (c.w() * 255.0f); |
514 | alphaIntBox->setValue(alpha); |
515 | |
516 | }); |
517 | |
518 | performLayout(); |
519 | |
520 | /* All NanoGUI widgets are initialized at this point. Now |
521 | create an OpenGL shader to draw the main window contents. |
522 | |
523 | NanoGUI comes with a simple Eigen-based wrapper around OpenGL 3, |
524 | which eliminates most of the tedious and error-prone shader and |
525 | buffer object management. |
526 | */ |
527 | |
528 | mShader.init( |
529 | /* An identifying name */ |
530 | "a_simple_shader" , |
531 | |
532 | /* Vertex shader */ |
533 | "#version 330\n" |
534 | "uniform mat4 modelViewProj;\n" |
535 | "in vec3 position;\n" |
536 | "void main() {\n" |
537 | " gl_Position = modelViewProj * vec4(position, 1.0);\n" |
538 | "}" , |
539 | |
540 | /* Fragment shader */ |
541 | "#version 330\n" |
542 | "out vec4 color;\n" |
543 | "uniform float intensity;\n" |
544 | "void main() {\n" |
545 | " color = vec4(vec3(intensity), 1.0);\n" |
546 | "}" |
547 | ); |
548 | |
549 | MatrixXu indices(3, 2); /* Draw 2 triangles */ |
550 | indices.col(0) << 0, 1, 2; |
551 | indices.col(1) << 2, 3, 0; |
552 | |
553 | MatrixXf positions(3, 4); |
554 | positions.col(0) << -1, -1, 0; |
555 | positions.col(1) << 1, -1, 0; |
556 | positions.col(2) << 1, 1, 0; |
557 | positions.col(3) << -1, 1, 0; |
558 | |
559 | mShader.bind(); |
560 | mShader.uploadIndices(indices); |
561 | mShader.uploadAttrib("position" , positions); |
562 | mShader.setUniform("intensity" , 0.5f); |
563 | } |
564 | |
565 | ~ExampleApplication() { |
566 | mShader.free(); |
567 | } |
568 | |
569 | virtual bool keyboardEvent(int key, int scancode, int action, int modifiers) { |
570 | if (Screen::keyboardEvent(key, scancode, action, modifiers)) |
571 | return true; |
572 | if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { |
573 | setVisible(false); |
574 | return true; |
575 | } |
576 | return false; |
577 | } |
578 | |
579 | virtual void draw(NVGcontext *ctx) { |
580 | /* Animate the scrollbar */ |
581 | mProgress->setValue(std::fmod((float) glfwGetTime() / 10, 1.0f)); |
582 | |
583 | /* Draw the user interface */ |
584 | Screen::draw(ctx); |
585 | } |
586 | |
587 | virtual void drawContents() { |
588 | using namespace nanogui; |
589 | |
590 | /* Draw the window contents using OpenGL */ |
591 | mShader.bind(); |
592 | |
593 | Matrix4f mvp; |
594 | mvp.setIdentity(); |
595 | mvp.topLeftCorner<3,3>() = Matrix3f(Eigen::AngleAxisf((float) glfwGetTime(), Vector3f::UnitZ())) * 0.25f; |
596 | |
597 | mvp.row(0) *= (float) mSize.y() / (float) mSize.x(); |
598 | |
599 | mShader.setUniform("modelViewProj" , mvp); |
600 | |
601 | /* Draw 2 triangles starting at index 0 */ |
602 | mShader.drawIndexed(GL_TRIANGLES, 0, 2); |
603 | } |
604 | private: |
605 | nanogui::ProgressBar *mProgress; |
606 | nanogui::GLShader mShader; |
607 | |
608 | using imagesDataType = vector<pair<GLTexture, GLTexture::handleType>>; |
609 | imagesDataType mImagesData; |
610 | int mCurrentImage; |
611 | }; |
612 | |
613 | int main(int /* argc */, char ** /* argv */) { |
614 | try { |
615 | nanogui::init(); |
616 | |
617 | /* scoped variables */ { |
618 | nanogui::ref<ExampleApplication> app = new ExampleApplication(); |
619 | app->drawAll(); |
620 | app->setVisible(true); |
621 | nanogui::mainloop(); |
622 | } |
623 | |
624 | nanogui::shutdown(); |
625 | } catch (const std::runtime_error &e) { |
626 | std::string error_msg = std::string("Caught a fatal error: " ) + std::string(e.what()); |
627 | #if defined(_WIN32) |
628 | MessageBoxA(nullptr, error_msg.c_str(), NULL, MB_ICONERROR | MB_OK); |
629 | #else |
630 | std::cerr << error_msg << endl; |
631 | #endif |
632 | return -1; |
633 | } |
634 | |
635 | return 0; |
636 | } |
637 | |