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

Keep rotation of grabbed objects faithful to Portal on PC #31

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
6 changes: 5 additions & 1 deletion src/decor/decor_object_list.c
Original file line number Diff line number Diff line change
Expand Up @@ -236,4 +236,8 @@ int decorIdForObjectDefinition(struct DecorObjectDefinition* def) {
}

return result;
}
}

int decorIdForCollisionObject(struct CollisionObject* collisionObject) {
return decorIdForObjectDefinition((struct DecorObjectDefinition*)collisionObject->collider);
}
2 changes: 2 additions & 0 deletions src/decor/decor_object_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@
struct DecorObjectDefinition* decorObjectDefinitionForId(int id);
int decorIdForObjectDefinition(struct DecorObjectDefinition* def);

int decorIdForCollisionObject(struct CollisionObject* collisionObject); // evil hack
Copy link
Owner

Choose a reason for hiding this comment

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

Consolidating this is a good call


#endif
6 changes: 6 additions & 0 deletions src/physics/collision_scene.c
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,12 @@ void collisionSceneGetPortalTransform(int fromPortal, struct Transform* out) {
transformConcat(gCollisionScene.portalTransforms[1 - fromPortal], &inverseA, out);
}

void collisionSceneGetPortalRotation(int fromPortal, struct Quaternion* out) {
struct Quaternion inverse;
quatConjugate(&gCollisionScene.portalTransforms[fromPortal]->rotation, &inverse);
quatMultiply(&gCollisionScene.portalTransforms[1 - fromPortal]->rotation, &inverse, out);
}

