NouVeL/ADVect/ADVect.cpp

412 lines
12 KiB
C++
Raw Normal View History

2022-05-09 01:50:09 -04:00
#include "ADVect.h"
#include "Common.h"
#include "Environment.h"
2022-05-09 01:50:09 -04:00
#include "Graphics.h"
#include "SDL2/SDL_thread.h"
#include "SDL2/SDL_timer.h"
2022-05-09 01:50:09 -04:00
#include <SDL2/SDL.h>
#include <bx/math.h>
2022-08-18 12:17:43 -04:00
#include <SDL2/SDL_syswm.h>
#include <bgfx/bgfx.h>
#include <bgfx/platform.h>
#include <cstddef>
#include <functional>
2022-05-09 01:50:09 -04:00
#include <iostream>
#include <list>
2022-08-18 12:17:43 -04:00
namespace {
template <typename T>
struct Track {
T current;
};
2022-08-18 12:17:43 -04:00
template <typename TR, typename T>
concept IsTrack = requires (const TR& t) {
std::is_same_v<T, decltype(t.current)>;
};
template <typename TR, typename T>
concept IsNamedTrack = IsTrack<TR, T> && requires (const TR& t) {
std::is_same_v<std::string, decltype(t.name)>;
};
template<typename T, typename TR> requires(!IsNamedTrack<TR, T>)
struct NamedTrack : TR {
std::string name;
};
template<typename T, IsTrack<T> TR>
struct VisualTrack : TR {
i32 pos_x = 0, pos_y = 0;
u32 w = 500, h = 500;
f32 opacity = 1.0f;
// TODO other transforms
};
template<typename T, IsTrack<T> TR>
struct FillTrack : TR {
i32 pos_x = 0, pos_y = 0;
u32 w = 500, h = 500;
f32 opacity = 1.0f;
u32 fill = 0xFFFFFFFF;
};
2022-08-18 12:17:43 -04:00
/* TODO impl
struct AudioTrack {};
*/
using TextTrack = FillTrack<NVL::String, Track<NVL::String>>;
using MarkupTextTrack = FillTrack<NVL::Environment::MarkupString, Track<NVL::Environment::MarkupString>>;
using NamedImageTrack = VisualTrack<ADVect::Graphics::ImageTexture*, NamedTrack<ADVect::Graphics::ImageTexture*, Track<ADVect::Graphics::ImageTexture*>>>;
/* TODO VideoTrack */
SDL_Window* window;
u16 window_width = 1280;
u16 window_height = 720;
bool running = false;
2022-08-18 12:17:43 -04:00
bool m_keys[65536]{};
u64 init_time, last_time, current_time, delta_t;
template <typename T>
struct Varying {
u64 begin, duration;
std::function<T(NVL::Number)> interp;
bool done = true;
T get() const {
if (done)
return interp(1.f);
else
return interp((current_time - begin) / static_cast<f32>(duration));
}
void kickoff(u64 dur, const std::function<T(NVL::Number)>& f = [](NVL::Number x) { return static_cast<T>(x); }) {
begin = current_time;
done = false;
duration = dur;
interp = f;
}
bool check() {
if (done) return true;
else if (current_time >= begin + duration) {
done = true;
return true;
}
else return false;
}
};
struct NamedImageTransitionTrack : NamedImageTrack {
ADVect::Graphics::ImageTexture* next = nullptr;
Varying<f32> anim;
std::function<void(const NamedImageTransitionTrack&)> draw;
void check() {
if (next != nullptr && anim.check()) {
current = next;
next = nullptr;
}
}
void change(ADVect::Graphics::ImageTexture* n, u64 dur, const std::function<f32(NVL::Number)>& f = [](NVL::Number x) { return x; }) {
if (current == nullptr)
current = n;
else if (n == nullptr) current = n;
else {
next = n;
anim.kickoff(dur, f);
}
}
};
struct TypewriterMarkupTextTrack : MarkupTextTrack {
Varying<u32> anim;
void check() { anim.check(); }
void change(const NVL::Environment::MarkupString& n, u16 speed) {
current = n;
2022-08-28 20:54:30 -04:00
anim.kickoff(speed * n.length(), [this](NVL::Number x) { return static_cast<size_t>(x * this->current.length()); });
}
void draw() {
if (anim.done)
ADVect::Graphics::RenderStringMarkup(current, pos_x, pos_y, fill);
else
ADVect::Graphics::RenderStringMarkup(current.substr(anim.get()), pos_x, pos_y, fill);
}
};
void draw_image_transition_fade(const NamedImageTransitionTrack& t) {
if (t.current != nullptr) {
if (t.next != nullptr) {
ADVect::Graphics::DrawTextureImage(*t.current, t.pos_x, t.pos_y);
ADVect::Graphics::DrawTextureImageAlpha(*t.next, t.pos_x, t.pos_y, t.anim.get());
}
else {
ADVect::Graphics::DrawTextureImage(*t.current, t.pos_x, t.pos_y);
}
}
}
void draw_image_transition_crossfade(const NamedImageTransitionTrack& t) {
if (t.current != nullptr) {
if (t.next != nullptr) {
ADVect::Graphics::DrawTextureImageAlpha(*t.current, t.pos_x, t.pos_y, 1.0f - t.anim.get());
ADVect::Graphics::DrawTextureImageAlpha(*t.next, t.pos_x, t.pos_y, t.anim.get());
}
else {
ADVect::Graphics::DrawTextureImage(*t.current, t.pos_x, t.pos_y);
}
}
}
2022-08-18 12:17:43 -04:00
std::vector<NVL::Parse::Scene> scenes;
u32 current_scene = 0;
u32 scene_pos = 0;
2022-08-22 02:15:25 -04:00
TypewriterMarkupTextTrack m_text{ { {}, 280, 90 } };
TextTrack speaker { u"", 250, 150 };
NamedImageTransitionTrack bg, avatar;
NamedImageTrack dialogue_bg;
/*
std::list<NamedImageTrack> tracks_img{}; // instead of vector because we need pointers to members; prevent use after free
NamedImageTrack* find_image_track(const std::string& s) {
for (auto& t : tracks_img)
if (t.name == s) {
return &t;
}
return nullptr;
}
bool add_image_track(const std::string& s) {
if (find_image_track(s) == nullptr) {
NamedImageTrack t;
t.name = s;
tracks_img.push_back(std::move(t));
return true;
}
std::cerr << "ADV: Redefinition of ImageTrack " << s << std::endl;
return false;
}
*/
2022-08-18 12:17:43 -04:00
}
2022-05-09 01:50:09 -04:00
namespace ADVect {
bool Init(std::string name, const std::vector<NVL::Parse::Scene>& sc) {
2022-08-18 12:17:43 -04:00
scenes = sc; // sure make a copy whatever
if (SDL_Init(SDL_INIT_VIDEO)) {
std::cerr << "ADV: Failed to init SDL: " << SDL_GetError() << std::endl;
return false;
2022-05-09 01:50:09 -04:00
}
2022-08-18 12:17:43 -04:00
window = SDL_CreateWindow(name.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, window_width, window_height, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
2022-08-21 16:24:13 -04:00
if (window == NULL) {
2022-08-22 02:15:25 -04:00
std::cerr << "ADV: Failed to create window: " << SDL_GetError() << std::endl;
return false;
2022-08-21 16:24:13 -04:00
}
2022-08-18 12:17:43 -04:00
bgfx::Init bgfxInit;
bgfxInit.type = bgfx::RendererType::Count; // Automatically choose a renderer.
bgfxInit.resolution.width = window_width;
bgfxInit.resolution.height = window_height;
2022-08-18 12:17:43 -04:00
bgfxInit.resolution.reset = BGFX_RESET_VSYNC;
#if !BX_PLATFORM_EMSCRIPTEN
SDL_SysWMinfo wmi;
SDL_VERSION(&wmi.version);
if (!SDL_GetWindowWMInfo(window, &wmi)) {
2022-08-22 02:15:25 -04:00
std::cerr << "ADV: SDL_SysWMinfo could not be retrieved: " << SDL_GetError() << std::endl;
2022-08-18 12:17:43 -04:00
}
#endif
#if BX_PLATFORM_WINDOWS
bgfxInit.platformData.nwh = wmi.info.win.window;
#elif BX_PLATFORM_OSX
bgfxInit.platformData.nwh = wmi.info.cocoa.window;
#elif BX_PLATFORM_LINUX
bgfxInit.platformData.ndt = wmi.info.x11.display;
bgfxInit.platformData.nwh = (void*)(uintptr_t)wmi.info.x11.window;
#elif BX_PLATFORM_EMSCRIPTEN
bgfxInit.platformData.nwh = (void*)"#canvas";
#endif
2022-08-21 16:24:13 -04:00
if (!bgfx::init(bgfxInit)) {
2022-08-22 02:15:25 -04:00
std::cerr << "ADV: Failed bgfx initialization" << std::endl;
return false;
2022-08-21 16:24:13 -04:00
};
2022-08-22 02:15:25 -04:00
bgfx::setDebug(BGFX_DEBUG_TEXT);
bgfx::setViewClear(0, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x443322ff, 1.0f, 0);
bgfx::setViewRect(0, 0, 0, window_width, window_height);
2022-08-22 02:15:25 -04:00
bgfx::setViewMode(0, bgfx::ViewMode::Sequential);
float view[16];
bx::mtxTranslate(view, 0.f, 0.f, 1.0f);
float proj[16];
bx::mtxOrtho(proj, 0.0f, window_width, 0.0f, window_height, 0.1f, 100.0f, 0.f, bgfx::getCaps()->homogeneousDepth);
bgfx::setViewTransform(0, view, proj);
if (!ADVect::Graphics::Init(window_width, window_height)) {
std::cerr << "ADV: Graphics init failed" << std::endl;
return false;
}
2022-08-18 12:17:43 -04:00
bg.draw = draw_image_transition_fade;
avatar.draw = draw_image_transition_crossfade;
2022-05-09 03:46:44 -04:00
Advance();
return true;
2022-05-09 01:50:09 -04:00
}
2022-08-18 12:17:43 -04:00
void Shutdown() {
2022-08-21 16:24:13 -04:00
Graphics::Shutdown();
bgfx::shutdown();
2022-08-18 12:17:43 -04:00
SDL_DestroyWindow(window);
2022-05-09 01:50:09 -04:00
SDL_Quit();
}
2022-08-18 12:17:43 -04:00
void Advance() {
2022-08-21 16:24:13 -04:00
size_t curr_size = scenes[current_scene].get().size();
while (scene_pos < curr_size && 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++;
}
if (curr_size == scene_pos) {
running = false;
return;
}
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++;
2022-05-09 03:46:44 -04:00
}
}
2022-08-18 12:17:43 -04:00
void Update() {
2022-05-09 03:46:44 -04:00
if (m_keys[SDL_SCANCODE_SPACE]) {
Advance();
m_keys[SDL_SCANCODE_SPACE] = false;
}
bg.check();
avatar.check();
m_text.check();
2022-05-09 03:46:44 -04:00
}
2022-08-18 12:17:43 -04:00
void Render() {
bgfx::touch(0);
/*
for (auto& t : tracks_img) {
if (t.current != nullptr)
ADVect::Graphics::DrawTextureImage(*t.current, t.pos_x, t.pos_y);
}
*/
bg.draw(bg);
if (dialogue_bg.current != nullptr)
ADVect::Graphics::DrawTextureImage(*dialogue_bg.current, dialogue_bg.pos_x, dialogue_bg.pos_y);
avatar.draw(avatar);
2022-08-22 02:15:25 -04:00
ADVect::Graphics::RenderString(speaker.current, speaker.pos_x, speaker.pos_y, speaker.fill, ADVect::Graphics::TEXT_BOLD);
m_text.draw();
2022-08-18 12:17:43 -04:00
bgfx::dbgTextClear();
bgfx::dbgTextPrintf(0, 0, 0xf8, "NouVeL x ADVect :: %s :: %s %s", BX_COMPILER_NAME, __DATE__, __TIME__);
bgfx::dbgTextPrintf(0, 1, 0xf8, "SDL %i.%i.%2i :: bgfx 1.%i :: %s", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL, BGFX_API_VERSION, bgfx::getRendererName(bgfx::getRendererType()));
bgfx::dbgTextPrintf(0, 2, 0xf8, "Current Position: %u", scene_pos);
bgfx::dbgTextPrintf(0, 3, 0xf8, "Current Scene: %s", NVL::to_std_string(scenes[current_scene].name).c_str());
2022-05-09 01:50:09 -04:00
2022-08-18 12:17:43 -04:00
bgfx::frame();
2022-05-09 01:50:09 -04:00
}
2022-05-09 03:46:44 -04:00
2022-08-18 12:17:43 -04:00
void Run() {
last_time = init_time = SDL_GetTicks64();
2022-08-18 12:17:43 -04:00
running = true;
while (running) {
current_time = SDL_GetTicks64();
delta_t = current_time - last_time;
2022-05-09 01:50:09 -04:00
for (SDL_Event event; SDL_PollEvent(&event);) {
switch (event.type) {
case SDL_QUIT:
2022-08-18 12:17:43 -04:00
running = false;
2022-05-09 01:50:09 -04:00
break;
2022-05-09 03:46:44 -04:00
case SDL_KEYDOWN:
m_keys[event.key.keysym.scancode] = true;
break;
case SDL_KEYUP:
m_keys[event.key.keysym.scancode] = false;
break;
2022-05-09 01:50:09 -04:00
}
2022-05-09 03:46:44 -04:00
}
Update();
2022-05-09 01:50:09 -04:00
Render();
last_time = current_time;
2022-05-09 01:50:09 -04:00
}
}
}
int main(int argc, char* argv[]) {
const std::string PJ_DIR = "E:\\Archive\\Projects\\NouVeL\\NVL\\";
2022-05-09 01:50:09 -04:00
2022-08-27 04:40:06 -04:00
NVL::Environment::ENVIRONMENT.enter({
{
u"Say",
NVL::Environment::Variable([](std::vector<NVL::Environment::Variable> args) {
m_text.change(NVL::Environment::UnpackMarkupVariable(std::get<std::vector<NVL::Environment::Variable>>(NVL::Environment::Variable(args[0]).value)), 10);
2022-08-27 04:40:06 -04:00
return NVL::Environment::Type::Nil;
}, 2)
},
{
u"SwitchSpeaker",
NVL::Environment::Variable([](std::vector<NVL::Environment::Variable> args) {
speaker.current = std::get<NVL::String>(NVL::Environment::Variable(args[0]).value);
2022-08-27 04:40:06 -04:00
return NVL::Environment::Type::Nil;
}, 1)
},
/* {
2022-08-27 04:40:06 -04:00
u"ImageTrack",
NVL::Environment::Variable([](std::vector<NVL::Environment::Variable> args) {
add_image_track(NVL::to_std_string(std::get<NVL::String>(NVL::Environment::Variable(args[0]).value)));
return NVL::Environment::Type::Nil;
}, 1)
},*/
2022-08-27 04:40:06 -04:00
{
u"Show",
NVL::Environment::Variable([](std::vector<NVL::Environment::Variable> args) {
std::string name = NVL::to_std_string(std::get<NVL::String>(NVL::Environment::Variable(args[0]).value));
/* NamedImageTrack* t = find_image_track(name);
2022-08-27 04:40:06 -04:00
if (t == nullptr) {
std::cerr << "ADV: Cannot find ImageTrack " << name << std::endl;
}
else {
auto s = std::get<NVL::String>(NVL::Environment::Variable(args[1]).value);
auto s2 = NVL::to_std_string(s);
t->current = ADVect::Graphics::GetImageTextureFromFile(s2);
}*/
if (name == "BG")
bg.change(ADVect::Graphics::GetImageTextureFromFile(NVL::to_std_string(std::get<NVL::String>(NVL::Environment::Variable(args[1]).value))), 200);
else if (name == "Avatar")
avatar.change(ADVect::Graphics::GetImageTextureFromFile(NVL::to_std_string(std::get<NVL::String>(NVL::Environment::Variable(args[1]).value))), 200);
else if (name == "BGDialogue")
dialogue_bg.current = ADVect::Graphics::GetImageTextureFromFile(NVL::to_std_string(std::get<NVL::String>(NVL::Environment::Variable(args[1]).value)));
else
std::cerr << "ADV: Cannot find ImageTrack " << name << std::endl;
2022-08-27 04:40:06 -04:00
return NVL::Environment::Type::Nil;
}, 2)
}
2022-08-27 04:40:06 -04:00
});
2022-08-27 04:40:06 -04:00
std::vector<NVL::Parse::Scene> SCENES = NVL::Parse::ParseFile(PJ_DIR + "dialogue.nvl");
if (SCENES.empty()) {
std::cerr << "ADV: Empty NVL parse, check file" << std::endl;
return EXIT_FAILURE;
}
if (!ADVect::Init("ADV Test", SCENES)) return EXIT_FAILURE;
2022-08-18 12:17:43 -04:00
ADVect::Run();
2022-05-09 01:50:09 -04:00
2022-08-18 12:17:43 -04:00
ADVect::Shutdown();
return EXIT_SUCCESS;
2022-05-09 01:50:09 -04:00
}