How to texture glDrawElements - opengl

I have no Idea how I would texture something drawn by using glDrawElements?
From what I gather you need to use glTexCoordPointer? but I'm still really confused.
I'll put the whole program here so you can see what I'm doing
public class DrawWater {
public Expr2 func; // The function that is being drawn.
private String functionInput;
private boolean version_1_5; // Check is OpenGL 1.5 is available; set in init().
public boolean dataIsValid; // Set to true whenever data needs to be recomputed.
public double xMax = 5;
public double yMax = 5;
private int[] textures = new int[3]; // Storage For 3 Textures
// This is checked in the display() method before drawing.
/* Buffers to hold the points and normals for the surface. */
private FloatBuffer vBuf = BufferUtil.newFloatBuffer(201 * 201 * 3);
private FloatBuffer nBuf = BufferUtil.newFloatBuffer(201 * 201 * 3);
/* Buffers to hold the indices for drawing the surface and lines with glDrawElements*/
private IntBuffer surfaceIndexBuffer = BufferUtil.newIntBuffer(200 * 201 * 2);
private IntBuffer xLineIndexBuffer = BufferUtil.newIntBuffer(21 * 201);
private IntBuffer yLineIndexBuffer = BufferUtil.newIntBuffer(21 * 201);
/* VBO ID numbers for holding the data when OpenGL version is 1.5 or higher */
private int vertexVBO, normalVBO; // VBO IDs for surface data.
private int xLineVBO, yLineVBO, surfaceVBO; // VBO IDs for index data.
public DrawWater() {
}
public void setup(GL gl, String equ) {
this.functionInput = equ;
this.func = new Expr2(equ);
}
public void draw(GL gl) {
version_1_5 = gl.isExtensionAvailable("GL_VERSION_1_5");
if (gl.isExtensionAvailable("GL_VERSION_1_3")) {
gl.glEnable(GL.GL_MULTISAMPLE);
}
makeElementBuffers(); // Generate lists of indices for glDrawElements. This data never changes.
if (version_1_5) {
// Generate VBOs for the data, and fill the ones that are for index data with
// data from Java nio buffers. The VBOs for index data won't change again and
// so use GL.GL_STATIC_DRAW.
int[] ids = new int[5];
gl.glGenBuffers(5, ids, 0);
this.vertexVBO = ids[0];
this.normalVBO = ids[1];
this.xLineVBO = ids[2];
this.yLineVBO = ids[3];
this.surfaceVBO = ids[4];
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vertexVBO);
gl.glVertexPointer(3, GL.GL_FLOAT, 0, 0);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, normalVBO);
gl.glNormalPointer(GL.GL_FLOAT, 0, 0);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, surfaceVBO);
gl.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, 4 * 2 * 200 * 201, surfaceIndexBuffer, GL.GL_STATIC_DRAW);
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, xLineVBO);
gl.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, 4 * 21 * 201, xLineIndexBuffer, GL.GL_STATIC_DRAW);
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, yLineVBO);
gl.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, 4 * 21 * 201, yLineIndexBuffer, GL.GL_STATIC_DRAW);
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0);
} else {
gl.glVertexPointer(3, GL.GL_FLOAT, 0, vBuf);
gl.glNormalPointer(GL.GL_FLOAT, 0, nBuf);
}
this.dataIsValid = false; // Force recomputation of data with new graph definition.
if (func != null) {
gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, new float[]{0.7f, 0.7f, 1}, 0);
gl.glMaterialfv(GL.GL_BACK, GL.GL_AMBIENT_AND_DIFFUSE, new float[]{0.8f, 0.8f, 0.5f}, 0);
if (!dataIsValid) {
this.computeSurfaceData();
if (version_1_5) {
// Set up VBOs for surface points and normals. Since these change
// pretty regularly, use GL.GL_DYNAMIC_DRAW.
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vertexVBO);
gl.glBufferData(GL.GL_ARRAY_BUFFER, 4 * 3 * 201 * 201, vBuf, GL.GL_DYNAMIC_DRAW);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, normalVBO);
gl.glBufferData(GL.GL_ARRAY_BUFFER, 4 * 3 * 201 * 201, nBuf, GL.GL_DYNAMIC_DRAW);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
}
}
gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL.GL_NORMAL_ARRAY);
this.drawSurface(gl); // Just draw the surface.
gl.glPolygonOffset(1, 1);
gl.glEnable(GL.GL_POLYGON_OFFSET_FILL);
this.drawSurface(gl);
gl.glDisable(GL.GL_POLYGON_OFFSET_FILL);
gl.glDisable(GL.GL_LIGHTING);
gl.glColor3f(0, 0, 0);
gl.glDisableClientState(GL.GL_NORMAL_ARRAY);
gl.glEnable(GL.GL_LIGHTING);
}
gl.glDisableClientState(GL.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL.GL_NORMAL_ARRAY);
}
private void makeElementBuffers() {
for (int i = 0; i < 201; i += 10) { // indices for drawing lines in x-direction
for (int j = 0; j < 201; j++) {
this.xLineIndexBuffer.put(201 * i + j);
}
}
for (int i = 0; i < 201; i += 10) { // indices for drawing lines in y-direction
for (int j = 0; j < 201; j++) {
this.yLineIndexBuffer.put(201 * j + i);
}
}
for (int i = 0; i < 200; i++) { // indices for drawing surface with GL_TRIANGLE_STRIPs
for (int j = 0; j < 201; j++) {
this.surfaceIndexBuffer.put(201 * (i + 1) + j);
this.surfaceIndexBuffer.put(201 * i + j);
}
}
this.xLineIndexBuffer.rewind();
this.yLineIndexBuffer.rewind();
this.surfaceIndexBuffer.rewind();
}
private void computeSurfaceData() {
double xmin = -xMax;
double xmax = xMax;
double ymin = -yMax;
double ymax = yMax;
double xRes = 200;
double yRes = 200;
float[] surfaceData = new float[301 * 3];
float[] normalData = new float[301 * 3];
double dx = (xmax - xmin) / xRes;
double dy = (ymax - ymin) / yRes;
for (int i = 0; i <= xRes; i++) {
int v = 0;
int n = 0;
double y1 = ymin + dy * i;
for (int j = 0; j <= yRes; j++) {
double x = xmin + dx * j;
func.setVariable('x', x);
func.setVariable('y', y1);
double z1 = func.value();
float[] normal1 = computeUnitNormal(x, y1);
surfaceData[v++] = (float) x;
surfaceData[v++] = (float) y1;
surfaceData[v++] = (float) z1;
normalData[n++] = normal1[0];
normalData[n++] = normal1[1];
normalData[n++] = normal1[2];
}
vBuf.put(surfaceData, 0, 201 * 3);
nBuf.put(normalData, 0, 201 * 3);
}
vBuf.rewind();
nBuf.rewind();
dataIsValid = true;
}
/**
* Draw the surface as a series of triangle strips.
*/
private void drawSurface(GL gl) {
if (version_1_5) {
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, surfaceVBO);
for (int i = 0; i < 200; i++) {
gl.glTexCoordPointer(3, GL.GL_TRIANGLE_STRIP, 402, nBuf);
gl.glDrawElements(GL.GL_TRIANGLE_STRIP, 402, GL.GL_UNSIGNED_INT, 402 * i * 4);
}
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0);
} else {
for (int i = 0; i < 200; i++) {
surfaceIndexBuffer.position(402 * i);
gl.glDrawElements(GL.GL_TRIANGLE_STRIP, 402, GL.GL_UNSIGNED_INT, surfaceIndexBuffer);
}
}
}
/**
* Compute a unit normal to the graph of z = func(x,y).
* This is only an approximation, using nearby points instead
* of exact derivatives.
*/
private float[] computeUnitNormal(double x, double y) {
double epsilon = 0.00001;
func.setVariable('x', x);
func.setVariable('y', y);
double z = this.func.value();
func.setVariable('x', x + epsilon);
double z1 = func.value();
func.setVariable('x', x);
func.setVariable('y', y + epsilon);
double z2 = this.func.value();
// normal is (epsilon,0,z1-z) X (0,epsilon,z2-z)
double a = -epsilon * (z1 - z);
double b = -epsilon * (z2 - z);
double c = epsilon * epsilon;
double length = Math.sqrt(a * a + b * b + c * c);
if (Double.isNaN(length) || Double.isInfinite(length)) {
return new float[]{0, 0, 1};
} else {
return new float[]{(float) (a / length), (float) (b / length), (float) (c / length)};
}
}
private void loadGLTextures(GLAutoDrawable gldrawable) throws IOException {
TextureReader.Texture texture = null;
texture = TextureReader.readTexture("data/images/04.bmp");
GL gl = gldrawable.getGL();
//Create Nearest Filtered Texture
gl.glGenTextures(1, textures, 0);
gl.glBindTexture(GL.GL_TEXTURE_2D, textures[0]);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
gl.glTexImage2D(GL.GL_TEXTURE_2D,
0,
3,
texture.getWidth(),
texture.getHeight(),
0,
GL.GL_RGB,
GL.GL_UNSIGNED_BYTE,
texture.getPixels());
}
}
Any help or advise would help I'm just confused ?
Ya this is the array buffer
int[] ids = new int[5];
gl.glGenBuffers(5, ids, 0);
this.vertexVBO = ids[0];
this.normalVBO = ids[1];
this.xLineVBO = ids[2];
this.yLineVBO = ids[3];
this.surfaceVBO = ids[4];
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vertexVBO);
gl.glVertexPointer(3, GL.GL_FLOAT, 0, 0);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, normalVBO);
gl.glNormalPointer(GL.GL_FLOAT, 0, 0);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, surfaceVBO);
gl.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, 4 * 2 * 200 * 201, surfaceIndexBuffer, GL.GL_STATIC_DRAW);
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, xLineVBO);
gl.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, 4 * 21 * 201, xLineIndexBuffer, GL.GL_STATIC_DRAW);
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, yLineVBO);
gl.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, 4 * 21 * 201, yLineIndexBuffer, GL.GL_STATIC_DRAW);
I can add the whole program if you'd like to see?

