Implements calling Lua.

This commit is contained in:
2021-08-25 12:27:39 +09:00
parent 1518e429b4
commit 213902021c
46 changed files with 2233 additions and 245 deletions

4
src/Font.cc Normal file
View File

@@ -0,0 +1,4 @@
/* Because an implementation of stb_truetype is huge, compiles it separately. */
#define STB_TRUETYPE_IMPLEMENTATION
#include "Font.h"

73
src/Font.h Normal file
View File

@@ -0,0 +1,73 @@
#pragma once
#include <cstdint>
#include <fstream>
#include <memory>
#include <string>
#include "thirdparty/stb_truetype.h"
#include "common.h"
#include "iAllocator.h"
#include "Rasterbuffer.h"
namespace gj {
class Font {
public:
Font() = delete;
Font(Font&&) = delete;
Font(const Font&) = delete;
Font& operator=(Font&&) = delete;
Font& operator=(const Font&) = delete;
Font(iAllocator* alloc, const std::string& path) : alloc_(alloc) {
std::ifstream ifs(path, std::ios::binary);
if (!ifs) {
Abort("failed to open: "+path);
}
ifs.seekg(0, std::ios::end);
const size_t size = ifs.tellg();
ifs.seekg(0);
buf_ = alloc_->MakeUniqArray<uint8_t>(size);
ifs.read(reinterpret_cast<char*>(buf_.get()), size);
const int offset = stbtt_GetFontOffsetForIndex(buf_.get(), 0);
if (!stbtt_InitFont(&stb_, buf_.get(), offset)) {
Abort("invalid font: "+path);
}
}
Colorbuffer&& RenderGlyph(char16_t c, uint32_t fontsize = 32) {
int w, h;
const float s = stbtt_ScaleForPixelHeight(&stb_, fontsize*1.f);
const uint8_t* src = stbtt_GetCodepointBitmap(&stb_, 0, s, c, &w, &h, 0, 0);
Colorbuffer ret(alloc_, w, h);
float* dst = ret.ptr();
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
*dst = static_cast<float>(*src*1./UINT8_MAX);
++dst, ++src;
}
}
return std::move(ret);
}
private:
iAllocator* alloc_;
stbtt_fontinfo stb_;
UniqPtr<uint8_t> buf_;
};
}

55
src/Frame.h Normal file
View File

@@ -0,0 +1,55 @@
#pragma once
#include <vector>
#include "iDrawable.h"
#include "iWritable.h"
namespace gj {
class Frame : public iDrawable, public iWritable {
public:
Frame() = delete;
Frame(Frame&&) = delete;
Frame(const Frame&) = delete;
Frame& operator=(Frame&&) = delete;
Frame& operator=(const Frame&) = delete;
Frame(size_t dres, size_t wres) {
draw_.reserve(dres);
write_.reserve(wres);
}
void Clear() {
draw_.clear();
write_.clear();
}
void Add(const iDrawable* d) {
draw_.push_back(d);
}
void Add(const iWritable* w) {
write_.push_back(w);
}
void Draw(Colorbuffer& fb) const override {
for (auto d : draw_) {
d->Draw(fb);
}
}
void Write(Textbuffer& fb) const override {
for (auto w : write_) {
w->Write(fb);
}
}
private:
std::vector<const iDrawable*> draw_;
std::vector<const iWritable*> write_;
};
}

19
src/Game.cc Normal file
View File

@@ -0,0 +1,19 @@
#include "Game.h"
#include "PlayScene.h"
gj::Game::Game(gj::Game::Param&& p) :
alloc_(p.alloc),
clock_(p.clock),
logger_(p.h),
w_(p.w), h_(p.h),
frame_(kReserveDrawable, kReserveWritable) {
gj::PlayScene::Param param;
param.alloc = alloc_;
param.clock = &clock_;
param.logger = &logger_;
param.w = w_;
param.h = h_;
param.score = "test"; /* TODO test */
scene_ = alloc_->MakeUniq<gj::iScene, gj::PlayScene>(std::move(param));
}

70
src/Game.h Normal file
View File

@@ -0,0 +1,70 @@
#pragma once
#include <cmath>
#include <cstdint>
#include <string>
#include "Frame.h"
#include "iDrawable.h"
#include "iWritable.h"
#include "iScene.h"
#include "Logger.h"
#include "TickingClock.h"
namespace gj {
class Game : public iDrawable, public iWritable {
public:
static constexpr size_t kReserveDrawable = 256;
static constexpr size_t kReserveWritable = 64;
struct Param {
iAllocator* alloc;
const iClock* clock;
uint32_t w, h;
};
Game() = delete;
Game(Game&&) = delete;
Game(const Game&) = delete;
Game& operator=(Game&&) = delete;
Game& operator=(const Game&) = delete;
Game(Param&& p);
void Update() {
clock_.Tick();
frame_.Clear();
UniqPtr<iScene> next = scene_->Update(frame_);
if (next) {
scene_ = std::move(next);
}
}
void Draw(Colorbuffer& fb) const override {
frame_.Draw(fb);
}
void Write(Textbuffer& fb) const override {
frame_.Write(fb);
logger_.Write(fb);
}
private:
iAllocator* alloc_;
TickingClock clock_;
Logger logger_;
uint32_t w_, h_;
Frame frame_;
UniqPtr<iScene> scene_;
};
}

97
src/LinearAllocator.h Normal file
View File

