WP7 Lagging when drawing lots of primitive cubes - c#

I'm currently working on a 3D Arkanoid clone, and I'm drawing the cubes using primitives.
I have 6 rows, each with 49 blocks, so 294 blocks total.
Normally I'm not to bothered about optomisation, but the game is ridiculously slower on lower end phones. I know the issue is with the drawing of the blocks, because the game runs fine with that code removed.
My code for setting up the primitives is
VertexPositionNormalTexture[] vertices1 = new VertexPositionNormalTexture[4];
VertexPositionNormalTexture[] vertices2 = new VertexPositionNormalTexture[4];
VertexPositionNormalTexture[] vertices3 = new VertexPositionNormalTexture[4];
VertexPositionNormalTexture[] vertices4 = new VertexPositionNormalTexture[4];
VertexPositionNormalTexture[] vertices5 = new VertexPositionNormalTexture[4];
VertexPositionNormalTexture[] vertices6 = new VertexPositionNormalTexture[4];
public void SetupVertices(){
vertices1[0].Position = new Vector3(position.X, position.Y, position.Z);
vertices1[0].TextureCoordinate = new Vector2(1, 1);
vertices1[1].Position = new Vector3(position.X, position.Y + size.Y, position.Z);
vertices1[1].TextureCoordinate = new Vector2(1, 0);
vertices1[2].Position = new Vector3(position.X + size.X, position.Y + size.Y, position.Z);
vertices1[2].TextureCoordinate = new Vector2(0, 0);
vertices1[3].Position = new Vector3(position.X + size.X, position.Y, position.Z);
vertices1[3].TextureCoordinate = new Vector2(0, 1);
//Another side
vertices2[0].Position = new Vector3(position.X, position.Y, position.Z);
vertices2[0].TextureCoordinate = new Vector2(0, 1);
vertices2[1].Position = new Vector3(position.X, position.Y + size.Y, position.Z);
vertices2[1].TextureCoordinate = new Vector2(0, 0);
vertices2[2].Position = new Vector3(position.X, position.Y + size.Y, position.Z + size.Z);
vertices2[2].TextureCoordinate = new Vector2(1, 0);
vertices2[3].Position = new Vector3(position.X, position.Y, position.Z + size.Z);
vertices2[3].TextureCoordinate = new Vector2(1, 1);
//ANOTHER SIDE
vertices3[0].Position = new Vector3(position.X + size.X, position.Y, position.Z + size.Z);
vertices3[0].TextureCoordinate = new Vector2(0, 1);
vertices3[1].Position = new Vector3(position.X + size.X, position.Y + size.Y, position.Z + size.Z);
vertices3[1].TextureCoordinate = new Vector2(0, 0);
vertices3[2].Position = new Vector3(position.X + size.X, position.Y + size.Y, position.Z);
vertices3[2].TextureCoordinate = new Vector2(1, 0);
vertices3[3].Position = new Vector3(position.X + size.X, position.Y, position.Z);
vertices3[3].TextureCoordinate = new Vector2(1, 1);
vertices4[0].Position = new Vector3(position.X, position.Y, position.Z + size.Z);
vertices4[0].TextureCoordinate = new Vector2(0, 1);
vertices4[1].Position = new Vector3(position.X, position.Y + size.Y, position.Z + size.Z);
vertices4[1].TextureCoordinate = new Vector2(0, 0);
vertices4[2].Position = new Vector3(position.X + size.X, position.Y + size.Y, position.Z + size.Z);
vertices4[2].TextureCoordinate = new Vector2(1, 0);
vertices4[3].Position = new Vector3(position.X + size.X, position.Y, position.Z + size.Z);
vertices4[3].TextureCoordinate = new Vector2(1, 1);
vertices5[0].Position = new Vector3(position.X, position.Y + size.Y, position.Z);
vertices5[0].TextureCoordinate = new Vector2(0, 0);
vertices5[1].Position = new Vector3(position.X + size.X, position.Y + size.Y, position.Z);
vertices5[1].TextureCoordinate = new Vector2(1, 0);
vertices5[2].Position = new Vector3(position.X + size.X, position.Y + size.Y, position.Z + size.Z);
vertices5[2].TextureCoordinate = new Vector2(1, 1);
vertices5[3].Position = new Vector3(position.X, position.Y + size.Y, position.Z + size.Z);
vertices5[3].TextureCoordinate = new Vector2(0, 1);
//bottom
vertices6[0].Position = new Vector3(position.X, position.Y, position.Z);
vertices6[0].TextureCoordinate = new Vector2(0, 0);
vertices6[1].Position = new Vector3(position.X + size.X, position.Y, position.Z);
vertices6[1].TextureCoordinate = new Vector2(1, 0);
vertices6[2].Position = new Vector3(position.X + size.X, position.Y, position.Z + size.Z);
vertices6[2].TextureCoordinate = new Vector2(1, 1);
vertices6[3].Position = new Vector3(position.X, position.Y, position.Z + size.Z);
vertices6[3].TextureCoordinate = new Vector2(0, 1);
numTriangles = 4;
indices = new short[numTriangles + 2];
int i = 0;
indices[i++] = 0;
indices[i++] = 1;
indices[i++] = 3;
indices[i++] = 2;
}
My code for drawing the blocks is basic, but I have no idea how to optomise it.
//Game1.CS Code
GraphicsDevice.SamplerStates[0] = SamplerState.LinearClamp; // need to do this on reach devices to allow non 2^n textures
#region GamePlaying
if (gameState == State.BREAKOUT || gameState == State.GAMEOVERBREAKOUT)
{
foreach (Block block in lastBlocks)
{
block.Draw(camera);
}
foreach (Block block in fourthBlocks)
{
block.Draw(camera);
}
foreach (Block block in thirdBlocks)
{
block.Draw(camera);
}
foreach (Block block in secondBlocks)
{
block.Draw(camera);
}
foreach (Block block in firstBlocks)
{
block.Draw(camera);
}
foreach (Block block in fifthBlocks)
{
block.Draw(camera);
}
}
Block.CS Draw Code
public void Draw(Camera camera)
{
effect.View = camera.View;
effect.Projection = camera.Projection;
effect.World = rotationMatrix;
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
graphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleStrip, vertices1, 0, 4, indices, 0, numTriangles);
graphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleStrip, vertices2, 0, 4, indices, 0, numTriangles);
graphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleStrip, vertices3, 0, 4, indices, 0, numTriangles);
graphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleStrip, vertices4, 0, 4, indices, 0, numTriangles);
graphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleStrip, vertices5, 0, 4, indices, 0, numTriangles);
graphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleStrip, vertices6, 0, 4, indices, 0, numTriangles);
}
}

