Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Player doc: some rotation logic/data #1443

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
22 changes: 14 additions & 8 deletions include/z64player.h
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,16 @@ typedef void (*PlayerFunc674)(struct Player*, struct PlayState*);
typedef s32 (*PlayerFunc82C)(struct Player*, struct PlayState*);
typedef void (*PlayerFuncA74)(struct PlayState*, struct Player*);

#define NOREST_ROT_FOCUS_X (1 << 0)
#define NOREST_ROT_FOCUS_Y (1 << 1)
#define NOREST_ROT_FOCUS_Z (1 << 2)
#define NOREST_ROT_HEAD_X (1 << 3)
#define NOREST_ROT_HEAD_Y (1 << 4)
#define NOREST_ROT_HEAD_Z (1 << 5)
#define NOREST_ROT_UPPER_X (1 << 6)
#define NOREST_ROT_UPPER_Y (1 << 7)
#define NOREST_ROT_UPPER_Z (1 << 8)

typedef struct Player {
/* 0x0000 */ Actor actor;
/* 0x014C */ s8 currentTunic; // current tunic from `PlayerTunic`
Expand Down Expand Up @@ -560,15 +570,11 @@ typedef struct Player {
/* 0x06A8 */ Actor* unk_6A8;
/* 0x06AC */ s8 unk_6AC;
/* 0x06AD */ u8 unk_6AD;
/* 0x06AE */ u16 unk_6AE;
/* 0x06B0 */ s16 unk_6B0;
/* 0x06AE */ u16 rotsNoRestFlags; // See `NOREST_ROT_` macros. If its flag isn't set, a rot steps to a rest value.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why "No Rest"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when the flag is set, the rotation does not step to a rest value

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Next question, what is a rest value? Something like the idle value?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good question and I'm open to suggestions on changing that

I got the term from Blender (a 3d modeling piece of software https://www.blender.org/ ), let me define a few terms

  • armature = skeleton, made of bones
  • pose = rotations and locations of the bones in an armature

For example when animated the armature has a different pose every frame

The "rest pose" is the pose the armature has when all its bones aren't transformed (all rotations/translations/... are 0/identity)


Back to OoT, all rotations involved with NOREST_ROT_ are stepped to 0 when their flag isn't set
For all these rotations, 0 corresponds visually to a neutral pose of Link, with only the animation affecting his bones, so I refered to those as "rest" values

Also the use case for these flags is when setting these rots to specific values, typically to look at something, which also can be seen as deviating from the "rest" animation-provided pose

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you maybe think of this as a "hold and release" system? So the rot is held to a target when the flag is set, and released back to rest when the flag is unset? So something like HOLD_ROT_? Or would that conflate with the "HOLD" action of "holding" something above Link's Head?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's because no rest is two words, so it feels more separate, while non-rest is one word, so it's easier to group together as a single concept?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to review the pr in full again, but I think in general itd be good to try a positive name instead of a negative one, like "modified" instead of "no rest" (which i interpret to mean "non default, offset from the rotation the animation is applying)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is related to three rotations / rotation offsets: focus, head, upper (body)

I'm not sure what's up with the focus one, but head and upper are I guess used to make Link face something

The flags control if these rotations are stepped toward "0" (not actually always 0 but something that is "normal"/"default" hence "rest")

  • flag unset = rotations are stepped toward "0"
  • flag set = rotations are not stepped toward "0"

Phrased this way it makes it arduous to make a positive name but I'm not sure how to phrase it another way

maybe like "flag set = rot is active" but idk I don't have many ideas

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I can tell down below, it seems like setting this flag just stops rotation back to the rest position (0). You might have been slightly joking, but activeRot doesn't seem too bad. Could possibly also do something like stayRot/remainRot, or maybe keepRot?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just brainstorming: what about something a little more general like overrideRot or overrideRotFlags, and leave it to the comments to describe that the default behaviour is to restore the rot to resting position, and that by setting this flag, it gives you manual control to control the rotations?

/* 0x06B0 */ s16 upperLimbYawSecondary;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/* 0x06B0 */ s16 upperLimbYawSecondary;
/* 0x06B0 */ s16 upperLimbYawOffset;

Maybe?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not

"secondary" is because

if (!(this->rotsNoRestFlags & NOREST_ROT_UPPER_Y)) {
    if (this->upperLimbYawSecondary != 0) {
        Player_ApproachZeroBinang(&this->upperLimbYawSecondary);
    } else {
        Player_ApproachZeroBinang(&this->upperLimbRot.y);
    }
}

, it steps this one to 0 before the main one

/* 0x06B2 */ char unk_6B4[0x004];
/* 0x06B6 */ s16 unk_6B6;
/* 0x06B8 */ s16 unk_6B8;
/* 0x06BA */ s16 unk_6BA;
/* 0x06BC */ s16 unk_6BC;
/* 0x06BE */ s16 unk_6BE;
/* 0x06C0 */ s16 unk_6C0;
/* 0x06B6 */ Vec3s headLimbRot;
/* 0x06BC */ Vec3s upperLimbRot;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is Limb necessary here? Which limbs are encompassed in upperLimbRot?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It corresponds to PLAYER_LIMB_UPPER

/* 0x06C2 */ s16 unk_6C2;
/* 0x06C4 */ f32 unk_6C4;
/* 0x06C8 */ SkelAnime skelAnime2;
Expand Down
2 changes: 1 addition & 1 deletion src/code/z_actor.c
Original file line number Diff line number Diff line change
Expand Up @@ -980,7 +980,7 @@ s32 func_8002DD6C(Player* player) {
}

s32 func_8002DD78(Player* player) {
return func_8002DD6C(player) && player->unk_834;
return func_8002DD6C(player) && (player->unk_834 != 0);
}

s32 func_8002DDA8(PlayState* play) {
Expand Down
24 changes: 12 additions & 12 deletions src/code/z_player_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -1086,22 +1086,22 @@ s32 Player_OverrideLimbDrawGameplayCommon(PlayState* play, s32 limbIndex, Gfx**
}

if (limbIndex == PLAYER_LIMB_HEAD) {
rot->x += this->unk_6BA;
rot->y -= this->unk_6B8;
rot->z += this->unk_6B6;
rot->x += this->headLimbRot.z;
rot->y -= this->headLimbRot.y;
rot->z += this->headLimbRot.x;
} else if (limbIndex == PLAYER_LIMB_UPPER) {
if (this->unk_6B0 != 0) {
if (this->upperLimbYawSecondary != 0) {
Matrix_RotateZ(BINANG_TO_RAD(0x44C), MTXMODE_APPLY);
Matrix_RotateY(BINANG_TO_RAD(this->unk_6B0), MTXMODE_APPLY);
Matrix_RotateY(BINANG_TO_RAD(this->upperLimbYawSecondary), MTXMODE_APPLY);
}
if (this->unk_6BE != 0) {
Matrix_RotateY(BINANG_TO_RAD(this->unk_6BE), MTXMODE_APPLY);
if (this->upperLimbRot.y != 0) {
Matrix_RotateY(BINANG_TO_RAD(this->upperLimbRot.y), MTXMODE_APPLY);
}
if (this->unk_6BC != 0) {
Matrix_RotateX(BINANG_TO_RAD(this->unk_6BC), MTXMODE_APPLY);
if (this->upperLimbRot.x != 0) {
Matrix_RotateX(BINANG_TO_RAD(this->upperLimbRot.x), MTXMODE_APPLY);
}
if (this->unk_6C0 != 0) {
Matrix_RotateZ(BINANG_TO_RAD(this->unk_6C0), MTXMODE_APPLY);
if (this->upperLimbRot.z != 0) {
Matrix_RotateZ(BINANG_TO_RAD(this->upperLimbRot.z), MTXMODE_APPLY);
}
} else if (limbIndex == PLAYER_LIMB_L_THIGH) {
func_8008F87C(play, this, &this->skelAnime, pos, rot, PLAYER_LIMB_L_THIGH, PLAYER_LIMB_L_SHIN,
Expand Down Expand Up @@ -1591,7 +1591,7 @@ void Player_PostLimbDrawGameplay(PlayState* play, s32 limbIndex, Gfx** dList, Ve
Matrix_MtxFToYXZRotS(&sp44, &heldActor->world.rot, 0);
heldActor->shape.rot = heldActor->world.rot;

if (func_8002DD78(this) != 0) {
if (func_8002DD78(this)) {
Matrix_Translate(500.0f, 300.0f, 0.0f, MTXMODE_APPLY);
Player_DrawHookshotReticle(play, this,
(this->heldItemAction == PLAYER_IA_HOOKSHOT) ? 38600.0f : 77600.0f);
Expand Down
2 changes: 1 addition & 1 deletion src/overlays/actors/ovl_En_Horse/z_en_horse.c
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ void EnHorse_ResetRace(EnHorse* this, PlayState* play) {
s32 EnHorse_PlayerCanMove(EnHorse* this, PlayState* play) {
Player* player = GET_PLAYER(play);

if ((player->stateFlags1 & PLAYER_STATE1_0) || func_8002DD78(GET_PLAYER(play)) == 1 ||
if ((player->stateFlags1 & PLAYER_STATE1_0) || func_8002DD78(GET_PLAYER(play)) == true ||
(player->stateFlags1 & PLAYER_STATE1_20) || ((this->stateFlags & ENHORSE_FLAG_19) && !this->inRace) ||
this->action == ENHORSE_ACT_HBA || player->actor.flags & ACTOR_FLAG_8 || play->csCtx.state != 0) {
return false;
Expand Down