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;
}
Related
I want to create a "3D console game" using RayCasting. To begin with, I needed a container with all the information about the world. First of all, I started with just two shapes, a rectangle and a circle. Actually, here's how I implemented them:
enum POINTS
{
POINT_1,
POINT_2
};
enum ASIXS
{
ASIX_X,
ASIX_Y,
};
class Point
{
private:
double properties[2];
public:
Point()
{
properties[ASIX_X] = 0;
properties[ASIX_Y] = 0;
}
Point(double x, double y)
{
properties[ASIX_X] = x;
properties[ASIX_Y] = y;
}
double& operator[] (int index)
{
return properties[index];
}
};
class Rectangle
{
private:
Point base[2];
public:
//Points getter
Point& operator[](int index)
{
return base[index];
}
};
class Circle
{
private:
Point center;
double radius = 0;
public:
Point& getCenter()
{
return center;
}
double& getRadius()
{
return radius;
}
};
Next, I need a generic container. Obviously this should be a pattern:
template<typename T>
class ObjectArr
{
protected:
int qty;
T* objs;
public:
ObjectArr()
{
qty = 0;
objs = nullptr;
}
~ObjectArr()
{
delete[] objs;
}
void add()
{
T* temp = new T[++qty];
for (int i = 0; i < qty - 1; i++)
temp[i] = objs[i];
delete[] objs;
objs = temp;
}
T getObj(int index)
{
return objs[index];
}
};
Here's what it will look like:
#include <iostream>
using namespace std;
int main()
{
ObjectArr<Rectangle> rect;
rect.add();
//rect.getObj(id)[point][asix]
rect.getObj(0)[POINT_1][ASIX_X] = 5;
cout << rect.getObj(0)[POINT_1][ASIX_X]; //Out 5
ObjectArr<Circle> cir;
cir.add();
//cir.getObj(id).getCenter()[asix]//getRadius()
cir.getObj(0).getCenter() = {0, 0};
return 0;
}
And in the end, I would like one common container that would store ObjectArr<>. From the beginning I thought about polymorphism. Something like this:
#include <vector>
class Container
{
//...
};
template<typename T>
class ObjectArr : public Container
{
//...
};
int main()
{
vector<Container*> test;
test.push_back(new ObjectArr<Rectangle>);
test.push_back(new ObjectArr<Circle>);
test[0]->add();
//...
return 0;
}
But with this approach, the function T getObj(int index) cannot be used from such a container. And writing it as a virtual one will not work either because of the template. And I also don’t want to prescribe every possible shape, so that it would be easier to add new shapes. Wrote a class of a new figure and all. Tell me, please, how should I be and what should I do, maybe I need to completely change the approach?
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.
I write a small animation program with OpenSceneGraph(osg). In each frame, I should update the models(vertex position,normal, color and other stuff) in the scene.
Now I shoudl convert the data to osg in each frame. The code is like:
cog_.vertex_->clear();
cog_.vertex_->push_back(osg::Vec3(1.0,1.0,1.0));
However these convertion is very time consuming. So I'm wondering how to access data directly via pointer. I tried but failed. The following is what I did, hope someone can help me to find the problem, or tell me better solution:
First, I wrap the a pointer to osg::Vec3,
class vec3_map : public osg::Vec3
{
public:
typedef osg::Vec3::value_type value_type;
osg::Vec3::value_type* p_;
vec3_map() :p_(0) {}
vec3_map(osg::Vec3::value_type*p, int offset = 0) :p_(p+offset){}
inline value_type& x() { return *(p_+0); }
inline value_type& y() { return *(p_+1); }
inline value_type& z() { return *(p_+2); }
// some other stuff
};
When I want to build an array of vec3_map, I found that osg use a tempalte to typedef Vec3Array, but there isn't any type for a pointer, so I choose the closet one (I know pointer is a variable with unsigned long int type):
typedef osg::TemplateArray<vec3_map, osg::Array::UIntArrayType, 1, GL_UNSIGNED_INT> Vec3_map_Array;
With this definition, I can rewrite my problem to the following:
#define BOOST_ALL_NO_LIB
#include <boost/shared_ptr.hpp>
#include <fstream>
#include <vector>
#include <osg/Group>
#include <osg/Geometry>
#include <osgViewer/Viewer>
#include <osg/Vec3>
class vec3_map : public osg::Vec3
{
public:
typedef osg::Vec3::value_type value_type;
osg::Vec3::value_type* p_;
vec3_map() :p_(0) {}
vec3_map(osg::Vec3::value_type*p, int offset = 0) :p_(p + offset) {}
inline value_type* ptr() { return p_; }
inline const value_type* ptr() const { return p_; }
inline value_type& operator [] (int i) { return *(p_ + i); }
inline value_type operator [] (int i) const { return *(p_ + i); }
inline value_type& x() { return *(p_ + 0); }
inline value_type& y() { return *(p_ + 1); }
inline value_type& z() { return *(p_ + 2); }
inline value_type x() const { return *(p_); }
inline value_type y() const { return *(p_ + 1); }
inline value_type z() const { return *(p_ + 2); }
};
typedef osg::TemplateArray<vec3_map, osg::Array::UIntArrayType, 1, GL_UNSIGNED_INT> Vec3_map_Array;
struct point_data
{
point_data(float xx, float yy, float zz) { x = xx; y = yy; z = zz; }
float x;
float y;
float z;
};
osg::Geode* create_point_node(boost::shared_ptr<std::vector<point_data> > ptr)
{
osg::Geode *geode_ = new osg::Geode;
osg::Geometry* geometry_ = new osg::Geometry;
Vec3_map_Array* vertex_ = new Vec3_map_Array;
osg::DrawElementsUInt* point_idx_ = new osg::DrawElementsUInt;
vertex_->reserve(ptr->size());
point_idx_->reserve(ptr->size());
vec3_map vm;
std::vector<point_data> & pd = *ptr;
for (size_t i = 0; i < ptr->size(); ++i)
{
vertex_->push_back(vec3_map(&(pd[i].x)));
point_idx_->push_back(i);
}
geometry_->setVertexArray(vertex_);
geometry_->addPrimitiveSet(point_idx_);
geode_->addDrawable(geometry_);
return geode_;
}
int main(int argc, char *argv[])
{
boost::shared_ptr<std::vector<point_data> > pd(new std::vector<point_data>);
pd->push_back(point_data(1.0, 1.0, 1.0));
pd->push_back(point_data(2.0, 2.0, 2.0));
osgViewer::Viewer viewer;
osg::Node *node = create_point_node(pd);
viewer.setSceneData(node);
return viewer.run();
}
However, this problem crashes. I'm not sure whether I can do something like this.
I´m creating a template class in order to initialize a VBO and fill it with different kind of data, i.e. 2d vectors, 3d vectors, indices and so on.
Values are passed to the class constructor in a std:vector and then, as far as I know, all of them must be concatenated in a array of floats to push them into the VBO.
Lets say I have a 3d vector class like this:
class Vector3
{
public:
GLfloat* Values;
public:
Vector3(GLfloat x, GLfloat y, GLfloat z)
{
Values = new GLfloat[3];
Values[0] = x;
Values[1] = y;
Values[2] = z;
}
~Vector3() { delete Values}
static int Size() { return 3; };
...
};
And then the template class with this constructor
template <class T1> class VBO
{
public:
VBO(std::vector<T1*> v_array)
{
int s = T1::Size();
GLfloat *map = new GLfloat[v_array.size() * s];
int pos = -s;
for (std::vector<T1*>::iterator it = v_array.begin(); it != v_array.end(); ++it)
std::copy((*it)->Values, (*it)->Values + s, map + (pos += s));
...
}
};
Now I can use 'map' to push data in the VBO using glBufferData
My question is. Is there a better way to do this? Say, is it possible to push std::vector v_array directly in the VBO?
I'm using a class 'triangle' which is expressed as a vector of type 'vertex', 'vertex' being a structure consisting of an x and y value. I have a member function in 'triangle' that is supposed to return the area using heron's formula. Everything works fine until I try to output the area in the main function. Here is my code
vertex.h file
#ifndef VERTEX_H
#define VERTEX_H
#include <iostream>
struct vertex
{
double x, y;
vertex(double ix = 0.0, double iy = 0.0)
{
x = ix;
y = iy;
}
};
#endif // VERTEX_H
triangle.h file
#ifndef TRIANGLE_H
#define TRIANGLE_H
#include <iostream>
#include <vector>
#include "vertex.h"
class triangle
{
public:
triangle(vertex iv0 = vertex(), vertex iv1 = vertex(), vertex iv2 = vertex());
// pre:
// post: empty triangle
triangle(const triangle & source);
// pre:
// post: triangle created and initialized to given triangle source
vertex operator[](size_t i) const;
// pre: 0 <= i < 3
// post: return vertex i in this triangle
double area() const;
//pre:
//post: returns area of triangle
private:
std::vector<vertex> v;
};
std::ostream & operator << (std::ostream & os, const triangle & p);
std::istream & operator >> (std::istream & is, triangle & p);
#endif // TRIANGLE.H
triangle.cpp file
#include <cassert>
#include <vector>
#include <math.h>
#include "triangle.h"
triangle::triangle(vertex iv0, vertex iv1, vertex iv2) : v(3)
{
v[0] = iv0;
v[1] = iv1;
v[2] = iv2;
}
triangle::triangle(const triangle &p)
{
v = p.v;
}
vertex triangle::operator[] (std::size_t i) const
{
assert(i < v.size());
return v[i];
}
double triangle::area() const
{
double a, b, c;
double s;
a = sqrt(pow((v[0].x-v[1].x), 2)+pow((v[0].y-v[1].y), 2));
b = sqrt(pow((v[1].x-v[2].x), 2)+pow((v[1].y-v[2].y), 2));
c = sqrt(pow((v[2].x-v[0].x), 2)+pow((v[2].y-v[0].y), 2));
s = (a+b+c)/2;
return sqrt(s*(s-a)*(s-b)*(s-c));
}
//PROBLEM IS HERE^
//(used distance formula to find side lengths a, b, and c)
main function
#include <iostream>
#include "triangle.h"
using namespace std;
int main()
{
triangle t;
t[0] = vertex(2,3);
t[1] = vertex(5,4);
t[2] = vertex(3,7);
cout << t << endl;
cout << t.area() << endl;
cout << t.operator [](2) << endl;
return 0;
}
Since you are initialising your triangle using operator[], you need to make a non-const version of that function that returns a reference. Generally you return a const reference from the const version too, rather than by value:
const vertex& triangle::operator[] (std::size_t i) const
{
assert(i < v.size());
return v[i];
}
vertex& triangle::operator[] (std::size_t i)
{
assert(i < v.size());
return v[i];
}
Your compiler really shouldn't have let you get away with the code you posted. Modifying an rvalue should be an error, or at the very least a warning. Make sure you compile with warnings turned on, and read them!
The issue has to do with how you are initializing the triangle object. Try initializing it this way:
int main()
{
triangle t (vertex(2,3), vertex(5,4), vertex(3,7));
cout << t.area() << endl;
return 0;
}
Another less ideal solution would be to make "v" a public member and then assign the values this way:
triangle t;
t.v[0] = vertex(2,3);
t.v[1] = vertex(5,4);
t.v[2] = vertex(3,7);