// Copyright 2021 Pose AI Ltd. All rights reserved using UnityEngine; using System.Collections.Generic; namespace PoseAI { /// /// Component to load /// public class PoseAIRuntimeLoader { // the path must be relative to a 'Resources' folder in the project structure. If necessary move files into a Resources folder public static string DefaultAnimationControllerPath = "StarterAssets/ThirdPersonController/Character/Animations/StarterAssetsThirdPerson"; // specify which type of rig you use public static PoseAI_Rigs rigType = PoseAI_Rigs.Unity; // specify the rig remapping here, if any, to make sure it is set for each new avatar public static string Remapping = ""; /* * pass in * 1. gameCharacter to be initialized with PoseAIComponents * 2. a configured avatar from a template character * 3. either a new port for a new source, or an exsiting PoseAISource (i.e. if player is already connected and we are animating a new character or scene). * 4. optionally pass in a different animation controller if you want to use different animation controllers in different parts of the game */ public static bool RuntimeLoad(GameObject gameCharacter, Avatar avatar, int port = 0, PoseAISourceDirect poseAISourceIn = null, RuntimeAnimatorController animationController = null) { Debug.Assert(poseAISourceIn != null || port != 0); /* * adds an animator component if it doesn't exist. Then configures key required properties: * 1. runtimeAnimatorController - this should be a controller with IK pass enabled and the state machine logic referred to in the PoseAICharacterController * 2. avatar - this should be a configured humanoid avatar with the correct mappings for the Mecanim system. Difficult to create at Runtime, easier to pass in from a template character in the project. */ Animator animator = gameCharacter.GetComponent(); if (animator == null) animator = gameCharacter.AddComponent() as Animator; if (animationController == null) { animator.runtimeAnimatorController = Resources.Load(DefaultAnimationControllerPath) as RuntimeAnimatorController; if (animator.runtimeAnimatorController == null) { Debug.LogWarning("Failed to load animator at " + DefaultAnimationControllerPath); return false; } } else { animator.runtimeAnimatorController = animationController; } animator.avatar = avatar; /* * adds an default character controller and configures the capsule. may need to be tweaked to suit game and rig */ CharacterController characterController = gameCharacter.GetComponent(); if (characterController == null) characterController = gameCharacter.AddComponent() as CharacterController; characterController.radius = 0.28f; characterController.height = 1.8f; characterController.center = new Vector3(0.0f, 0.93f, 0.0f); /* * if an established Source is not passed to loader, this sets up a new poseAI source to listen on the specified port. */ PoseAISource poseAISource; if (poseAISourceIn != null) { PoseAISourceShared poseAISourceShared = gameCharacter.AddComponent() as PoseAISourceShared; poseAISourceShared.Source = poseAISourceIn; poseAISource = poseAISourceShared; } else { PoseAISourceDirect poseAISourceDirect = gameCharacter.GetComponent(); if (poseAISourceDirect == null) poseAISourceDirect = gameCharacter.AddComponent() as PoseAISourceDirect; poseAISourceDirect.Mode = PoseAI_Modes.Room; poseAISourceDirect.RigType = rigType; poseAISourceDirect.Port = port; poseAISourceDirect.MirrorCamera = false; poseAISource = poseAISourceDirect; } /* * if an established Source is not passed to loader, this sets up a new poseAI source to listen on the specified port. */ PoseAICharacterAnimator poseAICharacterAnimator = gameCharacter.GetComponent(); if (poseAICharacterAnimator == null) poseAICharacterAnimator = gameCharacter.AddComponent() as PoseAICharacterAnimator; poseAICharacterAnimator.SetSource(poseAISource); poseAICharacterAnimator.SetRemapping(Remapping); poseAICharacterAnimator.OverrideRootName = ""; poseAICharacterAnimator.JointNamePrefix = ""; PoseAICharacterController poseAICharacterController = gameCharacter.GetComponent(); if (poseAICharacterController == null) poseAICharacterController = gameCharacter.AddComponent() as PoseAICharacterController; poseAICharacterController.SetSource(poseAISource); poseAICharacterController.CinemachineCameraTarget = GameObject.Find("PlayerFollowCamera"); if (animator == null) { Debug.LogWarning("Failed to load animator"); return false; } if (animator.avatar == null) { Debug.LogWarning("Failed to load animator's avatar"); return false; } if (characterController == null) { Debug.LogWarning("Failed to load character controller"); return false; } if (poseAISource == null) { Debug.LogWarning("Failed to load source"); return false; } if (poseAICharacterAnimator == null) { Debug.LogWarning("Failed to load poseaicharacteranimator"); return false; } if (poseAICharacterController == null) { Debug.LogWarning("Failed to load poseaicharactercontroller"); return false; } return true; } public void EnableOrDisablePoseAI(GameObject gameCharacter, bool enabled) { PoseAISourceDirect poseAISource = gameCharacter.GetComponent(); if (poseAISource != null) poseAISource.enabled = enabled; PoseAICharacterAnimator poseAICharacterAnimator = gameCharacter.GetComponent(); if (poseAICharacterAnimator != null) poseAICharacterAnimator.enabled = enabled; PoseAICharacterController poseAICharacterController = gameCharacter.GetComponent(); if (poseAICharacterController != null) poseAICharacterController.enabled = enabled; } } public static class PoseAI_Decoder { static readonly uint[] reverse_map = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 }; static readonly float[] firstByte = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.9384465070835368f, 0.9697117733268197f, 0.9384465070835368f, 0.9384465070835368f, 0.9697117733268197f, 0.6257938446507083f, 0.6570591108939912f, 0.688324377137274f, 0.7195896433805569f, 0.7508549096238397f, 0.7821201758671226f, 0.8133854421104054f, 0.8446507083536883f, 0.8759159745969711f, 0.907181240840254f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, -0.9687347337567171f, -0.9374694675134343f, -0.9062042012701514f, -0.8749389350268686f, -0.8436736687835857f, -0.8124084025403029f, -0.78114313629702f, -0.7498778700537372f, -0.7186126038104543f, -0.6873473375671715f, -0.6560820713238886f, -0.6248168050806058f, -0.5935515388373229f, -0.5622862725940401f, -0.5310210063507572f, -0.49975574010747437f, -0.4684904738641915f, -0.43722520762090866f, -0.4059599413776258f, -0.37469467513434296f, -0.3434294088910601f, -0.31216414264777725f, -0.2808988764044944f, -0.24963361016121155f, -0.2183683439179287f, 0.0f, 0.0f, 0.0f, 0.0f, 0.9697117733268197f, 0.0f, -0.18710307767464585f, -0.155837811431363f, -0.12457254518808014f, -0.09330727894479729f, -0.06204201270151444f, -0.030776746458231585f, 0.0004885197850512668f, 0.03175378602833412f, 0.06301905227161697f, 0.09428431851489982f, 0.12554958475818268f, 0.15681485100146553f, 0.18808011724474838f, 0.21934538348803123f, 0.2506106497313141f, 0.28187591597459694f, 0.3131411822178798f, 0.34440644846116264f, 0.3756717147044455f, 0.40693698094772834f, 0.4382022471910112f, 0.46946751343429405f, 0.5007327796775769f, 0.5319980459208598f, 0.5632633121641426f, 0.5945285784074255f }; static readonly float[] secondByte = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.030288226673180263f, 0.030776746458231558f, 0.030288226673180263f, 0.030288226673180263f, 0.030776746458231558f, 0.025403028822667317f, 0.025891548607718612f, 0.026380068392769906f, 0.0268685881778212f, 0.027357107962872496f, 0.02784562774792379f, 0.028334147532975085f, 0.02882266731802638f, 0.029311187103077674f, 0.02979970688812897f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0004885197850512946f, 0.0009770395701025891f, 0.0014655593551538837f, 0.0019540791402051783f, 0.002442598925256473f, 0.0029311187103077674f, 0.003419638495359062f, 0.0039081582804103565f, 0.004396678065461651f, 0.004885197850512946f, 0.00537371763556424f, 0.005862237420615535f, 0.006350757205666829f, 0.006839276990718124f, 0.0073277967757694185f, 0.007816316560820713f, 0.008304836345872008f, 0.008793356130923302f, 0.009281875915974597f, 0.009770395701025891f, 0.010258915486077186f, 0.01074743527112848f, 0.011235955056179775f, 0.01172447484123107f, 0.012212994626282364f, 0.0f, 0.0f, 0.0f, 0.0f, 0.030776746458231558f, 0.0f, 0.012701514411333659f, 0.013190034196384953f, 0.013678553981436248f, 0.014167073766487542f, 0.014655593551538837f, 0.015144113336590131f, 0.015632633121641426f, 0.01612115290669272f, 0.016609672691744015f, 0.01709819247679531f, 0.017586712261846604f, 0.0180752320468979f, 0.018563751831949193f, 0.019052271617000488f, 0.019540791402051783f, 0.020029311187103077f, 0.02051783097215437f, 0.021006350757205666f, 0.02149487054225696f, 0.021983390327308255f, 0.02247191011235955f, 0.022960429897410845f, 0.02344894968246214f, 0.023937469467513434f, 0.024425989252564728f, 0.024914509037616023f }; static int CharToInt(char c) { return (int)(c); } public static uint UintB64ToUint(char a, char b) { return reverse_map[CharToInt(a)] * 64 + reverse_map[CharToInt(b)]; } public static uint UintB64ToUint(char a, char b, char c) { return reverse_map[CharToInt(a)] * 4096 + reverse_map[CharToInt(b)] * 64 + reverse_map[CharToInt(c)]; } public static float FixedB64pairToFloat(char a, char b) { return firstByte[CharToInt(a)] + secondByte[CharToInt(b)]; } public static void FStringFixed12ToFloat(ref string data, ref List flatArray) { flatArray.Capacity = flatArray.Count + data.Length / 2; for (int i = 0; i < data.Length - 1; i += 2) flatArray.Add(FixedB64pairToFloat(data[i], data[i + 1])); } public static void FStringFixed12ToFloat(ref string data, ref List flatArray) { flatArray.Capacity = flatArray.Count + data.Length / 2; for (int i = 0; i < data.Length - 1; i += 2) { if(data[i]=='=' && data[i+1]=='=') flatArray.Add(null); else flatArray.Add(FixedB64pairToFloat(data[i], data[i + 1])); } } public static void FlatArrayToQuats(ref List flatArray, ref List quatArray) { quatArray.Capacity = quatArray.Count + flatArray.Count / 4; for (int i = 0; i < flatArray.Count - 3; i += 4) { //reordered Quats for Unity coordinate system. var quat = new Quaternion(flatArray[i + 1], -flatArray[i], flatArray[i + 3], -flatArray[i + 2]); quat.Normalize(); quatArray.Add(quat); } } } public class UITouch { public int index = 0; public float x = 0.0f; public float y = 0.0f; public TouchState state = TouchState.Ended; public override string ToString() { return "Touch_" + index + ":[" + x.ToString("0.000") + "," + y.ToString("0.000") + "] " + state; } public static void DecodeMultiTouchString(ref string data, ref Queue touchQueue) { for (int i = 0; i < data.Length - 4; i += 5) { UITouch touch = new UITouch() { index = data[i] - '0'}; if (data[i + 1] == '=' && data[i + 2] == '=') touch.state = TouchState.Ended; else if (data[i + 1] == '=' && data[i + 2] == '0') touch.state = TouchState.Cancelled; else { touch.x = PoseAI_Decoder.FixedB64pairToFloat(data[i + 1], data[i + 2]); touch.y = PoseAI_Decoder.FixedB64pairToFloat(data[i + 3], data[i + 4]); touch.state = TouchState.Touching; } touchQueue.Enqueue(touch); } } public static void DecodeMultiTouchStateString(ref string data, ref List touchArray) { for (int i = 0; i < data.Length - 3; i += 4) { int idx = i / 4; touchArray[idx].index = idx; if (data[i] == '=' && data[i + 1] == '=') touchArray[idx].state = TouchState.Ended; else if (data[i + 1] == '=' && data[i + 2] == '0') touchArray[idx].state = TouchState.Cancelled; else { touchArray[idx].x = PoseAI_Decoder.FixedB64pairToFloat(data[i], data[i + 1]); touchArray[idx].y = PoseAI_Decoder.FixedB64pairToFloat(data[i + 2], data[i + 3]); if (touchArray[idx].state == TouchState.Touching || touchArray[idx].state == TouchState.Begun) touchArray[idx].state = TouchState.Touching; else touchArray[idx].state = TouchState.Begun; } } } public static List MakeList(int number) { var retValue = new List(number); for (int i = 0; i < number; ++i) retValue.Add(new UITouch() { index = i }) ; return retValue; } } }