Initial commit
This commit is contained in:
commit
edfc5298e1
252 changed files with 93965 additions and 0 deletions
259
source/Graphics/BatchRenderer2D.cpp
Normal file
259
source/Graphics/BatchRenderer2D.cpp
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
|
||||
#include <Spectre/System/Log.h>
|
||||
#include <Spectre/Graphics/OpenGL.h>
|
||||
#include <Spectre/Graphics/Renderable.h>
|
||||
#include <Spectre/Graphics/RenderState.h>
|
||||
#include <Spectre/Graphics/ShaderProgram.h>
|
||||
#include <Spectre/Graphics/Texture.h>
|
||||
#include <Spectre/Graphics/BatchRenderer2D.h>
|
||||
#include <Spectre/Math/Math.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
static bool compare_renderable(const Renderable2D* a, const Renderable2D* b) {
|
||||
|
||||
if (a->getRenderType() != b->getRenderType()) {
|
||||
return a->getRenderType() < b->getRenderType();
|
||||
}
|
||||
if (a->getTexture() != b->getTexture()) {
|
||||
return a->getTexture() < b->getTexture();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
BatchRenderer2D::BatchRenderer2D()
|
||||
{
|
||||
// Generate buffer objects.
|
||||
glGenBuffers(1, &m_IBO);
|
||||
glGenBuffers(1, &m_VBO);
|
||||
|
||||
setBatchSize(10000);
|
||||
|
||||
m_textShader.create();
|
||||
if (!m_textShader.loadFromFile("assets/shaders/text.shader.glsl")) {
|
||||
std::cout << "Failed to load shader: " << m_textShader.getLastError() << std::endl;
|
||||
}
|
||||
|
||||
if (!m_textShader.link()) {
|
||||
std::cout << "Failed to link shader: " << m_textShader.getLastError() << std::endl;
|
||||
}
|
||||
|
||||
m_spriteShader.create();
|
||||
if (!m_spriteShader.loadFromFile("assets/shaders/standard.shader.glsl")) {
|
||||
std::cout << "Failed to load shader: " << m_spriteShader.getLastError() << std::endl;
|
||||
}
|
||||
|
||||
if (!m_spriteShader.link()) {
|
||||
std::cout << "Failed to link shader: " << m_spriteShader.getLastError() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
BatchRenderer2D::~BatchRenderer2D()
|
||||
{
|
||||
glDeleteBuffers(1, &m_VBO);
|
||||
glDeleteBuffers(1, &m_IBO);
|
||||
}
|
||||
|
||||
void BatchRenderer2D::setBatchSize(unsigned short size)
|
||||
{
|
||||
std::vector<unsigned short> indices;
|
||||
int vtx = 0;
|
||||
|
||||
if (size > 10000) {
|
||||
size = 10000;
|
||||
}
|
||||
m_size = size;
|
||||
|
||||
indices.resize(size * 6);
|
||||
|
||||
for(int i = 0; i < m_size * 6; i += 6) {
|
||||
indices[i + 0] = vtx + 0;
|
||||
indices[i + 1] = vtx + 1;
|
||||
indices[i + 2] = vtx + 2;
|
||||
indices[i + 3] = vtx + 2;
|
||||
indices[i + 4] = vtx + 3;
|
||||
indices[i + 5] = vtx + 0;
|
||||
vtx += 4;
|
||||
}
|
||||
|
||||
// Upload to GPU
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IBO);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned short), &indices[0], GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
// Allocate memory for vertex buffer.
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, m_size * sizeof(Vertex2D), NULL, GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
// Log
|
||||
log("BatchRenderer - BatchSize\n");
|
||||
log(" num sprites: %i\n", m_size);
|
||||
log(" num indices: %i\n", m_size * 6);
|
||||
log(" num vertices: %i\n", m_size);
|
||||
log("------\n");
|
||||
}
|
||||
|
||||
void BatchRenderer2D::begin()
|
||||
{
|
||||
}
|
||||
|
||||
void BatchRenderer2D::end()
|
||||
{
|
||||
}
|
||||
|
||||
void BatchRenderer2D::submit(const Renderable2D& renderable)
|
||||
{
|
||||
m_queue.push_back(&renderable);
|
||||
}
|
||||
|
||||
void BatchRenderer2D::render()
|
||||
{
|
||||
Matrix4f viewMatrix = m_camera ? m_camera->getViewMatrix() : Matrix4f::Identity;
|
||||
Matrix4f projMatrix = m_camera ? m_camera->getProjectionMatrix() : Matrix4f::Identity;
|
||||
Matrix4f guiMatrix = math::orthoProjection(0.0f, 800.0f, 600.0f, 0.0f);
|
||||
Matrix4f MVP = projMatrix * viewMatrix;
|
||||
|
||||
// Nothing to render.
|
||||
if (m_queue.size() < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bind buffers.
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IBO);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
|
||||
|
||||
// Setup position.
|
||||
glEnableVertexAttribArray(VertexAttribPosition);
|
||||
glVertexAttribPointer(VertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (void*) 0);
|
||||
|
||||
|
||||
// Setup color
|
||||
glEnableVertexAttribArray(VertexAttribColor0);
|
||||
glVertexAttribPointer(VertexAttribColor0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (void*) 8);
|
||||
|
||||
glEnableVertexAttribArray(VertexAttribTexCoord0);
|
||||
glVertexAttribPointer(VertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (void*) 20);
|
||||
|
||||
// Set shader uniforms.
|
||||
m_textShader.enable();
|
||||
m_textShader.setUniform("u_MVP", MVP);
|
||||
m_textShader.disable();
|
||||
|
||||
m_spriteShader.enable();
|
||||
m_spriteShader.setUniform("u_MVP", MVP);
|
||||
m_spriteShader.disable();
|
||||
|
||||
prepareQueue();
|
||||
|
||||
glDisableVertexAttribArray(VertexAttribPosition);
|
||||
glDisableVertexAttribArray(VertexAttribColor0);
|
||||
glDisableVertexAttribArray(VertexAttribTexCoord0);
|
||||
|
||||
// Unbind all buffers.
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
m_queue.clear();
|
||||
m_state.cleanup();
|
||||
}
|
||||
|
||||
void BatchRenderer2D::prepareQueue()
|
||||
{
|
||||
Vertex2D *buffer = static_cast<Vertex2D*>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY));
|
||||
|
||||
Batch current;
|
||||
|
||||
std::sort(m_queue.begin(), m_queue.end(), compare_renderable);
|
||||
|
||||
current.type = m_queue[0]->getRenderType();
|
||||
current.texture = m_queue[0]->getTexture();
|
||||
current.count = 0;
|
||||
current.offset = 0;
|
||||
|
||||
for(int i = 0; i < m_queue.size(); i++) {
|
||||
|
||||
const Texture *tex = m_queue[i]->getTexture();
|
||||
RenderType type = m_queue[i]->getRenderType();
|
||||
unsigned int num_vert = m_queue[i]->getVertices().size();
|
||||
|
||||
// TODO: Only upload and draw if buffer is full :)
|
||||
if (current.type != type || current.texture != tex || current.count >= m_size) {
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
drawBatch(current);
|
||||
buffer = static_cast<Vertex2D*>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY));
|
||||
|
||||
current.type = m_queue[i]->getRenderType();
|
||||
current.count = 0;
|
||||
current.offset = 0;
|
||||
current.texture = tex;
|
||||
}
|
||||
|
||||
buffer += addRenderable(buffer, m_queue[i]);
|
||||
current.count += num_vert;
|
||||
}
|
||||
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
|
||||
if (current.count > 0) {
|
||||
drawBatch(current);
|
||||
}
|
||||
}
|
||||
|
||||
void BatchRenderer2D::uploadBatch(const Batch& batch)
|
||||
{
|
||||
Vertex2D *buffer = static_cast<Vertex2D*>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY));
|
||||
unsigned int count = 0;
|
||||
|
||||
for(int i = 0; i < batch.count; i++) {
|
||||
buffer += addRenderable(buffer, m_queue[batch.offset + i]);
|
||||
}
|
||||
|
||||
// Upload gpu data and draw.
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
unsigned int BatchRenderer2D::addRenderable(Vertex2D* buffer, const Renderable2D* renderable)
|
||||
{
|
||||
const Transform& transform = renderable->getTransform();
|
||||
const std::vector<Vertex2D>& vertices = renderable->getVertices();
|
||||
|
||||
// Pretransform vertex positions to skip setting a uniform model
|
||||
// matrix in the shader for each renderable reducing the number of draw calls.
|
||||
|
||||
for(int i = 0; i < vertices.size(); i++) {
|
||||
buffer[i].position = transform * vertices[i].position;
|
||||
buffer[i].color = vertices[i].color;
|
||||
buffer[i].uv = vertices[i].uv;
|
||||
}
|
||||
|
||||
return vertices.size();
|
||||
}
|
||||
|
||||
|
||||
void BatchRenderer2D::drawBatch(Batch& batch)
|
||||
{
|
||||
const ShaderProgram* shader;
|
||||
|
||||
if (batch.type == RenderType_UI) {
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
shader = &m_textShader;
|
||||
} else {
|
||||
shader = &m_spriteShader;
|
||||
}
|
||||
|
||||
// enable shader.
|
||||
m_state.enableShader(shader);
|
||||
|
||||
// enable batch texture
|
||||
m_state.enableTexture(batch.texture);
|
||||
|
||||
glDrawElements(GL_TRIANGLES, (batch.count / 4) * 6, GL_UNSIGNED_SHORT, 0);
|
||||
|
||||
if (batch.type == RenderType_UI) {
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
}
|
||||
72
source/Graphics/DefaultRenderer2D.cpp
Normal file
72
source/Graphics/DefaultRenderer2D.cpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
|
||||
#include <Spectre/Graphics/Texture.h>
|
||||
#include <Spectre/Graphics/OpenGL.h>
|
||||
#include <Spectre/Graphics/Renderable.h>
|
||||
#include <Spectre/Graphics/DefaultRenderer2D.h>
|
||||
|
||||
DefaultRenderer2D::DefaultRenderer2D()
|
||||
{
|
||||
m_shader.create();
|
||||
if (!m_shader.loadFromFile("assets/shaders/standard.shader.glsl")) {
|
||||
std::cout << "Failed to load shader: " << m_shader.getLastError() << std::endl;
|
||||
}
|
||||
|
||||
if (!m_shader.link()) {
|
||||
std::cout << "Failed to link shader: " << m_shader.getLastError() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void DefaultRenderer2D::submit(const Renderable2D& renderable)
|
||||
{
|
||||
m_queue.push_back(&renderable);
|
||||
}
|
||||
|
||||
void DefaultRenderer2D::render()
|
||||
{
|
||||
Matrix4f viewMatrix;
|
||||
|
||||
if (m_camera) {
|
||||
viewMatrix = m_camera->getViewMatrix();
|
||||
} else {
|
||||
viewMatrix = Matrix4f::Identity;
|
||||
}
|
||||
|
||||
m_shader.enable();
|
||||
|
||||
glEnableVertexAttribArray(VertexAttribPosition);
|
||||
glEnableVertexAttribArray(VertexAttribColor0);
|
||||
glEnableVertexAttribArray(VertexAttribTexCoord0);
|
||||
|
||||
for(int i = 0; i < m_queue.size(); i++) {
|
||||
|
||||
const Renderable2D* obj = m_queue[i];
|
||||
const std::vector<Vertex2D> vertices = obj->getVertices();
|
||||
const std::vector<unsigned short> indices = obj->getIndices();
|
||||
unsigned char *data = (unsigned char*) &vertices[0];
|
||||
const Texture *tex = obj->getTexture();
|
||||
|
||||
Matrix4f modelViewMatrix = viewMatrix * obj->getTransform().getMatrix();
|
||||
m_shader.setMVPMatrix(modelViewMatrix);
|
||||
|
||||
glVertexAttribPointer(VertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), data + 0);
|
||||
glVertexAttribPointer(VertexAttribColor0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), data + 8);
|
||||
glVertexAttribPointer(VertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), data + 20);
|
||||
|
||||
if (tex) {
|
||||
tex->enable();
|
||||
}
|
||||
|
||||
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, &indices[0]);
|
||||
|
||||
if (tex) {
|
||||
tex->disable();
|
||||
}
|
||||
}
|
||||
|
||||
glDisableVertexAttribArray(VertexAttribPosition);
|
||||
glDisableVertexAttribArray(VertexAttribColor0);
|
||||
glDisableVertexAttribArray(VertexAttribTexCoord0);
|
||||
|
||||
m_shader.disable();
|
||||
m_queue.clear();
|
||||
}
|
||||
127
source/Graphics/Font.cpp
Normal file
127
source/Graphics/Font.cpp
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
|
||||
#include <Spectre/System/File.h>
|
||||
#include <Spectre/System/Log.h>
|
||||
#include <Spectre/Graphics/Font.h>
|
||||
#include <Spectre/Graphics/Image.h>
|
||||
#include <Spectre/Graphics/Texture.h>
|
||||
#include <Spectre/Math/Math.h>
|
||||
#include "Font/FreeTypeDriver.h"
|
||||
|
||||
Font::Font() :
|
||||
m_size (22),
|
||||
m_driver (new FreeTypeDriver())
|
||||
{
|
||||
}
|
||||
|
||||
Font::~Font()
|
||||
{
|
||||
delete m_driver;
|
||||
}
|
||||
|
||||
bool Font::loadFromFile(const std::string& filename, unsigned int size)
|
||||
{
|
||||
if (!m_driver->loadFromFile(filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_driver->setCharacterSize(size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_size = size;
|
||||
m_shelf = 0;
|
||||
m_texpos = vec2u(0, 0);
|
||||
|
||||
createTexture();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Font::loadFromMemory(const void *data, unsigned int size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Font::Glyph Font::getGlyph(unsigned int code) const
|
||||
{
|
||||
if (m_charset.find(code) == m_charset.end()) {
|
||||
|
||||
loadChar(code);
|
||||
}
|
||||
return m_charset[code];
|
||||
}
|
||||
|
||||
void Font::loadChar(unsigned char code) const
|
||||
{
|
||||
Image img;
|
||||
Font::Glyph glyph;
|
||||
|
||||
if (!m_driver) {
|
||||
return;
|
||||
}
|
||||
|
||||
glyph = m_driver->loadGlyph(code, img);
|
||||
|
||||
if (glyph.size > vec2b(0, 0)) {
|
||||
|
||||
Vector2u pos = findTextureRegion(img);
|
||||
|
||||
m_texture.enable();
|
||||
m_texture.update(pos, img);
|
||||
|
||||
glyph.texture = &m_texture;
|
||||
glyph.texture_origin = vec2u(pos.x, pos.y);
|
||||
} else {
|
||||
glyph.texture = NULL;
|
||||
}
|
||||
|
||||
m_charset[code] = glyph;
|
||||
}
|
||||
|
||||
void Font::createTexture()
|
||||
{
|
||||
m_texture.create(128, 32, PixelFormat::PF_Alpha);
|
||||
m_texture.enable();
|
||||
m_texture.setSmooth(true);
|
||||
m_texture.disable();
|
||||
}
|
||||
|
||||
void Font::resizeTexture() const
|
||||
{
|
||||
unsigned int nextSize = math::nextPowerOfTwo(m_texture.getSize().y + 1);
|
||||
|
||||
Image tmpImg = m_texture.copyToImage();
|
||||
|
||||
// recreate texture.
|
||||
m_texture.create(m_texture.getSize().x, nextSize, PixelFormat::PF_Alpha);
|
||||
m_texture.update(vec2u(0, 0), tmpImg);
|
||||
}
|
||||
|
||||
Vector2u Font::findTextureRegion(const Image& img) const
|
||||
{
|
||||
Vector2u pos;
|
||||
|
||||
if (m_texpos.x + img.getWidth() + 1 > m_texture.getSize().x) {
|
||||
m_texpos.y += m_shelf;
|
||||
m_texpos.x = 0;
|
||||
m_shelf = 0;
|
||||
}
|
||||
|
||||
if (img.getHeight() + 1 > m_shelf) {
|
||||
m_shelf = img.getHeight() + 1;
|
||||
if (m_texpos.y + m_shelf > m_texture.getSize().y) {
|
||||
resizeTexture();
|
||||
}
|
||||
}
|
||||
|
||||
pos = m_texpos;
|
||||
|
||||
m_texpos.x += img.getWidth() + 1;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
const Texture* Font::getTexture() const
|
||||
{
|
||||
return &m_texture;
|
||||
}
|
||||
12
source/Graphics/Font/FontDriver.cpp
Normal file
12
source/Graphics/Font/FontDriver.cpp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
#include "FontDriver.h"
|
||||
|
||||
FontDriver::FontDriver() :
|
||||
m_hinting (true)
|
||||
{
|
||||
}
|
||||
|
||||
void FontDriver::setHinting(bool value)
|
||||
{
|
||||
m_hinting = value;
|
||||
}
|
||||
31
source/Graphics/Font/FontDriver.h
Normal file
31
source/Graphics/Font/FontDriver.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
#ifndef SPECTRE_GRAPHICS_FONT_FONTDRIVER_H
|
||||
#define SPECTRE_GRAPHICS_FONT_FONTDRIVER_H
|
||||
|
||||
#include <string>
|
||||
#include <Spectre/Graphics/Image.h>
|
||||
#include <Spectre/Graphics/Font.h>
|
||||
|
||||
class FontDriver
|
||||
{
|
||||
public :
|
||||
|
||||
FontDriver();
|
||||
|
||||
void setHinting(bool value);
|
||||
|
||||
virtual bool setCharacterSize(unsigned int size) = 0;
|
||||
|
||||
virtual bool loadFromFile(const std::string& filename) = 0;
|
||||
|
||||
virtual Font::Glyph loadGlyph(unsigned int codepoint, Image& img) = 0;
|
||||
|
||||
virtual std::string getName() = 0;
|
||||
|
||||
protected :
|
||||
|
||||
// True if hinting is enabled. false otherwise.
|
||||
bool m_hinting;
|
||||
};
|
||||
|
||||
#endif /* SPECTRE_GRAPHICS_FONT_FONTDRIVER_H */
|
||||
131
source/Graphics/Font/FreeTypeDriver.cpp
Normal file
131
source/Graphics/Font/FreeTypeDriver.cpp
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
|
||||
#include <ft2build.h>
|
||||
|
||||
#include FT_MODULE_H
|
||||
#include FT_LCD_FILTER_H
|
||||
|
||||
#include <Spectre/System/Log.h>
|
||||
#include "FreeTypeError.h"
|
||||
#include "FreeTypeDriver.h"
|
||||
|
||||
class LibWrapper
|
||||
{
|
||||
public :
|
||||
static LibWrapper& getInstance()
|
||||
{
|
||||
static LibWrapper _inst;
|
||||
return _inst;
|
||||
};
|
||||
|
||||
// Do not implement.
|
||||
LibWrapper(const LibWrapper&);
|
||||
void operator=(const LibWrapper&);
|
||||
|
||||
FT_Library handle;
|
||||
|
||||
private :
|
||||
|
||||
LibWrapper();
|
||||
~LibWrapper();
|
||||
};
|
||||
|
||||
LibWrapper::LibWrapper()
|
||||
{
|
||||
FT_Error error = FT_Init_FreeType(&handle);
|
||||
if (error) {
|
||||
log("Could not initialize FreeType\n");
|
||||
} else {
|
||||
log("FreeType font driver was initialized.\n");
|
||||
}
|
||||
}
|
||||
|
||||
LibWrapper::~LibWrapper()
|
||||
{
|
||||
FT_Error error = FT_Done_FreeType(handle);
|
||||
if (error) {
|
||||
log("Could not close FreeType\n");
|
||||
}
|
||||
}
|
||||
|
||||
FreeTypeDriver::FreeTypeDriver() :
|
||||
m_face (NULL)
|
||||
{
|
||||
}
|
||||
|
||||
FreeTypeDriver::~FreeTypeDriver()
|
||||
{
|
||||
if (m_face) {
|
||||
FT_Done_Face(m_face);
|
||||
}
|
||||
}
|
||||
|
||||
bool FreeTypeDriver::setCharacterSize(unsigned int size)
|
||||
{
|
||||
FT_Error error = FT_Set_Pixel_Sizes(m_face, 0, size);
|
||||
if (error) {
|
||||
log("FreeType: failed to set character size\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FreeTypeDriver::loadFromFile(const std::string& filename)
|
||||
{
|
||||
FT_Face face;
|
||||
FT_Error error;
|
||||
|
||||
error = FT_New_Face(LibWrapper::getInstance().handle, filename.c_str(), 0, &face);
|
||||
if (error) {
|
||||
log("FreeType: could not load file (%s): %s\n",
|
||||
filename.c_str(), FT_GetErrorString(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
error = FT_Select_Charmap(face, FT_ENCODING_UNICODE);
|
||||
if (error) {
|
||||
log("FreeType: (%s) failed to set unicode charmap\n", filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_face = face;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Font::Glyph FreeTypeDriver::loadGlyph(unsigned int codepoint, Image& img)
|
||||
{
|
||||
Font::Glyph glyph;
|
||||
FT_Int32 flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL;
|
||||
|
||||
if (m_hinting) {
|
||||
flags |= FT_LOAD_FORCE_AUTOHINT;
|
||||
} else {
|
||||
flags |= FT_LOAD_NO_AUTOHINT;
|
||||
}
|
||||
|
||||
FT_Error error = FT_Load_Char(m_face, codepoint, flags);
|
||||
if (!error) {
|
||||
|
||||
FT_GlyphSlot slot = m_face->glyph;
|
||||
|
||||
if (slot->bitmap.buffer) {
|
||||
img.create(PixelFormat::PF_Alpha,
|
||||
slot->bitmap.width,
|
||||
slot->bitmap.rows,
|
||||
slot->bitmap.buffer);
|
||||
}
|
||||
|
||||
glyph.offset = vec2b(slot->bitmap_left, slot->bitmap_top);
|
||||
glyph.size = vec2b(slot->bitmap.width, slot->bitmap.rows);
|
||||
glyph.advance = static_cast<unsigned char>(slot->advance.x >> 6);
|
||||
} else {
|
||||
log("FreeType: failed to load glyph for character code '%c'\n", codepoint);
|
||||
}
|
||||
|
||||
return glyph;
|
||||
}
|
||||
|
||||
std::string FreeTypeDriver::getName()
|
||||
{
|
||||
return m_face->family_name;
|
||||
}
|
||||
29
source/Graphics/Font/FreeTypeDriver.h
Normal file
29
source/Graphics/Font/FreeTypeDriver.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
#ifndef SPECTRE_GRAPHICS_FONT_FREETYPEDRIVER_H
|
||||
#define SPECTRE_GRAPHICS_FONT_FREETYPEDRIVER_H
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
#include "FontDriver.h"
|
||||
|
||||
class FreeTypeDriver : public FontDriver
|
||||
{
|
||||
public:
|
||||
FreeTypeDriver();
|
||||
~FreeTypeDriver();
|
||||
|
||||
virtual bool setCharacterSize(unsigned int size);
|
||||
|
||||
virtual bool loadFromFile(const std::string& filename);
|
||||
|
||||
virtual Font::Glyph loadGlyph(unsigned int codepoint, Image& img);
|
||||
|
||||
virtual std::string getName();
|
||||
|
||||
private :
|
||||
|
||||
FT_Face m_face;
|
||||
};
|
||||
|
||||
#endif /* SPECTRE_GRAPHICS_FONT_FREETYPEDRIVER_H */
|
||||
28
source/Graphics/Font/FreeTypeError.cpp
Normal file
28
source/Graphics/Font/FreeTypeError.cpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
#include "FreeTypeError.h"
|
||||
|
||||
#include <ft2build.h>
|
||||
|
||||
#undef __FTERRORS_H__
|
||||
#define FT_ERROR_START_LIST {
|
||||
#define FT_ERRORDEF(e, v ,s) { e, s },
|
||||
#define FT_ERROR_END_LIST { 0, NULL } };
|
||||
|
||||
const struct error_list {
|
||||
int code;
|
||||
const char* msg;
|
||||
} ft_errors[] =
|
||||
#include FT_ERRORS_H
|
||||
|
||||
const char* FT_GetErrorString(FT_Error error) {
|
||||
|
||||
const struct error_list* ptr = ft_errors;
|
||||
|
||||
while(ptr->msg) {
|
||||
|
||||
if (ptr->code == error)
|
||||
return ptr->msg;
|
||||
ptr++;
|
||||
}
|
||||
return "Unkown error";
|
||||
}
|
||||
10
source/Graphics/Font/FreeTypeError.h
Normal file
10
source/Graphics/Font/FreeTypeError.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
#ifndef SPECTRE_GRAPHICS_FONT_FREETYPE_ERROR_H
|
||||
#define SPECTRE_GRAPHICS_FONT_FREETYPE_ERROR_H
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
const char* FT_GetErrorString(FT_Error error);
|
||||
|
||||
#endif /* SPECTRE_GRAPHICS_FONT_FREETYPE_ERROR_H */
|
||||
12
source/Graphics/GL/CheckError.cpp
Normal file
12
source/Graphics/GL/CheckError.cpp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
#include <string>
|
||||
#include "CheckError.h"
|
||||
|
||||
void glCheckError(const char *file, unsigned int line, const char *expr) {
|
||||
|
||||
GLenum err = glGetError();
|
||||
|
||||
if (err != GL_NO_ERROR) {
|
||||
std::string msg;
|
||||
}
|
||||
}
|
||||
11
source/Graphics/GL/CheckError.h
Normal file
11
source/Graphics/GL/CheckError.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
#ifndef SPECTRE_GRAPHICS_GL_CHECKERROR_H
|
||||
#define SPECTRE_GRAPHICS_GL_CHECKERROR_H
|
||||
|
||||
#include <Spectre/Graphics/OpenGL.h>
|
||||
|
||||
#define checkGLError(expr) do { expr; glCheckError(__FILE__, __LINE__, #expr); } while(false)
|
||||
|
||||
void glCheckError(const char *file, unsigned int line, const char *expr);
|
||||
|
||||
#endif /* SPECTRE_GRAPHICS_GL_CHECKERROR_H */
|
||||
208
source/Graphics/Image.cpp
Normal file
208
source/Graphics/Image.cpp
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
|
||||
#include "ImageLoader.h"
|
||||
#include <Spectre/System/Log.h>
|
||||
#include <Spectre/Graphics/Image.h>
|
||||
|
||||
static ImageLoader _loader;
|
||||
|
||||
Image::Image() :
|
||||
m_size (0, 0),
|
||||
m_format (PF_Unknown)
|
||||
{
|
||||
}
|
||||
|
||||
void Image::create(unsigned width, unsigned height, const Color& color)
|
||||
{
|
||||
m_size.x = width;
|
||||
m_size.y = height;
|
||||
m_format = PixelFormat::PF_RGBA;
|
||||
|
||||
m_pixels.resize(m_size.x * m_size.y * (getBpp() / 8));
|
||||
|
||||
for(int i = 0; i < m_pixels.size(); i += 4) {
|
||||
|
||||
m_pixels[i+0] = color.r;
|
||||
m_pixels[i+1] = color.g;
|
||||
m_pixels[i+2] = color.b;
|
||||
m_pixels[i+3] = color.a;
|
||||
}
|
||||
}
|
||||
|
||||
void Image::create(unsigned width, unsigned height, const void* pixels)
|
||||
{
|
||||
create(PixelFormat::PF_RGBA, width, height, pixels);
|
||||
}
|
||||
|
||||
void Image::create(PixelFormat format, unsigned width, unsigned height, const Color& color)
|
||||
{
|
||||
m_size.x = width;
|
||||
m_size.y = height;
|
||||
m_format = format;
|
||||
|
||||
m_pixels.resize(m_size.x * m_size.y * (getBpp() / 8));
|
||||
|
||||
if (getBpp() == 32) {
|
||||
for(int i = 0; i < m_pixels.size(); i += 4) {
|
||||
|
||||
m_pixels[i+0] = color.r;
|
||||
m_pixels[i+1] = color.g;
|
||||
m_pixels[i+2] = color.b;
|
||||
m_pixels[i+3] = color.a;
|
||||
}
|
||||
} else if (getBpp() == 24) {
|
||||
for(int i = 0; i < m_pixels.size(); i += 3) {
|
||||
|
||||
m_pixels[i+0] = color.r;
|
||||
m_pixels[i+1] = color.g;
|
||||
m_pixels[i+2] = color.b;
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < m_pixels.size(); i += 1) {
|
||||
m_pixels[i] = color.a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Image::create(PixelFormat format, unsigned width, unsigned height, const void* pixels)
|
||||
{
|
||||
m_size.x = width;
|
||||
m_size.y = height;
|
||||
m_format = format;
|
||||
|
||||
setPixels(pixels);
|
||||
}
|
||||
|
||||
const Vector2u& Image::getSize() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
unsigned int Image::getWidth() const
|
||||
{
|
||||
return m_size.x;
|
||||
}
|
||||
|
||||
unsigned int Image::getHeight() const
|
||||
{
|
||||
return m_size.y;
|
||||
}
|
||||
|
||||
unsigned int Image::getBpp() const
|
||||
{
|
||||
switch(m_format) {
|
||||
case PixelFormat::PF_RGBA : return 32;
|
||||
case PixelFormat::PF_RGB : return 24;
|
||||
case PixelFormat::PF_Alpha : return 8;
|
||||
case PixelFormat::PF_Unknown :
|
||||
default : break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int Image::getStride() const
|
||||
{
|
||||
return m_size.x * (getBpp() / 8);
|
||||
}
|
||||
|
||||
PixelFormat Image::getFormat() const
|
||||
{
|
||||
return m_format;
|
||||
}
|
||||
|
||||
bool Image::hasAlpha() const
|
||||
{
|
||||
return m_format == PixelFormat::PF_RGBA || m_format == PixelFormat::PF_Alpha;
|
||||
}
|
||||
|
||||
bool Image::loadFromFile(const std::string& filename)
|
||||
{
|
||||
m_pixels.clear();
|
||||
|
||||
return _loader.loadFromFile(filename.c_str(), *this);
|
||||
}
|
||||
|
||||
bool Image::loadFromMemory(const void *data, unsigned int size)
|
||||
{
|
||||
m_pixels.clear();
|
||||
|
||||
return _loader.loadFromMemory(data, size, *this);
|
||||
}
|
||||
|
||||
void Image::saveToFile(const std::string& filename) const
|
||||
{
|
||||
_loader.saveToFile(*this, filename.c_str());
|
||||
}
|
||||
|
||||
void Image::setPixel(unsigned x, unsigned y, const Color& c)
|
||||
{
|
||||
unsigned char *base = m_pixels.data() + ((getBpp() / 8) * ((y * m_size.x) + x));
|
||||
|
||||
if (m_format == PixelFormat::PF_RGB || m_format == PixelFormat::PF_RGBA) {
|
||||
base[0] = c.r;
|
||||
base[1] = c.g;
|
||||
base[2] = c.b;
|
||||
if (m_format == PixelFormat::PF_RGBA) {
|
||||
base[3] = c.a;
|
||||
}
|
||||
}
|
||||
else if (m_format == PixelFormat::PF_Alpha) {
|
||||
base[0] = c.a;
|
||||
}
|
||||
}
|
||||
|
||||
Color Image::getPixel(unsigned x, unsigned y) const
|
||||
{
|
||||
Color c;
|
||||
const unsigned char *base = m_pixels.data() + ((getBpp() / 8) * ((y * m_size.x) + x));
|
||||
|
||||
// RGB / RGBA formats.
|
||||
if (m_format == PixelFormat::PF_RGB || m_format == PixelFormat::PF_RGBA) {
|
||||
c.r = base[0];
|
||||
c.g = base[1];
|
||||
c.b = base[2];
|
||||
if (m_format == PixelFormat::PF_RGBA) {
|
||||
c.a = base[3];
|
||||
} else {
|
||||
c.a = 1.0f;
|
||||
}
|
||||
}
|
||||
// Alpha, single channel format.
|
||||
else if (m_format == PixelFormat::PF_Alpha) {
|
||||
c = Color(0, 0, 0, base[0]);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
void Image::flipY()
|
||||
{
|
||||
unsigned int stride = getStride();
|
||||
|
||||
// The quick and dirty way :)
|
||||
for(int y = 0; y < m_size.y / 2; y++) {
|
||||
|
||||
int row0 = y * stride;
|
||||
int row1 = (m_size.y - y - 1) * stride;
|
||||
|
||||
for(int x = 0; x < stride; x++) {
|
||||
unsigned char tmp = m_pixels[row0 + x];
|
||||
m_pixels[row0 + x] = m_pixels[row1 + x];
|
||||
m_pixels[row1 + x] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Image::setPixels(const void *pixels)
|
||||
{
|
||||
unsigned int size = m_size.y * getStride();
|
||||
|
||||
m_pixels.resize(size);
|
||||
std::memcpy(&m_pixels[0], pixels, m_pixels.size());
|
||||
}
|
||||
|
||||
const unsigned char* Image::getPixels() const
|
||||
{
|
||||
if (m_pixels.size()) {
|
||||
return &m_pixels[0];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
130
source/Graphics/ImageLoader.cpp
Normal file
130
source/Graphics/ImageLoader.cpp
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
|
||||
#include <Spectre/System/File.h>
|
||||
#include <Spectre/System/Log.h>
|
||||
#include <Spectre/Graphics/Image.h>
|
||||
#include "ImageLoader.h"
|
||||
|
||||
// Disable some file formats that we don't use.
|
||||
#define STBI_NO_PSD
|
||||
#define STBI_NO_PIC
|
||||
#define STBI_NO_GIF
|
||||
#define STBI_NO_HDR
|
||||
#define STBI_NO_PNM
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "stb_image_write.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
bool ImageLoader::loadFromFile(const char *filename, Image& img)
|
||||
{
|
||||
FILE *fd = fopen(filename, "rb");
|
||||
|
||||
if (fd) {
|
||||
std::vector<unsigned char> buf;
|
||||
fseek(fd, 0, SEEK_END);
|
||||
buf.resize(ftell(fd));
|
||||
rewind(fd);
|
||||
fread(&buf[0], 1, buf.size(), fd);
|
||||
fclose(fd);
|
||||
|
||||
// loaded into memory. now decode.
|
||||
if (decode((const char*)&buf[0], buf.size(), img) == false) {
|
||||
log("ImageLoader: could not load file '%s'. Reason: %s",
|
||||
filename, m_error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
log("ImageLoader: could not open file '%s'. Reason: %s",
|
||||
filename, std::strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ImageLoader::loadFromMemory(const void *data, unsigned size, Image& img)
|
||||
{
|
||||
//std::vector<unsigned char> buf;
|
||||
|
||||
//buf.assign(((unsigned char*) data), ((unsigned char*) data) + size);
|
||||
|
||||
return decode((const char*) data, size, img);
|
||||
}
|
||||
|
||||
// TODO: Support more formats.
|
||||
bool ImageLoader::saveToFile(const Image& img, const char *filename)
|
||||
{
|
||||
std::string ext = File::getExtension(filename);
|
||||
std::vector<unsigned char> encoded_data;
|
||||
|
||||
if (ext == "png") {
|
||||
|
||||
if (!encodePNG(img, encoded_data)) {
|
||||
log("ImageLoader: failed to save file '%s'. Reason: \n",
|
||||
filename, m_error);
|
||||
}
|
||||
|
||||
} else {
|
||||
log("ImageLoader: Invalid file format\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (encoded_data.size() > 0) {
|
||||
|
||||
FILE *fd = fopen(filename, "wb");
|
||||
if (fd) {
|
||||
fwrite(&encoded_data[0], 1, encoded_data.size(), fd);
|
||||
fclose(fd);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool ImageLoader::decode(const char *data, unsigned int size, Image& img)
|
||||
{
|
||||
// width, height, num components.
|
||||
int w, h, n_comp;
|
||||
const stbi_uc *ptr = (const stbi_uc *) data;
|
||||
|
||||
unsigned char *pixels = stbi_load_from_memory(ptr, size, &w, &h, &n_comp, 4);
|
||||
|
||||
if (pixels) {
|
||||
|
||||
unsigned int len = w * h * 4;
|
||||
|
||||
img.create(w, h, pixels);
|
||||
|
||||
stbi_image_free(pixels);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
m_error = stbi_failure_reason();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ImageLoader::encodePNG(const Image& img, std::vector<unsigned char>& data)
|
||||
{
|
||||
int buf_len;
|
||||
unsigned char *raw = (unsigned char*) img.getPixels();
|
||||
unsigned char *buf;
|
||||
|
||||
buf = stbi_write_png_to_mem(raw, img.getStride(), img.getWidth(), img.getHeight(), img.getBpp() / 8, &buf_len);
|
||||
if (buf && buf_len > 0) {
|
||||
|
||||
data.resize(buf_len);
|
||||
std::memcpy(&data[0], buf, buf_len);
|
||||
STBIW_FREE(buf);
|
||||
|
||||
return true;
|
||||
}
|
||||
m_error = stbi_failure_reason();
|
||||
return false;
|
||||
}
|
||||
33
source/Graphics/ImageLoader.h
Normal file
33
source/Graphics/ImageLoader.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
#ifndef SPECTRE_GRAPHICS_IMAGE_LOADER_H
|
||||
#define SPECTRE_GRAPHICS_IMAGE_LOADER_H
|
||||
|
||||
#include <Spectre/Graphics/Image.h>
|
||||
#include <Spectre/Math/Vector2.h>
|
||||
#include <vector>
|
||||
|
||||
class ImageLoader
|
||||
{
|
||||
public :
|
||||
bool loadFromFile(const char *filename, Image& img);
|
||||
|
||||
bool loadFromMemory(const void *data, unsigned size, Image& img);
|
||||
|
||||
bool saveToFile(const Image& img, const char *filename);
|
||||
|
||||
protected :
|
||||
|
||||
bool decode(const char* data, unsigned int len, Image& img);
|
||||
|
||||
bool encode(Image& img, std::vector<unsigned char>& data);
|
||||
|
||||
bool encodeJPEG(const Image& img, std::vector<unsigned char>& data);
|
||||
|
||||
bool encodePNG(const Image& img, std::vector<unsigned char>& data);
|
||||
|
||||
protected :
|
||||
|
||||
std::string m_error;
|
||||
};
|
||||
|
||||
#endif /* SPECTRE_GRAPHICS_IMAGE_LOADER_H */
|
||||
48
source/Graphics/RenderState.cpp
Normal file
48
source/Graphics/RenderState.cpp
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
|
||||
#include <Spectre/Graphics/ShaderProgram.h>
|
||||
#include <Spectre/Graphics/Texture.h>
|
||||
#include <Spectre/Graphics/RenderState.h>
|
||||
|
||||
RenderState::RenderState() :
|
||||
m_texture (0),
|
||||
m_shader (0)
|
||||
{
|
||||
}
|
||||
|
||||
RenderState::~RenderState()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void RenderState::enableTexture(const Texture *texture)
|
||||
{
|
||||
// Enable the new texture.
|
||||
if (texture) {
|
||||
texture->enable();
|
||||
}
|
||||
// No texture selected.
|
||||
// Must unbind cached texture if set.
|
||||
else if (m_texture) {
|
||||
m_texture->disable();
|
||||
}
|
||||
m_texture = texture;
|
||||
}
|
||||
|
||||
void RenderState::enableShader(const ShaderProgram *program)
|
||||
{
|
||||
m_shader = program;
|
||||
m_shader->enable();
|
||||
}
|
||||
|
||||
void RenderState::cleanup()
|
||||
{
|
||||
// Disable current texture if set.
|
||||
if (m_texture) {
|
||||
m_texture->disable();
|
||||
}
|
||||
|
||||
if (m_shader) {
|
||||
m_shader->disable();
|
||||
}
|
||||
}
|
||||
15
source/Graphics/Renderable2D.cpp
Normal file
15
source/Graphics/Renderable2D.cpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
#include <Spectre/Graphics/Renderable.h>
|
||||
|
||||
Renderable2D::Renderable2D()
|
||||
{
|
||||
}
|
||||
|
||||
Renderable2D::Renderable2D(const Vector2f& position) :
|
||||
Transformable (position)
|
||||
{
|
||||
}
|
||||
|
||||
Renderable2D::~Renderable2D()
|
||||
{
|
||||
}
|
||||
15
source/Graphics/Renderer2D.cpp
Normal file
15
source/Graphics/Renderer2D.cpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#include <Spectre/Graphics/Renderer2D.h>
|
||||
|
||||
Renderer2D::Renderer2D() :
|
||||
m_camera (NULL)
|
||||
{
|
||||
}
|
||||
|
||||
Renderer2D::~Renderer2D()
|
||||
{
|
||||
}
|
||||
|
||||
void Renderer2D::setCamera(const Camera2D& camera)
|
||||
{
|
||||
m_camera = &camera;
|
||||
}
|
||||
100
source/Graphics/Shader.cpp
Normal file
100
source/Graphics/Shader.cpp
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
|
||||
#include <fstream>
|
||||
#include <Spectre/System/File.h>
|
||||
#include <Spectre/Graphics/OpenGL.h>
|
||||
#include <Spectre/Graphics/Shader.h>
|
||||
|
||||
Shader::Shader(Type type, const std::string& name) :
|
||||
m_name (name)
|
||||
{
|
||||
GLenum internal_type = type == Vertex ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER;
|
||||
m_handle = glCreateShader(internal_type);
|
||||
}
|
||||
|
||||
Shader::~Shader()
|
||||
{
|
||||
if (m_handle) {
|
||||
glDeleteShader(m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Shader::getHandle() const
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
const std::string& Shader::getName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
bool Shader::loadFromFile(const std::string& file)
|
||||
{
|
||||
std::string src;
|
||||
std::ifstream strm;
|
||||
|
||||
// If this shader does not have a name, pick the filename.
|
||||
if (m_name.length() < 1) {
|
||||
m_name = File::getBasename(file);
|
||||
}
|
||||
|
||||
// Load file into memory.
|
||||
strm.open(file.c_str(), std::fstream::in);
|
||||
if (!strm.is_open()) {
|
||||
m_error = "Can't open file: " + file;
|
||||
return false;
|
||||
}
|
||||
|
||||
src.assign(std::istreambuf_iterator<char>(strm), std::istreambuf_iterator<char>());
|
||||
|
||||
strm.close();
|
||||
|
||||
return loadFromMemory(src);
|
||||
}
|
||||
|
||||
bool Shader::loadFromMemory(const std::string& source)
|
||||
{
|
||||
const char *s = source.c_str();
|
||||
|
||||
glShaderSource(m_handle, 1, &s, NULL);
|
||||
|
||||
return compile();
|
||||
}
|
||||
|
||||
const std::string& Shader::getError() const
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
bool Shader::isCompiled() const
|
||||
{
|
||||
// A shader without handle is not compiled.
|
||||
if (m_handle) {
|
||||
|
||||
// Query OpenGL for status.
|
||||
GLint status;
|
||||
glGetShaderiv(m_handle, GL_COMPILE_STATUS, &status);
|
||||
return status == GL_TRUE;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Shader::compile()
|
||||
{
|
||||
glCompileShader(m_handle);
|
||||
|
||||
if (!isCompiled()) {
|
||||
m_error = fetchErrorLog();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Shader::fetchErrorLog()
|
||||
{
|
||||
GLchar buf[4096] = { '\0 ' };
|
||||
|
||||
glGetShaderInfoLog(m_handle, sizeof(buf), NULL, buf);
|
||||
|
||||
return std::string(buf);
|
||||
}
|
||||
193
source/Graphics/ShaderProgram.cpp
Normal file
193
source/Graphics/ShaderProgram.cpp
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
|
||||
#include <Spectre/Math/Color.h>
|
||||
#include <Spectre/Graphics/OpenGL.h>
|
||||
#include <Spectre/Graphics/ShaderProgram.h>
|
||||
#include <Spectre/System/File.h>
|
||||
|
||||
ShaderProgram::ShaderProgram() :
|
||||
m_id (0)
|
||||
{
|
||||
}
|
||||
|
||||
ShaderProgram::~ShaderProgram()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
void ShaderProgram::create()
|
||||
{
|
||||
destroy();
|
||||
|
||||
m_id = glCreateProgram();
|
||||
}
|
||||
|
||||
void ShaderProgram::destroy()
|
||||
{
|
||||
if (m_id > 0) {
|
||||
glDeleteProgram(m_id);
|
||||
m_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderProgram::addShader(const Shader& shader)
|
||||
{
|
||||
glAttachShader(m_id, shader.getHandle());
|
||||
}
|
||||
|
||||
bool ShaderProgram::loadFromFile(const std::string& filename)
|
||||
{
|
||||
std::string extension = File::getExtension(filename);
|
||||
|
||||
// Meta file. load real shaders.
|
||||
if (extension == "shader.glsl") {
|
||||
|
||||
// FIXME: This is ugly :)
|
||||
std::string base_name = filename.substr(0, filename.length() - extension.length());
|
||||
|
||||
// vert and frag are not optional. they must exist.
|
||||
return loadFromFile(base_name + "vert.glsl", Shader::Type::Vertex) &&
|
||||
loadFromFile(base_name + "frag.glsl", Shader::Type::Fragment);
|
||||
|
||||
} else if (extension == "vert.glsl") {
|
||||
return loadFromFile(filename, Shader::Type::Vertex);
|
||||
} else if (extension == "frag.glsl") {
|
||||
return loadFromFile(filename, Shader::Type::Fragment);
|
||||
}
|
||||
|
||||
m_error = "Invalid file extension: " + extension;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderProgram::loadFromFile(const std::string& filename, Shader::Type type)
|
||||
{
|
||||
Shader shader(type);
|
||||
|
||||
if (!shader.loadFromFile(filename)) {
|
||||
m_error = shader.getName() + ": " + shader.getError();
|
||||
return false;
|
||||
}
|
||||
|
||||
addShader(shader);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShaderProgram::loadFromMemory(const std::string& source, Shader::Type type)
|
||||
{
|
||||
Shader shader(type);
|
||||
|
||||
if (!shader.loadFromMemory(source)) {
|
||||
m_error = shader.getError();
|
||||
return false;
|
||||
}
|
||||
|
||||
addShader(shader);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShaderProgram::link()
|
||||
{
|
||||
glLinkProgram(m_id);
|
||||
|
||||
if (!isLinked()) {
|
||||
m_error = fetchErrorLog();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShaderProgram::isLinked() const
|
||||
{
|
||||
// Make sure we have a id first.
|
||||
if (m_id) {
|
||||
|
||||
// Query OpenGL for link status.
|
||||
GLint status;
|
||||
glGetProgramiv(m_id, GL_LINK_STATUS, &status);
|
||||
return status == GL_TRUE;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShaderProgram::enable() const
|
||||
{
|
||||
glUseProgram(m_id);
|
||||
}
|
||||
|
||||
void ShaderProgram::disable() const
|
||||
{
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
std::string ShaderProgram::getLastError() const
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
bool ShaderProgram::setMVPMatrix(const Matrix4f& matrix)
|
||||
{
|
||||
return setUniform("u_MVP", matrix);
|
||||
}
|
||||
|
||||
bool ShaderProgram::setUniform(const std::string& name, const Matrix4f& matrix) const
|
||||
{
|
||||
int loc;
|
||||
if (getUniformLoc(name, loc)) {
|
||||
glUniformMatrix4fv(loc, 1, GL_FALSE, matrix.e);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderProgram::setAttribute(const std::string& name, const Matrix4f& matrix) const
|
||||
{
|
||||
int loc;
|
||||
if (getAttribLoc(name, loc)) {
|
||||
glVertexAttrib4fv(loc, matrix.e);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderProgram::setUniform(const std::string& name, const Color& color) const
|
||||
{
|
||||
int loc;
|
||||
if (getUniformLoc(name, loc)) {
|
||||
Vector4f c = color.toRGBAf();
|
||||
|
||||
glUniform4f(loc, c.x, c.y, c.z, c.w);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderProgram::getAttribLoc(const std::string& name, int& loc) const
|
||||
{
|
||||
loc = glGetAttribLocation(m_id, name.c_str());
|
||||
if (loc < 0) {
|
||||
m_error = "Attribute variable '" + name + "' was not found in shader.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShaderProgram::getUniformLoc(const std::string& name, int& loc) const
|
||||
{
|
||||
loc = glGetUniformLocation(m_id, name.c_str());
|
||||
if (loc < 0) {
|
||||
m_error = "Uniform variable '" + name + "' was not found in shader.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string ShaderProgram::fetchErrorLog()
|
||||
{
|
||||
GLchar buf[4096] = { '\0' };
|
||||
|
||||
glGetProgramInfoLog(m_id, sizeof(buf), NULL, buf);
|
||||
|
||||
return std::string(buf);
|
||||
}
|
||||
111
source/Graphics/Sprite.cpp
Normal file
111
source/Graphics/Sprite.cpp
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
|
||||
#include <Spectre/Graphics/Texture.h>
|
||||
#include <Spectre/Graphics/Sprite.h>
|
||||
|
||||
Sprite::Sprite() :
|
||||
m_texture (NULL)
|
||||
{
|
||||
unsigned short indices[6] = {
|
||||
0, 1, 2, 2, 3, 0
|
||||
};
|
||||
|
||||
m_indicies.assign(indices, indices + 6);
|
||||
m_vertices.resize(4);
|
||||
|
||||
setSize(vec2f(1.0f, 1.0f));
|
||||
setColor(Color(255, 255, 255));
|
||||
|
||||
// Texcords
|
||||
m_vertices[0].uv = Vector2f( 0.0f, 0.0f);
|
||||
m_vertices[1].uv = Vector2f( 0.0f, 1.0f);
|
||||
m_vertices[2].uv = Vector2f( 1.0f, 1.0f);
|
||||
m_vertices[3].uv = Vector2f( 1.0f, 0.0f);
|
||||
}
|
||||
|
||||
Sprite::Sprite(const vec2f& pos, const vec2f& size) :
|
||||
m_texture (NULL)
|
||||
{
|
||||
unsigned short indices[6] = {
|
||||
0, 1, 2, 2, 3, 0
|
||||
};
|
||||
|
||||
m_indicies.assign(indices, indices + 6);
|
||||
m_vertices.resize(4);
|
||||
|
||||
setPosition(pos);
|
||||
setSize(size);
|
||||
setColor(Color(255, 255, 255));
|
||||
|
||||
m_vertices[0].uv = Vector2f( 0.0f, 1.0f);
|
||||
m_vertices[1].uv = Vector2f( 0.0f, 0.0f);
|
||||
m_vertices[2].uv = Vector2f( 1.0f, 0.0f);
|
||||
m_vertices[3].uv = Vector2f( 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
void Sprite::init()
|
||||
{
|
||||
}
|
||||
|
||||
void Sprite::setSize(vec2f size)
|
||||
{
|
||||
if (size < vec2f(0.0, 0.0f)) {
|
||||
size = vec2f(0.0, 0.0f);
|
||||
}
|
||||
|
||||
m_vertices[0].position = vec2f(0.0f, 0.0f);
|
||||
m_vertices[1].position = vec2f(0.0f, size.y);
|
||||
m_vertices[2].position = vec2f(size.x, size.y);
|
||||
m_vertices[3].position = vec2f(size.x, 0.0f);
|
||||
}
|
||||
|
||||
void Sprite::setColor(const Color& color)
|
||||
{
|
||||
m_vertices[0].color = color.toRGBf();
|
||||
m_vertices[1].color = m_vertices[0].color;
|
||||
m_vertices[2].color = m_vertices[0].color;
|
||||
m_vertices[3].color = m_vertices[0].color;
|
||||
}
|
||||
|
||||
void Sprite::setTexture(const Texture& texture)
|
||||
{
|
||||
m_texture = &texture;
|
||||
}
|
||||
|
||||
void Sprite::setTextureCoords(const vec2u& pos, const vec2u& size)
|
||||
{
|
||||
// No texture, nothing to do.
|
||||
if (!m_texture) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2f tex_size = Vector2f(m_texture->getSize());
|
||||
|
||||
float left = pos.x / tex_size.x;
|
||||
float right = left + (size.x / tex_size.x);
|
||||
float top = pos.y / tex_size.y;
|
||||
float bottom = top + (size.y / tex_size.y);
|
||||
|
||||
m_vertices[0].uv = Vector2f(left , top);
|
||||
m_vertices[1].uv = Vector2f(left , bottom);
|
||||
m_vertices[2].uv = Vector2f(right, bottom);
|
||||
m_vertices[3].uv = Vector2f(right, top);
|
||||
}
|
||||
|
||||
const Texture* Sprite::getTexture() const
|
||||
{
|
||||
return m_texture;
|
||||
}
|
||||
|
||||
const std::vector<Vertex2D>& Sprite::getVertices() const
|
||||
{
|
||||
return m_vertices;
|
||||
}
|
||||
|
||||
const std::vector<unsigned short>& Sprite::getIndices() const
|
||||
{
|
||||
return m_indicies;
|
||||
}
|
||||
|
||||
void Sprite::updateGeometry()
|
||||
{
|
||||
}
|
||||
131
source/Graphics/Text.cpp
Normal file
131
source/Graphics/Text.cpp
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
|
||||
#include <Spectre/Graphics/Text.h>
|
||||
|
||||
Text::Text() :
|
||||
m_font (NULL),
|
||||
m_color (255, 255, 255, 255)
|
||||
{
|
||||
}
|
||||
|
||||
Text::Text(const std::string text, const Font& font) :
|
||||
m_font (&font),
|
||||
m_color (255, 255, 255, 255),
|
||||
m_geometryNeedsUpdate (true)
|
||||
{
|
||||
}
|
||||
|
||||
void Text::setString(const std::string& string)
|
||||
{
|
||||
m_string = string;
|
||||
m_geometryNeedsUpdate = true;
|
||||
}
|
||||
|
||||
const std::string& Text::getString() const
|
||||
{
|
||||
return m_string;
|
||||
}
|
||||
|
||||
void Text::setFont(const Font& font)
|
||||
{
|
||||
m_font = &font;
|
||||
m_geometryNeedsUpdate = true;
|
||||
}
|
||||
const Font* Text::getFont() const
|
||||
{
|
||||
return m_font;
|
||||
}
|
||||
|
||||
void Text::setColor(const Color& color)
|
||||
{
|
||||
m_color = color;
|
||||
m_geometryNeedsUpdate = true;
|
||||
}
|
||||
|
||||
const Color& Text::getColor() const
|
||||
{
|
||||
return m_color;
|
||||
}
|
||||
|
||||
Vector2f Text::getSize() const
|
||||
{
|
||||
Vector2f size;
|
||||
|
||||
for(int i = 0; i < m_string.size(); i++) {
|
||||
|
||||
Font::Glyph glyph = m_font->getGlyph(m_string[i]);
|
||||
|
||||
float h = glyph.size.y;
|
||||
float w = glyph.advance;
|
||||
|
||||
if (h > size.y) {
|
||||
size.y = h;
|
||||
}
|
||||
size.x += w;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
const Texture* Text::getTexture() const
|
||||
{
|
||||
if (m_geometryNeedsUpdate) {
|
||||
updateGeometry();
|
||||
m_geometryNeedsUpdate = false;
|
||||
}
|
||||
return m_font->getTexture();
|
||||
}
|
||||
|
||||
const std::vector<unsigned short>& Text::getIndices() const
|
||||
{
|
||||
return m_indicies;
|
||||
}
|
||||
|
||||
const std::vector<Vertex2D>& Text::getVertices() const
|
||||
{
|
||||
if (m_geometryNeedsUpdate) {
|
||||
updateGeometry();
|
||||
m_geometryNeedsUpdate = false;
|
||||
}
|
||||
return m_vertices;
|
||||
}
|
||||
|
||||
void Text::updateGeometry() const
|
||||
{
|
||||
Vector2f origin;
|
||||
Vector2f size = getSize();
|
||||
Vector3f color = getColor().toRGBf();
|
||||
|
||||
m_vertices.clear();
|
||||
|
||||
for(int i = 0; i < m_string.length(); i++) {
|
||||
|
||||
Vertex2D v1, v2, v3, v4;
|
||||
|
||||
Font::Glyph glyph = m_font->getGlyph(m_string[i]);
|
||||
|
||||
if (glyph.texture) {
|
||||
|
||||
Vector2f vpos = origin + vec2f(glyph.offset.x, size.y - glyph.offset.y);
|
||||
|
||||
Vertex2D v1 = Vertex2D(vpos + vec2f( 0.0f, 0.0f), vec2f(glyph.texture_origin.x, glyph.texture_origin.y));
|
||||
Vertex2D v2 = Vertex2D(vpos + vec2f( glyph.size.x, 0.0f), vec2f(glyph.texture_origin.x + glyph.size.x, glyph.texture_origin.y));
|
||||
Vertex2D v3 = Vertex2D(vpos + vec2f( glyph.size.x, glyph.size.y), vec2f(glyph.texture_origin.x + glyph.size.x, glyph.texture_origin.y + glyph.size.y));
|
||||
Vertex2D v4 = Vertex2D(vpos + vec2f( 0.0f, glyph.size.y), vec2f(glyph.texture_origin.x, glyph.texture_origin.y + glyph.size.y));
|
||||
|
||||
v1.color = color;
|
||||
v2.color = color;
|
||||
v3.color = color;
|
||||
v4.color = color;
|
||||
|
||||
m_vertices.push_back(v1);
|
||||
m_vertices.push_back(v2);
|
||||
m_vertices.push_back(v3);
|
||||
m_vertices.push_back(v4);
|
||||
}
|
||||
|
||||
origin.x += glyph.advance;
|
||||
}
|
||||
|
||||
int x = 0;
|
||||
|
||||
|
||||
}
|
||||
250
source/Graphics/Texture.cpp
Normal file
250
source/Graphics/Texture.cpp
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
|
||||
#include <Spectre/Graphics/Image.h>
|
||||
#include <Spectre/Graphics/OpenGL.h>
|
||||
#include <Spectre/Graphics/Texture.h>
|
||||
|
||||
namespace {
|
||||
|
||||
GLint pixelFormatToInternal(enum PixelFormat format) {
|
||||
|
||||
switch(format) {
|
||||
case PixelFormat::PF_RGBA : return GL_RGBA8;
|
||||
case PixelFormat::PF_RGB : return GL_RGB8;
|
||||
case PixelFormat::PF_Alpha :
|
||||
default : return GL_R8;
|
||||
}
|
||||
}
|
||||
|
||||
GLint pixelFormatToGL(enum PixelFormat format) {
|
||||
|
||||
switch(format) {
|
||||
case PixelFormat::PF_RGBA : return GL_RGBA;
|
||||
case PixelFormat::PF_RGB : return GL_RGB;
|
||||
case PixelFormat::PF_Alpha :
|
||||
default : return GL_RED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Texture::Texture() :
|
||||
m_id (0),
|
||||
m_size (0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
Texture::~Texture()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
void Texture::create(unsigned width, unsigned heigth, PixelFormat format)
|
||||
{
|
||||
std::vector<unsigned char> empty_pixels(width * heigth);
|
||||
|
||||
if (!m_id) {
|
||||
glGenTextures(1, &m_id);
|
||||
}
|
||||
|
||||
enable();
|
||||
|
||||
m_size = vec2u(width, heigth);
|
||||
m_format = format;
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0,
|
||||
pixelFormatToInternal(format), width, heigth, 0,
|
||||
GL_RED, GL_UNSIGNED_BYTE, &empty_pixels[0]);
|
||||
|
||||
setSmooth(true);
|
||||
setRepeat(false);
|
||||
|
||||
disable();
|
||||
}
|
||||
|
||||
void Texture::create(const Vector2u& size, PixelFormat format)
|
||||
{
|
||||
create(size.x, size.y, format);
|
||||
}
|
||||
|
||||
void Texture::create(const Image& image)
|
||||
{
|
||||
GLint srcFormat;
|
||||
GLint glFormat;
|
||||
|
||||
if (!m_id) {
|
||||
glGenTextures(1, &m_id);
|
||||
}
|
||||
|
||||
m_size = image.getSize();
|
||||
m_format = image.getFormat();
|
||||
|
||||
enable();
|
||||
|
||||
if (image.getFormat() == PixelFormat::PF_Alpha) {
|
||||
glFormat = GL_R8;
|
||||
srcFormat = GL_RED;
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
} else {
|
||||
glFormat = GL_RGBA;
|
||||
srcFormat = GL_RGBA;
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
}
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, glFormat, m_size.x, m_size.y,
|
||||
0, srcFormat, GL_UNSIGNED_BYTE, image.getPixels());
|
||||
|
||||
setSmooth(true);
|
||||
setRepeat(false);
|
||||
|
||||
disable();
|
||||
}
|
||||
|
||||
void Texture::create(const std::string& filename, bool isTopDown)
|
||||
{
|
||||
Image img;
|
||||
|
||||
if (img.loadFromFile(filename)) {
|
||||
|
||||
// OpenGL expects bottom-up images, flip.
|
||||
if (isTopDown) {
|
||||
img.flipY();
|
||||
}
|
||||
|
||||
create(img);
|
||||
}
|
||||
}
|
||||
|
||||
void Texture::destroy()
|
||||
{
|
||||
if (m_id) {
|
||||
glDeleteTextures(1, &m_id);
|
||||
m_id = 0;
|
||||
}
|
||||
|
||||
m_size = vec2u(0, 0);
|
||||
m_format = PixelFormat::PF_Unknown;
|
||||
}
|
||||
|
||||
bool Texture::isEmpty() const
|
||||
{
|
||||
return m_id == 0 || m_size == vec2u(0, 0);
|
||||
}
|
||||
|
||||
const Vector2u& Texture::getSize() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
void Texture::update(vec2u pos, const Image& image)
|
||||
{
|
||||
GLuint format;
|
||||
|
||||
enable();
|
||||
|
||||
if (image.getFormat() == PixelFormat::PF_Alpha) {
|
||||
format = GL_RED;
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
} else {
|
||||
format = GL_RGBA;
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
}
|
||||
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, pos.x, pos.y,
|
||||
image.getWidth(), image.getHeight(),
|
||||
format, GL_UNSIGNED_BYTE, image.getPixels());
|
||||
|
||||
disable();
|
||||
}
|
||||
|
||||
void Texture::update(const Image& image)
|
||||
{
|
||||
update(Vector2u(0, 0), image);
|
||||
}
|
||||
|
||||
Image Texture::copyToImage() const
|
||||
{
|
||||
Image img;
|
||||
|
||||
if (!isEmpty()) {
|
||||
std::vector<unsigned char> pixels(m_size.x * m_size.y * 4);
|
||||
|
||||
enable();
|
||||
|
||||
//glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
|
||||
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
glGetTexImage(GL_TEXTURE_2D, 0,
|
||||
pixelFormatToGL(m_format),
|
||||
GL_UNSIGNED_BYTE,
|
||||
&pixels[0]);
|
||||
|
||||
img.create(m_format, m_size.x, m_size.y, &pixels[0]);
|
||||
|
||||
disable();
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
void Texture::setMinFilter(enum Filter filter)
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
|
||||
}
|
||||
|
||||
void Texture::setMagFilter(enum Filter filter)
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
|
||||
}
|
||||
|
||||
void Texture::setSmooth(bool value)
|
||||
{
|
||||
Filter filter = value ? Filter::Linear : Filter::Nearest;
|
||||
|
||||
setMinFilter(filter);
|
||||
setMagFilter(filter);
|
||||
}
|
||||
|
||||
void Texture::setWrapMode(enum WrapMode mode)
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mode);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mode);
|
||||
}
|
||||
|
||||
void Texture::setRepeat(bool value)
|
||||
{
|
||||
setWrapMode(value ? WrapMode::Repeat : WrapMode::ClampToEdge);
|
||||
}
|
||||
|
||||
void Texture::enable() const
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, m_id);
|
||||
}
|
||||
|
||||
void Texture::disable() const
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
static unsigned int _getMaxSize() {
|
||||
|
||||
GLint size;
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size);
|
||||
return static_cast<unsigned int>(size);
|
||||
}
|
||||
|
||||
unsigned int Texture::getMaxSize()
|
||||
{
|
||||
static unsigned maxSize = _getMaxSize();
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
bool Texture::operator<(const Texture* other) const
|
||||
{
|
||||
return m_id < other->m_id;
|
||||
}
|
||||
|
||||
bool Texture::operator!=(const Texture* other) const
|
||||
{
|
||||
return m_id != other->m_id;
|
||||
}
|
||||
94
source/Graphics/Transformable.cpp
Normal file
94
source/Graphics/Transformable.cpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
|
||||
#include <Spectre/Graphics/Transformable.h>
|
||||
|
||||
Transformable::Transformable() :
|
||||
m_position (0.0f, 0.0f),
|
||||
m_scale (1.0f, 1.0f),
|
||||
m_rotation (0.0f),
|
||||
m_transformNeedsUpdate (false)
|
||||
{
|
||||
}
|
||||
|
||||
Transformable::Transformable(const Vector2f& position) :
|
||||
m_position (position),
|
||||
m_scale (1.0f, 1.0f),
|
||||
m_rotation (0.0f),
|
||||
m_transformNeedsUpdate (false)
|
||||
{
|
||||
}
|
||||
|
||||
Transformable::~Transformable()
|
||||
{
|
||||
}
|
||||
|
||||
void Transformable::setPosition(const Vector2f& position)
|
||||
{
|
||||
m_position = position;
|
||||
m_transformNeedsUpdate = true;
|
||||
}
|
||||
|
||||
void Transformable::setPosition(float x, float y)
|
||||
{
|
||||
setPosition(Vector2f(x, y));
|
||||
}
|
||||
|
||||
void Transformable::move(const Vector2f& offset)
|
||||
{
|
||||
setPosition(m_position + offset);
|
||||
}
|
||||
const Vector2f& Transformable::getPosition() const
|
||||
{
|
||||
return m_position;
|
||||
}
|
||||
|
||||
void Transformable::setRotation(float angle)
|
||||
{
|
||||
m_rotation = angle;
|
||||
m_transformNeedsUpdate = true;
|
||||
}
|
||||
|
||||
void Transformable::rotate(float angle)
|
||||
{
|
||||
setRotation(m_rotation + angle);
|
||||
}
|
||||
|
||||
float Transformable::getRotation() const
|
||||
{
|
||||
return m_rotation;
|
||||
}
|
||||
|
||||
void Transformable::setScale(const Vector2f& scale)
|
||||
{
|
||||
m_scale = scale;
|
||||
m_transformNeedsUpdate = true;
|
||||
}
|
||||
|
||||
void Transformable::setScale(float scale)
|
||||
{
|
||||
setScale(Vector2f(scale));
|
||||
}
|
||||
|
||||
void Transformable::scale(const Vector2f& offset)
|
||||
{
|
||||
m_scale += offset;
|
||||
}
|
||||
|
||||
void Transformable::scale(float offset)
|
||||
{
|
||||
scale(Vector2f(offset));
|
||||
}
|
||||
|
||||
const Vector2f Transformable::getScale() const
|
||||
{
|
||||
return m_scale;
|
||||
}
|
||||
|
||||
const Transform& Transformable::getTransform() const
|
||||
{
|
||||
if (m_transformNeedsUpdate) {
|
||||
//m_transform.reset();
|
||||
m_transform.set(m_position, m_rotation, m_scale);
|
||||
m_transformNeedsUpdate = false;
|
||||
}
|
||||
return m_transform;
|
||||
}
|
||||
30
source/Graphics/Vertex2D.cpp
Normal file
30
source/Graphics/Vertex2D.cpp
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
#include <Spectre/Graphics/Vertex2D.h>
|
||||
|
||||
Vertex2D::Vertex2D() :
|
||||
position (0.0f),
|
||||
color (1.0f),
|
||||
uv (0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
Vertex2D::Vertex2D(Vector2f _pos) :
|
||||
position (_pos),
|
||||
color (1.0f),
|
||||
uv (0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
Vertex2D::Vertex2D(Vector2f _pos, Vector2f _uv) :
|
||||
position (_pos),
|
||||
color (1.0f),
|
||||
uv (_uv)
|
||||
{
|
||||
}
|
||||
|
||||
Vertex2D::Vertex2D(Vector2f _pos, Vector3f _color, Vector2f _uv) :
|
||||
position (_pos),
|
||||
color (_color),
|
||||
uv (_uv)
|
||||
{
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue