1.2.1 Changes (#625)

* Update runtime for hook and callback sorting, update version number

* Actually update version number

* Automatically open mods menu when dragging a mod which also prevents issues when installing a mod from the launcher menu

* Update RT64 to add texture pack shift configuration and fix minimized memory leak

* Update runtime for return hook getter exports

* Actually update rt64

* Update RT64 to fix texture pack ordering

* Implement optional dependencies, fix memory slotmaps, bump version number to 1.2.1-dev

* Add command-line option to show console output on Windows. (#632)

* Add new raphnet adapter revision to controller DB

* Add another mayflash N64 adapter to the controller database file

* Update runtime after merge for optional dependencies

* Update runtime for optional dependency mod callback fix

* Add mayflash magic NS to controller database

* Update RT64 for extended address fix and x11 dependency removal

* Update RT64 to fix build issue caused by x11

* Update runtime to remove unnnecessary x11 includes

* Fix more x11 define compilation issues

* Fix the x86-64 CPU requirement listing in the readme (#634)

* Transform tagging for keaton grass tornado

* Interpolation for sword trails

* Switch RT64 to gEXVertex fix branch (temporary until merge)

* Add 8bitdo 64 bluetooth controller to database

* Add export to get bowstring transform ID

* Update RT64 to fix linux dev mode menu and texture streaming race condition

* Fix all the interpolation glitches in the Gibdo Mask cutscene (#641)

* Fix the actor extension API breaking when registering an extension for actor type 0 first

* Remove slotmap submodule and integrate header directly after submodule URL changed

* Transform tagging for ObjGrass

* Adding autosave events. (#611)

* Adding autosave events. Dead simple.

* Update autosaving.c to include @recomp_event comments

* Prevent autosaves during minigames and fix holding powder keg on autosave load (#640)

* Update runtime for more accurate VI and switch to improved pacing RT64 branch (#644)

* Update runtime for more accurate VI and switch to improved pacing RT64 branch

* Update N64ModernRuntime and RT64 after merge

* Update recompiler to match runtime symbol list

* Remove unused gibdo patch file

---------

Co-authored-by: Darío <dariosamo@gmail.com>
Co-authored-by: Reonu <15913880+Reonu@users.noreply.github.com>
This commit is contained in:
Wiseguy
2025-08-15 21:31:57 -04:00
committed by GitHub
parent 46d9e92dda
commit b5360b0546
23 changed files with 1070 additions and 44 deletions

View File

@@ -9,7 +9,7 @@ on:
N64RECOMP_COMMIT:
type: string
required: false
default: '989a86b36912403cd323de884bf834f2605ea770'
default: 'a13e5cff96686776b0e03baf23923e3c1927b770'
DXC_CHECKSUM:
type: string
required: false

View File

@@ -291,7 +291,7 @@ if (WIN32)
)
target_sources(Zelda64Recompiled PRIVATE ${CMAKE_SOURCE_DIR}/icons/app.rc)
target_link_libraries(Zelda64Recompiled PRIVATE SDL2)
target_link_libraries(Zelda64Recompiled PRIVATE SDL2 Winmm.lib)
endif()
if (APPLE)

View File

@@ -48,7 +48,7 @@ A GPU supporting Direct3D 12.0 (Shader Model 6), Vulkan 1.2, or Metal Argument B
* Intel HD 510 (Skylake)
* A Mac with Apple Silicon or an Intel 7th Gen CPU with MacOS 13.0+
On x86-64 PCs, a CPU supporting the AVX instruction set is also required (Intel Core 2000 series or AMD Bulldozer and newer). ARM64 builds will work on any ARM64 CPU.
On x86-64 PCs, a CPU supporting the SSE4.1 instruction set is also required (Intel Core 2 Penryn series or AMD Bulldozer and newer). ARM64 builds will work on any ARM64 CPU.
If you have issues with crashes on startup, make sure your graphics drivers are fully up to date.

View File

@@ -27,7 +27,7 @@ namespace zelda64 {
void enable_instant_present() override;
void send_dl(const OSTask *task) override;
void update_screen(uint32_t vi_origin) override;
void update_screen() override;
void shutdown() override;
uint32_t get_display_framerate() const override;
float get_resolution_scale() const override;

View File

@@ -7,7 +7,8 @@
"project": "CMakeLists.txt",
"projectTarget": "Zelda64Recompiled.exe",
"name": "Zelda64Recompiled.exe",
"currentDir": "${workspaceRoot}"
"currentDir": "${workspaceRoot}",
"args": ["--show-console"]
}
]
}

View File

@@ -76,6 +76,17 @@ void camera_post_play_update(PlayState* play) {
if (force_interpolation) {
force_camera_interpolation();
}
// Dedicated section for workarounds where the heuristic fails to detect small camera teleports.
bool force_no_interpolation = false;
// Music Box House. The camera gets teleported by a very small amount when Link gets the Gibdo mask.
if (play->sceneId == SCENE_MUSICHOUSE && play->csCtx.scriptIndex == 2 && play->csCtx.curFrame == 525 && active_cam->setting == CAM_SET_FREE0) {
force_no_interpolation = true;
}
if (force_no_interpolation) {
force_camera_skip_interpolation();
}
}
}
}

View File

@@ -14,3 +14,33 @@ symbols = [
{ name = "FileSelect_Init_NORELOCATE", vram = 0x80813C98 },
{ name = "DayTelop_Init_NORELOCATE", vram = 0x80815820 },
]
[[section]]
name = "..ovl_En_Hg"
rom = 0x01034170
vram = 0x80BCF1D0
size = 0x10E0
symbols = [
{ name = "sPamelasFatherGibdoAnimationInfo", vram = 0x80bd0008 },
]
[[section]]
name = "..ovl_En_Hgo"
rom = 0x01035250
vram = 0x80BD02B0
size = 0xF30
symbols = [
{ name = "sPamelasFatherHumanAnimationInfo", vram = 0x80BD0EA0 },
]
[[section]]
name = "..ovl_En_Pamera"
rom = 0x0103D250
vram = 0x80BD82B0
size = 0x2780
symbols = [
{ name = "sPamelaAnimationInfo", vram = 0x80BDA4B8 },
]

View File

@@ -0,0 +1,45 @@
typedef enum PamelasFatherGibdoLimb {
/* 0x00 */ PAMELAS_FATHER_GIBDO_LIMB_NONE,
/* 0x01 */ PAMELAS_FATHER_GIBDO_LIMB_ROOT,
/* 0x02 */ PAMELAS_FATHER_GIBDO_LIMB_ABDOMEN,
/* 0x03 */ PAMELAS_FATHER_GIBDO_LIMB_CHEST,
/* 0x04 */ PAMELAS_FATHER_GIBDO_LIMB_LEFT_UPPER_ARM,
/* 0x05 */ PAMELAS_FATHER_GIBDO_LIMB_LEFT_FOREARM,
/* 0x06 */ PAMELAS_FATHER_GIBDO_LIMB_LEFT_HAND,
/* 0x07 */ PAMELAS_FATHER_GIBDO_LIMB_RIGHT_UPPER_ARM,
/* 0x08 */ PAMELAS_FATHER_GIBDO_LIMB_RIGHT_FOREARM,
/* 0x09 */ PAMELAS_FATHER_GIBDO_LIMB_RIGHT_HAND,
/* 0x0A */ PAMELAS_FATHER_GIBDO_LIMB_EYEBROWS,
/* 0x0B */ PAMELAS_FATHER_GIBDO_LIMB_HEAD,
/* 0x0C */ PAMELAS_FATHER_GIBDO_LIMB_PELVIS,
/* 0x0D */ PAMELAS_FATHER_GIBDO_LIMB_LEFT_THIGH,
/* 0x0E */ PAMELAS_FATHER_GIBDO_LIMB_LEFT_SHIN,
/* 0x0F */ PAMELAS_FATHER_GIBDO_LIMB_LEFT_FOOT,
/* 0x10 */ PAMELAS_FATHER_GIBDO_LIMB_RIGHT_THIGH,
/* 0x11 */ PAMELAS_FATHER_GIBDO_LIMB_RIGHT_SHIN,
/* 0x12 */ PAMELAS_FATHER_GIBDO_LIMB_RIGHT_FOOT,
/* 0x13 */ PAMELAS_FATHER_GIBDO_LIMB_MAX
} PamelasFatherGibdoLimb;
typedef enum PamelasFatherHumanLimb {
/* 0x00 */ PAMELAS_FATHER_HUMAN_LIMB_NONE,
/* 0x01 */ PAMELAS_FATHER_HUMAN_LIMB_ROOT,
/* 0x02 */ PAMELAS_FATHER_HUMAN_LIMB_ABDOMEN,
/* 0x03 */ PAMELAS_FATHER_HUMAN_LIMB_CHEST,
/* 0x04 */ PAMELAS_FATHER_HUMAN_LIMB_LEFT_UPPER_ARM,
/* 0x05 */ PAMELAS_FATHER_HUMAN_LIMB_LEFT_FOREARM,
/* 0x06 */ PAMELAS_FATHER_HUMAN_LIMB_LEFT_HAND,
/* 0x07 */ PAMELAS_FATHER_HUMAN_LIMB_RIGHT_UPPER_ARM,
/* 0x08 */ PAMELAS_FATHER_HUMAN_LIMB_RIGHT_FOREARM,
/* 0x09 */ PAMELAS_FATHER_HUMAN_LIMB_RIGHT_HAND,
/* 0x0A */ PAMELAS_FATHER_HUMAN_LIMB_EYEBROWS,
/* 0x0B */ PAMELAS_FATHER_HUMAN_LIMB_HEAD,
/* 0x0C */ PAMELAS_FATHER_HUMAN_LIMB_PELVIS,
/* 0x0D */ PAMELAS_FATHER_HUMAN_LIMB_LEFT_THIGH,
/* 0x0E */ PAMELAS_FATHER_HUMAN_LIMB_LEFT_SHIN,
/* 0x0F */ PAMELAS_FATHER_HUMAN_LIMB_LEFT_FOOT,
/* 0x10 */ PAMELAS_FATHER_HUMAN_LIMB_RIGHT_THIGH,
/* 0x11 */ PAMELAS_FATHER_HUMAN_LIMB_RIGHT_SHIN,
/* 0x12 */ PAMELAS_FATHER_HUMAN_LIMB_RIGHT_FOOT,
/* 0x13 */ PAMELAS_FATHER_HUMAN_LIMB_MAX
} PamelasFatherHumanLimb;

View File

@@ -0,0 +1,26 @@
typedef enum PamelaLimb {
/* 0x00 */ PAMELA_LIMB_NONE,
/* 0x01 */ PAMELA_LIMB_ROOT,
/* 0x02 */ PAMELA_LIMB_UPPER_BODY_ROOT,
/* 0x03 */ PAMELA_LIMB_LEFT_UPPER_ARM,
/* 0x04 */ PAMELA_LIMB_LEFT_FOREARM,
/* 0x05 */ PAMELA_LIMB_LEFT_HAND,
/* 0x06 */ PAMELA_LIMB_RIGHT_UPPER_ARM,
/* 0x07 */ PAMELA_LIMB_RIGHT_FOREARM,
/* 0x08 */ PAMELA_LIMB_RIGHT_HAND,
/* 0x09 */ PAMELA_LIMB_HEAD,
/* 0x0A */ PAMELA_LIMB_HAIR_END,
/* 0x0B */ PAMELA_LIMB_CHEST,
/* 0x0C */ PAMELA_LIMB_NECK,
/* 0x0D */ PAMELA_LIMB_LEFT_THIGH,
/* 0x0E */ PAMELA_LIMB_LEFT_LEG,
/* 0x0F */ PAMELA_LIMB_LEFT_FOOT,
/* 0x10 */ PAMELA_LIMB_RIGHT_THIGH,
/* 0x11 */ PAMELA_LIMB_RIGHT_LEG,
/* 0x12 */ PAMELA_LIMB_RIGHT_FOOT,
/* 0x13 */ PAMELA_LIMB_FRONT_DRESS,
/* 0x14 */ PAMELA_LIMB_BACK_DRESS,
/* 0x15 */ PAMELA_LIMB_ABDOMEN,
/* 0x16 */ PAMELA_LIMB_PELVIS,
/* 0x17 */ PAMELA_LIMB_MAX
} PamelaLimb;

View File

@@ -0,0 +1,107 @@
#include "patches.h"
#include "transform_ids.h"
#include "overlays/actors/ovl_En_Hgo/z_en_hgo.h"
typedef enum {
/* 0 */ HGO_ANIM_ARMS_FOLDED,
/* 1 */ HGO_ANIM_ASTONISHED,
/* 2 */ HGO_ANIM_KNEEL_DOWN_AND_HUG,
/* 3 */ HGO_ANIM_CONSOLE,
/* 4 */ HGO_ANIM_CONSOLE_HEAD_UP,
/* 5 */ HGO_ANIM_REACH_DOWN_TO_LIFT,
/* 6 */ HGO_ANIM_TOSS,
/* 7 */ HGO_ANIM_MAX
} HgoAnimation;
extern AnimationInfo sPamelasFatherHumanAnimationInfo[];
extern void EnHgo_Draw(Actor* thisx, PlayState* play);
extern void EnHgo_DoNothing(EnHgo* this, PlayState* play);
extern void EnHgo_SetupInitCollision(EnHgo* this);
// @recomp Skip interpolation when the animations change during the cutscene, as the
// animation changes are meant to happen at the same time as the camera cuts.
RECOMP_PATCH s32 EnHgo_HandleCsAction(EnHgo* this, PlayState* play) {
s32 cueChannel;
if (Cutscene_IsCueInChannel(play, CS_CMD_ACTOR_CUE_486)) {
cueChannel = Cutscene_GetCueChannel(play, CS_CMD_ACTOR_CUE_486);
if (this->cueId != play->csCtx.actorCues[cueChannel]->id) {
this->cueId = play->csCtx.actorCues[cueChannel]->id;
switch (play->csCtx.actorCues[cueChannel]->id) {
case 1:
this->animIndex = HGO_ANIM_ARMS_FOLDED;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_ARMS_FOLDED);
break;
case 2:
this->actor.draw = EnHgo_Draw;
this->animIndex = HGO_ANIM_ASTONISHED;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_ASTONISHED);
break;
case 3:
this->animIndex = HGO_ANIM_KNEEL_DOWN_AND_HUG;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_KNEEL_DOWN_AND_HUG);
break;
case 4:
this->animIndex = HGO_ANIM_CONSOLE;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_CONSOLE);
break;
case 5:
this->animIndex = HGO_ANIM_CONSOLE_HEAD_UP;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_CONSOLE_HEAD_UP);
break;
case 6:
this->animIndex = HGO_ANIM_REACH_DOWN_TO_LIFT;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_REACH_DOWN_TO_LIFT);
break;
default:
break;
}
actor_set_interpolation_skipped(&this->actor);
} else if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) {
switch (this->animIndex) {
case HGO_ANIM_ASTONISHED:
if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame) && !this->isInCutscene) {
this->isInCutscene = true;
if ((gSaveContext.sceneLayer == 0) &&
((play->csCtx.scriptIndex == 2) || (play->csCtx.scriptIndex == 4))) {
Actor_PlaySfx(&this->actor, NA_SE_VO_GBVO02);
}
}
break;
case HGO_ANIM_KNEEL_DOWN_AND_HUG:
this->animIndex = HGO_ANIM_CONSOLE;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_CONSOLE);
break;
case HGO_ANIM_REACH_DOWN_TO_LIFT:
this->animIndex = HGO_ANIM_TOSS;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_TOSS);
default:
break;
}
}
Cutscene_ActorTranslateAndYaw(&this->actor, play, cueChannel);
return true;
}
if ((play->csCtx.state == CS_STATE_IDLE) && CHECK_WEEKEVENTREG(WEEKEVENTREG_75_20) &&
(this->actionFunc == EnHgo_DoNothing)) {
this->actor.shape.rot.y = this->actor.world.rot.y;
Actor_Spawn(&play->actorCtx, play, ACTOR_ELF_MSG2, this->actor.focus.pos.x, this->actor.focus.pos.y,
this->actor.focus.pos.z, 7, 0, 0, 0x7F5A);
EnHgo_SetupInitCollision(this);
}
this->cueId = 99;
return false;
}

