diff --git a/assets/test_chambers/test_chamber_00/test_chamber_00_0.blend b/assets/test_chambers/test_chamber_00/test_chamber_00_0.blend index 759550a..eff8915 100644 Binary files a/assets/test_chambers/test_chamber_00/test_chamber_00_0.blend and b/assets/test_chambers/test_chamber_00/test_chamber_00_0.blend differ diff --git a/src/math/quaternion.c b/src/math/quaternion.c index b71cd23..718599b 100644 --- a/src/math/quaternion.c +++ b/src/math/quaternion.c @@ -165,28 +165,49 @@ void quatRandom(struct Quaternion* q) { } void quatLook(struct Vector3* lookDir, struct Vector3* up, struct Quaternion* out) { - struct Vector3 horizontal; - horizontal = *lookDir; - horizontal.y = 0.0f; - vector3Normalize(&horizontal, &horizontal); + // calculate orthonormal basis + struct Vector3 zDir; + vector3Normalize(lookDir, &zDir); + vector3Negate(&zDir, &zDir); - struct Vector2 complex; - complex.x = -horizontal.z; - complex.y = -horizontal.x; + struct Vector3 yDir; + vector3AddScaled(up, &zDir, -vector3Dot(&zDir, up), &yDir); + vector3Normalize(&yDir, &yDir); - struct Quaternion yaw; - quatAxisComplex(&gUp, &complex, &yaw); + struct Vector3 xDir; + vector3Cross(&yDir, &zDir, &xDir); - struct Vector3 lookNormalized; - vector3Normalize(lookDir, &lookNormalized); - - complex.y = lookNormalized.y; - complex.x = sqrtf(1.0f - complex.y * complex.y); - - struct Quaternion pitch; - quatAxisComplex(&gRight, &complex, &pitch); - - quatMultiply(&yaw, &pitch, out); + // convert orthonormal basis to a quaternion + float trace = xDir.x + yDir.y + zDir.z; + if (trace > 0) { + float sqrtResult = sqrtf(trace+1.0f) * 2.0f; + float invSqrtResult = 1.0f / sqrtResult; + out->w = 0.25 * sqrtResult; + out->x = (yDir.z - zDir.y) * invSqrtResult; + out->y = (zDir.x - xDir.z) * invSqrtResult; + out->z = (xDir.y - yDir.x) * invSqrtResult; + } else if ((xDir.x > yDir.y) && (xDir.x > zDir.z)) { + float sqrtResult = sqrtf(1.0 + xDir.x - yDir.y - zDir.z) * 2.0f; + float invSqrtResult = 1.0f / sqrtResult; + out->w = (yDir.z - zDir.y) * invSqrtResult; + out->x = 0.25 * sqrtResult; + out->y = (yDir.x + xDir.y) * invSqrtResult; + out->z = (zDir.x + xDir.z) * invSqrtResult; + } else if (yDir.y > zDir.z) { + float sqrtResult = sqrtf(1.0 + yDir.y - xDir.x - zDir.z) * 2.0f; + float invSqrtResult = 1.0f / sqrtResult; + out->w = (zDir.x - xDir.z) * invSqrtResult; + out->x = (yDir.x + xDir.y) * invSqrtResult; + out->y = 0.25 * sqrtResult; + out->z = (zDir.y + yDir.z) * invSqrtResult; + } else { + float sqrtResult = sqrtf(1.0 + zDir.z - xDir.x - yDir.y) * 2.0f; + float invSqrtResult = 1.0f / sqrtResult; + out->w = (xDir.y - yDir.x) * invSqrtResult; + out->x = (zDir.x + xDir.z) * invSqrtResult; + out->y = (zDir.y + yDir.z) * invSqrtResult; + out->z = 0.25 * sqrtResult; + } } void quatLerp(struct Quaternion* a, struct Quaternion* b, float t, struct Quaternion* out) { diff --git a/src/physics/rigid_body.c b/src/physics/rigid_body.c index 144c878..fd75f36 100644 --- a/src/physics/rigid_body.c +++ b/src/physics/rigid_body.c @@ -124,7 +124,7 @@ void rigidBodyCheckPortals(struct RigidBody* rigidBody) { quatConjugate(&gCollisionScene.portalTransforms[i]->rotation, &inverseARotation); struct Quaternion rotationTransfer; - quatMultiply(&inverseARotation, &otherPortal->rotation, &rotationTransfer); + quatMultiply(&otherPortal->rotation, &inverseARotation, &rotationTransfer); quatMultVector(&rotationTransfer, &rigidBody->velocity, &rigidBody->velocity); quatMultVector(&rotationTransfer, &rigidBody->angularVelocity, &rigidBody->angularVelocity); diff --git a/src/player/player.c b/src/player/player.c index 14f8518..4a26f25 100644 --- a/src/player/player.c +++ b/src/player/player.c @@ -217,6 +217,14 @@ void playerUpdate(struct Player* player, struct Transform* cameraTransform) { player->pitchVelocity * targetPitch > 0.0f ? ROTATE_RATE_DELTA : ROTATE_RATE_STOP_DELTA ); + struct Vector3 lookingForward; + vector3Negate(&gForward, &lookingForward); + quatMultVector(&player->body.transform.rotation, &lookingForward, &lookingForward); + struct Quaternion upRotation; + quatLook(&lookingForward, &gUp, &upRotation); + quatLerp(&upRotation, &player->body.transform.rotation, 0.9f, &player->body.transform.rotation); + + struct Quaternion deltaRotate; quatAxisAngle(&gUp, player->yawVelocity * FIXED_DELTA_TIME, &deltaRotate); diff --git a/src/scene/portal_surface.c b/src/scene/portal_surface.c index 7a10554..a8a2390 100644 --- a/src/scene/portal_surface.c +++ b/src/scene/portal_surface.c @@ -48,11 +48,11 @@ int portalSurfaceIsInside(struct PortalSurface* surface, struct Transform* porta continue; } - int yIntersection = (int)(a.y - b.y) * (portalPosition.x - b.x) / (a.x - b.x); + int yIntersection = (int)(a.y - b.y) * (portalPosition.x - b.x) / (a.x - b.x) + b.y; - if (yIntersection > 0) { + if (yIntersection > portalPosition.y) { ++intersectionCount; - } else if (yIntersection == 0) { + } else if (yIntersection == portalPosition.y) { // portal is on an edge exit early return 0; } @@ -88,7 +88,7 @@ void portalSurfaceAdjustPosition(struct PortalSurface* surface, struct Transform halfSize.x = (maxPortal.x - minPortal.x) >> 1; halfSize.y = (maxPortal.y - minPortal.y) >> 1; - for (int i = 0; i < 2; ++i) { + for (int interation = 0; interation < 2; ++interation) { int minOverlap = NO_OVERLAP; struct Vector2s16 minOverlapOffset; @@ -136,11 +136,11 @@ void portalSurfaceAdjustPosition(struct PortalSurface* surface, struct Transform if (abs(offset.x) > abs(offset.y)) { lineAxis = 0; crossAxis = 1; - crossDirection = sign(offset.x); + crossDirection = -sign(offset.x); } else { lineAxis = 1; crossAxis = 0; - crossDirection = -sign(offset.y); + crossDirection = sign(offset.y); } int portalPosLineAxis = VECTOR2s16_AS_ARRAY(output)[lineAxis]; @@ -153,14 +153,14 @@ void portalSurfaceAdjustPosition(struct PortalSurface* surface, struct Transform int boxPosition = VECTOR2s16_AS_ARRAY(output)[crossAxis] + crossDirection * VECTOR2s16_AS_ARRAY(&halfSize)[crossAxis]; int distance = (boxPosition - VECTOR2s16_AS_ARRAY(&a)[crossAxis]) * crossDirection; - if (distance <= 0) { + if (distance <= 0 && distance < VECTOR2s16_AS_ARRAY(&halfSize)[crossAxis]) { continue; } if (distance < minOverlap) { minOverlap = distance; VECTOR2s16_AS_ARRAY(&minOverlapOffset)[lineAxis] = 0; - VECTOR2s16_AS_ARRAY(&minOverlapOffset)[crossAxis] = distance * crossDirection; + VECTOR2s16_AS_ARRAY(&minOverlapOffset)[crossAxis] = -distance * crossDirection; } } diff --git a/src/scene/scene.c b/src/scene/scene.c index 19460d5..0835fd2 100644 --- a/src/scene/scene.c +++ b/src/scene/scene.c @@ -103,13 +103,29 @@ void sceneRender(struct Scene* scene, struct RenderState* renderState, struct Gr // contactSolverDebugDraw(&gContactSolver, renderState); } -unsigned ignoreInputFrames = 10; +void sceneCheckPortals(struct Scene* scene) { + struct Ray raycastRay; + struct Vector3 playerUp; + raycastRay.origin = scene->player.body.transform.position; + vector3Negate(&gForward, &raycastRay.dir); + quatMultVector(&scene->player.body.transform.rotation, &raycastRay.dir, &raycastRay.dir); + quatMultVector(&scene->player.body.transform.rotation, &gUp, &playerUp); + + if (controllerGetButtonDown(0, Z_TRIG)) { + sceneFirePortal(scene, &raycastRay, &playerUp, 0); + } + + if (controllerGetButtonDown(0, R_TRIG)) { + sceneFirePortal(scene, &raycastRay, &playerUp, 1); + } +} void sceneUpdate(struct Scene* scene) { OSTime frameStart = osGetTime(); scene->lastFrameTime = frameStart - scene->lastFrameStart; playerUpdate(&scene->player, &scene->camera.transform); + sceneCheckPortals(scene); collisionSceneUpdateDynamics(); @@ -146,9 +162,6 @@ int sceneFirePortal(struct Scene* scene, struct Ray* ray, struct Vector3* player quatLook(&hitDirection, playerUp, &portalLocation.rotation); } - // TODO remove once there is a hole in the wall - vector3AddScaled(&portalLocation.position, &hit.normal, 0.1f, &portalLocation.position); - return sceneOpenPortal(scene, &portalLocation, portalIndex, quadIndex); } @@ -157,6 +170,11 @@ int sceneOpenPortal(struct Scene* scene, struct Transform* at, int portalIndex, for (int i = surfaceMapping.minPortalIndex; i < surfaceMapping.maxPortalIndex; ++i) { if (portalSurfaceGenerate(&gCurrentLevel->portalSurfaces[i], at, NULL, NULL)) { + struct Vector3 portalForward; + quatMultVector(&at->rotation, &gForward, &portalForward); + // TODO remove once there is a hole in the wall + vector3AddScaled(&at->position, &portalForward, (portalIndex == 0) ? -0.1f : 0.1f, &at->position); + scene->portals[portalIndex].transform = *at; gCollisionScene.portalTransforms[portalIndex] = &scene->portals[portalIndex].transform; return 1;