shithub: tinygl

Download patch

ref: a2713e4763320b7aeeffcc57770286451938ef7b
parent: 8ec1b78de635df7ceda13ff300aedc5b97408517
author: David <gek@katherine>
date: Wed Mar 3 07:28:48 EST 2021

OpenIMGUI standard added

--- /dev/null
+++ b/SDL_Examples/include/openimgui.h
@@ -1,0 +1,212 @@
+#include <math.h>
+//PROTOTYPE FOR THE OPENIMGUISTANDARD PROPOSAL
+
+//Licensed to you under the CC0 license.
+
+
+
+//This is the standard for an intuitive immediate-mode gui specification which gracefully solves many of the shortcomings of 
+//other immediate mode gui standards.
+
+//1) How elements are drawn across different environments
+//2) How keyboard/gamepad cursor navigation is handled
+//3) How the same GUI rendering code can be transported between backends.
+
+//This is a standard for immediate mode GUI elements which can be implemented anywhere and gracefully decreases in feature level based on platform.
+
+//If your target platform can render text and it can render boxes, then it can run openimgui.
+
+// The screen's top left corner is 0,0 and bottom right is 1,1
+
+// All coordinates and dimensions are specified relative to that.
+
+//HOW CURSOR BUTTON IS HANDLED~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+//Beginning of your frame...
+
+//omg_cb = 0; 
+//if(just_touched || just_mouseleftbuttondown || just_button_down) omg_cb = 1; //Pressed!
+//if(just_released_touch || just_mouseleftbutton up || just_button_up) omg_cb = 2; //Released!
+
+//Gui code this frame...
+
+//HOW CURSOR POSITION IS HANDLED:
+// On platforms with touch or mouse input, the polling of cursor position will occur like this~~~~~~~~~~~~~
+// omg_cursor_has_been_sucked = 0;
+// omg_cursorpos[0] = device_cursorpos.x / (float) screenWidth;
+// omg_cursorpos[1] = device_cursorpos.x / (float) screenHeight;
+// Clamp the cursorpos (if necessary)
+// omg_cursorpos[0] = omg_clampf(omg_cursorpos[0]);
+// omg_cursorpos[1] = omg_clampf(omg_cursorpos[1]);
+// omg_cursorpos_presuck[0] = -1;
+// omg_cursorpos_presuck[1] = -1;
+
+// On platforms which use buttons to navigate menu elements...~~~~~~~~~~~~~
+// omg_cursor_has_been_sucked = 0;
+// if(buttonleft) omg_cursorpos[0] -= omg_buttonjump[0];
+// if(buttonright) omg_cursorpos[0] += omg_buttonjump[0];
+// if(buttonup) omg_cursorpos[1] -= omg_buttonjump[1];
+// if(buttondown) omg_cursorpos[1] += omg_buttonjump[1];
+// Clamp the cursorpos
+// omg_cursorpos[0] = omg_wrapf(omg_cursorpos[0]);
+// omg_cursorpos[1] = omg_wrapf(omg_cursorpos[1]);
+// omg_cursorpos_presuck[0] = omg_cursorpos[0];
+// omg_cursorpos_presuck[1] = omg_cursorpos[1];
+
+// HOW BUTTON SUCKING WORKS ~~~~~~~~~~~~~~
+
+// On platforms without cursor input such as game consoles, there needs to be an ergonomic way to navigate menus.
+
+// This is achieved by simulating a virtual mouse cursor in the game and "Sucking" it into the closest sucking box.
+
+// We keep track of the cursorposition every frame as well as the position before an attempt to "suck" it has been made.
+// This allows us to determine (By testing, for every graphical object) whether or not the cursorposition should be "sucked" into
+// the graphical object.
+
+// Normalized cursor position
+#ifndef OPENIMGUI_IMPL
+extern float omg_cursorpos[2]; //Defaults to zero
+extern float omg_cursorpos_presuck[2]; //Defaults to zero
+extern int omg_cursor_has_been_sucked;
+extern float omg_buttonjump[2]; //Defaults to zero
+// Setting for users using 
+
+// cursor button
+extern int omg_cb; //Set to zero every iteration.
+#else
+float omg_cursorpos[2]; //Defaults to zero
+float omg_cursorpos_presuck[2]; //Defaults to zero
+int omg_cursor_has_been_sucked;
+float omg_buttonjump[2]; //Defaults to zero
+// Setting for users using 
+
+// cursor button
+int omg_cb; //Set to zero every iteration.
+#endif
+//Used for determining the closest button in sucking mode.
+static inline float omg_sqrlinelength(float x1, float y1, float x2, float y2){
+	return ((x1-x2) * (x1-x2) + (y1-y2) * (y1-y2));
+}
+//Used for clamping cursor position to the screen.
+static inline float omg_clampf(float x){
+	return (x>1.0)?1.0: (x<0.0)?0.0:x;
+}
+//Used for wrapping the cursor position to the screen in button cursor mode.
+static inline float omg_wrapf(float x){
+	float f = fmod(x, 1);
+	if(f<0.0) (f = 1.0 + f);
+	return f;
+}
+
+static inline void omg_update_keycursor(int up, int down, int left, int right, int bstate){
+	static int bstate_old = 0;
+	omg_cursor_has_been_sucked = 0;
+	if(left) omg_cursorpos[0] -= omg_buttonjump[0];
+	if(right) omg_cursorpos[0] += omg_buttonjump[0];
+	if(up) omg_cursorpos[1] -= omg_buttonjump[1];
+	if(down) omg_cursorpos[1] += omg_buttonjump[1];
+	//Clamp the cursorpos
+	omg_cursorpos[0] = omg_wrapf(omg_cursorpos[0]);
+	omg_cursorpos[1] = omg_wrapf(omg_cursorpos[1]);
+	omg_cursorpos_presuck[0] = omg_cursorpos[0];
+	omg_cursorpos_presuck[1] = omg_cursorpos[1];
+
+	//omb_cb = 0; 
+	if(bstate && !bstate_old) omg_cb = 1;
+	else omg_cb = 0;
+	bstate_old = bstate;
+}
+
+//for mouse cursors and touch input.
+static inline void omg_update_mcursor(float ncx, float ncy, int bstate){
+	static int bstate_old = 0;
+	omg_cursor_has_been_sucked = 0;
+	omg_cursorpos[0] = ncx;
+	omg_cursorpos[1] = ncy;
+	// Clamp the cursorpos (if necessary)
+	omg_cursorpos[0] = omg_clampf(omg_cursorpos[0]);
+	omg_cursorpos[1] = omg_clampf(omg_cursorpos[1]);
+	omg_cursorpos_presuck[0] = -1;
+	omg_cursorpos_presuck[1] = -1;
+
+	omg_cb = 0; 
+	if(bstate && !bstate_old) omg_cb = 1;
+	else if (!bstate && bstate_old) omg_cb = 2;
+	bstate_old = bstate;
+}
+static inline int omg_boxtest(float x, float y, float xdim, float ydim, float cx, float cy){
+	if((x <= cx) &&
+			(x+xdim >= cx) &&
+			(y <= cy) &&
+			(y+ydim >= cy))
+		return 1;
+	return 0;
+}
+static inline int omg_box_retval(float x, float y, float xdim, float ydim){
+	if(omg_cursorpos_presuck[0] == -1) 
+		return omg_boxtest(x,y,xdim,ydim,	omg_cursorpos[0],omg_cursorpos[1]);
+	return omg_boxtest(x,y,xdim,ydim,		omg_cursorpos_presuck[0],omg_cursorpos_presuck[1]);
+}
+static inline void omg_box_suck(float x, float y, float xdim, float ydim, int sucks, float buttonjumpx, float buttonjumpy){
+	 if(omg_cursorpos_presuck[0] != -1 && sucks){ //Do not attempt to suck if this graphical element does not suck or sucking is not enabled.
+		 if(!omg_cursor_has_been_sucked){
+		 	//We are free to try to suck up the cursor without a check.
+			omg_cursorpos[0] = x + xdim/2.0;
+			omg_cursorpos[1] = y + ydim/2.0;
+			omg_cursor_has_been_sucked = 1;
+		  omg_buttonjump[0] = buttonjumpx;
+		  omg_buttonjump[1] = buttonjumpy;
+		} else if (omg_sqrlinelength(x+xdim/2.0, y+ydim/2.0, omg_cursorpos_presuck[0],  omg_cursorpos_presuck[1]) < 
+		           omg_sqrlinelength(omg_cursorpos[0], omg_cursorpos[1], omg_cursorpos_presuck[0],  omg_cursorpos_presuck[1])){ 
+		           //The box is closer than the current suck position.
+			omg_cursorpos[0] = x+xdim/2.0;
+			omg_cursorpos[1] = y+ydim/2.0;
+			omg_cursor_has_been_sucked = 1;
+		  omg_buttonjump[0] = buttonjumpx;
+		  omg_buttonjump[1] = buttonjumpy;
+		}
+	}
+}
+// OMG_BOX:
+// Draws a box on the screen.
+// Returns whether or not the cursor was inside it this frame (NOT IF IT GOT __SUCKED__ INSIDE IT!)
+// x,y are the top left corner.
+// xdim, ydim, are the width and height of the box.
+// hints is a set of implementation-specific parameters describing the nature of how the box is drawn,
+// sucks indicates whether or not the cursor position is "sucked" into the button (See: HOW BUTTON SUCKING WORKS)
+// buttonjumpx and buttonjumpy are the amount by which the cursor will jump in X and Y when pressing the menu navigation arrows.
+// The return value is determined like this:
+// if(omg_cursorpos_presuck[0] == -1) return omg_boxtest(omg_cursorpos) else
+//	return boxtest(omg_cursorpos_presuck)
+// The suck test works like this:
+// if(omg_cursorpos_presuck[0] != -1 && sucks){ //Do not attempt to suck if this graphical element does not suck or sucking is not enabled.
+// if(!omg_cursor_has_been_sucked){ //We are free to try to suck up the cursor without a check.
+//	omg_cursorpos[0] = x+xdim/2.0;
+//	omg_cursorpos[1] = y+ydim/2.0;
+//	omg_cursor_has_been_sucked = 1;
+//  omg_buttonjump[0] = buttonjumpx;
+//  omg_buttonjump[1] = buttonjumpy;
+//} else if (omg_sqrlinelength(x+xdim/2.0, y+ydim/2.0, omg_cursorpos_presuck[0],  omg_cursorpos_presuck[1]) < 
+//           omg_sqrlinelength(omg_cursorpos[0], omg_cursorpos[1], omg_cursorpos_presuck[0],  omg_cursorpos_presuck[1])){ //The box is closer than the current suck position.
+//	omg_cursorpos[0] = x+xdim/2.0;
+//	omg_cursorpos[1] = y+ydim/2.0;
+//	omg_cursor_has_been_sucked = 1;
+//  omg_buttonjump[0] = buttonjumpx;
+//  omg_buttonjump[1] = buttonjumpy;
+//}}
+//When sucking is enabled (omg_cursorpos_presuck[0] != -1) the box test will be performed on cursorpos_presuck.
+//You can use the above static inline functions as a reference for your implementation.
+
+int omg_box(float x, float y, float xdim, float ydim, int sucks, float buttonjumpx, float buttonjumpy, int hints);
+
+// OMG_TEXTBOX:
+// Draws a box... with text in it
+// All the args are the same, and its return value is the same, except now it can draw text.
+// It should handle all the same hints as omg_box.
+// the hintstext variable should handle all 
+// The textsize is an implementation-specific indication of how large the text in the box should be.
+// The x and y dimensions of the box are automatically deduced from text.
+// Text containing newlines will extend the Y dimension of the box,
+// and the longest line of text will determine the x dimension of the box.
+// Otherwise, it is functionally identical to omg_box.
+int omg_textbox(float x, float y, const char* text, int textsize, int sucks, float buttonjumpx, float buttonjumpy, int hints, int hintstext);
--- a/SDL_Examples/menu.c
+++ b/SDL_Examples/menu.c
@@ -1,9 +1,10 @@
-/* sdlGears.c */
-/*
- * 3-D gear wheels by Brian Paul. This program is in the public domain.
- *
- * ported to libSDL/TinyGL by Gerald Franz (gfz@o2online.de)
- */
+/* OPENIMGUI STANDARD DEMO
+
+Demo of Gek's proposed Open Immediate Mode Gui Standard
+
+
+
+*/
 //#define PLAY_MUSIC
 
 #include <math.h>
