diff options
| author | Derek Hensley <hensley.derek58@gmail.com> | 2022-06-19 16:49:20 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-06-20 00:49:20 +0100 |
| commit | d318b1f2854e98e2510812dd09e7c84b14f17622 (patch) | |
| tree | 4d8ffa8fd06498e2be994a57d407e10bf5638180 /src/code | |
| parent | a039a2f34a65ec9b85763664b30981f715563558 (diff) | |
SubS Time Paths (Finale) (#823)
* Merge in sub_s_models
* Update subs DL names
* Unused Weight pathing
* Function headers for weightpathing
* TimePathing WIP
* Timepathing, still unsure about unk184
* Move subs functions from functions.h to z64subs.h
* Add fake comment
* Some cleanup and renames
* Renames/cleanup of actors that use timepath
* Cleanup
* More cleanup
* Rename unk stuff
* Merge in upstream/master
* TimeElapsed -> elapsedTime
* Fix
* Final cleanup
* Fix waypoint comments
* Review pt. 1
* Add clarifying comment to SubS_TimePathing_FillWeightArray
* format.sh
* Fix order comments
* weightArray -> knots
* Review pt 1
* Review pt 2
* Update src/code/z_sub_s.c
Co-authored-by: EllipticEllipsis <elliptic.ellipsis@gmail.com>
* Update src/code/z_sub_s.c
Co-authored-by: EllipticEllipsis <elliptic.ellipsis@gmail.com>
Co-authored-by: Maide <34639600+Kelebek1@users.noreply.github.com>
Co-authored-by: Tom Overton <tom-overton@users.noreply.github.com>
Co-authored-by: EllipticEllipsis <elliptic.ellipsis@gmail.com>
Diffstat (limited to 'src/code')
| -rw-r--r-- | src/code/z_sub_s.c | 470 |
1 files changed, 451 insertions, 19 deletions
diff --git a/src/code/z_sub_s.c b/src/code/z_sub_s.c index 3e5443bc6..d5b4f33b2 100644 --- a/src/code/z_sub_s.c +++ b/src/code/z_sub_s.c @@ -178,8 +178,7 @@ s32 SubS_InCsMode(GlobalContext* globalCtx) { * @param[in] stepRot boolean, step towards newRot instead of setting directly * @param[in] overrideRot boolean, override newRot with the specified input. * - * Note: - * If overrideRot is true, the rotation will automatically step instead of setting directly + * @note if overrideRot is true, the rotation will automatically step instead of setting directly */ s32 SubS_UpdateLimb(s16 newRotZ, s16 newRotY, Vec3f* pos, Vec3s* rot, s32 stepRot, s32 overrideRot) { Vec3f newPos; @@ -214,17 +213,273 @@ void SubS_UpdateFlags(u16* flags, u16 setBits, u16 unsetBits) { *flags = (*flags & ~unsetBits) | setBits; } -#pragma GLOBAL_ASM("asm/non_matchings/code/z_sub_s/func_8013AF00.s") +/** + * Fills the knot array to be used with time paths + * + * The default knot array just pads with `order` duplicate knots of the first knot at the front and of the last knot + * at the end. + * + * @param[out] knots an array of values that are used to compute the progress and the individual weights + * @param[in] order the order of the interpolation i.e. the number of points in the interpolation + * @param[in] numPoints the number of points to fill, generally the path count + order + * + * @note Same note as SubS_TimePathing_Update() + */ +void SubS_TimePathing_FillKnots(f32 knots[], s32 order, s32 numPoints) { + s32 i; + f32 val = 0.0f; + + for (i = 0; i < numPoints; i++) { + if ((i >= order) && (i < (numPoints - order + 1))) { + val += 1.0f; + } + knots[i] = val; + } +} + +typedef enum { + /* 0 */ SUBS_TIME_PATHING_PROGRESS_STATUS_ERROR, + /* 1 */ SUBS_TIME_PATHING_PROGRESS_STATUS_STILL_ON_PATH, + /* 2 */ SUBS_TIME_PATHING_PROGRESS_STATUS_SHOULD_REACH_END +} SUBS_TIME_PATHING_PROGRESS_STATUS; + +/** + * Computes the progress to be used with time paths + * + * @param[out] progress the progress along the path, used to compute the weights + * @param[in] elapsedTime how much time has passed + * @param[in] waypointTime how much time per each waypoint + * @param[in] totalTime how much time the path should take to travel + * @param[in] pathCount the path count + * @param[in] order the order of the interpolation i.e. the number of points in the interpolation + * @param[in] knots see SubS_TimePathing_FillKnots() + * + * @return see SUBS_TIME_PATHING_PROGRESS_STATUS + */ +s32 SubS_TimePathing_ComputeProgress(f32* progress, s32 elapsedTime, s32 waypointTime, s32 totalTime, s32 pathCount, + s32 order, f32 knots[]) { + s32 i; + s32 j; + s32 k; + f32 waypointTimeInv; // The fraction of a waypoint a single unit of time contains + + *progress = 0.0f; + if ((waypointTime <= 0) || (elapsedTime < 0)) { + return SUBS_TIME_PATHING_PROGRESS_STATUS_ERROR; + } -#pragma GLOBAL_ASM("asm/non_matchings/code/z_sub_s/func_8013B010.s") + // When using the knots from SubS_TimePathing_FillKnots() these nested loops seem to simplify to + // *progress = (f32)elapsedTime / (f32)waypointTime; + waypointTimeInv = 1.0f / waypointTime; + k = 0; + for (i = order - 1; i < pathCount; i++) { + for (j = 0; j < waypointTime; j++) { + if (k == elapsedTime) { + break; + } + *progress += (knots[i + 1] - knots[i]) * waypointTimeInv; + k++; + } + } + + return (elapsedTime == totalTime) ? SUBS_TIME_PATHING_PROGRESS_STATUS_SHOULD_REACH_END + : SUBS_TIME_PATHING_PROGRESS_STATUS_STILL_ON_PATH; +} -#pragma GLOBAL_ASM("asm/non_matchings/code/z_sub_s/func_8013B0C8.s") +/** + * Computes the interpolation weights to be used with time paths + * + * Seems to use some kind of B-Spline interpolation algorithm + * + * @param[in] order the order of the interpolation i.e. the number of points in the interpolation, max is 10 + * @param[in] progress see SubS_TimePathing_ComputeProgress() + * @param[in] waypoint the current waypoint + * @param[in] knots see SubS_TimePathing_FillKnots() + * @param[out] weights how much to weight each point considered + */ +void SubS_TimePathing_ComputeWeights(s32 order, f32 progress, s32 waypoint, f32 knots[], f32 weights[]) { + f32 weightsTemp[10][11]; + s32 i; + s32 j; + s32 k; + + for (i = 0; i < order; i++) { + for (j = 0; j < order + 1; j++) { + weightsTemp[i][j] = 0.0f; + } + } + + weightsTemp[0][order - 1] = 1.0f; + + for (i = 1; i < order; i++) { + for (j = waypoint - i, k = (order - 1) - i; j <= waypoint; j++, k++) { + if (knots[j + i] != knots[j]) { + weightsTemp[i][k] = ((progress - knots[j]) / (knots[j + i] - knots[j])) * weightsTemp[i - 1][k]; + } else { + weightsTemp[i][k] = 0.0f; + } + + if (knots[j + i + 1] != knots[j + 1]) { + weightsTemp[i][k] += + ((knots[j + i + 1] - progress) / (knots[j + i + 1] - knots[j + 1])) * weightsTemp[i - 1][k + 1]; + } + } + } + for (j = 0; j < order; j++) { + weights[j] = weightsTemp[order - 1][j]; + } +} + +/** + * Computes the X and Z component of the position to move to in time based paths + * + * @param[out] x computed x position + * @param[out] z computed z position + * @param[in] progress see SubS_TimePathing_ComputeProgress() + * @param[in] order the order of the interpolation i.e. the number of points in the interpolation, max is 10 + * @param[in] waypoint the current waypoint + * @param[in] points the path's points + * @param[in] knots see SubS_TimePathing_FillKnots() + */ +void SubS_TimePathing_ComputeTargetPosXZ(f32* x, f32* z, f32 progress, s32 order, s32 waypoint, Vec3s points[], + f32 knots[]) { + f32 xPos; + f32 zPos; + f32 weights[11]; + f32 weightedX; + f32 weightedZ; + f32 weightedTotal; + s32 i; + + SubS_TimePathing_ComputeWeights(order, progress, waypoint, knots, weights); + weightedTotal = 0.0f; + weightedZ = 0.0f; + weightedX = 0.0f; + + for (i = 0; i < order; i++) { + xPos = points[waypoint - order + i + 1].x; + zPos = points[waypoint - order + i + 1].z; + + weightedX += weights[i] * xPos; + weightedZ += weights[i] * zPos; + weightedTotal += weights[i]; + } + *x = weightedX / weightedTotal; + *z = weightedZ / weightedTotal; +} + +/** + * Updates a time based path that an actor follows by: + * - Computing the X and Z components of the next point to move to + * - Updating the waypoint + * - Updating the time + * + * @param[in] path + * @param[out] progress see SubS_TimePathing_ComputeProgress() + * @param[in,out] elapsedTime how much time has passed + * @param[in] waypointTime how much time per each waypoint + * @param[in] totalTime how much time the path should take to travel + * @param[in,out] waypoint the current waypoint, this and the previous two points will be used to compute the targetPos + * @param[in] knots see SubS_TimePathing_FillKnots() + * @param[out] targetPos the computed position to move to + * @param[in] timeSpeed how fast time moves + * + * @return s32 returns true when the end has been reached. + * + * @note This system/function makes a couple of assumptions about the order used: + * 1. the order is assumed to be 3, see SUBS_TIME_PATHING_ORDER + * 2. even if SUBS_TIME_PATHING_ORDER is updated, the order can only be a max of 10 + */ +s32 SubS_TimePathing_Update(Path* path, f32* progress, s32* elapsedTime, s32 waypointTime, s32 totalTime, s32* waypoint, + f32 knots[], Vec3f* targetPos, s32 timeSpeed) { + Vec3s* points = Lib_SegmentedToVirtual(path->points); + s32 state; + f32 endX; + f32 endZ; + s32 reachedEnd = false; + + if (*waypoint >= path->count) { + state = SUBS_TIME_PATHING_PROGRESS_STATUS_SHOULD_REACH_END; + } else { + state = SubS_TimePathing_ComputeProgress(progress, *elapsedTime, waypointTime, totalTime, path->count, + SUBS_TIME_PATHING_ORDER, knots); + } + + switch (state) { + case SUBS_TIME_PATHING_PROGRESS_STATUS_STILL_ON_PATH: + reachedEnd = false; + SubS_TimePathing_ComputeTargetPosXZ(&targetPos->x, &targetPos->z, *progress, SUBS_TIME_PATHING_ORDER, + *waypoint, points, knots); + break; + case SUBS_TIME_PATHING_PROGRESS_STATUS_SHOULD_REACH_END: + endX = points[path->count - 1].x; + endZ = points[path->count - 1].z; + targetPos->x = endX * 1; + targetPos->z = endZ * 1; + reachedEnd = true; + break; + } -#pragma GLOBAL_ASM("asm/non_matchings/code/z_sub_s/func_8013B350.s") + *elapsedTime += timeSpeed; + if (*elapsedTime >= totalTime) { + *elapsedTime = totalTime; + } else if (*elapsedTime < 0) { + *elapsedTime = 0; + } + *waypoint = (*elapsedTime / waypointTime) + (SUBS_TIME_PATHING_ORDER - 1); -#pragma GLOBAL_ASM("asm/non_matchings/code/z_sub_s/func_8013B6B0.s") + return reachedEnd; +} -#pragma GLOBAL_ASM("asm/non_matchings/code/z_sub_s/func_8013B878.s") +/** + * Computes the initial Y component of a time based path + * + * @param[in] globalCtx + * @param[in] path + * @param[in] waypoint the current waypoint, this and the previous two points will be used to compute the target pos + * @param[out] targetPos the computed position to move to, only the Y component has meaning + * + * @note Same note as SubS_TimePathing_Update() + */ +void SubS_TimePathing_ComputeInitialY(GlobalContext* globalCtx, Path* path, s32 waypoint, Vec3f* targetPos) { + Vec3s* points = Lib_SegmentedToVirtual(path->points); + Vec3f posA; + Vec3f posB; + Vec3f posResult; + s32 i = waypoint - (SUBS_TIME_PATHING_ORDER - 1); + s16 max; + s16 min; + s32 isSetup; + CollisionPoly* outPoly = NULL; + s32 bgId = 0; + + max = 0; + min = 0; + isSetup = false; + for (; i <= waypoint; i++) { + if (isSetup) { + if (max < points[i].y) { + max = points[i].y; + } + if (points[i].y < min) { + min = points[i].y; + } + } else { + max = min = points[i].y; + } + isSetup = true; + } + max += 30; + min -= 30; + posA = *targetPos; + posB = *targetPos; + posA.y = max; + posB.y = min; + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &posA, &posB, &posResult, &outPoly, true, true, true, true, + &bgId)) { + targetPos->y = posResult.y; + } +} Path* SubS_GetAdditionalPath(GlobalContext* globalCtx, u8 pathIndex, s32 max) { Path* path; @@ -362,9 +617,189 @@ Path* SubS_GetDayDependentPath(GlobalContext* globalCtx, u8 pathIndex, u8 max, s return path; } -#pragma GLOBAL_ASM("asm/non_matchings/code/z_sub_s/func_8013C068.s") +/** + * Computes the point to move toward using a weight based algorithm that considers 4 points along the path + * + * @param path + * @param waypoint the current waypoint, this and the previous three points will be used to compute the point + * @param point the point computed + * @param progress the main weight value used to compute the weights for the points considered + * @param direction the direciton along the path to move, 1 for forwards, anything else for backwards + * + * @note only computes X and Z components of the point + */ +s32 SubS_WeightPathing_ComputePoint(Path* path, s32 waypoint, Vec3f* point, f32 progress, s32 direction) { + s32 i; + f32 weight0; + f32 weight1; + f32 weight2; + f32 weight3; + s32 lastPoint; + s32 secondLastPoint; + s32 secondPoint; + s32 firstPoint; + f32 xPoints[4]; + f32 zPoints[4]; + f32 oneMinusProgress; + f32 squared; + f32 cubed; + Vec3s* points; + s32 count = path->count; + s32 pointIndex; + s32 tmp; -#pragma GLOBAL_ASM("asm/non_matchings/code/z_sub_s/func_8013C624.s") + if (path == NULL) { + return false; + } + if (direction == 1) { + if (waypoint <= 2) { + pointIndex = 2; + } else { + pointIndex = (waypoint == 3) ? 3 : waypoint; + } + for (i = 0; i < 4; i++, pointIndex--) { + if (pointIndex <= 0) { + pointIndex = 0; + } + points = Lib_SegmentedToVirtual(path->points); + points = &points[pointIndex]; + xPoints[i] = points->x; + zPoints[i] = points->z; + } + lastPoint = count - 1; + secondLastPoint = count - 2; + secondPoint = 3; + firstPoint = 2; + } else { + if (waypoint >= count - 3) { + pointIndex = count - 3; + } else { + tmp = waypoint + 4; + pointIndex = (count == tmp) ? count - 4 : waypoint; + } + for (i = 0; i < 4; i++, pointIndex++) { + if (pointIndex >= path->count) { + pointIndex = path->count - 1; + } + points = Lib_SegmentedToVirtual(path->points); + points = &points[pointIndex]; + xPoints[i] = points->x; + zPoints[i] = points->z; + } + lastPoint = 0; + secondLastPoint = 1; + secondPoint = count - 4; + firstPoint = count - 3; + } + if (waypoint == lastPoint) { + oneMinusProgress = 1.0f - progress; + squared = progress * progress; + cubed = progress * squared; + weight0 = oneMinusProgress * oneMinusProgress * oneMinusProgress; + weight1 = (1.75f * cubed) - (4.5f * squared) + (3.0f * progress); + weight2 = ((-11.0f / 12.0f) * cubed) + (1.5f * squared); + weight3 = (1.0f / 6.0f) * cubed; + } else if (waypoint == secondLastPoint) { + oneMinusProgress = 1.0f - progress; + squared = progress * progress; + cubed = progress * squared; + weight0 = oneMinusProgress * oneMinusProgress * oneMinusProgress * ((void)0, 0.25f); //! FAKE: + weight1 = ((7.0f / 12.0f) * cubed) - (1.25f * squared) + (0.25f * progress) + (7.0f / 12.0f); + weight2 = (-0.5f * cubed) + (0.5f * squared) + (progress * 0.5f) + (1.0f / 6.0f); + weight3 = cubed * (1.0f / 6.0f); + } else if (waypoint == secondPoint) { + oneMinusProgress = 1.0f - progress; + squared = oneMinusProgress * oneMinusProgress; + cubed = oneMinusProgress * squared; + weight0 = (1.0f / 6.0f) * cubed; + weight1 = (-0.5f * cubed) + (0.5f * squared) + (0.5f * oneMinusProgress) + (1.0f / 6.0f); + weight2 = ((7.0f / 12.0f) * cubed) - (1.25f * squared) + (0.25f * oneMinusProgress) + (7.0f / 12.0f); + weight3 = progress * progress * progress * 0.25f; + } else if (((direction == 1) && (firstPoint >= waypoint)) || ((direction != 1) && (waypoint >= firstPoint))) { + oneMinusProgress = 1.0f - progress; + squared = oneMinusProgress * oneMinusProgress; + cubed = oneMinusProgress * squared; + weight0 = (1.0f / 6.0f) * cubed; + weight1 = ((-11.0f / 12.0f) * cubed) + (1.5f * squared); + weight2 = (1.75f * cubed) - (4.5f * squared) + (3.0f * oneMinusProgress); + weight3 = progress * progress * progress; + } else { + oneMinusProgress = 1.0f - progress; + squared = progress * progress; + cubed = squared * progress; + weight0 = oneMinusProgress * oneMinusProgress; + weight0 = oneMinusProgress * weight0 / 6.0f; + weight1 = (cubed * 0.5f) - squared + (2.0f / 3.0f); + weight2 = (cubed / -2.0f) + (squared * 0.5f) + (progress * 0.5f) + (1.0f / 6.0f); + weight3 = cubed / 6.0f; + } + point->x = (weight0 * xPoints[0]) + (weight1 * xPoints[1]) + (weight2 * xPoints[2]) + (weight3 * xPoints[3]); + point->z = (weight0 * zPoints[0]) + (weight1 * zPoints[1]) + (weight2 * zPoints[2]) + (weight3 * zPoints[3]); + + return true; +} + +// WeightPathing System is completely unused +/** + * Moves an actor based on a weight based algorithm that takes into account 4 points along the path + * + * @param actor + * @param path + * @param waypoint the current waypoint, this and the previous three points will be used to move forward + * @param progress the progress towards a given waypoint, used to compute the weights + * @param direction the direction along the path to move, 1 for forwards, anything else for backwards + * @param returnStart boolean, true if the actor should wrap back to start when reaching the end + * + * @return s32 true if actor reached the end of the path in this iteration, false otherwise + */ +s32 SubS_WeightPathing_Move(Actor* actor, Path* path, s32* waypoint, f32* progress, s32 direction, s32 returnStart) { + Vec3f worldPos = actor->world.pos; + Vec3f velocity = actor->velocity; + Vec3f point; + f32 dist; + + if (((direction != 1) && (*waypoint >= (path->count - 2))) || ((direction == 1) && (*waypoint < 2))) { + return false; + } + while (true) { + if (!SubS_WeightPathing_ComputePoint(path, *waypoint, &point, *progress, direction) || + ((s32)(actor->speedXZ * 10000.0f) == 0)) { + return false; + } + dist = Math_Vec3f_DistXZ(&actor->world.pos, &point); + actor->world.rot.y = Math_Vec3f_Yaw(&actor->world.pos, &point); + Actor_MoveWithGravity(actor); + if (Math_Vec3f_DistXZ(&actor->world.pos, &point) < dist) { + break; + } + *progress += 0.1f; + if (*progress >= 1.1f) { + if (direction != 1) { + (*waypoint)++; + if (*waypoint >= (path->count - 2)) { + if (returnStart) { + *waypoint = 0; + } else { + return true; + } + } + } else { + (*waypoint)--; + if (*waypoint < 2) { + if (returnStart) { + *waypoint = path->count - 2; + } else { + return true; + } + } + } + *progress = 0.0f; + } + actor->world.pos = worldPos; + actor->velocity = velocity; + } + return false; +} s32 SubS_CopyPointFromPathCheckBounds(Path* path, s32 pointIndex, Vec3f* dst) { Vec3s* point; @@ -557,10 +992,10 @@ void SubS_DrawShadowTex(Actor* actor, GameState* gameState, u8* tex) { Matrix_Translate(actor->world.pos.x, 0.0f, actor->world.pos.z, MTXMODE_NEW); Matrix_Scale(0.6f, 1.0f, 0.6f, MTXMODE_APPLY); gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); - gSPDisplayList(POLY_OPA_DISP++, gShadowDL); + gSPDisplayList(POLY_OPA_DISP++, gShadowMaterialDL); gDPLoadTextureBlock(POLY_OPA_DISP++, tex, G_IM_FMT_I, G_IM_SIZ_8b, SUBS_SHADOW_TEX_WIDTH, SUBS_SHADOW_TEX_HEIGHT, 0, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, 6, 6, G_TX_NOLOD, G_TX_NOLOD); - gSPDisplayList(POLY_OPA_DISP++, gShadowVtxDL); + gSPDisplayList(POLY_OPA_DISP++, gShadowModelDL); CLOSE_DISPS(gfxCtx); } @@ -617,9 +1052,7 @@ s16 SubS_ComputeTurnToPointRot(s16* rot, s16 rotMax, s16 target, f32 slowness, f * @param[in,out] turnTarget the intermediate target step that headRot and torsoRot step towards * @param[in,out] headRot the computed head rotation * @param[in,out] torsoRot the computed torso rotation - * @param[in] options various options to adjust how the actor turns, see `SubS_ComputeTurnToPointRot and - * TurnOptions/TurnOptionsSet` - * + * @param[in] options various options to adjust how the actor turns, see SubS_ComputeTurnToPointRot() */ s32 SubS_TurnToPoint(Vec3f* point, Vec3f* focusPos, Vec3s* shapeRot, Vec3s* turnTarget, Vec3s* headRot, Vec3s* torsoRot, TurnOptionsSet* options) { @@ -781,8 +1214,8 @@ s32 SubS_CopyPointFromPathList(Path* paths, s32 pathIndex, s32 pointIndex, Vec3f return false; } -u8 SubS_GetPathCount(Path* paths, s32 index) { - Path* path = &paths[index]; +u8 SubS_GetPathCountFromPathList(Path* paths, s32 pathIndex) { + Path* path = &paths[pathIndex]; return path->count; } @@ -1004,8 +1437,7 @@ s32 SubS_FillCutscenesList(Actor* actor, s16 cutscenes[], s16 numCutscenes) { * @param[in] rot the angles to rotate with, uses just the x and y components * @param[out] plane the computed plane * - * Notes: - * The unit input vector is expected to already be normalized (only uses are with the z unit vector) + * @note the unit input vector is expected to already be normalized (only uses are with the z unit vector) * */ void SubS_ConstructPlane(Vec3f* point, Vec3f* unitVec, Vec3s* rot, Plane* plane) { |
