Skip to content

Instantly share code, notes, and snippets.

@Denchyaknow
Last active December 5, 2023 17:42
Show Gist options
  • Save Denchyaknow/138158b68dbc82619dbfcbd6ece65184 to your computer and use it in GitHub Desktop.
Save Denchyaknow/138158b68dbc82619dbfcbd6ece65184 to your computer and use it in GitHub Desktop.
Magical_Extensions
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//Made by Dencho, Just some useful extensions for Unity types, wrote them over the years for my own use, enjoi thx!
namespace Yakno
{
#region Debugging
public static class DebugExt
{
public static void DrawBounds(Transform _transform, Bounds _bounds, Color _color, float _duration = 0.04f)
{
Vector3 v3Center = _transform.position + _transform.TransformDirection(_bounds.center);
Vector3 v3Extents = _bounds.extents;//_transform.TransformDirection(_bounds.extents);
Vector3 v3FrontTopLeft = new Vector3(v3Center.x - v3Extents.x, v3Center.y + v3Extents.y, v3Center.z - v3Extents.z);
Vector3 v3FrontTopRight = new Vector3(v3Center.x + v3Extents.x, v3Center.y + v3Extents.y, v3Center.z - v3Extents.z);
Vector3 v3FrontBottomLeft = new Vector3(v3Center.x - v3Extents.x, v3Center.y - v3Extents.y, v3Center.z - v3Extents.z);
Vector3 v3FrontBottomRight = new Vector3(v3Center.x + v3Extents.x, v3Center.y - v3Extents.y, v3Center.z - v3Extents.z);
Vector3 v3BackTopLeft = new Vector3(v3Center.x - v3Extents.x, v3Center.y + v3Extents.y, v3Center.z + v3Extents.z);
Vector3 v3BackTopRight = new Vector3(v3Center.x + v3Extents.x, v3Center.y + v3Extents.y, v3Center.z + v3Extents.z);
Vector3 v3BackBottomLeft = new Vector3(v3Center.x - v3Extents.x, v3Center.y - v3Extents.y, v3Center.z + v3Extents.z);
Vector3 v3BackBottomRight = new Vector3(v3Center.x + v3Extents.x, v3Center.y - v3Extents.y, v3Center.z + v3Extents.z);
Debug.DrawLine(v3FrontTopLeft, v3FrontTopRight, _color, _duration);
Debug.DrawLine(v3FrontTopRight, v3FrontBottomRight, _color, _duration);
Debug.DrawLine(v3FrontBottomRight, v3FrontBottomLeft, _color, _duration);
Debug.DrawLine(v3FrontBottomLeft, v3FrontTopLeft, _color, _duration);
Debug.DrawLine(v3BackTopLeft, v3BackTopRight, _color, _duration);
Debug.DrawLine(v3BackTopRight, v3BackBottomRight, _color, _duration);
Debug.DrawLine(v3BackBottomRight, v3BackBottomLeft, _color, _duration);
Debug.DrawLine(v3BackBottomLeft, v3BackTopLeft, _color, _duration);
Debug.DrawLine(v3FrontTopLeft, v3BackTopLeft, _color, _duration);
Debug.DrawLine(v3FrontTopRight, v3BackTopRight, _color, _duration);
Debug.DrawLine(v3FrontBottomRight, v3BackBottomRight, _color, _duration);
Debug.DrawLine(v3FrontBottomLeft, v3BackBottomLeft, _color, _duration);
}
}
/// <summary>
/// Useful Gizmoes extensions made by Dencho
/// </summary>
public static class GizmosExt
{
public static void DrawAngleRange(Vector3 position_, float range_, Vector3 direction_, Vector3 aroundAxis_, float upAngle_, Vector3 upAxis_, float deg_ = 180f)
{
float fDeg = deg_ * range_;
Quaternion a = Quaternion.identity;
Quaternion b = Quaternion.identity;
Quaternion aa = Quaternion.identity;
Quaternion bb = Quaternion.identity;
float aP = Mathf.Lerp(0f, fDeg, 1);
float bP = Mathf.Lerp(0f, -fDeg, 1);
var up = Quaternion.AngleAxis(upAngle_, upAxis_);
a = Quaternion.AngleAxis(aP, aroundAxis_);
b = Quaternion.AngleAxis(bP, aroundAxis_);
aa = a;
bb = b;
Gizmos.DrawLine(position_, position_ + up * a * direction_);
Gizmos.DrawLine(position_, position_ + up * b * direction_);
aP = Mathf.Lerp(0f, fDeg, 0);
bP = Mathf.Lerp(0f, -fDeg, 0);
aa = Quaternion.AngleAxis(aP, aroundAxis_);
bb = Quaternion.AngleAxis(bP, aroundAxis_);
for (float t = 0f; t <= 1.05f; t += 0.05f)
{
aP = Mathf.Lerp(0f, fDeg, t);
bP = Mathf.Lerp(0f, -fDeg, t);
a = Quaternion.AngleAxis(aP, aroundAxis_);
b = Quaternion.AngleAxis(bP, aroundAxis_);
Gizmos.DrawLine(position_ + up * a * direction_, position_ + up * aa * direction_);
Gizmos.DrawLine(position_ + up * b * direction_, position_ + up * bb * direction_);
aa = a;
bb = b;
}
Gizmos.DrawLine(position_ + a * direction_, position_ + aa * direction_);
Gizmos.DrawLine(position_ + b * direction_, position_ + bb * direction_);
}
/// <summary>
/// If you have trouble with this, ask Dencho as he wrote this when he was haf
/// </summary>
public static void DrawAngleRange(Vector3 position_, float range_, Vector3 direction_, Vector3 aroundAxis_)
{
float fDeg = 2 * Mathf.PI * Mathf.Rad2Deg * range_ * 0.5f;
Quaternion a = Quaternion.identity;
Quaternion b = Quaternion.identity;
Quaternion aa = Quaternion.identity;
Quaternion bb = Quaternion.identity;
var up = Quaternion.AngleAxis(0f, aroundAxis_);
float aP = Mathf.Lerp(0f, fDeg, 1);
float bP = Mathf.Lerp(0f, -fDeg, 1);
a = Quaternion.AngleAxis(aP, aroundAxis_);
b = Quaternion.AngleAxis(bP, aroundAxis_);
aa = a;
bb = b;
Gizmos.DrawLine(position_, position_ + a * direction_);
Gizmos.DrawLine(position_, position_ + b * direction_);
aP = Mathf.Lerp(0f, fDeg, 0);
bP = Mathf.Lerp(0f, -fDeg, 0);
aa = Quaternion.AngleAxis(aP, aroundAxis_);
bb = Quaternion.AngleAxis(bP, aroundAxis_);
for (float t = 0f; t <= 1.05f; t += 0.05f)
{
aP = Mathf.Lerp(0f, fDeg, t);
bP = Mathf.Lerp(0f, -fDeg, t);
a = Quaternion.AngleAxis(aP, aroundAxis_);
b = Quaternion.AngleAxis(bP, aroundAxis_);
Gizmos.DrawLine(position_ + up * a * direction_, position_ + up * aa * direction_);
Gizmos.DrawLine(position_ + up * b * direction_, position_ + up * bb * direction_);
aa = a;
bb = b;
}
Gizmos.DrawLine(position_ + a * direction_, position_ + aa * direction_);
Gizmos.DrawLine(position_ + b * direction_, position_ + bb * direction_);
}
/// <summary>
/// Draw a modular Pipe, If you wanna know how to use it just read the code or ask Dencho
/// </summary>
/// <param name="origin">center lastVertexPoint</param>
/// <param name="aroundAxis">Axis to rotate around</param>
/// <param name="forwardAxis">If this forward changes then sow ill the lines</param>
/// <param name="innerRadius">Inner Radius of the wire circle or pipe</param>
/// <param name="outterRadius">Default = 0 = doesnt show</param>
/// <param name="height">Default = 0 = no walls</param>
/// <param name="resolution"></param>
/// <param name="drawOriginLines"></param>
public static void DrawWirePipe(Vector3 origin, Vector3 aroundAxis, Vector3 forwardAxis, Vector3 rightAxis, float innerRadius, float outterRadius = 0f, float height = 0f, int resolution = 5, bool drawOriginLines = false)
{
float d = 0.001f * Mathf.PI * Mathf.PI * (Mathf.InverseLerp(0f, 100f, 100 - resolution));
Vector3 a = origin + Quaternion.AngleAxis(360f, aroundAxis.normalized) * forwardAxis.normalized * outterRadius;
Vector3 aa = origin + Quaternion.AngleAxis(360f, aroundAxis.normalized) * forwardAxis.normalized * innerRadius;
Vector3 b = a;
Vector3 bb = aa;
Vector3 up = aroundAxis * height;
Vector3 right = rightAxis * innerRadius;
bool outter = outterRadius != 0f && outterRadius > innerRadius;
for (int i = 0; i <= 360 * 3; i += resolution)
{
a = origin + Quaternion.AngleAxis(i, aroundAxis.normalized) * rightAxis.normalized * innerRadius;
Gizmos.DrawLine(a, b);
if (height != 0f)
{
Gizmos.DrawLine(a + up, b + up);
Gizmos.DrawLine(a + up, a);
}
b = a;
if (outter)
{
aa = origin + Quaternion.AngleAxis(i, aroundAxis.normalized) * forwardAxis.normalized * outterRadius;
Gizmos.DrawLine(aa, bb);
if (height != 0f)
{
Gizmos.DrawLine(aa + up, bb + up);
Gizmos.DrawLine(aa + up, aa);
}
bb = aa;
if (drawOriginLines)
Gizmos.DrawLine(aa, a);
}
else
{
}
}
a = origin + Quaternion.AngleAxis(360f, aroundAxis.normalized) * forwardAxis.normalized * outterRadius;
Gizmos.DrawLine(a, b);
if (outter)
{
aa = origin + Quaternion.AngleAxis(360f, aroundAxis.normalized) * forwardAxis.normalized * innerRadius;
Gizmos.DrawLine(aa, bb);
}
}
/// <summary>
/// A function to draw a wire circle, If you wanna know how to use it just read the code or ask Dencho
/// </summary>
/// <param name="origin"></param>
/// <param name="radius"></param>
/// <param name="aroundAxis">The axis to rotate the circle around</param>
/// <param name="resolution">Will be clamped between 0 - 100</param>
public static void DrawWireCircle(Vector3 origin, float radius, Vector3 aroundAxis, Vector3 forwardAxis, int resolution = 10, bool drawOriginLines = false)
{
int count = 0;
if (resolution >= 360)
resolution = 359;
float d = ((Mathf.Deg2Rad * Mathf.PI * resolution) / 360f) * Mathf.InverseLerp(0f, 360, 360 - resolution);
Vector3 a = origin + Quaternion.AngleAxis(360f, aroundAxis.normalized) * forwardAxis.normalized * radius;
Vector3 b = a;
for (float i = 0f; i <= 1f; i += d)
{
a = origin + Quaternion.AngleAxis(i * 360f, aroundAxis.normalized) * forwardAxis.normalized * radius;
if (drawOriginLines)
Gizmos.DrawLine(origin, a);
Gizmos.DrawLine(a, b);
b = a;
count++;
}
a = origin + Quaternion.AngleAxis(360f, aroundAxis.normalized) * forwardAxis.normalized * radius;
Gizmos.DrawLine(a, b);
}
}
#endregion
#region Cameras and Renderers
/// <summary>
/// Useful Camera externions made by Dencho
/// </summary>
public static class CameraExt
{
public static void FocusOnGameObject(this Camera camera, Bounds targetBounds, float height = 0.5f, float distancePadding = 0f, float angle = 0f)
{
//var max = Mathf.Max(targetBounds.size.x, targetBounds.size.y, targetBounds.size.z);
//max / 2f;
var objectRadius = targetBounds.extents.magnitude;
var cameraView = 2.0f * Mathf.Tan(0.5f * Mathf.Deg2Rad * camera.fieldOfView);
var distance = 1f * objectRadius / cameraView;
distance += objectRadius;
Debug.Log(string.Format("radius: {0}, view: {1}, distance: {2}", objectRadius, cameraView, distance));
var pos = (distance + (distance * distancePadding)) * (Quaternion.AngleAxis(angle, Vector3.up) * Vector3.forward);
var dir = (targetBounds.center - pos);
float dis = dir.magnitude;
dir += (Vector3.up * (height + (height * distance)));
var newPos = targetBounds.center - (dir.normalized * dis);
//(distance + (distance * distancePadding)));
//pos = targetBounds.center - (dir.normalized * (distance + (distance * distancePadding)));
camera.transform.position = newPos;
camera.transform.rotation = Quaternion.LookRotation(targetBounds.center - camera.transform.position);
}
public static void FocusOnGameObject(this Camera camera, GameObject targetGameObject, float height = 0.5f)
{
var bounds = targetGameObject.GetTotalBounds();
var objectRadius = bounds.extents.magnitude;
var cameraView = 2.0f * Mathf.Tan(0.5f * Mathf.Deg2Rad * camera.fieldOfView);
var distance = 1f * objectRadius / cameraView;
distance += objectRadius;
camera.transform.position = (bounds.center - (distance * Vector3.forward)) + (Vector3.up * height);
camera.transform.rotation = Quaternion.LookRotation(bounds.center - camera.transform.position);
}
}
/// <summary>
/// Useful Renderer extensions made by Dencho
/// </summary>
public static class RendererExt
{
/// <summary>
/// Get the Max _bounds of all types of Renderers, Colliders, ect in the given GO, used for auto focusing a Camera.
/// </summary>
/// <param name="gameObject">the Go that will we AutoFocused on</param>
/// <returns>The _bounds to use in the AutoFocus Calculations</returns>
public static Bounds GetTotalBounds(this GameObject gameObject)
{
var meshRenderers = gameObject.GetComponentsInChildren<MeshRenderer>();
var skinnedMeshRenderers = gameObject.GetComponentsInChildren<SkinnedMeshRenderer>();
var colliders = gameObject.GetComponentsInChildren<Collider>();
var bounds = new Bounds();
foreach (var meshRenderer in meshRenderers)
{
bounds.Encapsulate(meshRenderer.bounds);
}
foreach (var skinnedMeshRenderer in skinnedMeshRenderers)
{
bounds.Encapsulate(skinnedMeshRenderer.bounds);
}
foreach (var collider in colliders)
{
bounds.Encapsulate(collider.bounds);
}
return bounds;
}
public static bool HasMat(this MeshRenderer renderer, Material mat)
{
if (mat == null)
return false;
if (renderer.sharedMaterials.Length > 0)
{
for (int i = 0; i < renderer.sharedMaterials.Length; i++)
{
if (renderer.sharedMaterials[i].shader.GetInstanceID() == mat.shader.GetInstanceID())
return true;
}
}
return false;
}
public static int GetMatIndex(this MeshRenderer renderer, Material mat)
{
if (mat == null)
return -1;
if (renderer.sharedMaterials.Length > 0)
{
for (int i = 0; i < renderer.sharedMaterials.Length; i++)
{
if (renderer.sharedMaterials[i].shader.GetInstanceID() == mat.shader.GetInstanceID())
return i;
}
}
return -1;
}
}
#endregion
#region Math Types
/// <summary>
/// Useful Float extensions made by Dencho
/// </summary>
public static class FloatExt
{
/// <summary>
/// Get the Angle of the forward direction
/// </summary>
/// <param name="planarDir"></param>
/// <returns></returns>
public static float ToAngle(this Vector3 planarDir)
{
return (Mathf.Atan2(planarDir.x, planarDir.z) * Mathf.Rad2Deg);
}
/// <summary>
/// Get the delta between 2 forward directions
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
/// <returns></returns>
public static float GetDeltaForward(Vector3 from, Vector3 to)
{
return (
Mathf.DeltaAngle(Mathf.Atan2(from.z, from.x) * Mathf.Rad2Deg, Mathf.Atan2(to.z, to.x) * Mathf.Rad2Deg)
);
}
/// <summary>
/// Get the delta between 2 forward directions
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
/// <returns></returns>
public static float GetDeltaForward(Vector2 from, Vector2 to)
{
return (
Mathf.DeltaAngle(Mathf.Atan2(from.x, from.y) * Mathf.Rad2Deg, Mathf.Atan2(to.x, to.y) * Mathf.Rad2Deg)
);
}
/// <summary>Original Float: 0.123, Converted to Short: 0.123 * 1000 = 123 </summary>
/// <param name="value">This float will lose some precision</param> <returns>A short ranging from -32,768 to 32,768</returns>
public static short ToShort(this float value, float scalingFactor = 1000f)
{
float scaled = value * scalingFactor;
return (short)Mathf.Clamp(scaled, short.MinValue, short.MaxValue);
}
/// <summary>Original Float: 0.123, Back to Float: 123 / 1000 = 0.123</summary>
/// <param name="value">This short should not be 0</param> <returns>A <see cref="float"/> of big number</returns>
public static float ToFloat(this short value, float scalingFactor = 1000f)
{
return value / scalingFactor;
}
/// <summary>Original Float: 1.23, Converted to sbyte: 1.23 * 10 = 12.3 (rounded to 12)</summary>
/// <param name="value">This float will lose some precision</param><returns>An <see cref="sbyte"/> ranging -128 to 127</returns>
public static sbyte ToSByte(this float value, float scalingFactor = 120f)
{
float scaled = value * scalingFactor;
return (sbyte)Mathf.Clamp(scaled, sbyte.MinValue, sbyte.MaxValue);
}
/// <summary> Original Float: 1.23, Back to Float: 12 / 10 = 1.2 </summary>
/// <param name="value">This short should not be 0</param> <returns>A <see cref="float"/> of big number</returns>
public static float ToFloat(this sbyte value, float scalingFactor = 120f)
{
return value / scalingFactor;
}
}
/// <summary>
/// Useful Vector3 extensions made by Dencho
/// </summary>
public static class Vector3Ext
{
public static Vector3[] LocalPath(this Transform[] points_, ref Vector3[] outPut_)
{
if (outPut_.Length != points_.Length)
outPut_ = new Vector3[points_.Length];
for (int i = 0; i < points_.Length; i++)
{
outPut_[i] = points_[i].localPosition;
}
return outPut_;
}
public static Vector3[] Path(this Transform[] points_, ref Vector3[] outPut_)
{
if (outPut_.Length != points_.Length)
outPut_ = new Vector3[points_.Length];
for (int i = 0; i < points_.Length; i++)
{
outPut_[i] = points_[i].position;
}
return outPut_;
}
public static Vector3 TransformRelative(this Vector3 value_, Transform target_)
{
return new Vector3(Vector3.Dot(target_.right, value_), Vector3.Dot(target_.right, value_), Vector3.Dot(target_.right, value_));
}
public static void ToTransformRelative(this Vector3 output_, Transform target_)
{
Vector3 temp = output_;
output_.x = Vector3.Dot(target_.right, temp);
output_.y = Vector3.Dot(target_.up, temp);
output_.z = Vector3.Dot(target_.forward, temp);
}
/// <summary>
/// Get the average via lerping of a Vector3 Aray relative to bias
/// No in the form of a Vector3!
/// </summary>
/// <param name="points">Data set of vector to iterate through</param>
/// <param name="avg">the resulting vector</param>
/// <param name="bias">Value 0 - 1 set how much to blend the vector by each iteration</param>
public static Vector3 LerpAverage(this List<Vector3> points, float bias, int normalCount = 0)
{
var avg = points[0];
for (int i = 1; i < (normalCount > 0 ? normalCount : points.Count); i++)
{
avg = Vector3.Lerp(avg, points[i], bias);
}
return avg;
}
/// <summary>
/// Get the average via lerping of a Vector3 Aray relative to bias
/// </summary>
/// <param name="points">Data set of vector to iterate through</param>
/// <param name="avg">the resulting vector</param>
/// <param name="bias">Value 0 - 1 set how much to blend the vector by each iteration</param>
public static void LerpAverage(this List<Vector3> points, out Vector3 avg, float bias, int normalCount = 0)
{
avg = points[0];
for (int i = 1; i < (normalCount > 0 ? normalCount : points.Count); i++)
{
avg = Vector3.Lerp(avg, points[i], bias);
}
}
/// <summary>
/// Get the average via Slerping of a Vector3 Aray relative to bias
/// </summary>
/// <param name="points">Data set of vector to iterate through</param>
/// <param name="avg">the resulting vector</param>
/// <param name="bias">Value 0 - 1 set how much to blend the vector by each iteration</param>
public static void SlerpAverage(this List<Vector3> points, out Vector3 avg, float bias)
{
avg = points[0];
for (int i = 1; i < points.Count; i++)
{
avg = Vector3.Slerp(avg, points[i], bias);
}
}
/// <summary>
/// Flattens the y axis from one vector to 0
/// </summary>
/// <param name="vectorToSet"></param>
/// <returns></returns>
public static Vector3 FlatY(this Vector3 vectorToSet)
{
vectorToSet.y = 0f;
return vectorToSet;
}
/// <summary>
/// Invert the X
/// </summary>
/// <param name="vectorToSet"></param>
/// <returns></returns>
public static Vector3 FlipX(this Vector3 vectorToSet)
{
vectorToSet.x = -vectorToSet.x;
return vectorToSet;
}
/// <summary>
/// Copys the y axis from one vector to another
/// </summary>
/// <param name="vectorToSet"></param>
/// <param name="vectorToCopy"></param>
/// <returns></returns>
public static Vector3 CopyY(this Vector3 vectorToSet, Vector3 vectorToCopy)
{
vectorToSet.y = vectorToCopy.y;
return vectorToSet;
}
/// <summary>
/// Copys the y axis from one vector to another
/// </summary>
/// <param name="vectorToSet"></param>
/// <param name="vectorToCopy"></param>
/// <returns></returns>
public static Vector3 CopyX(this Vector3 vectorToSet, Vector3 vectorToCopy)
{
vectorToSet.x = vectorToCopy.x;
return vectorToSet;
}
/// <summary>
/// Copies a vectors x and z axis
/// </summary>
/// <param name="vectorToSet"></param>
/// <param name="vectorToCopy"></param>
/// <returns></returns>
public static Vector3 CopyXZ(this Vector3 vectorToSet, Vector3 vectorToCopy)
{
vectorToSet.x = vectorToCopy.x;
vectorToSet.z = vectorToCopy.z;
return vectorToSet;
}
/// <summary>
/// Just a helper method fo easy distance debugging
/// </summary>
/// <param name="fromPos"></param>
/// <param name="toPos"></param>
/// <param name="active"></param>
/// <returns></returns>
public static void DrawLine(this Vector3 fromPos, Vector3 toPos, bool active)
{
if (active && Time.frameCount % 60 == 0)
{
Debug.DrawLine(fromPos, toPos, Color.cyan, 0.75f, false);
}
}
public static void DrawLine(this Vector3 fromPos, Vector3 toPos, bool active, Color color)
{
if (active && Time.frameCount % 60 == 0)
{
Debug.DrawLine(fromPos, toPos, color, 0.75f, false);
}
}
}
/// <summary>
/// Just a class of usefull functions for Quaternions and thier evil ways
/// </summary>
public static class QuaternionExt
{
/// <summary>
/// Used in <see cref="TransformOffset"/>
/// Slerps a current rotation of a _transform with to angle
/// </summary>
/// <param name="curForward">the current rotation of the _transform you want to rotate</param>
/// <param name="deltaAngle">+ or - a delta angle to add to the rotation</param>
/// <param name="deltaTime">Value between 0 to 1</param>
/// <param name="up">Default = <see cref="Vector3.up"/></param>
/// <returns></returns>
public static Quaternion SlerpToAngle(this Quaternion curForward, float deltaAngle, float deltaTime, Vector3 up)
{
return Quaternion.Slerp(curForward, curForward * Quaternion.AngleAxis(-deltaAngle, up), deltaTime);
}
/// <summary>
/// Optimized Quaternion.Lerp
/// </summary>
public static Quaternion Lerp(Quaternion fromRotation, Quaternion toRotation, float weight)
{
if (weight <= 0f) return fromRotation;
if (weight >= 1f) return toRotation;
return Quaternion.Lerp(fromRotation, toRotation, weight);
}
/// <summary>
/// Optimized Quaternion.Slerp
/// </summary>
public static Quaternion Slerp(Quaternion fromRotation, Quaternion toRotation, float weight)
{
if (weight <= 0f) return fromRotation;
if (weight >= 1f) return toRotation;
return Quaternion.Slerp(fromRotation, toRotation, weight);
}
/// <summary>
/// Returns the rotation from identity Quaternion to "q", interpolated linearily by "weight".
/// </summary>
public static Quaternion LinearBlend(Quaternion q, float weight)
{
if (weight <= 0f) return Quaternion.identity;
if (weight >= 1f) return q;
return Quaternion.Lerp(Quaternion.identity, q, weight);
}
/// <summary>
/// Returns the rotation from identity Quaternion to "q", interpolated spherically by "weight".
/// </summary>
public static Quaternion SphericalBlend(Quaternion q, float weight)
{
if (weight <= 0f) return Quaternion.identity;
if (weight >= 1f) return q;
return Quaternion.Slerp(Quaternion.identity, q, weight);
}
/// <summary>
/// Creates a FromToRotation, but makes sure it's axis remains fixed near to the Quaternion singularity lastVertexPoint.
/// </summary>
/// <returns>
/// The from to rotation around an axis.
/// </returns>
/// <param name='fromDirection'>
/// From direction.
/// </param>
/// <param name='toDirection'>
/// To direction.
/// </param>
/// <param name='axis'>
/// Axis. Should be normalized before passing into this method.
/// </param>
public static Quaternion FromToAroundAxis(Vector3 fromDirection, Vector3 toDirection, Vector3 axis)
{
Quaternion fromTo = Quaternion.FromToRotation(fromDirection, toDirection);
float angle = 0;
Vector3 freeAxis = Vector3.zero;
fromTo.ToAngleAxis(out angle, out freeAxis);
float dot = Vector3.Dot(freeAxis, axis);
if (dot < 0) angle = -angle;
return Quaternion.AngleAxis(angle, axis);
}
/// <summary>
/// Gets the rotation that can be used to convert a rotation from one axis space to another.
/// </summary>
public static Quaternion RotationToLocalSpace(Quaternion space, Quaternion rotation)
{
return Quaternion.Inverse(Quaternion.Inverse(space) * rotation);
}
/// <summary>
/// Gets the Quaternion from rotation "from" to rotation "to".
/// </summary>
public static Quaternion FromToRotation(Quaternion from, Quaternion to)
{
if (to == from) return Quaternion.identity;
return to * Quaternion.Inverse(from);
}
/// <summary>
/// Gets the closest direction axis to a vector. Input vector must be normalized!
/// </summary>
public static Vector3 GetAxis(Vector3 v)
{
Vector3 closest = Vector3.right;
bool neg = false;
float x = Vector3.Dot(v, Vector3.right);
float maxAbsDot = Mathf.Abs(x);
if (x < 0f) neg = true;
float y = Vector3.Dot(v, Vector3.up);
float absDot = Mathf.Abs(y);
if (absDot > maxAbsDot)
{
maxAbsDot = absDot;
closest = Vector3.up;
neg = y < 0f;
}
float z = Vector3.Dot(v, Vector3.forward);
absDot = Mathf.Abs(z);
if (absDot > maxAbsDot)
{
closest = Vector3.forward;
neg = z < 0f;
}
if (neg) closest = -closest;
return closest;
}
/// <summary>
/// Clamps the rotation similar to V3Tools.ClampDirection.
/// </summary>
public static Quaternion ClampRotation(Quaternion rotation, float clampWeight, int clampSmoothing)
{
if (clampWeight >= 1f) return Quaternion.identity;
if (clampWeight <= 0f) return rotation;
float angle = Quaternion.Angle(Quaternion.identity, rotation);
float dot = 1f - (angle / 180f);
float targetClampMlp = Mathf.Clamp(1f - ((clampWeight - dot) / (1f - dot)), 0f, 1f);
float clampMlp = Mathf.Clamp(dot / clampWeight, 0f, 1f);
// Sine smoothing iterations
for (int i = 0; i < clampSmoothing; i++)
{
float sinF = clampMlp * Mathf.PI * 0.5f;
clampMlp = Mathf.Sin(sinF);
}
return Quaternion.Slerp(Quaternion.identity, rotation, clampMlp * targetClampMlp);
}
/// <summary>
/// Clamps an angular value.
/// </summary>
public static float ClampAngle(float angle, float clampWeight, int clampSmoothing)
{
if (clampWeight >= 1f) return 0f;
if (clampWeight <= 0f) return angle;
float dot = 1f - (Mathf.Abs(angle) / 180f);
float targetClampMlp = Mathf.Clamp(1f - ((clampWeight - dot) / (1f - dot)), 0f, 1f);
float clampMlp = Mathf.Clamp(dot / clampWeight, 0f, 1f);
// Sine smoothing iterations
for (int i = 0; i < clampSmoothing; i++)
{
float sinF = clampMlp * Mathf.PI * 0.5f;
clampMlp = Mathf.Sin(sinF);
}
return Mathf.Lerp(0f, angle, clampMlp * targetClampMlp);
}
/// <summary>
/// Used for matching the rotations of objects that have different orientations.
/// </summary>
public static Quaternion MatchRotation(Quaternion targetRotation, Vector3 targetforwardAxis, Vector3 targetUpAxis, Vector3 forwardAxis, Vector3 upAxis)
{
Quaternion f = Quaternion.LookRotation(forwardAxis, upAxis);
Quaternion fTarget = Quaternion.LookRotation(targetforwardAxis, targetUpAxis);
Quaternion d = targetRotation * fTarget;
return d * Quaternion.Inverse(f);
}
/// <summary>
/// Converts an Euler rotation from 0 to 360 representation to -180 to 180.
/// </summary>
public static Vector3 ToBiPolar(Vector3 euler)
{
return new Vector3(ToBiPolar(euler.x), ToBiPolar(euler.y), ToBiPolar(euler.z));
}
/// <summary>
/// Converts an angular value from 0 to 360 representation to -180 to 180.
/// </summary>
public static float ToBiPolar(float angle)
{
angle = angle % 360f;
if (angle >= 180f) return angle - 360f;
if (angle <= -180f) return angle + 360f;
return angle;
}
}
#endregion
#region Physics
/// <summary>
/// Useful Configurable Joint extensions made by Dencho
/// </summary>
public static class JointExt
{
/// <summary>
/// Sets a joint's targetRotation to match a given local rotation.
/// The joint _transform's local rotation must be cached on Start and passed into this method.
/// </summary>
public static void SetTargetRotationLocal(this ConfigurableJoint joint, Quaternion targetLocalRotation, Quaternion startLocalRotation)
{
if (joint.configuredInWorldSpace)
{
Debug.LogError("SetTargetRotationLocal should not be used with joints that are configured in world space. For world space joints, use SetTargetRotation.", joint);
}
SetTargetRotationInternal(joint, targetLocalRotation, startLocalRotation, Space.Self);
}
/// <summary>
/// Sets a joint's targetRotation to match a given world rotation.
/// The joint _transform's world rotation must be cached on Start and passed into this method.
/// </summary>
public static void SetTargetRotation(this ConfigurableJoint joint, Quaternion targetWorldRotation, Quaternion startWorldRotation)
{
if (!joint.configuredInWorldSpace)
{
Debug.LogError("SetTargetRotation must be used with joints that are configured in world space. For local space joints, use SetTargetRotationLocal.", joint);
}
SetTargetRotationInternal(joint, targetWorldRotation, startWorldRotation, Space.World);
}
public static void SetTargetRotation(this ConfigurableJoint cj, Quaternion startRot, Quaternion target, Space space)
{
Vector3 right = cj.axis;
Vector3 forward = Vector3.Cross(cj.axis, cj.secondaryAxis).normalized;
Vector3 up = Vector3.Cross(forward, right).normalized;
Quaternion localToJointSpace = Quaternion.LookRotation(forward, up);
if (space == Space.World)
{
Quaternion worldToLocal = Quaternion.Inverse(cj.transform.parent.rotation);
target = worldToLocal * target;
}
cj.targetRotation = Quaternion.Inverse(localToJointSpace) * Quaternion.Inverse(target) * startRot * localToJointSpace;
}
static void SetTargetRotationInternal(ConfigurableJoint joint, Quaternion targetRotation, Quaternion startRotation, Space space)
{
// Calculate the rotation expressed by the joint's axis and secondary axis
var right = joint.axis;
var forward = Vector3.Cross(joint.axis, joint.secondaryAxis).normalized;
var up = Vector3.Cross(forward, right).normalized;
Quaternion worldToJointSpace = Quaternion.LookRotation(forward, up);
// Transform into world space
Quaternion resultRotation = Quaternion.Inverse(worldToJointSpace);
// Counter-rotate and apply the new local rotation.
// Joint space is the inverse of world space, so we need to invert our value
if (space == Space.World)
{
resultRotation *= startRotation * Quaternion.Inverse(targetRotation);
}
else
{
resultRotation *= Quaternion.Inverse(targetRotation) * startRotation;
}
// Transform back into joint space
resultRotation *= worldToJointSpace;
// SetSize target rotation to our newly calculated rotation
joint.targetRotation = resultRotation;
}
/// <summary>
/// Adjust ConfigurableJoint settings to closely match CharacterJoint behaviour
/// </summary>
//public static void SetupAsCharacterJoint(this ConfigurableJoint joint)
//{
// joint.xMotion = ConfigurableJointMotion.Locked;
// joint.yMotion = ConfigurableJointMotion.Locked;
// joint.zMotion = ConfigurableJointMotion.Locked;
// joint.angularXMotion = ConfigurableJointMotion.Limited;
// joint.angularYMotion = ConfigurableJointMotion.Limited;
// joint.angularZMotion = ConfigurableJointMotion.Limited;
// joint.breakForce = Mathf.Infinity;
// joint.breakTorque = Mathf.Infinity;
// joint.rotationDriveMode = RotationDriveMode.Slerp;
// var slerpDrive = joint.slerpDrive;
// slerpDrive.mode = JointDriveMode.Position;
// slerpDrive.maximumForce = Mathf.Infinity;
// joint.slerpDrive = slerpDrive;
//}
}
/// <summary>
/// Useful Extensions made by Dencho
/// </summary>
public static class PhysicsExt
{
/// <summary>
/// Does a bit check to determin if the gameobject is in a certain layer
/// </summary>
public static bool IsInLayer(this GameObject other_, int layer_)
{
return (layer_ == (layer_ | (1 << other_.layer)));
}
/// <summary>
/// Calculate the initial velocity of a jump based off gravity and desired maximum height attained
/// </summary>
public static float CalculateJumpSpeed(float jumpHeight, float gravity)
{
return Mathf.Sqrt(2 * jumpHeight * gravity);
}
/// <summary>
/// Blends <see cref="Rigidbody.velocity"/> towards a given direction relative to set weight
/// </summary>
/// <param name="body"></param>
/// <param name="direction"></param>
/// <param name="target"></param>
/// <param name="weight"></param>
public static void LerpRigidbodyVelocityTo(this Rigidbody body, Vector3 direction, out Vector3 target, float weight)
{
target = body.velocity;
target = Vector3.Lerp(target, direction, weight);
body.velocity = target;
}
public static void SlerpRigidbodyVelocityTo(this Rigidbody body, Vector3 direction, out Vector3 target, float weight)
{
target = body.velocity;
target = Vector3.Slerp(target, direction, weight);
body.velocity = target;
}
public static void LerpRigidbodyAngularVelocityTo(this Rigidbody body, Vector3 direction, out Vector3 target, float weight)
{
target = body.angularVelocity;
target = Vector3.Lerp(target, direction, weight);
body.angularVelocity = target;
}
public static void SlerpRigidbodyAngularVelocityTo(this Rigidbody body, Vector3 direction, out Vector3 target, float weight)
{
target = body.angularVelocity;
target = Vector3.Slerp(target, direction, weight);
body.angularVelocity = target;
}
}
#endregion
#region Unity Types
/// <summary>
/// Useful Transform extensions made by Dencho
/// </summary>
public static class TransformExt
{
/// <summary>
/// Say you use this like pelvisIKBone.GetRelativePositionFromRoot(rootTransform, out targetDirection);
/// Example of Calcs going on: pelvisToRoot = Quaternion.Inverse(ikDummyBone.rotation) * (root.position - ikDummyBone.position);
/// pelvisToRoot = The direction vector from root towards the pelvisIKBone
/// ikDummyBone = ik.references.pelvis, set to IKDumy not the ragDoll;
/// root = ik.references.root is always the root referenced in the ik solver;
/// </summary>
/// <param name="dumyBone">this is the bone being rotated, like the refference bone of an IK solver</param>
/// <param name="ikRoot">The root of the IKDummyRig(like [IKRig]) </param>
/// <param name="vector">The vector to set</param>
public static void GetRelativePositionFromRoot(this Transform dumyBone, Transform ikRoot, out Vector3 vector)
{
vector = Quaternion.Inverse(dumyBone.rotation) * (ikRoot.position - dumyBone.position);
}
/// <summary>
/// Say you use this like pelvisIKBone.GetRelativePositionFromRoot(rootTransform, out targetDirection);
/// Example: vector = Quaternion.Inverse(dumyBone.rotation) * (dumyBone.root.position - dumyBone.position);
/// pelvisToRoot = The direction vector from root towards the pelvisIKBone
/// ikDummyBone = ik.references.pelvis, set to IKDumy not the ragDoll;
/// root = ik.references.root is always the root referenced in the ik solver;
/// </summary>
/// <param name="dumyBone">this is the bone being rotated, like the refference bone of an IK solver</param>
/// <param name="vector">The vector to set</param>
public static void GetRelativePositionFromRoot(this Transform dumyBone, out Vector3 vector)
{
vector = Quaternion.Inverse(dumyBone.rotation) * (dumyBone.root.position - dumyBone.position);
}
/// <summary>
/// Example: quater = Quaternion.Inverse(dollBone.rotation) * ikRoot.rotation;
/// relativeRotationToTarget = the (inversed) dollBones rotation with an added (not inversed)ikRoot rotation
/// target = usually the _transform the IK is on, than the parent of the ik
/// </summary>
/// <param name="dollBone">The rag dollBoned</param>
/// <param name="ikRoot">The _transform that hold the IKSolver</param>
/// <returns></returns>
public static void GetRelativeRotationFromRoot(this Transform dollBone, Transform ikRoot, out Quaternion quater)
{
quater = Quaternion.Inverse(dollBone.rotation) * ikRoot.rotation;
}
/// <summary>
/// Example: quater = Quaternion.Slerp(dollBone.rotation, Quaternion.Inverse(dollBone.rotation), 0.5f);
/// relativeRotationToTarget = the (inversed) dollBones rotation with an added (not inversed)ikRoot rotation
/// target = usually the _transform the IK is on, than the parent of the ik
/// </summary>
/// <param name="dollBone">The rag dollBoned</param>
/// <returns></returns>
public static void GetRelativeRotationFromRoot(this Transform dollBone, out Quaternion quater)
{
quater = Quaternion.Slerp(dollBone.rotation, Quaternion.Inverse(dollBone.rotation), 0.5f);
}
/// <summary>
/// Helper Method but for RectTransforms
/// </summary>
/// <param name="transform"></param>
/// <param name="parent"></param>
public static void Reset(this RectTransform transform, Transform parent = null)
{
if (parent != null)
{
transform.SetParent(parent);
transform.anchoredPosition = Vector2.zero;
transform.localRotation = Quaternion.identity;
transform.localScale = Vector3.one;
}
else
{
transform.localPosition = Vector3.zero;
transform.localRotation = Quaternion.identity;
transform.localScale = Vector3.one;
}
}
/// <summary>
/// Helper method to Reset Transforms to thier parent, if a parent is provided it will reparent the _transform to it before zeroing out its _transform values
/// </summary>
/// <param name="transform"></param>
/// <param name="parent"></param>
public static void Reset(this Transform transform, Transform parent = null)
{
if (parent != null)
{
transform.SetParent(parent);
transform.localPosition = Vector3.zero;
transform.localRotation = Quaternion.identity;
transform.localScale = Vector3.one;
}
else
{
transform.localPosition = Vector3.zero;
transform.localRotation = Quaternion.identity;
transform.localScale = Vector3.one;
}
}
public static bool IsInLayer(this Transform transform, LayerMask layerMMask)
{
return (layerMMask == (layerMMask | (1 << transform.gameObject.layer)));
}
}
/// <summary>
/// Useful GameObject extensions made by Dencho
/// </summary>
public static class GameObjectExt
{
/// <summary>
/// Uses InstanceIDs to check if an object is the same or not
/// </summary>
/// <param name="gameObject"></param>
/// <param name="other"></param>
/// <returns></returns>
public static bool IsSameInstance(this GameObject gameObject, GameObject other)
{
return gameObject.GetInstanceID() == other.GetInstanceID();
}
/// <summary>
/// Helper method to check if a gameobject is included under the given <see cref="LayerMask"/>
/// </summary>
/// <param name="gameObject"></param>
/// <param name="layerMMask"></param>
/// <returns>True if gameobjects layer is in the given mask</returns>
public static bool IsInLayer(this GameObject gameObject, LayerMask layerMMask)
{
return (layerMMask == (layerMMask | (1 << gameObject.layer)));
}
}
#endregion
#region Structs
/// <summary>
/// Maps a <see cref="Vector3"/> using <see cref="Mathf.InverseLerp(float, float, float)"/>
/// </summary>
[System.Serializable]
public struct Vector3Curve
{
public AnimationCurve X;
public AnimationCurve Y;
public AnimationCurve Z;
public Vector3Curve(float min = 0f, float max = 1f)
{
X = AnimationCurve.Linear(0f, min, 1f, max);
Y = AnimationCurve.Linear(0f, min, 1f, max);
Z = AnimationCurve.Linear(0f, min, 1f, max);
}
public Vector3Curve LinearCurve()
{
return new Vector3Curve(0f, 1f);
}
public void Evaluate(out Vector3 output, float x = 0f, float y = 0f, float z = 0f)
{
output.x = X.Evaluate(x);
output.y = Y.Evaluate(y);
output.z = Z.Evaluate(z);
}
public void Evaluate(Vector3 input, out Vector3 output)
{
output.x = X.Evaluate(input.x);
output.y = Y.Evaluate(input.y);
output.z = Z.Evaluate(input.z);
}
}
/// <summary>
/// Maps a <see cref="Vector3"/> using <see cref="Mathf.InverseLerp(float, float, float)"/>
/// </summary>
[System.Serializable]
public struct Vector3Map
{
public bool zero;
[Tooltip("The min magnitude an axis can be before its returned as 0")]
public Vector3 axisMin;
[Tooltip("The max magnitude an axis can be before its returned as 1")]
public Vector3 axisMax;
[Range(0f, 6f)]
[Tooltip("The weight of this whole map, 0 = all axis will be 0, remember these are multipliers, these all are")]
public float multiplier;
public Vector3 Format(Vector3 input, out Vector3 output)
{
output.x = Mathf.InverseLerp(axisMin.x, axisMax.x, input.x);
output.y = Mathf.InverseLerp(axisMin.y, axisMax.y, input.y);
output.z = Mathf.InverseLerp(axisMin.z, axisMax.z, input.z);
return (output * multiplier);
}
}
/// <summary>
/// Maps a float using <see cref="Mathf.InverseLerp(float, float, float)"/>
/// </summary>
[System.Serializable]
public struct Vector1Map
{
public bool zero;
[Tooltip("The min magnitude an axis can be before its returned as 0")]
public float axisMin;
[Tooltip("The max magnitude an axis can be before its returned as 1")]
public float axisMax;
[Range(0f, 6f)]
[Tooltip("The weight of this whole map, 0 = all axis will be 0, remember these are multipliers, these all are")]
public float multiplier;
public float Format(float input, out float output)
{
output = Mathf.InverseLerp(axisMax, axisMax, input);
return (output * multiplier);
}
}
#endregion
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment