CelestiaContent/src/celengine/overlay.cpp

348 lines
8.0 KiB
C++

// overlay.cpp
//
// Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
#include <cstring>
#include <cstdarg>
#include <celutil/utf8.h>
#include <GL/glew.h>
#include <Eigen/Core>
#include <celutil/debug.h>
#include "vecgl.h"
#include "overlay.h"
#include "render.h"
using namespace std;
using namespace Eigen;
Overlay::Overlay(const Renderer& r) :
ostream(&sbuf),
renderer(r)
{
sbuf.setOverlay(this);
}
void Overlay::begin()
{
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, windowWidth, 0, windowHeight);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glTranslatef(0.125f, 0.125f, 0);
glDisable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
useTexture = false;
}
void Overlay::end()
{
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
void Overlay::setWindowSize(int w, int h)
{
windowWidth = w;
windowHeight = h;
}
void Overlay::setFont(TextureFont* f)
{
if (f != font)
{
font = f;
fontChanged = true;
}
}
void Overlay::beginText()
{
glPushMatrix();
textBlock++;
}
void Overlay::endText()
{
if (textBlock > 0)
{
textBlock--;
xoffset = 0.0f;
glPopMatrix();
}
}
void Overlay::print(wchar_t c)
{
if (font != nullptr)
{
if (!useTexture || fontChanged)
{
glEnable(GL_TEXTURE_2D);
font->bind();
useTexture = true;
fontChanged = false;
}
switch (c)
{
case '\n':
if (textBlock > 0)
{
glPopMatrix();
glTranslatef(0.0f, (float) -(1 + font->getHeight()), 0.0f);
xoffset = 0.0f;
glPushMatrix();
}
break;
default:
font->render(c, xoffset, yoffset);
xoffset += font->getAdvance(c);
break;
}
}
}
void Overlay::print(char c)
{
if (font != nullptr)
{
if (!useTexture || fontChanged)
{
glEnable(GL_TEXTURE_2D);
font->bind();
useTexture = true;
fontChanged = false;
}
switch (c)
{
case '\n':
if (textBlock > 0)
{
glPopMatrix();
glTranslatef(0.0f, (float) -(1 + font->getHeight()), 0.0f);
xoffset = 0.0f;
glPushMatrix();
}
break;
default:
font->render(c, xoffset, yoffset);
xoffset += font->getAdvance(c);
break;
}
}
}
void Overlay::print(const char* s)
{
int length = strlen(s);
bool validChar = true;
int i = 0;
while (i < length && validChar)
{
wchar_t ch = 0;
validChar = UTF8Decode(s, i, length, ch);
i += UTF8EncodedSize(ch);
print(ch);
}
}
static void drawRect(const Renderer& r,
const array<float, 8>& vertices,
const vector<Vector4f>& colors,
Overlay::RectType type,
float linewidth)
{
uint32_t p = 0;
switch (type)
{
case Overlay::RectType::Textured:
p |= ShaderProperties::HasTexture;
// [[ fallthrough ]]
case Overlay::RectType::Outlined:
case Overlay::RectType::Filled:
switch (colors.size())
{
case 0:
break;
case 1:
p |= ShaderProperties::UniformColor;
break;
case 4:
p |= ShaderProperties::PerVertexColor;
break;
default:
fmt::fprintf(cerr, "Incorrect number of colors: %i\n", colors.size());
break;
}
default:
break;
}
auto prog = r.getShaderManager().getShader(ShaderProperties(p));
if (prog == nullptr)
return;
constexpr array<short, 8> texels = {0, 1, 1, 1, 1, 0, 0, 0};
auto s = static_cast<GLsizeiptr>(memsize(vertices) + memsize(texels) + 4*4*sizeof(GLfloat));
static celgl::VertexObject vo{ GL_ARRAY_BUFFER, s, GL_DYNAMIC_DRAW };
vo.bindWritable();
if (!vo.initialized())
{
vo.allocate();
vo.setBufferData(texels.data(), memsize(vertices), memsize(texels));
vo.setVertices(2, GL_FLOAT);
vo.setTextureCoords(2, GL_SHORT, false, 0, memsize(vertices));
vo.setColors(4, GL_FLOAT, false, 0, memsize(vertices) + memsize(texels));
}
vo.setBufferData(vertices.data(), 0, memsize(vertices));
if (colors.size() == 4)
vo.setBufferData(colors.data(), memsize(vertices) + memsize(texels), 4*4*sizeof(GLfloat));
prog->use();
if (type == Overlay::RectType::Textured)
prog->samplerParam("tex") = 0;
if (colors.size() == 1)
prog->vec4Param("color") = colors[0];
if (type != Overlay::RectType::Outlined)
{
vo.draw(GL_TRIANGLE_FAN, 4);
}
else
{
if (linewidth != 1.0f)
glLineWidth(linewidth);
vo.draw(GL_LINE_LOOP, 4);
if (linewidth != 1.0f)
glLineWidth(1.0f);
}
glUseProgram(0);
vo.unbind();
}
void Overlay::rect(const Overlay::Rectangle& r)
{
if (useTexture && r.type != Overlay::RectType::Textured)
{
glDisable(GL_TEXTURE_2D);
useTexture = false;
}
vector<Vector4f> cv;
for (const Color& c : r.colors)
cv.push_back(c.toVector4());
array<float, 8> coord = { r.x, r.y, r.x+r.w, r.y, r.x+r.w, r.y+r.h, r.x, r.y+r.h };
drawRect(renderer, coord, cv, r.type, r.lw);
}
//
// OverlayStreamBuf implementation
//
OverlayStreamBuf::OverlayStreamBuf()
{
setbuf(nullptr, 0);
};
void OverlayStreamBuf::setOverlay(Overlay* o)
{
overlay = o;
}
int OverlayStreamBuf::overflow(int c)
{
if (overlay != nullptr)
{
switch (decodeState)
{
case UTF8DecodeStart:
if (c < 0x80)
{
// Just a normal 7-bit character
overlay->print((char) c);
}
else
{
unsigned int count;
if ((c & 0xe0) == 0xc0)
count = 2;
else if ((c & 0xf0) == 0xe0)
count = 3;
else if ((c & 0xf8) == 0xf0)
count = 4;
else if ((c & 0xfc) == 0xf8)
count = 5;
else if ((c & 0xfe) == 0xfc)
count = 6;
else
count = 1; // Invalid byte
if (count > 1)
{
unsigned int mask = (1 << (7 - count)) - 1;
decodeShift = (count - 1) * 6;
decodedChar = (c & mask) << decodeShift;
decodeState = UTF8DecodeMultibyte;
}
else
{
// If the character isn't valid multibyte sequence head,
// silently skip it by leaving the decoder state alone.
}
}
break;
case UTF8DecodeMultibyte:
if ((c & 0xc0) == 0x80)
{
// We have a valid non-head byte in the sequence
decodeShift -= 6;
decodedChar |= (c & 0x3f) << decodeShift;
if (decodeShift == 0)
{
overlay->print(decodedChar);
decodeState = UTF8DecodeStart;
}
}
else
{
// Bad byte in UTF-8 encoded sequence; we'll silently ignore
// it and reset the state of the UTF-8 decoder.
decodeState = UTF8DecodeStart;
}
break;
}
}
return c;
}