Everything I know about vector projection so far is from the internet so I am a little confused. First of all I assume when we project the vertices of a polygon on to an axis, we get the scalar projection as opposed to the vector projection. And the formula for this is (A.B) / |A|, now in this tutorial, they simply use the dot product to project on to the axis. Is this because the vector and the axis are perpendicular or something? Nonetheless I had a go at writing a crude version in C#, but it doesn't seem to return correct results
Rectangle r1 = new Rectangle(300, 200, 50, 50);
Rectangle r2 = new Rectangle(340, 240, 50, 50);
bool areColliding(Rectangle r1, Rectangle r2)
{
/* Using clockwise labelling
*
* B*
* . .
* . .
* A * C*
* . .
* . .
* D*
*
*/
//Calculate vectors and normals of Rectangle 1
Point r1A = r1.Location;
Point r1B = new Point(r1.Location.X + r1.Width, r1.Location.Y);
Point r1C = new Point(r1.Location.X, r1.Location.Y + r1.Height);
Point r1D = new Point(r1.Location.X + r1.Width, r1.Location.Y + r1.Height);
Vector2 r1AB = new Vector2(r1B.X - r1A.X, r1B.Y - r1A.Y);
Vector2 r1BC = new Vector2(r1C.X - r1B.X, r1C.Y - r1B.Y);
Vector2 r1CD = new Vector2(r1D.X - r1C.X, r1D.Y - r1C.Y);
Vector2 r1DA = new Vector2(r1A.X - r1D.X, r1A.Y - r1D.Y);
Vector2 r1AB_Normal = getNormal(r1AB);
Vector2 r1BC_Normal = getNormal(r1BC);
Vector2 r1CD_Normal = getNormal(r1CD);
Vector2 r1DA_Normal = getNormal(r1DA);
Point[] r1Points = {r1A, r1B, r1C, r1D};
Vector2[] Axes1 = { r1AB_Normal, r1BC_Normal, r1CD_Normal, r1DA_Normal };
//Calculate vectors and normals of Rectangle 2
Point r2A = r2.Location;
Point r2B = new Point(r2.Location.X + r2.Width, r2.Location.Y);
Point r2C = new Point(r2.Location.X, r2.Location.Y + r2.Height);
Point r2D = new Point(r2.Location.X + r2.Width, r2.Location.Y + r2.Height);
Vector2 r2AB = new Vector2(r2B.X - r2A.X, r2B.Y - r2A.Y);
Vector2 r2BC = new Vector2(r2C.X - r2B.X, r2C.Y - r2B.Y);
Vector2 r2CD = new Vector2(r2D.X - r2C.X, r2D.Y - r2C.Y);
Vector2 r2DA = new Vector2(r2A.X - r2D.X, r2A.Y - r2D.Y);
Vector2 r2AB_Normal = getNormal(r2AB);
Vector2 r2BC_Normal = getNormal(r2BC);
Vector2 r2CD_Normal = getNormal(r2CD);
Vector2 r2DA_Normal = getNormal(r2DA);
Point[] r2Points = { r2A, r2B, r2C, r2D };
Vector2[] Axes2 = { r2AB_Normal, r2BC_Normal, r2CD_Normal, r2DA_Normal };
//Start projecting each vertex on to each axis
for (int i = 0; i < Axes1.Length; i++)
{
float r1Min = Vector2.Dot(Axes1[i], new Vector2(r1Points[0].X, r1Points[0].Y));
float r1Max = float.NaN;
for (int p = 1; p < r1Points.Length; p++)
{
float dot = Vector2.Dot(Axes1[i], new Vector2(r1Points[p].X, r1Points[p].Y));
if (dot < r1Min)
{
r1Min = dot;
}
}
float r2Min = Vector2.Dot(Axes1[i], new Vector2(r1Points[0].X, r1Points[0].Y));
float r2Max = float.NaN;
for (int p = 1; p < r2Points.Length; p++)
{
float dot = Vector2.Dot(Axes1[i], new Vector2(r1Points[p].X, r1Points[p].Y));
if (dot < r2Min)
{
r2Min = dot;
}
}
if (r1Min < r2Max)
{
return true;
}
}
for (int i = 0; i < Axes2.Length; i++)
{
float r1Min = Vector2.Dot(Axes1[i], new Vector2(r1Points[0].X, r1Points[0].Y));
float r1Max = float.NaN;
for (int p = 1; p < r1Points.Length; p++)
{
float dot = Vector2.Dot(Axes1[i], new Vector2(r1Points[p].X, r1Points[p].Y));
if (dot < r1Min)
{
r1Min = dot;
}
}
float r2Min = Vector2.Dot(Axes1[i], new Vector2(r1Points[0].X, r1Points[0].Y));
float r2Max = float.NaN;
for (int p = 1; p < r2Points.Length; p++)
{
float dot = Vector2.Dot(Axes1[i], new Vector2(r1Points[p].X, r1Points[p].Y));
if (dot < r2Min)
{
r2Min = dot;
}
}
if (r1Min < r2Max)
{
return true;
}
}
return false;
}
Vector2 getNormal(Vector2 v)
{
return new Vector2(-v.Y, v.X);
}
Related
I use the following code to generate a Cube as a single mesh. My purpose is to generate a sphere from it by normalizing as I have shown in the commented line (I just have to do that to all those statements in the following lines). The problem here is that the mesh changes from a cube to a flat plane as I keep increasing the resolution (parameter given as public int resolution).
(This code was inspired by this video https://youtu.be/QN39W020LqU . But I am using the technique in my own way as given by the following code, so that I can generate a single mesh instead of a combination of 6 meshes, this is required for my work)
[code=CSharp]
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Sc_Planet : MonoBehaviour
{
[Range(2, 512)]
public int resolution = 2;
[Range(2, 256)]
public int radius = 10;
MeshFilter meshFilter;
void OnValidate()
{
Initialize();
}
void Initialize()
{
if (meshFilter == null)
{
GameObject meshObj = new GameObject("mesh_Planet");
meshObj.transform.parent = transform;
meshObj.AddComponent<MeshRenderer>().sharedMaterial = new Material(Shader.Find("Standard"));
meshFilter = meshObj.AddComponent<MeshFilter>();
meshFilter.sharedMesh = new Mesh();
}
int xmax = resolution + 1;
int ymax = resolution + 1;
float dx = 1.0f / resolution;
float dy = 1.0f / resolution;
Vector3[] vertsTop = new Vector3[xmax * ymax];
Vector3[] vertsRight = new Vector3[xmax * ymax];
Vector3[] vertsFront = new Vector3[xmax * ymax];
Vector3[] vertsBottom = new Vector3[xmax * ymax];
Vector3[] vertsLeft = new Vector3[xmax * ymax];
Vector3[] vertsBack = new Vector3[xmax * ymax];
for (int y = 0; y < ymax; y++)
{
for (int x = 0; x < xmax; x++)
{
float px = dx * x - 0.5f;
float py = dy * y - 0.5f;
int t = x + y * xmax;
//vertsTop[t] = new Vector3(py, 0.5f, px).normalized * radius;
vertsTop[t] = new Vector3(py, 0.5f, px);
vertsRight[t] = new Vector3(px, py, 0.5f);
vertsFront[t] = new Vector3(0.5f, px, py);
vertsBottom[t] = new Vector3(px, -0.5f, py);
vertsLeft[t] = new Vector3(py, px, -0.5f);
vertsBack[t] = new Vector3(-0.5f, py, px);
}
}
List<int> trianglesList = new List<int>();
for (int y = 0; y < ymax - 1; ++y)
{
for (int x = 0; x < xmax; ++x)
{
if (x % xmax != xmax - 1)
{
int f = x + y * xmax;
trianglesList.Add(f);
trianglesList.Add(f + 1);
trianglesList.Add(f + 1 + xmax);
trianglesList.Add(f);
trianglesList.Add(f + 1 + xmax);
trianglesList.Add(f + xmax);
}
}
}
List<Vector3> verts = new List<Vector3>();
Dictionary<Vector3, int> vdict = new Dictionary<Vector3, int>();
List<int> triangles = new List<int>();
int nextIndex = 0;
void addFace(Vector3 [] in_verts, List<int> in_triangles)
{
for(int i = 0; i < in_verts.Length; ++i)
{
if (!vdict.ContainsKey(in_verts[i]))
{
vdict.Add(in_verts[i], nextIndex);
verts.Add(in_verts[i]);
++nextIndex;
}
}
for(int i = 0; i < in_triangles.Count; ++i)
{
triangles.Add(vdict[in_verts[in_triangles[i]]]);
}
}
addFace(vertsTop, trianglesList);
addFace(vertsRight, trianglesList);
addFace(vertsFront, trianglesList);
addFace(vertsBottom, trianglesList);
addFace(vertsLeft, trianglesList);
addFace(vertsBack, trianglesList);
var mesh = meshFilter.sharedMesh;
mesh.Clear();
mesh.vertices = verts.ToArray();
mesh.triangles = triangles.ToArray();
mesh.RecalculateNormals();
}
}
[/code]
This code works in Blender (I used python to script it on Blender and it works very well for any resolution).
The only problem is that when I use this in Unity, the meshes become weird as I have shown in the images I have attached below.
At Resolution = 96 :
At Resolution = 122 :
At Resolution = 182 :
At Resolution = 344:
Why is this happening?
How should I correct it?
(I have also posted this in unity forums: Why cube mesh becomes a plane when in high resolution?)
Ok I found the answer. This is exceeding the limit of vertices on unity api for 16-bit based meshes. I had to change it to a 32-bit indexed mesh to correct it.
Details are in this docuemntaiton page : https://docs.unity3d.com/ScriptReference/Rendering.IndexFormat.html?_ga=2.9556401.501737799.1635227368-67181881.1629608252
I just had to add the code :
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
That was it.
My Winforms app gets a CSV file with XYZ coordinates given from a 3D camera. With these coordinates I need to calculate the object's volume in cubic decimeters (dm3).
I am overwhelmed and not a math expert. I expected to be a library or an algorithm to already do that but the only things I found are in C++ like the PCL library or they use Unity. Is there a simple/clean way for a geometry ignorant guy like me to get the volume of an object with the XYZ coordinates?
UPDATE
This is the fragment of the code I have this far:
public class Volume
{
//These are only part of the coordinates in the CSV file. There are more than 69.000 lines
Vector3[] vectors = new Vector3[8]
{
new Vector3 {X=-139,Y=-109,Z=285},
new Vector3 {X=-138,Y=-109,Z=286},
new Vector3 {X=-136,Y=-109,Z=286},
new Vector3 {X=-135,Y=-109,Z=286},
new Vector3 {X=-133,Y=-109,Z=286},
new Vector3 {X=-132,Y=-109,Z=286},
new Vector3 {X=-130,Y=-109,Z=286},
new Vector3 {X=-129,Y=-109,Z=286}
};
public double VolumeOfMesh()
{
Mesh _mesh = new Mesh();
double volume = 0.0;
_mesh.Vertices = vectors; //Should the vectors be organized by proximity to create the triangles?
_mesh.Triangles = null; //How do I calculate the triangles?
Vector3[] vertices = _mesh.Vertices;
int[] triangles = _mesh.Triangles;
for (int i = 0; i < _mesh.Triangles.Length; i += 3)
{
Vector3 p1 = vertices[triangles[i + 0]];
Vector3 p2 = vertices[triangles[i + 1]];
Vector3 p3 = vertices[triangles[i + 2]];
volume += SignedVolumeOfTriangle(p1, p2, p3);
}
return Math.Abs(volume);
}
private double SignedVolumeOfTriangle(Vector3 p1, Vector3 p2, Vector3 p3)
{
var v321 = p3.X * p2.Y * p1.Z;
var v231 = p2.X * p3.Y * p1.Z;
var v312 = p3.X * p1.Y * p2.Z;
var v132 = p1.X * p3.Y * p2.Z;
var v213 = p2.X * p1.Y * p3.Z;
var v123 = p1.X * p2.Y * p3.Z;
return (1.0 / 6.0) * (-v321 + v231 + v312 - v132 - v213 + v123);
}
}
Should the vectors array be ordered by proximity? How do I populate the Triangles property?
Any advice or guidance will be welcome.
This is how I did this using .STL files which arrange points into triangular faces. In your case, you need to somehow describe which points (nodes) combine to define faces, and to make sure the faces form a closed watertight solid.
The idea is the each three points ABC that form a face together with the origin form a solid of volume
where · is the vector dot product, and × the vector cross product.
It turns out that when you add up all the volumes, some will be positive (facing away from origin) and some negative (facing towards origin). In the end, the sum will equal the enclosed volume of the object.
Here is a sample of the C# code I am using to get solid object properties from a mesh. Remember a mesh is a collection of points called Nodes and a collection of triangles called Faces defined by the three index values of the points in the vertices.
public struct Face3
{
public Face3(int indexA, int indexB, int indexC)
{
this.IndexA = indexA;
this.IndexB = indexB;
this.IndexC = indexC;
}
public readonly int IndexA, IndexB, IndexC;
}
public class Mesh3
{
public Mesh3(int n_nodes, int n_elements)
{
this.Nodes = new Vector3[n_nodes];
this.Faces = new Face3[n_elements];
}
public Mesh3(Vector3[] nodes, Face3[] faces)
{
this.Nodes = nodes;
this.Faces = faces;
}
public Vector3[] Nodes { get; }
public Face3[] Faces { get; }
public void CalcRigidBodyProperties(double density)
{
double sum_vol = 0;
Vector3 sum_cg = Vector3.Zero;
for (int i = 0; i < Faces.Length; i++)
{
var face = this.Faces[i];
Vector3 a = this.Nodes[face.IndexA];
Vector3 b = this.Nodes[face.IndexB];
Vector3 c = this.Nodes[face.IndexC];
double face_vol = Vector3.Dot(a, Vector3.Cross(b,c))/6;
sum_vol += face_vol;
Vector3 face_cg = (a+b+c)/4;
sum_cg += face_vol*face_cg;
}
// scale volume with density for mass
var mass = density*sum_vol;
// find center of mass by dividing by total volume
var cg = sum_cg / sum_vol;
...
}
public static Mesh3 FromStl(string filename, double scale = 1)
{
// Imports a binary STL file
// Code Taken From:
// https://sukhbinder.wordpress.com/2013/12/10/new-fortran-stl-binary-file-reader/
// Aug 27, 2019
var fs = File.OpenRead(filename);
var stl = new BinaryReader(fs);
var header = new string(stl.ReadChars(80));
var n_elems = stl.ReadInt32();
var nodes = new List<Vector3>();
var faces = new List<Face3>();
bool FindIndexOf(Vector3 node, out int index)
{
for (index = 0; index < nodes.Count; index++)
{
if (nodes[index].Equals(node, TrigonometricPrecision))
{
return true;
}
}
index = -1;
return false;
}
for (int i = 0; i < n_elems; i++)
{
var normal = new Vector3(
stl.ReadSingle(),
stl.ReadSingle(),
stl.ReadSingle());
var a = new Vector3(
scale*stl.ReadSingle(),
scale*stl.ReadSingle(),
scale*stl.ReadSingle());
var b = new Vector3(
scale*stl.ReadSingle(),
scale*stl.ReadSingle(),
scale*stl.ReadSingle());
var c = new Vector3(
scale*stl.ReadSingle(),
scale*stl.ReadSingle(),
scale*stl.ReadSingle());
// burn two bytes
var temp = stl.ReadBytes(2);
// get index of next point, and add point to list of nodes
int index_a = nodes.Count;
nodes.Add(a);
int index_b = nodes.Count;
nodes.Add(b);
int index_c = nodes.Count;
nodes.Add(c);
// add face from the three index values
faces.Add(new Face3( index_a, index_b, index_c ));
}
stl.Close();
return new Mesh3(nodes.ToArray(), faces.ToArray());
}
}
as a test case I used just one triangle defined as follows:
furthermore, I verified the result with a more complex shape (an STL file consisting of many triangles) by comparing the above calculation to that produced by a commercial CAD package.
I am using Unity3D and I have a polygon (Vector2 array) as well as points to check.
For the last few days I was searching for solutions including pnpoly and other algorithms. The problem is I have an inaccuracy of up to 0.001f because I project 3D Faces onto a 2D plane by multiplying with a Quaternion right after using TransformPoint (to get the world position of a mesh-vertex).
I don't know anything about the polygons because they are formed from a number of mesh-triangles - they can have any shape.
How could I deal with this extreme inaccuracy and find all points that are inside or on the boundaries of the polygon?
public static bool IsInsidePolygon(Vector2[] vertices, Vector2 checkPoint)
{
float[] vertX = new float[vertices.Length];
float[] vertY = new float[vertices.Length];
for (int i = 0; i < vertices.Length; i++)
{
vertX[i] = vertices[i].x;
vertY[i] = vertices[i].y;
}
return IsInsidePolygon(vertices.Length, vertX, vertY, checkPoint.x, checkPoint.y);
}
public static bool IsInsidePolygon3(int nvert, float[] vertx, float[] verty, float testx, float testy)
{
int i, j = 0;
bool c = false;
for (i = 0, j = nvert - 1; i < nvert; j = i++)
{
if (((verty[i] > testy) != (verty[j] > testy)) &&
(testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i]))
c = !c;
}
return c;
}
The solution was to find the Closest Distance to the Polygon and return true if the distance is within the margin, here is the whole code:
'''
public static float DistancePointLine2D(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
return (ProjectPointLine2D(point, lineStart, lineEnd) - point).magnitude;
}
public static Vector2 ProjectPointLine2D(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
Vector2 rhs = point - lineStart;
Vector2 vector2 = lineEnd - lineStart;
float magnitude = vector2.magnitude;
Vector2 lhs = vector2;
if (magnitude > 1E-06f)
{
lhs = (Vector2)(lhs / magnitude);
}
float num2 = Mathf.Clamp(Vector2.Dot(lhs, rhs), 0f, magnitude);
return (lineStart + ((Vector2)(lhs * num2)));
}
public static float ClosestDistanceToPolygon(Vector2[] verts, Vector2 point)
{
int nvert = verts.Length;
int i, j = 0;
float minDistance = Mathf.Infinity;
for (i = 0, j = nvert - 1; i < nvert; j = i++)
{
float distance = DistancePointLine2D(point, verts[i], verts[j]);
minDistance = Mathf.Min(minDistance, distance);
}
return minDistance;
}
public static bool IsInsidePolygon(Vector2[] vertices, Vector2 checkPoint, float margin = 0.01f)
{
if(ClosestDistanceToPolygon(vertices, checkPoint) < margin)
{
return true;
}
float[] vertX = new float[vertices.Length];
float[] vertY = new float[vertices.Length];
for (int i = 0; i < vertices.Length; i++)
{
vertX[i] = vertices[i].x;
vertY[i] = vertices[i].y;
}
return IsInsidePolygon(vertices.Length, vertX, vertY, checkPoint.x, checkPoint.y);
}
public static bool IsInsidePolygon(int nvert, float[] vertx, float[] verty, float testx, float testy)
{
bool c = false;
int i, j = 0;
for (i = 0, j = nvert - 1; i < nvert; j = i++)
{
if ((((verty[i] <= testy) && (testy < verty[j])) ||
((verty[j] <= testy) && (testy < verty[i]))) &&
(testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i]))
c = !c;
}
return c;
}
'''
I currently have a "CreateMesh" script that can be put as a component of an object with a Mesh Renderer, and a Mesh Filter, and a 2D mesh is created with a polygon collider in the dimensions of the mesh given a "MeshType" variable is set to either "tri" or "box" (for a triangle and rectangle mesh respectively.) I want to also add the ability to create a circular mesh however from some research I've realised this isn't as simple as I first thought. However I'm yet to find anything that's helping.
This is the code I have for the box and triangle meshes:
public float width = 5f;
public float height = 5f;
public string meshType;
public PolygonCollider2D polyCollider;
void Start()
{
polyCollider = GetComponent<PolygonCollider2D>();
}
// Update is called once per frame
void Update () {
if (meshType == "tri")
{
TriangleMesh(width, height);
}
if (meshType == "box")
{
BoxMesh(width, height);
}
}
void TriangleMesh(float width, float height)
{
MeshFilter mf = GetComponent<MeshFilter>();
Mesh mesh = new Mesh();
mf.mesh = mesh;
//Verticies
Vector3[] verticies = new Vector3[3]
{
new Vector3(0,0,0), new Vector3(width, 0, 0), new Vector3(0, height, 0)
};
//Triangles
int[] tri = new int[3];
tri[0] = 0;
tri[1] = 2;
tri[2] = 1;
//normals
Vector3[] normals = new Vector3[3];
normals[0] = -Vector3.forward;
normals[1] = -Vector3.forward;
normals[2] = -Vector3.forward;
//UVs
Vector2[] uv = new Vector2[3];
uv[0] = new Vector2(0, 0);
uv[0] = new Vector2(1, 0);
uv[0] = new Vector2(0, 1);
//initialise
mesh.vertices = verticies;
mesh.triangles = tri;
mesh.normals = normals;
mesh.uv = uv;
//setting up collider
polyCollider.pathCount = 1;
Vector2[] path = new Vector2[3]
{
new Vector2(0,0), new Vector2(0, height), new Vector2(width, 0)
};
polyCollider.SetPath(0, path);
}
void BoxMesh(float width, float height)
{
MeshFilter mf = GetComponent<MeshFilter>();
Mesh mesh = new Mesh();
mf.mesh = mesh;
//Verticies
Vector3[] verticies = new Vector3[4]
{
new Vector3(0,0,0), new Vector3(0, height, 0), new Vector3(width, height, 0), new Vector3(width, 0, 0)
};
//Triangles
int[] tri = new int[6];
tri[0] = 0;
tri[1] = 1;
tri[2] = 3;
tri[3] = 1;
tri[4] = 2;
tri[5] = 3;
//normals
Vector3[] normals = new Vector3[4];
normals[0] = -Vector3.forward;
normals[1] = -Vector3.forward;
normals[2] = -Vector3.forward;
normals[3] = -Vector3.forward;
//UVs
Vector2[] uv = new Vector2[4];
uv[0] = new Vector2(0, 0);
uv[1] = new Vector2(0, 1);
uv[2] = new Vector2(1, 1);
uv[3] = new Vector2(1, 0);
//initialise
mesh.vertices = verticies;
mesh.triangles = tri;
mesh.normals = normals;
mesh.uv = uv;
//setting up collider
polyCollider.pathCount = 1;
Vector2[] path = new Vector2[4]
{
new Vector2(0,0), new Vector2(0, height), new Vector2(width, height), new Vector2(width, 0)
};
polyCollider.SetPath(0, path);
}
So essentially I want a function that I could call in the update method that would simply create a circular mesh. E.g:
void Update () {
if (meshType == "tri")
{
TriangleMesh(width, height);
}
if (meshType == "box")
{
BoxMesh(width, height);
}
if (meshType == "circle")
{
CircleMesh(radius);
}
}
The solution I've managed to find involves creating a regular polygon of n sides with a large value of n. I have a function called PolyMesh which creates a regular polygon mesh with n sides and a given radius.
Generating the vertices
For each vertex of a regular polygon with n sides the coordinates relative to the centre of the polygon are given by x = r*i*sin(θ) and y = r*i*cos(θ) so therefore x = r*i*sin(2π/2) and y = r*i*cos(2π/2). Where i iterates from 0 to n-1. We can therefore have a list which has vertices assigned to it and then is converted to an array afterwards:
//verticies
List<Vector3> verticiesList = new List<Vector3> { };
float x;
float y;
for (int i = 0; i < n; i ++)
{
x = radius * Mathf.Sin((2 * Mathf.PI * i) / n);
y = radius * Mathf.Cos((2 * Mathf.PI * i) / n);
verticiesList.Add(new Vector3(x, y, 0f));
}
Vector3[] verticies = verticiesList.ToArray();
Generating the triangles
A given regular polygon of n sides can be split into n-2 triangles from the same point. So we can generate each triangle as follows:
//triangles
List<int> trianglesList = new List<int> { };
for(int i = 0; i < (n-2); i++)
{
trianglesList.Add(0);
trianglesList.Add(i+1);
trianglesList.Add(i+2);
}
int[] triangles = trianglesList.ToArray();
Generating the Normals
Since this is a 2d object we can have every normal as -Vector3.forward like so:
//normals
List<Vector3> normalsList = new List<Vector3> { };
for (int i = 0; i < verticies.Length; i++)
{
normalsList.Add(-Vector3.forward);
}
Vector3[] normals = normalsList.ToArray();
Generating the collider
We could just use a circle collider with the same radius but in order to make this function work for a polygon of a smaller value of n we must use a PolygonCollider2D. Since the vertices are already in order in the vertices array we can simply use them as the paths for our PolygonCollider2D.
//polyCollider
polyCollider.pathCount = 1;
List<Vector2> pathList = new List<Vector2> { };
for (int i = 0; i < n; i++)
{
pathList.Add(new Vector2(verticies[i].x, verticies[i].y));
}
Vector2[] path = pathList.ToArray();
polyCollider.SetPath(0, path);
The complete code should look like this:
public PolygonCollider2D polyCollider;
void Start()
{
polyCollider = GetComponent<PolygonCollider2D>();
}
void PolyMesh(float radius, int n)
{
MeshFilter mf = GetComponent<MeshFilter>();
Mesh mesh = new Mesh();
mf.mesh = mesh;
//verticies
List<Vector3> verticiesList = new List<Vector3> { };
float x;
float y;
for (int i = 0; i < n; i ++)
{
x = radius * Mathf.Sin((2 * Mathf.PI * i) / n);
y = radius * Mathf.Cos((2 * Mathf.PI * i) / n);
verticiesList.Add(new Vector3(x, y, 0f));
}
Vector3[] verticies = verticiesList.ToArray();
//triangles
List<int> trianglesList = new List<int> { };
for(int i = 0; i < (n-2); i++)
{
trianglesList.Add(0);
trianglesList.Add(i+1);
trianglesList.Add(i+2);
}
int[] triangles = trianglesList.ToArray();
//normals
List<Vector3> normalsList = new List<Vector3> { };
for (int i = 0; i < verticies.Length; i++)
{
normalsList.Add(-Vector3.forward);
}
Vector3[] normals = normalsList.ToArray();
//initialise
mesh.vertices = verticies;
mesh.triangles = triangles;
mesh.normals = normals;
//polyCollider
polyCollider.pathCount = 1;
List<Vector2> pathList = new List<Vector2> { };
for (int i = 0; i < n; i++)
{
pathList.Add(new Vector2(verticies[i].x, verticies[i].y));
}
Vector2[] path = pathList.ToArray();
polyCollider.SetPath(0, path);
}
An introduction to meshes
I have less than 50 reputation and so I can't just comment on #Tom Ryan's answer.
With that said, beware that his solution doesn't include the UVs for the mesh. Here is that addition:
//uvs
Vector2[] uvs = new Vector2[vertices.Length];
for (int i = 0; i < uvs.Length; i++)
{
uvs[i] = new Vector2(vertices[i].x / (radius*2) + 0.5f, vertices[i].y / (radius*2) + 0.5f);
}
// Later...
mesh.uv = uvs;
I am trying to generate spheres within my gameworld.
I have this script: RandomEnvironment
using UnityEngine;
using System.Collections;
public class RandomEnvironment : MonoBehaviour
{
public Material[] meshMaterials;
int numberOfMeshes = 50;
// Use this for initialization
void Start()
{
for (int i = 1; i <= numberOfMeshes; i++)
{
GameObject planetObject = new GameObject("Planet" + i);
Randomize randomPlanet = planetObject.AddComponent<Randomize>();
planetObject.transform.position = new Vector3(Random.Range(-1000, 1000), Random.Range(-1000, 1000), Random.Range(-1000, 1000));
}
}
}
And then I have the script Randomize:
//http://wiki.unity3d.com/index.php/ProceduralPrimitives
using UnityEngine;
using System.Collections;
public class Randomize : MonoBehaviour {
// Use this for initialization
void Start () {
MeshFilter filter = gameObject.AddComponent<MeshFilter>();
Mesh mesh = filter.mesh;
mesh.Clear();
float radius = 1f;
// Longitude |||
int nbLong = 24;
// Latitude ---
int nbLat = 16;
#region Vertices
Vector3[] vertices = new Vector3[(nbLong + 1) * nbLat + 2];
float _pi = Mathf.PI;
float _2pi = _pi * 2f;
vertices[0] = Vector3.up * radius;
for (int lat = 0; lat < nbLat; lat++)
{
float a1 = _pi * (float)(lat + 1) / (nbLat + 1);
float sin1 = Mathf.Sin(a1);
float cos1 = Mathf.Cos(a1);
for (int lon = 0; lon <= nbLong; lon++)
{
float a2 = _2pi * (float)(lon == nbLong ? 0 : lon) / nbLong;
float sin2 = Mathf.Sin(a2);
float cos2 = Mathf.Cos(a2);
vertices[lon + lat * (nbLong + 1) + 1] = new Vector3(sin1 * cos2, cos1, sin1 * sin2) * radius;
}
}
vertices[vertices.Length - 1] = Vector3.up * -radius;
#endregion
#region Normales
Vector3[] normales = new Vector3[vertices.Length];
for (int n = 0; n < vertices.Length; n++)
normales[n] = vertices[n].normalized;
#endregion
#region UVs
Vector2[] uvs = new Vector2[vertices.Length];
uvs[0] = Vector2.up;
uvs[uvs.Length - 1] = Vector2.zero;
for (int lat = 0; lat < nbLat; lat++)
for (int lon = 0; lon <= nbLong; lon++)
uvs[lon + lat * (nbLong + 1) + 1] = new Vector2((float)lon / nbLong, 1f - (float)(lat + 1) / (nbLat + 1));
#endregion
#region Triangles
int nbFaces = vertices.Length;
int nbTriangles = nbFaces * 2;
int nbIndexes = nbTriangles * 3;
int[] triangles = new int[nbIndexes];
//Top Cap
int i = 0;
for (int lon = 0; lon < nbLong; lon++)
{
triangles[i++] = lon + 2;
triangles[i++] = lon + 1;
triangles[i++] = 0;
}
//Middle
for (int lat = 0; lat < nbLat - 1; lat++)
{
for (int lon = 0; lon < nbLong; lon++)
{
int current = lon + lat * (nbLong + 1) + 1;
int next = current + nbLong + 1;
triangles[i++] = current;
triangles[i++] = current + 1;
triangles[i++] = next + 1;
triangles[i++] = current;
triangles[i++] = next + 1;
triangles[i++] = next;
}
}
//Bottom Cap
for (int lon = 0; lon < nbLong; lon++)
{
triangles[i++] = vertices.Length - 1;
triangles[i++] = vertices.Length - (lon + 2) - 1;
triangles[i++] = vertices.Length - (lon + 1) - 1;
}
#endregion
mesh.vertices = vertices;
mesh.normals = normales;
mesh.uv = uvs;
mesh.triangles = triangles;
mesh.RecalculateBounds();
mesh.Optimize();
}
// Update is called once per frame
void Update () {
}
}
I found the script to generate a sphere on the internet. The Randomize script is supposed to draw me a sphere, and the RandomEnvironment is supposed to make use of that. When I launch the game, I do not get any errors, but there aren't any spheres either. I placed the RandomEnvironment script on an empty gameobject.
I am a beginner with Unity, any help is welcome!