void collisionSceneAddDynamicObject(struct CollisionObject* object) {
if (gCollisionScene.dynamicObjectCount < MAX_DYNAMIC_OBJECTS) {
gCollisionScene.dynamicObjects[gCollisionScene.dynamicObjectCount] = object;
Expand Down
1 change: 1 addition & 0 deletions src/physics/collision_scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ int collisionSceneRaycast(struct CollisionScene* scene, int roomIndex, struct Ra
int collisionSceneRaycastOnlyDynamic(struct CollisionScene* scene, struct Ray* ray, int collisionLayers, float maxDistance, struct RaycastHit* hit);

void collisionSceneGetPortalTransform(int fromPortal, struct Transform* out);
void collisionSceneGetPortalRotation(int fromPortal, struct Quaternion* out);

void collisionSceneAddDynamicObject(struct CollisionObject* object);
void collisionSceneRemoveDynamicObject(struct CollisionObject* object);
Expand Down
120 changes: 100 additions & 20 deletions src/player/player.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "../audio/soundplayer.h"
#include "../controls/controller_actions.h"
#include "../defs.h"
#include "../decor/decor_object_list.h"
#include "../levels/levels.h"
#include "../math/mathf.h"
#include "../physics/collision_capsule.h"
Expand Down Expand Up @@ -244,6 +245,11 @@ void playerApplyPortalGrab(struct Player* player, int portalIndex) {
}
}

int shouldRotateTowardsPlayer(struct CollisionObject* grabbing) {
int decorId = decorIdForCollisionObject(grabbing);
return decorId == DECOR_TYPE_RADIO;
}

void playerSetGrabbing(struct Player* player, struct CollisionObject* grabbing) {
if (grabbing && grabbing->flags & COLLISION_OBJECT_PLAYER_STANDING){
player->grabConstraint.object = NULL;
Expand All @@ -263,6 +269,54 @@ void playerSetGrabbing(struct Player* player, struct CollisionObject* grabbing)
} else if (grabbing != player->grabConstraint.object) {
pointConstraintInit(&player->grabConstraint, grabbing, 8.0f, 5.0f, 1.0f);
}

// remember object rotation relative to looking-direction on XZ plane
if (player->grabConstraint.object && !shouldRotateTowardsPlayer(player->grabConstraint.object)) {
struct Quaternion forwardRotationInv;
struct Vector3 forward, tmpVec;
playerGetMoveBasis(&player->lookTransform, &forward, &tmpVec);
vector3Negate(&forward, &tmpVec);
quatLook(&tmpVec, &gUp, &forwardRotationInv);
quatConjugate(&forwardRotationInv, &forwardRotationInv);

struct Quaternion objectRotation = player->grabConstraint.object->body->transform.rotation;

// if grabbed object is a cube, possibly clamp the target rotation if one side almost faces the player directly on XZ plane
int decorId = decorIdForCollisionObject(player->grabConstraint.object);
if (decorId == DECOR_TYPE_CUBE || decorId == DECOR_TYPE_CUBE_UNIMPORTANT) {
struct Vector3 surfaceNormal;
for (int i = 0; i < 6; ++i) {
if (i == 0) { surfaceNormal = (struct Vector3){ 1.0f, 0.0f, 0.0f}; }
else if (i == 1) { surfaceNormal = (struct Vector3){-1.0f, 0.0f, 0.0f}; }
else if (i == 2) { surfaceNormal = (struct Vector3){ 0.0f, 1.0f, 0.0f}; }
else if (i == 3) { surfaceNormal = (struct Vector3){ 0.0f, -1.0f, 0.0f}; }
else if (i == 4) { surfaceNormal = (struct Vector3){ 0.0f, 0.0f, 1.0f}; }
else if (i == 5) { surfaceNormal = (struct Vector3){ 0.0f, 0.0f, -1.0f}; }
quatMultVector(&objectRotation, &surfaceNormal, &surfaceNormal);

if (vector3Dot(&surfaceNormal, &forward) <= -0.85f) {
struct Quaternion surfaceRotation;
quatLook(&surfaceNormal, &gUp, &surfaceRotation);
quatMultiply(&forwardRotationInv, &surfaceRotation, &objectRotation);
quatConjugate(&objectRotation, &surfaceRotation);
quatMultiply(&surfaceRotation, &player->grabConstraint.object->body->transform.rotation, &objectRotation);
break;
}
}
}

if (player->grabbingThroughPortal != PLAYER_GRABBING_THROUGH_NOTHING) {
struct Quaternion portalRotation, tempRotation;
collisionSceneGetPortalRotation(player->grabbingThroughPortal > 0 ? 0 : 1, &portalRotation);
quatConjugate(&portalRotation, &portalRotation); // untangle objectRotation from portalRotation
for (int i = 0; i < abs(player->grabbingThroughPortal); ++i) {
quatMultiply(&portalRotation, &objectRotation, &tempRotation);
objectRotation = tempRotation;
}
}

quatMultiply(&forwardRotationInv, &objectRotation, &player->grabRotationBase);
}
}

void playerShakeUpdate(struct Player* player) {
Expand Down Expand Up @@ -337,7 +391,7 @@ int playerRaycastGrab(struct Player* player, struct RaycastHit* hit, int checkPa
return result;
}

void playerUpdateGrabbedObject(struct Player* player) {
void playerUpdateGrabbedObject(struct Player* player, struct Vector3* forward) {
if (playerIsDead(player)) {
return;
}
Expand All @@ -352,9 +406,9 @@ void playerUpdateGrabbedObject(struct Player* player) {
hit.object->flags |= COLLISION_OBJECT_INTERACTED;

if (hit.object->body && (hit.object->body->flags & RigidBodyFlagsGrabbable) && !(hit.object->flags & COLLISION_OBJECT_PLAYER_STANDING)) {
playerSetGrabbing(player, hit.object);
player->flags |= PlayerJustSelect;
player->grabbingThroughPortal = hit.numPortalsPassed;
player->flags |= PlayerJustSelect;
playerSetGrabbing(player, hit.object);
}
else if ((hit.object->body)){
player->flags |= PlayerJustSelect;
Expand Down Expand Up @@ -389,26 +443,31 @@ void playerUpdateGrabbedObject(struct Player* player) {
if (player->body.flags & RigidBodyFlagsCrossedPortal0) {
playerApplyPortalGrab(player, 1);
}

if (player->body.flags & RigidBodyFlagsCrossedPortal1) {
playerApplyPortalGrab(player, 0);
}

if (player->grabConstraint.object->body->flags & RigidBodyFlagsCrossedPortal0) {
playerApplyPortalGrab(player, 0);
}

if (player->grabConstraint.object->body->flags & RigidBodyFlagsCrossedPortal1) {
playerApplyPortalGrab(player, 1);
}


int rotateTowardsPlayer = shouldRotateTowardsPlayer(player->grabConstraint.object);

struct Vector3 grabPoint;
struct Quaternion grabRotation = player->lookTransform.rotation;

struct Quaternion grabRotation;
if (rotateTowardsPlayer) {
grabRotation = player->lookTransform.rotation;
}

// try to determine how far away to set the grab dist
struct RaycastHit hit;
struct Vector3 temp_grab_dist = gGrabDistance;

if (playerRaycastGrab(player, &hit, 1)){
float dist = hit.distance;
temp_grab_dist.z = maxf(((-1.0f*fabsf(dist))+0.2f), gGrabDistance.z);
Expand All @@ -419,27 +478,47 @@ void playerUpdateGrabbedObject(struct Player* player) {
playerSetGrabbing(player, NULL);
return;
}

transformPoint(&player->lookTransform, &temp_grab_dist, &grabPoint);


vector3Multiply(&player->lookTransform.scale, &temp_grab_dist, &temp_grab_dist);

// determine object target height
quatMultVector(&player->lookTransform.rotation, &temp_grab_dist, &grabPoint);
float grabY = grabPoint.y;

// keep object at steady XZ-planar distance in front of player
struct Quaternion forwardRotation;
struct Vector3 forwardNegate;
vector3Negate(forward, &forwardNegate);
quatLook(&forwardNegate, &gUp, &forwardRotation);
quatMultVector(&forwardRotation, &temp_grab_dist, &grabPoint);
vector3Add(&player->lookTransform.position, &grabPoint, &grabPoint);
grabPoint.y += grabY;

if (player->grabbingThroughPortal != PLAYER_GRABBING_THROUGH_NOTHING) {
if (!collisionSceneIsPortalOpen()) {
// portal was closed while holding object through it
playerSetGrabbing(player, NULL);
return;
}

struct Transform pointTransform;
collisionSceneGetPortalTransform(player->grabbingThroughPortal > 0 ? 0 : 1, &pointTransform);


struct Quaternion* transformRotation = (rotateTowardsPlayer) ? &grabRotation : &forwardRotation;
struct Quaternion* tempRotation = (rotateTowardsPlayer) ? &forwardRotation : &grabRotation;

for (int i = 0; i < abs(player->grabbingThroughPortal); ++i) {
transformPoint(&pointTransform, &grabPoint, &grabPoint);
struct Quaternion finalRotation;
quatMultiply(&pointTransform.rotation, &grabRotation, &finalRotation);
grabRotation = finalRotation;
quatMultiply(&pointTransform.rotation, transformRotation, tempRotation);
*transformRotation = *tempRotation;
}
}


// maintain object's relative rotation
if (!rotateTowardsPlayer) {
quatMultiply(&forwardRotation, &player->grabRotationBase, &grabRotation);
}

pointConstraintUpdateTarget(&player->grabConstraint, &grabPoint, &grabRotation);
}
}
Expand Down Expand Up @@ -938,9 +1017,10 @@ void playerUpdate(struct Player* player) {
quatMultVector(&tempRotation, &gUp, &newUp);
quatLook(&newForward, &newUp, &player->lookTransform.rotation);
player->pitchVelocity = 0.0f;
playerGetMoveBasis(&player->lookTransform, &forward, &right);
}

playerUpdateGrabbedObject(player);
playerUpdateGrabbedObject(player, &forward);

collisionObjectUpdateBB(&player->collisionObject);

Expand Down
1 change: 1 addition & 0 deletions src/player/player.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ struct Player {
short grabbingThroughPortal;
short dynamicId;
struct PointConstraint grabConstraint;
struct Quaternion grabRotationBase;
float pitchVelocity;
float yawVelocity;
enum PlayerFlags flags;
Expand Down
2 changes: 2 additions & 0 deletions src/savefile/scene_serialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ void playerSerialize(struct Serializer* serializer, SerializeAction action, stru
action(serializer, &player->body.velocity, sizeof(player->body.velocity));
action(serializer, &player->body.currentRoom, sizeof(player->body.currentRoom));
action(serializer, &player->flags, sizeof(player->flags));
action(serializer, &player->grabbingThroughPortal, sizeof(player->grabbingThroughPortal));
}

void playerDeserialize(struct Serializer* serializer, struct Player* player) {
Expand All @@ -22,6 +23,7 @@ void playerDeserialize(struct Serializer* serializer, struct Player* player) {
serializeRead(serializer, &player->body.velocity, sizeof(player->body.velocity));
serializeRead(serializer, &player->body.currentRoom, sizeof(player->body.currentRoom));
serializeRead(serializer, &player->flags, sizeof(player->flags));
serializeRead(serializer, &player->grabbingThroughPortal, sizeof(player->grabbingThroughPortal));
}

#define PORTAL_FLAGS_NO_PORTAL -1
Expand Down
2 changes: 1 addition & 1 deletion src/scene/fizzler.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ void fizzlerTrigger(void* data, struct CollisionObject* objectEnteringTrigger) {
}

if (fizzler->cubeSignalIndex != -1) {
int decorType = decorIdForObjectDefinition((struct DecorObjectDefinition*)objectEnteringTrigger->collider);
int decorType = decorIdForCollisionObject(objectEnteringTrigger);
if (decorType == DECOR_TYPE_CUBE || decorType == DECOR_TYPE_CUBE_UNIMPORTANT) {
signalsSend(fizzler->cubeSignalIndex);
}
Expand Down
2 changes: 1 addition & 1 deletion src/scene/trigger_listener.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ enum ObjectTriggerType triggerDetermineType(struct CollisionObject* objectEnteri
return TRIGGER_TYPE_TO_MASK(ObjectTriggerTypePlayer);
}

int decorType = decorIdForObjectDefinition((struct DecorObjectDefinition*)objectEnteringTrigger->collider);
int decorType = decorIdForCollisionObject(objectEnteringTrigger);

if (decorType == DECOR_TYPE_CUBE || decorType == DECOR_TYPE_CUBE_UNIMPORTANT) {
return gScene.player.grabConstraint.object == objectEnteringTrigger ? TRIGGER_TYPE_TO_MASK(ObjectTriggerTypeCubeHover) | TRIGGER_TYPE_TO_MASK(ObjectTriggerTypeCube) : TRIGGER_TYPE_TO_MASK(ObjectTriggerTypeCube);
Expand Down