@@ -0,0 +1,97 @@
#pragma once
#include <cassert>
#include <cstddef>
#include <cstdint>
#include "common.h"
#include "iAllocator.h"
namespace gj {
class LinearAllocator : public iAllocator {
public:
using iAllocator::Alloc;
using iAllocator::MakeUniq;
LinearAllocator() = delete;
LinearAllocator(LinearAllocator&&) = delete;
LinearAllocator(const LinearAllocator&) = delete;
LinearAllocator& operator=(LinearAllocator&&) = delete;
LinearAllocator& operator=(const LinearAllocator&) = delete;
LinearAllocator(void* ptr, const size_t size) :
ptr_(static_cast<uint8_t*>(ptr)) {
assert(size >= sizeof(Header)*2);
auto head = reinterpret_cast<Header*>(ptr_);
head->prev = 0;
head->next = size - sizeof(Header);
head->size = sizeof(Header);
auto tail = reinterpret_cast<Header*>(ptr_+head->next);
tail->prev = head->next;
tail->next = 0;
tail->size = sizeof(Header);
}
void* Alloc(const size_t size) override {
const size_t aligned_size = (((size >> 3) + !!(size & 0b111))) << 3;
const size_t whole_size = sizeof(Header) + aligned_size;
assert(aligned_size >= size);
auto h = reinterpret_cast<Header*>(ptr_);
while (h->next) {
const size_t remain = h->next - h->size;
if (remain >= whole_size) {
auto hprev = h;
auto hnext = reinterpret_cast<Header*>(reinterpret_cast<uint8_t*>(hprev) + hprev->next);
h = reinterpret_cast<Header*>(reinterpret_cast<uint8_t*>(hprev) + hprev->size);
h->prev = hprev->size;
h->next = remain;
h->size = whole_size;
hprev->next = h->prev;
hnext->prev = h->next;
return h + 1;
}
h = reinterpret_cast<Header*>(reinterpret_cast<uint8_t*>(h) + h->next);
}
Abort("LinearAllocator Allocation Failure");
}
void Free(void* ptr) override {
if (!ptr) return;
uint8_t* uptr = reinterpret_cast<uint8_t*>(ptr) - sizeof(Header);
auto h = reinterpret_cast<Header*>(uptr);
auto hprev = reinterpret_cast<Header*>(uptr - h->prev);
auto hnext = reinterpret_cast<Header*>(uptr + h->next);
hprev->next += h->next;
hnext->prev += h->prev;
}
private:
/* |------prev--------||--------next--------| */
/* |----size-----| */
/* memory: HHAAAAAAAAAAAAAAAAUUHHAAAAAAAAAAAAAUUUUUUUHHAAAAA */
/* H: header, A: allocated area, U: unused area */
struct Header {
size_t prev;
size_t next;
size_t size;
};
static_assert(sizeof(Header) == sizeof(size_t)*3, "padding detected in memory header");
uint8_t* const ptr_;
};
}

52
src/Logger.h Normal file
View File

@@ -0,0 +1,52 @@
#pragma once
#include <vector>
#include "iDrawable.h"
#include "iLogger.h"
#include "Text.h"
namespace gj {
class Logger : public iWritable, public iLogger {
public:
Logger(Logger&&) = delete;
Logger(const Logger&) = delete;
Logger& operator=(Logger&&) = delete;
Logger& operator=(const Logger&) = delete;
Logger(uint32_t height) : height_(height) {
}
void Write(Textbuffer& fb) const override {
for (const auto& text : lines_) {
text.Write(fb);
}
}
void Print(const std::wstring& msg) override {
size_t n = lines_.size();
if (n >= height_) {
lines_.erase(lines_.begin());
--n;
for (size_t i = 0; i < n; ++i) {
lines_[i].SetPosition(0, static_cast<int32_t>(i));
}
}
Text text(msg, 0, static_cast<uint32_t>(n));
lines_.push_back(std::move(text));
}
private:
uint32_t height_;
std::vector<Text> lines_;
};
}

119
src/Lua.cc Normal file
View File

