src/glutil.cc | src/glutil.cc | ||||
src/GlWrappers.cc | src/GlWrappers.cc | ||||
src/Image.cc | src/Image.cc | ||||
src/RenderQueue.cc | |||||
src/Window.cc) | src/Window.cc) | ||||
target_link_libraries(cygnet PUBLIC SDL2 SDL2_image GLESv2) | target_link_libraries(cygnet PUBLIC SDL2 SDL2_image GLESv2) | ||||
target_include_directories(cygnet | target_include_directories(cygnet |
void upload(GLsizei width, GLsizei height, void *data, | void upload(GLsizei width, GLsizei height, void *data, | ||||
GLenum format, GLenum type = GL_UNSIGNED_BYTE); | GLenum format, GLenum type = GL_UNSIGNED_BYTE); | ||||
GLuint id() { return id_; } | GLuint id() { return id_; } | ||||
int width() { return w_; } | |||||
int height() { return h_; } | |||||
private: | private: | ||||
GLuint id_; | GLuint id_; | ||||
int w_; | |||||
int h_; | |||||
}; | }; | ||||
inline GLint GlProgram::attribLoc(const char *name) { | inline GLint GlProgram::attribLoc(const char *name) { |
#pragma once | #pragma once | ||||
#include <memory> | #include <memory> | ||||
#include <stdlib.h> | |||||
#include "GlWrappers.h" | #include "GlWrappers.h" | ||||
Image(std::string path); | Image(std::string path); | ||||
GlTexture &texture(); | GlTexture &texture(); | ||||
int width() { return w_; } | |||||
int height() { return h_; } | |||||
private: | private: | ||||
std::string path_; | std::string path_; | ||||
int w_, h_, pitch_; | int w_, h_, pitch_; | ||||
CPtr<void, free> bytes_; | |||||
std::unique_ptr<unsigned char[]> bytes_; | |||||
GlTexture tex_; | GlTexture tex_; | ||||
bool tex_dirty_ = true; | bool tex_dirty_ = true; |
#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_; | |||||
}; | |||||
} |
#include <SDL.h> | #include <SDL.h> | ||||
#include <stdint.h> | #include <stdint.h> | ||||
#include <glm/mat4x4.hpp> | |||||
#include "util.h" | #include "util.h" | ||||
namespace Cygnet { | namespace Cygnet { | ||||
struct GlTexture; | |||||
class GlTexture; | |||||
class Window { | class Window { | ||||
public: | public: |
#include <cygnet/builtins.h> | #include <cygnet/builtins.h> | ||||
#include <cygnet/glutil.h> | #include <cygnet/glutil.h> | ||||
#include <cygnet/Image.h> | #include <cygnet/Image.h> | ||||
#include <cygnet/RenderQueue.h> | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <memory> | #include <memory> | ||||
#include <vector> | #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 { | enum class Key { | ||||
UP, DOWN, LEFT, RIGHT, NONE, | UP, DOWN, LEFT, RIGHT, NONE, | ||||
}; | }; | ||||
bool keys[(int)Key::NONE]{}; | bool keys[(int)Key::NONE]{}; | ||||
Cygnet::Window &win; | Cygnet::Window &win; | ||||
Cygnet::GlProgram &program; | Cygnet::GlProgram &program; | ||||
Cygnet::RenderQueue &q; | |||||
}; | }; | ||||
class Entity { | class Entity { | ||||
public: | public: | ||||
virtual ~Entity() = default; | |||||
virtual void update(State &state, float dt) = 0; | virtual void update(State &state, float dt) = 0; | ||||
virtual void draw(State &state) = 0; | virtual void draw(State &state) = 0; | ||||
}; | }; | ||||
fx -= 1; | fx -= 1; | ||||
if (state.keys[(int)Key::RIGHT]) | if (state.keys[(int)Key::RIGHT]) | ||||
fx += 1; | 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; | vx_ += fx * dt; | ||||
vy_ += fy * dt; | vy_ += fy * dt; | ||||
} | } | ||||
void draw(State &state) override { | 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: | private: | ||||
Cygnet::Deferred<SDL_Quit> sdl; | Cygnet::Deferred<SDL_Quit> sdl; | ||||
Cygnet::Window win("Game", 640, 480); | 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(); | program.use(); | ||||
Cygnet::RenderQueue q(program, 1/32.0); | |||||
q.scaleX(1 / (640.0 / 480.0)); | |||||
State state{ | State state{ | ||||
.keys{}, | .keys{}, | ||||
.win = win, | .win = win, | ||||
.program = program, | .program = program, | ||||
.q = q, | |||||
}; | }; | ||||
std::vector<std::unique_ptr<Entity>> entities; | std::vector<std::unique_ptr<Entity>> entities; | ||||
goto exit; | goto exit; | ||||
break; | 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: | case SDL_KEYDOWN: | ||||
{ | { | ||||
Key key = keyFromSym(evt.key.keysym); | Key key = keyFromSym(evt.key.keysym); | ||||
ent->draw(state); | ent->draw(state); | ||||
} | } | ||||
q.draw(); | |||||
win.flip(); | win.flip(); | ||||
} | } | ||||
void GlTexture::upload(GLsizei width, GLsizei height, void *data, | void GlTexture::upload(GLsizei width, GLsizei height, void *data, | ||||
GLenum format, GLenum type) { | GLenum format, GLenum type) { | ||||
w_ = width; | |||||
h_ = height; | |||||
bind(); | bind(); | ||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, format, type, data); | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, format, type, data); | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
w_ = surface->w; | w_ = surface->w; | ||||
h_ = surface->h; | h_ = surface->h; | ||||
pitch_ = surface->pitch; | 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() { | GlTexture &Image::texture() { |
#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(); | |||||
} | |||||
} |
namespace Cygnet { | namespace Cygnet { | ||||
Window::Window(const char *name, int width, int height) { | 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_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); | ||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); | ||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); | ||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); | SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); | ||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); | 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())); | sdlAssert(glctx_ = SDL_GL_CreateContext(win_.get())); | ||||
makeCurrent(); | makeCurrent(); |