Flat array from an array of structs?
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...
精彩评论