As you have created (I hope) a GL_ARRAY_BUFFER with your vertices and set it with glVertexPointer, you should create a GL_ARRAY_BUFFER with your texture coordinates and set it using glTexCoordPointer. Could you copy paste your glVertexPointer call?

Related

DirectX11 only draws half of the vertices in a populated index and vertex buffer

Im having an issue that I cannot seem to resolve.
Im importing obj files with TinyObjLoader, and copying vertex and index buffers to GPU memory to draw the model. There are only triangles in this model. No 4+ sided polygons, and no negative indices
The only problem is I cannot draw the full model, only 1/2 to 2/3 of the vertices are drawn, with correct vertices, correct normal, and correct textures.
I split the code in two methods with #defines to illustrate this issue.
Note, there is a heavy amount of trial and error that went into figuring this out to make it work, as there is a scarcity of tutorials and education online on DirectX11. If you see any issues with the way the code is structured please feel free to comment.
Method 1 is the failing code. Here I make duplicates of the vertices in order to have different normals. This is essential in my model because each surface needs a different shading. Note:I understand I can make this rendering more optimized with std::unordered_map for some duplicate vertices where normals point in the same direction.
#ifdef DUPVERTICES
std::vector<float> vertex_buffer;
std::vector<uint32_t> index_buffer;
struct T_Vertex
{
float vX;
float vY;
float vZ;
float nX;
float nY;
float nZ;
float tX;
float tY;
uint32_t vXIndex;
uint32_t vYIndex;
uint32_t vZIndex;
uint32_t nXIndex;
uint32_t nYIndex;
uint32_t nZIndex;
uint32_t tXIndex;
uint32_t tYIndex;
};
std::vector<T_Vertex> temp_vertices;
size_t index_offset = 0;
for (size_t f = 0; f < shapes[0].mesh.num_face_vertices.size(); f++) {
int fv = shapes[0].mesh.num_face_vertices[f];
// Loop over vertices in the face.
for (size_t v = 0; v < fv; v++) {
// access to vertex
tinyobj::index_t idx = shapes[0].mesh.indices[index_offset + v];
if (idx.vertex_index < 0 || idx.normal_index < 0 || idx.texcoord_index < 0)
continue;
T_Vertex temp_vertex;
temp_vertex.vX = idx.normal_index > 0 ? attrib.vertices[3 * idx.vertex_index + 0] : 0;
temp_vertex.vY = idx.normal_index > 0 ? attrib.vertices[3 * idx.vertex_index + 1] : 0;
temp_vertex.vZ = idx.normal_index > 0 ? attrib.vertices[3 * idx.vertex_index + 2] : 0;
temp_vertex.nX = idx.normal_index > 0 ? attrib.normals[3 * idx.normal_index + 0] : 0;
temp_vertex.nY = idx.normal_index > 0 ? attrib.normals[3 * idx.normal_index + 1] : 0;
temp_vertex.nZ = idx.normal_index > 0 ? attrib.normals[3 * idx.normal_index + 2] : 0;
temp_vertex.tX = idx.texcoord_index > 0 ? attrib.texcoords[2 * idx.texcoord_index + 0] : 0;
temp_vertex.tY = idx.texcoord_index > 0 ? attrib.texcoords[2 * idx.texcoord_index + 1] : 0;
temp_vertex.vXIndex = 3 * idx.vertex_index + 0;
temp_vertex.vYIndex = 3 * idx.vertex_index + 1;
temp_vertex.vZIndex = 3 * idx.vertex_index + 2;
temp_vertex.nXIndex = 3 * idx.normal_index + 0;
temp_vertex.nYIndex = 3 * idx.normal_index + 1;
temp_vertex.nZIndex = 3 * idx.normal_index + 2;
temp_vertex.tXIndex = 2 * idx.texcoord_index + 0;
temp_vertex.tYIndex = 2 * idx.texcoord_index + 1;
temp_vertices.push_back(temp_vertex);
}
index_offset += fv;
}
for (auto& temp_vertex : temp_vertices)
{
vertex_buffer.push_back(temp_vertex.vX);
vertex_buffer.push_back(temp_vertex.vY);
vertex_buffer.push_back(temp_vertex.vZ);
vertex_buffer.push_back(temp_vertex.nX);
vertex_buffer.push_back(temp_vertex.nY);
vertex_buffer.push_back(temp_vertex.nZ);
vertex_buffer.push_back(temp_vertex.tX); //Set to 0 for no texture
vertex_buffer.push_back(temp_vertex.tY); //Set to 0 for no texture
vertex_buffer.push_back(0.0F);
index_buffer.push_back(temp_vertex.vXIndex);
index_buffer.push_back(temp_vertex.vYIndex);
index_buffer.push_back(temp_vertex.vZIndex);
index_buffer.push_back(temp_vertex.nXIndex);
index_buffer.push_back(temp_vertex.nYIndex);
index_buffer.push_back(temp_vertex.nZIndex);
index_buffer.push_back(0);
index_buffer.push_back(0);
index_buffer.push_back(0);
}
Method 2 works (minus textures) but there are no duplicate vertices, and therefore the normal orientation is not good for rendering. However, in this configuration, every single vertex is drawn in correct place. Notice the stride and vertex and index buffer structures are the same in both codes, its the size of the buffers that differs.
#else
std::vector<float> vertex_buffer;
for (int i = 0, j = 0; i < attrib.vertices.size(); i += 3, j += 2)
{
vertex_buffer.push_back(attrib.vertices[i + 0]);
vertex_buffer.push_back(attrib.vertices[i + 1]);
vertex_buffer.push_back(attrib.vertices[i + 2]);
vertex_buffer.push_back(attrib.normals[i + 0]);
vertex_buffer.push_back(attrib.normals[i + 1]);
vertex_buffer.push_back(attrib.normals[i + 2]);
vertex_buffer.push_back(0);//attrib.texcoords[j + 0]);
vertex_buffer.push_back(0);//attrib.texcoords[j + 1]);
vertex_buffer.push_back(0.0F);
}
std::vector<UINT> index_buffer;
for (int i = 0, j = 0; i < shapes[0].mesh.indices.size(); i += 3, j += 2)
{
index_buffer.push_back(shapes[0].mesh.indices[i + 0].vertex_index);
index_buffer.push_back(shapes[0].mesh.indices[i + 1].vertex_index);
index_buffer.push_back(shapes[0].mesh.indices[i + 2].vertex_index);
index_buffer.push_back(shapes[0].mesh.indices[i + 0].normal_index);
index_buffer.push_back(shapes[0].mesh.indices[i + 1].normal_index);
index_buffer.push_back(shapes[0].mesh.indices[i + 2].normal_index);
index_buffer.push_back(0);
index_buffer.push_back(0);
index_buffer.push_back(0);
}
uint32_t vertexes_size = vertex_buffer.size() * sizeof(float);
uint32_t indexes_size = index_buffer.size() * sizeof(uint32_t);
int stride_bytes = 36;
#endif
This is where I create buffers, its the same code for both methods
//Set Vertex Buffer Array
g_Mesh11.m_pMeshArray = new SDKMESH_MESH;
g_Mesh11.m_pVertexBufferArray = new SDKMESH_VERTEX_BUFFER_HEADER;
int t = g_Mesh11.m_pMeshArray[0].VertexBuffers[0];
g_Mesh11.m_pMeshArray[0].VertexBuffers[0] = 0;
D3D11_BUFFER_DESC vertex_buf_desc;
vertex_buf_desc.ByteWidth = vertexes_size;
vertex_buf_desc.Usage = D3D11_USAGE_DEFAULT;
vertex_buf_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertex_buf_desc.CPUAccessFlags = 0;
vertex_buf_desc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA init_vertex_data;
init_vertex_data.pSysMem = &vertex_buffer[0];
dxCtr->m_pDevice->CreateBuffer(&vertex_buf_desc, &init_vertex_data, &g_Mesh11.m_pVertexBufferArray[g_Mesh11.m_pMeshArray[0].VertexBuffers[0]].pVB11);
g_Mesh11.m_pVertexBufferArray[g_Mesh11.m_pMeshArray[0].VertexBuffers[0]].StrideBytes = stride_bytes;
g_Mesh11.m_pVertexBufferArray[g_Mesh11.m_pMeshArray[0].VertexBuffers[0]].SizeBytes = vertexes_size;
//Set Index Buffer array
g_Mesh11.m_pMeshArray[0].IndexBuffer = 0;
g_Mesh11.m_pIndexBufferArray = new SDKMESH_INDEX_BUFFER_HEADER;
g_Mesh11.m_pIndexBufferArray[g_Mesh11.m_pMeshArray[0].IndexBuffer].IndexType = IT_32BIT;
D3D11_BUFFER_DESC index_buf_desc;
index_buf_desc.ByteWidth = indexes_size;
index_buf_desc.Usage = D3D11_USAGE_DEFAULT;
index_buf_desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
index_buf_desc.CPUAccessFlags = 0;
index_buf_desc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA init_index_data;
init_index_data.pSysMem = &index_buffer[0];
dxCtr->m_pDevice->CreateBuffer(&index_buf_desc, &init_index_data, &g_Mesh11.m_pIndexBufferArray[g_Mesh11.m_pMeshArray[0].IndexBuffer].pIB11);
g_Mesh11.m_pIndexBufferArray[g_Mesh11.m_pMeshArray[0].IndexBuffer].SizeBytes = indexes_size;
//Set subset
SDKMESH_SUBSET v_subset;
v_subset.MaterialID = 0;
v_subset.PrimitiveType = PT_TRIANGLE_LIST;
v_subset.IndexCount = index_buffer.size();
v_subset.VertexCount = vertex_buffer.size();
v_subset.VertexStart = 0;
v_subset.IndexStart = 0;
g_Mesh11.m_pMeshArray[0].pSubsets = new uint32_t;
g_Mesh11.m_pMeshArray[0].pSubsets[0] = 0;
g_Mesh11.m_pMeshArray[0].NumSubsets = 1;
g_Mesh11.m_pSubsetArray = new SDKMESH_SUBSET;
g_Mesh11.m_pSubsetArray[g_Mesh11.m_pMeshArray[0].pSubsets[0]] = v_subset;
Additional information:
I tried with lower poly count models and the issue is still there, so I am not hitting some limit of vertices.
Here are the sizes of the arrays
attrib.vertices.size = 150201
attrib.normals.size = 173712
attrib.normals.size = 135956
shapes[0].mesh.indices.size() = 300978
In Method 1 in the above example
temp_vertices.size() = 300978 (matches index size, so I'm not missing any vertices in temp_vertices)
vertex_buffer.size() = index_buffer.size() = 2708802
In Method 2 in the above example
vertex_buffer.size() = index_buffer.size() = 450603
Here is the input layout
// Create our vertex input layout
const D3D11_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
Here is the Vertex Shader
cbuffer cbPerObject : register( b0 )
{
matrix g_mWorldViewProjection : packoffset( c0 );
matrix g_mWorld : packoffset( c4 );
};
//--------------------------------------------------------------------------------------
// Input / Output structures
//--------------------------------------------------------------------------------------
struct VS_INPUT
{
float4 vPosition : POSITION;
float3 vNormal : NORMAL;
float2 vTexcoord : TEXCOORD0;
};
struct VS_OUTPUT
{
float3 vNormal : NORMAL;
float2 vTexcoord : TEXCOORD0;
float4 vPosition : SV_POSITION;
};
//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
VS_OUTPUT VSMain( VS_INPUT Input )
{
VS_OUTPUT Output;
Output.vPosition = mul( Input.vPosition, g_mWorldViewProjection );
Output.vNormal = mul( Input.vNormal, (float3x3)g_mWorld );
Output.vTexcoord = Input.vTexcoord;
return Output;
}
I realize there are inconsistencies between the input layout, the shader, and my vertex_buffer vector. Mainly, the input layout is 32 bytes, my buffer is 36 bytes with 3 bytes for texcoord, and the shader is 36 bytes, but the position is 4 bytes, and texcoords are 2 bytes.
The shader and initialization was from DXUT and I did not mess with those. It draws the supplied Tiny.sdkmesh model correctly, which incidentally has a stride of 32 bytes, matching that of input layout.
If somebody can help explain why the shader VS_INPUT differs from the input layout, it would greatly help as well.
Changing the vertex shader to math input layout resulted in compilation errors. Changing input layout to add an extra byte to texcoord did not really make a difference.
Note: I tried removing the vertex_buffer.push_back(0.0F); and index_buffer.push_back(0); from the end and changing stride_bytes to 32, but it was no longer drawing vertices correctly.
I've ran out of trial and error methods to test and looking for help figuring out what I'm doing wrong.
Thank you,
After dissecting the working tiny.sdkmesh file, I found what I was doing wrong.
The index is only 1 entry/byte per vertex (which is 32 bytes)
I tried it before, but not successfully, here is the working code:
Still looking for some explanation to help understand why the Input_Layout differs from Vertex Shader in number of bytes
std::vector<float> vertex_buffer;
std::vector<uint32_t> index_buffer;
struct T_Vertex
{
float vX;
float vY;
float vZ;
float nX;
float nY;
float nZ;
float tX;
float tY;
uint32_t vIndex;
};
std::vector<T_Vertex> temp_vertices;
size_t index_offset = 0;
int ind = 0;
for (size_t f = 0; f < shapes[0].mesh.num_face_vertices.size(); f++) {
int fv = shapes[0].mesh.num_face_vertices[f];
// Loop over vertices in the face.
for (size_t v = 0; v < fv; v++) {
// access to vertex
tinyobj::index_t idx = shapes[0].mesh.indices[index_offset + v];
if (idx.vertex_index < 0 || idx.normal_index < 0 || idx.texcoord_index < 0)
continue;
T_Vertex temp_vertex;
temp_vertex.vX = idx.normal_index > 0 ? attrib.vertices[3 * idx.vertex_index + 0] : 0;
temp_vertex.vY = idx.normal_index > 0 ? attrib.vertices[3 * idx.vertex_index + 1] : 0;
temp_vertex.vZ = idx.normal_index > 0 ? attrib.vertices[3 * idx.vertex_index + 2] : 0;
temp_vertex.nX = idx.normal_index > 0 ? attrib.normals[3 * idx.normal_index + 0] : 0;
temp_vertex.nY = idx.normal_index > 0 ? attrib.normals[3 * idx.normal_index + 1] : 0;
temp_vertex.nZ = idx.normal_index > 0 ? attrib.normals[3 * idx.normal_index + 2] : 0;
temp_vertex.tX = idx.texcoord_index > 0 ? attrib.texcoords[2 * idx.texcoord_index + 0] : 0;
temp_vertex.tY = idx.texcoord_index > 0 ? attrib.texcoords[2 * idx.texcoord_index + 1] : 0;
temp_vertices.push_back(temp_vertex);
temp_vertex.vIndex = ++ind;
}
index_offset += fv;
}
for (auto& temp_vertex : temp_vertices)
{
vertex_buffer.push_back(temp_vertex.vX);
vertex_buffer.push_back(temp_vertex.vY);
vertex_buffer.push_back(temp_vertex.vZ);
vertex_buffer.push_back(temp_vertex.nX);
vertex_buffer.push_back(temp_vertex.nY);
vertex_buffer.push_back(temp_vertex.nZ);
vertex_buffer.push_back(temp_vertex.tX); //Set to 0 for no texture
vertex_buffer.push_back(temp_vertex.tY); //Set to 0 for no texture
index_buffer.push_back(temp_vertex.vIndex);
}
uint32_t vertexes_size = vertex_buffer.size() * sizeof(float);
uint32_t indexes_size = index_buffer.size() * sizeof(uint32_t);
int stride_bytes = 32;

OpenGL application crashes some times

I am working on a OpenGL application in C++ for some months now and never faced this problem before.
Coding in Visual Studio 2012 I can either run the application inside of the IDE or launch the executable file manually. In both cases I can choose between the debug and the release build. The following issue only appears when I start the release build executable myself. Otherwise everything works fine.
The application crashes sometimes when I add new forms into the scene on the fly. My operating system Windows 8 64bit lets me Debug the program from the crash dialog which indeed didn't help so much since it is a release build with less debug informations available at runtime, but it at least told me that the application crashes near the draw call glDrawTriangles(). There is only one draw call inside a loop in my code.
It drives me crazy that the crash only occurs irregular. Sometimes the application runs quite well for some minutes, sometimes it crashes immediately, and sometimes it runs some seconds. But I think is is important to know that the application only crashes right after new forms are inserted into the scene which I first generate in another thread and second create the OpenGL buffers in the main thread.
Here are the problem details that were shown in the Windows crash dialog. It seems like the driver of my videocard ATI Radeon 7870 crashes.
Problem signature:
Problem Event Name: APPCRASH
Application Name: Application.exe
Application Version: 0.0.0.0
Application Timestamp: 50f1491a
Fault Module Name: atioglxx.dll
Fault Module Version: 6.14.10.11931
Fault Module Timestamp: 50650037
Exception Code: c0000005
Exception Offset: 001108ef
OS Version: 6.2.9200.2.0.0.256.27
Locale ID: 1031
Additional Information 1: 5861
Additional Information 2: 5861822e1919d7c014bbb064c64908b2
Additional Information 3: dac6
Additional Information 4: dac6c2650fa14dd558bd9f448e23afd1
Read our privacy statement online:
http://go.microsoft.com/fwlink/?linkid=190175
If the online privacy statement is not available, please read our privacy statement offline:
C:\Windows\system32\en-US\erofflps.txt
What I've done so far is updating my video card drivers and debugging my application since I notices that the result were not reliable since the crash occurs spontaneously. There are many source files and to be honest I am not sure which is effecting the bug. It might be a file called terrain.cpp so I paste the code here.
#pragma once
#include "system.h"
#include "debug.h"
#include <vector>
#include <cstdlib>
#include <future>
using namespace std;
#include <GLEW/glew.h>
#include <SFML/OpenGL.hpp>
#include <SFML/Graphics/Image.hpp>
using namespace sf;
#include <GLM/glm.hpp>
#include <GLM/gtc/noise.hpp>
using namespace glm;
#include "settings.h"
#include "camera.h"
#include "form.h"
#include "transform.h"
#include "terrain.h"
#include "shader.h"
#include "movement.h"
typedef detail::tvec3<int> vec3i;
class ComponentTerrain : public Component
{
void Init()
{
auto wld = Global->Add<StorageTerrain>("terrain");
tasking = false;
Texture();
Listeners();
}
void Update()
{
auto wld = Global->Get<StorageTerrain>("terrain");
auto stg = Global->Get<StorageSettings>("settings");
auto cam = Global->Get<StorageCamera>("camera");
auto cks = Entity->Get<StorageChunk>();
int Distance = (int)(.5f * stg->Viewdistance / CHUNK_X / 2);
Debug::Info("Terrain chunk distance " + to_string(Distance));
for(int X = -Distance; X <= Distance; ++X)
for(int Z = -Distance; Z <= Distance; ++Z)
{
addChunk(X + (int)cam->Position.x / CHUNK_X, 0, Z + (int)cam->Position.z / CHUNK_Z);
}
for(auto chunk : wld->chunks)
{
auto chk = cks.find(chunk.second);
float distance = (float)vec3(chunk.first[0] * CHUNK_X - cam->Position.x, chunk.first[1] * CHUNK_Y - cam->Position.y, chunk.first[2] * CHUNK_Z - cam->Position.z).length();
if(distance > stg->Viewdistance)
deleteChunk(chunk.first[0], chunk.first[1], chunk.first[2]);
}
if(tasking)
{
if(task.wait_for(chrono::milliseconds(0)) == future_status::ready)
{
tasking = false;
Data data = task.get();
Buffers(data);
}
}
else
{
for(auto chunk : cks)
if(chunk.second->changed)
{
tasking = true;
chunk.second->changed = false;
task = async(launch::async, &ComponentTerrain::Mesh, this, Data(chunk.first));
break;
}
}
}
struct Data
{
Data() {}
Data(unsigned int id) : id(id) {}
unsigned int id;
vector<float> Vertices, Normals, Texcoords;
vector<int> Elements;
};
future<Data> task;
bool tasking;
Image texture;
void Listeners()
{
Event->Listen("SystemInitialized", [=]{
auto cam = Global->Get<StorageCamera>("camera");
cam->Position = vec3(0, CHUNK_Y, 0);
cam->Angles = vec2(0.75, -0.25);
});
Event->Listen("InputBindChunk", [=]{
addChunk(rand() % 5, 0, rand() % 5);
addChunk(rand() % 5, 0, rand() % 5);
addChunk(rand() % 5, 0, rand() % 5);
});
}
unsigned int getChunk(int X, int Y, int Z)
{
auto wld = Global->Get<StorageTerrain>("terrain");
array<int, 3> key = {X, Y, Z};
auto i = wld->chunks.find(key);
return (i != wld->chunks.end()) ? i->second : 0;
}
int addChunk(int X, int Y, int Z)
{
auto wld = Global->Get<StorageTerrain>("terrain");
auto shd = Global->Get<StorageShader>("shader"); // moved this line
unsigned int id = getChunk(X, Y, Z);
if(!id)
{
id = Entity->New();
Entity->Add<StorageChunk>(id);
auto frm = Entity->Add<StorageForm>(id); // moved this line
auto tsf = Entity->Add<StorageTransform>(id);
frm->Program = shd->Program; // moved this line
tsf->Position = vec3(X * CHUNK_X, Y * CHUNK_Y, Z * CHUNK_Z);
Generate(id, X, Y, Z);
array<int, 3> key = {X, Y, Z};
wld->chunks.insert(make_pair(key, id));
}
return id;
}
void deleteChunk(int X, int Y, int Z)
{
auto wld = Global->Get<StorageTerrain>("terrain");
unsigned int id = getChunk(X, Y, Z);
if(id < 1) return;
array<int, 3> key = {X, Y, Z};
wld->chunks.erase(key);
Entity->Delete<StorageChunk>(id);
Entity->Delete<StorageForm>(id);
Entity->Delete<StorageTransform>(id);
// free buffers
}
void Generate(unsigned int id, int X, int Y, int Z)
{
auto cnk = Entity->Get<StorageChunk>(id);
cnk->changed = true;
for(int x = 0; x < CHUNK_X; ++x) {
const float i = X + (float)x / CHUNK_X;
for(int z = 0; z < CHUNK_Z; ++z) {
const float j = Z + (float)z / CHUNK_Z;
double height_bias = 0.30;
double height_base = 0.50 * (simplex(0.2f * vec2(i, j)) + 1) / 2;
double height_fine = 0.20 * (simplex(1.5f * vec2(i, j)) + 1) / 2;
int height = (int)((height_bias + height_base + height_fine) * CHUNK_Y);
for(int y = 0; y < height; ++y) cnk->blocks[x][y][z] = true;
} }
}
#define TILES_U 4
#define TILES_V 4
Data Mesh(Data data)
{
auto cnk = Entity->Get<StorageChunk>(data.id);
auto *Vertices = &data.Vertices, *Normals = &data.Normals, *Texcoords = &data.Texcoords;
auto *Elements = &data.Elements;
const vec2 grid(1.f / TILES_U, 1.f / TILES_V);
int n = 0;
for(int X = 0; X < CHUNK_X; ++X)
for(int Y = 0; Y < CHUNK_Y; ++Y)
for(int Z = 0; Z < CHUNK_Z; ++Z)
if(cnk->blocks[X][Y][Z])
{
int Tile = Clamp(rand() % 2 + 1, 0, TILES_U * TILES_V - 1);
for(int dim = 0; dim < 3; ++dim) { int dir = -1; do {
vec3i neigh = Shift(dim, vec3i(dir, 0, 0)) + vec3i(X, Y, Z);
if(Inside(neigh, vec3i(0), vec3i(CHUNK_X, CHUNK_Y, CHUNK_Z) - 1))
if(cnk->blocks[neigh.x][neigh.y][neigh.z])
{ dir *= -1; continue; }
for(float i = 0; i <= 1; ++i)
for(float j = 0; j <= 1; ++j)
{
vec3 vertex = vec3(X, Y, Z) + floatify(Shift(dim, vec3i((dir+1)/2, i, j)));
Vertices->push_back(vertex.x); Vertices->push_back(vertex.y); Vertices->push_back(vertex.z);
}
vec3 normal = normalize(floatify(Shift(dim, vec3i(dir, 0, 0))));
for(int i = 0; i < 4; ++i)
{
Normals->push_back(normal.x); Normals->push_back(normal.y); Normals->push_back(normal.z);
}
vec2 position = (vec2(Tile % TILES_U, Tile / TILES_U) + .25f) * grid;
Texcoords->push_back(position.x); Texcoords->push_back(position.y);
Texcoords->push_back(position.x + grid.x/2); Texcoords->push_back(position.y);
Texcoords->push_back(position.x); Texcoords->push_back(position.y + grid.y/2);
Texcoords->push_back(position.x + grid.x/2); Texcoords->push_back(position.y + grid.y/2);
if(dir == -1) {
Elements->push_back(n+0); Elements->push_back(n+1); Elements->push_back(n+2);
Elements->push_back(n+1); Elements->push_back(n+3); Elements->push_back(n+2);
} else {
Elements->push_back(n+0); Elements->push_back(n+2); Elements->push_back(n+1);
Elements->push_back(n+1); Elements->push_back(n+2); Elements->push_back(n+3);
}
n += 4;
dir *= -1; } while(dir > 0); }
}
return data;
}
void Buffers(Data data)
{
auto frm = Entity->Get<StorageForm>(data.id);
glGenBuffers(1, &frm->Positions);
glBindBuffer(GL_ARRAY_BUFFER, frm->Positions);
glBufferData(GL_ARRAY_BUFFER, data.Vertices.size() * sizeof(float), &(data.Vertices[0]), GL_STATIC_DRAW);
glGenBuffers(1, &frm->Normals);
glBindBuffer(GL_ARRAY_BUFFER, frm->Normals);
glBufferData(GL_ARRAY_BUFFER, data.Normals.size() * sizeof(float), &(data.Normals[0]), GL_STATIC_DRAW);
glGenBuffers(1, &frm->Texcoords);
glBindBuffer(GL_ARRAY_BUFFER, frm->Texcoords);
glBufferData(GL_ARRAY_BUFFER, data.Texcoords.size() * sizeof(float), &(data.Texcoords[0]), GL_STATIC_DRAW);
glGenBuffers(1, &frm->Elements);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, frm->Elements);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, data.Elements.size() * sizeof(int), &data.Elements[0], GL_STATIC_DRAW);
glGenTextures(1, &frm->Texture);
glBindTexture(GL_TEXTURE_2D, frm->Texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.getSize().x, texture.getSize().y, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.getPixelsPtr());
glGenerateMipmap(GL_TEXTURE_2D);
}
void Texture()
{
Image image;
bool result = image.loadFromFile("forms/textures/terrain.png");
if(!result){ Debug::Fail("Terrain texture loading fail"); return; }
Vector2u size = Vector2u(image.getSize().x / TILES_U, image.getSize().y / TILES_V);
texture.create(image.getSize().x * 2, image.getSize().y * 2, Color());
for(int u = 0; u < TILES_U; ++u)
for(int v = 0; v < TILES_V; ++v)
{
Image tile, quarter;
tile.create(size.x, size.y, Color());
tile.copy(image, 0, 0, IntRect(size.x * u, size.y * v, size.x, size.y), true);
quarter.create(size.x, size.y, Color());
quarter.copy(tile, 0, 0, IntRect(size.x / 2, size.y / 2, size.x / 2, size.y / 2), true);
quarter.copy(tile, size.x / 2, 0, IntRect(0, size.y / 2, size.x / 2, size.y / 2), true);
quarter.copy(tile, 0, size.y / 2, IntRect(size.x / 2, 0, size.x / 2, size.y / 2), true);
quarter.copy(tile, size.x / 2, size.y / 2, IntRect(0, 0, size.x / 2, size.y / 2), true);
texture.copy(quarter, (u * 2 ) * size.x, (v * 2 ) * size.y, IntRect(0, 0, 0, 0), true);
texture.copy(quarter, (u * 2 + 1) * size.x, (v * 2 ) * size.y, IntRect(0, 0, 0, 0), true);
texture.copy(quarter, (u * 2 ) * size.x, (v * 2 + 1) * size.y, IntRect(0, 0, 0, 0), true);
texture.copy(quarter, (u * 2 + 1) * size.x, (v * 2 + 1) * size.y, IntRect(0, 0, 0, 0), true);
}
}
template <typename T>
inline T Clamp(T Value, T Min, T Max)
{
if(Value < Min) return Min;
if(Value > Max) return Max;
return Value;
}
bool Inside(vec3i Position, vec3i Min, vec3i Max)
{
if(Position.x < Min.x || Position.y < Min.y || Position.z < Min.z) return false;
if(Position.x > Max.x || Position.y > Max.y || Position.z > Max.z) return false;
return true;
}
inline vec3i Shift(int Dimension, vec3i Vector)
{
if (Dimension % 3 == 1) return vec3i(Vector.z, Vector.x, Vector.y);
else if (Dimension % 3 == 2) return vec3i(Vector.y, Vector.z, Vector.x);
else return Vector;
}
vec3 floatify(vec3i Value)
{
return vec3(Value.x, Value.y, Value.z);
}
};
However, you can find the whole code on Github.
Do you have any idea what could cause the crash or how to find the bug? Please let me know if you need more information and of which kind.
Thanks to #doomster I could find and fix the bug now.
The renderer component loops over a vector of forms to draw them. The asynchronous thread added new forms to that vector but their buffers were created later in the main thread after the generated vertices were returned. That means that drawing and form adding ran in parallel. The crash occurred when the renderer tried to render a form without any created buffers yet.
I inserted comments the code above to highlight the three lines I moved from addChunk() to Buffers() for fixing the issue. I sill do not know why only the release build executable crashed but that doesn't really matter any more.
There are two things I would verify here:
You are using a future<T>. I'm not sure, but if it runs in a different thread, that could have a negative impact on OpenGL which I have heard can behave sensitive to multithreaded use. Treat this as a rumour though, I'm not really sure about it, but trying to convert to single-threaded code is worth an attempt.
You can activate debug symbols in a release build, too. This should at least give you a usable backtrace when the crash happens. Actually, VS's Debug/Release settings are just default settings but without intrinsic meaning, so you can modify the Debug settings in steps until it matches the Release settings. That should give you a variant that fails while still being usable in a debugger.

