Create struct for vertex buffer on DX9 - c++

I have a huge problem in my DirectX project.
I need to make struct for vertexbuffer..
CUSTOMVERTEX vertices[] =
{
{ 2.5f, -3.0f, 0.0f, D3DCOLOR_XRGB(0, 0, 255), },
{ 0.0f, 3.0f, 0.0f, D3DCOLOR_XRGB(0, 255, 0), },
{ -2.5f, -3.0f, 0.0f, D3DCOLOR_XRGB(255, 0, 0), },
};
But how do I do that in a loop?
I need to make huge field of triangles(3d terrain). So,
I tried something like this:
void Field_Generation::Field_Generate(int X, int Y, int Z)
{
int zeme[X][Y];
int n=0;
for (int i=0;i < X;i++)
{
for (int j=0;i < Y;j++)
{
zeme[i][j] = rand() % Z;
}
}
for (int i=0;i < X;i++)
{
for (int j=0;i < Y;j++)
{
int color1= rand() % 255;
int color2= rand() % 255;
int color3= rand() % 255;
n++;
CUSTOMVERTEX verticles[n] = {{i,j,zeme[i][j],D3DCOLOR_XRGB(color1,color2,color3),}};
}
}
}
But this ain't working for me. :( I can't figure out how to do that. Help please. Thank you.

There are a number of problems with the code you posted.
First of all, this won't work:
int zeme[X][Y];
You can't create a dynamic array this way. Although there are various ways to do it, I would recommend using std::vector so at the top of your source make sure you include the right header:
#include <vector>
using namespace std;
Then you can define your zeme like this:
vector<vector<int> > zeme(X, vector<int>(Y, 0));
Now you can access your elements using zeme[x][y] as you do already.
Next you need to create an array to hold your custom vertex data, and again you can use a std::vector. This should be defined as a class member or a global variable rather than being defined inside your function. Something like:
std::vector<CUSTOMVERTEX> m_pVertices;
Then, inside your loop, initialise each entry like this:
CUSTOMVERTEX pVertex = {i, j, zeme[i][j], D3DCOLOR_XRGB(color1,color2,color3) };
m_pVertices.push_back(pVertex);
Later, when you need to pass a pointer to your array of vertices to DirectX for drawing, use this syntax to get a pointer to the start of the data:
CUSTOMVERTEX *pData = &m_pVertices[0];
(Note that in C++11 there are alternative ways of handling all of the above)
Another alternative, if you already have an array of CUSTOMVERTEX is to initialise each component inside the loop. So, assuming your CUSTOMVERTEX has a structure like this (I'm guessing because you don't show it):
struct CUSTOMVERTEX
{
float x, y, z;
D3DCOLOR nCol;
};
Then, inside your loop, you could initialise each entry like this (you need to use whatever names are defined in your structure):
verticles[n].x = i;
verticles[n].y = j;
verticles[n].z = zeme[i][j];
verticles[n].nCol = D3DCOLOR_XRGB(color1,color2,color3);
So, you assign each value to a member of the struct rather than trying to assign the whole struct in one hit like I did in the first example.

You can set the value of a struct by setting each of it's component, like this.
struct Person
{
int age;
string name;
};
Person person[2];
person[0].age = 20;
person[0].name = "zdd";
person[1].age = 30;
person[1].name = "ddz";

Related

How to check whether an index in an array is empty

