Implements element handling.

This commit is contained in:
falsycat 2021-08-25 18:17:31 +09:00
parent 213902021c
commit e74a599ac1
20 changed files with 2081 additions and 76 deletions

View File

@ -156,8 +156,10 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\common.h" />
<ClInclude Include="src\ElementStore.h" />
<ClInclude Include="src\Font.h" />
<ClInclude Include="src\Game.h" />
<ClInclude Include="src\GlyphElementFactory.h" />
<ClInclude Include="src\iDrawable.h" />
<ClInclude Include="src\iElement.h" />
<ClInclude Include="src\iElementDriver.h" />
@ -180,6 +182,7 @@
<ClInclude Include="src\SystemClock.h" />
<ClInclude Include="src\Text.h" />
<ClInclude Include="src\Texture.h" />
<ClInclude Include="src\TextureElement.h" />
<ClInclude Include="src\TickingClock.h" />
<ClInclude Include="thirdparty\lauxlib.h" />
<ClInclude Include="thirdparty\linalg.h" />
@ -189,6 +192,7 @@
<ClInclude Include="thirdparty\lualib.h" />
<ClInclude Include="thirdparty\stb_truetype.h" />
<ClInclude Include="src\Win32Console.h" />
<ClInclude Include="thirdparty\utf8.h" />
</ItemGroup>
<ItemGroup>
<CopyFileToFolders Include="thirdparty\lua5.1.dll">

View File

@ -33,10 +33,10 @@
<ClCompile Include="src\Game.cc">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\PlayScene.cc">
<ClCompile Include="src\Lua.cc">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Lua.cc">
<ClCompile Include="src\PlayScene.cc">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
@ -143,6 +143,18 @@
<ClInclude Include="src\iElementDriver.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\TextureElement.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\GlyphElementFactory.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\ElementStore.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="thirdparty\utf8.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Library Include="thirdparty\lua5.1.lib" />

81
src/ElementStore.h Normal file
View File

@ -0,0 +1,81 @@
#pragma once
#include <algorithm>
#include <vector>
#include "iAllocator.h"
#include "iClock.h"
#include "iElement.h"
namespace gj {
class ElementStore {
public:
ElementStore() = delete;
ElementStore(ElementStore&&) = delete;
ElementStore(const ElementStore&) = delete;
ElementStore& operator=(ElementStore&&) = delete;
ElementStore& operator=(const ElementStore&) = delete;
ElementStore(iClock* clock, size_t reserve) : clock_(clock) {
pending_.reserve(reserve);
performing_.reserve(reserve);
}
void Schedule(UniqPtr<iElement>&& e) {
const uint64_t st = e->period.start;
auto insert_pos = std::find_if(pending_.begin(), pending_.end(),
[st](auto& x) { return x->period.start > st; });
pending_.insert(insert_pos, std::move(e));
}
void Update(Frame& frame) {
const uint64_t now = clock_->now();
auto pending_beg = pending_.begin();
auto pending_end = pending_.end();
auto pending_itr = pending_beg;
for (; pending_itr < pending_end; ++pending_itr) {
iElement* e = pending_itr->get();
if (e->period.start > now) {
break;
}
performing_.push_back(std::move(*pending_itr));
}
pending_.erase(pending_beg, pending_itr);
for (auto& eptr : performing_) {
iElement* e = eptr.get();
if (e->period.end <= now) {
eptr = nullptr;
} else {
e->Update(frame, e->period.Normalize(now));
}
}
/* erase nullptr */
performing_.erase(
std::remove_if(
performing_.begin(), performing_.end(), [](auto& x) {return !x; }),
performing_.end());
}
bool IsEmpty() {
return pending_.empty() && performing_.empty();
}
private:
const iClock* clock_;
std::vector<UniqPtr<iElement>> pending_;
std::vector<UniqPtr<iElement>> performing_;
};
}

View File