@@ -0,0 +1,119 @@
#include "Lua.h"
class LuaFunc : public gj::iElementDriver {
public:
LuaFunc() = delete;
LuaFunc(LuaFunc&&) = delete;
LuaFunc(const LuaFunc&) = delete;
LuaFunc& operator=(LuaFunc&&) = delete;
LuaFunc& operator=(const LuaFunc&) = delete;
LuaFunc(lua_State* L) : L(L) {
func_ = luaL_ref(L, LUA_REGISTRYINDEX);
lua_createtable(L, 0, 0);
table_ = luaL_ref(L, LUA_REGISTRYINDEX);
}
~LuaFunc() {
luaL_unref(L, LUA_REGISTRYINDEX, func_);
}
void Update(Param& param) override {
lua_rawgeti(L, LUA_REGISTRYINDEX, func_);
lua_rawgeti(L, LUA_REGISTRYINDEX, table_);
for (const auto& p : param) {
lua_pushstring(L, p.first.c_str());
if (std::holds_alternative<int64_t>(p.second)) {
lua_pushinteger(L, std::get<int64_t>(p.second));
} else if (std::holds_alternative<double>(p.second)) {
lua_pushnumber(L, std::get<double>(p.second));
} else if (std::holds_alternative<std::string>(p.second)) {
const std::string str = std::get<std::string>(p.second);
lua_pushstring(L, str.c_str());
} else {
assert(false);
}
lua_rawset(L, -3);
}
const int ret = lua_pcall(L, 1, 0, 0);
if (ret) {
gj::Abort(std::string("Lua error: ")+lua_tostring(L, -1));
}
lua_rawgeti(L, LUA_REGISTRYINDEX, table_);
for (auto& p : param) {
lua_pushstring(L, p.first.c_str());
lua_rawget(L, -2);
if (std::holds_alternative<int64_t>(p.second)) {
p.second = luaL_checkinteger(L, -1);
} else if (std::holds_alternative<double>(p.second)) {
p.second = luaL_checknumber(L, -1);
} else if (std::holds_alternative<std::string>(p.second)) {
p.second = luaL_checkstring(L, -1);
} else {
assert(false);
}
lua_pop(L, 1);
}
lua_pop(L, 1);
}
private:
lua_State* L;
int func_ = LUA_REFNIL;
int table_ = LUA_REFNIL;
};
static int CallFactory_(lua_State* L) {
gj::iAllocator* alloc =
reinterpret_cast<gj::iAllocator*>(lua_touserdata(L, lua_upvalueindex(1)));
gj::iElementFactory* factory =
reinterpret_cast<gj::iElementFactory*>(lua_touserdata(L, lua_upvalueindex(2)));
const lua_Integer st = luaL_checkinteger(L, 1);
const lua_Integer ed = luaL_checkinteger(L, 2);
if (st >= ed) {
return luaL_error(L, "invalid period");
}
if (!lua_isfunction(L, 3)) {
return luaL_error(L, "no driver specified");
}
lua_pushvalue(L, 3);
factory->Create(gj::Period(st, ed), alloc->MakeUniq<gj::iElementDriver, LuaFunc>(L));
return 0;
}
gj::Lua::Lua(iAllocator* alloc, const FactoryMap& factory, const std::string& path) {
L = luaL_newstate();
if (L == nullptr) {
Abort("lua_newstate failure");
}
for (const auto& f : factory) {
lua_pushstring(L, f.first.c_str());
lua_pushlightuserdata(L, alloc);
lua_pushlightuserdata(L, f.second);
lua_pushcclosure(L, CallFactory_, 2);
lua_rawset(L, LUA_GLOBALSINDEX);
}
if (0 != luaL_loadfile(L, path.c_str())) {
const char* msg = lua_tostring(L, -1);
Abort(std::string("luaL_loadfile failure: ") + msg);
}
const int ret = lua_pcall(L, 0, 0, 0);
if (ret) {
gj::Abort(std::string("Lua error: ") + lua_tostring(L, -1));
}
}

38
src/Lua.h Normal file
View File

@@ -0,0 +1,38 @@
#pragma once
#include <map>
#include <string>
#include "thirdparty/lua.hpp"
#include "common.h"
#include "iAllocator.h"
#include "iElementFactory.h"
namespace gj {
class Lua {
public:
using FactoryMap = std::map<std::string, iElementFactory*>;
Lua() = delete;
Lua(Lua&&) = delete;
Lua(const Lua&) = delete;
Lua& operator=(Lua&&) = delete;
Lua& operator=(const Lua&) = delete;
Lua(iAllocator* alloc, const FactoryMap& factory, const std::string& path);
~Lua() {
lua_close(L);
}
private:
lua_State* L = nullptr;
};
}

38
src/OffsetClock.h Normal file
View File

@@ -0,0 +1,38 @@
#pragma once
#include <cstdint>
#define NOMINMAX
#include <windows.h>
#undef NOMINMAX
#include "iClock.h"
namespace gj {
class OffsetClock : public iClock {
public:
OffsetClock(OffsetClock&&) = default;
OffsetClock(const OffsetClock&) = default;
OffsetClock& operator=(OffsetClock&&) = default;
OffsetClock& operator=(const OffsetClock&) = default;
OffsetClock(const iClock* parent, uint64_t offset) :
parent_(parent), offset_(offset) {
}
OffsetClock(const iClock* parent) : OffsetClock(parent, parent->now()) {
}
uint64_t now() const override {
return parent_->now() - offset_;
}
private:
const iClock* parent_;
uint64_t offset_;
};
}

29
src/Period.h Normal file
View File

@@ -0,0 +1,29 @@
#pragma once
#include <cstdint>
namespace gj {
struct Period {
public:
Period() = delete;
Period(uint64_t st, uint64_t ed) : start(st), end(ed) {
assert(st <= ed);
}
bool isHit(uint64_t now) const {
return start <= now && now < end;
}
uint64_t duration() const {
return end - start;
}
uint64_t start;
uint64_t end;
};
}

10
src/PlayScene.cc Normal file
View File

@@ -0,0 +1,10 @@
#include "PlayScene.h"
gj::PlayScene::PlayScene(gj::PlayScene::Param&& p) :
alloc_(p.alloc), logger_(p.logger), w_(p.w), h_(p.h),
clock_(p.clock) {
lua_ = alloc_->MakeUniq<Lua>(alloc_, Lua::FactoryMap(), "res/score/"+p.score+".lua");
logger_->Print(L"PlayScene init");
}

51
src/PlayScene.h Normal file
View File

