@@ -3,6 +3,7 @@ add_library(cygnet SHARED | |||
src/glutil.cc | |||
src/GlWrappers.cc | |||
src/Image.cc | |||
src/RenderQueue.cc | |||
src/Window.cc) | |||
target_link_libraries(cygnet PUBLIC SDL2 SDL2_image GLESv2) | |||
target_include_directories(cygnet |
@@ -53,9 +53,13 @@ public: | |||
void upload(GLsizei width, GLsizei height, void *data, | |||
GLenum format, GLenum type = GL_UNSIGNED_BYTE); | |||
GLuint id() { return id_; } | |||
int width() { return w_; } | |||
int height() { return h_; } | |||
private: | |||
GLuint id_; | |||
int w_; | |||
int h_; | |||
}; | |||
inline GLint GlProgram::attribLoc(const char *name) { |
@@ -1,7 +1,6 @@ | |||
#pragma once | |||
#include <memory> | |||
#include <stdlib.h> | |||
#include "GlWrappers.h" | |||
@@ -12,12 +11,14 @@ public: | |||
Image(std::string path); | |||
GlTexture &texture(); | |||
int width() { return w_; } | |||
int height() { return h_; } | |||
private: | |||
std::string path_; | |||
int w_, h_, pitch_; | |||
CPtr<void, free> bytes_; | |||
std::unique_ptr<unsigned char[]> bytes_; | |||
GlTexture tex_; | |||
bool tex_dirty_ = true; |
@@ -0,0 +1,64 @@ | |||
#pragma once | |||
#include <vector> | |||
#include "GlWrappers.h" | |||
namespace Cygnet { | |||
class RenderQueue { | |||
public: | |||
struct Locs { | |||
GLint transform; | |||
GLint position; | |||
GLint texCoord; | |||
GLint tex; | |||
}; | |||
RenderQueue(Locs locs, float scale): | |||
locs_(std::move(locs)), pixScale_(scale) {}; | |||
RenderQueue(GlProgram &prog, float scale): | |||
RenderQueue({ | |||
prog.uniformLoc("transform"), | |||
prog.attribLoc("position"), | |||
prog.attribLoc("texCoord"), | |||
prog.uniformLoc("tex"), | |||
}, scale) {} | |||
void show(float x, float y, GlTexture &tex) { show(x, y, tex.width(), tex.height(), tex.id()); } | |||
void show(float x, float y, float w, float h, GLuint tex) { | |||
queue_.push_back({ x, y, w * pixScale_, h * pixScale_, tex }); | |||
} | |||
float pixScale() { return pixScale_; } | |||
void pixScale(float scale) { pixScale_ = scale; } | |||
float scaleX() { return mat_[0]; } | |||
void scaleX(float sx) { mat_[0] = sx; } | |||
float scaleY() { return -mat_[4]; } | |||
void scaleY(float sy) { mat_[4] = -sy; } | |||
float translateX() { return mat_[2]; } | |||
void translateX(float tx) { mat_[2] = tx; } | |||
float translateY() { return mat_[5]; } | |||
void translateY(float ty) { mat_[5] = ty; } | |||
void draw(); | |||
private: | |||
struct Entry { | |||
float x, y, w, h; | |||
GLuint tex; | |||
}; | |||
Locs locs_; | |||
float pixScale_; | |||
GLfloat mat_[9] = { | |||
1, 0, 0, // scaleX, 0, translateX, | |||
0, -1, 0, // 0, -scaleY, translateY, | |||
0, 0, 1, // 0, 0, 1 | |||
}; | |||
std::vector<Entry> queue_; | |||
}; | |||
} |
@@ -2,13 +2,12 @@ | |||
#include <SDL.h> | |||
#include <stdint.h> | |||
#include <glm/mat4x4.hpp> | |||
#include "util.h" | |||
namespace Cygnet { | |||
struct GlTexture; | |||
class GlTexture; | |||
class Window { | |||
public: |
@@ -3,10 +3,32 @@ | |||
#include <cygnet/builtins.h> | |||
#include <cygnet/glutil.h> | |||
#include <cygnet/Image.h> | |||
#include <cygnet/RenderQueue.h> | |||
#include <stdio.h> | |||
#include <memory> | |||
#include <vector> | |||
const char *vertexShader = R"( | |||
uniform mat3 transform; | |||
attribute vec2 position; | |||
attribute vec2 texCoord; | |||
varying vec2 v_texCoord; | |||
void main() { | |||
vec3 pos = transform * vec3(position, 0); | |||
gl_Position = vec4(pos.x, pos.y, 0, 1); | |||
v_texCoord = texCoord; | |||
} | |||
)"; | |||
const char *fragmentShader = R"( | |||
precision mediump float; | |||
varying vec2 v_texCoord; | |||
uniform sampler2D tex; | |||
void main() { | |||
gl_FragColor = texture2D(tex, v_texCoord); | |||
} | |||
)"; | |||
enum class Key { | |||
UP, DOWN, LEFT, RIGHT, NONE, | |||
}; | |||
@@ -15,10 +37,12 @@ struct State { | |||
bool keys[(int)Key::NONE]{}; | |||
Cygnet::Window &win; | |||
Cygnet::GlProgram &program; | |||
Cygnet::RenderQueue &q; | |||
}; | |||
class Entity { | |||
public: | |||
virtual ~Entity() = default; | |||
virtual void update(State &state, float dt) = 0; | |||
virtual void draw(State &state) = 0; | |||
}; | |||
@@ -34,6 +58,13 @@ public: | |||
fx -= 1; | |||
if (state.keys[(int)Key::RIGHT]) | |||
fx += 1; | |||
if (state.keys[(int)Key::UP]) | |||
fy -= 1; | |||
if (state.keys[(int)Key::DOWN]) | |||
fy += 1; | |||
fy += vy_ * -0.9; | |||
fx += vx_ * -0.9; | |||
vx_ += fx * dt; | |||
vy_ += fy * dt; | |||
@@ -42,42 +73,7 @@ public: | |||
} | |||
void draw(State &state) override { | |||
printf("\ram at (%f,%f)", x_, y_); | |||
fflush(stdout); | |||
const GLfloat vertexes[] = { | |||
x_ - 0.5f, y_ + 0.5f, 0.0f, // pos 0: top left | |||
0.0f, 0.0f, // tex 0: top left | |||
x_ - 0.5f, y_ - 0.5f, 0.0f, // pos 1: bottom left | |||
0.0f, 1.0f, // tex 1: bottom left | |||
x_ + 0.5f, y_ - 0.5f, 0.0f, // pos 2: bottom right | |||
1.0f, 1.0f, // tex 2: bottom right | |||
x_ + 0.5f, y_ + 0.5f, 0.0f, // pos 3: top right | |||
1.0f, 0.0f, // tex 3: top right | |||
}; | |||
static const GLushort indexes[] = { | |||
0, 1, 2, // top left -> bottom left -> bottom right | |||
2, 3, 0, // bottom right -> top right -> top left | |||
}; | |||
GLint positionLoc = state.program.attribLoc("position", 0); | |||
GLint texCoordLoc = state.program.attribLoc("texCoord", 1); | |||
GLint texLoc = state.program.uniformLoc("tex"); | |||
glUniform1i(texLoc, 0); | |||
glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), vertexes); | |||
glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), &vertexes[3]); | |||
Cygnet::glCheck(); | |||
glEnableVertexAttribArray(positionLoc); | |||
glEnableVertexAttribArray(texCoordLoc); | |||
Cygnet::glCheck(); | |||
image_.texture().bind(); | |||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indexes); | |||
Cygnet::glCheck(); | |||
state.q.show(x_, y_, image_.texture()); | |||
} | |||
private: | |||
@@ -107,13 +103,19 @@ int main() { | |||
Cygnet::Deferred<SDL_Quit> sdl; | |||
Cygnet::Window win("Game", 640, 480); | |||
Cygnet::GlProgram program(Cygnet::builtinTextureVertex(), Cygnet::builtinTextureFragment()); | |||
Cygnet::GlProgram program( | |||
Cygnet::GlShader(Cygnet::GlShader::Type::VERTEX, vertexShader), | |||
Cygnet::GlShader(Cygnet::GlShader::Type::FRAGMENT, fragmentShader)); | |||
program.use(); | |||
Cygnet::RenderQueue q(program, 1/32.0); | |||
q.scaleX(1 / (640.0 / 480.0)); | |||
State state{ | |||
.keys{}, | |||
.win = win, | |||
.program = program, | |||
.q = q, | |||
}; | |||
std::vector<std::unique_ptr<Entity>> entities; | |||
@@ -127,6 +129,14 @@ int main() { | |||
goto exit; | |||
break; | |||
case SDL_WINDOWEVENT: | |||
if (evt.window.event == SDL_WINDOWEVENT_RESIZED) { | |||
glViewport(0, 0, evt.window.data1, evt.window.data2); | |||
float ratio = (float)evt.window.data1 / (float)evt.window.data2; | |||
q.scaleX(1 / ratio); | |||
} | |||
break; | |||
case SDL_KEYDOWN: | |||
{ | |||
Key key = keyFromSym(evt.key.keysym); | |||
@@ -157,6 +167,7 @@ int main() { | |||
ent->draw(state); | |||
} | |||
q.draw(); | |||
win.flip(); | |||
} | |||
@@ -84,6 +84,8 @@ void GlTexture::bind() { | |||
void GlTexture::upload(GLsizei width, GLsizei height, void *data, | |||
GLenum format, GLenum type) { | |||
w_ = width; | |||
h_ = height; | |||
bind(); | |||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, format, type, data); | |||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
@@ -28,8 +28,8 @@ Image::Image(std::string path) { | |||
w_ = surface->w; | |||
h_ = surface->h; | |||
pitch_ = surface->pitch; | |||
bytes_.reset(surface->pixels); | |||
surface->pixels = nullptr; | |||
bytes_.reset(new unsigned char[(size_t)pitch_ * h_]); | |||
memcpy(bytes_.get(), surface->pixels, (size_t)pitch_ * h_); | |||
} | |||
GlTexture &Image::texture() { |
@@ -0,0 +1,51 @@ | |||
#include "RenderQueue.h" | |||
#include "glutil.h" | |||
namespace Cygnet { | |||
static const GLfloat texCoords[] = { | |||
0.0f, 0.0f, // tex 0: top left | |||
0.0f, 1.0f, // tex 1: bottom left | |||
1.0f, 1.0f, // tex 2: bottom right | |||
1.0f, 0.0f, // tex 3: top right | |||
}; | |||
static const GLushort indexes[] = { | |||
0, 1, 2, // top left -> bottom left -> bottom right | |||
2, 3, 0, // bottom right -> top right -> top left | |||
}; | |||
void RenderQueue::draw() { | |||
glUniform1i(locs_.tex, 0); | |||
glVertexAttribPointer(locs_.texCoord, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), texCoords); | |||
glEnableVertexAttribArray(locs_.texCoord); | |||
glCheck(); | |||
glUniformMatrix3fv(locs_.transform, 1, GL_TRUE, mat_); | |||
glCheck(); | |||
glActiveTexture(GL_TEXTURE0); | |||
for (auto &entry: queue_) { | |||
GLfloat vertexes[] = { | |||
entry.x, entry.y, // top left | |||
entry.x, entry.y + entry.h, // bottom left | |||
entry.x + entry.w, entry.y + entry.h, // bottom right | |||
entry.x + entry.w, entry.y, // top right | |||
}; | |||
glVertexAttribPointer(locs_.position, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), vertexes); | |||
glEnableVertexAttribArray(locs_.position); | |||
glCheck(); | |||
glBindTexture(GL_TEXTURE_2D, entry.tex); | |||
glCheck(); | |||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indexes); | |||
} | |||
queue_.clear(); | |||
} | |||
} |
@@ -7,17 +7,18 @@ | |||
namespace Cygnet { | |||
Window::Window(const char *name, int width, int height) { | |||
win_.reset(SDL_CreateWindow( | |||
name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, | |||
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | | |||
SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL)); | |||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); | |||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); | |||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); | |||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); | |||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); | |||
glCheck(); | |||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); | |||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 16); | |||
win_.reset(SDL_CreateWindow( | |||
name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, | |||
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | | |||
SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL)); | |||
sdlAssert(glctx_ = SDL_GL_CreateContext(win_.get())); | |||
makeCurrent(); |