C# XNA 4.0 frame rate problems, possible .SetData missuse - c#

I have a problem regarding frame rate drop while trying to make real time 3D terrain changes. I use C#, XNA 4.0 and VS 2010. This is my old school project and time has come to finish it.
I already did terrain generation from image file, with all effects and stuff and it is running smoothly no matter what resolution image file is. Problem is with my terrain editor. I want it to be able to manually alter the terrain. I did that part too, but it works only if terrain size is equal or less than 128x128 pixels. If the terrain size is greater I start to get frame rate drops around 150x150 pixels, and it is completely unmanageable if terrain size is greater than 512x512 pixels.
I already tried several approaches:
tried to use threads, but then I get weird error saying something like "Draw method can be called in one thread at a time" or something similar, and that I can't resolve.
next I tried to use DynamicVertexBuffer and DynamicIndexBuffer. That helped a lot and now my code is working with acceptable frame rate for terrain size of up to 256x256 pixels.
Have a look at my code:
public void ChangeTerrain(float[,] heightData)
{
int x, y;
int v = 1;
if (currentMouseState.LeftButton == ButtonState.Pressed && currentMouseState.X < 512)
{
x = (int)currentMouseState.X / 2;
y = (int)currentMouseState.Y / 2;
if (x < 5)
x = 5;
if (x >= 251)
x = 251;
if (y < 5)
y = 5;
if (y >= 251)
y = 251;
for (int i = x - 4; i < x + 4; i++)
{
for (int j = y - 4; j < y + 4; j++)
{
if (i == x - 4 || i == x + 3 || j == y - 4 || j == y + 3)
v = 3;
else
v = 5;
if (heightData[i, j] < 210)
{
heightData[i, j] += v;
}
}
}
}
if (currentMouseState.RightButton == ButtonState.Pressed && currentMouseState.X < 512)
{
x = (int)currentMouseState.X / 2;
y = (int)currentMouseState.Y / 2;
if (x < 5)
x = 5;
if (x >= 251)
x = 251;
if (y < 5)
y = 5;
if (y >= 251)
y = 251;
for (int i = x - 4; i < x + 4; i++)
{
for (int j = y - 4; j < y + 4; j++)
{
if (heightData[i, j] > 0)
{
heightData[i, j] -= 1;
}
}
}
}
if (keyState.IsKeyDown(Keys.R))
{
for (int i = 0; i < 256; i++)
for (int j = 0; j < 256; j++)
heightData[i, j] = 0f;
}
SetUpTerrainVertices();
CalculateNormals();
terrainVertexBuffer.SetData(vertices, 0, vertices.Length);
}
I work with resolution of 1024x512 pixels, so I scale mouse position by 1/2 to get terrain position. I use left and right mouse button to alter terrain, i.e. to alter heightData from which 3D terrain is generated.
Last 3 lines create Vertices from new heightData, calculate Normals so shades could be applied and last line is just throwing Vertices data to Vertex Buffer.
Prior to that, I set up dynamic Vertex and Index buffer in LoadContent method and call initial Vertices and Indices setup. This method (ChangeTerrain) is called from Update method.
I did some debugging and found out that maximum size of vertices in most extreme case would be around 260000 +- few thousands. Is it possible that .SetData is so much time consuming it is causing frame rate drops? Or is it something else? How can I fix that and make my editor functioning normally for any terrain size?
Also, i red that I need to use this code with DynamicVertexBuffer, but I can't make it work in XNA 4.0.
terrainVertexBuffer.ContentLost += new EventHandler(TerrainVertexBufferContentLost);
public void TerrainVertexBufferContentLost()
{
terrainVertexBuffer(vertices, 0, vertices.Length, SetDataOptions.NoOverwrite);
}
Thanks for your help!
EDIT:
This is my SetUpTerrainVertices code:
private void SetUpTerrainVertices()
{
vertices = new VertexPositionNormalColored[terrainWidth * terrainLength];
for (int x = 0; x < terrainWidth; x++)
{
for (int y = 0; y < terrainLength; y++)
{
vertices[x + y * terrainWidth].Position = new Vector3(x, heightData[x, y], -y);
vertices[x + y * terrainWidth].Color = Color.Gray;
}
}
}
And my CalculateNormals
private void CalculateNormals()
{
for (int i = 0; i < vertices.Length; i++)
vertices[i].Normal = new Vector3(0, 0, 0);
for (int i = 0; i < indices.Length / 3; i++)
{
int index1 = indices[i * 3];
int index2 = indices[i * 3 + 1];
int index3 = indices[i * 3 + 2];
Vector3 side1 = vertices[index1].Position - vertices[index3].Position;
Vector3 side2 = vertices[index1].Position - vertices[index2].Position;
Vector3 normal = Vector3.Cross(side1, side2);
vertices[index1].Normal += normal;
vertices[index2].Normal += normal;
vertices[index3].Normal += normal;
}
for (int i = 0; i < vertices.Length; i++)
vertices[i].Normal.Normalize();
}
I set up vertex and index buffers in XNA LoadContent Method using lines:
terrainVertexBuffer = new DynamicVertexBuffer(device, VertexPositionNormalColored.VertexDeclaration, vertices.Length,
BufferUsage.None);
terrainIndexBuffer = new DynamicIndexBuffer(device, typeof(int), indices.Length, BufferUsage.None);
I call ChangeTerrain method from Update and this is how i Draw:
private void DrawTerrain(Matrix currentViewMatrix)
{
device.DepthStencilState = DepthStencilState.Default;
device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
effect.CurrentTechnique = effect.Techniques["Colored"];
Matrix worldMatrix = Matrix.Identity;
effect.Parameters["xWorld"].SetValue(worldMatrix);
effect.Parameters["xView"].SetValue(currentViewMatrix);
effect.Parameters["xProjection"].SetValue(projectionMatrix);
effect.Parameters["xEnableLighting"].SetValue(true);
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
device.Indices = terrainIndexBuffer;
device.SetVertexBuffer(terrainVertexBuffer);
device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertices.Length, 0, indices.Length / 3);
}
}
EDIT2:
Ok, I decided to go for your second suggestion and got into some problems. I modified my methods like this:
public void ChangeTerrain(Texture2D heightmap)
{
Color[] mapColors = new Color[256 * 256];
Color[] originalColors = new Color[256 * 256];
for (int i = 0; i < 256 * 256; i++)
originalColors[i] = new Color(0, 0, 0);
heightMap2.GetData(mapColors);
device.Textures[0] = null;
device.Textures[1] = null;
int x, y;
int v = 1;
if (currentMouseState.LeftButton == ButtonState.Pressed && currentMouseState.X < 512)
{
x = (int)currentMouseState.X / 2;
y = (int)currentMouseState.Y / 2;
if (x < 4)
x = 4;
if (x >= 251)
x = 251;
if (y < 4)
y = 4;
if (y >= 251)
y = 251;
for (int i = x-4; i < x+4; i++)
{
for (int j = y-4; j < y+4; j++)
{
if (i == x - 4 || i == x + 3 || j == y - 4 || j == y + 3)
v = 3;
else
v = 5;
if (mapColors[i + j * 256].R < 210)
{
mapColors[i + j * 256].R += (byte)(v);
mapColors[i + j * 256].G += (byte)(v);
mapColors[i + j * 256].B += (byte)(v);
}
heightMap2.SetData(mapColors);
}
}
}
if (currentMouseState.RightButton == ButtonState.Pressed && currentMouseState.X < 512)
{
x = (int)currentMouseState.X / 2;
y = (int)currentMouseState.Y / 2;
if (x < 4)
x = 4;
if (x >= 251)
x = 251;
if (y < 4)
y = 4;
if (y >= 251)
y = 251;
for (int i = x - 4; i < x + 4; i++)
{
for (int j = y - 4; j < y + 4; j++)
{
if (mapColors[i + j * 256].R > 0)
{
mapColors[i + j * 256].R -= 1;
mapColors[i + j * 256].G -= 1;
mapColors[i + j * 256].B -= 1;
}
heightMap2.SetData(mapColors);
}
}
}
if (keyState.IsKeyDown(Keys.R))
heightMap2.SetData(originalColors);
}
Generating flat surface - only once in LoadContent() method:
vertices are assigned only once
private void SetUpTerrainVertices()
{
for (int x = 0; x < terrainWidth; x++)
{
for (int y = 0; y < terrainLength; y++)
{
vertices[x + y * terrainWidth].Position = new Vector3(x, 0, -y);
vertices[x + y * terrainLength].Color = Color.Gray;
}
}
}
Draw method is same as previous, but with one extra line:
effect.Parameters["xTexture0"].SetValue(heightMap2);
also, I made new technique called Editor and it looks like this:
//------- Technique: Editor --------
struct EditorVertexToPixel
{
float4 Position : POSITION;
float4 Color : COLOR0;
float LightingFactor: TEXCOORD0;
float2 TextureColor : TEXCOORD1;
};
struct EditorPixelToFrame
{
float4 Color : COLOR0;
};
EditorVertexToPixel EditorVS( float4 inPos : POSITION, float4 inColor: COLOR, float3 inNormal: NORMAL, float2 inTextureColor: TEXCOORD1)
{
EditorVertexToPixel Output = (EditorVertexToPixel)0;
float4x4 preViewProjection = mul (xView, xProjection);
float4x4 preWorldViewProjection = mul (xWorld, preViewProjection);
float4 Height;
float4 position2 = inPos;
position2.y += Height;
Output.Color = inColor;
Output.Position = mul(position2, preWorldViewProjection);
Output.TextureColor = inTextureColor;
float3 Normal = normalize(mul(normalize(inNormal), xWorld));
Output.LightingFactor = 1;
if (xEnableLighting)
Output.LightingFactor = saturate(dot(Normal, -xLightDirection));
return Output;
}
EditorPixelToFrame EditorPS(EditorVertexToPixel PSIn)
{
EditorPixelToFrame Output = (EditorPixelToFrame)0;
//float4 height2 = tex2D(HeightSAmpler, PSIn.TextureColor);
float4 colorNEW = float4(0.1f, 0.1f, 0.6f, 1);
Output.Color = PSIn.Color * colorNEW;
Output.Color.rgb *= saturate(PSIn.LightingFactor) + xAmbient;
return Output;
}
technique Editor
{
pass Pass0
{
VertexShader = compile vs_3_0 EditorVS();
PixelShader = compile ps_3_0 EditorPS();
}
}
this code doesn't work because float4 Height is not set. What I wanted to do is to sample texture colors into float4 Height (using Sample), but I can not use sampler in VertexShader. I get error message "X4532 cannot map expression to vertex shader instruction set".
Then, I red that you can use SampleLevel in VertexShader to sample color data and thought I found solution, but I get strange error that is only documented in one Russian blog, but I can't speak or read Russian. Error is: "X4814 unexpected Alias on texture declaration"
Is there a way to sample colors in PixelShader and then pass them to VertexShader?
This could work cos I managed to set float4 Height to various values and it altered vertices height. Problem is, I don't know how to read texture color in VertexShader, or how to pass red texture color data from PixelShader to VertexShader.
EDIT3:
I think I found solution. Was searching the net and found out about tex2Dlod function to use as VertexShader texture sampler. But there are different syntax displayed and I can't make them work.
Can anyone point out on good HLSL literature to learn a bit about HLSL coding. This task seems pretty easy, but somehow, I can't make it to work.

