Hey guys. Thanks for clicking.
This is a problem that I'm encountering while coding OpenGL, but it's a pretty general problem overall - so nothing graphics specific.
I have a struct (not a class, just a simply struct), Particle.
typedef struct
{
float x;
float y;
float z;
}float3;
typedef struct
{
float3 position;
float3 velocity;
//...other stuff
}Particle;
And I am working with a bunch of particles (Particle* particles[]), but I have a function that requires a float* of positions packed in an x, y, z order.
Thus a summary of my problem:
My data:
//I have this in a bunch of encapsulated structs
[... {1.0f, 2.0f, 3.0f,} ... {4.0f, 5.0f, 6.0f} ...]
//I want...
[1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f]
My problem is...I have all the data there already! I don't want to have to malloc/memcpy around again. Is there a way to use the data that is already there? Any C pointer acrobatics? I am also worrying about things like alignment/padding.
(float3 is a struct defined in CUDA, if anyone is curious).
glVertexAttribPointer has a stride parameter that is designed for just this situation.
Typically you will load an array of Particle objects into a VBO, and then, with the VBO bound:
glVertexAttribPointer(shader_arg_position, 3, GL_FLOAT, GL_FALSE, sizeof (Particle), offsetof(Particle, position));
My solution is more C oriented.
The thing with pointers,you can use them to walk freely from one memory address to another with the idea "don't care what data is there". Combine that with the fact that when you allocate structs they are aligned in the order they are declared and you have yourself an easy solution to access your data without too much hassle.
Just make a float* index to point at the beginning of your vector structure where you hold all the points. Using index now you can traverse it how you please, however be careful where you stop with the pointer movement.
To explain a bit:
struct {
float3 position;
float3 velocity;
float3 more_data;
} Particle;
When you allocate this structure the memory will look like this:
3 floats for position || 3 floats for velocity || 3 floats for whatever data
Take a float* at the address position.x and increment it through you particles taking in consideration what data you want to process (position, velocity, etc).
Concerning alignment, it depends what alignment do you want your structure to have.
What about a reinterpret cast and a lot of care here?
Particle* pP[];
// Fill your array of particles
// And now at your own risk (memory accesses and so on... :)
float* pF = reinterpret_cast<float*>(&pP[0]);
float x = pF[0];
float y = pF[1];
float z = pF[2];
pF = reinterpret_cast<float*>(&pP[1]);
// ..
If you have your Particle* array, but you want to work with it as if it were an array of float positions, you could write something like this:
float getNthFloat(size_t n)
{
size_t i = n / 3;
size_t j = n % 3;
float* pF = reinterpret_cast<float*>(&pP[i]);
return pF[j];
}
// This would get 6th element in your virtual float array
// That is, second z position
size_t foo = 5;
float blah = getNthFloat(5);
And going one step further; you could probably rewrite this so it actually looks like accessing an array instead of calling a function.
The ideal solution is to fire whoever designed float3 and replace this structure with simple arrays across the board.
If you can't do that, you can try simply casting the pointer, but you might find your compiler refuses to generate working code since this is a violation of the aliasing rules.
And one more solution:
typedef struct {
float elem[3];
} float3;
#define x elem[0]
#define y elem[1]
#define z elem[2]
Unfortunately the names x, y, and z could be problematic to define as macros like this. That's one reason many classic C structures use prefixed names for struct elements, like st_dev, tv_sec, si_uid, etc...
Related
I am having a problem with vectors in c++. I am new to c++ so keep this in mind please.
So I have the following struct in the begining of my program:
typedef struct grupo
{
float transX, transY, transZ, rotX, rotY, rotZ, rotAngle, scaleX, scaleY, scaleZ;
char **modelos;
struct grupo** grupos;
int nNomes = 0, nGrupos=0;
std::vector<float> vertices;
};
struct grupo *grupo;
And I just start the main like this:
grupo = (struct grupo*)malloc(sizeof(struct grupo));
grupo->modelos = (char **)malloc(sizeof(1));
grupo->grupos = (struct grupo **)malloc(sizeof(struct grupo));
And in the middle of main I am tryting to use this:
grupo->vertices.push_back(x);
But it keeps crashing the program. I am using visual studio, and using debugging mode. I can see that is when pushing X to the vector that crashes (x has a value). I've tried to change the vector to an array of floats. So the problem i can imagine is with the initialization of the vector... Can you help with what am I missing?
Thank you :)
You're using C++, you don't need the "new" or malloc keyword unless for specific reasons. You don't need the typedef for structs.
I'm not sure what your char** is supposed to be, but you can use std::string for strings.
Maybe what you're trying to do is this:
struct Grupo
{
float transX, transY, transZ, rotX, rotY, rotZ, rotAngle, scaleX, scaleY, scaleZ;
std::string modelos;
int nNomes = 0, nGrupos=0;
std::vector<float> vertices;
};
In main:
Grupo grupo;
grupo.vertices.push_back( ... );
Then I would advise you to read a bit more about what is exactly the C++ language and how it's not C.
Since you're using C++; if you want to create a grupo dynamically, you should use the new operator, with the constructor:
grupo = new grupo();
malloc does not properly initialize C++ objects such as std::vector.
P.S. I am not sure what the grupo->modelos and grupo->grupos are supposed to be, but I'd use proper C++ types for them (perhaps modelos should be std::string, etc). Additionally, I suspect that you've got one * too much for both modelos and grupos.
C++ does not need the typedef in the declaration.
To properly initialize a structure, you should write a ctor to replace the compiler provided ctor (which generally does nothing).
Something like the following (with just a few attributes):
struct grupo
{
float transX, transY;
// ...
int nNomes;
int nGrupos;
std::vector<float> vertices;
// I prefer initialization list form
grupo() : transX(0.0),
transY(1.0),
// ...
nNomes(0),
nGrupos(0)
// vertices default ctor is ok, creates empty vector
{
// use vertices.push_back(...); to fill vertices
}
};
grupo grupo;
Next you will want to write a more useful ctor, one with parameters to use (instead of the literal constants), such that you could build multiple grupo.
grupo grupo1(1.0, 2.0, 3, 4);
grupo grupo2(3.0, 4.0, 5, 6);
// etc.
I have a class which is supposed to keep pixel data (floats for the position, floats for the color). I'm trying to use a C++ style in data members (the data is kept in std::array<float, N> instead of plain C arrays). The class has other getters, setters and functions meant to be "helpers" to populate these fields.
Now I need to create an OpenGL vertex data buffer where I should write out
4 floats for xyzw
4 floats for rgba
2 floats for UV coords
in this order. I'm wondering how should I do this.. I tried doing
class MyVertexData {
std::array<float, 4> pos;
std::array<float, 4> rgba;
std::array<float, 2> uv;
public:
void writeData(float *ptrToMemory) {
if(ptrToMemory == nullptr)
throw std::runtime_exception("Null pointer");
std::array<float, 10> output;
output= {
pos[0], pos[1], pos[2], pos[3],
rgba[0], rgba[1], rgba[2], rgba[3],
uv[0], uv[1]
};
memcpy(memory, out.data(), 10 * sizeof(float));
}
};
// Caller code
std::vector<float[10]> buffer(4);
vertex0.writeElements(buffer[0]);
vertex1.writeElements(buffer[1]);
vertex2.writeElements(buffer[2]);
vertex3.writeElements(buffer[3]);
but this approach has two problems:
I need to trust the caller to have allocated memory to store 10 floats
No C++11+ signature, I just get a float pointer
I can't just return a std::unique_ptr since I need a contiguous memory area (buffer) where the elements are to be stored, but I also need a distinction between the different elements (that would also make the code more readable).
It would be nice to return a smart pointer or something similar whose memory I can easily "concatenate" to other elements so I can safely pass this stuff to OpenGL.
CppCoreGuidelines introduces span which is a view of contiguous element, so you may use something like:
void writeData(gsl::span<float, 10> ptrToMemory)
to express the intend.
What data GLSL takes? For example I have a matrix 4x4, which is following kind:
float **matrix = new float*[4];
for(int i = 0; i < 4; i++)
matrix[i] = new matrix[4];
Can the GLSL to take this, as mat4x4?
Or, better to use following:
float *matrix = new float[16];
I haven't found this information in specification of GLSL 1.30(I have use particularly this version)
First of all you should make use of GLfloat instead that float, since it's there exactly to represent GL data which is used by your program and the GPU.
Regarding your specific question glUniform has many flavours used to send what you need. You have both
void glUniformMatrix2f(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix4f(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
You can use them easily by passing the pointer to your data:
GLint location = glGetUniformLocation(variable,"variable_name");
glUniformMatrix4f(location, 1, false, &matrix);
You can't use the first variant, because the 16 floats of the matrix data will be allocated in different memory areas. You store array of 4 pointers, each pointing to separate 4xfloat array. OpenGL expects data to be located sequentially, so either use the second variant or use struct/static 2d array:
GLfloat[4][4]
It may be convenient to use the existing library for that, fore example gl matrix
What data GLSL takes? For example I have a matrix 4x4, which is following kind:
float **matrix = new float*[4];
for(int i = 0; i < 4; i++)
matrix[i] = new matrix[4];
That's not a 4×4 matrix. That's an array of pointers to arrays of 4 float elements.
Can the GLSL to take this, as mat4x4?
No, because it's not a matrix.
A 4×4 matrix would be a region of contiguous memory that contains 4·4 = 16 values (floats if you will) of which you denote, that each n-tuple (n=4) of values forms a vector and the m-tuple (m=4) of vectors forms a matrix.
Or, better to use following:
float *matrix = new float[16];
That would be a contiguous region of memory holding 16 = 4·4 float values, which by denotion can be interpreted as a 4×4 matrix of floats. So yes, this can be interpreted by OpenGL / GLSL as a 4×4 matrix of floats mat4.
However if you make this part of some class don't use dynamic memory.
class foo {
foo() { matrix = new float[16]; }
float *matrix;
};
Is bad, because you create some unnecessary overhead. If the class is dynamically allocated (with new) that will trigger another dynamic memory allocation, which creates overhead. If the instances are on automatic memory (stack, i.e. no new), it also imposes unnecessary overhead.
class foo {
foo() { … }
float matrix[16];
};
is much better, because if the class instances are created with new that also covers the memory for the matrix. And if it's on automatic memory it completely avoids the overhead of dynamic allocation.
When I declare an array of structs explicitly my transform feedback loop works perfectly.
struct FeedBackInfo
{
float area;
float Pabs;
float Pref;
}
FeedBackInfo FBI[36];
....
....
glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0,sizeof(FBI), FBI);
However, I'd like to not limit myself to the 36 point box that I've created for my sample program and be able to scale up depending on the model loaded. But when I try to switch to a vector object that can be dynamically allocated I am unable to get the transform feedback to work.
struct FeedBackInfo
{
float area;
float Pabs;
float Pref;
};
std::vector<FeedBackInfo> FBI;
FBI.resize(36);
....
....
This will fail outright
glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0,sizeof(FBI), &FBI);
This will only fill in a few of the values in an offset manor
glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0,sizeof(FBI), &FBI[0]);
I cannot even itterate 0 through 35 and fill in all of the data, only the areas are being filled in.
I do not understand why an explicit declaration of the array of structs will work smoothly and why transforming it into a vector object suddenly doesn't work.
Is there a way to dynamically allocate the struct of FeedBackInfo and still work smoothly with the transform feedback loop?
std::vector<FeedBackInfo> FBI;
FBI.resize(36);
Your sizeof(FBI); wont be the size of the memory you want.
If you want to have the size of the memory (of the data, not of the vector) you need to use FBI.size()*sizeof(FeedBackInfo)
glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, FBI.size()*sizeof(FeedBackInfo), FBI.data());
I replaced your &FBI[0] with FBI.data() because I think it's better to read..
I'm using Eigen3 2-dimensional vector as 2D point for opengl drawing, storing them in a list:
typedef Eigen::Vector2d Vec2D;
std::list<Vec2D> points;
Now, I need an array of GLfloat to pass the entire data structure of raw float coordinate value to the graphic card:
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
_vertex = new GLfloat[points.size()*2];
_colors = new GLfloat[points.size()*4];
std::list<Vec2D>::const_iterator it;
int i=0, j=0;
for(it=points.begin(); it!=points.end(); ++it) {
_vertex[i] = it->x()+2;
_vertex[i+1] = it->y()+2;
i+=2;
_colors[j] = getRed(j/4.0f, it);
_colors[j+1] = getGreen(j/4.0f, it);
_colors[j+2] = getBlue(j/4.0f, it);
_colors[j+3] = getAlpha(j/4.0f, it);
j+=4;
}
glColorPointer(4, GL_FLOAT, 0, _colors);
glVertexPointer(2, GL_FLOAT, 0, _vertex);
glDrawArrays(GL_LINE_STRIP, 0, points.size());
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
delete _vertex;
delete _colors;
Is there a more efficent way for creating the arrays to pass to the graphic cards? like pass points.begin() and find out what is the offset and avoid to loop through all the points?
I mean.. in the memory the x and y coordinates of the Eigen::Vector2d has to be stored in some consecutive space.. so.. I think I can pass it directly to the graphic card.. but I can't unserstand how.
std::list does not hold it's data in contiguous memory, you need std::vector for that(or std::array if you know the size at compile time, but you probably don't). Vector has a method data() which returns a pointer to underlying data. However if you store internally Eigen::vec2d you can't pass it to openGl, since it's a dynamic structure and your data will be all over your memory. You need structure that keeps data in place(and btw is more readable than vec2d, which is kinda odd in this context). For example:
struct VertexData
{
GLfloat x;
GLfloat y;
GLfloat red;
GLfloat green;
GLfloat blue;
GLfloat alpha;
}
And then use glVertexPointer to pass it to openGL using sizeof(VertexData) as stride
To delete new'ed arrays you need to use
delete [] _vertex;
normal delete will only free the first element. Or even better, you could use smart pointers, std::unique_ptr would be best in this case
std::unique_ptr<GLfloat[]> _vertex(new GLfloat[points.size() * sizeof(GLfloat)]);
It will automatically free the memory when it goes out of scope(at the end of the block)
points is a std::list so it doesn't contain an contiguous array of data.
If you use std::vector instead however, then in C++11 you can access the array used internally with points.data(). This means you don't need _vertex anymore.
Know that, as an alternative, you can even go further and have a class (named for instance Vertex) which contains both the vertex position and its color and then use the data() method on your std::vector<Vertex> in combination with glInterleavedArrays (with stride=sizeof(Vertex)).