Skip to content

Commit

Permalink
v0.3.2 (#17)
Browse files Browse the repository at this point in the history
* Editor test assemblies for UpdateStateMachine

* Move reusable code to ECSTestsFixture

* Create minimum prefab for state machine tests

* Tests for RaiseAnimationEventsJob

* Performance tests

* Organize folders

* Basic performance tests

* Move required data to Tests folder

* Add performance benchmarks

* Performance benchmark per machine

* Use stable sampler id instead of sampler index

* Add test for ClipSamplerBuffer

* Fix one shot blend out transition

* Disable code coverage during tests

* Add code coverage package as a dependency + bumb version to 0.3.2

* Move PlayOneShot logic to another system

* Move sampling to separate system

* Remove unnecessary NormalizeWeights job

* Add ClipSamplingSystem to StateMachinePeformanceTests

* (WIP) Playables test

* PlayableSystem done

* Improve PlayableState.New

* Only update state machine if it's being played by PlayablesSystem'

* Remove redundant data on StateMachineStateRef

* Support One Shot and transitoining out/in to state machine

* minor tweaks

* Write Playable system tests

* SingleClipStateSystem tests

* Migrate LinearBlend and SingleState update logic to static class

* Separate clip index and clip threshold for performance

* Create Linear Blend State tests

* Consistent performance and debug configurations

* Make AnimationStateUpdate jobs run in parallel

* Adjust performance benchmarks

* Use IJobEntityBatch for UpdateAnimationStatesSystem

* Make sure bone samplings run in parallel if possible

* Remove two tests

* Update readme

* Cleanup: SingleCLip and LinearBlend jobs

* Rename Playable -> AnimationState

* Disable code pipeline change on performance tests

* Delete latio bootstrap from tests (require samples)

* Update changelog

* Improve timeline usage (#14)

* Fix new animation marker time

* Improve timeline usage

* Add clip preview camera orbit (#15)

* Fix preview mesh materials (#16)

* Disallow scene objects on preview object selector

* Update changelog

Co-authored-by: Reslav Hollós <[email protected]>
  • Loading branch information
gabrieldechichi and Radivarig authored Sep 19, 2022
1 parent f0c2dfd commit c316d1e
Show file tree
Hide file tree
Showing 216 changed files with 24,173 additions and 488 deletions.
19 changes: 18 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,26 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic
Versioning](http://semver.org/spec/v2.0.0.html).

## [0.3.2] – 2022-9-18

### Added

- Gracefully blend between multiple One Shot transitions
- Improvements to AnimationClip editor
- Timeline
- Camera orbit
- Unit and Performance Tests

### Changed
- Internal refactors and cleanups:
- Handling of Animation States now separate from State Machine (see UpdateAnimationStates)
- Clip Sampling now separate from State Machine (see ClipSamplingSystem)
- Generalize Blending of States (see BlendAnimationStatesSystem)
- Other bug fixes

## [0.3.1] – 2022-8-13

Netcode sample + bug fixes
- Netcode sample + bug fixes

### Changed

Expand Down
2 changes: 1 addition & 1 deletion Editor/CustomEditors/AnimationClipAssetEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public override void OnInspectorGUI()
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Preview Object");
preview.GameObject = (GameObject)EditorGUILayout.ObjectField(preview.GameObject, typeof(GameObject), true);
preview.GameObject = (GameObject)EditorGUILayout.ObjectField(preview.GameObject, typeof(GameObject), false);
}
using (var c = new EditorGUI.ChangeCheckScope())
{
Expand Down
16 changes: 8 additions & 8 deletions Editor/CustomEditors/RectElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,31 @@ namespace DMotion.Editor
{
internal struct RectElement
{
public Rect Rect;
public Vector2 Position;
public float Height;
public float ControlWidth;
public float VisualWidth;

public RectElement(Rect rect, float controlWidth, float visualWidth)
public RectElement(Vector2 position, float height, float controlWidth, float visualWidth)
{
Rect = rect;
Position = position;
Height = height;
ControlWidth = controlWidth;
VisualWidth = visualWidth;
}

public Rect VisualRect
{
get
{
Rect.width = VisualWidth;
return Rect;
return new Rect(Position.x - VisualWidth/2, Position.y, VisualWidth, Height);
}
}
public Rect ControlRect
{
get
{
Rect.width = ControlWidth;
return Rect;
return new Rect(Position.x - ControlWidth/2, Position.y, ControlWidth, Height);
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion Editor/DMotion.Editor.asmdef
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
"name": "DMotion.Editor",
"rootNamespace": "",
"references": [
"GUID:c39b61f68344c5e46ad36a286c992104"
"GUID:c39b61f68344c5e46ad36a286c992104",
"GUID:343deaaf83e0cee4ca978e7df0b80d21",
"GUID:2bafac87e7f4b9b418d9448d219b01ab",
"GUID:0acc523941302664db1f4e527237feb3",
"GUID:27619889b8ba8c24980f49ee34dbb44a"
],
"includePlatforms": [
"Editor"
Expand Down
79 changes: 68 additions & 11 deletions Editor/EditorPreview/PlayableGraphPreview.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ public GameObject GameObject
public abstract float SampleTime { get; }
public abstract float NormalizedSampleTime { get; set; }

private float camDistance;
private Vector3 camPivot;
private Vector3 lookAtOffset;
private Vector2 camEuler;
private Vector2 lastMousePosition;
private Boolean isMouseDrag;

public void Initialize()
{
AnimationMode.StartAnimationMode();
Expand Down Expand Up @@ -108,19 +115,19 @@ private void CreatePreviewUtility()
previewRenderUtility?.Cleanup();
previewRenderUtility = new PreviewRenderUtility();

var bounds = skinnedMeshRenderer.bounds;
var camPos = new Vector3(0f, bounds.size.y * 3, 10f);
previewRenderUtility.camera.transform.position = camPos;
var camRot = Quaternion.LookRotation(bounds.center - camPos);
previewRenderUtility.camera.transform.rotation = camRot;
previewRenderUtility.camera.nearClipPlane = 0.3f;
previewRenderUtility.camera.farClipPlane = 3000f;

var light1 = previewRenderUtility.lights[0];
light1.type = LightType.Directional;
light1.color = Color.white;
light1.intensity = 1;
light1.transform.rotation = camRot;

previewRenderUtility.camera.nearClipPlane = 0.3f;
previewRenderUtility.camera.farClipPlane = 3000f;

camPivot = skinnedMeshRenderer.transform.position;
lookAtOffset = Vector3.up * skinnedMeshRenderer.bounds.size.y / 2;
camDistance = 10;
camEuler = new Vector2(45, 30);
HandleCamera(true);
}
public void RefreshPreviewObjects()
{
Expand Down Expand Up @@ -189,15 +196,65 @@ public void DrawPreview(Rect r, GUIStyle background)
for (var i = 0; i < previewMesh.subMeshCount; i++)
{
previewRenderUtility.DrawMesh(previewMesh, Matrix4x4.identity,
skinnedMeshRenderer.sharedMaterial, i);
skinnedMeshRenderer.sharedMaterials[i], i);
}

HandleCamera();
previewRenderUtility.camera.Render();

var resultRender = previewRenderUtility.EndPreview();
GUI.DrawTexture(r, resultRender, ScaleMode.StretchToFill, false);
}
}
}

private void HandleCamera(Boolean force = false)
{
// must set hotControl or MouseUp event will not be detected outside window
int controlId = GUIUtility.GetControlID(FocusType.Passive);
if (Event.current.GetTypeForControl(controlId) == EventType.MouseDown)
GUIUtility.hotControl = controlId;

var isMouseDown = Event.current.type == EventType.MouseDown;
var isMouseUp = Event.current.type == EventType.MouseUp;

// track mouse drag on our own because out of window event types are "Layout" or "repaint"
if (Event.current.type == EventType.MouseDrag)
isMouseDrag = true;

if (force || isMouseDrag)
{
Vector2 delta = Event.current.mousePosition - lastMousePosition;

// ignore large deltas from SetWantsMouseJumping (Screen.currentResolution minus 20)
if (Mathf.Abs(delta.x) > Screen.width) delta.x = 0;
if (Mathf.Abs(delta.y) > Screen.height) delta.y = 0;

camEuler += delta;

camEuler.y = Mathf.Clamp(camEuler.y, -179, -1); // -1 to avoid perpendicular angles

previewRenderUtility.camera.transform.position =
Quaternion.Euler(-camEuler.y, camEuler.x, 0) * (Vector3.up * camDistance -camPivot) + camPivot;
previewRenderUtility.camera.transform.LookAt(camPivot + lookAtOffset);

// matcap feel
previewRenderUtility.lights[0].transform.rotation = previewRenderUtility.camera.transform.rotation;

// needed for repaint
GUI.changed = true;
}

// Undocumented feature that wraps the cursor around the screen edges by Screen.currentResolution minus 20
if (isMouseDown) EditorGUIUtility.SetWantsMouseJumping(1);
if (isMouseUp) {
EditorGUIUtility.SetWantsMouseJumping(0);
isMouseDrag = false;
}

// store lastMousePosition starting with mouseDown to prevent wrong initial delta
if (isMouseDown || isMouseDrag) {
lastMousePosition = Event.current.mousePosition;
}
}
}
}
56 changes: 34 additions & 22 deletions Editor/PropertyDrawers/AnimationEventsPropertyDrawer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,39 +226,50 @@ private void OnMouseDown(Event current)
{
eventMarkerDragIndex = i;
current.Use();
return;
break;
}
}

if (timeMarker.ControlRect.Contains(current.mousePosition))
{
isDraggingTimeMarker = true;

isDraggingTimeMarker = true;

if (eventMarkerDragIndex > -1) {
var time = clipAsset.Events[eventMarkerDragIndex].NormalizedTime;
timeMarkerTime = time;
preview.NormalizedSampleTime = time;
current.Use();
}
else {
SetTimeMarkerPosition(current.mousePosition.x);
current.Use();
return;
}

if (current.clickCount == 2 && dragArea.Contains(current.mousePosition))
{
var time = clipAsset.Clip.length * (current.mousePosition.x - dragArea.x) / dragArea.width;
var time = PixelsToNormalizedTime(current.mousePosition.x, dragArea);
AddEvent(time);
current.Use();
return;
}
}

private void SetTimeMarkerPosition(float xPosition)
{
var time = PixelsToNormalizedTime(xPosition, dragArea);
timeMarkerTime = time;
preview.NormalizedSampleTime = time;
}

private void OnMouseDrag(in Event currentEvent)
{
if (isDraggingTimeMarker)
{
var time = PixelsToNormalizedTime(currentEvent.mousePosition.x, timeMarker.VisualRect, dragArea);
timeMarkerTime = time;
preview.NormalizedSampleTime = time;
SetTimeMarkerPosition(currentEvent.mousePosition.x);
currentEvent.Use();
}
else if (eventMarkerDragIndex >= 0)

if (eventMarkerDragIndex >= 0)
{
var eventMarker = eventMarkers[eventMarkerDragIndex];
var normalizedTime = PixelsToNormalizedTime(currentEvent.mousePosition.x, eventMarker.VisualRect, dragArea);
var normalizedTime = PixelsToNormalizedTime(currentEvent.mousePosition.x, dragArea);
preview.NormalizedSampleTime = normalizedTime;

clipAsset.Events[eventMarkerDragIndex].NormalizedTime = normalizedTime;
Expand Down Expand Up @@ -288,8 +299,8 @@ private void DrawEventsDragArea(Rect area)
GUI.DrawTexture(bottomBarRect, WhiteTex);
}
{
timeMarker = new RectElement(area, 10, 2);
timeMarker.Rect.x = NormalizedTimeToPixels(timeMarkerTime, timeMarker.VisualRect, area);
timeMarker = new RectElement(area.position, area.height, 10, 2);
timeMarker.Position.x = NormalizedTimeToPixels(timeMarkerTime, area);
GUI.color = Color.red;
GUI.DrawTexture(timeMarker.VisualRect, WhiteTex);
}
Expand All @@ -298,25 +309,26 @@ private void DrawEventsDragArea(Rect area)
for (var i = 0; i < clipAsset.Events.Length; i++)
{
var e = clipAsset.Events[i];
var eventMarkerRect = new RectElement(area, 10, 5);
eventMarkerRect.Rect.height *= 0.9f;
eventMarkerRect.Rect.x = NormalizedTimeToPixels(e.NormalizedTime, eventMarkerRect.VisualRect, area);
var eventMarkerRect = new RectElement(area.position, area.height, 30, 5);
eventMarkerRect.Height *= 0.9f;
eventMarkerRect.Position.x = NormalizedTimeToPixels(e.NormalizedTime, area);

GUI.color = i == eventMarkerDragIndex ? selectedEventColor : unselectedEventColor;
GUI.DrawTexture(eventMarkerRect.VisualRect, EventMarkerTex, ScaleMode.ScaleAndCrop);
eventMarkers.Add(eventMarkerRect);
}
}
}

private float NormalizedTimeToPixels(float time, in Rect rect, in Rect parentRect)
private float NormalizedTimeToPixels(float time, in Rect parentRect)
{
var normalizedTime = Mathf.Clamp01(time);
return parentRect.xMin + (parentRect.width - rect.width) * normalizedTime;
return parentRect.xMin + parentRect.width * normalizedTime;
}

private float PixelsToNormalizedTime(float x, in Rect rect, in Rect parentRect)
private float PixelsToNormalizedTime(float x, in Rect parentRect)
{
return Mathf.Clamp01((x - parentRect.xMin) / (parentRect.width - rect.width));
return Mathf.Clamp01((x - parentRect.xMin) / parentRect.width);
}
}
}
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Overall, this system should feel similar to Unity's Mechanim system, however the
- Loop and speed control
- Blending between states during transitions
- Boolean transitions
- End Time transtiions
- End Time transtions
- Blend Parameters (float parameters for controlling Blend Trees)
- Animation Events
- Root Motion (with WriteGroup support, if you need to override default behaviour)
Expand All @@ -29,8 +29,10 @@ Overall, this system should feel similar to Unity's Mechanim system, however the

