Related
I'm new to using Unity compute shaders but have implemented a fluid solver in a script and a compute shader but the script is significantly faster even at high numbers of points to be simulated.
Is there anything majorly wrong with how I've implemented the shader? I think it might be the data I'm sending causing a bottleneck but I'm not entirely sure. Here is the function that runs the shader:
public struct dataLaxF
{
//Input Data
public Vector4 heightData;
public Vector4 fUData;
public Vector4 fVData;
public Vector4 uData;
public Vector4 vData;
//Output Data
public float outputHeight;
public float outputFU;
public float outputFV;
public float outputU;
public float outputV;
};
//Runs the algorithm on the GPU
public void SimulateGPU()
{
//Profiler Testing Start
Profiler.BeginSample("GPULaxF");
//Set data to structs for GPU
for (int x = 1; x < xGridSize-1; x++)
for (int y = 1; y < yGridSize-1; y++)
{
dataLaxF inData = new dataLaxF();
inData.heightData = new Vector4(height[x + 1, y], height[x - 1, y], height[x, y + 1], height[x, y - 1]);
inData.fUData = new Vector4(fU[x + 1, y], fU[x - 1, y], fU[x, y + 1], fU[x, y - 1]);
inData.fVData = new Vector4(fV[x + 1, y], fV[x - 1, y], fV[x, y + 1], fV[x, y - 1]);
inData.uData = new Vector4(u[x + 1, y], u[x - 1, y], u[x, y + 1], u[x, y - 1]);
inData.vData = new Vector4(v[x + 1, y], v[x - 1, y], v[x, y + 1], v[x, y - 1]);
inData.outputHeight = height[x, y];
inData.outputFU = fU[x, y];
inData.outputFV = fV[x, y];
inData.outputU = u[x, y];
inData.outputV = v[x, y];
data[(x * xGridSize) + y] = inData;
}
//Send data to GPU
int totalSize = sizeof(float) * 25;
ComputeBuffer dataBuffer = new ComputeBuffer(data.Length, totalSize);
dataBuffer.SetData(data);
computeShader.SetBuffer(0, "vDatas", dataBuffer);
computeShader.Dispatch(0, data.Length / 128, 1, 1);
dataBuffer.GetData(data);
//Recieve and read data
for (int x = 1; x < xGridSize-1; x++)
for (int y = 1; y < xGridSize-1; y++)
{
dataLaxF outData = data[x * xGridSize + y];
height[x, y] = outData.outputHeight;
fU[x, y] = outData.outputFU;
fV[x, y] = outData.outputFV;
u[x, y] = outData.outputU;
v[x, y] = outData.outputV;
}
dataBuffer.Dispose();
Profiler.EndSample();
}
And this is the compute shader:
#pragma kernel CSMain
struct dataLaxF
{
//Input Data
float4 heightData;
float4 fUData;
float4 fVData;
float4 uData;
float4 vData;
//Output Data
float outputHeight;
float outputFU;
float outputFV;
float outputU;
float outputV;
};
//Data struct arrays
RWStructuredBuffer<dataLaxF> vDatas;
//Kernel for Lax Friedrichs
[numthreads(128,1,1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
float alpha = 0.02f;
float g = 9.8f;
dataLaxF data = vDatas[id.x];
//Height
data.outputHeight = 0.25f * (data.heightData.x + data.heightData.y + data.heightData.z + data.heightData.a) - alpha * (data.fUData.x - data.fUData.y) - alpha * (data.fVData.z - data.fVData.a);
//FU
data.outputFU = 0.25f * (data.fUData.x + data.fUData.y + data.fUData.z + data.fUData.a);
data.outputFU = data.outputFU - alpha * ((data.heightData.x * (data.uData.x* data.uData.x) + (0.5f * g * (data.heightData.x* data.heightData.x))) - (data.heightData.y * (data.uData.y* data.uData.y) + (0.5f * g * (data.heightData.y* data.heightData.y))));
data.outputFU = data.outputFU - alpha * (data.heightData.z * data.vData.z * data.uData.z - data.heightData.a * data.vData.a * data.uData.a);
//FV
data.outputFV = 0.25f * (data.fVData.x + data.fVData.y + data.fVData.z + data.fVData.a);
data.outputFV = data.outputFV - alpha * (data.heightData.x * data.vData.x * data.uData.x - data.heightData.y * data.vData.y * data.uData.y);
data.outputFV = data.outputFV - alpha * ((data.heightData.z * (data.vData.z* data.vData.z) + (0.5f * g *(data.heightData.z* data.heightData.z))) - (data.heightData.a * (data.vData.a* data.vData.a) + (0.5f * g * (data.heightData.a* data.heightData.a))));
//U
data.outputU = data.outputFU / data.outputHeight;
//V
data.outputV = data.outputFV / data.outputHeight;
vDatas[id.x] = data;
}
Thanks in advance.
I am working on Hilbert curve, and I cant rotate the whole figure, just lower rectangle(look at the screenshots, on third and next steps I have a problem)
first 3 steps
I have a Figure class. I use it to store every figure and build next firuge from previous one. Here is my code
public class Fragment
{
public static int PADDING = 50;
public static float sideLength;
private readonly Pen crimsonPen = new Pen(Color.Crimson);
public List<PointF> pointsF = new List<PointF>();
public PointF[] points;
public Fragment(int step, Graphics graphics)
{
sideLength = Form1.sideLenght;
points = new PointF[(int)Math.Pow(4, step + 1)];
if (step.Equals(0))
{
points[0] = new PointF(PADDING, PADDING + sideLength);
points[1] = new PointF(PADDING, PADDING);
points[2] = new PointF(PADDING + sideLength, PADDING);
points[3] = new PointF(PADDING + sideLength, PADDING + sideLength);
graphics.DrawLines(crimsonPen, new[] { points[0], points[1], points[2], points[3] });
}
else
{
var frag = Form1.fragments[step - 1];
for (var i = 0; i < step; i++)
{
PointF tmpPoint;
// left lower #1
for (int j = frag.points.Length - 1; j >= 0; j--)
{
points[frag.points.Length - 1 - j] = frag.points[j];
points[frag.points.Length - 1 - j].Y += sideLength * 2 * (i + 1);
}
//rotate left lower #1
for (int b = 0; b < Math.Pow(4, step) - 1; b++)
{
tmpPoint = points[0];
for (int j = 0; j < frag.points.Length; j++)
{
if (j.Equals(frag.points.Length - 1))
{
points[j] = tmpPoint;
}
else
{
points[j] = points[j + 1];
}
}
}
// left upper #2
for (int j = 0; j < frag.points.Length; j++)
{
points[j + frag.points.Length] = frag.points[j];
}
// right upper #3
for (int j = 0; j < frag.points.Length; j++)
{
points[j + 2 * frag.points.Length] = points[j + frag.points.Length];
points[j + 2 * frag.points.Length].X += sideLength * 2 * (i + 1);
}
//right lower #4
for (int j = frag.points.Length - 1; j >= 0; j--)
{
points[3 * frag.points.Length + j] = points[2 * frag.points.Length + frag.points.Length - j - 1];
points[3 * frag.points.Length + j].Y += sideLength * 2 * (i + 1);
}
tmpPoint = points[3 * frag.points.Length];
//rotate right lower #4
for (int j = 0; j < frag.points.Length; j++)
{
if (j.Equals(frag.points.Length - 1))
{
points[4 * (frag.points.Length) - 1] = tmpPoint;
}
else
{
points[3 * frag.points.Length + j] = points[3 * frag.points.Length + j + 1];
}
}
}
graphics.DrawLines(crimsonPen, points);
}
}
}
Here I use my recursive method to draw figures
private void drawButton_Click(object sender, EventArgs e)
{
canvas.Refresh();
count = 0;
if (Int32.TryParse(stepsTextBox.Text, out steps))
{
sideLenght = (float)((canvas.Width - 100) / (Math.Pow(2, steps) - 1));
fragments = new Fragment[steps];
drawCurve();
}
else
{
MessageBox.Show("Wow, incorrect input", "Try again");
}
}
private void drawCurve()
{
if (count < steps)
{
fragments[count] = new Fragment(count, graphics);
++count;
drawCurve();
}
}
I've tried to rotate points around figure center and use next code but the rotation is incorrect
public PointF rotatePoint(PointF pointToRotate)
{
pointToRotate.X = (float)(Math.Cos(180 * Math.PI / 180) * (pointToRotate.X - centerPoint.X) -
Math.Sin(180 * Math.PI / 180) * (pointToRotate.Y - centerPoint.Y) +
centerPoint.X);
pointToRotate.Y = (float)(Math.Sin(0 * Math.PI / 180) * (pointToRotate.X - centerPoint.X) +
Math.Cos(0 * Math.PI / 180) * (pointToRotate.Y - centerPoint.Y) +
centerPoint.Y);
return pointToRotate;
}
The problem is that you are using the X co-ordinate that you have already rotated when you calculate the rotated Y co-ordinate. Use a temp variable to avoid this:
public PointF rotatePoint(PointF pointToRotate)
{
float rotatedX = (float)(Math.Cos(180 * Math.PI / 180) * (pointToRotate.X - centerPoint.X) -
Math.Sin(180 * Math.PI / 180) * (pointToRotate.Y - centerPoint.Y) +
centerPoint.X);
pointToRotate.Y = (float)(Math.Sin(0 * Math.PI / 180) * (pointToRotate.X - centerPoint.X) +
Math.Cos(0 * Math.PI / 180) * (pointToRotate.Y - centerPoint.Y) +
centerPoint.Y);
pointToRotate.X = rotatedX;
return pointToRotate;
}
I'm getting a problem with my terrain manager where I get these weirdly spiked columns instead of normal terrain (if there is a normal with this kind of generation) any ideas?
Image : http://bit.ly/1IVVsET
Code :
public class TerrainManager : MonoBehaviour, ICameraObserver {
public Terrain mainTerrain;
public float terrainChangeRate = 0.0001f;
public int brushArea = 20;
public static int viewDistance = 9;
public static Vector2 VECTOR_WILDCARD = new Vector2(-10000, -10000);
int resolutionX;
int resolutionY;
float[,] heights;
int heightEdit = 1;
//Chunks
List<Vector2> loadedChunks = new List<Vector2>();
Vector2[] visibleChunks = null;
Terrain[] chunkGraphics = new Terrain[viewDistance];
Vector2 curChunkIndex = new Vector2();
int chunkSizeX = 256;
int chunkSizeY = 256;
// Use this for initialization
void Start () {
resolutionX = mainTerrain.terrainData.heightmapWidth;
resolutionY = mainTerrain.terrainData.heightmapHeight;
heights = mainTerrain.terrainData.GetHeights(0, 0, resolutionX, resolutionY);
Camera.main.GetComponent<RTSCamera>().Subscribe(this);
GameObject world = new GameObject();
world.name = "World";
for (int i = 0; i < viewDistance; i++)
{
GameObject go = new GameObject();
go.name = "Chunk_" + i;
go.transform.SetParent(world.transform);
chunkGraphics[i] = go.AddComponent<Terrain>();
chunkGraphics[i].terrainData = new TerrainData();
go.AddComponent<TerrainCollider>().terrainData = chunkGraphics[i].terrainData;
chunkGraphics[i].terrainData.size = new Vector3((int)(chunkSizeX / 4), 600, (int)(chunkSizeY / 4));
chunkGraphics[i].terrainData.heightmapResolution = (int)(chunkSizeX / 2);
}
onCameraMove(Camera.main.transform.position);
}
// Update is called once per frame
void Update () {
if (Input.GetMouseButton(0))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
editTerrainHeight(hit.point, terrainChangeRate, brushArea);
}
}
}
void editTerrainHeight(Vector3 position, float amount, int diameter)
{
int terrainPosX = (int)((position.x / mainTerrain.terrainData.size.x) * resolutionX);
int terrainPosY = (int)((position.z / mainTerrain.terrainData.size.z) * resolutionY);
float[,] heightChange = new float[diameter, diameter];
int radius = diameter / 2;
if (Input.GetKey(KeyCode.LeftShift))
{
heightEdit = -1;
}
else
{
heightEdit = 1;
}
amount = amount * heightEdit;
for (int x = 0; x < diameter; x++)
{
for (int y = 0; y < diameter; y++)
{
int x2 = x - radius;
int y2 = y - radius;
heightChange[y, x] = heights[terrainPosY + y2, terrainPosX + x2] + amount;
heights[terrainPosY + y2, terrainPosX + x2] = heightChange[y, x];
}
}
mainTerrain.terrainData.SetHeights(terrainPosX - radius, terrainPosY - radius, heightChange);
}
public void onCameraMove(Vector3 newCameraPosition)
{
int chunkIndexX = Mathf.FloorToInt(newCameraPosition.x / chunkSizeX);
int chunkIndexY = Mathf.FloorToInt(newCameraPosition.z / chunkSizeY);
if (curChunkIndex.x == chunkIndexX && curChunkIndex.y == chunkIndexY)
{
return;
}
curChunkIndex.x = chunkIndexX;
curChunkIndex.y = chunkIndexY;
Vector2[] newVisibleChunks = new Vector2[viewDistance];
newVisibleChunks[0] = new Vector2(chunkIndexX - 1, chunkIndexY +1);
newVisibleChunks[1] = new Vector2(chunkIndexX, chunkIndexY +1);
newVisibleChunks[2] = new Vector2(chunkIndexX + 1, chunkIndexY +1);
newVisibleChunks[3] = new Vector2(chunkIndexX -1, chunkIndexY);
newVisibleChunks[4] = new Vector2(chunkIndexX, chunkIndexY);
newVisibleChunks[5] = new Vector2(chunkIndexX + 1, chunkIndexY);
newVisibleChunks[6] = new Vector2(chunkIndexX - 1, chunkIndexY -1);
newVisibleChunks[7] = new Vector2(chunkIndexX, chunkIndexY -1);
newVisibleChunks[8] = new Vector2(chunkIndexX + 1, chunkIndexY -1);
Terrain[] newChunkGraphics = new Terrain[viewDistance];
List<int> freeTerrains = new List<int>();
List<int> loadingIndexes = new List<int>();
for (int i = 0; i < viewDistance; i++)
{
bool found = false;
for (int j = 0; j < viewDistance; j++)
{
if (visibleChunks == null)
{
break;
}
if (newVisibleChunks[i].Equals(visibleChunks[j]))
{
visibleChunks[j] = VECTOR_WILDCARD;
newChunkGraphics[i] = chunkGraphics[j];
found = true;
break;
}
}
if (!found)
{
loadingIndexes.Add(i);
}
}
if (visibleChunks != null)
{
for (int i = 0; i < viewDistance; i++)
{
if (visibleChunks[i] != VECTOR_WILDCARD)
{
freeTerrains.Add(i);
saveChunkToMemory(chunkGraphics[i], visibleChunks[i]);
}
}
}
else
{
for (int i = 0; i < viewDistance; i++)
{
freeTerrains.Add(i);
}
}
for (int i = 0; i < loadingIndexes.Count; i++)
{
loadChunkFromMemory(newVisibleChunks[loadingIndexes[i]], freeTerrains[i]);
newChunkGraphics[loadingIndexes[i]] = chunkGraphics[freeTerrains[i]];
}
visibleChunks = newVisibleChunks;
chunkGraphics = newChunkGraphics;
}
void loadChunkFromMemory(Vector2 cordIndex, int graphicIndex)
{
bool found = false;
foreach (Vector2 v in loadedChunks)
{
if (v == cordIndex)
{
found = true;
break;
}
}
GameObject terrainGO;
if (!found)
{
terrainGO = generateChunk(cordIndex, graphicIndex);
}
else
{
//Load Chunk from Memory
Debug.Log("Loading Chunk(" + cordIndex.x + "," + cordIndex.y + ")");
terrainGO = chunkGraphics[graphicIndex].gameObject;
}
terrainGO.transform.position = new Vector3(chunkSizeX * cordIndex.x, 0, chunkSizeY * cordIndex.y);
}
GameObject generateChunk(Vector2 cordIndex, int graphicIndex)
{
GameObject terrainGO = chunkGraphics[graphicIndex].gameObject;
loadedChunks.Add(cordIndex);
setTerrainHeightMap(terrainGO.GetComponent<Terrain>(), cordIndex);
return terrainGO;
}
void setTerrainHeightMap(Terrain terrain, Vector2 cordIndex)
{
float[,] heights = new float[terrain.terrainData.heightmapHeight, terrain.terrainData.heightmapWidth];
heights[0, 0] = 0.5f;
heights[terrain.terrainData.heightmapWidth - 1, 0] = 0.5f;
heights[0, terrain.terrainData.heightmapHeight - 1] = 0.5f;
heights[terrain.terrainData.heightmapWidth - 1, terrain.terrainData.heightmapHeight - 1] = 0.5f;
heights = diamondSquare(heights, 0, 0, terrain.terrainData.heightmapWidth - 1, 0);
terrain.terrainData.SetHeights(0, 0, heights);
}
float[,] getTerrainHeightMap(Vector2 cordIndex)
{
return null;
}
float[,] diamondSquare(float[,] heights, int offSetX, int offSetY, int squareSize, int depth)
{
if (squareSize == 1)
{
return heights;
}
float topLeft = heights[offSetY, offSetX];
float topRight = heights[offSetY, offSetX + squareSize];
float bottomLeft = heights[offSetY + squareSize, offSetX];
float bottomRight = heights[offSetY + squareSize, offSetX + squareSize];
int size = squareSize / 2;
if (topLeft == 0 || topRight == 0 || bottomLeft == 0 || bottomRight == 0)
{
Debug.LogError("One or more Corner Seeds have not been set..");
}
if (heights[offSetY + size, offSetX + size] == 0)
{
heights[offSetY + size, offSetX + size] = getRandomHeight(depth + (int)averagePoints(topLeft, topRight, bottomLeft, bottomRight));
}
float centrePoint = heights[offSetY + size, offSetX + size];
//left Diamond
float runningAverage = averagePoints(topLeft, centrePoint, bottomLeft);
if (offSetX - size > 0 && heights[offSetY + size, offSetX - size] != 0)
{
runningAverage = averagePoints(topLeft, centrePoint, bottomLeft, heights[offSetY + size, offSetX - size]);
}
if (heights[offSetY + size, offSetX] == 0)
{
heights[offSetY + size, offSetX] = runningAverage + getRandomHeight(depth);
}
//right Diamond
runningAverage = averagePoints(topRight, centrePoint, bottomRight);
if (offSetX + (squareSize * 1.5f) < heights.GetLength(1) && heights[offSetY + size, offSetX + (int)(squareSize * 1.5f)] != 0)
{
runningAverage = averagePoints(topRight, centrePoint, bottomRight, heights[offSetY + size, offSetX + (int)(squareSize * 1.5f)]);
}
if (heights[offSetY + size, offSetX + squareSize] == 0)
{
heights[offSetY + size, offSetX + squareSize] = runningAverage + getRandomHeight(depth);
}
//top Diamond
runningAverage = averagePoints(topLeft, centrePoint, topRight);
if (offSetY - size > 0 && heights[offSetY - size, offSetX + size] != 0)
{
runningAverage = averagePoints(topLeft, centrePoint, topRight, heights[offSetY - size, offSetX + size]);
}
if (heights[offSetY, offSetX + size] == 0)
{
heights[offSetY, offSetX + size] = runningAverage + getRandomHeight(depth);
}
//bottom Diamond
runningAverage = averagePoints(bottomRight, centrePoint, bottomLeft);
if (offSetY + (squareSize * 1.5f) < heights.GetLength(0) && heights[offSetY + (int)(squareSize * 1.5f), offSetX + size] != 0)
{
runningAverage = averagePoints(bottomRight, centrePoint, topRight, heights[offSetY + (int)(squareSize * 1.5f), offSetX + size]);
}
if (heights[offSetY + squareSize, offSetX + size] == 0)
{
heights[offSetY + squareSize, offSetX + size] = runningAverage + getRandomHeight(depth);
}
heights = diamondSquare(heights, offSetX, offSetY, size, depth + 1);
heights = diamondSquare(heights, offSetX + size, offSetY, size, depth + 1);
heights = diamondSquare(heights, offSetX, offSetY + size, size, depth + 1);
heights = diamondSquare(heights, offSetX + size, offSetY + size, size, depth + 1);
return heights;
}
float averagePoints(float p1, float p2, float p3, float p4)
{
return (p1 + p2 + p3 + p4) * 0.25f;
}
float averagePoints(float p1, float p2, float p3)
{
return (p1 + p2 + p3) * 0.3333f;
}
float getRandomHeight(int depth)
{
return Random.Range(-0.1f, 0.0f) / Mathf.Pow(2, depth);
}
void saveChunkToMemory(Terrain chunk, Vector2 index)
{
Debug.Log("Unloading Chunk(" + index.x + "," + index.y + ")");
}
}
So after a few more hours here is a fix :D
public class TerrainManager : MonoBehaviour, ICameraObserver {
public Terrain mainTerrain;
public float terrainChangeRate = 0.0001f;
public int brushArea = 20;
public static int viewDistance = 9;
public static Vector2 VECTOR_WILDCARD = new Vector2(-10000, -10000);
int resolutionX;
int resolutionY;
float[,] heights;
int heightEdit = 1;
//Chunks
List<Vector2> loadedChunks = new List<Vector2>();
Vector2[] visibleChunks = null;
Terrain[] chunkGraphics = new Terrain[viewDistance];
Vector2 curChunkIndex = new Vector2();
int chunkSizeX = 256;
int chunkSizeY = 256;
// Use this for initialization
void Start () {
resolutionX = mainTerrain.terrainData.heightmapWidth;
resolutionY = mainTerrain.terrainData.heightmapHeight;
heights = mainTerrain.terrainData.GetHeights(0, 0, resolutionX, resolutionY);
Camera.main.GetComponent<RTSCamera>().Subscribe(this);
GameObject world = new GameObject();
world.name = "World";
for (int i = 0; i < viewDistance; i++)
{
GameObject go = new GameObject();
go.name = "Chunk_" + i;
go.transform.SetParent(world.transform);
chunkGraphics[i] = go.AddComponent<Terrain>();
chunkGraphics[i].terrainData = new TerrainData();
go.AddComponent<TerrainCollider>().terrainData = chunkGraphics[i].terrainData;
chunkGraphics[i].terrainData.size = new Vector3((int)(chunkSizeX / 4), 600, (int)(chunkSizeY / 4));
chunkGraphics[i].terrainData.heightmapResolution = (int)(chunkSizeX / 2);
}
onCameraMove(Camera.main.transform.position);
}
// Update is called once per frame
void Update () {
if (Input.GetMouseButton(0))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
editTerrainHeight(hit.point, terrainChangeRate, brushArea);
}
}
}
void editTerrainHeight(Vector3 position, float amount, int diameter)
{
int terrainPosX = (int)((position.x / mainTerrain.terrainData.size.x) * resolutionX);
int terrainPosY = (int)((position.z / mainTerrain.terrainData.size.z) * resolutionY);
float[,] heightChange = new float[diameter, diameter];
int radius = diameter / 2;
if (Input.GetKey(KeyCode.LeftShift))
{
heightEdit = -1;
}
else
{
heightEdit = 1;
}
amount = amount * heightEdit;
for (int x = 0; x < diameter; x++)
{
for (int y = 0; y < diameter; y++)
{
int x2 = x - radius;
int y2 = y - radius;
heightChange[y, x] = heights[terrainPosY + y2, terrainPosX + x2] + amount;
heights[terrainPosY + y2, terrainPosX + x2] = heightChange[y, x];
}
}
mainTerrain.terrainData.SetHeights(terrainPosX - radius, terrainPosY - radius, heightChange);
}
public void onCameraMove(Vector3 newCameraPosition)
{
//Debug.Log ("Camera Moved");
int chunkIndexX = Mathf.FloorToInt (newCameraPosition.x / chunkSizeX);
int chunkIndexY = Mathf.FloorToInt (newCameraPosition.z / chunkSizeY);
if (curChunkIndex.x == chunkIndexX && curChunkIndex.y == chunkIndexY) {
return;
}
curChunkIndex.x = chunkIndexX;
curChunkIndex.y = chunkIndexY;
//Debug.Log ("Chunk Index ( " + chunkIndexX + ", " + chunkIndexY + " )");
Vector2[] newVisibleChunks = new Vector2[9];
newVisibleChunks [0] = new Vector2 (chunkIndexX -1, chunkIndexY +1);
newVisibleChunks [1] = new Vector2 (chunkIndexX, chunkIndexY +1);
newVisibleChunks [2] = new Vector2 (chunkIndexX +1, chunkIndexY +1);
newVisibleChunks [3] = new Vector2 (chunkIndexX -1, chunkIndexY);
newVisibleChunks [4] = new Vector2 (chunkIndexX, chunkIndexY);
newVisibleChunks [5] = new Vector2 (chunkIndexX +1, chunkIndexY);
newVisibleChunks [6] = new Vector2 (chunkIndexX -1, chunkIndexY -1);
newVisibleChunks [7] = new Vector2 (chunkIndexX, chunkIndexY -1);
newVisibleChunks [8] = new Vector2 (chunkIndexX +1, chunkIndexY -1);
Terrain[] newChunkGraphics = new Terrain[chunkGraphics.Length];
List<int> freeTerrains = new List<int>();
List<int> loadingIndexes = new List<int>();
//List<int> newIndex = new List<int>();
for (int i =0; i <9; i++) {
bool found = false;
for (int j =0; j <9; j++) {
if (visibleChunks == null)
break;
if (newVisibleChunks[i].Equals (visibleChunks[j])){
visibleChunks[j] = VECTOR_WILDCARD;
newChunkGraphics[i] = chunkGraphics[j];
found = true;
break;
}
}
if (!found){
loadingIndexes.Add (i);
}
}
if (visibleChunks != null) {
for (int i = 0; i< 9; i++) {
if (visibleChunks [i] != VECTOR_WILDCARD) {
freeTerrains.Add (i);
saveChunkToMemory (chunkGraphics [i], visibleChunks [i]);
}
}
} else {
for (int i = 0; i< 9; i++) {
freeTerrains.Add (i);
}
}
for (int i = 0; i < loadingIndexes.Count; i++) {
newChunkGraphics[loadingIndexes[i]] = chunkGraphics[freeTerrains[i]];
}
visibleChunks = newVisibleChunks;
chunkGraphics = newChunkGraphics;
for (int i = 0; i < loadingIndexes.Count; i++) {
loadChunkFromMemory(visibleChunks[loadingIndexes[i]], loadingIndexes[i]);
}
}
void loadChunkFromMemory(Vector2 cordIndex, int graphicIndex)
{
bool found = false;
foreach (Vector2 v in loadedChunks)
{
if (v == cordIndex)
{
found = true;
break;
}
}
GameObject terrainGO;
if (!found)
{
terrainGO = generateChunk(cordIndex, graphicIndex);
}
else
{
//Load Chunk from Memory
Debug.Log("Loading Chunk(" + cordIndex.x + "," + cordIndex.y + ")");
terrainGO = chunkGraphics[graphicIndex].gameObject;
}
terrainGO.transform.position = new Vector3(chunkSizeX * cordIndex.x, 0, chunkSizeY * cordIndex.y);
}
GameObject generateChunk(Vector2 cordIndex, int graphicIndex)
{
GameObject terrainGO = chunkGraphics[graphicIndex].gameObject;
loadedChunks.Add(cordIndex);
setTerrainHeightMap(terrainGO.GetComponent<Terrain>(), cordIndex);
return terrainGO;
}
void setTerrainHeightMap(Terrain terrain, Vector2 cordIndex)
{
float[,] heights = new float[terrain.terrainData.heightmapHeight, terrain.terrainData.heightmapWidth];
bool left = false;
bool right = false;
bool top = false;
bool bottom = false;
//left
float[,] hm = getTerrainHeightMap(new Vector2(cordIndex.x - 1, cordIndex.y));
if(hm != null)
{
left = true;
for (int i = 0; i < hm.GetLength(0); i++)
{
heights[i, 0] = hm[i, hm.GetLength(1) - 1];
}
}
//Right
hm = getTerrainHeightMap(new Vector2(cordIndex.x + 1, cordIndex.y));
if (hm != null)
{
right = true;
for (int i = 0; i < hm.GetLength(0); i++)
{
heights[i, heights.GetLength(1) - 1] = hm[i, 0];
}
}
hm = getTerrainHeightMap(new Vector2(cordIndex.x, cordIndex.y - 1));
if (hm != null)
{
top = true;
for (int i = 0; i < hm.GetLength(1); i++)
{
heights[i, 0] = hm[hm.GetLength(0) - 1, i];
}
}
hm = getTerrainHeightMap(new Vector2(cordIndex.x, cordIndex.y + 1));
if (hm != null)
{
bottom = true;
for (int i = 0; i < hm.GetLength(0); i++)
{
heights[heights.GetLength(1) - 1, i] = hm [0, i];
}
}
if (!top && !left)
{
heights[0, 0] = 0.2f;
}
if (!bottom && !left)
{
heights[terrain.terrainData.heightmapHeight - 1, 0] = 0.2f;
}
if (!top && !right)
{
heights[0, terrain.terrainData.heightmapWidth - 1] = 0.2f;
}
if (!bottom && !right)
{
heights[terrain.terrainData.heightmapHeight - 1, terrain.terrainData.heightmapWidth - 1] = 0.2f;
}
heights[0, 0] = 0.2f;
heights[terrain.terrainData.heightmapHeight - 1, 0] = 0.2f;
heights[0, terrain.terrainData.heightmapWidth - 1] = 0.2f;
heights[terrain.terrainData.heightmapHeight - 1, terrain.terrainData.heightmapWidth - 1] = 0.2f;
heights = diamondSquare(heights, 0, 0, terrain.terrainData.heightmapWidth - 1, 0);
terrain.terrainData.SetHeights(0, 0, heights);
}
float[,] getTerrainHeightMap(Vector2 cordIndex)
{
if (loadedChunks.Contains(cordIndex))
{
for (int i = 0; i < visibleChunks.Length; i++)
{
if (visibleChunks[i].x == cordIndex.x && visibleChunks[i].y == cordIndex.y)
{
return chunkGraphics[i].terrainData.GetHeights(0, 0, chunkGraphics[i].terrainData.heightmapWidth, chunkGraphics[i].terrainData.heightmapWidth);
}
}
return loadHeightMapFromMemory(cordIndex);
}
else
{
return null;
}
}
float[,] loadHeightMapFromMemory(Vector2 cordIndex)
{
return null;
}
float[,] diamondSquare(float[,] heights, int offSetX, int offSetY, int squareSize, int depth)
{
if (squareSize == 1)
return heights;
float topLeft = heights[offSetY, offSetX];
float topRight = heights[offSetY, offSetX + squareSize];
float bottomLeft = heights[offSetY + squareSize, offSetX];
float bottomRight = heights[offSetY + squareSize, offSetX + squareSize];
if (topLeft == 0 || topRight == 0 || bottomLeft == 0 || bottomRight == 0)
{
Debug.LogError("One or more Corner Seed Values is not set");
}
if (heights[offSetY + (squareSize / 2), offSetX + (squareSize / 2)] == 0)
{
heights[offSetY + (squareSize / 2), offSetX + (squareSize / 2)] = getRandomHeight(depth) + averagePoints(topLeft, topRight, bottomLeft, bottomRight);
}
float centrePoint = heights[offSetY + (squareSize / 2), offSetX + (squareSize / 2)];
//left diamond
float runningAverage = averagePoints(topLeft, centrePoint, bottomLeft);
if (offSetX - (squareSize / 2) > 0 && heights[offSetY + (squareSize / 2), offSetX - (squareSize / 2)] != 0)
{
runningAverage = averagePoints(topLeft, centrePoint, bottomLeft, heights[offSetY + (squareSize / 2), offSetX - (squareSize / 2)]);
}
if (heights[offSetY + (squareSize / 2), offSetX] == 0)
{
heights[offSetY + (squareSize / 2), offSetX] = runningAverage + getRandomHeight(depth);
}
//right diamond
runningAverage = averagePoints(topRight, centrePoint, bottomRight);
if (offSetX + (squareSize * 1.5f) < heights.GetLength(1) && heights[offSetY + (squareSize / 2), offSetX + (int)(squareSize * 1.5f)] != 0)
{
runningAverage = averagePoints(topRight, centrePoint, bottomRight, heights[offSetY + (squareSize / 2), offSetX + (int)(squareSize * 1.5f)]);
}
if (heights[offSetY + (squareSize / 2), offSetX + squareSize] == 0)
{
heights[offSetY + (squareSize / 2), offSetX + squareSize] = runningAverage + getRandomHeight(depth);
}
//top diamond
runningAverage = averagePoints(topLeft, centrePoint, topRight);
if (offSetY - (squareSize / 2) > 0 && heights[offSetY - (squareSize / 2), offSetX + (squareSize / 2)] != 0)
{
runningAverage = averagePoints(topLeft, centrePoint, topRight, heights[offSetY - (squareSize / 2), offSetX + (squareSize / 2)]);
}
if (heights[offSetY, offSetX + (squareSize / 2)] == 0)
{
heights[offSetY, offSetX + (squareSize / 2)] = runningAverage + getRandomHeight(depth);
}
//bottom diamond
runningAverage = averagePoints(bottomRight, centrePoint, bottomLeft);
if (offSetY + (int)(squareSize * 1.5f) < heights.GetLength(0) && heights[offSetY + (int)(squareSize * 1.5f), offSetX + (squareSize / 2)] != 0)
{
runningAverage = averagePoints(bottomRight, centrePoint, topRight, heights[offSetY + (int)(squareSize * 1.5f), offSetX + (squareSize / 2)]);
}
if (heights[offSetY + squareSize, offSetX + (squareSize / 2)] == 0)
{
heights[offSetY + squareSize, offSetX + (squareSize / 2)] = runningAverage + getRandomHeight(depth);
}
heights = diamondSquare(heights, offSetX, offSetY, squareSize / 2, depth + 1);//top left
heights = diamondSquare(heights, offSetX + (squareSize / 2), offSetY, squareSize / 2, depth + 1);//top right
heights = diamondSquare(heights, offSetX, offSetY + (squareSize / 2), squareSize / 2, depth + 1);//bottom left
heights = diamondSquare(heights, offSetX + (squareSize / 2), offSetY + (squareSize / 2), squareSize / 2, depth + 1);//bottom right'
return heights;
}
float averagePoints(float p1, float p2, float p3, float p4)
{
return (p1 + p2 + p3 + p4) / 4;
}
float averagePoints(float p1, float p2, float p3)
{
return (p1 + p2 + p3) / 3;
}
float getRandomHeight(int depth)
{
return Random.Range(-0.1f, 0.1f) / Mathf.Pow(2, depth);
}
void saveChunkToMemory(Terrain chunk, Vector2 index)
{
Debug.Log("Unloading Chunk(" + index.x + "," + index.y + ")");
}
I'm trying to implement an N body simulation in C# using either Runge Kutta 4 or Velocity Verlet integration algorithms.
Before I move to a bigger number of particles, I wanted to test the simulation by modeling the earth's orbit around the sun, however, instead of the elliptical orbit, I get a weird spiral for some reason.
I can't figure out the problem since I made a simpler simulation of the solar system using the same algorithms where the sun was fixed in position and everything worked perfectly. The integrators work perfectly because it doesn't matter which one I use, I get the spiral with both.
Any help would be appreciated.
Here's the code:
class NBODY
{
public static double G = 4 * Math.PI * Math.PI;
class Particle
{
public double[] r; // position vector
public double[] v; // velocity vector
public double mass;
//constructor
public Particle() {}
public Particle(double x, double y, double z, double vx, double vy, double vz, double m)
{
this.r = new double[3];
this.v = new double[3];
this.r[0] = x;
this.r[1] = y;
this.r[2] = z;
this.v[0] = vx;
this.v[1] = vy;
this.v[2] = vz;
this.mass = m;
}
public void Update(Particle[] particles, double t, double h, int particleNumber)
{
RungeKutta4(particles, t, h, particleNumber);
}
private double acc(double r, Particle[] particles, int particleNumber, double[] r_temp, int l)
{
// dv/dt = f(x) = -G * m_i * (x - x_i) / [(x - x_i)^2 + (y - y_i)^2 + (z - z_i)^2]^(3/2)
double sum = 0;
switch (l)
{
case 0:
for (int i = 0; i < particles.Length; i++)
if (i != particleNumber)
sum += particles[i].mass * (r - particles[i].r[l]) / Math.Pow( Math.Pow(r - particles[i].r[l], 2)
+ Math.Pow(r_temp[1] - particles[i].r[1], 2) + Math.Pow(r_temp[2] - particles[i].r[2], 2), 1.5);
break;
case 1:
for (int i = 0; i < particles.Length; i++)
if (i != particleNumber)
sum += particles[i].mass * (r - particles[i].r[l]) / Math.Pow(Math.Pow(r - particles[i].r[l], 2)
+ Math.Pow(r_temp[0] - particles[i].r[0], 2) + Math.Pow(r_temp[2] - particles[i].r[2], 2), 1.5);
break;
case 2:
for (int i = 0; i < particles.Length; i++)
if (i != particleNumber)
sum += particles[i].mass * (r - particles[i].r[l]) / Math.Pow(Math.Pow(r - particles[i].r[l], 2)
+ Math.Pow(r_temp[0] - particles[i].r[0], 2) + Math.Pow(r_temp[1] - particles[i].r[1], 2), 1.5);
break;
}
return -G * sum;
}
private void RungeKutta4(Particle[] particles, double t, double h, int particleNumber)
{
//current position of the particle is saved in a vector
double[] r_temp = new double[3];
for (int j = 0; j < 3; j++)
r_temp[j] = this.r[j];
//loop going over all the coordinates and updating each using RK4 algorithm
for (int l = 0; l < 3; l++)
{
double[,] k = new double[4, 2];
k[0, 0] = this.v[l]; //k1_r
k[0, 1] = acc(this.r[l], particles, particleNumber, r_temp, l); //k1_v
k[1, 0] = this.v[l] + k[0, 1] * 0.5 * h; //k2_r
k[1, 1] = acc(this.r[l] + k[0, 0] * 0.5 * h, particles, particleNumber, r_temp, l); //k2_v
k[2, 0] = this.v[l] + k[1, 1] * 0.5 * h; //k3_r
k[2, 1] = acc(this.r[l] + k[1, 0] * 0.5 * h, particles, particleNumber, r_temp, l); //k3_v
k[3, 0] = this.v[l] + k[2, 1] * h; //k4_r
k[3, 1] = acc(this.r[l] + k[2, 0] * h, particles, particleNumber, r_temp, l); //k4_v
this.r[l] += (h / 6.0) * (k[0, 0] + 2 * k[1, 0] + 2 * k[2, 0] + k[3, 0]);
this.v[l] += (h / 6.0) * (k[0, 1] + 2 * k[1, 1] + 2 * k[2, 1] + k[3, 1]);
}
}
/*
Velocity Verlet algorithm:
1. Calculate y(t+h) = y(t) + v(t)h + 0.5a(t)h*h
2. Derive a(t+h) from dv/dt = -y using y(t+h)
3. Calculate v(t+h) = v(t) + 0.5*(a(t) + a(t+h))*h
*/
private void VelocityVerlet(Particle[] particles, double t, double h, int particleNumber)
{
double[] r_temp = new double[3];
for (int j = 0; j < 3; j++)
r_temp[j] = this.r[j];
//loop going over all the coordinates and updating each using RK4 algorithm
for (int l = 0; l < 3; l++)
{
//position
this.r[l] += h * this.v[l] + 0.5 * h * h * acc(this.r[l], particles, particleNumber, r_temp, l);
//velocity
this.v[l] += 0.5 * h * (acc(r_temp[l], particles, particleNumber, r_temp,l)
+ acc(this.r[l], particles, particleNumber, r_temp,l));
}
}
}
static void Main(string[] args)
{
//output file
TextWriter output = new StreamWriter("ispis.txt");
// declarations of variables
Particle[] particles = new Particle[2];
particles[0] = new Particle(0, 0, 0, 0, 0, 0, 1); //sun
particles[1] = new Particle(1, 0, 0, 0, 6.28, 0, 3.003467E-06); //earth
int N = 200;
double h, t, tmax;
double[,,] x = new double[particles.Length, N, 3]; //output
// setting initial values, step size and max time tmax
h = 0.01; // the step size in years
tmax = h * N;
// initial time
t = 0;
int i = 0;
while (t <= tmax) {
//updates position of all particles
for (int z = 1; z < particles.Length; z++)
particles[z].Update(particles, t, h, z);
//saves the position for output
for (int j = 1; j < particles.Length ; j++)
for (int z = 0; z < 3; z++ )
x[j,i,z] = particles[j].r[z];
t += h;
i++;
}
//output to file
for (int k = 0; k < particles.Length; k++ )
{
for (int f = 0; f < 3; f++)
{
for (int l = 0; l < N; l++)
output.Write(string.Format("{0,-15:0.########},", x[k,l,f]));
output.Write(string.Format("\n\n"));
}
output.Write(string.Format("\n\n\n\n"));
}
output.Close();
}
}
And here's the plot of the output data for earth's orbit:
Your model calculates the gravity force between two particles twice: for the first particle the force is based on their original coordinates, and for the second particle it is based on an updated position of the first one. This is a clear violation of the Newton's 3rd law. You must precompute all the forces before any update.
Your problem with the orbital of Earth is because the Center of Gravity of the System Earth-Sun, if you want to see the Orbital stay in loops you need to set center Of Gravity In (x,y,z)=(0,0,0) ;
I have a C# code based on your code Above so :
public partial class Form1 : Form
{
static
int
x1,y1,x2,y2, x3, y3;//for the 3th particule
private void timer1_Tick_1(object sender, EventArgs e)
{Moveu();
Invalidate();
}
private void button1_Click_1(object sender, EventArgs e)
{
timer1.Enabled = !timer1.Enabled;
}
public Form1()
{
InitializeComponent();
Paint += new PaintEventHandler(paint);
MouseDown += new MouseEventHandler(mouse_Click);
MouseUp += new MouseEventHandler(mouse_up);
MouseMove += new MouseEventHandler(mouse_move);
// x y z vx vy vz m
particles[0] = new Particle( 0, 0, 0, 0, 0, 0, 1 ) ; //sun
particles[1] = new Particle( 1, 0, 0, 0, 6, 0, 0.03 ); //earth
// particles[2] = new Particle( 0, 2, 0, 0, 0, 0, 1 ); //planet
x1 = (int)(100 * particles[0].r[0] + 300);
y1 = (int)(100 * particles[0].r[1] + 300);
x2 = (int)(100 * particles[1].r[0] + 300);
y2 = (int)(100 * particles[1].r[1] + 300);
}
Particle[] particles = new Particle[2];
void Moveu()
{
double h, t;
// setting initial values, step size and max time tmax
h = 0.005; // the step size in years
// initial time
t = 0;
//updates position of --all-- particles ( z=0 not z=1 )
for (int z = 0; z < particles.Length; z++)
particles[z].RungeKutta4(particles, t, h, z);
x1 = (int)(100 * particles[0].r[0] + 300); // +300 just for render it in centre
y1 = (int)(100 * particles[0].r[1] + 300);
x2 = (int)(100 * particles[1].r[0] + 300);
y2 = (int)(100 * particles[1].r[1] + 300);
// x3 = (int)(100 * particles[2].r[0] + 300);
// y3 = (int)(100 * particles[2].r[1] + 300);
}
void paint(object s, PaintEventArgs e)
{
Graphics graf;
graf = CreateGraphics();
graf.FillEllipse(new SolidBrush(Color.AntiqueWhite), x1 + move.X, y1 + move.Y, 50, 50);
graf.FillEllipse(new SolidBrush(Color.Blue), x2 + move.X, y2 + move.Y, 10, 10);
// graf.FillEllipse(new SolidBrush(Color.Yellow), x3, y3, 20, 20);
}
class Particle
{
public double[] r; // position vector
public double[] v; // velocity vector
public double mass;
//constructor
public Particle() { }
public Particle(double x, double y, double z, double vx, double vy, double vz, double m)
{
this.r = new double[3];
this.v = new double[3];
this.r[0] = x;
this.r[1] = y;
this.r[2] = z;
this.v[0] = vx;
this.v[1] = vy;
this.v[2] = vz;
this.mass = m;
}
private double acc(double r, Particle[] particles, int particleNumber, double[] r_temp, int l)
{
// dv/dt = f(x) = -G * m_i * (x - x_i) / [(x - x_i)^2 + (y - y_i)^2 + (z - z_i)^2]^(3/2)
double sum = 0;
switch (l)
{
case 0:
for (int i = 0; i < particles.Length; i++)
if (i != particleNumber)
sum += particles[i].mass * (r - particles[i].r[l]) / Math.Pow(Math.Pow(r - particles[i].r[l], 2)
+ Math.Pow(r_temp[1] - particles[i].r[1], 2) + Math.Pow(r_temp[2] - particles[i].r[2], 2), 1.5);
break;
case 1:
for (int i = 0; i < particles.Length; i++)
if (i != particleNumber)
sum += particles[i].mass * (r - particles[i].r[l]) / Math.Pow(Math.Pow(r - particles[i].r[l], 2)
+ Math.Pow(r_temp[0] - particles[i].r[0], 2) + Math.Pow(r_temp[2] - particles[i].r[2], 2), 1.5);
break;
case 2:
for (int i = 0; i < particles.Length; i++)
if (i != particleNumber)
sum += particles[i].mass * (r - particles[i].r[l]) / Math.Pow(Math.Pow(r - particles[i].r[l], 2)
+ Math.Pow(r_temp[0] - particles[i].r[0], 2) + Math.Pow(r_temp[1] - particles[i].r[1], 2), 1.5);
break;
}
return -G * sum;
}
public void RungeKutta4(Particle[] particles, double t, double h, int particleNumber)
{
//current position of the particle is saved in a vector
double[] r_temp = new double[3];
for (int j = 0; j < 3; j++)
r_temp[j] = this.r[j];
//loop going over all the coordinates and updating each using RK4 algorithm
for (int l = 0; l < 3; l++)
{
double[,] k = new double[4, 2];
k[0, 0] = this.v[l]; //k1_r
k[0, 1] = acc(this.r[l], particles, particleNumber, r_temp, l); //k1_v
k[1, 0] = this.v[l] + k[0, 1] * 0.5 * h; //k2_r
k[1, 1] = acc(this.r[l] + k[0, 0] * 0.5 * h, particles, particleNumber, r_temp, l); //k2_v
k[2, 0] = this.v[l] + k[1, 1] * 0.5 * h; //k3_r
k[2, 1] = acc(this.r[l] + k[1, 0] * 0.5 * h, particles, particleNumber, r_temp, l); //k3_v
k[3, 0] = this.v[l] + k[2, 1] * h; //k4_r
k[3, 1] = acc(this.r[l] + k[2, 0] * h, particles, particleNumber, r_temp, l); //k4_v
this.r[l] += (h / 6.0) * (k[0, 0] + 2 * k[1, 0] + 2 * k[2, 0] + k[3, 0]);
this.v[l] += (h / 6.0) * (k[0, 1] + 2 * k[1, 1] + 2 * k[2, 1] + k[3, 1]);
}
}
}
public static double G = 4 * Math.PI * Math.PI; //then time unite in years and length unite = distance between Earth and Sun and masse is the sun masse unite
void mouse_Click(object o, MouseEventArgs e)
{
dwn = new Point(e.X, e.Y);
if ("" + e.Button == "Left")
{
pos = move;
clicked = true;
}
}
void mouse_move(object o, MouseEventArgs e)
{
if (clicked)
{
move = new Point(e.X + pos.X - dwn.X, e.Y + pos.Y - dwn.Y);
Invalidate();
}
}
void mouse_up(object o, MouseEventArgs e)
{
clicked = false;
}
Point dwn, pos,move;
bool clicked;
}`you need to create a Timer and a button [![enter image description here][1]][1]
I'm making and OpenGL application that has MULTIPLE meshes that are described as lists of positions, normals, and uvs. I am binding these data to a vertex buffer but I was wondering how I would draw these meshes per frame without re-binding the vertex buffer. Correct me if I'm wrong, but isn't copying ~100KB of data to the vertex buffer slowish? How would I draw each mesh with separate transforms (position, rotation, scale). Thanks :) Here is my Mesh code:
using System;
using System.IO;
using OpenTK;
using OpenTK.Graphics.OpenGL;
public class Mesh
{
public Vector3[] positions;
public Vector3[] normals;
public Vector2[] uvs;
public Triangle[] triangles;
public int buffer;
public Mesh()
{
this.positions = new Vector3[0];
this.normals = new Vector3[0];
this.uvs = new Vector2[0];
this.triangles = new Triangle[0];
this.buffer = 0;
}
public Mesh(Vector3[] positions, Vector3[] normals, Vector2[] uvs, Triangle[] triangles, int buffer)
{
this.positions = positions;
this.normals = normals;
this.uvs = uvs;
this.triangles = triangles;
this.buffer = buffer;
}
public static Mesh fromFile(string fileName)
{
Mesh mesh = new Mesh();
BinaryReader binaryReader = new BinaryReader(new FileStream(fileName, FileMode.Open));
int positionCount = binaryReader.ReadInt32();
mesh.positions = new Vector3[positionCount];
for (int i = 0; i < positionCount; i++)
{
mesh.positions[i] = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
}
int normalCount = binaryReader.ReadInt32();
mesh.normals = new Vector3[normalCount];
for (int i = 0; i < normalCount; i++)
{
mesh.normals[i] = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
}
int uvCount = binaryReader.ReadInt32();
mesh.uvs = new Vector2[uvCount];
for (int i = 0; i < uvCount; i++)
{
mesh.uvs[i] = new Vector2(binaryReader.ReadSingle(), binaryReader.ReadSingle());
}
int triangleCount = binaryReader.ReadInt32();
mesh.triangles = new Triangle[triangleCount];
for (int i = 0; i < triangleCount; i++)
{
mesh.triangles[i] = new Triangle(binaryReader.ReadInt32(), binaryReader.ReadInt32(), binaryReader.ReadInt32(), binaryReader.ReadInt32(), binaryReader.ReadInt32(), binaryReader.ReadInt32(), binaryReader.ReadInt32(), binaryReader.ReadInt32(), binaryReader.ReadInt32());
}
binaryReader.Close();
return mesh;
}
public void toFile(string fileName)
{
BinaryWriter binaryWriter = new BinaryWriter(new FileStream(fileName, FileMode.OpenOrCreate));
binaryWriter.Write(positions.Length);
for (int i = 0; i < positions.Length; i++)
{
binaryWriter.Write(positions[i].X);
binaryWriter.Write(positions[i].Y);
binaryWriter.Write(positions[i].Z);
}
binaryWriter.Write(normals.Length);
for (int i = 0; i < normals.Length; i++)
{
binaryWriter.Write(normals[i].X);
binaryWriter.Write(normals[i].Y);
binaryWriter.Write(normals[i].Z);
}
binaryWriter.Write(uvs.Length);
for (int i = 0; i < uvs.Length; i++)
{
binaryWriter.Write(uvs[i].X);
binaryWriter.Write(uvs[i].Y);
}
binaryWriter.Write(triangles.Length);
for (int i = 0; i < triangles.Length; i++)
{
binaryWriter.Write(triangles[i].positionIndex0);
binaryWriter.Write(triangles[i].normalIndex0);
binaryWriter.Write(triangles[i].uvIndex0);
binaryWriter.Write(triangles[i].positionIndex1);
binaryWriter.Write(triangles[i].normalIndex1);
binaryWriter.Write(triangles[i].uvIndex1);
binaryWriter.Write(triangles[i].positionIndex2);
binaryWriter.Write(triangles[i].normalIndex2);
binaryWriter.Write(triangles[i].uvIndex2);
}
binaryWriter.Close();
}
public void draw(Transform transform)
{
float[] data = new float[triangles.Length * 24];
for (int i = 0; i < triangles.Length; i++)
{
data[(i * 9) + 0] = positions[triangles[i].positionIndex0].X;
data[(i * 9) + 1] = positions[triangles[i].positionIndex0].Y;
data[(i * 9) + 2] = positions[triangles[i].positionIndex0].Z;
data[(i * 9) + 3] = positions[triangles[i].positionIndex1].X;
data[(i * 9) + 4] = positions[triangles[i].positionIndex1].Y;
data[(i * 9) + 5] = positions[triangles[i].positionIndex1].Z;
data[(i * 9) + 6] = positions[triangles[i].positionIndex2].X;
data[(i * 9) + 7] = positions[triangles[i].positionIndex2].Y;
data[(i * 9) + 8] = positions[triangles[i].positionIndex2].Z;
data[(triangles.Length * 9) + (i * 9) + 0] = normals[triangles[i].normalIndex0].X;
data[(triangles.Length * 9) + (i * 9) + 1] = normals[triangles[i].normalIndex0].Y;
data[(triangles.Length * 9) + (i * 9) + 2] = normals[triangles[i].normalIndex0].Z;
data[(triangles.Length * 9) + (i * 9) + 3] = normals[triangles[i].normalIndex1].X;
data[(triangles.Length * 9) + (i * 9) + 4] = normals[triangles[i].normalIndex1].Y;
data[(triangles.Length * 9) + (i * 9) + 5] = normals[triangles[i].normalIndex1].Z;
data[(triangles.Length * 9) + (i * 9) + 6] = normals[triangles[i].normalIndex2].X;
data[(triangles.Length * 9) + (i * 9) + 7] = normals[triangles[i].normalIndex2].Y;
data[(triangles.Length * 9) + (i * 9) + 8] = normals[triangles[i].normalIndex2].Z;
data[(triangles.Length * 18) + (i * 6) + 0] = uvs[triangles[i].uvIndex0].X;
data[(triangles.Length * 18) + (i * 6) + 1] = uvs[triangles[i].uvIndex0].Y;
data[(triangles.Length * 18) + (i * 6) + 2] = uvs[triangles[i].uvIndex1].X;
data[(triangles.Length * 18) + (i * 6) + 3] = uvs[triangles[i].uvIndex1].Y;
data[(triangles.Length * 18) + (i * 6) + 4] = uvs[triangles[i].uvIndex2].X;
data[(triangles.Length * 18) + (i * 6) + 5] = uvs[triangles[i].uvIndex2].Y;
}
buffer = GL.GenBuffer();
GL.BindBuffer(BufferTarget.ArrayBuffer, buffer);
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(triangles.Length * 96), data, BufferUsageHint.StaticDraw);
//------------------------
//----------TODO----------
//------------------------
}
}
The last function, draw, is the one I'm working on.
The point is to have a single VBO per mesh that you load once and then just rebind as needed.
if you are in openGL 3.3+ you can collect all needed bindings for each mesh in a VAO per mesh: (pseudo-ish code)
class MeshBuffer{
int vao;
int vbo;
int numVertices;
void Dispose(){
if(vao==0)return;
GL.DeleteVertexArrays(1, ref vao);
GL.DeleteBuffers(1, ref vbo);
vao = 0;
vbo = 0;
}
void Bind(){
GL.BindVertexArray(vao);
}
void Unbind(){
GL.BindVertexArray(0);
}
void FillVBO(Mesh mesh){
Dispose();
GL.GenVertexArrays(1, out vao);
GL.GenBuffers(1, out vbo);
float[] data = new float[mesh.triangles.Length * 24];
//your for loop
GL.BindVertexArray(vao);
GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(triangles.Length * 96), data, BufferUsageHint.StaticDraw);
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, 0, 0);
GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, 0, triangles.Length * 9*4);
GL.VertexAttribPointer(2, 3, VertexAttribPointerType.Float, 0, triangles.Length * 18*4);
GL.BindVertexArray(0);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
}
}
Then to draw you just bind the MeshBuffer and load the transformation matrix into the relevant uniform.
int mvpMat = GL.GetUniformLocation(prog, "mvp");
GL.UseProgram(prog);
meshBuffer.Bind();
GL.UniformMatrix4(mvpMat, transform.Mat4());
GL.DrawArrays(GL_TRIANGLES, 0, meshBuffer.numVertices);