Dynamically allocating length to an objective C static array - objective-c

Hi I am relatively new to programming on iOS and using objective C. Recently I have come across an issue I cannot seem to solve, I am writing a OBJ model loader to use within my iOS programming. For this I use two arrays as below:
static CGFloat modelVertices[360*9]={};
static CGFloat modelColours[360*12]={};
As can be seen the length is currently allocated with a hard coded value of 360 (the number of faces in a particular model). Is there no way this can be dynamically allocated from a value that has been calculated after reading the OBJ file as is done below?
int numOfVertices = //whatever this is read from file;
static CGFloat modelColours[numOfVertices*12]={};
I have tried using NSMutable arrays but found these difficult to use as when it comes to actually drawing the mesh gathered I need to use this code:
-(void)render
{
// load arrays into the engine
glVertexPointer(vertexStride, GL_FLOAT, 0, vertexes);
glEnableClientState(GL_VERTEX_ARRAY);
glColorPointer(colorStride, GL_FLOAT, 0, colors);
glEnableClientState(GL_COLOR_ARRAY);
//render
glDrawArrays(renderStyle, 0, vertexCount);
}
As you can see the command glVertexPointer requires the values as a CGFloat array:
glVertexPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);

You could use a c-style malloc to dynamically allocate space for the array.
int numOfVertices = //whatever this is read from file;
CGFloat *modelColours = (CGFloat *) malloc(sizeof(CGFloat) * numOfVertices);

When you declare a static variable, its size and initial value must be known at compile time. What you can do is declare the variable as a pointer instead of an array, the use malloc or calloc to allocate space for the array and store the result in your variable.
static CGFloat *modelColours = NULL;
int numOfVertices = //whatever this is read from file;
if(modelColours == NULL) {
modelColours = (CGFloat *)calloc(sizeof(CGFloat),numOfVertices*12);
}
I used calloc instead of malloc here because a static array would be filled with 0s by default, and this would ensure that the code was consistent.

Related

Passing array of float pointers into VBO

I'm creating a particle system renderer, the problem is that all my particle positions are encapsulated into classes that integrate them over time and do some other stuff. Instead of copying the values into a separate array to render each frame, I created a point struct like this that I can use to point to all the values once:
typedef struct
{
float *x, *y, *z;
} point;
I then malloc an array of these the same size as my array of particles.
Declaration at the top:
point *points;
Malloc:
points = malloc(sizeof(point) * [particles count]);
I then loop through all the particles and copy the addresses of each position to the corresponding pointer so:
for (int i = 0; i < [particles count]; ++i)
{
points[i].x = &[[particles objectAtIndex:i] getPosition].x;
points[i].y = &[[particles objectAtIndex:i] getPosition].y;
points[i].z = &[[particles objectAtIndex:i] getPosition].z;
}
My get position function just returns a vector struct (float x, y, z).
The first problem I encountered with this is that when I display the values like so:
NSLog(#"%f", *points[0].x);
Instead of reading of the value (say: 0.5), it displays it rounded and negated (so: -1.0).
I then get the data into my VBO like this:
glBufferData(GL_ARRAY_BUFFER, sizeof(point) * [particles count], &points[0], GL_DYNAMIC_DRAW);
I think this is also wrong but can't find any information on how to pass pointers into VBOs.
What you're doing is wrong because in C everything is passed by value, so when you dereference the .x member of the returned struct, you're doing it on a copy of the position struct, which will be popped from the stack if not assigned to any value.
You're design is wrong, you don't need a struct like this one:
typedef struct
{
float *x, *y, *z;
} point;
Rather one like this:
typedef struct
{
float x,y,z;
}
If you still want to assign it to it's original value, in a way that if the original struct's values change, also this struct is changed, then you need a getter who will return a pointer to the struct, and hold a pointer to it:
points[i] = [[particles objectAtIndex:i] getPositionPointer];
This way you're sure that your struct contains the same values of the original struct, but you're violating encapsulation. Which makes me think that maybe you don't really need to hold a pointer to original struct, but if there's a reason to do then I've shown you the way.

Is there a difference between GLubyte and GL_UNSIGNED_BYTE?

I'm rendering an interleaved vbo using the following code which works fine.
glVertexPointer(3, GL_FLOAT, sizeof(InterleavedVertexData), (GLvoid*)((char*)0));
glNormalPointer(GL_FLOAT, sizeof(InterleavedVertexData), (GLvoid*)((char*)0+3*sizeof(GLfloat)));
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(InterleavedVertexData), (GLvoid*)((char*)0+6*sizeof(GL_UNSIGNED_BYTE)));
When I change glColorPointer's pointer paramater to use GLubyte i don't see anything rendered on the screen? I'm defining colour as GLubyte in my struct also.
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(InterleavedVertexData), (GLvoid*)((char*)0+6*sizeof(GLubyte)));
GLubyte is a type. GL_UNSIGNED_BYTE is an integer constant which is often used to indicate that you will pass a GLubyte in a pointer.
sizeof(GLubyte) is always 1 by definition. Taking sizeof(GL_UNSIGNED_BYTE) will typically return 4 or 8, because it's an integer constant, and has the size of whatever your system's integer size is.
GL_UNSIGNED_BYTE is a symbolic const while GLubyte is a type. GLubyte is commonly implemented as a typedef of unsigned char; you can confirm this by looking at your gl.h.
You should use GL_UNSIGNED_BYTE inside your OpenGL method to specify the type of data you are passing along and use GLubyte to compute the size of your data.

