Unspecified behaviour of std::vector with pointer to object - c++

I have found nerve wracking issue which I seem to be unable to solve.
SceneSettings::SceneSettings()
{
unsigned int w = (ConfigurationManager::GetInstance().GetWidth() >> 1) - 80;
unsigned int h = (ConfigurationManager::GetInstance().GetHeight() >> 1) - 50;
std::vector< std::string > menu_items;
menu_items.push_back("Graphic Settings");
menu_items.push_back("Sound Settings");
menu_items.push_back("Game Settings");
menu_items.push_back("Back");
Label* aux = NULL;
for ( unsigned int i = 0; i < menu_items.size(); i++ )
{
aux = new Label(menu_items[i], w, h);
items.push_back(aux);
aux = NULL;
aux = new Label(menu_items[i], w, h);
aux->SetColor(255, 0, 0);
hover_section.push_back(aux);
hover.push_back(false);
aux = NULL;
h += 25;
}
}
SceneSettings::~SceneSettings()
{
for (unsigned int i = 0; i < items.size(); i++)
{
delete items[i];
delete hover_section[i];
}
items.clear();
hover_section.clear();
}
void SceneSettings::Draw()
{
for ( unsigned int i = 0; i < items.size(); i++ )
{
if (hover[i])
hover_section[i]->Draw();
else
items[i]->Draw();
}
}
void SceneSettings::HandleEvents(SDL_Event& event)
{
switch(event.type)
{
case SDL_MOUSEMOTION :
{
int x = event.motion.x;
int y = event.motion.y;
for ( unsigned int i = 0; i < items.size(); i++ )
hover[i] = items[i]->GetIsInLabel(x, y);
} break;
}
}
Now what it does is, that first label "Graphic Settings" is not displayed. (not invisible, just plainly not visible)
items are defined as:
std::vector< Label* > items;
std::vector< Label* > hover_section;
std::vector< bool > hover;
For some reason it doesnt work, however in another scene ("main_menu") I have identical vector of labels (only captions are different) which works ok.
Now this one is more interesting that it seems. Since not only does it not display first item, but it displays 1st hover item even if mouse is completely out its range.
If I replace std::vector for direct Label* menu_item_1 in SceneSettings header, it displays correctly. Which leads me to think its connected to std::vector
One more thing I don't quite understand is, that if I run it in debug mode and going by steps, it displays correctly, however neither watches nor call stack does show anything unusual.
Any help is appreciated.

If you are passing SceneSettings objects by value anywhere, either as arguments to functions or returning such objects, then you definitely need to consider your use of pointers in the vectors, or have to implement a copy-constructor and a copy-assignment operator (as told by the rule of three).
The default functions created by the compiler will only do shallow copying, i.e. it copies the vectors and the pointers, it doesn't create new pointers (deep copying). That leads to you having two objects both containing pointers to the same objects in the vectors, and if one object is destructed if will free the memory, leaving the other object with stray pointers and that will lead to undefined behavior.

It turns out the problem was in Label class, in which I neglected to initialize texture and buffer ids for GLSL. Adding initial values (0) to them in Label constructor fixed the problem.

Related

Overflowing the stack memory with my quadtree class and don't know why

