#define GL_GLEXT_PROTOTYPES
#define _USE_MATH_DEFINES
#include <uthash.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#if defined(_WIN32)
#if defined(__GNUC__)
#include <cpuid.h>
#elif defined(_MSC_VER)
#include <intrin.h>
#endif
//#define APIENTRY __stdcall
#include <windows.h>
#include <errno.h>
#define GLEW_STATIC
#include <GL/glew.h>
//#include "glew/glew.h"
//#include <GL/gl.h>
//#include <GLES2/gl2.h>
//#include <GLES2/gl2ext.h>
//#define GLFW_INCLUDE_ES3
#define GLFW_EXPOSE_NATIVE_WGL
#define GLFW_EXPOSE_NATIVE_WIN32
#else
#include <GL/gl.h>
//#include <GL/glext.h>
#define GLFW_EXPOSE_NATIVE_X11
#define GLFW_EXPOSE_NATIVE_GLX
#endif
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>
#include "linmathv2.h"
//#include "geometry.h"
#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846264338327950288
#endif
#define degToRad(angleInDegrees) ((angleInDegrees) * M_PI / 180.0)
#define radToDeg(angleInRadians) ((angleInRadians) * 180.0 / M_PI)
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "custom.h"
#include "shaders.h"
#include "input.h"
#include "text.h"
#define nullptr ((void*)0)
#define vstr(s) str(s)
#define str(s) #s
float avgfps=0.0f;
float avgupdates=0.0f;
float avgfixed=0.0f;
char *fpstext;
char *timertext;
int firstTextRun = 0;
unsigned int TXT_WIDTH = 1920;
unsigned int TXT_HEIGHT = 1080; // values calculated on start
float txt_quality = 2.0;
//unsigned int textFB, textFBsample, textFBbuffer, textTexture;
unsigned int textFB, textFBsample, textTexture;
unsigned int cft_state = 0;
int CreateFrameText() {
if(cft_state == 1) { // fu
glDeleteFramebuffers(1, &textFB);
glDeleteTextures(1, &textFBsample);
//glDeleteRenderbuffers(1, &textFBbuffer);
//cft_state = 0;
//printf("deleting old custom fbo--text\n");
}
cft_state = 1;
//printf("setting up new custom buffer--text\n");
glGenFramebuffers(1, &textFB);
glBindFramebuffer(GL_FRAMEBUFFER, textFB); //bind both read/write to the target framebuffer
glGenTextures(1, &textFBsample); // texture object
glBindTexture(GL_TEXTURE_2D, textFBsample);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TXT_WIDTH, TXT_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
//glGenRenderbuffers(1, &textFBbuffer); // renderbuffer object
//glBindRenderbuffer(GL_RENDERBUFFER, textFBbuffer);
//glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT,SCR_WIDTH, SCR_HEIGHT);
//glBindRenderbuffer(GL_RENDERBUFFER, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textFBsample, 0);
//glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, textFBbuffer);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
printf("error on setup of custom buffer--text\n");
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
//glBindTexture(GL_TEXTURE_2D, 0);
return 0;
}
unsigned int uiFB, uiFBsample;//, uiFBdepth;
unsigned int cfu_state = 0;
int CreateFrameUI() {
if(cfu_state == 1) { // fu
glDeleteFramebuffers(1, &uiFB);
glDeleteTextures(1, &uiFBsample);
//glDeleteTextures(1, &uiFBdepth); // reset next run
//glDeleteRenderbuffers(1, &uiFBdepth); // reset next run
//cfu_state = 0;
//printf("deletingo old custom fbo--waterdepth\n");
}
cfu_state = 1;
//printf("setting up new custom fbo--waterdepth\n");
glGenFramebuffers(1, &uiFB);
glBindFramebuffer(GL_FRAMEBUFFER, uiFB); //bind both read/write to the target framebuffer
glGenTextures(1, &uiFBsample); // texture object
glBindTexture(GL_TEXTURE_2D, uiFBsample);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap
glBindTexture(GL_TEXTURE_2D, 0);
//--glGenTextures(1, &uiFBdepth); // texture object can access in shader
//--glBindTexture(GL_TEXTURE_2D, uiFBdepth);
//glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, SCR_WIDTH, SCR_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
//glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH_COMPONENT32F, SCR_WIDTH, SCR_HEIGHT);
//--glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH_COMPONENT24, SCR_WIDTH, SCR_HEIGHT);
//glBindTexture(GL_TEXTURE_2D, 0);
//glGenRenderbuffers(1, &uiFBdepth); // renderbuffer object cannot access in shader
//glBindRenderbuffer(GL_RENDERBUFFER, uiFBdepth);
//glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT,SCR_WIDTH, SCR_HEIGHT);
//glBindRenderbuffer(GL_RENDERBUFFER, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, uiFBsample, 0);
//
//--glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, uiFBdepth, 0);
//glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, uiFBdepth);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
printf("error on setup of buffer--ui\n");
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
//glBindTexture(GL_TEXTURE_2D, 0);
return 0;
}
struct GCharacter *pair = NULL;
void add_glyph(struct GCharacter *s) { HASH_ADD_INT( pair, ch, s ); }
struct GCharacter *find_glyph(int pair_id) {
struct GCharacter *s;
HASH_FIND_INT( pair, &pair_id, s );
return s;
}
void delete_glyph(struct GCharacter *s) { HASH_DEL( pair, s); }
struct Sentences *sts = NULL;
void add_sts(struct Sentences *s) { HASH_ADD_INT( sts, i, s ); }
struct Sentences *find_sts(int sts_id) {
struct Sentences *s;
HASH_FIND_INT( sts, &sts_id, s );
return s;
}
void delete_sts(struct Sentences *s) { HASH_DEL( sts, s); free(s); }
struct SentenceUpdates *uts = NULL;
void add_uts(struct SentenceUpdates *s) { HASH_ADD_INT( uts, i, s ); }
struct SentenceUpdates *find_uts(int uts_id) {
struct SentenceUpdates *s;
HASH_FIND_INT( uts, &uts_id, s );
return s;
}
void delete_uts(struct SentenceUpdates *s) { HASH_DEL( uts, s); free(s); }
float txtLineHeight=0.0f;
unsigned int VAO, VBO; // used for glyph and sentence quads
unsigned int highestTidx = 0;
int rerenderText=0;
unsigned int highestGidx = 0;
const struct Align Align = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// 0 = defaut bottom left corner
// 1 = top left
// 3 = bottom right
// 3 = top right
//
// 4 = world
//
// 5 = center
// 6 = middle left
// 7 = middle right
// 8 = middle top
// 9 = middle bottom
int UpdateText(unsigned int tidx, struct Vec3 *pos, struct Vec3 *rot, float scale, int align) {
SentenceUpdates *uts;
if((uts = find_uts(tidx))) {
//printf("delete old uts for tidx: %d, traf->pos->x: %f, traf->pos->y: %f\n",tidx, uts->transform->position->x, uts->transform->position->y);
free(uts->transform->position);
free(uts->transform->rotation);
free(uts->transform->scale);
free(uts->transform);
delete_uts(uts);
}
//if((uts = find_uts(tidx))) {
uts = malloc(sizeof(SentenceUpdates));
uts->transform = malloc(sizeof(Transform));
uts->transform->position = malloc(sizeof(Vec3));
uts->transform->rotation = malloc(sizeof(Vec3));
uts->transform->scale = malloc(sizeof(Vec3));
uts->transform->scale->x = scale;
uts->transform->scale->y = scale;
uts->transform->scale->z = scale;
uts->transform->rotation->x = rot->x;
uts->transform->rotation->y = rot->y;
uts->transform->rotation->z = rot->z;
uts->transform->position->x = pos->x;
uts->transform->position->y = pos->y;
uts->transform->position->z = pos->z;
uts->alignment = align;
uts->i = tidx;
uts->refresh = 1;
add_uts(uts);
return 0;
}
//float scrolltestY=0.0f;
int RenderText(unsigned int tidx, struct Vec3 *pos, struct Vec3 *rot, float scale, int align, char* text)
{
bool found=false;
int refresh=0;
SentenceUpdates *uts;
if((uts = find_uts(tidx))) {
refresh = uts->refresh;
found=true;
}
Sentences *sts;
if(!(sts = find_sts(tidx)) || rerenderText==1 || refresh==1) {
bool updateOnly=false;
bool prevExists=false;
float lastupdate = 0.0f;
if(sts != NULL) { // rerender(all) or refresh(specific)
//lastupdate = sts->lastupdate / (1.0f/60.0f);
lastupdate = updateCurrentTime - sts->lastupdate;
if(rerenderText!=1 && (lastupdate < 1.0f/12.0f)) { // 12 fps max
//if(rerenderText!=1 && (currentTime - sts->lastupdate < 0.1f)) {
return 0; // do not refresh auto refreshing text before 1 sec interval, only if when also rerenderText happens/resize etc
}
prevExists=true;
}
if(found && prevExists) { // rerender(all) or refresh(specific)
updateOnly=true; // or update ?
// existing refresh with UpdateText
scale = uts->transform->scale->x;
scale = uts->transform->scale->y;
scale = uts->transform->scale->z;
rot->x = uts->transform->rotation->x;
rot->y = uts->transform->rotation->y;
rot->z = uts->transform->rotation->z;
pos->x = uts->transform->position->x;
pos->y = uts->transform->position->y;
pos->z = uts->transform->position->z;
align = uts->alignment;
}
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // background framebuffer
//glClearColor(0.0f, 0.0f, 1.0f, 1.0f); // background framebuffer
glClear(GL_COLOR_BUFFER_BIT);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//glDrawBuffer(GL_COLOR_ATTACHMENT0); // _FRONT _BACK ?
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
float x = pos->x;
float y = pos->y;
//float z = pos->z;
if(align != 4) { // not world
pos->x = ((x/100.0f)-.5f) * 2.0f; // input is percentage 0-100 left-right
pos->y = (1 - (y/100.0f)-.5f) * 2.0f; // percentage 0-100 bottom to top
}
float wclip = 0.0f;
float hclip = 0.0f;
if(tidx > highestTidx) {
highestTidx=tidx;
}
float total_width = 0.0f;
float total_height = 0.0f;
int safeW = 0;
int safeH = 0;
float firstX=-2.0;
// ftmpY = quality derived~
// quality ftmpY
// 1 1
// 1.5 2
// 2 3
// 2.5 4
// 3 5
// 3.5 6
// 4 7
// 4.5 8
// 5 9
// 5.5 10
//unsigned int lowerQ = floor(txt_quality)-1;
//unsigned int upperQ = ceil(txt_quality);
//float ftmpY=upperQ+lowerQ; // top edge from -1, 0, 1, 2, ..
float ftmpY=0.97f; // coord scale -2 -3, start 1 for writing?
float ftmpX=-0.998f; // left
//float xclip = ((ftmpX + 1.0f) * 0.5f) * SCR_WIDTH; //to pixel
//float yclip = ((ftmpY + 1.0f) * 0.5f) * SCR_HEIGHT; //to pixel
float xclip = 0.0f;
float yclip = TXT_HEIGHT; // bottom
float tmpY = ftmpY; // top
float tmpX = ftmpX; // left
float fW=0.0f;
float fH=0.0f;
// render each char then clip
size_t length = strlen(text)+1; // +1 for null terminator
char *string = malloc(length);
memcpy(string, text, length);
float subscale = 0.001f; // subscale turn 48 px into 0.048 in screen ortho range -1 to 1 size
//float src_c=sqrt(pow(SCR_WIDTH,2)+pow(SCR_HEIGHT,2));
//float subscale = 10.0f/src_c;
for (size_t i = 0; i < length; i++)
{
char c = string[i];
//int ascii_value = getAsciiValue(c);
GCharacter *glyph;
if((glyph = find_glyph(c))) {
float bearingY = glyph->Bearing->y * subscale;
float bearingX = glyph->Bearing->x * subscale;
float sizeY = glyph->Size->y * subscale;
float sizeX = glyph->Size->x * subscale;
float NP2X = glyph->Size->np2x * subscale;
float NP2Y = glyph->Size->np2y * subscale;
float h = sizeY; // glyph normal
float w = sizeX;
hclip = h * TXT_HEIGHT; // glyph pixel
wclip = w * TXT_WIDTH;
//float a = w/h;
//wclip = hclip * a;
//--total_width += wclip;
if(tidx==0) {
if(hclip > total_height) { // currently only 1 line
total_height = hclip;
if(tmpY==ftmpY) { // first set increment
//tmpY = scrolltestY;
tmpY -= (total_height / TXT_HEIGHT);
//tmpY -= (sizeY + bearingY);
//tmpY = 1.0f;
}
}
} else {
total_height=txtLineHeight;
if(tmpY==ftmpY) { // first set increment
//tmpY = scrolltestY;
//tmpY -= (total_height * subscale) * (512.0f/SCR_WIDTH);
tmpY -= (total_height / TXT_HEIGHT);
//tmpY -= (total_height * subscale);
//tmpY -= (sizeY + bearingY);
//tmpY = 1.0f;
}
}
float xpos = (tmpX + bearingX);
float ypos = (tmpY - (sizeY - bearingY));
if(firstX==-2.0) {
firstX=xpos;
}
// refit uv to texture pow2 frame
float u = sizeX/NP2X;
float v = sizeY/NP2Y;
float vmin=v;
float vmax=0.0f;
// framing / full width normalized -1to1=2 *.5=0-1
fW = (1.0f - ((firstX + 1.0f) - xpos)) * 0.5f; // normal width
fH = (ftmpY - ypos); // normal width
//printf("fW==%f\n",fW);
// update VBO for each character
float textVertices[6][5] = {
{ xpos, ypos + h, 0.0f, 0.0f, vmax },
{ xpos, ypos, 0.0f, 0.0f, vmin },
{ xpos + w, ypos, 0.0f, u, vmin },
{ xpos, ypos + h, 0.0f, 0.0f, vmax },
{ xpos + w, ypos, 0.0f, u, vmin },
{ xpos + w, ypos + h, 0.0f, u, vmax }
};
glBindTexture(GL_TEXTURE_2D, glyph->TextureID);
//glActiveTexture(GL_TEXTURE0);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(textVertices), textVertices);
//render quad
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// now advance cursors for next glyph (note that advance is number of 1/64 pixels)
//x += ((glyph->Advance) >> 6) * subscale * scale; // bitshift by 6 to get value in pixels (2^6 = 64)
float adv = ((glyph->Advance) >> 6) * subscale;
tmpX += adv; // bitshift by 6 to get value in pixels (2^6 = 64)
//y -= 10.0f * subscale;
//
//if(spamonce<100) {
// printf("char '%c' texid: %d, xpos: %f, ypos: %f, w: %f, h: %f, adv: %f\n", c, glyph->TextureID, xpos, ypos, w, h, adv);
//spamonce++;
//}
}
}
free(string);
glBindTexture( GL_TEXTURE_2D, 0);
total_width = fW * TXT_WIDTH;
total_width += wclip;
if(tidx==0) { // initial test render
//total_height = fH / subscale;
//total_height = fH * SCR_HEIGHT;
total_height = fH * TXT_HEIGHT;
printf("fW=%f,fH=%f, total_width=%f, total_height=%f\n",fW,fH,total_width,total_height);
}
//safeW = (int)ceil(TXT_WIDTH);
//safeH = (int)ceil(TXT_HEIGHT);
//yclip = 0; // 800 - y - 1
//yclip = (int)TXT_HEIGHT/2; // 800 - y - 1
total_height+=6; // final padding adjustments
safeW = (int)ceil(total_width);
safeH = (int)ceil(total_height);
yclip-=(float)safeH-6; // final padding adjustments
//scrolltestY+=0.025f; // tests
//if(scrolltestY>txt_quality) scrolltestY=-1.0f;
unsigned int np2w = to_nearest_pow2(safeW);
unsigned int np2h = to_nearest_pow2(safeH);
//if(fullScreen==1) {
// printf("TXT_WIDTH=%d, TXT_HEIGHT=%d, glReadPixel(%f,%f,%d,%d)=>%d,%d,ftmpY=%f\n",TXT_WIDTH,TXT_HEIGHT,xclip,yclip,safeW,safeH,np2w,np2h,ftmpY);
//}
int CHANNEL_NUM=4;
//unsigned char *data = calloc(1,sizeof(unsigned char)*safeW*safeH*CHANNEL_NUM); // calloc to zero out memory
unsigned char *data = calloc(safeW*safeH*CHANNEL_NUM,sizeof(unsigned char)); // calloc to zero out memory
//--if(data) {
//-- printf("data initialized OK\n");
//--} else {
//-- printf("ERROR data not initialized!\n");
//--}
//--printf("point=%p , empty data='%s'\n", &data, data);
//glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
//--
//glCopyTexSubImage2D(textFBsample, 0, 0, 0, xclip, yclip, safeW, safeH);
glReadBuffer(GL_COLOR_ATTACHMENT0); // _FRONT _BACK ?
//glReadBuffer(GL_FRONT); // _FRONT _BACK ?
//glReadPixels(0, 0, SCR_WIDTH, SCR_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, -sts->data );
glReadPixels(xclip, yclip, safeW, safeH, GL_RGBA, GL_UNSIGNED_BYTE, data);
if(prevExists) {
if(np2w != sts->np2w || np2h != sts->np2h || rerenderText==1) {
updateOnly=false; // force new
glDeleteTextures(1,&sts->textureid);
}
}
if(!updateOnly || rerenderText==1) {
if(prevExists) {
free(sts->transform->position);
free(sts->transform->rotation);
free(sts->transform->scale);
free(sts->transform);
//printf("--delete old sts->tidx=%d, (old np2W=%d, o_np2H=%d), (new np2W=%d, np2H=%d)\n",tidx, sts->np2w, sts->np2h, np2w, np2h);
delete_sts(sts);
}
sts = malloc(sizeof(Sentences));
sts->transform = malloc(sizeof(Transform));
sts->transform->position = malloc(sizeof(Vec3));
sts->transform->rotation = malloc(sizeof(Vec3));
sts->transform->scale = malloc(sizeof(Vec3));
sts->i = tidx;
// xclip,yclip discarded after this stage
//glGenTextures(1, &textTexture);
//glBindTexture(GL_TEXTURE_2D, textTexture);
glGenTextures(1, &sts->textureid);
glBindTexture(GL_TEXTURE_2D, sts->textureid);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(
GL_TEXTURE_2D, // target
0, // level
GL_RGBA8, // internal format
np2w, // width
np2h, // height
//SCR_WIDTH, // width
//SCR_HEIGHT, // height
0, // number of layers?1 zero required?0
GL_RGBA, // format
GL_UNSIGNED_BYTE, // type
NULL // zero/memory
);
//--glBindTexture( GL_TEXTURE_2D, 0);
} else {
glBindTexture(GL_TEXTURE_2D, sts->textureid);
}
// now we know both sizes and position original
// include alignment to recompute coordinates for
// ortho screen, skip 4 (world):pos->x etc
float SW=SCR_WIDTH;
float SH=SCR_HEIGHT;
float SW2=SW/2;
float SH2=SH/2;
float W = total_width/4;
float H = total_height/2;
float W2 = W/2;
float H2 = H/2;
// redo scale step from above with alignment
pos->x = x;
pos->y = y;
switch(align) {
case 1: // top left
pos->y = SH - pos->y - H;
break;
case 2: // bottom right
pos->x = SW - pos->x - W;
break;
case 3: // top right
pos->x = SW - pos->x - W;
pos->y = SH - pos->y - H;
break;
case 5: // center
pos->x = SW2 - pos->x - W2;
pos->y = SH2 - pos->y - H2;
break;
case 6: // middle left
pos->y = SH2 - pos->y - H2;
break;
case 7: // middle right
pos->x = SW - pos->x - W;
pos->y = SH2 - pos->y - H2;
break;
case 8: // middle top
pos->x = SW2 - pos->x - W2;
pos->y = SH - pos->y - H;
break;
case 9: // middle bottom
pos->x = SW2 - pos->x - W2;
break;
}
if(align!=4) { // skip world
//pos->x = ((pos->x/100.0f)-.5f) * 2.0f; // input is percentage 0-100 left-right
//pos->y = (1 - (pos->y/100.0f)-.5f) * 2.0f; // percentage 0-100 bottom to top
// use pixel input coord instead of percentage
pos->x = ((pos->x/SCR_WIDTH)-.5f) * 2.0f; // input is percentage 0-100 left-right
pos->y = ((pos->y/SCR_HEIGHT)-.5f) * 2.0f; // percentage 0-100 bottom to top
}
//printf("idx: %d, align=%d, pos->x=%f, pos->y=%f, W=%f, H=%f\n",tidx,align,pos->x,pos->y,W,H);
sts->transform->scale->x = scale;
sts->transform->scale->y = scale;
sts->transform->scale->z = scale;
sts->transform->rotation->x = rot->x;
sts->transform->rotation->y = rot->y;
sts->transform->rotation->z = rot->z;
sts->transform->position->x = pos->x;
sts->transform->position->y = pos->y;
sts->transform->position->z = pos->z;
//int diffH=(int)np2h-safeH;
//int diffH=((int)np2h-safeH)/2;
int diffH=0;
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, diffH, safeW, safeH, GL_RGBA, GL_UNSIGNED_BYTE, data);
//--
//glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
//glPixelStorei(GL_PACK_ALIGNMENT, 4);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
sts->pxW = safeW;
sts->pxH = safeH;
sts->np2w = np2w;
sts->np2h = np2h;
sts->alignment = align;
sts->lastupdate = updateCurrentTime;
//--printf(" %f] Sentence tidx: %d, texid: %d, width: %d(%d), height: %d(%d), xclip: %f, yclip: %f, x(out): %f, y(out): %f, data: '%s', bits: ", currentTime, tidx, sts->textureid, safeW, np2w, safeH, np2h, xclip, yclip, sts->transform->position->x, sts->transform->position->y, data); // data empty
// save sentence id
//if(!prevExists && tidx!=0) { // 0 = early test only
if(!updateOnly && tidx!=0) { // 0 = early test only
add_sts(sts); // uthash
//printf(">>>>adding sts tidx = %d\n",tidx);
} else if(tidx==0) {
txtLineHeight=total_height;
//printf("set new line height\n");
}
if(found) {
free(uts->transform->position);
free(uts->transform->rotation);
free(uts->transform->scale);
free(uts->transform);
delete_uts(uts);
}
free(data);
glBlendFunc(GL_ONE, GL_ZERO);
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
}
return 0;
}
int PrepareAllText() {
// render text/hud orto mvp
glUseProgram(0);
glUseProgram(programText);
setRenderModeOrthographic();
execRenderModeOrthographic(v2);
setUniform4m(&programText, "MVP", mvp);
setUniform4m(&programText, "projection", uVp);
setUniform1i(&programText, "text", 0);
//glPixelStorei(GL_PACK_ALIGNMENT, 1); // to opengl
//glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // to gpu
//--glBindFramebuffer(GL_FRAMEBUFFER, textFB);
//glBindRenderbuffer(GL_RENDERBUFFER, textFBbuffer);
//uVp is 'projection * view' on the CPU sid
// after render lines also within framebuffer msaaFB
glBindVertexArray(VAO);
struct Vec3 *pos = malloc(sizeof(struct Vec3));
struct Vec3 *rot = malloc(sizeof(struct Vec3));
pos->x=0.0f; pos->y=0.0f; pos->z=0.0f;
rot->x=0.0f; rot->y=0.0f; rot->z=0.0f;
// do a pre-test string ABCQbcdgjlpqty0129 to find line-height?
// width symbol: !*"'`,@#$|/^AQbdgjlpqty0
if(firstTextRun==0 || rerenderText==1) { // sample test first run txtLineHeight
firstTextRun=1;
pos->x=0.0f; pos->y=0.0f;
setUniform3f(&programText, "textColor", 0.3f, 0.7f, 0.9f);
RenderText(0, pos, rot, 1.0f, Align.topleft, "!*\"'`,@#$|/^AGQbdgjlpqty0");
}
// defined 2d textx, updatetext is secondary
// rotation not included in 2d
pos->x=0.0f; pos->y=0.0f;
setUniform3f(&programText, "textColor", 0.0f, 0.8f, 0.2f);
RenderText(1, pos, rot, 1.0f, Align.topleft, fpstext);
pos->x=0.0f; pos->y=0.0f;
setUniform3f(&programText, "textColor", 0.0f, 0.8f, 0.2f);
RenderText(2, pos, rot, 1.0f, Align.topright, timertext);
pos->x=0.0f; pos->y=0.0f;
setUniform3f(&programText, "textColor", 1.0f, 0.2f, 0.0f);
RenderText(3, pos, rot, 1.0f, Align.bottomright, "awv");
// defined 3d textx, updatetext works the same
// for 3d text full range posiion and rotation
pos->x=-10.0f; pos->y=-2.0f; pos->z=0.0f;
rot->x=0.0f; rot->y=0.0f; rot->z=0.0f;
setUniform3f(&programText, "textColor", 0.8f, 0.0f, 1.0f);
RenderText(4, pos, rot, 1.0f, Align.world, "hello world!");
pos->x=10.0f; pos->y=2.0f; pos->z=0.0f;
rot->x=0.0f; rot->y=0.0f; rot->z=0.0f;
setUniform3f(&programText, "textColor", 0.0f, 0.8f, 0.4f);
RenderText(5, pos, rot, 10.0f, Align.world, "Big Test");
// free mallocs used by all texts when done
free(pos);
free(rot);
glBindVertexArray(0);
rerenderText=0; // resize etc rerender all
return 0;
}
int RenderAllText2D(int depth) {
for(unsigned int i = 0; i <= highestTidx; i++) {
Sentences *sts;
// render text/hud orto mvp
glUseProgram(0);
if(depth == -1) {
// custom stencil depth
glUseProgram(programStencil);
} else {
glUseProgram(programUI);
}
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
if(depth==-1) {
glDepthMask(GL_FALSE); // enable/disable writing+test
} else {
glDepthMask(GL_FALSE); // enable/disable writing+test
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
setRenderModeOrthographic();
execRenderModeOrthographic(v2);
setUniform1i(&programUI, "sampler", 0);
setUniform1f(&programUI, "alp", 1.0f);
setUniform1f(&programUI, "ris", .5f);
setUniform4m(&programUI, "MVP", mvp);
setUniform4m(&programUI, "projection", uVp);
glBindVertexArray(VAO);
if((sts = find_sts(i))) {
if(sts->alignment != 4) { // not world
float xpos = sts->transform->position->x;
float ypos = sts->transform->position->y;
float scale=sts->transform->scale->x;
float a=sts->pxW/sts->pxH; // safeH yclip
// screen scale
float h = (sts->pxH/SCR_HEIGHT)*scale;
//float w = (sts->pxW/TXT_WIDTH)*2.0f*scale;
//float w = h * a * .5 * .5;
float w = (h / ratio) * a * 0.5f;
// refit uv to texture pow2 frame
float u = sts->pxW/sts->np2w;
float v = sts->pxH/sts->np2h;
float vmax=v;
//float vmax=1.0f;
float vmin=0.0f;
float sentenceVertices[6][5] = {
{ xpos, ypos + h, 0.0f, 0.0f, vmax },
{ xpos, ypos, 0.0f, 0.0f, vmin },
{ xpos + w, ypos, 0.0f, u, vmin },
{ xpos, ypos + h, 0.0f, 0.0f, vmax },
{ xpos + w, ypos, 0.0f, u, vmin },
{ xpos + w, ypos + h, 0.0f, u, vmax }
};
//glActiveTexture(GL_TEXTURE0);
//glBindTexture(GL_TEXTURE_2D, texture1);
glBindTexture(GL_TEXTURE_2D, sts->textureid);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(sentenceVertices), sentenceVertices);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//if(spamonce<5) {
// printf("spamonce2D drawing sts=%d, texid=%d; xpos:%f, ypos:%f, w:%f, h:%f, u:%f, v:%f, vmin: %f, vmax: %f, align=%d\n",i,sts->textureid,xpos,ypos,w,h,u,v,vmin,vmax,sts->alignment);
// spamonce++;
//}
glBindVertexArray(0);
RenderBoundBox(xpos,ypos+h,0.0f,w,h,0.0f,0,mvp);
} // skip world render here
}
glBlendFunc(GL_ONE, GL_ZERO);
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
}
return 0;
}
int RenderAllText3D(int depth) {
for(unsigned int i = 0; i <= highestTidx; i++) {
Sentences *sts;
glUseProgram(0);
// render text/hud orto mvp
if(depth == -1) {
// custom stencil depth
glUseProgram(programStencil);
} else {
glUseProgram(programT3D);
}
glDisable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glDepthFunc(GL_LEQUAL);
//if(stencil==1) {
//glEnable(GL_STENCIL_TEST);
//glStencilFunc(GL_ALWAYS, 1, 0xFF);
//glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
//glStencilMask(0xFF); // test and write stencil
//}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//setRenderModeOrthographic();
//execRenderModeOrthographic(v2);
setRenderModePerspective();
execRenderModePerspective(v);
if(depth == -1) {
setUniform4m(&programStencil, "MVP", mvp);
setUniform4m(&programStencil, "projection", uVp);
} else {
//setUniform1i(&programT3D, "sampler", 0);
setUniform4m(&programT3D, "MVP", mvp);
setUniform4m(&programT3D, "projection", uVp);
setUniform1f(&programT3D, "alp", 0.75f);
setUniform1f(&programT3D, "ris", 0.25f);
}
glBindVertexArray(VAO);
if((sts = find_sts(i))) {
if(sts->alignment == 4) { // world only
float xpos = sts->transform->position->x;
float ypos = sts->transform->position->y;
float zpos = sts->transform->position->z; // depth only
float scale=sts->transform->scale->x;
float a=sts->pxW/sts->pxH; // safeH yclip
// world scale
float h = (1.0f)*scale;
//float w = (sts->pxW*0.01f)*scale;
//float w = (h / ratio) * a * 0.5f;
float w = (h / ratio) * a;
// refit uv to texture pow2 frame
float u = sts->pxW/sts->np2w;
float v = sts->pxH/sts->np2h;
float vmin=v;
//float vmin=1.0f;
float vmax=0.0f;
float sentenceVertices[6][5] = { // added z?
{ xpos, ypos + h, zpos, 0.0f, vmin },
{ xpos, ypos, zpos, 0.0f, vmax },
{ xpos + w, ypos, zpos, u, vmax },
{ xpos, ypos + h, zpos, 0.0f, vmin },
{ xpos + w, ypos, zpos, u, vmax },
{ xpos + w, ypos + h, zpos, u, vmin }
};
//glActiveTexture(GL_TEXTURE0);
//glBindTexture(GL_TEXTURE_2D, texture1);
glBindTexture(GL_TEXTURE_2D, sts->textureid);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(sentenceVertices), sentenceVertices);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//if(spamonce<5) {
// printf("spamonce3D drawing sts=%d, texid=%d; xpos:%f, ypos:%f, zpos:%f, w:%f, h:%f, u:%f, v:%f, align=%d\n",i,sts->textureid,xpos,ypos,zpos,w,h,u,v,sts->alignment);
// spamonce++;
//}
glBindVertexArray(0);
RenderBoundBox(xpos,ypos+h,zpos,w,h,zpos,1,mvp);
}
}
glBlendFunc(GL_ONE, GL_ZERO);
glDisable(GL_BLEND);
//if(stencil==1) {
//glStencilMask(0xFF);
//glDisable(GL_STENCIL_TEST);
//}
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
}
return 0;
}
static const char *ftstrerror(__attribute__((unused)) FT_Error error)
{
#undef FTERRORS_H_
#define FT_ERRORDEF(error_code, value, string) case error_code: return string;
#define FT_ERROR_START_LIST switch(error) {
#define FT_ERROR_END_LIST default: return "Unknown error"; }
#include FT_ERRORS_H
return "eee";
}
int loadFont() {
//char* font_name = "data/fonts/OldStandard-Regular.ttf";
char *pathname=malloc(sizeof(char)*250);
if(fontIdx==0) {
sprintf(pathname,"data/fonts/%s","OldStandard-Regular.ttf");
} else if(fontIdx==1) {
sprintf(pathname,"data/fonts/%s","SourceSansPro-Regular.ttf");
} else if(fontIdx==2) {
sprintf(pathname,"data/fonts/%s","Vera.ttf");
} else if(fontIdx==3) {
sprintf(pathname,"data/fonts/%s","VeraMoBd.ttf");
} else if(fontIdx==4) {
sprintf(pathname,"data/fonts/%s","LuckiestGuy.ttf");
}
//
//printf("calling path: %s\n", argv[0]);
// FT init and new face return <Not 0> if error
// ifs below are true if <Not 0>
FT_Library ft;
if (FT_Init_FreeType(&ft))
{
printf("ERROR::FREETYPE: Could not init FreeType Library");
return -1;
}
FT_Face face;
if (FT_New_Face(ft, pathname, 0, &face))
{
printf("ERROR::FREETYPE: Failed to load font");
return -1;
}
// set size to load glyphs as
//FT_Set_Pixel_Sizes(face, 256, 256);
FT_Set_Pixel_Sizes(face, 0, 64);
//FT_Set_Char_Size(face, 16 * 64, 16 * 64, 72, 72);
// unicode enable for emoji? use wchat_t wchar = *it to index
//FT_Select_Charmap(face, FT_ENCODING_UNICODE);
// disable byte-alignment restriction
//glPixelStorei(GL_PACK_ALIGNMENT, 1); // to opengl
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // to gpu
//FT_GlyphSlot slot = face->glyph;
printf("Loading font glyph bitmap buffers\n");
printf("[_detail_load_redacted_]\n");
// load first 128 characters of ASCII set
//for (wchar_t c = 0; c < 1151; c++) {
for (unsigned char c = 32; c < 127; c++) { // skipping first 32 require glyph nullptr check in rendertext
// Load character glyph
//if (FT_Load_Glyph(face, c, FT_LOAD_DEFAULT)) {
if (FT_Load_Char(face, c, FT_LOAD_RENDER)) {
printf("ERROR::FREETYTPE: Failed to load Glyph\n");
continue;
}
int err;
//if((err = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_LCD))) { // rgb
//if((err = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_SDF))) {
if ((err = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL))) { // l
printf("err: %d, char: %c, dec: %d\n", err, (char)c, c);
//printf("Err code: %s\n",FT_Error_String(err));
printf("Err code: %s\n",ftstrerror(err));
continue;
}
FT_GlyphSlot g = face->glyph;
//int width = to_nearest_pow2( g->bitmap.width );
//int height = to_nearest_pow2( g->bitmap.rows );
int width = (int)g->bitmap.width;
int height = (int)g->bitmap.rows;
//
unsigned int np2w = to_nearest_pow2(width);
unsigned int np2h = to_nearest_pow2(height);
//printf("main: num_glyphs %u\n", (unsigned int) face->num_glyphs);
//printf("main: units_per_EM %d\n", face->units_per_EM);
//--printf("bitmap width: np2(%d) = %d, rows: np2(%d) = %d pitch %d, char=%c\n", g->bitmap.width, np2w, g->bitmap.rows, np2h, g->bitmap.pitch, (char)c);
//unsigned char *tdata; // pointer to pointer
//tdata = g->bitmap.buffer; // linked
//strcat((char *)tdata,"\0");
//size_t plen = sizeof(tdata);
//size_t slen = strlen((char *)tdata);
//printf("tdata plen : %ld, slen: %ld, buff: %p\n", plen, slen, tdata);
//--printf("-bufferdata: '%s'\n", g->bitmap.buffer);
//FILE *f1 = fopen("file.bmp", "wb");
//if(f1) {
// size_t f11 = fwrite(tdata, slen, sizeof(unsigned char), f1);
// printf("wrote %zu elements requested \"%s\"\n", f11, tdata);
// fclose(f1);
//}
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// set texture options
//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_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//GLint swizzleMask[] = {GL_RED, GL_RED, GL_RED, GL_RED};
//glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
//
glTexImage2D(
GL_TEXTURE_2D, // target
0, // level
GL_R8, // internal format
//GL_RGBA8, // internal format
//GL_LUMINANCE_ALPHA, // internal format
//width, // width
//height, // height
np2w, // width
np2h, // height
0, // number of layers?1 zero required?0
GL_RED, // format
//GL_LUMINANCE_ALPHA, // internal format
GL_UNSIGNED_BYTE, // type
//tdata // zero/memory
//g->bitmap.buffer // zero/memory
NULL // zero/memory
);
//printf("glyph buffer data %s\n", g->bitmap.buffer);
//int diffH=np2h-height;
int diffH=0;
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, diffH, width, height, GL_RED, GL_UNSIGNED_BYTE, g->bitmap.buffer);
//
//
//glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
// now store character for later use
GCharacter *glyph = malloc(sizeof(GCharacter));
Size *gsz = malloc(sizeof(Size));
Bearing *gbr = malloc(sizeof(Bearing));
glyph->Size = gsz;
glyph->Bearing = gbr;
highestGidx = c;
glyph->ch = c;
glyph->Size->x = width;
glyph->Size->y = height;
glyph->Size->np2x = np2w;
glyph->Size->np2y = np2h;
glyph->Bearing->x = g->bitmap_left;
glyph->Bearing->y = g->bitmap_top;
glyph->Advance = g->advance.x;
glyph->TextureID = texture;
//printf("loading char idx %d bitleft=%d\n",c,g->bitmap_left);
//printf("loading char idx %d advx=%ld\n",c,g->advance.x);
//Characters.insert(std::pair<char, Character>(c, character));
//mapInsert(&hash_table, (void)&i, (void*)glyph);
//HASH_ADD_INT(characters, id, characters);
//add_glyph(pair);
add_glyph(glyph);
//printf("sizeof = %d\n",(int)sizeof(/glyph));
}
// free up resources after done processing glyphs
FT_Done_Face(face);
FT_Done_FreeType(ft);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // to opengl
//glPixelStorei(GL_PACK_ALIGNMENT, 4); // to gpu
return 0;
}
int cleanupFonts(int reload) {
while(highestGidx>0) { // cleanup sentences
GCharacter *glyph;
if((glyph = find_glyph(highestGidx))) {
if(glyph != NULL) { // rerender(all) or refresh(specific)
glDeleteTextures(1,&glyph->TextureID);
free(glyph->Size);
free(glyph->Bearing);
delete_glyph(glyph);
}
}
highestGidx--;
}
if(reload==1) {
highestGidx=0;
loadFont();
rerenderText=1;
}
return 0;
}
int InitializeText() {
// text quad initialize
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 5, NULL, GL_DYNAMIC_DRAW); // no vertices, quad created later
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glUseProgram(0);
glUseProgram(programText);
setUniform1i(&programText, "text", 0);
loadFont();
return 0;
}
Top