How to add a texture to dynamically generated terrain vertices in xna - c#

I'm trying to add texture coordinates to each of the vertices so that a grass texture is added to each triangle. The code I have stretches the texture across the entire area which works but doesn't scale up very well. How do I correctly add (0,0), (0,1), (1,1), etc to the vertices?
Currently they're added in the SetUpVertices() method, should they be added in the SetUpIndices() method when the code can distinguish whether it's top left, bottom left, bottom right, etc. Any help would be greatly appreciated. The relevant methods are below and the full Game1.cs code is here http://pastebin.com/REd8QDZA
private void SetUpVertices()
{
vertices = new VertexPositionNormalTexture[terrainWidth * terrainHeight];
for (int x = 0; x < terrainWidth; x++)
{
for (int y = 0; y < terrainHeight; y++)
{
vertices[x + y * terrainWidth].Position = new Vector3(x, -y, heightData[x, y]);
vertices[x + y * terrainWidth].TextureCoordinate.X = x / (terrainWidth - 1.0);
vertices[x + y * terrainWidth].TextureCoordinate.Y = y / (terrainHeight - 1.0);
}
}
}
private void SetUpIndices()
{
indices = new short[(terrainWidth - 1) * (terrainHeight - 1) * 6];
int counter = 0;
for (int y = 0; y < terrainHeight - 1; y++)
{
for (int x = 0; x < terrainWidth - 1; x++)
{
int lowerLeft = x + y * terrainWidth;
int lowerRight = (x + 1) + y * terrainWidth;
int topLeft = x + (y + 1) * terrainWidth;
int topRight = (x + 1) + (y + 1) * terrainWidth;
indices[counter++] = (short)topLeft;
indices[counter++] = (short)lowerRight;
indices[counter++] = (short)lowerLeft;
indices[counter++] = (short)topLeft;
indices[counter++] = (short)topRight;
indices[counter++] = (short)lowerRight;
}
}
}

Just specify
vertices[x + y * terrainWidth].TextureCoordinate.X = x;
vertices[x + y * terrainWidth].TextureCoordinate.Y = y;
By default, texture coordinates greater than 1 will be wrapped and the texture is repeated.

Related

How to create a 3D mesh from a heightmap represented as a float array

