mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-07-11 18:17:16 +00:00
856c838def
* In a previous update in 2021 many copyright ranges were truncated accidentally, and some files have been copy-pasted with wrong years. These dates have been fixed based on git history and original copyright messages.
581 lines
18 KiB
C++
581 lines
18 KiB
C++
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2018-2026 Baldur Karlsson
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
******************************************************************************/
|
|
|
|
#include "data/glsl_shaders.h"
|
|
#include "maths/matrix.h"
|
|
#include "maths/vec.h"
|
|
#include "stb/stb_truetype.h"
|
|
#include "strings/string_utils.h"
|
|
#include "gl_driver.h"
|
|
|
|
const float charPixelHeight = 20.0f;
|
|
|
|
#define FONT_FIRST_CHAR 32
|
|
#define FONT_LAST_CHAR 126
|
|
|
|
const int numChars = FONT_LAST_CHAR - FONT_FIRST_CHAR + 1;
|
|
|
|
stbtt_bakedchar chardata[numChars];
|
|
|
|
void WrappedOpenGL::ContextData::CreateDebugData()
|
|
{
|
|
// if these constants change, update glsl_ubos.h and gltext.vert
|
|
RDCCOMPILE_ASSERT(FONT_FIRST_CHAR == int(' '), "FONT_FIRST_CHAR is incorrect");
|
|
RDCCOMPILE_ASSERT(FONT_LAST_CHAR == 126, "FONT_LAST_CHAR is incorrect");
|
|
|
|
// to let us display the overlay on old GL contexts, use as simple a subset of functionality as
|
|
// possible to upload the texture. VAO and shaders are used optionally on modern contexts,
|
|
// otherwise we fall back to immediate mode rendering by hand
|
|
if(GL.glGetIntegerv && GL.glGenTextures && GL.glBindTexture && GL.glTexImage2D && GL.glTexParameteri)
|
|
{
|
|
rdcstr ttfstring = GetEmbeddedResource(sourcecodepro_ttf);
|
|
byte *ttfdata = (byte *)ttfstring.c_str();
|
|
|
|
byte *buf = new byte[FONT_TEX_WIDTH * FONT_TEX_HEIGHT];
|
|
|
|
stbtt_BakeFontBitmap(ttfdata, 0, charPixelHeight, buf, FONT_TEX_WIDTH, FONT_TEX_HEIGHT,
|
|
FONT_FIRST_CHAR + 1, numChars, chardata);
|
|
|
|
CharSize = charPixelHeight;
|
|
#if ENABLED(RDOC_ANDROID)
|
|
CharSize *= 2.0f;
|
|
#endif
|
|
CharAspect = chardata[0].xadvance / charPixelHeight;
|
|
|
|
stbtt_fontinfo f = {0};
|
|
stbtt_InitFont(&f, ttfdata, 0);
|
|
|
|
int ascent = 0;
|
|
stbtt_GetFontVMetrics(&f, &ascent, NULL, NULL);
|
|
|
|
float maxheight = float(ascent) * stbtt_ScaleForPixelHeight(&f, charPixelHeight);
|
|
|
|
{
|
|
PixelPackState pack;
|
|
PixelUnpackState unpack;
|
|
GLuint pixelPackBuffer = 0;
|
|
GLuint pixelUnpackBuffer = 0;
|
|
|
|
GL.glGetIntegerv(eGL_PIXEL_PACK_BUFFER_BINDING, (GLint *)&pixelPackBuffer);
|
|
GL.glGetIntegerv(eGL_PIXEL_UNPACK_BUFFER_BINDING, (GLint *)&pixelUnpackBuffer);
|
|
GL.glBindBuffer(eGL_PIXEL_PACK_BUFFER, 0);
|
|
GL.glBindBuffer(eGL_PIXEL_UNPACK_BUFFER, 0);
|
|
|
|
pack.Fetch(false);
|
|
unpack.Fetch(false);
|
|
|
|
ResetPixelPackState(false, 1);
|
|
ResetPixelUnpackState(false, 1);
|
|
|
|
GLenum oldActive = eGL_TEXTURE0;
|
|
GL.glGetIntegerv(eGL_ACTIVE_TEXTURE, (GLint *)&oldActive);
|
|
GL.glActiveTexture(eGL_TEXTURE0);
|
|
|
|
GLuint curtex = 0;
|
|
GL.glGetIntegerv(eGL_TEXTURE_BINDING_2D, (GLint *)&curtex);
|
|
|
|
GLenum texFmt = eGL_R8;
|
|
if(Legacy())
|
|
texFmt = eGL_LUMINANCE;
|
|
|
|
GL.glGenTextures(1, &GlyphTexture);
|
|
GL.glBindTexture(eGL_TEXTURE_2D, GlyphTexture);
|
|
GL.glTexImage2D(eGL_TEXTURE_2D, 0, texFmt, FONT_TEX_WIDTH, FONT_TEX_HEIGHT, 0, eGL_RED,
|
|
eGL_UNSIGNED_BYTE, buf);
|
|
GL.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MAX_LEVEL, 0);
|
|
GL.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MAG_FILTER, eGL_LINEAR);
|
|
GL.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MIN_FILTER, eGL_LINEAR);
|
|
|
|
GL.glBindTexture(eGL_TEXTURE_2D, curtex);
|
|
GL.glActiveTexture(oldActive);
|
|
|
|
pack.Apply(false);
|
|
unpack.Apply(false);
|
|
|
|
GL.glBindBuffer(eGL_PIXEL_PACK_BUFFER, pixelPackBuffer);
|
|
GL.glBindBuffer(eGL_PIXEL_UNPACK_BUFFER, pixelUnpackBuffer);
|
|
}
|
|
|
|
delete[] buf;
|
|
|
|
Vec4f posdata[numChars + 1] = {};
|
|
Vec4f uvdata[numChars + 1] = {};
|
|
|
|
// premultiply CharacterSize
|
|
const Vec4f uvscale =
|
|
Vec4f(1.0f / float(FONT_TEX_WIDTH), 1.0f / float(FONT_TEX_HEIGHT), 0.0f, 1.0f);
|
|
|
|
for(int i = 0; i < numChars; i++)
|
|
{
|
|
stbtt_bakedchar *b = chardata + i;
|
|
|
|
float x = b->xoff;
|
|
float y = b->yoff + maxheight;
|
|
|
|
posdata[i + 1] = Vec4f(x / b->xadvance, y / charPixelHeight, b->xadvance / float(b->x1 - b->x0),
|
|
charPixelHeight / float(b->y1 - b->y0));
|
|
uvdata[i + 1] =
|
|
Vec4f(b->x0 * uvscale.x, b->y0 * uvscale.y, b->x1 * uvscale.x, b->y1 * uvscale.y);
|
|
}
|
|
|
|
if(Modern())
|
|
{
|
|
if(GLCoreVersion > 20 && GL.glGenVertexArrays && GL.glBindVertexArray)
|
|
{
|
|
GLuint curvao = 0;
|
|
GL.glGetIntegerv(eGL_VERTEX_ARRAY_BINDING, (GLint *)&curvao);
|
|
|
|
GL.glGenVertexArrays(1, &DummyVAO);
|
|
GL.glBindVertexArray(DummyVAO);
|
|
|
|
GL.glBindVertexArray(curvao);
|
|
}
|
|
|
|
if(GL.glGenBuffers && GL.glBufferData && GL.glBindBuffer)
|
|
{
|
|
GLuint curbuf = 0;
|
|
GL.glGetIntegerv(eGL_ARRAY_BUFFER_BINDING, (GLint *)&curbuf);
|
|
|
|
GL.glGenBuffers(1, &ArrayBuffer);
|
|
GL.glBindBuffer(eGL_ARRAY_BUFFER, ArrayBuffer);
|
|
GL.glBufferData(eGL_ARRAY_BUFFER, sizeof(float) * 5 * FONT_MAX_CHARS * 6, NULL,
|
|
eGL_DYNAMIC_DRAW);
|
|
|
|
GL.glBindBuffer(eGL_ARRAY_BUFFER, curbuf);
|
|
}
|
|
|
|
if(GL.glCreateShader && GL.glShaderSource && GL.glCompileShader && GL.glGetShaderiv &&
|
|
GL.glGetShaderInfoLog && GL.glDeleteShader && GL.glCreateProgram && GL.glAttachShader &&
|
|
GL.glLinkProgram && GL.glGetProgramiv && GL.glGetProgramInfoLog)
|
|
{
|
|
rdcstr vs;
|
|
rdcstr fs;
|
|
|
|
ShaderType shaderType;
|
|
int glslVersion;
|
|
rdcstr vertDefines, fragDefines;
|
|
|
|
if(IsGLES)
|
|
{
|
|
shaderType = ShaderType::GLSLES;
|
|
glslVersion = 100;
|
|
fragDefines = "precision highp float;";
|
|
}
|
|
else
|
|
{
|
|
shaderType = ShaderType::GLSL;
|
|
glslVersion = 110;
|
|
|
|
#if ENABLED(RDOC_APPLE)
|
|
// on mac we need to define a more modern version and use modern texture sampling
|
|
glslVersion = GLCoreVersion * 10;
|
|
fragDefines =
|
|
"#define varying in\n"
|
|
"#define texture2D texture\n"
|
|
"#define gl_FragColor outcol\n"
|
|
"out vec4 outcol;";
|
|
vertDefines =
|
|
"#define varying out\n"
|
|
"#define attribute in";
|
|
#endif
|
|
}
|
|
|
|
vs = GenerateGLSLShader(GetEmbeddedResource(glsl_gltext_vert), shaderType, glslVersion,
|
|
vertDefines);
|
|
fs = GenerateGLSLShader(GetEmbeddedResource(glsl_gltext_frag), shaderType, glslVersion,
|
|
fragDefines);
|
|
|
|
GLuint vert = GL.glCreateShader(eGL_VERTEX_SHADER);
|
|
GLuint frag = GL.glCreateShader(eGL_FRAGMENT_SHADER);
|
|
|
|
const char *csrc = vs.c_str();
|
|
GL.glShaderSource(vert, 1, &csrc, NULL);
|
|
csrc = fs.c_str();
|
|
GL.glShaderSource(frag, 1, &csrc, NULL);
|
|
|
|
GL.glCompileShader(vert);
|
|
GL.glCompileShader(frag);
|
|
|
|
char buffer[1024] = {0};
|
|
GLint status = 0;
|
|
|
|
GL.glGetShaderiv(vert, eGL_COMPILE_STATUS, &status);
|
|
if(status == 0)
|
|
{
|
|
GL.glGetShaderInfoLog(vert, 1024, NULL, buffer);
|
|
RDCERR("Shader error: %s", buffer);
|
|
}
|
|
|
|
GL.glGetShaderiv(frag, eGL_COMPILE_STATUS, &status);
|
|
if(status == 0)
|
|
{
|
|
GL.glGetShaderInfoLog(frag, 1024, NULL, buffer);
|
|
RDCERR("Shader error: %s", buffer);
|
|
}
|
|
|
|
Program = GL.glCreateProgram();
|
|
|
|
GL.glAttachShader(Program, vert);
|
|
GL.glAttachShader(Program, frag);
|
|
|
|
GL.glBindAttribLocation(Program, 0, "pos");
|
|
GL.glBindAttribLocation(Program, 1, "uv");
|
|
GL.glBindAttribLocation(Program, 2, "charidx");
|
|
|
|
GL.glLinkProgram(Program);
|
|
|
|
GL.glGetProgramiv(Program, eGL_LINK_STATUS, &status);
|
|
if(status == 1)
|
|
{
|
|
GLuint prevProg = 0;
|
|
GL.glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint *)&prevProg);
|
|
|
|
GL.glUseProgram(Program);
|
|
|
|
// texture is from texture0
|
|
GL.glUniform1i(GL.glGetUniformLocation(Program, "font_tex"), 0);
|
|
|
|
// upload posdata and uvdata
|
|
GL.glUniform4fv(GL.glGetUniformLocation(Program, "posdata"), numChars, &posdata[0].x);
|
|
GL.glUniform4fv(GL.glGetUniformLocation(Program, "uvdata"), numChars, &uvdata[0].x);
|
|
|
|
GL.glUseProgram(prevProg);
|
|
}
|
|
else
|
|
{
|
|
GL.glGetProgramInfoLog(Program, 1024, NULL, buffer);
|
|
RDCERR("Link error: %s", buffer);
|
|
|
|
GL.glDeleteProgram(Program);
|
|
Program = 0;
|
|
}
|
|
|
|
GL.glDeleteShader(vert);
|
|
GL.glDeleteShader(frag);
|
|
|
|
ArrayMS.Create();
|
|
}
|
|
}
|
|
|
|
ready = true;
|
|
}
|
|
}
|
|
|
|
void WrappedOpenGL::RenderText(float x, float y, const rdcstr &text)
|
|
{
|
|
ContextData &ctxdata = GetCtxData();
|
|
|
|
GLPushPopState textState;
|
|
|
|
textState.Push(ctxdata.Modern());
|
|
|
|
rdcarray<rdcstr> lines;
|
|
split(text, lines, '\n');
|
|
|
|
for(const rdcstr &line : lines)
|
|
{
|
|
RenderTextInternal(x, y, line);
|
|
y += 1.0f;
|
|
}
|
|
|
|
textState.Pop(ctxdata.Modern());
|
|
}
|
|
|
|
void WrappedOpenGL::RenderTextInternal(float x, float y, const rdcstr &text)
|
|
{
|
|
if(text.empty())
|
|
return;
|
|
|
|
RDCASSERT(text.size() < FONT_MAX_CHARS);
|
|
|
|
ContextData &ctxdata = GetCtxData();
|
|
|
|
if(!ctxdata.built || !ctxdata.ready || (ctxdata.Modern() && ctxdata.Program == 0))
|
|
return;
|
|
|
|
// if it's reasonably modern context, assume we can use buffers and UBOs
|
|
if(ctxdata.Modern())
|
|
{
|
|
GL.glBindBuffer(eGL_ARRAY_BUFFER, ctxdata.ArrayBuffer);
|
|
|
|
size_t len = text.size();
|
|
|
|
if((int)len > FONT_MAX_CHARS)
|
|
{
|
|
static bool printedWarning = false;
|
|
|
|
// this could be called once a frame, don't want to spam the log
|
|
if(!printedWarning)
|
|
{
|
|
printedWarning = true;
|
|
RDCWARN("log string '%s' is too long", text.c_str(), (int)len);
|
|
}
|
|
|
|
len = FONT_MAX_CHARS;
|
|
}
|
|
|
|
static const Vec2f basePos[] = {
|
|
Vec2f(0.0, 0.0), Vec2f(1.0, 0.0), Vec2f(0.0, 1.0),
|
|
Vec2f(1.0, 0.0), Vec2f(0.0, 1.0), Vec2f(1.0, 1.0),
|
|
};
|
|
|
|
const Vec2f FontScreenAspect(ctxdata.CharAspect / RDCMAX(1.0f, float(ctxdata.initParams.width)),
|
|
1.0f / RDCMAX(1.0f, float(ctxdata.initParams.height)));
|
|
|
|
float *vertexData = new float[5 * len * 6];
|
|
|
|
for(size_t i = 0; i < len; i++)
|
|
{
|
|
for(int ch = 0; ch < 6; ch++)
|
|
{
|
|
Vec2f uv = basePos[ch];
|
|
|
|
Vec2f pos = uv;
|
|
pos.x += float(i) + x;
|
|
pos.y += y;
|
|
|
|
pos.x *= 2.0f * ctxdata.CharSize * FontScreenAspect.x;
|
|
pos.y *= 2.0f * ctxdata.CharSize * FontScreenAspect.y;
|
|
|
|
pos.x -= 1.0f;
|
|
pos.y -= 1.0f;
|
|
|
|
vertexData[(i * 6 + ch) * 5 + 0] = pos.x;
|
|
vertexData[(i * 6 + ch) * 5 + 1] = ctxdata.initParams.isYFlipped ? pos.y : -pos.y;
|
|
vertexData[(i * 6 + ch) * 5 + 2] = uv.x;
|
|
vertexData[(i * 6 + ch) * 5 + 3] = uv.y;
|
|
vertexData[(i * 6 + ch) * 5 + 4] = float(text[i] - FONT_FIRST_CHAR);
|
|
}
|
|
}
|
|
|
|
// we read 6 * len vec2 positions and 6 * len float characters
|
|
GL.glBufferSubData(eGL_ARRAY_BUFFER, 0, sizeof(float) * 5 * len * 6, vertexData);
|
|
|
|
delete[] vertexData;
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// Make sure if you change any other state in here, that you also update the push
|
|
// and pop functions in GLPushPopState
|
|
|
|
// set blend state
|
|
if(HasExt[ARB_draw_buffers_blend])
|
|
{
|
|
GL.glEnablei(eGL_BLEND, 0);
|
|
GL.glBlendFuncSeparatei(0, eGL_SRC_ALPHA, eGL_ONE_MINUS_SRC_ALPHA, eGL_SRC_ALPHA,
|
|
eGL_SRC_ALPHA);
|
|
GL.glBlendEquationSeparatei(0, eGL_FUNC_ADD, eGL_FUNC_ADD);
|
|
}
|
|
else
|
|
{
|
|
GL.glEnable(eGL_BLEND);
|
|
GL.glBlendFuncSeparate(eGL_SRC_ALPHA, eGL_ONE_MINUS_SRC_ALPHA, eGL_SRC_ALPHA, eGL_SRC_ALPHA);
|
|
GL.glBlendEquationSeparate(eGL_FUNC_ADD, eGL_FUNC_ADD);
|
|
}
|
|
|
|
if(HasExt[EXT_draw_buffers2] || HasExt[ARB_draw_buffers_blend])
|
|
{
|
|
GL.glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
}
|
|
else
|
|
{
|
|
GL.glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
}
|
|
|
|
// set depth & stencil
|
|
GL.glDisable(eGL_DEPTH_TEST);
|
|
if(!IsGLES)
|
|
GL.glDisable(eGL_DEPTH_CLAMP);
|
|
GL.glDisable(eGL_STENCIL_TEST);
|
|
GL.glDisable(eGL_CULL_FACE);
|
|
GL.glDisable(eGL_RASTERIZER_DISCARD);
|
|
if(HasExt[EXT_depth_bounds_test])
|
|
GL.glDisable(eGL_DEPTH_BOUNDS_TEST_EXT);
|
|
|
|
GL.glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, 0);
|
|
|
|
// set viewport & scissor
|
|
if(HasExt[ARB_viewport_array])
|
|
{
|
|
GL.glViewportIndexedf(0, 0.0f, 0.0f, (float)ctxdata.initParams.width,
|
|
(float)ctxdata.initParams.height);
|
|
GL.glDisablei(eGL_SCISSOR_TEST, 0);
|
|
}
|
|
else
|
|
{
|
|
GL.glViewport(0, 0, ctxdata.initParams.width, ctxdata.initParams.height);
|
|
GL.glDisable(eGL_SCISSOR_TEST);
|
|
}
|
|
|
|
if(!IsGLES)
|
|
GL.glPolygonMode(eGL_FRONT_AND_BACK, eGL_FILL);
|
|
|
|
if(GL.glClipControl && HasExt[ARB_clip_control])
|
|
GL.glClipControl(eGL_LOWER_LEFT, eGL_NEGATIVE_ONE_TO_ONE);
|
|
|
|
// bind VAO so we can fiddle with vertex attrib state
|
|
GL.glBindVertexArray(ctxdata.DummyVAO);
|
|
|
|
GLsizei stride = sizeof(float) * 5;
|
|
|
|
GL.glVertexAttribPointer(0, 2, eGL_FLOAT, false, stride, (void *)uintptr_t(sizeof(float) * 0));
|
|
GL.glVertexAttribPointer(1, 2, eGL_FLOAT, false, stride, (void *)uintptr_t(sizeof(float) * 2));
|
|
GL.glVertexAttribPointer(2, 1, eGL_FLOAT, false, stride, (void *)uintptr_t(sizeof(float) * 4));
|
|
GL.glEnableVertexAttribArray(0);
|
|
GL.glEnableVertexAttribArray(1);
|
|
GL.glEnableVertexAttribArray(2);
|
|
|
|
// bind textures
|
|
GL.glActiveTexture(eGL_TEXTURE0);
|
|
GL.glBindTexture(eGL_TEXTURE_2D, ctxdata.GlyphTexture);
|
|
|
|
// bind program
|
|
GL.glUseProgram(ctxdata.Program);
|
|
|
|
// draw string
|
|
GL.glDrawArrays(eGL_TRIANGLES, 0, 6 * (GLsizei)len);
|
|
}
|
|
else
|
|
{
|
|
// if it wasn't created in modern fashion with createattribs, assume the worst and draw with
|
|
// immediate mode (since it's impossible that the context is core profile, this will always
|
|
// work)
|
|
//
|
|
// This isn't perfect since without a lot of fiddling we'd need to check if e.g. indexed
|
|
// blending should be used or not. Since we're not too worried about working in this situation,
|
|
// just doing something reasonable, we just assume roughly ~2.0 functionality
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// Make sure if you change any other state in here, that you also update the push
|
|
// and pop functions above (RenderTextState)
|
|
|
|
// disable blending and some old-style fixed function features
|
|
GL.glDisable(eGL_BLEND);
|
|
GL.glDisable(eGL_LIGHTING);
|
|
GL.glDisable(eGL_ALPHA_TEST);
|
|
|
|
// set depth & stencil
|
|
GL.glDisable(eGL_DEPTH_TEST);
|
|
GL.glDisable(eGL_STENCIL_TEST);
|
|
GL.glDisable(eGL_CULL_FACE);
|
|
|
|
// set viewport & scissor
|
|
GL.glViewport(0, 0, (GLsizei)ctxdata.initParams.width, (GLsizei)ctxdata.initParams.height);
|
|
GL.glDisable(eGL_SCISSOR_TEST);
|
|
if(!IsGLES)
|
|
GL.glPolygonMode(eGL_FRONT_AND_BACK, eGL_FILL);
|
|
|
|
// bind textures
|
|
GL.glActiveTexture(eGL_TEXTURE0);
|
|
GL.glBindTexture(eGL_TEXTURE_2D, ctxdata.GlyphTexture);
|
|
GL.glEnable(eGL_TEXTURE_2D);
|
|
|
|
if(GL.glBindFramebuffer)
|
|
GL.glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, 0);
|
|
|
|
// just in case, try to disable the programmable pipeline
|
|
if(GL.glUseProgram)
|
|
GL.glUseProgram(0);
|
|
if(GL.glBindProgramPipeline)
|
|
GL.glBindProgramPipeline(0);
|
|
|
|
// draw string (based on sample code from stb_truetype.h)
|
|
rdcarray<Vec4f> vertices;
|
|
{
|
|
y += 1.0f;
|
|
y *= charPixelHeight;
|
|
|
|
#if ENABLED(RDOC_ANDROID)
|
|
y *= 2.0f;
|
|
#endif
|
|
|
|
float startx = x;
|
|
float starty = y;
|
|
|
|
float maxx = x, minx = x;
|
|
float maxy = y, miny = y - charPixelHeight;
|
|
|
|
stbtt_aligned_quad q;
|
|
|
|
const char *prepass = text.c_str();
|
|
while(*prepass)
|
|
{
|
|
char c = *prepass;
|
|
if(c > FONT_FIRST_CHAR && c < FONT_LAST_CHAR)
|
|
{
|
|
stbtt_GetBakedQuad(chardata, FONT_TEX_WIDTH, FONT_TEX_HEIGHT, c - FONT_FIRST_CHAR - 1, &x,
|
|
&y, &q, 1);
|
|
|
|
maxx = RDCMAX(maxx, RDCMAX(q.x0, q.x1));
|
|
maxy = RDCMAX(maxy, RDCMAX(q.y0, q.y1));
|
|
|
|
minx = RDCMIN(minx, RDCMIN(q.x0, q.x1));
|
|
miny = RDCMIN(miny, RDCMIN(q.y0, q.y1));
|
|
}
|
|
else
|
|
{
|
|
x += chardata[0].xadvance;
|
|
}
|
|
prepass++;
|
|
}
|
|
|
|
x = startx;
|
|
y = starty;
|
|
|
|
// draw black bar behind text
|
|
|
|
vertices.push_back(Vec4f(minx, maxy, 0.0f, 0.0f));
|
|
vertices.push_back(Vec4f(maxx, maxy, 0.0f, 0.0f));
|
|
vertices.push_back(Vec4f(maxx, miny, 0.0f, 0.0f));
|
|
vertices.push_back(Vec4f(minx, miny, 0.0f, 0.0f));
|
|
|
|
float mul = ctxdata.initParams.isYFlipped ? -1.0f : 1.0f;
|
|
|
|
const char *str = text.c_str();
|
|
while(*str)
|
|
{
|
|
char c = *str;
|
|
if(c > FONT_FIRST_CHAR && c < FONT_LAST_CHAR)
|
|
{
|
|
stbtt_GetBakedQuad(chardata, FONT_TEX_WIDTH, FONT_TEX_HEIGHT, c - FONT_FIRST_CHAR - 1, &x,
|
|
&y, &q, 1);
|
|
|
|
vertices.push_back(Vec4f(q.x0, q.y0 * mul, q.s0, q.t0));
|
|
vertices.push_back(Vec4f(q.x1, q.y0 * mul, q.s1, q.t0));
|
|
vertices.push_back(Vec4f(q.x1, q.y1 * mul, q.s1, q.t1));
|
|
vertices.push_back(Vec4f(q.x0, q.y1 * mul, q.s0, q.t1));
|
|
|
|
maxx = RDCMAX(maxx, RDCMAX(q.x0, q.x1));
|
|
maxy = RDCMAX(maxy, RDCMAX(q.y0, q.y1));
|
|
}
|
|
else
|
|
{
|
|
x += chardata[0].xadvance;
|
|
}
|
|
++str;
|
|
}
|
|
}
|
|
m_Platform.DrawQuads((float)ctxdata.initParams.width, (float)ctxdata.initParams.height, vertices);
|
|
}
|
|
}
|