I have been working on a quadtree class for possible collision detections, but the more items I add the faster I overflow the stack.
I also noticed the memory usage increases linearly as the program runs so I assume I am not deleting something properly and it is just stacking on each other?
I know most stack overflow errors are caused by recursion but it shouldn't be happening with just 100 objects.
#include "Quadtree.h"
Quadtree::Quadtree(Rectangle* bounds)
{
this->bIsSplit = false;
this->bounds = bounds;
this->maxObjects = 5;
}
void Quadtree::Split(std::vector<std::pair<SpaceObject*, SpaceObject*>> &collidingObjects)
{
float x = bounds->x;
float y = bounds->y;
float subWidth = bounds->width / 2;
float subHeight = bounds->height / 2;
// Top Left
nodes.push_back(new Quadtree(new Rectangle(x, y, subWidth, subHeight)));
// Top Right
nodes.push_back(new Quadtree(new Rectangle(x + subWidth, y, subWidth, subHeight)));
// Bottom Left
nodes.push_back(new Quadtree(new Rectangle(x, y + subHeight, subWidth, subHeight)));
// Bottom Right
nodes.push_back(new Quadtree(new Rectangle(x + subWidth, y + subHeight, subWidth, subHeight)));
// takes all the objects in the parent node and splits them into there corresponding subdivided quadtree
for (auto o : this->objects)
{
for (int i = 0; i < nodes.size(); i++)
{
if (nodes[i]->bounds->Contains(o->x, o->y, o->size))
{
nodes[i]->Insert(o, collidingObjects);
}
}
}
this->objects.clear();
this->bIsSplit = true;
}
void Quadtree::Insert(SpaceObject* spaceObject, std::vector<std::pair<SpaceObject*, SpaceObject*>> &collidingObjects)
{
// if the object does not fit inside this quadtree, this isn't the right quadtree
if (!bounds->Contains(spaceObject->x, spaceObject->y, spaceObject->size))
{
return;
}
if (!bIsSplit && this->objects.size() < maxObjects) // add the object to the quadtree if the max has not been hit
{
objects.push_back(spaceObject);
for (auto o : this->objects)
{
// if the objects are not the same and overlapping (pythag)
if (spaceObject != o &&
((spaceObject->x - o->x) * (spaceObject->x - o->x)) + ((spaceObject->y - o->y) * (spaceObject->y - o->y)) <= (spaceObject->size + o->size) * (spaceObject->size + o->size))
{
// Add colliding pair to the collidingObjects vector
collidingObjects.push_back(std::make_pair(spaceObject, o));
}
}
}
else
{
if (!this->bIsSplit) // splits the quadtree if this quadtree has not been split yet
{
this->Split(collidingObjects);
}
// adds the passed in object to one of the subnodes
for (auto n : nodes)
{
n->Insert(spaceObject, collidingObjects);
}
}
}
void Quadtree::Delete(SpaceObject* spaceObject)
{
if (!bounds->Contains(spaceObject->x, spaceObject->y, spaceObject->size))
{
return;
}
if (!bIsSplit)
{
for (int i = 0; i < objects.size(); i++)
{
if (objects[i] == spaceObject)
{
this->objects.erase(objects.begin() + i);
return;
}
}
}
else
{
for (auto n : nodes)
{
n->Delete(spaceObject);
}
}
}
void Quadtree::Clear()
{
if (bIsSplit)
{
for (auto n : nodes)
{
n->Clear();
}
this->bIsSplit = false;
}
nodes.clear();
objects.clear();
}
Well, I can't tell what nodes is or how Quadtree is defined because you are not showing the complete code. But it's suspicious that you are using new. Is nodes a collection of pointers? If so, you are probably dropping them on the floor since they are never deleted.
Don't write this-> all over the place. Members are in the scope of the member functions. Your Quadtree constructor should be using inline initializers for the constant members and an initializer list for the bounds. And, using a bare pointer here is a red flag. What is the ownership of this bounds? Why does it need to be a pointer rather than simply a value of type Rectangle?
I wonder if you are used to a different language, one that uses new for all construction, and has reference semantics and objects are really pointers. C++ is different.
I'd suggest writing it without using any pointers. For sure, the Rectangle should be a value. A vector of Quadtree can be efficient when inserting and deleting etc. if the elements can be efficiently moved. So, Quadtree should have a move constructor. That is probably an advanced concept if you don't know all about constructors and special members yet.
What you should definitely follow, even as (especially as) a beginner, is ⧺C.149 — no naked new or delete. So, if you need to make a vector of pointers to Quadtree, make it a vector of shared_ptr. Remember, you don't have garbage collection in C++ so anything that's using a pointer has to explicitly deal with responsibility of lifetime management. A shared_ptr will behave more like you're used to, and they are safe for use in containers like vector.

passing several boost multi_array from function