@ -42,7 +42,7 @@ namespace gj {
}
}
Colorbuffer&& RenderGlyph(char16_t c, uint32_t fontsize = 32) {
Colorbuffer RenderGlyph(char16_t c, uint32_t fontsize = 32) {
int w, h;
const float s = stbtt_ScaleForPixelHeight(&stb_, fontsize*1.f);
@ -60,6 +60,60 @@ namespace gj {
}
return std::move(ret);
}
Colorbuffer RenderGlyphs(const std::wstring& str, uint32_t fontsize = 32) {
const float s = stbtt_ScaleForPixelHeight(&stb_, fontsize * 1.f);
int ascent;
stbtt_GetFontVMetrics(&stb_, &ascent, 0, 0);
const int baseline = static_cast<int>(ascent * s);
/* calculate bitmap size */
float h = 0, w = 2;
for (auto c : str) {
int advance, lsb;
stbtt_GetCodepointHMetrics(&stb_, c, &advance, &lsb);
int x0, y0, x1, y1;
const float x_shift = w - (int) w;
stbtt_GetCodepointBitmapBoxSubpixel(&stb_, c, s, s, x_shift, 0, &x0, &y0, &x1, &y1);
const int ch = y1 - y0;
if (h < ch) h = static_cast<float>(ch);
w += advance * s;
}
Colorbuffer buf(alloc_, static_cast<uint32_t>(w), static_cast<uint32_t>(h));
buf.Clear();
float* dst = buf.ptr();
float x = 2;
for (auto c : str) {
int advance, lsb;
stbtt_GetCodepointHMetrics(&stb_, c, &advance, &lsb);
int x0, y0, x1, y1;
const float x_shift = x - (int)x;
stbtt_GetCodepointBitmapBoxSubpixel(&stb_, c, s, s, x_shift, 0, &x0, &y0, &x1, &y1);
uint8_t ptr[64][64] = {0};
stbtt_MakeCodepointBitmapSubpixel(&stb_, &ptr[0][0], x1-x0, y1-y0, 64, s, s, x_shift, 0, c);
const int l = static_cast<int>(x) + x0;
const int r = static_cast<int>(x) + x1;
const int u = baseline + y0;
const int b = baseline + y1;
for (int y = 0; y < b-u; ++y) {
for (int x = 0; x < r-l; ++x) {
dst[y*static_cast<int>(w) + l+x] = static_cast<float>(ptr[y][x]*1. / UINT8_MAX);
}
}
x += advance * s;
}
return std::move(buf);
}
private:
iAllocator* alloc_;

57
src/GlyphElementFactory.h Normal file
View File

@ -0,0 +1,57 @@
#pragma once
#include "common.h"
#include "Font.h"
#include "iAllocator.h"
#include "iClock.h"
#include "iElementFactory.h"
#include "Texture.h"
#include "TextureElement.h"
namespace gj {
class GlyphElementFactory : public iElementFactory {
public:
GlyphElementFactory(GlyphElementFactory&&) = delete;
GlyphElementFactory(const GlyphElementFactory&) = delete;
GlyphElementFactory& operator=(GlyphElementFactory&&) = delete;
GlyphElementFactory& operator=(const GlyphElementFactory&) = delete;
GlyphElementFactory(iAllocator* alloc) : alloc_(alloc) {
}
UniqPtr<iElement> Create(Param&& param) override {
if (param.custom.size() != 3) return nullptr;
const std::string text = std::get<std::string>(param.custom[0]);
const std::string name = std::get<std::string>(param.custom[1]);
const intmax_t size = std::get<double>(param.custom[2]);
auto& font = FindOrCreateFont(name);
auto tex = std::move(font.RenderGlyphs(ConvertUtf8ToUtf16(text), size)); /* TODO */
return alloc_->MakeUniq<iElement, TextureElement>(
param.period, std::move(tex), std::move(param.driver));
}
private:
Font& FindOrCreateFont(const std::string& name) {
auto found = fonts_.find(name);
if (found != fonts_.end()) {
return *found->second;
}
auto f = alloc_->MakeUniq<Font>(alloc_, "res/font/"+name);
auto ptr = f.get();
fonts_[name] = std::move(f);
return *ptr;
}
iAllocator* alloc_;
std::map<std::string, UniqPtr<Font>> fonts_;
};
}

View File

@ -76,6 +76,9 @@ public:
hprev->next += h->next;
hnext->prev += h->prev;
// chaos test
// std::memset(h, 0xFF, h->next);
}
private:

View File