Calling DrawUserIndexedPrimitives per block is the problem. The idea behind this kind of draw call is to let you draw a large number of primitives with few calls, not make many calls that draw a little.
You have two options:
Use SpriteBatch to draw the quads (this internally performs batching for you so you don't have to handle it).
Instead of calling DrawUserIndexedPrimitives directly inside each Draw call, accumulate the vertices (and re-numbered indices) in a temporary buffer and submit this once (per Effect for a set of indices) when all objects have been accumulated.
Hope this helps.

Related

Unity C# Generating custom mesh for voxel terrain not loading properly

So, I am in the process of creating a infinite terrain system similar to Minecraft's. I am making it myself out of personal interest to learn. I have most everything working, and I have made some functions to generate the faces of cubes and then I'm adding it all together into a chunk, but for some reason I am getting a really weird thing where each row of the blocks are randomly much higher than the others, ruining the mesh entire. Anyone able to understand why, because I am completely lost as to the issue.
Note: The highlighted part is supposed to be a singular chunk, and it extends upwards over the entire chunk. Each set of them are a slight bit behind the previous, its supposed to be a completely flat face on the top.
// Face Generator
public enum Face { top, bottom, north, south, west, east };
public Mesh GetMeshDataFromFace(Face face, Vector3 pos)
{
Mesh _face = new Mesh();
Vector3[] _normals = new Vector3[4];
if (face == Face.top)
{
//Create the mesh for a upwards facing surface.
_face.vertices = new Vector3[] {
pos + new Vector3(0, 1, 0),
pos + new Vector3(1, 1, 0),
pos + new Vector3(1, 1, 1),
pos + new Vector3(0, 1, 1)
};
_face.triangles = new int[]{
0, 3, 2, 2, 1, 0
};
_normals = new Vector3[]{
Vector3.up, Vector3.up, Vector3.up, Vector3.up
};
}
if (face == Face.bottom)
{
//Create the mesh for a upwards facing surface.
_face.vertices = new Vector3[] {
pos + new Vector3(0, 0, 0),
pos + new Vector3(1, 0, 0),
pos + new Vector3(1, 0, 1),
pos + new Vector3(0, 0, 1)
};
_normals = new Vector3[]{
Vector3.down, Vector3.down, Vector3.down, Vector3.down
};
_face.triangles = new int[]{
0, 1, 2, 2, 3, 0
};
}
if (face == Face.north)
{
//Create the mesh for a upwards facing surface.
_face.vertices = new Vector3[] {
pos + new Vector3(0, 0, 1),
pos + new Vector3(1, 0, 1),
pos + new Vector3(1, 1, 1),
pos + new Vector3(0, 1, 1)
};
_normals = new Vector3[]{
Vector3.right, Vector3.right, Vector3.right, Vector3.right
};
_face.triangles = new int[]{
0, 1, 2, 2, 3, 0
};
}
if (face == Face.south)
{
//Create the mesh for a upwards facing surface.
_face.vertices = new Vector3[] {
pos + new Vector3(0, 0, 0),
pos + new Vector3(1, 0, 0),
pos + new Vector3(1, 1, 0),
pos + new Vector3(0, 1, 0)
};
_normals = new Vector3[]{
Vector3.left, Vector3.left, Vector3.left, Vector3.left
};
_face.triangles = new int[]{
0, 3, 1, 3, 2, 1
};
}
if (face == Face.west)
{
//Create the mesh for a upwards facing surface.
_face.vertices = new Vector3[] {
pos + new Vector3(0, 0, 0), // 0
pos + new Vector3(0, 1, 0), // 3
pos + new Vector3(0, 1, 1), // 7
pos + new Vector3(0, 0, 1) // 4
};
_normals = new Vector3[]{
Vector3.back, Vector3.back, Vector3.back, Vector3.back
};
_face.triangles = new int[]{
2, 1, 0, 0, 3, 2
};
}
if (face == Face.east)
{
//Create the mesh for a upwards facing surface.
_face.vertices = new Vector3[] {
pos + new Vector3(1, 0, 0),
pos + new Vector3(1, 1, 0),
pos + new Vector3(1, 1, 1),
pos + new Vector3(1, 0, 1)
};
_normals = new Vector3[]{
Vector3.forward, Vector3.forward, Vector3.forward, Vector3.forward
};
_face.triangles = new int[]{
0, 1, 2, 2, 3, 0
};
}
_face.SetNormals(_normals);
return _face;
}
Chunks store block data as a singular byte[] array of 4096 indexs.
Here is the code for generating the mesh
public Mesh CreateChunkMesh(ChunkObject chunkObj)
{
Chunk chunkData = chunkObj.chunkData;
Mesh mesh = new Mesh();
List<Mesh> meshesToCombine = new List<Mesh>();
for (int i = 0; i < chunkData.blocks.Length; i++)
{
if (chunkData.blocks[i] != 0)
{
Vector3 blockPos = new Vector3((i % 16), Mathf.Floor(i % 256), Mathf.Floor((i % 256) / 16));
Debug.Log(i + " | " + blockPos);
if (i + 256 >= chunkData.blocks.Length)
{
//This is the top layer of blocks.
if (!_loadedChunkObjs.ContainsKey(chunkObj.chunkPosition + new Vector3Int(0, 1, 0)))
{
//If the block ABOVE this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.top, blockPos));
}
}
if (i < 256)
{
if (!_loadedChunkObjs.ContainsKey(chunkObj.chunkPosition + new Vector3Int(0, -1, 0)))
{
//If the block ABOVE this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.bottom, blockPos));
}
}
if ((i % 256) < 16)
{
//This is the EAST layer of the chunk.
if (!_loadedChunkObjs.ContainsKey(chunkObj.chunkPosition + new Vector3Int(0, 0, 1)))
{
//If the block ABOVE this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.east, blockPos));
}
}
if ((i % 256) >= 240)
{
//This is the WEST layer of the chunk.
if (!_loadedChunkObjs.ContainsKey(chunkObj.chunkPosition + new Vector3Int(0, 0, -1)))
{
//If the block ABOVE this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.west, blockPos));
}
}
if (i % 16 == 0)
{
//This is the NORTH layer of the chunk
if (!_loadedChunkObjs.ContainsKey(chunkObj.chunkPosition + new Vector3Int(1, 0, 0)))
{
//If the block ABOVE this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.north, blockPos));
}
}
if (i % 16 == 1)
{
//This is the SOUTH layer of the chunk.
if (!_loadedChunkObjs.ContainsKey(chunkObj.chunkPosition + new Vector3Int(-1, 0, 0)))
{
//If the block ABOVE this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.south, blockPos));
}
}
//This block is not within any of the faces.
//Check each direction of the block
if (i + 256 <= chunkData.blocks.Length)
{
if (chunkData.blocks[i + 255] == 0) //TOP direction
{
//If the block ABOVE this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.top, blockPos));
}
}
if (i - 256 >= 0)
{
if (chunkData.blocks[i - 256] == 0) //BOTTOM Direction
{
//If the block BELOW this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.bottom, blockPos));
}
}
if (i + 1 <= chunkData.blocks.Length - 1)
{
if (chunkData.blocks[i + 1] == 0) //NORTH Direction
{
//If the block NORTH of this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.north, blockPos));
}
}
if (i - 1 >= 0)
{
if (chunkData.blocks[i - 1] == 0) //SOUTH Direction
{
//If the block SOUTH of this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.south, blockPos));
}
}
if (i + 16 <= chunkData.blocks.Length)
{
if (chunkData.blocks[i + 15] == 0) //WEST Direction
{
//If the block WEST of this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.west, blockPos));
}
}
if (i - 16 >= 0)
{
if (chunkData.blocks[i - 16] == 0) //EAST Direction
{
//If the block EAST of this block is air then draw the face.
meshesToCombine.Add(GetMeshDataFromFace(Face.east, blockPos));
}
}
}
}
CombineInstance[] combine = new CombineInstance[meshesToCombine.Count];
for (int i = 0; i < combine.Length; i++)
{
combine[i].mesh = meshesToCombine[i];
combine[i].transform = chunkObj.transform.localToWorldMatrix;
}
mesh.CombineMeshes(combine);
return mesh;
}
If there are any necessary details I didn't include let me know and ill add them.

The length of this integer array is 0?

I've been trying to create a plane mesh in Unity using code, and I've come across a very interesting problem. I created an int[], filled it up with some values, and it's length is somehow zero. I've never encountered anything this quirky, so I'd enjoy a bit of help.
mesh.triangles = new int[]
{
4, 6, 5, 5, 6, 7
};
... // Not important stuff
Debug.Log(mesh.triangles.Length);
I don't know what is happening, so I really haven't tried anything. But in the console, there is an error message stating Failed setting triangles. Some indices are referencing out of bounds vertices. IndexCount: 6, VertexCount: 4.This is probably really important, but I don't understand some parts of the message(especially the last part). And if it makes a difference, I have an array concatenation method being called to add the first triangles to these ones. I initially identified this problem when the half of my mesh still wasn't appearing. I would really appreciate help; thanks.
Edit:
To cut confusion, I'm just going to paste my whole entire method.
private void CreateQuad(ref Mesh mesh, Vector3 offset, bool first)
{
if (first)
{
mesh.vertices = new Vector3[]
{
Vector3.zero, Vector3.right, Vector3.forward, new Vector3(1, 0, 1)
};
mesh.triangles = new int[]
{
0, 2, 1, 1, 2, 3
};
mesh.normals = new Vector3[]
{
Vector3.back, Vector3.back, Vector3.back, Vector3.back
};
mesh.tangents = new Vector4[]
{
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1)
};
mesh.uv = new Vector2[]
{
Vector2.zero, Vector2.right, Vector2.up, Vector2.one
};
}
else if (!first)
{
mesh.vertices = new Vector3[]
{
Vector3.zero + offset,
Vector3.right + offset,
Vector3.forward + offset,
new Vector3(1, 0, 1) + offset
};
mesh.triangles = new int[]
{
4, 6, 5, 5, 6, 7
};
mesh.normals = new Vector3[]
{
Vector3.back, Vector3.back, Vector3.back, Vector3.back
};
mesh.tangents = new Vector4[]
{
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1)
};
mesh.uv = new Vector2[]
{
Vector2.zero, Vector2.right, Vector2.up, Vector2.one
};
Debug.Log(mesh.triangles.Length);
}
}
You only have FOUR vertices!
mesh.vertices = new Vector3[]
{
Vector3.zero + offset,
Vector3.right + offset,
Vector3.forward + offset,
new Vector3(1, 0, 1) + offset
};
So the indices 4, 6, 5, 5, 6, 7 are all invalid! If you have only four vertices you can maximum have the indices 0, 1, 2, 3
=> Unity simply rejects them all. You should have already taken that hint from the error you get
Failed setting triangles. Some indices are referencing out of bounds vertices. IndexCount: 6, VertexCount: 4
Now it is a bit unclear what exactly you are trying to achieve here but
either you want to REPLACE the vertices: In this case there is no reason to set new triangle instances etc at all! It is enough to connect them only once:
private void CreateQuad(ref Mesh mesh, Vector3 offset, bool first)
{
if (first)
{
mesh.vertices = new Vector3[]
{
Vector3.zero, Vector3.right, Vector3.forward, new Vector3(1, 0, 1)
};
mesh.triangles = new int[]
{
0, 2, 1, 1, 2, 3
};
mesh.normals = new Vector3[]
{
Vector3.back, Vector3.back, Vector3.back, Vector3.back
};
mesh.tangents = new Vector4[]
{
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1)
};
mesh.uv = new Vector2[]
{
Vector2.zero, Vector2.right, Vector2.up, Vector2.one
};
}
else if (!first)
{
mesh.vertices = new Vector3[]
{
Vector3.zero + offset,
Vector3.right + offset,
Vector3.forward + offset,
new Vector3(1, 0, 1) + offset
};
}
}
the other properties can simply be left untouched since you only want to update the vertex positions.
Or you actually wanted to ADD more faces. In that case you rather want to append to the existing arrays:
private void CreateQuad(ref Mesh mesh, Vector3 offset, bool first)
{
if (first)
{
mesh.vertices = new Vector3[]
{
Vector3.zero, Vector3.right, Vector3.forward, new Vector3(1, 0, 1)
};
mesh.triangles = new int[]
{
0, 2, 1, 1, 2, 3
};
mesh.normals = new Vector3[]
{
Vector3.back, Vector3.back, Vector3.back, Vector3.back
};
mesh.tangents = new Vector4[]
{
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1)
};
mesh.uv = new Vector2[]
{
Vector2.zero, Vector2.right, Vector2.up, Vector2.one
};
}
else if (!first)
{
// fist get already existing verts etc
var oldVerts = mesh.vertices;
var oldTris = mesh.triangles;
// create new vertices and triangles arrays with additional space for the new quad
var newVerts = new Vector3[oldVerts.Length + 4];
var newTris = new int[oldTris.Length + 6];
// copy over the existing vertices and triangles
Array.Copy(oldVerts, newVerts, olVerts.Length);
Array.Copy(oldTris, newtris, oldtris.Length);
// then append the new vertices
newVerts[oldverts.Length + 0] = Vector3.zero + offset;
newVerts[oldverts.Length + 1] = Vector3.right + offset;
newVerts[oldverts.Length + 2] = Vector3.forward + offset;
newVerts[oldverts.Length + 3] = new Vector3(1, 0, 1) + offset;
// append the new triangles
newTris[oldTris.Length + 0] = oldverts.Length + 0;
newTris[oldTris.Length + 1] = oldverts.Length + 2;
newTris[oldTris.Length + 2] = oldverts.Length + 1;
newTris[oldTris.Length + 3] = oldverts.Length + 1;
newTris[oldTris.Length + 4] = oldverts.Length + 2;
newTris[oldTris.Length + 5] = oldverts.Length + 3;
// get the min and max points for filling the uvs (not the most efficient way probably but it is what it is ^^)
// we later want to spread out the UV values linear between 0 (min) and 1 (max) on the given vertices
var min = Vector3.zero;
var max = Vector3.zero;
foreach(var vertex in newVerts)
{
min = Vector3.Min(min, vertex);
max = Vector3.Max(max, vertex);
}
// also fill new tangents and normals and uvs (if really necessary)
var newNormals = new Vector3[newVerts.Length];
var newTangents = new Vector4[newVerts.Length];
var newUVs = new Vector2[newVerts.Length];
for(var i = 0; i < newVerts.Length; i++)
{
var vertex = newVerts[i];
newUVs[i] = new Vector2((vertex.x - min.x) / (max.x - min.x), (vertex.z - min.z) / (max.z - min.z));
newNormals[i] = Vector3.back;
newTangents[i] = new Vector4(1, 0, 0, -1);
};
// finally set them all back
mesh.vertices = newVerts;
mesh.triangles = newTris;
mesh.normals = newNormals;
mesh.tangents = newTangents;
mesh.uv = newUs;
}
}
You first need to set the vertex array before altering the triangles. As Unity writes "It is recommended to assign a triangle array after assigning the vertex array, in order to avoid out of bounds errors."
mesh.vertices = new Vector3[] { new Vector3(-1,0,1), new Vector3(-1,0,-1),
new Vector3(1,0,-1), new Vector3(1,0,1) };
mesh.triangles = new int[] {0,1,2,0,2,3};