@@ -0,0 +1,51 @@
#pragma once
#include "Frame.h"
#include "iAllocator.h"
#include "iLogger.h"
#include "iScene.h"
#include "Lua.h"
#include "OffsetClock.h"
namespace gj {
class PlayScene : public iScene {
public:
struct Param {
iAllocator* alloc;
iLogger* logger;
const iClock* clock;
uint32_t w, h;
std::string score;
};
PlayScene() = delete;
PlayScene(PlayScene&&) = delete;
PlayScene(const PlayScene&) = delete;
PlayScene& operator=(PlayScene&&) = delete;
PlayScene& operator=(const PlayScene&) = delete;
PlayScene(Param&& p);
UniqPtr<iScene> Update(Frame& f) override {
return nullptr;
}
private:
iAllocator* alloc_;
iLogger* logger_;
uint32_t w_, h_;
OffsetClock clock_;
UniqPtr<Lua> lua_;
};
}

72
src/Rasterbuffer.h Normal file
View File

@@ -0,0 +1,72 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include "iAllocator.h"
namespace gj {
template <typename T>
class Rasterbuffer {
public:
Rasterbuffer() = delete;
Rasterbuffer(const Rasterbuffer&) = delete;
Rasterbuffer& operator=(const Rasterbuffer&) = delete;
Rasterbuffer(iAllocator* alloc, uint32_t w, uint32_t h) :
alloc_(alloc), w_(w), h_(h),
buf_(alloc->Alloc<T>(static_cast<uint64_t>(w_)*h_)) {
_ASSERT(buf_ != nullptr);
}
~Rasterbuffer() {
alloc_->Free(buf_);
}
Rasterbuffer(Rasterbuffer&& src) noexcept :
alloc_(src.alloc_), w_(src.w_), h_(src.h_), buf_(src.buf_) {
src.buf_ = nullptr;
}
Rasterbuffer& operator=(Rasterbuffer&& src) noexcept {
if (this != &src) {
alloc_ = src.alloc_;
w_ = src.w_;
h_ = src.h_;
buf_ = src.buf_;
src.buf_ = nullptr;
}
return *this;
}
void Clear() {
memset(buf_, 0, static_cast<uint64_t>(w_) * h_ * sizeof(T));
}
uint32_t width() const {
return w_;
}
uint32_t height() const {
return h_;
}
T* ptr() {
return buf_;
}
const T* ptr() const {
return buf_;
}
private:
iAllocator* alloc_;
uint32_t w_, h_;
T* buf_;
};
using Colorbuffer = Rasterbuffer<float>;
using Textbuffer = Rasterbuffer<char16_t>;
}

63
src/StackAllocator.h Normal file
View File

@@ -0,0 +1,63 @@
#pragma once
#include <cassert>
#include <cstddef>
#include <cstdint>
#include "iAllocator.h"
namespace gj {
class StackAllocator : public iAllocator {
public:
using iAllocator::Alloc;
using iAllocator::MakeUniq;
StackAllocator() = delete;
StackAllocator(StackAllocator&&) = delete;
StackAllocator(const StackAllocator&) = delete;
StackAllocator& operator=(StackAllocator&&) = delete;
StackAllocator& operator=(const StackAllocator&) = delete;
StackAllocator(void* ptr, const size_t size) :
begin_(static_cast<uint8_t*>(ptr)),
end_ (begin_+size),
next_ (begin_) {
}
~StackAllocator() {
assert(refcnt_ == 0);
}
void* Alloc(const size_t size) override {
const size_t aligned_size = ((size >> 3) + !!(size & 0b111)) << 3;
void* ret = next_;
next_ += aligned_size;
if (next_ >= end_) {
return nullptr;
}
++refcnt_;
return ret;
}
void Free(void* ptr) override {
if (!ptr) return;
assert(refcnt_);
if (--refcnt_ == 0) {
next_ = begin_;
}
}
private:
uint8_t* const begin_;
uint8_t* const end_;
uint8_t* next_;
size_t refcnt_ = 0;
};
}

35
src/SystemClock.h Normal file
View File

@@ -0,0 +1,35 @@
#pragma once
#include <cstdint>
#define NOMINMAX
#include <windows.h>
#undef NOMINMAX
#include "iClock.h"
namespace gj {
class SystemClock : public iClock {
public:
static const SystemClock& instance() {
static const SystemClock instance_;
return instance_;
}
SystemClock() = default;
SystemClock(SystemClock&&) = default;
SystemClock(const SystemClock&) = default;
SystemClock& operator=(SystemClock&&) = default;
SystemClock& operator=(const SystemClock&) = default;
uint64_t now() const override {
return GetTickCount64();
}
};
}

47
src/Text.h Normal file
View File

@@ -0,0 +1,47 @@
#pragma once
#include <cstdint>
#include <string>
#include "iWritable.h"
namespace gj {
class Text : public WritableBase {
public:
Text(Text&&) = default;
Text(const Text&) = default;
Text& operator=(Text&&) = default;
Text& operator=(const Text&) = default;
Text(const std::wstring& str, uint32_t x = 0, uint32_t y = 0) :
WritableBase(x, y), entity_(str) {
}
void Write(Textbuffer& fb) const override {
const int32_t w = static_cast<int32_t>(fb.width());
const int32_t h = static_cast<int32_t>(fb.height());
char16_t* ptr = fb.ptr();
int32_t x = x_, y = y_;
if (y >= h) return;
const size_t len = entity_.size();
for (size_t i = 0; i < len; ++i) {
if (x < 0) continue;
if (x >= w) return;
ptr[x+y*w] = entity_[i];
x += (entity_[i] > UINT8_MAX)? 2: 1;
}
}
private:
std::wstring entity_;
};
}

