287 lines
13 KiB
C#
287 lines
13 KiB
C#
using UnityEngine;
|
|
using System.Collections.Generic;
|
|
|
|
namespace RTG
|
|
{
|
|
public static class PrimitiveFactory
|
|
{
|
|
public enum PolyBorderDirection
|
|
{
|
|
Inward = 0,
|
|
Outward
|
|
}
|
|
|
|
public static List<Vector2> Generate2DPolyBorderQuadsCW(List<Vector2> cwPolyPoints, List<Vector2> cwBorderPts, PolyBorderDirection borderDirection, bool isClosed)
|
|
{
|
|
if (cwPolyPoints.Count != cwBorderPts.Count) return new List<Vector2>();
|
|
|
|
int numQuads = cwPolyPoints.Count - 1;
|
|
if (isClosed && numQuads < 3) return new List<Vector2>();
|
|
if (!isClosed && numQuads < 2) return new List<Vector2>();
|
|
|
|
var quadPoints = new List<Vector2>(numQuads * 4);
|
|
|
|
if (borderDirection == PolyBorderDirection.Outward)
|
|
{
|
|
for (int ptIndex = 0; ptIndex < cwPolyPoints.Count - 1; ++ptIndex)
|
|
{
|
|
quadPoints.Add(cwPolyPoints[ptIndex]);
|
|
quadPoints.Add(cwBorderPts[ptIndex]);
|
|
quadPoints.Add(cwBorderPts[ptIndex + 1]);
|
|
quadPoints.Add(cwPolyPoints[ptIndex + 1]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int ptIndex = 0; ptIndex < cwPolyPoints.Count - 1; ++ptIndex)
|
|
{
|
|
quadPoints.Add(cwPolyPoints[ptIndex]);
|
|
quadPoints.Add(cwPolyPoints[ptIndex + 1]);
|
|
quadPoints.Add(cwBorderPts[ptIndex + 1]);
|
|
quadPoints.Add(cwBorderPts[ptIndex]);
|
|
}
|
|
}
|
|
|
|
return quadPoints;
|
|
}
|
|
|
|
public static float PolyBorderDirToSign(PolyBorderDirection borderDirection)
|
|
{
|
|
return borderDirection == PolyBorderDirection.Inward ? -1.0f : 1.0f;
|
|
}
|
|
|
|
public static List<Vector2> Generate2DPolyBorderPointsCW(List<Vector2> cwPolyPoints, PolyBorderDirection borderDirection, float borderThickness, bool isClosed)
|
|
{
|
|
int numSegments = cwPolyPoints.Count - 1;
|
|
if (isClosed && numSegments < 3) return new List<Vector2>();
|
|
if (!isClosed && numSegments < 2) return new List<Vector2>();
|
|
|
|
float borderWidth = borderThickness * PolyBorderDirToSign(borderDirection);
|
|
var outputPoints = new List<Vector2>(cwPolyPoints.Count);
|
|
|
|
if (isClosed)
|
|
{
|
|
Vector2 start0 = cwPolyPoints[0];
|
|
Vector2 end0 = cwPolyPoints[1];
|
|
Vector2 normal0 = (end0 - start0).GetNormal();
|
|
|
|
Vector2 start1 = cwPolyPoints[cwPolyPoints.Count - 2];
|
|
Vector2 end1 = cwPolyPoints[cwPolyPoints.Count - 1];
|
|
Vector2 normal1 = (end1 - start1).GetNormal();
|
|
|
|
float t;
|
|
Vector2 rayDir = (end1 - start1).normalized;
|
|
Vector2 rayOrigin = start1 + normal1 * borderWidth;
|
|
if (PlaneMath.Raycast2D(rayOrigin, rayDir, normal0, start0 + normal0 * borderWidth, out t)) outputPoints.Add(rayOrigin + rayDir * t);
|
|
else outputPoints.Add(start0);
|
|
|
|
for (int segmentIndex = 0; segmentIndex < numSegments - 1; ++segmentIndex)
|
|
{
|
|
Vector2 segmentStart = cwPolyPoints[segmentIndex];
|
|
Vector2 segmentEnd = cwPolyPoints[segmentIndex + 1];
|
|
Vector2 prevOutputPt = outputPoints[segmentIndex];
|
|
|
|
Vector2 nextSegmentEnd = cwPolyPoints[segmentIndex + 2];
|
|
Vector2 nextPlaneNormal = (nextSegmentEnd - segmentEnd).GetNormal();
|
|
Vector2 ptOnNextPlane = nextSegmentEnd + nextPlaneNormal * borderWidth;
|
|
|
|
rayDir = (segmentEnd - segmentStart).normalized;
|
|
if (PlaneMath.Raycast2D(prevOutputPt, rayDir, nextPlaneNormal, ptOnNextPlane, out t))
|
|
{
|
|
outputPoints.Add(prevOutputPt + rayDir * t);
|
|
}
|
|
}
|
|
|
|
outputPoints.Add(outputPoints[0]);
|
|
|
|
return outputPoints;
|
|
}
|
|
else
|
|
{
|
|
Vector2 start = cwPolyPoints[0];
|
|
Vector2 end = cwPolyPoints[1];
|
|
Vector2 normal = (end - start).GetNormal();
|
|
outputPoints.Add(start + normal * borderWidth);
|
|
|
|
float t;
|
|
for(int segmentIndex = 0; segmentIndex < numSegments - 1; ++segmentIndex)
|
|
{
|
|
Vector2 segmentStart = cwPolyPoints[segmentIndex];
|
|
Vector2 segmentEnd = cwPolyPoints[segmentIndex + 1];
|
|
Vector2 prevOutputPt = outputPoints[segmentIndex];
|
|
|
|
Vector2 nextSegmentEnd = cwPolyPoints[segmentIndex + 2];
|
|
Vector2 nextPlaneNormal = (nextSegmentEnd - segmentEnd).GetNormal();
|
|
Vector2 ptOnNextPlane = nextSegmentEnd + nextPlaneNormal * borderWidth;
|
|
|
|
Vector2 rayDir = (segmentEnd - segmentStart).normalized;
|
|
if (PlaneMath.Raycast2D(prevOutputPt, rayDir, nextPlaneNormal, ptOnNextPlane, out t))
|
|
{
|
|
outputPoints.Add(prevOutputPt + rayDir * t);
|
|
}
|
|
}
|
|
|
|
start = cwPolyPoints[cwPolyPoints.Count - 2];
|
|
end = cwPolyPoints[cwPolyPoints.Count - 1];
|
|
normal = (end - start).GetNormal();
|
|
outputPoints.Add(end + normal * borderWidth);
|
|
|
|
return outputPoints;
|
|
}
|
|
}
|
|
|
|
public static List<Vector2> Generate2DCircleBorderPointsCW(Vector2 circleCenter, float circleRadius, int numPoints)
|
|
{
|
|
numPoints = Mathf.Max(numPoints, 4);
|
|
var points = new List<Vector2>(numPoints);
|
|
|
|
float angleStep = 360.0f / (numPoints - 1);
|
|
for (int ptIndex = 0; ptIndex < numPoints; ++ptIndex)
|
|
{
|
|
float angle = angleStep * ptIndex * Mathf.Deg2Rad;
|
|
points.Add(circleCenter + new Vector2(Mathf.Sin(angle) * circleRadius, Mathf.Cos(angle) * circleRadius));
|
|
}
|
|
|
|
return points;
|
|
}
|
|
|
|
public static List<Vector3> Generate3DCircleBorderPoints(Vector3 circleCenter, float circleRadius, Vector3 circleRight, Vector3 circleUp, int numPoints)
|
|
{
|
|
numPoints = Mathf.Max(numPoints, 4);
|
|
var points = new List<Vector3>(numPoints);
|
|
|
|
float angleStep = 360.0f / (numPoints - 1);
|
|
for (int ptIndex = 0; ptIndex < numPoints; ++ptIndex)
|
|
{
|
|
float angle = angleStep * ptIndex * Mathf.Deg2Rad;
|
|
points.Add(circleCenter + circleRight * Mathf.Sin(angle) * circleRadius + circleUp * Mathf.Cos(angle) * circleRadius);
|
|
}
|
|
|
|
return points;
|
|
}
|
|
|
|
public static List<Vector3> GenerateSphereBorderPoints(Camera camera, Vector3 sphereCenter, float sphereRadius, int numPoints)
|
|
{
|
|
if (numPoints < 3) return new List<Vector3>();
|
|
|
|
Transform cameraTransform = camera.transform;
|
|
Plane cullPlane = new Plane(cameraTransform.forward, cameraTransform.position);
|
|
List<Vector3> borderPoints = Generate3DCircleBorderPoints(sphereCenter, sphereRadius, cameraTransform.right, cameraTransform.up, numPoints);
|
|
for (int ptIndex = 0; ptIndex < numPoints; ++ptIndex)
|
|
{
|
|
Vector3 borderPt = borderPoints[ptIndex];
|
|
Vector3 borderNormal = (borderPt - sphereCenter).normalized;
|
|
Vector3 cameraRay = (borderPt - cameraTransform.position).normalized;
|
|
if (camera.orthographic) cameraRay = cameraTransform.forward;
|
|
|
|
float dot = Vector3.Dot(borderNormal, cameraRay);
|
|
if (Mathf.Abs(dot) > 1e-5f)
|
|
{
|
|
float angle = MathEx.SafeAcos(dot) * Mathf.Rad2Deg;
|
|
Vector3 rotationAxis = Vector3.Cross(cameraRay, borderNormal).normalized;
|
|
|
|
Quaternion rotation = Quaternion.AngleAxis(90.0f - angle, rotationAxis);
|
|
borderNormal = (rotation * borderNormal).normalized;
|
|
borderPoints[ptIndex] = sphereCenter + borderNormal * sphereRadius;
|
|
|
|
// Note: Reject if any point lies behind the camera. In that case, we get
|
|
// invalid points when converted by the client in screen space.
|
|
if (cullPlane.GetDistanceToPoint(borderPoints[ptIndex]) < 0.0f) return new List<Vector3>();
|
|
}
|
|
}
|
|
|
|
return borderPoints;
|
|
}
|
|
|
|
public static List<Vector2> Generate2DArcBorderPoints(Vector2 arcOrigin, Vector2 arcStartPoint, float degreesFromStart, bool forceShortestArc, int numPoints)
|
|
{
|
|
if (numPoints < 2) return new List<Vector2>();
|
|
|
|
var arcPoints = new List<Vector2>(numPoints);
|
|
degreesFromStart %= 360.0f;
|
|
|
|
Vector2 toStartPt = (arcStartPoint - arcOrigin);
|
|
Quaternion rotation = Quaternion.AngleAxis(degreesFromStart, Vector3.forward);
|
|
float arcRadius = toStartPt.magnitude;
|
|
toStartPt.Normalize();
|
|
|
|
if (forceShortestArc) degreesFromStart = ArcMath.ConvertToSh2DArcAngle(arcOrigin, arcStartPoint, degreesFromStart);
|
|
|
|
float angleStep = degreesFromStart / (numPoints - 1);
|
|
for (int ptIndex = 0; ptIndex < numPoints; ++ptIndex)
|
|
{
|
|
float angle = ptIndex * angleStep;
|
|
rotation = Quaternion.AngleAxis(angle, Vector3.forward);
|
|
Vector2 rotatedAxis = (rotation * toStartPt).normalized;
|
|
arcPoints.Add(arcOrigin + rotatedAxis * arcRadius);
|
|
}
|
|
|
|
return arcPoints;
|
|
}
|
|
|
|
public static List<Vector2> ProjectArcPointsOnPoly2DBorder(Vector2 arcOrigin, List<Vector2> arcPoints, List<Vector2> clockwisePolyPoints)
|
|
{
|
|
if (arcPoints.Count < 2 || clockwisePolyPoints.Count < 3) return new List<Vector2>();
|
|
|
|
int numPolyPoints = clockwisePolyPoints.Count;
|
|
var projectedPoints = new List<Vector2>(arcPoints.Count);
|
|
foreach (var arcPoint in arcPoints)
|
|
{
|
|
for (int polyPointIndex = 0; polyPointIndex < numPolyPoints; ++polyPointIndex)
|
|
{
|
|
Vector2 firstPolyPoint = clockwisePolyPoints[polyPointIndex];
|
|
Vector2 secondPolyPoint = clockwisePolyPoints[(polyPointIndex + 1) % numPolyPoints];
|
|
|
|
Vector2 polySegment = secondPolyPoint - firstPolyPoint;
|
|
Plane2D segmentPlane = new Plane2D(polySegment.GetNormal(), firstPolyPoint);
|
|
|
|
float hitEnter;
|
|
Vector2 rayDir = (arcPoint - arcOrigin).normalized;
|
|
if (segmentPlane.Raycast(arcOrigin, rayDir, out hitEnter))
|
|
{
|
|
Vector2 hitPoint = arcOrigin + rayDir * hitEnter;
|
|
Vector2 toHitPoint = hitPoint - firstPolyPoint;
|
|
float dot = Vector2.Dot(toHitPoint, polySegment.normalized);
|
|
if (dot >= 0.0f && dot <= polySegment.magnitude)
|
|
{
|
|
projectedPoints.Add(hitPoint);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return projectedPoints;
|
|
}
|
|
|
|
public static List<Vector3> Generate3DArcBorderPoints(Vector3 arcOrigin, Vector3 arcStartPoint, Plane arcPlane, float degreesFromStart, bool forceShortestArc, int numPoints)
|
|
{
|
|
if (numPoints < 2) return new List<Vector3>();
|
|
|
|
var arcPoints = new List<Vector3>(numPoints);
|
|
degreesFromStart %= 360.0f;
|
|
|
|
arcOrigin = arcPlane.ProjectPoint(arcOrigin);
|
|
arcStartPoint = arcPlane.ProjectPoint(arcStartPoint);
|
|
|
|
Vector3 toStartPt = (arcStartPoint - arcOrigin);
|
|
Quaternion rotation = Quaternion.AngleAxis(degreesFromStart, arcPlane.normal);
|
|
float arcRadius = toStartPt.magnitude;
|
|
toStartPt.Normalize();
|
|
|
|
if (forceShortestArc) degreesFromStart = ArcMath.ConvertToSh3DArcAngle(arcOrigin, arcStartPoint, arcPlane.normal, degreesFromStart);
|
|
|
|
float angleStep = degreesFromStart / (numPoints - 1);
|
|
for (int ptIndex = 0; ptIndex < numPoints; ++ptIndex)
|
|
{
|
|
float angle = ptIndex * angleStep;
|
|
rotation = Quaternion.AngleAxis(angle, arcPlane.normal);
|
|
Vector3 rotatedAxis = (rotation * toStartPt).normalized;
|
|
arcPoints.Add(arcOrigin + rotatedAxis * arcRadius);
|
|
}
|
|
|
|
return arcPoints;
|
|
}
|
|
}
|
|
}
|