using System; using System.IO; using System.Xml.Serialization; using System.Collections.Generic; using System.Runtime.InteropServices; using RageCoop.Core; using GTA; using GTA.Native; using GTA.Math; using System.Linq; using System.Diagnostics; namespace RageCoop.Client { /// /// /// internal static partial class Util { #region -- POINTER -- private static int _steeringAngleOffset { get; set; } public static unsafe void NativeMemory() { IntPtr address; address = Game.FindPattern("\x74\x0A\xF3\x0F\x11\xB3\x1C\x09\x00\x00\xEB\x25", "xxxxxx????xx"); if (address != IntPtr.Zero) { _steeringAngleOffset = *(int*)(address + 6) + 8; } // breaks some stuff. /* address = Game.FindPattern("\x32\xc0\xf3\x0f\x11\x09", "xxxxxx"); // Weapon / Radio slowdown if (address != IntPtr.Zero) { for (int i = 0; i < 6; i++) { *(byte*)(address + i).ToPointer() = 0x90; } } */ } public static unsafe void CustomSteeringAngle(this Vehicle veh, float value) { IntPtr address = new IntPtr((long)veh.MemoryAddress); if (address == IntPtr.Zero || _steeringAngleOffset == 0) { return; } *(float*)(address + _steeringAngleOffset).ToPointer() = value; } #endregion public static Settings ReadSettings() { XmlSerializer ser = new XmlSerializer(typeof(Settings)); string path = Directory.GetCurrentDirectory() + "\\Scripts\\RageCoop\\RageCoop.Client.Settings.xml"; Settings settings = null; if (File.Exists(path)) { using (FileStream stream = File.OpenRead(path)) { settings = (RageCoop.Client.Settings)ser.Deserialize(stream); } using (FileStream stream = new FileStream(path, FileMode.Truncate, FileAccess.ReadWrite)) { ser.Serialize(stream, settings); } } else { using (FileStream stream = File.OpenWrite(path)) { ser.Serialize(stream, settings = new Settings()); } } return settings; } public static void SaveSettings() { try { string path = Directory.GetCurrentDirectory() + "\\Scripts\\RageCoop\\RageCoop.Client.Settings.xml"; using (FileStream stream = new FileStream(path, File.Exists(path) ? FileMode.Truncate : FileMode.Create, FileAccess.ReadWrite)) { XmlSerializer ser = new XmlSerializer(typeof(Settings)); ser.Serialize(stream, Main.Settings); } } catch (Exception ex) { GTA.UI.Notification.Show("Error saving player settings: " + ex.Message); } } public static bool IsBetween(this T item, T start, T end) { return Comparer.Default.Compare(item, start) >= 0 && Comparer.Default.Compare(item, end) <= 0; } public static bool Compare(this Dictionary item, Dictionary item2) { if (item == null || item2 == null || item.Count != item2.Count) { return false; } foreach (KeyValuePair pair in item) { if (item2.TryGetValue(pair.Key, out Y value) && Equals(value, pair.Value)) { continue; } // TryGetValue() or Equals failed return false; } // No difference between item and item2 return true; } #region MATH public static Vector3 LinearVectorLerp(Vector3 start, Vector3 end, ulong currentTime, int duration) { return new Vector3() { X = LinearFloatLerp(start.X, end.X, currentTime, duration), Y = LinearFloatLerp(start.Y, end.Y, currentTime, duration), Z = LinearFloatLerp(start.Z, end.Z, currentTime, duration), }; } public static float LinearFloatLerp(float start, float end, ulong currentTime, int duration) { return (end - start) * currentTime / duration + start; } public static float Lerp(float from, float to, float fAlpha) { return (from * (1.0f - fAlpha)) + (to * fAlpha); //from + (to - from) * fAlpha } public static Vector3 RotationToDirection(Vector3 rotation) { double z = MathExtensions.DegToRad(rotation.Z); double x = MathExtensions.DegToRad(rotation.X); double num = Math.Abs(Math.Cos(x)); return new Vector3 { X = (float)(-Math.Sin(z) * num), Y = (float)(Math.Cos(z) * num), Z = (float)Math.Sin(x) }; } #endregion public static Model ModelRequest(this int hash) { Model model = new Model(hash); if (!model.IsValid) { //GTA.UI.Notification.Show("~y~Not valid!"); return null; } if (!model.IsLoaded) { return model.Request(1000) ? model : null; } return model; } #region PED public static byte GetPedSpeed(this Ped ped) { if (ped.IsSprinting) { return 3; } if (ped.IsRunning) { return 2; } if (ped.IsWalking) { return 1; } return 0; } public static Dictionary GetPedClothes(this Ped ped) { Dictionary result = new Dictionary(); for (byte i = 0; i < 11; i++) { short mod = Function.Call(Hash.GET_PED_DRAWABLE_VARIATION, ped.Handle, i); result.Add(i, mod); } return result; } public static PedDataFlags GetPedFlags(this Ped ped) { PedDataFlags flags = PedDataFlags.None; if (ped.IsAiming || ped.IsOnTurretSeat()) { flags |= PedDataFlags.IsAiming; } if (ped.IsReloading) { flags |= PedDataFlags.IsReloading; } if (ped.IsJumping) { flags |= PedDataFlags.IsJumping; } if (ped.IsRagdoll) { flags |= PedDataFlags.IsRagdoll; } if (ped.IsOnFire) { flags |= PedDataFlags.IsOnFire; } if (ped.IsInParachuteFreeFall) { flags |= PedDataFlags.IsInParachuteFreeFall; } if (ped.ParachuteState == ParachuteState.Gliding) { flags |= PedDataFlags.IsParachuteOpen; } bool climbingLadder = ped.IsTaskActive(TaskType.CTaskGoToAndClimbLadder); if (climbingLadder) { flags |= PedDataFlags.IsOnLadder; } if (ped.IsVaulting && !climbingLadder) { flags |= PedDataFlags.IsVaulting; } if (ped.IsInCover || ped.IsGoingIntoCover) { flags |=PedDataFlags.IsInCover; } return flags; } public static string[] GetReloadingAnimation(this Ped ped) { switch (ped.Weapons.Current.Hash) { case WeaponHash.Revolver: case WeaponHash.RevolverMk2: case WeaponHash.DoubleActionRevolver: case WeaponHash.NavyRevolver: return new string[2] { "anim@weapons@pistol@revolver_str", "reload_aim" }; case WeaponHash.APPistol: return new string[2] { "weapons@pistol@ap_pistol_str", "reload_aim" }; case WeaponHash.Pistol50: return new string[2] { "weapons@pistol@pistol_50_str", "reload_aim" }; case WeaponHash.Pistol: case WeaponHash.PistolMk2: case WeaponHash.PericoPistol: case WeaponHash.SNSPistol: case WeaponHash.SNSPistolMk2: case WeaponHash.HeavyPistol: case WeaponHash.VintagePistol: case WeaponHash.CeramicPistol: case WeaponHash.MachinePistol: return new string[2] { "weapons@pistol@pistol_str", "reload_aim" }; case WeaponHash.AssaultRifle: case WeaponHash.AssaultrifleMk2: return new string[2] { "weapons@rifle@hi@assault_rifle_str", "reload_aim" }; case WeaponHash.SniperRifle: return new string[2] { "weapons@rifle@hi@sniper_rifle_str", "reload_aim" }; case WeaponHash.HeavySniper: case WeaponHash.HeavySniperMk2: return new string[2] { "weapons@rifle@lo@sniper_heavy_str", "reload_aim" }; case WeaponHash.PumpShotgun: case WeaponHash.PumpShotgunMk2: return new string[2] { "weapons@rifle@pump_str", "reload_aim" }; case WeaponHash.Railgun: return new string[2] { "weapons@rifle@lo@rail_gun_str", "reload_aim" }; case WeaponHash.SawnOffShotgun: return new string[2] { "weapons@rifle@lo@sawnoff_str", "reload_aim" }; case WeaponHash.AssaultShotgun: return new string[2] { "weapons@rifle@lo@shotgun_assault_str", "reload_aim" }; case WeaponHash.BullpupShotgun: return new string[2] { "weapons@rifle@lo@shotgun_bullpup_str", "reload_aim" }; case WeaponHash.AdvancedRifle: return new string[2] { "weapons@submg@advanced_rifle_str", "reload_aim" }; case WeaponHash.CarbineRifle: case WeaponHash.CarbineRifleMk2: case WeaponHash.CompactRifle: return new string[2] { "weapons@rifle@lo@carbine_str", "reload_aim" }; case WeaponHash.Gusenberg: return new string[2] { "anim@weapons@machinegun@gusenberg_str", "reload_aim" }; case WeaponHash.Musket: return new string[2] { "anim@weapons@musket@musket_str", "reload_aim" }; case WeaponHash.FlareGun: return new string[2] { "anim@weapons@pistol@flare_str", "reload_aim" }; case WeaponHash.SpecialCarbine: case WeaponHash.SpecialCarbineMk2: return new string[2] { "anim@weapons@rifle@lo@spcarbine_str", "reload_aim" }; case WeaponHash.CombatPDW: return new string[2] { "anim@weapons@rifle@lo@pdw_str", "reload_aim" }; case WeaponHash.BullpupRifle: case WeaponHash.BullpupRifleMk2: return new string[2] { "anim@weapons@submg@bullpup_rifle_str", "reload_aim" }; case WeaponHash.AssaultSMG: return new string[2] { "weapons@submg@assault_smg_str", "reload_aim" }; case WeaponHash.MicroSMG: case WeaponHash.MiniSMG: return new string[2] { "weapons@submg@lo@micro_smg_str", "reload_aim" }; case WeaponHash.SMG: case WeaponHash.SMGMk2: return new string[2] { "weapons@rifle@smg_str", "reload_aim" }; case WeaponHash.GrenadeLauncher: case WeaponHash.GrenadeLauncherSmoke: case WeaponHash.CompactGrenadeLauncher: return new string[2] { "weapons@heavy@grenade_launcher_str", "reload_aim" }; case WeaponHash.RPG: case WeaponHash.Firework: return new string[2] { "weapons@heavy@rpg_str", "reload_aim" }; case WeaponHash.CombatMG: case WeaponHash.CombatMGMk2: return new string[2] { "weapons@machinegun@combat_mg_str", "reload_aim" }; case WeaponHash.MG: return new string[2] { "weapons@machinegun@mg_str", "reload_aim" }; default: Main.Logger.Warning($"~r~Reloading failed! Weapon ~g~[{ped.Weapons.Current.Hash}]~r~ could not be found!"); return null; } } public static VehicleSeat GetNearestSeat(Ped ped, Vehicle veh, float distanceToignoreDoors = 50f) { float num = 99f; int result = -2; Dictionary dictionary = new Dictionary(); dictionary.Add("door_dside_f", -1); dictionary.Add("door_pside_f", 0); dictionary.Add("door_dside_r", 1); dictionary.Add("door_pside_r", 2); foreach (string text in dictionary.Keys) { bool flag = veh.Bones[text].Position != Vector3.Zero; if (flag) { float num2 = ped.Position.DistanceTo(Function.Call(Hash.GET_WORLD_POSITION_OF_ENTITY_BONE, new InputArgument[] { veh, veh.Bones[text].Index })); bool flag2 = (num2 < distanceToignoreDoors) && (num2 < num)&& IsSeatUsableByPed(ped, veh, dictionary[text]); if (flag2) { num = num2; result = dictionary[text]; } } } return (VehicleSeat)result; } public static bool IsSeatUsableByPed(Ped ped, Vehicle veh, int _seat) { VehicleSeat seat = (VehicleSeat)_seat; bool result = false; bool flag = veh.IsSeatFree(seat); if (flag) { result = true; } else { bool isDead = veh.GetPedOnSeat(seat).IsDead; if (isDead) { result = true; } else { int num = Function.Call(Hash.GET_RELATIONSHIP_BETWEEN_PEDS, new InputArgument[] { ped, veh.GetPedOnSeat(seat) }); bool flag2 = num > 2; if (flag2) { result = true; } } } return result; } public static bool IsTaskActive(this Ped p,TaskType task) { return Function.Call(Hash.GET_IS_TASK_ACTIVE, p.Handle, task); } public static Vector3 GetAimCoord(this Ped p) { var weapon = p.Weapons.CurrentWeaponObject; var v = p.CurrentVehicle; // Rhino if (v!=null && v.Model.Hash==782665360) { return v.Bones[35].Position+v.Bones[35].ForwardVector*100; } if (p.IsOnTurretSeat()) { return p.GetLookingCoord(); } if (weapon!=null) { // Not very accurate, but doesn't matter Vector3 dir = weapon.RightVector; return weapon.Position+dir*20; } return GetLookingCoord(p); } public static Vector3 GetLookingCoord(this Ped p) { EntityBone b = p.Bones[Bone.FacialForehead]; Vector3 v = b.UpVector.Normalized; return b.Position+200*v; } public static void StayInCover(this Ped p) { Function.Call(Hash.TASK_STAY_IN_COVER, p); } public static VehicleSeat GetSeatTryingToEnter(this Ped p) { return (VehicleSeat)Function.Call(Hash.GET_SEAT_PED_IS_TRYING_TO_ENTER, p); } #endregion #region VEHICLE public static VehicleDataFlags GetVehicleFlags(this Vehicle veh) { VehicleDataFlags flags = 0; if (veh.IsEngineRunning) { flags |= VehicleDataFlags.IsEngineRunning; } if (veh.AreLightsOn) { flags |= VehicleDataFlags.AreLightsOn; } if (veh.BrakePower >= 0.01f) { flags |= VehicleDataFlags.AreBrakeLightsOn; } if (veh.AreHighBeamsOn) { flags |= VehicleDataFlags.AreHighBeamsOn; } if (veh.IsSirenActive) { flags |= VehicleDataFlags.IsSirenActive; } if (veh.IsDead) { flags |= VehicleDataFlags.IsDead; } if (Function.Call(Hash.IS_HORN_ACTIVE, veh.Handle)) { flags |= VehicleDataFlags.IsHornActive; } if (veh.IsSubmarineCar && Function.Call(Hash._GET_IS_SUBMARINE_VEHICLE_TRANSFORMED, veh.Handle)) { flags |= VehicleDataFlags.IsTransformed; } if (veh.HasRoof && (veh.RoofState == VehicleRoofState.Opened || veh.RoofState == VehicleRoofState.Opening)) { flags |= VehicleDataFlags.RoofOpened; } if (veh.IsAircraft) { flags |= VehicleDataFlags.IsAircraft; } if (veh.Model.Hash==1483171323 && veh.IsDeluxoHovering()) { flags|= VehicleDataFlags.IsDeluxoHovering; } return flags; } public static bool HasFlag(this PedDataFlags flagToCheck,PedDataFlags flag) { return (flagToCheck & flag)!=0; } public static bool HasFlag(this VehicleDataFlags flagToCheck, VehicleDataFlags flag) { return (flagToCheck & flag)!=0; } public static Dictionary GetWeaponComponents(this Weapon weapon) { Dictionary result = null; if (weapon.Components.Count > 0) { result = new Dictionary(); foreach (var comp in weapon.Components) { result.Add((uint)comp.ComponentHash, comp.Active); } } return result; } public static Dictionary GetVehicleMods(this VehicleModCollection mods) { Dictionary result = new Dictionary(); foreach (VehicleMod mod in mods.ToArray()) { result.Add((int)mod.Type, mod.Index); } return result; } public static VehicleDamageModel GetVehicleDamageModel(this Vehicle veh) { // Broken windows byte brokenWindows = 0; for (int i = 0; i < 8; i++) { if (!veh.Windows[(VehicleWindowIndex)i].IsIntact) { brokenWindows |= (byte)(1 << i); } } // Broken doors byte brokenDoors = 0; byte openedDoors = 0; foreach (VehicleDoor door in veh.Doors) { if (door.IsBroken) { brokenDoors |= (byte)(1 << (byte)door.Index); } else if (door.IsOpen) { openedDoors |= (byte)(1 << (byte)door.Index); } } // Bursted tires short burstedTires = 0; foreach (VehicleWheel wheel in veh.Wheels.GetAllWheels()) { if (wheel.IsBursted) { burstedTires |= (short)(1 << (int)wheel.BoneId); } } return new VehicleDamageModel() { BrokenDoors = brokenDoors, OpenedDoors = openedDoors, BrokenWindows = brokenWindows, BurstedTires = burstedTires, LeftHeadLightBroken = (byte)(veh.IsLeftHeadLightBroken ? 1 : 0), RightHeadLightBroken = (byte)(veh.IsRightHeadLightBroken ? 1 : 0) }; } public static Dictionary GetPassengers(this Vehicle veh) { Dictionary ps=new Dictionary(); var d = veh.Driver; if (d!=null&&d.IsSittingInVehicle()) { ps.Add(-1, d.GetSyncEntity().ID); } foreach(Ped p in veh.Passengers) { if (p.IsSittingInVehicle()) { ps.Add((int)p.SeatIndex, (int)p.GetSyncEntity().ID); } } return ps; } public static void SetDeluxoHoverState(this Vehicle deluxo,bool hover) { Function.Call(Hash._SET_VEHICLE_HOVER_TRANSFORM_PERCENTAGE, deluxo, hover? 1f: 0f); } public static bool IsDeluxoHovering(this Vehicle deluxo) { return Math.Abs(deluxo.Bones[27].ForwardVector.GetCosTheta(deluxo.ForwardVector)-1)>0.05; } public static float GetNozzleAngel(this Vehicle plane) { return Function.Call(Hash._GET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane); } public static bool HasNozzle(this Vehicle v) { switch (v.Model.Hash) { // Hydra case 970385471: return true; // Avenger case -2118308144: return true; // Avenger case 408970549: return true; } return false; } public static void SetNozzleAngel(this Vehicle plane,float ratio) { Function.Call(Hash.SET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane, ratio); } public static void SetDamageModel(this Vehicle veh, VehicleDamageModel model, bool leavedoors = true) { for (int i = 0; i < 8; i++) { var door = veh.Doors[(VehicleDoorIndex)i]; if ((model.BrokenDoors & (byte)(1 << i)) != 0) { door.Break(leavedoors); } else if (door.IsBroken) { // The vehicle can only fix a door if the vehicle was completely fixed veh.Repair(); return; } if ((model.OpenedDoors & (byte)(1 << i)) != 0) { if ((!door.IsOpen)&&(!door.IsBroken)) { door.Open(); } } else if (door.IsOpen) { if (!door.IsBroken) { door.Close(); } } if ((model.BrokenWindows & (byte)(1 << i)) != 0) { veh.Windows[(VehicleWindowIndex)i].Smash(); } else if (!veh.Windows[(VehicleWindowIndex)i].IsIntact) { veh.Windows[(VehicleWindowIndex)i].Repair(); } } foreach (VehicleWheel wheel in veh.Wheels) { if ((model.BurstedTires & (short)(1 << (int)wheel.BoneId)) != 0) { if (!wheel.IsBursted) { wheel.Puncture(); wheel.Burst(); } } else if (wheel.IsBursted) { wheel.Fix(); } } veh.IsLeftHeadLightBroken = model.LeftHeadLightBroken > 0; veh.IsRightHeadLightBroken = model.RightHeadLightBroken > 0; } public static Vector3 PredictPosition(this Entity e,bool applyDefault=true) { return e.Position+e.Velocity*((applyDefault?SyncParameters.PositioinPredictionDefault:0)+Networking.Latency); } #endregion public static void SetOnFire(this Entity e,bool toggle) { if (toggle) { Function.Call(Hash.START_ENTITY_FIRE, e.Handle); } else { Function.Call(Hash.STOP_ENTITY_FIRE, e.Handle); } } public static SyncedPed GetSyncEntity(this Ped p) { if(p == null) { return null; } var c = EntityPool.GetPedByHandle(p.Handle); if(c==null) { EntityPool.Add(c=new SyncedPed(p)); } return c; } public static SyncedVehicle GetSyncEntity(this Vehicle veh) { if(veh == null) { return null; } var v=EntityPool.GetVehicleByHandle(veh.Handle); if (v==null) { EntityPool.Add(v=new SyncedVehicle(veh)); } return v; } public static bool IsTurretSeat(this Vehicle veh, int seat) { if (!Function.Call(Hash.DOES_VEHICLE_HAVE_WEAPONS, veh.Handle)) { return false; } switch (seat) { case -1: return (VehicleHash)veh.Model.Hash == VehicleHash.Rhino || (VehicleHash)veh.Model.Hash == VehicleHash.Khanjari || (VehicleHash)veh.Model.Hash == VehicleHash.FireTruck || (VehicleHash)veh.Model.Hash == VehicleHash.Riot2 || (VehicleHash)veh.Model.Hash == VehicleHash.Cerberus || (VehicleHash)veh.Model.Hash == VehicleHash.Cerberus2 || (VehicleHash)veh.Model.Hash == VehicleHash.Cerberus3; case 0: return (VehicleHash)veh.Model.Hash == VehicleHash.Apc; case 1: return (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie || (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie2 || (VehicleHash)veh.Model.Hash == VehicleHash.Technical || (VehicleHash)veh.Model.Hash == VehicleHash.Technical2 || (VehicleHash)veh.Model.Hash == VehicleHash.Technical3 || (VehicleHash)veh.Model.Hash == VehicleHash.HalfTrack || (VehicleHash)veh.Model.Hash == VehicleHash.Barrage; case 2: return (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie || (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie2 || (VehicleHash)veh.Model.Hash == VehicleHash.Barrage; case 3: return (VehicleHash)veh.Model.Hash == VehicleHash.Limo2 || (VehicleHash)veh.Model.Hash == VehicleHash.Dinghy5; case 7: return (VehicleHash)veh.Model.Hash == VehicleHash.Insurgent; } return false; } public static bool IsOnTurretSeat(this Ped P) { if (P.CurrentVehicle == null) { return false; } return IsTurretSeat(P.CurrentVehicle, (int)P.SeatIndex); } [DllImport("kernel32.dll")] public static extern ulong GetTickCount64(); } /// /// /// public static class MathExtensions { /// /// /// public static Vector3 ToVector(this Quaternion vec) { return new Vector3() { X = vec.X, Y = vec.Y, Z = vec.Z }; } /// /// /// public static Quaternion ToQuaternion(this Vector3 vec, float vW = 0.0f) { return new Quaternion() { X = vec.X, Y = vec.Y, Z = vec.Z, W = vW }; } public static float Denormalize(this float h) { return h < 0f ? h + 360f : h; } public static float ToRadians(this float val) { return (float)(Math.PI / 180) * val; } public static Vector3 ToRadians(this Vector3 i) { return new Vector3() { X = ToRadians(i.X), Y = ToRadians(i.Y), Z = ToRadians(i.Z), }; } public static Quaternion ToQuaternion(this Vector3 vect) { vect = new Vector3() { X = vect.X.Denormalize() * -1, Y = vect.Y.Denormalize() - 180f, Z = vect.Z.Denormalize() - 180f, }; vect = vect.ToRadians(); float rollOver2 = vect.Z * 0.5f; float sinRollOver2 = (float)Math.Sin((double)rollOver2); float cosRollOver2 = (float)Math.Cos((double)rollOver2); float pitchOver2 = vect.Y * 0.5f; float sinPitchOver2 = (float)Math.Sin((double)pitchOver2); float cosPitchOver2 = (float)Math.Cos((double)pitchOver2); float yawOver2 = vect.X * 0.5f; // pitch float sinYawOver2 = (float)Math.Sin((double)yawOver2); float cosYawOver2 = (float)Math.Cos((double)yawOver2); Quaternion result = new Quaternion() { X = cosYawOver2 * cosPitchOver2 * cosRollOver2 + sinYawOver2 * sinPitchOver2 * sinRollOver2, Y = cosYawOver2 * cosPitchOver2 * sinRollOver2 - sinYawOver2 * sinPitchOver2 * cosRollOver2, Z = cosYawOver2 * sinPitchOver2 * cosRollOver2 + sinYawOver2 * cosPitchOver2 * sinRollOver2, W = sinYawOver2 * cosPitchOver2 * cosRollOver2 - cosYawOver2 * sinPitchOver2 * sinRollOver2 }; return result; } /// /// /// public static LVector3 ToLVector(this Vector3 vec) { return new LVector3() { X = vec.X, Y = vec.Y, Z = vec.Z }; } /// /// /// public static LQuaternion ToLQuaternion(this Quaternion vec) { return new LQuaternion() { X = vec.X, Y = vec.Y, Z = vec.Z, W = vec.W }; } public static double DegToRad(double deg) { return deg * Math.PI / 180.0; } public static Vector3 ToEulerRotation(this Vector3 dir,Vector3 up) { var rot = Quaternion.LookRotation(dir.Normalized,up).ToEulerAngles().ToDegree(); return rot; } public static Vector3 ToDegree(this Vector3 radian) { return radian*(float)(180/Math.PI); } public static Vector3 ToEulerAngles(this Quaternion q) { Vector3 angles = new Vector3(); // roll / x double sinr_cosp = 2 * (q.W * q.X + q.Y * q.Z); double cosr_cosp = 1 - 2 * (q.X * q.X + q.Y * q.Y); angles.X = (float)Math.Atan2(sinr_cosp, cosr_cosp); // pitch / y double sinp = 2 * (q.W * q.Y - q.Z * q.X); if (Math.Abs(sinp) >= 1) { angles.Y = CopySign(Math.PI / 2, sinp); } else { angles.Y = (float)Math.Asin(sinp); } // yaw / z double siny_cosp = 2 * (q.W * q.Z + q.X * q.Y); double cosy_cosp = 1 - 2 * (q.Y * q.Y + q.Z * q.Z); angles.Z = (float)Math.Atan2(siny_cosp, cosy_cosp); return angles; } private static float CopySign(double x, double y) { bool isPositive = y>=0; if (isPositive) { if (x>=0) { return (float)x; } else { return (float)-x; } } else { if (x>=0) { return (float)-x; } else { return (float)x; } } } public static double AngelTo(this Vector3 v1, Vector3 v2) { return Math.Acos(v1.GetCosTheta(v2)); } public static float GetCosTheta(this Vector3 v1, Vector3 v2) { return Vector3.Dot(v1, v2)/(v1.Length()*v2.Length()); } } }