87
src/Texture.cc Normal file
View File

@@ -0,0 +1,87 @@
#include "Texture.h"
void gj::Texture::Draw(Colorbuffer& fb) const {
const int32_t w = static_cast<int32_t>(fb.width());
const int32_t h = static_cast<int32_t>(fb.height());
const int32_t srcw = static_cast<int32_t>(src_.width());
const int32_t srch = static_cast<int32_t>(src_.height());
/* dst coordinate */
vec3 p[4] = {
{ -1, 1, 1 },
{ -1, -1, 1 },
{ 1, -1, 1 },
{ 1, 1, 1 },
};
for (size_t i = 0; i < 4; ++i) {
p[i] = ::linalg::mul(mat_, p[i]);
}
const double pl = std::min({p[0].x, p[1].x, p[2].x, p[3].x})-.1;
const double pr = std::max({p[0].x, p[1].x, p[2].x, p[3].x})+.1;
const double pu = std::max({p[0].y, p[1].y, p[2].y, p[3].y})+.1;
const double pb = std::min({p[0].y, p[1].y, p[2].y, p[3].y})-.1;
const double pw = pr - pl;
const double ph = pu - pb;
const int32_t pli = static_cast<int32_t>((pl + 1) / 2 * w);
const int32_t pri = static_cast<int32_t>((pr + 1) / 2 * w);
const int32_t pui = static_cast<int32_t>((pu + 1) / 2 * h);
const int32_t pbi = static_cast<int32_t>((pb + 1) / 2 * h);
const int32_t pwi = pri - pli;
const int32_t phi = pui - pbi;
/* src coordinate */
vec3 q[4] = {
{ pl, pu, 1 },
{ pl, pb, 1 },
{ pr, pb, 1 },
{ pr, pu, 1 },
};
for (size_t i = 0; i < 4; ++i) {
q[i] = ::linalg::mul(invmat_, q[i]);
}
const double ql = std::min({q[0].x, q[1].x, q[2].x, q[3].x});
const double qr = std::max({q[0].x, q[1].x, q[2].x, q[3].x});
const double qu = std::max({q[0].y, q[1].y, q[2].y, q[3].y});
const double qb = std::min({q[0].y, q[1].y, q[2].y, q[3].y});
const double qldx = q[0].x - q[1].x;
const double qrdx = q[3].x - q[2].x;
const double qldy = q[0].y - q[1].y;
const double qrdy = q[3].y - q[2].y;
/* blit with transformation */
const float* src = src_.ptr();
float* dst = fb.ptr();
for (int32_t y = 0; y <= phi; ++y) {
const double yfr = y*1. / phi;
const double lxf = qldx * yfr + q[1].x;
const double rxf = qrdx * yfr + q[2].x;
const double ax = (rxf - lxf) / pwi;
const double lyf = qldy * yfr + q[1].y;
const double ryf = qrdy * yfr + q[2].y;
const double ay = (ryf - lyf) / pwi;
for (int32_t x = 0; x <= pwi; ++x) {
const double xf = lxf + ax*x;
const double yf = lyf + ay*x;
if (std::abs(xf) > 1 || std::abs(yf) > 1) continue;
int32_t srcx = static_cast<int32_t>((xf+1)/2 * srcw);
int32_t srcy = srch - 1 - static_cast<int32_t>((yf+1)/2 * srch);
if (srcx >= srcw) srcx = srcw - 1;
if (srcy >= srch) srcy = srch - 1;
const int32_t dstx = pli + x;
const int32_t dsty = (h-pui) + y;
if (dstx < 0 || w <= dstx) continue;
if (dsty < 0 || h <= dsty) continue;
dst[dstx + w*dsty] = src[srcx + srcw*srcy];
}
}
}

37
src/Texture.h Normal file
View File

@@ -0,0 +1,37 @@
#pragma once
#include <algorithm>
#include "thirdparty/linalg.h"
#include "iDrawable.h"
#include "Rasterbuffer.h"
namespace gj {
class Texture : public DrawableBase {
public:
Texture() = delete;
Texture(Texture&&) = default;
Texture(const Texture&) = default;
Texture& operator=(Texture&&) = default;
Texture& operator=(const Texture&) = default;
Texture(Colorbuffer&& src) : src_(std::move(src)) {
}
void Draw(Colorbuffer& fb) const override;
void SetSource(Colorbuffer&& src) {
src_ = std::move(src);
}
private:
Colorbuffer src_;
};
}

43
src/TickingClock.h Normal file
View File

@@ -0,0 +1,43 @@
#pragma once
#include <cstdint>
#define NOMINMAX
#include <windows.h>
#undef NOMINMAX
#include "iClock.h"
namespace gj {
class TickingClock : public iClock {
public:
TickingClock() = delete;
TickingClock(TickingClock&&) = default;
TickingClock(const TickingClock&) = default;
TickingClock& operator=(TickingClock&&) = default;
TickingClock& operator=(const TickingClock&) = default;
TickingClock(const iClock* parent) : parent_(parent) {
Tick();
}
void Tick() {
now_ = parent_->now();
}
uint64_t now() const override {
return now_;
}
private:
const iClock* parent_;
uint64_t now_;
};
}

87
src/Win32Console.cc Normal file
View File