View File

@@ -0,0 +1,26 @@
#include "patches.h"
#include "transform_ids.h"
#include "overlays/actors/ovl_Dm_Char05/z_dm_char05.h"
extern void func_80AADF54(PlayState* play, DmChar05* this);
// @recomp Patched to avoid an interpolation glitch in Pamela's dad's cutscene
// that happens when the mask is meant to teleport offscreen.
RECOMP_PATCH void func_80AADB4C(Actor* thisx, PlayState* play) {
DmChar05* this = (DmChar05*)thisx;
if (this->unk_18E == 0) {
if (Cutscene_IsCueInChannel(play, CS_CMD_ACTOR_CUE_518) &&
(play->csCtx.actorCues[Cutscene_GetCueChannel(play, CS_CMD_ACTOR_CUE_518)]->id != 1)) {
// @recomp During this cue the mask does nothing other than teleport offscreen and stay still,
// so we can just skip interpolation the entire time.
if (play->csCtx.actorCues[Cutscene_GetCueChannel(play, CS_CMD_ACTOR_CUE_518)]->id == 3) {
actor_set_interpolation_skipped(thisx);
}
Gfx_SetupDL25_Opa(play->state.gfxCtx);
SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
this->skelAnime.dListCount, NULL, NULL, &this->actor);
}
} else if (this->unk_18E == 1) {
func_80AADF54(play, this);
}
}

