Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions soh/soh/SohGui/SohMenuEnhancements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,21 @@ void SohMenu::AddMenuEnhancements() {
.Options(CheckboxOptions().Tooltip(
"Disables bombs always rotating to face the camera. To be used in conjunction with mods that want to "
"replace bombs with 3D objects."));
AddWidget(path, "Disable Fairy Billboarding", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("DisableFairyBillboarding"))
.RaceDisable(false)
.Options(CheckboxOptions().Tooltip(
"Disables fairies always rotating to face the camera. Use when replacing fairies with 3D models."));
AddWidget(path, "Hide Fairy Glow Plane", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("DisableFairyGlow"))
.RaceDisable(false)
.PreFunc([](WidgetInfo& info) {
const bool billboardOff = CVarGetInteger(CVAR_ENHANCEMENT("DisableFairyBillboarding"), 0);
info.options->disabled = !billboardOff;
info.options->disabledTooltip = "Enable \"Disable Fairy Billboarding\" to toggle this.";
})
.Options(CheckboxOptions().Tooltip("Hides the billboarded glow plane on fairies.\n"
"Requires a scene reload after changing."));
AddWidget(path, "Disable Grotto Fixed Rotation", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("DisableGrottoRotation"))
.RaceDisable(false)
Expand Down
62 changes: 60 additions & 2 deletions soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -327,9 +327,16 @@ void EnElf_Init(Actor* thisx, PlayState* play) {
ActorShape_Init(&thisx->shape, 0.0f, NULL, 15.0f);
thisx->shape.shadowAlpha = 0xFF;

bool disableGlowPlane = CVarGetInteger(CVAR_ENHANCEMENT("DisableFairyBillboarding"), 0) &&
CVarGetInteger(CVAR_ENHANCEMENT("DisableFairyGlow"), 0);

Lights_PointGlowSetInfo(&this->lightInfoGlow, thisx->world.pos.x, thisx->world.pos.y, thisx->world.pos.z, 255, 255,
255, 0);
this->lightNodeGlow = LightContext_InsertLight(play, &play->lightCtx, &this->lightInfoGlow);
if (!disableGlowPlane) {
this->lightNodeGlow = LightContext_InsertLight(play, &play->lightCtx, &this->lightInfoGlow);
} else {
this->lightNodeGlow = NULL;
}

Lights_PointNoGlowSetInfo(&this->lightInfoNoGlow, thisx->world.pos.x, thisx->world.pos.y, thisx->world.pos.z, 255,
255, 255, 0);
Expand Down Expand Up @@ -440,7 +447,9 @@ void EnElf_Destroy(Actor* thisx, PlayState* play) {
s32 pad;
EnElf* this = (EnElf*)thisx;

LightContext_RemoveLight(play, &play->lightCtx, this->lightNodeGlow);
if (this->lightNodeGlow != NULL) {
LightContext_RemoveLight(play, &play->lightCtx, this->lightNodeGlow);
}
LightContext_RemoveLight(play, &play->lightCtx, this->lightNodeNoGlow);

ResourceMgr_UnregisterSkeleton(&this->skelAnime);
Expand Down Expand Up @@ -1455,6 +1464,8 @@ void func_80A053F0(Actor* thisx, PlayState* play) {
void EnElf_Update(Actor* thisx, PlayState* play) {
s32 pad;
EnElf* this = (EnElf*)thisx;
bool disableGlowPlane = CVarGetInteger(CVAR_ENHANCEMENT("DisableFairyBillboarding"), 0) &&
CVarGetInteger(CVAR_ENHANCEMENT("DisableFairyGlow"), 0);

this->actionFunc(this, play);
this->actor.shape.rot.y = this->unk_2BC;
Expand All @@ -1463,6 +1474,16 @@ void EnElf_Update(Actor* thisx, PlayState* play) {
if (this->fairyFlags & FAIRY_FLAG_BIG) {
func_80A04D90(this, play);
}

// Keep glow in sync with toggle: remove when disabled, re-add when enabled.
if (disableGlowPlane) {
if (this->lightNodeGlow != NULL) {
LightContext_RemoveLight(play, &play->lightCtx, this->lightNodeGlow);
this->lightNodeGlow = NULL;
}
} else if (this->lightNodeGlow == NULL) {
this->lightNodeGlow = LightContext_InsertLight(play, &play->lightCtx, &this->lightInfoGlow);
}
}

s32 EnElf_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx,
Expand All @@ -1472,6 +1493,7 @@ s32 EnElf_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* p
f32 scale;
Vec3f mtxMult;
EnElf* this = (EnElf*)thisx;
bool disableBillboarding = CVarGetInteger(CVAR_ENHANCEMENT("DisableFairyBillboarding"), 0);

if (limbIndex == 8) {
scale = ((Math_SinS(this->timer * 4096) * 0.1f) + 1.0f) * 0.012f;
Expand All @@ -1494,6 +1516,12 @@ s32 EnElf_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* p
}
}

// If billboarding is disabled, explicitly rotate the body limb (8) using the actor's yaw so 3D models
// that don't rely on the billboard segment still face the intended direction.
if (limbIndex == 8 && disableBillboarding) {
rot->y = rot->y + this->actor.shape.rot.y;
}

return false;
}

Expand All @@ -1505,6 +1533,10 @@ void EnElf_Draw(Actor* thisx, PlayState* play) {
s32 pad1;
Gfx* dListHead;
Player* player = GET_PLAYER(play);
bool disableBillboarding = CVarGetInteger(CVAR_ENHANCEMENT("DisableFairyBillboarding"), 0);
Mtx* customBillboardMtx = NULL;
Mtx* prevBillboardSegment = play->billboardMtx;
MtxF customRotMtxF;

if ((this->unk_2A8 != 8) && !(this->fairyFlags & 8)) {
if (!(player->stateFlags1 & PLAYER_STATE1_FIRST_PERSON) || (kREG(90) < this->actor.projectedPos.z)) {
Expand All @@ -1514,6 +1546,28 @@ void EnElf_Draw(Actor* thisx, PlayState* play) {

Gfx_SetupDL_27Xlu(play->state.gfxCtx);

if (!disableBillboarding) {
// Keep fairies facing the camera unless explicitly disabled
Matrix_ReplaceRotation(&play->billboardMtxF);
} else {
// Provide a billboard segment aligned to the actor/root rotation so the body can face its heading
customBillboardMtx = Graph_Alloc(play->state.gfxCtx, sizeof(Mtx));
if (customBillboardMtx != NULL) {
Matrix_Push();
Matrix_Put(&gMtxFClear);
Matrix_RotateZYX(BINANG_TO_RAD(this->actor.shape.rot.x), BINANG_TO_RAD(this->actor.shape.rot.y),
BINANG_TO_RAD(this->actor.shape.rot.z), MTXMODE_APPLY);
Matrix_RotateZYX(BINANG_TO_RAD(this->skelAnime.jointTable[0].x),
BINANG_TO_RAD(this->skelAnime.jointTable[0].y),
BINANG_TO_RAD(this->skelAnime.jointTable[0].z), MTXMODE_APPLY);
Matrix_Get(&customRotMtxF);
Matrix_Pop();
Matrix_MtxFToMtx(MATRIX_CHECKFLOATS(&customRotMtxF), customBillboardMtx);
gSPSegment(POLY_XLU_DISP++, 0x01, customBillboardMtx);
}
}
func_8002EBCC(thisx, play, 0);

envAlpha = (this->timer * 50) & 0x1FF;
envAlpha = (envAlpha > 255) ? 511 - envAlpha : envAlpha;

Expand All @@ -1536,6 +1590,10 @@ void EnElf_Draw(Actor* thisx, PlayState* play) {
POLY_XLU_DISP =
SkelAnime_DrawSkeleton2(play, &this->skelAnime, EnElf_OverrideLimbDraw, NULL, this, POLY_XLU_DISP);

if (disableBillboarding && customBillboardMtx != NULL) {
gSPSegment(POLY_XLU_DISP++, 0x01, prevBillboardSegment);
}

CLOSE_DISPS(play->state.gfxCtx);
}
}
Expand Down
Loading