#include #include #include #include #include "pc/controller/controller_sdl.h" #include "pc/controller/controller_mouse.h" #include "pc/gfx/gfx_pc.h" #include "pc/gfx/gfx_window_manager_api.h" #include "pc/pc_main.h" #include "pc/utils/misc.h" #include "djui_gfx.h" #include "gfx_dimensions.h" #include "config.h" #include "djui.h" #include "djui_unicode.h" #include "djui_hud_utils.h" #include "djui_panel_pause.h" #include "game/camera.h" #include "game/hud.h" #include "game/rendering_graph_node.h" #include "engine/math_util.h" static enum HudUtilsResolution sResolution = RESOLUTION_DJUI; static enum HudUtilsFilter sFilter = FILTER_NEAREST; static enum DjuiFontType sFont = FONT_NORMAL; static struct HudUtilsRotation sRotation = { 0, 0, 0 }; static struct DjuiColor sColor = { 255, 255, 255, 255 }; static struct DjuiColor sRefColor = { 255, 255, 255, 255 }; static bool sLegacy = false; f32 gDjuiHudUtilsZ = 0; u8 gDjuiHudLockMouse = false; extern ALIGNED8 const u8 texture_hud_char_camera[]; extern ALIGNED8 const u8 texture_hud_char_lakitu[]; extern ALIGNED8 const u8 texture_hud_char_no_camera[]; extern ALIGNED8 const u8 texture_hud_char_arrow_up[]; extern ALIGNED8 const u8 texture_hud_char_arrow_down[]; extern ALIGNED8 const u8 texture_hud_char_coin[]; extern ALIGNED8 const u8 texture_hud_char_star[]; extern ALIGNED8 const u8 texture_hud_char_mario_head[]; extern ALIGNED8 const u8 texture_hud_char_luigi_head[]; extern ALIGNED8 const u8 texture_hud_char_toad_head[]; extern ALIGNED8 const u8 texture_hud_char_waluigi_head[]; extern ALIGNED8 const u8 texture_hud_char_wario_head[]; extern ALIGNED8 const u8 texture_hud_char_apostrophe[]; extern ALIGNED8 const u8 texture_hud_char_double_quote[]; struct GlobalTextures gGlobalTextures = { .camera = { .texture = (u8*)texture_hud_char_camera, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_camera" }, .lakitu = { .texture = (u8*)texture_hud_char_lakitu, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_lakitu" }, .no_camera = { .texture = (u8*)texture_hud_char_no_camera, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_no_camera" }, .arrow_up = { .texture = (u8*)texture_hud_char_arrow_up, .bitSize = 8, .width = 8, .height = 8, "texture_hud_char_arrow_up" }, .arrow_down = { .texture = (u8*)texture_hud_char_arrow_down, .bitSize = 8, .width = 8, .height = 8, "texture_hud_char_arrow_down" }, .coin = { .texture = (u8*)texture_hud_char_coin, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_coin" }, .star = { .texture = (u8*)texture_hud_char_star, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_star" }, .apostrophe = { .texture = (u8*)texture_hud_char_apostrophe, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_apostrophe" }, .double_quote = { .texture = (u8*)texture_hud_char_double_quote, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_double_quote" }, .mario_head = { .texture = (u8*)texture_hud_char_mario_head, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_mario_head" }, .luigi_head = { .texture = (u8*)texture_hud_char_luigi_head, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_luigi_head" }, .toad_head = { .texture = (u8*)texture_hud_char_toad_head, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_toad_head" }, .waluigi_head = { .texture = (u8*)texture_hud_char_waluigi_head, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_waluigi_head" }, .wario_head = { .texture = (u8*)texture_hud_char_wario_head, .bitSize = 8, .width = 16, .height = 16, "texture_hud_char_wario_head" } }; static void djui_hud_position_translate(f32* x, f32* y) { if (sResolution == RESOLUTION_DJUI) { djui_gfx_position_translate(x, y); } else { *x = gfx_dimensions_rect_from_left_edge(0) + *x; *y = SCREEN_HEIGHT - *y; } } static void djui_hud_size_translate(f32* size) { if (sResolution == RESOLUTION_DJUI) { djui_gfx_size_translate(size); } } //////////// // interp // //////////// #define MAX_INTERP_HUD 512 struct InterpHud { Gfx* headPos; f32 z; f32 prevX; f32 prevY; f32 x; f32 y; f32 prevScaleW; f32 prevScaleH; f32 scaleW; f32 scaleH; f32 width; f32 height; enum HudUtilsResolution resolution; }; static struct InterpHud sInterpHuds[MAX_INTERP_HUD] = { 0 }; static u16 sInterpHudCount = 0; static u8 sColorAltered = FALSE; void patch_djui_hud_before(void) { sInterpHudCount = 0; } void patch_djui_hud(f32 delta) { f32 savedZ = gDjuiHudUtilsZ; Gfx* savedHeadPos = gDisplayListHead; enum HudUtilsResolution savedResolution = sResolution; for (u16 i = 0; i < sInterpHudCount; i++) { struct InterpHud* interp = &sInterpHuds[i]; f32 x = delta_interpolate_f32(interp->prevX, interp->x, delta); f32 y = delta_interpolate_f32(interp->prevY, interp->y, delta); f32 scaleW = delta_interpolate_f32(interp->prevScaleW, interp->scaleW, delta); f32 scaleH = delta_interpolate_f32(interp->prevScaleH, interp->scaleH, delta); sResolution = interp->resolution; gDjuiHudUtilsZ = interp->z; gDisplayListHead = interp->headPos; // translate position f32 translatedX = x; f32 translatedY = y; djui_hud_position_translate(&translatedX, &translatedY); create_dl_translation_matrix(DJUI_MTX_PUSH, translatedX, translatedY, gDjuiHudUtilsZ); // translate scale f32 translatedW = scaleW; f32 translatedH = scaleH; djui_hud_size_translate(&translatedW); djui_hud_size_translate(&translatedH); create_dl_scale_matrix(DJUI_MTX_NOPUSH, interp->width * translatedW, interp->height * translatedH, 1.0f); } sResolution = savedResolution; gDisplayListHead = savedHeadPos; gDjuiHudUtilsZ = savedZ; } //////////// // others // //////////// u8 djui_hud_get_resolution(void) { return sResolution; } void djui_hud_set_resolution(enum HudUtilsResolution resolutionType) { if (resolutionType >= RESOLUTION_COUNT) { return; } sResolution = resolutionType; } u8 djui_hud_get_filter(void) { return sFilter; } void djui_hud_set_filter(enum HudUtilsFilter filterType) { if (filterType >= FILTER_COUNT) { return; } sFilter = filterType; } u8 djui_hud_get_font(void) { return sFont; } void djui_hud_set_font(s8 fontType) { if (fontType >= FONT_COUNT) { return; } sLegacy = fontType == -1; if (sLegacy) { fontType = 0; } sFont = fontType; } struct DjuiColor* djui_hud_get_color(void) { sRefColor.r = sColor.r; sRefColor.g = sColor.g; sRefColor.b = sColor.b; sRefColor.a = sColor.a; return &sRefColor; } void djui_hud_set_color(u8 r, u8 g, u8 b, u8 a) { sColor.r = r; sColor.g = g; sColor.b = b; sColor.a = a; sColorAltered = TRUE; gDPSetEnvColor(gDisplayListHead++, r, g, b, a); } void djui_hud_reset_color(void) { if (sColorAltered) { sColor.r = 255; sColor.g = 255; sColor.b = 255; sColor.a = 255; sColorAltered = FALSE; gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); } } struct HudUtilsRotation* djui_hud_get_rotation(void) { return &sRotation; } void djui_hud_set_rotation(s16 rotation, f32 pivotX, f32 pivotY) { sRotation.rotation = (rotation * 180.f) / 0x8000; sRotation.pivotX = pivotX; sRotation.pivotY = pivotY; } u32 djui_hud_get_screen_width(void) { u32 windowWidth, windowHeight; wm_api->get_dimensions(&windowWidth, &windowHeight); if (use_forced_4by3() && sResolution == RESOLUTION_DJUI) { windowWidth -= (GFX_DIMENSIONS_FROM_LEFT_EDGE(0) + GFX_DIMENSIONS_FROM_RIGHT_EDGE(0)); } if (sResolution == RESOLUTION_N64) { f32 aspect = use_forced_4by3() ? (4.0f / 3.0f) : GFX_DIMENSIONS_ASPECT_RATIO; return (aspect) * SCREEN_HEIGHT; } else { return (windowWidth / djui_gfx_get_scale()); } } u32 djui_hud_get_screen_height(void) { u32 windowWidth, windowHeight; wm_api->get_dimensions(&windowWidth, &windowHeight); return (sResolution == RESOLUTION_N64) ? SCREEN_HEIGHT : (windowHeight / djui_gfx_get_scale()); } f32 djui_hud_get_mouse_x(void) { #ifdef HAVE_SDL2 controller_sdl_read_mouse_window(); #endif return mouse_window_x / djui_gfx_get_scale(); } f32 djui_hud_get_mouse_y(void) { #ifdef HAVE_SDL2 controller_sdl_read_mouse_window(); #endif return mouse_window_y / djui_gfx_get_scale(); } f32 djui_hud_get_raw_mouse_x(void) { return mouse_x; } f32 djui_hud_get_raw_mouse_y(void) { return mouse_y; } void djui_hud_set_mouse_locked(bool locked) { gDjuiHudLockMouse = locked; } f32 djui_hud_measure_text(const char* message) { if (message == NULL) { return 0; } const struct DjuiFont* font = gDjuiFonts[sFont]; f32 width = 0; const char* c = message; while(*c != '\0') { width += font->char_width((char*)c) * (sLegacy ? 0.5f : 1.0f); c = djui_unicode_next_char((char*)c); } return width * font->defaultFontScale; } void djui_hud_print_text(const char* message, f32 x, f32 y, f32 scale) { if (message == NULL) { return; } gDjuiHudUtilsZ += 0.01f; if (sLegacy) { scale *= 0.5f; } const struct DjuiFont* font = gDjuiFonts[sFont]; f32 fontScale = font->defaultFontScale * scale; // setup display list if (font->textBeginDisplayList != NULL) { gSPDisplayList(gDisplayListHead++, font->textBeginDisplayList); } // translate position f32 translatedX = x; f32 translatedY = y; djui_hud_position_translate(&translatedX, &translatedY); create_dl_translation_matrix(DJUI_MTX_PUSH, translatedX, translatedY, gDjuiHudUtilsZ); // compute font size f32 translatedFontSize = fontScale; djui_hud_size_translate(&translatedFontSize); create_dl_scale_matrix(DJUI_MTX_NOPUSH, translatedFontSize, translatedFontSize, 1.0f); // render the line f32 addX = 0; char* c = (char*)message; while (*c != '\0') { f32 charWidth = font->char_width(c); if (*c == '\n' && *c == ' ') { addX += charWidth; c++; continue; } // render font->render_char(c); create_dl_translation_matrix(DJUI_MTX_NOPUSH, charWidth + addX, 0, 0); addX = 0; c = djui_unicode_next_char(c); } // pop gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); } void djui_hud_print_text_interpolated(const char* message, f32 prevX, f32 prevY, f32 prevScale, f32 x, f32 y, f32 scale) { if (message == NULL) { return; } f32 savedZ = gDjuiHudUtilsZ; gDjuiHudUtilsZ += 0.01f; if (sLegacy) { prevScale *= 0.5f; scale *= 0.5f; } const struct DjuiFont* font = gDjuiFonts[sFont]; f32 fontScale = font->defaultFontScale * scale; // setup display list if (font->textBeginDisplayList != NULL) { gSPDisplayList(gDisplayListHead++, font->textBeginDisplayList); } Gfx* savedHeadPos = gDisplayListHead; // translate position f32 translatedX = x; f32 translatedY = y; djui_hud_position_translate(&translatedX, &translatedY); create_dl_translation_matrix(DJUI_MTX_PUSH, translatedX, translatedY, gDjuiHudUtilsZ); // compute font size f32 translatedFontSize = fontScale; djui_hud_size_translate(&translatedFontSize); create_dl_scale_matrix(DJUI_MTX_NOPUSH, translatedFontSize, translatedFontSize, 1.0f); // render the line f32 addX = 0; char* c = (char*)message; while (*c != '\0') { f32 charWidth = font->char_width(c); if (*c == '\n' && *c == ' ') { addX += charWidth; c++; continue; } // render font->render_char(c); create_dl_translation_matrix(DJUI_MTX_NOPUSH, charWidth + addX, 0, 0); addX = 0; c = djui_unicode_next_char(c); } // pop gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); if (sInterpHudCount >= MAX_INTERP_HUD) { return; } struct InterpHud* interp = &sInterpHuds[sInterpHudCount++]; interp->headPos = savedHeadPos; interp->prevX = prevX; interp->prevY = prevY; interp->prevScaleW = prevScale; interp->prevScaleH = prevScale; interp->x = x; interp->y = y; interp->scaleW = scale; interp->scaleH = scale; interp->width = font->defaultFontScale; interp->height = font->defaultFontScale; interp->z = savedZ; interp->resolution = sResolution; } void djui_hud_render_texture_raw(const u8* texture, u32 bitSize, u32 width, u32 height, f32 x, f32 y, f32 scaleW, f32 scaleH) { gDjuiHudUtilsZ += 0.01f; // translate position f32 translatedX = x; f32 translatedY = y; djui_hud_position_translate(&translatedX, &translatedY); create_dl_translation_matrix(DJUI_MTX_PUSH, translatedX, translatedY, gDjuiHudUtilsZ); // rotate f32 translatedW = scaleW; f32 translatedH = scaleH; djui_hud_size_translate(&translatedW); djui_hud_size_translate(&translatedH); if (sRotation.rotation != 0) { f32 pivotTranslationX = width * translatedW * sRotation.pivotX; f32 pivotTranslationY = height * translatedH * sRotation.pivotY; create_dl_translation_matrix(DJUI_MTX_NOPUSH, +pivotTranslationX, -pivotTranslationY, 0); create_dl_rotation_matrix(DJUI_MTX_NOPUSH, sRotation.rotation, 0, 0, 1); create_dl_translation_matrix(DJUI_MTX_NOPUSH, -pivotTranslationX, +pivotTranslationY, 0); } // translate scale create_dl_scale_matrix(DJUI_MTX_NOPUSH, width * translatedW, height * translatedH, 1.0f); // render djui_gfx_render_texture(texture, width, height, bitSize, sFilter); // pop gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); } void djui_hud_render_texture_tile_raw(const u8* texture, u32 bitSize, u32 width, u32 height, f32 x, f32 y, f32 scaleW, f32 scaleH, u32 tileX, u32 tileY, u32 tileW, u32 tileH) { gDjuiHudUtilsZ += 0.01f; scaleW *= (f32) tileW / (f32) width; scaleH *= (f32) tileH / (f32) height; // translate position f32 translatedX = x; f32 translatedY = y; djui_hud_position_translate(&translatedX, &translatedY); create_dl_translation_matrix(DJUI_MTX_PUSH, translatedX, translatedY, gDjuiHudUtilsZ); // rotate f32 translatedW = scaleW; f32 translatedH = scaleH; djui_hud_size_translate(&translatedW); djui_hud_size_translate(&translatedH); if (sRotation.rotation != 0) { f32 aspect = tileH ? ((f32) tileW / (f32) tileH) : 1.f; f32 pivotTranslationX = width * translatedW * aspect * sRotation.pivotX; f32 pivotTranslationY = height * translatedH * sRotation.pivotY; create_dl_translation_matrix(DJUI_MTX_NOPUSH, +pivotTranslationX, -pivotTranslationY, 0); create_dl_rotation_matrix(DJUI_MTX_NOPUSH, sRotation.rotation, 0, 0, 1); create_dl_translation_matrix(DJUI_MTX_NOPUSH, -pivotTranslationX, +pivotTranslationY, 0); } // translate scale create_dl_scale_matrix(DJUI_MTX_NOPUSH, width * translatedW, height * translatedH, 1.0f); // render djui_gfx_render_texture_tile(texture, width, height, bitSize, tileX, tileY, tileW, tileH, sFilter); // pop gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); } void djui_hud_render_texture(struct TextureInfo* texInfo, f32 x, f32 y, f32 scaleW, f32 scaleH) { djui_hud_render_texture_raw(texInfo->texture, texInfo->bitSize, texInfo->width, texInfo->height, x, y, scaleW, scaleH); } void djui_hud_render_texture_tile(struct TextureInfo* texInfo, f32 x, f32 y, f32 scaleW, f32 scaleH, u32 tileX, u32 tileY, u32 tileW, u32 tileH) { djui_hud_render_texture_tile_raw(texInfo->texture, texInfo->bitSize, texInfo->width, texInfo->height, x, y, scaleW, scaleH, tileX, tileY, tileW, tileH); } void djui_hud_render_texture_interpolated(struct TextureInfo* texInfo, f32 prevX, f32 prevY, f32 prevScaleW, f32 prevScaleH, f32 x, f32 y, f32 scaleW, f32 scaleH) { Gfx* savedHeadPos = gDisplayListHead; f32 savedZ = gDjuiHudUtilsZ; djui_hud_render_texture_raw(texInfo->texture, texInfo->bitSize, texInfo->width, texInfo->height, prevX, prevY, prevScaleW, prevScaleH); if (sInterpHudCount >= MAX_INTERP_HUD) { return; } struct InterpHud* interp = &sInterpHuds[sInterpHudCount++]; interp->headPos = savedHeadPos; interp->prevX = prevX; interp->prevY = prevY; interp->prevScaleW = prevScaleW; interp->prevScaleH = prevScaleH; interp->x = x; interp->y = y; interp->scaleW = scaleW; interp->scaleH = scaleH; interp->width = texInfo->width; interp->height = texInfo->height; interp->z = savedZ; interp->resolution = sResolution; } void djui_hud_render_texture_tile_interpolated(struct TextureInfo* texInfo, f32 prevX, f32 prevY, f32 prevScaleW, f32 prevScaleH, f32 x, f32 y, f32 scaleW, f32 scaleH, u32 tileX, u32 tileY, u32 tileW, u32 tileH) { Gfx* savedHeadPos = gDisplayListHead; f32 savedZ = gDjuiHudUtilsZ; djui_hud_render_texture_tile_raw(texInfo->texture, texInfo->bitSize, texInfo->width, texInfo->height, prevX, prevY, prevScaleW, prevScaleH, tileX, tileY, tileW, tileH); if (sInterpHudCount >= MAX_INTERP_HUD) { return; } struct InterpHud* interp = &sInterpHuds[sInterpHudCount++]; interp->headPos = savedHeadPos; interp->prevX = prevX; interp->prevY = prevY; interp->prevScaleW = prevScaleW; interp->prevScaleH = prevScaleH; interp->x = x; interp->y = y; interp->scaleW = scaleW; interp->scaleH = scaleH; interp->width = texInfo->width; interp->height = texInfo->height; interp->z = savedZ; interp->resolution = sResolution; } void djui_hud_render_rect(f32 x, f32 y, f32 width, f32 height) { gDjuiHudUtilsZ += 0.01f; // translate position f32 translatedX = x; f32 translatedY = y; djui_hud_position_translate(&translatedX, &translatedY); create_dl_translation_matrix(DJUI_MTX_PUSH, translatedX, translatedY, gDjuiHudUtilsZ); // rotate f32 translatedW = width; f32 translatedH = height; djui_hud_size_translate(&translatedW); djui_hud_size_translate(&translatedH); if (sRotation.rotation != 0) { f32 pivotTranslationX = translatedW * sRotation.pivotX; f32 pivotTranslationY = translatedH * sRotation.pivotY; create_dl_translation_matrix(DJUI_MTX_NOPUSH, +pivotTranslationX, -pivotTranslationY, 0); create_dl_rotation_matrix(DJUI_MTX_NOPUSH, sRotation.rotation, 0, 0, 1); create_dl_translation_matrix(DJUI_MTX_NOPUSH, -pivotTranslationX, +pivotTranslationY, 0); } // translate scale create_dl_scale_matrix(DJUI_MTX_NOPUSH, translatedW, translatedH, 1.0f); // render gSPDisplayList(gDisplayListHead++, dl_djui_simple_rect); // pop gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); } void djui_hud_render_rect_interpolated(f32 prevX, f32 prevY, f32 prevWidth, f32 prevHeight, f32 x, f32 y, f32 width, f32 height) { Gfx* savedHeadPos = gDisplayListHead; f32 savedZ = gDjuiHudUtilsZ; djui_hud_render_rect(prevX, prevY, prevWidth, prevHeight); if (sInterpHudCount >= MAX_INTERP_HUD) { return; } struct InterpHud* interp = &sInterpHuds[sInterpHudCount++]; interp->headPos = savedHeadPos; interp->prevX = prevX; interp->prevY = prevY; interp->prevScaleW = prevWidth; interp->prevScaleH = prevHeight; interp->x = x; interp->y = y; interp->scaleW = width; interp->scaleH = height; interp->width = 1; interp->height = 1; interp->z = savedZ; interp->resolution = sResolution; } static void hud_rotate_and_translate_vec3f(Vec3f vec, Mat4* mtx, Vec3f out) { out[0] = (*mtx)[0][0] * vec[0] + (*mtx)[1][0] * vec[1] + (*mtx)[2][0] * vec[2]; out[1] = (*mtx)[0][1] * vec[0] + (*mtx)[1][1] * vec[1] + (*mtx)[2][1] * vec[2]; out[2] = (*mtx)[0][2] * vec[0] + (*mtx)[1][2] * vec[1] + (*mtx)[2][2] * vec[2]; out[0] += (*mtx)[3][0]; out[1] += (*mtx)[3][1]; out[2] += (*mtx)[3][2]; } bool djui_hud_world_pos_to_screen_pos(Vec3f pos, Vec3f out) { if (!gCamera) { return false; } hud_rotate_and_translate_vec3f(pos, &gCamera->mtx, out); if (out[2] >= 0.0f) { return false; } out[0] *= 256.0f / -out[2]; out[1] *= 256.0f / out[2]; // fov of 45.0 is the default fov f32 fovDefault = tanf(not_zero(45.0f, gOverrideFOV) * ((f32)M_PI / 360.0f)); f32 fovCurrent = tanf((gFOVState.fov + gFOVState.fovOffset) * ((f32)M_PI / 360.0f)); f32 fovDifference = (fovDefault / fovCurrent) * 1.13f; out[0] *= fovDifference; out[1] *= fovDifference; out[0] += djui_hud_get_screen_width() / 2.0f; out[1] += djui_hud_get_screen_height() / 2.0f; return true; } bool djui_hud_is_pause_menu_created(void) { return gDjuiPanelPauseCreated; } void djui_open_pause_menu(void) { djui_panel_pause_create(NULL); }