@@ -0,0 +1,87 @@
#include "Win32Console.h"
#include <cmath>
static void CalcChar(CHAR_INFO& c, float color, uint16_t text) {
constexpr wchar_t chars[] = L" .,:x!|X#%@$M";
constexpr uint8_t attrs[] = {
FOREGROUND_INTENSITY,
BACKGROUND_INTENSITY,
BACKGROUND_INTENSITY | FOREGROUND_RED,
BACKGROUND_RED | FOREGROUND_INTENSITY,
};
constexpr size_t char_expr_count = sizeof(chars)/sizeof(chars[0])-1;
constexpr size_t attr_expr_count = sizeof(attrs)/sizeof(attrs[0]);
constexpr size_t reso = char_expr_count*attr_expr_count;
/* post effect */
color = static_cast<float>(std::pow(color, 1.9));
int8_t a = static_cast<int8_t>(color*reso);
if (a >= reso) a = reso-1;
if (a < 0) a = 0;
size_t ci = a%char_expr_count;
size_t ai = a/char_expr_count;
if (ai%2 == 1) ci = char_expr_count-ci-1;
c.Char.UnicodeChar = chars[ci];
c.Attributes = attrs[ai];
if (text) c.Char.UnicodeChar = text;
}
void gj::Win32Console::main() {
bool shown = false;
while (alive_.load()) {
if (shown_.load()) {
if (!shown) {
shown = true;
ShowWindow(win_, TRUE);
}
constexpr CONSOLE_CURSOR_INFO cursor{ 1, FALSE };
SetConsoleCursorInfo(screen_, &cursor);
CHAR_INFO* c = chars_.get();
{ /* critical section */
std::lock_guard<std::mutex> _(mtx_);
const float* cb = cb_main_.ptr();
const char16_t* tb = tb_main_.ptr();
for (uint32_t y = 0; y < h_; ++y) {
bool mb = false;
for (uint32_t x = 0; x < w_; ++x) {
if (mb) {
*c = *(c - 1);
(c - 1)->Attributes |= COMMON_LVB_LEADING_BYTE;
c->Attributes |= COMMON_LVB_TRAILING_BYTE;
mb = false;
} else {
CalcChar(*c, *cb, *tb);
mb = *tb > UINT8_MAX;
}
++cb, ++tb, ++c;
}
}
}
const COORD size = { static_cast<SHORT>(w_), static_cast<SHORT>(h_), };
const COORD pos = { 0, 0, };
SMALL_RECT rc = { 0, 0, static_cast<SHORT>(w_), static_cast<SHORT>(h_), };
WriteConsoleOutput(screen_, chars_.get(), size, pos, &rc);
} else {
if (shown) {
ShowWindow(win_, FALSE);
shown = false;
}
}
Sleep(30);
}
}

116
src/Win32Console.h Normal file
View File

@@ -0,0 +1,116 @@
#pragma once
#include <atomic>
#include <cstdint>
#include <mutex>
#include <thread>
#include <vector>
#define NOMINMAX
#include <windows.h>
#undef NOMINMAX
#include "iConsole.h"
namespace gj {
class Win32Console : public iConsole {
public:
Win32Console() = delete;
Win32Console(Win32Console&&) = delete;
Win32Console(const Win32Console&) = delete;
Win32Console& operator=(Win32Console&&) = delete;
Win32Console& operator=(const Win32Console&) = delete;
Win32Console(iAllocator* alloc, uint32_t w, uint32_t h) :
w_(w), h_(h), shown_(false), alive_(true),
th_([this]() { main(); }),
cb_main_(alloc, w, h), cb_sub_(alloc, w, h),
tb_main_(alloc, w, h), tb_sub_(alloc, w, h),
chars_(std::make_unique<CHAR_INFO[]>(static_cast<uint64_t>(w)*h)),
win_(GetConsoleWindow()) {
_ASSERT(win_);
screen_ = GetStdHandle(STD_OUTPUT_HANDLE);
_ASSERT(screen_ != INVALID_HANDLE_VALUE);
CONSOLE_SCREEN_BUFFER_INFOEX size;
size.cbSize = sizeof(size);
GetConsoleScreenBufferInfoEx(screen_, &size);
COORD c;
c.X = w_;
c.Y = h_;
size.dwSize = c;
size.srWindow.Left = 0;
size.srWindow.Right = w_ + 1;
size.srWindow.Top = 0;
size.srWindow.Bottom = h_ + 1;
SetConsoleScreenBufferInfoEx(screen_, &size);
ShowWindow(win_, FALSE);
SetWindowLong(win_, GWL_STYLE, GetWindowLong(win_, GWL_STYLE) & ~(WS_SIZEBOX | WS_MAXIMIZEBOX));
}
~Win32Console() {
alive_.store(false);
th_.join();
}
void Show() override {
shown_.store(true);
}
void Hide() override {
shown_.store(false);
}
Colorbuffer& TakeColorbuffer() override {
return cb_sub_;
}
void SwapColorbuffer() override {
std::lock_guard<std::mutex> _(mtx_);
std::swap(cb_main_, cb_sub_);
}
Textbuffer& TakeTextbuffer() override {
return tb_sub_;
}
void SwapTextbuffer() override {
std::lock_guard<std::mutex> _(mtx_);
std::swap(tb_main_, tb_sub_);
}
uint32_t width() const override {
return w_;
}
uint32_t height() const override {
return h_;
}
private:
const uint32_t w_, h_;
std::atomic_bool alive_;
std::atomic_bool shown_;
std::mutex mtx_;
std::thread th_;
Colorbuffer cb_main_;
Colorbuffer cb_sub_;
Textbuffer tb_main_;
Textbuffer tb_sub_;
std::unique_ptr<CHAR_INFO[]> chars_;
HANDLE screen_ = INVALID_HANDLE_VALUE;
HWND win_ = nullptr;
void main();
};
}