Lighting a 2D mesh in a 3d environnement

I'm making a 2.5D game with Monogame. I have 2D meshes in a 3D space, and I want them to shine. But if I activate default lighting, they're pitch black.
Here the render code:
BasicEffect effect = new BasicEffect(graphicsDevice);
VertexPositionTexture[] vertices =
{
new VertexPositionTexture(new Vector3(-.5f + Position.X, 0.5f + Position.Y, 0.0f), new Vector2(0, 0)),
new VertexPositionTexture(new Vector3(-.5f + Position.X, -.5f + Position.Y, 0.0f), new Vector2(0, 1)),
new VertexPositionTexture(new Vector3(0.5f + Position.X, 0.5f + Position.Y, 0.0f), new Vector2(1, 0)),
new VertexPositionTexture(new Vector3(0.5f + Position.X, -.5f + Position.Y, 0.0f), new Vector2(1, 1)),
};
graphicsDevice.BlendState = BlendState.AlphaBlend;
effect.EnableDefaultLighting();
effect.LightingEnabled = true;
//effect.AmbientLightColor = new Vector3(.75f, .75f, .75f);
effect.DirectionalLight0.DiffuseColor = new Vector3(.75f, .75f, .75f);
effect.DirectionalLight0.Direction = new Vector3(0, 0, -1);
effect.DirectionalLight0.SpecularColor = new Vector3(.75f, .60f, .60f);
effect.TextureEnabled = true;
effect.Texture = Subtexture;
effect.Projection = camera.Fov;
effect.View = camera.ViewMatrix;
effect.World = camera.WorldMatrix;
VertexBuffer buffer = new VertexBuffer(graphicsDevice, typeof(VertexPositionTexture), vertices.Length, BufferUsage.WriteOnly);
buffer.SetData(vertices);
graphicsDevice.SetVertexBuffer(buffer);
graphicsDevice.RasterizerState = RasterizerState.CullNone;
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, vertices, 0, 2);
}

