looking like a real one
This commit is contained in:
parent
646d24b47c
commit
b0b73bb47e
16 changed files with 8233 additions and 209 deletions
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"files.associations": {
|
||||
"xstring": "cpp"
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
#include <bgfx/bgfx.h>
|
||||
#include <bgfx/platform.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
namespace {
|
||||
|
@ -23,10 +24,17 @@ namespace {
|
|||
std::vector<NVL::Environment::Variable> m_text{};
|
||||
NVL::String speaker;
|
||||
u32 scene_pos = 0;
|
||||
|
||||
char* date;
|
||||
}
|
||||
|
||||
namespace ADVect {
|
||||
void Init(std::string name, const std::vector<NVL::Parse::Scene>& sc) {
|
||||
std::time_t now_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
||||
date = std::ctime(&now_time);
|
||||
date[10] = '\0';
|
||||
date = &date[4];
|
||||
|
||||
m_name = name;
|
||||
scenes = sc; // sure make a copy whatever
|
||||
|
||||
|
@ -37,7 +45,7 @@ namespace ADVect {
|
|||
window = SDL_CreateWindow(m_name.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, m_width, m_height, SDL_WINDOW_SHOWN);
|
||||
|
||||
if (window == NULL) {
|
||||
std::cerr << "Failed to create window: " << SDL_GetError() << std::endl;
|
||||
std::cerr << "ADV: Failed to create window: " << SDL_GetError() << std::endl;
|
||||
return;
|
||||
}
|
||||
bgfx::Init bgfxInit;
|
||||
|
@ -50,7 +58,7 @@ namespace ADVect {
|
|||
SDL_SysWMinfo wmi;
|
||||
SDL_VERSION(&wmi.version);
|
||||
if (!SDL_GetWindowWMInfo(window, &wmi)) {
|
||||
std::cerr << "SDL_SysWMinfo could not be retrieved: " << SDL_GetError() << std::endl;
|
||||
std::cerr << "ADV: SDL_SysWMinfo could not be retrieved: " << SDL_GetError() << std::endl;
|
||||
}
|
||||
#endif
|
||||
#if BX_PLATFORM_WINDOWS
|
||||
|
@ -65,22 +73,22 @@ namespace ADVect {
|
|||
#endif
|
||||
|
||||
if (!bgfx::init(bgfxInit)) {
|
||||
std::cerr << "Failed bgfx initialization" << std::endl;
|
||||
std::cerr << "ADV: Failed bgfx initialization" << std::endl;
|
||||
return;
|
||||
};
|
||||
bgfx::setDebug(BGFX_DEBUG_TEXT | BGFX_DEBUG_STATS);
|
||||
bgfx::setViewClear(0, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x443355ff, 1.0f, 0);
|
||||
bgfx::setDebug(BGFX_DEBUG_TEXT);
|
||||
bgfx::setViewClear(0, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x443322ff, 1.0f, 0);
|
||||
bgfx::setViewRect(0, 0, 0, m_width, m_height);
|
||||
bgfx::setViewMode(0, bgfx::ViewMode::Sequential);
|
||||
|
||||
ADVect::Graphics::Init(m_width, m_height);
|
||||
|
||||
|
||||
NVL::Environment::ENVIRONMENT.enter(L"Say", NVL::Environment::Variable([&](std::vector<NVL::Environment::Variable> args) {
|
||||
NVL::Environment::ENVIRONMENT.enter(u"Say", NVL::Environment::Variable([&](std::vector<NVL::Environment::Variable> args) {
|
||||
m_text = args;
|
||||
return NVL::Environment::Type::Nil;
|
||||
}, 2));
|
||||
|
||||
NVL::Environment::ENVIRONMENT.enter(L"SwitchSpeaker", NVL::Environment::Variable([&](std::vector<NVL::Environment::Variable> args) {
|
||||
NVL::Environment::ENVIRONMENT.enter(u"SwitchSpeaker", NVL::Environment::Variable([&](std::vector<NVL::Environment::Variable> args) {
|
||||
speaker = std::get<NVL::String>(NVL::Environment::Variable(args[0]).value);
|
||||
return NVL::Environment::Type::Nil;
|
||||
}, 1));
|
||||
|
@ -97,7 +105,7 @@ namespace ADVect {
|
|||
void Advance() {
|
||||
size_t curr_size = scenes[current_scene].get().size();
|
||||
while (scene_pos < curr_size) {
|
||||
if (std::get<NVL::String>(scenes[current_scene].get()[scene_pos][0].value) == L"Say") {
|
||||
if (std::get<NVL::String>(scenes[current_scene].get()[scene_pos][0].value) == u"Say") {
|
||||
NVL::Environment::Apply(scenes[current_scene].get()[scene_pos]);
|
||||
scene_pos++;
|
||||
return;
|
||||
|
@ -113,7 +121,7 @@ namespace ADVect {
|
|||
case 1:
|
||||
return;
|
||||
default:
|
||||
if (std::get<NVL::String>(scenes[current_scene].get()[scene_pos + 1][0].value) == L"Say") {
|
||||
if (std::get<NVL::String>(scenes[current_scene].get()[scene_pos + 1][0].value) == u"Say") {
|
||||
NVL::Environment::Apply(scenes[current_scene].get()[scene_pos]);
|
||||
scene_pos++;
|
||||
return;
|
||||
|
@ -137,10 +145,15 @@ namespace ADVect {
|
|||
void Render() {
|
||||
bgfx::touch(0);
|
||||
|
||||
ADVect::Graphics::RenderString(speaker, 50, 630, 0xFFFFFFFF);
|
||||
ADVect::Graphics::RenderStringMarkup(m_text, 50, 580, 0xFFFFFFFF);
|
||||
ADVect::Graphics::DrawRandomShit();
|
||||
ADVect::Graphics::RenderString(speaker, 100, 150, 0xFFFFFFFF, ADVect::Graphics::TEXT_BOLD);
|
||||
ADVect::Graphics::RenderStringMarkup(m_text, 300, 90, 0xFFFFFFFF);
|
||||
|
||||
|
||||
bgfx::dbgTextClear();
|
||||
bgfx::dbgTextPrintf(0, 44, 0xF0, "NouVeL x ADVect :: %s 2022", date);
|
||||
bgfx::dbgTextPrintf(0, 43, 0xF0, "Current Position: %u", scene_pos);
|
||||
bgfx::dbgTextPrintf(0, 42, 0xF0, "Current Scene: %s", NVL::to_std_string(scenes[current_scene].name).c_str());
|
||||
|
||||
bgfx::frame();
|
||||
|
||||
|
@ -173,7 +186,7 @@ int main(int argc, char* argv[]) {
|
|||
const std::string PJ_DIR = "..\\..\\..\\..\\NVL\\";
|
||||
std::vector<NVL::Parse::Scene> SCENES = NVL::Parse::ParseFile(PJ_DIR + "dialogue.nvl");
|
||||
|
||||
ADVect::Init("Test", SCENES);
|
||||
ADVect::Init("ADV", SCENES);
|
||||
ADVect::Run();
|
||||
|
||||
ADVect::Shutdown();
|
||||
|
|
|
@ -5,17 +5,15 @@ namespace ADVect {
|
|||
auto f = [](NVL::Environment::Variable x) {
|
||||
return NVL::Environment::Variable(NVL::Environment::Type::Nil);
|
||||
};
|
||||
NVL::Environment::ENVIRONMENT.enter(L"Hello", { f, 1 });
|
||||
NVL::Environment::ENVIRONMENT.enter(L"Command", { f, 1 });
|
||||
NVL::Environment::ENVIRONMENT.enter(L"Do", { f, 2 });
|
||||
NVL::Environment::ENVIRONMENT.enter(L"Set", { f, 2 });
|
||||
NVL::Environment::ENVIRONMENT.enter(L"This", { f, 1 });
|
||||
NVL::Environment::ENVIRONMENT.enter(L"ClearDialog", { f, 0 });
|
||||
NVL::Environment::ENVIRONMENT.enter(L"Choice", { f, 2 });
|
||||
NVL::Environment::ENVIRONMENT.enter(L"=?", { f, 2 });
|
||||
NVL::Environment::ENVIRONMENT.enter(L"+", { f, 2 });
|
||||
NVL::Environment::ENVIRONMENT.enter(L"switch", { f, 2 });
|
||||
NVL::Environment::ENVIRONMENT.enter(L"SwitchSpeaker", { f, 1 });
|
||||
NVL::Environment::ENVIRONMENT.enter(L"Say", { f, 1 });
|
||||
NVL::Environment::ENVIRONMENT.enter(u"Hello", { f, 1 });
|
||||
NVL::Environment::ENVIRONMENT.enter(u"Command", { f, 1 });
|
||||
NVL::Environment::ENVIRONMENT.enter(u"Do", { f, 2 });
|
||||
NVL::Environment::ENVIRONMENT.enter(u"Set", { f, 2 });
|
||||
NVL::Environment::ENVIRONMENT.enter(u"This", { f, 1 });
|
||||
NVL::Environment::ENVIRONMENT.enter(u"ClearDialog", { f, 0 });
|
||||
NVL::Environment::ENVIRONMENT.enter(u"Choice", { f, 2 });
|
||||
NVL::Environment::ENVIRONMENT.enter(u"=?", { f, 2 });
|
||||
NVL::Environment::ENVIRONMENT.enter(u"+", { f, 2 });
|
||||
NVL::Environment::ENVIRONMENT.enter(u"switch", { f, 2 });
|
||||
}
|
||||
}
|
|
@ -2,26 +2,26 @@
|
|||
#include FT_FREETYPE_H
|
||||
#include "Graphics.h"
|
||||
|
||||
#include <bgfx/bgfx.h>
|
||||
#include <bx/math.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
|
||||
namespace {
|
||||
FT_Library library;
|
||||
FT_Error error;
|
||||
FT_Face face;
|
||||
FT_Face face_regular, face_bold;
|
||||
|
||||
struct PosUVVertex
|
||||
static struct PosUVVertex
|
||||
{
|
||||
f32 x;
|
||||
f32 y;
|
||||
f32 z;
|
||||
f32 u;
|
||||
f32 v;
|
||||
};
|
||||
|
||||
static PosUVVertex quad_vert[] =
|
||||
} quad_vert[] =
|
||||
{
|
||||
{ 0.f, 0.f, 0.0f, 0.0f, 0.0f },
|
||||
{ 0.f, 1.f, 0.0f, 0.0f, 1.0f },
|
||||
|
@ -67,38 +67,45 @@ namespace {
|
|||
return bgfx::createShader(mem);
|
||||
}
|
||||
|
||||
bgfx::ShaderHandle vsh;
|
||||
bgfx::ShaderHandle fsh;
|
||||
bgfx::ProgramHandle program;
|
||||
bgfx::ShaderHandle a_vsh, img_vsh;
|
||||
bgfx::ShaderHandle a_fsh, img_fsh;
|
||||
bgfx::ProgramHandle a_program, img_program;
|
||||
bgfx::IndexBufferHandle ibh;
|
||||
bgfx::VertexBufferHandle vbh;
|
||||
bgfx::UniformHandle s_texColor;
|
||||
bgfx::VertexLayout pcvDecl;
|
||||
|
||||
std::unordered_map<wchar_t, bgfx::TextureHandle> cache{};
|
||||
std::unordered_map<NVL::Char, bgfx::TextureHandle> cache_regular {};
|
||||
std::unordered_map<NVL::Char, bgfx::TextureHandle> cache_bold {};
|
||||
|
||||
int w, h, channels;
|
||||
u8* img;
|
||||
bgfx::TextureHandle tx;
|
||||
}
|
||||
|
||||
namespace ADVect::Graphics {
|
||||
i16 Init(u16 width, u16 height) {
|
||||
error = FT_Init_FreeType(&library);
|
||||
if (error) {
|
||||
std::cerr << "FreeType init error: " << error << std::endl;
|
||||
std::cerr << "ADV: FreeType init error: " << error << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
error = FT_New_Face(library, "SourceHanSerif-Heavy.ttc", 0, &face);
|
||||
error = FT_New_Face(library, "SourceHanSans-Regular.ttc", 0, &face_regular);
|
||||
error = FT_New_Face(library, "SourceHanSans-Bold.ttc", 0, &face_bold);
|
||||
if (error == FT_Err_Unknown_File_Format) {
|
||||
std::cerr << "FreeType Unknown_File_Format: " << error << std::endl;
|
||||
std::cerr << "ADV: FreeType Unknown_File_Format: " << error << std::endl;
|
||||
return -1;
|
||||
}
|
||||
else if (error) {
|
||||
std::cerr << "FreeType font loading error: " << error << std::endl;
|
||||
std::cerr << "ADV: FreeType font loading error: " << error << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
error = FT_Set_Char_Size(face, 0, 20 * 64, 72, 72);
|
||||
error = FT_Set_Char_Size(face_regular, 0, 20 * 64, 72, 72);
|
||||
error = FT_Set_Char_Size(face_bold, 0, 20 * 64, 72, 72);
|
||||
if (error == FT_Err_Unknown_File_Format) {
|
||||
std::cerr << "FreeType Set_Char_Size error: " << error << std::endl;
|
||||
std::cerr << "ADV: FreeType Set_Char_Size error: " << error << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -115,47 +122,122 @@ namespace ADVect::Graphics {
|
|||
ibh = bgfx::createIndexBuffer(bgfx::makeRef(quad_indices, sizeof(quad_indices)));
|
||||
vbh = bgfx::createVertexBuffer(bgfx::makeRef(quad_vert, sizeof(quad_vert)), pcvDecl);
|
||||
|
||||
vsh = loadShader("test.vert.bin");
|
||||
fsh = loadShader("test.frag.bin");
|
||||
program = bgfx::createProgram(vsh, fsh, true);
|
||||
img_vsh = loadShader("test.vert.bin");
|
||||
img_fsh = loadShader("test.frag.bin");
|
||||
img_program = bgfx::createProgram(img_vsh, img_fsh, true);
|
||||
|
||||
a_vsh = loadShader("swizzle_aaaa.vert.bin");
|
||||
a_fsh = loadShader("swizzle_aaaa.frag.bin");
|
||||
a_program = bgfx::createProgram(a_vsh, a_fsh, true);
|
||||
|
||||
s_texColor = bgfx::createUniform("s_texColor", bgfx::UniformType::Sampler);
|
||||
|
||||
|
||||
img = stbi_load("image.png", &w, &h, &channels, 0);
|
||||
if (img == NULL) {
|
||||
std::cerr << "ADV: STB IMAGE loading failed" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
const bgfx::Memory* buf = bgfx::makeRef(img, w * h * channels * sizeof(u8));
|
||||
tx = bgfx::createTexture2D(
|
||||
w,
|
||||
h,
|
||||
false,
|
||||
1,
|
||||
bgfx::TextureFormat::RGBA8,
|
||||
BGFX_TEXTURE_NONE | BGFX_SAMPLER_UVW_CLAMP,
|
||||
buf
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
FT_Done_Face(face);
|
||||
FT_Done_Face(face_regular);
|
||||
FT_Done_Face(face_bold);
|
||||
FT_Done_FreeType(library);
|
||||
|
||||
|
||||
bgfx::destroy(vsh);
|
||||
bgfx::destroy(fsh);
|
||||
bgfx::destroy(program);
|
||||
bgfx::destroy(a_vsh);
|
||||
bgfx::destroy(a_fsh);
|
||||
bgfx::destroy(img_vsh);
|
||||
bgfx::destroy(img_fsh);
|
||||
bgfx::destroy(a_program);
|
||||
bgfx::destroy(img_program);
|
||||
bgfx::destroy(ibh);
|
||||
bgfx::destroy(vbh);
|
||||
bgfx::destroy(s_texColor);
|
||||
for (auto it = cache.begin(); it != cache.end(); it++) {
|
||||
for (auto it = cache_regular .begin(); it != cache_regular.end(); it++) {
|
||||
bgfx::destroy(it->second);
|
||||
}
|
||||
for (auto it = cache_bold.begin(); it != cache_bold.end(); it++) {
|
||||
bgfx::destroy(it->second);
|
||||
}
|
||||
bgfx::destroy(tx);
|
||||
}
|
||||
|
||||
void RenderString(const NVL::String& s, u32& pos_x, u32& pos_y, u32 col) {
|
||||
FT_GlyphSlot slot = face->glyph;
|
||||
// Draws quad with texture on Z = 0
|
||||
void DrawTexture(const bgfx::TextureHandle& tex, i32 pos_x, i32 pos_y, u32 w, u32 h) {
|
||||
float mtx1[16], mtx2[16], mtx3[16];
|
||||
bx::mtxTranslate(mtx1, pos_x, pos_y, 0.0f);
|
||||
bx::mtxScale(mtx2, w, h, 1.0f);
|
||||
bx::mtxMul(mtx3, mtx2, mtx1);
|
||||
|
||||
bgfx::setTransform(mtx3);
|
||||
|
||||
bgfx::setVertexBuffer(0, vbh);
|
||||
bgfx::setIndexBuffer(ibh);
|
||||
|
||||
bgfx::setTexture(0, s_texColor, tex);
|
||||
|
||||
bgfx::setState(BGFX_STATE_DEFAULT | BGFX_STATE_BLEND_NORMAL);
|
||||
bgfx::submit(0, img_program);
|
||||
}
|
||||
|
||||
void DrawTextureStencilAlpha(const bgfx::TextureHandle& tex, i32 pos_x, i32 pos_y, u32 w, u32 h) {
|
||||
float mtx1[16], mtx2[16], mtx3[16];
|
||||
bx::mtxTranslate(mtx1, pos_x, pos_y, 0.0f);
|
||||
bx::mtxScale(mtx2, w, h, 1.0f);
|
||||
bx::mtxMul(mtx3, mtx2, mtx1);
|
||||
|
||||
bgfx::setTransform(mtx3);
|
||||
|
||||
bgfx::setVertexBuffer(0, vbh);
|
||||
bgfx::setIndexBuffer(ibh);
|
||||
|
||||
bgfx::setTexture(0, s_texColor, tex);
|
||||
|
||||
bgfx::setState((BGFX_STATE_DEFAULT | BGFX_STATE_BLEND_ALPHA) & ~(BGFX_STATE_WRITE_Z | BGFX_STATE_DEPTH_TEST_LESS));
|
||||
bgfx::submit(0, a_program);
|
||||
}
|
||||
|
||||
void RenderString(const NVL::String& s, u32& pos_x, u32& pos_y, u32 col, u32 style_flags) {
|
||||
FT_Face active = face_regular;
|
||||
std::unordered_map<NVL::Char, bgfx::TextureHandle>* cache = &cache_regular;
|
||||
switch (style_flags) {
|
||||
case TEXT_NONE: break;
|
||||
case TEXT_BOLD: active = face_bold; cache = &cache_bold; break;
|
||||
case TEXT_ITALIC: break;
|
||||
case TEXT_ITALIC | TEXT_BOLD: break;
|
||||
}
|
||||
|
||||
|
||||
FT_GlyphSlot slot = active->glyph;
|
||||
|
||||
for (const auto& c : s) {
|
||||
error = FT_Load_Char(face, c, FT_LOAD_RENDER);
|
||||
error = FT_Load_Char(active, c, FT_LOAD_RENDER);
|
||||
if (error) continue;
|
||||
|
||||
if (cache.find(c) == cache.end() && slot->bitmap.width != 0) {
|
||||
if (cache->find(c) == cache->end() && slot->bitmap.width != 0) {
|
||||
const bgfx::Memory* buf = bgfx::copy(slot->bitmap.buffer, slot->bitmap.pitch * slot->bitmap.rows);
|
||||
|
||||
cache.emplace(c, bgfx::createTexture2D(
|
||||
cache->emplace(c, bgfx::createTexture2D(
|
||||
slot->bitmap.width,
|
||||
slot->bitmap.rows,
|
||||
false,
|
||||
1,
|
||||
bgfx::TextureFormat::A8,
|
||||
BGFX_TEXTURE_NONE | BGFX_SAMPLER_POINT | BGFX_SAMPLER_UVW_CLAMP,
|
||||
BGFX_TEXTURE_NONE | BGFX_SAMPLER_UVW_CLAMP,
|
||||
buf
|
||||
));
|
||||
}
|
||||
|
@ -164,111 +246,70 @@ namespace ADVect::Graphics {
|
|||
float ypos = pos_y - (slot->bitmap.rows - slot->bitmap_top);
|
||||
|
||||
if (slot->bitmap.width != 0) {
|
||||
float mtx1[16], mtx2[16], mtx3[16];
|
||||
bx::mtxTranslate(mtx1, (f32)pos_x, (f32)ypos, 0.0f);
|
||||
bx::mtxScale(mtx2, slot->bitmap.width, slot->bitmap.rows, 1.0f);
|
||||
bx::mtxMul(mtx3, mtx2, mtx1);
|
||||
|
||||
bgfx::setTransform(mtx3);
|
||||
|
||||
bgfx::setVertexBuffer(0, vbh);
|
||||
bgfx::setIndexBuffer(ibh);
|
||||
|
||||
|
||||
bgfx::setTexture(0, s_texColor, cache[c]);
|
||||
|
||||
bgfx::setState(BGFX_STATE_DEFAULT | BGFX_STATE_BLEND_ADD);
|
||||
bgfx::submit(0, program);
|
||||
DrawTextureStencilAlpha((*cache)[c], pos_x, ypos, slot->bitmap.width, slot->bitmap.rows);
|
||||
}
|
||||
|
||||
pos_x += slot->advance.x >> 6;
|
||||
pos_y += slot->advance.y >> 6;
|
||||
}
|
||||
}
|
||||
void RenderString(const NVL::String& s, u32&& pos_x, u32&& pos_y, u32 col) {
|
||||
|
||||
void RenderString(const NVL::String& s, u32&& pos_x, u32&& pos_y, u32 col, u32 style_flags) {
|
||||
u32 x = pos_x, y = pos_y;
|
||||
RenderString(s, x, y, col);
|
||||
}
|
||||
|
||||
// Check ParseMarkup for more information
|
||||
struct Markup {
|
||||
u32 begin, end;
|
||||
std::vector<std::pair<NVL::String, std::vector<NVL::String>>> efs;
|
||||
} UnpackMarkupVariable(const NVL::Environment::Variable& m) {
|
||||
const auto& range = std::get<std::vector<NVL::Environment::Variable>>(std::get<std::vector<NVL::Environment::Variable>>(m.value)[0].value);
|
||||
const u32 begin = std::get<NVL::Number>(range[0].value), end = std::get<NVL::Number>(range[1].value);
|
||||
|
||||
std::vector<std::pair<NVL::String, std::vector<NVL::String>>> efs;
|
||||
for (const NVL::Environment::Variable& combination : std::get<std::vector<NVL::Environment::Variable>>(std::get<std::vector<NVL::Environment::Variable>>(m.value)[1].value)) {
|
||||
if (combination.type == NVL::Environment::Type::String) {
|
||||
const NVL::String ef = std::get<NVL::String>(combination.value);
|
||||
efs.push_back({ ef, {} });
|
||||
}
|
||||
else if (combination.type == NVL::Environment::Type::Array) {
|
||||
const std::vector<NVL::Environment::Variable>& ef_t = std::get<std::vector<NVL::Environment::Variable>>(combination.value);
|
||||
const NVL::String& ef = std::get<NVL::String>(ef_t[0].value);
|
||||
const std::vector<NVL::Environment::Variable>& args = std::get<std::vector<NVL::Environment::Variable>>(ef_t[1].value);
|
||||
std::vector<NVL::String> ss{};
|
||||
for (const auto& s : args) { ss.push_back(std::get<NVL::String>(s.value)); }
|
||||
efs.push_back({ ef, ss });
|
||||
}
|
||||
}
|
||||
return { begin, end, efs };
|
||||
RenderString(s, x, y, col, style_flags);
|
||||
}
|
||||
|
||||
void RenderStringMarkup(const std::vector<NVL::Environment::Variable>& s, u32 pos_x, u32 pos_y, u32 col) {
|
||||
const NVL::String& str = std::get<NVL::String>(s[0].value);
|
||||
|
||||
std::vector<Markup> ms{};
|
||||
std::vector<NVL::Environment::Markup> ms{};
|
||||
for (const NVL::Environment::Variable& markup : std::get<std::vector<NVL::Environment::Variable>>(s[1].value)) {
|
||||
ms.push_back(UnpackMarkupVariable(markup));
|
||||
ms.push_back(NVL::Environment::UnpackMarkupVariable(markup));
|
||||
}
|
||||
|
||||
if (ms.empty()) {
|
||||
RenderString(str, pos_x, pos_y, col);
|
||||
RenderString(str, pos_x, pos_y, col, TEXT_NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ms[0].begin != 0)
|
||||
RenderString(str.substr(0, ms[0].begin), pos_x, pos_y, col);
|
||||
RenderString(str.substr(0, ms[0].begin), pos_x, pos_y, col, TEXT_NONE);
|
||||
|
||||
// We assume markups are already sorted
|
||||
for (size_t i = 0; i < ms.size(); i++) {
|
||||
bool bold = false,
|
||||
italic = false;
|
||||
u32 style_flags = TEXT_NONE;
|
||||
const NVL::String* ruby = nullptr;
|
||||
|
||||
for (const auto& e : ms[i].efs) {
|
||||
if (e.first == L"b") { bold = true; }
|
||||
else if (e.first == L"i") { italic = true; }
|
||||
else if (e.first == L"rb") { ruby = &e.second[0]; }
|
||||
if (e.first == u"b") { style_flags |= TEXT_BOLD; }
|
||||
else if (e.first == u"i") { style_flags |= TEXT_ITALIC; }
|
||||
else if (e.first == u"u") { style_flags |= TEXT_UNDERLINE; }
|
||||
else if (e.first == u"s") { style_flags |= TEXT_STRIKETHROUGH; }
|
||||
else if (e.first == u"o") { style_flags |= TEXT_OVERLINE; }
|
||||
else if (e.first == u"rb") { ruby = &e.second[0]; }
|
||||
else {
|
||||
std::cerr << "ADV: Unrecognized text markup: " << NVL::to_std_string(e.first) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (ruby != nullptr) {
|
||||
u32 temp_x = pos_x, temp_y = pos_y + 20;
|
||||
RenderString(*ruby, temp_x, temp_y, col);
|
||||
}
|
||||
if (bold && italic) {
|
||||
|
||||
}
|
||||
else if (bold) {
|
||||
u32 temp_x = pos_x, temp_y = pos_y;
|
||||
RenderString(str.substr(ms[i].begin, ms[i].end - ms[i].begin), pos_x, pos_y, col);
|
||||
RenderString(str.substr(ms[i].begin, ms[i].end - ms[i].begin), temp_x + 2, temp_y - 1, col);
|
||||
}
|
||||
else if (italic) {
|
||||
|
||||
}
|
||||
else {
|
||||
RenderString(str.substr(ms[i].begin, ms[i].end - ms[i].begin), pos_x, pos_y, col);
|
||||
RenderString(*ruby, temp_x, temp_y, col, TEXT_NONE);
|
||||
}
|
||||
|
||||
if (i != ms.size() - 1 && ms[i + 1].begin - 1 != ms[i].end) {
|
||||
RenderString(str.substr(ms[i].end, ms[i + 1].begin - ms[i].end), pos_x, pos_y, col);
|
||||
}
|
||||
RenderString(str.substr(ms[i].begin, ms[i].end - ms[i].begin), pos_x, pos_y, col, style_flags);
|
||||
|
||||
if (i != ms.size() - 1 && ms[i + 1].begin - 1 != ms[i].end)
|
||||
RenderString(str.substr(ms[i].end, ms[i + 1].begin - ms[i].end), pos_x, pos_y, col, TEXT_NONE);
|
||||
|
||||
}
|
||||
|
||||
if (ms.back().end != str.length())
|
||||
RenderString(str.substr(ms.back().end), pos_x, pos_y, col);
|
||||
RenderString(str.substr(ms.back().end), pos_x, pos_y, col, TEXT_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawRandomShit() {
|
||||
|
||||
DrawTexture(tx, 0, -40, w, h);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,29 @@
|
|||
#include <SDL2/SDL.h>
|
||||
|
||||
#include <bgfx/bgfx.h>
|
||||
|
||||
#include <Common.h>
|
||||
#include <Environment.h>
|
||||
|
||||
namespace ADVect::Graphics {
|
||||
i16 Init(u16 width, u16 height);
|
||||
void Shutdown();
|
||||
void RenderString(const NVL::String& s, u32& pos_x, u32& pos_y, u32 col);
|
||||
void RenderString(const NVL::String& s, u32&& pos_x, u32&& pos_y, u32 col);
|
||||
|
||||
enum MarkupStyle {
|
||||
TEXT_NONE = 0,
|
||||
TEXT_BOLD = 1 << 0,
|
||||
TEXT_ITALIC = 1 << 1,
|
||||
TEXT_UNDERLINE = 1 << 2,
|
||||
TEXT_STRIKETHROUGH = 1 << 3,
|
||||
TEXT_OVERLINE = 1 << 4
|
||||
};
|
||||
|
||||
void DrawTexture(const bgfx::TextureHandle& tex, i32 pos_x, i32 pos_y, u32 w, u32 h);
|
||||
void DrawTextureStencilAlpha(const bgfx::TextureHandle& tex, i32 pos_x, i32 pos_y, u32 w, u32 h);
|
||||
|
||||
void RenderString(const NVL::String& s, u32& pos_x, u32& pos_y, u32 col, u32 style_flags);
|
||||
void RenderString(const NVL::String& s, u32&& pos_x, u32&& pos_y, u32 col, u32 style_flags);
|
||||
void RenderStringMarkup(const std::vector<NVL::Environment::Variable>& s, u32 pos_x, u32 pos_y, u32 col);
|
||||
|
||||
void DrawRandomShit();
|
||||
}
|
||||
|
|
7897
ADVect/include/stb_image.h
Normal file
7897
ADVect/include/stb_image.h
Normal file
File diff suppressed because it is too large
Load diff
12
ADVect/shaders/swizzle_aaaa/swizzle_aaaa.frag
Normal file
12
ADVect/shaders/swizzle_aaaa/swizzle_aaaa.frag
Normal file
|
@ -0,0 +1,12 @@
|
|||
$input v_texcoord0
|
||||
|
||||
#include <bgfx_shader.sh>
|
||||
#include <shaderlib.sh>
|
||||
|
||||
SAMPLER2D(s_texColor, 0);
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 uv = { v_texcoord0.x, 1.0f - v_texcoord0.y };
|
||||
gl_FragColor = vec4(1.0, 1.0, 1.0, texture2D(s_texColor, uv).a);
|
||||
}
|
11
ADVect/shaders/swizzle_aaaa/swizzle_aaaa.vert
Normal file
11
ADVect/shaders/swizzle_aaaa/swizzle_aaaa.vert
Normal file
|
@ -0,0 +1,11 @@
|
|||
$input a_position, a_texcoord0
|
||||
$output v_texcoord0
|
||||
|
||||
#include <bgfx_shader.sh>
|
||||
#include <shaderlib.sh>
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = mul(u_modelViewProj, vec4(a_position, 1.0));
|
||||
v_texcoord0 = a_texcoord0;
|
||||
}
|
4
ADVect/shaders/swizzle_aaaa/varying.def.sc
Normal file
4
ADVect/shaders/swizzle_aaaa/varying.def.sc
Normal file
|
@ -0,0 +1,4 @@
|
|||
vec2 v_texcoord0 : TEXCOORD0 = vec2(0.0, 0.0);
|
||||
|
||||
vec3 a_position : POSITION;
|
||||
vec2 a_texcoord0 : TEXCOORD0;
|
|
@ -8,6 +8,5 @@ SAMPLER2D(s_texColor, 0);
|
|||
void main()
|
||||
{
|
||||
vec2 uv = { v_texcoord0.x, 1.0f - v_texcoord0.y };
|
||||
gl_FragColor = texture2D(s_texColor, uv).aaaa;
|
||||
//gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
|
||||
gl_FragColor = texture2D(s_texColor, uv);
|
||||
}
|
||||
|
|
12
NVL/Common.h
12
NVL/Common.h
|
@ -14,7 +14,17 @@ typedef uint64_t u64;
|
|||
|
||||
#include <string>
|
||||
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
|
||||
|
||||
namespace NVL {
|
||||
using String = std::wstring;
|
||||
using Number = f32;
|
||||
using Char = char16_t;
|
||||
using String = std::u16string;
|
||||
|
||||
static std::wstring_convert<std::codecvt_utf8_utf16<Char>, Char> str_converter;
|
||||
|
||||
inline static std::string to_std_string(const String& s) noexcept { return str_converter.to_bytes(s); }
|
||||
inline static String to_NVL_string(const std::string& s) noexcept { return str_converter.from_bytes(s); }
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace NVL::Environment {
|
|||
case Type::Number:
|
||||
return std::get<Number>(other.value) == std::get<Number>(value);
|
||||
case Type::String:
|
||||
return std::get<String>(other.value) == std::get<NVL::String>(value);
|
||||
return std::get<String>(other.value) == std::get<String>(value);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -81,4 +81,27 @@ namespace NVL::Environment {
|
|||
for (const auto& c : s.get())
|
||||
Apply(c);
|
||||
}
|
||||
|
||||
// Check ParseMarkup for more information
|
||||
Markup UnpackMarkupVariable(const Variable& m) {
|
||||
const auto& range = std::get<std::vector<Variable>>(std::get<std::vector<Variable>>(m.value)[0].value);
|
||||
const u32 begin = std::get<Number>(range[0].value), end = std::get<Number>(range[1].value);
|
||||
|
||||
std::vector<std::pair<String, std::vector<String>>> efs;
|
||||
for (const Variable& combination : std::get<std::vector<Variable>>(std::get<std::vector<Variable>>(m.value)[1].value)) {
|
||||
if (combination.type == Type::String) {
|
||||
const String ef = std::get<String>(combination.value);
|
||||
efs.push_back({ ef, {} });
|
||||
}
|
||||
else if (combination.type == Type::Array) {
|
||||
const std::vector<Variable>& ef_t = std::get<std::vector<Variable>>(combination.value);
|
||||
const String& ef = std::get<String>(ef_t[0].value);
|
||||
const std::vector<Variable>& args = std::get<std::vector<Variable>>(ef_t[1].value);
|
||||
std::vector<String> ss{};
|
||||
for (const auto& s : args) { ss.push_back(std::get<String>(s.value)); }
|
||||
efs.push_back({ ef, ss });
|
||||
}
|
||||
}
|
||||
return { begin, end, efs };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,4 +37,9 @@ namespace NVL::Environment {
|
|||
Variable Eval(const Parse::Object& obj);
|
||||
Variable Apply(const Parse::Command& c);
|
||||
void EvalScene(const Parse::Scene & s);
|
||||
|
||||
struct Markup {
|
||||
u32 begin, end;
|
||||
std::vector<std::pair<String, std::vector<String>>> efs;
|
||||
} UnpackMarkupVariable(const Variable& m);
|
||||
}
|
||||
|
|
136
NVL/Parser.cpp
136
NVL/Parser.cpp
|
@ -11,8 +11,6 @@
|
|||
#include <stdexcept>
|
||||
|
||||
#include "Environment.h"
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
|
||||
namespace {
|
||||
using namespace NVL;
|
||||
|
@ -31,11 +29,11 @@ namespace {
|
|||
struct Match {
|
||||
String accept;
|
||||
|
||||
operator wchar_t() const {
|
||||
operator Char() const {
|
||||
if (accept.length() == 1)
|
||||
return accept[0];
|
||||
else {
|
||||
std::wcerr << "Cannot demote Match " << accept << " to char" << std::endl;
|
||||
std::cerr << "NVL: Cannot demote Match " << to_std_string(accept) << " to char" << std::endl;
|
||||
return '\0';
|
||||
}
|
||||
}
|
||||
|
@ -44,74 +42,74 @@ namespace {
|
|||
}
|
||||
};
|
||||
|
||||
const ParseGroup NUMERIC = { L"1234567890" };
|
||||
const Match DECIMAL_DOT = { L"." };
|
||||
const Match NEGATIVE = { L"-" };
|
||||
const ParseGroup ALPHA = { L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" };
|
||||
const Match ARRAY_OPEN = { L"[" };
|
||||
const Match ARRAY_CLOSE = { L"]" };
|
||||
const Match ARRAY_DELIM = { L"," };
|
||||
const Match GROUP_OPEN = { L"(" };
|
||||
const Match GROUP_CLOSE = { L")" };
|
||||
const Match QUOTE = { L"\"" };
|
||||
const Match COMMENT_BEGIN = { L"//" };
|
||||
const Match DIALOGUE_OPEN = { L"<<-" };
|
||||
const Match DIALOGUE_CLOSE = { L"->>" };
|
||||
const Match BEGIN = { L"BEGIN" };
|
||||
const Match END = { L"END" };
|
||||
const ParseGroup SYMBOL = { ALPHA.accept + NUMERIC.accept + L"_"};
|
||||
const ParseGroup NUMERIC = { u"1234567890" };
|
||||
const Match DECIMAL_DOT = { u"." };
|
||||
const Match NEGATIVE = { u"-" };
|
||||
const ParseGroup ALPHA = { u"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" };
|
||||
const Match ARRAY_OPEN = { u"[" };
|
||||
const Match ARRAY_CLOSE = { u"]" };
|
||||
const Match ARRAY_DELIM = { u"," };
|
||||
const Match GROUP_OPEN = { u"(" };
|
||||
const Match GROUP_CLOSE = { u")" };
|
||||
const Match QUOTE = { u"\"" };
|
||||
const Match COMMENT_BEGIN = { u"//" };
|
||||
const Match DIALOGUE_OPEN = { u"<<-" };
|
||||
const Match DIALOGUE_CLOSE = { u"->>" };
|
||||
const Match BEGIN = { u"BEGIN" };
|
||||
const Match END = { u"END" };
|
||||
const ParseGroup SYMBOL = { ALPHA.accept + NUMERIC.accept + u"_"};
|
||||
const Match SPECIAL_SYMBOLS[] = {
|
||||
{ L"+" },
|
||||
{ L"-" },
|
||||
{ L"*" },
|
||||
{ L"/" },
|
||||
{ L"=?" },
|
||||
{ L">?" },
|
||||
{ L"<?" },
|
||||
{ L"<=?" },
|
||||
{ L">=?" }
|
||||
{ u"+" },
|
||||
{ u"-" },
|
||||
{ u"*" },
|
||||
{ u"/" },
|
||||
{ u"=?" },
|
||||
{ u">?" },
|
||||
{ u"<?" },
|
||||
{ u"<=?" },
|
||||
{ u">=?" }
|
||||
};
|
||||
const ParseGroup WS = { L" \t\v\f\r\n" };
|
||||
const ParseGroup WS = { u" \t\v\f\r\n" };
|
||||
const ParseGroup SEPARATOR = {
|
||||
WS.accept +
|
||||
wchar_t(ARRAY_OPEN) +
|
||||
wchar_t(ARRAY_CLOSE) +
|
||||
wchar_t(GROUP_OPEN) +
|
||||
wchar_t(GROUP_CLOSE) +
|
||||
wchar_t(ARRAY_DELIM) +
|
||||
Char(ARRAY_OPEN) +
|
||||
Char(ARRAY_CLOSE) +
|
||||
Char(GROUP_OPEN) +
|
||||
Char(GROUP_CLOSE) +
|
||||
Char(ARRAY_DELIM) +
|
||||
COMMENT_BEGIN.accept[0]
|
||||
};
|
||||
const Match NEWLINE = { L"\n" };
|
||||
const ParseGroup ESCAPED = { L"\\\"" };
|
||||
const Match NEWLINE = { u"\n" };
|
||||
const ParseGroup ESCAPED = { u"\\\"" };
|
||||
|
||||
const Match ESCAPE = { L"\\" };
|
||||
const Match ESCAPE = { u"\\" };
|
||||
|
||||
// Dialogue mode matches
|
||||
const Match MARKUP_OPEN = { L"[" };
|
||||
const Match MARKUP_CLOSE = { L"]" };
|
||||
const Match SPEAKER_OPEN = { L"[" };
|
||||
const Match SPEAKER_CLOSE = { L"]" };
|
||||
const Match MARKUP_TEXT_OPEN = { L"{" };
|
||||
const Match MARKUP_TEXT_CLOSE = { L"}" };
|
||||
const Match TEMPLATE_IND = { L"$" };
|
||||
const Match TEMPLATE_OPEN = { L"{" };
|
||||
const Match TEMPLATE_CLOSE = { L"}" };
|
||||
const Match MARKUP_OPEN = { u"[" };
|
||||
const Match MARKUP_CLOSE = { u"]" };
|
||||
const Match SPEAKER_OPEN = { u"[" };
|
||||
const Match SPEAKER_CLOSE = { u"]" };
|
||||
const Match MARKUP_TEXT_OPEN = { u"{" };
|
||||
const Match MARKUP_TEXT_CLOSE = { u"}" };
|
||||
const Match TEMPLATE_IND = { u"$" };
|
||||
const Match TEMPLATE_OPEN = { u"{" };
|
||||
const Match TEMPLATE_CLOSE = { u"}" };
|
||||
|
||||
const Match COMMAND_ESCAPE = { L"*!" };
|
||||
const Match COMMAND_ESCAPE = { u"*!" };
|
||||
const ParseGroup DIALOGUE_ESCAPED_SINGLE = {
|
||||
ESCAPE.accept +
|
||||
wchar_t(MARKUP_OPEN) +
|
||||
wchar_t(MARKUP_CLOSE) +
|
||||
wchar_t(MARKUP_TEXT_OPEN) +
|
||||
wchar_t(MARKUP_TEXT_CLOSE) +
|
||||
// char(SPEAKER_OPEN) +
|
||||
// char(SPEAKER_CLOSE) +
|
||||
wchar_t(TEMPLATE_IND)
|
||||
// char(TEMPLATE_OPEN) +
|
||||
// char(TEMPLATE_CLOSE)
|
||||
Char(MARKUP_OPEN) +
|
||||
Char(MARKUP_CLOSE) +
|
||||
Char(MARKUP_TEXT_OPEN) +
|
||||
Char(MARKUP_TEXT_CLOSE) +
|
||||
// Char(SPEAKER_OPEN) +
|
||||
// Char(SPEAKER_CLOSE) +
|
||||
Char(TEMPLATE_IND)
|
||||
// Char(TEMPLATE_OPEN) +
|
||||
// Char(TEMPLATE_CLOSE)
|
||||
};
|
||||
|
||||
std::wstring read_file_to_string(const std::string& path) {
|
||||
String read_file_to_string(const std::string& path) {
|
||||
std::ifstream f(path);
|
||||
{ // Some apps on Windows adds this signature in front of UTF-8 files when saving
|
||||
char a, b, c;
|
||||
|
@ -119,12 +117,12 @@ namespace {
|
|||
if (a != (char)0xEF || b != (char)0xBB || c != (char)0xBF)
|
||||
f.seekg(0);
|
||||
else
|
||||
std::cerr << "Warning: Windows UTF-8 BOM skipped" << std::endl;
|
||||
std::cerr << "NVL Warning: Windows UTF-8 BOM skipped" << std::endl;
|
||||
}
|
||||
std::stringstream buffer;
|
||||
buffer << f.rdbuf();
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
return converter.from_bytes(buffer.str());
|
||||
|
||||
return to_NVL_string(buffer.str());
|
||||
}
|
||||
|
||||
std::vector<String> split_string_by_lines(const String& str) {
|
||||
|
@ -321,7 +319,7 @@ namespace {
|
|||
else {
|
||||
auto token = GetToken(f, pos);
|
||||
if (IsNumeric(token))
|
||||
return { std::stof(token) };
|
||||
return { std::stof(to_std_string(token)) };
|
||||
else if (IsLegalSymbolName(token))
|
||||
return { Parse::Type::Symbol, token };
|
||||
else
|
||||
|
@ -352,9 +350,9 @@ namespace {
|
|||
|
||||
std::vector<Parse::Object> tags;
|
||||
|
||||
std::wsmatch tags_match;
|
||||
std::wsmatch effects_match;
|
||||
std::wsmatch params_match;
|
||||
std::match_results<String::const_iterator> tags_match;
|
||||
std::match_results<String::const_iterator> effects_match;
|
||||
std::match_results<String::const_iterator> params_match;
|
||||
|
||||
String reconstruction{};
|
||||
|
||||
|
@ -421,7 +419,7 @@ namespace {
|
|||
if (s.substr(0, 2) == COMMAND_ESCAPE.accept) {
|
||||
size_t dummy = 0;
|
||||
// Pad a space towards the end, the helpers do not expect strings to immediately terminate
|
||||
return ParseCommand(s.substr(2) + L" ", dummy);
|
||||
return ParseCommand(s.substr(2) + u" ", dummy);
|
||||
}
|
||||
|
||||
// assume arity for SwitchSpeaker and Say
|
||||
|
@ -430,7 +428,7 @@ namespace {
|
|||
if (s.front() == SPEAKER_OPEN) {
|
||||
auto name = s.substr(1, s.length() - 2);
|
||||
// if (IsLegalSymbolName(name))
|
||||
return { { Parse::Type::Symbol, L"SwitchSpeaker" }, { Parse::Type::String, name } };
|
||||
return { { Parse::Type::Symbol, u"SwitchSpeaker" }, { Parse::Type::String, name } };
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Malformed speaker command");
|
||||
|
@ -439,7 +437,7 @@ namespace {
|
|||
String copy{ s };
|
||||
Parse::Object tags = MatchMarkup(copy); // THIS WILL MODIFY COPY
|
||||
|
||||
return { { Parse::Type::Symbol, L"Say" }, { Parse::Type::String, copy }, tags };
|
||||
return { { Parse::Type::Symbol, u"Say" }, { Parse::Type::String, copy }, tags };
|
||||
}
|
||||
|
||||
Parse::Scene ParseScene(const String& f, size_t& pos) {
|
||||
|
@ -513,7 +511,7 @@ namespace NVL::Parse {
|
|||
}
|
||||
|
||||
std::vector<Scene> ParseFile(const std::string& path) {
|
||||
std::wstring f = read_file_to_string(path);
|
||||
String f = read_file_to_string(path);
|
||||
|
||||
std::vector<Scene> list {}; // Vector of scenes which each contain a vector of Parses
|
||||
for (size_t i = 0; i < f.length(); i++) {
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include <utility>
|
||||
#include "Common.h"
|
||||
|
||||
|
||||
namespace NVL::Parse {
|
||||
enum class Type { Symbol, Number, String, Array, Subexpression };
|
||||
|
||||
|
@ -29,9 +28,9 @@ namespace NVL::Parse {
|
|||
using Command = std::vector<Object>;
|
||||
|
||||
class Scene {
|
||||
String name;
|
||||
std::vector<Command> commands{};
|
||||
public:
|
||||
String name;
|
||||
Scene(const String& name) : name(name) {}
|
||||
void append(const Command& c) {
|
||||
commands.push_back(c);
|
||||
|
|
|
@ -5,6 +5,8 @@ Hi I'm Electric [b, rb("Programmer")] {プログラマー} from Michigan.
|
|||
Commencing JS hypnosis...
|
||||
Enchanté !
|
||||
Advance
|
||||
[Terrio]
|
||||
mesugaki
|
||||
Thanks
|
||||
[死大师]
|
||||
他们肯定做炫酷的漫画[b] {静止系mad}。并拉扯运动曲线和使用[rb("思源宋体")] {Source Han Serif}作为主要的字体。
|
||||
|
|
Loading…
Add table
Reference in a new issue