@@ -0,0 +1,77 @@ | |||
#pragma once | |||
#include <iostream> | |||
#include <cmath> | |||
#include <array> | |||
namespace SwanCommon { | |||
template<typename T> | |||
struct Matrix3 { | |||
static constexpr std::array<T, 9> identity = {1, 0, 0, 0, 1, 0, 0, 0, 1}; | |||
std::array<T, 9> vals; | |||
constexpr Matrix3(): vals(identity) {} | |||
constexpr T *data() { | |||
return vals.data(); | |||
} | |||
constexpr const T *data() const { | |||
return vals.data(); | |||
} | |||
constexpr T &at(int x, int y) { | |||
return vals[y * 3 + x]; | |||
} | |||
constexpr const T &at(int x, int y) const { | |||
return vals[y * 3 + x]; | |||
} | |||
constexpr Matrix3<T> &set(std::initializer_list<T> vals) { | |||
this->vals = vals; | |||
return *this; | |||
} | |||
constexpr Matrix3<T> &reset() { | |||
vals = identity; | |||
return *this; | |||
} | |||
constexpr Matrix3<T> &translate(T x, T y) { | |||
at(2, 0) += x; | |||
at(2, 1) += y; | |||
return *this; | |||
} | |||
constexpr Matrix3<T> &scale(T x, T y) { | |||
at(0, 0) *= x; | |||
at(1, 1) *= y; | |||
return *this; | |||
} | |||
constexpr Matrix3<T> &rotate(T rads) { | |||
T s = std::sin(rads); | |||
T c = std::cos(rads); | |||
at(0, 0) += c; | |||
at(1, 0) -= s; | |||
at(0, 1) += s; | |||
at(1, 1) += c; | |||
return *this; | |||
} | |||
template<typename U> | |||
friend std::ostream &operator<<(std::ostream &os, const Matrix3<U> &mat); | |||
}; | |||
template<typename T> | |||
std::ostream &operator<<(std::ostream &os, const Matrix3<T> &mat) { | |||
os << '(' | |||
<< '(' << mat.at(0, 0) << ", " << mat.at(1, 0) << ", " << mat.at(2, 0) << "), " | |||
<< '(' << mat.at(0, 1) << ", " << mat.at(1, 1) << ", " << mat.at(2, 1) << "), " | |||
<< '(' << mat.at(0, 2) << ", " << mat.at(1, 2) << ", " << mat.at(2, 2) << "))"; | |||
return os; | |||
} | |||
} |
@@ -2,10 +2,7 @@ | |||
namespace Cygnet::Shaders { | |||
extern const char *basicVx; | |||
extern const char *texturedVx; | |||
extern const char *solidColorFr; | |||
extern const char *texturedFr; | |||
} |
@@ -2,7 +2,8 @@ | |||
#include <stdexcept> | |||
#include <stdint.h> | |||
#include <SDL.h> | |||
#include "swan-common/Matrix3.h" | |||
namespace Cygnet { | |||
@@ -11,6 +12,9 @@ using GLint = int32_t; | |||
using GLuint = uint32_t; | |||
using GLsizei = int32_t; | |||
using GLenum = uint32_t; | |||
using GLfloat = float; | |||
using Mat3gf = SwanCommon::Matrix3<GLfloat>; | |||
struct SDLError: public std::exception { | |||
SDLError(std::string msg): message(std::move(msg)) {} |
@@ -1,6 +1,9 @@ | |||
#include "Renderer.h" | |||
#include <iostream> | |||
#include <stdio.h> | |||
#include <SDL_opengles2.h> | |||
#include <swan-common/constants.h> | |||
#include "shaders.h" | |||
#include "GlWrappers.h" | |||
@@ -12,29 +15,22 @@ namespace Cygnet { | |||
struct TexturedProg: public GlProgram { | |||
using GlProgram::GlProgram; | |||
GLint transform = uniformLoc("camera"); | |||
GLint position = attribLoc("position"); | |||
GLint texCoord = attribLoc("texCoord"); | |||
GLint tex = uniformLoc("tex"); | |||
}; | |||
struct SolidColorProg: public GlProgram { | |||
using GlProgram::GlProgram; | |||
GLint position = attribLoc("position"); | |||
GLint color = uniformLoc("color"); | |||
}; | |||
struct RendererState { | |||
GlVxShader basicVx{Shaders::basicVx}; | |||
GlVxShader texturedVx{Shaders::texturedVx}; | |||
GlFrShader solidColorFr{Shaders::solidColorFr}; | |||
GlFrShader texturedFr{Shaders::texturedFr}; | |||
TexturedProg texturedProg{texturedVx, texturedFr}; | |||
SolidColorProg solidColorProg{basicVx, solidColorFr}; | |||
TileAtlas atlas; | |||
GlTexture atlasTex; | |||
Mat3gf camera; | |||
}; | |||
Renderer::Renderer(): state_(std::make_unique<RendererState>()) {} | |||
@@ -43,6 +39,42 @@ Renderer::~Renderer() = default; | |||
void Renderer::draw() { | |||
state_->texturedProg.use(); | |||
float tw = (6 * SwanCommon::TILE_SIZE) / (float)state_->atlasTex.width(); | |||
GLfloat vertexes[] = { | |||
-0.5f, 0.5f, // pos 0: top left | |||
0.0f, 0.0f, // tex 0: top left | |||
-0.5f, -0.5f, // pos 1: bottom left | |||
0.0f, 1.0f, // tex 1: bottom left | |||
0.5f, -0.5f, // pos 2: bottom right | |||
tw, 1.0f, // tex 2: bottom right | |||
0.5f, 0.5f, // pos 3: top right | |||
tw, 0.0f, // tex 3: top right | |||
}; | |||
GLushort indexes[] = { | |||
0, 1, 2, // top left -> bottom left -> bottom right | |||
2, 3, 0, // bottom right -> top right -> top left | |||
}; | |||
state_->camera.translate(0.01, 0); | |||
glUniformMatrix3fv(state_->texturedProg.transform, 1, GL_TRUE, state_->camera.data()); | |||
glVertexAttribPointer(state_->texturedProg.position, 2, GL_FLOAT, GL_FALSE, | |||
4 * sizeof(GLfloat), vertexes); | |||
glVertexAttribPointer(state_->texturedProg.texCoord, 2, GL_FLOAT, GL_FALSE, | |||
4 * sizeof(GLfloat), &vertexes[2]); | |||
glCheck(); | |||
glEnableVertexAttribArray(state_->texturedProg.position); | |||
glEnableVertexAttribArray(state_->texturedProg.texCoord); | |||
glCheck(); | |||
state_->atlasTex.bind(); | |||
glUniform1i(state_->texturedProg.tex, 0); | |||
glCheck(); | |||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indexes); | |||
glCheck(); | |||
} | |||
void Renderer::registerTileTexture(size_t tileId, const void *data, size_t len) { | |||
@@ -53,6 +85,11 @@ void Renderer::uploadTileTexture() { | |||
size_t w, h; | |||
const unsigned char *data = state_->atlas.getImage(&w, &h); | |||
state_->atlasTex.upload(w, h, (void *)data, GL_RGBA, GL_UNSIGNED_BYTE); | |||
std::cerr << "Uploaded image of size " << w << 'x' << h << '\n'; | |||
FILE *f = fopen("lol.rgb", "w"); | |||
fwrite(data, 1, w * h * 4, f); | |||
fclose(f); | |||
} | |||
} |
@@ -2,6 +2,7 @@ | |||
#include <iostream> | |||
#include <vector> | |||
#include <algorithm> | |||
#include <SDL_opengles2.h> | |||
#include <stdio.h> | |||
#include <string.h> | |||
@@ -20,7 +21,7 @@ struct AtlasState { | |||
TileAtlas::TileAtlas(): state_(std::make_unique<AtlasState>()) { | |||
GLint size; | |||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size); | |||
state_->tilesPerLine = size / SwanCommon::TILE_SIZE; | |||
state_->tilesPerLine = std::min(size / SwanCommon::TILE_SIZE, 1024); | |||
} | |||
TileAtlas::~TileAtlas() = default; | |||
@@ -30,6 +31,7 @@ void TileAtlas::addTile(size_t tileId, const void *data, size_t len) { | |||
const unsigned char *bytes = (const unsigned char *)data; | |||
size_t x = tileId % state_->tilesPerLine; | |||
size_t y = tileId / state_->tilesPerLine; | |||
std::cerr << "Adding tile " << x << ", " << y << '\n'; | |||
if (y >= state_->tilesPerLine) { | |||
std::cerr << "Cygnet: Warning: Tile ID " << tileId << " too big for texture atlas\n"; | |||
return; | |||
@@ -43,21 +45,21 @@ void TileAtlas::addTile(size_t tileId, const void *data, size_t len) { | |||
state_->height = y + 1; | |||
} | |||
size_t tileImgSize = SwanCommon::TILE_SIZE * SwanCommon::TILE_SIZE * 4; | |||
size_t requiredSize = (x + 1) * (y + 1) * tileImgSize; | |||
state_->data.resize(requiredSize); | |||
size_t requiredSize = state_->tilesPerLine * SwanCommon::TILE_SIZE * | |||
state_->height * SwanCommon::TILE_SIZE * 4; | |||
state_->data.reserve(requiredSize); | |||
for (size_t ty = 0; ty < rows; ++ty) { | |||
const unsigned char *src = bytes + ty * SwanCommon::TILE_SIZE * 4; | |||
unsigned char *dest = state_->data.data() + | |||
((y + ty) * state_->width * SwanCommon::TILE_SIZE * 4) + | |||
(y * SwanCommon::TILE_SIZE + ty) * state_->tilesPerLine * SwanCommon::TILE_SIZE * 4 + | |||
(x * SwanCommon::TILE_SIZE * 4); | |||
const unsigned char *src = bytes + ty * SwanCommon::TILE_SIZE * 4; | |||
memcpy(dest, src, SwanCommon::TILE_SIZE * 4); | |||
} | |||
} | |||
const unsigned char *TileAtlas::getImage(size_t *w, size_t *h) { | |||
*w = state_->width * SwanCommon::TILE_SIZE; | |||
*w = state_->tilesPerLine * SwanCommon::TILE_SIZE; | |||
*h = state_->height * SwanCommon::TILE_SIZE; | |||
return state_->data.data(); | |||
} |
@@ -2,35 +2,18 @@ | |||
namespace Cygnet::Shaders { | |||
const char *basicVx = R"glsl( | |||
uniform mat3 transform; | |||
attribute vec2 position; | |||
void main() { | |||
vec3 pos = transform * vec3(position, 0); | |||
gl_Position = vec4(pos.x, pos.y, 0, 1); | |||
} | |||
)glsl"; | |||
const char *texturedVx = R"glsl( | |||
uniform mat3 transform; | |||
uniform mat3 camera; | |||
attribute vec2 position; | |||
attribute vec2 texCoord; | |||
varying vec2 v_texCoord; | |||
void main() { | |||
vec3 pos = transform * vec3(position, 0); | |||
vec3 pos = camera * vec3(position, 1); | |||
gl_Position = vec4(pos.x, pos.y, 0, 1); | |||
v_texCoord = texCoord; | |||
} | |||
)glsl"; | |||
const char *solidColorFr = R"glsl( | |||
precision mediump float; | |||
uniform vec4 color; | |||
void main() { | |||
gl_FragColor = color; | |||
} | |||
)glsl"; | |||
const char *texturedFr = R"glsl( | |||
precision mediump float; | |||
varying vec2 v_texCoord; |
@@ -1,5 +1,6 @@ | |||
#include "util.h" | |||
#include <SDL.h> | |||
#include <SDL_opengles2.h> | |||
namespace Cygnet { |
@@ -4,18 +4,31 @@ | |||
#include <swan-common/constants.h> | |||
#include <stdint.h> | |||
#include <SDL_image.h> | |||
#include <SDL.h> | |||
void addTile(Cygnet::Renderer &rnd, const char *path) { | |||
static size_t id = 0; | |||
SDL_Surface *surf = IMG_Load(path); | |||
rnd.registerTileTexture(id++, surf->pixels, surf->pitch * surf->h * 4); | |||
SDL_FreeSurface(surf); | |||
} | |||
int main() { | |||
Cygnet::Context ctx; | |||
Cygnet::Window win("Cygnet Test", 640, 480); | |||
IMG_Init(IMG_INIT_PNG); | |||
//Cygnet::Window win("Cygnet Test", 640, 480); | |||
Cygnet::Window win("Cygnet Test", 1280, 256); | |||
Cygnet::Renderer rnd; | |||
uint32_t img[SwanCommon::TILE_SIZE * SwanCommon::TILE_SIZE]; | |||
for (size_t i = 0; i < sizeof(img) / sizeof(*img); ++i) { | |||
img[i] = 0xff00aaff; | |||
} | |||
rnd.registerTileTexture(0, img, sizeof(img)); | |||
for (auto path: { | |||
"core.mod/assets/tile/dirt.png", | |||
"core.mod/assets/tile/grass.png", | |||
"core.mod/assets/tile/leaves.png", | |||
"core.mod/assets/tile/stone.png", | |||
"core.mod/assets/tile/torch.png", | |||
"core.mod/assets/tile/tree-trunk.png", | |||
}) addTile(rnd, path); | |||
rnd.uploadTileTexture(); | |||
while (true) { | |||
@@ -33,5 +46,5 @@ int main() { | |||
} | |||
exit: | |||
; | |||
IMG_Quit(); | |||
} |