How to draw only visible data XNA My method doesn't work

I have a 10*10 plane of cubes. I want only visible cubes to be drawn, so I use a Bounding frustum. The problem is that when the first cube from the grid (at location 0,0) gets out of the frustum, all cubes disappear.
Here is my code in Cube.cs:
public class Cube
{
Vector3 scale;
float textureScale;
GraphicsDevice device;
Effect effect;
VertexBuffer vertexBuffer;
Texture2D texture;
public Vector3 GlobalPosition = new Vector3(0, 0, 0);
public Vector3[] vertices = new Vector3[36];
public Cube(Vector3 scale, float textureScale, Effect effect, Texture2D texture, GraphicsDevice device)
{
this.scale = scale;
this.textureScale = textureScale;
this.device = device;
this.effect = effect;
this.texture = texture;
vertices[0] = new Vector3(-1, 1, -1);
vertices[1] = new Vector3(-1, -1, -1);
vertices[2] = new Vector3(1, -1, -1);
vertices[3] = new Vector3(1, -1, -1);
vertices[4] = new Vector3(1, 1, -1);
vertices[5] = new Vector3(-1, 1, -1);
//Front
vertices[6] = new Vector3(1, -1, 1);
vertices[7] = new Vector3(-1, -1, 1);
vertices[8] = new Vector3(-1, 1, 1);
vertices[9] = new Vector3(-1, 1, 1);
vertices[10] = new Vector3(1, 1, 1);
vertices[11] = new Vector3(1, -1, 1);
//Bottom
vertices[12] = new Vector3(-1, -1, -1);
vertices[13] = new Vector3(-1, -1, 1);
vertices[14] = new Vector3(1, -1, -1);
vertices[15] = new Vector3(1, -1, 1);
vertices[16] = new Vector3(1, -1, -1);
vertices[17] = new Vector3(-1, -1, 1);
//Top
vertices[18] = new Vector3(1, 1, -1);
vertices[19] = new Vector3(-1, 1, 1);
vertices[20] = new Vector3(-1, 1, -1);
vertices[21] = new Vector3(-1, 1, 1);
vertices[22] = new Vector3(1, 1, -1);
vertices[23] = new Vector3(1, 1, 1);
//Left
vertices[24] = new Vector3(-1, -1, 1);
vertices[25] = new Vector3(-1, -1, -1);
vertices[26] = new Vector3(-1, 1, -1);
vertices[27] = new Vector3(-1, 1, -1);
vertices[28] = new Vector3(-1, 1, 1);
vertices[29] = new Vector3(-1, -1, 1);
//Right
vertices[30] = new Vector3(1, -1, -1);
vertices[31] = new Vector3(1, -1, 1);
vertices[32] = new Vector3(1, 1, -1);
vertices[33] = new Vector3(1, -1, 1);
vertices[34] = new Vector3(1, 1, 1);
vertices[35] = new Vector3(1, 1, -1);
for (int i = 0; i < 36; i++)
{
vertices[i] = vertices[i] * scale + GlobalPosition;
}
VertexPositionNormalTexture[] verticesList = GetVPNT();
vertexBuffer = new VertexBuffer(device, VertexPositionNormalTexture.VertexDeclaration, verticesList.Length, BufferUsage.WriteOnly);
vertexBuffer.SetData<VertexPositionNormalTexture>(verticesList.ToArray());
}
public void Draw(Matrix View, Matrix Projection, Vector3 pos)
{
effect.CurrentTechnique = effect.Techniques["Textured"];
effect.Parameters["xWorld"].SetValue(Matrix.Identity * Matrix.CreateTranslation(pos));
effect.Parameters["xView"].SetValue(View);
effect.Parameters["xProjection"].SetValue(Projection);
effect.Parameters["xTexture"].SetValue(texture);
effect.Parameters["xEnableLighting"].SetValue(true);
effect.Parameters["xLightDirection"].SetValue(new Vector3(30, 30, 30));
effect.Parameters["xAmbient"].SetValue(0.5f);
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
device.SetVertexBuffer(vertexBuffer);
device.DrawPrimitives(PrimitiveType.TriangleList, 0, vertexBuffer.VertexCount / 3);
}
}
public VertexPositionNormalTexture[] GetVPNT()
{
VertexPositionNormalTexture[] vpnt = new VertexPositionNormalTexture[36];
Vector2[] texCoords = CalculateTexCoords(vertices, "tile");
for (int i = 0; i < 36; i++)
{
vpnt[i] = new VertexPositionNormalTexture(vertices[i], new Vector3(0, 0, 1), texCoords[i]);
}
return vpnt;
}
public Vector2[] CalculateTexCoords(Vector3[] vec, string type)
{
List<Vector2> texCoords = new List<Vector2>();
for (int i = 0; i < 12; i++)
{
if (AllEqual<float>(vertices[i * 3 + 0].X, vertices[i * 3 + 1].X, vertices[i * 3 + 2].X))
{
Vector2[] normvec = new Vector2[3];
normvec[0] = TexNorm(new Vector2(vertices[i * 3 + 0].Z, vertices[i * 3 + 0].Y), type);
normvec[1] = TexNorm(new Vector2(vertices[i * 3 + 1].Z, vertices[i * 3 + 1].Y), type);
normvec[2] = TexNorm(new Vector2(vertices[i * 3 + 2].Z, vertices[i * 3 + 2].Y), type);
texCoords.AddRange(normvec);
}
if (AllEqual<float>(vertices[i * 3 + 0].Y, vertices[i * 3 + 1].Y, vertices[i * 3 + 2].Y))
{
Vector2[] normvec = new Vector2[3];
normvec[0] = TexNorm(new Vector2(vertices[i * 3 + 0].X, vertices[i * 3 + 0].Z), type);
normvec[1] = TexNorm(new Vector2(vertices[i * 3 + 1].X, vertices[i * 3 + 1].Z), type);
normvec[2] = TexNorm(new Vector2(vertices[i * 3 + 2].X, vertices[i * 3 + 2].Z), type);
texCoords.AddRange(normvec);
}
if (AllEqual<float>(vertices[i * 3 + 0].Z, vertices[i * 3 + 1].Z, vertices[i * 3 + 2].Z))
{
Vector2[] normvec = new Vector2[3];
normvec[0] = TexNorm(new Vector2(vertices[i * 3 + 0].X, vertices[i * 3 + 0].Y), type);
normvec[1] = TexNorm(new Vector2(vertices[i * 3 + 1].X, vertices[i * 3 + 1].Y), type);
normvec[2] = TexNorm(new Vector2(vertices[i * 3 + 2].X, vertices[i * 3 + 2].Y), type);
texCoords.AddRange(normvec);
}
}
return texCoords.ToArray();
}
public bool AllEqual<T>(params T[] values)
{
if (values == null || values.Length == 0)
return true;
return values.All(v => v.Equals(values[0]));
}
public Vector2 TexNorm(Vector2 vecIn, string type)
{
Vector2 vec = new Vector2();
//Remove negative coordinates
if (vecIn.Y < 0)
vec.Y = 0;
if (vecIn.X < 0)
vec.X = 0;
switch (type)
{
case "stretch":
{
if (vecIn.X > 0)
vec.X = 1;
if (vecIn.Y > 0)
vec.Y = 1;
break;
}
case "tile":
{
if (vecIn.X > 0)
vec.X = textureScale;
if (vecIn.Y > 0)
vec.Y = textureScale;
break;
}
}
return vec;
}
public bool IsVisible(Matrix VP)
{
BoundingFrustum bf = new BoundingFrustum(VP);
bool isVis = true;
//Check weather at least one vertices are out of the frustum
for (int i = 0; i < 36; i++)
{
if (bf.Contains(vertices[i]) != ContainmentType.Contains)
{
isVis = false;
break;
}
}
return isVis;
}
}
Draw() method in Main.cs:
for (int i = 0; i < 10; i++)
{
for (int k = 0; k < 10; k++)
{
Cube cube = new Cube(Vector3.One, 1, effect, woodTexture, device);
if (cube.IsVisible(View * Projection))
{
cube.Draw(View, Projection, new Vector3(2 * i, 0, 2 * k));
}
}
}
I would also like if you can give me link to an atlas texturing tutorial/sample, Thanks!
Your cube.IsVisible(...) method does not take into account latter transformation you're applying when you're drawing the cube, i.e you're always checking against your original cube, not the transformed ones.
Please reference this answer as it touches both your culling problem (frustum-box) as well as instancing.