### Coming soon

- Enum Transitions
- 2D Blend Tree (cartesian/freeform)
- State Machine Override (a.k.a Animator Override Controller)
- IK Support

### Planned
- Retargeting for Humanoid skeletons
Expand Down
4 changes: 4 additions & 0 deletions Runtime/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("DMotion.Tests.Editor")]
[assembly: InternalsVisibleTo("DMotion.Tests.Runtime")]
3 changes: 3 additions & 0 deletions Runtime/AssemblyInfo.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 12 additions & 4 deletions Runtime/Authoring/AnimationStateMachineAuthoring.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public static class StateMachineEditorConstants
{
public const string DMotionPath = "DMotion";
}

public class AnimationStateMachineAuthoring : MonoBehaviour, IConvertGameObjectToEntity, IRequestBlobAssets
{
public GameObject Owner;
Expand All @@ -36,12 +37,19 @@ public void Convert(Entity entity, EntityManager dstManager, GameObjectConversio
StateMachineBlob = stateMachineBlob,
ClipsBlob = clipsBlob,
ClipEventsBlob = clipEventsBlob,
CurrentState = AnimationState.Null,
NextState = AnimationState.Null,
Weight = 1
CurrentState = StateMachineStateRef.Null
};

dstManager.AddComponentData(entity, stateMachine);
dstManager.AddComponentData(entity, AnimationStateMachineTransitionRequest.Null);

dstManager.AddBuffer<SingleClipState>(entity);
dstManager.AddBuffer<LinearBlendStateMachineState>(entity);

dstManager.AddBuffer<AnimationState>(entity);
dstManager.AddComponentData(entity, AnimationStateTransition.Null);
dstManager.AddComponentData(entity, AnimationStateTransitionRequest.Null);
dstManager.AddComponentData(entity, AnimationCurrentState.Null);
var clipSamplers = dstManager.AddBuffer<ClipSampler>(entity);
clipSamplers.Capacity = 10;

Expand Down Expand Up @@ -110,7 +118,7 @@ public void RequestBlobAssets(Entity entity, EntityManager dstEntityManager, Gam
{
ValidateStateMachine();
clipsBlobHandle = conversionSystem.RequestClipsBlob(Animator, StateMachineAsset.Clips);
stateMachineBlobHandle = conversionSystem.RequestStateMachineBlob(Animator.gameObject, new StateMachineBlobBakeData()
stateMachineBlobHandle = conversionSystem.RequestStateMachineBlob(Animator.gameObject, new StateMachineBlobBakeData
{
StateMachineAsset = StateMachineAsset
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Latios.Authoring;
using Latios.Kinemation;
using Latios.Kinemation.Authoring;
using Unity.Entities;
using UnityEngine;

namespace DMotion.Authoring
Expand Down
Loading

0 comments on commit c316d1e

Please sign in to comment.