diff --git a/src/main.cpp b/src/main.cpp index b9558f9..f736904 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,20 +1,58 @@ -#include +// this file is an absolute clusterfuck to demo the internals. +// if you know any better please make a pr. please. +// i hate lines++; but i don't know any better, do i look like i know frontend? +// branching? use arbitrary lines value, do i look like i know any better? +#include #include #include #include #include +#include #include #include #include #include +// --- RAII wrappers for engine objects --- +struct WindowHandle { + window_t* ptr; + WindowHandle(window_t* w) : ptr(w) {} + ~WindowHandle() { if (ptr) window_destroy(ptr); } + window_t* get() { return ptr; } + operator window_t* () { return ptr; } +}; + +struct CompositorHandle { + compositor_t* ptr; + CompositorHandle(compositor_t* c) : ptr(c) {} + ~CompositorHandle() { if (ptr) compositor_destroy(ptr); } + compositor_t* get() { return ptr; } + operator compositor_t* () { return ptr; } +}; + +struct TextHandle { + text_t* ptr; + TextHandle(text_t* t) : ptr(t) {} + ~TextHandle() { if (ptr) text_destroy(ptr); } + text_t* get() { return ptr; } + operator text_t* () { return ptr; } +}; + +struct ImageHandle { + uint32_t* ptr; + ImageHandle(uint32_t* p) : ptr(p) {} + ~ImageHandle() { if (ptr) image_free(ptr); } + uint32_t* get() { return ptr; } + operator uint32_t* () { return ptr; } +}; + int main() { const int WIDTH = 1280; const int HEIGHT = 720; const int CHAR_WIDTH = 400; const int CHAR_HEIGHT = 600; - // --- New Character Positioning Constants --- + // --- Character Positioning Constants --- // The vertical baseline for character sprites, making positioning independent of sprite height. const int CHAR_BASELINE_Y = HEIGHT - 140; // The horizontal center for the character on the left. @@ -24,86 +62,82 @@ int main() { // An off-screen position to the left for transitions, based on the final position. const int OFFSCREEN_LEFT_X = LEFT_CHAR_CENTER_X - 660; - window_t* window = window_create(WIDTH, HEIGHT, u8"The Obwarzanek Narrative Engine"); - if (!window) { + WindowHandle window(window_create(WIDTH, HEIGHT, u8"The Obwarzanek Narrative Engine")); + if (!window.get()) { printf("Failed to create window\n"); return 1; } - compositor_t* compositor = compositor_init(WIDTH, HEIGHT); - if (!compositor) { + CompositorHandle compositor(compositor_init(WIDTH, HEIGHT)); + if (!compositor.get()) { printf("Failed to initialize compositor\n"); - window_destroy(window); return 1; } - uint32_t* bg = image_load_resized("assets/background.png", WIDTH, HEIGHT); - if (bg) { - uint32_t bg_id = compositor_insert(compositor, bg, WIDTH, HEIGHT, 0, 0, 0, 1.0f, ANCHOR_TOP_LEFT); - compositor_set_alpha(compositor, bg_id, 1.0f); + ImageHandle bg(image_load_resized("assets/background.png", WIDTH, HEIGHT)); + if (bg.get()) { + uint32_t bg_id = compositor_insert(compositor.get(), bg.get(), WIDTH, HEIGHT, 0, 0, 0, 1.0f, ANCHOR_TOP_LEFT); + compositor_set_alpha(compositor.get(), bg_id, 1.0f); } - uint32_t* textbox = image_load_resized("assets/textbox.png", WIDTH, 180); - if (textbox) { + ImageHandle textbox(image_load_resized("assets/textbox.png", WIDTH, 180)); + if (textbox.get()) { // Position via the bottom-left corner for easier alignment with screen bottom - compositor_insert(compositor, textbox, WIDTH, 180, 0, HEIGHT, 3, 1.0f, ANCHOR_BOTTOM_LEFT); + compositor_insert(compositor.get(), textbox.get(), WIDTH, 180, 0, HEIGHT, 3, 1.0f, ANCHOR_BOTTOM_LEFT); } - uint32_t* maksw_happy = image_load_resized("assets/maksw.png", CHAR_WIDTH, CHAR_HEIGHT); + ImageHandle maksw_happy(image_load_resized("assets/maksw.png", CHAR_WIDTH, CHAR_HEIGHT)); // Insert off-screen left, anchored at bottom-center for natural scaling - uint32_t maksw_happy_id = compositor_insert(compositor, maksw_happy, + uint32_t maksw_happy_id = compositor_insert(compositor.get(), maksw_happy.get(), CHAR_WIDTH, CHAR_HEIGHT, OFFSCREEN_LEFT_X, CHAR_BASELINE_Y, 1, 1.0f, ANCHOR_BOTTOM_CENTER); - uint32_t* maksw_neutral = image_load_resized("assets/maksw2.png", CHAR_WIDTH, CHAR_HEIGHT); + ImageHandle maksw_neutral(image_load_resized("assets/maksw2.png", CHAR_WIDTH, CHAR_HEIGHT)); // Insert at the final left position, anchored at bottom-center - uint32_t maksw_neutral_id = compositor_insert(compositor, maksw_neutral, + uint32_t maksw_neutral_id = compositor_insert(compositor.get(), maksw_neutral.get(), CHAR_WIDTH, CHAR_HEIGHT, LEFT_CHAR_CENTER_X, CHAR_BASELINE_Y, 1, 1.0f, ANCHOR_BOTTOM_CENTER); - compositor_set_visible(compositor, maksw_neutral_id, false); + compositor_set_visible(compositor.get(), maksw_neutral_id, false); - uint32_t* wikor = image_load_resized("assets/wikor.png", CHAR_WIDTH, CHAR_HEIGHT); + ImageHandle wikor(image_load_resized("assets/wikor.png", CHAR_WIDTH, CHAR_HEIGHT)); // Insert at the final right position, anchored at bottom-center - uint32_t wikor_id = compositor_insert(compositor, wikor, + uint32_t wikor_id = compositor_insert(compositor.get(), wikor.get(), CHAR_WIDTH, CHAR_HEIGHT, RIGHT_CHAR_CENTER_X, CHAR_BASELINE_Y, 2, 1.0f, ANCHOR_BOTTOM_CENTER); - text_t* name_text = text_init(300, 100); - text_set_font(name_text, "assets/Aller_Rg.ttf", 40.0f); + TextHandle name_text(text_init(300, 100)); + text_set_font(name_text.get(), "assets/Aller_Rg.ttf", 40.0f); // Text is anchored at top-left, positioned relative to textbox - uint32_t name_id = compositor_insert(compositor, text_getfb(name_text), + uint32_t name_id = compositor_insert(compositor.get(), text_getfb(name_text.get()), 300, 100, 60, HEIGHT - 180, 4, 1.0f, ANCHOR_TOP_LEFT); - text_t* dialogue_text = text_init(WIDTH - 140, 160); - text_set_font(dialogue_text, "assets/Vera.ttf", 30.0f); + TextHandle dialogue_text(text_init(WIDTH - 140, 160)); + text_set_font(dialogue_text.get(), "assets/Vera.ttf", 30.0f); // Text is anchored at top-left, positioned relative to textbox - uint32_t dialogue_id = compositor_insert(compositor, text_getfb(dialogue_text), + uint32_t dialogue_id = compositor_insert(compositor.get(), text_getfb(dialogue_text.get()), WIDTH - 140, 160, 60, HEIGHT - 130, 4, 1.0f, ANCHOR_TOP_LEFT); - uint32_t* mask = image_load_resized("assets/mask.png", 1280, 1080); + ImageHandle mask(image_load_resized("assets/mask.png", 1280, 1080)); // Use top-left anchor for full-screen wipes - uint32_t mask_id = compositor_insert(compositor, mask, 1280, 1080, 0, -1080, 5, 1.0f, ANCHOR_TOP_LEFT); - compositor_set_visible(compositor, mask_id, false); + uint32_t mask_id = compositor_insert(compositor.get(), mask.get(), 1280, 1080, 0, -1080, 5, 1.0f, ANCHOR_TOP_LEFT); + compositor_set_visible(compositor.get(), mask_id, false); - uint32_t* mask2 = image_load_resized("assets/mask2.png", 1280, 1080); + ImageHandle mask2(image_load_resized("assets/mask2.png", 1280, 1080)); // Use top-left anchor for full-screen wipes - uint32_t mask2_id = compositor_insert(compositor, mask2, 1280, 1080, 0, 0, 5, 1.0f, ANCHOR_TOP_LEFT); - compositor_set_visible(compositor, mask2_id, false); + uint32_t mask2_id = compositor_insert(compositor.get(), mask2.get(), 1280, 1080, 0, 0, 5, 1.0f, ANCHOR_TOP_LEFT); + compositor_set_visible(compositor.get(), mask2_id, false); - uint32_t* black = (uint32_t*)malloc(WIDTH * HEIGHT * sizeof(uint32_t)); - for (int i = 0; i < WIDTH * HEIGHT; i++) { - black[i] = 0xFF000000; - } - // Use top-left anchor for full-screen fade - uint32_t black_id = compositor_insert(compositor, black, WIDTH, HEIGHT, 0, 0, 6, 1.0f, ANCHOR_TOP_LEFT); - compositor_set_visible(compositor, black_id, false); - compositor_set_alpha(compositor, black_id, 0.0f); + // yeah! memory savings! + //std::unique_ptr black(new uint32_t[WIDTH * HEIGHT]); + //for (int i = 0; i < WIDTH * HEIGHT; i++) black[i] = 0xFF000000; + //// Use top-left anchor for full-screen fade + //uint32_t black_id = compositor_insert(compositor.get(), black.get(), WIDTH, HEIGHT, 0, 0, 6, 1.0f, ANCHOR_TOP_LEFT); + //compositor_set_visible(compositor.get(), black_id, false); + //compositor_set_alpha(compositor.get(), black_id, 0.0f); // Animate character into position using the new centered coordinates - compositor_animate_move(compositor, maksw_happy_id, LEFT_CHAR_CENTER_X, CHAR_BASELINE_Y, 1.0f, EASE_OUT_CUBIC); - compositor_animate_scale(compositor, maksw_happy_id, 1.05f, 0.3f, EASE_IN_OUT_CUBIC, true); - text_push(name_text, "maksw", 0.0f, true); - text_push(dialogue_text, - "Hey there! Welcome to the Obwarzanek Narrative Engine demo. " - "This is a simple visual novel engine showcasing text rendering, " - "image compositing, and basic animation features.", + compositor_animate_move(compositor.get(), maksw_happy_id, LEFT_CHAR_CENTER_X, CHAR_BASELINE_Y, 1.0f, EASE_OUT_CUBIC); + compositor_animate_scale(compositor.get(), maksw_happy_id, 1.05f, 0.3f, EASE_IN_OUT_CUBIC, true); + text_push(name_text.get(), "maksw", 0.0f, true); + text_push(dialogue_text.get(), + "Hello and welcome to the Obwarzanek Narrative Engine demo!", 30.0f, true); bool running = true; @@ -116,7 +150,7 @@ int main() { const std::chrono::duration REPORT_INTERVAL = std::chrono::seconds(5); while (running) { - if (window_pollevent(window)) running = false; + if (window_pollevent(window.get())) running = false; auto current_time = std::chrono::high_resolution_clock::now(); std::chrono::duration delta_duration = current_time - last_frame_time; @@ -129,74 +163,135 @@ int main() { std::chrono::duration time_since_last_report = current_time - last_report_time; if (time_since_last_report >= REPORT_INTERVAL) { - // Only report if at least one frame has passed if (frame_count > 0) { float avg_delta_time = frame_time_sum / (float)frame_count; float fps = 1.0f / avg_delta_time; - - // Calculate the actual duration the FPS was measured over (should be approx 5.0f) float actual_duration = time_since_last_report.count(); printf("FPS: %.2f (over %d frames in %.2f seconds)\n", fps, frame_count, actual_duration); } - // Reset counters and update last_report_time frame_count = 0; frame_time_sum = 0.0f; - last_report_time = current_time; // Mark the current time as the new report start + last_report_time = current_time; } static bool mouse_was_down = false; - bool mouse_is_down = window_is_mouse_button_down(window, MOUSE_BUTTON_LEFT); + bool mouse_is_down = window_is_mouse_button_down(window.get(), MOUSE_BUTTON_LEFT); bool mouse_clicked = mouse_is_down && !mouse_was_down; mouse_was_down = mouse_is_down; static bool key_was_down = false; - bool key_is_down = window_is_key_down(window, KEY_SPACE); + bool key_is_down = window_is_key_down(window.get(), KEY_SPACE); bool key_pressed = key_is_down && !key_was_down; key_was_down = key_is_down; // don't judge me, it will work for now - static unsigned int lines = 0; + static unsigned int lines = 2; static bool dialog_finished = false; static bool transitioning = false; if (mouse_clicked || key_pressed) { - if (text_is_animating(dialogue_text)) { - text_show_all_current(dialogue_text); + if (text_is_animating(dialogue_text.get())) { + text_show_all_current(dialogue_text.get()); } else { - // this will bite me switch (lines) { - case 0: - // line 2 - compositor_animation_complete_all(compositor); - compositor_animate_scale(compositor, maksw_happy_id, 1.0f, 0.3f, EASE_IN_OUT_CUBIC); - compositor_animate_scale(compositor, wikor_id, 1.1f, 0.3f, EASE_IN_OUT_CUBIC); - text_clear(dialogue_text); - text_clear(name_text); - text_push(name_text, "wikor", 0.0f, true); - text_push(dialogue_text, "Hello to you too, maksw..", 30.0f, true); + case 2: + compositor_animation_complete_all(compositor.get()); + text_clear(dialogue_text.get()); + text_push(dialogue_text.get(), "Today I'll present to you-", 30.0f, true); lines++; break; - case 1: - // line 3 - compositor_animation_complete_all(compositor); - - compositor_set_visible(compositor, mask_id, true); - compositor_animate_move(compositor, mask_id, 0, 0, 0.5f, EASE_OUT_CUBIC, false); - + case 3: + compositor_animation_complete_all(compositor.get()); + compositor_animate_scale(compositor.get(), maksw_happy_id, 1.0f, 0.3f, EASE_IN_OUT_CUBIC); + compositor_animate_scale(compositor.get(), wikor_id, 1.05f, 0.3f, EASE_IN_OUT_CUBIC); + text_clear(dialogue_text.get()); + text_clear(name_text.get()); + text_push(name_text.get(), "wikor", 0.0f, true); + text_push(dialogue_text.get(), "Yeah, hello to you too.", 30.0f, true); + lines++; + break; + case 4: + compositor_animation_complete_all(compositor.get()); + compositor_animate_scale(compositor.get(), wikor_id, 1.0f, 0.3f, EASE_IN_OUT_CUBIC); + compositor_animate_scale(compositor.get(), maksw_happy_id, 1.05f, 0.3f, EASE_IN_OUT_CUBIC); + text_clear(dialogue_text.get()); + text_clear(name_text.get()); + text_push(name_text.get(), "maksw", 0.0f, true); + text_push(dialogue_text.get(), "Oh Wiktor, what a nice suprise!", 30.0f, true); + lines++; + break; + case 5: + compositor_animation_complete_all(compositor.get()); + text_clear(dialogue_text.get()); + text_push(dialogue_text.get(), "Do you need something?", 30.0f, true); + lines++; + break; + case 6: + compositor_animation_complete_all(compositor.get()); + compositor_animate_scale(compositor.get(), maksw_happy_id, 1.0f, 0.3f, EASE_IN_OUT_CUBIC); + compositor_animate_scale(compositor.get(), wikor_id, 1.05f, 0.3f, EASE_IN_OUT_CUBIC); + text_clear(dialogue_text.get()); + text_clear(name_text.get()); + text_push(name_text.get(), "wikor", 0.0f, true); + text_push(dialogue_text.get(), "No, i was just walking by.", 30.0f, true); + lines++; + break; + case 7: + compositor_animation_complete_all(compositor.get()); + text_clear(dialogue_text.get()); + text_push(dialogue_text.get(), "Cool engine though.", 30.0f, true); + lines++; + break; + case 8: + // fade transition + compositor_animation_complete_all(compositor.get()); + compositor_set_visible(compositor.get(), mask_id, true); + compositor_animate_move(compositor.get(), mask_id, 0, 0, 0.5f, EASE_OUT_CUBIC, false); transitioning = true; lines++; break; - case 2: - // (line 4), fade out - compositor_animation_complete_all(compositor); - compositor_animate_scale(compositor, maksw_neutral_id, 1.0f, 0.3f, EASE_IN_OUT_CUBIC, false); - - compositor_set_visible(compositor, black_id, true); - compositor_animate_fade(compositor, black_id, 1.0f, 1.0f, EASE_IN_CUBIC, false); - + case 9: + // some fucking how? + break; + case 10: + compositor_animation_complete_all(compositor.get()); + compositor_animate_scale(compositor.get(), wikor_id, 1.0f, 0.3f, EASE_IN_OUT_CUBIC); + compositor_animate_scale(compositor.get(), maksw_neutral_id, 1.05f, 0.3f, EASE_IN_OUT_CUBIC); + text_clear(dialogue_text.get()); + text_clear(name_text.get()); + text_push(name_text.get(), "maksw", 0.0f, true); + text_push(dialogue_text.get(), "Can't complain, the winter break is just a week away. Plus the compositor is finally cooperating.", 30.0f, true); + lines++; + break; + case 11: + compositor_animation_complete_all(compositor.get()); + compositor_animate_scale(compositor.get(), maksw_neutral_id, 1.0f, 0.3f, EASE_IN_OUT_CUBIC); + compositor_animate_scale(compositor.get(), wikor_id, 1.05f, 0.3f, EASE_IN_OUT_CUBIC); + text_clear(dialogue_text.get()); + text_clear(name_text.get()); + text_push(name_text.get(), "wikor", 0.0f, true); + text_push(dialogue_text.get(), "Sure thing..", 30.0f, true); + lines++; + break; + case 12: + compositor_animation_complete_all(compositor.get()); + text_clear(dialogue_text.get()); + text_push(dialogue_text.get(), "You're talking to me like i know anything about computers.", 30.0f, true); + lines++; + break; + case 13: + // fade to black and end + compositor_animation_complete_all(compositor.get()); + compositor_animate_scale(compositor.get(), maksw_neutral_id, 1.0f, 0.3f, EASE_IN_OUT_CUBIC, false); + //compositor_set_visible(compositor.get(), black_id, true); + //compositor_animate_fade(compositor.get(), black_id, 1.0f, 0.7f, EASE_IN_CUBIC, false); + // yeah! memory savings! + compositor_set_visible(compositor.get(), mask_id, true); + compositor_set_alpha(compositor.get(), mask_id, 0.0f); + compositor_animate_fade(compositor.get(), mask_id, 1.0f, 0.8f, EASE_IN_OUT_CUBIC, false); dialog_finished = true; lines++; break; @@ -207,58 +302,43 @@ int main() { } } - compositor_tick(compositor, delta_time_sec); + compositor_tick(compositor.get(), delta_time_sec); if (transitioning) { - if (compositor_is_element_animating(compositor, mask_id) == false) { - compositor_set_visible(compositor, maksw_happy_id, false); - compositor_set_visible(compositor, maksw_neutral_id, true); - compositor_set_scale(compositor, maksw_neutral_id, 1.05f); - text_clear(dialogue_text); - text_clear(name_text); - text_push(name_text, "maksw", 0.0f, true); - text_push(dialogue_text, "why so.. serious?", 30.0f, true); + if (!compositor_is_element_animating(compositor.get(), mask_id)) { + compositor_set_visible(compositor.get(), maksw_happy_id, false); + compositor_set_visible(compositor.get(), maksw_neutral_id, true); + compositor_set_scale(compositor.get(), maksw_neutral_id, 1.0f); + compositor_set_scale(compositor.get(), wikor_id, 1.05f); + text_clear(dialogue_text.get()); + text_clear(name_text.get()); + text_push(name_text.get(), "wikor", 0.0f, true); + text_push(dialogue_text.get(), "Anyways, how's your day going?", 30.0f, true); - compositor_set_visible(compositor, mask_id, false); - compositor_set_visible(compositor, mask2_id, true); - compositor_animate_move(compositor, mask2_id, 0, HEIGHT, 0.5f, EASE_OUT_CUBIC, false); + compositor_set_visible(compositor.get(), mask_id, false); + compositor_set_visible(compositor.get(), mask2_id, true); + compositor_animate_move(compositor.get(), mask2_id, 0, HEIGHT, 0.5f, EASE_OUT_CUBIC, false); transitioning = false; + lines++; } } if (dialog_finished) { - compositor_mark_content_dirty(compositor, black_id); - if (compositor_is_element_animating(compositor, black_id) == false) { + compositor_mark_content_dirty(compositor.get(), mask_id); + if (!compositor_is_element_animating(compositor.get(), mask_id)) { running = false; } } - if (text_nextframe(name_text, delta_time_sec)) - compositor_mark_content_dirty(compositor, name_id); - if (text_nextframe(dialogue_text, delta_time_sec)) - compositor_mark_content_dirty(compositor, dialogue_id); + if (text_nextframe(name_text.get(), delta_time_sec)) + compositor_mark_content_dirty(compositor.get(), name_id); + if (text_nextframe(dialogue_text.get(), delta_time_sec)) + compositor_mark_content_dirty(compositor.get(), dialogue_id); - const uint32_t* fb = compositor_compose(compositor); - window_blit(window, fb); + const uint32_t* fb = compositor_compose(compositor.get()); + window_blit(window.get(), fb); } - // no raii because i'm tired already - if (maksw_happy) image_free(maksw_happy); - if (maksw_neutral) image_free(maksw_neutral); - if (wikor) image_free(wikor); - - if (black) free(black); - if (mask) image_free(mask); - if (mask2) image_free(mask2); - - if (bg) image_free(bg); - if (textbox) image_free(textbox); - - text_destroy(name_text); - text_destroy(dialogue_text); - compositor_destroy(compositor); - window_destroy(window); - return 0; -} \ No newline at end of file +} diff --git a/vn.vcxproj b/vn.vcxproj index f39bfb6..e41ae19 100644 --- a/vn.vcxproj +++ b/vn.vcxproj @@ -102,6 +102,7 @@ MultiThreadedDebug StreamingSIMDExtensions2 /Zc:char8_t- %(AdditionalOptions) + false Console @@ -144,6 +145,7 @@ true StreamingSIMDExtensions2 /Zc:char8_t- %(AdditionalOptions) + false Console