I am writing a Quad tree structure for a planet, that decreases and in increases in detail when you are far away from the quad and close to it receptively. However, I am running into some really serious, and annoying bugs.
I have two preprocessor defined constant that determines the size of the Quad tree (QUAD_WIDTH and QUAD_HEIGHT) when I change the value to anything but 32 (16 or 64 for example) I get a blue screen of death. I am using code::blocks as my IDE, another thing: Whenever I try to debug the program in code::blocks I also get a blue screen of death (Doesn't matter if the constants are 32 or not)
Why is this the case? And how can I fix it.
PQuad.cpp
#include "..\include\PQuad.h"
#include "..\include\Color3.h"
#include <iostream>
#include <vector>
#include <cmath>
#include <GL/glew.h>
#include <GL/glu.h>
#include <GL/gl.h>
#define QUAD_WIDTH 32
#define QUAD_HEIGHT 32
#define NUM_OF_CHILDREN 4
#define MAX_DEPTH 4
PQuad::PQuad(FaceDirection face_direction, float planet_radius) {
this->built = false;
this->spherised = false;
this->face_direction = face_direction;
this->radius = planet_radius;
this->planet_centre = glm::vec3(0, 0, 0);
}
PQuad::~PQuad() {
}
std::vector<PQuad> PQuad::get_children() {
return children;
}
bool PQuad::get_built() {
return this->built;
}
int PQuad::get_depth() {
return this->depth;
}
float *PQuad::get_table() {
return tree;
}
float PQuad::get_element_width() {
return element_width;
}
glm::vec3 PQuad::get_position() {
return position;
}
glm::vec3 PQuad::get_centre() {
return centre;
}
void PQuad::get_recursive(glm::vec3 player_pos, std::vector<PQuad*>& out_children) {
for (size_t i = 0; i < children.size(); i++) {
children[i].get_recursive(player_pos, out_children);
}
if (this->should_draw(player_pos) ||
this->depth == 0) {
out_children.emplace_back(this);
}
}
GLuint PQuad::get_vertexbuffer() {
return vbo_vertices;
}
GLuint PQuad::get_colorbuffer() {
return vbo_colors;
}
GLuint PQuad::get_normalbuffer() {
return vbo_normals;
}
GLuint PQuad::get_elementbuffer() {
return ibo_elements;
}
void PQuad::set_parent(PQuad *quad) {
this->parent = quad;
}
void PQuad::set_child_index(int child_index) {
this->child_index = child_index;
}
void PQuad::set_depth(int depth) {
this->depth = depth;
}
void PQuad::set_root(bool root) {
this->root = root;
}
void PQuad::calculate_position() {
this->element_width = depth == 0 ? 1.0f : parent->get_element_width() / 2.0f;
float quad_y = child_index / 2 == 0 ? 0 : element_width * QUAD_HEIGHT - element_width;
float quad_x = child_index % 2 == 0 ? 0 : element_width * QUAD_WIDTH - element_width;
if (this->depth != 0) {
quad_x += parent->get_position().x;
quad_y += parent->get_position().y;
}
this->position = glm::vec3(quad_x, quad_y, 0);
}
void PQuad::construct() {
if (!this->built) {
std::vector<glm::vec3> vertices;
std::vector<glm::vec3> normals;
std::vector<Color3> colors;
std::vector<GLushort> elements;
construct_vertices(&vertices, &colors);
construct_elements(&elements);
spherise(&vertices, &normals);
construct_normals(&vertices, &elements, &normals);
construct_buffers(&vertices, &colors, &elements, &normals);
float distance = radius;
if (!spherised) {
distance = QUAD_WIDTH;
}
construct_depth_table(distance);
this->built = true;
}
}
void PQuad::construct_depth_table(float distance) {
tree[0] = -1;
for (int i = 1; i < MAX_DEPTH; i++) {
tree[i] = distance;
distance /= 2.0f;
}
}
void PQuad::construct_children() {
calculate_position();
if (depth < (int)MAX_DEPTH) {
children.reserve((int)NUM_OF_CHILDREN);
for (int i = 0; i < (int)NUM_OF_CHILDREN; i++) {
children.emplace_back(PQuad(this->face_direction, this->radius));
PQuad *child = &children.back();
child->set_depth(depth + 1);
child->set_child_index(i);
child->set_parent(this);
child->construct_children();
}
} else {
leaf = true;
}
}
void PQuad::construct_vertices(std::vector<glm::vec3> *vertices, std::vector<Color3> *colors) {
vertices->reserve(QUAD_WIDTH * QUAD_HEIGHT);
for (int y = 0; y < QUAD_HEIGHT; y++) {
for (int x = 0; x < QUAD_WIDTH; x++) {
switch (face_direction) {
case YIncreasing:
vertices->emplace_back(glm::vec3(position.x + x * element_width, QUAD_HEIGHT - 1, -(position.y + y * element_width)));
break;
case YDecreasing:
vertices->emplace_back(glm::vec3(position.x + x * element_width, 0, -(position.y + y * element_width)));
break;
case XIncreasing:
vertices->emplace_back(glm::vec3(QUAD_WIDTH - 1, position.y + y * element_width, -(position.x + x * element_width)));
break;
case XDecreasing:
vertices->emplace_back(glm::vec3(0, position.y + y * element_width, -(position.x + x * element_width)));
break;
case ZIncreasing:
vertices->emplace_back(glm::vec3(position.x + x * element_width, position.y + y * element_width, 0));
break;
case ZDecreasing:
vertices->emplace_back(glm::vec3(position.x + x * element_width, position.y + y * element_width, -(QUAD_WIDTH - 1)));
break;
}
// Position the bottom, right, front vertex of the cube from being (0,0,0) to (-16, -16, 16)
(*vertices)[vertices->size() - 1] -= glm::vec3(QUAD_WIDTH / 2.0f, QUAD_WIDTH / 2.0f, -(QUAD_WIDTH / 2.0f));
colors->emplace_back(Color3(255.0f, 255.0f, 255.0f, false));
}
}
switch (face_direction) {
case YIncreasing:
this->centre = glm::vec3(position.x + QUAD_WIDTH / 2.0f, QUAD_HEIGHT - 1, -(position.y + QUAD_HEIGHT / 2.0f));
break;
case YDecreasing:
this->centre = glm::vec3(position.x + QUAD_WIDTH / 2.0f, 0, -(position.y + QUAD_HEIGHT / 2));
break;
case XIncreasing:
this->centre = glm::vec3(QUAD_WIDTH - 1, position.y + QUAD_HEIGHT / 2.0f, -(position.x + QUAD_WIDTH / 2.0f));
break;
case XDecreasing:
this->centre = glm::vec3(0, position.y + QUAD_HEIGHT / 2.0f, -(position.x + QUAD_WIDTH / 2.0f));
break;
case ZIncreasing:
this->centre = glm::vec3(position.x + QUAD_WIDTH / 2.0f, position.y + QUAD_HEIGHT / 2.0f, 0);
break;
case ZDecreasing:
this->centre = glm::vec3(position.x + QUAD_WIDTH / 2.0f, position.y + QUAD_HEIGHT / 2.0f, -(QUAD_HEIGHT - 1));
break;
}
this->centre -= glm::vec3(QUAD_WIDTH / 2.0f, QUAD_WIDTH / 2.0f, -(QUAD_WIDTH / 2.0f));
}
void PQuad::construct_elements(std::vector<GLushort> *elements) {
int index = 0;
elements->reserve((QUAD_WIDTH - 1) * (QUAD_HEIGHT - 1) * 6);
for (int y = 0; y < QUAD_HEIGHT - 1; y++) {
for (int x = 0; x < QUAD_WIDTH - 1; x++) {
GLushort bottom_left = x + y * QUAD_WIDTH;
GLushort bottom_right = (x + 1) + y * QUAD_WIDTH;
GLushort top_left = x + (y + 1) * QUAD_WIDTH;
GLushort top_right = (x + 1) + (y + 1) * QUAD_WIDTH;
elements->emplace_back(top_left);
elements->emplace_back(bottom_right);
elements->emplace_back(bottom_left);
elements->emplace_back(top_left);
elements->emplace_back(top_right);
elements->emplace_back(bottom_right);
}
}
}
void PQuad::construct_normals(std::vector<glm::vec3> *vertices, std::vector<GLushort> *elements, std::vector<glm::vec3> *normals) {
normals->reserve(QUAD_WIDTH * QUAD_HEIGHT);
for (int i = 0; i < elements->size() / 3; i++) {
int index1 = elements->at(i * 3);
int index2 = elements->at(i * 3 + 1);
int index3 = elements->at(i * 3 + 2);
glm::vec3 side1 = vertices->at(index1) - vertices->at(index3);
glm::vec3 side2 = vertices->at(index1) - vertices->at(index2);
glm::vec3 normal = glm::cross(side1, side2);
normal = glm::normalize(normal);
normals->emplace_back(normal);
normals->emplace_back(normal);
normals->emplace_back(normal);
}
}
void PQuad::spherise(std::vector<glm::vec3> *vertices, std::vector<glm::vec3> *normals) {
for (int i = 0; i < QUAD_WIDTH * QUAD_HEIGHT; i++) {
glm::vec3 normal = glm::normalize(vertices->at(i) - planet_centre);
(*vertices)[i] = (float)(radius) * normal;
}
glm::vec3 normal = glm::normalize(centre - planet_centre);
centre = normal * (float)(radius);
this->spherised = true;
}
void PQuad::construct_buffers(std::vector<glm::vec3> *vertices, std::vector<Color3> *colors, std::vector<GLushort> *elements, std::vector<glm::vec3> *normals) {
glGenBuffers(1, &vbo_vertices);
glBindBuffer(GL_ARRAY_BUFFER, vbo_vertices);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * vertices->size(), &((*vertices)[0]), GL_STATIC_DRAW);
glGenBuffers(1, &vbo_colors);
glBindBuffer(GL_ARRAY_BUFFER, vbo_colors);
glBufferData(GL_ARRAY_BUFFER, sizeof(Color3) * colors->size(), &((*colors)[0]), GL_STATIC_DRAW);
glGenBuffers(1, &vbo_normals);
glBindBuffer(GL_ARRAY_BUFFER, vbo_normals);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * normals->size(), &((*normals)[0]), GL_STATIC_DRAW);
glGenBuffers(1, &ibo_elements);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_elements);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * elements->size(), &((*elements)[0]), GL_STATIC_DRAW);
}
float distance3(glm::vec3 v1, glm::vec3 v2) {
return sqrt(pow(abs(v1.x - v2.x), 2) + pow(abs(v1.y - v2.y), 2) + pow(abs(v1.z - v2.z), 2));
}
bool PQuad::should_draw(glm::vec3 player_position) {
float distance = distance3(player_position, centre);
if (distance < tree[depth]) {
return true;
}
return false;
}
A blue screen of death should be just impossible to reach from a regular user space program... no matter what you do.
However unfortunately it's easy to bump into this kind of system level bug when writing software that interacts heavily with device drivers because they are software too, and they are not bug free (and a bug in a device driver can take down the whole system with a BSOD).
The meaning is that you are making some call to OpenGL with wrong parameters, and that the driver of your video card has a bug and instead of detecting the problem and returning a failure code, it just takes down the machine.
You may try to use a log of the operations, writing to a file each single step so after you get a BSOD and reboot you can check what was the last command written to the file. Note that you should open the file in append, write the log line and then close the file. Not even this gives you a 100% guarantee the content of the file will have been written really to the disk when you get the BSOD, but IMO in this case the probability should be high. A better alternative would be just sending log messages over the serial line or using the network to another computer.
It may be a difficult problem to track and solve.
Another option would be using a different OpenGL implementation (like Mesa). May be with another implementation calls are checked better and you can spot what is the call with wrong parameters.
It could even be that your code is just triggering a bug in the video driver and your code is not doing anything wrong. This should be your last thought however.
Actually the answer is quite simple. There is something really wrong with the debugger in Code::Blocks on Windows. I have seen it blue screen multiple systems. Switch to using output statements or another IDE.
Related
Is there an algorithm that could be used to generate a plane using the GL_TRIANGLES primitive type?
Here's my current function:
Mesh* Mesh::CreateMeshPlane(vec2 bottomleft, ivec2 numvertices, vec2 worldsize){
int numVerts = numvertices.x * numvertices.y;
float xStep = worldsize.x / (numvertices.x - 1);
float yStep = worldsize.y / (numvertices.y - 1);
VertexFormat* verts = new VertexFormat[numVerts];
for (int y = 0; y < numvertices.y; y++)
{
for (int x = 0; x < numvertices.x; x++)
{
verts[x + (y * numvertices.x)].pos.x = bottomleft.x + (xStep * x);
verts[x + (y * numvertices.x)].pos.y = bottomleft.y + (yStep * y);
verts[x + (y * numvertices.x)].pos.z = 0;
}
}
Mesh* pMesh = new Mesh();
pMesh->Init(verts, numVerts, indices, 6, GL_STATIC_DRAW);
glPointSize(10.0f);
pMesh->m_PrimitiveType = GL_POINTS;
delete[] verts;
return pMesh;}
I'm just unsure how to implement indices into the for loop to be able to know which points to draw.
What I think I need to know:
Each square will be made up of 2 triangles, each square requiring 6 indices
Currently I'm drawing from the bottom left
I need to know how many squares I'll have from the numbers passed in
Maybe something like this:
int width = 4;
int length = 6;
int height = 1;
std::vector<float> planeVertices;
for (int x = 0; x < width - 1; x++) {
for (int z = 0; z < length - 1; z++) {
planeVertices.push_back(x);
planeVertices.push_back(height);
planeVertices.push_back(z);
planeVertices.push_back(x);
planeVertices.push_back(height);
planeVertices.push_back(z + 1);
planeVertices.push_back(x + 1);
planeVertices.push_back(height);
planeVertices.push_back(z + 1);
planeVertices.push_back(x);
planeVertices.push_back(height);
planeVertices.push_back(z);
planeVertices.push_back(x + 1);
planeVertices.push_back(height);
planeVertices.push_back(z);
planeVertices.push_back(x + 1);
planeVertices.push_back(height);
planeVertices.push_back(z + 1);
}
}
...
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, planeVertices.size() * sizeof(float), planeVertices.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
glEnableVertexAttribArray(0);
...
glDrawArrays(GL_TRIANGLES, 0, (width - 1) * (length - 1) * 6);
This code creates an std::vector<float> and adds the plane vertices to it. The nested for loops add two triangles for every unit of the plane (so with width as 4 and length as 6 the plane will be 4 units by 6 units, and will be made of 6 * 4 * 2 = 48 triangles). The height of the plane is set by the height variable. This only generates flat planes, but a simple transformation lets you rotate and scale this as you need.
WARNING: this code is untested.
Just to close this question here's how I did it:
Mesh* Mesh::CreateMeshPlane(vec3 bottomleft, ivec2 numvertices, vec2
worldsize, vec2 texturerepetition)
{
int numVerts = numvertices.x * numvertices.y;
int numFaces = (numvertices.x - 1) * (numvertices.y - 1);
int numIndices = numFaces * 6;
float xStep = worldsize.x / (numvertices.x - 1);
float yStep = worldsize.y / (numvertices.y - 1);
float zStep = worldsize.y / (numvertices.y - 1);
float uStep = texturerepetition.x / (numvertices.x - 1);
float vStep = texturerepetition.y / (numvertices.y - 1);
VertexFormat* verts = new VertexFormat[numVerts];
unsigned int* indices = new unsigned int[numIndices];
for (int y = 0; y < numvertices.y; y++)
{
for (int x = 0; x < numvertices.x; x++)
{
verts[x + (y * numvertices.x)].pos.x = bottomleft.x + (xStep * x);
verts[x + (y * numvertices.x)].pos.y = bottomleft.y;
verts[x + (y * numvertices.x)].pos.z = bottomleft.z + (zStep * y);
verts[y * numvertices.x + x].uv.x = uStep * x;
verts[y * numvertices.x + x].uv.y = vStep * y;
}
}
int offset = 0;
for (int i = 0; i < numIndices; i++)
{
// The bottom left index of the current face
// + the offset to snap back when we hit the edge
unsigned int cornerIndex = i/6 + offset;
// If we reach the edge we increase the offset so that it goes to the next bottom left
if ((cornerIndex + 1)%numvertices.x == 0)
{
offset++;
cornerIndex++; // Adding new offset to the bottom left
}
// First triangle
indices[i] = (unsigned int)cornerIndex;
i++;
indices[i] = (unsigned int)cornerIndex + numvertices.x;
i++;
indices[i] = (unsigned int)cornerIndex + numvertices.x + 1;
i++;
// Second triangle
indices[i] = (unsigned int)cornerIndex;
i++;
indices[i] = (unsigned int)cornerIndex + numvertices.x + 1;
i++;
indices[i] = (unsigned int)cornerIndex + 1;
}
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
Mesh* pMesh = new Mesh();
pMesh->Init(verts, numVerts, indices, numIndices, GL_STATIC_DRAW);
delete[] verts;
return pMesh;
}
Workflow:
1. Calculating number of faces I need, then the number of indices
2. Creating an offset that is added to the cornerIndex when we realize we hit the edge of the vertex array (by using modulus numvertices.y)
3. Doing simple math to draw corners in correct order based on the cornerIndex
Notes:
1. Im drawing using GL_TRIANGLES as the primitive type
2. Drawing from bottom left to top right
3. cornerIndex therefore is the bottom left of the current square we're drawing on
Hope someone can find this helpful!
I found an example online that shows how to draw a cone in OpenGL, which is located here: It was written in C++, and so I translated it to C#. Here is the new code:
public void RenderCone(Vector3 d, Vector3 a, float h, float rd, int n)
{
Vector3 c = new Vector3(a + (-d * h));
Vector3 e0 = Perp(d);
Vector3 e1 = Vector3.Cross(e0, d);
float angInc = (float)(360.0 / n * GrimoireMath.Pi / 180);
// calculate points around directrix
List<Vector3> pts = new List<Vector3>();
for (int i = 0; i < n; ++i)
{
float rad = angInc * i;
Vector3 p = c + (((e0 * (float)Math.Cos((rad)) + (e1 * (float)Math.Sin(rad))) * rd));
pts.Add(p);
}
// draw cone top
GL.Begin(PrimitiveType.TriangleFan);
GL.Vertex3(a);
for (int i = 0; i < n; ++i)
{
GL.Vertex3(pts[i]);
}
GL.End();
// draw cone bottom
GL.Begin(PrimitiveType.TriangleFan);
GL.Vertex3(c);
for (int i = n - 1; i >= 0; --i)
{
GL.Vertex3(pts[i]);
}
GL.End();
}
public Vector3 Perp(Vector3 v)
{
float min = Math.Abs(v.X);
Vector3 cardinalAxis = new Vector3(1, 0, 0);
if (Math.Abs(v.Y) < min)
{
min = Math.Abs(v.Y);
cardinalAxis = new Vector3(0, 1, 0);
}
if (Math.Abs(v.Z) < min)
{
cardinalAxis = new Vector3(0, 0, 1);
}
return Vector3.Cross(v, cardinalAxis);
}
I think I am using the parameters correctly(the page isnt exactly coherent in terms of actual function-usage). Here is the legend that the original creator supplied:
But when I enter in the following as parameters:
RenderCone(new Vector3(0.0f, 1.0f, 0.0f), new Vector3(1.0f, 1.0f, 1.0f), 20.0f, 10.0f, 8);
I receive this(Wireframe enabled):
As you can see, I'm missing a slice, either at the very beginning, or the very end. Does anyone know what's wrong with this method? Or what I could be doing wrong that would cause an incomplete cone?
// draw cone bottom
GL.Begin(PrimitiveType.TriangleFan);
GL.Vertex3(c);
for (int i = n - 1; i >= 0; --i)
{
GL.Vertex3(pts[i]);
}
GL.End();
That connects all vertices to each other and center but there is one connection missing. There is nothing the specifies connection from first to last vertex. Adding GL.Vertex3(pts[n-1]); after loop would add the missing connection.
The Solution was actually extremely simple, I needed to increase the number of slices by 1. Pretty special if you ask me.
public void RenderCone(Vector3 baseToApexLength, Vector3 apexLocation, float height, float radius, int slices)
{
Vector3 c = new Vector3(apexLocation + (-baseToApexLength * height));
Vector3 e0 = Perpendicular(baseToApexLength);
Vector3 e1 = Vector3.Cross(e0, baseToApexLength);
float angInc = (float)(360.0 / slices * GrimoireMath.Pi / 180);
slices++; // this was the fix for my problem.
/**
* Compute the Vertices around the Directrix
*/
Vector3[] vertices = new Vector3[slices];
for (int i = 0; i < vertices.Length; ++i)
{
float rad = angInc * i;
Vector3 p = c + (((e0 * (float)Math.Cos((rad)) + (e1 * (float)Math.Sin(rad))) * radius));
vertices[i] = p;
}
/**
* Draw the Top of the Cone.
*/
GL.Begin(PrimitiveType.TriangleFan);
GL.Vertex3(apexLocation);
for (int i = 0; i < slices; ++i)
{
GL.Vertex3(vertices[i]);
}
GL.End();
/**
* Draw the Base of the Cone.
*/
GL.Begin(PrimitiveType.TriangleFan);
GL.Vertex3(c);
for (int i = slices - 1; i >= 0; --i)
{
GL.Vertex3(vertices[i]);
}
GL.End();
}
I'am using quadtree in 2D coordinates (base code get here: https://github.com/veeableful/Quadtree_Minimal)
At start, code using just one point to detect rectangles inside quadtree.
But detecting object is quad - select rectagles from quadtree become more complex. First decision was: detect rectangles form quadtree by 4 points, then sort them and remove duplicates. But question is - how good is this realization? Maybe there is more fast/optimized ways?
Rectangle:
struct stRectangle
{
float p[2]; //start point x/y
float s[2]; //width/height
};
Quad:
struct stQuad
{
glm::vec2 p[4];
};
Detecting by point:
vector<stRectangle*> MQuadTree::GetRectanglesAt(float inX, float inY)
{
if(Level == MaxLevel) return Rectangles;
vector<stRectangle*> ReturnRectangles, ChildReturnRectangles;
if(!Rectangles.empty()) ReturnRectangles = Rectangles;
const float HalfWidth = Width * 0.5;
const float HalfHeight = Height * 0.5;
if(inX > X + HalfWidth && inX < X + Width)
{
if(inY > Y + HalfHeight && inY < Y + Height)
{
ChildReturnRectangles = SE->GetRectanglesAt(inX, inY);
ReturnRectangles.insert(ReturnRectangles.begin(), ChildReturnRectangles.begin(), ChildReturnRectangles.end());
return ReturnRectangles;
}
if(inY > Y && inY <= Y + HalfHeight)
{
ChildReturnRectangles = NE->GetRectanglesAt(inX, inY);
ReturnRectangles.insert(ReturnRectangles.begin(), ChildReturnRectangles.begin(), ChildReturnRectangles.end());
return ReturnRectangles;
}
}
if(inX > X && inX <= X + HalfWidth)
{
if(inY > Y + HalfHeight && inY < Y + Height)
{
ChildReturnRectangles = SW->GetRectanglesAt(inX, inY);
ReturnRectangles.insert(ReturnRectangles.begin(), ChildReturnRectangles.begin(), ChildReturnRectangles.end());
return ReturnRectangles;
}
if(inY > Y && inY <= Y + HalfHeight)
{
ChildReturnRectangles = NW->GetRectanglesAt(inX, inY);
ReturnRectangles.insert(ReturnRectangles.begin(), ChildReturnRectangles.begin(), ChildReturnRectangles.end());
return ReturnRectangles;
}
}
return ReturnRectangles;
}
Detecting by Quad (suspicious code):
vector<stRectangle*> MQuadTree::GetRectanglesAt(float* Points)
{
vector<stRectangle*> ReturnRectangles;
vector<stRectangle*> CurrentRectangles;
CurrentRectangles = GetRectanglesAt(*(Points + 0), *(Points + 1));
ReturnRectangles.insert(ReturnRectangles.end(), CurrentRectangles.begin(), CurrentRectangles.end());
CurrentRectangles = GetRectanglesAt(*(Points + 2), *(Points + 3));
ReturnRectangles.insert(ReturnRectangles.end(), CurrentRectangles.begin(), CurrentRectangles.end());
CurrentRectangles = GetRectanglesAt(*(Points + 4), *(Points + 5));
ReturnRectangles.insert(ReturnRectangles.end(), CurrentRectangles.begin(), CurrentRectangles.end());
CurrentRectangles = GetRectanglesAt(*(Points + 6), *(Points + 7));
ReturnRectangles.insert(ReturnRectangles.end(), CurrentRectangles.begin(), CurrentRectangles.end());
sort(ReturnRectangles.begin(), ReturnRectangles.end());
ReturnRectangles.erase(unique(ReturnRectangles.begin(), ReturnRectangles.end()), ReturnRectangles.end());
return ReturnRectangles;
}
Buttons processing:
void MGame2::OnKeyDown(WPARAM wParam, LPARAM lParam)
{
Key[wParam] = 1;
if(pDrawFunc == &MGame2::DrawGame)
{
if(Key[VK_ESCAPE])
{
SendMessage(m_hWnd, WM_DESTROY, 0, 0);
return;
}
if(!Pause)
{
if(Key[VK_LEFT]) MoveQuad(PointQuad, -5, 0);
if(Key[VK_RIGHT]) MoveQuad(PointQuad, 5, 0);
if(Key[VK_UP]) MoveQuad(PointQuad, 0, 5);
if(Key[VK_DOWN]) MoveQuad(PointQuad, 0, -5);
if(Key[VK_LEFT] || Key[VK_RIGHT] || Key[VK_UP] || Key[VK_DOWN])
{
glBindBuffer(GL_ARRAY_BUFFER, vtxPointQuadId);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec2) * 4, &PointQuad.p[0][0], GL_STREAM_DRAW);
NearRectanglesToBuffer(PointQuad);
if(TestCollisions()) SetPointQuadColor(1, 0, 0, 1); else SetPointQuadColor(0, 0, 1, 1);
}
}
}
}
I tried to draw Bezier surface using OpenGL. My program reads an input file with number of sample points for plot, control points and color palette for surface coloring. It must output a new window with surface plot where I can manipulate properties of surface and control points.
Points generated from Bernstein polynomial are triangulated and assigned a color by color palette by mapping it from minimum and maximum height of triangles.
When I execute it, one row of triangles is assigned with a wrong color. I thought that I have an error in one of my loops but changing values proved nothing.
Here is the code:
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <cmath>
using namespace std;
struct vertex
{
float x;
float y;
float z;
};
struct RGB
{
float r;
float g;
float b;
};
int main_window;
bool TM = true;
vertex surfaceTranslate;
float surfaceRotate = 0;
vertex camera;
vertex up;
int currentPointX = 0, currentPointY = 0;
vertex toY;
int SampleR, SampleC;
int M, N;
int K;
vector < vector <vertex> > points;
vector <RGB> palette;
vector < vector <vertex> > control;
float minH, maxH;
int fact (int n)
{
if (!n || n == 1)
{
return 1;
}
return n * fact (n - 1);
}
int C (int n, int i)
{
return fact (n) / (fact (i) * fact (n - i));
}
void initialDisplay(void)
{
glClearColor (1.0, 1.0, 1.0, 1.0);
glDisable (GL_LIGHTING);
glEnable (GL_RESCALE_NORMAL);
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
gluPerspective (60, 1, 1, 1000000);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity();
}
void updateControl()
{
control.clear();
for (int i = 0; i < SampleR; ++i)
{
vector <vertex> temp;
for (int j = 0; j < SampleC; ++j)
{
float u = (float)(i) / (SampleR - 1);
float v = (float)(j) / (SampleC - 1);
vertex p;
p.x = p.y = p.z = 0;
for (int k = 0; k < M; ++k)
{
for (int m = 0; m < N; ++m)
{
float B_u = C (M - 1, k) * pow (u, k) * pow (1 - u, M - 1 - k);
float B_v = C (N - 1, m) * pow (v, m) * pow (1 - v, N - 1 - m);
p.x += B_u * B_v * points[k][m].x;
p.y += B_u * B_v * points[k][m].y;
p.z += B_u * B_v * points[k][m].z;
}
}
temp.push_back (p);
}
control.push_back (temp);
}
maxH = 1 << ((sizeof(float) * 8) - 1);
minH = -maxH;
for (int i = 0; i < SampleR - 1; ++i)
{
for (int j = 0; j < SampleC - 1; ++j)
{
float h = (control[i][j].y + control[i + 1][j].y + control[i][j + 1].y) / 3;
if (h > maxH)
{
maxH = h;
}
if (h < minH)
{
minH = h;
}
h = (control[i + 1][j].y + control[i][j + 1].y + control[i + 1][j + 1].y) / 3;
if (h > maxH)
{
maxH = h;
}
if (h < minH)
{
minH = h;
}
}
}
for (int i = 0; i < SampleR; ++i)
{
for (int j = 0; j < SampleC; ++j)
{
toY.x += control[i][j].x;
toY.z += control[i][j].z;
}
toY.x /= SampleR * SampleC;
toY.z /= SampleR * SampleC;
}
}
void display(void)
{
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt (camera.x, camera.y, camera.z, 0, 0, 0, up.x, up.y, up.z);
glTranslatef (surfaceTranslate.x, surfaceTranslate.y, surfaceTranslate.z);
glTranslatef (toY.x, toY.y, toY.z);
glRotatef (surfaceRotate, 0, 1, 0);
glTranslatef (- toY.x, -toY.y, -toY.z);
if (!TM)
{
glPointSize (10);
glBegin (GL_POINTS);
for (int i = 0; i < M; ++i)
{
for (int j = 0; j < N; ++j)
{
if (i == currentPointX && j == currentPointY)
{
glColor3f (1.0, 0.0, 0.0);
}
else
{
glColor3f (0.0, 0.0, 1.0);
}
glVertex3f (points[i][j].x, points[i][j].y, points[i][j].z);
}
}
glEnd();
}
glBegin (GL_TRIANGLES);
for (int i = 0; i < SampleR - 1; ++i)
{
for (int j = 0; j < SampleC - 1; ++j)
{
glVertex3f (control[i][j].x, control[i][j].y, control[i][j].z);
glVertex3f (control[i + 1][j].x, control[i + 1][j].y, control[i + 1][j].z);
glVertex3f (control[i][j + 1].x, control[i][j + 1].y, control[i][j + 1].z);
float h = (control[i][j].y + control[i + 1][j].y + control[i][j + 1].y) / 3;
int index = K * (h - minH) / (maxH - minH);
glColor3f (palette[index].r / 255, palette[index].g / 255, palette[index].b / 255);
glVertex3f (control[i + 1][j].x, control[i + 1][j].y, control[i + 1][j].z);
glVertex3f (control[i][j + 1].x, control[i][j + 1].y, control[i][j + 1].z);
glVertex3f (control[i + 1][j + 1].x, control[i + 1][j + 1].y, control[i + 1][j + 1].z);
h = (control[i + 1][j].y + control[i][j + 1].y + control[i + 1][j + 1].y) / 3;
index = K * (h - minH) / (maxH - minH);
glColor3f (palette[index].r / 255, palette[index].g / 255, palette[index].b / 255);
}
}
glEnd();
glutSwapBuffers();
}
void keyboardEvent (unsigned char key, int x, int y)
{
if (TM)
{
switch (key)
{
case ('2'):
TM = false;
glutSetWindowTitle ("Surface Editing Mode");
break;
case ('q'):
--surfaceTranslate.x;
break;
case ('w'):
++surfaceTranslate.x;
break;
case ('a'):
--surfaceTranslate.y;
break;
case ('s'):
++surfaceTranslate.y;
break;
case ('z'):
--surfaceTranslate.z;
break;
case ('x'):
++surfaceTranslate.z;
break;
case ('r'):
++surfaceRotate;
break;
case ('t'):
--surfaceRotate;
break;
}
}
else
{
switch (key)
{
case ('1'):
TM = true;
glutSetWindowTitle ("Transformation Mode");
break;
case ('q'):
--points[currentPointX][currentPointY].x;
updateControl();
break;
case ('w'):
++points[currentPointX][currentPointY].x;
updateControl();
break;
case ('a'):
--points[currentPointX][currentPointY].y;
updateControl();
break;
case ('s'):
++points[currentPointX][currentPointY].y;
updateControl();
break;
case ('z'):
--points[currentPointX][currentPointY].z;
updateControl();
break;
case ('x'):
++points[currentPointX][currentPointY].z;
updateControl();
break;
case ('i'):
if (!(SampleR % 2))
{
SampleR /= 2;
updateControl();
}
break;
case ('o'):
SampleR *= 2;
updateControl();
break;
case ('k'):
if (!(SampleC % 2))
{
SampleC /= 2;
updateControl();
}
break;
case ('l'):
SampleC *= 2;
updateControl();
break;
}
}
glutPostRedisplay();
}
void arrowKeys (int key, int x, int y)
{
if (!TM)
{
switch (key)
{
case (GLUT_KEY_UP):
if (currentPointY < N - 1)
{
++currentPointY;
}
break;
case (GLUT_KEY_DOWN):
if (currentPointY)
{
--currentPointY;
}
break;
case (GLUT_KEY_LEFT):
if (currentPointX)
{
--currentPointX;
}
break;
case (GLUT_KEY_RIGHT):
if (currentPointX < M - 1)
{
++currentPointX;
}
break;
}
glutPostRedisplay();
}
}
void changeDirection (int x, int y)
{
float dist = sqrt (pow (camera.x, 2) + pow (camera.y, 2) + pow (camera.z, 2));
camera.x = dist * sin (360.0 / 800 * x * 0.0174532) * sin (360.0 / 800 * y * 0.0174532);
camera.y = dist * cos (360.0 / 800 * y * 0.0174532);
camera.z = dist * cos (360.0 / 800 * x * 0.0174532) * sin (360.0 / 800 * y * 0.0174532);
up.x = dist * sin (360.0 / 800 * x * 0.0174532) * sin (360.0 / 800 * y * 0.0174532 - 1) - camera.x;
up.y = dist * cos (360.0 / 800 * y * 0.0174532 - 1) - camera.y;
up.z = dist * cos (360.0 / 800 * x * 0.0174532) * sin (360.0 / 800 * y * 0.0174532 - 1) - camera.z;
glutPostRedisplay();
}
void mouseEvent (int key, int state, int x, int y)
{
if (key == GLUT_KEY_LEFT)
{
changeDirection (x, y);
}
}
void readFile (char *fname)
{
ifstream file (fname);
if (file.is_open())
{
file >> SampleR >> SampleC;
file >> M >> N;
for (int i = 0; i < M; ++i)
{
vector <vertex> tempv;
for (int j = 0; j < N; ++j)
{
vertex temp;
file >> temp.x >> temp.y >> temp.z;
tempv.push_back (temp);
}
points.push_back (tempv);
}
file >> K;
for (int i = 0; i < K; ++i)
{
RGB temp;
file >> temp.r >> temp.g >> temp.b;
palette.push_back (temp);
}
}
file.close();
}
int main (int argc, char *argv[])
{
surfaceTranslate.x = surfaceTranslate.y = surfaceTranslate.z = toY.x = toY.y = toY.z = up.x = up.z = 0;
up.y = 1;
camera.x = camera.y = camera.z = 100;
readFile (argv[1]);
updateControl();
glutInit (&argc,argv);
glutInitDisplayMode (GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowPosition (50, 50);
glutInitWindowSize (800, 800);
main_window = glutCreateWindow ("Transformation Mode");
glutDisplayFunc (display);
glutKeyboardFunc (keyboardEvent);
glutSpecialFunc (arrowKeys);
glutMouseFunc (mouseEvent);
glutMotionFunc (changeDirection);
initialDisplay();
glutMainLoop();
}
Two things are problematic in display():
glColor3f should be called before the calls to glVertex3f to color them
your palette index calculation is wrong when h == maxH, it makes an out of bounds access
When h == maxH, (h - minH)/(maxH - minH) equals 1.0f, which means the line:
index = K * (h - minH) / (maxH - minH);
simplifies into:
index = K;
and K is the size of the palette. An easy fix would be to add a call to min(), like that:
index = min(K-1, K * (h - minH) / (maxH - minH));
I'm not sure if this belongs here or in graphichs progamming.....I am getting a really annoying access violation reading error and I can't figure out why. What I am trying to do is to refactor a keyframing function (calculate the mid position between two vertices positions). This function compiles and works fine
glBegin(GL_TRIANGLES);
for(int i = 0; i < numTriangles; i++) {
MD2Triangle* triangle = triangles + i;
for(int j = 0; j < 3; j++) {
MD2Vertex* v1 = frame1->vertices + triangle->vertices[j];
MD2Vertex* v2 = frame2->vertices + triangle->vertices[j];
Vec3f pos = v1->pos * (1 - frac) + v2->pos * frac;
Vec3f normal = v1->normal * (1 - frac) + v2->normal * frac;
if (normal[0] == 0 && normal[1] == 0 && normal[2] == 0) {
normal = Vec3f(0, 0, 1);
}
glNormal3f(normal[0], normal[1], normal[2]);
MD2TexCoord* texCoord = texCoords + triangle->texCoords[j];
glTexCoord2f(texCoord->texCoordX, texCoord->texCoordY);
glVertex3f(pos[0], pos[1], pos[2]);
}
}
glEnd();
Here the function calculates the positions and draws them. What I'd like to do is to calculate all positions before hand, store them in a Vertex array and then draw them.
If I try and remove it and replace this bloc in the exact same part of the program with the following
int vCount = 0;
for(int i = 0; i < numTriangles; i++) {
MD2Triangle* triangle = triangles + i;
for(int j = 0; j < 3; j++) {
MD2Vertex* v1 = frame1->vertices + triangle->vertices[j];
MD2Vertex* v2 = frame2->vertices + triangle->vertices[j];
Vec3f pos = v1->pos * (1 - frac) + v2->pos * frac;
Vec3f normal = v1->normal * (1 - frac) + v2->normal * frac;
if (normal[0] == 0 && normal[1] == 0 && normal[2] == 0) {
normal = Vec3f(0, 0, 1);
}
indices[vCount] = normal[0];
vCount++;
indices[vCount] = normal[1];
vCount++;
indices[vCount] = normal[2];
vCount++;
MD2TexCoord* texCoord = texCoords + triangle->texCoords[j];
indices[vCount] = texCoord->texCoordX;
vCount++;
indices[vCount] = texCoord->texCoordY;
vCount++;
indices[vCount] = pos[0];
vCount++;
indices[vCount] = pos[1];
vCount++;
indices[vCount] = pos[2];
vCount++;
}
}
I get access violation error "Unhandled exception at 0x01455626 in Graphics_template_1.exe: 0xC0000005: Access violation reading location 0xed5243c0" pointing at line 7
Vec3f pos = v1->pos * (1 - frac) + v2->pos * frac;
where the two Vs seems to have no value in the debugger.... Till this point the function behaves in exactly the same way as the one above, I don't understand why this happens?
EDIT ------------------------------------------------------------------------------------
Thank you Werner for spotting that the issue was the array initialization! As per your advice I refactored the function using std:vector containers and made the drawing use glDrawArrays instead of immediate mode.... But instead of an improvement in performance the framerate is a lot lower than before! Am I using this function right/efficiently? This is the refactored draw function:
for(int i = 0; i < numTriangles; i++) {
MD2Triangle* triangle = triangles + i;
for(int j = 0; j < 3; j++) {
MD2Vertex* v1 = frame1->vertices + triangle->vertices[j];
MD2Vertex* v2 = frame2->vertices + triangle->vertices[j];
Vec3f pos = v1->pos * (1 - frac) + v2->pos * frac;
Vec3f normal = v1->normal * (1 - frac) + v2->normal * frac;
if (normal[0] == 0 && normal[1] == 0 && normal[2] == 0) {
normal = Vec3f(0, 0, 1);
}
normals.push_back(normal[0]);
normals.push_back(normal[1]);
normals.push_back(normal[2]);
MD2TexCoord* texCoord = texCoords + triangle->texCoords[j];
textCoords.push_back(texCoord->texCoordX);
textCoords.push_back(texCoord->texCoordY);
vertices.push_back(pos[0]);
vertices.push_back(pos[1]);
vertices.push_back(pos[2]);
}
}
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glNormalPointer(GL_FLOAT, 0, &normals[0]);
glTexCoordPointer(2, GL_FLOAT, 0, &textCoords[0]);
glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
glDrawArrays(GL_TRIANGLES, 0, vertices.size()/3);
glDisableClientState(GL_VERTEX_ARRAY); // disable vertex arrays
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
vertices.clear();
textCoords.clear();
normals.clear();
Is there something extra that I am doing here? Cos this is really meant to be more efficient that glBegin()/End(), right?
Thanks for your time and help!
OK, let's make it an answer. The guess was:
Does it crash when you do not write to indices? Did you check that you
are not writing past the end of indices (overwriting your other data
structs)?
Your reply wass, that you create an array GLfloat indices[] since you do not know the array size in advance.
The best (performing) solution would be to calculate the array size in advance and create the array approriately.
GLfloat *indices = new GLfloat[calculated_number_of_elements];
...use array...;
delete [] indices;
Better yet you can create a std::vector:
std::vector<GLfloat> indices(calculated_number_of_elements);
The vector also has the advantage that it can be resized dynamically.