I'm making a small OpenGL program for my intro to C++ class in Uni. I have a program that is complete but I want to change it up a bit to make it more unique. I have a Cube class:
class Cube {
public:
Cube(Mesh* mesh, Texture2D* texture, float x, float y, float z);
~Cube();
void Draw();
void Update(float rSpeed);
Vector3 position;
private:
GLfloat rotationSpeed;
Vector3 rotationVector;
Mesh* _mesh;
Texture2D* _texture;
};
I then create an array of type Cube:
Cube* cubes[CUBE_AMOUNT];
I then fill each index of this array with data to draw the cube on screen later in the program:
for (int i = 0; i < CUBE_AMOUNT; i++) {
float x = ((rand() % 400) / 10.0f) - 20.0f;
float y = ((rand() % 200) / 10.0f) - 10.0f;
float z = -(rand() % 1000);
if (i % 2 == 1) {
cubes[i] = new Cube(cubeMesh, textureStars, x, y, z);
}
else {
cubes[i] = new Cube(cubeMesh, texturePenguins, x, y, z);
}
}
With this new thing I want to add to the program, I want to check whether an index of cubes[] has been filled with the data yet. However I keep getting exceptions when running. I have tried to check whether cubes[i] is equal to nullptr, and tried checking whether it is NULL too, but neither seem to match.
Sorry for any errors in terminology that I used. New to C++, and having come from only doing Python before this, it is confusing!
Solution:
When I create the array, I changed it to Cube* cubes[CUBE_AMOUNT] = { NULL }, and now when checking the array, cubes[i] == NULL!
If cubes is not a global variable, you can use:
Cube* cubes[CUBE_AMOUNT] = {};
to initialize all the elements to nullptr.
You can also use:
std::vector<std::unique_ptr<Cube>> cubes(CUBE_AMOUNT);
to remove the burden of having to deallocate dynamic memory in your code.
In either case, can use:
if ( cubes[index] )
{
// Got a valid pointer. Use it.
}
Your cubes variable is not automatically initialized with null_ptr's. Until you either fill it with null_ptr's or good pointers it initially points to random garbage.
I think this would work
//This bit should check if theres anything stored currently.
cout << "\nWhich Slot would you like to store the informaton in ?(1-10)";
cin >> i;
i--;
if (information[i] != NULL){
// Already written
cout << "THERES SOMETHING HERE";
}
else{
cout << "\nEMPTY!!!!!!!!!";
}

How to add a for loop in a DirectX 10 project?

Before I ask for help, I would like to mention that I am very new to DirectX and yes, I do know how to code in C++. But I'm getting errors when I try to complete my homework.
The homework assignment is simple. Draw a circle (using a minimum of 20 triangles) and put a texture on the circle. No problem. But I have to create the vertices using a For loop. This is what I have so far:
double x = 0.1;
double y = 1.0;
double z = 0.5;
double xin = 0.3;
double yin = -0.1;
// Create vertex buffer
SimpleVertex vertices[] =
{
for (int i = 0; i < 4; i++) {
XMFLOAT3(0.0f, 0.0f, zf),
XMFLOAT3(xf, yf, zf),
XMFLOAT3((x+xin)f, (y+yin)f, zf),
}
};
I haven't finished adding all the code I want to make the circle. It's more of a test run. But I get an error on my for loop. It doesn't seem to want to read the for, expected an expression. I tried putting the For loop outside of SimpleVertex and it works.
How would I add a for loop to make my circle?
Thanks for helping the noob.
I would wait for office hours, but it's Labor day and I've been at it all weekend.
You have your for loop in a declaration, which you cannot do. You need to declare vertices first, then initialise it with for loop:
int main()
{
// Declare your vertices:
const auto maxVertices = 20;
SimpleVertex vertices[maxVertices];
// Initialise the vertices in a for loop
for (int i = 0; i < maxVertices; ++i)
{
vertices[i].Position = /* calculate position */
}
}
It's likely you'll use i to calculate one of the positions, something like:
vertices[i].Position = { i * x, y, z };

Deletion of std::list causing Access violation

