// TODO: could probably figure out "inverse" from direction of topY compared to bottomY public static Vector3[] GetConeFrustumVertices(CSGCircleDefinition definition, float topHeight, float rotation, int segments, ref Vector3[] vertices, bool inverse = false) { var rotate = Quaternion.AngleAxis(rotation, Vector3.up); var bottomAxisX = rotate * Vector3.right * definition.diameterX * 0.5f; var bottomAxisZ = rotate * Vector3.forward * definition.diameterZ * 0.5f; var topY = Vector3.up * topHeight; var bottomY = Vector3.up * definition.height; if (vertices == null || vertices.Length != segments + 1) { vertices = new Vector3[segments + 1]; } float angleOffset = ((segments & 1) == 1) ? 0.0f : ((360.0f / segments) * 0.5f); vertices[0] = topY; for (int v = 0; v < segments; v++) { var r = (((v * 360.0f) / (float)segments) + angleOffset) * Mathf.Deg2Rad; var s = Mathf.Sin(r); var c = Mathf.Cos(r); var bottomVertex = (bottomAxisX * c) + (bottomAxisZ * s); bottomVertex += bottomY; var vi = inverse ? (segments - v) : (v + 1); vertices[vi] = bottomVertex; } return(vertices); }
public static Vector2 Rotate(Vector2 v, float rad) { float cos = Mathf.Cos(rad); float sin = Mathf.Sin(rad); return(new Vector2(cos * v.x - sin * v.y, sin * v.x + cos * v.y)); }
public static void CreateSphereVertices(Vector3 diameterXYZ, float offsetY, bool generateFromCenter, int horzSegments, int vertSegments, ref Vector3[] vertices) { //var lastVertSegment = vertSegments - 1; int vertexCount = (horzSegments * (vertSegments - 1)) + 2; if (vertices == null || vertices.Length != vertexCount) { vertices = new Vector3[vertexCount]; } var radius = 0.5f * diameterXYZ; var offset = generateFromCenter ? offsetY : radius.y + offsetY; vertices[0] = Vector3.down * radius.y; vertices[1] = Vector3.up * radius.y; vertices[0].y += offset; vertices[1].y += offset; // TODO: optimize var degreePerSegment = (360.0f / horzSegments) * Mathf.Deg2Rad; var angleOffset = ((horzSegments & 1) == 1) ? 0.0f : ((360.0f / horzSegments) * 0.5f) * Mathf.Deg2Rad; for (int v = 1, vertexIndex = 2; v < vertSegments; v++) { var segmentFactor = ((v - (vertSegments / 2.0f)) / vertSegments); // [-0.5f ... 0.5f] var segmentDegree = (segmentFactor * 180); // [-90 .. 90] var segmentHeight = Mathf.Sin(segmentDegree * Mathf.Deg2Rad); var segmentRadius = Mathf.Cos(segmentDegree * Mathf.Deg2Rad); // [0 .. 0.707 .. 1 .. 0.707 .. 0] var yRingPos = (segmentHeight * radius.y) + offset; var xRingRadius = segmentRadius * radius.x; var zRingRadius = segmentRadius * radius.z; if (radius.y < 0) { for (int h = horzSegments - 1; h >= 0; h--, vertexIndex++) { var hRad = (h * degreePerSegment) + angleOffset; vertices[vertexIndex] = new Vector3(Mathf.Cos(hRad) * segmentRadius * radius.x, yRingPos, Mathf.Sin(hRad) * segmentRadius * radius.z); } } else { for (int h = 0; h < horzSegments; h++, vertexIndex++) { var hRad = (h * degreePerSegment) + angleOffset; vertices[vertexIndex] = new Vector3(Mathf.Cos(hRad) * segmentRadius * radius.x, yRingPos, Mathf.Sin(hRad) * segmentRadius * radius.z); } } } }
/// <summary> /// Modeled after the piecewise exponentially-damped sine wave: /// y = (1/2)*sin(13pi/2*(2*x))*Math.Pow(2, 10 * ((2*x) - 1)) ; [0,0.5) /// y = (1/2)*(sin(-13pi/2*((2x-1)+1))*Math.Pow(2,-10(2*x-1)) + 2) ; [0.5, 1] /// </summary> public static float ElasticEaseInOut(float p) { if (p < 0.5f) { return(0.5f * Math.Sin(13 * HALFPI * (2 * p)) * Math.Pow(2, 10 * ((2 * p) - 1))); } return(0.5f * (Math.Sin(-13 * HALFPI * ((2 * p - 1) + 1)) * Math.Pow(2, -10 * (2 * p - 1)) + 2)); }
/// <summary> /// Modeled after the piecewise exponentially-damped sine wave: /// y = (1/2)*sin(13pi/2*(2*x))*Math.Pow(2, 10 * ((2*x) - 1)) ; [0,0.5) /// y = (1/2)*(sin(-13pi/2*((2x-1)+1))*Math.Pow(2,-10(2*x-1)) + 2) ; [0.5, 1] /// </summary> public static float EaseInOutElastic(this float This) { if (This < 0.5f) { return(0.5f * Math.Sin(13 * HalfPi * (2 * This)) * Math.Pow(2, 10 * (2 * This - 1))); } return(0.5f * (Math.Sin(-13 * HalfPi * (2 * This - 1 + 1)) * Math.Pow(2, -10 * (2 * This - 1)) + 2)); }
Vector3 RandomCircle(Vector3 center, float radius, float angle) { Vector3 pos = new Vector3(0, 0, 0); pos.x = center.x + radius * Mathf.Sin(angle * Mathf.Deg2Rad); pos.y = center.y + radius * Mathf.Cos(angle * Mathf.Deg2Rad); pos.z = angle; return(pos); }
public static bool GenerateHemisphereVertices(Vector3 diameterXYZ, Matrix4x4 transform, int horzSegments, int vertSegments, ref Vector3[] vertices) { var bottomCap = true; var topCap = false; var extraVertices = ((!bottomCap) ? 1 : 0) + ((!topCap) ? 1 : 0); var rings = (vertSegments) + (topCap ? 1 : 0); var vertexCount = (horzSegments * rings) + extraVertices; var topVertex = 0; var bottomVertex = (!topCap) ? 1 : 0; var radius = new Vector3(diameterXYZ.x * 0.5f, diameterXYZ.y, diameterXYZ.z * 0.5f); if (vertices == null || vertices.Length != vertexCount) { vertices = new Vector3[vertexCount]; } if (!topCap) { vertices[topVertex] = transform.MultiplyPoint(Vector3.up * radius.y); // top } if (!bottomCap) { vertices[bottomVertex] = transform.MultiplyPoint(Vector3.zero); // bottom } var degreePerSegment = (360.0f / horzSegments) * Mathf.Deg2Rad; var angleOffset = ((horzSegments & 1) == 1) ? 0.0f : 0.5f * degreePerSegment; var vertexIndex = extraVertices; { for (int h = 0; h < horzSegments; h++, vertexIndex++) { var hRad = (h * degreePerSegment) + angleOffset; vertices[vertexIndex] = transform.MultiplyPoint(new Vector3(Mathf.Cos(hRad) * radius.x, 0.0f, Mathf.Sin(hRad) * radius.z)); } } for (int v = 1; v < rings; v++) { var segmentFactor = ((v - (rings / 2.0f)) / rings) + 0.5f; // [0.0f ... 1.0f] var segmentDegree = (segmentFactor * 90); // [0 .. 90] var segmentHeight = Mathf.Sin(segmentDegree * Mathf.Deg2Rad) * radius.y; var segmentRadius = Mathf.Cos(segmentDegree * Mathf.Deg2Rad); // [0 .. 0.707 .. 1 .. 0.707 .. 0] for (int h = 0; h < horzSegments; h++, vertexIndex++) { vertices[vertexIndex].x = vertices[h + extraVertices].x * segmentRadius; vertices[vertexIndex].y = segmentHeight; vertices[vertexIndex].z = vertices[h + extraVertices].z * segmentRadius; } } return(true); }
public static UnityEngine.Vector2 Rotate(this UnityEngine.Vector2 v, float degrees) { float radians = degrees * Mathf.Deg2Rad; float sin = Mathf.Sin(radians); float cos = Mathf.Cos(radians); float tx = v.x; float ty = v.y; return(new UnityEngine.Vector2(cos * tx - sin * ty, sin * tx + cos * ty)); }
/// <summary> /// Modeled after the piecewise expoly-damped sine wave: /// y = (1/2)*sin(13pi/2*(2*x))*Math.Pow(2, 10 * ((2*x) - 1)) ; [0,0.5) /// y = (1/2)*(sin(-13pi/2*((2x-1)+1))*Math.Pow(2,-10(2*x-1)) + 2) ; [0.5, 1] /// </summary> internal static float EaseInOutElastic(float p) { if (p < 0.5f) { return(0.5f * Math.Sin(13 * HALFPI * (2 * p)) * Math.Pow(2, 10 * ((2 * p) - 1))); } else { return(0.5f * ((Math.Sin(-13 * HALFPI * (2 * p)) * Math.Pow(2, -10 * ((2 * p) - 1))) + 2)); } }
/// <summary> /// Modeled after the piecewise overshooting cubic function: /// y = (1/2)*((2x)^3-(2x)*sin(2*x*pi)) ; [0, 0.5) /// y = (1/2)*(1-((1-x)^3-(1-x)*sin((1-x)*pi))+1) ; [0.5, 1] /// </summary> public static float EaseInOutBack(this float This) { if (This < 0.5f) { var f = 2 * This; return(0.5f * (f * f * f - f * Math.Sin(f * Pi))); } else { var f = 1 - (2 * This - 1); return(0.5f * (1 - (f * f * f - f * Math.Sin(f * Pi))) + 0.5f); } }
/// <summary> /// Modeled after the piecewise overshooting cubic function: /// y = (1/2)*((2x)^3-(2x)*sin(2*x*pi)) ; [0, 0.5) /// y = (1/2)*(1-((1-x)^3-(1-x)*sin((1-x)*pi))+1) ; [0.5, 1] /// </summary> public static float BackEaseInOut(float p) { if (p < 0.5f) { float f = 2 * p; return(0.5f * (f * f * f - f * Math.Sin(f * Pi))); } else { float f = (1 - (2 * p - 1)); return(0.5f * (1 - (f * f * f - f * Math.Sin(f * Pi))) + 0.5f); } }
/// <summary> /// Modeled after the piecewise overshooting cubic function: /// y = (1/2)*((2x)^3-(2x)*sin(2*x*pi)) ; [0, 0.5) /// y = (1/2)*(1-((1-x)^3-(1-x)*sin((1-x)*pi))+1) ; [0.5, 1] /// </summary> static internal float easeInOutBack(float p) { if (p < 0.5f) { float f = 2 * p; return(0.5f * (f * f * f - f * Math.Sin(f * PI))); } else { float f = (1 - (2 * p - 1)); return(0.5f * (1 - (f * f * f - f * Math.Sin(f * PI))) + 0.5f); } }
/// <summary> /// Modeled after the piecewise overshooting cubic function: /// y = (1/2)*((2x)^3-(2x)*sin(2*x*pi)) ; [0, 0.5) /// y = (1/2)*(1-((1-x)^3-(1-x)*sin((1-x)*pi))+1) ; [0.5, 1] /// </summary> internal static float EaseInOutBack(float p) { if (p < 0.5f) { float f = 2 * p; return(0.5f * ((f * f * f) - (f * Math.Sin(f * PI)))); } else { float f = 1 - ((2 * p) - 1); return((0.5f * (1 - ((f * f * f) - (f * Math.Sin(f * PI))))) + 0.5f); } }
/** Clamps the velocity to the max speed and optionally the forwards direction. * \param velocity Desired velocity of the character. In world units per second. * \param maxSpeed Max speed of the character. In world units per second. * \param slowdownFactor Value between 0 and 1 which determines how much slower the character should move than normal. * Normally 1 but should go to 0 when the character approaches the end of the path. * \param slowWhenNotFacingTarget Prevent the velocity from being too far away from the forward direction of the character * and slow the character down if the desired velocity is not in the same direction as the forward vector. * \param forward Forward direction of the character. Used together with the \a slowWhenNotFacingTarget parameter. * * Note that all vectors are 2D vectors, not 3D vectors. * * \returns The clamped velocity in world units per second. */ public static Vector2 ClampVelocity(Vector2 velocity, float maxSpeed, float slowdownFactor, bool slowWhenNotFacingTarget, Vector2 forward) { // Max speed to use for this frame var currentMaxSpeed = maxSpeed * slowdownFactor; // Check if the agent should slow down in case it is not facing the direction it wants to move in if (slowWhenNotFacingTarget && (forward.x != 0 || forward.y != 0)) { float currentSpeed; var normalizedVelocity = VectorMath.Normalize(velocity.ToPFV2(), out currentSpeed); float dot = Vector2.Dot(normalizedVelocity.ToUnityV2(), forward); // Lower the speed when the character's forward direction is not pointing towards the desired velocity // 1 when velocity is in the same direction as forward // 0.2 when they point in the opposite directions float directionSpeedFactor = Mathf.Clamp(dot + 0.707f, 0.2f, 1.0f); currentMaxSpeed *= directionSpeedFactor; currentSpeed = Mathf.Min(currentSpeed, currentMaxSpeed); // Angle between the forwards direction of the character and our desired velocity float angle = Mathf.Acos(Mathf.Clamp(dot, -1, 1)); // Clamp the angle to 20 degrees // We cannot keep the velocity exactly in the forwards direction of the character // because we use the rotation to determine in which direction to rotate and if // the velocity would always be in the forwards direction of the character then // the character would never rotate. // Allow larger angles when near the end of the path to prevent oscillations. angle = Mathf.Min(angle, (20f + 180f * (1 - slowdownFactor * slowdownFactor)) * Mathf.Deg2Rad); float sin = Mathf.Sin(angle); float cos = Mathf.Cos(angle); // Determine if we should rotate clockwise or counter-clockwise to move towards the current velocity sin *= Mathf.Sign(normalizedVelocity.x * forward.y - normalizedVelocity.y * forward.x); // Rotate the #forward vector by #angle radians // The rotation is done using an inlined rotation matrix. // See https://en.wikipedia.org/wiki/Rotation_matrix return(new Vector2(forward.x * cos + forward.y * sin, forward.y * cos - forward.x * sin) * currentSpeed); } else { return(Vector2.ClampMagnitude(velocity, currentMaxSpeed)); } }
public static Vector3[] GetConicalFrustumVertices(CSGCircleDefinition bottom, CSGCircleDefinition top, float rotation, int segments, ref Vector3[] vertices) { if (top.height > bottom.height) { var temp = top; top = bottom; bottom = temp; } var rotate = Quaternion.AngleAxis(rotation, Vector3.up); var topAxisX = rotate * Vector3.right * top.diameterX * 0.5f; var topAxisZ = rotate * Vector3.forward * top.diameterZ * 0.5f; var bottomAxisX = rotate * Vector3.right * bottom.diameterX * 0.5f; var bottomAxisZ = rotate * Vector3.forward * bottom.diameterZ * 0.5f; var topY = Vector3.up * top.height; var bottomY = Vector3.up * bottom.height; // TODO: handle situation where diameterX & diameterZ are 0 (only create one vertex) if (vertices == null || vertices.Length != segments * 2) { vertices = new Vector3[segments * 2]; } float angleOffset = ((segments & 1) == 1) ? 0.0f : ((360.0f / segments) * 0.5f); for (int v = 0; v < segments; v++) { var r = (((v * 360.0f) / (float)segments) + angleOffset) * Mathf.Deg2Rad; var s = Mathf.Sin(r); var c = Mathf.Cos(r); var topVertex = (topAxisX * c) + (topAxisZ * s); var bottomVertex = (bottomAxisX * c) + (bottomAxisZ * s); topVertex += topY; bottomVertex += bottomY; vertices[v] = topVertex; vertices[v + segments] = bottomVertex; } return(vertices); }
public void Validate() { if (surfaceDefinition == null) { surfaceDefinition = new ChiselSurfaceDefinition(); } topDiameterX = Mathf.Abs(topDiameterX); topDiameterZ = Mathf.Abs(topDiameterZ); bottomDiameterX = Mathf.Abs(bottomDiameterX); bottomDiameterZ = Mathf.Abs(bottomDiameterZ); sides = Mathf.Max(3, sides); if (surfaceDefinition.EnsureSize(2 + sides)) { // Top plane surfaceDefinition.surfaces[0].surfaceDescription.UV0 = UVMatrix.centered; // Bottom plane surfaceDefinition.surfaces[1].surfaceDescription.UV0 = UVMatrix.centered; float radius = topDiameterX * 0.5f; float angle = (360.0f / sides); float sideLength = (2 * Mathf.Sin((angle / 2.0f) * Mathf.Deg2Rad)) * radius; // Side planes for (int i = 2; i < 2 + sides; i++) { var uv0 = UVMatrix.identity; uv0.U.w = ((i - 2) + 0.5f) * sideLength; // TODO: align with bottom //uv0.V.w = 0.5f; surfaceDefinition.surfaces[i].surfaceDescription.UV0 = uv0; surfaceDefinition.surfaces[i].surfaceDescription.smoothingGroup = smoothingGroup; } } }
/// <summary> /// Modeled after quarter-cycle of sine wave (different phase) /// </summary> internal static float EaseOutSine(float p) { return(Math.Sin(p * HALFPI)); }
/// <summary> /// Modeled after overshooting cubic y = 1-((1-x)^3-(1-x)*sin((1-x)*pi)) /// </summary> public static float BackEaseOut(float p) { float f = (1 - p); return(1 - (f * f * f - f * Math.Sin(f * Pi))); }
/// <summary> /// Modeled after the overshooting cubic y = x^3-x*sin(x*pi) /// </summary> public static float BackEaseIn(float p) { return(p * p * p - p * Math.Sin(p * Pi)); }
/// <summary> /// Modeled after the damped sine wave y = sin(-13pi/2*(x + 1))*Math.Pow(2, -10x) + 1 /// </summary> public static float ElasticEaseOut(float p) { return(Math.Sin(-13 * HalfPi * (p + 1)) * Math.Pow(2, -10 * p) + 1); }
/// <summary> /// Modeled after the damped sine wave y = sin(13pi/2*x)*Math.Pow(2, 10 * (x - 1)) /// </summary> public static float ElasticEaseIn(float p) { return(Math.Sin(13 * HalfPi * p) * Math.Pow(2, 10 * (p - 1))); }
/// <summary> /// Modeled after quarter-cycle of sine wave (different phase) /// </summary> public static float SineEaseOut(float p) { return(Math.Sin(p * HalfPi)); }
/// <summary> /// Modeled after quarter-cycle of sine wave /// </summary> public static float SineEaseIn(float p) { return(Math.Sin((p - 1) * HalfPi) + 1); }
public static float ElasticIn(float p) { return(-(Mathf.Pow(2, 10 * (p - 1f)) * Mathf.Sin((p - 1.075f) * (Mathf.PI * 2) / 0.3f))); }
// possible situations: // capsule with top AND bottom set to >0 height // capsule with top OR bottom set to 0 height // capsule with both top AND bottom set to 0 height // capsule with height equal to top and bottom height public static bool GenerateCapsuleVertices(ref CSGCapsuleDefinition definition, ref Vector3[] vertices) { definition.Validate(); var haveTopHemisphere = definition.haveRoundedTop; var haveBottomHemisphere = definition.haveRoundedBottom; var haveMiddleCylinder = definition.haveCylinder; if (!haveBottomHemisphere && !haveTopHemisphere && !haveMiddleCylinder) { return(false); } var radiusX = definition.diameterX * 0.5f; var radiusZ = definition.diameterZ * 0.5f; var topHeight = haveTopHemisphere ? definition.topHeight : 0; var bottomHeight = haveBottomHemisphere ? definition.bottomHeight : 0; var totalHeight = definition.height; var cylinderHeight = definition.cylinderHeight; var sides = definition.sides; var extraVertices = definition.extraVertexCount; var bottomRings = definition.bottomRingCount; var topRings = definition.topRingCount; var ringCount = definition.ringCount; var vertexCount = definition.vertexCount; var bottomVertex = definition.bottomVertex; var topVertex = definition.topVertex; var topOffset = definition.topOffset + definition.offsetY; var bottomOffset = definition.bottomOffset + definition.offsetY; if (vertices == null || vertices.Length != vertexCount) { vertices = new Vector3[vertexCount]; } if (haveBottomHemisphere) { vertices[bottomVertex] = Vector3.up * (bottomOffset - bottomHeight); // bottom } if (haveTopHemisphere) { vertices[topVertex] = Vector3.up * (topOffset + topHeight); // top } var degreePerSegment = (360.0f / sides) * Mathf.Deg2Rad; var angleOffset = definition.rotation + (((sides & 1) == 1) ? 0.0f : 0.5f * degreePerSegment); var topVertexOffset = extraVertices + ((topRings - 1) * sides); var bottomVertexOffset = extraVertices + ((ringCount - bottomRings) * sides); var unitCircleOffset = topVertexOffset; var vertexIndex = unitCircleOffset; { for (int h = sides - 1; h >= 0; h--, vertexIndex++) { var hRad = (h * degreePerSegment) + angleOffset; vertices[vertexIndex] = new Vector3(Mathf.Cos(hRad) * radiusX, 0.0f, Mathf.Sin(hRad) * radiusZ); } } for (int v = 1; v < topRings; v++) { vertexIndex = topVertexOffset - (v * sides); var segmentFactor = ((v - (topRings * 0.5f)) / topRings) + 0.5f; // [0.0f ... 1.0f] var segmentDegree = (segmentFactor * 90); // [0 .. 90] var segmentHeight = topOffset + (Mathf.Sin(segmentDegree * Mathf.Deg2Rad) * topHeight); var segmentRadius = Mathf.Cos(segmentDegree * Mathf.Deg2Rad); // [0 .. 0.707 .. 1 .. 0.707 .. 0] for (int h = 0; h < sides; h++, vertexIndex++) { vertices[vertexIndex].x = vertices[h + unitCircleOffset].x * segmentRadius; vertices[vertexIndex].y = segmentHeight; vertices[vertexIndex].z = vertices[h + unitCircleOffset].z * segmentRadius; } } vertexIndex = bottomVertexOffset; { for (int h = 0; h < sides; h++, vertexIndex++) { vertices[vertexIndex] = new Vector3(vertices[h + unitCircleOffset].x, bottomOffset, vertices[h + unitCircleOffset].z); } } for (int v = 1; v < bottomRings; v++) { var segmentFactor = ((v - (bottomRings * 0.5f)) / bottomRings) + 0.5f; // [0.0f ... 1.0f] var segmentDegree = (segmentFactor * 90); // [0 .. 90] var segmentHeight = bottomOffset - bottomHeight + ((1 - Mathf.Sin(segmentDegree * Mathf.Deg2Rad)) * bottomHeight); var segmentRadius = Mathf.Cos(segmentDegree * Mathf.Deg2Rad); // [0 .. 0.707 .. 1 .. 0.707 .. 0] for (int h = 0; h < sides; h++, vertexIndex++) { vertices[vertexIndex].x = vertices[h + unitCircleOffset].x * segmentRadius; vertices[vertexIndex].y = segmentHeight; vertices[vertexIndex].z = vertices[h + unitCircleOffset].z * segmentRadius; } } { for (int h = 0; h < sides; h++, vertexIndex++) { vertices[h + unitCircleOffset].y = topOffset; } } return(true); }
/// <summary> /// Modeled after overshooting cubic y = 1-((1-x)^3-(1-x)*sin((1-x)*pi)) /// </summary> internal static float EaseOutBack(float p) { float f = 1 - p; return(1 - ((f * f * f) - (f * Math.Sin(f * PI)))); }
/// <summary> /// Modeled after the overshooting cubic y = x^3-x*sin(x*pi) /// </summary> internal static float EaseInBack(float p) { return((p * p * p) - (p * Math.Sin(p * PI))); }
/// <summary> /// Modeled after the damped sine wave y = sin(-13pi/2*(x + 1))*Math.Pow(2, -10x) + 1 /// </summary> internal static float EaseOutElastic(float p) { return((Math.Sin(-13 * HALFPI * (p + 1)) * Math.Pow(2, -10 * p)) + 1); }
/// <summary> /// Modeled after the damped sine wave y = sin(13pi/2*x)*Math.Pow(2, 10 * (x - 1)) /// </summary> internal static float EaseInElastic(float p) { return(Math.Sin(13 * HALFPI * p) * Math.Pow(2, 10 * (p - 1))); }
/// <summary> /// Modeled after quarter-cycle of sine wave /// </summary> internal static float EaseInSine(float p) { return(Math.Sin((p - 1) * HALFPI) + 1); }