35
src/common.h Normal file
View File

@@ -0,0 +1,35 @@
#pragma once
#include <codecvt>
#include <cstdlib>
#include <sstream>
#include <string>
#define NOMINMAX
#include <windows.h>
#undef NOMINMAX
#include "thirdparty/linalg.h"
namespace gj {
using mat3 = ::linalg::mat<double, 3, 3>;
using vec3 = ::linalg::vec<double, 3>;
static inline std::wstring ConvertUtf8ToUtf16(const std::string& str) {
std::wostringstream conv;
conv << str.c_str();
return conv.str();
}
[[noreturn]]
static inline void Abort(const std::string& msg) {
MessageBox(NULL, ConvertUtf8ToUtf16(msg).c_str(), L"PROGRAM ABORTED", MB_OK);
std::exit(1);
}
}

76
src/iAllocator.h Normal file
View File

@@ -0,0 +1,76 @@
#pragma once
#include <cassert>
#include <cstddef>
#include <memory>
namespace gj {
class iAllocator {
public:
template <typename T>
struct Deleter {
Deleter() = default;
Deleter(Deleter&&) = default;
Deleter(const Deleter&) = delete;
Deleter& operator=(Deleter&&) = default;
Deleter& operator=(const Deleter&) = delete;
Deleter(iAllocator* alloc) : alloc_(alloc) {
}
void operator()(T* ptr) {
assert(alloc_);
ptr->~T();
alloc_->Free(ptr);
}
iAllocator* alloc_;
};
template <typename T>
using UniqPtr = std::unique_ptr<T, iAllocator::Deleter<T>>;
iAllocator(iAllocator&&) = default;
iAllocator(const iAllocator&) = default;
iAllocator& operator=(iAllocator&&) = default;
iAllocator& operator=(const iAllocator&) = default;
iAllocator() = default;
virtual ~iAllocator() = default;
virtual void* Alloc(const size_t size) = 0;
virtual void Free(void* ptr) = 0;
template <typename T>
T* Alloc(size_t n = 1) {
return reinterpret_cast<T*>(Alloc(sizeof(T) * n));
}
template <typename T, typename... Args>
UniqPtr<T> MakeUniq(Args&&... args) {
T* ptr = Alloc<T>();
return std::unique_ptr<T, Deleter<T>>(new(ptr) T(std::forward<Args>(args)...), Deleter<T>(this));
}
template <typename I, typename T, typename... Args>
UniqPtr<I> MakeUniq(Args&&... args) {
T* ptr = Alloc<T>();
return std::unique_ptr<I, Deleter<I>>(new(ptr) T(std::forward<Args>(args)...), Deleter<I>(this));
}
template <typename T>
UniqPtr<T> MakeUniqArray(size_t n) {
T* ptr = Alloc<T>(n);
return std::unique_ptr<T, Deleter<T>>(ptr, Deleter<T>(this));
}
};
template <typename T>
using UniqPtr = iAllocator::UniqPtr<T>;
}

25
src/iClock.h Normal file
View File

@@ -0,0 +1,25 @@
#pragma once
#include <cstdint>
namespace gj {
class iClock {
public:
iClock() = default;
iClock(iClock&&) = default;
iClock(const iClock&) = default;
iClock& operator=(iClock&&) = default;
iClock& operator=(const iClock&) = default;
virtual ~iClock() = default;
virtual uint64_t now() const = 0;
};
}

34
src/iConsole.h Normal file
View File

@@ -0,0 +1,34 @@
#pragma once
#include "Rasterbuffer.h"
namespace gj {
class iConsole {
public:
iConsole(iConsole&&) = delete;
iConsole(const iConsole&) = delete;
iConsole& operator=(iConsole&&) = delete;
iConsole& operator=(const iConsole&) = delete;
iConsole() = default;
virtual ~iConsole() = default;
virtual void Show() = 0;
virtual void Hide() = 0;
virtual Colorbuffer& TakeColorbuffer() = 0;
virtual void SwapColorbuffer() = 0;
virtual Textbuffer& TakeTextbuffer() = 0;
virtual void SwapTextbuffer() = 0;
virtual uint32_t width() const = 0;
virtual uint32_t height() const = 0;
};
}

49
src/iDrawable.h Normal file
View File

@@ -0,0 +1,49 @@
#pragma once
#include "common.h"
#include "Rasterbuffer.h"
namespace gj {
class iDrawable {
public:
iDrawable(iDrawable&&) = default;
iDrawable(const iDrawable&) = default;
iDrawable& operator=(iDrawable&&) = default;
iDrawable& operator=(const iDrawable&) = default;
iDrawable() = default;
virtual ~iDrawable() = default;
virtual void Draw(Colorbuffer& buf) const = 0;
};
class DrawableBase : public iDrawable {
public:
DrawableBase() = default;
~DrawableBase() = default;
DrawableBase(DrawableBase&&) = default;
DrawableBase(const DrawableBase&) = default;
DrawableBase& operator=(DrawableBase&&) = default;
DrawableBase& operator=(const DrawableBase&) = default;
void SetMatrix(const mat3& m) {
SetMatrix(mat3(m));
}
void SetMatrix(mat3&& m) {
mat_ = std::move(m);
invmat_ = ::linalg::inverse(mat_);
}
protected:
mat3 mat_ = ::linalg::identity;
mat3 invmat_ = ::linalg::identity;
};
}