NSMutableDictionary for huge dataset of floats

I've got some code to convert a large (many gigabytes) XML file into another format.
Among other things, I need to store one or two gigabytes of floats in a hash table (two floats for each entry), with an int as the value's key.
Currently, I'm using NSMutableDictionary and a custom class containing the two floats:
// create the dictionary
NSMutableDictionary *points = [[NSMutableDictionary alloc] init];
// add an entry (the data is read from an XML file using libxml)
int pointId = 213453;
float x = 42.313554;
float y = -21.135213;
MyPoint *point = [[MyPoint alloc] initWithX:x Y:y];
[points setObject:point forKey:[NSNumber numberWithInt:pointId]];
[point release];
// retrieve an entry (this happens later on while parsing the same XML file)
int pointId = 213453;
float x;
float y;
MyPoint *point = [points objectForKey:[NSNumber numberWithInt:pointId]];
x = point.x;
y = point.y;
This data set is consuming about 800MB of RAM with the XML file I'm working with now, and it takes quite a long time to execute. I'd like to have better performance, but even more important I need to get the memory consumption down so I can process even larger XML files.
objc_msg_send is right up there in a profile of the code, as is - [NSNumber numberWithInt:], and I'm sure I can get the memory usage down by avoiding objects altogether, but I don't know much about C programming (this project is certainly teaching me!).
How can I replace NSMuableDictionary, NSNumber MyPoint with an efficient C data structure? Without any third party library dependencies?
I'd also like to be able to write this data structure to files on the disk, so I can work with a dataset that doesn't entirely fit into memory, but I can probably live without this capability.
(for those not familiar with Objective-C, the NSMutableDictionary class can only store Obj-C objects, and it the keys must also be objects. NSNumber and MyPoint are dumb container classes to allow NSMutableDictionary to work with float and int values.)
EDIT:
I've tried using CFMutableDictionary to store structs, as per apple's sample code. When the dictionary is empty, it performs great. But as the dictionary grows it gets slower and slower. About 25% through parsing a file (~4 million items in the dictionary) it starts to chug, two orders of magnitude slower than earlier in the file.
NSMutableDictionary doesn't have the same performance issue. Instruments shows a lot of activity applying hashes and comparing the keys of the dictionary (the intEqual() method below). Comparing an int is fast, so something is very wrong for it to be executing so often.
Here's my code to create the dictionary:
typedef struct {
float lat;
float lon;
} AGPrimitiveCoord;
void agPrimitveCoordRelease(CFAllocatorRef allocator, const void *ptr) {
CFAllocatorDeallocate(allocator, (AGPrimitiveCoord *)ptr);
}
Boolean agPrimitveCoordEqual(const void *ptr1, const void *ptr2) {
AGPrimitiveCoord *p1 = (AGPrimitiveCoord *)ptr1;
AGPrimitiveCoord *p2 = (AGPrimitiveCoord *)ptr2;
return (fabsf(p1->lat - p2->lat) < 0.0000001 && fabsf(p1->lon - p2->lon) < 0.0000001);
}
Boolean intEqual(const void *ptr1, const void *ptr2) {
return (int)ptr1 == (int)ptr2;
}
CFHashCode intHash(const void *ptr) {
return (CFHashCode)((int)ptr);
}
// init storage dictionary
CFDictionaryKeyCallBacks intKeyCallBacks = {0, NULL, NULL, NULL, intEqual, intHash};
CFDictionaryValueCallBacks agPrimitveCoordValueCallBacks = {0, NULL /*agPrimitveCoordRetain*/, agPrimitveCoordRelease, NULL, agPrimitveCoordEqual};
temporaryNodeStore = CFDictionaryCreateMutable(NULL, 0, &intKeyCallBacks, &agPrimitveCoordValueCallBacks);
// add an item to the dictionary
- (void)parserRecordNode:(int)nodeId lat:(float)lat lon:(float)lon
{
AGPrimitiveCoord *coordPtr = (AGPrimitiveCoord *)CFAllocatorAllocate(NULL, sizeof(AGPrimitiveCoord), 0);
coordPtr->lat = lat;
coordPtr->lon = lon;
CFDictionarySetValue(temporaryNodeStore, (void *)nodeId, coordPtr);
}
EDIT 2:
The performance problem was due to the almost useless hashing implementation in Apple's sample code. I got the performance way up by using this:
// hash algorithm from http://burtleburtle.net/bob/hash/integer.html
uint32_t a = abs((int)ptr);
a = (a+0x7ed55d16) + (a<<12);
a = (a^0xc761c23c) ^ (a>>19);
a = (a+0x165667b1) + (a<<5);
a = (a+0xd3a2646c) ^ (a<<9);
a = (a+0xfd7046c5) + (a<<3);
a = (a^0xb55a4f09) ^ (a>>16);
If you want NSMutableDictionary-like behavior but with malloc'd memory, you can drop down to CFDictionary (or in your case, CFMutableDictionary). It's actually the underpinnings of NSMutableDictionary, but it allows some customization, namely you can tell it that you're not storing objects. When you call CFDictionaryCreateMutable() you give it a struct that describes what sort of values you're handing it (it contains pointers that tell it how to retain, release, describe, hash, and compare your values). So if you want to use a struct containing two floats, and you're happy using malloc'd memory for each struct, you can malloc your struct, populate it, and hand that to the CFDictionary, and then you can write the callback functions such that they work with your particular struct. The only restriction on the keys and objects you can use CFDictionary with is they need to fit inside a void *.
For this sort of thing I would just use C++ containers std::unordered_map and std::pair. You can use them in Objective-C++. Just give your files a .mm extension instead of the usual .m extension.
Update
In your comment you said you've never done C++ before. In that case, you should either try Kevin Ballard's answer of CFDictionary, or check out the hcreate, hdestroy, and hsearch functions in the standard library.
hcreate man page
Rename your .m file to .mm and switch to using C++:
std::map<int, std::pair<float>> points;

