Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ public class AnimationSystem : BasePlayerBehaviour
[Inject] private readonly NpcService _npcService;



public Transform RootBone;

// Caching bone Transforms makes it faster to apply them to animations later.
Expand All @@ -55,7 +54,16 @@ public class AnimationSystem : BasePlayerBehaviour
private bool _isSittingInverted;

// Some sitting animations are rotated wrong. They need to be inverted in y-axis.
private string[] _animationsToInvertYAxis = new[] { "S_BENCH_S1", "S_THRONE_S1" };
private string[] _animationsToInvertYAxis = { "S_BENCH_S1", "S_THRONE_S1" };


// Attack information
private bool IsAttack => AttackAnimation.NotNullOrEmpty();
private string AttackAnimation;
private string AttackHitLimb;
private List<int> AttackOptFrame;
private List<int> AttackHitEnd;
private List<int> AttackWindowFrames;


protected override void Awake()
Expand Down Expand Up @@ -93,6 +101,7 @@ public void DisableObject()
_bones[i].SetLocalPositionAndRotation(_initialMeshBonePos[i], _initialMeshBoneRot[i]);
}

DisableAttack();
_trackInstances.Clear();
}

Expand Down Expand Up @@ -220,8 +229,7 @@ public void StopAnimation(string animationName)
}
#endif

var newTrack = _animationService.GetTrack(animationName, Properties.MdsNameBase, Properties.MdsNameOverlay);

var trackToStop = _animationService.GetTrack(animationName, Properties.MdsNameBase, Properties.MdsNameOverlay);

Logger.LogEditor($"Stopping animation: {animationName}", LogCat.Animation);
AnimationTrackInstance instanceToStop = null;
Expand All @@ -231,10 +239,13 @@ public void StopAnimation(string animationName)
{
var instance = _trackInstances[i];
// If animation is found, then mark it as "BlendOut"
if (instance.Track.Name.EqualsIgnoreCase(newTrack.Name))
if (instance.Track.Name.EqualsIgnoreCase(trackToStop.Name))
{
instanceToStop = instance;
instance.BlendOutTrack(instance.Track.BlendOut);

if (AttackAnimation == trackToStop.Name)
AttackAnimation = null;
// Do not break. We could potentially need to stop multiple instances of the same animation.
}
}
Expand Down Expand Up @@ -400,6 +411,23 @@ private void PreStopAnimation(AnimationTrackInstance instance)
{
if (_animationsToInvertYAxis.Contains(instance.Track.Name.ToUpper()))
_isSittingInverted = false;

if (AttackAnimation.EqualsIgnoreCase(instance.AnimationName))
DisableAttack();
}

private void DisableAttack()
{
if (AttackAnimation == null)
return;

AttackAnimation = null;
AttackHitLimb = null;
AttackOptFrame = null;
AttackHitEnd = null;
AttackWindowFrames = null;

// FIXME - We need to disable all limbs, if they are still active from current attack window.
}

private void ApplyFinalPose()
Expand Down Expand Up @@ -532,6 +560,22 @@ private void ApplyEventTags(AnimationTrackInstance trackInstance)
case EventType.TorchInventory:
// TODO - I assume this means: if torch is in inventory, then put it out. But not really sure. Need a NPC with real usage of it to predict right.
break;
case EventType.HitLimb:
AttackHitLimb = eventTag.Slots.Item1;
AttackAnimation = trackInstance.AnimationName;
break;
case EventType.OptimalFrame:
AttackOptFrame = eventTag.Slots.Item1.Split(' ').Select(i => Convert.ToInt32(i)).ToList();
break;
case EventType.HitEnd:
AttackHitEnd = eventTag.Slots.Item1.Split(' ').Select(i => Convert.ToInt32(i)).ToList();
break;
case EventType.ComboWindow:
AttackWindowFrames = eventTag.Slots.Item1.Split(' ').Select(i => Convert.ToInt32(i)).ToList();
break;
// Unused. @see: https://gothic-modding-community.github.io/gmc/zengin/anims/events/#def_dir
case EventType.HitDirection:
break;
default:
Logger.LogWarning($"EventType.type {eventTag.Type} not yet supported.", LogCat.Animation);
break;
Expand Down
75 changes: 0 additions & 75 deletions Assets/UnZENity-Core/Scripts/Adapters/Npc/FistFightAdapter.cs

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;
using GUZ.Core.Adapters.Npc;
using GUZ.Core.Logging;
using GUZ.Core.Models.Vm;
using GUZ.Core.Services.Caches;
using Reflex.Attributes;
using UnityEngine;
using ZenKit;
using Logger = GUZ.Core.Logging.Logger;
using Mesh = UnityEngine.Mesh;
using Vector3 = System.Numerics.Vector3;

namespace GUZ.Core.Domain.Meshes.Builder
Expand All @@ -27,6 +27,7 @@ public virtual void SetBodyData(ExtSetVisualBodyData body)
public override GameObject Build()
{
BuildViaMdmAndMdh();
CreateBoneColliders();

return RootGo;
}
Expand Down Expand Up @@ -87,5 +88,74 @@ protected override List<Vector3> GetSoftSkinMeshPositions(ISoftSkinMesh softSkin
{
return _npcArmorCacheService.TryGetPositions(softSkinMesh, Mdh);
}

/// <summary>
/// During fight situations, the bones are checked for physical collision via e.g. *eventTag(0 "DEF_HIT_LIMB" "BIP01 R HAND")
/// We therefore calculate a box collider for all of the limbs/bones and disable it until its needed at fight time.
///
/// Hint: We assume that the bounding boxes of the bones will stay stable and no long stretches will happen
/// (which would force a recalculation).
/// </summary>
private void CreateBoneColliders()
{
var renderers = RootGo.GetComponentsInChildren<SkinnedMeshRenderer>();
var boneBoundsMap = new Dictionary<Transform, Bounds>();

foreach (var renderer in renderers)
{
var mesh = renderer.sharedMesh;
if (mesh == null)
continue;

var vertices = mesh.vertices;
var weights = mesh.boneWeights;
var smrBones = renderer.bones;
var bindPoses = mesh.bindposes;

for (var i = 0; i < vertices.Length; i++)
{
var weight = weights[i];
var boneIdx = weight.boneIndex0;

// Use vertices with more than 10% weight.
if (weight.weight0 > 0.1f)
{
var boneTransform = smrBones[boneIdx];

// DIRECT CALCULATION:
// Multiply the vertex by the bind pose matrix to get the
// position relative to the bone at the time of rigging.
var localPt = bindPoses[boneIdx].MultiplyPoint3x4(vertices[i]);

if (!boneBoundsMap.ContainsKey(boneTransform))
{
boneBoundsMap[boneTransform] = new Bounds(localPt, UnityEngine.Vector3.zero);
}
else
{
var bounds = boneBoundsMap[boneTransform];
bounds.Encapsulate(localPt);
boneBoundsMap[boneTransform] = bounds;
}
}
}
}

// Apply to Colliders
foreach (var boneBound in boneBoundsMap)
{
var boneTransform = boneBound.Key;
var finalBounds = boneBound.Value;

if (finalBounds.size.sqrMagnitude < 0.0001f)
continue;

var col = boneTransform.gameObject.AddComponent<BoxCollider>();
col.center = finalBounds.center;
col.size = finalBounds.size;
col.isTrigger = true; // We want to calculate Triggering only, not pushing/colliding.
col.enabled = false; // Will be enabled at runtime during fights when DEF_HIT_LIMB is set.
}
}
}
}