public void ApplyFriction(Rigidbody rigidbody, float staticCoefficient, float kineticCoefficient, Vector3 contactPoint) { // Square the threshold for efficiency. var threshold = rigidbody.sleepThreshold * rigidbody.sleepThreshold; var gravityToApply = rigidbody.simulateGravity ? gravity : Vector3.zero; var rigidbodyToContact = (contactPoint - rigidbody.position); var gravitationalForce = rigidbody.mass * gravityToApply; var angle = Vector3.Angle(-rigidbodyToContact, Vector3.down) * Mathf.Deg2Rad; var N = gravitationalForce * Mathf.Cos(angle); var force = rigidbody.momentum / Time.fixedDeltaTime + gravitationalForce; var direction = force.normalized; // Apply to linear momentum. if (force.sqrMagnitude < threshold) { rigidbody.AddForce(-staticCoefficient * direction * N.magnitude); } else { rigidbody.AddForce(-kineticCoefficient * direction * N.magnitude); } rigidbody.AddForce(N); }
// 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); } } } }
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)); }
/** 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); }
/// <summary> /// Modeled after half sine wave /// </summary> internal static float EaseInOutSine(float p) { return(0.5f * (1 - Math.Cos(p * PI))); }
// 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 half sine wave /// </summary> internal static float EaseInOutSine(float p) => 0.5f * (1 - Math.Cos(p * PI));
public static bool GenerateStadiumVertices(CSGStadiumDefinition definition, ref Vector3[] vertices) { definition.Validate(); var topSides = definition.topSides; var bottomSides = definition.bottomSides; var sides = definition.sides; var length = definition.length; var topLength = definition.topLength; var bottomLength = definition.bottomLength; var diameter = definition.diameter; var radius = diameter * 0.5f; if (vertices == null || vertices.Length != sides * 2) { vertices = new Vector3[sides * 2]; } var firstTopSide = definition.firstTopSide; var lastTopSide = definition.lastTopSide; var firstBottomSide = definition.firstBottomSide; var lastBottomSide = definition.lastBottomSide; var haveCenter = definition.haveCenter; int vertexIndex = 0; if (!definition.haveRoundedTop) { vertices[vertexIndex] = new Vector3(-radius, 0, length * -0.5f); vertexIndex++; vertices[vertexIndex] = new Vector3(radius, 0, length * -0.5f); vertexIndex++; } else { var degreeOffset = -180.0f * Mathf.Deg2Rad; var degreePerSegment = (180.0f / topSides) * Mathf.Deg2Rad; var center = new Vector3(0, 0, (length * -0.5f) + topLength); for (int s = 0; s <= topSides; s++) { var hRad = (s * degreePerSegment) + degreeOffset; var x = center.x + (Mathf.Cos(hRad) * radius); var y = center.y; var z = center.z + (Mathf.Sin(hRad) * topLength); vertices[vertexIndex] = new Vector3(x, y, z); vertexIndex++; } } if (!haveCenter) { vertexIndex--; } //vertexIndex = definition.firstBottomSide; if (!definition.haveRoundedBottom) { vertices[vertexIndex] = new Vector3(radius, 0, length * 0.5f); vertexIndex++; vertices[vertexIndex] = new Vector3(-radius, 0, length * 0.5f); vertexIndex++; } else { var degreeOffset = 0.0f * Mathf.Deg2Rad; var degreePerSegment = (180.0f / bottomSides) * Mathf.Deg2Rad; var center = new Vector3(0, 0, (length * 0.5f) - bottomLength); for (int s = 0; s <= bottomSides; s++) { var hRad = (s * degreePerSegment) + degreeOffset; var x = center.x + (Mathf.Cos(hRad) * radius); var y = center.y; var z = center.z + (Mathf.Sin(hRad) * bottomLength); vertices[vertexIndex] = new Vector3(x, y, z); vertexIndex++; } } var extrusion = Vector3.up * definition.height; for (int s = 0; s < sides; s++) { vertices[s + sides] = vertices[s] + extrusion; } return(true); }
public static bool GenerateHemisphereSubMesh(CSGBrushSubMesh subMesh, Vector3 diameterXYZ, Matrix4x4 transform, int horzSegments, int vertSegments, CSGSurfaceAsset[] surfaceAssets, SurfaceDescription[] surfaceDescriptions) { if (diameterXYZ.x == 0 || diameterXYZ.y == 0 || diameterXYZ.z == 0) { subMesh.Clear(); return(false); } 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); var heightY = radius.y; float topY, bottomY; if (heightY < 0) { topY = 0; bottomY = heightY; } else { topY = heightY; bottomY = 0; } var vertices = new Vector3[vertexCount]; if (!topCap) { vertices[topVertex] = transform.MultiplyPoint(Vector3.up * topY); // top } if (!bottomCap) { vertices[bottomVertex] = transform.MultiplyPoint(Vector3.up * bottomY); // bottom } var degreePerSegment = (360.0f / horzSegments) * Mathf.Deg2Rad; var angleOffset = ((horzSegments & 1) == 1) ? 0.0f : 0.5f * degreePerSegment; var vertexIndex = extraVertices; if (heightY < 0) { for (int h = horzSegments - 1; h >= 0; 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)); } } else { 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) * heightY; 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(GenerateSegmentedSubMesh(subMesh, horzSegments, vertSegments, vertices, bottomCap, topCap, bottomVertex, topVertex, surfaceAssets, surfaceDescriptions)); }
public static float SineIn(float p) { return(-Mathf.Cos(p * (Mathf.PI * 0.5f)) + 1f); }
public static bool GenerateTorusAsset(CSGBrushMeshAsset brushMeshAsset, CSGTorusDefinition definition) { Vector3[] vertices = null; if (!GenerateTorusVertices(definition, ref vertices)) { brushMeshAsset.Clear(); return(false); } definition.Validate(); var surfaces = definition.surfaceAssets; var descriptions = definition.surfaceDescriptions; var tubeRadiusX = (definition.tubeWidth * 0.5f); var tubeRadiusY = (definition.tubeHeight * 0.5f); var torusRadius = (definition.outerDiameter * 0.5f) - tubeRadiusX; var horzSegments = definition.horizontalSegments; var vertSegments = definition.verticalSegments; var horzDegreePerSegment = (definition.totalAngle / horzSegments); var vertDegreePerSegment = (360.0f / vertSegments) * Mathf.Deg2Rad; var descriptionIndex = new int[2 + vertSegments]; descriptionIndex[0] = 0; descriptionIndex[1] = 1; var circleVertices = new Vector2[vertSegments]; var min = new Vector2(float.PositiveInfinity, float.PositiveInfinity); var max = new Vector2(float.NegativeInfinity, float.NegativeInfinity); var tubeAngleOffset = ((((vertSegments & 1) == 1) ? 0.0f : ((360.0f / vertSegments) * 0.5f)) + definition.tubeRotation) * Mathf.Deg2Rad; for (int v = 0; v < vertSegments; v++) { var vRad = tubeAngleOffset + (v * vertDegreePerSegment); circleVertices[v] = new Vector2((Mathf.Cos(vRad) * tubeRadiusX) - torusRadius, (Mathf.Sin(vRad) * tubeRadiusY)); min.x = Mathf.Min(min.x, circleVertices[v].x); min.y = Mathf.Min(min.y, circleVertices[v].y); max.x = Mathf.Max(max.x, circleVertices[v].x); max.y = Mathf.Max(max.y, circleVertices[v].y); descriptionIndex[v + 2] = 2; } if (definition.fitCircle) { var center = (max + min) * 0.5f; var size = (max - min) * 0.5f; size.x = tubeRadiusX / size.x; size.y = tubeRadiusY / size.y; for (int v = 0; v < vertSegments; v++) { circleVertices[v].x = (circleVertices[v].x - center.x) * size.x; circleVertices[v].y = (circleVertices[v].y - center.y) * size.y; circleVertices[v].x -= torusRadius; } } var subMeshes = new CSGBrushSubMesh[horzSegments]; var horzOffset = definition.startAngle; for (int h = 1, p = 0; h < horzSegments + 1; p = h, h++) { var hDegree0 = (p * horzDegreePerSegment) + horzOffset; var hDegree1 = (h * horzDegreePerSegment) + horzOffset; var rotation0 = Quaternion.AngleAxis(hDegree0, Vector3.up); var rotation1 = Quaternion.AngleAxis(hDegree1, Vector3.up); var subMeshVertices = new Vector3[vertSegments * 2]; for (int v = 0; v < vertSegments; v++) { subMeshVertices[v + vertSegments] = rotation0 * circleVertices[v]; subMeshVertices[v] = rotation1 * circleVertices[v]; } var subMesh = new CSGBrushSubMesh(); CreateExtrudedSubMesh(subMesh, vertSegments, descriptionIndex, descriptionIndex, 0, 1, subMeshVertices, surfaces, descriptions); if (!subMesh.Validate()) { brushMeshAsset.Clear(); return(false); } subMeshes[h - 1] = subMesh; } brushMeshAsset.SubMeshes = subMeshes; brushMeshAsset.CalculatePlanes(); brushMeshAsset.SetDirty(); return(true); }
public static bool GenerateTorusVertices(CSGTorusDefinition definition, ref Vector3[] vertices) { definition.Validate(); //var surfaces = definition.surfaceAssets; //var descriptions = definition.surfaceDescriptions; var tubeRadiusX = (definition.tubeWidth * 0.5f); var tubeRadiusY = (definition.tubeHeight * 0.5f); var torusRadius = (definition.outerDiameter * 0.5f) - tubeRadiusX; var horzSegments = definition.horizontalSegments; var vertSegments = definition.verticalSegments; var horzDegreePerSegment = (definition.totalAngle / horzSegments); var vertDegreePerSegment = (360.0f / vertSegments) * Mathf.Deg2Rad; var circleVertices = new Vector2[vertSegments]; var min = new Vector2(float.PositiveInfinity, float.PositiveInfinity); var max = new Vector2(float.NegativeInfinity, float.NegativeInfinity); var tubeAngleOffset = ((((vertSegments & 1) == 1) ? 0.0f : ((360.0f / vertSegments) * 0.5f)) + definition.tubeRotation) * Mathf.Deg2Rad; for (int v = 0; v < vertSegments; v++) { var vRad = tubeAngleOffset + (v * vertDegreePerSegment); circleVertices[v] = new Vector2((Mathf.Cos(vRad) * tubeRadiusX) - torusRadius, (Mathf.Sin(vRad) * tubeRadiusY)); min.x = Mathf.Min(min.x, circleVertices[v].x); min.y = Mathf.Min(min.y, circleVertices[v].y); max.x = Mathf.Max(max.x, circleVertices[v].x); max.y = Mathf.Max(max.y, circleVertices[v].y); } if (definition.fitCircle) { var center = (max + min) * 0.5f; var size = (max - min) * 0.5f; size.x = tubeRadiusX / size.x; size.y = tubeRadiusY / size.y; for (int v = 0; v < vertSegments; v++) { circleVertices[v].x = (circleVertices[v].x - center.x) * size.x; circleVertices[v].y = (circleVertices[v].y - center.y) * size.y; circleVertices[v].x -= torusRadius; } } if (definition.totalAngle != 360) { horzSegments++; } var horzOffset = definition.startAngle; var vertexCount = vertSegments * horzSegments; if (vertices == null || vertices.Length != vertexCount) { vertices = new Vector3[vertexCount]; } for (int h = 0, v = 0; h < horzSegments; h++) { var hDegree1 = (h * horzDegreePerSegment) + horzOffset; var rotation1 = Quaternion.AngleAxis(hDegree1, Vector3.up); for (int i = 0; i < vertSegments; i++, v++) { vertices[v] = rotation1 * circleVertices[i]; } } return(true); }
public static bool GenerateSpiralStairsAsset(CSGBrushMeshAsset brushMeshAsset, ref CSGSpiralStairsDefinition definition, CSGSurfaceAsset[] surfaceAssets, ref SurfaceDescription[] surfaceDescriptions) { if (surfaceAssets == null || surfaceDescriptions == null || surfaceAssets.Length != 6 || surfaceDescriptions.Length != 6) { brushMeshAsset.Clear(); return(false); } definition.Validate(); const float kEpsilon = 0.001f; var nosingDepth = definition.nosingDepth; var treadHeight = (nosingDepth < kEpsilon) ? 0 : definition.treadHeight; var haveTread = (treadHeight >= kEpsilon); var innerDiameter = definition.innerDiameter; var haveInnerCyl = (innerDiameter >= kEpsilon); var riserType = definition.riserType; var haveRiser = riserType != StairsRiserType.None; if (!haveRiser && !haveTread) { brushMeshAsset.Clear(); return(false); } var origin = definition.origin; var startAngle = definition.startAngle * Mathf.Deg2Rad; var anglePerStep = definition.AnglePerStep * Mathf.Deg2Rad; var nosingWidth = definition.nosingWidth; var outerDiameter = definition.outerDiameter; var p0 = new Vector2(Mathf.Sin(0), Mathf.Cos(0)); var p1 = new Vector2(Mathf.Sin(anglePerStep), Mathf.Cos(anglePerStep)); var pm = new Vector2(Mathf.Sin(anglePerStep * 0.5f), Mathf.Cos(anglePerStep * 0.5f)); var pn = (p0 + p1) * 0.5f; var stepOuterDiameter = outerDiameter + ((pm.magnitude - pn.magnitude) * (outerDiameter * 1.25f)); // TODO: figure out why we need the 1.25 magic number to fit the step in the outerDiameter? var stepOuterRadius = stepOuterDiameter * 0.5f; var stepInnerRadius = 0.0f; var stepHeight = definition.stepHeight; var height = definition.height; var stepCount = definition.StepCount; if (height < 0) { origin.y += height; height = -height; } // TODO: expose this to user var smoothSubDivisions = 3; var cylinderSubMeshCount = haveInnerCyl ? 2 : 1; var subMeshPerRiser = (riserType == StairsRiserType.None) ? 0 : (riserType == StairsRiserType.Smooth) ? (2 * smoothSubDivisions) : 1; var riserSubMeshCount = (stepCount * subMeshPerRiser) + ((riserType == StairsRiserType.None) ? 0 : cylinderSubMeshCount); var treadSubMeshCount = (haveTread ? stepCount + cylinderSubMeshCount : 0); var subMeshCount = (treadSubMeshCount + riserSubMeshCount); var treadStart = !haveRiser ? 0 : riserSubMeshCount; var innerSides = definition.innerSegments; var outerSides = definition.outerSegments; var riserDepth = definition.riserDepth; CSGBrushSubMesh[] subMeshes; if (brushMeshAsset.SubMeshCount != subMeshCount) { subMeshes = new CSGBrushSubMesh[subMeshCount]; for (int i = 0; i < subMeshCount; i++) { subMeshes[i] = new CSGBrushSubMesh(); } } else { subMeshes = brushMeshAsset.SubMeshes; } if (haveRiser) { if (riserType == StairsRiserType.ThinRiser) { var minY = origin.y; var maxY = origin.y + stepHeight - treadHeight; Vector2 o0, o1; float angle = startAngle; var c1 = Mathf.Sin(angle) * stepOuterRadius; var s1 = Mathf.Cos(angle) * stepOuterRadius; for (int i = 0; i < stepCount; i++) { var c0 = c1; var s0 = s1; angle += anglePerStep; c1 = Mathf.Sin(angle) * stepOuterRadius; s1 = Mathf.Cos(angle) * stepOuterRadius; o0 = new Vector2(origin.x + c0, origin.z + s0); o1 = new Vector2(origin.x, origin.z); var riserVector = (new Vector2((c0 - c1), (s0 - s1)).normalized) * riserDepth; var i0 = o0 - riserVector; var i1 = o1 - riserVector; var vertices = new[] { new Vector3(i0.x, maxY, i0.y), // 0 new Vector3(i1.x, maxY, i1.y), // 1 new Vector3(o1.x, maxY, o1.y), // 2 new Vector3(o0.x, maxY, o0.y), // 3 new Vector3(i0.x, minY, i0.y), // 4 new Vector3(i1.x, minY, i1.y), // 5 new Vector3(o1.x, minY, o1.y), // 6 new Vector3(o0.x, minY, o0.y), // 7 }; if (i == 0) { subMeshes[i].Polygons = CreateBoxAssetPolygons(surfaceAssets, surfaceDescriptions); minY -= treadHeight; } else { subMeshes[i].Polygons = subMeshes[0].Polygons.ToArray(); } subMeshes[i].HalfEdges = (anglePerStep > 0) ? invertedBoxHalfEdges.ToArray() : boxHalfEdges.ToArray(); subMeshes[i].Vertices = vertices; minY += stepHeight; maxY += stepHeight; } } else if (riserType == StairsRiserType.Smooth) { //var stepY = stepHeight; var minY = origin.y; var maxY = origin.y + stepHeight - treadHeight; var maxY2 = origin.y + (stepHeight * 2) - treadHeight; float angle = startAngle; var c1 = Mathf.Sin(angle); var s1 = Mathf.Cos(angle); angle += anglePerStep; var c2 = Mathf.Sin(angle); var s2 = Mathf.Cos(angle); for (int i = 0; i < riserSubMeshCount; i += subMeshPerRiser) { var c0 = c1; var s0 = s1; c1 = c2; s1 = s2; angle += anglePerStep; c2 = Mathf.Sin(angle); s2 = Mathf.Cos(angle); var c0o = c0 * stepOuterRadius; var c1o = c1 * stepOuterRadius; var s0o = s0 * stepOuterRadius; var s1o = s1 * stepOuterRadius; var o0 = new Vector2(origin.x + c0o, origin.z + s0o); var o1 = new Vector2(origin.x + c1o, origin.z + s1o); var i0 = o0; var i1 = o1; int subMeshIndex = i; for (int subDiv = 1; subDiv < smoothSubDivisions; subDiv++) { // TODO: need to space the subdivisions from smallest spaces to bigger spaces float stepMidRadius; stepMidRadius = (((outerDiameter * 0.5f) * (1.0f / (smoothSubDivisions + 1))) * ((smoothSubDivisions - 1) - (subDiv - 1))); if (subDiv == (smoothSubDivisions - 1)) { var innerRadius = (innerDiameter * 0.5f) - 0.1f; stepMidRadius = (innerRadius < 0.1f) ? stepMidRadius : innerRadius; } var c0i = c0 * stepMidRadius; var c1i = c1 * stepMidRadius; var s0i = s0 * stepMidRadius; var s1i = s1 * stepMidRadius; i0 = new Vector2(origin.x + c0i, origin.z + s0i); i1 = new Vector2(origin.x + c1i, origin.z + s1i); { var vertices = new[] { new Vector3(i0.x, maxY, i0.y), // 0 new Vector3(i0.x, minY, i0.y), // 1 new Vector3(o0.x, minY, o0.y), // 2 new Vector3(o0.x, maxY, o0.y), // 3 new Vector3(o1.x, maxY, o1.y), // 4 }; if (i == 0) { subMeshes[subMeshIndex].Polygons = CreateSquarePyramidAssetPolygons(surfaceAssets, surfaceDescriptions); } else { subMeshes[subMeshIndex].Polygons = subMeshes[subMeshIndex - i].Polygons.ToArray(); } subMeshes[subMeshIndex].HalfEdges = (anglePerStep > 0) ? invertedSquarePyramidHalfEdges.ToArray() : squarePyramidHalfEdges.ToArray(); subMeshes[subMeshIndex].Vertices = vertices; subMeshIndex++; } { var vertices = new[] { new Vector3(i0.x, maxY, i0.y), // 0 new Vector3(i0.x, minY, i0.y), // 1 new Vector3(i1.x, maxY, i1.y), // 2 new Vector3(o1.x, maxY, o1.y), // 3 }; if (i == 0) { subMeshes[subMeshIndex].Polygons = CreateTriangularPyramidAssetPolygons(surfaceAssets, surfaceDescriptions); } else { subMeshes[subMeshIndex].Polygons = subMeshes[subMeshIndex - i].Polygons.ToArray(); } subMeshes[subMeshIndex].HalfEdges = (anglePerStep > 0) ? invertedTriangularPyramidHalfEdges.ToArray() : triangularPyramidHalfEdges.ToArray(); subMeshes[subMeshIndex].Vertices = vertices; subMeshIndex++; } o0 = i0; o1 = i1; } { var vertices = new[] { new Vector3(i0.x, maxY, i0.y), // 0 new Vector3(i1.x, maxY, i1.y), // 2 new Vector3(i0.x, minY, i0.y), // 1 new Vector3(origin.x, minY, origin.y), // 3 }; if (i == 0) { subMeshes[subMeshIndex].Polygons = CreateTriangularPyramidAssetPolygons(surfaceAssets, surfaceDescriptions); } else { subMeshes[subMeshIndex].Polygons = subMeshes[subMeshIndex - i].Polygons.ToArray(); } subMeshes[subMeshIndex].HalfEdges = (anglePerStep > 0) ? invertedTriangularPyramidHalfEdges.ToArray() : triangularPyramidHalfEdges.ToArray(); subMeshes[subMeshIndex].Vertices = vertices; subMeshIndex++; } { var vertices = new[] { new Vector3(i1.x, maxY, i1.y), // 2 new Vector3(i0.x, maxY, i0.y), // 0 new Vector3(origin.x, maxY, origin.y), // 1 new Vector3(origin.x, minY, origin.y), // 3 }; if (i == 0) { subMeshes[subMeshIndex].Polygons = CreateTriangularPyramidAssetPolygons(surfaceAssets, surfaceDescriptions); } else { subMeshes[subMeshIndex].Polygons = subMeshes[subMeshIndex - i].Polygons.ToArray(); } subMeshes[subMeshIndex].HalfEdges = (anglePerStep > 0) ? invertedTriangularPyramidHalfEdges.ToArray() : triangularPyramidHalfEdges.ToArray(); subMeshes[subMeshIndex].Vertices = vertices; subMeshIndex++; } if (i == 0) { minY -= treadHeight; } minY += stepHeight; maxY += stepHeight; maxY2 += stepHeight; } } else { var minY = origin.y; var maxY = origin.y + stepHeight - treadHeight; Vector2 o0, o1; float angle = startAngle; var c1 = Mathf.Sin(angle) * stepOuterRadius; var s1 = Mathf.Cos(angle) * stepOuterRadius; for (int i = 0; i < stepCount; i++) { var c0 = c1; var s0 = s1; angle += anglePerStep; c1 = Mathf.Sin(angle) * stepOuterRadius; s1 = Mathf.Cos(angle) * stepOuterRadius; o0 = new Vector2(origin.x + c0, origin.z + s0); o1 = new Vector2(origin.x + c1, origin.z + s1); var vertices = new[] { new Vector3(origin.x, maxY, origin.z), // 0 new Vector3(o1.x, maxY, o1.y), // 1 new Vector3(o0.x, maxY, o0.y), // 2 new Vector3(origin.x, minY, origin.z), // 3 new Vector3(o1.x, minY, o1.y), // 4 new Vector3(o0.x, minY, o0.y), // 5 }; if (i == 0) { subMeshes[i].Polygons = CreateWedgeAssetPolygons(surfaceAssets, surfaceDescriptions); minY -= treadHeight; } else { subMeshes[i].Polygons = subMeshes[0].Polygons.ToArray(); } subMeshes[i].HalfEdges = (anglePerStep > 0) ? invertedWedgeHalfEdges.ToArray() : wedgeHalfEdges.ToArray(); subMeshes[i].Vertices = vertices; if (riserType != StairsRiserType.FillDown) { minY += stepHeight; } maxY += stepHeight; } } { var subMeshIndex = treadStart - cylinderSubMeshCount; var cylinderSurfaceAssets = new CSGSurfaceAsset[3] { surfaceAssets[0], surfaceAssets[1], surfaceAssets[2] }; var cylinderSurfaceDescriptions = new SurfaceDescription[outerSides + 2]; //surfaceDescriptions[0] cylinderSurfaceDescriptions[0] = surfaceDescriptions[0]; cylinderSurfaceDescriptions[1] = surfaceDescriptions[1]; for (int i = 0; i < outerSides; i++) { cylinderSurfaceDescriptions[i + 2] = surfaceDescriptions[2]; } GenerateCylinderSubMesh(subMeshes[subMeshIndex], outerDiameter, origin.y, origin.y + height, 0, outerSides, cylinderSurfaceAssets, cylinderSurfaceDescriptions); subMeshes[subMeshIndex].Operation = CSGOperationType.Intersecting; } if (haveInnerCyl) { var subMeshIndex = treadStart - 1; var cylinderSurfaceAssets = new CSGSurfaceAsset[3] { surfaceAssets[0], surfaceAssets[1], surfaceAssets[2] }; var cylinderSurfaceDescriptions = new SurfaceDescription[innerSides + 2]; //surfaceDescriptions[0] cylinderSurfaceDescriptions[0] = surfaceDescriptions[0]; cylinderSurfaceDescriptions[1] = surfaceDescriptions[1]; for (int i = 0; i < innerSides; i++) { cylinderSurfaceDescriptions[i + 2] = surfaceDescriptions[2]; } GenerateCylinderSubMesh(subMeshes[subMeshIndex], innerDiameter, origin.y, origin.y + height, 0, innerSides, cylinderSurfaceAssets, cylinderSurfaceDescriptions); subMeshes[subMeshIndex].Operation = CSGOperationType.Subtractive; } } if (haveTread) { var minY = origin.y + stepHeight - treadHeight; var maxY = origin.y + stepHeight; Vector2 i0, i1, o0, o1; float angle = startAngle; var c1 = Mathf.Sin(angle); var s1 = Mathf.Cos(angle); var startIndex = treadStart; for (int n = 0, i = startIndex; n < stepCount; n++, i++) { var c0 = c1; var s0 = s1; angle += anglePerStep; c1 = Mathf.Sin(angle); s1 = Mathf.Cos(angle); i0 = new Vector2(origin.x + (c0 * (stepInnerRadius)), origin.z + (s0 * (stepInnerRadius))); i1 = new Vector2(origin.x + (c1 * (stepInnerRadius)), origin.z + (s1 * (stepInnerRadius))); o0 = new Vector2(origin.x + (c0 * (stepOuterRadius + nosingWidth)), origin.z + (s0 * (stepOuterRadius + nosingWidth))); o1 = new Vector2(origin.x + (c1 * (stepOuterRadius + nosingWidth)), origin.z + (s1 * (stepOuterRadius + nosingWidth))); var noseSizeDeep = (new Vector2((c0 - c1), (s0 - s1)).normalized) * nosingDepth; i0 += noseSizeDeep; o0 += noseSizeDeep; var vertices = new[] { new Vector3(i1.x, maxY, i1.y), // 1 new Vector3(i0.x, maxY, i0.y), // 0 new Vector3(o0.x, maxY, o0.y), // 3 new Vector3(o1.x, maxY, o1.y), // 2 new Vector3(i1.x, minY, i1.y), // 5 new Vector3(i0.x, minY, i0.y), // 4 new Vector3(o0.x, minY, o0.y), // 7 new Vector3(o1.x, minY, o1.y), // 6 }; if (n == 0) { subMeshes[i].Polygons = CreateBoxAssetPolygons(surfaceAssets, surfaceDescriptions); } else { subMeshes[i].Polygons = subMeshes[startIndex].Polygons.ToArray(); } subMeshes[i].HalfEdges = (anglePerStep > 0) ? invertedBoxHalfEdges.ToArray() : boxHalfEdges.ToArray(); subMeshes[i].Vertices = vertices; minY += stepHeight; maxY += stepHeight; } } { var subMeshIndex = subMeshCount - cylinderSubMeshCount; var cylinderSurfaceAssets = new CSGSurfaceAsset[3] { surfaceAssets[0], surfaceAssets[1], surfaceAssets[2] }; var cylinderSurfaceDescriptions = new SurfaceDescription[outerSides + 2]; //surfaceDescriptions[0] cylinderSurfaceDescriptions[0] = surfaceDescriptions[0]; cylinderSurfaceDescriptions[1] = surfaceDescriptions[1]; for (int i = 0; i < outerSides; i++) { cylinderSurfaceDescriptions[i + 2] = surfaceDescriptions[2]; } GenerateCylinderSubMesh(subMeshes[subMeshIndex], outerDiameter + nosingWidth, origin.y, origin.y + height, 0, outerSides, cylinderSurfaceAssets, cylinderSurfaceDescriptions); subMeshes[subMeshIndex].Operation = CSGOperationType.Intersecting; } if (haveInnerCyl) { var subMeshIndex = subMeshCount - 1; var cylinderSurfaceAssets = new CSGSurfaceAsset[3] { surfaceAssets[0], surfaceAssets[1], surfaceAssets[2] }; var cylinderSurfaceDescriptions = new SurfaceDescription[innerSides + 2]; //surfaceDescriptions[0] cylinderSurfaceDescriptions[0] = surfaceDescriptions[0]; cylinderSurfaceDescriptions[1] = surfaceDescriptions[1]; for (int i = 0; i < innerSides; i++) { cylinderSurfaceDescriptions[i + 2] = surfaceDescriptions[2]; } GenerateCylinderSubMesh(subMeshes[subMeshIndex], innerDiameter - nosingWidth, origin.y, origin.y + height, 0, innerSides, cylinderSurfaceAssets, cylinderSurfaceDescriptions); subMeshes[subMeshIndex].Operation = CSGOperationType.Subtractive; } brushMeshAsset.SubMeshes = subMeshes; brushMeshAsset.CalculatePlanes(); brushMeshAsset.SetDirty(); return(true); }
/// <summary> /// Modeled after half sine wave /// </summary> public static float SineEaseInOut(float p) { return(0.5f * (1 - Math.Cos(p * Pi))); }
/// <summary> /// Modeled after half sine wave /// </summary> public static float EaseInOutSine(this float This) { return(0.5f * (1 - Math.Cos(This * Pi))); }