Calculate object volume with XYZ coordinates - c#

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.

Related

Terrain having visible seams at vertices - How can i fix this?

i'm a newbie and i am following a tutorial on procedural landmass generation. However, my plane does not look right. It has a lot of seams/cracks. Is there someone who can point me in the right direction?
Below is my MeshGenerator scripts:
public static class MeshGenerator
{
public static MeshData GenerateTerrainMesh(float[,] heightMap, float heightMultiplier, AnimationCurve heightCurve)
{
int width = heightMap.GetLength(0);
int height = heightMap.GetLength(1);
float topLeftX = (width - 1) / -2f;
float topLeftZ = (height - 1) / 2f;
MeshData meshData = new MeshData(width, height);
int vertexIndex = 0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
meshData.vertices[vertexIndex] = new Vector3(topLeftX + x, heightCurve.Evaluate(heightMap[x,y]) * heightMultiplier, topLeftZ - y);
meshData.uvs[vertexIndex] = new Vector2(x / (float)width, y / (float)height);
if (x < width - 1 && y < height - 1)
{
meshData.AddTriangle(vertexIndex, vertexIndex + width + 1, vertexIndex + width);
meshData.AddTriangle(vertexIndex, + width + 1, vertexIndex + 1);
}
vertexIndex++;
}
}
return meshData;
}
}
public class MeshData
{
public Vector3[] vertices;
public int[] triangles;
public Vector2[] uvs;
int triangleIndex;
public MeshData(int meshWidth, int meshHeight)
{
vertices = new Vector3[meshWidth * meshHeight];
uvs = new Vector2[meshWidth * meshHeight];
triangles = new int[(meshWidth-1) * (meshHeight-1)*6];
}
public void AddTriangle(int a, int b, int c)
{
triangles[triangleIndex] = a;
triangles[triangleIndex+1] = b;
triangles[triangleIndex+2] = c;
triangleIndex += 3;
}
public Mesh CreateMesh()
{
Mesh mesh = new Mesh();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.uv = uvs;
mesh.RecalculateNormals();
return mesh;
}
}
You triangle indices are wrong, this is rather obvious since you have a bunch of triangle-shaped holes. Notably
meshData.AddTriangle(vertexIndex, + width + 1, vertexIndex + 1);
the second vertex of the second triangle is a constant value, and that is most likely incorrect
You should not need to keep a running total of vertexIndex, you should be perfectly able to compute the triangle indices from the grid indices:
var v1 = y * (width+1) + x; // You should have one more column of vertices than you have grid cells
var v2 = v2 + 1; // the vertex one column to the right
var v3 = v1 + width+1; // the vertex one row down
var v4 = v3 + 1;
meshData.AddTriangle(v1, v2, v4);
meshData.AddTriangle(v1, v4, v3);
You may need to invert the vertex order to ensure the normals are oriented correctly.

Why cube mesh becomes a plane when in high resolution?

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.

Unity3d Method Must Have a Return Type And Error CS0501

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.

How to find some additional points on a line between two points in 3D?

Is there a function in C# which can give me all the points on a straight line between two points in 3D?
To calculate the distance between those two points, I use this:
public class Position {
public float x;
public float y;
public float z;
}
public void CalculateDistance(Position position1, Position position2, int mapId){
float deltaX = position1.x - position2.x;
float deltaY = position1.y - position2.y;
float deltaZ = position1.z - position2.z;
float distance = (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ);
Console.WriteLine("Distance is: " + distance);
}
Example coordinates:
Position pos1 = new Position();
pos1.x = 141.6586f;
pos1.y = 0.6852107f;
pos1.z = 153.2231f;
Position pos2 = new Position();
pos2.x = 142.336f;
pos2.y = 0.8685942f;
pos2.z = 130.8394f;
Let's say, the distance in line between those two 3d coordinates can be passed for 5 seconds. How can I print the current coordinate for every 1 second?
what you want to do is well described in this answer
And here is example of code how you can print your values:
var mx = pos2.x - pos1.x;
var my = pos2.y - pos1.y;
var mz = pos2.z - pos1.z;
for(var t=0; t < 10; t++) {
var x = pos1.x + mx * t;
var y = pos1.y + my * t;
var z = pos1.z + mz * t;
//TODO: use you 3D point
}
Hope this helps!

Spiked vertexes on generated Icosphere

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).

Categories

Resources