@ -1,6 +1,45 @@
#include "Lua.h"
struct LuaPusher {
LuaPusher() = delete;
LuaPusher(lua_State* L) : L(L) {
}
void operator()(int64_t v) {
lua_pushinteger(L, v);
}
void operator()(double v) {
lua_pushnumber(L, v);
}
void operator()(const std::string& v) {
lua_pushstring(L, v.c_str());
}
private:
lua_State* L;
};
struct LuaTaker {
LuaTaker() = delete;
LuaTaker(lua_State* L, int index) : L(L), index_(index) {
}
void operator()(int64_t& v) {
v = luaL_checkinteger(L, index_);
}
void operator()(double& v) {
v = luaL_checknumber(L, index_);
}
void operator()(std::string& v) {
v = luaL_checkstring(L, index_);
}
private:
lua_State* L;
int index_;
};
class LuaFunc : public gj::iElementDriver {
public:
LuaFunc() = delete;
@ -10,7 +49,8 @@ class LuaFunc : public gj::iElementDriver {
LuaFunc& operator=(LuaFunc&&) = delete;
LuaFunc& operator=(const LuaFunc&) = delete;
LuaFunc(lua_State* L) : L(L) {
LuaFunc(lua_State* L, int index) : L(L) {
lua_pushvalue(L, index);
func_ = luaL_ref(L, LUA_REGISTRYINDEX);
lua_createtable(L, 0, 0);
@ -20,26 +60,19 @@ class LuaFunc : public gj::iElementDriver {
luaL_unref(L, LUA_REGISTRYINDEX, func_);
}
void Update(Param& param) override {
void Update(Param& param, double t) override {
lua_rawgeti(L, LUA_REGISTRYINDEX, func_);
lua_pushnumber(L, t);
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);
}
std::visit(LuaPusher(L), p.second);
lua_rawset(L, -3);
}
const int ret = lua_pcall(L, 1, 0, 0);
const int ret = lua_pcall(L, 2, 0, 0);
if (ret) {
gj::Abort(std::string("Lua error: ")+lua_tostring(L, -1));
}
@ -48,16 +81,7 @@ class LuaFunc : public gj::iElementDriver {
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);
}
std::visit(LuaTaker(L, -1), p.second);
lua_pop(L, 1);
}
lua_pop(L, 1);
@ -74,25 +98,46 @@ class LuaFunc : public gj::iElementDriver {
static int CallFactory_(lua_State* L) {
gj::iAllocator* alloc =
reinterpret_cast<gj::iAllocator*>(lua_touserdata(L, lua_upvalueindex(1)));
gj::ElementStore* store =
reinterpret_cast<gj::ElementStore*>(lua_touserdata(L, lua_upvalueindex(2)));
gj::iElementFactory* factory =
reinterpret_cast<gj::iElementFactory*>(lua_touserdata(L, lua_upvalueindex(2)));
reinterpret_cast<gj::iElementFactory*>(lua_touserdata(L, lua_upvalueindex(3)));
const int n = lua_gettop(L);
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)) {
if (!lua_isfunction(L, n)) {
return luaL_error(L, "no driver specified");
}
lua_pushvalue(L, 3);
factory->Create(gj::Period(st, ed), alloc->MakeUniq<gj::iElementDriver, LuaFunc>(L));
gj::iElementFactory::Param param;
param.period = gj::Period(st, ed);
param.driver = alloc->MakeUniq<gj::iElementDriver, LuaFunc>(L, n);
for (int i = 3; i < n; ++i) {
gj::iElementFactory::Param::CustomValue v;
if (lua_isnumber(L, i)) {
v = lua_tonumber(L, i);
} else if (lua_isstring(L, i)) {
v = lua_tostring(L, i);
} else {
return luaL_error(L, "invalid args");
}
param.custom.push_back(v);
}
auto e = factory->Create(std::move(param));
if (!e) return luaL_error(L, "factory returned nullptr");
store->Schedule(std::move(e));
return 0;
}
gj::Lua::Lua(iAllocator* alloc, const FactoryMap& factory, const std::string& path) {
gj::Lua::Lua(iAllocator* alloc, ElementStore* store, const FactoryMap& factory, const std::string& path) {
L = luaL_newstate();
if (L == nullptr) {
Abort("lua_newstate failure");
@ -102,8 +147,9 @@ gj::Lua::Lua(iAllocator* alloc, const FactoryMap& factory, const std::string& pa
lua_pushstring(L, f.first.c_str());
lua_pushlightuserdata(L, alloc);
lua_pushlightuserdata(L, store);
lua_pushlightuserdata(L, f.second);
lua_pushcclosure(L, CallFactory_, 2);
lua_pushcclosure(L, CallFactory_, 3);
lua_rawset(L, LUA_GLOBALSINDEX);
}

View File

@ -8,6 +8,7 @@
#include "common.h"
#include "iAllocator.h"
#include "iElementFactory.h"
#include "ElementStore.h"
namespace gj {
@ -24,7 +25,7 @@ class Lua {
Lua& operator=(Lua&&) = delete;
Lua& operator=(const Lua&) = delete;
Lua(iAllocator* alloc, const FactoryMap& factory, const std::string& path);
Lua(iAllocator* alloc, ElementStore* store, const FactoryMap& factory, const std::string& path);
~Lua() {
lua_close(L);

View File

@ -7,13 +7,17 @@ namespace gj {
struct Period {
public:
Period() = delete;
Period() : Period(0, 0) {
}
Period(uint64_t st, uint64_t ed) : start(st), end(ed) {
assert(st <= ed);
}
bool isHit(uint64_t now) const {
double Normalize(uint64_t now) const {
return (static_cast<int64_t>(now) - start)*1./duration();
}
bool IsHit(uint64_t now) const {
return start <= now && now < end;
}

View File

@ -1,10 +1,18 @@
#include "PlayScene.h"
#include "GlyphElementFactory.h"
gj::PlayScene::PlayScene(gj::PlayScene::Param&& p) :
gj::PlayScene::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");
clock_(p.clock), store_(&clock_, 256) {
GlyphElementFactory glyph(alloc_);
Lua::FactoryMap map;
map["Glyph"] = &glyph;
lua_ = alloc_->MakeUniq<Lua>(
alloc_, &store_, map, "res/score/" + p.score + ".lua");
logger_->Print(L"PlayScene init");
}

View File

@ -1,5 +1,7 @@
#pragma once
#include "ElementStore.h"
#include "Frame.h"
#include "iAllocator.h"
#include "iLogger.h"
@ -33,6 +35,10 @@ class PlayScene : public iScene {
PlayScene(Param&& p);
UniqPtr<iScene> Update(Frame& f) override {
if (store_.IsEmpty()) {
/* TODO create and return next scene */
}
store_.Update(f);
return nullptr;
}
@ -44,6 +50,7 @@ class PlayScene : public iScene {
OffsetClock clock_;
ElementStore store_;
UniqPtr<Lua> lua_;
};

View File

@ -2,6 +2,7 @@
#include <cstddef>
#include <cstdint>
#include <cstring>
#include "iAllocator.h"
@ -15,36 +16,18 @@ class Rasterbuffer {
Rasterbuffer(const Rasterbuffer&) = delete;
Rasterbuffer& operator=(const Rasterbuffer&) = delete;
Rasterbuffer(Rasterbuffer&& src) = default;
Rasterbuffer& operator=(Rasterbuffer&& src) = default;
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);
buf_(alloc->MakeUniqArray<T>(static_cast<uint64_t>(w_)*h_)) {
}
~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));
std::memset(buf_.get(), 0, static_cast<uint64_t>(w_) * h_ * sizeof(T));
}
uint32_t width() const {
return w_;
}
@ -52,17 +35,17 @@ class Rasterbuffer {
return h_;
}
T* ptr() {
return buf_;
return buf_.get();
}
const T* ptr() const {
return buf_;
return buf_.get();
}
private:
iAllocator* alloc_;
uint32_t w_, h_;
T* buf_;
UniqPtr<T> buf_;
};
using Colorbuffer = Rasterbuffer<float>;

View File

@ -72,7 +72,7 @@ void gj::Texture::Draw(Colorbuffer& fb) const {
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);
int32_t srcy = static_cast<int32_t>((yf+1)/2 * srch);
if (srcx >= srcw) srcx = srcw - 1;
if (srcy >= srch) srcy = srch - 1;
@ -81,7 +81,7 @@ void gj::Texture::Draw(Colorbuffer& fb) const {
if (dstx < 0 || w <= dstx) continue;
if (dsty < 0 || h <= dsty) continue;
dst[dstx + w*dsty] = src[srcx + srcw*srcy];
dst[dstx + w*dsty] = src[srcx + srcw*srcy] * alpha_;
}
}
}

55
src/TextureElement.h Normal file
View File

@ -0,0 +1,55 @@
#pragma once
#include "iElement.h"
#include "iElementDriver.h"
#include "Texture.h"
namespace gj {
class TextureElement : public iElement {
public:
TextureElement() = delete;
TextureElement(TextureElement&&) = delete;
TextureElement(const TextureElement&) = delete;
TextureElement& operator=(TextureElement&&) = delete;
TextureElement& operator=(const TextureElement&) = delete;
TextureElement(const Period& p, Texture&& tex, UniqPtr<iElementDriver>&& drv) :
iElement(p), tex_(std::move(tex)), drv_(std::move(drv)) {
param_["posX"] = 0.;
param_["posY"] = 0.;
param_["scaleX"] = 1.;
param_["scaleY"] = 1.;
param_["alpha"] = 1.;
}
void Update(Frame& frame, double t) override {
drv_->Update(param_, t);
const double posX = std::get<double>(param_["posX"]);
const double posY = std::get<double>(param_["posY"]);
const double scaleX = std::get<double>(param_["scaleX"]);
const double scaleY = std::get<double>(param_["scaleY"]);
const double alpha = std::get<double>(param_["alpha"]);
tex_.SetMatrix(mat3{
{ scaleX, 0, 0 },
{ 0, scaleY, 0 },
{ posX, posY, 1},
});
tex_.SetAlpha(static_cast<float>(alpha));
frame.Add(&tex_);
}
private:
Texture tex_;
UniqPtr<iElementDriver> drv_;
iElementDriver::Param param_;
};
}

View File

@ -1,8 +1,7 @@
#pragma once
#include <codecvt>
#include <cstdint>
#include <cstdlib>
#include <sstream>
#include <string>
#define NOMINMAX
@ -10,6 +9,7 @@
#undef NOMINMAX
#include "thirdparty/linalg.h"
#include "thirdparty/utf8.h"
namespace gj {
@ -19,9 +19,15 @@ 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();
std::wstring ret;
const void* c = str.c_str();
for (;;) {
int32_t code;
c = utf8codepoint(c, &code);
if (!code) return ret;
ret += static_cast<wchar_t>(code);
}
}

View File

@ -40,9 +40,15 @@ class DrawableBase : public iDrawable {
invmat_ = ::linalg::inverse(mat_);
}
void SetAlpha(float a) {
alpha_ = a;
}
protected:
mat3 mat_ = ::linalg::identity;
mat3 invmat_ = ::linalg::identity;
float alpha_ = 1;
};

View File

@ -9,16 +9,19 @@ namespace gj {
class iElement {
public:
iElement() = delete;
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;
iElement(const Period& p) : period(p) {
}
virtual void Update(Frame& frame, double t) = 0;
/* Interfaces had better not have a variable but this is for optimization. */
const Period period;

View File

@ -22,7 +22,7 @@ class iElementDriver {
iElementDriver() = default;
virtual ~iElementDriver() = default;
virtual void Update(Param&) = 0;
virtual void Update(Param&, double t) = 0;
};

View File

@ -1,5 +1,10 @@
#pragma once
#include <cstdint>
#include <string>
#include <variant>
#include <vector>
#include "iAllocator.h"
#include "iElement.h"
#include "iElementDriver.h"
@ -11,6 +16,14 @@ namespace gj {
class iElementFactory {
public:
struct Param {
using CustomValue = std::variant<int64_t, double, std::string>;
Period period;
std::vector<CustomValue> custom;
UniqPtr<iElementDriver> driver;
};
iElementFactory(iElementFactory&&) = default;
iElementFactory(const iElementFactory&) = default;
@ -20,7 +33,7 @@ class iElementFactory {
iElementFactory() = default;
virtual ~iElementFactory() = default;
virtual UniqPtr<iElement> Create(const Period& p, UniqPtr<iElementDriver>&& drv) = 0;
virtual UniqPtr<iElement> Create(Param&& p) = 0;
};

1662
thirdparty/utf8.h vendored Normal file

File diff suppressed because it is too large Load Diff