Sharing Struct Information Between Classes - Objective C - OpenGL ES - iphone

I have an OpenGL ES based app with the following structure :
I parse vertices from a DAE file which contains a number of objects (using NSXMLParser).
As I parse each set of vertices, I create an object (sceneObject) and set the vertices array in this object to match the parsed vertices. I am then planning to add these sceneObjects to array of objects to be rendered by OpenGL ES.
Initially I had taken an approach of using NSArrays to store the vertice data, however I understand that the OpenGL commands (such as glvertexpointer) do not accept NSObject values, bue instead need raw values (glfloats and gluints etc).
Rather than convert the NSObjects back to GL raw values within the object class, I am now trying to take a struct approach. This is the code where I parse the vertice information :
if (floatArray) {
if (currentlyParsing == kVerticeInformation) {
if (currentParserTagType == kGeometry) {
//NSLog(#"Loaded Vertices %#", string);
NSArray * nums = [string componentsSeparatedByString:#" "];
culmunativeCount += [nums count];
//NSLog(#"Culm Count is %d", culmunativeCount);
[fullParseString appendString : string];
if (checkSumCount <= culmunativeCount) {
//NSLog(#"Full Parse String is %#", fullParseString);
NSArray * finishedArray = [fullParseString componentsSeparatedByString:#" "];
//NSLog(#"Finihsed ARray is %d", [finishedArray count]);
[finishedParsingArray addObjectsFromArray:finishedArray];
//NSLog(#" FINISHED PARSING = %d", [finishedParsingArray count]);
NSUInteger baseIndex;
for (baseIndex=0; baseIndex <[finishedParsingArray count]; baseIndex +=3) {
NSString * xPoint = [finishedParsingArray objectAtIndex:baseIndex];
NSDecimalNumber * errorCheckX = [NSDecimalNumber decimalNumberWithString:xPoint];
float x = [errorCheckX floatValue];
NSString * yPoint = [finishedParsingArray objectAtIndex:baseIndex +1];
NSDecimalNumber * errorCheckY = [NSDecimalNumber decimalNumberWithString:yPoint];
float y = [errorCheckY floatValue];
NSString * zPoint = [finishedParsingArray objectAtIndex:baseIndex+2];
NSDecimalNumber * errorCheckZ = [NSDecimalNumber decimalNumberWithString:zPoint];
float z = [errorCheckZ floatValue];
Vertex3D vertexItem = {x,y,z}
//NSLog(#"Vertices stored are %f, %f, %f", x,y,z);
}
currentlyParsing = kNilSetting;
checkSumCount = 0;
[finishedParsingArray removeAllObjects];
//[finishedArray release];
culmunativeCount = 0;
[fullParseString setString:#""];
}
}
}
}
As you can see, I am now creating a Vertex3D C Struct each time with the parsed vertice information.
My questions are :
How would I then create an array of these individual C Vertex3D structs ?
How can I then pass this array of structs to my sceneObject ?

Create Objective C wrapper class for your array. It can hold several arrays (e.g. vertices, normals and tangents). You know the array size beforehand, so you can preallocate the required amount of space:
#interface MYVertexArray : NSObject {
float* _mVerts;
float* _mNormals;
int _mCount;
}
#property(nonatomic, readonly) float* verts;
#property(nonatomic, readonly) float* norms;
/**
Here you're allocating your arrays (don't forget to free them is dealloc).
*/
-(id) initWithSize:(int) size;
#end
and then in cycle, you're just doing this:
MYVertexArray* my_vertex_data = [[MYVertexArray alloc] initWithSize: [finishedParsingArray count]];
for(int i=0; i<[finishedParsingArray count]; ++i) {
my_vertex_data.verts[i] = [[finishedParsingArray objectAtIndex: i] floatValue];
}

Related

how to save the touched position of x and y values in a array

I'm working with the cocos2d 3.x and Xcode 5.1.1.i'm doing the game like a candy crush,here i load the sprites to 5*5 matrix,and i already get the position for the touched sprite,now i need to save and use that x,y value in a array like (0,0),(3,0),(2,2)
there are a few of ways to store coordinates, it is hard to tell which way would fit better for, regarding I don't know what you mean exactly when you say save...
options #1
CGPoint _coords = CGPointMake(x, y);
obvious choice to store them in a CGPoint struct, however the struct is designed to store fraction coordinates, but it can handle integer values as well.
you cannot insert a CGPoint directly into any collection type, like e.g. NSArray, NSSet or NSSDictionary, but you can store them in e.g. a fix-sized C-array, like:
CGPoint _fiveCoordinates[4];
option #2
NSString *_coordinates = [NSString stringWithFormat:#"%d %d", x, y];
that is a quick and ugly solution, I personally don't like it – however in certain cases is useful (vs. option #4!). it is also possible to store it any collection type, and you can extract the coordinates after, for further usage, like e.g.:
NSArray *_components = [_coordinates componentsSeparatedByString:#" "];
NSInteger x = [[_components firstObject] integerValue];
NSInteger y = [[_components lastObject] integerValue];
if you'd store the values in a simple NSArray like
NSArray *_coordinates = [NSArray arrayWithObjects:#(x), #(y)];
the extraction procedure would be the similar to the idea above.
option #3
NSDictionary *_coordinates = [NSDictionary dictionaryWithObjectsAndKeys:#(x), #"x", #(y), #"y"];
a simple dictionary can store them flawlessly, if you need to extract the values, it is like e.g.
NSInteger x = [[_coordinates valueForKey:#"x"] integerValue];
NSInteger y = [[_coordinates valueForKey:#"y"] integerValue];
option #4
NSIndexPath *_coordinates = [NSIndexPath indexPathForRow:y inSection:x];
if you like to work with index paths, that is a very straightforward way to store indices, because the NSIndexPath widely used and can be inserted directly into any collection type.
extracting the coordinates would be the same easy way:
NSInteger x = [_coordinates section];
NSInteger y = [_coordinates row];
option #5A
another obvious way would be that creating an own class to store those coordinates, like e.g.:
.h
#interface MyCoordinates : NSObject { }
#property (nonatomic) NSinteger x;
#property (nonatomic) NSinteger y;
#end
.m
#implementation MyCoordinates
#end
option #5B
and you can also extend it comforming the NSCoding protocol, if you want to get a pure serialisable object, which can be archived with the NSArray for permanent storing, like:
.h
#interface MyCoordinates : NSObject <NSCoding> { }
#property (nonatmic) NSInteger x;
#property (nonatmic) NSInteger y;
#end
.m
#implementation MyCoordinates
#pragma mark - <NSCoding>
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
_x = [[aDecoder decodeObjectForKey:#"x"] integerValue];
_y = [[aDecoder decodeObjectForKey:#"y"] IntegerValue];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:#(_x) forKey:#"x"];
[aCoder encodeObject:#(_y) forKey:#"y"];
}
#end
...or something similar, or you can combine them together as you'd feel which the most convenient way is in your personal view.
//SET
NSMutableArray* points = [[NSMutableArray alloc] initWithCapacity:10];
CGPoint pointToBeStored = CGPointMake(0,0);
[points addObject:[NSValue valueWithCGPoint:pointToBeStored]];
//GET
NSValue *value = [points objectAtIndex:index];
CGPoint storedPoint = [value CGPointValue];

Encode/Decode struct object

I am studying this wavefront obj loader example: https://github.com/jlamarche/iOS-OpenGLES-Stuff/tree/master/Wavefront%20OBJ%20Loader
And in order to improve object loading speed - I decided to encode loaded objects as NSValue and then store in Core data. Later, when corresponding object needs to be shown again - I fetch from database necessary nsvalue and unarchieve it.
It's all working, but problem is - it's not as fast I hoped it would be.
One of the reasons - because, in that example are used struct objects.
I was not successful to encode/decode them, so I used NSMutableArray, in which I write all struct data, and later - I iterate through it to put back values to struct object.
For example, there is struct:
typedef struct {
GLfloat x;
GLfloat y;
GLfloat z;
} Vertex3D;
It is then defined:
typedef Vertex3D Vector3D;
But in OpenGLWaveFrontObject class it is used like this:
Vector3D *vertexNormals;
how can I encode/decode struct like this?
I've tried like this:
[coder encodeObject:[NSValue value:&vertexNormals withObjCType:#encode(Vector3D)] forKey:#"vertexNormals"];
or like this:
[coder encodeObject:[NSValue value:&vertexNormals withObjCType:#encode(Vector3D[30])] forKey:#"vertexNormals"];
but its not working - if successfully encoded, then when decoding - values are incorrect.
example, how I put back in array of struct necessary values:
vertices = malloc(sizeof(Vertex3D) * [verticesArray count]);
for(int i = 0; i < [verticesArray count]; i++)
{
vertices[i].x = [[[verticesArray objectAtIndex:i] objectAtIndex:0] floatValue];
vertices[i].y = [[[verticesArray objectAtIndex:i] objectAtIndex:1] floatValue];
vertices[i].z = [[[verticesArray objectAtIndex:i] objectAtIndex:2] floatValue];
}
this works, but I have few more struct arrays and in case arrays are big - this becomes a big issue.
EDIT:
Using Amin Negm-Awad provided answer:
If I simply change NSArchiver to NSKeyedArchiver - it throws error:
[NSKeyedArchiver encodeArrayOfObjCType:count:at:]: unsupported type "{?=fff}]" for array encoding'
When I try:
NSValue *encapsulated = [NSValue valueWithBytes:vertexNormals objCType:#encode(Vertex3D[3])];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:encapsulated];
[coder encodeObject:data forKey:#"vertexNormals"];
And vertexNormals were created:
vertexNormals = calloc(3, sizeof(Vector3D));
and filled with similar data You provided.
EDIT 2:
Using updated answer which Amin Negm-Awad provided,
I was successfully able to store struct object as NSData and encode it, and after that - decode it. It works!
example, so that it might help some one else too:
//Encode
NSData *verticesData = [NSData dataWithBytes:vertices length:numberOfVertices * sizeof(Vector3D)];
[coder encodeObject:verticesData forKey:#"vertices"];
//Decode
vertices = malloc(sizeof(Vertex3D) * numberOfVertices);
NSData *verticesData = [decoder decodeObjectForKey:#"vertices"];
[verticesData getBytes:vertices length:sizeof(Vertex3D) * numberOfVertices];
where Vertex3D was a struct:
typedef struct {
GLfloat x;
GLfloat y;
GLfloat z;
} Vertex3D;
and used as a struct array (I don't know how it is really called):
Vertex3D *vertices;
Unfortunately I was not able to store texture data. I can encode and decode, but decoded data are always randomly bizarre.
I am declaring it in this way:
GLfloat *textureCoords;
And I Encoded/Decoded in this way:
//Encode
NSData *textureCoordsData = [NSData dataWithBytes:textureCoords length:valuesPerCoord * numberOfVertices];
[coder encodeObject:textureCoordsData forKey:#"textureCoords"];
//Decode
textureCoords = malloc(sizeof(GLfloat) * valuesPerCoord * numberOfVertices);
NSData *textureCoordsData = [decoder decodeObjectForKey:#"textureCoords"];
[textureCoordsData getBytes:textureCoords length:valuesPerCoord * numberOfVertices];
What could be the problem?
First of all: If the array has variable length, it is simply not possible to use NSValue. This is documented:
The type you specify must be of constant length. You cannot store C
strings, variable-length arrays and structures, and other data types
of indeterminate length in an NSValue—you should use NSString or
NSData objects for these types.
So you should really take into account to use NSData instead.
But, if you want to use NSValue (because you have a constant-length array):
Both of your approaches has an extra indirection. The first one has the wrong type, too. What should work:
// Create some data
Vertex3D *cArray = malloc(30 * sizeof(Vertex3D));
NSInteger i;
for (i=0; i<30; i++)
{
cArray[i].x = i;
cArray[i].y = 9811;
cArray[i].z = 29-i;
}
NSLog(#"%p", cArray);
// Encapsulate that in a value
// Have a look at the parameters
NSValue *encapsulated = [NSValue valueWithBytes:cArray objCType:#encode(Vertex3D[30])];
// Put it through a coder and store the coded data on disk
NSData *data = [NSArchiver archivedDataWithRootObject:encapsulated];
[data writeToFile:[#"~/Desktop/valueTest" stringByExpandingTildeInPath] atomically:YES];
// Out is done here
// from disk
data = [NSData dataWithContentsOfFile:[#"~/Desktop/valueTest" stringByExpandingTildeInPath]];
encapsulated = [NSUnarchiver unarchiveObjectWithData:data];
Vertex3D *cArray2 = malloc(30 * sizeof(Vertex3D));
[encapsulated getValue:cArray2];
for (i=0; i<30; i++)
{
NSLog(#"%f %f %f", cArray[i].x, cArray[i].y, cArray[i].z);
}
NSLog(#"%p", cArray2);
This works in my test code
Update because of the problem with keyed achieving:
To store the c array in an instance of NSData instead of an instance of NSValue do this instead
After
NSLog(#"%p", cArray);
change the code to
// Convert to an NSData instance
NSData *data = [NSData dataWithBytes:cArray length:lengthInBytes];
[data writeToFile:[#"~/Desktop/valueTest" stringByExpandingTildeInPath] atomically:YES];
// Out is done here
// from disk
data = [NSData dataWithContentsOfFile:[#"~/Desktop/valueTest" stringByExpandingTildeInPath]];
Vertex3D *cArray2 = malloc(lengthInBytes);
[data getBytes:cArray2 length:lengthInBytes];
until
for (i=0; i<30; i++)

Converting an NSMutableArray to an Array of C Structs - iPhone - OpenGL ES

I have an OpenGL ES based app which parses in information from an xml file using NSXMLParser. As the vertice information comes in for each scene object in the file, I am saving it in an NSMutableArray as follows :
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (floatArray) {
if (currentlyParsing == kVerticeInformation) {
if (currentParserTagType == kGeometry) {
//NSLog(#"Loaded Vertices %#", string);
NSArray * nums = [string componentsSeparatedByString:#" "];
culmunativeCount += [nums count];
//NSLog(#"Culm Count is %d", culmunativeCount);
[fullParseString appendString : string];
if (checkSumCount <= culmunativeCount) {
//NSLog(#"Full Parse String is %#", fullParseString);
NSArray * finishedArray = [fullParseString componentsSeparatedByString:#" "];
//NSLog(#"Finihsed ARray is %d", [finishedArray count]);
[finishedParsingArray addObjectsFromArray:finishedArray];
//NSLog(#" FINISHED PARSING = %d", [finishedParsingArray count]);
NSUInteger baseIndex;
for (baseIndex=0; baseIndex <[finishedParsingArray count]; baseIndex +=3) {
NSString * xPoint = [finishedParsingArray objectAtIndex:baseIndex];
NSDecimalNumber * errorCheckX = [NSDecimalNumber decimalNumberWithString:xPoint];
float x = [errorCheckX floatValue];
NSString * yPoint = [finishedParsingArray objectAtIndex:baseIndex +1];
NSDecimalNumber * errorCheckY = [NSDecimalNumber decimalNumberWithString:yPoint];
float y = [errorCheckY floatValue];
NSString * zPoint = [finishedParsingArray objectAtIndex:baseIndex+2];
NSDecimalNumber * errorCheckZ = [NSDecimalNumber decimalNumberWithString:zPoint];
float z = [errorCheckZ floatValue];
vertex3D = [[Vertex3D alloc]init];
vertex3D.value1 = x;
vertex3D.value2 = y;
vertex3D.value3 = z;
[verticesArray addObject:vertex3D];
//NSLog(#"Vertices stored are %f, %f, %f", x,y,z);
}
currentlyParsing = kNilSetting;
checkSumCount = 0;
[finishedParsingArray removeAllObjects];
//Set object to adds vertice array to verticesArray;
vertex3D = nil;
objectToAdd.Vertices = verticesArray;
[verticesArray removeAllObjects];
//[finishedArray release];
culmunativeCount = 0;
[fullParseString setString:#""];
}
}
}
}
Vertex3D is a custom class to store the x,y,z points :
#import <Foundation/Foundation.h>
#interface Vertex3D : NSObject
{
float value1;
float value2;
float value3;
}
#property (assign) float value1;
#property (assign) float value2;
#property (assign) float value3;
#end
What I am then doing is passing this array to a SceneObject class which is instantiated for every object in the file. The idea is then to use these SceneObject objects to feed the OpenGL ES engine. I am using glDrawElements as the xml file uses indices.
The problem is that I cannot pass an NSMutable array into the OpenGL ES calls (such as glVertexPointer).
From an example I have been following at http://www.everita.com/lightwave-collada-and-opengles-on-the-iphone, it looks like I need to structure the vertices data as follows in order to use indices :
const Vertex3D tigerBottomPositions[] = {
{0.176567, 0.143711, 0.264963},
{0.176567, 0.137939, 0.177312},
{0.198811, 0.135518, 0.179324},
…
};
This is where I get a bit confused - I think this is an array of C structs which contains 3 Vertex3D objects, each of which contains three floats.
Can anybody advise me on how I can convert my NSMutableArray into a C struct in the correct format for use with OpenGL ES ? I'd also appreciate help on how to setup this C struct in code as my C knowledge is not as strong as my obj c.
Thank you in advance !
You need a dynamic array... given a C structure Vertex3D (not an Objective-C class):
NSArray *arr = ...;
const size_t count = [arr count];
Vertex3D *positions = (Vertex3D*)calloc(count, sizeof(Vertex3D));
for (size_t i=0; i<count; ++i) {
positions[i].x = ...;
// ...
}
// ... use positions
free(positions); // clean up

Storing Pointers to a Struct in An Array ? iPhone

I have the below code in my app which mallocs memory to a struct object. This method is called several times in the code, each time mallocing memory for a new Vector3D object.
What I want to do is capture the pointer to the malloced ShapeClass object (a C struct) so that I can later free each object in my dealloc method.
Can anyone advise me how I can add the pointer to an array ?
Thank you.
vectorCount = [floatArray.array count] / 3;
ShapeClass *vectorArray = malloc(sizeof(Vector3D) * vectorCount);
for (int i=0; i<vectorCount; i++) {
vectorArray[i].x = [[floatArray.array objectAtIndex:(i*3)] floatValue];
vectorArray[i].y = [[floatArray.array objectAtIndex:(i*3)+1] floatValue];
vectorArray[i].z = [[floatArray.array objectAtIndex:(i*3)+2] floatValue];
}
return vectorArray;
// I WANT TO INSERT SOMETHING HERE TO CAPTURE THE POINTER TO vectorArray - E.G ADD IT TO AN ARRAY ?
}
}
return NULL;
}
Use [NSValue valueWithPointer:] to store it in the container classes.
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
SomeObject *object = [[SomeObject alloc] init];
//Store the pointer to object
[dictionary setValue:[NSValue valueWithPointer:object] forKey:#"object"];

How to find the median value of NSNumbers in an NSArray?

I'm trying to calculate the median of a (small) set of NSNumbers in an NSArray. Every object in the NSArray is a NSNumber.
Here is what I'm trying, but it's not working:
NSNumber *median = [smallNSArray valueForKeyPath:#"#median.floatValue"];
NSArray *sorted = [smallNSArray sortedArrayUsingSelector:#selector(compare:)]; // Sort the array by value
NSUInteger middle = [sorted count] / 2; // Find the index of the middle element
NSNumber *median = [sorted objectAtIndex:middle]; // Get the middle element
You can get fancier. For example, the median of a set with an even number of numbers is technically the average of the middle two numbers. You could also wrap this up into a neat one-line method in a category on NSArray:
#interface NSArray (Statistics)
- (id)median;
#end
#implementation NSArray (Statistics)
- (id)median
{
return [[self sortedArrayUsingSelector:#selector(compare:)] objectAtIndex:[self count] / 2];
}
#end
For anyone who has the unusual need for this function, here's a category method on NSArray that will work with both an odd and an even number of elements:
NSARRAY CATEGORY METHOD
- (float)median {
if (self.count == 1) return [self[0] floatValue];
float result = 0;
NSUInteger middle;
NSArray * sorted = [self sortedArrayUsingSelector:#selector(compare:)];
if (self.count % 2 != 0) { //odd number of members
middle = (sorted.count / 2);
result = [[sorted objectAtIndex:middle] floatValue];
}
else {
middle = (sorted.count / 2) - 1;
result = [[#[[sorted objectAtIndex:middle], [sorted objectAtIndex:middle + 1]] valueForKeyPath:#"#avg.self"] floatValue];
}
return result;
}
TEST
NSArray * singleElement = #[#1];
NSArray * oddNumberOfElements = #[#3, #5, #7, #12, #13, #14, #19, #20, #21, #22, #23, #29, #39, #40, #56];
NSArray * evenNumberOfElements = #[#3, #5, #7, #12, #13, #14, #19, #20, #21, #22, #23, #29, #40, #56];
NSLog(
#"oddNumberOfElements: %f, evenNumberOfElements: %f singleElement: %f",
[oddNumberOfElements median], [evenNumberOfElements median], [singleElement median]
);
//oddNumberOfElements: 20.000000, evenNumberOfElements: 19.500000 singleElement: 1.000000
Swift Extension
extension Array where Element: Comparable {
var median: Element {
return self.sort(<)[self.count / 2]
}
}