Skip to content

Commit

Permalink
Smooth frame interpolation + Soloud audio limit (Albeoris#509)
Browse files Browse the repository at this point in the history
- OVERHAUL of the interpolation systems (Battle, Field and World), 60fps+ are much smoother, fixed related visual glitches
- Added a limit on how many times the same sound can play simultaneously to soloud backend
- Fixed Big enemies have small shadow until you hit them. Albeoris#510
  • Loading branch information
SamsamTS authored Jun 8, 2024
1 parent bdfce75 commit 532119d
Show file tree
Hide file tree
Showing 15 changed files with 695 additions and 452 deletions.
69 changes: 13 additions & 56 deletions Assembly-CSharp/FF9/btl_mot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,65 +203,22 @@ public static Boolean IsLoopingMotion(BattlePlayerCharacter.PlayerMotionIndex in

public static void PlayAnim(BTL_DATA btl)
{
btl._smoothUpdatePlayingAnim = false;
if (btl.currentAnimationName == null)
if (btl.currentAnimationName == null || btl.bi.stop_anim != 0)
return;
GameObject gameObject = btl.gameObject;
String currentAnimationName = btl.currentAnimationName;
UInt16 animMaxFrame = GeoAnim.geoAnimGetNumFrames(btl, currentAnimationName);
Boolean reverseSpeed = btl.animSpeed < 0f;
Single animFrame = btl.evt.animFrame; // + (reverseSpeed ? -btl.animFrameFrac : btl.animFrameFrac);
if (!gameObject.GetComponent<Animation>().IsPlaying(currentAnimationName))
{
if (gameObject.GetComponent<Animation>().GetClip(currentAnimationName) == null)

Animation anim = btl.gameObject.GetComponent<Animation>();
String animName = btl.currentAnimationName;
Int32 animMaxFrame = GeoAnim.geoAnimGetNumFrames(btl, animName);
if (!anim.IsPlaying(animName))
{
if (anim.GetClip(animName) == null)
return;
gameObject.GetComponent<Animation>().Play(currentAnimationName);
}
AnimationState clipState = gameObject.GetComponent<Animation>()[currentAnimationName];
Single time = animMaxFrame == 0 ? 0f : Mathf.Clamp(animFrame / animMaxFrame * clipState.length, 0f, clipState.length);
Int32 animLoopFrame = GeoAnim.getAnimationLoopFrame(btl);
clipState.speed = 0f;
btl._smoothUpdatePlayingAnim = true;
btl._smoothUpdateAnimTimePrevious = clipState.time;
if (animMaxFrame != 0 && btl.bi.disappear == 0 && !btl_mot.IsAnimationFrozen(btl))
{
if (btl.evt.animFrame == 0 && !reverseSpeed)
btl._smoothUpdateAnimTimePrevious = time - btl.animSpeed / animMaxFrame * clipState.length;
else if (btl.evt.animFrame == animLoopFrame && reverseSpeed)
btl._smoothUpdateAnimTimePrevious = time + btl.animSpeed / animMaxFrame * clipState.length;
}
btl._smoothUpdateAnimTimeActual = time;
clipState.time = time;
gameObject.GetComponent<Animation>().Sample();
if (btl.evt.animFrame == animLoopFrame && !reverseSpeed)
{
// Try to smoothen standard animation chains
if (btl_mot.checkMotion(btl, BattlePlayerCharacter.PlayerMotionIndex.MP_RUN))
{
gameObject.GetComponent<Animation>().CrossFade(btl.mot[(Int32)BattlePlayerCharacter.PlayerMotionIndex.MP_RUN_TO_ATTACK], 1f / FPSManager.GetMainLoopSpeed());
btl._smoothUpdatePlayingAnim = false;
}
else if (btl_mot.checkMotion(btl, BattlePlayerCharacter.PlayerMotionIndex.MP_RUN_TO_ATTACK))
{
gameObject.GetComponent<Animation>().CrossFade(btl.mot[(Int32)BattlePlayerCharacter.PlayerMotionIndex.MP_ATTACK], 1f / FPSManager.GetMainLoopSpeed());
btl._smoothUpdatePlayingAnim = false;
}
else if (btl_mot.checkMotion(btl, BattlePlayerCharacter.PlayerMotionIndex.MP_ATTACK))
{
gameObject.GetComponent<Animation>().CrossFade(btl.mot[(Int32)BattlePlayerCharacter.PlayerMotionIndex.MP_BACK], 1f / FPSManager.GetMainLoopSpeed());
btl._smoothUpdatePlayingAnim = false;
}
else if (btl_mot.checkMotion(btl, BattlePlayerCharacter.PlayerMotionIndex.MP_BACK))
{
gameObject.GetComponent<Animation>().CrossFade(btl.mot[(Int32)BattlePlayerCharacter.PlayerMotionIndex.MP_ATK_TO_NORMAL], 1f / FPSManager.GetMainLoopSpeed());
btl._smoothUpdatePlayingAnim = false;
}
else if (btl_mot.checkMotion(btl, BattlePlayerCharacter.PlayerMotionIndex.MP_ATK_TO_NORMAL))
{
gameObject.GetComponent<Animation>().CrossFade(btl.mot[(Int32)BattlePlayerCharacter.PlayerMotionIndex.MP_IDLE_NORMAL], 1f / FPSManager.GetMainLoopSpeed());
btl._smoothUpdatePlayingAnim = false;
}
anim.Play(animName);
}
AnimationState animState = anim[animName];
animState.speed = 0f;
animState.time = (Single)btl.evt.animFrame / animMaxFrame * animState.length;
anim.Sample();
}

public static Int32 GetDirection(BTL_DATA btl)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,7 @@ private void ProcessAnime(Actor actor)
}
else
{
int num3 = this.NextFrame(actor);
if (actor.sid == 17)
{
}
if (num3 >= (int)actor.frameN)
if (NextFrame(actor) >= actor.frameN)
{
actor.animFrame = 0;
if (actor.anim == actor.idle)
Expand Down Expand Up @@ -225,4 +221,4 @@ private void FinishJump(Actor actor)
actor.inFrame = (Byte)0;
actor.outFrame = Byte.MaxValue;
}
}
}
70 changes: 31 additions & 39 deletions Assembly-CSharp/Global/Event/Engine/EventEngineUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1839,15 +1839,7 @@ public static Int32 GetCharAnimFrame(GameObject go, Int32 AnmNo)
if (go.GetComponent<Animation>().GetClip(name) == null)
return -1;
Single f = go.GetComponent<Animation>()[name].clip.length * go.GetComponent<Animation>()[name].clip.frameRate;
Int32 int1 = Mathf.CeilToInt(f);
Int32 int2 = Mathf.FloorToInt(f);
Int32 int3 = Mathf.RoundToInt(f);
Int32 num = int1;
if (int3 == int1)
num = int1;
else if (int3 == int2)
num = int2;
return num + 1;
return Mathf.RoundToInt(f) + 1;
}

