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};
Related
Trying to generate a sphere made of cubes like so:
This was easy, but any sphere of significant sizes has obvious performance issues if each cube is a separate gameobject, especially if the sphere is solid. After some digging, the answer appears to be to create a single mesh from only the exposed edges. Okay, makes sense. Found several videos, this one was the best I could find: link
I was able to customize the script for my needs, but I keep getting what appears to be a 2x2x2 cube regardless of the diameter input. I should be getting a hollow sphere made of a single mesh.
I suspect the issue is that I am not attaching the script to the correct type of gameobject, am missing a needed component on the gameobject, or because I skipped the UV portion.
Can anyone offer some advice?
Code:
public class PlanetGeneration : MonoBehaviour
{
private int planetDia = 30;
private int planetRadius;
//array of bools to keep track if each space is solid(true) or void(false)
private bool[,,] currentPlanet;
void Start()
{
currentPlanet = new bool[planetDia, planetDia, planetDia];
planetRadius = planetDia / 2 + 1;
GeneratePlanet();
GenerateMesh();
}
//define each block as void or solid
public void GeneratePlanet()
{
Vector3 planetCenter = new(planetRadius, planetRadius, planetRadius);
Vector3 currentPoint;
for (int x = 0; x < planetDia; x++)
{
for (int y = 0; y < planetDia; y++)
{
for (int z = 0; z < planetDia; z++)
{
//set the current position
currentPoint = new Vector3(x, y, z);
//check if this point is within the desired planet size
if (Vector3.Distance(planetCenter, currentPoint) <= planetRadius)
{
//make it a solid block
currentPlanet[x, y, z] = true;
}
//make it void
else currentPlanet[x, y, z] = false;
}
}
}
}
//generate mesh
public void GenerateMesh()
{
List<Vector3> Vertices = new List<Vector3>();
List<int> Triangles = new List<int>();
for (int x = 0; x < planetDia; x++)
{
for (int y = 0; y < planetDia; y++)
{
for (int z = 0; z < planetDia; z++)
{
//the 8 corners of a cube
Vector3[] VertexPos = new Vector3[8]
{
new Vector3(-1, 1, -1), new Vector3(-1, 1, 1),
new Vector3(1, 1, 1), new Vector3(1, 1, -1),
new Vector3(-1, -1, -1), new Vector3(-1, -1, 1),
new Vector3(1, -1, 1), new Vector3(1, -1, -1),
};
//first 4 digits are the vertices of the face, and the last 3 are the relative coords of the next block in that direction
int[,] Faces = new int[6, 7]{
{0, 1, 2, 3, 0, 1, 0}, //top
{7, 6, 5, 4, 0, -1, 0}, //bottom
{2, 1, 5, 6, 0, 0, 1}, //right
{0, 3, 7, 4, 0, 0, -1}, //left
{3, 2, 6, 7, 1, 0, 0}, //front
{1, 0, 4, 5, -1, 0, 0} //back
};
//if this block is solid
if (currentPlanet[x, y, z] == true)
{
//for each face of the block
for (int i = 0; i < 6; i++)
{
//check if it is on the edge of the overall array
//This part is dumb and needs to be removed. Keeping for now to prevent the array out of bounds error.
if(x + Faces[i, 4] == -1 || y + Faces[i, 5] == -1 || z + Faces[i, 6] == -1 ||
x + Faces[i, 4] == planetDia || y + Faces[i, 5] == planetDia || z + Faces[i, 6] == planetDia)
{
//do nothing
}
//check if the block next to it is void
else if(currentPlanet[x + Faces[i, 4], y + Faces[i, 5], z + Faces[i, 6]] == false)
{
AddQuad(Faces[i, 0], Faces[i, 1], Faces[i, 2], Faces[i, 3], Vertices.Count);
}
}
}
void AddQuad(int ai, int bi, int ci, int di, int i)
{
Vector3 a = VertexPos[ai];
Vector3 b = VertexPos[bi];
Vector3 c = VertexPos[ci];
Vector3 d = VertexPos[di];
Vertices.AddRange(new List<Vector3>() { a, b, c, d });
Triangles.AddRange(new List<int>() { i, i + 1, i + 2, i, i + 2, i + 3 });
}
}
}
}
this.gameObject.GetComponent<MeshFilter>().mesh = new Mesh()
{
vertices = Vertices.ToArray(),
triangles = Triangles.ToArray(),
};
}
}
After some more testing, and finding the youtube video authors updated code on github, I made several corrections and got it working. Code below in case anyone wants to reference.
Also of note - make a blank game object, attach the script, add a mesh filter and mesh renderer, set the material in the renderer.
This is a good starting point for anyone looking for this basic idea.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class PlanetGeneration : MonoBehaviour
{
//must be an odd number
private int planetDia = 39;
private int planetRadius;
//array of bools to keep track if each space is solid(true) or void(false)
private bool[,,] currentPlanet;
void Start()
{
currentPlanet = new bool[planetDia, planetDia, planetDia];
planetRadius = planetDia / 2 - 1;
GeneratePlanet();
GenerateMesh();
}
//define each block as void or solid
public void GeneratePlanet()
{
Vector3 planetCenter = new(planetRadius + 1, planetRadius + 1, planetRadius + 1);
Vector3 currentPoint;
for (int x = 0; x < planetDia; x++)
{
for (int y = 0; y < planetDia; y++)
{
for (int z = 0; z < planetDia; z++)
{
//set the current position
currentPoint = new Vector3(x, y, z);
//check if this point is within the desired planet size
if (Vector3.Distance(planetCenter, currentPoint) <= planetRadius)
{
//make it a solid block
currentPlanet[x, y, z] = true;
}
//make it void
else currentPlanet[x, y, z] = false;
}
}
}
}
//generate mesh
public void GenerateMesh()
{
List<int> Triangles = new();
List<Vector3> Verticies = new();
List<Vector2> uv = new();
for (int x = 1; x < planetDia; x++)
{
for (int y = 1; y < planetDia; y++)
{
for (int z = 1; z < planetDia; z++)
{
//the 8 corners of a cube
Vector3[] VertexPos = new Vector3[8]
{
new Vector3(-1, 1, -1), new Vector3(-1, 1, 1),
new Vector3(1, 1, 1), new Vector3(1, 1, -1),
new Vector3(-1, -1, -1), new Vector3(-1, -1, 1),
new Vector3(1, -1, 1), new Vector3(1, -1, -1),
};
//first 4 digits are the vertices of the face, and the last 3 are the relative coords of the next block in that direction, the last two are for the UV
int[,] Faces = new int[6, 9]{
{0, 1, 2, 3, 0, 1, 0, 0, 0}, //top
{7, 6, 5, 4, 0, -1, 0, 1, 0}, //bottom
{2, 1, 5, 6, 0, 0, 1, 1, 1}, //right
{0, 3, 7, 4, 0, 0, -1, 1, 1}, //left
{3, 2, 6, 7, 1, 0, 0, 1, 1}, //front
{1, 0, 4, 5, -1, 0, 0, 1, 1} //back
};
//if this block is solid
if (currentPlanet[x, y, z] == true)
{
//for each face of the block
for (int i = 0; i < 6; i++)
{
//check if the block next to it is void
if(currentPlanet[x + Faces[i, 4], y + Faces[i, 5], z + Faces[i, 6]] == false)
{
AddQuad(i, Verticies.Count);
}
}
}
void AddQuad(int facenum, int v)
{
// Add Mesh
for (int i = 0; i < 4; i++) Verticies.Add(new Vector3(x, y, z) + VertexPos[Faces[facenum, i]] / 2f);
Triangles.AddRange(new List<int>() { v, v + 1, v + 2, v, v + 2, v + 3 });
// Add uvs
Vector2 bottomleft = new Vector2(Faces[facenum, 7], Faces[facenum, 8]) / 2f;
uv.AddRange(new List<Vector2>() { bottomleft + new Vector2(0, 0.5f), bottomleft + new Vector2(0.5f, 0.5f), bottomleft + new Vector2(0.5f, 0), bottomleft });
}
}
}
}
GetComponent<MeshFilter>().mesh = new Mesh()
{
vertices = Verticies.ToArray(),
triangles = Triangles.ToArray(),
uv = uv.ToArray()
};
}
}
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.
I have a little problem with rendering in my SharpDX Direct11 App.
I had being tested rendering scene on a texture, and then draw this texture on backBuffer... but unfortunately renderTexture do not contains primitives which should be drawn. Texture is only filled by color.
Whole project on github: https://github.com/Kordi3112/SharpDXTest11
Main code part with rendering methods:
public override void Render()
{
//Camera
var proj = Matrix.OrthoLH(3 * Form.Bounds.Width / Form.Bounds.Height, 3, 0.01f, 100f);
var view = Matrix.LookAtLH(new Vector3(0, 0, -10), new Vector3(0, 0, 20), Vector3.UnitY);
var viewProj = Matrix.Multiply(view, proj);
var world = Matrix.Identity;
var worldViewProj = world * viewProj;
worldViewProj.Transpose();
//Update wvp matrix
Context.UpdateSubresource(ref worldViewProj, ContantBuffer);
DrawOnTexture();
//Set BackBuffer as render target
Context.OutputMerger.SetTargets(depthView, renderView);
// Clear views
Context.ClearDepthStencilView(depthView, DepthStencilClearFlags.Depth, 1.0f, 0);
Context.ClearRenderTargetView(renderView, Color.Pink);
//Set TextureColor Shader
Effect2.ApplyShader(Context);
//Set Buffers
Context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(VertexBuffer2, Utilities.SizeOf<VertexPositionColorTexture>(), 0));
Context.InputAssembler.SetIndexBuffer(IndexBuffer, Format.R32_UInt, 0);
//Set Texture to Shader
Context.PixelShader.SetShaderResource(0, RenderTexture.ShaderResourceView);
//Draw
Context.DrawIndexed(6, 0, 0);
// Present!
SwapChain.Present(0, PresentFlags.None);
}
private void DrawOnTexture()
{
//Set Color Shader
Effect1.ApplyShader(Context);
//Set Buffers
Context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(VertexBuffer, Utilities.SizeOf<VertexPositionColor>(), 0));
Context.InputAssembler.SetIndexBuffer(IndexBuffer, Format.R32_UInt, 0);
//Set Target
RenderTexture.SetRenderTarget(Context, depthView);
//Clear Targets - Green Bgound
RenderTexture.ClearRenderTarget(Context, depthView, 0, 1, 0, 1);
//Draw on RenderTarget
Context.DrawIndexed(6, 0, 0);
}
After call: Context.DrawIndexed(6, 0, 0); in private void DrawOnTexture() primitive should be drawn.
What this code above do
What i wanted to get
What's wrong with my code?
I'm sure the problem is not matrix or camera. When i will modify code to render primitive directly on backBuffer then its drawing normaly.
public override void Render()
{
//Camera
var proj = Matrix.OrthoLH(3 * Form.Bounds.Width / Form.Bounds.Height, 3, 0.01f, 100f);
var view = Matrix.LookAtLH(new Vector3(0, 0, -10), new Vector3(0, 0, 20), Vector3.UnitY);
var viewProj = Matrix.Multiply(view, proj);
var world = Matrix.Identity;
var worldViewProj = world * viewProj;
worldViewProj.Transpose();
//Update wvp matrix
Context.UpdateSubresource(ref worldViewProj, ContantBuffer);
//DrawOnTexture();
//Set BackBuffer as render target
Context.OutputMerger.SetTargets(depthView, renderView);
// Clear views
Context.ClearDepthStencilView(depthView, DepthStencilClearFlags.Depth, 1.0f, 0);
Context.ClearRenderTargetView(renderView, Color.Pink);
//Set Color Shader
Effect1.ApplyShader(Context);
//Set Buffers
Context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(VertexBuffer, Utilities.SizeOf<VertexPositionColor>(), 0));
Context.InputAssembler.SetIndexBuffer(IndexBuffer, Format.R32_UInt, 0);
//Set Texture to Shader
//Context.PixelShader.SetShaderResource(0, RenderTexture.ShaderResourceView);
//Draw
Context.DrawIndexed(6, 0, 0);
// Present!
SwapChain.Present(0, PresentFlags.None);
}
output
Vertex Buffers declaration:
//Position Color
VertexBuffer = Buffer.Create(Device, BindFlags.VertexBuffer, new[] {
new VertexPositionColor(new Vector4(-1, -1, 0, 1), Color.Red.ToVector4()),
new VertexPositionColor(new Vector4(-1, 1, 0, 1), Color.Green.ToVector4()),
new VertexPositionColor(new Vector4(1, 1, 0, 1), Color.Blue.ToVector4()),
new VertexPositionColor(new Vector4(1, -1, 0, 1), Color.Yellow.ToVector4())
});
//Position Color Texture
VertexBuffer2 = Buffer.Create(Device, BindFlags.VertexBuffer, new[] {
new VertexPositionColorTexture(new Vector4(-1, -1, 0, 1), Color.White.ToVector4(), new Vector2(0,1)),
new VertexPositionColorTexture(new Vector4(-1, 1, 0, 1), Color.White.ToVector4(),new Vector2(0,0)),
new VertexPositionColorTexture(new Vector4(1, 1, 0, 1), Color.White.ToVector4(),new Vector2(1,0)),
new VertexPositionColorTexture(new Vector4(1, -1, 0, 1), Color.White.ToVector4(),new Vector2(1,1))
});
IndexBuffer = Buffer.Create(Device, BindFlags.IndexBuffer, new[] {
0,1,2,
0,2,3
});
I'm trying to draw a polygon of a solid color. I'm given a vector of 2D vectors, ordered such that there will be an edge from v0 to v1 to v2 to ... to vn to v0.
Does such a primitive exist?
Credit to Stefan Agartsson for the post this answer's code is based upon.
To describe the vertices as edges instead of triangles, You can use the PrimitiveType.LineStrip instead of PrimitiveType.TriangleList you have to adjust the index and counts as well since each vertex is used only once.
BasicEffect basicEffect = new BasicEffect(device);
basicEffect.Texture = myTexture;
basicEffect.TextureEnabled = true;
VertexPositionTexture[] vert = new VertexPositionTexture[4];
vert[0].Position = new Vector3(0, 0, 0); //These connect in order
vert[1].Position = new Vector3(100, 0, 0);
vert[2].Position = new Vector3(100, 100, 0);
vert[3].Position = new Vector3(0, 100, 0);
vert[0].TextureCoordinate = new Vector2(0, 0);
vert[1].TextureCoordinate = new Vector2(1, 0);
vert[2].TextureCoordinate = new Vector2(1, 1);
vert[3].TextureCoordinate = new Vector2(0, 1);
short[] ind = new short[n+1]; // the +1 is to close the shape
for(int i = 0; i < n; i++)
ind[i] = i;
ind[n] = 0; // return to start to close.
Then in your render loop you do this:
foreach (EffectPass effectPass in basicEffect.CurrentTechnique.Passes)
{
effectPass.Apply();
device.DrawUserIndexedPrimitives<VertexPositionTexture>(
PrimitiveType.LineStrip, vert, 0, vert.Length, ind, 0, ind.Length);
}
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.