For a school project, my group is using OpenCV to capture video. From these (top-down) images, positions of objects are extracted and turned into a list of Points. Those Points then get triangulated using http://code.google.com/p/poly2tri/ (to overcome the problem of possible non-convex objects). Then, using the coordinates of the triangulated ground pane, we draw the objects in 3D using freeglut. (Side and Top panes are calculated using the ground pane coordinates). The problem we have is that when we delete our old list of Points, the application randomly crashes. Sometimes after 1 second, sometimes after 30 seconds, sometimes after a few minutes. The error we get is "Access violation writing location 0xCCCCCCCC"
Our code:
void WorldLayoutBuilder::update()
{
pointList.clear();
// Capture image
<code to capture image and get countours>
for(size_t i = 0; i < contours.size(); i++)
{
if(contours[i].size() > 50)
{
approxPolyDP(contours[i], approxShape, cv::arcLength(cv::Mat(contours[i]), true)*0.04, true);
drawContours(drawing, contours, i, cv::Scalar(255, 0, 0), 0);
std::vector<Point> newObject;
for(size_t j = 0; j < contours[i].size(); j++)
{
cv::Point newPoint = contours[i][j];
newObject.push_back(Point((float) newPoint.x / 100, 0.0f,(float) newPoint.y / 100));
}
pointList.push_back(newObject);
}
}
ObjectCreator3D::createObjects(&pointList);
contours.clear();
<code to release images, etc>
}
This captures an image, retrieves coordinates of objects, and then calls ObjectCreator3D::createObjects():
void ObjectCreator3D::createObjects(std::list<std::vector<Point>>* inputList)
{
std::list<WorldObject>* tempObjects = new std::list<WorldObject>;
for(std::vector<Point>&pointObject : *inputList)
{
WorldObject worldObject(&pointObject);
tempObjects->push_back(worldObject);
}
DataStorage::getInstance()->setObjects(tempObjects);
}
All objects are turned into WorldObjects:
#include <list>
#include <iostream>
#include <GL/glut.h>
#include <GL/freeglut.h>
#include <time.h>
#include "WorldObject.h"
#include "Point.h"
//Constant height - adjustable/randomized solution is partially implemented in the constructor.
const float WorldObject::HEIGHT = 5.0f;
template <class C> void FreeClear(C & cntr)
{
for(typename C::iterator it = cntr.begin(); it != cntr.end(); ++it)
{
delete * it;
}
cntr.clear();
}
WorldObject::WorldObject(std::vector<Point>* pointList)
{
//TODO, when we have time. Seems difficult because height will change each update...
/*srand (time(NULL));
float fGeneratedY = (rand() % 20 + 2) / 2.0f;*/
cdt = nullptr;
for (Point &point : *pointList)
//point.setY(fGeneratedY);
point.setY(HEIGHT);
this->pointList = pointList;
}
WorldObject::~WorldObject()
{
//Cleanup
delete cdt;
FreeClear(polyPoints);
}
/*
Author Tim Cocu & Bas Rops
Function for drawing the WorldObject
*/
void WorldObject::draw()
{
glPushMatrix();
glColor3f(0.8f, 0.8f, 0.8f);
//Calculate our bottom pane
calculateTriangles();
//BOTTOM PANE
for (unsigned int i = 0; i < calculatedTriangles.size(); i++)
{
p2t::Triangle& t = *calculatedTriangles[i];
p2t::Point& a = *t.GetPoint(0);
p2t::Point& b = *t.GetPoint(1);
p2t::Point& c = *t.GetPoint(2);
glBegin(GL_TRIANGLES);
glNormal3f(0, -1, 0);
glVertex3f((GLfloat)a.x, (GLfloat)0.0f, (GLfloat)a.y);
glVertex3f((GLfloat)b.x, (GLfloat)0.0f, (GLfloat)b.y);
glVertex3f((GLfloat)c.x, (GLfloat)0.0f, (GLfloat)c.y);
glEnd();
}
//TOP PANE
for (unsigned int i = 0; i < calculatedTriangles.size(); i++)
{
p2t::Triangle& t = *calculatedTriangles[i];
p2t::Point& a = *t.GetPoint(0);
p2t::Point& b = *t.GetPoint(1);
p2t::Point& c = *t.GetPoint(2);
glBegin(GL_TRIANGLES);
glNormal3f(0, 1, 0);
glVertex3f((GLfloat)a.x, (GLfloat)HEIGHT, (GLfloat)a.y);
glVertex3f((GLfloat)b.x, (GLfloat)HEIGHT, (GLfloat)b.y);
glVertex3f((GLfloat)c.x, (GLfloat)HEIGHT, (GLfloat)c.y);
glEnd();
}
glColor3f(1.0f, 1.0f, 1.0f);
//SIDE PANES
for(std::size_t iPaneCounter = 0; iPaneCounter < pointList->size(); iPaneCounter++)
{
Point firstPoint = (*pointList)[iPaneCounter];
Point secondPoint (0.0f, 0.0f, 0.0f);
if(iPaneCounter + 1 < pointList->size())
secondPoint.set((*pointList)[iPaneCounter + 1].getX(), (*pointList)[iPaneCounter + 1].getY(), (*pointList)[iPaneCounter + 1].getZ() );
else
secondPoint.set((*pointList)[0].getX(), (*pointList)[0].getY(), (*pointList)[0].getZ());
glBegin(GL_POLYGON);
float fNormalX = (firstPoint.getY() * secondPoint.getZ()) - (firstPoint.getZ() * secondPoint.getY());
float fNormalY = -((secondPoint.getZ() * firstPoint.getX()) - (secondPoint.getX() * firstPoint.getZ()));
float fNormalZ = (firstPoint.getX() * secondPoint.getY()) - (firstPoint.getY() * secondPoint.getX());
glNormal3f(fNormalX, fNormalY, fNormalZ);
glVertex3f(firstPoint.getX(), 0.0f, firstPoint.getZ());
glVertex3f(secondPoint.getX(), 0.0f, secondPoint.getZ());
glVertex3f(secondPoint.getX(), secondPoint.getY(), secondPoint.getZ());
glVertex3f(firstPoint.getX(), firstPoint.getY(), firstPoint.getZ());
glEnd();
}
}
/*
Calculates triangles that make a ground or top pane. Used for calculating possible non-convex objects
*/
void WorldObject::calculateTriangles()
{
//Empty the polyPoints list
if(polyPoints.size() > 0)
FreeClear(polyPoints);
//Convert our Points to p2t::Points
for(std::size_t iBottomIndex = 0; iBottomIndex < pointList->size(); iBottomIndex++)
polyPoints.push_back(new p2t::Point((*pointList)[iBottomIndex].getX(), (*pointList)[iBottomIndex].getZ()));
if(cdt == nullptr)
//Create CDT (Constrained Delaunay Triangulation) and add primary polyPoints
//NOTE: polyPoints must be a simple polygon. The polyPoints' points constitute constrained edges. No repeating points are allowed!
cdt = new p2t::CDT(polyPoints);
//Turn our polyPoints into p2t::Triangles
cdt->Triangulate();
//Set the triangles to use for drawing
calculatedTriangles = cdt->GetTriangles();
}
/*
Retrieve a pointer to a list of Points
*/
std::vector<Point>* WorldObject::getPoints()
{
return pointList;
}
/*
Retrieve a pointer to a list of p2t::Triangles
*/
std::vector<p2t::Triangle*> WorldObject::getCalculatedTriangles()
{
return calculatedTriangles;
}
When all WorldObjects are created, they are stored in DataStorage, DataStorage::getInstance()->setObjects() is called:
void DataStorage::setObjects(std::list<WorldObject>* objectList)
{
delete this->objectList;
this->objectList = objectList;
}
The application seems to crash on delete this->objectList; in setObjects(), so we think the application is trying to delete things he can't delete.
Any help would be greatly appreciated, we've been on this for a few days already
Here, you pass a pointer to an object owned by the list to the constructor of WorldObject:
for(std::vector<Point>&pointObject : *inputList)
{
WorldObject worldObject(&pointObject);
tempObjects->push_back(worldObject);
}
In WorldObject you store the pointer:
//Default Constructor
WorldObject::WorldObject(std::vector<Point>* pointList)
{
float fGeneratedY = (rand() % 20 + 2) / 2.0f;*/
cdt = nullptr;
for (Point &point : *pointList)
point.setY(HEIGHT);
this->pointList = pointList;
}
Which means WorldObject::pointList is only valid so long as the std::list which you constructed your WorldObjects from is still around. (After that, the result is undefined -- it could work, it could crash, it could format your hard drive and leak your identity to Texas).
If you insist on working with raw pointers, you as programmer are responsible for checking and keeping track of the lifetime of every single pointer. This is error prone and will cause random crashes that you will find difficult to track down.
Stop using raw pointers. Instead, if an object owns a resource, store it in a std::unique_ptr<>. If you want the same resource to be shared by multiple objects, use std::shared_ptr and std::weak_ptr, unless the lifetime of all but one of these objects is much, much shorter than the others in a guaranteed way.