Using glColorPointer with glDrawElements results in nothing being drawn

I'm working on just making uniformly colors spheres for a project and I'm running into an issue. The spheres run fine but when I try to color them with glColorPointer they stop appearing. OpenGL isn't showing any errors when I call glGetError so I'm at a loss for why this would happen.
The code to generate the vertices, colors etc:
void SphereObject::setupVertices()
{
//determine the array sizes
//vertices per row (+1 for the repeated one at the end) * three for each coordinate
//times the number of rows
int arraySize = myNumVertices * 3;
myNumIndices = (myVerticesPerRow + 1) * myRows * 2;
myVertices = new GLdouble[arraySize];
myIndices = new GLuint[myNumIndices];
myNormals = new GLdouble[arraySize];
myColors = new GLint[myNumVertices * 4];
//use spherical coordinates to calculate the vertices
double phiIncrement = 360 / myVerticesPerRow;
double thetaIncrement = 180 / (double)myRows;
int arrayIndex = 0;
int colorArrayIndex = 0;
int indicesIndex = 0;
double x, y, z = 0;
for(double theta = 0; theta <= 180; theta += thetaIncrement)
{
//loop including the repeat for the last vertex
for(double phi = 0; phi <= 360; phi += phiIncrement)
{
//make sure that the last vertex is repeated
if(360 - phi < phiIncrement)
{
x = myRadius * sin(radians(theta)) * cos(radians(0));
y = myRadius * sin(radians(theta)) * sin(radians(0));
z = myRadius * cos(radians(theta));
}
else
{
x = myRadius * sin(radians(theta)) * cos(radians(phi));
y = myRadius * sin(radians(theta)) * sin(radians(phi));
z = myRadius * cos(radians(theta));
}
myColors[colorArrayIndex] = myColor.getX();
myColors[colorArrayIndex + 1] = myColor.getY();
myColors[colorArrayIndex + 2] = myColor.getZ();
myColors[colorArrayIndex + 3] = 1;
myVertices[arrayIndex] = x;
myVertices[arrayIndex + 1] = y;
myVertices[arrayIndex + 2] = z;
if(theta <= 180 - thetaIncrement)
{
myIndices[indicesIndex] = arrayIndex / 3;
myIndices[indicesIndex + 1] = (arrayIndex / 3) + myVerticesPerRow + 1;
indicesIndex += 2;
}
arrayIndex += 3;
colorArrayIndex += 4;
}
}
}
And the code to actually render the thing
void SphereObject::render()
{
glPushMatrix();
glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_INT, 0, myColors);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_DOUBLE, 0, myVertices);
glDrawElements(GL_QUAD_STRIP, myNumIndices, GL_UNSIGNED_INT, myIndices);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glPopClientAttrib();
glPopMatrix();
}
Any and all help would be appreciated. I'm really having a hard time for some reason.
When you use GL_INT (or any integer type) for color pointer, it linearly maps the largest possible integer value to 1.0f (maximum color), and 0 to 0.0f (minimum color).
Therefore unless your values of RGB and A are in the billions, they will likely appear completely black (or transparent if that's enabled). I see that you've got alpha = 1, which will essentially be zero after conversion to float.

A method for indexing triangles from a loaded heightmap?

I am currently making a method to load in a noisy heightmap, but lack the triangles to do so. I want to make an algorithm that will take an image, its width and height and construct a terrain node out of it.
Here's what I have so far, in somewhat pseudo
Vertex* vertices = new Vertices[image.width * image.height];
Index* indices; // How do I judge how many indices I will have?
float scaleX = 1 / image.width;
float scaleY = 1 / image.height;
float currentYScale = 0;
for(int y = 0; y < image.height; ++y) {
float currentXScale = 0;
for (int x = 0; x < image.width; ++x) {
Vertex* v = vertices[x * y];
v.x = currentXScale;
v.y = currentYScale;
v.z = image[x,y];
currentXScale += scaleX;
}
currentYScale += scaleY;
}
This works well enough to my needs, my only problem is this: How would I calculate the # of indices and their positions for drawing the triangles? I have somewhat familiarity with indices, but not how to programmatically calculate them, I can only do that statically.
As far as your code above goes, using vertices[x * y] isn't right - if you use that, then e.g. vert(2,3) == vert(3,2). What you want is something like vertices[y * image.width + x], but you can do it more efficiently by incrementing a counter (see below).
Here's the equivalent code I use. It's in C# unfortunately, but hopefully it should illustrate the point:
/// <summary>
/// Constructs the vertex and index buffers for the terrain (for use when rendering the terrain).
/// </summary>
private void ConstructBuffers()
{
int heightmapHeight = Heightmap.GetLength(0);
int heightmapWidth = Heightmap.GetLength(1);
int gridHeight = heightmapHeight - 1;
int gridWidth = heightmapWidth - 1;
// Construct the individual vertices for the terrain.
var vertices = new VertexPositionTexture[heightmapHeight * heightmapWidth];
int vertIndex = 0;
for(int y = 0; y < heightmapHeight; ++y)
{
for(int x = 0; x < heightmapWidth; ++x)
{
var position = new Vector3(x, y, Heightmap[y,x]);
var texCoords = new Vector2(x * 2f / heightmapWidth, y * 2f / heightmapHeight);
vertices[vertIndex++] = new VertexPositionTexture(position, texCoords);
}
}
// Create the vertex buffer and fill it with the constructed vertices.
this.VertexBuffer = new VertexBuffer(Renderer.GraphicsDevice, typeof(VertexPositionTexture), vertices.Length, BufferUsage.WriteOnly);
this.VertexBuffer.SetData(vertices);
// Construct the index array.
var indices = new short[gridHeight * gridWidth * 6]; // 2 triangles per grid square x 3 vertices per triangle
int indicesIndex = 0;
for(int y = 0; y < gridHeight; ++y)
{
for(int x = 0; x < gridWidth; ++x)
{
int start = y * heightmapWidth + x;
indices[indicesIndex++] = (short)start;
indices[indicesIndex++] = (short)(start + 1);
indices[indicesIndex++] = (short)(start + heightmapWidth);
indices[indicesIndex++] = (short)(start + 1);
indices[indicesIndex++] = (short)(start + 1 + heightmapWidth);
indices[indicesIndex++] = (short)(start + heightmapWidth);
}
}
// Create the index buffer.
this.IndexBuffer = new IndexBuffer(Renderer.GraphicsDevice, typeof(short), indices.Length, BufferUsage.WriteOnly);
this.IndexBuffer.SetData(indices);
}
I guess the key point is that given a heightmap of size heightmapHeight * heightmapWidth, you need (heightmapHeight - 1) * (heightmapWidth - 1) * 6 indices, since you're drawing:
2 triangles per grid square
3 vertices per triangle
(heightmapHeight - 1) * (heightmapWidth - 1) grid squares in your terrain.

Run-Time Check Failure #2 - Stack around the variable 'indices' was corrupted

well I think I know what the problem is. I am just having a hard time debugging it. I am working with the directx api and I am trying to generate a plane along the x and z axis according to a book I have. The problem is when I am creating my indices. I think I am setting values out of the bounds of the indices array. I am just having a hard time figuring out what I did wrong. I am unfamiliar with the this method of generating a plane. so its a little difficult for me. below is my code. Take emphasis on the indices loop.
[edit]
Ive been reviewing it. This is how the indices works
int curVertex = x + (z * NUM_VERTSX);
This always gets the beginning vertices. so say we have 17 vertices on the x axis and 17 vertices on the z axis and we are on the first loop of the x and z axis
curVertx = 0 + (0 * 17)
curVertx = 0 + 0 = 0
say we are on the first loop of the z axis and second loop of the x axis
curVertx = 1 + (0 * 17)
curVertx = 1+ 0 = 1
indices[curIndex] = curVertex;
indices[curIndex + 1] = curVertex + NUM_VERTSX;
indices[curIndex + 2] = curVertex + 1;
indices[curIndex + 3] = curVertex + 1;
indices[curIndex + 4] = curVertex + NUM_VERTSX;
indices[curIndex + 5] = curVertex + NUM_VERTSX + 1;
if we are on the first
loop indices[curIndex] = curVertex;
this equals the first vertex = 0.
indices[curIndex + 1] = curVertex + NUM_VERTSX;
this equals the second row vertices (its always the vertices below the starting vertices
x x x x
[x] x x x
#include "MyGame.h"
//#include "CubeVector.h"
/* This code sets a projection and shows a turning cube. What has been added is the project, rotation and
a rasterizer to change the rasterization of the cube. The issue that was going on was something with the effect file
which was causing the vertices not to be rendered correctly.*/
typedef struct
{
ID3D10Effect* pEffect;
ID3D10EffectTechnique* pTechnique;
//vertex information
ID3D10Buffer* pVertexBuffer;
ID3D10Buffer* pIndicesBuffer;
ID3D10InputLayout* pVertexLayout;
UINT numVertices;
UINT numIndices;
}ModelObject;
ModelObject modelObject;
// World Matrix
D3DXMATRIX WorldMatrix;
// View Matrix
D3DXMATRIX ViewMatrix;
// Projection Matrix
D3DXMATRIX ProjectionMatrix;
ID3D10EffectMatrixVariable* pProjectionMatrixVariable = NULL;
//grid information
#define NUM_COLS 16
#define NUM_ROWS 16
#define CELL_WIDTH 32
#define CELL_HEIGHT 32
#define NUM_VERTSX (NUM_COLS + 1)
#define NUM_VERTSY (NUM_ROWS + 1)
bool MyGame::InitDirect3D()
{
if(!DX3dApp::InitDirect3D())
{
return false;
}
D3D10_RASTERIZER_DESC rastDesc;
rastDesc.FillMode = D3D10_FILL_WIREFRAME;
rastDesc.CullMode = D3D10_CULL_FRONT;
rastDesc.FrontCounterClockwise = true;
rastDesc.DepthBias = false;
rastDesc.DepthBiasClamp = 0;
rastDesc.SlopeScaledDepthBias = 0;
rastDesc.DepthClipEnable = false;
rastDesc.ScissorEnable = false;
rastDesc.MultisampleEnable = false;
rastDesc.AntialiasedLineEnable = false;
ID3D10RasterizerState *g_pRasterizerState;
mpD3DDevice->CreateRasterizerState(&rastDesc, &g_pRasterizerState);
mpD3DDevice->RSSetState(g_pRasterizerState);
// Set up the World Matrix
D3DXMatrixIdentity(&WorldMatrix);
D3DXMatrixLookAtLH(&ViewMatrix, new D3DXVECTOR3(0.0f, 10.0f, -20.0f), new D3DXVECTOR3(0.0f, 0.0f, 0.0f), new D3DXVECTOR3(0.0f, 1.0f, 0.0f));
// Set up the projection matrix
D3DXMatrixPerspectiveFovLH(&ProjectionMatrix, (float)D3DX_PI * 0.5f, (float)mWidth/(float)mHeight, 0.1f, 100.0f);
if(!CreateObject())
{
return false;
}
return true;
}
//These are actions that take place after the clearing of the buffer and before the present
void MyGame::GameDraw()
{
static float rotationAngle = 0.0f;
// create the rotation matrix using the rotation angle
D3DXMatrixRotationY(&WorldMatrix, rotationAngle);
rotationAngle += (float)D3DX_PI * 0.0f;
// Set the input layout
mpD3DDevice->IASetInputLayout(modelObject.pVertexLayout);
// Set vertex buffer
UINT stride = sizeof(VertexPos);
UINT offset = 0;
mpD3DDevice->IASetVertexBuffers(0, 1, &modelObject.pVertexBuffer, &stride, &offset);
mpD3DDevice->IASetIndexBuffer(modelObject.pIndicesBuffer, DXGI_FORMAT_R32_UINT, 0);
// Set primitive topology
mpD3DDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// Combine and send the final matrix to the shader
D3DXMATRIX finalMatrix = (WorldMatrix * ViewMatrix * ProjectionMatrix);
pProjectionMatrixVariable->SetMatrix((float*)&finalMatrix);
// make sure modelObject is valid
// Render a model object
D3D10_TECHNIQUE_DESC techniqueDescription;
modelObject.pTechnique->GetDesc(&techniqueDescription);
// Loop through the technique passes
for(UINT p=0; p < techniqueDescription.Passes; ++p)
{
modelObject.pTechnique->GetPassByIndex(p)->Apply(0);
// draw the cube using all 36 vertices and 12 triangles
mpD3DDevice->DrawIndexed(modelObject.numIndices,0,0);
}
}
//Render actually incapsulates Gamedraw, so you can call data before you actually clear the buffer or after you
//present data
void MyGame::Render()
{
DX3dApp::Render();
}
bool MyGame::CreateObject()
{
VertexPos vertices[NUM_VERTSX * NUM_VERTSY];
for(int z=0; z < NUM_VERTSY; ++z)
{
for(int x = 0; x < NUM_VERTSX; ++x)
{
vertices[x + z * NUM_VERTSX].pos.x = (float)x * CELL_WIDTH;
vertices[x + z * NUM_VERTSX].pos.z = (float)z * CELL_HEIGHT;
vertices[x + z * NUM_VERTSX].pos.y = 0.0f;
vertices[x + z * NUM_VERTSX].color = D3DXVECTOR4(1.0, 0.0f, 0.0f, 0.0f);
}
}
DWORD indices[NUM_VERTSX * NUM_VERTSY];
int curIndex = 0;
for(int z=0; z < NUM_ROWS; ++z)
{
for(int x = 0; x < NUM_COLS; ++x)
{
int curVertex = x + (z * NUM_VERTSX);
indices[curIndex] = curVertex;
indices[curIndex + 1] = curVertex + NUM_VERTSX;
indices[curIndex + 2] = curVertex + 1;
indices[curIndex + 3] = curVertex + 1;
indices[curIndex + 4] = curVertex + NUM_VERTSX;
indices[curIndex + 5] = curVertex + NUM_VERTSX + 1;
curIndex += 6;
}
}
//Create Layout
D3D10_INPUT_ELEMENT_DESC layout[] = {
{"POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT, 0 , 0, D3D10_INPUT_PER_VERTEX_DATA, 0},
{"COLOR",0,DXGI_FORMAT_R32G32B32A32_FLOAT, 0 , 12, D3D10_INPUT_PER_VERTEX_DATA, 0}
};
UINT numElements = (sizeof(layout)/sizeof(layout[0]));
modelObject.numVertices = sizeof(vertices)/sizeof(VertexPos);
//Create buffer desc
D3D10_BUFFER_DESC bufferDesc;
bufferDesc.Usage = D3D10_USAGE_DEFAULT;
bufferDesc.ByteWidth = sizeof(VertexPos) * modelObject.numVertices;
bufferDesc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
bufferDesc.CPUAccessFlags = 0;
bufferDesc.MiscFlags = 0;
D3D10_SUBRESOURCE_DATA initData;
initData.pSysMem = vertices;
//Create the buffer
HRESULT hr = mpD3DDevice->CreateBuffer(&bufferDesc, &initData, &modelObject.pVertexBuffer);
if(FAILED(hr))
return false;
modelObject.numIndices = sizeof(indices)/sizeof(DWORD);
bufferDesc.ByteWidth = sizeof(DWORD) * modelObject.numIndices;
bufferDesc.BindFlags = D3D10_BIND_INDEX_BUFFER;
initData.pSysMem = indices;
hr = mpD3DDevice->CreateBuffer(&bufferDesc, &initData, &modelObject.pIndicesBuffer);
if(FAILED(hr))
return false;
/////////////////////////////////////////////////////////////////////////////
//Set up fx files
LPCWSTR effectFilename = L"effect.fx";
modelObject.pEffect = NULL;
hr = D3DX10CreateEffectFromFile(effectFilename,
NULL,
NULL,
"fx_4_0",
D3D10_SHADER_ENABLE_STRICTNESS,
0,
mpD3DDevice,
NULL,
NULL,
&modelObject.pEffect,
NULL,
NULL);
if(FAILED(hr))
return false;
pProjectionMatrixVariable = modelObject.pEffect->GetVariableByName("Projection")->AsMatrix();
//Dont sweat the technique. Get it!
LPCSTR effectTechniqueName = "Render";
modelObject.pTechnique = modelObject.pEffect->GetTechniqueByName(effectTechniqueName);
if(modelObject.pTechnique == NULL)
return false;
//Create Vertex layout
D3D10_PASS_DESC passDesc;
modelObject.pTechnique->GetPassByIndex(0)->GetDesc(&passDesc);
hr = mpD3DDevice->CreateInputLayout(layout, numElements,
passDesc.pIAInputSignature,
passDesc.IAInputSignatureSize,
&modelObject.pVertexLayout);
if(FAILED(hr))
return false;
return true;
}
Your indices array contains 6 entries per 'cell' (since you're drawing two triangles for each), therefore it should be declared as
DWORD indices[NUM_ROWS * NUM_COLS * 6]
The error you get tells you, that you write outside the boundaries of indices, this is usually either a hint towards a wrong declaration (or a wrong index calculation).
Now let us take the code snippet in question (probable root cause)
Code
DWORD indices[NUM_VERTSX * NUM_VERTSY];
int curIndex = 0;
for(int z=0; z < NUM_ROWS; ++z)
{
for(int x = 0; x < NUM_COLS; ++x)
{
int curVertex = x + (z * NUM_VERTSX);
indices[curIndex] = curVertex;
indices[curIndex + 1] = curVertex + NUM_VERTSX;
indices[curIndex + 2] = curVertex + 1;
indices[curIndex + 3] = curVertex + 1;
indices[curIndex + 4] = curVertex + NUM_VERTSX;
indices[curIndex + 5] = curVertex + NUM_VERTSX + 1;
curIndex += 6;
}
}
Analysis
Here indices have max number of 'cells' = NUM_VERTX * NUM_VERTSY = (16 + 1) * (16+1) = 289. So there are 0...288 'cells'. During the boundary condition - there value of z = 15, x = 15. So curIndex would be 15 * 15 * 6 = 1350. This far exceeds allocated cells.
Suggestion
Since three values determine the size of the target array, all three must be part of the allocation of the array. so if you use DWORD indices[NUM_VERTSX * NUM_VERTSY * UNIT_BLOCK], where UNIT_BLOCK = 6, it should work fine.
Also instead of embedding magic number inside the code, you can use a const variable - it would help a great deal later (if you want to change the value of the index).
HTH