How do I draw an annulus (doughnut) using GDI+?

I have been trying to draw an annulus (ring with thickness) with a transparent hole and a gradient rim in C# with very little success. Does anyone have any suggestions on how to do this?
here's a nice Blend Utility
Here's the Final result - thanks to BlueMonkMN
Rectangle GetSquareRec(double radius, int x, int y)
{
double r = radius;
double side = Math.Sqrt(Math.Pow(r, 2) / 2);
Rectangle rec = new Rectangle(x - ((int)side), y - ((int)side), (int)(side * 2) + x, (int)(side * 2) + y);
return rec;
}
void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics gTarget = e.Graphics;
gTarget.SmoothingMode = SmoothingMode.AntiAlias;
GraphicsPath pTemp = new GraphicsPath();
Rectangle r = GetSquareRec(200, 225, 225);
pTemp.AddEllipse(r);
pTemp.AddEllipse(GetSquareRec(50, 225, 225));
Color[] colors = new Color[5];
colors[0] = Color.FromArgb(192, 192, 192);
colors[1] = Color.FromArgb(105, 0, 0);
colors[2] = Color.FromArgb(169, 169, 169);
colors[3] = Color.FromArgb(0, 0, 0);
colors[4] = Color.FromArgb(0, 0, 0);
float[] positions = new float[5];
positions[0] = 0f;
positions[1] = 0.1f;
positions[2] = 0.35f;
positions[3] = 0.5f;
positions[4] = 1f;
ColorBlend Cb = new ColorBlend();
Cb.Colors = colors;
Cb.Positions = positions;
PathGradientBrush pgb = new PathGradientBrush(pTemp);
pgb.InterpolationColors = Cb;
pgb.CenterPoint = new PointF(r.X + (r.Width / 2), r.Y + (r.Height / 2));
gTarget.FillPath(pgb, pTemp);
}
http://www.freeimagehosting.net/uploads/th.515733e62e.jpg
This is how I did it in the Scrolling Game Development Kit:
pTemp = new GraphicsPath();
pTemp.AddEllipse(Start.X, Start.Y, End.X - Start.X, End.Y - Start.Y);
pTemp.AddEllipse((Start.X * 3 + End.X) / 4f,
(Start.Y * 3 + End.Y) / 4f,
(End.X - Start.X) / 2f,
(End.Y - Start.Y) / 2f);
PathGradientBrush pgb = new PathGradientBrush(pTemp);
Blend b = new Blend();
b.Factors = new float[] { 0, 1, 1 };
b.Positions = new float[] { 0, .5F, 1 };
pgb.Blend = b;
pgb.CenterColor = ((SolidBrush)CurrentBrush).Color;
pgb.SurroundColors = new Color[] {CurrentPen.Color};
gTarget.FillPath(pgb, pTemp);
pgb.Dispose();
pTemp.Dispose();
(source: enigmadream.com)
I edited the original SGDK code for this sample because originally I wasn't smart enough to scale the gradient to exclude the hole, but now I guess I am :).
If you would rather see the gradient like this:
(source: enigmadream.com)
Then change the blend code to look like this:
Blend blend = new Blend();
blend.Factors = new float[] { 0, 1, 0, 0 };
blend.Positions = new float[] { 0, 0.25F, .5F, 1 };
pgb.Blend = blend;
You may use two calls to Graphics.DrawArc combined, drawing the top and bottom or left and right portions of the annulus, one portion at a time.

Categories