src/graphics moved to src/graphics/core
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
#include "Atlas.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include "../../maths/LMPacker.h"
|
||||
#include "Texture.h"
|
||||
#include "ImageData.h"
|
||||
|
||||
Atlas::Atlas(ImageData* image, std::unordered_map<std::string, UVRegion> regions)
|
||||
: texture(Texture::from(image)),
|
||||
image(image),
|
||||
regions(regions) {
|
||||
}
|
||||
|
||||
Atlas::~Atlas() {
|
||||
}
|
||||
|
||||
bool Atlas::has(const std::string& name) const {
|
||||
return regions.find(name) != regions.end();
|
||||
}
|
||||
|
||||
const UVRegion& Atlas::get(const std::string& name) const {
|
||||
return regions.at(name);
|
||||
}
|
||||
|
||||
Texture* Atlas::getTexture() const {
|
||||
return texture.get();
|
||||
}
|
||||
|
||||
ImageData* Atlas::getImage() const {
|
||||
return image.get();
|
||||
}
|
||||
|
||||
void AtlasBuilder::add(std::string name, ImageData* image) {
|
||||
entries.push_back(atlasentry{name, std::shared_ptr<ImageData>(image)});
|
||||
names.insert(name);
|
||||
}
|
||||
|
||||
bool AtlasBuilder::has(const std::string& name) const {
|
||||
return names.find(name) != names.end();
|
||||
}
|
||||
|
||||
Atlas* AtlasBuilder::build(uint extrusion, uint maxResolution) {
|
||||
auto sizes = std::make_unique<uint[]>(entries.size() * 2);
|
||||
uint index = 0;
|
||||
for (auto& entry : entries) {
|
||||
auto image = entry.image;
|
||||
sizes[index++] = image->getWidth();
|
||||
sizes[index++] = image->getHeight();
|
||||
}
|
||||
LMPacker packer(sizes.get(), entries.size()*2);
|
||||
sizes.reset(nullptr);
|
||||
|
||||
uint width = 32;
|
||||
uint height = 32;
|
||||
while (!packer.buildCompact(width, height, extrusion)) {
|
||||
if (width > height) {
|
||||
height *= 2;
|
||||
} else {
|
||||
width *= 2;
|
||||
}
|
||||
if (width > maxResolution || height > maxResolution) {
|
||||
throw std::runtime_error("max atlas resolution "+
|
||||
std::to_string(maxResolution)+" exceeded");
|
||||
}
|
||||
}
|
||||
|
||||
auto canvas = std::make_unique<ImageData>(ImageFormat::rgba8888, width, height);
|
||||
std::unordered_map<std::string, UVRegion> regions;
|
||||
std::vector<rectangle> rects = packer.getResult();
|
||||
for (uint i = 0; i < entries.size(); i++) {
|
||||
const rectangle& rect = rects[i];
|
||||
const atlasentry& entry = entries[rect.idx];
|
||||
uint x = rect.x;
|
||||
uint y = rect.y;
|
||||
uint w = rect.width;
|
||||
uint h = rect.height;
|
||||
canvas->blit(entry.image.get(), rect.x, rect.y);
|
||||
for (uint j = 0; j < extrusion; j++) {
|
||||
canvas->extrude(x - j, y - j, w + j*2, h + j*2);
|
||||
}
|
||||
float unitX = 1.0f / width;
|
||||
float unitY = 1.0f / height;
|
||||
regions[entry.name] = UVRegion(unitX * x, unitY * y,
|
||||
unitX * (x + w), unitY * (y + h));
|
||||
}
|
||||
return new Atlas(canvas.release(), regions);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef GRAPHICS_CORE_ATLAS_H_
|
||||
#define GRAPHICS_CORE_ATLAS_H_
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include "UVRegion.h"
|
||||
#include "../../typedefs.h"
|
||||
|
||||
class ImageData;
|
||||
class Texture;
|
||||
|
||||
class Atlas {
|
||||
std::unique_ptr<Texture> texture;
|
||||
std::unique_ptr<ImageData> image;
|
||||
std::unordered_map<std::string, UVRegion> regions;
|
||||
public:
|
||||
Atlas(ImageData* image, std::unordered_map<std::string, UVRegion> regions);
|
||||
~Atlas();
|
||||
|
||||
bool has(const std::string& name) const;
|
||||
const UVRegion& get(const std::string& name) const;
|
||||
|
||||
Texture* getTexture() const;
|
||||
ImageData* getImage() const;
|
||||
};
|
||||
|
||||
|
||||
struct atlasentry {
|
||||
std::string name;
|
||||
std::shared_ptr<ImageData> image;
|
||||
};
|
||||
|
||||
class AtlasBuilder {
|
||||
std::vector<atlasentry> entries;
|
||||
std::set<std::string> names;
|
||||
public:
|
||||
AtlasBuilder() {}
|
||||
void add(std::string name, ImageData* image);
|
||||
bool has(const std::string& name) const;
|
||||
const std::set<std::string>& getNames() { return names; };
|
||||
|
||||
Atlas* build(uint extrusion, uint maxResolution=8192);
|
||||
};
|
||||
|
||||
#endif // GRAPHICS_CORE_ATLAS_H_
|
||||
@@ -0,0 +1,311 @@
|
||||
#include "Batch2D.h"
|
||||
#include "Mesh.h"
|
||||
#include "Texture.h"
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
inline constexpr uint B2D_VERTEX_SIZE = 8;
|
||||
|
||||
Batch2D::Batch2D(size_t capacity) : capacity(capacity), color(1.0f){
|
||||
const vattr attrs[] = {
|
||||
{2}, {2}, {4}, {0}
|
||||
};
|
||||
|
||||
buffer = new float[capacity * B2D_VERTEX_SIZE];
|
||||
mesh = std::make_unique<Mesh>(buffer, 0, attrs);
|
||||
index = 0;
|
||||
|
||||
ubyte pixels[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
blank = std::make_unique<Texture>(pixels, 1, 1, ImageFormat::rgba8888);
|
||||
_texture = nullptr;
|
||||
}
|
||||
|
||||
Batch2D::~Batch2D(){
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
void Batch2D::begin(){
|
||||
_texture = nullptr;
|
||||
blank->bind();
|
||||
color = glm::vec4(1.0f);
|
||||
}
|
||||
|
||||
void Batch2D::vertex(
|
||||
float x, float y,
|
||||
float u, float v,
|
||||
float r, float g, float b, float a
|
||||
) {
|
||||
buffer[index++] = x;
|
||||
buffer[index++] = y;
|
||||
buffer[index++] = u;
|
||||
buffer[index++] = v;
|
||||
buffer[index++] = r;
|
||||
buffer[index++] = g;
|
||||
buffer[index++] = b;
|
||||
buffer[index++] = a;
|
||||
}
|
||||
void Batch2D::vertex(
|
||||
glm::vec2 point,
|
||||
glm::vec2 uvpoint,
|
||||
float r, float g, float b, float a
|
||||
) {
|
||||
buffer[index++] = point.x;
|
||||
buffer[index++] = point.y;
|
||||
buffer[index++] = uvpoint.x;
|
||||
buffer[index++] = uvpoint.y;
|
||||
buffer[index++] = r;
|
||||
buffer[index++] = g;
|
||||
buffer[index++] = b;
|
||||
buffer[index++] = a;
|
||||
}
|
||||
|
||||
void Batch2D::texture(Texture* new_texture){
|
||||
if (_texture == new_texture)
|
||||
return;
|
||||
flush(GL_TRIANGLES);
|
||||
_texture = new_texture;
|
||||
if (new_texture == nullptr)
|
||||
blank->bind();
|
||||
else
|
||||
new_texture->bind();
|
||||
}
|
||||
|
||||
void Batch2D::untexture() {
|
||||
texture(nullptr);
|
||||
}
|
||||
|
||||
void Batch2D::point(float x, float y, float r, float g, float b, float a){
|
||||
if (index + 6*B2D_VERTEX_SIZE >= capacity)
|
||||
flush(GL_TRIANGLES);
|
||||
|
||||
vertex(x, y, 0, 0, r,g,b,a);
|
||||
flush(GL_POINTS);
|
||||
}
|
||||
|
||||
void Batch2D::line(float x1, float y1, float x2, float y2, float r, float g, float b, float a){
|
||||
if (index + 6*B2D_VERTEX_SIZE >= capacity)
|
||||
flush(GL_TRIANGLES);
|
||||
|
||||
vertex(x1, y1, 0, 0, r,g,b,a);
|
||||
vertex(x2, y2, 1, 1, r,g,b,a);
|
||||
flush(GL_LINES);
|
||||
}
|
||||
|
||||
void Batch2D::rect(float x, float y, float w, float h){
|
||||
const float r = color.r;
|
||||
const float g = color.g;
|
||||
const float b = color.b;
|
||||
const float a = color.a;
|
||||
if (index + 6*B2D_VERTEX_SIZE >= capacity)
|
||||
flush(GL_TRIANGLES);
|
||||
|
||||
vertex(x, y, 0, 0, r,g,b,a);
|
||||
vertex(x, y+h, 0, 1, r,g,b,a);
|
||||
vertex(x+w, y+h, 1, 1, r,g,b,a);
|
||||
|
||||
vertex(x, y, 0, 0, r,g,b,a);
|
||||
vertex(x+w, y+h, 1, 1, r,g,b,a);
|
||||
vertex(x+w, y, 1, 0, r,g,b,a);
|
||||
}
|
||||
|
||||
void Batch2D::rect(
|
||||
float x, float y,
|
||||
float w, float h,
|
||||
float ox, float oy,
|
||||
float angle,
|
||||
UVRegion region,
|
||||
bool flippedX,
|
||||
bool flippedY,
|
||||
glm::vec4 tint
|
||||
) {
|
||||
if (index + 6*B2D_VERTEX_SIZE >= capacity)
|
||||
flush(GL_TRIANGLES);
|
||||
|
||||
float centerX = w*ox;
|
||||
float centerY = h*oy;
|
||||
float acenterX = w-centerX;
|
||||
float acenterY = h-centerY;
|
||||
|
||||
float _x1 = -centerX;
|
||||
float _y1 = -centerY;
|
||||
|
||||
float _x2 = -centerX;
|
||||
float _y2 = +acenterY;
|
||||
|
||||
float _x3 = +acenterX;
|
||||
float _y3 = +acenterY;
|
||||
|
||||
float _x4 = +acenterX;
|
||||
float _y4 = -centerY;
|
||||
|
||||
float x1,y1,x2,y2,x3,y3,x4,y4;
|
||||
|
||||
if (angle != 0) {
|
||||
float s = sin(angle);
|
||||
float c = cos(angle);
|
||||
|
||||
x1 = c * _x1 - s * _y1;
|
||||
y1 = s * _x1 + c * _y1;
|
||||
|
||||
x2 = c * _x2 - s * _y2;
|
||||
y2 = s * _x2 + c * _y2;
|
||||
|
||||
x3 = c * _x3 - s * _y3;
|
||||
y3 = s * _x3 + c * _y3;
|
||||
|
||||
x4 = x1 + (x3 - x2);
|
||||
y4 = y3 - (y2 - y1);
|
||||
} else {
|
||||
x1 = _x1;
|
||||
y1 = _y1;
|
||||
x2 = _x2;
|
||||
y2 = _y2;
|
||||
x3 = _x3;
|
||||
y3 = _y3;
|
||||
x4 = _x4;
|
||||
y4 = _y4;
|
||||
}
|
||||
|
||||
x1 += x; x2 += x; x3 += x; x4 += x;
|
||||
y1 += y; y2 += y; y3 += y; y4 += y;
|
||||
|
||||
float u1 = region.u1;
|
||||
float v1 = region.v1;
|
||||
float u2 = region.u1;
|
||||
float v2 = region.v2;
|
||||
float u3 = region.u2;
|
||||
float v3 = region.v2;
|
||||
float u4 = region.u2;
|
||||
float v4 = region.v1;
|
||||
|
||||
if (flippedX) {
|
||||
float temp = u1;
|
||||
u1 = u3;
|
||||
u4 = temp;
|
||||
u2 = u3;
|
||||
u3 = temp;
|
||||
}
|
||||
if (flippedY) {
|
||||
float temp = v1;
|
||||
v1 = v2;
|
||||
v4 = v2;
|
||||
v2 = temp;
|
||||
v3 = temp;
|
||||
}
|
||||
|
||||
vertex(x1, y1, u1, v1, tint.r, tint.g, tint.b, tint.a);
|
||||
vertex(x2, y2, u2, v2, tint.r, tint.g, tint.b, tint.a);
|
||||
vertex(x3, y3, u3, v3, tint.r, tint.g, tint.b, tint.a);
|
||||
|
||||
/* Right down triangle */
|
||||
vertex(x1, y1, u1, v1, tint.r, tint.g, tint.b, tint.a);
|
||||
vertex(x3, y3, u3, v3, tint.r, tint.g, tint.b, tint.a);
|
||||
vertex(x4, y4, u4, v4, tint.r, tint.g, tint.b, tint.a);
|
||||
}
|
||||
|
||||
void Batch2D::rect(
|
||||
float x, float y, float w, float h,
|
||||
float u, float v, float tx, float ty,
|
||||
float r, float g, float b, float a
|
||||
){
|
||||
if (index + 6*B2D_VERTEX_SIZE >= capacity)
|
||||
flush(GL_TRIANGLES);
|
||||
vertex(x, y, u, v+ty, r,g,b,a);
|
||||
vertex(x+w, y+h, u+tx, v, r,g,b,a);
|
||||
vertex(x, y+h, u, v, r,g,b,a);
|
||||
|
||||
vertex(x, y, u, v+ty, r,g,b,a);
|
||||
vertex(x+w, y, u+tx, v+ty, r,g,b,a);
|
||||
vertex(x+w, y+h, u+tx, v, r,g,b,a);
|
||||
}
|
||||
|
||||
void Batch2D::rect(
|
||||
float x, float y, float w, float h,
|
||||
float r0, float g0, float b0,
|
||||
float r1, float g1, float b1,
|
||||
float r2, float g2, float b2,
|
||||
float r3, float g3, float b3,
|
||||
float r4, float g4, float b4, int sh
|
||||
){
|
||||
if (index + 30*B2D_VERTEX_SIZE >= capacity)
|
||||
flush(GL_TRIANGLES);
|
||||
glm::vec2 v0(x+h/2,y+h/2);
|
||||
glm::vec2 v1(x+w-sh,y);
|
||||
glm::vec2 v2(x+sh,y);
|
||||
glm::vec2 v3(x,y+sh);
|
||||
glm::vec2 v4(x,y+h-sh);
|
||||
glm::vec2 v5(x+sh,y+h);
|
||||
glm::vec2 v6(x+w-h/2,y+h/2);
|
||||
glm::vec2 v7(x+w-sh,y+h);
|
||||
glm::vec2 v8(x+w,y+h-sh);
|
||||
glm::vec2 v9(x+w,y+sh);
|
||||
|
||||
vertex(v0, glm::vec2(0, 0), r1,g1,b1,1.0f);
|
||||
vertex(v6, glm::vec2(0, 0), r1,g1,b1,1.0f);
|
||||
vertex(v1, glm::vec2(0, 0), r1,g1,b1,1.0f);
|
||||
|
||||
vertex(v0, glm::vec2(0, 0), r1,g1,b1,1.0f);
|
||||
vertex(v1, glm::vec2(0, 0), r1,g1,b1,1.0f);
|
||||
vertex(v2, glm::vec2(0, 0), r1,g1,b1,1.0f);
|
||||
|
||||
vertex(v0, glm::vec2(0, 0), r0,g0,b0,1.0f);
|
||||
vertex(v2, glm::vec2(0, 0), r0,g0,b0,1.0f);
|
||||
vertex(v3, glm::vec2(0, 0), r0,g0,b0,1.0f);
|
||||
|
||||
vertex(v0, glm::vec2(0, 0), r1,g1,b1,1.0f);
|
||||
vertex(v3, glm::vec2(0, 0), r1,g1,b1,1.0f);
|
||||
vertex(v4, glm::vec2(0, 0), r1,g1,b1,1.0f);
|
||||
|
||||
vertex(v0, glm::vec2(0, 0), r2,g2,b2,1.0f);
|
||||
vertex(v4, glm::vec2(0, 0), r2,g2,b2,1.0f);
|
||||
vertex(v5, glm::vec2(0, 0), r2,g2,b2,1.0f);
|
||||
|
||||
vertex(v0, glm::vec2(0, 0), r3,g3,b3,1.0f);
|
||||
vertex(v5, glm::vec2(0, 0), r3,g3,b3,1.0f);
|
||||
vertex(v6, glm::vec2(0, 0), r3,g3,b3,1.0f);
|
||||
|
||||
vertex(v6, glm::vec2(0, 0), r3,g3,b3,1.0f);
|
||||
vertex(v5, glm::vec2(0, 0), r3,g3,b3,1.0f);
|
||||
vertex(v7, glm::vec2(0, 0), r3,g3,b3,1.0f);
|
||||
|
||||
vertex(v6, glm::vec2(0, 0), r4,g4,b4,1.0f);
|
||||
vertex(v7, glm::vec2(0, 0), r4,g4,b4,1.0f);
|
||||
vertex(v8, glm::vec2(0, 0), r4,g4,b4,1.0f);
|
||||
|
||||
vertex(v6, glm::vec2(0, 0), r3,g3,b3,1.0f);
|
||||
vertex(v8, glm::vec2(0, 0), r3,g3,b3,1.0f);
|
||||
vertex(v9, glm::vec2(0, 0), r3,g3,b3,1.0f);
|
||||
|
||||
vertex(v6, glm::vec2(0, 0), r2,g2,b2,1.0f);
|
||||
vertex(v9, glm::vec2(0, 0), r2,g2,b2,1.0f);
|
||||
vertex(v1, glm::vec2(0, 0), r2,g2,b2,1.0f);
|
||||
}
|
||||
|
||||
void Batch2D::sprite(float x, float y, float w, float h, const UVRegion& region, glm::vec4 tint){
|
||||
rect(x, y, w, h, region.u1, region.v1, region.u2-region.u1, region.v2-region.v1, tint.r, tint.g, tint.b, tint.a);
|
||||
}
|
||||
|
||||
void Batch2D::sprite(float x, float y, float w, float h, int atlasRes, int index, glm::vec4 tint){
|
||||
float scale = 1.0f / (float)atlasRes;
|
||||
float u = (index % atlasRes) * scale;
|
||||
float v = 1.0f - ((index / atlasRes) * scale) - scale;
|
||||
rect(x, y, w, h, u, v, scale, scale, tint.r, tint.g, tint.b, tint.a);
|
||||
}
|
||||
|
||||
void Batch2D::flush(unsigned int gl_primitive) {
|
||||
if (index == 0)
|
||||
return;
|
||||
mesh->reload(buffer, index / B2D_VERTEX_SIZE);
|
||||
mesh->draw(gl_primitive);
|
||||
index = 0;
|
||||
}
|
||||
|
||||
void Batch2D::flush() {
|
||||
flush(GL_TRIANGLES);
|
||||
}
|
||||
|
||||
void Batch2D::lineWidth(float width) {
|
||||
glLineWidth(width);
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
#ifndef GRAPHICS_CORE_BATCH2D_H_
|
||||
#define GRAPHICS_CORE_BATCH2D_H_
|
||||
|
||||
#include <memory>
|
||||
#include <stdlib.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "UVRegion.h"
|
||||
|
||||
class Mesh;
|
||||
class Texture;
|
||||
|
||||
class Batch2D {
|
||||
float* buffer;
|
||||
size_t capacity;
|
||||
std::unique_ptr<Mesh> mesh;
|
||||
std::unique_ptr<Texture> blank;
|
||||
size_t index;
|
||||
glm::vec4 color;
|
||||
Texture* _texture;
|
||||
|
||||
void vertex(
|
||||
float x, float y,
|
||||
float u, float v,
|
||||
float r, float g, float b, float a
|
||||
);
|
||||
|
||||
void vertex(
|
||||
glm::vec2 point,
|
||||
glm::vec2 uvpoint,
|
||||
float r, float g, float b, float a
|
||||
);
|
||||
|
||||
public:
|
||||
Batch2D(size_t capacity);
|
||||
~Batch2D();
|
||||
|
||||
void begin();
|
||||
void texture(Texture* texture);
|
||||
void untexture();
|
||||
void sprite(float x, float y, float w, float h, const UVRegion& region, glm::vec4 tint);
|
||||
void sprite(float x, float y, float w, float h, int atlasRes, int index, glm::vec4 tint);
|
||||
void point(float x, float y, float r, float g, float b, float a);
|
||||
|
||||
inline void setColor(glm::vec4 color) {
|
||||
this->color = color;
|
||||
}
|
||||
inline glm::vec4 getColor() const {
|
||||
return color;
|
||||
}
|
||||
|
||||
void line(
|
||||
float x1, float y1,
|
||||
float x2, float y2,
|
||||
float r, float g, float b, float a
|
||||
);
|
||||
|
||||
void rect(
|
||||
float x, float y,
|
||||
float w, float h,
|
||||
float ox, float oy,
|
||||
float angle, UVRegion region,
|
||||
bool flippedX, bool flippedY,
|
||||
glm::vec4 tint
|
||||
);
|
||||
|
||||
void rect(float x, float y, float w, float h);
|
||||
|
||||
void rect(
|
||||
float x, float y, float w, float h,
|
||||
float u, float v, float tx, float ty,
|
||||
float r, float g, float b, float a
|
||||
);
|
||||
|
||||
void rect(
|
||||
float x, float y, float w, float h,
|
||||
float r0, float g0, float b0,
|
||||
float r1, float g1, float b1,
|
||||
float r2, float g2, float b2,
|
||||
float r3, float g3, float b3,
|
||||
float r4, float g4, float b4, int sh
|
||||
);
|
||||
|
||||
void flush(unsigned int gl_primitive);
|
||||
void flush();
|
||||
|
||||
void lineWidth(float width);
|
||||
};
|
||||
|
||||
#endif // GRAPHICS_CORE_BATCH2D_H_
|
||||
@@ -0,0 +1,256 @@
|
||||
#include "Batch3D.h"
|
||||
|
||||
#include "Mesh.h"
|
||||
#include "Texture.h"
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include "../../typedefs.h"
|
||||
|
||||
inline constexpr uint B3D_VERTEX_SIZE = 9;
|
||||
|
||||
Batch3D::Batch3D(size_t capacity)
|
||||
: capacity(capacity) {
|
||||
const vattr attrs[] = {
|
||||
{3}, {2}, {4}, {0}
|
||||
};
|
||||
|
||||
buffer = new float[capacity * B3D_VERTEX_SIZE];
|
||||
mesh = std::make_unique<Mesh>(buffer, 0, attrs);
|
||||
index = 0;
|
||||
|
||||
ubyte pixels[] = {
|
||||
255, 255, 255, 255,
|
||||
};
|
||||
blank = std::make_unique<Texture>(pixels, 1, 1, ImageFormat::rgba8888);
|
||||
_texture = nullptr;
|
||||
}
|
||||
|
||||
Batch3D::~Batch3D(){
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
void Batch3D::begin(){
|
||||
_texture = nullptr;
|
||||
blank->bind();
|
||||
}
|
||||
|
||||
void Batch3D::vertex(float x, float y, float z, float u, float v,
|
||||
float r, float g, float b, float a) {
|
||||
buffer[index++] = x;
|
||||
buffer[index++] = y;
|
||||
buffer[index++] = z;
|
||||
buffer[index++] = u;
|
||||
buffer[index++] = v;
|
||||
buffer[index++] = r;
|
||||
buffer[index++] = g;
|
||||
buffer[index++] = b;
|
||||
buffer[index++] = a;
|
||||
}
|
||||
void Batch3D::vertex(glm::vec3 coord, float u, float v,
|
||||
float r, float g, float b, float a) {
|
||||
buffer[index++] = coord.x;
|
||||
buffer[index++] = coord.y;
|
||||
buffer[index++] = coord.z;
|
||||
buffer[index++] = u;
|
||||
buffer[index++] = v;
|
||||
buffer[index++] = r;
|
||||
buffer[index++] = g;
|
||||
buffer[index++] = b;
|
||||
buffer[index++] = a;
|
||||
}
|
||||
void Batch3D::vertex(glm::vec3 point,
|
||||
glm::vec2 uvpoint,
|
||||
float r, float g, float b, float a) {
|
||||
buffer[index++] = point.x;
|
||||
buffer[index++] = point.y;
|
||||
buffer[index++] = point.z;
|
||||
buffer[index++] = uvpoint.x;
|
||||
buffer[index++] = uvpoint.y;
|
||||
buffer[index++] = r;
|
||||
buffer[index++] = g;
|
||||
buffer[index++] = b;
|
||||
buffer[index++] = a;
|
||||
}
|
||||
|
||||
void Batch3D::face(
|
||||
const glm::vec3& coord,
|
||||
float w, float h,
|
||||
const glm::vec3& axisX,
|
||||
const glm::vec3& axisY,
|
||||
const UVRegion& region,
|
||||
const glm::vec4& tint
|
||||
) {
|
||||
if (index + B3D_VERTEX_SIZE * 6 > capacity) {
|
||||
flush();
|
||||
}
|
||||
vertex(coord, region.u1, region.v1,
|
||||
tint.r, tint.g, tint.b, tint.a);
|
||||
vertex(coord + axisX * w, region.u2, region.v1,
|
||||
tint.r, tint.g, tint.b, tint.a);
|
||||
vertex(coord + axisX * w + axisY * h, region.u2, region.v2,
|
||||
tint.r, tint.g, tint.b, tint.a);
|
||||
|
||||
vertex(coord, region.u1, region.v1,
|
||||
tint.r, tint.g, tint.b, tint.a);
|
||||
vertex(coord + axisX * w + axisY * h, region.u2, region.v2,
|
||||
tint.r, tint.g, tint.b, tint.a);
|
||||
vertex(coord + axisY * h, region.u1, region.v2,
|
||||
tint.r, tint.g, tint.b, tint.a);
|
||||
}
|
||||
|
||||
void Batch3D::texture(Texture* new_texture){
|
||||
if (_texture == new_texture)
|
||||
return;
|
||||
flush();
|
||||
_texture = new_texture;
|
||||
if (new_texture == nullptr)
|
||||
blank->bind();
|
||||
else
|
||||
new_texture->bind();
|
||||
}
|
||||
|
||||
void Batch3D::sprite(
|
||||
glm::vec3 pos,
|
||||
glm::vec3 up,
|
||||
glm::vec3 right,
|
||||
float w, float h,
|
||||
const UVRegion& uv,
|
||||
glm::vec4 color
|
||||
){
|
||||
const float r = color.r;
|
||||
const float g = color.g;
|
||||
const float b = color.b;
|
||||
const float a = color.a;
|
||||
if (index + 6*B3D_VERTEX_SIZE >= capacity) {
|
||||
flush();
|
||||
}
|
||||
|
||||
vertex(pos.x - right.x * w - up.x * h,
|
||||
pos.y - right.y * w - up.y * h,
|
||||
pos.z - right.z * w - up.z * h,
|
||||
uv.u1, uv.v1,
|
||||
r,g,b,a);
|
||||
|
||||
vertex(pos.x + right.x * w + up.x * h,
|
||||
pos.y + right.y * w + up.y * h,
|
||||
pos.z + right.z * w + up.z * h,
|
||||
uv.u2, uv.v2,
|
||||
r,g,b,a);
|
||||
|
||||
vertex(pos.x - right.x * w + up.x * h,
|
||||
pos.y - right.y * w + up.y * h,
|
||||
pos.z - right.z * w + up.z * h,
|
||||
uv.u1, uv.v2,
|
||||
r,g,b,a);
|
||||
|
||||
vertex(pos.x - right.x * w - up.x * h,
|
||||
pos.y - right.y * w - up.y * h,
|
||||
pos.z - right.z * w - up.z * h,
|
||||
uv.u1, uv.v1,
|
||||
r,g,b,a);
|
||||
|
||||
vertex(pos.x + right.x * w - up.x * h,
|
||||
pos.y + right.y * w - up.y * h,
|
||||
pos.z + right.z * w - up.z * h,
|
||||
uv.u2, uv.v1,
|
||||
r,g,b,a);
|
||||
|
||||
vertex(pos.x + right.x * w + up.x * h,
|
||||
pos.y + right.y * w + up.y * h,
|
||||
pos.z + right.z * w + up.z * h,
|
||||
uv.u2, uv.v2,
|
||||
r,g,b,a);
|
||||
}
|
||||
|
||||
inline glm::vec4 do_tint(float value) {
|
||||
return glm::vec4(value, value, value, 1.0f);
|
||||
}
|
||||
|
||||
void Batch3D::xSprite(float w, float h, const UVRegion& uv, const glm::vec4 tint, bool shading) {
|
||||
face(
|
||||
glm::vec3(-w * 0.25f, 0.0f, -w * 0.25f),
|
||||
w, h,
|
||||
glm::vec3(1, 0, 0),
|
||||
glm::vec3(0, 1, 0),
|
||||
uv, (shading ? do_tint(1.0f)*tint : tint)
|
||||
);
|
||||
face(
|
||||
glm::vec3(w * 0.25f, 0.0f, w * 0.5f - w * 0.25f),
|
||||
w, h,
|
||||
glm::vec3(0, 0, -1),
|
||||
glm::vec3(0, 1, 0),
|
||||
uv, (shading ? do_tint(0.9f)*tint : tint)
|
||||
);
|
||||
}
|
||||
|
||||
void Batch3D::cube(
|
||||
const glm::vec3 coord,
|
||||
const glm::vec3 size,
|
||||
const UVRegion(&texfaces)[6],
|
||||
const glm::vec4 tint,
|
||||
bool shading
|
||||
) {
|
||||
const glm::vec3 X(1.0f, 0.0f, 0.0f);
|
||||
const glm::vec3 Y(0.0f, 1.0f, 0.0f);
|
||||
const glm::vec3 Z(0.0f, 0.0f, 1.0f);
|
||||
|
||||
face(
|
||||
coord+glm::vec3(0.0f, 0.0f, 0.0f),
|
||||
size.x, size.y, X, Y, texfaces[5],
|
||||
(shading ? do_tint(0.8)*tint : tint)
|
||||
);
|
||||
face(
|
||||
coord+glm::vec3(size.x, 0.0f, -size.z),
|
||||
size.x, size.y, -X, Y, texfaces[4],
|
||||
(shading ? do_tint(0.8f)*tint : tint)
|
||||
);
|
||||
face(
|
||||
coord+glm::vec3(0.0f, size.y, 0.0f),
|
||||
size.x, size.z, X, -Z, texfaces[3],
|
||||
(shading ? do_tint(1.0f)*tint : tint)
|
||||
);
|
||||
face(
|
||||
coord+glm::vec3(0.0f, 0.0f, -size.z),
|
||||
size.x, size.z, X, Z, texfaces[2],
|
||||
(shading ? do_tint(0.7f)*tint : tint)
|
||||
);
|
||||
face(
|
||||
coord+glm::vec3(0.0f, 0.0f, -size.z),
|
||||
size.z, size.y, Z, Y, texfaces[0],
|
||||
(shading ? do_tint(0.9f)*tint : tint)
|
||||
);
|
||||
face(
|
||||
coord+glm::vec3(size.x, 0.0f, 0.0f),
|
||||
size.z, size.y, -Z, Y, texfaces[1],
|
||||
(shading ? do_tint(0.9f)*tint : tint)
|
||||
);
|
||||
}
|
||||
|
||||
void Batch3D::blockCube(
|
||||
const glm::vec3 size,
|
||||
const UVRegion(&texfaces)[6],
|
||||
const glm::vec4 tint,
|
||||
bool shading
|
||||
) {
|
||||
cube((1.0f - size) * -0.5f, size, texfaces, tint, shading);
|
||||
}
|
||||
|
||||
void Batch3D::point(glm::vec3 coord, glm::vec2 uv, glm::vec4 tint) {
|
||||
vertex(coord, uv, tint.r, tint.g, tint.b, tint.a);
|
||||
}
|
||||
|
||||
void Batch3D::point(glm::vec3 coord, glm::vec4 tint) {
|
||||
point(coord, glm::vec2(), tint);
|
||||
}
|
||||
|
||||
void Batch3D::flush() {
|
||||
mesh->reload(buffer, index / B3D_VERTEX_SIZE);
|
||||
mesh->draw();
|
||||
index = 0;
|
||||
}
|
||||
|
||||
void Batch3D::flushPoints() {
|
||||
mesh->reload(buffer, index / B3D_VERTEX_SIZE);
|
||||
mesh->draw(GL_POINTS);
|
||||
index = 0;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef GRAPHICS_CORE_BATCH3D_H_
|
||||
#define GRAPHICS_CORE_BATCH3D_H_
|
||||
|
||||
#include "UVRegion.h"
|
||||
#include "../../typedefs.h"
|
||||
|
||||
#include <memory>
|
||||
#include <stdlib.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class Mesh;
|
||||
class Texture;
|
||||
|
||||
class Batch3D {
|
||||
float* buffer;
|
||||
size_t capacity;
|
||||
std::unique_ptr<Mesh> mesh;
|
||||
std::unique_ptr<Texture> blank;
|
||||
size_t index;
|
||||
|
||||
Texture* _texture;
|
||||
|
||||
void vertex(float x, float y, float z,
|
||||
float u, float v,
|
||||
float r, float g, float b, float a);
|
||||
void vertex(glm::vec3 coord,
|
||||
float u, float v,
|
||||
float r, float g, float b, float a);
|
||||
void vertex(glm::vec3 point, glm::vec2 uvpoint,
|
||||
float r, float g, float b, float a);
|
||||
|
||||
void face(const glm::vec3& coord, float w, float h,
|
||||
const glm::vec3& axisX,
|
||||
const glm::vec3& axisY,
|
||||
const UVRegion& region,
|
||||
const glm::vec4& tint);
|
||||
|
||||
public:
|
||||
Batch3D(size_t capacity);
|
||||
~Batch3D();
|
||||
|
||||
void begin();
|
||||
void texture(Texture* texture);
|
||||
void sprite(glm::vec3 pos, glm::vec3 up, glm::vec3 right, float w, float h, const UVRegion& uv, glm::vec4 tint);
|
||||
void xSprite(float w, float h, const UVRegion& uv, const glm::vec4 tint, bool shading=true);
|
||||
void cube(const glm::vec3 coords, const glm::vec3 size, const UVRegion(&texfaces)[6], const glm::vec4 tint, bool shading=true);
|
||||
void blockCube(const glm::vec3 size, const UVRegion(&texfaces)[6], const glm::vec4 tint, bool shading=true);
|
||||
void point(glm::vec3 pos, glm::vec2 uv, glm::vec4 tint);
|
||||
void point(glm::vec3 pos, glm::vec4 tint);
|
||||
void flush();
|
||||
void flushPoints();
|
||||
};
|
||||
|
||||
#endif // GRAPHICS_CORE_BATCH3D_H_
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "Cubemap.h"
|
||||
#include "gl_util.h"
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
Cubemap::Cubemap(uint width, uint height, ImageFormat imageFormat)
|
||||
: Texture(0, width, height)
|
||||
{
|
||||
glGenTextures(1, &id);
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, id);
|
||||
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||
|
||||
uint format = gl::to_gl_format(imageFormat);
|
||||
for (uint face = 0; face < 6; face++) {
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
|
||||
0,
|
||||
format,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
format,
|
||||
GL_UNSIGNED_BYTE,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void Cubemap::bind(){
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, id);
|
||||
}
|
||||
|
||||
void Cubemap::unbind() {
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef GRAPHICS_CORE_CUBEMAP_H_
|
||||
#define GRAPHICS_CORE_CUBEMAP_H_
|
||||
|
||||
#include "Texture.h"
|
||||
|
||||
/// @brief Cubemap texture
|
||||
class Cubemap : public Texture {
|
||||
public:
|
||||
Cubemap(uint width, uint height, ImageFormat format);
|
||||
|
||||
virtual void bind() override;
|
||||
virtual void unbind() override;
|
||||
};
|
||||
|
||||
#endif // GRAPHICS_CORE_CUBEMAP_H_
|
||||
@@ -0,0 +1,97 @@
|
||||
#include "Font.h"
|
||||
#include "Texture.h"
|
||||
#include "Batch2D.h"
|
||||
|
||||
inline constexpr uint GLYPH_SIZE = 16;
|
||||
inline constexpr uint MAX_CODEPAGES = 10000; // idk ho many codepages unicode has
|
||||
inline constexpr glm::vec4 SHADOW_TINT(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
Font::Font(std::vector<std::unique_ptr<Texture>> pages, int lineHeight, int yoffset)
|
||||
: lineHeight(lineHeight), yoffset(yoffset), pages(std::move(pages)) {
|
||||
}
|
||||
|
||||
Font::~Font(){
|
||||
}
|
||||
|
||||
int Font::getYOffset() const {
|
||||
return yoffset;
|
||||
}
|
||||
|
||||
int Font::getLineHeight() const {
|
||||
return lineHeight;
|
||||
}
|
||||
|
||||
bool Font::isPrintableChar(uint codepoint) const {
|
||||
switch (codepoint){
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\f':
|
||||
case '\r':
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int Font::calcWidth(std::wstring text, size_t length) {
|
||||
return std::min(text.length(), length) * 8;
|
||||
}
|
||||
|
||||
void Font::draw(Batch2D* batch, std::wstring text, int x, int y) {
|
||||
draw(batch, text, x, y, FontStyle::none);
|
||||
}
|
||||
|
||||
static inline void drawGlyph(Batch2D* batch, int x, int y, uint c, FontStyle style) {
|
||||
switch (style){
|
||||
case FontStyle::none:
|
||||
break;
|
||||
case FontStyle::shadow:
|
||||
batch->sprite(x+1, y+1, GLYPH_SIZE, GLYPH_SIZE, 16, c, SHADOW_TINT);
|
||||
break;
|
||||
case FontStyle::outline:
|
||||
for (int oy = -1; oy <= 1; oy++){
|
||||
for (int ox = -1; ox <= 1; ox++){
|
||||
if (ox || oy) {
|
||||
batch->sprite(x+ox, y+oy, GLYPH_SIZE, GLYPH_SIZE, 16, c, SHADOW_TINT);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
batch->sprite(x, y, GLYPH_SIZE, GLYPH_SIZE, 16, c, batch->getColor());
|
||||
}
|
||||
|
||||
void Font::draw(Batch2D* batch, std::wstring text, int x, int y, FontStyle style) {
|
||||
draw(batch, std::wstring_view(text.c_str(), text.length()), x, y, style);
|
||||
}
|
||||
|
||||
void Font::draw(Batch2D* batch, std::wstring_view text, int x, int y, FontStyle style) {
|
||||
uint page = 0;
|
||||
uint next = MAX_CODEPAGES;
|
||||
int init_x = x;
|
||||
do {
|
||||
for (uint c : text){
|
||||
if (!isPrintableChar(c)) {
|
||||
x += 8;
|
||||
continue;
|
||||
}
|
||||
uint charpage = c >> 8;
|
||||
if (charpage == page){
|
||||
Texture* texture = pages[charpage].get();
|
||||
if (texture == nullptr){
|
||||
texture = pages[0].get();
|
||||
}
|
||||
batch->texture(texture);
|
||||
drawGlyph(batch, x, y, c, style);
|
||||
}
|
||||
else if (charpage > page && charpage < next){
|
||||
next = charpage;
|
||||
}
|
||||
x += 8;//getGlyphWidth(c);
|
||||
}
|
||||
page = next;
|
||||
next = MAX_CODEPAGES;
|
||||
x = init_x;
|
||||
} while (page < MAX_CODEPAGES);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef GRAPHICS_CORE_FONT_H_
|
||||
#define GRAPHICS_CORE_FONT_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "../../typedefs.h"
|
||||
|
||||
class Texture;
|
||||
class Batch2D;
|
||||
|
||||
enum class FontStyle {
|
||||
none,
|
||||
shadow,
|
||||
outline
|
||||
};
|
||||
|
||||
class Font {
|
||||
int lineHeight;
|
||||
int yoffset;
|
||||
public:
|
||||
std::vector<std::unique_ptr<Texture>> pages;
|
||||
Font(std::vector<std::unique_ptr<Texture>> pages, int lineHeight, int yoffset);
|
||||
~Font();
|
||||
|
||||
int getLineHeight() const;
|
||||
int getYOffset() const;
|
||||
|
||||
/// @brief Calculate text width in pixels
|
||||
/// @param text selected text
|
||||
/// @param length max text chunk length (default: no limit)
|
||||
/// @return pixel width of the text
|
||||
int calcWidth(std::wstring text, size_t length=-1);
|
||||
|
||||
/// @brief Check if character is visible (non-whitespace)
|
||||
/// @param codepoint character unicode codepoint
|
||||
bool isPrintableChar(uint codepoint) const;
|
||||
void draw(Batch2D* batch, std::wstring text, int x, int y);
|
||||
void draw(Batch2D* batch, std::wstring text, int x, int y, FontStyle style);
|
||||
void draw(Batch2D* batch, std::wstring_view text, int x, int y, FontStyle style);
|
||||
};
|
||||
|
||||
#endif // GRAPHICS_CORE_FONT_H_
|
||||
@@ -0,0 +1,84 @@
|
||||
#include "Framebuffer.h"
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include "Texture.h"
|
||||
|
||||
Framebuffer::Framebuffer(uint fbo, uint depth, std::unique_ptr<Texture> texture)
|
||||
: fbo(fbo), depth(depth), texture(std::move(texture))
|
||||
{
|
||||
if (texture) {
|
||||
width = texture->getWidth();
|
||||
height = texture->getHeight();
|
||||
} else {
|
||||
width = 0;
|
||||
height = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Framebuffer::Framebuffer(uint width, uint height, bool alpha)
|
||||
: width(width), height(height)
|
||||
{
|
||||
glGenFramebuffers(1, &fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
|
||||
// Setup color attachment (texture)
|
||||
GLuint tex;
|
||||
format = alpha ? GL_RGBA : GL_RGB;
|
||||
glGenTextures(1, &tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
|
||||
texture = std::make_unique<Texture>(tex, width, height);
|
||||
|
||||
// Setup depth attachment
|
||||
glGenRenderbuffers(1, &depth);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, depth);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
Framebuffer::~Framebuffer() {
|
||||
glDeleteFramebuffers(1, &fbo);
|
||||
glDeleteRenderbuffers(1, &depth);
|
||||
}
|
||||
|
||||
void Framebuffer::bind() {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
}
|
||||
|
||||
void Framebuffer::unbind() {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void Framebuffer::resize(uint width, uint height) {
|
||||
if (this->width == width && this->height == height) {
|
||||
return;
|
||||
}
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, depth);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
texture->bind();
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, nullptr);
|
||||
texture->unbind();
|
||||
}
|
||||
|
||||
Texture* Framebuffer::getTexture() const {
|
||||
return texture.get();
|
||||
}
|
||||
|
||||
uint Framebuffer::getWidth() const {
|
||||
return width;
|
||||
}
|
||||
|
||||
uint Framebuffer::getHeight() const {
|
||||
return height;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef GRAPHICS_CORE_FRAMEBUFFER_H_
|
||||
#define GRAPHICS_CORE_FRAMEBUFFER_H_
|
||||
|
||||
#include "../../typedefs.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class Texture;
|
||||
|
||||
class Framebuffer {
|
||||
uint fbo;
|
||||
uint depth;
|
||||
uint width;
|
||||
uint height;
|
||||
uint format;
|
||||
std::unique_ptr<Texture> texture;
|
||||
public:
|
||||
Framebuffer(uint fbo, uint depth, std::unique_ptr<Texture> texture);
|
||||
Framebuffer(uint width, uint height, bool alpha=false);
|
||||
~Framebuffer();
|
||||
|
||||
/// @brief Use framebuffer
|
||||
void bind();
|
||||
|
||||
/// @brief Stop using framebuffer
|
||||
void unbind();
|
||||
|
||||
/// @brief Update framebuffer texture size
|
||||
/// @param width new width
|
||||
/// @param height new height
|
||||
void resize(uint width, uint height);
|
||||
|
||||
/// @brief Get framebuffer color attachment
|
||||
Texture* getTexture() const;
|
||||
|
||||
/// @brief Get framebuffer width
|
||||
uint getWidth() const;
|
||||
/// @brief Get framebuffer height
|
||||
uint getHeight() const;
|
||||
};
|
||||
|
||||
#endif // GRAPHICS_CORE_FRAMEBUFFER_H_
|
||||
@@ -0,0 +1,132 @@
|
||||
#include "GfxContext.h"
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include "Batch2D.h"
|
||||
#include "Framebuffer.h"
|
||||
|
||||
GfxContext::GfxContext(
|
||||
const GfxContext* parent,
|
||||
const Viewport& viewport,
|
||||
Batch2D* g2d
|
||||
) : parent(parent),
|
||||
viewport(viewport),
|
||||
g2d(g2d)
|
||||
{}
|
||||
|
||||
GfxContext::~GfxContext() {
|
||||
if (g2d) {
|
||||
g2d->flush();
|
||||
}
|
||||
|
||||
while (scissorsCount--) {
|
||||
Window::popScissor();
|
||||
}
|
||||
|
||||
if (parent == nullptr)
|
||||
return;
|
||||
|
||||
if (fbo != parent->fbo) {
|
||||
if (fbo) {
|
||||
fbo->unbind();
|
||||
}
|
||||
if (parent->fbo) {
|
||||
parent->fbo->bind();
|
||||
}
|
||||
}
|
||||
|
||||
Window::viewport(
|
||||
0, 0,
|
||||
parent->viewport.getWidth(),
|
||||
parent->viewport.getHeight()
|
||||
);
|
||||
|
||||
if (depthMask != parent->depthMask) {
|
||||
glDepthMask(parent->depthMask);
|
||||
}
|
||||
if (depthTest != parent->depthTest) {
|
||||
if (depthTest) glDisable(GL_DEPTH_TEST);
|
||||
else glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
if (cullFace != parent->cullFace) {
|
||||
if (cullFace) glDisable(GL_CULL_FACE);
|
||||
else glEnable(GL_CULL_FACE);
|
||||
}
|
||||
if (blendMode != parent->blendMode) {
|
||||
Window::setBlendMode(parent->blendMode);
|
||||
}
|
||||
}
|
||||
|
||||
const Viewport& GfxContext::getViewport() const {
|
||||
return viewport;
|
||||
}
|
||||
|
||||
Batch2D* GfxContext::getBatch2D() const {
|
||||
return g2d;
|
||||
}
|
||||
|
||||
GfxContext GfxContext::sub() const {
|
||||
auto ctx = GfxContext(this, viewport, g2d);
|
||||
ctx.depthTest = depthTest;
|
||||
ctx.cullFace = cullFace;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void GfxContext::setViewport(const Viewport& viewport) {
|
||||
this->viewport = viewport;
|
||||
Window::viewport(
|
||||
0, 0,
|
||||
viewport.getWidth(),
|
||||
viewport.getHeight()
|
||||
);
|
||||
}
|
||||
|
||||
void GfxContext::setFramebuffer(Framebuffer* fbo) {
|
||||
if (this->fbo == fbo)
|
||||
return;
|
||||
this->fbo = fbo;
|
||||
if (fbo) {
|
||||
fbo->bind();
|
||||
}
|
||||
}
|
||||
|
||||
void GfxContext::setDepthMask(bool flag) {
|
||||
if (depthMask == flag)
|
||||
return;
|
||||
depthMask = flag;
|
||||
glDepthMask(GL_FALSE + flag);
|
||||
}
|
||||
|
||||
void GfxContext::setDepthTest(bool flag) {
|
||||
if (depthTest == flag)
|
||||
return;
|
||||
depthTest = flag;
|
||||
if (flag) {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
} else {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
void GfxContext::setCullFace(bool flag) {
|
||||
if (cullFace == flag)
|
||||
return;
|
||||
cullFace = flag;
|
||||
if (flag) {
|
||||
glEnable(GL_CULL_FACE);
|
||||
} else {
|
||||
glDisable(GL_CULL_FACE);
|
||||
}
|
||||
}
|
||||
|
||||
void GfxContext::setBlendMode(blendmode mode) {
|
||||
if (blendMode == mode)
|
||||
return;
|
||||
blendMode = mode;
|
||||
Window::setBlendMode(mode);
|
||||
}
|
||||
|
||||
void GfxContext::setScissors(glm::vec4 area) {
|
||||
Window::pushScissor(area);
|
||||
scissorsCount++;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef GRAPHICS_CORE_GFX_CONTEXT_H_
|
||||
#define GRAPHICS_CORE_GFX_CONTEXT_H_
|
||||
|
||||
#include "Viewport.h"
|
||||
#include "../../window/Window.h"
|
||||
#include "../../typedefs.h"
|
||||
|
||||
class Batch2D;
|
||||
class Framebuffer;
|
||||
|
||||
class GfxContext {
|
||||
const GfxContext* parent;
|
||||
Viewport viewport;
|
||||
Batch2D* const g2d;
|
||||
Framebuffer* fbo = nullptr;
|
||||
bool depthMask = true;
|
||||
bool depthTest = false;
|
||||
bool cullFace = false;
|
||||
blendmode blendMode = blendmode::normal;
|
||||
int scissorsCount = 0;
|
||||
public:
|
||||
GfxContext(const GfxContext* parent, const Viewport& viewport, Batch2D* g2d);
|
||||
~GfxContext();
|
||||
|
||||
Batch2D* getBatch2D() const;
|
||||
const Viewport& getViewport() const;
|
||||
GfxContext sub() const;
|
||||
|
||||
void setViewport(const Viewport& viewport);
|
||||
void setFramebuffer(Framebuffer* fbo);
|
||||
void setDepthMask(bool flag);
|
||||
void setDepthTest(bool flag);
|
||||
void setCullFace(bool flag);
|
||||
void setBlendMode(blendmode mode);
|
||||
void setScissors(glm::vec4 area);
|
||||
};
|
||||
|
||||
#endif // GRAPHICS_CORE_GFX_CONTEXT_H_
|
||||
@@ -0,0 +1,305 @@
|
||||
#include "ImageData.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdexcept>
|
||||
|
||||
inline int min(int a, int b) {
|
||||
return (a < b) ? a : b;
|
||||
}
|
||||
|
||||
inline int max(int a, int b) {
|
||||
return (a > b) ? a : b;
|
||||
}
|
||||
|
||||
ImageData::ImageData(ImageFormat format, uint width, uint height)
|
||||
: format(format), width(width), height(height) {
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888: data = new ubyte[width*height*3]{}; break;
|
||||
case ImageFormat::rgba8888: data = new ubyte[width*height*4]{}; break;
|
||||
default:
|
||||
throw std::runtime_error("format is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
ImageData::ImageData(ImageFormat format, uint width, uint height, void* data)
|
||||
: format(format), width(width), height(height), data(data) {
|
||||
}
|
||||
|
||||
ImageData::~ImageData() {
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888:
|
||||
case ImageFormat::rgba8888:
|
||||
delete[] (ubyte*)data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ImageData::flipX() {
|
||||
uint size;
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888:
|
||||
case ImageFormat::rgba8888: {
|
||||
size = (format == ImageFormat::rgba8888) ? 4 : 3;
|
||||
ubyte* pixels = (ubyte*)data;
|
||||
for (uint y = 0; y < height; y++) {
|
||||
for (uint x = 0; x < width/2; x++) {
|
||||
for (uint c = 0; c < size; c++) {
|
||||
ubyte temp = pixels[(y * width + x) * size + c];
|
||||
pixels[(y * width + x) * size + c] = pixels[(y * width + (width - x - 1)) * size + c];
|
||||
pixels[(y * width + (width - x - 1)) * size + c] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error("format is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
void ImageData::flipY() {
|
||||
uint size;
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888:
|
||||
case ImageFormat::rgba8888: {
|
||||
size = (format == ImageFormat::rgba8888) ? 4 : 3;
|
||||
ubyte* pixels = (ubyte*)data;
|
||||
for (uint y = 0; y < height/2; y++) {
|
||||
for (uint x = 0; x < width; x++) {
|
||||
for (uint c = 0; c < size; c++) {
|
||||
ubyte temp = pixels[(y * width + x) * size + c];
|
||||
pixels[(y * width + x) * size + c] =
|
||||
pixels[((height-y-1) * width + x) * size + c];
|
||||
pixels[((height-y-1) * width + x) * size + c] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error("format is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
void ImageData::blit(const ImageData* image, int x, int y) {
|
||||
if (format == image->format) {
|
||||
blitMatchingFormat(image, x, y);
|
||||
return;
|
||||
}
|
||||
if (format == ImageFormat::rgba8888 &&
|
||||
image->format == ImageFormat::rgb888) {
|
||||
blitRGB_on_RGBA(image, x, y);
|
||||
return;
|
||||
}
|
||||
throw std::runtime_error("mismatching format");
|
||||
}
|
||||
|
||||
void ImageData::blitRGB_on_RGBA(const ImageData* image, int x, int y) {
|
||||
ubyte* pixels = static_cast<ubyte*>(data);
|
||||
ubyte* source = static_cast<ubyte*>(image->getData());
|
||||
uint srcwidth = image->getWidth();
|
||||
uint srcheight = image->getHeight();
|
||||
|
||||
for (uint srcy = max(0, -y); (int)srcy < min(srcheight, height-y); srcy++) {
|
||||
for (uint srcx = max(0, -x); (int)srcx < min(srcwidth, width-x); srcx++) {
|
||||
uint dstx = srcx + x;
|
||||
uint dsty = srcy + y;
|
||||
uint dstidx = (dsty * width + dstx) * 4;
|
||||
uint srcidx = (srcy * srcwidth + srcx) * 3;
|
||||
for (uint c = 0; c < 3; c++) {
|
||||
pixels[dstidx + c] = source[srcidx + c];
|
||||
}
|
||||
pixels[dstidx + 3] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImageData::blitMatchingFormat(const ImageData* image, int x, int y) {
|
||||
uint comps;
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888: comps = 3; break;
|
||||
case ImageFormat::rgba8888: comps = 4; break;
|
||||
default:
|
||||
throw std::runtime_error("only unsigned byte formats supported");
|
||||
}
|
||||
ubyte* pixels = static_cast<ubyte*>(data);
|
||||
ubyte* source = static_cast<ubyte*>(image->getData());
|
||||
uint srcwidth = image->getWidth();
|
||||
uint srcheight = image->getHeight();
|
||||
|
||||
for (uint srcy = max(0, -y); (int)srcy < min(srcheight, height-y); srcy++) {
|
||||
for (uint srcx = max(0, -x); (int)srcx < min(srcwidth, width-x); srcx++) {
|
||||
uint dstx = srcx + x;
|
||||
uint dsty = srcy + y;
|
||||
uint dstidx = (dsty * width + dstx) * comps;
|
||||
uint srcidx = (srcy * srcwidth + srcx) * comps;
|
||||
for (uint c = 0; c < comps; c++) {
|
||||
pixels[dstidx + c] = source[srcidx + c];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Extrude rectangle zone border pixels out by 1 pixel.
|
||||
Used to remove atlas texture border artifacts */
|
||||
void ImageData::extrude(int x, int y, int w, int h) {
|
||||
uint comps;
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888: comps = 3; break;
|
||||
case ImageFormat::rgba8888: comps = 4; break;
|
||||
default:
|
||||
throw std::runtime_error("only unsigned byte formats supported");
|
||||
}
|
||||
ubyte* pixels = static_cast<ubyte*>(data);
|
||||
|
||||
int rx = x + w - 1;
|
||||
int ry = y + h - 1;
|
||||
|
||||
// top-left pixel
|
||||
if (x > 0 && (uint)x < width && y > 0 && (uint)y < height) {
|
||||
uint srcidx = (y * width + x) * comps;
|
||||
uint dstidx = ((y - 1) * width + x - 1) * comps;
|
||||
for (uint c = 0; c < comps; c++) {
|
||||
pixels[dstidx + c] = pixels[srcidx + c];
|
||||
}
|
||||
}
|
||||
|
||||
// top-right pixel
|
||||
if (rx >= 0 && (uint)rx < width-1 && y > 0 && (uint)y < height) {
|
||||
uint srcidx = (y * width + rx) * comps;
|
||||
uint dstidx = ((y - 1) * width + rx + 1) * comps;
|
||||
for (uint c = 0; c < comps; c++) {
|
||||
pixels[dstidx + c] = pixels[srcidx + c];
|
||||
}
|
||||
}
|
||||
|
||||
// bottom-left pixel
|
||||
if (x > 0 && (uint)x < width && ry >= 0 && (uint)ry < height-1) {
|
||||
uint srcidx = (ry * width + x) * comps;
|
||||
uint dstidx = ((ry + 1) * width + x - 1) * comps;
|
||||
for (uint c = 0; c < comps; c++) {
|
||||
pixels[dstidx + c] = pixels[srcidx + c];
|
||||
}
|
||||
}
|
||||
|
||||
// bottom-right pixel
|
||||
if (rx >= 0 && (uint)rx < width-1 && ry >= 0 && (uint)ry < height-1) {
|
||||
uint srcidx = (ry * width + rx) * comps;
|
||||
uint dstidx = ((ry + 1) * width + rx + 1) * comps;
|
||||
for (uint c = 0; c < comps; c++) {
|
||||
pixels[dstidx + c] = pixels[srcidx + c];
|
||||
}
|
||||
}
|
||||
|
||||
// left border
|
||||
if (x > 0 && (uint)x < width) {
|
||||
for (uint ey = max(y, 0); (int)ey < y + h; ey++) {
|
||||
uint srcidx = (ey * width + x) * comps;
|
||||
uint dstidx = (ey * width + x - 1) * comps;
|
||||
for (uint c = 0; c < comps; c++) {
|
||||
pixels[dstidx + c] = pixels[srcidx + c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// top border
|
||||
if (y > 0 && (uint)y < height) {
|
||||
for (uint ex = max(x, 0); (int)ex < x + w; ex++) {
|
||||
uint srcidx = (y * width + ex) * comps;
|
||||
uint dstidx = ((y-1) * width + ex) * comps;
|
||||
for (uint c = 0; c < comps; c++) {
|
||||
pixels[dstidx + c] = pixels[srcidx + c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// right border
|
||||
if (rx >= 0 && (uint)rx < width-1) {
|
||||
for (uint ey = max(y, 0); (int)ey < y + h; ey++) {
|
||||
uint srcidx = (ey * width + rx) * comps;
|
||||
uint dstidx = (ey * width + rx + 1) * comps;
|
||||
for (uint c = 0; c < comps; c++) {
|
||||
pixels[dstidx + c] = pixels[srcidx + c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// bottom border
|
||||
if (ry >= 0 && (uint)ry < height-1) {
|
||||
for (uint ex = max(x, 0); (int)ex < x + w; ex++) {
|
||||
uint srcidx = (ry * width + ex) * comps;
|
||||
uint dstidx = ((ry+1) * width + ex) * comps;
|
||||
for (uint c = 0; c < comps; c++) {
|
||||
pixels[dstidx + c] = pixels[srcidx + c];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImageData::fixAlphaColor() {
|
||||
ubyte* pixels = static_cast<ubyte*>(data);
|
||||
|
||||
// Fixing black transparent pixels for Mip-Mapping
|
||||
for (uint ly = 0; ly < height-1; ly++) {
|
||||
for (uint lx = 0; lx < width-1; lx++) {
|
||||
if (pixels[((ly) * width + lx) * 4 + 3]) {
|
||||
for (int c = 0; c < 3; c++) {
|
||||
int val = pixels[((ly) + + lx) * 4 + c];
|
||||
if (pixels[((ly) * width + lx + 1) * 4 + 3] == 0)
|
||||
pixels[((ly) * width + lx + 1) * 4 + c] = val;
|
||||
if (pixels[((ly + 1) * width + lx) * 4 + 3] == 0)
|
||||
pixels[((ly + 1) * width + lx) * 4 + c] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImageData* add_atlas_margins(ImageData* image, int grid_size) {
|
||||
// RGBA is only supported
|
||||
assert(image->getFormat() == ImageFormat::rgba8888);
|
||||
assert(image->getWidth() == image->getHeight());
|
||||
|
||||
int srcwidth = image->getWidth();
|
||||
int srcheight = image->getHeight();
|
||||
int dstwidth = srcwidth + grid_size * 2;
|
||||
int dstheight = srcheight + grid_size * 2;
|
||||
|
||||
const ubyte* srcdata = (const ubyte*)image->getData();
|
||||
ubyte* dstdata = new ubyte[dstwidth*dstheight * 4];
|
||||
|
||||
int imgres = image->getWidth() / grid_size;
|
||||
for (int row = 0; row < grid_size; row++) {
|
||||
for (int col = 0; col < grid_size; col++) {
|
||||
int sox = col * imgres;
|
||||
int soy = row * imgres;
|
||||
int dox = 1 + col * (imgres + 2);
|
||||
int doy = 1 + row * (imgres + 2);
|
||||
for (int ly = -1; ly <= imgres; ly++) {
|
||||
for (int lx = -1; lx <= imgres; lx++) {
|
||||
int sy = max(min(ly, imgres-1), 0);
|
||||
int sx = max(min(lx, imgres-1), 0);
|
||||
for (int c = 0; c < 4; c++)
|
||||
dstdata[((doy+ly) * dstwidth + dox + lx) * 4 + c] =
|
||||
srcdata[((soy+sy) * srcwidth + sox + sx) * 4 + c];
|
||||
}
|
||||
}
|
||||
|
||||
// Fixing black transparent pixels for Mip-Mapping
|
||||
for (int ly = 0; ly < imgres; ly++) {
|
||||
for (int lx = 0; lx < imgres; lx++) {
|
||||
if (srcdata[((soy+ly) * srcwidth + sox + lx) * 4 + 3]) {
|
||||
for (int c = 0; c < 3; c++) {
|
||||
int val = srcdata[((soy+ly) * srcwidth + sox + lx) * 4 + c];
|
||||
if (dstdata[((doy+ly) * dstwidth + dox + lx + 1) * 4 + 3] == 0)
|
||||
dstdata[((doy+ly) * dstwidth + dox + lx + 1) * 4 + c] = val;
|
||||
if (dstdata[((doy+ly + 1) * dstwidth + dox + lx) * 4 + 3] == 0)
|
||||
dstdata[((doy+ly + 1) * dstwidth + dox + lx) * 4 + c] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ImageData(image->getFormat(), dstwidth, dstheight, dstdata);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef GRAPHICS_CORE_IMAGE_DATA_H_
|
||||
#define GRAPHICS_CORE_IMAGE_DATA_H_
|
||||
|
||||
#include "../../typedefs.h"
|
||||
|
||||
enum class ImageFormat {
|
||||
rgb888,
|
||||
rgba8888
|
||||
};
|
||||
|
||||
class ImageData {
|
||||
ImageFormat format;
|
||||
uint width;
|
||||
uint height;
|
||||
void* data;
|
||||
public:
|
||||
ImageData(ImageFormat format, uint width, uint height);
|
||||
ImageData(ImageFormat format, uint width, uint height, void* data);
|
||||
~ImageData();
|
||||
|
||||
void flipX();
|
||||
void flipY();
|
||||
|
||||
void blitRGB_on_RGBA(const ImageData* image, int x, int y);
|
||||
void blitMatchingFormat(const ImageData* image, int x, int y);
|
||||
void blit(const ImageData* image, int x, int y);
|
||||
void extrude(int x, int y, int w, int h);
|
||||
void fixAlphaColor();
|
||||
|
||||
void* getData() const {
|
||||
return data;
|
||||
}
|
||||
|
||||
ImageFormat getFormat() const {
|
||||
return format;
|
||||
}
|
||||
|
||||
uint getWidth() const {
|
||||
return width;
|
||||
}
|
||||
|
||||
uint getHeight() const {
|
||||
return height;
|
||||
}
|
||||
};
|
||||
|
||||
extern ImageData* add_atlas_margins(ImageData* image, int grid_size);
|
||||
|
||||
#endif // GRAPHICS_CORE_IMAGE_DATA_H_
|
||||
@@ -0,0 +1,79 @@
|
||||
#include "LineBatch.h"
|
||||
#include "Mesh.h"
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
inline constexpr uint LB_VERTEX_SIZE = (3+4);
|
||||
|
||||
LineBatch::LineBatch(size_t capacity) : capacity(capacity) {
|
||||
const vattr attrs[] = { {3},{4}, {0} };
|
||||
buffer = new float[capacity * LB_VERTEX_SIZE * 2];
|
||||
mesh = std::make_unique<Mesh>(buffer, 0, attrs);
|
||||
index = 0;
|
||||
}
|
||||
|
||||
LineBatch::~LineBatch(){
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
void LineBatch::line(
|
||||
float x1, float y1,
|
||||
float z1, float x2,
|
||||
float y2, float z2,
|
||||
float r, float g, float b, float a
|
||||
) {
|
||||
if (index + LB_VERTEX_SIZE * 2 >= capacity) {
|
||||
render();
|
||||
}
|
||||
buffer[index] = x1;
|
||||
buffer[index+1] = y1;
|
||||
buffer[index+2] = z1;
|
||||
buffer[index+3] = r;
|
||||
buffer[index+4] = g;
|
||||
buffer[index+5] = b;
|
||||
buffer[index+6] = a;
|
||||
index += LB_VERTEX_SIZE;
|
||||
|
||||
buffer[index] = x2;
|
||||
buffer[index+1] = y2;
|
||||
buffer[index+2] = z2;
|
||||
buffer[index+3] = r;
|
||||
buffer[index+4] = g;
|
||||
buffer[index+5] = b;
|
||||
buffer[index+6] = a;
|
||||
index += LB_VERTEX_SIZE;
|
||||
}
|
||||
|
||||
void LineBatch::box(float x, float y, float z, float w, float h, float d,
|
||||
float r, float g, float b, float a) {
|
||||
w *= 0.5f;
|
||||
h *= 0.5f;
|
||||
d *= 0.5f;
|
||||
|
||||
line(x-w, y-h, z-d, x+w, y-h, z-d, r,g,b,a);
|
||||
line(x-w, y+h, z-d, x+w, y+h, z-d, r,g,b,a);
|
||||
line(x-w, y-h, z+d, x+w, y-h, z+d, r,g,b,a);
|
||||
line(x-w, y+h, z+d, x+w, y+h, z+d, r,g,b,a);
|
||||
|
||||
line(x-w, y-h, z-d, x-w, y+h, z-d, r,g,b,a);
|
||||
line(x+w, y-h, z-d, x+w, y+h, z-d, r,g,b,a);
|
||||
line(x-w, y-h, z+d, x-w, y+h, z+d, r,g,b,a);
|
||||
line(x+w, y-h, z+d, x+w, y+h, z+d, r,g,b,a);
|
||||
|
||||
line(x-w, y-h, z-d, x-w, y-h, z+d, r,g,b,a);
|
||||
line(x+w, y-h, z-d, x+w, y-h, z+d, r,g,b,a);
|
||||
line(x-w, y+h, z-d, x-w, y+h, z+d, r,g,b,a);
|
||||
line(x+w, y+h, z-d, x+w, y+h, z+d, r,g,b,a);
|
||||
}
|
||||
|
||||
void LineBatch::render(){
|
||||
if (index == 0)
|
||||
return;
|
||||
mesh->reload(buffer, index / LB_VERTEX_SIZE);
|
||||
mesh->draw(GL_LINES);
|
||||
index = 0;
|
||||
}
|
||||
|
||||
void LineBatch::lineWidth(float width) {
|
||||
glLineWidth(width);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef GRAPHICS_CORE_LINEBATCH_H_
|
||||
#define GRAPHICS_CORE_LINEBATCH_H_
|
||||
|
||||
#include <memory>
|
||||
#include <stdlib.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class Mesh;
|
||||
|
||||
class LineBatch {
|
||||
std::unique_ptr<Mesh> mesh;
|
||||
float* buffer;
|
||||
size_t index;
|
||||
size_t capacity;
|
||||
public:
|
||||
LineBatch(size_t capacity=4096);
|
||||
~LineBatch();
|
||||
|
||||
inline void line(const glm::vec3 a, const glm::vec3 b, const glm::vec4 color) {
|
||||
line(a.x, a.y, a.z, b.x, b.y, b.z, color.r, color.g, color.b, color.a);
|
||||
}
|
||||
void line(float x1, float y1, float z1, float x2, float y2, float z2,
|
||||
float r, float g, float b, float a);
|
||||
void box(float x, float y, float z, float w, float h, float d,
|
||||
float r, float g, float b, float a);
|
||||
|
||||
inline void box(glm::vec3 xyz, glm::vec3 whd, glm::vec4 rgba) {
|
||||
box(xyz.x, xyz.y, xyz.z, whd.x, whd.y, whd.z,
|
||||
rgba.r, rgba.g, rgba.b, rgba.a);
|
||||
}
|
||||
|
||||
void render();
|
||||
void lineWidth(float width);
|
||||
};
|
||||
|
||||
#endif // GRAPHICS_CORE_LINEBATCH_H_
|
||||
@@ -0,0 +1,75 @@
|
||||
#include "Mesh.h"
|
||||
#include <GL/glew.h>
|
||||
|
||||
int Mesh::meshesCount = 0;
|
||||
|
||||
Mesh::Mesh(const float* vertexBuffer, size_t vertices, const int* indexBuffer, size_t indices, const vattr* attrs) :
|
||||
ibo(0),
|
||||
vertices(vertices),
|
||||
indices(indices)
|
||||
{
|
||||
meshesCount++;
|
||||
vertexSize = 0;
|
||||
for (int i = 0; attrs[i].size; i++) {
|
||||
vertexSize += attrs[i].size;
|
||||
}
|
||||
|
||||
glGenVertexArrays(1, &vao);
|
||||
glGenBuffers(1, &vbo);
|
||||
|
||||
reload(vertexBuffer, vertices, indexBuffer, indices);
|
||||
|
||||
// attributes
|
||||
int offset = 0;
|
||||
for (int i = 0; attrs[i].size; i++) {
|
||||
int size = attrs[i].size;
|
||||
glVertexAttribPointer(i, size, GL_FLOAT, GL_FALSE, vertexSize * sizeof(float), (GLvoid*)(offset * sizeof(float)));
|
||||
glEnableVertexAttribArray(i);
|
||||
offset += size;
|
||||
}
|
||||
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
Mesh::~Mesh(){
|
||||
meshesCount--;
|
||||
glDeleteVertexArrays(1, &vao);
|
||||
glDeleteBuffers(1, &vbo);
|
||||
if (ibo != 0) glDeleteBuffers(1, &ibo);
|
||||
}
|
||||
|
||||
void Mesh::reload(const float* vertexBuffer, size_t vertices, const int* indexBuffer, size_t indices){
|
||||
glBindVertexArray(vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
if (vertexBuffer != nullptr && vertices != 0) {
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertexSize * vertices, vertexBuffer, GL_STATIC_DRAW);
|
||||
}
|
||||
else {
|
||||
glBufferData(GL_ARRAY_BUFFER, 0, {}, GL_STATIC_DRAW);
|
||||
}
|
||||
if (indexBuffer != nullptr && indices != 0) {
|
||||
if (ibo == 0) glGenBuffers(1, &ibo);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * indices, indexBuffer, GL_STATIC_DRAW);
|
||||
}
|
||||
else if (ibo != 0) {
|
||||
glDeleteBuffers(1, &ibo);
|
||||
}
|
||||
this->vertices = vertices;
|
||||
this->indices = indices;
|
||||
}
|
||||
|
||||
void Mesh::draw(unsigned int primitive){
|
||||
glBindVertexArray(vao);
|
||||
if (ibo != 0) {
|
||||
glDrawElements(primitive, indices, GL_UNSIGNED_INT, 0);
|
||||
}
|
||||
else {
|
||||
glDrawArrays(primitive, 0, vertices);
|
||||
}
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void Mesh::draw() {
|
||||
draw(GL_TRIANGLES);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef GRAPHICS_CORE_MESH_H_
|
||||
#define GRAPHICS_CORE_MESH_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "../../typedefs.h"
|
||||
|
||||
struct vattr {
|
||||
ubyte size;
|
||||
};
|
||||
|
||||
class Mesh {
|
||||
unsigned int vao;
|
||||
unsigned int vbo;
|
||||
unsigned int ibo;
|
||||
size_t vertices;
|
||||
size_t indices;
|
||||
size_t vertexSize;
|
||||
public:
|
||||
Mesh(const float* vertexBuffer, size_t vertices, const int* indexBuffer, size_t indices, const vattr* attrs);
|
||||
Mesh(const float* vertexBuffer, size_t vertices, const vattr* attrs) :
|
||||
Mesh(vertexBuffer, vertices, nullptr, 0, attrs) {};
|
||||
~Mesh();
|
||||
|
||||
/// @brief Update GL vertex and index buffers data without changing VAO attributes
|
||||
/// @param vertexBuffer vertex data buffer
|
||||
/// @param vertices number of vertices in new buffer
|
||||
/// @param indexBuffer indices buffer
|
||||
/// @param indices number of values in indices buffer
|
||||
void reload(const float* vertexBuffer, size_t vertices, const int* indexBuffer = nullptr, size_t indices = 0);
|
||||
|
||||
/// @brief Draw mesh with specified primitives type
|
||||
/// @param primitive primitives type
|
||||
void draw(unsigned int primitive);
|
||||
|
||||
/// @brief Draw mesh as triangles
|
||||
void draw();
|
||||
|
||||
/// @brief Total numbers of alive mesh objects
|
||||
static int meshesCount;
|
||||
};
|
||||
|
||||
#endif // GRAPHICS_CORE_MESH_H_
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "PostProcessing.h"
|
||||
#include "Mesh.h"
|
||||
#include "Shader.h"
|
||||
#include "Texture.h"
|
||||
#include "Framebuffer.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
PostProcessing::PostProcessing() {
|
||||
// Fullscreen quad mesh bulding
|
||||
float vertices[] {
|
||||
-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
|
||||
-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f
|
||||
};
|
||||
vattr attrs[] {{2}, {0}};
|
||||
quadMesh = std::make_unique<Mesh>(vertices, 6, attrs);
|
||||
}
|
||||
|
||||
PostProcessing::~PostProcessing() {
|
||||
}
|
||||
|
||||
void PostProcessing::use(GfxContext& context) {
|
||||
const auto& vp = context.getViewport();
|
||||
if (fbo) {
|
||||
fbo->resize(vp.getWidth(), vp.getHeight());
|
||||
} else {
|
||||
fbo = std::make_unique<Framebuffer>(vp.getWidth(), vp.getHeight());
|
||||
}
|
||||
context.setFramebuffer(fbo.get());
|
||||
}
|
||||
|
||||
void PostProcessing::render(const GfxContext& context, Shader* screenShader) {
|
||||
if (fbo == nullptr) {
|
||||
throw std::runtime_error("'use(...)' was never called");
|
||||
}
|
||||
|
||||
const auto& viewport = context.getViewport();
|
||||
screenShader->use();
|
||||
screenShader->uniform2i("u_screenSize", viewport.size());
|
||||
fbo->getTexture()->bind();
|
||||
quadMesh->draw();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef GRAPHICS_CORE_POST_PROCESSING_H_
|
||||
#define GRAPHICS_CORE_POST_PROCESSING_H_
|
||||
|
||||
#include "Viewport.h"
|
||||
#include "GfxContext.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class Mesh;
|
||||
class Shader;
|
||||
class Framebuffer;
|
||||
|
||||
/// @brief Framebuffer with blitting with shaders.
|
||||
/// @attention Current implementation does not support multiple render passes
|
||||
/// for multiple effects. Will be implemented in v0.21
|
||||
class PostProcessing {
|
||||
/// @brief Main framebuffer (lasy field)
|
||||
std::unique_ptr<Framebuffer> fbo;
|
||||
/// @brief Fullscreen quad mesh as the post-processing canvas
|
||||
std::unique_ptr<Mesh> quadMesh;
|
||||
public:
|
||||
PostProcessing();
|
||||
~PostProcessing();
|
||||
|
||||
/// @brief Prepare and bind framebuffer
|
||||
/// @param context graphics context will be modified
|
||||
void use(GfxContext& context);
|
||||
|
||||
/// @brief Render fullscreen quad using the passed shader
|
||||
/// with framebuffer texture bound
|
||||
/// @param context graphics context
|
||||
/// @param screenShader shader used for fullscreen quad
|
||||
/// @throws std::runtime_error if use(...) wasn't called before
|
||||
void render(const GfxContext& context, Shader* screenShader);
|
||||
};
|
||||
|
||||
#endif // GRAPHICS_CORE_POST_PROCESSING_H_
|
||||
@@ -0,0 +1,133 @@
|
||||
#include "Shader.h"
|
||||
|
||||
#include <exception>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <filesystem>
|
||||
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "../../coders/GLSLExtension.h"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
GLSLExtension* Shader::preprocessor = new GLSLExtension();
|
||||
|
||||
Shader::Shader(unsigned int id) : id(id){
|
||||
}
|
||||
|
||||
Shader::~Shader(){
|
||||
glDeleteProgram(id);
|
||||
}
|
||||
|
||||
void Shader::use(){
|
||||
glUseProgram(id);
|
||||
}
|
||||
|
||||
void Shader::uniformMatrix(std::string name, glm::mat4 matrix){
|
||||
GLuint transformLoc = glGetUniformLocation(id, name.c_str());
|
||||
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(matrix));
|
||||
}
|
||||
|
||||
void Shader::uniform1i(std::string name, int x){
|
||||
GLuint transformLoc = glGetUniformLocation(id, name.c_str());
|
||||
glUniform1i(transformLoc, x);
|
||||
}
|
||||
|
||||
void Shader::uniform1f(std::string name, float x){
|
||||
GLuint transformLoc = glGetUniformLocation(id, name.c_str());
|
||||
glUniform1f(transformLoc, x);
|
||||
}
|
||||
|
||||
void Shader::uniform2f(std::string name, float x, float y){
|
||||
GLuint transformLoc = glGetUniformLocation(id, name.c_str());
|
||||
glUniform2f(transformLoc, x, y);
|
||||
}
|
||||
|
||||
void Shader::uniform2f(std::string name, glm::vec2 xy){
|
||||
GLuint transformLoc = glGetUniformLocation(id, name.c_str());
|
||||
glUniform2f(transformLoc, xy.x, xy.y);
|
||||
}
|
||||
|
||||
void Shader::uniform2i(std::string name, glm::ivec2 xy){
|
||||
GLuint transformLoc = glGetUniformLocation(id, name.c_str());
|
||||
glUniform2i(transformLoc, xy.x, xy.y);
|
||||
}
|
||||
|
||||
void Shader::uniform3f(std::string name, float x, float y, float z){
|
||||
GLuint transformLoc = glGetUniformLocation(id, name.c_str());
|
||||
glUniform3f(transformLoc, x,y,z);
|
||||
}
|
||||
|
||||
void Shader::uniform3f(std::string name, glm::vec3 xyz){
|
||||
GLuint transformLoc = glGetUniformLocation(id, name.c_str());
|
||||
glUniform3f(transformLoc, xyz.x, xyz.y, xyz.z);
|
||||
}
|
||||
|
||||
|
||||
Shader* Shader::create(
|
||||
std::string vertexFile,
|
||||
std::string fragmentFile,
|
||||
std::string vertexCode,
|
||||
std::string fragmentCode
|
||||
) {
|
||||
vertexCode = preprocessor->process(fs::path(vertexFile), vertexCode);
|
||||
fragmentCode = preprocessor->process(fs::path(fragmentFile), fragmentCode);
|
||||
|
||||
const GLchar* vShaderCode = vertexCode.c_str();
|
||||
const GLchar* fShaderCode = fragmentCode.c_str();
|
||||
|
||||
GLuint vertex, fragment;
|
||||
GLint success;
|
||||
GLchar infoLog[512];
|
||||
|
||||
// Vertex Shader
|
||||
vertex = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(vertex, 1, &vShaderCode, nullptr);
|
||||
glCompileShader(vertex);
|
||||
glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
|
||||
if (!success){
|
||||
glGetShaderInfoLog(vertex, 512, nullptr, infoLog);
|
||||
std::cerr << "SHADER::VERTEX: compilation failed: " << vertexFile << std::endl;
|
||||
std::cerr << infoLog << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Fragment Shader
|
||||
fragment = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(fragment, 1, &fShaderCode, nullptr);
|
||||
glCompileShader(fragment);
|
||||
glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
|
||||
if (!success){
|
||||
glGetShaderInfoLog(fragment, 512, nullptr, infoLog);
|
||||
std::cerr << "SHADER::FRAGMENT: compilation failed: " << vertexFile << std::endl;
|
||||
std::cerr << infoLog << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Shader Program
|
||||
GLuint id = glCreateProgram();
|
||||
glAttachShader(id, vertex);
|
||||
glAttachShader(id, fragment);
|
||||
glLinkProgram(id);
|
||||
|
||||
glGetProgramiv(id, GL_LINK_STATUS, &success);
|
||||
if (!success){
|
||||
glGetProgramInfoLog(id, 512, nullptr, infoLog);
|
||||
std::cerr << "SHADER::PROGRAM: linking failed" << std::endl;
|
||||
std::cerr << infoLog << std::endl;
|
||||
|
||||
glDeleteShader(vertex);
|
||||
glDeleteShader(fragment);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
glDeleteShader(vertex);
|
||||
glDeleteShader(fragment);
|
||||
|
||||
return new Shader(id);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef GRAPHICS_CORE_SHADER_H_
|
||||
#define GRAPHICS_CORE_SHADER_H_
|
||||
|
||||
#include <string>
|
||||
#include <glm/glm.hpp>
|
||||
#include "../../typedefs.h"
|
||||
|
||||
class GLSLExtension;
|
||||
|
||||
class Shader {
|
||||
uint id;
|
||||
public:
|
||||
static GLSLExtension* preprocessor;
|
||||
|
||||
Shader(unsigned int id);
|
||||
~Shader();
|
||||
|
||||
void use();
|
||||
void uniformMatrix(std::string name, glm::mat4 matrix);
|
||||
void uniform1i(std::string name, int x);
|
||||
void uniform1f(std::string name, float x);
|
||||
void uniform2f(std::string name, float x, float y);
|
||||
void uniform2f(std::string name, glm::vec2 xy);
|
||||
void uniform2i(std::string name, glm::ivec2 xy);
|
||||
void uniform3f(std::string name, float x, float y, float z);
|
||||
void uniform3f(std::string name, glm::vec3 xyz);
|
||||
|
||||
static Shader* create(
|
||||
std::string vertexFile,
|
||||
std::string fragmentFile,
|
||||
std::string vertexSource,
|
||||
std::string fragmentSource
|
||||
);
|
||||
};
|
||||
|
||||
#endif // GRAPHICS_SHADER_H_
|
||||
@@ -0,0 +1,82 @@
|
||||
#include "Texture.h"
|
||||
#include <GL/glew.h>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
|
||||
#include "ImageData.h"
|
||||
#include "gl_util.h"
|
||||
|
||||
Texture::Texture(uint id, uint width, uint height)
|
||||
: id(id), width(width), height(height) {
|
||||
}
|
||||
|
||||
Texture::Texture(ubyte* data, uint width, uint height, ImageFormat imageFormat)
|
||||
: width(width), height(height) {
|
||||
glGenTextures(1, &id);
|
||||
glBindTexture(GL_TEXTURE_2D, id);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
GLenum format = gl::to_gl_format(imageFormat);
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D, 0, format, width, height, 0,
|
||||
format, GL_UNSIGNED_BYTE, (GLvoid *) data
|
||||
);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
Texture::~Texture() {
|
||||
glDeleteTextures(1, &id);
|
||||
}
|
||||
|
||||
void Texture::bind(){
|
||||
glBindTexture(GL_TEXTURE_2D, id);
|
||||
}
|
||||
|
||||
void Texture::unbind() {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
void Texture::reload(ubyte* data){
|
||||
glBindTexture(GL_TEXTURE_2D, id);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *) data);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
ImageData* Texture::readData() {
|
||||
std::unique_ptr<ubyte[]> data (new ubyte[width * height * 4]);
|
||||
glBindTexture(GL_TEXTURE_2D, id);
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.get());
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
return new ImageData(ImageFormat::rgba8888, width, height, data.release());
|
||||
}
|
||||
|
||||
void Texture::setNearestFilter() {
|
||||
bind();
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
Texture* Texture::from(const ImageData* image) {
|
||||
uint width = image->getWidth();
|
||||
uint height = image->getHeight();
|
||||
const void* data = image->getData();
|
||||
return new Texture((ubyte*)data, width, height, image->getFormat());
|
||||
}
|
||||
|
||||
uint Texture::getWidth() const {
|
||||
return width;
|
||||
}
|
||||
|
||||
uint Texture::getHeight() const {
|
||||
return height;
|
||||
}
|
||||
|
||||
uint Texture::getId() const {
|
||||
return id;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef GRAPHICS_CORE_TEXTURE_H_
|
||||
#define GRAPHICS_CORE_TEXTURE_H_
|
||||
|
||||
#include <string>
|
||||
#include "../../typedefs.h"
|
||||
#include "ImageData.h"
|
||||
|
||||
class Texture {
|
||||
protected:
|
||||
uint id;
|
||||
uint width;
|
||||
uint height;
|
||||
public:
|
||||
Texture(uint id, uint width, uint height);
|
||||
Texture(ubyte* data, uint width, uint height, ImageFormat format);
|
||||
virtual ~Texture();
|
||||
|
||||
virtual void bind();
|
||||
virtual void unbind();
|
||||
virtual void reload(ubyte* data);
|
||||
|
||||
void setNearestFilter();
|
||||
|
||||
virtual ImageData* readData();
|
||||
|
||||
virtual uint getWidth() const;
|
||||
virtual uint getHeight() const;
|
||||
|
||||
virtual uint getId() const;
|
||||
|
||||
static Texture* from(const ImageData* image);
|
||||
};
|
||||
|
||||
#endif // GRAPHICS_CORE_TEXTURE_H_
|
||||
@@ -0,0 +1,90 @@
|
||||
#include "TextureAnimation.h"
|
||||
#include "Texture.h"
|
||||
#include "Framebuffer.h"
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <unordered_set>
|
||||
|
||||
TextureAnimator::TextureAnimator() {
|
||||
glGenFramebuffers(1, &fboR);
|
||||
glGenFramebuffers(1, &fboD);
|
||||
}
|
||||
|
||||
TextureAnimator::~TextureAnimator() {
|
||||
glDeleteFramebuffers(1, &fboR);
|
||||
glDeleteFramebuffers(1, &fboD);
|
||||
}
|
||||
|
||||
void TextureAnimator::addAnimations(const std::vector<TextureAnimation>& animations) {
|
||||
for (const auto& elem : animations) {
|
||||
addAnimation(elem);
|
||||
}
|
||||
}
|
||||
|
||||
void TextureAnimator::update(float delta) {
|
||||
std::unordered_set<uint> changedTextures;
|
||||
|
||||
for (auto& elem : animations) {
|
||||
elem.timer += delta;
|
||||
size_t frameNum = elem.currentFrame;
|
||||
Frame frame = elem.frames[elem.currentFrame];
|
||||
while (elem.timer >= frame.duration) {
|
||||
elem.timer -= frame.duration;
|
||||
elem.currentFrame++;
|
||||
if (elem.currentFrame >= elem.frames.size()) elem.currentFrame = 0;
|
||||
frame = elem.frames[elem.currentFrame];
|
||||
}
|
||||
if (frameNum != elem.currentFrame){
|
||||
uint elemDstId = elem.dstTexture->getId();
|
||||
uint elemSrcId = elem.srcTexture->getId();
|
||||
|
||||
if (changedTextures.find(elemDstId) == changedTextures.end())
|
||||
changedTextures.insert(elemDstId);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fboD);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, elemDstId, 0);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fboR);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, elemSrcId, 0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboD);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboR);
|
||||
|
||||
float srcPosY = elem.srcTexture->getHeight() - frame.size.y - frame.srcPos.y; // vertical flip
|
||||
|
||||
// Extensions
|
||||
const int ext = 2;
|
||||
for (int y = -1; y <= 1; y++) {
|
||||
for (int x = -1; x <= 1; x++) {
|
||||
if (x == 0 && y == 0)
|
||||
continue;
|
||||
glBlitFramebuffer(
|
||||
frame.srcPos.x, srcPosY, frame.srcPos.x + frame.size.x, srcPosY + frame.size.y,
|
||||
frame.dstPos.x+x*ext, frame.dstPos.y+y*ext,
|
||||
frame.dstPos.x + frame.size.x+x*ext, frame.dstPos.y + frame.size.y+y*ext,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
glBlitFramebuffer(
|
||||
frame.srcPos.x, srcPosY,
|
||||
frame.srcPos.x + frame.size.x,
|
||||
srcPosY + frame.size.y,
|
||||
frame.dstPos.x, frame.dstPos.y,
|
||||
frame.dstPos.x + frame.size.x,
|
||||
frame.dstPos.y + frame.size.y,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST
|
||||
);
|
||||
}
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
for (auto& elem : changedTextures) {
|
||||
glBindTexture(GL_TEXTURE_2D, elem);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef GRAPHICS_CORE_TEXTURE_ANIMATION_H_
|
||||
#define GRAPHICS_CORE_TEXTURE_ANIMATION_H_
|
||||
|
||||
#include "../../typedefs.h"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
|
||||
class Assets;
|
||||
class Texture;
|
||||
class Framebuffer;
|
||||
|
||||
constexpr float DEFAULT_FRAME_DURATION = 0.150f;
|
||||
|
||||
struct Frame {
|
||||
glm::ivec2 srcPos;
|
||||
glm::ivec2 dstPos;
|
||||
glm::ivec2 size;
|
||||
float duration = DEFAULT_FRAME_DURATION;
|
||||
};
|
||||
|
||||
class TextureAnimation {
|
||||
public:
|
||||
TextureAnimation(Texture* srcTex, Texture* dstTex) : srcTexture(srcTex), dstTexture(dstTex) {};
|
||||
~TextureAnimation() {};
|
||||
|
||||
void addFrame(const Frame& frame) { frames.emplace_back(frame); };
|
||||
|
||||
size_t currentFrame = 0;
|
||||
float timer = 0.f;
|
||||
Texture* srcTexture;
|
||||
Texture* dstTexture;
|
||||
std::vector<Frame> frames;
|
||||
};
|
||||
|
||||
class TextureAnimator {
|
||||
public:
|
||||
TextureAnimator();
|
||||
~TextureAnimator();
|
||||
|
||||
void addAnimation(const TextureAnimation& animation) { animations.emplace_back(animation); };
|
||||
void addAnimations(const std::vector<TextureAnimation>& animations);
|
||||
|
||||
void update(float delta);
|
||||
private:
|
||||
uint fboR;
|
||||
uint fboD;
|
||||
|
||||
std::vector<TextureAnimation> animations;
|
||||
};
|
||||
|
||||
#endif // GRAPHICS_CORE_TEXTURE_ANIMATION_H_
|
||||
@@ -0,0 +1 @@
|
||||
#include "UVRegion.h"
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef GRAPHICS_CORE_UVREGION_H_
|
||||
#define GRAPHICS_CORE_UVREGION_H_
|
||||
|
||||
class UVRegion {
|
||||
public:
|
||||
float u1;
|
||||
float v1;
|
||||
float u2;
|
||||
float v2;
|
||||
|
||||
UVRegion(float u1, float v1, float u2, float v2)
|
||||
: u1(u1), v1(v1), u2(u2), v2(v2){}
|
||||
|
||||
UVRegion() : u1(0.0f), v1(0.0f), u2(1.0f), v2(1.0f){}
|
||||
};
|
||||
|
||||
#endif // SRC_GRAPHICS_UVREGION_H_
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "Viewport.h"
|
||||
|
||||
Viewport::Viewport(uint width, uint height)
|
||||
: width(width), height(height) {
|
||||
}
|
||||
|
||||
uint Viewport::getWidth() const {
|
||||
return width;
|
||||
}
|
||||
|
||||
uint Viewport::getHeight() const {
|
||||
return height;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef GRAPHICS_CORE_VIEWPORT_H_
|
||||
#define GRAPHICS_CORE_VIEWPORT_H_
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "../../typedefs.h"
|
||||
|
||||
class Viewport {
|
||||
uint width;
|
||||
uint height;
|
||||
public:
|
||||
Viewport(uint width, uint height);
|
||||
|
||||
virtual uint getWidth() const;
|
||||
virtual uint getHeight() const;
|
||||
|
||||
glm::ivec2 size() const {
|
||||
return glm::ivec2(width, height);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // GRAPHICS_VIEWPORT_H_
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef GRAPHICS_CORE_GL_UTIL_H_
|
||||
#define GRAPHICS_CORE_GL_UTIL_H_
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include "ImageData.h"
|
||||
|
||||
namespace gl {
|
||||
inline GLenum to_gl_format(ImageFormat imageFormat) {
|
||||
switch (imageFormat) {
|
||||
case ImageFormat::rgb888: return GL_RGB;
|
||||
case ImageFormat::rgba8888: return GL_RGBA;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // GRAPHICS_CORE_GL_UTIL_H_
|
||||
Reference in New Issue
Block a user