OpenGL ES Interleaving Vertex Buffer Object

I've been looking at the new OpenGL framework for iOS, aptly named GLKit, and have been playing around with porting some existing OpenGL 1.0 code to OpenGL ES 2.0 just to dip my toe in the water and get to grips with things.
After reading the API and a whole ream of other best practices provided by Apple and the OpenGL documentation, i've had it pretty much ingrained into me that I should be using Vertex Buffer Objects and using "elements" or rather, vertex indices. There seems to be a lot of mention of optimising memory storage by using padding where necessary too but that's a conversation for another day perhaps ;)
I read on SO a while ago about the benefits of using NSMutableData over classic malloc/free and wanted to try and take this approach when writing my VBO. So far i've managed to bungle together a snippet that looks like i'm heading down the right track but i'm not entirely sure about how much data a VBO should contain. Here's what i've got so far:
//import headers
#import <GLKit/GLKit.h>
#pragma mark -
#pragma mark InterleavingVertexData
//vertex buffer object struct
struct InterleavingVertexData
{
//vertices
GLKVector3 vertices;
//normals
GLKVector3 normal;
//color
GLKVector4 color;
//texture coordinates
GLKVector2 texture;
};
typedef struct InterleavingVertexData InterleavingVertexData;
#pragma mark -
#pragma mark VertexIndices
//vertex indices struct
struct VertexIndices
{
//vertex indices
GLuint a;
GLuint b;
GLuint c;
};
typedef struct VertexIndices VertexIndices;
//create and return a vertex index with specified indices
static inline VertexIndices VertexIndicesMake(GLuint a, GLuint b, GLuint c)
{
//declare vertex indices
VertexIndices vertexIndices;
//set indices
vertexIndices.a = a;
vertexIndices.b = b;
vertexIndices.c = c;
//return vertex indices
return vertexIndices;
}
#pragma mark -
#pragma mark VertexBuffer
//vertex buffer struct
struct VertexBuffer
{
//vertex data
NSMutableData *vertexData;
//vertex indices
NSMutableData *indices;
//total number of vertices
NSUInteger totalVertices;
//total number of indices
NSUInteger totalIndices;
};
typedef struct VertexBuffer VertexBuffer;
//create and return a vertex buffer with allocated data
static inline VertexBuffer VertexBufferMake(NSUInteger totalVertices, NSUInteger totalIndices)
{
//declare vertex buffer
VertexBuffer vertexBuffer;
//set vertices and indices count
vertexBuffer.totalVertices = totalVertices;
vertexBuffer.totalIndices = totalIndices;
//set vertex data and indices
vertexBuffer.vertexData = nil;
vertexBuffer.indices = nil;
//check vertices count
if(totalVertices > 0)
{
//allocate data
vertexBuffer.vertexData = [[NSMutableData alloc] initWithLength:(sizeof(InterleavingVertexData) * totalVertices)];
}
//check indices count
if(totalIndices > 0)
{
//allocate data
vertexBuffer.indices = [[NSMutableData alloc] initWithLength:(sizeof(VertexIndices) * totalIndices)];
}
//return vertex buffer
return vertexBuffer;
}
//grow or shrink a vertex buffer
static inline void VertexBufferResize(VertexBuffer *vertexBuffer, NSUInteger totalVertices, NSUInteger totalIndices)
{
//check adjusted vertices count
if(vertexBuffer->totalVertices != totalVertices && totalVertices > 0)
{
//set vertices count
vertexBuffer->totalVertices = totalVertices;
//check data is valid
if(vertexBuffer->vertexData)
{
//allocate data
[vertexBuffer->vertexData setLength:(sizeof(InterleavingVertexData) * totalVertices)];
}
else
{
//allocate data
vertexBuffer->vertexData = [[NSMutableData alloc] initWithLength:(sizeof(InterleavingVertexData) * totalVertices)];
}
}
//check adjusted indices count
if(vertexBuffer->totalIndices != totalIndices && totalIndices > 0)
{
//set indices count
vertexBuffer->totalIndices = totalIndices;
//check data is valid
if(vertexBuffer->indices)
{
//allocate data
[vertexBuffer->indices setLength:(sizeof(VertexIndices) * totalIndices)];
}
else
{
//allocate data
vertexBuffer->indices = [[NSMutableData alloc] initWithLength:(sizeof(VertexIndices) * totalIndices)];
}
}
}
//release vertex buffer data
static inline void VertexBufferRelease(VertexBuffer *vertexBuffer)
{
//set vertices and indices count
vertexBuffer->totalVertices = 0;
vertexBuffer->totalIndices = 0;
//check vertices are valid
if(vertexBuffer->vertexData)
{
//clean up
[vertexBuffer->vertexData release];
vertexBuffer->vertexData = nil;
}
//check indices are valid
if(vertexBuffer->indices)
{
//clean up
[vertexBuffer->indices release];
vertexBuffer->indices = nil;
}
}
Currently, the interleaving vertex data contains enough to store the vertices, normals, colors and texture coordinates for each vertex. I was under the impression that there would be an equal number of vertices and indices but in practice this obviously isn't the case so for this reason, the indices are part of the VBO rather than the InterleavingVertexData.
Question Updated:
I've updated the code above after managing to wrangle it into a working state. Hopefully it will come in useful to someone in the future.
Now that i've managed to set everything up, i'm having trouble getting the expected results from rendering the content bound to the VBO. Here's the code i've got so far for loading my data into OpenGL:
//generate buffers
glGenBuffers(2, buffers);
//bind vertices buffer
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
glBufferData(GL_ARRAY_BUFFER, (sizeof(InterleavingVertexData) * vertexBuffer.totalVertices), self.vertexData, GL_STATIC_DRAW);
//bind indices buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (sizeof(VertexIndices) * vertexBuffer.totalIndices), self.vertexIndices, GL_STATIC_DRAW);
//reset buffers
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
And the code for rendering everything:
//enable required attributes
glEnableVertexAttribArray(GLKVertexAttribPosition);
glEnableVertexAttribArray(GLKVertexAttribNormal);
glEnableVertexAttribArray(GLKVertexAttribColor);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
//bind buffers
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
//set shape attributes
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(InterleavingVertexData), (void *)offsetof(InterleavingVertexData, vertices));
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_TRUE, sizeof(InterleavingVertexData), (void *)offsetof(InterleavingVertexData, normal));
glVertexAttribPointer(GLKVertexAttribColor, 4, GL_FLOAT, GL_TRUE, sizeof(InterleavingVertexData), (void *)offsetof(InterleavingVertexData, color));
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_TRUE, sizeof(InterleavingVertexData), (void *)offsetof(InterleavingVertexData, texture));
//draw shape
glDrawElements(GL_TRIANGLES, vertexBuffer.totalIndices, GL_UNSIGNED_INT, (void *)0);
//reset buffers
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
//disable atttributes
glDisableVertexAttribArray(GLKVertexAttribTexCoord0);
glDisableVertexAttribArray(GLKVertexAttribColor);
glDisableVertexAttribArray(GLKVertexAttribNormal);
glDisableVertexAttribArray(GLKVertexAttribPosition);
Whilst my iPhone hasn't yet exploded with awesome graphics of unicorns shooting rainbows from their eyes, I haven't been able to render a simple shape in it's entirety without tearing my hair out.
From the rendering it looks as though only 1/3rd of each shape is being drawn, perhaps 1/2 depending on the viewing angle. It seems the culprit it the count parameter passed to glDrawElements as fiddling with this has differing results but I've read the documentation and checked the value over and over again and it does indeed expect the total number of indices (which is what i'm passing currently).
As I mentioned in my original question, i'm quite confused by VBO's currently or rather, confused by the implementation rather than the concept at least. If anyone would be so kind as to cast an eye over my implementation, that would be super awesome as i'm sure i've made a rookie error somewhere along the way but you know how it is when you stare at something for hours on end with no progress.
Thanks for reading!
I think I see your problem.
You've got a struct, VertexIndices which contains three indices, or the indices for one triangle. When you bind your IBO (Index Buffer Object, the buffer object containing your indices), you do this:
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (sizeof(VertexIndices) * vertexBuffer.totalIndices), self.vertexIndices, GL_STATIC_DRAW);
Which is fine. The size parameter in glBufferData is in bytes so you're multiplying sizeof(3 floats) by the number of groups of 3 floats that you have. Great.
But then when you actually call glDrawElements, you do this:
glDrawElements(GL_TRIANGLES, vertexBuffer.totalIndices, GL_UNSIGNED_INT, (void *)0);
However, the vertexBuffer.totalIndices is equal to the number of VertexIndices structs you've got, which is equal to the total number of indices / 3 (or total number of triangles). So you need to do one of the following:
Easy fix yet stupid: glDrawElements(..., vertexBuffer.totalIndices * 3, ...);
Proper yet more work: vertexBuffer.totalIndices should contain the actual total number of indices that you've got, not the total number of triangles you're rendering.
You need to do one of these because right now totalIndices contains the total number VertexIndices you've got, and each one has 3 indices. The right thing to do here is either rename totalIndices to totalTriangles, or keep track of the actual total number of indices somewhere.