View File

@@ -0,0 +1,74 @@
#include "patches.h"
#include "transform_ids.h"
#include "overlays/actors/ovl_En_Pamera/z_en_pamera.h"
extern void EnPamera_Draw(Actor* thisx, PlayState* play);
extern void func_80BD9E88(EnPamera* this);
extern void func_80BD9EE0(EnPamera* this);
extern void func_80BDA038(EnPamera* this);
extern void func_80BDA0A0(EnPamera* this);
extern void func_80BDA170(EnPamera* this);
extern void func_80BDA288(EnPamera* this);
extern void func_80BD994C(EnPamera* this, PlayState* play);
extern void EnPamera_HandleDialogue(EnPamera* this, PlayState* play);
extern void func_80BD9904(EnPamera* this);
extern void func_80BD9E60(EnPamera* this);
// @recomp Skip interpolation when the animations change during the cutscene, as the
// animation changes are meant to happen at the same time as the camera cuts.
RECOMP_PATCH s32 func_80BD9CB8(EnPamera* this, PlayState* play) {
s32 cueChannel;
if (Cutscene_IsCueInChannel(play, CS_CMD_ACTOR_CUE_485)) {
cueChannel = Cutscene_GetCueChannel(play, CS_CMD_ACTOR_CUE_485);
if (this->cueId != play->csCtx.actorCues[cueChannel]->id) {
this->cueId = play->csCtx.actorCues[cueChannel]->id;
switch (play->csCtx.actorCues[cueChannel]->id) {
case 1:
func_80BD9E88(this);
break;
case 2:
if (this->actor.draw == NULL) {
this->actor.draw = EnPamera_Draw;
this->actor.flags |= ACTOR_FLAG_TARGETABLE;
}
func_80BD9EE0(this);
break;
case 3:
func_80BDA038(this);
break;
case 4:
func_80BDA0A0(this);
break;
case 5:
func_80BDA170(this);
break;
case 6:
func_80BDA288(this);
break;
default:
break;
}
actor_set_interpolation_skipped(&this->actor);
}
Cutscene_ActorTranslateAndYaw(&this->actor, play, cueChannel);
this->setupFunc(this, play);
return true;
}
if ((play->csCtx.state == CS_STATE_IDLE) && CHECK_WEEKEVENTREG(WEEKEVENTREG_75_20)) {
if ((this->actionFunc != func_80BD994C) && (this->actionFunc != EnPamera_HandleDialogue)) {
this->actor.shape.rot.y = this->actor.world.rot.y;
func_80BD9904(this);
func_80BD9E60(this);
}
}
this->cueId = 99;
return false;
}

View File

@@ -185,3 +185,6 @@ RECOMP_PATCH void Player_DrawGameplay(PlayState* play, Player* this, s32 lod, Gf
CLOSE_DISPS(play->state.gfxCtx);
}
RECOMP_EXPORT u32 z64recomp_get_bowstring_transform_id() {
return BOWSTRING_TRANSFORM_ID;
}

View File

@@ -12,6 +12,8 @@
#include "overlays/actors/ovl_En_Twig/z_en_twig.h"
#include "overlays/actors/ovl_En_Honotrap/z_en_honotrap.h"
#include "overlays/actors/ovl_En_Tanron1/z_en_tanron1.h"
#include "overlays/actors/ovl_En_Kusa2/z_en_kusa2.h"
#include "overlays/actors/ovl_Obj_Grass/z_obj_grass.h"
// Decomp renames, TODO update decomp and remove these
#define EnHonotrap_FlameGroup func_8092F878
@@ -1331,3 +1333,154 @@ RECOMP_PATCH void func_80BB5AAC(EnTanron1* this, PlayState* play) {
CLOSE_DISPS(play->state.gfxCtx);
}
extern Gfx gKakeraLeafTipDL[];
extern Gfx gKakeraLeafMiddleDL[];
extern EnKusa2UnkBssStruct D_80A5F1C0;
// Patched to tag the particles that spawn from Keaton grass.
RECOMP_PATCH void func_80A5E6F0(Actor* thisx, PlayState* play) {
static Gfx* D_80A5EB68[] = {
gKakeraLeafTipDL,
gKakeraLeafMiddleDL,
};
EnKusa2* this = (EnKusa2*)thisx;
s32 i;
OPEN_DISPS(play->state.gfxCtx);
Gfx_SetupDL25_Opa(play->state.gfxCtx);
// @recomp Get the base transform ID for this actor.
u32 cur_transform_id = actor_transform_id(thisx);
for (i = 0; i < ARRAY_COUNT(D_80A5F1C0.unk_0480); i++) {
EnKusa2UnkBssSubStruct2* s = &D_80A5F1C0.unk_0480[i];
if (s->unk_2C > 0) {
Matrix_SetTranslateRotateYXZ(s->unk_04.x, s->unk_04.y, s->unk_04.z, &s->unk_20);
Matrix_Scale(s->unk_00, s->unk_00, s->unk_00, MTXMODE_APPLY);
// @recomp Create a matrix group for this particle.
gEXMatrixGroupDecomposedNormal(POLY_OPA_DISP++, cur_transform_id + i, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, D_80A5EB68[i & 1]);
// @recomp Pop the matrix group.
gEXPopMatrixGroup(POLY_OPA_DISP++, G_MTX_MODELVIEW);
}
}
CLOSE_DISPS(play->state.gfxCtx);
}
extern Gfx gObjGrass_D_809AA9F0[];
extern Gfx gObjGrass_D_809AAA68[];
extern Gfx gObjGrass_D_809AAAE0[];
void ObjGrass_OverrideMatrixCurrent(MtxF* matrix);
// @recomp Patched to set matrix groups for grass.
RECOMP_PATCH void ObjGrass_DrawOpa(Actor* thisx, PlayState* play2) {
ObjGrass* this = (ObjGrass*)thisx;
PlayState* play = play2;
Lights* lights;
ObjGrassGroup* grassGroup;
s32 i;
s32 j;
Vec3s rot = { 0, 0, 0 };
ObjGrassElement* grassElem;
OPEN_DISPS(play->state.gfxCtx);
Gfx_SetupDL25_Opa(play->state.gfxCtx);
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255);
gSPDisplayList(POLY_OPA_DISP++, gObjGrass_D_809AA9F0);
// @recomp Extract this actor's ID.
u32 actor_id = actor_transform_id(thisx);
for (i = 0; i < this->activeGrassGroups; i++) {
grassGroup = &this->grassGroups[i];
if (grassGroup->flags & OBJ_GRASS_GROUP_DRAW) {
lights = LightContext_NewLights(&play->lightCtx, play->state.gfxCtx);
Lights_BindAll(lights, play->lightCtx.listHead, &grassGroup->homePos, play);
Lights_Draw(lights, play->state.gfxCtx);
for (j = 0; j < grassGroup->count; j++) {
grassElem = &grassGroup->elements[j];
if ((grassElem->flags & OBJ_GRASS_ELEM_DRAW) && (grassElem->alpha == 255)) {
rot.y = grassElem->rotY;
Matrix_SetTranslateRotateYXZ(grassElem->pos.x, grassElem->pos.y, grassElem->pos.z, &rot);
Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY);
if (grassElem->flags & OBJ_GRASS_ELEM_ANIM) {
ObjGrass_OverrideMatrixCurrent(&this->distortionMtx[j]);
}
// @recomp Push a matrix group.
gEXMatrixGroupDecomposedNormal(POLY_OPA_DISP++, actor_id + i * OBJ_GRASS_GROUP_ELEM_COUNT_MAX + j, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, gObjGrass_D_809AAAE0);
// @recomp Pop the matrix group.
gEXPopMatrixGroup(POLY_OPA_DISP++, G_MTX_MODELVIEW);
}
}
}
}
CLOSE_DISPS(play->state.gfxCtx);
}
// @recomp Patched to set matrix groups for grass.
RECOMP_PATCH void ObjGrass_DrawXlu(Actor* thisx, PlayState* play) {
ObjGrass* this = (ObjGrass*)thisx;
ObjGrassGroup* grassGroup;
ObjGrassElement* grassElem;
s32 i;
s32 j;
Vec3s rot = { 0, 0, 0 };
OPEN_DISPS(play->state.gfxCtx);
Gfx_SetupDL25_Xlu(play->state.gfxCtx);
gSPDisplayList(POLY_XLU_DISP++, gObjGrass_D_809AAA68);
// @recomp Extract this actor's ID.
u32 actor_id = actor_transform_id(thisx);
for (i = 0; i < this->activeGrassGroups; i++) {
grassGroup = &this->grassGroups[i];
if (grassGroup->flags & OBJ_GRASS_GROUP_DRAW) {
for (j = 0; j < grassGroup->count; j++) {
grassElem = &grassGroup->elements[j];
if ((grassElem->flags & OBJ_GRASS_ELEM_DRAW) && (grassElem->alpha > 0) && (grassElem->alpha < 255)) {
rot.y = grassElem->rotY;
Matrix_SetTranslateRotateYXZ(grassElem->pos.x, grassElem->pos.y, grassElem->pos.z, &rot);
Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY);
// @recomp Push a matrix group.
gEXMatrixGroupDecomposedNormal(POLY_XLU_DISP++, actor_id + i * OBJ_GRASS_GROUP_ELEM_COUNT_MAX + j, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, grassElem->alpha);
gSPDisplayList(POLY_XLU_DISP++, gObjGrass_D_809AAAE0);
// @recomp Pop the matrix group.
gEXPopMatrixGroup(POLY_XLU_DISP++, G_MTX_MODELVIEW);
}
}
}
}
CLOSE_DISPS(play->state.gfxCtx);
}