@@ -24,7 +25,11 @@
 #endif
 #include <SDL/SDL.h>
 
+//Gek's OpenIMGUI standard.
+#define OPENIMGUI_IMPL
+#include "include/openimgui.h"
 
+
 #ifndef M_PI
 #define M_PI 3.14159265
 #endif
@@ -31,10 +36,13 @@
 
 int winSizeX = 640;
 int winSizeY = 480;
-int mousepos[2];
-int mb = 0;
 double tpassed = 0;
 int isRunning = 1;
+int dirbstates[4] = {0,0,0,0}; //up,down,left,right
+int mousepos[2] = {0,0};
+int using_cursorkeys = 0; //Switches to cursor keys upon pressing a key.
+int mb = 0; //cursor button
+int mb2 = 0; //cursor second button.
 
 #define BEGIN_EVENT_HANDLER void events(SDL_Event* e){switch(e->type){
 #define E_KEYSYM e->key.keysym.sym
@@ -47,22 +55,14 @@
 #define E_WINW e->window.data1
 #define E_WINH e->window.data2
 
-vec3 mouse_to_normal(){
+vec3 mouse_to_normal(int mx, int my){
 	vec3 r;
-	r.d[0] = mousepos[0] / (float) winSizeX;
-	r.d[1] = mousepos[1] / (float) winSizeY;
+	r.d[0] = mx / (float) winSizeX;
+	r.d[1] = my / (float) winSizeY;
 	return r;
 }
 
-int drawBox(GLfloat x, GLfloat y, GLfloat xdim, GLfloat ydim){ //0,0 is top left, 1,1 is bottom right
-	vec3 r = mouse_to_normal(); 
-	int retval = 0;
-	if(
-		(x <= r.d[0]) &&
-		(x+xdim >= r.d[0]) &&
-		(y <= r.d[1]) &&
-		(y+ydim >= r.d[1])
-	) retval = 1;
+void drawBox(GLfloat x, GLfloat y, GLfloat xdim, GLfloat ydim){ //0,0 is top left, 1,1 is bottom right
 
 	x*=2;xdim*=2;
 	y*=2;ydim*=2;
@@ -86,26 +86,23 @@
 	glTexCoord2f(1, -1);
 	glVertex3f(-1+x+xdim, 1-y , 0.5); //Top Right Corner
 	glEnd();
-	return retval;
+	return;
 }
 
 
 
 void drawMouse(){
-	vec3 r;
-	r.d[0] = mousepos[0] / (float) winSizeX;
-	r.d[1] = mousepos[1] / (float) winSizeY;
-	if(!mb)
+	if(!omg_cb)
 		glColor3f(0.7,0.7,0.7);
 	else
 		glColor3f(1.0,0.1,0.1);
-	drawBox(r.d[0], r.d[1], 0.03, 0.03);
+	drawBox(omg_cursorpos[0],omg_cursorpos[1], 0.03, 0.03);
 }
 
-int drawTB(const char* text, GLuint textcolor, GLfloat x, GLfloat y, GLint size){
+void drawTB(const char* text, GLuint textcolor, GLfloat x, GLfloat y, GLint size, float* tw, float* th){
 	size = (size>64)?64:((size<8)?8:size); 
 	size >>= 3; //divide by 8 to get the GLTEXTSIZE
-	if(!size || !text) return 0;
+	if(!size || !text) return;
 	int mw = 0, h = 1, cw = 0; //max width, height, current width
 	for(int i = 0; text[i] != '\0' && (text[i] & 127);i++){
 		if(text[i] != '\n') 
@@ -118,17 +115,46 @@
 	float bw = 3*size/(float)winSizeX;
 	float h_ = (size)*8*(h)/(float)winSizeY;
 	float bh = 3*size/(float)winSizeY;
-	int retval = drawBox(x-bw/2,y-bh/2, w+bw, h_+bh);
+	drawBox(x,y, w, h_);
+	*tw = w+bw;
+	*th = h_+bh;
 	glTextSize(size);
 	glDrawText((unsigned char*)text, x*winSizeX, y*winSizeY, textcolor);
-	return retval;
+	return;
 }
 
+int omg_box(float x, float y, float xdim, float ydim, int sucks, float buttonjumpx, float buttonjumpy, int hints){
+	//hints is the color of the box.
+	float r = ((hints & 0xFF0000)>>16) 	/ 255.0;
+	float g = ((hints & 0xFF00)>>8) 	/ 255.0;
+	float b = ((hints & 0xFF)) 			/ 255.0;
+	glColor3f(r,g,b);
+	drawBox(x,y,xdim,ydim);
+	omg_box_suck(x, y, xdim, ydim, sucks, buttonjumpx, buttonjumpy);
+	return omg_box_retval(x, y, xdim, ydim);
+}
+
+int omg_textbox(float x, float y, const char* text, int textsize, int sucks, float buttonjumpx, float buttonjumpy, int hints, int hintstext){
+	float r = ((hints & 0xFF0000)>>16) 	/ 255.0;
+	float g = ((hints & 0xFF00)>>8) 	/ 255.0;
+	float b = ((hints & 0xFF)) 			/ 255.0;
+	glColor3f(r,g,b);
+	float xdim = 0, ydim = 0;
+	drawTB(text, (GLuint)hintstext, x,y,textsize, &xdim, &ydim);
+	omg_box_suck(x, y, xdim, ydim, sucks, buttonjumpx, buttonjumpy);
+	return omg_box_retval(x, y, xdim, ydim);
+}
+
 int haveclicked = 0;
 vec3 tbcoords = (vec3){{0.4,0.4,0}};
 void draw() {
-	glColor3f(1,1,1);
-	if(drawTB("Click me and I toggle color!", haveclicked?0xFF0000:0x00, tbcoords.d[0], tbcoords.d[1],16) && mb == 1)
+	if(mb2){
+		tbcoords.d[0] = omg_cursorpos[0];
+		tbcoords.d[1] =  omg_cursorpos[1];
+		haveclicked = 0;
+	}
+	if(
+	omg_textbox(tbcoords.d[0], tbcoords.d[1], "Click me and I toggle color!", 16, 1, 20, 20, 0xFFFFFF, haveclicked?0xFF0000:0x00) && omg_cb == 1)
 		{puts("Detected click! EVENT FIRED!\n");haveclicked = !haveclicked; }
 	drawMouse();
 }
@@ -163,15 +189,16 @@
 
 BEGIN_EVENT_HANDLER
 	case SDL_KEYDOWN:
+		using_cursorkeys = 1;
 		switch(E_KEYSYM){
 			case SDLK_ESCAPE:
 			case SDLK_q:
 				isRunning = 0;
 			break;
-			case SDLK_UP: mousepos[1] -= 4;  mousepos[1]%= winSizeY; break;
-			case SDLK_DOWN: mousepos[1] += 4;mousepos[1]%= winSizeY; break;
-			case SDLK_LEFT: mousepos[0] -= 4;mousepos[0]%= winSizeX; break;
-			case SDLK_RIGHT: mousepos[0] += 4;mousepos[0]%= winSizeX; break;
+			case SDLK_UP:   dirbstates[0] = 1; break;
+			case SDLK_DOWN: dirbstates[1] = 1; break;
+			case SDLK_LEFT: dirbstates[2] = 1; break;
+			case SDLK_RIGHT:dirbstates[3] = 1; break;
 			case SDLK_SPACE: case SDLK_RETURN:
 				mb = 1;
 			break;
@@ -190,14 +217,15 @@
 	case SDL_MOUSEBUTTONDOWN:
 		if(E_BUTTON==SDL_BUTTON_LEFT) mb = 1;
 		if(E_BUTTON==SDL_BUTTON_RIGHT) {
-			tbcoords = mouse_to_normal();
-			haveclicked = 0;
+			mb2 = 1;
 		}
 		break;
 	case SDL_MOUSEBUTTONUP:
-		if(E_BUTTON==SDL_BUTTON_LEFT) mb = 2;
+		if(E_BUTTON==SDL_BUTTON_LEFT) mb = 0;
+		if(E_BUTTON==SDL_BUTTON_RIGHT) mb2 = 0;
 		break;
 	case SDL_MOUSEMOTION:
+		using_cursorkeys = 0;
 		mousepos[0] = E_MOTION.x;
 		mousepos[1] = E_MOTION.y;
 	break;
@@ -312,9 +340,13 @@
 		tNow = SDL_GetTicks();
 		// do event handling:
 		SDL_Event ev;
-		mb = 0; //Very important
 		while (SDL_PollEvent(&ev)) events(&ev);
-
+		if(using_cursorkeys)
+			omg_update_keycursor(dirbstates[0], dirbstates[1], dirbstates[2], dirbstates[3], mb);
+		else{
+			vec3 r = mouse_to_normal(mousepos[0], mousepos[1]);
+			omg_update_mcursor(r.d[0], r.d[1], mb);
+		}
 		// draw scene:
 		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 		//This is where we render our GUI!