I've been working on a program to generate an icosphere. (A sphere with evenly distributed vertexes across the face, to be used for terrain deformation)
I have pretty much everything done, the sphere is generated, subdivided and drawn. The problem I am running into is that somewhere in the code, some of the vertices (the twelve starting vertexes, I believe) are being set to twice the radius, rather than just the radius.
Here are three images, showing the icosphere at zero, one and two refinement passes:
http://i41.photobucket.com/albums/e262/cstgirllover/Cho/Ico0Refinement.png
http://i41.photobucket.com/albums/e262/cstgirllover/Cho/Ico1Refinement.png
http://i41.photobucket.com/albums/e262/cstgirllover/Cho/Ico2Refinement.png
and here is the code that generates the icosahedron, and then breaks it down into the icosphere:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Icosahedron_Test
{
class Icosahedron
{
int radius; // radius of the planet
int refinement; // number of times to refine the traingles
int faces = 20;
Vector3[] basePositions; // Vertex points for three defining rectangles
TriXYZ[] vertices; // Vertex points for triangles which define the spherical surface
public Icosahedron(int tRadius, int tRefinement, TriXYZ[] tVertices)
{
radius = tRadius;
refinement = tRefinement;
vertices = tVertices;
}
public TriXYZ[] InitializeArray()
{
double t = radius*((1+Math.Sqrt(5))/2);
Vector3[] basePositions =
{
//First Rectangle
new Vector3(-radius, (float)t, 0),
new Vector3(radius, (float)t, 0),
new Vector3(-radius, (float)-t, 0),
new Vector3(radius, (float)-t, 0),
//Seconds Rectangle
new Vector3(0, -radius, (float)t),
new Vector3(0, radius, (float)t),
new Vector3(0, -radius, (float)-t),
new Vector3(0, radius, (float)-t),
//Third Rectangle
new Vector3((float)t, 0, -radius),
new Vector3((float)t, 0, radius),
new Vector3((float)-t, 0, -radius),
new Vector3((float)-t, 0, radius)
};
TriXYZ[] vertices =
{
new TriXYZ(basePositions[5], basePositions[11], basePositions[0], 1),
new TriXYZ(basePositions[1], basePositions[5], basePositions[0], 1),
new TriXYZ(basePositions[7], basePositions[1], basePositions[0], 1),
new TriXYZ(basePositions[10], basePositions[7], basePositions[0], 1),
new TriXYZ(basePositions[11], basePositions[10], basePositions[0], 1),
new TriXYZ(basePositions[9], basePositions[5], basePositions[1], 1),
new TriXYZ(basePositions[4], basePositions[11], basePositions[5], 1),
new TriXYZ(basePositions[2], basePositions[10], basePositions[11], 1),
new TriXYZ(basePositions[6], basePositions[7], basePositions[10], 1),
new TriXYZ(basePositions[8], basePositions[1], basePositions[7], 1),
new TriXYZ(basePositions[4], basePositions[9], basePositions[3], 1),
new TriXYZ(basePositions[2], basePositions[4], basePositions[3], 1),
new TriXYZ(basePositions[6], basePositions[2], basePositions[3], 1),
new TriXYZ(basePositions[8], basePositions[6], basePositions[3], 1),
new TriXYZ(basePositions[9], basePositions[8], basePositions[3], 1),
new TriXYZ(basePositions[5], basePositions[9], basePositions[4], 1),
new TriXYZ(basePositions[11], basePositions[4], basePositions[2], 1),
new TriXYZ(basePositions[10], basePositions[2], basePositions[6], 1),
new TriXYZ(basePositions[7], basePositions[6], basePositions[8], 1),
new TriXYZ(basePositions[1], basePositions[8], basePositions[9], 1),
};
return vertices;
}
public TriXYZ[] Refine(TriXYZ[] rVertices, int rRefinement, float radius)
{
TriXYZ[] tVertices; // Temp list of triangles
Vector3 vertex1; // position of first vertex of base triangle
Vector3 vertex2; // position of second vertex of base triangle
Vector3 vertex3; // position of third vertex of base triangle
int tDepth; // depth of the current triangle
//int listPos = 0; // base list position integer
int nListPos = 0; // new list position integer
int cRefine = 0; // current refinement iteration
while(cRefine < rRefinement) // loop until the icosphere has been refined the inputted number of times
{
tVertices = new TriXYZ[20 + (4*rVertices.Length)]; // make the temporary list empty, and long enough for the original 20 triangles, plus four per triangle for each level of refinement.
for (int listPos = 0; listPos < rVertices.Length; listPos++ ) // Loop through every triangle in the list
{
TriXYZ cTriangle = rVertices[listPos];
tDepth = cTriangle.GetDepth;
vertex1 = cTriangle.GetVertex1; // point 0
vertex2 = cTriangle.GetVertex2; // point 1
vertex3 = cTriangle.GetVertex3; // point 2
if (tDepth == cRefine + 1) // if the depth of this triangle in the list equals the current refinement iteration;
// depth one for first refinement pass, depth two for second, etc; subdivide the triangle
// This prevents unnecessarily re-refining old triangles
{
TriXYZ[] parts = new TriXYZ[5];
parts = cTriangle.subDivide(radius);
tVertices[nListPos] = parts[0]; // Put the original larger triangle at the front if the list
tVertices[nListPos + 1] = parts[1]; // First subdivided triangle
tVertices[nListPos + 2] = parts[2]; // Second subdivided triangle
tVertices[nListPos + 3] = parts[3]; // Third subdivided triangle
tVertices[nListPos + 4] = parts[4]; // Fourth subdivided triangle
nListPos = nListPos + 5; // Move forward in the new triangle list so the next set of triangles doesn't overwrite this set.
}
else if (tDepth < cRefine + 1) // Ifthe triangle's depth is less than the current refinement iteration (depth 1 on refinement 2) then add the current triangle to the new list at nListPos
{
tVertices[nListPos] = new TriXYZ(vertex1, vertex2, vertex3, tDepth);
nListPos++;
}
// it shouldn't be possible for the tDepth to be greater than cRefine
} // end for loop: either move to the next triangel in the original list, or move on to the next level of refinement
rVertices = tVertices; // Replace the old list with the new one, so that the next time it
// runs through the refinement process, it will refine the new
// traingles
cRefine++; // increase refinement interation variable so that it will either refine the next set of triangles, or exit the refinement loop.
nListPos = 0; // reset the new list position integer so it overwrites the exiting data
} // end while loop: either move on to the next refinement set, or exit the loop
vertices = rVertices; // make sure the class=level vertices
return rVertices;
} // End Refinement Class
public int Length
{
get { return vertices.Length; }
private set { }
}
public VertexPositionColor[] BuildList(TriXYZ[] tList, int tDepth)
{
VertexPositionColor[] finalList = new VertexPositionColor[tList.Length*3]; // final list to be returned for drawing
int listPos = 0; // current position in the final list (where the vector 3 is being applied)
Vector3 pos1; // Vertex 1 position of TriXYZ triangle
Vector3 pos2; // Vertex 2 position of TriXYZ triangle
Vector3 pos3; // Vertex 3 position of TriXYZ triangle
int depth;
for(int cTri = 0; cTri<tList.Length; cTri+=1) // Loop through the TriXYZ list and get all the vertexes from it, then apply them to the final draw list
{
pos1 = tList[cTri].GetVertex1;
pos2 = tList[cTri].GetVertex2;
pos3 = tList[cTri].GetVertex3;
depth = tList[cTri].GetDepth;
if (depth == tDepth)
{
finalList[listPos] = new VertexPositionColor(pos1, Color.Blue);
finalList[listPos + 1] = new VertexPositionColor(pos2, Color.Red);
finalList[listPos + 2] = new VertexPositionColor(pos3, Color.Green);
listPos = listPos + 3;
}
}
return finalList;
}
}
}
and here is the TriXYZ class, that holds the triangle data:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Icosahedron_Test
{
class TriXYZ
{
Vector3 vertex1;
Vector3 vertex2;
Vector3 vertex3;
int depth;
float material1; // float for first material value amount (in %) deals with blending
float material2; // float for second material value amount (in %) deals with blending
public TriXYZ(Vector3 pos1, Vector3 pos2, Vector3 pos3, int tDepth)
{
vertex1 = pos1;
vertex2 = pos2;
vertex3 = pos3;
depth = tDepth;
}
public TriXYZ(Vector3 pos1, Vector3 pos2, Vector3 pos3, int tDepth, float tMaterial1, float tMaterial2)
{
vertex1 = pos1;
vertex2 = pos2;
vertex3 = pos3;
depth = tDepth;
material1 = tMaterial1;
material2 = tMaterial2;
}
// public access to triangle data, read-write
public Vector3 GetVertex1
{
get { return vertex1; }
set { vertex1 = value; }
}
public Vector3 GetVertex2
{
get { return vertex2; }
set { vertex2 = value; }
}
public Vector3 GetVertex3
{
get { return vertex3; }
set { vertex3 = value; }
}
public int GetDepth
{
get { return depth; }
set { depth = value; }
}
public static Vector3 Midpoint(Vector3 pos1, Vector3 pos2, float radius)
{
Vector3 midpoint; // returned midpoint between the two inputted vectors
float x;
float y;
float z;
x = (pos1.X + pos2.X)/2;
y = (pos1.Y + pos2.Y)/2;
z = (pos1.Z + pos2.Z)/2;
midpoint = new Vector3(x, y, z);
midpoint.Normalize();
midpoint = midpoint * radius;
return midpoint;
}
public TriXYZ[] subDivide(float radius)
{
Vector3 r; // placeholder for new vertex position, aligned to planet sphere radius
Vector3 UV; // new vector position
TriXYZ[] nTriangle = new TriXYZ[5]; // array of triangle values to return
Vector3 mid1 = Midpoint(vertex1, vertex2, radius);
Vector3 mid2 = Midpoint(vertex2, vertex3, radius);
Vector3 mid3 = Midpoint(vertex3, vertex1, radius);
nTriangle[0] = new TriXYZ(vertex1, vertex2, vertex3, depth); // Put the original larger triangle at the front if the list
nTriangle[1] = new TriXYZ(vertex1, mid1, mid3, depth + 1); // First subdivided triangle
nTriangle[2] = new TriXYZ(mid1, vertex2, mid2, depth + 1); // Second subdivided triangle
nTriangle[3] = new TriXYZ(mid3, mid2, vertex3, depth + 1); // Third subdivided triangle
nTriangle[4] = new TriXYZ(mid3, mid1, mid2, depth + 1); // Fourth subdivided triangle
return nTriangle;
}
}
}
Any help will be greatly appreciate. I imagine it's something simple, I just cannot seem to find the problem.
It's definitely the initial vectors. If memory serves (it's been a while since I've dealt with icosahedrons, etc.), you just use the golden ratio to create one with and edge length of 2 (which you're doing). Perhaps normalize the vectors before multiplying by the radius? The reason I say that is because those vertices never get updated in your code, so it has to be the initial values (unless of course I missed something, which is possible).
Related
I'm quite new to coding in general. I have found some answers for this question but the answers seem advanced for me.
I'm trying to write my own Finite Element Project. For this I would like to write a method that checks if random 4 nodes given as input form a convex quadrilateral.
My method is supposed to look like this:
private bool IsConvex(Node[4] corners)
{
bool isConvex;
//CODE//
return isConvex;
}
the Node class is defined by three public properties referring to their coordinates (.coordX, .coordY, .coordZ)
In order to know if a quadrilateral is convex or not, you can make a triangle of three points and see if the fourth point is located inside that triangle or not. If you manage finding one triangle, which contains the fourth point, then you don't have a convex quadrilateral.
Ok, and how can you know if a point is located inside a triangle?
Well, you start by determining at which side a point is located compared to a vector.
Come again?
Well, for each vector, you can find out if a point is located at the left side or at the right side: you just rotate the vector back to the Y-axis, you do the same with the point and if the X coordinate of the point is negative your point is located at the left side, otherwise it's at the right side, like in these three cases (left, left and right):
Once you have figured that out, you define a point being inside a triangle if, after having described the triangle as a triangle of vectors, your point is at the same side of all vectors, like in this example (be aware that your triangle consists of the vectors AB, BC and CA: the points must follow up each other):
Good luck
First a little helper class to handle things related to triangles made up of three nodes.
using System.Numerics;
public readonly struct Triangle
{
public const float DistanceTolerance = 1e-6f;
public Triangle(Vector3 a, Vector3 b, Vector3 c)
{
A = a;
B = b;
C = c;
}
public Vector3 A { get; }
public Vector3 B { get; }
public Vector3 C { get; }
private Vector3 AreaVector { get => (Vector3.Cross(A, B) + Vector3.Cross(B, C) + Vector3.Cross(C, A)) / 2; }
public float Area { get => AreaVector.Length(); }
public Vector3 Normal { get => Vector3.Normalize(AreaVector); }
public float DistanceTo(Vector3 point) => Vector3.Dot(Normal, point - A);
public Vector3 Project(Vector3 point)
{
// A projected point lies on the plane defined by the three veertices A,B,C
Vector3 n = Normal;
float d = Vector3.Dot(n, point - A);
return point - n * d;
}
public void Barycentric(Vector3 P, out (float w_A, float w_B, float w_C) coordinates)
{
Vector3 n = Vector3.Cross(A, B) + Vector3.Cross(B, C) + Vector3.Cross(C, A);
float w_A = Vector3.Dot(n, Vector3.Cross(P, B) + Vector3.Cross(B, C) + Vector3.Cross(C, P));
float w_B = Vector3.Dot(n, Vector3.Cross(A, P) + Vector3.Cross(P, C) + Vector3.Cross(C, A));
float w_C = Vector3.Dot(n, Vector3.Cross(A, B) + Vector3.Cross(B, P) + Vector3.Cross(P, A));
float sum = w_A + w_B + w_C;
coordinates = (w_A / sum, w_B / sum, w_C / sum);
}
public bool Contains(Vector3 P)
{
if (Math.Abs(DistanceTo(P)) <= DistanceTolerance)
{
Barycentric(P, out var coordinates);
return coordinates.w_A >= 0 && coordinates.w_A <= 1
&& coordinates.w_B >= 0 && coordinates.w_B <= 1
&& coordinates.w_C >= 0 && coordinates.w_C <= 1;
}
return false;
}
}
If you are not familiar with barycentric coordinates, they are the linear combinations of the vertices that make up an interior (or exterior) point.
For example if a point is defined as P = 0.3*A + 0.5*B + 0.2*C then the barycentric coordinates of P are (0.3,0.5,0.2). The only restriction here is that the sum of the barycentric coordinates must equal to 1.
A point P is interior to the triangle ABC if all the barycentric coordinates of P are between 0 and 1.
This is the rule that I am using to write the Triangle.Contains(point) function. I also check to see if the point is on the same plane as the triangle.
Now to get to the algorithm to check if an n-gon is convex, all I have to do is take 3 vertices at a time, and check that all remaining other vertices are exterior to those three.
public static bool IsConvex(Vector3[] nodes)
{
for (int i = 0; i < nodes.Length; i++)
{
// pick three nodes at a time i,j,k
var j = (i + 1) % nodes.Length;
var k = (i + 2) % nodes.Length;
var A = nodes[i];
var B = nodes[j];
var C = nodes[k];
// deefine triangle ABC from three nodes
var trig = new Triangle(A, B, C);
// check nodes after the three and wrap around to grab first nodes also
for (int r = 3; r < nodes.Length; r++)
{
var P = nodes[(r + i) % nodes.Length];
// if _any_ node is interior to ABC then non-convex
if (trig.Contains(P))
{
return false;
}
}
}
return true;
}
and some test code to make sure it all works as intended.
static readonly Random rng = new Random();
static void Main(string[] args)
{
// Generate a random 3D triangle
var trig = new Triangle(
new Vector3(10 * (float)rng.NextDouble(), 0, 0),
new Vector3(0, 10 * (float)rng.NextDouble(), 0),
new Vector3(0, 0, 10 * (float)rng.NextDouble()));
// Generate an interior point (in the plane)
var point1 = 0.3f * trig.A + 0.5f * trig.B + 0.2f * trig.C;
// Check that it is contained inside the triangle
Debug.Assert(trig.Contains(point1));
// Generate an exterior point (on the plane)
var point2 = -0.3f * trig.A + 0.5f * trig.B + 0.8f * trig.C;
// Check that it is not contained inside the triangle
Debug.Assert(!trig.Contains(point2));
// Generate a point out of plane
var point3 = point1 + 2.5f * trig.Normal;
// Check that it is not contained inside the triangle
Debug.Assert(!trig.Contains(point3));
// Generate a convex quadrilateral
var poly1 = new Vector3[] {
new Vector3(0f,0f,0f),
new Vector3(5f,0f,0f),
new Vector3(5f,3f,0f),
new Vector3(1f,7f,0f),
};
// Generate a non-convex quadrilateral
var poly2 = new Vector3[] {
new Vector3(0f,0f,0f),
new Vector3(5f,0f,0f),
new Vector3(2f,2f,0f),
new Vector3(1f,7f,0f),
};
// Check that it is convex
Debug.Assert(IsConvex(poly1));
// Check that it is not convex
Debug.Assert(!IsConvex(poly2));
}
I am making a cubic voxel game. I have chunks, world, blocks and mesh generation done, but there's one problem - I could not do the texturing.
Everything I need is just add a texture to a side of a 3D mesh (Texture of every is different!). I've seen some implementations but it's hard to read somebody else's code (I've tried to use them, but it didn't work). I've tried to do this by myself, but with no results.
Can anybody explain how to do this??
Here is my current code:
[ExecuteInEditMode]
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class Chunk : MonoBehaviour
{
private ushort[] _voxels = new ushort[16 * 16 * 16];
private MeshFilter meshFilter;
private Vector3[] cubeVertices = new[] {
new Vector3 (0, 0, 0),
new Vector3 (1, 0, 0),
new Vector3 (1, 1, 0),
new Vector3 (0, 1, 0),
new Vector3 (0, 1, 1),
new Vector3 (1, 1, 1),
new Vector3 (1, 0, 1),
new Vector3 (0, 0, 1),
};
private int[] cubeTriangles = new[] {
// Front
0, 2, 1,
0, 3, 2,
// Top
2, 3, 4,
2, 4, 5,
// Right
1, 2, 5,
1, 5, 6,
// Left
0, 7, 4,
0, 4, 3,
// Back
5, 4, 7,
5, 7, 6,
// Bottom
0, 6, 7,
0, 1, 6
};
public ushort this[int x, int y, int z]
{
get { return _voxels[x * 16 * 16 + y * 16 + z]; }
set { _voxels[x * 16 * 16 + y * 16 + z] = value; }
}
void Start()
{
meshFilter = GetComponent<MeshFilter>();
}
private void Update()
{
GenerateMesh();
}
public void GenerateMesh()
{
Mesh mesh = new Mesh();
List<Vector3> vertices = new List<Vector3>();
List<int> triangles = new List<int>();
for (var x = 0; x < 16; x++)
{
for (var y = 0; y < 16; y++)
{
for (var z = 0; z < 16; z++)
{
var voxelType = this[x, y, z];
if (voxelType == 0)
continue;
var pos = new Vector3(x, y, z);
var verticesPos = vertices.Count;
foreach (var vert in cubeVertices)
vertices.Add(pos + vert);
foreach (var tri in cubeTriangles)
triangles.Add(verticesPos + tri);
}
}
}
mesh.SetVertices(vertices);
mesh.SetTriangles(triangles.ToArray(), 0);
meshFilter.mesh = mesh;
}
}
NOTE: This is a repost with many edits so it is focused on one problem plus has better explanation. Sorry for that.
Like your SetVertices() and SetTriangles(), you can call a SetUVs() with a list of the UV coordinates of each vertex on your texture.
The UV list size must match the vertices list size!
The UV coordinate are expressed as Vector2 with values between 0 and 1.
For example, to apply the whole texture on the front face of your cube, you have the first 4 uvs like this:
private Vector2[] cubeUVs = new[] {
new Vector2 (0, 0),
new Vector2 (1, 0),
new Vector2 (1, 1),
new Vector2 (0, 1),
...
}
...
mesh.SetUVs(0, cubeUVs);
If your texture is not a square, then it will be stretched.
You should also call RecalculateBounds() and RecalculateNormals() at the end of your GenerateMesh() method to avoid some issues later.
EDIT
If you really want different texture files for each side of the cube, then the cleanest and most performant solution for me is to set a different VertexColor for each side of your cube, eg. (1,0,0), (0,1,0), (0,0,1), (1,1,0), (1,0,1) and (0,1,1).
However, you will have to duplicate all your vertices 3 times. (because the vertex color is bound to a vertex, and each vertex of a cube belongs to 3 sides)
(You still have to set the UVs like I said previously, but each side has the whole texture instead of only a part of the texture)
Then, you will have to create a custom shader with 6 textures in inputs (one for each side).
And in the fragment function, you select the right texture color according to the vertex color.
You can for that, do some if to select the texture, but it will be not very performant:
float3 finalColor;
if(vertexColor.r > 0.5f && vertexColor.g < 0.5f && vertexColor.b < 0.5f)
{
finalColor = text2D(_TopTexture, in.uv);
}
else if(...)
{
...
}
...
Or if you want more perf (with a lot of cubes), you can instead do some multiplications to select the right texture:
float3 topTexColor = text2D(_TopTexture, in.uv) * vertexColor.r * (1.0f - vertexColor.g) * (1.0f - vertexColor.b);
float3 frontTexColor = ...;
...
float3 finalColor = topTexColor + frontTexColor + ...;
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 following a tutorial about voxel meshes and ran into an error that I did not expect. Here is the tutorial, I am on the second part if you go and take a look at that. I also thought maybe this is a unity version issue as this is from 2014. The errors I have searched and found no conclusive results. I checked for the void statements over and over and seen many repetitive stackoverflow questions. I have also tried restarting unity multiple times. More specific information on the errors: On Line 118,119,and 120 i get these two errors. Error CS0501: Method Must have return type. Error CS0501: 'PolygonGenerator.PolygonGenerator()' must declare a body because it is not marked abstract, extern, or partial. Anyways, my code is as follows: (also, please correct my comments and add more)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PolygonGenerator : MonoBehaviour
{
// This first list contains every vertex of the mesh that we are going to render
public List<Vector3> newVertices = new List<Vector3>();
// The triangles tell Unity how to build each section of the mesh joining
// the vertices
public List<int> newTriangles = new List<int>();
// The UV list is unimportant right now but it tells Unity how the texture is
// aligned on each polygon
public List<Vector2> newUV = new List<Vector2>();
private float tUnit = 0.25f;
private Vector2 tStone = new Vector2(0, 0);
private Vector2 tGrass = new Vector2(0, 1);
// A mesh is made up of the vertices, triangles and UVs we are going to define,
// after we make them up we'll save them as this mesh
private Mesh mesh;
private int squareCount;
public byte[,] blocks; //0=air 1=rock, 2=grass
// Start is called before the first frame update
void Start()
{
//gets the mesh of the gameobject
mesh = GetComponent<MeshFilter>().mesh;
//gets the x,y, and z values of the gameobject
//writing x is easier than transform.position.x many times
float x = transform.position.x;
float y = transform.position.y;
float z = transform.position.z;
// defines what corners of the mesh to use for the four corners of the texture
newUV.Add(new Vector2(tUnit * tStone.x, tUnit * tStone.y + tUnit));
newUV.Add(new Vector2(tUnit * tStone.x + tUnit, tUnit * tStone.y + tUnit));
newUV.Add(new Vector2(tUnit * tStone.x + tUnit, tUnit * tStone.y));
newUV.Add(new Vector2(tUnit * tStone.x, tUnit * tStone.y));
//clear anything within the meshes boundries
mesh.Clear();
mesh.vertices = newVertices.ToArray(); //set the meshes vertecies to the new ones we just made
mesh.triangles = newTriangles.ToArray();
mesh.uv = newUV.ToArray(); // applys uvs to the mesh
mesh.Optimize(); //unity does some stuff
mesh.RecalculateNormals(); //
}
void GenSquare(int x, int y, Vector2 texture)
{
//defines the vertexes of the new square
newVertices.Add(new Vector3(x, y, z));
newVertices.Add(new Vector3(x + 1, y, z));
newVertices.Add(new Vector3(x + 1, y - 1, z));
newVertices.Add(new Vector3(x, y - 1, z));
//without triangels all we have is points in space, no connections
//these are added clockwise
newTriangles.Add(squareCount * 4); // 0,0
newTriangles.Add((squareCount * 4) +1); // 1,0
newTriangles.Add((squareCount * 4)+3); // -1,0
newTriangles.Add((squareCount * 4)+1); // 1,0
newTriangles.Add((squareCount * 4)+2); // -1,1
newTriangles.Add((squareCount * 4)+3); // -1,0
// defines what corners of the mesh to use for the four corners of the texture
newUV.Add(new Vector2(tUnit * texture.x, tUnit * texture.y + tUnit));
newUV.Add(new Vector2(tUnit * texture.x + tUnit, tUnit * texture.y + tUnit));
newUV.Add(new Vector2(tUnit * texture.x + tUnit, tUnit * texture.y));
newUV.Add(new Vector2(tUnit * texture.x, tUnit * texture.y));
squareCount++;
}
void GenTerrain()
{
blocks = new byte[10, 10];
for (int px = 0; px < blocks.GetLength(0); px++)
{
for (int py = 0; py < blocks.GetLength(1); py++)
{
if (py >= 5)
{
blocks[px, py] = 2;
}
else if (py < 5)
{
blocks[px, py] = 1;
}
}
}
}
void BuildMesh()
{
for (int px = 0; px < blocks.GetLength(0); px++)
{
for (int py = 0; py < blocks.GetLength(1); py++)
{
if (blocks[px, py] == 1)
{
GenSquare(px, py, tStone);
}
else if (blocks[px, py] == 2)
{
GenSquare(px, py, tGrass);
}
}
}
}
GenTerrain();
BuildMesh();
UpdateMesh();
// Update is called once per frame
void Update()
{
//clear anything within the meshes boundries
mesh.Clear();
mesh.vertices = newVertices.ToArray(); //set the meshes vertecies to the new ones we just made
mesh.triangles = newTriangles.ToArray();
mesh.uv = newUV.ToArray(); // applys uvs to the mesh
mesh.Optimize(); //unity does some stuff
mesh.RecalculateNormals(); //
squareCount = 0;
newVertices.Clear();
newTriangles.Clear();
newUV.Clear();
}
}
GenTerrain();
BuildMesh();
UpdateMesh();
These three method calls should be in a method, probably start or update. The computer thinks you're trying to define new methods here.
I have a texture that I want to slice into 2 parts, using a Vector2 array.
I have all the Vector2 points for the curved line.
Question
How can I slice the texture into 2 parts using the curved line of points.
Alternative Solutions/Questions
How can I 'pixel' fill a Vector2[] shape to create a Texture?
My attempts
1) Generating Vector2 points to create a square, with the top part being the curve edge. Looked promising but when I tried generating a Mesh, the points sorting was incorrect.
2) Dynamically created a Polygon2D Collider - mimicking the bottom part of the sliced texture - this had the same issue as attempt 1, the point ordering. So when convert the Collider to Mesh, it obviously had the same results as attempt
In the picture below:
The red line simulates my Vector2 array
The gray+green square is the texture 1024 x 1024 pixels
The green area is the target area I want
This makes a mesh that is the shape you want (but with jagged edges on top), hopefully that is a step in the right direction. The Vector2 points[] array contains your red line. It should be sorted by the x coordinate, and all the numbers should be between 0 and 1. Needs a mesh filter and a mesh renderer with your texture.
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class createMesh : MonoBehaviour {
void Start () {
Vector2[] points = new Vector2[4];
points [0] = new Vector2 (0, .5f);
points [1] = new Vector2 (.33f, 1f);
points [2] = new Vector2 (.66f, .5f);
points [3] = new Vector2 (1, 1f);
MeshFilter mf = GetComponent<MeshFilter> ();
Mesh mesh = new Mesh();
Vector3[] verticies = new Vector3[points.Length * 2];
int[] triangles = new int[(points.Length - 1)*6];
Vector3[] normals = new Vector3[points.Length * 2];
Vector2[] uv = new Vector2[points.Length * 2];
int vIndex = 0;
int tIndex = 0;
int nIndex = 0;
int uvIndex = 0;
for (int i = 0; i< points.Length; i++) {
Vector3 topVert = points[i];
Vector3 bottomVert = topVert;
bottomVert.y = 0;
verticies[vIndex++]= bottomVert;
verticies[vIndex++]=topVert;
//uv
uv[uvIndex++] = bottomVert;
uv[uvIndex++] = topVert;
//normals
normals[nIndex++] = -Vector3.forward;
normals[nIndex++] = -Vector3.forward;
if (i<points.Length - 1) {
//triangles
triangles[tIndex++] = (i)*2;
triangles[tIndex++] = (i)*2+1;
triangles[tIndex++] = (i)*2+2;
triangles[tIndex++] = (i)*2+2;
triangles[tIndex++] = (i)*2+1;
triangles[tIndex++] = (i)*2+3;
}
}
mesh.vertices = verticies;
mesh.triangles = triangles;
mesh.normals = normals;
mesh.uv = uv;
mf.mesh = mesh;
}
}
Bonus: here's a way to do it just with the texture. To use this the bitmap has to be set to Advanced, with read/write enabled in the import settings. This method uses 0 to 1023 (or however large your texture is) for coordinates, and should work for numbers out of that range too.
using UnityEngine;
using System.Collections;
public class tex2d : MonoBehaviour {
public Vector2[] points;
void Start () {
MeshRenderer mr;
Texture2D t2d;
Texture2D newTex = new Texture2D (1024, 1024);
mr = GetComponent<MeshRenderer> ();
t2d = mr.material.GetTexture (0) as Texture2D;
MakeTex (points, t2d, ref newTex, 1024);
mr.material.SetTexture (0, newTex);
}
void MakeTex(Vector2[] pnts, Texture2D inputTex, ref Texture2D outputTex, int size){
Color bgcolor = new Color (1, 0, 1, 1);
for (int i=0; i<(pnts.Length-1); i++) {
Vector2 p1=pnts[i];
Vector2 p2=pnts[i+1];
//skip points that are out of range
if ((p1.x <0 && p2.x <0) || (p1.x > size && p2.x>size)) continue;
for (int x =(int)p1.x; x<(int)p2.x; x++) {
if (x<0) continue;
if (x>=size) break;
float interpX = (x-p1.x)/(p2.x-p1.x);
int interpY = (int) ((p2.y-p1.y)*interpX + p1.y);
for (int y=0; y<interpY; y++) {
outputTex.SetPixel(x,y,inputTex.GetPixel(x,y));
}
for (int y= interpY; y<size; y++) {
outputTex.SetPixel(x,y,bgcolor);
}
}
}
outputTex.Apply ();
}
}