View File

@@ -0,0 +1,509 @@
#include "patches.h"
#define VTX_EX_T(x, y, z, s, t, cr, cg, cb, a, px, py, pz) \
{ { x, y, z }, 0, { s, t }, { cr, cg, cb, a }, {px, py, pz} }
void EffectBlure_GetComputedValues(EffectBlure* this, s32 index, f32 ratio, Vec3s* vec1, Vec3s* vec2,
Color_RGBA8* color1, Color_RGBA8* color2);
void EffectBlure_DrawSimple(EffectBlure* this2, GraphicsContext* gfxCtx);
void EffectBlure_DrawSmooth(EffectBlure* this2, GraphicsContext* gfxCtx);
// @recomp Patched to interpolate the vertices towards the front of the trail section if this is the last trail being drawn currently.
RECOMP_PATCH void EffectBlure_DrawElemNoInterpolation(EffectBlure* this, EffectBlureElement* elem, s32 index,
GraphicsContext* gfxCtx) {
// @recomp Change baseVtx to a VertexEX.
static VertexEXColor baseVtx = VTX_EX_T(/* pos */ 0, 0, 0, /* st */ 0, 0, /* color */ 255, 255, 255, 255, /* prev pos */ 0, 0, 0);
// @recomp Change the vertex type to VertexEX.
VertexEX* vtx;
Vec3s sp8C;
Vec3s sp84;
f32 ratio;
Color_RGBA8 sp7C;
Color_RGBA8 sp78;
Vec3f sp6C;
Vec3f sp60;
Vec3f sp54;
OPEN_DISPS(gfxCtx);
Math_Vec3s_ToVec3f(&sp6C, &this->elements[0].p2);
// @recomp Debug print.
// recomp_printf("No interpolation index %d:\n"
// " Blure: calcMode %08X flags: %04X addAngle %04X addAngle %04X elemDuration %d\n"
// " Element: state %08X timer: %d flags %04X\n",
// index,
// this->calcMode, this->flags, (u16)this->addAngleChange, (u16)this->addAngle, this->elemDuration,
// elem->state, elem->timer, elem->flags);
// @recomp Allocate using the size of VertexEX instead.
vtx = GRAPH_ALLOC(gfxCtx, 4 * sizeof(VertexEX));
if (vtx == NULL) {
} else {
vtx[0].v = baseVtx;
vtx[1].v = baseVtx;
vtx[2].v = baseVtx;
vtx[3].v = baseVtx;
ratio = (f32)elem->timer / (f32)this->elemDuration;
EffectBlure_GetComputedValues(this, index, ratio, &sp8C, &sp84, &sp7C, &sp78);
sp60.x = sp84.x;
sp60.y = sp84.y;
sp60.z = sp84.z;
Math_Vec3f_Diff(&sp60, &sp6C, &sp54);
Math_Vec3f_Scale(&sp54, 10.0f);
vtx[0].v.ob[0] = sp54.x;
vtx[0].v.ob[1] = sp54.y;
vtx[0].v.ob[2] = sp54.z;
vtx[0].v.cn[0] = sp78.r;
vtx[0].v.cn[1] = sp78.g;
vtx[0].v.cn[2] = sp78.b;
vtx[0].v.cn[3] = sp78.a;
sp60.x = sp8C.x;
sp60.y = sp8C.y;
sp60.z = sp8C.z;
Math_Vec3f_Diff(&sp60, &sp6C, &sp54);
Math_Vec3f_Scale(&sp54, 10.0f);
vtx[1].v.ob[0] = sp54.x;
vtx[1].v.ob[1] = sp54.y;
vtx[1].v.ob[2] = sp54.z;
vtx[1].v.cn[0] = sp7C.r;
vtx[1].v.cn[1] = sp7C.g;
vtx[1].v.cn[2] = sp7C.b;
vtx[1].v.cn[3] = sp7C.a;
ratio = (f32)(elem + 1)->timer / (f32)this->elemDuration;
EffectBlure_GetComputedValues(this, index + 1, ratio, &sp8C, &sp84, &sp7C, &sp78);
sp60.x = sp8C.x;
sp60.y = sp8C.y;
sp60.z = sp8C.z;
Math_Vec3f_Diff(&sp60, &sp6C, &sp54);
Math_Vec3f_Scale(&sp54, 10.0f);
vtx[2].v.ob[0] = sp54.x;
vtx[2].v.ob[1] = sp54.y;
vtx[2].v.ob[2] = sp54.z;
vtx[2].v.cn[0] = sp7C.r;
vtx[2].v.cn[1] = sp7C.g;
vtx[2].v.cn[2] = sp7C.b;
vtx[2].v.cn[3] = sp7C.a;
sp60.x = sp84.x;
sp60.y = sp84.y;
sp60.z = sp84.z;
Math_Vec3f_Diff(&sp60, &sp6C, &sp54);
Math_Vec3f_Scale(&sp54, 10.0f);
vtx[3].v.ob[0] = sp54.x;
vtx[3].v.ob[1] = sp54.y;
vtx[3].v.ob[2] = sp54.z;
vtx[3].v.cn[0] = sp78.r;
vtx[3].v.cn[1] = sp78.g;
vtx[3].v.cn[2] = sp78.b;
vtx[3].v.cn[3] = sp78.a;
// @recomp Set the previous position of the first two vertices to their current position.
vtx[0].v.obp[0] = vtx[0].v.ob[0];
vtx[0].v.obp[1] = vtx[0].v.ob[1];
vtx[0].v.obp[2] = vtx[0].v.ob[2];
vtx[1].v.obp[0] = vtx[1].v.ob[0];
vtx[1].v.obp[1] = vtx[1].v.ob[1];
vtx[1].v.obp[2] = vtx[1].v.ob[2];
// @recomp If this trail just spawned (timer == 2), set the previous vertex positions for the last two vertices to the positions of the last two (interpolation).
// Otherwise, set them to the current position of the respective vertex (no interpolation).
if (elem->timer == 2) {
// Vertex 2 interpolates from a start position equal to the position of vertex 1.
vtx[2].v.obp[0] = vtx[1].v.ob[0];
vtx[2].v.obp[1] = vtx[1].v.ob[1];
vtx[2].v.obp[2] = vtx[1].v.ob[2];
// Vertex 3 interpolates from a start position equal to the position of vertex 0.
vtx[3].v.obp[0] = vtx[0].v.ob[0];
vtx[3].v.obp[1] = vtx[0].v.ob[1];
vtx[3].v.obp[2] = vtx[0].v.ob[2];
}
else {
vtx[2].v.obp[0] = vtx[2].v.ob[0];
vtx[2].v.obp[1] = vtx[2].v.ob[1];
vtx[2].v.obp[2] = vtx[2].v.ob[2];
vtx[3].v.obp[0] = vtx[3].v.ob[0];
vtx[3].v.obp[1] = vtx[3].v.ob[1];
vtx[3].v.obp[2] = vtx[3].v.ob[2];
}
// @recomp Use gEXVertex in place of gSPVertex.
gEXVertex(POLY_XLU_DISP++, vtx, 4, 0);
gSP2Triangles(POLY_XLU_DISP++, 0, 1, 2, 0, 0, 2, 3, 0);
}
CLOSE_DISPS(gfxCtx);
}
// @recomp Patched to interpolate the vertices towards the front of the trail section if this is the last trail being drawn currently.
RECOMP_PATCH void EffectBlure_DrawElemHermiteInterpolation(EffectBlure* this, EffectBlureElement* elem, s32 index,
GraphicsContext* gfxCtx) {
// @recomp Change baseVtx to a VertexEX.
static VertexEXColor baseVtx = VTX_EX_T(/* pos */ 0, 0, 0, /* st */ 0, 0, /* color */ 255, 255, 255, 255, /* prev pos */ 0, 0, 0);
// @recomp Change the vertex type to VertexEX.
VertexEX* vtx;
Vec3s sp1EC;
Vec3s sp1E4;
f32 ratio;
Color_RGBA8 sp1DC;
Color_RGBA8 sp1D8;
Vec3f sp1CC;
Vec3f sp1C0;
Vec3f sp1B4;
Vec3f sp1A8;
Color_RGBA8 sp1A4;
Color_RGBA8 sp1A0;
Color_RGBA8 sp19C;
Color_RGBA8 sp198;
Vec3f sp18C;
Vec3f sp180;
Vec3f sp174;
Vec3f sp168;
s32 i;
Vec3f sp158;
Vec3f sp14C;
Color_RGBA8 sp148;
Color_RGBA8 sp144;
Vec3f sp138;
// @recomp Debug print.
// recomp_printf("Hermite interpolation index %d:\n"
// " Blure: calcMode %08X flags: %04X addAngle %04X addAngle %04X elemDuration %d\n"
// " Element: state %08X timer: %d flags %04X\n",
// index,
// this->calcMode, this->flags, (u16)this->addAngleChange, (u16)this->addAngle, this->elemDuration,
// elem->state, elem->timer, elem->flags);
OPEN_DISPS(gfxCtx);
Math_Vec3s_ToVec3f(&sp138, &this->elements[0].p2);
ratio = (f32)elem->timer / (f32)this->elemDuration;
EffectBlure_GetComputedValues(this, index, ratio, &sp1EC, &sp1E4, &sp1A4, &sp1A0);
Math_Vec3s_ToVec3f(&sp1CC, &sp1EC);
Math_Vec3s_ToVec3f(&sp1C0, &sp1E4);
ratio = (f32)(elem + 1)->timer / (f32)this->elemDuration;
EffectBlure_GetComputedValues(this, index + 1, ratio, &sp1EC, &sp1E4, &sp19C, &sp198);
Math_Vec3s_ToVec3f(&sp18C, &sp1EC);
Math_Vec3s_ToVec3f(&sp180, &sp1E4);
if ((elem->flags & (EFFECT_BLURE_ELEMENT_FLAG_1 | EFFECT_BLURE_ELEMENT_FLAG_2)) == EFFECT_BLURE_ELEMENT_FLAG_2) {
Math_Vec3f_Diff(&sp18C, &sp1CC, &sp1B4);
Math_Vec3f_Diff(&sp180, &sp1C0, &sp1A8);
} else {
Vec3f sp118;
Vec3f sp10C;
ratio = (f32)(elem - 1)->timer / (f32)this->elemDuration;
EffectBlure_GetComputedValues(this, index - 1, ratio, &sp1EC, &sp1E4, &sp1DC, &sp1D8);
Math_Vec3s_ToVec3f(&sp118, &sp1EC);
Math_Vec3s_ToVec3f(&sp10C, &sp1E4);
Math_Vec3f_Diff(&sp18C, &sp118, &sp1B4);
Math_Vec3f_Diff(&sp180, &sp10C, &sp1A8);
}
Math_Vec3f_Scale(&sp1B4, 0.5f);
Math_Vec3f_Scale(&sp1A8, 0.5f);
if (((elem + 1)->flags & (EFFECT_BLURE_ELEMENT_FLAG_1 | EFFECT_BLURE_ELEMENT_FLAG_2)) ==
EFFECT_BLURE_ELEMENT_FLAG_2) {
Math_Vec3f_Diff(&sp18C, &sp1CC, &sp174);
Math_Vec3f_Diff(&sp180, &sp1C0, &sp168);
} else {
Vec3f sp100;
Vec3f spF4;
ratio = (f32)(elem + 2)->timer / (f32)this->elemDuration;
EffectBlure_GetComputedValues(this, index + 2, ratio, &sp1EC, &sp1E4, &sp1DC, &sp1D8);
Math_Vec3s_ToVec3f(&sp100, &sp1EC);
Math_Vec3s_ToVec3f(&spF4, &sp1E4);
Math_Vec3f_Diff(&sp100, &sp1CC, &sp174);
Math_Vec3f_Diff(&spF4, &sp1C0, &sp168);
}
Math_Vec3f_Scale(&sp174, 0.5f);
Math_Vec3f_Scale(&sp168, 0.5f);
// @recomp Allocate using the size of VertexEX instead.
vtx = GRAPH_ALLOC(gfxCtx, 16 * sizeof(VertexEX));
if (vtx == NULL) {
} else {
Math_Vec3f_Diff(&sp1CC, &sp138, &sp158);
Math_Vec3f_Scale(&sp158, 10.0f);
Math_Vec3f_Diff(&sp1C0, &sp138, &sp14C);
Math_Vec3f_Scale(&sp14C, 10.0f);
Color_RGBA8_Copy(&sp148, &sp1A4);
Color_RGBA8_Copy(&sp144, &sp1A0);
vtx[0].v = baseVtx;
vtx[1].v = baseVtx;
vtx[0].v.ob[0] = Math_FNearbyIntF(sp158.x);
vtx[0].v.ob[1] = Math_FNearbyIntF(sp158.y);
vtx[0].v.ob[2] = Math_FNearbyIntF(sp158.z);
vtx[0].v.cn[0] = sp148.r;
vtx[0].v.cn[1] = sp148.g;
vtx[0].v.cn[2] = sp148.b;
vtx[0].v.cn[3] = sp148.a;
vtx[1].v.ob[0] = Math_FNearbyIntF(sp14C.x);
vtx[1].v.ob[1] = Math_FNearbyIntF(sp14C.y);
vtx[1].v.ob[2] = Math_FNearbyIntF(sp14C.z);
vtx[1].v.cn[0] = sp144.r;
vtx[1].v.cn[1] = sp144.g;
vtx[1].v.cn[2] = sp144.b;
vtx[1].v.cn[3] = sp144.a;
// @recomp Set the previous position of the first two vertices to their current position.
vtx[0].v.obp[0] = vtx[0].v.ob[0];
vtx[0].v.obp[1] = vtx[0].v.ob[1];
vtx[0].v.obp[2] = vtx[0].v.ob[2];
vtx[1].v.obp[0] = vtx[1].v.ob[0];
vtx[1].v.obp[1] = vtx[1].v.ob[1];
vtx[1].v.obp[2] = vtx[1].v.ob[2];
for (i = 1; i < 8; i++) {
s32 j1 = 2 * i;
s32 j2 = 2 * i + 1;
Vec3f spE0;
f32 temp_f28 = i / 7.0f; // t
f32 temp_f0 = SQ(temp_f28); // t^2
f32 temp_f2 = temp_f0 * temp_f28; // t^3
f32 temp_f20 = temp_f2 - temp_f0; // t^3 - t^2
f32 temp_f22 = temp_f2 - 2.0f * temp_f0 + temp_f28; // t^3 - 2t^2 + t
f32 temp_f24 = 2.0f * temp_f2 - temp_f0 * 3.0f + 1.0f; // 2t^3 - 3t^2 + 1
f32 temp_f26 = temp_f0 * 3.0f - 2.0f * temp_f2; // 3t^2 - 2t^3
s32 pad1;
s32 pad2;
// p = (2t^3 - 3t^2 + 1)p0 + (3t^2 - 2t^3)p1 + (t^3 - 2t^2 + t)m0 + (t^3 - t^2)m1
spE0.x = (temp_f24 * sp1CC.x) + (temp_f26 * sp18C.x) + (temp_f22 * sp1B4.x) + (temp_f20 * sp174.x);
spE0.y = (temp_f24 * sp1CC.y) + (temp_f26 * sp18C.y) + (temp_f22 * sp1B4.y) + (temp_f20 * sp174.y);
spE0.z = (temp_f24 * sp1CC.z) + (temp_f26 * sp18C.z) + (temp_f22 * sp1B4.z) + (temp_f20 * sp174.z);
Math_Vec3f_Diff(&spE0, &sp138, &sp158);
Math_Vec3f_Scale(&sp158, 10.0f);
spE0.x = (temp_f24 * sp1C0.x) + (temp_f26 * sp180.x) + (temp_f22 * sp1A8.x) + (temp_f20 * sp168.x);
spE0.y = (temp_f24 * sp1C0.y) + (temp_f26 * sp180.y) + (temp_f22 * sp1A8.y) + (temp_f20 * sp168.y);
spE0.z = (temp_f24 * sp1C0.z) + (temp_f26 * sp180.z) + (temp_f22 * sp1A8.z) + (temp_f20 * sp168.z);
Math_Vec3f_Diff(&spE0, &sp138, &sp14C);
Math_Vec3f_Scale(&sp14C, 10.0f);
vtx[j1].v = baseVtx;
vtx[j2].v = baseVtx;
vtx[j1].v.ob[0] = Math_FNearbyIntF(sp158.x);
vtx[j1].v.ob[1] = Math_FNearbyIntF(sp158.y);
vtx[j1].v.ob[2] = Math_FNearbyIntF(sp158.z);
vtx[j1].v.cn[0] = func_800B0A24(sp1A4.r, sp19C.r, temp_f28);
vtx[j1].v.cn[1] = func_800B0A24(sp1A4.g, sp19C.g, temp_f28);
vtx[j1].v.cn[2] = func_800B0A24(sp1A4.b, sp19C.b, temp_f28);
vtx[j1].v.cn[3] = func_800B0A24(sp1A4.a, sp19C.a, temp_f28);
vtx[j2].v.ob[0] = Math_FNearbyIntF(sp14C.x);
vtx[j2].v.ob[1] = Math_FNearbyIntF(sp14C.y);
vtx[j2].v.ob[2] = Math_FNearbyIntF(sp14C.z);
vtx[j2].v.cn[0] = func_800B0A24(sp1A0.r, sp198.r, temp_f28);
vtx[j2].v.cn[1] = func_800B0A24(sp1A0.g, sp198.g, temp_f28);
vtx[j2].v.cn[2] = func_800B0A24(sp1A0.b, sp198.b, temp_f28);
vtx[j2].v.cn[3] = func_800B0A24(sp1A0.a, sp198.a, temp_f28);
// @recomp If this trail just spawned (timer == 2), set the previous vertex positions for the remaining vertices to the positions of the first two (interpolation).
// Otherwise, set them to the current position of the respective vertex (no interpolation).
if (elem->timer == 2) {
vtx[j1].v.obp[0] = vtx[0].v.ob[0];
vtx[j1].v.obp[1] = vtx[0].v.ob[1];
vtx[j1].v.obp[2] = vtx[0].v.ob[2];
vtx[j2].v.obp[0] = vtx[1].v.ob[0];
vtx[j2].v.obp[1] = vtx[1].v.ob[1];
vtx[j2].v.obp[2] = vtx[1].v.ob[2];
}
else {
vtx[j1].v.obp[0] = vtx[j1].v.ob[0];
vtx[j1].v.obp[1] = vtx[j1].v.ob[1];
vtx[j1].v.obp[2] = vtx[j1].v.ob[2];
vtx[j2].v.obp[0] = vtx[j2].v.ob[0];
vtx[j2].v.obp[1] = vtx[j2].v.ob[1];
vtx[j2].v.obp[2] = vtx[j2].v.ob[2];
}
}
// @recomp Use gEXVertex in place of gSPVertex.
gEXVertex(POLY_XLU_DISP++, vtx, 16, 0);
gSP2Triangles(POLY_XLU_DISP++, 0, 1, 3, 0, 0, 3, 2, 0);
gSP2Triangles(POLY_XLU_DISP++, 2, 3, 5, 0, 2, 5, 4, 0);
gSP2Triangles(POLY_XLU_DISP++, 4, 5, 7, 0, 4, 7, 6, 0);
gSP2Triangles(POLY_XLU_DISP++, 6, 7, 9, 0, 6, 9, 8, 0);
gSP2Triangles(POLY_XLU_DISP++, 8, 9, 11, 0, 8, 11, 10, 0);
gSP2Triangles(POLY_XLU_DISP++, 10, 11, 13, 0, 10, 13, 12, 0);
gSP2Triangles(POLY_XLU_DISP++, 12, 13, 15, 0, 12, 15, 14, 0);
}
CLOSE_DISPS(gfxCtx);
}
// @recomp Patched to interpolate the vertices towards the front of the trail section if this is the last trail being drawn currently.
RECOMP_PATCH void EffectBlure_Draw(void* thisx, GraphicsContext* gfxCtx) {
EffectBlure* this = (EffectBlure*)thisx;
// @recomp Change the vertex type to VertexEX.
VertexEX* vtx;
EffectBlureElement* elem;
s32 i;
s32 j;
s32 phi_t2;
OPEN_DISPS(gfxCtx);
gSPMatrix(POLY_XLU_DISP++, &gIdentityMtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
if (this->numElements != 0) {
if (this->flags == 0) {
Gfx_SetupDL38_Xlu(gfxCtx);
gDPPipeSync(POLY_XLU_DISP++);
// @recomp Allocate using the size of VertexEX instead.
vtx = GRAPH_ALLOC(gfxCtx, 32 * sizeof(VertexEX));
if (vtx == NULL) {
} else {
j = 0;
for (i = 0; i < this->numElements; i++) {
elem = &this->elements[i];
// @recomp Debug print.
// recomp_printf("Flag 0 %d:\n"
// " Blure: calcMode %08X flags: %04X addAngle %04X addAngle %04X elemDuration %d\n"
// " Element: state %08X timer: %d flags %04X\n",
// i,
// this->calcMode, this->flags, (u16)this->addAngleChange, (u16)this->addAngle, this->elemDuration,
// elem->state, elem->timer, elem->flags);
if (elem->state == 1) {
f32 ratio = (f32)elem->timer / (f32)this->elemDuration;
switch (this->calcMode) {
case 1:
vtx[j].v.ob[0] = func_800B09D0(elem->p1.x, elem->p2.x, ratio);
vtx[j].v.ob[1] = func_800B09D0(elem->p1.y, elem->p2.y, ratio);
vtx[j].v.ob[2] = func_800B09D0(elem->p1.z, elem->p2.z, ratio);
vtx[j + 1].v.ob[0] = elem->p2.x;
vtx[j + 1].v.ob[1] = elem->p2.y;
vtx[j + 1].v.ob[2] = elem->p2.z;
break;
case 2:
vtx[j].v.ob[0] = elem->p1.x;
vtx[j].v.ob[1] = elem->p1.y;
vtx[j].v.ob[2] = elem->p1.z;
vtx[j + 1].v.ob[0] = func_800B09D0(elem->p2.x, elem->p1.x, ratio);
vtx[j + 1].v.ob[1] = func_800B09D0(elem->p2.y, elem->p1.y, ratio);
vtx[j + 1].v.ob[2] = func_800B09D0(elem->p2.z, elem->p1.z, ratio);
break;
case 3:
ratio *= 0.5f;
vtx[j].v.ob[0] = func_800B09D0(elem->p1.x, elem->p2.x, ratio);
vtx[j].v.ob[1] = func_800B09D0(elem->p1.y, elem->p2.y, ratio);
vtx[j].v.ob[2] = func_800B09D0(elem->p1.z, elem->p2.z, ratio);
vtx[j + 1].v.ob[0] = func_800B09D0(elem->p2.x, elem->p1.x, ratio);
vtx[j + 1].v.ob[1] = func_800B09D0(elem->p2.y, elem->p1.y, ratio);
vtx[j + 1].v.ob[2] = func_800B09D0(elem->p2.z, elem->p1.z, ratio);
ratio *= 2.0f;
break;
case 0:
default:
vtx[j].v.ob[0] = elem->p1.x;
vtx[j].v.ob[1] = elem->p1.y;
vtx[j].v.ob[2] = elem->p1.z;
vtx[j + 1].v.ob[0] = elem->p2.x;
vtx[j + 1].v.ob[1] = elem->p2.y;
vtx[j + 1].v.ob[2] = elem->p2.z;
break;
}
// @recomp If this trail just spawned (timer == 1), set the previous vertex positions for this vertex that of the second previous vertex (interpolation).
// Otherwise, set them to the current position of the respective vertex (no interpolation).
if (elem->timer == 1 && j >= 2) {
vtx[j].v.obp[0] = vtx[j - 2].v.ob[0];
vtx[j].v.obp[1] = vtx[j - 2].v.ob[1];
vtx[j].v.obp[2] = vtx[j - 2].v.ob[2];
}
else {
vtx[j].v.obp[0] = vtx[j].v.ob[0];
vtx[j].v.obp[1] = vtx[j].v.ob[1];
vtx[j].v.obp[2] = vtx[j].v.ob[2];
}
vtx[j].v.flag = 0;
vtx[j].v.tc[0] = 0;
vtx[j].v.tc[1] = 0;
vtx[j].v.cn[0] = func_800B0A24(this->p1StartColor[0], this->p1EndColor[0], ratio);
vtx[j].v.cn[1] = func_800B0A24(this->p1StartColor[1], this->p1EndColor[1], ratio);
vtx[j].v.cn[2] = func_800B0A24(this->p1StartColor[2], this->p1EndColor[2], ratio);
vtx[j].v.cn[3] = func_800B0A24(this->p1StartColor[3], this->p1EndColor[3], ratio);
j++;
// @recomp If this trail just spawned (timer == 1), set the previous vertex positions for this vertex that of the second previous vertex (interpolation).
// Otherwise, set them to the current position of the respective vertex (no interpolation).
if (elem->timer == 1 && j >= 2) {
vtx[j].v.obp[0] = vtx[j - 2].v.ob[0];
vtx[j].v.obp[1] = vtx[j - 2].v.ob[1];
vtx[j].v.obp[2] = vtx[j - 2].v.ob[2];
}
else {
vtx[j].v.obp[0] = vtx[j].v.ob[0];
vtx[j].v.obp[1] = vtx[j].v.ob[1];
vtx[j].v.obp[2] = vtx[j].v.ob[2];
}
vtx[j].v.flag = 0;
vtx[j].v.tc[0] = 0;
vtx[j].v.tc[1] = 0;
vtx[j].v.cn[0] = func_800B0A24(this->p2StartColor[0], this->p2EndColor[0], ratio);
vtx[j].v.cn[1] = func_800B0A24(this->p2StartColor[1], this->p2EndColor[1], ratio);
vtx[j].v.cn[2] = func_800B0A24(this->p2StartColor[2], this->p2EndColor[2], ratio);
vtx[j].v.cn[3] = func_800B0A24(this->p2StartColor[3], this->p2EndColor[3], ratio);
j++;
}
}
phi_t2 = 0;
j = 0;
// @recomp Use gEXVertex in place of gSPVertex.
gEXVertex(POLY_XLU_DISP++, vtx, 32, 0);
for (i = 0; i < this->numElements; i++) {
elem = &this->elements[i];
if (elem->state == 0) {
phi_t2 = 0;
} else {
if (phi_t2 == 0) {
phi_t2 = 1;
} else {
gSP1Quadrangle(POLY_XLU_DISP++, j - 2, j - 1, j + 1, j, 0);
if (this->unkFlag == 1) {
phi_t2 = 0;
}
}
j += 2;
}
}
}
} else if (this->drawMode <= EFF_BLURE_DRAW_MODE_SIMPLE_ALT_COLORS) {
EffectBlure_DrawSimple(this, gfxCtx);
} else {
EffectBlure_DrawSmooth(this, gfxCtx);
}
}
CLOSE_DISPS(gfxCtx);
}

View File

@@ -17,8 +17,12 @@
03000000790000004e95000000000000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Windows,
03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,
03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,
03000000242f00007300000000000000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,
0300000079000000d218000000000000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000d620000010a7000000000000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000242f0000f400000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows,
03000000790000007918000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,righttrigger:b7,rightx:a3,righty:a2,start:b8,platform:Windows,
030000008f0e00001330000000000000,Mayflash Controller Adapter,a:b1,b:b2,back:b8,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3~,righty:a2,start:b9,x:b0,y:b3,platform:Windows,
03000000f70600000100000000000000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,platform:Windows,
030000006f0e00001311000000000000,N64 Controller,+rightx:b10,+righty:b3,-rightx:b0,-righty:b11,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,platform:Windows,
03000000790000004518000000000000,NEXILUX GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,
@@ -30,8 +34,10 @@
030000009b2800006000000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,
030000009b2800006100000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows,
030000009b2800006300000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows,
030000009b2800006400000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows,
03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Windows,
03000000c82d00006928000000010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X,
03000000c82d00001930000000000000,8bitdo 64 BT,platform:Windows,a:b0,b:b1,back:b17,guide:b10,start:b11,leftstick:b13,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b8,righttrigger:b9,
03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X,
03000000790000004618000000010000,GameCube Controller Adapter,a:b4,b:b0,dpdown:b56,dpleft:b60,dpright:b52,dpup:b48,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X,
03000000242e0000ff0b000000010000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Mac OS X,

View File

@@ -79,7 +79,7 @@ extern "C" void recomp_register_actor_extension(uint8_t* rdram, recomp_context*
}
if (actor_data_sizes.size() <= actor_type) {
actor_data_sizes.resize(2 * actor_type);
actor_data_sizes.resize(actor_type + 1);
}
// Increase the actor type's extension data size by the provided size (rounded up to a multiple of 16).

View File

@@ -569,8 +569,18 @@ void recomputil_u32_slotmap_size(uint8_t* rdram, recomp_context* ctx) {
// memory slotmap.
void recomputil_create_memory_slotmap(uint8_t* rdram, recomp_context* ctx) {
(void)rdram;
_return(ctx, memory_slotmaps.create());
uint32_t element_size = _arg<0, uint32_t>(rdram, ctx);
// Create the map.
uint32_t map_key = memory_slotmaps.create();
// Retrieve the map and set its element size to the provided value.
MemorySlotmap* map;
memory_slotmaps.get(map_key, &map);
map->second = element_size;
// Return the created map's key.
_return(ctx, map_key);
}
void recomputil_destroy_memory_slotmap(uint8_t* rdram, recomp_context* ctx) {
@@ -628,7 +638,7 @@ void recomputil_memory_slotmap_create(uint8_t* rdram, recomp_context* ctx) {
// Store the allocated pointer.
PTR(void)* value_ptr;
map->first.get(key, &value_ptr);
MEM_W(0, *value_ptr) = addr;
*value_ptr = static_cast<PTR(void)>(addr);
// Return the key.
_return(ctx, key);

View File

@@ -18,6 +18,13 @@
#else
#include "SDL2/SDL.h"
#include "SDL2/SDL_syswm.h"
// Undefine x11 macros that get included by SDL_syswm.h.
#undef None
#undef Status
#undef LockMask
#undef ControlMask
#undef Success
#undef Always
#endif
#include "recomp_ui.h"
@@ -43,12 +50,13 @@
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <timeapi.h>
#include "SDL_syswm.h"
#endif
#include "../../lib/rt64/src/contrib/stb/stb_image.h"
const std::string version_string = "1.2.0";
const std::string version_string = "1.2.1-dev";
template<typename... Ts>
void exit_error(const char* str, Ts ...args) {
@@ -579,6 +587,26 @@ int main(int argc, char** argv) {
}
#ifdef _WIN32
// Set up high resolution timing period.
timeBeginPeriod(1);
// Process arguments.
for (int i = 1; i < argc; i++)
{
if (strcmp(argv[i], "--show-console") == 0)
{
if (GetConsoleWindow() == nullptr)
{
AllocConsole();
freopen("CONIN$", "r", stdin);
freopen("CONOUT$", "w", stderr);
freopen("CONOUT$", "w", stdout);
}
break;
}
}
// Set up console output to accept UTF-8 on windows
SetConsoleOutputCP(CP_UTF8);
@@ -722,5 +750,10 @@ int main(int argc, char** argv) {
release_preload(preload_context);
}
#ifdef _WIN32
// End high resolution timing period.
timeEndPeriod(1);
#endif
return EXIT_SUCCESS;
}

View File

@@ -56,21 +56,6 @@ unsigned int DPC_BUFBUSY_REG = 0;
unsigned int DPC_PIPEBUSY_REG = 0;
unsigned int DPC_TMEM_REG = 0;
unsigned int VI_STATUS_REG = 0;
unsigned int VI_ORIGIN_REG = 0;
unsigned int VI_WIDTH_REG = 0;
unsigned int VI_INTR_REG = 0;
unsigned int VI_V_CURRENT_LINE_REG = 0;
unsigned int VI_TIMING_REG = 0;
unsigned int VI_V_SYNC_REG = 0;
unsigned int VI_H_SYNC_REG = 0;
unsigned int VI_LEAP_REG = 0;
unsigned int VI_H_START_REG = 0;
unsigned int VI_V_START_REG = 0;
unsigned int VI_V_BURST_REG = 0;
unsigned int VI_X_SCALE_REG = 0;
unsigned int VI_Y_SCALE_REG = 0;
void dummy_check_interrupts() {}
RT64::UserConfiguration::Antialiasing compute_max_supported_aa(RT64::RenderSampleCounts bits) {
@@ -250,20 +235,22 @@ zelda64::renderer::RT64Context::RT64Context(uint8_t* rdram, ultramodern::rendere
appCore.DPC_PIPEBUSY_REG = &DPC_PIPEBUSY_REG;
appCore.DPC_TMEM_REG = &DPC_TMEM_REG;
appCore.VI_STATUS_REG = &VI_STATUS_REG;
appCore.VI_ORIGIN_REG = &VI_ORIGIN_REG;
appCore.VI_WIDTH_REG = &VI_WIDTH_REG;
appCore.VI_INTR_REG = &VI_INTR_REG;
appCore.VI_V_CURRENT_LINE_REG = &VI_V_CURRENT_LINE_REG;
appCore.VI_TIMING_REG = &VI_TIMING_REG;
appCore.VI_V_SYNC_REG = &VI_V_SYNC_REG;
appCore.VI_H_SYNC_REG = &VI_H_SYNC_REG;
appCore.VI_LEAP_REG = &VI_LEAP_REG;
appCore.VI_H_START_REG = &VI_H_START_REG;
appCore.VI_V_START_REG = &VI_V_START_REG;
appCore.VI_V_BURST_REG = &VI_V_BURST_REG;
appCore.VI_X_SCALE_REG = &VI_X_SCALE_REG;
appCore.VI_Y_SCALE_REG = &VI_Y_SCALE_REG;
ultramodern::renderer::ViRegs* vi_regs = ultramodern::renderer::get_vi_regs();
appCore.VI_STATUS_REG = &vi_regs->VI_STATUS_REG;
appCore.VI_ORIGIN_REG = &vi_regs->VI_ORIGIN_REG;
appCore.VI_WIDTH_REG = &vi_regs->VI_WIDTH_REG;
appCore.VI_INTR_REG = &vi_regs->VI_INTR_REG;
appCore.VI_V_CURRENT_LINE_REG = &vi_regs->VI_V_CURRENT_LINE_REG;
appCore.VI_TIMING_REG = &vi_regs->VI_TIMING_REG;
appCore.VI_V_SYNC_REG = &vi_regs->VI_V_SYNC_REG;
appCore.VI_H_SYNC_REG = &vi_regs->VI_H_SYNC_REG;
appCore.VI_LEAP_REG = &vi_regs->VI_LEAP_REG;
appCore.VI_H_START_REG = &vi_regs->VI_H_START_REG;
appCore.VI_V_START_REG = &vi_regs->VI_V_START_REG;
appCore.VI_V_BURST_REG = &vi_regs->VI_V_BURST_REG;
appCore.VI_X_SCALE_REG = &vi_regs->VI_X_SCALE_REG;
appCore.VI_Y_SCALE_REG = &vi_regs->VI_Y_SCALE_REG;
// Set up the RT64 application configuration fields.
RT64::ApplicationConfiguration appConfig;
@@ -338,9 +325,7 @@ void zelda64::renderer::RT64Context::send_dl(const OSTask* task) {
app->processDisplayLists(app->core.RDRAM, task->t.data_ptr & 0x3FFFFFF, 0, true);
}
void zelda64::renderer::RT64Context::update_screen(uint32_t vi_origin) {
VI_ORIGIN_REG = vi_origin;
void zelda64::renderer::RT64Context::update_screen() {
app->updateScreen();
}

View File

@@ -900,6 +900,13 @@ void recompui::drop_files(const std::list<std::filesystem::path> &file_list) {
return;
}
recompui::set_config_tab(recompui::ConfigTab::Mods);
// If the config menu isn't open, open it in the mods tab.
if (!recompui::is_context_shown(recompui::get_config_context_id())) {
recompui::hide_all_contexts();
recompui::show_context(recompui::get_config_context_id(), "");
}
recompui::open_notification("Installing Mods", "Please Wait");
// TODO: Needs a progress callback and a prompt for every mod that needs to be confirmed to be overwritten.
// TODO: Run this on a background thread and use the callbacks to advance the state instead of blocking.