Problem while converting NSData to int

Using foundation and cocoa frameworks on Mac, I am trying to convert an NSData object in humanly understandable number.
Let say the NSData object is an image of NPIXEL. I know the binary data are coded in big endian and represent 32 bit integer (to be more precise 32 bit two complements integer). I write the piece of code bellow to convert the NSData into an int array. But the value I got are completely wrong (this does not means the measurement are bad, I used a special software to read the data and the value given by the software are different from the one I got with my code).
-(int *) GetArrayOfLongInt
{
//Get the total number of element into the Array
int Nelements=[self NPIXEL];
//CREATE THE ARRAY
int array[Nelements];
//FILL THE ARRAY
int32_t intValue;
int32_t swappedValue;
double Value;
int Nbit = abs(BITPIX)*GCOUNT*(PCOUNT + Nelements); Nbit/=sizeof(int32_t);
int i=0;
int step=sizeof(int32_t);
for(int bit=0; bit < Nbit; bit+=step)
{
[Img getBytes:&swappedValue range:NSMakeRange(bit,step)];
intValue= NSSwapBigIntToHost(swappedValue);
array[i]=intValue;
i++;
}
return array;
}
This piece of code (with minor change) work perfectly when the binary data represent float or double, but I dont when it is 16,32 or 64 bit integer. I also tried changingNSSapBigIntToHostintoNSSwapLittleInttoHost`. I even tried with long, but the results is still the same, I got bad values. What wrong I am doing ?
PS: Some of the variable in my code are already set elsewhere in my program. BITPIX is the bit size of each pixel. In this case 32. GCOUNT is equal to 1, PCOUNT 0 and Nelements is the total number of pixel I should have in my image.
Returning a pointer to a local variable is a very bad idea. array could get overwritten at any time (or if you were to write through the pointer, you could corrupt the stack). You probably want something like:
// CREATE THE ARRAY
int *array = malloc(Nelements * sizeof(int));
Your algorithm seems a bit overkill, too. Why not just copy out the whole array from the NSData object, and then byteswap the entries in place? Something like:
int32_t length = [Img length];
int32_t *array = malloc(length);
[Img getBytes:array length:length];
for (i = 0; i < length/sizeof(int32_t); i++)
{
array[i] = NSSwapBigIntToHost(array[i]);
}