Sphere mesh in modern OpenGL - c++

I'm trying to create a triangle mesh of a sphere surface and draw it with OpenGL 4.1.
This is the code that I'm currently using obtained from the second answer of that question, The vertex layout is [x, y, z, r, b, g, a] that's why there is 7 float foreach vertex:
std::vector<float> vertices;
std::vector<unsigned int> indices;
const float dLambda = 2 * glm::pi<float>() / meridianNumber;
const float dPhi = glm::pi<float>() / parallelNumber;
unsigned int lastVertex = 0;
for (int i = 0; i < parallelNumber; ++i) {
for (int j = 0; j < meridianNumber; ++j) {
std::cout << "lot: " << glm::degrees(j * dLambda);
std::cout << "\tlat: " << glm::degrees(i * dPhi);
std::cout << std::endl;
float lambda1 = j * dLambda;
float phi1 = i * dPhi;
float lambda2 = j+1 == parallelNumber ? 2 * glm::pi<float>()
: (j+1) * dLambda;
float phi2 = i+1 == meridianNumber ? glm::pi<float>()
: (i+1) * dPhi;
// vertex 1
vertices.emplace_back(cosf(lambda1) * sinf(phi1) * radius);
vertices.emplace_back(cosf(phi1) * radius);
vertices.emplace_back(sinf(lambda1) * sinf(phi1) * radius);
vertices.emplace_back(0.5f);
vertices.emplace_back(1.0f);
vertices.emplace_back(1.0f);
vertices.emplace_back(1.0f);
// vertex 2
vertices.emplace_back(cosf(lambda1) * sinf(phi2) * radius);
vertices.emplace_back(cosf(phi2) * radius);
vertices.emplace_back(sinf(lambda1) * sinf(phi2) * radius);
vertices.emplace_back(0.5f);
vertices.emplace_back(1.0f);
vertices.emplace_back(1.0f);
vertices.emplace_back(1.0f);
// vertex 3
vertices.emplace_back(cosf(lambda2) * sinf(phi1) * radius);
vertices.emplace_back(cosf(phi1) * radius);
vertices.emplace_back(sinf(lambda2) * sinf(phi1) * radius);
vertices.emplace_back(0.5f);
vertices.emplace_back(1.0f);
vertices.emplace_back(1.0f);
vertices.emplace_back(1.0f);
// vertex 4
vertices.emplace_back(cosf(lambda2) * sinf(phi2) * radius);
vertices.emplace_back(cosf(phi2) * radius);
vertices.emplace_back(sinf(lambda2) * sinf(phi2) * radius);
vertices.emplace_back(0.5f);
vertices.emplace_back(1.0f);
vertices.emplace_back(1.0f);
vertices.emplace_back(1.0f);
indices.emplace_back(lastVertex);
indices.emplace_back(lastVertex+1);
indices.emplace_back(lastVertex+2);
indices.emplace_back(lastVertex+1);
indices.emplace_back(lastVertex+3);
indices.emplace_back(lastVertex+2);
lastVertex += 4;
}
But I am doing something wrong because that's what I'm drawing:
the code that I'm using to draw is:
GLCall(glDrawElements(
GL_TRIANGLES,
indicesNumber,
GL_UNSIGNED_INT,
(const void*) 0
));
EDIT 1:
The VAO settings are pretty complicated because I wrote a little layer of abstraction over opengl...
I have a class called VertexBuffer that creates, keeps alive and destroys an OpenGL array buffer.
Another class IndexBuffer is very similar to the previous one that manages the Element array buffer.
This two classes are very simple to use they can be constructed, binded, unbinded and destroyed, nothing more.
There is a third class that represents a layout of a single vertex in an OpenGL vertex buffer; this class called VertexLayout contains all the data that is necessary to call the glVertexAttribPointer.
hpp:
class VertexLayout {
private:
struct Element {
unsigned int type;
unsigned int count;
unsigned char normalized;
size_t typeSize;
Element(
unsigned int type, unsigned int count, unsigned char normalized,
size_t typeSize
);
};
std::vector<Element> elements;
unsigned int stride;
public:
VertexLayout();
template<typename T>
VertexLayout &push(unsigned int count, unsigned char normalized = GL_FALSE){
std::fputs(
"this function has to be implemented for desired type",
stderr
);
assert(false);
return *this;
}
const std::vector<Element> &getElements() const;
unsigned int getStride() const;
};
cpp:
template<>
VertexLayout &VertexLayout::push<unsigned int>(
unsigned int count, unsigned char normalized
) {
elements.emplace_back(
GL_UNSIGNED_INT, count, normalized, sizeof(unsigned int)
);
stride += count * sizeof(unsigned int);
return *this;
};
template<>
VertexLayout &VertexLayout::push<unsigned char>(
unsigned int count, unsigned char normalized
) {
elements.emplace_back(
GL_UNSIGNED_BYTE, count, normalized, sizeof(unsigned char)
);
stride += count * sizeof(unsigned char);
return *this;
};
template<>
VertexLayout &VertexLayout::push<float>(unsigned int count, unsigned char normalized){
elements.emplace_back(GL_FLOAT, count, normalized, sizeof(float));
stride += count * sizeof(float);
return *this;
}
VertexLayout::Element::Element(
unsigned int type, unsigned int count,
unsigned char normalized, size_t typeSize
) : type(type), count(count), normalized(normalized), typeSize(typeSize) {}
const std::vector<VertexLayout::Element> &VertexLayout::getElements() const {
return elements;
}
unsigned int VertexLayout::getStride() const {
return stride;
}
VertexLayout::VertexLayout() : stride(0) {}
So an instance of VertexLayout should be created foreach VertexBuffer object and foreach opengl attribute should be called a push<type>(numberOfElementOfThatType).
The fourth and last class is the VertexArray class that represents a VAO: this last class keeps trace of all the VertexBuffer and IndexBuffer objects that are connected to the vao and sets the layout calling glVertexAttribPointer when a VertexBuffer is added using the following method:
void VertexArray::addBuffer(
const VertexBuffer &buffer, const VertexLayout &layout
) {
GLCall(glBindVertexArray(id));
buffer.bind();
const auto &elements = layout.getElements();
size_t offset = 0;
for (unsigned int i = 0; i < elements.size(); ++i) {
const auto &element = elements[i];
GLCall(glEnableVertexAttribArray(i));
GLCall(glVertexAttribPointer(
i, element.count, element.type, element.normalized,
layout.getStride(), (const void *)offset
));
offset += element.count * element.typeSize;
}
vertexBuffers.emplace_back(buffer);
}
GLCall is a macro that does nothing in release while in debug is clears the OpenGL erros and prints the new errors.
EDIT 2:
This is the class VertexBuffer that represents one VBO:
hpp
class VertexBuffer {
private: // static
static std::map<unsigned int, unsigned int> references;
private: // member
unsigned int rendererID;
public:
VertexBuffer();
VertexBuffer(
const void *data, unsigned long size,
unsigned int usage = GL_STATIC_DRAW
);
VertexBuffer(const VertexBuffer &oth);
VertexBuffer &operator=(const VertexBuffer &rhs);
~VertexBuffer();
void bind() const;
void unbind() const;
};
cpp:
std::map<unsigned int, unsigned int> VertexBuffer::references;
VertexBuffer::VertexBuffer(
const void *data,
unsigned long size,
unsigned int usage
) {
GLCall(glGenBuffers(1, &rendererID));
GLCall(glBindBuffer(GL_ARRAY_BUFFER, rendererID));
GLCall(glBufferData(GL_ARRAY_BUFFER, size, data, usage));
references.insert_or_assign(rendererID, 1);
}
VertexBuffer::VertexBuffer(const VertexBuffer &oth) {
if (oth.rendererID != 0){
auto ref = references.find(oth.rendererID);
assert(ref != references.end());
ref->second++;
}
rendererID = oth.rendererID;
}
VertexBuffer &VertexBuffer::operator=(const VertexBuffer &rhs) {
if (rendererID != 0) {
auto refs = references.find(rendererID);
assert(refs != references.end());
if (--refs->second == 0) {
GLCall(glDeleteBuffers(1, &rendererID));
references.erase(refs);
}
}
if (rhs.rendererID != 0){
auto ref = references.find(rhs.rendererID);
assert(ref != references.end());
ref->second++;
}
rendererID = rhs.rendererID;
return *this;
}
VertexBuffer::VertexBuffer() : rendererID(0) {}
VertexBuffer::~VertexBuffer() {
if (rendererID != 0) {
auto ref = references.find(rendererID);
assert(ref != references.end());
if (--ref->second == 0) {
GLCall(glDeleteBuffers(1, &rendererID));
references.erase(ref);
}
}
}
void VertexBuffer::bind() const {
GLCall(glBindBuffer(GL_ARRAY_BUFFER, rendererID));
}
void VertexBuffer::unbind() const {
GLCall(glBindBuffer(GL_ARRAY_BUFFER, 0));
}
In the sphere I have only one big buffer that contains both positions and colors.

I found the solution. It was a very stupid error: The constructor of the VertexBuffer class needs the size of the buffer in bytes but when I called it I passed only the size of the std::vector that is the number of elements.

Related

Compilation error in cuda kernel calling/ passing parameters

In the actual code, my intention is to get the output array by comparing the input array to the scalar. Or simply output = input > scalar.
Simple sample host-side code as shown below is working as expected.
float *h_data1 = (float *)malloc(W1*H1 * sizeof(float));
bool *h_result = (bool *)malloc(H1*W2 * sizeof(bool));
float *d_data1; gpuErrchk(cudaMalloc(&d_data1, W1*H1 * sizeof(float)));
bool *d_result; gpuErrchk(cudaMalloc(&d_result, H1*W2 * sizeof(bool)));
for (int i = 0; i < W1*H1; i++) h_data1[i] = (float)i;
gpuErrchk(cudaMemcpy(d_data1, h_data1, W1*H1 * sizeof(float), cudaMemcpyHostToDevice));
float scalar = 2;
compGraterRetOut<float, bool><< <outw, outh >> > (d_data1, d_result, scalar);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
The device side code is
template<typename TType, typename TTypeOut>
__global__ void compGraterRetOut(TType *dataIn, TTypeOut *dataOut, const TType scalar)
{
int i = blockIdx.x * blockDim.x + threadIdx.x;
dataOut[i] = (dataIn[i] > scalar);
}
Coming to the actual code, I have an image class as shown below (Only some part of the class is shown).
template<typename TType, ImageType TImageType>
class Image
{
public:
Image(uint32_t width, uint32_t height, uint32_t depth = 1);
private:
TType* m_data;
uint32_t m_width;
uint32_t m_height;
uint32_t m_depth;
uint32_t m_bufferSize;
};
template<typename TType, ImageType TImageType>
Image<TType, TImageType>::Image(uint32_t width, uint32_t height, uint32_t depth) :m_width(width), \
m_height(height), m_depth(depth)
{
if (width == 0 || height == 0)
return;
cudaError_t cudaStatus;
//m_data = new TType[m_width * m_height * m_depth];
gpuErrchk(cudaStatus = cudaMalloc(&m_data, sizeof(TType) * m_width * m_height * m_depth));
if (cudaStatus == cudaSuccess)
{
m_bufferSize = m_width * m_height * m_depth;
}
else
{
std::cout << "Error malloc function failed [" << cudaStatus << "]" << std::endl;
}
};
To achieve the objective out = in > scalar, operator> is overloaded as shown below. This threw a compilation error as
"member "Image::m_data [with TType=float_t,
TImageType=ImageType::WHD]""
the code looks as shown below.
inline Image<uint32_t, TImageType> Image<TType, TImageType>::operator>(TType scalar) const
{
Image<uint32_t, TImageType> ret(m_width, m_height, m_depth);
compGraterRetOut<TType, uint32_t> << <m_width * 4, (m_height * m_depth/4) >> > (m_data, ret.m_data, scalar);
gpuErrchk(cudaGetLastError());
gpuErrchk(cudaDeviceSynchronize());
return std::move(ret);
}
To fix the compilation error I changed the function operator>. Here, cuda memory is allocated inside the function instead of inside of class's contructor.
template<class TType, ImageType TImageType>
inline Image<uint32_t, TImageType> Image<TType, TImageType>::operator>(TType scalar) const
{
cudaError_t cudaStatus;
uint32_t *dataout;
gpuErrchk(cudaMalloc(&dataout, m_width*m_height*m_depth * sizeof(uint32_t)));
Image<uint32_t, TImageType> ret(dataout, m_width, m_height, m_depth);
compGraterRetOut<TType, uint32_t> << <m_width * 4, (m_height * m_depth/4) >> > (m_data, dataout, scalar);
gpuErrchk(cudaGetLastError());
gpuErrchk(cudaDeviceSynchronize());
return std::move(ret);
}
Finally, my question is why last code compiled without an error, but not previous to that?
The problem has nothing to do with Cuda. It is the problem with templates and OOPS. When template class access a member in its own type it would not violate OOPS paradigm. Accessing a private member of the same class with different template arguments violate the OOPS paradigm. That is the answer.

Class constructor does not take input parameters C++ (all attributes get the same value and the same value applies to all objects)

I created a class named Triangle that takes triangles as objects.
The attributes are the ID of the triangle (m_ID), the index of the three vertices (m_S1, m_S2, m_S3) and the index of the three adjacent triangles (m_T1, m_T2, m_T3).
I want to create a triangle object by passing input parameters in the constructor.
The parameters are passed by value, but the object is badly created. In fact, all three triangles I tried to create (triangle2, 3 and 4) have the same value for attributes. The value is -858993460 which looks like an address.
Even when I use the simplest input values (Triangle triangle4(1,1,1,1,1,1,1)), the attributes are not created correctly.
/*
Name: insert
Description: insert the new point and create 3 new triangles
*/
void insert(const int triangleIndex)
{
// Extract the Index of the new point
int newPointIndex = pointList.size();
// Create the line of the second new triangle
Triangle triangle2(triangleList.size() + 1, newPointIndex, triangleList[triangleIndex].getS3(), triangleList[triangleIndex].getS1(), triangleList[triangleIndex].getT2(), triangleList.size() + 2, triangleIndex);
// Create the line of the third new triangle
Triangle triangle3(triangleList.size() + 2, newPointIndex, triangleList[triangleIndex].getS1(), triangleList[triangleIndex].getS2(), triangleList[triangleIndex].getT3(), triangleIndex, triangleList.size() + 1);
// Test only
Triangle triangle4(1, 1, 1, 1, 1, 1, 1);
// Get the adjacent triangles of the base triangle
int T1 = triangleList[triangleIndex].getT1();
int T2 = triangleList[triangleIndex].getT2();
int T3 = triangleList[triangleIndex].getT3();
// Update the line of first new triangle
triangleList[triangleIndex].setS1(newPointIndex);
triangleList[triangleIndex].setT2(triangleList.size() + 1);
triangleList[triangleIndex].setT3(triangleList.size() + 2);
// Update the adjacent triangles
triangleList[T2].setT3(triangleList.size() + 1);
triangleList[T3].setT2(triangleList.size() + 2);
// Insert the new triangles in the triangulation
triangleList.push_back(triangle2);
triangleList.push_back(triangle3);
}
/*-----------------------------------------
Name: Triangle
Author: Michael Landry
Description: Implementation of class Triangle
Date: 2018-02-23
Version: 1.00
-------------------------------------------*/
#include "Triangle.h"
// Constructor with no parameter
Triangle::Triangle() : m_ID(0), m_S1(0), m_S2(0), m_S3(0), m_T1(0), m_T2(0), m_T3(0)
{
}
// Constructor with parameters
Triangle::Triangle(int p_ID, int p_S1, int p_S2, int p_S3, int p_T1, int p_T2, int p_T3) : m_ID(p_ID), m_S1(p_S1), m_S2(p_S2), m_S3(p_S3), m_T1(p_T1), m_T2(p_T2), m_T3(p_T3)
{
}
// Global setter
void Triangle::setTriangle(int p_ID, int p_S1, int p_S2, int p_S3, int p_T1, int p_T2, int p_T3)
{
m_ID = p_ID;
m_S1 = p_S1;
m_S2 = p_S2;
m_S3 = p_S3;
m_T1 = p_T1;
m_T2 = p_T2;
m_T3 = p_T3;
}
// Setter for each individual parameter
void Triangle::setID(int p_ID)
{
m_ID = p_ID;
}
void Triangle::setS1(int p_S1)
{
m_S1 = p_S1;
}
void Triangle::setS2(int p_S2)
{
m_S2 = p_S2;
}
void Triangle::setS3(int p_S3)
{
m_S3 = p_S3;
}
void Triangle::setT1(int p_T1)
{
m_T1 = p_T1;
}
void Triangle::setT2(int p_T2)
{
m_T2 = p_T2;
}
void Triangle::setT3(int p_T3)
{
m_T3 = p_T3;
}
// Getter for each individual parameter
int Triangle::getID() const
{
return m_ID;
}
int Triangle::getS1() const
{
return m_S1;
}
int Triangle::getS2() const
{
return m_S2;
}
int Triangle::getS3() const
{
return m_S3;
}
int Triangle::getT1() const
{
return m_T1;
}
int Triangle::getT2() const
{
return m_T2;
}
int Triangle::getT3() const
{
return m_T3;
}
/*-----------------------------------------
Name: Triangle
Author: Michael Landry
Description: Declaration of class Triangle
Date: 2018-02-23
Version: 1.00
-------------------------------------------*/
#ifndef TRIANGLE_H_
#define TRIANGLE_H_
class Triangle
{
public:
// Constructor with no parameter
Triangle::Triangle();
// Constructor with parameters
Triangle::Triangle(int, int, int, int, int, int, int);
// Setters
void Triangle::setTriangle(int p_ID, int p_S1, int p_S2, int p_S3, int p_T1, int p_T2, int p_T3);
void Triangle::setID(int p_ID);
void Triangle::setS1(int p_S1);
void Triangle::setS2(int p_S2);
void Triangle::setS3(int p_S3);
void Triangle::setT1(int p_T1);
void Triangle::setT2(int p_T1);
void Triangle::setT3(int p_T1);
// Getters
int Triangle::getID() const;
int Triangle::getS1() const;
int Triangle::getS2() const;
int Triangle::getS3() const;
int Triangle::getT1() const;
int Triangle::getT2() const;
int Triangle::getT3() const;
private:
// The ID of the triangle
int m_ID;
// The 3 vertices forming the triangle
int m_S1;
int m_S2;
int m_S3;
// The 3 adjacent triangles
int m_T1;
int m_T2;
int m_T3;
};
#endif /* TRIANGLE_H_ */

Fast allocation of vectors of vectors

I store 3D data in a std::vector-based structure:
std::shared_ptr< std::vector< std::vector< std::vector< Type > > > > data;
I allocate this structure by calling resize while iterating through the vectors:
arraydata.reset(new std::vector< std::vector< std::vector< Type > > >());
arraydata->resize(m_width);
for (unsigned int col_index = 0; col_index < m_width; ++col_index)
{
(*arraydata)[col_index].resize(m_height);
for (unsigned int line_index = 0; line_index < m_height; ++line_index)
{
(*arraydata)[col_index][line_index].resize(m_nbbands);
}
}
But this allocation takes a lot of time when the dimensions are big...
Is there a way to allocate in a single operation all the needed space (with malloc(m_width*m_height*m_nbbands*sizeof(Type)) for example) and then to assign to each vector its own data space in the global space ? Would it be more performant ?
Edit: I tested #justin-time's idea
arraydata.reset(new std::vector< std::vector< std::vector< T > > >(m_width,
std::vector< std::vector< T > >(m_height, std::vector< T > (m_nbbands))));
gives an execution time comparable to original code, around 4.9 s for allocation and 40 s for deallocation ???
This can be seen in the memory manager:
I don't succeed in testing allocation from a malloc, this code fails at std::vector< T > tmp(datptr, (T*)(datptr+arraySize));
unsigned int arraySize = m_nbbands*sizeof(T);
T *datptr = (T*)malloc(m_width*m_height*arraySize);
arraydata.reset(new std::vector< std::vector< std::vector< T > > >(m_width));
for (unsigned int col_index = 0; col_index < m_width; ++col_index)
{
(*arraydata)[col_index].resize(m_height);
for (unsigned int line_index = 0; line_index < m_height; ++line_index)
{
std::vector< T > tmp(datptr, (T*)(datptr+arraySize));
(*arraydata)[col_index][line_index].swap(tmp);
// also tested with same results:
//(*arraydata)[col_index][line_index] =
// std::vector< T >(datptr, (T*)(datptr+arraySize));
datptr += arraySize;
}
}
Don't use a vector of vector of vectors. Use a class which has an internal array and then provides a way to access the elements. For example:
template <typename T>
class vec3d {
std::vector<T> data;
size_t xmax, ymax, zmax;
public:
T& operator()(size_t x, size_t y, size_t z)
{ return data[x+y*xmax+z*xmax*ymax]; }
const T& operator()(size_t x, size_t y, size_t z)
{ return data[x+y*xmax+z*xmax*ymax]; }
vec3d(size_t x, size_t y, size_t z)
: xmax(x), ymax(y), zmax(z), data(x*y*z) {}
T& v(size_t x, size_t y, size_t z) { return (*this)(x,y,z); }
};
access would then be like
shared_ptr<vec3d<int>> p = make_shared<vec3d<int>>(10, 20, 30);
p->v(5,6,7) = 14;
or
vec3d vec(5,6,7);
vec(1,2,4) = 16.0f; // Fortran style indexing.
You will probably want some more members to allow iteration, the dimensions, etc. Because this is a single allocation, it will be much faster.
Following Martin Bonner's answer, I came to the following solution, for which allocation and deallocation take less than a second.
Data is accessible with arraydata[x][y][z].
arraydata = std::make_shared< CImageStorage< T > >(m_width,
m_height, m_nbbands, bppixel);
with
template<class T>
class CImageStorage1D
{
T* data;
unsigned int m_nbbands;
public:
CImageStorage1D(T* p, unsigned int nbbands) :
data(p), m_nbbands(nbbands) {}
T& DataPtr(unsigned int band) { return data[band]; }
const T& DataPtr(unsigned int band) const { return data[band]; }
T& operator[](unsigned int band) { return (data[band]); }
const T& operator[] (unsigned int band) const { return (data[band]); }
unsigned int size()const { return m_nbbands; }
};
template<class T>
class CImageStorage2D
{
T* data;
unsigned int m_height, m_nbbands, m_bppixel;
public:
CImageStorage2D(T* p, unsigned int height, unsigned int nbbands,
unsigned int bppixel, std::shared_ptr< std::vector<int> > heightShift) :
data(p), m_height(height), m_nbbands(nbbands), m_bppixel(bppixel) {}
T* DataPtr(unsigned int height) { return (T*)(data+m_height*m_nbbands); }
const T* DataPtr(unsigned int height) const {
return (T*)(data+m_height*m_nbbands); }
CImageStorage1D<T> operator[](unsigned int height) {
return CImageStorage1D<T>((T*)(data+m_height*m_nbbands), m_nbbands); }
const CImageStorage1D<T> operator[] (unsigned int height) const {
return CImageStorage1D<T>((T*)(data+m_height*m_nbbands), m_nbbands); }
unsigned int size()const { return m_height; }
};
template<class T>
class CImageStorage
{
T* data;
unsigned int m_width, m_height, m_nbbands, m_bppixel;
public:
CImageStorage(unsigned int width, unsigned int height, unsigned int nbbands,
unsigned int bppixel) :
m_width(width), m_height(height), m_nbbands(nbbands), m_bppixel(bppixel)
{
data = (T*)malloc(m_width*m_height*m_nbbands*m_bppixel);
}
~CImageStorage() { free(data); }
bool IsValid() { return (data != nullptr); }
T** DataPtr(unsigned int width) {
return (T**)(data+width*m_height*m_nbbands); }
const T** DataPtr(unsigned int width) const {
return (T**)(data+width*m_height*m_nbbands); }
CImageStorage2D<T> operator[](unsigned int width) {
return CImageStorage2D<T>( (T*)(data+width*m_height*m_nbbands),
m_height, m_nbbands, m_bppixel); }
const CImageStorage2D<T> operator[] (unsigned int width) const {
return CImageStorage2D<T>((T*)(data+width*m_height*m_nbbands),
m_height, m_nbbands, m_bppixel); }
unsigned int size()const { return m_width; }
};

Create and Iterate through an array of templated objects

I have create a C++ template define different data type 2D points, as a task to re-learn C++ in an OpenGL program.
template <typename T>
class Point2D {
public:
Point2D(const T& x, const T& y) {
_data[0] = x;
_data[1] = y;
}
const T & x() { return _data[0]; }
const T & y() { return _data[1]; }
private:
T _data[2];
};
I can create multiple named instances of the Point2D and store values and use them to draw basic shapes
Point2D <GLdouble> c0( 0.5, 0.5);
Point2D <GLdouble> c1(-0.5, 0.5);
then to draw a like I access the values in the points.
glBegin(GL_LINES);
glVertex2d(c0.x(), c0.y());
glVertex2d(c1.x(), c1.y());
glEnd();
However, I would like to create an array of Points2D objects of the same templated type (eg double) to represent a shape.
How do I create and array of templated Point2D represent more complex objects that I could draw in for loop array of templated objects.
glBegin(GL_POLYGON);
for (k = 0; k < 4; k++) {
glVertex2d( ????? [i].x(), ????? [i].x());
}
glEnd();
Could someone suggest a way I can do this?
Thanks
Stuart
To storing multiple points use a C array or even better std::vector container and then in loop enumerate points and call glVertex2d for each
#include <vector>
std::vector< Point2D <GLdouble> > points = {Point2D<GLdouble>(0.5, 0.5),
Point2D<GLdouble>(-0.5, 0.5)};
size_t size = points.size();
glBegin(GL_LINES);
for (size_t i = 0; i < size; ++i)
{
glVertex2d( points[i].x(), points[i].x());
}
glEnd();
But if you use OpenGL 1.1 or greater it is better to use vertex array buffer instead individual glVertex call. Try to modify your point class by adding method which return ponter to firts array element
template <typename T>
class Point2D {
public:
Point2D(const T& x, const T& y) {
_data[0] = x;
_data[1] = y;
}
const T & x() { return _data[0]; }
const T & y() { return _data[1]; }
T* data() const { return &_data[0]; }
private:
T _data[2];
};
then add points to container, create once vertex buffer then bind it and call function glVertexPointer. To draw elements use glDrawArrays
GLuint bufferId;
glGenBuffers(1, &bufferId);
std::vector< Point2D <GLdouble> > points = {Point2D<GLdouble>(0.5, 0.5),
Point2D<GLdouble>(-0.5, 0.5)};
size_t size = points.size();
glBindBuffer(GL_ARRAY_BUFFER, bufferId);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_DOUBLE, 0, (const GLvoid *)points[0].data()); // <= 2 - X and Y
glDrawArrays(GL_LINES, 0, points.size());
why don't you use the std::vector ? Something like :
#include <iostream>
#include <vector>
using namespace std;
template <typename T>
class Point2D {
public:
Point2D(const T& x = T(), const T& y = T()) {
_data[0] = x; _data[1] = y;
}
const T & x() { return _data[0]; }
const T & y() { return _data[1]; }
private:
T _data[2];
};
int main()
{
vector<Point2D<double> > polygon(4);
for(int i=0;i < polygon.size(); ++i)
cout << "Point " << i << " = (" << polygon[i].x() << "," << polygon[i].y() << ")" << endl;
return 0;
}

