glutesselator always crashes at gluTessEndPolygon() [closed] - c++

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I use glu tessellation to tessellate complex polygons. The code simplified is listed bellow.
It always crashes at gluTessEndPolygon(GLUtessobj) with error:
Error: 0xC0000005: Access violation reading location 0x57783b39;
The code works when the number of points of the polygon is small (<100).
I just can't figure out why.
typedef boost::geometry::model::point<float, 2, boost::geometry::cs::cartesian> pt;
typedef boost::geometry::model::polygon<pt> Polygon;
typedef boost::geometry::model::ring<pt> Ring;
vector<Polygon> g_myPolys;
// ------Static variables used in glu tessellation------
static GLUtesselator *GLUtessobj;
static unsigned int s_gltri_type;
static int s_tess_orient;
static int s_cur_pt_idx;
// Create an array to hold pointers to allocated vertices created by "combine" callback,
// so that they may be deleted after tessellation.
static std::vector<GLdouble*> s_combineVertexArray;
// Store tessellated results
static std::vector<double> s_vecTriVerts; // Store area objects' tessellated triangle( triangle fan, triangle strip and triangles) vertices.
static std::vector<int> s_vecTriStripVertCnts; // Store every triangle strips' start indices in m_vecTriVerts.
static std::vector<int> s_vecTriStripFirstIdx; // Store every triangle strips' vertex count start from its start index.
static std::vector<int> s_vecTriFanVertCnts; // Store every triangle fans' start indices in m_vecTriVerts.
static std::vector<int> s_vecTriFanFirstIdx; // Store every triangle fans' vertex count start from its start index.
static std::vector<int> s_vecTrisVertCnts; // Store every triangles' start indices in m_vecTriVerts.
static std::vector<int> s_vecTrisFirstIdx; // Store every triangles' vertex count start from its start index.
static int s_cur_tri_fans_vert_cnt;
static int s_cur_tri_strips_vert_cnt;
static int s_cur_tris_vert_cnt;
static std::vector<double*> s_vecTmp;
void beginCallback(GLenum which)
{
s_gltri_type = which;
switch ( s_gltri_type)
{
case GL_TRIANGLE_FAN:
s_vecTriFanFirstIdx.push_back(s_cur_pt_idx);
s_cur_tri_fans_vert_cnt = 0;
break;
case GL_TRIANGLE_STRIP:
s_vecTriStripFirstIdx.push_back(s_cur_pt_idx);
s_cur_tri_strips_vert_cnt = 0;
break;
case GL_TRIANGLES:
s_vecTrisFirstIdx.push_back(s_cur_pt_idx);
s_cur_tris_vert_cnt = 0;
break;
}
}
void vertexCallback(GLvoid *vertex)
{
GLdouble *pv = (GLdouble *) vertex;
s_vecTriVerts.push_back(pv[0]);
s_vecTriVerts.push_back(pv[1]);
s_cur_pt_idx ++;
switch ( s_gltri_type)
{
case GL_TRIANGLE_FAN:
s_cur_tri_fans_vert_cnt ++;
break;
case GL_TRIANGLE_STRIP:
s_cur_tri_strips_vert_cnt ++;
break;
case GL_TRIANGLES:
s_cur_tris_vert_cnt ++;
break;
}
}
void combineCallback(GLdouble coords[3],
GLdouble *vertex_data[4],
GLfloat weight[4], GLdouble **dataOut )
{
GLdouble *vertex = (GLdouble *)malloc(6 * sizeof(GLdouble));
vertex[0] = coords[0];
vertex[1] = coords[1];
vertex[2] = coords[2];
vertex[3] = vertex[4] = vertex[5] = 0.0;
*dataOut = vertex;
s_combineVertexArray.push_back(vertex);
}
void endCallback()
{
switch ( s_gltri_type)
{
case GL_TRIANGLE_FAN:
s_vecTriFanVertCnts.push_back(s_cur_tri_fans_vert_cnt);
break;
case GL_TRIANGLE_STRIP:
s_vecTriStripVertCnts.push_back(s_cur_tri_strips_vert_cnt);
break;
case GL_TRIANGLES:
s_vecTrisVertCnts.push_back(s_cur_tris_vert_cnt);
break;
}
}
void errorCallback(GLenum errorCode)
{
const GLubyte *estring;
estring = gluErrorString(errorCode);
printf ("Tessellation Error: %s\n", estring);
}
void Tessellate()
{
// Create tessellate object
GLUtessobj = gluNewTess();
// Register the callbacks
gluTessCallback(GLUtessobj, GLU_TESS_BEGIN, (void (__stdcall*)())&beginCallback);
gluTessCallback(GLUtessobj, GLU_TESS_VERTEX, (void (__stdcall*)())&vertexCallback);
gluTessCallback(GLUtessobj, GLU_TESS_END, (void (__stdcall*)())&endCallback);
gluTessCallback(GLUtessobj, GLU_TESS_COMBINE, (void (__stdcall*)())&combineCallback);
gluTessCallback(GLUtessobj, GLU_TESS_ERROR, (void (__stdcall*)())&errorCallback);
gluTessProperty(GLUtessobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
gluTessBeginPolygon(GLUtessobj, NULL);
gluTessBeginContour(GLUtessobj);
Polygon pp = g_myPolys[0];
for ( int i = 0; i < pp.outer().size(); i ++)
{
GLdouble *p = new GLdouble[3];
s_vecTmp.push_back(p);
p[0] = pp.outer()[i].get<0>();
p[1] = pp.outer()[i].get<1>();
p[2] = 0.0;
gluTessVertex( GLUtessobj, p, p ) ;
}
gluTessEndContour(GLUtessobj);
gluTessEndPolygon(GLUtessobj);
gluDeleteTess(GLUtessobj);
for ( int i = 0; i < s_vecTmp.size(); i ++)
delete[] s_vecTmp[i];
s_vecTmp.clear();
// Free up any "Combine" vertices created
for(unsigned int i = 0; i < s_combineVertexArray.size(); i++)
free (s_combineVertexArray[i]);
s_combineVertexArray.clear();
}

One thing that immediately strikes me as odd is, that you do the cast to __stdcall there.
gluTessCallback(GLUtessobj, GLU_TESS_BEGIN, (void (__stdcall*)())&beginCallback);
Why are you doing that? If your compiler complains about incompatible calling conventions, then the last thing you should do there is casting the calling convention. Only despair and horror await if you cast a calling convention. It's already a bad idea to cast pointers (in C++ casting from/to void* is kind of okay, but that's it).
And then there are a few other weird things you do with pointers. For example you're mixing std::vector with manually managed memory (new GLdouble[3]). Seriously, why?!
I strongly suggest you simplify your data structures and clean up that pointer juggling. Most likely you have some out of bounds buffer write somewhere in your code, but it's difficult to see where exactly.

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!!!!!!!!!";
}

