// // Copyright 2013 Pixar // // Licensed under the Apache License, Version 2.0 (the "Apache License") // with the following modification; you may not use this file except in // compliance with the Apache License and the following modification to it: // Section 6. Trademarks. is deleted and replaced with: // // 6. Trademarks. This License does not grant permission to use the trade // names, trademarks, service marks, or product names of the Licensor // and its affiliates, except as required to comply with Section 4(c) of // the License and to reproduce the content of the NOTICE file. // // You may obtain a copy of the Apache License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the Apache License with the above modification is // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the Apache License for the specific // language governing permissions and limitations under the Apache License. // #include #include #include #include #include #include "hud.h" #include "font_image.h" #if _MSC_VER #define snprintf _snprintf #endif Hud::Hud() : _visible(true), _windowWidth(0), _windowHeight(0), _framebufferWidth(0), _framebufferHeight(0), _requiresRebuildStatic(true) { _capturedSlider = -1; } Hud::~Hud() { } void Hud::Init(int width, int height, int framebufferWidth, int framebufferHeight) { _windowWidth = width; _windowHeight = height; _framebufferWidth = framebufferWidth; _framebufferHeight = framebufferHeight; } int Hud::GetWidth() const { return _windowWidth; } int Hud::GetHeight() const { return _windowHeight; } void Hud::Clear() { _radioButtons.clear(); _checkBoxes.clear(); _sliders.clear(); _vboSource.clear(); _requiresRebuildStatic = true; } bool Hud::KeyDown(int key) { for (std::vector::iterator it = _radioButtons.begin(); it != _radioButtons.end(); ++it) { if (key == it->shortcut) { int nextLocalIndex = it->localIndex; if (it->sharedShortcut) { // find checked radio button in this group int maxLocalIndex = 0; for (std::vector::iterator it2 = _radioButtons.begin(); it2 != _radioButtons.end(); ++it2) { if (it2->group == it->group) { maxLocalIndex = std::max(maxLocalIndex, it2->localIndex); if (it2->checked) { nextLocalIndex = it2->localIndex+1; } } } if (nextLocalIndex > maxLocalIndex) nextLocalIndex = 0; } for (std::vector::iterator it2 = _radioButtons.begin(); it2 != _radioButtons.end(); ++it2) { if (it2->group == it->group) { if (it2->localIndex == nextLocalIndex) { it2->checked = true; it2->callback(it2->callbackData); } else { it2->checked = false; } } } _requiresRebuildStatic = true; return true; } } for (std::vector::iterator it = _checkBoxes.begin(); it != _checkBoxes.end(); ++it) { if (key == it->shortcut) { it->checked = !it->checked; it->callback(it->checked, it->callbackData); _requiresRebuildStatic = true; return true; } } for (std::vector::iterator it = _pulldowns.begin(); it != _pulldowns.end(); ++it) { if (key==it->shortcut) { // cycle through selections ++it->selected; if (it->selected>=(int)it->labels.size()) { it->selected=0; } it->callback(it->values[it->selected]); _requiresRebuildStatic = true; return true; } } return false; } bool Hud::MouseClick(int x, int y) { if (!IsVisible()) { return false; } for (std::vector::iterator it = _radioButtons.begin(); it != _radioButtons.end(); ++it) { if (hitTest(*it, x, y)) { for (std::vector::iterator it2 = _radioButtons.begin(); it2 != _radioButtons.end(); ++it2) { if (it2->group == it->group && it != it2) it2->checked = false; } it->checked = true; it->callback(it->callbackData); _requiresRebuildStatic = true; return true; } } for (std::vector::iterator it = _checkBoxes.begin(); it != _checkBoxes.end(); ++it) { if (hitTest(*it, x, y)) { it->checked = !it->checked; it->callback(it->checked, it->callbackData); _requiresRebuildStatic = true; return true; } } for (std::vector::iterator it = _sliders.begin(); it != _sliders.end(); ++it) { if (hitTest(*it, x, y)) { int bx = 0, by = 0; getWindowPos(*it, &bx, &by); it->SetValue(((x-bx-FONT_CHAR_WIDTH/2)/float(it->w))*(it->max - it->min) + it->min); it->callback(it->value, it->callbackData); _capturedSlider = (int)(it - _sliders.begin()); _requiresRebuildStatic = true; return true; } } for (std::vector::iterator it = _pulldowns.begin(); it != _pulldowns.end(); ++it) { if (hitTest(*it, x, y)) { if (not it->open) { it->h = FONT_CHAR_HEIGHT; it->h *= (int)it->labels.size(); it->open=true; } else { int label_width = (3+(int)it->label.size()) * FONT_CHAR_WIDTH; int bx = 0, by = 0; getWindowPos(*it, &bx, &by); if (x > (bx+label_width)) { int sel = it->selected; it->SetSelected((y-by)/FONT_CHAR_HEIGHT); if (it->selected!=sel) { it->callback(it->values[it->selected]); } } else { it->open=not it->open; } } _requiresRebuildStatic = true; return true; } } return false; } void Hud::MouseMotion(int x, int /* y */) { if (_capturedSlider != -1) { std::vector::iterator it = _sliders.begin() + _capturedSlider; int bx = it->x > 0 ? it->x : _windowWidth + it->x; it->SetValue(((x-bx-FONT_CHAR_WIDTH/2)/float(it->w))*(it->max - it->min) + it->min); it->callback(it->value, it->callbackData); _requiresRebuildStatic = true; } } bool Hud::MouseCapture() const { return _capturedSlider != -1; } void Hud::MouseRelease() { _capturedSlider = -1; } void Hud::AddLabel(const char *label, int x, int y) { Item l; l.label = label; l.x = x; l.y = y; _labels.push_back(l); _requiresRebuildStatic = true; } void Hud::AddCheckBox(const char *label, bool checked, int x, int y, CheckBoxCallback callback, int data, int shortcut) { CheckBox cb; cb.label = label; cb.checked = checked; cb.x = x; cb.y = y; cb.w = (int)(strlen(label)+1) * FONT_CHAR_WIDTH; cb.h = FONT_CHAR_HEIGHT; cb.callback = callback; cb.callbackData = data; cb.shortcut = shortcut; _checkBoxes.push_back(cb); _requiresRebuildStatic = true; } void Hud::AddRadioButton(int group, const char *label, bool checked, int x, int y, RadioButtonCallback callback, int data, int shortcut) { RadioButton rb; rb.group = group; rb.label = label; rb.checked = checked; rb.x = x; rb.y = y; rb.w = (int)(strlen(label)+1) * FONT_CHAR_WIDTH; rb.h = FONT_CHAR_HEIGHT; rb.callback = callback; rb.callbackData = data; rb.shortcut = shortcut; rb.sharedShortcut = false; rb.localIndex = 0; for (std::vector::iterator it = _radioButtons.begin(); it != _radioButtons.end(); ++it) { if (it->group == group) { rb.localIndex = it->localIndex+1; if (it->shortcut == shortcut) { it->sharedShortcut = true; rb.sharedShortcut = true; } } } _radioButtons.push_back(rb); _requiresRebuildStatic = true; } void Hud::AddSlider(const char *label, float min, float max, float value, int x, int y, int width, bool intStep, SliderCallback callback, int data) { Slider slider; slider.label = label; slider.x = x; slider.y = y; slider.w = width * FONT_CHAR_WIDTH; slider.h = FONT_CHAR_HEIGHT * 2; slider.min = min; slider.max = max; slider.value = value; slider.callback = callback; slider.callbackData = data; slider.intStep = intStep; _sliders.push_back(slider); _requiresRebuildStatic = true; } int Hud::AddPullDown(const char *label, int x, int y, int width, PullDownCallback callback, int shortcut) { PullDown pd; pd.label = label; pd.x = x; pd.y = y; pd.w = width; pd.h = FONT_CHAR_HEIGHT; pd.open = false; pd.selected = 0; pd.callback = callback; pd.shortcut = shortcut; _pulldowns.push_back(pd); _requiresRebuildStatic = true; return (int)_pulldowns.size()-1; } void Hud::AddPullDownButton(int handle, const char *label, int value, bool checked) { if (handle < (int)_pulldowns.size()) { PullDown & pulldown = _pulldowns[handle]; pulldown.labels.push_back(label); pulldown.values.push_back(value); if (checked) { pulldown.selected = (int)pulldown.labels.size()-1; } } } int Hud::drawChar(std::vector &vboSource, int x, int y, float r, float g, float b, char ch) { const float w = 1.0f/FONT_TEXTURE_COLUMNS; const float h = 1.0f/FONT_TEXTURE_ROWS; float u = (ch%FONT_TEXTURE_COLUMNS)/(float)FONT_TEXTURE_COLUMNS; float v = (ch/FONT_TEXTURE_COLUMNS)/(float)FONT_TEXTURE_ROWS; vboSource.push_back(float(x)); vboSource.push_back(float(y)); vboSource.push_back(r); vboSource.push_back(g); vboSource.push_back(b); vboSource.push_back(u); vboSource.push_back(v); vboSource.push_back(float(x)); vboSource.push_back(float(y+FONT_CHAR_HEIGHT)); vboSource.push_back(r); vboSource.push_back(g); vboSource.push_back(b); vboSource.push_back(u); vboSource.push_back(v+h); vboSource.push_back(float(x+FONT_CHAR_WIDTH)); vboSource.push_back(float(y)); vboSource.push_back(r); vboSource.push_back(g); vboSource.push_back(b); vboSource.push_back(u+w); vboSource.push_back(v); vboSource.push_back(float(x+FONT_CHAR_WIDTH)); vboSource.push_back(float(y)); vboSource.push_back(r); vboSource.push_back(g); vboSource.push_back(b); vboSource.push_back(u+w); vboSource.push_back(v); vboSource.push_back(float(x)); vboSource.push_back(float(y+FONT_CHAR_HEIGHT)); vboSource.push_back(r); vboSource.push_back(g); vboSource.push_back(b); vboSource.push_back(u); vboSource.push_back(v+h); vboSource.push_back(float(x+FONT_CHAR_WIDTH)); vboSource.push_back(float(y+FONT_CHAR_HEIGHT)); vboSource.push_back(r); vboSource.push_back(g); vboSource.push_back(b); vboSource.push_back(u+w); vboSource.push_back(v+h); return x + FONT_CHAR_WIDTH; } int Hud::drawString(std::vector &vboSource, int x, int y, float r, float g, float b, const char *c) { while (*c) { char ch = (char)((*c) & 0x7f); x = drawChar(vboSource, x, y, r, g, b, ch); c++; } return x; } void Hud::DrawString(int x, int y, const char *fmt, ...) { char buf[1024]; va_list args; va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); x = x > 0 ? x : _windowWidth + x; y = y > 0 ? y : _windowHeight + y; drawString(_vboSource, x, y, 1, 1, 1, buf); } void Hud::DrawString(int x, int y, float r, float g, float b, const char *fmt, ...) { char buf[1024]; va_list args; va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); x = x > 0 ? x : _windowWidth + x; y = y > 0 ? y : _windowHeight + y; drawString(_vboSource, x, y, r, g, b, buf); } void Hud::Rebuild(int width, int height, int framebufferWidth, int framebufferHeight) { _requiresRebuildStatic = false; _windowWidth = width; _windowHeight = height; _framebufferWidth = framebufferWidth; _framebufferHeight = framebufferHeight; _staticVboSource.clear(); int x, y; // add UI elements for (std::vector::const_iterator it = _labels.begin(); it != _labels.end(); ++it) { getWindowPos(*it, &x, &y); drawString(_staticVboSource, x, y, 1, 1, 1, it->label.c_str()); } // draw radio buttons for (std::vector::const_iterator it = _radioButtons.begin(); it != _radioButtons.end(); ++it) { getWindowPos(*it, &x, &y); if (it->checked) { x = drawChar(_staticVboSource, x, y, 1, 1, 1, FONT_RADIO_BUTTON_ON); drawString(_staticVboSource, x, y, 1, 1, 0, it->label.c_str()); } else { x = drawChar(_staticVboSource, x, y, 1, 1, 1, FONT_RADIO_BUTTON_OFF); drawString(_staticVboSource, x, y, .5f, .5f, .5f, it->label.c_str()); } } // draw checkboxes for (std::vector::const_iterator it = _checkBoxes.begin(); it != _checkBoxes.end(); ++it) { getWindowPos(*it, &x, &y); if (it->checked) { x = drawChar(_staticVboSource, x, y, 1, 1, 1, FONT_CHECK_BOX_ON); drawString(_staticVboSource, x, y, 1, 1, 0, it->label.c_str()); } else { x = drawChar(_staticVboSource, x, y, 1, 1, 1, FONT_CHECK_BOX_OFF); drawString(_staticVboSource, x, y, .5f, .5f, .5f, it->label.c_str()); } } // draw sliders for (std::vector::const_iterator it = _sliders.begin(); it != _sliders.end(); ++it) { getWindowPos(*it, &x, &y); int sx = x; x = drawString(_staticVboSource, x, y, 1, 1, 1, it->label.c_str()); char value[16]; if (it->intStep) { snprintf(value, 16, " : %d", (int)it->value); } else { snprintf(value, 16, " : %.2f", it->value); } drawString(_staticVboSource, x, y, 1, 1, 1, value); // new line y += FONT_CHAR_HEIGHT; x = sx; x = drawChar(_staticVboSource, x, y, 1, 1, 1, FONT_SLIDER_LEFT); int nw = it->w / FONT_CHAR_WIDTH; for (int i = 1; i < nw; ++i) { x = drawChar(_staticVboSource, x, y, 1, 1, 1, FONT_SLIDER_MIDDLE); } drawChar(_staticVboSource, x, y, 1, 1, 1, FONT_SLIDER_RIGHT); int pos = (int)((it->value/float(it->max-it->min))*it->w); drawChar(_staticVboSource, sx+pos, y, 1, 1, 0, FONT_SLIDER_CURSOR); } // draw pulldowns for (std::vector::const_iterator it = _pulldowns.begin(); it != _pulldowns.end(); ++it) { getWindowPos(*it, &x, &y); x = drawString(_staticVboSource, x, y, .5f, .5f, .5f, it->label.c_str()); x += FONT_CHAR_WIDTH; if (it->open) { x = drawChar(_staticVboSource, x, y, 1, 1, 0, FONT_ARROW_DOWN); x += FONT_CHAR_WIDTH; for (int i=0; i<(int)it->labels.size(); ++i, y+=FONT_CHAR_HEIGHT) { if (i==it->selected) { drawString(_staticVboSource, x, y, 1, 1, 0, it->labels[i]); } else { drawString(_staticVboSource, x, y, 0.5f, 0.5f, 0.5f, it->labels[i]); } } } else { x = drawChar(_staticVboSource, x, y, .5f, .5f, .5f, FONT_ARROW_RIGHT); x += FONT_CHAR_WIDTH; drawString(_staticVboSource, x, y, 1, 1, 0, it->labels[it->selected]); } } drawString(_staticVboSource, _windowWidth-80, _windowHeight-48, .5, .5, .5, "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"); drawString(_staticVboSource, _windowWidth-80, _windowHeight-32, .5, .5, .5, "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"); } bool Hud::Flush() { if (!_visible) { _vboSource.clear(); return false; } if (_requiresRebuildStatic) this->Rebuild(_windowWidth, _windowHeight, _framebufferWidth, _framebufferHeight); return true; } bool Hud::IsVisible() const { return _visible; } void Hud::SetVisible(bool visible) { _visible = visible; } std::vector & Hud::getVboSource() { return _vboSource; } std::vector & Hud::getStaticVboSource() { return _staticVboSource; }