From 6a5c51bc693ff1872f88d81ce19a6a026435fdb6 Mon Sep 17 00:00:00 2001 From: vincent Date: Wed, 10 Apr 2013 21:31:29 +0200 Subject: [PATCH] #19171 Add a simple openGL menu that will work x-platform --- interface/src/Menu.cpp | 330 +++++++++++++++++++++++++++++++++++++++++ interface/src/Menu.h | 72 +++++++++ interface/src/Util.cpp | 11 ++ interface/src/Util.h | 1 + interface/src/main.cpp | 112 +++++++++++--- 5 files changed, 508 insertions(+), 18 deletions(-) create mode 100644 interface/src/Menu.cpp create mode 100644 interface/src/Menu.h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp new file mode 100644 index 0000000000..160b59fae7 --- /dev/null +++ b/interface/src/Menu.cpp @@ -0,0 +1,330 @@ + +#include +#include "InterfaceConfig.h" +#include "Menu.h" +#include "Util.h" + +int menuHeight = 30; +int lineHeight = 30; +int yOffset = 8; // under windows we have 8 vertical pixels offset. In 2D an object with y=8, the object is displayed at y=0 + // change the value in the other platforms (if required). + +MenuRow::MenuRow() +{ +} +MenuRow::MenuRow(char * columnName, PFNRowCallback callback) +{ + int length = std::min(MAX_COLUMN_NAME - 5,(int) strlen(columnName)); + strncpy(this->rowName, columnName, length); + memcpy(this->rowName + length, " \0", 5); + this->callback = callback; + rowWidth = 0; +} + +MenuRow::~MenuRow() +{ +} + +void MenuRow::call() { + callback(-2); +} + +char * MenuRow::getName() { + int length = (int) strlen(this->rowName) - 4; + int currentValue = callback(-1); + if (currentValue == 0) { + memcpy(this->rowName + length, " OFF\0", 5); + } else if (currentValue == 1) { + memcpy(this->rowName + length, " ON \0", 5); + } else { + memcpy(this->rowName + length, " \0", 5); + } + return this->rowName; +} + +int MenuRow::getWidth(float scale, int mono, int leftPosition){ + if (rowWidth == 0) { + rowWidth = widthText( scale, mono, this->rowName); + } + return rowWidth; +} + +int MenuRow::getWidth(){ + return rowWidth; +} + + +MenuColumn::MenuColumn() +{ +} +MenuColumn::MenuColumn(char * columnName) +{ + int length = std::min(MAX_COLUMN_NAME - 1,(int) strlen(columnName)); + strncpy(this->columnName, columnName, length); + this->columnName[length] = '\0'; + columnWidth = 0; + leftPosition = 0; + xLeftMouseOver = 0; + xRightMouseOver = 0; + yTopMouseOver = 0; + yBottomMouseOver = 0; +} + +MenuColumn::~MenuColumn() +{ +} + +void MenuColumn::mouseClickRow(int iRowIndex) { + rows[iRowIndex].call(); +} + +bool MenuColumn::mouseClick(int x, int y, int xLeft) { + int xRight = xLeft + 200; + int yTop = menuHeight; + int yBottom = menuHeight; + int columnWidth; + bool bRet = false; + for (unsigned int i = 0; i < rows.size(); ++i) { + columnWidth = rows[i].getWidth(); + yTop = yBottom + lineHeight; + if (x > xLeft && x < xRight && y > yBottom && y < yTop) { + mouseClickRow(i); + bRet = true; + break; + } + yBottom = yTop; + } + return bRet; +} + +void MenuColumn::setMouseOver(int xLeft, int xRight, int yTop, int yBottom) { + xLeftMouseOver = xLeft; + xRightMouseOver = xRight; + yTopMouseOver = yTop; + yBottomMouseOver = yBottom; +} + +bool MenuColumn::mouseOver(int x, int y, int xLeft) { + int xRight = xLeft + 100; + int yTop = menuHeight; + int yBottom = menuHeight; + int columnWidth; + bool bRet = false; + for (unsigned int i = 0; i < rows.size(); ++i) { + columnWidth = rows[i].getWidth(); + yTop = yBottom + lineHeight ; + if (x > xLeft && x < xRight && y > yBottom && y < yTop) { + setMouseOver(xLeft, xRight, yBottom, yTop); + bRet = true; + break; + } + yBottom = yTop; + } + if (!bRet) { + setMouseOver(0, 0, 0, 0); + } + return bRet; +} + +char * MenuColumn::getName() { + return this->columnName; +} + +int MenuColumn::getWidth(float scale, int mono, int leftPosition){ + if (columnWidth == 0) { + columnWidth = widthText( scale, mono, this->columnName); + this->leftPosition = leftPosition; + } + return columnWidth; +} + +int MenuColumn::getWidth(){ + return columnWidth; +} + +int MenuColumn::getLeftPosition(){ + return leftPosition; +} + +int MenuColumn::addRow(char * rowName, PFNRowCallback callback){ + MenuRow * pRow; + pRow = new MenuRow(rowName, callback); + rows.push_back(*pRow); + delete pRow; + return 0; + +} + +void MenuColumn::render() { + int iRow = rows.size(); + if (iRow > 0) { + glColor3f(0.9,0.9,0.9); + glBegin(GL_QUADS); { + glVertex2f(leftPosition, yOffset + menuHeight); + glVertex2f(leftPosition+100, yOffset + menuHeight); + glVertex2f(leftPosition+100, yOffset + menuHeight + iRow*lineHeight); + glVertex2f(leftPosition , yOffset + menuHeight + iRow* lineHeight); + } + glEnd(); + } + float scale = 0.10; + int mono = 0; + int y = menuHeight + lineHeight / 2 ; + char * rowName; + int columnWidth; + for (unsigned int i = 0; i < rows.size(); ++i) + { + rowName = rows[i].getName(); + columnWidth = rows[i].getWidth(scale, mono, 0); + drawtext(leftPosition + SPACE_BEFORE_ROW_NAME, y+5 + yOffset, scale, 0, 1.0, mono, rowName, 0, 0, 0); + y += lineHeight; + } + renderMouseOver(); +} + +void MenuColumn::renderMouseOver() { + if (xLeftMouseOver != 0 || yTopMouseOver != 0 || xRightMouseOver != 0 ||& yBottomMouseOver != 0){ + glColor4f(0,0,0,0.1); + glBegin(GL_QUADS); { + glVertex2f(xLeftMouseOver, yOffset + yTopMouseOver); + glVertex2f(xRightMouseOver, yOffset + yTopMouseOver); + glVertex2f(xRightMouseOver, yOffset + yBottomMouseOver); + glVertex2f(xLeftMouseOver , yOffset + yBottomMouseOver); + } + glEnd(); + } +} + +Menu::Menu(){ + iCurrentColumn = -1; + xLeftMouseOver = 0; + xRightMouseOver = 0; + yTopMouseOver = 0; + yBottomMouseOver = 0; +} + + +Menu::~Menu(){ + columns.clear(); +} + +void Menu::mouseClickColumn(int iColumnIndex) { + if (iCurrentColumn == iColumnIndex) { + iCurrentColumn = -1; + } else { + iCurrentColumn = iColumnIndex; + } +} + +void Menu::setMouseOver(int xLeft, int xRight, int yTop, int yBottom) { + xLeftMouseOver = xLeft; + xRightMouseOver = xRight; + yTopMouseOver = yTop; + yBottomMouseOver = yBottom; +} + +void Menu::renderMouseOver() { + if (xLeftMouseOver != 0 || yTopMouseOver != 0 || xRightMouseOver != 0 ||& yBottomMouseOver != 0){ + glColor4f(0,0,0,0.1); + glBegin(GL_QUADS); { + glVertex2f(xLeftMouseOver, yOffset + yTopMouseOver); + glVertex2f(xRightMouseOver, yOffset + yTopMouseOver); + glVertex2f(xRightMouseOver, yOffset + yBottomMouseOver); + glVertex2f(xLeftMouseOver , yOffset + yBottomMouseOver); + } + glEnd(); + } +} + +bool Menu::mouseClick(int x, int y) { + int xLeft = 0.5 * SPACE_BETWEEN_COLUMNS; + int xRight; + int columnWidth; + bool bRet = false; + for (unsigned int i = 0; i < columns.size(); ++i) + { + columnWidth = columns[i].getWidth(); + xRight = xLeft + columnWidth + 1.5 * SPACE_BETWEEN_COLUMNS; + if (x > xLeft && x < xRight && y > 0 && y < menuHeight){ + mouseClickColumn(i); + bRet = true; + break; + } + if (iCurrentColumn == i) { + bRet = columns[i].mouseClick(x, y, xLeft); + if (bRet) { + iCurrentColumn = -1; + } + } + xLeft = xRight; + } + return bRet; +} + +bool Menu::mouseOver(int x, int y) { + int xLeft = 0.5 * SPACE_BETWEEN_COLUMNS; + int xRight; + int columnWidth; + bool bRet = false; + for (unsigned int i = 0; i < columns.size(); ++i) + { + columnWidth = columns[i].getWidth(); + xRight = xLeft + columnWidth + SPACE_BETWEEN_COLUMNS; + if (x > xLeft && x < xRight && y > 0 && y < menuHeight){ + printf("Mouse Over: %d %d", x, y); + + setMouseOver(xLeft, xRight, 0, menuHeight); + bRet = true; + if (iCurrentColumn >= 0) { + columns[iCurrentColumn].setMouseOver(0, 0, 0, 0); + iCurrentColumn = i; + } + break; + } else if (iCurrentColumn == i) { + columns[i].mouseOver(x, y, xLeft); + } + xLeft = xRight; + } + if (!bRet) { + setMouseOver(0, 0, 0, 0); + } + return bRet; +} + +void Menu::render(int screenwidth, int screenheight) { + float scale = 0.10; + int mono = 0; + glColor3f(0.9,0.9,0.9); + int width = screenwidth; + int height = screenheight; + glBegin(GL_QUADS); { + glVertex2f(0, yOffset); + glVertex2f(width, yOffset); + glVertex2f( width, menuHeight + yOffset); + glVertex2f(0 , menuHeight + yOffset); + } + glEnd(); + int x = SPACE_BETWEEN_COLUMNS; + char * columnName; + int columnWidth; + for (unsigned int i = 0; i < columns.size(); ++i) + { + columnName = columns[i].getName(); + columnWidth = columns[i].getWidth(scale, mono, x- 0.5 * SPACE_BETWEEN_COLUMNS); + drawtext(x,18 + yOffset, scale, 0, 1.0, mono, columnName, 0, 0, 0); + x += columnWidth + SPACE_BETWEEN_COLUMNS; + if (iCurrentColumn == i) { + columns[i].render(); + } + } + renderMouseOver(); + } + + +MenuColumn* Menu::addColumn(char *columnName) { + MenuColumn * pColumn; + pColumn = new MenuColumn(columnName); + columns.push_back(*pColumn); + delete pColumn; + return &columns[columns.size()-1]; +} \ No newline at end of file diff --git a/interface/src/Menu.h b/interface/src/Menu.h new file mode 100644 index 0000000000..7b8eb9bad6 --- /dev/null +++ b/interface/src/Menu.h @@ -0,0 +1,72 @@ + +#include + +#define MAX_COLUMN_NAME 50 +#define SPACE_BETWEEN_COLUMNS 20 +#define SPACE_BEFORE_ROW_NAME 10 +typedef int(CALLBACK * PFNRowCallback)(int); + + +class MenuRow { + public: + MenuRow(); + MenuRow(char * rowName, PFNRowCallback); + ~MenuRow(); + void call(); + char * getName(); + int getWidth(float scale, int mono, int leftPosition); + int getWidth(); +private: + char rowName[MAX_COLUMN_NAME]; + int rowWidth; + PFNRowCallback callback; +}; + + +class MenuColumn { + public: + MenuColumn(); + MenuColumn(char * columnName); + ~MenuColumn(); + void mouseClickRow(int iColumnIndex); + bool mouseClick(int x, int y, int xLeft); + void setMouseOver(int xLeft, int xRight, int yTop, int yBottom); + bool mouseOver(int x, int y, int xLeft); + char * getName(); + int getWidth(float scale, int mono, int leftPosition); + int getWidth(); + int getLeftPosition(); + void render(); + void MenuColumn::renderMouseOver(); + int addRow(char * rowName, PFNRowCallback callback); +private: + char columnName[MAX_COLUMN_NAME]; + int columnWidth; + int leftPosition; + std::vector rows; + int xLeftMouseOver; + int xRightMouseOver; + int yTopMouseOver; + int yBottomMouseOver; +}; + +class Menu { + public: + Menu(); + ~Menu(); + void mouseClickColumn(int iColumnIndex); + void setMouseOver(int xLeft, int xRight, int yTop, int yBottom); + void renderMouseOver(); + bool mouseClick(int x, int y); + bool mouseOver(int x, int y); + void render(int screenwidth, int screenheight); + void renderColumn(int i); + MenuColumn* addColumn(char *columnName); +private: + std::vector columns; + int iCurrentColumn; + int xLeftMouseOver; + int xRightMouseOver; + int yTopMouseOver; + int yBottomMouseOver; +}; \ No newline at end of file diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index a670f48f9e..46f0398a36 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -90,6 +90,17 @@ double diffclock(timeval *clock1,timeval *clock2) return diffms; } +int widthText(float scale, int mono, char *string) { + int width = 0; + if (!mono) { + width =scale * glutStrokeLength(GLUT_STROKE_ROMAN, (const unsigned char *) string); + } + else { + width =scale * glutStrokeLength(GLUT_STROKE_MONO_ROMAN, (const unsigned char *) string); + } + return width; +} + void drawtext(int x, int y, float scale, float rotate, float thick, int mono, char const* string, float r, float g, float b) { diff --git a/interface/src/Util.h b/interface/src/Util.h index 8efe6f549e..766b094f8c 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -41,6 +41,7 @@ float angle_to(glm::vec3 head_pos, glm::vec3 source_pos, float render_yaw, float float randFloat(); void render_world_box(); void render_vector(glm::vec3 * vec); +int widthText(float scale, int mono, char *string); void drawtext(int x, int y, float scale, float rotate, float thick, int mono, char const* string, float r=1.0, float g=1.0, float b=1.0); void drawvec3(int x, int y, float scale, float rotate, float thick, int mono, glm::vec3 vec, diff --git a/interface/src/main.cpp b/interface/src/main.cpp index c2d8c1fc73..b1e69d749c 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -57,6 +57,7 @@ #include "FieldOfView.h" #include "Stars.h" +#include "Menu.h" #include "Head.h" #include "Hand.h" #include "Camera.h" @@ -168,6 +169,9 @@ int mouseX, mouseY; // Where is the mouse int mouseStartX, mouseStartY; // Mouse location at start of last down click int mousePressed = 0; // true if mouse has been pressed (clear when finished) +Menu menu; // main menu +int menuOn = 0; // Whether to show onscreen menu + // // Serial USB Variables // @@ -233,23 +237,27 @@ void Timer(int extra) void displayStats(void) { + int statsVerticalOffset = 50; + if (menuOn == 0) { + statsVerticalOffset = 8; + } // bitmap chars are about 10 pels high char legend[] = "/ - toggle this display, Q - exit, H - show head, M - show hand, T - test audio"; - drawtext(10, 15, 0.10f, 0, 1.0, 0, legend); + drawtext(10, statsVerticalOffset + 15, 0.10f, 0, 1.0, 0, legend); char legend2[] = "* - toggle stars, & - toggle paint mode, '-' - send erase all, '%' - send add scene"; - drawtext(10, 32, 0.10f, 0, 1.0, 0, legend2); + drawtext(10, statsVerticalOffset + 32, 0.10f, 0, 1.0, 0, legend2); glm::vec3 headPos = myHead.getPos(); char stats[200]; sprintf(stats, "FPS = %3.0f Pkts/s = %d Bytes/s = %d Head(x,y,z)=( %f , %f , %f )", FPS, packetsPerSecond, bytesPerSecond, headPos.x,headPos.y,headPos.z); - drawtext(10, 49, 0.10f, 0, 1.0, 0, stats); + drawtext(10, statsVerticalOffset + 49, 0.10f, 0, 1.0, 0, stats); if (serialPort.active) { sprintf(stats, "ADC samples = %d, LED = %d", serialPort.getNumSamples(), serialPort.getLED()); - drawtext(300, 30, 0.10f, 0, 1.0, 0, stats); + drawtext(300, statsVerticalOffset + 30, 0.10f, 0, 1.0, 0, stats); } // Output the ping times to the various agents @@ -262,22 +270,22 @@ void displayStats(void) std::stringstream voxelStats; voxelStats << "Voxels Rendered: " << voxels.getVoxelsRendered(); - drawtext(10,70,0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); + drawtext(10, statsVerticalOffset + 70, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); voxelStats.str(""); voxelStats << "Voxels Created: " << voxels.getVoxelsCreated() << " (" << voxels.getVoxelsCreatedRunningAverage() << "/sec in last "<< COUNTETSTATS_TIME_FRAME << " seconds) "; - drawtext(10,250,0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); + drawtext(10, statsVerticalOffset + 250, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); voxelStats.str(""); voxelStats << "Voxels Colored: " << voxels.getVoxelsColored() << " (" << voxels.getVoxelsColoredRunningAverage() << "/sec in last "<< COUNTETSTATS_TIME_FRAME << " seconds) "; - drawtext(10,270,0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); + drawtext(10, statsVerticalOffset + 270, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); voxelStats.str(""); voxelStats << "Voxels Bytes Read: " << voxels.getVoxelsBytesRead() << " (" << voxels.getVoxelsBytesReadRunningAverage() << "/sec in last "<< COUNTETSTATS_TIME_FRAME << " seconds) "; - drawtext(10,290,0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); + drawtext(10, statsVerticalOffset + 290,0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); voxelStats.str(""); long int voxelsBytesPerColored = voxels.getVoxelsColored() ? voxels.getVoxelsBytesRead()/voxels.getVoxelsColored() : 0; @@ -286,7 +294,7 @@ void displayStats(void) voxelStats << "Voxels Bytes per Colored: " << voxelsBytesPerColored << " (" << voxelsBytesPerColoredAvg << "/sec in last "<< COUNTETSTATS_TIME_FRAME << " seconds) "; - drawtext(10,310,0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); + drawtext(10, statsVerticalOffset + 310, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); if (::perfStatsOn) { @@ -295,7 +303,7 @@ void displayStats(void) int lines = PerfStat::DumpStats(perfStatLinesArray); int atZ = 150; // arbitrary place on screen that looks good for (int line=0; line < lines; line++) { - drawtext(10,atZ,0.10f, 0, 1.0, 0, perfStatLinesArray[line]); + drawtext(10, statsVerticalOffset + atZ, 0.10f, 0, 1.0, 0, perfStatLinesArray[line]); delete perfStatLinesArray[line]; // we're responsible for cleanup perfStatLinesArray[line]=NULL; atZ+=20; // height of a line @@ -828,6 +836,13 @@ void display(void) glPointSize(1.0f); displayStats(); } + + // Show menu + if (menuOn) { + glLineWidth(1.0f); + glPointSize(1.0f); + menu.render(WIDTH,HEIGHT); + } // Draw number of nearby people always glPointSize(1.0f); @@ -851,6 +866,62 @@ void display(void) frameCount++; } +int CALLBACK setValue(int state, int *value) { + if (state == -2) { + *value = !(*value); + } else if (state == -1) { + return *value; + } else { + *value = state; + } + return *value; +} + +int CALLBACK setHead(int state) { + return setValue(state, &displayHead); +} + +int CALLBACK setField(int state) { + return setValue(state, &displayField); +} + +int CALLBACK setNoise(int state) { + int iRet = setValue(state, &noiseOn); + if (noiseOn) + { + myHead.setNoise(noise); + } + else + { + myHead.setNoise(0); + } + return iRet; +} + +int CALLBACK setStats(int state) { + return setValue(state, &statsOn); +} + +int CALLBACK setMenu(int state) { + return setValue(state, &menuOn); +} + +int CALLBACK setMirror(int state) { + return setValue(state, &headMirror); +} + +void initMenu() { + MenuColumn *menuColumnOptions, *menuColumnTools; + menuColumnOptions = menu.addColumn("Options"); + menuColumnOptions->addRow("Head", setHead); + menuColumnOptions->addRow("Field", setField); + menuColumnOptions->addRow("Noise", setNoise); + menuColumnOptions->addRow("Mirror", setMirror); + menuColumnTools = menu.addColumn("Tools"); + menuColumnTools->addRow("Stats", setStats); + menuColumnTools->addRow("Menu", setMenu); +} + void testPointToVoxel() { float y=0; @@ -1022,7 +1093,8 @@ void key(unsigned char k, int x, int y) #endif } - if (k == 'm') headMirror = !headMirror; +// if (k == 'm') headMirror = !headMirror; // move in the menu + if (k == 'm') setMenu(-2); if (k == 'f') displayField = !displayField; if (k == 'l') displayLevels = !displayLevels; @@ -1166,12 +1238,14 @@ void mouseFunc( int button, int state, int x, int y ) { if( button == GLUT_LEFT_BUTTON && state == GLUT_DOWN ) { - mouseX = x; - mouseY = y; - mousePressed = 1; - lattice.mouseClick((float)x/(float)WIDTH,(float)y/(float)HEIGHT); - mouseStartX = x; - mouseStartY = y; + if (menu.mouseClick(x, y) == false) { + mouseX = x; + mouseY = y; + mousePressed = 1; + lattice.mouseClick((float)x/(float)WIDTH,(float)y/(float)HEIGHT); + mouseStartX = x; + mouseStartY = y; + } } if( button == GLUT_LEFT_BUTTON && state == GLUT_UP ) { @@ -1192,6 +1266,7 @@ void motionFunc( int x, int y) void mouseoverFunc( int x, int y) { + menu.mouseOver(x, y); mouseX = x; mouseY = y; if (mousePressed == 0) @@ -1255,7 +1330,8 @@ int main(int argc, const char * argv[]) #endif printf( "Created Display Window.\n" ); - + + initMenu(); initDisplay(); printf( "Initialized Display.\n" );