Tessellation - saving data

I am using tessellation in order to transfer non triangled polygons to triangle polygons.
I am trying to save the data in variable and run the tessellation code once while saving the data. The code use the saved data in order to draw it in draw function.
I am trying the draw a star polygon, The problem is that I can see some triangles but not a star. Why when I save the data the drawing go wrong?
Here is the initialize code:
#define callback void(CALLBACK*)()
void Init()
{
GLdouble star[5][6] = /* star data, The data is 100% perfect */
glColor3f(0.0f, 1.0f, 0.0f);
GLUtesselator *pTess = gluNewTess();
indexNum = 0;
gluTessCallback(pTess, GLU_TESS_BEGIN, (callback)glDrowMode);
gluTessCallback(pTess, GLU_TESS_VERTEX, (callback)saveData);
gluTessCallback(pTess, GLU_TESS_ERROR, (callback)tessError);
gluTessCallback(pTess, GLU_TESS_COMBINE, (callback) scbCombine);
gluTessProperty(pTess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
gluTessBeginPolygon(pTess, NULL);
gluTessBeginContour(pTess);
for(int i = 0; i < 5; i++)
gluTessVertex(pTess, star[i], star[i]);
gluTessEndContour(pTess);
gluTessEndPolygon(pTess);
gluDeleteTess(pTess);
}
The saving data code and saving the drawing mode:
struct vecStruct
{
GLdouble *vertex, *color;
};
vecStruct vec[16];
int indexNum = 0;
void CALLBACK saveData(const GLvoid *ptr)
{
const GLdouble *data = (const GLdouble*)ptr;
vec[indexNum].vertex = new GLdouble[3];
vec[indexNum].color = new GLdouble[3];
vec[indexNum].vertex[0] = data[0];
vec[indexNum].vertex[1] = data[1];
vec[indexNum].vertex[2] = data[2];
vec[indexNum].color[0] = data[3];
vec[indexNum].color[1] = data[4];
vec[indexNum].color[2] = data[5];
indexNum++;
}
GLenum drawMode;
void CALLBACK glDrowMode(GLenum where)
{
drawMode = where;
}
And last the drow function:
void vboDraw()
{
glBegin(drawMode);
for (int i = 0; i < indexNum; i++)
{
glColor3dv(vec[i].color);
glVertex3dv(vec[i].vertex);
}
glEnd();
}
As I have said I should see star:
But what I can see is some triangles:
What is wrong with the code?
Why can not I save the data for doing the tessellation code only once?

Access Violation when writing to pointer variables in dynamically allocated classes

Ok so the title doesn't explain my situation very well so I'll try to explain a little better here:
Here is part of my class structure:
ObjectView (abstract class)
ShipView : ObjectView (child of object view)
In a method I create a new ShipView:
ShipView *shipview (in header file).
shipview = new ShipView(in main part of code).
I then run shipview->Initialise();
to set everything up in the new class.
But when I get to any lines of code that try to write to a pointer declared in the ObjectView class it won't allow me to do so and gives me an Access Violation message.
The message that I get is below:
"Unhandled exception at 0x00a0cf1c in AsteroidGame.exe: 0xC0000005: Access violation writing location 0xbaadf011."
For example this line:
_ObjectData = new Model[mesh->mNumVertices];
will give me the error.
Just fyi I have put this in the header file:
struct Model{
GLfloat x,y,z;
GLfloat nX,nY,nZ;
GLfloat u,v;
};
Model *_ObjectData;
However if I was to do something along the lines of
Model *_ObjectData = new Model[mesh->mNumVertices];
(declare and initialise all at once)
it would work....
It's like it doesn't know the header file is there, or the class has not been properly constructed therefore the memory has not been allocated properly.
Any help would be greatly appreciated.
EDIT
Header File:
class ObjectView
{
public:
ObjectView(void);
virtual ~ObjectView(void);
void Initialise(std::string objectpath, std::string texturepath);
void InitialiseVBO(const aiScene* sc);
void RenderObject();
virtual void ScaleObject() = 0;
virtual void TranslateObject() = 0;
virtual void RotateObject() = 0;
protected:
struct Model{
GLfloat x,y,z;
GLfloat nX,nY,nZ;
GLfloat u,v;
};
Model *_ObjectData;
struct Indices{
GLuint x,y,z;
};
Indices *_IndicesData;
TextureLoader _textureloader;
GLuint _objectTexture;
GLuint _objectVBO;
GLuint _indicesVBO;
int _numOfIndices;
};
Code:
void ObjectView::InitialiseVBO(const aiScene* sc)
{
const aiMesh* mesh = sc->mMeshes[0];
_ObjectData = new Model[mesh->mNumVertices];
for(unsigned int i = 0; i < mesh->mNumVertices; i++)
{
_ObjectData[i].x = mesh->mVertices[i].x;
_ObjectData[i].y = mesh->mVertices[i].y;
_ObjectData[i].z = mesh->mVertices[i].z;
_ObjectData[i].nX = mesh->mNormals[i].x;
_ObjectData[i].nY = mesh->mNormals[i].y;
_ObjectData[i].nZ = mesh->mNormals[i].z;
_ObjectData[i].u = mesh->mTextureCoords[0][i].x;
_ObjectData[i].v = 1-mesh->mTextureCoords[0][i].y;
}
glGenBuffers(1, &_objectVBO);
glBindBuffer(GL_ARRAY_BUFFER, _objectVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(Model) * mesh->mNumVertices, &_ObjectData[0].x, GL_STATIC_DRAW);
_IndicesData = new Indices[mesh->mNumFaces];
for(unsigned int i = 0; i < mesh->mNumFaces; ++i)
{
for (unsigned int a = 0; a < 3; ++a)
{
unsigned int temp = mesh->mFaces[i].mIndices[a];
if(a == 0)
_IndicesData[i].x = temp;
else if(a == 1)
_IndicesData[i].y = temp;
else
_IndicesData[i].z = temp;
}
}
glGenBuffers(1, &_indicesVBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indicesVBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices) * mesh->mNumFaces, _IndicesData, GL_STATIC_DRAW);
_numOfIndices = sizeof(Indices) * mesh->mNumFaces;
glBindBuffer(GL_ARRAY_BUFFER, 0);
delete _ObjectData;
delete _IndicesData;
}
If
_ObjectData = new Model[mesh->mNumVertices];
crashes while
Model *_ObjectData = new Model[mesh->mNumVertices];
doesn't, it certainly looks like your object hasn't been initialized.
My psychic debugger suggests that you have declared a different variable, also called shipview, which is what you're calling Initialize() on.
0xbaadf011 is probably an offset from 0xbaadf00d which is a hexadecimal synonym for Bad Food. It is certainly an uninitialized pointer.
You are obviously not setting your pointers before using them. I suggest looking at the call stack when it crashes, set a breakpoint at one of the functions above your crash, then restart the program and run it line by line until you find the pointer that is set to 0xbaadf011 or 0xbaadf00d. Then figure out where you were supposed to set that pointer.

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

