/// <summary> /// Computes and applies a convex shape description for this WrappedShape. /// </summary> /// <param name="center">Computed center of the shape before recentering.</param> public void UpdateConvexShapeInfo(out Vector3 center) { //Compute the volume distribution. var samples = CommonResources.GetVectorList(); if (samples.Capacity < InertiaHelper.SampleDirections.Length) { samples.Capacity = InertiaHelper.SampleDirections.Length; } samples.Count = InertiaHelper.SampleDirections.Length; for (int i = 0; i < InertiaHelper.SampleDirections.Length; ++i) { GetLocalExtremePoint(InertiaHelper.SampleDirections[i], out samples.Elements[i]); } var triangles = CommonResources.GetIntList(); ConvexHullHelper.GetConvexHull(samples, triangles); float volume; InertiaHelper.ComputeShapeDistribution(samples, triangles, out center, out volume, out volumeDistribution); Volume = volume; CommonResources.GiveBack(samples); CommonResources.GiveBack(triangles); //Now recenter the shape and compute the radii estimates. for (int i = 0; i < shapes.Count; i++) { shapes.WrappedList.Elements[i].Transform.Position -= center; } MinimumRadius = ComputeMinimumRadius(); MaximumRadius = ComputeMaximumRadius(); }
/// <summary> /// Computes a convex shape description for a TransformableShape and applies it. /// </summary> public void UpdateConvexShapeInfo() { //Compute the volume distribution. var samples = CommonResources.GetVectorList(); if (samples.Capacity < InertiaHelper.SampleDirections.Length) { samples.Capacity = InertiaHelper.SampleDirections.Length; } samples.Count = InertiaHelper.SampleDirections.Length; for (int i = 0; i < InertiaHelper.SampleDirections.Length; ++i) { shape.GetLocalExtremePointWithoutMargin(ref InertiaHelper.SampleDirections[i], out samples.Elements[i]); } var triangles = CommonResources.GetIntList(); ConvexHullHelper.GetConvexHull(samples, triangles); Fix64 volume; InertiaHelper.ComputeShapeDistribution(samples, triangles, out volume, out volumeDistribution); Volume = volume; //Estimate the minimum radius based on the surface mesh. MinimumRadius = InertiaHelper.ComputeMinimumRadius(samples, triangles, ref Toolbox.ZeroVector) + collisionMargin; MaximumRadius = ComputeMaximumRadius(); CommonResources.GiveBack(samples); CommonResources.GiveBack(triangles); }
/// <summary> /// Identifies the points on the surface of hull. /// </summary> /// <param name="points">List of points in the set.</param> /// <param name="outputSurfacePoints">Unique points on the surface of the convex hull.</param> public static void GetConvexHull(IList <Vector3> points, IList <Vector3> outputSurfacePoints) { var rawPoints = CommonResources.GetVectorList(); rawPoints.AddRange(points); GetConvexHull(rawPoints, outputSurfacePoints); CommonResources.GiveBack(rawPoints); }
///<summary> /// Computes the center and volume of a convex hull defined by a pointset. ///</summary> ///<param name="vertices">Point set defining the convex hull.</param> ///<param name="volume">Volume of the convex hull.</param> ///<returns>Center of the convex hull.</returns> public static Vector3 ComputeCenter(IList <Vector3> vertices, out float volume) { var localSurfaceVertices = CommonResources.GetVectorList(); var surfaceTriangles = CommonResources.GetIntList(); Vector3 toReturn = ComputeCenter(vertices, out volume, surfaceTriangles, localSurfaceVertices); CommonResources.GiveBack(localSurfaceVertices); CommonResources.GiveBack(surfaceTriangles); return(toReturn); }
///<summary> /// Computes the volume and volume distribution of a shape based on a given center. ///</summary> ///<param name="shape">Shape to compute the volume information of.</param> ///<param name="center">Location to use as the center of the shape when computing the volume distribution.</param> ///<param name="volume">Volume of the shape.</param> ///<returns>Volume distribution of the shape.</returns> public static Matrix3x3 ComputeVolumeDistribution(ConvexShape shape, ref Vector3 center, out float volume) { var pointContributions = CommonResources.GetVectorList(); GetPoints(shape, out volume, pointContributions); Matrix3x3 volumeDistribution = ComputeVolumeDistribution(pointContributions, ref center); CommonResources.GiveBack(pointContributions); return(volumeDistribution); }
///<summary> /// Computes the center and volume of a convex shape. ///</summary> ///<param name="shape">Shape to compute the center of.</param> ///<param name="volume">Volume of the shape.</param> ///<returns>Center of the shape.</returns> public static Vector3 ComputeCenter(ConvexShape shape, out float volume) { var pointContributions = CommonResources.GetVectorList(); GetPoints(shape, out volume, pointContributions); Vector3 center = AveragePoints(pointContributions); CommonResources.GiveBack(pointContributions); MathChecker.Validate(center); return(center); }
/// <summary> /// Computes the volume distribution of the shape as well as its volume. /// The volume distribution can be used to compute inertia tensors when /// paired with mass and other tuning factors. /// </summary> /// <param name="volume">Volume of the shape.</param> /// <returns>Volume distribution of the shape.</returns> public override Matrix3x3 ComputeVolumeDistribution(out float volume) { var surfaceTriangles = CommonResources.GetIntList(); var surfaceVertices = CommonResources.GetVectorList(); ComputeCenter(out volume, surfaceTriangles, surfaceVertices); Matrix3x3 toReturn = ComputeVolumeDistribution(volume, surfaceTriangles); CommonResources.GiveBack(surfaceTriangles); CommonResources.GiveBack(surfaceVertices); return(toReturn); }
/// <summary> /// Removes redundant points. Two points are redundant if they occupy the same hash grid cell. /// </summary> /// <param name="points">List of points to prune.</param> /// <param name="cellSize">Size of cells to determine redundancy.</param> public static void RemoveRedundantPoints(IList <Vector3> points, double cellSize) { var rawPoints = CommonResources.GetVectorList(); rawPoints.AddRange(points); RemoveRedundantPoints(rawPoints, cellSize); points.Clear(); for (int i = 0; i < rawPoints.Count; ++i) { points.Add(rawPoints.Elements[i]); } CommonResources.GiveBack(rawPoints); }
/// <summary> /// Identifies the points on the surface of hull. /// </summary> /// <param name="points">List of points in the set.</param> /// <param name="outputTriangleIndices">List of indices into the input point set composing the triangulated surface of the convex hull. /// Each group of 3 indices represents a triangle on the surface of the hull.</param> /// <param name="outputSurfacePoints">Unique points on the surface of the convex hull.</param> public static void GetConvexHull(IList <Vector3> points, IList <int> outputTriangleIndices, IList <Vector3> outputSurfacePoints) { var rawPoints = CommonResources.GetVectorList(); var rawIndices = CommonResources.GetIntList(); rawPoints.AddRange(points); GetConvexHull(rawPoints, rawIndices, outputSurfacePoints); CommonResources.GiveBack(rawPoints); for (int i = 0; i < rawIndices.Count; i++) { outputTriangleIndices.Add(rawIndices[i]); } CommonResources.GiveBack(rawIndices); }
///<summary> /// Constructs a new convex hull shape. /// The point set will be recentered on the local origin. ///</summary> ///<param name="vertices">Point set to use to construct the convex hull.</param> /// <param name="center">Computed center of the convex hull shape prior to recentering.</param> ///<exception cref="ArgumentException">Thrown when the point set is empty.</exception> public ConvexHullShape(IList <Vector3> vertices, out Vector3 center) { if (vertices.Count == 0) { throw new ArgumentException("Vertices list used to create a ConvexHullShape cannot be empty."); } var surfaceVertices = CommonResources.GetVectorList(); center = ComputeCenter(vertices, surfaceVertices); this.vertices = new RawList <Vector3>(surfaceVertices); CommonResources.GiveBack(surfaceVertices); OnShapeChanged(); }
/// <summary> /// Computes and applies a convex shape description for this MinkowskiSumShape. /// </summary> /// <returns>Description required to define a convex shape.</returns> public void UpdateConvexShapeInfo() { //Compute the volume distribution. RawList <Vector3> samples = CommonResources.GetVectorList(); if (samples.Capacity < InertiaHelper.SampleDirections.Length) { samples.Capacity = InertiaHelper.SampleDirections.Length; } samples.Count = InertiaHelper.SampleDirections.Length; for (int i = 0; i < InertiaHelper.SampleDirections.Length; ++i) { GetLocalExtremePoint(InertiaHelper.SampleDirections[i], out samples.Elements[i]); } RawList <int> triangles = CommonResources.GetIntList(); ConvexHullHelper.GetConvexHull(samples, triangles); float volume; Vector3 center; InertiaHelper.ComputeShapeDistribution(samples, triangles, out center, out volume, out volumeDistribution); Volume = volume; //Recenter the shape. localOffset = -center; CommonResources.GiveBack(samples); CommonResources.GiveBack(triangles); //Compute the radii. float minRadius = 0, maxRadius = 0; for (int i = 0; i < Shapes.Count; i++) { minRadius += Shapes.WrappedList.Elements[i].CollisionShape.MinimumRadius; maxRadius += Shapes.WrappedList.Elements[i].CollisionShape.MaximumRadius; } MinimumRadius = minRadius + collisionMargin; MaximumRadius = maxRadius + collisionMargin; }
///<summary> /// Constructs a new convex hull shape. /// The point set will be recentered on the local origin. ///</summary> ///<param name="vertices">Point set to use to construct the convex hull.</param> /// <param name="center">Computed center of the convex hull shape prior to recentering.</param> ///<exception cref="ArgumentException">Thrown when the point set is empty.</exception> public ConvexHullShape(IList <Vector3> vertices, out Vector3 center) { if (vertices.Count == 0) { throw new ArgumentException("Vertices list used to create a ConvexHullShape cannot be empty."); } var surfaceVertices = CommonResources.GetVectorList(); var hullTriangleIndices = CommonResources.GetIntList(); UpdateConvexShapeInfo(ComputeDescription(vertices, collisionMargin, out center, hullTriangleIndices, surfaceVertices)); this.vertices = surfaceVertices.ToArray(); CommonResources.GiveBack(hullTriangleIndices); CommonResources.GiveBack(surfaceVertices); unexpandedMaximumRadius = MaximumRadius - collisionMargin; unexpandedMinimumRadius = MinimumRadius - collisionMargin; }
///<summary> /// Computes the center, volume, and surface triangles of a convex hull defined by a point set. ///</summary> ///<param name="vertices">Point set defining the convex hull.</param> ///<param name="volume">Volume of the convex hull.</param> ///<param name="outputSurfaceTriangles">Indices of surface triangles of the convex hull.</param> ///<param name="outputLocalSurfaceVertices">Local positions of vertices on the convex hull.</param> ///<returns>Center of the convex hull.</returns> public static Vector3 ComputeCenter(IList <Vector3> vertices, out float volume, IList <int> outputSurfaceTriangles, IList <Vector3> outputLocalSurfaceVertices) { Vector3 centroid = Toolbox.ZeroVector; for (int k = 0; k < vertices.Count; k++) { centroid += vertices[k]; } centroid /= vertices.Count; //Toolbox.GetConvexHull(vertices, outputSurfaceTriangles, outputLocalSurfaceVertices); ConvexHullHelper.GetConvexHull(vertices, outputSurfaceTriangles, outputLocalSurfaceVertices); volume = 0; var volumes = CommonResources.GetFloatList(); var centroids = CommonResources.GetVectorList(); for (int k = 0; k < outputSurfaceTriangles.Count; k += 3) { volumes.Add(Vector3.Dot( Vector3.Cross(vertices[outputSurfaceTriangles[k + 1]] - vertices[outputSurfaceTriangles[k]], vertices[outputSurfaceTriangles[k + 2]] - vertices[outputSurfaceTriangles[k]]), centroid - vertices[outputSurfaceTriangles[k]])); volume += volumes[k / 3]; centroids.Add((vertices[outputSurfaceTriangles[k]] + vertices[outputSurfaceTriangles[k + 1]] + vertices[outputSurfaceTriangles[k + 2]] + centroid) / 4); } Vector3 center = Toolbox.ZeroVector; for (int k = 0; k < centroids.Count; k++) { center += centroids[k] * (volumes[k] / volume); } volume /= 6; for (int k = 0; k < outputLocalSurfaceVertices.Count; k++) { outputLocalSurfaceVertices[k] -= center; } CommonResources.GiveBack(centroids); CommonResources.GiveBack(volumes); return(center); }
/// <summary> /// Recalculates the bounding box of the fluid based on its depth, surface normal, and surface triangles. /// </summary> public void RecalculateBoundingBox() { var points = CommonResources.GetVectorList(); foreach (var tri in SurfaceTriangles) { points.Add(tri[0]); points.Add(tri[1]); points.Add(tri[2]); points.Add(tri[0] - upVector * MaxDepth); points.Add(tri[1] - upVector * MaxDepth); points.Add(tri[2] - upVector * MaxDepth); } boundingBox = BoundingBox.CreateFromPoints(points); CommonResources.GiveBack(points); //Compute the transforms used to pull objects into fluid local space. Quaternion.GetQuaternionBetweenNormalizedVectors(ref Toolbox.UpVector, ref upVector, out surfaceTransform.Orientation); Matrix3f.FromQuaternion(ref surfaceTransform.Orientation, out toSurfaceRotationMatrix); surfaceTransform.Position = surfaceTriangles[0][0]; }
/// <summary> /// Recenters the triangle data and computes the volume distribution. /// </summary> /// <param name="data">Mesh data to analyze.</param> /// <returns>Computed center, volume, and volume distribution.</returns> private ShapeDistributionInformation ComputeVolumeDistribution(TransformableMeshData data) { //Compute the surface vertices of the shape. ShapeDistributionInformation shapeInformation; if (solidity == MobileMeshSolidity.Solid) { //The following inertia tensor calculation assumes a closed mesh. var transformedVertices = CommonResources.GetVectorList(); if (transformedVertices.Capacity < data.vertices.Length) { transformedVertices.Capacity = data.vertices.Length; } transformedVertices.Count = data.vertices.Length; for (int i = 0; i < data.vertices.Length; ++i) { data.GetVertexPosition(i, out transformedVertices.Elements[i]); } InertiaHelper.ComputeShapeDistribution(transformedVertices, data.indices, out shapeInformation.Center, out shapeInformation.Volume, out shapeInformation.VolumeDistribution); CommonResources.GiveBack(transformedVertices); if (shapeInformation.Volume > F64.C0) { return(shapeInformation); } throw new ArgumentException("A solid mesh must have volume."); } shapeInformation.Center = new Vector3(); shapeInformation.VolumeDistribution = new Matrix3x3(); Fix64 totalWeight = F64.C0; for (int i = 0; i < data.indices.Length; i += 3) { //Compute the center contribution. Vector3 vA, vB, vC; data.GetTriangle(i, out vA, out vB, out vC); Vector3 vAvB; Vector3 vAvC; Vector3.Subtract(ref vB, ref vA, out vAvB); Vector3.Subtract(ref vC, ref vA, out vAvC); Vector3 cross; Vector3.Cross(ref vAvB, ref vAvC, out cross); Fix64 weight = cross.Length(); totalWeight += weight; Fix64 perVertexWeight = weight * F64.OneThird; shapeInformation.Center += perVertexWeight * (vA + vB + vC); //Compute the inertia contribution of this triangle. //Approximate it using pointmasses positioned at the triangle vertices. //(There exists a direct solution, but this approximation will do plenty fine.) Matrix3x3 aContribution, bContribution, cContribution; InertiaHelper.GetPointContribution(perVertexWeight, ref Toolbox.ZeroVector, ref vA, out aContribution); InertiaHelper.GetPointContribution(perVertexWeight, ref Toolbox.ZeroVector, ref vB, out bContribution); InertiaHelper.GetPointContribution(perVertexWeight, ref Toolbox.ZeroVector, ref vC, out cContribution); Matrix3x3.Add(ref aContribution, ref shapeInformation.VolumeDistribution, out shapeInformation.VolumeDistribution); Matrix3x3.Add(ref bContribution, ref shapeInformation.VolumeDistribution, out shapeInformation.VolumeDistribution); Matrix3x3.Add(ref cContribution, ref shapeInformation.VolumeDistribution, out shapeInformation.VolumeDistribution); } shapeInformation.Center /= totalWeight; //The extra factor of 2 is used because the cross product length was twice the actual area. Matrix3x3.Multiply(ref shapeInformation.VolumeDistribution, F64.C1 / (F64.C2 * totalWeight), out shapeInformation.VolumeDistribution); //Move the inertia tensor into position according to the center. Matrix3x3 additionalInertia; InertiaHelper.GetPointContribution(F64.C0p5, ref Toolbox.ZeroVector, ref shapeInformation.Center, out additionalInertia); Matrix3x3.Subtract(ref shapeInformation.VolumeDistribution, ref additionalInertia, out shapeInformation.VolumeDistribution); shapeInformation.Volume = F64.C0; return(shapeInformation); }