#include "ADVect.h" #include "Common.h" #include "Environment.h" #include "Graphics.h" #include "SDL2/SDL_thread.h" #include "SDL2/SDL_timer.h" #include #include #include #include #include #include #include #include #include namespace { template struct Track { T current; }; template concept IsTrack = requires (const TR& t) { std::is_same_v; }; template concept IsNamedTrack = IsTrack && requires (const TR& t) { std::is_same_v; }; template requires(!IsNamedTrack) struct NamedTrack : TR { std::string name; }; template TR> struct VisualTrack : TR { i32 pos_x = 0, pos_y = 0; u32 w = 500, h = 500; f32 opacity = 1.0f; // TODO other transforms }; template TR> struct FillTrack : TR { i32 pos_x = 0, pos_y = 0; u32 w = 500, h = 500; f32 opacity = 1.0f; u32 fill = 0xFFFFFFFF; }; /* TODO impl struct AudioTrack {}; */ using TextTrack = FillTrack>; using MarkupTextTrack = FillTrack>; using NamedImageTrack = VisualTrack>>; /* TODO VideoTrack */ SDL_Window* window; u16 window_width = 1280; u16 window_height = 720; bool running = false; bool m_keys[65536]{}; u64 init_time, last_time, current_time, delta_t; template struct Varying { u64 begin, duration; std::function interp; bool done = true; T get() const { if (done) return interp(1.f); else return interp((current_time - begin) / static_cast(duration)); } void kickoff(u64 dur, const std::function& f = [](NVL::Number x) { return static_cast(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 anim; std::function draw; void check() { if (next != nullptr && anim.check()) { current = next; next = nullptr; } } void change(ADVect::Graphics::ImageTexture* n, u64 dur, const std::function& 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 anim; void check() { anim.check(); } void change(const NVL::Environment::MarkupString& n, u16 speed) { current = n; anim.kickoff(speed * n.length(), [this](NVL::Number x) { return static_cast(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); } } } std::vector scenes; u32 current_scene = 0; u32 scene_pos = 0; TypewriterMarkupTextTrack m_text{ { {}, 280, 90 } }; TextTrack speaker { u"", 250, 150 }; NamedImageTransitionTrack bg, avatar; NamedImageTrack dialogue_bg; /* std::list 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; } */ } namespace ADVect { bool Init(std::string name, const std::vector& sc) { 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; } window = SDL_CreateWindow(name.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, window_width, window_height, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); if (window == NULL) { std::cerr << "ADV: Failed to create window: " << SDL_GetError() << std::endl; return false; } bgfx::Init bgfxInit; bgfxInit.type = bgfx::RendererType::Count; // Automatically choose a renderer. bgfxInit.resolution.width = window_width; bgfxInit.resolution.height = window_height; bgfxInit.resolution.reset = BGFX_RESET_VSYNC; #if !BX_PLATFORM_EMSCRIPTEN SDL_SysWMinfo wmi; SDL_VERSION(&wmi.version); if (!SDL_GetWindowWMInfo(window, &wmi)) { std::cerr << "ADV: SDL_SysWMinfo could not be retrieved: " << SDL_GetError() << std::endl; } #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 if (!bgfx::init(bgfxInit)) { std::cerr << "ADV: Failed bgfx initialization" << std::endl; return false; }; 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); 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; } bg.draw = draw_image_transition_fade; avatar.draw = draw_image_transition_crossfade; Advance(); return true; } void Shutdown() { Graphics::Shutdown(); bgfx::shutdown(); SDL_DestroyWindow(window); SDL_Quit(); } void Advance() { size_t curr_size = scenes[current_scene].get().size(); while (scene_pos < curr_size && std::get(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(scenes[current_scene].get()[scene_pos][0].value) == u"Say") { NVL::Environment::Apply(scenes[current_scene].get()[scene_pos]); scene_pos++; } } void Update() { if (m_keys[SDL_SCANCODE_SPACE]) { Advance(); m_keys[SDL_SCANCODE_SPACE] = false; } bg.check(); avatar.check(); m_text.check(); } 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); ADVect::Graphics::RenderString(speaker.current, speaker.pos_x, speaker.pos_y, speaker.fill, ADVect::Graphics::TEXT_BOLD); m_text.draw(); 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()); bgfx::frame(); } void Run() { last_time = init_time = SDL_GetTicks64(); running = true; while (running) { current_time = SDL_GetTicks64(); delta_t = current_time - last_time; for (SDL_Event event; SDL_PollEvent(&event);) { switch (event.type) { case SDL_QUIT: running = false; break; case SDL_KEYDOWN: m_keys[event.key.keysym.scancode] = true; break; case SDL_KEYUP: m_keys[event.key.keysym.scancode] = false; break; } } Update(); Render(); last_time = current_time; } } } int main(int argc, char* argv[]) { const std::string PJ_DIR = "E:\\Archive\\Projects\\NouVeL\\NVL\\"; NVL::Environment::ENVIRONMENT.enter({ { u"Say", NVL::Environment::Variable([](std::vector args) { m_text.change(NVL::Environment::UnpackMarkupVariable(std::get>(NVL::Environment::Variable(args[0]).value)), 10); return NVL::Environment::Type::Nil; }, 2) }, { u"SwitchSpeaker", NVL::Environment::Variable([](std::vector args) { speaker.current = std::get(NVL::Environment::Variable(args[0]).value); return NVL::Environment::Type::Nil; }, 1) }, /* { u"ImageTrack", NVL::Environment::Variable([](std::vector args) { add_image_track(NVL::to_std_string(std::get(NVL::Environment::Variable(args[0]).value))); return NVL::Environment::Type::Nil; }, 1) },*/ { u"Show", NVL::Environment::Variable([](std::vector args) { std::string name = NVL::to_std_string(std::get(NVL::Environment::Variable(args[0]).value)); /* NamedImageTrack* t = find_image_track(name); if (t == nullptr) { std::cerr << "ADV: Cannot find ImageTrack " << name << std::endl; } else { auto s = std::get(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::Environment::Variable(args[1]).value))), 200); else if (name == "Avatar") avatar.change(ADVect::Graphics::GetImageTextureFromFile(NVL::to_std_string(std::get(NVL::Environment::Variable(args[1]).value))), 200); else if (name == "BGDialogue") dialogue_bg.current = ADVect::Graphics::GetImageTextureFromFile(NVL::to_std_string(std::get(NVL::Environment::Variable(args[1]).value))); else std::cerr << "ADV: Cannot find ImageTrack " << name << std::endl; return NVL::Environment::Type::Nil; }, 2) } }); std::vector 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; ADVect::Run(); ADVect::Shutdown(); return EXIT_SUCCESS; }