Ok, so I can't offer "real" performance advice - because I haven't measured your code. And measuring is probably the most important part of performance optimisation - you need to be able to answer the questions: "am I slower than my performance target?" and "why am I slower than my target?"
That being said - here are the things that stand out to me as a seasoned developer:
This method (ChangeTerrain) is called from Update method
You should probably consider splitting that method up so that, rather than recreating your data every frame, it only does work when the terrain is actually changed.
vertices = new VertexPositionNormalColored[terrainWidth * terrainLength];
Allocating a new vertices buffer each frame is huge memory allocation (6MB at 512x512). This is going to put a big strain on the garbage collector - and I suspect this is the primary cause of your performance issues.
Given that you're about to set all the data in that array anyway, simply delete that line and the old data in the array will be overwritten.
Better yet, you could leave the data that doesn't change as-is, and only modify the vertices that are actually changed. In much the same way you are doing for heightData.
As part of this, it would be a very good idea to modify CalculateNormals so that, rather than having to rely on the index buffer and going through every triangle, it could calculate the indices of surrounding vertices (that form triangles) for any specific vertex - something you can do because vertices is ordered. Again, kind of like what you're doing for heightData.
terrainVertexBuffer.SetData(vertices, 0, vertices.Length);
This is sending the full 6MB buffer to the GPU. There are versions of SetData that only send a subset of the full buffer to the GPU. You should probably try and use these.
Just remember that each SetData call comes with some overhead, so don't get too granular. It's probably best to have just one call per vertex buffer, even if that means some unmodified parts of the buffer must be sent.
This is probably the only place where "chunking" your terrain would have an significant impact, as it would allow you to specify a tighter region for each SetData call - allowing you to send less unmodified data. (I'll leave figuring out why this is the case as an exercise.)
(You're already using DynamicVertexBuffer, which is good, because this means the GPU will automatically handle the pipeline issues of having its buffer changed on-the-fly.)
Finally, if performance is still an issue, you could consider a different approach entirely.
One example might be to offload the calculation of the geometry to the GPU. You'd convert your heightData to a texture, and use a vertex shader (with a flat grid of vertices as input) to sample that texture and output the appropriate positions and normals.
One big advantage of this approach is that heightData can be a lot smaller (0.25MB at 512x512) than your vertex buffer - that's much less data that the CPU needs to process and send to the GPU.

Related

Raycast not capturing all Vector coordinates

I have a gameobject that occupies the whole screen just for testing purposes. I'm drawing a line btw. What I'm trying to achieve is if the mouse position hits a gameobject it will store the vector2 coordinates in a list. But raycast is not storing all the coordinates. Below is my code
private void Update()
{
if (Input.GetMouseButton(0))
{
Vector2 mousePos = Input.mousePosition;
Vector2 Pos = _camera.ScreenToWorldPoint(mousePos);
if(!mousePositions.Contains(Pos))
mousePositions.Add(Pos);
if (Physics.Raycast(Camera.main.ScreenPointToRay(mousePos), out RaycastHit hit))
{
Vector2 textureCoord = hit.textureCoord;
int pixelX = (int)(textureCoord.x * _templateDirtMask.width);
int pixelY = (int)(textureCoord.y * _templateDirtMask.height);
Vector2Int paintPixelPosition = new Vector2Int(pixelX, pixelY);
if (!linePositions.Contains(paintPixelPosition))
linePositions.Add(paintPixelPosition);
foreach (Vector2Int pos in linePositions)
{
int pixelXOffset = pos.x - (_brush.width / 2);
int pixelYOffset = pos.y - (_brush.height / 2);
for (int x = 0; x < _brush.width; x++)
{
for (int y = 0; y < _brush.height; y++)
{
_templateDirtMask.SetPixel(
pixelXOffset + x,
pixelYOffset + y,
Color.black
);
}
}
}
_templateDirtMask.Apply();
}
}
}
Everytime I checked the element count mousePositions are always greater than linePositions. I don't know what's causing this
the element count mousePositions are always greater than linePosition
well it is quite simple: In
int pixelX = (int)(textureCoord.x * _templateDirtMask.width);
int pixelY = (int)(textureCoord.y * _templateDirtMask.height);
you are casting to int and cut off any decimals after the comma (basically like doing Mathf.FloorToInt).
So you can totally have multiple mouse positions which result in float pixel positions like e.g.
1.2, 1.2
1.4, 1.7
1.02, 1.93
...
all these will map to
Vector2Int paintPixelPosition = new Vector2Int(1, 1);
Besides, you might want to look at some better line drawing algorithms like e.g. this simple one
And then note that calling SetPixel repeatedly is quite expensive. You want to do a single SetPixels call like e.g.
var pixels = _templateDirtMask.GetPixels();
foreach (Vector2Int pos in linePositions)
{
int pixelXOffset = pos.x - (_brush.width / 2);
int pixelYOffset = pos.y - (_brush.height / 2);
for (int x = 0; x < _brush.width; x++)
{
for (int y = 0; y < _brush.height; y++)
{
pixels[(pixelXOffset + x) + (pixelYOffset + y) * _templateDirtMask.width] = Color.black;
}
}
}
_templateDirtMask.SetPixels(pixels);
_templateDirtMask.Apply();
It happens because there is really could be a case, when several elements from mousePositions are associated with one elment from linePositions.
Rough example: your texture resolution is only 1x1px. In this case you linePositons will contain only one element. And this element will be associated with all elements from mosePositions.
So, relation of the number of elements in these lists depends on relation of your texture and screen resolutions.

c# Android - Find object by color on bitmap

I would like to find an object on a bitmap using its HSV color. I know there already are some topics created about this issue, but they seem not to work on Android (I didn't find System.Drawing.Imaging on Android) or to be less efficient than the algorithm I already created:
public Rect FindObjectOnBitmap(Bitmap bm)
{
bm.GetPixels(pixels, 0, bm.Width, 0, 0, bm.Width, bm.Height);
// Get the matching pixels.
for(int x = 0; x<bm.Width; x++)
for (int y = 0; y < bm.Height; y++)
{
Color color = new Color(pixels[x + bm.Width * y]);
pixelsMatch[x,y] = (color.GetBrightness() > Settings.Tracker.blackFilter && color.GetSaturation() > Settings.Tracker.whiteFilter) && (Angle(Settings.Tracker.hue, color.GetHue()) < Settings.Tracker.sensitivity);
}
Rect objectRect = null;
int maxNbPixel = 0;
// for each matching pixel
for (int x = 0; x < bm.Width; x ++)
for (int y = 0; y < bm.Height; y ++)
if(pixelsMatch[x, y])
{
int nbPixel = 0;
Rect objectRectTemp = new Rect(x, y, x, y);
Queue<Pixel> zone = new Queue<Pixel>();
zone.Enqueue(new Pixel(x, y));
while(zone.Count != 0)
{
Pixel px = zone.Dequeue();
nbPixel++;
// Updates object position
if (px.x < objectRectTemp.Left)
objectRectTemp.Left = px.x;
if (px.x > objectRectTemp.Right)
objectRectTemp.Right = px.x;
if (px.y < objectRectTemp.Bottom)
objectRectTemp.Bottom = px.y;
if (px.y > objectRectTemp.Top)
objectRectTemp.Top = px.y;
// for each nearby pixel
for (int side = 0; side < 4; side++)
{
Pixel sidePx = px.Side(side);
if (sidePx.x >= 0 && sidePx.x < bm.Width && sidePx.y >= 0 && sidePx.y < bm.Height)
{
if (pixelsMatch[sidePx.x, sidePx.y])
{
zone.Enqueue(sidePx);
pixelsMatch[sidePx.x, sidePx.y] = false;
}
}
}
}
// Save the object if it is big enough.
if(nbPixel > maxNbPixel && nbPixel >= 4)
{
maxNbPixel = nbPixel;
objectRect = objectRectTemp;
}
}
return objectRect;
}
// Function comparing hues.
static float Angle(float deg1, float deg2)
{
if (Math.Abs(deg1 - deg2) > 180f)
return (360f - Math.Abs(deg1 - deg2));
else
return Math.Abs(deg1 - deg2);
}
However, I am reading a stream, and this algorithm is also not efficient enough since I get 0.1 fps on a 640x480 image... I get a good fps only if I create the bitmap with a smaller resolution than the real image (for example 40x30). That enables me to detect big or close objects with a bad precision (position and size).
How can I increase the efficiency enough, knowing I am on an ANDROID device...
Thanks in advance.
PS: Sorry for my poor English ^^

Cliffs terrain generation in minecraft-like game

I want to generate something like this:
I use Perlin Noise with sharp curve, my code produces those cliffs:
.
for (int x = 0; x < sizeX; x++)
{
for (int z = 0; z < sizeZ; z++)
{
int floorY = map.GetMaxYNotWater(x, z);
float n = hillsNoise.Noise(x, z);
int hillY = (int)(curveHills.Evaluate(n) * 80f);
if (hillY > floorY + 5)
{
for (int y = hillY; y > floorY; y--)
{
map.SetBlock(GetBlock(y), new Vector3i(x, y, z));
}
}
}
}
How can I "cut" them to make hanging things?
I tried to do it like this with additional curve:
for (int x = 0; x < sizeX; x++)
{
for (int z = 0; z < sizeZ; z++)
{
int floorY = map.GetMaxYNotWater(x, z);
float n = hillsNoise.Noise(x, z);
int hillY = (int)(curveHills.Evaluate(n) * 80f);
if (hillY > floorY + 5)
{
int c = 0;
int max = hillY - floorY;
max = (int)(max * curveHillsFull.Evaluate(n)) + 1;
for (int y = hillY; y > floorY && c < max; y--, c++)
{
map.SetBlock(GetBlock(y), new Vector3i(x, y, z));
}
}
}
}
But it produces flying islands.
So what can I do to achieve the first screenshot results?
I can't say how Minecraft does it, but from my own experience with voxel terrain, the best way to approach it is to think of the voxel grid as something like a cloud: each voxel has a density, and when that density is high enough, it becomes a 'visible' part of the cloud and you fill the voxel.
So rather than calculating the min and max Y levels, work on calculating the density value, something like this:
for (int x = 0; x < sizeX; x++)
{
for (int y = 0; y > sizeY; y--)
{
for (int z = 0; z < sizeZ; z++)
{
//This means less density at higher elevations, great for turning
//a uniform cloud into a terrain. Multiply this for flatter worlds
float flatWorldDensity = y;
//This calculates 3d Noise: you will probably have to tweak this
//heavily. Multiplying input co-ordinates will allow you to scale
//terrain features, while multiplying the noise itself will make the
//features stronger and more or less apparent
float xNoise = hillsNoise.Noise(x, y);
float yNoise = hillsNoise.Noise(x, z);
float zNoise = hillsNoise.Noise(y, z);
float 3dNoiseDensity = (xNoise + yNoise + zNoise) / 3;
//And this adds them together. Change the constant "1" to get more or
//less land material. Simple!
float ActualDensity = flatWorldDensity + 3dNoiseDensity;
if (ActualDensity > 1)
{
map.SetBlock(GetBlock(y), new Vector3i(x, y, z));
}
}
}
}

ERROR XNA Framework VertexBuffer IndexBuffer usage

I am creating a game after working through a XNA 4.0 book. It will be 3D, but I am already stuck in creating the terrain...
UPDATE: Everything starting from here is an update...
Terrain Update:
public void Update(Matrix view, Matrix projection)
{
View = view;
Projection = projection;
World = Matrix.CreateTranslation(-Width / 2f, 0, Height / 2f);
}
Terrain Draw:
public void Draw(GraphicsDevice g)
{
effect.CurrentTechnique = effect.Techniques["ColoredNoShading"];
effect.Parameters["xView"].SetValue(View);
effect.Parameters["xProjection"].SetValue(Projection);
effect.Parameters["xWorld"].SetValue(World);
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
//g.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices, 0, vertices.Length, indices, 0, indices.Length / 3, VertexPositionColorNormal.VertexDeclaration);
g.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertices.Length, 0, indices.Length / 3);
}
}
The commented line is working, in the both cases I am able to see the terrain...
The following code is to initialize Vertex and Index Buffer:
private void SetUpVertices(GraphicsDevice g)
{
float currentH;
int currentI;
vertices = new VertexPositionColorNormal[Width * Height];
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
currentH = heightData[x,y];
currentI = x + y * Width;
vertices[currentI].Position = new Vector3(x, currentH , -y);
if (currentH < minH + (maxH - minH) / 3)
vertices[currentI].Color = Color.ForestGreen;
else if (currentH < maxH - (maxH - minH) / 3)
vertices[currentI].Color = Color.LawnGreen;
else
vertices[currentI].Color = Color.White;
}
}
SetUpIndices(g);
}
private void SetUpIndices(GraphicsDevice g)
{
indices = new int[(Width - 1) * (Height - 1) * 6];
int counter = 0;
for (int y = 0; y < Height - 1; y++)
{
for (int x = 0; x < Width - 1; x++)
{
int lowerLeft = x + y * Width;
int lowerRight = (x + 1) + y * Width;
int topLeft = x + (y + 1) * Width;
int topRight = (x + 1) + (y + 1) * Width;
indices[counter++] = topLeft;
indices[counter++] = lowerRight;
indices[counter++] = lowerLeft;
indices[counter++] = topLeft;
indices[counter++] = topRight;
indices[counter++] = lowerRight;
}
}
SetUpNormals(g);
}
private void SetUpNormals(GraphicsDevice g)
{
for (int i = 0; i < vertices.Length; i++)
{
vertices[i].Normal = Vector3.Zero;
}
int[] index = new int[3];
Vector3 s1, s2, n;
for (int i = 0; i < vertices.Length / 3; i++)
{
for (int y = 0; y < 3; y++)
index[y] = indices[i * 3 + y];
s1 = vertices[index[0]].Position - vertices[index[2]].Position;
s2 = vertices[index[0]].Position - vertices[index[1]].Position;
n = Vector3.Cross(s1, s2);
for (int y = 0; y < 3; y++)
{
vertices[index[y]].Normal += n;
vertices[index[y]].Normal.Normalize();
}
}
FillBuffers(g);
}
private void FillBuffers(GraphicsDevice g)
{
VertexBuffer = new VertexBuffer(g, VertexPositionColorNormal.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly);
VertexBuffer.SetData(vertices);
IndexBuffer = new IndexBuffer(g, typeof(int), indices.Length, BufferUsage.WriteOnly);
IndexBuffer.SetData(indices);
g.Indices = IndexBuffer;
g.SetVertexBuffer(VertexBuffer);
}
I don't think, that there is a mistake, because it is working with the other line. Might there be an error with the .fx file I am using. If you think so, I am going to switch to BasicEffects...
(You might notice, that the code is from http://www.riemers.net/eng/Tutorials/XNA/Csharp/series1.php )
Thanks for your help...
Yours,
Florian
(Answer to original revision of the question.)
You're not setting your vertex buffer and index buffer onto the graphics device. These two lines of code (untested) should do what you need:
g.GraphicsDevice.Indices = indexBuffer;
g.GraphicsDevice.SetVertexBuffer(vertexBuffer);
Place them just after you set the parameters on your effect (ef), before the loop.
The vertex buffer provides the vertex declaration that the exception message is asking for.
Edit after question update: In your new version you're setting the vertex and index buffers - but it's in the wrong place. You need to set them onto the graphics device each frame. Your code would only work if nothing changes them after you set them in FillBuffers. But I'm guessing that stuff is being drawn outside your class's Draw method?
If that something else is a SpriteBatch, even it works using vertex buffers and index buffers. So it will reset your settings. (It's worth adding that it also sets render states - in which case you might need to see this article.)

issues with laggy c# code in xna game studio

My code seems to compile okay, but when I try to run it, it hangs very badly.
I've been following along with Riemers XNA tutorial here.
I'm pretty familiar with C#, but an expert by no means. I've had no problems getting any of this to work up to this point, and there are no errors or exceptions being thrown... it just hangs up. I've read on his related forum, where users discussed having other problems, usually relating to typos or code errors, but there's nothing like this in there... everyone seems to be able to run it fine.
Is there something I've done wrong perhaps? The nested for-loop at the bottom seems a bit heavy-handed to me. screenWidth and screenHeight are 500 and 500.
BTW: this is run from the LoadContent override method, so it should only run once as far as I know.
private void GenerateTerrainContour()
{
terrainContour = new int[screenWidth];
for (int x = 0; x < screenWidth; x++)
terrainContour[x] = screenHeight / 2;
}
private void CreateForeground()
{
Color[] foregroundColors = new Color[screenWidth * screenHeight];
for (int x = 0; x < screenWidth; x++)
{
for (int y = 0; y < screenHeight; y++)
{
if (y > terrainContour[x])
foregroundColors[x + y * screenWidth] = Color.Green;
else
foregroundColors[x + y * screenWidth] = Color.Transparent;
fgTexture = new Texture2D(device, screenWidth, screenHeight, false, SurfaceFormat.Color);
fgTexture.SetData(foregroundColors);
}
}
}
Probably something to do with the fact that you're creating 250,000 screen sized textures (holy moly)!
Resource allocation is always heavy - especially when you're dealing with media such as sounds and images.
It seems like you only really need one texture here, try moving fgTexture = new Texture2D(device, screenWidth, screenHeight, false, SurfaceFormat.Color); outside of the loop. Then try moving fgTexture.SetData(foregroundColors); outside of the loop too.
private void CreateForeground()
{
Color[] foregroundColors = new Color[screenWidth * screenHeight];
fgTexture = new Texture2D(device, screenWidth, screenHeight, false, SurfaceFormat.Color);
for (int x = 0; x < screenWidth; x++)
{
for (int y = 0; y < screenHeight; y++)
{
if (y > terrainContour[x])
foregroundColors[x + y * screenWidth] = Color.Green;
else
foregroundColors[x + y * screenWidth] = Color.Transparent;
}
}
fgTexture.SetData(foregroundColors);
}
for (int x = 0; x < screenWidth; x++)
{
for (int y = 0; y < screenHeight; y++)
{
if (y > terrainContour[x])
foregroundColors[x + y * screenWidth] = Color.Green;
else
foregroundColors[x + y * screenWidth] = Color.Transparent;
}
}
foregroundTexture = new Texture2D(device, screenWidth, screenHeight, false, SurfaceFormat.Color);
foregroundTexture.SetData(foregroundColors);
Your issue is on the last two lines. In your loop, you're creating 500 x 500 Texture2D objects, which is slowing you down. Move them outside the for loop.

Categories