Struct property that returns a struct of its own type

I'm trying to define a struct in C++ that has properties to return pre-defined values of it's own type.
Like many APIs have for Vectors and Colors like:
Vector.Zero; // Returns a vector with values 0, 0, 0
Color.White; // Returns a Color with values 1, 1, 1, 1 (on scale from 0 to 1)
Vector.Up; // Returns a vector with values 0, 1 , 0 (Y up)
Source: http://msdn.microsoft.com/en-us/library/system.drawing.color.aspx
(MSDN's page of their Color type)
I've been trying to search for hours but I can't for the heart of me even figure out what it's called.
//in h file
struct Vector {
int x,y,z;
static const Vector Zero;
};
// in cpp file
const Vector Vector::Zero = {0,0,0};
Like this?
You can mimic it with static members:
struct Color {
float r, g, b;
Foo(float v_r, float v_g, float v_b):
r(v_r), g(v_g), b(v_b){};
static const Color White;
};
const Color Color::White(1.0f, 1.0f, 1.0f);
// In your own code
Color theColor = Color::White;
This is a static property. Unfortunately, C++ does not have properties of any type. To implement this, you probably want either a static method or a static variable. I would recommend the former.
For the Vector example, you would want something like:
struct Vector {
int _x;
int _y;
int _z;
Vector(int x, int y, int z) {
_x = x;
_y = y;
_z = z;
}
static Vector Zero() {
return Vector(0,0,0);
}
}
You would then write Vector::Zero() to get the zero vector.

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