Why doesn't this work?

I'v tried to solve a memory leak in the GLU callback by creating a global variable but now it dos not draw anything:
GLdouble *gluptr = NULL;
void CALLBACK combineCallback(GLdouble coords[3], GLdouble *vertex_data[4],
GLfloat weight[4], GLdouble **dataOut)
{
GLdouble *vertex;
if(gluptr == NULL)
{
gluptr = (GLdouble *) malloc(6 * sizeof(GLdouble));
}
vertex = (GLdouble*)gluptr;
vertex[0] = coords[0];
vertex[1] = coords[1];
vertex[2] = coords[2];
for (int i = 3; i < 6; i++)
{
vertex[i] = weight[0] * vertex_data[0][i] +
weight[1] * vertex_data[0][i] +
weight[2] * vertex_data[0][i] +
weight[3] * vertex_data[0][i];
}
*dataOut = vertex;
}
basically instead of doing malloc each time in the loop (thus the memory leak) im using a global pointer, but this doesn't work (drawing to the screen not working) which means dataOut is not receiving the vertex data pointed to by my pointer. Why would using malloc to a pointer created in the function work any different than a global variable?
Thanks
You allocate the data only once -- but GLUtesselator needs more than one set of data at a time!
What you do here, is putting all the vertex data into a single place in memory, where in the original code, you had memory per vertex. GLUtesselator needs more then one vertex to function properly.
You do call
void gluDeleteTess(GLUtesselator *tessobj);
...afterwards, do you?
Most likely the reason is that something outside of your callback is holding on to the returned data across calls to combineCallback(), and subsequent calls to combineCallback() clobber now the data from the older calls.
Looking at the code, you need to rewrite this, there's a few things wrong which shows the inherent lack of understanding pointers and using call-by-reference parameter such as dataOut, secondly, there is no checking on the call to malloc which can fail and WILL fail, the code blindly assumes that the memory is available, thirdly, you have redundant pointer variables used such as vertex and gluptr for a reason. You are actually trying to build up a block of memory by copying the contents from gluptr to vertex, and use the coords pointer-to-block of data type of 'GLDouble', then build up the vertex block of memory... and finally assign it back to dataOut...forgive me if I misunderstand but read on...
This is the code that has removed redundant variables as shown below and fixes up the lack of checking for a NULL pointer...
GLdouble *gluptr = NULL;
void CALLBACK combineCallback(GLdouble coords[3], GLdouble *vertex_data[4],
GLfloat weight[4], GLdouble **dataOut)
{
if((*dataOut) == NULL)
{
(*dataOut) = (GLdouble *) malloc(6 * sizeof(GLdouble));
}
if (*dataOut != NULL){
/* PASSED MEMORY ALLOC! */
(*dataOut)[0] = coords[0];
(*dataOut)[1] = coords[1];
(*dataOut)[2] = coords[2];
for (int i = 3; i < 6; i++)
{
(*dataOut)[i] = weight[0] * vertex_data[0][i] +
weight[1] * vertex_data[0][i] +
weight[2] * vertex_data[0][i] +
weight[3] * vertex_data[0][i];
}
}
}
The last parameter when calling this function combineCallback is a call-by-reference parameter, hence the usage of the double asterisk..
I must ask this, is dataOut definitely a fixed size of 6 elements? if so then the parameter would need to be tweaked up...to make it look like *(*dataOut[6])... looking at it top off my head (it's late and past my bedtime...)