I am attempting to understand how a 3D mesh has been constructed from a heightmap stored as a one dimensional float array. The only examples I have seen previously have made use of a 2D float array, and I am struggling to wrap my head around the math involved here. Any insight into it would be appreciated. I have commented the code which I do not quite understand yet for your convenience.
Source of code: https://github.com/SebLague/Hydraulic-Erosion
public void ContructMesh () {
Vector3[] verts = new Vector3[mapSize * mapSize];
int[] triangles = new int[(mapSize - 1) * (mapSize - 1) * 6];
int t = 0;
//Note that default mapSize is 255
for (int i = 0; i < mapSize * mapSize; i++) {
//Following code is not properly understood
int x = i % mapSize;
int y = i / mapSize;
int meshMapIndex = y * mapSize + x;
Vector2 percent = new Vector2 (x / (mapSize - 1f), y / (mapSize - 1f));
Vector3 pos = new Vector3 (percent.x * 2 - 1, 0, percent.y * 2 - 1) * scale;
pos += Vector3.up * map[meshMapIndex] * elevationScale; //Elevation scale is 20 by default
verts[meshMapIndex] = pos;
//End of misunderstood code
if (x != mapSize - 1 && y != mapSize - 1) {
t = (y * (mapSize - 1) + x) * 3 * 2;
triangles[t + 0] = meshMapIndex + mapSize;
triangles[t + 1] = meshMapIndex + mapSize + 1;
triangles[t + 2] = meshMapIndex;
triangles[t + 3] = meshMapIndex + mapSize + 1;
triangles[t + 4] = meshMapIndex + 1;
triangles[t + 5] = meshMapIndex;
t += 6;
}
}
Mesh mesh = new Mesh();
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
mesh.vertices = verts;
mesh.triangles = triangles;
mesh.RecalculateNormals ();
What specifically do you not understand?
int x = i % mapSize; // Get the x location of the current point
int y = i / mapSize; // Get the y location of the current point
// This should be equal to i, IDK why this is even calculated
int meshMapIndex = y * mapSize + x;
// How far along either direction are we?
Vector2 percent = new Vector2 (x / (mapSize - 1f), y / (mapSize - 1f));
// Make a new vector that scales the X and Y coordinates up.
// The Y coordinate is set to the Z element in this vector
// Presumably because whatever you use to render uses the Y element as "up"
// And the X-Z plane is the horizontal plane
// Also normalize X and Z to lie between -1*scale and 1*scale
Vector3 pos = new Vector3 (percent.x * 2 - 1, 0, percent.y * 2 - 1) * scale;
// Add the value at the current index, times the scale, as the Y element of pos
pos += Vector3.up * map[meshMapIndex] * elevationScale; //Elevation scale is 20 by default
// The X-Z values of pos give you the location of the vertex in the horizontal plane
// The Y value of pos gives you the height
// save the newly calculated pos in verts
verts[meshMapIndex] = pos;

Adding multiple elements to a single line of a List

Hi I am trying to add sets of coordinates to a single line of a List (so x, y as a single list line). I am currently generating the x and y coordinates as I break up an image and display the chunks in a grid. I am hitting a brick wall in being able to add both x and y to the same List line. Here is the code I have so far -
float sf = 1f;
int x = 0;
int y = 0;
int width = tileImage.Width / tileNumber;
int height = tileImage.Height / tileNumber;
int placeXValue = (width / 10);
int placeYValue = (height / 10);
int placeX = placeXValue;
int placeY = placeYValue;
Rectangle tileRect = tileImage.Bounds;
tileRect.Width = width;
tileRect.Height = height;
coordinates = new List<int>();
for (int i = 0; i < tileNumber; i++)
{
for (int j = 0; j < tileNumber; j++)
{
tileRect.X = x;
tileRect.Y = y;
_spriteBatch.Draw(tileImage, new Vector2((x + placeX) * sf, (y + placeY) * sf), tileRect, Color.White, 0, new Vector2(0, 0), sf, SpriteEffects.None, 0);
placeX += placeXValue;
x += width;
coordinates.Add(x,y);
}
x = 0;
y += height;
placeX = placeXValue;
placeY += placeYValue;
}
I found out how to do what I was trying to in the question. The first thing that I did was change my - private List<int> coordinates; - to a tuple list like this - List<Tuple<int, int>> coordinates; - This then allowed me to add my x and y elements to a new list by using the - coordinates.Add(new Tuple<int, int>(x,y)); - tuple functionality. Making this change has allowed me to add my generated coordinate pairs to my list.

Draw Cosx with specific period width

how can i draw a period of Cosx with width =100 and height 50?
my recent code is very hard to control them :(
// this to find y with each x to draw Cos
for (int i = 0, j = 0; i < 50; i += 1, j++)
{
int y = (int)((Math.Cos((double)i * height/10F * Math.PI / cy) + 1.0) * (cx - 1) / widtd/10F);
poi.SetValue(new Point(i, y),j); // poi is an aray of point
}
Based on OP's last comment I assume "width" means a single period. Therefore to calculate the points of a cosine function with amplitude amp and a period of period looks like this:
int amp = 50, period = 100;
Point[] poi = new Point[period];
for (int x = 0; x < period; x++)
{
int y = (int)(amp * Math.Cos(x * 2 * Math.PI * (1.0 / period)));
poi[x] = new Point(x, y);
}
Note that this is a "1:1" calculation, i.e. one point is one pixel.
I found solution here:
string filename = #"D:\test.bmp";
int width = 200;
int height = 300;
Bitmap b = new Bitmap(width, height);
for (int i = 0; i < width; i++)
{
int y = (int)((Math.Cos((double)i * 2.0 * Math.PI / width) + 1.0) * (height - 1) / 2.0);
b.SetPixel(i, y, Color.Black);
}
b.Save(filename);

Looping through pixels excluding an area

I have the following code for looping pixels in an image:
Bitmap myBitmap = new Bitmap(pictureBox1.Image);
for (x = 0; x < pictureBox1.Image.Width; x += 1)
{
for (y = 0; y < pictureBox1.Image.Height; y += 1)
{
Color pixelColor = myBitmap.GetPixel(x, y);
aws = pixelColor.GetBrightness();
}
}
The above works. What if i want to exclude an area of the image. The area is an area selected by the user,for example:
from x=200&y=30 to x=250&y=30
from x=200&y=35 to x=250&y=35
from x=200&y=40 to x=250&y=40
from x=200&y=45 to x=250&y=45
How i loop all pixels except the one i wrote above?
Thank you!
I think you can do something like the following
Rectangle rectToExclude = new Rectangle(200, 30, 250, 30);//rectangle to be excluded
Bitmap myBitmap = new Bitmap(pictureBox1.Image);
for (x = 0; x < pictureBox1.Image.Width; x += 1)
{
for (y = 0; y < pictureBox1.Image.Height; y += 1)
{
if (rectToExclude.Contains(x, y)) continue; // if in the exclude rect then continue without doing any effects
Color pixelColor = myBitmap.GetPixel(x, y);
aws = pixelColor.GetBrightness();
}
}
Maybe write a function to test x and y like this:
public static bool IsInArea(int testx, int testy, int x1, int y1, int x2, int y2)
{
if (testx > x1 && testx < x2 && testy > y1 && testy < y2)
{
return true;
}
else
{
return false;
}
}
and then use it like this:
BitmapData bmd = myBitmap.LockBits(new Rectangle(0, 0, myBitmap.Width, myBitmap.Height),
System.Drawing.Imaging.ImageLockMode.ReadWrite,
myBitmap.PixelFormat);
int PixelSize = 4;
unsafe
{
for (int y = 0; y < bmd.Height; y++)
{
byte* row = (byte*)bmd.Scan0 + (y * bmd.Stride);
for (int x = 0; x < bmd.Width; x++)
{
if (!IsInArea(x, y, 200, 30, 250, 30))
{
int b = row[x * PixelSize]; //Blue
int g = row[x * PixelSize + 1]; //Green
int r = row[x * PixelSize + 2]; //Red
int a = row[x * PixelSize + 3]; //Alpha
Color c = Color.FromArgb(a, r, g, b);
float brightness = c.GetBrightness();
}
}
}
}
myBitmap.UnlockBits(bmd);

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

Categories