How to balance style and efficiecny when accessing two-dimensional array, vector of vectors?

CObject below has vertex arrays and buffers and various other member variables. The question is, how should I access the data in CAgent from CAnotherClass especially with the two-dimensional array (currently vectors) concerning style and also efficiency within and near openGL ES rendering callback function. The vectors of sub-objects are normally reasonable but could get as large as 32,000.
class CObject
{
// vertex arrays, buffers, and data
int val;
public:
int GetValue(){ return val; }
void SetValue( int v ){ val = v; }
};
class CAgent
{
friend class CAnotherClass; // may I, since like an extension?
enum ObjType { a, b, c, };
std::vector< CObject * > m_Objects; // implementation fills these
std::vector< std::vector< CObject * > > m_SubObjects; // each object above has n sub-objects
// for option B below
CObject * GetSubObjectN( int type, int n ) { return m_SubObjects[type][n]; }
std::vector< CObject * > & GetSubObjects( int type ) { return m_SubObjects[type]; }
};
class CAgentSub : public CAgent
{
class CAnotherClass * m_AgentExtension; // really just an extension of this
void Render( double timestamp ); // implementation calls CAnotherClass::Render()
};
class CAnotherClass
{
CAgentSub * m_Agent;
CObject * m_CurrentObject; // for convenience
std::vector< CObject * > * m_CurrentSubObjects;
int m_ObjectType; // or this way
void SetObjectType( int type )
{
m_CurrentObject = m_Agent->m_Objects[type]; // for convenience
m_CurrentSubObjects = &m_Agent->m_SubObjects[type];
m_ObjectType = type; // or this way?
}
void DoSomethingInteresting()
{
int val = -1;
int startVal = 0;
// option A
for ( int n = 0; n < m_CurrentSubObjects->size(); ++n )
{
val = (*m_CurrentSubObjects)[n]->GetValue(); // this is what I'm doing but seems cumbersome
// and not that easy to read
(*m_CurrentSubObjects)[n]->SetValue( startVal );
}
// option B
for ( int n = 0; n < m_Agent->GetSubObjects(m_ObjectType).size(); ++n )
val = m_Agent->GetSubObjectN( m_ObjectType, n )->GetValue();
// option C
for ( int n = 0; n < m_Agent->m_SubObjects[m_ObjectType].size(); ++n )
val = m_Agent->m_SubObjects[m_ObjectType][n]->GetValue();
// option D
for ( std::vector<CObject *>::iterator it = m_Agent->m_SubObjects[m_ObjectType].begin();
it != m_Agent->m_SubObjects[m_ObjectType].end();
++it )
{
(*it)->SetValue( startVal );
}
// setting data at various indexes
int index = 23;
(*m_CurrentSubObjects)[index]->SetValue( startVal );
m_Agent->GetSubObjectN( m_ObjectType, index )->SetValue( startVal );
m_Agent->m_SubObjects[m_ObjectType][index]->SetValue( startVal );
std::vector<CObject *>::iterator nth = m_Agent->m_SubObjects[m_ObjectType].begin() + index;
(*nth)->SetValue( startVal );
}
void Render( double timestamp );
};