28
src/iElement.h Normal file
View File

@@ -0,0 +1,28 @@
#pragma once
#include "Frame.h"
#include "Period.h"
namespace gj {
class iElement {
public:
iElement(iElement&&) = default;
iElement(const iElement&) = default;
iElement& operator=(iElement&&) = default;
iElement& operator=(const iElement&) = default;
iElement() = default;
virtual ~iElement() = default;
virtual void Update(Frame& f) = 0;
/* Interfaces had better not have a variable but this is for optimization. */
const Period period;
};
}

29
src/iElementDriver.h Normal file
View File

@@ -0,0 +1,29 @@
#pragma once
#include <map>
#include <string>
#include <variant>
namespace gj {
class iElementDriver {
public:
using Value = std::variant<int64_t, double, std::string>;
using Param = std::map<std::string, Value>;
iElementDriver(iElementDriver&&) = default;
iElementDriver(const iElementDriver&) = default;
iElementDriver& operator=(iElementDriver&&) = default;
iElementDriver& operator=(const iElementDriver&) = default;
iElementDriver() = default;
virtual ~iElementDriver() = default;
virtual void Update(Param&) = 0;
};
}

27
src/iElementFactory.h Normal file
View File

@@ -0,0 +1,27 @@
#pragma once
#include "iAllocator.h"
#include "iElement.h"
#include "iElementDriver.h"
#include "Period.h"
namespace gj {
class iElementFactory {
public:
iElementFactory(iElementFactory&&) = default;
iElementFactory(const iElementFactory&) = default;
iElementFactory& operator=(iElementFactory&&) = default;
iElementFactory& operator=(const iElementFactory&) = default;
iElementFactory() = default;
virtual ~iElementFactory() = default;
virtual UniqPtr<iElement> Create(const Period& p, UniqPtr<iElementDriver>&& drv) = 0;
};
}

24
src/iLogger.h Normal file
View File

@@ -0,0 +1,24 @@
#pragma once
#include <string>
namespace gj {
class iLogger {
public:
iLogger(iLogger&&) = delete;
iLogger(const iLogger&) = delete;
iLogger& operator=(iLogger&&) = delete;
iLogger& operator=(const iLogger&) = delete;
iLogger() = default;
virtual ~iLogger() = default;
virtual void Print(const std::wstring& msg) = 0;
};
}

30
src/iScene.h Normal file
View File

@@ -0,0 +1,30 @@
#pragma once
#include <cstdint>
#include "Frame.h"
#include "iAllocator.h"
#include "iDrawable.h"
#include "iWritable.h"
namespace gj {
class iScene {
public:
iScene() = default;
iScene(iScene&&) = default;
iScene(const iScene&) = default;
iScene& operator=(iScene&&) = default;
iScene& operator=(const iScene&) = default;
virtual ~iScene() = default;
/* Returns next scene if this scene ends, otherwise nullptr. */
virtual UniqPtr<iScene> Update(Frame& f) = 0;
};
}

46
src/iWritable.h Normal file
View File

@@ -0,0 +1,46 @@
#pragma once
#include <cstdint>
#include "Rasterbuffer.h"
namespace gj {
class iWritable {
public:
iWritable(iWritable&&) = default;
iWritable(const iWritable&) = default;
iWritable& operator=(iWritable&&) = default;
iWritable& operator=(const iWritable&) = default;
iWritable() = default;
virtual ~iWritable() = default;
virtual void Write(Textbuffer&) const = 0;
};
class WritableBase : public iWritable {
public:
WritableBase(WritableBase&&) = default;
WritableBase(const WritableBase&) = default;
WritableBase& operator=(WritableBase&&) = default;
WritableBase& operator=(const WritableBase&) = default;
WritableBase(int32_t x, int32_t y) : x_(x), y_(y) {
}
void SetPosition(int32_t x, int32_t y) {
x_ = x;
y_ = y;
}
protected:
int32_t x_ = 0, y_ = 0;
};
}

50
src/main.cc Normal file
View File

@@ -0,0 +1,50 @@
#include <iostream>
#include <string>
#define NOMINMAX
#include <windows.h>
#undef NOMINMAX
#include "common.h"
#include "Font.h"
#include "Game.h"
#include "LinearAllocator.h"
#include "SystemClock.h"
#include "Win32Console.h"
constexpr size_t kHeapSize = 1024*1024*64;
constexpr uint32_t kWidth = 96;
constexpr uint32_t kHeight = 28;
int main() {
auto memory = std::make_unique<uint8_t[]>(kHeapSize);
gj::LinearAllocator alloc(memory.get(), kHeapSize);
gj::Win32Console console(&alloc, kWidth, kHeight);
console.Show();
gj::Game::Param param;
param.alloc = &alloc;
param.clock = &gj::SystemClock::instance();
param.w = kWidth;
param.h = kHeight;
gj::Game game(std::move(param));
while (true) {
game.Update();
{
auto& fb = console.TakeColorbuffer();
fb.Clear();
game.Draw(fb);
console.SwapColorbuffer();
}
{
auto& fb = console.TakeTextbuffer();
fb.Clear();
game.Write(fb);
console.SwapTextbuffer();
}
Sleep(10);
}
}