I have a bunch of 3d arrays generated using boost::multi_array in a function. I would not want to use all these arrays in another code of mine is there any way to do this?
When I had a 2d case what I did was
typedef boost::numeric::ublas::matrix<double> fils;
boost::array<fils,5> filter1(unsigned width, unsigned height)
{
matrix<double>l,m,n,o,p;
//perform other steps//
boost::array<fils,5> t={l,m,n,o,p};
return t;
}
main.cpp
int main()
{
boost::array<fils,5> z;
z= t(w,h);
}
for the 2d case this method works fine. I now want to do the same with a 3D case where
typedef boost::multi_array<double,3>x;
boost::array<x,12>x1(unsigned w,unsigned h,unsigned s)
{
typedef boost::multi_array<double,3>M;
typedef M::index Mi;
m l(boost::extents[w][h][s]),m(boost::extents[w][h][s]),n(boost::extents[w][h][s]),o(boost::extents[w][h][s]);
//perform steps//
}
how do I get the matrices l,m,n,o,p so that I can use them as source in other bits of code.
In my opinion the most elegant solution is to declare a 4-D multi_array like so :
typedef boost::multi_array<double,4> FloatArray4D;
typedef M::index Mi;
function create4dArray()
{
FloatArray4D returnValue(boost::extents[w][h][s][4]);
// Populate the array as you please here is an example.
for (int i = 0; i < 4; i++) {
for (int j = 0; j < w; j++) {
for (int k = 0; k < h; k++) {
for (int x = 0; x < s; x++) {
returnValue[j][k][x][i] = i+j*10+k*100+x*1000;
}
}
}
}
return returnValue;
}
Then you can access the subarray by indexing on the last coordinate. It might be more efficient to index them by the first coordinate (in terms of localization of the data) but I don't know the implementation details of boost::multi_array (can someone weight in on this in comments ?)
To extract a view (no-copy) of your 3-D data from the 4-D multi_array created you can use this :
typedef boost::multi_array_types::index_range range;
FloatArray4D::index_gen indices;
FloatArray4D my4DArray = create4dArray();
// Create a new view with 3 dimentions (corresponding to your l) fixing the 4th dimention to 0
FloatArray4D::array_view<3>::type l = [indices[range()][range()][range()][0];
then you can use l as if it was your 3-D array.
PS: NEVER name something x or M, especially not a type. Yes long names are a pain to type, but get a decent text editor with auto-completion and it won't be a problem.
Knowing what an object is by its name however, will always be great. It improves readability, for you and for anyone else who has to read your code.
Also do not typedef inside a function. If you want to define a custom type do it in a header file that is shared.
You don't want to have to declare that type everywhere.
And actually don't overuse typedef, only use it if it improves readability.

OpenGL - drawing objects using text file

I have loaded an array from a text file, which contains positions of objects, and it looks like this:
0,0,0,5
0,5,0,0
0,0,5,0
0,5,5,0
The object looks like this:
struct object
{
int x, y, value;
}
Where x,y are coordinates, and value is 1 or 0 (it tells if an object was "picked", all objects have 1 at the beginning). Objects are stored in an array object obj_array[5].
To draw them, I use this function:
(BOARD_Y and BOARD_Y is size of the array, here is 4x4)
void draw_board(){
for (int iy = 0; iy < BOARD_Y; iy++) {
for (int ix = 0; ix < BOARD_X; ix++) {
if ( (board[iy][ix] == 5) )
{
glPushMatrix();
glTranslatef( ix, iy, 0 );
glutSolidCube(1);
glPopMatrix();
}
}
}
}
And it draws all of them perfectly. But I want to skip drawing an object, if its value is 0 (the object was picked by a player). How can I do this?
Okey, I can see what's going on; you've complicated the things a bit. There's no way to access the arbitrary object just from this loop, apart from pretty stupid comparison of position:
if ( (board[iy][ix] == 5) ) {
for (auto const& obj : objects) {
if (obj.x == ix && obj.y == iy) {
// obj is the object in given position
// ...
break;
}
}
}
Don't do that.
Instead either store some reference to the objects on the board. By reference I mean (not limited to!):
An unique object ID
Pointer to the object
Then, you will be able to access the object residing on given tile much faster and easier. If you want examples, drop a comment, but I think both options are fairly easy to implement.
If you still want to hold these "5" inside, change board to array of structs. And oh, please use std::array instead of int**.
Here's the example
using id_t = unsigned;
std::map<id_t, object> objects;
constexpr std::size_t size_x = 4, size_y = 4;
std::array<id_t, size_x * size_y> board;
Let's assume that id equal to 0 means that the object is not there.
Now you can access the specific object by:
unsigned x, y;
id_t obj_id = board[x + size_x * y];
if (obj_id != 0) // is there any object?
if (objects[obj_id].value != 0) // is its value equal to 0?
// ...
And set it by board[...] = obj;
Easy way to generate unique id for every object is just to increment the counter.
id_t last_id = 1;
objects[last_id++] = obj_id;

Dynamically allocating 2D int array

Can someone please point out what I am doing wrong in the following code?
int* a = NULL;
int* b = NULL;
a = new int[map->mapSize.width];
b = new int[map->mapSize.height];
layer->tileGids = new int[a][b];
Here's what the code uses:
typedef struct _size {
int width, height;
} size;
class Map {
size mapSize;
}
class Layer {
int * tileGids;
}
EDIT: Compiler-Errors (in line 6 of the first bit of code):
error: expression in new-declarator must have integral or enumeration type|
error: 'b' cannot appear in a constant-expression|
Solution:
I have decided to accept lightalchemist's answer. In essence, what works for me is use a vector instead of the array. Vector manages the memory for you and hence is a lot easier to deal with.
You can't pass a pointer for initializing the size of an array. Others have now mentioned this.
This post (it's not mine) seems like it might help you: http://eli.thegreenplace.net/2003/07/23/allocating-multi-dimensional-arrays-in-c/
You should also consider doing the allocation in the class Layer's constructor and then deleting the memory in it's destructor (i.e. RAII - resource acquisition is initialization). This is considered good style.
Finally, you might consider using continuous memory and a custom indexing scheme, which you could easily use Layer to encapsulate. This of course depends upon how big things will get. The bigger they get the better the case for continuous memory becomes.
This should give you a flavor.
#include <iostream>
#include <cstdlib>
int main()
{
const size_t ROWS = 5;
const size_t COLS = 2;
const size_t size = ROWS*COLS;
int* arr = new int[size];
int i = 0;
for ( size_t r = 0 ; r < ROWS; ++r )
{
for (size_t c = 0; c < COLS; ++c )
{
arr[r*COLS+c] = i++;
}
}
for ( int j = 0; j < i; ++j)
{
std::cout << arr[j] << std::endl;
}
delete [] arr;
}
Firstly, your variables "a" and "b" are pointers. Your code:
layer->tileGids = new int[a][b]
is the root cause of the problem.
I'm trying to guess your intention here and I think what you are trying to do is make layer.tileGids a 2 dimension array to reference a "grid" of size (mapSize.Width, mapSize.height) so that you can refer to each "cell" in the grid using layer.tileGids[x][y].
If you are indeed trying to create a 2 dimension array, there are 2 methods to do it.
Method 1:
class Layer {
int ** tileGids; // NOTE the "**" to indicate tileGids is a pointer to pointer i.e. 2D array.
}
To initialize it:
int width = map->mapSize.width;
int height = map->mapSize.height;
layer.tileGids = new int*[width]; // NOTE the "int*" to indicate tileGids is a new array of pointers to int.
for (int i = 0; i < width; i++) // Initialize each element in layer.tileGids[] to be a pointer to int.
{
layer.tileGids[i] = new int[height];
}
Now you can access the items in layer.tileGids using:
int value = layer.tileGids[x][y] // where 0 <= x < width and 0 <= y < height
To deallocate this data structure, similar to how you allocate it, you need to deallocate each dynamically allocated array in each "row":
for (int i = 0; i < width; i++)
{
delete [] layer.tileGids[i]; // Deallocate each row.
}
delete [] layer.tileGids; // Deallocate "array" to the pointers itself.
Method 2:
Now another easier, less messy method (avoid pointers) is to use the C++ vector class. You need to make the following changes:
#include <vector>
class Layer {
vector<vector<int> > tileGids; // Note the space at "<int> >".
}
To initialize:
int width = map->mapSize.width;
int height = map->mapSize.height;
layer.tileGids = vector<vector<int> >(width, vector<int>(height, 0)); // Initialize all entries to 0.
To access the elements:
int value = layer.tileGids[x][y]; // Where 0 <= x < width and 0 <= y < height
Note that for the second method using vectors, you do not have to do any memory cleanup as is required in the first method because the vector will automatically take care of it. However, because a vector can grow dynamically i.e. you can add items to it, you lose the safety of having a fixed size array i.e. someone could accidentally increase the size of your grid if you use the vector method but if he tries to do that when you intialized it using the first method above an error will occur and you will immediately know that something is wrong.
Can someone please point out what I am doing wrong in the following code?
A lot. You're allocating two single arrays (a "row array" and a "column array", not what you need), and then you try to do something strange.
Generally you can't (strictly speaking) dynamically allocate a 2D array in C++ (because the type system would still need the type, along with the dimensions, to be known at compile time). You can emulate it with an array of arrays or so, but the best way is to allocate an 1D array:
int width=5;
std::vector<int> tab(width*height);
and then access the element by calculating the coordinates manually:
// access the element (1,2)
tab[1 + 2*width] = 10;
This way you're essentially interpreting a 1D array as a 2D array (with performance equal to static 2D arrays).
Then it's best to wrap the indexing with a class for convenience; boost::multi_array also has this done for you already.
a and b are int* here:
layer->tileGids = new int[a][b];
Perhaps you meant to say this?
layer->tileGids = new int[*a][*b];

Trying to make an array of DirectX vertex with out knowing until run time what type they will be

Bit of background for those who don't know DirectX. A vertex is not just an XYZ position, it can have other data in it as well. DirectX uses a system known as Flexible Vertex Format, FVF, to let you define what format you want your vertexs to be in. You define these by passing a number to DirectX that use bitwise or to build it up, eg (D3DFVF_XYZ | D3DFVF_DIFFUSE)means you are going to start using (from when you tell DirectX) vertexs that have an XYZ (three floats) and a RGB components (DWORD / unsigned long).
In order to pass your vertexs to the graphics card, you basicaly lock the memory in the graphics card where your buffer is, and use memcpy to transfer your array over.
Your array is an array of a struct you deffine your self, so in this case you would have made a struct like...
struct CUSTOMVERTEX {
FLOAT X, Y, Z;
DWORD COLOR;
};
You then make an array of type CUSTOMVERTEX and fill in the data fields.
I think my best appraoch is let my class build up an array of each component type, so an array of struct pos{ flaot x,y,z;}; an array of struct colour{ DWROD colour;}; etc.
But I will then need to merge these together so that I have an array structs like CUSTOMVERTEX.
Now, I think I have made a function that will merge to arrays together, but I am not sure if it is going to work as intended, here it is (currently missing the abilaty to actually return this 'interlaced' array)
void Utils::MergeArrays(char *ArrayA, char *ArrayB, int elementSizeA, int elementSizeB, int numElements)
{
char *packedElements = (char*)malloc(numElements* (elementSizeA, elementSizeB));
char *nextElement = packedElements;
for(int i = 0; i < numElements; ++i)
{
memcpy(nextElement, (void*)ArrayA[i], elementSizeA);
nextElement += elementSizeA;
memcpy(nextElement, (void*)ArrayB[i], elementSizeB);
nextElement += elementSizeB;
}
}
when calling this function, you will pass in the two arrays you want merged, and size of the elements in each array and the number of elements in your array.
I was asking about this in chat for a while whilst SO was down. A few things to say.
I am dealing with fairly small data sets, like 100 tops, and this (in theory) is more of an initialisation task, so should only get done once, so a bit of time is ok by me.
My final array that I want to be able to use memcpy on to transfer into the graphics card needs to have no padding, it has to be contiguous data.
EDIT The combined array of vertex data will be transfered to the GPU, this is first done by requesting the GPU to set a void* to the start of the memory I have access to and requesting space the size of my customVertex * NumOfVertex. So if my mergeArray function does loose what the types are within it, that is ok, just a long as I get my single combined array to transfer in one block /EDIT
Finally, their is a dam good chance I am barking up the wrong tree with this, so their may well be a much simpler way to just not have this problem in the first place, but part of me has dug my heals in and wants to get this system working, so I would appreciate knowing how to get such a system to work (the interlacing arrays thing)
Thank you so much... I need to sooth my head now, so I look forward to hearing any ideas on the problem.
No, no, no. The FVF system has been deprecated for years and isn't even available in D3D10 or later. D3D9 uses the VertexElement system. Sample code:
D3DVERTEXELEMENT9 VertexColElements[] =
{
{0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
{0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
D3DDECL_END(),
};
The FVF system has a number of fundamental flaws - for example, which order the bytes go in.
On top of that, if you want to make a runtime-variant vertex data format, then you will need to write a shader for every possible variant that you may want to have, and compile them all, and spend your life swapping them around. And, the effects on the final product would be insane - for example, how could you possibly write a competitive rendering engine if you decide to take out the lighting data you need to Phong shade?
The reality is that a runtime-variant vertex format is more than a tad insane.
However, I guess I'd better lend a hand. What you really need is a polymorphic function object and some plain memory- D3D takes void*s or somesuch so that's not a big deal. When you call the function object, it adds to the FVF declaration and copies data into the memory.
class FunctionBase {
public:
virtual ~FunctionBase() {}
virtual void Call(std::vector<std::vector<char>>& vertices, std::vector<D3DVERTEXELEMENT9>& vertexdecl, int& offset) = 0;
};
// Example implementation
class Position : public FunctionBase {
virtual void Call(std::vector<std::vector<char>>& vertices, std::vector<D3DVERTEXELEMENT9>& vertexdecl, int& offset) {
std::for_each(vertices.begin(), vertices.end(), [&](std::vector<char>& data) {
float x[3] = {0};
char* ptr = (char*)x;
for(int i = 0; i < sizeof(x); i++) {
data.push_back(ptr[i]);
}
}
vertexdecl.push_back({0, offset, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0});
offset += sizeof(x);
}
};
std::vector<std::vector<char>> vertices;
std::vector<D3DVERTEXELEMENT9> vertexdecl;
vertices.resize(vertex_count);
std::vector<std::shared_ptr<FunctionBase>> functions;
// add to functions here
int offset = 0;
std::for_each(functions.begin(), functions.end(), [&](std::shared_ptr<FunctionBase>& ref) {
ref->Call(vertices, vertexdecl, offset);
});
vertexdecl.push_back(D3DDECL_END());
Excuse my use of lambdas, I use a C++0x compiler.
Your solution looks fine. But if you want something a bit more C++ish, you could try something like this:
Edit My previous solution basically recreated something that already existed, std::pair. I don't know what I was thinking, here's the even more C++ish solution:
template<typename InIt_A, typename InIt_B, typename OutIt>
void MergeArrays(InIt_A ia, InIt_B ib, OutIt out, std::size_t size)
{
for(std::size_t i=0; i<size; i++)
{
*out = make_pair(*ia,*ib);
++out;
++ia;
++ib;
}
}
int main()
{
pos p[100];
color c[100];
typedef pair<pos,color> CustomVertex;
CustomVertex cv[100];
MergeArrays(p,c,cv,100);
}
You shouldn't have to worry about padding, because all elements in a D3D vertex are either 32 bit floats, or 32 bit integers.
Edit
Here's a solution that might work. It will do all your mergings at once, and you don't need to worry about passing around the size:
// declare a different struct for each possible vertex element
struct Position { FLOAT x,y,z; };
struct Normal { FLOAT x,y,z; };
struct Diffuse { BYTE a,r,g,b; };
struct TextureCoordinates { FLOAT u,v; };
// etc...
// I'm not all too sure about all the different elements you can have in a vertex
// But you would want a parameter for each one in this function. Any element that
// you didn't use, you would just pass in a null pointer. Since it's properly
// typed, you won't be able to pass in an array of the wrong type without casting.
std::vector<char> MergeArrays(Position * ppos, Normal * pnorm, Diffuse * pdif, TextureCoordinates * ptex, int size)
{
int element_size = 0;
if(ppos) element_size += sizeof(Position);
if(pnorm) element_size += sizeof(Normal);
if(pdif) element_size += sizeof(Diffuse);
if(ptex) element_size += sizeof(TextureCoordinates);
vector<char> packed(element_size * size);
vector<char>::iterator it = packed.begin();
while(it != packed.end())
{
if(ppos)
{
it = std::copy_n(reinterpret_cast<char*>(ppos), sizeof(Position), it);
ppos++;
}
if(pnorm)
{
it = std::copy_n(reinterpret_cast<char*>(pnorm), sizeof(Normal), it);
pnorm++;
}
if(pdif)
{
it = std::copy_n(reinterpret_cast<char*>(pdif), sizeof(Diffuse), it);
pdif++;
}
if(ptex)
{
it = std::copy_n(reinterpret_cast<char*>(ptex), sizeof(TextureCoordinates), it);
ptex++;
}
}
return packed;
}
// Testing it out. We'll create an array of 10 each of some of the elements.
// We'll use Position, Normal, and Texture Coordinates. We'll pass in a NULL
// for Diffuse.
int main()
{
Position p[10];
Normal n[10];
TextureCoordinates tc[10];
// Fill in the arrays with dummy data that we can easily read. In this
// case, what we'll do is cast each array to a char*, and fill in each
// successive element with an incrementing value.
for(int i=0; i<10*sizeof(Position); i++)
{
reinterpret_cast<char*>(p)[i] = i;
}
for(int i=0; i<10*sizeof(Normal); i++)
{
reinterpret_cast<char*>(n)[i] = i;
}
for(int i=0; i<10*sizeof(TextureCoordinates); i++)
{
reinterpret_cast<char*>(tc)[i] = i;
}
vector<char> v = MergeArrays(p,n,NULL,tc,10);
// Output the vector. It should be interlaced:
// Position-Normal-TexCoordinates-Position-Normal-TexCoordinates-etc...
for_each(v.begin(), v.end(),
[](const char & c) { cout << (int)c << endl; });
cout << endl;
}
Altering your code, this should do it:
void* Utils::MergeArrays(char *ArrayA, char *ArrayB, int elementSizeA, int elementSizeB, int numElements)
{
char *packedElements = (char*)malloc(numElements* (elementSizeA + elementSizeB));
char *nextElement = packedElements;
for(int i = 0; i < numElements; ++i)
{
memcpy(nextElement, ArrayA + i*elementSizeA, elementSizeA);
nextElement += elementSizeA;
memcpy(nextElement, ArrayB + i*elementSizeB, elementSizeB);
nextElement += elementSizeB;
}
return packedElements;
}
Note that you probably want some code that merges all the attributes at once, rather than 2 at a time (think position+normal+texture coordinate+color+...). Also note that you can do that merging at the time you fill out your vertex buffer, so that you don't ever need to allocate packedElements.
Something like:
//pass the Locked buffer in as destArray
void Utils::MergeArrays(char* destArray, char **Arrays, int* elementSizes, int numArrays, int numElements)
{
char* nextElement = destArray;
for(int i = 0; i < numElements; ++i)
{
for (int array=0; array<numArrays; ++array)
{
int elementSize = elementSizes[array];
memcpy(nextElement, Arrays[array] + i*elementSize, elementSize);
nextElement += elementSize;
}
}
}
I don't know DirectX, but the exact same sort of concept exists in OpenGL, and in OpenGL you can specify the location and stride of each vertex attribute. You can have alternating attributes (like your first struct) or you scan store them in different blocks. In OpenGL you use glVertexPointer to set these things up. Considering that DirectX is ultimately running on the same hardware underneath, I suspect there's some way to do the same thing in DirectX, but I don't know what it is.
Some Googling with DirectX and glVertexPointer as keywords turns up SetFVF and SetVertexDeclaration
MSDN on SetFVF, gamedev discussion comparing them