public static Byte[] loadEventData(String ebFileName, String ebSubFolder)
Expand Down Expand Up @@ -1930,7 +1922,7 @@ public class BinaryScript
public List<Entry> entries;

public BinaryScript(Byte[] raw)
{
{
if (raw.Length < 4)
return;
if (raw[0] != 'E' || raw[1] != 'V')
Expand Down Expand Up @@ -1980,7 +1972,7 @@ public HashSet<UInt32> GetVariableUsage(Boolean withGeneral = true, Boolean with
}

public static Boolean IsVariableInUsage(HashSet<UInt32> pool, UInt32 specific)
{
{
if (pool.Contains(specific))
return true;
if ((specific & NON_BOOLEAN_FLAG) != 0)
Expand All @@ -1992,10 +1984,10 @@ public static Boolean IsVariableInUsage(HashSet<UInt32> pool, UInt32 specific)
else if (pool.Contains((specific & 0xFFFFFFF8u) | NON_BOOLEAN_FLAG))
return true;
return false;
}
}

public static UInt32 GetVariableFromIndex(EBin.VariableSource source, UInt16 index, Boolean isBoolType = false)
{
{
if (isBoolType)
return index | ((UInt32)source << 26);
return NON_BOOLEAN_FLAG | (UInt32)(index << 3) | ((UInt32)source << 26);
Expand All @@ -2009,16 +2001,16 @@ public class Entry
public Byte flags;
public List<Function> functions;
public Entry(Byte[] raw, ref UInt32 pos, Byte id, Byte lvc, Byte fl, UInt32 maxPosEntry)
{
{
sid = id;
localVarCount = lvc;
flags = fl;
functions = new List<Function>();
if (maxPosEntry <= pos)
{
{
type = 0xFF;
return;
}
}
type = raw[pos++];
Int32 funcCount = raw[pos++];
UInt16[] funcTag = new UInt16[funcCount];
Expand Down Expand Up @@ -2054,7 +2046,7 @@ public UInt32 GetBinarySize()
}

public class Function
{
{
public Int32 tagNumber;
public List<Code> codes;

Expand All @@ -2067,28 +2059,28 @@ public Function(Byte[] raw, ref UInt32 pos, Int32 tag, UInt32 maxPosFunc)
}

public HashSet<UInt32> GetVariableUsage(Boolean withGeneral = true, Boolean withGlobal = true, Boolean withLocal = true)
{
{
HashSet<UInt32> result = new HashSet<UInt32>();
HashSet<UInt32> piece;
Int32 i;
foreach (Code c in codes)
for (i = 0; i < c.arguments.Count; i++)
{
{
piece = c.arguments[i].GetVariableUsage(withGeneral, withGlobal, withLocal);
if (piece != null)
result.UnionWith(piece);
}
}
return result;
}

public class Code
{
{
public UInt16 opcode;
public Byte argFlag;
public List<Argument> arguments;

public Code(Byte[] raw, ref UInt32 pos)
{
{
arguments = new List<Argument>();
opcode = raw[pos++];
if (opcode == 0xFF)
Expand All @@ -2115,7 +2107,7 @@ public Code(Byte[] raw, ref UInt32 pos)
}

public UInt32 GetBinarySize()
{
{
UInt32 size = opcode >= 0x100 ? 2u : 1u;
if (opcode >= 0x10 && opArgCount[opcode] != 0)
size++;
Expand Down Expand Up @@ -2148,7 +2140,7 @@ public UInt32 GetBinarySize()
};

Byte GetArgTypeSize(UInt16 op, Int32 i)
{
{
if (op == 0x29)
return 4;
if (op == 0x06 || op == 0x0B || op == 0x0D)
Expand Down Expand Up @@ -2178,22 +2170,22 @@ Byte GetArgTypeSize(UInt16 op, Int32 i)
};

public abstract class Argument
{
{
public abstract Boolean IsConstantValue();
public abstract HashSet<UInt32> GetVariableUsage(Boolean withGeneral = true, Boolean withGlobal = true, Boolean withLocal = true);
public abstract UInt32 GetBinarySize();
}

public class ArgumentConstant : Argument
{
{
public Int64 value;
public Byte size;
public override Boolean IsConstantValue() => true;
public override HashSet<UInt32> GetVariableUsage(Boolean withGeneral = true, Boolean withGlobal = true, Boolean withLocal = true) => null;
public override UInt32 GetBinarySize() => size;

public ArgumentConstant(Byte[] raw, ref UInt32 pos, Byte sz)
{
{
size = sz;
if (size == 1)
value = raw[pos++];
Expand All @@ -2211,7 +2203,7 @@ public class ArgumentExpression : Argument
public List<ExpressionOperation> operations;

public ArgumentExpression(Byte[] raw, ref UInt32 pos)
{
{
operations = new List<ExpressionOperation>();
do
operations.Add(new ExpressionOperation(raw, ref pos));
Expand All @@ -2220,10 +2212,10 @@ public ArgumentExpression(Byte[] raw, ref UInt32 pos)

public override Boolean IsConstantValue() => false;
public override HashSet<UInt32> GetVariableUsage(Boolean withGeneral = true, Boolean withGlobal = true, Boolean withLocal = true)
{
{
HashSet<UInt32> result = new HashSet<UInt32>();
foreach (ExpressionOperation op in operations)
{
{
if ((op.IsGeneralVariable() && withGeneral) || (op.IsGlobalVariable() && withGlobal) || (op.IsLocalVariable() && withLocal))
{
UInt32 variable = op.IsGeneralVariable() ? (UInt32)EBin.VariableSource.Global << 26 : (op.IsGlobalVariable() ? (UInt32)EBin.VariableSource.Map << 26 : (UInt32)EBin.VariableSource.Instance << 26);
Expand All @@ -2249,11 +2241,11 @@ public override HashSet<UInt32> GetVariableUsage(Boolean withGeneral = true, Boo
result.Add(variable);
}
}
}
}
return result;
}
public override UInt32 GetBinarySize()
{
{
UInt32 result = 0;
foreach (ExpressionOperation op in operations)
result += 1u + (op.arguments == null ? 0u : (UInt32)op.arguments.Length);
Expand All @@ -2262,12 +2254,12 @@ public override UInt32 GetBinarySize()
}

public class ExpressionOperation
{
{
public Byte operation;
public Byte[] arguments;

public ExpressionOperation(Byte[] raw, ref UInt32 pos)
{
{
operation = raw[pos++];
if (!IsConstantValue() && !IsVariable())
{
Expand Down Expand Up @@ -2304,10 +2296,10 @@ public ExpressionOperation(Byte[] raw, ref UInt32 pos)
public Boolean IsConstantValue() => operation == 0x7D || operation == 0x7E;
}
}
}
}
}
}

private const UInt32 NON_BOOLEAN_FLAG = 0x40000u;
}
#endregion
}
}
#endregion
}
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,6 @@ public void UpdateMovement(Boolean copyLastPos = true)
private void PlayAnimationViaEventScript()
{
String curAnim = FF9DBAll.AnimationDB.GetValue(this.originalActor.anim);
this._smoothUpdatePlayingAnim = false;
if (!this.animation.IsPlaying(curAnim))
{
AnimationClip clip = this.animation.GetClip(curAnim);
Expand All @@ -426,9 +425,6 @@ private void PlayAnimationViaEventScript()
{
Single time = (Single)this.originalActor.animFrame / (Single)this.originalActor.frameN * this.animation[curAnim].length;
this.animation[curAnim].speed = 0f;
this._smoothUpdatePlayingAnim = true;
this._smoothUpdateAnimTimePrevious = this.animation[curAnim].time;
this._smoothUpdateAnimTimeActual = time;
this.animation[curAnim].time = time;
this.animation.Sample();
}
Expand Down
Loading

0 comments on commit 532119d

Please sign in to comment.