Initial DJUI commit

The birth of a custom user interface system.

Has the ability to draw things to the screen at native resolution
regardless of window size.

Components can be nested within one another to an arbitrary depth.

Text rendering is completely rewritten.

Text and images can be clipped by their parent container.
This commit is contained in:
MysterD 2021-06-18 15:23:59 -07:00
parent 52d07c4dde
commit 038c1135b3
27 changed files with 1370 additions and 21 deletions

View file

@ -316,7 +316,7 @@ LEVEL_DIRS := $(patsubst levels/%,%,$(dir $(wildcard levels/*/header.h)))
# Hi, I'm a PC # Hi, I'm a PC
SRC_DIRS := src src/engine src/game src/audio src/menu src/buffers actors levels bin data assets src/pc src/pc/gfx src/pc/audio src/pc/controller src/pc/fs src/pc/fs/packtypes SRC_DIRS := src src/engine src/game src/audio src/menu src/buffers actors levels bin data assets src/pc src/pc/gfx src/pc/audio src/pc/controller src/pc/fs src/pc/fs/packtypes
SRC_DIRS += src/pc/network src/pc/network/packets src/pc/network/socket src/pc/utils SRC_DIRS += src/pc/network src/pc/network/packets src/pc/network/socket src/pc/utils src/pc/djui
ASM_DIRS := ASM_DIRS :=
#ifeq ($(DISCORDRPC),1) #ifeq ($(DISCORDRPC),1)

View file

@ -3940,6 +3940,14 @@
<ClCompile Include="..\src\pc\controller\controller_recorded_tas.c" /> <ClCompile Include="..\src\pc\controller\controller_recorded_tas.c" />
<ClCompile Include="..\src\pc\controller\controller_sdl.c" /> <ClCompile Include="..\src\pc\controller\controller_sdl.c" />
<ClCompile Include="..\src\pc\discord\discordrpc.c" /> <ClCompile Include="..\src\pc\discord\discordrpc.c" />
<ClCompile Include="..\src\pc\djui\djui.c" />
<ClCompile Include="..\src\pc\djui\djui_base.c" />
<ClCompile Include="..\src\pc\djui\djui_button.c" />
<ClCompile Include="..\src\pc\djui\djui_gfx.c" />
<ClCompile Include="..\src\pc\djui\djui_image.c" />
<ClCompile Include="..\src\pc\djui\djui_rect.c" />
<ClCompile Include="..\src\pc\djui\djui_root.c" />
<ClCompile Include="..\src\pc\djui\djui_text.c" />
<ClCompile Include="..\src\pc\fs\dirtree.c" /> <ClCompile Include="..\src\pc\fs\dirtree.c" />
<ClCompile Include="..\src\pc\fs\fs.c" /> <ClCompile Include="..\src\pc\fs\fs.c" />
<ClCompile Include="..\src\pc\fs\fs_packtype_dir.c" /> <ClCompile Include="..\src\pc\fs\fs_packtype_dir.c" />
@ -4351,6 +4359,16 @@
<ClInclude Include="..\src\menu\custom_menu_system.h" /> <ClInclude Include="..\src\menu\custom_menu_system.h" />
<ClInclude Include="..\src\pc\controller\controller_keyboard_debug.h" /> <ClInclude Include="..\src\pc\controller\controller_keyboard_debug.h" />
<ClInclude Include="..\src\pc\debuglog.h" /> <ClInclude Include="..\src\pc\debuglog.h" />
<ClInclude Include="..\src\pc\djui\djui.h" />
<ClInclude Include="..\src\pc\djui\djui_base.h" />
<ClInclude Include="..\src\pc\djui\djui_button.h" />
<ClInclude Include="..\src\pc\djui\djui_gbi.h" />
<ClInclude Include="..\src\pc\djui\djui_gfx.h" />
<ClInclude Include="..\src\pc\djui\djui_image.h" />
<ClInclude Include="..\src\pc\djui\djui_rect.h" />
<ClInclude Include="..\src\pc\djui\djui_root.h" />
<ClInclude Include="..\src\pc\djui\djui_text.h" />
<ClInclude Include="..\src\pc\djui\djui_types.h" />
<ClInclude Include="..\src\pc\network\branch.h" /> <ClInclude Include="..\src\pc\network\branch.h" />
<ClInclude Include="..\src\pc\network\discord\activity.h" /> <ClInclude Include="..\src\pc\network\discord\activity.h" />
<ClInclude Include="..\src\pc\network\discord\discord.h" /> <ClInclude Include="..\src\pc\network\discord\discord.h" />

View file

@ -3445,6 +3445,12 @@
<Filter Include="Source Files\src\pc\network\packets\reservation-area"> <Filter Include="Source Files\src\pc\network\packets\reservation-area">
<UniqueIdentifier>{9ddfaa87-399e-4f61-aae3-f91af79e14cc}</UniqueIdentifier> <UniqueIdentifier>{9ddfaa87-399e-4f61-aae3-f91af79e14cc}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="Source Files\src\pc\djui">
<UniqueIdentifier>{0e1e4798-796e-4801-be6e-69d0c3b05ad7}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\src\pc\djui\components">
<UniqueIdentifier>{471ac819-ed6b-4a33-8952-50a8e0d2d839}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\actors\amp\anims\anim_0800401C.inc.c"> <ClCompile Include="..\actors\amp\anims\anim_0800401C.inc.c">
@ -15144,6 +15150,30 @@
<ClCompile Include="..\src\pc\network\packets\packet_save_set_flag.c"> <ClCompile Include="..\src\pc\network\packets\packet_save_set_flag.c">
<Filter>Source Files\src\pc\network\packets</Filter> <Filter>Source Files\src\pc\network\packets</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\src\pc\djui\djui.c">
<Filter>Source Files\src\pc\djui</Filter>
</ClCompile>
<ClCompile Include="..\src\pc\djui\djui_gfx.c">
<Filter>Source Files\src\pc\djui</Filter>
</ClCompile>
<ClCompile Include="..\src\pc\djui\djui_image.c">
<Filter>Source Files\src\pc\djui\components</Filter>
</ClCompile>
<ClCompile Include="..\src\pc\djui\djui_rect.c">
<Filter>Source Files\src\pc\djui\components</Filter>
</ClCompile>
<ClCompile Include="..\src\pc\djui\djui_text.c">
<Filter>Source Files\src\pc\djui\components</Filter>
</ClCompile>
<ClCompile Include="..\src\pc\djui\djui_base.c">
<Filter>Source Files\src\pc\djui\components</Filter>
</ClCompile>
<ClCompile Include="..\src\pc\djui\djui_root.c">
<Filter>Source Files\src\pc\djui\components</Filter>
</ClCompile>
<ClCompile Include="..\src\pc\djui\djui_button.c">
<Filter>Source Files\src\pc\djui\components</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\actors\common0.h"> <ClCompile Include="..\actors\common0.h">
@ -16117,5 +16147,35 @@
<ClInclude Include="..\src\pc\network\reservation_area.h"> <ClInclude Include="..\src\pc\network\reservation_area.h">
<Filter>Header Files\src\pc\network</Filter> <Filter>Header Files\src\pc\network</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\src\pc\djui\djui.h">
<Filter>Source Files\src\pc\djui</Filter>
</ClInclude>
<ClInclude Include="..\src\pc\djui\djui_gfx.h">
<Filter>Source Files\src\pc\djui</Filter>
</ClInclude>
<ClInclude Include="..\src\pc\djui\djui_types.h">
<Filter>Source Files\src\pc\djui</Filter>
</ClInclude>
<ClInclude Include="..\src\pc\djui\djui_gbi.h">
<Filter>Source Files\src\pc\djui</Filter>
</ClInclude>
<ClInclude Include="..\src\pc\djui\djui_image.h">
<Filter>Source Files\src\pc\djui\components</Filter>
</ClInclude>
<ClInclude Include="..\src\pc\djui\djui_rect.h">
<Filter>Source Files\src\pc\djui\components</Filter>
</ClInclude>
<ClInclude Include="..\src\pc\djui\djui_text.h">
<Filter>Source Files\src\pc\djui\components</Filter>
</ClInclude>
<ClInclude Include="..\src\pc\djui\djui_base.h">
<Filter>Source Files\src\pc\djui\components</Filter>
</ClInclude>
<ClInclude Include="..\src\pc\djui\djui_root.h">
<Filter>Source Files\src\pc\djui\components</Filter>
</ClInclude>
<ClInclude Include="..\src\pc\djui\djui_button.h">
<Filter>Source Files\src\pc\djui\components</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -1,2 +1,4 @@
#!/bin/bash #!/bin/bash
make BETTERCAMERA=1 NODRAWINGDISTANCE=1 DEBUG=1 IMMEDIATELOAD=1 DEVELOPMENT=1 STRICT=1 && winpty cgdb ./build/us_pc/sm64.us.f3dex2e.exe -ex 'break debug_breakpoint_here' -ex 'run' -ex 'quit' #make BETTERCAMERA=1 NODRAWINGDISTANCE=1 DEBUG=1 IMMEDIATELOAD=1 DEVELOPMENT=1 STRICT=1 && winpty cgdb ./build/us_pc/sm64.us.f3dex2e.exe -ex 'break debug_breakpoint_here' -ex 'run' -ex 'quit'
#make BETTERCAMERA=1 NODRAWINGDISTANCE=1 DEBUG=1 IMMEDIATELOAD=1 DEVELOPMENT=1 STRICT=1 && winpty cgdb ./build/us_pc/sm64.us.f3dex2e.exe -ex 'break debug_breakpoint_here' -ex 'run --server 27015 --configfile sm64config_server.txt' -ex 'quit'
make BETTERCAMERA=1 NODRAWINGDISTANCE=1 DEBUG=1 IMMEDIATELOAD=1 DEVELOPMENT=1 STRICT=1 && ./build/us_pc/sm64.us.f3dex2e.exe --server 27015 --configfile sm64config_server.txt

View file

@ -1,2 +1,2 @@
#!/bin/bash #!/bin/bash
winpty cgdb ./build/us_pc/sm64.us.f3dex2e.exe -ex 'break debug_breakpoint_here' make BETTERCAMERA=1 NODRAWINGDISTANCE=1 DEBUG=1 IMMEDIATELOAD=1 DEVELOPMENT=1 STRICT=1 && winpty cgdb ./build/us_pc/sm64.us.f3dex2e.exe -ex 'break debug_breakpoint_here'

View file

@ -21,6 +21,7 @@
#define _GBI_H_ #define _GBI_H_
#include <PR/ultratypes.h> #include <PR/ultratypes.h>
#include "src/pc/djui/djui_gbi.h"
/* /*
* To use the F3DEX ucodes, define F3DEX_GBI before include this file. * To use the F3DEX ucodes, define F3DEX_GBI before include this file.

View file

@ -26,6 +26,7 @@
#include "macros.h" #include "macros.h"
#include "pc/cheats.h" #include "pc/cheats.h"
#include "pc/network/network.h" #include "pc/network/network.h"
#include "pc/djui/djui.h"
#ifdef BETTERCAMERA #ifdef BETTERCAMERA
#include "bettercamera.h" #include "bettercamera.h"
#endif #endif
@ -414,26 +415,31 @@ void render_multi_text_string(s16 *xPos, s16 *yPos, s8 multiTextID)
} }
#endif #endif
u8 str_ascii_char_to_dialog(char c) {
switch (c) {
case '\'': return 0x3E;
case '.': return 0x3F;
case ',': return DIALOG_CHAR_COMMA;
case '-': return 0x9F;
case '(': return 0xE1;
case ')': return 0xE3;
case '&': return 0xE5;
case '!': return 0xF2;
case '%': return 0xF3;
case '?': return 0xF4;
case '"': return 0xF6; // 0xF5 is opening quote
case '~': return 0xF7;
case '*': return 0xFB;
case ' ': return DIALOG_CHAR_SPACE;
case '\n': return DIALOG_CHAR_NEWLINE;
case '\0': return DIALOG_CHAR_TERMINATOR;
default: return ((u8)c < 0xF0) ? ASCII_TO_DIALOG(c) : c;
}
}
void str_ascii_to_dialog(const char* string, u8* dialog, u16 length) { void str_ascii_to_dialog(const char* string, u8* dialog, u16 length) {
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
switch (string[i]) { dialog[i] = str_ascii_char_to_dialog(string[i]);
case '\'': dialog[i] = 0x3E; break;
case '.': dialog[i] = 0x3F; break;
case ',': dialog[i] = DIALOG_CHAR_COMMA; break;
case '-': dialog[i] = 0x9F; break;
case '(': dialog[i] = 0xE1; break;
case ')': dialog[i] = 0xE3; break;
case '&': dialog[i] = 0xE5; break;
case '!': dialog[i] = 0xF2; break;
case '%': dialog[i] = 0xF3; break;
case '?': dialog[i] = 0xF4; break;
case '"': dialog[i] = 0xF6; break; // 0xF5 is opening quote
case '~': dialog[i] = 0xF7; break;
case '*': dialog[i] = 0xFB; break;
case ' ': dialog[i] = DIALOG_CHAR_SPACE; break;
case '\n': dialog[i] = DIALOG_CHAR_NEWLINE; break;
default: dialog[i] = ((u8)string[i] < 0xF0) ? ASCII_TO_DIALOG(string[i]) : string[i];
}
} }
dialog[length] = DIALOG_CHAR_TERMINATOR; dialog[length] = DIALOG_CHAR_TERMINATOR;
} }
@ -3196,6 +3202,7 @@ s16 render_menus_and_dialogs() {
create_dl_ortho_matrix(); create_dl_ortho_matrix();
render_chat(); render_chat();
djui_render();
if (gMenuMode != -1) { if (gMenuMode != -1) {
switch (gMenuMode) { switch (gMenuMode) {

View file

@ -30,6 +30,10 @@
#define RENDER_COURSE_DONE_SCREEN 2 #define RENDER_COURSE_DONE_SCREEN 2
#if !defined(VERSION_JP) && !defined(VERSION_SH)
extern u8 gDialogCharWidths[];
#endif
extern s8 gDialogCourseActNum; extern s8 gDialogCourseActNum;
extern s8 gHudFlash; extern s8 gHudFlash;
@ -117,6 +121,7 @@ void create_dl_identity_matrix(void);
void create_dl_translation_matrix(s8 pushOp, f32 x, f32 y, f32 z); void create_dl_translation_matrix(s8 pushOp, f32 x, f32 y, f32 z);
void create_dl_ortho_matrix(void); void create_dl_ortho_matrix(void);
void render_generic_char(u8 c); void render_generic_char(u8 c);
u8 str_ascii_char_to_dialog(char c);
void str_ascii_to_dialog(const char* string, u8* dialog, u16 length); void str_ascii_to_dialog(const char* string, u8* dialog, u16 length);
f32 get_generic_dialog_width(u8* dialog); f32 get_generic_dialog_width(u8* dialog);
f32 get_generic_ascii_string_width(const char* ascii); f32 get_generic_ascii_string_width(const char* ascii);

89
src/pc/djui/djui.c Normal file
View file

@ -0,0 +1,89 @@
#include "djui.h"
struct DjuiRoot* gDjuiRoot = NULL;
ALIGNED8 static const u8 texture32x32[] = {
#include "actors/bubble/mr_i_bubble.rgba16.inc.c"
};
ALIGNED8 static const u8 texture16x16[] = {
#include "textures/segment2/custom_luigi_head.rgba16.inc.c"
};
void djui_render(void) {
static struct DjuiRect* sDjuiRect = NULL;
static struct DjuiRect* sDjuiRect2 = NULL;
static struct DjuiText* sDjuiText = NULL;
static struct DjuiImage* sDjuiImage = NULL;
static struct DjuiButton* sDjuiButton = NULL;
if (gDjuiRoot == NULL) {
gDjuiRoot = djui_root_create();
struct DjuiRect* imageContainer = djui_rect_create(&gDjuiRoot->base);
imageContainer->base.x = 32;
imageContainer->base.y = 32;
imageContainer->base.width = 64;
imageContainer->base.height = 64;
sDjuiImage = djui_image_create(&imageContainer->base, texture16x16, 16, 16);
sDjuiImage->base.x = 16;
sDjuiImage->base.y = 16;
sDjuiImage->base.width = 32;
sDjuiImage->base.height = 32;
sDjuiImage->base.color.r = 255;
//////////////
sDjuiRect = djui_rect_create(&gDjuiRoot->base);
sDjuiRect->base.x = 64;
sDjuiRect->base.y = 64;
sDjuiRect->base.width = 188;
sDjuiRect->base.height = 64;
sDjuiRect->base.color.a = 200;
sDjuiRect->base.hAlign = DJUI_HALIGN_LEFT;
sDjuiRect->base.vAlign = DJUI_VALIGN_BOTTOM;
sDjuiRect2 = djui_rect_create(&sDjuiRect->base);
sDjuiRect2->base.x = 0;
sDjuiRect2->base.y = 0;
//sDjuiRect2->base.color.r = 0;
sDjuiRect2->base.hAlign = DJUI_HALIGN_CENTER;
sDjuiRect2->base.vAlign = DJUI_VALIGN_CENTER;
sDjuiRect2->base.color.a = 255;
sDjuiRect2->base.width = 188 - 8;
sDjuiRect2->base.height = 64 - 8;
sDjuiText = djui_text_create(&sDjuiRect2->base, "Host");
sDjuiText->base.color.r = 111;
sDjuiText->base.color.g = 111;
sDjuiText->base.color.b = 111;
sDjuiText->fontSize = 2;
sDjuiText->base.color.a = 255;
sDjuiText->base.width = 188 - 8;
sDjuiText->base.height = 64 - 8;
sDjuiText->base.x = 0;
sDjuiText->base.y = 0;
sDjuiText->textHAlign = DJUI_HALIGN_CENTER;
sDjuiText->textVAlign = DJUI_VALIGN_CENTER;
sDjuiButton = djui_button_create(&gDjuiRoot->base, "button");
}
djui_base_render(&gDjuiRoot->base);
if (sDjuiRect2 != NULL) {
static u32 sTimer = 0;
sTimer++;
sDjuiImage->base.x = 16.0f + cos((sTimer) / 10.0f) * 24.0f;
sDjuiImage->base.y = 16.0f + sin((sTimer) / 31.0f) * 24.0f;
sDjuiImage->base.color.r = 127.0 + sin((sTimer) / 13.0f) * 127.0f;
sDjuiImage->base.color.g = 127.0 + sin((sTimer) / 17.0f) * 127.0f;
sDjuiImage->base.color.b = 127.0 + sin((sTimer) / 23.0f) * 127.0f;
sDjuiRect2->base.x = 32.0f + cos((sTimer) / 10.0f) * 64.0f;
sDjuiRect2->base.y = 32.0f + sin((sTimer) / 31.0f) * 64.0f;
sDjuiButton->base.width = 200 + cos((sTimer) / 10.0f) * 64.0f;
sDjuiButton->base.height = 64 + sin((sTimer) / 10.0f) * 16.0f;
}
}

24
src/pc/djui/djui.h Normal file
View file

@ -0,0 +1,24 @@
#pragma once
#include <PR/gbi.h>
#include <PR/ultratypes.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include "djui_types.h"
#include "djui_gfx.h"
#include "djui_base.h"
#include "djui_root.h"
#include "djui_rect.h"
#include "djui_text.h"
#include "djui_image.h"
#include "djui_button.h"
#include "game/game_init.h"
#include "game/ingame_menu.h"
extern struct DjuiRoot* gDjuiRoot;
void djui_render(void);

209
src/pc/djui/djui_base.c Normal file
View file

@ -0,0 +1,209 @@
#include "djui.h"
////////////////
// properties //
////////////////
void djui_base_set_location(struct DjuiBase* base, f32 x, f32 y) {
base->x = x;
base->y = y;
}
void djui_base_set_size(struct DjuiBase* base, f32 width, f32 height) {
base->width = width;
base->height = height;
base->widthFill = false;
base->heightFill = false;
}
void djui_base_set_size_fill(struct DjuiBase* base, f32 widthScale, f32 heightScale) {
base->width = widthScale;
base->height = heightScale;
base->widthFill = true;
base->heightFill = true;
}
void djui_base_set_color(struct DjuiBase* base, u8 r, u8 g, u8 b, u8 a) {
base->color.r = r;
base->color.g = g;
base->color.b = b;
base->color.a = a;
}
void djui_base_set_alignment(struct DjuiBase* base, enum DjuiHAlign hAlign, enum DjuiVAlign vAlign) {
base->hAlign = hAlign;
base->vAlign = vAlign;
}
/////////////
// utility //
/////////////
static void djui_base_clip(struct DjuiBase* base) {
struct DjuiBase* parent = base->parent;
struct DjuiBaseRect* comp = &base->comp;
struct DjuiBaseRect* clip = &base->clip;
clip->x = comp->x;
clip->y = comp->y;
clip->width = comp->width;
clip->height = comp->height;
if (parent == NULL) { return; }
clip->x = fmax(clip->x, parent->clip.x);
clip->y = fmax(clip->y, parent->clip.y);
clip->width = (comp->x + comp->width) - clip->x;
clip->height = (comp->y + comp->height) - clip->y;
clip->width = fmin(clip->width, (parent->clip.x + parent->clip.width) - clip->x);
clip->height = fmin(clip->height, (parent->clip.y + parent->clip.height) - clip->y);
}
void djui_base_compute(struct DjuiBase* base) {
struct DjuiBase* parent = base->parent;
struct DjuiBaseRect* comp = &base->comp;
f32 x = base->x;
f32 y = base->y;
f32 width = base->widthFill ? (parent->comp.width - x) : base->width;
f32 height = base->heightFill ? (parent->comp.height - y) : base->height;
// horizontal alignment
if (base->hAlign == DJUI_HALIGN_CENTER) {
x += (parent->comp.width - width) / 2.0f;
} else if (base->hAlign == DJUI_HALIGN_RIGHT) {
x = parent->comp.width - width - x;
}
// vertical alignment
if (base->vAlign == DJUI_VALIGN_CENTER) {
y += (parent->comp.height - height) / 2.0f;
} else if (base->vAlign == DJUI_VALIGN_BOTTOM) {
y = parent->comp.height - height - y;
}
// offset comp on parent's location
if (base->parent != NULL) {
x += parent->comp.x;
y += parent->comp.y;
}
// set computed values
comp->x = x;
comp->y = y;
comp->width = width;
comp->height = height;
djui_base_clip(base);
}
static void djui_base_add_child(struct DjuiBase* parent, struct DjuiBase* base) {
if (parent == NULL) { return; }
// allocate linked list node
struct DjuiBaseChild* baseChild = malloc(sizeof(struct DjuiBaseChild));
baseChild->base = base;
baseChild->next = NULL;
// add it to the head
if (parent->child == NULL) {
parent->child = baseChild;
return;
}
// add it to the tail
struct DjuiBaseChild* parentChild = parent->child;
while (parentChild != NULL) {
if (parentChild->next == NULL) {
parentChild->next = baseChild;
break;
}
parentChild = parentChild->next;
}
}
////////////
// events //
////////////
void djui_base_render(struct DjuiBase* base) {
if (!base->visible) { return; }
struct DjuiBaseRect* comp = &base->comp;
djui_base_compute(base);
if (comp->width <= 0) { return; }
if (comp->height <= 0) { return; }
if (base->render != NULL) {
base->render(base);
}
// render all children
struct DjuiBaseChild* child = base->child;
while (child != NULL) {
djui_base_render(child->base);
child = child->next;
}
}
void djui_base_destroy(struct DjuiBase* base) {
// remove myself from parent's linked list
if (base->parent != NULL) {
struct DjuiBaseChild* child = base->parent->child;
struct DjuiBaseChild* lastChild = NULL;
while (child != NULL) {
if (child->base == base) {
// adjust linked list
if (lastChild == NULL) {
base->parent->child = child->next;
} else {
lastChild->next = child->next;
}
// deallocate child node
free(child);
base->parent = NULL;
break;
}
// iterate
lastChild = child;
child = child->next;
}
}
// destroy all children and our linked list
struct DjuiBaseChild* child = base->child;
while (child != NULL) {
struct DjuiBaseChild* nextChild = child;
child->base->parent = NULL;
djui_base_destroy(child->base);
free(child);
child = nextChild->next;
}
// destroy this
base->destroy(base);
}
void djui_base_init(struct DjuiBase* parent, struct DjuiBase* base, void(*render)(struct DjuiBase*), void (*destroy)(struct DjuiBase*)) {
base->parent = parent;
base->child = NULL;
base->visible = true;
base->x = 0;
base->y = 0;
base->width = 64;
base->height = 64;
base->widthFill = false;
base->heightFill = false;
base->color.r = 255;
base->color.g = 255;
base->color.b = 255;
base->color.a = 255;
base->hAlign = DJUI_HALIGN_LEFT;
base->vAlign = DJUI_VALIGN_TOP;
base->render = render;
base->destroy = destroy;
djui_base_add_child(parent, base);
}

48
src/pc/djui/djui_base.h Normal file
View file

@ -0,0 +1,48 @@
#pragma once
#include "djui.h"
#pragma pack(1)
struct DjuiBaseRect {
f32 x;
f32 y;
f32 width;
f32 height;
};
#pragma pack(1)
struct DjuiBaseChild {
struct DjuiBase* base;
struct DjuiBaseChild* next;
};
#pragma pack(1)
struct DjuiBase {
struct DjuiBase* parent;
struct DjuiBaseChild* child;
bool visible;
f32 x;
f32 y;
f32 width;
f32 height;
bool widthFill;
bool heightFill;
struct DjuiColor color;
enum DjuiHAlign hAlign;
enum DjuiVAlign vAlign;
struct DjuiBaseRect comp;
struct DjuiBaseRect clip;
void (*render)(struct DjuiBase*);
void (*destroy)(struct DjuiBase*);
};
void djui_base_set_location(struct DjuiBase* base, f32 x, f32 y);
void djui_base_set_size(struct DjuiBase* base, f32 width, f32 height);
void djui_base_set_size_fill(struct DjuiBase* base, f32 widthScale, f32 heightScale);
void djui_base_set_color(struct DjuiBase* base, u8 r, u8 g, u8 b, u8 a);
void djui_base_set_alignment(struct DjuiBase* base, enum DjuiHAlign hAlign, enum DjuiVAlign vAlign);
void djui_base_compute(struct DjuiBase* base);
void djui_base_render(struct DjuiBase* base);
void djui_base_destroy(struct DjuiBase* base);
void djui_base_init(struct DjuiBase* parent, struct DjuiBase* base, void (*render)(struct DjuiBase*), void (*destroy)(struct DjuiBase*));

27
src/pc/djui/djui_button.c Normal file
View file

@ -0,0 +1,27 @@
#include "djui.h"
static void djui_button_destroy(struct DjuiBase* base) {
struct DjuiButton* button = (struct DjuiButton*)base;
free(button);
}
struct DjuiButton* djui_button_create(struct DjuiBase* parent, const char* message) {
struct DjuiButton* button = malloc(sizeof(struct DjuiButton));
struct DjuiBase* base = &button->base;
djui_base_init(parent, base, NULL, djui_button_destroy);
djui_base_set_size(base, 200, 64);
struct DjuiRect* rect = djui_rect_create(&button->base);
djui_base_set_size_fill(&rect->base, 1.0f, 1.0f);
djui_base_set_color(&rect->base, 225, 225, 225, 255);
button->rect = rect;
struct DjuiText* text = djui_text_create(&rect->base, message);
djui_base_set_size_fill(&text->base, 1.0f, 1.0f);
djui_base_set_color(&text->base, 11, 11, 11, 255);
djui_text_set_alignment(text, DJUI_HALIGN_CENTER, DJUI_VALIGN_CENTER);
button->text = text;
return button;
}

11
src/pc/djui/djui_button.h Normal file
View file

@ -0,0 +1,11 @@
#pragma once
#include "djui.h"
#pragma pack(1)
struct DjuiButton {
struct DjuiBase base;
struct DjuiRect* rect;
struct DjuiText* text;
};
struct DjuiButton* djui_button_create(struct DjuiBase* parent, const char* message);

29
src/pc/djui/djui_gbi.h Normal file
View file

@ -0,0 +1,29 @@
#pragma once
#define G_TEXCLIP_DJUI 0xe1
#define G_TEXOVERRIDE_DJUI 0xe0
#define G_EXECUTE_DJUI 0xdd
#define gSetClippingDjui(pkt, cmd, rot, x1, y1, x2, y2) \
{ \
Gfx *_g = (Gfx *)(pkt); \
_g->words.w0 = _SHIFTL(cmd, 24, 8) | _SHIFTL( x1, 16, 8) | \
_SHIFTL( y1, 8, 8) | _SHIFTL(rot, 0, 8); \
_g->words.w1 = _SHIFTL(x2, 16, 8) | _SHIFTL(y2, 8, 8); \
}
#define gSetOverrideDjui(pkt, cmd, texture, w, h) \
{ \
Gfx *_g = (Gfx *)(pkt); \
_g->words.w0 = _SHIFTL(cmd, 24, 8) | _SHIFTL(w, 16, 8) | \
_SHIFTL(h, 8, 8); \
_g->words.w1 = (uintptr_t)(texture); \
}
#define gsSPExecuteDjui(word) \
{{ \
_SHIFTL(G_EXECUTE_DJUI, 24, 8), (unsigned int)(word) \
}}
#define gDPSetTextureClippingDjui(pkt, rot, x1, y1, x2, y2) gSetClippingDjui(pkt, G_TEXCLIP_DJUI, rot, x1, y1, x2, y2)
#define gDPSetTextureOverrideDjui(pkt, texture, w, h) gSetOverrideDjui(pkt, G_TEXOVERRIDE_DJUI, texture, w, h)

156
src/pc/djui/djui_gfx.c Normal file
View file

@ -0,0 +1,156 @@
#include <ultra64.h>
#include "sm64.h"
#include "djui.h"
#include "game/ingame_menu.h"
#include "game/segment2.h"
#include "src/pc/pc_main.h"
#include "src/pc/gfx/gfx_window_manager_api.h"
#include "gfx_dimensions.h"
static const Vtx vertex_djui_simple_rect[] = {
{{{ 0, -1, 0}, 0, { 0, 0 }, { 0xff, 0xff, 0xff, 0xff }}},
{{{ 1, -1, 0}, 0, { 0, 0 }, { 0xff, 0xff, 0xff, 0xff }}},
{{{ 1, 0, 0}, 0, { 0, 0 }, { 0xff, 0xff, 0xff, 0xff }}},
{{{ 0, 0, 0}, 0, { 0, 0 }, { 0xff, 0xff, 0xff, 0xff }}},
};
const Gfx dl_djui_simple_rect[] = {
gsDPPipeSync(),
gsSPClearGeometryMode(G_LIGHTING),
gsDPSetCombineMode(G_CC_FADE, G_CC_FADE),
gsDPSetRenderMode(G_RM_XLU_SURF, G_RM_XLU_SURF2),
gsSPVertex(vertex_djui_simple_rect, 4, 0),
gsSP2Triangles(0, 1, 2, 0x0, 0, 2, 3, 0x0),
gsSPEndDisplayList(),
};
/////////////////////////////////////////////
static Vtx vertex_djui_ia_char[] = {
{{{ 0, -16, 0}, 0, { 0, 256}, { 0xff, 0xff, 0xff, 0xff }}},
{{{ 8, -16, 0}, 0, { 0, 0}, { 0xff, 0xff, 0xff, 0xff }}},
{{{ 8, 0, 0}, 0, { 512, 0}, { 0xff, 0xff, 0xff, 0xff }}},
{{{ 0, 0, 0}, 0, { 512, 256}, { 0xff, 0xff, 0xff, 0xff }}},
};
const Gfx dl_djui_ia_text_begin[] = {
gsDPPipeSync(),
gsSPClearGeometryMode(G_LIGHTING),
gsDPSetCombineMode(G_CC_FADEA, G_CC_FADEA),
gsDPSetEnvColor(255, 255, 255, 255),
gsDPSetRenderMode(G_RM_XLU_SURF, G_RM_XLU_SURF2),
gsDPSetTextureFilter(G_TF_POINT),
gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON),
gsSPEndDisplayList(),
};
const Gfx dl_djui_ia_text_settings[] = {
gsDPSetTile(G_IM_FMT_IA, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, G_TX_WRAP | G_TX_NOMIRROR, 3, G_TX_NOLOD, G_TX_WRAP | G_TX_NOMIRROR, 4, G_TX_NOLOD),
gsDPLoadSync(),
gsDPLoadBlock(G_TX_LOADTILE, 0, 0, ((16 * 8 + G_IM_SIZ_4b_INCR) >> G_IM_SIZ_4b_SHIFT) - 1, CALC_DXT(16, G_IM_SIZ_4b_BYTES)),
gsDPSetTile(G_IM_FMT_IA, G_IM_SIZ_4b, 1, 0, G_TX_RENDERTILE, 0, G_TX_WRAP | G_TX_NOMIRROR, 3, G_TX_NOLOD, G_TX_WRAP | G_TX_NOMIRROR, 4, G_TX_NOLOD),
gsDPSetTileSize(0, 0, 0, (16 - 1) << G_TEXTURE_IMAGE_FRAC, (8 - 1) << G_TEXTURE_IMAGE_FRAC),
gsSPVertex(vertex_djui_ia_char, 4, 0),
gsSPExecuteDjui(G_TEXCLIP_DJUI),
gsSP2Triangles(0, 1, 2, 0x0, 0, 2, 3, 0x0),
gsSPEndDisplayList(),
};
void djui_gfx_render_char(u8 c) {
void** fontLUT;
void* packedTexture;
fontLUT = segmented_to_virtual(main_font_lut);
packedTexture = segmented_to_virtual(fontLUT[c]);
gDPPipeSync(gDisplayListHead++);
gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_16b, 1, VIRTUAL_TO_PHYSICAL(packedTexture));
gSPDisplayList(gDisplayListHead++, dl_djui_ia_text_settings);
}
/////////////////////////////////////////////
static const Vtx vertex_djui_image[] = {
{{{ 0, -1, 0 }, 0, { 0, 512 }, { 0xff, 0xff, 0xff, 0xff }}},
{{{ 1, -1, 0 }, 0, { 512, 512 }, { 0xff, 0xff, 0xff, 0xff }}},
{{{ 1, 0, 0 }, 0, { 512, 0 }, { 0xff, 0xff, 0xff, 0xff }}},
{{{ 0, 0, 0 }, 0, { 0, 0 }, { 0xff, 0xff, 0xff, 0xff }}},
};
const Gfx dl_djui_image[] = {
gsDPPipeSync(),
gsSPClearGeometryMode(G_LIGHTING),
gsDPSetCombineMode(G_CC_FADEA, G_CC_FADEA),
gsDPSetRenderMode(G_RM_XLU_SURF, G_RM_XLU_SURF2),
gsDPSetTextureFilter(G_TF_POINT),
gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON),
gsDPLoadTextureBlock(NULL, G_IM_FMT_RGBA, G_IM_SIZ_16b, 16, 16, 0, G_TX_CLAMP, G_TX_CLAMP, 5, 5, G_TX_NOLOD, G_TX_NOLOD),
gsSPExecuteDjui(G_TEXOVERRIDE_DJUI),
gsSPVertex(vertex_djui_image, 4, 0),
gsSPExecuteDjui(G_TEXCLIP_DJUI),
gsSP2Triangles(0, 1, 2, 0x0, 0, 2, 3, 0x0),
gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF),
gsDPSetCombineMode(G_CC_SHADE, G_CC_SHADE),
gsSPEndDisplayList(),
};
void djui_gfx_render_texture(const u8* texture, u16 w, u16 h) {
gDPSetTextureOverrideDjui(gDisplayListHead++, texture, w, h);
gSPDisplayList(gDisplayListHead++, dl_djui_image);
}
/////////////////////////////////////////////
void djui_gfx_position_translate(f32* x, f32* y) {
u32 windowWidth, windowHeight;
wm_api->get_dimensions(&windowWidth, &windowHeight);
*x = GFX_DIMENSIONS_FROM_LEFT_EDGE(0) + *x * ((f32)SCREEN_HEIGHT / (f32)windowHeight);
*y = SCREEN_HEIGHT - *y * ((f32)SCREEN_HEIGHT / (f32)windowHeight);
}
void djui_gfx_scale_translate(f32* width, f32* height) {
u32 windowWidth, windowHeight;
wm_api->get_dimensions(&windowWidth, &windowHeight);
*width = *width * ((f32)SCREEN_HEIGHT / (f32)windowHeight);
*height = *height * ((f32)SCREEN_HEIGHT / (f32)windowHeight);
}
void djui_gfx_size_translate(f32* size) {
u32 windowWidth, windowHeight;
wm_api->get_dimensions(&windowWidth, &windowHeight);
*size = *size * ((f32)SCREEN_HEIGHT / (f32)windowHeight);
}
bool djui_gfx_add_clipping_specific(struct DjuiBase* base, bool rotatedUV, f32 dX, f32 dY, f32 dW, f32 dH) {
struct DjuiBaseRect* clip = &base->clip;
f32 clipX2 = clip->x + clip->width;
f32 clipY2 = clip->y + clip->height;
f32 dX2 = dX + dW;
f32 dY2 = dY + dH;
// completely clipped
if (dX2 < clip->x) { return true; }
if (dX > clipX2) { return true; }
if (dY2 < clip->y) { return true; }
if (dY > clipY2) { return true; }
f32 dClipX1 = fmax((clip->x - dX) / dW, 0);
f32 dClipY1 = fmax((clip->y - dY) / dH, 0);
f32 dClipX2 = fmax((dX - (clipX2 - dW)) / dW, 0);
f32 dClipY2 = fmax((dY - (clipY2 - dH)) / dH, 0);
if ((dClipX1 != 0) || (dClipY1 != 0) || (dClipX2 != 0) || (dClipY2 != 0)) {
gDPSetTextureClippingDjui(gDisplayListHead++, rotatedUV, (u8)(dClipX1 * 255), (u8)(dClipY1 * 255), (u8)(dClipX2 * 255), (u8)(dClipY2 * 255));
}
return false;
}
bool djui_gfx_add_clipping(struct DjuiBase* base) {
struct DjuiBaseRect* comp = &base->comp;
return djui_gfx_add_clipping_specific(base, false, comp->x, comp->y, comp->width, comp->height);
}

21
src/pc/djui/djui_gfx.h Normal file
View file

@ -0,0 +1,21 @@
#pragma once
#include "djui.h"
#include "djui_base.h"
#define DJUI_MTX_PUSH 1
#define DJUI_MTX_NOPUSH 2
extern const Gfx dl_djui_simple_rect[];
extern const Gfx dl_djui_ia_text_begin[];
extern const Gfx dl_djui_img_begin[];
extern const Gfx dl_djui_img_end[];
void djui_gfx_render_char(u8 c);
void djui_gfx_render_texture(const u8* texture, u16 w, u16 h);
void djui_gfx_position_translate(f32* x, f32* y);
void djui_gfx_scale_translate(f32* width, f32* height);
void djui_gfx_size_translate(f32* size);
bool djui_gfx_add_clipping_specific(struct DjuiBase* base, bool rotatedUV, f32 dX, f32 dY, f32 dW, f32 dH);
bool djui_gfx_add_clipping(struct DjuiBase* base);

59
src/pc/djui/djui_image.c Normal file
View file

@ -0,0 +1,59 @@
#include "djui.h"
#include "game/segment2.h"
#include "src/pc/network/network.h"
////////////////
// properties //
////////////////
void djui_image_set_image(struct DjuiImage* image, const u8* texture, u16 textureWidth, u16 textureHeight) {
image->texture = texture;
image->textureWidth = textureWidth;
image->textureHeight = textureHeight;
}
////////////
// events //
////////////
static void djui_image_render(struct DjuiBase* base) {
struct DjuiImage* image = (struct DjuiImage*)base;
struct DjuiBaseRect* comp = &base->comp;
struct DjuiBaseRect* clip = &base->clip;
// translate position
f32 translatedX = comp->x;
f32 translatedY = comp->y;
djui_gfx_position_translate(&translatedX, &translatedY);
create_dl_translation_matrix(DJUI_MTX_PUSH, translatedX, translatedY, 0);
// translate size
f32 translatedWidth = comp->width;
f32 translatedHeight = comp->height;
djui_gfx_scale_translate(&translatedWidth, &translatedHeight);
create_dl_scale_matrix(DJUI_MTX_NOPUSH, translatedWidth, translatedHeight, 1.0f);
// render
if (!djui_gfx_add_clipping(base)) {
gDPSetEnvColor(gDisplayListHead++, base->color.r, base->color.g, base->color.b, base->color.a);
djui_gfx_render_texture(image->texture, image->textureWidth, image->textureHeight);
}
gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
}
static void djui_image_destroy(struct DjuiBase* base) {
struct DjuiImage* image = (struct DjuiImage*)base;
free(image);
}
struct DjuiImage* djui_image_create(struct DjuiBase* parent, const u8* texture, u16 textureWidth, u16 textureHeight) {
struct DjuiImage* image = malloc(sizeof(struct DjuiImage));
struct DjuiBase* base = &image->base;
djui_base_init(parent, base, djui_image_render, djui_image_destroy);
djui_image_set_image(image, texture, textureWidth, textureHeight);
return image;
}

14
src/pc/djui/djui_image.h Normal file
View file

@ -0,0 +1,14 @@
#pragma once
#include "djui.h"
#pragma pack(1)
struct DjuiImage {
struct DjuiBase base;
const u8* texture;
u16 textureWidth;
u16 textureHeight;
};
void djui_image_set_image(struct DjuiImage* image, const u8* texture, u16 textureWidth, u16 textureHeight);
struct DjuiImage* djui_image_create(struct DjuiBase* parent, const u8* texture, u16 textureWidth, u16 textureHeight);

41
src/pc/djui/djui_rect.c Normal file
View file

@ -0,0 +1,41 @@
#include "djui.h"
////////////
// events //
////////////
static void djui_rect_render(struct DjuiBase* base) {
struct DjuiBaseRect* clip = &base->clip;
// translate position
f32 translatedX = clip->x;
f32 translatedY = clip->y;
djui_gfx_position_translate(&translatedX, &translatedY);
create_dl_translation_matrix(DJUI_MTX_PUSH, translatedX, translatedY, 0);
// translate size
f32 translatedWidth = clip->width;
f32 translatedHeight = clip->height;
djui_gfx_scale_translate(&translatedWidth, &translatedHeight);
create_dl_scale_matrix(DJUI_MTX_NOPUSH, translatedWidth, translatedHeight, 1.0f);
// render
gDPSetEnvColor(gDisplayListHead++, base->color.r, base->color.g, base->color.b, base->color.a);
gSPDisplayList(gDisplayListHead++, dl_djui_simple_rect);
gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
}
static void djui_rect_destroy(struct DjuiBase* base) {
struct DjuiRect* rect = (struct DjuiRect*)base;
free(rect);
}
struct DjuiRect* djui_rect_create(struct DjuiBase* parent) {
struct DjuiRect* rect = malloc(sizeof(struct DjuiRect));
struct DjuiBase* base = &rect->base;
djui_base_init(parent, base, djui_rect_render, djui_rect_destroy);
return rect;
}

9
src/pc/djui/djui_rect.h Normal file
View file

@ -0,0 +1,9 @@
#pragma once
#include "djui.h"
#pragma pack(1)
struct DjuiRect {
struct DjuiBase base;
};
struct DjuiRect* djui_rect_create(struct DjuiBase* parent);

42
src/pc/djui/djui_root.c Normal file
View file

@ -0,0 +1,42 @@
#include "djui.h"
#include "src/pc/pc_main.h"
#include "src/pc/gfx/gfx_window_manager_api.h"
static void djui_root_render(struct DjuiBase* base) {
// grab window height
u32 windowWidth, windowHeight;
wm_api->get_dimensions(&windowWidth, &windowHeight);
// fill the screen
base->x = 0;
base->y = 0;
base->width = windowWidth;
base->height = windowHeight;
// compute base
djui_base_compute(base);
}
static void djui_root_destroy(struct DjuiBase* base) {
struct DjuiRoot* root = (struct DjuiRoot*)base;
free(root);
gDjuiRoot = NULL;
}
struct DjuiRoot* djui_root_create(void) {
struct DjuiRoot* root = malloc(sizeof(struct DjuiRoot));
struct DjuiBase* base = &root->base;
djui_base_init(NULL, base, djui_root_render, djui_root_destroy);
u32 windowWidth, windowHeight;
wm_api->get_dimensions(&windowWidth, &windowHeight);
base->x = 0;
base->y = 0;
base->width = windowWidth;
base->height = windowHeight;
base->color.a = 0;
return root;
}

9
src/pc/djui/djui_root.h Normal file
View file

@ -0,0 +1,9 @@
#pragma once
#include "djui.h"
#pragma pack(1)
struct DjuiRoot {
struct DjuiBase base;
};
struct DjuiRoot* djui_root_create(void);

280
src/pc/djui/djui_text.c Normal file
View file

@ -0,0 +1,280 @@
#include <string.h>
#include "djui.h"
#include "game/segment2.h"
////////////////
// properties //
////////////////
void djui_text_set_text(struct DjuiText* text, const char* message) {
// deallocate old message
if (text->message != NULL) {
free(text->message);
}
// allocate and set new message
u16 messageLen = strlen(message);
text->message = malloc(sizeof(char) * (messageLen + 1));
memcpy(text->message, message, sizeof(char) * (messageLen + 1));
}
void djui_text_set_font_size(struct DjuiText* text, f32 fontSize) {
text->fontSize = fontSize;
}
void djui_text_set_alignment(struct DjuiText* text, enum DjuiHAlign hAlign, enum DjuiVAlign vAlign) {
text->textHAlign = hAlign;
text->textVAlign = vAlign;
}
///////////////
// rendering //
///////////////
#define DJUI_TEXT_CHAR_HEIGHT 16
#define DJUI_TEXT_CHAR_WIDTH 8
static f32 sTextRenderX = 0;
static f32 sTextRenderY = 0;
static f32 sTextRenderLastX = 0;
static f32 sTextRenderLastY = 0;
static void djui_text_translate(f32 x, f32 y) {
sTextRenderX += x;
sTextRenderY += y;
}
static void djui_text_render_char(struct DjuiText* text, u8 d) {
struct DjuiBaseRect* comp = &text->base.comp;
struct DjuiBaseRect* clip = &text->base.clip;
f32 dX = comp->x + sTextRenderX * text->fontSize;
f32 dY = comp->y + sTextRenderY * text->fontSize;
f32 dW = DJUI_TEXT_CHAR_WIDTH * text->fontSize;
f32 dH = DJUI_TEXT_CHAR_HEIGHT * text->fontSize;
if (djui_gfx_add_clipping_specific(&text->base, true, dX, dY, dW, dH)) {
return;
}
create_dl_translation_matrix(DJUI_MTX_NOPUSH, sTextRenderX - sTextRenderLastX, (sTextRenderY - sTextRenderLastY) * -1.0f, 0);
djui_gfx_render_char(d);
sTextRenderLastX = sTextRenderX;
sTextRenderLastY = sTextRenderY;
}
static f32 djui_text_measure_word_width(char* message) {
f32 width = 0;
while (*message != '\0') {
u8 d = str_ascii_char_to_dialog(*message);
if (d == DIALOG_CHAR_SPACE) { return width; }
if (d == DIALOG_CHAR_NEWLINE) { return width; }
if (d == DIALOG_CHAR_TERMINATOR) { return width; }
width += gDialogCharWidths[d];
message++;
}
return width;
}
static void djui_text_read_line(char* message, u16* index, f32* lineWidth, f32 maxLineWidth, bool onLastLine, bool* ellipses) {
*lineWidth = 0;
u8 lastD = DIALOG_CHAR_TERMINATOR;
f32 ellipsesWidth = gDialogCharWidths[0x3F] * 3.0f;
u16 lastSafeEllipsesIndex = *index;
u16 lastSafeEllipsesLineWidth = *lineWidth + ellipsesWidth;
while (message[*index] != '\0') {
u8 d = str_ascii_char_to_dialog(message[*index]);
// check for newline
if (d == DIALOG_CHAR_NEWLINE) {
*index = *index + 1;
break;
}
// check to see if this character would exceed size
if (*lineWidth + gDialogCharWidths[d] >= maxLineWidth) {
break;
}
// check to see if this word exceeds size
if (!onLastLine && lastD == DIALOG_CHAR_SPACE && d != DIALOG_CHAR_SPACE) {
f32 wordWidth = djui_text_measure_word_width(&message[*index]);
if (*lineWidth + wordWidth >= maxLineWidth) {
return;
}
}
*lineWidth += gDialogCharWidths[d];
// check for safe ellipses index
if (onLastLine && ((*lineWidth + ellipsesWidth) < maxLineWidth)) {
lastSafeEllipsesIndex = *index;
lastSafeEllipsesLineWidth = *lineWidth + ellipsesWidth;
}
*index = *index + 1;
lastD = d;
}
// check to see if we should replace the end of the last line with ellipses
if (onLastLine && message[*index] != '\0') {
*index = lastSafeEllipsesIndex;
*lineWidth = lastSafeEllipsesLineWidth;
*ellipses = true;
}
}
static void djui_text_render_line(struct DjuiText* text, u16 startIndex, u16 endIndex, f32 lineWidth, bool ellipses) {
struct DjuiBase* base = &text->base;
struct DjuiBaseRect* comp = &base->comp;
f32 curWidth = 0;
// horizontal alignment
if (text->textHAlign == DJUI_HALIGN_CENTER) {
// center text
f32 offset = (comp->width / text->fontSize) / 2.0f - lineWidth / 2.0f;
djui_text_translate(offset, 0);
curWidth = offset;
} else if (text->textHAlign == DJUI_HALIGN_RIGHT) {
// right align text
f32 offset = (comp->width / text->fontSize) - lineWidth;
djui_text_translate(offset, 0);
curWidth = offset;
}
// render the line
for (int i = startIndex; i < endIndex; i++) {
u8 d = str_ascii_char_to_dialog(text->message[i]);
switch (d) {
case DIALOG_CHAR_SPACE:
break;
case DIALOG_CHAR_NEWLINE:
break;
default:
djui_text_render_char(text, d);
break;
}
djui_text_translate(gDialogCharWidths[d], 0);
curWidth += gDialogCharWidths[d];
}
// render ellipses
if (ellipses) {
for (int i = 0; i < 3; i++) {
u8 d = str_ascii_char_to_dialog('.');
djui_text_render_char(text, d);
djui_text_translate(gDialogCharWidths[d], 0);
curWidth += gDialogCharWidths[d];
}
}
// reset translation matrix
djui_text_translate(-curWidth, DJUI_TEXT_CHAR_HEIGHT);
}
////////////
// events //
////////////
static void djui_text_render(struct DjuiBase* base) {
gSPDisplayList(gDisplayListHead++, dl_djui_ia_text_begin);
struct DjuiText* text = (struct DjuiText*)base;
struct DjuiBaseRect* comp = &base->comp;
// reset render location
sTextRenderX = 0;
sTextRenderY = 0;
sTextRenderLastX = 0;
sTextRenderLastY = 0;
// compute base
djui_base_compute(base);
// translate position
f32 translatedX = comp->x;
f32 translatedY = comp->y;
djui_gfx_position_translate(&translatedX, &translatedY);
create_dl_translation_matrix(DJUI_MTX_PUSH, translatedX, translatedY, 0);
// compute size
f32 translatedWidth = comp->width;
f32 translatedHeight = comp->height;
djui_gfx_scale_translate(&translatedWidth, &translatedHeight);
// compute font size
f32 translatedFontSize = text->fontSize;
djui_gfx_size_translate(&translatedFontSize);
create_dl_scale_matrix(DJUI_MTX_NOPUSH, translatedFontSize, translatedFontSize, 1.0f);
// set color
gDPSetEnvColor(gDisplayListHead++, base->color.r, base->color.g, base->color.b, base->color.a);
// count lines
u16 startIndex = 0;
u16 endIndex = 0;
f32 maxLineWidth = comp->width / text->fontSize;
u16 lineCount = 0;
u16 maxLines = comp->height / ((f32)DJUI_TEXT_CHAR_HEIGHT * text->fontSize);
while (text->message[startIndex] != '\0') {
bool onLastLine = lineCount + 1 >= maxLines;
f32 lineWidth;
bool ellipses;
djui_text_read_line(text->message, &endIndex, &lineWidth, maxLineWidth, onLastLine, &ellipses);
startIndex = endIndex;
lineCount++;
if (onLastLine) { break; }
}
// do vertical alignment
f32 vOffset = 0;
if (text->textVAlign == DJUI_VALIGN_CENTER) {
vOffset += (comp->height / text->fontSize) / 2.0f;
vOffset -= (lineCount * DJUI_TEXT_CHAR_HEIGHT) / 2.0f;
} else if (text->textVAlign == DJUI_VALIGN_BOTTOM) {
vOffset += (comp->height / text->fontSize);
vOffset -= (lineCount * DJUI_TEXT_CHAR_HEIGHT);
}
djui_text_translate(0, vOffset);
// render lines
startIndex = 0;
endIndex = 0;
f32 lineWidth;
u16 lineIndex = 0;
bool ellipses = false;
while (text->message[startIndex] != '\0') {
bool onLastLine = lineIndex + 1 >= maxLines;
djui_text_read_line(text->message, &endIndex, &lineWidth, maxLineWidth, onLastLine, &ellipses);
djui_text_render_line(text, startIndex, endIndex, lineWidth, ellipses);
startIndex = endIndex;
lineIndex++;
if (onLastLine) { break; }
}
gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
}
static void djui_text_destroy(struct DjuiBase* base) {
struct DjuiText* text = (struct DjuiText*)base;
free(text->message);
free(text);
}
struct DjuiText* djui_text_create(struct DjuiBase* parent, const char* message) {
struct DjuiText* text = malloc(sizeof(struct DjuiText));
struct DjuiBase* base = &text->base;
djui_base_init(parent, base, djui_text_render, djui_text_destroy);
text->message = NULL;
djui_text_set_text(text, message);
djui_text_set_font_size(text, 2.0f);
djui_text_set_alignment(text, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP);
return text;
}

17
src/pc/djui/djui_text.h Normal file
View file

@ -0,0 +1,17 @@
#pragma once
#include "djui.h"
#pragma pack(1)
struct DjuiText {
struct DjuiBase base;
char* message;
f32 fontSize;
enum DjuiHAlign textHAlign;
enum DjuiVAlign textVAlign;
};
void djui_text_set_text(struct DjuiText* text, const char* message);
void djui_text_set_font_size(struct DjuiText* text, f32 fontSize);
void djui_text_set_alignment(struct DjuiText* text, enum DjuiHAlign hAlign, enum DjuiVAlign vAlign);
struct DjuiText* djui_text_create(struct DjuiBase* parent, const char* message);

21
src/pc/djui/djui_types.h Normal file
View file

@ -0,0 +1,21 @@
#pragma once
#include "djui.h"
#pragma pack(1)
struct DjuiColor {
u8 r;
u8 g;
u8 b;
u8 a;
};
enum DjuiScreenValueType { DJUI_SVT_ABSOLUTE, DJUI_SVT_RELATIVE };
#pragma pack(1)
struct DjuiScreenValue {
enum DjuiScreenValueType type;
f32 value;
};
enum DjuiHAlign { DJUI_HALIGN_LEFT, DJUI_HALIGN_CENTER, DJUI_HALIGN_RIGHT };
enum DjuiVAlign { DJUI_VALIGN_TOP, DJUI_VALIGN_CENTER, DJUI_VALIGN_BOTTOM };

View file

@ -186,6 +186,12 @@ static const uint8_t missing_texture[MISSING_W * MISSING_H * 4] = {
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF,
}; };
//////////////////////////////////
// forward declaration for djui //
//////////////////////////////////
void djui_gfx_run_dl(Gfx* cmd);
//////////////////////////////////
#ifdef EXTERNAL_DATA #ifdef EXTERNAL_DATA
static inline size_t string_hash(const uint8_t *str) { static inline size_t string_hash(const uint8_t *str) {
size_t h = 0; size_t h = 0;
@ -1725,6 +1731,9 @@ static void gfx_run_dl(Gfx* cmd) {
case G_SETCIMG: case G_SETCIMG:
gfx_dp_set_color_image(C0(21, 3), C0(19, 2), C0(0, 11), seg_addr(cmd->words.w1)); gfx_dp_set_color_image(C0(21, 3), C0(19, 2), C0(0, 11), seg_addr(cmd->words.w1));
break; break;
default:
djui_gfx_run_dl(cmd);
break;
} }
++cmd; ++cmd;
} }
@ -1839,3 +1848,144 @@ void gfx_shutdown(void) {
gfx_wapi = NULL; gfx_wapi = NULL;
} }
} }
/////////////////////////
// v custom for djui v //
/////////////////////////
static bool sDjuiClip = 0;
static bool sDjuiClipRotatedUV = 0;
static uint8_t sDjuiClipX1 = 0;
static uint8_t sDjuiClipY1 = 0;
static uint8_t sDjuiClipX2 = 0;
static uint8_t sDjuiClipY2 = 0;
static bool sDjuiOverride = false;
static void* sDjuiOverrideTexture = NULL;
static uint8_t sDjuiOverrideW = 0;
static uint8_t sDjuiOverrideH = 0;
static void djui_gfx_dp_set_clipping(bool rotatedUV, uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2) {
sDjuiClipRotatedUV = rotatedUV;
sDjuiClipX1 = x1;
sDjuiClipY1 = y1;
sDjuiClipX2 = x2;
sDjuiClipY2 = y2;
sDjuiClip = true;
}
static void djui_gfx_dp_execute_clipping(void) {
if (!sDjuiClip) { return; }
sDjuiClip = 0;
size_t start_index = 0;
size_t dest_index = 4;
float minX = rsp.loaded_vertices[start_index].x;
float maxX = rsp.loaded_vertices[start_index].x;
float minY = rsp.loaded_vertices[start_index].y;
float maxY = rsp.loaded_vertices[start_index].y;
float minU = rsp.loaded_vertices[start_index].u;
float maxU = rsp.loaded_vertices[start_index].u;
float minV = rsp.loaded_vertices[start_index].v;
float maxV = rsp.loaded_vertices[start_index].v;
for (size_t i = start_index; i < dest_index; i++) {
struct LoadedVertex* d = &rsp.loaded_vertices[i];
minX = fmin(minX, d->x);
maxX = fmax(maxX, d->x);
minY = fmin(minY, d->y);
maxY = fmax(maxY, d->y);
minU = fmin(minU, d->u);
maxU = fmax(maxU, d->u);
minV = fmin(minV, d->v);
maxV = fmax(maxV, d->v);
}
float midY = (minY + maxY) / 2.0f;
float midX = (minX + maxX) / 2.0f;
float midU = (minU + maxU) / 2.0f;
float midV = (minV + maxV) / 2.0f;
for (size_t i = start_index; i < dest_index; i++) {
struct LoadedVertex* d = &rsp.loaded_vertices[i];
if (d->x <= midX) {
d->x += (maxX - minX) * (sDjuiClipX1 / 255.0f);
} else {
d->x -= (maxX - minX) * (sDjuiClipX2 / 255.0f);
}
if (d->y <= midY) {
d->y += (maxY - minY) * (sDjuiClipY2 / 255.0f);
} else {
d->y -= (maxY - minY) * (sDjuiClipY1 / 255.0f);
}
if (sDjuiClipRotatedUV) {
if (d->u <= midU) {
d->u += (maxU - minU) * (sDjuiClipY2 / 255.0f);
}
else {
d->u -= (maxU - minU) * (sDjuiClipY1 / 255.0f);
}
if (d->v <= midV) {
d->v += (maxV - minV) * (sDjuiClipX2 / 255.0f);
}
else {
d->v -= (maxV - minV) * (sDjuiClipX1 / 255.0f);
}
} else {
if (d->u <= midU) {
d->u += (maxU - minU) * (sDjuiClipX1 / 255.0f);
} else {
d->u -= (maxU - minU) * (sDjuiClipX2 / 255.0f);
}
if (d->v <= midV) {
d->v += (maxV - minV) * (sDjuiClipY1 / 255.0f);
} else {
d->v -= (maxV - minV) * (sDjuiClipY2 / 255.0f);
}
}
}
}
static void djui_gfx_dp_set_override(void* texture, uint8_t w, uint8_t h) {
sDjuiOverrideTexture = texture;
sDjuiOverrideW = w;
sDjuiOverrideH = h;
sDjuiOverride = (texture != NULL);
}
static void djui_gfx_dp_execute_override(void) {
if (!sDjuiOverride) { return; }
sDjuiOverride = false;
rdp.texture_to_load.addr = sDjuiOverrideTexture;
rdp.loaded_texture[rdp.texture_to_load.tile_number].addr = rdp.texture_to_load.addr;
uint32_t line = (((sDjuiOverrideW * 2) + 7) >> 3);
rdp.texture_tile.line_size_bytes = line * 8;
uint32_t lrs = (sDjuiOverrideW * sDjuiOverrideH) - 1;
rdp.loaded_texture[rdp.texture_to_load.tile_number].size_bytes = (lrs + 1) << 1;
}
static void djui_gfx_dp_execute_djui(uint32_t opcode) {
switch (opcode) {
case G_TEXOVERRIDE_DJUI: djui_gfx_dp_execute_override(); break;
case G_TEXCLIP_DJUI: djui_gfx_dp_execute_clipping(); break;
}
}
void djui_gfx_run_dl(Gfx* cmd) {
uint32_t opcode = cmd->words.w0 >> 24;
switch (opcode) {
case G_TEXCLIP_DJUI:
djui_gfx_dp_set_clipping(C0(0, 8), C0(16, 8), C0(8, 8), C1(16, 8), C1(8, 8));
break;
case G_TEXOVERRIDE_DJUI:
djui_gfx_dp_set_override(seg_addr(cmd->words.w1), C0(16, 8), C0(8, 8));
break;
case G_EXECUTE_DJUI:
djui_gfx_dp_execute_djui(cmd->words.w1);
break;
}
}